@vellumai/assistant 0.9.1-staging.1 → 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 (433) hide show
  1. package/docs/activation-funnel-telemetry.md +24 -18
  2. package/node_modules/@vellumai/gateway-client/src/admission-policy-contract.ts +97 -0
  3. package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +10 -0
  4. package/node_modules/@vellumai/gateway-client/src/index.ts +15 -0
  5. package/openapi.yaml +852 -15
  6. package/package.json +1 -1
  7. package/src/__tests__/access-request-card-view.test.ts +98 -0
  8. package/src/__tests__/access-request-seed-content-blocks.test.ts +2 -4
  9. package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +59 -7
  10. package/src/__tests__/agent-loop-compaction-strip.test.ts +17 -16
  11. package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +16 -13
  12. package/src/__tests__/app-compiler.test.ts +15 -1
  13. package/src/__tests__/auth-fallback-events-store.test.ts +6 -14
  14. package/src/__tests__/call-pointer-messages.test.ts +28 -0
  15. package/src/__tests__/cancel-clears-processing.test.ts +89 -0
  16. package/src/__tests__/channel-approval-routes.test.ts +0 -4
  17. package/src/__tests__/channel-inbound-disk-pressure.test.ts +5 -15
  18. package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +3 -4
  19. package/src/__tests__/compactor-image-manifest-trust.test.ts +21 -1
  20. package/src/__tests__/compactor-summary-call-truncation.test.ts +223 -0
  21. package/src/__tests__/config-loader-backfill.test.ts +174 -30
  22. package/src/__tests__/config-schema.test.ts +35 -0
  23. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +2 -2
  24. package/src/__tests__/contact-store-user-file.test.ts +0 -6
  25. package/src/__tests__/contacts-tools.test.ts +29 -0
  26. package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -0
  27. package/src/__tests__/conversation-agent-loop.test.ts +58 -0
  28. package/src/__tests__/conversation-attention-telegram.test.ts +0 -1
  29. package/src/__tests__/conversation-lifecycle.test.ts +7 -9
  30. package/src/__tests__/conversation-load-history-repair.test.ts +101 -0
  31. package/src/__tests__/conversation-routes-guardian-reply.test.ts +15 -12
  32. package/src/__tests__/conversation-surfaces-activation-emit.test.ts +6 -3
  33. package/src/__tests__/conversation-title-service.test.ts +62 -0
  34. package/src/__tests__/credential-execution-shell-lockdown.test.ts +18 -11
  35. package/src/__tests__/credential-prompt-route.test.ts +1 -0
  36. package/src/__tests__/credential-security-invariants.test.ts +2 -0
  37. package/src/__tests__/disk-pressure-policy.test.ts +12 -0
  38. package/src/__tests__/disk-usage.test.ts +65 -0
  39. package/src/__tests__/dynamic-page-surface.test.ts +51 -0
  40. package/src/__tests__/gateway-flag-listener.test.ts +110 -1
  41. package/src/__tests__/guardian-binding-drift-heal.test.ts +1 -1
  42. package/src/__tests__/guardian-card-withdrawal.test.ts +403 -0
  43. package/src/__tests__/guardian-decision-primitive-canonical.test.ts +5 -3
  44. package/src/__tests__/guardian-grant-minting.test.ts +3 -35
  45. package/src/__tests__/guardian-routing-invariants.test.ts +64 -26
  46. package/src/__tests__/guardian-routing-state.test.ts +0 -1
  47. package/src/__tests__/headless-browser-mode.test.ts +10 -0
  48. package/src/__tests__/headless-browser-navigate.test.ts +8 -3
  49. package/src/__tests__/helpers/create-guardian-binding.ts +0 -1
  50. package/src/__tests__/host-browser-proxy.test.ts +87 -0
  51. package/src/__tests__/injector-v3-suppression.test.ts +27 -20
  52. package/src/__tests__/internal-telemetry-routes.test.ts +6 -14
  53. package/src/__tests__/invite-redemption-service.test.ts +0 -3
  54. package/src/__tests__/llm-catalog-parity.test.ts +30 -1
  55. package/src/__tests__/llm-resolver.test.ts +21 -0
  56. package/src/__tests__/llm-schema.test.ts +1 -0
  57. package/src/__tests__/managed-profile-guard.test.ts +163 -4
  58. package/src/__tests__/mcp-health-check.test.ts +6 -7
  59. package/src/__tests__/media-stream-server-integration.test.ts +317 -13
  60. package/src/__tests__/path-policy.test.ts +34 -0
  61. package/src/__tests__/persona-resolver.test.ts +38 -0
  62. package/src/__tests__/plugin-api-provider.test.ts +24 -0
  63. package/src/__tests__/plugin-tool-contribution.test.ts +6 -3
  64. package/src/__tests__/post-compaction-reinjection-idempotency.test.ts +214 -0
  65. package/src/__tests__/provider-send-message-override-profile.test.ts +76 -0
  66. package/src/__tests__/reaction-persistence.test.ts +150 -29
  67. package/src/__tests__/relay-server.test.ts +285 -0
  68. package/src/__tests__/runtime-attachment-metadata.test.ts +0 -1
  69. package/src/__tests__/scheduler-reuse-conversation.test.ts +8 -5
  70. package/src/__tests__/skill-execute-input.test.ts +5 -0
  71. package/src/__tests__/skills.test.ts +51 -0
  72. package/src/__tests__/slack-notification-approval-card.test.ts +176 -0
  73. package/src/__tests__/slack-reaction-canonical-approval.test.ts +285 -0
  74. package/src/__tests__/subagent-tools.test.ts +150 -0
  75. package/src/__tests__/task-progress-nudge-hook.test.ts +1 -1
  76. package/src/__tests__/title-generate-hook.test.ts +100 -3
  77. package/src/__tests__/tool-approval-seed-content-blocks.test.ts +1 -1
  78. package/src/__tests__/tool-audit-listener.test.ts +7 -7
  79. package/src/__tests__/tool-executor-lifecycle-events.test.ts +6 -3
  80. package/src/__tests__/trusted-contact-approval-notifier.test.ts +4 -2
  81. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +220 -3
  82. package/src/__tests__/trusted-contact-verification.test.ts +2 -4
  83. package/src/__tests__/twilio-routes.test.ts +81 -1
  84. package/src/__tests__/voice-invite-redemption.test.ts +0 -1
  85. package/src/__tests__/weak-open-model.test.ts +30 -0
  86. package/src/__tests__/workspace-migration-105-enable-memory-v3-live-for-new-workspaces.test.ts +149 -0
  87. package/src/__tests__/workspace-migration-108-drop-balanced-economy-profile.test.ts +285 -0
  88. package/src/__tests__/workspace-migration-add-send-diagnostics.test.ts +1 -1
  89. package/src/__tests__/workspace-migration-drop-collect-usage-data.test.ts +118 -0
  90. package/src/__tests__/workspace-migration-drop-send-diagnostics.test.ts +118 -0
  91. package/src/a2a/__tests__/e2e-a2a-channel.test.ts +0 -4
  92. package/src/agent/loop.ts +33 -33
  93. package/src/api/events/tool-result.ts +6 -0
  94. package/src/api/events/workflow-completed.ts +53 -0
  95. package/src/api/events/workflow-leaf-finished.ts +38 -0
  96. package/src/api/events/workflow-leaf-started.ts +35 -0
  97. package/src/api/events/workflow-progress.ts +32 -0
  98. package/src/api/events/workflow-started.ts +31 -0
  99. package/src/api/index.ts +40 -0
  100. package/src/api/responses/conversation-message.ts +26 -0
  101. package/src/api/responses/home.ts +26 -0
  102. package/src/api/responses/workflow-journal.ts +53 -0
  103. package/src/approvals/guardian-card-withdrawal.ts +145 -0
  104. package/src/approvals/guardian-decision-primitive.ts +26 -3
  105. package/src/approvals/guardian-request-resolvers.ts +181 -78
  106. package/src/calls/__tests__/channel-admission-reader.test.ts +132 -0
  107. package/src/calls/__tests__/relay-setup-router.test.ts +350 -0
  108. package/src/calls/call-pointer-messages.ts +10 -4
  109. package/src/calls/channel-admission-reader.ts +104 -0
  110. package/src/calls/guardian-dispatch.ts +17 -45
  111. package/src/calls/media-stream-server.ts +84 -2
  112. package/src/calls/relay-server.ts +66 -0
  113. package/src/calls/relay-setup-router.ts +82 -1
  114. package/src/calls/twilio-routes.ts +17 -8
  115. package/src/cli/commands/clients.ts +3 -0
  116. package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v2-compare-render.test.ts +1 -1
  117. package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v2.test.ts +8 -7
  118. package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v3.test.ts +5 -4
  119. package/src/cli/commands/memory/index.ts +30 -0
  120. package/src/cli/commands/{memory-v2-compare-render.ts → memory/memory-v2-compare-render.ts} +1 -1
  121. package/src/cli/commands/{memory-v2.ts → memory/memory-v2.ts} +6 -15
  122. package/src/cli/commands/{memory-v3.ts → memory/memory-v3.ts} +97 -11
  123. package/src/cli/commands/oauth/status.test.ts +36 -0
  124. package/src/cli/commands/oauth/status.ts +23 -3
  125. package/src/cli/commands/plugins.ts +57 -5
  126. package/src/cli/lib/__tests__/inspect-plugin.test.ts +54 -0
  127. package/src/cli/lib/__tests__/merge-plugin-tree.test.ts +134 -4
  128. package/src/cli/lib/__tests__/plugin-surfaces.test.ts +111 -0
  129. package/src/cli/lib/__tests__/upgrade-plugin.test.ts +53 -11
  130. package/src/cli/lib/inspect-plugin.ts +12 -1
  131. package/src/cli/lib/merge-plugin-tree.ts +149 -49
  132. package/src/cli/lib/plugin-surfaces.ts +104 -0
  133. package/src/cli/lib/upgrade-plugin.ts +64 -36
  134. package/src/cli/program.ts +2 -4
  135. package/src/config/__tests__/sync-gated-profiles.test.ts +368 -0
  136. package/src/config/assistant-feature-flags.ts +22 -7
  137. package/src/config/bundled-skills/contacts/tools/contact-search.ts +0 -1
  138. package/src/config/bundled-skills/messaging/SKILL.md +6 -4
  139. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +9 -8
  140. package/src/config/bundled-skills/workflows/SKILL.md +14 -7
  141. package/src/config/call-site-defaults.ts +3 -0
  142. package/src/config/feature-flag-registry.json +49 -18
  143. package/src/config/llm-resolver.ts +3 -0
  144. package/src/config/memory-v3-gate.ts +11 -0
  145. package/src/config/schema.ts +8 -6
  146. package/src/config/schemas/__tests__/memory-v3.test.ts +1 -0
  147. package/src/config/schemas/call-site-catalog.ts +7 -0
  148. package/src/config/schemas/channels.ts +11 -0
  149. package/src/config/schemas/llm.ts +31 -0
  150. package/src/config/schemas/memory-lifecycle.ts +3 -7
  151. package/src/config/schemas/memory-v3.ts +6 -0
  152. package/src/config/schemas/services.ts +18 -0
  153. package/src/config/seed-inference-profiles.ts +94 -34
  154. package/src/config/skills.ts +21 -0
  155. package/src/config/sync-gated-profiles.ts +220 -0
  156. package/src/contacts/contact-store.ts +2 -10
  157. package/src/contacts/contacts-write.ts +1 -2
  158. package/src/contacts/types.ts +0 -1
  159. package/src/context/compactor.ts +86 -52
  160. package/src/context/strip-injections.ts +58 -10
  161. package/src/context/token-estimator.ts +1 -1
  162. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -2
  163. package/src/daemon/conversation-agent-loop-handlers.ts +2 -0
  164. package/src/daemon/conversation-agent-loop.ts +100 -19
  165. package/src/daemon/conversation-history.ts +1 -1
  166. package/src/daemon/conversation-lifecycle.ts +3 -5
  167. package/src/daemon/conversation-process.ts +13 -5
  168. package/src/daemon/conversation-runtime-assembly.ts +13 -15
  169. package/src/daemon/conversation-surfaces.ts +26 -0
  170. package/src/daemon/conversation-tool-setup.ts +16 -11
  171. package/src/daemon/conversation.ts +64 -14
  172. package/src/daemon/disk-pressure-policy.ts +5 -3
  173. package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +0 -1
  174. package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +0 -1
  175. package/src/daemon/handlers/config-a2a.ts +0 -2
  176. package/src/daemon/handlers/config-channels.ts +5 -10
  177. package/src/daemon/handlers/config-slack-channel.ts +20 -0
  178. package/src/daemon/handlers/conversations.ts +107 -0
  179. package/src/daemon/host-browser-proxy.ts +41 -0
  180. package/src/daemon/lifecycle.ts +55 -20
  181. package/src/daemon/message-provenance.ts +2 -0
  182. package/src/daemon/message-types/contacts.ts +0 -1
  183. package/src/daemon/message-types/web-activity.ts +7 -1
  184. package/src/daemon/message-types/workflows.ts +83 -1
  185. package/src/daemon/tool-setup-types.ts +4 -0
  186. package/src/daemon/trust-context.ts +1 -1
  187. package/src/events/tool-audit-listener.ts +2 -2
  188. package/src/home/feed-source-enrichment.test.ts +151 -0
  189. package/src/home/feed-source-enrichment.ts +176 -0
  190. package/src/instrument.ts +18 -6
  191. package/src/ipc/__tests__/binary-result-ipc.test.ts +81 -0
  192. package/src/ipc/__tests__/clients-list-ipc.test.ts +20 -0
  193. package/src/ipc/assistant-server.ts +37 -4
  194. package/src/ipc/gateway-flag-listener.ts +18 -2
  195. package/src/memory/__tests__/auto-analysis-enqueue.test.ts +5 -16
  196. package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +7 -11
  197. package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +37 -7
  198. package/src/memory/__tests__/memory-retrospective-job.test.ts +34 -0
  199. package/src/memory/__tests__/onboarding-events-store.test.ts +7 -7
  200. package/src/memory/auth-fallback-events-store.ts +2 -2
  201. package/src/memory/auto-analysis-enqueue.ts +3 -5
  202. package/src/memory/canonical-guardian-store.ts +39 -1
  203. package/src/memory/conversation-crud.ts +9 -4
  204. package/src/memory/conversation-key-store.ts +17 -2
  205. package/src/memory/conversation-title-service.ts +64 -7
  206. package/src/memory/db-init.ts +10 -0
  207. package/src/memory/embedding-backend.ts +15 -1
  208. package/src/memory/jobs-worker.ts +2 -1
  209. package/src/memory/lifecycle-events-store.ts +2 -2
  210. package/src/memory/memory-retrospective-enqueue.ts +31 -6
  211. package/src/memory/memory-retrospective-job.ts +9 -0
  212. package/src/memory/migrations/129-contact-channels-access-fields.ts +18 -9
  213. package/src/memory/migrations/131-drop-legacy-member-guardian-tables.ts +14 -2
  214. package/src/memory/migrations/289-contact-channels-unique-ext-user.ts +10 -0
  215. package/src/memory/migrations/291-contact-channels-renormalize-addresses.ts +10 -0
  216. package/src/memory/migrations/292-schedule-default-no-reuse-conversation.test.ts +67 -0
  217. package/src/memory/migrations/292-schedule-default-no-reuse-conversation.ts +25 -0
  218. package/src/memory/migrations/293-workflow-journal-leaf-tokens.ts +32 -0
  219. package/src/memory/migrations/294-drop-external-user-id.ts +31 -0
  220. package/src/memory/migrations/295-drop-approval-prompt-ts-tracker.ts +20 -0
  221. package/src/memory/migrations/296-rewrite-balanced-economy-profile-pins.test.ts +110 -0
  222. package/src/memory/migrations/296-rewrite-balanced-economy-profile-pins.ts +68 -0
  223. package/src/memory/migrations/__tests__/131-drop-legacy-member-guardian-tables.test.ts +154 -0
  224. package/src/memory/migrations/__tests__/289-contact-channels-unique-ext-user.test.ts +31 -0
  225. package/src/memory/migrations/__tests__/291-contact-channels-renormalize-addresses.test.ts +30 -0
  226. package/src/memory/migrations/index.ts +5 -0
  227. package/src/memory/onboarding-events-store.ts +3 -3
  228. package/src/memory/schema/contacts.ts +0 -1
  229. package/src/memory/skill-loaded-events-store.test.ts +7 -15
  230. package/src/memory/skill-loaded-events-store.ts +2 -2
  231. package/src/memory/tool-executed-events-store.test.ts +7 -7
  232. package/src/memory/turn-trace-store.test.ts +736 -0
  233. package/src/memory/turn-trace-store.ts +364 -0
  234. package/src/memory/v2/__tests__/consolidation-job.test.ts +8 -0
  235. package/src/memory/v2/__tests__/skill-content.test.ts +30 -0
  236. package/src/memory/v2/consolidation-job.ts +2 -2
  237. package/src/memory/v2/skill-content.ts +25 -7
  238. package/src/memory/v2/skill-store.ts +7 -1
  239. package/src/memory/v3-eval/__tests__/eval-packets.test.ts +248 -0
  240. package/src/memory/v3-eval/eval-packets.ts +546 -0
  241. package/src/messaging/providers/slack/api.ts +31 -0
  242. package/src/messaging/providers/slack/send.test.ts +114 -2
  243. package/src/messaging/providers/slack/send.ts +30 -7
  244. package/src/messaging/providers/slack/withdraw.test.ts +200 -0
  245. package/src/messaging/providers/slack/withdraw.ts +161 -0
  246. package/src/notifications/AGENTS.md +2 -0
  247. package/src/notifications/access-request-copy.ts +72 -59
  248. package/src/notifications/adapters/slack.ts +55 -73
  249. package/src/notifications/approval-card-data.ts +333 -0
  250. package/src/notifications/broadcaster.ts +6 -2
  251. package/src/notifications/canonical-delivery-recorder.ts +139 -0
  252. package/src/notifications/copy-composer.ts +3 -3
  253. package/src/notifications/decision-engine.ts +4 -2
  254. package/src/notifications/destination-resolver.ts +4 -6
  255. package/src/notifications/guardian-question-mode.ts +10 -0
  256. package/src/notifications/home-feed-side-effect.ts +3 -13
  257. package/src/notifications/notification-utils.ts +2 -1
  258. package/src/notifications/signal.ts +79 -43
  259. package/src/notifications/types.ts +98 -128
  260. package/src/permissions/checker.test.ts +51 -0
  261. package/src/permissions/checker.ts +185 -26
  262. package/src/permissions/ipc-risk-types.ts +24 -0
  263. package/src/permissions/question-prompter.test.ts +27 -0
  264. package/src/permissions/question-prompter.ts +4 -0
  265. package/src/platform/client.test.ts +119 -0
  266. package/src/platform/client.ts +66 -0
  267. package/src/platform/consent-cache.test.ts +267 -0
  268. package/src/platform/consent-cache.ts +174 -0
  269. package/src/plugin-api/index.ts +27 -0
  270. package/src/plugins/defaults/advisor/__tests__/advisor-gate.test.ts +56 -0
  271. package/src/plugins/defaults/advisor/__tests__/advisor-state-store.test.ts +43 -0
  272. package/src/plugins/defaults/advisor/__tests__/agent-loop-integration.test.ts +137 -0
  273. package/src/plugins/defaults/advisor/__tests__/consult.test.ts +153 -0
  274. package/src/plugins/defaults/advisor/__tests__/hooks.test.ts +138 -0
  275. package/src/plugins/defaults/advisor/__tests__/transcript.test.ts +147 -0
  276. package/src/plugins/defaults/advisor/advisor-gate.ts +29 -0
  277. package/src/plugins/defaults/advisor/advisor-state-store.ts +94 -0
  278. package/src/plugins/defaults/advisor/config.ts +21 -0
  279. package/src/plugins/defaults/advisor/consult.ts +93 -0
  280. package/src/plugins/defaults/advisor/hooks/post-model-call.ts +34 -0
  281. package/src/plugins/defaults/advisor/hooks/pre-model-call.ts +30 -0
  282. package/src/plugins/defaults/advisor/hooks/user-prompt-submit.ts +19 -0
  283. package/src/plugins/defaults/advisor/package.json +14 -0
  284. package/src/plugins/defaults/advisor/steering.ts +67 -0
  285. package/src/plugins/defaults/advisor/tools/advisor.ts +65 -0
  286. package/src/plugins/defaults/advisor/transcript.ts +76 -0
  287. package/src/plugins/defaults/index.ts +35 -0
  288. package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +22 -9
  289. package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +2 -2
  290. package/src/plugins/defaults/memory-retrieval/tail-reinjection-strip.ts +64 -0
  291. package/src/plugins/defaults/memory-retrieval/unified-turn-context.ts +29 -21
  292. package/src/plugins/defaults/memory-v3-shadow/__tests__/carry-integration.test.ts +1 -0
  293. package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +1 -0
  294. package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +75 -7
  295. package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +31 -4
  296. package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +77 -2
  297. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +1 -0
  298. package/src/plugins/defaults/memory-v3-shadow/injector.ts +7 -10
  299. package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +37 -4
  300. package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +32 -20
  301. package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +56 -3
  302. package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +23 -2
  303. package/src/plugins/defaults/task-progress-nudge/hooks/post-tool-use.ts +2 -12
  304. package/src/plugins/defaults/title-generate/hooks/stop.ts +56 -21
  305. package/src/prompts/persona-resolver.ts +12 -2
  306. package/src/prompts/templates/system-sections.ts +7 -2
  307. package/src/providers/__tests__/provider-env-vars.test.ts +6 -0
  308. package/src/providers/__tests__/provider-secret-catalog.test.ts +1 -0
  309. package/src/providers/__tests__/retry-callsite.test.ts +176 -0
  310. package/src/providers/atlascloud/client.ts +85 -0
  311. package/src/providers/fetch-provider-catalog.ts +85 -0
  312. package/src/providers/inference/adapter-factory.ts +3 -0
  313. package/src/providers/model-catalog.ts +58 -0
  314. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +33 -0
  315. package/src/providers/openai/chat-completions-provider.ts +7 -0
  316. package/src/providers/openai/responses-provider.ts +10 -0
  317. package/src/providers/provider-send-message.ts +11 -3
  318. package/src/providers/retry.ts +53 -12
  319. package/src/providers/search-provider-catalog.ts +10 -0
  320. package/src/providers/weak-open-model.ts +22 -0
  321. package/src/runtime/__tests__/agent-wake.test.ts +181 -0
  322. package/src/runtime/__tests__/client-health.test.ts +44 -0
  323. package/src/runtime/access-request-helper.ts +21 -53
  324. package/src/runtime/actor-trust-resolver.ts +49 -21
  325. package/src/runtime/agent-wake.ts +52 -0
  326. package/src/runtime/assistant-event-hub.ts +18 -4
  327. package/src/runtime/auth/__tests__/route-policy.test.ts +12 -0
  328. package/src/runtime/auth/require-bound-guardian.ts +1 -4
  329. package/src/runtime/capabilities.test.ts +120 -0
  330. package/src/runtime/capabilities.ts +197 -0
  331. package/src/runtime/channel-approval-types.ts +5 -1
  332. package/src/runtime/channel-retry-sweep.ts +1 -0
  333. package/src/runtime/channel-verification-service.ts +1 -2
  334. package/src/runtime/client-health.ts +26 -0
  335. package/src/runtime/confirmation-request-guardian-bridge.ts +38 -29
  336. package/src/runtime/effective-capabilities.test.ts +128 -0
  337. package/src/runtime/effective-capabilities.ts +84 -0
  338. package/src/runtime/guardian-reply-router.ts +106 -21
  339. package/src/runtime/invite-redemption-service.ts +6 -22
  340. package/src/runtime/migrations/__tests__/vbundle-builder-fd-leak.test.ts +123 -0
  341. package/src/runtime/migrations/vbundle-builder.ts +49 -20
  342. package/src/runtime/pending-interactions.ts +15 -0
  343. package/src/runtime/routes/__tests__/client-routes.test.ts +13 -0
  344. package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +67 -0
  345. package/src/runtime/routes/__tests__/plugins-routes.test.ts +35 -13
  346. package/src/runtime/routes/browser-tabs-routes.ts +9 -0
  347. package/src/runtime/routes/canonical-guardian-expiry-sweep.ts +17 -8
  348. package/src/runtime/routes/client-routes.ts +10 -0
  349. package/src/runtime/routes/contact-routes.ts +31 -8
  350. package/src/runtime/routes/conversation-management-routes.ts +80 -1
  351. package/src/runtime/routes/conversation-query-routes.ts +68 -22
  352. package/src/runtime/routes/conversation-routes.ts +37 -12
  353. package/src/runtime/routes/events-routes.ts +1 -3
  354. package/src/runtime/routes/guardian-approval-interception.ts +14 -73
  355. package/src/runtime/routes/guardian-approval-prompt.ts +22 -4
  356. package/src/runtime/routes/home-feed-routes.ts +8 -3
  357. package/src/runtime/routes/inbound-message-handler.ts +214 -228
  358. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +88 -6
  359. package/src/runtime/routes/inbound-stages/admission-policy.test.ts +154 -0
  360. package/src/runtime/routes/inbound-stages/admission-policy.ts +140 -0
  361. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +3 -3
  362. package/src/runtime/routes/inbound-stages/background-dispatch.ts +11 -6
  363. package/src/runtime/routes/inbound-stages/escalation-intercept.ts +1 -2
  364. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +1 -2
  365. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +7 -7
  366. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +47 -28
  367. package/src/runtime/routes/inbound-stages/reaction-intercept.ts +358 -0
  368. package/src/runtime/routes/index.ts +2 -0
  369. package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +8 -0
  370. package/src/runtime/routes/integrations/slack/channel.ts +36 -0
  371. package/src/runtime/routes/internal-telemetry-routes.ts +1 -1
  372. package/src/runtime/routes/mcp-auth-routes.ts +233 -41
  373. package/src/runtime/routes/memory-eval-routes.ts +87 -0
  374. package/src/runtime/routes/notification-routes.ts +122 -133
  375. package/src/runtime/routes/platform-routes.ts +2 -2
  376. package/src/runtime/routes/plugins-routes.ts +40 -7
  377. package/src/runtime/routes/secret-routes.ts +10 -0
  378. package/src/runtime/routes/surface-action-routes.ts +2 -1
  379. package/src/runtime/routes/tool-call-question-enrichment.test.ts +146 -0
  380. package/src/runtime/routes/tool-call-question-enrichment.ts +66 -0
  381. package/src/runtime/routes/workflow-routes.test.ts +225 -1
  382. package/src/runtime/routes/workflow-routes.ts +131 -1
  383. package/src/runtime/tool-grant-request-helper.ts +18 -16
  384. package/src/runtime/trust-context-resolver.ts +8 -5
  385. package/src/schedule/schedule-store.ts +1 -1
  386. package/src/schedule/scheduler-types.ts +5 -1
  387. package/src/security/__tests__/provider-key-env-fallback.test.ts +6 -0
  388. package/src/security/secret-patterns.ts +3 -0
  389. package/src/subagent/manager.ts +11 -4
  390. package/src/telemetry/trace-collection-policy.test.ts +28 -0
  391. package/src/telemetry/trace-collection-policy.ts +30 -0
  392. package/src/telemetry/types.ts +89 -0
  393. package/src/telemetry/usage-telemetry-reporter.test.ts +586 -36
  394. package/src/telemetry/usage-telemetry-reporter.ts +148 -41
  395. package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +31 -0
  396. package/src/tools/browser/browser-execution.ts +29 -18
  397. package/src/tools/document/document-tool.ts +2 -3
  398. package/src/tools/executor.ts +5 -3
  399. package/src/tools/host-terminal/host-shell.ts +5 -4
  400. package/src/tools/memory/register.ts +2 -2
  401. package/src/tools/network/__tests__/web-fetch-firecrawl.test.ts +360 -0
  402. package/src/tools/network/__tests__/web-search.test.ts +143 -0
  403. package/src/tools/network/web-fetch.ts +372 -1
  404. package/src/tools/network/web-search.ts +213 -10
  405. package/src/tools/permission-checker.ts +3 -2
  406. package/src/tools/registry.ts +20 -0
  407. package/src/tools/schedule/create.ts +4 -3
  408. package/src/tools/schedule/update.ts +2 -1
  409. package/src/tools/shared/filesystem/path-policy.ts +39 -13
  410. package/src/tools/skills/execute.ts +1 -2
  411. package/src/tools/subagent/spawn.ts +37 -13
  412. package/src/tools/terminal/shell.ts +10 -4
  413. package/src/tools/tool-approval-handler.ts +17 -10
  414. package/src/tools/types.ts +9 -0
  415. package/src/tools/ui-surface/definitions.ts +25 -2
  416. package/src/tools/verification-control-plane-policy.ts +3 -1
  417. package/src/tools/workflows/run-workflow.ts +1 -0
  418. package/src/util/disk-usage.ts +78 -23
  419. package/src/util/platform.ts +8 -1
  420. package/src/watcher/telemetry.ts +2 -2
  421. package/src/workflows/engine.test.ts +175 -1
  422. package/src/workflows/engine.ts +82 -0
  423. package/src/workflows/journal-store.test.ts +70 -0
  424. package/src/workflows/journal-store.ts +18 -3
  425. package/src/workflows/run-manager.test.ts +171 -3
  426. package/src/workflows/run-manager.ts +64 -0
  427. package/src/workspace/migrations/105-enable-memory-v3-live-for-new-workspaces.ts +63 -0
  428. package/src/workspace/migrations/106-drop-collect-usage-data.ts +47 -0
  429. package/src/workspace/migrations/107-drop-send-diagnostics.ts +47 -0
  430. package/src/workspace/migrations/108-drop-balanced-economy-profile.ts +129 -0
  431. package/src/workspace/migrations/registry.ts +8 -0
  432. package/src/notifications/tool-approval-copy.ts +0 -142
  433. package/src/runtime/routes/approval-prompt-ts-tracker.ts +0 -78
