@vellumai/assistant 0.9.0 → 0.10.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 (572) hide show
  1. package/ARCHITECTURE.md +18 -34
  2. package/bun.lock +7 -8
  3. package/docs/activation-funnel-telemetry.md +28 -22
  4. package/docs/architecture/security.md +29 -28
  5. package/docs/stt-provider-onboarding.md +3 -5
  6. package/docs/workflows-testing.md +13 -44
  7. package/docs/workflows.md +3 -5
  8. package/node_modules/@vellumai/ces-client/src/__tests__/ces-client.test.ts +47 -0
  9. package/node_modules/@vellumai/ces-client/src/rpc-client.ts +28 -5
  10. package/node_modules/@vellumai/environments/src/seeds.ts +2 -5
  11. package/node_modules/@vellumai/gateway-client/src/admission-policy-contract.ts +97 -0
  12. package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +10 -0
  13. package/node_modules/@vellumai/gateway-client/src/index.ts +32 -6
  14. package/node_modules/@vellumai/gateway-client/src/outbound-contract.ts +119 -0
  15. package/node_modules/@vellumai/gateway-client/src/types.ts +15 -84
  16. package/openapi.yaml +976 -63
  17. package/package.json +2 -1
  18. package/scripts/sync-llm-catalog.ts +6 -15
  19. package/scripts/sync-web-search-catalog.ts +3 -11
  20. package/src/__tests__/access-request-card-view.test.ts +98 -0
  21. package/src/__tests__/access-request-seed-content-blocks.test.ts +2 -4
  22. package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +72 -32
  23. package/src/__tests__/agent-loop-compaction-strip.test.ts +241 -0
  24. package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +16 -13
  25. package/src/__tests__/agent-loop-output-hooks.test.ts +69 -0
  26. package/src/__tests__/agent-loop-override-profile.test.ts +25 -0
  27. package/src/__tests__/always-loaded-tools-guard.test.ts +2 -3
  28. package/src/__tests__/app-compiler.test.ts +15 -1
  29. package/src/__tests__/app-dir-path-guard.test.ts +0 -1
  30. package/src/__tests__/assistant-feature-flag-guard.test.ts +1 -4
  31. package/src/__tests__/assistant-feature-flag-guardrails.test.ts +0 -2
  32. package/src/__tests__/auth-fallback-events-store.test.ts +6 -14
  33. package/src/__tests__/avatar-identity-sync.test.ts +2 -27
  34. package/src/__tests__/btw-routes.test.ts +6 -8
  35. package/src/__tests__/call-pointer-messages.test.ts +28 -0
  36. package/src/__tests__/cancel-clears-processing.test.ts +89 -0
  37. package/src/__tests__/channel-approval-routes.test.ts +0 -4
  38. package/src/__tests__/channel-inbound-disk-pressure.test.ts +5 -15
  39. package/src/__tests__/checker.test.ts +0 -3
  40. package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +3 -4
  41. package/src/__tests__/compactor-image-manifest-trust.test.ts +21 -1
  42. package/src/__tests__/compactor-summary-call-truncation.test.ts +223 -0
  43. package/src/__tests__/config-loader-backfill.test.ts +268 -27
  44. package/src/__tests__/config-schema.test.ts +35 -0
  45. package/src/__tests__/config-watcher.test.ts +0 -18
  46. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +2 -2
  47. package/src/__tests__/contact-store-user-file.test.ts +0 -6
  48. package/src/__tests__/contacts-tools.test.ts +29 -0
  49. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +22 -0
  50. package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -0
  51. package/src/__tests__/conversation-agent-loop.test.ts +58 -0
  52. package/src/__tests__/conversation-attention-telegram.test.ts +0 -1
  53. package/src/__tests__/conversation-lifecycle.test.ts +7 -9
  54. package/src/__tests__/conversation-load-history-repair.test.ts +101 -0
  55. package/src/__tests__/conversation-routes-guardian-reply.test.ts +15 -12
  56. package/src/__tests__/conversation-surfaces-activation-emit.test.ts +6 -3
  57. package/src/__tests__/conversation-title-service.test.ts +62 -0
  58. package/src/__tests__/credential-broker.test.ts +449 -1
  59. package/src/__tests__/credential-execution-shell-lockdown.test.ts +18 -11
  60. package/src/__tests__/credential-execution-tools.test.ts +0 -1
  61. package/src/__tests__/credential-prompt-route.test.ts +4 -4
  62. package/src/__tests__/credential-routes.test.ts +360 -0
  63. package/src/__tests__/credential-security-invariants.test.ts +4 -13
  64. package/src/__tests__/disk-pressure-policy.test.ts +12 -0
  65. package/src/__tests__/disk-usage.test.ts +65 -0
  66. package/src/__tests__/dynamic-page-surface.test.ts +152 -1
  67. package/src/__tests__/fixtures/credential-security-fixtures.ts +2 -33
  68. package/src/__tests__/gateway-flag-listener.test.ts +110 -1
  69. package/src/__tests__/gateway-only-guard.test.ts +3 -7
  70. package/src/__tests__/guardian-binding-drift-heal.test.ts +1 -1
  71. package/src/__tests__/guardian-card-withdrawal.test.ts +403 -0
  72. package/src/__tests__/guardian-decision-primitive-canonical.test.ts +5 -3
  73. package/src/__tests__/guardian-grant-minting.test.ts +3 -35
  74. package/src/__tests__/guardian-routing-invariants.test.ts +64 -26
  75. package/src/__tests__/guardian-routing-state.test.ts +0 -1
  76. package/src/__tests__/headless-browser-mode.test.ts +10 -0
  77. package/src/__tests__/headless-browser-navigate.test.ts +8 -3
  78. package/src/__tests__/helpers/create-guardian-binding.ts +0 -1
  79. package/src/__tests__/host-browser-proxy.test.ts +87 -0
  80. package/src/__tests__/identity-routes.test.ts +0 -189
  81. package/src/__tests__/inbound-invite-redemption.test.ts +4 -4
  82. package/src/__tests__/injector-v3-suppression.test.ts +27 -20
  83. package/src/__tests__/internal-telemetry-routes.test.ts +6 -14
  84. package/src/__tests__/invite-redemption-service.test.ts +4 -7
  85. package/src/__tests__/llm-callsite-catalog.test.ts +5 -6
  86. package/src/__tests__/llm-catalog-parity.test.ts +30 -23
  87. package/src/__tests__/llm-resolver.test.ts +70 -24
  88. package/src/__tests__/llm-schema.test.ts +1 -0
  89. package/src/__tests__/managed-profile-guard.test.ts +163 -4
  90. package/src/__tests__/mcp-health-check.test.ts +6 -7
  91. package/src/__tests__/media-stream-server-integration.test.ts +317 -13
  92. package/src/__tests__/oauth-provider-seed-logos.test.ts +4 -6
  93. package/src/__tests__/onboarding-persona-write.test.ts +1 -1
  94. package/src/__tests__/path-policy.test.ts +34 -0
  95. package/src/__tests__/persona-resolver.test.ts +49 -14
  96. package/src/__tests__/plugin-api-model-profiles.test.ts +178 -0
  97. package/src/__tests__/plugin-api-provider.test.ts +24 -0
  98. package/src/__tests__/plugin-tool-contribution.test.ts +6 -3
  99. package/src/__tests__/post-compaction-reinjection-idempotency.test.ts +214 -0
  100. package/src/__tests__/provider-send-message-override-profile.test.ts +76 -0
  101. package/src/__tests__/reaction-persistence.test.ts +150 -29
  102. package/src/__tests__/registry.test.ts +2 -7
  103. package/src/__tests__/relay-server.test.ts +285 -0
  104. package/src/__tests__/runtime-attachment-metadata.test.ts +0 -1
  105. package/src/__tests__/schedule-routes-workflow-validation.test.ts +1 -10
  106. package/src/__tests__/schedule-routes.test.ts +0 -30
  107. package/src/__tests__/schedule-tools.test.ts +2 -18
  108. package/src/__tests__/scheduler-reuse-conversation.test.ts +8 -5
  109. package/src/__tests__/skill-execute-input.test.ts +51 -1
  110. package/src/__tests__/skill-runtime-path.test.ts +2 -3
  111. package/src/__tests__/skills.test.ts +51 -0
  112. package/src/__tests__/slack-notification-approval-card.test.ts +176 -0
  113. package/src/__tests__/slack-reaction-canonical-approval.test.ts +285 -0
  114. package/src/__tests__/subagent-tools.test.ts +266 -0
  115. package/src/__tests__/surface-completion-nudge-hook.test.ts +367 -0
  116. package/src/__tests__/task-progress-nudge-hook.test.ts +1 -1
  117. package/src/__tests__/title-generate-hook.test.ts +100 -3
  118. package/src/__tests__/token-estimator-accuracy.benchmark.test.ts +1 -29
  119. package/src/__tests__/token-manager.test.ts +519 -0
  120. package/src/__tests__/tool-approval-seed-content-blocks.test.ts +1 -1
  121. package/src/__tests__/tool-audit-listener.test.ts +7 -7
  122. package/src/__tests__/tool-executor-lifecycle-events.test.ts +6 -3
  123. package/src/__tests__/tool-executor.test.ts +0 -79
  124. package/src/__tests__/trusted-contact-approval-notifier.test.ts +4 -2
  125. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +220 -3
  126. package/src/__tests__/trusted-contact-multichannel.test.ts +3 -3
  127. package/src/__tests__/trusted-contact-verification.test.ts +8 -10
  128. package/src/__tests__/twilio-routes.test.ts +81 -1
  129. package/src/__tests__/voice-invite-redemption.test.ts +2 -3
  130. package/src/__tests__/weak-open-model.test.ts +30 -0
  131. package/src/__tests__/web-search-catalog-parity.test.ts +6 -25
  132. package/src/__tests__/workspace-greetings.test.ts +152 -0
  133. package/src/__tests__/workspace-migration-105-enable-memory-v3-live-for-new-workspaces.test.ts +149 -0
  134. package/src/__tests__/workspace-migration-108-drop-balanced-economy-profile.test.ts +285 -0
  135. package/src/__tests__/workspace-migration-add-send-diagnostics.test.ts +1 -1
  136. package/src/__tests__/workspace-migration-drop-collect-usage-data.test.ts +118 -0
  137. package/src/__tests__/workspace-migration-drop-send-diagnostics.test.ts +118 -0
  138. package/src/a2a/__tests__/e2e-a2a-channel.test.ts +0 -4
  139. package/src/agent/loop.ts +49 -29
  140. package/src/api/README.md +6 -6
  141. package/src/api/events/tool-result.ts +6 -0
  142. package/src/api/events/workflow-completed.ts +53 -0
  143. package/src/api/events/workflow-leaf-finished.ts +38 -0
  144. package/src/api/events/workflow-leaf-started.ts +35 -0
  145. package/src/api/events/workflow-progress.ts +32 -0
  146. package/src/api/events/workflow-started.ts +31 -0
  147. package/src/api/index.ts +40 -0
  148. package/src/api/responses/conversation-message.ts +28 -4
  149. package/src/api/responses/home.ts +26 -4
  150. package/src/api/responses/workflow-journal.ts +53 -0
  151. package/src/approvals/guardian-card-withdrawal.ts +145 -0
  152. package/src/approvals/guardian-decision-primitive.ts +26 -3
  153. package/src/approvals/guardian-request-resolvers.ts +183 -80
  154. package/src/calls/__tests__/channel-admission-reader.test.ts +132 -0
  155. package/src/calls/__tests__/relay-setup-router.test.ts +350 -0
  156. package/src/calls/call-pointer-messages.ts +10 -4
  157. package/src/calls/channel-admission-reader.ts +104 -0
  158. package/src/calls/guardian-dispatch.ts +17 -45
  159. package/src/calls/media-stream-server.ts +84 -2
  160. package/src/calls/relay-access-wait.ts +1 -1
  161. package/src/calls/relay-server.ts +66 -0
  162. package/src/calls/relay-setup-router.ts +82 -1
  163. package/src/calls/twilio-routes.ts +17 -8
  164. package/src/calls/voice-session-bridge.ts +2 -2
  165. package/src/cli/commands/clients.ts +3 -0
  166. package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v2-compare-render.test.ts +1 -1
  167. package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v2.test.ts +8 -7
  168. package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v3.test.ts +5 -4
  169. package/src/cli/commands/memory/index.ts +30 -0
  170. package/src/cli/commands/{memory-v2-compare-render.ts → memory/memory-v2-compare-render.ts} +1 -1
  171. package/src/cli/commands/{memory-v2.ts → memory/memory-v2.ts} +6 -15
  172. package/src/cli/commands/{memory-v3.ts → memory/memory-v3.ts} +97 -11
  173. package/src/cli/commands/oauth/status.test.ts +36 -0
  174. package/src/cli/commands/oauth/status.ts +23 -3
  175. package/src/cli/commands/plugins.ts +197 -4
  176. package/src/cli/lib/__tests__/diff-plugin.test.ts +443 -0
  177. package/src/cli/lib/__tests__/inspect-plugin.test.ts +54 -0
  178. package/src/cli/lib/__tests__/merge-plugin-tree.test.ts +443 -0
  179. package/src/cli/lib/__tests__/plugin-surfaces.test.ts +111 -0
  180. package/src/cli/lib/__tests__/upgrade-plugin.test.ts +295 -2
  181. package/src/cli/lib/diff-plugin.ts +346 -0
  182. package/src/cli/lib/inspect-plugin.ts +12 -1
  183. package/src/cli/lib/install-from-github.ts +105 -17
  184. package/src/cli/lib/merge-plugin-tree.ts +328 -0
  185. package/src/cli/lib/plugin-fingerprint.ts +14 -0
  186. package/src/cli/lib/plugin-surfaces.ts +104 -0
  187. package/src/cli/lib/upgrade-plugin.ts +298 -10
  188. package/src/cli/program.ts +2 -6
  189. package/src/config/__tests__/sync-gated-profiles.test.ts +368 -0
  190. package/src/config/assistant-feature-flags.ts +22 -7
  191. package/src/config/bundled-skills/contacts/tools/contact-search.ts +0 -1
  192. package/src/config/bundled-skills/messaging/SKILL.md +6 -4
  193. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +9 -8
  194. package/src/config/bundled-skills/subagent/SKILL.md +4 -0
  195. package/src/config/bundled-skills/subagent/TOOLS.json +4 -0
  196. package/src/config/bundled-skills/workflows/SKILL.md +14 -8
  197. package/src/config/bundled-tool-registry.ts +2 -7
  198. package/src/config/call-site-defaults.ts +15 -2
  199. package/src/config/feature-flag-registry.json +46 -31
  200. package/src/config/inference-profile-validation.ts +26 -0
  201. package/src/config/llm-resolver.ts +3 -0
  202. package/src/config/loader.ts +4 -0
  203. package/src/config/memory-v3-gate.ts +11 -0
  204. package/src/config/profile-order.ts +28 -0
  205. package/src/config/schema.ts +8 -6
  206. package/src/config/schemas/__tests__/memory-v3.test.ts +1 -0
  207. package/src/config/schemas/call-site-catalog.ts +7 -0
  208. package/src/config/schemas/channels.ts +11 -0
  209. package/src/config/schemas/elevenlabs.ts +0 -1
  210. package/src/config/schemas/llm.ts +31 -0
  211. package/src/config/schemas/memory-lifecycle.ts +3 -7
  212. package/src/config/schemas/memory-v3.ts +6 -0
  213. package/src/config/schemas/platform.ts +0 -8
  214. package/src/config/schemas/services.ts +18 -0
  215. package/src/config/seed-inference-profiles.ts +109 -44
  216. package/src/config/skills.ts +21 -0
  217. package/src/config/sync-gated-profiles.ts +220 -0
  218. package/src/contacts/contact-store.ts +89 -106
  219. package/src/contacts/contacts-write.ts +5 -22
  220. package/src/contacts/types.ts +0 -1
  221. package/src/context/compactor.ts +88 -54
  222. package/src/context/strip-injections.ts +58 -10
  223. package/src/context/token-estimator.ts +1 -1
  224. package/src/credential-execution/process-manager.ts +55 -14
  225. package/src/credential-execution/prompted-credential.ts +2 -3
  226. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -2
  227. package/src/daemon/config-watcher.ts +0 -4
  228. package/src/daemon/conversation-agent-loop-handlers.ts +2 -0
  229. package/src/daemon/conversation-agent-loop.ts +114 -22
  230. package/src/daemon/conversation-history.ts +1 -1
  231. package/src/daemon/conversation-lifecycle.ts +3 -5
  232. package/src/daemon/conversation-process.ts +13 -5
  233. package/src/daemon/conversation-runtime-assembly.ts +13 -15
  234. package/src/daemon/conversation-slash.ts +2 -23
  235. package/src/daemon/conversation-surfaces.ts +26 -0
  236. package/src/daemon/conversation-tool-setup.ts +27 -14
  237. package/src/daemon/conversation.ts +66 -14
  238. package/src/daemon/disk-pressure-policy.ts +5 -3
  239. package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +0 -1
  240. package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +0 -1
  241. package/src/daemon/handlers/config-a2a.ts +0 -2
  242. package/src/daemon/handlers/config-channels.ts +15 -16
  243. package/src/daemon/handlers/config-slack-channel.ts +22 -3
  244. package/src/daemon/handlers/conversations.ts +107 -0
  245. package/src/daemon/host-browser-proxy.ts +41 -0
  246. package/src/daemon/lifecycle.ts +55 -27
  247. package/src/daemon/message-provenance.ts +2 -0
  248. package/src/daemon/message-types/contacts.ts +0 -1
  249. package/src/daemon/message-types/conversations.ts +3 -3
  250. package/src/daemon/message-types/sync.ts +0 -1
  251. package/src/daemon/message-types/web-activity.ts +7 -1
  252. package/src/daemon/message-types/workflows.ts +83 -1
  253. package/src/daemon/orphan-reaper.test.ts +0 -19
  254. package/src/daemon/orphan-reaper.ts +2 -24
  255. package/src/daemon/server.ts +0 -10
  256. package/src/daemon/tool-setup-types.ts +4 -0
  257. package/src/daemon/trust-context.ts +1 -1
  258. package/src/events/tool-audit-listener.ts +2 -2
  259. package/src/home/feed-source-enrichment.test.ts +151 -0
  260. package/src/home/feed-source-enrichment.ts +176 -0
  261. package/src/home/relationship-state.ts +2 -4
  262. package/src/instrument.ts +18 -6
  263. package/src/ipc/__tests__/binary-result-ipc.test.ts +81 -0
  264. package/src/ipc/__tests__/clients-list-ipc.test.ts +20 -0
  265. package/src/ipc/assistant-server.ts +37 -4
  266. package/src/ipc/gateway-flag-listener.ts +18 -2
  267. package/src/memory/__tests__/auto-analysis-enqueue.test.ts +5 -16
  268. package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +7 -11
  269. package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +37 -7
  270. package/src/memory/__tests__/memory-retrospective-job.test.ts +229 -401
  271. package/src/memory/__tests__/onboarding-events-store.test.ts +7 -7
  272. package/src/memory/auth-fallback-events-store.ts +2 -2
  273. package/src/memory/auto-analysis-enqueue.ts +3 -5
  274. package/src/memory/bookmark-crud.ts +1 -2
  275. package/src/memory/canonical-guardian-store.ts +39 -1
  276. package/src/memory/conversation-crud.ts +9 -4
  277. package/src/memory/conversation-key-store.ts +17 -2
  278. package/src/memory/conversation-title-service.ts +64 -7
  279. package/src/memory/db-init.ts +17 -17
  280. package/src/memory/embedding-backend.ts +38 -1
  281. package/src/memory/embedding-billing-breaker.ts +96 -0
  282. package/src/memory/jobs-store.ts +25 -13
  283. package/src/memory/jobs-worker.ts +54 -1
  284. package/src/memory/lifecycle-events-store.ts +2 -2
  285. package/src/memory/memory-retrospective-constants.ts +4 -4
  286. package/src/memory/memory-retrospective-enqueue.ts +31 -6
  287. package/src/memory/memory-retrospective-job.ts +28 -227
  288. package/src/memory/migrations/129-contact-channels-access-fields.ts +18 -9
  289. package/src/memory/migrations/131-drop-legacy-member-guardian-tables.ts +14 -2
  290. package/src/memory/migrations/289-contact-channels-unique-ext-user.ts +10 -0
  291. package/src/memory/migrations/291-contact-channels-renormalize-addresses.ts +72 -0
  292. package/src/memory/migrations/292-schedule-default-no-reuse-conversation.test.ts +67 -0
  293. package/src/memory/migrations/292-schedule-default-no-reuse-conversation.ts +25 -0
  294. package/src/memory/migrations/293-workflow-journal-leaf-tokens.ts +32 -0
  295. package/src/memory/migrations/294-drop-external-user-id.ts +31 -0
  296. package/src/memory/migrations/295-drop-approval-prompt-ts-tracker.ts +20 -0
  297. package/src/memory/migrations/296-rewrite-balanced-economy-profile-pins.test.ts +110 -0
  298. package/src/memory/migrations/296-rewrite-balanced-economy-profile-pins.ts +68 -0
  299. package/src/memory/migrations/__tests__/131-drop-legacy-member-guardian-tables.test.ts +154 -0
  300. package/src/memory/migrations/__tests__/289-contact-channels-unique-ext-user.test.ts +31 -0
  301. package/src/memory/migrations/__tests__/291-contact-channels-renormalize-addresses.test.ts +341 -0
  302. package/src/memory/migrations/__tests__/run-migrations.test.ts +52 -0
  303. package/src/memory/migrations/index.ts +6 -0
  304. package/src/memory/migrations/run-migrations.ts +41 -0
  305. package/src/memory/migrations/validate-migration-state.ts +1 -1
  306. package/src/memory/onboarding-events-store.ts +3 -3
  307. package/src/memory/schema/contacts.ts +0 -5
  308. package/src/memory/skill-loaded-events-store.test.ts +7 -15
  309. package/src/memory/skill-loaded-events-store.ts +2 -2
  310. package/src/memory/tool-executed-events-store.test.ts +7 -7
  311. package/src/memory/turn-trace-store.test.ts +736 -0
  312. package/src/memory/turn-trace-store.ts +364 -0
  313. package/src/memory/v2/__tests__/consolidation-job.test.ts +8 -0
  314. package/src/memory/v2/__tests__/skill-content.test.ts +30 -0
  315. package/src/memory/v2/consolidation-job.ts +2 -2
  316. package/src/memory/v2/skill-content.ts +25 -7
  317. package/src/memory/v2/skill-store.ts +7 -1
  318. package/src/memory/v3-eval/__tests__/eval-packets.test.ts +248 -0
  319. package/src/memory/v3-eval/eval-packets.ts +546 -0
  320. package/src/messaging/providers/slack/adapter.ts +1 -1
  321. package/src/messaging/providers/slack/api.ts +31 -0
  322. package/src/messaging/providers/slack/send.test.ts +114 -2
  323. package/src/messaging/providers/slack/send.ts +30 -7
  324. package/src/messaging/providers/slack/withdraw.test.ts +200 -0
  325. package/src/messaging/providers/slack/withdraw.ts +161 -0
  326. package/src/notifications/AGENTS.md +2 -0
  327. package/src/notifications/access-request-copy.ts +72 -59
  328. package/src/notifications/adapters/shared.ts +29 -0
  329. package/src/notifications/adapters/slack.ts +58 -103
  330. package/src/notifications/adapters/telegram.ts +2 -20
  331. package/src/notifications/approval-card-data.ts +333 -0
  332. package/src/notifications/broadcaster.ts +16 -3
  333. package/src/notifications/canonical-delivery-recorder.ts +139 -0
  334. package/src/notifications/copy-composer.ts +3 -3
  335. package/src/notifications/decision-engine.ts +4 -2
  336. package/src/notifications/destination-resolver.ts +4 -6
  337. package/src/notifications/guardian-question-mode.ts +10 -0
  338. package/src/notifications/home-feed-side-effect.ts +7 -16
  339. package/src/notifications/notification-utils.ts +19 -20
  340. package/src/notifications/signal.ts +79 -43
  341. package/src/notifications/types.ts +98 -121
  342. package/src/oauth/AGENTS.md +5 -24
  343. package/src/permissions/checker.test.ts +51 -0
  344. package/src/permissions/checker.ts +185 -26
  345. package/src/permissions/ipc-risk-types.ts +24 -0
  346. package/src/permissions/question-prompter.test.ts +27 -0
  347. package/src/permissions/question-prompter.ts +4 -0
  348. package/src/platform/client.test.ts +119 -0
  349. package/src/platform/client.ts +66 -0
  350. package/src/platform/consent-cache.test.ts +267 -0
  351. package/src/platform/consent-cache.ts +174 -0
  352. package/src/plugin-api/constants.ts +1 -1
  353. package/src/plugin-api/index.ts +33 -1
  354. package/src/plugin-api/model-profiles.ts +33 -0
  355. package/src/plugin-api/types.ts +50 -2
  356. package/src/plugins/defaults/advisor/__tests__/advisor-gate.test.ts +56 -0
  357. package/src/plugins/defaults/advisor/__tests__/advisor-state-store.test.ts +43 -0
  358. package/src/plugins/defaults/advisor/__tests__/agent-loop-integration.test.ts +137 -0
  359. package/src/plugins/defaults/advisor/__tests__/consult.test.ts +153 -0
  360. package/src/plugins/defaults/advisor/__tests__/hooks.test.ts +138 -0
  361. package/src/plugins/defaults/advisor/__tests__/transcript.test.ts +147 -0
  362. package/src/plugins/defaults/advisor/advisor-gate.ts +29 -0
  363. package/src/plugins/defaults/advisor/advisor-state-store.ts +94 -0
  364. package/src/plugins/defaults/advisor/config.ts +21 -0
  365. package/src/plugins/defaults/advisor/consult.ts +93 -0
  366. package/src/plugins/defaults/advisor/hooks/post-model-call.ts +34 -0
  367. package/src/plugins/defaults/advisor/hooks/pre-model-call.ts +30 -0
  368. package/src/plugins/defaults/advisor/hooks/user-prompt-submit.ts +19 -0
  369. package/src/plugins/defaults/advisor/package.json +14 -0
  370. package/src/plugins/defaults/advisor/steering.ts +67 -0
  371. package/src/plugins/defaults/advisor/tools/advisor.ts +65 -0
  372. package/src/plugins/defaults/advisor/transcript.ts +76 -0
  373. package/src/plugins/defaults/index.ts +60 -0
  374. package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +22 -9
  375. package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +2 -2
  376. package/src/plugins/defaults/memory-retrieval/tail-reinjection-strip.ts +64 -0
  377. package/src/plugins/defaults/memory-retrieval/unified-turn-context.ts +29 -21
  378. package/src/plugins/defaults/memory-v3-shadow/__tests__/carry-integration.test.ts +1 -0
  379. package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +1 -0
  380. package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +129 -9
  381. package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +31 -4
  382. package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +77 -2
  383. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +1 -0
  384. package/src/plugins/defaults/memory-v3-shadow/injector.ts +7 -10
  385. package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +144 -11
  386. package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +32 -20
  387. package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +56 -3
  388. package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +23 -2
  389. package/src/plugins/defaults/surface-completion-nudge/hooks/post-model-call.ts +276 -0
  390. package/src/plugins/defaults/surface-completion-nudge/hooks/stop.ts +22 -0
  391. package/src/plugins/defaults/surface-completion-nudge/nudge-state-store.ts +46 -0
  392. package/src/plugins/defaults/surface-completion-nudge/package.json +14 -0
  393. package/src/plugins/defaults/task-progress-nudge/hooks/post-tool-use.ts +3 -13
  394. package/src/plugins/defaults/title-generate/hooks/stop.ts +56 -21
  395. package/src/prompts/persona-resolver.ts +14 -4
  396. package/src/prompts/templates/system-sections.ts +7 -2
  397. package/src/providers/__tests__/provider-env-vars.test.ts +6 -0
  398. package/src/providers/__tests__/provider-secret-catalog.test.ts +1 -0
  399. package/src/providers/__tests__/retry-callsite.test.ts +176 -0
  400. package/src/providers/atlascloud/client.ts +85 -0
  401. package/src/providers/fetch-provider-catalog.ts +85 -0
  402. package/src/providers/inference/adapter-factory.ts +3 -0
  403. package/src/providers/model-catalog.ts +58 -0
  404. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +33 -0
  405. package/src/providers/openai/chat-completions-provider.ts +7 -0
  406. package/src/providers/openai/responses-provider.ts +10 -0
  407. package/src/providers/provider-send-message.ts +11 -3
  408. package/src/providers/retry.ts +53 -12
  409. package/src/providers/search-provider-catalog.ts +10 -0
  410. package/src/providers/weak-open-model.ts +22 -0
  411. package/src/runtime/AGENTS.md +0 -1
  412. package/src/runtime/__tests__/agent-wake.test.ts +181 -0
  413. package/src/runtime/__tests__/client-health.test.ts +44 -0
  414. package/src/runtime/access-request-helper.ts +21 -53
  415. package/src/runtime/actor-trust-resolver.ts +59 -63
  416. package/src/runtime/agent-wake.ts +52 -0
  417. package/src/runtime/assistant-event-hub.ts +18 -4
  418. package/src/runtime/auth/__tests__/route-policy.test.ts +12 -0
  419. package/src/runtime/auth/require-bound-guardian.ts +1 -4
  420. package/src/runtime/btw-sidechain.ts +3 -6
  421. package/src/runtime/capabilities.test.ts +120 -0
  422. package/src/runtime/capabilities.ts +197 -0
  423. package/src/runtime/channel-approval-types.ts +22 -45
  424. package/src/runtime/channel-invite-transports/telegram.ts +4 -4
  425. package/src/runtime/channel-retry-sweep.ts +1 -0
  426. package/src/runtime/channel-verification-service.ts +3 -3
  427. package/src/runtime/client-health.ts +26 -0
  428. package/src/runtime/confirmation-request-guardian-bridge.ts +38 -29
  429. package/src/runtime/effective-capabilities.test.ts +128 -0
  430. package/src/runtime/effective-capabilities.ts +84 -0
  431. package/src/runtime/guardian-reply-router.ts +106 -21
  432. package/src/runtime/invite-redemption-service.ts +9 -25
  433. package/src/runtime/migrations/__tests__/vbundle-builder-fd-leak.test.ts +123 -0
  434. package/src/runtime/migrations/vbundle-builder.ts +49 -20
  435. package/src/runtime/pending-interactions.ts +15 -0
  436. package/src/runtime/routes/__tests__/client-routes.test.ts +13 -0
  437. package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +67 -0
  438. package/src/runtime/routes/__tests__/plugins-routes.test.ts +240 -1
  439. package/src/runtime/routes/app-routes.ts +1 -1
  440. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +2 -2
  441. package/src/runtime/routes/assets/vellum-design-system.css +1959 -0
  442. package/src/runtime/routes/browser-tabs-routes.ts +9 -0
  443. package/src/runtime/routes/btw-routes.ts +1 -27
  444. package/src/runtime/routes/canonical-guardian-expiry-sweep.ts +17 -8
  445. package/src/runtime/routes/client-routes.ts +10 -0
  446. package/src/runtime/routes/contact-routes.ts +31 -8
  447. package/src/runtime/routes/conversation-compaction-routes.ts +1 -1
  448. package/src/runtime/routes/conversation-management-routes.ts +80 -1
  449. package/src/runtime/routes/conversation-query-routes.ts +68 -22
  450. package/src/runtime/routes/conversation-routes.ts +39 -14
  451. package/src/runtime/routes/credential-routes.ts +40 -16
  452. package/src/runtime/routes/empty-state-greeting-cache.ts +1 -2
  453. package/src/runtime/routes/events-routes.ts +1 -3
  454. package/src/runtime/routes/guardian-approval-interception.ts +14 -73
  455. package/src/runtime/routes/guardian-approval-prompt.ts +22 -4
  456. package/src/runtime/routes/home-feed-routes.ts +8 -3
  457. package/src/runtime/routes/identity-routes.ts +1 -296
  458. package/src/runtime/routes/inbound-message-handler.ts +214 -228
  459. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +89 -7
  460. package/src/runtime/routes/inbound-stages/admission-policy.test.ts +154 -0
  461. package/src/runtime/routes/inbound-stages/admission-policy.ts +140 -0
  462. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +3 -3
  463. package/src/runtime/routes/inbound-stages/background-dispatch.ts +11 -6
  464. package/src/runtime/routes/inbound-stages/escalation-intercept.ts +1 -2
  465. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +1 -2
  466. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +7 -7
  467. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +47 -28
  468. package/src/runtime/routes/inbound-stages/reaction-intercept.ts +358 -0
  469. package/src/runtime/routes/index.ts +2 -0
  470. package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +8 -0
  471. package/src/runtime/routes/integrations/slack/channel.ts +36 -0
  472. package/src/runtime/routes/internal-telemetry-routes.ts +1 -1
  473. package/src/runtime/routes/mcp-auth-routes.ts +233 -41
  474. package/src/runtime/routes/memory-eval-routes.ts +87 -0
  475. package/src/runtime/routes/notification-routes.ts +122 -133
  476. package/src/runtime/routes/platform-routes.ts +2 -2
  477. package/src/runtime/routes/plugins-routes.ts +202 -3
  478. package/src/runtime/routes/schedule-routes.ts +0 -22
  479. package/src/runtime/routes/secret-routes.ts +10 -0
  480. package/src/runtime/routes/surface-action-routes.ts +2 -1
  481. package/src/runtime/routes/tool-call-question-enrichment.test.ts +146 -0
  482. package/src/runtime/routes/tool-call-question-enrichment.ts +66 -0
  483. package/src/runtime/routes/workflow-routes.test.ts +229 -44
  484. package/src/runtime/routes/workflow-routes.ts +131 -29
  485. package/src/runtime/routes/workspace-greetings.ts +55 -0
  486. package/src/runtime/sync/resource-sync-events.ts +1 -11
  487. package/src/runtime/tool-grant-request-helper.ts +18 -16
  488. package/src/runtime/trust-context-resolver.ts +8 -5
  489. package/src/schedule/inference-profile.ts +2 -14
  490. package/src/schedule/schedule-store.ts +1 -1
  491. package/src/schedule/scheduler-types.ts +5 -1
  492. package/src/security/__tests__/provider-key-env-fallback.test.ts +6 -0
  493. package/src/security/secret-patterns.ts +3 -0
  494. package/src/subagent/manager.ts +17 -4
  495. package/src/subagent/types.ts +6 -0
  496. package/src/telemetry/trace-collection-policy.test.ts +28 -0
  497. package/src/telemetry/trace-collection-policy.ts +30 -0
  498. package/src/telemetry/types.ts +89 -0
  499. package/src/telemetry/usage-telemetry-reporter.test.ts +586 -36
  500. package/src/telemetry/usage-telemetry-reporter.ts +148 -41
  501. package/src/tools/AGENTS.md +3 -3
  502. package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +31 -0
  503. package/src/tools/browser/browser-execution.ts +30 -19
  504. package/src/tools/document/document-tool.ts +2 -3
  505. package/src/tools/executor.ts +5 -3
  506. package/src/tools/host-terminal/host-shell.ts +5 -4
  507. package/src/tools/memory/register.ts +2 -2
  508. package/src/tools/network/__tests__/web-fetch-firecrawl.test.ts +360 -0
  509. package/src/tools/network/__tests__/web-search.test.ts +143 -0
  510. package/src/tools/network/web-fetch.ts +372 -1
  511. package/src/tools/network/web-search-error.ts +1 -1
  512. package/src/tools/network/web-search.ts +213 -10
  513. package/src/tools/permission-checker.ts +4 -3
  514. package/src/tools/registry.ts +20 -0
  515. package/src/tools/schedule/create.ts +7 -12
  516. package/src/tools/schedule/update.ts +4 -11
  517. package/src/tools/shared/filesystem/path-policy.ts +39 -13
  518. package/src/tools/side-effects.ts +2 -17
  519. package/src/tools/skills/execute.ts +33 -0
  520. package/src/tools/subagent/spawn.ts +61 -12
  521. package/src/tools/terminal/shell.ts +10 -4
  522. package/src/tools/tool-approval-handler.ts +18 -13
  523. package/src/tools/tool-manifest.ts +0 -2
  524. package/src/tools/types.ts +9 -0
  525. package/src/tools/ui-surface/definitions.ts +64 -3
  526. package/src/tools/verification-control-plane-policy.ts +3 -1
  527. package/src/tools/workflows/run-workflow.test.ts +8 -18
  528. package/src/tools/workflows/run-workflow.ts +1 -0
  529. package/src/util/disk-usage.ts +78 -23
  530. package/src/util/platform.ts +10 -3
  531. package/src/watcher/telemetry.ts +2 -2
  532. package/src/workflows/capabilities.ts +2 -3
  533. package/src/workflows/engine.test.ts +175 -1
  534. package/src/workflows/engine.ts +82 -0
  535. package/src/workflows/journal-store.test.ts +70 -0
  536. package/src/workflows/journal-store.ts +18 -3
  537. package/src/workflows/run-manager.test.ts +171 -28
  538. package/src/workflows/run-manager.ts +66 -24
  539. package/src/workspace/migrations/105-enable-memory-v3-live-for-new-workspaces.ts +63 -0
  540. package/src/workspace/migrations/106-drop-collect-usage-data.ts +47 -0
  541. package/src/workspace/migrations/107-drop-send-diagnostics.ts +47 -0
  542. package/src/workspace/migrations/108-drop-balanced-economy-profile.ts +129 -0
  543. package/src/workspace/migrations/registry.ts +8 -0
  544. package/src/__tests__/app-control-no-global-cgevent.test.ts +0 -98
  545. package/src/__tests__/credential-security-e2e.test.ts +0 -362
  546. package/src/__tests__/credential-vault-unit.test.ts +0 -1528
  547. package/src/__tests__/credential-vault.test.ts +0 -1706
  548. package/src/__tests__/identity-intro-cache.test.ts +0 -315
  549. package/src/__tests__/secret-onetime-send.test.ts +0 -182
  550. package/src/cli/commands/__tests__/task.test.ts +0 -914
  551. package/src/cli/commands/task.ts +0 -771
  552. package/src/config/bundled-skills/personal-page/SKILL.md +0 -57
  553. package/src/config/bundled-skills/personal-page/TOOLS.json +0 -27
  554. package/src/config/bundled-skills/personal-page/tools/app-refresh.ts +0 -17
  555. package/src/config/preloaded-apps/personal-page/src/components/About.tsx +0 -22
  556. package/src/config/preloaded-apps/personal-page/src/components/App.tsx +0 -16
  557. package/src/config/preloaded-apps/personal-page/src/components/Features.tsx +0 -77
  558. package/src/config/preloaded-apps/personal-page/src/components/Hero.tsx +0 -57
  559. package/src/config/preloaded-apps/personal-page/src/components/Pending.tsx +0 -28
  560. package/src/config/preloaded-apps/personal-page/src/components/animations.tsx +0 -234
  561. package/src/config/preloaded-apps/personal-page/src/components/icons.tsx +0 -48
  562. package/src/config/preloaded-apps/personal-page/src/components/media.ts +0 -16
  563. package/src/config/preloaded-apps/personal-page/src/index.html +0 -20
  564. package/src/config/preloaded-apps/personal-page/src/main.tsx +0 -7
  565. package/src/config/preloaded-apps/personal-page/src/profile-data.ts +0 -82
  566. package/src/config/preloaded-apps/personal-page/src/styles.css +0 -759
  567. package/src/memory/__tests__/preloaded-apps.test.ts +0 -85
  568. package/src/memory/preloaded-apps.ts +0 -116
  569. package/src/notifications/tool-approval-copy.ts +0 -142
  570. package/src/runtime/routes/approval-prompt-ts-tracker.ts +0 -78
  571. package/src/runtime/routes/identity-intro-cache.ts +0 -172
  572. package/src/tools/credentials/vault.ts +0 -712
