@vellumai/assistant 0.9.0 → 0.10.0-staging.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (572) hide show
  1. package/ARCHITECTURE.md +18 -34
  2. package/bun.lock +7 -8
  3. package/docs/activation-funnel-telemetry.md +28 -22
  4. package/docs/architecture/security.md +29 -28
  5. package/docs/stt-provider-onboarding.md +3 -5
  6. package/docs/workflows-testing.md +13 -44
  7. package/docs/workflows.md +3 -5
  8. package/node_modules/@vellumai/ces-client/src/__tests__/ces-client.test.ts +47 -0
  9. package/node_modules/@vellumai/ces-client/src/rpc-client.ts +28 -5
  10. package/node_modules/@vellumai/environments/src/seeds.ts +2 -5
  11. package/node_modules/@vellumai/gateway-client/src/admission-policy-contract.ts +97 -0
  12. package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +10 -0
  13. package/node_modules/@vellumai/gateway-client/src/index.ts +32 -6
  14. package/node_modules/@vellumai/gateway-client/src/outbound-contract.ts +119 -0
  15. package/node_modules/@vellumai/gateway-client/src/types.ts +15 -84
  16. package/openapi.yaml +976 -63
  17. package/package.json +2 -1
  18. package/scripts/sync-llm-catalog.ts +6 -15
  19. package/scripts/sync-web-search-catalog.ts +3 -11
  20. package/src/__tests__/access-request-card-view.test.ts +98 -0
  21. package/src/__tests__/access-request-seed-content-blocks.test.ts +2 -4
  22. package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +72 -32
  23. package/src/__tests__/agent-loop-compaction-strip.test.ts +241 -0
  24. package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +16 -13
  25. package/src/__tests__/agent-loop-output-hooks.test.ts +69 -0
  26. package/src/__tests__/agent-loop-override-profile.test.ts +25 -0
  27. package/src/__tests__/always-loaded-tools-guard.test.ts +2 -3
  28. package/src/__tests__/app-compiler.test.ts +15 -1
  29. package/src/__tests__/app-dir-path-guard.test.ts +0 -1
  30. package/src/__tests__/assistant-feature-flag-guard.test.ts +1 -4
  31. package/src/__tests__/assistant-feature-flag-guardrails.test.ts +0 -2
  32. package/src/__tests__/auth-fallback-events-store.test.ts +6 -14
  33. package/src/__tests__/avatar-identity-sync.test.ts +2 -27
  34. package/src/__tests__/btw-routes.test.ts +6 -8
  35. package/src/__tests__/call-pointer-messages.test.ts +28 -0
  36. package/src/__tests__/cancel-clears-processing.test.ts +89 -0
  37. package/src/__tests__/channel-approval-routes.test.ts +0 -4
  38. package/src/__tests__/channel-inbound-disk-pressure.test.ts +5 -15
  39. package/src/__tests__/checker.test.ts +0 -3
  40. package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +3 -4
  41. package/src/__tests__/compactor-image-manifest-trust.test.ts +21 -1
  42. package/src/__tests__/compactor-summary-call-truncation.test.ts +223 -0
  43. package/src/__tests__/config-loader-backfill.test.ts +268 -27
  44. package/src/__tests__/config-schema.test.ts +35 -0
  45. package/src/__tests__/config-watcher.test.ts +0 -18
  46. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +2 -2
  47. package/src/__tests__/contact-store-user-file.test.ts +0 -6
  48. package/src/__tests__/contacts-tools.test.ts +29 -0
  49. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +22 -0
  50. package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -0
  51. package/src/__tests__/conversation-agent-loop.test.ts +58 -0
  52. package/src/__tests__/conversation-attention-telegram.test.ts +0 -1
  53. package/src/__tests__/conversation-lifecycle.test.ts +7 -9
  54. package/src/__tests__/conversation-load-history-repair.test.ts +101 -0
  55. package/src/__tests__/conversation-routes-guardian-reply.test.ts +15 -12
  56. package/src/__tests__/conversation-surfaces-activation-emit.test.ts +6 -3
  57. package/src/__tests__/conversation-title-service.test.ts +62 -0
  58. package/src/__tests__/credential-broker.test.ts +449 -1
  59. package/src/__tests__/credential-execution-shell-lockdown.test.ts +18 -11
  60. package/src/__tests__/credential-execution-tools.test.ts +0 -1
  61. package/src/__tests__/credential-prompt-route.test.ts +4 -4
  62. package/src/__tests__/credential-routes.test.ts +360 -0
  63. package/src/__tests__/credential-security-invariants.test.ts +4 -13
  64. package/src/__tests__/disk-pressure-policy.test.ts +12 -0
  65. package/src/__tests__/disk-usage.test.ts +65 -0
  66. package/src/__tests__/dynamic-page-surface.test.ts +152 -1
  67. package/src/__tests__/fixtures/credential-security-fixtures.ts +2 -33
  68. package/src/__tests__/gateway-flag-listener.test.ts +110 -1
  69. package/src/__tests__/gateway-only-guard.test.ts +3 -7
  70. package/src/__tests__/guardian-binding-drift-heal.test.ts +1 -1
  71. package/src/__tests__/guardian-card-withdrawal.test.ts +403 -0
  72. package/src/__tests__/guardian-decision-primitive-canonical.test.ts +5 -3
  73. package/src/__tests__/guardian-grant-minting.test.ts +3 -35
  74. package/src/__tests__/guardian-routing-invariants.test.ts +64 -26
  75. package/src/__tests__/guardian-routing-state.test.ts +0 -1
  76. package/src/__tests__/headless-browser-mode.test.ts +10 -0
  77. package/src/__tests__/headless-browser-navigate.test.ts +8 -3
  78. package/src/__tests__/helpers/create-guardian-binding.ts +0 -1
  79. package/src/__tests__/host-browser-proxy.test.ts +87 -0
  80. package/src/__tests__/identity-routes.test.ts +0 -189
  81. package/src/__tests__/inbound-invite-redemption.test.ts +4 -4
  82. package/src/__tests__/injector-v3-suppression.test.ts +27 -20
  83. package/src/__tests__/internal-telemetry-routes.test.ts +6 -14
  84. package/src/__tests__/invite-redemption-service.test.ts +4 -7
  85. package/src/__tests__/llm-callsite-catalog.test.ts +5 -6
  86. package/src/__tests__/llm-catalog-parity.test.ts +30 -23
  87. package/src/__tests__/llm-resolver.test.ts +70 -24
  88. package/src/__tests__/llm-schema.test.ts +1 -0
  89. package/src/__tests__/managed-profile-guard.test.ts +163 -4
  90. package/src/__tests__/mcp-health-check.test.ts +6 -7
  91. package/src/__tests__/media-stream-server-integration.test.ts +317 -13
  92. package/src/__tests__/oauth-provider-seed-logos.test.ts +4 -6
  93. package/src/__tests__/onboarding-persona-write.test.ts +1 -1
  94. package/src/__tests__/path-policy.test.ts +34 -0
  95. package/src/__tests__/persona-resolver.test.ts +49 -14
  96. package/src/__tests__/plugin-api-model-profiles.test.ts +178 -0
  97. package/src/__tests__/plugin-api-provider.test.ts +24 -0
  98. package/src/__tests__/plugin-tool-contribution.test.ts +6 -3
  99. package/src/__tests__/post-compaction-reinjection-idempotency.test.ts +214 -0
  100. package/src/__tests__/provider-send-message-override-profile.test.ts +76 -0
  101. package/src/__tests__/reaction-persistence.test.ts +150 -29
  102. package/src/__tests__/registry.test.ts +2 -7
  103. package/src/__tests__/relay-server.test.ts +285 -0
  104. package/src/__tests__/runtime-attachment-metadata.test.ts +0 -1
  105. package/src/__tests__/schedule-routes-workflow-validation.test.ts +1 -10
  106. package/src/__tests__/schedule-routes.test.ts +0 -30
  107. package/src/__tests__/schedule-tools.test.ts +2 -18
  108. package/src/__tests__/scheduler-reuse-conversation.test.ts +8 -5
  109. package/src/__tests__/skill-execute-input.test.ts +51 -1
  110. package/src/__tests__/skill-runtime-path.test.ts +2 -3
  111. package/src/__tests__/skills.test.ts +51 -0
  112. package/src/__tests__/slack-notification-approval-card.test.ts +176 -0
  113. package/src/__tests__/slack-reaction-canonical-approval.test.ts +285 -0
  114. package/src/__tests__/subagent-tools.test.ts +266 -0
  115. package/src/__tests__/surface-completion-nudge-hook.test.ts +367 -0
  116. package/src/__tests__/task-progress-nudge-hook.test.ts +1 -1
  117. package/src/__tests__/title-generate-hook.test.ts +100 -3
  118. package/src/__tests__/token-estimator-accuracy.benchmark.test.ts +1 -29
  119. package/src/__tests__/token-manager.test.ts +519 -0
  120. package/src/__tests__/tool-approval-seed-content-blocks.test.ts +1 -1
  121. package/src/__tests__/tool-audit-listener.test.ts +7 -7
  122. package/src/__tests__/tool-executor-lifecycle-events.test.ts +6 -3
  123. package/src/__tests__/tool-executor.test.ts +0 -79
  124. package/src/__tests__/trusted-contact-approval-notifier.test.ts +4 -2
  125. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +220 -3
  126. package/src/__tests__/trusted-contact-multichannel.test.ts +3 -3
  127. package/src/__tests__/trusted-contact-verification.test.ts +8 -10
  128. package/src/__tests__/twilio-routes.test.ts +81 -1
  129. package/src/__tests__/voice-invite-redemption.test.ts +2 -3
  130. package/src/__tests__/weak-open-model.test.ts +30 -0
  131. package/src/__tests__/web-search-catalog-parity.test.ts +6 -25
  132. package/src/__tests__/workspace-greetings.test.ts +152 -0
  133. package/src/__tests__/workspace-migration-105-enable-memory-v3-live-for-new-workspaces.test.ts +149 -0
  134. package/src/__tests__/workspace-migration-108-drop-balanced-economy-profile.test.ts +285 -0
  135. package/src/__tests__/workspace-migration-add-send-diagnostics.test.ts +1 -1
  136. package/src/__tests__/workspace-migration-drop-collect-usage-data.test.ts +118 -0
  137. package/src/__tests__/workspace-migration-drop-send-diagnostics.test.ts +118 -0
  138. package/src/a2a/__tests__/e2e-a2a-channel.test.ts +0 -4
  139. package/src/agent/loop.ts +49 -29
  140. package/src/api/README.md +6 -6
  141. package/src/api/events/tool-result.ts +6 -0
  142. package/src/api/events/workflow-completed.ts +53 -0
  143. package/src/api/events/workflow-leaf-finished.ts +38 -0
  144. package/src/api/events/workflow-leaf-started.ts +35 -0
  145. package/src/api/events/workflow-progress.ts +32 -0
  146. package/src/api/events/workflow-started.ts +31 -0
  147. package/src/api/index.ts +40 -0
  148. package/src/api/responses/conversation-message.ts +28 -4
  149. package/src/api/responses/home.ts +26 -4
  150. package/src/api/responses/workflow-journal.ts +53 -0
  151. package/src/approvals/guardian-card-withdrawal.ts +145 -0
  152. package/src/approvals/guardian-decision-primitive.ts +26 -3
  153. package/src/approvals/guardian-request-resolvers.ts +183 -80
  154. package/src/calls/__tests__/channel-admission-reader.test.ts +132 -0
  155. package/src/calls/__tests__/relay-setup-router.test.ts +350 -0
  156. package/src/calls/call-pointer-messages.ts +10 -4
  157. package/src/calls/channel-admission-reader.ts +104 -0
  158. package/src/calls/guardian-dispatch.ts +17 -45
  159. package/src/calls/media-stream-server.ts +84 -2
  160. package/src/calls/relay-access-wait.ts +1 -1
  161. package/src/calls/relay-server.ts +66 -0
  162. package/src/calls/relay-setup-router.ts +82 -1
  163. package/src/calls/twilio-routes.ts +17 -8
  164. package/src/calls/voice-session-bridge.ts +2 -2
  165. package/src/cli/commands/clients.ts +3 -0
  166. package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v2-compare-render.test.ts +1 -1
  167. package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v2.test.ts +8 -7
  168. package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v3.test.ts +5 -4
  169. package/src/cli/commands/memory/index.ts +30 -0
  170. package/src/cli/commands/{memory-v2-compare-render.ts → memory/memory-v2-compare-render.ts} +1 -1
  171. package/src/cli/commands/{memory-v2.ts → memory/memory-v2.ts} +6 -15
  172. package/src/cli/commands/{memory-v3.ts → memory/memory-v3.ts} +97 -11
  173. package/src/cli/commands/oauth/status.test.ts +36 -0
  174. package/src/cli/commands/oauth/status.ts +23 -3
  175. package/src/cli/commands/plugins.ts +197 -4
  176. package/src/cli/lib/__tests__/diff-plugin.test.ts +443 -0
  177. package/src/cli/lib/__tests__/inspect-plugin.test.ts +54 -0
  178. package/src/cli/lib/__tests__/merge-plugin-tree.test.ts +443 -0
  179. package/src/cli/lib/__tests__/plugin-surfaces.test.ts +111 -0
  180. package/src/cli/lib/__tests__/upgrade-plugin.test.ts +295 -2
  181. package/src/cli/lib/diff-plugin.ts +346 -0
  182. package/src/cli/lib/inspect-plugin.ts +12 -1
  183. package/src/cli/lib/install-from-github.ts +105 -17
  184. package/src/cli/lib/merge-plugin-tree.ts +328 -0
  185. package/src/cli/lib/plugin-fingerprint.ts +14 -0
  186. package/src/cli/lib/plugin-surfaces.ts +104 -0
  187. package/src/cli/lib/upgrade-plugin.ts +298 -10
  188. package/src/cli/program.ts +2 -6
  189. package/src/config/__tests__/sync-gated-profiles.test.ts +368 -0
  190. package/src/config/assistant-feature-flags.ts +22 -7
  191. package/src/config/bundled-skills/contacts/tools/contact-search.ts +0 -1
  192. package/src/config/bundled-skills/messaging/SKILL.md +6 -4
  193. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +9 -8
  194. package/src/config/bundled-skills/subagent/SKILL.md +4 -0
  195. package/src/config/bundled-skills/subagent/TOOLS.json +4 -0
  196. package/src/config/bundled-skills/workflows/SKILL.md +14 -8
  197. package/src/config/bundled-tool-registry.ts +2 -7
  198. package/src/config/call-site-defaults.ts +15 -2
  199. package/src/config/feature-flag-registry.json +46 -31
  200. package/src/config/inference-profile-validation.ts +26 -0
  201. package/src/config/llm-resolver.ts +3 -0
  202. package/src/config/loader.ts +4 -0
  203. package/src/config/memory-v3-gate.ts +11 -0
  204. package/src/config/profile-order.ts +28 -0
  205. package/src/config/schema.ts +8 -6
  206. package/src/config/schemas/__tests__/memory-v3.test.ts +1 -0
  207. package/src/config/schemas/call-site-catalog.ts +7 -0
  208. package/src/config/schemas/channels.ts +11 -0
  209. package/src/config/schemas/elevenlabs.ts +0 -1
  210. package/src/config/schemas/llm.ts +31 -0
  211. package/src/config/schemas/memory-lifecycle.ts +3 -7
  212. package/src/config/schemas/memory-v3.ts +6 -0
  213. package/src/config/schemas/platform.ts +0 -8
  214. package/src/config/schemas/services.ts +18 -0
  215. package/src/config/seed-inference-profiles.ts +109 -44
  216. package/src/config/skills.ts +21 -0
  217. package/src/config/sync-gated-profiles.ts +220 -0
  218. package/src/contacts/contact-store.ts +89 -106
  219. package/src/contacts/contacts-write.ts +5 -22
  220. package/src/contacts/types.ts +0 -1
  221. package/src/context/compactor.ts +88 -54
  222. package/src/context/strip-injections.ts +58 -10
  223. package/src/context/token-estimator.ts +1 -1
  224. package/src/credential-execution/process-manager.ts +55 -14
  225. package/src/credential-execution/prompted-credential.ts +2 -3
  226. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -2
  227. package/src/daemon/config-watcher.ts +0 -4
  228. package/src/daemon/conversation-agent-loop-handlers.ts +2 -0
  229. package/src/daemon/conversation-agent-loop.ts +114 -22
  230. package/src/daemon/conversation-history.ts +1 -1
  231. package/src/daemon/conversation-lifecycle.ts +3 -5
  232. package/src/daemon/conversation-process.ts +13 -5
  233. package/src/daemon/conversation-runtime-assembly.ts +13 -15
  234. package/src/daemon/conversation-slash.ts +2 -23
  235. package/src/daemon/conversation-surfaces.ts +26 -0
  236. package/src/daemon/conversation-tool-setup.ts +27 -14
  237. package/src/daemon/conversation.ts +66 -14
  238. package/src/daemon/disk-pressure-policy.ts +5 -3
  239. package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +0 -1
  240. package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +0 -1
  241. package/src/daemon/handlers/config-a2a.ts +0 -2
  242. package/src/daemon/handlers/config-channels.ts +15 -16
  243. package/src/daemon/handlers/config-slack-channel.ts +22 -3
  244. package/src/daemon/handlers/conversations.ts +107 -0
  245. package/src/daemon/host-browser-proxy.ts +41 -0
  246. package/src/daemon/lifecycle.ts +55 -27
  247. package/src/daemon/message-provenance.ts +2 -0
  248. package/src/daemon/message-types/contacts.ts +0 -1
  249. package/src/daemon/message-types/conversations.ts +3 -3
  250. package/src/daemon/message-types/sync.ts +0 -1
  251. package/src/daemon/message-types/web-activity.ts +7 -1
  252. package/src/daemon/message-types/workflows.ts +83 -1
  253. package/src/daemon/orphan-reaper.test.ts +0 -19
  254. package/src/daemon/orphan-reaper.ts +2 -24
  255. package/src/daemon/server.ts +0 -10
  256. package/src/daemon/tool-setup-types.ts +4 -0
  257. package/src/daemon/trust-context.ts +1 -1
  258. package/src/events/tool-audit-listener.ts +2 -2
  259. package/src/home/feed-source-enrichment.test.ts +151 -0
  260. package/src/home/feed-source-enrichment.ts +176 -0
  261. package/src/home/relationship-state.ts +2 -4
  262. package/src/instrument.ts +18 -6
  263. package/src/ipc/__tests__/binary-result-ipc.test.ts +81 -0
  264. package/src/ipc/__tests__/clients-list-ipc.test.ts +20 -0
  265. package/src/ipc/assistant-server.ts +37 -4
  266. package/src/ipc/gateway-flag-listener.ts +18 -2
  267. package/src/memory/__tests__/auto-analysis-enqueue.test.ts +5 -16
  268. package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +7 -11
  269. package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +37 -7
  270. package/src/memory/__tests__/memory-retrospective-job.test.ts +229 -401
  271. package/src/memory/__tests__/onboarding-events-store.test.ts +7 -7
  272. package/src/memory/auth-fallback-events-store.ts +2 -2
  273. package/src/memory/auto-analysis-enqueue.ts +3 -5
  274. package/src/memory/bookmark-crud.ts +1 -2
  275. package/src/memory/canonical-guardian-store.ts +39 -1
  276. package/src/memory/conversation-crud.ts +9 -4
  277. package/src/memory/conversation-key-store.ts +17 -2
  278. package/src/memory/conversation-title-service.ts +64 -7
  279. package/src/memory/db-init.ts +17 -17
  280. package/src/memory/embedding-backend.ts +38 -1
  281. package/src/memory/embedding-billing-breaker.ts +96 -0
  282. package/src/memory/jobs-store.ts +25 -13
  283. package/src/memory/jobs-worker.ts +54 -1
  284. package/src/memory/lifecycle-events-store.ts +2 -2
  285. package/src/memory/memory-retrospective-constants.ts +4 -4
  286. package/src/memory/memory-retrospective-enqueue.ts +31 -6
  287. package/src/memory/memory-retrospective-job.ts +28 -227
  288. package/src/memory/migrations/129-contact-channels-access-fields.ts +18 -9
  289. package/src/memory/migrations/131-drop-legacy-member-guardian-tables.ts +14 -2
  290. package/src/memory/migrations/289-contact-channels-unique-ext-user.ts +10 -0
  291. package/src/memory/migrations/291-contact-channels-renormalize-addresses.ts +72 -0
  292. package/src/memory/migrations/292-schedule-default-no-reuse-conversation.test.ts +67 -0
  293. package/src/memory/migrations/292-schedule-default-no-reuse-conversation.ts +25 -0
  294. package/src/memory/migrations/293-workflow-journal-leaf-tokens.ts +32 -0
  295. package/src/memory/migrations/294-drop-external-user-id.ts +31 -0
  296. package/src/memory/migrations/295-drop-approval-prompt-ts-tracker.ts +20 -0
  297. package/src/memory/migrations/296-rewrite-balanced-economy-profile-pins.test.ts +110 -0
  298. package/src/memory/migrations/296-rewrite-balanced-economy-profile-pins.ts +68 -0
  299. package/src/memory/migrations/__tests__/131-drop-legacy-member-guardian-tables.test.ts +154 -0
  300. package/src/memory/migrations/__tests__/289-contact-channels-unique-ext-user.test.ts +31 -0
  301. package/src/memory/migrations/__tests__/291-contact-channels-renormalize-addresses.test.ts +341 -0
  302. package/src/memory/migrations/__tests__/run-migrations.test.ts +52 -0
  303. package/src/memory/migrations/index.ts +6 -0
  304. package/src/memory/migrations/run-migrations.ts +41 -0
  305. package/src/memory/migrations/validate-migration-state.ts +1 -1
  306. package/src/memory/onboarding-events-store.ts +3 -3
  307. package/src/memory/schema/contacts.ts +0 -5
  308. package/src/memory/skill-loaded-events-store.test.ts +7 -15
  309. package/src/memory/skill-loaded-events-store.ts +2 -2
  310. package/src/memory/tool-executed-events-store.test.ts +7 -7
  311. package/src/memory/turn-trace-store.test.ts +736 -0
  312. package/src/memory/turn-trace-store.ts +364 -0
  313. package/src/memory/v2/__tests__/consolidation-job.test.ts +8 -0
  314. package/src/memory/v2/__tests__/skill-content.test.ts +30 -0
  315. package/src/memory/v2/consolidation-job.ts +2 -2
  316. package/src/memory/v2/skill-content.ts +25 -7
  317. package/src/memory/v2/skill-store.ts +7 -1
  318. package/src/memory/v3-eval/__tests__/eval-packets.test.ts +248 -0
  319. package/src/memory/v3-eval/eval-packets.ts +546 -0
  320. package/src/messaging/providers/slack/adapter.ts +1 -1
  321. package/src/messaging/providers/slack/api.ts +31 -0
  322. package/src/messaging/providers/slack/send.test.ts +114 -2
  323. package/src/messaging/providers/slack/send.ts +30 -7
  324. package/src/messaging/providers/slack/withdraw.test.ts +200 -0
  325. package/src/messaging/providers/slack/withdraw.ts +161 -0
  326. package/src/notifications/AGENTS.md +2 -0
  327. package/src/notifications/access-request-copy.ts +72 -59
  328. package/src/notifications/adapters/shared.ts +29 -0
  329. package/src/notifications/adapters/slack.ts +58 -103
  330. package/src/notifications/adapters/telegram.ts +2 -20
  331. package/src/notifications/approval-card-data.ts +333 -0
  332. package/src/notifications/broadcaster.ts +16 -3
  333. package/src/notifications/canonical-delivery-recorder.ts +139 -0
  334. package/src/notifications/copy-composer.ts +3 -3
  335. package/src/notifications/decision-engine.ts +4 -2
  336. package/src/notifications/destination-resolver.ts +4 -6
  337. package/src/notifications/guardian-question-mode.ts +10 -0
  338. package/src/notifications/home-feed-side-effect.ts +7 -16
  339. package/src/notifications/notification-utils.ts +19 -20
  340. package/src/notifications/signal.ts +79 -43
  341. package/src/notifications/types.ts +98 -121
  342. package/src/oauth/AGENTS.md +5 -24
  343. package/src/permissions/checker.test.ts +51 -0
  344. package/src/permissions/checker.ts +185 -26
  345. package/src/permissions/ipc-risk-types.ts +24 -0
  346. package/src/permissions/question-prompter.test.ts +27 -0
  347. package/src/permissions/question-prompter.ts +4 -0
  348. package/src/platform/client.test.ts +119 -0
  349. package/src/platform/client.ts +66 -0
  350. package/src/platform/consent-cache.test.ts +267 -0
  351. package/src/platform/consent-cache.ts +174 -0
  352. package/src/plugin-api/constants.ts +1 -1
  353. package/src/plugin-api/index.ts +33 -1
  354. package/src/plugin-api/model-profiles.ts +33 -0
  355. package/src/plugin-api/types.ts +50 -2
  356. package/src/plugins/defaults/advisor/__tests__/advisor-gate.test.ts +56 -0
  357. package/src/plugins/defaults/advisor/__tests__/advisor-state-store.test.ts +43 -0
  358. package/src/plugins/defaults/advisor/__tests__/agent-loop-integration.test.ts +137 -0
  359. package/src/plugins/defaults/advisor/__tests__/consult.test.ts +153 -0
  360. package/src/plugins/defaults/advisor/__tests__/hooks.test.ts +138 -0
  361. package/src/plugins/defaults/advisor/__tests__/transcript.test.ts +147 -0
  362. package/src/plugins/defaults/advisor/advisor-gate.ts +29 -0
  363. package/src/plugins/defaults/advisor/advisor-state-store.ts +94 -0
  364. package/src/plugins/defaults/advisor/config.ts +21 -0
  365. package/src/plugins/defaults/advisor/consult.ts +93 -0
  366. package/src/plugins/defaults/advisor/hooks/post-model-call.ts +34 -0
  367. package/src/plugins/defaults/advisor/hooks/pre-model-call.ts +30 -0
  368. package/src/plugins/defaults/advisor/hooks/user-prompt-submit.ts +19 -0
  369. package/src/plugins/defaults/advisor/package.json +14 -0
  370. package/src/plugins/defaults/advisor/steering.ts +67 -0
  371. package/src/plugins/defaults/advisor/tools/advisor.ts +65 -0
  372. package/src/plugins/defaults/advisor/transcript.ts +76 -0
  373. package/src/plugins/defaults/index.ts +60 -0
  374. package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +22 -9
  375. package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +2 -2
  376. package/src/plugins/defaults/memory-retrieval/tail-reinjection-strip.ts +64 -0
  377. package/src/plugins/defaults/memory-retrieval/unified-turn-context.ts +29 -21
  378. package/src/plugins/defaults/memory-v3-shadow/__tests__/carry-integration.test.ts +1 -0
  379. package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +1 -0
  380. package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +129 -9
  381. package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +31 -4
  382. package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +77 -2
  383. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +1 -0
  384. package/src/plugins/defaults/memory-v3-shadow/injector.ts +7 -10
  385. package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +144 -11
  386. package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +32 -20
  387. package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +56 -3
  388. package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +23 -2
  389. package/src/plugins/defaults/surface-completion-nudge/hooks/post-model-call.ts +276 -0
  390. package/src/plugins/defaults/surface-completion-nudge/hooks/stop.ts +22 -0
  391. package/src/plugins/defaults/surface-completion-nudge/nudge-state-store.ts +46 -0
  392. package/src/plugins/defaults/surface-completion-nudge/package.json +14 -0
  393. package/src/plugins/defaults/task-progress-nudge/hooks/post-tool-use.ts +3 -13
  394. package/src/plugins/defaults/title-generate/hooks/stop.ts +56 -21
  395. package/src/prompts/persona-resolver.ts +14 -4
  396. package/src/prompts/templates/system-sections.ts +7 -2
  397. package/src/providers/__tests__/provider-env-vars.test.ts +6 -0
  398. package/src/providers/__tests__/provider-secret-catalog.test.ts +1 -0
  399. package/src/providers/__tests__/retry-callsite.test.ts +176 -0
  400. package/src/providers/atlascloud/client.ts +85 -0
  401. package/src/providers/fetch-provider-catalog.ts +85 -0
  402. package/src/providers/inference/adapter-factory.ts +3 -0
  403. package/src/providers/model-catalog.ts +58 -0
  404. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +33 -0
  405. package/src/providers/openai/chat-completions-provider.ts +7 -0
  406. package/src/providers/openai/responses-provider.ts +10 -0
  407. package/src/providers/provider-send-message.ts +11 -3
  408. package/src/providers/retry.ts +53 -12
  409. package/src/providers/search-provider-catalog.ts +10 -0
  410. package/src/providers/weak-open-model.ts +22 -0
  411. package/src/runtime/AGENTS.md +0 -1
  412. package/src/runtime/__tests__/agent-wake.test.ts +181 -0
  413. package/src/runtime/__tests__/client-health.test.ts +44 -0
  414. package/src/runtime/access-request-helper.ts +21 -53
  415. package/src/runtime/actor-trust-resolver.ts +59 -63
  416. package/src/runtime/agent-wake.ts +52 -0
  417. package/src/runtime/assistant-event-hub.ts +18 -4
  418. package/src/runtime/auth/__tests__/route-policy.test.ts +12 -0
  419. package/src/runtime/auth/require-bound-guardian.ts +1 -4
  420. package/src/runtime/btw-sidechain.ts +3 -6
  421. package/src/runtime/capabilities.test.ts +120 -0
  422. package/src/runtime/capabilities.ts +197 -0
  423. package/src/runtime/channel-approval-types.ts +22 -45
  424. package/src/runtime/channel-invite-transports/telegram.ts +4 -4
  425. package/src/runtime/channel-retry-sweep.ts +1 -0
  426. package/src/runtime/channel-verification-service.ts +3 -3
  427. package/src/runtime/client-health.ts +26 -0
  428. package/src/runtime/confirmation-request-guardian-bridge.ts +38 -29
  429. package/src/runtime/effective-capabilities.test.ts +128 -0
  430. package/src/runtime/effective-capabilities.ts +84 -0
  431. package/src/runtime/guardian-reply-router.ts +106 -21
  432. package/src/runtime/invite-redemption-service.ts +9 -25
  433. package/src/runtime/migrations/__tests__/vbundle-builder-fd-leak.test.ts +123 -0
  434. package/src/runtime/migrations/vbundle-builder.ts +49 -20
  435. package/src/runtime/pending-interactions.ts +15 -0
  436. package/src/runtime/routes/__tests__/client-routes.test.ts +13 -0
  437. package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +67 -0
  438. package/src/runtime/routes/__tests__/plugins-routes.test.ts +240 -1
  439. package/src/runtime/routes/app-routes.ts +1 -1
  440. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +2 -2
  441. package/src/runtime/routes/assets/vellum-design-system.css +1959 -0
  442. package/src/runtime/routes/browser-tabs-routes.ts +9 -0
  443. package/src/runtime/routes/btw-routes.ts +1 -27
  444. package/src/runtime/routes/canonical-guardian-expiry-sweep.ts +17 -8
  445. package/src/runtime/routes/client-routes.ts +10 -0
  446. package/src/runtime/routes/contact-routes.ts +31 -8
  447. package/src/runtime/routes/conversation-compaction-routes.ts +1 -1
  448. package/src/runtime/routes/conversation-management-routes.ts +80 -1
  449. package/src/runtime/routes/conversation-query-routes.ts +68 -22
  450. package/src/runtime/routes/conversation-routes.ts +39 -14
  451. package/src/runtime/routes/credential-routes.ts +40 -16
  452. package/src/runtime/routes/empty-state-greeting-cache.ts +1 -2
  453. package/src/runtime/routes/events-routes.ts +1 -3
  454. package/src/runtime/routes/guardian-approval-interception.ts +14 -73
  455. package/src/runtime/routes/guardian-approval-prompt.ts +22 -4
  456. package/src/runtime/routes/home-feed-routes.ts +8 -3
  457. package/src/runtime/routes/identity-routes.ts +1 -296
  458. package/src/runtime/routes/inbound-message-handler.ts +214 -228
  459. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +89 -7
  460. package/src/runtime/routes/inbound-stages/admission-policy.test.ts +154 -0
  461. package/src/runtime/routes/inbound-stages/admission-policy.ts +140 -0
  462. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +3 -3
  463. package/src/runtime/routes/inbound-stages/background-dispatch.ts +11 -6
  464. package/src/runtime/routes/inbound-stages/escalation-intercept.ts +1 -2
  465. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +1 -2
  466. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +7 -7
  467. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +47 -28
  468. package/src/runtime/routes/inbound-stages/reaction-intercept.ts +358 -0
  469. package/src/runtime/routes/index.ts +2 -0
  470. package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +8 -0
  471. package/src/runtime/routes/integrations/slack/channel.ts +36 -0
  472. package/src/runtime/routes/internal-telemetry-routes.ts +1 -1
  473. package/src/runtime/routes/mcp-auth-routes.ts +233 -41
  474. package/src/runtime/routes/memory-eval-routes.ts +87 -0
  475. package/src/runtime/routes/notification-routes.ts +122 -133
  476. package/src/runtime/routes/platform-routes.ts +2 -2
  477. package/src/runtime/routes/plugins-routes.ts +202 -3
  478. package/src/runtime/routes/schedule-routes.ts +0 -22
  479. package/src/runtime/routes/secret-routes.ts +10 -0
  480. package/src/runtime/routes/surface-action-routes.ts +2 -1
  481. package/src/runtime/routes/tool-call-question-enrichment.test.ts +146 -0
  482. package/src/runtime/routes/tool-call-question-enrichment.ts +66 -0
  483. package/src/runtime/routes/workflow-routes.test.ts +229 -44
  484. package/src/runtime/routes/workflow-routes.ts +131 -29
  485. package/src/runtime/routes/workspace-greetings.ts +55 -0
  486. package/src/runtime/sync/resource-sync-events.ts +1 -11
  487. package/src/runtime/tool-grant-request-helper.ts +18 -16
  488. package/src/runtime/trust-context-resolver.ts +8 -5
  489. package/src/schedule/inference-profile.ts +2 -14
  490. package/src/schedule/schedule-store.ts +1 -1
  491. package/src/schedule/scheduler-types.ts +5 -1
  492. package/src/security/__tests__/provider-key-env-fallback.test.ts +6 -0
  493. package/src/security/secret-patterns.ts +3 -0
  494. package/src/subagent/manager.ts +17 -4
  495. package/src/subagent/types.ts +6 -0
  496. package/src/telemetry/trace-collection-policy.test.ts +28 -0
  497. package/src/telemetry/trace-collection-policy.ts +30 -0
  498. package/src/telemetry/types.ts +89 -0
  499. package/src/telemetry/usage-telemetry-reporter.test.ts +586 -36
  500. package/src/telemetry/usage-telemetry-reporter.ts +148 -41
  501. package/src/tools/AGENTS.md +3 -3
  502. package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +31 -0
  503. package/src/tools/browser/browser-execution.ts +30 -19
  504. package/src/tools/document/document-tool.ts +2 -3
  505. package/src/tools/executor.ts +5 -3
  506. package/src/tools/host-terminal/host-shell.ts +5 -4
  507. package/src/tools/memory/register.ts +2 -2
  508. package/src/tools/network/__tests__/web-fetch-firecrawl.test.ts +360 -0
  509. package/src/tools/network/__tests__/web-search.test.ts +143 -0
  510. package/src/tools/network/web-fetch.ts +372 -1
  511. package/src/tools/network/web-search-error.ts +1 -1
  512. package/src/tools/network/web-search.ts +213 -10
  513. package/src/tools/permission-checker.ts +4 -3
  514. package/src/tools/registry.ts +20 -0
  515. package/src/tools/schedule/create.ts +7 -12
  516. package/src/tools/schedule/update.ts +4 -11
  517. package/src/tools/shared/filesystem/path-policy.ts +39 -13
  518. package/src/tools/side-effects.ts +2 -17
  519. package/src/tools/skills/execute.ts +33 -0
  520. package/src/tools/subagent/spawn.ts +61 -12
  521. package/src/tools/terminal/shell.ts +10 -4
  522. package/src/tools/tool-approval-handler.ts +18 -13
  523. package/src/tools/tool-manifest.ts +0 -2
  524. package/src/tools/types.ts +9 -0
  525. package/src/tools/ui-surface/definitions.ts +64 -3
  526. package/src/tools/verification-control-plane-policy.ts +3 -1
  527. package/src/tools/workflows/run-workflow.test.ts +8 -18
  528. package/src/tools/workflows/run-workflow.ts +1 -0
  529. package/src/util/disk-usage.ts +78 -23
  530. package/src/util/platform.ts +10 -3
  531. package/src/watcher/telemetry.ts +2 -2
  532. package/src/workflows/capabilities.ts +2 -3
  533. package/src/workflows/engine.test.ts +175 -1
  534. package/src/workflows/engine.ts +82 -0
  535. package/src/workflows/journal-store.test.ts +70 -0
  536. package/src/workflows/journal-store.ts +18 -3
  537. package/src/workflows/run-manager.test.ts +171 -28
  538. package/src/workflows/run-manager.ts +66 -24
  539. package/src/workspace/migrations/105-enable-memory-v3-live-for-new-workspaces.ts +63 -0
  540. package/src/workspace/migrations/106-drop-collect-usage-data.ts +47 -0
  541. package/src/workspace/migrations/107-drop-send-diagnostics.ts +47 -0
  542. package/src/workspace/migrations/108-drop-balanced-economy-profile.ts +129 -0
  543. package/src/workspace/migrations/registry.ts +8 -0
  544. package/src/__tests__/app-control-no-global-cgevent.test.ts +0 -98
  545. package/src/__tests__/credential-security-e2e.test.ts +0 -362
  546. package/src/__tests__/credential-vault-unit.test.ts +0 -1528
  547. package/src/__tests__/credential-vault.test.ts +0 -1706
  548. package/src/__tests__/identity-intro-cache.test.ts +0 -315
  549. package/src/__tests__/secret-onetime-send.test.ts +0 -182
  550. package/src/cli/commands/__tests__/task.test.ts +0 -914
  551. package/src/cli/commands/task.ts +0 -771
  552. package/src/config/bundled-skills/personal-page/SKILL.md +0 -57
  553. package/src/config/bundled-skills/personal-page/TOOLS.json +0 -27
  554. package/src/config/bundled-skills/personal-page/tools/app-refresh.ts +0 -17
  555. package/src/config/preloaded-apps/personal-page/src/components/About.tsx +0 -22
  556. package/src/config/preloaded-apps/personal-page/src/components/App.tsx +0 -16
  557. package/src/config/preloaded-apps/personal-page/src/components/Features.tsx +0 -77
  558. package/src/config/preloaded-apps/personal-page/src/components/Hero.tsx +0 -57
  559. package/src/config/preloaded-apps/personal-page/src/components/Pending.tsx +0 -28
  560. package/src/config/preloaded-apps/personal-page/src/components/animations.tsx +0 -234
  561. package/src/config/preloaded-apps/personal-page/src/components/icons.tsx +0 -48
  562. package/src/config/preloaded-apps/personal-page/src/components/media.ts +0 -16
  563. package/src/config/preloaded-apps/personal-page/src/index.html +0 -20
  564. package/src/config/preloaded-apps/personal-page/src/main.tsx +0 -7
  565. package/src/config/preloaded-apps/personal-page/src/profile-data.ts +0 -82
  566. package/src/config/preloaded-apps/personal-page/src/styles.css +0 -759
  567. package/src/memory/__tests__/preloaded-apps.test.ts +0 -85
  568. package/src/memory/preloaded-apps.ts +0 -116
  569. package/src/notifications/tool-approval-copy.ts +0 -142
  570. package/src/runtime/routes/approval-prompt-ts-tracker.ts +0 -78
  571. package/src/runtime/routes/identity-intro-cache.ts +0 -172
  572. package/src/tools/credentials/vault.ts +0 -712