@@ -68,6 +68,9 @@ export interface WorkflowJournalEntry {
68
68
  request: unknown;
69
69
  result: unknown;
70
70
  status: string;
71
+ /** Per-leaf token usage for a completed agent leaf; undefined otherwise. */
72
+ inputTokens?: number;
73
+ outputTokens?: number;
71
74
  createdAt: number | null;
72
75
  }
73
76
 
@@ -111,6 +114,9 @@ export interface AppendJournalEntryInput {
111
114
  request?: unknown;
112
115
  result?: unknown;
113
116
  status: string;
117
+ /** Per-leaf token usage; set for a completed agent leaf, omitted otherwise. */
118
+ inputTokens?: number;
119
+ outputTokens?: number;
114
120
  }
115
121
 
116
122
  // ---------------------------------------------------------------------------
@@ -145,6 +151,8 @@ interface WorkflowJournalRow {
145
151
  request_json: string | null;
146
152
  result_json: string | null;
147
153
  status: string;
154
+ input_tokens: number | null;
155
+ output_tokens: number | null;
148
156
  created_at: number | null;
149
157
  }
150
158
 
@@ -194,6 +202,8 @@ function rowToJournalEntry(row: WorkflowJournalRow): WorkflowJournalEntry {
194
202
  request: parseJsonColumn(row.request_json),
195
203
  result: parseJsonColumn(row.result_json),
196
204
  status: row.status,
205
+ inputTokens: row.input_tokens ?? undefined,
206
+ outputTokens: row.output_tokens ?? undefined,
197
207
  createdAt: row.created_at,
198
208
  };
