@xopcai/xopc 0.0.122 → 0.0.124

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 (273) hide show
  1. package/README.md +28 -11
  2. package/README.zh-CN.md +28 -11
  3. package/dist/browser-ext/manifest.json +1 -1
  4. package/dist/extensions/telegram/xopc.extension.json +1 -1
  5. package/dist/gateway/static/root/assets/{agent-avatar-dicebear-CK3s5cJo.js → agent-avatar-dicebear-B5uSVAyJ.js} +1 -1
  6. package/dist/gateway/static/root/assets/agents-BjIxt67Z.js +2 -0
  7. package/dist/gateway/static/root/assets/{app-management-settings-panel-CWsNHtTs.js → app-management-settings-panel-CYCONLIL.js} +1 -1
  8. package/dist/gateway/static/root/assets/{appearance-settings-C4b8y6iZ.js → appearance-settings-Clw3ryKg.js} +1 -1
  9. package/dist/gateway/static/root/assets/apps-page-QICTeGjX.js +1 -0
  10. package/dist/gateway/static/root/assets/{archive-plugin-DAtNW3Ia.js → archive-plugin-CXUGRp2i.js} +1 -1
  11. package/dist/gateway/static/root/assets/{attachment-load-Dm-RY54y.js → attachment-load-Cmiub3Wg.js} +1 -1
  12. package/dist/gateway/static/root/assets/{automation-suggestion-card-BKHewSVT.js → automation-suggestion-card-CU0-4nQ-.js} +1 -1
  13. package/dist/gateway/static/root/assets/automations-page-Car-jtiz.js +1 -0
  14. package/dist/gateway/static/root/assets/{binary-plugins-BG6Xnk71.js → binary-plugins-0Izz-N3p.js} +1 -1
  15. package/dist/gateway/static/root/assets/{block-editor-DfndiO3h.js → block-editor-C1Kv0IkJ.js} +28 -28
  16. package/dist/gateway/static/root/assets/browser-settings-page-CMeBJzlS.js +8 -0
  17. package/dist/gateway/static/root/assets/{capability-presets-api-Cx93GLdw.js → capability-presets-api-BmQTM9Il.js} +1 -1
  18. package/dist/gateway/static/root/assets/capability-presets-settings-panel-DZEJEoCF.js +1 -0
  19. package/dist/gateway/static/root/assets/{channel-recipient-api-DdPTtEs8.js → channel-recipient-api-BeA2aVor.js} +1 -1
  20. package/dist/gateway/static/root/assets/channels-settings-kkW-xFau.js +1 -0
  21. package/dist/gateway/static/root/assets/connectors-page-BvaQvO5F.js +2 -0
  22. package/dist/gateway/static/root/assets/{directory-picker-path-field-D835EHJd.js → directory-picker-path-field-2rjzUu57.js} +1 -1
  23. package/dist/gateway/static/root/assets/{dreaming-settings-CCa1F7Fg.js → dreaming-settings-DnHO686d.js} +1 -1
  24. package/dist/gateway/static/root/assets/{extension-debug-page-W2ITCPSA.js → extension-debug-page-BGLRuWVN.js} +1 -1
  25. package/dist/gateway/static/root/assets/{extension-page-DrvUytvn.js → extension-page-D978ePp9.js} +1 -1
  26. package/dist/gateway/static/root/assets/{extension-settings-page-qM8aLT5u.js → extension-settings-page-B6Ih4Swx.js} +1 -1
  27. package/dist/gateway/static/root/assets/{fetch-ovkvM8KQ.js → fetch-BU5S0Sn7.js} +1 -1
  28. package/dist/gateway/static/root/assets/{gateway-config-api-BB3biuD0.js → gateway-config-api-BIPGwrl1.js} +1 -1
  29. package/dist/gateway/static/root/assets/gateway-settings-CMEA3r5u.js +1 -0
  30. package/dist/gateway/static/root/assets/{gateway-startup-retry-CanQ8UcR.js → gateway-startup-retry-Cd0Zz30N.js} +1 -1
  31. package/dist/gateway/static/root/assets/{goal-create-dialog-CNqNdEkQ.js → goal-create-dialog-B-KHKeXN.js} +1 -1
  32. package/dist/gateway/static/root/assets/goal-detail-page-BxrSoWdj.js +1 -0
  33. package/dist/gateway/static/root/assets/{goals-config-api-BBOX6mUl.js → goals-config-api-B95KdGMj.js} +1 -1
  34. package/dist/gateway/static/root/assets/{goals-page-BP1kfvWv.js → goals-page-BTJzIl28.js} +2 -2
  35. package/dist/gateway/static/root/assets/{goals-settings-Bh3A7OZV.js → goals-settings-BJyEuLLm.js} +1 -1
  36. package/dist/gateway/static/root/assets/heartbeat-settings-wkOjYNQT.js +1 -0
  37. package/dist/gateway/static/root/assets/index-BLaXr6Ev.css +1 -0
  38. package/dist/gateway/static/root/assets/index-c_9TVr_C.js +191 -0
  39. package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-B4TKJHla.js → keyboard-shortcuts-settings-BT8T4DyC.js} +1 -1
  40. package/dist/gateway/static/root/assets/logs-page-CrxBJUgI.js +2 -0
  41. package/dist/gateway/static/root/assets/{markdown-split-DcFtdLci.js → markdown-split-Dv66bO2b.js} +1 -1
  42. package/dist/gateway/static/root/assets/{media-plugins-B6SmeLVB.js → media-plugins-BhIQU5yS.js} +1 -1
  43. package/dist/gateway/static/root/assets/messages-O6TAtdFC.js +3 -0
  44. package/dist/gateway/static/root/assets/models-hub-panel-DOOpz6y1.js +2 -0
  45. package/dist/gateway/static/root/assets/notes-page-Br16pK9v.js +1 -0
  46. package/dist/gateway/static/root/assets/notes-workbench-PSJAi0ZC.js +2 -0
  47. package/dist/gateway/static/root/assets/{preview-open-alternatives-C29O19s7.js → preview-open-alternatives-DGzet1d7.js} +1 -1
  48. package/dist/gateway/static/root/assets/project-detail-page-CIGGgKuw.js +1 -0
  49. package/dist/gateway/static/root/assets/projects-page-B2grhsBt.js +1 -0
  50. package/dist/gateway/static/root/assets/remote-access-hub-CXMORxBD.js +1 -0
  51. package/dist/gateway/static/root/assets/{save-bar-controls-B4ad9GfB.js → save-bar-controls-Cqgb-jqa.js} +1 -1
  52. package/dist/gateway/static/root/assets/{schedule-field-DdYncfLR.js → schedule-field-DVZcI8Yt.js} +1 -1
  53. package/dist/gateway/static/root/assets/{schema-form-BMTrDE-7.js → schema-form-Bj7sOxC8.js} +1 -1
  54. package/dist/gateway/static/root/assets/sessions-page-Bi2wkBzU.js +1 -0
  55. package/dist/gateway/static/root/assets/settings-advanced-gate-B1L3G4IA.js +1 -0
  56. package/dist/gateway/static/root/assets/{settings-collapsible-section-MwTW2eK4.js → settings-collapsible-section-BmXn-8N6.js} +1 -1
  57. package/dist/gateway/static/root/assets/{settings-form-section-Dg6LBbcr.js → settings-form-section-BnqYMWHR.js} +1 -1
  58. package/dist/gateway/static/root/assets/settings-page-BfRSHJmO.js +1 -0
  59. package/dist/gateway/static/root/assets/{settings-page-layout-BX4T-OLF.js → settings-page-layout-CdWZU1hd.js} +1 -1
  60. package/dist/gateway/static/root/assets/{setup-status-panel-Ce1pcFtR.js → setup-status-panel-CWtPM7RK.js} +1 -1
  61. package/dist/gateway/static/root/assets/{share-preview-page-DWE-dUFh.js → share-preview-page-IB1v8i86.js} +1 -1
  62. package/dist/gateway/static/root/assets/shares-settings-ngbsBgij.js +1 -0
  63. package/dist/gateway/static/root/assets/skill-reload-api-BIe442Gv.js +1 -0
  64. package/dist/gateway/static/root/assets/skills-page-BfchZRVa.js +2 -0
  65. package/dist/gateway/static/root/assets/{system-settings-panel-Dd6Y35Cm.js → system-settings-panel-BxQ384RC.js} +1 -1
  66. package/dist/gateway/static/root/assets/{typed-models-lib-Cx6XV5A2.js → typed-models-lib-BkcpvFYO.js} +1 -1
  67. package/dist/gateway/static/root/assets/{use-channel-catalog-D4sMbmy7.js → use-channel-catalog-Cd0FaEQ4.js} +1 -1
  68. package/dist/gateway/static/root/assets/{user-profile-settings-panel-Bx-hjMQc.js → user-profile-settings-panel-DQ7NnRSY.js} +1 -1
  69. package/dist/gateway/static/root/assets/{utils-D7b_vZnv.js → utils-YKhVf7Ka.js} +1 -1
  70. package/dist/gateway/static/root/assets/voice-api-key-field-C5Ov8Uk_.js +1 -0
  71. package/dist/gateway/static/root/assets/{workflow-run-setup-panel--TdohF5N.js → workflow-run-setup-panel-BYD9BtOq.js} +2 -2
  72. package/dist/gateway/static/root/assets/{workflow-start-dialog-B0lavx_o.js → workflow-start-dialog-BNJVCWoS.js} +1 -1
  73. package/dist/gateway/static/root/assets/workflows-page-BYF29ANm.js +18 -0
  74. package/dist/gateway/static/root/index.html +5 -5
  75. package/dist/package.js +1 -1
  76. package/dist/src/agent/agent-manager.d.ts +6 -1
  77. package/dist/src/agent/agent-manager.js +28 -12
  78. package/dist/src/agent/agent-manager.js.map +1 -1
  79. package/dist/src/agent/context/workspace-templates/AGENTS.md +1 -1
  80. package/dist/src/agent/embedded/xopc-tools-bridge.js +11 -7
  81. package/dist/src/agent/embedded/xopc-tools-bridge.js.map +1 -1
  82. package/dist/src/agent/lifecycle/progress.js +3 -1
  83. package/dist/src/agent/lifecycle/progress.js.map +1 -1
  84. package/dist/src/agent/lifecycle/timeout-wrapper.d.ts +2 -2
  85. package/dist/src/agent/lifecycle/timeout-wrapper.js +4 -4
  86. package/dist/src/agent/lifecycle/timeout-wrapper.js.map +1 -1
  87. package/dist/src/agent/memory/summary-generator.d.ts +1 -1
  88. package/dist/src/agent/memory/summary-generator.js +1 -2
  89. package/dist/src/agent/memory/summary-generator.js.map +1 -1
  90. package/dist/src/agent/middleware/index.d.ts +1 -1
  91. package/dist/src/agent/middleware/self-verify.d.ts +44 -14
  92. package/dist/src/agent/middleware/self-verify.js +214 -44
  93. package/dist/src/agent/middleware/self-verify.js.map +1 -1
  94. package/dist/src/agent/orchestration/agent-event-handler.js +57 -15
  95. package/dist/src/agent/orchestration/agent-event-handler.js.map +1 -1
  96. package/dist/src/agent/prompt/sections/tooling.js +18 -13
  97. package/dist/src/agent/prompt/sections/tooling.js.map +1 -1
  98. package/dist/src/agent/prompt/system-reminder.js +2 -2
  99. package/dist/src/agent/prompt/system-reminder.js.map +1 -1
  100. package/dist/src/agent/sandbox/sanitize-env-vars.d.ts +2 -2
  101. package/dist/src/agent/sandbox/sanitize-env-vars.js +2 -2
  102. package/dist/src/agent/sandbox/sanitize-env-vars.js.map +1 -1
  103. package/dist/src/agent/sandbox/types.d.ts +2 -2
  104. package/dist/src/agent/sandbox/types.js.map +1 -1
  105. package/dist/src/agent/service/btw-query.js +1 -1
  106. package/dist/src/agent/service/btw-query.js.map +1 -1
  107. package/dist/src/agent/service/process-direct-one-shot.js +6 -2
  108. package/dist/src/agent/service/process-direct-one-shot.js.map +1 -1
  109. package/dist/src/agent/service/process-direct-streaming.js +12 -7
  110. package/dist/src/agent/service/process-direct-streaming.js.map +1 -1
  111. package/dist/src/agent/service.js +2 -1
  112. package/dist/src/agent/service.js.map +1 -1
  113. package/dist/src/agent/skills/skill-tool-gating.js +1 -1
  114. package/dist/src/agent/skills/skill-tool-gating.js.map +1 -1
  115. package/dist/src/agent/starter-agents.js +9 -9
  116. package/dist/src/agent/starter-agents.js.map +1 -1
  117. package/dist/src/agent/tools/apply-patch.d.ts +25 -0
  118. package/dist/src/agent/tools/apply-patch.js +256 -0
  119. package/dist/src/agent/tools/apply-patch.js.map +1 -0
  120. package/dist/src/agent/tools/delegate-tool.d.ts +1 -1
  121. package/dist/src/agent/tools/delegate-tool.js +3 -3
  122. package/dist/src/agent/tools/delegate-tool.js.map +1 -1
  123. package/dist/src/agent/tools/exec-command.d.ts +30 -0
  124. package/dist/src/agent/tools/exec-command.js +224 -0
  125. package/dist/src/agent/tools/exec-command.js.map +1 -0
  126. package/dist/src/agent/tools/execute-code-tool.js +7 -4
  127. package/dist/src/agent/tools/execute-code-tool.js.map +1 -1
  128. package/dist/src/agent/tools/factory.d.ts +3 -3
  129. package/dist/src/agent/tools/factory.js +8 -9
  130. package/dist/src/agent/tools/factory.js.map +1 -1
  131. package/dist/src/agent/tools/goal-tool.d.ts +5 -1
  132. package/dist/src/agent/tools/goal-tool.js +8 -2
  133. package/dist/src/agent/tools/goal-tool.js.map +1 -1
  134. package/dist/src/agent/tools/index.d.ts +4 -3
  135. package/dist/src/agent/tools/index.js +5 -4
  136. package/dist/src/agent/tools/skills-tools.d.ts +1 -1
  137. package/dist/src/agent/tools/skills-tools.js.map +1 -1
  138. package/dist/src/agent/tools/update-plan-tool.d.ts +11 -0
  139. package/dist/src/agent/tools/update-plan-tool.js +72 -0
  140. package/dist/src/agent/tools/update-plan-tool.js.map +1 -0
  141. package/dist/src/agent/workflow/builtins/debug-incident.d.ts +1 -1
  142. package/dist/src/agent/workflow/builtins/debug-incident.js +1 -1
  143. package/dist/src/agent/workflow/builtins/debug-incident.js.map +1 -1
  144. package/dist/src/agent/workflow/builtins/pr-review.d.ts +1 -1
  145. package/dist/src/agent/workflow/builtins/pr-review.js +1 -1
  146. package/dist/src/agent/workflow/builtins/pr-review.js.map +1 -1
  147. package/dist/src/agent/workflow/builtins/release-check.d.ts +1 -1
  148. package/dist/src/agent/workflow/builtins/release-check.js +1 -1
  149. package/dist/src/agent/workflow/builtins/release-check.js.map +1 -1
  150. package/dist/src/agent/workflow/step-labels.js +7 -3
  151. package/dist/src/agent/workflow/step-labels.js.map +1 -1
  152. package/dist/src/chat-commands/agent-edit.js +1 -1
  153. package/dist/src/chat-commands/agent-edit.js.map +1 -1
  154. package/dist/src/chat-commands/builtins/goal.js +4 -1
  155. package/dist/src/chat-commands/builtins/goal.js.map +1 -1
  156. package/dist/src/chat-commands/builtins/review.d.ts +1 -0
  157. package/dist/src/chat-commands/builtins/review.js +167 -0
  158. package/dist/src/chat-commands/builtins/review.js.map +1 -0
  159. package/dist/src/chat-commands/index.d.ts +1 -0
  160. package/dist/src/chat-commands/index.js +3 -1
  161. package/dist/src/chat-commands/index.js.map +1 -1
  162. package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
  163. package/dist/src/cli/commands/gateway/lifecycle.js +1 -1
  164. package/dist/src/config/agent-profile.js +1 -1
  165. package/dist/src/gateway/agent-builtin-tools.d.ts +1 -1
  166. package/dist/src/gateway/agent-builtin-tools.js +3 -2
  167. package/dist/src/gateway/agent-builtin-tools.js.map +1 -1
  168. package/dist/src/gateway/agents-admin.js +1 -1
  169. package/dist/src/gateway/chat-stream/mapper.d.ts +6 -0
  170. package/dist/src/gateway/chat-stream/mapper.js +149 -6
  171. package/dist/src/gateway/chat-stream/mapper.js.map +1 -1
  172. package/dist/src/gateway/chat-stream/protocol.d.ts +51 -1
  173. package/dist/src/gateway/hono/routes/commands-skills.js +21 -0
  174. package/dist/src/gateway/hono/routes/commands-skills.js.map +1 -1
  175. package/dist/src/gateway/hono/routes/index.js +2 -0
  176. package/dist/src/gateway/hono/routes/index.js.map +1 -1
  177. package/dist/src/gateway/hono/routes/lazy-bundles.js +1 -0
  178. package/dist/src/gateway/hono/routes/lazy-bundles.js.map +1 -1
  179. package/dist/src/gateway/hono/routes/projects.js +136 -14
  180. package/dist/src/gateway/hono/routes/projects.js.map +1 -1
  181. package/dist/src/gateway/hono/routes/search.d.ts +3 -0
  182. package/dist/src/gateway/hono/routes/search.js +47 -0
  183. package/dist/src/gateway/hono/routes/search.js.map +1 -0
  184. package/dist/src/gateway/security/dangerous-tools.js +1 -1
  185. package/dist/src/gateway/security/dangerous-tools.js.map +1 -1
  186. package/dist/src/infra/update-restart.js +1 -1
  187. package/dist/src/projects/index.d.ts +3 -0
  188. package/dist/src/projects/index.js +4 -1
  189. package/dist/src/projects/project-agent-suggestion.d.ts +14 -0
  190. package/dist/src/projects/project-agent-suggestion.js +19 -0
  191. package/dist/src/projects/project-agent-suggestion.js.map +1 -0
  192. package/dist/src/projects/project-kind.d.ts +13 -0
  193. package/dist/src/projects/project-kind.js +147 -0
  194. package/dist/src/projects/project-kind.js.map +1 -0
  195. package/dist/src/projects/project-service.d.ts +15 -0
  196. package/dist/src/projects/project-service.js +96 -3
  197. package/dist/src/projects/project-service.js.map +1 -1
  198. package/dist/src/projects/project-store.js +32 -8
  199. package/dist/src/projects/project-store.js.map +1 -1
  200. package/dist/src/projects/types.d.ts +4 -1
  201. package/dist/src/projects/workspace-project.d.ts +22 -0
  202. package/dist/src/projects/workspace-project.js +143 -0
  203. package/dist/src/projects/workspace-project.js.map +1 -0
  204. package/dist/src/review/review-git.d.ts +48 -0
  205. package/dist/src/review/review-git.js +333 -0
  206. package/dist/src/review/review-git.js.map +1 -0
  207. package/dist/src/review/review-types.d.ts +22 -0
  208. package/dist/src/review/review-types.js +29 -0
  209. package/dist/src/review/review-types.js.map +1 -0
  210. package/dist/src/session/session-context-for-llm.js +151 -0
  211. package/dist/src/session/session-context-for-llm.js.map +1 -1
  212. package/dist/src/storage/sqlite/migrations/021_projects_fts.sql +21 -0
  213. package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
  214. package/dist/src/storage/sqlite/migrations/runner.js +3 -3
  215. package/dist/src/storage/sqlite/migrations/runner.js.map +1 -1
  216. package/dist/src/storage/sqlite/migrations/runner.ts +1 -1
  217. package/dist/src/storage/sqlite/schema.js +1 -1
  218. package/dist/src/tui/backends/embedded-backend.d.ts +8 -1
  219. package/dist/src/tui/backends/embedded-backend.js +60 -1
  220. package/dist/src/tui/backends/embedded-backend.js.map +1 -1
  221. package/dist/src/tui/backends/gateway-sse-backend.d.ts +9 -1
  222. package/dist/src/tui/backends/gateway-sse-backend.js +29 -0
  223. package/dist/src/tui/backends/gateway-sse-backend.js.map +1 -1
  224. package/dist/src/tui/components/assistant-message.js +35 -0
  225. package/dist/src/tui/components/assistant-message.js.map +1 -1
  226. package/dist/src/tui/tui-agent-events.js +25 -0
  227. package/dist/src/tui/tui-agent-events.js.map +1 -1
  228. package/dist/src/tui/tui-backend.d.ts +20 -0
  229. package/dist/src/tui/tui-commands.d.ts +1 -0
  230. package/dist/src/tui/tui-commands.js +110 -4
  231. package/dist/src/tui/tui-commands.js.map +1 -1
  232. package/dist/src/tui/tui-picker-overlay.d.ts +2 -0
  233. package/dist/src/tui/tui-picker-overlay.js +153 -1
  234. package/dist/src/tui/tui-picker-overlay.js.map +1 -1
  235. package/dist/src/tui/tui.js +36 -2
  236. package/dist/src/tui/tui.js.map +1 -1
  237. package/dist/src/workflows/service/workflow-run-service.js +1 -1
  238. package/dist/src/workflows/service/workflow-run-service.js.map +1 -1
  239. package/package.json +1 -1
  240. package/dist/gateway/static/root/assets/agents-SsQrj9B-.js +0 -2
  241. package/dist/gateway/static/root/assets/apps-page-CYb6fame.js +0 -1
  242. package/dist/gateway/static/root/assets/automations-page-DpOOq1nk.js +0 -1
  243. package/dist/gateway/static/root/assets/browser-settings-page-9nXxgh4u.js +0 -8
  244. package/dist/gateway/static/root/assets/capability-presets-settings-panel-D528_YFy.js +0 -1
  245. package/dist/gateway/static/root/assets/channels-settings-Ck-3h5lI.js +0 -1
  246. package/dist/gateway/static/root/assets/connectors-page-DL-fAqE_.js +0 -2
  247. package/dist/gateway/static/root/assets/gateway-settings-mmmnIH27.js +0 -1
  248. package/dist/gateway/static/root/assets/goal-detail-page-Ceu5zrN6.js +0 -1
  249. package/dist/gateway/static/root/assets/heartbeat-settings-YoYgKlfX.js +0 -1
  250. package/dist/gateway/static/root/assets/index-DdqSO9R2.js +0 -191
  251. package/dist/gateway/static/root/assets/index-jGB-2nI-.css +0 -1
  252. package/dist/gateway/static/root/assets/logs-page-BgVk5DvZ.js +0 -2
  253. package/dist/gateway/static/root/assets/messages-B5O4OIEo.js +0 -3
  254. package/dist/gateway/static/root/assets/models-hub-panel-BU0x8PGP.js +0 -2
  255. package/dist/gateway/static/root/assets/notes-page-D6JYVP4I.js +0 -1
  256. package/dist/gateway/static/root/assets/notes-workbench-mqRJYnJS.js +0 -2
  257. package/dist/gateway/static/root/assets/project-detail-page-DOTgzoeh.js +0 -1
  258. package/dist/gateway/static/root/assets/projects-page-DGUIb4y5.js +0 -1
  259. package/dist/gateway/static/root/assets/remote-access-hub-DVKKUj3u.js +0 -1
  260. package/dist/gateway/static/root/assets/sessions-page-BVbzUWxW.js +0 -1
  261. package/dist/gateway/static/root/assets/settings-advanced-gate-DrNNoSqE.js +0 -1
  262. package/dist/gateway/static/root/assets/settings-page-CMDqgeMR.js +0 -1
  263. package/dist/gateway/static/root/assets/shares-settings-aKSraR8l.js +0 -1
  264. package/dist/gateway/static/root/assets/skill-reload-api-CS-FU_B-.js +0 -1
  265. package/dist/gateway/static/root/assets/skills-page-BaF3u0LA.js +0 -2
  266. package/dist/gateway/static/root/assets/voice-api-key-field-CkhbRt1q.js +0 -1
  267. package/dist/gateway/static/root/assets/workflows-page-CZR05Bu_.js +0 -18
  268. package/dist/src/agent/tools/edit.d.ts +0 -14
  269. package/dist/src/agent/tools/edit.js +0 -132
  270. package/dist/src/agent/tools/edit.js.map +0 -1
  271. package/dist/src/agent/tools/shell.d.ts +0 -15
  272. package/dist/src/agent/tools/shell.js +0 -158
  273. package/dist/src/agent/tools/shell.js.map +0 -1