package/ARCHITECTURE.md CHANGED
@@ -343,11 +343,11 @@ The Slack channel provides text-based messaging via Slack's Socket Mode API. Unl
343
343
 
344
344
  **Control-plane endpoints** (`/v1/integrations/slack/channel/config`):
345
345
 
346
- | Endpoint | Method | Description |
347
- | --------------------------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
348
- | `/v1/integrations/slack/channel/config` | GET | Returns current config status: `hasBotToken`, `hasAppToken`, `hasUserToken`, `connected`, plus workspace metadata (`teamId`, `teamName`, `botUserId`, `botUsername`) |
349
- | `/v1/integrations/slack/channel/config` | POST | Validates and stores credentials. Body: `{ botToken?: string, appToken?: string, userToken?: string }` |
350
- | `/v1/integrations/slack/channel/config` | DELETE | Clears all Slack channel credentials (bot, app, and user tokens) from secure storage and credential metadata. Surgical user-token-only deletion is exposed internally via `clearSlackUserToken` (used by the credential vault) but is not reachable through this HTTP endpoint. |
346
+ | Endpoint | Method | Description |
347
+ | --------------------------------------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
348
+ | `/v1/integrations/slack/channel/config` | GET | Returns current config status: `hasBotToken`, `hasAppToken`, `hasUserToken`, `connected`, plus workspace metadata (`teamId`, `teamName`, `botUserId`, `botUsername`) |
349
+ | `/v1/integrations/slack/channel/config` | POST | Validates and stores credentials. Body: `{ botToken?: string, appToken?: string, userToken?: string }` |
350
+ | `/v1/integrations/slack/channel/config` | DELETE | Clears all Slack channel credentials (bot, app, and user tokens) from secure storage and credential metadata. The `credentials delete` route handles `slack_channel`/`user_token` like any other credential except that it skips the OAuth teardown, so removing just the user token leaves the channel's connection (run on the bot + app tokens) intact. `clearSlackUserToken` is the surgical user-token-only helper used internally by Slack config handling. |
351
351
 
