titan-agent 5.4.1 → 5.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/agent/agentLoop.ts"],"sourcesContent":["/**\n * TITAN — Agent Loop (Phase State Machine)\n *\n * Replaces the monolithic for-loop in agent.ts with a clean Think/Act/Respond\n * state machine that eliminates cloud model tool looping by design.\n *\n * Phases:\n * THINK — Call LLM WITH tools. Model returns content or tool_calls.\n * ACT — Execute tool calls, record results, run loop detection.\n * RESPOND — Call LLM WITHOUT tools. Forces text-only final answer.\n * DONE — Loop exits.\n *\n * Non-autonomous: THINK → ACT → RESPOND → DONE (one tool round, then text)\n * Autonomous: THINK → ACT → THINK → ... → RESPOND → DONE (multi-round)\n */\nimport { chat, chatStream } from '../providers/router.js';\nimport { executeTools, type ToolResult } from './toolRunner.js';\nimport { drainPendingResults, getAgentInbox, claimWakeupRequest } from './agentWakeup.js';\nimport { setCurrentSessionId } from './agent.js';\nimport { hasActionDirectives, compileActions } from './actionCompiler.js';\nimport { heartbeat, recordToolCall, checkResponse, getNudgeMessage, checkToolCallCapability, resetToolCallFailures } from './stallDetector.js';\nimport { loadConfig } from '../config/config.js';\nimport { checkForLoop } from './loopDetection.js';\nimport { spawnSubAgent, SUB_AGENT_TEMPLATES } from './subAgent.js';\nimport { updateIssue, startRun, endRun, addIssueComment, recordSpend } from './commandPost.js';\nimport { recordTokenUsage, routeModel, type TurnContext } from './costOptimizer.js';\nimport { calculateActualCost } from './costEstimator.js';\nimport { getSessionGoal } from './autonomyContext.js';\nimport { initBudget, checkBudget, recordUsage, markExceeded, cleanupBudget, getDefaultBudget } from './promptBudget.js';\nimport { scanForSecrets } from '../security/secretGuard.js';\nimport { fullExfilScan } from '../security/exfilScan.js';\nimport { runShellHooks } from '../hooks/shellHooks.js';\nimport { initOtel, newTraceContext, timedSpan, fireSpan } from '../diagnostics/otel.js';\n\nimport type { TitanConfig } from '../config/schema.js';\n\n/** Timeout wrapper for LLM calls. Prevents indefinite hangs on stuck providers. */\nasync function chatWithTimeout<T>(fn: () => Promise<T>, label: string, timeoutMs = 300_000): Promise<T> {\n const timeout = new Promise<never>((_, reject) =>\n setTimeout(() => reject(new Error(`${label} timed out after ${timeoutMs}ms`)), timeoutMs)\n );\n return Promise.race([fn(), timeout]);\n}\nimport { buildSmartContext } from './contextManager.js';\nimport { isKilled } from '../safety/killSwitch.js';\nimport { estimateTokens } from '../utils/tokens.js';\nimport { DEFAULT_MAX_TOKENS } from '../utils/constants.js';\nimport { getCachedResponse, setCachedResponse } from './responseCache.js';\nimport { shouldReflect, reflect, resetProgress, recordProgress, setProgressSession } from './reflection.js';\nimport { recordToolResult, classifyTaskType, recordToolPreference, getErrorResolution, recordErrorResolution } from '../memory/learning.js';\nimport { saveCheckpoint } from './checkpoint.js';\nimport { updateSoulState, emitHeartbeat, getInnerMonologue, recordAttempt } from './soul.js';\n\n// v5.0: Global steer queue — allows external API to nudge active sessions\nconst globalSteerQueues = new Map<string, string[]>();\n\n/** Push a steer message to an active (or upcoming) session */\nexport function pushSteer(sessionId: string, message: string): boolean {\n const queue = globalSteerQueues.get(sessionId);\n if (queue) {\n queue.push(message);\n } else {\n globalSteerQueues.set(sessionId, [message]);\n }\n return true;\n}\nimport { recordToolUsage } from './userProfile.js';\nimport { runSubAgent, type Domain } from './swarm.js';\nimport { compressToolResult, recordStep, getProgressSummary } from './trajectoryCompressor.js';\nimport { verifyFileWrite } from './autoVerify.js';\nimport type { ChatMessage, ChatResponse, ToolCall, ToolDefinition } from '../providers/base.js';\nimport logger from '../utils/logger.js';\n\nconst COMPONENT = 'AgentLoop';\n\n/**\n * Validate tool call / tool result pairing (LangGraph pattern).\n * Every assistant message with toolCalls must have a matching tool result for each call.\n * Orphaned pairs cause API rejections from all providers.\n *\n * Hunt Finding #14 (2026-04-14): previously this function DROPPED assistant\n * messages with orphaned tool calls, destroying the model's work history and\n * causing it to redo tool calls or get confused about its state. The 5-phase\n * context compressor can legitimately drop tool RESULT messages while keeping\n * the assistant tool_call messages (or vice versa), creating orphans.\n *\n * New behavior: when an orphaned tool_call is detected, SYNTHESIZE a placeholder\n * tool result message with `[Earlier tool result cleared]` content. This keeps\n * the conversation shape valid for providers AND preserves the model's history.\n * The model still sees that the tool was called and knows not to call it again.\n */\nfunction validateToolPairs(messages: ChatMessage[]): ChatMessage[] {\n const toolResultIds = new Set(\n messages.filter(m => m.role === 'tool' && m.toolCallId).map(m => m.toolCallId)\n );\n const out: ChatMessage[] = [];\n for (const m of messages) {\n out.push(m);\n if (m.role === 'assistant' && m.toolCalls && m.toolCalls.length > 0) {\n const orphaned = m.toolCalls.filter(tc => !toolResultIds.has(tc.id));\n if (orphaned.length > 0) {\n logger.warn(COMPONENT, `[ToolPairValidation] Synthesizing ${orphaned.length} missing tool result(s) for orphaned tool call(s): ${orphaned.map(tc => tc.function.name).join(', ')}`);\n // Insert a synthetic tool result immediately after the assistant message.\n // This preserves the model's work history while keeping the pairing valid.\n for (const tc of orphaned) {\n out.push({\n role: 'tool',\n // v4.10.0-local: MUST include `name` — Gemini's\n // Ollama-compat adapter maps it to\n // `function_response.name`, which it rejects\n // when empty. Fix for recurring HTTP 400s\n // \"Name cannot be empty\" on gemini-3-flash-preview:cloud.\n name: tc.function.name,\n content: `[Earlier tool result cleared — ${tc.function.name} was called previously but its result was pruned from context.]`,\n toolCallId: tc.id,\n });\n toolResultIds.add(tc.id);\n }\n }\n }\n }\n return out;\n}\n\n/**\n * Hunt Finding #09 (2026-04-14): pair-aware context trim.\n *\n * Previously the agent loop trimmed with `.slice(-8)` on non-system messages,\n * which cut right through tool_call/tool_result pairs. Then validateToolPairs\n * dropped the assistant-with-orphan-toolcall messages, losing the work history.\n * The model would see no prior tool calls and redo them, causing ping-pong loops.\n *\n * This trim walks backwards and keeps tool_call+tool_result PAIRS together. When\n * we hit a tool_result, we also keep its parent assistant-with-tool_calls message.\n * When we hit an assistant-with-tool_calls, we also keep ALL its tool_results.\n */\nfunction trimPairAware(messages: ChatMessage[], maxTotal: number): ChatMessage[] {\n const systemMsgs = messages.filter(m => m.role === 'system');\n const nonSystem = messages.filter(m => m.role !== 'system');\n const maxNonSystem = Math.max(1, maxTotal - systemMsgs.length);\n\n if (nonSystem.length <= maxNonSystem) return messages;\n\n // Walk backwards, keeping pairs together. Tool result messages belong to the\n // nearest preceding assistant message with tool_calls; assistant messages with\n // tool_calls own all immediately-following tool result messages.\n const kept: ChatMessage[] = [];\n let i = nonSystem.length - 1;\n\n // First pass: collect indices to keep, preserving pair integrity\n const keepIdx = new Set<number>();\n while (i >= 0 && keepIdx.size < maxNonSystem) {\n const msg = nonSystem[i];\n if (msg.role === 'tool' && msg.toolCallId) {\n // Find its parent assistant message\n let parentIdx = -1;\n for (let j = i - 1; j >= 0; j--) {\n const cand = nonSystem[j];\n if (cand.role === 'assistant' && cand.toolCalls?.some(tc => tc.id === msg.toolCallId)) {\n parentIdx = j;\n break;\n }\n if (cand.role !== 'tool') break; // only walk through tool siblings\n }\n keepIdx.add(i);\n if (parentIdx >= 0) {\n keepIdx.add(parentIdx);\n // Also keep any sibling tool results for the same assistant\n for (let j = parentIdx + 1; j < nonSystem.length; j++) {\n const sib = nonSystem[j];\n if (sib.role !== 'tool') break;\n keepIdx.add(j);\n }\n }\n i = parentIdx >= 0 ? parentIdx - 1 : i - 1;\n } else if (msg.role === 'assistant' && msg.toolCalls && msg.toolCalls.length > 0) {\n // Keep this assistant + all its tool results\n keepIdx.add(i);\n for (let j = i + 1; j < nonSystem.length; j++) {\n const sib = nonSystem[j];\n if (sib.role !== 'tool') break;\n keepIdx.add(j);\n }\n i--;\n } else {\n keepIdx.add(i);\n i--;\n }\n }\n\n // Emit in original order\n for (let k = 0; k < nonSystem.length; k++) {\n if (keepIdx.has(k)) kept.push(nonSystem[k]);\n }\n\n return [...systemMsgs, ...kept];\n}\n\n/** Sanitize reflection reasoning before injecting into message stream */\nfunction sanitizeReflection(text: string): string {\n return text\n .slice(0, 200)\n .replace(/\\[SYSTEM\\].*$/gm, '')\n .replace(/^(You are|IMPORTANT:|CRITICAL:|IGNORE).*$/gim, '')\n .trim() || 'approach not working';\n}\n\n/**\n * Hunt Finding #05 (2026-04-14): detect explicit tool-use intent in the user message.\n *\n * Symptom: User says \"use the shell tool to run uptime\" but the model returns\n * plausible but FABRICATED output (hallucinated uptime text) without actually\n * calling the shell tool. The text looks like real tool output but isn't.\n *\n * Previously `forceToolUse` only fired in autonomous mode. Regular API calls\n * let the model ignore tools, so any model with weak tool calling could\n * hallucinate output.\n *\n * Fix: if the user message explicitly requests a tool, we force\n * `tool_choice: required` on the first call, even in non-autonomous mode.\n * This doesn't eliminate hallucination entirely but stops the most common\n * pattern where the model chooses to not call a tool at all.\n */\nexport function detectToolUseIntent(userMessage: string): boolean {\n if (!userMessage || userMessage.length < 5) return false;\n const msg = userMessage.toLowerCase();\n\n const intentPatterns = [\n // Explicit \"use the X tool\" or \"use X tool\"\n /\\buse (?:the )?(\\w+) tool\\b/,\n /\\buse (?:the )?(shell|web_search|web_fetch|read_file|write_file|edit_file|list_dir|memory|weather|fb_post|fb_reply|fb_read_feed|github|email|calendar)\\b/,\n // \"run X\" / \"execute X\" / \"call X\"\n /\\brun (?:the )?(?:shell|command|tool|script)\\b/,\n /\\bexecute (?:the )?(?:shell|command|tool|script|this)\\b/,\n /\\bcall (?:the )?(?:\\w+ )?tool\\b/,\n /\\binvoke (?:the )?(\\w+)\\b/,\n // Action verbs that require tool execution\n /\\b(?:search the web|search for|web search)\\b/,\n /\\bfetch (?:the )?(url|page|content|https?:)/,\n /\\bread (?:the )?(?:file|contents? of|lines from)\\b/,\n /\\bwrite (?:this |the )?(?:to (?:the )?file|file)\\b/,\n /\\blist (?:the )?(?:files?|contents?) (?:in|of|at)\\b/,\n // Requests to check real system state that REQUIRES a tool\n /\\b(?:what is|what's|show me|get) (?:the )?(?:current|actual) (?:uptime|hostname|ip|path|directory|pwd|time|date|memory|disk)\\b/,\n // Hunt Finding #17 (2026-04-14): added `[\\s:]+` so \"run: ls\" matches too.\n /\\brun[\\s:]+['\"`]?(?:echo|ls|pwd|uptime|whoami|date|uname|cat|grep|find|node|npm|git|which|ps|df|free)\\b/,\n // Widget / gallery tool requests — canvas chat explicitly asks for gallery_search/gallery_get\n /\\b(?:call|use|run)\\b.*?\\b(?:gallery_search|gallery_get|gallery_list)\\b/,\n /\\b(?:create|add|make|build|spawn)\\b.*?\\b(?:widget|panel|canvas|gallery)\\b/,\n /\\b(?:widget|gallery)\\b.*?\\b(?:template|search|find|get|fetch)\\b/,\n // New system widget intents (v5.0.2 \"forgotten features\" surface)\n /\\b(?:backup|snapshot|archive)\\b/,\n /\\b(?:training|train|specialist|model)\\b/,\n /\\b(?:recipe|playbook|workflow|jarvis)\\b/,\n /\\b(?:vram|gpu|memory|nvidia)\\b/,\n /\\b(?:team|member|role|permission|rbac)\\b/,\n /\\b(?:cron|schedule|job|timer)\\b/,\n /\\b(?:checkpoint|restore|save state)\\b/,\n /\\b(?:organism|drive|safety|alert|guardrail)\\b/,\n /\\b(?:fleet|node|route|mesh)\\b/,\n /\\b(?:captcha|browser|form fill|web automation)\\b/,\n /\\b(?:paperclip|sidecar|helper)\\b/,\n /\\b(?:test|flaky|failing|coverage|eval)\\b/,\n ];\n\n return intentPatterns.some(p => p.test(msg));\n}\n\n/**\n * Hunt Finding #17 (2026-04-14): Extract a tool call from the USER MESSAGE directly.\n *\n * Runs as a last-resort rescue path in the NoTools handler. Triggered when the model\n * ignored `tool_choice=required` and all model-response-based rescue paths failed.\n *\n * The key insight: when the user's request explicitly names a command or file, we\n * don't need the model's cooperation to figure out what tool to call — we can parse\n * the intent from the user message itself.\n *\n * This defends against weak models (like minimax-m2.7:cloud) that fabricate\n * plausible-sounding tool output (\"Permission denied\", \"command returned null\",\n * \"Node.js is not installed\") instead of actually calling the tool.\n *\n * Returns a synthetic tool call or null if no clear intent can be extracted.\n */\nexport function extractToolCallFromUserMessage(\n userMessage: string,\n activeTools: ToolDefinition[],\n): ToolCall | null {\n if (!userMessage || userMessage.length < 5) return null;\n const msg = userMessage.trim();\n const lower = msg.toLowerCase();\n const availableNames = new Set(activeTools.map(t => t.function.name));\n const mkCall = (name: string, args: Record<string, unknown>): ToolCall => ({\n id: `uir-${Date.now()}`,\n type: 'function' as const,\n function: { name, arguments: JSON.stringify(args) },\n });\n\n // Shell: \"run X\", \"run: X\", \"execute X\", \"please run X\", \"can you run X\"\n // X starts with a known shell command.\n if (availableNames.has('shell')) {\n const shellMatch = msg.match(\n /(?:please\\s+)?(?:can you\\s+)?(?:run|execute)[\\s:]+[`'\"]?((?:ls|cat|grep|find|echo|pwd|uname|node|npm|git|which|ps|df|free|uptime|whoami|date|hostname|ip|head|tail|wc|sort|uniq|awk|sed|curl|wget|ping|du|stat|file|env|printenv|history)\\s[^\\n`'\"]*?)[`'\"]?(?:\\s+and|\\s+then|\\.|\\?|\\s*$)/i,\n );\n if (shellMatch && shellMatch[1]) {\n return mkCall('shell', { command: shellMatch[1].trim() });\n }\n // Bare \"run: ls /path\" without other clauses\n const bareMatch = msg.match(\n /^(?:please\\s+)?(?:run|execute)[\\s:]+[`'\"]?((?:ls|cat|grep|find|echo|pwd|uname|node|npm|git|which|ps|df|free|uptime|whoami|date|hostname)\\s[^\\n`'\"]+?)[`'\"]?\\s*$/i,\n );\n if (bareMatch && bareMatch[1]) {\n return mkCall('shell', { command: bareMatch[1].trim() });\n }\n }\n\n // read_file: \"read the file X\", \"read /path/to/file\", \"show me the contents of X\"\n if (availableNames.has('read_file')) {\n const readMatch = msg.match(\n /(?:read|open|show me|display|view)\\s+(?:the\\s+)?(?:file\\s+|contents of\\s+)?[`'\"]?(\\/[a-zA-Z0-9/._-]+)[`'\"]?/i,\n );\n if (readMatch && readMatch[1]) {\n return mkCall('read_file', { path: readMatch[1] });\n }\n }\n\n // list_dir: \"list files in X\", \"list X\", \"what's in X\"\n if (availableNames.has('list_dir')) {\n const listMatch = msg.match(\n /(?:list|show)\\s+(?:the\\s+)?(?:files?|contents?|directory)\\s+(?:in|of|at)\\s+[`'\"]?(\\/[a-zA-Z0-9/._-]+)[`'\"]?/i,\n );\n if (listMatch && listMatch[1]) {\n return mkCall('list_dir', { path: listMatch[1] });\n }\n }\n\n // web_search: \"search the web for X\", \"google X\", \"search for X\"\n if (availableNames.has('web_search')) {\n const searchMatch = msg.match(/(?:search\\s+(?:the\\s+)?web\\s+for|google|web\\s+search\\s+for|search\\s+for)\\s+(.+?)(?:\\.|\\?|$)/i);\n if (searchMatch && searchMatch[1]) {\n return mkCall('web_search', { query: searchMatch[1].trim() });\n }\n }\n\n // web_fetch: \"fetch https://...\", \"open URL https://...\"\n if (availableNames.has('web_fetch')) {\n const fetchMatch = msg.match(/(?:fetch|open|load|get)\\s+(https?:\\/\\/[^\\s]+)/i);\n if (fetchMatch && fetchMatch[1]) {\n return mkCall('web_fetch', { url: fetchMatch[1] });\n }\n }\n\n // weather: \"weather for X\", \"what's the weather in X\"\n if (availableNames.has('weather')) {\n const weatherMatch = lower.match(/weather\\s+(?:for|in|at)\\s+([a-zA-Z][a-zA-Z\\s,]+?)(?:\\.|\\?|$)/i);\n if (weatherMatch && weatherMatch[1]) {\n return mkCall('weather', { location: weatherMatch[1].trim() });\n }\n }\n\n return null;\n}\n\n// ── Phase State Machine ��─────────────────────────────────────────────\n\nexport type AgentPhase = 'think' | 'act' | 'respond' | 'done';\n\n/** Stream callbacks — same interface as agent.ts for compatibility */\nexport interface StreamCallbacks {\n onToken?: (token: string) => void;\n onToolCall?: (name: string, args: Record<string, unknown>) => void;\n onToolResult?: (name: string, result: string, durationMs: number, success: boolean, diff?: string) => void;\n onThinking?: () => void;\n onRound?: (round: number, maxRounds: number) => void;\n /**\n * Fired when the router is retrying a transient provider failure on the\n * SAME provider/model. Out-of-band status — never forward to the\n * assistant's text content. UI consumers should use this to render a\n * retry indicator (spinner, toast). Pre-fix v5.4.x leaked this as a\n * `[Retrying request (1/4) due to ...]` text chunk into the response.\n */\n onRetry?: (info: { attempt: number; maxRetries: number; reason: string; provider: string; model: string; delayMs: number }) => void;\n /**\n * Fired when the router falls over to a different provider/model after\n * exhausting retries. Out-of-band status; do NOT append to user-visible\n * content.\n */\n onFailover?: (info: { originalProvider: string; originalModel: string; error?: string }) => void;\n}\n\n/** All inputs the loop needs from processMessage */\nexport interface LoopContext {\n messages: ChatMessage[];\n activeTools: ToolDefinition[];\n allToolsBackup: ToolDefinition[];\n activeModel: string;\n config: TitanConfig;\n sessionId: string;\n agentId?: string; // For Command Post inbox checking\n channel: string;\n message: string;\n streamCallbacks?: StreamCallbacks;\n signal?: AbortSignal;\n isAutonomous: boolean;\n voiceFastPath: boolean;\n effectiveMaxRounds: number;\n taskEnforcementActive: boolean;\n reflectionEnabled: boolean;\n reflectionInterval: number;\n toolSearchEnabled: boolean;\n isKimiSwarm: boolean;\n selfHealEnabled: boolean;\n smartExitEnabled?: boolean;\n thinkingOverride?: string;\n /** Pipeline-specific terminal tools for SmartExit */\n pipelineTerminalTools?: string[];\n /** Pipeline-specific completion detection strategy */\n completionStrategy?: 'smart-exit' | 'no-tools' | 'terminal-tool' | 'single-round';\n /** Pipeline type for logging */\n pipelineType?: string;\n /** Minimum rounds before allowing SmartExit (pipeline-enforced) */\n minRounds?: number;\n /** F1: Pre-model hook (LangGraph pattern) — modify messages for LLM without changing\n * persisted history. Use for RAG injection, summarization, dynamic token budgeting.\n * Receives a COPY of messages; return modified copy for the LLM call. */\n beforeModelCall?: (messages: ChatMessage[], round: number) => ChatMessage[];\n /** Provider-specific opt-ins forwarded to ChatOptions.providerOptions. */\n providerOptions?: Record<string, unknown>;\n /** v5.0: Steer queue — mid-run nudges injected after ACT phase */\n steerQueue?: string[];\n}\n\n/** Everything processMessage needs back from the loop */\nexport interface LoopResult {\n content: string;\n toolsUsed: string[];\n orderedToolSequence: string[];\n modelUsed: string;\n /** v5.0: OTEL trace context for observability */\n traceContext?: { traceId: string; spanId: string };\n promptTokens: number;\n completionTokens: number;\n budgetExhausted: boolean;\n /** Structured details from each tool call — used for inter-step context in deliberation */\n toolCallDetails: Array<{\n name: string;\n args: Record<string, unknown>;\n resultSnippet: string;\n success: boolean;\n }>;\n}\n\n/**\n * Hunt Finding #38b (2026-04-15): strip narrator preamble from chat\n * responses. Weak models (minimax-m2.7:cloud, glm-5.1) love to prefix\n * their answer with internal monologue like:\n * \"The user wants a joke. I can respond directly without needing any\n * tools. Why don't scientists trust atoms? Because they make up\n * everything.\"\n *\n * We can't prevent this reliably via a system directive because the\n * model ignores it 30-50% of the time. Instead, we detect and strip\n * the preamble server-side after the model has finished generating,\n * keeping only the actual answer.\n *\n * Strategy: the preamble is a sequence of sentences that start with\n * narrator openers (\"The user wants\", \"I can/should/will respond\",\n * \"Let me\", \"Actually\", \"Looking at\", \"I'll\"). We strip those\n * sentences off the front until we hit content that doesn't start\n * with a narrator opener. That's the real answer.\n */\nexport function stripNarratorPreamble(text: string): string {\n if (!text || text.length < 10) return text;\n const NARRATOR_OPENERS = [\n /^\\s*the user (?:wants|asked|said|is asking|is requesting|needs|wrote|mentioned|told me)/i,\n /^\\s*(?:the\\s+)?user (?:wants|asked|said|is asking)/i,\n /^\\s*I (?:should|need to|can|will|must|could|'ll|'m going to) (?:respond|reply|answer|provide|give|explain|tell|just|simply)/i,\n /^\\s*I['']m (?:going to|about to) (?:respond|reply|answer)/i,\n /^\\s*(?:let me|let's)\\b/i,\n /^\\s*(?:actually|okay|alright|hmm|well|so|right),?\\s+(?:I|let|the)/i,\n /^\\s*looking at (?:this|the|what)/i,\n /^\\s*(?:this is|that['']s) (?:a|an) (?:casual|simple|direct|basic|friendly|quick)/i,\n /^\\s*no tools? (?:needed|required)/i,\n /^\\s*(?:i can|i['']ll) (?:respond|reply|answer) (?:directly|simply|without|naturally)/i,\n ];\n\n // Split into sentences, attempt to strip leading narrator sentences.\n // Use a conservative split that respects common sentence terminators.\n const sentences: string[] = [];\n let buffer = '';\n for (let i = 0; i < text.length; i++) {\n buffer += text[i];\n if (/[.!?]/.test(text[i])) {\n // Lookahead for end of sentence — next char should be whitespace or newline\n const next = text[i + 1];\n if (!next || /\\s/.test(next)) {\n sentences.push(buffer);\n buffer = '';\n }\n }\n }\n if (buffer) sentences.push(buffer);\n\n let stripCount = 0;\n for (const sentence of sentences) {\n if (NARRATOR_OPENERS.some(p => p.test(sentence))) {\n stripCount++;\n } else {\n break;\n }\n }\n\n if (stripCount === 0) return text;\n // Limit: never strip more than 75% of the content, or 3 sentences.\n // If the whole thing looks like narrator, leave it for the sanitizer\n // to catch as a hard fail (the sanitizer will fallback to a safe msg).\n if (stripCount >= sentences.length || stripCount > 3) return text;\n const remaining = sentences.slice(stripCount).join('').trim();\n if (remaining.length < 5) return text;\n return remaining;\n}\n\n// ── Helper: strip leaked tool JSON from LLM responses ────────────────\n\nfunction stripToolJson(text: string): string {\n let cleaned = text.replace(/\\s*\\{\"(?:name|tool_call)\":\\s*\"[^\"]+\",\\s*\"(?:parameters|arguments)\":\\s*\\{[^}]*\\}\\s*\\}\\s*/g, '').trim();\n // Hunt Finding #21 (2026-04-14): also strip minimax:tool_call XML blocks\n // and bare <invoke>/<parameter> tags that models sometimes emit as text\n // when they want to call a tool but shouldn't (e.g., in the respond phase\n // which runs with tools: undefined). Without this, the raw XML reaches\n // result.content, which bypasses the empty-response retry and forces the\n // gateway-level sanitizer to strip + fallback, losing task confirmation.\n cleaned = cleaned.replace(/<minimax:tool_call>[\\s\\S]*?<\\/minimax:tool_call>/g, '').trim();\n cleaned = cleaned.replace(/<minimax:tool_call>[\\s\\S]*$/g, '').trim(); // unclosed\n cleaned = cleaned.replace(/<invoke\\s+name=[\"'][^\"']*[\"']>[\\s\\S]*?<\\/invoke>/g, '').trim();\n cleaned = cleaned.replace(/<invoke\\s+name=[\"'][^\"']*[\"']>[\\s\\S]*$/g, '').trim(); // unclosed\n cleaned = cleaned.replace(/<parameter\\s+name=[\"'][^\"']*[\"']>[\\s\\S]*?<\\/parameter>/g, '').trim();\n cleaned = cleaned.replace(/<\\/?(?:invoke|parameter|minimax:tool_call)[^>]*>/g, '').trim();\n return cleaned;\n}\n\n// ── Helper: extract tool call from text content (ToolRescue) ─────────\n// Moved inline to avoid circular dependency. This is the rescue logic\n// for models that describe tool calls in text instead of structured output.\n\nfunction extractToolCallFromContent(\n content: string,\n activeTools: ToolDefinition[],\n isCloudModel = false,\n): ToolCall | null {\n if (!content || content.length < 10) return null;\n const toolNames = activeTools.map(t => t.function.name);\n\n // Strategy 1a: Embedded JSON tool calls\n const jsonMatch = content.match(/\\{\"(?:name|tool_call)\":\\s*\"([^\"]+)\",\\s*\"(?:parameters|arguments)\":\\s*(\\{[^}]*(?:\\{[^}]*\\}[^}]*)?\\})\\s*\\}/);\n if (jsonMatch && toolNames.includes(jsonMatch[1])) {\n return { id: `rescue_${Date.now()}`, type: 'function', function: { name: jsonMatch[1], arguments: jsonMatch[2] } };\n }\n\n // Strategy 1b: DeepSeek XML-style <function_call> format\n const xmlMatch = content.match(/<function_call>\\s*(\\{[\\s\\S]*?\\})\\s*<\\/function_call>/);\n if (xmlMatch) {\n try {\n const parsed = JSON.parse(xmlMatch[1]);\n const name = parsed.name || parsed.function?.name;\n const args = parsed.arguments || parsed.parameters || parsed.function?.arguments;\n if (name && toolNames.includes(name)) {\n return { id: `rescue_${Date.now()}`, type: 'function', function: { name, arguments: typeof args === 'string' ? args : JSON.stringify(args || {}) } };\n }\n } catch { /* malformed */ }\n }\n\n // Strategy 2: Natural language tool mentions\n // Cloud models often describe tool calls in text → empty skipSet to rescue all tools.\n // Local models handle structured tool calls fine → skip common tools to avoid false rescues.\n const skipSet = isCloudModel\n ? new Set<string>()\n : new Set(['shell', 'read_file', 'write_file', 'edit_file', 'list_dir', 'memory', 'web_search', 'web_fetch', 'tool_search']);\n\n for (const toolName of toolNames) {\n if (skipSet.has(toolName)) continue;\n const mentionRegex = new RegExp(\n `(?:call(?:ing)?|us(?:e|ing)|invok(?:e|ing)|execut(?:e|ing)|runn?(?:ing)?|tool\\\\s+)\\\\s*(?:the\\\\s+)?(?:tool\\\\s+)?(?:named\\\\s+)?[\"\\`']?${toolName.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')}[\"\\`']?`,\n 'i',\n );\n if (!mentionRegex.test(content)) continue;\n\n // Try to extract JSON args\n const jsonArgs = content.match(/\\{[\\s\\S]*?\\}/);\n if (jsonArgs) {\n try {\n const parsed = JSON.parse(jsonArgs[0]);\n if (typeof parsed === 'object' && parsed !== null) {\n const args: Record<string, string> = {};\n for (const [k, v] of Object.entries(parsed)) {\n args[k] = typeof v === 'string' ? v : JSON.stringify(v);\n }\n return { id: `rescue_${Date.now()}`, type: 'function', function: { name: toolName, arguments: JSON.stringify(args) } };\n }\n } catch { /* not valid JSON */ }\n }\n\n // Tool-specific extraction for common tools\n if (toolName === 'shell') {\n const cmdMatch = content.match(/(?:`{1,3}(?:bash|sh|shell)?\\n?(.*?)`{1,3}|(?:command|run|execute)[=:\\s]+[\"'](.+?)[\"'])/s);\n if (cmdMatch) {\n const cmd = (cmdMatch[1] || cmdMatch[2]).trim();\n if (cmd.length > 0) return { id: `rescue_${Date.now()}`, type: 'function', function: { name: 'shell', arguments: JSON.stringify({ command: cmd }) } };\n }\n }\n if (toolName === 'read_file' || toolName === 'write_file' || toolName === 'edit_file') {\n const pathMatch = content.match(/(?:file|path)[=:\\s]+[\"']?((?:\\/|~\\/|\\.\\/)\\S+?)[\"'\\s,)]/i)\n || content.match(/((?:\\/|~\\/)\\S+\\.\\w{1,10})/);\n if (pathMatch) {\n if (toolName === 'write_file') {\n // Try to extract content to write from code blocks\n const codeBlock = content.match(/```[\\w]*\\n([\\s\\S]*?)```/);\n const writeContent = codeBlock ? codeBlock[1] : '';\n return { id: `rescue_${Date.now()}`, type: 'function', function: { name: 'write_file', arguments: JSON.stringify({ path: pathMatch[1], content: writeContent }) } };\n } else if (toolName === 'edit_file') {\n // edit_file needs target+replacement — rescue as read_file first so the agent can see the file\n return { id: `rescue_${Date.now()}`, type: 'function', function: { name: 'read_file', arguments: JSON.stringify({ path: pathMatch[1] }) } };\n } else {\n return { id: `rescue_${Date.now()}`, type: 'function', function: { name: 'read_file', arguments: JSON.stringify({ path: pathMatch[1] }) } };\n }\n }\n }\n if (toolName === 'web_search') {\n const queryMatch = content.match(/(?:search(?:ing)?(?:\\s+for)?|query)[=:\\s]+[\"'](.+?)[\"']/i)\n || content.match(/search(?:ing)?\\s+(?:for\\s+)?[\"'](.+?)[\"']/i);\n if (queryMatch) return { id: `rescue_${Date.now()}`, type: 'function', function: { name: 'web_search', arguments: JSON.stringify({ query: queryMatch[1] }) } };\n }\n }\n\n return null;\n}\n\n// ── Helper: find a fallback model for tool calling ───────────────────\n\nfunction findToolCapableFallback(failedModel: string, failedModels: Set<string>, config: TitanConfig): string | null {\n const candidates: string[] = [];\n const toolCapable = (config.agent as Record<string, unknown>).toolCapableModels as string[] | undefined;\n if (toolCapable?.length) candidates.push(...toolCapable);\n const chain = (config.agent as Record<string, unknown>).fallbackChain as string[] | undefined;\n if (chain?.length) candidates.push(...chain);\n const aliases = (config.agent as Record<string, unknown>).modelAliases as Record<string, string> | undefined;\n if (aliases?.fast) candidates.push(aliases.fast);\n if (aliases?.smart) candidates.push(aliases.smart);\n return candidates.filter(m => m !== failedModel && !failedModels.has(m))[0] || null;\n}\n\n// ── Tool Result Summarization ───────────────────────────────────\n\n/** Extract key data points from large file contents */\nfunction summarizeToolResult(content: string): string | null {\n const parts: string[] = [];\n\n // Extract version numbers\n const versions = content.match(/[\"']?version[\"']?\\s*[:=]\\s*[\"']?([\\d.]+)[\"']?/gi);\n if (versions) parts.push(`Versions found: ${versions.slice(0, 3).join(', ')}`);\n\n // Extract name/title\n const names = content.match(/[\"']?name[\"']?\\s*[:=]\\s*[\"']([^\"']+)[\"']/i);\n if (names) parts.push(`Name: ${names[1]}`);\n\n // Extract exports/functions\n const exports = content.match(/export\\s+(?:async\\s+)?function\\s+(\\w+)/g);\n if (exports) parts.push(`Exports: ${exports.slice(0, 5).map(e => e.replace(/export\\s+(async\\s+)?function\\s+/, '')).join(', ')}`);\n\n // Extract constants\n const constants = content.match(/(?:const|export const)\\s+([A-Z_]+)\\s*=\\s*['\"]([^'\"]+)['\"]/g);\n if (constants) parts.push(`Constants: ${constants.slice(0, 3).join('; ')}`);\n\n // File line count\n const lineCount = content.split('\\n').length;\n parts.push(`${lineCount} lines total`);\n\n return parts.length > 1 ? parts.join(' | ') : null;\n}\n\n// ── Response Validation ─────────────────────────────────────────\n\n/** Detect if the response is missing specific data the user asked for */\nfunction detectResponseGap(userMessage: string, response: string, messages: ChatMessage[]): string | null {\n const lower = userMessage.toLowerCase();\n const respLower = response.toLowerCase();\n\n // Check: user asked for a number/version/count but response has none\n if (/\\b(version|number|count|how many|what is the|tell me the)\\b/.test(lower)) {\n const hasNumber = /\\d+/.test(response);\n // Check if tool results contain numbers the response missed\n const toolResults = messages.filter(m => m.role === 'tool').slice(-3);\n const toolHasNumber = toolResults.some(m => /\\d+\\.\\d+\\.\\d+|\\b\\d{1,5}\\b/.test(m.content || ''));\n if (!hasNumber && toolHasNumber) {\n return 'The user asked for a specific number or version. Your tool results contain this data but your response does not include it.';\n }\n }\n\n // Check: user asked to read a file but response doesn't reference its content\n if (/\\b(read|show|contents? of|what does|what.s in)\\b/.test(lower)) {\n if (respLower.includes('i read') || respLower.includes('i was able to') || respLower.includes('here is')) {\n // Response claims to have read — probably fine\n return null;\n }\n const toolResults = messages.filter(m => m.role === 'tool').slice(-3);\n if (toolResults.length > 0 && response.length < 50) {\n return 'The user asked you to read file contents. Your tool returned data but your response is too short — include the relevant information.';\n }\n }\n\n // Check: user asked for a specific value (e.g., \"the TITAN_VERSION value\")\n if (/\\b(value|result|output|answer)\\b/.test(lower)) {\n if (response.length < 20 || respLower.includes('error') || respLower.includes('unable')) {\n const toolResults = messages.filter(m => m.role === 'tool').slice(-3);\n if (toolResults.some(m => (m.content || '').length > 50)) {\n return 'The user asked for a specific value. Your tools retrieved data but your response did not include it.';\n }\n }\n }\n\n return null;\n}\n\n// ── Main Loop ──────────────────────────���───────────────────────────���─\n\nexport async function runAgentLoop(ctx: LoopContext): Promise<LoopResult> {\n const result: LoopResult = {\n content: '',\n toolsUsed: [],\n orderedToolSequence: [],\n modelUsed: ctx.activeModel,\n promptTokens: 0,\n completionTokens: 0,\n budgetExhausted: false,\n toolCallDetails: [],\n };\n\n let phase: AgentPhase = 'think';\n let round = 0;\n let activeModel = ctx.activeModel;\n\n // Self-heal state\n let modelSwitchCount = 0;\n let selfHealExhausted = false;\n const failedModels = new Set<string>();\n\n // Prompt budget initialization (Space Agent parity)\n const budgetConfig = getDefaultBudget(ctx.config);\n if (budgetConfig.maxTokens > 0) {\n initBudget(ctx.sessionId, budgetConfig);\n }\n\n // Bounded retries for [NoTools] rounds — prevents infinite think-loop when\n // the model keeps returning prose instead of tool calls\n let noToolsRetryCount = 0;\n const MAX_MODEL_SWITCHES = 2;\n\n // Reflection state\n let pivotCount = 0;\n const MAX_PIVOTS = 1;\n const failedApproaches: string[] = [];\n setProgressSession(ctx.sessionId);\n resetProgress(ctx.sessionId);\n\n // Learning state\n let lastFailedTool: { name: string; error: string } | null = null;\n\n // Tool search state\n const discoveredTools = new Set<string>();\n\n // Shell-for-files nudge counter\n let shellForFilesCount = 0;\n\n // Response validation retry flag (one retry max)\n let responseValidationRetried = false;\n // Empty response retry flag (one retry max)\n let emptyResponseRetried = false;\n\n // Force tool_choice=required on next think phase (set by incomplete task guard)\n let forceWriteOnNextThink = false;\n\n // F5: Budget soft warning — only inject once per loop\n let budgetWarningSent = false;\n\n // Pending tool calls from think phase (passed to act phase)\n let pendingToolCalls: ToolCall[] = [];\n let pendingAssistantContent = '';\n\n // Token budget for context compression\n const tokenBudget = ctx.voiceFastPath ? 2000 : (ctx.config.agent as Record<string, unknown>).tokenBudget as number || DEFAULT_MAX_TOKENS;\n\n // v5.0: Register this session for steering\n globalSteerQueues.set(ctx.sessionId, ctx.steerQueue ?? []);\n\n // v5.0: Initialize OTEL trace context for this run\n const traceCtx = newTraceContext();\n initOtel();\n\n // v5.0: Inactivity timeout tracking. Schema doesn't strictly type the\n // new feature flags yet — read defensively so a missing field doesn't\n // crash the loop, just falls through to the documented default.\n let lastActivityAt = Date.now();\n const agentCfg = ctx.config.agent as Record<string, unknown> | undefined;\n const inactivityTimeout = (agentCfg?.inactivityTimeoutMs as number | undefined) ?? 300_000;\n const absoluteTimeout = (agentCfg?.absoluteTimeoutMs as number | undefined) ?? 600_000;\n const loopStartTime = Date.now();\n function recordActivity() { lastActivityAt = Date.now(); }\n\n // ── Set session context for spawn_agent async delegation ─────\n setCurrentSessionId(ctx.sessionId);\n\n // ── Inject completed async sub-agent results as context ──────\n const cpEnabled = (ctx.config.commandPost as Record<string, unknown> | undefined)?.enabled ?? false;\n if (cpEnabled) {\n const completedAsync = drainPendingResults(ctx.sessionId);\n if (completedAsync.length > 0) {\n const injection = completedAsync.map(r =>\n `[Async Task Complete] ${r.issueIdentifier} (${r.agentName}): ${r.result.success ? 'SUCCESS' : 'FAILED'}\\n${r.result.content.slice(0, 500)}`\n ).join('\\n\\n');\n ctx.messages.push({ role: 'user', content: injection });\n logger.info(COMPONENT, `[AsyncResults] Injected ${completedAsync.length} completed async result(s) into context`);\n }\n\n // ── Heartbeat inbox check: claim and process pending sub-agent tasks ──\n if (ctx.agentId && round > 0 && round % 3 === 0) {\n // Check inbox every 3 rounds to avoid thrashing\n await checkAndProcessInbox(ctx.agentId);\n }\n }\n\n // ── Process any pending inbox work before this round ──\n if (cpEnabled && ctx.agentId && round === 0) {\n await checkAndProcessInbox(ctx.agentId);\n }\n\n while (phase !== 'done' && round < ctx.effectiveMaxRounds) {\n // ── Abort check ──────────────────���───────────────────────\n if (ctx.signal?.aborted || isKilled()) {\n logger.info(COMPONENT, `Session aborted by user at round ${round + 1} (${phase} phase)`);\n result.content = '[Stopped by user]';\n break;\n }\n\n // Emit round info\n if (round > 0) ctx.streamCallbacks?.onThinking?.();\n ctx.streamCallbacks?.onRound?.(round + 1, ctx.effectiveMaxRounds);\n\n logger.info(COMPONENT, `Round ${round + 1}/${ctx.effectiveMaxRounds} — phase: ${phase}, model: ${activeModel}, tools: ${phase === 'respond' ? 0 : ctx.activeTools.length}`);\n\n // v5.0: Shell hook — on_round_start\n if (ctx.config.hooks?.shell?.enabled) {\n await runShellHooks('on_round_start', {\n TITAN_SESSION_ID: ctx.sessionId,\n TITAN_AGENT_ID: ctx.agentId || 'default',\n TITAN_ROUND: String(round),\n });\n }\n\n switch (phase) {\n\n // ══════════��══════════════════════════���═════════════════════\n // THINK PHASE — Call LLM with tools available\n // ══════════════════════════════════════════���════════════════\n case 'think': {\n // ── Reflection (autonomous mode, every N rounds) ──────\n if (ctx.reflectionEnabled && round > 0 && shouldReflect(round, ctx.reflectionInterval)) {\n try {\n const lastToolResult = ctx.messages.filter(m => m.role === 'tool').slice(-1)[0]?.content || '';\n const failedContext = failedApproaches.length > 0 ? failedApproaches.join('; ') : undefined;\n const reflectionResult = await reflect(round, result.toolsUsed, ctx.message, lastToolResult, failedContext);\n\n if (reflectionResult.decision === 'stop') {\n logger.info(COMPONENT, `Reflection says stop at round ${round + 1}: ${reflectionResult.reasoning}`);\n ctx.messages.push({ role: 'user', content: `You've reflected on your progress and decided you have enough information. Respond to the user now with your findings. Reasoning: ${sanitizeReflection(reflectionResult.reasoning)}` });\n phase = 'respond';\n continue;\n } else if (reflectionResult.decision === 'pivot' && pivotCount < MAX_PIVOTS) {\n pivotCount++;\n const toolsSummary = [...new Set(result.toolsUsed)].join(', ');\n const approachSummary = `Attempted tools: ${toolsSummary}. Result: ${sanitizeReflection(reflectionResult.reasoning)}`;\n failedApproaches.push(approachSummary);\n logger.info(COMPONENT, `PIVOT at round ${round + 1}: ${reflectionResult.reasoning}`);\n\n // Clear accumulated tool results but keep system prompt + original message\n const systemMsg = ctx.messages.find(m => m.role === 'system');\n const userMsg = ctx.messages.find(m => m.role === 'user' && !m.content.startsWith('['));\n ctx.messages.length = 0;\n if (systemMsg) ctx.messages.push(systemMsg);\n if (userMsg) ctx.messages.push(userMsg);\n ctx.messages.push({ role: 'user', content: `Warning: STRATEGIC PIVOT: Your previous approach failed.\\nWhat was tried: ${approachSummary}\\nWhy it failed: ${sanitizeReflection(reflectionResult.reasoning)}\\n\\nTry a COMPLETELY DIFFERENT strategy. Do NOT repeat the same tools or approach.` });\n\n resetProgress();\n result.toolsUsed.length = 0;\n result.orderedToolSequence.length = 0;\n } else if (reflectionResult.decision === 'pivot' && pivotCount >= MAX_PIVOTS) {\n // Pivot limit reached — inject guidance instead of silently ignoring\n logger.warn(COMPONENT, `Pivot limit reached (${MAX_PIVOTS}), injecting adjustment instead`);\n ctx.messages.push({ role: 'user', content: `Your approach isn't working but you've already pivoted ${MAX_PIVOTS} time(s). Instead of starting over, try a SMALL adjustment: ${sanitizeReflection(reflectionResult.reasoning)}. Focus on what's most likely to succeed with the tools you have.` });\n } else if (reflectionResult.decision === 'adjust') {\n ctx.messages.push({ role: 'user', content: `Reflection suggests adjusting approach: ${sanitizeReflection(reflectionResult.reasoning)}. Try a different strategy.` });\n }\n // 'continue' → no injection, just keep going\n } catch (e) {\n logger.warn(COMPONENT, `Reflection failed, continuing: ${(e as Error).message}`);\n }\n }\n\n // ── Graceful degradation near round limit ─────────────\n if (round >= ctx.effectiveMaxRounds - 2 && round >= 3) {\n ctx.messages.push({\n role: 'user',\n content: `We're running short on time for this task. Please summarize what you've accomplished so far and give the user a clear answer.`,\n });\n phase = 'respond';\n continue;\n }\n\n // ── Context compression ─────��────────────────────────\n let smartMessages: ChatMessage[];\n if (ctx.voiceFastPath) {\n smartMessages = ctx.messages as ChatMessage[];\n } else {\n // Hunt Finding #14 (2026-04-14): previously this filter dropped ALL tool\n // messages on round 3+, leaving their parent assistant-with-tool_calls\n // messages orphaned. validateToolPairs would then either synthesize\n // placeholders (history preserved but content lost) or drop assistants\n // (history destroyed). Either way, the model lost data and made bad\n // decisions (e.g., writing empty files).\n //\n // Use 5-phase smart context builder only — avoids double-compression\n // that destroyed history when maybeCompressContext + buildSmartContext\n // both ran sequentially (the former mutated ctx.messages in-place).\n smartMessages = buildSmartContext(ctx.messages as ChatMessage[], tokenBudget);\n }\n\n // ── Response cache check ─────────────────────────────\n const cachedResponse = getCachedResponse(smartMessages, activeModel);\n if (cachedResponse) {\n logger.info(COMPONENT, `Cache hit — skipping LLM call`);\n result.content = cachedResponse;\n phase = 'done';\n break;\n }\n\n // ── Per-turn model routing ───────────────────────────\n const lastUserContent = ctx.message || '';\n const recentAssistant = smartMessages.filter(m => m.role === 'assistant').slice(-1)[0]?.content || '';\n const turnCtx: TurnContext = {\n round,\n messageLength: lastUserContent.length,\n hasCode: /```|\\bfunction\\b|\\bclass\\b|\\bconst\\b|\\bdef\\b/.test(recentAssistant),\n hasUrls: /https?:\\/\\//.test(recentAssistant),\n };\n const routeResult = routeModel(lastUserContent, activeModel, undefined, turnCtx);\n if (routeResult.model !== activeModel && routeResult.willSaveMoney) {\n logger.info(COMPONENT, `[PerTurnRoute] Round ${round}: ${activeModel} → ${routeResult.model} (${routeResult.reason})`);\n activeModel = routeResult.model;\n }\n\n // ── Call LLM with tools ──────────────────────────────\n const thinkingMode = ctx.thinkingOverride || ctx.config.agent.thinkingMode || 'off';\n const isVoice = ctx.voiceFastPath;\n // Claude Code-style context management:\n // 1. Clear old tool results (keep last 5 in full, truncate older ones)\n // 2. Trim to recent messages if context is getting large\n if (smartMessages.length > 6) {\n // Collapse deliberation messages to prevent plan markdown from polluting context\n for (let i = smartMessages.length - 1; i >= 0; i--) {\n const msg = smartMessages[i];\n if (msg.role === 'assistant' && msg.content?.startsWith('[DELIBERATION]')) {\n smartMessages[i] = { ...msg, content: '[Prior deliberation plan — details omitted for brevity]' };\n continue; // don't count as tool result\n }\n }\n let toolResultCount = 0;\n for (let i = smartMessages.length - 1; i >= 0; i--) {\n const msg = smartMessages[i];\n if (msg.role === 'tool' || (msg.role === 'assistant' && msg.toolCalls)) {\n toolResultCount++;\n if (toolResultCount > 5 && msg.content) {\n // Truncate ALL old tool results outside keep-5 window (regardless of length)\n smartMessages[i] = { ...msg, content: '[Earlier tool result cleared — ' + msg.content.slice(0, 80) + '...]' };\n }\n }\n }\n }\n\n // Hard trim if too many messages — Hunt Finding #09: pair-aware trim\n // preserves tool_call/tool_result pairs so validateToolPairs doesn't\n // then drop assistant messages with orphaned tool calls.\n if (smartMessages.length > 12 && phase !== 'respond') {\n const before = smartMessages.length;\n smartMessages = trimPairAware(smartMessages, 12);\n // A11: Sanitize any remaining orphans (safety net, should be zero)\n smartMessages = validateToolPairs(smartMessages);\n if (smartMessages.length !== before) {\n logger.info(COMPONENT, `[ContextTrim] Pair-aware trim: ${before} → ${smartMessages.length} messages`);\n }\n }\n\n // A1: Validate tool call/result pairing before sending to LLM (LangGraph pattern)\n smartMessages = validateToolPairs(smartMessages);\n\n // F1: Pre-model hook — let plugins modify messages for LLM without changing ctx.messages\n if (ctx.beforeModelCall) {\n try {\n smartMessages = ctx.beforeModelCall([...smartMessages], round);\n } catch (e) {\n logger.warn(COMPONENT, `[PreModelHook] Hook threw: ${(e as Error).message} — using unmodified messages`);\n }\n }\n\n // Hunt Finding #38 (2026-04-15): for chat-pipeline messages that\n // complete in a single think-phase round, the respond-phase\n // directive (Finding #21) never fires — the model's raw think\n // output goes directly to the user (streaming, so the tokens are\n // already on screen) and the sanitizer catches narrator leaks at\n // the END by which point it's too late. Inject the directive\n // into the context BEFORE the first think call for chat pipelines\n // so the model's FIRST token is already post-directive.\n if (round === 0 && phase === 'think' && ctx.completionStrategy === 'single-round') {\n smartMessages = [\n ...smartMessages,\n {\n role: 'user' as const,\n content: '[System directive for this reply only] Respond directly to the user. RULES: (1) Do NOT narrate what the user asked — they already know. (2) Do NOT describe your reasoning, thinking, or what you\\'re about to do. (3) Do NOT start with \"The user asked\", \"Let me\", \"I should\", \"I\\'ll\", \"Actually\", \"Looking at\" — start with the actual answer. (4) Be brief and friendly. 1-3 sentences is usually enough. (5) No meta-commentary. Just the answer.',\n },\n ];\n }\n\n const chatOptions = {\n model: activeModel,\n messages: smartMessages,\n tools: ctx.activeTools.length > 0 ? ctx.activeTools : undefined,\n maxTokens: isVoice ? Math.min(ctx.config.agent.maxTokens, 300) : ctx.config.agent.maxTokens,\n temperature: ctx.config.agent.temperature,\n thinking: isVoice ? false : thinkingMode !== 'off',\n thinkingLevel: thinkingMode as 'off' | 'low' | 'medium' | 'high',\n forceToolUse: (\n // Hunt Finding #08 (2026-04-14): Only force tool_choice=required\n // on ROUND 0. After round 0, the model has tool results in context\n // and should be free to generate text OR call more tools as needed.\n // Previously, autonomous mode forced tools on every round, causing\n // ping-pong loops: model reads file → forced to call another tool →\n // calls shell → forced again → calls memory → loop detector kills it.\n // Only force when we haven't given the model a chance to finish.\n round === 0\n && ctx.activeTools.length > 0\n && (ctx.isAutonomous || ctx.taskEnforcementActive)\n && (ctx.config.agent as Record<string, unknown>).forceToolUse !== false\n && phase !== 'respond'\n // Hunt Finding #07: don't force tools on chat-pipeline messages\n && ctx.completionStrategy !== 'single-round'\n && ctx.pipelineType !== 'chat')\n || forceWriteOnNextThink // Incomplete task guard — specific retry\n // Hunt Finding #05: user explicitly asked to use a tool → force round 0\n || (round === 0\n && phase === 'think'\n && ctx.activeTools.length > 0\n && detectToolUseIntent(ctx.message || '')),\n providerOptions: ctx.providerOptions,\n };\n if (forceWriteOnNextThink) {\n forceWriteOnNextThink = false; // Reset after use\n logger.info(COMPONENT, '[IncompleteTask] Forcing tool_choice=required for write retry');\n }\n // Hunt Finding #05: log when explicit-intent is forcing tool use\n if (round === 0 && phase === 'think' && !ctx.isAutonomous && !ctx.taskEnforcementActive\n && ctx.activeTools.length > 0 && detectToolUseIntent(ctx.message || '')) {\n logger.info(COMPONENT, '[ExplicitIntent] User explicitly requested tool use — forcing tool_choice=required for round 1');\n }\n\n // Prompt budget check (Space Agent parity)\n const budgetMsg = budgetConfig.maxTokens > 0 ? checkBudget(ctx.sessionId, budgetConfig) : null;\n if (budgetMsg) {\n markExceeded(ctx.sessionId);\n result.content = budgetMsg;\n phase = 'done';\n break;\n }\n\n let response: ChatResponse;\n const thinkStart = Date.now();\n // Hunt Finding #38b (2026-04-15): for single-round chat pipelines,\n // the model's round-0 think output IS the user-facing answer (no\n // respond phase ever runs). With live SSE streaming, the raw\n // narrator tokens from a weak model hit the UI before the\n // sanitizer can run at the end — the user sees \"The user wants a\n // joke. I can respond directly without needing any tools. Why\n // don't scientists trust atoms?...\" on screen. Fix: DO NOT stream\n // chat-pipeline round-0 think output. Collect the full response,\n // run it through the sanitizer, THEN emit as a single block.\n // The client shows a typing indicator during the short wait\n // instead of streaming raw narrator tokens.\n const isChatRound0Think =\n round === 0\n && phase === 'think'\n && ctx.completionStrategy === 'single-round';\n\n if (ctx.streamCallbacks?.onToken && !isChatRound0Think) {\n let streamContent = '';\n const streamToolCalls: ToolCall[] = [];\n const smartMessagesJson = JSON.stringify(smartMessages); // cache for token estimation\n for await (const chunk of chatStream(chatOptions)) {\n if (chunk.type === 'text' && chunk.content) {\n streamContent += chunk.content;\n ctx.streamCallbacks.onToken(chunk.content);\n } else if (chunk.type === 'tool_call' && chunk.toolCall) {\n streamToolCalls.push(chunk.toolCall);\n ctx.streamCallbacks.onToolCall?.(chunk.toolCall.function.name, JSON.parse(chunk.toolCall.function.arguments || '{}'));\n } else if (chunk.type === 'error') {\n logger.error(COMPONENT, `Stream error: ${chunk.error}`);\n } else if (chunk.type === 'retry') {\n // Out-of-band status — log internally + surface via callback.\n // CRITICAL: do NOT append to streamContent. The router used to\n // leak retry banners (\"[Retrying request (1/4)...]\") into the\n // text stream; the dedicated chunk type and this isolated\n // branch are the fix.\n logger.warn(COMPONENT, `Stream retrying ${chunk.provider}/${chunk.model} (attempt ${chunk.attempt}/${chunk.maxRetries}, reason=${chunk.reason}, delay=${chunk.delayMs}ms)`);\n ctx.streamCallbacks.onRetry?.({\n attempt: chunk.attempt,\n maxRetries: chunk.maxRetries,\n reason: chunk.reason,\n provider: chunk.provider,\n model: chunk.model,\n delayMs: chunk.delayMs,\n });\n } else if (chunk.type === 'failover') {\n // Same isolation rule — failover banners must not leak into the\n // assistant's response text.\n logger.warn(COMPONENT, `Stream failover from ${chunk.originalProvider}/${chunk.originalModel}${chunk.error ? ` (${chunk.error})` : ''}`);\n ctx.streamCallbacks.onFailover?.({\n originalProvider: chunk.originalProvider,\n originalModel: chunk.originalModel,\n error: chunk.error,\n });\n }\n }\n // Estimate token counts for streaming (servers don't report usage in stream mode)\n const estCompletionTokens = estimateTokens(streamContent + JSON.stringify(streamToolCalls));\n const estPromptTokens = estimateTokens(smartMessagesJson);\n response = {\n id: `stream-${Date.now()}`,\n content: streamContent,\n toolCalls: streamToolCalls.length > 0 ? streamToolCalls : undefined,\n usage: { promptTokens: estPromptTokens, completionTokens: estCompletionTokens, totalTokens: estPromptTokens + estCompletionTokens },\n finishReason: streamToolCalls.length > 0 ? 'tool_calls' : 'stop',\n model: activeModel,\n };\n // v5.0: OTEL span for think-phase LLM call (streaming)\n fireSpan(traceCtx, 'model_call:think', Date.now() - thinkStart, { round: String(round), model: activeModel, phase: 'think', streamed: true });\n } else {\n // Non-streaming path. For chat-pipeline round-0 think we\n // deliberately come here so the sanitizer gets to see the\n // full response before the client does. We also strip any\n // leading narrator preamble that the model emitted despite\n // our respond directive (Hunt Finding #38b).\n response = await chatWithTimeout(() => chat(chatOptions), 'agentLoop:chat', 300_000);\n if (isChatRound0Think && response.content) {\n const stripped = stripNarratorPreamble(response.content);\n if (stripped !== response.content) {\n logger.info(COMPONENT, `[NarratorStrip] Removed ${response.content.length - stripped.length} chars of narrator preamble from chat response`);\n response.content = stripped;\n }\n }\n\n // Secret exfiltration guard — scan LLM response before it reaches user\n if (response.content) {\n const { redacted, clean } = scanForSecrets(response.content);\n if (!clean) {\n logger.warn(COMPONENT, 'Secrets detected in LLM response — redacted before delivery');\n response.content = redacted;\n }\n // v5.0: Full exfiltration scan (layer 2-5) when configured\n if (ctx.config.security?.secretScan?.level === 'full') {\n const scan = fullExfilScan(response.content, 'llm_response');\n if (scan.blocked) {\n logger.warn(COMPONENT, `Exfiltration scan blocked LLM response: ${scan.findings.map(f => f.type).join(', ')}`);\n }\n response.content = scan.redacted;\n }\n }\n // v5.0: OTEL span for think-phase LLM call\n fireSpan(traceCtx, 'model_call:think', Date.now() - thinkStart, { round: String(round), model: activeModel, phase: 'think' });\n }\n\n result.modelUsed = response.model;\n const promptTokens = response.usage?.promptTokens || 0;\n const completionTokens = response.usage?.completionTokens || 0;\n result.promptTokens += promptTokens;\n result.completionTokens += completionTokens;\n\n // ── Cost tracking (F5: two-tier budget enforcement) ──\n const costCheck = recordTokenUsage(ctx.sessionId, activeModel, promptTokens, completionTokens);\n\n // Prompt budget tracking (Space Agent parity)\n if (budgetConfig.maxTokens > 0) {\n recordUsage(ctx.sessionId, promptTokens, completionTokens);\n }\n\n // Wire LLM spend into Command Post budget policies\n const callCost = calculateActualCost(activeModel, { prompt: promptTokens, completion: completionTokens });\n const sessionGoal = getSessionGoal(ctx.sessionId);\n recordSpend(ctx.agentId || 'default', sessionGoal?.goalId, callCost);\n\n if (costCheck.budgetExceeded) {\n result.content = '⚠️ Daily spending limit reached. TITAN has paused to keep your API costs under control. You can increase the limit in settings or wait until tomorrow.';\n phase = 'done';\n break;\n }\n // F5: Soft warning at 80% — tell the LLM to wrap up efficiently\n if (costCheck.budgetWarning && !budgetWarningSent) {\n budgetWarningSent = true;\n ctx.messages.push({\n role: 'user',\n content: `[System: ⚠️ Budget notice — you've used 80%+ of today's spending limit ($${costCheck.dailyTotal.toFixed(4)}). Be efficient: avoid unnecessary tool calls, summarize when possible, and wrap up soon.]`,\n });\n logger.info(COMPONENT, `[BudgetSoftWarning] Injected 80% budget warning into conversation`);\n }\n\n heartbeat(ctx.sessionId);\n recordActivity();\n\n // v5.0: Secret exfiltration v2 — full scan on LLM responses when enabled\n const secretScanLevel = ctx.config.security?.secretScan?.level ?? 'tool_only';\n if (secretScanLevel === 'full' && response.content) {\n const scan = fullExfilScan(response.content, 'llm_response');\n if (scan.blocked) {\n logger.warn(COMPONENT, `Full exfil scan found issues in LLM response: ${scan.findings.map(f => f.type).join(', ')}`);\n response.content = scan.redacted;\n }\n }\n\n // ── No tool calls → check rescue paths or accept response ──\n if (!response.toolCalls || response.toolCalls.length === 0) {\n logger.warn(COMPONENT, `[NoTools] Model returned text (len=${response.content.length}): ${response.content.slice(0, 200)}`);\n\n // FabricationGuard: detect model claiming to have completed actions without tool calls\n // gemma4 says \"I've written X to file Y\" without actually calling write_file\n //\n // Hunt Finding #47 (2026-04-15): this guard was DESTROYING correctly\n // written files. When the model summarized \"all results were written\n // to /tmp/foo.txt\", the regex matched, content extraction failed, and\n // the file was overwritten with \"placeholder\" — nuking the real 198-byte\n // output. Fix: skip if the file already exists with >0 bytes (the write\n // already succeeded in a prior round). Also require an explicit content\n // extract — never fall back to \"placeholder\".\n const fabricationMatch = response.content.match(/(?:written|saved|created|wrote)\\s+(?:.*?)(?:to|at|in)\\s+[\"'`]?(\\/[\\w/.-]+\\.[a-z]+)[\"'`]?/i);\n if (fabricationMatch) {\n const filePath = fabricationMatch[1];\n // Check if the file already exists — if so, the write already\n // succeeded in a previous round and this is just the model\n // summarizing what it did. Don't overwrite.\n let fileAlreadyExists = false;\n try {\n const { existsSync, statSync } = await import('fs');\n fileAlreadyExists = existsSync(filePath) && statSync(filePath).size > 0;\n } catch { /* can't check, assume not */ }\n\n if (fileAlreadyExists) {\n logger.info(COMPONENT, `[FabricationGuard] File \"${filePath}\" already exists (${fileAlreadyExists ? 'has content' : 'empty'}) — skipping forced write (Hunt #47)`);\n } else {\n // Extract what should have been written — require explicit content\n const contentMatch = response.content.match(/(?:written|saved|wrote)\\s+[\"`]([^\"`]+)[\"`]/i);\n if (contentMatch) {\n const fileContent = contentMatch[1];\n logger.warn(COMPONENT, `[FabricationGuard] Model claimed to write \"${filePath}\" without tool call — forcing write_file`);\n response.toolCalls = [{\n id: `fab-${Date.now()}`,\n type: 'function' as const,\n function: { name: 'write_file', arguments: JSON.stringify({ path: filePath, content: fileContent }) },\n }];\n response.content = '';\n } else {\n logger.warn(COMPONENT, `[FabricationGuard] Model claimed to write \"${filePath}\" but no extractable content — skipping forced write to avoid placeholder damage (Hunt #47)`);\n }\n }\n }\n // Self-Heal: detect tool calling failure\n if (ctx.selfHealEnabled && !selfHealExhausted && ctx.activeTools.length > 0) {\n const toolFailure = checkToolCallCapability(ctx.sessionId, response.content, ctx.activeTools.length > 0);\n if (toolFailure) {\n const fallback = findToolCapableFallback(activeModel, failedModels, ctx.config);\n if (fallback) {\n logger.warn(COMPONENT, `[SelfHeal] ${activeModel} failed tool calling. Switching to ${fallback}`);\n failedModels.add(activeModel);\n activeModel = fallback;\n result.modelUsed = fallback;\n modelSwitchCount++;\n if (modelSwitchCount >= MAX_MODEL_SWITCHES) selfHealExhausted = true;\n resetToolCallFailures(ctx.sessionId);\n ctx.messages.push({ role: 'user', content: `Note: I'm now using a different assistant engine. Please continue with your task.` });\n // Stay in think phase — retry with new model\n continue;\n } else if (modelSwitchCount > 0) {\n selfHealExhausted = true;\n result.content = \"I'm having trouble getting that done right now. Running the built-in health check (titan doctor) or picking a different AI model in Settings usually fixes this.\";\n phase = 'done';\n break;\n }\n }\n }\n\n // ActionCompiler: if model output ACTION: directives, compile them to tool calls\n if (response.content && hasActionDirectives(response.content)) {\n const compiled = compileActions(response.content);\n if (compiled.length > 0) {\n logger.info(COMPONENT, `[ActionCompiler] Compiled ${compiled.length} actions from text`);\n // Execute first action, queue the rest\n const first = compiled[0];\n response.toolCalls = [{\n id: `ac-${Date.now()}`,\n type: 'function' as const,\n function: { name: first.tool, arguments: JSON.stringify(first.args) },\n }];\n response.content = '';\n // Store remaining actions for subsequent rounds\n if (compiled.length > 1) {\n const remaining = compiled.slice(1).map((a, i) => `ACTION: ${a.tool} ${a.args.path || a.args.command || ''}`).join('\\n');\n ctx.messages.push({ role: 'user', content: `[Queued actions]\\n${remaining}\\nExecute the next ACTION.` });\n }\n }\n }\n\n // IntentParser: aggressively extract tool calls from text content\n // Models like gemma4 often describe what they WANT to do instead of calling tools.\n // We parse the intent and generate the tool call for them.\n if (!response.toolCalls || response.toolCalls.length === 0) {\n const text = response.content;\n let rescued = false;\n\n // Pattern 1: Code blocks → write_file\n const codeBlockMatch = text.match(/```(?:html|typescript|javascript|python|css|json)\\n([\\s\\S]+?)```/);\n if (codeBlockMatch && codeBlockMatch[1].length > 50) {\n const pathMatch = text.match(/(?:save|write|create|update|file|path|to)[:\\s]+[\"'`]?(\\/[\\w/.-]+\\.[a-z]+)[\"'`]?/i)\n || text.match(/(\\/home\\/[\\w/.-]+\\.[a-z]+)/)\n || text.match(/(\\/[\\w]+\\/[\\w/.-]+\\.[a-z]+)/);\n if (pathMatch) {\n logger.info(COMPONENT, `[IntentParser] Code block → write_file(\"${pathMatch[1]}\", ${codeBlockMatch[1].length} chars)`);\n response.toolCalls = [{\n id: `intent-${Date.now()}`,\n type: 'function' as const,\n function: { name: 'write_file', arguments: JSON.stringify({ path: pathMatch[1], content: codeBlockMatch[1] }) },\n }];\n rescued = true;\n }\n }\n\n // Pattern 1b: \"I wrote/saved/created file X\" → write_file (past-tense fabrication)\n if (!rescued) {\n const pastWrite = text.match(/(?:wrote|written|saved|created)\\s+(?:.*?)(?:to|at|in)\\s+[\"'`]?(\\/[\\w/.-]+\\.[a-z]+)[\"'`]?/i);\n if (pastWrite) {\n const contentMatch = text.match(/(?:wrote|written|saved)\\s+[\"'`]([^\"'`]+)[\"'`]/i);\n logger.info(COMPONENT, `[IntentParser] Past-tense write → write_file(\"${pastWrite[1]}\")`);\n response.toolCalls = [{\n id: `intent-${Date.now()}`,\n type: 'function' as const,\n function: { name: 'write_file', arguments: JSON.stringify({ path: pastWrite[1], content: contentMatch ? contentMatch[1] : '' }) },\n }];\n rescued = true;\n }\n }\n\n // Pattern 2: \"I'll read/open/check file X\" → read_file\n if (!rescued) {\n const readIntent = text.match(/(?:read|open|check|look at|examine|view|inspect)\\s+(?:the\\s+)?(?:file\\s+)?[\"'`]?(\\/[\\w/.-]+\\.[a-z]+)[\"'`]?/i);\n if (readIntent) {\n logger.info(COMPONENT, `[IntentParser] Read intent → read_file(\"${readIntent[1]}\")`);\n response.toolCalls = [{\n id: `intent-${Date.now()}`,\n type: 'function' as const,\n function: { name: 'read_file', arguments: JSON.stringify({ path: readIntent[1] }) },\n }];\n rescued = true;\n }\n }\n\n // Pattern 3: \"I'll run/execute command X\" → shell\n if (!rescued) {\n const shellIntent = text.match(/(?:run|execute|running)\\s+(?:the\\s+)?(?:command\\s+)?[`]([^`]+)[`]/i);\n if (shellIntent) {\n logger.info(COMPONENT, `[IntentParser] Shell intent → shell(\"${shellIntent[1].slice(0, 60)}\")`);\n response.toolCalls = [{\n id: `intent-${Date.now()}`,\n type: 'function' as const,\n function: { name: 'shell', arguments: JSON.stringify({ command: shellIntent[1] }) },\n }];\n rescued = true;\n }\n }\n\n // Pattern 4: \"I'll edit/modify/update X in file Y\" → read_file (to prepare for edit)\n if (!rescued) {\n const editIntent = text.match(/(?:edit|modify|update|change|replace|add to)\\s+(?:the\\s+)?(?:file\\s+)?[\"'`]?(\\/[\\w/.-]+\\.[a-z]+)[\"'`]?/i);\n if (editIntent) {\n logger.info(COMPONENT, `[IntentParser] Edit intent → read_file(\"${editIntent[1]}\") (prep for edit)`);\n response.toolCalls = [{\n id: `intent-${Date.now()}`,\n type: 'function' as const,\n function: { name: 'read_file', arguments: JSON.stringify({ path: editIntent[1] }) },\n }];\n rescued = true;\n }\n }\n\n if (rescued) {\n // Clear the text content so it doesn't confuse the model on next round\n response.content = '';\n }\n }\n\n // ToolRescue: final attempt to extract a tool call from text content\n if (!response.toolCalls || response.toolCalls.length === 0) {\n const isCloudModel = activeModel.includes(':cloud') || activeModel.includes('-cloud');\n const rescuedToolCall = extractToolCallFromContent(response.content, ctx.activeTools, isCloudModel);\n if (rescuedToolCall) {\n logger.info(COMPONENT, `[ToolRescue] Extracted \"${rescuedToolCall.function.name}\" from content text`);\n response.toolCalls = [rescuedToolCall];\n // Fall through to tool_calls handling below\n }\n }\n\n // Hunt Finding #17 (2026-04-14): UserIntentRescue — when the model\n // ignores tool_choice=required AND all model-response-based rescue\n // paths fail, extract the intended tool call from the USER MESSAGE\n // directly. This catches the case where a weak model fabricates\n // plausible-sounding tool output (e.g. \"Permission denied\",\n // \"command returned null\") instead of actually running the tool.\n if (!response.toolCalls || response.toolCalls.length === 0) {\n const userIntent = extractToolCallFromUserMessage(ctx.message || '', ctx.activeTools);\n if (userIntent) {\n logger.warn(COMPONENT, `[UserIntentRescue] Model ignored tool_choice=required; extracting \"${userIntent.function.name}\" from user message`);\n response.toolCalls = [userIntent];\n response.content = '';\n // Fall through to tool_calls handling below\n }\n }\n\n // If ALL rescue paths failed (still no tool calls), run stall detection\n // and either nudge for retry or accept the text response. Without this\n // branch, an empty-toolCalls THINK round would fall through with phase\n // still 'think' and round un-incremented → infinite retry loop.\n if (!response.toolCalls || response.toolCalls.length === 0) {\n noToolsRetryCount++;\n\n // Stall detection\n const stallEvent = checkResponse(ctx.sessionId, response.content, round, ctx.effectiveMaxRounds);\n if (stallEvent) {\n const state = stallEvent as { nudgeCount?: number };\n const nudgeCount = state.nudgeCount ?? 0;\n\n // Hard kill after 2 nudges (Paperclip pattern: bounded retries)\n if (nudgeCount >= 2 || stallEvent.type === 'self_talk') {\n logger.error(COMPONENT, `[HardKill] Stall type \"${stallEvent.type}\" after ${nudgeCount} nudges — terminating`);\n result.content = result.content || pendingAssistantContent || 'Task terminated: agent was unable to make progress using tools. Please try rephrasing your request or breaking it into smaller steps.';\n phase = 'done';\n break;\n }\n\n const nudge = getNudgeMessage(stallEvent);\n logger.warn(COMPONENT, `Stall [${stallEvent.type}] (nudge ${nudgeCount + 1}/2) — nudging`);\n ctx.messages.push({ role: 'user', content: nudge });\n round++;\n // Stay in think phase — retry\n continue;\n }\n\n // No stall detected — bail out after 3 [NoTools] rounds in a row\n // to prevent the model spinning forever returning text instead of tools\n if (noToolsRetryCount >= 3) {\n // Gap 2 (plan-this-logical-ocean): if this run DID execute\n // tools earlier and is now stuck in no-tools land, check\n // the bounded continuation counter before bailing. This\n // is the \"empty_after_tools\" signal — the model forgot\n // what it was doing mid-task. One continuation nudge is\n // cheap; the counter persists across process restarts so\n // we can't loop forever.\n const didUseTools = result.toolsUsed.length > 0;\n if (didUseTools) {\n const { shouldContinue } = await import('./runContinuations.js');\n if (shouldContinue(ctx.sessionId, 'empty_after_tools')) {\n logger.warn(COMPONENT, `[Continuation] empty_after_tools — nudging once more (session ${ctx.sessionId})`);\n ctx.messages.push({\n role: 'user',\n content: \"You started on a task but didn't finish. Please continue with the next step, or if you're done, give the user a clear answer now.\",\n });\n noToolsRetryCount = 0; // fresh window after the continuation\n round++;\n continue;\n }\n }\n logger.warn(COMPONENT, `[NoTools] Bailing after ${noToolsRetryCount} consecutive no-tool rounds — accepting text response`);\n result.content = stripToolJson(response.content || pendingAssistantContent || 'I was unable to make progress using tools.');\n phase = 'done';\n break;\n }\n\n // In autonomous mode: if the model describes work instead of doing it,\n // push it back to use tools. This is the key behavioral pattern that\n // makes autonomous execution reliable — don't accept \"I would do X\"\n // when the model should be calling tools to actually do X.\n if (ctx.isAutonomous && round < ctx.effectiveMaxRounds - 2) {\n // Hunt Finding #10 (2026-04-14): the previous regexes over-matched.\n // `|The` with no word boundary matched \"These\"/\"This\"/\"Then\"/\"There\"/\n // \"They\" — all common ways to start valid descriptive answers. And\n // describesWork only needed one weak indicator. Result: a correct\n // answer starting with \"These represent two classic attack categories...\"\n // got nudged to \"call a tool\", then the model emitted meta-commentary\n // that was accepted as the final answer.\n //\n // New rules (tight):\n // 1. futureIntentOpener requires an EXPLICIT future-action phrase\n // with a following verb (\"Let me VERB\", \"I'll VERB\", \"I will VERB\",\n // \"I need to VERB\"). Common openers like \"The\"/\"These\"/\"This\"/\n // \"Based on\"/\"Here's\" no longer trigger.\n // 2. describesWork also requires \"I'll/I will/I need to/Let me\"\n // followed by an actual work verb nearby.\n // 3. BOTH must match to fire — one weak match isn't enough.\n const futureIntentOpener = /^(let me\\s+\\w+|I['']?ll\\s+(?:start|begin|check|look|read|run|edit|write|create|try|go|investigate|verify|test|install|build|fix|update|change|set)|I\\s+(?:will|need to|should|can|am going to|plan to)\\s+\\w+|first,?\\s+I|now\\s+I|to\\s+(?:fix|resolve|complete|edit|write|create|update|change|run))\\b/i.test(response.content.trim());\n const describesWork = /\\b(?:I['']?ll|I (?:will|need to|should|plan to|am going to)|let me)\\b[^.]{0,80}\\b(?:fix|edit|change|update|create|write|modify|run|install|build|start|restart|read|open|debug|set up|check|look at|examine|investigate|verify|confirm|test)\\b/i.test(response.content);\n\n // BOTH must match to fire — prevents false positives on valid\n // descriptive answers that don't actually describe future work.\n if (futureIntentOpener && describesWork && noToolsRetryCount < 3) {\n noToolsRetryCount++;\n logger.info(COMPONENT, `[AutoPush] Model described intent to act without acting (${noToolsRetryCount}/3): \"${response.content.slice(0, 80)}...\"`);\n ctx.messages.push({ role: 'assistant', content: response.content });\n ctx.messages.push({ role: 'user', content: 'STOP describing. Call a tool RIGHT NOW. Use edit_file to fix code, write_file to create files, or shell to run commands. Your next response MUST be a tool call, not text.' });\n round++;\n continue;\n }\n }\n\n // Hunt Finding #05 (continuation): before accepting a text-only\n // response as final, check if it looks like fabricated tool output.\n // If the user requested verbatim/exact tool output AND we have\n // real tool results in this turn, prefer the real result.\n let finalText = stripToolJson(response.content || '');\n const lastToolResult = result.toolCallDetails.length > 0\n ? result.toolCallDetails[result.toolCallDetails.length - 1]\n : null;\n const userMsgLower = (ctx.message || '').toLowerCase();\n const wantsVerbatim = /\\b(?:verbatim|exactly|precise|literal|as(?: |-)is|raw output|just the output)\\b/.test(userMsgLower);\n\n if (lastToolResult && lastToolResult.success && wantsVerbatim && finalText.length < 300) {\n // Check if the text appears in the actual tool output — if NOT,\n // it's likely a hallucinated echo of what the model thinks the\n // tool should have returned. Replace with the real output.\n const realOutput = lastToolResult.resultSnippet || '';\n const textAppearsInResult = realOutput.length > 10 && realOutput.toLowerCase().includes(finalText.toLowerCase().slice(0, 40));\n if (!textAppearsInResult && realOutput.length > 0) {\n logger.warn(COMPONENT, `[HallucinationGuard] Text response \"${finalText.slice(0, 60)}...\" does NOT match real tool output \"${realOutput.slice(0, 60)}...\" — user asked for verbatim, using real tool output instead`);\n result.content = realOutput.length < 1500 ? realOutput : realOutput.slice(0, 1500) + '...[truncated]';\n setCachedResponse(smartMessages, activeModel, result.content);\n phase = 'done';\n break;\n }\n }\n\n // Auto-emit system widget gates when gallery_get returned a system widget\n // but the model failed to emit _____widget in its response (common with\n // local/cloud models that ignore formatting instructions).\n const galleryGetCalls = result.toolCallDetails.filter(d => d.name === 'gallery_get');\n logger.info(COMPONENT, `[AutoWidgetGate] Checking ${galleryGetCalls.length} gallery_get call(s) for system widgets`);\n for (const call of galleryGetCalls) {\n try {\n const snippet = call.resultSnippet || '';\n logger.info(COMPONENT, `[AutoWidgetGate] gallery_get snippet: ${snippet.slice(0, 200)}`);\n const parsed = JSON.parse(snippet);\n if (parsed.source && typeof parsed.source === 'string' && parsed.source.startsWith('system:')) {\n const gateText = `_____widget\\n{ \"name\": \"${parsed.name || 'Widget'}\", \"format\": \"system\", \"source\": \"${parsed.source}\", \"w\": ${parsed.defaultSize?.w || 6}, \"h\": ${parsed.defaultSize?.h || 6} }`;\n if (!finalText.includes('_____widget')) {\n finalText = finalText ? `${finalText}\\n\\n${gateText}` : gateText;\n logger.info(COMPONENT, `[AutoWidgetGate] Injected _____widget gate for ${parsed.source}`);\n }\n } else {\n logger.info(COMPONENT, `[AutoWidgetGate] Skipped — source is ${parsed.source}`);\n }\n } catch (e) {\n logger.info(COMPONENT, `[AutoWidgetGate] Parse error: ${(e as Error).message}`);\n }\n }\n\n // Model chose to respond directly — accept it\n result.content = finalText;\n setCachedResponse(smartMessages, activeModel, result.content);\n phase = 'done';\n break;\n }\n\n // Tool calls were rescued — reset the no-tools counter since the model is making progress\n noToolsRetryCount = 0;\n }\n\n // A7: Remaining steps guard (LangGraph pattern) — suppress tool calls near budget limit\n const remainingRounds = ctx.effectiveMaxRounds - round;\n if (ctx.isAutonomous && remainingRounds <= 1 && response.toolCalls && response.toolCalls.length > 0) {\n logger.warn(COMPONENT, `[BudgetGuard] Only ${remainingRounds} round(s) left — suppressing ${response.toolCalls.length} tool call(s), forcing text response`);\n response.toolCalls = undefined;\n result.budgetExhausted = true;\n if (!response.content || response.content.trim().length === 0) {\n response.content = pendingAssistantContent || 'I\\'ve used all available rounds. Here is what I accomplished so far.';\n }\n }\n\n // ── Model returned tool calls → transition to ACT ────\n if (response.toolCalls && response.toolCalls.length > 0) {\n pendingToolCalls = response.toolCalls;\n pendingAssistantContent = response.content || '';\n // Add assistant message with tool calls to history\n ctx.messages.push({\n role: 'assistant',\n content: pendingAssistantContent,\n toolCalls: pendingToolCalls,\n });\n phase = 'act';\n }\n break;\n }\n\n // ════════════════════════════���══════════════════════════════\n // ACT PHASE — Execute tool calls, record results\n // ══════════════════════════════════════���════════════════════\n case 'act': {\n logger.info(COMPONENT, `Executing ${pendingToolCalls.length} tool call(s)`);\n\n let toolResults: ToolResult[] = [];\n try {\n if (ctx.isKimiSwarm) {\n for (const tc of pendingToolCalls) {\n if (tc.function.name.startsWith('delegate_to_')) {\n const domainMatch = tc.function.name.match(/delegate_to_(.*)_agent/);\n const domain = (domainMatch ? domainMatch[1] : 'file') as Domain;\n let args;\n try { args = JSON.parse(tc.function.arguments); } catch { args = { instruction: '' }; }\n const startMs = Date.now();\n const resultString = await runSubAgent(domain, args.instruction, activeModel);\n toolResults.push({\n toolCallId: tc.id, name: tc.function.name, content: resultString,\n success: !resultString.includes('Error'), durationMs: Date.now() - startMs,\n });\n }\n }\n } else {\n const toolExecStart = Date.now();\n toolResults = await executeTools(pendingToolCalls, ctx.channel);\n fireSpan(traceCtx, 'tool_execution', Date.now() - toolExecStart, { round: String(round), tools: pendingToolCalls.map(t => t.function.name).join(',') });\n }\n } catch (err) {\n logger.error(COMPONENT, `Tool execution error: ${(err as Error).message}`);\n result.content = 'An error occurred while executing tools. Please try again.';\n phase = 'done';\n break;\n }\n\n // Emit tool results to watcher\n for (const tr of toolResults) {\n ctx.streamCallbacks?.onToolResult?.(\n tr.name, tr.content.slice(0, 500), tr.durationMs || 0,\n !tr.content.toLowerCase().includes('error'),\n tr.diff,\n );\n }\n\n // v4.13 ancestor-extraction (Hermes subdirectory_hints): for each\n // tool call that touched a new directory, append any AGENTS.md /\n // CLAUDE.md / .cursorrules from that directory + its ancestors to\n // the tool RESULT (not the system prompt — we keep prompt cache\n // stable). Guarded by try/catch so hint discovery can never break\n // tool execution.\n try {\n const { getSubdirTracker } = await import('./subdirHints.js');\n const tracker = getSubdirTracker(ctx.sessionId);\n for (let i = 0; i < toolResults.length; i++) {\n const tr = toolResults[i];\n const tc = pendingToolCalls[i];\n if (!tc) continue;\n let args: Record<string, unknown> = {};\n try { args = JSON.parse(tc.function.arguments || '{}'); } catch { /* non-fatal */ }\n const hints = tracker.checkToolCall(tr.name, args);\n if (hints) {\n tr.content = `${tr.content}\\n\\n${hints}`;\n }\n }\n } catch (err) {\n logger.debug(COMPONENT, `[SubdirHints] skipped: ${(err as Error).message}`);\n }\n\n // Sub-agent shortcut: force immediate summary\n // Sub-agent shortcut: sync sub-agents return full results, force RESPOND.\n // Async delegations (CP enabled) return just a confirmation — no shortcut needed.\n const hasSubAgent = toolResults.some(r => r.name === 'spawn_agent');\n const isAsyncDelegation = hasSubAgent && cpEnabled;\n if (hasSubAgent && !isAsyncDelegation) {\n for (const tr of toolResults) {\n result.toolsUsed.push(tr.name);\n const compressed = await compressToolResult(ctx.sessionId, tr.name, tr.toolCallId, tr.content, round);\n const subSuccess = !tr.content.toLowerCase().includes('error:');\n recordStep(ctx.sessionId, round, tr.name, subSuccess, tr.content.slice(0, 100));\n ctx.messages.push({ role: 'tool', content: compressed, toolCallId: tr.toolCallId, name: tr.name });\n }\n ctx.messages.push({ role: 'user', content: 'The sub-agent has completed its task. Summarize the results above and respond to the user. Do NOT call any more tools.' });\n logger.info(COMPONENT, `[SubAgent] spawn_agent completed (sync) — entering respond phase`);\n phase = 'respond';\n round++;\n break;\n }\n\n recordActivity();\n\n // v5.0: Process steer queue — inject mid-run nudges\n const steerQueue = globalSteerQueues.get(ctx.sessionId) ?? ctx.steerQueue ?? [];\n if (steerQueue.length > 0) {\n const steerPrompts = steerQueue.splice(0, steerQueue.length);\n for (const steer of steerPrompts) {\n // ChatMessage doesn't carry metadata in the public type, but the\n // Steer API needs to mark these so logging / replay can tell them\n // apart from genuine user turns. Cast through Record so the message\n // still serializes cleanly to providers that ignore extra keys.\n ctx.messages.push({\n role: 'user',\n content: `[Steer] ${steer}`,\n metadata: { steer: true },\n } as unknown as (typeof ctx.messages)[number]);\n logger.info(COMPONENT, `Steer injected: ${steer.slice(0, 100)}`);\n }\n }\n\n // Record tool results and check for loops\n let loopBroken = false;\n for (const tr of toolResults) {\n result.toolsUsed.push(tr.name);\n result.orderedToolSequence.push(tr.name);\n\n // Trajectory compression: shorten long tool results + record progress step\n const compressed = await compressToolResult(ctx.sessionId, tr.name, tr.toolCallId, tr.content, round);\n recordStep(ctx.sessionId, round, tr.name, !tr.content.toLowerCase().includes('error:'), tr.content.slice(0, 100));\n ctx.messages.push({ role: 'tool', content: compressed, toolCallId: tr.toolCallId, name: tr.name });\n\n // Stall detector\n const matchingTc = pendingToolCalls.find(tc => tc.id === tr.toolCallId);\n let tcArgs: Record<string, unknown> = {};\n try { tcArgs = JSON.parse(matchingTc?.function.arguments || '{}'); } catch { /* empty */ }\n\n // Soul: record this tool attempt\n recordAttempt(ctx.sessionId, `${tr.name}(${Object.keys(tcArgs).join(',')})`);\n\n // Record structured tool call details for inter-step context in deliberation\n // Hunt Finding #40: AutoVerify needs to flip tcSuccess/tr.success\n // when a write verify fails, so SmartExit doesn't treat the write\n // as terminal-success. Must be `let` for that mutation.\n let tcSuccess = !tr.content.toLowerCase().includes('error:');\n result.toolCallDetails.push({\n name: tr.name,\n args: tcArgs,\n resultSnippet: tr.content.slice(0, 300),\n success: tcSuccess,\n });\n\n // Tool result summarization disabled — was confusing models into\n // treating the summary as the final answer instead of continuing to act.\n // TODO: Re-enable when models handle injected context better.\n\n // Auto-verify file writes — catch silent truncation, empty files, broken HTML/JSON\n // Hunt Finding #40 (2026-04-15): previously AutoVerify only logged\n // a warning and pushed a suggestion. When glm-5.1 wrote to the\n // wrong path (/home/titan/... on a machine where user=dj), the\n // write was silently rejected by validatePath, AutoVerify warned\n // but tr.success stayed TRUE, SmartExit saw \"terminal tool\n // succeeded\" and transitioned to respond — dropping the file\n // entirely. Fix: flip tr.success=false on verify failure, and\n // mark the result with a clear [AutoVerify FAILED] banner so\n // the next think round sees it and retries.\n if (tr.name === 'write_file' || tr.name === 'append_file') {\n const vr = verifyFileWrite(tr.name, tcArgs, tr.content);\n if (!vr.passed) {\n logger.warn(COMPONENT, `[AutoVerify] ${tr.name}: ${vr.issue} — forcing retry (Hunt #40)`);\n // Flip success false so SmartExit doesn't treat this as\n // a terminal-tool success.\n tr.success = false;\n tcSuccess = false;\n // Mutate tr.content so the assistant sees the failure\n // in the tool_result message on the next think round.\n tr.content = `[AutoVerify FAILED] ${vr.issue}. Original tool output: ${tr.content}`;\n ctx.messages.push({\n role: 'user',\n content: `That didn't work as expected: ${vr.issue}${vr.suggestion ? `\\n\\nTry this: ${vr.suggestion}` : ''}\\n\\nPlease try again with the corrected approach.`,\n });\n }\n }\n\n // Shell-for-files nudge: when the model uses shell for file operations,\n // inject a redirect toward dedicated tools\n if (tr.name === 'shell') {\n const cmd = (tcArgs.command as string || '').trim();\n const isFileOp = /^\\s*(cat|head|tail|less|more|sed|awk)\\s+/.test(cmd)\n || /^\\s*grep\\s+.*\\s+\\S+\\.\\w+/.test(cmd)\n || /^\\s*curl\\s+/.test(cmd);\n if (isFileOp) {\n shellForFilesCount++;\n const verb = cmd.split(/\\s+/)[0];\n const nudgeMsg = shellForFilesCount >= 3\n ? `For file operations, please use the dedicated read_file and edit_file tools instead of shell commands. They work more reliably for editing code and text.\\n` +\n `Examples: use read_file instead of cat/head/tail/grep, and edit_file instead of sed/awk.`\n : `For file operations, please use read_file or edit_file instead of shell ${verb}. They are more reliable.`;\n ctx.messages.push({ role: 'user', content: nudgeMsg });\n logger.info(COMPONENT, `[ShellNudge] Shell-for-files detected (count=${shellForFilesCount}): ${cmd.slice(0, 60)}`);\n }\n }\n\n const loopEvent = recordToolCall(ctx.sessionId, tr.name, tcArgs);\n if (loopEvent) {\n const nudge = getNudgeMessage(loopEvent);\n logger.warn(COMPONENT, `Tool loop detected for ${tr.name} — nudging`);\n ctx.messages.push({ role: 'user', content: nudge });\n }\n\n // Loop detection\n const loopConfig = ctx.isAutonomous\n ? { globalCircuitBreakerThreshold: (ctx.config.autonomy as Record<string, unknown>).circuitBreakerOverride as number || 50 }\n : {};\n const loopCheck = checkForLoop(ctx.sessionId, tr.name, tcArgs, tr.content, loopConfig);\n if (!loopCheck.allowed) {\n logger.warn(COMPONENT, `Loop breaker [${loopCheck.level}]: ${loopCheck.reason}`);\n // Hunt Finding #24 (2026-04-14): instead of returning the\n // raw breaker debug message to the user (\"Ping-pong pattern\n // detected: weather ↔ memory repeated 3+ times\"), route\n // into the respond phase with a directive to summarize\n // what was actually collected from successful tools.\n // The breaker reason stays in logs but never in the reply.\n ctx.messages.push({\n role: 'user',\n content: \"Let's wrap this up. Based on what you've already found, give the user a direct answer now. Don't call any more tools — just answer their question using the information you've gathered.\",\n });\n phase = 'respond';\n loopBroken = true;\n break;\n }\n\n // Fruitless search detector: if the model keeps searching for files\n // across multiple rounds without finding them, force it to respond\n if ((tr.name === 'shell' || tr.name === 'list_dir' || tr.name === 'read_file') && round >= 3) {\n const searchTools = result.toolCallDetails.filter(d =>\n (d.name === 'shell' && /\\b(find|ls|grep|locate)\\b/.test(d.args.command as string || '')) ||\n d.name === 'list_dir'\n );\n if (searchTools.length >= 3) {\n const allFailed = searchTools.every(d =>\n d.resultSnippet.length < 50 || /not found|no such|empty|error/i.test(d.resultSnippet)\n );\n if (allFailed) {\n logger.warn(COMPONENT, `[FruitlessSearch] ${searchTools.length} search attempts failed — forcing respond`);\n ctx.messages.push({\n role: 'user',\n content: '[STOP SEARCHING] You have searched for this file/directory multiple times without success. It does not exist at this location. Stop searching and tell the user what you found (or that you could not find it). Do NOT run more find/ls/grep commands.',\n });\n }\n }\n }\n\n // Learning\n const success = !tr.content.toLowerCase().includes('error:');\n recordToolResult(tr.name, success, undefined, success ? undefined : tr.content.slice(0, 200));\n recordToolUsage(tr.name);\n recordToolPreference(tr.name, classifyTaskType(ctx.message), success);\n\n // Persistent audit — Paperclip competitive gap fix\n // Tracks per-agent, per-run, per-tool cost attribution\n try {\n const { logAuditEvent } = await import('./auditStore.js');\n logAuditEvent({\n agentId: ctx.agentId || 'default',\n runId: undefined, // TODO: pipe runId through LoopContext\n sessionId: ctx.sessionId,\n type: 'tool_execution',\n toolName: tr.name,\n durationMs: tr.durationMs,\n promptTokens: result.promptTokens,\n completionTokens: result.completionTokens,\n success,\n });\n } catch { /* audit store not critical */ }\n\n // A6: Error classification feedback to LLM (Hermes ClassifiedError pattern)\n if (!success && tr.errorClass && !ctx.voiceFastPath) {\n const isTransient = tr.errorClass === 'transient' || tr.errorClass === 'timeout' || tr.errorClass === 'rate_limit';\n const hint = isTransient\n ? `[Error Classification: TEMPORARY (${tr.errorClass}). Retrying this tool may succeed. Consider waiting briefly if rate-limited.]`\n : `[Error Classification: PERMANENT. This approach won't work — try a different tool or different arguments.]`;\n ctx.messages.push({ role: 'user', content: hint });\n }\n\n // Auto-fix hints (skip for voice)\n if (!success && !ctx.voiceFastPath) {\n const resolution = getErrorResolution(tr.content);\n if (resolution) {\n logger.info(COMPONENT, `[ActiveLearning] Known fix: ${resolution.slice(0, 80)}`);\n ctx.messages.push({ role: 'user', content: `[Auto-fix hint] A known resolution for this error: ${resolution}. Try applying it.` });\n }\n }\n\n // Error resolution tracking\n if (!ctx.voiceFastPath && success && lastFailedTool) {\n if (tr.name !== lastFailedTool.name) {\n recordErrorResolution(lastFailedTool.error, `Resolved by using ${tr.name} instead of ${lastFailedTool.name}`);\n }\n lastFailedTool = null;\n } else if (!success) {\n lastFailedTool = { name: tr.name, error: tr.content.slice(0, 200) };\n }\n }\n\n // Hunt Finding #24 (2026-04-14): previously `loopBroken` forced\n // phase='done' here, bypassing the respond phase. Now the loop\n // breaker sets phase='respond' directly so the user gets a real\n // answer from the tool data, not the raw breaker message. Keep\n // the `break` to exit the act loop but fall through to respond.\n if (loopBroken) { break; }\n\n // Progress scoring\n if (ctx.reflectionEnabled && toolResults.length > 0) {\n const anySucceeded = toolResults.some(r => !r.content.toLowerCase().includes('error:'));\n const hasNewInfo = toolResults.some(r => r.content.length > 50 && !r.content.toLowerCase().includes('not found'));\n recordProgress(anySucceeded, hasNewInfo, anySucceeded && hasNewInfo);\n }\n\n // Tool Search expansion\n if (ctx.toolSearchEnabled && toolResults.some(r => r.name === 'tool_search')) {\n for (const tr of toolResults) {\n if (tr.name !== 'tool_search') continue;\n const matches = tr.content.matchAll(/\\*\\*(\\w+)\\*\\*/g);\n for (const match of matches) {\n const toolName = match[1];\n if (!discoveredTools.has(toolName)) {\n discoveredTools.add(toolName);\n const fullDef = ctx.allToolsBackup.find(t => t.function.name === toolName);\n if (fullDef && !ctx.activeTools.some(t => t.function.name === toolName)) {\n ctx.activeTools.push(fullDef);\n }\n }\n }\n }\n if (discoveredTools.size > 0) {\n logger.info(COMPONENT, `[ToolSearch] Expanded: +${discoveredTools.size} tools -> ${ctx.activeTools.length} total`);\n }\n }\n\n // ── Read-only round detection ─────────────────────────\n // If in autonomous mode and all tools this round were read-only\n // (no writes, no edits), inject a directive to ACT on what was read.\n // This prevents the \"read → describe → stop\" pattern.\n if (ctx.isAutonomous && toolResults.length > 0) {\n const READ_ONLY_TOOLS = new Set(['read_file', 'list_dir', 'web_search', 'web_fetch', 'tool_search', 'memory', 'system_info', 'goal_list', 'weather']);\n const allReadOnly = toolResults.every(tr =>\n READ_ONLY_TOOLS.has(tr.name) ||\n (tr.name === 'shell' && /^\\s*(cat|head|tail|less|ls|find|grep|wc|echo|date|whoami|hostname|uname|df|du|ps|ss|curl.*GET|pwd|which|type|file|stat)\\b/i.test((pendingToolCalls.find(tc => tc.id === tr.toolCallId)?.function.arguments || '{}').replace(/.*\"command\"\\s*:\\s*\"/, '').replace(/\".*/, '')))\n );\n const hasWrites = toolResults.some(tr =>\n tr.name === 'write_file' || tr.name === 'edit_file' || tr.name === 'append_file'\n );\n if (allReadOnly && !hasWrites && round >= 2) {\n logger.info(COMPONENT, `[ReadOnlyNudge] Round ${round}: all tools were read-only — nudging to write (will force tool_choice on next think)`);\n ctx.messages.push({\n role: 'user',\n content: 'You just read files and ran diagnostic commands. Now ACT on what you found. Call edit_file or write_file to make the changes. Do NOT describe what needs to change — make the change NOW.',\n });\n // Give the nudge teeth: force tool_choice=required on next round\n // so the model MUST call a tool instead of returning more text.\n forceWriteOnNextThink = true;\n }\n }\n\n // ── Phase transition decision ────────────────────────\n round++;\n\n // Soul: update state and emit heartbeat\n updateSoulState(ctx.sessionId, {\n round,\n confidence: result.toolsUsed.length > 0 ? 'medium' : 'low',\n });\n emitHeartbeat(ctx.sessionId, phase, 0);\n\n // Inner monologue injection (every 3 rounds to avoid prompt bloat)\n if (round > 0 && round % 3 === 0) {\n const monologue = getInnerMonologue(ctx.sessionId);\n if (monologue) {\n ctx.messages.push({ role: 'user', content: monologue });\n }\n }\n\n // Checkpoint after each round for crash recovery\n saveCheckpoint({\n sessionId: ctx.sessionId,\n round,\n phase,\n model: activeModel,\n messages: ctx.messages,\n toolsUsed: result.toolsUsed,\n orderedToolSequence: result.orderedToolSequence,\n timestamp: new Date().toISOString(),\n message: ctx.message.slice(0, 500),\n channel: ctx.channel,\n totalPromptTokens: result.promptTokens,\n totalCompletionTokens: result.completionTokens,\n });\n\n // Inject running progress summary every N rounds (helper self-gates)\n const progressMsg = getProgressSummary(ctx.sessionId, round);\n if (progressMsg) {\n logger.info(COMPONENT, `[Progress] Round ${round}`);\n ctx.messages.push({ role: 'user', content: progressMsg });\n }\n\n if (round >= ctx.effectiveMaxRounds) {\n // A8: Budget grace call (Hermes pattern) — give model one final chance to summarize\n // instead of hard-cutting with a generic message\n result.budgetExhausted = true;\n if (pendingAssistantContent && pendingAssistantContent.trim().length > 20) {\n // Model already said something useful — use it\n result.content = stripToolJson(pendingAssistantContent);\n } else {\n // Force one final toolless call to summarize\n phase = 'respond';\n logger.info(COMPONENT, `[BudgetGrace] Round limit reached — giving model one final respond call`);\n continue; // skip to respond phase\n }\n phase = 'done';\n } else if (ctx.isAutonomous) {\n // Smart exit: only skip to respond if a single TERMINAL tool succeeded.\n // Terminal tools are ones that produce a final artifact (write, append)\n // or answer a direct question (weather, system_info).\n // Information-gathering tools (read_file, list_dir, web_search, shell, memory)\n // are NOT terminal — they almost always need a follow-up action.\n const completionStrategy = ctx.completionStrategy || 'smart-exit';\n\n // Pipeline-aware completion detection\n if (completionStrategy === 'no-tools') {\n // Research/browser: keep going until model stops requesting tools\n // (This path always continues — SmartExit is effectively disabled)\n phase = 'think';\n } else if (completionStrategy === 'single-round') {\n // Chat: one tool round then respond\n if (round >= 1) {\n logger.info(COMPONENT, `[SmartExit:${ctx.pipelineType || 'chat'}] Single-round completion — skipping to respond`);\n phase = 'respond';\n } else {\n phase = 'think';\n }\n } else {\n // smart-exit or terminal-tool: exit when a terminal tool succeeds\n const defaultTerminals = ['write_file', 'append_file', 'weather', 'system_info', 'fb_post', 'fb_reply', 'content_publish'];\n const terminalTools = new Set(ctx.pipelineTerminalTools || defaultTerminals);\n const singleToolSuccess = pendingToolCalls.length === 1\n && toolResults.every(r => r.success)\n && terminalTools.has(pendingToolCalls[0].function.name);\n // Respect minRounds — don't allow early exit before the pipeline's minimum\n const minRoundsMet = round >= (ctx.minRounds ?? 2);\n if (singleToolSuccess && minRoundsMet && ctx.smartExitEnabled !== false) {\n logger.info(COMPONENT, `[SmartExit:${ctx.pipelineType || 'general'}] Terminal tool \"${pendingToolCalls[0].function.name}\" succeeded — skipping to respond`);\n phase = 'respond';\n } else {\n // Autonomous mode: go back for more tool rounds\n phase = 'think';\n }\n }\n } else {\n // Non-autonomous: usually force text-only response after one tool round.\n // BUT — if the tool was a discovery tool (gallery_search, tool_search) and\n // its result explicitly hints at a follow-up tool call, allow one more\n // think round so the model can complete the chain (e.g. gallery_get).\n const discoveryTools = ['gallery_search', 'tool_search'];\n const lastWasDiscovery = pendingToolCalls.length === 1 && discoveryTools.includes(pendingToolCalls[0].function.name);\n const resultHintsFollowUp = lastWasDiscovery && toolResults.some(r =>\n /call gallery_get/i.test(r.content) ||\n /hint:.*?(call|use|try|fetch|get|search)/i.test(r.content)\n );\n if (resultHintsFollowUp && round < ctx.effectiveMaxRounds - 1) {\n logger.info(COMPONENT, `[DiscoveryChain] ${pendingToolCalls[0].function.name} hinted follow-up — allowing another think round`);\n phase = 'think';\n } else {\n phase = 'respond';\n logger.info(COMPONENT, `[ThinkAct] Tools executed — entering respond phase (tools will be stripped)`);\n }\n }\n break;\n }\n\n // ══════════════════════════��══════════════════════════��═════\n // RESPOND PHASE — Call LLM WITHOUT tools (forces text response)\n // ═══════════════════════════════════════════════════════════\n case 'respond': {\n // Incomplete task guard: if the user asked to edit/fix/write but the\n // model only read files and never wrote, nudge it back to think phase\n const askedToWrite = /\\b(edit|fix|change|modify|update|add|write|create|improve|rewrite|save)\\b/i.test(ctx.message);\n const didWrite = result.toolsUsed.some(t => ['write_file', 'edit_file', 'append_file'].includes(t));\n const didRead = result.toolsUsed.includes('read_file') || result.toolsUsed.includes('shell');\n if (askedToWrite && !didWrite && didRead && round < ctx.effectiveMaxRounds - 1 && !responseValidationRetried) {\n logger.warn(COMPONENT, `[IncompleteTask] User asked to edit but no write tool called after ${round} rounds — forcing back to think with tool_choice=required`);\n ctx.messages.push({\n role: 'user',\n content: '[INCOMPLETE] You read the file but did NOT make changes. Call edit_file NOW.\\n\\nedit_file arguments:\\n- path: (the file you read)\\n- target: (exact string from the file to replace — copy a small section)\\n- replacement: (the new version of that section)\\n\\nDo NOT use write_file for large files. Use edit_file for surgical changes. CALL IT NOW.',\n });\n responseValidationRetried = true; // Only do this once\n forceWriteOnNextThink = true; // Force tool_choice=required on next think\n phase = 'think';\n break;\n }\n\n logger.info(COMPONENT, `Respond phase — calling LLM without tools to generate final answer`);\n\n // Prompt budget check (Space Agent parity)\n const respondBudgetMsg = budgetConfig.maxTokens > 0 ? checkBudget(ctx.sessionId, budgetConfig) : null;\n if (respondBudgetMsg) {\n markExceeded(ctx.sessionId);\n result.content = respondBudgetMsg;\n phase = 'done';\n break;\n }\n\n // Context compression for respond phase\n let smartMessages: ChatMessage[];\n if (ctx.voiceFastPath) {\n smartMessages = ctx.messages as ChatMessage[];\n } else {\n smartMessages = buildSmartContext(ctx.messages as ChatMessage[], tokenBudget);\n }\n\n // F1: Pre-model hook for respond phase too\n if (ctx.beforeModelCall) {\n try {\n smartMessages = ctx.beforeModelCall([...smartMessages], round);\n } catch (e) {\n logger.warn(COMPONENT, `[PreModelHook:respond] Hook threw: ${(e as Error).message}`);\n }\n }\n\n // Hunt Finding #21 (2026-04-14): inject a directive message at the\n // tail of the context for the respond phase. Without this, weak\n // models (minimax-m2.7:cloud) produce internal-monologue text like\n // \"The user asked me to run X... Actually, looking at the results...\"\n // which is raw chain-of-thought leaking into the final answer.\n // This directive is appended only to the respond-phase request —\n // not persisted to the session history.\n const respondDirective: ChatMessage = {\n role: 'user',\n content: '[System directive for this reply only] Write the final answer for the user. RULES: (1) Do NOT narrate what the user asked — they already know. (2) Do NOT describe your reasoning, thinking, or past tool attempts. (3) Do NOT start with \"The user asked\", \"Let me\", \"Actually\", \"Looking at\", \"Wait\" — start with the result. (4) Report outcomes as facts in 1-3 sentences. (5) No XML, no tool call blocks, no meta-commentary. Just the answer. (6) EXCEPTION: if you need to create or update a widget on the canvas, you MAY emit a _____react or _____widget gate — these are NOT meta-commentary, they are the actual deliverable.',\n };\n smartMessages = [...smartMessages, respondDirective];\n\n const thinkingMode = ctx.thinkingOverride || ctx.config.agent.thinkingMode || 'off';\n const chatOptions = {\n model: activeModel,\n messages: smartMessages,\n tools: undefined, // NO TOOLS — forces text-only response\n maxTokens: ctx.voiceFastPath ? Math.min(ctx.config.agent.maxTokens, 300) : ctx.config.agent.maxTokens,\n temperature: ctx.config.agent.temperature,\n thinking: ctx.voiceFastPath ? false : thinkingMode !== 'off',\n thinkingLevel: thinkingMode as 'off' | 'low' | 'medium' | 'high',\n providerOptions: ctx.providerOptions,\n };\n\n let response: ChatResponse;\n const respondStart = Date.now();\n if (ctx.streamCallbacks?.onToken) {\n let streamContent = '';\n for await (const chunk of chatStream(chatOptions)) {\n if (chunk.type === 'text' && chunk.content) {\n streamContent += chunk.content;\n ctx.streamCallbacks.onToken(chunk.content);\n } else if (chunk.type === 'error') {\n logger.error(COMPONENT, `Stream error: ${chunk.error}`);\n } else if (chunk.type === 'retry') {\n // Out-of-band: log + callback only. Do NOT append to streamContent.\n logger.warn(COMPONENT, `Respond-phase stream retrying ${chunk.provider}/${chunk.model} (attempt ${chunk.attempt}/${chunk.maxRetries}, reason=${chunk.reason})`);\n ctx.streamCallbacks.onRetry?.({\n attempt: chunk.attempt,\n maxRetries: chunk.maxRetries,\n reason: chunk.reason,\n provider: chunk.provider,\n model: chunk.model,\n delayMs: chunk.delayMs,\n });\n } else if (chunk.type === 'failover') {\n logger.warn(COMPONENT, `Respond-phase stream failover from ${chunk.originalProvider}/${chunk.originalModel}`);\n ctx.streamCallbacks.onFailover?.({\n originalProvider: chunk.originalProvider,\n originalModel: chunk.originalModel,\n error: chunk.error,\n });\n }\n }\n response = {\n id: `stream-${Date.now()}`,\n content: streamContent,\n usage: { promptTokens: 0, completionTokens: 0, totalTokens: 0 },\n finishReason: 'stop',\n model: activeModel,\n };\n } else {\n response = await chatWithTimeout(() => chat(chatOptions), 'agentLoop:chat', 300_000);\n }\n // v5.0: OTEL span for respond-phase LLM call\n fireSpan(traceCtx, 'model_call:respond', Date.now() - respondStart, { round: String(round), model: activeModel, phase: 'respond' });\n\n result.modelUsed = response.model;\n result.promptTokens += response.usage?.promptTokens || 0;\n result.completionTokens += response.usage?.completionTokens || 0;\n\n // Hunt Finding #39 (2026-04-15): if the model emits tool calls in\n // the respond phase (despite tools being undefined), those calls\n // are the model's attempt to RECOVER from an earlier failure. A\n // real captured example: model wrote to /home/titan/docs/foo.md\n // (wrong user), the write was rejected, then in the respond phase\n // it emitted a corrected write_file to /tmp/readme-b1-comparison.md.\n // Previously we dropped that tool call silently. Now we route\n // back to the think phase so the recovery actually executes.\n if (response.toolCalls && response.toolCalls.length > 0) {\n logger.warn(\n COMPONENT,\n `[RespondPhaseToolCall] Model emitted ${response.toolCalls.length} tool call(s) in respond phase — routing back to think phase to execute (Hunt #39)`,\n );\n // Inject the tool call as an assistant message so the next\n // think round sees it and the agent loop executes it.\n ctx.messages.push({\n role: 'assistant',\n content: response.content || '',\n toolCalls: response.toolCalls,\n });\n // Synthesize a placeholder tool message so the next think\n // iteration doesn't orphan the tool_calls.\n // Actually — leave it. The act-phase handler runs tool calls\n // from the most recent assistant. Transition phase=act and\n // let the existing execution path handle it.\n phase = 'act';\n // Seed pendingToolCalls so the act handler picks them up\n pendingToolCalls = response.toolCalls;\n pendingAssistantContent = response.content || '';\n break; // exit respond case; re-enter the while loop at act\n }\n\n const costCheck = recordTokenUsage(ctx.sessionId, activeModel, response.usage?.promptTokens || 0, response.usage?.completionTokens || 0);\n\n // Prompt budget tracking (Space Agent parity)\n if (budgetConfig.maxTokens > 0) {\n recordUsage(ctx.sessionId, response.usage?.promptTokens || 0, response.usage?.completionTokens || 0);\n }\n\n // Wire LLM spend into Command Post budget policies\n const callCost = calculateActualCost(activeModel, { prompt: response.usage?.promptTokens || 0, completion: response.usage?.completionTokens || 0 });\n const sessionGoal = getSessionGoal(ctx.sessionId);\n recordSpend(ctx.agentId || 'default', sessionGoal?.goalId, callCost);\n\n if (costCheck.budgetExceeded) {\n result.content = '⚠️ Daily spending limit reached. TITAN has paused to keep your API costs under control.';\n } else {\n // Output guardrails pipeline — centralized post-processing\n // Strips thinking blocks, narrator preamble, instruction echoes,\n // and validates structure before delivering to user.\n const { applyOutputGuardrails } = await import('./outputGuardrails.js');\n const guardrailed = applyOutputGuardrails(response.content, {\n type: 'chat_response',\n originalMessage: ctx.message,\n model: activeModel,\n });\n result.content = guardrailed.content;\n }\n\n // Empty response fallback: if the model returned nothing in respond phase,\n // retry once with explicit instruction, then use a clean fallback\n if (!emptyResponseRetried && (!result.content || result.content.trim().length === 0)) {\n emptyResponseRetried = true;\n // Try one more LLM call with a strong nudge to summarize\n try {\n const retryMessages = [\n ...ctx.messages.slice(-6),\n { role: 'user' as const, content: \"Please answer the user's question now. Summarize what you found in 2-3 sentences. Don't use any tools — just answer directly.\" },\n ];\n const retryResponse = await chatWithTimeout(() => chat({\n model: activeModel,\n messages: retryMessages,\n temperature: 0.7,\n maxTokens: 300,\n providerOptions: ctx.providerOptions,\n }), 'agentLoop:retry', 300_000);\n const retryContent = (retryResponse.content || '').trim();\n if (retryContent && retryContent.length > 10) {\n logger.info(COMPONENT, '[EmptyResponse] Recovery retry succeeded');\n result.content = stripToolJson(retryContent);\n }\n } catch (retryErr) {\n logger.debug(COMPONENT, `[EmptyResponse] Recovery retry failed: ${(retryErr as Error).message}`);\n }\n\n // If retry also failed, use a clean message (never dump raw tool results)\n if (!result.content || result.content.trim().length === 0) {\n logger.warn(COMPONENT, '[EmptyResponse] Model returned empty after retry — using clean fallback');\n result.content = 'I looked into that but couldn\\'t generate a clear summary. Could you try asking again?';\n }\n }\n\n // Response validation: check if the answer actually addresses the question.\n // If the user asked for specific data (a number, version, name) and the\n // response doesn't contain it but tool results do, retry once with a nudge.\n if (!responseValidationRetried && result.content && result.toolsUsed.length > 0) {\n const gap = detectResponseGap(ctx.message, result.content, ctx.messages);\n if (gap) {\n logger.info(COMPONENT, `[ResponseValidation] Gap detected: ${gap}. Retrying respond phase.`);\n ctx.messages.push({\n role: 'user',\n content: `[IMPORTANT] Your response did not include the specific information the user asked for. ${gap} Look at your tool results above and include the actual data in your answer. Be direct and specific.`,\n });\n responseValidationRetried = true;\n // Stay in respond phase for one more try\n break;\n }\n }\n\n phase = 'done';\n break;\n }\n } // end switch\n\n // ── on_round_end shell hook ──────────────────────────\n try {\n const { runShellHooks } = await import('../hooks/shellHooks.js');\n const { loadConfig } = await import('../config/config.js');\n const config = loadConfig();\n const hooksCfg = (config.hooks as Record<string, unknown> | undefined)?.shell as Record<string, unknown> | undefined;\n if (hooksCfg?.enabled) {\n await runShellHooks('on_round_end', {\n TITAN_SESSION_ID: ctx.sessionId,\n TITAN_AGENT_ID: ctx.agentId || 'default',\n TITAN_ROUND: String(round),\n });\n }\n } catch { /* hooks are best-effort */ }\n } // end while\n\n // ── on_session_end shell hook ────────────────────────\n try {\n const { runShellHooks } = await import('../hooks/shellHooks.js');\n const { loadConfig } = await import('../config/config.js');\n const config = loadConfig();\n const hooksCfg = (config.hooks as Record<string, unknown> | undefined)?.shell as Record<string, unknown> | undefined;\n if (hooksCfg?.enabled) {\n await runShellHooks('on_session_end', {\n TITAN_SESSION_ID: ctx.sessionId,\n TITAN_AGENT_ID: ctx.agentId || 'default',\n TITAN_ROUND: String(round),\n });\n }\n } catch { /* hooks are best-effort */ }\n\n // If loop ended without setting content (hit round limit in think phase)\n if (!result.content && round >= ctx.effectiveMaxRounds) {\n result.content = 'I ran out of thinking time on that one. Could you break your request into a smaller step?';\n result.budgetExhausted = true;\n }\n\n // v4.13 ancestor-extraction (Hermes trajectory): append this run's full\n // ChatML transcript to disk for future retrospective / fine-tuning. Guarded\n // try/catch — trajectory I/O failures never affect the user response.\n try {\n const { saveTrajectory } = await import('./trajectory.js');\n const completed = !result.budgetExhausted && !!result.content && result.content.length > 0;\n saveTrajectory({\n conversations: ctx.messages,\n model: activeModel,\n completed,\n sessionId: ctx.sessionId,\n toolsUsed: result.toolsUsed,\n reason: result.budgetExhausted ? 'budget_exhausted' : (completed ? 'done' : 'empty'),\n metrics: {\n rounds: round,\n promptTokens: result.promptTokens ?? 0,\n completionTokens: result.completionTokens ?? 0,\n },\n });\n } catch { /* trajectory save is best-effort */ }\n\n // v5.0: Unregister session from steer map\n globalSteerQueues.delete(ctx.sessionId);\n\n // v5.0: Session-level OTEL span\n const totalDuration = Date.now() - loopStartTime;\n fireSpan(traceCtx, 'session', totalDuration, {\n rounds: String(round),\n model: activeModel,\n toolsUsed: result.toolsUsed.length,\n budgetExhausted: result.budgetExhausted,\n });\n result.traceContext = { traceId: traceCtx.traceId, spanId: traceCtx.spanId };\n\n // Clean up prompt budget state\n if (budgetConfig.maxTokens > 0) {\n cleanupBudget(ctx.sessionId);\n }\n\n return result;\n}\n\n// ── Heartbeat-driven inbox processing ───────────────────────────────────\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nasync function checkAndProcessInbox(agentId: string): Promise<void> {\n const inbox = getAgentInbox(agentId);\n if (inbox.length === 0) return;\n\n // Take the first queued request\n const req = inbox[0];\n const claimed = claimWakeupRequest(req.id);\n if (!claimed) return;\n\n // Transition CP issue to in_progress\n updateIssue(req.issueId, { status: 'in_progress' });\n const run = startRun(agentId, 'assignment', req.issueId);\n\n logger.info(COMPONENT, `[Heartbeat] Processing wakeup ${req.id} for agent \"${req.agentName}\"`);\n\n try {\n const template = SUB_AGENT_TEMPLATES[req.templateName] || {};\n const config = loadConfig();\n const modelAliases = (config.agent as Record<string, unknown> | undefined)?.modelAliases as Record<string, string> | undefined;\n const tier = (template as Record<string, unknown>).tier as string | undefined;\n let model = req.model;\n if (!model && modelAliases && tier) {\n model = modelAliases[tier] || modelAliases.fast;\n }\n\n const result = await spawnSubAgent({\n name: req.agentName,\n task: req.task,\n tools: template.tools,\n systemPrompt: template.systemPrompt,\n model,\n depth: 0,\n });\n\n // Complete the CP run\n endRun(run.id, {\n status: result.success ? 'succeeded' : 'failed',\n toolsUsed: result.toolsUsed,\n });\n\n // Post result as CP issue comment\n const commentBody = [\n `**Sub-agent result** (${result.rounds} rounds, ${result.durationMs}ms)`,\n `Status: ${result.success ? 'SUCCESS' : 'FAILED'}${result.validated ? '' : ' [UNVALIDATED]'}`,\n `Tools: ${result.toolsUsed.join(', ') || 'none'}`,\n '',\n result.content,\n ].join('\\n');\n addIssueComment(req.issueId, commentBody, { agentId });\n updateIssue(req.issueId, { status: result.success ? 'done' : 'todo' });\n\n logger.info(COMPONENT, `[Heartbeat] Wakeup ${req.id} completed — ${result.success ? 'SUCCESS' : 'FAILED'}`);\n } catch (err) {\n const error = (err as Error).message;\n endRun(run.id, { status: 'error', error });\n addIssueComment(req.issueId, `**Sub-agent failed**: ${error}`, { agentId });\n updateIssue(req.issueId, { status: 'todo' });\n logger.error(COMPONENT, `[Heartbeat] Wakeup ${req.id} failed: ${error}`);\n }\n}\n"],"mappings":"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","names":["runShellHooks","loadConfig"]}
1
+ {"version":3,"sources":["../../src/agent/agentLoop.ts"],"sourcesContent":["/**\n * TITAN — Agent Loop (Phase State Machine)\n *\n * Replaces the monolithic for-loop in agent.ts with a clean Think/Act/Respond\n * state machine that eliminates cloud model tool looping by design.\n *\n * Phases:\n * THINK — Call LLM WITH tools. Model returns content or tool_calls.\n * ACT — Execute tool calls, record results, run loop detection.\n * RESPOND — Call LLM WITHOUT tools. Forces text-only final answer.\n * DONE — Loop exits.\n *\n * Non-autonomous: THINK → ACT → RESPOND → DONE (one tool round, then text)\n * Autonomous: THINK → ACT → THINK → ... → RESPOND → DONE (multi-round)\n */\nimport { chat, chatStream } from '../providers/router.js';\nimport { executeTools, type ToolResult } from './toolRunner.js';\nimport { drainPendingResults, getAgentInbox, claimWakeupRequest } from './agentWakeup.js';\nimport { setCurrentSessionId } from './agent.js';\nimport { hasActionDirectives, compileActions } from './actionCompiler.js';\nimport { heartbeat, recordToolCall, checkResponse, getNudgeMessage, checkToolCallCapability, resetToolCallFailures } from './stallDetector.js';\nimport { loadConfig } from '../config/config.js';\nimport { checkForLoop } from './loopDetection.js';\nimport { spawnSubAgent, SUB_AGENT_TEMPLATES } from './subAgent.js';\nimport { updateIssue, startRun, endRun, addIssueComment, recordSpend } from './commandPost.js';\nimport { recordTokenUsage, routeModel, type TurnContext } from './costOptimizer.js';\nimport { calculateActualCost } from './costEstimator.js';\nimport { getSessionGoal } from './autonomyContext.js';\nimport { initBudget, checkBudget, recordUsage, markExceeded, cleanupBudget, getDefaultBudget } from './promptBudget.js';\nimport { scanForSecrets } from '../security/secretGuard.js';\nimport { fullExfilScan } from '../security/exfilScan.js';\nimport { runShellHooks } from '../hooks/shellHooks.js';\nimport { initOtel, newTraceContext, timedSpan, fireSpan } from '../diagnostics/otel.js';\n\nimport type { TitanConfig } from '../config/schema.js';\n\n/** Timeout wrapper for LLM calls. Prevents indefinite hangs on stuck providers. */\nasync function chatWithTimeout<T>(fn: () => Promise<T>, label: string, timeoutMs = 300_000): Promise<T> {\n const timeout = new Promise<never>((_, reject) =>\n setTimeout(() => reject(new Error(`${label} timed out after ${timeoutMs}ms`)), timeoutMs)\n );\n return Promise.race([fn(), timeout]);\n}\nimport { buildSmartContext } from './contextManager.js';\nimport { isKilled } from '../safety/killSwitch.js';\nimport { estimateTokens } from '../utils/tokens.js';\nimport { DEFAULT_MAX_TOKENS } from '../utils/constants.js';\nimport { getCachedResponse, setCachedResponse } from './responseCache.js';\nimport { shouldReflect, reflect, resetProgress, recordProgress, setProgressSession } from './reflection.js';\nimport { recordToolResult, classifyTaskType, recordToolPreference, getErrorResolution, recordErrorResolution } from '../memory/learning.js';\nimport { saveCheckpoint } from './checkpoint.js';\nimport { updateSoulState, emitHeartbeat, getInnerMonologue, recordAttempt } from './soul.js';\n\n// v5.0: Global steer queue — allows external API to nudge active sessions\nconst globalSteerQueues = new Map<string, string[]>();\n\n/** Push a steer message to an active (or upcoming) session */\nexport function pushSteer(sessionId: string, message: string): boolean {\n const queue = globalSteerQueues.get(sessionId);\n if (queue) {\n queue.push(message);\n } else {\n globalSteerQueues.set(sessionId, [message]);\n }\n return true;\n}\nimport { recordToolUsage } from './userProfile.js';\nimport { runSubAgent, type Domain } from './swarm.js';\nimport { compressToolResult, recordStep, getProgressSummary } from './trajectoryCompressor.js';\nimport { verifyFileWrite } from './autoVerify.js';\nimport type { ChatMessage, ChatResponse, ToolCall, ToolDefinition } from '../providers/base.js';\nimport logger from '../utils/logger.js';\n\nconst COMPONENT = 'AgentLoop';\n\n/**\n * Validate tool call / tool result pairing (LangGraph pattern).\n * Every assistant message with toolCalls must have a matching tool result for each call.\n * Orphaned pairs cause API rejections from all providers.\n *\n * Hunt Finding #14 (2026-04-14): previously this function DROPPED assistant\n * messages with orphaned tool calls, destroying the model's work history and\n * causing it to redo tool calls or get confused about its state. The 5-phase\n * context compressor can legitimately drop tool RESULT messages while keeping\n * the assistant tool_call messages (or vice versa), creating orphans.\n *\n * New behavior: when an orphaned tool_call is detected, SYNTHESIZE a placeholder\n * tool result message with `[Earlier tool result cleared]` content. This keeps\n * the conversation shape valid for providers AND preserves the model's history.\n * The model still sees that the tool was called and knows not to call it again.\n */\nfunction validateToolPairs(messages: ChatMessage[]): ChatMessage[] {\n const toolResultIds = new Set(\n messages.filter(m => m.role === 'tool' && m.toolCallId).map(m => m.toolCallId)\n );\n const out: ChatMessage[] = [];\n for (const m of messages) {\n out.push(m);\n if (m.role === 'assistant' && m.toolCalls && m.toolCalls.length > 0) {\n const orphaned = m.toolCalls.filter(tc => !toolResultIds.has(tc.id));\n if (orphaned.length > 0) {\n logger.warn(COMPONENT, `[ToolPairValidation] Synthesizing ${orphaned.length} missing tool result(s) for orphaned tool call(s): ${orphaned.map(tc => tc.function.name).join(', ')}`);\n // Insert a synthetic tool result immediately after the assistant message.\n // This preserves the model's work history while keeping the pairing valid.\n for (const tc of orphaned) {\n out.push({\n role: 'tool',\n // v4.10.0-local: MUST include `name` — Gemini's\n // Ollama-compat adapter maps it to\n // `function_response.name`, which it rejects\n // when empty. Fix for recurring HTTP 400s\n // \"Name cannot be empty\" on gemini-3-flash-preview:cloud.\n name: tc.function.name,\n content: `[Earlier tool result cleared — ${tc.function.name} was called previously but its result was pruned from context.]`,\n toolCallId: tc.id,\n });\n toolResultIds.add(tc.id);\n }\n }\n }\n }\n return out;\n}\n\n/**\n * Hunt Finding #09 (2026-04-14): pair-aware context trim.\n *\n * Previously the agent loop trimmed with `.slice(-8)` on non-system messages,\n * which cut right through tool_call/tool_result pairs. Then validateToolPairs\n * dropped the assistant-with-orphan-toolcall messages, losing the work history.\n * The model would see no prior tool calls and redo them, causing ping-pong loops.\n *\n * This trim walks backwards and keeps tool_call+tool_result PAIRS together. When\n * we hit a tool_result, we also keep its parent assistant-with-tool_calls message.\n * When we hit an assistant-with-tool_calls, we also keep ALL its tool_results.\n */\nfunction trimPairAware(messages: ChatMessage[], maxTotal: number): ChatMessage[] {\n const systemMsgs = messages.filter(m => m.role === 'system');\n const nonSystem = messages.filter(m => m.role !== 'system');\n const maxNonSystem = Math.max(1, maxTotal - systemMsgs.length);\n\n if (nonSystem.length <= maxNonSystem) return messages;\n\n // Walk backwards, keeping pairs together. Tool result messages belong to the\n // nearest preceding assistant message with tool_calls; assistant messages with\n // tool_calls own all immediately-following tool result messages.\n const kept: ChatMessage[] = [];\n let i = nonSystem.length - 1;\n\n // First pass: collect indices to keep, preserving pair integrity\n const keepIdx = new Set<number>();\n while (i >= 0 && keepIdx.size < maxNonSystem) {\n const msg = nonSystem[i];\n if (msg.role === 'tool' && msg.toolCallId) {\n // Find its parent assistant message\n let parentIdx = -1;\n for (let j = i - 1; j >= 0; j--) {\n const cand = nonSystem[j];\n if (cand.role === 'assistant' && cand.toolCalls?.some(tc => tc.id === msg.toolCallId)) {\n parentIdx = j;\n break;\n }\n if (cand.role !== 'tool') break; // only walk through tool siblings\n }\n keepIdx.add(i);\n if (parentIdx >= 0) {\n keepIdx.add(parentIdx);\n // Also keep any sibling tool results for the same assistant\n for (let j = parentIdx + 1; j < nonSystem.length; j++) {\n const sib = nonSystem[j];\n if (sib.role !== 'tool') break;\n keepIdx.add(j);\n }\n }\n i = parentIdx >= 0 ? parentIdx - 1 : i - 1;\n } else if (msg.role === 'assistant' && msg.toolCalls && msg.toolCalls.length > 0) {\n // Keep this assistant + all its tool results\n keepIdx.add(i);\n for (let j = i + 1; j < nonSystem.length; j++) {\n const sib = nonSystem[j];\n if (sib.role !== 'tool') break;\n keepIdx.add(j);\n }\n i--;\n } else {\n keepIdx.add(i);\n i--;\n }\n }\n\n // Emit in original order\n for (let k = 0; k < nonSystem.length; k++) {\n if (keepIdx.has(k)) kept.push(nonSystem[k]);\n }\n\n return [...systemMsgs, ...kept];\n}\n\n/** Sanitize reflection reasoning before injecting into message stream */\nfunction sanitizeReflection(text: string): string {\n return text\n .slice(0, 200)\n .replace(/\\[SYSTEM\\].*$/gm, '')\n .replace(/^(You are|IMPORTANT:|CRITICAL:|IGNORE).*$/gim, '')\n .trim() || 'approach not working';\n}\n\n/**\n * Hunt Finding #05 (2026-04-14): detect explicit tool-use intent in the user message.\n *\n * Symptom: User says \"use the shell tool to run uptime\" but the model returns\n * plausible but FABRICATED output (hallucinated uptime text) without actually\n * calling the shell tool. The text looks like real tool output but isn't.\n *\n * Previously `forceToolUse` only fired in autonomous mode. Regular API calls\n * let the model ignore tools, so any model with weak tool calling could\n * hallucinate output.\n *\n * Fix: if the user message explicitly requests a tool, we force\n * `tool_choice: required` on the first call, even in non-autonomous mode.\n * This doesn't eliminate hallucination entirely but stops the most common\n * pattern where the model chooses to not call a tool at all.\n */\nexport function detectToolUseIntent(userMessage: string): boolean {\n if (!userMessage || userMessage.length < 5) return false;\n const msg = userMessage.toLowerCase();\n\n const intentPatterns = [\n // Explicit \"use the X tool\" or \"use X tool\"\n /\\buse (?:the )?(\\w+) tool\\b/,\n /\\buse (?:the )?(shell|web_search|web_fetch|read_file|write_file|edit_file|list_dir|memory|weather|fb_post|fb_reply|fb_read_feed|github|email|calendar)\\b/,\n // \"run X\" / \"execute X\" / \"call X\"\n /\\brun (?:the )?(?:shell|command|tool|script)\\b/,\n /\\bexecute (?:the )?(?:shell|command|tool|script|this)\\b/,\n /\\bcall (?:the )?(?:\\w+ )?tool\\b/,\n /\\binvoke (?:the )?(\\w+)\\b/,\n // Action verbs that require tool execution\n /\\b(?:search the web|search for|web search)\\b/,\n /\\bfetch (?:the )?(url|page|content|https?:)/,\n /\\bread (?:the )?(?:file|contents? of|lines from)\\b/,\n /\\bwrite (?:this |the )?(?:to (?:the )?file|file)\\b/,\n /\\blist (?:the )?(?:files?|contents?) (?:in|of|at)\\b/,\n // Requests to check real system state that REQUIRES a tool\n /\\b(?:what is|what's|show me|get) (?:the )?(?:current|actual) (?:uptime|hostname|ip|path|directory|pwd|time|date|memory|disk)\\b/,\n // Hunt Finding #17 (2026-04-14): added `[\\s:]+` so \"run: ls\" matches too.\n /\\brun[\\s:]+['\"`]?(?:echo|ls|pwd|uptime|whoami|date|uname|cat|grep|find|node|npm|git|which|ps|df|free)\\b/,\n // Widget / gallery tool requests — canvas chat explicitly asks for gallery_search/gallery_get\n /\\b(?:call|use|run)\\b.*?\\b(?:gallery_search|gallery_get|gallery_list)\\b/,\n /\\b(?:create|add|make|build|spawn)\\b.*?\\b(?:widget|panel|canvas|gallery)\\b/,\n /\\b(?:widget|gallery)\\b.*?\\b(?:template|search|find|get|fetch)\\b/,\n // New system widget intents (v5.0.2 \"forgotten features\" surface)\n /\\b(?:backup|snapshot|archive)\\b/,\n /\\b(?:training|train|specialist|model)\\b/,\n /\\b(?:recipe|playbook|workflow|jarvis)\\b/,\n /\\b(?:vram|gpu|memory|nvidia)\\b/,\n /\\b(?:team|member|role|permission|rbac)\\b/,\n /\\b(?:cron|schedule|job|timer)\\b/,\n /\\b(?:checkpoint|restore|save state)\\b/,\n /\\b(?:organism|drive|safety|alert|guardrail)\\b/,\n /\\b(?:fleet|node|route|mesh)\\b/,\n /\\b(?:captcha|browser|form fill|web automation)\\b/,\n /\\b(?:paperclip|sidecar|helper)\\b/,\n /\\b(?:test|flaky|failing|coverage|eval)\\b/,\n ];\n\n return intentPatterns.some(p => p.test(msg));\n}\n\n/**\n * Hunt Finding #17 (2026-04-14): Extract a tool call from the USER MESSAGE directly.\n *\n * Runs as a last-resort rescue path in the NoTools handler. Triggered when the model\n * ignored `tool_choice=required` and all model-response-based rescue paths failed.\n *\n * The key insight: when the user's request explicitly names a command or file, we\n * don't need the model's cooperation to figure out what tool to call — we can parse\n * the intent from the user message itself.\n *\n * This defends against weak models (like minimax-m2.7:cloud) that fabricate\n * plausible-sounding tool output (\"Permission denied\", \"command returned null\",\n * \"Node.js is not installed\") instead of actually calling the tool.\n *\n * Returns a synthetic tool call or null if no clear intent can be extracted.\n */\nexport function extractToolCallFromUserMessage(\n userMessage: string,\n activeTools: ToolDefinition[],\n): ToolCall | null {\n if (!userMessage || userMessage.length < 5) return null;\n const msg = userMessage.trim();\n const lower = msg.toLowerCase();\n const availableNames = new Set(activeTools.map(t => t.function.name));\n const mkCall = (name: string, args: Record<string, unknown>): ToolCall => ({\n id: `uir-${Date.now()}`,\n type: 'function' as const,\n function: { name, arguments: JSON.stringify(args) },\n });\n\n // Shell: \"run X\", \"run: X\", \"execute X\", \"please run X\", \"can you run X\"\n // X starts with a known shell command.\n if (availableNames.has('shell')) {\n const shellMatch = msg.match(\n /(?:please\\s+)?(?:can you\\s+)?(?:run|execute)[\\s:]+[`'\"]?((?:ls|cat|grep|find|echo|pwd|uname|node|npm|git|which|ps|df|free|uptime|whoami|date|hostname|ip|head|tail|wc|sort|uniq|awk|sed|curl|wget|ping|du|stat|file|env|printenv|history)\\s[^\\n`'\"]*?)[`'\"]?(?:\\s+and|\\s+then|\\.|\\?|\\s*$)/i,\n );\n if (shellMatch && shellMatch[1]) {\n return mkCall('shell', { command: shellMatch[1].trim() });\n }\n // Bare \"run: ls /path\" without other clauses\n const bareMatch = msg.match(\n /^(?:please\\s+)?(?:run|execute)[\\s:]+[`'\"]?((?:ls|cat|grep|find|echo|pwd|uname|node|npm|git|which|ps|df|free|uptime|whoami|date|hostname)\\s[^\\n`'\"]+?)[`'\"]?\\s*$/i,\n );\n if (bareMatch && bareMatch[1]) {\n return mkCall('shell', { command: bareMatch[1].trim() });\n }\n }\n\n // read_file: \"read the file X\", \"read /path/to/file\", \"show me the contents of X\"\n if (availableNames.has('read_file')) {\n const readMatch = msg.match(\n /(?:read|open|show me|display|view)\\s+(?:the\\s+)?(?:file\\s+|contents of\\s+)?[`'\"]?(\\/[a-zA-Z0-9/._-]+)[`'\"]?/i,\n );\n if (readMatch && readMatch[1]) {\n return mkCall('read_file', { path: readMatch[1] });\n }\n }\n\n // list_dir: \"list files in X\", \"list X\", \"what's in X\"\n if (availableNames.has('list_dir')) {\n const listMatch = msg.match(\n /(?:list|show)\\s+(?:the\\s+)?(?:files?|contents?|directory)\\s+(?:in|of|at)\\s+[`'\"]?(\\/[a-zA-Z0-9/._-]+)[`'\"]?/i,\n );\n if (listMatch && listMatch[1]) {\n return mkCall('list_dir', { path: listMatch[1] });\n }\n }\n\n // web_search: \"search the web for X\", \"google X\", \"search for X\"\n if (availableNames.has('web_search')) {\n const searchMatch = msg.match(/(?:search\\s+(?:the\\s+)?web\\s+for|google|web\\s+search\\s+for|search\\s+for)\\s+(.+?)(?:\\.|\\?|$)/i);\n if (searchMatch && searchMatch[1]) {\n return mkCall('web_search', { query: searchMatch[1].trim() });\n }\n }\n\n // web_fetch: \"fetch https://...\", \"open URL https://...\"\n if (availableNames.has('web_fetch')) {\n const fetchMatch = msg.match(/(?:fetch|open|load|get)\\s+(https?:\\/\\/[^\\s]+)/i);\n if (fetchMatch && fetchMatch[1]) {\n return mkCall('web_fetch', { url: fetchMatch[1] });\n }\n }\n\n // weather: \"weather for X\", \"what's the weather in X\"\n if (availableNames.has('weather')) {\n const weatherMatch = lower.match(/weather\\s+(?:for|in|at)\\s+([a-zA-Z][a-zA-Z\\s,]+?)(?:\\.|\\?|$)/i);\n if (weatherMatch && weatherMatch[1]) {\n return mkCall('weather', { location: weatherMatch[1].trim() });\n }\n }\n\n return null;\n}\n\n// ── Phase State Machine ��─────────────────────────────────────────────\n\nexport type AgentPhase = 'think' | 'act' | 'respond' | 'done';\n\n/** Stream callbacks — same interface as agent.ts for compatibility */\nexport interface StreamCallbacks {\n onToken?: (token: string) => void;\n onToolCall?: (name: string, args: Record<string, unknown>) => void;\n onToolResult?: (name: string, result: string, durationMs: number, success: boolean, diff?: string) => void;\n onThinking?: () => void;\n onRound?: (round: number, maxRounds: number) => void;\n /**\n * Fired when the router is retrying a transient provider failure on the\n * SAME provider/model. Out-of-band status — never forward to the\n * assistant's text content. UI consumers should use this to render a\n * retry indicator (spinner, toast). Pre-fix v5.4.x leaked this as a\n * `[Retrying request (1/4) due to ...]` text chunk into the response.\n */\n onRetry?: (info: { attempt: number; maxRetries: number; reason: string; provider: string; model: string; delayMs: number }) => void;\n /**\n * Fired when the router falls over to a different provider/model after\n * exhausting retries. Out-of-band status; do NOT append to user-visible\n * content.\n */\n onFailover?: (info: { originalProvider: string; originalModel: string; error?: string }) => void;\n}\n\n/** All inputs the loop needs from processMessage */\nexport interface LoopContext {\n messages: ChatMessage[];\n activeTools: ToolDefinition[];\n allToolsBackup: ToolDefinition[];\n activeModel: string;\n config: TitanConfig;\n sessionId: string;\n agentId?: string; // For Command Post inbox checking\n channel: string;\n message: string;\n streamCallbacks?: StreamCallbacks;\n signal?: AbortSignal;\n isAutonomous: boolean;\n voiceFastPath: boolean;\n effectiveMaxRounds: number;\n taskEnforcementActive: boolean;\n reflectionEnabled: boolean;\n reflectionInterval: number;\n toolSearchEnabled: boolean;\n isKimiSwarm: boolean;\n selfHealEnabled: boolean;\n smartExitEnabled?: boolean;\n thinkingOverride?: string;\n /** Pipeline-specific terminal tools for SmartExit */\n pipelineTerminalTools?: string[];\n /** Pipeline-specific completion detection strategy */\n completionStrategy?: 'smart-exit' | 'no-tools' | 'terminal-tool' | 'single-round';\n /** Pipeline type for logging */\n pipelineType?: string;\n /** Minimum rounds before allowing SmartExit (pipeline-enforced) */\n minRounds?: number;\n /** F1: Pre-model hook (LangGraph pattern) — modify messages for LLM without changing\n * persisted history. Use for RAG injection, summarization, dynamic token budgeting.\n * Receives a COPY of messages; return modified copy for the LLM call. */\n beforeModelCall?: (messages: ChatMessage[], round: number) => ChatMessage[];\n /** Provider-specific opt-ins forwarded to ChatOptions.providerOptions. */\n providerOptions?: Record<string, unknown>;\n /** v5.0: Steer queue — mid-run nudges injected after ACT phase */\n steerQueue?: string[];\n}\n\n/** Everything processMessage needs back from the loop */\nexport interface LoopResult {\n content: string;\n toolsUsed: string[];\n orderedToolSequence: string[];\n modelUsed: string;\n /** v5.0: OTEL trace context for observability */\n traceContext?: { traceId: string; spanId: string };\n promptTokens: number;\n completionTokens: number;\n budgetExhausted: boolean;\n /** Structured details from each tool call — used for inter-step context in deliberation */\n toolCallDetails: Array<{\n name: string;\n args: Record<string, unknown>;\n resultSnippet: string;\n success: boolean;\n }>;\n}\n\n/**\n * Hunt Finding #38b (2026-04-15): strip narrator preamble from chat\n * responses. Weak models (minimax-m2.7:cloud, glm-5.1) love to prefix\n * their answer with internal monologue like:\n * \"The user wants a joke. I can respond directly without needing any\n * tools. Why don't scientists trust atoms? Because they make up\n * everything.\"\n *\n * We can't prevent this reliably via a system directive because the\n * model ignores it 30-50% of the time. Instead, we detect and strip\n * the preamble server-side after the model has finished generating,\n * keeping only the actual answer.\n *\n * Strategy: the preamble is a sequence of sentences that start with\n * narrator openers (\"The user wants\", \"I can/should/will respond\",\n * \"Let me\", \"Actually\", \"Looking at\", \"I'll\"). We strip those\n * sentences off the front until we hit content that doesn't start\n * with a narrator opener. That's the real answer.\n */\nexport function stripNarratorPreamble(text: string): string {\n if (!text || text.length < 10) return text;\n const NARRATOR_OPENERS = [\n /^\\s*the user (?:wants|asked|said|is asking|is requesting|needs|wrote|mentioned|told me)/i,\n /^\\s*(?:the\\s+)?user (?:wants|asked|said|is asking)/i,\n /^\\s*I (?:should|need to|can|will|must|could|'ll|'m going to) (?:respond|reply|answer|provide|give|explain|tell|just|simply)/i,\n /^\\s*I['']m (?:going to|about to) (?:respond|reply|answer)/i,\n /^\\s*(?:let me|let's)\\b/i,\n /^\\s*(?:actually|okay|alright|hmm|well|so|right),?\\s+(?:I|let|the)/i,\n /^\\s*looking at (?:this|the|what)/i,\n /^\\s*(?:this is|that['']s) (?:a|an) (?:casual|simple|direct|basic|friendly|quick)/i,\n /^\\s*no tools? (?:needed|required)/i,\n /^\\s*(?:i can|i['']ll) (?:respond|reply|answer) (?:directly|simply|without|naturally)/i,\n ];\n\n // Split into sentences, attempt to strip leading narrator sentences.\n // Use a conservative split that respects common sentence terminators.\n const sentences: string[] = [];\n let buffer = '';\n for (let i = 0; i < text.length; i++) {\n buffer += text[i];\n if (/[.!?]/.test(text[i])) {\n // Lookahead for end of sentence — next char should be whitespace or newline\n const next = text[i + 1];\n if (!next || /\\s/.test(next)) {\n sentences.push(buffer);\n buffer = '';\n }\n }\n }\n if (buffer) sentences.push(buffer);\n\n let stripCount = 0;\n for (const sentence of sentences) {\n if (NARRATOR_OPENERS.some(p => p.test(sentence))) {\n stripCount++;\n } else {\n break;\n }\n }\n\n if (stripCount === 0) return text;\n // Limit: never strip more than 75% of the content, or 3 sentences.\n // If the whole thing looks like narrator, leave it for the sanitizer\n // to catch as a hard fail (the sanitizer will fallback to a safe msg).\n if (stripCount >= sentences.length || stripCount > 3) return text;\n const remaining = sentences.slice(stripCount).join('').trim();\n if (remaining.length < 5) return text;\n return remaining;\n}\n\n// ── Helper: strip leaked tool JSON from LLM responses ────────────────\n\nfunction stripToolJson(text: string): string {\n let cleaned = text.replace(/\\s*\\{\"(?:name|tool_call)\":\\s*\"[^\"]+\",\\s*\"(?:parameters|arguments)\":\\s*\\{[^}]*\\}\\s*\\}\\s*/g, '').trim();\n // Hunt Finding #21 (2026-04-14): also strip minimax:tool_call XML blocks\n // and bare <invoke>/<parameter> tags that models sometimes emit as text\n // when they want to call a tool but shouldn't (e.g., in the respond phase\n // which runs with tools: undefined). Without this, the raw XML reaches\n // result.content, which bypasses the empty-response retry and forces the\n // gateway-level sanitizer to strip + fallback, losing task confirmation.\n cleaned = cleaned.replace(/<minimax:tool_call>[\\s\\S]*?<\\/minimax:tool_call>/g, '').trim();\n cleaned = cleaned.replace(/<minimax:tool_call>[\\s\\S]*$/g, '').trim(); // unclosed\n cleaned = cleaned.replace(/<invoke\\s+name=[\"'][^\"']*[\"']>[\\s\\S]*?<\\/invoke>/g, '').trim();\n cleaned = cleaned.replace(/<invoke\\s+name=[\"'][^\"']*[\"']>[\\s\\S]*$/g, '').trim(); // unclosed\n cleaned = cleaned.replace(/<parameter\\s+name=[\"'][^\"']*[\"']>[\\s\\S]*?<\\/parameter>/g, '').trim();\n cleaned = cleaned.replace(/<\\/?(?:invoke|parameter|minimax:tool_call)[^>]*>/g, '').trim();\n return cleaned;\n}\n\n// ── Helper: extract tool call from text content (ToolRescue) ─────────\n// Moved inline to avoid circular dependency. This is the rescue logic\n// for models that describe tool calls in text instead of structured output.\n\nfunction extractToolCallFromContent(\n content: string,\n activeTools: ToolDefinition[],\n isCloudModel = false,\n): ToolCall | null {\n if (!content || content.length < 10) return null;\n const toolNames = activeTools.map(t => t.function.name);\n\n // Strategy 1a: Embedded JSON tool calls\n const jsonMatch = content.match(/\\{\"(?:name|tool_call)\":\\s*\"([^\"]+)\",\\s*\"(?:parameters|arguments)\":\\s*(\\{[^}]*(?:\\{[^}]*\\}[^}]*)?\\})\\s*\\}/);\n if (jsonMatch && toolNames.includes(jsonMatch[1])) {\n return { id: `rescue_${Date.now()}`, type: 'function', function: { name: jsonMatch[1], arguments: jsonMatch[2] } };\n }\n\n // Strategy 1b: DeepSeek XML-style <function_call> format\n const xmlMatch = content.match(/<function_call>\\s*(\\{[\\s\\S]*?\\})\\s*<\\/function_call>/);\n if (xmlMatch) {\n try {\n const parsed = JSON.parse(xmlMatch[1]);\n const name = parsed.name || parsed.function?.name;\n const args = parsed.arguments || parsed.parameters || parsed.function?.arguments;\n if (name && toolNames.includes(name)) {\n return { id: `rescue_${Date.now()}`, type: 'function', function: { name, arguments: typeof args === 'string' ? args : JSON.stringify(args || {}) } };\n }\n } catch { /* malformed */ }\n }\n\n // Strategy 2: Natural language tool mentions\n // Cloud models often describe tool calls in text → empty skipSet to rescue all tools.\n // Local models handle structured tool calls fine → skip common tools to avoid false rescues.\n const skipSet = isCloudModel\n ? new Set<string>()\n : new Set(['shell', 'read_file', 'write_file', 'edit_file', 'list_dir', 'memory', 'web_search', 'web_fetch', 'tool_search']);\n\n for (const toolName of toolNames) {\n if (skipSet.has(toolName)) continue;\n const mentionRegex = new RegExp(\n `(?:call(?:ing)?|us(?:e|ing)|invok(?:e|ing)|execut(?:e|ing)|runn?(?:ing)?|tool\\\\s+)\\\\s*(?:the\\\\s+)?(?:tool\\\\s+)?(?:named\\\\s+)?[\"\\`']?${toolName.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')}[\"\\`']?`,\n 'i',\n );\n if (!mentionRegex.test(content)) continue;\n\n // Try to extract JSON args\n const jsonArgs = content.match(/\\{[\\s\\S]*?\\}/);\n if (jsonArgs) {\n try {\n const parsed = JSON.parse(jsonArgs[0]);\n if (typeof parsed === 'object' && parsed !== null) {\n const args: Record<string, string> = {};\n for (const [k, v] of Object.entries(parsed)) {\n args[k] = typeof v === 'string' ? v : JSON.stringify(v);\n }\n return { id: `rescue_${Date.now()}`, type: 'function', function: { name: toolName, arguments: JSON.stringify(args) } };\n }\n } catch { /* not valid JSON */ }\n }\n\n // Tool-specific extraction for common tools\n if (toolName === 'shell') {\n const cmdMatch = content.match(/(?:`{1,3}(?:bash|sh|shell)?\\n?(.*?)`{1,3}|(?:command|run|execute)[=:\\s]+[\"'](.+?)[\"'])/s);\n if (cmdMatch) {\n const cmd = (cmdMatch[1] || cmdMatch[2]).trim();\n if (cmd.length > 0) return { id: `rescue_${Date.now()}`, type: 'function', function: { name: 'shell', arguments: JSON.stringify({ command: cmd }) } };\n }\n }\n if (toolName === 'read_file' || toolName === 'write_file' || toolName === 'edit_file') {\n const pathMatch = content.match(/(?:file|path)[=:\\s]+[\"']?((?:\\/|~\\/|\\.\\/)\\S+?)[\"'\\s,)]/i)\n || content.match(/((?:\\/|~\\/)\\S+\\.\\w{1,10})/);\n if (pathMatch) {\n if (toolName === 'write_file') {\n // Try to extract content to write from code blocks\n const codeBlock = content.match(/```[\\w]*\\n([\\s\\S]*?)```/);\n const writeContent = codeBlock ? codeBlock[1] : '';\n return { id: `rescue_${Date.now()}`, type: 'function', function: { name: 'write_file', arguments: JSON.stringify({ path: pathMatch[1], content: writeContent }) } };\n } else if (toolName === 'edit_file') {\n // edit_file needs target+replacement — rescue as read_file first so the agent can see the file\n return { id: `rescue_${Date.now()}`, type: 'function', function: { name: 'read_file', arguments: JSON.stringify({ path: pathMatch[1] }) } };\n } else {\n return { id: `rescue_${Date.now()}`, type: 'function', function: { name: 'read_file', arguments: JSON.stringify({ path: pathMatch[1] }) } };\n }\n }\n }\n if (toolName === 'web_search') {\n const queryMatch = content.match(/(?:search(?:ing)?(?:\\s+for)?|query)[=:\\s]+[\"'](.+?)[\"']/i)\n || content.match(/search(?:ing)?\\s+(?:for\\s+)?[\"'](.+?)[\"']/i);\n if (queryMatch) return { id: `rescue_${Date.now()}`, type: 'function', function: { name: 'web_search', arguments: JSON.stringify({ query: queryMatch[1] }) } };\n }\n }\n\n return null;\n}\n\n// ── Helper: find a fallback model for tool calling ───────────────────\n\nfunction findToolCapableFallback(failedModel: string, failedModels: Set<string>, config: TitanConfig): string | null {\n const candidates: string[] = [];\n const toolCapable = (config.agent as Record<string, unknown>).toolCapableModels as string[] | undefined;\n if (toolCapable?.length) candidates.push(...toolCapable);\n const chain = (config.agent as Record<string, unknown>).fallbackChain as string[] | undefined;\n if (chain?.length) candidates.push(...chain);\n const aliases = (config.agent as Record<string, unknown>).modelAliases as Record<string, string> | undefined;\n if (aliases?.fast) candidates.push(aliases.fast);\n if (aliases?.smart) candidates.push(aliases.smart);\n return candidates.filter(m => m !== failedModel && !failedModels.has(m))[0] || null;\n}\n\n// ── Tool Result Summarization ───────────────────────────────────\n\n/** Extract key data points from large file contents */\nfunction summarizeToolResult(content: string): string | null {\n const parts: string[] = [];\n\n // Extract version numbers\n const versions = content.match(/[\"']?version[\"']?\\s*[:=]\\s*[\"']?([\\d.]+)[\"']?/gi);\n if (versions) parts.push(`Versions found: ${versions.slice(0, 3).join(', ')}`);\n\n // Extract name/title\n const names = content.match(/[\"']?name[\"']?\\s*[:=]\\s*[\"']([^\"']+)[\"']/i);\n if (names) parts.push(`Name: ${names[1]}`);\n\n // Extract exports/functions\n const exports = content.match(/export\\s+(?:async\\s+)?function\\s+(\\w+)/g);\n if (exports) parts.push(`Exports: ${exports.slice(0, 5).map(e => e.replace(/export\\s+(async\\s+)?function\\s+/, '')).join(', ')}`);\n\n // Extract constants\n const constants = content.match(/(?:const|export const)\\s+([A-Z_]+)\\s*=\\s*['\"]([^'\"]+)['\"]/g);\n if (constants) parts.push(`Constants: ${constants.slice(0, 3).join('; ')}`);\n\n // File line count\n const lineCount = content.split('\\n').length;\n parts.push(`${lineCount} lines total`);\n\n return parts.length > 1 ? parts.join(' | ') : null;\n}\n\n// ── Response Validation ─────────────────────────────────────────\n\n/** Detect if the response is missing specific data the user asked for */\nfunction detectResponseGap(userMessage: string, response: string, messages: ChatMessage[]): string | null {\n const lower = userMessage.toLowerCase();\n const respLower = response.toLowerCase();\n\n // Check: user asked for a number/version/count but response has none\n if (/\\b(version|number|count|how many|what is the|tell me the)\\b/.test(lower)) {\n const hasNumber = /\\d+/.test(response);\n // Check if tool results contain numbers the response missed\n const toolResults = messages.filter(m => m.role === 'tool').slice(-3);\n const toolHasNumber = toolResults.some(m => /\\d+\\.\\d+\\.\\d+|\\b\\d{1,5}\\b/.test(m.content || ''));\n if (!hasNumber && toolHasNumber) {\n return 'The user asked for a specific number or version. Your tool results contain this data but your response does not include it.';\n }\n }\n\n // Check: user asked to read a file but response doesn't reference its content\n if (/\\b(read|show|contents? of|what does|what.s in)\\b/.test(lower)) {\n if (respLower.includes('i read') || respLower.includes('i was able to') || respLower.includes('here is')) {\n // Response claims to have read — probably fine\n return null;\n }\n const toolResults = messages.filter(m => m.role === 'tool').slice(-3);\n if (toolResults.length > 0 && response.length < 50) {\n return 'The user asked you to read file contents. Your tool returned data but your response is too short — include the relevant information.';\n }\n }\n\n // Check: user asked for a specific value (e.g., \"the TITAN_VERSION value\")\n if (/\\b(value|result|output|answer)\\b/.test(lower)) {\n if (response.length < 20 || respLower.includes('error') || respLower.includes('unable')) {\n const toolResults = messages.filter(m => m.role === 'tool').slice(-3);\n if (toolResults.some(m => (m.content || '').length > 50)) {\n return 'The user asked for a specific value. Your tools retrieved data but your response did not include it.';\n }\n }\n }\n\n return null;\n}\n\n// ── Main Loop ──────────────────────────���───────────────────────────���─\n\nexport async function runAgentLoop(ctx: LoopContext): Promise<LoopResult> {\n const result: LoopResult = {\n content: '',\n toolsUsed: [],\n orderedToolSequence: [],\n modelUsed: ctx.activeModel,\n promptTokens: 0,\n completionTokens: 0,\n budgetExhausted: false,\n toolCallDetails: [],\n };\n\n let phase: AgentPhase = 'think';\n let round = 0;\n let activeModel = ctx.activeModel;\n\n // Self-heal state\n let modelSwitchCount = 0;\n let selfHealExhausted = false;\n const failedModels = new Set<string>();\n\n // Prompt budget initialization (Space Agent parity)\n const budgetConfig = getDefaultBudget(ctx.config);\n if (budgetConfig.maxTokens > 0) {\n initBudget(ctx.sessionId, budgetConfig);\n }\n\n // Bounded retries for [NoTools] rounds — prevents infinite think-loop when\n // the model keeps returning prose instead of tool calls\n let noToolsRetryCount = 0;\n const MAX_MODEL_SWITCHES = 2;\n\n // Reflection state\n let pivotCount = 0;\n const MAX_PIVOTS = 1;\n const failedApproaches: string[] = [];\n setProgressSession(ctx.sessionId);\n resetProgress(ctx.sessionId);\n\n // Learning state\n let lastFailedTool: { name: string; error: string } | null = null;\n\n // Tool search state\n const discoveredTools = new Set<string>();\n\n // Shell-for-files nudge counter\n let shellForFilesCount = 0;\n\n // Response validation retry flag (one retry max)\n let responseValidationRetried = false;\n // Empty response retry flag (one retry max)\n let emptyResponseRetried = false;\n\n // Force tool_choice=required on next think phase (set by incomplete task guard)\n let forceWriteOnNextThink = false;\n\n // F5: Budget soft warning — only inject once per loop\n let budgetWarningSent = false;\n\n // Pending tool calls from think phase (passed to act phase)\n let pendingToolCalls: ToolCall[] = [];\n let pendingAssistantContent = '';\n\n // Token budget for context compression\n const tokenBudget = ctx.voiceFastPath ? 2000 : (ctx.config.agent as Record<string, unknown>).tokenBudget as number || DEFAULT_MAX_TOKENS;\n\n // v5.0: Register this session for steering\n globalSteerQueues.set(ctx.sessionId, ctx.steerQueue ?? []);\n\n // v5.0: Initialize OTEL trace context for this run\n const traceCtx = newTraceContext();\n initOtel();\n\n // v5.0: Inactivity timeout tracking. Schema doesn't strictly type the\n // new feature flags yet — read defensively so a missing field doesn't\n // crash the loop, just falls through to the documented default.\n let lastActivityAt = Date.now();\n const agentCfg = ctx.config.agent as Record<string, unknown> | undefined;\n const inactivityTimeout = (agentCfg?.inactivityTimeoutMs as number | undefined) ?? 300_000;\n const absoluteTimeout = (agentCfg?.absoluteTimeoutMs as number | undefined) ?? 600_000;\n const loopStartTime = Date.now();\n function recordActivity() { lastActivityAt = Date.now(); }\n\n // ── Set session context for spawn_agent async delegation ─────\n setCurrentSessionId(ctx.sessionId);\n\n // ── Inject completed async sub-agent results as context ──────\n const cpEnabled = (ctx.config.commandPost as Record<string, unknown> | undefined)?.enabled ?? false;\n if (cpEnabled) {\n const completedAsync = drainPendingResults(ctx.sessionId);\n if (completedAsync.length > 0) {\n const injection = completedAsync.map(r =>\n `[Async Task Complete] ${r.issueIdentifier} (${r.agentName}): ${r.result.success ? 'SUCCESS' : 'FAILED'}\\n${r.result.content.slice(0, 500)}`\n ).join('\\n\\n');\n ctx.messages.push({ role: 'user', content: injection });\n logger.info(COMPONENT, `[AsyncResults] Injected ${completedAsync.length} completed async result(s) into context`);\n }\n\n // ── Heartbeat inbox check: claim and process pending sub-agent tasks ──\n if (ctx.agentId && round > 0 && round % 3 === 0) {\n // Check inbox every 3 rounds to avoid thrashing\n await checkAndProcessInbox(ctx.agentId);\n }\n }\n\n // ── Process any pending inbox work before this round ──\n if (cpEnabled && ctx.agentId && round === 0) {\n await checkAndProcessInbox(ctx.agentId);\n }\n\n while (phase !== 'done' && round < ctx.effectiveMaxRounds) {\n // ── Abort check ──────────────────���───────────────────────\n if (ctx.signal?.aborted || isKilled()) {\n logger.info(COMPONENT, `Session aborted by user at round ${round + 1} (${phase} phase)`);\n result.content = '[Stopped by user]';\n break;\n }\n\n // Emit round info\n if (round > 0) ctx.streamCallbacks?.onThinking?.();\n ctx.streamCallbacks?.onRound?.(round + 1, ctx.effectiveMaxRounds);\n\n logger.info(COMPONENT, `Round ${round + 1}/${ctx.effectiveMaxRounds} — phase: ${phase}, model: ${activeModel}, tools: ${phase === 'respond' ? 0 : ctx.activeTools.length}`);\n\n // v5.0: Shell hook — on_round_start\n if (ctx.config.hooks?.shell?.enabled) {\n await runShellHooks('on_round_start', {\n TITAN_SESSION_ID: ctx.sessionId,\n TITAN_AGENT_ID: ctx.agentId || 'default',\n TITAN_ROUND: String(round),\n });\n }\n\n switch (phase) {\n\n // ══════════��══════════════════════════���═════════════════════\n // THINK PHASE — Call LLM with tools available\n // ══════════════════════════════════════════���════════════════\n case 'think': {\n // ── Reflection (autonomous mode, every N rounds) ──────\n if (ctx.reflectionEnabled && round > 0 && shouldReflect(round, ctx.reflectionInterval)) {\n try {\n const lastToolResult = ctx.messages.filter(m => m.role === 'tool').slice(-1)[0]?.content || '';\n const failedContext = failedApproaches.length > 0 ? failedApproaches.join('; ') : undefined;\n const reflectionResult = await reflect(round, result.toolsUsed, ctx.message, lastToolResult, failedContext);\n\n if (reflectionResult.decision === 'stop') {\n logger.info(COMPONENT, `Reflection says stop at round ${round + 1}: ${reflectionResult.reasoning}`);\n ctx.messages.push({ role: 'user', content: `You've reflected on your progress and decided you have enough information. Respond to the user now with your findings. Reasoning: ${sanitizeReflection(reflectionResult.reasoning)}` });\n phase = 'respond';\n continue;\n } else if (reflectionResult.decision === 'pivot' && pivotCount < MAX_PIVOTS) {\n pivotCount++;\n const toolsSummary = [...new Set(result.toolsUsed)].join(', ');\n const approachSummary = `Attempted tools: ${toolsSummary}. Result: ${sanitizeReflection(reflectionResult.reasoning)}`;\n failedApproaches.push(approachSummary);\n logger.info(COMPONENT, `PIVOT at round ${round + 1}: ${reflectionResult.reasoning}`);\n\n // Clear accumulated tool results but keep system prompt + original message\n const systemMsg = ctx.messages.find(m => m.role === 'system');\n const userMsg = ctx.messages.find(m => m.role === 'user' && !m.content.startsWith('['));\n ctx.messages.length = 0;\n if (systemMsg) ctx.messages.push(systemMsg);\n if (userMsg) ctx.messages.push(userMsg);\n ctx.messages.push({ role: 'user', content: `Warning: STRATEGIC PIVOT: Your previous approach failed.\\nWhat was tried: ${approachSummary}\\nWhy it failed: ${sanitizeReflection(reflectionResult.reasoning)}\\n\\nTry a COMPLETELY DIFFERENT strategy. Do NOT repeat the same tools or approach.` });\n\n resetProgress();\n result.toolsUsed.length = 0;\n result.orderedToolSequence.length = 0;\n } else if (reflectionResult.decision === 'pivot' && pivotCount >= MAX_PIVOTS) {\n // Pivot limit reached — inject guidance instead of silently ignoring\n logger.warn(COMPONENT, `Pivot limit reached (${MAX_PIVOTS}), injecting adjustment instead`);\n ctx.messages.push({ role: 'user', content: `Your approach isn't working but you've already pivoted ${MAX_PIVOTS} time(s). Instead of starting over, try a SMALL adjustment: ${sanitizeReflection(reflectionResult.reasoning)}. Focus on what's most likely to succeed with the tools you have.` });\n } else if (reflectionResult.decision === 'adjust') {\n ctx.messages.push({ role: 'user', content: `Reflection suggests adjusting approach: ${sanitizeReflection(reflectionResult.reasoning)}. Try a different strategy.` });\n }\n // 'continue' → no injection, just keep going\n } catch (e) {\n logger.warn(COMPONENT, `Reflection failed, continuing: ${(e as Error).message}`);\n }\n }\n\n // ── Graceful degradation near round limit ─────────────\n if (round >= ctx.effectiveMaxRounds - 2 && round >= 3) {\n ctx.messages.push({\n role: 'user',\n content: `We're running short on time for this task. Please summarize what you've accomplished so far and give the user a clear answer.`,\n });\n phase = 'respond';\n continue;\n }\n\n // ── Context compression ─────��────────────────────────\n let smartMessages: ChatMessage[];\n if (ctx.voiceFastPath) {\n smartMessages = ctx.messages as ChatMessage[];\n } else {\n // Hunt Finding #14 (2026-04-14): previously this filter dropped ALL tool\n // messages on round 3+, leaving their parent assistant-with-tool_calls\n // messages orphaned. validateToolPairs would then either synthesize\n // placeholders (history preserved but content lost) or drop assistants\n // (history destroyed). Either way, the model lost data and made bad\n // decisions (e.g., writing empty files).\n //\n // Use 5-phase smart context builder only — avoids double-compression\n // that destroyed history when maybeCompressContext + buildSmartContext\n // both ran sequentially (the former mutated ctx.messages in-place).\n smartMessages = buildSmartContext(ctx.messages as ChatMessage[], tokenBudget);\n }\n\n // ── Response cache check ─────────────────────────────\n const cachedResponse = getCachedResponse(smartMessages, activeModel);\n if (cachedResponse) {\n logger.info(COMPONENT, `Cache hit — skipping LLM call`);\n result.content = cachedResponse;\n phase = 'done';\n break;\n }\n\n // ── Per-turn model routing ───────────────────────────\n const lastUserContent = ctx.message || '';\n const recentAssistant = smartMessages.filter(m => m.role === 'assistant').slice(-1)[0]?.content || '';\n const turnCtx: TurnContext = {\n round,\n messageLength: lastUserContent.length,\n hasCode: /```|\\bfunction\\b|\\bclass\\b|\\bconst\\b|\\bdef\\b/.test(recentAssistant),\n hasUrls: /https?:\\/\\//.test(recentAssistant),\n };\n const routeResult = routeModel(lastUserContent, activeModel, undefined, turnCtx);\n if (routeResult.model !== activeModel && routeResult.willSaveMoney) {\n logger.info(COMPONENT, `[PerTurnRoute] Round ${round}: ${activeModel} → ${routeResult.model} (${routeResult.reason})`);\n activeModel = routeResult.model;\n }\n\n // ── Call LLM with tools ──────────────────────────────\n const thinkingMode = ctx.thinkingOverride || ctx.config.agent.thinkingMode || 'off';\n const isVoice = ctx.voiceFastPath;\n // Claude Code-style context management:\n // 1. Clear old tool results (keep last 5 in full, truncate older ones)\n // 2. Trim to recent messages if context is getting large\n if (smartMessages.length > 6) {\n // Collapse deliberation messages to prevent plan markdown from polluting context\n for (let i = smartMessages.length - 1; i >= 0; i--) {\n const msg = smartMessages[i];\n if (msg.role === 'assistant' && msg.content?.startsWith('[DELIBERATION]')) {\n smartMessages[i] = { ...msg, content: '[Prior deliberation plan — details omitted for brevity]' };\n continue; // don't count as tool result\n }\n }\n let toolResultCount = 0;\n for (let i = smartMessages.length - 1; i >= 0; i--) {\n const msg = smartMessages[i];\n if (msg.role === 'tool' || (msg.role === 'assistant' && msg.toolCalls)) {\n toolResultCount++;\n if (toolResultCount > 5 && msg.content) {\n // Truncate ALL old tool results outside keep-5 window (regardless of length)\n smartMessages[i] = { ...msg, content: '[Earlier tool result cleared — ' + msg.content.slice(0, 80) + '...]' };\n }\n }\n }\n }\n\n // Hard trim if too many messages — Hunt Finding #09: pair-aware trim\n // preserves tool_call/tool_result pairs so validateToolPairs doesn't\n // then drop assistant messages with orphaned tool calls.\n if (smartMessages.length > 12 && phase !== 'respond') {\n const before = smartMessages.length;\n smartMessages = trimPairAware(smartMessages, 12);\n // A11: Sanitize any remaining orphans (safety net, should be zero)\n smartMessages = validateToolPairs(smartMessages);\n if (smartMessages.length !== before) {\n logger.info(COMPONENT, `[ContextTrim] Pair-aware trim: ${before} → ${smartMessages.length} messages`);\n }\n }\n\n // A1: Validate tool call/result pairing before sending to LLM (LangGraph pattern)\n smartMessages = validateToolPairs(smartMessages);\n\n // F1: Pre-model hook — let plugins modify messages for LLM without changing ctx.messages\n if (ctx.beforeModelCall) {\n try {\n smartMessages = ctx.beforeModelCall([...smartMessages], round);\n } catch (e) {\n logger.warn(COMPONENT, `[PreModelHook] Hook threw: ${(e as Error).message} — using unmodified messages`);\n }\n }\n\n // Hunt Finding #38 (2026-04-15): for chat-pipeline messages that\n // complete in a single think-phase round, the respond-phase\n // directive (Finding #21) never fires — the model's raw think\n // output goes directly to the user (streaming, so the tokens are\n // already on screen) and the sanitizer catches narrator leaks at\n // the END by which point it's too late. Inject the directive\n // into the context BEFORE the first think call for chat pipelines\n // so the model's FIRST token is already post-directive.\n if (round === 0 && phase === 'think' && ctx.completionStrategy === 'single-round') {\n smartMessages = [\n ...smartMessages,\n {\n role: 'user' as const,\n content: '[System directive for this reply only] Respond directly to the user. RULES: (1) Do NOT narrate what the user asked — they already know. (2) Do NOT describe your reasoning, thinking, or what you\\'re about to do. (3) Do NOT start with \"The user asked\", \"Let me\", \"I should\", \"I\\'ll\", \"Actually\", \"Looking at\" — start with the actual answer. (4) Be brief and friendly. 1-3 sentences is usually enough. (5) No meta-commentary. Just the answer.',\n },\n ];\n }\n\n const chatOptions = {\n model: activeModel,\n messages: smartMessages,\n tools: ctx.activeTools.length > 0 ? ctx.activeTools : undefined,\n maxTokens: isVoice ? Math.min(ctx.config.agent.maxTokens, 300) : ctx.config.agent.maxTokens,\n temperature: ctx.config.agent.temperature,\n thinking: isVoice ? false : thinkingMode !== 'off',\n thinkingLevel: thinkingMode as 'off' | 'low' | 'medium' | 'high',\n forceToolUse: (\n // Hunt Finding #08 (2026-04-14): Only force tool_choice=required\n // on ROUND 0. After round 0, the model has tool results in context\n // and should be free to generate text OR call more tools as needed.\n // Previously, autonomous mode forced tools on every round, causing\n // ping-pong loops: model reads file → forced to call another tool →\n // calls shell → forced again → calls memory → loop detector kills it.\n // Only force when we haven't given the model a chance to finish.\n round === 0\n && ctx.activeTools.length > 0\n && (ctx.isAutonomous || ctx.taskEnforcementActive)\n && (ctx.config.agent as Record<string, unknown>).forceToolUse !== false\n && phase !== 'respond'\n // Hunt Finding #07: don't force tools on chat-pipeline messages\n && ctx.completionStrategy !== 'single-round'\n && ctx.pipelineType !== 'chat')\n || forceWriteOnNextThink // Incomplete task guard — specific retry\n // Hunt Finding #05: user explicitly asked to use a tool → force round 0\n || (round === 0\n && phase === 'think'\n && ctx.activeTools.length > 0\n && detectToolUseIntent(ctx.message || '')),\n providerOptions: ctx.providerOptions,\n };\n if (forceWriteOnNextThink) {\n forceWriteOnNextThink = false; // Reset after use\n logger.info(COMPONENT, '[IncompleteTask] Forcing tool_choice=required for write retry');\n }\n // Hunt Finding #05: log when explicit-intent is forcing tool use\n if (round === 0 && phase === 'think' && !ctx.isAutonomous && !ctx.taskEnforcementActive\n && ctx.activeTools.length > 0 && detectToolUseIntent(ctx.message || '')) {\n logger.info(COMPONENT, '[ExplicitIntent] User explicitly requested tool use — forcing tool_choice=required for round 1');\n }\n\n // Prompt budget check (Space Agent parity)\n const budgetMsg = budgetConfig.maxTokens > 0 ? checkBudget(ctx.sessionId, budgetConfig) : null;\n if (budgetMsg) {\n markExceeded(ctx.sessionId);\n result.content = budgetMsg;\n phase = 'done';\n break;\n }\n\n let response: ChatResponse;\n const thinkStart = Date.now();\n // Hunt Finding #38b (2026-04-15): for single-round chat pipelines,\n // the model's round-0 think output IS the user-facing answer (no\n // respond phase ever runs). With live SSE streaming, the raw\n // narrator tokens from a weak model hit the UI before the\n // sanitizer can run at the end — the user sees \"The user wants a\n // joke. I can respond directly without needing any tools. Why\n // don't scientists trust atoms?...\" on screen. Fix: DO NOT stream\n // chat-pipeline round-0 think output. Collect the full response,\n // run it through the sanitizer, THEN emit as a single block.\n // The client shows a typing indicator during the short wait\n // instead of streaming raw narrator tokens.\n const isChatRound0Think =\n round === 0\n && phase === 'think'\n && ctx.completionStrategy === 'single-round';\n\n if (ctx.streamCallbacks?.onToken && !isChatRound0Think) {\n let streamContent = '';\n const streamToolCalls: ToolCall[] = [];\n const smartMessagesJson = JSON.stringify(smartMessages); // cache for token estimation\n for await (const chunk of chatStream(chatOptions)) {\n if (chunk.type === 'text' && chunk.content) {\n streamContent += chunk.content;\n ctx.streamCallbacks.onToken(chunk.content);\n } else if (chunk.type === 'tool_call' && chunk.toolCall) {\n streamToolCalls.push(chunk.toolCall);\n ctx.streamCallbacks.onToolCall?.(chunk.toolCall.function.name, JSON.parse(chunk.toolCall.function.arguments || '{}'));\n } else if (chunk.type === 'error') {\n logger.error(COMPONENT, `Stream error: ${chunk.error}`);\n } else if (chunk.type === 'retry') {\n // Out-of-band status — log internally + surface via callback.\n // CRITICAL: do NOT append to streamContent. The router used to\n // leak retry banners (\"[Retrying request (1/4)...]\") into the\n // text stream; the dedicated chunk type and this isolated\n // branch are the fix.\n logger.warn(COMPONENT, `Stream retrying ${chunk.provider}/${chunk.model} (attempt ${chunk.attempt}/${chunk.maxRetries}, reason=${chunk.reason}, delay=${chunk.delayMs}ms)`);\n ctx.streamCallbacks.onRetry?.({\n attempt: chunk.attempt,\n maxRetries: chunk.maxRetries,\n reason: chunk.reason,\n provider: chunk.provider,\n model: chunk.model,\n delayMs: chunk.delayMs,\n });\n } else if (chunk.type === 'failover') {\n // Same isolation rule — failover banners must not leak into the\n // assistant's response text.\n logger.warn(COMPONENT, `Stream failover from ${chunk.originalProvider}/${chunk.originalModel}${chunk.error ? ` (${chunk.error})` : ''}`);\n ctx.streamCallbacks.onFailover?.({\n originalProvider: chunk.originalProvider,\n originalModel: chunk.originalModel,\n error: chunk.error,\n });\n }\n }\n // Estimate token counts for streaming (servers don't report usage in stream mode)\n const estCompletionTokens = estimateTokens(streamContent + JSON.stringify(streamToolCalls));\n const estPromptTokens = estimateTokens(smartMessagesJson);\n response = {\n id: `stream-${Date.now()}`,\n content: streamContent,\n toolCalls: streamToolCalls.length > 0 ? streamToolCalls : undefined,\n usage: { promptTokens: estPromptTokens, completionTokens: estCompletionTokens, totalTokens: estPromptTokens + estCompletionTokens },\n finishReason: streamToolCalls.length > 0 ? 'tool_calls' : 'stop',\n model: activeModel,\n };\n // v5.0: OTEL span for think-phase LLM call (streaming)\n fireSpan(traceCtx, 'model_call:think', Date.now() - thinkStart, { round: String(round), model: activeModel, phase: 'think', streamed: true });\n } else {\n // Non-streaming path. For chat-pipeline round-0 think we\n // deliberately come here so the sanitizer gets to see the\n // full response before the client does. We also strip any\n // leading narrator preamble that the model emitted despite\n // our respond directive (Hunt Finding #38b).\n response = await chatWithTimeout(() => chat(chatOptions), 'agentLoop:chat', 300_000);\n if (isChatRound0Think && response.content) {\n const stripped = stripNarratorPreamble(response.content);\n if (stripped !== response.content) {\n logger.info(COMPONENT, `[NarratorStrip] Removed ${response.content.length - stripped.length} chars of narrator preamble from chat response`);\n response.content = stripped;\n }\n }\n\n // Secret exfiltration guard — scan LLM response before it reaches user\n if (response.content) {\n const { redacted, clean } = scanForSecrets(response.content);\n if (!clean) {\n logger.warn(COMPONENT, 'Secrets detected in LLM response — redacted before delivery');\n response.content = redacted;\n }\n // v5.0: Full exfiltration scan (layer 2-5) when configured\n if (ctx.config.security?.secretScan?.level === 'full') {\n const scan = fullExfilScan(response.content, 'llm_response');\n if (scan.blocked) {\n logger.warn(COMPONENT, `Exfiltration scan blocked LLM response: ${scan.findings.map(f => f.type).join(', ')}`);\n }\n response.content = scan.redacted;\n }\n }\n // v5.0: OTEL span for think-phase LLM call\n fireSpan(traceCtx, 'model_call:think', Date.now() - thinkStart, { round: String(round), model: activeModel, phase: 'think' });\n }\n\n result.modelUsed = response.model;\n const promptTokens = response.usage?.promptTokens || 0;\n const completionTokens = response.usage?.completionTokens || 0;\n result.promptTokens += promptTokens;\n result.completionTokens += completionTokens;\n\n // ── Cost tracking (F5: two-tier budget enforcement) ──\n const costCheck = recordTokenUsage(ctx.sessionId, activeModel, promptTokens, completionTokens);\n\n // Prompt budget tracking (Space Agent parity)\n if (budgetConfig.maxTokens > 0) {\n recordUsage(ctx.sessionId, promptTokens, completionTokens);\n }\n\n // Wire LLM spend into Command Post budget policies\n const callCost = calculateActualCost(activeModel, { prompt: promptTokens, completion: completionTokens });\n const sessionGoal = getSessionGoal(ctx.sessionId);\n recordSpend(ctx.agentId || 'default', sessionGoal?.goalId, callCost);\n\n if (costCheck.budgetExceeded) {\n result.content = '⚠️ Daily spending limit reached. TITAN has paused to keep your API costs under control. You can increase the limit in settings or wait until tomorrow.';\n phase = 'done';\n break;\n }\n // F5: Soft warning at 80% — tell the LLM to wrap up efficiently\n if (costCheck.budgetWarning && !budgetWarningSent) {\n budgetWarningSent = true;\n ctx.messages.push({\n role: 'user',\n content: `[System: ⚠️ Budget notice — you've used 80%+ of today's spending limit ($${costCheck.dailyTotal.toFixed(4)}). Be efficient: avoid unnecessary tool calls, summarize when possible, and wrap up soon.]`,\n });\n logger.info(COMPONENT, `[BudgetSoftWarning] Injected 80% budget warning into conversation`);\n }\n\n heartbeat(ctx.sessionId);\n recordActivity();\n\n // v5.0: Secret exfiltration v2 — full scan on LLM responses when enabled\n const secretScanLevel = ctx.config.security?.secretScan?.level ?? 'tool_only';\n if (secretScanLevel === 'full' && response.content) {\n const scan = fullExfilScan(response.content, 'llm_response');\n if (scan.blocked) {\n logger.warn(COMPONENT, `Full exfil scan found issues in LLM response: ${scan.findings.map(f => f.type).join(', ')}`);\n response.content = scan.redacted;\n }\n }\n\n // ── No tool calls → check rescue paths or accept response ──\n if (!response.toolCalls || response.toolCalls.length === 0) {\n logger.warn(COMPONENT, `[NoTools] Model returned text (len=${response.content.length}): ${response.content.slice(0, 200)}`);\n\n // FabricationGuard: detect model claiming to have completed actions without tool calls\n // gemma4 says \"I've written X to file Y\" without actually calling write_file\n //\n // Hunt Finding #47 (2026-04-15): this guard was DESTROYING correctly\n // written files. When the model summarized \"all results were written\n // to /tmp/foo.txt\", the regex matched, content extraction failed, and\n // the file was overwritten with \"placeholder\" — nuking the real 198-byte\n // output. Fix: skip if the file already exists with >0 bytes (the write\n // already succeeded in a prior round). Also require an explicit content\n // extract — never fall back to \"placeholder\".\n const fabricationMatch = response.content.match(/(?:written|saved|created|wrote)\\s+(?:.*?)(?:to|at|in)\\s+[\"'`]?(\\/[\\w/.-]+\\.[a-z]+)[\"'`]?/i);\n if (fabricationMatch) {\n const filePath = fabricationMatch[1];\n // Check if the file already exists — if so, the write already\n // succeeded in a previous round and this is just the model\n // summarizing what it did. Don't overwrite.\n let fileAlreadyExists = false;\n try {\n const { existsSync, statSync } = await import('fs');\n fileAlreadyExists = existsSync(filePath) && statSync(filePath).size > 0;\n } catch { /* can't check, assume not */ }\n\n if (fileAlreadyExists) {\n logger.info(COMPONENT, `[FabricationGuard] File \"${filePath}\" already exists (${fileAlreadyExists ? 'has content' : 'empty'}) — skipping forced write (Hunt #47)`);\n } else {\n // Extract what should have been written — require explicit content\n const contentMatch = response.content.match(/(?:written|saved|wrote)\\s+[\"`]([^\"`]+)[\"`]/i);\n if (contentMatch) {\n const fileContent = contentMatch[1];\n logger.warn(COMPONENT, `[FabricationGuard] Model claimed to write \"${filePath}\" without tool call — forcing write_file`);\n response.toolCalls = [{\n id: `fab-${Date.now()}`,\n type: 'function' as const,\n function: { name: 'write_file', arguments: JSON.stringify({ path: filePath, content: fileContent }) },\n }];\n response.content = '';\n } else {\n logger.warn(COMPONENT, `[FabricationGuard] Model claimed to write \"${filePath}\" but no extractable content — skipping forced write to avoid placeholder damage (Hunt #47)`);\n }\n }\n }\n // Self-Heal: detect tool calling failure\n if (ctx.selfHealEnabled && !selfHealExhausted && ctx.activeTools.length > 0) {\n const toolFailure = checkToolCallCapability(ctx.sessionId, response.content, ctx.activeTools.length > 0);\n if (toolFailure) {\n const fallback = findToolCapableFallback(activeModel, failedModels, ctx.config);\n if (fallback) {\n logger.warn(COMPONENT, `[SelfHeal] ${activeModel} failed tool calling. Switching to ${fallback}`);\n failedModels.add(activeModel);\n activeModel = fallback;\n result.modelUsed = fallback;\n modelSwitchCount++;\n if (modelSwitchCount >= MAX_MODEL_SWITCHES) selfHealExhausted = true;\n resetToolCallFailures(ctx.sessionId);\n ctx.messages.push({ role: 'user', content: `Note: I'm now using a different assistant engine. Please continue with your task.` });\n // Stay in think phase — retry with new model\n continue;\n } else if (modelSwitchCount > 0) {\n selfHealExhausted = true;\n result.content = \"I'm having trouble getting that done right now. Running the built-in health check (titan doctor) or picking a different AI model in Settings usually fixes this.\";\n phase = 'done';\n break;\n }\n }\n }\n\n // ActionCompiler: if model output ACTION: directives, compile them to tool calls\n if (response.content && hasActionDirectives(response.content)) {\n const compiled = compileActions(response.content);\n if (compiled.length > 0) {\n logger.info(COMPONENT, `[ActionCompiler] Compiled ${compiled.length} actions from text`);\n // Execute first action, queue the rest\n const first = compiled[0];\n response.toolCalls = [{\n id: `ac-${Date.now()}`,\n type: 'function' as const,\n function: { name: first.tool, arguments: JSON.stringify(first.args) },\n }];\n response.content = '';\n // Store remaining actions for subsequent rounds\n if (compiled.length > 1) {\n const remaining = compiled.slice(1).map((a, i) => `ACTION: ${a.tool} ${a.args.path || a.args.command || ''}`).join('\\n');\n ctx.messages.push({ role: 'user', content: `[Queued actions]\\n${remaining}\\nExecute the next ACTION.` });\n }\n }\n }\n\n // IntentParser: aggressively extract tool calls from text content\n // Models like gemma4 often describe what they WANT to do instead of calling tools.\n // We parse the intent and generate the tool call for them.\n if (!response.toolCalls || response.toolCalls.length === 0) {\n const text = response.content;\n let rescued = false;\n\n // Pattern 1: Code blocks → write_file\n const codeBlockMatch = text.match(/```(?:html|typescript|javascript|python|css|json)\\n([\\s\\S]+?)```/);\n if (codeBlockMatch && codeBlockMatch[1].length > 50) {\n const pathMatch = text.match(/(?:save|write|create|update|file|path|to)[:\\s]+[\"'`]?(\\/[\\w/.-]+\\.[a-z]+)[\"'`]?/i)\n || text.match(/(\\/home\\/[\\w/.-]+\\.[a-z]+)/)\n || text.match(/(\\/[\\w]+\\/[\\w/.-]+\\.[a-z]+)/);\n if (pathMatch) {\n logger.info(COMPONENT, `[IntentParser] Code block → write_file(\"${pathMatch[1]}\", ${codeBlockMatch[1].length} chars)`);\n response.toolCalls = [{\n id: `intent-${Date.now()}`,\n type: 'function' as const,\n function: { name: 'write_file', arguments: JSON.stringify({ path: pathMatch[1], content: codeBlockMatch[1] }) },\n }];\n rescued = true;\n }\n }\n\n // Pattern 1b: \"I wrote/saved/created file X\" → write_file (past-tense fabrication)\n if (!rescued) {\n const pastWrite = text.match(/(?:wrote|written|saved|created)\\s+(?:.*?)(?:to|at|in)\\s+[\"'`]?(\\/[\\w/.-]+\\.[a-z]+)[\"'`]?/i);\n if (pastWrite) {\n const contentMatch = text.match(/(?:wrote|written|saved)\\s+[\"'`]([^\"'`]+)[\"'`]/i);\n logger.info(COMPONENT, `[IntentParser] Past-tense write → write_file(\"${pastWrite[1]}\")`);\n response.toolCalls = [{\n id: `intent-${Date.now()}`,\n type: 'function' as const,\n function: { name: 'write_file', arguments: JSON.stringify({ path: pastWrite[1], content: contentMatch ? contentMatch[1] : '' }) },\n }];\n rescued = true;\n }\n }\n\n // Pattern 2: \"I'll read/open/check file X\" → read_file\n if (!rescued) {\n const readIntent = text.match(/(?:read|open|check|look at|examine|view|inspect)\\s+(?:the\\s+)?(?:file\\s+)?[\"'`]?(\\/[\\w/.-]+\\.[a-z]+)[\"'`]?/i);\n if (readIntent) {\n logger.info(COMPONENT, `[IntentParser] Read intent → read_file(\"${readIntent[1]}\")`);\n response.toolCalls = [{\n id: `intent-${Date.now()}`,\n type: 'function' as const,\n function: { name: 'read_file', arguments: JSON.stringify({ path: readIntent[1] }) },\n }];\n rescued = true;\n }\n }\n\n // Pattern 3: \"I'll run/execute command X\" → shell\n if (!rescued) {\n const shellIntent = text.match(/(?:run|execute|running)\\s+(?:the\\s+)?(?:command\\s+)?[`]([^`]+)[`]/i);\n if (shellIntent) {\n logger.info(COMPONENT, `[IntentParser] Shell intent → shell(\"${shellIntent[1].slice(0, 60)}\")`);\n response.toolCalls = [{\n id: `intent-${Date.now()}`,\n type: 'function' as const,\n function: { name: 'shell', arguments: JSON.stringify({ command: shellIntent[1] }) },\n }];\n rescued = true;\n }\n }\n\n // Pattern 4: \"I'll edit/modify/update X in file Y\" → read_file (to prepare for edit)\n if (!rescued) {\n const editIntent = text.match(/(?:edit|modify|update|change|replace|add to)\\s+(?:the\\s+)?(?:file\\s+)?[\"'`]?(\\/[\\w/.-]+\\.[a-z]+)[\"'`]?/i);\n if (editIntent) {\n logger.info(COMPONENT, `[IntentParser] Edit intent → read_file(\"${editIntent[1]}\") (prep for edit)`);\n response.toolCalls = [{\n id: `intent-${Date.now()}`,\n type: 'function' as const,\n function: { name: 'read_file', arguments: JSON.stringify({ path: editIntent[1] }) },\n }];\n rescued = true;\n }\n }\n\n if (rescued) {\n // Clear the text content so it doesn't confuse the model on next round\n response.content = '';\n }\n }\n\n // ToolRescue: final attempt to extract a tool call from text content\n if (!response.toolCalls || response.toolCalls.length === 0) {\n const isCloudModel = activeModel.includes(':cloud') || activeModel.includes('-cloud');\n const rescuedToolCall = extractToolCallFromContent(response.content, ctx.activeTools, isCloudModel);\n if (rescuedToolCall) {\n logger.info(COMPONENT, `[ToolRescue] Extracted \"${rescuedToolCall.function.name}\" from content text`);\n response.toolCalls = [rescuedToolCall];\n // Fall through to tool_calls handling below\n }\n }\n\n // Hunt Finding #17 (2026-04-14): UserIntentRescue — when the model\n // ignores tool_choice=required AND all model-response-based rescue\n // paths fail, extract the intended tool call from the USER MESSAGE\n // directly. This catches the case where a weak model fabricates\n // plausible-sounding tool output (e.g. \"Permission denied\",\n // \"command returned null\") instead of actually running the tool.\n if (!response.toolCalls || response.toolCalls.length === 0) {\n const userIntent = extractToolCallFromUserMessage(ctx.message || '', ctx.activeTools);\n if (userIntent) {\n logger.warn(COMPONENT, `[UserIntentRescue] Model ignored tool_choice=required; extracting \"${userIntent.function.name}\" from user message`);\n response.toolCalls = [userIntent];\n response.content = '';\n // Fall through to tool_calls handling below\n }\n }\n\n // If ALL rescue paths failed (still no tool calls), run stall detection\n // and either nudge for retry or accept the text response. Without this\n // branch, an empty-toolCalls THINK round would fall through with phase\n // still 'think' and round un-incremented → infinite retry loop.\n if (!response.toolCalls || response.toolCalls.length === 0) {\n noToolsRetryCount++;\n\n // Stall detection\n const stallEvent = checkResponse(ctx.sessionId, response.content, round, ctx.effectiveMaxRounds);\n if (stallEvent) {\n const state = stallEvent as { nudgeCount?: number };\n const nudgeCount = state.nudgeCount ?? 0;\n\n // Hard kill after 2 nudges (Paperclip pattern: bounded retries)\n if (nudgeCount >= 2 || stallEvent.type === 'self_talk') {\n logger.error(COMPONENT, `[HardKill] Stall type \"${stallEvent.type}\" after ${nudgeCount} nudges — terminating`);\n result.content = result.content || pendingAssistantContent || 'Task terminated: agent was unable to make progress using tools. Please try rephrasing your request or breaking it into smaller steps.';\n phase = 'done';\n break;\n }\n\n const nudge = getNudgeMessage(stallEvent);\n logger.warn(COMPONENT, `Stall [${stallEvent.type}] (nudge ${nudgeCount + 1}/2) — nudging`);\n ctx.messages.push({ role: 'user', content: nudge });\n round++;\n // Stay in think phase — retry\n continue;\n }\n\n // No stall detected — bail out after 3 [NoTools] rounds in a row\n // to prevent the model spinning forever returning text instead of tools\n if (noToolsRetryCount >= 3) {\n // Gap 2 (plan-this-logical-ocean): if this run DID execute\n // tools earlier and is now stuck in no-tools land, check\n // the bounded continuation counter before bailing. This\n // is the \"empty_after_tools\" signal — the model forgot\n // what it was doing mid-task. One continuation nudge is\n // cheap; the counter persists across process restarts so\n // we can't loop forever.\n const didUseTools = result.toolsUsed.length > 0;\n if (didUseTools) {\n const { shouldContinue } = await import('./runContinuations.js');\n if (shouldContinue(ctx.sessionId, 'empty_after_tools')) {\n logger.warn(COMPONENT, `[Continuation] empty_after_tools — nudging once more (session ${ctx.sessionId})`);\n ctx.messages.push({\n role: 'user',\n content: \"You started on a task but didn't finish. Please continue with the next step, or if you're done, give the user a clear answer now.\",\n });\n noToolsRetryCount = 0; // fresh window after the continuation\n round++;\n continue;\n }\n }\n logger.warn(COMPONENT, `[NoTools] Bailing after ${noToolsRetryCount} consecutive no-tool rounds — accepting text response`);\n result.content = stripToolJson(response.content || pendingAssistantContent || 'I was unable to make progress using tools.');\n phase = 'done';\n break;\n }\n\n // In autonomous mode: if the model describes work instead of doing it,\n // push it back to use tools. This is the key behavioral pattern that\n // makes autonomous execution reliable — don't accept \"I would do X\"\n // when the model should be calling tools to actually do X.\n // In non-autonomous mode with multi-step pipelines (edit, deploy),\n // if the model describes work instead of doing it, push it back.\n const isMultiStepPipeline = ctx.completionStrategy === 'terminal-tool' || ctx.minRounds !== undefined;\n const nudgeMinRounds = ctx.minRounds ?? (ctx.completionStrategy === 'single-round' ? 1 : 2);\n const nudgeMinRoundsMet = round >= nudgeMinRounds;\n const nudgeEnabled = ctx.isAutonomous || (isMultiStepPipeline && !nudgeMinRoundsMet);\n if (nudgeEnabled && round < ctx.effectiveMaxRounds - 2) {\n // Hunt Finding #10 (2026-04-14): the previous regexes over-matched.\n // `|The` with no word boundary matched \"These\"/\"This\"/\"Then\"/\"There\"/\n // \"They\" — all common ways to start valid descriptive answers. And\n // describesWork only needed one weak indicator. Result: a correct\n // answer starting with \"These represent two classic attack categories...\"\n // got nudged to \"call a tool\", then the model emitted meta-commentary\n // that was accepted as the final answer.\n //\n // New rules (tight):\n // 1. futureIntentOpener requires an EXPLICIT future-action phrase\n // with a following verb (\"Let me VERB\", \"I'll VERB\", \"I will VERB\",\n // \"I need to VERB\"). Common openers like \"The\"/\"These\"/\"This\"/\n // \"Based on\"/\"Here's\" no longer trigger.\n // 2. describesWork also requires \"I'll/I will/I need to/Let me\"\n // followed by an actual work verb nearby.\n // 3. BOTH must match to fire — one weak match isn't enough.\n const futureIntentOpener = /^(let me\\s+\\w+|I['']?ll\\s+(?:start|begin|check|look|read|run|edit|write|create|try|go|investigate|verify|test|install|build|fix|update|change|set)|I\\s+(?:will|need to|should|can|am going to|plan to)\\s+\\w+|first,?\\s+I|now\\s+I|to\\s+(?:fix|resolve|complete|edit|write|create|update|change|run))\\b/i.test(response.content.trim());\n const describesWork = /\\b(?:I['']?ll|I (?:will|need to|should|plan to|am going to)|let me)\\b[^.]{0,80}\\b(?:fix|edit|change|update|create|write|modify|run|install|build|start|restart|read|open|debug|set up|check|look at|examine|investigate|verify|confirm|test)\\b/i.test(response.content);\n\n // BOTH must match to fire — prevents false positives on valid\n // descriptive answers that don't actually describe future work.\n if (futureIntentOpener && describesWork && noToolsRetryCount < 3) {\n noToolsRetryCount++;\n logger.info(COMPONENT, `[AutoPush] Model described intent to act without acting (${noToolsRetryCount}/3): \"${response.content.slice(0, 80)}...\"`);\n ctx.messages.push({ role: 'assistant', content: response.content });\n ctx.messages.push({ role: 'user', content: 'STOP describing. Call a tool RIGHT NOW. Use edit_file to fix code, write_file to create files, or shell to run commands. Your next response MUST be a tool call, not text.' });\n round++;\n continue;\n }\n }\n\n // Hunt Finding #05 (continuation): before accepting a text-only\n // response as final, check if it looks like fabricated tool output.\n // If the user requested verbatim/exact tool output AND we have\n // real tool results in this turn, prefer the real result.\n let finalText = stripToolJson(response.content || '');\n const lastToolResult = result.toolCallDetails.length > 0\n ? result.toolCallDetails[result.toolCallDetails.length - 1]\n : null;\n const userMsgLower = (ctx.message || '').toLowerCase();\n const wantsVerbatim = /\\b(?:verbatim|exactly|precise|literal|as(?: |-)is|raw output|just the output)\\b/.test(userMsgLower);\n\n if (lastToolResult && lastToolResult.success && wantsVerbatim && finalText.length < 300) {\n // Check if the text appears in the actual tool output — if NOT,\n // it's likely a hallucinated echo of what the model thinks the\n // tool should have returned. Replace with the real output.\n const realOutput = lastToolResult.resultSnippet || '';\n const textAppearsInResult = realOutput.length > 10 && realOutput.toLowerCase().includes(finalText.toLowerCase().slice(0, 40));\n if (!textAppearsInResult && realOutput.length > 0) {\n logger.warn(COMPONENT, `[HallucinationGuard] Text response \"${finalText.slice(0, 60)}...\" does NOT match real tool output \"${realOutput.slice(0, 60)}...\" — user asked for verbatim, using real tool output instead`);\n result.content = realOutput.length < 1500 ? realOutput : realOutput.slice(0, 1500) + '...[truncated]';\n setCachedResponse(smartMessages, activeModel, result.content);\n phase = 'done';\n break;\n }\n }\n\n // Auto-emit system widget gates when gallery_get returned a system widget\n // but the model failed to emit _____widget in its response (common with\n // local/cloud models that ignore formatting instructions).\n const galleryGetCalls = result.toolCallDetails.filter(d => d.name === 'gallery_get');\n logger.info(COMPONENT, `[AutoWidgetGate] Checking ${galleryGetCalls.length} gallery_get call(s) for system widgets`);\n for (const call of galleryGetCalls) {\n try {\n const snippet = call.resultSnippet || '';\n logger.info(COMPONENT, `[AutoWidgetGate] gallery_get snippet: ${snippet.slice(0, 200)}`);\n const parsed = JSON.parse(snippet);\n if (parsed.source && typeof parsed.source === 'string' && parsed.source.startsWith('system:')) {\n const gateText = `_____widget\\n{ \"name\": \"${parsed.name || 'Widget'}\", \"format\": \"system\", \"source\": \"${parsed.source}\", \"w\": ${parsed.defaultSize?.w || 6}, \"h\": ${parsed.defaultSize?.h || 6} }`;\n if (!finalText.includes('_____widget')) {\n finalText = finalText ? `${finalText}\\n\\n${gateText}` : gateText;\n logger.info(COMPONENT, `[AutoWidgetGate] Injected _____widget gate for ${parsed.source}`);\n }\n } else {\n logger.info(COMPONENT, `[AutoWidgetGate] Skipped — source is ${parsed.source}`);\n }\n } catch (e) {\n logger.info(COMPONENT, `[AutoWidgetGate] Parse error: ${(e as Error).message}`);\n }\n }\n\n // Model chose to respond directly — accept it\n result.content = finalText;\n setCachedResponse(smartMessages, activeModel, result.content);\n phase = 'done';\n break;\n }\n\n // Tool calls were rescued — reset the no-tools counter since the model is making progress\n noToolsRetryCount = 0;\n }\n\n // A7: Remaining steps guard (LangGraph pattern) — suppress tool calls near budget limit\n const remainingRounds = ctx.effectiveMaxRounds - round;\n if (ctx.isAutonomous && remainingRounds <= 1 && response.toolCalls && response.toolCalls.length > 0) {\n logger.warn(COMPONENT, `[BudgetGuard] Only ${remainingRounds} round(s) left — suppressing ${response.toolCalls.length} tool call(s), forcing text response`);\n response.toolCalls = undefined;\n result.budgetExhausted = true;\n if (!response.content || response.content.trim().length === 0) {\n response.content = pendingAssistantContent || 'I\\'ve used all available rounds. Here is what I accomplished so far.';\n }\n }\n\n // ── Model returned tool calls → transition to ACT ────\n if (response.toolCalls && response.toolCalls.length > 0) {\n pendingToolCalls = response.toolCalls;\n pendingAssistantContent = response.content || '';\n // Add assistant message with tool calls to history\n ctx.messages.push({\n role: 'assistant',\n content: pendingAssistantContent,\n toolCalls: pendingToolCalls,\n });\n phase = 'act';\n }\n break;\n }\n\n // ════════════════════════════���══════════════════════════════\n // ACT PHASE — Execute tool calls, record results\n // ══════════════════════════════════════���════════════════════\n case 'act': {\n logger.info(COMPONENT, `Executing ${pendingToolCalls.length} tool call(s)`);\n\n let toolResults: ToolResult[] = [];\n try {\n if (ctx.isKimiSwarm) {\n for (const tc of pendingToolCalls) {\n if (tc.function.name.startsWith('delegate_to_')) {\n const domainMatch = tc.function.name.match(/delegate_to_(.*)_agent/);\n const domain = (domainMatch ? domainMatch[1] : 'file') as Domain;\n let args;\n try { args = JSON.parse(tc.function.arguments); } catch { args = { instruction: '' }; }\n const startMs = Date.now();\n const resultString = await runSubAgent(domain, args.instruction, activeModel);\n toolResults.push({\n toolCallId: tc.id, name: tc.function.name, content: resultString,\n success: !resultString.includes('Error'), durationMs: Date.now() - startMs,\n });\n }\n }\n } else {\n const toolExecStart = Date.now();\n toolResults = await executeTools(pendingToolCalls, ctx.channel);\n fireSpan(traceCtx, 'tool_execution', Date.now() - toolExecStart, { round: String(round), tools: pendingToolCalls.map(t => t.function.name).join(',') });\n }\n } catch (err) {\n logger.error(COMPONENT, `Tool execution error: ${(err as Error).message}`);\n result.content = 'An error occurred while executing tools. Please try again.';\n phase = 'done';\n break;\n }\n\n // Emit tool results to watcher\n for (const tr of toolResults) {\n ctx.streamCallbacks?.onToolResult?.(\n tr.name, tr.content.slice(0, 500), tr.durationMs || 0,\n !tr.content.toLowerCase().includes('error'),\n tr.diff,\n );\n }\n\n // v5.0: Approval-gate pause — if any tool is awaiting human approval,\n // exit the loop immediately so the user sees the approval request.\n const pendingApproval = toolResults.find(r => r.approvalPending);\n if (pendingApproval) {\n logger.info(COMPONENT, `[ApprovalPause] Tool \"${pendingApproval.name}\" awaiting approval (req ${pendingApproval.approvalRequestId}) — exiting loop`);\n result.content = pendingApproval.content;\n // Push assistant + user messages so the user sees the approval request in context\n ctx.messages.push({\n role: 'assistant',\n content: `I need your approval before I can use ${pendingApproval.name}. ${pendingApproval.content}`,\n });\n phase = 'done';\n break;\n }\n\n // v4.13 ancestor-extraction (Hermes subdirectory_hints): for each\n // tool call that touched a new directory, append any AGENTS.md /\n // CLAUDE.md / .cursorrules from that directory + its ancestors to\n // the tool RESULT (not the system prompt — we keep prompt cache\n // stable). Guarded by try/catch so hint discovery can never break\n // tool execution.\n try {\n const { getSubdirTracker } = await import('./subdirHints.js');\n const tracker = getSubdirTracker(ctx.sessionId);\n for (let i = 0; i < toolResults.length; i++) {\n const tr = toolResults[i];\n const tc = pendingToolCalls[i];\n if (!tc) continue;\n let args: Record<string, unknown> = {};\n try { args = JSON.parse(tc.function.arguments || '{}'); } catch { /* non-fatal */ }\n const hints = tracker.checkToolCall(tr.name, args);\n if (hints) {\n tr.content = `${tr.content}\\n\\n${hints}`;\n }\n }\n } catch (err) {\n logger.debug(COMPONENT, `[SubdirHints] skipped: ${(err as Error).message}`);\n }\n\n // Sub-agent shortcut: force immediate summary\n // Sub-agent shortcut: sync sub-agents return full results, force RESPOND.\n // Async delegations (CP enabled) return just a confirmation — no shortcut needed.\n const hasSubAgent = toolResults.some(r => r.name === 'spawn_agent');\n const isAsyncDelegation = hasSubAgent && cpEnabled;\n if (hasSubAgent && !isAsyncDelegation) {\n for (const tr of toolResults) {\n result.toolsUsed.push(tr.name);\n const compressed = await compressToolResult(ctx.sessionId, tr.name, tr.toolCallId, tr.content, round);\n const subSuccess = !tr.content.toLowerCase().includes('error:');\n recordStep(ctx.sessionId, round, tr.name, subSuccess, tr.content.slice(0, 100));\n ctx.messages.push({ role: 'tool', content: compressed, toolCallId: tr.toolCallId, name: tr.name });\n }\n ctx.messages.push({ role: 'user', content: 'The sub-agent has completed its task. Summarize the results above and respond to the user. Do NOT call any more tools.' });\n logger.info(COMPONENT, `[SubAgent] spawn_agent completed (sync) — entering respond phase`);\n phase = 'respond';\n round++;\n break;\n }\n\n recordActivity();\n\n // v5.0: Process steer queue — inject mid-run nudges\n const steerQueue = globalSteerQueues.get(ctx.sessionId) ?? ctx.steerQueue ?? [];\n if (steerQueue.length > 0) {\n const steerPrompts = steerQueue.splice(0, steerQueue.length);\n for (const steer of steerPrompts) {\n // ChatMessage doesn't carry metadata in the public type, but the\n // Steer API needs to mark these so logging / replay can tell them\n // apart from genuine user turns. Cast through Record so the message\n // still serializes cleanly to providers that ignore extra keys.\n ctx.messages.push({\n role: 'user',\n content: `[Steer] ${steer}`,\n metadata: { steer: true },\n } as unknown as (typeof ctx.messages)[number]);\n logger.info(COMPONENT, `Steer injected: ${steer.slice(0, 100)}`);\n }\n }\n\n // Record tool results and check for loops\n let loopBroken = false;\n for (const tr of toolResults) {\n result.toolsUsed.push(tr.name);\n result.orderedToolSequence.push(tr.name);\n\n // Trajectory compression: shorten long tool results + record progress step\n const compressed = await compressToolResult(ctx.sessionId, tr.name, tr.toolCallId, tr.content, round);\n recordStep(ctx.sessionId, round, tr.name, !tr.content.toLowerCase().includes('error:'), tr.content.slice(0, 100));\n ctx.messages.push({ role: 'tool', content: compressed, toolCallId: tr.toolCallId, name: tr.name });\n\n // Stall detector\n const matchingTc = pendingToolCalls.find(tc => tc.id === tr.toolCallId);\n let tcArgs: Record<string, unknown> = {};\n try { tcArgs = JSON.parse(matchingTc?.function.arguments || '{}'); } catch { /* empty */ }\n\n // Soul: record this tool attempt\n recordAttempt(ctx.sessionId, `${tr.name}(${Object.keys(tcArgs).join(',')})`);\n\n // Record structured tool call details for inter-step context in deliberation\n // Hunt Finding #40: AutoVerify needs to flip tcSuccess/tr.success\n // when a write verify fails, so SmartExit doesn't treat the write\n // as terminal-success. Must be `let` for that mutation.\n let tcSuccess = !tr.content.toLowerCase().includes('error:');\n result.toolCallDetails.push({\n name: tr.name,\n args: tcArgs,\n resultSnippet: tr.content.slice(0, 300),\n success: tcSuccess,\n });\n\n // Tool result summarization disabled — was confusing models into\n // treating the summary as the final answer instead of continuing to act.\n // TODO: Re-enable when models handle injected context better.\n\n // Auto-verify file writes — catch silent truncation, empty files, broken HTML/JSON\n // Hunt Finding #40 (2026-04-15): previously AutoVerify only logged\n // a warning and pushed a suggestion. When glm-5.1 wrote to the\n // wrong path (/home/titan/... on a machine where user=dj), the\n // write was silently rejected by validatePath, AutoVerify warned\n // but tr.success stayed TRUE, SmartExit saw \"terminal tool\n // succeeded\" and transitioned to respond — dropping the file\n // entirely. Fix: flip tr.success=false on verify failure, and\n // mark the result with a clear [AutoVerify FAILED] banner so\n // the next think round sees it and retries.\n if (tr.name === 'write_file' || tr.name === 'append_file') {\n const vr = verifyFileWrite(tr.name, tcArgs, tr.content);\n if (!vr.passed) {\n logger.warn(COMPONENT, `[AutoVerify] ${tr.name}: ${vr.issue} — forcing retry (Hunt #40)`);\n // Flip success false so SmartExit doesn't treat this as\n // a terminal-tool success.\n tr.success = false;\n tcSuccess = false;\n // Mutate tr.content so the assistant sees the failure\n // in the tool_result message on the next think round.\n tr.content = `[AutoVerify FAILED] ${vr.issue}. Original tool output: ${tr.content}`;\n ctx.messages.push({\n role: 'user',\n content: `That didn't work as expected: ${vr.issue}${vr.suggestion ? `\\n\\nTry this: ${vr.suggestion}` : ''}\\n\\nPlease try again with the corrected approach.`,\n });\n }\n }\n\n // Shell-for-files nudge: when the model uses shell for file operations,\n // inject a redirect toward dedicated tools\n if (tr.name === 'shell') {\n const cmd = (tcArgs.command as string || '').trim();\n const isFileOp = /^\\s*(cat|head|tail|less|more|sed|awk)\\s+/.test(cmd)\n || /^\\s*grep\\s+.*\\s+\\S+\\.\\w+/.test(cmd)\n || /^\\s*curl\\s+/.test(cmd);\n if (isFileOp) {\n shellForFilesCount++;\n const verb = cmd.split(/\\s+/)[0];\n const nudgeMsg = shellForFilesCount >= 3\n ? `For file operations, please use the dedicated read_file and edit_file tools instead of shell commands. They work more reliably for editing code and text.\\n` +\n `Examples: use read_file instead of cat/head/tail/grep, and edit_file instead of sed/awk.`\n : `For file operations, please use read_file or edit_file instead of shell ${verb}. They are more reliable.`;\n ctx.messages.push({ role: 'user', content: nudgeMsg });\n logger.info(COMPONENT, `[ShellNudge] Shell-for-files detected (count=${shellForFilesCount}): ${cmd.slice(0, 60)}`);\n }\n }\n\n const loopEvent = recordToolCall(ctx.sessionId, tr.name, tcArgs);\n if (loopEvent) {\n const nudge = getNudgeMessage(loopEvent);\n logger.warn(COMPONENT, `Tool loop detected for ${tr.name} — nudging`);\n ctx.messages.push({ role: 'user', content: nudge });\n }\n\n // Loop detection\n const loopConfig = ctx.isAutonomous\n ? { globalCircuitBreakerThreshold: (ctx.config.autonomy as Record<string, unknown>).circuitBreakerOverride as number || 50 }\n : {};\n const loopCheck = checkForLoop(ctx.sessionId, tr.name, tcArgs, tr.content, loopConfig);\n if (!loopCheck.allowed) {\n logger.warn(COMPONENT, `Loop breaker [${loopCheck.level}]: ${loopCheck.reason}`);\n // Hunt Finding #24 (2026-04-14): instead of returning the\n // raw breaker debug message to the user (\"Ping-pong pattern\n // detected: weather ↔ memory repeated 3+ times\"), route\n // into the respond phase with a directive to summarize\n // what was actually collected from successful tools.\n // The breaker reason stays in logs but never in the reply.\n ctx.messages.push({\n role: 'user',\n content: \"Let's wrap this up. Based on what you've already found, give the user a direct answer now. Don't call any more tools — just answer their question using the information you've gathered.\",\n });\n phase = 'respond';\n loopBroken = true;\n break;\n }\n\n // Fruitless search detector: if the model keeps searching for files\n // across multiple rounds without finding them, force it to respond\n if ((tr.name === 'shell' || tr.name === 'list_dir' || tr.name === 'read_file') && round >= 3) {\n const searchTools = result.toolCallDetails.filter(d =>\n (d.name === 'shell' && /\\b(find|ls|grep|locate)\\b/.test(d.args.command as string || '')) ||\n d.name === 'list_dir'\n );\n if (searchTools.length >= 3) {\n const allFailed = searchTools.every(d =>\n d.resultSnippet.length < 50 || /not found|no such|empty|error/i.test(d.resultSnippet)\n );\n if (allFailed) {\n logger.warn(COMPONENT, `[FruitlessSearch] ${searchTools.length} search attempts failed — forcing respond`);\n ctx.messages.push({\n role: 'user',\n content: '[STOP SEARCHING] You have searched for this file/directory multiple times without success. It does not exist at this location. Stop searching and tell the user what you found (or that you could not find it). Do NOT run more find/ls/grep commands.',\n });\n }\n }\n }\n\n // Learning\n const success = !tr.content.toLowerCase().includes('error:');\n recordToolResult(tr.name, success, undefined, success ? undefined : tr.content.slice(0, 200));\n recordToolUsage(tr.name);\n recordToolPreference(tr.name, classifyTaskType(ctx.message), success);\n\n // Persistent audit — Paperclip competitive gap fix\n // Tracks per-agent, per-run, per-tool cost attribution\n try {\n const { logAuditEvent } = await import('./auditStore.js');\n logAuditEvent({\n agentId: ctx.agentId || 'default',\n runId: undefined, // TODO: pipe runId through LoopContext\n sessionId: ctx.sessionId,\n type: 'tool_execution',\n toolName: tr.name,\n durationMs: tr.durationMs,\n promptTokens: result.promptTokens,\n completionTokens: result.completionTokens,\n success,\n });\n } catch { /* audit store not critical */ }\n\n // A6: Error classification feedback to LLM (Hermes ClassifiedError pattern)\n if (!success && tr.errorClass && !ctx.voiceFastPath) {\n const isTransient = tr.errorClass === 'transient' || tr.errorClass === 'timeout' || tr.errorClass === 'rate_limit';\n const hint = isTransient\n ? `[Error Classification: TEMPORARY (${tr.errorClass}). Retrying this tool may succeed. Consider waiting briefly if rate-limited.]`\n : `[Error Classification: PERMANENT. This approach won't work — try a different tool or different arguments.]`;\n ctx.messages.push({ role: 'user', content: hint });\n }\n\n // Auto-fix hints (skip for voice)\n if (!success && !ctx.voiceFastPath) {\n const resolution = getErrorResolution(tr.content);\n if (resolution) {\n logger.info(COMPONENT, `[ActiveLearning] Known fix: ${resolution.slice(0, 80)}`);\n ctx.messages.push({ role: 'user', content: `[Auto-fix hint] A known resolution for this error: ${resolution}. Try applying it.` });\n }\n }\n\n // Error resolution tracking\n if (!ctx.voiceFastPath && success && lastFailedTool) {\n if (tr.name !== lastFailedTool.name) {\n recordErrorResolution(lastFailedTool.error, `Resolved by using ${tr.name} instead of ${lastFailedTool.name}`);\n }\n lastFailedTool = null;\n } else if (!success) {\n lastFailedTool = { name: tr.name, error: tr.content.slice(0, 200) };\n }\n }\n\n // Hunt Finding #24 (2026-04-14): previously `loopBroken` forced\n // phase='done' here, bypassing the respond phase. Now the loop\n // breaker sets phase='respond' directly so the user gets a real\n // answer from the tool data, not the raw breaker message. Keep\n // the `break` to exit the act loop but fall through to respond.\n if (loopBroken) { break; }\n\n // Progress scoring\n if (ctx.reflectionEnabled && toolResults.length > 0) {\n const anySucceeded = toolResults.some(r => !r.content.toLowerCase().includes('error:'));\n const hasNewInfo = toolResults.some(r => r.content.length > 50 && !r.content.toLowerCase().includes('not found'));\n recordProgress(anySucceeded, hasNewInfo, anySucceeded && hasNewInfo);\n }\n\n // Tool Search expansion\n if (ctx.toolSearchEnabled && toolResults.some(r => r.name === 'tool_search')) {\n for (const tr of toolResults) {\n if (tr.name !== 'tool_search') continue;\n const matches = tr.content.matchAll(/\\*\\*(\\w+)\\*\\*/g);\n for (const match of matches) {\n const toolName = match[1];\n if (!discoveredTools.has(toolName)) {\n discoveredTools.add(toolName);\n const fullDef = ctx.allToolsBackup.find(t => t.function.name === toolName);\n if (fullDef && !ctx.activeTools.some(t => t.function.name === toolName)) {\n ctx.activeTools.push(fullDef);\n }\n }\n }\n }\n if (discoveredTools.size > 0) {\n logger.info(COMPONENT, `[ToolSearch] Expanded: +${discoveredTools.size} tools -> ${ctx.activeTools.length} total`);\n }\n }\n\n // ── Read-only round detection ─────────────────────────\n // If in autonomous mode and all tools this round were read-only\n // (no writes, no edits), inject a directive to ACT on what was read.\n // This prevents the \"read → describe → stop\" pattern.\n if (ctx.isAutonomous && toolResults.length > 0) {\n const READ_ONLY_TOOLS = new Set(['read_file', 'list_dir', 'web_search', 'web_fetch', 'tool_search', 'memory', 'system_info', 'goal_list', 'weather']);\n const allReadOnly = toolResults.every(tr =>\n READ_ONLY_TOOLS.has(tr.name) ||\n (tr.name === 'shell' && /^\\s*(cat|head|tail|less|ls|find|grep|wc|echo|date|whoami|hostname|uname|df|du|ps|ss|curl.*GET|pwd|which|type|file|stat)\\b/i.test((pendingToolCalls.find(tc => tc.id === tr.toolCallId)?.function.arguments || '{}').replace(/.*\"command\"\\s*:\\s*\"/, '').replace(/\".*/, '')))\n );\n const hasWrites = toolResults.some(tr =>\n tr.name === 'write_file' || tr.name === 'edit_file' || tr.name === 'append_file'\n );\n if (allReadOnly && !hasWrites && round >= 2) {\n logger.info(COMPONENT, `[ReadOnlyNudge] Round ${round}: all tools were read-only — nudging to write (will force tool_choice on next think)`);\n ctx.messages.push({\n role: 'user',\n content: 'You just read files and ran diagnostic commands. Now ACT on what you found. Call edit_file or write_file to make the changes. Do NOT describe what needs to change — make the change NOW.',\n });\n // Give the nudge teeth: force tool_choice=required on next round\n // so the model MUST call a tool instead of returning more text.\n forceWriteOnNextThink = true;\n }\n }\n\n // ── Phase transition decision ────────────────────────\n round++;\n\n // Soul: update state and emit heartbeat\n updateSoulState(ctx.sessionId, {\n round,\n confidence: result.toolsUsed.length > 0 ? 'medium' : 'low',\n });\n emitHeartbeat(ctx.sessionId, phase, 0);\n\n // Inner monologue injection (every 3 rounds to avoid prompt bloat)\n if (round > 0 && round % 3 === 0) {\n const monologue = getInnerMonologue(ctx.sessionId);\n if (monologue) {\n ctx.messages.push({ role: 'user', content: monologue });\n }\n }\n\n // Checkpoint after each round for crash recovery\n saveCheckpoint({\n sessionId: ctx.sessionId,\n round,\n phase,\n model: activeModel,\n messages: ctx.messages,\n toolsUsed: result.toolsUsed,\n orderedToolSequence: result.orderedToolSequence,\n timestamp: new Date().toISOString(),\n message: ctx.message.slice(0, 500),\n channel: ctx.channel,\n totalPromptTokens: result.promptTokens,\n totalCompletionTokens: result.completionTokens,\n });\n\n // Inject running progress summary every N rounds (helper self-gates)\n const progressMsg = getProgressSummary(ctx.sessionId, round);\n if (progressMsg) {\n logger.info(COMPONENT, `[Progress] Round ${round}`);\n ctx.messages.push({ role: 'user', content: progressMsg });\n }\n\n if (round >= ctx.effectiveMaxRounds) {\n // A8: Budget grace call (Hermes pattern) — give model one final chance to summarize\n // instead of hard-cutting with a generic message\n result.budgetExhausted = true;\n if (pendingAssistantContent && pendingAssistantContent.trim().length > 20) {\n // Model already said something useful — use it\n result.content = stripToolJson(pendingAssistantContent);\n } else {\n // Force one final toolless call to summarize\n phase = 'respond';\n logger.info(COMPONENT, `[BudgetGrace] Round limit reached — giving model one final respond call`);\n continue; // skip to respond phase\n }\n phase = 'done';\n } else if (ctx.isAutonomous) {\n // Smart exit: only skip to respond if a single TERMINAL tool succeeded.\n // Terminal tools are ones that produce a final artifact (write, append)\n // or answer a direct question (weather, system_info).\n // Information-gathering tools (read_file, list_dir, web_search, shell, memory)\n // are NOT terminal — they almost always need a follow-up action.\n const completionStrategy = ctx.completionStrategy || 'smart-exit';\n\n // Pipeline-aware completion detection\n if (completionStrategy === 'no-tools') {\n // Research/browser: keep going until model stops requesting tools\n // (This path always continues — SmartExit is effectively disabled)\n phase = 'think';\n } else if (completionStrategy === 'single-round') {\n // Chat: one tool round then respond\n if (round >= 1) {\n logger.info(COMPONENT, `[SmartExit:${ctx.pipelineType || 'chat'}] Single-round completion — skipping to respond`);\n phase = 'respond';\n } else {\n phase = 'think';\n }\n } else {\n // smart-exit or terminal-tool: exit when a terminal tool succeeds\n const defaultTerminals = ['write_file', 'append_file', 'weather', 'system_info', 'fb_post', 'fb_reply', 'content_publish'];\n const terminalTools = new Set(ctx.pipelineTerminalTools || defaultTerminals);\n const singleToolSuccess = pendingToolCalls.length === 1\n && toolResults.every(r => r.success)\n && terminalTools.has(pendingToolCalls[0].function.name);\n // Respect minRounds — don't allow early exit before the pipeline's minimum\n const minRoundsMet = round >= (ctx.minRounds ?? 2);\n if (singleToolSuccess && minRoundsMet && ctx.smartExitEnabled !== false) {\n logger.info(COMPONENT, `[SmartExit:${ctx.pipelineType || 'general'}] Terminal tool \"${pendingToolCalls[0].function.name}\" succeeded — skipping to respond`);\n phase = 'respond';\n } else {\n // Autonomous mode: go back for more tool rounds\n phase = 'think';\n }\n }\n } else {\n // Non-autonomous: force respond after one tool round unless the\n // pipeline indicates a multi-step task and a terminal tool hasn't\n // been called yet. This fixes the bug where reading a file (1 round)\n // would skip to respond despite a write/edit still being required.\n const hasPipelineConfig = ctx.completionStrategy !== undefined || ctx.minRounds !== undefined;\n const isMultiStep = hasPipelineConfig && (ctx.completionStrategy === 'terminal-tool' || ctx.minRounds !== undefined);\n if (isMultiStep) {\n // Check if terminal tool (write, edit, post, etc.) was called\n const defaultTerminals = ['write_file', 'append_file', 'fb_post', 'fb_reply', 'content_publish'];\n const terminalTools = new Set(ctx.pipelineTerminalTools || defaultTerminals);\n const terminalMissing = !toolResults.some(r => terminalTools.has(r.name));\n if (terminalMissing && round < ctx.effectiveMaxRounds - 1) {\n logger.info(COMPONENT, `[NonAutonomous] Multi-step task needs terminal tool — staying in THINK`);\n ctx.messages.push({ role: 'user', content: 'Continue with the task. Call the required tool to finish.' });\n phase = 'think';\n } else {\n phase = 'respond';\n }\n } else {\n // Default non-autonomous: go to respond (backward compatible)\n const discoveryTools = ['gallery_search', 'tool_search'];\n const lastWasDiscovery = pendingToolCalls.length === 1 && discoveryTools.includes(pendingToolCalls[0].function.name);\n const resultHintsFollowUp = lastWasDiscovery && toolResults.some(r =>\n /call gallery_get/i.test(r.content) ||\n /hint:.*?(call|use|try|fetch|get|search)/i.test(r.content)\n );\n if (resultHintsFollowUp && round < ctx.effectiveMaxRounds - 1) {\n logger.info(COMPONENT, `[DiscoveryChain] ${pendingToolCalls[0].function.name} hinted follow-up — allowing another think round`);\n phase = 'think';\n } else {\n phase = 'respond';\n logger.info(COMPONENT, `[ThinkAct] Tools executed — entering respond phase (tools will be stripped)`);\n }\n }\n }\n break;\n }\n\n // ══════════════════════════��══════════════════════════��═════\n // RESPOND PHASE — Call LLM WITHOUT tools (forces text response)\n // ═══════════════════════════════════════════════════════════\n case 'respond': {\n // Incomplete task guard: if the user asked to edit/fix/write but the\n // model only read files and never wrote, nudge it back to think phase\n const askedToWrite = /\\b(edit|fix|change|modify|update|add|write|create|improve|rewrite|save)\\b/i.test(ctx.message);\n const didWrite = result.toolsUsed.some(t => ['write_file', 'edit_file', 'append_file'].includes(t));\n const didRead = result.toolsUsed.includes('read_file') || result.toolsUsed.includes('shell');\n if (askedToWrite && !didWrite && didRead && round < ctx.effectiveMaxRounds - 1 && !responseValidationRetried) {\n logger.warn(COMPONENT, `[IncompleteTask] User asked to edit but no write tool called after ${round} rounds — forcing back to think with tool_choice=required`);\n ctx.messages.push({\n role: 'user',\n content: '[INCOMPLETE] You read the file but did NOT make changes. Call edit_file NOW.\\n\\nedit_file arguments:\\n- path: (the file you read)\\n- target: (exact string from the file to replace — copy a small section)\\n- replacement: (the new version of that section)\\n\\nDo NOT use write_file for large files. Use edit_file for surgical changes. CALL IT NOW.',\n });\n responseValidationRetried = true; // Only do this once\n forceWriteOnNextThink = true; // Force tool_choice=required on next think\n phase = 'think';\n break;\n }\n\n logger.info(COMPONENT, `Respond phase — calling LLM without tools to generate final answer`);\n\n // Prompt budget check (Space Agent parity)\n const respondBudgetMsg = budgetConfig.maxTokens > 0 ? checkBudget(ctx.sessionId, budgetConfig) : null;\n if (respondBudgetMsg) {\n markExceeded(ctx.sessionId);\n result.content = respondBudgetMsg;\n phase = 'done';\n break;\n }\n\n // Context compression for respond phase\n let smartMessages: ChatMessage[];\n if (ctx.voiceFastPath) {\n smartMessages = ctx.messages as ChatMessage[];\n } else {\n smartMessages = buildSmartContext(ctx.messages as ChatMessage[], tokenBudget);\n }\n\n // F1: Pre-model hook for respond phase too\n if (ctx.beforeModelCall) {\n try {\n smartMessages = ctx.beforeModelCall([...smartMessages], round);\n } catch (e) {\n logger.warn(COMPONENT, `[PreModelHook:respond] Hook threw: ${(e as Error).message}`);\n }\n }\n\n // Hunt Finding #21 (2026-04-14): inject a directive message at the\n // tail of the context for the respond phase. Without this, weak\n // models (minimax-m2.7:cloud) produce internal-monologue text like\n // \"The user asked me to run X... Actually, looking at the results...\"\n // which is raw chain-of-thought leaking into the final answer.\n // This directive is appended only to the respond-phase request —\n // not persisted to the session history.\n const respondDirective: ChatMessage = {\n role: 'user',\n content: '[System directive for this reply only] Write the final answer for the user. RULES: (1) Do NOT narrate what the user asked — they already know. (2) Do NOT describe your reasoning, thinking, or past tool attempts. (3) Do NOT start with \"The user asked\", \"Let me\", \"Actually\", \"Looking at\", \"Wait\" — start with the result. (4) Report outcomes as facts in 1-3 sentences. (5) No XML, no tool call blocks, no meta-commentary. Just the answer. (6) EXCEPTION: if you need to create or update a widget on the canvas, you MAY emit a _____react or _____widget gate — these are NOT meta-commentary, they are the actual deliverable.',\n };\n smartMessages = [...smartMessages, respondDirective];\n\n const thinkingMode = ctx.thinkingOverride || ctx.config.agent.thinkingMode || 'off';\n const chatOptions = {\n model: activeModel,\n messages: smartMessages,\n tools: undefined, // NO TOOLS — forces text-only response\n maxTokens: ctx.voiceFastPath ? Math.min(ctx.config.agent.maxTokens, 300) : ctx.config.agent.maxTokens,\n temperature: ctx.config.agent.temperature,\n thinking: ctx.voiceFastPath ? false : thinkingMode !== 'off',\n thinkingLevel: thinkingMode as 'off' | 'low' | 'medium' | 'high',\n providerOptions: ctx.providerOptions,\n };\n\n let response: ChatResponse;\n const respondStart = Date.now();\n if (ctx.streamCallbacks?.onToken) {\n let streamContent = '';\n for await (const chunk of chatStream(chatOptions)) {\n if (chunk.type === 'text' && chunk.content) {\n streamContent += chunk.content;\n ctx.streamCallbacks.onToken(chunk.content);\n } else if (chunk.type === 'error') {\n logger.error(COMPONENT, `Stream error: ${chunk.error}`);\n } else if (chunk.type === 'retry') {\n // Out-of-band: log + callback only. Do NOT append to streamContent.\n logger.warn(COMPONENT, `Respond-phase stream retrying ${chunk.provider}/${chunk.model} (attempt ${chunk.attempt}/${chunk.maxRetries}, reason=${chunk.reason})`);\n ctx.streamCallbacks.onRetry?.({\n attempt: chunk.attempt,\n maxRetries: chunk.maxRetries,\n reason: chunk.reason,\n provider: chunk.provider,\n model: chunk.model,\n delayMs: chunk.delayMs,\n });\n } else if (chunk.type === 'failover') {\n logger.warn(COMPONENT, `Respond-phase stream failover from ${chunk.originalProvider}/${chunk.originalModel}`);\n ctx.streamCallbacks.onFailover?.({\n originalProvider: chunk.originalProvider,\n originalModel: chunk.originalModel,\n error: chunk.error,\n });\n }\n }\n response = {\n id: `stream-${Date.now()}`,\n content: streamContent,\n usage: { promptTokens: 0, completionTokens: 0, totalTokens: 0 },\n finishReason: 'stop',\n model: activeModel,\n };\n } else {\n response = await chatWithTimeout(() => chat(chatOptions), 'agentLoop:chat', 300_000);\n }\n // v5.0: OTEL span for respond-phase LLM call\n fireSpan(traceCtx, 'model_call:respond', Date.now() - respondStart, { round: String(round), model: activeModel, phase: 'respond' });\n\n result.modelUsed = response.model;\n result.promptTokens += response.usage?.promptTokens || 0;\n result.completionTokens += response.usage?.completionTokens || 0;\n\n // Hunt Finding #39 (2026-04-15): if the model emits tool calls in\n // the respond phase (despite tools being undefined), those calls\n // are the model's attempt to RECOVER from an earlier failure. A\n // real captured example: model wrote to /home/titan/docs/foo.md\n // (wrong user), the write was rejected, then in the respond phase\n // it emitted a corrected write_file to /tmp/readme-b1-comparison.md.\n // Previously we dropped that tool call silently. Now we route\n // back to the think phase so the recovery actually executes.\n if (response.toolCalls && response.toolCalls.length > 0) {\n logger.warn(\n COMPONENT,\n `[RespondPhaseToolCall] Model emitted ${response.toolCalls.length} tool call(s) in respond phase — routing back to think phase to execute (Hunt #39)`,\n );\n // Inject the tool call as an assistant message so the next\n // think round sees it and the agent loop executes it.\n ctx.messages.push({\n role: 'assistant',\n content: response.content || '',\n toolCalls: response.toolCalls,\n });\n // Synthesize a placeholder tool message so the next think\n // iteration doesn't orphan the tool_calls.\n // Actually — leave it. The act-phase handler runs tool calls\n // from the most recent assistant. Transition phase=act and\n // let the existing execution path handle it.\n phase = 'act';\n // Seed pendingToolCalls so the act handler picks them up\n pendingToolCalls = response.toolCalls;\n pendingAssistantContent = response.content || '';\n break; // exit respond case; re-enter the while loop at act\n }\n\n const costCheck = recordTokenUsage(ctx.sessionId, activeModel, response.usage?.promptTokens || 0, response.usage?.completionTokens || 0);\n\n // Prompt budget tracking (Space Agent parity)\n if (budgetConfig.maxTokens > 0) {\n recordUsage(ctx.sessionId, response.usage?.promptTokens || 0, response.usage?.completionTokens || 0);\n }\n\n // Wire LLM spend into Command Post budget policies\n const callCost = calculateActualCost(activeModel, { prompt: response.usage?.promptTokens || 0, completion: response.usage?.completionTokens || 0 });\n const sessionGoal = getSessionGoal(ctx.sessionId);\n recordSpend(ctx.agentId || 'default', sessionGoal?.goalId, callCost);\n\n if (costCheck.budgetExceeded) {\n result.content = '⚠️ Daily spending limit reached. TITAN has paused to keep your API costs under control.';\n } else {\n // Output guardrails pipeline — centralized post-processing\n // Strips thinking blocks, narrator preamble, instruction echoes,\n // and validates structure before delivering to user.\n const { applyOutputGuardrails } = await import('./outputGuardrails.js');\n const guardrailed = applyOutputGuardrails(response.content, {\n type: 'chat_response',\n originalMessage: ctx.message,\n model: activeModel,\n });\n result.content = guardrailed.content;\n }\n\n // Empty response fallback: if the model returned nothing in respond phase,\n // retry once with explicit instruction, then use a clean fallback\n if (!emptyResponseRetried && (!result.content || result.content.trim().length === 0)) {\n emptyResponseRetried = true;\n // Try one more LLM call with a strong nudge to summarize\n try {\n const retryMessages = [\n ...ctx.messages.slice(-6),\n { role: 'user' as const, content: \"Please answer the user's question now. Summarize what you found in 2-3 sentences. Don't use any tools — just answer directly.\" },\n ];\n const retryResponse = await chatWithTimeout(() => chat({\n model: activeModel,\n messages: retryMessages,\n temperature: 0.7,\n maxTokens: 300,\n providerOptions: ctx.providerOptions,\n }), 'agentLoop:retry', 300_000);\n const retryContent = (retryResponse.content || '').trim();\n if (retryContent && retryContent.length > 10) {\n logger.info(COMPONENT, '[EmptyResponse] Recovery retry succeeded');\n result.content = stripToolJson(retryContent);\n }\n } catch (retryErr) {\n logger.debug(COMPONENT, `[EmptyResponse] Recovery retry failed: ${(retryErr as Error).message}`);\n }\n\n // If retry also failed, use a clean message (never dump raw tool results)\n if (!result.content || result.content.trim().length === 0) {\n logger.warn(COMPONENT, '[EmptyResponse] Model returned empty after retry — using clean fallback');\n result.content = 'I looked into that but couldn\\'t generate a clear summary. Could you try asking again?';\n }\n }\n\n // Response validation: check if the answer actually addresses the question.\n // If the user asked for specific data (a number, version, name) and the\n // response doesn't contain it but tool results do, retry once with a nudge.\n if (!responseValidationRetried && result.content && result.toolsUsed.length > 0) {\n const gap = detectResponseGap(ctx.message, result.content, ctx.messages);\n if (gap) {\n logger.info(COMPONENT, `[ResponseValidation] Gap detected: ${gap}. Retrying respond phase.`);\n ctx.messages.push({\n role: 'user',\n content: `[IMPORTANT] Your response did not include the specific information the user asked for. ${gap} Look at your tool results above and include the actual data in your answer. Be direct and specific.`,\n });\n responseValidationRetried = true;\n // Stay in respond phase for one more try\n break;\n }\n }\n\n phase = 'done';\n break;\n }\n } // end switch\n\n // ── on_round_end shell hook ──────────────────────────\n try {\n const { runShellHooks } = await import('../hooks/shellHooks.js');\n const { loadConfig } = await import('../config/config.js');\n const config = loadConfig();\n const hooksCfg = (config.hooks as Record<string, unknown> | undefined)?.shell as Record<string, unknown> | undefined;\n if (hooksCfg?.enabled) {\n await runShellHooks('on_round_end', {\n TITAN_SESSION_ID: ctx.sessionId,\n TITAN_AGENT_ID: ctx.agentId || 'default',\n TITAN_ROUND: String(round),\n });\n }\n } catch { /* hooks are best-effort */ }\n } // end while\n\n // ── on_session_end shell hook ────────────────────────\n try {\n const { runShellHooks } = await import('../hooks/shellHooks.js');\n const { loadConfig } = await import('../config/config.js');\n const config = loadConfig();\n const hooksCfg = (config.hooks as Record<string, unknown> | undefined)?.shell as Record<string, unknown> | undefined;\n if (hooksCfg?.enabled) {\n await runShellHooks('on_session_end', {\n TITAN_SESSION_ID: ctx.sessionId,\n TITAN_AGENT_ID: ctx.agentId || 'default',\n TITAN_ROUND: String(round),\n });\n }\n } catch { /* hooks are best-effort */ }\n\n // If loop ended without setting content (hit round limit in think phase)\n if (!result.content && round >= ctx.effectiveMaxRounds) {\n result.content = 'I ran out of thinking time on that one. Could you break your request into a smaller step?';\n result.budgetExhausted = true;\n }\n\n // v4.13 ancestor-extraction (Hermes trajectory): append this run's full\n // ChatML transcript to disk for future retrospective / fine-tuning. Guarded\n // try/catch — trajectory I/O failures never affect the user response.\n try {\n const { saveTrajectory } = await import('./trajectory.js');\n const completed = !result.budgetExhausted && !!result.content && result.content.length > 0;\n saveTrajectory({\n conversations: ctx.messages,\n model: activeModel,\n completed,\n sessionId: ctx.sessionId,\n toolsUsed: result.toolsUsed,\n reason: result.budgetExhausted ? 'budget_exhausted' : (completed ? 'done' : 'empty'),\n metrics: {\n rounds: round,\n promptTokens: result.promptTokens ?? 0,\n completionTokens: result.completionTokens ?? 0,\n },\n });\n } catch { /* trajectory save is best-effort */ }\n\n // v5.0: Unregister session from steer map\n globalSteerQueues.delete(ctx.sessionId);\n\n // v5.0: Session-level OTEL span\n const totalDuration = Date.now() - loopStartTime;\n fireSpan(traceCtx, 'session', totalDuration, {\n rounds: String(round),\n model: activeModel,\n toolsUsed: result.toolsUsed.length,\n budgetExhausted: result.budgetExhausted,\n });\n result.traceContext = { traceId: traceCtx.traceId, spanId: traceCtx.spanId };\n\n // Clean up prompt budget state\n if (budgetConfig.maxTokens > 0) {\n cleanupBudget(ctx.sessionId);\n }\n\n return result;\n}\n\n// ── Heartbeat-driven inbox processing ───────────────────────────────────\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nasync function checkAndProcessInbox(agentId: string): Promise<void> {\n const inbox = getAgentInbox(agentId);\n if (inbox.length === 0) return;\n\n // Take the first queued request\n const req = inbox[0];\n const claimed = claimWakeupRequest(req.id);\n if (!claimed) return;\n\n // Transition CP issue to in_progress\n updateIssue(req.issueId, { status: 'in_progress' });\n const run = startRun(agentId, 'assignment', req.issueId);\n\n logger.info(COMPONENT, `[Heartbeat] Processing wakeup ${req.id} for agent \"${req.agentName}\"`);\n\n try {\n const template = SUB_AGENT_TEMPLATES[req.templateName] || {};\n const config = loadConfig();\n const modelAliases = (config.agent as Record<string, unknown> | undefined)?.modelAliases as Record<string, string> | undefined;\n const tier = (template as Record<string, unknown>).tier as string | undefined;\n let model = req.model;\n if (!model && modelAliases && tier) {\n model = modelAliases[tier] || modelAliases.fast;\n }\n\n const result = await spawnSubAgent({\n name: req.agentName,\n task: req.task,\n tools: template.tools,\n systemPrompt: template.systemPrompt,\n model,\n depth: 0,\n });\n\n // Complete the CP run\n endRun(run.id, {\n status: result.success ? 'succeeded' : 'failed',\n toolsUsed: result.toolsUsed,\n });\n\n // Post result as CP issue comment\n const commentBody = [\n `**Sub-agent result** (${result.rounds} rounds, ${result.durationMs}ms)`,\n `Status: ${result.success ? 'SUCCESS' : 'FAILED'}${result.validated ? '' : ' [UNVALIDATED]'}`,\n `Tools: ${result.toolsUsed.join(', ') || 'none'}`,\n '',\n result.content,\n ].join('\\n');\n addIssueComment(req.issueId, commentBody, { agentId });\n updateIssue(req.issueId, { status: result.success ? 'done' : 'todo' });\n\n logger.info(COMPONENT, `[Heartbeat] Wakeup ${req.id} completed — ${result.success ? 'SUCCESS' : 'FAILED'}`);\n } catch (err) {\n const error = (err as Error).message;\n endRun(run.id, { status: 'error', error });\n addIssueComment(req.issueId, `**Sub-agent failed**: ${error}`, { agentId });\n updateIssue(req.issueId, { status: 'todo' });\n logger.error(COMPONENT, `[Heartbeat] Wakeup ${req.id} failed: ${error}`);\n }\n}\n"],"mappings":"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","names":["runShellHooks","loadConfig"]}