@vellumai/assistant 0.8.12 → 0.9.0-staging.2

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 (442) hide show
  1. package/AGENTS.md +0 -14
  2. package/ARCHITECTURE.md +45 -45
  3. package/README.md +1 -1
  4. package/bun.lock +200 -154
  5. package/docs/architecture/integrations.md +3 -3
  6. package/docs/architecture/memory.md +2 -2
  7. package/docs/architecture/security.md +10 -10
  8. package/docs/runbook-trusted-contacts.md +12 -12
  9. package/docs/skills.md +6 -6
  10. package/docs/workflows-testing.md +221 -0
  11. package/docs/workflows.md +510 -0
  12. package/examples/plugins/echo/README.md +5 -5
  13. package/knip.json +2 -0
  14. package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +105 -0
  15. package/node_modules/@vellumai/gateway-client/src/index.ts +12 -0
  16. package/openapi.yaml +7197 -5708
  17. package/package.json +8 -4
  18. package/scripts/generate-openapi.ts +66 -114
  19. package/src/__tests__/access-request-seed-content-blocks.test.ts +213 -0
  20. package/src/__tests__/adaptive-thinking-repair.test.ts +32 -3
  21. package/src/__tests__/agent-loop-output-hooks.test.ts +183 -0
  22. package/src/__tests__/agent-loop-regrowth-guard.test.ts +506 -0
  23. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +2 -0
  24. package/src/__tests__/agent-wake-override-profile.test.ts +77 -0
  25. package/src/__tests__/app-compiler.test.ts +7 -1
  26. package/src/__tests__/app-dir-path-guard.test.ts +27 -3
  27. package/src/__tests__/app-executors.test.ts +43 -0
  28. package/src/__tests__/approval-cascade.test.ts +0 -5
  29. package/src/__tests__/approval-routes-http.test.ts +91 -0
  30. package/src/__tests__/assistant-stream-state.test.ts +107 -0
  31. package/src/__tests__/browser-fill-credential.test.ts +3 -3
  32. package/src/__tests__/bundled-skill-retrieval-guard.test.ts +1 -1
  33. package/src/__tests__/compaction-events.test.ts +63 -7
  34. package/src/__tests__/compaction-trail-store.test.ts +74 -1
  35. package/src/__tests__/compaction.benchmark.test.ts +63 -41
  36. package/src/__tests__/compactor-low-watermark-cut.test.ts +349 -0
  37. package/src/__tests__/context-window-manager-compact-retry.test.ts +64 -0
  38. package/src/__tests__/conversation-abort-tool-results.test.ts +0 -5
  39. package/src/__tests__/conversation-confirmation-signals.test.ts +0 -5
  40. package/src/__tests__/conversation-history-web-search.test.ts +7 -0
  41. package/src/__tests__/conversation-process-callsite.test.ts +0 -5
  42. package/src/__tests__/conversation-provider-retry-repair.test.ts +0 -5
  43. package/src/__tests__/conversation-queue.test.ts +0 -5
  44. package/src/__tests__/conversation-slash-queue.test.ts +0 -5
  45. package/src/__tests__/conversation-slash-unknown.test.ts +0 -5
  46. package/src/__tests__/conversation-speed-override.test.ts +0 -5
  47. package/src/__tests__/conversation-surfaces-data-persist.test.ts +97 -0
  48. package/src/__tests__/conversation-surfaces-task-progress.test.ts +67 -0
  49. package/src/__tests__/conversation-usage.test.ts +2 -0
  50. package/src/__tests__/conversation-workspace-injection.test.ts +0 -5
  51. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +0 -5
  52. package/src/__tests__/credential-broker-browser-fill.test.ts +2 -2
  53. package/src/__tests__/credential-broker-server-use.test.ts +2 -2
  54. package/src/__tests__/credential-broker.test.ts +1 -1
  55. package/src/__tests__/credential-prompt-route.test.ts +417 -0
  56. package/src/__tests__/credential-security-invariants.test.ts +1 -0
  57. package/src/__tests__/credential-vault.test.ts +37 -0
  58. package/src/__tests__/db-schedule-syntax-migration.test.ts +24 -0
  59. package/src/__tests__/dynamic-page-surface.test.ts +219 -0
  60. package/src/__tests__/empty-state-greeting-cache.test.ts +94 -0
  61. package/src/__tests__/gateway-flag-listener.test.ts +24 -7
  62. package/src/__tests__/guardian-action-sweep.test.ts +56 -219
  63. package/src/__tests__/guardian-routing-invariants.test.ts +138 -0
  64. package/src/__tests__/helpers/channel-test-adapter.ts +0 -2
  65. package/src/__tests__/list-messages-hidden-metadata.test.ts +99 -0
  66. package/src/__tests__/llm-request-log-source-clickhouse.test.ts +87 -1
  67. package/src/__tests__/llm-resolver.test.ts +115 -0
  68. package/src/__tests__/managed-profile-guard.test.ts +6 -5
  69. package/src/__tests__/max-tokens-continue-hook.test.ts +184 -0
  70. package/src/__tests__/media-generate-image.test.ts +20 -9
  71. package/src/__tests__/mock-gateway-ipc.ts +23 -0
  72. package/src/__tests__/model-intents.test.ts +1 -1
  73. package/src/__tests__/normalize-onboarding.test.ts +26 -0
  74. package/src/__tests__/notification-decision-strategy.test.ts +4 -2
  75. package/src/__tests__/notification-telegram-adapter.test.ts +21 -3
  76. package/src/__tests__/pending-interactions-resolved-event.test.ts +62 -0
  77. package/src/__tests__/post-turn-tool-result-truncation.test.ts +72 -18
  78. package/src/__tests__/require-fresh-approval.test.ts +425 -1
  79. package/src/__tests__/resolve-app-id.test.ts +56 -0
  80. package/src/__tests__/runtime-events-sse-parity.test.ts +2 -0
  81. package/src/__tests__/schedule-routes-workflow-validation.test.ts +408 -0
  82. package/src/__tests__/schedule-routes.test.ts +257 -4
  83. package/src/__tests__/schedule-store.test.ts +60 -0
  84. package/src/__tests__/schedule-tools.test.ts +247 -2
  85. package/src/__tests__/skill-execute-input.test.ts +85 -0
  86. package/src/__tests__/skill-secret-handling-guard.test.ts +21 -20
  87. package/src/__tests__/skills.test.ts +3 -3
  88. package/src/__tests__/slack-app-setup-skill-regression.test.ts +1 -1
  89. package/src/__tests__/subagent-tool-filtering.test.ts +50 -0
  90. package/src/__tests__/subagent-tool-gate-mode.test.ts +547 -0
  91. package/src/__tests__/system-prompt.test.ts +1 -1
  92. package/src/__tests__/task-progress-nudge-hook.test.ts +372 -0
  93. package/src/__tests__/task-scheduler.test.ts +299 -0
  94. package/src/__tests__/tool-approval-seed-content-blocks.test.ts +209 -0
  95. package/src/__tests__/tool-result-spool.test.ts +3 -1
  96. package/src/__tests__/workspace-migration-102-preserve-heartbeat-enabled-for-existing-workspaces.test.ts +181 -0
  97. package/src/__tests__/workspace-migration-103-upgrade-quality-profile-to-opus-4-8.test.ts +174 -0
  98. package/src/agent/compaction-circuit.ts +11 -0
  99. package/src/agent/loop.ts +181 -12
  100. package/src/api/constants/call-sites.ts +12 -0
  101. package/src/api/events/assistant-thinking-delta.ts +10 -0
  102. package/src/api/events/usage-update.ts +7 -0
  103. package/src/api/index.ts +4 -1
  104. package/src/api/responses/memory-v3-selection-log.ts +18 -11
  105. package/src/approvals/approval-primitive.ts +2 -2
  106. package/src/background-wake/background-wake-routes.test.ts +5 -2
  107. package/src/bundler/compiler-tools.ts +1 -1
  108. package/src/bundler/package-resolver.ts +0 -1
  109. package/src/calls/call-domain.ts +1 -1
  110. package/src/calls/guardian-action-sweep.ts +16 -93
  111. package/src/cli/AGENTS.md +4 -0
  112. package/src/cli/commands/__tests__/schedules.test.ts +430 -1
  113. package/src/cli/commands/credentials.ts +28 -24
  114. package/src/cli/commands/image-generation.ts +23 -9
  115. package/src/cli/commands/notifications.ts +1 -1
  116. package/src/cli/commands/plugins.ts +89 -46
  117. package/src/cli/commands/schedules.ts +384 -11
  118. package/src/cli/lib/__tests__/inspect-plugin.test.ts +69 -5
  119. package/src/cli/lib/__tests__/install-from-github.test.ts +15 -0
  120. package/src/cli/lib/__tests__/upgrade-plugin.test.ts +81 -4
  121. package/src/cli/lib/inspect-plugin.ts +62 -1
  122. package/src/cli/lib/install-from-github.ts +52 -5
  123. package/src/cli/lib/upgrade-plugin.ts +18 -0
  124. package/src/config/__tests__/workflows-schema.test.ts +60 -0
  125. package/src/config/bundled-skills/acp/SKILL.md +2 -2
  126. package/src/config/bundled-skills/app-builder/SKILL.md +1 -1
  127. package/src/config/bundled-skills/app-builder/tools/app-create.ts +6 -1
  128. package/src/config/bundled-skills/app-builder/tools/app-generate-icon.ts +7 -1
  129. package/src/config/bundled-skills/app-builder/tools/app-refresh.ts +7 -1
  130. package/src/config/bundled-skills/app-builder/tools/app-update.ts +10 -1
  131. package/src/config/bundled-skills/image-studio/SKILL.md +66 -19
  132. package/src/config/bundled-skills/image-studio/TOOLS.json +1 -6
  133. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +22 -3
  134. package/src/config/bundled-skills/personal-page/SKILL.md +57 -0
  135. package/src/config/bundled-skills/personal-page/TOOLS.json +27 -0
  136. package/src/config/bundled-skills/personal-page/tools/app-refresh.ts +17 -0
  137. package/src/config/bundled-skills/schedule/SKILL.md +7 -2
  138. package/src/config/bundled-skills/schedule/TOOLS.json +48 -4
  139. package/src/config/bundled-skills/workflows/SKILL.md +214 -0
  140. package/src/config/bundled-skills/workflows/TOOLS.json +84 -0
  141. package/src/config/bundled-skills/workflows/tools/manage-workflows.ts +12 -0
  142. package/src/config/bundled-skills/workflows/tools/run-workflow.ts +12 -0
  143. package/src/config/bundled-tool-registry.ts +14 -2
  144. package/src/config/call-site-defaults.ts +5 -0
  145. package/src/config/feature-flag-registry.json +12 -4
  146. package/src/config/llm-context-resolution.ts +8 -0
  147. package/src/config/llm-resolver.ts +30 -0
  148. package/src/config/preloaded-apps/personal-page/src/components/About.tsx +22 -0
  149. package/src/config/preloaded-apps/personal-page/src/components/App.tsx +16 -0
  150. package/src/config/preloaded-apps/personal-page/src/components/Features.tsx +77 -0
  151. package/src/config/preloaded-apps/personal-page/src/components/Hero.tsx +57 -0
  152. package/src/config/preloaded-apps/personal-page/src/components/Pending.tsx +28 -0
  153. package/src/config/preloaded-apps/personal-page/src/components/animations.tsx +234 -0
  154. package/src/config/preloaded-apps/personal-page/src/components/icons.tsx +48 -0
  155. package/src/config/preloaded-apps/personal-page/src/components/media.ts +16 -0
  156. package/src/config/preloaded-apps/personal-page/src/index.html +20 -0
  157. package/src/config/preloaded-apps/personal-page/src/main.tsx +7 -0
  158. package/src/config/preloaded-apps/personal-page/src/profile-data.ts +82 -0
  159. package/src/config/preloaded-apps/personal-page/src/styles.css +759 -0
  160. package/src/config/schema.ts +2 -0
  161. package/src/config/schemas/call-site-catalog.ts +7 -0
  162. package/src/config/schemas/heartbeat.ts +4 -1
  163. package/src/config/schemas/llm.ts +33 -26
  164. package/src/config/schemas/memory-retrospective.ts +19 -0
  165. package/src/config/schemas/platform.ts +8 -0
  166. package/src/config/schemas/services.ts +5 -2
  167. package/src/config/schemas/workflows.ts +42 -0
  168. package/src/config/skills.ts +3 -3
  169. package/src/context/compactor.ts +273 -39
  170. package/src/context/post-turn-tool-result-truncation.ts +23 -6
  171. package/src/context/tool-result-spool.ts +12 -17
  172. package/src/credential-execution/executable-discovery.ts +1 -1
  173. package/src/credential-execution/process-manager.ts +37 -3
  174. package/src/credential-execution/prompted-credential.ts +205 -0
  175. package/src/daemon/conversation-agent-loop-handlers.ts +14 -0
  176. package/src/daemon/conversation-process.ts +11 -2
  177. package/src/daemon/conversation-surfaces.ts +167 -3
  178. package/src/daemon/conversation-tool-setup.ts +103 -26
  179. package/src/daemon/conversation-usage.ts +2 -0
  180. package/src/daemon/conversation.ts +115 -11
  181. package/src/daemon/handlers/shared.ts +26 -14
  182. package/src/daemon/host-cu-proxy.ts +15 -12
  183. package/src/daemon/host-file-proxy.ts +15 -12
  184. package/src/daemon/host-transfer-proxy.ts +30 -24
  185. package/src/daemon/lifecycle.ts +40 -3
  186. package/src/daemon/message-protocol.ts +3 -0
  187. package/src/daemon/message-types/messages.ts +2 -10
  188. package/src/daemon/message-types/workflows.ts +49 -0
  189. package/src/daemon/parse-actual-tokens-from-error.test.ts +62 -1
  190. package/src/daemon/parse-actual-tokens-from-error.ts +43 -4
  191. package/src/daemon/process-message.ts +6 -0
  192. package/src/daemon/tool-setup-types.ts +57 -0
  193. package/src/daemon/wake-conversation-ops.ts +18 -0
  194. package/src/heartbeat/heartbeat-run-store.ts +8 -2
  195. package/src/home/feed-types.ts +1 -1
  196. package/src/ipc/gateway-flag-listener.ts +28 -6
  197. package/src/mcp/mcp-auth-state.ts +8 -20
  198. package/src/media/__tests__/image-models.test.ts +57 -0
  199. package/src/media/image-models.ts +66 -0
  200. package/src/memory/__tests__/auto-analysis-enqueue.test.ts +38 -0
  201. package/src/memory/__tests__/find-most-recent-retrospective-for.test.ts +12 -2
  202. package/src/memory/__tests__/memory-retrospective-job.test.ts +911 -34
  203. package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +227 -5
  204. package/src/memory/__tests__/memory-retrospective-state.test.ts +195 -0
  205. package/src/memory/__tests__/preloaded-apps.test.ts +85 -0
  206. package/src/memory/auto-analysis-enqueue.ts +14 -1
  207. package/src/memory/compaction-log-store-clickhouse.ts +6 -4
  208. package/src/memory/conversation-crud.ts +9 -2
  209. package/src/memory/conversation-disk-view.ts +1 -1
  210. package/src/memory/conversation-queries.ts +22 -7
  211. package/src/memory/db-init.ts +20 -0
  212. package/src/memory/db-maintenance.ts +16 -0
  213. package/src/memory/embedding-runtime-manager.ts +1 -1
  214. package/src/memory/llm-request-log-source-clickhouse.ts +112 -14
  215. package/src/memory/llm-request-log-source-local.ts +19 -1
  216. package/src/memory/llm-request-log-source.ts +35 -6
  217. package/src/memory/llm-request-log-store.ts +90 -2
  218. package/src/memory/memory-retrospective-constants.ts +9 -0
  219. package/src/memory/memory-retrospective-enqueue.ts +3 -6
  220. package/src/memory/memory-retrospective-fork-boundary.ts +94 -0
  221. package/src/memory/memory-retrospective-job.ts +500 -208
  222. package/src/memory/memory-retrospective-startup-cleanup.ts +97 -19
  223. package/src/memory/memory-retrospective-state.ts +85 -2
  224. package/src/memory/migrations/281-memory-retrospective-remembered-log.ts +40 -0
  225. package/src/memory/migrations/282-schedule-inference-profile.test.ts +77 -0
  226. package/src/memory/migrations/282-schedule-inference-profile.ts +26 -0
  227. package/src/memory/migrations/283-memory-v3-selections-message-id-and-sections.test.ts +102 -0
  228. package/src/memory/migrations/283-memory-v3-selections-message-id-and-sections.ts +53 -0
  229. package/src/memory/migrations/284-workflow-runs.ts +51 -0
  230. package/src/memory/migrations/285-schedule-workflow-mode.ts +26 -0
  231. package/src/memory/migrations/286-workflow-run-trust.ts +27 -0
  232. package/src/memory/migrations/287-conversation-origin-channel-index.ts +15 -0
  233. package/src/memory/migrations/288-backfill-origin-channel-from-bindings.ts +43 -0
  234. package/src/memory/migrations/289-contact-channels-unique-ext-user.ts +115 -0
  235. package/src/memory/migrations/290-schedule-capabilities.test.ts +77 -0
  236. package/src/memory/migrations/290-schedule-capabilities.ts +25 -0
  237. package/src/memory/migrations/__tests__/281-memory-retrospective-remembered-log.test.ts +96 -0
  238. package/src/memory/migrations/__tests__/289-contact-channels-unique-ext-user.test.ts +571 -0
  239. package/src/memory/migrations/index.ts +10 -0
  240. package/src/memory/preloaded-apps.ts +116 -0
  241. package/src/memory/schema/infrastructure.ts +4 -0
  242. package/src/memory/schema/memory-core.ts +4 -0
  243. package/src/memory/v2/__tests__/concept-page-frontmatter-schema.test.ts +45 -0
  244. package/src/memory/v2/__tests__/frontmatter-sweep.test.ts +11 -7
  245. package/src/memory/v2/__tests__/page-store.test.ts +13 -2
  246. package/src/memory/v2/__tests__/qdrant.test.ts +24 -0
  247. package/src/memory/v2/frontmatter-sweep.ts +7 -6
  248. package/src/memory/v2/page-store.ts +4 -3
  249. package/src/memory/v2/qdrant.ts +42 -3
  250. package/src/memory/v2/types.ts +16 -10
  251. package/src/messaging/draft-store.ts +1 -1
  252. package/src/notifications/access-request-copy.ts +200 -113
  253. package/src/notifications/adapters/slack.ts +250 -111
  254. package/src/notifications/adapters/telegram.ts +7 -44
  255. package/src/notifications/approval-card-builder.ts +93 -0
  256. package/src/notifications/broadcaster.ts +74 -0
  257. package/src/notifications/conversation-pairing.ts +8 -6
  258. package/src/notifications/copy-composer.ts +32 -26
  259. package/src/notifications/decision-engine.ts +59 -7
  260. package/src/notifications/guardian-question-mode.ts +145 -155
  261. package/src/notifications/home-feed-side-effect.ts +28 -11
  262. package/src/notifications/notification-utils.ts +66 -0
  263. package/src/notifications/signal.ts +6 -0
  264. package/src/notifications/tool-approval-copy.ts +142 -0
  265. package/src/notifications/types.ts +19 -0
  266. package/src/permissions/threshold.ts +11 -0
  267. package/src/plugin-api/types.ts +16 -4
  268. package/src/plugins/defaults/compaction/window-manager.ts +44 -0
  269. package/src/plugins/defaults/index.ts +46 -0
  270. package/src/plugins/defaults/max-tokens-continue/continue-state-store.ts +53 -0
  271. package/src/plugins/defaults/max-tokens-continue/hooks/post-model-call.ts +80 -0
  272. package/src/plugins/defaults/max-tokens-continue/hooks/stop.ts +20 -0
  273. package/src/plugins/defaults/max-tokens-continue/package.json +14 -0
  274. package/src/plugins/defaults/memory-retrieval/hooks/__tests__/user-prompt-submit.test.ts +37 -0
  275. package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +29 -1
  276. package/src/plugins/defaults/memory-v3-shadow/__tests__/carry-integration.test.ts +8 -3
  277. package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +4 -2
  278. package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +28 -18
  279. package/src/plugins/defaults/memory-v3-shadow/__tests__/section-dense-store.test.ts +67 -0
  280. package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +122 -22
  281. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-integration.test.ts +2 -0
  282. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +63 -1
  283. package/src/plugins/defaults/memory-v3-shadow/injector.ts +61 -18
  284. package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +1 -1
  285. package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +39 -10
  286. package/src/plugins/defaults/memory-v3-shadow/section-dense-store.ts +34 -1
  287. package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +112 -47
  288. package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +78 -15
  289. package/src/plugins/defaults/task-progress-nudge/hooks/post-tool-use.ts +206 -0
  290. package/src/plugins/defaults/task-progress-nudge/package.json +15 -0
  291. package/src/prompts/__tests__/system-prompt.test.ts +100 -1
  292. package/src/prompts/__tests__/task-progress-hint-section.test.ts +5 -7
  293. package/src/prompts/normalize-onboarding.ts +2 -0
  294. package/src/prompts/persona-resolver.ts +3 -0
  295. package/src/prompts/system-prompt.ts +51 -2
  296. package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +3 -1
  297. package/src/prompts/templates/system-sections.ts +8 -3
  298. package/src/providers/call-site-routing.ts +6 -3
  299. package/src/providers/fireworks/client.ts +3 -0
  300. package/src/providers/inference/auth.ts +52 -46
  301. package/src/providers/model-intents.ts +2 -2
  302. package/src/providers/openai/__tests__/coerce-object-args.test.ts +105 -0
  303. package/src/providers/openai/chat-completions-provider.ts +47 -9
  304. package/src/providers/openai/coerce-object-args.ts +104 -0
  305. package/src/providers/retry.ts +8 -5
  306. package/src/providers/types.ts +10 -0
  307. package/src/runtime/__tests__/agent-wake.test.ts +629 -7
  308. package/src/runtime/access-request-helper.ts +16 -9
  309. package/src/runtime/agent-wake.ts +302 -51
  310. package/src/runtime/assistant-stream-state.ts +141 -8
  311. package/src/runtime/background-job-runner.ts +9 -0
  312. package/src/runtime/channel-approval-types.ts +1 -0
  313. package/src/runtime/channel-invite-transports/telegram.ts +6 -5
  314. package/src/runtime/channel-invite-transports/voice.ts +2 -2
  315. package/src/runtime/channel-invite-types.ts +4 -2
  316. package/src/runtime/channel-retry-sweep.ts +19 -41
  317. package/src/runtime/finalize-event-delivery.ts +72 -0
  318. package/src/runtime/guardian-action-message-composer.ts +0 -54
  319. package/src/runtime/http-server.ts +6 -14
  320. package/src/runtime/http-types.ts +0 -1
  321. package/src/runtime/message-composer-types.ts +0 -9
  322. package/src/runtime/middleware/__tests__/rate-limiter.test.ts +63 -0
  323. package/src/runtime/middleware/auth.ts +27 -3
  324. package/src/runtime/middleware/rate-limiter.ts +28 -1
  325. package/src/runtime/migrations/vbundle-builder.ts +6 -5
  326. package/src/runtime/pending-interactions.ts +20 -1
  327. package/src/runtime/routes/__tests__/conversation-compaction-routes.test.ts +232 -173
  328. package/src/runtime/routes/__tests__/plugins-routes.test.ts +18 -0
  329. package/src/runtime/routes/__tests__/retrospective-routes.test.ts +436 -0
  330. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +11 -0
  331. package/src/runtime/routes/approval-routes.ts +35 -8
  332. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +2 -1
  333. package/src/runtime/routes/btw-routes.ts +37 -1
  334. package/src/runtime/routes/channel-delivery-routes.ts +11 -7
  335. package/src/runtime/routes/channel-route-definitions.ts +3 -0
  336. package/src/runtime/routes/channel-route-shared.ts +3 -1
  337. package/src/runtime/routes/consolidation-routes.ts +17 -13
  338. package/src/runtime/routes/conversation-compaction-routes.ts +159 -119
  339. package/src/runtime/routes/conversation-list-routes.ts +41 -4
  340. package/src/runtime/routes/conversation-query-routes.ts +196 -11
  341. package/src/runtime/routes/conversation-routes.ts +15 -1
  342. package/src/runtime/routes/credential-prompt-routes.ts +39 -17
  343. package/src/runtime/routes/empty-state-greeting-cache.ts +65 -0
  344. package/src/runtime/routes/heartbeat-routes.ts +18 -13
  345. package/src/runtime/routes/image-generation-routes.ts +20 -2
  346. package/src/runtime/routes/inbound-message-handler.ts +13 -12
  347. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +32 -31
  348. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +7 -5
  349. package/src/runtime/routes/inbound-stages/background-dispatch.ts +7 -21
  350. package/src/runtime/routes/inbound-stages/escalation-intercept.ts +5 -5
  351. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +6 -15
  352. package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +1 -1
  353. package/src/runtime/routes/index.ts +6 -0
  354. package/src/runtime/routes/log-export/AGENTS.md +1 -1
  355. package/src/runtime/routes/log-export/workspace-allowlist.ts +1 -1
  356. package/src/runtime/routes/migration-routes.ts +5 -9
  357. package/src/runtime/routes/plugins-routes.ts +26 -0
  358. package/src/runtime/routes/ps-routes.ts +10 -8
  359. package/src/runtime/routes/retrospective-routes.ts +235 -0
  360. package/src/runtime/routes/runs-pagination.ts +75 -0
  361. package/src/runtime/routes/schedule-routes.ts +367 -59
  362. package/src/runtime/routes/sounds-config-routes.ts +239 -0
  363. package/src/runtime/routes/surface-action-routes.ts +84 -4
  364. package/src/runtime/routes/workflow-routes.test.ts +372 -0
  365. package/src/runtime/routes/workflow-routes.ts +363 -0
  366. package/src/runtime/routes/workspace-routes.test.ts +61 -1
  367. package/src/runtime/routes/workspace-routes.ts +26 -1
  368. package/src/runtime/services/__tests__/analyze-conversation.test.ts +38 -0
  369. package/src/runtime/services/analyze-conversation.ts +26 -13
  370. package/src/schedule/inference-profile.ts +28 -0
  371. package/src/schedule/schedule-store.ts +152 -4
  372. package/src/schedule/scheduler-types.ts +6 -0
  373. package/src/schedule/scheduler.ts +96 -0
  374. package/src/security/secret-allowlist.ts +1 -1
  375. package/src/skills/path-classifier.ts +1 -1
  376. package/src/tools/apps/executors.ts +23 -0
  377. package/src/tools/apps/resolve-app-id.ts +42 -0
  378. package/src/tools/browser/browser-execution.ts +9 -11
  379. package/src/tools/credentials/broker.ts +4 -4
  380. package/src/tools/credentials/vault.ts +26 -137
  381. package/src/tools/executor.ts +69 -0
  382. package/src/tools/flag-gated-tools.test.ts +76 -0
  383. package/src/tools/permission-checker.ts +8 -1
  384. package/src/tools/registry.ts +51 -0
  385. package/src/tools/schedule/create.ts +77 -1
  386. package/src/tools/schedule/list.ts +1 -0
  387. package/src/tools/schedule/update.ts +73 -1
  388. package/src/tools/skills/execute.ts +56 -0
  389. package/src/tools/terminal/shell.ts +1 -1
  390. package/src/tools/ui-surface/definitions.ts +91 -2
  391. package/src/tools/workflows/manage-workflows.ts +183 -0
  392. package/src/tools/workflows/run-workflow.test.ts +442 -0
  393. package/src/tools/workflows/run-workflow.ts +88 -0
  394. package/src/types/onboarding-context.ts +2 -0
  395. package/src/usage/attribution.ts +24 -0
  396. package/src/util/canonicalize-identity.ts +12 -3
  397. package/src/util/platform.ts +17 -17
  398. package/src/watcher/__tests__/engine.test.ts +24 -0
  399. package/src/watcher/__tests__/telemetry.test.ts +135 -0
  400. package/src/watcher/engine.ts +7 -0
  401. package/src/watcher/telemetry.ts +74 -0
  402. package/src/workflows/capabilities.test.ts +365 -0
  403. package/src/workflows/capabilities.ts +359 -0
  404. package/src/workflows/deterministic-stringify.ts +27 -0
  405. package/src/workflows/engine-integration.test.ts +656 -0
  406. package/src/workflows/engine.test.ts +1144 -0
  407. package/src/workflows/engine.ts +1078 -0
  408. package/src/workflows/fanout-load.test.ts +168 -0
  409. package/src/workflows/journal-store.test.ts +369 -0
  410. package/src/workflows/journal-store.ts +470 -0
  411. package/src/workflows/leaf-runner.test.ts +704 -0
  412. package/src/workflows/leaf-runner.ts +589 -0
  413. package/src/workflows/library.test.ts +134 -0
  414. package/src/workflows/library.ts +124 -0
  415. package/src/workflows/run-manager.test.ts +711 -0
  416. package/src/workflows/run-manager.ts +593 -0
  417. package/src/workflows/sandbox-escape.test.ts +339 -0
  418. package/src/workflows/sandbox.test.ts +251 -0
  419. package/src/workflows/sandbox.ts +447 -0
  420. package/src/workspace/adaptive-thinking-repair.ts +33 -11
  421. package/src/workspace/migrations/021-move-signals-to-workspace.ts +1 -1
  422. package/src/workspace/migrations/022-move-hooks-to-workspace.ts +1 -1
  423. package/src/workspace/migrations/026-backfill-install-meta.ts +1 -1
  424. package/src/workspace/migrations/030-seed-pkb-autoinject.ts +2 -1
  425. package/src/workspace/migrations/031-drop-user-md.ts +1 -4
  426. package/src/workspace/migrations/048-remove-workspace-hooks.ts +1 -1
  427. package/src/workspace/migrations/056-release-notes-inference-profile-reordering.ts +5 -2
  428. package/src/workspace/migrations/061-move-backup-key-to-workspace.ts +1 -1
  429. package/src/workspace/migrations/082-backfill-managed-profile-labels.ts +8 -2
  430. package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +41 -14
  431. package/src/workspace/migrations/102-preserve-heartbeat-enabled-for-existing-workspaces.ts +69 -0
  432. package/src/workspace/migrations/103-upgrade-quality-profile-to-opus-4-8.ts +83 -0
  433. package/src/workspace/migrations/104-recheck-adaptive-thinking-model-implied-anthropic.ts +133 -0
  434. package/src/workspace/migrations/registry.ts +6 -0
  435. package/src/workspace/migrations/runner.ts +1 -1
  436. package/tsconfig.json +1 -1
  437. package/src/__tests__/guardian-action-copy-generator.test.ts +0 -200
  438. package/src/__tests__/guardian-action-grant-mint-consume.test.ts +0 -579
  439. package/src/__tests__/guardian-action-store.test.ts +0 -106
  440. package/src/daemon/guardian-action-generators.ts +0 -71
  441. package/src/memory/guardian-action-store.ts +0 -484
  442. package/src/runtime/guardian-action-grant-minter.ts +0 -150