352
352
  All endpoints are JWT-authenticated via `Authorization: Bearer <jwt>`.
353
353
 
@@ -609,10 +609,9 @@ Audio-to-text conversion occurs in six distinct runtime boundaries, each with it
609
609
  | ---------------------------- | ----------------------------------------------------------------------------- | -------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ |
610
610
  | **Telephony (hybrid)** | Twilio-native ConversationRelay or daemon media-stream (provider-conditional) | Configured STT provider (via `services.stt`) | `src/calls/telephony-stt-routing.ts` | `src/calls/twilio-routes.ts` |
611
611
  | **Daemon batch** | Daemon process (REST API to provider) | Configured STT provider (via `services.stt`) | `src/stt/daemon-batch-transcriber.ts` | `src/runtime/routes/inbound-stages/transcribe-audio.ts` |
612
- | **Conversation streaming** | Daemon process (WebSocket-based) | Configured STT provider (via `services.stt`) | `src/stt/stt-stream-session.ts`, `src/providers/speech-to-text/deepgram-realtime.ts`, `src/providers/speech-to-text/google-gemini-live-stream.ts`, `src/providers/speech-to-text/openai-whisper-stream.ts`, `src/providers/speech-to-text/xai-realtime.ts` | `VoiceInputManager` (macOS conversation) via gateway WS proxy |
613
- | **Live voice channel** | Assistant process (gateway-authenticated WebSocket) | Configured STT provider (via `services.stt`) | `src/runtime/http-server.ts`, `src/live-voice/live-voice-session-manager.ts`, `src/live-voice/live-voice-session.ts`, `src/providers/speech-to-text/resolve.ts`, streaming provider adapters | `LiveVoiceChannelManager` (macOS voice mode) via `/v1/live-voice` |
614
- | **Client service-first** | macOS via gateway → daemon | Configured STT provider (via `services.stt`) | `src/runtime/routes/stt-routes.ts`, `clients/shared/Network/STTClient.swift` | `VoiceInputManager` (macOS dictation), `OpenAIVoiceService` (macOS voice mode) |
615
- | **Client-native (fallback)** | macOS on-device | Apple Speech (`SFSpeechRecognizer`) | `clients/macos/.../SpeechRecognizerAdapter.swift` | Fallback when STT service is unconfigured or fails |
612
+ | **Conversation streaming** | Daemon process (WebSocket-based) | Configured STT provider (via `services.stt`) | `src/stt/stt-stream-session.ts`, `src/providers/speech-to-text/deepgram-realtime.ts`, `src/providers/speech-to-text/google-gemini-live-stream.ts`, `src/providers/speech-to-text/openai-whisper-stream.ts`, `src/providers/speech-to-text/xai-realtime.ts` | Web/Electron dictation client via gateway WS proxy |
613
+ | **Live voice channel** | Assistant process (gateway-authenticated WebSocket) | Configured STT provider (via `services.stt`) | `src/runtime/http-server.ts`, `src/live-voice/live-voice-session-manager.ts`, `src/live-voice/live-voice-session.ts`, `src/providers/speech-to-text/resolve.ts`, streaming provider adapters | Web/Electron live voice client via `/v1/live-voice` |
614
+ | **Client service-first** | Web/Electron via gateway → daemon | Configured STT provider (via `services.stt`) | `src/runtime/routes/stt-routes.ts` | Web/Electron dictation and voice clients |
616
615
 