@@ -0,0 +1,224 @@
1
+ import { formatSize, truncateTail } from "./truncate.js";
2
+ import { evaluateExecPolicy } from "../sandbox/exec-policy.js";
3
+ import { resolve } from "node:path";
4
+ import { spawn } from "node:child_process";
5
+ import { Type } from "@sinclair/typebox";
6
+ //#region src/agent/tools/exec-command.ts
7
+ const MAX_COMMAND_TIMEOUT_MS = 3e5;
8
+ const DEFAULT_TIMEOUT_MS = 12e4;
9
+ const DEFAULT_MAX_OUTPUT_CHARS = 5e4;
10
+ const STREAM_DELTA_MAX_CHARS = 16e3;
11
+ const ExecCommandSchema = Type.Object({
12
+ cmd: Type.String({ description: "Shell command to execute." }),
13
+ cwd: Type.Optional(Type.String({ description: "Working directory. Relative paths are resolved under the agent workspace." })),
14
+ timeoutMs: Type.Optional(Type.Number({ description: "Command timeout in milliseconds. Defaults to 120000 and is capped at 300000." })),
15
+ maxOutputChars: Type.Optional(Type.Number({ description: "Maximum characters returned to the model. UI streaming still receives output deltas." }))
16
+ });
17
+ function clampTimeoutMs(raw) {
18
+ return Math.min(MAX_COMMAND_TIMEOUT_MS, Math.max(1e3, Math.floor(typeof raw === "number" && Number.isFinite(raw) ? raw : DEFAULT_TIMEOUT_MS)));
19
+ }
20
+ function clampMaxOutputChars(raw) {
21
+ return Math.max(1e3, Math.min(5e5, Math.floor(typeof raw === "number" && Number.isFinite(raw) ? raw : DEFAULT_MAX_OUTPUT_CHARS)));
22
+ }
23
+ function isLikelyTestCommand(command) {
24
+ return /\b(test|vitest|jest|pytest|go test|cargo test|npm test|pnpm test|yarn test|typecheck|type-check|lint)\b/i.test(command);
25
+ }
26
+ function commandStatus(exitCode, timedOut) {
27
+ if (timedOut) return "timed_out";
28
+ if (exitCode === 0) return "success";
29
+ return "failed";
30
+ }
31
+ function commandFailureHint(details) {
32
+ if (details.status === "success") return void 0;
33
+ if (details.status === "timed_out") return "The command timed out. Re-run a narrower command, increase timeoutMs if the long run is expected, or inspect partial output before retrying.";
34
+ if (details.exitCode !== 0 && details.stderr.trim()) return "The command failed. Inspect stderr first, fix the underlying issue, then re-run the narrowest relevant verification command.";
35
+ if (details.exitCode !== 0 && details.stdout.trim()) return "The command failed. Inspect stdout for the failure location, then re-run the narrowest relevant verification command.";
36
+ return "The command failed without output. Verify the command, cwd, and required dependencies before retrying.";
37
+ }
38
+ function formatOutputForModel(details, maxOutputChars) {
39
+ let prefix = "";
40
+ if (details.timedOut) prefix += `Command timed out after ${Math.round(details.durationMs / 1e3)}s\n`;
41
+ if (details.exitCode !== 0 && details.exitCode != null) prefix += `Command exited with code ${details.exitCode}\n`;
42
+ const truncation = truncateTail(details.aggregatedOutput, { maxBytes: Math.min(maxOutputChars, DEFAULT_MAX_OUTPUT_CHARS) });
43
+ let text = `${prefix}${truncation.content}`.trimEnd();
44
+ if (!text) text = "(no output)";
45
+ if (truncation.truncated) text += `\n\n[Output truncated: ${formatSize(truncation.outputBytes)} of ${formatSize(truncation.totalBytes)}]`;
46
+ if (details.failureHint) text += `\n\nNext action: ${details.failureHint}`;
47
+ return {
48
+ text,
49
+ truncated: truncation.truncated,
50
+ truncatedBy: truncation.truncatedBy,
51
+ outputBytes: truncation.outputBytes
52
+ };
53
+ }
54
+ function createExecCommandTool(workspaceCwd, options) {
55
+ return {
56
+ name: "exec_command",
57
+ description: [
58
+ "Run shell commands for code inspection, builds, tests, type checks, and verification.",
59
+ "Use apply_patch for file edits; do not edit files by shell redirection unless explicitly necessary.",
60
+ "Relative cwd values resolve under the current agent workspace."
61
+ ].join(" "),
62
+ parameters: ExecCommandSchema,
63
+ label: "Run Command",
64
+ async execute(_toolCallId, params, signal, onUpdate) {
65
+ const command = (params.cmd ?? params.command ?? "").trim();
66
+ if (!command) return {
67
+ content: [{
68
+ type: "text",
69
+ text: "Error: cmd is required"
70
+ }],
71
+ details: {
72
+ command,
73
+ cwd: workspaceCwd,
74
+ status: "failed",
75
+ exitCode: null,
76
+ durationMs: 0,
77
+ timedOut: false,
78
+ truncated: false,
79
+ outputBytes: 0,
80
+ stdout: "",
81
+ stderr: "cmd is required",
82
+ aggregatedOutput: "cmd is required",
83
+ failureHint: "Provide a non-empty cmd value."
84
+ }
85
+ };
86
+ const requestedCwd = params.cwd?.trim() ? resolve(workspaceCwd, params.cwd.trim()) : workspaceCwd;
87
+ const policy = evaluateExecPolicy({
88
+ command,
89
+ cwd: requestedCwd,
90
+ allowedEnvVars: options?.getSkillPassthroughEnvVarNames?.() ?? []
91
+ });
92
+ if (!policy.allowed) {
93
+ const text = `Sandbox blocked command: ${policy.reason}`;
94
+ return {
95
+ content: [{
96
+ type: "text",
97
+ text
98
+ }],
99
+ details: {
100
+ command,
101
+ cwd: requestedCwd,
102
+ status: "blocked",
103
+ exitCode: null,
104
+ durationMs: 0,
105
+ timedOut: false,
106
+ truncated: false,
107
+ outputBytes: Buffer.byteLength(text),
108
+ stdout: "",
109
+ stderr: text,
110
+ aggregatedOutput: text,
111
+ failureHint: "Use a narrower workspace-safe command or inspect files with read/search tools instead."
112
+ }
113
+ };
114
+ }
115
+ const timeoutMs = Math.min(clampTimeoutMs(params.timeoutMs), policy.timeoutMs);
116
+ const maxOutputChars = clampMaxOutputChars(params.maxOutputChars);
117
+ const startTime = Date.now();
118
+ let stdout = "";
119
+ let stderr = "";
120
+ let aggregatedOutput = "";
121
+ let timedOut = false;
122
+ let settled = false;
123
+ return new Promise((resolveTool) => {
124
+ const proc = spawn(command, [], {
125
+ shell: true,
126
+ cwd: policy.effectiveCwd,
127
+ env: {
128
+ ...policy.sanitizedEnv,
129
+ COLUMNS: "200"
130
+ }
131
+ });
132
+ const publishDelta = (stream, raw) => {
133
+ const text = raw.toString();
134
+ if (!text) return;
135
+ if (stream === "stdout") stdout += text;
136
+ else stderr += text;
137
+ aggregatedOutput += text;
138
+ const delta = text.length > STREAM_DELTA_MAX_CHARS ? `${text.slice(0, STREAM_DELTA_MAX_CHARS)}\n[stream delta truncated]` : text;
139
+ onUpdate?.({
140
+ content: [],
141
+ details: {
142
+ kind: "command_output_delta",
143
+ command,
144
+ cwd: policy.effectiveCwd,
145
+ stream,
146
+ delta
147
+ }
148
+ });
149
+ };
150
+ const finish = (exitCode, errorText) => {
151
+ if (settled) return;
152
+ settled = true;
153
+ clearTimeout(timeout);
154
+ signal?.removeEventListener("abort", abort);
155
+ if (errorText) {
156
+ stderr += errorText;
157
+ aggregatedOutput += errorText;
158
+ }
159
+ const durationMs = Date.now() - startTime;
160
+ const baseDetails = {
161
+ command,
162
+ cwd: policy.effectiveCwd,
163
+ status: commandStatus(exitCode, timedOut),
164
+ exitCode,
165
+ durationMs,
166
+ timedOut,
167
+ truncated: false,
168
+ outputBytes: Buffer.byteLength(aggregatedOutput),
169
+ stdout,
170
+ stderr,
171
+ aggregatedOutput
172
+ };
173
+ baseDetails.failureHint = commandFailureHint(baseDetails);
174
+ const formatted = formatOutputForModel(baseDetails, maxOutputChars);
175
+ const details = {
176
+ ...baseDetails,
177
+ truncated: formatted.truncated,
178
+ truncatedBy: formatted.truncatedBy,
179
+ outputBytes: formatted.outputBytes
180
+ };
181
+ const result = {
182
+ content: [{
183
+ type: "text",
184
+ text: formatted.text
185
+ }],
186
+ details
187
+ };
188
+ Promise.resolve(options?.recordGoalEvidence?.({
189
+ kind: timedOut || exitCode !== 0 ? "command" : isLikelyTestCommand(command) ? "test" : "command",
190
+ title: `Command: ${command.slice(0, 120)}`,
191
+ summary: formatted.text.slice(0, 2e3),
192
+ data: {
193
+ command,
194
+ cwd: policy.effectiveCwd,
195
+ exitCode,
196
+ durationMs,
197
+ timedOut,
198
+ truncated: details.truncated,
199
+ outputBytes: details.outputBytes
200
+ }
201
+ })).finally(() => resolveTool(result));
202
+ };
203
+ const abort = () => {
204
+ timedOut = true;
205
+ proc.kill("SIGKILL");
206
+ };
207
+ const timeout = setTimeout(() => {
208
+ timedOut = true;
209
+ proc.kill("SIGKILL");
210
+ }, timeoutMs);
211
+ if (signal?.aborted) abort();
212
+ else signal?.addEventListener("abort", abort, { once: true });
213
+ proc.stdout?.on("data", (data) => publishDelta("stdout", data));
214
+ proc.stderr?.on("data", (data) => publishDelta("stderr", data));
215
+ proc.on("error", (err) => finish(null, `Error: ${err.message}`));
216
+ proc.on("close", (code) => finish(code));
217
+ });
218
+ }
219
+ };
220
+ }
221
+ //#endregion
222
+ export { createExecCommandTool };
223
+
224
+ //# sourceMappingURL=exec-command.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exec-command.js","names":[],"sources":["../../../../src/agent/tools/exec-command.ts"],"sourcesContent":["import { Type } from '@sinclair/typebox';\nimport type {\n AgentTool,\n AgentToolResult,\n AgentToolUpdateCallback,\n} from '@earendil-works/pi-agent-core';\nimport { spawn } from 'node:child_process';\nimport { resolve } from 'node:path';\n\nimport { evaluateExecPolicy } from '../sandbox/exec-policy.js';\nimport { formatSize, truncateTail } from './truncate.js';\nimport type { GoalEvidenceRecordInput } from './goal-evidence-recorder.js';\n\nconst MAX_COMMAND_TIMEOUT_MS = 300_000;\nconst DEFAULT_TIMEOUT_MS = 120_000;\nconst DEFAULT_MAX_OUTPUT_CHARS = 50_000;\nconst STREAM_DELTA_MAX_CHARS = 16_000;\n\nconst ExecCommandSchema = Type.Object({\n cmd: Type.String({ description: 'Shell command to execute.' }),\n cwd: Type.Optional(\n Type.String({\n description: 'Working directory. Relative paths are resolved under the agent workspace.',\n }),\n ),\n timeoutMs: Type.Optional(\n Type.Number({\n description: 'Command timeout in milliseconds. Defaults to 120000 and is capped at 300000.',\n }),\n ),\n maxOutputChars: Type.Optional(\n Type.Number({\n description: 'Maximum characters returned to the model. UI streaming still receives output deltas.',\n }),\n ),\n});\n\nexport interface ExecCommandDetails {\n command: string;\n cwd: string;\n status: 'success' | 'failed' | 'timed_out' | 'blocked';\n exitCode: number | null;\n durationMs: number;\n timedOut: boolean;\n truncated: boolean;\n truncatedBy?: 'lines' | 'bytes' | null;\n outputBytes: number;\n stdout: string;\n stderr: string;\n aggregatedOutput: string;\n failureHint?: string;\n}\n\nexport interface ExecCommandUpdateDetails {\n kind: 'command_output_delta';\n command: string;\n cwd: string;\n stream: 'stdout' | 'stderr';\n delta: string;\n}\n\nexport interface CreateExecCommandToolOptions {\n /** Env var names allowed through prepareSafeToolEnv even if they match secret heuristics. */\n getSkillPassthroughEnvVarNames?: () => string[];\n recordGoalEvidence?: (input: GoalEvidenceRecordInput) => Promise<void> | void;\n}\n\ntype ExecCommandParams = {\n cmd?: string;\n command?: string;\n cwd?: string;\n timeoutMs?: number;\n maxOutputChars?: number;\n};\n\nfunction clampTimeoutMs(raw: unknown): number {\n const n = typeof raw === 'number' && Number.isFinite(raw) ? raw : DEFAULT_TIMEOUT_MS;\n return Math.min(MAX_COMMAND_TIMEOUT_MS, Math.max(1_000, Math.floor(n)));\n}\n\nfunction clampMaxOutputChars(raw: unknown): number {\n const n = typeof raw === 'number' && Number.isFinite(raw) ? raw : DEFAULT_MAX_OUTPUT_CHARS;\n return Math.max(1_000, Math.min(500_000, Math.floor(n)));\n}\n\nfunction isLikelyTestCommand(command: string): boolean {\n return /\\b(test|vitest|jest|pytest|go test|cargo test|npm test|pnpm test|yarn test|typecheck|type-check|lint)\\b/i.test(command);\n}\n\nfunction commandStatus(exitCode: number | null, timedOut: boolean): ExecCommandDetails['status'] {\n if (timedOut) return 'timed_out';\n if (exitCode === 0) return 'success';\n return 'failed';\n}\n\nfunction commandFailureHint(details: Pick<ExecCommandDetails, 'status' | 'exitCode' | 'timedOut' | 'stderr' | 'stdout'>): string | undefined {\n if (details.status === 'success') return undefined;\n if (details.status === 'timed_out') {\n return 'The command timed out. Re-run a narrower command, increase timeoutMs if the long run is expected, or inspect partial output before retrying.';\n }\n if (details.exitCode !== 0 && details.stderr.trim()) {\n return 'The command failed. Inspect stderr first, fix the underlying issue, then re-run the narrowest relevant verification command.';\n }\n if (details.exitCode !== 0 && details.stdout.trim()) {\n return 'The command failed. Inspect stdout for the failure location, then re-run the narrowest relevant verification command.';\n }\n return 'The command failed without output. Verify the command, cwd, and required dependencies before retrying.';\n}\n\nfunction formatOutputForModel(\n details: ExecCommandDetails,\n maxOutputChars: number,\n): { text: string; truncated: boolean; truncatedBy?: 'lines' | 'bytes' | null; outputBytes: number } {\n let prefix = '';\n if (details.timedOut) {\n prefix += `Command timed out after ${Math.round(details.durationMs / 1000)}s\\n`;\n }\n if (details.exitCode !== 0 && details.exitCode != null) {\n prefix += `Command exited with code ${details.exitCode}\\n`;\n }\n\n const truncation = truncateTail(details.aggregatedOutput, {\n maxBytes: Math.min(maxOutputChars, DEFAULT_MAX_OUTPUT_CHARS),\n });\n let text = `${prefix}${truncation.content}`.trimEnd();\n if (!text) text = '(no output)';\n if (truncation.truncated) {\n text += `\\n\\n[Output truncated: ${formatSize(truncation.outputBytes)} of ${formatSize(truncation.totalBytes)}]`;\n }\n if (details.failureHint) {\n text += `\\n\\nNext action: ${details.failureHint}`;\n }\n return {\n text,\n truncated: truncation.truncated,\n truncatedBy: truncation.truncatedBy,\n outputBytes: truncation.outputBytes,\n };\n}\n\nexport function createExecCommandTool(\n workspaceCwd: string,\n options?: CreateExecCommandToolOptions,\n): AgentTool {\n return {\n name: 'exec_command',\n description: [\n 'Run shell commands for code inspection, builds, tests, type checks, and verification.',\n 'Use apply_patch for file edits; do not edit files by shell redirection unless explicitly necessary.',\n 'Relative cwd values resolve under the current agent workspace.',\n ].join(' '),\n parameters: ExecCommandSchema,\n label: 'Run Command',\n\n async execute(\n _toolCallId: string,\n params: ExecCommandParams,\n signal?: AbortSignal,\n onUpdate?: AgentToolUpdateCallback<ExecCommandUpdateDetails>,\n ): Promise<AgentToolResult<ExecCommandDetails>> {\n const command = (params.cmd ?? params.command ?? '').trim();\n if (!command) {\n return {\n content: [{ type: 'text', text: 'Error: cmd is required' }],\n details: {\n command,\n cwd: workspaceCwd,\n status: 'failed',\n exitCode: null,\n durationMs: 0,\n timedOut: false,\n truncated: false,\n outputBytes: 0,\n stdout: '',\n stderr: 'cmd is required',\n aggregatedOutput: 'cmd is required',\n failureHint: 'Provide a non-empty cmd value.',\n },\n };\n }\n\n const requestedCwd = params.cwd?.trim()\n ? resolve(workspaceCwd, params.cwd.trim())\n : workspaceCwd;\n const passthroughNames = options?.getSkillPassthroughEnvVarNames?.() ?? [];\n const policy = evaluateExecPolicy({\n command,\n cwd: requestedCwd,\n allowedEnvVars: passthroughNames,\n });\n if (!policy.allowed) {\n const text = `Sandbox blocked command: ${policy.reason}`;\n return {\n content: [{ type: 'text', text }],\n details: {\n command,\n cwd: requestedCwd,\n status: 'blocked',\n exitCode: null,\n durationMs: 0,\n timedOut: false,\n truncated: false,\n outputBytes: Buffer.byteLength(text),\n stdout: '',\n stderr: text,\n aggregatedOutput: text,\n failureHint: 'Use a narrower workspace-safe command or inspect files with read/search tools instead.',\n },\n };\n }\n\n const timeoutMs = Math.min(clampTimeoutMs(params.timeoutMs), policy.timeoutMs);\n const maxOutputChars = clampMaxOutputChars(params.maxOutputChars);\n const startTime = Date.now();\n let stdout = '';\n let stderr = '';\n let aggregatedOutput = '';\n let timedOut = false;\n let settled = false;\n\n return new Promise((resolveTool) => {\n const proc = spawn(command, [], {\n shell: true,\n cwd: policy.effectiveCwd,\n env: {\n ...policy.sanitizedEnv,\n COLUMNS: '200',\n },\n });\n\n const publishDelta = (stream: 'stdout' | 'stderr', raw: Buffer | string) => {\n const text = raw.toString();\n if (!text) return;\n if (stream === 'stdout') stdout += text;\n else stderr += text;\n aggregatedOutput += text;\n\n const delta = text.length > STREAM_DELTA_MAX_CHARS\n ? `${text.slice(0, STREAM_DELTA_MAX_CHARS)}\\n[stream delta truncated]`\n : text;\n onUpdate?.({\n content: [],\n details: {\n kind: 'command_output_delta',\n command,\n cwd: policy.effectiveCwd,\n stream,\n delta,\n },\n });\n };\n\n const finish = (\n exitCode: number | null,\n errorText?: string,\n ) => {\n if (settled) return;\n settled = true;\n clearTimeout(timeout);\n signal?.removeEventListener('abort', abort);\n if (errorText) {\n stderr += errorText;\n aggregatedOutput += errorText;\n }\n\n const durationMs = Date.now() - startTime;\n const baseDetails: ExecCommandDetails = {\n command,\n cwd: policy.effectiveCwd,\n status: commandStatus(exitCode, timedOut),\n exitCode,\n durationMs,\n timedOut,\n truncated: false,\n outputBytes: Buffer.byteLength(aggregatedOutput),\n stdout,\n stderr,\n aggregatedOutput,\n };\n baseDetails.failureHint = commandFailureHint(baseDetails);\n const formatted = formatOutputForModel(baseDetails, maxOutputChars);\n const details: ExecCommandDetails = {\n ...baseDetails,\n truncated: formatted.truncated,\n truncatedBy: formatted.truncatedBy,\n outputBytes: formatted.outputBytes,\n };\n\n const result: AgentToolResult<ExecCommandDetails> = {\n content: [{ type: 'text', text: formatted.text }],\n details,\n };\n\n void Promise.resolve(options?.recordGoalEvidence?.({\n kind: timedOut || exitCode !== 0 ? 'command' : isLikelyTestCommand(command) ? 'test' : 'command',\n title: `Command: ${command.slice(0, 120)}`,\n summary: formatted.text.slice(0, 2000),\n data: {\n command,\n cwd: policy.effectiveCwd,\n exitCode,\n durationMs,\n timedOut,\n truncated: details.truncated,\n outputBytes: details.outputBytes,\n },\n })).finally(() => resolveTool(result));\n };\n\n const abort = () => {\n timedOut = true;\n proc.kill('SIGKILL');\n };\n\n const timeout = setTimeout(() => {\n timedOut = true;\n proc.kill('SIGKILL');\n }, timeoutMs);\n\n if (signal?.aborted) {\n abort();\n } else {\n signal?.addEventListener('abort', abort, { once: true });\n }\n\n proc.stdout?.on('data', (data) => publishDelta('stdout', data));\n proc.stderr?.on('data', (data) => publishDelta('stderr', data));\n proc.on('error', (err) => finish(null, `Error: ${err.message}`));\n proc.on('close', (code) => finish(code));\n });\n },\n } as any;\n}\n"],"mappings":"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"}
@@ -10,7 +10,7 @@ const DEFAULT_TIMEOUT_SEC = 1800;
10
10
  /** Hard cap for `timeout` parameter (4 hours). */