199
209
  }
@@ -318,14 +328,17 @@ export function appendJournalEntry(
318
328
  rawRun(
319
329
  /*sql*/ `
320
330
  INSERT INTO workflow_journal (
321
- run_id, seq, call_hash, kind, request_json, result_json, status, created_at
322
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
331
+ run_id, seq, call_hash, kind, request_json, result_json, status,
332
+ input_tokens, output_tokens, created_at
333
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
323
334
  ON CONFLICT(run_id, seq) DO UPDATE SET
324
335
  call_hash = excluded.call_hash,
325
336
  kind = excluded.kind,
326
337
  request_json = excluded.request_json,
327
338
  result_json = excluded.result_json,
328
- status = excluded.status
339
+ status = excluded.status,
340
+ input_tokens = excluded.input_tokens,
341
+ output_tokens = excluded.output_tokens
329
342
  `,
330
343
  input.runId,
331
344
  input.seq,
@@ -334,6 +347,8 @@ export function appendJournalEntry(
334
347
  serializeJsonColumn(input.request),
335
348
  serializeJsonColumn(input.result),
336
349
  input.status,
350
+ input.inputTokens ?? null,
351
+ input.outputTokens ?? null,
337
352
  Date.now(),
338
353
  );
339
354
  return getJournalEntry(input.runId, input.seq)!;
@@ -292,6 +292,7 @@ describe("WorkflowRunManager.abort", () => {
292
292
  scriptSource: "export const meta = { name: 'x', description: 'y' }",
293
293
  args: {},
294
294
  manifest: { tools: [], hostFunctions: [], persona: false },
295
+ conversationId: "conv-1",
295
296
  trustContext: TRUST,
296
297
  });
297
298
 
@@ -318,6 +319,7 @@ describe("WorkflowRunManager — progress events", () => {
318
319
  scriptSource: "export const meta = { name: 'x', description: 'y' }",
319
320
  args: {},
320
321
  manifest: { tools: [], hostFunctions: [], persona: false },
322
+ conversationId: "conv-1",
321
323
  label: "My Flow",
322
324
  trustContext: TRUST,
323
325
  });
@@ -330,14 +332,164 @@ describe("WorkflowRunManager — progress events", () => {
330
332
  expect(progress).toHaveLength(2);
331
333
  expect(progress[0]).toMatchObject({
332
334
  type: "workflow_progress",
335
+ conversationId: "conv-1",
333
336
  label: "My Flow",
334
337
  phase: "Gathering",
335
338
  });
336
339
  expect(progress[1]).toMatchObject({
337
340
  type: "workflow_progress",
341
+ conversationId: "conv-1",
338
342
  message: "step done",
339
343
  });
340
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
+ });
341
493
  });