@@ -22,7 +22,6 @@ import {
22
22
  WorkflowRunCapError,
23
23
  WorkflowRunManager,
24
24
  type WorkflowRunManagerDeps,
25
- WorkflowsDisabledError,
26
25
  } from "./run-manager.js";
27
26
 
28
27
  const TRUST: TrustContext = {
@@ -144,7 +143,6 @@ interface EngineResult {
144
143
  * deterministically), an in-memory journal, and spies for broadcast + wake.
145
144
  */
146
145
  function makeHarness(opts?: {
147
- flagEnabled?: boolean;
148
146
  maxConcurrentRuns?: number;
149
147
  /** Custom engine impl; defaults to the deferred-resolver fake. */
150
148
  engine?: WorkflowRunManagerDeps["executeWorkflow"];
@@ -190,7 +188,6 @@ function makeHarness(opts?: {
190
188
  }) as unknown as WorkflowRunManagerDeps["leafRunner"],
191
189
  journal: fake.journal,
192
190
  getConfig: () => makeConfig(opts?.maxConcurrentRuns),
193
- isFlagEnabled: () => opts?.flagEnabled ?? true,
194
191
  wake: (async (wakeOpts) => {
195
192
  wakes.push({
196
193
  conversationId: wakeOpts.conversationId,
@@ -227,22 +224,6 @@ function makeHarness(opts?: {
227
224
  };
228
225
  }
229
226
 
230
- describe("WorkflowRunManager.start — flag gate", () => {
231
- test("flag off → start throws and the engine is never invoked", () => {
232
- const h = makeHarness({ flagEnabled: false });
233
- expect(() =>
234
- h.manager.start({
235
- scriptSource: "export const meta = { name: 'x', description: 'y' }",
236
- args: {},
237
- manifest: { tools: [], hostFunctions: [], persona: false },
238
- trustContext: TRUST,
239
- }),
240
- ).toThrow(WorkflowsDisabledError);
241
- expect(h.executeCalls).toHaveLength(0);
242
- expect(h.fake.rows.size).toBe(0);
243
- });
244
- });
245
-
246
227
  describe("WorkflowRunManager.start — concurrent-run cap", () => {
247
228
  test("the (N+1)th concurrent start is rejected", () => {
248
229
  const h = makeHarness({ maxConcurrentRuns: 2 });
@@ -311,6 +292,7 @@ describe("WorkflowRunManager.abort", () => {
311
292
  scriptSource: "export const meta = { name: 'x', description: 'y' }",
312
293
  args: {},
313
294
  manifest: { tools: [], hostFunctions: [], persona: false },
295
+ conversationId: "conv-1",
314
296
  trustContext: TRUST,
315
297
  });
316
298
 
@@ -337,6 +319,7 @@ describe("WorkflowRunManager — progress events", () => {
337
319
  scriptSource: "export const meta = { name: 'x', description: 'y' }",
338
320
  args: {},
339
321
  manifest: { tools: [], hostFunctions: [], persona: false },
322
+ conversationId: "conv-1",
340
323
  label: "My Flow",
341
324
  trustContext: TRUST,
342
325
  });
@@ -349,14 +332,164 @@ describe("WorkflowRunManager — progress events", () => {
349
332
  expect(progress).toHaveLength(2);
350
333
  expect(progress[0]).toMatchObject({
351
334
  type: "workflow_progress",
335
+ conversationId: "conv-1",
352
336
  label: "My Flow",
353
337
  phase: "Gathering",
354
338
  });
355
339
  expect(progress[1]).toMatchObject({
356
340
  type: "workflow_progress",
341
+ conversationId: "conv-1",
357
342
  message: "step done",
358
343
  });
359
344
  });
345
+
346
+ test("a run with no conversation broadcasts progress unscoped (no conversationId)", () => {
347
+ const h = makeHarness();
348
+ h.manager.start({
349
+ scriptSource: "export const meta = { name: 'x', description: 'y' }",
350
+ args: {},
351
+ manifest: { tools: [], hostFunctions: [], persona: false },
352
+ label: "My Flow",
353
+ trustContext: TRUST,
354
+ });
355
+
356
+ const onProgress = h.executeCalls[0]!.onProgress!;
357
+ onProgress({ type: "phase", title: "Gathering" });
358
+ onProgress({ type: "log", message: "step done" });
359
+
360
+ // `workflow_progress` is a pre-existing event; it still broadcasts for a
361
+ // conversationless run, just without a `conversationId` (unscoped).
362
+ const progress = h.broadcasts.filter((b) => b.type === "workflow_progress");
363
+ expect(progress).toHaveLength(2);
364
+ expect(progress[0]).toMatchObject({
365
+ type: "workflow_progress",
366
+ label: "My Flow",
367
+ phase: "Gathering",
368
+ });
369
+ expect(progress[0]).not.toHaveProperty("conversationId");
370
+ });
371
+
372
+ test("onLeaf start/finish are republished as scoped leaf events", () => {
373
+ const fakeEngine: WorkflowRunManagerDeps["executeWorkflow"] = (options) => {
374
+ options.onLeaf?.({
375
+ type: "leaf_started",
376
+ seq: 1,
377
+ label: "Research",
378
+ phase: "Gather",
379
+ promptSummary: "look it up",
380
+ });
381
+ options.onLeaf?.({
382
+ type: "leaf_finished",
383
+ seq: 1,
384
+ status: "completed",
385
+ label: "Research",
386
+ inputTokens: 30,
387
+ outputTokens: 12,
388
+ resultSummary: "found it",
389
+ });
390
+ return new Promise(() => {}) as ReturnType<
391
+ WorkflowRunManagerDeps["executeWorkflow"]
392
+ >;
393
+ };
394
+ const h = makeHarness({ engine: fakeEngine });
395
+
396
+ const { runId } = h.manager.start({
397
+ scriptSource: "export const meta = { name: 'x', description: 'y' }",
398
+ args: {},
399
+ manifest: { tools: [], hostFunctions: [], persona: false },
400
+ conversationId: "conv-1",
401
+ trustContext: TRUST,
402
+ });
403
+
404
+ const started = h.broadcasts.find(
405
+ (b) => b.type === "workflow_leaf_started",
406
+ );
407
+ expect(started).toMatchObject({
408
+ type: "workflow_leaf_started",
409
+ runId,
410
+ conversationId: "conv-1",
411
+ seq: 1,
412
+ label: "Research",
413
+ phase: "Gather",
414
+ promptSummary: "look it up",
415
+ });
416
+
417
+ const finished = h.broadcasts.find(
418
+ (b) => b.type === "workflow_leaf_finished",
419
+ );
420
+ expect(finished).toMatchObject({
421
+ type: "workflow_leaf_finished",
422
+ runId,
423
+ conversationId: "conv-1",
424
+ seq: 1,
425
+ status: "completed",
426
+ label: "Research",
427
+ inputTokens: 30,
428
+ outputTokens: 12,
429
+ resultSummary: "found it",
430
+ });
431
+ });
432
+
433
+ test("a run with no conversation gets no onLeaf callback", () => {
434
+ const h = makeHarness();
435
+ h.manager.start({
436
+ scriptSource: "export const meta = { name: 'x', description: 'y' }",
437
+ args: {},
438
+ manifest: { tools: [], hostFunctions: [], persona: false },
439
+ trustContext: TRUST,
440
+ });
441
+
442
+ expect(h.executeCalls[0]!.onLeaf).toBeUndefined();
443
+ });
444
+ });
445
+
446
+ describe("WorkflowRunManager.start — workflow_started", () => {
447
+ test("start with a conversation broadcasts workflow_started carrying toolUseId", () => {
448
+ const h = makeHarness();
449
+ const { runId } = h.manager.start({
450
+ scriptSource: "export const meta = { name: 'x', description: 'y' }",
451
+ args: {},
452
+ manifest: { tools: [], hostFunctions: [], persona: false },
453
+ conversationId: "conv-1",
454
+ toolUseId: "toolu-abc",
455
+ label: "My Flow",
456
+ trustContext: TRUST,
457
+ });
458
+
459
+ const started = h.broadcasts.find((b) => b.type === "workflow_started");
460
+ expect(started).toMatchObject({
461
+ type: "workflow_started",
462
+ runId,
463
+ conversationId: "conv-1",
464
+ toolUseId: "toolu-abc",
465
+ label: "My Flow",
466
+ });
467
+ });
468
+
469
+ test("start without a conversation broadcasts no workflow_started", () => {
470
+ const h = makeHarness();
471
+ h.manager.start({
472
+ scriptSource: "export const meta = { name: 'x', description: 'y' }",
473
+ args: {},
474
+ manifest: { tools: [], hostFunctions: [], persona: false },
475
+ trustContext: TRUST,
476
+ });
477
+
478
+ expect(h.broadcasts.some((b) => b.type === "workflow_started")).toBe(false);
479
+ });
480
+
481
+ test("resume never broadcasts workflow_started", () => {
482
+ const h = makeHarness({});
483
+ seedRun(h, {
484
+ id: "run-x",
485
+ status: "interrupted",
486
+ conversationId: "conv-1",
487
+ });
488
+
489
+ h.manager.resume("run-x");
490
+
491
+ expect(h.broadcasts.some((b) => b.type === "workflow_started")).toBe(false);
492
+ });
360
493
  });
361
494
 
362
495
  describe("WorkflowRunManager — completion", () => {
@@ -383,6 +516,7 @@ describe("WorkflowRunManager — completion", () => {
383
516
  expect(completed).toMatchObject({
384
517
  type: "workflow_completed",
385
518
  runId,
519
+ conversationId: "conv-1",
386
520
  status: "completed",
387
521
  agentsSpawned: 4,
388
522
  inputTokens: 100,
@@ -437,7 +571,7 @@ describe("WorkflowRunManager — completion", () => {
437
571
  expect(h.manager.status(runId)?.result).toBe(big);
438
572
  });
439
573
 
440
- test("no conversationId → completion events fire but no wake", async () => {
574
+ test("no conversationId → pre-existing events broadcast unscoped; UI events and wake are gated", async () => {
441
575
  const h = makeHarness();
442
576
  h.manager.start({
443
577
  scriptSource: "export const meta = { name: 'x', description: 'y' }",
@@ -454,8 +588,23 @@ describe("WorkflowRunManager — completion", () => {
454
588
  outputTokens: 1,
455
589
  });
456
590
 
457
- expect(h.broadcasts.some((b) => b.type === "workflow_completed")).toBe(
458
- true,
591
+ // The pre-existing terminal event still broadcasts (unscoped) so raw SSE
592
+ // listeners and conversationless scheduled runs are still surfaced.
593
+ const completed = h.broadcasts.find((b) => b.type === "workflow_completed");
594
+ expect(completed).toMatchObject({
595
+ type: "workflow_completed",
596
+ status: "completed",
597
+ });
598
+ expect(completed).not.toHaveProperty("conversationId");
599
+
600
+ // The conversation-only signals stay gated: no `workflow_started`, no leaf
601
+ // events, and no completion wake without an originating conversation.
602
+ expect(h.broadcasts.some((b) => b.type === "workflow_started")).toBe(false);
603
+ expect(h.broadcasts.some((b) => b.type === "workflow_leaf_started")).toBe(
604
+ false,
605
+ );
606
+ expect(h.broadcasts.some((b) => b.type === "workflow_leaf_finished")).toBe(
607
+ false,
459
608
  );
460
609
  expect(h.wakes).toHaveLength(0);
461
610
  });
@@ -702,10 +851,4 @@ describe("WorkflowRunManager.resume", () => {
702
851
 
703
852
  expect(() => h.manager.resume("run-x")).toThrow(WorkflowRunCapError);
704
853
  });
705
-
706
- test("resume is rejected when the flag is off", () => {
707
- const h = makeHarness({ flagEnabled: false });
708
- seedRun(h, { id: "run-x", status: "interrupted" });
709
- expect(() => h.manager.resume("run-x")).toThrow(WorkflowsDisabledError);
710
- });
711
854
  });
@@ -5,8 +5,6 @@
5
5
  * and routes (later PRs) drive. It owns everything the raw {@link executeWorkflow}
6
6
  * engine deliberately does NOT:
7
7
  *
8
- * - **Feature-flag gate.** `start` hard-fails with {@link WorkflowsDisabledError}
9
- * when the `workflows` flag is off, BEFORE any engine code path is reachable.
10
8
  * - **Concurrent-run cap.** At most `config.workflows.maxConcurrentRuns` runs
11
9
  * may be in flight; the (N+1)th `start` throws {@link WorkflowRunCapError}.
12
10
  * - **Async launch.** `start` resolves capabilities, creates the journal run
@@ -28,7 +26,6 @@
28
26
 
29
27
  import { createHash, randomUUID } from "node:crypto";
30
28
 
31
- import { isAssistantFeatureFlagEnabled } from "../config/assistant-feature-flags.js";
32
29
  import { getConfig } from "../config/loader.js";
33
30
  import type { AssistantConfig } from "../config/schema.js";
34
31
  import {
@@ -58,15 +55,6 @@ const log = getLogger("workflow-run-manager");
58
55
  /** Source tag for the completion wake (shows up in the wake's structured log). */
59
56
  const WORKFLOW_WAKE_SOURCE = "workflow_completed";
60
57
 
61
- /** Thrown by `start` when the `workflows` feature flag is disabled. */
62
- export class WorkflowsDisabledError extends Error {
63
- readonly code = "workflows_disabled" as const;
64
- constructor() {
65
- super("Workflows are not enabled.");
66
- this.name = "WorkflowsDisabledError";
67
- }
68
- }
69
-
70
58
  /** Thrown by `start` when the concurrent-run cap is already reached. */
71
59
  export class WorkflowRunCapError extends Error {
72
60
  readonly code = "workflow_run_cap_exceeded" as const;
@@ -126,6 +114,11 @@ interface StartWorkflowCommon {
126
114
  * scheduled run with no chat surface) — the summary is then events-only.
127
115
  */
128
116
  conversationId?: string;
117
+ /**
118
+ * The `skill_execute` tool-use block id that launched this run; forwarded on
119
+ * `workflow_started` so the client anchors the inline card to the spawn call.
120
+ */
121
+ toolUseId?: string;
129
122
  /** Human-readable label for display; defaults to the run id. */
130
123
  label?: string;
131
124
  /** Trust/auth context forwarded to every leaf. */
@@ -141,7 +134,6 @@ export interface WorkflowRunManagerDeps {
141
134
  leafRunner: typeof runLeaf;
142
135
  journal: typeof journalStore;
143
136
  getConfig: () => AssistantConfig;
144
- isFlagEnabled: (config: AssistantConfig) => boolean;
145
137
  wake: typeof wakeAgentForOpportunity;
146
138
  broadcast: typeof broadcastMessage;
147
139
  newRunId: () => string;
@@ -155,8 +147,6 @@ function defaultDeps(): WorkflowRunManagerDeps {
155
147
  leafRunner: runLeaf,
156
148
  journal: journalStore,
157
149
  getConfig,
158
- isFlagEnabled: (config) =>
159
- isAssistantFeatureFlagEnabled("workflows", config),
160
150
  wake: wakeAgentForOpportunity,
161
151
  broadcast: broadcastMessage,
162
152
  newRunId: () => randomUUID(),
@@ -178,18 +168,14 @@ export class WorkflowRunManager {
178
168
  }
179
169
 
180
170
  /**
181
- * Launch a workflow run. Gates on the `workflows` flag and the concurrent-run
182
- * cap (both throw before any engine code is reachable), resolves capabilities,
171
+ * Launch a workflow run. Gates on the concurrent-run cap (which throws before
172
+ * any engine code is reachable), resolves capabilities,
183
173
  * creates the journal run row, and kicks off {@link executeWorkflow}
184
174
  * asynchronously. Returns the `runId` immediately — completion is surfaced via
185
175
  * events and a conversation wake.
186
176
  */
187
177
  start(opts: StartWorkflowOptions): { runId: string } {
188
178
  const config = this.deps.getConfig();
189
- if (!this.deps.isFlagEnabled(config)) {
190
- throw new WorkflowsDisabledError();
191
- }
192
-
193
179
  const limit = config.workflows.maxConcurrentRuns;
194
180
  if (this.inflight.size >= limit) {
195
181
  throw new WorkflowRunCapError(limit);
@@ -234,6 +220,19 @@ export class WorkflowRunManager {
234
220
  const controller = new AbortController();
235
221
  this.inflight.set(runId, controller);
236
222
 
223
+ // Announce the launch only when there's a conversation to scope it to (the
224
+ // same gate the in-flight/terminal broadcasts use). `resume` never emits
225
+ // this — the run already announced itself on its original `start`.
226
+ if (opts.conversationId) {
227
+ this.deps.broadcast({
228
+ type: "workflow_started",
229
+ runId,
230
+ conversationId: opts.conversationId,
231
+ ...(opts.toolUseId ? { toolUseId: opts.toolUseId } : {}),
232
+ label,
233
+ });
234
+ }
235
+
237
236
  // Fire-and-forget: the engine owns its own try/catch and always finishes
238
237
  // the run row. We never await it — `start` must return synchronously.
239
238
  void this.runToCompletion(
@@ -282,9 +281,6 @@ export class WorkflowRunManager {
282
281
  */
283
282
  resume(runId: string): { runId: string } {
284
283
  const config = this.deps.getConfig();
285
- if (!this.deps.isFlagEnabled(config)) {
286
- throw new WorkflowsDisabledError();
287
- }
288
284
 
289
285
  if (this.inflight.has(runId)) {
290
286
  throw new WorkflowResumeNotPossibleError(runId, "in_flight");
@@ -398,6 +394,14 @@ export class WorkflowRunManager {
398
394
  ctx: RunContext,
399
395
  ): Promise<void> {
400
396
  const config = this.deps.getConfig();
397
+ // `workflow_progress` / `workflow_completed` always broadcast: a run with no
398
+ // originating conversation emits them unscoped (no `conversationId`) for raw
399
+ // SSE listeners and the DB record, preserving the pre-scoping contract that
400
+ // surfaces conversationless scheduled runs. The conversation-only signals —
401
+ // `workflow_started`, the `onLeaf` leaf events, and the completion wake —
402
+ // stay gated on `conversationId` since they exist solely to drive the inline
403
+ // workflow card in that conversation.
404
+ const conversationId = ctx.conversationId;
401
405
  try {
402
406
  const result = await this.deps.executeWorkflow({
403
407
  runId,
@@ -415,6 +419,7 @@ export class WorkflowRunManager {
415
419
  this.deps.broadcast({
416
420
  type: "workflow_progress",
417
421
  runId,
422
+ ...(conversationId ? { conversationId } : {}),
418
423
  label,
419
424
  agentsSpawned,
420
425
  ...(event.type === "phase"
@@ -422,6 +427,41 @@ export class WorkflowRunManager {
422
427
  : { message: event.message }),
423
428
  });
424
429
  },
430
+ ...(conversationId
431
+ ? {
432
+ onLeaf: (event) => {
433
+ if (event.type === "leaf_started") {
434
+ this.deps.broadcast({
435
+ type: "workflow_leaf_started",
436
+ runId,
437
+ conversationId,
438
+ seq: event.seq,
439
+ ...(event.label !== undefined
440
+ ? { label: event.label }
441
+ : {}),
442
+ ...(event.phase !== undefined
443
+ ? { phase: event.phase }
444
+ : {}),
445
+ promptSummary: event.promptSummary,
446
+ });
447
+ } else {
448
+ this.deps.broadcast({
449
+ type: "workflow_leaf_finished",
450
+ runId,
451
+ conversationId,
452
+ seq: event.seq,
453
+ status: event.status,
454
+ ...(event.label !== undefined
455
+ ? { label: event.label }
456
+ : {}),
457
+ inputTokens: event.inputTokens,
458
+ outputTokens: event.outputTokens,
459
+ resultSummary: event.resultSummary,
460
+ });
461
+ }
462
+ },
463
+ }
464
+ : {}),
425
465
  });
426
466
 
427
467
  const summary = buildCompletionSummary(
@@ -434,6 +474,7 @@ export class WorkflowRunManager {
434
474
  this.deps.broadcast({
435
475
  type: "workflow_completed",
436
476
  runId,
477
+ ...(conversationId ? { conversationId } : {}),
437
478
  status: result.status,
438
479
  agentsSpawned: result.agentsSpawned,
439
480
  inputTokens: result.inputTokens,
@@ -495,6 +536,7 @@ interface RunContext {
495
536
  const VALID_TRUST_CLASSES: ReadonlySet<TrustContext["trustClass"]> = new Set([
496
537
  "guardian",
497
538
  "trusted_contact",
539
+ "unverified_contact",
498
540
  "unknown",
499
541
  ]);
500
542
 
@@ -0,0 +1,63 @@
1
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+
4
+ import type { MigrationRunContext, WorkspaceMigration } from "./types.js";
5
+
6
+ /**
7
+ * Switch brand-new assistants onto memory-v3 (the live injected memory source)
8
+ * at creation by persisting `memory.v3.live = true`.
9
+ *
10
+ * The schema default is `false`, so existing assistants keep running v2 on
11
+ * upgrade — this migration writes the value only for freshly-created
12
+ * workspaces. The value gates v3 via `isMemoryV3Live`; existing assistants are
13
+ * switched on deliberately, never automatically. Covers every surface (local,
14
+ * Docker, managed) uniformly because all run workspace migrations on first boot.
15
+ */
16
+ export const enableMemoryV3LiveForNewWorkspacesMigration: WorkspaceMigration = {
17
+ id: "105-enable-memory-v3-live-for-new-workspaces",
18
+ description:
19
+ "Persist memory.v3.live=true for brand-new workspaces so new assistants run memory-v3 from creation",
20
+
21
+ run(workspaceDir: string, ctx?: MigrationRunContext): void {
22
+ // Only switch new assistants on. Existing workspaces fall through to the
23
+ // schema default (false) and keep running v2 until enabled explicitly.
24
+ // Without a context (older callers) treat the workspace as existing.
25
+ if (!ctx?.isNewWorkspace) return;
26
+
27
+ const configPath = join(workspaceDir, "config.json");
28
+
29
+ // A fresh workspace may have no config.json yet (schema defaults are
30
+ // applied in memory at load); create one so the live default persists.
31
+ let config: Record<string, unknown> = {};
32
+ if (existsSync(configPath)) {
33
+ try {
34
+ const raw = JSON.parse(readFileSync(configPath, "utf-8"));
35
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return;
36
+ config = raw as Record<string, unknown>;
37
+ } catch {
38
+ return;
39
+ }
40
+ }
41
+
42
+ if (config.memory === undefined) config.memory = {};
43
+ const memory = config.memory;
44
+ if (!memory || typeof memory !== "object" || Array.isArray(memory)) return;
45
+ const memoryConfig = memory as Record<string, unknown>;
46
+
47
+ if (memoryConfig.v3 === undefined) memoryConfig.v3 = {};
48
+ const v3 = memoryConfig.v3;
49
+ if (!v3 || typeof v3 !== "object" || Array.isArray(v3)) return;
50
+ const v3Config = v3 as Record<string, unknown>;
51
+
52
+ // Respect an explicit value already present (idempotent re-runs, or a
53
+ // hatch-time override that set it deliberately).
54
+ if ("live" in v3Config) return;
55
+
56
+ v3Config.live = true;
57
+ writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
58
+ },
59
+
60
+ down(_workspaceDir: string): void {
61
+ // Forward-only: cannot distinguish a user's explicit choice from this seed.
62
+ },
63
+ };
@@ -0,0 +1,47 @@
1
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+
4
+ import type { WorkspaceMigration } from "./types.js";
5
+
6
+ /**
7
+ * Drops the `collectUsageData` config key (gating is governed by the platform
8
+ * `share_analytics` consent). An explicit local opt-out (`collectUsageData:
9
+ * false`) is preserved as `legacyTelemetryOptOut: true` so telemetry stays off
10
+ * regardless of platform consent, which defaults to opt-in and cannot be
11
+ * written by the daemon. A `true`/non-false value carries no opt-out intent, so
12
+ * the key is removed without setting the marker. The schema strips unknown keys
13
+ * on the next save, so a stale value is otherwise harmless.
14
+ */
15
+ export const dropCollectUsageDataMigration: WorkspaceMigration = {
16
+ id: "106-drop-collect-usage-data",
17
+ description:
18
+ "Drop collectUsageData; preserve an explicit opt-out as legacyTelemetryOptOut (telemetry is gated by platform share_analytics consent)",
19
+ run(workspaceDir: string): void {
20
+ const configPath = join(workspaceDir, "config.json");
21
+ if (!existsSync(configPath)) return;
22
+
23
+ let config: Record<string, unknown>;
24
+ try {
25
+ const raw = JSON.parse(readFileSync(configPath, "utf-8"));
26
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return;
27
+ config = raw as Record<string, unknown>;
28
+ } catch {
29
+ return; // Malformed config — skip
30
+ }
31
+
32
+ if (!("collectUsageData" in config)) return;
33
+
34
+ // An explicit opt-out is preserved as a fail-closed marker; any non-false
35
+ // value carries no opt-out intent.
36
+ if (config.collectUsageData === false) {
37
+ config.legacyTelemetryOptOut = true;
38
+ }
39
+
40
+ delete config.collectUsageData;
41
+ writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
42
+ },
43
+ down(_workspaceDir: string): void {
44
+ // No-op: the forward migration only removes a key and sets a fail-closed
45
+ // marker, so there is nothing to restore.
46
+ },
47
+ };
@@ -0,0 +1,47 @@
1
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+
4
+ import type { WorkspaceMigration } from "./types.js";
5
+
6
+ /**
7
+ * Drops the `sendDiagnostics` config key (crash reporting is governed by the
8
+ * platform `share_diagnostics` consent). An explicit local opt-out
9
+ * (`sendDiagnostics: false`) is preserved as `legacyDiagnosticsOptOut: true` so
10
+ * Sentry stays off regardless of platform consent, which defaults to opt-in and
11
+ * cannot be written by the daemon. A `true`/non-false value carries no opt-out
12
+ * intent, so the key is removed without setting the marker. The schema strips
13
+ * unknown keys on the next save, so a stale value is otherwise harmless.
14
+ */
15
+ export const dropSendDiagnosticsMigration: WorkspaceMigration = {
16
+ id: "107-drop-send-diagnostics",
17
+ description:
18
+ "Drop sendDiagnostics; preserve an explicit opt-out as legacyDiagnosticsOptOut (crash reporting is gated by platform share_diagnostics consent)",
19
+ run(workspaceDir: string): void {
20
+ const configPath = join(workspaceDir, "config.json");
21
+ if (!existsSync(configPath)) return;
22
+
23
+ let config: Record<string, unknown>;
24
+ try {
25
+ const raw = JSON.parse(readFileSync(configPath, "utf-8"));
26
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return;
27
+ config = raw as Record<string, unknown>;
28
+ } catch {
29
+ return; // Malformed config — skip
30
+ }
31
+
32
+ if (!("sendDiagnostics" in config)) return;
33
+
34
+ // An explicit opt-out is preserved as a fail-closed marker; any non-false
35
+ // value carries no opt-out intent.
36
+ if (config.sendDiagnostics === false) {
37
+ config.legacyDiagnosticsOptOut = true;
38
+ }
39
+
40
+ delete config.sendDiagnostics;
41
+ writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
42
+ },
43
+ down(_workspaceDir: string): void {
44
+ // No-op: the forward migration only removes a key and sets a fail-closed
45
+ // marker, so there is nothing to restore.
46
+ },
47
+ };