617
616
  **Telephony boundary (hybrid routing):**
618
617
 
@@ -679,16 +678,14 @@ Two provider adapters are supported, each implementing the `StreamingTranscriber
679
678
 
680
679
  **Session lifecycle (client side):**
681
680
 
682
- - `STTStreamingClient` (`clients/shared/Network/STTStreamingClient.swift`) manages the WebSocket session using `URLSessionWebSocketTask`. It builds the gateway WebSocket URL via `GatewayHTTPClient.buildWebSocketRequest(path: "stt/stream", params:)`.
683
- - `STTProviderRegistry` (`clients/shared/Utilities/STTProviderRegistry.swift`) exposes `isStreamingAvailable` (checks the configured provider's `conversationStreamingMode` from the `GET /v1/stt/providers` API) and `isServiceConfigured` (checks whether any STT provider is set).
684
- - macOS: `VoiceInputManager.startStreamingSession()` creates a fresh `STTStreamingClient` per recording session. Streaming partials take priority over `SFSpeechRecognizer` partials while the stream is active and healthy. When recording stops, if the stream delivered at least one `final` event (`streamingReceivedFinal`) and has not failed (`streamingFailed`), the streaming final text is used directly. Otherwise, the batch STT path (`STTClient.transcribe()`) provides the fallback.
681
+ The web client streaming dictation client lives at `clients/web/src/domains/chat/voice/dictation-stream.ts`; it opens the gateway WebSocket to `/v1/stt/stream`, parses `partial`/`final` frames, and reports failures so the caller can fall back to batch transcription. Before opening a session the client checks the configured provider's `conversationStreamingMode` from the `GET /v1/stt/providers` API. When the stream delivers at least one `final` event and has not failed, the streaming final text is used directly; otherwise the batch STT path (`clients/web/src/domains/chat/voice/stt-api.ts`) provides the fallback.
685
682
 
686
683
  **Fallback semantics:**
687
684
 
688
685
  The conversation streaming path degrades gracefully to the existing batch STT path:
689
686
 
690
- 1. **Unsupported provider** (a hypothetical provider with `conversationStreamingMode: "none"`): The client checks `STTProviderRegistry.isStreamingAvailable` before attempting a streaming session. When `false`, recording proceeds with the batch-only flow (no WebSocket is opened). On the daemon side, if a streaming session is somehow opened for an unsupported provider, the session sends an `error` event followed by `closed` and closes the socket with code 1000.
691
- 2. **Connection failure** (network error, gateway down, auth failure): The `STTStreamingClient` reports an `STTStreamFailure` to the client's `onFailure` callback. macOS sets `streamingFailed = true` and falls through to batch STT resolution when recording stops.
687
+ 1. **Unsupported provider** (a hypothetical provider with `conversationStreamingMode: "none"`): The client checks streaming availability (the configured provider's `conversationStreamingMode` from `GET /v1/stt/providers`) before attempting a streaming session. When unavailable, recording proceeds with the batch-only flow (no WebSocket is opened). On the daemon side, if a streaming session is somehow opened for an unsupported provider, the session sends an `error` event followed by `closed` and closes the socket with code 1000.
688
+ 2. **Connection failure** (network error, gateway down, auth failure): The streaming client reports the failure to its caller, which marks the stream as failed and falls through to batch STT resolution when recording stops.
692
689
  3. **Mid-session provider error** (provider WebSocket disconnect, timeout, rate limit): The daemon session emits an `error` event (with a normalized `SttErrorCategory`) followed by `closed`. The client marks the stream as failed and defers to batch STT.
693
690
  4. **Missing credentials**: `resolveStreamingTranscriber()` returns `null` when the API key is not configured. The session sends an `error`+`closed` pair and the client falls back to batch.
694
691
 
@@ -713,9 +710,8 @@ The conversation streaming path degrades gracefully to the existing batch STT pa
713
710
  | `src/providers/speech-to-text/resolve.ts` | `resolveStreamingTranscriber()`: credential-aware factory for streaming adapters; `resolveConversationStreamingSttCapability()`: capability validator |
714
711
  | `src/runtime/http-server.ts` | Runtime WebSocket upgrade handler for `/v1/stt/stream`, session registry (`activeSttStreamSessions`), graceful shutdown |
715
712
  | `gateway/src/http/routes/stt-stream-websocket.ts` | Gateway WebSocket proxy: authenticates client, opens upstream WS to daemon with service token |
716
- | `clients/shared/Network/STTStreamingClient.swift` | Shared Swift WebSocket client: `URLSessionWebSocketTask`-based, event parsing, failure reporting |
717
- | `clients/shared/Utilities/STTProviderRegistry.swift` | Client-side provider catalog: `isStreamingAvailable`, `conversationStreamingMode` per provider |
718
- | `clients/macos/.../VoiceInputManager.swift` | macOS integration: `startStreamingSession()`, streaming/batch priority, fallback on failure |
713
+ | `clients/web/src/domains/chat/voice/dictation-stream.ts` | Web streaming dictation client: WebSocket session, event parsing, failure reporting |
714
+ | `clients/web/src/domains/chat/voice/voice-recording-store.ts` | Web voice recording state: streaming/batch priority, fallback on failure |
719
715
 
720
716
  **Live voice channel boundary:**
721
717
 
@@ -742,37 +738,25 @@ V1 is local/gateway-scoped. Managed/cloud WebSocket proxy support, cross-region
742
738
 
743
739
  **Client service-first boundary:**
744
740
 
745
- All product-facing dictation and voice-streaming paths on macOS use a service-first STT strategy. Clients record audio, encode it to WAV via `AudioWavEncoder` (shared utility in `clients/shared/Utilities/AudioWavEncoder.swift`), and POST it through the gateway to the daemon's `POST /v1/stt/transcribe` endpoint via `STTClient` (`clients/shared/Network/STTClient.swift`). The daemon resolves the configured STT provider through `resolveBatchTranscriber()` and returns the transcribed text.
741
+ All product-facing dictation and voice-streaming paths use a service-first STT strategy. Clients record audio, encode it to WAV, and POST it through the gateway to the daemon's `POST /v1/stt/transcribe` endpoint. The daemon resolves the configured STT provider through `resolveBatchTranscriber()` and returns the transcribed text.
746
742
 
747
- - `STTClient` conforms to `STTClientProtocol` and returns a typed `STTResult` enum (`success`, `notConfigured`, `serviceUnavailable`, `error`). Callers pattern-match on the result to deterministically trigger native fallback.
743
+ - The client receives a typed result distinguishing success from `notConfigured`, `serviceUnavailable`, and `error`, so callers can deterministically trigger their fallback path.
748
744
  - The gateway proxies the request via assistant-scoped path rewriting: `/v1/assistants/:id/stt/transcribe` is rewritten to `/v1/stt/transcribe` on the daemon.
749
745
  - `stt-routes.ts` (`src/runtime/routes/stt-routes.ts`) defines the HTTP endpoint, validates the audio payload, and delegates to `resolveBatchTranscriber()`.
750
746
 
751
- Product-facing flows using service-first STT:
752
-
753
- | Flow | Client | Entry point |
754
- | ----------------------------- | ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
755
- | **Push-to-talk dictation** | macOS | `VoiceInputManager.resolveTranscription()` — encodes accumulated PCM buffers to WAV, calls `sttClient.transcribe()`, falls back to native text on failure |
756
- | **Conversation chat capture** | macOS | `VoiceInputManager.handleFinalTranscription()` — prefers streaming final when available; falls back to batch `sttClient.transcribe()` when streaming was not used, failed, or produced no finals |
757
- | **Voice mode (streaming)** | macOS | `OpenAIVoiceService.stopRecordingAndGetTranscription()` — encodes per-turn PCM to WAV, calls `sttClient.transcribe()` for turn-final transcript resolution, falls back to SFSpeechRecognizer result |
758
-
759
- **Client-native fallback boundary:**
760
-
761
- Apple-native on-device recognition via `SFSpeechRecognizer` serves two roles in all three product-facing flows above: (1) it provides low-latency partial transcriptions for real-time display during recording, and (2) it provides the fallback final transcription when the STT service is unconfigured (HTTP 503), temporarily unavailable (HTTP 5xx), or returns an empty result. The `SpeechRecognizerAdapter` protocols on each platform abstract Apple Speech for **testability and dependency injection**.
762
-
763
- The macOS `SpeechRecognizerAdapter` protocol in `clients/macos/vellum-assistant/Features/Voice/SpeechRecognizerAdapter.swift` abstracts `SFSpeechRecognizer` static APIs and instance creation. `AppleSpeechRecognizerAdapter` is the production implementation. `OpenAIVoiceService` and `VoiceInputManager` consume the adapter via dependency injection. **Note:** The protocol leaks Apple Speech types through its surface — `authorizationStatus()` returns `SFSpeechRecognizerAuthorizationStatus` and `makeRecognizer(locale:)` returns `SFSpeechRecognizer?` directly. This means callers depend on the Speech framework at compile time.
747
+ The web client implements these flows in `clients/web/src/domains/chat/voice/`: `stt-api.ts` (batch transcribe), `dictation-stream.ts` (streaming dictation), and `voice-recording-store.ts` (recording state, streaming/batch priority, fallback). Push-to-talk dictation and conversation chat capture prefer a streaming final when available and fall back to batch transcription when streaming was not used, failed, or produced no finals.
764
748
 
765
749
  **Cross-boundary notes:**
766
750
 
767
751
  - The `services.stt` config block is the single source of truth for STT provider selection across the daemon batch boundary, the conversation streaming boundary, the client service-first boundary, and the telephony boundary. The batch and streaming resolvers (`resolveBatchTranscriber()`, `resolveStreamingTranscriber()`) both read from `services.stt.provider` and resolve credentials through the same catalog; the telephony boundary uses `resolveTelephonySttRouting()` to determine the Twilio integration strategy. The daemon provider catalog (`src/providers/speech-to-text/provider-catalog.ts`) is the authoritative registry of supported providers. Native clients fetch display metadata via `GET /v1/stt/providers`.
768
752
  - Conversation streaming does not replace the client service-first batch path. When streaming is available, it runs concurrently during recording and provides real-time partials and finals. The batch path remains the fallback for providers that do not support streaming, when streaming fails mid-session, or when streaming produces no final transcript.
769
753
  - Credential mapping is catalog-driven: `provider-secret-catalog.ts` derives STT API-key provider names from the daemon catalog via `listCredentialProviderNames()`, deduplicating against the LLM/search provider list. Adding a provider to the catalog automatically includes its credential name in `API_KEY_PROVIDERS`.
770
- - Terminology: "STT" and "transcription" refer to the same operation (converting audio to text). "Speech recognition" is used in client-native contexts where Apple's Speech framework terminology is canonical. All three terms map to the same conceptual operation.
754
+ - Terminology: "STT", "transcription", and "speech recognition" all refer to the same operation (converting audio to text).
771
755
  - **Onboarding**: For a step-by-step guide to adding a new STT provider, see `docs/stt-provider-onboarding.md`.
772
756
 
773
757
  ### On-Demand Home Content Generation
774
758
 
775
- LLM-generated content shown by clients (personalized home greeting, suggested prompts, conversation starters, identity intro) is produced on demand, never at daemon startup or on unconditional timers.
759
+ LLM-generated content shown by clients (personalized home greeting, suggested prompts, conversation starters) is produced on demand, never at daemon startup or on unconditional timers.
776
760
 
777
761
  **Data flow (home greeting + suggested prompts):**
778
762
 
package/bun.lock CHANGED
@@ -25,6 +25,7 @@
25
25
  "@vellumai/twilio-client": "file:../packages/twilio-client",
26
26
  "commander": "13.1.0",
27
27
  "croner": "10.0.1",
28
+ "diff": "8.0.4",
28
29
  "dotenv": "17.3.1",
29
30
  "drizzle-orm": "0.45.2",
30
31
  "jszip": "3.10.1",
@@ -1244,17 +1245,13 @@
1244
1245
 
1245
1246
  "@vellumai/ces-client/@types/bun": ["@types/bun@1.2.4", "", { "dependencies": { "bun-types": "1.2.4" } }, "sha512-QtuV5OMR8/rdKJs213iwXDpfVvnskPXY/S0ZiFbsTjQZycuqPbMW8Gf/XhLfwE5njW8sxI2WjISURXPlHypMFA=="],
1246
1247
 
1247
- "@vellumai/ces-client/@vellumai/service-contracts": ["@vellumai/service-contracts@file:../packages/service-contracts", {}],
1248
+ "@vellumai/ces-client/@vellumai/service-contracts": ["@vellumai/service-contracts@file:../packages/service-contracts", { "dependencies": { "zod": "4.3.6" }, "devDependencies": { "@types/bun": "1.2.4", "typescript": "5.7.3" } }],
1248
1249
 
1249
1250
  "@vellumai/ces-client/typescript": ["typescript@5.7.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw=="],
1250
1251
 
1251
1252
  "@vellumai/environments/@types/bun": ["@types/bun@1.3.11", "", { "dependencies": { "bun-types": "1.3.11" } }, "sha512-5vPne5QvtpjGpsGYXiFyycfpDF2ECyPcTSsFBMa0fraoxiQyMJ3SmuQIGhzPg2WJuWxVBoxWJ2kClYTcw/4fAg=="],
1252
1253
 
1253
- "@vellumai/gateway-client/@vellumai/service-contracts": ["@vellumai/service-contracts@file:../packages/service-contracts", {}],
1254
-
1255
- "@vellumai/service-contracts/@types/bun": ["@types/bun@1.2.4", "", { "dependencies": { "bun-types": "1.2.4" } }, "sha512-QtuV5OMR8/rdKJs213iwXDpfVvnskPXY/S0ZiFbsTjQZycuqPbMW8Gf/XhLfwE5njW8sxI2WjISURXPlHypMFA=="],
1256
-
1257
- "@vellumai/service-contracts/typescript": ["typescript@5.7.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw=="],
1254
+ "@vellumai/gateway-client/@vellumai/service-contracts": ["@vellumai/service-contracts@file:../packages/service-contracts", { "dependencies": { "zod": "4.3.6" }, "devDependencies": { "@types/bun": "1.2.4", "typescript": "5.7.3" } }],
1258
1255
 
1259
1256
  "bl/readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="],
1260
1257
 
@@ -1342,9 +1339,11 @@
1342
1339
 
1343
1340
  "@vellumai/ces-client/@types/bun/bun-types": ["bun-types@1.2.4", "", { "dependencies": { "@types/node": "*", "@types/ws": "~8.5.10" } }, "sha512-nDPymR207ZZEoWD4AavvEaa/KZe/qlrbMSchqpQwovPZCKc7pwMoENjEtHgMKaAjJhy+x6vfqSBA1QU3bJgs0Q=="],
1344
1341
 
1345
- "@vellumai/environments/@types/bun/bun-types": ["bun-types@1.3.11", "", { "dependencies": { "@types/node": "*" } }, "sha512-1KGPpoxQWl9f6wcZh57LvrPIInQMn2TQ7jsgxqpRzg+l0QPOFvJVH7HmvHo/AiPgwXy+/Thf6Ov3EdVn1vOabg=="],
1342
+ "@vellumai/ces-client/@vellumai/service-contracts/@types/bun": ["@types/bun@1.3.10", "", { "dependencies": { "bun-types": "1.3.10" } }, "sha512-0+rlrUrOrTSskibryHbvQkDOWRJwJZqZlxrUs1u4oOoTln8+WIXBPmAuCF35SWB2z4Zl3E84Nl/D0P7803nigQ=="],
1346
1343
 
1347
- "@vellumai/service-contracts/@types/bun/bun-types": ["bun-types@1.2.4", "", { "dependencies": { "@types/node": "*", "@types/ws": "~8.5.10" } }, "sha512-nDPymR207ZZEoWD4AavvEaa/KZe/qlrbMSchqpQwovPZCKc7pwMoENjEtHgMKaAjJhy+x6vfqSBA1QU3bJgs0Q=="],
1344
+ "@vellumai/ces-client/@vellumai/service-contracts/typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
1345
+
1346
+ "@vellumai/environments/@types/bun/bun-types": ["bun-types@1.3.11", "", { "dependencies": { "@types/node": "*" } }, "sha512-1KGPpoxQWl9f6wcZh57LvrPIInQMn2TQ7jsgxqpRzg+l0QPOFvJVH7HmvHo/AiPgwXy+/Thf6Ov3EdVn1vOabg=="],
1348
1347
 
1349
1348
  "detective-typescript/@typescript-eslint/typescript-estree/@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.61.0", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.61.0", "@typescript-eslint/types": "^8.61.0", "debug": "^4.4.3" }, "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-DV42F7MLJO6Rax7SK1yg43tcnEfGUrurSpSxKuVX+a3RCTzBlH3fuxprrOJXKCJGAaw82xXocikJ0uQaqwXgGA=="],
1350
1349
 
@@ -1,6 +1,6 @@
1
1
  # Activation Funnel Telemetry — Runbook + Analytics Handoff
2
2
 
3
- > **Linear:** JARVIS-1102 (event emission) — gates JARVIS-1092 (10% rollout) and JARVIS-1093 (dashboard).
3
+ > **Linear:** event emission ticket — gates rollout and dashboard tickets.
4
4
  > **Funnel version:** `activation_v1_2026_06`
5
5
  > **LD flag:** `experiment-activation-flow-2026-06-03`
6
6
  > **Cohort arm tag:** `ab_variant = "variant-a"` (treatment); `control` = the no-rail arm.
@@ -50,8 +50,9 @@ Flow, end to end:
50
50
  `assistant/src/telemetry/activation-funnel.ts`;
51
51
  - emission is best-effort (wrapped in try/catch) and never blocks or alters
52
52
  the surface-action flow;
53
- - `recordActivationEvent` respects the `getConfig().collectUsageData` opt-out
54
- gate (returns `null` / no row when disabled).
53
+ - `recordActivationEvent` respects the platform `share_analytics` consent gate
54
+ via `getCachedShareAnalytics()` (returns `null` / no row when the platform
55
+ user has not consented; default-off when there is no platform session).
55
56
  2. **Reporter flushes** every ~5 min: `usage-telemetry-reporter.ts`
56
57
  (`REPORT_INTERVAL_MS = 5 * 60 * 1000`, with a one-time
57
58
  `INITIAL_FLUSH_DELAY_MS = 30_000` after startup) POSTs unreported onboarding
@@ -172,22 +173,27 @@ When the flag is ON, the web prechat context selects
172
173
  `BOOTSTRAP-ACTIVATION-RAIL.md` as the bootstrap template, and the daemon marks
173
174
  the conversation in `activation_sessions` on first build of the system prompt.
174
175
 
175
- ### 5.2 Confirm usage-data collection is enabled — and do NOT run in dev mode
176
-
177
- Two gates must both be satisfied or no rows reach BigQuery:
178
-
179
- 1. `recordActivationEvent` no-ops when `config.collectUsageData` is false, so the
180
- dev build/config must have usage-data collection enabled, or no rows are
181
- written to SQLite.
182
- 2. **Dev mode disables the flush entirely.** `assistant/src/daemon/lifecycle.ts`
183
- computes the effective setting as `collectUsageData = !isDevMode &&
184
- config.collectUsageData`, so when the daemon runs in dev mode (`VELLUM_DEV=1`)
185
- the `UsageTelemetryReporter` is never started rows accumulate in SQLite but
186
- are never POSTed, and the "wait/restart for flush" steps below will never
187
- reach BigQuery. For an end-to-end smoke test, run the daemon **outside dev
188
- mode** (so the reporter starts), or explicitly invoke the reporter's
189
- `flush()` via a dev hook. The SQLite rows can still be inspected directly in
190
- dev mode, but the BigQuery verification (§6) requires a real flush.
176
+ ### 5.2 Confirm share_analytics consent is on — and pick the right daemon mode
177
+
178
+ Two distinct concerns: whether record-time rows reach SQLite (consent gate), and
179
+ whether those rows reach BigQuery (flush gate, dev-disabled).
180
+
181
+ 1. `recordActivationEvent` no-ops when the platform `share_analytics` consent is
182
+ off (it reads `getCachedShareAnalytics()`), and the consent is **default-off
183
+ when there is no platform session**. So the dev session must be signed in to
184
+ the platform with `share_analytics` consent enabled, or no rows are written to
185
+ SQLite. The consent cache is refreshed by `startConsentRefresh()` in
186
+ `assistant/src/daemon/lifecycle.ts`, which runs **regardless of dev mode** so
187
+ a dev session signed in with `share_analytics` consent enabled writes
188
+ record-time rows to SQLite, where they can be inspected directly.
189
+ 2. **Dev mode disables the flush entirely.** When the daemon runs in dev mode
190
+ (`VELLUM_DEV=1`), `assistant/src/daemon/lifecycle.ts` never starts the
191
+ `UsageTelemetryReporter` rows accumulate in SQLite (consent permitting) but
192
+ are never POSTed, and the "wait/restart for flush" steps below will never reach
193
+ BigQuery. For an end-to-end smoke test, run the daemon **outside dev mode** (so
194
+ the reporter starts), or explicitly invoke the reporter's `flush()` via a dev
195
+ hook. The SQLite rows can be inspected directly in dev mode, but the BigQuery
196
+ verification (§6) requires a real flush.
191
197
 
192
198
  ### 5.3 Start a fresh activation conversation and capture its id
193
199
 
@@ -261,9 +267,9 @@ collapses it earliest-wins).
261
267
 
262
268
  ---
263
269
 
264
- ## 7. Canonical handoff blurb (paste-ready for the final JARVIS-1102 PR description)
270
+ ## 7. Canonical handoff blurb (paste-ready for the final PR description)
265
271
 
266
- > **Activation funnel telemetry — handoff for JARVIS-1093.** The activation rail
272
+ > **Activation funnel telemetry — handoff for dashboard ticket.** The activation rail
267
273
  > now emits five milestone funnel events into the existing onboarding telemetry
268
274
  > substrate (`type: "onboarding"` → `/v1/telemetry/ingest/` → GCS NDJSON →
269
275
  > `vellum-ai-prod.telemetry.onboarding_raw`), so no new event type, ingest
@@ -290,7 +296,7 @@ collapses it earliest-wins).
290
296
 
291
297
  ## 8. Notes
292
298
 
293
- - **JARVIS-1102 "naming check" (resolved).** Events fire **deterministically on
299
+ - **"naming check" (resolved).** Events fire **deterministically on
294
300
  the real user commit of a `ui_show` surface, not every text turn**. The model
295
301
  passively tags the surface for a rail move with `activation_moment`; the daemon
296
302
  records the milestone in `handleSurfaceAction` when the user commits that
@@ -223,35 +223,35 @@ The credential system enforces four security invariants:
223
223
 
224
224
  ```mermaid
