@vellumai/assistant 0.9.0 → 0.10.0-dev.202606192226.8ed8a6a

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 (624) hide show
  1. package/ARCHITECTURE.md +19 -35
  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 +1128 -119
  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__/agent-loop-pushes-post-hook-prompt.test.ts +306 -0
  28. package/src/__tests__/always-loaded-tools-guard.test.ts +2 -3
  29. package/src/__tests__/app-compiler.test.ts +15 -1
  30. package/src/__tests__/app-dir-path-guard.test.ts +0 -1
  31. package/src/__tests__/approval-cascade.test.ts +6 -0
  32. package/src/__tests__/approval-routes-http.test.ts +128 -0
  33. package/src/__tests__/assistant-feature-flag-guard.test.ts +1 -4
  34. package/src/__tests__/assistant-feature-flag-guardrails.test.ts +0 -2
  35. package/src/__tests__/auth-fallback-events-store.test.ts +6 -14
  36. package/src/__tests__/avatar-identity-sync.test.ts +2 -27
  37. package/src/__tests__/btw-routes.test.ts +6 -8
  38. package/src/__tests__/call-pointer-messages.test.ts +28 -0
  39. package/src/__tests__/cancel-clears-processing.test.ts +89 -0
  40. package/src/__tests__/channel-approval-routes.test.ts +0 -4
  41. package/src/__tests__/channel-inbound-disk-pressure.test.ts +5 -15
  42. package/src/__tests__/checker.test.ts +0 -3
  43. package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +3 -4
  44. package/src/__tests__/compaction-events.test.ts +6 -0
  45. package/src/__tests__/compactor-image-manifest-trust.test.ts +21 -1
  46. package/src/__tests__/compactor-summary-call-truncation.test.ts +223 -0
  47. package/src/__tests__/config-loader-backfill.test.ts +268 -27
  48. package/src/__tests__/config-schema.test.ts +35 -0
  49. package/src/__tests__/config-watcher.test.ts +0 -18
  50. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +2 -2
  51. package/src/__tests__/contact-store-user-file.test.ts +0 -6
  52. package/src/__tests__/contacts-tools.test.ts +29 -0
  53. package/src/__tests__/conversation-abort-tool-results.test.ts +6 -0
  54. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +22 -0
  55. package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -0
  56. package/src/__tests__/conversation-agent-loop.test.ts +58 -0
  57. package/src/__tests__/conversation-attention-telegram.test.ts +0 -1
  58. package/src/__tests__/conversation-confirmation-signals.test.ts +6 -0
  59. package/src/__tests__/conversation-lifecycle.test.ts +7 -9
  60. package/src/__tests__/conversation-load-history-repair.test.ts +101 -0
  61. package/src/__tests__/conversation-process-callsite.test.ts +6 -0
  62. package/src/__tests__/conversation-provider-retry-repair.test.ts +6 -0
  63. package/src/__tests__/conversation-queue.test.ts +6 -0
  64. package/src/__tests__/conversation-routes-guardian-reply.test.ts +15 -12
  65. package/src/__tests__/conversation-slash-queue.test.ts +6 -0
  66. package/src/__tests__/conversation-slash-unknown.test.ts +6 -0
  67. package/src/__tests__/conversation-speed-override.test.ts +6 -0
  68. package/src/__tests__/conversation-surfaces-activation-emit.test.ts +6 -3
  69. package/src/__tests__/conversation-title-service.test.ts +62 -0
  70. package/src/__tests__/conversation-workspace-cache-state.test.ts +6 -0
  71. package/src/__tests__/conversation-workspace-injection.test.ts +6 -0
  72. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +6 -0
  73. package/src/__tests__/credential-broker.test.ts +449 -1
  74. package/src/__tests__/credential-execution-shell-lockdown.test.ts +18 -11
  75. package/src/__tests__/credential-execution-tools.test.ts +0 -1
  76. package/src/__tests__/credential-prompt-route.test.ts +4 -4
  77. package/src/__tests__/credential-routes.test.ts +360 -0
  78. package/src/__tests__/credential-security-invariants.test.ts +4 -13
  79. package/src/__tests__/disk-pressure-policy.test.ts +12 -0
  80. package/src/__tests__/disk-pressure-tools.test.ts +41 -1
  81. package/src/__tests__/disk-usage.test.ts +65 -0
  82. package/src/__tests__/document-tool-security.test.ts +9 -6
  83. package/src/__tests__/document-update-default-surface.test.ts +202 -0
  84. package/src/__tests__/dynamic-page-surface.test.ts +152 -1
  85. package/src/__tests__/fixtures/credential-security-fixtures.ts +2 -33
  86. package/src/__tests__/gateway-flag-listener.test.ts +110 -1
  87. package/src/__tests__/gateway-only-guard.test.ts +3 -7
  88. package/src/__tests__/gemini-count-tokens.test.ts +70 -0
  89. package/src/__tests__/guardian-binding-drift-heal.test.ts +1 -1
  90. package/src/__tests__/guardian-card-withdrawal.test.ts +403 -0
  91. package/src/__tests__/guardian-decision-primitive-canonical.test.ts +5 -3
  92. package/src/__tests__/guardian-grant-minting.test.ts +3 -35
  93. package/src/__tests__/guardian-routing-invariants.test.ts +64 -26
  94. package/src/__tests__/guardian-routing-state.test.ts +0 -1
  95. package/src/__tests__/headless-browser-mode.test.ts +10 -0
  96. package/src/__tests__/headless-browser-navigate.test.ts +8 -3
  97. package/src/__tests__/helpers/create-guardian-binding.ts +0 -1
  98. package/src/__tests__/host-browser-proxy.test.ts +87 -0
  99. package/src/__tests__/identity-routes.test.ts +0 -189
  100. package/src/__tests__/inbound-invite-redemption.test.ts +4 -4
  101. package/src/__tests__/injector-disk-pressure.test.ts +10 -5
  102. package/src/__tests__/injector-v3-suppression.test.ts +27 -20
  103. package/src/__tests__/internal-telemetry-routes.test.ts +6 -14
  104. package/src/__tests__/invite-redemption-service.test.ts +4 -7
  105. package/src/__tests__/llm-callsite-catalog.test.ts +5 -6
  106. package/src/__tests__/llm-catalog-parity.test.ts +30 -23
  107. package/src/__tests__/llm-resolver.test.ts +70 -24
  108. package/src/__tests__/llm-schema.test.ts +1 -0
  109. package/src/__tests__/managed-profile-guard.test.ts +163 -4
  110. package/src/__tests__/mcp-health-check.test.ts +6 -7
  111. package/src/__tests__/media-stream-server-integration.test.ts +317 -13
  112. package/src/__tests__/oauth-provider-seed-logos.test.ts +4 -6
  113. package/src/__tests__/onboarding-persona-write.test.ts +1 -1
  114. package/src/__tests__/path-policy.test.ts +34 -0
  115. package/src/__tests__/persona-resolver.test.ts +49 -14
  116. package/src/__tests__/plugin-api-model-profiles.test.ts +178 -0
  117. package/src/__tests__/plugin-api-provider.test.ts +24 -0
  118. package/src/__tests__/plugin-tool-contribution.test.ts +6 -3
  119. package/src/__tests__/post-compaction-reinjection-idempotency.test.ts +214 -0
  120. package/src/__tests__/provider-send-message-override-profile.test.ts +76 -0
  121. package/src/__tests__/reaction-persistence.test.ts +150 -29
  122. package/src/__tests__/registry.test.ts +2 -7
  123. package/src/__tests__/relay-server.test.ts +285 -0
  124. package/src/__tests__/runtime-attachment-metadata.test.ts +0 -1
  125. package/src/__tests__/schedule-routes-workflow-validation.test.ts +1 -10
  126. package/src/__tests__/schedule-routes.test.ts +0 -30
  127. package/src/__tests__/schedule-tools.test.ts +2 -18
  128. package/src/__tests__/scheduler-reuse-conversation.test.ts +8 -5
  129. package/src/__tests__/secret-response-routing.test.ts +35 -0
  130. package/src/__tests__/secret-routes-platform-proxy.test.ts +16 -2
  131. package/src/__tests__/skill-execute-input.test.ts +177 -1
  132. package/src/__tests__/skill-load-tool.test.ts +72 -0
  133. package/src/__tests__/skill-runtime-path.test.ts +2 -3
  134. package/src/__tests__/skills.test.ts +51 -0
  135. package/src/__tests__/slack-notification-approval-card.test.ts +176 -0
  136. package/src/__tests__/slack-reaction-canonical-approval.test.ts +285 -0
  137. package/src/__tests__/subagent-tools.test.ts +266 -0
  138. package/src/__tests__/surface-completion-nudge-hook.test.ts +367 -0
  139. package/src/__tests__/system-storage-cleanup-skill.test.ts +56 -0
  140. package/src/__tests__/task-progress-nudge-hook.test.ts +1 -1
  141. package/src/__tests__/title-generate-hook.test.ts +100 -3
  142. package/src/__tests__/token-estimator-accuracy.benchmark.test.ts +1 -29
  143. package/src/__tests__/token-manager.test.ts +519 -0
  144. package/src/__tests__/tool-approval-seed-content-blocks.test.ts +1 -1
  145. package/src/__tests__/tool-audit-listener.test.ts +7 -7
  146. package/src/__tests__/tool-executor-lifecycle-events.test.ts +6 -3
  147. package/src/__tests__/tool-executor.test.ts +0 -79
  148. package/src/__tests__/trusted-contact-approval-notifier.test.ts +4 -2
  149. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +220 -3
  150. package/src/__tests__/trusted-contact-multichannel.test.ts +3 -3
  151. package/src/__tests__/trusted-contact-verification.test.ts +8 -10
  152. package/src/__tests__/twilio-routes.test.ts +81 -1
  153. package/src/__tests__/voice-invite-redemption.test.ts +2 -3
  154. package/src/__tests__/weak-open-model.test.ts +30 -0
  155. package/src/__tests__/web-search-catalog-parity.test.ts +6 -25
  156. package/src/__tests__/workspace-greetings.test.ts +152 -0
  157. package/src/__tests__/workspace-migration-105-enable-memory-v3-live-for-new-workspaces.test.ts +149 -0
  158. package/src/__tests__/workspace-migration-108-drop-balanced-economy-profile.test.ts +285 -0
  159. package/src/__tests__/workspace-migration-add-send-diagnostics.test.ts +1 -1
  160. package/src/__tests__/workspace-migration-drop-collect-usage-data.test.ts +118 -0
  161. package/src/__tests__/workspace-migration-drop-send-diagnostics.test.ts +118 -0
  162. package/src/a2a/__tests__/e2e-a2a-channel.test.ts +0 -4
  163. package/src/agent/loop.ts +75 -29
  164. package/src/api/README.md +6 -6
  165. package/src/api/events/tool-result.ts +6 -0
  166. package/src/api/events/workflow-completed.ts +53 -0
  167. package/src/api/events/workflow-leaf-finished.ts +38 -0
  168. package/src/api/events/workflow-leaf-started.ts +35 -0
  169. package/src/api/events/workflow-progress.ts +32 -0
  170. package/src/api/events/workflow-started.ts +31 -0
  171. package/src/api/index.ts +40 -0
  172. package/src/api/responses/conversation-message.ts +28 -4
  173. package/src/api/responses/home.ts +26 -4
  174. package/src/api/responses/workflow-journal.ts +53 -0
  175. package/src/approvals/guardian-card-withdrawal.ts +145 -0
  176. package/src/approvals/guardian-decision-primitive.ts +26 -3
  177. package/src/approvals/guardian-request-resolvers.ts +183 -80
  178. package/src/calls/__tests__/channel-admission-reader.test.ts +132 -0
  179. package/src/calls/__tests__/relay-setup-router.test.ts +350 -0
  180. package/src/calls/call-pointer-messages.ts +10 -4
  181. package/src/calls/channel-admission-reader.ts +104 -0
  182. package/src/calls/guardian-dispatch.ts +17 -45
  183. package/src/calls/media-stream-server.ts +84 -2
  184. package/src/calls/relay-access-wait.ts +1 -1
  185. package/src/calls/relay-server.ts +66 -0
  186. package/src/calls/relay-setup-router.ts +82 -1
  187. package/src/calls/twilio-routes.ts +17 -8
  188. package/src/calls/voice-session-bridge.ts +2 -2
  189. package/src/cli/commands/clients.ts +3 -0
  190. package/src/cli/commands/db/status.ts +37 -3
  191. package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v2-compare-render.test.ts +1 -1
  192. package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v2.test.ts +8 -7
  193. package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v3.test.ts +5 -4
  194. package/src/cli/commands/memory/index.ts +30 -0
  195. package/src/cli/commands/{memory-v2-compare-render.ts → memory/memory-v2-compare-render.ts} +1 -1
  196. package/src/cli/commands/{memory-v2.ts → memory/memory-v2.ts} +6 -15
  197. package/src/cli/commands/{memory-v3.ts → memory/memory-v3.ts} +97 -11
  198. package/src/cli/commands/oauth/status.test.ts +36 -0
  199. package/src/cli/commands/oauth/status.ts +23 -3
  200. package/src/cli/commands/plugins.ts +400 -14
  201. package/src/cli/lib/__tests__/diff-plugin.test.ts +443 -0
  202. package/src/cli/lib/__tests__/inspect-plugin.test.ts +54 -0
  203. package/src/cli/lib/__tests__/install-from-github.test.ts +31 -0
  204. package/src/cli/lib/__tests__/merge-plugin-tree.test.ts +443 -0
  205. package/src/cli/lib/__tests__/plugin-pin-history.test.ts +162 -0
  206. package/src/cli/lib/__tests__/plugin-surfaces.test.ts +111 -0
  207. package/src/cli/lib/__tests__/upgrade-plugin.test.ts +295 -2
  208. package/src/cli/lib/diff-plugin.ts +346 -0
  209. package/src/cli/lib/inspect-plugin.ts +12 -1
  210. package/src/cli/lib/install-from-github.ts +128 -19
  211. package/src/cli/lib/merge-plugin-tree.ts +328 -0
  212. package/src/cli/lib/plugin-fingerprint.ts +14 -0
  213. package/src/cli/lib/plugin-marketplace.ts +11 -0
  214. package/src/cli/lib/plugin-pin-history.ts +257 -0
  215. package/src/cli/lib/plugin-surfaces.ts +104 -0
  216. package/src/cli/lib/upgrade-plugin.ts +298 -10
  217. package/src/cli/program.ts +2 -6
  218. package/src/config/__tests__/sync-gated-profiles.test.ts +368 -0
  219. package/src/config/assistant-feature-flags.ts +22 -7
  220. package/src/config/bundled-skills/contacts/tools/contact-search.ts +0 -1
  221. package/src/config/bundled-skills/document-editor/SKILL.md +3 -3
  222. package/src/config/bundled-skills/document-editor/TOOLS.json +2 -2
  223. package/src/config/bundled-skills/messaging/SKILL.md +6 -4
  224. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +9 -8
  225. package/src/config/bundled-skills/subagent/SKILL.md +4 -0
  226. package/src/config/bundled-skills/subagent/TOOLS.json +4 -0
  227. package/src/config/bundled-skills/system-storage-cleanup/SKILL.md +74 -0
  228. package/src/config/bundled-skills/workflows/SKILL.md +14 -8
  229. package/src/config/bundled-tool-registry.ts +2 -7
  230. package/src/config/call-site-defaults.ts +15 -2
  231. package/src/config/feature-flag-registry.json +46 -31
  232. package/src/config/inference-profile-validation.ts +26 -0
  233. package/src/config/llm-resolver.ts +3 -0
  234. package/src/config/loader.ts +4 -0
  235. package/src/config/memory-v3-gate.ts +11 -0
  236. package/src/config/profile-order.ts +28 -0
  237. package/src/config/schema.ts +8 -6
  238. package/src/config/schemas/__tests__/memory-v3.test.ts +1 -0
  239. package/src/config/schemas/call-site-catalog.ts +7 -0
  240. package/src/config/schemas/channels.ts +11 -0
  241. package/src/config/schemas/elevenlabs.ts +0 -1
  242. package/src/config/schemas/llm.ts +31 -0
  243. package/src/config/schemas/memory-lifecycle.ts +3 -7
  244. package/src/config/schemas/memory-v3.ts +6 -0
  245. package/src/config/schemas/platform.ts +0 -8
  246. package/src/config/schemas/services.ts +18 -0
  247. package/src/config/seed-inference-profiles.ts +109 -44
  248. package/src/config/skills.ts +21 -0
  249. package/src/config/sync-gated-profiles.ts +220 -0
  250. package/src/contacts/contact-store.ts +89 -106
  251. package/src/contacts/contacts-write.ts +5 -22
  252. package/src/contacts/types.ts +0 -1
  253. package/src/context/compactor.ts +88 -54
  254. package/src/context/strip-injections.ts +58 -10
  255. package/src/context/token-estimator.ts +1 -1
  256. package/src/credential-execution/process-manager.ts +55 -14
  257. package/src/credential-execution/prompted-credential.ts +2 -3
  258. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -2
  259. package/src/daemon/__tests__/conversation-tool-setup.test.ts +44 -0
  260. package/src/daemon/config-watcher.ts +0 -4
  261. package/src/daemon/conversation-agent-loop-handlers.ts +2 -0
  262. package/src/daemon/conversation-agent-loop.ts +114 -22
  263. package/src/daemon/conversation-history.ts +1 -1
  264. package/src/daemon/conversation-lifecycle.ts +3 -5
  265. package/src/daemon/conversation-process.ts +13 -5
  266. package/src/daemon/conversation-runtime-assembly.ts +13 -15
  267. package/src/daemon/conversation-slash.ts +2 -23
  268. package/src/daemon/conversation-surfaces.ts +26 -0
  269. package/src/daemon/conversation-tool-setup.ts +53 -19
  270. package/src/daemon/conversation.ts +131 -20
  271. package/src/daemon/disk-pressure-policy.ts +5 -3
  272. package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +0 -1
  273. package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +0 -1
  274. package/src/daemon/handlers/config-a2a.ts +0 -2
  275. package/src/daemon/handlers/config-channels.ts +15 -16
  276. package/src/daemon/handlers/config-slack-channel.ts +22 -3
  277. package/src/daemon/handlers/conversations.ts +107 -0
  278. package/src/daemon/host-browser-proxy.ts +41 -0
  279. package/src/daemon/lifecycle.ts +55 -27
  280. package/src/daemon/memory-v2-startup.test.ts +131 -0
  281. package/src/daemon/memory-v2-startup.ts +100 -1
  282. package/src/daemon/message-provenance.ts +2 -0
  283. package/src/daemon/message-types/contacts.ts +0 -1
  284. package/src/daemon/message-types/conversations.ts +3 -3
  285. package/src/daemon/message-types/sync.ts +0 -1
  286. package/src/daemon/message-types/web-activity.ts +7 -1
  287. package/src/daemon/message-types/workflows.ts +83 -1
  288. package/src/daemon/orphan-reaper.test.ts +0 -19
  289. package/src/daemon/orphan-reaper.ts +2 -24
  290. package/src/daemon/server.ts +0 -10
  291. package/src/daemon/tool-setup-types.ts +4 -0
  292. package/src/daemon/trust-context.ts +1 -1
  293. package/src/daemon/wake-conversation-ops.ts +1 -0
  294. package/src/events/tool-audit-listener.ts +2 -2
  295. package/src/home/feed-source-enrichment.test.ts +151 -0
  296. package/src/home/feed-source-enrichment.ts +176 -0
  297. package/src/home/relationship-state.ts +2 -4
  298. package/src/instrument.ts +18 -6
  299. package/src/ipc/__tests__/binary-result-ipc.test.ts +81 -0
  300. package/src/ipc/__tests__/clients-list-ipc.test.ts +20 -0
  301. package/src/ipc/assistant-server.ts +37 -4
  302. package/src/ipc/gateway-flag-listener.ts +18 -2
  303. package/src/memory/__tests__/auto-analysis-enqueue.test.ts +5 -16
  304. package/src/memory/__tests__/db-logs-attach.test.ts +122 -0
  305. package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +7 -11
  306. package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +37 -7
  307. package/src/memory/__tests__/memory-retrospective-job.test.ts +229 -401
  308. package/src/memory/__tests__/onboarding-events-store.test.ts +7 -7
  309. package/src/memory/auth-fallback-events-store.ts +2 -2
  310. package/src/memory/auto-analysis-enqueue.ts +3 -5
  311. package/src/memory/bookmark-crud.ts +1 -2
  312. package/src/memory/canonical-guardian-store.ts +39 -1
  313. package/src/memory/conversation-crud.ts +9 -4
  314. package/src/memory/conversation-key-store.ts +17 -2
  315. package/src/memory/conversation-title-service.ts +64 -7
  316. package/src/memory/db-async-query.ts +20 -2
  317. package/src/memory/db-connection.ts +49 -5
  318. package/src/memory/db-init.ts +17 -17
  319. package/src/memory/embedding-backend.ts +38 -1
  320. package/src/memory/embedding-billing-breaker.ts +96 -0
  321. package/src/memory/jobs-store.ts +25 -13
  322. package/src/memory/jobs-worker.ts +54 -1
  323. package/src/memory/lifecycle-events-store.ts +2 -2
  324. package/src/memory/memory-retrospective-constants.ts +4 -4
  325. package/src/memory/memory-retrospective-enqueue.ts +31 -6
  326. package/src/memory/memory-retrospective-job.ts +28 -227
  327. package/src/memory/migrations/129-contact-channels-access-fields.ts +18 -9
  328. package/src/memory/migrations/131-drop-legacy-member-guardian-tables.ts +14 -2
  329. package/src/memory/migrations/289-contact-channels-unique-ext-user.ts +10 -0
  330. package/src/memory/migrations/291-contact-channels-renormalize-addresses.ts +72 -0
  331. package/src/memory/migrations/292-schedule-default-no-reuse-conversation.test.ts +67 -0
  332. package/src/memory/migrations/292-schedule-default-no-reuse-conversation.ts +25 -0
  333. package/src/memory/migrations/293-workflow-journal-leaf-tokens.ts +32 -0
  334. package/src/memory/migrations/294-drop-external-user-id.ts +31 -0
  335. package/src/memory/migrations/295-drop-approval-prompt-ts-tracker.ts +20 -0
  336. package/src/memory/migrations/296-rewrite-balanced-economy-profile-pins.test.ts +110 -0
  337. package/src/memory/migrations/296-rewrite-balanced-economy-profile-pins.ts +68 -0
  338. package/src/memory/migrations/__tests__/131-drop-legacy-member-guardian-tables.test.ts +154 -0
  339. package/src/memory/migrations/__tests__/289-contact-channels-unique-ext-user.test.ts +31 -0
  340. package/src/memory/migrations/__tests__/291-contact-channels-renormalize-addresses.test.ts +341 -0
  341. package/src/memory/migrations/__tests__/run-migrations.test.ts +52 -0
  342. package/src/memory/migrations/index.ts +6 -0
  343. package/src/memory/migrations/run-migrations.ts +41 -0
  344. package/src/memory/migrations/validate-migration-state.ts +1 -1
  345. package/src/memory/onboarding-events-store.ts +3 -3
  346. package/src/memory/schema/contacts.ts +0 -5
  347. package/src/memory/skill-loaded-events-store.test.ts +7 -15
  348. package/src/memory/skill-loaded-events-store.ts +2 -2
  349. package/src/memory/tool-executed-events-store.test.ts +7 -7
  350. package/src/memory/turn-trace-store.test.ts +736 -0
  351. package/src/memory/turn-trace-store.ts +364 -0
  352. package/src/memory/v2/__tests__/consolidation-job.test.ts +8 -0
  353. package/src/memory/v2/__tests__/skill-content.test.ts +30 -0
  354. package/src/memory/v2/consolidation-job.ts +2 -2
  355. package/src/memory/v2/skill-content.ts +25 -7
  356. package/src/memory/v2/skill-store.ts +7 -1
  357. package/src/memory/v3-eval/__tests__/eval-packets.test.ts +248 -0
  358. package/src/memory/v3-eval/eval-packets.ts +546 -0
  359. package/src/messaging/providers/slack/adapter.ts +1 -1
  360. package/src/messaging/providers/slack/api.ts +31 -0
  361. package/src/messaging/providers/slack/send.test.ts +114 -2
  362. package/src/messaging/providers/slack/send.ts +30 -7
  363. package/src/messaging/providers/slack/withdraw.test.ts +200 -0
  364. package/src/messaging/providers/slack/withdraw.ts +161 -0
  365. package/src/notifications/AGENTS.md +2 -0
  366. package/src/notifications/access-request-copy.ts +72 -59
  367. package/src/notifications/adapters/shared.ts +29 -0
  368. package/src/notifications/adapters/slack.ts +58 -103
  369. package/src/notifications/adapters/telegram.ts +2 -20
  370. package/src/notifications/approval-card-data.ts +333 -0
  371. package/src/notifications/broadcaster.ts +16 -3
  372. package/src/notifications/canonical-delivery-recorder.ts +139 -0
  373. package/src/notifications/copy-composer.ts +3 -3
  374. package/src/notifications/decision-engine.ts +4 -2
  375. package/src/notifications/destination-resolver.ts +4 -6
  376. package/src/notifications/guardian-question-mode.ts +10 -0
  377. package/src/notifications/home-feed-side-effect.ts +7 -16
  378. package/src/notifications/notification-utils.ts +19 -20
  379. package/src/notifications/signal.ts +79 -43
  380. package/src/notifications/types.ts +98 -121
  381. package/src/oauth/AGENTS.md +5 -24
  382. package/src/permissions/checker.test.ts +51 -0
  383. package/src/permissions/checker.ts +185 -26
  384. package/src/permissions/ipc-risk-types.ts +24 -0
  385. package/src/permissions/question-prompter.test.ts +27 -0
  386. package/src/permissions/question-prompter.ts +4 -0
  387. package/src/permissions/secret-prompter.ts +15 -1
  388. package/src/platform/client.test.ts +119 -0
  389. package/src/platform/client.ts +66 -0
  390. package/src/platform/consent-cache.test.ts +267 -0
  391. package/src/platform/consent-cache.ts +174 -0
  392. package/src/plugin-api/constants.ts +1 -1
  393. package/src/plugin-api/index.ts +33 -1
  394. package/src/plugin-api/model-profiles.ts +33 -0
  395. package/src/plugin-api/types.ts +50 -2
  396. package/src/plugins/defaults/advisor/__tests__/advisor-gate.test.ts +56 -0
  397. package/src/plugins/defaults/advisor/__tests__/advisor-state-store.test.ts +43 -0
  398. package/src/plugins/defaults/advisor/__tests__/agent-loop-integration.test.ts +137 -0
  399. package/src/plugins/defaults/advisor/__tests__/consult.test.ts +153 -0
  400. package/src/plugins/defaults/advisor/__tests__/hooks.test.ts +138 -0
  401. package/src/plugins/defaults/advisor/__tests__/transcript.test.ts +147 -0
  402. package/src/plugins/defaults/advisor/advisor-gate.ts +29 -0
  403. package/src/plugins/defaults/advisor/advisor-state-store.ts +94 -0
  404. package/src/plugins/defaults/advisor/config.ts +21 -0
  405. package/src/plugins/defaults/advisor/consult.ts +93 -0
  406. package/src/plugins/defaults/advisor/hooks/post-model-call.ts +34 -0
  407. package/src/plugins/defaults/advisor/hooks/pre-model-call.ts +30 -0
  408. package/src/plugins/defaults/advisor/hooks/user-prompt-submit.ts +19 -0
  409. package/src/plugins/defaults/advisor/package.json +14 -0
  410. package/src/plugins/defaults/advisor/steering.ts +67 -0
  411. package/src/plugins/defaults/advisor/tools/advisor.ts +65 -0
  412. package/src/plugins/defaults/advisor/transcript.ts +76 -0
  413. package/src/plugins/defaults/compaction/window-manager.ts +18 -0
  414. package/src/plugins/defaults/index.ts +60 -0
  415. package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +22 -9
  416. package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +2 -2
  417. package/src/plugins/defaults/memory-retrieval/injectors.ts +4 -4
  418. package/src/plugins/defaults/memory-retrieval/tail-reinjection-strip.ts +64 -0
  419. package/src/plugins/defaults/memory-retrieval/unified-turn-context.ts +29 -21
  420. package/src/plugins/defaults/memory-v3-shadow/__tests__/carry-integration.test.ts +1 -0
  421. package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +1 -0
  422. package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +129 -9
  423. package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +31 -4
  424. package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +77 -2
  425. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +1 -0
  426. package/src/plugins/defaults/memory-v3-shadow/injector.ts +7 -10
  427. package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +144 -11
  428. package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +32 -20
  429. package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +56 -3
  430. package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +23 -2
  431. package/src/plugins/defaults/surface-completion-nudge/hooks/post-model-call.ts +276 -0
  432. package/src/plugins/defaults/surface-completion-nudge/hooks/stop.ts +22 -0
  433. package/src/plugins/defaults/surface-completion-nudge/nudge-state-store.ts +46 -0
  434. package/src/plugins/defaults/surface-completion-nudge/package.json +14 -0
  435. package/src/plugins/defaults/task-progress-nudge/hooks/post-tool-use.ts +3 -13
  436. package/src/plugins/defaults/title-generate/hooks/stop.ts +56 -21
  437. package/src/prompts/persona-resolver.ts +14 -4
  438. package/src/prompts/templates/system-sections.ts +7 -2
  439. package/src/providers/__tests__/count-tokens-forwarding.test.ts +98 -0
  440. package/src/providers/__tests__/provider-env-vars.test.ts +6 -0
  441. package/src/providers/__tests__/provider-secret-catalog.test.ts +1 -0
  442. package/src/providers/__tests__/retry-callsite.test.ts +176 -0
  443. package/src/providers/anthropic/client.ts +230 -175
  444. package/src/providers/atlascloud/client.ts +85 -0
  445. package/src/providers/call-site-routing.ts +10 -0
  446. package/src/providers/fetch-provider-catalog.ts +85 -0
  447. package/src/providers/gemini/client.ts +43 -0
  448. package/src/providers/inference/adapter-factory.ts +3 -0
  449. package/src/providers/model-catalog.ts +58 -0
  450. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +33 -0
  451. package/src/providers/openai/chat-completions-provider.ts +7 -0
  452. package/src/providers/openai/responses-provider.ts +10 -0
  453. package/src/providers/provider-send-message.ts +11 -3
  454. package/src/providers/ratelimit.ts +9 -0
  455. package/src/providers/retry.ts +62 -12
  456. package/src/providers/search-provider-catalog.ts +10 -0
  457. package/src/providers/types.ts +16 -0
  458. package/src/providers/usage-tracking.ts +7 -0
  459. package/src/providers/weak-open-model.ts +22 -0
  460. package/src/runtime/AGENTS.md +0 -1
  461. package/src/runtime/__tests__/agent-wake.test.ts +227 -0
  462. package/src/runtime/__tests__/client-health.test.ts +44 -0
  463. package/src/runtime/access-request-helper.ts +21 -53
  464. package/src/runtime/actor-trust-resolver.ts +59 -63
  465. package/src/runtime/agent-wake.ts +71 -0
  466. package/src/runtime/assistant-event-hub.ts +18 -4
  467. package/src/runtime/auth/__tests__/route-policy.test.ts +12 -0
  468. package/src/runtime/auth/require-bound-guardian.ts +1 -4
  469. package/src/runtime/btw-sidechain.ts +3 -6
  470. package/src/runtime/capabilities.test.ts +120 -0
  471. package/src/runtime/capabilities.ts +197 -0
  472. package/src/runtime/channel-approval-types.ts +22 -45
  473. package/src/runtime/channel-invite-transports/telegram.ts +4 -4
  474. package/src/runtime/channel-retry-sweep.ts +1 -0
  475. package/src/runtime/channel-verification-service.ts +3 -3
  476. package/src/runtime/client-health.ts +26 -0
  477. package/src/runtime/confirmation-request-guardian-bridge.ts +38 -29
  478. package/src/runtime/effective-capabilities.test.ts +128 -0
  479. package/src/runtime/effective-capabilities.ts +84 -0
  480. package/src/runtime/guardian-reply-router.ts +106 -21
  481. package/src/runtime/invite-redemption-service.ts +9 -25
  482. package/src/runtime/migrations/__tests__/vbundle-builder-fd-leak.test.ts +126 -0
  483. package/src/runtime/migrations/vbundle-builder.ts +49 -20
  484. package/src/runtime/pending-interactions.ts +35 -0
  485. package/src/runtime/routes/__tests__/client-routes.test.ts +13 -0
  486. package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +67 -0
  487. package/src/runtime/routes/__tests__/plugins-routes.test.ts +383 -1
  488. package/src/runtime/routes/app-routes.ts +1 -1
  489. package/src/runtime/routes/approval-routes.ts +40 -9
  490. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +2 -2
  491. package/src/runtime/routes/assets/vellum-design-system.css +1959 -0
  492. package/src/runtime/routes/browser-tabs-routes.ts +9 -0
  493. package/src/runtime/routes/btw-routes.ts +1 -27
  494. package/src/runtime/routes/canonical-guardian-expiry-sweep.ts +17 -8
  495. package/src/runtime/routes/client-routes.ts +10 -0
  496. package/src/runtime/routes/contact-routes.ts +31 -8
  497. package/src/runtime/routes/conversation-compaction-routes.ts +1 -1
  498. package/src/runtime/routes/conversation-management-routes.ts +80 -1
  499. package/src/runtime/routes/conversation-query-routes.ts +68 -22
  500. package/src/runtime/routes/conversation-routes.ts +39 -14
  501. package/src/runtime/routes/credential-routes.ts +40 -16
  502. package/src/runtime/routes/empty-state-greeting-cache.ts +1 -2
  503. package/src/runtime/routes/events-routes.ts +1 -3
  504. package/src/runtime/routes/guardian-approval-interception.ts +14 -73
  505. package/src/runtime/routes/guardian-approval-prompt.ts +22 -4
  506. package/src/runtime/routes/home-feed-routes.ts +8 -3
  507. package/src/runtime/routes/identity-routes.ts +1 -296
  508. package/src/runtime/routes/inbound-message-handler.ts +214 -228
  509. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +89 -7
  510. package/src/runtime/routes/inbound-stages/admission-policy.test.ts +154 -0
  511. package/src/runtime/routes/inbound-stages/admission-policy.ts +140 -0
  512. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +3 -3
  513. package/src/runtime/routes/inbound-stages/background-dispatch.ts +11 -6
  514. package/src/runtime/routes/inbound-stages/escalation-intercept.ts +1 -2
  515. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +1 -2
  516. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +7 -7
  517. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +47 -28
  518. package/src/runtime/routes/inbound-stages/reaction-intercept.ts +358 -0
  519. package/src/runtime/routes/index.ts +2 -0
  520. package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +8 -0
  521. package/src/runtime/routes/integrations/slack/channel.ts +36 -0
  522. package/src/runtime/routes/internal-telemetry-routes.ts +1 -1
  523. package/src/runtime/routes/mcp-auth-routes.ts +233 -41
  524. package/src/runtime/routes/memory-eval-routes.ts +87 -0
  525. package/src/runtime/routes/notification-routes.ts +122 -133
  526. package/src/runtime/routes/platform-routes.ts +2 -2
  527. package/src/runtime/routes/plugins-routes.ts +366 -11
  528. package/src/runtime/routes/schedule-routes.ts +0 -22
  529. package/src/runtime/routes/secret-routes.ts +15 -0
  530. package/src/runtime/routes/surface-action-routes.ts +2 -1
  531. package/src/runtime/routes/tool-call-question-enrichment.test.ts +146 -0
  532. package/src/runtime/routes/tool-call-question-enrichment.ts +66 -0
  533. package/src/runtime/routes/workflow-routes.test.ts +229 -44
  534. package/src/runtime/routes/workflow-routes.ts +131 -29
  535. package/src/runtime/routes/workspace-greetings.ts +55 -0
  536. package/src/runtime/sync/resource-sync-events.ts +1 -11
  537. package/src/runtime/tool-grant-request-helper.ts +18 -16
  538. package/src/runtime/trust-context-resolver.ts +8 -5
  539. package/src/schedule/inference-profile.ts +2 -14
  540. package/src/schedule/schedule-store.ts +1 -1
  541. package/src/schedule/scheduler-types.ts +5 -1
  542. package/src/security/__tests__/provider-key-env-fallback.test.ts +6 -0
  543. package/src/security/secret-patterns.ts +3 -0
  544. package/src/subagent/manager.ts +17 -4
  545. package/src/subagent/types.ts +6 -0
  546. package/src/telemetry/trace-collection-policy.test.ts +28 -0
  547. package/src/telemetry/trace-collection-policy.ts +30 -0
  548. package/src/telemetry/types.ts +89 -0
  549. package/src/telemetry/usage-telemetry-reporter.test.ts +586 -36
  550. package/src/telemetry/usage-telemetry-reporter.ts +148 -41
  551. package/src/tools/AGENTS.md +3 -3
  552. package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +31 -0
  553. package/src/tools/browser/browser-execution.ts +30 -19
  554. package/src/tools/document/document-tool.ts +30 -6
  555. package/src/tools/executor.ts +5 -3
  556. package/src/tools/host-terminal/host-shell.ts +5 -4
  557. package/src/tools/memory/register.ts +2 -2
  558. package/src/tools/network/__tests__/web-fetch-firecrawl.test.ts +360 -0
  559. package/src/tools/network/__tests__/web-search.test.ts +143 -0
  560. package/src/tools/network/web-fetch.ts +372 -1
  561. package/src/tools/network/web-search-error.ts +1 -1
  562. package/src/tools/network/web-search.ts +213 -10
  563. package/src/tools/permission-checker.ts +4 -3
  564. package/src/tools/registry.ts +20 -0
  565. package/src/tools/schedule/create.ts +7 -12
  566. package/src/tools/schedule/update.ts +4 -11
  567. package/src/tools/shared/filesystem/path-policy.ts +39 -13
  568. package/src/tools/side-effects.ts +2 -17
  569. package/src/tools/skills/execute.ts +99 -2
  570. package/src/tools/skills/load.ts +43 -2
  571. package/src/tools/subagent/spawn.ts +64 -15
  572. package/src/tools/terminal/shell.ts +10 -4
  573. package/src/tools/tool-approval-handler.ts +18 -13
  574. package/src/tools/tool-manifest.ts +0 -2
  575. package/src/tools/types.ts +10 -0
  576. package/src/tools/ui-surface/definitions.ts +64 -3
  577. package/src/tools/verification-control-plane-policy.ts +3 -1
  578. package/src/tools/workflows/run-workflow.test.ts +8 -18
  579. package/src/tools/workflows/run-workflow.ts +1 -0
  580. package/src/util/disk-usage.ts +78 -23
  581. package/src/util/logs-db-path.ts +22 -0
  582. package/src/util/platform.ts +10 -3
  583. package/src/watcher/telemetry.ts +2 -2
  584. package/src/workflows/capabilities.ts +2 -3
  585. package/src/workflows/engine.test.ts +175 -1
  586. package/src/workflows/engine.ts +82 -0
  587. package/src/workflows/journal-store.test.ts +70 -0
  588. package/src/workflows/journal-store.ts +18 -3
  589. package/src/workflows/run-manager.test.ts +171 -28
  590. package/src/workflows/run-manager.ts +66 -24
  591. package/src/workspace/migrations/105-enable-memory-v3-live-for-new-workspaces.ts +63 -0
  592. package/src/workspace/migrations/106-drop-collect-usage-data.ts +47 -0
  593. package/src/workspace/migrations/107-drop-send-diagnostics.ts +47 -0
  594. package/src/workspace/migrations/108-drop-balanced-economy-profile.ts +129 -0
  595. package/src/workspace/migrations/registry.ts +8 -0
  596. package/src/__tests__/app-control-no-global-cgevent.test.ts +0 -98
  597. package/src/__tests__/credential-security-e2e.test.ts +0 -362
  598. package/src/__tests__/credential-vault-unit.test.ts +0 -1528
  599. package/src/__tests__/credential-vault.test.ts +0 -1706
  600. package/src/__tests__/identity-intro-cache.test.ts +0 -315
  601. package/src/__tests__/secret-onetime-send.test.ts +0 -182
  602. package/src/cli/commands/__tests__/task.test.ts +0 -914
  603. package/src/cli/commands/task.ts +0 -771
  604. package/src/config/bundled-skills/personal-page/SKILL.md +0 -57
  605. package/src/config/bundled-skills/personal-page/TOOLS.json +0 -27
  606. package/src/config/bundled-skills/personal-page/tools/app-refresh.ts +0 -17
  607. package/src/config/preloaded-apps/personal-page/src/components/About.tsx +0 -22
  608. package/src/config/preloaded-apps/personal-page/src/components/App.tsx +0 -16
  609. package/src/config/preloaded-apps/personal-page/src/components/Features.tsx +0 -77
  610. package/src/config/preloaded-apps/personal-page/src/components/Hero.tsx +0 -57
  611. package/src/config/preloaded-apps/personal-page/src/components/Pending.tsx +0 -28
  612. package/src/config/preloaded-apps/personal-page/src/components/animations.tsx +0 -234
  613. package/src/config/preloaded-apps/personal-page/src/components/icons.tsx +0 -48
  614. package/src/config/preloaded-apps/personal-page/src/components/media.ts +0 -16
  615. package/src/config/preloaded-apps/personal-page/src/index.html +0 -20
  616. package/src/config/preloaded-apps/personal-page/src/main.tsx +0 -7
  617. package/src/config/preloaded-apps/personal-page/src/profile-data.ts +0 -82
  618. package/src/config/preloaded-apps/personal-page/src/styles.css +0 -759
  619. package/src/memory/__tests__/preloaded-apps.test.ts +0 -85
  620. package/src/memory/preloaded-apps.ts +0 -116
  621. package/src/notifications/tool-approval-copy.ts +0 -142
  622. package/src/runtime/routes/approval-prompt-ts-tracker.ts +0 -78
  623. package/src/runtime/routes/identity-intro-cache.ts +0 -172
  624. package/src/tools/credentials/vault.ts +0 -712
