@xopcai/xopc 0.0.85 → 0.0.87

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 (407) hide show
  1. package/dist/browser-ext/manifest.json +1 -1
  2. package/dist/extensions/feishu/src/adapters/cli-login.js +3 -3
  3. package/dist/extensions/feishu/src/adapters/cli-login.js.map +1 -1
  4. package/dist/extensions/telegram/src/delivery-chat-id.d.ts +1 -1
  5. package/dist/extensions/telegram/src/delivery-chat-id.js +1 -1
  6. package/dist/extensions/telegram/src/delivery-chat-id.js.map +1 -1
  7. package/dist/extensions/telegram/src/routing-integration.js +1 -0
  8. package/dist/extensions/telegram/src/routing-integration.js.map +1 -1
  9. package/dist/extensions/telegram/xopc.extension.json +1 -1
  10. package/dist/extensions/weixin/src/__tests__/workflow-progress.test.js +2 -2
  11. package/dist/extensions/weixin/src/__tests__/workflow-progress.test.js.map +1 -1
  12. package/dist/extensions/weixin/src/api/api.js +2 -2
  13. package/dist/extensions/weixin/src/api/api.js.map +1 -1
  14. package/dist/extensions/weixin/src/auth/accounts.js +12 -12
  15. package/dist/extensions/weixin/src/auth/accounts.js.map +1 -1
  16. package/dist/extensions/weixin/src/delivery-to.js +2 -2
  17. package/dist/extensions/weixin/src/delivery-to.js.map +1 -1
  18. package/dist/extensions/weixin/src/messaging/debug-mode.js +5 -5
  19. package/dist/extensions/weixin/src/messaging/debug-mode.js.map +1 -1
  20. package/dist/extensions/weixin/src/messaging/inbound.js +11 -11
  21. package/dist/extensions/weixin/src/messaging/inbound.js.map +1 -1
  22. package/dist/extensions/weixin/src/storage/sync-buf.js +4 -4
  23. package/dist/extensions/weixin/src/storage/sync-buf.js.map +1 -1
  24. package/dist/extensions/weixin/src/workflow-progress.d.ts +1 -1
  25. package/dist/extensions/weixin/src/workflow-progress.js.map +1 -1
  26. package/dist/gateway/static/root/assets/agents-BEAbXpuP.js +222 -0
  27. package/dist/gateway/static/root/assets/{apps-page-D7v7649T.js → apps-page-Dg8R-Szf.js} +1 -1
  28. package/dist/gateway/static/root/assets/{channels-settings-nCaMb0a7.js → channels-settings-yohw9YSu.js} +1 -1
  29. package/dist/gateway/static/root/assets/{channels-status-swr-C1gZBcJV.js → channels-status-swr-BSHqqCF1.js} +1 -1
  30. package/dist/gateway/static/root/assets/{cron-api-CoYK0hlm.js → cron-api-0h_QT8U3.js} +1 -1
  31. package/dist/gateway/static/root/assets/{cron-page-DeGo-Vjc.js → cron-page-BkfKFfFk.js} +1 -1
  32. package/dist/gateway/static/root/assets/{dist-DaK4dsss.js → dist-Cmjp2APP.js} +1 -1
  33. package/dist/gateway/static/root/assets/{extension-debug-page-BZngZWbO.js → extension-debug-page-CFa9z_1N.js} +1 -1
  34. package/dist/gateway/static/root/assets/{extension-page-D6JSyV27.js → extension-page-BI8eaTPq.js} +1 -1
  35. package/dist/gateway/static/root/assets/extension-settings-page-x4BB7q1X.js +1 -0
  36. package/dist/gateway/static/root/assets/{fetch-B2MYHbWg.js → fetch-DRqwef_Q.js} +1 -1
  37. package/dist/gateway/static/root/assets/{field-primitives-Zzl22MvN.js → field-primitives-BiNHBo2Y.js} +1 -1
  38. package/dist/gateway/static/root/assets/{heartbeat-config-api-BtIcpG0O.js → heartbeat-config-api-ZRb8qhuz.js} +1 -1
  39. package/dist/gateway/static/root/assets/{index-D4vM3-P7.js → index-Cu7bKuUi.js} +96 -94
  40. package/dist/gateway/static/root/assets/index-a5gWIdZQ.css +1 -0
  41. package/dist/gateway/static/root/assets/{logs-page-_d4UJ-qQ.js → logs-page-BFZ8GgCv.js} +1 -1
  42. package/dist/gateway/static/root/assets/{sessions-page-5N4aF2Wk.js → sessions-page-CD7AfB-2.js} +1 -1
  43. package/dist/gateway/static/root/assets/settings-form-section-DiqqVs6m.js +1 -0
  44. package/dist/gateway/static/root/assets/settings-page-BBOjEQW3.js +3 -0
  45. package/dist/gateway/static/root/assets/{share-preview-page-D4EG_vM1.js → share-preview-page-n1Gprylk.js} +1 -1
  46. package/dist/gateway/static/root/assets/{skills-page-sPAXhh8w.js → skills-page-CcN_gj--.js} +1 -1
  47. package/dist/gateway/static/root/assets/{theme-store-DryYl3qD.js → theme-store-CZOh1nT3.js} +1 -1
  48. package/dist/gateway/static/root/assets/url-Dd8Q7kZZ.js +3 -0
  49. package/dist/gateway/static/root/assets/{utils-CYO9eTCM.js → utils-CkWBfxs4.js} +1 -1
  50. package/dist/gateway/static/root/assets/{voice-api-key-field-Ds51havm.js → voice-api-key-field-O6awz9hi.js} +1 -1
  51. package/dist/gateway/static/root/index.html +5 -5
  52. package/dist/package.js +1 -1
  53. package/dist/src/agent/agent-scope.d.ts +4 -0
  54. package/dist/src/agent/agent-scope.js +53 -10
  55. package/dist/src/agent/agent-scope.js.map +1 -1
  56. package/dist/src/agent/bootstrap/filter-bootstrap-files.js +2 -1
  57. package/dist/src/agent/bootstrap/filter-bootstrap-files.js.map +1 -1
  58. package/dist/src/agent/embedded/session-tool-result-guard.js +2 -1
  59. package/dist/src/agent/embedded/session-tool-result-guard.js.map +1 -1
  60. package/dist/src/agent/embedded/tool-result-truncation.js +2 -1
  61. package/dist/src/agent/embedded/tool-result-truncation.js.map +1 -1
  62. package/dist/src/agent/fallback/candidates.js +2 -2
  63. package/dist/src/agent/fallback/candidates.js.map +1 -1
  64. package/dist/src/agent/goals/persistent-goal-apis.d.ts +0 -2
  65. package/dist/src/agent/goals/persistent-goal-service.js +0 -1
  66. package/dist/src/agent/goals/persistent-goal-service.js.map +1 -1
  67. package/dist/src/agent/image/generation/normalization.js +2 -12
  68. package/dist/src/agent/image/generation/normalization.js.map +1 -1
  69. package/dist/src/agent/image/generation/provider-registry.d.ts +4 -8
  70. package/dist/src/agent/image/generation/provider-registry.js.map +1 -1
  71. package/dist/src/agent/image/generation/runtime.d.ts +2 -2
  72. package/dist/src/agent/image/generation/runtime.js.map +1 -1
  73. package/dist/src/agent/image/generation/types.d.ts +0 -18
  74. package/dist/src/agent/image/image-helpers.js +6 -1
  75. package/dist/src/agent/image/image-helpers.js.map +1 -1
  76. package/dist/src/agent/image/index.d.ts +1 -1
  77. package/dist/src/agent/inbound/inbound-loop.d.ts +5 -0
  78. package/dist/src/agent/inbound/inbound-loop.js +41 -10
  79. package/dist/src/agent/inbound/inbound-loop.js.map +1 -1
  80. package/dist/src/agent/inbound/turn-dispatcher.d.ts +4 -0
  81. package/dist/src/agent/inbound/turn-dispatcher.js +6 -4
  82. package/dist/src/agent/inbound/turn-dispatcher.js.map +1 -1
  83. package/dist/src/agent/mcp/bundle-mcp-materialize.js +2 -1
  84. package/dist/src/agent/mcp/bundle-mcp-materialize.js.map +1 -1
  85. package/dist/src/agent/mcp/bundle-mcp-names.js +2 -1
  86. package/dist/src/agent/mcp/bundle-mcp-names.js.map +1 -1
  87. package/dist/src/agent/mcp/bundle-mcp-runtime.js +2 -1
  88. package/dist/src/agent/mcp/bundle-mcp-runtime.js.map +1 -1
  89. package/dist/src/agent/mcp/mcp-transport-config.js +2 -1
  90. package/dist/src/agent/mcp/mcp-transport-config.js.map +1 -1
  91. package/dist/src/agent/mcp/mcp-transport.js +2 -1
  92. package/dist/src/agent/mcp/mcp-transport.js.map +1 -1
  93. package/dist/src/agent/media-generation/runtime-shared.js +2 -9
  94. package/dist/src/agent/media-generation/runtime-shared.js.map +1 -1
  95. package/dist/src/agent/messaging/command-handler.d.ts +6 -0
  96. package/dist/src/agent/messaging/command-handler.js +5 -0
  97. package/dist/src/agent/messaging/command-handler.js.map +1 -1
  98. package/dist/src/agent/prompt/safety.d.ts +0 -7
  99. package/dist/src/agent/prompt/safety.js +1 -20
  100. package/dist/src/agent/prompt/safety.js.map +1 -1
  101. package/dist/src/agent/service/build-direct-message-content.js +1 -1
  102. package/dist/src/agent/service/build-direct-message-content.js.map +1 -1
  103. package/dist/src/agent/service/direct-turn-helpers.d.ts +3 -1
  104. package/dist/src/agent/service/direct-turn-helpers.js +6 -1
  105. package/dist/src/agent/service/direct-turn-helpers.js.map +1 -1
  106. package/dist/src/agent/service/process-direct-one-shot.d.ts +4 -0
  107. package/dist/src/agent/service/process-direct-one-shot.js +15 -2
  108. package/dist/src/agent/service/process-direct-one-shot.js.map +1 -1
  109. package/dist/src/agent/service/process-direct-streaming.d.ts +4 -0
  110. package/dist/src/agent/service/process-direct-streaming.js +34 -4
  111. package/dist/src/agent/service/process-direct-streaming.js.map +1 -1
  112. package/dist/src/agent/service/webchat-tts.js +1 -1
  113. package/dist/src/agent/service/webchat-tts.js.map +1 -1
  114. package/dist/src/agent/service.d.ts +8 -0
  115. package/dist/src/agent/service.js +21 -1
  116. package/dist/src/agent/service.js.map +1 -1
  117. package/dist/src/agent/tools/create-share-tool.js +27 -20
  118. package/dist/src/agent/tools/create-share-tool.js.map +1 -1
  119. package/dist/src/agent/tools/factory.js +1 -1
  120. package/dist/src/agent/tools/index.d.ts +0 -1
  121. package/dist/src/agent/tools/index.js +4 -5
  122. package/dist/src/agent/tools/shell.js +0 -13
  123. package/dist/src/agent/tools/shell.js.map +1 -1
  124. package/dist/src/agent/tools/workflow-tool.js +10 -4
  125. package/dist/src/agent/tools/workflow-tool.js.map +1 -1
  126. package/dist/src/agent/workflow/builtins/audit-repo.d.ts +5 -1
  127. package/dist/src/agent/workflow/builtins/audit-repo.js +52 -11
  128. package/dist/src/agent/workflow/builtins/audit-repo.js.map +1 -1
  129. package/dist/src/agent/workflow/builtins/debug-incident.d.ts +13 -0
  130. package/dist/src/agent/workflow/builtins/debug-incident.js +155 -0
  131. package/dist/src/agent/workflow/builtins/debug-incident.js.map +1 -0
  132. package/dist/src/agent/workflow/builtins/index.d.ts +3 -1
  133. package/dist/src/agent/workflow/builtins/index.js +11 -1
  134. package/dist/src/agent/workflow/builtins/index.js.map +1 -1
  135. package/dist/src/agent/workflow/builtins/multi-perspective-review.d.ts +6 -1
  136. package/dist/src/agent/workflow/builtins/multi-perspective-review.js +66 -30
  137. package/dist/src/agent/workflow/builtins/multi-perspective-review.js.map +1 -1
  138. package/dist/src/agent/workflow/builtins/pr-review.d.ts +12 -0
  139. package/dist/src/agent/workflow/builtins/pr-review.js +156 -0
  140. package/dist/src/agent/workflow/builtins/pr-review.js.map +1 -0
  141. package/dist/src/agent/workflow/builtins/research.d.ts +5 -1
  142. package/dist/src/agent/workflow/builtins/research.js +37 -6
  143. package/dist/src/agent/workflow/builtins/research.js.map +1 -1
  144. package/dist/src/agent/workflow/catalog.d.ts +5 -0
  145. package/dist/src/agent/workflow/catalog.js +6 -2
  146. package/dist/src/agent/workflow/catalog.js.map +1 -1
  147. package/dist/src/agent/workflow/channel-capability.d.ts +3 -3
  148. package/dist/src/agent/workflow/index.d.ts +1 -1
  149. package/dist/src/agent/workflow/lint.d.ts +38 -0
  150. package/dist/src/agent/workflow/lint.js +74 -0
  151. package/dist/src/agent/workflow/lint.js.map +1 -0
  152. package/dist/src/agent/workflow/parser.js +13 -1
  153. package/dist/src/agent/workflow/parser.js.map +1 -1
  154. package/dist/src/agent/workflow/runtime.d.ts +3 -0
  155. package/dist/src/agent/workflow/runtime.js +76 -3
  156. package/dist/src/agent/workflow/runtime.js.map +1 -1
  157. package/dist/src/agent/workflow/types.d.ts +11 -1
  158. package/dist/src/browser/index.js +4 -4
  159. package/dist/src/browser/manager.d.ts +1 -3
  160. package/dist/src/browser/manager.js +0 -6
  161. package/dist/src/browser/manager.js.map +1 -1
  162. package/dist/src/browser/providers/browser-ext-install.d.ts +4 -4
  163. package/dist/src/browser/providers/browser-ext-install.js +38 -85
  164. package/dist/src/browser/providers/browser-ext-install.js.map +1 -1
  165. package/dist/src/browser/providers/cloakbrowser.d.ts +0 -5
  166. package/dist/src/browser/providers/cloakbrowser.js +2 -55
  167. package/dist/src/browser/providers/cloakbrowser.js.map +1 -1
  168. package/dist/src/channels/attachments/voice-stt-webchat.js +10 -8
  169. package/dist/src/channels/attachments/voice-stt-webchat.js.map +1 -1
  170. package/dist/src/channels/pairing/allow-from-file.js +9 -9
  171. package/dist/src/channels/pairing/allow-from-file.js.map +1 -1
  172. package/dist/src/channels/pairing/pairing-store.js +6 -6
  173. package/dist/src/channels/pairing/pairing-store.js.map +1 -1
  174. package/dist/src/chat-commands/builtins/session.js +1 -1
  175. package/dist/src/chat-commands/builtins/session.js.map +1 -1
  176. package/dist/src/chat-commands/builtins/tts.js +2 -2
  177. package/dist/src/chat-commands/builtins/tts.js.map +1 -1
  178. package/dist/src/chat-commands/builtins/workflow.js +7 -2
  179. package/dist/src/chat-commands/builtins/workflow.js.map +1 -1
  180. package/dist/src/chat-commands/context.d.ts +3 -0
  181. package/dist/src/chat-commands/context.js +21 -3
  182. package/dist/src/chat-commands/context.js.map +1 -1
  183. package/dist/src/chat-commands/session-key.d.ts +4 -37
  184. package/dist/src/chat-commands/session-key.js +49 -85
  185. package/dist/src/chat-commands/session-key.js.map +1 -1
  186. package/dist/src/chat-commands/types.d.ts +2 -0
  187. package/dist/src/cli/commands/agent/interactive.js +2 -2
  188. package/dist/src/cli/commands/agent/interactive.js.map +1 -1
  189. package/dist/src/cli/commands/agent/sessions.js +2 -2
  190. package/dist/src/cli/commands/agent/sessions.js.map +1 -1
  191. package/dist/src/cli/commands/agent.js +4 -5
  192. package/dist/src/cli/commands/agent.js.map +1 -1
  193. package/dist/src/cli/commands/channels.js +1 -5
  194. package/dist/src/cli/commands/channels.js.map +1 -1
  195. package/dist/src/cli/commands/gateway/lifecycle-core.js +1 -1
  196. package/dist/src/cli/commands/gateway/lifecycle-core.js.map +1 -1
  197. package/dist/src/cli/commands/gateway/logs.d.ts +9 -0
  198. package/dist/src/cli/commands/gateway/logs.js +50 -17
  199. package/dist/src/cli/commands/gateway/logs.js.map +1 -1
  200. package/dist/src/cli/commands/image.js +22 -21
  201. package/dist/src/cli/commands/image.js.map +1 -1
  202. package/dist/src/cli/commands/session/utils.js +2 -2
  203. package/dist/src/cli/commands/session/utils.js.map +1 -1
  204. package/dist/src/cli/commands/update.js +26 -46
  205. package/dist/src/cli/commands/update.js.map +1 -1
  206. package/dist/src/cli/utils/session.d.ts +0 -5
  207. package/dist/src/cli/utils/session.js +1 -6
  208. package/dist/src/cli/utils/session.js.map +1 -1
  209. package/dist/src/commands/agents.config.js +1 -1
  210. package/dist/src/commands/agents.config.js.map +1 -1
  211. package/dist/src/config/agent-profile.js +5 -27
  212. package/dist/src/config/agent-profile.js.map +1 -1
  213. package/dist/src/config/index.js +2 -2
  214. package/dist/src/config/model-input.js +2 -5
  215. package/dist/src/config/model-input.js.map +1 -1
  216. package/dist/src/config/schema.d.ts +201 -217
  217. package/dist/src/config/schema.js +54 -39
  218. package/dist/src/config/schema.js.map +1 -1
  219. package/dist/src/config/workspace-path-helpers.d.ts +1 -2
  220. package/dist/src/config/workspace-path-helpers.js.map +1 -1
  221. package/dist/src/daemon/install-plan.js +25 -1
  222. package/dist/src/daemon/install-plan.js.map +1 -1
  223. package/dist/src/daemon/launchd.d.ts +8 -0
  224. package/dist/src/daemon/launchd.js +5 -12
  225. package/dist/src/daemon/launchd.js.map +1 -1
  226. package/dist/src/daemon/schtasks.d.ts +25 -0
  227. package/dist/src/daemon/schtasks.js +166 -46
  228. package/dist/src/daemon/schtasks.js.map +1 -1
  229. package/dist/src/daemon/service.js +5 -4
  230. package/dist/src/daemon/service.js.map +1 -1
  231. package/dist/src/daemon/systemd.d.ts +6 -0
  232. package/dist/src/daemon/systemd.js +18 -3
  233. package/dist/src/daemon/systemd.js.map +1 -1
  234. package/dist/src/extensions/activation-context.js +0 -1
  235. package/dist/src/extensions/activation-context.js.map +1 -1
  236. package/dist/src/extensions/normalize-manifest.js +0 -1
  237. package/dist/src/extensions/normalize-manifest.js.map +1 -1
  238. package/dist/src/extensions/types/manifest.d.ts +0 -2
  239. package/dist/src/gateway/agent-builtin-tools.d.ts +1 -1
  240. package/dist/src/gateway/agent-builtin-tools.js +1 -0
  241. package/dist/src/gateway/agent-builtin-tools.js.map +1 -1
  242. package/dist/src/gateway/agents-admin.js +10 -2
  243. package/dist/src/gateway/agents-admin.js.map +1 -1
  244. package/dist/src/gateway/heartbeat/service.js +1 -1
  245. package/dist/src/gateway/heartbeat/service.js.map +1 -1
  246. package/dist/src/gateway/hono/app.js +1 -1
  247. package/dist/src/gateway/hono/lib/agent-model.d.ts +18 -10
  248. package/dist/src/gateway/hono/lib/agent-model.js +24 -35
  249. package/dist/src/gateway/hono/lib/agent-model.js.map +1 -1
  250. package/dist/src/gateway/hono/lib/config-payload.js +1 -1
  251. package/dist/src/gateway/hono/lib/config-payload.js.map +1 -1
  252. package/dist/src/gateway/hono/lib/safe-voice-config.js +14 -53
  253. package/dist/src/gateway/hono/lib/safe-voice-config.js.map +1 -1
  254. package/dist/src/gateway/hono/routes/config-patch/agents.js +17 -5
  255. package/dist/src/gateway/hono/routes/config-patch/agents.js.map +1 -1
  256. package/dist/src/gateway/hono/routes/config-patch/channels.js +0 -11
  257. package/dist/src/gateway/hono/routes/config-patch/channels.js.map +1 -1
  258. package/dist/src/gateway/hono/routes/goals.js +1 -1
  259. package/dist/src/gateway/hono/routes/goals.js.map +1 -1
  260. package/dist/src/gateway/hono/routes/sessions.js +28 -7
  261. package/dist/src/gateway/hono/routes/sessions.js.map +1 -1
  262. package/dist/src/gateway/hono/routes/shares.js +14 -12
  263. package/dist/src/gateway/hono/routes/shares.js.map +1 -1
  264. package/dist/src/gateway/hono/routes/tunnel.js +1 -1
  265. package/dist/src/gateway/hono/routes/update.js +4 -2
  266. package/dist/src/gateway/hono/routes/update.js.map +1 -1
  267. package/dist/src/gateway/hono/sse.js +16 -33
  268. package/dist/src/gateway/hono/sse.js.map +1 -1
  269. package/dist/src/gateway/lock.js +10 -10
  270. package/dist/src/gateway/lock.js.map +1 -1
  271. package/dist/src/gateway/ports.js +6 -6
  272. package/dist/src/gateway/ports.js.map +1 -1
  273. package/dist/src/gateway/resolve-webchat-session-key.d.ts +19 -0
  274. package/dist/src/gateway/resolve-webchat-session-key.js +46 -0
  275. package/dist/src/gateway/resolve-webchat-session-key.js.map +1 -0
  276. package/dist/src/gateway/service/run-gateway-agent.js +27 -11
  277. package/dist/src/gateway/service/run-gateway-agent.js.map +1 -1
  278. package/dist/src/gateway/service/sessions-api.d.ts +3 -0
  279. package/dist/src/gateway/service/sessions-api.js +8 -0
  280. package/dist/src/gateway/service/sessions-api.js.map +1 -1
  281. package/dist/src/gateway/service.d.ts +0 -2
  282. package/dist/src/gateway/service.js +2 -7
  283. package/dist/src/gateway/service.js.map +1 -1
  284. package/dist/src/gateway/session-reset-service.d.ts +20 -0
  285. package/dist/src/gateway/session-reset-service.js +54 -0
  286. package/dist/src/gateway/session-reset-service.js.map +1 -0
  287. package/dist/src/gateway/startup-readiness.d.ts +1 -1
  288. package/dist/src/gateway/startup-readiness.js +1 -0
  289. package/dist/src/gateway/startup-readiness.js.map +1 -1
  290. package/dist/src/infra/gateway-processes.js +2 -2
  291. package/dist/src/infra/gateway-processes.js.map +1 -1
  292. package/dist/src/infra/run-command.d.ts +16 -0
  293. package/dist/src/infra/run-command.js +67 -0
  294. package/dist/src/infra/run-command.js.map +1 -0
  295. package/dist/src/infra/update-global.d.ts +45 -0
  296. package/dist/src/infra/update-global.js +224 -0
  297. package/dist/src/infra/update-global.js.map +1 -0
  298. package/dist/src/mcp/channel-bridge.js +1 -1
  299. package/dist/src/mcp/channel-shared.js +2 -1
  300. package/dist/src/mcp/channel-shared.js.map +1 -1
  301. package/dist/src/providers/auth-runtime/auth-profile-store.js +1 -1
  302. package/dist/src/providers/auth-runtime/auth-profile-store.js.map +1 -1
  303. package/dist/src/providers/auth-runtime/resolve-auth.js +1 -12
  304. package/dist/src/providers/auth-runtime/resolve-auth.js.map +1 -1
  305. package/dist/src/providers/auth-runtime/types.d.ts +6 -12
  306. package/dist/src/routing/agent-session-key.d.ts +58 -0
  307. package/dist/src/routing/agent-session-key.js +164 -0
  308. package/dist/src/routing/agent-session-key.js.map +1 -0
  309. package/dist/src/routing/index.d.ts +1 -1
  310. package/dist/src/routing/index.js +4 -2
  311. package/dist/src/routing/index.js.map +1 -1
  312. package/dist/src/routing/resolve-route.d.ts +15 -0
  313. package/dist/src/routing/resolve-route.js +41 -20
  314. package/dist/src/routing/resolve-route.js.map +1 -1
  315. package/dist/src/routing/resolve-tui-session-key.d.ts +25 -0
  316. package/dist/src/routing/resolve-tui-session-key.js +54 -0
  317. package/dist/src/routing/resolve-tui-session-key.js.map +1 -0
  318. package/dist/src/routing/session-key-utils.d.ts +24 -0
  319. package/dist/src/routing/session-key-utils.js +92 -0
  320. package/dist/src/routing/session-key-utils.js.map +1 -0
  321. package/dist/src/routing/session-key.d.ts +19 -49
  322. package/dist/src/routing/session-key.js +143 -116
  323. package/dist/src/routing/session-key.js.map +1 -1
  324. package/dist/src/session/index.d.ts +6 -0
  325. package/dist/src/session/index.js +7 -1
  326. package/dist/src/session/init-session-turn.d.ts +30 -0
  327. package/dist/src/session/init-session-turn.js +102 -0
  328. package/dist/src/session/init-session-turn.js.map +1 -0
  329. package/dist/src/session/lifecycle-timestamps.d.ts +8 -0
  330. package/dist/src/session/lifecycle-timestamps.js +16 -0
  331. package/dist/src/session/lifecycle-timestamps.js.map +1 -0
  332. package/dist/src/session/manager.d.ts +7 -1
  333. package/dist/src/session/manager.js +8 -1
  334. package/dist/src/session/manager.js.map +1 -1
  335. package/dist/src/session/parity/transcript-paths.js +2 -2
  336. package/dist/src/session/parity/transcript-paths.js.map +1 -1
  337. package/dist/src/session/parity/xopc-session-disk-entry.d.ts +6 -0
  338. package/dist/src/session/reset-policy.d.ts +32 -0
  339. package/dist/src/session/reset-policy.js +65 -0
  340. package/dist/src/session/reset-policy.js.map +1 -0
  341. package/dist/src/session/reset-triggers.d.ts +20 -0
  342. package/dist/src/session/reset-triggers.js +63 -0
  343. package/dist/src/session/reset-triggers.js.map +1 -0
  344. package/dist/src/session/reset-type.d.ts +12 -0
  345. package/dist/src/session/reset-type.js +25 -0
  346. package/dist/src/session/reset-type.js.map +1 -0
  347. package/dist/src/session/resolve-session.d.ts +30 -0
  348. package/dist/src/session/resolve-session.js +93 -0
  349. package/dist/src/session/resolve-session.js.map +1 -0
  350. package/dist/src/session/session-title.js +3 -2
  351. package/dist/src/session/session-title.js.map +1 -1
  352. package/dist/src/session/store.d.ts +11 -4
  353. package/dist/src/session/store.js +57 -6
  354. package/dist/src/session/store.js.map +1 -1
  355. package/dist/src/session/transcript-events.js +2 -1
  356. package/dist/src/session/transcript-events.js.map +1 -1
  357. package/dist/src/share/share-url.d.ts +33 -0
  358. package/dist/src/share/share-url.js +56 -14
  359. package/dist/src/share/share-url.js.map +1 -1
  360. package/dist/src/tui/backends/embedded-backend.js +4 -9
  361. package/dist/src/tui/backends/embedded-backend.js.map +1 -1
  362. package/dist/src/tui/backends/gateway-sse-backend.js +1 -1
  363. package/dist/src/tui/backends/gateway-sse-backend.js.map +1 -1
  364. package/dist/src/tui/components/chat-log.js +3 -3
  365. package/dist/src/tui/components/chat-log.js.map +1 -1
  366. package/dist/src/tui/theme.d.ts +0 -2
  367. package/dist/src/tui/theme.js +1 -3
  368. package/dist/src/tui/theme.js.map +1 -1
  369. package/dist/src/tui/tui-commands.d.ts +3 -0
  370. package/dist/src/tui/tui-commands.js +45 -10
  371. package/dist/src/tui/tui-commands.js.map +1 -1
  372. package/dist/src/tui/tui-keybindings-file.js +1 -21
  373. package/dist/src/tui/tui-keybindings-file.js.map +1 -1
  374. package/dist/src/tui/tui-session-actions.d.ts +28 -0
  375. package/dist/src/tui/tui-session-actions.js +88 -0
  376. package/dist/src/tui/tui-session-actions.js.map +1 -0
  377. package/dist/src/tui/tui.js +52 -47
  378. package/dist/src/tui/tui.js.map +1 -1
  379. package/dist/src/utils/string-coerce.d.ts +2 -0
  380. package/dist/src/utils/string-coerce.js +10 -1
  381. package/dist/src/utils/string-coerce.js.map +1 -1
  382. package/dist/src/voice/stt/config-slice.d.ts +2 -5
  383. package/dist/src/voice/stt/config-slice.js +5 -26
  384. package/dist/src/voice/stt/config-slice.js.map +1 -1
  385. package/dist/src/voice/stt/types.d.ts +1 -18
  386. package/dist/src/voice/stt/types.js +4 -2
  387. package/dist/src/voice/stt/types.js.map +1 -1
  388. package/dist/src/voice/tts/config-slice.d.ts +3 -7
  389. package/dist/src/voice/tts/config-slice.js +7 -38
  390. package/dist/src/voice/tts/config-slice.js.map +1 -1
  391. package/dist/src/voice/tts/merge-config.js +2 -48
  392. package/dist/src/voice/tts/merge-config.js.map +1 -1
  393. package/dist/src/voice/tts/providers/alibaba-speech.js +1 -1
  394. package/dist/src/voice/tts/providers/alibaba-speech.js.map +1 -1
  395. package/dist/src/voice/tts/types.d.ts +1 -29
  396. package/dist/src/voice/tts/types.js +19 -17
  397. package/dist/src/voice/tts/types.js.map +1 -1
  398. package/package.json +1 -4
  399. package/dist/gateway/static/root/assets/agents-D3_-kNlZ.js +0 -222
  400. package/dist/gateway/static/root/assets/extension-settings-page-8PZcmWI7.js +0 -1
  401. package/dist/gateway/static/root/assets/index-ew_2L2We.css +0 -1
  402. package/dist/gateway/static/root/assets/settings-form-section-D_tgb8r2.js +0 -1
  403. package/dist/gateway/static/root/assets/settings-page-C18xBt4X.js +0 -3
  404. package/dist/gateway/static/root/assets/url-BwNL6Rgk.js +0 -3
  405. package/dist/src/agent/tools/browser-legacy-tools.d.ts +0 -17
  406. package/dist/src/agent/tools/browser-legacy-tools.js +0 -766
  407. package/dist/src/agent/tools/browser-legacy-tools.js.map +0 -1