225
225
  sequenceDiagram
226
- participant Model as LLM
227
- participant Vault as credential_store tool
226
+ participant Model as LLM (assistant credentials prompt)
227
+ participant Route as credentials prompt route
228
228
  participant Prompter as SecretPrompter
229
229
  participant HTTP as HTTP Transport
230
- participant UI as SecretPromptManager (Swift)
230
+ participant UI as Secret prompt UI (client)
231
231
  participant Store as Credential Store (CES / encrypted file)
232
232
 
233
- Model->>Vault: action: "prompt", service, field, label
234
- Vault->>Prompter: requestSecret(service, field, label, ...)
233
+ Model->>Route: assistant credentials prompt --service --field --label
234
+ Route->>Prompter: requestSecretStandalone(service, field, label, ...)
235
235
  Prompter->>HTTP: secret_request {requestId, service, field, label, allowOneTimeSend}
236
- HTTP->>UI: Show SecretPromptView (floating panel)
237
- UI->>UI: User enters value in SecureField
236
+ HTTP->>UI: Show secret prompt
237
+ UI->>UI: User enters value in a masked field
238
238
  alt Store (default)
239
239
  UI->>HTTP: secret_response {requestId, value, delivery: "store"}
240
240
  HTTP->>Prompter: resolve(value, "store")
