@xopcai/xopc 0.0.87 → 0.0.89

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 (282) hide show
  1. package/README.md +8 -1
  2. package/README.zh-CN.md +8 -1
  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/agents-B6PJB07W.js +222 -0
  6. package/dist/gateway/static/root/assets/apps-page-BOr0B1wv.js +1 -0
  7. package/dist/gateway/static/root/assets/channels-settings-BelUKggl.js +1 -0
  8. package/dist/gateway/static/root/assets/{channels-status-swr-BSHqqCF1.js → channels-status-swr-DaHGkRF1.js} +1 -1
  9. package/dist/gateway/static/root/assets/cron-api-CjOg-BIj.js +1 -0
  10. package/dist/gateway/static/root/assets/cron-page-DhoZmZXb.js +1 -0
  11. package/dist/gateway/static/root/assets/{dist-Cmjp2APP.js → dist-6LecgDx5.js} +1 -1
  12. package/dist/gateway/static/root/assets/{extension-debug-page-CFa9z_1N.js → extension-debug-page-CtuKJ9tE.js} +1 -1
  13. package/dist/gateway/static/root/assets/{extension-page-BI8eaTPq.js → extension-page-ykzjOkR5.js} +1 -1
  14. package/dist/gateway/static/root/assets/extension-settings-page-Ce2qrdpO.js +1 -0
  15. package/dist/gateway/static/root/assets/{fetch-DRqwef_Q.js → fetch-C9FFJjuH.js} +1 -1
  16. package/dist/gateway/static/root/assets/{field-primitives-BiNHBo2Y.js → field-primitives-BFcrNeTU.js} +1 -1
  17. package/dist/gateway/static/root/assets/{heartbeat-config-api-ZRb8qhuz.js → heartbeat-config-api-CEg4Vr9R.js} +1 -1
  18. package/dist/gateway/static/root/assets/{index-Cu7bKuUi.js → index-CZfy9oxs.js} +85 -85
  19. package/dist/gateway/static/root/assets/index-CiN1cQiQ.css +1 -0
  20. package/dist/gateway/static/root/assets/logs-page-BwWLfqvd.js +1 -0
  21. package/dist/gateway/static/root/assets/sessions-page-DV5WN8uk.js +1 -0
  22. package/dist/gateway/static/root/assets/{settings-form-section-DiqqVs6m.js → settings-form-section-BqdzA28u.js} +1 -1
  23. package/dist/gateway/static/root/assets/settings-page-CfOBRbPX.js +3 -0
  24. package/dist/gateway/static/root/assets/{share-preview-page-n1Gprylk.js → share-preview-page-Di5Bzh4g.js} +1 -1
  25. package/dist/gateway/static/root/assets/skills-page-D0H5Kaxg.js +2 -0
  26. package/dist/gateway/static/root/assets/{theme-store-CZOh1nT3.js → theme-store-CNqbmTNV.js} +1 -1
  27. package/dist/gateway/static/root/assets/url-aYn-Rj1C.js +7 -0
  28. package/dist/gateway/static/root/assets/{utils-CkWBfxs4.js → utils-BWm2tG2w.js} +1 -1
  29. package/dist/gateway/static/root/assets/voice-api-key-field-X2UfnHeq.js +1 -0
  30. package/dist/gateway/static/root/assets/workflows-page-BOPpO3NG.js +27 -0
  31. package/dist/gateway/static/root/index.html +5 -5
  32. package/dist/package.js +1 -1
  33. package/dist/src/agent/agent-manager.d.ts +2 -0
  34. package/dist/src/agent/agent-manager.js +1 -0
  35. package/dist/src/agent/agent-manager.js.map +1 -1
  36. package/dist/src/agent/child-agent-factory.d.ts +15 -0
  37. package/dist/src/agent/child-agent-factory.js +35 -2
  38. package/dist/src/agent/child-agent-factory.js.map +1 -1
  39. package/dist/src/agent/client-error-format.d.ts +20 -0
  40. package/dist/src/agent/client-error-format.js +97 -0
  41. package/dist/src/agent/client-error-format.js.map +1 -0
  42. package/dist/src/agent/embedded/run-turn.js +23 -4
  43. package/dist/src/agent/embedded/run-turn.js.map +1 -1
  44. package/dist/src/agent/goals/goal-locale.d.ts +1 -1
  45. package/dist/src/agent/inbound/turn-dispatcher.js +1 -1
  46. package/dist/src/agent/inbound/turn-dispatcher.js.map +1 -1
  47. package/dist/src/agent/orchestration/llm-turn-retry.d.ts +2 -0
  48. package/dist/src/agent/orchestration/llm-turn-retry.js +9 -1
  49. package/dist/src/agent/orchestration/llm-turn-retry.js.map +1 -1
  50. package/dist/src/agent/service/process-direct-streaming.js +19 -3
  51. package/dist/src/agent/service/process-direct-streaming.js.map +1 -1
  52. package/dist/src/agent/service/webchat-tts.d.ts +1 -2
  53. package/dist/src/agent/service/webchat-tts.js +1 -1
  54. package/dist/src/agent/service/webchat-tts.js.map +1 -1
  55. package/dist/src/agent/service.js +2 -1
  56. package/dist/src/agent/service.js.map +1 -1
  57. package/dist/src/agent/service.types.d.ts +3 -1
  58. package/dist/src/agent/tools/cronjob-tool.js +2 -1
  59. package/dist/src/agent/tools/cronjob-tool.js.map +1 -1
  60. package/dist/src/agent/tools/factory.d.ts +3 -0
  61. package/dist/src/agent/tools/factory.js +2 -23
  62. package/dist/src/agent/tools/factory.js.map +1 -1
  63. package/dist/src/agent/tools/workflow-tool.d.ts +6 -28
  64. package/dist/src/agent/tools/workflow-tool.js +61 -213
  65. package/dist/src/agent/tools/workflow-tool.js.map +1 -1
  66. package/dist/src/agent/workflow/agent-progress.d.ts +5 -0
  67. package/dist/src/agent/workflow/agent-progress.js +65 -0
  68. package/dist/src/agent/workflow/agent-progress.js.map +1 -0
  69. package/dist/src/agent/workflow/builtins/audit-repo.d.ts +1 -1
  70. package/dist/src/agent/workflow/builtins/audit-repo.js +14 -0
  71. package/dist/src/agent/workflow/builtins/audit-repo.js.map +1 -1
  72. package/dist/src/agent/workflow/builtins/debug-incident.d.ts +1 -1
  73. package/dist/src/agent/workflow/builtins/debug-incident.js +14 -0
  74. package/dist/src/agent/workflow/builtins/debug-incident.js.map +1 -1
  75. package/dist/src/agent/workflow/builtins/implementation-plan.d.ts +12 -0
  76. package/dist/src/agent/workflow/builtins/implementation-plan.js +175 -0
  77. package/dist/src/agent/workflow/builtins/implementation-plan.js.map +1 -0
  78. package/dist/src/agent/workflow/builtins/index.d.ts +3 -1
  79. package/dist/src/agent/workflow/builtins/index.js +11 -1
  80. package/dist/src/agent/workflow/builtins/index.js.map +1 -1
  81. package/dist/src/agent/workflow/builtins/multi-perspective-review.d.ts +1 -1
  82. package/dist/src/agent/workflow/builtins/multi-perspective-review.js +14 -0
  83. package/dist/src/agent/workflow/builtins/multi-perspective-review.js.map +1 -1
  84. package/dist/src/agent/workflow/builtins/pr-review.d.ts +1 -1
  85. package/dist/src/agent/workflow/builtins/pr-review.js +14 -0
  86. package/dist/src/agent/workflow/builtins/pr-review.js.map +1 -1
  87. package/dist/src/agent/workflow/builtins/release-check.d.ts +11 -0
  88. package/dist/src/agent/workflow/builtins/release-check.js +165 -0
  89. package/dist/src/agent/workflow/builtins/release-check.js.map +1 -0
  90. package/dist/src/agent/workflow/builtins/research.d.ts +1 -1
  91. package/dist/src/agent/workflow/builtins/research.js +14 -0
  92. package/dist/src/agent/workflow/builtins/research.js.map +1 -1
  93. package/dist/src/agent/workflow/index.d.ts +2 -1
  94. package/dist/src/agent/workflow/index.js +3 -2
  95. package/dist/src/agent/workflow/meta-locale.d.ts +12 -0
  96. package/dist/src/agent/workflow/meta-locale.js +62 -0
  97. package/dist/src/agent/workflow/meta-locale.js.map +1 -0
  98. package/dist/src/agent/workflow/parser.js +3 -0
  99. package/dist/src/agent/workflow/parser.js.map +1 -1
  100. package/dist/src/agent/workflow/runtime.d.ts +2 -2
  101. package/dist/src/agent/workflow/runtime.js +21 -14
  102. package/dist/src/agent/workflow/runtime.js.map +1 -1
  103. package/dist/src/agent/workflow/snapshot.js +2 -12
  104. package/dist/src/agent/workflow/snapshot.js.map +1 -1
  105. package/dist/src/agent/workflow/step-labels.d.ts +8 -0
  106. package/dist/src/agent/workflow/step-labels.js +48 -0
  107. package/dist/src/agent/workflow/step-labels.js.map +1 -0
  108. package/dist/src/agent/workflow/subagent-runner.js +46 -1
  109. package/dist/src/agent/workflow/subagent-runner.js.map +1 -1
  110. package/dist/src/agent/workflow/types.d.ts +74 -1
  111. package/dist/src/agent/workflow/workflow-child-tools.d.ts +4 -0
  112. package/dist/src/agent/workflow/workflow-child-tools.js +21 -0
  113. package/dist/src/agent/workflow/workflow-child-tools.js.map +1 -0
  114. package/dist/src/auth/credentials.d.ts +19 -2
  115. package/dist/src/auth/credentials.js +47 -13
  116. package/dist/src/auth/credentials.js.map +1 -1
  117. package/dist/src/auth/oauth/types.d.ts +16 -0
  118. package/dist/src/cli/commands/auth.js +6 -0
  119. package/dist/src/cli/commands/auth.js.map +1 -1
  120. package/dist/src/cli/commands/gateway/lifecycle.js +1 -1
  121. package/dist/src/cli/commands/onboard/model.js +6 -0
  122. package/dist/src/cli/commands/onboard/model.js.map +1 -1
  123. package/dist/src/config/agent-typed-models.d.ts +18 -0
  124. package/dist/src/config/agent-typed-models.js +53 -0
  125. package/dist/src/config/agent-typed-models.js.map +1 -0
  126. package/dist/src/config/index.js +2 -2
  127. package/dist/src/config/schema.d.ts +52 -0
  128. package/dist/src/config/schema.js +39 -3
  129. package/dist/src/config/schema.js.map +1 -1
  130. package/dist/src/config/voice.d.ts +3 -28
  131. package/dist/src/config/voice.js +27 -261
  132. package/dist/src/config/voice.js.map +1 -1
  133. package/dist/src/cron/executor.d.ts +2 -0
  134. package/dist/src/cron/executor.js +59 -5
  135. package/dist/src/cron/executor.js.map +1 -1
  136. package/dist/src/cron/job-content.js +2 -1
  137. package/dist/src/cron/job-content.js.map +1 -1
  138. package/dist/src/cron/types.d.ts +21 -1
  139. package/dist/src/cron/validation.d.ts +76 -0
  140. package/dist/src/cron/validation.js +26 -1
  141. package/dist/src/cron/validation.js.map +1 -1
  142. package/dist/src/gateway/agents-admin.d.ts +9 -0
  143. package/dist/src/gateway/agents-admin.js +16 -0
  144. package/dist/src/gateway/agents-admin.js.map +1 -1
  145. package/dist/src/gateway/config-tools-web.js +3 -2
  146. package/dist/src/gateway/config-tools-web.js.map +1 -1
  147. package/dist/src/gateway/gateway-workflow-host.types.d.ts +17 -0
  148. package/dist/src/gateway/gateway-workflow-host.types.js +1 -0
  149. package/dist/src/gateway/hono/lib/agent-model.d.ts +7 -0
  150. package/dist/src/gateway/hono/lib/agent-model.js +36 -1
  151. package/dist/src/gateway/hono/lib/agent-model.js.map +1 -1
  152. package/dist/src/gateway/hono/lib/config-payload.js +28 -5
  153. package/dist/src/gateway/hono/lib/config-payload.js.map +1 -1
  154. package/dist/src/gateway/hono/lib/mask-secret-length.d.ts +6 -0
  155. package/dist/src/gateway/hono/lib/mask-secret-length.js +16 -0
  156. package/dist/src/gateway/hono/lib/mask-secret-length.js.map +1 -0
  157. package/dist/src/gateway/hono/lib/safe-providers-config.d.ts +1 -1
  158. package/dist/src/gateway/hono/lib/safe-providers-config.js +2 -1
  159. package/dist/src/gateway/hono/lib/safe-providers-config.js.map +1 -1
  160. package/dist/src/gateway/hono/lib/safe-voice-config.js +2 -1
  161. package/dist/src/gateway/hono/lib/safe-voice-config.js.map +1 -1
  162. package/dist/src/gateway/hono/oauth-async.js +40 -15
  163. package/dist/src/gateway/hono/oauth-async.js.map +1 -1
  164. package/dist/src/gateway/hono/oauth.js +31 -6
  165. package/dist/src/gateway/hono/oauth.js.map +1 -1
  166. package/dist/src/gateway/hono/routes/agents.js +1 -1
  167. package/dist/src/gateway/hono/routes/config-patch/agents.js +8 -2
  168. package/dist/src/gateway/hono/routes/config-patch/agents.js.map +1 -1
  169. package/dist/src/gateway/hono/routes/config-patch/gateway.js +3 -2
  170. package/dist/src/gateway/hono/routes/config-patch/gateway.js.map +1 -1
  171. package/dist/src/gateway/hono/routes/config-patch/misc.js +7 -2
  172. package/dist/src/gateway/hono/routes/config-patch/misc.js.map +1 -1
  173. package/dist/src/gateway/hono/routes/config.js +59 -0
  174. package/dist/src/gateway/hono/routes/config.js.map +1 -1
  175. package/dist/src/gateway/hono/routes/lazy-bundles.js +8 -0
  176. package/dist/src/gateway/hono/routes/lazy-bundles.js.map +1 -1
  177. package/dist/src/gateway/hono/routes/models.js +84 -15
  178. package/dist/src/gateway/hono/routes/models.js.map +1 -1
  179. package/dist/src/gateway/hono/routes/voice.js +75 -0
  180. package/dist/src/gateway/hono/routes/voice.js.map +1 -1
  181. package/dist/src/gateway/hono/routes/workflows.d.ts +3 -0
  182. package/dist/src/gateway/hono/routes/workflows.js +226 -0
  183. package/dist/src/gateway/hono/routes/workflows.js.map +1 -0
  184. package/dist/src/gateway/service/run-gateway-agent.js +2 -20
  185. package/dist/src/gateway/service/run-gateway-agent.js.map +1 -1
  186. package/dist/src/gateway/service.d.ts +8 -0
  187. package/dist/src/gateway/service.js +28 -2
  188. package/dist/src/gateway/service.js.map +1 -1
  189. package/dist/src/mcp/channel-bridge.js +1 -1
  190. package/dist/src/providers/index.d.ts +8 -0
  191. package/dist/src/providers/index.js +51 -12
  192. package/dist/src/providers/index.js.map +1 -1
  193. package/dist/src/share/site-share-config.d.ts +3 -2
  194. package/dist/src/share/site-share-config.js.map +1 -1
  195. package/dist/src/tui/tui-agent-events.js +2 -1
  196. package/dist/src/tui/tui-agent-events.js.map +1 -1
  197. package/dist/src/voice/metadata/builtin.d.ts +2 -0
  198. package/dist/src/voice/metadata/builtin.js +420 -0
  199. package/dist/src/voice/metadata/builtin.js.map +1 -0
  200. package/dist/src/voice/metadata/index.d.ts +4 -0
  201. package/dist/src/voice/metadata/index.js +3 -0
  202. package/dist/src/voice/metadata/registry.d.ts +5 -0
  203. package/dist/src/voice/metadata/registry.js +34 -0
  204. package/dist/src/voice/metadata/registry.js.map +1 -0
  205. package/dist/src/voice/metadata/types.d.ts +41 -0
  206. package/dist/src/voice/metadata/types.js +1 -0
  207. package/dist/src/voice/stt/list-providers.d.ts +3 -3
  208. package/dist/src/voice/stt/list-providers.js +41 -6
  209. package/dist/src/voice/stt/list-providers.js.map +1 -1
  210. package/dist/src/voice/tts/list-providers.d.ts +3 -3
  211. package/dist/src/voice/tts/list-providers.js +41 -6
  212. package/dist/src/voice/tts/list-providers.js.map +1 -1
  213. package/dist/src/workflows/domain/command.d.ts +19 -0
  214. package/dist/src/workflows/domain/command.js +1 -0
  215. package/dist/src/workflows/domain/definition-utils.d.ts +14 -0
  216. package/dist/src/workflows/domain/definition-utils.js +50 -0
  217. package/dist/src/workflows/domain/definition-utils.js.map +1 -0
  218. package/dist/src/workflows/domain/definition.d.ts +62 -0
  219. package/dist/src/workflows/domain/definition.js +1 -0
  220. package/dist/src/workflows/domain/event.d.ts +67 -0
  221. package/dist/src/workflows/domain/event.js +1 -0
  222. package/dist/src/workflows/domain/index.d.ts +7 -0
  223. package/dist/src/workflows/domain/index.js +4 -0
  224. package/dist/src/workflows/domain/result.d.ts +65 -0
  225. package/dist/src/workflows/domain/result.js +1 -0
  226. package/dist/src/workflows/domain/run.d.ts +177 -0
  227. package/dist/src/workflows/domain/run.js +14 -0
  228. package/dist/src/workflows/domain/run.js.map +1 -0
  229. package/dist/src/workflows/domain/validation.d.ts +19 -0
  230. package/dist/src/workflows/domain/validation.js +66 -0
  231. package/dist/src/workflows/domain/validation.js.map +1 -0
  232. package/dist/src/workflows/engine/index.d.ts +2 -0
  233. package/dist/src/workflows/engine/index.js +3 -0
  234. package/dist/src/workflows/engine/projector.d.ts +3 -0
  235. package/dist/src/workflows/engine/projector.js +205 -0
  236. package/dist/src/workflows/engine/projector.js.map +1 -0
  237. package/dist/src/workflows/engine/workflow-engine.d.ts +32 -0
  238. package/dist/src/workflows/engine/workflow-engine.js +189 -0
  239. package/dist/src/workflows/engine/workflow-engine.js.map +1 -0
  240. package/dist/src/workflows/index.d.ts +10 -0
  241. package/dist/src/workflows/index.js +18 -0
  242. package/dist/src/workflows/runtime/index.d.ts +1 -0
  243. package/dist/src/workflows/runtime/index.js +4 -0
  244. package/dist/src/workflows/runtime/script-runtime.d.ts +3 -0
  245. package/dist/src/workflows/runtime/script-runtime.js +3 -0
  246. package/dist/src/workflows/service/run-view-to-snapshot.d.ts +4 -0
  247. package/dist/src/workflows/service/run-view-to-snapshot.js +61 -0
  248. package/dist/src/workflows/service/run-view-to-snapshot.js.map +1 -0
  249. package/dist/src/workflows/service/workflow-run-service.d.ts +36 -0
  250. package/dist/src/workflows/service/workflow-run-service.js +279 -0
  251. package/dist/src/workflows/service/workflow-run-service.js.map +1 -0
  252. package/dist/src/workflows/service/workflow-run-service.types.d.ts +47 -0
  253. package/dist/src/workflows/service/workflow-run-service.types.js +1 -0
  254. package/dist/src/workflows/service/workflow-session-bridge.d.ts +29 -0
  255. package/dist/src/workflows/service/workflow-session-bridge.js +177 -0
  256. package/dist/src/workflows/service/workflow-session-bridge.js.map +1 -0
  257. package/dist/src/workflows/service/workflow-session-key.d.ts +3 -0
  258. package/dist/src/workflows/service/workflow-session-key.js +21 -0
  259. package/dist/src/workflows/service/workflow-session-key.js.map +1 -0
  260. package/dist/src/workflows/store/event-store.d.ts +17 -0
  261. package/dist/src/workflows/store/event-store.js +83 -0
  262. package/dist/src/workflows/store/event-store.js.map +1 -0
  263. package/dist/src/workflows/store/paths.d.ts +7 -0
  264. package/dist/src/workflows/store/paths.js +26 -0
  265. package/dist/src/workflows/store/paths.js.map +1 -0
  266. package/dist/src/workflows/store/run-store.d.ts +13 -0
  267. package/dist/src/workflows/store/run-store.js +69 -0
  268. package/dist/src/workflows/store/run-store.js.map +1 -0
  269. package/package.json +5 -5
  270. package/dist/gateway/static/root/assets/agents-BEAbXpuP.js +0 -222
  271. package/dist/gateway/static/root/assets/apps-page-Dg8R-Szf.js +0 -1
  272. package/dist/gateway/static/root/assets/channels-settings-yohw9YSu.js +0 -1
  273. package/dist/gateway/static/root/assets/cron-api-0h_QT8U3.js +0 -1
  274. package/dist/gateway/static/root/assets/cron-page-BkfKFfFk.js +0 -1
  275. package/dist/gateway/static/root/assets/extension-settings-page-x4BB7q1X.js +0 -1
  276. package/dist/gateway/static/root/assets/index-a5gWIdZQ.css +0 -1
  277. package/dist/gateway/static/root/assets/logs-page-BFZ8GgCv.js +0 -1
  278. package/dist/gateway/static/root/assets/sessions-page-CD7AfB-2.js +0 -1
  279. package/dist/gateway/static/root/assets/settings-page-BBOjEQW3.js +0 -3
  280. package/dist/gateway/static/root/assets/skills-page-CcN_gj--.js +0 -2
  281. package/dist/gateway/static/root/assets/url-Dd8Q7kZZ.js +0 -3
  282. package/dist/gateway/static/root/assets/voice-api-key-field-O6awz9hi.js +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"factory.js","names":["parseRoutingSessionKey"],"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 createSendMediaTool,\n createCreateShareTool,\n isShareToolAvailable,\n createMemorySearchTool,\n createMemoryGetTool,\n createTodoTool,\n createSessionStatusTool,\n createDreamingTool,\n createClarifyTool,\n} from './index.js';\nimport { createCuratedMemoryTool } from './curated-memory-tool.js';\nimport { createSessionSearchTool } from './session-search-tool.js';\nimport type { BuiltinMemoryStore } from '../memory/builtin-memory-store.js';\nimport type { MemoryManager } from '../memory/manager.js';\nimport { shouldRegisterCuratedMemoryTool } from '../memory/memory-config.js';\nimport type { SessionStore } from '../../session/store.js';\nimport { parseSessionKey as parseRoutingSessionKey } from '../../routing/session-key.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 { createCronjobTool } from './cronjob-tool.js';\nimport type { CronService } from '../../cron/index.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';\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\nfunction clarifyTransportSource(sessionKey: string): string | undefined {\n const parsed = parseRoutingSessionKey(sessionKey);\n if (parsed) return parsed.source;\n // Fallback for simple `<channel>:<chatId>` keys used by webchat and CLI.\n const first = sessionKey.split(':').filter(Boolean)[0] ?? '';\n if (first === 'cli' || first === 'webchat') return first;\n return undefined;\n}\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 /** Built-in curated memory store (agent home `memories/`). */\n getBuiltinMemoryStore?: () => BuiltinMemoryStore;\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 `cronjob` tool. */\n getCronService?: () => CronService | 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 getBuiltinMemoryStore?: () => BuiltinMemoryStore;\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 ext = cfg?.agents?.defaults?.browser?.extension;\n const host = typeof ext?.host === 'string' && ext.host.trim() ? ext.host.trim() : '127.0.0.1';\n const port = typeof ext?.port === 'number' ? ext.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 const b = this.deps.getConfig?.()?.agents?.defaults?.browser;\n const dialogPolicy =\n b?.dialogPolicy === 'must_respond' || b?.dialogPolicy === 'auto_accept' || b?.dialogPolicy === 'auto_dismiss'\n ? b.dialogPolicy\n : 'auto_dismiss';\n const dialogTimeoutSeconds =\n typeof b?.dialogTimeoutSeconds === 'number' &&\n Number.isFinite(b.dialogTimeoutSeconds) &&\n b.dialogTimeoutSeconds >= 1\n ? Math.floor(b.dialogTimeoutSeconds)\n : 300;\n s = new CdpSupervisor({ dialogPolicy, dialogTimeoutSeconds });\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: () => this.deps.getConfig?.()?.agents?.defaults?.browser?.headless === true,\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 getBuiltin = options?.getBuiltinMemoryStore ?? this.deps.getBuiltinMemoryStore;\n const builtinStore = getBuiltin?.();\n const memoriesDir = builtinStore?.memoriesDir;\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 imageTool = createImageTool({\n config: cfg,\n workspace,\n modelHasVision,\n });\n const imageGenerateTool = createImageGenerateTool({\n config: cfg,\n workspace,\n });\n\n const optionalTools = [imageTool, imageGenerateTool].filter((t) => t != null) as any[];\n\n const readTool = createReadFileTool(workspace, {\n profileMarkdownRoot: options?.profileMarkdownRoot,\n });\n const writeTool = createWriteFileTool(workspace);\n const editTool = createEditFileTool(workspace);\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 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 const source = clarifyTransportSource(ctx.sessionKey);\n if (!source || !CLARIFY_SUPPORTED_CHANNELS.has(source)) 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 }),\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 ...(isShareToolAvailable(cfg)\n ? [\n createCreateShareTool({\n workspace,\n getConfig: () => this.deps.getConfig?.(),\n }),\n ]\n : []),\n createMemorySearchTool({ workspaceDir: workspace, memoriesDir }),\n createMemoryGetTool({ workspaceDir: workspace, memoriesDir }),\n ...(getBuiltin && shouldRegisterCuratedMemoryTool(this.deps.getConfig?.())\n ? [\n createCuratedMemoryTool(getBuiltin, {\n onMemoryWrite: (action, target, content) => {\n getMemMgr?.().onMemoryWrite(action, target, content);\n },\n }),\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.getCronService\n ? [\n createCronjobTool({\n getCronService: this.deps.getCronService,\n }),\n ]\n : []),\n ...(cfg?.agents?.defaults?.browser?.enabled !== false\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 ...(cfg?.agents?.defaults?.workflow?.enabled !== false && primary\n ? [\n createWorkflowTool({\n workspace,\n bus: this.deps.bus,\n getConfig: () => this.deps.getConfig?.(),\n catalog: createWorkflowCatalog(),\n getCurrentSessionKey: () => this.deps.getCurrentContext()?.sessionKey,\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 workflow');\n }\n return m;\n },\n toolExecutorConfig: this.deps.toolExecutorConfig,\n // Injected so `subagent-runner.ts` doesn't import `AgentToolsFactory`\n // (which would form the same cycle delegate-tool was carved out to avoid).\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 ...(cfg?.agents?.defaults?.delegate?.enabled === true && 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 const cfg = this.deps.getConfig?.();\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 =\n cfg?.agents?.defaults?.executeCode?.enabled === true &&\n !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":["parseRoutingSessionKey"],"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 createSendMediaTool,\n createCreateShareTool,\n isShareToolAvailable,\n createMemorySearchTool,\n createMemoryGetTool,\n createTodoTool,\n createSessionStatusTool,\n createDreamingTool,\n createClarifyTool,\n} from './index.js';\nimport { createCuratedMemoryTool } from './curated-memory-tool.js';\nimport { createSessionSearchTool } from './session-search-tool.js';\nimport type { BuiltinMemoryStore } from '../memory/builtin-memory-store.js';\nimport type { MemoryManager } from '../memory/manager.js';\nimport { shouldRegisterCuratedMemoryTool } from '../memory/memory-config.js';\nimport type { SessionStore } from '../../session/store.js';\nimport { parseSessionKey as parseRoutingSessionKey } from '../../routing/session-key.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 { createCronjobTool } from './cronjob-tool.js';\nimport type { CronService } from '../../cron/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';\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\nfunction clarifyTransportSource(sessionKey: string): string | undefined {\n const parsed = parseRoutingSessionKey(sessionKey);\n if (parsed) return parsed.source;\n // Fallback for simple `<channel>:<chatId>` keys used by webchat and CLI.\n const first = sessionKey.split(':').filter(Boolean)[0] ?? '';\n if (first === 'cli' || first === 'webchat') return first;\n return undefined;\n}\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 /** Built-in curated memory store (agent home `memories/`). */\n getBuiltinMemoryStore?: () => BuiltinMemoryStore;\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 `cronjob` tool. */\n getCronService?: () => CronService | 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 getBuiltinMemoryStore?: () => BuiltinMemoryStore;\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 ext = cfg?.agents?.defaults?.browser?.extension;\n const host = typeof ext?.host === 'string' && ext.host.trim() ? ext.host.trim() : '127.0.0.1';\n const port = typeof ext?.port === 'number' ? ext.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 const b = this.deps.getConfig?.()?.agents?.defaults?.browser;\n const dialogPolicy =\n b?.dialogPolicy === 'must_respond' || b?.dialogPolicy === 'auto_accept' || b?.dialogPolicy === 'auto_dismiss'\n ? b.dialogPolicy\n : 'auto_dismiss';\n const dialogTimeoutSeconds =\n typeof b?.dialogTimeoutSeconds === 'number' &&\n Number.isFinite(b.dialogTimeoutSeconds) &&\n b.dialogTimeoutSeconds >= 1\n ? Math.floor(b.dialogTimeoutSeconds)\n : 300;\n s = new CdpSupervisor({ dialogPolicy, dialogTimeoutSeconds });\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: () => this.deps.getConfig?.()?.agents?.defaults?.browser?.headless === true,\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 getBuiltin = options?.getBuiltinMemoryStore ?? this.deps.getBuiltinMemoryStore;\n const builtinStore = getBuiltin?.();\n const memoriesDir = builtinStore?.memoriesDir;\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 imageTool = createImageTool({\n config: cfg,\n workspace,\n modelHasVision,\n });\n const imageGenerateTool = createImageGenerateTool({\n config: cfg,\n workspace,\n });\n\n const optionalTools = [imageTool, imageGenerateTool].filter((t) => t != null) as any[];\n\n const readTool = createReadFileTool(workspace, {\n profileMarkdownRoot: options?.profileMarkdownRoot,\n });\n const writeTool = createWriteFileTool(workspace);\n const editTool = createEditFileTool(workspace);\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 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 const source = clarifyTransportSource(ctx.sessionKey);\n if (!source || !CLARIFY_SUPPORTED_CHANNELS.has(source)) 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 }),\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 ...(isShareToolAvailable(cfg)\n ? [\n createCreateShareTool({\n workspace,\n getConfig: () => this.deps.getConfig?.(),\n }),\n ]\n : []),\n createMemorySearchTool({ workspaceDir: workspace, memoriesDir }),\n createMemoryGetTool({ workspaceDir: workspace, memoriesDir }),\n ...(getBuiltin && shouldRegisterCuratedMemoryTool(this.deps.getConfig?.())\n ? [\n createCuratedMemoryTool(getBuiltin, {\n onMemoryWrite: (action, target, content) => {\n getMemMgr?.().onMemoryWrite(action, target, content);\n },\n }),\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.getCronService\n ? [\n createCronjobTool({\n getCronService: this.deps.getCronService,\n }),\n ]\n : []),\n ...(cfg?.agents?.defaults?.browser?.enabled !== false\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 ...(cfg?.agents?.defaults?.workflow?.enabled !== false && 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 ...(cfg?.agents?.defaults?.delegate?.enabled === true && 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 const cfg = this.deps.getConfig?.();\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 =\n cfg?.agents?.defaults?.executeCode?.enabled === true &&\n !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,41 +1,19 @@
