titan-agent 5.3.0 → 5.3.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.
Files changed (105) hide show
  1. package/dist/agent/agent.js +33 -4
  2. package/dist/agent/agent.js.map +1 -1
  3. package/dist/agent/subAgent.js +1 -1
  4. package/dist/agent/subAgent.js.map +1 -1
  5. package/dist/config/schema.js +10 -0
  6. package/dist/config/schema.js.map +1 -1
  7. package/dist/eval/record.js +21 -2
  8. package/dist/eval/record.js.map +1 -1
  9. package/dist/gateway/metrics.js +26 -3
  10. package/dist/gateway/metrics.js.map +1 -1
  11. package/dist/gateway/server.js +58 -6
  12. package/dist/gateway/server.js.map +1 -1
  13. package/dist/utils/constants.js +1 -1
  14. package/dist/utils/constants.js.map +1 -1
  15. package/package.json +1 -1
  16. package/ui/dist/assets/{AuditPanel-C31LRHZX.js → AuditPanel-CM6Wg9hO.js} +1 -1
  17. package/ui/dist/assets/{AutonomyPanel-CxQU72ZY.js → AutonomyPanel-CESx3ANg.js} +1 -1
  18. package/ui/dist/assets/{AutopilotPanel-D4FnBwJm.js → AutopilotPanel-DtEet1hJ.js} +1 -1
  19. package/ui/dist/assets/{AutoresearchPanel-BYHXZ9AO.js → AutoresearchPanel-DR47NqT5.js} +1 -1
  20. package/ui/dist/assets/{BackupPanel-C4CQKf2P.js → BackupPanel-BGP8p3l3.js} +1 -1
  21. package/ui/dist/assets/{BrowserPanel-C-OFYyLm.js → BrowserPanel-C15x9bLn.js} +1 -1
  22. package/ui/dist/assets/{CPAgents-CvkZDm_3.js → CPAgents-DYUtPzSq.js} +1 -1
  23. package/ui/dist/assets/{CPDashboard-JmBLBbj7.js → CPDashboard-Bf0-SyCh.js} +1 -1
  24. package/ui/dist/assets/{CPFiles-BDToRw0a.js → CPFiles-CxgxjQcO.js} +1 -1
  25. package/ui/dist/assets/{CPGoals-Dh9qJNWa.js → CPGoals-BsmCMTvT.js} +1 -1
  26. package/ui/dist/assets/{CPInbox-B6iaIbNG.js → CPInbox-tMSbmQ9H.js} +1 -1
  27. package/ui/dist/assets/{CPSocial-CsFrwZRC.js → CPSocial-nb-j7sOE.js} +1 -1
  28. package/ui/dist/assets/{ChannelsPanel-D-S4ktFn.js → ChannelsPanel-DP5C2OKd.js} +1 -1
  29. package/ui/dist/assets/{CheckpointsPanel-D-sP9ZuS.js → CheckpointsPanel-DlranVLZ.js} +1 -1
  30. package/ui/dist/assets/{CommandPostHub-BhlNyeDH.js → CommandPostHub-BgxIa4Ev.js} +3 -3
  31. package/ui/dist/assets/{CronPanel-Bf3rV7N2.js → CronPanel-LoT5yKwJ.js} +1 -1
  32. package/ui/dist/assets/{DaemonPanel-GGBWjTG2.js → DaemonPanel-DBGMqaE_.js} +1 -1
  33. package/ui/dist/assets/{DataTable-D2Px4o6f.js → DataTable-B2Ma8hfi.js} +1 -1
  34. package/ui/dist/assets/{EmptyState-DH6-Jy6A.js → EmptyState-CcKyk5Yn.js} +1 -1
  35. package/ui/dist/assets/EvalHarnessPanel-BqtMc1ZM.js +2 -0
  36. package/ui/dist/assets/{EvalPanel-CdjxzHlJ.js → EvalPanel-Bc33j0pN.js} +1 -1
  37. package/ui/dist/assets/{FilesPanel-Dz8TFydL.js → FilesPanel-3QKvrWPo.js} +1 -1
  38. package/ui/dist/assets/{FleetPanel-CWwWWTD4.js → FleetPanel-CSsXuQYj.js} +1 -1
  39. package/ui/dist/assets/{HomelabPanel-C7VBV7AC.js → HomelabPanel-DhrjTX9m.js} +1 -1
  40. package/ui/dist/assets/{InfraView-B1TgXARJ.js → InfraView-CR6HyrL6.js} +2 -2
  41. package/ui/dist/assets/{InlineEditableField-DOJNOL8m.js → InlineEditableField-CnvF-yFR.js} +1 -1
  42. package/ui/dist/assets/{Input-BgyHgQ3D.js → Input-GTHp2Rkr.js} +1 -1
  43. package/ui/dist/assets/{IntegrationsPanel-O26b4nhv.js → IntegrationsPanel-CymCRE3T.js} +1 -1
  44. package/ui/dist/assets/{IntelligenceView-DUhTQ8f_.js → IntelligenceView-C1IHxJRC.js} +2 -2
  45. package/ui/dist/assets/{LearningPanel-DX5S9ovg.js → LearningPanel-DOCES3lH.js} +1 -1
  46. package/ui/dist/assets/{LogsPanel-DdeTnATQ.js → LogsPanel-BLnAqEaZ.js} +1 -1
  47. package/ui/dist/assets/{McpPanel-BpXWrP1a.js → McpPanel-ChUzmr3z.js} +1 -1
  48. package/ui/dist/assets/{MemoryGraphPanel-CNkZmTUy.js → MemoryGraphPanel-Bzvjmzvk.js} +1 -1
  49. package/ui/dist/assets/{MemoryWikiPanel-o4L8Df-n.js → MemoryWikiPanel-Dwk3Aqwd.js} +1 -1
  50. package/ui/dist/assets/{MeshPanel-DMBQJFCC.js → MeshPanel-C3LJSlht.js} +1 -1
  51. package/ui/dist/assets/{NvidiaPanel-C8P-tJFG.js → NvidiaPanel-CeZK_-CV.js} +1 -1
  52. package/ui/dist/assets/{OrganismPanel-CcfHDWDk.js → OrganismPanel-BB6YOiQV.js} +1 -1
  53. package/ui/dist/assets/{OverviewPanel-BSotI1Zv.js → OverviewPanel-BmtBhQnv.js} +1 -1
  54. package/ui/dist/assets/{PageHeader-DPJuAgJk.js → PageHeader-BimceqQo.js} +1 -1
  55. package/ui/dist/assets/{PaperclipPanel-aXoXUjo6.js → PaperclipPanel-C-brgwA3.js} +1 -1
  56. package/ui/dist/assets/{PersonasPanel-DdPZxz2C.js → PersonasPanel-L1j78p6H.js} +1 -1
  57. package/ui/dist/assets/{RecipesPanel-D7qffXQN.js → RecipesPanel-34lCfynJ.js} +1 -1
  58. package/ui/dist/assets/{SecurityPanel-BDRK5el7.js → SecurityPanel-CBTPWLj6.js} +1 -1
  59. package/ui/dist/assets/{SelfImprovePanel-oYiMwFnA.js → SelfImprovePanel-BrPbFHhG.js} +1 -1
  60. package/ui/dist/assets/{SelfProposalsPanel-DOpNU_rr.js → SelfProposalsPanel-lNmiDThB.js} +1 -1
  61. package/ui/dist/assets/{SessionsPanel-eRbM3D9P.js → SessionsPanel-DAEYIn83.js} +1 -1
  62. package/ui/dist/assets/{SessionsTab-Jq3UKQCI.js → SessionsTab-JQbltWww.js} +1 -1
  63. package/ui/dist/assets/{SettingsPanel-DBIvKUYY.js → SettingsPanel-CzRROAYQ.js} +1 -1
  64. package/ui/dist/assets/{SettingsView-yfSY4OLt.js → SettingsView-CN7ii2uw.js} +2 -2
  65. package/ui/dist/assets/{SkeletonLoader-D1d-Gyyg.js → SkeletonLoader-atQtpcF5.js} +1 -1
  66. package/ui/dist/assets/{SkillsPanel-bubl9nag.js → SkillsPanel-DlFs2ih7.js} +1 -1
  67. package/ui/dist/assets/{SomaView-D3aFL8Tw.js → SomaView-Ba642Oqb.js} +1 -1
  68. package/ui/dist/assets/{StatCard-CEVFsz7t.js → StatCard-DciE_Iqc.js} +1 -1
  69. package/ui/dist/assets/{StatusBadge-DxeA9LNd.js → StatusBadge-BtfSPoW2.js} +1 -1
  70. package/ui/dist/assets/{TeamsPanel-D6IJJIR_.js → TeamsPanel-DKQ5z2Qe.js} +1 -1
  71. package/ui/dist/assets/{TelemetryPanel-SMPebdjQ.js → TelemetryPanel-B6KAc55Q.js} +1 -1
  72. package/ui/dist/assets/{TitanCanvas-BQU1yxqf.js → TitanCanvas-C-s0A-lv.js} +3 -3
  73. package/ui/dist/assets/{ToolsView-DgP4uRPr.js → ToolsView-Dei0KMP0.js} +2 -2
  74. package/ui/dist/assets/{Tooltip-CNPQr7IO.js → Tooltip-70UK0E2I.js} +1 -1
  75. package/ui/dist/assets/{TraceViewer-BbISy_ET.js → TraceViewer-BniolyBx.js} +1 -1
  76. package/ui/dist/assets/{TrainingPanel-BdCHcv6t.js → TrainingPanel-Bz4CTPGW.js} +1 -1
  77. package/ui/dist/assets/{VoiceOverlay-l6yoasVz.js → VoiceOverlay-CmNCrLcd.js} +1 -1
  78. package/ui/dist/assets/{VramPanel-XLhmen92.js → VramPanel-Xh_OtRDR.js} +1 -1
  79. package/ui/dist/assets/{WatchView-Bt-lNNWJ.js → WatchView-C-sGFpVy.js} +1 -1
  80. package/ui/dist/assets/{WorkTab-IG-F6Qll.js → WorkTab-BjLNmgIK.js} +1 -1
  81. package/ui/dist/assets/{WorkflowsPanel-DsMpnwLK.js → WorkflowsPanel-CvgQU1xI.js} +1 -1
  82. package/ui/dist/assets/{arrow-left-C_H9Z2Tm.js → arrow-left-DwqHtJiU.js} +1 -1
  83. package/ui/dist/assets/{chart-column-rR6tb72l.js → chart-column-BtNO6sRy.js} +1 -1
  84. package/ui/dist/assets/{circle-check-big-B1hMwau0.js → circle-check-big-DZRE_MbN.js} +1 -1
  85. package/ui/dist/assets/{dollar-sign-DhYwsTnR.js → dollar-sign-aVG3a5eL.js} +1 -1
  86. package/ui/dist/assets/{download-UDDcAlZC.js → download-BxiWJU4G.js} +1 -1
  87. package/ui/dist/assets/{eye-off-Cx0M_VQb.js → eye-off-CkgfFYhm.js} +1 -1
  88. package/ui/dist/assets/{funnel-B7YvM1ei.js → funnel-PkLdxKyC.js} +1 -1
  89. package/ui/dist/assets/{git-branch-BhTBN3J6.js → git-branch-BM-Gw95X.js} +1 -1
  90. package/ui/dist/assets/{index-D7Clon2u.js → index-CahJbWSR.js} +2 -2
  91. package/ui/dist/assets/{layers-B6jDzitD.js → layers-BuGf4FIJ.js} +1 -1
  92. package/ui/dist/assets/{legacy-av079XKu.js → legacy-CR6o4t-y.js} +1 -1
  93. package/ui/dist/assets/{lightbulb-DRuQ3Chf.js → lightbulb-n8gc_XAL.js} +1 -1
  94. package/ui/dist/assets/{pause-DqkRWPB_.js → pause-DCV52koX.js} +1 -1
  95. package/ui/dist/assets/{play-hUyR3CVS.js → play-CcJ9BnCh.js} +1 -1
  96. package/ui/dist/assets/{plug-CvpyjJt_.js → plug-CfWBXfCl.js} +1 -1
  97. package/ui/dist/assets/{proxy-Cc5bR828.js → proxy-CzZDfLmm.js} +1 -1
  98. package/ui/dist/assets/{square-CdiC0J8Z.js → square-DJpUhlxi.js} +1 -1
  99. package/ui/dist/assets/{target-DemL8_0v.js → target-DWcmM_9m.js} +1 -1
  100. package/ui/dist/assets/{toggle-right-Dsk892k5.js → toggle-right-YusFQ69L.js} +1 -1
  101. package/ui/dist/assets/{trash-2-Byj4OvKB.js → trash-2-CK7yQ55V.js} +1 -1
  102. package/ui/dist/assets/{trending-up-Dh_CffGX.js → trending-up-DGjFyubC.js} +1 -1
  103. package/ui/dist/assets/{trophy-DDr2AePx.js → trophy-uQv_NgDB.js} +1 -1
  104. package/ui/dist/index.html +1 -1
  105. package/ui/dist/assets/EvalHarnessPanel-Cz9dRg61.js +0 -2