241
- Prompter->>Vault: {value, delivery: "store"}
242
- Vault->>Store: setSecureKeyAsync("credential/svc/field", value)
243
- Vault->>Model: "Credential stored securely" (no value in output)
241
+ Prompter->>Route: {value, delivery: "store"}
242
+ Route->>Store: persistPromptedCredential → setSecureKeyAsync("credential/svc/field", value)
243
+ Route->>Model: "Stored credential ..." (no value in output)
244
244
  else One-Time Send (if enabled)
245
245
  UI->>HTTP: secret_response {requestId, value, delivery: "transient_send"}
246
246
  HTTP->>Prompter: resolve(value, "transient_send")
247
- Prompter->>Vault: {value, delivery: "transient_send"}
248
- Note over Vault: Hands value to CredentialBroker<br/>for single-use consumption
249
- Vault->>Model: "One-time credential provided" (no value in output)
247
+ Prompter->>Route: {value, delivery: "transient_send"}
248
+ Note over Route: Hands value to CredentialBroker<br/>for single-use consumption
249
+ Route->>Model: "One-time credential provided" (no value in output)
250
250
  else Cancel
251
251
  UI->>HTTP: secret_response {requestId, value: null}
252
252
  HTTP->>Prompter: resolve(null)
253
- Prompter->>Vault: null
254
- Vault->>Model: "User cancelled"
253
+ Prompter->>Route: null
254
+ Route->>Model: "User cancelled"
255
255
  end