1
1
  /**
2
- * `workflow` — the AgentTool the parent model calls to spawn a fan-out run.
3
- *
4
- * Shape mirrors `delegate-tool`: factory builds a closure over deps; `execute`
5
- * parses the script, instantiates the {@link DelegateSubagentRunner}, drives the
6
- * {@link runWorkflow} runtime, and pushes a live text snapshot through
7
- * `onUpdate` for streaming UIs (TUI, gateway console).
8
- *
9
- * Why this lives in `src/agent/tools/` (not under `src/agent/workflow/`):
10
- * the runtime is reusable infrastructure; the AgentTool wrapping is a
11
- * presentation concern that depends on the AgentToolsFactory wiring. Keeping
12
- * the wrapper here matches how `delegate-tool` and `execute-code-tool` are
13
- * organised today.
2
+ * `workflow` — starts a persisted workflow run in a dedicated chat session.
14
3
  */
15
4
  import type { AgentTool } from '@earendil-works/pi-agent-core';
16
- import type { Api, Model } from '@earendil-works/pi-ai';
17
- import type { Config } from '../../config/schema.js';
18
- import type { MessageBus } from '../../infra/bus/index.js';
19
- import type { BuildChildToolsOptions } from '../child-agent-factory.js';
20
- import { type WorkflowCatalog } from '../workflow/index.js';
21
- import type { ToolExecutorConfig } from './executor.js';
5
+ import type { WorkflowCatalog } from '../workflow/catalog.js';
6
+ import type { StartWorkflowRunServiceParams, WorkflowRunServiceResult } from '../../workflows/service/workflow-run-service.types.js';
22
7
  export type WorkflowToolInput = {
23
8
  name?: string;
24
9
  script?: string;
25
10
  args?: unknown;
11
+ goal?: string;
26
12
  };