@@ -5,21 +5,18 @@
5
5
  * shared ROUTES array. They are read/abort surfaces over the workflow run
6
6
  * manager and the saved-workflow library — a human (via the `vellum workflows`
7
7
  * CLI or the app) can inspect runs and abort an in-flight one.
8
- *
9
- * Every handler is gated on the `workflows` feature flag: when it is off, the
10
- * routes behave as if they do not exist (404), so disabling the flag fully
11
- * hides the surface.
12
8
  */
13
9
 
14
10
  import { z } from "zod";
15
11
 
16
- import { isAssistantFeatureFlagEnabled } from "../../config/assistant-feature-flags.js";
17
- import { getConfig } from "../../config/loader.js";
18
- import type { AssistantConfig } from "../../config/schema.js";
19
12
  import { getAutoApproveThreshold } from "../../permissions/gateway-threshold-reader.js";
20
13
  import { isFullAccessThreshold } from "../../permissions/threshold.js";
21
14
  import { manifestGrantsSideEffects } from "../../workflows/capabilities.js";
22
- import type { WorkflowRun } from "../../workflows/journal-store.js";
15
+ import {
16
+ getJournal,
17
+ type WorkflowJournalEntry,
18
+ type WorkflowRun,
19
+ } from "../../workflows/journal-store.js";
23
20
  import { listWorkflows } from "../../workflows/library.js";
