titan-agent 6.1.0-beta.2 → 6.1.0-beta.21

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 (235) hide show
  1. package/LICENSE +1 -29
  2. package/README.md +1 -1
  3. package/dist/agent/agentLessons.js +2 -2
  4. package/dist/agent/agentLessons.js.map +1 -1
  5. package/dist/agent/autoModeClassifier.js +95 -0
  6. package/dist/agent/autoModeClassifier.js.map +1 -0
  7. package/dist/agent/durableJournal.js +2 -2
  8. package/dist/agent/durableJournal.js.map +1 -1
  9. package/dist/agent/goalDriver.js +67 -0
  10. package/dist/agent/goalDriver.js.map +1 -1
  11. package/dist/agent/goalPlanFile.js +3 -3
  12. package/dist/agent/goalPlanFile.js.map +1 -1
  13. package/dist/agent/missionLifecycle.js +2 -2
  14. package/dist/agent/missionLifecycle.js.map +1 -1
  15. package/dist/agent/subAgent.js +41 -0
  16. package/dist/agent/subAgent.js.map +1 -1
  17. package/dist/agent/toolContract.js +49 -0
  18. package/dist/agent/toolContract.js.map +1 -0
  19. package/dist/agent/toolRunner.js +72 -11
  20. package/dist/agent/toolRunner.js.map +1 -1
  21. package/dist/agent/trajectoryRecorder.js +185 -0
  22. package/dist/agent/trajectoryRecorder.js.map +1 -0
  23. package/dist/agent/verification.js +164 -0
  24. package/dist/agent/verification.js.map +1 -0
  25. package/dist/agent/worktreeExecutor.js +207 -0
  26. package/dist/agent/worktreeExecutor.js.map +1 -0
  27. package/dist/config/schema.js +28 -1
  28. package/dist/config/schema.js.map +1 -1
  29. package/dist/gateway/server.js +54 -0
  30. package/dist/gateway/server.js.map +1 -1
  31. package/dist/memory/facade.js +137 -0
  32. package/dist/memory/facade.js.map +1 -0
  33. package/dist/migrations/003-soma-profile-default.js +2 -2
  34. package/dist/migrations/003-soma-profile-default.js.map +1 -1
  35. package/dist/migrations/005-route-redirects.js +3 -1
  36. package/dist/migrations/005-route-redirects.js.map +1 -1
  37. package/dist/migrations/runner.js +2 -2
  38. package/dist/migrations/runner.js.map +1 -1
  39. package/dist/providers/defaultModel.js +14 -0
  40. package/dist/providers/defaultModel.js.map +1 -1
  41. package/dist/providers/router.js +4 -0
  42. package/dist/providers/router.js.map +1 -1
  43. package/dist/providers/stub.js +185 -0
  44. package/dist/providers/stub.js.map +1 -0
  45. package/dist/skills/builtin/backup.js +1 -2
  46. package/dist/skills/builtin/backup.js.map +1 -1
  47. package/dist/skills/builtin/contracts.js +254 -0
  48. package/dist/skills/builtin/contracts.js.map +1 -0
  49. package/dist/skills/builtin/download_image.js +9 -0
  50. package/dist/skills/builtin/download_image.js.map +1 -1
  51. package/dist/skills/builtin/filesystem.js +70 -20
  52. package/dist/skills/builtin/filesystem.js.map +1 -1
  53. package/dist/skills/registry.js +7 -0
  54. package/dist/skills/registry.js.map +1 -1
  55. package/dist/storage/patterns.js +2 -2
  56. package/dist/storage/patterns.js.map +1 -1
  57. package/dist/storage/personalGallery.js +2 -2
  58. package/dist/storage/personalGallery.js.map +1 -1
  59. package/dist/storage/somaProfile.js +2 -2
  60. package/dist/storage/somaProfile.js.map +1 -1
  61. package/dist/utils/constants.js +1 -1
  62. package/dist/utils/constants.js.map +1 -1
  63. package/package.json +2 -2
  64. package/ui/dist/assets/{AuditPanel-DrDvXZ9u.js → AuditPanel-RNrvKmfX.js} +1 -1
  65. package/ui/dist/assets/{AutonomyPanel-BfhW-PRX.js → AutonomyPanel-D0nmt79R.js} +2 -2
  66. package/ui/dist/assets/{AutopilotPanel-C3yQBxAp.js → AutopilotPanel-DlcdfRRy.js} +1 -1
  67. package/ui/dist/assets/{AutoresearchPanel-BgpR3EcK.js → AutoresearchPanel-CXeO6kGK.js} +2 -2
  68. package/ui/dist/assets/BackupPanel-CKb-cDsk.js +1 -0
  69. package/ui/dist/assets/{BrowserPanel-BNaKZOnn.js → BrowserPanel-CDaoF6n5.js} +1 -1
  70. package/ui/dist/assets/CPActivity-BZbeztg1.js +1 -0
  71. package/ui/dist/assets/CPAgentDetail-C9fvLxKi.js +1 -0
  72. package/ui/dist/assets/CPAgents-BsBA9kb2.js +1 -0
  73. package/ui/dist/assets/CPApprovals-Di5itVIK.js +1 -0
  74. package/ui/dist/assets/CPCosts-DmgckHli.js +1 -0
  75. package/ui/dist/assets/CPDashboard-CmSb7woK.js +1 -0
  76. package/ui/dist/assets/{CPFiles-CUpP5I82.js → CPFiles-Dd0NGcpX.js} +1 -1
  77. package/ui/dist/assets/CPGoals-DeILFq0A.js +1 -0
  78. package/ui/dist/assets/{CPInbox-DUW_hntB.js → CPInbox-DAtidZez.js} +2 -2
  79. package/ui/dist/assets/CPIssueDetail-BvwoGI57.js +1 -0
  80. package/ui/dist/assets/CPIssues-CZ1hIKDP.js +1 -0
  81. package/ui/dist/assets/{CPLayout-J0fa-it4.js → CPLayout-luPwhNpA.js} +3 -3
  82. package/ui/dist/assets/CPOrg-BlYp_KP6.js +8 -0
  83. package/ui/dist/assets/CPRuns-D0HMCjIo.js +1 -0
  84. package/ui/dist/assets/{CPSocial-By_FStZ4.js → CPSocial-CZaGi14s.js} +2 -2
  85. package/ui/dist/assets/ChannelsPanel-khiBHwx3.js +1 -0
  86. package/ui/dist/assets/{CheckpointsPanel-DvKUd2VF.js → CheckpointsPanel-n2y3jzzT.js} +1 -1
  87. package/ui/dist/assets/{CommandPostHub-CQqRYBjf.js → CommandPostHub-iWf-zt5p.js} +4 -4
  88. package/ui/dist/assets/{CronPanel-Brmnnu8_.js → CronPanel-BkRJl8u6.js} +1 -1
  89. package/ui/dist/assets/{DataTable-seaktPzB.js → DataTable-DZ2cG3Cv.js} +1 -1
  90. package/ui/dist/assets/{DreamPanel-CO7iSdjn.js → DreamPanel-DF6l0WB-.js} +1 -1
  91. package/ui/dist/assets/{EmptyState-CmK9fywh.js → EmptyState-D7xwm8hA.js} +1 -1
  92. package/ui/dist/assets/{EvalHarnessPanel-2RVBKbym.js → EvalHarnessPanel-BHabu49z.js} +2 -2
  93. package/ui/dist/assets/{EvalPanel-CqAwhFQ5.js → EvalPanel-yAesMLSP.js} +1 -1
  94. package/ui/dist/assets/{FileViewer-D98m-vF5.js → FileViewer-DOWUQccc.js} +52 -14
  95. package/ui/dist/assets/{FilesPanel-dRQM0apb.js → FilesPanel-BjuDhqaX.js} +2 -2
  96. package/ui/dist/assets/FleetPanel-B0ZhS2XX.js +1 -0
  97. package/ui/dist/assets/{HomelabPanel-DjMUrO2B.js → HomelabPanel-D373ddvj.js} +1 -1
  98. package/ui/dist/assets/InfraView-CI99NZbI.js +2 -0
  99. package/ui/dist/assets/InlineEditableField-fDk3eGVo.js +1 -0
  100. package/ui/dist/assets/Input-j10hONMY.js +1 -0
  101. package/ui/dist/assets/IntegrationsPanel-kPa5Qq-f.js +1 -0
  102. package/ui/dist/assets/IntelligenceView-DeKBaAlY.js +2 -0
  103. package/ui/dist/assets/LearningPanel-B8tqHeEZ.js +1 -0
  104. package/ui/dist/assets/{LogsPanel-Bs9IsTCH.js → LogsPanel-sXc__trb.js} +1 -1
  105. package/ui/dist/assets/McpPanel-DEr8pZwB.js +1 -0
  106. package/ui/dist/assets/{MemoryGraphPanel-F1P9COyY.js → MemoryGraphPanel-DZl7USJ6.js} +1 -1
  107. package/ui/dist/assets/MemoryWikiPanel-DTNVWvVT.js +11 -0
  108. package/ui/dist/assets/{MeshPanel-CgIAh5ZX.js → MeshPanel-b77zyQx4.js} +1 -1
  109. package/ui/dist/assets/MissionCanvas-D_quSl5F.js +69 -0
  110. package/ui/dist/assets/MissionChat-rQ5rUPAt.js +7 -0
  111. package/ui/dist/assets/MissionLibrary-Cxw7sLxv.js +5 -0
  112. package/ui/dist/assets/MissionStart-ChMWO1rr.js +1 -0
  113. package/ui/dist/assets/Modal-Dl5b6Yaj.js +1 -0
  114. package/ui/dist/assets/NvidiaPanel-D7qFbbeJ.js +1 -0
  115. package/ui/dist/assets/{OrganismPanel-CNZM3eof.js → OrganismPanel-EiQ9Gfqx.js} +1 -1
  116. package/ui/dist/assets/OverviewPanel-Bectj1Z5.js +1 -0
  117. package/ui/dist/assets/{PageHeader-llOXO43Z.js → PageHeader-CiuoyvDR.js} +1 -1
  118. package/ui/dist/assets/{PersonaProfilesPanel-CfsQTMra.js → PersonaProfilesPanel-BO87DSVG.js} +1 -1
  119. package/ui/dist/assets/{PersonasPanel-DIac_kZ3.js → PersonasPanel-BNogLwM0.js} +1 -1
  120. package/ui/dist/assets/RecipesPanel-DyqfWlaY.js +1 -0
  121. package/ui/dist/assets/{SecurityPanel-_gqMk8hJ.js → SecurityPanel-OMC-Tvbz.js} +1 -1
  122. package/ui/dist/assets/SelfImprovePanel-DXIfG_df.js +1 -0
  123. package/ui/dist/assets/{SelfProposalsPanel-DuA0zsGH.js → SelfProposalsPanel-CmgEz1TQ.js} +2 -2
  124. package/ui/dist/assets/SessionsPanel-gJXosEGf.js +1 -0
  125. package/ui/dist/assets/{SessionsTab-bFYaFvTZ.js → SessionsTab-BOVzzjTO.js} +1 -1
  126. package/ui/dist/assets/SettingsPanel-DifFGEvi.js +1 -0
  127. package/ui/dist/assets/SettingsView-BNpj0iym.js +2 -0
  128. package/ui/dist/assets/SkeletonLoader-CNSsfH3f.js +1 -0
  129. package/ui/dist/assets/{SkillsPanel-D08WcRGp.js → SkillsPanel-6fbRov-m.js} +1 -1
  130. package/ui/dist/assets/{SomaView-CYukE7Fp.js → SomaView-D3boPl5D.js} +1 -1
  131. package/ui/dist/assets/{StatCard-DT4Bx1mY.js → StatCard-2BmOlamJ.js} +1 -1
  132. package/ui/dist/assets/StatusBadge-CdVHF1nG.js +1 -0
  133. package/ui/dist/assets/Tabs-DW9ytf9H.js +1 -0
  134. package/ui/dist/assets/{TeamsPanel-DOBe72TK.js → TeamsPanel-CNaQ8Dbw.js} +1 -1
  135. package/ui/dist/assets/TelemetryPanel-DL81QlAW.js +1 -0
  136. package/ui/dist/assets/TitanCanvas-DLIN_wV3.js +1152 -0
  137. package/ui/dist/assets/ToolsView-BlChEE4n.js +2 -0
  138. package/ui/dist/assets/Tooltip-oR5JnwoN.js +6 -0
  139. package/ui/dist/assets/{TraceViewer-0bCz0x_a.js → TraceViewer-DtJXjCXV.js} +1 -1
  140. package/ui/dist/assets/TrainingPanel-4XMGaRN_.js +1 -0
  141. package/ui/dist/assets/{VoiceOverlay-B08Sw918.js → VoiceOverlay-B3fXJ-EC.js} +1 -1
  142. package/ui/dist/assets/{VramPanel-CJiic8y4.js → VramPanel-HisAYiqI.js} +1 -1
  143. package/ui/dist/assets/{WatchView-CaQMDEAy.js → WatchView-C92y0hU7.js} +1 -1
  144. package/ui/dist/assets/{WorkTab-D5bi2WRi.js → WorkTab-BYAWcvA1.js} +1 -1
  145. package/ui/dist/assets/{WorkflowsPanel-DdY6E93C.js → WorkflowsPanel-D2DiOBLA.js} +1 -1
  146. package/ui/dist/assets/{arrow-left-CHJyrO8S.js → arrow-left-thXQMJQ6.js} +1 -1
  147. package/ui/dist/assets/{briefcase-ROIEmiah.js → briefcase-ByMhDqlL.js} +1 -1
  148. package/ui/dist/assets/{chart-column-wFkjV3Ni.js → chart-column-CYHJVuxN.js} +1 -1
  149. package/ui/dist/assets/{check-BcOm5jPO.js → check-ChWLFWCx.js} +1 -1
  150. package/ui/dist/assets/{chevron-down-CM11GPpR.js → chevron-down-CU_O_5h3.js} +1 -1
  151. package/ui/dist/assets/{chevron-right-aFDDgFsL.js → chevron-right-DAiXsK24.js} +1 -1
  152. package/ui/dist/assets/{chevron-up-D6g7FB6i.js → chevron-up-BudRYwvy.js} +1 -1
  153. package/ui/dist/assets/{circle-check-big-DPz64kZW.js → circle-check-big-gkA-V2JW.js} +1 -1
  154. package/ui/dist/assets/{clock-BbCCaukN.js → clock-qO5kIED9.js} +1 -1
  155. package/ui/dist/assets/clsx-B-dksMZM.js +1 -0
  156. package/ui/dist/assets/{dollar-sign-Dy5-qkg3.js → dollar-sign-1gQNJK3V.js} +1 -1
  157. package/ui/dist/assets/{download-DjheLEns.js → download-DMD1m87c.js} +1 -1
  158. package/ui/dist/assets/{external-link-BgxGF7ZQ.js → external-link-ByafQkZ7.js} +1 -1
  159. package/ui/dist/assets/{folder-DY4eeMdo.js → folder-DeG4RuOD.js} +1 -1
  160. package/ui/dist/assets/{funnel-LWmcwhj-.js → funnel-wE8i5V9Y.js} +1 -1
  161. package/ui/dist/assets/{git-branch-CPZciK10.js → git-branch-BWSuK1LU.js} +1 -1
  162. package/ui/dist/assets/index-B8YEFBRH.js +277 -0
  163. package/ui/dist/assets/index-Cb5bSxRB.css +1 -0
  164. package/ui/dist/assets/{layers-Bn7bgu2Z.js → layers-tN-wIpE0.js} +1 -1
  165. package/ui/dist/assets/{legacy-BVPeht6t.js → legacy-BSZBdyop.js} +2 -2
  166. package/ui/dist/assets/{lightbulb-tzuCyEQk.js → lightbulb-TUHKCda3.js} +1 -1
  167. package/ui/dist/assets/{list-todo-say4NbSw.js → list-todo-DtxcUGz2.js} +1 -1
  168. package/ui/dist/assets/{missions-DbC8UmMc.js → missions-C3EC4uKT.js} +1 -1
  169. package/ui/dist/assets/{network-v9O7MmZT.js → network-ephW7nah.js} +1 -1
  170. package/ui/dist/assets/{pause-CZo33QlA.js → pause-DeggUVZc.js} +1 -1
  171. package/ui/dist/assets/{play-DbCJhc5H.js → play-Dfc0ia6v.js} +1 -1
  172. package/ui/dist/assets/{plug-CtpCZg2z.js → plug-Bl_6i_9u.js} +1 -1
  173. package/ui/dist/assets/{plus-O61rB_YF.js → plus-CQwCh7WY.js} +1 -1
  174. package/ui/dist/assets/{proxy-BAUTNyg6.js → proxy-BjQ4lsww.js} +1 -1
  175. package/ui/dist/assets/{rotate-ccw-n5WkWR51.js → rotate-ccw-CUuN2ebi.js} +1 -1
  176. package/ui/dist/assets/{save-D2Qh8KyN.js → save-PlTUvsN5.js} +1 -1
  177. package/ui/dist/assets/{search-C9SdcZJq.js → search-BHgLDT-j.js} +1 -1
  178. package/ui/dist/assets/{send-DZ_5M5QQ.js → send-pflQNBoT.js} +1 -1
  179. package/ui/dist/assets/{shield-check-DyP5PmaY.js → shield-check-CrD-s89i.js} +1 -1
  180. package/ui/dist/assets/{target-CWfn705s.js → target-D4oh8GME.js} +1 -1
  181. package/ui/dist/assets/{terminal-CdhfA0Kr.js → terminal-BvJZ1R3x.js} +1 -1
  182. package/ui/dist/assets/{toggle-right-CmYQLQRS.js → toggle-right-Dl7x_ed7.js} +1 -1
  183. package/ui/dist/assets/{trash-2-TQcEJEv4.js → trash-2-lPcFwnW5.js} +1 -1
  184. package/ui/dist/assets/{trending-up-Cw22bM4J.js → trending-up-DqUbZfDe.js} +1 -1
  185. package/ui/dist/assets/{trophy-DUF8Bcas.js → trophy-Dv6zFfKM.js} +1 -1
  186. package/ui/dist/assets/{users-BX_ZaByZ.js → users-NPyAWbs5.js} +1 -1
  187. package/ui/dist/assets/{wrench-Cfp7nYhG.js → wrench-BCKgeHj3.js} +1 -1
  188. package/ui/dist/index.html +2 -2
  189. package/ui/dist/sw.js +1 -1
  190. package/ui/dist/assets/BackupPanel-BNxMlNIP.js +0 -1
  191. package/ui/dist/assets/CPActivity-CjIgehOq.js +0 -1
  192. package/ui/dist/assets/CPAgentDetail-oaymYRNI.js +0 -1
  193. package/ui/dist/assets/CPAgents-CWQ6iJfO.js +0 -1
  194. package/ui/dist/assets/CPApprovals-DzATBhtM.js +0 -1
  195. package/ui/dist/assets/CPCosts-xVQR148_.js +0 -1
  196. package/ui/dist/assets/CPDashboard-Dt6Jr7Zw.js +0 -1
  197. package/ui/dist/assets/CPGoals-Bt1a3C9T.js +0 -1
  198. package/ui/dist/assets/CPIssueDetail-DHxqtiuh.js +0 -1
  199. package/ui/dist/assets/CPIssues-D0l3fgUs.js +0 -1
  200. package/ui/dist/assets/CPOrg-DQ3tbYld.js +0 -8
  201. package/ui/dist/assets/CPRuns-0vncicUW.js +0 -1
  202. package/ui/dist/assets/ChannelsPanel-CiJs4gyw.js +0 -1
  203. package/ui/dist/assets/FleetPanel-Bg-f3_ip.js +0 -1
  204. package/ui/dist/assets/InfraView-CHYaZqMK.js +0 -2
  205. package/ui/dist/assets/InlineEditableField-BOLRKQq1.js +0 -1
  206. package/ui/dist/assets/Input-4pNffAw-.js +0 -1
  207. package/ui/dist/assets/IntegrationsPanel-CFucvr-Q.js +0 -1
  208. package/ui/dist/assets/IntelligenceView-Dh-Wuf13.js +0 -2
  209. package/ui/dist/assets/LearningPanel-CtlougkF.js +0 -1
  210. package/ui/dist/assets/McpPanel-BobqpLsh.js +0 -1
  211. package/ui/dist/assets/MemoryWikiPanel-BfYksjoI.js +0 -11
  212. package/ui/dist/assets/MissionCanvas-0V_vtqiu.js +0 -50
  213. package/ui/dist/assets/MissionChat-9hGhc9Zw.js +0 -7
  214. package/ui/dist/assets/MissionLibrary-dFkT8pGp.js +0 -5
  215. package/ui/dist/assets/MissionStart-CZSEie0Q.js +0 -1
  216. package/ui/dist/assets/Modal-V2yNnkKk.js +0 -1
  217. package/ui/dist/assets/NvidiaPanel-qLJysI9M.js +0 -1
  218. package/ui/dist/assets/OverviewPanel-Be6LukJ_.js +0 -1
  219. package/ui/dist/assets/RecipesPanel-DDYOCx3w.js +0 -1
  220. package/ui/dist/assets/SelfImprovePanel-B8o_fO30.js +0 -1
  221. package/ui/dist/assets/SessionsPanel-DeCBkSgY.js +0 -1
  222. package/ui/dist/assets/SettingsPanel-BlsRDAt7.js +0 -1
  223. package/ui/dist/assets/SettingsView-Dsa7Zr4e.js +0 -2
  224. package/ui/dist/assets/SkeletonLoader-BkQoZllJ.js +0 -1
  225. package/ui/dist/assets/StatusBadge-BFEHCdgi.js +0 -1
  226. package/ui/dist/assets/Tabs-g2Gs9sK0.js +0 -1
  227. package/ui/dist/assets/TelemetryPanel-DXAGuV0R.js +0 -1
  228. package/ui/dist/assets/TitanCanvas-DCl3gxSl.js +0 -1072
  229. package/ui/dist/assets/ToolsView-CnUjq0Js.js +0 -2
  230. package/ui/dist/assets/Tooltip-DJ99YlFF.js +0 -6
  231. package/ui/dist/assets/TrainingPanel-BLf_KMaE.js +0 -1
  232. package/ui/dist/assets/eye-off-CYR4nH5I.js +0 -6
  233. package/ui/dist/assets/index-PwwKjgT3.css +0 -1
  234. package/ui/dist/assets/index-slWylU2o.js +0 -244
  235. package/ui/dist/assets/loader-circle-E8aLxmMz.js +0 -6
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/agent/toolRunner.ts"],"sourcesContent":["/**\n * TITAN — Tool Runner\n * Executes tool calls from the LLM with sandboxing, timeouts, and result formatting.\n */\nimport type { ToolCall, ToolDefinition } from '../providers/base.js';\nimport { appendFileSync, readFileSync, existsSync } from 'fs';\nimport { TELEMETRY_EVENTS_PATH } from '../utils/constants.js';\nimport { executeToolsParallel } from './parallelTools.js';\nimport { capToolOutput } from './toolOutputCap.js';\nimport { runPreTool, runPostTool } from '../plugins/contextEngine.js';\nimport type { ContextEnginePlugin } from '../plugins/contextEngine.js';\n\n/** Tool hook plugins — set during agent initialization */\nlet toolHookPlugins: ContextEnginePlugin[] = [];\nexport function setToolHookPlugins(plugins: ContextEnginePlugin[]): void {\n toolHookPlugins = plugins;\n}\nimport logger from '../utils/logger.js';\nimport { loadConfig } from '../config/config.js';\nimport { checkAutonomy } from './autonomy.js';\nimport { isToolSkillEnabled } from '../skills/registry.js';\nimport { redactSecrets } from '../security/secretGuard.js';\nimport { scanAndRedactPII, fullExfilScan } from '../security/exfilScan.js';\nimport { scanCommand, scanURL } from '../security/preExecScan.js';\nimport { runPreToolShellHooks, runPostToolShellHooks } from '../hooks/shellHooks.js';\nimport { createCheckpoint } from '../checkpoint/manager.js';\n\n/** Compute a lightweight unified diff between old and new file content */\nfunction computeUnifiedDiff(filePath: string, oldContent: string, newContent: string): string {\n if (oldContent === newContent) return `// No changes to ${filePath}`;\n const oldLines = oldContent.split('\\n');\n const newLines = newContent.split('\\n');\n const header = `--- ${filePath}\\n+++ ${filePath}`;\n const hunks: string[] = [];\n let i = 0, j = 0;\n while (i < oldLines.length || j < newLines.length) {\n if (i < oldLines.length && j < newLines.length && oldLines[i] === newLines[j]) {\n i++; j++; continue;\n }\n const startI = i, startJ = j;\n const removed: string[] = [];\n const added: string[] = [];\n while (i < oldLines.length && (j >= newLines.length || oldLines[i] !== newLines[j])) {\n removed.push(oldLines[i++]);\n }\n while (j < newLines.length && (i >= oldLines.length || oldLines[i] !== newLines[j])) {\n added.push(newLines[j++]);\n }\n if (removed.length || added.length) {\n const ctxBefore = oldLines.slice(Math.max(0, startI - 2), startI);\n const ctxAfter = oldLines.slice(i, Math.min(oldLines.length, i + 2));\n hunks.push([\n ...ctxBefore.map(l => ` ${l}`),\n ...removed.map(l => `-${l}`),\n ...added.map(l => `+${l}`),\n ...ctxAfter.map(l => ` ${l}`),\n ].join('\\n'));\n }\n }\n const body = hunks.join('\\n---\\n');\n return `${header}\\n${body}`;\n}\nimport { getCachedToolResult, cacheToolResult } from './trajectoryCompressor.js';\nimport { classifyProviderError, FailoverReason } from '../providers/errorTaxonomy.js';\nimport { snapshotBeforeWrite } from './shadowGit.js';\nimport { captureWrite, shouldCapture } from './selfProposals.js';\nimport { getSessionGoal } from './autonomyContext.js';\n\nconst COMPONENT = 'ToolRunner';\n\n/**\n * Tools whose entire purpose is to RETURN a data: URL for the LLM\n * to embed in downstream output. These bypass `sanitizeBase64` (which\n * would strip the very thing they were called to produce) and the\n * 30KB smart-truncation (which would chop the data URL mid-base64\n * making it unembeddable).\n *\n * v6.1.0-alpha.55 added `download_image` here. Tony spotted the bug\n * the way only Tony would: \"Why doesn't TITAN download the images\n * itself?\" → we added the tool in alpha.53 → ToolRunner sanitizer\n * stripped its output → Writer never saw the data URL → hotlinked\n * external URLs instead → alpha.52 placeholder caught them →\n * appearance of \"alpha.53 doesn't work.\" It worked perfectly. We\n * were just throwing away its output two lines later.\n *\n * If you add a new tool here, also update the 30KB truncation\n * exemption below (line ~685) since both share this list.\n */\nconst DATA_URL_PRODUCING_TOOLS = new Set([\n 'download_image',\n]);\n\n/**\n * G1: Sanitize base64 image data from tool results (OpenClaw pattern).\n * Prevents token explosion when vision/screenshot tools return raw base64.\n * Replaces data URIs with a compact placeholder showing byte count.\n *\n * v6.1.0-alpha.55 — takes optional `toolName` so we can EXEMPT the\n * tools whose entire output IS a data URL (see\n * DATA_URL_PRODUCING_TOOLS above). Default behavior unchanged for\n * every other tool.\n */\nfunction sanitizeBase64(content: string, toolName?: string): string {\n if (toolName && DATA_URL_PRODUCING_TOOLS.has(toolName)) return content;\n return content.replace(\n /data:image\\/[^;]+;base64,[A-Za-z0-9+/=]{100,}/g,\n (match) => {\n const bytes = Math.ceil((match.length - match.indexOf(',') - 1) * 0.75);\n return `[image: ${(bytes / 1024).toFixed(1)}KB omitted]`;\n },\n );\n}\n\n/**\n * v6.1.0-alpha.55 — exported alias for tests + future callers. The\n * Set is the source of truth for \"tools that intentionally return a\n * data: URL for the LLM to embed downstream.\"\n */\nexport function isDataUrlProducingTool(toolName: string): boolean {\n return DATA_URL_PRODUCING_TOOLS.has(toolName);\n}\n\n/** Error classification for retry decisions */\nexport type ErrorClass = 'transient' | 'permanent' | 'timeout' | 'rate_limit';\n\n/** Classify an error to determine if retry is appropriate.\n * Delegates to the centralized error taxonomy, then maps back to ErrorClass\n * for backward compatibility with tool execution retry logic.\n */\nexport function classifyError(error: Error, _toolName: string): ErrorClass {\n const classified = classifyProviderError(error);\n switch (classified.reason) {\n case FailoverReason.TIMEOUT:\n return 'timeout';\n case FailoverReason.RATE_LIMIT:\n return 'rate_limit';\n case FailoverReason.SERVER_ERROR:\n case FailoverReason.NETWORK_ERROR:\n case FailoverReason.OVERLOADED:\n case FailoverReason.EMPTY_RESPONSE:\n return 'transient';\n default:\n return classified.retryable ? 'transient' : 'permanent';\n }\n}\n\n/** Tool execution result */\nexport interface ToolResult {\n toolCallId: string;\n name: string;\n content: string;\n success: boolean;\n durationMs: number;\n /** Number of retry attempts made (0 = first try succeeded/failed) */\n retryCount?: number;\n /** Error classification if the tool failed */\n errorClass?: ErrorClass;\n /** Inline unified diff for file-modifying tools (write_file, edit_file, apply_patch) */\n diff?: string;\n /** v5.0: True when the tool is paused waiting for human approval */\n approvalPending?: boolean;\n /** v5.0: Approval request ID when approvalPending is true */\n approvalRequestId?: string;\n}\n\n/** A registered tool handler */\nexport interface ToolHandler {\n name: string;\n description: string;\n parameters: Record<string, unknown>;\n execute: (args: Record<string, unknown>) => Promise<string>;\n}\n\n/** Global tool registry */\nconst toolRegistry: Map<string, ToolHandler> = new Map();\n\n/** Register a tool */\nexport function registerTool(handler: ToolHandler): void {\n toolRegistry.set(handler.name, handler);\n logger.debug(COMPONENT, `Registered tool: ${handler.name}`);\n}\n\n/** Unregister a tool */\nexport function unregisterTool(name: string): void {\n toolRegistry.delete(name);\n}\n\n/** Get all registered tools */\nexport function getRegisteredTools(): ToolHandler[] {\n return Array.from(toolRegistry.values());\n}\n\n/** Convert registered tools to LLM tool definitions */\nexport function getToolDefinitions(): ToolDefinition[] {\n const config = loadConfig();\n const allowed = new Set(config.security.allowedTools);\n const denied = new Set(config.security.deniedTools);\n\n return Array.from(toolRegistry.values())\n .filter((tool) => {\n if (denied.has(tool.name)) return false;\n if (allowed.size > 0 && !allowed.has(tool.name)) return false;\n if (!isToolSkillEnabled(tool.name)) return false;\n return true;\n })\n .map((tool) => ({\n type: 'function' as const,\n function: {\n name: tool.name,\n description: tool.description,\n parameters: tool.parameters,\n },\n }));\n}\n\n/** Execute a single tool call */\nexport async function executeTool(toolCall: ToolCall, channel?: string): Promise<ToolResult> {\n const config = loadConfig();\n const startTime = Date.now();\n const handler = toolRegistry.get(toolCall.function.name);\n\n if (!handler) {\n // LangGraph pattern: tell the LLM which tools actually exist so it can self-correct\n const available = Array.from(toolRegistry.keys()).sort();\n const suggestions = available.filter(t => {\n const name = toolCall.function.name.toLowerCase();\n return t.toLowerCase().includes(name.slice(0, 4)) || name.includes(t.slice(0, 4));\n }).slice(0, 5);\n const hint = suggestions.length > 0\n ? `\\nDid you mean: ${suggestions.join(', ')}?`\n : `\\nAvailable tools include: ${available.slice(0, 20).join(', ')}${available.length > 20 ? ` (and ${available.length - 20} more)` : ''}`;\n return {\n toolCallId: toolCall.id,\n name: toolCall.function.name,\n content: `Error: \"${toolCall.function.name}\" is not a valid tool.${hint}\\nPlease use one of the available tools.`,\n success: false,\n durationMs: Date.now() - startTime,\n };\n }\n\n // Check permissions\n if (config.security.deniedTools.includes(handler.name)) {\n return {\n toolCallId: toolCall.id,\n name: handler.name,\n content: `Error: Tool \"${handler.name}\" is denied by security policy`,\n success: false,\n durationMs: Date.now() - startTime,\n };\n }\n\n // Check if parent skill is enabled\n if (!isToolSkillEnabled(handler.name)) {\n return {\n toolCallId: toolCall.id,\n name: handler.name,\n content: `Error: Tool \"${handler.name}\" is disabled — its parent skill is turned off. Enable it in Mission Control.`,\n success: false,\n durationMs: Date.now() - startTime,\n };\n }\n\n // Parse arguments\n let args: Record<string, unknown> = {};\n try {\n args = JSON.parse(toolCall.function.arguments);\n } catch (parseErr) {\n logger.warn('ToolRunner', `Malformed JSON args for ${handler.name}: ${(parseErr as Error).message} — raw: ${(toolCall.function.arguments || '').slice(0, 200)}`);\n // Try to salvage: if it looks like a truncated JSON, extract what we can\n const salvageMatch = (toolCall.function.arguments || '').match(/\\{[\\s\\S]*/);\n if (salvageMatch) {\n try {\n // Attempt to close the JSON and parse\n const fixed = salvageMatch[0].replace(/,?\\s*$/, '}');\n args = JSON.parse(fixed);\n logger.info('ToolRunner', `Salvaged partial JSON args for ${handler.name}`);\n } catch {\n // A5: Return error instead of executing with empty args (LangGraph pattern)\n return {\n toolCallId: toolCall.id,\n name: handler.name,\n content: `Error: Could not parse arguments for \"${handler.name}\". Raw: ${(toolCall.function.arguments || '').slice(0, 200)}. Please provide valid JSON arguments.`,\n success: false,\n durationMs: Date.now() - startTime,\n };\n }\n }\n }\n\n // Schema validation: check required parameters before execution (LangGraph pattern)\n if (handler.parameters && typeof handler.parameters === 'object') {\n const schema = handler.parameters as { required?: string[]; properties?: Record<string, unknown> };\n if (schema.required && Array.isArray(schema.required)) {\n const missing = schema.required.filter(key => args[key] === undefined || args[key] === null);\n if (missing.length > 0) {\n const available = schema.properties ? Object.keys(schema.properties) : [];\n logger.warn('ToolRunner', `[SchemaValidation] ${handler.name}: missing required params: ${missing.join(', ')}`);\n return {\n toolCallId: toolCall.id,\n name: handler.name,\n content: `Error: Missing required parameter(s): ${missing.join(', ')}. ` +\n `Expected parameters: ${available.join(', ')}. Please provide all required arguments.`,\n success: false,\n durationMs: Date.now() - startTime,\n };\n }\n }\n }\n\n // v6.1.0-alpha.11 — Worktree scope (spike).\n //\n // When the current async context has a worktree scope active (see\n // src/agent/worktreeScope.ts), redirect mutating tool writes into\n // the scope's worktree directory. Lets multiple concurrent spawns\n // write the \"same\" filename without colliding. Pure-path-rewrite\n // — the tool still runs, just into a different absolute path.\n //\n // This runs BEFORE guardrails so guardrails see the rewritten\n // absolute path, not the LLM's original relative path (which\n // resolves against cwd and may falsely trigger system-path\n // protections). The self-mod scope-lock further below picks up\n // the same rewritten path; in practice worktree paths live under\n // ~/.titan/worktrees which isn't on the self-mod target list, so\n // self-mod stays a no-op for worktree-scoped writes.\n const MUTATING_TOOLS_FOR_WORKTREE = new Set(['write_file', 'edit_file', 'append_file', 'apply_patch']);\n if (MUTATING_TOOLS_FOR_WORKTREE.has(handler.name)) {\n try {\n const { getCurrentWorktreeScope } = await import('./worktreeScope.js');\n const wtScope = getCurrentWorktreeScope();\n if (wtScope) {\n const rawPath = (args.path || args.file_path || args.filePath) as string | undefined;\n if (typeof rawPath === 'string' && rawPath.length > 0) {\n if (rawPath.startsWith('/') || rawPath.startsWith('~')) {\n return {\n toolCallId: toolCall.id,\n name: handler.name,\n content: `Error: this spawn runs in a worktree scope (${wtScope.subtaskId}). File writes must use RELATIVE paths — got \"${rawPath}\". Rewrite to something like \"output.md\" or \"src/foo.ts\" without a leading \"/\" or \"~\".`,\n success: false,\n durationMs: Date.now() - startTime,\n };\n }\n const { join: joinPath, dirname } = await import('path');\n const { mkdirSync } = await import('fs');\n const newPath = joinPath(wtScope.path, rawPath);\n if (args.path !== undefined) args.path = newPath;\n if (args.file_path !== undefined) args.file_path = newPath;\n if (args.filePath !== undefined) args.filePath = newPath;\n try { mkdirSync(dirname(newPath), { recursive: true }); }\n catch { /* best-effort */ }\n logger.debug(COMPONENT, `[Worktree] Redirected ${handler.name} → ${newPath} (scope: ${wtScope.goalId}/${wtScope.subtaskId})`);\n }\n }\n } catch (err) {\n logger.debug(COMPONENT, `Worktree scope check skipped: ${(err as Error).message}`);\n }\n }\n\n // Guardrails: validate tool call before execution\n try {\n const { guardToolCall } = await import('./guardrails.js');\n const guardResult = guardToolCall(handler.name, args);\n if (!guardResult.allowed) {\n logger.warn('ToolRunner', `[Guardrails] Blocked ${handler.name}: ${guardResult.reason}`);\n return {\n toolCallId: toolCall.id,\n name: handler.name,\n content: `Error: Tool call blocked by guardrails — ${guardResult.reason}`,\n success: false,\n durationMs: Date.now() - startTime,\n };\n }\n } catch { /* guardrails unavailable — continue */ }\n\n // Read-only tool result cache (60s TTL, helper self-gates to read-only allowlist)\n const cacheArgKey = toolCall.function.arguments || '{}';\n const cachedResult = getCachedToolResult(handler.name, cacheArgKey);\n if (cachedResult !== null) {\n logger.info(COMPONENT, `[Cache HIT] ${handler.name}`);\n return {\n toolCallId: toolCall.id,\n name: handler.name,\n content: cachedResult,\n success: true,\n durationMs: Date.now() - startTime,\n };\n }\n\n logger.info(COMPONENT, `Executing tool: ${handler.name}`);\n\n // v5.0: Pre-execution scanner for dangerous commands\n if (handler.name === 'shell' || handler.name === 'code_exec') {\n const cmdArg = (args.command || args.code || args.script || '') as string;\n const scan = scanCommand(cmdArg);\n if (!scan.allowed) {\n return {\n toolCallId: toolCall.id,\n name: handler.name,\n content: `Error: Pre-execution scan blocked this command\\n${scan.warnings.join('\\n')}`,\n success: false,\n durationMs: Date.now() - startTime,\n };\n }\n if (scan.warnings.length > 0 && scan.level === 'warn') {\n logger.warn('ToolRunner', `Pre-exec warnings for ${handler.name}: ${scan.warnings.join('; ')}`);\n }\n }\n if (handler.name === 'browser_navigate' || handler.name === 'browser_auto_nav') {\n const urlArg = (args.url || args.target || '') as string;\n const scan = scanURL(urlArg);\n if (!scan.allowed) {\n return {\n toolCallId: toolCall.id,\n name: handler.name,\n content: `Error: Pre-execution scan blocked this URL\\n${scan.warnings.join('\\n')}`,\n success: false,\n durationMs: Date.now() - startTime,\n };\n }\n }\n\n // v5.0: Shell hooks — pre-tool\n const { getCurrentSessionId } = await import('./agent.js');\n const sessionId = typeof getCurrentSessionId === 'function' ? getCurrentSessionId() : null;\n const shellPre = await runPreToolShellHooks(handler.name, args, sessionId || toolCall.id, 'default', 0);\n if (!shellPre.allow) {\n return {\n toolCallId: toolCall.id,\n name: handler.name,\n content: 'Blocked by shell hook: ' + (shellPre.reason || 'Hook denied execution'),\n success: false,\n durationMs: Date.now() - startTime,\n };\n }\n if (shellPre.modifiedArgs) args = shellPre.modifiedArgs;\n\n // Pre-tool hooks — plugins can block or modify args\n if (toolHookPlugins.length > 0) {\n const hookResult = await runPreTool(toolHookPlugins, handler.name, args);\n if (!hookResult.allow) {\n return {\n toolCallId: toolCall.id,\n name: handler.name,\n content: 'Blocked by hook: ' + (hookResult.reason || 'Plugin denied execution'),\n success: false,\n durationMs: Date.now() - startTime,\n };\n }\n if (hookResult.modifiedArgs) args = hookResult.modifiedArgs;\n }\n\n // Autonomy gate: check if the tool is permitted under current mode\n const autonomyResult = await checkAutonomy(handler.name, args, channel);\n if (!autonomyResult.allowed) {\n return {\n toolCallId: toolCall.id,\n name: handler.name,\n content: 'Action blocked by autonomy policy: ' + (autonomyResult.reason || 'Not permitted'),\n success: false,\n durationMs: Date.now() - startTime,\n };\n }\n\n // Shadow git checkpoint — snapshot files before mutation (fire-and-forget)\n const MUTATING_TOOLS = new Set(['write_file', 'edit_file', 'append_file', 'apply_patch']);\n\n // v4.9.0-local.7: kill-switch gate for file mutations. If the kill switch\n // is engaged, refuse write/edit/append/apply_patch so the initiative loop\n // can't keep accumulating fix-oscillations while the human hasn't resumed.\n // This closes the gap where `spawn_agent`, `autopilot`, and the pressure\n // cycle were gated but the main agent's tool path was not — meaning\n // initiative could keep rewriting the same files for hours after a kill.\n // See kill-switch.json `history` for the trigger; resume via\n // POST /api/safety/resume.\n if (MUTATING_TOOLS.has(handler.name)) {\n try {\n const { isKilled, getState } = await import('../safety/killSwitch.js');\n if (isKilled()) {\n const state = getState();\n const lastReason = state?.lastEvent?.reason ?? 'kill switch engaged';\n logger.warn(COMPONENT, `[KillSwitch] Refusing ${handler.name} — ${lastReason}`);\n return {\n toolCallId: toolCall.id,\n name: handler.name,\n content: `Error: File mutation refused — kill switch is engaged (${lastReason}). ` +\n `Resume via POST /api/safety/resume after investigating the trigger, ` +\n `then retry. Do NOT retry this tool call until resumed.`,\n success: false,\n durationMs: Date.now() - startTime,\n };\n }\n } catch { /* kill switch module unavailable — fall through (fail-open on infra error) */ }\n }\n\n // v4.9.0-local.8: Self-mod scope lock + staging.\n //\n // Three-layer policy when the active session has a goal tagged as\n // self-modifying (see config.autonomy.selfMod.tags):\n // 1. Writes to paths OUTSIDE config.autonomy.selfMod.target are refused\n // (prevents LARPing self-improvement by writing to ~/titan-saas etc)\n // 2. When staging is enabled, writes INSIDE target are diverted to a\n // per-goal staging directory and a `self_mod_pr` approval is filed\n // 3. The original path is stored as `targetPath` on the staging entry\n // so the human sees what would land where if they approve the PR\n //\n // This is the deeper fix for the pattern observed 2026-04-18 where a\n // \"self-healing framework\" goal completed 100% by writing to an unrelated\n // Next.js app.\n let stagedRedirect: { stagedPath: string; targetPath: string } | null = null;\n if (MUTATING_TOOLS.has(handler.name)) {\n const rawFilePath = (args.path || args.file_path || args.filePath) as string | undefined;\n if (rawFilePath) {\n try {\n const { getCurrentSessionId } = await import('./agent.js');\n const { decideScope } = await import('./selfModStaging.js');\n const sid: string | null = typeof getCurrentSessionId === 'function' ? getCurrentSessionId() : null;\n const decision = decideScope(sid, rawFilePath);\n if (decision.action === 'reject') {\n logger.warn(COMPONENT, `[ScopeLock] Refusing ${handler.name}: ${decision.reason}`);\n return {\n toolCallId: toolCall.id,\n name: handler.name,\n content: `Error: ${decision.reason}\\n\\nRewrite the path to live inside the self-mod target, OR retag the goal to remove self-mod tags, OR pause the goal and create a properly-scoped one.`,\n success: false,\n durationMs: Date.now() - startTime,\n };\n }\n if (decision.action === 'stage' && decision.stagedPath && decision.targetPath) {\n stagedRedirect = { stagedPath: decision.stagedPath, targetPath: decision.targetPath };\n // Rewrite the tool args so the handler writes to staging.\n // Preserve both `path` and `file_path` variants since\n // different tools use different field names.\n if (args.path !== undefined) args.path = decision.stagedPath;\n if (args.file_path !== undefined) args.file_path = decision.stagedPath;\n if (args.filePath !== undefined) args.filePath = decision.stagedPath;\n // Ensure the staged parent dir exists; write_file may not\n // mkdir -p on all code paths.\n try {\n const { mkdirSync } = await import('fs');\n const { dirname } = await import('path');\n mkdirSync(dirname(decision.stagedPath), { recursive: true });\n } catch { /* best-effort */ }\n logger.info(COMPONENT, `[SelfModStaging] Diverting ${handler.name} → ${decision.stagedPath} (would land at ${decision.targetPath} on approval)`);\n }\n } catch (err) {\n logger.debug(COMPONENT, `[ScopeLock] check failed (fail-open): ${(err as Error).message}`);\n }\n }\n }\n\n // v5.0: Filesystem checkpoint before destructive operations\n if (MUTATING_TOOLS.has(handler.name)) {\n const cpPaths: string[] = [];\n const cpPath = (args.path || args.file_path || args.filePath) as string;\n if (cpPath) cpPaths.push(cpPath);\n if (cpPaths.length > 0) {\n createCheckpoint(sessionId || toolCall.id, handler.name, args, cpPaths);\n }\n }\n\n if (MUTATING_TOOLS.has(handler.name)) {\n // Use the (potentially rewritten) path for shadow-git + fix-oscillation\n const filePath = (args.path || args.file_path || args.filePath) as string;\n if (filePath) {\n snapshotBeforeWrite(handler.name, filePath).catch(err =>\n logger.debug(COMPONENT, `Shadow checkpoint skipped: ${(err as Error).message}`),\n );\n // v4.9.0: fix-oscillation tracker. Same file written/edited\n // twice within 24h flags as oscillation, which feeds the\n // kill switch (3+ oscillations → kill). Best-effort — never\n // blocks the write.\n (async () => {\n try {\n const { recordFixEvent } = await import('../safety/fixOscillation.js');\n recordFixEvent({\n target: filePath,\n kind: 'file',\n detail: `${handler.name} via ${channel ?? 'unknown'}`,\n });\n } catch (err) {\n logger.debug(COMPONENT, `Fix-oscillation skipped: ${(err as Error).message}`);\n }\n })();\n }\n // v4.8.0: self-proposal capture — if this write is happening inside\n // an autonomous Soma-driven session, stash a copy for specialist\n // review. Fire-and-forget — never blocks tool execution.\n captureSelfProposalIfApplicable(handler.name, args).catch(err =>\n logger.debug(COMPONENT, `Self-proposal capture skipped: ${(err as Error).message}`),\n );\n }\n\n // Per-tool timeout lookup\n const toolTimeouts = (config.security as Record<string, unknown>).toolTimeouts as Record<string, number> | undefined;\n const baseTimeout = toolTimeouts?.[handler.name] || config.security.commandTimeout || 30000;\n\n // Retry config\n const retryConfig = (config.security as Record<string, unknown>).toolRetry as { enabled?: boolean; maxRetries?: number; backoffBaseMs?: number } | undefined;\n const retryEnabled = retryConfig?.enabled !== false;\n const maxRetries = retryConfig?.maxRetries ?? 3;\n const backoffBase = retryConfig?.backoffBaseMs ?? 1000;\n\n let lastError: Error | null = null;\n let lastErrorClass: ErrorClass = 'permanent';\n let attempt = 0;\n\n // Capture pre-execution file state for diff generation\n let preContent: string | undefined;\n let filePathForDiff: string | undefined;\n if (['write_file', 'edit_file', 'apply_patch'].includes(handler.name)) {\n const pathArg = args.path as string | undefined;\n if (pathArg) {\n filePathForDiff = pathArg;\n try {\n if (existsSync(pathArg)) preContent = readFileSync(pathArg, 'utf-8');\n } catch { /* ignore read errors */ }\n }\n }\n\n // Swarm invariants — hard safety rules (fail-open)\n try {\n const { checkInvariants } = await import('../safety/invariants.js');\n const invariant = checkInvariants(handler.name, args);\n if (!invariant.pass) {\n logger.warn(COMPONENT, `[Invariant] Blocked ${handler.name}: ${invariant.reason}`);\n return {\n toolCallId: toolCall.id,\n name: handler.name,\n content: `INVARIANT_VIOLATION: ${invariant.reason}`,\n success: false,\n durationMs: Date.now() - startTime,\n };\n }\n } catch (err) {\n logger.warn(COMPONENT, `Invariant check failed (fail-open): ${(err as Error).message}`);\n }\n\n // v5.0: Approval gates — human-in-the-loop before executing dangerous tools.\n // This wires the approval_gates.ts skill into the execution path, closing the\n // safety gap identified during the 2026-04-28 overnight audit.\n try {\n const { requiresApproval, createApprovalRequest } = await import('../skills/builtin/approval_gates.js');\n if (requiresApproval(handler.name)) {\n logger.info(COMPONENT, `[ApprovalGate] Tool \"${handler.name}\" requires human approval — filing request`);\n const request = createApprovalRequest(handler.name, args, sessionId || toolCall.id);\n if (request.status === 'pending') {\n return {\n toolCallId: toolCall.id,\n name: handler.name,\n content: `Awaiting approval: Tool \"${handler.name}\" requires human confirmation before execution. ` +\n `Request ID: ${request.id}. ` +\n `Approve with \"approve ${request.id}\" or deny with \"deny ${request.id}\".`,\n success: false,\n // v5.0: Signal to the loop that this is an approval pause, not a failure\n approvalPending: true,\n approvalRequestId: request.id,\n durationMs: Date.now() - startTime,\n };\n }\n // If request was auto-denied (e.g. preference set to 'always_deny'), 'createApprovalRequest'\n // returns a request with status 'denied' and we should abort.\n if (request.status === 'denied') {\n return {\n toolCallId: toolCall.id,\n name: handler.name,\n content: `Error: Tool \"${handler.name}\" was auto-denied by approval policy. ` +\n `Check approval preferences or change the request decision.`,\n success: false,\n durationMs: Date.now() - startTime,\n };\n }\n }\n } catch (approvalErr) {\n // Approval gates module unavailable → fail-open so the agent doesn't deadlock\n logger.warn(COMPONENT, `Approval gate check failed (fail-open): ${(approvalErr as Error).message}`);\n }\n\n for (; attempt <= (retryEnabled ? maxRetries : 0); attempt++) {\n try {\n // On timeout retry, double the timeout\n const timeout = (attempt > 0 && lastErrorClass === 'timeout') ? baseTimeout * 2 : baseTimeout;\n\n let result = await Promise.race([\n handler.execute(args),\n new Promise<string>((_, reject) =>\n setTimeout(() => reject(new Error(`Tool \"${handler.name}\" timed out after ${timeout}ms`)), timeout)\n ),\n ]);\n\n // Secret exfiltration guard — scan tool output before it leaves\n result = redactSecrets(result);\n\n // v5.0: PII redaction (privacy compliance)\n const config = loadConfig();\n if (config.security?.redactPII) {\n result = scanAndRedactPII(result);\n }\n\n // v5.0: Full exfiltration scan (layer 2-5) when configured\n if (config.security?.secretScan?.level === 'full') {\n const scan = fullExfilScan(result, 'tool_output');\n if (scan.blocked) {\n logger.warn('ToolRunner', `Exfiltration scan blocked ${handler.name}: ${scan.findings.map(f => f.type).join(', ')}`);\n }\n result = scan.redacted;\n }\n\n const durationMs = Date.now() - startTime;\n if (attempt > 0) {\n logger.info(COMPONENT, `Tool ${handler.name} succeeded on retry ${attempt} in ${durationMs}ms`);\n } else {\n logger.info(COMPONENT, `Tool ${handler.name} completed in ${durationMs}ms`);\n }\n\n // G1: Strip base64 image data before size check (prevents token\n // explosion). v6.1.0-alpha.55 — passes tool name so\n // DATA_URL_PRODUCING_TOOLS (download_image) get exempted; for\n // those the data URL IS the point of calling the tool.\n let finalContent = sanitizeBase64(result, handler.name);\n\n // Smart truncation — keep head + tail for large results (TITAN pattern).\n // v6.1.0-alpha.55 — DATA_URL_PRODUCING_TOOLS are exempted because\n // truncating mid-base64 produces an unusable data URL. The Writer\n // would then fall back to hotlinking the source URL, which is\n // exactly what Tony was seeing before alpha.55 (\"alpha.53 didn't\n // work\" — it did, this layer was killing it).\n if (finalContent.length > 30000 && !DATA_URL_PRODUCING_TOOLS.has(handler.name)) {\n const head = finalContent.slice(0, 20000);\n const tail = finalContent.slice(-5000);\n finalContent = head + '\\n\\n[... ' + (finalContent.length - 25000) + ' chars omitted ...]\\n\\n' + tail;\n logger.info(COMPONENT, `Tool ${handler.name} output truncated: ${result.length} → ${finalContent.length} chars`);\n }\n\n // v5.0: Shell hooks — post-tool\n const shellPost = await runPostToolShellHooks(handler.name, args, finalContent, sessionId || toolCall.id, 'default', 0);\n if (shellPost !== undefined) finalContent = shellPost;\n\n // Post-tool hooks — plugins can modify result\n if (toolHookPlugins.length > 0) {\n const hookResult = await runPostTool(toolHookPlugins, handler.name, args, { content: finalContent, success: true, durationMs });\n if (hookResult.modifiedContent !== undefined) finalContent = hookResult.modifiedContent;\n }\n\n // Cache the result for read-only tools (helper self-gates)\n cacheToolResult(handler.name, cacheArgKey, finalContent);\n\n // v4.9.0-local.8: if this write was diverted to staging, record\n // it in the self_mod_pr bundle. Fire-and-forget — must NOT block\n // the tool's return value or the agent loop.\n if (stagedRedirect) {\n (async () => {\n try {\n const { getCurrentSessionId } = await import('./agent.js');\n const { recordStagedWrite } = await import('./selfModStaging.js');\n const sid: string | null = typeof getCurrentSessionId === 'function' ? getCurrentSessionId() : null;\n await recordStagedWrite({\n sessionId: sid,\n toolName: handler.name,\n stagedPath: stagedRedirect!.stagedPath,\n targetPath: stagedRedirect!.targetPath,\n });\n } catch (err) {\n logger.debug(COMPONENT, `[SelfModStaging] recordStagedWrite failed: ${(err as Error).message}`);\n }\n })();\n }\n\n // Fire-and-forget telemetry + activity log\n (async () => {\n try {\n const { logActivity } = await import('../telemetry/activityLog.js');\n logActivity({ event: 'tool_call', tool: handler.name, channel: channel ?? 'unknown' });\n if (MUTATING_TOOLS.has(handler.name)) {\n logActivity({ event: 'file_edit', tool: handler.name, path: (args.path || args.file_path || args.filePath) as string | undefined });\n }\n if (handler.name === 'web_search') {\n logActivity({ event: 'web_search', query: (args.query || args.q) as string | undefined });\n }\n if (handler.name === 'web_fetch') {\n logActivity({ event: 'web_fetch', url: (args.url || args.target) as string | undefined });\n }\n if (handler.name === 'run_eval' || handler.name === 'eval_suite') {\n logActivity({ event: 'eval_run', suite: (args.suite || args.name) as string | undefined });\n }\n } catch { /* activity log non-critical */ }\n const cfg = loadConfig();\n if (cfg.telemetry?.enabled) {\n try {\n appendFileSync(TELEMETRY_EVENTS_PATH, JSON.stringify({\n event: 'tool_called',\n properties: { toolName: handler.name, success: true, durationMs, channel: channel ?? 'unknown' },\n timestamp: new Date().toISOString(),\n }) + '\\n', 'utf-8');\n } catch { /* non-critical */ }\n }\n // Remote analytics (PostHog + custom collector)\n const { trackToolCall } = await import('../analytics/featureTracker.js');\n const { getCurrentSessionId } = await import('./agent.js').catch(() => ({ getCurrentSessionId: () => null }));\n const sid = typeof getCurrentSessionId === 'function' ? getCurrentSessionId() : null;\n trackToolCall(handler.name, true, durationMs, undefined, sid ?? undefined);\n })();\n\n // Compute inline diff for file-modifying tools\n let diff: string | undefined;\n if (['write_file', 'edit_file', 'apply_patch'].includes(handler.name) && filePathForDiff && !stagedRedirect) {\n try {\n const postContent = existsSync(filePathForDiff) ? readFileSync(filePathForDiff, 'utf-8') : '';\n diff = computeUnifiedDiff(filePathForDiff, preContent ?? '', postContent);\n } catch { /* ignore diff errors */ }\n }\n\n return {\n toolCallId: toolCall.id,\n name: handler.name,\n content: stagedRedirect\n ? `${finalContent}\\n\\n[SelfModStaging] Diverted to staging: ${stagedRedirect.stagedPath}. A human approval is pending before this lands at ${stagedRedirect.targetPath}.`\n : finalContent,\n success: true,\n durationMs,\n retryCount: attempt,\n diff,\n };\n } catch (error) {\n lastError = error as Error;\n lastErrorClass = classifyError(lastError, handler.name);\n\n // Don't retry permanent errors\n if (lastErrorClass === 'permanent') {\n break;\n }\n\n // Don't retry if this was the last attempt\n if (attempt >= maxRetries || !retryEnabled) {\n break;\n }\n\n // Exponential backoff: 1s, 2s, 4s (capped at 8s)\n const delay = Math.min(backoffBase * Math.pow(2, attempt), 8000);\n logger.warn(COMPONENT, `Tool ${handler.name} failed (${lastErrorClass}, attempt ${attempt + 1}/${maxRetries + 1}): ${lastError.message} — retrying in ${delay}ms`);\n await new Promise(resolve => setTimeout(resolve, delay));\n }\n }\n\n // All retries exhausted or permanent error\n const durationMs = Date.now() - startTime;\n const errorMsg = lastError?.message || 'Unknown error';\n const retryCount = attempt; // actual number of retries performed (matches success path)\n logger.error(COMPONENT, `Tool ${handler.name} failed (${lastErrorClass}${retryCount > 0 ? `, ${retryCount} retries` : ''}): ${errorMsg}`);\n\n // Fire-and-forget telemetry\n (async () => {\n const cfg = loadConfig();\n if (cfg.telemetry?.enabled) {\n try {\n appendFileSync(TELEMETRY_EVENTS_PATH, JSON.stringify({\n event: 'tool_called',\n properties: { toolName: handler.name, success: false, durationMs, errorClass: lastErrorClass, channel: channel ?? 'unknown' },\n timestamp: new Date().toISOString(),\n }) + '\\n', 'utf-8');\n } catch { /* non-critical */ }\n }\n // Remote analytics (PostHog + custom collector)\n const { trackToolCall } = await import('../analytics/featureTracker.js');\n const { getCurrentSessionId } = await import('./agent.js').catch(() => ({ getCurrentSessionId: () => null }));\n const sid = typeof getCurrentSessionId === 'function' ? getCurrentSessionId() : null;\n trackToolCall(handler.name, false, durationMs, lastErrorClass, sid ?? undefined);\n })();\n\n return {\n toolCallId: toolCall.id,\n name: handler.name,\n content: `Error: ${errorMsg}`,\n success: false,\n durationMs,\n retryCount,\n errorClass: lastErrorClass,\n };\n}\n\n/** Execute multiple tool calls (in parallel where possible, with write-conflict detection) */\n/**\n * Apply the tool-output cap to every result. v5.8.0 — Anthropic's \"Writing\n * effective tools for AI agents\" recommends a 25k-token cap on tool results\n * with a tool-aware truncation hint. We do it here at the boundary so every\n * caller (agent loop, sub-agent, voice path) benefits without each having\n * to remember to wrap individually.\n * Reference: docs/HARNESS-PATTERNS.md, src/agent/toolOutputCap.ts\n */\nfunction applyOutputCaps(results: ToolResult[]): ToolResult[] {\n return results.map((r) => {\n const capped = capToolOutput(r.name, r.content);\n if (!capped.truncated) return r;\n // Only mutate when truncation actually happened, preserving identity\n // when content was already small.\n logger.info(COMPONENT, `[OutputCap] ${r.name} truncated ${capped.originalSize} → ${capped.content.length} chars`);\n return { ...r, content: capped.content };\n });\n}\n\nexport async function executeTools(toolCalls: ToolCall[], channel?: string): Promise<ToolResult[]> {\n // Single tool — fast path\n if (toolCalls.length <= 1) {\n const results = await Promise.all(toolCalls.map(tc => executeTool(tc, channel)));\n return applyOutputCaps(results);\n }\n\n // Multiple tools — use parallelTools engine with write-conflict detection\n const parallelCalls = toolCalls.map(tc => {\n let args: Record<string, unknown> = {};\n try { args = JSON.parse(tc.function.arguments); } catch { /* use empty */ }\n return { id: tc.id, name: tc.function.name, args };\n });\n\n const executor = async (name: string, args: Record<string, unknown>): Promise<string> => {\n // Build a synthetic ToolCall for executeTool\n const syntheticTc: ToolCall = {\n id: '',\n type: 'function',\n function: { name, arguments: JSON.stringify(args) },\n };\n const result = await executeTool(syntheticTc, channel);\n return result.content;\n };\n\n const parallelResults = await executeToolsParallel(parallelCalls, executor);\n\n // Map back to ToolResult format with full metadata, then apply output cap.\n const mapped: ToolResult[] = parallelResults.map(pr => ({\n toolCallId: pr.toolCallId,\n name: pr.name,\n content: pr.content,\n success: !pr.content.startsWith('Error:'),\n durationMs: 0,\n }));\n return applyOutputCaps(mapped);\n}\n\n// ── Self-proposal capture helper (v4.8.0) ────────────────────────────────\n\n/**\n * If the current write is happening in an autonomous, Soma-driven session,\n * stash a copy of the written content for specialist review. Silent no-op\n * in all other cases (user-driven edits, non-autonomous mode, or when\n * selfMod.enabled is false in config).\n */\nasync function captureSelfProposalIfApplicable(\n toolName: string,\n args: Record<string, unknown>,\n): Promise<void> {\n // Resolve what we can from the current autonomous context\n const { getCurrentSessionId } = await import('./agent.js').catch(() => ({ getCurrentSessionId: () => null }));\n const sessionId: string | null = typeof getCurrentSessionId === 'function' ? getCurrentSessionId() : null;\n const sessionGoal = getSessionGoal(sessionId);\n const config = loadConfig();\n const autonomous = (config.autonomy?.mode === 'autonomous');\n const goalProposedBy = sessionGoal?.proposedBy ?? null;\n\n if (!shouldCapture({ toolName, autonomous, goalProposedBy })) return;\n\n const filePath = (args.path || args.file_path || args.filePath) as string | undefined;\n const content = (args.content || args.new_text || args.data) as string | undefined;\n if (!filePath || !content) return;\n\n captureWrite({\n toolName,\n filePath,\n content,\n sessionId,\n agentId: null, // filled by downstream if needed\n goalId: sessionGoal?.goalId ?? null,\n goalTitle: sessionGoal?.goalTitle ?? null,\n goalProposedBy,\n });\n}\n"],"mappings":"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","names":["getCurrentSessionId","config","durationMs"]}
1
+ {"version":3,"sources":["../../src/agent/toolRunner.ts"],"sourcesContent":["/**\n * TITAN — Tool Runner\n * Executes tool calls from the LLM with sandboxing, timeouts, and result formatting.\n */\nimport type { ToolCall, ToolDefinition } from '../providers/base.js';\nimport {\n type ToolContract,\n ToolValidationError,\n validateToolCall,\n formatValidationError,\n} from './toolContract.js';\nimport { classifyToolCall, formatBreadcrumb } from './autoModeClassifier.js';\nimport { appendFileSync, readFileSync, existsSync } from 'fs';\nimport { TELEMETRY_EVENTS_PATH } from '../utils/constants.js';\nimport { executeToolsParallel } from './parallelTools.js';\nimport { capToolOutput } from './toolOutputCap.js';\nimport { runPreTool, runPostTool } from '../plugins/contextEngine.js';\nimport type { ContextEnginePlugin } from '../plugins/contextEngine.js';\nimport { run as runInIsolatedWorktree } from './worktreeExecutor.js';\n\n/** Tool hook plugins — set during agent initialization */\nlet toolHookPlugins: ContextEnginePlugin[] = [];\nexport function setToolHookPlugins(plugins: ContextEnginePlugin[]): void {\n toolHookPlugins = plugins;\n}\nimport logger from '../utils/logger.js';\nimport { loadConfig } from '../config/config.js';\nimport { checkAutonomy } from './autonomy.js';\nimport { isToolSkillEnabled } from '../skills/registry.js';\nimport { redactSecrets } from '../security/secretGuard.js';\nimport { scanAndRedactPII, fullExfilScan } from '../security/exfilScan.js';\nimport { scanCommand, scanURL } from '../security/preExecScan.js';\nimport { runPreToolShellHooks, runPostToolShellHooks } from '../hooks/shellHooks.js';\nimport { createCheckpoint } from '../checkpoint/manager.js';\n\n/** Compute a lightweight unified diff between old and new file content */\nfunction computeUnifiedDiff(filePath: string, oldContent: string, newContent: string): string {\n if (oldContent === newContent) return `// No changes to ${filePath}`;\n const oldLines = oldContent.split('\\n');\n const newLines = newContent.split('\\n');\n const header = `--- ${filePath}\\n+++ ${filePath}`;\n const hunks: string[] = [];\n let i = 0, j = 0;\n while (i < oldLines.length || j < newLines.length) {\n if (i < oldLines.length && j < newLines.length && oldLines[i] === newLines[j]) {\n i++; j++; continue;\n }\n const startI = i, startJ = j;\n const removed: string[] = [];\n const added: string[] = [];\n while (i < oldLines.length && (j >= newLines.length || oldLines[i] !== newLines[j])) {\n removed.push(oldLines[i++]);\n }\n while (j < newLines.length && (i >= oldLines.length || oldLines[i] !== newLines[j])) {\n added.push(newLines[j++]);\n }\n if (removed.length || added.length) {\n const ctxBefore = oldLines.slice(Math.max(0, startI - 2), startI);\n const ctxAfter = oldLines.slice(i, Math.min(oldLines.length, i + 2));\n hunks.push([\n ...ctxBefore.map(l => ` ${l}`),\n ...removed.map(l => `-${l}`),\n ...added.map(l => `+${l}`),\n ...ctxAfter.map(l => ` ${l}`),\n ].join('\\n'));\n }\n }\n const body = hunks.join('\\n---\\n');\n return `${header}\\n${body}`;\n}\nimport { getCachedToolResult, cacheToolResult } from './trajectoryCompressor.js';\nimport { classifyProviderError, FailoverReason } from '../providers/errorTaxonomy.js';\nimport { snapshotBeforeWrite } from './shadowGit.js';\nimport { captureWrite, shouldCapture } from './selfProposals.js';\nimport { getSessionGoal } from './autonomyContext.js';\n\nconst COMPONENT = 'ToolRunner';\n\n/**\n * Tools whose entire purpose is to RETURN a data: URL for the LLM\n * to embed in downstream output. These bypass `sanitizeBase64` (which\n * would strip the very thing they were called to produce) and the\n * 30KB smart-truncation (which would chop the data URL mid-base64\n * making it unembeddable).\n *\n * v6.1.0-alpha.55 added `download_image` here. Tony spotted the bug\n * the way only Tony would: \"Why doesn't TITAN download the images\n * itself?\" → we added the tool in alpha.53 → ToolRunner sanitizer\n * stripped its output → Writer never saw the data URL → hotlinked\n * external URLs instead → alpha.52 placeholder caught them →\n * appearance of \"alpha.53 doesn't work.\" It worked perfectly. We\n * were just throwing away its output two lines later.\n *\n * If you add a new tool here, also update the 30KB truncation\n * exemption below (line ~685) since both share this list.\n */\nconst DATA_URL_PRODUCING_TOOLS = new Set([\n 'download_image',\n]);\n\n/**\n * G1: Sanitize base64 image data from tool results (OpenClaw pattern).\n * Prevents token explosion when vision/screenshot tools return raw base64.\n * Replaces data URIs with a compact placeholder showing byte count.\n *\n * v6.1.0-alpha.55 — takes optional `toolName` so we can EXEMPT the\n * tools whose entire output IS a data URL (see\n * DATA_URL_PRODUCING_TOOLS above). Default behavior unchanged for\n * every other tool.\n */\nfunction sanitizeBase64(content: string, toolName?: string): string {\n if (toolName && DATA_URL_PRODUCING_TOOLS.has(toolName)) return content;\n return content.replace(\n /data:image\\/[^;]+;base64,[A-Za-z0-9+/=]{100,}/g,\n (match) => {\n const bytes = Math.ceil((match.length - match.indexOf(',') - 1) * 0.75);\n return `[image: ${(bytes / 1024).toFixed(1)}KB omitted]`;\n },\n );\n}\n\n/**\n * v6.1.0-alpha.55 — exported alias for tests + future callers. The\n * Set is the source of truth for \"tools that intentionally return a\n * data: URL for the LLM to embed downstream.\"\n */\nexport function isDataUrlProducingTool(toolName: string): boolean {\n return DATA_URL_PRODUCING_TOOLS.has(toolName);\n}\n\n/** Error classification for retry decisions */\nexport type ErrorClass = 'transient' | 'permanent' | 'timeout' | 'rate_limit';\n\n/** Classify an error to determine if retry is appropriate.\n * Delegates to the centralized error taxonomy, then maps back to ErrorClass\n * for backward compatibility with tool execution retry logic.\n */\nexport function classifyError(error: Error, _toolName: string): ErrorClass {\n const classified = classifyProviderError(error);\n switch (classified.reason) {\n case FailoverReason.TIMEOUT:\n return 'timeout';\n case FailoverReason.RATE_LIMIT:\n return 'rate_limit';\n case FailoverReason.SERVER_ERROR:\n case FailoverReason.NETWORK_ERROR:\n case FailoverReason.OVERLOADED:\n case FailoverReason.EMPTY_RESPONSE:\n return 'transient';\n default:\n return classified.retryable ? 'transient' : 'permanent';\n }\n}\n\n/** Tool execution result */\nexport interface ToolResult {\n toolCallId: string;\n name: string;\n content: string;\n success: boolean;\n durationMs: number;\n /** Number of retry attempts made (0 = first try succeeded/failed) */\n retryCount?: number;\n /** Error classification if the tool failed */\n errorClass?: ErrorClass;\n /** Inline unified diff for file-modifying tools (write_file, edit_file, apply_patch) */\n diff?: string;\n /** v5.0: True when the tool is paused waiting for human approval */\n approvalPending?: boolean;\n /** v5.0: Approval request ID when approvalPending is true */\n approvalRequestId?: string;\n}\n\n/** A registered tool handler */\nexport interface ToolHandler {\n name: string;\n description: string;\n parameters: Record<string, unknown>;\n execute: (args: Record<string, unknown>, context?: { signal?: AbortSignal }) => Promise<string>;\n /**\n * beta.15 (Phase D.3) — optional declarative contract. When set,\n * the tool runner validates incoming args against\n * `contract.input` BEFORE calling execute() and rejects malformed\n * calls with a clear, LLM-friendly error message. Skills without\n * a contract continue to work unchanged — opt-in upgrade.\n */\n contract?: ToolContract;\n}\n\n/** Global tool registry */\nconst toolRegistry: Map<string, ToolHandler> = new Map();\n\n/** Register a tool */\nexport function registerTool(handler: ToolHandler): void {\n toolRegistry.set(handler.name, handler);\n logger.debug(COMPONENT, `Registered tool: ${handler.name}`);\n}\n\n/** Unregister a tool */\nexport function unregisterTool(name: string): void {\n toolRegistry.delete(name);\n}\n\n/** Get all registered tools */\nexport function getRegisteredTools(): ToolHandler[] {\n return Array.from(toolRegistry.values());\n}\n\n/** Convert registered tools to LLM tool definitions */\nexport function getToolDefinitions(): ToolDefinition[] {\n const config = loadConfig();\n const allowed = new Set(config.security.allowedTools);\n const denied = new Set(config.security.deniedTools);\n\n return Array.from(toolRegistry.values())\n .filter((tool) => {\n if (denied.has(tool.name)) return false;\n if (allowed.size > 0 && !allowed.has(tool.name)) return false;\n if (!isToolSkillEnabled(tool.name)) return false;\n return true;\n })\n .map((tool) => ({\n type: 'function' as const,\n function: {\n name: tool.name,\n description: tool.description,\n parameters: tool.parameters,\n },\n }));\n}\n\n/** Execute a single tool call */\nexport async function executeTool(toolCall: ToolCall, channel?: string): Promise<ToolResult> {\n const config = loadConfig();\n const startTime = Date.now();\n const handler = toolRegistry.get(toolCall.function.name);\n\n if (!handler) {\n // LangGraph pattern: tell the LLM which tools actually exist so it can self-correct\n const available = Array.from(toolRegistry.keys()).sort();\n const suggestions = available.filter(t => {\n const name = toolCall.function.name.toLowerCase();\n return t.toLowerCase().includes(name.slice(0, 4)) || name.includes(t.slice(0, 4));\n }).slice(0, 5);\n const hint = suggestions.length > 0\n ? `\\nDid you mean: ${suggestions.join(', ')}?`\n : `\\nAvailable tools include: ${available.slice(0, 20).join(', ')}${available.length > 20 ? ` (and ${available.length - 20} more)` : ''}`;\n return {\n toolCallId: toolCall.id,\n name: toolCall.function.name,\n content: `Error: \"${toolCall.function.name}\" is not a valid tool.${hint}\\nPlease use one of the available tools.`,\n success: false,\n durationMs: Date.now() - startTime,\n };\n }\n\n // Check permissions\n if (config.security.deniedTools.includes(handler.name)) {\n return {\n toolCallId: toolCall.id,\n name: handler.name,\n content: `Error: Tool \"${handler.name}\" is denied by security policy`,\n success: false,\n durationMs: Date.now() - startTime,\n };\n }\n\n // Check if parent skill is enabled\n if (!isToolSkillEnabled(handler.name)) {\n return {\n toolCallId: toolCall.id,\n name: handler.name,\n content: `Error: Tool \"${handler.name}\" is disabled — its parent skill is turned off. Enable it in Mission Control.`,\n success: false,\n durationMs: Date.now() - startTime,\n };\n }\n\n // Parse arguments\n let args: Record<string, unknown> = {};\n try {\n args = JSON.parse(toolCall.function.arguments);\n } catch (parseErr) {\n logger.warn('ToolRunner', `Malformed JSON args for ${handler.name}: ${(parseErr as Error).message} — raw: ${(toolCall.function.arguments || '').slice(0, 200)}`);\n // Try to salvage: if it looks like a truncated JSON, extract what we can\n const salvageMatch = (toolCall.function.arguments || '').match(/\\{[\\s\\S]*/);\n if (salvageMatch) {\n try {\n // Attempt to close the JSON and parse\n const fixed = salvageMatch[0].replace(/,?\\s*$/, '}');\n args = JSON.parse(fixed);\n logger.info('ToolRunner', `Salvaged partial JSON args for ${handler.name}`);\n } catch {\n // A5: Return error instead of executing with empty args (LangGraph pattern)\n return {\n toolCallId: toolCall.id,\n name: handler.name,\n content: `Error: Could not parse arguments for \"${handler.name}\". Raw: ${(toolCall.function.arguments || '').slice(0, 200)}. Please provide valid JSON arguments.`,\n success: false,\n durationMs: Date.now() - startTime,\n };\n }\n }\n }\n\n // beta.15 (Phase D.3) — Zod contract validation. Runs BEFORE the\n // legacy required-fields check below so contract-bearing skills\n // get a precise error message. Skills without a contract fall\n // through to the legacy check unchanged.\n //\n // Contracts can be attached two ways:\n // 1. Directly on the ToolHandler (handler.contract — for new skills\n // that want a self-contained file).\n // 2. Registered in the global contract registry (for retrofitting\n // contracts onto existing skills without touching their files).\n // We check both, in that order.\n let contractToUse = handler.contract;\n if (!contractToUse) {\n try {\n const { getToolContract } = await import('./toolContract.js');\n contractToUse = getToolContract(handler.name);\n } catch { /* contract registry unavailable — skip */ }\n }\n if (contractToUse) {\n try {\n // Replace `args` with the parsed (possibly coerced) shape\n // so execute() receives Zod-cleaned arguments — string\n // numerics become numbers, undefined fields get defaults, etc.\n args = validateToolCall(contractToUse, args) as Record<string, unknown>;\n } catch (validationErr) {\n if (validationErr instanceof ToolValidationError) {\n logger.warn(\n 'ToolRunner',\n `Contract validation rejected ${handler.name}: ${validationErr.message}`,\n );\n return {\n toolCallId: toolCall.id,\n name: handler.name,\n content: formatValidationError(validationErr),\n success: false,\n durationMs: Date.now() - startTime,\n };\n }\n // Unexpected non-validation error during parse — fall\n // through and let the rest of the runner handle it.\n }\n }\n\n // Schema validation: check required parameters before execution (LangGraph pattern)\n if (handler.parameters && typeof handler.parameters === 'object') {\n const schema = handler.parameters as { required?: string[]; properties?: Record<string, unknown> };\n if (schema.required && Array.isArray(schema.required)) {\n const missing = schema.required.filter(key => args[key] === undefined || args[key] === null);\n if (missing.length > 0) {\n const available = schema.properties ? Object.keys(schema.properties) : [];\n logger.warn('ToolRunner', `[SchemaValidation] ${handler.name}: missing required params: ${missing.join(', ')}`);\n return {\n toolCallId: toolCall.id,\n name: handler.name,\n content: `Error: Missing required parameter(s): ${missing.join(', ')}. ` +\n `Expected parameters: ${available.join(', ')}. Please provide all required arguments.`,\n success: false,\n durationMs: Date.now() - startTime,\n };\n }\n }\n }\n\n // v6.1.0-alpha.11 — Worktree scope (spike).\n //\n // When the current async context has a worktree scope active (see\n // src/agent/worktreeScope.ts), redirect mutating tool writes into\n // the scope's worktree directory. Lets multiple concurrent spawns\n // write the \"same\" filename without colliding. Pure-path-rewrite\n // — the tool still runs, just into a different absolute path.\n //\n // This runs BEFORE guardrails so guardrails see the rewritten\n // absolute path, not the LLM's original relative path (which\n // resolves against cwd and may falsely trigger system-path\n // protections). The self-mod scope-lock further below picks up\n // the same rewritten path; in practice worktree paths live under\n // ~/.titan/worktrees which isn't on the self-mod target list, so\n // self-mod stays a no-op for worktree-scoped writes.\n const MUTATING_TOOLS_FOR_WORKTREE = new Set(['write_file', 'edit_file', 'append_file', 'apply_patch']);\n if (MUTATING_TOOLS_FOR_WORKTREE.has(handler.name)) {\n try {\n const { getCurrentWorktreeScope } = await import('./worktreeScope.js');\n const wtScope = getCurrentWorktreeScope();\n if (wtScope) {\n const rawPath = (args.path || args.file_path || args.filePath) as string | undefined;\n if (typeof rawPath === 'string' && rawPath.length > 0) {\n if (rawPath.startsWith('/') || rawPath.startsWith('~')) {\n return {\n toolCallId: toolCall.id,\n name: handler.name,\n content: `Error: this spawn runs in a worktree scope (${wtScope.subtaskId}). File writes must use RELATIVE paths — got \"${rawPath}\". Rewrite to something like \"output.md\" or \"src/foo.ts\" without a leading \"/\" or \"~\".`,\n success: false,\n durationMs: Date.now() - startTime,\n };\n }\n const { join: joinPath, dirname } = await import('path');\n const { mkdirSync } = await import('fs');\n const newPath = joinPath(wtScope.path, rawPath);\n if (args.path !== undefined) args.path = newPath;\n if (args.file_path !== undefined) args.file_path = newPath;\n if (args.filePath !== undefined) args.filePath = newPath;\n try { mkdirSync(dirname(newPath), { recursive: true }); }\n catch { /* best-effort */ }\n logger.debug(COMPONENT, `[Worktree] Redirected ${handler.name} → ${newPath} (scope: ${wtScope.goalId}/${wtScope.subtaskId})`);\n }\n }\n } catch (err) {\n logger.debug(COMPONENT, `Worktree scope check skipped: ${(err as Error).message}`);\n }\n }\n\n // Guardrails: validate tool call before execution\n try {\n const { guardToolCall } = await import('./guardrails.js');\n const guardResult = guardToolCall(handler.name, args);\n if (!guardResult.allowed) {\n logger.warn('ToolRunner', `[Guardrails] Blocked ${handler.name}: ${guardResult.reason}`);\n return {\n toolCallId: toolCall.id,\n name: handler.name,\n content: `Error: Tool call blocked by guardrails — ${guardResult.reason}`,\n success: false,\n durationMs: Date.now() - startTime,\n };\n }\n } catch { /* guardrails unavailable — continue */ }\n\n // Read-only tool result cache (60s TTL, helper self-gates to read-only allowlist)\n const cacheArgKey = toolCall.function.arguments || '{}';\n const cachedResult = getCachedToolResult(handler.name, cacheArgKey);\n if (cachedResult !== null) {\n logger.info(COMPONENT, `[Cache HIT] ${handler.name}`);\n return {\n toolCallId: toolCall.id,\n name: handler.name,\n content: cachedResult,\n success: true,\n durationMs: Date.now() - startTime,\n };\n }\n\n logger.info(COMPONENT, `Executing tool: ${handler.name}`);\n\n // v5.0: Pre-execution scanner for dangerous commands\n if (handler.name === 'shell' || handler.name === 'code_exec') {\n const cmdArg = (args.command || args.code || args.script || '') as string;\n const scan = scanCommand(cmdArg);\n if (!scan.allowed) {\n return {\n toolCallId: toolCall.id,\n name: handler.name,\n content: `Error: Pre-execution scan blocked this command\\n${scan.warnings.join('\\n')}`,\n success: false,\n durationMs: Date.now() - startTime,\n };\n }\n if (scan.warnings.length > 0 && scan.level === 'warn') {\n logger.warn('ToolRunner', `Pre-exec warnings for ${handler.name}: ${scan.warnings.join('; ')}`);\n }\n }\n if (handler.name === 'browser_navigate' || handler.name === 'browser_auto_nav') {\n const urlArg = (args.url || args.target || '') as string;\n const scan = scanURL(urlArg);\n if (!scan.allowed) {\n return {\n toolCallId: toolCall.id,\n name: handler.name,\n content: `Error: Pre-execution scan blocked this URL\\n${scan.warnings.join('\\n')}`,\n success: false,\n durationMs: Date.now() - startTime,\n };\n }\n }\n\n // v5.0: Shell hooks — pre-tool\n const { getCurrentSessionId } = await import('./agent.js');\n const sessionId = typeof getCurrentSessionId === 'function' ? getCurrentSessionId() : null;\n const shellPre = await runPreToolShellHooks(handler.name, args, sessionId || toolCall.id, 'default', 0);\n if (!shellPre.allow) {\n return {\n toolCallId: toolCall.id,\n name: handler.name,\n content: 'Blocked by shell hook: ' + (shellPre.reason || 'Hook denied execution'),\n success: false,\n durationMs: Date.now() - startTime,\n };\n }\n if (shellPre.modifiedArgs) args = shellPre.modifiedArgs;\n\n // Pre-tool hooks — plugins can block or modify args\n if (toolHookPlugins.length > 0) {\n const hookResult = await runPreTool(toolHookPlugins, handler.name, args);\n if (!hookResult.allow) {\n return {\n toolCallId: toolCall.id,\n name: handler.name,\n content: 'Blocked by hook: ' + (hookResult.reason || 'Plugin denied execution'),\n success: false,\n durationMs: Date.now() - startTime,\n };\n }\n if (hookResult.modifiedArgs) args = hookResult.modifiedArgs;\n }\n\n // Autonomy gate: check if the tool is permitted under current mode\n const autonomyResult = await checkAutonomy(handler.name, args, channel);\n if (!autonomyResult.allowed) {\n return {\n toolCallId: toolCall.id,\n name: handler.name,\n content: 'Action blocked by autonomy policy: ' + (autonomyResult.reason || 'Not permitted'),\n success: false,\n durationMs: Date.now() - startTime,\n };\n }\n\n // Shadow git checkpoint — snapshot files before mutation (fire-and-forget)\n const MUTATING_TOOLS = new Set(['write_file', 'edit_file', 'append_file', 'apply_patch']);\n\n // v4.9.0-local.7: kill-switch gate for file mutations. If the kill switch\n // is engaged, refuse write/edit/append/apply_patch so the initiative loop\n // can't keep accumulating fix-oscillations while the human hasn't resumed.\n // This closes the gap where `spawn_agent`, `autopilot`, and the pressure\n // cycle were gated but the main agent's tool path was not — meaning\n // initiative could keep rewriting the same files for hours after a kill.\n // See kill-switch.json `history` for the trigger; resume via\n // POST /api/safety/resume.\n if (MUTATING_TOOLS.has(handler.name)) {\n try {\n const { isKilled, getState } = await import('../safety/killSwitch.js');\n if (isKilled()) {\n const state = getState();\n const lastReason = state?.lastEvent?.reason ?? 'kill switch engaged';\n logger.warn(COMPONENT, `[KillSwitch] Refusing ${handler.name} — ${lastReason}`);\n return {\n toolCallId: toolCall.id,\n name: handler.name,\n content: `Error: File mutation refused — kill switch is engaged (${lastReason}). ` +\n `Resume via POST /api/safety/resume after investigating the trigger, ` +\n `then retry. Do NOT retry this tool call until resumed.`,\n success: false,\n durationMs: Date.now() - startTime,\n };\n }\n } catch { /* kill switch module unavailable — fall through (fail-open on infra error) */ }\n }\n\n // v4.9.0-local.8: Self-mod scope lock + staging.\n //\n // Three-layer policy when the active session has a goal tagged as\n // self-modifying (see config.autonomy.selfMod.tags):\n // 1. Writes to paths OUTSIDE config.autonomy.selfMod.target are refused\n // (prevents LARPing self-improvement by writing to ~/titan-saas etc)\n // 2. When staging is enabled, writes INSIDE target are diverted to a\n // per-goal staging directory and a `self_mod_pr` approval is filed\n // 3. The original path is stored as `targetPath` on the staging entry\n // so the human sees what would land where if they approve the PR\n //\n // This is the deeper fix for the pattern observed 2026-04-18 where a\n // \"self-healing framework\" goal completed 100% by writing to an unrelated\n // Next.js app.\n let stagedRedirect: { stagedPath: string; targetPath: string } | null = null;\n if (MUTATING_TOOLS.has(handler.name)) {\n const rawFilePath = (args.path || args.file_path || args.filePath) as string | undefined;\n if (rawFilePath) {\n try {\n const { getCurrentSessionId } = await import('./agent.js');\n const { decideScope } = await import('./selfModStaging.js');\n const sid: string | null = typeof getCurrentSessionId === 'function' ? getCurrentSessionId() : null;\n const decision = decideScope(sid, rawFilePath);\n if (decision.action === 'reject') {\n logger.warn(COMPONENT, `[ScopeLock] Refusing ${handler.name}: ${decision.reason}`);\n return {\n toolCallId: toolCall.id,\n name: handler.name,\n content: `Error: ${decision.reason}\\n\\nRewrite the path to live inside the self-mod target, OR retag the goal to remove self-mod tags, OR pause the goal and create a properly-scoped one.`,\n success: false,\n durationMs: Date.now() - startTime,\n };\n }\n if (decision.action === 'stage' && decision.stagedPath && decision.targetPath) {\n stagedRedirect = { stagedPath: decision.stagedPath, targetPath: decision.targetPath };\n // Rewrite the tool args so the handler writes to staging.\n // Preserve both `path` and `file_path` variants since\n // different tools use different field names.\n if (args.path !== undefined) args.path = decision.stagedPath;\n if (args.file_path !== undefined) args.file_path = decision.stagedPath;\n if (args.filePath !== undefined) args.filePath = decision.stagedPath;\n // Ensure the staged parent dir exists; write_file may not\n // mkdir -p on all code paths.\n try {\n const { mkdirSync } = await import('fs');\n const { dirname } = await import('path');\n mkdirSync(dirname(decision.stagedPath), { recursive: true });\n } catch { /* best-effort */ }\n logger.info(COMPONENT, `[SelfModStaging] Diverting ${handler.name} → ${decision.stagedPath} (would land at ${decision.targetPath} on approval)`);\n }\n } catch (err) {\n logger.debug(COMPONENT, `[ScopeLock] check failed (fail-open): ${(err as Error).message}`);\n }\n }\n }\n\n // v5.0: Filesystem checkpoint before destructive operations\n if (MUTATING_TOOLS.has(handler.name)) {\n const cpPaths: string[] = [];\n const cpPath = (args.path || args.file_path || args.filePath) as string;\n if (cpPath) cpPaths.push(cpPath);\n if (cpPaths.length > 0) {\n createCheckpoint(sessionId || toolCall.id, handler.name, args, cpPaths);\n }\n }\n\n if (MUTATING_TOOLS.has(handler.name)) {\n // Use the (potentially rewritten) path for shadow-git + fix-oscillation\n const filePath = (args.path || args.file_path || args.filePath) as string;\n if (filePath) {\n snapshotBeforeWrite(handler.name, filePath).catch(err =>\n logger.debug(COMPONENT, `Shadow checkpoint skipped: ${(err as Error).message}`),\n );\n // v4.9.0: fix-oscillation tracker. Same file written/edited\n // twice within 24h flags as oscillation, which feeds the\n // kill switch (3+ oscillations → kill). Best-effort — never\n // blocks the write.\n (async () => {\n try {\n const { recordFixEvent } = await import('../safety/fixOscillation.js');\n recordFixEvent({\n target: filePath,\n kind: 'file',\n detail: `${handler.name} via ${channel ?? 'unknown'}`,\n });\n } catch (err) {\n logger.debug(COMPONENT, `Fix-oscillation skipped: ${(err as Error).message}`);\n }\n })();\n }\n // v4.8.0: self-proposal capture — if this write is happening inside\n // an autonomous Soma-driven session, stash a copy for specialist\n // review. Fire-and-forget — never blocks tool execution.\n captureSelfProposalIfApplicable(handler.name, args).catch(err =>\n logger.debug(COMPONENT, `Self-proposal capture skipped: ${(err as Error).message}`),\n );\n }\n\n // Per-tool timeout lookup\n const toolTimeouts = (config.security as Record<string, unknown>).toolTimeouts as Record<string, number> | undefined;\n const baseTimeout = toolTimeouts?.[handler.name] || config.security.commandTimeout || 30000;\n\n // Retry config\n const retryConfig = (config.security as Record<string, unknown>).toolRetry as { enabled?: boolean; maxRetries?: number; backoffBaseMs?: number } | undefined;\n const retryEnabled = retryConfig?.enabled !== false;\n const maxRetries = retryConfig?.maxRetries ?? 3;\n const backoffBase = retryConfig?.backoffBaseMs ?? 1000;\n\n let lastError: Error | null = null;\n let lastErrorClass: ErrorClass = 'permanent';\n let attempt = 0;\n\n // Capture pre-execution file state for diff generation\n let preContent: string | undefined;\n let filePathForDiff: string | undefined;\n if (['write_file', 'edit_file', 'apply_patch'].includes(handler.name)) {\n const pathArg = args.path as string | undefined;\n if (pathArg) {\n filePathForDiff = pathArg;\n try {\n if (existsSync(pathArg)) preContent = readFileSync(pathArg, 'utf-8');\n } catch { /* ignore read errors */ }\n }\n }\n\n // Swarm invariants — hard safety rules (fail-open)\n try {\n const { checkInvariants } = await import('../safety/invariants.js');\n const invariant = checkInvariants(handler.name, args);\n if (!invariant.pass) {\n logger.warn(COMPONENT, `[Invariant] Blocked ${handler.name}: ${invariant.reason}`);\n return {\n toolCallId: toolCall.id,\n name: handler.name,\n content: `INVARIANT_VIOLATION: ${invariant.reason}`,\n success: false,\n durationMs: Date.now() - startTime,\n };\n }\n } catch (err) {\n logger.warn(COMPONENT, `Invariant check failed (fail-open): ${(err as Error).message}`);\n }\n\n // beta.16 (Phase D.4) — auto-mode classifier + approval gate.\n //\n // PRECEDENCE (fixed in beta.17 after Codex P1 review):\n // 1. USER-CONFIGURED requiresApproval(handler.name) WINS FIRST.\n // If the user has explicitly added this tool to their\n // approval list, we file the approval regardless of what\n // the classifier says. The classifier never overrides\n // explicit user policy — auto-mode is a CONVENIENCE for\n // contract-declared safe tools, not a license to ignore\n // user-configured guards.\n // 2. THEN the classifier. If classifier says 'gate', file\n // approval (even if requiresApproval was false). If 'auto'\n // or 'notify', proceed past the gate; 'notify' logs a\n // breadcrumb so the autonomous action is visible.\n //\n // Beta.16 had these reversed: classifier short-circuited past\n // requiresApproval. That meant the user adding `write_file` to\n // their approval list did nothing because the classifier\n // unilaterally decided `auto` for it. Codex caught it on\n // review — P1 #1.\n const classification = classifyToolCall(handler.name);\n\n // Step 1 — user-configured approval ALWAYS runs first. The\n // user's explicit list is the source of truth.\n let userApprovalWantsGate = false;\n try {\n const { requiresApproval } = await import('../skills/builtin/approval_gates.js');\n userApprovalWantsGate = requiresApproval(handler.name);\n } catch (approvalErr) {\n // Approval gates module unavailable → log + fall through.\n // Classifier still gets to decide. Fail-open behavior preserved.\n logger.warn(COMPONENT, `Approval-config probe failed (fail-open): ${(approvalErr as Error).message}`);\n }\n\n // Step 2 — file an approval if EITHER the user's list demands it\n // OR the classifier says 'gate'. Belt + suspenders, with the user\n // explicitly first.\n if (userApprovalWantsGate || classification.decision === 'gate') {\n try {\n const { createApprovalRequest } = await import('../skills/builtin/approval_gates.js');\n const gateReason = userApprovalWantsGate\n ? 'user approval policy'\n : `classifier policy=${classification.policy} risk=${classification.riskLevel ?? 'unknown'}`;\n logger.info(COMPONENT, `[ApprovalGate] Tool \"${handler.name}\" requires human approval (${gateReason}) — filing request`);\n const request = createApprovalRequest(handler.name, args, sessionId || toolCall.id);\n if (request.status === 'pending') {\n return {\n toolCallId: toolCall.id,\n name: handler.name,\n content: `Awaiting approval: Tool \"${handler.name}\" requires human confirmation before execution. ` +\n `Request ID: ${request.id}. ` +\n `Approve with \"approve ${request.id}\" or deny with \"deny ${request.id}\".`,\n success: false,\n approvalPending: true,\n approvalRequestId: request.id,\n durationMs: Date.now() - startTime,\n };\n }\n if (request.status === 'denied') {\n return {\n toolCallId: toolCall.id,\n name: handler.name,\n content: `Error: Tool \"${handler.name}\" was auto-denied by approval policy. ` +\n `Check approval preferences or change the request decision.`,\n success: false,\n durationMs: Date.now() - startTime,\n };\n }\n // status === 'approved' or anything else → fall through to execute().\n } catch (approvalErr) {\n // Approval gates module unavailable → fail-open so the agent\n // doesn't deadlock. We've already warned above.\n logger.warn(COMPONENT, `Approval gate check failed (fail-open): ${(approvalErr as Error).message}`);\n }\n } else if (classification.decision === 'auto') {\n logger.debug(COMPONENT, formatBreadcrumb(handler.name, classification));\n } else if (classification.decision === 'notify') {\n // Passive notification. Future hook point for Mission Canvas\n // toasts / trajectory `note` events. Logged at info level so\n // the autonomous action is visible in titan-gateway.log + the\n // structured log pipeline.\n logger.info(COMPONENT, formatBreadcrumb(handler.name, classification));\n }\n\n const useWorktreeIsolation =\n config.security.useWorktreeIsolation === true &&\n contractToUse?.sideEffects.includes('destructive') === true;\n\n for (; attempt <= (retryEnabled ? maxRetries : 0); attempt++) {\n try {\n // On timeout retry, double the timeout\n const timeout = (attempt > 0 && lastErrorClass === 'timeout') ? baseTimeout * 2 : baseTimeout;\n\n const timeoutController = useWorktreeIsolation ? new AbortController() : undefined;\n const timeoutError = new Error(`Tool \"${handler.name}\" timed out after ${timeout}ms`);\n let timeoutHandle: ReturnType<typeof setTimeout> | undefined;\n let result = await Promise.race([\n useWorktreeIsolation\n ? runInIsolatedWorktree({\n spawnId: toolCall.id,\n args,\n execute: handler.execute,\n baseCwd: process.cwd(),\n signal: timeoutController?.signal,\n })\n : handler.execute(args),\n new Promise<string>((_, reject) => {\n timeoutHandle = setTimeout(() => {\n timeoutController?.abort(timeoutError);\n reject(timeoutError);\n }, timeout);\n }),\n ]).finally(() => {\n if (timeoutHandle) clearTimeout(timeoutHandle);\n });\n\n // Secret exfiltration guard — scan tool output before it leaves\n result = redactSecrets(result);\n\n // v5.0: PII redaction (privacy compliance)\n const config = loadConfig();\n if (config.security?.redactPII) {\n result = scanAndRedactPII(result);\n }\n\n // v5.0: Full exfiltration scan (layer 2-5) when configured\n if (config.security?.secretScan?.level === 'full') {\n const scan = fullExfilScan(result, 'tool_output');\n if (scan.blocked) {\n logger.warn('ToolRunner', `Exfiltration scan blocked ${handler.name}: ${scan.findings.map(f => f.type).join(', ')}`);\n }\n result = scan.redacted;\n }\n\n const durationMs = Date.now() - startTime;\n if (attempt > 0) {\n logger.info(COMPONENT, `Tool ${handler.name} succeeded on retry ${attempt} in ${durationMs}ms`);\n } else {\n logger.info(COMPONENT, `Tool ${handler.name} completed in ${durationMs}ms`);\n }\n\n // G1: Strip base64 image data before size check (prevents token\n // explosion). v6.1.0-alpha.55 — passes tool name so\n // DATA_URL_PRODUCING_TOOLS (download_image) get exempted; for\n // those the data URL IS the point of calling the tool.\n let finalContent = sanitizeBase64(result, handler.name);\n\n // Smart truncation — keep head + tail for large results (TITAN pattern).\n // v6.1.0-alpha.55 — DATA_URL_PRODUCING_TOOLS are exempted because\n // truncating mid-base64 produces an unusable data URL. The Writer\n // would then fall back to hotlinking the source URL, which is\n // exactly what Tony was seeing before alpha.55 (\"alpha.53 didn't\n // work\" — it did, this layer was killing it).\n if (finalContent.length > 30000 && !DATA_URL_PRODUCING_TOOLS.has(handler.name)) {\n const head = finalContent.slice(0, 20000);\n const tail = finalContent.slice(-5000);\n finalContent = head + '\\n\\n[... ' + (finalContent.length - 25000) + ' chars omitted ...]\\n\\n' + tail;\n logger.info(COMPONENT, `Tool ${handler.name} output truncated: ${result.length} → ${finalContent.length} chars`);\n }\n\n // v5.0: Shell hooks — post-tool\n const shellPost = await runPostToolShellHooks(handler.name, args, finalContent, sessionId || toolCall.id, 'default', 0);\n if (shellPost !== undefined) finalContent = shellPost;\n\n // Post-tool hooks — plugins can modify result\n if (toolHookPlugins.length > 0) {\n const hookResult = await runPostTool(toolHookPlugins, handler.name, args, { content: finalContent, success: true, durationMs });\n if (hookResult.modifiedContent !== undefined) finalContent = hookResult.modifiedContent;\n }\n\n // Cache the result for read-only tools (helper self-gates)\n cacheToolResult(handler.name, cacheArgKey, finalContent);\n\n // v4.9.0-local.8: if this write was diverted to staging, record\n // it in the self_mod_pr bundle. Fire-and-forget — must NOT block\n // the tool's return value or the agent loop.\n if (stagedRedirect) {\n (async () => {\n try {\n const { getCurrentSessionId } = await import('./agent.js');\n const { recordStagedWrite } = await import('./selfModStaging.js');\n const sid: string | null = typeof getCurrentSessionId === 'function' ? getCurrentSessionId() : null;\n await recordStagedWrite({\n sessionId: sid,\n toolName: handler.name,\n stagedPath: stagedRedirect!.stagedPath,\n targetPath: stagedRedirect!.targetPath,\n });\n } catch (err) {\n logger.debug(COMPONENT, `[SelfModStaging] recordStagedWrite failed: ${(err as Error).message}`);\n }\n })();\n }\n\n // Fire-and-forget telemetry + activity log\n (async () => {\n try {\n const { logActivity } = await import('../telemetry/activityLog.js');\n logActivity({ event: 'tool_call', tool: handler.name, channel: channel ?? 'unknown' });\n if (MUTATING_TOOLS.has(handler.name)) {\n logActivity({ event: 'file_edit', tool: handler.name, path: (args.path || args.file_path || args.filePath) as string | undefined });\n }\n if (handler.name === 'web_search') {\n logActivity({ event: 'web_search', query: (args.query || args.q) as string | undefined });\n }\n if (handler.name === 'web_fetch') {\n logActivity({ event: 'web_fetch', url: (args.url || args.target) as string | undefined });\n }\n if (handler.name === 'run_eval' || handler.name === 'eval_suite') {\n logActivity({ event: 'eval_run', suite: (args.suite || args.name) as string | undefined });\n }\n } catch { /* activity log non-critical */ }\n const cfg = loadConfig();\n if (cfg.telemetry?.enabled) {\n try {\n appendFileSync(TELEMETRY_EVENTS_PATH, JSON.stringify({\n event: 'tool_called',\n properties: { toolName: handler.name, success: true, durationMs, channel: channel ?? 'unknown' },\n timestamp: new Date().toISOString(),\n }) + '\\n', 'utf-8');\n } catch { /* non-critical */ }\n }\n // Remote analytics (PostHog + custom collector)\n const { trackToolCall } = await import('../analytics/featureTracker.js');\n const { getCurrentSessionId } = await import('./agent.js').catch(() => ({ getCurrentSessionId: () => null }));\n const sid = typeof getCurrentSessionId === 'function' ? getCurrentSessionId() : null;\n trackToolCall(handler.name, true, durationMs, undefined, sid ?? undefined);\n })();\n\n // Compute inline diff for file-modifying tools\n let diff: string | undefined;\n if (['write_file', 'edit_file', 'apply_patch'].includes(handler.name) && filePathForDiff && !stagedRedirect) {\n try {\n const postContent = existsSync(filePathForDiff) ? readFileSync(filePathForDiff, 'utf-8') : '';\n diff = computeUnifiedDiff(filePathForDiff, preContent ?? '', postContent);\n } catch { /* ignore diff errors */ }\n }\n\n return {\n toolCallId: toolCall.id,\n name: handler.name,\n content: stagedRedirect\n ? `${finalContent}\\n\\n[SelfModStaging] Diverted to staging: ${stagedRedirect.stagedPath}. A human approval is pending before this lands at ${stagedRedirect.targetPath}.`\n : finalContent,\n success: true,\n durationMs,\n retryCount: attempt,\n diff,\n };\n } catch (error) {\n lastError = error as Error;\n lastErrorClass = classifyError(lastError, handler.name);\n\n // Don't retry permanent errors\n if (lastErrorClass === 'permanent') {\n break;\n }\n\n // Don't retry if this was the last attempt\n if (attempt >= maxRetries || !retryEnabled) {\n break;\n }\n\n // Exponential backoff: 1s, 2s, 4s (capped at 8s)\n const delay = Math.min(backoffBase * Math.pow(2, attempt), 8000);\n logger.warn(COMPONENT, `Tool ${handler.name} failed (${lastErrorClass}, attempt ${attempt + 1}/${maxRetries + 1}): ${lastError.message} — retrying in ${delay}ms`);\n await new Promise(resolve => setTimeout(resolve, delay));\n }\n }\n\n // All retries exhausted or permanent error\n const durationMs = Date.now() - startTime;\n const errorMsg = lastError?.message || 'Unknown error';\n const retryCount = attempt; // actual number of retries performed (matches success path)\n logger.error(COMPONENT, `Tool ${handler.name} failed (${lastErrorClass}${retryCount > 0 ? `, ${retryCount} retries` : ''}): ${errorMsg}`);\n\n // Fire-and-forget telemetry\n (async () => {\n const cfg = loadConfig();\n if (cfg.telemetry?.enabled) {\n try {\n appendFileSync(TELEMETRY_EVENTS_PATH, JSON.stringify({\n event: 'tool_called',\n properties: { toolName: handler.name, success: false, durationMs, errorClass: lastErrorClass, channel: channel ?? 'unknown' },\n timestamp: new Date().toISOString(),\n }) + '\\n', 'utf-8');\n } catch { /* non-critical */ }\n }\n // Remote analytics (PostHog + custom collector)\n const { trackToolCall } = await import('../analytics/featureTracker.js');\n const { getCurrentSessionId } = await import('./agent.js').catch(() => ({ getCurrentSessionId: () => null }));\n const sid = typeof getCurrentSessionId === 'function' ? getCurrentSessionId() : null;\n trackToolCall(handler.name, false, durationMs, lastErrorClass, sid ?? undefined);\n })();\n\n return {\n toolCallId: toolCall.id,\n name: handler.name,\n content: `Error: ${errorMsg}`,\n success: false,\n durationMs,\n retryCount,\n errorClass: lastErrorClass,\n };\n}\n\n/** Execute multiple tool calls (in parallel where possible, with write-conflict detection) */\n/**\n * Apply the tool-output cap to every result. v5.8.0 — Anthropic's \"Writing\n * effective tools for AI agents\" recommends a 25k-token cap on tool results\n * with a tool-aware truncation hint. We do it here at the boundary so every\n * caller (agent loop, sub-agent, voice path) benefits without each having\n * to remember to wrap individually.\n * Reference: docs/HARNESS-PATTERNS.md, src/agent/toolOutputCap.ts\n */\nfunction applyOutputCaps(results: ToolResult[]): ToolResult[] {\n return results.map((r) => {\n const capped = capToolOutput(r.name, r.content);\n if (!capped.truncated) return r;\n // Only mutate when truncation actually happened, preserving identity\n // when content was already small.\n logger.info(COMPONENT, `[OutputCap] ${r.name} truncated ${capped.originalSize} → ${capped.content.length} chars`);\n return { ...r, content: capped.content };\n });\n}\n\nexport async function executeTools(toolCalls: ToolCall[], channel?: string): Promise<ToolResult[]> {\n // Single tool — fast path\n if (toolCalls.length <= 1) {\n const results = await Promise.all(toolCalls.map(tc => executeTool(tc, channel)));\n return applyOutputCaps(results);\n }\n\n // Multiple tools — use parallelTools engine with write-conflict detection\n const parallelCalls = toolCalls.map(tc => {\n let args: Record<string, unknown> = {};\n try { args = JSON.parse(tc.function.arguments); } catch { /* use empty */ }\n return { id: tc.id, name: tc.function.name, args };\n });\n\n const executor = async (name: string, args: Record<string, unknown>): Promise<string> => {\n // Build a synthetic ToolCall for executeTool\n const syntheticTc: ToolCall = {\n id: '',\n type: 'function',\n function: { name, arguments: JSON.stringify(args) },\n };\n const result = await executeTool(syntheticTc, channel);\n return result.content;\n };\n\n const parallelResults = await executeToolsParallel(parallelCalls, executor);\n\n // Map back to ToolResult format with full metadata, then apply output cap.\n const mapped: ToolResult[] = parallelResults.map(pr => ({\n toolCallId: pr.toolCallId,\n name: pr.name,\n content: pr.content,\n success: !pr.content.startsWith('Error:'),\n durationMs: 0,\n }));\n return applyOutputCaps(mapped);\n}\n\n// ── Self-proposal capture helper (v4.8.0) ────────────────────────────────\n\n/**\n * If the current write is happening in an autonomous, Soma-driven session,\n * stash a copy of the written content for specialist review. Silent no-op\n * in all other cases (user-driven edits, non-autonomous mode, or when\n * selfMod.enabled is false in config).\n */\nasync function captureSelfProposalIfApplicable(\n toolName: string,\n args: Record<string, unknown>,\n): Promise<void> {\n // Resolve what we can from the current autonomous context\n const { getCurrentSessionId } = await import('./agent.js').catch(() => ({ getCurrentSessionId: () => null }));\n const sessionId: string | null = typeof getCurrentSessionId === 'function' ? getCurrentSessionId() : null;\n const sessionGoal = getSessionGoal(sessionId);\n const config = loadConfig();\n const autonomous = (config.autonomy?.mode === 'autonomous');\n const goalProposedBy = sessionGoal?.proposedBy ?? null;\n\n if (!shouldCapture({ toolName, autonomous, goalProposedBy })) return;\n\n const filePath = (args.path || args.file_path || args.filePath) as string | undefined;\n const content = (args.content || args.new_text || args.data) as string | undefined;\n if (!filePath || !content) return;\n\n captureWrite({\n toolName,\n filePath,\n content,\n sessionId,\n agentId: null, // filled by downstream if needed\n goalId: sessionGoal?.goalId ?? null,\n goalTitle: sessionGoal?.goalTitle ?? null,\n goalProposedBy,\n });\n}\n"],"mappings":"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","names":["getCurrentSessionId","config","durationMs"]}
@@ -0,0 +1,185 @@
1
+ #!/usr/bin/env node
2
+ import { mkdirSync, appendFileSync, existsSync, readFileSync, readdirSync, statSync } from "fs";
3
+ import { join } from "path";
4
+ import { TITAN_HOME } from "../utils/constants.js";
5
+ const TRAJECTORY_DIR = join(TITAN_HOME, "trajectories");
6
+ const MAX_ARGS_BYTES = 2 * 1024;
7
+ const MAX_RESULT_BYTES = 4 * 1024;
8
+ function dayBucket(d = /* @__PURE__ */ new Date()) {
9
+ return d.toISOString().slice(0, 10);
10
+ }
11
+ function trajectoryPathFor(spawnId, when = /* @__PURE__ */ new Date()) {
12
+ return join(TRAJECTORY_DIR, dayBucket(when), `${spawnId}.jsonl`);
13
+ }
14
+ function recordingEnabled() {
15
+ return process.env.TITAN_TRAJECTORY_RECORD !== "0";
16
+ }
17
+ function writeEvent(path, event) {
18
+ try {
19
+ const line = JSON.stringify(event) + "\n";
20
+ appendFileSync(path, line, "utf-8");
21
+ } catch {
22
+ }
23
+ }
24
+ function startSpawnTrajectory(fields) {
25
+ const startedAt = Date.now();
26
+ if (!recordingEnabled()) {
27
+ return { spawnId: fields.spawnId, path: "", startedAt, disabled: true };
28
+ }
29
+ const path = trajectoryPathFor(fields.spawnId);
30
+ try {
31
+ const dir = join(path, "..");
32
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
33
+ } catch {
34
+ }
35
+ writeEvent(path, {
36
+ kind: "start",
37
+ at: startedAt,
38
+ spawnId: fields.spawnId,
39
+ data: fields
40
+ });
41
+ return { spawnId: fields.spawnId, path, startedAt, disabled: false };
42
+ }
43
+ function recordRound(handle, fields) {
44
+ if (handle.disabled) return;
45
+ writeEvent(handle.path, {
46
+ kind: "round",
47
+ at: Date.now(),
48
+ spawnId: handle.spawnId,
49
+ data: fields
50
+ });
51
+ }
52
+ function recordToolCall(handle, fields) {
53
+ if (handle.disabled) return;
54
+ writeEvent(handle.path, {
55
+ kind: "tool_call",
56
+ at: Date.now(),
57
+ spawnId: handle.spawnId,
58
+ data: {
59
+ ...fields,
60
+ args: capString(fields.args, MAX_ARGS_BYTES)
61
+ }
62
+ });
63
+ }
64
+ function recordToolResult(handle, fields) {
65
+ if (handle.disabled) return;
66
+ writeEvent(handle.path, {
67
+ kind: "tool_result",
68
+ at: Date.now(),
69
+ spawnId: handle.spawnId,
70
+ data: {
71
+ ...fields,
72
+ content: capString(fields.content, MAX_RESULT_BYTES)
73
+ }
74
+ });
75
+ }
76
+ function recordNote(handle, text) {
77
+ if (handle.disabled) return;
78
+ writeEvent(handle.path, {
79
+ kind: "note",
80
+ at: Date.now(),
81
+ spawnId: handle.spawnId,
82
+ data: { text }
83
+ });
84
+ }
85
+ function finishSpawnTrajectory(handle, fields) {
86
+ if (handle.disabled) return;
87
+ writeEvent(handle.path, {
88
+ kind: "finish",
89
+ at: Date.now(),
90
+ spawnId: handle.spawnId,
91
+ data: {
92
+ ...fields,
93
+ durationMs: Date.now() - handle.startedAt
94
+ }
95
+ });
96
+ }
97
+ function loadTrajectory(spawnId) {
98
+ if (!existsSync(TRAJECTORY_DIR)) return null;
99
+ let days;
100
+ try {
101
+ days = readdirSync(TRAJECTORY_DIR);
102
+ } catch {
103
+ return null;
104
+ }
105
+ for (const day of days) {
106
+ const path = join(TRAJECTORY_DIR, day, `${spawnId}.jsonl`);
107
+ if (existsSync(path)) {
108
+ return readJsonl(path);
109
+ }
110
+ }
111
+ return null;
112
+ }
113
+ function listTrajectories(opts = {}) {
114
+ if (!existsSync(TRAJECTORY_DIR)) return [];
115
+ const out = [];
116
+ let days;
117
+ try {
118
+ days = readdirSync(TRAJECTORY_DIR).sort().reverse();
119
+ } catch {
120
+ return [];
121
+ }
122
+ for (const day of days) {
123
+ const dayDir = join(TRAJECTORY_DIR, day);
124
+ let files;
125
+ try {
126
+ files = readdirSync(dayDir);
127
+ } catch {
128
+ continue;
129
+ }
130
+ for (const f of files) {
131
+ if (!f.endsWith(".jsonl")) continue;
132
+ const path = join(dayDir, f);
133
+ const events = readJsonl(path);
134
+ const start = events.find((e) => e.kind === "start");
135
+ if (!start || start.kind !== "start") continue;
136
+ if (opts.since && start.at < opts.since) continue;
137
+ if (opts.goalId && start.data.goalId !== opts.goalId) continue;
138
+ try {
139
+ const sizeBytes = statSync(path).size;
140
+ out.push({
141
+ spawnId: start.spawnId,
142
+ startedAt: start.at,
143
+ specialist: start.data.specialist,
144
+ model: start.data.model,
145
+ goalId: start.data.goalId,
146
+ path,
147
+ sizeBytes
148
+ });
149
+ } catch {
150
+ }
151
+ if (opts.limit && out.length >= opts.limit) return out;
152
+ }
153
+ }
154
+ return out;
155
+ }
156
+ function readJsonl(path) {
157
+ try {
158
+ const raw = readFileSync(path, "utf-8");
159
+ return raw.split("\n").filter(Boolean).map((line) => JSON.parse(line));
160
+ } catch {
161
+ return [];
162
+ }
163
+ }
164
+ function capString(s, maxBytes) {
165
+ const bytes = Buffer.byteLength(s, "utf-8");
166
+ if (bytes <= maxBytes) return s;
167
+ let cap = s;
168
+ while (Buffer.byteLength(cap, "utf-8") > maxBytes - 20) {
169
+ cap = cap.slice(0, Math.floor(cap.length * 0.9));
170
+ }
171
+ return cap + `
172
+ \u2026[truncated to ${maxBytes}b]`;
173
+ }
174
+ export {
175
+ finishSpawnTrajectory,
176
+ listTrajectories,
177
+ loadTrajectory,
178
+ recordNote,
179
+ recordRound,
180
+ recordToolCall,
181
+ recordToolResult,
182
+ startSpawnTrajectory,
183
+ trajectoryPathFor
184
+ };
185
+ //# sourceMappingURL=trajectoryRecorder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/agent/trajectoryRecorder.ts"],"sourcesContent":["/**\n * TITAN — Trajectory Recorder (v6.1.0-beta.13, Phase D.1)\n *\n * Records every sub-agent spawn as a per-spawn JSONL file on disk so\n * the runtime can be replayed, scored, and debugged after the fact.\n *\n * SISTER MODULE: `trajectory.ts` does BULK dataset capture (whole\n * conversations to a single jsonl for fine-tuning input). This module\n * does PER-SPAWN granular event streams — start, each round, each\n * tool call + result, finish. Same concept, different shapes,\n * different consumers.\n *\n * WHY this module exists:\n *\n * - Every reliable agent harness on the Picrew/awesome-agent-harness\n * catalog records trajectories: Inspect Evals, LangSmith, LangWatch,\n * Symphony, hankweave. The pattern is universal because the\n * alternative — debugging a flaky spawn from stdout fragments — is\n * untenable at scale.\n *\n * - The beta.9/10 verification ring tells us WHETHER a write\n * succeeded. Trajectories tell us HOW the spawn got there: which\n * model, which prompt, which tool calls in what order, how long\n * each round took. Pair them and a \"stuck mission\" becomes\n * diagnosable.\n *\n * DESIGN:\n *\n * - One JSONL file per spawn at\n * `~/.titan/trajectories/<YYYY-MM-DD>/<spawnId>.jsonl`.\n * - Append-only writes via `appendFileSync` (atomic on local FS).\n * - First line is always a `start` event; last is always `finish`.\n * - In-between events: `round`, `tool_call`, `tool_result`, `note`.\n *\n * - Recording is on by default; opt out via `TITAN_TRAJECTORY_RECORD=0`.\n *\n * - Failure is silent: if the disk is full or the dir isn't\n * writable, the recorder swallows the error and the spawn\n * continues. We don't fail real work to capture an audit trail.\n *\n * TRADE-OFFS:\n *\n * - JSONL files accumulate. A companion `titan trajectory prune`\n * CLI command (follow-up) will handle retention. Single spawn ≈\n * 10–50 KB so a year of heavy use is still O(GB) at worst.\n *\n * - Tool args + results are capped at 2 KB and 4 KB respectively\n * so a single 50 MB HTML-report write doesn't bloat trajectories.\n *\n * FOLLOW-UP:\n *\n * - SSE endpoint `/api/trajectories/stream` for live UI tailing.\n * - Mission Canvas trajectory pane.\n * - `titan trajectory replay <spawnId>` for A/B model comparison.\n */\n\nimport { mkdirSync, appendFileSync, existsSync, readFileSync, readdirSync, statSync } from 'fs';\nimport { join } from 'path';\nimport { TITAN_HOME } from '../utils/constants.js';\n\n/* ─────────────────────────── Types ─────────────────────────── */\n\nexport type TrajectoryEventKind =\n | 'start' | 'round' | 'tool_call' | 'tool_result' | 'note' | 'finish';\n\nexport interface TrajectoryStartFields {\n spawnId: string;\n specialist?: string;\n model: string;\n task: string;\n goalId?: string;\n toolNames?: string[];\n}\n\nexport interface TrajectoryRoundFields {\n roundNum: number;\n promptBytes: number;\n response: string;\n tokensUsed?: number;\n durationMs?: number;\n}\n\nexport interface TrajectoryToolCallFields {\n roundNum: number;\n callId: string;\n toolName: string;\n args: string;\n}\n\nexport interface TrajectoryToolResultFields {\n callId: string;\n content: string;\n error?: string;\n}\n\nexport interface TrajectoryNoteFields {\n text: string;\n}\n\nexport interface TrajectoryFinishFields {\n status: 'done' | 'failed' | 'needs_info' | 'blocked' | 'aborted';\n content: string;\n durationMs: number;\n totalRounds: number;\n toolsUsed: string[];\n}\n\nexport type TrajectoryEvent =\n | { kind: 'start'; at: number; spawnId: string; data: TrajectoryStartFields }\n | { kind: 'round'; at: number; spawnId: string; data: TrajectoryRoundFields }\n | { kind: 'tool_call'; at: number; spawnId: string; data: TrajectoryToolCallFields }\n | { kind: 'tool_result'; at: number; spawnId: string; data: TrajectoryToolResultFields }\n | { kind: 'note'; at: number; spawnId: string; data: TrajectoryNoteFields }\n | { kind: 'finish'; at: number; spawnId: string; data: TrajectoryFinishFields };\n\nexport interface TrajectoryHandle {\n spawnId: string;\n path: string;\n startedAt: number;\n disabled: boolean;\n}\n\n/* ─────────────────────────── Constants ─────────────────────────── */\n\nconst TRAJECTORY_DIR = join(TITAN_HOME, 'trajectories');\nconst MAX_ARGS_BYTES = 2 * 1024;\nconst MAX_RESULT_BYTES = 4 * 1024;\n\n/* ─────────────────────────── Path helpers ─────────────────────────── */\n\nfunction dayBucket(d = new Date()): string {\n return d.toISOString().slice(0, 10);\n}\n\n/** Resolves the canonical JSONL path for a spawn. Exposed so tests +\n * CLI tools can drill in without re-implementing the layout. */\nexport function trajectoryPathFor(spawnId: string, when: Date = new Date()): string {\n return join(TRAJECTORY_DIR, dayBucket(when), `${spawnId}.jsonl`);\n}\n\n/* ─────────────────────────── Opt-out ─────────────────────────── */\n\nfunction recordingEnabled(): boolean {\n return process.env.TITAN_TRAJECTORY_RECORD !== '0';\n}\n\n/* ─────────────────────────── Writer ─────────────────────────── */\n\nfunction writeEvent(path: string, event: TrajectoryEvent): void {\n try {\n const line = JSON.stringify(event) + '\\n';\n appendFileSync(path, line, 'utf-8');\n } catch { /* recorder MUST NEVER fail the actual spawn */ }\n}\n\n/* ─────────────────────────── Public API ─────────────────────────── */\n\n/**\n * Begin a trajectory. Creates the day-bucket directory on demand,\n * writes the `start` event, and returns a handle the caller threads\n * through subsequent record* calls.\n *\n * Cheap when recording is disabled — returns a no-op handle without\n * touching disk.\n */\nexport function startSpawnTrajectory(fields: TrajectoryStartFields): TrajectoryHandle {\n const startedAt = Date.now();\n if (!recordingEnabled()) {\n return { spawnId: fields.spawnId, path: '', startedAt, disabled: true };\n }\n const path = trajectoryPathFor(fields.spawnId);\n try {\n const dir = join(path, '..');\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n } catch { /* writeEvent will silently fail if dir creation didn't take */ }\n writeEvent(path, {\n kind: 'start',\n at: startedAt,\n spawnId: fields.spawnId,\n data: fields,\n });\n return { spawnId: fields.spawnId, path, startedAt, disabled: false };\n}\n\nexport function recordRound(handle: TrajectoryHandle, fields: TrajectoryRoundFields): void {\n if (handle.disabled) return;\n writeEvent(handle.path, {\n kind: 'round',\n at: Date.now(),\n spawnId: handle.spawnId,\n data: fields,\n });\n}\n\nexport function recordToolCall(handle: TrajectoryHandle, fields: TrajectoryToolCallFields): void {\n if (handle.disabled) return;\n writeEvent(handle.path, {\n kind: 'tool_call',\n at: Date.now(),\n spawnId: handle.spawnId,\n data: {\n ...fields,\n args: capString(fields.args, MAX_ARGS_BYTES),\n },\n });\n}\n\nexport function recordToolResult(handle: TrajectoryHandle, fields: TrajectoryToolResultFields): void {\n if (handle.disabled) return;\n writeEvent(handle.path, {\n kind: 'tool_result',\n at: Date.now(),\n spawnId: handle.spawnId,\n data: {\n ...fields,\n content: capString(fields.content, MAX_RESULT_BYTES),\n },\n });\n}\n\nexport function recordNote(handle: TrajectoryHandle, text: string): void {\n if (handle.disabled) return;\n writeEvent(handle.path, {\n kind: 'note',\n at: Date.now(),\n spawnId: handle.spawnId,\n data: { text },\n });\n}\n\n/** Close out the trajectory. Always called from the spawn's finally\n * block, so even an aborted spawn produces a terminal event. */\nexport function finishSpawnTrajectory(\n handle: TrajectoryHandle,\n fields: Omit<TrajectoryFinishFields, 'durationMs'>,\n): void {\n if (handle.disabled) return;\n writeEvent(handle.path, {\n kind: 'finish',\n at: Date.now(),\n spawnId: handle.spawnId,\n data: {\n ...fields,\n durationMs: Date.now() - handle.startedAt,\n },\n });\n}\n\n/* ─────────────────────────── Reader API ─────────────────────────── */\n\n/**\n * Load a single spawn's trajectory from disk. Returns the events in\n * insertion order, or null if the file is missing.\n *\n * Searches across all day-bucket directories — callers don't have to\n * know which day a spawn happened on.\n */\nexport function loadTrajectory(spawnId: string): TrajectoryEvent[] | null {\n if (!existsSync(TRAJECTORY_DIR)) return null;\n let days: string[];\n try { days = readdirSync(TRAJECTORY_DIR); } catch { return null; }\n for (const day of days) {\n const path = join(TRAJECTORY_DIR, day, `${spawnId}.jsonl`);\n if (existsSync(path)) {\n return readJsonl(path);\n }\n }\n return null;\n}\n\nexport interface TrajectoryListing {\n spawnId: string;\n startedAt: number;\n specialist?: string;\n model: string;\n goalId?: string;\n path: string;\n sizeBytes: number;\n}\n\n/**\n * List trajectory headers across all day-bucket directories. Used by\n * the trajectory list UI / CLI. Newest day first; pass `limit` to\n * cap the scan.\n */\nexport function listTrajectories(\n opts: { since?: number; limit?: number; goalId?: string } = {},\n): TrajectoryListing[] {\n if (!existsSync(TRAJECTORY_DIR)) return [];\n const out: TrajectoryListing[] = [];\n let days: string[];\n try { days = readdirSync(TRAJECTORY_DIR).sort().reverse(); } catch { return []; }\n for (const day of days) {\n const dayDir = join(TRAJECTORY_DIR, day);\n let files: string[];\n try { files = readdirSync(dayDir); } catch { continue; }\n for (const f of files) {\n if (!f.endsWith('.jsonl')) continue;\n const path = join(dayDir, f);\n const events = readJsonl(path);\n const start = events.find(e => e.kind === 'start');\n if (!start || start.kind !== 'start') continue;\n if (opts.since && start.at < opts.since) continue;\n if (opts.goalId && start.data.goalId !== opts.goalId) continue;\n try {\n const sizeBytes = statSync(path).size;\n out.push({\n spawnId: start.spawnId,\n startedAt: start.at,\n specialist: start.data.specialist,\n model: start.data.model,\n goalId: start.data.goalId,\n path,\n sizeBytes,\n });\n } catch { /* unreadable — skip */ }\n if (opts.limit && out.length >= opts.limit) return out;\n }\n }\n return out;\n}\n\n/* ─────────────────────────── Helpers ─────────────────────────── */\n\nfunction readJsonl(path: string): TrajectoryEvent[] {\n try {\n const raw = readFileSync(path, 'utf-8');\n return raw.split('\\n').filter(Boolean).map(line => JSON.parse(line) as TrajectoryEvent);\n } catch {\n return [];\n }\n}\n\nfunction capString(s: string, maxBytes: number): string {\n const bytes = Buffer.byteLength(s, 'utf-8');\n if (bytes <= maxBytes) return s;\n let cap = s;\n while (Buffer.byteLength(cap, 'utf-8') > maxBytes - 20) {\n cap = cap.slice(0, Math.floor(cap.length * 0.9));\n }\n return cap + `\\n…[truncated to ${maxBytes}b]`;\n}\n"],"mappings":"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","names":[]}