@@ -5,5 +5,9 @@
5
5
  * exploration / source-reading angles in parallel, then synthesises a cited
6
6
  * report. Each angle is its own subagent so source reading does not pollute the
7
7
  * parent context.
8
+ *
9
+ * Args:
10
+ * - question: research question
11
+ * - depth: 'quick' (2 angles) | 'standard' (4) | 'deep' (6)
8
12
  */
9
- export declare const RESEARCH_SCRIPT = "export const meta = {\n name: 'research',\n description: 'Multi-angle research on a question with parallel exploration and a cited synthesis.',\n whenToUse: 'User asks a non-trivial research question that benefits from multiple search angles or source reads.',\n phases: [\n { title: 'Frame' },\n { title: 'Sweep' },\n { title: 'Synthesize' },\n ],\n}\n\nconst question = args && typeof args === 'object' && args.question\n ? String(args.question)\n : 'No explicit question supplied; infer from the most recent user turn.'\n\nphase('Frame')\nconst frame = await agent(\n 'Frame this research question. Return 3\u20135 distinct angles worth investigating, plus the single most decisive sub-question for each. Be concrete.\\n\\nQUESTION:\\n' +\n question,\n {\n label: 'framing',\n schema: {\n type: 'object',\n properties: {\n angles: {\n type: 'array',\n items: {\n type: 'object',\n properties: {\n title: { type: 'string' },\n key_question: { type: 'string' },\n },\n required: ['title', 'key_question'],\n },\n },\n },\n required: ['angles'],\n },\n },\n)\n\nif (!frame || !frame.angles?.length) {\n return { ok: false, reason: 'framing failed', question }\n}\n\nphase('Sweep')\nconst angleReports = await parallel(\n frame.angles.map((a) => () =>\n agent(\n 'Investigate this angle. Use search and source-read tools liberally. Distinguish what you can confirm from what is conjecture.\\n\\n' +\n 'ANGLE: ' + a.title + '\\n' +\n 'KEY QUESTION: ' + a.key_question + '\\n\\n' +\n 'Return: 3\u20136 grounded findings (each with a 1-line claim and a source URL or file path), plus the strongest counter-evidence.',\n {\n label: a.title,\n schema: {\n type: 'object',\n properties: {\n findings: {\n type: 'array',\n items: {\n type: 'object',\n properties: {\n claim: { type: 'string' },\n source: { type: 'string' },\n confidence: { type: 'string', enum: ['low', 'med', 'high'] },\n },\n required: ['claim', 'source', 'confidence'],\n },\n },\n counterEvidence: { type: 'string' },\n },\n required: ['findings'],\n },\n },\n ),\n ),\n)\n\nphase('Synthesize')\nconst live = angleReports.filter(Boolean)\nconst synthesis = await agent(\n 'Synthesize a cited research report from these angle-level findings. Drop unsupported or duplicate claims. Use the highest-confidence source per claim. ' +\n 'Return: an executive summary (max 5 sentences), a bullet list of top findings with inline source URLs, and one section listing open questions.\\n\\n' +\n 'QUESTION:\\n' + question + '\\n\\n' +\n JSON.stringify({ angles: frame.angles, reports: live }, null, 2),\n {\n label: 'synthesis',\n schema: {\n type: 'object',\n properties: {\n executiveSummary: { type: 'string' },\n topFindings: {\n type: 'array',\n items: {\n type: 'object',\n properties: {\n claim: { type: 'string' },\n source: { type: 'string' },\n },\n required: ['claim', 'source'],\n },\n },\n openQuestions: { type: 'array', items: { type: 'string' } },\n },\n required: ['executiveSummary', 'topFindings'],\n },\n },\n)\n\nreturn {\n ok: true,\n question,\n ...(synthesis ?? { executiveSummary: 'synthesis failed', topFindings: [] }),\n}\n";
13
+ export declare const RESEARCH_SCRIPT = "export const meta = {\n name: 'research',\n description: 'Multi-angle research on a question with parallel exploration and a cited synthesis.',\n whenToUse: 'User asks a non-trivial research question that benefits from multiple search angles or source reads.',\n tags: ['research', 'investigation'],\n estimatedAgents: { min: 4, max: 8 },\n phases: [\n { title: 'Frame' },\n { title: 'Sweep' },\n { title: 'Synthesize' },\n ],\n}\n\nconst RESEARCH_TOOLS = ['web_search', 'web_fetch', 'read_file', 'grep', 'find', 'list_dir']\n\nconst question = args && typeof args === 'object' && args.question\n ? String(args.question)\n : 'No explicit question supplied; infer from the most recent user turn.'\n\nconst depth = args && typeof args === 'object' && args.depth\n ? String(args.depth)\n : 'standard'\nconst maxAngles = depth === 'quick' ? 2 : depth === 'deep' ? 6 : 4\n\nphase('Frame')\nconst frame = await agent(\n 'Frame this research question. Return exactly ' + maxAngles + ' distinct angles worth investigating, ' +\n 'each with the single most decisive sub-question. Be concrete.\\n\\nQUESTION:\\n' +\n question,\n {\n label: 'framing',\n schema: {\n type: 'object',\n properties: {\n angles: {\n type: 'array',\n items: {\n type: 'object',\n properties: {\n title: { type: 'string' },\n key_question: { type: 'string' },\n },\n required: ['title', 'key_question'],\n },\n },\n },\n required: ['angles'],\n },\n },\n)\n\nif (!frame || !frame.angles?.length) {\n return { ok: false, reason: 'framing failed', question, depth }\n}\n\nconst angles = frame.angles.slice(0, maxAngles)\n\nphase('Sweep')\nconst angleReports = await parallel(\n angles.map((a) => () =>\n agent(\n 'Investigate this angle. Use search and source-read tools liberally. Distinguish what you can confirm from what is conjecture.\\n\\n' +\n 'ANGLE: ' + a.title + '\\n' +\n 'KEY QUESTION: ' + a.key_question + '\\n\\n' +\n 'Return: 3\u20136 grounded findings (each with a 1-line claim and a source URL or file path), plus the strongest counter-evidence.',\n {\n label: a.title,\n toolset: RESEARCH_TOOLS,\n maxIterations: depth === 'deep' ? 40 : 30,\n schema: {\n type: 'object',\n properties: {\n findings: {\n type: 'array',\n items: {\n type: 'object',\n properties: {\n claim: { type: 'string' },\n source: { type: 'string' },\n confidence: { type: 'string', enum: ['low', 'med', 'high'] },\n },\n required: ['claim', 'source', 'confidence'],\n },\n },\n counterEvidence: { type: 'string' },\n },\n required: ['findings'],\n },\n },\n ),\n ),\n)\n\nphase('Synthesize')\nconst live = angleReports.filter(Boolean)\nconst synthesis = await agent(\n 'Synthesize a cited research report from these angle-level findings. Drop unsupported or duplicate claims. Use the highest-confidence source per claim. ' +\n 'Explicitly list contradictions where angles disagree. Return: an executive summary (max 5 sentences), top findings with inline source URLs, open questions, and contradictions.\\n\\n' +\n 'QUESTION:\\n' + question + '\\n\\n' +\n JSON.stringify({ angles, reports: live }, null, 2),\n {\n label: 'synthesis',\n schema: {\n type: 'object',\n properties: {\n executiveSummary: { type: 'string' },\n topFindings: {\n type: 'array',\n items: {\n type: 'object',\n properties: {\n claim: { type: 'string' },\n source: { type: 'string' },\n },\n required: ['claim', 'source'],\n },\n },\n openQuestions: { type: 'array', items: { type: 'string' } },\n contradictions: {\n type: 'array',\n items: {\n type: 'object',\n properties: {\n topic: { type: 'string' },\n sides: { type: 'array', items: { type: 'string' } },\n },\n required: ['topic', 'sides'],\n },\n },\n },\n required: ['executiveSummary', 'topFindings'],\n },\n },\n)\n\nreturn {\n ok: true,\n question,\n depth,\n angleCount: angles.length,\n ...(synthesis ?? { executiveSummary: 'synthesis failed', topFindings: [], contradictions: [] }),\n}\n";
@@ -6,11 +6,17 @@
6
6
  * exploration / source-reading angles in parallel, then synthesises a cited