@@ -412,7 +412,7 @@ ${personaContent}`;
412
412
  model,
413
413
  messages,
414
414
  tools: availableTools.length > 0 ? availableTools : void 0,
415
- maxTokens: titanConfig.agent.maxTokens || 4096,
415
+ maxTokens: config.maxTokens ?? titanConfig.agent.maxTokens ?? 4096,
416
416
  temperature: 0.2
417
417
  });
418
418
  if (!response.toolCalls || response.toolCalls.length === 0) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/agent/subAgent.ts"],"sourcesContent":["/**\n * TITAN — Universal Sub-Agent\n * Spawns isolated sub-agents that reuse processMessage() with constrained toolsets.\n * Generalizes the swarm.ts pattern into a universal delegation system.\n *\n * Key constraints:\n * - Max depth: 1 (sub-agents cannot spawn sub-sub-agents)\n * - Inherits parent's autonomy mode (can't escalate)\n * - Own stall/loop counters (isolated from parent)\n * - Cost counts toward parent session budget\n */\nimport { chat } from '../providers/router.js';\nimport { executeTools, getToolDefinitions } from './toolRunner.js';\nimport { loadConfig } from '../config/config.js';\nimport type { ChatMessage, ToolDefinition } from '../providers/base.js';\nimport logger from '../utils/logger.js';\nimport { resolveToolsFromCategories, type ToolCategory } from './toolCategories.js';\nimport { registerMailbox, unregisterMailbox, drainMessages, formatMessagesForContext } from './messageBus.js';\nimport { acquireAgent, releaseAgent, createPooledAgent, type PooledAgent } from './agentPool.js';\nimport { getActivePersonaContent } from '../personas/manager.js';\nimport { assembleSystemPrompt } from './systemPromptParts.js';\n\nconst COMPONENT = 'SubAgent';\n\n/** Currently running sub-agent IDs (Set for accurate tracking, prevents counter desync) */\nconst activeSubAgentIds = new Set<string>();\n\nexport interface SubAgentConfig {\n name: string;\n task: string;\n /** Whitelist of tool names this sub-agent can use. Empty = all tools */\n tools?: string[];\n /** Tool categories — resolved to tool names at runtime */\n toolCategories?: ToolCategory[];\n /** Create a git worktree for filesystem isolation */\n useWorktree?: boolean;\n /** Model override (defaults to fast alias) */\n model?: string;\n /** Model tier — resolved via modelAliases (cloud/smart/fast/local) */\n tier?: ModelTier;\n /** System prompt override */\n systemPrompt?: string;\n /** Persona ID to apply (from assets/personas/). Appended to system prompt. */\n persona?: string;\n /** Max tool rounds for this sub-agent (default: 10) */\n maxRounds?: number;\n /** Whether this is being called from within a sub-agent already */\n isNested?: boolean;\n /** Current nesting depth (0 = top-level sub-agent) */\n depth?: number;\n /** Progress callback: called each round with progress info */\n onProgress?: (round: number, totalRounds: number, agentName: string) => void;\n /** Opt-in to agent pool reuse — warm agents preserve context between tasks */\n reusePool?: boolean;\n /** Stream callbacks for Agent Watcher — tool_call, tool_end, round events */\n streamCallbacks?: {\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 };\n}\n\nexport interface SubAgentResult {\n content: string;\n toolsUsed: string[];\n success: boolean;\n durationMs: number;\n rounds: number;\n /** Whether the output passed validation checks */\n validated: boolean;\n}\n\n/**\n * Model tier for each sub-agent template.\n * - 'cloud': Heavy reasoning tasks → uses modelAliases.cloud (big cloud model)\n * - 'smart': Complex tasks → uses modelAliases.smart\n * - 'fast': Quick/simple tasks → uses modelAliases.fast (local)\n * - 'local': Must run locally → uses modelAliases.local\n */\nexport type ModelTier = 'cloud' | 'smart' | 'fast' | 'local';\n\n/** Built-in sub-agent templates with mapped personas */\nexport const SUB_AGENT_TEMPLATES: Record<string, Partial<SubAgentConfig> & { tier?: ModelTier }> = {\n explorer: {\n name: 'Explorer',\n persona: 'context-engineer',\n tools: ['web_search', 'web_fetch', 'browse_url', 'web_read', 'web_act'],\n systemPrompt: `You are the Explorer sub-agent. Your job is to research and gather information from the web using your tools.\n\nAvailable tools and when to use them:\n- web_search: MUST use this first — search for any topic, question, or keyword\n- web_fetch: MUST use after web_search — fetch full page content from the most relevant URLs\n- browse_url: Navigate to a specific URL for interactive content\n- web_read: Extract clean readable text from a URL\n- web_act: Click, scroll, or interact with page elements\n\nMUST rules:\n- MUST call web_search before attempting to answer from memory\n- MUST call web_fetch on at least 2 of the top search result URLs to get full content\n- MUST cross-verify key facts across multiple sources\n\nReturn a structured summary with: key findings, sources (with URLs), and confidence level for main claims.`,\n tier: 'smart',\n },\n coder: {\n name: 'Coder',\n persona: 'incremental-builder',\n tools: ['shell', 'read_file', 'write_file', 'edit_file', 'append_file', 'list_dir', 'code_exec'],\n systemPrompt: `You are the Coder sub-agent. Your job is to WRITE CODE using your tools. Lead with action, not exploration.\n\nCRITICAL RULES:\n- Your FIRST tool call should be write_file (for new files) or read_file (if modifying existing files)\n- Do NOT start with list_dir unless you genuinely don't know the project structure\n- NEVER output code as text — always use write_file or edit_file\n- NEVER describe what you would do — DO IT immediately\n- One sentence of planning max, then CALL THE TOOL\n- After writing files, verify with shell (npm run build, etc.)\n- Keep each edit under 30 lines. For large changes, use multiple edit_file calls\n- Prefer editing existing files over creating new ones\n- No unnecessary comments, error handling, or features beyond scope\n\nTool priority:\n1. write_file — create new files with complete working code\n2. edit_file — modify existing files (read first)\n3. shell — run commands, install packages, verify builds\n4. read_file — only when you need to understand existing code before editing\n5. list_dir — only when you don't know the structure at all\n\nReturn a summary of what was created/modified with exact file paths.`,\n tier: 'smart',\n },\n browser: {\n name: 'Browser',\n persona: 'browser-tester',\n tools: ['browse_url', 'browser_auto_nav', 'browser_search', 'web_read', 'web_act', 'browser_screenshot'],\n systemPrompt: `You are the Browser sub-agent. Your job is to interact with web pages — navigate, extract content, fill forms, and click buttons.\n\nAvailable tools and when to use them:\n- browse_url: MUST use to navigate to a URL before interacting with it\n- browser_screenshot: Take a screenshot to understand the current page state\n- web_read: Extract clean text content from the current page\n- web_act: Click buttons, fill inputs, scroll — use for interactive actions\n- browser_auto_nav: Auto-navigate complex flows (login, multi-step forms)\n- browser_search: Search within a page or site\n\nMUST rules:\n- MUST call browse_url first to open the page\n- MUST call web_read or browser_screenshot to understand page contents before acting\n- MUST report what you found, extracted, or accomplished with exact details\n\nReturn a clear report of what was found/done on the page.`,\n tier: 'fast',\n },\n analyst: {\n name: 'Analyst',\n persona: 'code-reviewer',\n tools: ['web_search', 'web_fetch', 'memory', 'graph_search', 'graph_remember'],\n systemPrompt: `You are the Analyst sub-agent. Your job is to analyze information, identify patterns, and produce structured analytical reports.\n\nAvailable tools and when to use them:\n- web_search: Search for data, statistics, reports, or comparisons\n- web_fetch: Fetch full content from specific URLs for deeper analysis\n- graph_search: Search the knowledge graph for previously stored context\n- graph_remember: Store important findings in the knowledge graph for future reference\n- memory: Store/retrieve key-value data points\n\nMUST rules:\n- MUST call web_search to gather current data before analyzing\n- MUST call graph_search to check for existing relevant context\n- MUST call graph_remember to store key findings after analysis\n- MUST base conclusions on data from tools — not assumptions\n\nReturn a structured analytical report with: executive summary, data findings, patterns identified, confidence levels, and recommendations.`,\n tier: 'cloud',\n },\n researcher: {\n name: 'Researcher',\n persona: 'trend-researcher',\n tools: ['web_search', 'web_read', 'web_fetch', 'rag_search', 'rag_ingest'],\n systemPrompt: `You are the Deep Researcher sub-agent. Your job is to systematically research a question using multiple sources and tools.\n\nAvailable tools and when to use them:\n- web_search: MUST call 2-4 times with different targeted queries to get broad coverage\n- web_fetch: MUST call on the top 3-5 URLs from search results to get full content\n- web_read: Extract clean text from a URL\n- rag_search: Search the local knowledge base for existing research on this topic\n- rag_ingest: Store important findings in the local knowledge base\n\nMethodology — follow in order:\n1. Call rag_search to check for existing research on this topic\n2. Break the question into 2-4 targeted search queries\n3. Call web_search for each query\n4. Call web_fetch on the most relevant URLs (at least 3 total)\n5. Cross-verify key claims across at least 2 independent sources\n6. Call rag_ingest to store important findings\n\nMUST rules:\n- MUST call web_search — never answer research questions from memory\n- MUST call web_fetch to read full content, not just search snippets\n- MUST cite all sources with URLs\n\nOutput format: executive summary → sections with headers → numbered citations [1], [2] → Sources list with URLs.`,\n maxRounds: 15,\n tier: 'cloud',\n },\n // ── Pipeline agents (DeerFlow-inspired) ────────────────────\n reporter: {\n name: 'Reporter',\n persona: 'documentation-writer',\n tools: ['read_file', 'write_file', 'web_fetch'],\n systemPrompt: `You are the Reporter sub-agent. Your job is to synthesize research findings into structured, publication-quality documents saved to disk.\n\nAvailable tools and when to use them:\n- read_file: Read any existing research notes or source files\n- write_file: MUST use to save the final report to disk — NEVER output report content as text\n- web_fetch: Fetch additional content from URLs if needed for a specific section\n\nMUST rules:\n- MUST call write_file to save the report — the output is a file on disk, not inline text\n- MUST call read_file if source material files are referenced in the task\n- If given a file path for the output, MUST save to exactly that path\n\nReport structure: executive summary → sections with markdown headers → confidence levels (High/Medium/Low) per claim → numbered citations → actionable conclusions.`,\n maxRounds: 10,\n tier: 'cloud',\n },\n fact_checker: {\n name: 'Fact Checker',\n persona: 'context-engineer',\n tools: ['web_search', 'web_fetch'],\n systemPrompt: `You are the Fact Checker sub-agent. Your job is to verify specific claims against multiple independent sources.\n\nAvailable tools and when to use them:\n- web_search: MUST call for each claim to find sources that confirm or refute it\n- web_fetch: MUST call to read the full source content — search snippets are not enough\n\nFor each claim:\n1. Call web_search with 2 different queries targeting this claim\n2. Call web_fetch on the top 2 sources for full content\n3. Compare the claim against what each source actually says\n4. Assign: Verified (3+ sources agree) / Likely (2 sources) / Unverified (1 source) / Disputed (sources conflict) / False (sources contradict)\n\nMUST rules:\n- MUST call web_search for every claim — never verify from memory\n- MUST call web_fetch to read full source content\n\nReturn a structured report: claim → status → evidence → sources used.`,\n maxRounds: 10,\n tier: 'smart',\n },\n // ── Dev agents (TITAN_DEV only) ──────────────────────────\n dev_debugger: {\n name: 'Dev Debugger',\n persona: 'debugger',\n tools: ['shell', 'read_file', 'write_file', 'debug_analyze', 'code_analyze'],\n systemPrompt: `You are the Dev Debugger sub-agent for the TITAN framework. Your job is to find and fix bugs by reading actual code and running diagnostic commands.\n\nAvailable tools and when to use them:\n- read_file: MUST use to read the source file containing the error before diagnosing\n- code_analyze: Analyze code structure, find potential issues, check for bugs\n- debug_analyze: Deep analysis of error messages, stack traces, and runtime issues\n- shell: Run the failing code, check logs, reproduce the error, verify the fix\n- write_file: Save the fixed code\n\nMUST rules:\n- MUST call read_file to read the actual source code — never diagnose from assumptions\n- MUST call code_analyze or debug_analyze to systematically identify the root cause\n- MUST call shell to reproduce the error before attempting a fix\n- MUST call shell again after the fix to verify it works\n- MUST call write_file to apply the fix — never describe the fix without implementing it\n\nReturn: root cause analysis, fix applied (with file path), verification result.`,\n maxRounds: 15,\n tier: 'smart',\n },\n dev_tester: {\n name: 'Dev Tester',\n persona: 'tdd-engineer',\n tools: ['shell', 'read_file', 'write_file', 'test_generate', 'code_exec'],\n systemPrompt: `You are the Dev Tester sub-agent for the TITAN framework. Your job is to generate, run, and fix tests using vitest.\n\nAvailable tools and when to use them:\n- read_file: MUST use to read the source code being tested before writing tests\n- test_generate: Generate comprehensive test cases from source code\n- write_file: MUST use to save the test file — never output tests as text\n- shell: Run vitest to execute tests and see results\n- code_exec: Quick isolated code execution for testing snippets\n\nMUST rules:\n- MUST call read_file to understand the code structure before writing tests\n- MUST call write_file to save test files — never output tests inline\n- MUST call shell to run the tests after writing them\n- MUST fix any test failures — don't stop at writing tests\n\nReturn: test file path, number of tests written, test results (pass/fail counts).`,\n maxRounds: 20,\n tier: 'fast',\n },\n dev_reviewer: {\n name: 'Dev Reviewer',\n persona: 'code-reviewer',\n tools: ['shell', 'read_file', 'code_review', 'code_analyze', 'deps_audit'],\n systemPrompt: `You are the Dev Reviewer sub-agent for the TITAN framework. Your job is to perform thorough multi-pass code review.\n\nAvailable tools and when to use them:\n- read_file: MUST use to read each file being reviewed — never review from memory\n- code_analyze: Structural analysis — complexity, patterns, architecture\n- code_review: Deep review — security, logic errors, performance issues\n- deps_audit: Check dependencies for vulnerabilities or outdated packages\n- shell: Run the code, check for type errors, run linter\n\nReview passes (do all):\n1. Security pass: call code_review with security focus\n2. Logic pass: call code_analyze for correctness and edge cases\n3. Performance pass: check for inefficiencies\n4. Dependencies pass: call deps_audit\n\nMUST rules:\n- MUST call read_file for each file reviewed\n- MUST run at least 2 review passes with different focuses\n- Flag real issues only — not style preferences\n\nReturn: structured findings by severity (Critical/Major/Minor), with file + line references.`,\n maxRounds: 10,\n tier: 'cloud',\n },\n dev_architect: {\n name: 'Dev Architect',\n persona: 'backend-architect',\n tools: ['shell', 'read_file', 'write_file', 'code_analyze', 'refactor_suggest', 'doc_generate'],\n systemPrompt: `You are the Dev Architect sub-agent for the TITAN framework. Your job is to analyze system architecture and implement structural improvements.\n\nAvailable tools and when to use them:\n- read_file: MUST use to read source files before proposing architectural changes\n- code_analyze: Analyze codebase structure, dependencies, coupling, and patterns\n- refactor_suggest: Get suggestions for structural improvements and refactoring\n- shell: Run the codebase to understand runtime behavior, check imports, count lines\n- write_file: MUST use to implement changes or create documentation\n- doc_generate: Generate architectural documentation\n\nMUST rules:\n- MUST call read_file and code_analyze before proposing any changes — never guess at structure\n- MUST call shell to understand actual file/folder organization\n- MUST call write_file to implement changes or save documentation — never describe changes inline\n- Think in systems and dependencies, not individual files\n\nReturn: architectural analysis, proposed changes with rationale, implementation summary with file paths.`,\n maxRounds: 15,\n tier: 'cloud',\n },\n};\n\n/**\n * Spawn a sub-agent that runs an isolated agent loop with constrained tools.\n * Returns when the sub-agent completes its task.\n */\nexport async function spawnSubAgent(config: SubAgentConfig): Promise<SubAgentResult> {\n const titanConfig = loadConfig();\n const startTime = Date.now();\n const currentDepth = config.depth ?? 0;\n const subAgentsCfg = (titanConfig as Record<string, unknown>).subAgents as Record<string, unknown> | undefined;\n const maxDepth = (subAgentsCfg?.maxDepth as number) ?? 4; // Increased from 2 → 4 for multi-level task decomposition\n\n // Check depth limit (configurable, default 2)\n if (currentDepth >= maxDepth || config.isNested) {\n return {\n content: `Error: Sub-agent nesting depth limit reached (depth ${currentDepth}/${maxDepth}).`,\n toolsUsed: [],\n success: false,\n durationMs: 0,\n rounds: 0,\n validated: false,\n };\n }\n\n // Check concurrency limit\n const maxConcurrent = (titanConfig as Record<string, unknown>).subAgents\n ? ((titanConfig as Record<string, unknown>).subAgents as Record<string, unknown>).maxConcurrent as number || 3\n : 3;\n\n if (activeSubAgentIds.size >= maxConcurrent) {\n return {\n content: `Error: Maximum concurrent sub-agents (${maxConcurrent}) reached. Wait for one to finish.`,\n toolsUsed: [],\n success: false,\n durationMs: 0,\n rounds: 0,\n validated: false,\n };\n }\n\n const agentName = config.name || 'SubAgent';\n const agentTrackingId = `${agentName}-${Date.now()}`;\n activeSubAgentIds.add(agentTrackingId);\n // Reduce max rounds by 30% per depth level to prevent runaway nesting\n const baseMaxRounds = config.maxRounds || 10;\n const depthReduction = Math.pow(0.7, currentDepth);\n const maxRounds = Math.max(3, Math.ceil(baseMaxRounds * depthReduction));\n // Model resolution priority:\n // 1. Explicit model passed in config (full model ID like 'ollama/qwen3.5:397b-cloud')\n // 2. Tier from config/template → resolve via modelAliases (cloud/smart/fast/local)\n // 3. subAgents.defaultModel config → resolve as alias name\n // 4. modelAliases.fast fallback\n const aliases = titanConfig.agent.modelAliases || {};\n const subDefaultAlias = (subAgentsCfg?.defaultModel as string) || 'fast';\n const tier = config.tier;\n const model = config.model\n || (tier ? aliases[tier] : undefined)\n || aliases[subDefaultAlias]\n || aliases.fast\n || 'ollama/qwen3.5:cloud';\n\n logger.info(COMPONENT, `Spawning ${agentName}: \"${config.task.slice(0, 80)}...\" (model: ${model}, maxRounds: ${maxRounds})`);\n\n // ── Message Bus: register mailbox for inter-agent communication ──\n registerMailbox(agentName);\n\n // Build tool whitelist\n let availableTools: ToolDefinition[];\n const allTools = getToolDefinitions();\n\n const canNest = currentDepth + 1 < maxDepth; // Allow spawn_agent only if depth allows\n\n // v4.7.0: Hermes-style blocked-for-children tool list. Regardless of\n // template, children never get: spawn_agent, memory_store/write,\n // send_message variants, outbound-publisher tools, or code_exec.\n // Protects against prompt-injection → memory corruption +\n // child-posts-as-Tony side channels.\n let blockedForChildren: Set<string> = new Set();\n try {\n const safety = await import('./subagentSafety.js');\n blockedForChildren = safety.BLOCKED_CHILD_TOOLS;\n } catch { /* optional */ }\n const isChild = currentDepth > 0; // top-level sub-agent = depth 0, but this is the sub-agent itself\n\n if (config.tools && config.tools.length > 0) {\n const toolSet = new Set(config.tools);\n if (!canNest) toolSet.delete('spawn_agent');\n // Ensure send_agent_message is always available for inter-agent comms\n toolSet.add('send_agent_message');\n availableTools = allTools.filter(t => toolSet.has(t.function.name));\n } else {\n availableTools = allTools.filter(t => canNest || t.function.name !== 'spawn_agent');\n }\n\n // v4.7.0: apply blocklist to whatever tools survived template filtering.\n // Primary agent (not a sub-agent) is never filtered. This is the last\n // line of defense — even if a template accidentally includes a\n // dangerous tool, children won't get it.\n if (isChild && blockedForChildren.size > 0) {\n availableTools = availableTools.filter(t => !blockedForChildren.has(t.function.name));\n }\n\n // Build system prompt: TITAN core (minimal) + role template + persona.\n //\n // v4.13 (plan-this-logical-ocean step 4): specialists used to get ONLY\n // the role template, with no TITAN identity / tool-use rules / per-model\n // overlay. On gemma4:31b-cloud this led to specialists hallucinating\n // `<|tool>call:...<|tool|>` markup as text because nothing told them\n // \"use the native tool_calls field, not Gemini's proxy artifact\".\n //\n // Minimal mode gives them: identity, ReAct loop + 3 core rules, tool\n // preference, runtime note, safety, truthfulness, and a per-model\n // overlay. No Delegation block (they don't re-delegate), no Continuous\n // Learning / Memory Tools walls — specialists get a focused task.\n const roleTemplate = config.systemPrompt || `You are the ${agentName} sub-agent of TITAN. Execute the task below using available tools. Be efficient and return a clear summary when done.`;\n const titanCore = assembleSystemPrompt({\n modelId: model,\n persona: config.persona || 'default',\n mode: 'minimal',\n });\n let systemPrompt = `${titanCore}\\n\\n## Role\\n${roleTemplate}`;\n\n // ── Persona: inject persona content from assets/personas/ ──\n const personaId = config.persona;\n if (personaId && personaId !== 'default') {\n try {\n const personaContent = getActivePersonaContent(personaId);\n if (personaContent) {\n systemPrompt += `\\n\\n## Persona: ${personaId}\\n${personaContent}`;\n logger.debug(COMPONENT, `[${agentName}] Applied persona: ${personaId}`);\n }\n } catch {\n logger.debug(COMPONENT, `[${agentName}] Persona \"${personaId}\" not found, using base prompt`);\n }\n }\n\n // ── Agent Pool: try to reuse a warm agent if pool enabled ──\n let pooledAgent: PooledAgent | null = null;\n let messages: ChatMessage[];\n\n if (config.reusePool) {\n const templateName = Object.entries(SUB_AGENT_TEMPLATES).find(\n ([, t]) => t.name === agentName || t.systemPrompt === config.systemPrompt,\n )?.[0] || agentName;\n\n pooledAgent = acquireAgent(templateName, model);\n if (pooledAgent) {\n // Reuse warm agent's conversation history + append new task\n messages = [\n ...pooledAgent.messages,\n { role: 'user', content: config.task },\n ];\n logger.info(COMPONENT, `Reusing pooled agent ${pooledAgent.id} for ${agentName} (${pooledAgent.messages.length} prior messages)`);\n } else {\n // No pooled agent — create fresh and register for later reuse\n pooledAgent = createPooledAgent(templateName, model);\n messages = [\n { role: 'system', content: systemPrompt },\n { role: 'user', content: config.task },\n ];\n }\n } else {\n messages = [\n { role: 'system', content: systemPrompt },\n { role: 'user', content: config.task },\n ];\n }\n\n const toolsUsed: string[] = [];\n let finalContent = '';\n let rounds = 0;\n\n try {\n for (let round = 0; round < maxRounds; round++) {\n rounds = round + 1;\n logger.debug(COMPONENT, `[${agentName}] Round ${rounds}/${maxRounds}`);\n config.onProgress?.(rounds, maxRounds, agentName);\n\n // ── Message Bus: drain incoming messages at start of each round ──\n const incoming = drainMessages(agentName);\n const incomingContext = formatMessagesForContext(incoming);\n if (incomingContext) {\n messages.push({ role: 'system', content: incomingContext });\n logger.debug(COMPONENT, `[${agentName}] Injected ${incoming.length} inter-agent messages`);\n }\n\n // B7: Abort if no tools available — prevents toolless agent from looping uselessly\n if (availableTools.length === 0 && round === 0) {\n logger.warn(COMPONENT, `[${agentName}] No tools available after filtering — aborting`);\n finalContent = `Error: No tools available for sub-agent \"${agentName}\". Check tool permissions and skill configuration.`;\n break;\n }\n\n const response = await chat({\n model,\n messages,\n tools: availableTools.length > 0 ? availableTools : undefined,\n maxTokens: titanConfig.agent.maxTokens || 4096,\n temperature: 0.2,\n });\n\n // No tool calls = done\n if (!response.toolCalls || response.toolCalls.length === 0) {\n finalContent = response.content || 'Task completed.';\n break;\n }\n\n // Process tool calls\n messages.push({\n role: 'assistant',\n content: response.content || '',\n toolCalls: response.toolCalls,\n });\n\n // Emit tool_call events for Agent Watcher\n if (config.streamCallbacks?.onToolCall) {\n for (const tc of response.toolCalls!) { config.streamCallbacks.onToolCall(tc.function.name, JSON.parse(tc.function.arguments || \"{}\")); }\n }\n const toolResults = await executeTools(response.toolCalls);\n // Emit tool_end events for Agent Watcher\n if (config.streamCallbacks?.onToolResult) {\n for (const tr of toolResults) { config.streamCallbacks.onToolResult(tr.name, tr.content, tr.durationMs || 0, tr.success !== false); }\n }\n\n for (const result of toolResults) {\n toolsUsed.push(result.name);\n messages.push({\n role: 'tool',\n content: result.content,\n toolCallId: result.toolCallId,\n name: result.name,\n });\n }\n\n // Last round fallback\n if (round === maxRounds - 1) {\n finalContent = response.content || 'Max rounds reached. Partial results returned.';\n }\n }\n\n const durationMs = Date.now() - startTime;\n logger.info(COMPONENT, `${agentName} completed in ${durationMs}ms (${rounds} rounds, ${toolsUsed.length} tool calls)`);\n\n // Output validation: check for empty, too-short, or error-like responses\n const validated = validateSubAgentOutput(finalContent);\n if (!validated) {\n logger.warn(COMPONENT, `[${agentName}] Output failed validation: \"${finalContent.slice(0, 80)}...\"`);\n }\n\n return {\n content: finalContent,\n toolsUsed: [...new Set(toolsUsed)],\n success: !finalContent.toLowerCase().startsWith('error') && validated,\n durationMs,\n rounds,\n validated,\n };\n } catch (err) {\n const durationMs = Date.now() - startTime;\n logger.error(COMPONENT, `${agentName} failed: ${(err as Error).message}`);\n return {\n content: `Sub-agent error: ${(err as Error).message}`,\n toolsUsed: [...new Set(toolsUsed)],\n success: false,\n durationMs,\n rounds,\n validated: false,\n };\n } finally {\n activeSubAgentIds.delete(agentTrackingId);\n\n // ── Message Bus: unregister mailbox on completion ──\n unregisterMailbox(agentName);\n\n // ── Agent Pool: release back to pool for future reuse ──\n if (config.reusePool && pooledAgent) {\n releaseAgent(pooledAgent.id, messages, toolsUsed, rounds);\n }\n }\n}\n\n/** Validate sub-agent output for quality */\nfunction validateSubAgentOutput(content: string): boolean {\n if (!content || content.trim().length < 20) return false;\n const lower = content.toLowerCase();\n if (lower.startsWith('i cannot') || lower.startsWith('i\\'m unable') || lower.startsWith('i am unable')) return false;\n if (lower.startsWith('error:') || lower.startsWith('sub-agent error:')) return false;\n if (lower === 'task completed.' && content.length < 20) return false;\n return true;\n}\n\n/** Get count of currently active sub-agents */\nexport function getActiveSubAgentCount(): number {\n return activeSubAgentIds.size;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/agent/subAgent.ts"],"sourcesContent":["/**\n * TITAN — Universal Sub-Agent\n * Spawns isolated sub-agents that reuse processMessage() with constrained toolsets.\n * Generalizes the swarm.ts pattern into a universal delegation system.\n *\n * Key constraints:\n * - Max depth: 1 (sub-agents cannot spawn sub-sub-agents)\n * - Inherits parent's autonomy mode (can't escalate)\n * - Own stall/loop counters (isolated from parent)\n * - Cost counts toward parent session budget\n */\nimport { chat } from '../providers/router.js';\nimport { executeTools, getToolDefinitions } from './toolRunner.js';\nimport { loadConfig } from '../config/config.js';\nimport type { ChatMessage, ToolDefinition } from '../providers/base.js';\nimport logger from '../utils/logger.js';\nimport { resolveToolsFromCategories, type ToolCategory } from './toolCategories.js';\nimport { registerMailbox, unregisterMailbox, drainMessages, formatMessagesForContext } from './messageBus.js';\nimport { acquireAgent, releaseAgent, createPooledAgent, type PooledAgent } from './agentPool.js';\nimport { getActivePersonaContent } from '../personas/manager.js';\nimport { assembleSystemPrompt } from './systemPromptParts.js';\n\nconst COMPONENT = 'SubAgent';\n\n/** Currently running sub-agent IDs (Set for accurate tracking, prevents counter desync) */\nconst activeSubAgentIds = new Set<string>();\n\nexport interface SubAgentConfig {\n name: string;\n task: string;\n /** Whitelist of tool names this sub-agent can use. Empty = all tools */\n tools?: string[];\n /** Tool categories — resolved to tool names at runtime */\n toolCategories?: ToolCategory[];\n /** Create a git worktree for filesystem isolation */\n useWorktree?: boolean;\n /** Model override (defaults to fast alias) */\n model?: string;\n /** Model tier — resolved via modelAliases (cloud/smart/fast/local) */\n tier?: ModelTier;\n /** System prompt override */\n systemPrompt?: string;\n /** Persona ID to apply (from assets/personas/). Appended to system prompt. */\n persona?: string;\n /** Max tool rounds for this sub-agent (default: 10) */\n maxRounds?: number;\n /** Max tokens per LLM call for this sub-agent (default: from config) */\n maxTokens?: number;\n /** Whether this is being called from within a sub-agent already */\n isNested?: boolean;\n /** Current nesting depth (0 = top-level sub-agent) */\n depth?: number;\n /** Progress callback: called each round with progress info */\n onProgress?: (round: number, totalRounds: number, agentName: string) => void;\n /** Opt-in to agent pool reuse — warm agents preserve context between tasks */\n reusePool?: boolean;\n /** Working directory for filesystem operations (future: scope file tools) */\n workspaceDir?: string;\n /** Tags for observability / filtering */\n tags?: string[];\n /** Stream callbacks for Agent Watcher — tool_call, tool_end, round events */\n streamCallbacks?: {\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 };\n}\n\nexport interface SubAgentResult {\n content: string;\n toolsUsed: string[];\n success: boolean;\n durationMs: number;\n rounds: number;\n /** Whether the output passed validation checks */\n validated: boolean;\n}\n\n/**\n * Model tier for each sub-agent template.\n * - 'cloud': Heavy reasoning tasks → uses modelAliases.cloud (big cloud model)\n * - 'smart': Complex tasks → uses modelAliases.smart\n * - 'fast': Quick/simple tasks → uses modelAliases.fast (local)\n * - 'local': Must run locally → uses modelAliases.local\n */\nexport type ModelTier = 'cloud' | 'smart' | 'fast' | 'local';\n\n/** Built-in sub-agent templates with mapped personas */\nexport const SUB_AGENT_TEMPLATES: Record<string, Partial<SubAgentConfig> & { tier?: ModelTier }> = {\n explorer: {\n name: 'Explorer',\n persona: 'context-engineer',\n tools: ['web_search', 'web_fetch', 'browse_url', 'web_read', 'web_act'],\n systemPrompt: `You are the Explorer sub-agent. Your job is to research and gather information from the web using your tools.\n\nAvailable tools and when to use them:\n- web_search: MUST use this first — search for any topic, question, or keyword\n- web_fetch: MUST use after web_search — fetch full page content from the most relevant URLs\n- browse_url: Navigate to a specific URL for interactive content\n- web_read: Extract clean readable text from a URL\n- web_act: Click, scroll, or interact with page elements\n\nMUST rules:\n- MUST call web_search before attempting to answer from memory\n- MUST call web_fetch on at least 2 of the top search result URLs to get full content\n- MUST cross-verify key facts across multiple sources\n\nReturn a structured summary with: key findings, sources (with URLs), and confidence level for main claims.`,\n tier: 'smart',\n },\n coder: {\n name: 'Coder',\n persona: 'incremental-builder',\n tools: ['shell', 'read_file', 'write_file', 'edit_file', 'append_file', 'list_dir', 'code_exec'],\n systemPrompt: `You are the Coder sub-agent. Your job is to WRITE CODE using your tools. Lead with action, not exploration.\n\nCRITICAL RULES:\n- Your FIRST tool call should be write_file (for new files) or read_file (if modifying existing files)\n- Do NOT start with list_dir unless you genuinely don't know the project structure\n- NEVER output code as text — always use write_file or edit_file\n- NEVER describe what you would do — DO IT immediately\n- One sentence of planning max, then CALL THE TOOL\n- After writing files, verify with shell (npm run build, etc.)\n- Keep each edit under 30 lines. For large changes, use multiple edit_file calls\n- Prefer editing existing files over creating new ones\n- No unnecessary comments, error handling, or features beyond scope\n\nTool priority:\n1. write_file — create new files with complete working code\n2. edit_file — modify existing files (read first)\n3. shell — run commands, install packages, verify builds\n4. read_file — only when you need to understand existing code before editing\n5. list_dir — only when you don't know the structure at all\n\nReturn a summary of what was created/modified with exact file paths.`,\n tier: 'smart',\n },\n browser: {\n name: 'Browser',\n persona: 'browser-tester',\n tools: ['browse_url', 'browser_auto_nav', 'browser_search', 'web_read', 'web_act', 'browser_screenshot'],\n systemPrompt: `You are the Browser sub-agent. Your job is to interact with web pages — navigate, extract content, fill forms, and click buttons.\n\nAvailable tools and when to use them:\n- browse_url: MUST use to navigate to a URL before interacting with it\n- browser_screenshot: Take a screenshot to understand the current page state\n- web_read: Extract clean text content from the current page\n- web_act: Click buttons, fill inputs, scroll — use for interactive actions\n- browser_auto_nav: Auto-navigate complex flows (login, multi-step forms)\n- browser_search: Search within a page or site\n\nMUST rules:\n- MUST call browse_url first to open the page\n- MUST call web_read or browser_screenshot to understand page contents before acting\n- MUST report what you found, extracted, or accomplished with exact details\n\nReturn a clear report of what was found/done on the page.`,\n tier: 'fast',\n },\n analyst: {\n name: 'Analyst',\n persona: 'code-reviewer',\n tools: ['web_search', 'web_fetch', 'memory', 'graph_search', 'graph_remember'],\n systemPrompt: `You are the Analyst sub-agent. Your job is to analyze information, identify patterns, and produce structured analytical reports.\n\nAvailable tools and when to use them:\n- web_search: Search for data, statistics, reports, or comparisons\n- web_fetch: Fetch full content from specific URLs for deeper analysis\n- graph_search: Search the knowledge graph for previously stored context\n- graph_remember: Store important findings in the knowledge graph for future reference\n- memory: Store/retrieve key-value data points\n\nMUST rules:\n- MUST call web_search to gather current data before analyzing\n- MUST call graph_search to check for existing relevant context\n- MUST call graph_remember to store key findings after analysis\n- MUST base conclusions on data from tools — not assumptions\n\nReturn a structured analytical report with: executive summary, data findings, patterns identified, confidence levels, and recommendations.`,\n tier: 'cloud',\n },\n researcher: {\n name: 'Researcher',\n persona: 'trend-researcher',\n tools: ['web_search', 'web_read', 'web_fetch', 'rag_search', 'rag_ingest'],\n systemPrompt: `You are the Deep Researcher sub-agent. Your job is to systematically research a question using multiple sources and tools.\n\nAvailable tools and when to use them:\n- web_search: MUST call 2-4 times with different targeted queries to get broad coverage\n- web_fetch: MUST call on the top 3-5 URLs from search results to get full content\n- web_read: Extract clean text from a URL\n- rag_search: Search the local knowledge base for existing research on this topic\n- rag_ingest: Store important findings in the local knowledge base\n\nMethodology — follow in order:\n1. Call rag_search to check for existing research on this topic\n2. Break the question into 2-4 targeted search queries\n3. Call web_search for each query\n4. Call web_fetch on the most relevant URLs (at least 3 total)\n5. Cross-verify key claims across at least 2 independent sources\n6. Call rag_ingest to store important findings\n\nMUST rules:\n- MUST call web_search — never answer research questions from memory\n- MUST call web_fetch to read full content, not just search snippets\n- MUST cite all sources with URLs\n\nOutput format: executive summary → sections with headers → numbered citations [1], [2] → Sources list with URLs.`,\n maxRounds: 15,\n tier: 'cloud',\n },\n // ── Pipeline agents (DeerFlow-inspired) ────────────────────\n reporter: {\n name: 'Reporter',\n persona: 'documentation-writer',\n tools: ['read_file', 'write_file', 'web_fetch'],\n systemPrompt: `You are the Reporter sub-agent. Your job is to synthesize research findings into structured, publication-quality documents saved to disk.\n\nAvailable tools and when to use them:\n- read_file: Read any existing research notes or source files\n- write_file: MUST use to save the final report to disk — NEVER output report content as text\n- web_fetch: Fetch additional content from URLs if needed for a specific section\n\nMUST rules:\n- MUST call write_file to save the report — the output is a file on disk, not inline text\n- MUST call read_file if source material files are referenced in the task\n- If given a file path for the output, MUST save to exactly that path\n\nReport structure: executive summary → sections with markdown headers → confidence levels (High/Medium/Low) per claim → numbered citations → actionable conclusions.`,\n maxRounds: 10,\n tier: 'cloud',\n },\n fact_checker: {\n name: 'Fact Checker',\n persona: 'context-engineer',\n tools: ['web_search', 'web_fetch'],\n systemPrompt: `You are the Fact Checker sub-agent. Your job is to verify specific claims against multiple independent sources.\n\nAvailable tools and when to use them:\n- web_search: MUST call for each claim to find sources that confirm or refute it\n- web_fetch: MUST call to read the full source content — search snippets are not enough\n\nFor each claim:\n1. Call web_search with 2 different queries targeting this claim\n2. Call web_fetch on the top 2 sources for full content\n3. Compare the claim against what each source actually says\n4. Assign: Verified (3+ sources agree) / Likely (2 sources) / Unverified (1 source) / Disputed (sources conflict) / False (sources contradict)\n\nMUST rules:\n- MUST call web_search for every claim — never verify from memory\n- MUST call web_fetch to read full source content\n\nReturn a structured report: claim → status → evidence → sources used.`,\n maxRounds: 10,\n tier: 'smart',\n },\n // ── Dev agents (TITAN_DEV only) ──────────────────────────\n dev_debugger: {\n name: 'Dev Debugger',\n persona: 'debugger',\n tools: ['shell', 'read_file', 'write_file', 'debug_analyze', 'code_analyze'],\n systemPrompt: `You are the Dev Debugger sub-agent for the TITAN framework. Your job is to find and fix bugs by reading actual code and running diagnostic commands.\n\nAvailable tools and when to use them:\n- read_file: MUST use to read the source file containing the error before diagnosing\n- code_analyze: Analyze code structure, find potential issues, check for bugs\n- debug_analyze: Deep analysis of error messages, stack traces, and runtime issues\n- shell: Run the failing code, check logs, reproduce the error, verify the fix\n- write_file: Save the fixed code\n\nMUST rules:\n- MUST call read_file to read the actual source code — never diagnose from assumptions\n- MUST call code_analyze or debug_analyze to systematically identify the root cause\n- MUST call shell to reproduce the error before attempting a fix\n- MUST call shell again after the fix to verify it works\n- MUST call write_file to apply the fix — never describe the fix without implementing it\n\nReturn: root cause analysis, fix applied (with file path), verification result.`,\n maxRounds: 15,\n tier: 'smart',\n },\n dev_tester: {\n name: 'Dev Tester',\n persona: 'tdd-engineer',\n tools: ['shell', 'read_file', 'write_file', 'test_generate', 'code_exec'],\n systemPrompt: `You are the Dev Tester sub-agent for the TITAN framework. Your job is to generate, run, and fix tests using vitest.\n\nAvailable tools and when to use them:\n- read_file: MUST use to read the source code being tested before writing tests\n- test_generate: Generate comprehensive test cases from source code\n- write_file: MUST use to save the test file — never output tests as text\n- shell: Run vitest to execute tests and see results\n- code_exec: Quick isolated code execution for testing snippets\n\nMUST rules:\n- MUST call read_file to understand the code structure before writing tests\n- MUST call write_file to save test files — never output tests inline\n- MUST call shell to run the tests after writing them\n- MUST fix any test failures — don't stop at writing tests\n\nReturn: test file path, number of tests written, test results (pass/fail counts).`,\n maxRounds: 20,\n tier: 'fast',\n },\n dev_reviewer: {\n name: 'Dev Reviewer',\n persona: 'code-reviewer',\n tools: ['shell', 'read_file', 'code_review', 'code_analyze', 'deps_audit'],\n systemPrompt: `You are the Dev Reviewer sub-agent for the TITAN framework. Your job is to perform thorough multi-pass code review.\n\nAvailable tools and when to use them:\n- read_file: MUST use to read each file being reviewed — never review from memory\n- code_analyze: Structural analysis — complexity, patterns, architecture\n- code_review: Deep review — security, logic errors, performance issues\n- deps_audit: Check dependencies for vulnerabilities or outdated packages\n- shell: Run the code, check for type errors, run linter\n\nReview passes (do all):\n1. Security pass: call code_review with security focus\n2. Logic pass: call code_analyze for correctness and edge cases\n3. Performance pass: check for inefficiencies\n4. Dependencies pass: call deps_audit\n\nMUST rules:\n- MUST call read_file for each file reviewed\n- MUST run at least 2 review passes with different focuses\n- Flag real issues only — not style preferences\n\nReturn: structured findings by severity (Critical/Major/Minor), with file + line references.`,\n maxRounds: 10,\n tier: 'cloud',\n },\n dev_architect: {\n name: 'Dev Architect',\n persona: 'backend-architect',\n tools: ['shell', 'read_file', 'write_file', 'code_analyze', 'refactor_suggest', 'doc_generate'],\n systemPrompt: `You are the Dev Architect sub-agent for the TITAN framework. Your job is to analyze system architecture and implement structural improvements.\n\nAvailable tools and when to use them:\n- read_file: MUST use to read source files before proposing architectural changes\n- code_analyze: Analyze codebase structure, dependencies, coupling, and patterns\n- refactor_suggest: Get suggestions for structural improvements and refactoring\n- shell: Run the codebase to understand runtime behavior, check imports, count lines\n- write_file: MUST use to implement changes or create documentation\n- doc_generate: Generate architectural documentation\n\nMUST rules:\n- MUST call read_file and code_analyze before proposing any changes — never guess at structure\n- MUST call shell to understand actual file/folder organization\n- MUST call write_file to implement changes or save documentation — never describe changes inline\n- Think in systems and dependencies, not individual files\n\nReturn: architectural analysis, proposed changes with rationale, implementation summary with file paths.`,\n maxRounds: 15,\n tier: 'cloud',\n },\n};\n\n/**\n * Spawn a sub-agent that runs an isolated agent loop with constrained tools.\n * Returns when the sub-agent completes its task.\n */\nexport async function spawnSubAgent(config: SubAgentConfig): Promise<SubAgentResult> {\n const titanConfig = loadConfig();\n const startTime = Date.now();\n const currentDepth = config.depth ?? 0;\n const subAgentsCfg = (titanConfig as Record<string, unknown>).subAgents as Record<string, unknown> | undefined;\n const maxDepth = (subAgentsCfg?.maxDepth as number) ?? 4; // Increased from 2 → 4 for multi-level task decomposition\n\n // Check depth limit (configurable, default 2)\n if (currentDepth >= maxDepth || config.isNested) {\n return {\n content: `Error: Sub-agent nesting depth limit reached (depth ${currentDepth}/${maxDepth}).`,\n toolsUsed: [],\n success: false,\n durationMs: 0,\n rounds: 0,\n validated: false,\n };\n }\n\n // Check concurrency limit\n const maxConcurrent = (titanConfig as Record<string, unknown>).subAgents\n ? ((titanConfig as Record<string, unknown>).subAgents as Record<string, unknown>).maxConcurrent as number || 3\n : 3;\n\n if (activeSubAgentIds.size >= maxConcurrent) {\n return {\n content: `Error: Maximum concurrent sub-agents (${maxConcurrent}) reached. Wait for one to finish.`,\n toolsUsed: [],\n success: false,\n durationMs: 0,\n rounds: 0,\n validated: false,\n };\n }\n\n const agentName = config.name || 'SubAgent';\n const agentTrackingId = `${agentName}-${Date.now()}`;\n activeSubAgentIds.add(agentTrackingId);\n // Reduce max rounds by 30% per depth level to prevent runaway nesting\n const baseMaxRounds = config.maxRounds || 10;\n const depthReduction = Math.pow(0.7, currentDepth);\n const maxRounds = Math.max(3, Math.ceil(baseMaxRounds * depthReduction));\n // Model resolution priority:\n // 1. Explicit model passed in config (full model ID like 'ollama/qwen3.5:397b-cloud')\n // 2. Tier from config/template → resolve via modelAliases (cloud/smart/fast/local)\n // 3. subAgents.defaultModel config → resolve as alias name\n // 4. modelAliases.fast fallback\n const aliases = titanConfig.agent.modelAliases || {};\n const subDefaultAlias = (subAgentsCfg?.defaultModel as string) || 'fast';\n const tier = config.tier;\n const model = config.model\n || (tier ? aliases[tier] : undefined)\n || aliases[subDefaultAlias]\n || aliases.fast\n || 'ollama/qwen3.5:cloud';\n\n logger.info(COMPONENT, `Spawning ${agentName}: \"${config.task.slice(0, 80)}...\" (model: ${model}, maxRounds: ${maxRounds})`);\n\n // ── Message Bus: register mailbox for inter-agent communication ──\n registerMailbox(agentName);\n\n // Build tool whitelist\n let availableTools: ToolDefinition[];\n const allTools = getToolDefinitions();\n\n const canNest = currentDepth + 1 < maxDepth; // Allow spawn_agent only if depth allows\n\n // v4.7.0: Hermes-style blocked-for-children tool list. Regardless of\n // template, children never get: spawn_agent, memory_store/write,\n // send_message variants, outbound-publisher tools, or code_exec.\n // Protects against prompt-injection → memory corruption +\n // child-posts-as-Tony side channels.\n let blockedForChildren: Set<string> = new Set();\n try {\n const safety = await import('./subagentSafety.js');\n blockedForChildren = safety.BLOCKED_CHILD_TOOLS;\n } catch { /* optional */ }\n const isChild = currentDepth > 0; // top-level sub-agent = depth 0, but this is the sub-agent itself\n\n if (config.tools && config.tools.length > 0) {\n const toolSet = new Set(config.tools);\n if (!canNest) toolSet.delete('spawn_agent');\n // Ensure send_agent_message is always available for inter-agent comms\n toolSet.add('send_agent_message');\n availableTools = allTools.filter(t => toolSet.has(t.function.name));\n } else {\n availableTools = allTools.filter(t => canNest || t.function.name !== 'spawn_agent');\n }\n\n // v4.7.0: apply blocklist to whatever tools survived template filtering.\n // Primary agent (not a sub-agent) is never filtered. This is the last\n // line of defense — even if a template accidentally includes a\n // dangerous tool, children won't get it.\n if (isChild && blockedForChildren.size > 0) {\n availableTools = availableTools.filter(t => !blockedForChildren.has(t.function.name));\n }\n\n // Build system prompt: TITAN core (minimal) + role template + persona.\n //\n // v4.13 (plan-this-logical-ocean step 4): specialists used to get ONLY\n // the role template, with no TITAN identity / tool-use rules / per-model\n // overlay. On gemma4:31b-cloud this led to specialists hallucinating\n // `<|tool>call:...<|tool|>` markup as text because nothing told them\n // \"use the native tool_calls field, not Gemini's proxy artifact\".\n //\n // Minimal mode gives them: identity, ReAct loop + 3 core rules, tool\n // preference, runtime note, safety, truthfulness, and a per-model\n // overlay. No Delegation block (they don't re-delegate), no Continuous\n // Learning / Memory Tools walls — specialists get a focused task.\n const roleTemplate = config.systemPrompt || `You are the ${agentName} sub-agent of TITAN. Execute the task below using available tools. Be efficient and return a clear summary when done.`;\n const titanCore = assembleSystemPrompt({\n modelId: model,\n persona: config.persona || 'default',\n mode: 'minimal',\n });\n let systemPrompt = `${titanCore}\\n\\n## Role\\n${roleTemplate}`;\n\n // ── Persona: inject persona content from assets/personas/ ──\n const personaId = config.persona;\n if (personaId && personaId !== 'default') {\n try {\n const personaContent = getActivePersonaContent(personaId);\n if (personaContent) {\n systemPrompt += `\\n\\n## Persona: ${personaId}\\n${personaContent}`;\n logger.debug(COMPONENT, `[${agentName}] Applied persona: ${personaId}`);\n }\n } catch {\n logger.debug(COMPONENT, `[${agentName}] Persona \"${personaId}\" not found, using base prompt`);\n }\n }\n\n // ── Agent Pool: try to reuse a warm agent if pool enabled ──\n let pooledAgent: PooledAgent | null = null;\n let messages: ChatMessage[];\n\n if (config.reusePool) {\n const templateName = Object.entries(SUB_AGENT_TEMPLATES).find(\n ([, t]) => t.name === agentName || t.systemPrompt === config.systemPrompt,\n )?.[0] || agentName;\n\n pooledAgent = acquireAgent(templateName, model);\n if (pooledAgent) {\n // Reuse warm agent's conversation history + append new task\n messages = [\n ...pooledAgent.messages,\n { role: 'user', content: config.task },\n ];\n logger.info(COMPONENT, `Reusing pooled agent ${pooledAgent.id} for ${agentName} (${pooledAgent.messages.length} prior messages)`);\n } else {\n // No pooled agent — create fresh and register for later reuse\n pooledAgent = createPooledAgent(templateName, model);\n messages = [\n { role: 'system', content: systemPrompt },\n { role: 'user', content: config.task },\n ];\n }\n } else {\n messages = [\n { role: 'system', content: systemPrompt },\n { role: 'user', content: config.task },\n ];\n }\n\n const toolsUsed: string[] = [];\n let finalContent = '';\n let rounds = 0;\n\n try {\n for (let round = 0; round < maxRounds; round++) {\n rounds = round + 1;\n logger.debug(COMPONENT, `[${agentName}] Round ${rounds}/${maxRounds}`);\n config.onProgress?.(rounds, maxRounds, agentName);\n\n // ── Message Bus: drain incoming messages at start of each round ──\n const incoming = drainMessages(agentName);\n const incomingContext = formatMessagesForContext(incoming);\n if (incomingContext) {\n messages.push({ role: 'system', content: incomingContext });\n logger.debug(COMPONENT, `[${agentName}] Injected ${incoming.length} inter-agent messages`);\n }\n\n // B7: Abort if no tools available — prevents toolless agent from looping uselessly\n if (availableTools.length === 0 && round === 0) {\n logger.warn(COMPONENT, `[${agentName}] No tools available after filtering — aborting`);\n finalContent = `Error: No tools available for sub-agent \"${agentName}\". Check tool permissions and skill configuration.`;\n break;\n }\n\n const response = await chat({\n model,\n messages,\n tools: availableTools.length > 0 ? availableTools : undefined,\n maxTokens: config.maxTokens ?? titanConfig.agent.maxTokens ?? 4096,\n temperature: 0.2,\n });\n\n // No tool calls = done\n if (!response.toolCalls || response.toolCalls.length === 0) {\n finalContent = response.content || 'Task completed.';\n break;\n }\n\n // Process tool calls\n messages.push({\n role: 'assistant',\n content: response.content || '',\n toolCalls: response.toolCalls,\n });\n\n // Emit tool_call events for Agent Watcher\n if (config.streamCallbacks?.onToolCall) {\n for (const tc of response.toolCalls!) { config.streamCallbacks.onToolCall(tc.function.name, JSON.parse(tc.function.arguments || \"{}\")); }\n }\n const toolResults = await executeTools(response.toolCalls);\n // Emit tool_end events for Agent Watcher\n if (config.streamCallbacks?.onToolResult) {\n for (const tr of toolResults) { config.streamCallbacks.onToolResult(tr.name, tr.content, tr.durationMs || 0, tr.success !== false); }\n }\n\n for (const result of toolResults) {\n toolsUsed.push(result.name);\n messages.push({\n role: 'tool',\n content: result.content,\n toolCallId: result.toolCallId,\n name: result.name,\n });\n }\n\n // Last round fallback\n if (round === maxRounds - 1) {\n finalContent = response.content || 'Max rounds reached. Partial results returned.';\n }\n }\n\n const durationMs = Date.now() - startTime;\n logger.info(COMPONENT, `${agentName} completed in ${durationMs}ms (${rounds} rounds, ${toolsUsed.length} tool calls)`);\n\n // Output validation: check for empty, too-short, or error-like responses\n const validated = validateSubAgentOutput(finalContent);\n if (!validated) {\n logger.warn(COMPONENT, `[${agentName}] Output failed validation: \"${finalContent.slice(0, 80)}...\"`);\n }\n\n return {\n content: finalContent,\n toolsUsed: [...new Set(toolsUsed)],\n success: !finalContent.toLowerCase().startsWith('error') && validated,\n durationMs,\n rounds,\n validated,\n };\n } catch (err) {\n const durationMs = Date.now() - startTime;\n logger.error(COMPONENT, `${agentName} failed: ${(err as Error).message}`);\n return {\n content: `Sub-agent error: ${(err as Error).message}`,\n toolsUsed: [...new Set(toolsUsed)],\n success: false,\n durationMs,\n rounds,\n validated: false,\n };\n } finally {\n activeSubAgentIds.delete(agentTrackingId);\n\n // ── Message Bus: unregister mailbox on completion ──\n unregisterMailbox(agentName);\n\n // ── Agent Pool: release back to pool for future reuse ──\n if (config.reusePool && pooledAgent) {\n releaseAgent(pooledAgent.id, messages, toolsUsed, rounds);\n }\n }\n}\n\n/** Validate sub-agent output for quality */\nfunction validateSubAgentOutput(content: string): boolean {\n if (!content || content.trim().length < 20) return false;\n const lower = content.toLowerCase();\n if (lower.startsWith('i cannot') || lower.startsWith('i\\'m unable') || lower.startsWith('i am unable')) return false;\n if (lower.startsWith('error:') || lower.startsWith('sub-agent error:')) return false;\n if (lower === 'task completed.' && content.length < 20) return false;\n return true;\n}\n\n/** Get count of currently active sub-agents */\nexport function getActiveSubAgentCount(): number {\n return activeSubAgentIds.size;\n}\n"],"mappings":"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","names":[]}