11
11
  const MAX_TIMEOUT_SEC = 14400;
12
12
  const ExecuteCodeSchema = Type.Object({
13
- code: Type.String({ description: "JavaScript to run. Exposed as `tools` (async functions) and `console`:\n await tools.web_search(query, count?)\n await tools.web_fetch(url, maxChars?)\n await tools.read_file(path, limit?)\n await tools.write_file(path, content)\n await tools.grep(pattern, { path?, glob?, ignoreCase?, literal?, context?, limit? })\n await tools.find(pattern, { path?, limit? })\n await tools.shell(command)\n\nUse console.log() for output; combined stdout/stderr is returned." }),
13
+ code: Type.String({ description: "JavaScript to run. Exposed as `tools` (async functions) and `console`:\n await tools.web_search(query, count?)\n await tools.web_fetch(url, maxChars?)\n await tools.read_file(path, limit?)\n await tools.write_file(path, content)\n await tools.grep(pattern, { path?, glob?, ignoreCase?, literal?, context?, limit? })\n await tools.find(pattern, { path?, limit? })\n await tools.exec_command(cmd, cwd?)\n\nUse console.log() for output; combined stdout/stderr is returned." }),
14
14
  timeout: Type.Optional(Type.Number({
15
15
  description: "Execution timeout in seconds (default: 3600 = 60m, max: 14400 = 4h)",
16
16
  default: DEFAULT_TIMEOUT_SEC
@@ -23,7 +23,7 @@ const SANDBOX_ALLOWED_TOOLS = new Set([
23
23
  "write_file",
24
24
  "grep",
25
25
  "find",
26
- "shell",
26
+ "exec_command",
27
27
  "skills_list",
28
28
  "skill_view"
29
29
  ]);
@@ -99,7 +99,10 @@ function createToolsApi(getMap, signal) {
99
99
  ...typeof o.limit === "number" && Number.isFinite(o.limit) ? { limit: o.limit } : {}
100
100
  });
101
101
  },
102
- shell: async (command) => run("shell", { command: String(command ?? "") })
102
+ exec_command: async (cmd, cwd) => run("exec_command", {
103
+ cmd: String(cmd ?? ""),
104
+ ...typeof cwd === "string" && cwd.trim() ? { cwd } : {}
105
+ })
103
106
  };
104
107
  }
105
108
  async function runSandboxedScript(code, sandbox, timeoutMs, signal) {
@@ -128,7 +131,7 @@ function createExecuteCodeTool(deps) {
128
131
  return {
129
132
  name: "execute_code",
130
133
  label: "⚡ Execute Code",
131
- description: "Run sandboxed JavaScript that calls a subset of tools via `tools.*` (batch work in one step).\n\nOnly stdout/stderr from `console` plus tool return text (as strings) are visible — not full tool JSON.\n\nWHEN TO USE: loops over files/URLs, simple branching between tool calls.\nWHEN NOT TO USE: single tool calls; tasks needing full tool schemas or disallowed tools.\n\nAPI: `await tools.web_search(q, count?)`, `web_fetch(url, maxChars?)`, `read_file(path, limit?)`, `write_file(path, content)`, `grep(pattern, opts?)`, `find(glob, opts?)`, `shell(command)`. Use `console.log` for output.",
134
+ description: "Run sandboxed JavaScript that calls a subset of tools via `tools.*` (batch work in one step).\n\nOnly stdout/stderr from `console` plus tool return text (as strings) are visible — not full tool JSON.\n\nWHEN TO USE: loops over files/URLs, simple branching between tool calls.\nWHEN NOT TO USE: single tool calls; tasks needing full tool schemas or disallowed tools.\n\nAPI: `await tools.web_search(q, count?)`, `web_fetch(url, maxChars?)`, `read_file(path, limit?)`, `write_file(path, content)`, `grep(pattern, opts?)`, `find(glob, opts?)`, `exec_command(cmd, cwd?)`. Use `console.log` for output.",
132
135
  parameters: ExecuteCodeSchema,
133
136
  async execute(_toolCallId, params, signal) {
134
137
  const p = params;
@@ -1 +1 @@
1
- {"version":3,"file":"execute-code-tool.js","names":[],"sources":["../../../../src/agent/tools/execute-code-tool.ts"],"sourcesContent":["/**\n * Programmatic tool calling (PTC): run sandboxed JS that invokes a subset of agent tools.\n * vm is not a strong security boundary; only trusted models should use this tool.\n */\n\nimport { Script, createContext } from 'node:vm';\n\nimport { Type } from '@sinclair/typebox';\nimport type { AgentTool, AgentToolResult } from '@earendil-works/pi-agent-core';\n\n/** Default script wall time when `timeout` omitted (30 minutes). */\nconst DEFAULT_TIMEOUT_SEC = 30 * 60;\n/** Hard cap for `timeout` parameter (4 hours). */\nconst MAX_TIMEOUT_SEC = 4 * 60 * 60;\n\nconst ExecuteCodeSchema = Type.Object({\n code: Type.String({\n description:\n 'JavaScript to run. Exposed as `tools` (async functions) and `console`:\\n' +\n ' await tools.web_search(query, count?)\\n' +\n ' await tools.web_fetch(url, maxChars?)\\n' +\n ' await tools.read_file(path, limit?)\\n' +\n ' await tools.write_file(path, content)\\n' +\n ' await tools.grep(pattern, { path?, glob?, ignoreCase?, literal?, context?, limit? })\\n' +\n ' await tools.find(pattern, { path?, limit? })\\n' +\n ' await tools.shell(command)\\n\\n' +\n 'Use console.log() for output; combined stdout/stderr is returned.',\n }),\n timeout: Type.Optional(\n Type.Number({\n description: 'Execution timeout in seconds (default: 3600 = 60m, max: 14400 = 4h)',\n default: DEFAULT_TIMEOUT_SEC,\n }),\n ),\n});\n\nexport const SANDBOX_ALLOWED_TOOLS = new Set([\n 'web_search',\n 'web_fetch',\n 'read_file',\n 'write_file',\n 'grep',\n 'find',\n 'shell',\n 'skills_list',\n 'skill_view',\n]);\n\nconst MAX_TIMEOUT_MS = MAX_TIMEOUT_SEC * 1000;\nconst MAX_TOOL_CALLS = 50;\nconst MAX_STDOUT_CHARS = 50_000;\nconst MAX_STDERR_CHARS = 10_000;\n\nfunction stringifyArg(a: unknown): string {\n if (typeof a === 'string') {\n return a;\n }\n try {\n return JSON.stringify(a);\n } catch {\n return String(a);\n }\n}\n\nfunction extractTextFromResult(result: AgentToolResult<unknown>): string {\n return result.content\n .filter((c): c is { type: 'text'; text: string } => c.type === 'text')\n .map((c) => c.text)\n .join('');\n}\n\nexport function buildSandboxToolMap(tools: AgentTool<any, any>[]): Map<string, AgentTool<any, any>> {\n const m = new Map<string, AgentTool<any, any>>();\n for (const t of tools) {\n if (SANDBOX_ALLOWED_TOOLS.has(t.name)) {\n m.set(t.name, t);\n }\n }\n return m;\n}\n\nfunction createToolsApi(\n getMap: () => Map<string, AgentTool<any, any>>,\n signal: AbortSignal | undefined,\n): Record<string, (...args: unknown[]) => Promise<string>> {\n let seq = 0;\n let calls = 0;\n\n const bump = (): void => {\n calls += 1;\n if (calls > MAX_TOOL_CALLS) {\n throw new Error(`Exceeded max sandbox tool calls (${MAX_TOOL_CALLS})`);\n }\n };\n\n const run = async (name: string, params: Record<string, unknown>): Promise<string> => {\n if (signal?.aborted) {\n throw new Error('aborted');\n }\n bump();\n const tool = getMap().get(name);\n if (!tool) {\n throw new Error(`Tool not available in sandbox: ${name}`);\n }\n const id = `ptc-${Date.now()}-${seq++}`;\n const result = await (tool as any).execute(id, params, signal);\n return extractTextFromResult(result);\n };\n\n return {\n web_search: async (query: unknown, count?: unknown) =>\n run('web_search', {\n query: String(query ?? ''),\n ...(typeof count === 'number' && Number.isFinite(count) ? { count } : {}),\n }),\n\n web_fetch: async (url: unknown, maxChars?: unknown) =>\n run('web_fetch', {\n url: String(url ?? ''),\n ...(typeof maxChars === 'number' && Number.isFinite(maxChars) ? { maxChars } : {}),\n }),\n\n read_file: async (path: unknown, limit?: unknown) =>\n run('read_file', {\n path: String(path ?? ''),\n ...(typeof limit === 'number' && Number.isFinite(limit) ? { limit } : {}),\n }),\n\n write_file: async (path: unknown, content: unknown) =>\n run('write_file', {\n path: String(path ?? ''),\n content: String(content ?? ''),\n }),\n\n grep: async (pattern: unknown, opts?: unknown) => {\n const o = opts && typeof opts === 'object' && opts !== null ? (opts as Record<string, unknown>) : {};\n return run('grep', {\n pattern: String(pattern ?? ''),\n ...(typeof o.path === 'string' ? { path: o.path } : {}),\n ...(typeof o.glob === 'string' ? { glob: o.glob } : {}),\n ...(typeof o.ignoreCase === 'boolean' ? { ignoreCase: o.ignoreCase } : {}),\n ...(typeof o.literal === 'boolean' ? { literal: o.literal } : {}),\n ...(typeof o.context === 'number' && Number.isFinite(o.context) ? { context: o.context } : {}),\n ...(typeof o.limit === 'number' && Number.isFinite(o.limit) ? { limit: o.limit } : {}),\n });\n },\n\n find: async (pattern: unknown, opts?: unknown) => {\n const o = opts && typeof opts === 'object' && opts !== null ? (opts as Record<string, unknown>) : {};\n return run('find', {\n pattern: String(pattern ?? ''),\n ...(typeof o.path === 'string' ? { path: o.path } : {}),\n ...(typeof o.limit === 'number' && Number.isFinite(o.limit) ? { limit: o.limit } : {}),\n });\n },\n\n shell: async (command: unknown) => run('shell', { command: String(command ?? '') }),\n };\n}\n\nasync function runSandboxedScript(\n code: string,\n sandbox: Record<string, unknown>,\n timeoutMs: number,\n signal?: AbortSignal,\n): Promise<void> {\n const context = createContext(sandbox, { name: 'execute_code' });\n const wrapped = `(async () => {\\n${code}\\n})()`;\n const script = new Script(wrapped, { filename: 'execute_code.vm' });\n const runPromise = script.runInContext(context) as Promise<unknown>;\n\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n\n const timeoutPromise = new Promise<never>((_, rej) => {\n timeoutId = setTimeout(\n () => rej(new Error(`Script timed out after ${Math.round(timeoutMs / 1000)}s`)),\n timeoutMs,\n );\n });\n\n const abortPromise =\n signal &&\n new Promise<never>((_, rej) => {\n if (signal.aborted) {\n rej(new Error('aborted'));\n return;\n }\n signal.addEventListener('abort', () => rej(new Error('aborted')), { once: true });\n });\n\n const racers: Promise<unknown>[] = [runPromise, timeoutPromise];\n if (abortPromise) {\n racers.push(abortPromise);\n }\n\n try {\n await Promise.race(racers);\n } finally {\n if (timeoutId !== undefined) {\n clearTimeout(timeoutId);\n }\n }\n}\n\nexport interface ExecuteCodeToolDeps {\n getSandboxToolMap: () => Map<string, AgentTool<any, any>>;\n}\n\ntype ExecuteCodeParams = { code: string; timeout?: number };\n\nexport function createExecuteCodeTool(deps: ExecuteCodeToolDeps): AgentTool {\n return {\n name: 'execute_code',\n label: '⚡ Execute Code',\n description:\n 'Run sandboxed JavaScript that calls a subset of tools via `tools.*` (batch work in one step).\\n\\n' +\n 'Only stdout/stderr from `console` plus tool return text (as strings) are visible — not full tool JSON.\\n\\n' +\n 'WHEN TO USE: loops over files/URLs, simple branching between tool calls.\\n' +\n 'WHEN NOT TO USE: single tool calls; tasks needing full tool schemas or disallowed tools.\\n\\n' +\n 'API: `await tools.web_search(q, count?)`, `web_fetch(url, maxChars?)`, `read_file(path, limit?)`, ' +\n '`write_file(path, content)`, `grep(pattern, opts?)`, `find(glob, opts?)`, `shell(command)`. ' +\n 'Use `console.log` for output.',\n parameters: ExecuteCodeSchema,\n\n async execute(\n _toolCallId: string,\n params: any,\n signal?: AbortSignal,\n ): Promise<AgentToolResult<{ exitCode: number }>> {\n const p = params as ExecuteCodeParams;\n const sec = p.timeout ?? DEFAULT_TIMEOUT_SEC;\n const timeoutMs = Math.min(\n Math.max(1, Number.isFinite(sec) ? sec : DEFAULT_TIMEOUT_SEC) * 1000,\n MAX_TIMEOUT_MS,\n );\n\n const stdout: string[] = [];\n const stderr: string[] = [];\n\n const toolsApi = createToolsApi(deps.getSandboxToolMap, signal);\n\n const sandbox: Record<string, unknown> = {\n tools: toolsApi,\n Promise,\n JSON,\n Math,\n Date,\n Array,\n Object,\n String,\n Number,\n Boolean,\n RegExp,\n Map,\n Set,\n Error,\n TypeError,\n RangeError,\n parseInt,\n parseFloat,\n isNaN,\n isFinite,\n console: {\n log: (...args: unknown[]) => {\n stdout.push(args.map(stringifyArg).join(' '));\n },\n error: (...args: unknown[]) => {\n stderr.push(args.map(stringifyArg).join(' '));\n },\n warn: (...args: unknown[]) => {\n stderr.push(args.map(stringifyArg).join(' '));\n },\n },\n setTimeout,\n clearTimeout,\n };\n\n try {\n await runSandboxedScript(p.code, sandbox, timeoutMs, signal);\n\n let out = stdout.join('\\n');\n if (out.length > MAX_STDOUT_CHARS) {\n out = `${out.slice(0, MAX_STDOUT_CHARS)}\\n...(truncated)`;\n }\n\n const parts: string[] = [];\n if (out.length > 0) {\n parts.push(out);\n }\n if (stderr.length > 0) {\n let err = stderr.join('\\n');\n if (err.length > MAX_STDERR_CHARS) {\n err = `${err.slice(0, MAX_STDERR_CHARS)}\\n...(truncated)`;\n }\n parts.push(`\\nSTDERR:\\n${err}`);\n }\n\n return {\n content: [{ type: 'text', text: parts.join('') || '(no output)' }],\n details: { exitCode: 0 },\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return {\n content: [{ type: 'text', text: `Execution failed: ${message}` }],\n details: { exitCode: 1 },\n };\n }\n },\n } as any;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"execute-code-tool.js","names":[],"sources":["../../../../src/agent/tools/execute-code-tool.ts"],"sourcesContent":["/**\n * Programmatic tool calling (PTC): run sandboxed JS that invokes a subset of agent tools.\n * vm is not a strong security boundary; only trusted models should use this tool.\n */\n\nimport { Script, createContext } from 'node:vm';\n\nimport { Type } from '@sinclair/typebox';\nimport type { AgentTool, AgentToolResult } from '@earendil-works/pi-agent-core';\n\n/** Default script wall time when `timeout` omitted (30 minutes). */\nconst DEFAULT_TIMEOUT_SEC = 30 * 60;\n/** Hard cap for `timeout` parameter (4 hours). */\nconst MAX_TIMEOUT_SEC = 4 * 60 * 60;\n\nconst ExecuteCodeSchema = Type.Object({\n code: Type.String({\n description:\n 'JavaScript to run. Exposed as `tools` (async functions) and `console`:\\n' +\n ' await tools.web_search(query, count?)\\n' +\n ' await tools.web_fetch(url, maxChars?)\\n' +\n ' await tools.read_file(path, limit?)\\n' +\n ' await tools.write_file(path, content)\\n' +\n ' await tools.grep(pattern, { path?, glob?, ignoreCase?, literal?, context?, limit? })\\n' +\n ' await tools.find(pattern, { path?, limit? })\\n' +\n ' await tools.exec_command(cmd, cwd?)\\n\\n' +\n 'Use console.log() for output; combined stdout/stderr is returned.',\n }),\n timeout: Type.Optional(\n Type.Number({\n description: 'Execution timeout in seconds (default: 3600 = 60m, max: 14400 = 4h)',\n default: DEFAULT_TIMEOUT_SEC,\n }),\n ),\n});\n\nexport const SANDBOX_ALLOWED_TOOLS = new Set([\n 'web_search',\n 'web_fetch',\n 'read_file',\n 'write_file',\n 'grep',\n 'find',\n 'exec_command',\n 'skills_list',\n 'skill_view',\n]);\n\nconst MAX_TIMEOUT_MS = MAX_TIMEOUT_SEC * 1000;\nconst MAX_TOOL_CALLS = 50;\nconst MAX_STDOUT_CHARS = 50_000;\nconst MAX_STDERR_CHARS = 10_000;\n\nfunction stringifyArg(a: unknown): string {\n if (typeof a === 'string') {\n return a;\n }\n try {\n return JSON.stringify(a);\n } catch {\n return String(a);\n }\n}\n\nfunction extractTextFromResult(result: AgentToolResult<unknown>): string {\n return result.content\n .filter((c): c is { type: 'text'; text: string } => c.type === 'text')\n .map((c) => c.text)\n .join('');\n}\n\nexport function buildSandboxToolMap(tools: AgentTool<any, any>[]): Map<string, AgentTool<any, any>> {\n const m = new Map<string, AgentTool<any, any>>();\n for (const t of tools) {\n if (SANDBOX_ALLOWED_TOOLS.has(t.name)) {\n m.set(t.name, t);\n }\n }\n return m;\n}\n\nfunction createToolsApi(\n getMap: () => Map<string, AgentTool<any, any>>,\n signal: AbortSignal | undefined,\n): Record<string, (...args: unknown[]) => Promise<string>> {\n let seq = 0;\n let calls = 0;\n\n const bump = (): void => {\n calls += 1;\n if (calls > MAX_TOOL_CALLS) {\n throw new Error(`Exceeded max sandbox tool calls (${MAX_TOOL_CALLS})`);\n }\n };\n\n const run = async (name: string, params: Record<string, unknown>): Promise<string> => {\n if (signal?.aborted) {\n throw new Error('aborted');\n }\n bump();\n const tool = getMap().get(name);\n if (!tool) {\n throw new Error(`Tool not available in sandbox: ${name}`);\n }\n const id = `ptc-${Date.now()}-${seq++}`;\n const result = await (tool as any).execute(id, params, signal);\n return extractTextFromResult(result);\n };\n\n return {\n web_search: async (query: unknown, count?: unknown) =>\n run('web_search', {\n query: String(query ?? ''),\n ...(typeof count === 'number' && Number.isFinite(count) ? { count } : {}),\n }),\n\n web_fetch: async (url: unknown, maxChars?: unknown) =>\n run('web_fetch', {\n url: String(url ?? ''),\n ...(typeof maxChars === 'number' && Number.isFinite(maxChars) ? { maxChars } : {}),\n }),\n\n read_file: async (path: unknown, limit?: unknown) =>\n run('read_file', {\n path: String(path ?? ''),\n ...(typeof limit === 'number' && Number.isFinite(limit) ? { limit } : {}),\n }),\n\n write_file: async (path: unknown, content: unknown) =>\n run('write_file', {\n path: String(path ?? ''),\n content: String(content ?? ''),\n }),\n\n grep: async (pattern: unknown, opts?: unknown) => {\n const o = opts && typeof opts === 'object' && opts !== null ? (opts as Record<string, unknown>) : {};\n return run('grep', {\n pattern: String(pattern ?? ''),\n ...(typeof o.path === 'string' ? { path: o.path } : {}),\n ...(typeof o.glob === 'string' ? { glob: o.glob } : {}),\n ...(typeof o.ignoreCase === 'boolean' ? { ignoreCase: o.ignoreCase } : {}),\n ...(typeof o.literal === 'boolean' ? { literal: o.literal } : {}),\n ...(typeof o.context === 'number' && Number.isFinite(o.context) ? { context: o.context } : {}),\n ...(typeof o.limit === 'number' && Number.isFinite(o.limit) ? { limit: o.limit } : {}),\n });\n },\n\n find: async (pattern: unknown, opts?: unknown) => {\n const o = opts && typeof opts === 'object' && opts !== null ? (opts as Record<string, unknown>) : {};\n return run('find', {\n pattern: String(pattern ?? ''),\n ...(typeof o.path === 'string' ? { path: o.path } : {}),\n ...(typeof o.limit === 'number' && Number.isFinite(o.limit) ? { limit: o.limit } : {}),\n });\n },\n\n exec_command: async (cmd: unknown, cwd?: unknown) =>\n run('exec_command', {\n cmd: String(cmd ?? ''),\n ...(typeof cwd === 'string' && cwd.trim() ? { cwd } : {}),\n }),\n };\n}\n\nasync function runSandboxedScript(\n code: string,\n sandbox: Record<string, unknown>,\n timeoutMs: number,\n signal?: AbortSignal,\n): Promise<void> {\n const context = createContext(sandbox, { name: 'execute_code' });\n const wrapped = `(async () => {\\n${code}\\n})()`;\n const script = new Script(wrapped, { filename: 'execute_code.vm' });\n const runPromise = script.runInContext(context) as Promise<unknown>;\n\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n\n const timeoutPromise = new Promise<never>((_, rej) => {\n timeoutId = setTimeout(\n () => rej(new Error(`Script timed out after ${Math.round(timeoutMs / 1000)}s`)),\n timeoutMs,\n );\n });\n\n const abortPromise =\n signal &&\n new Promise<never>((_, rej) => {\n if (signal.aborted) {\n rej(new Error('aborted'));\n return;\n }\n signal.addEventListener('abort', () => rej(new Error('aborted')), { once: true });\n });\n\n const racers: Promise<unknown>[] = [runPromise, timeoutPromise];\n if (abortPromise) {\n racers.push(abortPromise);\n }\n\n try {\n await Promise.race(racers);\n } finally {\n if (timeoutId !== undefined) {\n clearTimeout(timeoutId);\n }\n }\n}\n\nexport interface ExecuteCodeToolDeps {\n getSandboxToolMap: () => Map<string, AgentTool<any, any>>;\n}\n\ntype ExecuteCodeParams = { code: string; timeout?: number };\n\nexport function createExecuteCodeTool(deps: ExecuteCodeToolDeps): AgentTool {\n return {\n name: 'execute_code',\n label: '⚡ Execute Code',\n description:\n 'Run sandboxed JavaScript that calls a subset of tools via `tools.*` (batch work in one step).\\n\\n' +\n 'Only stdout/stderr from `console` plus tool return text (as strings) are visible — not full tool JSON.\\n\\n' +\n 'WHEN TO USE: loops over files/URLs, simple branching between tool calls.\\n' +\n 'WHEN NOT TO USE: single tool calls; tasks needing full tool schemas or disallowed tools.\\n\\n' +\n 'API: `await tools.web_search(q, count?)`, `web_fetch(url, maxChars?)`, `read_file(path, limit?)`, ' +\n '`write_file(path, content)`, `grep(pattern, opts?)`, `find(glob, opts?)`, `exec_command(cmd, cwd?)`. ' +\n 'Use `console.log` for output.',\n parameters: ExecuteCodeSchema,\n\n async execute(\n _toolCallId: string,\n params: any,\n signal?: AbortSignal,\n ): Promise<AgentToolResult<{ exitCode: number }>> {\n const p = params as ExecuteCodeParams;\n const sec = p.timeout ?? DEFAULT_TIMEOUT_SEC;\n const timeoutMs = Math.min(\n Math.max(1, Number.isFinite(sec) ? sec : DEFAULT_TIMEOUT_SEC) * 1000,\n MAX_TIMEOUT_MS,\n );\n\n const stdout: string[] = [];\n const stderr: string[] = [];\n\n const toolsApi = createToolsApi(deps.getSandboxToolMap, signal);\n\n const sandbox: Record<string, unknown> = {\n tools: toolsApi,\n Promise,\n JSON,\n Math,\n Date,\n Array,\n Object,\n String,\n Number,\n Boolean,\n RegExp,\n Map,\n Set,\n Error,\n TypeError,\n RangeError,\n parseInt,\n parseFloat,\n isNaN,\n isFinite,\n console: {\n log: (...args: unknown[]) => {\n stdout.push(args.map(stringifyArg).join(' '));\n },\n error: (...args: unknown[]) => {\n stderr.push(args.map(stringifyArg).join(' '));\n },\n warn: (...args: unknown[]) => {\n stderr.push(args.map(stringifyArg).join(' '));\n },\n },\n setTimeout,\n clearTimeout,\n };\n\n try {\n await runSandboxedScript(p.code, sandbox, timeoutMs, signal);\n\n let out = stdout.join('\\n');\n if (out.length > MAX_STDOUT_CHARS) {\n out = `${out.slice(0, MAX_STDOUT_CHARS)}\\n...(truncated)`;\n }\n\n const parts: string[] = [];\n if (out.length > 0) {\n parts.push(out);\n }\n if (stderr.length > 0) {\n let err = stderr.join('\\n');\n if (err.length > MAX_STDERR_CHARS) {\n err = `${err.slice(0, MAX_STDERR_CHARS)}\\n...(truncated)`;\n }\n parts.push(`\\nSTDERR:\\n${err}`);\n }\n\n return {\n content: [{ type: 'text', text: parts.join('') || '(no output)' }],\n details: { exitCode: 0 },\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return {\n content: [{ type: 'text', text: `Execution failed: ${message}` }],\n details: { exitCode: 1 },\n };\n }\n },\n } as any;\n}\n"],"mappings":"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"}
@@ -51,17 +51,17 @@ export interface ToolFactoryDeps {
51
51
  } | undefined;
52
52
  /** After skill_manage mutates disk, reload skills + refresh agent prompts (optional). */
53
53
  onSkillsFilesystemMutate?: () => void;
54
- /** Names registered via skill_view for shell env passthrough. */
54
+ /** Names registered via skill_view for command env passthrough. */
55
55
  getSkillPassthroughEnvVarNames?: () => string[];
56
56
  /** Add declared env names for the current session (no values stored). */
57
57
  registerSkillEnvPassthrough?: (names: string[]) => void;
58
58
  }
59
59
  export interface CreateCoreToolsOptions {
60
- /** Workspace root for file/shell tools (defaults to factory workspace). */
60
+ /** Workspace root for file/command tools (defaults to factory workspace). */
61
61
  workspace?: string;
62
62
  /** Canonical `agents/<id>/profile/`: bare SOUL.md / IDENTITY.md resolve here after the workspace. */
63
63
  profileMarkdownRoot?: string;
64
- /** Tool `name` values to omit (e.g. `shell`, `extensions` for extension tools). */
64
+ /** Tool `name` values to omit (e.g. `exec_command`, `extensions` for extension tools). */
65
65
  disabledTools?: Set<string>;
66
66
  /** Optional primary model for image tool heuristics. */
67
67
  getPrimaryModel?: () => Model<Api>;
@@ -2,9 +2,9 @@ import { createLogger } from "../../utils/logger/index.js";
2
2
  import { init_logger } from "../../utils/logger.js";
3
3
  import { createReadFileTool } from "./read.js";
4
4
  import { createWriteFileTool } from "./write.js";
5
- import { createEditFileTool } from "./edit.js";
6
5
  import { createListDirTool } from "./list-dir.js";
7
- import { createShellTool } from "./shell.js";
6
+ import { createExecCommandTool } from "./exec-command.js";
7
+ import { createApplyPatchTool } from "./apply-patch.js";
8
8
  import { createMemoryGetTool, createMemorySearchTool } from "./memory-tool.js";
9
9
  import { createCuratedMemoryTool } from "./curated-memory-tool.js";
10
10
  import { createSessionSearchTool } from "./session-search-tool.js";
@@ -18,6 +18,7 @@ import { createSendMediaTool } from "./send-media.js";
18
18
  import { createReadMediaTool } from "./media-read-tool.js";
19
19
  import { createCreateShareTool, isShareToolAvailable } from "./create-share-tool.js";
20
20
  import { createTodoTool } from "./todo-tool.js";
21
+ import { createUpdatePlanTool } from "./update-plan-tool.js";
21
22
  import { createSessionStatusTool } from "./session-status-tool.js";
22
23
  import { createDreamingTool } from "./dreaming-tool.js";
23
24
  import { createClarifyTool } from "./clarify-tool.js";
@@ -170,10 +171,7 @@ var AgentToolsFactory = class {
170
171
  profileMarkdownRoot: options?.profileMarkdownRoot,
171
172
  recordGoalEvidence
172
173
  });
173
- const editTool = createEditFileTool(workspace, {
174
- profileMarkdownRoot: options?.profileMarkdownRoot,
175
- recordGoalEvidence
176
- });
174
+ const applyPatchTool = createApplyPatchTool(workspace, { recordGoalEvidence });
177
175
  const listDir = createListDirTool(workspace);
178
176
  const grep = createGrepTool(workspace);
179
177
  const find = createFindTool(workspace);
@@ -193,6 +191,7 @@ var AgentToolsFactory = class {
193
191
  return (r) => req(ctx.sessionKey, r);
194
192
  } }),
195
193
  createTodoTool({ getSessionKey: () => this.deps.getCurrentContext()?.sessionKey }),
194
+ createUpdatePlanTool(),
196
195
  ...getSkillMgr ? [
197
196
  createSkillsListTool({
198
197
  getSkillManager: getSkillMgr,
@@ -211,11 +210,11 @@ var AgentToolsFactory = class {
211
210
  ] : [],
212
211
  readTool,
213
212
  writeTool,
214
- editTool,
213
+ applyPatchTool,
215
214
  listDir,
216
215
  grep,
217
216
  find,
218
- createShellTool(workspace, {
217
+ createExecCommandTool(workspace, {
219
218
  getSkillPassthroughEnvVarNames: this.deps.getSkillPassthroughEnvVarNames,
220
219
  recordGoalEvidence
221
220
  }),
@@ -249,7 +248,7 @@ var AgentToolsFactory = class {
249
248
  getCurrentSessionKey: () => this.deps.getCurrentContext()?.sessionKey
250
249
  })] : [],
251
250
  ...this.deps.getAutomationService ? [createAutomationTool({ getAutomationService: this.deps.getAutomationService })] : [],
252
- createGoalTool(),
251
+ createGoalTool({ getCurrentSessionKey: () => this.deps.getCurrentContext()?.sessionKey }),
253
252
  ...browserEnabled ? [createBrowserUseTool({
254
253
  getManager: () => this.ensureBrowserManager(),
255
254
  getPageForTask: () => this.acquireBrowserPage(),
@@ -1 +1 @@
1
- {"version":3,"file":"factory.js","names":[],"sources":["../../../../src/agent/tools/factory.ts"],"sourcesContent":["/**\n * Agent Tools Factory - Creates and configures agent tools\n *\n * Centralizes tool creation logic to keep service.ts focused on orchestration.\n *\n * TTS: auto TTS is applied at the ChannelManager via maybeApplyTtsToPayload().\n * Optional \\`text_to_speech\\` tool sends explicit voice when TTS is enabled.\n */\n\nimport type { AgentTool } from '@earendil-works/pi-agent-core';\nimport type { Model, Api } from '@earendil-works/pi-ai';\nimport type { Page } from 'playwright-core';\nimport type { Config } from '../../config/schema.js';\nimport type { MessageBus } from '../../infra/bus/index.js';\nimport {\n createReadFileTool,\n createWriteFileTool,\n createEditFileTool,\n createListDirTool,\n createGrepTool,\n createFindTool,\n createShellTool,\n createWebSearchTool,\n createWebFetchTool,\n createWebExtractTool,\n createMessageTool,\n createComposioExecuteTool,\n createSendMediaTool,\n createReadMediaTool,\n createCreateShareTool,\n isShareToolAvailable,\n createMemorySearchTool,\n createMemoryGetTool,\n createTodoTool,\n createSessionStatusTool,\n createDreamingTool,\n createClarifyTool,\n createToolManualTool,\n createGoalTool,\n createAutomationTool,\n} from './index.js';\nimport { createCuratedMemoryTool } from './curated-memory-tool.js';\nimport { createSessionSearchTool } from './session-search-tool.js';\nimport type { MemoryManager } from '../memory/manager.js';\nimport { shouldRegisterCuratedMemoryTool } from '../memory/memory-config.js';\nimport type { SessionStore } from '../../session/store.js';\nimport type { GatewayClarifyRequestFn } from './clarify-tool.js';\nimport { createImageTool } from './image-tool.js';\nimport { createImageGenerateTool } from './image-generate-tool.js';\nimport {\n BrowserManager,\n BrowserNotReadyError,\n CdpSupervisor,\n checkBrowserReadiness,\n resolveBrowserBackendFromConfig,\n} from '../../browser/index.js';\nimport { createBrowserUseTool } from './browser/tool/browser-use-tool.js';\nimport { createDelegateTool } from './delegate-tool.js';\nimport { createWorkflowTool } from './workflow-tool.js';\nimport { createWorkflowCatalog } from '../workflow/catalog.js';\nimport { buildSandboxToolMap, createExecuteCodeTool } from './execute-code-tool.js';\nimport type { AutomationService } from '../../automations/index.js';\nimport type { WorkflowRunServiceLike } from '../../workflows/service/workflow-run-service.types.js';\nimport { createLogger } from '../../utils/logger.js';\nimport type { SkillManager } from '../skills/skill-manager.js';\nimport { wrapToolsWithProtection, type ToolExecutorConfig } from './executor.js';\nimport { createSkillsListTool, createSkillViewTool } from './skills-tools.js';\nimport { createSkillManageTool } from './skill-manage-tool.js';\nimport { createTextToSpeechTool } from './tts-tool.js';\nimport { mergeTtsConfigFromAppConfig } from '../../voice/tts/merge-config.js';\nimport { createGoalEvidenceRecorder } from './goal-evidence-recorder.js';\n\nconst log = createLogger('AgentToolsFactory');\n\n/** Channels where `clarify` can block for a user answer (web UI, Telegram, CLI readline). */\nconst CLARIFY_SUPPORTED_CHANNELS = new Set(['webchat', 'telegram', 'cli']);\n\nexport interface ToolFactoryDeps {\n workspace: string;\n extensionRegistry?: any;\n getCurrentContext: () => { channel: string; chatId: string; sessionKey: string } | null;\n hookRunner?: import('../../extensions/index.js').ExtensionHookRunner;\n bus: MessageBus;\n toolExecutorConfig?: Partial<ToolExecutorConfig>;\n /** Agent defaults (image tools, etc.); use getter so hot-reloaded config applies. */\n getConfig?: () => Config | undefined;\n /** Session / default chat model for vision tool description. */\n getPrimaryModel?: () => Model<Api>;\n /** Memory orchestration (prefetch/sync + external tools). */\n getMemoryManager?: () => MemoryManager;\n /** Session store for `session_search`. */\n getSessionStore?: () => SessionStore;\n /** When set (gateway webchat), enables the `clarify` tool. */\n gatewayClarify?: { requestClarification: GatewayClarifyRequestFn };\n /** Gateway: enables the `automation` tool. */\n getAutomationService?: () => AutomationService | undefined;\n /** Gateway: starts persisted workflow runs (dedicated chat session per run). */\n getWorkflowRunService?: () => WorkflowRunServiceLike | undefined;\n /** Current session skill indexing (tool gating + allowlist); used by skills_list / skill_view. */\n getSkillIndexingContext?: () =>\n | { registeredToolNames: string[]; skillAllowlist?: string[] }\n | undefined;\n /** After skill_manage mutates disk, reload skills + refresh agent prompts (optional). */\n onSkillsFilesystemMutate?: () => void;\n /** Names registered via skill_view for shell env passthrough. */\n getSkillPassthroughEnvVarNames?: () => string[];\n /** Add declared env names for the current session (no values stored). */\n registerSkillEnvPassthrough?: (names: string[]) => void;\n}\n\nexport interface CreateCoreToolsOptions {\n /** Workspace root for file/shell tools (defaults to factory workspace). */\n workspace?: string;\n /** Canonical `agents/<id>/profile/`: bare SOUL.md / IDENTITY.md resolve here after the workspace. */\n profileMarkdownRoot?: string;\n /** Tool `name` values to omit (e.g. `shell`, `extensions` for extension tools). */\n disabledTools?: Set<string>;\n /** Optional primary model for image tool heuristics. */\n getPrimaryModel?: () => Model<Api>;\n getMemoryManager?: () => MemoryManager;\n /** When set, registers `skills_list` and `skill_view` bound to this workspace\\'s skills. */\n getSkillManager?: () => SkillManager;\n}\n\nexport class AgentToolsFactory {\n private browserManager: BrowserManager | null = null;\n /** One dialog/console supervisor per chat session (browser tab). */\n private readonly browserTaskSupervisors = new Map<string, CdpSupervisor>();\n /** Cached readiness probe — keyed by backend mode + extension host:port. */\n private browserReadinessCache: {\n key: string;\n expiresAt: number;\n inflight?: Promise<BrowserNotReadyError | null>;\n result?: BrowserNotReadyError | null;\n } | null = null;\n\n constructor(private deps: ToolFactoryDeps) {}\n\n private browserReadinessKey(): string {\n const cfg = this.deps.getConfig?.();\n const backend = resolveBrowserBackendFromConfig(cfg);\n const host = '127.0.0.1';\n const port = 19820;\n const cdpUrl = backend.mode === 'cdp' ? backend.config.wsEndpoint : '';\n const cloudKind = backend.mode === 'cloud' ? backend.config.type : '';\n return `${backend.mode}@${host}:${port}|${cdpUrl}|${cloudKind}`;\n }\n\n private async checkBrowserReadinessCached(): Promise<BrowserNotReadyError | null> {\n const key = this.browserReadinessKey();\n const now = Date.now();\n const cached = this.browserReadinessCache;\n if (cached && cached.key === key && cached.expiresAt > now && cached.inflight === undefined) {\n return cached.result ?? null;\n }\n if (cached && cached.key === key && cached.inflight) {\n return cached.inflight;\n }\n const inflight = checkBrowserReadiness(this.deps.getConfig?.());\n this.browserReadinessCache = { key, expiresAt: now + 30_000, inflight };\n try {\n const result = await inflight;\n this.browserReadinessCache = { key, expiresAt: Date.now() + 30_000, result };\n return result;\n } catch (e) {\n // Probe should never throw, but if it does we just bypass the cache.\n this.browserReadinessCache = null;\n log.warn({ err: e }, 'browserReadiness probe failed');\n return null;\n }\n }\n\n /** Invalidate the readiness cache (config hot-reload, settings-page save, etc.). */\n invalidateBrowserReadinessCache(): void {\n this.browserReadinessCache = null;\n }\n\n private browserSupervisorForTask(taskId: string): CdpSupervisor {\n let s = this.browserTaskSupervisors.get(taskId);\n if (!s) {\n s = new CdpSupervisor({ dialogPolicy: 'auto_dismiss', dialogTimeoutSeconds: 300 });\n this.browserTaskSupervisors.set(taskId, s);\n }\n return s;\n }\n\n private async acquireBrowserPage(): Promise<Page> {\n const taskId = this.deps.getCurrentContext()?.sessionKey ?? 'default';\n const mgr = this.ensureBrowserManager();\n await mgr.ensureConnected();\n if (mgr.getExtensionProvider()) {\n return null as unknown as Page;\n }\n const page = await mgr.getPage(taskId);\n this.browserSupervisorForTask(taskId).attach(page);\n return page;\n }\n\n private ensureBrowserManager(): BrowserManager {\n if (!this.browserManager) {\n this.browserManager = new BrowserManager({\n getHeadless: () => false,\n getBackend: () => resolveBrowserBackendFromConfig(this.deps.getConfig?.()),\n });\n }\n return this.browserManager;\n }\n\n /** Close Playwright and all pages (gateway stop, agent manager dispose, or config hot-reload). */\n async shutdownBrowser(): Promise<void> {\n this.browserReadinessCache = null;\n if (!this.browserManager) {\n return;\n }\n await this.browserManager.shutdown();\n this.browserManager = null;\n this.browserTaskSupervisors.clear();\n }\n\n /** Drop the tab for a session when its agent instance is removed. */\n async closeBrowserPageForSession(sessionKey: string): Promise<void> {\n this.browserTaskSupervisors.delete(sessionKey);\n await this.browserManager?.closePage(sessionKey);\n }\n\n createCoreTools(options?: CreateCoreToolsOptions): AgentTool<any, any>[] {\n const workspace = options?.workspace ?? this.deps.workspace;\n const { bus } = this.deps;\n const getPrimary = options?.getPrimaryModel ?? this.deps.getPrimaryModel;\n const getMemMgr = options?.getMemoryManager ?? this.deps.getMemoryManager;\n const getSkillMgr = options?.getSkillManager;\n const disabled = options?.disabledTools;\n\n const primary = getPrimary?.();\n const modelHasVision = primary?.input?.includes('image') ?? false;\n const cfg = this.deps.getConfig?.();\n const browserEnabled = cfg?.browser?.enabled !== false;\n const recordGoalEvidence = createGoalEvidenceRecorder({\n getSessionKey: () => this.deps.getCurrentContext()?.sessionKey,\n });\n const imageTool = createImageTool({\n config: cfg,\n workspace,\n modelHasVision,\n });\n const imageGenerateTool = createImageGenerateTool({\n config: cfg,\n workspace,\n });\n\n const composioTool = createComposioExecuteTool(() => this.deps.getConfig?.());\n const optionalTools = [imageTool, imageGenerateTool, composioTool].filter((t) => t != null) as any[];\n\n const readTool = createReadFileTool(workspace, {\n profileMarkdownRoot: options?.profileMarkdownRoot,\n });\n const writeTool = createWriteFileTool(workspace, {\n profileMarkdownRoot: options?.profileMarkdownRoot,\n recordGoalEvidence,\n });\n const editTool = createEditFileTool(workspace, {\n profileMarkdownRoot: options?.profileMarkdownRoot,\n recordGoalEvidence,\n });\n const listDir = createListDirTool(workspace);\n const grep = createGrepTool(workspace);\n const find = createFindTool(workspace);\n\n const core: AgentTool<any, any>[] = [\n createSessionStatusTool(),\n createDreamingTool({\n getWorkspace: () => workspace,\n getConfig: () => this.deps.getConfig?.(),\n }),\n createToolManualTool(),\n createClarifyTool({\n resolveAskUser: () => {\n const req = this.deps.gatewayClarify?.requestClarification;\n if (!req) return null;\n const ctx = this.deps.getCurrentContext();\n if (!ctx?.sessionKey) return null;\n if (!CLARIFY_SUPPORTED_CHANNELS.has(ctx.channel)) return null;\n return (r) => req(ctx.sessionKey, r);\n },\n }),\n createTodoTool({\n getSessionKey: () => this.deps.getCurrentContext()?.sessionKey,\n }),\n ...(getSkillMgr\n ? [\n createSkillsListTool({\n getSkillManager: getSkillMgr,\n getSkillIndexingContext: this.deps.getSkillIndexingContext,\n }),\n createSkillViewTool({\n getSkillManager: getSkillMgr,\n getSkillIndexingContext: this.deps.getSkillIndexingContext,\n registerSkillEnvPassthrough: this.deps.registerSkillEnvPassthrough,\n }),\n createSkillManageTool({\n getSkillManager: getSkillMgr,\n getWorkspace: () => workspace,\n onSkillsFilesystemMutate: this.deps.onSkillsFilesystemMutate,\n }),\n ]\n : []),\n readTool,\n writeTool,\n editTool,\n listDir,\n grep,\n find,\n createShellTool(workspace, {\n getSkillPassthroughEnvVarNames: this.deps.getSkillPassthroughEnvVarNames,\n recordGoalEvidence,\n }),\n createWebSearchTool(() => this.deps.getConfig?.()),\n createWebFetchTool(() => this.deps.getConfig?.()),\n createWebExtractTool({ getConfig: () => this.deps.getConfig?.() }),\n // Note: TTS is NOT handled by send_message tool anymore\n // TTS is applied at the ChannelManager dispatch layer\n createMessageTool(bus, () => this.deps.getCurrentContext()),\n ...(mergeTtsConfigFromAppConfig(cfg?.messages?.tts).enabled\n ? [\n createTextToSpeechTool({\n bus,\n getContext: () => this.deps.getCurrentContext(),\n getConfig: () => this.deps.getConfig?.(),\n }),\n ]\n : []),\n createSendMediaTool(workspace, bus, () => this.deps.getCurrentContext()),\n createReadMediaTool(),\n ...(isShareToolAvailable(cfg)\n ? [\n createCreateShareTool({\n workspace,\n getConfig: () => this.deps.getConfig?.(),\n }),\n ]\n : []),\n ...(getMemMgr\n ? [\n createMemorySearchTool({ workspaceDir: workspace, getMemoryManager: () => getMemMgr() }),\n createMemoryGetTool({ workspaceDir: workspace, getMemoryManager: () => getMemMgr() }),\n ]\n : []),\n ...(getMemMgr && shouldRegisterCuratedMemoryTool(this.deps.getConfig?.())\n ? [\n createCuratedMemoryTool(() => getMemMgr()),\n ]\n : []),\n ...(getMemMgr?.().getAdditionalTools() ?? []),\n ...(this.deps.getSessionStore\n ? [\n createSessionSearchTool({\n getSessionStore: this.deps.getSessionStore,\n getConfig: this.deps.getConfig,\n getCurrentSessionKey: () => this.deps.getCurrentContext()?.sessionKey,\n }),\n ]\n : []),\n ...(this.deps.getAutomationService\n ? [\n createAutomationTool({\n getAutomationService: this.deps.getAutomationService,\n }),\n ]\n : []),\n createGoalTool(),\n ...(browserEnabled\n ? [\n createBrowserUseTool({\n getManager: () => this.ensureBrowserManager(),\n getPageForTask: () => this.acquireBrowserPage(),\n getTaskId: () => this.deps.getCurrentContext()?.sessionKey ?? 'default',\n getConfig: () => this.deps.getConfig?.(),\n getReadiness: () => this.checkBrowserReadinessCached(),\n getSupervisor: () =>\n this.browserSupervisorForTask(this.deps.getCurrentContext()?.sessionKey ?? 'default'),\n notifyBrowserPageClosed: (taskId) => {\n this.browserTaskSupervisors.delete(taskId);\n },\n }),\n ]\n : []),\n ...(primary\n ? [\n createWorkflowTool({\n catalog: createWorkflowCatalog(),\n getCurrentSessionKey: () => this.deps.getCurrentContext()?.sessionKey,\n getConfig: () => this.deps.getConfig?.(),\n startWorkflowRun: this.deps.getWorkflowRunService\n ? (params) => this.deps.getWorkflowRunService!().startWorkflowRun(params)\n : undefined,\n }),\n ]\n : []),\n ...(false && primary\n ? [\n createDelegateTool({\n workspace,\n getSubagentModel: () => {\n const gp = options?.getPrimaryModel ?? this.deps.getPrimaryModel;\n const m = gp?.();\n if (!m) {\n throw new Error('No primary model configured for delegate_task');\n }\n return m;\n },\n bus: this.deps.bus,\n getConfig: () => this.deps.getConfig?.(),\n getCurrentContext: () => this.deps.getCurrentContext?.() ?? null,\n hookRunner: this.deps.hookRunner,\n toolExecutorConfig: this.deps.toolExecutorConfig,\n // Injected so `child-agent-factory.ts` does not need to import\n // `AgentToolsFactory` directly (which would form a cycle).\n buildChildTools: (childOpts) => {\n const childFactory = new AgentToolsFactory({\n workspace: childOpts.workspace,\n bus: childOpts.bus,\n getCurrentContext: () => null,\n getConfig: childOpts.getConfig,\n getPrimaryModel: () => childOpts.model,\n toolExecutorConfig: childOpts.toolExecutorConfig,\n });\n return childFactory.createAllTools({\n workspace: childOpts.workspace,\n getPrimaryModel: () => childOpts.model,\n disabledTools: new Set(['extensions']),\n });\n },\n }),\n ]\n : []),\n ...optionalTools,\n ];\n\n return filterToolsByDisabledSet(core, disabled);\n }\n\n createAllTools(coreOptions?: CreateCoreToolsOptions): AgentTool<any, any>[] {\n const coreTools = this.createCoreTools(coreOptions);\n const disableExtensions = coreOptions?.disabledTools?.has('extensions');\n\n let bundled: AgentTool<any, any>[];\n if (!this.deps.extensionRegistry || disableExtensions) {\n bundled = coreTools;\n } else {\n const extensionTools = this.deps.extensionRegistry.getAllTools();\n log.info({ count: extensionTools.length }, 'Loaded extension tools');\n bundled = [...coreTools, ...extensionTools];\n }\n\n const wrapped = wrapToolsWithProtection(bundled, this.deps.toolExecutorConfig);\n\n const executeEnabled = false && !coreOptions?.disabledTools?.has('execute_code');\n\n if (executeEnabled) {\n const sandboxMap = buildSandboxToolMap(wrapped);\n const executeTool = createExecuteCodeTool({ getSandboxToolMap: () => sandboxMap });\n const wrappedExecute = wrapToolsWithProtection([executeTool as any], this.deps.toolExecutorConfig);\n return [...wrapped, ...wrappedExecute];\n }\n\n return wrapped;\n }\n}\n\nfunction filterToolsByDisabledSet(\n tools: any[],\n disabled: Set<string> | undefined,\n): any[] {\n if (!disabled || disabled.size === 0) {\n return tools;\n }\n return tools.filter((t) => !disabled.has(t.name));\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"factory.js","names":[],"sources":["../../../../src/agent/tools/factory.ts"],"sourcesContent":["/**\n * Agent Tools Factory - Creates and configures agent tools\n *\n * Centralizes tool creation logic to keep service.ts focused on orchestration.\n *\n * TTS: auto TTS is applied at the ChannelManager via maybeApplyTtsToPayload().\n * Optional \\`text_to_speech\\` tool sends explicit voice when TTS is enabled.\n */\n\nimport type { AgentTool } from '@earendil-works/pi-agent-core';\nimport type { Model, Api } from '@earendil-works/pi-ai';\nimport type { Page } from 'playwright-core';\nimport type { Config } from '../../config/schema.js';\nimport type { MessageBus } from '../../infra/bus/index.js';\nimport {\n createReadFileTool,\n createWriteFileTool,\n createApplyPatchTool,\n createListDirTool,\n createGrepTool,\n createFindTool,\n createExecCommandTool,\n createWebSearchTool,\n createWebFetchTool,\n createWebExtractTool,\n createMessageTool,\n createComposioExecuteTool,\n createSendMediaTool,\n createReadMediaTool,\n createCreateShareTool,\n isShareToolAvailable,\n createMemorySearchTool,\n createMemoryGetTool,\n createTodoTool,\n createUpdatePlanTool,\n createSessionStatusTool,\n createDreamingTool,\n createClarifyTool,\n createToolManualTool,\n createGoalTool,\n createAutomationTool,\n} from './index.js';\nimport { createCuratedMemoryTool } from './curated-memory-tool.js';\nimport { createSessionSearchTool } from './session-search-tool.js';\nimport type { MemoryManager } from '../memory/manager.js';\nimport { shouldRegisterCuratedMemoryTool } from '../memory/memory-config.js';\nimport type { SessionStore } from '../../session/store.js';\nimport type { GatewayClarifyRequestFn } from './clarify-tool.js';\nimport { createImageTool } from './image-tool.js';\nimport { createImageGenerateTool } from './image-generate-tool.js';\nimport {\n BrowserManager,\n BrowserNotReadyError,\n CdpSupervisor,\n checkBrowserReadiness,\n resolveBrowserBackendFromConfig,\n} from '../../browser/index.js';\nimport { createBrowserUseTool } from './browser/tool/browser-use-tool.js';\nimport { createDelegateTool } from './delegate-tool.js';\nimport { createWorkflowTool } from './workflow-tool.js';\nimport { createWorkflowCatalog } from '../workflow/catalog.js';\nimport { buildSandboxToolMap, createExecuteCodeTool } from './execute-code-tool.js';\nimport type { AutomationService } from '../../automations/index.js';\nimport type { WorkflowRunServiceLike } from '../../workflows/service/workflow-run-service.types.js';\nimport { createLogger } from '../../utils/logger.js';\nimport type { SkillManager } from '../skills/skill-manager.js';\nimport { wrapToolsWithProtection, type ToolExecutorConfig } from './executor.js';\nimport { createSkillsListTool, createSkillViewTool } from './skills-tools.js';\nimport { createSkillManageTool } from './skill-manage-tool.js';\nimport { createTextToSpeechTool } from './tts-tool.js';\nimport { mergeTtsConfigFromAppConfig } from '../../voice/tts/merge-config.js';\nimport { createGoalEvidenceRecorder } from './goal-evidence-recorder.js';\n\nconst log = createLogger('AgentToolsFactory');\n\n/** Channels where `clarify` can block for a user answer (web UI, Telegram, CLI readline). */\nconst CLARIFY_SUPPORTED_CHANNELS = new Set(['webchat', 'telegram', 'cli']);\n\nexport interface ToolFactoryDeps {\n workspace: string;\n extensionRegistry?: any;\n getCurrentContext: () => { channel: string; chatId: string; sessionKey: string } | null;\n hookRunner?: import('../../extensions/index.js').ExtensionHookRunner;\n bus: MessageBus;\n toolExecutorConfig?: Partial<ToolExecutorConfig>;\n /** Agent defaults (image tools, etc.); use getter so hot-reloaded config applies. */\n getConfig?: () => Config | undefined;\n /** Session / default chat model for vision tool description. */\n getPrimaryModel?: () => Model<Api>;\n /** Memory orchestration (prefetch/sync + external tools). */\n getMemoryManager?: () => MemoryManager;\n /** Session store for `session_search`. */\n getSessionStore?: () => SessionStore;\n /** When set (gateway webchat), enables the `clarify` tool. */\n gatewayClarify?: { requestClarification: GatewayClarifyRequestFn };\n /** Gateway: enables the `automation` tool. */\n getAutomationService?: () => AutomationService | undefined;\n /** Gateway: starts persisted workflow runs (dedicated chat session per run). */\n getWorkflowRunService?: () => WorkflowRunServiceLike | undefined;\n /** Current session skill indexing (tool gating + allowlist); used by skills_list / skill_view. */\n getSkillIndexingContext?: () =>\n | { registeredToolNames: string[]; skillAllowlist?: string[] }\n | undefined;\n /** After skill_manage mutates disk, reload skills + refresh agent prompts (optional). */\n onSkillsFilesystemMutate?: () => void;\n /** Names registered via skill_view for command env passthrough. */\n getSkillPassthroughEnvVarNames?: () => string[];\n /** Add declared env names for the current session (no values stored). */\n registerSkillEnvPassthrough?: (names: string[]) => void;\n}\n\nexport interface CreateCoreToolsOptions {\n /** Workspace root for file/command tools (defaults to factory workspace). */\n workspace?: string;\n /** Canonical `agents/<id>/profile/`: bare SOUL.md / IDENTITY.md resolve here after the workspace. */\n profileMarkdownRoot?: string;\n /** Tool `name` values to omit (e.g. `exec_command`, `extensions` for extension tools). */\n disabledTools?: Set<string>;\n /** Optional primary model for image tool heuristics. */\n getPrimaryModel?: () => Model<Api>;\n getMemoryManager?: () => MemoryManager;\n /** When set, registers `skills_list` and `skill_view` bound to this workspace\\'s skills. */\n getSkillManager?: () => SkillManager;\n}\n\nexport class AgentToolsFactory {\n private browserManager: BrowserManager | null = null;\n /** One dialog/console supervisor per chat session (browser tab). */\n private readonly browserTaskSupervisors = new Map<string, CdpSupervisor>();\n /** Cached readiness probe — keyed by backend mode + extension host:port. */\n private browserReadinessCache: {\n key: string;\n expiresAt: number;\n inflight?: Promise<BrowserNotReadyError | null>;\n result?: BrowserNotReadyError | null;\n } | null = null;\n\n constructor(private deps: ToolFactoryDeps) {}\n\n private browserReadinessKey(): string {\n const cfg = this.deps.getConfig?.();\n const backend = resolveBrowserBackendFromConfig(cfg);\n const host = '127.0.0.1';\n const port = 19820;\n const cdpUrl = backend.mode === 'cdp' ? backend.config.wsEndpoint : '';\n const cloudKind = backend.mode === 'cloud' ? backend.config.type : '';\n return `${backend.mode}@${host}:${port}|${cdpUrl}|${cloudKind}`;\n }\n\n private async checkBrowserReadinessCached(): Promise<BrowserNotReadyError | null> {\n const key = this.browserReadinessKey();\n const now = Date.now();\n const cached = this.browserReadinessCache;\n if (cached && cached.key === key && cached.expiresAt > now && cached.inflight === undefined) {\n return cached.result ?? null;\n }\n if (cached && cached.key === key && cached.inflight) {\n return cached.inflight;\n }\n const inflight = checkBrowserReadiness(this.deps.getConfig?.());\n this.browserReadinessCache = { key, expiresAt: now + 30_000, inflight };\n try {\n const result = await inflight;\n this.browserReadinessCache = { key, expiresAt: Date.now() + 30_000, result };\n return result;\n } catch (e) {\n // Probe should never throw, but if it does we just bypass the cache.\n this.browserReadinessCache = null;\n log.warn({ err: e }, 'browserReadiness probe failed');\n return null;\n }\n }\n\n /** Invalidate the readiness cache (config hot-reload, settings-page save, etc.). */\n invalidateBrowserReadinessCache(): void {\n this.browserReadinessCache = null;\n }\n\n private browserSupervisorForTask(taskId: string): CdpSupervisor {\n let s = this.browserTaskSupervisors.get(taskId);\n if (!s) {\n s = new CdpSupervisor({ dialogPolicy: 'auto_dismiss', dialogTimeoutSeconds: 300 });\n this.browserTaskSupervisors.set(taskId, s);\n }\n return s;\n }\n\n private async acquireBrowserPage(): Promise<Page> {\n const taskId = this.deps.getCurrentContext()?.sessionKey ?? 'default';\n const mgr = this.ensureBrowserManager();\n await mgr.ensureConnected();\n if (mgr.getExtensionProvider()) {\n return null as unknown as Page;\n }\n const page = await mgr.getPage(taskId);\n this.browserSupervisorForTask(taskId).attach(page);\n return page;\n }\n\n private ensureBrowserManager(): BrowserManager {\n if (!this.browserManager) {\n this.browserManager = new BrowserManager({\n getHeadless: () => false,\n getBackend: () => resolveBrowserBackendFromConfig(this.deps.getConfig?.()),\n });\n }\n return this.browserManager;\n }\n\n /** Close Playwright and all pages (gateway stop, agent manager dispose, or config hot-reload). */\n async shutdownBrowser(): Promise<void> {\n this.browserReadinessCache = null;\n if (!this.browserManager) {\n return;\n }\n await this.browserManager.shutdown();\n this.browserManager = null;\n this.browserTaskSupervisors.clear();\n }\n\n /** Drop the tab for a session when its agent instance is removed. */\n async closeBrowserPageForSession(sessionKey: string): Promise<void> {\n this.browserTaskSupervisors.delete(sessionKey);\n await this.browserManager?.closePage(sessionKey);\n }\n\n createCoreTools(options?: CreateCoreToolsOptions): AgentTool<any, any>[] {\n const workspace = options?.workspace ?? this.deps.workspace;\n const { bus } = this.deps;\n const getPrimary = options?.getPrimaryModel ?? this.deps.getPrimaryModel;\n const getMemMgr = options?.getMemoryManager ?? this.deps.getMemoryManager;\n const getSkillMgr = options?.getSkillManager;\n const disabled = options?.disabledTools;\n\n const primary = getPrimary?.();\n const modelHasVision = primary?.input?.includes('image') ?? false;\n const cfg = this.deps.getConfig?.();\n const browserEnabled = cfg?.browser?.enabled !== false;\n const recordGoalEvidence = createGoalEvidenceRecorder({\n getSessionKey: () => this.deps.getCurrentContext()?.sessionKey,\n });\n const imageTool = createImageTool({\n config: cfg,\n workspace,\n modelHasVision,\n });\n const imageGenerateTool = createImageGenerateTool({\n config: cfg,\n workspace,\n });\n\n const composioTool = createComposioExecuteTool(() => this.deps.getConfig?.());\n const optionalTools = [imageTool, imageGenerateTool, composioTool].filter((t) => t != null) as any[];\n\n const readTool = createReadFileTool(workspace, {\n profileMarkdownRoot: options?.profileMarkdownRoot,\n });\n const writeTool = createWriteFileTool(workspace, {\n profileMarkdownRoot: options?.profileMarkdownRoot,\n recordGoalEvidence,\n });\n const applyPatchTool = createApplyPatchTool(workspace, { recordGoalEvidence });\n const listDir = createListDirTool(workspace);\n const grep = createGrepTool(workspace);\n const find = createFindTool(workspace);\n\n const core: AgentTool<any, any>[] = [\n createSessionStatusTool(),\n createDreamingTool({\n getWorkspace: () => workspace,\n getConfig: () => this.deps.getConfig?.(),\n }),\n createToolManualTool(),\n createClarifyTool({\n resolveAskUser: () => {\n const req = this.deps.gatewayClarify?.requestClarification;\n if (!req) return null;\n const ctx = this.deps.getCurrentContext();\n if (!ctx?.sessionKey) return null;\n if (!CLARIFY_SUPPORTED_CHANNELS.has(ctx.channel)) return null;\n return (r) => req(ctx.sessionKey, r);\n },\n }),\n createTodoTool({\n getSessionKey: () => this.deps.getCurrentContext()?.sessionKey,\n }),\n createUpdatePlanTool(),\n ...(getSkillMgr\n ? [\n createSkillsListTool({\n getSkillManager: getSkillMgr,\n getSkillIndexingContext: this.deps.getSkillIndexingContext,\n }),\n createSkillViewTool({\n getSkillManager: getSkillMgr,\n getSkillIndexingContext: this.deps.getSkillIndexingContext,\n registerSkillEnvPassthrough: this.deps.registerSkillEnvPassthrough,\n }),\n createSkillManageTool({\n getSkillManager: getSkillMgr,\n getWorkspace: () => workspace,\n onSkillsFilesystemMutate: this.deps.onSkillsFilesystemMutate,\n }),\n ]\n : []),\n readTool,\n writeTool,\n applyPatchTool,\n listDir,\n grep,\n find,\n createExecCommandTool(workspace, {\n getSkillPassthroughEnvVarNames: this.deps.getSkillPassthroughEnvVarNames,\n recordGoalEvidence,\n }),\n createWebSearchTool(() => this.deps.getConfig?.()),\n createWebFetchTool(() => this.deps.getConfig?.()),\n createWebExtractTool({ getConfig: () => this.deps.getConfig?.() }),\n // Note: TTS is NOT handled by send_message tool anymore\n // TTS is applied at the ChannelManager dispatch layer\n createMessageTool(bus, () => this.deps.getCurrentContext()),\n ...(mergeTtsConfigFromAppConfig(cfg?.messages?.tts).enabled\n ? [\n createTextToSpeechTool({\n bus,\n getContext: () => this.deps.getCurrentContext(),\n getConfig: () => this.deps.getConfig?.(),\n }),\n ]\n : []),\n createSendMediaTool(workspace, bus, () => this.deps.getCurrentContext()),\n createReadMediaTool(),\n ...(isShareToolAvailable(cfg)\n ? [\n createCreateShareTool({\n workspace,\n getConfig: () => this.deps.getConfig?.(),\n }),\n ]\n : []),\n ...(getMemMgr\n ? [\n createMemorySearchTool({ workspaceDir: workspace, getMemoryManager: () => getMemMgr() }),\n createMemoryGetTool({ workspaceDir: workspace, getMemoryManager: () => getMemMgr() }),\n ]\n : []),\n ...(getMemMgr && shouldRegisterCuratedMemoryTool(this.deps.getConfig?.())\n ? [\n createCuratedMemoryTool(() => getMemMgr()),\n ]\n : []),\n ...(getMemMgr?.().getAdditionalTools() ?? []),\n ...(this.deps.getSessionStore\n ? [\n createSessionSearchTool({\n getSessionStore: this.deps.getSessionStore,\n getConfig: this.deps.getConfig,\n getCurrentSessionKey: () => this.deps.getCurrentContext()?.sessionKey,\n }),\n ]\n : []),\n ...(this.deps.getAutomationService\n ? [\n createAutomationTool({\n getAutomationService: this.deps.getAutomationService,\n }),\n ]\n : []),\n createGoalTool({\n getCurrentSessionKey: () => this.deps.getCurrentContext()?.sessionKey,\n }),\n ...(browserEnabled\n ? [\n createBrowserUseTool({\n getManager: () => this.ensureBrowserManager(),\n getPageForTask: () => this.acquireBrowserPage(),\n getTaskId: () => this.deps.getCurrentContext()?.sessionKey ?? 'default',\n getConfig: () => this.deps.getConfig?.(),\n getReadiness: () => this.checkBrowserReadinessCached(),\n getSupervisor: () =>\n this.browserSupervisorForTask(this.deps.getCurrentContext()?.sessionKey ?? 'default'),\n notifyBrowserPageClosed: (taskId) => {\n this.browserTaskSupervisors.delete(taskId);\n },\n }),\n ]\n : []),\n ...(primary\n ? [\n createWorkflowTool({\n catalog: createWorkflowCatalog(),\n getCurrentSessionKey: () => this.deps.getCurrentContext()?.sessionKey,\n getConfig: () => this.deps.getConfig?.(),\n startWorkflowRun: this.deps.getWorkflowRunService\n ? (params) => this.deps.getWorkflowRunService!().startWorkflowRun(params)\n : undefined,\n }),\n ]\n : []),\n ...(false && primary\n ? [\n createDelegateTool({\n workspace,\n getSubagentModel: () => {\n const gp = options?.getPrimaryModel ?? this.deps.getPrimaryModel;\n const m = gp?.();\n if (!m) {\n throw new Error('No primary model configured for delegate_task');\n }\n return m;\n },\n bus: this.deps.bus,\n getConfig: () => this.deps.getConfig?.(),\n getCurrentContext: () => this.deps.getCurrentContext?.() ?? null,\n hookRunner: this.deps.hookRunner,\n toolExecutorConfig: this.deps.toolExecutorConfig,\n // Injected so `child-agent-factory.ts` does not need to import\n // `AgentToolsFactory` directly (which would form a cycle).\n buildChildTools: (childOpts) => {\n const childFactory = new AgentToolsFactory({\n workspace: childOpts.workspace,\n bus: childOpts.bus,\n getCurrentContext: () => null,\n getConfig: childOpts.getConfig,\n getPrimaryModel: () => childOpts.model,\n toolExecutorConfig: childOpts.toolExecutorConfig,\n });\n return childFactory.createAllTools({\n workspace: childOpts.workspace,\n getPrimaryModel: () => childOpts.model,\n disabledTools: new Set(['extensions']),\n });\n },\n }),\n ]\n : []),\n ...optionalTools,\n ];\n\n return filterToolsByDisabledSet(core, disabled);\n }\n\n createAllTools(coreOptions?: CreateCoreToolsOptions): AgentTool<any, any>[] {\n const coreTools = this.createCoreTools(coreOptions);\n const disableExtensions = coreOptions?.disabledTools?.has('extensions');\n\n let bundled: AgentTool<any, any>[];\n if (!this.deps.extensionRegistry || disableExtensions) {\n bundled = coreTools;\n } else {\n const extensionTools = this.deps.extensionRegistry.getAllTools();\n log.info({ count: extensionTools.length }, 'Loaded extension tools');\n bundled = [...coreTools, ...extensionTools];\n }\n\n const wrapped = wrapToolsWithProtection(bundled, this.deps.toolExecutorConfig);\n\n const executeEnabled = false && !coreOptions?.disabledTools?.has('execute_code');\n\n if (executeEnabled) {\n const sandboxMap = buildSandboxToolMap(wrapped);\n const executeTool = createExecuteCodeTool({ getSandboxToolMap: () => sandboxMap });\n const wrappedExecute = wrapToolsWithProtection([executeTool as any], this.deps.toolExecutorConfig);\n return [...wrapped, ...wrappedExecute];\n }\n\n return wrapped;\n }\n}\n\nfunction filterToolsByDisabledSet(\n tools: any[],\n disabled: Set<string> | undefined,\n): any[] {\n if (!disabled || disabled.size === 0) {\n return tools;\n }\n return tools.filter((t) => !disabled.has(t.name));\n}\n"],"mappings":"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"}
@@ -13,7 +13,11 @@ declare const GoalToolSchema: import("@sinclair/typebox/build/cjs/index.js").TOb
13
13
  judgeModelRef: import("@sinclair/typebox/build/cjs/index.js").TOptional<import("@sinclair/typebox/build/cjs/index.js").TString>;
14
14
  agentId: import("@sinclair/typebox/build/cjs/index.js").TOptional<import("@sinclair/typebox/build/cjs/index.js").TString>;
15
15
  sessionKey: import("@sinclair/typebox/build/cjs/index.js").TOptional<import("@sinclair/typebox/build/cjs/index.js").TString>;
16
+ projectId: import("@sinclair/typebox/build/cjs/index.js").TOptional<import("@sinclair/typebox/build/cjs/index.js").TString>;
16
17
  limit: import("@sinclair/typebox/build/cjs/index.js").TOptional<import("@sinclair/typebox/build/cjs/index.js").TNumber>;
17
18
  }>;
18
- export declare function createGoalTool(): AgentTool<typeof GoalToolSchema, {}>;
19
+ export interface GoalToolOptions {
20
+ getCurrentSessionKey?: () => string | undefined;
21
+ }
22
+ export declare function createGoalTool(options?: GoalToolOptions): AgentTool<typeof GoalToolSchema, {}>;
19
23
  export {};