27
13
  export interface WorkflowToolDeps {
28
- workspace: string;
29
- bus: MessageBus;
30
- /** Returns the parent agent's primary model — subagents default to this. */
31
- getSubagentModel: () => Model<Api>;
32
- getConfig: () => Config | undefined;
33
- /** Same injection point delegate-tool uses; supplied by AgentToolsFactory. */
34
- buildChildTools: (opts: BuildChildToolsOptions) => AgentTool<any, any>[];
35
- toolExecutorConfig?: Partial<ToolExecutorConfig>;
36
- /** Catalog for `name` lookups (built-in + ~/.xopc/workflows/). */
37
14
  catalog: WorkflowCatalog;
38
- /** Per-call sessionKey lookup — used to record "last successful workflow" for /workflow save. */
39
15
  getCurrentSessionKey?: () => string | undefined;
16
+ getConfig: () => import('../../config/schema.js').Config | undefined;
17
+ startWorkflowRun?: (params: StartWorkflowRunServiceParams) => Promise<WorkflowRunServiceResult>;
40
18
  }
41
19
  export declare function createWorkflowTool(deps: WorkflowToolDeps): AgentTool;
@@ -1,75 +1,42 @@
1
+ import { extractProfileAgentId } from "../../config/agent-profile.js";
1
2
  import { createLogger } from "../../utils/logger/index.js";