7
7
  * report. Each angle is its own subagent so source reading does not pollute the
8
8
  * parent context.
9
+ *
10
+ * Args:
11
+ * - question: research question
12
+ * - depth: 'quick' (2 angles) | 'standard' (4) | 'deep' (6)
9
13
  */
10
14
  const RESEARCH_SCRIPT = `export const meta = {
11
15
  name: 'research',
12
16
  description: 'Multi-angle research on a question with parallel exploration and a cited synthesis.',
13
17
  whenToUse: 'User asks a non-trivial research question that benefits from multiple search angles or source reads.',
18
+ tags: ['research', 'investigation'],
19
+ estimatedAgents: { min: 4, max: 8 },
14
20
  phases: [
15
21
  { title: 'Frame' },
16
22
  { title: 'Sweep' },
@@ -18,13 +24,21 @@ const RESEARCH_SCRIPT = `export const meta = {
18
24
  ],
19
25
  }
20
26
 
27
+ const RESEARCH_TOOLS = ['web_search', 'web_fetch', 'read_file', 'grep', 'find', 'list_dir']
28
+
21
29
  const question = args && typeof args === 'object' && args.question
22
30
  ? String(args.question)
23
31
  : 'No explicit question supplied; infer from the most recent user turn.'
24
32
 
33
+ const depth = args && typeof args === 'object' && args.depth
34
+ ? String(args.depth)
35
+ : 'standard'
36
+ const maxAngles = depth === 'quick' ? 2 : depth === 'deep' ? 6 : 4
37
+
25
38
  phase('Frame')
