feat: add P2 features — retry policy, prompt templating, usage tracking, tech debt cleanup

- Extract shared splitMessage() into channels/utils.ts (dedup 4 adapters)
- Add Slack user name resolution with caching (users.info API)
- Add withRetry() with exponential backoff + jitter, isRetryable() filter
- Wire retry config into ModelRouter.chat() (non-streaming only)
- Add assembleSystemPrompt() multi-file template system (SOUL/AGENTS/IDENTITY/USER/TOOLS.md)
- Add usage tracking accumulators in NativeAgent + AgentOrchestrator
- Add estimateCost() with per-model pricing table
- Add /usage TUI command with full usage report formatting
- Add retrySchema and promptSchema to config schema

Tests: 569 passing, typecheck clean
This commit is contained in:
William Valentin
2026-02-06 15:12:35 -08:00
parent de68deb1b2
commit 4316dbd3be
24 changed files with 902 additions and 143 deletions
+21 -1
View File
@@ -1,4 +1,4 @@
import type { ModelClient, Message, ChatRequest, ChatResponse, ModelToolCall } from '../../models/types.js';
import type { ModelClient, Message, ChatRequest, ChatResponse, ModelToolCall, TokenUsage } from '../../models/types.js';
import type { ModelRouter, ModelTier } from '../../models/router.js';
import type { Session } from '../../session/index.js';
import type { ToolRegistry } from '../../tools/registry.js';
@@ -39,6 +39,8 @@ export class NativeAgent {
private toolExecutor?: ToolExecutor;
private maxIterations: number;
private onToolUse?: (event: ToolUseEvent) => void;
private _totalUsage: TokenUsage = { inputTokens: 0, outputTokens: 0 };
private _callCount: number = 0;
constructor(config: NativeAgentConfig) {
this.modelClient = config.modelClient;
@@ -79,6 +81,10 @@ export class NativeAgent {
const response = await this.chatWithRouter(request);
this._totalUsage.inputTokens += response.usage.inputTokens;
this._totalUsage.outputTokens += response.usage.outputTokens;
this._callCount++;
if (response.fallback) {
console.warn(`[Flynn] ${response.fallbackReason}`);
}
@@ -110,6 +116,10 @@ export class NativeAgent {
const response = await this.chatWithRouter(request);
this._totalUsage.inputTokens += response.usage.inputTokens;
this._totalUsage.outputTokens += response.usage.outputTokens;
this._callCount++;
if (response.fallback) {
console.warn(`[Flynn] ${response.fallbackReason}`);
}
@@ -185,6 +195,16 @@ export class NativeAgent {
} else {
this.inMemoryHistory = [];
}
this.resetUsage();
}
getUsage(): { inputTokens: number; outputTokens: number; calls: number } {
return { ...this._totalUsage, calls: this._callCount };
}
resetUsage(): void {
this._totalUsage = { inputTokens: 0, outputTokens: 0 };
this._callCount = 0;
}
getHistory(): Message[] {