2
3
  import { init_logger } from "../../utils/logger.js";
3
- import { init_providers, resolveModel } from "../../providers/index.js";
4
4
  import { parseWorkflowScript } from "../workflow/parser.js";
5
- import { getLastWorkflowMemory } from "../workflow/last-run-memory.js";
6
- import { previewValue, recomputeCounts, renderWorkflowText } from "../workflow/snapshot.js";
7
- import { runWorkflow } from "../workflow/runtime.js";
8
- import { DelegateSubagentRunner } from "../workflow/subagent-runner.js";
9
5
  import "../workflow/index.js";
10
6
  import { Type } from "@sinclair/typebox";
11
7
  //#region src/agent/tools/workflow-tool.ts
12
8
  /**
13
- * `workflow` — the AgentTool the parent model calls to spawn a fan-out run.
14
- *
15
- * Shape mirrors `delegate-tool`: factory builds a closure over deps; `execute`
16
- * parses the script, instantiates the {@link DelegateSubagentRunner}, drives the
17
- * {@link runWorkflow} runtime, and pushes a live text snapshot through
18
- * `onUpdate` for streaming UIs (TUI, gateway console).
19
- *
20
- * Why this lives in `src/agent/tools/` (not under `src/agent/workflow/`):
21
- * the runtime is reusable infrastructure; the AgentTool wrapping is a
22
- * presentation concern that depends on the AgentToolsFactory wiring. Keeping
23
- * the wrapper here matches how `delegate-tool` and `execute-code-tool` are
24
- * organised today.
9
+ * `workflow` — starts a persisted workflow run in a dedicated chat session.
25
10
  */