26
39
  const frame = await agent(
27
- 'Frame this research question. Return 3–5 distinct angles worth investigating, plus the single most decisive sub-question for each. Be concrete.\\n\\nQUESTION:\\n' +
40
+ 'Frame this research question. Return exactly ' + maxAngles + ' distinct angles worth investigating, ' +
41
+ 'each with the single most decisive sub-question. Be concrete.\\n\\nQUESTION:\\n' +
28
42
  question,
29
43
  {
30
44
  label: 'framing',
@@ -49,12 +63,14 @@ const frame = await agent(
49
63
  )
50
64
 
51
65
  if (!frame || !frame.angles?.length) {
52
- return { ok: false, reason: 'framing failed', question }
66
+ return { ok: false, reason: 'framing failed', question, depth }
53
67
  }
54
68
 
69
+ const angles = frame.angles.slice(0, maxAngles)
70
+
55
71
  phase('Sweep')
56
72
  const angleReports = await parallel(
57
- frame.angles.map((a) => () =>
73
+ angles.map((a) => () =>
58
74
  agent(
59
75
  'Investigate this angle. Use search and source-read tools liberally. Distinguish what you can confirm from what is conjecture.\\n\\n' +
60
76
  'ANGLE: ' + a.title + '\\n' +
@@ -62,6 +78,8 @@ const angleReports = await parallel(
62
78
  'Return: 3–6 grounded findings (each with a 1-line claim and a source URL or file path), plus the strongest counter-evidence.',
63
79
  {
64
80
  label: a.title,
81
+ toolset: RESEARCH_TOOLS,
82
+ maxIterations: depth === 'deep' ? 40 : 30,
65
83
  schema: {
66
84
  type: 'object',
67
85
  properties: {
@@ -90,9 +108,9 @@ phase('Synthesize')
90
108
  const live = angleReports.filter(Boolean)
91
109
  const synthesis = await agent(
92
110
  'Synthesize a cited research report from these angle-level findings. Drop unsupported or duplicate claims. Use the highest-confidence source per claim. ' +
93
- 'Return: an executive summary (max 5 sentences), a bullet list of top findings with inline source URLs, and one section listing open questions.\\n\\n' +
111
+ 'Explicitly list contradictions where angles disagree. Return: an executive summary (max 5 sentences), top findings with inline source URLs, open questions, and contradictions.\\n\\n' +
94
112
  'QUESTION:\\n' + question + '\\n\\n' +
95
- JSON.stringify({ angles: frame.angles, reports: live }, null, 2),
113
+ JSON.stringify({ angles, reports: live }, null, 2),
96
114
  {
97
115
  label: 'synthesis',
98
116
  schema: {
@@ -111,6 +129,17 @@ const synthesis = await agent(
111
129
  },
112
130
  },
113
131
  openQuestions: { type: 'array', items: { type: 'string' } },
132
+ contradictions: {
133
+ type: 'array',
134
+ items: {
135
+ type: 'object',
136
+ properties: {
137
+ topic: { type: 'string' },
138
+ sides: { type: 'array', items: { type: 'string' } },
139
+ },
140
+ required: ['topic', 'sides'],
141
+ },
142
+ },
114
143
  },
115
144
  required: ['executiveSummary', 'topFindings'],
116
145
  },
@@ -120,7 +149,9 @@ const synthesis = await agent(
120
149
  return {
121
150
  ok: true,
122
151
  question,
123
- ...(synthesis ?? { executiveSummary: 'synthesis failed', topFindings: [] }),
152
+ depth,
153
+ angleCount: angles.length,
154
+ ...(synthesis ?? { executiveSummary: 'synthesis failed', topFindings: [], contradictions: [] }),
124
155
  }
125
156
  `;
126
157
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"research.js","names":[],"sources":["../../../../../src/agent/workflow/builtins/research.ts"],"sourcesContent":["/**\n * Built-in workflow: `research`\n *\n * Multi-modal research sweep on a question (args.question). Fans out search /\n * exploration / source-reading angles in parallel, then synthesises a cited\n * report. Each angle is its own subagent so source reading does not pollute the\n * parent context.\n */\n\nexport const RESEARCH_SCRIPT = `export const meta = {\n name: 'research',\n description: 'Multi-angle research on a question with parallel exploration and a cited synthesis.',\n whenToUse: 'User asks a non-trivial research question that benefits from multiple search angles or source reads.',\n phases: [\n { title: 'Frame' },\n { title: 'Sweep' },\n { title: 'Synthesize' },\n ],\n}\n\nconst question = args && typeof args === 'object' && args.question\n ? String(args.question)\n : 'No explicit question supplied; infer from the most recent user turn.'\n\nphase('Frame')\nconst frame = await agent(\n 'Frame this research question. Return 3–5 distinct angles worth investigating, plus the single most decisive sub-question for each. Be concrete.\\\\n\\\\nQUESTION:\\\\n' +\n question,\n {\n label: 'framing',\n schema: {\n type: 'object',\n properties: {\n angles: {\n type: 'array',\n items: {\n type: 'object',\n properties: {\n title: { type: 'string' },\n key_question: { type: 'string' },\n },\n required: ['title', 'key_question'],\n },\n },\n },\n required: ['angles'],\n },\n },\n)\n\nif (!frame || !frame.angles?.length) {\n return { ok: false, reason: 'framing failed', question }\n}\n\nphase('Sweep')\nconst angleReports = await parallel(\n frame.angles.map((a) => () =>\n agent(\n 'Investigate this angle. Use search and source-read tools liberally. Distinguish what you can confirm from what is conjecture.\\\\n\\\\n' +\n 'ANGLE: ' + a.title + '\\\\n' +\n 'KEY QUESTION: ' + a.key_question + '\\\\n\\\\n' +\n 'Return: 3–6 grounded findings (each with a 1-line claim and a source URL or file path), plus the strongest counter-evidence.',\n {\n label: a.title,\n schema: {\n type: 'object',\n properties: {\n findings: {\n type: 'array',\n items: {\n type: 'object',\n properties: {\n claim: { type: 'string' },\n source: { type: 'string' },\n confidence: { type: 'string', enum: ['low', 'med', 'high'] },\n },\n required: ['claim', 'source', 'confidence'],\n },\n },\n counterEvidence: { type: 'string' },\n },\n required: ['findings'],\n },\n },\n ),\n ),\n)\n\nphase('Synthesize')\nconst live = angleReports.filter(Boolean)\nconst synthesis = await agent(\n 'Synthesize a cited research report from these angle-level findings. Drop unsupported or duplicate claims. Use the highest-confidence source per claim. ' +\n 'Return: an executive summary (max 5 sentences), a bullet list of top findings with inline source URLs, and one section listing open questions.\\\\n\\\\n' +\n 'QUESTION:\\\\n' + question + '\\\\n\\\\n' +\n JSON.stringify({ angles: frame.angles, reports: live }, null, 2),\n {\n label: 'synthesis',\n schema: {\n type: 'object',\n properties: {\n executiveSummary: { type: 'string' },\n topFindings: {\n type: 'array',\n items: {\n type: 'object',\n properties: {\n claim: { type: 'string' },\n source: { type: 'string' },\n },\n required: ['claim', 'source'],\n },\n },\n openQuestions: { type: 'array', items: { type: 'string' } },\n },\n required: ['executiveSummary', 'topFindings'],\n },\n },\n)\n\nreturn {\n ok: true,\n question,\n ...(synthesis ?? { executiveSummary: 'synthesis failed', topFindings: [] }),\n}\n`\n"],"mappings":";;;;;;;;;AASA,MAAa,kBAAkB"}
1
+ {"version":3,"file":"research.js","names":[],"sources":["../../../../../src/agent/workflow/builtins/research.ts"],"sourcesContent":["/**\n * Built-in workflow: `research`\n *\n * Multi-modal research sweep on a question (args.question). Fans out search /\n * exploration / source-reading angles in parallel, then synthesises a cited\n * report. Each angle is its own subagent so source reading does not pollute the\n * parent context.\n *\n * Args:\n * - question: research question\n * - depth: 'quick' (2 angles) | 'standard' (4) | 'deep' (6)\n */\n\nexport const RESEARCH_SCRIPT = `export const meta = {\n name: 'research',\n description: 'Multi-angle research on a question with parallel exploration and a cited synthesis.',\n whenToUse: 'User asks a non-trivial research question that benefits from multiple search angles or source reads.',\n tags: ['research', 'investigation'],\n estimatedAgents: { min: 4, max: 8 },\n phases: [\n { title: 'Frame' },\n { title: 'Sweep' },\n { title: 'Synthesize' },\n ],\n}\n\nconst RESEARCH_TOOLS = ['web_search', 'web_fetch', 'read_file', 'grep', 'find', 'list_dir']\n\nconst question = args && typeof args === 'object' && args.question\n ? String(args.question)\n : 'No explicit question supplied; infer from the most recent user turn.'\n\nconst depth = args && typeof args === 'object' && args.depth\n ? String(args.depth)\n : 'standard'\nconst maxAngles = depth === 'quick' ? 2 : depth === 'deep' ? 6 : 4\n\nphase('Frame')\nconst frame = await agent(\n 'Frame this research question. Return exactly ' + maxAngles + ' distinct angles worth investigating, ' +\n 'each with the single most decisive sub-question. Be concrete.\\\\n\\\\nQUESTION:\\\\n' +\n question,\n {\n label: 'framing',\n schema: {\n type: 'object',\n properties: {\n angles: {\n type: 'array',\n items: {\n type: 'object',\n properties: {\n title: { type: 'string' },\n key_question: { type: 'string' },\n },\n required: ['title', 'key_question'],\n },\n },\n },\n required: ['angles'],\n },\n },\n)\n\nif (!frame || !frame.angles?.length) {\n return { ok: false, reason: 'framing failed', question, depth }\n}\n\nconst angles = frame.angles.slice(0, maxAngles)\n\nphase('Sweep')\nconst angleReports = await parallel(\n angles.map((a) => () =>\n agent(\n 'Investigate this angle. Use search and source-read tools liberally. Distinguish what you can confirm from what is conjecture.\\\\n\\\\n' +\n 'ANGLE: ' + a.title + '\\\\n' +\n 'KEY QUESTION: ' + a.key_question + '\\\\n\\\\n' +\n 'Return: 3–6 grounded findings (each with a 1-line claim and a source URL or file path), plus the strongest counter-evidence.',\n {\n label: a.title,\n toolset: RESEARCH_TOOLS,\n maxIterations: depth === 'deep' ? 40 : 30,\n schema: {\n type: 'object',\n properties: {\n findings: {\n type: 'array',\n items: {\n type: 'object',\n properties: {\n claim: { type: 'string' },\n source: { type: 'string' },\n confidence: { type: 'string', enum: ['low', 'med', 'high'] },\n },\n required: ['claim', 'source', 'confidence'],\n },\n },\n counterEvidence: { type: 'string' },\n },\n required: ['findings'],\n },\n },\n ),\n ),\n)\n\nphase('Synthesize')\nconst live = angleReports.filter(Boolean)\nconst synthesis = await agent(\n 'Synthesize a cited research report from these angle-level findings. Drop unsupported or duplicate claims. Use the highest-confidence source per claim. ' +\n 'Explicitly list contradictions where angles disagree. Return: an executive summary (max 5 sentences), top findings with inline source URLs, open questions, and contradictions.\\\\n\\\\n' +\n 'QUESTION:\\\\n' + question + '\\\\n\\\\n' +\n JSON.stringify({ angles, reports: live }, null, 2),\n {\n label: 'synthesis',\n schema: {\n type: 'object',\n properties: {\n executiveSummary: { type: 'string' },\n topFindings: {\n type: 'array',\n items: {\n type: 'object',\n properties: {\n claim: { type: 'string' },\n source: { type: 'string' },\n },\n required: ['claim', 'source'],\n },\n },\n openQuestions: { type: 'array', items: { type: 'string' } },\n contradictions: {\n type: 'array',\n items: {\n type: 'object',\n properties: {\n topic: { type: 'string' },\n sides: { type: 'array', items: { type: 'string' } },\n },\n required: ['topic', 'sides'],\n },\n },\n },\n required: ['executiveSummary', 'topFindings'],\n },\n },\n)\n\nreturn {\n ok: true,\n question,\n depth,\n angleCount: angles.length,\n ...(synthesis ?? { executiveSummary: 'synthesis failed', topFindings: [], contradictions: [] }),\n}\n`\n"],"mappings":";;;;;;;;;;;;;AAaA,MAAa,kBAAkB"}
@@ -24,6 +24,11 @@ export interface CatalogEntry {
24
24
  path: string | null;
25
25
  description: string;
26
26
  whenToUse?: string;
27
+ tags?: string[];
28
+ estimatedAgents?: {
29
+ min: number;
30
+ max: number;
31
+ };
27
32
  }
28
33
  export interface LoadedWorkflow {
29
34
  name: string;
@@ -34,7 +34,9 @@ function createWorkflowCatalog(opts = {}) {
34
34
  source: "builtin",
35
35
  path: null,
36
36
  description: meta?.description ?? "(unparseable)",
37
- whenToUse: meta?.whenToUse
37
+ whenToUse: meta?.whenToUse,
38
+ tags: meta?.tags,
39
+ estimatedAgents: meta?.estimatedAgents
38
40
  });
39
41
  }
40
42
  for (const file of safeListUserFiles(userDir)) {
@@ -47,7 +49,9 @@ function createWorkflowCatalog(opts = {}) {
47
49
  source: "user",
48
50
  path: full,
49
51
  description: meta?.description ?? "(unparseable)",
50
- whenToUse: meta?.whenToUse
52
+ whenToUse: meta?.whenToUse,
53
+ tags: meta?.tags,
54
+ estimatedAgents: meta?.estimatedAgents
51
55
  });
52
56
  }
53
57
  return [...entries.values()].sort((a, b) => a.name.localeCompare(b.name));
@@ -1 +1 @@
1
- {"version":3,"file":"catalog.js","names":[],"sources":["../../../../src/agent/workflow/catalog.ts"],"sourcesContent":["/**\n * Catalog for named workflows.\n *\n * Resolution order (built-ins are starting points, user workflows win):\n * 1. `~/.xopc/workflows/<name>.js` (or `<name>.workflow.js`)\n * 2. {@link BUILTIN_WORKFLOWS}\n *\n * The user dir is discovered via {@link resolveStateDir}, so `XOPC_STATE_DIR`\n * overrides apply automatically (matches how skills / extensions are wired).\n *\n * Listing is filesystem-cheap (single `readdir`) and runs synchronously — the\n * `/workflows` slash command is interactive and should return immediately.\n *\n * Validation: on load we re-parse the script to make sure `meta.name` matches\n * the filename. This prevents copy-pasted scripts from being silently\n * mis-addressed when invoked by name.\n */\n\nimport {\n existsSync,\n mkdirSync,\n readdirSync,\n readFileSync,\n statSync,\n unlinkSync,\n writeFileSync,\n} from 'node:fs';\nimport { join } from 'node:path';\n\nimport { resolveStateDir } from '../../config/paths-state.js';\n\nimport { BUILTIN_WORKFLOWS } from './builtins/index.js';\nimport { parseWorkflowScript } from './parser.js';\nimport type { WorkflowMeta } from './types.js';\n\nexport type WorkflowSource = 'user' | 'builtin';\n\nexport interface CatalogEntry {\n name: string;\n source: WorkflowSource;\n /** Absolute path for user entries; null for built-ins (in-memory). */\n path: string | null;\n description: string;\n whenToUse?: string;\n}\n\nexport interface LoadedWorkflow {\n name: string;\n source: WorkflowSource;\n script: string;\n meta: WorkflowMeta;\n path: string | null;\n}\n\nexport interface WorkflowCatalog {\n list(): CatalogEntry[];\n /** Load a named workflow. Throws if missing or meta.name disagrees with filename. */\n load(name: string): LoadedWorkflow;\n /** Save a script as a user workflow. Throws if the script fails to parse. */\n save(name: string, script: string): { path: string };\n /** Remove a user workflow. No-op if absent. Built-ins are never removed. */\n remove(name: string): boolean;\n /** Absolute path to the user workflows directory (created lazily on save). */\n userDir: string;\n}\n\nconst NAME_RE = /^[a-z][a-z0-9_-]*$/;\n\nexport function createWorkflowCatalog(opts: { userDir?: string } = {}): WorkflowCatalog {\n const userDir = opts.userDir ?? defaultUserDir();\n\n const list = (): CatalogEntry[] => {\n const entries = new Map<string, CatalogEntry>();\n for (const b of BUILTIN_WORKFLOWS) {\n const meta = safeMeta(b.script);\n entries.set(b.name, {\n name: b.name,\n source: 'builtin',\n path: null,\n description: meta?.description ?? '(unparseable)',\n whenToUse: meta?.whenToUse,\n });\n }\n for (const file of safeListUserFiles(userDir)) {\n const name = stripExt(file);\n if (!isValidName(name)) continue;\n const full = join(userDir, file);\n const meta = safeMeta(readScript(full));\n // User wins on collision.\n entries.set(name, {\n name,\n source: 'user',\n path: full,\n description: meta?.description ?? '(unparseable)',\n whenToUse: meta?.whenToUse,\n });\n }\n return [...entries.values()].sort((a, b) => a.name.localeCompare(b.name));\n };\n\n const load = (name: string): LoadedWorkflow => {\n requireValidName(name);\n const userPath = findUserPath(userDir, name);\n if (userPath) {\n const script = readScript(userPath);\n const { meta } = parseWorkflowScript(script);\n ensureMetaNameMatches(meta, name, userPath);\n return { name, source: 'user', script, meta, path: userPath };\n }\n const builtin = BUILTIN_WORKFLOWS.find((b) => b.name === name);\n if (builtin) {\n const { meta } = parseWorkflowScript(builtin.script);\n ensureMetaNameMatches(meta, name, '<builtin>');\n return { name, source: 'builtin', script: builtin.script, meta, path: null };\n }\n throw new Error(\n `workflow not found: ${name}. Drop a script at ${join(userDir, `${name}.js`)} or pick one of: ${list()\n .map((e) => e.name)\n .join(', ')}`,\n );\n };\n\n const save = (name: string, script: string): { path: string } => {\n requireValidName(name);\n const { meta } = parseWorkflowScript(script);\n if (meta.name !== name) {\n throw new Error(\n `meta.name \"${meta.name}\" does not match save name \"${name}\". Adjust one to match the other.`,\n );\n }\n if (!existsSync(userDir)) {\n mkdirSync(userDir, { recursive: true });\n }\n const path = join(userDir, `${name}.js`);\n writeFileSync(path, normalizeNewlines(script), 'utf-8');\n return { path };\n };\n\n const remove = (name: string): boolean => {\n requireValidName(name);\n const userPath = findUserPath(userDir, name);\n if (!userPath) return false;\n unlinkSync(userPath);\n return true;\n };\n\n return { list, load, save, remove, userDir };\n}\n\n// ---------------------------------------------------------------------------\n\nexport function defaultUserDir(): string {\n return join(resolveStateDir(), 'workflows');\n}\n\nfunction safeListUserFiles(dir: string): string[] {\n try {\n if (!existsSync(dir)) return [];\n const st = statSync(dir);\n if (!st.isDirectory()) return [];\n return readdirSync(dir).filter((f) => /\\.(js|workflow\\.js)$/i.test(f));\n } catch {\n return [];\n }\n}\n\nfunction findUserPath(dir: string, name: string): string | null {\n for (const candidate of [`${name}.js`, `${name}.workflow.js`]) {\n const full = join(dir, candidate);\n if (existsSync(full)) return full;\n }\n return null;\n}\n\nfunction readScript(path: string): string {\n return readFileSync(path, 'utf-8');\n}\n\nfunction safeMeta(script: string): WorkflowMeta | null {\n try {\n return parseWorkflowScript(script).meta;\n } catch {\n return null;\n }\n}\n\nfunction stripExt(filename: string): string {\n return filename.replace(/\\.workflow\\.js$/i, '').replace(/\\.js$/i, '');\n}\n\nfunction isValidName(name: string): boolean {\n return NAME_RE.test(name);\n}\n\nfunction requireValidName(name: string): void {\n if (!isValidName(name)) {\n throw new Error(`invalid workflow name \"${name}\". Use lowercase snake_case, e.g. \"audit_repo\".`);\n }\n}\n\nfunction ensureMetaNameMatches(meta: WorkflowMeta, name: string, locator: string): void {\n if (meta.name !== name) {\n throw new Error(\n `workflow ${locator}: meta.name \"${meta.name}\" disagrees with addressable name \"${name}\". ` +\n 'Rename the file or the meta.name to match.',\n );\n }\n}\n\nfunction normalizeNewlines(s: string): string {\n return s.endsWith('\\n') ? s : `${s}\\n`;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"catalog.js","names":[],"sources":["../../../../src/agent/workflow/catalog.ts"],"sourcesContent":["/**\n * Catalog for named workflows.\n *\n * Resolution order (built-ins are starting points, user workflows win):\n * 1. `~/.xopc/workflows/<name>.js` (or `<name>.workflow.js`)\n * 2. {@link BUILTIN_WORKFLOWS}\n *\n * The user dir is discovered via {@link resolveStateDir}, so `XOPC_STATE_DIR`\n * overrides apply automatically (matches how skills / extensions are wired).\n *\n * Listing is filesystem-cheap (single `readdir`) and runs synchronously — the\n * `/workflows` slash command is interactive and should return immediately.\n *\n * Validation: on load we re-parse the script to make sure `meta.name` matches\n * the filename. This prevents copy-pasted scripts from being silently\n * mis-addressed when invoked by name.\n */\n\nimport {\n existsSync,\n mkdirSync,\n readdirSync,\n readFileSync,\n statSync,\n unlinkSync,\n writeFileSync,\n} from 'node:fs';\nimport { join } from 'node:path';\n\nimport { resolveStateDir } from '../../config/paths-state.js';\n\nimport { BUILTIN_WORKFLOWS } from './builtins/index.js';\nimport { parseWorkflowScript } from './parser.js';\nimport type { WorkflowMeta } from './types.js';\n\nexport type WorkflowSource = 'user' | 'builtin';\n\nexport interface CatalogEntry {\n name: string;\n source: WorkflowSource;\n /** Absolute path for user entries; null for built-ins (in-memory). */\n path: string | null;\n description: string;\n whenToUse?: string;\n tags?: string[];\n estimatedAgents?: { min: number; max: number };\n}\n\nexport interface LoadedWorkflow {\n name: string;\n source: WorkflowSource;\n script: string;\n meta: WorkflowMeta;\n path: string | null;\n}\n\nexport interface WorkflowCatalog {\n list(): CatalogEntry[];\n /** Load a named workflow. Throws if missing or meta.name disagrees with filename. */\n load(name: string): LoadedWorkflow;\n /** Save a script as a user workflow. Throws if the script fails to parse. */\n save(name: string, script: string): { path: string };\n /** Remove a user workflow. No-op if absent. Built-ins are never removed. */\n remove(name: string): boolean;\n /** Absolute path to the user workflows directory (created lazily on save). */\n userDir: string;\n}\n\nconst NAME_RE = /^[a-z][a-z0-9_-]*$/;\n\nexport function createWorkflowCatalog(opts: { userDir?: string } = {}): WorkflowCatalog {\n const userDir = opts.userDir ?? defaultUserDir();\n\n const list = (): CatalogEntry[] => {\n const entries = new Map<string, CatalogEntry>();\n for (const b of BUILTIN_WORKFLOWS) {\n const meta = safeMeta(b.script);\n entries.set(b.name, {\n name: b.name,\n source: 'builtin',\n path: null,\n description: meta?.description ?? '(unparseable)',\n whenToUse: meta?.whenToUse,\n tags: meta?.tags,\n estimatedAgents: meta?.estimatedAgents,\n });\n }\n for (const file of safeListUserFiles(userDir)) {\n const name = stripExt(file);\n if (!isValidName(name)) continue;\n const full = join(userDir, file);\n const meta = safeMeta(readScript(full));\n // User wins on collision.\n entries.set(name, {\n name,\n source: 'user',\n path: full,\n description: meta?.description ?? '(unparseable)',\n whenToUse: meta?.whenToUse,\n tags: meta?.tags,\n estimatedAgents: meta?.estimatedAgents,\n });\n }\n return [...entries.values()].sort((a, b) => a.name.localeCompare(b.name));\n };\n\n const load = (name: string): LoadedWorkflow => {\n requireValidName(name);\n const userPath = findUserPath(userDir, name);\n if (userPath) {\n const script = readScript(userPath);\n const { meta } = parseWorkflowScript(script);\n ensureMetaNameMatches(meta, name, userPath);\n return { name, source: 'user', script, meta, path: userPath };\n }\n const builtin = BUILTIN_WORKFLOWS.find((b) => b.name === name);\n if (builtin) {\n const { meta } = parseWorkflowScript(builtin.script);\n ensureMetaNameMatches(meta, name, '<builtin>');\n return { name, source: 'builtin', script: builtin.script, meta, path: null };\n }\n throw new Error(\n `workflow not found: ${name}. Drop a script at ${join(userDir, `${name}.js`)} or pick one of: ${list()\n .map((e) => e.name)\n .join(', ')}`,\n );\n };\n\n const save = (name: string, script: string): { path: string } => {\n requireValidName(name);\n const { meta } = parseWorkflowScript(script);\n if (meta.name !== name) {\n throw new Error(\n `meta.name \"${meta.name}\" does not match save name \"${name}\". Adjust one to match the other.`,\n );\n }\n if (!existsSync(userDir)) {\n mkdirSync(userDir, { recursive: true });\n }\n const path = join(userDir, `${name}.js`);\n writeFileSync(path, normalizeNewlines(script), 'utf-8');\n return { path };\n };\n\n const remove = (name: string): boolean => {\n requireValidName(name);\n const userPath = findUserPath(userDir, name);\n if (!userPath) return false;\n unlinkSync(userPath);\n return true;\n };\n\n return { list, load, save, remove, userDir };\n}\n\n// ---------------------------------------------------------------------------\n\nexport function defaultUserDir(): string {\n return join(resolveStateDir(), 'workflows');\n}\n\nfunction safeListUserFiles(dir: string): string[] {\n try {\n if (!existsSync(dir)) return [];\n const st = statSync(dir);\n if (!st.isDirectory()) return [];\n return readdirSync(dir).filter((f) => /\\.(js|workflow\\.js)$/i.test(f));\n } catch {\n return [];\n }\n}\n\nfunction findUserPath(dir: string, name: string): string | null {\n for (const candidate of [`${name}.js`, `${name}.workflow.js`]) {\n const full = join(dir, candidate);\n if (existsSync(full)) return full;\n }\n return null;\n}\n\nfunction readScript(path: string): string {\n return readFileSync(path, 'utf-8');\n}\n\nfunction safeMeta(script: string): WorkflowMeta | null {\n try {\n return parseWorkflowScript(script).meta;\n } catch {\n return null;\n }\n}\n\nfunction stripExt(filename: string): string {\n return filename.replace(/\\.workflow\\.js$/i, '').replace(/\\.js$/i, '');\n}\n\nfunction isValidName(name: string): boolean {\n return NAME_RE.test(name);\n}\n\nfunction requireValidName(name: string): void {\n if (!isValidName(name)) {\n throw new Error(`invalid workflow name \"${name}\". Use lowercase snake_case, e.g. \"audit_repo\".`);\n }\n}\n\nfunction ensureMetaNameMatches(meta: WorkflowMeta, name: string, locator: string): void {\n if (meta.name !== name) {\n throw new Error(\n `workflow ${locator}: meta.name \"${meta.name}\" disagrees with addressable name \"${name}\". ` +\n 'Rename the file or the meta.name to match.',\n );\n }\n}\n\nfunction normalizeNewlines(s: string): string {\n return s.endsWith('\\n') ? s : `${s}\\n`;\n}\n"],"mappings":"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"}
@@ -42,9 +42,9 @@ export interface WorkflowProgressPostInput {
42
42
  * prefixes mid-run `append` messages with "▾ progress" so the user can tell
43
43
  * them apart from the final summary).
44
44
  *
45
- * Optional for backwards compatibility with hand-rolled callers and unit
46
- * tests; the broker always provides it. Capabilities that consume the field
47
- * should default to `'edit'` when missing.
45
+ * Optional only to keep hand-rolled callers and unit-test stubs lean — the
46
+ * broker always provides it. Capabilities that consume the field should
47
+ * default to `'edit'` when missing.
48
48
  */
49
49
  mode?: WorkflowProgressMode;
50
50
  }
@@ -8,4 +8,4 @@ export { emptySnapshotFor, runWorkflow, type RunWorkflowDeps, } from './runtime.
8
8
  export { createWorkflowSnapshot, previewValue, recomputeCounts, renderWorkflowText, type RenderOptions, } from './snapshot.js';
9
9
  export { createStructuredOutputTool, STRUCTURED_OUTPUT_TOOL_NAME, type CreateStructuredOutputToolOptions, type StructuredOutputCapture, } from './structured-output-tool.js';
10
10
  export { DelegateSubagentRunner, type DelegateSubagentRunnerDeps, } from './subagent-runner.js';
11
- export type { AgentScriptOptions, JsonSchema, SubagentRunner, SubagentRunOptions, WorkflowAgentSnapshot, WorkflowAgentStatus, WorkflowMeta, WorkflowMetaPhase, WorkflowRunOptions, WorkflowRunResult, WorkflowSnapshot, } from './types.js';
11
+ export type { AgentScriptOptions, JsonSchema, SubagentRunner, SubagentRunOptions, WorkflowAgentSnapshot, WorkflowAgentStatus, WorkflowMeta, WorkflowMetaEstimatedAgents, WorkflowMetaPhase, WorkflowRunOptions, WorkflowRunResult, WorkflowSnapshot, } from './types.js';
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Static lint for workflow scripts.
3
+ *
4
+ * Catches the single most common AI-authoring bug: calling `agent()`,
5
+ * `parallel()`, or `pipeline()` without `await` and then using the returned
6
+ * Promise as if it were a value. The runtime would throw a cryptic TypeError
7
+ * like `results.map is not a function`; this lint converts it into an
8
+ * actionable error before the script ever runs.
9
+ *
10
+ * Rules (per matching CallExpression):
11
+ * Allowed parents
12
+ * - AwaitExpression argument `await parallel(...)`
13
+ * - ReturnStatement argument `return agent(...)` (auto-unwraps)
14
+ * - ExpressionStatement fire-and-forget; pending agents drain
15
+ * - ArrowFunctionExpression expression body `() => agent(...)` (thunk for parallel/pipeline)
16
+ * Anything else (VariableDeclarator init, MemberExpression object, template
17
+ * interpolation, binary/logical operand, call argument, property/array
18
+ * element, ...) is rejected with a teaching message.
19
+ *
20
+ * Out of scope (handled by the runtime):
21
+ * - `parallel([agent(...), agent(...)])` (promises instead of thunks) — caught
22
+ * by the runtime's TypeError ("expects an array of functions, not promises").
23
+ * - Promise-as-value bugs that survive lint (dynamic indirection, late await).
24
+ */
25
+ import type { Node } from 'acorn';
26
+ type AnyNode = Node & {
27
+ [key: string]: any;
28
+ start: number;
29
+ end: number;
30
+ loc?: {
31
+ start: {
32
+ line: number;
33
+ column: number;
34
+ };
35
+ };
36
+ };
37
+ export declare function lintAwaits(ast: AnyNode): void;
38
+ export {};
@@ -0,0 +1,74 @@
1
+ //#region src/agent/workflow/lint.ts
2
+ const LINT_TARGETS = new Set([
3
+ "agent",
4
+ "parallel",
5
+ "pipeline"
6
+ ]);
7
+ function lintAwaits(ast) {
8
+ walk(ast, null, (node, parent) => {
9
+ if (!isLintTarget(node)) return;
10
+ if (isAcceptableParent(parent, node)) return;
11
+ const calleeName = node.callee.name;
12
+ const line = node.loc?.start.line ?? "?";
13
+ throw new Error(formatError(calleeName, line, parent));
14
+ });
15
+ }
16
+ function isLintTarget(node) {
17
+ if (node.type !== "CallExpression") return false;
18
+ const callee = node.callee;
19
+ if (!callee || callee.type !== "Identifier") return false;
20
+ return LINT_TARGETS.has(callee.name);
21
+ }
22
+ function isAcceptableParent(parent, call) {
23
+ if (!parent) return false;
24
+ switch (parent.type) {
25
+ case "AwaitExpression": return parent.argument === call;
26
+ case "ReturnStatement": return parent.argument === call;
27
+ case "ExpressionStatement": return parent.expression === call;
28
+ case "ArrowFunctionExpression": return parent.expression === true && parent.body === call;
29
+ default: return false;
30
+ }
31
+ }
32
+ function formatError(name, line, parent) {
33
+ const ctx = parentContextHint(parent);
34
+ return [
35
+ `workflow lint error at line ${line}: \`${name}(...)\` returns a Promise but is used without 'await'${ctx ? ` (${ctx})` : ""}.`,
36
+ ` ❌ const x = ${name}(...); x.map(...)`,
37
+ ` ✅ const x = await ${name}(...); x.map(...)`,
38
+ `Workflow scripts run in an async IIFE — 'await' parallel()/pipeline()/agent() before using their results, or 'return' them directly.`
39
+ ].join("\n");
40
+ }
41
+ function parentContextHint(parent) {
42
+ if (!parent) return "";
43
+ switch (parent.type) {
44
+ case "VariableDeclarator": return "assigned to variable";
45
+ case "AssignmentExpression": return "assigned via =";
46
+ case "MemberExpression": return "method/property access on Promise";
47
+ case "TemplateLiteral": return "interpolated in template string";
48
+ case "BinaryExpression":
49
+ case "LogicalExpression": return "used in expression";
50
+ case "ConditionalExpression": return "used as ternary operand";
51
+ case "CallExpression": return "passed as argument";
52
+ case "Property": return "used as object property value";
53
+ case "ArrayExpression": return "placed in array (use a thunk: () => call)";
54
+ case "SpreadElement": return "spread";
55
+ case "IfStatement":
56
+ case "WhileStatement":
57
+ case "DoWhileStatement":
58
+ case "ForStatement": return "used as condition";
59
+ default: return parent.type;
60
+ }
61
+ }
62
+ function walk(node, parent, visit) {
63
+ visit(node, parent);
64
+ for (const value of Object.values(node)) if (Array.isArray(value)) {
65
+ for (const v of value) if (isAstNode(v)) walk(v, node, visit);
66
+ } else if (isAstNode(value)) walk(value, node, visit);
67
+ }
68
+ function isAstNode(value) {
69
+ return !!value && typeof value === "object" && typeof value.type === "string";
70
+ }
71
+ //#endregion
72
+ export { lintAwaits };
73
+
74
+ //# sourceMappingURL=lint.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lint.js","names":[],"sources":["../../../../src/agent/workflow/lint.ts"],"sourcesContent":["/**\n * Static lint for workflow scripts.\n *\n * Catches the single most common AI-authoring bug: calling `agent()`,\n * `parallel()`, or `pipeline()` without `await` and then using the returned\n * Promise as if it were a value. The runtime would throw a cryptic TypeError\n * like `results.map is not a function`; this lint converts it into an\n * actionable error before the script ever runs.\n *\n * Rules (per matching CallExpression):\n * Allowed parents\n * - AwaitExpression argument `await parallel(...)`\n * - ReturnStatement argument `return agent(...)` (auto-unwraps)\n * - ExpressionStatement fire-and-forget; pending agents drain\n * - ArrowFunctionExpression expression body `() => agent(...)` (thunk for parallel/pipeline)\n * Anything else (VariableDeclarator init, MemberExpression object, template\n * interpolation, binary/logical operand, call argument, property/array\n * element, ...) is rejected with a teaching message.\n *\n * Out of scope (handled by the runtime):\n * - `parallel([agent(...), agent(...)])` (promises instead of thunks) — caught\n * by the runtime's TypeError (\"expects an array of functions, not promises\").\n * - Promise-as-value bugs that survive lint (dynamic indirection, late await).\n */\n\nimport type { Node } from 'acorn';\n\ntype AnyNode = Node & {\n [key: string]: any;\n start: number;\n end: number;\n loc?: { start: { line: number; column: number } };\n};\n\nconst LINT_TARGETS = new Set(['agent', 'parallel', 'pipeline']);\n\nexport function lintAwaits(ast: AnyNode): void {\n walk(ast, null, (node, parent) => {\n if (!isLintTarget(node)) return;\n if (isAcceptableParent(parent, node)) return;\n const calleeName = (node.callee as AnyNode).name as string;\n const line = node.loc?.start.line ?? '?';\n throw new Error(formatError(calleeName, line, parent));\n });\n}\n\nfunction isLintTarget(node: AnyNode): boolean {\n if (node.type !== 'CallExpression') return false;\n const callee = node.callee as AnyNode | undefined;\n if (!callee || callee.type !== 'Identifier') return false;\n return LINT_TARGETS.has(callee.name);\n}\n\nfunction isAcceptableParent(parent: AnyNode | null, call: AnyNode): boolean {\n if (!parent) return false;\n switch (parent.type) {\n case 'AwaitExpression':\n return parent.argument === call;\n case 'ReturnStatement':\n return parent.argument === call;\n case 'ExpressionStatement':\n return parent.expression === call;\n case 'ArrowFunctionExpression':\n // `() => agent(...)` — thunk-style body that the runtime invokes/awaits.\n return parent.expression === true && parent.body === call;\n default:\n return false;\n }\n}\n\nfunction formatError(name: string, line: number | string, parent: AnyNode | null): string {\n const ctx = parentContextHint(parent);\n return [\n `workflow lint error at line ${line}: \\`${name}(...)\\` returns a Promise but is used without 'await'${ctx ? ` (${ctx})` : ''}.`,\n ` ❌ const x = ${name}(...); x.map(...)`,\n ` ✅ const x = await ${name}(...); x.map(...)`,\n `Workflow scripts run in an async IIFE — 'await' parallel()/pipeline()/agent() before using their results, or 'return' them directly.`,\n ].join('\\n');\n}\n\nfunction parentContextHint(parent: AnyNode | null): string {\n if (!parent) return '';\n switch (parent.type) {\n case 'VariableDeclarator':\n return 'assigned to variable';\n case 'AssignmentExpression':\n return 'assigned via =';\n case 'MemberExpression':\n return 'method/property access on Promise';\n case 'TemplateLiteral':\n return 'interpolated in template string';\n case 'BinaryExpression':\n case 'LogicalExpression':\n return 'used in expression';\n case 'ConditionalExpression':\n return 'used as ternary operand';\n case 'CallExpression':\n return 'passed as argument';\n case 'Property':\n return 'used as object property value';\n case 'ArrayExpression':\n return 'placed in array (use a thunk: () => ' + 'call)';\n case 'SpreadElement':\n return 'spread';\n case 'IfStatement':\n case 'WhileStatement':\n case 'DoWhileStatement':\n case 'ForStatement':\n return 'used as condition';\n default:\n return parent.type;\n }\n}\n\nfunction walk(\n node: AnyNode,\n parent: AnyNode | null,\n visit: (n: AnyNode, p: AnyNode | null) => void,\n): void {\n visit(node, parent);\n for (const value of Object.values(node)) {\n if (Array.isArray(value)) {\n for (const v of value) {\n if (isAstNode(v)) walk(v, node, visit);\n }\n } else if (isAstNode(value)) {\n walk(value, node, visit);\n }\n }\n}\n\nfunction isAstNode(value: unknown): value is AnyNode {\n return !!value && typeof value === 'object' && typeof (value as AnyNode).type === 'string';\n}\n"],"mappings":";AAkCA,MAAM,eAAe,IAAI,IAAI;CAAC;CAAS;CAAY;CAAW,CAAC;AAE/D,SAAgB,WAAW,KAAoB;AAC7C,MAAK,KAAK,OAAO,MAAM,WAAW;AAChC,MAAI,CAAC,aAAa,KAAK,CAAE;AACzB,MAAI,mBAAmB,QAAQ,KAAK,CAAE;EACtC,MAAM,aAAc,KAAK,OAAmB;EAC5C,MAAM,OAAO,KAAK,KAAK,MAAM,QAAQ;AACrC,QAAM,IAAI,MAAM,YAAY,YAAY,MAAM,OAAO,CAAC;GACtD;;AAGJ,SAAS,aAAa,MAAwB;AAC5C,KAAI,KAAK,SAAS,iBAAkB,QAAO;CAC3C,MAAM,SAAS,KAAK;AACpB,KAAI,CAAC,UAAU,OAAO,SAAS,aAAc,QAAO;AACpD,QAAO,aAAa,IAAI,OAAO,KAAK;;AAGtC,SAAS,mBAAmB,QAAwB,MAAwB;AAC1E,KAAI,CAAC,OAAQ,QAAO;AACpB,SAAQ,OAAO,MAAf;EACE,KAAK,kBACH,QAAO,OAAO,aAAa;EAC7B,KAAK,kBACH,QAAO,OAAO,aAAa;EAC7B,KAAK,sBACH,QAAO,OAAO,eAAe;EAC/B,KAAK,0BAEH,QAAO,OAAO,eAAe,QAAQ,OAAO,SAAS;EACvD,QACE,QAAO;;;AAIb,SAAS,YAAY,MAAc,MAAuB,QAAgC;CACxF,MAAM,MAAM,kBAAkB,OAAO;AACrC,QAAO;EACL,+BAA+B,KAAK,MAAM,KAAK,uDAAuD,MAAM,KAAK,IAAI,KAAK,GAAG;EAC7H,iBAAiB,KAAK;EACtB,uBAAuB,KAAK;EAC5B;EACD,CAAC,KAAK,KAAK;;AAGd,SAAS,kBAAkB,QAAgC;AACzD,KAAI,CAAC,OAAQ,QAAO;AACpB,SAAQ,OAAO,MAAf;EACE,KAAK,qBACH,QAAO;EACT,KAAK,uBACH,QAAO;EACT,KAAK,mBACH,QAAO;EACT,KAAK,kBACH,QAAO;EACT,KAAK;EACL,KAAK,oBACH,QAAO;EACT,KAAK,wBACH,QAAO;EACT,KAAK,iBACH,QAAO;EACT,KAAK,WACH,QAAO;EACT,KAAK,kBACH,QAAO;EACT,KAAK,gBACH,QAAO;EACT,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK,eACH,QAAO;EACT,QACE,QAAO,OAAO;;;AAIpB,SAAS,KACP,MACA,QACA,OACM;AACN,OAAM,MAAM,OAAO;AACnB,MAAK,MAAM,SAAS,OAAO,OAAO,KAAK,CACrC,KAAI,MAAM,QAAQ,MAAM;OACjB,MAAM,KAAK,MACd,KAAI,UAAU,EAAE,CAAE,MAAK,GAAG,MAAM,MAAM;YAE/B,UAAU,MAAM,CACzB,MAAK,OAAO,MAAM,MAAM;;AAK9B,SAAS,UAAU,OAAkC;AACnD,QAAO,CAAC,CAAC,SAAS,OAAO,UAAU,YAAY,OAAQ,MAAkB,SAAS"}
@@ -1,3 +1,4 @@
1
+ import { lintAwaits } from "./lint.js";
1
2
  import { parse } from "acorn";
2
3
  //#region src/agent/workflow/parser.ts
3
4
  /**
@@ -23,7 +24,8 @@ function parseWorkflowScript(script) {
23
24
  sourceType: "module",
24
25
  allowAwaitOutsideFunction: true,
25
26
  allowReturnOutsideFunction: true,
26
- ranges: false
27
+ ranges: false,
28
+ locations: true
27
29
  });
28
30
  } catch (e) {
29
31
  const msg = e instanceof Error ? e.message : String(e);
@@ -31,6 +33,7 @@ function parseWorkflowScript(script) {
31
33
  }
32
34
  assertDeterministicAst(ast);
33
35
  assertNoDangerousImports(ast);
36
+ lintAwaits(ast);
34
37
  const first = ast.body?.[0];
35
38
  if (first?.type !== "ExportNamedDeclaration") throw new Error("`export const meta = { name, description }` must be the first statement in the script");
36
39
  const declaration = first.declaration;
@@ -130,6 +133,15 @@ function validateMeta(meta) {
130
133
  if (!Array.isArray(value.phases)) throw new Error("meta.phases must be an array");
131
134
  for (const phase of value.phases) if (!phase || typeof phase !== "object" || typeof phase.title !== "string") throw new Error("each meta phase must have a title string");
132
135
  }
136
+ if (value.tags !== void 0) {
137
+ if (!Array.isArray(value.tags) || value.tags.some((tag) => typeof tag !== "string" || !tag.trim())) throw new Error("meta.tags must be an array of non-empty strings");
138
+ }
139
+ if (value.estimatedAgents !== void 0) {
140
+ const est = value.estimatedAgents;
141
+ if (!est || typeof est !== "object") throw new Error("meta.estimatedAgents must be an object");
142
+ if (typeof est.min !== "number" || typeof est.max !== "number" || !Number.isFinite(est.min) || !Number.isFinite(est.max)) throw new Error("meta.estimatedAgents.min and .max must be finite numbers");
143
+ if (est.min < 1 || est.max < est.min) throw new Error("meta.estimatedAgents requires min >= 1 and max >= min");
144
+ }
133
145
  }
134
146
  //#endregion
135
147
  export { parseWorkflowScript };
@@ -1 +1 @@
1
- {"version":3,"file":"parser.js","names":[],"sources":["../../../../src/agent/workflow/parser.ts"],"sourcesContent":["/**\n * Workflow script parser.\n *\n * Responsibilities:\n * 1. Parse the script with acorn (latest ECMA, top-level await + return allowed).\n * 2. Enforce determinism — reject `Date.now()`, `Math.random()`, `new Date()`,\n * `require`, `import`, dynamic eval. This keeps future resume/replay possible\n * and surfaces non-deterministic mistakes early.\n * 3. Require the first statement to be `export const meta = <literal>`, validate\n * the literal shape, and strip that line from the body returned to the runtime.\n *\n * Returning a `{ meta, body }` pair means the runtime can `vm.Script(body)` without\n * any further AST work.\n */\n\nimport { parse } from 'acorn';\nimport type { Node } from 'acorn';\n\nimport type { WorkflowMeta, WorkflowMetaPhase } from './types.js';\n\ntype AnyNode = Node & { [key: string]: any; start: number; end: number };\n\nconst NONDETERMINISM_ERROR =\n 'Workflow scripts must be deterministic: Date.now()/Math.random()/new Date() are unavailable. ' +\n 'Pass timestamps via args or stamp them after the workflow returns.';\n\nexport interface ParsedWorkflow {\n meta: WorkflowMeta;\n body: string;\n}\n\nexport function parseWorkflowScript(script: string): ParsedWorkflow {\n let ast: AnyNode;\n try {\n ast = parse(script, {\n ecmaVersion: 'latest',\n sourceType: 'module',\n allowAwaitOutsideFunction: true,\n allowReturnOutsideFunction: true,\n ranges: false,\n }) as AnyNode;\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n throw new Error(`Workflow script parse error: ${msg}`);\n }\n\n assertDeterministicAst(ast);\n assertNoDangerousImports(ast);\n\n const first = ast.body?.[0] as AnyNode | undefined;\n if (first?.type !== 'ExportNamedDeclaration') {\n throw new Error(\n '`export const meta = { name, description }` must be the first statement in the script',\n );\n }\n\n const declaration = first.declaration as AnyNode | null;\n if (declaration?.type !== 'VariableDeclaration' || declaration.kind !== 'const') {\n throw new Error('meta export must be `export const meta = ...`');\n }\n if (declaration.declarations.length !== 1) {\n throw new Error('meta export must declare only `meta`');\n }\n const declarator = declaration.declarations[0] as AnyNode;\n if (declarator.id?.type !== 'Identifier' || declarator.id.name !== 'meta') {\n throw new Error('meta export must declare `meta`');\n }\n if (!declarator.init) {\n throw new Error('meta must have a literal value');\n }\n\n const meta = evaluateLiteral(declarator.init, 'meta');\n validateMeta(meta);\n\n return {\n meta,\n body: script.slice(0, first.start) + script.slice(first.end),\n };\n}\n\n// ---------------------------------------------------------------------------\n// Determinism / safety guards\n// ---------------------------------------------------------------------------\n\nfunction assertDeterministicAst(node: AnyNode): void {\n if (isDateNowCall(node) || isMathRandomCall(node) || isNewDateExpression(node)) {\n throw new Error(NONDETERMINISM_ERROR);\n }\n for (const child of astChildren(node)) {\n assertDeterministicAst(child);\n }\n}\n\nfunction assertNoDangerousImports(node: AnyNode): void {\n if (node.type === 'ImportDeclaration' || node.type === 'ImportExpression') {\n throw new Error(\n \"Workflow scripts cannot use `import` — only the exposed globals (agent, parallel, pipeline, phase, log, args, cwd, budget) are available.\",\n );\n }\n if (node.type === 'CallExpression') {\n const callee = node.callee as AnyNode | undefined;\n if (callee?.type === 'Identifier' && (callee.name === 'require' || callee.name === 'eval')) {\n throw new Error(`Workflow scripts cannot call \\`${callee.name}\\`.`);\n }\n }\n for (const child of astChildren(node)) {\n assertNoDangerousImports(child);\n }\n}\n\nfunction astChildren(node: AnyNode): AnyNode[] {\n const children: AnyNode[] = [];\n for (const value of Object.values(node)) {\n if (Array.isArray(value)) {\n for (const v of value) {\n if (isAstNode(v)) children.push(v);\n }\n } else if (isAstNode(value)) {\n children.push(value);\n }\n }\n return children;\n}\n\nfunction isAstNode(value: unknown): value is AnyNode {\n return !!value && typeof value === 'object' && typeof (value as AnyNode).type === 'string';\n}\n\nfunction isDateNowCall(node: AnyNode): boolean {\n return node.type === 'CallExpression' && isMemberExpression(node.callee, 'Date', 'now');\n}\n\nfunction isMathRandomCall(node: AnyNode): boolean {\n return node.type === 'CallExpression' && isMemberExpression(node.callee, 'Math', 'random');\n}\n\nfunction isNewDateExpression(node: AnyNode): boolean {\n return (\n node.type === 'NewExpression' &&\n (node.callee as AnyNode | undefined)?.type === 'Identifier' &&\n (node.callee as AnyNode).name === 'Date'\n );\n}\n\nfunction isMemberExpression(\n node: AnyNode | undefined,\n objectName: string,\n propertyName: string,\n): boolean {\n if (\n node?.type !== 'MemberExpression' ||\n (node.object as AnyNode | undefined)?.type !== 'Identifier' ||\n (node.object as AnyNode).name !== objectName\n ) {\n return false;\n }\n const prop = node.property as AnyNode | undefined;\n if (!node.computed && prop?.type === 'Identifier') return prop.name === propertyName;\n if (prop?.type === 'Literal' && typeof prop.value === 'string') return prop.value === propertyName;\n return false;\n}\n\n// ---------------------------------------------------------------------------\n// Literal evaluator (meta must be a pure literal)\n// ---------------------------------------------------------------------------\n\nfunction evaluateLiteral(node: AnyNode, path: string): any {\n switch (node.type) {\n case 'ObjectExpression': {\n const out: Record<string, unknown> = {};\n for (const prop of node.properties as AnyNode[]) {\n if (prop.type === 'SpreadElement') {\n throw new Error(`spread not allowed in ${path}`);\n }\n if (prop.type !== 'Property') {\n throw new Error(`only plain properties allowed in ${path}`);\n }\n if (prop.computed) {\n throw new Error(`computed keys not allowed in ${path}`);\n }\n if (prop.kind !== 'init' || prop.method) {\n throw new Error(`methods/accessors not allowed in ${path}`);\n }\n const key = propertyKey(prop.key as AnyNode, path);\n if (key === '__proto__' || key === 'constructor' || key === 'prototype') {\n throw new Error(`reserved key name not allowed in ${path}: ${key}`);\n }\n out[key] = evaluateLiteral(prop.value as AnyNode, `${path}.${key}`);\n }\n return out;\n }\n case 'ArrayExpression':\n return (node.elements as Array<AnyNode | null>).map((element, index) => {\n if (!element) throw new Error(`sparse arrays not allowed in ${path}`);\n if (element.type === 'SpreadElement') {\n throw new Error(`spread not allowed in ${path}`);\n }\n return evaluateLiteral(element, `${path}[${index}]`);\n });\n case 'Literal':\n return node.value;\n case 'TemplateLiteral':\n if (node.expressions.length > 0) {\n throw new Error(`template interpolation not allowed in ${path}`);\n }\n return node.quasis\n .map((quasi: AnyNode) => quasi.value.cooked ?? quasi.value.raw)\n .join('');\n case 'UnaryExpression':\n if (\n node.operator === '-' &&\n node.argument?.type === 'Literal' &&\n typeof node.argument.value === 'number'\n ) {\n return -node.argument.value;\n }\n throw new Error(`only negative-number unary allowed in ${path}`);\n default:\n throw new Error(`non-literal node type in ${path}: ${node.type}`);\n }\n}\n\nfunction propertyKey(node: AnyNode, path: string): string {\n if (node.type === 'Identifier') return node.name;\n if (node.type === 'Literal' && (typeof node.value === 'string' || typeof node.value === 'number')) {\n return String(node.value);\n }\n throw new Error(`unsupported key type in ${path}: ${node.type}`);\n}\n\nfunction validateMeta(meta: unknown): asserts meta is WorkflowMeta {\n if (!meta || typeof meta !== 'object') {\n throw new Error('meta must be an object');\n }\n const value = meta as WorkflowMeta;\n if (typeof value.name !== 'string' || !value.name.trim()) {\n throw new Error('meta.name must be a non-empty string');\n }\n if (!/^[a-z][a-z0-9_-]*$/.test(value.name)) {\n throw new Error(\n `meta.name must be lowercase snake_case (got \"${value.name}\"). Example: \"audit_repo\".`,\n );\n }\n if (typeof value.description !== 'string' || !value.description.trim()) {\n throw new Error('meta.description must be a non-empty string');\n }\n if (value.whenToUse !== undefined && typeof value.whenToUse !== 'string') {\n throw new Error('meta.whenToUse must be a string');\n }\n if (value.phases !== undefined) {\n if (!Array.isArray(value.phases)) {\n throw new Error('meta.phases must be an array');\n }\n for (const phase of value.phases) {\n if (!phase || typeof phase !== 'object' || typeof (phase as WorkflowMetaPhase).title !== 'string') {\n throw new Error('each meta phase must have a title string');\n }\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"parser.js","names":[],"sources":["../../../../src/agent/workflow/parser.ts"],"sourcesContent":["/**\n * Workflow script parser.\n *\n * Responsibilities:\n * 1. Parse the script with acorn (latest ECMA, top-level await + return allowed).\n * 2. Enforce determinism — reject `Date.now()`, `Math.random()`, `new Date()`,\n * `require`, `import`, dynamic eval. This keeps future resume/replay possible\n * and surfaces non-deterministic mistakes early.\n * 3. Require the first statement to be `export const meta = <literal>`, validate\n * the literal shape, and strip that line from the body returned to the runtime.\n *\n * Returning a `{ meta, body }` pair means the runtime can `vm.Script(body)` without\n * any further AST work.\n */\n\nimport { parse } from 'acorn';\nimport type { Node } from 'acorn';\n\nimport { lintAwaits } from './lint.js';\nimport type { WorkflowMeta, WorkflowMetaPhase } from './types.js';\n\ntype AnyNode = Node & { [key: string]: any; start: number; end: number };\n\nconst NONDETERMINISM_ERROR =\n 'Workflow scripts must be deterministic: Date.now()/Math.random()/new Date() are unavailable. ' +\n 'Pass timestamps via args or stamp them after the workflow returns.';\n\nexport interface ParsedWorkflow {\n meta: WorkflowMeta;\n body: string;\n}\n\nexport function parseWorkflowScript(script: string): ParsedWorkflow {\n let ast: AnyNode;\n try {\n ast = parse(script, {\n ecmaVersion: 'latest',\n sourceType: 'module',\n allowAwaitOutsideFunction: true,\n allowReturnOutsideFunction: true,\n ranges: false,\n locations: true,\n }) as AnyNode;\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n throw new Error(`Workflow script parse error: ${msg}`);\n }\n\n assertDeterministicAst(ast);\n assertNoDangerousImports(ast);\n lintAwaits(ast);\n\n const first = ast.body?.[0] as AnyNode | undefined;\n if (first?.type !== 'ExportNamedDeclaration') {\n throw new Error(\n '`export const meta = { name, description }` must be the first statement in the script',\n );\n }\n\n const declaration = first.declaration as AnyNode | null;\n if (declaration?.type !== 'VariableDeclaration' || declaration.kind !== 'const') {\n throw new Error('meta export must be `export const meta = ...`');\n }\n if (declaration.declarations.length !== 1) {\n throw new Error('meta export must declare only `meta`');\n }\n const declarator = declaration.declarations[0] as AnyNode;\n if (declarator.id?.type !== 'Identifier' || declarator.id.name !== 'meta') {\n throw new Error('meta export must declare `meta`');\n }\n if (!declarator.init) {\n throw new Error('meta must have a literal value');\n }\n\n const meta = evaluateLiteral(declarator.init, 'meta');\n validateMeta(meta);\n\n return {\n meta,\n body: script.slice(0, first.start) + script.slice(first.end),\n };\n}\n\n// ---------------------------------------------------------------------------\n// Determinism / safety guards\n// ---------------------------------------------------------------------------\n\nfunction assertDeterministicAst(node: AnyNode): void {\n if (isDateNowCall(node) || isMathRandomCall(node) || isNewDateExpression(node)) {\n throw new Error(NONDETERMINISM_ERROR);\n }\n for (const child of astChildren(node)) {\n assertDeterministicAst(child);\n }\n}\n\nfunction assertNoDangerousImports(node: AnyNode): void {\n if (node.type === 'ImportDeclaration' || node.type === 'ImportExpression') {\n throw new Error(\n \"Workflow scripts cannot use `import` — only the exposed globals (agent, parallel, pipeline, phase, log, args, cwd, budget) are available.\",\n );\n }\n if (node.type === 'CallExpression') {\n const callee = node.callee as AnyNode | undefined;\n if (callee?.type === 'Identifier' && (callee.name === 'require' || callee.name === 'eval')) {\n throw new Error(`Workflow scripts cannot call \\`${callee.name}\\`.`);\n }\n }\n for (const child of astChildren(node)) {\n assertNoDangerousImports(child);\n }\n}\n\nfunction astChildren(node: AnyNode): AnyNode[] {\n const children: AnyNode[] = [];\n for (const value of Object.values(node)) {\n if (Array.isArray(value)) {\n for (const v of value) {\n if (isAstNode(v)) children.push(v);\n }\n } else if (isAstNode(value)) {\n children.push(value);\n }\n }\n return children;\n}\n\nfunction isAstNode(value: unknown): value is AnyNode {\n return !!value && typeof value === 'object' && typeof (value as AnyNode).type === 'string';\n}\n\nfunction isDateNowCall(node: AnyNode): boolean {\n return node.type === 'CallExpression' && isMemberExpression(node.callee, 'Date', 'now');\n}\n\nfunction isMathRandomCall(node: AnyNode): boolean {\n return node.type === 'CallExpression' && isMemberExpression(node.callee, 'Math', 'random');\n}\n\nfunction isNewDateExpression(node: AnyNode): boolean {\n return (\n node.type === 'NewExpression' &&\n (node.callee as AnyNode | undefined)?.type === 'Identifier' &&\n (node.callee as AnyNode).name === 'Date'\n );\n}\n\nfunction isMemberExpression(\n node: AnyNode | undefined,\n objectName: string,\n propertyName: string,\n): boolean {\n if (\n node?.type !== 'MemberExpression' ||\n (node.object as AnyNode | undefined)?.type !== 'Identifier' ||\n (node.object as AnyNode).name !== objectName\n ) {\n return false;\n }\n const prop = node.property as AnyNode | undefined;\n if (!node.computed && prop?.type === 'Identifier') return prop.name === propertyName;\n if (prop?.type === 'Literal' && typeof prop.value === 'string') return prop.value === propertyName;\n return false;\n}\n\n// ---------------------------------------------------------------------------\n// Literal evaluator (meta must be a pure literal)\n// ---------------------------------------------------------------------------\n\nfunction evaluateLiteral(node: AnyNode, path: string): any {\n switch (node.type) {\n case 'ObjectExpression': {\n const out: Record<string, unknown> = {};\n for (const prop of node.properties as AnyNode[]) {\n if (prop.type === 'SpreadElement') {\n throw new Error(`spread not allowed in ${path}`);\n }\n if (prop.type !== 'Property') {\n throw new Error(`only plain properties allowed in ${path}`);\n }\n if (prop.computed) {\n throw new Error(`computed keys not allowed in ${path}`);\n }\n if (prop.kind !== 'init' || prop.method) {\n throw new Error(`methods/accessors not allowed in ${path}`);\n }\n const key = propertyKey(prop.key as AnyNode, path);\n if (key === '__proto__' || key === 'constructor' || key === 'prototype') {\n throw new Error(`reserved key name not allowed in ${path}: ${key}`);\n }\n out[key] = evaluateLiteral(prop.value as AnyNode, `${path}.${key}`);\n }\n return out;\n }\n case 'ArrayExpression':\n return (node.elements as Array<AnyNode | null>).map((element, index) => {\n if (!element) throw new Error(`sparse arrays not allowed in ${path}`);\n if (element.type === 'SpreadElement') {\n throw new Error(`spread not allowed in ${path}`);\n }\n return evaluateLiteral(element, `${path}[${index}]`);\n });\n case 'Literal':\n return node.value;\n case 'TemplateLiteral':\n if (node.expressions.length > 0) {\n throw new Error(`template interpolation not allowed in ${path}`);\n }\n return node.quasis\n .map((quasi: AnyNode) => quasi.value.cooked ?? quasi.value.raw)\n .join('');\n case 'UnaryExpression':\n if (\n node.operator === '-' &&\n node.argument?.type === 'Literal' &&\n typeof node.argument.value === 'number'\n ) {\n return -node.argument.value;\n }\n throw new Error(`only negative-number unary allowed in ${path}`);\n default:\n throw new Error(`non-literal node type in ${path}: ${node.type}`);\n }\n}\n\nfunction propertyKey(node: AnyNode, path: string): string {\n if (node.type === 'Identifier') return node.name;\n if (node.type === 'Literal' && (typeof node.value === 'string' || typeof node.value === 'number')) {\n return String(node.value);\n }\n throw new Error(`unsupported key type in ${path}: ${node.type}`);\n}\n\nfunction validateMeta(meta: unknown): asserts meta is WorkflowMeta {\n if (!meta || typeof meta !== 'object') {\n throw new Error('meta must be an object');\n }\n const value = meta as WorkflowMeta;\n if (typeof value.name !== 'string' || !value.name.trim()) {\n throw new Error('meta.name must be a non-empty string');\n }\n if (!/^[a-z][a-z0-9_-]*$/.test(value.name)) {\n throw new Error(\n `meta.name must be lowercase snake_case (got \"${value.name}\"). Example: \"audit_repo\".`,\n );\n }\n if (typeof value.description !== 'string' || !value.description.trim()) {\n throw new Error('meta.description must be a non-empty string');\n }\n if (value.whenToUse !== undefined && typeof value.whenToUse !== 'string') {\n throw new Error('meta.whenToUse must be a string');\n }\n if (value.phases !== undefined) {\n if (!Array.isArray(value.phases)) {\n throw new Error('meta.phases must be an array');\n }\n for (const phase of value.phases) {\n if (!phase || typeof phase !== 'object' || typeof (phase as WorkflowMetaPhase).title !== 'string') {\n throw new Error('each meta phase must have a title string');\n }\n }\n }\n if (value.tags !== undefined) {\n if (!Array.isArray(value.tags) || value.tags.some((tag) => typeof tag !== 'string' || !tag.trim())) {\n throw new Error('meta.tags must be an array of non-empty strings');\n }\n }\n if (value.estimatedAgents !== undefined) {\n const est = value.estimatedAgents;\n if (!est || typeof est !== 'object') {\n throw new Error('meta.estimatedAgents must be an object');\n }\n if (typeof est.min !== 'number' || typeof est.max !== 'number' || !Number.isFinite(est.min) || !Number.isFinite(est.max)) {\n throw new Error('meta.estimatedAgents.min and .max must be finite numbers');\n }\n if (est.min < 1 || est.max < est.min) {\n throw new Error('meta.estimatedAgents requires min >= 1 and max >= min');\n }\n }\n}\n"],"mappings":"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"}
@@ -23,9 +23,12 @@
23
23
  * carries no LLM dependency, and is fully driven by injected callbacks — that
24
24
  * means the workflow tool, tests, and any future runner share one runtime.
25
25
  */
26
+ import type { Api, Model } from '@earendil-works/pi-ai';
26
27
  import type { SubagentRunner, WorkflowRunOptions, WorkflowRunResult, WorkflowSnapshot } from './types.js';
27
28
  export interface RunWorkflowDeps {
28
29
  runner: SubagentRunner;
30
+ /** Resolve a real model id (must contain `/`) to a {@link Model}. Throws on unknown id. */
31
+ resolveModelId?: (modelId: string) => Model<Api>;
29
32
  }
30
33
  export declare function runWorkflow<T = unknown>(script: string, deps: RunWorkflowDeps, options: WorkflowRunOptions): Promise<WorkflowRunResult<T>>;
31
34
  export declare function emptySnapshotFor(name: string, description?: string): WorkflowSnapshot;