titan-agent 6.5.2 → 7.0.0

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 (311) hide show
  1. package/README.md +180 -53
  2. package/assets/agent-skills/NOTICE-ECC.md +39 -0
  3. package/assets/agent-skills/agent-introspection-debugging/SKILL.md +153 -0
  4. package/assets/agent-skills/continuous-agent-loop/SKILL.md +45 -0
  5. package/assets/agent-skills/continuous-learning/SKILL.md +131 -0
  6. package/assets/agent-skills/continuous-learning/config.json +18 -0
  7. package/assets/agent-skills/continuous-learning/evaluate-session.sh +69 -0
  8. package/assets/agent-skills/production-audit/SKILL.md +206 -0
  9. package/assets/agent-skills/recursive-decision-ledger/SKILL.md +79 -0
  10. package/assets/agent-skills/scientific-thinking-literature-review/SKILL.md +192 -0
  11. package/assets/agent-skills/scientific-thinking-scholar-evaluation/SKILL.md +160 -0
  12. package/assets/agent-skills/verification-loop/SKILL.md +126 -0
  13. package/dist/agent/agent.js +67 -26
  14. package/dist/agent/agent.js.map +1 -1
  15. package/dist/agent/agentLoop.js +6 -2
  16. package/dist/agent/agentLoop.js.map +1 -1
  17. package/dist/agent/autoModeClassifier.js +26 -2
  18. package/dist/agent/autoModeClassifier.js.map +1 -1
  19. package/dist/agent/goalDriver.js +2 -0
  20. package/dist/agent/goalDriver.js.map +1 -1
  21. package/dist/agent/goalSpec.js +229 -0
  22. package/dist/agent/goalSpec.js.map +1 -0
  23. package/dist/agent/heartbeat.js +130 -0
  24. package/dist/agent/heartbeat.js.map +1 -0
  25. package/dist/agent/missionDecompose.js +38 -0
  26. package/dist/agent/missionDecompose.js.map +1 -0
  27. package/dist/agent/muscleMemory.js +291 -0
  28. package/dist/agent/muscleMemory.js.map +1 -0
  29. package/dist/agent/observationWriter.js +56 -0
  30. package/dist/agent/observationWriter.js.map +1 -0
  31. package/dist/agent/promptSectionCaps.js +6 -0
  32. package/dist/agent/promptSectionCaps.js.map +1 -1
  33. package/dist/agent/reminders.js +101 -0
  34. package/dist/agent/reminders.js.map +1 -0
  35. package/dist/agent/systemWidgets.js +41 -0
  36. package/dist/agent/systemWidgets.js.map +1 -0
  37. package/dist/agent/toolRunner.js +24 -0
  38. package/dist/agent/toolRunner.js.map +1 -1
  39. package/dist/agent/toolSearch.js +3 -0
  40. package/dist/agent/toolSearch.js.map +1 -1
  41. package/dist/channels/messenger.js +39 -0
  42. package/dist/channels/messenger.js.map +1 -1
  43. package/dist/config/config.js +7 -0
  44. package/dist/config/config.js.map +1 -1
  45. package/dist/config/schema.js +18 -1
  46. package/dist/config/schema.js.map +1 -1
  47. package/dist/eval/harness.js +13 -3
  48. package/dist/eval/harness.js.map +1 -1
  49. package/dist/gateway/onboarding.js +91 -0
  50. package/dist/gateway/onboarding.js.map +1 -0
  51. package/dist/gateway/routes/skills.js +2 -1
  52. package/dist/gateway/routes/skills.js.map +1 -1
  53. package/dist/gateway/routes/studioRouter.js +88 -0
  54. package/dist/gateway/routes/studioRouter.js.map +1 -0
  55. package/dist/gateway/routes/watchRouter.js +4 -4
  56. package/dist/gateway/routes/watchRouter.js.map +1 -1
  57. package/dist/gateway/server.js +251 -43
  58. package/dist/gateway/server.js.map +1 -1
  59. package/dist/memory/taxonomy.js +117 -0
  60. package/dist/memory/taxonomy.js.map +1 -0
  61. package/dist/providers/modelCapabilities.js +13 -0
  62. package/dist/providers/modelCapabilities.js.map +1 -1
  63. package/dist/providers/ollama.js +8 -2
  64. package/dist/providers/ollama.js.map +1 -1
  65. package/dist/providers/openai.js +1 -1
  66. package/dist/providers/openai.js.map +1 -1
  67. package/dist/providers/openai_compat.js +38 -2
  68. package/dist/providers/openai_compat.js.map +1 -1
  69. package/dist/providers/router.js +14 -0
  70. package/dist/providers/router.js.map +1 -1
  71. package/dist/recipes/runner.js +8 -1
  72. package/dist/recipes/runner.js.map +1 -1
  73. package/dist/recipes/store.js +2 -1
  74. package/dist/recipes/store.js.map +1 -1
  75. package/dist/skills/builtin/codebase_explore.js +214 -0
  76. package/dist/skills/builtin/codebase_explore.js.map +1 -0
  77. package/dist/skills/builtin/config_audit.js +160 -0
  78. package/dist/skills/builtin/config_audit.js.map +1 -0
  79. package/dist/skills/builtin/delegate_agent.js +107 -0
  80. package/dist/skills/builtin/delegate_agent.js.map +1 -0
  81. package/dist/skills/builtin/memory_map.js +38 -0
  82. package/dist/skills/builtin/memory_map.js.map +1 -0
  83. package/dist/skills/builtin/reminders.js +67 -0
  84. package/dist/skills/builtin/reminders.js.map +1 -0
  85. package/dist/skills/builtin/spec.js +139 -0
  86. package/dist/skills/builtin/spec.js.map +1 -0
  87. package/dist/skills/builtin/widget_gallery.js +12 -5
  88. package/dist/skills/builtin/widget_gallery.js.map +1 -1
  89. package/dist/skills/frontmatterLoader.js +7 -1
  90. package/dist/skills/frontmatterLoader.js.map +1 -1
  91. package/dist/skills/registry.js +12 -0
  92. package/dist/skills/registry.js.map +1 -1
  93. package/dist/substrate/traceBus.js.map +1 -1
  94. package/dist/telemetry/traceStore.js +127 -0
  95. package/dist/telemetry/traceStore.js.map +1 -0
  96. package/dist/utils/constants.js +1 -1
  97. package/dist/utils/constants.js.map +1 -1
  98. package/dist/utils/contrast.js +69 -0
  99. package/dist/utils/contrast.js.map +1 -0
  100. package/dist/watch/sessionContext.js +14 -0
  101. package/dist/watch/sessionContext.js.map +1 -0
  102. package/dist/watch/sessionTrace.js +51 -0
  103. package/dist/watch/sessionTrace.js.map +1 -0
  104. package/dist/watch/studioBuffer.js +53 -0
  105. package/dist/watch/studioBuffer.js.map +1 -0
  106. package/package.json +4 -3
  107. package/ui/dist/assets/AboutYouMemory-CZKEOd_O.js +11 -0
  108. package/ui/dist/assets/AuditPanel-Bu71N5Er.js +1 -0
  109. package/ui/dist/assets/{AutonomyPanel-CmaXJV8d.js → AutonomyPanel-DB-39dX4.js} +2 -2
  110. package/ui/dist/assets/{AutopilotPanel-CbohPtXW.js → AutopilotPanel-Cj8oKxWv.js} +1 -1
  111. package/ui/dist/assets/{AutoresearchPanel-DCwiQ2v2.js → AutoresearchPanel-3s8pjm6Z.js} +2 -2
  112. package/ui/dist/assets/BackupPanel-EUQJMJE7.js +1 -0
  113. package/ui/dist/assets/{BrowserPanel-D6UaSxap.js → BrowserPanel-Beo4F5Ar.js} +2 -2
  114. package/ui/dist/assets/Button-MdXmb4_n.js +1 -0
  115. package/ui/dist/assets/CPActivity-B1Se9VVc.js +1 -0
  116. package/ui/dist/assets/CPAgentDetail-BYji6tT8.js +1 -0
  117. package/ui/dist/assets/CPAgents-BSUKucTe.js +1 -0
  118. package/ui/dist/assets/CPApprovals-B4v0em6e.js +1 -0
  119. package/ui/dist/assets/CPCosts-9ikh61Kv.js +1 -0
  120. package/ui/dist/assets/CPDashboard-sOok2T5v.js +1 -0
  121. package/ui/dist/assets/{CPFiles-BO73EZwJ.js → CPFiles-DB2nY5JE.js} +1 -1
  122. package/ui/dist/assets/CPGoals-8Z1yOY8e.js +1 -0
  123. package/ui/dist/assets/CPInbox-Cq6-UY8F.js +11 -0
  124. package/ui/dist/assets/CPIssueDetail-NAGfmJg8.js +1 -0
  125. package/ui/dist/assets/CPIssues-BzX6Rckx.js +1 -0
  126. package/ui/dist/assets/CPLayout-BcRM6QSg.js +2 -0
  127. package/ui/dist/assets/CPOrg-B7DQ4UQP.js +1 -0
  128. package/ui/dist/assets/CPRuns-D-cpPw_M.js +1 -0
  129. package/ui/dist/assets/{CPSocial-Qw5rKy_4.js → CPSocial-DshtmcK0.js} +3 -3
  130. package/ui/dist/assets/CPVoice-DfXRnQVU.js +1 -0
  131. package/ui/dist/assets/{ChannelsPanel-DS21E-Iz.js → ChannelsPanel-BnaTOeyf.js} +1 -1
  132. package/ui/dist/assets/CheckpointsPanel-C62naE4U.js +1 -0
  133. package/ui/dist/assets/CommandPostHub-B_L1CAss.js +24 -0
  134. package/ui/dist/assets/ConfirmDialog-VErHWAbo.js +1 -0
  135. package/ui/dist/assets/CronPanel-BwMrrZ5d.js +1 -0
  136. package/ui/dist/assets/{DataTable-PnhPeDBG.js → DataTable-BCqSUuN7.js} +1 -1
  137. package/ui/dist/assets/{DreamPanel-DIrtlk2K.js → DreamPanel-BNAcCLBB.js} +2 -2
  138. package/ui/dist/assets/EmptyState-D9kQf1PX.js +1 -0
  139. package/ui/dist/assets/{EvalHarnessPanel-uYb_ZEQG.js → EvalHarnessPanel-BaMLdBNm.js} +2 -2
  140. package/ui/dist/assets/EvalPanel-CW-SSjtg.js +1 -0
  141. package/ui/dist/assets/{FileViewer-CVO3TRs3.js → FileViewer-BL00nsd7.js} +1 -1
  142. package/ui/dist/assets/FilesPanel-CeC6YUSd.js +16 -0
  143. package/ui/dist/assets/FleetPanel-EyWF7l3W.js +1 -0
  144. package/ui/dist/assets/{HomelabPanel-BZvN4LPM.js → HomelabPanel-BRfKRYe7.js} +2 -2
  145. package/ui/dist/assets/InfraView-Jmqhg_Yc.js +2 -0
  146. package/ui/dist/assets/{InlineEditableField-mBzpbcLB.js → InlineEditableField-DgMrLDj1.js} +1 -1
  147. package/ui/dist/assets/Input-CBTwa_Iw.js +1 -0
  148. package/ui/dist/assets/IntegrationsPanel-DQL-7Ec6.js +1 -0
  149. package/ui/dist/assets/IntelligenceView-DxD78qfP.js +2 -0
  150. package/ui/dist/assets/LearningPanel-C_JzSbJU.js +1 -0
  151. package/ui/dist/assets/LiveStudio-Db5yeaWo.js +7 -0
  152. package/ui/dist/assets/{LogsPanel-DbP3Mwnr.js → LogsPanel-CpinKwaZ.js} +1 -1
  153. package/ui/dist/assets/McpPanel-D-MqN_f-.js +1 -0
  154. package/ui/dist/assets/MemoryGraphPanel-BnD5jRqf.js +1 -0
  155. package/ui/dist/assets/MemoryTaxonomyPanel-BBI202dd.js +6 -0
  156. package/ui/dist/assets/MemoryWikiPanel-Dn3XZHL0.js +6 -0
  157. package/ui/dist/assets/{MeshPanel-Bf-4brXL.js → MeshPanel-C-mRJpr0.js} +1 -1
  158. package/ui/dist/assets/{MissionCanvas-CJyhnauG.js → MissionCanvas-CbZW83Ch.js} +1 -1
  159. package/ui/dist/assets/{MissionChat-C2Nto3zM.js → MissionChat-Bs75jPAD.js} +1 -1
  160. package/ui/dist/assets/{MissionLibrary-RXyBHsLL.js → MissionLibrary-BxQ6SxPe.js} +1 -1
  161. package/ui/dist/assets/MissionStart-CBuBPasl.js +1 -0
  162. package/ui/dist/assets/Modal-ybPGvlQs.js +9 -0
  163. package/ui/dist/assets/{NvidiaPanel-4DlcQlyl.js → NvidiaPanel-DdG13OX4.js} +1 -1
  164. package/ui/dist/assets/OrganismPanel-xZWUJKyr.js +1 -0
  165. package/ui/dist/assets/OverviewPanel-B45ZYhJV.js +1 -0
  166. package/ui/dist/assets/{PageHeader-bnc85wQN.js → PageHeader-DdrbS1aq.js} +1 -1
  167. package/ui/dist/assets/PersonaProfilesPanel-CKER-EU5.js +8 -0
  168. package/ui/dist/assets/{PersonasPanel-dR8VppcD.js → PersonasPanel-CMMQ3nh3.js} +1 -1
  169. package/ui/dist/assets/{PhoneDeskPanel-CdVOY7H9.js → PhoneDeskPanel-Bw_Y-cVE.js} +2 -2
  170. package/ui/dist/assets/RecipesPanel-DzMcxpJJ.js +1 -0
  171. package/ui/dist/assets/SecurityPanel-BCq3Fzbr.js +1 -0
  172. package/ui/dist/assets/SelfImprovePanel-YE3hT8jK.js +1 -0
  173. package/ui/dist/assets/SelfProposalsPanel-DXuqBM6p.js +1 -0
  174. package/ui/dist/assets/SessionsPanel-DORz5goR.js +1 -0
  175. package/ui/dist/assets/SessionsTab-qYHDOuC8.js +1 -0
  176. package/ui/dist/assets/SettingsPanel-BbwPu1i7.js +1 -0
  177. package/ui/dist/assets/SettingsView-zgqkDDBJ.js +2 -0
  178. package/ui/dist/assets/{SkeletonLoader-Cax7YTqW.js → SkeletonLoader-r6OGXaU9.js} +1 -1
  179. package/ui/dist/assets/SkillsPanel-BjEOBMRo.js +11 -0
  180. package/ui/dist/assets/{SomaView-eG3wI4S1.js → SomaView-DLzApZVW.js} +2 -2
  181. package/ui/dist/assets/{StatCard-Djr487DV.js → StatCard-DLq8aBps.js} +1 -1
  182. package/ui/dist/assets/{StatusBadge-JpCoA33_.js → StatusBadge-BqlvIbdj.js} +1 -1
  183. package/ui/dist/assets/{Tabs-DJHu-roP.js → Tabs-DdUThlp1.js} +1 -1
  184. package/ui/dist/assets/TeamsPanel-uIIKoU7I.js +1 -0
  185. package/ui/dist/assets/{TelemetryPanel-D4PXsd-X.js → TelemetryPanel-dVr3yQQr.js} +1 -1
  186. package/ui/dist/assets/TitanCanvas-xHAJqwG2.js +945 -0
  187. package/ui/dist/assets/ToolsView-CG1mZdiy.js +2 -0
  188. package/ui/dist/assets/{Tooltip-B1LhTXJ8.js → Tooltip-C2tXJcrN.js} +1 -1
  189. package/ui/dist/assets/TraceViewer-BIMD_Osr.js +1 -0
  190. package/ui/dist/assets/TracesPanel-nMUiyMj5.js +1 -0
  191. package/ui/dist/assets/TrainingPanel-CT5R4HWm.js +1 -0
  192. package/ui/dist/assets/{VoiceOverlay-B_ss7Zg_.js → VoiceOverlay-Br4QnCtw.js} +3 -3
  193. package/ui/dist/assets/VramPanel-CdnJYytg.js +1 -0
  194. package/ui/dist/assets/WatchView-TNU6Ns1Q.js +13 -0
  195. package/ui/dist/assets/WorkTab-wTlX_UKP.js +1 -0
  196. package/ui/dist/assets/WorkflowBuilderPanel-CDpVt5ux.js +6 -0
  197. package/ui/dist/assets/WorkflowsPanel-zsFqsLdn.js +11 -0
  198. package/ui/dist/assets/{archive-gZFnGd5A.js → archive-Bh689DNg.js} +1 -1
  199. package/ui/dist/assets/{arrow-left-qA0Qer2h.js → arrow-left-WkVYLm80.js} +1 -1
  200. package/ui/dist/assets/{book-open-DIxdPqmD.js → book-open-ohEXgUsS.js} +1 -1
  201. package/ui/dist/assets/{bot-CvIF1uHi.js → bot-DvEcyQcD.js} +1 -1
  202. package/ui/dist/assets/{chart-column-BQiM-Td4.js → chart-column-w3goFd5f.js} +1 -1
  203. package/ui/dist/assets/{check-DgyDEFtq.js → check-CuRYh4Zh.js} +1 -1
  204. package/ui/dist/assets/{chevron-right-CQ1NTOtd.js → chevron-right-DbLSHmff.js} +1 -1
  205. package/ui/dist/assets/{circle-check-big-CAFcaPPH.js → circle-check-big-Bkba568b.js} +1 -1
  206. package/ui/dist/assets/{circle-stop-C-8xGv0n.js → circle-stop-Bx2ohEg8.js} +1 -1
  207. package/ui/dist/assets/{clipboard-list-9DdqBj1F.js → clipboard-list-Dj9tORnu.js} +1 -1
  208. package/ui/dist/assets/{clock-CF9BCIpL.js → clock-JyQAYy6X.js} +1 -1
  209. package/ui/dist/assets/{dollar-sign-LDRZBae6.js → dollar-sign-XxwPAA2a.js} +1 -1
  210. package/ui/dist/assets/{download-BBNXvBO0.js → download-D1izVEOL.js} +1 -1
  211. package/ui/dist/assets/{external-link-D_ji4Bec.js → external-link-DZ_rZsIt.js} +1 -1
  212. package/ui/dist/assets/{file-text-DrdTDEYp.js → file-text-Cfawu8Bs.js} +1 -1
  213. package/ui/dist/assets/{flask-conical-BGn5PgFX.js → flask-conical-Ql8QxhgY.js} +1 -1
  214. package/ui/dist/assets/{folder-BeZZUGnK.js → folder-DAdIw-Pb.js} +1 -1
  215. package/ui/dist/assets/{funnel-CAJgGO5M.js → funnel-FycfZcXo.js} +1 -1
  216. package/ui/dist/assets/{git-branch-BcXabeOs.js → git-branch-C2qrhGGu.js} +1 -1
  217. package/ui/dist/assets/index-Cml2x9pA.js +507 -0
  218. package/ui/dist/assets/index-yWybhcfI.css +1 -0
  219. package/ui/dist/assets/{layers-DxYDmPA_.js → layers-yzjtGlHB.js} +1 -1
  220. package/ui/dist/assets/{legacy-dHWI7k8P.js → legacy-D21WSMF1.js} +1 -1
  221. package/ui/dist/assets/{lightbulb-B0Du4SM4.js → lightbulb-BZJNK69S.js} +1 -1
  222. package/ui/dist/assets/{link-CUpaumqr.js → link-CnFkBkaY.js} +1 -1
  223. package/ui/dist/assets/{list-todo-CDDhmPQB.js → list-todo-BlgfQHup.js} +1 -1
  224. package/ui/dist/assets/{message-square-D5VWlv-Q.js → message-square-CRsG9u6_.js} +1 -1
  225. package/ui/dist/assets/{mic-Ddcc6Ann.js → mic-B32RHc7Y.js} +1 -1
  226. package/ui/dist/assets/{missions-2zox16Tc.js → missions-1iw8Z2_0.js} +1 -1
  227. package/ui/dist/assets/{network-DaJ7ugwX.js → network-C9RXQGrV.js} +1 -1
  228. package/ui/dist/assets/{pause-BukaK6rs.js → pause-BT1Bc2q3.js} +1 -1
  229. package/ui/dist/assets/{pencil-D_YwQBeg.js → pencil-BK5IU6o0.js} +1 -1
  230. package/ui/dist/assets/{play-BjdwtYMk.js → play-Cl1nC_gc.js} +1 -1
  231. package/ui/dist/assets/{plug-yISDdeW2.js → plug-DvcpOalj.js} +1 -1
  232. package/ui/dist/assets/{plus-BSjTdepW.js → plus-Dpo1qfrl.js} +1 -1
  233. package/ui/dist/assets/{radio-B7kX_yWE.js → radio-C5c9IyqX.js} +1 -1
  234. package/ui/dist/assets/{refresh-cw-CwDpzXUn.js → refresh-cw-C-iZkzuS.js} +1 -1
  235. package/ui/dist/assets/{rotate-ccw-7SQDTI0_.js → rotate-ccw-qzrB3SNw.js} +1 -1
  236. package/ui/dist/assets/{save-CjTPAQM0.js → save-DwR9wQVu.js} +1 -1
  237. package/ui/dist/assets/{search-BP7VlNxX.js → search-D10l0m3-.js} +1 -1
  238. package/ui/dist/assets/{send-C2XDuRPf.js → send-DtXGsaFW.js} +1 -1
  239. package/ui/dist/assets/{server-BE8W-AT4.js → server-BzisF3LL.js} +1 -1
  240. package/ui/dist/assets/settings-Dbwd1_-c.js +6 -0
  241. package/ui/dist/assets/{shield-check-s7la2J2K.js → shield-check-CXlDJyAW.js} +1 -1
  242. package/ui/dist/assets/square-BBC-JKzY.js +11 -0
  243. package/ui/dist/assets/{terminal-B8IK4WsO.js → terminal-DaBIlWEM.js} +1 -1
  244. package/ui/dist/assets/{test-tube-Xfn9DJeg.js → test-tube-CDATRD5t.js} +1 -1
  245. package/ui/dist/assets/{toggle-right-CHpzOIlj.js → toggle-right-DTmrC5RD.js} +1 -1
  246. package/ui/dist/assets/{trash-2-B1C85ePK.js → trash-2-CTeyjxD_.js} +1 -1
  247. package/ui/dist/assets/{trending-up-DDGHQZY1.js → trending-up-CXPRjRV4.js} +1 -1
  248. package/ui/dist/assets/{trophy-D0LhJSPA.js → trophy-DbVdXRtU.js} +1 -1
  249. package/ui/dist/assets/{volume-2-DMaE91b5.js → volume-2-DYDBmm3M.js} +1 -1
  250. package/ui/dist/assets/{wifi-off-DR9h8HGA.js → wifi-off-BBuIGbiP.js} +1 -1
  251. package/ui/dist/assets/zoom-out-D47_pRAO.js +16 -0
  252. package/ui/dist/index.html +2 -2
  253. package/ui/dist/sw.js +1 -1
  254. package/ui/dist/assets/AuditPanel-BaftX5MP.js +0 -1
  255. package/ui/dist/assets/BackupPanel-DAy9G7Wt.js +0 -1
  256. package/ui/dist/assets/CPActivity-BYDsUoMn.js +0 -1
  257. package/ui/dist/assets/CPAgentDetail-Bb9-GAJY.js +0 -1
  258. package/ui/dist/assets/CPAgents-BTZuGnki.js +0 -1
  259. package/ui/dist/assets/CPApprovals-BME2ARkO.js +0 -1
  260. package/ui/dist/assets/CPCosts-mOXwWq0i.js +0 -1
  261. package/ui/dist/assets/CPDashboard-CTlKGXXQ.js +0 -1
  262. package/ui/dist/assets/CPGoals-BNWhNP14.js +0 -1
  263. package/ui/dist/assets/CPInbox-ybe4r8LW.js +0 -16
  264. package/ui/dist/assets/CPIssueDetail-DaMMGbjw.js +0 -1
  265. package/ui/dist/assets/CPIssues-BgpQOISw.js +0 -1
  266. package/ui/dist/assets/CPLayout-bgErauTx.js +0 -2
  267. package/ui/dist/assets/CPOrg-C8l5SIWJ.js +0 -8
  268. package/ui/dist/assets/CPRuns-DB8S7loF.js +0 -1
  269. package/ui/dist/assets/CPVoice-DfwHPewA.js +0 -1
  270. package/ui/dist/assets/CheckpointsPanel-9-l_8GdG.js +0 -1
  271. package/ui/dist/assets/CommandPostHub-z55Yrhj1.js +0 -24
  272. package/ui/dist/assets/CronPanel-kEZK3gpx.js +0 -1
  273. package/ui/dist/assets/EmptyState-CfvU7SdB.js +0 -1
  274. package/ui/dist/assets/EvalPanel-CDG-njyj.js +0 -1
  275. package/ui/dist/assets/FilesPanel-CaLPog2e.js +0 -16
  276. package/ui/dist/assets/FleetPanel-BtlaV6pH.js +0 -1
  277. package/ui/dist/assets/InfraView-C0zEuFrR.js +0 -2
  278. package/ui/dist/assets/Input-CbrMbLgn.js +0 -1
  279. package/ui/dist/assets/IntegrationsPanel-B4TyR6jC.js +0 -1
  280. package/ui/dist/assets/IntelligenceView-DZ4HxYZ3.js +0 -2
  281. package/ui/dist/assets/LearningPanel-B4RIm_iQ.js +0 -1
  282. package/ui/dist/assets/McpPanel-BmNUFozc.js +0 -1
  283. package/ui/dist/assets/MemoryGraphPanel-JPurHV0L.js +0 -16
  284. package/ui/dist/assets/MemoryWikiPanel-DdvCAZHa.js +0 -6
  285. package/ui/dist/assets/MissionStart-wUkcEP0T.js +0 -1
  286. package/ui/dist/assets/Modal-BmS7C6FN.js +0 -1
  287. package/ui/dist/assets/OrganismPanel-C2LYDPrn.js +0 -1
  288. package/ui/dist/assets/OverviewPanel-DK8uuq4D.js +0 -1
  289. package/ui/dist/assets/PersonaProfilesPanel-DGtokXUu.js +0 -8
  290. package/ui/dist/assets/RecipesPanel-D3Zs2wxr.js +0 -1
  291. package/ui/dist/assets/SecurityPanel-BMERf796.js +0 -1
  292. package/ui/dist/assets/SelfImprovePanel-BxqWHXCU.js +0 -1
  293. package/ui/dist/assets/SelfProposalsPanel-CuC94vdx.js +0 -2
  294. package/ui/dist/assets/SessionsPanel-CQ8qvzp7.js +0 -1
  295. package/ui/dist/assets/SessionsTab-B3eHV3jf.js +0 -1
  296. package/ui/dist/assets/SettingsPanel-Cumls7N2.js +0 -1
  297. package/ui/dist/assets/SettingsView-BtA67pol.js +0 -2
  298. package/ui/dist/assets/SkillsPanel-CA415CT8.js +0 -11
  299. package/ui/dist/assets/TeamsPanel-C_Vu6vnY.js +0 -1
  300. package/ui/dist/assets/TitanCanvas-TuYZSZO7.js +0 -1117
  301. package/ui/dist/assets/ToolsView-BvTN6vFn.js +0 -2
  302. package/ui/dist/assets/TraceViewer-Bh8Jo3Dr.js +0 -1
  303. package/ui/dist/assets/TrainingPanel-Dmbd1rJ-.js +0 -1
  304. package/ui/dist/assets/VramPanel-CPICbZ2s.js +0 -1
  305. package/ui/dist/assets/WatchView-BCgLVfiV.js +0 -13
  306. package/ui/dist/assets/WorkTab-iptofXO9.js +0 -1
  307. package/ui/dist/assets/WorkflowsPanel-3RYkFHyf.js +0 -11
  308. package/ui/dist/assets/chevron-up-UyfdjRp6.js +0 -6
  309. package/ui/dist/assets/index-CUgepw5Q.js +0 -337
  310. package/ui/dist/assets/index-Dm6zcSTS.css +0 -1
  311. package/ui/dist/assets/proxy-CWET5FFX.js +0 -9
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env node
2
+ const IMPERATIVE_RE = /\b(?:add|open|show|pin|create|launch|put|display|view|bring\s+up|give\s+me|i\s+want|let'?s\s+see|i\s+need)\b/i;
3
+ const DISPLAY_RE = /\b(?:show|open|display|view|pin|launch|bring\s+up|pull\s+up|let'?s\s+see)\b/i;
4
+ const WIDGET_NOUN_RE = /\b(?:widget|panel|dashboard|hub|gallery|kitchen|scheduler|monitor)\b/i;
5
+ const NOT_WIDGET_RE = /\b(?:files?|contents?|text|code|source|readme|directory|folder|logs?|\.[a-z]{2,4}\b)\b/i;
6
+ const SYSTEM_WIDGET_PATTERNS = [
7
+ { pattern: /\b(?:backups?|snapshots?|archives?)\b/i, source: "system:backup", name: "Backup Manager" },
8
+ { pattern: /\b(?:training|train|specialists?|models?)\b/i, source: "system:training", name: "Training Dashboard" },
9
+ { pattern: /\b(?:recipes?|playbooks?|workflows?|jarvis)\b/i, source: "system:recipes", name: "Recipe Kitchen" },
10
+ { pattern: /\b(?:vram|gpu|nvidia)\b/i, source: "system:vram", name: "VRAM Monitor" },
11
+ { pattern: /\b(?:teams?|members?|roles?|permissions?|rbac)\b/i, source: "system:teams", name: "Team Hub" },
12
+ { pattern: /\b(?:cron|schedules?|jobs?|timers?)\b/i, source: "system:cron", name: "Cron Scheduler" },
13
+ { pattern: /\b(?:checkpoints?|restores?|save\s+state)\b/i, source: "system:checkpoints", name: "Checkpoints" },
14
+ { pattern: /\b(?:organism|guardrails?)\b/i, source: "system:organism", name: "Organism Monitor" },
15
+ { pattern: /\b(?:fleet|nodes?|routes?|mesh)\b/i, source: "system:fleet", name: "Fleet Router" },
16
+ { pattern: /\b(?:browser|captcha|form\s+fill|web\s+automation)\b/i, source: "system:browser", name: "Browser Tools" },
17
+ { pattern: /\b(?:flaky|failing|coverage|test\s+lab|evals?)\b/i, source: "system:eval", name: "Test Lab" }
18
+ ];
19
+ function detectSystemWidget(message) {
20
+ if (!message || typeof message !== "string") return null;
21
+ if (NOT_WIDGET_RE.test(message)) return null;
22
+ const wordCount = message.trim().split(/\s+/).filter(Boolean).length;
23
+ const hasWidgetIntent = (
24
+ // (a) explicit widget-noun anywhere ("show the training dashboard").
25
+ IMPERATIVE_RE.test(message) && WIDGET_NOUN_RE.test(message) || // (b) a SHORT display command that names a system widget ("show backup").
26
+ DISPLAY_RE.test(message) && wordCount <= 6
27
+ );
28
+ if (!hasWidgetIntent) return null;
29
+ const m = SYSTEM_WIDGET_PATTERNS.find((p) => p.pattern.test(message));
30
+ return m ? { source: m.source, name: m.name } : null;
31
+ }
32
+ function buildSystemWidgetGate(w, size = { w: 6, h: 6 }) {
33
+ return `_____widget
34
+ { "name": "${w.name}", "format": "system", "source": "${w.source}", "w": ${size.w}, "h": ${size.h} }`;
35
+ }
36
+ export {
37
+ SYSTEM_WIDGET_PATTERNS,
38
+ buildSystemWidgetGate,
39
+ detectSystemWidget
40
+ };
41
+ //# sourceMappingURL=systemWidgets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/agent/systemWidgets.ts"],"sourcesContent":["/**\n * TITAN — System-widget shortcut detection + deterministic gate emission.\n *\n * Why this exists (model-agnostic)\n * --------------------------------\n * Short commands like \"show backup\" / \"show recipes\" should render a built-in\n * system widget by emitting a `_____widget` gate in the reply. Relying on the\n * LLM to emit that gate is fragile: weaker models read \"show backup\" as \"list\n * backups\" and call a tool (e.g. `backup_list`) instead of emitting the gate —\n * so the widget never appears. A model-AGNOSTIC harness must not depend on the\n * model getting this right.\n *\n * This module centralizes the detection (previously inline + too strict in\n * agent.ts — it required a generic widget-noun, so \"show backup\" never matched)\n * and the canonical gate text, so the agent can:\n * 1. inject a strong instruction when a shortcut is detected (agent.ts), AND\n * 2. GUARANTEE the gate is in the final reply even if the model ignored it\n * (agent.ts post-loop), regardless of which model is driving.\n */\n\nexport interface SystemWidgetMatch {\n /** `system:xxx` source reference for the built-in panel. */\n source: string;\n /** Human-readable widget name. */\n name: string;\n}\n\n/** Broad imperative — paired with an explicit widget-noun (e.g. \"create a panel\"). */\nconst IMPERATIVE_RE = /\\b(?:add|open|show|pin|create|launch|put|display|view|bring\\s+up|give\\s+me|i\\s+want|let'?s\\s+see|i\\s+need)\\b/i;\n/** DISPLAY verbs only — used for the short-command path, where there's no\n * widget-noun to disambiguate. \"show/open/view\" clearly mean \"render it\";\n * \"create/add\" are excluded there because they often mean an ACTION. */\nconst DISPLAY_RE = /\\b(?:show|open|display|view|pin|launch|bring\\s+up|pull\\s+up|let'?s\\s+see)\\b/i;\n/** Generic widget nouns — a strong signal even in a longer sentence. */\nconst WIDGET_NOUN_RE = /\\b(?:widget|panel|dashboard|hub|gallery|kitchen|scheduler|monitor)\\b/i;\n/** File / read / code indicators — these are NOT widget requests even though\n * they share verbs+keywords (\"show me the backup file contents\"). */\nconst NOT_WIDGET_RE = /\\b(?:files?|contents?|text|code|source|readme|directory|folder|logs?|\\.[a-z]{2,4}\\b)\\b/i;\n\n/** Specific system widgets + the keywords that name them. Order = priority. */\nexport const SYSTEM_WIDGET_PATTERNS: Array<{ pattern: RegExp; source: string; name: string }> = [\n { pattern: /\\b(?:backups?|snapshots?|archives?)\\b/i, source: 'system:backup', name: 'Backup Manager' },\n { pattern: /\\b(?:training|train|specialists?|models?)\\b/i, source: 'system:training', name: 'Training Dashboard' },\n { pattern: /\\b(?:recipes?|playbooks?|workflows?|jarvis)\\b/i, source: 'system:recipes', name: 'Recipe Kitchen' },\n { pattern: /\\b(?:vram|gpu|nvidia)\\b/i, source: 'system:vram', name: 'VRAM Monitor' },\n { pattern: /\\b(?:teams?|members?|roles?|permissions?|rbac)\\b/i, source: 'system:teams', name: 'Team Hub' },\n { pattern: /\\b(?:cron|schedules?|jobs?|timers?)\\b/i, source: 'system:cron', name: 'Cron Scheduler' },\n { pattern: /\\b(?:checkpoints?|restores?|save\\s+state)\\b/i, source: 'system:checkpoints', name: 'Checkpoints' },\n { pattern: /\\b(?:organism|guardrails?)\\b/i, source: 'system:organism', name: 'Organism Monitor' },\n { pattern: /\\b(?:fleet|nodes?|routes?|mesh)\\b/i, source: 'system:fleet', name: 'Fleet Router' },\n { pattern: /\\b(?:browser|captcha|form\\s+fill|web\\s+automation)\\b/i, source: 'system:browser', name: 'Browser Tools' },\n { pattern: /\\b(?:flaky|failing|coverage|test\\s+lab|evals?)\\b/i, source: 'system:eval', name: 'Test Lab' },\n];\n\n/**\n * Detect a system-widget shortcut request. Returns the matched widget or null.\n *\n * Fires when the message has an imperative verb AND either:\n * (a) a generic widget-noun (e.g. \"show the training dashboard\"), OR\n * (b) it's a SHORT command (≤6 words) that names a specific system widget\n * (e.g. \"show backup\", \"show recipes\") — the case the old inline check\n * missed because it demanded a generic widget-noun.\n *\n * The short-command guard prevents hijacking longer task sentences (\"show me\n * how to back up the database to S3\" → not a widget request).\n */\nexport function detectSystemWidget(message: string): SystemWidgetMatch | null {\n if (!message || typeof message !== 'string') return null;\n // A file/read/code request is never a widget shortcut, even if it shares a\n // verb + keyword (\"show me the backup file contents\").\n if (NOT_WIDGET_RE.test(message)) return null;\n const wordCount = message.trim().split(/\\s+/).filter(Boolean).length;\n const hasWidgetIntent =\n // (a) explicit widget-noun anywhere (\"show the training dashboard\").\n (IMPERATIVE_RE.test(message) && WIDGET_NOUN_RE.test(message)) ||\n // (b) a SHORT display command that names a system widget (\"show backup\").\n (DISPLAY_RE.test(message) && wordCount <= 6);\n if (!hasWidgetIntent) return null;\n const m = SYSTEM_WIDGET_PATTERNS.find((p) => p.pattern.test(message));\n return m ? { source: m.source, name: m.name } : null;\n}\n\n/** Build the canonical `_____widget` gate text for a system widget. */\nexport function buildSystemWidgetGate(w: SystemWidgetMatch, size: { w: number; h: number } = { w: 6, h: 6 }): string {\n return `_____widget\\n{ \"name\": \"${w.name}\", \"format\": \"system\", \"source\": \"${w.source}\", \"w\": ${size.w}, \"h\": ${size.h} }`;\n}\n"],"mappings":";AA4BA,MAAM,gBAAgB;AAItB,MAAM,aAAa;AAEnB,MAAM,iBAAiB;AAGvB,MAAM,gBAAgB;AAGf,MAAM,yBAAmF;AAAA,EAC5F,EAAE,SAAS,0CAA0C,QAAQ,iBAAiB,MAAM,iBAAiB;AAAA,EACrG,EAAE,SAAS,gDAAgD,QAAQ,mBAAmB,MAAM,qBAAqB;AAAA,EACjH,EAAE,SAAS,kDAAkD,QAAQ,kBAAkB,MAAM,iBAAiB;AAAA,EAC9G,EAAE,SAAS,4BAA4B,QAAQ,eAAe,MAAM,eAAe;AAAA,EACnF,EAAE,SAAS,qDAAqD,QAAQ,gBAAgB,MAAM,WAAW;AAAA,EACzG,EAAE,SAAS,0CAA0C,QAAQ,eAAe,MAAM,iBAAiB;AAAA,EACnG,EAAE,SAAS,gDAAgD,QAAQ,sBAAsB,MAAM,cAAc;AAAA,EAC7G,EAAE,SAAS,iCAAiC,QAAQ,mBAAmB,MAAM,mBAAmB;AAAA,EAChG,EAAE,SAAS,sCAAsC,QAAQ,gBAAgB,MAAM,eAAe;AAAA,EAC9F,EAAE,SAAS,yDAAyD,QAAQ,kBAAkB,MAAM,gBAAgB;AAAA,EACpH,EAAE,SAAS,qDAAqD,QAAQ,eAAe,MAAM,WAAW;AAC5G;AAcO,SAAS,mBAAmB,SAA2C;AAC1E,MAAI,CAAC,WAAW,OAAO,YAAY,SAAU,QAAO;AAGpD,MAAI,cAAc,KAAK,OAAO,EAAG,QAAO;AACxC,QAAM,YAAY,QAAQ,KAAK,EAAE,MAAM,KAAK,EAAE,OAAO,OAAO,EAAE;AAC9D,QAAM;AAAA;AAAA,IAED,cAAc,KAAK,OAAO,KAAK,eAAe,KAAK,OAAO;AAAA,IAE1D,WAAW,KAAK,OAAO,KAAK,aAAa;AAAA;AAC9C,MAAI,CAAC,gBAAiB,QAAO;AAC7B,QAAM,IAAI,uBAAuB,KAAK,CAAC,MAAM,EAAE,QAAQ,KAAK,OAAO,CAAC;AACpE,SAAO,IAAI,EAAE,QAAQ,EAAE,QAAQ,MAAM,EAAE,KAAK,IAAI;AACpD;AAGO,SAAS,sBAAsB,GAAsB,OAAiC,EAAE,GAAG,GAAG,GAAG,EAAE,GAAW;AACjH,SAAO;AAAA,aAA2B,EAAE,IAAI,qCAAqC,EAAE,MAAM,WAAW,KAAK,CAAC,UAAU,KAAK,CAAC;AAC1H;","names":[]}
@@ -25,6 +25,7 @@ import { scanCommand, scanURL } from "../security/preExecScan.js";
25
25
  import { runPreToolShellHooks, runPostToolShellHooks } from "../hooks/shellHooks.js";
26
26
  import { createCheckpoint } from "../checkpoint/manager.js";
27
27
  import { mintActionId } from "../receipts/mint.js";
28
+ import { emitToolCall, emitToolResult } from "../watch/sessionTrace.js";
28
29
  import { writeReceipt } from "../receipts/store.js";
29
30
  function computeUnifiedDiff(filePath, oldContent, newContent) {
30
31
  if (oldContent === newContent) return `// No changes to ${filePath}`;
@@ -165,10 +166,15 @@ function writeToolCallReceipt(params) {
165
166
  } catch {
166
167
  }
167
168
  }
169
+ function filePathFromDiff(diff) {
170
+ const m = diff.match(/^--- (.+)$/m) || diff.match(/No changes to (.+?)\s*$/m);
171
+ return m ? m[1].trim() : void 0;
172
+ }
168
173
  async function executeTool(toolCall, channel) {
169
174
  const _aid = mintActionId();
170
175
  const _t0 = Date.now();
171
176
  const argInfo = summarizeToolArguments(toolCall.function.arguments);
177
+ emitToolCall({ tool: toolCall.function.name, argsPreview: argInfo.summary, actionId: _aid });
172
178
  try {
173
179
  const result = await executeToolInner(toolCall, channel);
174
180
  writeToolCallReceipt({
@@ -181,6 +187,16 @@ async function executeTool(toolCall, channel) {
181
187
  argKeys: argInfo.argKeys,
182
188
  error: result.success ? void 0 : result.content
183
189
  });
190
+ emitToolResult({
191
+ tool: toolCall.function.name,
192
+ success: result.success,
193
+ durationMs: Date.now() - _t0,
194
+ actionId: _aid,
195
+ resultPreview: typeof result.content === "string" ? result.content.slice(0, 300) : void 0,
196
+ // Live Studio diff stream — present for file-modifying tools.
197
+ diff: result.diff,
198
+ filePath: result.diff ? filePathFromDiff(result.diff) : void 0
199
+ });
184
200
  return result;
185
201
  } catch (err) {
186
202
  writeToolCallReceipt({
@@ -193,6 +209,13 @@ async function executeTool(toolCall, channel) {
193
209
  argKeys: argInfo.argKeys,
194
210
  error: err.message
195
211
  });
212
+ emitToolResult({
213
+ tool: toolCall.function.name,
214
+ success: false,
215
+ durationMs: Date.now() - _t0,
216
+ actionId: _aid,
217
+ resultPreview: err.message?.slice(0, 300)
218
+ });
196
219
  throw err;
197
220
  }
198
221
  }
@@ -869,6 +892,7 @@ export {
869
892
  classifyError,
870
893
  executeTool,
871
894
  executeTools,
895
+ filePathFromDiff,
872
896
  getRegisteredTools,
873
897
  getToolDefinitions,
874
898
  isDataUrlProducingTool,
@@ -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 {\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';\nimport { mintActionId } from '../receipts/mint.js';\nimport { writeReceipt } from '../receipts/store.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 */\nfunction safeReceiptText(text: string, maxChars: number): string {\n return scanAndRedactPII(redactSecrets(text)).replace(/\\s+/g, ' ').trim().slice(0, maxChars);\n}\n\nfunction summarizeToolArguments(rawArgs: string | undefined): { summary: string; argKeys?: string[] } {\n if (!rawArgs?.trim()) return { summary: 'args=none' };\n try {\n const parsed = JSON.parse(rawArgs) as unknown;\n if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {\n const allKeys = Object.keys(parsed).sort();\n const visibleKeys = allKeys\n .slice(0, 8)\n .map((key) => safeReceiptText(key, 32))\n .filter(Boolean);\n if (visibleKeys.length === 0) return { summary: 'args=empty', argKeys: [] };\n const suffix = allKeys.length > visibleKeys.length ? ` +${allKeys.length - visibleKeys.length}` : '';\n return { summary: `args=${visibleKeys.join(',')}${suffix}`, argKeys: visibleKeys };\n }\n return { summary: `args=${Array.isArray(parsed) ? 'array' : typeof parsed}` };\n } catch {\n return { summary: 'args=invalid-json' };\n }\n}\n\nfunction writeToolCallReceipt(params: {\n actionId: string;\n toolName: string;\n status: 'ok' | 'fail';\n channel?: string;\n durationMs: number;\n argSummary: string;\n argKeys?: string[];\n error?: string;\n}): void {\n try {\n const safeError = params.error ? safeReceiptText(params.error, 200) : undefined;\n writeReceipt({\n action_id: params.actionId,\n kind: 'tool_call',\n summary: safeReceiptText(`${params.toolName} ${params.argSummary}`, 200),\n status: params.status,\n channel: params.channel,\n duration_ms: params.durationMs,\n meta: params.status === 'fail'\n ? { ...(params.argKeys ? { arg_keys: params.argKeys } : {}), ...(safeError ? { error: safeError } : {}) }\n : (params.argKeys ? { arg_keys: params.argKeys } : undefined),\n });\n } catch {\n /* receipt writes are best-effort and must not affect tool behavior */\n }\n}\n\nexport async function executeTool(toolCall: ToolCall, channel?: string): Promise<ToolResult> {\n const _aid = mintActionId();\n const _t0 = Date.now();\n const argInfo = summarizeToolArguments(toolCall.function.arguments);\n try {\n const result = await executeToolInner(toolCall, channel);\n writeToolCallReceipt({\n actionId: _aid,\n toolName: toolCall.function.name,\n status: result.success ? 'ok' : 'fail',\n channel,\n durationMs: Date.now() - _t0,\n argSummary: argInfo.summary,\n argKeys: argInfo.argKeys,\n error: result.success ? undefined : result.content,\n });\n return result;\n } catch (err) {\n writeToolCallReceipt({\n actionId: _aid,\n toolName: toolCall.function.name,\n status: 'fail',\n channel,\n durationMs: Date.now() - _t0,\n argSummary: argInfo.summary,\n argKeys: argInfo.argKeys,\n error: (err as Error).message,\n });\n throw err;\n }\n}\n\nasync function executeToolInner(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\n/**\n * v6.5 — would this tool trip the approval gate? Mirrors the gate decision made\n * inside executeTool (classifier 'gate' OR the user's explicit approval list)\n * with no side effects. Used by executeTools to decide whether a multi-tool\n * batch must run sequentially so a gated call cannot be bypassed by its siblings.\n */\nasync function toolWouldGate(name: string): Promise<boolean> {\n try {\n if (classifyToolCall(name).decision === 'gate') return true;\n } catch { /* classifier unavailable — fall through */ }\n try {\n const { requiresApproval } = await import('../skills/builtin/approval_gates.js');\n return requiresApproval(name);\n } catch { /* approval module unavailable — fail-open probe */ }\n return false;\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 // v6.5 — approval-gate sibling protection. The per-tool approval gate (in\n // executeTool) only defers the GATED call. Without this, ungated siblings in\n // the same assistant turn execute in parallel BEFORE the agent loop notices\n // the pending approval — so a model (or prompt injection) could co-schedule a\n // destructive gated tool (e.g. shell `rm -rf`) alongside benign calls and have\n // the benign ones run unreviewed. When the batch contains any potentially\n // gated tool, run SEQUENTIALLY and stop at the first call that actually pends\n // approval, holding the remaining siblings unexecuted. Stays correct across\n // the approve→resume cycle: an already-approved tool simply executes (not\n // pending) and the loop continues to its siblings.\n const maybeGated = (await Promise.all(\n toolCalls.map(tc => toolWouldGate(tc.function.name)),\n )).some(Boolean);\n if (maybeGated) {\n const gatedResults: ToolResult[] = [];\n let paused = false;\n for (const tc of toolCalls) {\n if (paused) {\n gatedResults.push({\n toolCallId: tc.id,\n name: tc.function.name,\n content: 'Deferred: held until the human resolves the pending approval for a tool requested in the same turn. Re-issue this call after approval.',\n success: false,\n durationMs: 0,\n });\n continue;\n }\n const r = await executeTool(tc, channel);\n gatedResults.push(r);\n if (r.approvalPending) paused = true;\n }\n return applyOutputCaps(gatedResults);\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<ToolResult> => {\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 return executeTool(syntheticTc, channel);\n };\n\n const parallelResults = await executeToolsParallel<ToolResult>(parallelCalls, executor);\n\n // Map back to ToolResult format with full metadata, then apply output cap.\n const mapped: ToolResult[] = parallelResults.map(pr => ({\n ...pr.content,\n toolCallId: pr.toolCallId,\n name: pr.name,\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';\nimport { mintActionId } from '../receipts/mint.js';\nimport { emitToolCall, emitToolResult } from '../watch/sessionTrace.js';\nimport { writeReceipt } from '../receipts/store.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 */\nfunction safeReceiptText(text: string, maxChars: number): string {\n return scanAndRedactPII(redactSecrets(text)).replace(/\\s+/g, ' ').trim().slice(0, maxChars);\n}\n\nfunction summarizeToolArguments(rawArgs: string | undefined): { summary: string; argKeys?: string[] } {\n if (!rawArgs?.trim()) return { summary: 'args=none' };\n try {\n const parsed = JSON.parse(rawArgs) as unknown;\n if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {\n const allKeys = Object.keys(parsed).sort();\n const visibleKeys = allKeys\n .slice(0, 8)\n .map((key) => safeReceiptText(key, 32))\n .filter(Boolean);\n if (visibleKeys.length === 0) return { summary: 'args=empty', argKeys: [] };\n const suffix = allKeys.length > visibleKeys.length ? ` +${allKeys.length - visibleKeys.length}` : '';\n return { summary: `args=${visibleKeys.join(',')}${suffix}`, argKeys: visibleKeys };\n }\n return { summary: `args=${Array.isArray(parsed) ? 'array' : typeof parsed}` };\n } catch {\n return { summary: 'args=invalid-json' };\n }\n}\n\nfunction writeToolCallReceipt(params: {\n actionId: string;\n toolName: string;\n status: 'ok' | 'fail';\n channel?: string;\n durationMs: number;\n argSummary: string;\n argKeys?: string[];\n error?: string;\n}): void {\n try {\n const safeError = params.error ? safeReceiptText(params.error, 200) : undefined;\n writeReceipt({\n action_id: params.actionId,\n kind: 'tool_call',\n summary: safeReceiptText(`${params.toolName} ${params.argSummary}`, 200),\n status: params.status,\n channel: params.channel,\n duration_ms: params.durationMs,\n meta: params.status === 'fail'\n ? { ...(params.argKeys ? { arg_keys: params.argKeys } : {}), ...(safeError ? { error: safeError } : {}) }\n : (params.argKeys ? { arg_keys: params.argKeys } : undefined),\n });\n } catch {\n /* receipt writes are best-effort and must not affect tool behavior */\n }\n}\n\n/** Recover the target path from a computeUnifiedDiff header for the live view. */\nexport function filePathFromDiff(diff: string): string | undefined {\n const m = diff.match(/^--- (.+)$/m) || diff.match(/No changes to (.+?)\\s*$/m);\n return m ? m[1].trim() : undefined;\n}\n\nexport async function executeTool(toolCall: ToolCall, channel?: string): Promise<ToolResult> {\n const _aid = mintActionId();\n const _t0 = Date.now();\n const argInfo = summarizeToolArguments(toolCall.function.arguments);\n // Live-agents event spine: this is the universal fallback PRODUCER for the\n // tool:call/tool:result topics (deduped by actionId against any richer\n // agentLoop emit). Additive + never throws to the caller.\n emitToolCall({ tool: toolCall.function.name, argsPreview: argInfo.summary, actionId: _aid });\n try {\n const result = await executeToolInner(toolCall, channel);\n writeToolCallReceipt({\n actionId: _aid,\n toolName: toolCall.function.name,\n status: result.success ? 'ok' : 'fail',\n channel,\n durationMs: Date.now() - _t0,\n argSummary: argInfo.summary,\n argKeys: argInfo.argKeys,\n error: result.success ? undefined : result.content,\n });\n emitToolResult({\n tool: toolCall.function.name,\n success: result.success,\n durationMs: Date.now() - _t0,\n actionId: _aid,\n resultPreview: typeof result.content === 'string' ? result.content.slice(0, 300) : undefined,\n // Live Studio diff stream — present for file-modifying tools.\n diff: result.diff,\n filePath: result.diff ? filePathFromDiff(result.diff) : undefined,\n });\n return result;\n } catch (err) {\n writeToolCallReceipt({\n actionId: _aid,\n toolName: toolCall.function.name,\n status: 'fail',\n channel,\n durationMs: Date.now() - _t0,\n argSummary: argInfo.summary,\n argKeys: argInfo.argKeys,\n error: (err as Error).message,\n });\n emitToolResult({\n tool: toolCall.function.name,\n success: false,\n durationMs: Date.now() - _t0,\n actionId: _aid,\n resultPreview: (err as Error).message?.slice(0, 300),\n });\n throw err;\n }\n}\n\nasync function executeToolInner(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\n/**\n * v6.5 — would this tool trip the approval gate? Mirrors the gate decision made\n * inside executeTool (classifier 'gate' OR the user's explicit approval list)\n * with no side effects. Used by executeTools to decide whether a multi-tool\n * batch must run sequentially so a gated call cannot be bypassed by its siblings.\n */\nasync function toolWouldGate(name: string): Promise<boolean> {\n try {\n if (classifyToolCall(name).decision === 'gate') return true;\n } catch { /* classifier unavailable — fall through */ }\n try {\n const { requiresApproval } = await import('../skills/builtin/approval_gates.js');\n return requiresApproval(name);\n } catch { /* approval module unavailable — fail-open probe */ }\n return false;\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 // v6.5 — approval-gate sibling protection. The per-tool approval gate (in\n // executeTool) only defers the GATED call. Without this, ungated siblings in\n // the same assistant turn execute in parallel BEFORE the agent loop notices\n // the pending approval — so a model (or prompt injection) could co-schedule a\n // destructive gated tool (e.g. shell `rm -rf`) alongside benign calls and have\n // the benign ones run unreviewed. When the batch contains any potentially\n // gated tool, run SEQUENTIALLY and stop at the first call that actually pends\n // approval, holding the remaining siblings unexecuted. Stays correct across\n // the approve→resume cycle: an already-approved tool simply executes (not\n // pending) and the loop continues to its siblings.\n const maybeGated = (await Promise.all(\n toolCalls.map(tc => toolWouldGate(tc.function.name)),\n )).some(Boolean);\n if (maybeGated) {\n const gatedResults: ToolResult[] = [];\n let paused = false;\n for (const tc of toolCalls) {\n if (paused) {\n gatedResults.push({\n toolCallId: tc.id,\n name: tc.function.name,\n content: 'Deferred: held until the human resolves the pending approval for a tool requested in the same turn. Re-issue this call after approval.',\n success: false,\n durationMs: 0,\n });\n continue;\n }\n const r = await executeTool(tc, channel);\n gatedResults.push(r);\n if (r.approvalPending) paused = true;\n }\n return applyOutputCaps(gatedResults);\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<ToolResult> => {\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 return executeTool(syntheticTc, channel);\n };\n\n const parallelResults = await executeToolsParallel<ToolResult>(parallelCalls, executor);\n\n // Map back to ToolResult format with full metadata, then apply output cap.\n const mapped: ToolResult[] = parallelResults.map(pr => ({\n ...pr.content,\n toolCallId: pr.toolCallId,\n name: pr.name,\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"]}
@@ -14,6 +14,9 @@ const DEFAULT_CORE_TOOLS = [
14
14
  "smart_form_fill",
15
15
  "weather",
16
16
  "memory",
17
+ // v7.0: "remind me at X" is a core personal-assistant verb — models were
18
+ // fabricating reminders precisely because this tool wasn't in their view.
19
+ "reminder",
17
20
  "system_info",
18
21
  "goal_list",
19
22
  "spawn_agent",
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/agent/toolSearch.ts"],"sourcesContent":["/**\n * TITAN — Tool Search\n *\n * Meta-tool that lets the LLM discover tools on demand instead of seeing\n * all 80+ tool schemas on every request. Reduces input tokens by 60-80%.\n *\n * How it works:\n * 1. Only core tools + tool_search are sent to the LLM (5-8 tools, ~600 tokens)\n * 2. tool_search description includes a compact catalog of ALL available tools\n * 3. When the LLM calls tool_search, matching tools are returned\n * 4. Agent loop adds discovered tools to activeTools for subsequent rounds\n *\n * Inspired by Anthropic's tool search pattern (2025) but works with ALL providers.\n */\nimport { getRegisteredTools, type ToolHandler } from './toolRunner.js';\nimport logger from '../utils/logger.js';\n\nconst COMPONENT = 'ToolSearch';\n\n/** Default core tools — always sent to the LLM without needing search */\nexport const DEFAULT_CORE_TOOLS = [\n 'shell',\n 'read_file',\n 'write_file',\n 'edit_file',\n 'list_dir',\n 'web_search',\n 'web_fetch',\n 'web_act',\n 'smart_form_fill',\n 'weather',\n 'memory',\n 'system_info',\n 'goal_list',\n 'spawn_agent',\n 'tool_search',\n 'tool_expand',\n 'self_doctor',\n 'ha_control',\n 'ha_devices',\n 'ha_status',\n 'gallery_search',\n 'gallery_get',\n 'gallery_list',\n // v6.0.2 — Canvas widget tools must always be visible so the model\n // reaches for them on \"build me X\" prompts (the v6.0 thesis). Before\n // this, only gallery_search was in core; create_widget was only\n // discoverable via tool_search, which the model rarely called for\n // novel asks like \"build me a solar system\" — it'd fall through to\n // shell + mkdir instead. These four tools are cheap (~80 tokens\n // combined) and they're load-bearing for the Living Canvas pitch.\n 'create_widget',\n 'update_widget',\n 'remove_widget',\n 'list_active_widgets',\n // Spaces tools — same logic: workspaces are a v6.0 first-class concept,\n // the model needs to know it can create / switch / list them by name.\n 'create_space',\n 'switch_space',\n 'list_spaces',\n];\n\n/** Build a compact one-line catalog of all tools for the tool_search description */\nexport function buildToolCatalog(): string {\n const tools = getRegisteredTools();\n return tools\n .filter(t => t.name !== 'tool_search')\n .map(t => `${t.name}: ${t.description.slice(0, 50)}`)\n .join(' | ');\n}\n\n/**\n * Search registered tools by keyword query.\n *\n * Progressive disclosure mode (Hermes competitive gap fix):\n * When metadataOnly=true, returns tools with truncated descriptions\n * and no parameter schemas — ~20 tokens each instead of ~200.\n * The model then calls tool_expand(name) to get the full schema\n * for just the tools it needs.\n */\nexport function searchTools(query: string, metadataOnly = false): ToolHandler[] {\n const tools = getRegisteredTools();\n const terms = query.toLowerCase().split(/\\s+/).filter(Boolean);\n\n const scored = tools\n .filter(t => t.name !== 'tool_search' && t.name !== 'tool_expand')\n .map(t => {\n const text = `${t.name} ${t.description}`.toLowerCase();\n const score = terms.reduce((s, term) => s + (text.includes(term) ? 1 : 0), 0);\n return { tool: t, score };\n })\n .filter(({ score }) => score > 0)\n .sort((a, b) => b.score - a.score)\n .slice(0, 8);\n\n if (metadataOnly) {\n // Return lightweight metadata — just name + one-line description\n return scored.map(({ tool }) => ({\n ...tool,\n description: tool.description.slice(0, 80),\n parameters: { type: 'object' as const, properties: {}, required: [] },\n }));\n }\n\n return scored.map(({ tool }) => tool);\n}\n\n/**\n * Expand a single tool's full schema by name.\n * Part of the progressive disclosure pattern — model calls tool_search\n * to get names, then tool_expand to get the full schema for just the\n * tools it needs.\n */\nexport function expandTool(name: string): ToolHandler | null {\n const tools = getRegisteredTools();\n return tools.find(t => t.name === name) || null;\n}\n\n/** Get the tool_search tool handler */\nexport function getToolSearchHandler(): ToolHandler {\n const catalog = buildToolCatalog();\n\n return {\n name: 'tool_search',\n description: `Search for tools by keyword to discover capabilities. Call this FIRST when you need a tool not in your current list. Available tools: ${catalog}`,\n parameters: {\n type: 'object',\n properties: {\n query: {\n type: 'string',\n description: 'Search query — keywords describing what you need (e.g. \"email send\", \"browser screenshot\", \"github pr\", \"cron schedule\")',\n },\n },\n required: ['query'],\n },\n execute: async (args: Record<string, unknown>) => {\n const query = (args.query as string) || '';\n if (!query.trim()) {\n return 'Please provide a search query. Example: tool_search({query: \"email\"})';\n }\n\n const results = searchTools(query);\n\n if (results.length === 0) {\n return `No tools found matching \"${query}\". Try broader keywords.`;\n }\n\n logger.info(COMPONENT, `Search \"${query}\" → ${results.length} tools: [${results.map(t => t.name).join(', ')}]`);\n\n const formatted = results.map(t => {\n const params = t.parameters as Record<string, unknown>;\n const props = (params.properties || {}) as Record<string, unknown>;\n const paramNames = Object.keys(props).join(', ');\n return `**${t.name}**(${paramNames}): ${t.description}`;\n }).join('\\n');\n\n return `Found ${results.length} tools:\\n${formatted}\\n\\nYou can now call these tools directly.`;\n },\n };\n}\n\n/**\n * Get the tool_expand handler — progressive disclosure (Hermes gap fix).\n * Returns the full JSON schema for a single tool by name.\n */\nexport function getToolExpandHandler(): ToolHandler {\n return {\n name: 'tool_expand',\n description: 'Get the full parameter schema for a specific tool. Use after tool_search to see the detailed parameters before calling the tool.',\n parameters: {\n type: 'object',\n properties: {\n name: {\n type: 'string',\n description: 'The exact tool name to expand (e.g. \"email_send\", \"freelance_search\")',\n },\n },\n required: ['name'],\n },\n execute: async (args: Record<string, unknown>) => {\n const name = (args.name as string) || '';\n if (!name.trim()) {\n return 'Please provide a tool name. Example: tool_expand({name: \"email_send\"})';\n }\n\n const tool = expandTool(name);\n if (!tool) {\n return `Tool \"${name}\" not found. Use tool_search to find the correct name.`;\n }\n\n logger.info(COMPONENT, `Expand \"${name}\" → full schema loaded`);\n\n const params = tool.parameters as Record<string, unknown>;\n const props = (params.properties || {}) as Record<string, { type?: string; description?: string }>;\n const required = (params.required || []) as string[];\n\n const paramDocs = Object.entries(props).map(([key, val]) => {\n const req = required.includes(key) ? ' (required)' : '';\n return ` - **${key}**${req}: ${val.description || val.type || 'any'}`;\n }).join('\\n');\n\n return `## ${tool.name}\\n${tool.description}\\n\\n### Parameters\\n${paramDocs || ' (no parameters)'}\\n\\nYou can now call ${tool.name}() with these parameters.`;\n },\n };\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/agent/toolSearch.ts"],"sourcesContent":["/**\n * TITAN — Tool Search\n *\n * Meta-tool that lets the LLM discover tools on demand instead of seeing\n * all 80+ tool schemas on every request. Reduces input tokens by 60-80%.\n *\n * How it works:\n * 1. Only core tools + tool_search are sent to the LLM (5-8 tools, ~600 tokens)\n * 2. tool_search description includes a compact catalog of ALL available tools\n * 3. When the LLM calls tool_search, matching tools are returned\n * 4. Agent loop adds discovered tools to activeTools for subsequent rounds\n *\n * Inspired by Anthropic's tool search pattern (2025) but works with ALL providers.\n */\nimport { getRegisteredTools, type ToolHandler } from './toolRunner.js';\nimport logger from '../utils/logger.js';\n\nconst COMPONENT = 'ToolSearch';\n\n/** Default core tools — always sent to the LLM without needing search */\nexport const DEFAULT_CORE_TOOLS = [\n 'shell',\n 'read_file',\n 'write_file',\n 'edit_file',\n 'list_dir',\n 'web_search',\n 'web_fetch',\n 'web_act',\n 'smart_form_fill',\n 'weather',\n 'memory',\n // v7.0: \"remind me at X\" is a core personal-assistant verb — models were\n // fabricating reminders precisely because this tool wasn't in their view.\n 'reminder',\n 'system_info',\n 'goal_list',\n 'spawn_agent',\n 'tool_search',\n 'tool_expand',\n 'self_doctor',\n 'ha_control',\n 'ha_devices',\n 'ha_status',\n 'gallery_search',\n 'gallery_get',\n 'gallery_list',\n // v6.0.2 — Canvas widget tools must always be visible so the model\n // reaches for them on \"build me X\" prompts (the v6.0 thesis). Before\n // this, only gallery_search was in core; create_widget was only\n // discoverable via tool_search, which the model rarely called for\n // novel asks like \"build me a solar system\" — it'd fall through to\n // shell + mkdir instead. These four tools are cheap (~80 tokens\n // combined) and they're load-bearing for the Living Canvas pitch.\n 'create_widget',\n 'update_widget',\n 'remove_widget',\n 'list_active_widgets',\n // Spaces tools — same logic: workspaces are a v6.0 first-class concept,\n // the model needs to know it can create / switch / list them by name.\n 'create_space',\n 'switch_space',\n 'list_spaces',\n];\n\n/** Build a compact one-line catalog of all tools for the tool_search description */\nexport function buildToolCatalog(): string {\n const tools = getRegisteredTools();\n return tools\n .filter(t => t.name !== 'tool_search')\n .map(t => `${t.name}: ${t.description.slice(0, 50)}`)\n .join(' | ');\n}\n\n/**\n * Search registered tools by keyword query.\n *\n * Progressive disclosure mode (Hermes competitive gap fix):\n * When metadataOnly=true, returns tools with truncated descriptions\n * and no parameter schemas — ~20 tokens each instead of ~200.\n * The model then calls tool_expand(name) to get the full schema\n * for just the tools it needs.\n */\nexport function searchTools(query: string, metadataOnly = false): ToolHandler[] {\n const tools = getRegisteredTools();\n const terms = query.toLowerCase().split(/\\s+/).filter(Boolean);\n\n const scored = tools\n .filter(t => t.name !== 'tool_search' && t.name !== 'tool_expand')\n .map(t => {\n const text = `${t.name} ${t.description}`.toLowerCase();\n const score = terms.reduce((s, term) => s + (text.includes(term) ? 1 : 0), 0);\n return { tool: t, score };\n })\n .filter(({ score }) => score > 0)\n .sort((a, b) => b.score - a.score)\n .slice(0, 8);\n\n if (metadataOnly) {\n // Return lightweight metadata — just name + one-line description\n return scored.map(({ tool }) => ({\n ...tool,\n description: tool.description.slice(0, 80),\n parameters: { type: 'object' as const, properties: {}, required: [] },\n }));\n }\n\n return scored.map(({ tool }) => tool);\n}\n\n/**\n * Expand a single tool's full schema by name.\n * Part of the progressive disclosure pattern — model calls tool_search\n * to get names, then tool_expand to get the full schema for just the\n * tools it needs.\n */\nexport function expandTool(name: string): ToolHandler | null {\n const tools = getRegisteredTools();\n return tools.find(t => t.name === name) || null;\n}\n\n/** Get the tool_search tool handler */\nexport function getToolSearchHandler(): ToolHandler {\n const catalog = buildToolCatalog();\n\n return {\n name: 'tool_search',\n description: `Search for tools by keyword to discover capabilities. Call this FIRST when you need a tool not in your current list. Available tools: ${catalog}`,\n parameters: {\n type: 'object',\n properties: {\n query: {\n type: 'string',\n description: 'Search query — keywords describing what you need (e.g. \"email send\", \"browser screenshot\", \"github pr\", \"cron schedule\")',\n },\n },\n required: ['query'],\n },\n execute: async (args: Record<string, unknown>) => {\n const query = (args.query as string) || '';\n if (!query.trim()) {\n return 'Please provide a search query. Example: tool_search({query: \"email\"})';\n }\n\n const results = searchTools(query);\n\n if (results.length === 0) {\n return `No tools found matching \"${query}\". Try broader keywords.`;\n }\n\n logger.info(COMPONENT, `Search \"${query}\" → ${results.length} tools: [${results.map(t => t.name).join(', ')}]`);\n\n const formatted = results.map(t => {\n const params = t.parameters as Record<string, unknown>;\n const props = (params.properties || {}) as Record<string, unknown>;\n const paramNames = Object.keys(props).join(', ');\n return `**${t.name}**(${paramNames}): ${t.description}`;\n }).join('\\n');\n\n return `Found ${results.length} tools:\\n${formatted}\\n\\nYou can now call these tools directly.`;\n },\n };\n}\n\n/**\n * Get the tool_expand handler — progressive disclosure (Hermes gap fix).\n * Returns the full JSON schema for a single tool by name.\n */\nexport function getToolExpandHandler(): ToolHandler {\n return {\n name: 'tool_expand',\n description: 'Get the full parameter schema for a specific tool. Use after tool_search to see the detailed parameters before calling the tool.',\n parameters: {\n type: 'object',\n properties: {\n name: {\n type: 'string',\n description: 'The exact tool name to expand (e.g. \"email_send\", \"freelance_search\")',\n },\n },\n required: ['name'],\n },\n execute: async (args: Record<string, unknown>) => {\n const name = (args.name as string) || '';\n if (!name.trim()) {\n return 'Please provide a tool name. Example: tool_expand({name: \"email_send\"})';\n }\n\n const tool = expandTool(name);\n if (!tool) {\n return `Tool \"${name}\" not found. Use tool_search to find the correct name.`;\n }\n\n logger.info(COMPONENT, `Expand \"${name}\" → full schema loaded`);\n\n const params = tool.parameters as Record<string, unknown>;\n const props = (params.properties || {}) as Record<string, { type?: string; description?: string }>;\n const required = (params.required || []) as string[];\n\n const paramDocs = Object.entries(props).map(([key, val]) => {\n const req = required.includes(key) ? ' (required)' : '';\n return ` - **${key}**${req}: ${val.description || val.type || 'any'}`;\n }).join('\\n');\n\n return `## ${tool.name}\\n${tool.description}\\n\\n### Parameters\\n${paramDocs || ' (no parameters)'}\\n\\nYou can now call ${tool.name}() with these parameters.`;\n },\n };\n}\n"],"mappings":";AAcA,SAAS,0BAA4C;AACrD,OAAO,YAAY;AAEnB,MAAM,YAAY;AAGX,MAAM,qBAAqB;AAAA,EAC9B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AACJ;AAGO,SAAS,mBAA2B;AACvC,QAAM,QAAQ,mBAAmB;AACjC,SAAO,MACF,OAAO,OAAK,EAAE,SAAS,aAAa,EACpC,IAAI,OAAK,GAAG,EAAE,IAAI,KAAK,EAAE,YAAY,MAAM,GAAG,EAAE,CAAC,EAAE,EACnD,KAAK,KAAK;AACnB;AAWO,SAAS,YAAY,OAAe,eAAe,OAAsB;AAC5E,QAAM,QAAQ,mBAAmB;AACjC,QAAM,QAAQ,MAAM,YAAY,EAAE,MAAM,KAAK,EAAE,OAAO,OAAO;AAE7D,QAAM,SAAS,MACV,OAAO,OAAK,EAAE,SAAS,iBAAiB,EAAE,SAAS,aAAa,EAChE,IAAI,OAAK;AACN,UAAM,OAAO,GAAG,EAAE,IAAI,IAAI,EAAE,WAAW,GAAG,YAAY;AACtD,UAAM,QAAQ,MAAM,OAAO,CAAC,GAAG,SAAS,KAAK,KAAK,SAAS,IAAI,IAAI,IAAI,IAAI,CAAC;AAC5E,WAAO,EAAE,MAAM,GAAG,MAAM;AAAA,EAC5B,CAAC,EACA,OAAO,CAAC,EAAE,MAAM,MAAM,QAAQ,CAAC,EAC/B,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK,EAChC,MAAM,GAAG,CAAC;AAEf,MAAI,cAAc;AAEd,WAAO,OAAO,IAAI,CAAC,EAAE,KAAK,OAAO;AAAA,MAC7B,GAAG;AAAA,MACH,aAAa,KAAK,YAAY,MAAM,GAAG,EAAE;AAAA,MACzC,YAAY,EAAE,MAAM,UAAmB,YAAY,CAAC,GAAG,UAAU,CAAC,EAAE;AAAA,IACxE,EAAE;AAAA,EACN;AAEA,SAAO,OAAO,IAAI,CAAC,EAAE,KAAK,MAAM,IAAI;AACxC;AAQO,SAAS,WAAW,MAAkC;AACzD,QAAM,QAAQ,mBAAmB;AACjC,SAAO,MAAM,KAAK,OAAK,EAAE,SAAS,IAAI,KAAK;AAC/C;AAGO,SAAS,uBAAoC;AAChD,QAAM,UAAU,iBAAiB;AAEjC,SAAO;AAAA,IACH,MAAM;AAAA,IACN,aAAa,yIAAyI,OAAO;AAAA,IAC7J,YAAY;AAAA,MACR,MAAM;AAAA,MACN,YAAY;AAAA,QACR,OAAO;AAAA,UACH,MAAM;AAAA,UACN,aAAa;AAAA,QACjB;AAAA,MACJ;AAAA,MACA,UAAU,CAAC,OAAO;AAAA,IACtB;AAAA,IACA,SAAS,OAAO,SAAkC;AAC9C,YAAM,QAAS,KAAK,SAAoB;AACxC,UAAI,CAAC,MAAM,KAAK,GAAG;AACf,eAAO;AAAA,MACX;AAEA,YAAM,UAAU,YAAY,KAAK;AAEjC,UAAI,QAAQ,WAAW,GAAG;AACtB,eAAO,4BAA4B,KAAK;AAAA,MAC5C;AAEA,aAAO,KAAK,WAAW,WAAW,KAAK,YAAO,QAAQ,MAAM,YAAY,QAAQ,IAAI,OAAK,EAAE,IAAI,EAAE,KAAK,IAAI,CAAC,GAAG;AAE9G,YAAM,YAAY,QAAQ,IAAI,OAAK;AAC/B,cAAM,SAAS,EAAE;AACjB,cAAM,QAAS,OAAO,cAAc,CAAC;AACrC,cAAM,aAAa,OAAO,KAAK,KAAK,EAAE,KAAK,IAAI;AAC/C,eAAO,KAAK,EAAE,IAAI,MAAM,UAAU,MAAM,EAAE,WAAW;AAAA,MACzD,CAAC,EAAE,KAAK,IAAI;AAEZ,aAAO,SAAS,QAAQ,MAAM;AAAA,EAAY,SAAS;AAAA;AAAA;AAAA,IACvD;AAAA,EACJ;AACJ;AAMO,SAAS,uBAAoC;AAChD,SAAO;AAAA,IACH,MAAM;AAAA,IACN,aAAa;AAAA,IACb,YAAY;AAAA,MACR,MAAM;AAAA,MACN,YAAY;AAAA,QACR,MAAM;AAAA,UACF,MAAM;AAAA,UACN,aAAa;AAAA,QACjB;AAAA,MACJ;AAAA,MACA,UAAU,CAAC,MAAM;AAAA,IACrB;AAAA,IACA,SAAS,OAAO,SAAkC;AAC9C,YAAM,OAAQ,KAAK,QAAmB;AACtC,UAAI,CAAC,KAAK,KAAK,GAAG;AACd,eAAO;AAAA,MACX;AAEA,YAAM,OAAO,WAAW,IAAI;AAC5B,UAAI,CAAC,MAAM;AACP,eAAO,SAAS,IAAI;AAAA,MACxB;AAEA,aAAO,KAAK,WAAW,WAAW,IAAI,6BAAwB;AAE9D,YAAM,SAAS,KAAK;AACpB,YAAM,QAAS,OAAO,cAAc,CAAC;AACrC,YAAM,WAAY,OAAO,YAAY,CAAC;AAEtC,YAAM,YAAY,OAAO,QAAQ,KAAK,EAAE,IAAI,CAAC,CAAC,KAAK,GAAG,MAAM;AACxD,cAAM,MAAM,SAAS,SAAS,GAAG,IAAI,gBAAgB;AACrD,eAAO,SAAS,GAAG,KAAK,GAAG,KAAK,IAAI,eAAe,IAAI,QAAQ,KAAK;AAAA,MACxE,CAAC,EAAE,KAAK,IAAI;AAEZ,aAAO,MAAM,KAAK,IAAI;AAAA,EAAK,KAAK,WAAW;AAAA;AAAA;AAAA,EAAuB,aAAa,mBAAmB;AAAA;AAAA,mBAAwB,KAAK,IAAI;AAAA,IACvI;AAAA,EACJ;AACJ;","names":[]}