26
11
  init_logger();
27
- init_providers();
28
12
  const log = createLogger("workflow-tool");
29
- const DEFAULT_TIMEOUT_SEC = 1800;
30
- const MAX_TIMEOUT_SEC = 14400;
31
- const DEFAULT_MAX_CONCURRENCY = 16;
32
- const DEFAULT_MAX_SUBAGENTS = 1e3;
33
13
  const WorkflowToolSchema = Type.Object({
34
14
  name: Type.Optional(Type.String({ description: "Name of a saved workflow to run. Either `name` or `script` is required. Use `name` whenever the user references a known workflow (built-in or in ~/.xopc/workflows/)." })),
35
- script: Type.Optional(Type.String({ description: [
36
- "Raw JavaScript workflow script (no Markdown fences, no TypeScript syntax). Ignored when `name` is set.",
37
- "First statement: export const meta = { name: 'snake_case', description: 'short, human-readable' }.",
38
- "Use phase(title), agent(prompt, opts), parallel(arrayOfFunctions), pipeline(items, ...stages), log(message), args, and budget.",
39
- "The script must call agent() at least once.",
40
- "parallel() requires functions: await parallel(items.map(item => () => agent(...)))."
41
- ].join(" ") })),
42
- args: Type.Optional(Type.Any({ description: "Optional JSON value exposed to the workflow script as the global `args`." }))
15
+ script: Type.Optional(Type.String({ description: ["Raw JavaScript workflow script (no Markdown fences, no TypeScript syntax). Ignored when `name` is set.", "First statement: export const meta = { name: 'snake_case', description: 'short, human-readable' }."].join(" ") })),
16
+ args: Type.Optional(Type.Any({ description: "Optional JSON value passed as workflow input payload." })),
17
+ goal: Type.Optional(Type.String({ description: "Optional goal or task description for this workflow run (defaults to user intent in chat)." }))
43
18
  });