24
21
  import {
25
22
  getWorkflowRunManager,
@@ -46,19 +43,16 @@ export interface WorkflowRoutesDeps {
46
43
  "list" | "status" | "abort" | "resume"
47
44
  >;
48
45
  listWorkflows: typeof listWorkflows;
49
- getConfig: () => AssistantConfig;
50
- isFlagEnabled: (config: AssistantConfig) => boolean;
51
46
  getAutoApproveThreshold: typeof getAutoApproveThreshold;
47
+ getJournal: typeof getJournal;
52
48
  }
53
49
 
54
50
  function defaultDeps(): WorkflowRoutesDeps {
55
51
  return {
56
52
  getManager: getWorkflowRunManager,
57
53
  listWorkflows,
58
- getConfig,
59
- isFlagEnabled: (config) =>
60
- isAssistantFeatureFlagEnabled("workflows", config),
61
54
  getAutoApproveThreshold,
55
+ getJournal,
62
56
  };
63
57
  }
64
58
 
@@ -105,21 +99,33 @@ const savedWorkflowSchema = z.object({
105
99
  path: z.string(),
106
100
  });
107
101
 
102
+ const workflowLeafSchema = z.object({
103
+ seq: z.number(),
104
+ kind: z.enum(["agent", "workflow"]),
105
+ label: z.string().optional(),
106
+ phase: z.string().optional(),
107
+ promptSummary: z.string().optional(),
108
+ status: z.string(),
109
+ resultSummary: z.string().optional(),
110
+ inputTokens: z.number().optional(),
111
+ outputTokens: z.number().optional(),
112
+ createdAt: z.number().nullable(),
113
+ });
114
+
115
+ const workflowJournalSchema = z.object({
116
+ runId: z.string(),
117
+ status: z.enum(VALID_RUN_STATUSES).optional(),
118
+ agentsSpawned: z.number().optional(),
119
+ inputTokens: z.number().optional(),
120
+ outputTokens: z.number().optional(),
121
+ phase: z.string().optional(),
122
+ leaves: z.array(workflowLeafSchema),
123
+ });
124
+
108
125
  // ---------------------------------------------------------------------------
109
126
  // Handlers (transport-agnostic)
110
127
  // ---------------------------------------------------------------------------
111
128
 
112
- /**
113
- * Throw a 404 when the `workflows` flag is off, hiding the whole surface. The
114
- * NotFoundError (rather than a 403) keeps a disabled-flag indistinguishable
115
- * from a route that does not exist.
116
- */
117
- function assertFlagEnabled(): void {
118
- if (!deps.isFlagEnabled(deps.getConfig())) {
119
- throw new NotFoundError("Workflows are not enabled.");
120
- }
121
- }
122
-
123
129
  /**
124
130
  * The single compact-run wire contract, inferred from {@link workflowRunSchema}.
125
131
  * The HTTP routes return this shape; other producers (e.g. the CLI's client-side
@@ -145,6 +151,64 @@ export function toWireRun(run: WorkflowRun): WorkflowRunWire {
145
151
  };
146
152
  }
147
153
 
154
+ /**
155
+ * Render an arbitrary journal value (prompt, result, `{ error }`) into a short
156
+ * single-string preview, truncating to `max` characters. Objects are
157
+ * JSON-stringified so a structured result still produces a readable summary.
158
+ */
159
+ function summarize(value: unknown, max = 200): string | undefined {
160
+ if (value === undefined || value === null) return undefined;
161
+ let text: string;
162
+ if (typeof value === "string") {
163
+ text = value;
164
+ } else {
165
+ try {
166
+ text = JSON.stringify(value);
167
+ } catch {
168
+ return undefined;
169
+ }
170
+ }
171
+ return text.length > max ? `${text.slice(0, max)}…` : text;
172
+ }
173
+
174
+ /**
175
+ * Project a journal entry into the bounded leaf wire shape, dropping the bulky
176
+ * raw `request`/`result` payloads in favor of short summaries. The `request`
177
+ * and `result` columns are `unknown`, so read their fields defensively.
178
+ */
179
+ export function toWireLeaf(
180
+ entry: WorkflowJournalEntry,
181
+ ): z.infer<typeof workflowLeafSchema> {
182
+ const request =
183
+ entry.request && typeof entry.request === "object"
184
+ ? (entry.request as Record<string, unknown>)
185
+ : undefined;
186
+ const opts =
187
+ request?.opts && typeof request.opts === "object"
188
+ ? (request.opts as Record<string, unknown>)
189
+ : undefined;
190
+ const label = typeof opts?.label === "string" ? opts.label : undefined;
191
+ const phase = typeof opts?.phase === "string" ? opts.phase : undefined;
192
+ const promptSummary = summarize(request?.prompt);
193
+ const resultSummary = summarize(entry.result);
194
+ return {
195
+ seq: entry.seq,
196
+ kind: entry.kind,
197
+ ...(label !== undefined ? { label } : {}),
198
+ ...(phase !== undefined ? { phase } : {}),
199
+ ...(promptSummary !== undefined ? { promptSummary } : {}),
200
+ status: entry.status,
201
+ ...(resultSummary !== undefined ? { resultSummary } : {}),
202
+ ...(entry.inputTokens !== undefined
203
+ ? { inputTokens: entry.inputTokens }
204
+ : {}),
205
+ ...(entry.outputTokens !== undefined
206
+ ? { outputTokens: entry.outputTokens }
207
+ : {}),
208
+ createdAt: entry.createdAt,
209
+ };
210
+ }
211
+
148
212
  function parseLimit(raw: string | undefined): number | undefined {
149
213
  if (raw === undefined) return undefined;
150
214
  const n = Number(raw);
@@ -162,7 +226,6 @@ function parseStatus(
162
226
  }
163
227
 
164
228
  function handleListRuns(queryParams: Record<string, string>) {
165
- assertFlagEnabled();
166
229
  const limit = parseLimit(queryParams.limit);
167
230
  const status = parseStatus(queryParams.status);
168
231
  const runs = deps.getManager().list({
@@ -173,7 +236,6 @@ function handleListRuns(queryParams: Record<string, string>) {
173
236
  }
174
237
 
175
238
  function handleGetRun(id: string) {
176
- assertFlagEnabled();
177
239
  const run = deps.getManager().status(id);
178
240
  if (!run) {
179
241
  throw new NotFoundError(`Workflow run ${id} not found`);
@@ -181,8 +243,29 @@ function handleGetRun(id: string) {
181
243
  return toWireRun(run);
182
244
  }
183
245
 
246
+ function handleGetRunJournal(id: string) {
247
+ const run = deps.getManager().status(id);
248
+ if (!run) {
249
+ throw new NotFoundError(`Workflow run ${id} not found`);
250
+ }
251
+ return {
252
+ runId: run.id,
253
+ status: run.status,
254
+ agentsSpawned: run.agentsSpawned,
255
+ inputTokens: run.inputTokens,
256
+ outputTokens: run.outputTokens,
257
+ // Only agent leaves; the live `workflow_leaf_*` stream is emitted solely for
258
+ // agent leaves (nested workflow resolutions never reach it), so the
259
+ // backfilled set must match. `kind: "workflow"` entries carry no label and
260
+ // would render as phantom unlabeled nodes.
261
+ leaves: deps
262
+ .getJournal(id)
263
+ .filter((entry) => entry.kind === "agent")
264
+ .map(toWireLeaf),
265
+ };
266
+ }
267
+
184
268
  function handleAbortRun(id: string) {
185
- assertFlagEnabled();
186
269
  // status() is the source of truth for existence; abort() itself is a no-op
187
270
  // for unknown/finished runs, so 404 on an unknown id is surfaced here.
188
271
  const manager = deps.getManager();
@@ -194,7 +277,6 @@ function handleAbortRun(id: string) {
194
277
  }
195
278
 
196
279
  async function handleResumeRun(id: string) {
197
- assertFlagEnabled();
198
280
  const manager = deps.getManager();
199
281
  // status() is the source of truth for existence, so 404 an unknown id here
200
282
  // (matching abort) rather than letting resume's "not_found" reach the client
@@ -245,7 +327,6 @@ async function handleResumeRun(id: string) {
245
327
  }
246
328
 
247
329
  function handleListSavedWorkflows() {
248
- assertFlagEnabled();
249
330
  return { workflows: deps.listWorkflows() };
250
331
  }
251
332
 
@@ -299,6 +380,27 @@ export const ROUTES: RouteDefinition[] = [
299
380
  additionalResponses: { "404": { description: "Run not found" } },
300
381
  handler: ({ pathParams }: RouteHandlerArgs) => handleGetRun(pathParams!.id),
301
382
  },
383
+ {
384
+ operationId: "getWorkflowRunJournal",
385
+ endpoint: "workflows/runs/:id/journal",
386
+ method: "GET",
387
+ // Unlike the run list/status routes (settings metadata), the journal returns
388
+ // per-leaf prompt/result summaries — conversation/work-product content — so it
389
+ // requires `chat.read`, matching the subagent-detail route (the analogous
390
+ // content surface), not the `settings.read` used by the management routes.
391
+ policy: {
392
+ requiredScopes: ["chat.read"],
393
+ allowedPrincipalTypes: ACTOR_PRINCIPALS,
394
+ },
395
+ summary: "Get workflow run journal",
396
+ description:
397
+ "Return a workflow run's leaf journal as bounded per-leaf summaries (one entry per finished leaf).",
398
+ tags: ["workflows"],
399
+ responseBody: workflowJournalSchema,
400
+ additionalResponses: { "404": { description: "Run not found" } },
401
+ handler: ({ pathParams }: RouteHandlerArgs) =>
402
+ handleGetRunJournal(pathParams!.id),
403
+ },
302
404
  {
303
405
  operationId: "abortWorkflowRun",
304
406
  endpoint: "workflows/runs/:id/abort",
@@ -0,0 +1,55 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+
3
+ import { getWorkspacePromptPath } from "../../util/platform.js";
4
+
5
+ function readWorkspaceFile(name: string): string {
6
+ try {
7
+ const path = getWorkspacePromptPath(name);
8
+ if (!existsSync(path)) return "";
9
+ return readFileSync(path, "utf-8");
10
+ } catch {
11
+ return "";
12
+ }
13
+ }
14
+
15
+ /**
16
+ * Parse the `## Greetings` section from SOUL.md. Returns bullet items as an
17
+ * array of strings, or `null` if the section is missing or empty.
18
+ */
19
+ export function parseGreetingsSection(content: string): string[] | null {
20
+ let inSection = false;
21
+ let sectionLevel: number | null = null;
22
+ const greetings: string[] = [];
23
+
24
+ for (const line of content.split("\n")) {
25
+ const trimmed = line.trim();
26
+ const heading = /^(#{1,6})\s+(.+?)\s*#*$/.exec(trimmed);
27
+ if (heading) {
28
+ const level = heading[1]!.length;
29
+ const title = heading[2]!.trim();
30
+ if (inSection) {
31
+ if (sectionLevel !== null && level <= sectionLevel) break;
32
+ continue;
33
+ }
34
+ if (level === 2 && /^greetings$/i.test(title)) {
35
+ inSection = true;
36
+ sectionLevel = level;
37
+ }
38
+ continue;
39
+ }
40
+ if (!inSection) continue;
41
+ const bullet = /^(?:[-*+]\s+|\d+[.)]\s+)(.+)$/.exec(trimmed);
42
+ const greeting = bullet?.[1]?.trim();
43
+ if (greeting) {
44
+ greetings.push(greeting);
45
+ }
46
+ }
47
+
48
+ return greetings.length > 0 ? greetings : null;
49
+ }
50
+
51
+ export function readWorkspaceGreetings(): string[] | null {
52
+ const soulContent = readWorkspaceFile("SOUL.md");
53
+ if (!soulContent) return null;
54
+ return parseGreetingsSection(soulContent);
55
+ }
@@ -29,17 +29,7 @@ export function publishIdentityChanged(
29
29
  emoji: fields.emoji,
30
30
  home: fields.home,
31
31
  });
32
- void publishSyncInvalidation(
33
- [SYNC_TAGS.assistantIdentity, SYNC_TAGS.assistantIdentityIntro],
34
- originClientId,
35
- );
36
- }
37
-
38
- export function publishIdentityIntroChanged(originClientId?: string): void {
39
- void publishSyncInvalidation(
40
- [SYNC_TAGS.assistantIdentityIntro],
41
- originClientId,
42
- );
32
+ void publishSyncInvalidation([SYNC_TAGS.assistantIdentity], originClientId);
43
33
  }
44
34
 
45
35
  export function publishConfigChanged(originClientId?: string): void {
@@ -13,12 +13,14 @@
13
13
 
14
14
  import type { ChannelId } from "../channels/types.js";
15
15
  import {
16
- createCanonicalGuardianDelivery,
17
16
  createCanonicalGuardianRequest,
18
17
  listCanonicalGuardianRequests,
19
18
  } from "../memory/canonical-guardian-store.js";
19
+ import {
20
+ recordApprovalCardDelivery,
21
+ recordGuardianRequestDeliveries,
22
+ } from "../notifications/canonical-delivery-recorder.js";
20
23
  import { emitNotificationSignal } from "../notifications/emit-signal.js";
21
- import type { NotificationSourceChannel } from "../notifications/signal.js";
22
24
  import { getLogger } from "../util/logger.js";
23
25
  import { getGuardianBinding } from "./channel-verification-service.js";
24
26
  import { GUARDIAN_APPROVAL_TTL_MS } from "./routes/channel-route-shared.js";
@@ -140,12 +142,16 @@ export function createOrReuseToolGrantRequest(
140
142
  canonicalRequest.requestCode ??
141
143
  canonicalRequest.id.slice(0, 6).toUpperCase();
142
144
 
145
+ // The vellum delivery row is created up front in onConversationCreated so the
146
+ // in-app client sees it immediately; the post-resolve recorder reuses it.
147
+ let vellumDeliveryId: string | undefined;
148
+
143
149
  // Emit notification so guardian is alerted. Uses 'guardian.question' as
144
150
  // sourceEventName so that existing request-code guidance in the notification
145
151
  // pipeline is preserved.
146
152
  const signalPromise = emitNotificationSignal({
147
153
  sourceEventName: "guardian.question",
148
- sourceChannel: sourceChannel as NotificationSourceChannel,
154
+ sourceChannel,
149
155
  sourceContextId: conversationId,
150
156
  requiresConversation: true,
151
157
  attentionHints: {
@@ -167,25 +173,21 @@ export function createOrReuseToolGrantRequest(
167
173
  },
168
174
  dedupeKey: `tool-grant-request:${canonicalRequest.id}`,
169
175
  onConversationCreated: (info) => {
170
- createCanonicalGuardianDelivery({
176
+ vellumDeliveryId = recordApprovalCardDelivery({
171
177
  requestId: canonicalRequest.id,
172
- destinationChannel: "vellum",
173
- destinationConversationId: info.conversationId,
174
- });
178
+ channel: "vellum",
179
+ conversationId: info.conversationId,
180
+ })?.id;
175
181
  },
176
182
  });
177
183
 
178
184
  // Record deliveries from the notification pipeline results (fire-and-forget).
179
185
  void signalPromise.then((signalResult) => {
180
- for (const result of signalResult.deliveryResults) {
181
- if (result.channel === "vellum") continue; // handled in onConversationCreated
182
- createCanonicalGuardianDelivery({
183
- requestId: canonicalRequest.id,
184
- destinationChannel: result.channel,
185
- destinationChatId:
186
- result.destination.length > 0 ? result.destination : undefined,
187
- });
188
- }
186
+ recordGuardianRequestDeliveries({
187
+ requestId: canonicalRequest.id,
188
+ deliveryResults: signalResult.deliveryResults,
189
+ vellumDeliveryId,
190
+ });
189
191
  });
190
192
 
191
193
  log.info(
@@ -16,6 +16,7 @@ import {
16
16
  type ResolveActorTrustInput,
17
17
  toTrustContext,
18
18
  } from "./actor-trust-resolver.js";
19
+ import { resolveCapabilities } from "./capabilities.js";
19
20
 
20
21
  /**
21
22
  * Resolve route-level trust context from canonical identity state.
@@ -64,14 +65,15 @@ export interface RoutingState {
64
65
  * Compute the routing state for a channel actor turn.
65
66
  *
66
67
  * Guardian actors are always interactive (they self-approve).
67
- * Trusted contacts are only interactive when a guardian binding exists
68
- * to receive approval notifications. Unknown actors are never interactive.
68
+ * Trusted and unverified contacts are only interactive when a guardian
69
+ * binding exists to receive approval notifications. Unknown actors are
70
+ * never interactive.
69
71
  */
70
72
  export function resolveRoutingState(
71
73
  ctx: Pick<TrustContext, "trustClass" | "guardianExternalUserId">,
72
74
  ): RoutingState {
75
+ const caps = resolveCapabilities(ctx.trustClass);
73
76
  const isGuardian = ctx.trustClass === "guardian";
74
- const isTrustedContact = ctx.trustClass === "trusted_contact";
75
77
 
76
78
  // Guardians self-approve — they are always interactive and route-resolvable.
77
79
  if (isGuardian) {
@@ -82,12 +84,13 @@ export function resolveRoutingState(
82
84
  };
83
85
  }
84
86
 
85
- // Trusted contacts can be interactive only if a guardian destination
87
+ // Identity-known non-guardian contacts (trusted_contact /
88
+ // unverified_contact) can be interactive only if a guardian destination
86
89
  // exists. The guardian binding populates guardianExternalUserId during
87
90
  // trust resolution; its presence means there is a verified guardian
88
91
  // to route approval notifications to.
89
92
  const guardianRouteResolvable = !!ctx.guardianExternalUserId;
90
- if (isTrustedContact) {
93
+ if (caps.mayBeInteractive) {
91
94
  return {
92
95
  canBeInteractive: true,
93
96
  guardianRouteResolvable,
@@ -1,4 +1,4 @@
1
- import { getConfigReadOnly } from "../config/loader.js";
1
+ import { validateInferenceProfileKey } from "../config/inference-profile-validation.js";
2
2
 
3
3
  /**
4
4
  * Validate a schedule's inference-profile key against the configured
@@ -12,17 +12,5 @@ import { getConfigReadOnly } from "../config/loader.js";
12
12
  export function validateScheduleInferenceProfile(
13
13
  profile: string,
14
14
  ): string | null {
15
- if (!profile.trim()) {
16
- return "inferenceProfile must be a non-empty string";
17
- }
18
- const profiles = getConfigReadOnly().llm?.profiles ?? {};
19
- if (!Object.prototype.hasOwnProperty.call(profiles, profile)) {
20
- const available = Object.keys(profiles).sort();
21
- const hint =
22
- available.length > 0
23
- ? ` Available profiles: ${available.join(", ")}.`
24
- : " No profiles defined in llm.profiles.";
25
- return `Inference profile "${profile}" is not defined in llm.profiles.${hint}`;
26
- }
27
- return null;
15
+ return validateInferenceProfileKey(profile);
28
16
  }
@@ -158,7 +158,7 @@ export function createSchedule(params: {
158
158
  const routingIntent = params.routingIntent ?? "all_channels";
159
159
  const routingHints = params.routingHints ?? {};
160
160
  const quiet = params.quiet ?? false;
161
- const reuseConversation = params.reuseConversation ?? !isOneShot;
161
+ const reuseConversation = params.reuseConversation ?? false;
162
162
  const maxRetries = params.maxRetries ?? 3;
163
163
  const retryBackoffMs = params.retryBackoffMs ?? 60000;
164
164
  const timeoutMs = params.timeoutMs ?? null;
@@ -7,7 +7,11 @@
7
7
  import type { LLMCallSite } from "../config/schemas/llm.js";
8
8
 
9
9
  export interface ScheduleMessageOptions {
10
- trustClass?: "guardian" | "trusted_contact" | "unknown";
10
+ trustClass?:
11
+ | "guardian"
12
+ | "trusted_contact"
13
+ | "unverified_contact"
14
+ | "unknown";
11
15
  taskRunId?: string;
12
16
  /**
13
17
  * Optional LLM call-site identifier propagated to the per-call provider
@@ -53,6 +53,7 @@ describe("getProviderKeyAsync env-var fallback (regression #27126)", () => {
53
53
  "BRAVE_API_KEY",
54
54
  "PERPLEXITY_API_KEY",
55
55
  "TAVILY_API_KEY",
56
+ "FIRECRAWL_API_KEY",
56
57
  "ANTHROPIC_API_KEY",
57
58
  "OPENAI_API_KEY",
58
59
  ];
@@ -103,6 +104,11 @@ describe("getProviderKeyAsync env-var fallback (regression #27126)", () => {
103
104
  expect(await getProviderKeyAsync("tavily")).toBe("tavily-env-test");
104
105
  });
105
106
 
107
+ test("returns FIRECRAWL_API_KEY from process.env when secure store is empty", async () => {
108
+ process.env.FIRECRAWL_API_KEY = "fc-env-test";
109
+ expect(await getProviderKeyAsync("firecrawl")).toBe("fc-env-test");
110
+ });
111
+
106
112
  test("returns ANTHROPIC_API_KEY from process.env when secure store is empty (LLM regression)", async () => {
107
113
  process.env.ANTHROPIC_API_KEY = "anthropic-env-test";
108
114
  expect(await getProviderKeyAsync("anthropic")).toBe("anthropic-env-test");
@@ -133,4 +133,7 @@ export const PREFIX_PATTERNS: SecretPrefixPattern[] = [
133
133
 
134
134
  // -- Tavily --
135
135
  { label: "Tavily API Key", regex: /tvly-[A-Za-z0-9]{20,}/ },
136
+
137
+ // -- Firecrawl --
138
+ { label: "Firecrawl API Key", regex: /fc-[A-Za-z0-9]{20,}/ },
136
139
  ];
@@ -237,10 +237,17 @@ export class SubagentManager {
237
237
  config.systemPromptOverride ??
238
238
  buildSubagentSystemPrompt({ ...config, id: subagentId }, role);
239
239
  }
240
- const maxTokens = resolveCallSiteConfig(
241
- "subagentSpawn",
242
- appConfig.llm,
243
- ).maxTokens;
240
+ // Resolve under the same profile the run will use (forwarded via
241
+ // `SubagentConfig`) so the constructed conversation's default token cap
242
+ // matches the inherited profile rather than the static `subagentSpawn`
243
+ // default. Per-call routing re-resolves the model anyway; this keeps the
244
+ // initial value consistent.
245
+ const maxTokens = resolveCallSiteConfig("subagentSpawn", appConfig.llm, {
246
+ ...(config.overrideProfile
247
+ ? { overrideProfile: config.overrideProfile }
248
+ : {}),
249
+ ...(config.forceOverrideProfile ? { forceOverrideProfile: true } : {}),
250
+ }).maxTokens;
244
251
  const workingDir = getSandboxWorkingDir();
245
252
 
246
253
  // ── Initialise state ────────────────────────────────────────────
@@ -453,6 +460,9 @@ export class SubagentManager {
453
460
  ...(managed.state.config.overrideProfile
454
461
  ? { overrideProfile: managed.state.config.overrideProfile }
455
462
  : {}),
463
+ ...(managed.state.config.forceOverrideProfile
464
+ ? { forceOverrideProfile: true }
465
+ : {}),
456
466
  });
457
467
 
458
468
  // Agent loop completed successfully.
@@ -641,6 +651,9 @@ export class SubagentManager {
641
651
  ...(managed.state.config.overrideProfile
642
652
  ? { overrideProfile: managed.state.config.overrideProfile }
643
653
  : {}),
654
+ ...(managed.state.config.forceOverrideProfile
655
+ ? { forceOverrideProfile: true }
656
+ : {}),
644
657
  })
645
658
  .catch((err) => {
646
659
  log.error({ subagentId, err }, "Subagent message processing failed");
@@ -69,6 +69,12 @@ export interface SubagentConfig {
69
69
  * profile, every spawned subagent inherits it automatically.
70
70
  */
71
71
  overrideProfile?: string;
72
+ /**
73
+ * When true, the subagent's `overrideProfile` is an explicit spawn-time
74
+ * request and must float above call-site layers. Inherited parent profiles
75
+ * leave this unset so existing call-site precedence stays intact.
76
+ */
77
+ forceOverrideProfile?: boolean;
72
78
  /**
73
79
  * Tool-use id of the `skill_execute` call that spawned this subagent.
74
80
  * Forwarded into the `subagent_spawned` event so the client can anchor the
@@ -0,0 +1,28 @@
1
+ import { describe, expect, test } from "bun:test";
2
+
3
+ import {
4
+ isDiagnosticsConsentVersionEligible,
5
+ TRACE_COLLECTION_MIN_DIAGNOSTICS_CONSENT_VERSION,
6
+ } from "./trace-collection-policy.js";
7
+
8
+ describe("isDiagnosticsConsentVersionEligible", () => {
9
+ test("the threshold version itself is eligible (>=, inclusive boundary)", () => {
10
+ expect(
11
+ isDiagnosticsConsentVersionEligible(
12
+ TRACE_COLLECTION_MIN_DIAGNOSTICS_CONSENT_VERSION,
13
+ ),
14
+ ).toBe(true);
15
+ });
16
+
17
+ test("a later version is eligible", () => {
18
+ expect(isDiagnosticsConsentVersionEligible("2999-01-01")).toBe(true);
19
+ });
20
+
21
+ test("an earlier version fails closed", () => {
22
+ expect(isDiagnosticsConsentVersionEligible("2000-01-01")).toBe(false);
23
+ });
24
+
25
+ test("the empty version (never accepted / unversioned default) fails closed", () => {
26
+ expect(isDiagnosticsConsentVersionEligible("")).toBe(false);
27
+ });
28
+ });
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Per-turn trace-collection disclosure gate (daemon side).
3
+ *
4
+ * A "trace" is the full transcript of one assistant turn. Traces are only
5
+ * collected from owners who accepted the diagnostics-sharing consent copy that
6
+ * *discloses* trace collection — identified by its accepted version.
7
+ *
8
+ * This mirrors the platform's authoritative ingest gate in
9
+ * `vellum-assistant-platform`:
10
+ * - `django/app/core/trace_collection.py` (`is_trace_collection_enabled`)
11
+ * - `config.settings.TRACE_COLLECTION_MIN_DIAGNOSTICS_CONSENT_VERSION`
12
+ *
13
+ * The daemon applies the same `share_diagnostics_accepted_version >= threshold`
14
+ * check the platform does, so traces for ineligible owners never leave the
15
+ * device. (The platform would drop them anyway, but checking here keeps the PII
16
+ * local — data minimization.)
17
+ *
18
+ * Keep this constant in lockstep with the platform setting. Versions are
19
+ * "YYYY-MM-DD" strings, so the lexicographic compare is chronological; ""
20
+ * (never accepted) and older versions fail closed.
21
+ */
22
+ export const TRACE_COLLECTION_MIN_DIAGNOSTICS_CONSENT_VERSION = "2026-06-18";
23
+
24
+ /**
25
+ * True iff `version` (the owner's `share_diagnostics_accepted_version`) is at or
26
+ * past the disclosing version. Fails closed for "" / older versions.
27
+ */
28
+ export function isDiagnosticsConsentVersionEligible(version: string): boolean {
29
+ return version >= TRACE_COLLECTION_MIN_DIAGNOSTICS_CONSENT_VERSION;
30
+ }