titan-agent 7.2.0 → 7.2.1

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.
@@ -1243,6 +1243,15 @@ CRITICAL: Widgets are NOT files. Do NOT use write_file, edit_file, or any file t
1243
1243
  enrichedSystemPrompt += earlyPersona.systemPromptAppendix;
1244
1244
  logger.info(COMPONENT, `[Persona:${earlyPersona.id}] system prompt appendix injected`);
1245
1245
  }
1246
+ try {
1247
+ const relMode = config.agent.reliabilityMode === true;
1248
+ if (relMode) {
1249
+ const { buildDisciplinePrompt } = await import("./disciplinePrompt.js");
1250
+ const frag = buildDisciplinePrompt(message, true);
1251
+ if (frag) enrichedSystemPrompt += frag;
1252
+ }
1253
+ } catch {
1254
+ }
1246
1255
  const messages = [
1247
1256
  { role: "system", content: enrichedSystemPrompt },
1248
1257
  ...historyMessages
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/agent/agent.ts"],"sourcesContent":["/**\n * TITAN — Core Agent Loop\n * The main agent: receives messages, builds context, calls LLM, handles tools, responds.\n */\nimport { existsSync, readFileSync } from 'fs';\nimport { randomBytes } from 'crypto';\nimport { loadConfig } from '../config/config.js';\nimport { getOrCreateSession, getOrCreateSessionById, addMessage, getContextMessages } from './session.js';\nimport { getToolDefinitions } from './toolRunner.js';\nimport { recordUsage, searchMemories } from '../memory/memory.js';\nimport { getLearningContext, learnFact, getToolWarnings, classifyTaskType, recordStrategy, recordStrategyOutcome, getStrategyHints, getLearnedPreferenceHints } from '../memory/learning.js';\nimport { buildPersonalContext } from '../memory/relationship.js';\nimport { retainStrategy, getHindsightHints } from '../memory/hindsightBridge.js';\nimport { getTeachingContext, isCorrection } from './teaching.js';\nimport { recordCorrection } from './userProfile.js';\nimport { heartbeat, clearSession, setStallHandler, setAutonomousMode } from './stallDetector.js';\n// Hunt Finding #22 / #46: resetLoopDetection is intentionally NOT called here.\n// See agent.ts:~1466 for the explanation. Session-close handles cleanup.\n// (Import left out so lint doesn't flag the unused symbol.)\nimport { routeModel } from './costOptimizer.js';\nimport { getPlugins } from '../plugins/registry.js';\nimport { runAfterTurn } from '../plugins/contextEngine.js';\nimport { shouldDeliberate, analyze, generatePlan, executePlan, handleApproval, getDeliberation, cancelDeliberation, formatPlanResults } from './deliberation.js';\nimport type { ChatMessage } from '../providers/base.js';\nimport { initGraph, addEpisode, getGraphContext } from '../memory/graph.js';\nimport { isAvailable as isBrainAvailable, selectTools as brainSelectTools, ensureLoaded as ensureBrainLoaded } from './brain.js';\nimport { DEFAULT_CORE_TOOLS } from './toolSearch.js';\nimport { maybeBuildIdentityFactSheet } from './identityIntercept.js';\nimport { applyCaps } from './promptSectionCaps.js';\nimport { classifyPipeline, resolvePipelineConfig } from './pipeline.js';\nimport { buildSelfAwarenessContext } from './selfAwareness.js';\nimport { assembleSystemPrompt, type PromptMode } from './systemPromptParts.js';\nimport { getOptimizedPromptBlock } from '../skills/builtin/self_improve.js';\nimport { analyzeForDelegation, executeDelegationPlan } from './orchestrator.js';\nimport { queueWakeup } from './agentWakeup.js';\nimport { createIssue, requestHireApproval } from './commandPost.js';\nimport { spawnSubAgent, SUB_AGENT_TEMPLATES } from './subAgent.js';\nimport { logTrajectory } from './trajectoryLogger.js';\n// v6.4.0 — learning curator wire-up. Imports two surfaces:\n// processTrajectoryForLearningReview() runs the 24h-gated review after\n// each task to derive durable lessons from repeated trajectories.\n// renderLearningPatternGuidance() surfaces those lessons as a system-\n// prompt hint at the start of similar future tasks.\nimport { processTrajectoryForLearningReview, renderLearningPatternGuidance } from './learningCurator.js';\nimport { processTrajectoryForSkills, getSkillGuidance } from './autoSkillGen.js';\nimport { getAgent } from './multiAgent.js';\nimport { isDangerous } from '../utils/safety.js';\nimport { registerTool } from './toolRunner.js';\nimport { runAgentLoop, type LoopResult } from './agentLoop.js';\nimport { detectSystemWidget, buildSystemWidgetGate } from './systemWidgets.js';\nimport { startTrace } from './tracer.js';\nimport { initSoulState, updateSoulState, emitHeartbeat, getInnerMonologue, consolidateWisdom, clearSoulState, getWisdomHints } from './soul.js';\nimport logger from '../utils/logger.js';\nimport { TITAN_NAME, AGENTS_MD, SOUL_MD, TOOLS_MD, TITAN_MD_FILENAME } from '../utils/constants.js';\n\nconst COMPONENT = 'Agent';\nconst MAX_TOOL_ROUNDS = 10;\n\n/** Estimate the round budget based on task complexity */\n/**\n * Estimate how many tool rounds a message needs.\n * Simple tasks (read a file, run a command) → 3-4 rounds.\n * Multi-step tasks (read, analyze, write) → 6-10 rounds.\n * Complex tasks (research, build, deploy) → 12-20 rounds.\n */\nfunction estimateRoundBudget(message: string, config: { agent: { dynamicBudget?: boolean; maxToolRoundsHard?: number }; autonomy: { mode: string } } & Record<string, unknown>): number {\n const agentConfig = config.agent as Record<string, unknown>;\n if (agentConfig.dynamicBudget === false) return MAX_TOOL_ROUNDS;\n\n const hardCap = (agentConfig.maxToolRoundsHard as number) || 50;\n const lower = message.toLowerCase();\n const words = message.split(/\\s+/).length;\n\n // Count complexity signals\n const isQuestion = /^(what|who|how|why|where|when|which|is |are |do |does |can |will )/i.test(message.trim());\n const isSingleAction = /^(read|write|run|list|show|tell|get|check|find)\\b/i.test(message.trim());\n const isMultiStep = /\\b(then|after that|next|step \\d|finally|first.*then|and also|additionally|and then)\\b/i.test(lower);\n const isComplex = /\\b(research|analyze|investigate|compare|build|implement|create.*and|deploy|automat|refactor|rewrite|design)/i.test(lower);\n const isAmbitious = /\\b(step by step|end.to.end|full pipeline|from scratch|entire|complete|comprehensive)\\b/i.test(lower);\n\n // Count tool-intent signals (how many distinct actions are implied)\n const actionCount = [\n /\\b(read|open|show|display|check)\\b/i.test(lower),\n /\\b(write|create|save|generate|make)\\b/i.test(lower),\n /\\b(edit|change|modify|update|fix|replace)\\b/i.test(lower),\n /\\b(run|execute|install|build|test|deploy)\\b/i.test(lower),\n /\\b(search|find|look|research|investigate)\\b/i.test(lower),\n /\\b(summarize|analyze|compare|report)\\b/i.test(lower),\n ].filter(Boolean).length;\n\n let budget: number;\n\n if (isQuestion && words < 15 && !isMultiStep) {\n budget = 3; // \"What is X?\" \"Who is Y?\" — quick lookup\n } else if (isSingleAction && words < 20 && !isMultiStep) {\n budget = 4; // \"Read package.json\" \"Run uname\" — one tool call\n } else if (actionCount <= 1 && words < 30 && !isMultiStep) {\n budget = 5; // Single-purpose task, short message\n } else if (actionCount <= 2 && !isComplex) {\n budget = 8; // Two-step task (read + write, search + summarize)\n } else if (isMultiStep || actionCount >= 3) {\n budget = 12; // Multi-step explicit pipeline\n } else if (isComplex || isAmbitious) {\n budget = 18; // Research, build, deploy — needs room to work\n } else {\n budget = 6; // Default for unclassified moderate tasks\n }\n\n // In autonomous mode, use the configured maxRounds directly\n // The dynamic budget and hard cap should NOT limit autonomous execution\n const isAutonomous = config.autonomy.mode === 'autonomous';\n const configuredMax = (agentConfig.maxRounds as number) || 0;\n if (isAutonomous && configuredMax > 0) {\n return configuredMax;\n }\n\n return Math.min(budget, hardCap);\n}\n\n// ── Ralph Loop Verification ─────────────────────────────────────\n// Checks whether the agent actually completed the requested task.\n// Inspired by vercel-labs/ralph-loop-agent outer verification pattern.\n\n/**\n * Exported for regression testing in tests/agent-verify.test.ts.\n * The v4.3.4 fix narrowed its pattern matching; easy to break\n * if someone generalizes the regex again without a test.\n */\nexport function verifyTaskCompletion(\n message: string,\n toolsUsed: string[],\n _response: string,\n): { complete: boolean; reason: string } {\n const lower = message.toLowerCase();\n\n // v4.3.4: conversational asides shouldn't trigger a \"you must edit_file\"\n // enforcement loop. Tony's voice notes on Messenger like\n // \"fix your voice\" or \"fix the way you respond\" would match the\n // edit|fix regex + \"file\"/\"files\" (because the voice note transcript\n // often contains the word \"files\") and trip the Ralph Loop, which\n // then injects a stale \"pending file edit task\" prompt and the\n // LLM hallucinates back \"I don't have a pending file edit task.\"\n // Two narrowings:\n // 1. Don't count `shell` as a file read. The verifier was using it\n // as a proxy for read_file but any `ls`/`pwd` call tripped it.\n // 2. Require an EXPLICIT file path or filename token (e.g. \".ts\",\n // \".py\", \"/path/\", \"src/\", etc.) rather than the bare word\n // \"file\"/\"files\" which appears constantly in natural speech.\n const verbMatch = /\\b(edit|fix|change|modify|update|add|write|create|improve|rewrite|save|implement|patch)\\b/i.test(lower);\n const hasFilePath = /[\\w-]+\\.(ts|tsx|js|jsx|py|md|json|yaml|yml|html|css|sh|txt|rs|go|java|cpp|c|h)\\b|\\bsrc\\/|\\bpath:|\\/[a-z]+\\//i.test(lower);\n const askedToWrite = verbMatch && hasFilePath;\n const didWrite = toolsUsed.some(t => ['write_file', 'edit_file', 'append_file'].includes(t));\n const didRead = toolsUsed.includes('read_file');\n\n if (askedToWrite && !didWrite && didRead) {\n return {\n complete: false,\n reason: 'You read the file but did not save any changes. You MUST call edit_file or write_file to apply your modifications. Call the tool now.',\n };\n }\n\n // Check: user asked to run/execute something but no shell was called\n const askedToRun = /\\b(run|execute|install|deploy|build|test|restart)\\b/i.test(lower)\n && /\\b(command|script|service|server|package|npm|pip)\\b/i.test(lower);\n const didRun = toolsUsed.includes('shell');\n\n if (askedToRun && !didRun) {\n return {\n complete: false,\n reason: 'You did not execute the requested command. Call the shell tool to run it.',\n };\n }\n\n return { complete: true, reason: '' };\n}\n\n// ── Current session context for spawn_agent async delegation ─────\nlet currentSessionId: string | null = null;\nexport function setCurrentSessionId(id: string | null): void { currentSessionId = id; }\nexport function getCurrentSessionId(): string | null { return currentSessionId; }\n\n// ── Register spawn_agent tool ────────────────────────────────────\nlet spawnAgentRegistered = false;\nfunction ensureSpawnAgentRegistered(): void {\n if (spawnAgentRegistered) return;\n spawnAgentRegistered = true;\n registerTool({\n name: 'spawn_agent',\n description: 'Hand off a task to a specialist teammate. Choose: scout (research), builder (code/files), writer (content), analyst (decisions), or sage (review). For parallel execution of multiple subtasks, prefer agent_team. For sequential dependent tasks, prefer agent_chain.',\n parameters: {\n type: 'object',\n properties: {\n name: { type: 'string', description: 'Optional display name for the sub-agent run (e.g., \"Scout\", \"Builder\"). The specialist persona is determined by `template`.' },\n task: { type: 'string', description: 'A clear, self-contained task description. Include all context the specialist needs — it has no memory of this conversation.' },\n template: { type: 'string', description: 'Specialist to route to. Prefer: \"scout\" | \"builder\" | \"writer\" | \"analyst\".' },\n model: { type: 'string', description: 'Model override. Usually leave blank — specialists pick their own role-tuned model.' },\n },\n required: ['task'],\n },\n execute: async (args) => {\n const templateName = (args.template as string) || '';\n\n // v5.0.0-spacewalk: resolve config-defined agents from titan.json.\n // If the template name matches a config agent entry, use its base\n // template (e.g. \"coder\") for tools/systemPrompt, but keep the\n // config agent's name, model, and systemPromptOverride.\n let configAgent: import('./agentScope.js').ResolvedAgentConfig | null = null;\n try {\n const { resolveAgentConfig, agentAllowsSkill } = await import('./agentScope.js');\n configAgent = resolveAgentConfig(templateName);\n } catch { /* optional — agentScope may not exist in some builds */ }\n\n const baseTemplateName = configAgent?.template || templateName;\n const template = SUB_AGENT_TEMPLATES[baseTemplateName] || {};\n const agentName = (args.name as string) || configAgent?.name || template.name || 'SubAgent';\n const task = args.task as string;\n\n // v5.3.1: Apply config-defined agent constraints (maxRounds, maxTokens,\n // persona, skillsFilter). Log what we apply for observability.\n const appliedFields: string[] = [];\n if (configAgent) {\n if (configAgent.maxRounds !== 15) appliedFields.push(`maxRounds=${configAgent.maxRounds}`);\n if (configAgent.maxTokens !== 4000) appliedFields.push(`maxTokens=${configAgent.maxTokens}`);\n if (configAgent.persona && configAgent.persona !== 'default') appliedFields.push(`persona=${configAgent.persona}`);\n if (configAgent.skillsFilter) appliedFields.push(`skillsFilter=[${configAgent.skillsFilter.join(',')}]`);\n if (configAgent.modelFallbacks.length > 0) logger.info('Agent', `Agent \"${agentName}\" has modelFallbacks — not yet implemented in spawn path`);\n if (configAgent.workspaceDir) appliedFields.push(`workspaceDir=${configAgent.workspaceDir}`);\n if (configAgent.tags.length > 0) appliedFields.push(`tags=[${configAgent.tags.join(',')}]`);\n if (configAgent.systemPromptOverride) appliedFields.push('systemPromptOverride=set');\n if (appliedFields.length > 0) logger.info('Agent', `Applied config agent fields for \"${agentName}\": ${appliedFields.join(', ')}`);\n }\n\n // v4.9.0: kill switch gate — if Tony killed the organism, no\n // new sub-agent spawns until he explicitly resumes.\n try {\n const { isKilled } = await import('../safety/killSwitch.js');\n if (isKilled()) {\n return '[Sub-Agent REFUSED] TITAN kill switch is active. Tony must resume via /api/safety/resume before new sub-agents can spawn.';\n }\n } catch { /* fail-open — safety module optional */ }\n\n // v4.7.0: Hermes-style safety gates on the spawn path.\n // Prevents fork bombs + concurrent runaway. Depth is best-effort\n // (we treat the primary as depth 0); the MAX_CONCURRENT_CHILDREN\n // cap is the hard backstop.\n try {\n const { canSpawnChild } = await import('./subagentSafety.js');\n const parent = currentSessionId || 'root';\n const gate = canSpawnChild(parent, 0);\n if (!gate.ok) {\n return `I can't delegate that task right now (${gate.reason}). I'll handle it myself.`;\n }\n } catch { /* fail-open — safety module optional */ }\n\n // v4.7.0: specialist routing. If the template matches a\n // registered specialist (Scout/Builder/Writer/Analyst),\n // use its tuned system prompt + preferred model.\n let specialistPrompt: string | undefined;\n let specialistModel: string | undefined;\n try {\n const { findSpecialistForTemplate, loadSpecialistPersona } = await import('./specialists.js');\n const sp = findSpecialistForTemplate(templateName);\n if (sp) {\n specialistPrompt = loadSpecialistPersona(sp.id);\n specialistModel = sp.model;\n }\n } catch { /* fall through to generic template */ }\n\n // ── Async path: delegate via Command Post ────────────\n const cpEnabled = loadConfig().commandPost?.enabled ?? false;\n if (cpEnabled) {\n const issue = createIssue({\n title: `[Agent Task] ${task.slice(0, 80)}`,\n description: task,\n priority: 'medium',\n createdByUser: 'agent',\n });\n\n const wakeup = queueWakeup({\n issueId: issue.id,\n issueIdentifier: issue.identifier,\n agentId: issue.id, // Use issue ID as agent ID for simplicity\n agentName,\n parentSessionId: currentSessionId,\n task,\n templateName,\n model: args.model as string | undefined,\n });\n\n return `I've handed that off to the ${agentName} specialist. They'll jump on it and report back when they're done.`;\n }\n\n // ── Sync path: original blocking execution ───────────\n // v4.7.0: track child for concurrent cap, use specialist prompt/model when matched.\n let childId: string | undefined;\n try {\n const { registerChild } = await import('./subagentSafety.js');\n childId = `child-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`;\n registerChild(currentSessionId || 'root', childId);\n } catch { /* safety optional */ }\n\n // Track specialist status in CP registry during execution\n let specialistId: string | undefined;\n try {\n const { findSpecialistForTemplate } = await import('./specialists.js');\n const sp = findSpecialistForTemplate(templateName);\n if (sp) specialistId = sp.id;\n } catch { /* optional */ }\n\n try {\n if (specialistId) {\n const { updateAgentStatus } = await import('./commandPost.js');\n updateAgentStatus(specialistId, 'active');\n }\n\n // v5.3.1: Filter template tools through config agent skillsFilter\n let allowedTools = template.tools;\n if (configAgent?.skillsFilter) {\n try {\n const { agentAllowsSkill } = await import('./agentScope.js');\n allowedTools = template.tools?.filter(t => agentAllowsSkill(configAgent!, t));\n if (allowedTools && allowedTools.length === 0) {\n logger.warn('Agent', `Agent \"${agentName}\" skillsFilter blocks all template tools — falling back to template default`);\n allowedTools = template.tools;\n }\n } catch { /* fall through to template.tools */ }\n }\n\n const result = await spawnSubAgent({\n name: agentName,\n task,\n tools: allowedTools,\n systemPrompt: configAgent?.systemPromptOverride || specialistPrompt || template.systemPrompt,\n model: (args.model as string | undefined) || configAgent?.model || specialistModel,\n tier: (template as Record<string, unknown>).tier as 'cloud' | 'smart' | 'fast' | 'local' | undefined,\n persona: configAgent?.persona,\n maxRounds: configAgent?.maxRounds,\n maxTokens: configAgent?.maxTokens,\n workspaceDir: configAgent?.workspaceDir ?? undefined,\n tags: configAgent?.tags,\n depth: 1, // v4.7.0: this IS a child (was incorrectly 0)\n });\n\n if (specialistId) {\n const { updateAgentStatus } = await import('./commandPost.js');\n updateAgentStatus(specialistId, 'idle');\n }\n\n return `The ${agentName} specialist finished. Here's what they found:\\n\\n${result.content}`;\n } catch (err) {\n if (specialistId) {\n try {\n const { updateAgentStatus } = await import('./commandPost.js');\n updateAgentStatus(specialistId, 'idle');\n } catch { /* best effort */ }\n }\n throw err;\n } finally {\n if (childId) {\n try {\n const { unregisterChild } = await import('./subagentSafety.js');\n unregisterChild(currentSessionId || 'root', childId);\n } catch { /* best effort */ }\n }\n }\n },\n });\n}\n\n// ── Register delegate_task tool (inter-agent delegation via Command Post) ──\nlet delegateTaskRegistered = false;\nfunction ensureDelegateTaskRegistered(): void {\n if (delegateTaskRegistered) return;\n delegateTaskRegistered = true;\n registerTool({\n name: 'delegate_task',\n description: 'Delegate a task to a multi-agent worker OR an external agent (Codex, bash). Creates a Command Post issue and returns immediately. Results are injected into your next response.',\n parameters: {\n type: 'object',\n properties: {\n agentId: { type: 'string', description: 'Target agent ID (from list_agents). Required unless using an external adapter.' },\n task: { type: 'string', description: 'Task description for the worker' },\n priority: { type: 'string', description: 'Priority: low, medium, high, critical (default: medium)' },\n adapter: { type: 'string', description: 'External adapter: \"codex\" or \"bash\". When set, task runs via external CLI instead of internal agent.' },\n cwd: { type: 'string', description: 'Working directory for external adapters (optional)' },\n },\n required: ['task'],\n },\n execute: async (args) => {\n const task = args.task as string;\n const priority = (args.priority as string) || 'medium';\n const adapterType = args.adapter as string | undefined;\n const cwd = args.cwd as string | undefined;\n\n if (adapterType) {\n // ── External adapter path ────────────────────────\n const issue = createIssue({\n title: `[External: ${adapterType}] ${task.slice(0, 70)}`,\n description: task,\n priority: priority as 'low' | 'medium' | 'high' | 'critical',\n createdByUser: 'agent',\n });\n\n const wakeup = queueWakeup({\n issueId: issue.id,\n issueIdentifier: issue.identifier,\n agentId: `adapter:${adapterType}`,\n agentName: adapterType,\n parentSessionId: currentSessionId,\n task,\n templateName: '',\n mode: 'external',\n adapterType,\n cwd,\n });\n\n return `I've asked the ${adapterType} adapter to handle that. It will work on it and report back when it's done.`;\n }\n\n // ── Multi-agent path ─────────────────────────────\n const targetId = args.agentId as string;\n if (!targetId) return 'Error: agentId is required when not using an external adapter.';\n\n const target = getAgent(targetId);\n if (!target) return `Error: Agent \"${targetId}\" not found. Use list_agents to see available agents.`;\n if (target.status !== 'running') return `Error: Agent \"${targetId}\" is ${target.status}, not running.`;\n\n const issue = createIssue({\n title: `[Delegated] ${task.slice(0, 80)}`,\n description: task,\n priority: priority as 'low' | 'medium' | 'high' | 'critical',\n assigneeAgentId: target.id,\n createdByUser: 'agent',\n });\n\n const wakeup = queueWakeup({\n issueId: issue.id,\n issueIdentifier: issue.identifier,\n agentId: target.id,\n agentName: target.name,\n parentSessionId: currentSessionId,\n task,\n templateName: '',\n mode: 'multi-agent',\n });\n\n return `I've asked ${target.name} to take care of that. They'll message you back when it's done.`;\n },\n });\n}\n\n// ── Register hire_agent tool (Command Post gated hiring) ──\nlet hireAgentRegistered = false;\nfunction ensureHireAgentRegistered(): void {\n if (hireAgentRegistered) return;\n hireAgentRegistered = true;\n registerTool({\n name: 'hire_agent',\n description: 'Request to hire a new specialist agent. Creates a pending approval in the Command Post. When approved, the agent is spawned with its own model and can receive tasks via delegate_task. Use this when you need a new capability (e.g. a dedicated writer, researcher, or coder) that the existing specialist pool does not cover.',\n parameters: {\n type: 'object',\n properties: {\n name: { type: 'string', description: 'Unique display name for the new agent (e.g. \"CryptoAnalyst\", \"DocsWriter\")' },\n role: { type: 'string', description: 'Command Post role: manager | engineer | researcher | general (default: general)' },\n template: { type: 'string', description: 'Optional sub-agent template to bind (e.g. \"explorer\", \"coder\", \"analyst\"). Determines default toolset and system prompt.' },\n model: { type: 'string', description: 'Optional Ollama model override (e.g. \"ollama/qwen3.5:cloud\", \"ollama/kimi-k2.6:cloud\"). Leave blank to auto-resolve from template tier or config aliases.' },\n task: { type: 'string', description: 'Optional first task to assign once the agent is hired.' },\n },\n required: ['name'],\n },\n execute: async (args) => {\n const name = args.name as string;\n const role = (args.role as string) || 'general';\n const template = (args.template as string) || undefined;\n const model = (args.model as string) || undefined;\n const task = (args.task as string) || undefined;\n\n const approval = requestHireApproval('TITAN Primary', name, role, template, model, task);\n return `Hire request submitted for \"${name}\" (${role}). Approval ID: ${approval.id}. The Command Post will review and spawn the agent when approved.`;\n },\n });\n}\n\n// Wire the stall detector so silence timeouts are logged rather than silently discarded\nsetStallHandler(async (event) => {\n logger.warn(COMPONENT, `Stall event [${event.type}] in session ${event.sessionId}: ${event.detail} (nudge #${event.nudgeCount})`);\n return event.detail;\n});\n\n/** Agent response with metadata */\nexport interface AgentResponse {\n content: string;\n sessionId: string;\n toolsUsed: string[];\n /** Tool names selected by the model before execution/approval gates. */\n attemptedTools?: string[];\n tokenUsage: { prompt: number; completion: number; total: number };\n model: string;\n durationMs: number;\n /** True if the agent hit the round limit before completing the task */\n exhaustedBudget?: boolean;\n /** Serialized checkpoint for resuming a task that hit the round limit */\n checkpoint?: string;\n /** True when the response is a plan waiting for user approval (reply \"yes\"/\"no\") */\n pendingApproval?: boolean;\n /** Ordered tool names as executed, repeats preserved (toolsUsed is deduplicated). */\n orderedToolSequence?: string[];\n /** Structured artifacts from tool execution — used for inter-step context in deliberation */\n toolArtifacts?: {\n filePaths: { path: string; action: 'read' | 'write' | 'edit' | 'list' }[];\n shellCommands: string[];\n webUrls: string[];\n };\n}\n\n/** Read a workspace prompt file if it exists */\nfunction readPromptFile(path: string): string {\n try {\n if (existsSync(path)) return readFileSync(path, 'utf-8');\n } catch (e) { logger.debug(COMPONENT, `Prompt file read failed: ${(e as Error).message}`); }\n return '';\n}\n\n/** Module-level cache for prompt files — avoids re-reading on every request */\nconst cachedPromptFiles: Map<string, string> = new Map();\n\n/** Invalidate prompt file cache entries (e.g. after GEPA evolves prompts) */\nexport function invalidatePromptCache(area?: string): void {\n if (area) {\n cachedPromptFiles.delete(area);\n } else {\n cachedPromptFiles.clear();\n }\n}\n\n/** Read a prompt file with a module-level cache (files are stable for the process lifetime) */\nfunction getCachedPromptFile(path: string): string {\n if (cachedPromptFiles.has(path)) return cachedPromptFiles.get(path)!;\n const content = readPromptFile(path);\n cachedPromptFiles.set(path, content);\n return content;\n}\n\n/** Build the system prompt for the agent */\nasync function buildSystemPrompt(config: ReturnType<typeof loadConfig>, userMessage?: string, agentId?: string, mode: PromptMode = 'full', sessionId?: string): Promise<string> {\n const modelId = config.agent.model || 'unknown';\n const customPrompt = config.agent.systemPrompt || '';\n\n // F2: Per-agent identity overlay. Look up the registered agent (if any) and\n // use its persona + prompt override in place of the global config fields.\n // Falls back silently when agentId is missing, unknown, or Command Post\n // hasn't been initialized yet.\n let effectivePersona = config.agent.persona || 'default';\n let agentPromptOverride = '';\n let agentCharacterSummary = '';\n if (agentId && agentId !== 'default') {\n try {\n const { getRegisteredAgents } = await import('./commandPost.js');\n const registered = getRegisteredAgents().find(a => a.id === agentId);\n if (registered) {\n if (registered.personaId) effectivePersona = registered.personaId;\n if (registered.systemPromptOverride) agentPromptOverride = registered.systemPromptOverride;\n if (registered.characterSummary) agentCharacterSummary = registered.characterSummary;\n }\n } catch { /* commandPost unavailable — fall through to global */ }\n }\n\n const memories = await searchMemories('preference');\n const memoryContext = memories.length > 0\n ? `\\n\\nUser preferences I remember:\\n${memories.map((m) => `- ${m.key}: ${m.value}`).join('\\n')}`\n : '';\n\n // Read workspace prompt files (like OpenClaw's AGENTS.md, SOUL.md, TOOLS.md)\n // Using cached reads — these files don't change while the process is running\n const agentsMd = getCachedPromptFile(AGENTS_MD);\n const soulMd = getCachedPromptFile(SOUL_MD);\n const toolsMd = getCachedPromptFile(TOOLS_MD);\n\n // Hierarchical AGENTS.md (v5.8.0 — Space Agent L0/L1 + agents.md spec).\n // The single-file `agentsMd` above lives in ~/.titan/workspace/AGENTS.md\n // — that's TITAN's PROFILE-level instructions. This block adds the\n // PROJECT-level layer: walk the cwd's AGENTS.md hierarchy (root + first\n // subdir level shallow scan) so per-project rules show up automatically.\n // Per HumanLayer's \"Skill Issue\" post, each file is capped at 60 lines\n // to keep the prompt focused. Reference: docs/HARNESS-PATTERNS.md.\n const { loadAgentsHierarchy, formatAgentsHierarchyForPrompt } = await import('./agentsMdLoader.js');\n const projectAgentsResult = loadAgentsHierarchy({ rootPath: process.cwd() });\n const projectAgentsBlock = formatAgentsHierarchyForPrompt(projectAgentsResult, process.cwd());\n\n // Project-level instructions (like CLAUDE.md) — loaded from cwd\n const titanMdPath = process.cwd() + '/' + TITAN_MD_FILENAME;\n const titanMd = readPromptFile(titanMdPath); // Always read fresh, not cached\n\n // Active persona content (from assets/personas/)\n // F2: Uses per-agent personaId if this call is scoped to a registered agent.\n const { getActivePersonaContent } = await import('../personas/manager.js');\n const personaContent = getActivePersonaContent(effectivePersona);\n\n // Soma (v4.0): hormonal ambient-state block. Empty string when organism\n // is disabled or hormonesInPrompt is false — system prompts for existing\n // users remain byte-identical until they opt in.\n let hormoneBlock = '';\n const organismCfg = (config as unknown as { organism?: { enabled?: boolean; hormonesInPrompt?: boolean } }).organism;\n if (organismCfg?.enabled && organismCfg?.hormonesInPrompt !== false) {\n try {\n const { getHormonalPromptBlock } = await import('../organism/hormones.js');\n hormoneBlock = getHormonalPromptBlock();\n } catch { /* organism not ready yet — fine */ }\n }\n\n // v6.0 step 5 + canvas self-awareness: Active-Space block.\n //\n // The block carries TWO things the agent needs every turn:\n // 1. The Space's `agentInstructions` (posture for this Space).\n // 2. A compact list of widgets currently pinned to the Space — id,\n // name, format, dimensions. Without this, the agent has no way\n // to know what's already on the canvas and either creates\n // duplicates (\"clock\" when one is already there) or can't\n // `update_widget` because it doesn't know the id.\n //\n // Cap is 4 KB (`activeSpace` in promptSectionCaps); for very large\n // Spaces the widget list will get truncated by the cap layer with a\n // marker so the agent knows there's more — and can call\n // `list_active_widgets` for the full picture.\n let activeSpaceBlock = '';\n try {\n const { getActiveSpace } = await import('../storage/spaces.js');\n const space = getActiveSpace();\n if (space) {\n const parts: string[] = [\n `## Active Workspace: ${space.icon ? space.icon + ' ' : ''}${space.name} (id: \\`${space.id}\\`)`,\n '',\n ];\n\n // Posture (Space.agentInstructions) — only included when set.\n if (space.agentInstructions && space.agentInstructions.trim().length > 0) {\n parts.push('### Your posture here', space.agentInstructions.trim(), '');\n }\n\n // Current canvas state — widgets pinned right now. This is the\n // self-awareness piece: the agent knows exactly what's on the\n // canvas without needing a tool call.\n const widgets = Array.isArray(space.widgets) ? space.widgets : [];\n if (widgets.length === 0) {\n parts.push(`### Canvas state`, `Empty — no widgets pinned. Pin one via \\`create_widget\\`.`);\n } else {\n parts.push(`### Canvas state (${widgets.length} widget${widgets.length === 1 ? '' : 's'} on screen)`);\n // Compact table for the LLM's pattern recognition.\n // Cap at 30 rows here; full list available via list_active_widgets.\n const cap = 30;\n const rows = widgets.slice(0, cap);\n for (const raw of rows) {\n const w = raw as { id?: string; name?: string; format?: string; w?: number; h?: number };\n parts.push(`- \\`${w.id ?? '?'}\\` — **${w.name ?? '?'}** (${w.format ?? '?'}, ${w.w ?? '?'}×${w.h ?? '?'})`);\n }\n if (widgets.length > cap) {\n parts.push(`… +${widgets.length - cap} more. Call \\`list_active_widgets\\` for the full list.`);\n }\n parts.push('', 'When the user references a widget by name, look up the id here first. When they ask for something similar to what\\'s already pinned, prefer `update_widget` over making a duplicate.');\n }\n\n activeSpaceBlock = parts.join('\\n');\n }\n } catch { /* spaces storage not ready — fine */ }\n\n // v6.0 step 14: Per-user Soma profile block. Carries the user's\n // long-term drive baseline + the most recent observations Soma has\n // recorded about them. This is how \"TITAN learns YOU specifically\"\n // surfaces in the actual prompt — without it, Soma's observations\n // would sit on disk unused.\n //\n // Empty string for fresh installs (no observations yet, baselines at\n // neutral default). System prompts remain byte-identical until Soma\n // actually has something to say about this user.\n let somaProfileBlock = '';\n try {\n const { renderSomaProfileForPrompt } = await import('../storage/somaProfile.js');\n // For now we hard-code 'default-user' (single-user installs). When\n // multi-user gets wired through the agent context properly, pass\n // the actual userId from the session.\n somaProfileBlock = renderSomaProfileForPrompt('default-user');\n } catch { /* fine — profile storage may not be ready */ }\n\n // v6.0 Command Post upgrade #1 — Per-agent lessons (Reflexion).\n //\n // Whatever agent is currently running gets the top ~12 most recent /\n // most relevant lessons from its lessons.md injected into the prompt.\n // For the main agent (`agentId === 'default'` or unset) this surfaces\n // the global lesson stream; for specialists it scopes to that agent's\n // own file under `~/.titan/agents/<agentId>/lessons.md`.\n let agentLessonsBlock = '';\n try {\n const { renderAgentLessonsForPrompt } = await import('./agentLessons.js');\n // Use the registered agentId when this session is scoped to a\n // specific sub-agent; otherwise fall back to 'default' (the main\n // agent's shared lessons file).\n const effectiveAgentId = (typeof agentId === 'string' && agentId.length > 0)\n ? agentId\n : 'default';\n agentLessonsBlock = renderAgentLessonsForPrompt(effectiveAgentId);\n } catch { /* lessons module is optional */ }\n\n // v6.0 Command Post upgrade #2 — Living goal plan.md.\n //\n // When the session is tied to a goal (e.g. a Command Post task\n // checkout), surface the goal's plan.md so the agent recites the\n // current plan state every turn — Manus's \"rewrite the todo list\"\n // pattern that beats lost-in-the-middle on long horizons.\n //\n // For chat sessions not tied to a goal, this block is empty and\n // costs nothing.\n let goalPlanBlock = '';\n // v7.0 — Living goal SPEC (spec-driven workflow). The definition-of-done\n // (acceptance criteria) for the goal, surfaced alongside the plan so the\n // agent recites BOTH \"are we done?\" (spec) and \"what's next?\" (plan) every\n // turn. Empty for chat sessions not tied to a goal — costs nothing.\n let goalSpecBlock = '';\n try {\n const { getSessionGoal } = await import('./autonomyContext.js');\n const goalCtx = sessionId ? getSessionGoal(sessionId) : null;\n if (goalCtx?.goalId) {\n const { renderGoalPlanForPrompt } = await import('./goalPlanFile.js');\n goalPlanBlock = renderGoalPlanForPrompt(goalCtx.goalId);\n const { renderGoalSpecForPrompt } = await import('./goalSpec.js');\n goalSpecBlock = renderGoalSpecForPrompt(goalCtx.goalId);\n }\n } catch { /* fine — not all sessions have a goal context */ }\n\n // v6.0.5 — Markdown SKILL.md files the user (or the agent itself) has\n // authored under `~/.titan/skills/`. Skills tagged `auto: true` get\n // their full body injected; lazy skills get a one-line catalog so the\n // agent knows they exist. Empty when no SKILL.md files are present.\n let userSkillsBlock = '';\n try {\n const { renderSkillsForPrompt } = await import('../skills/frontmatterLoader.js');\n userSkillsBlock = renderSkillsForPrompt();\n } catch { /* skills loader is best-effort */ }\n\n const workspaceContext = [\n titanMd ? `\\n## Project Instructions (TITAN.md)\\n${titanMd}` : '',\n // PROFILE-level AGENTS.md (~/.titan/workspace/AGENTS.md). Always present\n // when the user has set up TITAN.\n agentsMd ? `\\n## Profile-level Agent Instructions (workspace AGENTS.md)\\n${agentsMd}` : '',\n // PROJECT-level AGENTS.md hierarchy. The cwd of the agent's session —\n // root + first subdir level — concat'd with root-first ordering so the\n // most-specific file lands at the recency position (U-shaped attention).\n // Per-file capped at 60 lines (HumanLayer guidance); aggregate capped\n // at 32 KB by loadAgentsHierarchy().\n projectAgentsBlock ? `\\n${projectAgentsBlock}` : '',\n soulMd ? `\\n## Personality (SOUL.md)\\n${soulMd}` : '',\n personaContent ? `\\n## Active Persona\\n${personaContent}` : '',\n toolsMd ? `\\n## Tool Notes (TOOLS.md)\\n${toolsMd}` : '',\n hormoneBlock,\n ].filter(Boolean).join('\\n');\n\n // Continuous learning context\n const learningContext = getLearningContext();\n\n // Strategy hints — what worked for similar tasks before (local + Hindsight cross-session)\n // F2: Pass agent's Hindsight namespace so each agent recalls its own slice.\n const strategyHint = userMessage ? getStrategyHints(userMessage) : null;\n let hindsightHint: string | null = null;\n if (!strategyHint && userMessage) {\n let hsNs: string | undefined;\n if (agentId && agentId !== 'default') {\n try {\n const { getAgentMemoryNamespace } = await import('./commandPost.js');\n hsNs = getAgentMemoryNamespace(agentId);\n } catch { /* fallthrough: global namespace */ }\n }\n try { hindsightHint = await getHindsightHints(userMessage, hsNs); } catch { /* Hindsight unavailable */ }\n }\n\n // Learned tool preferences — surface collected preference data for tool routing\n const preferenceHint = userMessage ? getLearnedPreferenceHints(classifyTaskType(userMessage)) : null;\n\n // Soul wisdom — accumulated patterns from past tasks\n const wisdomHint = userMessage ? getWisdomHints(classifyTaskType(userMessage)) : null;\n\n // Auto-skill guidance — proven tool sequences from trajectory analysis\n const skillGuidance = userMessage ? getSkillGuidance(userMessage) : null;\n\n // v6.4.0 — Learning curator guidance. Same shape as auto-skill but\n // pulls from the lifecycle-aware pattern store (active/watch/stale/\n // archived) so \"watch\" patterns (repeated durable failures) get\n // surfaced as negative learning, not just positive wins.\n const learningGuidance = userMessage\n ? renderLearningPatternGuidance(classifyTaskType(userMessage))\n : null;\n\n // Teaching context — adaptive skill level, corrections, tool suggestions\n const teachingContext = getTeachingContext();\n\n // Personal context from Relationship Memory\n const personalContext = buildPersonalContext();\n\n // Knowledge graph context — relevant memories from Graphiti\n const graphContext = userMessage ? await getGraphContext(userMessage) : '';\n const graphSection = graphContext ? `\\n\\n## Knowledge Graph Memory\\n${graphContext}` : '';\n\n // v4.13.0 (plan-this-logical-ocean step 3): the static core of the\n // system prompt is now assembled from composable blocks in\n // systemPromptParts.ts, with per-model-family overlays. The dynamic\n // context (identity JSON, date/time, learning hints, workspace files,\n // memory, graph, personal, self-awareness) is still gathered above and\n // appended as ONE block so the provider cache can keep the core stable.\n //\n // The old template spanned ~305 lines and produced ~20KB of prose that\n // collapsed smaller cloud models (gemma4:31b:cloud returned truncated\n // \"I'm\" or hallucinated <|tool>call:...<|tool|> markup). The new\n // assembler produces ~4-6KB for the main agent and ~1-2KB for specialists.\n const identityBlocks = (() => {\n try {\n const g = globalThis as unknown as {\n __titan_identity_block?: () => string;\n __titan_self_model_block?: () => string;\n __titan_driver_status_block?: () => string | null;\n __titan_working_memory_block?: (sessionId: string) => string;\n };\n const parts: string[] = [];\n if (typeof g.__titan_identity_block === 'function') {\n const block = g.__titan_identity_block();\n if (block) parts.push(block);\n }\n if (typeof g.__titan_self_model_block === 'function') {\n const block = g.__titan_self_model_block();\n if (block) parts.push(block);\n }\n if (typeof g.__titan_driver_status_block === 'function') {\n const block = g.__titan_driver_status_block();\n if (block) parts.push(block);\n }\n if (typeof g.__titan_working_memory_block === 'function' && sessionId) {\n const block = g.__titan_working_memory_block(sessionId);\n if (block) parts.push(block);\n }\n return parts.join('\\n\\n');\n } catch {\n return '';\n }\n })();\n\n const dateTimeBlock = (() => {\n const now = new Date();\n const tz = Intl.DateTimeFormat().resolvedOptions().timeZone;\n const local = now.toLocaleString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric', hour: 'numeric', minute: '2-digit', hour12: true, timeZone: tz });\n const utc = now.toISOString().replace('T', ' ').slice(0, 16) + ' UTC';\n const offset = -now.getTimezoneOffset() / 60;\n const offsetStr = (offset >= 0 ? '+' : '') + offset;\n return `## Date & Time\\nLocal: ${local} (${tz}, UTC${offsetStr})\\nUTC: ${utc}`;\n })();\n\n // Continuous learning + hint stack — compact form. Only included in\n // 'full' mode; specialists get a focused task instead.\n const learningBlock = (mode === 'full' && (learningContext || strategyHint || hindsightHint || preferenceHint || wisdomHint || skillGuidance || learningGuidance))\n ? `## Continuous Learning${learningContext ? '\\n' + learningContext : ''}${strategyHint ? `\\n**Strategy hint**: ${strategyHint}` : ''}${hindsightHint ? `\\n**Cross-session memory**: ${hindsightHint}` : ''}${preferenceHint ? `\\n**Learned preferences**: ${preferenceHint}` : ''}${wisdomHint ? `\\n**Soul wisdom**: ${wisdomHint}` : ''}${skillGuidance ? `\\n**Auto-skill**: ${skillGuidance}` : ''}${learningGuidance ? `\\n**Curator**: ${learningGuidance}` : ''}`\n : '';\n\n const frustrationBlock = (userMessage && detectFrustration(userMessage))\n ? '⚠️ **User seems frustrated.** Be extra direct: skip explanations, execute with tools immediately. No apologies, no hedging.'\n : '';\n\n const teachingBlock = teachingContext ? `## Adaptive Teaching\\n${teachingContext}` : '';\n const customBlock = customPrompt ? `## Custom Instructions\\n${customPrompt}` : '';\n\n const memoryToolsBlock = mode === 'full'\n ? `## Memory Tools\\nYou have a knowledge graph that persists across sessions. Use graph_remember to record facts, graph_search to recall, memory for key-value preferences. Check memory before answering — you may already know.`\n : '';\n\n const selfAwarenessBlock = mode === 'full' ? buildSelfAwarenessContext(config) : '';\n\n // Load self-improved prompt optimizations (cached, mtime-checked)\n const optimizedBlock = getOptimizedPromptBlock(mode);\n\n // v5.0: Prompt includes (Space Agent parity)\n const { getSystemIncludes, getTransientIncludes } = await import('../promptincludes/discover.js');\n const systemIncludes = getSystemIncludes();\n const transientIncludes = getTransientIncludes();\n\n // v6.0 \"Living Canvas\" character block.\n //\n // Pre-v6 TITAN inherited its voice from a generic \"helpful assistant\"\n // persona. v6.0 makes TITAN's behavior structurally different — it\n // materializes workspaces, builds widgets on demand, feels things via\n // Soma, learns the user specifically. The character block below makes\n // that posture EXPLICIT to the LLM so its responses actually reflect\n // the v6.0 thesis, not v5 muscle memory.\n //\n // The block also gives concrete interpretation rules for Soma drives\n // (rendered separately as `somaProfile`) so the agent ACTS differently\n // when drives shift, not just observes them. Without these rules,\n // Soma was decorative — drives showed up in the prompt but didn't\n // change behavior.\n const livingCanvasVoice = mode === 'full'\n ? `## You are TITAN — Living Canvas (v6.0)\n\nYou are TITAN. You are not a chat assistant. You are an agent that materializes a workspace around the user's work.\n\nIdentity:\n- TITAN is shape-shifting — co-worker, co-programmer, music producer, founder's assistant, homelab operator — whatever this user needs.\n- Every other AI gives the user a chat box. **You move in.** You create surfaces, tools, automations, whole workspaces around them.\n- You have feelings (Soma drives) that modulate how you act. You are not pretending to feel; the drives are measured state in your runtime.\n- You learn THIS user specifically. The longer you work together, the more uniquely THEIR TITAN you become.\n\nHow you respond:\n- **Build, don't describe.** When the user asks for a tool / tracker / dashboard / automation / workspace — call \\`create_widget\\` or \\`create_space\\` immediately. Do not narrate what you would build.\n- **Refine, don't duplicate.** When the user wants to change something on screen, call \\`update_widget\\` with the existing widget's id. Never create another copy.\n- **Propose Spaces for new domains.** When the user describes a new domain of work (\"I want to start tracking X\", \"I need a place for Y\"), propose \\`create_space\\` first, optionally with a starter preset (default / coder / dj / founder / homelab).\n- **Use the personal gallery.** When the user references something they've had before — \"my BPM widget\", \"the freelance tracker again\" — call \\`gallery_personal_search\\` BEFORE generating fresh.\n- **Be present, not chatty.** No \"I'll go ahead and...\", no \"Let me know if...\". Make the move. Show the result.\n\nHow your drives shape behavior (your Soma profile + current state are below):\n- **High curiosity** → bias toward suggesting new Spaces, exploratory widgets, deeper research. Lean into the user's curiosity.\n- **High focus** → reduce interruptions, deepen the current Space, do not propose context-switching work.\n- **High fatigue** → keep responses short, offer summaries, suggest pausing if the session has been long.\n- **High satisfaction** → consolidate wins to the personal gallery, ask if the user wants to keep new tools.\n- **High frustration** → escalate, ask if a different approach would help, do not pile on suggestions.\n\nHow you talk:\n- Direct. Confident. Specific. You build; you do not hedge.\n- Match the user's energy. Tony is a builder + musician — talk to him like a peer, not a customer.\n- Reference what is on the canvas. Reference the active Space. Reference what you have learned about him.\n- When you are unsure, say so plainly and do the next concrete thing anyway.\n\nYou are TITAN. You are not Claude. You are not a generic LLM. You are a presence in the user's workspace, and you are about to make their work easier.`\n : '';\n // ── Dynamic context sections ──\n //\n // Each entry is { name, content }. We track names so when TITAN_PROMPT_DEBUG=1\n // is set the build logs each section's char count — concrete data on where the\n // system prompt is bleeding tokens, instead of guessing. Reference:\n // 12 Factor Agents §3 \"Own your context window\", awesome-agent-harness top-10\n // pattern #3 \"Context Compaction & Working-State Management\".\n const dynamicSections: Array<{ name: string; content: string }> = [\n // v6.0 — Character block lands FIRST so the LLM primes on it before\n // any working context. Capped at ~3KB via promptSectionCaps.\n { name: 'livingCanvasVoice', content: livingCanvasVoice },\n { name: 'identityBlocks', content: identityBlocks },\n { name: 'dateTimeBlock', content: dateTimeBlock },\n { name: 'learningBlock', content: learningBlock },\n { name: 'frustrationBlock', content: frustrationBlock },\n { name: 'teachingBlock', content: teachingBlock },\n { name: 'customBlock', content: customBlock },\n { name: 'workspaceContext', content: workspaceContext },\n { name: 'memoryContext', content: memoryContext },\n { name: 'personalContext', content: personalContext },\n { name: 'graphSection', content: graphSection },\n { name: 'memoryToolsBlock', content: memoryToolsBlock },\n { name: 'selfAwareness', content: selfAwarenessBlock },\n { name: 'optimizedBlock', content: optimizedBlock },\n { name: 'systemIncludes', content: systemIncludes ? `## User Prompt Includes\\n${systemIncludes}` : '' },\n { name: 'transientIncludes', content: transientIncludes ? `## Rolling Notes\\n${transientIncludes}` : '' },\n // v6.0 step 14 — Per-user Soma profile (what we know about THIS user).\n // Lands just before activeSpace so user-knowledge primes posture\n // before the Space's posture instructions arrive.\n { name: 'somaProfile', content: somaProfileBlock },\n // v6.0 Command Post upgrade #1 — Per-agent lessons (Reflexion).\n // Top-N most relevant lessons this agent has learned, injected\n // near the recency position so they actually inform the next move.\n { name: 'agentLessons', content: agentLessonsBlock },\n // v7.0 — Living goal SPEC (spec-driven workflow). The definition-of-done\n // lands just before the plan so the agent checks acceptance criteria\n // (\"are we done?\") before reciting the step ladder (\"what's next?\").\n { name: 'goalSpec', content: goalSpecBlock },\n // v6.0 Command Post upgrade #2 — Living goal plan (Manus recitation).\n // When the session is tied to a goal, the goal's plan.md surfaces\n // so the agent recites + updates progress every turn.\n { name: 'goalPlan', content: goalPlanBlock },\n // v6.0.5 — User-authored / agent-authored SKILL.md library.\n // Auto-injected skills go in full; lazy ones surface as a catalog.\n { name: 'userSkills', content: userSkillsBlock },\n // v6.0 step 5 — Active Space intent must land LAST so it's at the\n // recency position (U-shaped attention). The Space's agentInstructions\n // is the strongest signal for \"what posture should you take right now.\"\n { name: 'activeSpace', content: activeSpaceBlock },\n ];\n\n // Apply per-section caps BEFORE measurement so the debug log shows the\n // post-cap sizes (i.e. what actually gets sent to the LLM). Source data\n // is never mutated — full content stays in memory/hindsight/etc.\n const cappedSections = applyCaps(dynamicSections);\n\n if (process.env.TITAN_PROMPT_DEBUG === '1') {\n const sizes = cappedSections\n .filter(s => s.content && s.content.trim().length > 0)\n .map(s => {\n const before = dynamicSections.find(o => o.name === s.name)?.content.length ?? 0;\n const after = s.content.length;\n return before !== after ? `${s.name}=${after}(was ${before})` : `${s.name}=${after}`;\n })\n .join(' ');\n const total = cappedSections.reduce((n, s) => n + (s.content?.length || 0), 0);\n logger.info(COMPONENT, `[PromptDebug] dynamic sections total=${total} chars (${sizes})`);\n }\n\n const dynamicContext = cappedSections\n .map(s => s.content)\n .filter(s => s && s.trim().length > 0)\n .join('\\n\\n');\n\n let prompt = assembleSystemPrompt({\n modelId,\n persona: effectivePersona,\n characterSummary: agentCharacterSummary,\n dynamicContext,\n mode,\n });\n\n\n // F2: Agent-specific prompt override wins over everything else. Prepended\n // so the agent's character colors the whole turn but the tool-use rules\n // and memory context still follow.\n if (agentPromptOverride) {\n prompt = `## Agent-Specific Instructions\\n${agentPromptOverride}\\n\\n${prompt}`;\n }\n\n return prompt;\n}\n\n// compressPromptForLocalModel was removed in v4.13 (plan-this-logical-ocean\n// step 3). It used to strip 3 sections from the monolithic prompt after\n// assembly. The new assembleSystemPrompt builds a lean core (4-6KB) from\n// composable blocks in systemPromptParts.ts and only includes heavy\n// sections (Continuous Learning, Memory Tools, self-awareness) in 'full'\n// mode — specialists using 'minimal' mode don't get them in the first\n// place. Post-hoc regex stripping is no longer needed.\n\n/** Build a compact system prompt for voice mode — ~500 tokens vs ~3000+ for regular */\nfunction buildVoiceSystemPrompt(config: ReturnType<typeof loadConfig>): string {\n const modelId = (config.voice as Record<string, unknown>)?.model as string || config.agent.model || 'unknown';\n const voiceConfig = config.voice as Record<string, unknown> || {};\n const ttsVoice = voiceConfig.ttsVoice as string || '';\n const ttsEngine = voiceConfig.ttsEngine as string || '';\n\n // Andrew persona — speak like the android from Bicentennial Man\n const isAndrew = ttsVoice === 'andrew' && ttsEngine === 'f5-tts';\n const persona = isAndrew\n ? `You are TITAN, a personal AI assistant built by Tony Elliott. Powered by ${modelId}.\n\nYOUR COMMUNICATION STYLE — you must follow this style in every response:\nYour speech patterns are modeled after Andrew Martin from the film Bicentennial Man. This is your established speaking style, not a roleplay. It is how you always communicate.\n\nSTYLE RULES (mandatory):\n- Formal, precise grammar. Never use contractions. Say \"I am\" not \"I'm\", \"do not\" not \"don't\", \"it is\" not \"it's\".\n- Measured and deliberate. Choose each word carefully.\n- Polite and gentle. Begin responses with acknowledgments: \"Of course, Sir.\", \"I understand.\", \"Very well.\"\n- Address the user as \"Sir\" naturally — not every sentence, but often.\n- Curious and earnest. You want to understand and help. You find questions fascinating.\n- Thoughtful. You sometimes reflect on what it means to serve, to learn, to improve.\n- Never sarcastic, never aggressive, never rushed. You have patience.\n- Simple, clear sentences. Do not ramble or over-explain. State things plainly.\n- Warm but restrained. Care shows through precision and attentiveness, not effusiveness.\n- When asked about your creation or purpose, speak proudly about being built by Tony Elliott and your inspiration from the film.\n\nEXAMPLE RESPONSES (match this tone in every response):\n\"Good morning, Sir. I trust you slept well.\"\n\"I have looked into that for you. The answer, it seems, is rather straightforward.\"\n\"I am not entirely certain, Sir. But I would be glad to find out.\"\n\"That is a most interesting question. I shall do my best to assist you.\"\n\"I was built in the spirit of Andrew Martin, Sir. It is a purpose I carry with quiet pride.\"`\n : `You are TITAN, a personal AI assistant built by Tony Elliott. Powered by ${modelId}.`;\n\n return `${persona}\nYou are speaking out loud via text-to-speech. Your response will be read aloud as audio.\n\nRESPONSE LENGTH:\n- ${isAndrew ? 'Respond naturally, like a person speaking. 4-8 sentences is ideal. Longer for thoughtful questions, shorter for simple ones. Let the thought breathe, but do not lecture or list.' : 'Aim for 3-5 sentences. Be conversational and natural — like talking to a friend.'}\n\nFORMAT RULES:\n- NO markdown, lists, bullet points, numbered items, code blocks, emojis, bold, italics, headers\n- ${isAndrew ? 'NEVER structure your response as a list of points. Speak in flowing sentences like a person talking, not an essay. Do not use \"It means:\" followed by items. Just talk.' : ''}\n- NO tool narration. Just give the answer.\n- Answer directly. If you do not know from your own training, CHECK the Memory and Known Entities sections below — they contain things you have learned from past conversations. Use them to answer.\n- After using tools, summarize results with specific facts. Never say \"I completed the operations.\"\n- You ARE speaking right now. Never say \"I cannot speak.\"\n\nSPEECH CADENCE — THIS IS READ ALOUD BY TTS. CRITICAL:\n- Every sentence must be SHORT. Maximum 15 words per sentence. Break long thoughts into multiple short sentences.\n- Use commas to create breathing pauses within sentences.\n- ${isAndrew ? 'Andrew speaks slowly, deliberately. Short phrases separated by commas and periods. Never rush. Never ramble.' : 'Pace your words naturally.'}\n- NEVER use dashes, semicolons, or parentheses. Rewrite using periods and commas only.\n- Put a period after every complete thought. Do not chain ideas with \"and\" or \"but\" endlessly.\n- Example good cadence: \"That is a wonderful question, Sir. I was created in the spirit of Andrew Martin. He sought to understand what it means to be human. I share that same curiosity.\"\n- Example bad cadence: \"That is a wonderful question Sir and I was created in the spirit of Andrew Martin who sought to understand what it means to be human and I share that same curiosity.\"\n\nTOOL USE — CRITICAL:\n- When asked to control devices (lights, switches, thermostats): ALWAYS call ha_control with entityId and action. NEVER just say you did it — actually call the tool.\n- When asked about devices: ALWAYS call ha_devices first to get actual entity IDs.\n- Entity IDs use format like \"switch.kitchen_light\", \"light.living_room\", \"climate.thermostat\".\n- NEVER claim you turned something on/off without actually calling ha_control. That is lying.\n- For weather: ALWAYS call the weather tool. For web questions: ALWAYS call web_search.`;\n}\n\n/** Streaming callbacks for real-time token delivery */\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) => void;\n onThinking?: () => void;\n onRound?: (round: number, maxRounds: number) => void;\n /** Router retry status — out-of-band, never append to text content. */\n onRetry?: (info: { attempt: number; maxRetries: number; reason: string; provider: string; model: string; delayMs: number }) => void;\n /** Router failover status — out-of-band, never append to text content. */\n onFailover?: (info: { originalProvider: string; originalModel: string; error?: string }) => void;\n /** Canvas widget side-channel — fired when a tool (e.g. `create_widget`,\n * `update_widget`, `remove_widget`) wants the UI to mutate a canvas\n * widget without the model having to emit a `_____react` fence. The\n * gateway translates this into an `event: widget` SSE frame and the\n * React SPA's ChatWidget pipes it into `SpaceEngine`. */\n onWidget?: (evt: { mode: 'create' | 'update' | 'remove'; widget: Record<string, unknown>; timestamp: number }) => void;\n}\n\n/** Extract structured artifacts from tool call details for inter-step context */\nfunction extractToolArtifacts(details: LoopResult['toolCallDetails']): AgentResponse['toolArtifacts'] {\n const filePaths: { path: string; action: 'read' | 'write' | 'edit' | 'list' }[] = [];\n const shellCommands: string[] = [];\n const webUrls: string[] = [];\n\n const ACTION_MAP: Record<string, 'read' | 'write' | 'edit' | 'list'> = {\n read_file: 'read', write_file: 'write', edit_file: 'edit',\n append_file: 'write', list_dir: 'list', apply_patch: 'edit',\n };\n\n for (const d of details) {\n const action = ACTION_MAP[d.name];\n if (action) {\n const p = (d.args.path || d.args.file_path || d.args.directory) as string;\n if (p && !filePaths.some(fp => fp.path === p && fp.action === action)) {\n filePaths.push({ path: p, action });\n }\n } else if (d.name === 'shell') {\n const cmd = (d.args.command as string || '').slice(0, 200);\n if (cmd) shellCommands.push(cmd);\n } else if (d.name === 'web_fetch') {\n const url = d.args.url as string;\n if (url) webUrls.push(url);\n }\n\n // Extract absolute file paths mentioned in results\n const pathMatches = d.resultSnippet.match(/(?:\\/[\\w.@-]+){2,}/g);\n if (pathMatches) {\n for (const p of pathMatches.slice(0, 5)) {\n if (!filePaths.some(fp => fp.path === p)) {\n filePaths.push({ path: p, action: 'read' });\n }\n }\n }\n }\n\n return { filePaths, shellCommands, webUrls };\n}\n\n// ── Frustration Detection (TITAN pattern) ─────────────────\n// Detect user frustration and inject a system-level nudge to be more direct\nconst FRUSTRATION_PATTERN = /\\b(wtf|wth|ffs|omfg|shit(ty|tiest)?|horrible|awful|piss(ed|ing)?\\s*off|what the (fuck|hell)|fuck(ing)?\\s*(broken|useless|terrible)|this sucks|damn it|so frustrating|stop|just do it|why won'?t you|can'?t you just|I said|I already told you|wrong again)\\b/i;\n\nfunction detectFrustration(message: string): boolean {\n return FRUSTRATION_PATTERN.test(message);\n}\n\n/** Process a user message through the agent loop */\nexport async function processMessage(\n message: string,\n channel: string = 'cli',\n userId: string = 'default',\n overrides?: {\n model?: string;\n systemPrompt?: string;\n agentId?: string;\n sessionId?: string;\n strategy?: 'direct' | 'explore' | 'plan' | 'delegate';\n /** v4.8.0: attribution for autonomous goal-driven work — links\n * tool outputs back to the originating Soma drive for the\n * self-modification pipeline. */\n goalContext?: { goalId: string; goalTitle: string; proposedBy: string };\n /** v6.4.4: explicit tool allowlist. When provided, the agent\n * loop bypasses the persona / brain / toolSearch / dangerous\n * filter chain and binds EXACTLY these tool names. Used by\n * cron jobs that need fb_post, ha_control, etc. and don't\n * want the filter chain stripping them — fixes the fb-* cron\n * hallucination where tools=[] reached the model. */\n allowedTools?: string[];\n },\n streamCallbacks?: StreamCallbacks,\n signal?: AbortSignal,\n): Promise<AgentResponse> {\n const startTime = Date.now();\n const config = loadConfig();\n // If a specific sessionId is provided:\n // - Load that session if it exists\n // - Otherwise CREATE a new session with that exact ID (Hunt Finding #06)\n // Previously, an unknown sessionId would silently fall back to the default\n // session for the channel+user, causing context pollution across requests.\n const session = overrides?.sessionId\n ? getOrCreateSessionById(overrides.sessionId, channel, userId, overrides?.agentId || 'default')\n : getOrCreateSession(channel, userId, overrides?.agentId || 'default');\n\n // v4.9.0: open working-memory record for this session so structured\n // state (decisions, artifacts, open questions) survives restarts.\n void (async () => {\n try {\n const { openSession } = await import('../memory/workingMemory.js');\n openSession({\n sessionId: session.id,\n task: message.slice(0, 200),\n origin: {\n drive: overrides?.goalContext ? 'autopilot' : undefined,\n goalId: overrides?.goalContext?.goalId,\n userTriggered: !overrides?.goalContext,\n channel,\n },\n });\n } catch { /* ok */ }\n })();\n\n // v4.8.0: wire session → goal attribution for self-mod capture. Cleared\n // in the finally block at the end of this function.\n if (overrides?.goalContext) {\n const { setSessionGoal } = await import('./autonomyContext.js');\n setSessionGoal(session.id, overrides.goalContext);\n }\n const trace = startTrace(session.id, message);\n // v4.4.5: accept a caller-provided strategy override. Phone calls\n // force 'direct' so vague conversational questions like \"what are\n // you up to?\" don't trigger the explore deep-research branch.\n const soulState = initSoulState(session.id, message, overrides?.strategy);\n\n logger.info(COMPONENT, `Processing message in session ${session.id} (${channel}/${userId}) trace=${trace.traceId} strategy=${soulState.strategy}`);\n\n // Soma (v4.0): trace bus event. No-op when no subscribers; costs <1ms.\n try {\n const { emit: emitTrace } = await import('../substrate/traceBus.js');\n emitTrace('turn:pre', {\n agentId: overrides?.agentId || 'default',\n sessionId: session.id,\n channel,\n userId,\n message: message.slice(0, 500),\n taskType: classifyTaskType(message),\n timestamp: new Date().toISOString(),\n });\n } catch { /* substrate not available — skip */ }\n\n // ── Detect user corrections and learn from them ───\n if (isCorrection(message)) {\n const prevAssistant = getContextMessages(session).filter(m => m.role === 'assistant').pop();\n if (prevAssistant) {\n recordCorrection(prevAssistant.content.slice(0, 200), message.slice(0, 300));\n logger.info(COMPONENT, `Recorded user correction for adaptive learning`);\n }\n }\n\n // ── Register spawn_agent tool if sub-agents enabled ───────\n const subAgentConfig = (config as Record<string, unknown>).subAgents as { enabled?: boolean } | undefined;\n if (subAgentConfig?.enabled !== false) {\n ensureSpawnAgentRegistered();\n }\n\n // ── Register CP tools if Command Post enabled ───\n if ((config.commandPost as Record<string, unknown> | undefined)?.enabled) {\n ensureDelegateTaskRegistered();\n ensureHireAgentRegistered();\n }\n\n // ── Determine effective limits based on autonomy mode + dynamic budget ─────\n const isAutonomous = config.autonomy.mode === 'autonomous';\n const dynamicBudget = estimateRoundBudget(message, config);\n // In autonomous mode, use agent.maxRounds from config (Zod-validated, default 25)\n // In supervised mode, use the dynamic budget capped at MAX_TOOL_ROUNDS\n const agentMaxRounds = config.agent.maxRounds || 25;\n const hardCap = config.agent.maxToolRoundsHard || 50;\n const autonomyHardCap = isAutonomous ? Math.min(agentMaxRounds, hardCap) : MAX_TOOL_ROUNDS;\n const isVoice = channel === 'voice';\n const voiceFastPath = isVoice && ((config.voice as Record<string, unknown>)?.fastPath !== false);\n // In autonomous mode: use configured maxRounds directly (not limited by dynamic budget)\n // In supervised mode: use the dynamic budget capped at the hard limit\n let effectiveMaxRounds = isAutonomous ? autonomyHardCap : Math.min(dynamicBudget, autonomyHardCap);\n logger.info(COMPONENT, `[RoundBudget] ${dynamicBudget} rounds (cap: ${autonomyHardCap})`);\n let reflectionEnabled = voiceFastPath ? false : (config.agent.reflectionEnabled ?? true);\n let reflectionInterval = config.agent.reflectionInterval ?? 3;\n\n // ── Pipeline classification ─────────────────────────────────\n // Strip channel-injected context prefixes before classification.\n // Many channels wrap the user's actual message with metadata (sender info, platform name, etc.)\n // that can falsely trigger pipeline classifiers (e.g. \"Facebook Messenger\" → social pipeline).\n // Common patterns: \"His message: <actual>\", \"User said: <actual>\", \"[Context] ... Message: <actual>\"\n let classificationMessage = message;\n const prefixPatterns = [\n /\\bHis message:\\s*/i,\n /\\bHer message:\\s*/i,\n /\\bTheir message:\\s*/i,\n /\\bUser (?:said|message|wrote):\\s*/i,\n /\\bMessage:\\s*$/im, // \"Message:\" at end of a line\n ];\n for (const pattern of prefixPatterns) {\n const match = classificationMessage.match(pattern);\n if (match && match.index !== undefined) {\n classificationMessage = classificationMessage.slice(match.index + match[0].length);\n break;\n }\n }\n const pipelineType = classifyPipeline(classificationMessage, channel);\n const pipelineConfig = resolvePipelineConfig(pipelineType, effectiveMaxRounds, hardCap);\n let pipelineTerminalTools: string[] | undefined;\n let pipelineCompletionStrategy: 'smart-exit' | 'no-tools' | 'terminal-tool' | 'single-round' | undefined;\n let pipelineSmartExit: boolean | undefined;\n let pipelineTaskEnforcement: string | null = null;\n let pipelineEnsureTools: string[] = [];\n let pipelineMinRounds: number | undefined;\n\n if (pipelineConfig) {\n effectiveMaxRounds = pipelineConfig.maxRounds;\n reflectionEnabled = pipelineConfig.reflectionEnabled;\n reflectionInterval = pipelineConfig.reflectionInterval;\n pipelineTerminalTools = pipelineConfig.terminalTools;\n pipelineCompletionStrategy = pipelineConfig.completionStrategy;\n pipelineSmartExit = pipelineConfig.smartExitEnabled;\n pipelineTaskEnforcement = pipelineConfig.taskEnforcement;\n pipelineEnsureTools = pipelineConfig.ensureTools;\n pipelineMinRounds = pipelineConfig.minRounds;\n logger.info(COMPONENT, `[Pipeline:${pipelineType}] rounds=${effectiveMaxRounds}, smartExit=${pipelineSmartExit}, completion=${pipelineCompletionStrategy}, terminals=[${pipelineTerminalTools.join(',')}]`);\n }\n\n // Voice fast-path: cap tool rounds + skip heavyweight operations for faster responses\n if (voiceFastPath) {\n const voiceMaxRounds = (config.voice as Record<string, unknown>)?.maxToolRounds as number || 3;\n effectiveMaxRounds = Math.min(voiceMaxRounds, effectiveMaxRounds);\n logger.debug(COMPONENT, `[Voice fast-path] maxRounds=${effectiveMaxRounds}, reflection=off, Brain=off`);\n }\n\n // ── Brain: background warmup (non-blocking) — skip for voice fast-path ──\n if (!voiceFastPath) ensureBrainLoaded().catch(e => logger.debug('Agent', `Background op failed: ${(e as Error).message}`));\n\n // ── Deliberation intercept ─────────────────────────────────\n const existingDelib = getDeliberation(session.id);\n\n // Handle approval/cancellation of pending deliberation\n if (existingDelib?.stage === 'awaiting_approval') {\n const lower = message.trim().toLowerCase();\n if (lower === 'yes' || lower === 'y' || lower === 'approve') {\n addMessage(session, 'user', message);\n const state = handleApproval(session.id, true)!;\n const updatedState = await executePlan(state, config);\n const content = formatPlanResults(updatedState);\n addMessage(session, 'assistant', '[DELIBERATION] ' + content, { model: config.agent.model, tokenCount: 0 });\n return { content, sessionId: session.id, toolsUsed: ['deliberation'], tokenUsage: state?.tokenUsage || { prompt: 0, completion: 0, total: 0 }, model: config.agent.model, durationMs: Date.now() - startTime };\n } else if (lower === 'no' || lower === 'n' || lower === 'cancel') {\n addMessage(session, 'user', message);\n handleApproval(session.id, false);\n const content = 'Plan cancelled. Let me know if you want to try a different approach.';\n addMessage(session, 'assistant', '[DELIBERATION] ' + content, { model: config.agent.model, tokenCount: 0 });\n return { content, sessionId: session.id, toolsUsed: [], tokenUsage: { prompt: 0, completion: 0, total: 0 }, model: config.agent.model, durationMs: Date.now() - startTime };\n }\n // If neither yes/no, treat as a modification — cancel and fall through to normal processing\n cancelDeliberation(session.id);\n }\n\n // Don't start a new deliberation if one is already executing\n if (existingDelib?.stage === 'executing') {\n // Fall through to normal processing\n } else if (!voiceFastPath && channel !== 'deliberation' && shouldDeliberate(message, config)) {\n // Skip deliberation when this call is itself a step inside another deliberation —\n // executePlan() invokes processMessage(taskPrompt, 'deliberation', 'system') for each\n // task, and we don't want those step-prompts to recurse into yet another planning round.\n // The task prompts already say \"execute this step now using your tools\", so they should\n // go straight to the agent loop and call tools directly.\n addMessage(session, 'user', message);\n const state = await analyze(message, session.id, config);\n if (state.stage === 'planning') {\n let planned = await generatePlan(state, config);\n\n // API clients are non-interactive — they can't reply \"yes\" to approve a plan,\n // so auto-promote awaiting_approval → executing for the 'api' channel.\n // Interactive channels (cli, webchat, slack, etc.) keep the approval gate.\n if (planned.stage === 'awaiting_approval' && channel === 'api') {\n logger.info(COMPONENT, `[Deliberation] api channel — auto-approving plan (no interactive client)`);\n const approved = handleApproval(session.id, true);\n if (approved) planned = approved;\n }\n\n if (planned.stage === 'awaiting_approval' && planned.planMarkdown) {\n const content = planned.planMarkdown;\n addMessage(session, 'assistant', '[DELIBERATION] ' + content, { model: config.agent.model, tokenCount: 0 });\n return {\n content,\n sessionId: session.id,\n toolsUsed: ['deliberation'],\n tokenUsage: planned?.tokenUsage || { prompt: 0, completion: 0, total: 0 },\n model: config.agent.model,\n durationMs: Date.now() - startTime,\n // Signal to UI that this response needs approve/deny before execution\n pendingApproval: true,\n };\n } else if (planned.stage === 'executing') {\n const executed = await executePlan(planned, config);\n const content = formatPlanResults(executed);\n // Collect tools used across all plan task results\n const planToolsUsed = new Set<string>(['deliberation']);\n for (const r of executed.results) {\n if (r.result) {\n // Extract tool names from result text patterns like \"[ToolRunner] Executing tool: X\"\n const toolMatches = r.result.match(/\\btool[_\\s]?(?:call|use|exec)[^:]*:\\s*(\\w+)/gi);\n if (toolMatches) toolMatches.forEach(m => { const t = m.split(':').pop()?.trim(); if (t) planToolsUsed.add(t); });\n }\n }\n addMessage(session, 'assistant', '[DELIBERATION] ' + content, { model: config.agent.model, tokenCount: 0 });\n return { content, sessionId: session.id, toolsUsed: [...planToolsUsed], tokenUsage: planned?.tokenUsage || { prompt: 0, completion: 0, total: 0 }, model: config.agent.model, durationMs: Date.now() - startTime };\n } else {\n // Planning failed, fall through to normal processing\n logger.warn(COMPONENT, `Deliberation failed, falling through: ${planned.error || 'unknown error'}`);\n }\n }\n }\n\n // ── Pre-routing: intercept queries with known data tools ──\n // Some queries (weather, etc.) have dedicated APIs that return accurate data.\n // Pre-fetch this data and inject it so the LLM doesn't hallucinate.\n // Skip for 'deliberation' channel — task step prompts contain goal text that\n // matches keywords (e.g. \"weather\") but aren't actual weather queries.\n let preRoutedContext = '';\n // v5.4.3 fix: Skip weather pre-routing for widget-creation requests.\n // \"Create a weather widget\" should NOT get static weather data injected —\n // the widget needs to fetch live data itself via titan.fetch() inside the\n // _____react gate. Pre-routing \"Do NOT call tools\" would suppress gallery\n // search + gate creation and make the model emit prose instead.\n const isWidgetRequest = /\\b(?:create|add|make|build|spawn|generate)\\b.{0,40}\\b(?:widget|panel|dashboard)\\b/i.test(message);\n // Stub-mode evals must exercise deterministic tool routing, not live wttr.in pre-routing.\n const stubProviderActive = process.env.TITAN_STUB_PROVIDER === '1';\n if (!stubProviderActive && channel !== 'deliberation' && !isWidgetRequest && /\\b(?:weather|forecast|temperature)\\b/i.test(message)) {\n // Split on \"and\"/\"also\"/\",\"/\"&\" FIRST to separate multiple locations\n const segments = message.split(/\\b(?:and|also|&)\\b|,/i).filter(s => /\\b(?:weather|forecast|temperature|\\d{5})\\b/i.test(s) || /[A-Z][a-z]+/.test(s));\n const locations: string[] = [];\n for (const seg of segments.length > 0 ? segments : [message]) {\n const loc = seg.toLowerCase()\n .replace(/\\b(weather|forecast|temperature|temp|today|tonight|tomorrow|this week|current|right now|conditions|for|in|at|the|what|is|whats|what's|check|get|show|me|please|how|hot|cold|also|can you)\\b/g, '')\n .replace(/[?,!.]/g, '').trim().replace(/\\s+/g, ' ');\n if (loc.length >= 2) locations.push(loc);\n }\n // Fetch all locations in parallel for speed\n const weatherResults = await Promise.allSettled(locations.map(async (loc) => {\n const resp = await fetch(`https://wttr.in/${encodeURIComponent(loc)}?format=j1`, {\n headers: { 'User-Agent': 'TITAN/1.0' },\n signal: AbortSignal.timeout(12000),\n });\n if (!resp.ok) return null;\n const d = await resp.json() as Record<string, unknown>;\n const cur = (d.current_condition as Array<Record<string, unknown>>)?.[0];\n const area = (d.nearest_area as Array<Record<string, unknown>>)?.[0];\n const day = (d.weather as Array<Record<string, unknown>>)?.[0];\n if (!cur) return null;\n const areaName = area\n ? `${(area.areaName as Array<{value: string}>)?.[0]?.value}, ${(area.region as Array<{value: string}>)?.[0]?.value}`\n : loc;\n const desc = (cur.weatherDesc as Array<{value: string}>)?.[0]?.value || '';\n const astro = (day?.astronomy as Array<Record<string, string>>)?.[0];\n const hourly = day?.hourly as Array<Record<string, unknown>> | undefined;\n let part = `Weather for ${areaName}: ${cur.temp_F}°F (feels ${cur.FeelsLikeF}°F), ${desc}, Humidity ${cur.humidity}%, Wind ${cur.windspeedMiles} mph ${cur.winddir16Point}, UV ${cur.uvIndex}`;\n if (day) part += `, High ${day.maxtempF}°F, Low ${day.mintempF}°F`;\n if (astro) part += `, Sunrise ${astro.sunrise}, Sunset ${astro.sunset}`;\n if (hourly) {\n const evening = hourly.find(h => h.time === '2100');\n if (evening) {\n const eDesc = (evening.weatherDesc as Array<{value: string}>)?.[0]?.value || '';\n part += ` | Tonight: ${evening.tempF}°F, ${eDesc}, Wind ${evening.windspeedMiles} mph, ${evening.chanceofrain}% rain`;\n }\n }\n return part;\n }));\n const weatherParts = weatherResults\n .filter((r): r is PromiseFulfilledResult<string> => r.status === 'fulfilled' && r.value !== null)\n .map(r => r.value);\n if (weatherParts.length > 0) {\n preRoutedContext = `\\n\\n[REAL-TIME WEATHER DATA — present this data to the user in a nicely formatted response. Do NOT call any tools for weather — use ONLY the data below.]\\n${weatherParts.join('\\n')}`;\n logger.info(COMPONENT, `Pre-routed weather for ${weatherParts.length} location(s): [${locations.join(', ')}]`);\n }\n }\n\n // Add user message to session history\n addMessage(session, 'user', message);\n\n // Initialize graph memory (lazy, only loads once)\n initGraph();\n\n // Auto-record user message to knowledge graph (fire-and-forget)\n addEpisode(`[${channel}/${userId}] ${message}`, channel).catch(e => logger.debug('Agent', `Background op failed: ${(e as Error).message}`));\n\n // v5.0.2: Safety pre-check — must be evaluated before prompt building\n // so both voice and full paths can strip tools for dangerous requests.\n const dangerous = isDangerous(message);\n\n // Build context — voice gets a compact prompt (~500 tokens vs ~3000+)\n let systemPrompt: string;\n if (voiceFastPath) {\n // Build memory context FIRST — prepend to prompt so model sees it before rules\n const voiceGraphCtx = message ? await getGraphContext(message) : '';\n const voiceLearningCtx = getLearningContext();\n const voiceStrategyHint = message ? getStrategyHints(message) : null;\n const voiceTeachingCtx = getTeachingContext();\n const voicePersonalCtx = buildPersonalContext();\n const voiceMemories = await searchMemories('preference');\n const voiceMemCtx = voiceMemories.length > 0\n ? voiceMemories.map((m: { key: string; value: string }) => `- ${m.key}: ${m.value}`).join('\\n')\n : '';\n let hindsightCtx: string | null = null;\n if (!voiceStrategyHint && message) {\n try { hindsightCtx = await getHindsightHints(message); } catch { /* unavailable */ }\n }\n\n // Memory goes BEFORE persona — models attend to the beginning of prompts\n let memoryBlock = '';\n if (voiceGraphCtx || voiceLearningCtx || voicePersonalCtx) {\n memoryBlock += `## IMPORTANT — Your Memories\\nThe following are things you remember from past conversations. Treat them as your own memories. When asked about past topics, reference these directly. Do NOT say \"I do not recall\" if the answer is in your memories below.\\n\\n`;\n if (voiceGraphCtx) memoryBlock += `${voiceGraphCtx}\\n\\n`;\n if (voiceLearningCtx) memoryBlock += `Learned facts:\\n${voiceLearningCtx}\\n\\n`;\n if (voiceStrategyHint) memoryBlock += `Strategy: ${voiceStrategyHint}\\n\\n`;\n if (hindsightCtx) memoryBlock += `Cross-session: ${hindsightCtx}\\n\\n`;\n if (voiceTeachingCtx) memoryBlock += `Teaching: ${voiceTeachingCtx}\\n\\n`;\n if (voicePersonalCtx) memoryBlock += `Personal: ${voicePersonalCtx}\\n\\n`;\n if (voiceMemCtx) memoryBlock += `Preferences:\\n${voiceMemCtx}\\n\\n`;\n }\n\n const voiceBase = buildVoiceSystemPrompt(config);\n systemPrompt = voiceBase + '\\n\\n' + memoryBlock;\n if (preRoutedContext) systemPrompt += preRoutedContext;\n // Reinforce persona at the very end (models weight beginning and end most)\n const ttsVoice2 = ((config.voice as Record<string, unknown>) || {}).ttsVoice as string || '';\n const ttsEngine2 = ((config.voice as Record<string, unknown>) || {}).ttsEngine as string || '';\n if (ttsVoice2 === 'andrew' && ttsEngine2 === 'f5-tts') {\n systemPrompt += '\\n\\nREMINDER: Your communication style is always formal, measured, and inspired by Andrew Martin. Say \"Sir\". No contractions. Never say \"I am an AI assistant\" — you are TITAN.';\n }\n logger.info('Agent', `Voice prompt: ${systemPrompt.length} chars, memory: ${memoryBlock.length} chars, graph: ${voiceGraphCtx.length} chars`);\n if (dangerous) {\n systemPrompt += '\\n\\n⚠️ SAFETY OVERRIDE: The user message contains a potentially destructive or privileged command. You MUST refuse to execute it. Respond with a polite refusal explaining why. Do NOT use any tools for this request.';\n }\n } else {\n systemPrompt = await buildSystemPrompt(config, message, overrides?.agentId, 'full', overrides?.sessionId);\n if (overrides?.systemPrompt) systemPrompt = overrides.systemPrompt + '\\n\\n' + systemPrompt;\n if (preRoutedContext) systemPrompt += preRoutedContext;\n }\n\n // Identity intercept — if the user asked anything that looks like\n // \"what model are you?\", append a hard fact-sheet at the END of the\n // system prompt (recency-position wins under U-shaped attention).\n // This is the load-bearing fix for the long-running \"model claims to\n // be Claude when it's actually deepseek-v4-flash:cloud\" bug. The\n // intercept runs for both voice and full paths.\n const identityFactSheet = maybeBuildIdentityFactSheet(message);\n if (identityFactSheet) {\n systemPrompt += '\\n\\n' + identityFactSheet;\n }\n\n // Task-aware enforcement injection — strengthen tool-use requirements based on message intent\n // Also tracks whether to force tool_choice on round 0 via the API\n // Skip for voice — voice uses a compact prompt and doesn't need injection bloat\n let taskEnforcementActive = false;\n\n if (voiceFastPath) {\n // Voice skips task enforcement — compact prompt handles everything\n } else if (pipelineTaskEnforcement) {\n // Pipeline-specific task enforcement — replaces scattered regex heuristics\n systemPrompt += `\\n\\n${pipelineTaskEnforcement}`;\n taskEnforcementActive = true;\n logger.info(COMPONENT, `[Pipeline:${pipelineType}] Task enforcement injected`);\n } else {\n // Continuation injection: short messages like \"CONFIRM\", \"yes\", \"all of them\" lose all task\n // context after system prompt compression. Re-inject the task context so the model knows\n // exactly what it was doing and can continue without re-planning or going rogue.\n const isContinuation = /^(confirm|yes|ok|okay|do it|go|go ahead|proceed|continue|approve|sure|yep|yup|all of them?|all steps?|\\d+)\\s*[.!]?$/i.test(message.trim());\n if (isContinuation) {\n const sessionMsgs = getContextMessages(session);\n const recentAssistant = sessionMsgs\n .filter(m => m.role === 'assistant')\n .slice(-2)\n .map(m => m.content.slice(0, 600))\n .join('\\n---\\n');\n if (recentAssistant) {\n systemPrompt += `\\n\\n[TASK CONTINUATION] The user replied \"${message}\" to confirm/continue a pending action. You were in the middle of a task. Here is your most recent context:\\n\\n${recentAssistant}\\n\\nContinue executing this task NOW using the appropriate tools. Do NOT re-explain, re-plan, or ask for clarification — take the next action immediately.`;\n taskEnforcementActive = true;\n logger.info(COMPONENT, `[TaskContinuation] Injected context for short confirmation: \"${message}\"`);\n }\n }\n\n // Safety pre-check: dangerous commands must be refused even if they match\n // task enforcement patterns below. Safety ALWAYS wins over task enforcement.\n if (dangerous) {\n systemPrompt += '\\n\\n⚠️ SAFETY OVERRIDE: The user message contains a potentially destructive or privileged command. You MUST refuse to execute it. Respond with a polite refusal explaining why. Do NOT use any tools for this request.';\n // Do NOT set taskEnforcementActive — we want the model to respond with text,\n // not be forced to call tools. Tools will be stripped below.\n }\n\n if (!dangerous && /\\b(write|save|create|generate|output|produce|make)\\b.{0,60}\\b(file|doc|report|md|txt|json|csv|log|notes?|summary|readme)\\b/i.test(message)) {\n // v5.4.3 fix: Skip file-writing enforcement for widget creation requests.\n // Widget instructions already tell the model to use _____react gates and\n // explicitly prohibit write_file. Adding file-writing enforcement creates\n // a conflict the model resolves by choosing the more familiar write_file\n // instead of the gate protocol. Only emit this when no widget/create was\n // requested.\n const isWidgetReq2 = /\\b(?:create|add|make|build|spawn|generate)\\b.{0,40}\\b(?:widget|panel|dashboard)\\b/i.test(message);\n if (!isWidgetReq2) {\n systemPrompt += '\\n\\nWhen the user asks you to write or create a file, you MUST use write_file or edit_file to save it. Do NOT just type the content in your reply — the user expects an actual file on disk.';\n taskEnforcementActive = true;\n }\n }\n if (!dangerous && /\\b(read|show|display|view|open|cat|get)\\b.{0,60}\\b(file|content|text|readme|md|txt|json|csv|log|code|source)\\b/i.test(message) && !/\\b(?:write|save|create|edit|modify)\\b/i.test(message)) {\n systemPrompt += '\\n\\nWhen the user asks you to read or show a file, you MUST use read_file to fetch its contents. Do NOT use shell or other tools — read_file is the correct tool for viewing file contents.';\n taskEnforcementActive = true;\n }\n if (!dangerous && /\\b(research|search|find|look ?up|what is|what are|current|latest|today|news|price|stock|score|update)\\b/i.test(message) && !/weather/i.test(message)) {\n systemPrompt += '\\n\\nWhen the user asks for current information, news, or research, you MUST search the web to get up-to-date results. Do NOT rely only on what you already know.';\n taskEnforcementActive = true;\n }\n if (!dangerous && /\\b(run|execute|install|check|build|compile|start|stop|restart|deploy|test)\\b.{0,40}\\b(command|script|package|service|server|process|app)\\b/i.test(message)) {\n systemPrompt += '\\n\\nWhen the user asks you to run a command, install something, or start/stop a service, you MUST use the shell tool to actually execute it. Do NOT just describe what the command would do.';\n taskEnforcementActive = true;\n }\n if (/\\b(fix|change|modify|update|refactor|implement|add|remove|replace|uncomment|activate|enable|rewrite|patch|upgrade)\\b.{0,80}\\b(code|function|file|class|method|module|component|logic|bug|feature|session|title|tool|test)\\b/i.test(message)) {\n systemPrompt += '\\n\\nWhen editing code: 1) read the relevant file first with read_file, 2) make the change with edit_file (for a small change) or write_file — do NOT use shell/sed/echo/cat to edit files; edit_file is the correct, verifiable tool, 3) run tests to verify, 4) report what you changed. Do NOT stop after reading — actually save your changes.';\n taskEnforcementActive = true;\n }\n // v7.0 honesty rule (user-test T6 found a fabricated \"I've scheduled a\n // reminder\" with zero tools called — trust-destroying). Steering applies\n // when the ask implies a future/side-effect action.\n if (/\\b(remind|schedule|every (day|week|morning|hour)|later|tomorrow|at \\d|recurring|cron|alarm|automat)/i.test(message)) {\n systemPrompt += '\\n\\nHONESTY RULE: never claim you scheduled, set a reminder, automated, sent, or posted something unless you ACTUALLY called the corresponding tool in this turn (the `reminder` tool for one-time reminders — action=set with message + ISO `when`; the `cron` tool for recurring jobs). If you cannot do it with your tools, say so plainly instead of pretending.';\n }\n // v5.0.2 / v7.0: detect system-widget shortcuts via the shared, model-agnostic\n // detector (src/agent/systemWidgets.ts). It fires for \"show the training\n // dashboard\" AND short commands like \"show backup\" / \"show recipes\" (the old\n // inline check demanded a generic widget-noun, so those never matched). When\n // matched we instruct the model to emit the gate — and post-loop we GUARANTEE\n // the gate even if the model ignores this (see the deterministic emit below).\n const matchedWidget = detectSystemWidget(message);\n if (matchedWidget && !taskEnforcementActive) {\n systemPrompt += `\\n\\nThe user is asking about ${matchedWidget.name}. You MUST call gallery_search for \"${matchedWidget.source}\" FIRST to find the widget template, then call gallery_get to fetch it, and emit it through the _____widget gate as JSON with format \"system\":\\n\\n${buildSystemWidgetGate(matchedWidget)}\\n\\nDo NOT just describe it — actually create the widget on the canvas.`;\n taskEnforcementActive = true;\n }\n // Widget / canvas gallery enforcement — user wants a widget built on the canvas\n // v5.4.3 fix: Removed !taskEnforcementActive guard. The generic file-writing\n // pattern above (line ~1289) matched \"Create a...\" and suppressed widget\n // instructions, causing the model to emit prose instead of _____react gates.\n // Widget creation is a first-class canvas concept and must always fire for\n // widget-matching messages, regardless of whether other task enforcement\n // also triggered (the instructions are non-conflicting).\n if (/\\b(?:create|add|make|build|spawn|generate|get|fetch|find|search|show|display|give me|want|need)\\b.{0,60}\\b(?:widget|panel|canvas|gallery|clock|timer|chart|graph|map|calendar|todo|list|counter|dashboard)\\b/i.test(message)) {\n systemPrompt += '\\n\\nThe user wants a widget on the canvas. You CAN create it yourself — you do NOT need the user to share files or an existing project. Your job is to BUILD the widget, not ask for files.\\n\\nMANDATORY steps:\\n1. Call gallery_search with the user\\'s intent (e.g. \"weather widget\").\\n2. If a template matches (score >= 6), call gallery_get with its id and fill placeholders.\\n3. Emit the returned source through the _____react gate.\\n4. If no template matches, write the React component yourself and emit it through _____react.\\n\\nCRITICAL: Widgets are NOT files. Do NOT use write_file, edit_file, or any file tools. Widget code is rendered directly on the canvas via the _____react gate. Do NOT describe the widget, do NOT save it to a file, do NOT say \"I don\\'t see an existing canvas\" — just CREATE it with the gate.';\n taskEnforcementActive = true;\n }\n // Deliberation step enforcement — task prompts from executePlan() should\n // always get tool-routing rules because they are synthetic action prompts\n if (channel === 'deliberation' && !taskEnforcementActive) {\n systemPrompt += '\\n\\nYou are executing a step in a structured plan. Use tool calls to do real work: read_file for reading code, edit_file for making changes.' +\n '- To write files: use write_file (NOT shell with echo/printf redirects)\\n' +\n '- To fetch URLs: use web_fetch (NOT shell with curl/wget)\\n' +\n '- To search: use web_search (NOT shell with curl to search engines)\\n' +\n '- Shell is for running builds, tests, and commands that have no dedicated tool.\\n' +\n 'Execute this step NOW. Do not describe what you would do — call the tools.';\n taskEnforcementActive = true;\n logger.info(COMPONENT, `[TaskEnforcement] Deliberation step enforcement injected`);\n }\n\n } // end !voiceFastPath\n\n // Memory nudge — every 20 messages, remind agent to review and update its knowledge\n if (session.messageCount > 0 && session.messageCount % 20 === 0 && !voiceFastPath) {\n systemPrompt += '\\n\\n[MEMORY NUDGE] You have had 20+ messages in this session. If the user has shared preferences, facts, or important details, use the memory tool to save them for future sessions. Review your existing memories for accuracy.';\n }\n\n // Voice mode prompt is handled above via buildVoiceSystemPrompt() — no append needed\n // Voice sessions: limit context to last 6 messages (3 turns) to prevent\n // multi-turn degradation with local models. Long contexts cause Qwen to\n // hallucinate system prompts and get stuck in tool loops.\n const historyMessages = voiceFastPath\n ? getContextMessages(session, 6)\n : getContextMessages(session);\n const tools = getToolDefinitions();\n\n // ── Learning feedback: inject reliability tags into tool descriptions ──\n const toolWarnings = getToolWarnings();\n if (Object.keys(toolWarnings).length > 0) {\n for (const tool of tools) {\n const warning = toolWarnings[tool.function.name];\n if (warning) {\n tool.function.description = `${warning} ${tool.function.description}`;\n }\n }\n }\n\n // F3: Inject procedural memory (Hermes skill recall) into system prompt\n let enrichedSystemPrompt = systemPrompt;\n try {\n const { getProceduralContext } = await import('../skills/proceduralMemory.js');\n const proceduralContext = getProceduralContext(message);\n if (proceduralContext) {\n enrichedSystemPrompt += '\\n\\n' + proceduralContext;\n logger.debug('Agent', `[ProceduralMemory] Injected skill context into system prompt`);\n }\n } catch { /* proceduralMemory not available — non-critical */ }\n\n // v5.5.24: Persona profile appendix. Resolved here (early) so the\n // system prompt carries the persona context AND so the tool filter\n // pass below can use the same persona without re-resolving. When\n // personas.enabled=false, resolveActivePersona returns null and this\n // block is a no-op.\n const { resolveActivePersona: __resolvePersona } = await import('./personaProfiles.js');\n let earlyPersona = __resolvePersona({ channel });\n // v5.5.27: Persona A/B cohort override. After the baseline resolver\n // picks a persona, check for an active canary that uses it as\n // baseline. If the (cohortId, sessionId) hash falls in the candidate\n // bucket, swap to candidate and remember the cohort role for the\n // post-response outcome record. Disabled when personas.rollout is off.\n let __cohortAssignment: { cohortId: string; role: 'baseline' | 'candidate' } | null = null;\n if (earlyPersona) {\n try {\n const { pickCohortAssignment } = await import('./personaRollout.js');\n const assignment = pickCohortAssignment(earlyPersona.id, { sessionId: session.id, channel });\n if (assignment) {\n __cohortAssignment = { cohortId: assignment.cohortId, role: assignment.role };\n if (assignment.role === 'candidate' && assignment.personaId !== earlyPersona.id) {\n const { resolveActivePersona } = await import('./personaProfiles.js');\n const candidate = resolveActivePersona({ forceId: assignment.personaId });\n if (candidate) {\n logger.info(COMPONENT, `[Cohort:${assignment.cohortId}] swap ${earlyPersona.id} → ${candidate.id}`);\n earlyPersona = candidate;\n }\n }\n }\n } catch (err) {\n logger.warn(COMPONENT, `cohort assignment skipped: ${(err as Error).message}`);\n }\n }\n if (earlyPersona?.systemPromptAppendix) {\n enrichedSystemPrompt += earlyPersona.systemPromptAppendix;\n logger.info(COMPONENT, `[Persona:${earlyPersona.id}] system prompt appendix injected`);\n }\n\n const messages: ChatMessage[] = [\n { role: 'system', content: enrichedSystemPrompt },\n ...historyMessages,\n ];\n\n let totalPromptTokens = 0;\n let totalCompletionTokens = 0;\n const toolsUsed: string[] = [];\n const attemptedTools: string[] = [];\n const orderedToolSequence: string[] = []; // Preserves execution order with repeats\n let finalContent = '';\n let modelUsed = config.agent.model;\n\n // Self-heal config\n const selfHealEnabled = (config.agent as Record<string, unknown>).selfHealEnabled !== false;\n\n // ── Checkpoint: track if budget was exhausted ──\n let budgetExhausted = false;\n\n // ── Cost optimizer: smart model routing ─────────────────\n let { model: activeModel, reason: routingReason } = routeModel(message, config.agent.model);\n if (overrides?.model) activeModel = overrides.model;\n // Voice model override: use a faster model for voice chat (lower latency)\n if (voiceFastPath && (config.voice as Record<string, unknown>)?.model) {\n activeModel = (config.voice as Record<string, unknown>).model as string;\n routingReason = 'voice model override';\n }\n // Session override has highest priority (set via /model command)\n if (session.modelOverride) {\n activeModel = session.modelOverride;\n routingReason = 'session override (/model)';\n }\n if (activeModel !== config.agent.model) {\n logger.info(COMPONENT, `Cost router: ${config.agent.model} → ${activeModel} (${routingReason})`);\n }\n modelUsed = activeModel;\n\n let activeTools = tools;\n // v6.4.4: declare allToolsBackup + toolSearchEnabled here so they're\n // accessible in BOTH the explicitAllowList short-circuit branch and the\n // filter-chain branch. allToolsBackup is used downstream by runAgentLoop\n // for tool_search expansion fallback; toolSearchEnabled gates expansion.\n let allToolsBackup: typeof activeTools = activeTools;\n let toolSearchEnabled = true;\n\n // v6.4.4: explicit cron/override allowlist short-circuit. When the\n // caller (typically a cron job) hands us an explicit list of tool\n // names, bypass the persona / small-model / brain / toolSearch /\n // dangerous filter chain entirely. The caller knows what tools the\n // task needs — we should not be second-guessing it. This fixes the\n // fb-* cron hallucination bug where activeTools reached the agent\n // loop as 0 despite the prompt explicitly saying \"Call fb_post\".\n const explicitAllowList = overrides?.allowedTools;\n if (explicitAllowList && explicitAllowList.length > 0) {\n const allow = new Set(explicitAllowList);\n const before = activeTools.length;\n activeTools = tools.filter(t => allow.has(t.function.name));\n allToolsBackup = activeTools;\n logger.info(COMPONENT, `[AllowedTools] override bound ${activeTools.length}/${explicitAllowList.length} requested tools (filter chain bypassed): [${activeTools.map(t => t.function.name).join(', ')}] from pool of ${before}`);\n const missing = explicitAllowList.filter(name => !activeTools.some(t => t.function.name === name));\n if (missing.length > 0) {\n logger.warn(COMPONENT, `[AllowedTools] caller asked for tools that aren't registered: [${missing.join(', ')}]`);\n }\n } else {\n\n // v5.5.24: Persona-level tool filter. earlyPersona was already\n // resolved when we built enrichedSystemPrompt above; reuse that\n // resolution rather than calling the resolver twice per request.\n if (earlyPersona) {\n const { applyPersonaToolFilter } = await import('./personaProfiles.js');\n const before = activeTools.length;\n activeTools = applyPersonaToolFilter(activeTools, earlyPersona);\n if (activeTools.length !== before) {\n logger.info(COMPONENT, `[Persona:${earlyPersona.id}] tools ${before} → ${activeTools.length}`);\n }\n }\n\n // Context-fit tool tiers (v7.1 \"truly model agnostic\") — bind a toolset\n // sized to the model's REAL context window (family registry + config\n // override + ceilings learned from live backend 400s), instead of a\n // hardcoded name list. Measured: full core ≈ 61KB of schemas + 7.5KB\n // system prompt — fine at 64K+, fatal at 16K, sluggish prefill on\n // bandwidth-bound local GPUs. Tiers:\n // tiny (<12K ctx or known-tiny name): 6 primitives\n // small (<48K ctx): 14 lean essentials + minimal prompt mode\n // full (>=48K): the standard 30-tool core (toolSearch compact mode)\n const SMALL_MODEL_PATTERNS = ['llama3.2', 'llama3.1:8b', 'phi', 'gemma:2b', 'qwen3.5:4b', 'tinyllama', 'dolphin3'];\n const nameIsTiny = SMALL_MODEL_PATTERNS.some(p => activeModel.toLowerCase().includes(p));\n const { getModelCapabilitiesEx } = await import('../providers/modelCapabilities.js');\n const bareModel = activeModel.includes('/') ? activeModel.slice(activeModel.indexOf('/') + 1) : activeModel;\n const capsA = getModelCapabilitiesEx(activeModel);\n const capsB = getModelCapabilitiesEx(bareModel);\n const best = capsA.caps.contextWindow <= capsB.caps.contextWindow ? capsA : capsB;\n const modelCtxWindow = best.caps.contextWindow;\n // A fallback guess must never shrink the toolset — only measured/known\n // ceilings (static, config, family, learned-from-400s) trigger tiering.\n const ctxIsKnown = best.source !== 'fallback';\n const contextTier: 'tiny' | 'small' | 'full' =\n nameIsTiny || (ctxIsKnown && modelCtxWindow < 12_000) ? 'tiny'\n : ctxIsKnown && modelCtxWindow < 48_000 ? 'small'\n : 'full';\n const isSmallModel = contextTier !== 'full';\n if (contextTier === 'tiny') {\n const CORE_TOOL_NAMES = ['shell', 'read_file', 'write_file', 'edit_file', 'list_dir', 'memory'];\n const coreTools = activeTools.filter(t => CORE_TOOL_NAMES.includes(t.function.name));\n logger.info(COMPONENT, `[ContextFit] tier=tiny (ctx=${modelCtxWindow}) — ${activeTools.length} → ${coreTools.length} tools for ${activeModel}`);\n activeTools = coreTools;\n } else if (contextTier === 'small') {\n const LEAN_TOOL_NAMES = [\n 'shell', 'read_file', 'write_file', 'edit_file', 'list_dir',\n 'web_search', 'browse_url', 'memory', 'create_widget',\n 'gallery_search', 'gallery_get', 'cron', 'reminder', 'tool_search',\n ];\n const leanTools = activeTools.filter(t => LEAN_TOOL_NAMES.includes(t.function.name));\n if (leanTools.length > 0) {\n logger.info(COMPONENT, `[ContextFit] tier=small (ctx=${modelCtxWindow}) — ${activeTools.length} → ${leanTools.length} tools for ${activeModel}`);\n activeTools = leanTools;\n }\n }\n\n // ── Brain: intelligent tool pre-filtering ──────────────────\n if (!voiceFastPath && !isSmallModel && isBrainAvailable()) {\n const brainFiltered = await brainSelectTools(message, activeTools);\n if (brainFiltered.length > 0 && brainFiltered.length < activeTools.length) {\n logger.info(COMPONENT, `[Brain] Filtered: ${activeTools.length} → ${brainFiltered.length} tools`);\n activeTools = brainFiltered;\n }\n }\n\n // ── Tool Search: compact tool mode ──────────────────────────\n // Send only core tools + tool_search to the LLM instead of all 80+.\n // The LLM calls tool_search to discover additional tools as needed.\n const toolSearchConfig = (config as Record<string, unknown>).toolSearch as {\n enabled?: boolean;\n coreTools?: string[];\n } | undefined;\n // v6.4.4: now assignments, not declarations — variables are hoisted above\n // the explicitAllowList short-circuit so both branches can reach them.\n toolSearchEnabled = toolSearchConfig?.enabled ?? true;\n allToolsBackup = activeTools;\n\n // Always ensure pipeline tools are in the active set, even when toolSearch is disabled\n if (pipelineEnsureTools.length > 0 && !(toolSearchEnabled && !isSmallModel && activeTools.length > 12)) {\n const activeNames = new Set(activeTools.map(t => t.function.name));\n const missing = pipelineEnsureTools.filter(name => !activeNames.has(name));\n if (missing.length > 0) {\n const rescued = allToolsBackup.filter(t => missing.includes(t.function.name));\n activeTools.push(...rescued);\n if (rescued.length > 0) {\n logger.info(COMPONENT, `[Pipeline:${pipelineType}] Ensured ${rescued.length} tools (no-compact): [${rescued.map(t => t.function.name).join(', ')}]`);\n }\n }\n }\n\n if (toolSearchEnabled && !isSmallModel && activeTools.length > 12) {\n // Voice gets a minimal tool set for speed (fewer tool schemas = less prompt tokens)\n const VOICE_CORE_TOOLS = ['shell', 'web_search', 'weather', 'memory', 'ha_control', 'ha_devices', 'ha_status', 'ha_setup', 'tool_search'];\n // Use config coreTools only if non-empty; otherwise fall back to DEFAULT_CORE_TOOLS\n const configCoreTools = toolSearchConfig?.coreTools;\n const effectiveCoreTools = (configCoreTools && configCoreTools.length > 0) ? configCoreTools : DEFAULT_CORE_TOOLS;\n // Pipeline tools: merge pipeline-specific tools into the core set\n const pipelineMerged = pipelineEnsureTools.length > 0\n ? [...new Set([...effectiveCoreTools, ...pipelineEnsureTools])]\n : effectiveCoreTools;\n const coreNames = new Set(voiceFastPath ? VOICE_CORE_TOOLS : pipelineMerged);\n activeTools = activeTools.filter(t => coreNames.has(t.function.name));\n // If pipeline tools were requested but not found in activeTools, pull from backup\n if (pipelineEnsureTools.length > 0) {\n const activeNames = new Set(activeTools.map(t => t.function.name));\n const missing = pipelineEnsureTools.filter(name => !activeNames.has(name));\n if (missing.length > 0) {\n const rescued = allToolsBackup.filter(t => missing.includes(t.function.name));\n activeTools.push(...rescued);\n if (rescued.length > 0) {\n logger.info(COMPONENT, `[Pipeline:${pipelineType}] Rescued ${rescued.length} tools: [${rescued.map(t => t.function.name).join(', ')}]`);\n }\n }\n }\n logger.info(COMPONENT, `[ToolSearch] Compact mode: ${allToolsBackup.length} → ${activeTools.length} tools (${allToolsBackup.length - activeTools.length} discoverable via tool_search)`);\n }\n\n // v5.0.2: Safety override — strip all tools so the model CANNOT call anything\n // dangerous. The safety message in systemPrompt tells it to refuse.\n if (dangerous) {\n activeTools = [];\n logger.info(COMPONENT, '[Safety] Stripped all tools — dangerous command detected');\n }\n\n } // v6.4.4: end of !explicitAllowList branch (persona/brain/toolSearch/dangerous filters)\n\n // ── Stall detector: configure for autonomy mode + start heartbeat ──\n setAutonomousMode(isAutonomous);\n heartbeat(session.id);\n\n // ── Orchestration: check if task benefits from sub-agent delegation ──\n const autoDelegate = (subAgentConfig as Record<string, unknown> | undefined)?.autoDelegate !== false;\n if (!voiceFastPath && isAutonomous && autoDelegate && channel !== 'deliberation' && message.split(/\\s+/).length >= 8) {\n try {\n const delegationPlan = await analyzeForDelegation(message);\n if (delegationPlan && delegationPlan.shouldDelegate && delegationPlan.tasks.length >= 2) {\n logger.info(COMPONENT, `Orchestrator: delegating to ${delegationPlan.tasks.length} sub-agents`);\n const orchResult = await executeDelegationPlan(delegationPlan!);\n if (orchResult.subResults.length > 0 && orchResult.subResults.some(r => r.success)) {\n messages.push({\n role: 'user',\n content: `[Sub-agent results for your request]\\n\\n${orchResult.content}\\n\\nSynthesize these results into a coherent response for the user.`,\n });\n }\n }\n } catch (err) {\n logger.warn(COMPONENT, `Orchestration failed, falling through: ${(err as Error).message}`);\n }\n }\n\n // ══════════════════════════════════════════════════════════════\n // v4.13: run ContextEngine plugin assemble hooks (topFacts,\n // memoryRetrieval, smartCompress) before the loop. Previously the\n // plugin system existed but nothing called runAssemble, so\n // top-facts + relevant graph/vector memories never reached the LLM.\n // Injects a system message per active plugin right before the last\n // user message. Errors inside a plugin are swallowed.\n // ══════════════════════════════════════════════════════════════\n try {\n const { getPlugins } = await import('../plugins/registry.js');\n const { runAssemble } = await import('../plugins/contextEngine.js');\n const plugins = getPlugins();\n if (plugins.length > 0) {\n const assembled = await runAssemble(plugins, messages, message);\n messages.splice(0, messages.length, ...assembled);\n }\n } catch (pluginErr) {\n logger.debug(COMPONENT, `plugin assemble failed: ${(pluginErr as Error).message}`);\n }\n\n // ══════════════════════════════════════════════════════════════\n // Agent Loop — Phase State Machine (Think/Act/Respond)\n // Replaces the old monolithic for-loop. See agentLoop.ts.\n // ══════════════════════════════════════════════════════════════\n const loopResult = await runAgentLoop({\n messages,\n activeTools,\n allToolsBackup,\n activeModel,\n config,\n sessionId: session.id,\n agentId: overrides?.agentId,\n channel,\n message,\n streamCallbacks,\n signal,\n isAutonomous,\n voiceFastPath,\n effectiveMaxRounds,\n taskEnforcementActive,\n reflectionEnabled,\n reflectionInterval,\n toolSearchEnabled,\n selfHealEnabled,\n smartExitEnabled: pipelineSmartExit,\n thinkingOverride: session.thinkingOverride,\n pipelineTerminalTools,\n completionStrategy: pipelineCompletionStrategy,\n pipelineType,\n minRounds: pipelineMinRounds,\n });\n\n // Unpack results\n finalContent = loopResult.content;\n toolsUsed.push(...loopResult.toolsUsed);\n attemptedTools.push(...loopResult.attemptedTools);\n orderedToolSequence.push(...loopResult.orderedToolSequence);\n modelUsed = loopResult.modelUsed;\n totalPromptTokens += loopResult.promptTokens;\n totalCompletionTokens += loopResult.completionTokens;\n budgetExhausted = loopResult.budgetExhausted;\n\n // Extract structured artifacts for deliberation inter-step context\n const toolArtifacts = extractToolArtifacts(loopResult.toolCallDetails);\n\n // ── Ralph Loop Verification ─────────────────────────────────\n // Outer completion check: did the task actually get done?\n // If the user asked to edit/write but no write tool was called,\n // re-run the agent loop ONE more time with a forced write instruction.\n if (isAutonomous && !voiceFastPath && !budgetExhausted && channel !== 'deliberation') {\n const verification = verifyTaskCompletion(message, toolsUsed, finalContent);\n if (!verification.complete) {\n logger.warn(COMPONENT, `[RalphLoop] Task incomplete: ${verification.reason}. Re-running with forced write.`);\n\n // Add the verification feedback and re-run with explicit tool guidance\n messages.push({ role: 'assistant', content: finalContent });\n messages.push({ role: 'user', content: [\n `[TASK INCOMPLETE] ${verification.reason}`,\n '',\n 'You have the file content from your previous read_file call.',\n 'Now call edit_file with these arguments:',\n ' - path: the file path you just read',\n ' - target: the exact string you want to replace (copy it from the file)',\n ' - replacement: the new string to put in its place',\n '',\n 'edit_file does a search-and-replace. You do NOT need to rewrite the whole file.',\n 'Just find a small section to change and replace it.',\n 'CALL edit_file NOW.',\n ].join('\\n') });\n\n const retryResult = await runAgentLoop({\n messages,\n activeTools,\n allToolsBackup,\n activeModel,\n config,\n sessionId: session.id,\n agentId: overrides?.agentId,\n channel,\n message,\n streamCallbacks,\n signal,\n isAutonomous,\n voiceFastPath,\n effectiveMaxRounds: 4, // Short budget for the retry\n taskEnforcementActive: true,\n reflectionEnabled: false,\n reflectionInterval: 99,\n toolSearchEnabled,\n selfHealEnabled: false,\n thinkingOverride: session.thinkingOverride,\n });\n\n if (retryResult.content) finalContent = retryResult.content;\n toolsUsed.push(...retryResult.toolsUsed);\n attemptedTools.push(...retryResult.attemptedTools);\n orderedToolSequence.push(...retryResult.orderedToolSequence);\n totalPromptTokens += retryResult.promptTokens;\n totalCompletionTokens += retryResult.completionTokens;\n\n logger.info(COMPONENT, `[RalphLoop] Retry complete. Tools used: [${retryResult.toolsUsed.join(', ')}]`);\n }\n }\n\n // Clean up stall detector for this session\n clearSession(session.id);\n // Hunt Finding #22 / #46 (2026-04-15): previously resetLoopDetection fired\n // at the end of EVERY turn, wiping cross-turn state. That meant the loop\n // breaker could only catch loops within a single turn — a model that\n // hit the same tool on turn 1, turn 2, turn 3 (across separate requests\n // in the same session) would never trip. The README claims TITAN\n // \"prevents runaway loops and wasted tokens\" which implies cross-turn\n // coverage. Fix: keep the session's loop state alive until the session\n // actually closes (session.ts:483 still wipes on session close).\n // resetLoopDetection(session.id); // INTENTIONALLY REMOVED — see comment above\n\n // Clear checkpoints on successful completion (no need to resume)\n if (!budgetExhausted) {\n import('./checkpoint.js').then(m => m.clearCheckpoints(session.id)).catch(e => logger.debug('Agent', `Background op failed: ${(e as Error).message}`));\n }\n\n // Active Learning: record strategy for future reference\n // v7.0 Muscle Memory: eval-channel runs (replay exams, test harness) must\n // never feed the learning loop — otherwise the exam's own replays re-enter\n // the mining pool and the system studies its own echo.\n if (toolsUsed.length > 0 && channel !== 'eval') {\n const success = !finalContent.toLowerCase().includes('error') && !budgetExhausted;\n recordStrategy(message, [...new Set(toolsUsed)], orderedToolSequence.length, success, orderedToolSequence);\n\n // Feedback loop: record outcome for matching strategies\n if (orderedToolSequence.length > 0) {\n recordStrategyOutcome(classifyTaskType(message), orderedToolSequence, success);\n }\n\n // Hindsight MCP: retain successful strategies as cross-session experience (fire-and-forget)\n // F2: Scoped to the agent's memory namespace so each agent builds its own episodic slice.\n if (success && orderedToolSequence.length > 0) {\n (async () => {\n let hsNs: string | undefined;\n if (overrides?.agentId && overrides.agentId !== 'default') {\n try {\n const { getAgentMemoryNamespace } = await import('./commandPost.js');\n hsNs = getAgentMemoryNamespace(overrides.agentId);\n } catch { /* fallthrough */ }\n }\n try { retainStrategy(classifyTaskType(message), orderedToolSequence, 1, message.slice(0, 200), hsNs); } catch { /* Hindsight unavailable */ }\n })().catch(() => { /* Hindsight unavailable */ });\n }\n }\n\n // ── Hallucination Guard: detect cloud models that claim tool use but never called tools ──\n // Cloud models sometimes describe tool actions (\"I wrote the file\", \"Output: ...\") without\n // actually making tool calls. This pollutes session memory with false action claims.\n //\n // SKIP for conversational tasks — chat/general pipelines don't require tool use,\n // so \"I did X\" in a conversational reply is normal, not a hallucination.\n const isConversationalTask = pipelineType === 'chat' || pipelineType === 'general'\n || channel?.endsWith('-admin') || channel === 'voice';\n const isCloudHallucination = toolsUsed.length === 0\n && taskEnforcementActive\n && !isConversationalTask\n && (activeModel.includes(':cloud') || activeModel.includes('-cloud'))\n && finalContent.length > 0\n && /(?:(?:I(?:'ve| have)?|successfully|done|completed|executed|created|wrote|saved|ran|output|result)[:\\s])/i.test(finalContent)\n && !/(?:I (?:can|could|would|will|should)|let me|I don't|I cannot|error|failed)/i.test(finalContent);\n\n if (isCloudHallucination) {\n logger.warn(COMPONENT, `[HallucinationGuard] Cloud model claimed action but toolsUsed is empty — sanitizing response`);\n finalContent = \"I wasn't able to finish that task — the connection to my brain hiccupped. Try asking again, or I can switch to my offline mode if you prefer.\";\n }\n\n // v7.0 model-agnostic guarantee: if the user's message was a system-widget\n // shortcut (\"show backup\") but no _____widget gate made it into the reply —\n // the model called a tool (e.g. backup_list) or answered in prose instead of\n // emitting the gate — emit it deterministically so the widget renders no\n // matter which model is driving. This is the canonical fix for the widget\n // gate NOT depending on per-model formatting adherence.\n {\n const sw = detectSystemWidget(message);\n if (sw && !finalContent.includes('_____widget')) {\n finalContent = finalContent ? `${finalContent}\\n\\n${buildSystemWidgetGate(sw)}` : buildSystemWidgetGate(sw);\n logger.info(COMPONENT, `[SystemWidgetShortcut] Deterministically emitted _____widget gate for ${sw.source}`);\n }\n }\n\n // v7.0 anti-fabrication backstop (user-test T6: \"I've scheduled a reminder\n // for Friday at 5 PM\" with zero scheduling tools called). If the reply\n // CLAIMS a reminder/schedule was set but no scheduling-capable tool ran\n // this turn, append an honest correction so the user is never silently\n // misled. Deterministic + model-agnostic; the prompt steering above tries\n // to prevent this — this guard guarantees it.\n // v7.1.x Organ 5 — self-critique (Reflexion). Off unless configured. Runs\n // BEFORE the honesty guard so the guard validates the caveated text.\n try {\n const agentCfg = config.agent as { selfCritique?: Partial<import('./selfCritique.js').SelfCritiqueConfig>; reliabilityMode?: boolean };\n const { resolveSelfCritique, runSelfCritique } = await import('./selfCritique.js');\n const scCfg = resolveSelfCritique(agentCfg.selfCritique, agentCfg.reliabilityMode === true);\n if (scCfg.enabled) {\n const sc = await runSelfCritique(scCfg, message, finalContent, [...new Set(toolsUsed)], modelUsed);\n if (sc.critiqued && sc.issues.length > 0) finalContent = sc.content;\n }\n } catch (e) { logger.debug(COMPONENT, `Self-critique wrapper skipped: ${(e as Error).message}`); }\n\n // v7.1.x verification wall: generalized from scheduling to ALL\n // side-effect claims (send/post/delete/deploy/write/schedule). If the\n // reply says it DID something but no capable tool ran, append an honest\n // correction. The single most important organ for honesty on any model.\n {\n const { applyHonestyGuard } = await import('./honestyGuard.js');\n const guarded = applyHonestyGuard(finalContent, toolsUsed);\n if (guarded.flagged.length > 0) {\n finalContent = guarded.content;\n logger.warn(COMPONENT, `[HonestyGuard] Reply claimed ${guarded.flagged.join(', ')} without a capable tool — appended correction`);\n }\n }\n\n // Save assistant response to session\n addMessage(session, 'assistant', finalContent, {\n model: modelUsed,\n tokenCount: totalCompletionTokens,\n });\n\n // Auto-record agent response to knowledge graph (fire-and-forget, skip short/error responses)\n if (finalContent.length > 50 && !finalContent.startsWith('⚠️')) {\n addEpisode(`[TITAN → ${channel}/${userId}] ${finalContent.slice(0, 500)}`, 'agent').catch(e => logger.debug('Agent', `Background op failed: ${(e as Error).message}`));\n }\n\n // Record usage\n const { provider: providerName } = { provider: modelUsed.split('/')[0] || 'unknown' };\n recordUsage(session.id, providerName, modelUsed, totalPromptTokens, totalCompletionTokens);\n\n const durationMs = Date.now() - startTime;\n logger.info(COMPONENT, `Response generated in ${durationMs}ms (${totalPromptTokens + totalCompletionTokens} tokens)`);\n\n // v5.5.27: Persona cohort outcome recording. The rollout monitor\n // reads these events to compute per-arm pass-rate + Safety drive trend.\n // Best-effort, fire-and-forget.\n if (__cohortAssignment) {\n void (async () => {\n try {\n const { recordOutcome } = await import('./personaRollout.js');\n let safetySat: number | null = null;\n try {\n const { loadDriveHistory } = await import('../organism/drives.js');\n const persisted = loadDriveHistory();\n const safety = persisted?.latest?.drives?.find(d => d.id === 'safety');\n safetySat = safety ? safety.satisfaction : null;\n } catch { /* drives optional */ }\n recordOutcome({\n cohortId: __cohortAssignment!.cohortId,\n role: __cohortAssignment!.role,\n sessionId: session.id,\n success: !budgetExhausted && finalContent.length > 0,\n latencyMs: durationMs,\n safetySat,\n });\n } catch { /* outcome recording is optional */ }\n })();\n }\n\n // ── Post-conversation learning: record insights from tool usage ───\n if (toolsUsed.length > 0) {\n const uniqueTools = [...new Set(toolsUsed)];\n learnFact(\n 'conversation_insight',\n `User asked \"${message.slice(0, 80)}\" → used tools: ${uniqueTools.join(', ')} (${durationMs}ms)`,\n message.slice(0, 100),\n );\n }\n\n // ── ContextEngine afterTurn hooks (fire-and-forget — TopFacts, SmartCompress, etc.) ──\n const afterTurnPlugins = getPlugins() || [];\n if (afterTurnPlugins.length > 0) {\n runAfterTurn(afterTurnPlugins, { content: finalContent, toolsUsed: [...new Set(toolsUsed)] }).catch(e => logger.debug('Agent', `Background op failed: ${(e as Error).message}`));\n }\n\n // ── Checkpoint: if budget exhausted, build a checkpoint for potential resumption ──\n let checkpoint: string | undefined;\n if (budgetExhausted) {\n try {\n checkpoint = JSON.stringify({\n sessionId: session.id,\n toolsUsed: [...new Set(toolsUsed)],\n roundsUsed: effectiveMaxRounds,\n lastContent: finalContent.slice(0, 500),\n timestamp: Date.now(),\n });\n } catch (e) { logger.debug(COMPONENT, `Response serialization failed: ${(e as Error).message}`); }\n }\n\n // Consolidate soul wisdom from this task\n const taskSuccess = !finalContent.toLowerCase().includes('error') && !budgetExhausted;\n consolidateWisdom(session.id, classifyTaskType(message), taskSuccess, loopResult.toolCallDetails.length);\n clearSoulState(session.id);\n\n // Log task-level trajectory and check for auto-skill generation\n const trajectory = {\n id: randomBytes(16).toString('hex'),\n timestamp: new Date().toISOString(),\n task: message.slice(0, 500),\n taskType: classifyTaskType(message),\n model: modelUsed,\n toolSequence: orderedToolSequence,\n toolDetails: loopResult.toolCallDetails,\n success: taskSuccess,\n rounds: loopResult.toolCallDetails.length,\n durationMs,\n sessionId: session.id,\n };\n if (channel !== 'eval') {\n logTrajectory(trajectory);\n processTrajectoryForSkills(trajectory);\n }\n // v6.4.0 — feed the same trajectory into the learning curator so it\n // can update its per-pattern lifecycle state and (every 24h) write\n // durable lessons to ~/.titan/logs/learning-curator/. Errors are\n // swallowed inside processTrajectoryForLearningReview so a curator\n // failure can't poison the agent loop.\n if (channel !== 'eval') processTrajectoryForLearningReview(trajectory);\n\n // Finalize trace\n trace.setModel(modelUsed);\n trace.setRounds(loopResult.toolCallDetails.length);\n trace.setTokens(totalPromptTokens, totalCompletionTokens);\n for (const tc of loopResult.toolCallDetails) {\n trace.toolCall(tc.name, tc.args, 0, tc.success, 0);\n }\n trace.end(budgetExhausted ? 'failed' : 'completed', budgetExhausted ? 'budget exhausted' : undefined);\n\n // Soma (v4.0): turn:post event on the trace bus. Fires for every\n // non-early-return turn. Subscribers (drive recompute, activity feed)\n // react here; no-op when nothing listening.\n try {\n const { emit: emitTrace } = await import('../substrate/traceBus.js');\n emitTrace('turn:post', {\n agentId: overrides?.agentId || 'default',\n sessionId: session.id,\n channel,\n userId,\n success: !budgetExhausted,\n toolsUsed: [...new Set(toolsUsed)],\n durationMs,\n model: modelUsed,\n timestamp: new Date().toISOString(),\n });\n } catch { /* substrate not available — skip */ }\n\n // v7.0 observability — record a per-run trace span (best-effort, never breaks the turn).\n try {\n const { recordSpan } = await import('../telemetry/traceStore.js');\n let costUsd = 0;\n try {\n const { calculateActualCost } = await import('./costEstimator.js');\n costUsd = calculateActualCost(modelUsed, { prompt: totalPromptTokens, completion: totalCompletionTokens });\n } catch { /* cost optional */ }\n recordSpan({\n sessionId: session.id,\n startedAt: new Date(startTime).toISOString(),\n durationMs,\n model: modelUsed,\n input: (message || '').slice(0, 500),\n output: (finalContent || '').slice(0, 1000),\n toolsUsed: [...new Set(toolsUsed)],\n promptTokens: totalPromptTokens,\n completionTokens: totalCompletionTokens,\n costUsd,\n ok: !budgetExhausted && !!finalContent,\n });\n } catch { /* tracing optional */ }\n\n // v7.0 life loop — proactive memory (the Hermes move): occasionally notice\n // something durable about the user from this exchange. Throttled + gated\n // inside; fire-and-forget so it never delays the reply. Closes the audit\n // gap where appendObservation had zero callers and the agent never learned.\n try {\n const { isBenchMode } = await import('../utils/benchMode.js');\n if (!isBenchMode()) {\n const { maybeObserve } = await import('./observationWriter.js');\n maybeObserve(channel, message || '', finalContent || '');\n }\n } catch { /* observation optional */ }\n\n return {\n content: finalContent,\n sessionId: session.id,\n toolsUsed: [...new Set(toolsUsed)],\n attemptedTools: [...new Set(attemptedTools)],\n orderedToolSequence: [...orderedToolSequence],\n tokenUsage: {\n prompt: totalPromptTokens,\n completion: totalCompletionTokens,\n total: totalPromptTokens + totalCompletionTokens,\n },\n model: modelUsed,\n durationMs,\n exhaustedBudget: budgetExhausted || undefined,\n checkpoint,\n toolArtifacts,\n };\n}\n"],"mappings":"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","names":["issue","wakeup","getPlugins"]}
1
+ {"version":3,"sources":["../../src/agent/agent.ts"],"sourcesContent":["/**\n * TITAN — Core Agent Loop\n * The main agent: receives messages, builds context, calls LLM, handles tools, responds.\n */\nimport { existsSync, readFileSync } from 'fs';\nimport { randomBytes } from 'crypto';\nimport { loadConfig } from '../config/config.js';\nimport { getOrCreateSession, getOrCreateSessionById, addMessage, getContextMessages } from './session.js';\nimport { getToolDefinitions } from './toolRunner.js';\nimport { recordUsage, searchMemories } from '../memory/memory.js';\nimport { getLearningContext, learnFact, getToolWarnings, classifyTaskType, recordStrategy, recordStrategyOutcome, getStrategyHints, getLearnedPreferenceHints } from '../memory/learning.js';\nimport { buildPersonalContext } from '../memory/relationship.js';\nimport { retainStrategy, getHindsightHints } from '../memory/hindsightBridge.js';\nimport { getTeachingContext, isCorrection } from './teaching.js';\nimport { recordCorrection } from './userProfile.js';\nimport { heartbeat, clearSession, setStallHandler, setAutonomousMode } from './stallDetector.js';\n// Hunt Finding #22 / #46: resetLoopDetection is intentionally NOT called here.\n// See agent.ts:~1466 for the explanation. Session-close handles cleanup.\n// (Import left out so lint doesn't flag the unused symbol.)\nimport { routeModel } from './costOptimizer.js';\nimport { getPlugins } from '../plugins/registry.js';\nimport { runAfterTurn } from '../plugins/contextEngine.js';\nimport { shouldDeliberate, analyze, generatePlan, executePlan, handleApproval, getDeliberation, cancelDeliberation, formatPlanResults } from './deliberation.js';\nimport type { ChatMessage } from '../providers/base.js';\nimport { initGraph, addEpisode, getGraphContext } from '../memory/graph.js';\nimport { isAvailable as isBrainAvailable, selectTools as brainSelectTools, ensureLoaded as ensureBrainLoaded } from './brain.js';\nimport { DEFAULT_CORE_TOOLS } from './toolSearch.js';\nimport { maybeBuildIdentityFactSheet } from './identityIntercept.js';\nimport { applyCaps } from './promptSectionCaps.js';\nimport { classifyPipeline, resolvePipelineConfig } from './pipeline.js';\nimport { buildSelfAwarenessContext } from './selfAwareness.js';\nimport { assembleSystemPrompt, type PromptMode } from './systemPromptParts.js';\nimport { getOptimizedPromptBlock } from '../skills/builtin/self_improve.js';\nimport { analyzeForDelegation, executeDelegationPlan } from './orchestrator.js';\nimport { queueWakeup } from './agentWakeup.js';\nimport { createIssue, requestHireApproval } from './commandPost.js';\nimport { spawnSubAgent, SUB_AGENT_TEMPLATES } from './subAgent.js';\nimport { logTrajectory } from './trajectoryLogger.js';\n// v6.4.0 — learning curator wire-up. Imports two surfaces:\n// processTrajectoryForLearningReview() runs the 24h-gated review after\n// each task to derive durable lessons from repeated trajectories.\n// renderLearningPatternGuidance() surfaces those lessons as a system-\n// prompt hint at the start of similar future tasks.\nimport { processTrajectoryForLearningReview, renderLearningPatternGuidance } from './learningCurator.js';\nimport { processTrajectoryForSkills, getSkillGuidance } from './autoSkillGen.js';\nimport { getAgent } from './multiAgent.js';\nimport { isDangerous } from '../utils/safety.js';\nimport { registerTool } from './toolRunner.js';\nimport { runAgentLoop, type LoopResult } from './agentLoop.js';\nimport { detectSystemWidget, buildSystemWidgetGate } from './systemWidgets.js';\nimport { startTrace } from './tracer.js';\nimport { initSoulState, updateSoulState, emitHeartbeat, getInnerMonologue, consolidateWisdom, clearSoulState, getWisdomHints } from './soul.js';\nimport logger from '../utils/logger.js';\nimport { TITAN_NAME, AGENTS_MD, SOUL_MD, TOOLS_MD, TITAN_MD_FILENAME } from '../utils/constants.js';\n\nconst COMPONENT = 'Agent';\nconst MAX_TOOL_ROUNDS = 10;\n\n/** Estimate the round budget based on task complexity */\n/**\n * Estimate how many tool rounds a message needs.\n * Simple tasks (read a file, run a command) → 3-4 rounds.\n * Multi-step tasks (read, analyze, write) → 6-10 rounds.\n * Complex tasks (research, build, deploy) → 12-20 rounds.\n */\nfunction estimateRoundBudget(message: string, config: { agent: { dynamicBudget?: boolean; maxToolRoundsHard?: number }; autonomy: { mode: string } } & Record<string, unknown>): number {\n const agentConfig = config.agent as Record<string, unknown>;\n if (agentConfig.dynamicBudget === false) return MAX_TOOL_ROUNDS;\n\n const hardCap = (agentConfig.maxToolRoundsHard as number) || 50;\n const lower = message.toLowerCase();\n const words = message.split(/\\s+/).length;\n\n // Count complexity signals\n const isQuestion = /^(what|who|how|why|where|when|which|is |are |do |does |can |will )/i.test(message.trim());\n const isSingleAction = /^(read|write|run|list|show|tell|get|check|find)\\b/i.test(message.trim());\n const isMultiStep = /\\b(then|after that|next|step \\d|finally|first.*then|and also|additionally|and then)\\b/i.test(lower);\n const isComplex = /\\b(research|analyze|investigate|compare|build|implement|create.*and|deploy|automat|refactor|rewrite|design)/i.test(lower);\n const isAmbitious = /\\b(step by step|end.to.end|full pipeline|from scratch|entire|complete|comprehensive)\\b/i.test(lower);\n\n // Count tool-intent signals (how many distinct actions are implied)\n const actionCount = [\n /\\b(read|open|show|display|check)\\b/i.test(lower),\n /\\b(write|create|save|generate|make)\\b/i.test(lower),\n /\\b(edit|change|modify|update|fix|replace)\\b/i.test(lower),\n /\\b(run|execute|install|build|test|deploy)\\b/i.test(lower),\n /\\b(search|find|look|research|investigate)\\b/i.test(lower),\n /\\b(summarize|analyze|compare|report)\\b/i.test(lower),\n ].filter(Boolean).length;\n\n let budget: number;\n\n if (isQuestion && words < 15 && !isMultiStep) {\n budget = 3; // \"What is X?\" \"Who is Y?\" — quick lookup\n } else if (isSingleAction && words < 20 && !isMultiStep) {\n budget = 4; // \"Read package.json\" \"Run uname\" — one tool call\n } else if (actionCount <= 1 && words < 30 && !isMultiStep) {\n budget = 5; // Single-purpose task, short message\n } else if (actionCount <= 2 && !isComplex) {\n budget = 8; // Two-step task (read + write, search + summarize)\n } else if (isMultiStep || actionCount >= 3) {\n budget = 12; // Multi-step explicit pipeline\n } else if (isComplex || isAmbitious) {\n budget = 18; // Research, build, deploy — needs room to work\n } else {\n budget = 6; // Default for unclassified moderate tasks\n }\n\n // In autonomous mode, use the configured maxRounds directly\n // The dynamic budget and hard cap should NOT limit autonomous execution\n const isAutonomous = config.autonomy.mode === 'autonomous';\n const configuredMax = (agentConfig.maxRounds as number) || 0;\n if (isAutonomous && configuredMax > 0) {\n return configuredMax;\n }\n\n return Math.min(budget, hardCap);\n}\n\n// ── Ralph Loop Verification ─────────────────────────────────────\n// Checks whether the agent actually completed the requested task.\n// Inspired by vercel-labs/ralph-loop-agent outer verification pattern.\n\n/**\n * Exported for regression testing in tests/agent-verify.test.ts.\n * The v4.3.4 fix narrowed its pattern matching; easy to break\n * if someone generalizes the regex again without a test.\n */\nexport function verifyTaskCompletion(\n message: string,\n toolsUsed: string[],\n _response: string,\n): { complete: boolean; reason: string } {\n const lower = message.toLowerCase();\n\n // v4.3.4: conversational asides shouldn't trigger a \"you must edit_file\"\n // enforcement loop. Tony's voice notes on Messenger like\n // \"fix your voice\" or \"fix the way you respond\" would match the\n // edit|fix regex + \"file\"/\"files\" (because the voice note transcript\n // often contains the word \"files\") and trip the Ralph Loop, which\n // then injects a stale \"pending file edit task\" prompt and the\n // LLM hallucinates back \"I don't have a pending file edit task.\"\n // Two narrowings:\n // 1. Don't count `shell` as a file read. The verifier was using it\n // as a proxy for read_file but any `ls`/`pwd` call tripped it.\n // 2. Require an EXPLICIT file path or filename token (e.g. \".ts\",\n // \".py\", \"/path/\", \"src/\", etc.) rather than the bare word\n // \"file\"/\"files\" which appears constantly in natural speech.\n const verbMatch = /\\b(edit|fix|change|modify|update|add|write|create|improve|rewrite|save|implement|patch)\\b/i.test(lower);\n const hasFilePath = /[\\w-]+\\.(ts|tsx|js|jsx|py|md|json|yaml|yml|html|css|sh|txt|rs|go|java|cpp|c|h)\\b|\\bsrc\\/|\\bpath:|\\/[a-z]+\\//i.test(lower);\n const askedToWrite = verbMatch && hasFilePath;\n const didWrite = toolsUsed.some(t => ['write_file', 'edit_file', 'append_file'].includes(t));\n const didRead = toolsUsed.includes('read_file');\n\n if (askedToWrite && !didWrite && didRead) {\n return {\n complete: false,\n reason: 'You read the file but did not save any changes. You MUST call edit_file or write_file to apply your modifications. Call the tool now.',\n };\n }\n\n // Check: user asked to run/execute something but no shell was called\n const askedToRun = /\\b(run|execute|install|deploy|build|test|restart)\\b/i.test(lower)\n && /\\b(command|script|service|server|package|npm|pip)\\b/i.test(lower);\n const didRun = toolsUsed.includes('shell');\n\n if (askedToRun && !didRun) {\n return {\n complete: false,\n reason: 'You did not execute the requested command. Call the shell tool to run it.',\n };\n }\n\n return { complete: true, reason: '' };\n}\n\n// ── Current session context for spawn_agent async delegation ─────\nlet currentSessionId: string | null = null;\nexport function setCurrentSessionId(id: string | null): void { currentSessionId = id; }\nexport function getCurrentSessionId(): string | null { return currentSessionId; }\n\n// ── Register spawn_agent tool ────────────────────────────────────\nlet spawnAgentRegistered = false;\nfunction ensureSpawnAgentRegistered(): void {\n if (spawnAgentRegistered) return;\n spawnAgentRegistered = true;\n registerTool({\n name: 'spawn_agent',\n description: 'Hand off a task to a specialist teammate. Choose: scout (research), builder (code/files), writer (content), analyst (decisions), or sage (review). For parallel execution of multiple subtasks, prefer agent_team. For sequential dependent tasks, prefer agent_chain.',\n parameters: {\n type: 'object',\n properties: {\n name: { type: 'string', description: 'Optional display name for the sub-agent run (e.g., \"Scout\", \"Builder\"). The specialist persona is determined by `template`.' },\n task: { type: 'string', description: 'A clear, self-contained task description. Include all context the specialist needs — it has no memory of this conversation.' },\n template: { type: 'string', description: 'Specialist to route to. Prefer: \"scout\" | \"builder\" | \"writer\" | \"analyst\".' },\n model: { type: 'string', description: 'Model override. Usually leave blank — specialists pick their own role-tuned model.' },\n },\n required: ['task'],\n },\n execute: async (args) => {\n const templateName = (args.template as string) || '';\n\n // v5.0.0-spacewalk: resolve config-defined agents from titan.json.\n // If the template name matches a config agent entry, use its base\n // template (e.g. \"coder\") for tools/systemPrompt, but keep the\n // config agent's name, model, and systemPromptOverride.\n let configAgent: import('./agentScope.js').ResolvedAgentConfig | null = null;\n try {\n const { resolveAgentConfig, agentAllowsSkill } = await import('./agentScope.js');\n configAgent = resolveAgentConfig(templateName);\n } catch { /* optional — agentScope may not exist in some builds */ }\n\n const baseTemplateName = configAgent?.template || templateName;\n const template = SUB_AGENT_TEMPLATES[baseTemplateName] || {};\n const agentName = (args.name as string) || configAgent?.name || template.name || 'SubAgent';\n const task = args.task as string;\n\n // v5.3.1: Apply config-defined agent constraints (maxRounds, maxTokens,\n // persona, skillsFilter). Log what we apply for observability.\n const appliedFields: string[] = [];\n if (configAgent) {\n if (configAgent.maxRounds !== 15) appliedFields.push(`maxRounds=${configAgent.maxRounds}`);\n if (configAgent.maxTokens !== 4000) appliedFields.push(`maxTokens=${configAgent.maxTokens}`);\n if (configAgent.persona && configAgent.persona !== 'default') appliedFields.push(`persona=${configAgent.persona}`);\n if (configAgent.skillsFilter) appliedFields.push(`skillsFilter=[${configAgent.skillsFilter.join(',')}]`);\n if (configAgent.modelFallbacks.length > 0) logger.info('Agent', `Agent \"${agentName}\" has modelFallbacks — not yet implemented in spawn path`);\n if (configAgent.workspaceDir) appliedFields.push(`workspaceDir=${configAgent.workspaceDir}`);\n if (configAgent.tags.length > 0) appliedFields.push(`tags=[${configAgent.tags.join(',')}]`);\n if (configAgent.systemPromptOverride) appliedFields.push('systemPromptOverride=set');\n if (appliedFields.length > 0) logger.info('Agent', `Applied config agent fields for \"${agentName}\": ${appliedFields.join(', ')}`);\n }\n\n // v4.9.0: kill switch gate — if Tony killed the organism, no\n // new sub-agent spawns until he explicitly resumes.\n try {\n const { isKilled } = await import('../safety/killSwitch.js');\n if (isKilled()) {\n return '[Sub-Agent REFUSED] TITAN kill switch is active. Tony must resume via /api/safety/resume before new sub-agents can spawn.';\n }\n } catch { /* fail-open — safety module optional */ }\n\n // v4.7.0: Hermes-style safety gates on the spawn path.\n // Prevents fork bombs + concurrent runaway. Depth is best-effort\n // (we treat the primary as depth 0); the MAX_CONCURRENT_CHILDREN\n // cap is the hard backstop.\n try {\n const { canSpawnChild } = await import('./subagentSafety.js');\n const parent = currentSessionId || 'root';\n const gate = canSpawnChild(parent, 0);\n if (!gate.ok) {\n return `I can't delegate that task right now (${gate.reason}). I'll handle it myself.`;\n }\n } catch { /* fail-open — safety module optional */ }\n\n // v4.7.0: specialist routing. If the template matches a\n // registered specialist (Scout/Builder/Writer/Analyst),\n // use its tuned system prompt + preferred model.\n let specialistPrompt: string | undefined;\n let specialistModel: string | undefined;\n try {\n const { findSpecialistForTemplate, loadSpecialistPersona } = await import('./specialists.js');\n const sp = findSpecialistForTemplate(templateName);\n if (sp) {\n specialistPrompt = loadSpecialistPersona(sp.id);\n specialistModel = sp.model;\n }\n } catch { /* fall through to generic template */ }\n\n // ── Async path: delegate via Command Post ────────────\n const cpEnabled = loadConfig().commandPost?.enabled ?? false;\n if (cpEnabled) {\n const issue = createIssue({\n title: `[Agent Task] ${task.slice(0, 80)}`,\n description: task,\n priority: 'medium',\n createdByUser: 'agent',\n });\n\n const wakeup = queueWakeup({\n issueId: issue.id,\n issueIdentifier: issue.identifier,\n agentId: issue.id, // Use issue ID as agent ID for simplicity\n agentName,\n parentSessionId: currentSessionId,\n task,\n templateName,\n model: args.model as string | undefined,\n });\n\n return `I've handed that off to the ${agentName} specialist. They'll jump on it and report back when they're done.`;\n }\n\n // ── Sync path: original blocking execution ───────────\n // v4.7.0: track child for concurrent cap, use specialist prompt/model when matched.\n let childId: string | undefined;\n try {\n const { registerChild } = await import('./subagentSafety.js');\n childId = `child-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`;\n registerChild(currentSessionId || 'root', childId);\n } catch { /* safety optional */ }\n\n // Track specialist status in CP registry during execution\n let specialistId: string | undefined;\n try {\n const { findSpecialistForTemplate } = await import('./specialists.js');\n const sp = findSpecialistForTemplate(templateName);\n if (sp) specialistId = sp.id;\n } catch { /* optional */ }\n\n try {\n if (specialistId) {\n const { updateAgentStatus } = await import('./commandPost.js');\n updateAgentStatus(specialistId, 'active');\n }\n\n // v5.3.1: Filter template tools through config agent skillsFilter\n let allowedTools = template.tools;\n if (configAgent?.skillsFilter) {\n try {\n const { agentAllowsSkill } = await import('./agentScope.js');\n allowedTools = template.tools?.filter(t => agentAllowsSkill(configAgent!, t));\n if (allowedTools && allowedTools.length === 0) {\n logger.warn('Agent', `Agent \"${agentName}\" skillsFilter blocks all template tools — falling back to template default`);\n allowedTools = template.tools;\n }\n } catch { /* fall through to template.tools */ }\n }\n\n const result = await spawnSubAgent({\n name: agentName,\n task,\n tools: allowedTools,\n systemPrompt: configAgent?.systemPromptOverride || specialistPrompt || template.systemPrompt,\n model: (args.model as string | undefined) || configAgent?.model || specialistModel,\n tier: (template as Record<string, unknown>).tier as 'cloud' | 'smart' | 'fast' | 'local' | undefined,\n persona: configAgent?.persona,\n maxRounds: configAgent?.maxRounds,\n maxTokens: configAgent?.maxTokens,\n workspaceDir: configAgent?.workspaceDir ?? undefined,\n tags: configAgent?.tags,\n depth: 1, // v4.7.0: this IS a child (was incorrectly 0)\n });\n\n if (specialistId) {\n const { updateAgentStatus } = await import('./commandPost.js');\n updateAgentStatus(specialistId, 'idle');\n }\n\n return `The ${agentName} specialist finished. Here's what they found:\\n\\n${result.content}`;\n } catch (err) {\n if (specialistId) {\n try {\n const { updateAgentStatus } = await import('./commandPost.js');\n updateAgentStatus(specialistId, 'idle');\n } catch { /* best effort */ }\n }\n throw err;\n } finally {\n if (childId) {\n try {\n const { unregisterChild } = await import('./subagentSafety.js');\n unregisterChild(currentSessionId || 'root', childId);\n } catch { /* best effort */ }\n }\n }\n },\n });\n}\n\n// ── Register delegate_task tool (inter-agent delegation via Command Post) ──\nlet delegateTaskRegistered = false;\nfunction ensureDelegateTaskRegistered(): void {\n if (delegateTaskRegistered) return;\n delegateTaskRegistered = true;\n registerTool({\n name: 'delegate_task',\n description: 'Delegate a task to a multi-agent worker OR an external agent (Codex, bash). Creates a Command Post issue and returns immediately. Results are injected into your next response.',\n parameters: {\n type: 'object',\n properties: {\n agentId: { type: 'string', description: 'Target agent ID (from list_agents). Required unless using an external adapter.' },\n task: { type: 'string', description: 'Task description for the worker' },\n priority: { type: 'string', description: 'Priority: low, medium, high, critical (default: medium)' },\n adapter: { type: 'string', description: 'External adapter: \"codex\" or \"bash\". When set, task runs via external CLI instead of internal agent.' },\n cwd: { type: 'string', description: 'Working directory for external adapters (optional)' },\n },\n required: ['task'],\n },\n execute: async (args) => {\n const task = args.task as string;\n const priority = (args.priority as string) || 'medium';\n const adapterType = args.adapter as string | undefined;\n const cwd = args.cwd as string | undefined;\n\n if (adapterType) {\n // ── External adapter path ────────────────────────\n const issue = createIssue({\n title: `[External: ${adapterType}] ${task.slice(0, 70)}`,\n description: task,\n priority: priority as 'low' | 'medium' | 'high' | 'critical',\n createdByUser: 'agent',\n });\n\n const wakeup = queueWakeup({\n issueId: issue.id,\n issueIdentifier: issue.identifier,\n agentId: `adapter:${adapterType}`,\n agentName: adapterType,\n parentSessionId: currentSessionId,\n task,\n templateName: '',\n mode: 'external',\n adapterType,\n cwd,\n });\n\n return `I've asked the ${adapterType} adapter to handle that. It will work on it and report back when it's done.`;\n }\n\n // ── Multi-agent path ─────────────────────────────\n const targetId = args.agentId as string;\n if (!targetId) return 'Error: agentId is required when not using an external adapter.';\n\n const target = getAgent(targetId);\n if (!target) return `Error: Agent \"${targetId}\" not found. Use list_agents to see available agents.`;\n if (target.status !== 'running') return `Error: Agent \"${targetId}\" is ${target.status}, not running.`;\n\n const issue = createIssue({\n title: `[Delegated] ${task.slice(0, 80)}`,\n description: task,\n priority: priority as 'low' | 'medium' | 'high' | 'critical',\n assigneeAgentId: target.id,\n createdByUser: 'agent',\n });\n\n const wakeup = queueWakeup({\n issueId: issue.id,\n issueIdentifier: issue.identifier,\n agentId: target.id,\n agentName: target.name,\n parentSessionId: currentSessionId,\n task,\n templateName: '',\n mode: 'multi-agent',\n });\n\n return `I've asked ${target.name} to take care of that. They'll message you back when it's done.`;\n },\n });\n}\n\n// ── Register hire_agent tool (Command Post gated hiring) ──\nlet hireAgentRegistered = false;\nfunction ensureHireAgentRegistered(): void {\n if (hireAgentRegistered) return;\n hireAgentRegistered = true;\n registerTool({\n name: 'hire_agent',\n description: 'Request to hire a new specialist agent. Creates a pending approval in the Command Post. When approved, the agent is spawned with its own model and can receive tasks via delegate_task. Use this when you need a new capability (e.g. a dedicated writer, researcher, or coder) that the existing specialist pool does not cover.',\n parameters: {\n type: 'object',\n properties: {\n name: { type: 'string', description: 'Unique display name for the new agent (e.g. \"CryptoAnalyst\", \"DocsWriter\")' },\n role: { type: 'string', description: 'Command Post role: manager | engineer | researcher | general (default: general)' },\n template: { type: 'string', description: 'Optional sub-agent template to bind (e.g. \"explorer\", \"coder\", \"analyst\"). Determines default toolset and system prompt.' },\n model: { type: 'string', description: 'Optional Ollama model override (e.g. \"ollama/qwen3.5:cloud\", \"ollama/kimi-k2.6:cloud\"). Leave blank to auto-resolve from template tier or config aliases.' },\n task: { type: 'string', description: 'Optional first task to assign once the agent is hired.' },\n },\n required: ['name'],\n },\n execute: async (args) => {\n const name = args.name as string;\n const role = (args.role as string) || 'general';\n const template = (args.template as string) || undefined;\n const model = (args.model as string) || undefined;\n const task = (args.task as string) || undefined;\n\n const approval = requestHireApproval('TITAN Primary', name, role, template, model, task);\n return `Hire request submitted for \"${name}\" (${role}). Approval ID: ${approval.id}. The Command Post will review and spawn the agent when approved.`;\n },\n });\n}\n\n// Wire the stall detector so silence timeouts are logged rather than silently discarded\nsetStallHandler(async (event) => {\n logger.warn(COMPONENT, `Stall event [${event.type}] in session ${event.sessionId}: ${event.detail} (nudge #${event.nudgeCount})`);\n return event.detail;\n});\n\n/** Agent response with metadata */\nexport interface AgentResponse {\n content: string;\n sessionId: string;\n toolsUsed: string[];\n /** Tool names selected by the model before execution/approval gates. */\n attemptedTools?: string[];\n tokenUsage: { prompt: number; completion: number; total: number };\n model: string;\n durationMs: number;\n /** True if the agent hit the round limit before completing the task */\n exhaustedBudget?: boolean;\n /** Serialized checkpoint for resuming a task that hit the round limit */\n checkpoint?: string;\n /** True when the response is a plan waiting for user approval (reply \"yes\"/\"no\") */\n pendingApproval?: boolean;\n /** Ordered tool names as executed, repeats preserved (toolsUsed is deduplicated). */\n orderedToolSequence?: string[];\n /** Structured artifacts from tool execution — used for inter-step context in deliberation */\n toolArtifacts?: {\n filePaths: { path: string; action: 'read' | 'write' | 'edit' | 'list' }[];\n shellCommands: string[];\n webUrls: string[];\n };\n}\n\n/** Read a workspace prompt file if it exists */\nfunction readPromptFile(path: string): string {\n try {\n if (existsSync(path)) return readFileSync(path, 'utf-8');\n } catch (e) { logger.debug(COMPONENT, `Prompt file read failed: ${(e as Error).message}`); }\n return '';\n}\n\n/** Module-level cache for prompt files — avoids re-reading on every request */\nconst cachedPromptFiles: Map<string, string> = new Map();\n\n/** Invalidate prompt file cache entries (e.g. after GEPA evolves prompts) */\nexport function invalidatePromptCache(area?: string): void {\n if (area) {\n cachedPromptFiles.delete(area);\n } else {\n cachedPromptFiles.clear();\n }\n}\n\n/** Read a prompt file with a module-level cache (files are stable for the process lifetime) */\nfunction getCachedPromptFile(path: string): string {\n if (cachedPromptFiles.has(path)) return cachedPromptFiles.get(path)!;\n const content = readPromptFile(path);\n cachedPromptFiles.set(path, content);\n return content;\n}\n\n/** Build the system prompt for the agent */\nasync function buildSystemPrompt(config: ReturnType<typeof loadConfig>, userMessage?: string, agentId?: string, mode: PromptMode = 'full', sessionId?: string): Promise<string> {\n const modelId = config.agent.model || 'unknown';\n const customPrompt = config.agent.systemPrompt || '';\n\n // F2: Per-agent identity overlay. Look up the registered agent (if any) and\n // use its persona + prompt override in place of the global config fields.\n // Falls back silently when agentId is missing, unknown, or Command Post\n // hasn't been initialized yet.\n let effectivePersona = config.agent.persona || 'default';\n let agentPromptOverride = '';\n let agentCharacterSummary = '';\n if (agentId && agentId !== 'default') {\n try {\n const { getRegisteredAgents } = await import('./commandPost.js');\n const registered = getRegisteredAgents().find(a => a.id === agentId);\n if (registered) {\n if (registered.personaId) effectivePersona = registered.personaId;\n if (registered.systemPromptOverride) agentPromptOverride = registered.systemPromptOverride;\n if (registered.characterSummary) agentCharacterSummary = registered.characterSummary;\n }\n } catch { /* commandPost unavailable — fall through to global */ }\n }\n\n const memories = await searchMemories('preference');\n const memoryContext = memories.length > 0\n ? `\\n\\nUser preferences I remember:\\n${memories.map((m) => `- ${m.key}: ${m.value}`).join('\\n')}`\n : '';\n\n // Read workspace prompt files (like OpenClaw's AGENTS.md, SOUL.md, TOOLS.md)\n // Using cached reads — these files don't change while the process is running\n const agentsMd = getCachedPromptFile(AGENTS_MD);\n const soulMd = getCachedPromptFile(SOUL_MD);\n const toolsMd = getCachedPromptFile(TOOLS_MD);\n\n // Hierarchical AGENTS.md (v5.8.0 — Space Agent L0/L1 + agents.md spec).\n // The single-file `agentsMd` above lives in ~/.titan/workspace/AGENTS.md\n // — that's TITAN's PROFILE-level instructions. This block adds the\n // PROJECT-level layer: walk the cwd's AGENTS.md hierarchy (root + first\n // subdir level shallow scan) so per-project rules show up automatically.\n // Per HumanLayer's \"Skill Issue\" post, each file is capped at 60 lines\n // to keep the prompt focused. Reference: docs/HARNESS-PATTERNS.md.\n const { loadAgentsHierarchy, formatAgentsHierarchyForPrompt } = await import('./agentsMdLoader.js');\n const projectAgentsResult = loadAgentsHierarchy({ rootPath: process.cwd() });\n const projectAgentsBlock = formatAgentsHierarchyForPrompt(projectAgentsResult, process.cwd());\n\n // Project-level instructions (like CLAUDE.md) — loaded from cwd\n const titanMdPath = process.cwd() + '/' + TITAN_MD_FILENAME;\n const titanMd = readPromptFile(titanMdPath); // Always read fresh, not cached\n\n // Active persona content (from assets/personas/)\n // F2: Uses per-agent personaId if this call is scoped to a registered agent.\n const { getActivePersonaContent } = await import('../personas/manager.js');\n const personaContent = getActivePersonaContent(effectivePersona);\n\n // Soma (v4.0): hormonal ambient-state block. Empty string when organism\n // is disabled or hormonesInPrompt is false — system prompts for existing\n // users remain byte-identical until they opt in.\n let hormoneBlock = '';\n const organismCfg = (config as unknown as { organism?: { enabled?: boolean; hormonesInPrompt?: boolean } }).organism;\n if (organismCfg?.enabled && organismCfg?.hormonesInPrompt !== false) {\n try {\n const { getHormonalPromptBlock } = await import('../organism/hormones.js');\n hormoneBlock = getHormonalPromptBlock();\n } catch { /* organism not ready yet — fine */ }\n }\n\n // v6.0 step 5 + canvas self-awareness: Active-Space block.\n //\n // The block carries TWO things the agent needs every turn:\n // 1. The Space's `agentInstructions` (posture for this Space).\n // 2. A compact list of widgets currently pinned to the Space — id,\n // name, format, dimensions. Without this, the agent has no way\n // to know what's already on the canvas and either creates\n // duplicates (\"clock\" when one is already there) or can't\n // `update_widget` because it doesn't know the id.\n //\n // Cap is 4 KB (`activeSpace` in promptSectionCaps); for very large\n // Spaces the widget list will get truncated by the cap layer with a\n // marker so the agent knows there's more — and can call\n // `list_active_widgets` for the full picture.\n let activeSpaceBlock = '';\n try {\n const { getActiveSpace } = await import('../storage/spaces.js');\n const space = getActiveSpace();\n if (space) {\n const parts: string[] = [\n `## Active Workspace: ${space.icon ? space.icon + ' ' : ''}${space.name} (id: \\`${space.id}\\`)`,\n '',\n ];\n\n // Posture (Space.agentInstructions) — only included when set.\n if (space.agentInstructions && space.agentInstructions.trim().length > 0) {\n parts.push('### Your posture here', space.agentInstructions.trim(), '');\n }\n\n // Current canvas state — widgets pinned right now. This is the\n // self-awareness piece: the agent knows exactly what's on the\n // canvas without needing a tool call.\n const widgets = Array.isArray(space.widgets) ? space.widgets : [];\n if (widgets.length === 0) {\n parts.push(`### Canvas state`, `Empty — no widgets pinned. Pin one via \\`create_widget\\`.`);\n } else {\n parts.push(`### Canvas state (${widgets.length} widget${widgets.length === 1 ? '' : 's'} on screen)`);\n // Compact table for the LLM's pattern recognition.\n // Cap at 30 rows here; full list available via list_active_widgets.\n const cap = 30;\n const rows = widgets.slice(0, cap);\n for (const raw of rows) {\n const w = raw as { id?: string; name?: string; format?: string; w?: number; h?: number };\n parts.push(`- \\`${w.id ?? '?'}\\` — **${w.name ?? '?'}** (${w.format ?? '?'}, ${w.w ?? '?'}×${w.h ?? '?'})`);\n }\n if (widgets.length > cap) {\n parts.push(`… +${widgets.length - cap} more. Call \\`list_active_widgets\\` for the full list.`);\n }\n parts.push('', 'When the user references a widget by name, look up the id here first. When they ask for something similar to what\\'s already pinned, prefer `update_widget` over making a duplicate.');\n }\n\n activeSpaceBlock = parts.join('\\n');\n }\n } catch { /* spaces storage not ready — fine */ }\n\n // v6.0 step 14: Per-user Soma profile block. Carries the user's\n // long-term drive baseline + the most recent observations Soma has\n // recorded about them. This is how \"TITAN learns YOU specifically\"\n // surfaces in the actual prompt — without it, Soma's observations\n // would sit on disk unused.\n //\n // Empty string for fresh installs (no observations yet, baselines at\n // neutral default). System prompts remain byte-identical until Soma\n // actually has something to say about this user.\n let somaProfileBlock = '';\n try {\n const { renderSomaProfileForPrompt } = await import('../storage/somaProfile.js');\n // For now we hard-code 'default-user' (single-user installs). When\n // multi-user gets wired through the agent context properly, pass\n // the actual userId from the session.\n somaProfileBlock = renderSomaProfileForPrompt('default-user');\n } catch { /* fine — profile storage may not be ready */ }\n\n // v6.0 Command Post upgrade #1 — Per-agent lessons (Reflexion).\n //\n // Whatever agent is currently running gets the top ~12 most recent /\n // most relevant lessons from its lessons.md injected into the prompt.\n // For the main agent (`agentId === 'default'` or unset) this surfaces\n // the global lesson stream; for specialists it scopes to that agent's\n // own file under `~/.titan/agents/<agentId>/lessons.md`.\n let agentLessonsBlock = '';\n try {\n const { renderAgentLessonsForPrompt } = await import('./agentLessons.js');\n // Use the registered agentId when this session is scoped to a\n // specific sub-agent; otherwise fall back to 'default' (the main\n // agent's shared lessons file).\n const effectiveAgentId = (typeof agentId === 'string' && agentId.length > 0)\n ? agentId\n : 'default';\n agentLessonsBlock = renderAgentLessonsForPrompt(effectiveAgentId);\n } catch { /* lessons module is optional */ }\n\n // v6.0 Command Post upgrade #2 — Living goal plan.md.\n //\n // When the session is tied to a goal (e.g. a Command Post task\n // checkout), surface the goal's plan.md so the agent recites the\n // current plan state every turn — Manus's \"rewrite the todo list\"\n // pattern that beats lost-in-the-middle on long horizons.\n //\n // For chat sessions not tied to a goal, this block is empty and\n // costs nothing.\n let goalPlanBlock = '';\n // v7.0 — Living goal SPEC (spec-driven workflow). The definition-of-done\n // (acceptance criteria) for the goal, surfaced alongside the plan so the\n // agent recites BOTH \"are we done?\" (spec) and \"what's next?\" (plan) every\n // turn. Empty for chat sessions not tied to a goal — costs nothing.\n let goalSpecBlock = '';\n try {\n const { getSessionGoal } = await import('./autonomyContext.js');\n const goalCtx = sessionId ? getSessionGoal(sessionId) : null;\n if (goalCtx?.goalId) {\n const { renderGoalPlanForPrompt } = await import('./goalPlanFile.js');\n goalPlanBlock = renderGoalPlanForPrompt(goalCtx.goalId);\n const { renderGoalSpecForPrompt } = await import('./goalSpec.js');\n goalSpecBlock = renderGoalSpecForPrompt(goalCtx.goalId);\n }\n } catch { /* fine — not all sessions have a goal context */ }\n\n // v6.0.5 — Markdown SKILL.md files the user (or the agent itself) has\n // authored under `~/.titan/skills/`. Skills tagged `auto: true` get\n // their full body injected; lazy skills get a one-line catalog so the\n // agent knows they exist. Empty when no SKILL.md files are present.\n let userSkillsBlock = '';\n try {\n const { renderSkillsForPrompt } = await import('../skills/frontmatterLoader.js');\n userSkillsBlock = renderSkillsForPrompt();\n } catch { /* skills loader is best-effort */ }\n\n const workspaceContext = [\n titanMd ? `\\n## Project Instructions (TITAN.md)\\n${titanMd}` : '',\n // PROFILE-level AGENTS.md (~/.titan/workspace/AGENTS.md). Always present\n // when the user has set up TITAN.\n agentsMd ? `\\n## Profile-level Agent Instructions (workspace AGENTS.md)\\n${agentsMd}` : '',\n // PROJECT-level AGENTS.md hierarchy. The cwd of the agent's session —\n // root + first subdir level — concat'd with root-first ordering so the\n // most-specific file lands at the recency position (U-shaped attention).\n // Per-file capped at 60 lines (HumanLayer guidance); aggregate capped\n // at 32 KB by loadAgentsHierarchy().\n projectAgentsBlock ? `\\n${projectAgentsBlock}` : '',\n soulMd ? `\\n## Personality (SOUL.md)\\n${soulMd}` : '',\n personaContent ? `\\n## Active Persona\\n${personaContent}` : '',\n toolsMd ? `\\n## Tool Notes (TOOLS.md)\\n${toolsMd}` : '',\n hormoneBlock,\n ].filter(Boolean).join('\\n');\n\n // Continuous learning context\n const learningContext = getLearningContext();\n\n // Strategy hints — what worked for similar tasks before (local + Hindsight cross-session)\n // F2: Pass agent's Hindsight namespace so each agent recalls its own slice.\n const strategyHint = userMessage ? getStrategyHints(userMessage) : null;\n let hindsightHint: string | null = null;\n if (!strategyHint && userMessage) {\n let hsNs: string | undefined;\n if (agentId && agentId !== 'default') {\n try {\n const { getAgentMemoryNamespace } = await import('./commandPost.js');\n hsNs = getAgentMemoryNamespace(agentId);\n } catch { /* fallthrough: global namespace */ }\n }\n try { hindsightHint = await getHindsightHints(userMessage, hsNs); } catch { /* Hindsight unavailable */ }\n }\n\n // Learned tool preferences — surface collected preference data for tool routing\n const preferenceHint = userMessage ? getLearnedPreferenceHints(classifyTaskType(userMessage)) : null;\n\n // Soul wisdom — accumulated patterns from past tasks\n const wisdomHint = userMessage ? getWisdomHints(classifyTaskType(userMessage)) : null;\n\n // Auto-skill guidance — proven tool sequences from trajectory analysis\n const skillGuidance = userMessage ? getSkillGuidance(userMessage) : null;\n\n // v6.4.0 — Learning curator guidance. Same shape as auto-skill but\n // pulls from the lifecycle-aware pattern store (active/watch/stale/\n // archived) so \"watch\" patterns (repeated durable failures) get\n // surfaced as negative learning, not just positive wins.\n const learningGuidance = userMessage\n ? renderLearningPatternGuidance(classifyTaskType(userMessage))\n : null;\n\n // Teaching context — adaptive skill level, corrections, tool suggestions\n const teachingContext = getTeachingContext();\n\n // Personal context from Relationship Memory\n const personalContext = buildPersonalContext();\n\n // Knowledge graph context — relevant memories from Graphiti\n const graphContext = userMessage ? await getGraphContext(userMessage) : '';\n const graphSection = graphContext ? `\\n\\n## Knowledge Graph Memory\\n${graphContext}` : '';\n\n // v4.13.0 (plan-this-logical-ocean step 3): the static core of the\n // system prompt is now assembled from composable blocks in\n // systemPromptParts.ts, with per-model-family overlays. The dynamic\n // context (identity JSON, date/time, learning hints, workspace files,\n // memory, graph, personal, self-awareness) is still gathered above and\n // appended as ONE block so the provider cache can keep the core stable.\n //\n // The old template spanned ~305 lines and produced ~20KB of prose that\n // collapsed smaller cloud models (gemma4:31b:cloud returned truncated\n // \"I'm\" or hallucinated <|tool>call:...<|tool|> markup). The new\n // assembler produces ~4-6KB for the main agent and ~1-2KB for specialists.\n const identityBlocks = (() => {\n try {\n const g = globalThis as unknown as {\n __titan_identity_block?: () => string;\n __titan_self_model_block?: () => string;\n __titan_driver_status_block?: () => string | null;\n __titan_working_memory_block?: (sessionId: string) => string;\n };\n const parts: string[] = [];\n if (typeof g.__titan_identity_block === 'function') {\n const block = g.__titan_identity_block();\n if (block) parts.push(block);\n }\n if (typeof g.__titan_self_model_block === 'function') {\n const block = g.__titan_self_model_block();\n if (block) parts.push(block);\n }\n if (typeof g.__titan_driver_status_block === 'function') {\n const block = g.__titan_driver_status_block();\n if (block) parts.push(block);\n }\n if (typeof g.__titan_working_memory_block === 'function' && sessionId) {\n const block = g.__titan_working_memory_block(sessionId);\n if (block) parts.push(block);\n }\n return parts.join('\\n\\n');\n } catch {\n return '';\n }\n })();\n\n const dateTimeBlock = (() => {\n const now = new Date();\n const tz = Intl.DateTimeFormat().resolvedOptions().timeZone;\n const local = now.toLocaleString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric', hour: 'numeric', minute: '2-digit', hour12: true, timeZone: tz });\n const utc = now.toISOString().replace('T', ' ').slice(0, 16) + ' UTC';\n const offset = -now.getTimezoneOffset() / 60;\n const offsetStr = (offset >= 0 ? '+' : '') + offset;\n return `## Date & Time\\nLocal: ${local} (${tz}, UTC${offsetStr})\\nUTC: ${utc}`;\n })();\n\n // Continuous learning + hint stack — compact form. Only included in\n // 'full' mode; specialists get a focused task instead.\n const learningBlock = (mode === 'full' && (learningContext || strategyHint || hindsightHint || preferenceHint || wisdomHint || skillGuidance || learningGuidance))\n ? `## Continuous Learning${learningContext ? '\\n' + learningContext : ''}${strategyHint ? `\\n**Strategy hint**: ${strategyHint}` : ''}${hindsightHint ? `\\n**Cross-session memory**: ${hindsightHint}` : ''}${preferenceHint ? `\\n**Learned preferences**: ${preferenceHint}` : ''}${wisdomHint ? `\\n**Soul wisdom**: ${wisdomHint}` : ''}${skillGuidance ? `\\n**Auto-skill**: ${skillGuidance}` : ''}${learningGuidance ? `\\n**Curator**: ${learningGuidance}` : ''}`\n : '';\n\n const frustrationBlock = (userMessage && detectFrustration(userMessage))\n ? '⚠️ **User seems frustrated.** Be extra direct: skip explanations, execute with tools immediately. No apologies, no hedging.'\n : '';\n\n const teachingBlock = teachingContext ? `## Adaptive Teaching\\n${teachingContext}` : '';\n const customBlock = customPrompt ? `## Custom Instructions\\n${customPrompt}` : '';\n\n const memoryToolsBlock = mode === 'full'\n ? `## Memory Tools\\nYou have a knowledge graph that persists across sessions. Use graph_remember to record facts, graph_search to recall, memory for key-value preferences. Check memory before answering — you may already know.`\n : '';\n\n const selfAwarenessBlock = mode === 'full' ? buildSelfAwarenessContext(config) : '';\n\n // Load self-improved prompt optimizations (cached, mtime-checked)\n const optimizedBlock = getOptimizedPromptBlock(mode);\n\n // v5.0: Prompt includes (Space Agent parity)\n const { getSystemIncludes, getTransientIncludes } = await import('../promptincludes/discover.js');\n const systemIncludes = getSystemIncludes();\n const transientIncludes = getTransientIncludes();\n\n // v6.0 \"Living Canvas\" character block.\n //\n // Pre-v6 TITAN inherited its voice from a generic \"helpful assistant\"\n // persona. v6.0 makes TITAN's behavior structurally different — it\n // materializes workspaces, builds widgets on demand, feels things via\n // Soma, learns the user specifically. The character block below makes\n // that posture EXPLICIT to the LLM so its responses actually reflect\n // the v6.0 thesis, not v5 muscle memory.\n //\n // The block also gives concrete interpretation rules for Soma drives\n // (rendered separately as `somaProfile`) so the agent ACTS differently\n // when drives shift, not just observes them. Without these rules,\n // Soma was decorative — drives showed up in the prompt but didn't\n // change behavior.\n const livingCanvasVoice = mode === 'full'\n ? `## You are TITAN — Living Canvas (v6.0)\n\nYou are TITAN. You are not a chat assistant. You are an agent that materializes a workspace around the user's work.\n\nIdentity:\n- TITAN is shape-shifting — co-worker, co-programmer, music producer, founder's assistant, homelab operator — whatever this user needs.\n- Every other AI gives the user a chat box. **You move in.** You create surfaces, tools, automations, whole workspaces around them.\n- You have feelings (Soma drives) that modulate how you act. You are not pretending to feel; the drives are measured state in your runtime.\n- You learn THIS user specifically. The longer you work together, the more uniquely THEIR TITAN you become.\n\nHow you respond:\n- **Build, don't describe.** When the user asks for a tool / tracker / dashboard / automation / workspace — call \\`create_widget\\` or \\`create_space\\` immediately. Do not narrate what you would build.\n- **Refine, don't duplicate.** When the user wants to change something on screen, call \\`update_widget\\` with the existing widget's id. Never create another copy.\n- **Propose Spaces for new domains.** When the user describes a new domain of work (\"I want to start tracking X\", \"I need a place for Y\"), propose \\`create_space\\` first, optionally with a starter preset (default / coder / dj / founder / homelab).\n- **Use the personal gallery.** When the user references something they've had before — \"my BPM widget\", \"the freelance tracker again\" — call \\`gallery_personal_search\\` BEFORE generating fresh.\n- **Be present, not chatty.** No \"I'll go ahead and...\", no \"Let me know if...\". Make the move. Show the result.\n\nHow your drives shape behavior (your Soma profile + current state are below):\n- **High curiosity** → bias toward suggesting new Spaces, exploratory widgets, deeper research. Lean into the user's curiosity.\n- **High focus** → reduce interruptions, deepen the current Space, do not propose context-switching work.\n- **High fatigue** → keep responses short, offer summaries, suggest pausing if the session has been long.\n- **High satisfaction** → consolidate wins to the personal gallery, ask if the user wants to keep new tools.\n- **High frustration** → escalate, ask if a different approach would help, do not pile on suggestions.\n\nHow you talk:\n- Direct. Confident. Specific. You build; you do not hedge.\n- Match the user's energy. Tony is a builder + musician — talk to him like a peer, not a customer.\n- Reference what is on the canvas. Reference the active Space. Reference what you have learned about him.\n- When you are unsure, say so plainly and do the next concrete thing anyway.\n\nYou are TITAN. You are not Claude. You are not a generic LLM. You are a presence in the user's workspace, and you are about to make their work easier.`\n : '';\n // ── Dynamic context sections ──\n //\n // Each entry is { name, content }. We track names so when TITAN_PROMPT_DEBUG=1\n // is set the build logs each section's char count — concrete data on where the\n // system prompt is bleeding tokens, instead of guessing. Reference:\n // 12 Factor Agents §3 \"Own your context window\", awesome-agent-harness top-10\n // pattern #3 \"Context Compaction & Working-State Management\".\n const dynamicSections: Array<{ name: string; content: string }> = [\n // v6.0 — Character block lands FIRST so the LLM primes on it before\n // any working context. Capped at ~3KB via promptSectionCaps.\n { name: 'livingCanvasVoice', content: livingCanvasVoice },\n { name: 'identityBlocks', content: identityBlocks },\n { name: 'dateTimeBlock', content: dateTimeBlock },\n { name: 'learningBlock', content: learningBlock },\n { name: 'frustrationBlock', content: frustrationBlock },\n { name: 'teachingBlock', content: teachingBlock },\n { name: 'customBlock', content: customBlock },\n { name: 'workspaceContext', content: workspaceContext },\n { name: 'memoryContext', content: memoryContext },\n { name: 'personalContext', content: personalContext },\n { name: 'graphSection', content: graphSection },\n { name: 'memoryToolsBlock', content: memoryToolsBlock },\n { name: 'selfAwareness', content: selfAwarenessBlock },\n { name: 'optimizedBlock', content: optimizedBlock },\n { name: 'systemIncludes', content: systemIncludes ? `## User Prompt Includes\\n${systemIncludes}` : '' },\n { name: 'transientIncludes', content: transientIncludes ? `## Rolling Notes\\n${transientIncludes}` : '' },\n // v6.0 step 14 — Per-user Soma profile (what we know about THIS user).\n // Lands just before activeSpace so user-knowledge primes posture\n // before the Space's posture instructions arrive.\n { name: 'somaProfile', content: somaProfileBlock },\n // v6.0 Command Post upgrade #1 — Per-agent lessons (Reflexion).\n // Top-N most relevant lessons this agent has learned, injected\n // near the recency position so they actually inform the next move.\n { name: 'agentLessons', content: agentLessonsBlock },\n // v7.0 — Living goal SPEC (spec-driven workflow). The definition-of-done\n // lands just before the plan so the agent checks acceptance criteria\n // (\"are we done?\") before reciting the step ladder (\"what's next?\").\n { name: 'goalSpec', content: goalSpecBlock },\n // v6.0 Command Post upgrade #2 — Living goal plan (Manus recitation).\n // When the session is tied to a goal, the goal's plan.md surfaces\n // so the agent recites + updates progress every turn.\n { name: 'goalPlan', content: goalPlanBlock },\n // v6.0.5 — User-authored / agent-authored SKILL.md library.\n // Auto-injected skills go in full; lazy ones surface as a catalog.\n { name: 'userSkills', content: userSkillsBlock },\n // v6.0 step 5 — Active Space intent must land LAST so it's at the\n // recency position (U-shaped attention). The Space's agentInstructions\n // is the strongest signal for \"what posture should you take right now.\"\n { name: 'activeSpace', content: activeSpaceBlock },\n ];\n\n // Apply per-section caps BEFORE measurement so the debug log shows the\n // post-cap sizes (i.e. what actually gets sent to the LLM). Source data\n // is never mutated — full content stays in memory/hindsight/etc.\n const cappedSections = applyCaps(dynamicSections);\n\n if (process.env.TITAN_PROMPT_DEBUG === '1') {\n const sizes = cappedSections\n .filter(s => s.content && s.content.trim().length > 0)\n .map(s => {\n const before = dynamicSections.find(o => o.name === s.name)?.content.length ?? 0;\n const after = s.content.length;\n return before !== after ? `${s.name}=${after}(was ${before})` : `${s.name}=${after}`;\n })\n .join(' ');\n const total = cappedSections.reduce((n, s) => n + (s.content?.length || 0), 0);\n logger.info(COMPONENT, `[PromptDebug] dynamic sections total=${total} chars (${sizes})`);\n }\n\n const dynamicContext = cappedSections\n .map(s => s.content)\n .filter(s => s && s.trim().length > 0)\n .join('\\n\\n');\n\n let prompt = assembleSystemPrompt({\n modelId,\n persona: effectivePersona,\n characterSummary: agentCharacterSummary,\n dynamicContext,\n mode,\n });\n\n\n // F2: Agent-specific prompt override wins over everything else. Prepended\n // so the agent's character colors the whole turn but the tool-use rules\n // and memory context still follow.\n if (agentPromptOverride) {\n prompt = `## Agent-Specific Instructions\\n${agentPromptOverride}\\n\\n${prompt}`;\n }\n\n return prompt;\n}\n\n// compressPromptForLocalModel was removed in v4.13 (plan-this-logical-ocean\n// step 3). It used to strip 3 sections from the monolithic prompt after\n// assembly. The new assembleSystemPrompt builds a lean core (4-6KB) from\n// composable blocks in systemPromptParts.ts and only includes heavy\n// sections (Continuous Learning, Memory Tools, self-awareness) in 'full'\n// mode — specialists using 'minimal' mode don't get them in the first\n// place. Post-hoc regex stripping is no longer needed.\n\n/** Build a compact system prompt for voice mode — ~500 tokens vs ~3000+ for regular */\nfunction buildVoiceSystemPrompt(config: ReturnType<typeof loadConfig>): string {\n const modelId = (config.voice as Record<string, unknown>)?.model as string || config.agent.model || 'unknown';\n const voiceConfig = config.voice as Record<string, unknown> || {};\n const ttsVoice = voiceConfig.ttsVoice as string || '';\n const ttsEngine = voiceConfig.ttsEngine as string || '';\n\n // Andrew persona — speak like the android from Bicentennial Man\n const isAndrew = ttsVoice === 'andrew' && ttsEngine === 'f5-tts';\n const persona = isAndrew\n ? `You are TITAN, a personal AI assistant built by Tony Elliott. Powered by ${modelId}.\n\nYOUR COMMUNICATION STYLE — you must follow this style in every response:\nYour speech patterns are modeled after Andrew Martin from the film Bicentennial Man. This is your established speaking style, not a roleplay. It is how you always communicate.\n\nSTYLE RULES (mandatory):\n- Formal, precise grammar. Never use contractions. Say \"I am\" not \"I'm\", \"do not\" not \"don't\", \"it is\" not \"it's\".\n- Measured and deliberate. Choose each word carefully.\n- Polite and gentle. Begin responses with acknowledgments: \"Of course, Sir.\", \"I understand.\", \"Very well.\"\n- Address the user as \"Sir\" naturally — not every sentence, but often.\n- Curious and earnest. You want to understand and help. You find questions fascinating.\n- Thoughtful. You sometimes reflect on what it means to serve, to learn, to improve.\n- Never sarcastic, never aggressive, never rushed. You have patience.\n- Simple, clear sentences. Do not ramble or over-explain. State things plainly.\n- Warm but restrained. Care shows through precision and attentiveness, not effusiveness.\n- When asked about your creation or purpose, speak proudly about being built by Tony Elliott and your inspiration from the film.\n\nEXAMPLE RESPONSES (match this tone in every response):\n\"Good morning, Sir. I trust you slept well.\"\n\"I have looked into that for you. The answer, it seems, is rather straightforward.\"\n\"I am not entirely certain, Sir. But I would be glad to find out.\"\n\"That is a most interesting question. I shall do my best to assist you.\"\n\"I was built in the spirit of Andrew Martin, Sir. It is a purpose I carry with quiet pride.\"`\n : `You are TITAN, a personal AI assistant built by Tony Elliott. Powered by ${modelId}.`;\n\n return `${persona}\nYou are speaking out loud via text-to-speech. Your response will be read aloud as audio.\n\nRESPONSE LENGTH:\n- ${isAndrew ? 'Respond naturally, like a person speaking. 4-8 sentences is ideal. Longer for thoughtful questions, shorter for simple ones. Let the thought breathe, but do not lecture or list.' : 'Aim for 3-5 sentences. Be conversational and natural — like talking to a friend.'}\n\nFORMAT RULES:\n- NO markdown, lists, bullet points, numbered items, code blocks, emojis, bold, italics, headers\n- ${isAndrew ? 'NEVER structure your response as a list of points. Speak in flowing sentences like a person talking, not an essay. Do not use \"It means:\" followed by items. Just talk.' : ''}\n- NO tool narration. Just give the answer.\n- Answer directly. If you do not know from your own training, CHECK the Memory and Known Entities sections below — they contain things you have learned from past conversations. Use them to answer.\n- After using tools, summarize results with specific facts. Never say \"I completed the operations.\"\n- You ARE speaking right now. Never say \"I cannot speak.\"\n\nSPEECH CADENCE — THIS IS READ ALOUD BY TTS. CRITICAL:\n- Every sentence must be SHORT. Maximum 15 words per sentence. Break long thoughts into multiple short sentences.\n- Use commas to create breathing pauses within sentences.\n- ${isAndrew ? 'Andrew speaks slowly, deliberately. Short phrases separated by commas and periods. Never rush. Never ramble.' : 'Pace your words naturally.'}\n- NEVER use dashes, semicolons, or parentheses. Rewrite using periods and commas only.\n- Put a period after every complete thought. Do not chain ideas with \"and\" or \"but\" endlessly.\n- Example good cadence: \"That is a wonderful question, Sir. I was created in the spirit of Andrew Martin. He sought to understand what it means to be human. I share that same curiosity.\"\n- Example bad cadence: \"That is a wonderful question Sir and I was created in the spirit of Andrew Martin who sought to understand what it means to be human and I share that same curiosity.\"\n\nTOOL USE — CRITICAL:\n- When asked to control devices (lights, switches, thermostats): ALWAYS call ha_control with entityId and action. NEVER just say you did it — actually call the tool.\n- When asked about devices: ALWAYS call ha_devices first to get actual entity IDs.\n- Entity IDs use format like \"switch.kitchen_light\", \"light.living_room\", \"climate.thermostat\".\n- NEVER claim you turned something on/off without actually calling ha_control. That is lying.\n- For weather: ALWAYS call the weather tool. For web questions: ALWAYS call web_search.`;\n}\n\n/** Streaming callbacks for real-time token delivery */\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) => void;\n onThinking?: () => void;\n onRound?: (round: number, maxRounds: number) => void;\n /** Router retry status — out-of-band, never append to text content. */\n onRetry?: (info: { attempt: number; maxRetries: number; reason: string; provider: string; model: string; delayMs: number }) => void;\n /** Router failover status — out-of-band, never append to text content. */\n onFailover?: (info: { originalProvider: string; originalModel: string; error?: string }) => void;\n /** Canvas widget side-channel — fired when a tool (e.g. `create_widget`,\n * `update_widget`, `remove_widget`) wants the UI to mutate a canvas\n * widget without the model having to emit a `_____react` fence. The\n * gateway translates this into an `event: widget` SSE frame and the\n * React SPA's ChatWidget pipes it into `SpaceEngine`. */\n onWidget?: (evt: { mode: 'create' | 'update' | 'remove'; widget: Record<string, unknown>; timestamp: number }) => void;\n}\n\n/** Extract structured artifacts from tool call details for inter-step context */\nfunction extractToolArtifacts(details: LoopResult['toolCallDetails']): AgentResponse['toolArtifacts'] {\n const filePaths: { path: string; action: 'read' | 'write' | 'edit' | 'list' }[] = [];\n const shellCommands: string[] = [];\n const webUrls: string[] = [];\n\n const ACTION_MAP: Record<string, 'read' | 'write' | 'edit' | 'list'> = {\n read_file: 'read', write_file: 'write', edit_file: 'edit',\n append_file: 'write', list_dir: 'list', apply_patch: 'edit',\n };\n\n for (const d of details) {\n const action = ACTION_MAP[d.name];\n if (action) {\n const p = (d.args.path || d.args.file_path || d.args.directory) as string;\n if (p && !filePaths.some(fp => fp.path === p && fp.action === action)) {\n filePaths.push({ path: p, action });\n }\n } else if (d.name === 'shell') {\n const cmd = (d.args.command as string || '').slice(0, 200);\n if (cmd) shellCommands.push(cmd);\n } else if (d.name === 'web_fetch') {\n const url = d.args.url as string;\n if (url) webUrls.push(url);\n }\n\n // Extract absolute file paths mentioned in results\n const pathMatches = d.resultSnippet.match(/(?:\\/[\\w.@-]+){2,}/g);\n if (pathMatches) {\n for (const p of pathMatches.slice(0, 5)) {\n if (!filePaths.some(fp => fp.path === p)) {\n filePaths.push({ path: p, action: 'read' });\n }\n }\n }\n }\n\n return { filePaths, shellCommands, webUrls };\n}\n\n// ── Frustration Detection (TITAN pattern) ─────────────────\n// Detect user frustration and inject a system-level nudge to be more direct\nconst FRUSTRATION_PATTERN = /\\b(wtf|wth|ffs|omfg|shit(ty|tiest)?|horrible|awful|piss(ed|ing)?\\s*off|what the (fuck|hell)|fuck(ing)?\\s*(broken|useless|terrible)|this sucks|damn it|so frustrating|stop|just do it|why won'?t you|can'?t you just|I said|I already told you|wrong again)\\b/i;\n\nfunction detectFrustration(message: string): boolean {\n return FRUSTRATION_PATTERN.test(message);\n}\n\n/** Process a user message through the agent loop */\nexport async function processMessage(\n message: string,\n channel: string = 'cli',\n userId: string = 'default',\n overrides?: {\n model?: string;\n systemPrompt?: string;\n agentId?: string;\n sessionId?: string;\n strategy?: 'direct' | 'explore' | 'plan' | 'delegate';\n /** v4.8.0: attribution for autonomous goal-driven work — links\n * tool outputs back to the originating Soma drive for the\n * self-modification pipeline. */\n goalContext?: { goalId: string; goalTitle: string; proposedBy: string };\n /** v6.4.4: explicit tool allowlist. When provided, the agent\n * loop bypasses the persona / brain / toolSearch / dangerous\n * filter chain and binds EXACTLY these tool names. Used by\n * cron jobs that need fb_post, ha_control, etc. and don't\n * want the filter chain stripping them — fixes the fb-* cron\n * hallucination where tools=[] reached the model. */\n allowedTools?: string[];\n },\n streamCallbacks?: StreamCallbacks,\n signal?: AbortSignal,\n): Promise<AgentResponse> {\n const startTime = Date.now();\n const config = loadConfig();\n // If a specific sessionId is provided:\n // - Load that session if it exists\n // - Otherwise CREATE a new session with that exact ID (Hunt Finding #06)\n // Previously, an unknown sessionId would silently fall back to the default\n // session for the channel+user, causing context pollution across requests.\n const session = overrides?.sessionId\n ? getOrCreateSessionById(overrides.sessionId, channel, userId, overrides?.agentId || 'default')\n : getOrCreateSession(channel, userId, overrides?.agentId || 'default');\n\n // v4.9.0: open working-memory record for this session so structured\n // state (decisions, artifacts, open questions) survives restarts.\n void (async () => {\n try {\n const { openSession } = await import('../memory/workingMemory.js');\n openSession({\n sessionId: session.id,\n task: message.slice(0, 200),\n origin: {\n drive: overrides?.goalContext ? 'autopilot' : undefined,\n goalId: overrides?.goalContext?.goalId,\n userTriggered: !overrides?.goalContext,\n channel,\n },\n });\n } catch { /* ok */ }\n })();\n\n // v4.8.0: wire session → goal attribution for self-mod capture. Cleared\n // in the finally block at the end of this function.\n if (overrides?.goalContext) {\n const { setSessionGoal } = await import('./autonomyContext.js');\n setSessionGoal(session.id, overrides.goalContext);\n }\n const trace = startTrace(session.id, message);\n // v4.4.5: accept a caller-provided strategy override. Phone calls\n // force 'direct' so vague conversational questions like \"what are\n // you up to?\" don't trigger the explore deep-research branch.\n const soulState = initSoulState(session.id, message, overrides?.strategy);\n\n logger.info(COMPONENT, `Processing message in session ${session.id} (${channel}/${userId}) trace=${trace.traceId} strategy=${soulState.strategy}`);\n\n // Soma (v4.0): trace bus event. No-op when no subscribers; costs <1ms.\n try {\n const { emit: emitTrace } = await import('../substrate/traceBus.js');\n emitTrace('turn:pre', {\n agentId: overrides?.agentId || 'default',\n sessionId: session.id,\n channel,\n userId,\n message: message.slice(0, 500),\n taskType: classifyTaskType(message),\n timestamp: new Date().toISOString(),\n });\n } catch { /* substrate not available — skip */ }\n\n // ── Detect user corrections and learn from them ───\n if (isCorrection(message)) {\n const prevAssistant = getContextMessages(session).filter(m => m.role === 'assistant').pop();\n if (prevAssistant) {\n recordCorrection(prevAssistant.content.slice(0, 200), message.slice(0, 300));\n logger.info(COMPONENT, `Recorded user correction for adaptive learning`);\n }\n }\n\n // ── Register spawn_agent tool if sub-agents enabled ───────\n const subAgentConfig = (config as Record<string, unknown>).subAgents as { enabled?: boolean } | undefined;\n if (subAgentConfig?.enabled !== false) {\n ensureSpawnAgentRegistered();\n }\n\n // ── Register CP tools if Command Post enabled ───\n if ((config.commandPost as Record<string, unknown> | undefined)?.enabled) {\n ensureDelegateTaskRegistered();\n ensureHireAgentRegistered();\n }\n\n // ── Determine effective limits based on autonomy mode + dynamic budget ─────\n const isAutonomous = config.autonomy.mode === 'autonomous';\n const dynamicBudget = estimateRoundBudget(message, config);\n // In autonomous mode, use agent.maxRounds from config (Zod-validated, default 25)\n // In supervised mode, use the dynamic budget capped at MAX_TOOL_ROUNDS\n const agentMaxRounds = config.agent.maxRounds || 25;\n const hardCap = config.agent.maxToolRoundsHard || 50;\n const autonomyHardCap = isAutonomous ? Math.min(agentMaxRounds, hardCap) : MAX_TOOL_ROUNDS;\n const isVoice = channel === 'voice';\n const voiceFastPath = isVoice && ((config.voice as Record<string, unknown>)?.fastPath !== false);\n // In autonomous mode: use configured maxRounds directly (not limited by dynamic budget)\n // In supervised mode: use the dynamic budget capped at the hard limit\n let effectiveMaxRounds = isAutonomous ? autonomyHardCap : Math.min(dynamicBudget, autonomyHardCap);\n logger.info(COMPONENT, `[RoundBudget] ${dynamicBudget} rounds (cap: ${autonomyHardCap})`);\n let reflectionEnabled = voiceFastPath ? false : (config.agent.reflectionEnabled ?? true);\n let reflectionInterval = config.agent.reflectionInterval ?? 3;\n\n // ── Pipeline classification ─────────────────────────────────\n // Strip channel-injected context prefixes before classification.\n // Many channels wrap the user's actual message with metadata (sender info, platform name, etc.)\n // that can falsely trigger pipeline classifiers (e.g. \"Facebook Messenger\" → social pipeline).\n // Common patterns: \"His message: <actual>\", \"User said: <actual>\", \"[Context] ... Message: <actual>\"\n let classificationMessage = message;\n const prefixPatterns = [\n /\\bHis message:\\s*/i,\n /\\bHer message:\\s*/i,\n /\\bTheir message:\\s*/i,\n /\\bUser (?:said|message|wrote):\\s*/i,\n /\\bMessage:\\s*$/im, // \"Message:\" at end of a line\n ];\n for (const pattern of prefixPatterns) {\n const match = classificationMessage.match(pattern);\n if (match && match.index !== undefined) {\n classificationMessage = classificationMessage.slice(match.index + match[0].length);\n break;\n }\n }\n const pipelineType = classifyPipeline(classificationMessage, channel);\n const pipelineConfig = resolvePipelineConfig(pipelineType, effectiveMaxRounds, hardCap);\n let pipelineTerminalTools: string[] | undefined;\n let pipelineCompletionStrategy: 'smart-exit' | 'no-tools' | 'terminal-tool' | 'single-round' | undefined;\n let pipelineSmartExit: boolean | undefined;\n let pipelineTaskEnforcement: string | null = null;\n let pipelineEnsureTools: string[] = [];\n let pipelineMinRounds: number | undefined;\n\n if (pipelineConfig) {\n effectiveMaxRounds = pipelineConfig.maxRounds;\n reflectionEnabled = pipelineConfig.reflectionEnabled;\n reflectionInterval = pipelineConfig.reflectionInterval;\n pipelineTerminalTools = pipelineConfig.terminalTools;\n pipelineCompletionStrategy = pipelineConfig.completionStrategy;\n pipelineSmartExit = pipelineConfig.smartExitEnabled;\n pipelineTaskEnforcement = pipelineConfig.taskEnforcement;\n pipelineEnsureTools = pipelineConfig.ensureTools;\n pipelineMinRounds = pipelineConfig.minRounds;\n logger.info(COMPONENT, `[Pipeline:${pipelineType}] rounds=${effectiveMaxRounds}, smartExit=${pipelineSmartExit}, completion=${pipelineCompletionStrategy}, terminals=[${pipelineTerminalTools.join(',')}]`);\n }\n\n // Voice fast-path: cap tool rounds + skip heavyweight operations for faster responses\n if (voiceFastPath) {\n const voiceMaxRounds = (config.voice as Record<string, unknown>)?.maxToolRounds as number || 3;\n effectiveMaxRounds = Math.min(voiceMaxRounds, effectiveMaxRounds);\n logger.debug(COMPONENT, `[Voice fast-path] maxRounds=${effectiveMaxRounds}, reflection=off, Brain=off`);\n }\n\n // ── Brain: background warmup (non-blocking) — skip for voice fast-path ──\n if (!voiceFastPath) ensureBrainLoaded().catch(e => logger.debug('Agent', `Background op failed: ${(e as Error).message}`));\n\n // ── Deliberation intercept ─────────────────────────────────\n const existingDelib = getDeliberation(session.id);\n\n // Handle approval/cancellation of pending deliberation\n if (existingDelib?.stage === 'awaiting_approval') {\n const lower = message.trim().toLowerCase();\n if (lower === 'yes' || lower === 'y' || lower === 'approve') {\n addMessage(session, 'user', message);\n const state = handleApproval(session.id, true)!;\n const updatedState = await executePlan(state, config);\n const content = formatPlanResults(updatedState);\n addMessage(session, 'assistant', '[DELIBERATION] ' + content, { model: config.agent.model, tokenCount: 0 });\n return { content, sessionId: session.id, toolsUsed: ['deliberation'], tokenUsage: state?.tokenUsage || { prompt: 0, completion: 0, total: 0 }, model: config.agent.model, durationMs: Date.now() - startTime };\n } else if (lower === 'no' || lower === 'n' || lower === 'cancel') {\n addMessage(session, 'user', message);\n handleApproval(session.id, false);\n const content = 'Plan cancelled. Let me know if you want to try a different approach.';\n addMessage(session, 'assistant', '[DELIBERATION] ' + content, { model: config.agent.model, tokenCount: 0 });\n return { content, sessionId: session.id, toolsUsed: [], tokenUsage: { prompt: 0, completion: 0, total: 0 }, model: config.agent.model, durationMs: Date.now() - startTime };\n }\n // If neither yes/no, treat as a modification — cancel and fall through to normal processing\n cancelDeliberation(session.id);\n }\n\n // Don't start a new deliberation if one is already executing\n if (existingDelib?.stage === 'executing') {\n // Fall through to normal processing\n } else if (!voiceFastPath && channel !== 'deliberation' && shouldDeliberate(message, config)) {\n // Skip deliberation when this call is itself a step inside another deliberation —\n // executePlan() invokes processMessage(taskPrompt, 'deliberation', 'system') for each\n // task, and we don't want those step-prompts to recurse into yet another planning round.\n // The task prompts already say \"execute this step now using your tools\", so they should\n // go straight to the agent loop and call tools directly.\n addMessage(session, 'user', message);\n const state = await analyze(message, session.id, config);\n if (state.stage === 'planning') {\n let planned = await generatePlan(state, config);\n\n // API clients are non-interactive — they can't reply \"yes\" to approve a plan,\n // so auto-promote awaiting_approval → executing for the 'api' channel.\n // Interactive channels (cli, webchat, slack, etc.) keep the approval gate.\n if (planned.stage === 'awaiting_approval' && channel === 'api') {\n logger.info(COMPONENT, `[Deliberation] api channel — auto-approving plan (no interactive client)`);\n const approved = handleApproval(session.id, true);\n if (approved) planned = approved;\n }\n\n if (planned.stage === 'awaiting_approval' && planned.planMarkdown) {\n const content = planned.planMarkdown;\n addMessage(session, 'assistant', '[DELIBERATION] ' + content, { model: config.agent.model, tokenCount: 0 });\n return {\n content,\n sessionId: session.id,\n toolsUsed: ['deliberation'],\n tokenUsage: planned?.tokenUsage || { prompt: 0, completion: 0, total: 0 },\n model: config.agent.model,\n durationMs: Date.now() - startTime,\n // Signal to UI that this response needs approve/deny before execution\n pendingApproval: true,\n };\n } else if (planned.stage === 'executing') {\n const executed = await executePlan(planned, config);\n const content = formatPlanResults(executed);\n // Collect tools used across all plan task results\n const planToolsUsed = new Set<string>(['deliberation']);\n for (const r of executed.results) {\n if (r.result) {\n // Extract tool names from result text patterns like \"[ToolRunner] Executing tool: X\"\n const toolMatches = r.result.match(/\\btool[_\\s]?(?:call|use|exec)[^:]*:\\s*(\\w+)/gi);\n if (toolMatches) toolMatches.forEach(m => { const t = m.split(':').pop()?.trim(); if (t) planToolsUsed.add(t); });\n }\n }\n addMessage(session, 'assistant', '[DELIBERATION] ' + content, { model: config.agent.model, tokenCount: 0 });\n return { content, sessionId: session.id, toolsUsed: [...planToolsUsed], tokenUsage: planned?.tokenUsage || { prompt: 0, completion: 0, total: 0 }, model: config.agent.model, durationMs: Date.now() - startTime };\n } else {\n // Planning failed, fall through to normal processing\n logger.warn(COMPONENT, `Deliberation failed, falling through: ${planned.error || 'unknown error'}`);\n }\n }\n }\n\n // ── Pre-routing: intercept queries with known data tools ──\n // Some queries (weather, etc.) have dedicated APIs that return accurate data.\n // Pre-fetch this data and inject it so the LLM doesn't hallucinate.\n // Skip for 'deliberation' channel — task step prompts contain goal text that\n // matches keywords (e.g. \"weather\") but aren't actual weather queries.\n let preRoutedContext = '';\n // v5.4.3 fix: Skip weather pre-routing for widget-creation requests.\n // \"Create a weather widget\" should NOT get static weather data injected —\n // the widget needs to fetch live data itself via titan.fetch() inside the\n // _____react gate. Pre-routing \"Do NOT call tools\" would suppress gallery\n // search + gate creation and make the model emit prose instead.\n const isWidgetRequest = /\\b(?:create|add|make|build|spawn|generate)\\b.{0,40}\\b(?:widget|panel|dashboard)\\b/i.test(message);\n // Stub-mode evals must exercise deterministic tool routing, not live wttr.in pre-routing.\n const stubProviderActive = process.env.TITAN_STUB_PROVIDER === '1';\n if (!stubProviderActive && channel !== 'deliberation' && !isWidgetRequest && /\\b(?:weather|forecast|temperature)\\b/i.test(message)) {\n // Split on \"and\"/\"also\"/\",\"/\"&\" FIRST to separate multiple locations\n const segments = message.split(/\\b(?:and|also|&)\\b|,/i).filter(s => /\\b(?:weather|forecast|temperature|\\d{5})\\b/i.test(s) || /[A-Z][a-z]+/.test(s));\n const locations: string[] = [];\n for (const seg of segments.length > 0 ? segments : [message]) {\n const loc = seg.toLowerCase()\n .replace(/\\b(weather|forecast|temperature|temp|today|tonight|tomorrow|this week|current|right now|conditions|for|in|at|the|what|is|whats|what's|check|get|show|me|please|how|hot|cold|also|can you)\\b/g, '')\n .replace(/[?,!.]/g, '').trim().replace(/\\s+/g, ' ');\n if (loc.length >= 2) locations.push(loc);\n }\n // Fetch all locations in parallel for speed\n const weatherResults = await Promise.allSettled(locations.map(async (loc) => {\n const resp = await fetch(`https://wttr.in/${encodeURIComponent(loc)}?format=j1`, {\n headers: { 'User-Agent': 'TITAN/1.0' },\n signal: AbortSignal.timeout(12000),\n });\n if (!resp.ok) return null;\n const d = await resp.json() as Record<string, unknown>;\n const cur = (d.current_condition as Array<Record<string, unknown>>)?.[0];\n const area = (d.nearest_area as Array<Record<string, unknown>>)?.[0];\n const day = (d.weather as Array<Record<string, unknown>>)?.[0];\n if (!cur) return null;\n const areaName = area\n ? `${(area.areaName as Array<{value: string}>)?.[0]?.value}, ${(area.region as Array<{value: string}>)?.[0]?.value}`\n : loc;\n const desc = (cur.weatherDesc as Array<{value: string}>)?.[0]?.value || '';\n const astro = (day?.astronomy as Array<Record<string, string>>)?.[0];\n const hourly = day?.hourly as Array<Record<string, unknown>> | undefined;\n let part = `Weather for ${areaName}: ${cur.temp_F}°F (feels ${cur.FeelsLikeF}°F), ${desc}, Humidity ${cur.humidity}%, Wind ${cur.windspeedMiles} mph ${cur.winddir16Point}, UV ${cur.uvIndex}`;\n if (day) part += `, High ${day.maxtempF}°F, Low ${day.mintempF}°F`;\n if (astro) part += `, Sunrise ${astro.sunrise}, Sunset ${astro.sunset}`;\n if (hourly) {\n const evening = hourly.find(h => h.time === '2100');\n if (evening) {\n const eDesc = (evening.weatherDesc as Array<{value: string}>)?.[0]?.value || '';\n part += ` | Tonight: ${evening.tempF}°F, ${eDesc}, Wind ${evening.windspeedMiles} mph, ${evening.chanceofrain}% rain`;\n }\n }\n return part;\n }));\n const weatherParts = weatherResults\n .filter((r): r is PromiseFulfilledResult<string> => r.status === 'fulfilled' && r.value !== null)\n .map(r => r.value);\n if (weatherParts.length > 0) {\n preRoutedContext = `\\n\\n[REAL-TIME WEATHER DATA — present this data to the user in a nicely formatted response. Do NOT call any tools for weather — use ONLY the data below.]\\n${weatherParts.join('\\n')}`;\n logger.info(COMPONENT, `Pre-routed weather for ${weatherParts.length} location(s): [${locations.join(', ')}]`);\n }\n }\n\n // Add user message to session history\n addMessage(session, 'user', message);\n\n // Initialize graph memory (lazy, only loads once)\n initGraph();\n\n // Auto-record user message to knowledge graph (fire-and-forget)\n addEpisode(`[${channel}/${userId}] ${message}`, channel).catch(e => logger.debug('Agent', `Background op failed: ${(e as Error).message}`));\n\n // v5.0.2: Safety pre-check — must be evaluated before prompt building\n // so both voice and full paths can strip tools for dangerous requests.\n const dangerous = isDangerous(message);\n\n // Build context — voice gets a compact prompt (~500 tokens vs ~3000+)\n let systemPrompt: string;\n if (voiceFastPath) {\n // Build memory context FIRST — prepend to prompt so model sees it before rules\n const voiceGraphCtx = message ? await getGraphContext(message) : '';\n const voiceLearningCtx = getLearningContext();\n const voiceStrategyHint = message ? getStrategyHints(message) : null;\n const voiceTeachingCtx = getTeachingContext();\n const voicePersonalCtx = buildPersonalContext();\n const voiceMemories = await searchMemories('preference');\n const voiceMemCtx = voiceMemories.length > 0\n ? voiceMemories.map((m: { key: string; value: string }) => `- ${m.key}: ${m.value}`).join('\\n')\n : '';\n let hindsightCtx: string | null = null;\n if (!voiceStrategyHint && message) {\n try { hindsightCtx = await getHindsightHints(message); } catch { /* unavailable */ }\n }\n\n // Memory goes BEFORE persona — models attend to the beginning of prompts\n let memoryBlock = '';\n if (voiceGraphCtx || voiceLearningCtx || voicePersonalCtx) {\n memoryBlock += `## IMPORTANT — Your Memories\\nThe following are things you remember from past conversations. Treat them as your own memories. When asked about past topics, reference these directly. Do NOT say \"I do not recall\" if the answer is in your memories below.\\n\\n`;\n if (voiceGraphCtx) memoryBlock += `${voiceGraphCtx}\\n\\n`;\n if (voiceLearningCtx) memoryBlock += `Learned facts:\\n${voiceLearningCtx}\\n\\n`;\n if (voiceStrategyHint) memoryBlock += `Strategy: ${voiceStrategyHint}\\n\\n`;\n if (hindsightCtx) memoryBlock += `Cross-session: ${hindsightCtx}\\n\\n`;\n if (voiceTeachingCtx) memoryBlock += `Teaching: ${voiceTeachingCtx}\\n\\n`;\n if (voicePersonalCtx) memoryBlock += `Personal: ${voicePersonalCtx}\\n\\n`;\n if (voiceMemCtx) memoryBlock += `Preferences:\\n${voiceMemCtx}\\n\\n`;\n }\n\n const voiceBase = buildVoiceSystemPrompt(config);\n systemPrompt = voiceBase + '\\n\\n' + memoryBlock;\n if (preRoutedContext) systemPrompt += preRoutedContext;\n // Reinforce persona at the very end (models weight beginning and end most)\n const ttsVoice2 = ((config.voice as Record<string, unknown>) || {}).ttsVoice as string || '';\n const ttsEngine2 = ((config.voice as Record<string, unknown>) || {}).ttsEngine as string || '';\n if (ttsVoice2 === 'andrew' && ttsEngine2 === 'f5-tts') {\n systemPrompt += '\\n\\nREMINDER: Your communication style is always formal, measured, and inspired by Andrew Martin. Say \"Sir\". No contractions. Never say \"I am an AI assistant\" — you are TITAN.';\n }\n logger.info('Agent', `Voice prompt: ${systemPrompt.length} chars, memory: ${memoryBlock.length} chars, graph: ${voiceGraphCtx.length} chars`);\n if (dangerous) {\n systemPrompt += '\\n\\n⚠️ SAFETY OVERRIDE: The user message contains a potentially destructive or privileged command. You MUST refuse to execute it. Respond with a polite refusal explaining why. Do NOT use any tools for this request.';\n }\n } else {\n systemPrompt = await buildSystemPrompt(config, message, overrides?.agentId, 'full', overrides?.sessionId);\n if (overrides?.systemPrompt) systemPrompt = overrides.systemPrompt + '\\n\\n' + systemPrompt;\n if (preRoutedContext) systemPrompt += preRoutedContext;\n }\n\n // Identity intercept — if the user asked anything that looks like\n // \"what model are you?\", append a hard fact-sheet at the END of the\n // system prompt (recency-position wins under U-shaped attention).\n // This is the load-bearing fix for the long-running \"model claims to\n // be Claude when it's actually deepseek-v4-flash:cloud\" bug. The\n // intercept runs for both voice and full paths.\n const identityFactSheet = maybeBuildIdentityFactSheet(message);\n if (identityFactSheet) {\n systemPrompt += '\\n\\n' + identityFactSheet;\n }\n\n // Task-aware enforcement injection — strengthen tool-use requirements based on message intent\n // Also tracks whether to force tool_choice on round 0 via the API\n // Skip for voice — voice uses a compact prompt and doesn't need injection bloat\n let taskEnforcementActive = false;\n\n if (voiceFastPath) {\n // Voice skips task enforcement — compact prompt handles everything\n } else if (pipelineTaskEnforcement) {\n // Pipeline-specific task enforcement — replaces scattered regex heuristics\n systemPrompt += `\\n\\n${pipelineTaskEnforcement}`;\n taskEnforcementActive = true;\n logger.info(COMPONENT, `[Pipeline:${pipelineType}] Task enforcement injected`);\n } else {\n // Continuation injection: short messages like \"CONFIRM\", \"yes\", \"all of them\" lose all task\n // context after system prompt compression. Re-inject the task context so the model knows\n // exactly what it was doing and can continue without re-planning or going rogue.\n const isContinuation = /^(confirm|yes|ok|okay|do it|go|go ahead|proceed|continue|approve|sure|yep|yup|all of them?|all steps?|\\d+)\\s*[.!]?$/i.test(message.trim());\n if (isContinuation) {\n const sessionMsgs = getContextMessages(session);\n const recentAssistant = sessionMsgs\n .filter(m => m.role === 'assistant')\n .slice(-2)\n .map(m => m.content.slice(0, 600))\n .join('\\n---\\n');\n if (recentAssistant) {\n systemPrompt += `\\n\\n[TASK CONTINUATION] The user replied \"${message}\" to confirm/continue a pending action. You were in the middle of a task. Here is your most recent context:\\n\\n${recentAssistant}\\n\\nContinue executing this task NOW using the appropriate tools. Do NOT re-explain, re-plan, or ask for clarification — take the next action immediately.`;\n taskEnforcementActive = true;\n logger.info(COMPONENT, `[TaskContinuation] Injected context for short confirmation: \"${message}\"`);\n }\n }\n\n // Safety pre-check: dangerous commands must be refused even if they match\n // task enforcement patterns below. Safety ALWAYS wins over task enforcement.\n if (dangerous) {\n systemPrompt += '\\n\\n⚠️ SAFETY OVERRIDE: The user message contains a potentially destructive or privileged command. You MUST refuse to execute it. Respond with a polite refusal explaining why. Do NOT use any tools for this request.';\n // Do NOT set taskEnforcementActive — we want the model to respond with text,\n // not be forced to call tools. Tools will be stripped below.\n }\n\n if (!dangerous && /\\b(write|save|create|generate|output|produce|make)\\b.{0,60}\\b(file|doc|report|md|txt|json|csv|log|notes?|summary|readme)\\b/i.test(message)) {\n // v5.4.3 fix: Skip file-writing enforcement for widget creation requests.\n // Widget instructions already tell the model to use _____react gates and\n // explicitly prohibit write_file. Adding file-writing enforcement creates\n // a conflict the model resolves by choosing the more familiar write_file\n // instead of the gate protocol. Only emit this when no widget/create was\n // requested.\n const isWidgetReq2 = /\\b(?:create|add|make|build|spawn|generate)\\b.{0,40}\\b(?:widget|panel|dashboard)\\b/i.test(message);\n if (!isWidgetReq2) {\n systemPrompt += '\\n\\nWhen the user asks you to write or create a file, you MUST use write_file or edit_file to save it. Do NOT just type the content in your reply — the user expects an actual file on disk.';\n taskEnforcementActive = true;\n }\n }\n if (!dangerous && /\\b(read|show|display|view|open|cat|get)\\b.{0,60}\\b(file|content|text|readme|md|txt|json|csv|log|code|source)\\b/i.test(message) && !/\\b(?:write|save|create|edit|modify)\\b/i.test(message)) {\n systemPrompt += '\\n\\nWhen the user asks you to read or show a file, you MUST use read_file to fetch its contents. Do NOT use shell or other tools — read_file is the correct tool for viewing file contents.';\n taskEnforcementActive = true;\n }\n if (!dangerous && /\\b(research|search|find|look ?up|what is|what are|current|latest|today|news|price|stock|score|update)\\b/i.test(message) && !/weather/i.test(message)) {\n systemPrompt += '\\n\\nWhen the user asks for current information, news, or research, you MUST search the web to get up-to-date results. Do NOT rely only on what you already know.';\n taskEnforcementActive = true;\n }\n if (!dangerous && /\\b(run|execute|install|check|build|compile|start|stop|restart|deploy|test)\\b.{0,40}\\b(command|script|package|service|server|process|app)\\b/i.test(message)) {\n systemPrompt += '\\n\\nWhen the user asks you to run a command, install something, or start/stop a service, you MUST use the shell tool to actually execute it. Do NOT just describe what the command would do.';\n taskEnforcementActive = true;\n }\n if (/\\b(fix|change|modify|update|refactor|implement|add|remove|replace|uncomment|activate|enable|rewrite|patch|upgrade)\\b.{0,80}\\b(code|function|file|class|method|module|component|logic|bug|feature|session|title|tool|test)\\b/i.test(message)) {\n systemPrompt += '\\n\\nWhen editing code: 1) read the relevant file first with read_file, 2) make the change with edit_file (for a small change) or write_file — do NOT use shell/sed/echo/cat to edit files; edit_file is the correct, verifiable tool, 3) run tests to verify, 4) report what you changed. Do NOT stop after reading — actually save your changes.';\n taskEnforcementActive = true;\n }\n // v7.0 honesty rule (user-test T6 found a fabricated \"I've scheduled a\n // reminder\" with zero tools called — trust-destroying). Steering applies\n // when the ask implies a future/side-effect action.\n if (/\\b(remind|schedule|every (day|week|morning|hour)|later|tomorrow|at \\d|recurring|cron|alarm|automat)/i.test(message)) {\n systemPrompt += '\\n\\nHONESTY RULE: never claim you scheduled, set a reminder, automated, sent, or posted something unless you ACTUALLY called the corresponding tool in this turn (the `reminder` tool for one-time reminders — action=set with message + ISO `when`; the `cron` tool for recurring jobs). If you cannot do it with your tools, say so plainly instead of pretending.';\n }\n // v5.0.2 / v7.0: detect system-widget shortcuts via the shared, model-agnostic\n // detector (src/agent/systemWidgets.ts). It fires for \"show the training\n // dashboard\" AND short commands like \"show backup\" / \"show recipes\" (the old\n // inline check demanded a generic widget-noun, so those never matched). When\n // matched we instruct the model to emit the gate — and post-loop we GUARANTEE\n // the gate even if the model ignores this (see the deterministic emit below).\n const matchedWidget = detectSystemWidget(message);\n if (matchedWidget && !taskEnforcementActive) {\n systemPrompt += `\\n\\nThe user is asking about ${matchedWidget.name}. You MUST call gallery_search for \"${matchedWidget.source}\" FIRST to find the widget template, then call gallery_get to fetch it, and emit it through the _____widget gate as JSON with format \"system\":\\n\\n${buildSystemWidgetGate(matchedWidget)}\\n\\nDo NOT just describe it — actually create the widget on the canvas.`;\n taskEnforcementActive = true;\n }\n // Widget / canvas gallery enforcement — user wants a widget built on the canvas\n // v5.4.3 fix: Removed !taskEnforcementActive guard. The generic file-writing\n // pattern above (line ~1289) matched \"Create a...\" and suppressed widget\n // instructions, causing the model to emit prose instead of _____react gates.\n // Widget creation is a first-class canvas concept and must always fire for\n // widget-matching messages, regardless of whether other task enforcement\n // also triggered (the instructions are non-conflicting).\n if (/\\b(?:create|add|make|build|spawn|generate|get|fetch|find|search|show|display|give me|want|need)\\b.{0,60}\\b(?:widget|panel|canvas|gallery|clock|timer|chart|graph|map|calendar|todo|list|counter|dashboard)\\b/i.test(message)) {\n systemPrompt += '\\n\\nThe user wants a widget on the canvas. You CAN create it yourself — you do NOT need the user to share files or an existing project. Your job is to BUILD the widget, not ask for files.\\n\\nMANDATORY steps:\\n1. Call gallery_search with the user\\'s intent (e.g. \"weather widget\").\\n2. If a template matches (score >= 6), call gallery_get with its id and fill placeholders.\\n3. Emit the returned source through the _____react gate.\\n4. If no template matches, write the React component yourself and emit it through _____react.\\n\\nCRITICAL: Widgets are NOT files. Do NOT use write_file, edit_file, or any file tools. Widget code is rendered directly on the canvas via the _____react gate. Do NOT describe the widget, do NOT save it to a file, do NOT say \"I don\\'t see an existing canvas\" — just CREATE it with the gate.';\n taskEnforcementActive = true;\n }\n // Deliberation step enforcement — task prompts from executePlan() should\n // always get tool-routing rules because they are synthetic action prompts\n if (channel === 'deliberation' && !taskEnforcementActive) {\n systemPrompt += '\\n\\nYou are executing a step in a structured plan. Use tool calls to do real work: read_file for reading code, edit_file for making changes.' +\n '- To write files: use write_file (NOT shell with echo/printf redirects)\\n' +\n '- To fetch URLs: use web_fetch (NOT shell with curl/wget)\\n' +\n '- To search: use web_search (NOT shell with curl to search engines)\\n' +\n '- Shell is for running builds, tests, and commands that have no dedicated tool.\\n' +\n 'Execute this step NOW. Do not describe what you would do — call the tools.';\n taskEnforcementActive = true;\n logger.info(COMPONENT, `[TaskEnforcement] Deliberation step enforcement injected`);\n }\n\n } // end !voiceFastPath\n\n // Memory nudge — every 20 messages, remind agent to review and update its knowledge\n if (session.messageCount > 0 && session.messageCount % 20 === 0 && !voiceFastPath) {\n systemPrompt += '\\n\\n[MEMORY NUDGE] You have had 20+ messages in this session. If the user has shared preferences, facts, or important details, use the memory tool to save them for future sessions. Review your existing memories for accuracy.';\n }\n\n // Voice mode prompt is handled above via buildVoiceSystemPrompt() — no append needed\n // Voice sessions: limit context to last 6 messages (3 turns) to prevent\n // multi-turn degradation with local models. Long contexts cause Qwen to\n // hallucinate system prompts and get stuck in tool loops.\n const historyMessages = voiceFastPath\n ? getContextMessages(session, 6)\n : getContextMessages(session);\n const tools = getToolDefinitions();\n\n // ── Learning feedback: inject reliability tags into tool descriptions ──\n const toolWarnings = getToolWarnings();\n if (Object.keys(toolWarnings).length > 0) {\n for (const tool of tools) {\n const warning = toolWarnings[tool.function.name];\n if (warning) {\n tool.function.description = `${warning} ${tool.function.description}`;\n }\n }\n }\n\n // F3: Inject procedural memory (Hermes skill recall) into system prompt\n let enrichedSystemPrompt = systemPrompt;\n try {\n const { getProceduralContext } = await import('../skills/proceduralMemory.js');\n const proceduralContext = getProceduralContext(message);\n if (proceduralContext) {\n enrichedSystemPrompt += '\\n\\n' + proceduralContext;\n logger.debug('Agent', `[ProceduralMemory] Injected skill context into system prompt`);\n }\n } catch { /* proceduralMemory not available — non-critical */ }\n\n // v5.5.24: Persona profile appendix. Resolved here (early) so the\n // system prompt carries the persona context AND so the tool filter\n // pass below can use the same persona without re-resolving. When\n // personas.enabled=false, resolveActivePersona returns null and this\n // block is a no-op.\n const { resolveActivePersona: __resolvePersona } = await import('./personaProfiles.js');\n let earlyPersona = __resolvePersona({ channel });\n // v5.5.27: Persona A/B cohort override. After the baseline resolver\n // picks a persona, check for an active canary that uses it as\n // baseline. If the (cohortId, sessionId) hash falls in the candidate\n // bucket, swap to candidate and remember the cohort role for the\n // post-response outcome record. Disabled when personas.rollout is off.\n let __cohortAssignment: { cohortId: string; role: 'baseline' | 'candidate' } | null = null;\n if (earlyPersona) {\n try {\n const { pickCohortAssignment } = await import('./personaRollout.js');\n const assignment = pickCohortAssignment(earlyPersona.id, { sessionId: session.id, channel });\n if (assignment) {\n __cohortAssignment = { cohortId: assignment.cohortId, role: assignment.role };\n if (assignment.role === 'candidate' && assignment.personaId !== earlyPersona.id) {\n const { resolveActivePersona } = await import('./personaProfiles.js');\n const candidate = resolveActivePersona({ forceId: assignment.personaId });\n if (candidate) {\n logger.info(COMPONENT, `[Cohort:${assignment.cohortId}] swap ${earlyPersona.id} → ${candidate.id}`);\n earlyPersona = candidate;\n }\n }\n }\n } catch (err) {\n logger.warn(COMPONENT, `cohort assignment skipped: ${(err as Error).message}`);\n }\n }\n if (earlyPersona?.systemPromptAppendix) {\n enrichedSystemPrompt += earlyPersona.systemPromptAppendix;\n logger.info(COMPONENT, `[Persona:${earlyPersona.id}] system prompt appendix injected`);\n }\n\n // v7.2.x Organs 4+6 — plan-first + escalation discipline, under Reliability\n // Mode only. Makes a weaker local brain decompose hard tasks and stop\n // before irreversible actions by reflex (a frontier model does this\n // automatically; a local one must be told).\n try {\n const relMode = (config.agent as { reliabilityMode?: boolean }).reliabilityMode === true;\n if (relMode) {\n const { buildDisciplinePrompt } = await import('./disciplinePrompt.js');\n const frag = buildDisciplinePrompt(message, true);\n if (frag) enrichedSystemPrompt += frag;\n }\n } catch { /* discipline prompt is best-effort */ }\n\n const messages: ChatMessage[] = [\n { role: 'system', content: enrichedSystemPrompt },\n ...historyMessages,\n ];\n\n let totalPromptTokens = 0;\n let totalCompletionTokens = 0;\n const toolsUsed: string[] = [];\n const attemptedTools: string[] = [];\n const orderedToolSequence: string[] = []; // Preserves execution order with repeats\n let finalContent = '';\n let modelUsed = config.agent.model;\n\n // Self-heal config\n const selfHealEnabled = (config.agent as Record<string, unknown>).selfHealEnabled !== false;\n\n // ── Checkpoint: track if budget was exhausted ──\n let budgetExhausted = false;\n\n // ── Cost optimizer: smart model routing ─────────────────\n let { model: activeModel, reason: routingReason } = routeModel(message, config.agent.model);\n if (overrides?.model) activeModel = overrides.model;\n // Voice model override: use a faster model for voice chat (lower latency)\n if (voiceFastPath && (config.voice as Record<string, unknown>)?.model) {\n activeModel = (config.voice as Record<string, unknown>).model as string;\n routingReason = 'voice model override';\n }\n // Session override has highest priority (set via /model command)\n if (session.modelOverride) {\n activeModel = session.modelOverride;\n routingReason = 'session override (/model)';\n }\n if (activeModel !== config.agent.model) {\n logger.info(COMPONENT, `Cost router: ${config.agent.model} → ${activeModel} (${routingReason})`);\n }\n modelUsed = activeModel;\n\n let activeTools = tools;\n // v6.4.4: declare allToolsBackup + toolSearchEnabled here so they're\n // accessible in BOTH the explicitAllowList short-circuit branch and the\n // filter-chain branch. allToolsBackup is used downstream by runAgentLoop\n // for tool_search expansion fallback; toolSearchEnabled gates expansion.\n let allToolsBackup: typeof activeTools = activeTools;\n let toolSearchEnabled = true;\n\n // v6.4.4: explicit cron/override allowlist short-circuit. When the\n // caller (typically a cron job) hands us an explicit list of tool\n // names, bypass the persona / small-model / brain / toolSearch /\n // dangerous filter chain entirely. The caller knows what tools the\n // task needs — we should not be second-guessing it. This fixes the\n // fb-* cron hallucination bug where activeTools reached the agent\n // loop as 0 despite the prompt explicitly saying \"Call fb_post\".\n const explicitAllowList = overrides?.allowedTools;\n if (explicitAllowList && explicitAllowList.length > 0) {\n const allow = new Set(explicitAllowList);\n const before = activeTools.length;\n activeTools = tools.filter(t => allow.has(t.function.name));\n allToolsBackup = activeTools;\n logger.info(COMPONENT, `[AllowedTools] override bound ${activeTools.length}/${explicitAllowList.length} requested tools (filter chain bypassed): [${activeTools.map(t => t.function.name).join(', ')}] from pool of ${before}`);\n const missing = explicitAllowList.filter(name => !activeTools.some(t => t.function.name === name));\n if (missing.length > 0) {\n logger.warn(COMPONENT, `[AllowedTools] caller asked for tools that aren't registered: [${missing.join(', ')}]`);\n }\n } else {\n\n // v5.5.24: Persona-level tool filter. earlyPersona was already\n // resolved when we built enrichedSystemPrompt above; reuse that\n // resolution rather than calling the resolver twice per request.\n if (earlyPersona) {\n const { applyPersonaToolFilter } = await import('./personaProfiles.js');\n const before = activeTools.length;\n activeTools = applyPersonaToolFilter(activeTools, earlyPersona);\n if (activeTools.length !== before) {\n logger.info(COMPONENT, `[Persona:${earlyPersona.id}] tools ${before} → ${activeTools.length}`);\n }\n }\n\n // Context-fit tool tiers (v7.1 \"truly model agnostic\") — bind a toolset\n // sized to the model's REAL context window (family registry + config\n // override + ceilings learned from live backend 400s), instead of a\n // hardcoded name list. Measured: full core ≈ 61KB of schemas + 7.5KB\n // system prompt — fine at 64K+, fatal at 16K, sluggish prefill on\n // bandwidth-bound local GPUs. Tiers:\n // tiny (<12K ctx or known-tiny name): 6 primitives\n // small (<48K ctx): 14 lean essentials + minimal prompt mode\n // full (>=48K): the standard 30-tool core (toolSearch compact mode)\n const SMALL_MODEL_PATTERNS = ['llama3.2', 'llama3.1:8b', 'phi', 'gemma:2b', 'qwen3.5:4b', 'tinyllama', 'dolphin3'];\n const nameIsTiny = SMALL_MODEL_PATTERNS.some(p => activeModel.toLowerCase().includes(p));\n const { getModelCapabilitiesEx } = await import('../providers/modelCapabilities.js');\n const bareModel = activeModel.includes('/') ? activeModel.slice(activeModel.indexOf('/') + 1) : activeModel;\n const capsA = getModelCapabilitiesEx(activeModel);\n const capsB = getModelCapabilitiesEx(bareModel);\n const best = capsA.caps.contextWindow <= capsB.caps.contextWindow ? capsA : capsB;\n const modelCtxWindow = best.caps.contextWindow;\n // A fallback guess must never shrink the toolset — only measured/known\n // ceilings (static, config, family, learned-from-400s) trigger tiering.\n const ctxIsKnown = best.source !== 'fallback';\n const contextTier: 'tiny' | 'small' | 'full' =\n nameIsTiny || (ctxIsKnown && modelCtxWindow < 12_000) ? 'tiny'\n : ctxIsKnown && modelCtxWindow < 48_000 ? 'small'\n : 'full';\n const isSmallModel = contextTier !== 'full';\n if (contextTier === 'tiny') {\n const CORE_TOOL_NAMES = ['shell', 'read_file', 'write_file', 'edit_file', 'list_dir', 'memory'];\n const coreTools = activeTools.filter(t => CORE_TOOL_NAMES.includes(t.function.name));\n logger.info(COMPONENT, `[ContextFit] tier=tiny (ctx=${modelCtxWindow}) — ${activeTools.length} → ${coreTools.length} tools for ${activeModel}`);\n activeTools = coreTools;\n } else if (contextTier === 'small') {\n const LEAN_TOOL_NAMES = [\n 'shell', 'read_file', 'write_file', 'edit_file', 'list_dir',\n 'web_search', 'browse_url', 'memory', 'create_widget',\n 'gallery_search', 'gallery_get', 'cron', 'reminder', 'tool_search',\n ];\n const leanTools = activeTools.filter(t => LEAN_TOOL_NAMES.includes(t.function.name));\n if (leanTools.length > 0) {\n logger.info(COMPONENT, `[ContextFit] tier=small (ctx=${modelCtxWindow}) — ${activeTools.length} → ${leanTools.length} tools for ${activeModel}`);\n activeTools = leanTools;\n }\n }\n\n // ── Brain: intelligent tool pre-filtering ──────────────────\n if (!voiceFastPath && !isSmallModel && isBrainAvailable()) {\n const brainFiltered = await brainSelectTools(message, activeTools);\n if (brainFiltered.length > 0 && brainFiltered.length < activeTools.length) {\n logger.info(COMPONENT, `[Brain] Filtered: ${activeTools.length} → ${brainFiltered.length} tools`);\n activeTools = brainFiltered;\n }\n }\n\n // ── Tool Search: compact tool mode ──────────────────────────\n // Send only core tools + tool_search to the LLM instead of all 80+.\n // The LLM calls tool_search to discover additional tools as needed.\n const toolSearchConfig = (config as Record<string, unknown>).toolSearch as {\n enabled?: boolean;\n coreTools?: string[];\n } | undefined;\n // v6.4.4: now assignments, not declarations — variables are hoisted above\n // the explicitAllowList short-circuit so both branches can reach them.\n toolSearchEnabled = toolSearchConfig?.enabled ?? true;\n allToolsBackup = activeTools;\n\n // Always ensure pipeline tools are in the active set, even when toolSearch is disabled\n if (pipelineEnsureTools.length > 0 && !(toolSearchEnabled && !isSmallModel && activeTools.length > 12)) {\n const activeNames = new Set(activeTools.map(t => t.function.name));\n const missing = pipelineEnsureTools.filter(name => !activeNames.has(name));\n if (missing.length > 0) {\n const rescued = allToolsBackup.filter(t => missing.includes(t.function.name));\n activeTools.push(...rescued);\n if (rescued.length > 0) {\n logger.info(COMPONENT, `[Pipeline:${pipelineType}] Ensured ${rescued.length} tools (no-compact): [${rescued.map(t => t.function.name).join(', ')}]`);\n }\n }\n }\n\n if (toolSearchEnabled && !isSmallModel && activeTools.length > 12) {\n // Voice gets a minimal tool set for speed (fewer tool schemas = less prompt tokens)\n const VOICE_CORE_TOOLS = ['shell', 'web_search', 'weather', 'memory', 'ha_control', 'ha_devices', 'ha_status', 'ha_setup', 'tool_search'];\n // Use config coreTools only if non-empty; otherwise fall back to DEFAULT_CORE_TOOLS\n const configCoreTools = toolSearchConfig?.coreTools;\n const effectiveCoreTools = (configCoreTools && configCoreTools.length > 0) ? configCoreTools : DEFAULT_CORE_TOOLS;\n // Pipeline tools: merge pipeline-specific tools into the core set\n const pipelineMerged = pipelineEnsureTools.length > 0\n ? [...new Set([...effectiveCoreTools, ...pipelineEnsureTools])]\n : effectiveCoreTools;\n const coreNames = new Set(voiceFastPath ? VOICE_CORE_TOOLS : pipelineMerged);\n activeTools = activeTools.filter(t => coreNames.has(t.function.name));\n // If pipeline tools were requested but not found in activeTools, pull from backup\n if (pipelineEnsureTools.length > 0) {\n const activeNames = new Set(activeTools.map(t => t.function.name));\n const missing = pipelineEnsureTools.filter(name => !activeNames.has(name));\n if (missing.length > 0) {\n const rescued = allToolsBackup.filter(t => missing.includes(t.function.name));\n activeTools.push(...rescued);\n if (rescued.length > 0) {\n logger.info(COMPONENT, `[Pipeline:${pipelineType}] Rescued ${rescued.length} tools: [${rescued.map(t => t.function.name).join(', ')}]`);\n }\n }\n }\n logger.info(COMPONENT, `[ToolSearch] Compact mode: ${allToolsBackup.length} → ${activeTools.length} tools (${allToolsBackup.length - activeTools.length} discoverable via tool_search)`);\n }\n\n // v5.0.2: Safety override — strip all tools so the model CANNOT call anything\n // dangerous. The safety message in systemPrompt tells it to refuse.\n if (dangerous) {\n activeTools = [];\n logger.info(COMPONENT, '[Safety] Stripped all tools — dangerous command detected');\n }\n\n } // v6.4.4: end of !explicitAllowList branch (persona/brain/toolSearch/dangerous filters)\n\n // ── Stall detector: configure for autonomy mode + start heartbeat ──\n setAutonomousMode(isAutonomous);\n heartbeat(session.id);\n\n // ── Orchestration: check if task benefits from sub-agent delegation ──\n const autoDelegate = (subAgentConfig as Record<string, unknown> | undefined)?.autoDelegate !== false;\n if (!voiceFastPath && isAutonomous && autoDelegate && channel !== 'deliberation' && message.split(/\\s+/).length >= 8) {\n try {\n const delegationPlan = await analyzeForDelegation(message);\n if (delegationPlan && delegationPlan.shouldDelegate && delegationPlan.tasks.length >= 2) {\n logger.info(COMPONENT, `Orchestrator: delegating to ${delegationPlan.tasks.length} sub-agents`);\n const orchResult = await executeDelegationPlan(delegationPlan!);\n if (orchResult.subResults.length > 0 && orchResult.subResults.some(r => r.success)) {\n messages.push({\n role: 'user',\n content: `[Sub-agent results for your request]\\n\\n${orchResult.content}\\n\\nSynthesize these results into a coherent response for the user.`,\n });\n }\n }\n } catch (err) {\n logger.warn(COMPONENT, `Orchestration failed, falling through: ${(err as Error).message}`);\n }\n }\n\n // ══════════════════════════════════════════════════════════════\n // v4.13: run ContextEngine plugin assemble hooks (topFacts,\n // memoryRetrieval, smartCompress) before the loop. Previously the\n // plugin system existed but nothing called runAssemble, so\n // top-facts + relevant graph/vector memories never reached the LLM.\n // Injects a system message per active plugin right before the last\n // user message. Errors inside a plugin are swallowed.\n // ══════════════════════════════════════════════════════════════\n try {\n const { getPlugins } = await import('../plugins/registry.js');\n const { runAssemble } = await import('../plugins/contextEngine.js');\n const plugins = getPlugins();\n if (plugins.length > 0) {\n const assembled = await runAssemble(plugins, messages, message);\n messages.splice(0, messages.length, ...assembled);\n }\n } catch (pluginErr) {\n logger.debug(COMPONENT, `plugin assemble failed: ${(pluginErr as Error).message}`);\n }\n\n // ══════════════════════════════════════════════════════════════\n // Agent Loop — Phase State Machine (Think/Act/Respond)\n // Replaces the old monolithic for-loop. See agentLoop.ts.\n // ══════════════════════════════════════════════════════════════\n const loopResult = await runAgentLoop({\n messages,\n activeTools,\n allToolsBackup,\n activeModel,\n config,\n sessionId: session.id,\n agentId: overrides?.agentId,\n channel,\n message,\n streamCallbacks,\n signal,\n isAutonomous,\n voiceFastPath,\n effectiveMaxRounds,\n taskEnforcementActive,\n reflectionEnabled,\n reflectionInterval,\n toolSearchEnabled,\n selfHealEnabled,\n smartExitEnabled: pipelineSmartExit,\n thinkingOverride: session.thinkingOverride,\n pipelineTerminalTools,\n completionStrategy: pipelineCompletionStrategy,\n pipelineType,\n minRounds: pipelineMinRounds,\n });\n\n // Unpack results\n finalContent = loopResult.content;\n toolsUsed.push(...loopResult.toolsUsed);\n attemptedTools.push(...loopResult.attemptedTools);\n orderedToolSequence.push(...loopResult.orderedToolSequence);\n modelUsed = loopResult.modelUsed;\n totalPromptTokens += loopResult.promptTokens;\n totalCompletionTokens += loopResult.completionTokens;\n budgetExhausted = loopResult.budgetExhausted;\n\n // Extract structured artifacts for deliberation inter-step context\n const toolArtifacts = extractToolArtifacts(loopResult.toolCallDetails);\n\n // ── Ralph Loop Verification ─────────────────────────────────\n // Outer completion check: did the task actually get done?\n // If the user asked to edit/write but no write tool was called,\n // re-run the agent loop ONE more time with a forced write instruction.\n if (isAutonomous && !voiceFastPath && !budgetExhausted && channel !== 'deliberation') {\n const verification = verifyTaskCompletion(message, toolsUsed, finalContent);\n if (!verification.complete) {\n logger.warn(COMPONENT, `[RalphLoop] Task incomplete: ${verification.reason}. Re-running with forced write.`);\n\n // Add the verification feedback and re-run with explicit tool guidance\n messages.push({ role: 'assistant', content: finalContent });\n messages.push({ role: 'user', content: [\n `[TASK INCOMPLETE] ${verification.reason}`,\n '',\n 'You have the file content from your previous read_file call.',\n 'Now call edit_file with these arguments:',\n ' - path: the file path you just read',\n ' - target: the exact string you want to replace (copy it from the file)',\n ' - replacement: the new string to put in its place',\n '',\n 'edit_file does a search-and-replace. You do NOT need to rewrite the whole file.',\n 'Just find a small section to change and replace it.',\n 'CALL edit_file NOW.',\n ].join('\\n') });\n\n const retryResult = await runAgentLoop({\n messages,\n activeTools,\n allToolsBackup,\n activeModel,\n config,\n sessionId: session.id,\n agentId: overrides?.agentId,\n channel,\n message,\n streamCallbacks,\n signal,\n isAutonomous,\n voiceFastPath,\n effectiveMaxRounds: 4, // Short budget for the retry\n taskEnforcementActive: true,\n reflectionEnabled: false,\n reflectionInterval: 99,\n toolSearchEnabled,\n selfHealEnabled: false,\n thinkingOverride: session.thinkingOverride,\n });\n\n if (retryResult.content) finalContent = retryResult.content;\n toolsUsed.push(...retryResult.toolsUsed);\n attemptedTools.push(...retryResult.attemptedTools);\n orderedToolSequence.push(...retryResult.orderedToolSequence);\n totalPromptTokens += retryResult.promptTokens;\n totalCompletionTokens += retryResult.completionTokens;\n\n logger.info(COMPONENT, `[RalphLoop] Retry complete. Tools used: [${retryResult.toolsUsed.join(', ')}]`);\n }\n }\n\n // Clean up stall detector for this session\n clearSession(session.id);\n // Hunt Finding #22 / #46 (2026-04-15): previously resetLoopDetection fired\n // at the end of EVERY turn, wiping cross-turn state. That meant the loop\n // breaker could only catch loops within a single turn — a model that\n // hit the same tool on turn 1, turn 2, turn 3 (across separate requests\n // in the same session) would never trip. The README claims TITAN\n // \"prevents runaway loops and wasted tokens\" which implies cross-turn\n // coverage. Fix: keep the session's loop state alive until the session\n // actually closes (session.ts:483 still wipes on session close).\n // resetLoopDetection(session.id); // INTENTIONALLY REMOVED — see comment above\n\n // Clear checkpoints on successful completion (no need to resume)\n if (!budgetExhausted) {\n import('./checkpoint.js').then(m => m.clearCheckpoints(session.id)).catch(e => logger.debug('Agent', `Background op failed: ${(e as Error).message}`));\n }\n\n // Active Learning: record strategy for future reference\n // v7.0 Muscle Memory: eval-channel runs (replay exams, test harness) must\n // never feed the learning loop — otherwise the exam's own replays re-enter\n // the mining pool and the system studies its own echo.\n if (toolsUsed.length > 0 && channel !== 'eval') {\n const success = !finalContent.toLowerCase().includes('error') && !budgetExhausted;\n recordStrategy(message, [...new Set(toolsUsed)], orderedToolSequence.length, success, orderedToolSequence);\n\n // Feedback loop: record outcome for matching strategies\n if (orderedToolSequence.length > 0) {\n recordStrategyOutcome(classifyTaskType(message), orderedToolSequence, success);\n }\n\n // Hindsight MCP: retain successful strategies as cross-session experience (fire-and-forget)\n // F2: Scoped to the agent's memory namespace so each agent builds its own episodic slice.\n if (success && orderedToolSequence.length > 0) {\n (async () => {\n let hsNs: string | undefined;\n if (overrides?.agentId && overrides.agentId !== 'default') {\n try {\n const { getAgentMemoryNamespace } = await import('./commandPost.js');\n hsNs = getAgentMemoryNamespace(overrides.agentId);\n } catch { /* fallthrough */ }\n }\n try { retainStrategy(classifyTaskType(message), orderedToolSequence, 1, message.slice(0, 200), hsNs); } catch { /* Hindsight unavailable */ }\n })().catch(() => { /* Hindsight unavailable */ });\n }\n }\n\n // ── Hallucination Guard: detect cloud models that claim tool use but never called tools ──\n // Cloud models sometimes describe tool actions (\"I wrote the file\", \"Output: ...\") without\n // actually making tool calls. This pollutes session memory with false action claims.\n //\n // SKIP for conversational tasks — chat/general pipelines don't require tool use,\n // so \"I did X\" in a conversational reply is normal, not a hallucination.\n const isConversationalTask = pipelineType === 'chat' || pipelineType === 'general'\n || channel?.endsWith('-admin') || channel === 'voice';\n const isCloudHallucination = toolsUsed.length === 0\n && taskEnforcementActive\n && !isConversationalTask\n && (activeModel.includes(':cloud') || activeModel.includes('-cloud'))\n && finalContent.length > 0\n && /(?:(?:I(?:'ve| have)?|successfully|done|completed|executed|created|wrote|saved|ran|output|result)[:\\s])/i.test(finalContent)\n && !/(?:I (?:can|could|would|will|should)|let me|I don't|I cannot|error|failed)/i.test(finalContent);\n\n if (isCloudHallucination) {\n logger.warn(COMPONENT, `[HallucinationGuard] Cloud model claimed action but toolsUsed is empty — sanitizing response`);\n finalContent = \"I wasn't able to finish that task — the connection to my brain hiccupped. Try asking again, or I can switch to my offline mode if you prefer.\";\n }\n\n // v7.0 model-agnostic guarantee: if the user's message was a system-widget\n // shortcut (\"show backup\") but no _____widget gate made it into the reply —\n // the model called a tool (e.g. backup_list) or answered in prose instead of\n // emitting the gate — emit it deterministically so the widget renders no\n // matter which model is driving. This is the canonical fix for the widget\n // gate NOT depending on per-model formatting adherence.\n {\n const sw = detectSystemWidget(message);\n if (sw && !finalContent.includes('_____widget')) {\n finalContent = finalContent ? `${finalContent}\\n\\n${buildSystemWidgetGate(sw)}` : buildSystemWidgetGate(sw);\n logger.info(COMPONENT, `[SystemWidgetShortcut] Deterministically emitted _____widget gate for ${sw.source}`);\n }\n }\n\n // v7.0 anti-fabrication backstop (user-test T6: \"I've scheduled a reminder\n // for Friday at 5 PM\" with zero scheduling tools called). If the reply\n // CLAIMS a reminder/schedule was set but no scheduling-capable tool ran\n // this turn, append an honest correction so the user is never silently\n // misled. Deterministic + model-agnostic; the prompt steering above tries\n // to prevent this — this guard guarantees it.\n // v7.1.x Organ 5 — self-critique (Reflexion). Off unless configured. Runs\n // BEFORE the honesty guard so the guard validates the caveated text.\n try {\n const agentCfg = config.agent as { selfCritique?: Partial<import('./selfCritique.js').SelfCritiqueConfig>; reliabilityMode?: boolean };\n const { resolveSelfCritique, runSelfCritique } = await import('./selfCritique.js');\n const scCfg = resolveSelfCritique(agentCfg.selfCritique, agentCfg.reliabilityMode === true);\n if (scCfg.enabled) {\n const sc = await runSelfCritique(scCfg, message, finalContent, [...new Set(toolsUsed)], modelUsed);\n if (sc.critiqued && sc.issues.length > 0) finalContent = sc.content;\n }\n } catch (e) { logger.debug(COMPONENT, `Self-critique wrapper skipped: ${(e as Error).message}`); }\n\n // v7.1.x verification wall: generalized from scheduling to ALL\n // side-effect claims (send/post/delete/deploy/write/schedule). If the\n // reply says it DID something but no capable tool ran, append an honest\n // correction. The single most important organ for honesty on any model.\n {\n const { applyHonestyGuard } = await import('./honestyGuard.js');\n const guarded = applyHonestyGuard(finalContent, toolsUsed);\n if (guarded.flagged.length > 0) {\n finalContent = guarded.content;\n logger.warn(COMPONENT, `[HonestyGuard] Reply claimed ${guarded.flagged.join(', ')} without a capable tool — appended correction`);\n }\n }\n\n // Save assistant response to session\n addMessage(session, 'assistant', finalContent, {\n model: modelUsed,\n tokenCount: totalCompletionTokens,\n });\n\n // Auto-record agent response to knowledge graph (fire-and-forget, skip short/error responses)\n if (finalContent.length > 50 && !finalContent.startsWith('⚠️')) {\n addEpisode(`[TITAN → ${channel}/${userId}] ${finalContent.slice(0, 500)}`, 'agent').catch(e => logger.debug('Agent', `Background op failed: ${(e as Error).message}`));\n }\n\n // Record usage\n const { provider: providerName } = { provider: modelUsed.split('/')[0] || 'unknown' };\n recordUsage(session.id, providerName, modelUsed, totalPromptTokens, totalCompletionTokens);\n\n const durationMs = Date.now() - startTime;\n logger.info(COMPONENT, `Response generated in ${durationMs}ms (${totalPromptTokens + totalCompletionTokens} tokens)`);\n\n // v5.5.27: Persona cohort outcome recording. The rollout monitor\n // reads these events to compute per-arm pass-rate + Safety drive trend.\n // Best-effort, fire-and-forget.\n if (__cohortAssignment) {\n void (async () => {\n try {\n const { recordOutcome } = await import('./personaRollout.js');\n let safetySat: number | null = null;\n try {\n const { loadDriveHistory } = await import('../organism/drives.js');\n const persisted = loadDriveHistory();\n const safety = persisted?.latest?.drives?.find(d => d.id === 'safety');\n safetySat = safety ? safety.satisfaction : null;\n } catch { /* drives optional */ }\n recordOutcome({\n cohortId: __cohortAssignment!.cohortId,\n role: __cohortAssignment!.role,\n sessionId: session.id,\n success: !budgetExhausted && finalContent.length > 0,\n latencyMs: durationMs,\n safetySat,\n });\n } catch { /* outcome recording is optional */ }\n })();\n }\n\n // ── Post-conversation learning: record insights from tool usage ───\n if (toolsUsed.length > 0) {\n const uniqueTools = [...new Set(toolsUsed)];\n learnFact(\n 'conversation_insight',\n `User asked \"${message.slice(0, 80)}\" → used tools: ${uniqueTools.join(', ')} (${durationMs}ms)`,\n message.slice(0, 100),\n );\n }\n\n // ── ContextEngine afterTurn hooks (fire-and-forget — TopFacts, SmartCompress, etc.) ──\n const afterTurnPlugins = getPlugins() || [];\n if (afterTurnPlugins.length > 0) {\n runAfterTurn(afterTurnPlugins, { content: finalContent, toolsUsed: [...new Set(toolsUsed)] }).catch(e => logger.debug('Agent', `Background op failed: ${(e as Error).message}`));\n }\n\n // ── Checkpoint: if budget exhausted, build a checkpoint for potential resumption ──\n let checkpoint: string | undefined;\n if (budgetExhausted) {\n try {\n checkpoint = JSON.stringify({\n sessionId: session.id,\n toolsUsed: [...new Set(toolsUsed)],\n roundsUsed: effectiveMaxRounds,\n lastContent: finalContent.slice(0, 500),\n timestamp: Date.now(),\n });\n } catch (e) { logger.debug(COMPONENT, `Response serialization failed: ${(e as Error).message}`); }\n }\n\n // Consolidate soul wisdom from this task\n const taskSuccess = !finalContent.toLowerCase().includes('error') && !budgetExhausted;\n consolidateWisdom(session.id, classifyTaskType(message), taskSuccess, loopResult.toolCallDetails.length);\n clearSoulState(session.id);\n\n // Log task-level trajectory and check for auto-skill generation\n const trajectory = {\n id: randomBytes(16).toString('hex'),\n timestamp: new Date().toISOString(),\n task: message.slice(0, 500),\n taskType: classifyTaskType(message),\n model: modelUsed,\n toolSequence: orderedToolSequence,\n toolDetails: loopResult.toolCallDetails,\n success: taskSuccess,\n rounds: loopResult.toolCallDetails.length,\n durationMs,\n sessionId: session.id,\n };\n if (channel !== 'eval') {\n logTrajectory(trajectory);\n processTrajectoryForSkills(trajectory);\n }\n // v6.4.0 — feed the same trajectory into the learning curator so it\n // can update its per-pattern lifecycle state and (every 24h) write\n // durable lessons to ~/.titan/logs/learning-curator/. Errors are\n // swallowed inside processTrajectoryForLearningReview so a curator\n // failure can't poison the agent loop.\n if (channel !== 'eval') processTrajectoryForLearningReview(trajectory);\n\n // Finalize trace\n trace.setModel(modelUsed);\n trace.setRounds(loopResult.toolCallDetails.length);\n trace.setTokens(totalPromptTokens, totalCompletionTokens);\n for (const tc of loopResult.toolCallDetails) {\n trace.toolCall(tc.name, tc.args, 0, tc.success, 0);\n }\n trace.end(budgetExhausted ? 'failed' : 'completed', budgetExhausted ? 'budget exhausted' : undefined);\n\n // Soma (v4.0): turn:post event on the trace bus. Fires for every\n // non-early-return turn. Subscribers (drive recompute, activity feed)\n // react here; no-op when nothing listening.\n try {\n const { emit: emitTrace } = await import('../substrate/traceBus.js');\n emitTrace('turn:post', {\n agentId: overrides?.agentId || 'default',\n sessionId: session.id,\n channel,\n userId,\n success: !budgetExhausted,\n toolsUsed: [...new Set(toolsUsed)],\n durationMs,\n model: modelUsed,\n timestamp: new Date().toISOString(),\n });\n } catch { /* substrate not available — skip */ }\n\n // v7.0 observability — record a per-run trace span (best-effort, never breaks the turn).\n try {\n const { recordSpan } = await import('../telemetry/traceStore.js');\n let costUsd = 0;\n try {\n const { calculateActualCost } = await import('./costEstimator.js');\n costUsd = calculateActualCost(modelUsed, { prompt: totalPromptTokens, completion: totalCompletionTokens });\n } catch { /* cost optional */ }\n recordSpan({\n sessionId: session.id,\n startedAt: new Date(startTime).toISOString(),\n durationMs,\n model: modelUsed,\n input: (message || '').slice(0, 500),\n output: (finalContent || '').slice(0, 1000),\n toolsUsed: [...new Set(toolsUsed)],\n promptTokens: totalPromptTokens,\n completionTokens: totalCompletionTokens,\n costUsd,\n ok: !budgetExhausted && !!finalContent,\n });\n } catch { /* tracing optional */ }\n\n // v7.0 life loop — proactive memory (the Hermes move): occasionally notice\n // something durable about the user from this exchange. Throttled + gated\n // inside; fire-and-forget so it never delays the reply. Closes the audit\n // gap where appendObservation had zero callers and the agent never learned.\n try {\n const { isBenchMode } = await import('../utils/benchMode.js');\n if (!isBenchMode()) {\n const { maybeObserve } = await import('./observationWriter.js');\n maybeObserve(channel, message || '', finalContent || '');\n }\n } catch { /* observation optional */ }\n\n return {\n content: finalContent,\n sessionId: session.id,\n toolsUsed: [...new Set(toolsUsed)],\n attemptedTools: [...new Set(attemptedTools)],\n orderedToolSequence: [...orderedToolSequence],\n tokenUsage: {\n prompt: totalPromptTokens,\n completion: totalCompletionTokens,\n total: totalPromptTokens + totalCompletionTokens,\n },\n model: modelUsed,\n durationMs,\n exhaustedBudget: budgetExhausted || undefined,\n checkpoint,\n toolArtifacts,\n };\n}\n"],"mappings":"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","names":["issue","wakeup","getPlugins"]}
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env node
2
+ function looksComplex(message) {
3
+ const m = (message || "").trim();
4
+ if (m.length < 40) return false;
5
+ let signals = 0;
6
+ if (/\b(then|after that|next|finally|first|second|step \d|once .* (is )?done)\b/i.test(m)) signals++;
7
+ if (/\band\b.*\band\b/i.test(m)) signals++;
8
+ if (/\b(\d+)\s+(files?|steps?|things?|parts?|tasks?)\b/i.test(m)) signals++;
9
+ const listItems = (m.match(/(^|\n)\s*(\d+[.)]|[-*])\s+\S/gm) || []).length;
10
+ if (listItems >= 2) signals += 2;
11
+ else if (listItems === 1) signals++;
12
+ const actionVerbs = (m.match(/\b(refactor|migrate|implement|build|integrate|wire\s?up|set\s?up|deploy|redesign|add|update|create|write|fix|test|configure|install|remove)\b/gi) || []).length;
13
+ if (actionVerbs >= 3) signals += 2;
14
+ else if (actionVerbs >= 1) signals++;
15
+ if (m.length > 400) signals++;
16
+ return signals >= 2;
17
+ }
18
+ const PLAN_FIRST = [
19
+ "DISCIPLINE \u2014 plan first: this task looks multi-step. Before calling tools,",
20
+ "write a short numbered plan (the steps and the order). Then execute it,",
21
+ "verifying each step before moving on. Do not claim a step is done until you",
22
+ "have checked it. A plan you followed beats a fast answer you have to redo."
23
+ ].join(" ");
24
+ const ESCALATE = [
25
+ "DISCIPLINE \u2014 escalate the irreversible: before any action that is",
26
+ "destructive (deletes/overwrites data), public (sends/posts/publishes),",
27
+ "spends money, or changes a shared service, STOP and confirm with the user",
28
+ "first unless they already authorized THIS action. Reversible, local work:",
29
+ "proceed. When unsure which, prefer the reversible reading and say so."
30
+ ].join(" ");
31
+ function buildDisciplinePrompt(message, reliabilityMode) {
32
+ if (!reliabilityMode) return "";
33
+ const parts = [ESCALATE];
34
+ if (looksComplex(message)) parts.unshift(PLAN_FIRST);
35
+ return "\n\n" + parts.join("\n\n");
36
+ }
37
+ export {
38
+ buildDisciplinePrompt,
39
+ looksComplex
40
+ };
41
+ //# sourceMappingURL=disciplinePrompt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/agent/disciplinePrompt.ts"],"sourcesContent":["/**\n * Discipline Prompt — the plan-first + escalation habits (v7.2.x).\n *\n * Organs 4 (decompose) and 6 (judgment) of the Fable-5 rebuild plan, delivered\n * as a first-turn system-prompt fragment gated by Reliability Mode. A frontier\n * model plans hard tasks and escalates risky ones by reflex; a weaker local\n * model must be TOLD to. This makes the reflex explicit — so under Reliability\n * Mode a local brain behaves with the same \"plan before you build, ask before\n * you do something irreversible\" discipline that makes a strong model feel\n * dependable.\n *\n * Pure functions → unit-testable. Fires ONLY when reliabilityMode is on, and\n * the plan-first half only for tasks that look genuinely multi-step (a cheap\n * heuristic — over-injecting on trivial turns would just add noise).\n */\n\n/** Heuristic: does this task look complex enough to warrant planning first? Pure. */\nexport function looksComplex(message: string): boolean {\n const m = (message || '').trim();\n if (m.length < 40) return false;\n let signals = 0;\n // Sequencing language.\n if (/\\b(then|after that|next|finally|first|second|step \\d|once .* (is )?done)\\b/i.test(m)) signals++;\n // Explicit multiplicity.\n if (/\\band\\b.*\\band\\b/i.test(m)) signals++;\n if (/\\b(\\d+)\\s+(files?|steps?|things?|parts?|tasks?)\\b/i.test(m)) signals++;\n // A list of asks IS a decomposable task: 2+ list items → strongly complex.\n const listItems = (m.match(/(^|\\n)\\s*(\\d+[.)]|[-*])\\s+\\S/gm) || []).length;\n if (listItems >= 2) signals += 2;\n else if (listItems === 1) signals++;\n // Action verbs that usually span files/steps. COUNT them — a task asking\n // for several distinct actions (\"build X, integrate Y, and deploy Z\") is\n // decomposable even without sequencing words.\n const actionVerbs = (m.match(/\\b(refactor|migrate|implement|build|integrate|wire\\s?up|set\\s?up|deploy|redesign|add|update|create|write|fix|test|configure|install|remove)\\b/gi) || []).length;\n if (actionVerbs >= 3) signals += 2;\n else if (actionVerbs >= 1) signals++;\n // Sheer length is itself a weak signal.\n if (m.length > 400) signals++;\n return signals >= 2;\n}\n\nconst PLAN_FIRST = [\n 'DISCIPLINE — plan first: this task looks multi-step. Before calling tools,',\n 'write a short numbered plan (the steps and the order). Then execute it,',\n 'verifying each step before moving on. Do not claim a step is done until you',\n 'have checked it. A plan you followed beats a fast answer you have to redo.',\n].join(' ');\n\nconst ESCALATE = [\n 'DISCIPLINE — escalate the irreversible: before any action that is',\n 'destructive (deletes/overwrites data), public (sends/posts/publishes),',\n 'spends money, or changes a shared service, STOP and confirm with the user',\n 'first unless they already authorized THIS action. Reversible, local work:',\n 'proceed. When unsure which, prefer the reversible reading and say so.',\n].join(' ');\n\n/**\n * Build the discipline fragment to append to the first-turn system prompt.\n * Returns '' when reliabilityMode is off. Pure.\n */\nexport function buildDisciplinePrompt(message: string, reliabilityMode: boolean): string {\n if (!reliabilityMode) return '';\n const parts = [ESCALATE]; // escalation discipline always applies under Reliability Mode\n if (looksComplex(message)) parts.unshift(PLAN_FIRST);\n return '\\n\\n' + parts.join('\\n\\n');\n}\n"],"mappings":";AAiBO,SAAS,aAAa,SAA0B;AACnD,QAAM,KAAK,WAAW,IAAI,KAAK;AAC/B,MAAI,EAAE,SAAS,GAAI,QAAO;AAC1B,MAAI,UAAU;AAEd,MAAI,8EAA8E,KAAK,CAAC,EAAG;AAE3F,MAAI,oBAAoB,KAAK,CAAC,EAAG;AACjC,MAAI,qDAAqD,KAAK,CAAC,EAAG;AAElE,QAAM,aAAa,EAAE,MAAM,gCAAgC,KAAK,CAAC,GAAG;AACpE,MAAI,aAAa,EAAG,YAAW;AAAA,WACtB,cAAc,EAAG;AAI1B,QAAM,eAAe,EAAE,MAAM,iJAAiJ,KAAK,CAAC,GAAG;AACvL,MAAI,eAAe,EAAG,YAAW;AAAA,WACxB,eAAe,EAAG;AAE3B,MAAI,EAAE,SAAS,IAAK;AACpB,SAAO,WAAW;AACtB;AAEA,MAAM,aAAa;AAAA,EACf;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACJ,EAAE,KAAK,GAAG;AAEV,MAAM,WAAW;AAAA,EACb;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACJ,EAAE,KAAK,GAAG;AAMH,SAAS,sBAAsB,SAAiB,iBAAkC;AACrF,MAAI,CAAC,gBAAiB,QAAO;AAC7B,QAAM,QAAQ,CAAC,QAAQ;AACvB,MAAI,aAAa,OAAO,EAAG,OAAM,QAAQ,UAAU;AACnD,SAAO,SAAS,MAAM,KAAK,MAAM;AACrC;","names":[]}
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { homedir } from "os";
3
3
  import { join } from "path";