44
19
  function createWorkflowTool(deps) {
45
20
  return {
46
21
  name: "workflow",
47
22
  label: "◆ Workflow",
48
23
  description: [
49
- "Run a deterministic JavaScript workflow that orchestrates multiple isolated subagents through agent(), parallel(), and pipeline().",
50
- "Two ways to invoke:",
51
- " 1. `name`: run a saved workflow from the catalog (built-in or ~/.xopc/workflows/). Prefer this when the user references a known name.",
52
- " 2. `script`: provide a raw JS workflow inline. Use when no saved workflow fits. Header is required: export const meta = { name, description }.",
53
- "Named-workflow triggers — call this tool with `{ name: \"<name>\" }` IMMEDIATELY when the user message is any of:",
54
- " • a bare workflow name like \"/audit_repo\", \"/pr_review\", \"/research\", or \"audit_repo\"",
55
- " • \"run the audit_repo workflow\", \"review this PR\", \"debug this error\", \"kick off research\", \"do a multi_perspective_review on X\" (extract args when natural: target, question, error, diff)",
56
- " • after /workflows lists saved workflows and the user picks one",
57
- "Use phase(title) at runtime to mark progress groups. Each agent() returns a string, or a schema-validated object when opts.schema is set.",
58
- "Prefer for decomposable work: repo audits, PR review, incident triage, multi-perspective review, fan-out research, large refactors. Do not use for a single quick read/edit.",
59
- "parallel() takes thunks, not promises: parallel(items.map(item => () => agent(...))).",
60
- "pipeline(items, ...stages) interleaves items across stages — fastest path by default; only use parallel() when you genuinely need a cross-item barrier.",
61
- "Failed agent()/parallel()/pipeline() entries resolve to null; check before synthesizing.",
62
- "Do not use Date.now(), Math.random(), new Date(), require, import, fs, or network APIs — they are unavailable for determinism.",
63
- "Always end with a synthesis agent() that consolidates findings, especially when you fan out for review or research."
64
- ].join("\n\n"),
24
+ "Start a multi-agent workflow run in its own chat session.",
25
+ "Use `name` for catalog workflows, or `script` for an inline workflow (saved under meta.name before run).",
26
+ "Returns immediately with runId + sessionKey track progress in the linked chat session."
27
+ ].join(" "),
65
28
  parameters: WorkflowToolSchema,
66
- async execute(_toolCallId, params, signal, onUpdate) {
67
- let script;
68
- let resolvedSource = "script";
29
+ async execute(_toolCallId, params) {
30
+ if (!deps.startWorkflowRun) return {
31
+ content: [{
32
+ type: "text",
33
+ text: "workflow: gateway workflow runs are not available in this context"
34
+ }],
35
+ details: { error: "workflow_run_unavailable" }
36
+ };
37
+ let definitionId;
69
38
  try {
70
- const resolved = resolveScript(params, deps.catalog);
71
- script = resolved.script;
72
- resolvedSource = resolved.source;
39
+ definitionId = resolveDefinitionId(params, deps.catalog);
73
40
  } catch (e) {
74
41
  const message = e instanceof Error ? e.message : String(e);
75
42
  return {
@@ -80,197 +47,78 @@ function createWorkflowTool(deps) {
80
47
  details: { error: message }
81
48
  };
82
49
  }
83
- const wfCfg = deps.getConfig()?.agents?.defaults?.workflow;
84
- const concurrency = wfCfg?.maxConcurrency ?? DEFAULT_MAX_CONCURRENCY;
85
- const maxSubagents = wfCfg?.maxSubagents ?? DEFAULT_MAX_SUBAGENTS;
86
- const timeoutSec = clampTimeout(wfCfg?.defaultTimeoutSec);
87
- let meta;
50
+ const config = deps.getConfig();
51
+ const parentSessionKey = deps.getCurrentSessionKey?.()?.trim();
52
+ const agentId = extractProfileAgentId(parentSessionKey, config);
53
+ const goal = params.goal?.trim() || "";
54
+ const source = parentSessionKey ? {
55
+ kind: "chat",
56
+ sessionKey: parentSessionKey
57
+ } : { kind: "api" };
88
58
  try {
89
- meta = parseWorkflowScript(script).meta;
90
- } catch (e) {
91
- const message = e instanceof Error ? e.message : String(e);
92
- return {
59
+ const result = await deps.startWorkflowRun({
60
+ agentId,
61
+ definitionId,
62
+ goal,
63
+ input: params.args,
64
+ parentSessionKey,
65
+ source
66
+ });
67
+ if (result.ok === false) return {
93
68
  content: [{
94
69
  type: "text",
95
- text: resolvedSource === "name" ? `workflow "${params.name}" failed to parse: ${message}` : `workflow parse error: ${message}`
70
+ text: `workflow: ${result.message}`
96
71
  }],
97
- details: { error: message }
72
+ details: { error: result.message }
98
73
  };
99
- }
100
- const snapshot = {
101
- name: meta.name,
102
- description: meta.description,
103
- phases: [],
104
- logs: [],
105
- agents: [],
106
- agentCount: 0,
107
- runningCount: 0,
108
- doneCount: 0,
109
- errorCount: 0,
110
- skippedCount: 0
111
- };
112
- const pushUpdate = (completed = false) => {
113
- recomputeCounts(snapshot);
114
- onUpdate?.({
115
- content: [{
116
- type: "text",
117
- text: renderWorkflowText(snapshot, completed, { showResultPreviews: false })
118
- }],
119
- details: snapshot
120
- });
121
- };
122
- const runner = new DelegateSubagentRunner({
123
- workspace: deps.workspace,
124
- bus: deps.bus,
125
- getDefaultModel: deps.getSubagentModel,
126
- getConfig: deps.getConfig,
127
- toolExecutorConfig: deps.toolExecutorConfig,
128
- buildChildTools: deps.buildChildTools
129
- });
130
- const resolveModelId = (modelId) => resolveModel(modelId);
131
- const controller = new AbortController();
132
- const onParentAbort = () => controller.abort();
133
- signal?.addEventListener("abort", onParentAbort, { once: true });
134
- const timeoutHandle = timeoutSec > 0 ? setTimeout(() => controller.abort(), timeoutSec * 1e3) : void 0;
135
- pushUpdate();
136
- try {
137
- const result = await runWorkflow(script, {
138
- runner,
139
- resolveModelId
140
- }, {
141
- cwd: deps.workspace,
142
- args: params.args,
143
- signal: controller.signal,
144
- concurrency,
145
- maxSubagents,
146
- onLog: (message) => {
147
- snapshot.logs.push(message);
148
- pushUpdate();
149
- },
150
- onPhase: (title) => {
151
- snapshot.currentPhase = title;
152
- if (!snapshot.phases.includes(title)) snapshot.phases.push(title);
153
- pushUpdate();
154
- },
155
- onAgentStart: (event) => {
156
- snapshot.agents.push({
157
- id: event.id,
158
- label: event.label,
159
- phase: event.phase,
160
- prompt: event.prompt,
161
- status: "running"
162
- });
163
- pushUpdate();
164
- },
165
- onAgentEnd: (event) => {
166
- const agent = findAgentById(snapshot.agents, event.id);
167
- if (agent) {
168
- agent.status = event.status;
169
- agent.resultPreview = previewValue(event.result);
170
- }
171
- pushUpdate();
172
- }
173
- });
174
- if (result.agentCount === 0) {
175
- const reason = "workflow scripts must call agent() at least once; this workflow declared phases but never ran a subagent.";
176
- snapshot.logs.push(reason);
177
- pushUpdate(true);
178
- return {
179
- content: [{
180
- type: "text",
181
- text: reason
182
- }],
183
- details: snapshot
184
- };
185
- }
186
- snapshot.result = result.result;
187
- snapshot.durationMs = result.durationMs;
188
- pushUpdate(true);
189
- try {
190
- getLastWorkflowMemory().record(deps.getCurrentSessionKey?.(), {
191
- script,
192
- metaName: result.meta.name,
193
- source: resolvedSource,
194
- recordedAt: Date.now()
195
- });
196
- } catch {}
197
74
  return {
198
75
  content: [{
199
76
  type: "text",
200
- text: `workflow ${result.meta.name} completed: ${result.agentCount} subagent(s), ${snapshot.errorCount} error(s).\n\nResult:\n${safeStringify(result.result)}`
77
+ text: `${goal ? `Started workflow \`${definitionId}\` (run ${result.runId}). Open chat session to track progress and continue.` : `Started workflow \`${definitionId}\` (run ${result.runId}). Open the workflow chat session to track progress.`}\n\nsessionKey: ${result.sessionKey}`
201
78
  }],
202
- details: snapshot
79
+ details: {
80
+ runId: result.runId,
81
+ sessionKey: result.sessionKey,
82
+ definitionId,
83
+ parentSessionKey: parentSessionKey ?? null
84
+ }
203
85
  };
204
86
  } catch (e) {
205
- if (controller.signal.aborted) {
206
- for (const a of snapshot.agents) if (a.status === "running") {
207
- a.status = "skipped";
208
- a.error = "aborted";
209
- }
210
- pushUpdate(true);
211
- return {
212
- content: [{
213
- type: "text",
214
- text: signal?.aborted ? "workflow aborted" : `workflow timed out after ${timeoutSec}s`
215
- }],
216
- details: snapshot
217
- };
218
- }
219
87
  const message = e instanceof Error ? e.message : String(e);
220
88
  log.warn({
221
89
  err: e,
222
90
  errorMessage: message,
223
- workflow: meta.name
224
- }, `workflow failed: ${message}`);
225
- snapshot.logs.push(`workflow failed: ${message}`);
226
- pushUpdate(true);
91
+ workflow: definitionId
92
+ }, `workflow start failed: ${message}`);
227
93
  return {
228
94
  content: [{
229
95
  type: "text",
230
- text: `workflow failed: ${message}`
96
+ text: `workflow: ${message}`
231
97
  }],
232
- details: snapshot
98
+ details: { error: message }
233
99
  };
234
- } finally {
235
- if (timeoutHandle) clearTimeout(timeoutHandle);
236
- signal?.removeEventListener("abort", onParentAbort);
237
100
  }
238
101
  }
239
102
  };
240
103
  }
104
+ function resolveDefinitionId(params, catalog) {
105
+ const name = params.name?.trim();
106
+ if (name) {
107
+ catalog.load(name);
108
+ return name;
109
+ }
110
+ if (!params.script?.trim()) throw new Error("either `name` or `script` is required.");
111
+ const script = normalizeScript(params.script);
112
+ const meta = parseWorkflowScript(script).meta;
113
+ catalog.save(meta.name, script);
114
+ return meta.name;
115
+ }
241
116
  function normalizeScript(script) {
242
117
  let text = script.trim();
243
118
  const fence = text.match(/^```(?:js|javascript)?\s*\n([\s\S]*?)\n```$/i);
244
119
  if (fence) text = fence[1].trim();
245
120
  return text;
246
121
  }
247
- function resolveScript(params, catalog) {
248
- const name = params.name?.trim();
249
- if (name) return {
250
- script: catalog.load(name).script,
251
- source: "name"
252
- };
253
- if (!params.script || !params.script.trim()) throw new Error("either `name` or `script` is required.");
254
- return {
255
- script: normalizeScript(params.script),
256
- source: "script"
257
- };
258
- }
259
- function clampTimeout(requested) {
260
- const v = typeof requested === "number" && Number.isFinite(requested) ? requested : DEFAULT_TIMEOUT_SEC;
261
- if (v <= 0) return 0;
262
- return Math.min(MAX_TIMEOUT_SEC, Math.max(1, Math.floor(v)));
263
- }
264
- function findAgentById(agents, id) {
265
- for (let i = agents.length - 1; i >= 0; i--) if (agents[i].id === id) return agents[i];
266
- }
267
- function safeStringify(value) {
268
- try {
269
- return JSON.stringify(value, null, 2);
270
- } catch {
271
- return String(value);
272
- }
273
- }
274
122
  //#endregion
275
123
  export { createWorkflowTool };
276
124
 
@@ -1 +1 @@
1
- {"version":3,"file":"workflow-tool.js","names":["resolveModelById"],"sources":["../../../../src/agent/tools/workflow-tool.ts"],"sourcesContent":["/**\n * `workflow` — the AgentTool the parent model calls to spawn a fan-out run.\n *\n * Shape mirrors `delegate-tool`: factory builds a closure over deps; `execute`\n * parses the script, instantiates the {@link DelegateSubagentRunner}, drives the\n * {@link runWorkflow} runtime, and pushes a live text snapshot through\n * `onUpdate` for streaming UIs (TUI, gateway console).\n *\n * Why this lives in `src/agent/tools/` (not under `src/agent/workflow/`):\n * the runtime is reusable infrastructure; the AgentTool wrapping is a\n * presentation concern that depends on the AgentToolsFactory wiring. Keeping\n * the wrapper here matches how `delegate-tool` and `execute-code-tool` are\n * organised today.\n */\n\nimport { Type } from '@sinclair/typebox';\nimport type { AgentTool, AgentToolResult } from '@earendil-works/pi-agent-core';\nimport type { Api, Model } from '@earendil-works/pi-ai';\n\nimport type { Config } from '../../config/schema.js';\nimport type { MessageBus } from '../../infra/bus/index.js';\nimport { createLogger } from '../../utils/logger.js';\n\nimport type { BuildChildToolsOptions } from '../child-agent-factory.js';\nimport {\n DelegateSubagentRunner,\n getLastWorkflowMemory,\n parseWorkflowScript,\n previewValue,\n recomputeCounts,\n renderWorkflowText,\n runWorkflow,\n type WorkflowAgentSnapshot,\n type WorkflowCatalog,\n type WorkflowMeta,\n type WorkflowSnapshot,\n} from '../workflow/index.js';\nimport { resolveModel as resolveModelById } from '../../providers/index.js';\nimport type { ToolExecutorConfig } from './executor.js';\n\nconst log = createLogger('workflow-tool');\n\nconst DEFAULT_TIMEOUT_SEC = 30 * 60;\nconst MAX_TIMEOUT_SEC = 4 * 60 * 60;\nconst DEFAULT_MAX_CONCURRENCY = 16;\nconst DEFAULT_MAX_SUBAGENTS = 1000;\n\nconst WorkflowToolSchema = Type.Object({\n name: Type.Optional(\n Type.String({\n description:\n 'Name of a saved workflow to run. Either `name` or `script` is required. ' +\n 'Use `name` whenever the user references a known workflow (built-in or in ~/.xopc/workflows/).',\n }),\n ),\n script: Type.Optional(\n Type.String({\n description: [\n 'Raw JavaScript workflow script (no Markdown fences, no TypeScript syntax). Ignored when `name` is set.',\n \"First statement: export const meta = { name: 'snake_case', description: 'short, human-readable' }.\",\n 'Use phase(title), agent(prompt, opts), parallel(arrayOfFunctions), pipeline(items, ...stages), log(message), args, and budget.',\n 'The script must call agent() at least once.',\n 'parallel() requires functions: await parallel(items.map(item => () => agent(...))).',\n ].join(' '),\n }),\n ),\n args: Type.Optional(\n Type.Any({\n description: 'Optional JSON value exposed to the workflow script as the global `args`.',\n }),\n ),\n});\n\nexport type WorkflowToolInput = {\n name?: string;\n script?: string;\n args?: unknown;\n};\n\nexport interface WorkflowToolDeps {\n workspace: string;\n bus: MessageBus;\n /** Returns the parent agent's primary model — subagents default to this. */\n getSubagentModel: () => Model<Api>;\n getConfig: () => Config | undefined;\n /** Same injection point delegate-tool uses; supplied by AgentToolsFactory. */\n buildChildTools: (opts: BuildChildToolsOptions) => AgentTool<any, any>[];\n toolExecutorConfig?: Partial<ToolExecutorConfig>;\n /** Catalog for `name` lookups (built-in + ~/.xopc/workflows/). */\n catalog: WorkflowCatalog;\n /** Per-call sessionKey lookup — used to record \"last successful workflow\" for /workflow save. */\n getCurrentSessionKey?: () => string | undefined;\n}\n\nexport function createWorkflowTool(deps: WorkflowToolDeps): AgentTool {\n return {\n name: 'workflow',\n label: '◆ Workflow',\n description: [\n 'Run a deterministic JavaScript workflow that orchestrates multiple isolated subagents through agent(), parallel(), and pipeline().',\n 'Two ways to invoke:',\n ' 1. `name`: run a saved workflow from the catalog (built-in or ~/.xopc/workflows/). Prefer this when the user references a known name.',\n ' 2. `script`: provide a raw JS workflow inline. Use when no saved workflow fits. Header is required: export const meta = { name, description }.',\n 'Named-workflow triggers — call this tool with `{ name: \"<name>\" }` IMMEDIATELY when the user message is any of:',\n ' • a bare workflow name like \"/audit_repo\", \"/pr_review\", \"/research\", or \"audit_repo\"',\n ' • \"run the audit_repo workflow\", \"review this PR\", \"debug this error\", \"kick off research\", \"do a multi_perspective_review on X\" (extract args when natural: target, question, error, diff)',\n ' • after /workflows lists saved workflows and the user picks one',\n 'Use phase(title) at runtime to mark progress groups. Each agent() returns a string, or a schema-validated object when opts.schema is set.',\n 'Prefer for decomposable work: repo audits, PR review, incident triage, multi-perspective review, fan-out research, large refactors. Do not use for a single quick read/edit.',\n 'parallel() takes thunks, not promises: parallel(items.map(item => () => agent(...))).',\n 'pipeline(items, ...stages) interleaves items across stages — fastest path by default; only use parallel() when you genuinely need a cross-item barrier.',\n 'Failed agent()/parallel()/pipeline() entries resolve to null; check before synthesizing.',\n 'Do not use Date.now(), Math.random(), new Date(), require, import, fs, or network APIs — they are unavailable for determinism.',\n 'Always end with a synthesis agent() that consolidates findings, especially when you fan out for review or research.',\n ].join('\\n\\n'),\n parameters: WorkflowToolSchema,\n\n async execute(\n _toolCallId: string,\n params: WorkflowToolInput,\n signal?: AbortSignal,\n onUpdate?: (update: AgentToolResult<WorkflowSnapshot | undefined>) => void,\n ): Promise<AgentToolResult<WorkflowSnapshot | { error: string }>> {\n let script: string;\n let resolvedSource: 'name' | 'script' = 'script';\n try {\n const resolved = resolveScript(params, deps.catalog);\n script = resolved.script;\n resolvedSource = resolved.source;\n } catch (e) {\n const message = e instanceof Error ? e.message : String(e);\n return {\n content: [{ type: 'text', text: `workflow: ${message}` }],\n details: { error: message },\n };\n }\n\n const cfg = deps.getConfig();\n const wfCfg = cfg?.agents?.defaults?.workflow;\n const concurrency = wfCfg?.maxConcurrency ?? DEFAULT_MAX_CONCURRENCY;\n const maxSubagents = wfCfg?.maxSubagents ?? DEFAULT_MAX_SUBAGENTS;\n const timeoutSec = clampTimeout(wfCfg?.defaultTimeoutSec);\n\n // Parse early so a bad script returns an error result instead of throwing\n // through the agent loop. The runtime parses again, but that's cheap.\n let meta: WorkflowMeta;\n try {\n meta = parseWorkflowScript(script).meta;\n } catch (e) {\n const message = e instanceof Error ? e.message : String(e);\n return {\n content: [\n {\n type: 'text',\n text:\n resolvedSource === 'name'\n ? `workflow \"${params.name}\" failed to parse: ${message}`\n : `workflow parse error: ${message}`,\n },\n ],\n details: { error: message },\n };\n }\n\n const snapshot: WorkflowSnapshot = {\n name: meta.name,\n description: meta.description,\n phases: [],\n logs: [],\n agents: [],\n agentCount: 0,\n runningCount: 0,\n doneCount: 0,\n errorCount: 0,\n skippedCount: 0,\n };\n\n const pushUpdate = (completed = false) => {\n recomputeCounts(snapshot);\n onUpdate?.({\n content: [\n {\n type: 'text',\n text: renderWorkflowText(snapshot, completed, { showResultPreviews: false }),\n },\n ],\n details: snapshot,\n });\n };\n\n const runner = new DelegateSubagentRunner({\n workspace: deps.workspace,\n bus: deps.bus,\n getDefaultModel: deps.getSubagentModel,\n getConfig: deps.getConfig,\n toolExecutorConfig: deps.toolExecutorConfig,\n buildChildTools: deps.buildChildTools,\n });\n\n const resolveModelId = (modelId: string): Model<Api> => resolveModelById(modelId);\n\n // Combined abort: parent signal + per-run timeout.\n const controller = new AbortController();\n const onParentAbort = () => controller.abort();\n signal?.addEventListener('abort', onParentAbort, { once: true });\n const timeoutHandle =\n timeoutSec > 0\n ? setTimeout(() => controller.abort(), timeoutSec * 1000)\n : undefined;\n\n pushUpdate();\n\n try {\n const result = await runWorkflow(script, { runner, resolveModelId }, {\n cwd: deps.workspace,\n args: params.args,\n signal: controller.signal,\n concurrency,\n maxSubagents,\n onLog: (message) => {\n snapshot.logs.push(message);\n pushUpdate();\n },\n onPhase: (title) => {\n snapshot.currentPhase = title;\n if (!snapshot.phases.includes(title)) snapshot.phases.push(title);\n pushUpdate();\n },\n onAgentStart: (event) => {\n snapshot.agents.push({\n id: event.id,\n label: event.label,\n phase: event.phase,\n prompt: event.prompt,\n status: 'running',\n });\n pushUpdate();\n },\n onAgentEnd: (event) => {\n const agent = findAgentById(snapshot.agents, event.id);\n if (agent) {\n agent.status = event.status;\n agent.resultPreview = previewValue(event.result);\n }\n pushUpdate();\n },\n });\n\n if (result.agentCount === 0) {\n const reason =\n 'workflow scripts must call agent() at least once; this workflow declared phases but never ran a subagent.';\n snapshot.logs.push(reason);\n pushUpdate(true);\n return {\n content: [{ type: 'text', text: reason }],\n details: snapshot,\n };\n }\n\n snapshot.result = result.result;\n snapshot.durationMs = result.durationMs;\n pushUpdate(true);\n\n // Record for /workflow save — last successful run per session.\n // Failures are intentionally skipped so users do not save broken scripts.\n try {\n getLastWorkflowMemory().record(deps.getCurrentSessionKey?.(), {\n script,\n metaName: result.meta.name,\n source: resolvedSource,\n recordedAt: Date.now(),\n });\n } catch {\n // Memory recording is best-effort; never break a successful run on it.\n }\n\n return {\n content: [\n {\n type: 'text',\n text: `workflow ${result.meta.name} completed: ${result.agentCount} subagent(s), ${snapshot.errorCount} error(s).\\n\\nResult:\\n${safeStringify(result.result)}`,\n },\n ],\n details: snapshot,\n };\n } catch (e) {\n if (controller.signal.aborted) {\n for (const a of snapshot.agents) {\n if (a.status === 'running') {\n a.status = 'skipped';\n a.error = 'aborted';\n }\n }\n pushUpdate(true);\n const reason = signal?.aborted ? 'workflow aborted' : `workflow timed out after ${timeoutSec}s`;\n return {\n content: [{ type: 'text', text: reason }],\n details: snapshot,\n };\n }\n const message = e instanceof Error ? e.message : String(e);\n log.warn({ err: e, errorMessage: message, workflow: meta.name }, `workflow failed: ${message}`);\n snapshot.logs.push(`workflow failed: ${message}`);\n pushUpdate(true);\n return {\n content: [{ type: 'text', text: `workflow failed: ${message}` }],\n details: snapshot,\n };\n } finally {\n if (timeoutHandle) clearTimeout(timeoutHandle);\n signal?.removeEventListener('abort', onParentAbort);\n }\n },\n } as unknown as AgentTool;\n}\n\n// ---------------------------------------------------------------------------\n\nfunction normalizeScript(script: string): string {\n let text = script.trim();\n const fence = text.match(/^```(?:js|javascript)?\\s*\\n([\\s\\S]*?)\\n```$/i);\n if (fence) text = fence[1].trim();\n return text;\n}\n\nfunction resolveScript(\n params: WorkflowToolInput,\n catalog: WorkflowCatalog,\n): { script: string; source: 'name' | 'script' } {\n const name = params.name?.trim();\n if (name) {\n const loaded = catalog.load(name);\n return { script: loaded.script, source: 'name' };\n }\n if (!params.script || !params.script.trim()) {\n throw new Error('either `name` or `script` is required.');\n }\n return { script: normalizeScript(params.script), source: 'script' };\n}\n\nfunction clampTimeout(requested: number | undefined): number {\n const v = typeof requested === 'number' && Number.isFinite(requested) ? requested : DEFAULT_TIMEOUT_SEC;\n if (v <= 0) return 0;\n return Math.min(MAX_TIMEOUT_SEC, Math.max(1, Math.floor(v)));\n}\n\nfunction findAgentById(agents: WorkflowAgentSnapshot[], id: number): WorkflowAgentSnapshot | undefined {\n // Linear scan — agent lists are small in practice (capped at maxSubagents).\n for (let i = agents.length - 1; i >= 0; i--) {\n if (agents[i].id === id) return agents[i];\n }\n return undefined;\n}\n\nfunction safeStringify(value: unknown): string {\n try {\n return JSON.stringify(value, null, 2);\n } catch {\n return String(value);\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"workflow-tool.js","names":[],"sources":["../../../../src/agent/tools/workflow-tool.ts"],"sourcesContent":["/**\n * `workflow` — starts a persisted workflow run in a dedicated chat session.\n */\n\nimport { Type } from '@sinclair/typebox';\nimport type { AgentTool, AgentToolResult } from '@earendil-works/pi-agent-core';\n\nimport { extractProfileAgentId } from '../../config/agent-profile.js';\nimport { createLogger } from '../../utils/logger.js';\nimport { parseWorkflowScript } from '../workflow/index.js';\nimport type { WorkflowCatalog } from '../workflow/catalog.js';\nimport type {\n StartWorkflowRunServiceParams,\n WorkflowRunServiceResult,\n} from '../../workflows/service/workflow-run-service.types.js';\n\nconst log = createLogger('workflow-tool');\n\nconst WorkflowToolSchema = Type.Object({\n name: Type.Optional(\n Type.String({\n description:\n 'Name of a saved workflow to run. Either `name` or `script` is required. ' +\n 'Use `name` whenever the user references a known workflow (built-in or in ~/.xopc/workflows/).',\n }),\n ),\n script: Type.Optional(\n Type.String({\n description: [\n 'Raw JavaScript workflow script (no Markdown fences, no TypeScript syntax). Ignored when `name` is set.',\n \"First statement: export const meta = { name: 'snake_case', description: 'short, human-readable' }.\",\n ].join(' '),\n }),\n ),\n args: Type.Optional(\n Type.Any({\n description: 'Optional JSON value passed as workflow input payload.',\n }),\n ),\n goal: Type.Optional(\n Type.String({\n description: 'Optional goal or task description for this workflow run (defaults to user intent in chat).',\n }),\n ),\n});\n\nexport type WorkflowToolInput = {\n name?: string;\n script?: string;\n args?: unknown;\n goal?: string;\n};\n\nexport interface WorkflowToolDeps {\n catalog: WorkflowCatalog;\n getCurrentSessionKey?: () => string | undefined;\n getConfig: () => import('../../config/schema.js').Config | undefined;\n startWorkflowRun?: (params: StartWorkflowRunServiceParams) => Promise<WorkflowRunServiceResult>;\n}\n\nexport function createWorkflowTool(deps: WorkflowToolDeps): AgentTool {\n return {\n name: 'workflow',\n label: '◆ Workflow',\n description: [\n 'Start a multi-agent workflow run in its own chat session.',\n 'Use `name` for catalog workflows, or `script` for an inline workflow (saved under meta.name before run).',\n 'Returns immediately with runId + sessionKey — track progress in the linked chat session.',\n ].join(' '),\n parameters: WorkflowToolSchema,\n\n async execute(\n _toolCallId: string,\n params: WorkflowToolInput,\n ): Promise<AgentToolResult<{ runId: string; sessionKey: string } | { error: string }>> {\n if (!deps.startWorkflowRun) {\n return {\n content: [{ type: 'text', text: 'workflow: gateway workflow runs are not available in this context' }],\n details: { error: 'workflow_run_unavailable' },\n };\n }\n\n let definitionId: string;\n try {\n definitionId = resolveDefinitionId(params, deps.catalog);\n } catch (e) {\n const message = e instanceof Error ? e.message : String(e);\n return {\n content: [{ type: 'text', text: `workflow: ${message}` }],\n details: { error: message },\n };\n }\n\n const config = deps.getConfig();\n const parentSessionKey = deps.getCurrentSessionKey?.()?.trim();\n const agentId = extractProfileAgentId(parentSessionKey, config);\n\n const goal = params.goal?.trim() || '';\n const source = parentSessionKey\n ? ({ kind: 'chat' as const, sessionKey: parentSessionKey })\n : ({ kind: 'api' as const });\n\n try {\n const result = await deps.startWorkflowRun({\n agentId,\n definitionId,\n goal,\n input: params.args,\n parentSessionKey,\n source,\n });\n\n if (result.ok === false) {\n return {\n content: [{ type: 'text', text: `workflow: ${result.message}` }],\n details: { error: result.message },\n };\n }\n\n const summary = goal\n ? `Started workflow \\`${definitionId}\\` (run ${result.runId}). Open chat session to track progress and continue.`\n : `Started workflow \\`${definitionId}\\` (run ${result.runId}). Open the workflow chat session to track progress.`;\n\n return {\n content: [\n {\n type: 'text',\n text: `${summary}\\n\\nsessionKey: ${result.sessionKey}`,\n },\n ],\n details: {\n runId: result.runId,\n sessionKey: result.sessionKey,\n definitionId,\n parentSessionKey: parentSessionKey ?? null,\n } as { runId: string; sessionKey: string },\n };\n } catch (e) {\n const message = e instanceof Error ? e.message : String(e);\n log.warn({ err: e, errorMessage: message, workflow: definitionId }, `workflow start failed: ${message}`);\n return {\n content: [{ type: 'text', text: `workflow: ${message}` }],\n details: { error: message },\n };\n }\n },\n } as unknown as AgentTool;\n}\n\nfunction resolveDefinitionId(params: WorkflowToolInput, catalog: WorkflowCatalog): string {\n const name = params.name?.trim();\n if (name) {\n catalog.load(name);\n return name;\n }\n if (!params.script?.trim()) {\n throw new Error('either `name` or `script` is required.');\n }\n const script = normalizeScript(params.script);\n const meta = parseWorkflowScript(script).meta;\n catalog.save(meta.name, script);\n return meta.name;\n}\n\nfunction normalizeScript(script: string): string {\n let text = script.trim();\n const fence = text.match(/^```(?:js|javascript)?\\s*\\n([\\s\\S]*?)\\n```$/i);\n if (fence) text = fence[1].trim();\n return text;\n}\n"],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Apply live subagent progress events onto a {@link WorkflowAgentSnapshot}.
3
+ */
4
+ import type { SubagentProgressEvent, WorkflowAgentSnapshot } from './types.js';
5
+ export declare function applySubagentProgress(agent: WorkflowAgentSnapshot, event: SubagentProgressEvent): boolean;