342
494
 
343
495
  describe("WorkflowRunManager — completion", () => {
@@ -364,6 +516,7 @@ describe("WorkflowRunManager — completion", () => {
364
516
  expect(completed).toMatchObject({
365
517
  type: "workflow_completed",
366
518
  runId,
519
+ conversationId: "conv-1",
367
520
  status: "completed",
368
521
  agentsSpawned: 4,
369
522
  inputTokens: 100,
@@ -418,7 +571,7 @@ describe("WorkflowRunManager — completion", () => {
418
571
  expect(h.manager.status(runId)?.result).toBe(big);
419
572
  });
420
573
 
421
- 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 () => {
422
575
  const h = makeHarness();
423
576
  h.manager.start({
424
577
  scriptSource: "export const meta = { name: 'x', description: 'y' }",
@@ -435,8 +588,23 @@ describe("WorkflowRunManager — completion", () => {
435
588
  outputTokens: 1,
436
589
  });
437
590
 
438
- expect(h.broadcasts.some((b) => b.type === "workflow_completed")).toBe(
439
- 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,
440
608
  );
441
609
  expect(h.wakes).toHaveLength(0);
442
610
  });
@@ -114,6 +114,11 @@ interface StartWorkflowCommon {
114
114
  * scheduled run with no chat surface) — the summary is then events-only.
115
115
  */
116
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;
117
122
  /** Human-readable label for display; defaults to the run id. */
118
123
  label?: string;
119
124
  /** Trust/auth context forwarded to every leaf. */
@@ -215,6 +220,19 @@ export class WorkflowRunManager {
215
220
  const controller = new AbortController();
216
221
  this.inflight.set(runId, controller);
217
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
+
218
236
  // Fire-and-forget: the engine owns its own try/catch and always finishes
219
237
  // the run row. We never await it — `start` must return synchronously.
220
238
  void this.runToCompletion(
@@ -376,6 +394,14 @@ export class WorkflowRunManager {
376
394
  ctx: RunContext,
377
395
  ): Promise<void> {
378
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;
379
405
  try {
380
406
  const result = await this.deps.executeWorkflow({
381
407
  runId,
@@ -393,6 +419,7 @@ export class WorkflowRunManager {
393
419
  this.deps.broadcast({
394
420
  type: "workflow_progress",
395
421
  runId,
422
+ ...(conversationId ? { conversationId } : {}),
396
423
  label,
397
424
  agentsSpawned,
398
425
  ...(event.type === "phase"
@@ -400,6 +427,41 @@ export class WorkflowRunManager {
400
427
  : { message: event.message }),
401
428
  });
402
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
+ : {}),
403
465
  });
404
466
 
405
467
  const summary = buildCompletionSummary(
@@ -412,6 +474,7 @@ export class WorkflowRunManager {
412
474
  this.deps.broadcast({
413
475
  type: "workflow_completed",
414
476
  runId,
477
+ ...(conversationId ? { conversationId } : {}),
415
478
  status: result.status,
416
479
  agentsSpawned: result.agentsSpawned,
417
480
  inputTokens: result.inputTokens,
@@ -473,6 +536,7 @@ interface RunContext {
473
536
  const VALID_TRUST_CLASSES: ReadonlySet<TrustContext["trustClass"]> = new Set([
474
537
  "guardian",
475
538
  "trusted_contact",
539
+ "unverified_contact",
476
540
  "unknown",
477
541
  ]);
478
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
+ };
@@ -0,0 +1,129 @@
1
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+
4
+ import type { WorkspaceMigration } from "./types.js";
5
+
6
+ // Reclaim the managed `balanced-economy` inference profile. Its
7
+ // MiniMax-M3-on-Fireworks config lives on `balanced`, whose content the seeder
8
+ // reconciles from the templates on every boot; the seeder never deletes a
9
+ // profile, so this migration deletes the managed `balanced-economy` object and
10
+ // repoints every reference to it — its `profileOrder` entry, the `activeProfile`
11
+ // and `advisorProfile` selections, call-site `profile` overrides, and mix-profile
12
+ // arms — onto `balanced`, which resolves to the same MiniMax M3 route. A
13
+ // `balanced-economy` profile that the user owns (`source !== "managed"`) is left
14
+ // fully intact, references included.
15
+
16
+ const ECONOMY = "balanced-economy";
17
+ const REPLACEMENT = "balanced";
18
+
19
+ export const dropBalancedEconomyProfileMigration: WorkspaceMigration = {
20
+ id: "108-drop-balanced-economy-profile",
21
+ description:
22
+ "Remove the managed Balanced Economy profile and repoint its references to Balanced",
23
+ run(workspaceDir: string): void {
24
+ const configPath = join(workspaceDir, "config.json");
25
+ if (!existsSync(configPath)) return;
26
+
27
+ let config: Record<string, unknown>;
28
+ try {
29
+ const raw = JSON.parse(readFileSync(configPath, "utf-8"));
30
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return;
31
+ config = raw as Record<string, unknown>;
32
+ } catch {
33
+ return;
34
+ }
35
+
36
+ const llm = readObject(config.llm);
37
+ if (llm === null) return;
38
+
39
+ const profiles = readObject(llm.profiles);
40
+ const economy = profiles !== null ? readObject(profiles[ECONOMY]) : null;
41
+
42
+ // A user-owned profile that happens to use this name keeps everything,
43
+ // references included — only the managed profile is reclaimed.
44
+ if (economy !== null && economy.source !== "managed") return;
45
+
46
+ let changed = false;
47
+
48
+ if (economy !== null && profiles !== null) {
49
+ delete profiles[ECONOMY];
50
+ changed = true;
51
+ }
52
+
53
+ if (Array.isArray(llm.profileOrder)) {
54
+ const order = llm.profileOrder as unknown[];
55
+ const pruned = order.filter((name) => name !== ECONOMY);
56
+ if (pruned.length !== order.length) {
57
+ llm.profileOrder = pruned;
58
+ changed = true;
59
+ }
60
+ }
61
+
62
+ // Repoint call-site overrides — LLMSchema strips a profile name absent from
63
+ // `llm.profiles` at load, which would drop the equivalent MiniMax route.
64
+ const callSites = readObject(llm.callSites);
65
+ if (callSites !== null) {
66
+ for (const value of Object.values(callSites)) {
67
+ const site = readObject(value);
68
+ if (site !== null && site.profile === ECONOMY) {
69
+ site.profile = REPLACEMENT;
70
+ changed = true;
71
+ }
72
+ }
73
+ }
74
+
75
+ // Repoint mix-profile arms — LLMSchema.superRefine rejects a mix arm whose
76
+ // referenced profile is absent, failing config validation at load.
77
+ if (profiles !== null) {
78
+ for (const value of Object.values(profiles)) {
79
+ const entry = readObject(value);
80
+ if (entry === null || !Array.isArray(entry.mix)) continue;
81
+ for (const arm of entry.mix) {
82
+ const armObj = readObject(arm);
83
+ if (armObj !== null && armObj.profile === ECONOMY) {
84
+ armObj.profile = REPLACEMENT;
85
+ changed = true;
86
+ }
87
+ }
88
+ }
89
+ }
90
+
91
+ if (llm.activeProfile === ECONOMY) {
92
+ llm.activeProfile = REPLACEMENT;
93
+ changed = true;
94
+
95
+ // The replaced selection was an enabled profile, so `balanced` must be
96
+ // usable: a disabled profile is skipped by the resolver and rejected by
97
+ // validation. Clearing the status sticks because the seeder preserves
98
+ // whatever status is on disk.
99
+ const balanced =
100
+ profiles !== null ? readObject(profiles[REPLACEMENT]) : null;
101
+ if (balanced !== null && balanced.status === "disabled") {
102
+ delete balanced.status;
103
+ }
104
+ }
105
+
106
+ // The advisor selection is the other top-level profile reference
107
+ // `LLMSchema.superRefine` validates against `llm.profiles`; an unresolvable
108
+ // value invalidates the config and is stripped at load.
109
+ if (llm.advisorProfile === ECONOMY) {
110
+ llm.advisorProfile = REPLACEMENT;
111
+ changed = true;
112
+ }
113
+
114
+ if (!changed) return;
115
+
116
+ config.llm = llm;
117
+ writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
118
+ },
119
+ down(_workspaceDir: string): void {
120
+ // Forward-only.
121
+ },
122
+ };
123
+
124
+ function readObject(value: unknown): Record<string, unknown> | null {
125
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
126
+ return null;
127
+ }
128
+ return value as Record<string, unknown>;
129
+ }