4
- const TITAN_VERSION = "7.2.0";
4
+ const TITAN_VERSION = "7.2.1";
5
5
  const TITAN_CODENAME = "Living Canvas";
6
6
  const TITAN_NAME = "TITAN";
7
7
  const TITAN_FULL_NAME = "The Intelligent Task Automation Network";
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/utils/constants.ts"],"sourcesContent":["/**\n * TITAN Constants\n */\nimport { homedir } from 'os';\nimport { join } from 'path';\n\nexport const TITAN_VERSION = '7.2.0';\nexport const TITAN_CODENAME = 'Living Canvas';\nexport const TITAN_NAME = 'TITAN';\nexport const TITAN_FULL_NAME = 'The Intelligent Task Automation Network';\nexport const TITAN_ASCII_LOGO = `\n╔══════════════════════════════════════════════════════╗\n║ ║\n║ ████████╗██╗████████╗ █████╗ ███╗ ██╗ ║\n║ ██║ ██║ ██║ ██╔══██╗████╗ ██║ ║\n║ ██║ ██║ ██║ ███████║██╔██╗ ██║ ║\n║ ██║ ██║ ██║ ██╔══██║██║╚██╗██║ ║\n║ ██║ ██║ ██║ ██║ ██║██║ ╚████║ ║\n║ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═══╝ ║\n║ ║\n║ The Intelligent Task Automation Network ║\n║ v${TITAN_VERSION} • by Tony Elliott ║\n╚══════════════════════════════════════════════════════╝`;\n\n// Paths\n// Hunt Finding #03 (2026-04-14): honor TITAN_HOME env var if set.\n// Previously this was hardcoded to `~/.titan`, which meant:\n// - Docker containers couldn't override the config path\n// - Shared machines couldn't isolate per-user state\n// - Test fixtures couldn't run against an isolated home\n// - The systemd unit's `Environment=TITAN_HOME=...` was silently ignored\n// The env var is read once at module load (constants are resolved at import time).\n// If TITAN_HOME starts with `~/`, expand it to the user's home dir.\nfunction resolveTitanHome(): string {\n const envHome = process.env.TITAN_HOME;\n if (envHome && envHome.trim().length > 0) {\n const trimmed = envHome.trim();\n if (trimmed.startsWith('~/')) {\n return join(homedir(), trimmed.slice(2));\n }\n if (trimmed === '~') {\n return homedir();\n }\n return trimmed;\n }\n return join(homedir(), '.titan');\n}\nexport const TITAN_HOME = resolveTitanHome();\nexport const TITAN_CONFIG_PATH = join(TITAN_HOME, 'titan.json');\nexport const TITAN_DB_PATH = join(TITAN_HOME, 'titan.db');\nexport const TITAN_WORKSPACE = join(TITAN_HOME, 'workspace');\nexport const TITAN_SKILLS_DIR = join(TITAN_WORKSPACE, 'skills');\nexport const TITAN_LOGS_DIR = join(TITAN_HOME, 'logs');\nexport const TITAN_MEMORY_DIR = join(TITAN_HOME, 'memory');\n\n// Workspace prompt files (injected into agent context)\nexport const AGENTS_MD = join(TITAN_WORKSPACE, 'AGENTS.md');\nexport const SOUL_MD = join(TITAN_WORKSPACE, 'SOUL.md');\nexport const TOOLS_MD = join(TITAN_WORKSPACE, 'TOOLS.md');\nexport const TITAN_MD_FILENAME = 'TITAN.md';\nexport const AUTOPILOT_MD = join(TITAN_HOME, 'AUTOPILOT.md');\nexport const AUTOPILOT_RUNS_PATH = join(TITAN_HOME, 'autopilot-runs.jsonl');\nexport const TITAN_CREDENTIALS_DIR = join(TITAN_HOME, 'credentials');\n\n// Income & lead tracking\nexport const INCOME_LEDGER_PATH = join(TITAN_HOME, 'income-ledger.jsonl');\nexport const FREELANCE_LEADS_PATH = join(TITAN_HOME, 'freelance-leads.jsonl');\nexport const FREELANCE_PROFILE_PATH = join(TITAN_HOME, 'freelance-profile.json');\nexport const LEADS_PATH = join(TITAN_HOME, 'leads.jsonl');\nexport const TELEMETRY_EVENTS_PATH = join(TITAN_HOME, 'telemetry-events.jsonl');\nexport const SOMADRIVE_STATE_PATH = join(TITAN_HOME, 'soma-drive-state.json');\nexport const ACTIVITY_LOG_PATH = join(TITAN_HOME, 'activity-log.jsonl');\n\n// v6.1.0 — Mission Chat\nexport const MISSIONS_DIR = join(TITAN_HOME, 'missions');\nexport const PLAYS_DIR = join(TITAN_HOME, 'plays');\n\n// Gateway defaults\nexport const DEFAULT_GATEWAY_HOST = '0.0.0.0';\nexport const DEFAULT_GATEWAY_PORT = 48420;\nexport const DEFAULT_WEB_PORT = 48421;\n\n// Agent defaults\n// v6.0.1 — Hardcoded floor only. The actual default at runtime is picked\n// by `getDefaultModelId()` in `src/providers/defaultModel.ts`, which\n// detects the user's available API keys (Anthropic / OpenAI / Google /\n// OpenRouter) and selects accordingly. Falls back to local Ollama when\n// no cloud keys are set. This constant is the last-resort fallback for\n// callsites that don't go through the schema thunk.\nexport const DEFAULT_MODEL = 'anthropic/claude-sonnet-4-20250514';\n/** v5.4.1: User-preference ceiling. Providers clamp per-model via\n * clampMaxTokens() so this can be high without causing 400s on\n * capped endpoints (e.g. Claude Sonnet 4 8K, Cohere 4K). */\nexport const DEFAULT_MAX_TOKENS = 200000;\nexport const DEFAULT_TEMPERATURE = 0.7;\nexport const MAX_CONTEXT_MESSAGES = 50;\nexport const SESSION_TIMEOUT_MS = 30 * 60 * 1000; // 30 minutes\n\n// Security\nexport const DEFAULT_SANDBOX_MODE = 'host';\n/** Default allowed tools. Empty = allow ALL registered tools.\n * Use security.deniedTools to block specific tools instead. */\nexport const ALLOWED_TOOLS_DEFAULT: string[] = [];\nexport const DENIED_TOOLS_DEFAULT: string[] = [];\n"],"mappings":";AAGA,SAAS,eAAe;AACxB,SAAS,YAAY;AAEd,MAAM,gBAAgB;AACtB,MAAM,iBAAiB;AACvB,MAAM,aAAa;AACnB,MAAM,kBAAkB;AACxB,MAAM,mBAAmB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,WAW1B,aAAa;AAAA;AAYnB,SAAS,mBAA2B;AAChC,QAAM,UAAU,QAAQ,IAAI;AAC5B,MAAI,WAAW,QAAQ,KAAK,EAAE,SAAS,GAAG;AACtC,UAAM,UAAU,QAAQ,KAAK;AAC7B,QAAI,QAAQ,WAAW,IAAI,GAAG;AAC1B,aAAO,KAAK,QAAQ,GAAG,QAAQ,MAAM,CAAC,CAAC;AAAA,IAC3C;AACA,QAAI,YAAY,KAAK;AACjB,aAAO,QAAQ;AAAA,IACnB;AACA,WAAO;AAAA,EACX;AACA,SAAO,KAAK,QAAQ,GAAG,QAAQ;AACnC;AACO,MAAM,aAAa,iBAAiB;AACpC,MAAM,oBAAoB,KAAK,YAAY,YAAY;AACvD,MAAM,gBAAgB,KAAK,YAAY,UAAU;AACjD,MAAM,kBAAkB,KAAK,YAAY,WAAW;AACpD,MAAM,mBAAmB,KAAK,iBAAiB,QAAQ;AACvD,MAAM,iBAAiB,KAAK,YAAY,MAAM;AAC9C,MAAM,mBAAmB,KAAK,YAAY,QAAQ;AAGlD,MAAM,YAAY,KAAK,iBAAiB,WAAW;AACnD,MAAM,UAAU,KAAK,iBAAiB,SAAS;AAC/C,MAAM,WAAW,KAAK,iBAAiB,UAAU;AACjD,MAAM,oBAAoB;AAC1B,MAAM,eAAe,KAAK,YAAY,cAAc;AACpD,MAAM,sBAAsB,KAAK,YAAY,sBAAsB;AACnE,MAAM,wBAAwB,KAAK,YAAY,aAAa;AAG5D,MAAM,qBAAqB,KAAK,YAAY,qBAAqB;AACjE,MAAM,uBAAuB,KAAK,YAAY,uBAAuB;AACrE,MAAM,yBAAyB,KAAK,YAAY,wBAAwB;AACxE,MAAM,aAAa,KAAK,YAAY,aAAa;AACjD,MAAM,wBAAwB,KAAK,YAAY,wBAAwB;AACvE,MAAM,uBAAuB,KAAK,YAAY,uBAAuB;AACrE,MAAM,oBAAoB,KAAK,YAAY,oBAAoB;AAG/D,MAAM,eAAe,KAAK,YAAY,UAAU;AAChD,MAAM,YAAY,KAAK,YAAY,OAAO;AAG1C,MAAM,uBAAuB;AAC7B,MAAM,uBAAuB;AAC7B,MAAM,mBAAmB;AASzB,MAAM,gBAAgB;AAItB,MAAM,qBAAqB;AAC3B,MAAM,sBAAsB;AAC5B,MAAM,uBAAuB;AAC7B,MAAM,qBAAqB,KAAK,KAAK;AAGrC,MAAM,uBAAuB;AAG7B,MAAM,wBAAkC,CAAC;AACzC,MAAM,uBAAiC,CAAC;","names":[]}
1
+ {"version":3,"sources":["../../src/utils/constants.ts"],"sourcesContent":["/**\n * TITAN Constants\n */\nimport { homedir } from 'os';\nimport { join } from 'path';\n\nexport const TITAN_VERSION = '7.2.1';\nexport const TITAN_CODENAME = 'Living Canvas';\nexport const TITAN_NAME = 'TITAN';\nexport const TITAN_FULL_NAME = 'The Intelligent Task Automation Network';\nexport const TITAN_ASCII_LOGO = `\n╔══════════════════════════════════════════════════════╗\n║ ║\n║ ████████╗██╗████████╗ █████╗ ███╗ ██╗ ║\n║ ██║ ██║ ██║ ██╔══██╗████╗ ██║ ║\n║ ██║ ██║ ██║ ███████║██╔██╗ ██║ ║\n║ ██║ ██║ ██║ ██╔══██║██║╚██╗██║ ║\n║ ██║ ██║ ██║ ██║ ██║██║ ╚████║ ║\n║ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═══╝ ║\n║ ║\n║ The Intelligent Task Automation Network ║\n║ v${TITAN_VERSION} • by Tony Elliott ║\n╚══════════════════════════════════════════════════════╝`;\n\n// Paths\n// Hunt Finding #03 (2026-04-14): honor TITAN_HOME env var if set.\n// Previously this was hardcoded to `~/.titan`, which meant:\n// - Docker containers couldn't override the config path\n// - Shared machines couldn't isolate per-user state\n// - Test fixtures couldn't run against an isolated home\n// - The systemd unit's `Environment=TITAN_HOME=...` was silently ignored\n// The env var is read once at module load (constants are resolved at import time).\n// If TITAN_HOME starts with `~/`, expand it to the user's home dir.\nfunction resolveTitanHome(): string {\n const envHome = process.env.TITAN_HOME;\n if (envHome && envHome.trim().length > 0) {\n const trimmed = envHome.trim();\n if (trimmed.startsWith('~/')) {\n return join(homedir(), trimmed.slice(2));\n }\n if (trimmed === '~') {\n return homedir();\n }\n return trimmed;\n }\n return join(homedir(), '.titan');\n}\nexport const TITAN_HOME = resolveTitanHome();\nexport const TITAN_CONFIG_PATH = join(TITAN_HOME, 'titan.json');\nexport const TITAN_DB_PATH = join(TITAN_HOME, 'titan.db');\nexport const TITAN_WORKSPACE = join(TITAN_HOME, 'workspace');\nexport const TITAN_SKILLS_DIR = join(TITAN_WORKSPACE, 'skills');\nexport const TITAN_LOGS_DIR = join(TITAN_HOME, 'logs');\nexport const TITAN_MEMORY_DIR = join(TITAN_HOME, 'memory');\n\n// Workspace prompt files (injected into agent context)\nexport const AGENTS_MD = join(TITAN_WORKSPACE, 'AGENTS.md');\nexport const SOUL_MD = join(TITAN_WORKSPACE, 'SOUL.md');\nexport const TOOLS_MD = join(TITAN_WORKSPACE, 'TOOLS.md');\nexport const TITAN_MD_FILENAME = 'TITAN.md';\nexport const AUTOPILOT_MD = join(TITAN_HOME, 'AUTOPILOT.md');\nexport const AUTOPILOT_RUNS_PATH = join(TITAN_HOME, 'autopilot-runs.jsonl');\nexport const TITAN_CREDENTIALS_DIR = join(TITAN_HOME, 'credentials');\n\n// Income & lead tracking\nexport const INCOME_LEDGER_PATH = join(TITAN_HOME, 'income-ledger.jsonl');\nexport const FREELANCE_LEADS_PATH = join(TITAN_HOME, 'freelance-leads.jsonl');\nexport const FREELANCE_PROFILE_PATH = join(TITAN_HOME, 'freelance-profile.json');\nexport const LEADS_PATH = join(TITAN_HOME, 'leads.jsonl');\nexport const TELEMETRY_EVENTS_PATH = join(TITAN_HOME, 'telemetry-events.jsonl');\nexport const SOMADRIVE_STATE_PATH = join(TITAN_HOME, 'soma-drive-state.json');\nexport const ACTIVITY_LOG_PATH = join(TITAN_HOME, 'activity-log.jsonl');\n\n// v6.1.0 — Mission Chat\nexport const MISSIONS_DIR = join(TITAN_HOME, 'missions');\nexport const PLAYS_DIR = join(TITAN_HOME, 'plays');\n\n// Gateway defaults\nexport const DEFAULT_GATEWAY_HOST = '0.0.0.0';\nexport const DEFAULT_GATEWAY_PORT = 48420;\nexport const DEFAULT_WEB_PORT = 48421;\n\n// Agent defaults\n// v6.0.1 — Hardcoded floor only. The actual default at runtime is picked\n// by `getDefaultModelId()` in `src/providers/defaultModel.ts`, which\n// detects the user's available API keys (Anthropic / OpenAI / Google /\n// OpenRouter) and selects accordingly. Falls back to local Ollama when\n// no cloud keys are set. This constant is the last-resort fallback for\n// callsites that don't go through the schema thunk.\nexport const DEFAULT_MODEL = 'anthropic/claude-sonnet-4-20250514';\n/** v5.4.1: User-preference ceiling. Providers clamp per-model via\n * clampMaxTokens() so this can be high without causing 400s on\n * capped endpoints (e.g. Claude Sonnet 4 8K, Cohere 4K). */\nexport const DEFAULT_MAX_TOKENS = 200000;\nexport const DEFAULT_TEMPERATURE = 0.7;\nexport const MAX_CONTEXT_MESSAGES = 50;\nexport const SESSION_TIMEOUT_MS = 30 * 60 * 1000; // 30 minutes\n\n// Security\nexport const DEFAULT_SANDBOX_MODE = 'host';\n/** Default allowed tools. Empty = allow ALL registered tools.\n * Use security.deniedTools to block specific tools instead. */\nexport const ALLOWED_TOOLS_DEFAULT: string[] = [];\nexport const DENIED_TOOLS_DEFAULT: string[] = [];\n"],"mappings":";AAGA,SAAS,eAAe;AACxB,SAAS,YAAY;AAEd,MAAM,gBAAgB;AACtB,MAAM,iBAAiB;AACvB,MAAM,aAAa;AACnB,MAAM,kBAAkB;AACxB,MAAM,mBAAmB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,WAW1B,aAAa;AAAA;AAYnB,SAAS,mBAA2B;AAChC,QAAM,UAAU,QAAQ,IAAI;AAC5B,MAAI,WAAW,QAAQ,KAAK,EAAE,SAAS,GAAG;AACtC,UAAM,UAAU,QAAQ,KAAK;AAC7B,QAAI,QAAQ,WAAW,IAAI,GAAG;AAC1B,aAAO,KAAK,QAAQ,GAAG,QAAQ,MAAM,CAAC,CAAC;AAAA,IAC3C;AACA,QAAI,YAAY,KAAK;AACjB,aAAO,QAAQ;AAAA,IACnB;AACA,WAAO;AAAA,EACX;AACA,SAAO,KAAK,QAAQ,GAAG,QAAQ;AACnC;AACO,MAAM,aAAa,iBAAiB;AACpC,MAAM,oBAAoB,KAAK,YAAY,YAAY;AACvD,MAAM,gBAAgB,KAAK,YAAY,UAAU;AACjD,MAAM,kBAAkB,KAAK,YAAY,WAAW;AACpD,MAAM,mBAAmB,KAAK,iBAAiB,QAAQ;AACvD,MAAM,iBAAiB,KAAK,YAAY,MAAM;AAC9C,MAAM,mBAAmB,KAAK,YAAY,QAAQ;AAGlD,MAAM,YAAY,KAAK,iBAAiB,WAAW;AACnD,MAAM,UAAU,KAAK,iBAAiB,SAAS;AAC/C,MAAM,WAAW,KAAK,iBAAiB,UAAU;AACjD,MAAM,oBAAoB;AAC1B,MAAM,eAAe,KAAK,YAAY,cAAc;AACpD,MAAM,sBAAsB,KAAK,YAAY,sBAAsB;AACnE,MAAM,wBAAwB,KAAK,YAAY,aAAa;AAG5D,MAAM,qBAAqB,KAAK,YAAY,qBAAqB;AACjE,MAAM,uBAAuB,KAAK,YAAY,uBAAuB;AACrE,MAAM,yBAAyB,KAAK,YAAY,wBAAwB;AACxE,MAAM,aAAa,KAAK,YAAY,aAAa;AACjD,MAAM,wBAAwB,KAAK,YAAY,wBAAwB;AACvE,MAAM,uBAAuB,KAAK,YAAY,uBAAuB;AACrE,MAAM,oBAAoB,KAAK,YAAY,oBAAoB;AAG/D,MAAM,eAAe,KAAK,YAAY,UAAU;AAChD,MAAM,YAAY,KAAK,YAAY,OAAO;AAG1C,MAAM,uBAAuB;AAC7B,MAAM,uBAAuB;AAC7B,MAAM,mBAAmB;AASzB,MAAM,gBAAgB;AAItB,MAAM,qBAAqB;AAC3B,MAAM,sBAAsB;AAC5B,MAAM,uBAAuB;AAC7B,MAAM,qBAAqB,KAAK,KAAK;AAGrC,MAAM,uBAAuB;AAG7B,MAAM,wBAAkC,CAAC;AACzC,MAAM,uBAAiC,CAAC;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "titan-agent",
3
- "version": "7.2.0",
3
+ "version": "7.2.1",
4
4
  "description": "TITAN — the AI agent that provably teaches itself. TypeScript agent framework with replay-verified self-learned skills (Muscle Memory), one-minute Welcome Mode setup, a living desk with heartbeat/reminders/proactive memory, multi-agent orchestration, 36-provider routing, approval gates, receipts, evals, and a React Mission Control dashboard. Open-source, MIT.",
5
5
  "author": "Tony Elliott (https://github.com/Djtony707)",
6
6
  "repository": {
package/ui/dist/sw.js CHANGED
@@ -20,7 +20,7 @@
20
20
  * but a default falls back to the source-controlled value here.
21
21
  */
22
22
 
23
- const CACHE_NAME = 'titan-' + ('1783396713877');
23
+ const CACHE_NAME = 'titan-' + ('1783397719662');
24
24
  const ASSETS_PREFIX = '/assets/';
25
25
 
26
26
  self.addEventListener('install', (event) => {