256
256
  ```
257
257
 
@@ -273,7 +273,7 @@ The `allowOneTimeSend` config gate (default: `false`) enables a secondary "Send
273
273
  - The secret value is handed to the `CredentialBroker`, which holds it in memory for the next `consume` or `browserFill` call
274
274
  - The value is **not** persisted to the credential store
275
275
  - The broker discards the value after a single use
276
- - The vault tool output confirms delivery without including the secret value — the value is never returned to the model
276
+ - The credentials prompt route output confirms delivery without including the secret value — the value is never returned to the model
277
277
  - The config gate must be explicitly enabled by the operator
278
278
 
279
279
  ### Storage Layout
@@ -286,18 +286,19 @@ The `allowOneTimeSend` config gate (default: `false`) enables a secondary "Send
286
286
 
287
287
  ### Key Files
288
288
 
289
- | File | Role |
290
- | ---------------------------------------------------- | ---------------------------------------------------------------------------------- |
291
- | `assistant/src/tools/credentials/vault.ts` | `credential_store` tool — store, list, delete, prompt actions |
292
- | `assistant/src/security/secure-keys.ts` | Async secure key CRUD via CES and encrypted file store |
293
- | `assistant/src/tools/credentials/metadata-store.ts` | JSON file metadata CRUD for credential records |
294
- | `assistant/src/tools/credentials/broker.ts` | Brokered credential access with policy enforcement and transient send |
295
- | `assistant/src/tools/credentials/policy-validate.ts` | Policy input validation (allowedTools, allowedDomains) |
296
- | `assistant/src/permissions/secret-prompter.ts` | HTTP secret_request/secret_response flow |
297
- | `assistant/src/security/secret-scanner.ts` | Prefix + shape-based secret regex detection (used by display-time `redactSecrets`) |
298
- | `assistant/src/security/secret-ingress.ts` | Prefix-only ingress check on user messages |
299
- | `assistant/src/util/log-redact.ts` | Pino log serializers prefix-based redaction for logs |
300
- | `clients/macos/.../SecretPromptManager.swift` | Floating panel UI for secure credential entry |
289
+ | File | Role |
290
+ | ----------------------------------------------------------- | ---------------------------------------------------------------------------------- |
291
+ | `assistant/src/runtime/routes/credential-routes.ts` | `assistant credentials` CLI — store, list, delete, inspect, reveal handlers |
292
+ | `assistant/src/credential-execution/prompted-credential.ts` | Persists credentials collected through the secure `credentials prompt` flow |
293
+ | `assistant/src/security/secure-keys.ts` | Async secure key CRUD via CES and encrypted file store |
294
+ | `assistant/src/tools/credentials/metadata-store.ts` | JSON file metadata CRUD for credential records |
295
+ | `assistant/src/tools/credentials/broker.ts` | Brokered credential access with policy enforcement and transient send |
296
+ | `assistant/src/tools/credentials/policy-validate.ts` | Policy input validation (allowedTools, allowedDomains) |
297
+ | `assistant/src/permissions/secret-prompter.ts` | HTTP secret_request/secret_response flow |
298
+ | `assistant/src/security/secret-scanner.ts` | Prefix + shape-based secret regex detection (used by display-time `redactSecrets`) |
299
+ | `assistant/src/security/secret-ingress.ts` | Prefix-only ingress check on user messages |
300
+ | `assistant/src/util/log-redact.ts` | Pino log serializers prefix-based redaction for logs |
301
+ | `clients/web/src/domains/chat/components/secret-prompt-card.tsx` | UI for secure credential entry |
301
302
 
302
303
  ---
303
304
 
@@ -72,13 +72,11 @@ Native clients fetch this metadata at launch via `GET /v1/stt/providers`. No sep
72
72
  | `google-gemini` | `gemini` | shared |
73
73
  | `xai` | `xai` | exclusive |
74
74
 
75
- When the provider ID differs from the credential provider name (e.g. `google-gemini` maps to `gemini`), the key is **shared** with other services that use the same credential. The `sttKeyIsExclusive` / `sttKeyIsShared` helpers in the macOS settings layer derive this automatically from the catalog.
75
+ When the provider ID differs from the credential provider name (e.g. `google-gemini` maps to `gemini`), the key is **shared** with other services that use the same credential.
76
76
 
77
- ### macOS settings key behavior
77
+ ### Client settings key behavior
78
78
 
79
- **File:** `clients/macos/vellum-assistant/Features/Settings/SettingsStore.swift`
80
-
81
- The `sttKeyIsExclusive(for:)` / `sttKeyIsShared(for:)` helpers derive shared-vs-exclusive key behavior from the catalog automatically: if `apiKeyProviderName == id`, the key is exclusive; otherwise it is shared. No new conditionals are needed unless the provider has a non-standard key-ownership model.
79
+ Clients derive shared-vs-exclusive key behavior from the catalog automatically: if `apiKeyProviderName == id`, the key is exclusive; otherwise it is shared. No new conditionals are needed unless the provider has a non-standard key-ownership model. The web settings UI lives in `clients/web/src/domains/settings/ai/speech-to-text-card.tsx`.
82
80
 
83
81
  ## 7. Verify unified STT architecture
84
82
 
@@ -2,14 +2,13 @@
2
2
 
3
3
  This runbook covers the parts of the workflow engine that automated tests do not:
4
4
  real provider calls, real journaled resume across a restart, capability
5
- containment under a live model, flag-off inertness, and persona-leaf voice. Unit
5
+ containment under a live model, and persona-leaf voice. Unit
6
6
  and integration coverage lives under `assistant/src/workflows/*.test.ts` and
7
7
  `assistant/src/__tests__/`; this is the human-in-the-loop pass that runs **last**,
8
8
  on throwaway instances first, and only touches a real inbox or a production
9
9
  instance at the very end.
10
10
 
11
- Work through the steps in order. The `workflows` flag is **off by default**, so
12
- every step that exercises the engine first enables it on a throwaway instance.
11
+ Work through the steps in order, on throwaway instances first.
13
12
 
14
13
  > **Safety rule:** Nothing in this runbook touches a real inbox, a real workspace,
15
14
  > or a production instance until every throwaway-instance step has passed. The
@@ -17,7 +16,7 @@ every step that exercises the engine first enables it on a throwaway instance.
17
16
 
18
17
  ---
19
18
 
20
- ## 1. Hatch a throwaway instance and enable the flag
19
+ ## 1. Hatch a throwaway instance
21
20
 
22
21
  Hatch a disposable Docker instance built from local source:
23
22
 
@@ -26,25 +25,14 @@ vellum hatch --remote docker --source .
26
25
  ```
27
26
 
28
27
  Note the instance name it prints (e.g. `vellum-<adjective>-<animal>`). Use it as
29
- `--assistant <name>` for everything below, or set it active.
30
-
31
- Enable the `workflows` flag via that instance's feature-flag override file. The
32
- override lives in the instance's `protected/feature-flags.json` (overrides here
33
- win over the registry default — see the feature-flag-overrides gotcha):
34
-
35
- ```jsonc
36
- // .../<instance>/protected/feature-flags.json
37
- { "workflows": true }
38
- ```
39
-
40
- Restart the instance so it re-reads the override, then confirm it is up:
28
+ `--assistant <name>` for everything below, or set it active. Confirm it is up:
41
29
 
42
30
  ```
43
31
  vellum ps
44
32
  ```
45
33
 
46
- Sanity-check that the surface is now live: `vellum workflows runs --assistant <name>`
47
- should return an (empty) table rather than a 404.
34
+ Sanity-check that the workflow surface is live: `vellum workflows runs --assistant <name>`
35
+ should return an (empty) table.
48
36
 
49
37
  ---
50
38
 
@@ -168,27 +156,9 @@ or a send tool, with `capabilities.tools` left empty). Run it and confirm:
168
156
 
169
157
  ---
170
158
 
171
- ## 6. Flag-off inertness
172
-
173
- On a **default** instance (no `workflows` override, or set back to `false` and
174
- restart), confirm the whole surface is gone:
175
-
176
- - `run_workflow` and `manage_workflows` are **absent** from the tool set (the
177
- assistant cannot call them).
178
- - The routes 404:
179
-
180
- ```
181
- vellum workflows runs --assistant <default-instance> # request fails (404)
182
- ```
183
-
184
- - A scheduler `workflow`-mode job is **rejected** (the engine gate throws before
185
- any run is launched).
186
-
187
- ---
188
-
189
- ## 7. Real-data smoke (throwaway TEST account)
159
+ ## 6. Real-data smoke (throwaway TEST account)
190
160
 
191
- Only after steps 1–6 pass: run the full path against a **throwaway TEST Google or
161
+ Only after steps 1–5 pass: run the full path against a **throwaway TEST Google or
192
162
  Slack account** with about a dozen messages — never a real account. Drive the
193
163
  end-to-end flow:
194
164
 
@@ -203,17 +173,16 @@ real content, and every side effect was one the manifest declared.
203
173
 
204
174
  ---
205
175
 
206
- ## 8. Production / persona instance — LAST
176
+ ## 7. Production / persona instance — LAST
207
177
 
208
- Flip the `workflows` flag on a real (e.g. persona) instance **only after** the
209
- throwaway instances above pass, and only after the instance is on current code:
178
+ Run on a real (e.g. persona) instance **only after** the throwaway instances
179
+ above pass, and only after the instance is on current code:
210
180
 
211
181
  1. `git pull` and **restart** the instance so it runs the merged code (a restart
212
182
  alone re-runs stale code — see the deploy gotcha).
213
- 2. Enable the flag and restart.
214
- 3. Start with the **smallest real slice** — a few items, dry-run actions where the
183
+ 2. Start with the **smallest real slice** — a few items, dry-run actions where the
215
184
  tool supports it — before any larger or side-effecting run.
216
- 4. **Human-eval the persona-leaf voice**: read a persona leaf's output and confirm
185
+ 3. **Human-eval the persona-leaf voice**: read a persona leaf's output and confirm
217
186
  it reads as the assistant, not as a generic worker.
218
187
 
219
188
  Stop and reassess if any run's `agentsSpawned` approaches `maxAgentsPerRun`, if
package/docs/workflows.md CHANGED
@@ -8,11 +8,9 @@ from each of a hundred documents, draft-then-verify a batch — and you want the
8
8
  results orchestrated deterministically and reported back when the whole run
9
9
  finishes.
10
10
 
11
- Workflows are gated behind the `workflows` feature flag (default **off**). The
12
- `run_workflow` / `manage_workflows` tools are served by the flag-gated `workflows`
11
+ The `run_workflow` / `manage_workflows` tools are served by the `workflows`
13
12
  bundled skill rather than as always-on tools — load it with `skill_load` and invoke
14
- its tools via `skill_execute`. When the flag is off, the skill is absent, the
15
- management routes 404, and the scheduler rejects `workflow`-mode jobs.
13
+ its tools via `skill_execute`.
16
14
 
17
15
  - Engine code: `assistant/src/workflows/`
18
16
  - Skill (tool surface): `assistant/src/config/bundled-skills/workflows/`
@@ -437,7 +435,7 @@ Reached via the skill (`skill_load` then `skill_execute`), not as always-on tool
437
435
  `list_profiles` returns `{ profiles, activeProfile }` — the defined LLM profile
438
436
  names plus the workspace active profile, used to pick a valid leaf `profile`.
439
437
 
440
- ### Routes (read/abort/resume, all 404 when the flag is off)
438
+ ### Routes (read/abort/resume)
441
439
 
442
440
  | Method | Path | Purpose |
443
441
  | ------ | ------------------------------- | ---------------------------------------------------------------------------------------------------- |
@@ -74,7 +74,11 @@ function createMockTransport(): CesTransport & {
74
74
  messages: string[];
75
75
  messageHandler: ((msg: string) => void) | null;
76
76
  alive: boolean;
77
+ /** Simulate the transport dying (e.g. a spurious stdout EOF): mark it dead
78
+ * and fire the registered close handlers, as the real transports do. */
79
+ die: () => void;
77
80
  } {
81
+ const closeHandlers: Array<() => void> = [];
78
82
  const transport = {
79
83
  messages: [] as string[],
80
84
  messageHandler: null as ((msg: string) => void) | null,
@@ -88,9 +92,16 @@ function createMockTransport(): CesTransport & {
88
92
  isAlive() {
89
93
  return transport.alive;
90
94
  },
95
+ onClose(handler: () => void) {
96
+ closeHandlers.push(handler);
97
+ },
91
98
  close() {
92
99
  transport.alive = false;
93
100
  },
101
+ die() {
102
+ transport.alive = false;
103
+ for (const handler of closeHandlers) handler();
104
+ },
94
105
  };
95
106
  return transport;
96
107
  }
@@ -605,6 +616,42 @@ describe("CesRpcClient", () => {
605
616
  expect(client.isReady()).toBe(false);
606
617
  });
607
618
 
619
+ test("a pending request fails fast when the transport dies (no timeout wait)", async () => {
620
+ const transport = createMockTransport();
621
+ const client = createCesRpcClient(transport, {
622
+ handshakeTimeoutMs: 5000,
623
+ // Long timeout on purpose: the call must reject from the transport
624
+ // DEATH, not this timer. Without the fail-fast it would hang 60s.
625
+ requestTimeoutMs: 60_000,
626
+ });
627
+
628
+ // Handshake
629
+ const hPromise = client.handshake();
630
+ const hSent = JSON.parse(transport.messages[0]!);
631
+ transport.messageHandler!(
632
+ JSON.stringify({
633
+ type: "handshake_ack",
634
+ protocolVersion: CES_PROTOCOL_VERSION,
635
+ sessionId: hSent.sessionId,
636
+ accepted: true,
637
+ }),
638
+ );
639
+ await hPromise;
640
+
641
+ const callPromise = client.call("list_credentials", {});
642
+
643
+ // The transport's read side dies (e.g. a spurious stdout EOF on a CES
644
+ // bounce) while the request is in flight — the response can never arrive.
645
+ transport.die();
646
+
647
+ try {
648
+ await callPromise;
649
+ throw new Error("should have thrown");
650
+ } catch (err) {
651
+ expect(err).toBeInstanceOf(CesTransportError);
652
+ }
653
+ });
654
+
608
655
  test("subsequent handshake call returns immediately if already ready", async () => {
609
656
  const transport = createMockTransport();
610
657
  const client = createCesRpcClient(transport, { handshakeTimeoutMs: 5000 });