@@ -1,3 +1,6 @@
1
+ import { isAssistantFeatureFlagEnabled } from "../../config/assistant-feature-flags.js";
2
+ import { getConfig } from "../../config/loader.js";
3
+ import { validateScheduleInferenceProfile } from "../../schedule/inference-profile.js";
1
4
  import { validateRruleSetLines } from "../../schedule/recurrence-engine.js";
2
5
  import {
3
6
  detectScheduleSyntax,
@@ -17,7 +20,7 @@ import {
17
20
  } from "../../schedule/schedule-store.js";
18
21
  import type { ToolContext, ToolExecutionResult } from "../types.js";
19
22
 
20
- const VALID_MODES: ScheduleMode[] = ["notify", "execute", "script"];
23
+ const VALID_MODES: ScheduleMode[] = ["notify", "execute", "script", "workflow"];
21
24
  const VALID_ROUTING_INTENTS: RoutingIntent[] = [
22
25
  "single_channel",
23
26
  "multi_channel",
@@ -92,6 +95,17 @@ export async function executeScheduleUpdate(
92
95
  updates.mode = mode;
93
96
  }
94
97
 
98
+ // Workflow fields pass-through (validated against the resulting mode below)
99
+ if (input.workflow_name !== undefined) {
100
+ updates.workflowName =
101
+ typeof input.workflow_name === "string"
102
+ ? input.workflow_name.trim()
103
+ : null;
104
+ }
105
+ if (input.workflow_args !== undefined) {
106
+ updates.workflowArgs = input.workflow_args;
107
+ }
108
+
95
109
  // Routing intent validation and pass-through
96
110
  if (input.routing_intent !== undefined) {
97
111
  const routingIntent = input.routing_intent as RoutingIntent;
@@ -127,6 +141,27 @@ export async function executeScheduleUpdate(
127
141
  updates.retryBackoffMs = input.retry_backoff_ms;
128
142
  }
129
143
 
144
+ // Inference profile override (null clears it, reverting to the default
145
+ // main-agent model selection)
146
+ if (input.inference_profile !== undefined) {
147
+ if (input.inference_profile === null) {
148
+ updates.inferenceProfile = null;
149
+ } else {
150
+ const inferenceProfile = input.inference_profile;
151
+ if (typeof inferenceProfile !== "string") {
152
+ return {
153
+ content: "Error: inference_profile must be a string or null",
154
+ isError: true,
155
+ };
156
+ }
157
+ const profileError = validateScheduleInferenceProfile(inferenceProfile);
158
+ if (profileError) {
159
+ return { content: `Error: ${profileError}`, isError: true };
160
+ }
161
+ updates.inferenceProfile = inferenceProfile;
162
+ }
163
+ }
164
+
130
165
  // Script execution timeout override (null clears it, reverting to default)
131
166
  if (input.timeout_ms !== undefined) {
132
167
  if (input.timeout_ms === null) {
@@ -170,6 +205,39 @@ export async function executeScheduleUpdate(
170
205
  };
171
206
  }
172
207
 
208
+ // Mirror the HTTP route: a schedule whose RESULTING mode is `workflow` must be
209
+ // flag-enabled and carry a non-empty workflowName. Compute the post-update
210
+ // state (the update's value if present, else the persisted one) so both
211
+ // "switch to workflow without a name" and "clear the name on a workflow
212
+ // schedule" are rejected — otherwise the scheduler hits the `!job.workflowName`
213
+ // skip branch and a one-shot firing job wedges.
214
+ if (updates.mode !== undefined || updates.workflowName !== undefined) {
215
+ const existing = getSchedule(jobId);
216
+ if (existing) {
217
+ const resultingMode =
218
+ updates.mode !== undefined ? (updates.mode as string) : existing.mode;
219
+ if (resultingMode === "workflow") {
220
+ if (!isAssistantFeatureFlagEnabled("workflows", getConfig())) {
221
+ return {
222
+ content: "Error: workflows are not enabled.",
223
+ isError: true,
224
+ };
225
+ }
226
+ const resultingWorkflowName =
227
+ updates.workflowName !== undefined
228
+ ? ((updates.workflowName as string | null) ?? "")
229
+ : (existing.workflowName ?? "");
230
+ if (!resultingWorkflowName) {
231
+ return {
232
+ content:
233
+ "Error: workflow_name is required for workflow-mode schedules",
234
+ isError: true,
235
+ };
236
+ }
237
+ }
238
+ }
239
+ }
240
+
173
241
  // Set-aware pre-validation for RRULE expressions
174
242
  const effectiveSyntax = updates.syntax as string | undefined;
175
243
  const effectiveExpr =
@@ -210,6 +278,9 @@ export async function executeScheduleUpdate(
210
278
  maxRetries?: number;
211
279
  retryBackoffMs?: number;
212
280
  timeoutMs?: number | null;
281
+ workflowName?: string | null;
282
+ workflowArgs?: unknown;
283
+ inferenceProfile?: string | null;
213
284
  },
214
285
  );
215
286
 
@@ -231,6 +302,7 @@ export async function executeScheduleUpdate(
231
302
  ` Description: ${job.description}`,
232
303
  ` Syntax: ${job.syntax}`,
233
304
  ` Mode: ${job.mode}`,
305
+ ` Inference profile: ${job.inferenceProfile ?? "default (mainAgent)"}`,
234
306
  ` Schedule: ${scheduleDescription}${job.timezone ? ` (${job.timezone})` : ""}`,
235
307
  ` Enabled: ${job.enabled}`,
236
308
  ` Next run: ${job.enabled ? formatLocalDate(job.nextRunAt) : "n/a (disabled)"}`,
@@ -6,6 +6,62 @@ import type {
6
6
  ToolExecutionResult,
7
7
  } from "../types.js";
8
8
 
9
+ /** Envelope keys consumed by `skill_execute` itself, never inner-tool params. */
10
+ const SKILL_EXECUTE_ENVELOPE_KEYS = new Set(["tool", "input", "activity"]);
11
+
12
+ /**
13
+ * Resolve the inner tool's parameters from a `skill_execute` envelope.
14
+ *
15
+ * The documented contract is `{ tool, input: {...}, activity }` — the inner
16
+ * tool's parameters nested under `input`. A correctly nested, non-empty object
17
+ * is returned unchanged, so well-formed callers are unaffected.
18
+ *
19
+ * Weaker models routinely misplace the parameters. Left unhandled, the inner
20
+ * tool receives `{}`, fails schema validation ("<field> is required"), and the
21
+ * model retries the identical malformed call until it gives up — the empty-
22
+ * input retry loop. Two common misplacements are rescued so the call can
23
+ * succeed instead:
24
+ *
25
+ * 1. `input` passed as a JSON-encoded string instead of an object.
26
+ * 2. Parameters spread as top-level siblings of `tool`/`activity`, with `input`
27
+ * absent or an empty object.
28
+ */
29
+ export function resolveSkillExecuteInput(
30
+ envelope: Record<string, unknown>,
31
+ ): Record<string, unknown> {
32
+ const raw = envelope.input;
33
+
34
+ if (raw != null && typeof raw === "object" && !Array.isArray(raw)) {
35
+ const obj = raw as Record<string, unknown>;
36
+ // A populated object is the well-formed case. An empty object falls
37
+ // through to sibling rescue: the model may have nested nothing yet placed
38
+ // the real parameters at the top level.
39
+ if (Object.keys(obj).length > 0) return obj;
40
+ } else if (typeof raw === "string" && raw.trim()) {
41
+ try {
42
+ const parsed: unknown = JSON.parse(raw);
43
+ if (
44
+ parsed != null &&
45
+ typeof parsed === "object" &&
46
+ !Array.isArray(parsed)
47
+ ) {
48
+ return parsed as Record<string, unknown>;
49
+ }
50
+ } catch {
51
+ // Not JSON — fall through to sibling rescue.
52
+ }
53
+ }
54
+
55
+ const siblings = Object.fromEntries(
56
+ Object.entries(envelope).filter(
57
+ ([key]) => !SKILL_EXECUTE_ENVELOPE_KEYS.has(key),
58
+ ),
59
+ );
60
+ if (Object.keys(siblings).length > 0) return siblings;
61
+
62
+ return {};
63
+ }
64
+
9
65
  export const skillExecuteTool = {
10
66
  name: "skill_execute",
11
67
  description:
@@ -200,7 +200,7 @@ export const shellTool = {
200
200
  return {
201
201
  content: `Error: unknown credential reference(s): ${unresolvedRefs.join(
202
202
  ", ",
203
- )}. Use credential_store list to see available credentials.`,
203
+ )}. Use \`assistant credentials list\` to see available credentials.`,
204
204
  isError: true,
205
205
  };
206
206
  }
@@ -35,6 +35,22 @@ function proxyExecute(toolName: string) {
35
35
  input: Record<string, unknown>,
36
36
  context: ToolContext,
37
37
  ): Promise<ToolExecutionResult> => {
38
+ if (toolName === "ui_show" && isEmptyDynamicPage(input)) {
39
+ return {
40
+ content:
41
+ "Error: ui_show dynamic_page requires non-empty HTML in `data.html`. The surface was not displayed because no content was provided — the user would see a blank box. Resend ui_show with the full HTML markup in `data.html`.",
42
+ isError: true,
43
+ };
44
+ }
45
+
46
+ if (toolName === "ui_show" && isEmptyCard(input)) {
47
+ return {
48
+ content:
49
+ "Error: ui_show card requires content — provide `data.body`, a `template` (e.g. task_progress with steps), `data.metadata`, or `actions`. The surface was not displayed because it carried only a title, which renders as a blank box. Resend ui_show with populated card content.",
50
+ isError: true,
51
+ };
52
+ }
53
+
38
54
  if (toolName === "ui_show" && isDynamicPageAppSubstitute(input)) {
39
55
  return {
40
56
  content:
@@ -49,10 +65,83 @@ function proxyExecute(toolName: string) {
49
65
  isError: true,
50
66
  };
51
67
  }
52
- return context.proxyToolResolver(toolName, input);
68
+ const result = await context.proxyToolResolver(toolName, input);
69
+ if (
70
+ toolName === "ui_show" &&
71
+ !result.isError &&
72
+ typeof result.content === "string" &&
73
+ isTaskProgressCardShow(input)
74
+ ) {
75
+ return {
76
+ ...result,
77
+ content: `${result.content}\n\n${TASK_PROGRESS_UPDATE_HINT}`,
78
+ };
79
+ }
80
+ return result;
53
81
  };
54
82
  }
55
83
 
84
+ /**
85
+ * Worked ui_update example, appended to a successful task_progress `ui_show`
86
+ * result so the model learns the update pattern at the point of use (with the
87
+ * real surface_id in hand) rather than carrying it in the always-present tool
88
+ * description.
89
+ */
90
+ const TASK_PROGRESS_UPDATE_HINT =
91
+ 'As each step finishes, call ui_update with this surface_id to advance it — e.g. ui_update { surface_id: "<the surface_id above>", data: { templateData: { steps: [{ label: "Scaffold project", status: "completed" }, { label: "Wire up commands", status: "in_progress" }] } } }';
92
+
93
+ function isTaskProgressCardShow(input: Record<string, unknown>): boolean {
94
+ if (input.template === "task_progress") {
95
+ return true;
96
+ }
97
+ const data = asRecord(input.data);
98
+ return data?.template === "task_progress";
99
+ }
100
+
101
+ function isEmptyDynamicPage(input: Record<string, unknown>): boolean {
102
+ if (input.surface_type !== "dynamic_page") {
103
+ return false;
104
+ }
105
+ const data = asRecord(input.data);
106
+ const html = data?.html;
107
+ return typeof html !== "string" || html.trim().length === 0;
108
+ }
109
+
110
+ /**
111
+ * A `card` ui_show carrying no renderable content — only a title (or nothing)
112
+ * — renders as a blank bordered box. A declared `template` (task_progress,
113
+ * weather_forecast, …) renders its own shell, and `body`/`subtitle`/`metadata`/
114
+ * `actions` are real content; any of those passes. The model places these
115
+ * either nested in `data` or at the top level, so both are checked. Title is
116
+ * intentionally not content: a title-only card is the blank box.
117
+ */
118
+ function isEmptyCard(input: Record<string, unknown>): boolean {
119
+ if (input.surface_type !== "card") {
120
+ return false;
121
+ }
122
+ const data = asRecord(input.data) ?? {};
123
+
124
+ const template =
125
+ nonEmptyString(input.template) ?? nonEmptyString(data.template);
126
+ if (template) {
127
+ return false;
128
+ }
129
+
130
+ const hasBody = !!(nonEmptyString(input.body) ?? nonEmptyString(data.body));
131
+ const hasSubtitle = !!nonEmptyString(data.subtitle);
132
+ const hasMetadata = Array.isArray(data.metadata) && data.metadata.length > 0;
133
+ const actions = input.actions ?? data.actions;
134
+ const hasActions = Array.isArray(actions) && actions.length > 0;
135
+
136
+ return !(hasBody || hasSubtitle || hasMetadata || hasActions);
137
+ }
138
+
139
+ function nonEmptyString(value: unknown): string | undefined {
140
+ return typeof value === "string" && value.trim().length > 0
141
+ ? value
142
+ : undefined;
143
+ }
144
+
56
145
  function isDynamicPageAppSubstitute(input: Record<string, unknown>): boolean {
57
146
  if (input.surface_type !== "dynamic_page") {
58
147
  return false;
@@ -137,7 +226,7 @@ export const uiShowTool = {
137
226
  "- file_upload: { prompt, acceptedTypes?, maxFiles? }\n" +
138
227
  "- task_preferences: {} (no data needed — categories are rendered client-side)\n" +
139
228
  '- work_result: { eyebrow?, status?: "completed"|"partial"|"failed"|"in_progress", summary?, metrics?: [{ label, value, detail?, tone?: "neutral"|"positive"|"warning"|"negative" }], sections?: [{ id?, title, description?, type?: "items"|"timeline"|"diff"|"artifacts"|"warnings", items?: [{ id?, title, description?, status?, tone?, metadata?: [{ label, value }], href? }], diffs?: [{ label?, before?, after? }] }] }. Shows a structured receipt after real work: what changed, what was skipped, proof points, and next actions. Keep display-only unless explicit follow-up buttons are needed.\n\n' +
140
- "Emit a task_progress card BEFORE your first tool call on any multi-step, long-running, or post-form turn (web searches, file operations, research), and keep its steps updated as work progresses. Skills that estimate >30s of work should declare progress upfront with concrete step labels (and counts when known).",
229
+ "For multi-step or long-running turns (web searches, file operations, research), show a task_progress card early and keep its steps updated as work progresses. Coarse steps are fine, and you can add or revise them as the work takes shape a rough card beats no signal.",
141
230
  category: "ui-surface",
142
231
  defaultRiskLevel: RiskLevel.Low,
143
232
  executionTarget: "host",
@@ -0,0 +1,183 @@
1
+ /**
2
+ * `manage_workflows` core implementation — thin reads/controls over
3
+ * {@link WorkflowRunManager}: check a run's status, abort a run, or list recent
4
+ * runs. All state lives in the run manager / journal; this delegates and
5
+ * compacts the result to JSON.
6
+ *
7
+ * NOTE: this runs in-process and returns a `runId`-keyed, deliberately trimmed
8
+ * projection for the model — a DIFFERENT contract from the HTTP wire shape
9
+ * (`id`-keyed `toWireRun`/`workflowRunSchema` in
10
+ * `runtime/routes/workflow-routes.ts`). The two are intentionally not unified:
11
+ * converging on `toWireRun` here would change this tool's emitted JSON. Both
12
+ * project from the same `WorkflowRun` source type, so field renames are caught
13
+ * by the type checker.
14
+ */
15
+
16
+ import { loadConfig } from "../../config/loader.js";
17
+ import { callerOwnsWorkflowRun } from "../../workflows/capabilities.js";
18
+ import type { WorkflowRun } from "../../workflows/journal-store.js";
19
+ import { getWorkflowRunManager } from "../../workflows/run-manager.js";
20
+ import type { ToolContext, ToolExecutionResult } from "../types.js";
21
+
22
+ export async function executeManageWorkflows(
23
+ input: Record<string, unknown>,
24
+ context: ToolContext,
25
+ ): Promise<ToolExecutionResult> {
26
+ const action = input.action as string | undefined;
27
+ const runId = input.run_id as string | undefined;
28
+ const manager = getWorkflowRunManager();
29
+
30
+ // Authorization scope ({@link callerOwnsWorkflowRun}, shared with the
31
+ // executor's resume-approval gate): a guardian may inspect/control every run,
32
+ // but a non-guardian conversation is limited to runs IT originated. Without
33
+ // this, a contact could enumerate a guardian's workflows (names/statuses) via
34
+ // list_runs and then abort/resume them by id — the tool is low-risk and
35
+ // reachable in non-guardian conversations. A run the caller may not see is
36
+ // treated as not-found so the tool never reveals another conversation's run.
37
+ const ownsRun = (run: WorkflowRun): boolean =>
38
+ callerOwnsWorkflowRun(run, context);
39
+
40
+ switch (action) {
41
+ case "status": {
42
+ if (!runId) {
43
+ return {
44
+ content: '"run_id" is required for action "status".',
45
+ isError: true,
46
+ };
47
+ }
48
+ const run = manager.status(runId);
49
+ if (!run || !ownsRun(run)) {
50
+ return {
51
+ content: JSON.stringify({ runId, found: false }),
52
+ isError: false,
53
+ };
54
+ }
55
+ return {
56
+ content: JSON.stringify({
57
+ runId: run.id,
58
+ name: run.name,
59
+ status: run.status,
60
+ agentsSpawned: run.agentsSpawned,
61
+ inputTokens: run.inputTokens,
62
+ outputTokens: run.outputTokens,
63
+ error: run.error,
64
+ }),
65
+ isError: false,
66
+ };
67
+ }
68
+ case "get_result": {
69
+ if (!runId) {
70
+ return {
71
+ content: '"run_id" is required for action "get_result".',
72
+ isError: true,
73
+ };
74
+ }
75
+ // The full result payload rides on the run record (journal getRun);
76
+ // `status` omits it to stay lightweight, so this action returns it in
77
+ // full. The completion-summary wake truncates large results and points
78
+ // the assistant here for the complete value.
79
+ const run = manager.status(runId);
80
+ if (!run || !ownsRun(run)) {
81
+ return {
82
+ content: JSON.stringify({ runId, found: false }),
83
+ isError: false,
84
+ };
85
+ }
86
+ return {
87
+ content: JSON.stringify({
88
+ runId: run.id,
89
+ status: run.status,
90
+ result: run.result ?? null,
91
+ error: run.error ?? null,
92
+ }),
93
+ isError: false,
94
+ };
95
+ }
96
+ case "abort": {
97
+ if (!runId) {
98
+ return {
99
+ content: '"run_id" is required for action "abort".',
100
+ isError: true,
101
+ };
102
+ }
103
+ // Only signal a run the caller owns. A non-owned (or absent) run is a
104
+ // no-op with the same response shape, so existence is never revealed.
105
+ const run = manager.status(runId);
106
+ if (run && ownsRun(run)) manager.abort(runId);
107
+ return {
108
+ content: JSON.stringify({
109
+ runId,
110
+ message: "Abort signalled (no-op if the run already finished).",
111
+ }),
112
+ isError: false,
113
+ };
114
+ }
115
+ case "resume": {
116
+ if (!runId) {
117
+ return {
118
+ content: '"run_id" is required for action "resume".',
119
+ isError: true,
120
+ };
121
+ }
122
+ // Don't resume — or reveal — a run the caller doesn't own; mirror
123
+ // resume()'s own not-found message.
124
+ const run = manager.status(runId);
125
+ if (!run || !ownsRun(run)) {
126
+ return {
127
+ content: `Failed to resume workflow: Workflow run ${runId} not found.`,
128
+ isError: true,
129
+ };
130
+ }
131
+ try {
132
+ const { runId: resumedId } = manager.resume(runId);
133
+ return {
134
+ content: JSON.stringify({
135
+ runId: resumedId,
136
+ status: "running",
137
+ message:
138
+ "Workflow resumed. The completed prefix is replayed from the journal and the run continues from the first unfinished step. You will be notified in this conversation when it completes — do NOT poll.",
139
+ }),
140
+ isError: false,
141
+ };
142
+ } catch (err) {
143
+ const msg = err instanceof Error ? err.message : String(err);
144
+ return { content: `Failed to resume workflow: ${msg}`, isError: true };
145
+ }
146
+ }
147
+ case "list_runs": {
148
+ const runs = manager.list().filter(ownsRun);
149
+ return {
150
+ content: JSON.stringify({
151
+ runs: runs.map((r) => ({
152
+ runId: r.id,
153
+ name: r.name,
154
+ status: r.status,
155
+ agentsSpawned: r.agentsSpawned,
156
+ })),
157
+ }),
158
+ isError: false,
159
+ };
160
+ }
161
+ case "list_profiles": {
162
+ // Mirror the `config/llm/profiles` route: sorted profile names plus the
163
+ // workspace-wide active profile. Read-only — leaves use this to pick a
164
+ // valid `profile` for `run_workflow` (an unknown profile throws).
165
+ const { llm } = loadConfig();
166
+ const profiles = llm?.profiles ?? {};
167
+ return {
168
+ content: JSON.stringify({
169
+ profiles: Object.keys(profiles).sort(),
170
+ activeProfile:
171
+ typeof llm?.activeProfile === "string" ? llm.activeProfile : null,
172
+ }),
173
+ isError: false,
174
+ };
175
+ }
176
+ default:
177
+ return {
178
+ content:
179
+ 'Unknown action. Use one of: "status", "get_result", "abort", "resume", "list_runs", "list_profiles".',
180
+ isError: true,
181
+ };
182
+ }
183
+ }