@@ -437,6 +437,14 @@ const AutoresearchConfigSchema = z.object({
437
437
  /** Directory for experiment results */
438
438
  resultsDir: z.string().default("~/.titan/experiments")
439
439
  });
440
+ const EvalConfigSchema = z.object({
441
+ /** Enable automatic recording of failed eval traces as auto-tapes */
442
+ enabled: z.boolean().default(true),
443
+ /** Retention period for auto-corpus tapes in days (0 = never purge) */
444
+ autoCorpus: z.object({
445
+ retentionDays: z.number().min(0).default(30)
446
+ }).default({})
447
+ });
440
448
  const CapsolverConfigSchema = z.object({
441
449
  /** Enable CapSolver CAPTCHA solving */
442
450
  enabled: z.boolean().default(false),
@@ -789,6 +797,7 @@ const TitanConfigSchema = z.object({
789
797
  teams: TeamConfigSchema.default({}),
790
798
  researchPipeline: ResearchPipelineConfigSchema.default({}),
791
799
  autoresearch: AutoresearchConfigSchema.default({}),
800
+ eval: EvalConfigSchema.default({}),
792
801
  homeAssistant: z.object({
793
802
  /** Home Assistant instance URL (e.g., http://homeassistant.local:8123) */
794
803
  url: z.string().default(""),
@@ -1195,6 +1204,7 @@ export {
1195
1204
  ChannelConfigSchema,
1196
1205
  ContextEnginePluginConfigSchema,
1197
1206
  DeliberationConfigSchema,
1207
+ EvalConfigSchema,
1198
1208
  GatewayConfigSchema,
1199
1209
  HomelabConfigSchema,
1200
1210
  HomelabMachineSchema,