@vellumai/assistant 0.12.1 → 0.12.2-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 (577) hide show
  1. package/AGENTS.md +4 -0
  2. package/ARCHITECTURE.md +20 -21
  3. package/docs/architecture/turn-actor.md +15 -8
  4. package/docs/guardian-request-flow.md +16 -0
  5. package/node_modules/@vellumai/slack-text/src/index.ts +13 -8
  6. package/node_modules/@vellumai/slack-text/src/label-resolution-entities.test.ts +95 -0
  7. package/openapi.yaml +360 -51
  8. package/package.json +3 -1
  9. package/scripts/smoke-desktop-window-theme.ts +186 -0
  10. package/src/__tests__/always-loaded-tools-guard.test.ts +18 -10
  11. package/src/__tests__/anthropic-provider.test.ts +0 -63
  12. package/src/__tests__/app-compiler.test.ts +6 -0
  13. package/src/__tests__/app-control-tool-schemas.test.ts +5 -2
  14. package/src/__tests__/app-dir-path-guard.test.ts +1 -3
  15. package/src/__tests__/assistant-event-hub-machine-name.test.ts +6 -7
  16. package/src/__tests__/assistant-event-hub.test.ts +44 -0
  17. package/src/__tests__/computer-use-skill-manifest-regression.test.ts +1 -1
  18. package/src/__tests__/computer-use-skill-proxy-bridge.test.ts +5 -2
  19. package/src/__tests__/computer-use-tools.test.ts +19 -7
  20. package/src/__tests__/config-schema-cmd.test.ts +2 -2
  21. package/src/__tests__/config-schema.test.ts +28 -29
  22. package/src/__tests__/config-watcher.test.ts +23 -8
  23. package/src/__tests__/connection-model-compat.test.ts +2 -2
  24. package/src/__tests__/conversation-key-store-disk-view.test.ts +1 -1
  25. package/src/__tests__/conversation-pairing.test.ts +26 -0
  26. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +4 -10
  27. package/src/__tests__/conversation-queue.test.ts +48 -207
  28. package/src/__tests__/conversation-retry-discard.test.ts +54 -1
  29. package/src/__tests__/conversation-runtime-assembly.test.ts +28 -0
  30. package/src/__tests__/conversation-skill-tools.test.ts +27 -0
  31. package/src/__tests__/conversation-surfaces-app-control.test.ts +8 -3
  32. package/src/__tests__/conversation-surfaces-point-at-budget.test.ts +1 -0
  33. package/src/__tests__/conversation-surfaces-point-at-capability.test.ts +1 -0
  34. package/src/__tests__/conversation-surfaces-task-progress.test.ts +67 -0
  35. package/src/__tests__/conversation-title-service.test.ts +14 -10
  36. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +3 -5
  37. package/src/__tests__/conversation-tool-setup-attribution.test.ts +51 -0
  38. package/src/__tests__/credential-record-retire.test.ts +171 -1
  39. package/src/__tests__/credential-routes.test.ts +250 -3
  40. package/src/__tests__/credential-security-invariants.test.ts +5 -3
  41. package/src/__tests__/cu-unified-flow.test.ts +14 -4
  42. package/src/__tests__/document-sync-tags.test.ts +25 -0
  43. package/src/__tests__/drain-kick-guard.test.ts +0 -2
  44. package/src/__tests__/drain-requeue-on-contention.test.ts +0 -6
  45. package/src/__tests__/evict-conversations-for-reload.test.ts +1 -1
  46. package/src/__tests__/git-porcelain.test.ts +47 -0
  47. package/src/__tests__/heartbeat-service.test.ts +56 -40
  48. package/src/__tests__/helpers/set-workspace-mcp.ts +11 -0
  49. package/src/__tests__/host-bash-proxy.test.ts +26 -45
  50. package/src/__tests__/host-browser-proxy.test.ts +6 -17
  51. package/src/__tests__/host-cu-proxy.test.ts +596 -0
  52. package/src/__tests__/host-file-proxy-targeted.test.ts +23 -0
  53. package/src/__tests__/host-file-proxy.test.ts +11 -23
  54. package/src/__tests__/host-transfer-proxy-targeted.test.ts +30 -9
  55. package/src/__tests__/list-messages-camera-frame.test.ts +121 -0
  56. package/src/__tests__/llm-catalog-parity.test.ts +25 -0
  57. package/src/__tests__/mcp-auth-routes.test.ts +6 -9
  58. package/src/__tests__/mcp-config-secret-boundary.test.ts +78 -136
  59. package/src/__tests__/mcp-health-check.test.ts +8 -11
  60. package/src/__tests__/mcp-list-plugin-servers.test.ts +8 -10
  61. package/src/__tests__/message-deleted-hook-dispatch.test.ts +119 -0
  62. package/src/__tests__/messages-after-tiebreaker.test.ts +91 -0
  63. package/src/__tests__/messages-read-boundary-guard.test.ts +1 -1
  64. package/src/__tests__/messaging-send-tool.test.ts +355 -548
  65. package/src/__tests__/model-intents.test.ts +3 -0
  66. package/src/__tests__/notification-vellum-adapter.test.ts +85 -2
  67. package/src/__tests__/oauth-commands-routes.test.ts +168 -0
  68. package/src/__tests__/oauth-provider-profiles.test.ts +18 -0
  69. package/src/__tests__/oauth-provider-serializer.test.ts +22 -0
  70. package/src/__tests__/oauth-providers-routes.test.ts +1 -0
  71. package/src/__tests__/openai-provider.test.ts +1 -0
  72. package/src/__tests__/outlook-messaging-provider.test.ts +6 -6
  73. package/src/__tests__/platform-callback-registration.test.ts +8 -16
  74. package/src/__tests__/platform-client-verify-credential.test.ts +2 -0
  75. package/src/__tests__/plugin-import-boundary-guard.test.ts +14 -0
  76. package/src/__tests__/qdrant-manager.test.ts +132 -0
  77. package/src/__tests__/resolve-app.test.ts +135 -0
  78. package/src/__tests__/scaffold-managed-skill-tool.test.ts +229 -6
  79. package/src/__tests__/script-proxy-certs.test.ts +35 -0
  80. package/src/__tests__/secret-routes-acp-guard.test.ts +59 -1
  81. package/src/__tests__/service-account-token.test.ts +247 -0
  82. package/src/__tests__/skill-feature-flags-integration.test.ts +1 -1
  83. package/src/__tests__/skill-feature-flags.test.ts +2 -2
  84. package/src/__tests__/skills.test.ts +9 -4
  85. package/src/__tests__/source-fingerprint.test.ts +184 -0
  86. package/src/__tests__/subagent-tool-gate-mode.test.ts +70 -3
  87. package/src/__tests__/subagent-tools.test.ts +3 -2
  88. package/src/__tests__/telephony-tts-guard.test.ts +38 -3
  89. package/src/__tests__/test-support/computer-use-skill-harness.ts +3 -2
  90. package/src/__tests__/ui-channel-variants.test.ts +1 -56
  91. package/src/__tests__/unicode.test.ts +13 -0
  92. package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +4 -0
  93. package/src/__tests__/watch-retro-tool-availability.test.ts +8 -17
  94. package/src/__tests__/workspace-git-service.test.ts +26 -0
  95. package/src/__tests__/workspace-migration-154-repair-retired-codex-gpt-5-4-model-ids.test.ts +336 -0
  96. package/src/acp/__tests__/acp-claude-oauth.test.ts +257 -6
  97. package/src/acp/__tests__/acp-credentials.test.ts +13 -0
  98. package/src/acp/__tests__/agent-process.test.ts +258 -4
  99. package/src/acp/__tests__/auth-required.test.ts +96 -0
  100. package/src/acp/__tests__/claude-token-refresh.test.ts +257 -0
  101. package/src/acp/__tests__/client-handler.test.ts +90 -2
  102. package/src/acp/__tests__/helpers/acp-model-option.ts +51 -0
  103. package/src/acp/__tests__/prepare-agent-env.test.ts +60 -1
  104. package/src/acp/__tests__/session-manager-persistence.test.ts +5 -1
  105. package/src/acp/__tests__/session-manager-resume.test.ts +270 -6
  106. package/src/acp/__tests__/session-manager-usage.test.ts +4 -2
  107. package/src/acp/__tests__/session-manager.test.ts +895 -4
  108. package/src/acp/acp-claude-oauth.ts +328 -14
  109. package/src/acp/acp-credentials.ts +19 -0
  110. package/src/acp/agent-process.test.ts +4 -1
  111. package/src/acp/agent-process.ts +96 -10
  112. package/src/acp/auth-required.ts +50 -1
  113. package/src/acp/auto-install.test.ts +9 -7
  114. package/src/acp/claude-token-refresh.ts +150 -0
  115. package/src/acp/client-handler.ts +16 -3
  116. package/src/acp/model-config.test.ts +166 -0
  117. package/src/acp/model-config.ts +138 -0
  118. package/src/acp/prepare-agent-env.ts +21 -6
  119. package/src/acp/resolve-agent.test.ts +220 -6
  120. package/src/acp/resolve-agent.ts +88 -23
  121. package/src/acp/session-manager.test.ts +196 -3
  122. package/src/acp/session-manager.ts +441 -18
  123. package/src/acp/types.ts +50 -5
  124. package/src/agent/loop.ts +1 -0
  125. package/src/api/events/acp-session-model-update.test.ts +88 -0
  126. package/src/api/events/acp-session-model-update.ts +46 -0
  127. package/src/api/events/app-files-changed.ts +4 -3
  128. package/src/api/events/notification-intent.ts +2 -0
  129. package/src/api/events/user-message-echo.ts +7 -1
  130. package/src/api/index.ts +6 -0
  131. package/src/api/responses/conversation-message.ts +5 -0
  132. package/src/apps/app-store.ts +1 -1
  133. package/src/apps/resolve-app.ts +81 -0
  134. package/src/apps/source-fingerprint.ts +266 -0
  135. package/src/avatar/identity-avatar.ts +7 -2
  136. package/src/bundler/app-compiler.ts +15 -0
  137. package/src/calls/__tests__/barge-in-guard.test.ts +81 -0
  138. package/src/{__tests__ → calls/__tests__}/call-controller.test.ts +503 -82
  139. package/src/{__tests__ → calls/__tests__}/media-stream-audio-transcode.test.ts +1 -1
  140. package/src/{__tests__ → calls/__tests__}/media-stream-output.test.ts +37 -8
  141. package/src/{__tests__ → calls/__tests__}/media-stream-parser.test.ts +1 -1
  142. package/src/{__tests__ → calls/__tests__}/media-stream-server-integration.test.ts +150 -57
  143. package/src/{__tests__ → calls/__tests__}/media-stream-stt-session.test.ts +5 -5
  144. package/src/{__tests__ → calls/__tests__}/media-turn-detector.test.ts +1 -1
  145. package/src/{live-voice → calls}/__tests__/progress-narration.test.ts +4 -4
  146. package/src/{live-voice → calls}/__tests__/progress-phrases.test.ts +1 -1
  147. package/src/calls/__tests__/voice-control-protocol.test.ts +120 -25
  148. package/src/calls/__tests__/voice-leg-coordinator.test.ts +229 -0
  149. package/src/calls/__tests__/voice-progress-cadence.test.ts +274 -0
  150. package/src/{__tests__/voice-session-bridge.test.ts → calls/__tests__/voice-session-bridge-turn-flow.test.ts} +20 -12
  151. package/src/calls/__tests__/voice-session-bridge.test.ts +56 -1
  152. package/src/calls/__tests__/voice-triage-escalate.test.ts +105 -0
  153. package/src/calls/barge-in-guard.ts +118 -0
  154. package/src/calls/call-constants.ts +24 -4
  155. package/src/calls/call-controller.ts +499 -199
  156. package/src/calls/call-transport.ts +16 -0
  157. package/src/calls/media-stream-output.ts +32 -0
  158. package/src/calls/media-stream-server.ts +82 -30
  159. package/src/calls/media-stream-stt-session.ts +15 -0
  160. package/src/{live-voice → calls}/progress-narration.ts +2 -2
  161. package/src/calls/resolve-call-tts-provider.ts +33 -2
  162. package/src/calls/tts-call-strategy.ts +9 -5
  163. package/src/calls/types.ts +2 -0
  164. package/src/calls/voice-control-protocol.ts +155 -21
  165. package/src/calls/voice-leg-coordinator.ts +189 -0
  166. package/src/calls/voice-progress-cadence.ts +423 -0
  167. package/src/calls/voice-session-bridge.ts +92 -72
  168. package/src/calls/voice-triage-escalate.ts +178 -59
  169. package/src/cli/commands/__tests__/apps.test.ts +245 -0
  170. package/src/cli/commands/__tests__/bash.test.ts +53 -0
  171. package/src/cli/commands/__tests__/roadmap.help.test.ts +19 -0
  172. package/src/cli/commands/apps.help.ts +67 -13
  173. package/src/cli/commands/apps.ts +183 -0
  174. package/src/cli/commands/bash.help.ts +8 -13
  175. package/src/cli/commands/bash.ts +7 -25
  176. package/src/cli/commands/channels/__tests__/send.test.ts +218 -0
  177. package/src/cli/commands/channels/index.help.ts +70 -2
  178. package/src/cli/commands/channels/index.ts +16 -3
  179. package/src/cli/commands/channels/send.ts +128 -0
  180. package/src/cli/commands/credentials.help.ts +6 -2
  181. package/src/cli/commands/credentials.ts +2 -2
  182. package/src/cli/commands/mcp.help.ts +8 -8
  183. package/src/cli/commands/mcp.ts +1 -1
  184. package/src/cli/commands/oauth/status.ts +5 -0
  185. package/src/cli/commands/roadmap.help.ts +5 -1
  186. package/src/cli/commands/roadmap.ts +1 -1
  187. package/src/cli/lib/bundled-marketplace.json +103 -90
  188. package/src/config/__tests__/default-profile-catalog.test.ts +22 -0
  189. package/src/config/__tests__/memory-retrospective-schema.test.ts +15 -1
  190. package/src/config/__tests__/platform-identity.test.ts +255 -0
  191. package/src/config/__tests__/platform-rehydration.test.ts +99 -9
  192. package/src/config/acp-agent-write.ts +43 -0
  193. package/src/config/acp-defaults.test.ts +13 -3
  194. package/src/config/acp-defaults.ts +23 -6
  195. package/src/config/acp-schema.test.ts +81 -0
  196. package/src/config/acp-schema.ts +30 -2
  197. package/src/config/bundled-skills/acp/SKILL.md +22 -14
  198. package/src/config/bundled-skills/acp/TOOLS.json +5 -1
  199. package/src/config/bundled-skills/app-builder/SKILL.md +12 -9
  200. package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +1 -1
  201. package/src/config/bundled-skills/computer-use/SKILL.md +29 -2
  202. package/src/config/bundled-skills/computer-use/TOOLS.json +99 -0
  203. package/src/config/bundled-skills/computer-use/tools/computer-use-sequence.ts +12 -0
  204. package/src/config/bundled-skills/media-processing/services/preprocess.ts +2 -1
  205. package/src/config/bundled-skills/media-processing/services/processing-pipeline.ts +2 -1
  206. package/src/config/bundled-skills/media-processing/tools/generate-clip.ts +2 -1
  207. package/src/config/bundled-skills/messaging/SKILL.md +3 -3
  208. package/src/config/bundled-skills/messaging/TOOLS.json +3 -3
  209. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +67 -139
  210. package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +1 -1
  211. package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +2 -1
  212. package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +2 -1
  213. package/src/config/bundled-skills/schedule/SKILL.md +3 -4
  214. package/src/config/bundled-skills/screen-annotation/SKILL.md +8 -1
  215. package/src/config/bundled-skills/screen-annotation/tools/screen-annotation.test.ts +4 -1
  216. package/src/config/bundled-skills/skill-management/TOOLS.json +6 -2
  217. package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +5 -2
  218. package/src/config/default-profile-catalog.ts +1 -1
  219. package/src/config/env.ts +2 -2
  220. package/src/config/feature-flag-registry.json +33 -13
  221. package/src/config/loader.ts +2 -0
  222. package/src/config/memory-v3-gate.test.ts +31 -0
  223. package/src/config/memory-v3-gate.ts +20 -16
  224. package/src/config/platform-identity.ts +201 -0
  225. package/src/config/platform-rehydration.ts +11 -27
  226. package/src/config/schema.ts +2 -2
  227. package/src/config/schemas/__tests__/live-voice.test.ts +8 -158
  228. package/src/config/schemas/__tests__/voice.test.ts +148 -0
  229. package/src/config/schemas/calls.ts +1 -28
  230. package/src/config/schemas/live-voice.ts +1 -173
  231. package/src/config/schemas/mcp.ts +1 -1
  232. package/src/config/schemas/memory-retrospective.ts +10 -0
  233. package/src/config/schemas/services.ts +18 -0
  234. package/src/config/schemas/skills.ts +0 -14
  235. package/src/config/schemas/voice.ts +150 -0
  236. package/src/credential-execution/process-manager.test.ts +18 -19
  237. package/src/credential-health/credential-health-service.ts +11 -17
  238. package/src/daemon/__tests__/conversation-tool-setup.test.ts +66 -121
  239. package/src/daemon/__tests__/plugin-mcp-reconcile.test.ts +4 -1
  240. package/src/daemon/__tests__/skill-memory-refresh-mtime.test.ts +133 -0
  241. package/src/daemon/app-change-notify.ts +45 -0
  242. package/src/daemon/config-watcher.ts +13 -194
  243. package/src/daemon/conversation-error.ts +3 -2
  244. package/src/daemon/conversation-media-retry.ts +2 -1
  245. package/src/daemon/conversation-process.ts +35 -64
  246. package/src/daemon/conversation-runtime-assembly.ts +20 -7
  247. package/src/daemon/conversation-skill-tools.ts +13 -31
  248. package/src/daemon/conversation-store.ts +1 -1
  249. package/src/daemon/conversation-surfaces.ts +21 -10
  250. package/src/daemon/conversation-tool-setup.ts +45 -59
  251. package/src/daemon/conversation.ts +0 -3
  252. package/src/daemon/dictation-profile-store.ts +2 -1
  253. package/src/daemon/handlers/shared.ts +2 -1
  254. package/src/daemon/handlers/skills.ts +6 -3
  255. package/src/daemon/host-bash-proxy.ts +8 -12
  256. package/src/daemon/host-browser-proxy.ts +15 -1
  257. package/src/daemon/host-cu-proxy.ts +224 -19
  258. package/src/daemon/host-file-proxy.ts +8 -10
  259. package/src/daemon/host-proxy-preactivation.ts +2 -1
  260. package/src/daemon/host-transfer-proxy.ts +14 -18
  261. package/src/daemon/lifecycle.ts +0 -5
  262. package/src/daemon/mcp-reload-service.ts +5 -5
  263. package/src/daemon/message-types/sync.ts +1 -0
  264. package/src/daemon/orphan-reaper.ts +1 -1
  265. package/src/daemon/providers-setup.ts +1 -1
  266. package/src/daemon/reaction-history-render.ts +2 -1
  267. package/src/daemon/shutdown-handlers.ts +1 -9
  268. package/src/daemon/skill-memory-refresh.ts +64 -0
  269. package/src/daemon/tool-side-effects.ts +7 -18
  270. package/src/desktop/__tests__/fake-desktop.ts +122 -0
  271. package/src/desktop/desktop-chrome-policy.test.ts +79 -0
  272. package/src/desktop/desktop-chrome-policy.ts +47 -0
  273. package/src/desktop/desktop-chrome-session.ts +37 -0
  274. package/src/desktop/desktop-dependencies.test.ts +102 -0
  275. package/src/desktop/desktop-dependencies.ts +308 -0
  276. package/src/desktop/desktop-feature.test.ts +18 -0
  277. package/src/desktop/desktop-feature.ts +19 -0
  278. package/src/desktop/desktop-panel-config.test.ts +135 -0
  279. package/src/desktop/desktop-panel-config.ts +149 -0
  280. package/src/desktop/desktop-session-manager.test.ts +730 -0
  281. package/src/desktop/desktop-session-manager.ts +850 -0
  282. package/src/desktop/desktop-stream-bridge.test.ts +395 -0
  283. package/src/desktop/desktop-stream-bridge.ts +214 -0
  284. package/src/desktop/desktop-wallpaper.test.ts +191 -0
  285. package/src/desktop/desktop-wallpaper.ts +113 -0
  286. package/src/desktop/desktop-window-drag.ts +177 -0
  287. package/src/desktop/desktop-window-manager-config.test.ts +230 -0
  288. package/src/desktop/desktop-window-manager-config.ts +238 -0
  289. package/src/desktop/desktop-window-theme.ts +170 -0
  290. package/src/heartbeat/heartbeat-service.ts +21 -3
  291. package/src/home/__tests__/feed-writer.test.ts +22 -0
  292. package/src/home/feed-writer.ts +11 -2
  293. package/src/hooks/types.ts +33 -0
  294. package/src/i18n/AGENTS.md +22 -0
  295. package/src/i18n/__tests__/i18n.test.ts +108 -0
  296. package/src/i18n/index.ts +16 -0
  297. package/src/i18n/locales.ts +93 -0
  298. package/src/i18n/messages.ts +61 -0
  299. package/src/i18n/title.ts +25 -0
  300. package/src/inbound/platform-callback-registration.ts +4 -5
  301. package/src/live-voice/__tests__/live-voice-agent-turn.test.ts +121 -0
  302. package/src/live-voice/__tests__/live-voice-events.test.ts +8 -7
  303. package/src/live-voice/__tests__/live-voice-metrics.test.ts +0 -3
  304. package/src/live-voice/__tests__/live-voice-progress.test.ts +94 -15
  305. package/src/live-voice/__tests__/live-voice-sight-frame-echo.test.ts +238 -0
  306. package/src/live-voice/__tests__/live-voice-vad.test.ts +2 -2
  307. package/src/live-voice/__tests__/protocol.test.ts +39 -0
  308. package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +13 -69
  309. package/src/live-voice/__tests__/session-controls.test.ts +79 -0
  310. package/src/live-voice/live-voice-photo.ts +25 -6
  311. package/src/live-voice/live-voice-session.ts +297 -699
  312. package/src/live-voice/protocol.ts +60 -2
  313. package/src/live-voice/session-controls.ts +111 -0
  314. package/src/mcp/__tests__/effective-config.test.ts +2 -2
  315. package/src/mcp/__tests__/workspace-mcp-config.test.ts +135 -0
  316. package/src/mcp/effective-config.ts +3 -3
  317. package/src/mcp/mcp-header-store.ts +29 -45
  318. package/src/mcp/spec-schema.ts +111 -0
  319. package/src/mcp/startup.ts +8 -4
  320. package/src/mcp/workspace-mcp-config.ts +188 -0
  321. package/src/messaging/provider.ts +10 -3
  322. package/src/messaging/providers/__tests__/proactive-address.test.ts +95 -0
  323. package/src/messaging/providers/callback-routing.ts +18 -0
  324. package/src/messaging/providers/channel-transport.ts +56 -0
  325. package/src/messaging/providers/discord/transport.ts +17 -0
  326. package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +6 -17
  327. package/src/messaging/providers/slack/adapter.ts +7 -44
  328. package/src/messaging/providers/slack/transport.ts +16 -0
  329. package/src/messaging/providers/telegram-bot/adapter.ts +1 -48
  330. package/src/messaging/providers/telegram-bot/client.ts +0 -26
  331. package/src/messaging/providers/telegram-bot/transport.ts +20 -0
  332. package/src/messaging/providers/whatsapp/adapter.ts +1 -48
  333. package/src/messaging/providers/whatsapp/transport.ts +18 -0
  334. package/src/monitoring/__tests__/workspace-git-heartbeat.test.ts +8 -0
  335. package/src/notifications/__tests__/assistant-reply-producer.test.ts +55 -0
  336. package/src/notifications/__tests__/guardian-feed-projection.test.ts +158 -2
  337. package/src/notifications/__tests__/home-feed-side-effect.test.ts +153 -3
  338. package/src/notifications/__tests__/schedule-result-producer.test.ts +99 -3
  339. package/src/notifications/adapters/macos.ts +3 -0
  340. package/src/notifications/assistant-reply-producer.ts +8 -3
  341. package/src/notifications/conversation-pairing.ts +36 -16
  342. package/src/notifications/delivered-post-record.ts +11 -1
  343. package/src/notifications/guardian-feed-projection.ts +125 -5
  344. package/src/notifications/home-feed-side-effect.ts +55 -7
  345. package/src/notifications/schedule-result-producer.ts +66 -22
  346. package/src/oauth/AGENTS.md +2 -0
  347. package/src/oauth/__tests__/seed-providers-managed.test.ts +149 -0
  348. package/src/oauth/byo-connection.test.ts +105 -0
  349. package/src/oauth/byo-connection.ts +36 -6
  350. package/src/oauth/connection-resolver.test.ts +100 -0
  351. package/src/oauth/connection-resolver.ts +120 -3
  352. package/src/oauth/provider-serializer.ts +9 -0
  353. package/src/oauth/scope-utils.ts +24 -0
  354. package/src/oauth/seed-providers.ts +314 -27
  355. package/src/outbound-proxy/certs.ts +3 -5
  356. package/src/permissions/checker.test.ts +11 -6
  357. package/src/persistence/compaction-log-store-clickhouse.ts +3 -3
  358. package/src/persistence/conversation-crud.ts +71 -63
  359. package/src/persistence/conversation-lineage.ts +12 -4
  360. package/src/persistence/conversation-queries.ts +2 -2
  361. package/src/persistence/conversation-title-placeholders.ts +23 -0
  362. package/src/persistence/conversation-title-service.ts +9 -26
  363. package/src/persistence/embeddings/qdrant-manager.ts +163 -18
  364. package/src/persistence/llm-request-log-sink-clickhouse.ts +3 -3
  365. package/src/persistence/llm-request-log-source-clickhouse.ts +3 -3
  366. package/src/persistence/message-cursor.ts +78 -0
  367. package/src/persistence/message-reads.ts +30 -0
  368. package/src/persistence/migrations/157-invite-contact-id.ts +18 -6
  369. package/src/persistence/migrations/__tests__/157-invite-contact-id.test.ts +149 -0
  370. package/src/persistence/schema/memory-core.ts +5 -0
  371. package/src/platform/client.test.ts +4 -2
  372. package/src/platform/client.ts +2 -10
  373. package/src/plugin-api/constants.ts +2 -0
  374. package/src/plugin-api/index.ts +1 -0
  375. package/src/plugin-api/types.ts +2 -0
  376. package/src/plugins/defaults/index.ts +2 -0
  377. package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +46 -0
  378. package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-backfill.test.ts +118 -0
  379. package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-preserve.test.ts +83 -0
  380. package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +9 -5
  381. package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +20 -10
  382. package/src/plugins/defaults/memory/__tests__/memory-retrospective-prompt.test.ts +5 -0
  383. package/src/plugins/defaults/memory/__tests__/memory-retrospective-state.test.ts +219 -0
  384. package/src/plugins/defaults/memory/__tests__/memory-retrospective-sweep.test.ts +31 -0
  385. package/src/plugins/defaults/memory/__tests__/memory-retrospective-trigger-check.test.ts +63 -0
  386. package/src/plugins/defaults/memory/__tests__/shutdown-hook.test.ts +4 -0
  387. package/src/plugins/defaults/memory/context-search/agent-runner.ts +3 -2
  388. package/src/plugins/defaults/memory/context-search/format.ts +3 -2
  389. package/src/plugins/defaults/memory/context-search/sources/memory-v2.ts +6 -3
  390. package/src/plugins/defaults/memory/context-search/sources/workspace.ts +3 -2
  391. package/src/plugins/defaults/memory/graph/capability-seed.ts +6 -7
  392. package/src/plugins/defaults/memory/hooks/init.ts +10 -0
  393. package/src/plugins/defaults/memory/hooks/message-deleted.ts +21 -0
  394. package/src/plugins/defaults/memory/hooks/shutdown.ts +2 -0
  395. package/src/plugins/defaults/memory/host-utils.ts +10 -0
  396. package/src/plugins/defaults/memory/jobs-worker.ts +13 -0
  397. package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +34 -60
  398. package/src/plugins/defaults/memory/memory-retrospective-cursor-backfill.ts +126 -0
  399. package/src/plugins/defaults/memory/memory-retrospective-cursor-preserve.ts +79 -0
  400. package/src/plugins/defaults/memory/memory-retrospective-cursor.ts +28 -0
  401. package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +3 -5
  402. package/src/plugins/defaults/memory/memory-retrospective-job.ts +12 -3
  403. package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +1 -1
  404. package/src/plugins/defaults/memory/memory-retrospective-state.ts +312 -71
  405. package/src/plugins/defaults/memory/memory-retrospective-sweep.ts +2 -1
  406. package/src/plugins/defaults/memory/memory-retrospective-trigger-check.ts +5 -3
  407. package/src/plugins/defaults/memory/skill-md-mtime-poll.ts +9 -0
  408. package/src/plugins/defaults/memory/startup.ts +28 -2
  409. package/src/plugins/defaults/memory/substrate/__tests__/skill-content.test.ts +7 -9
  410. package/src/plugins/defaults/memory/substrate/page-index.ts +6 -1
  411. package/src/plugins/defaults/memory/substrate/skill-content.ts +2 -2
  412. package/src/plugins/defaults/memory/v1/graph/consolidation.ts +4 -2
  413. package/src/plugins/defaults/memory/v1/graph/extraction.ts +4 -2
  414. package/src/plugins/defaults/memory/v1/graph/retriever.ts +4 -1
  415. package/src/plugins/defaults/memory/v2/reranker.ts +6 -1
  416. package/src/plugins/defaults/memory/v3/__tests__/card.test.ts +24 -0
  417. package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +125 -0
  418. package/src/plugins/defaults/memory/v3/__tests__/sections.test.ts +26 -0
  419. package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +30 -0
  420. package/src/plugins/defaults/memory/v3/card.ts +3 -2
  421. package/src/plugins/defaults/memory/v3/pool-select.ts +5 -3
  422. package/src/plugins/defaults/memory/v3/sections.ts +9 -3
  423. package/src/plugins/defaults/memory/v3/shadow-plugin.ts +5 -2
  424. package/src/plugins/defaults/memory/worker.ts +6 -0
  425. package/src/plugins/mcp-servers.ts +15 -75
  426. package/src/providers/__tests__/connection-model-compat.test.ts +3 -3
  427. package/src/providers/__tests__/retry-callsite.test.ts +41 -0
  428. package/src/providers/__tests__/retry-outbound-sanitize.test.ts +176 -0
  429. package/src/providers/anthropic/client.ts +0 -47
  430. package/src/providers/gemini/client.ts +9 -23
  431. package/src/providers/gemini-thought-signature.test.ts +63 -0
  432. package/src/providers/gemini-thought-signature.ts +44 -0
  433. package/src/providers/inference/__tests__/endpoint-probe.test.ts +42 -3
  434. package/src/providers/inference/auth.ts +2 -3
  435. package/src/providers/inference/connection-availability.ts +27 -8
  436. package/src/providers/inference/endpoint-probe.ts +10 -1
  437. package/src/providers/inference/resolve-auth.ts +22 -4
  438. package/src/providers/inference/service-account-token.ts +248 -0
  439. package/src/providers/model-catalog.ts +34 -0
  440. package/src/providers/model-intents.ts +10 -5
  441. package/src/providers/openai/__tests__/chat-completions-output-token-limit.test.ts +141 -0
  442. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +146 -18
  443. package/src/providers/openai/__tests__/chat-completions-provider-thought-signature.test.ts +613 -0
  444. package/src/providers/openai/__tests__/google-thought-signature.test.ts +235 -0
  445. package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +83 -1
  446. package/src/providers/openai/chat-completions-provider.ts +212 -79
  447. package/src/providers/openai/codex-models.ts +0 -4
  448. package/src/providers/openai/google-thought-signature.ts +320 -0
  449. package/src/providers/opencode/client.test.ts +73 -4
  450. package/src/providers/opencode/client.ts +60 -3
  451. package/src/providers/openrouter/client.ts +20 -0
  452. package/src/providers/outbound-request-sanitize.ts +96 -0
  453. package/src/providers/retry.ts +30 -19
  454. package/src/providers/unparseable-tool-args.ts +5 -1
  455. package/src/runtime/AGENTS.md +3 -1
  456. package/src/runtime/__tests__/channel-send.test.ts +443 -0
  457. package/src/runtime/__tests__/desktop-stream-upgrade.test.ts +89 -0
  458. package/src/runtime/__tests__/host-observe.test.ts +40 -1
  459. package/src/runtime/__tests__/live-voice-upgrade.test.ts +58 -0
  460. package/src/runtime/__tests__/runtime-ws-test-utils.ts +72 -0
  461. package/src/runtime/__tests__/watch-stream-upgrade.test.ts +59 -0
  462. package/src/runtime/agent-wake.ts +2 -1
  463. package/src/runtime/assistant-event-hub.ts +21 -3
  464. package/src/runtime/auth/same-actor.ts +38 -3
  465. package/src/runtime/channel-send.ts +316 -0
  466. package/src/runtime/host-observe.ts +11 -1
  467. package/src/runtime/http-server.ts +160 -151
  468. package/src/runtime/routes/__tests__/acp-claude-auth-routes.test.ts +16 -5
  469. package/src/runtime/routes/__tests__/acp-routes.test.ts +159 -4
  470. package/src/runtime/routes/__tests__/apps-refresh-route.test.ts +275 -0
  471. package/src/runtime/routes/__tests__/channel-send-routes.test.ts +127 -0
  472. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +108 -1
  473. package/src/runtime/routes/__tests__/default-provider-routes.test.ts +2 -2
  474. package/src/runtime/routes/__tests__/dictation-cleanup-guard.test.ts +107 -0
  475. package/src/runtime/routes/__tests__/mcp-add-default-risk.test.ts +16 -12
  476. package/src/runtime/routes/acp-claude-auth-routes.ts +11 -3
  477. package/src/runtime/routes/acp-routes.test.ts +15 -0
  478. package/src/runtime/routes/acp-routes.ts +72 -9
  479. package/src/runtime/routes/app-management-routes.ts +90 -2
  480. package/src/runtime/routes/app-routes.ts +1 -1
  481. package/src/runtime/routes/channel-send-routes.ts +85 -0
  482. package/src/runtime/routes/conversation-list-routes.ts +14 -3
  483. package/src/runtime/routes/conversation-query-routes.ts +40 -0
  484. package/src/runtime/routes/conversation-routes.ts +16 -3
  485. package/src/runtime/routes/credential-routes.ts +20 -14
  486. package/src/runtime/routes/desktop-setup-routes.test.ts +74 -0
  487. package/src/runtime/routes/desktop-setup-routes.ts +35 -0
  488. package/src/runtime/routes/diagnostics-routes.ts +61 -4
  489. package/src/runtime/routes/events-routes.ts +4 -0
  490. package/src/runtime/routes/host-cu-routes.ts +6 -0
  491. package/src/runtime/routes/index.ts +4 -2
  492. package/src/runtime/routes/log-export-routes.ts +2 -0
  493. package/src/runtime/routes/mcp-auth-routes.ts +39 -47
  494. package/src/runtime/routes/migration-routes.ts +6 -20
  495. package/src/runtime/routes/oauth-commands-routes.ts +50 -0
  496. package/src/runtime/routes/oauth-providers.ts +7 -0
  497. package/src/runtime/routes/platform-routes.ts +18 -15
  498. package/src/runtime/routes/workspace-routes.test.ts +55 -26
  499. package/src/runtime/routes/workspace-routes.ts +46 -63
  500. package/src/runtime/routes/workspace-tree-walk.test.ts +244 -0
  501. package/src/runtime/routes/workspace-tree-walk.ts +240 -0
  502. package/src/runtime/routes/workspace-utils.ts +17 -0
  503. package/src/runtime/services/__tests__/conversation-serializer.test.ts +28 -0
  504. package/src/runtime/services/conversation-serializer.ts +16 -3
  505. package/src/runtime/sync/resource-sync-events.ts +2 -1
  506. package/src/schedule/run-script.ts +2 -1
  507. package/src/schedule/schedule-store.ts +9 -2
  508. package/src/schedule/worker.ts +1 -1
  509. package/src/security/__tests__/ces-http-record-backend.test.ts +137 -0
  510. package/src/security/ces-http-record-backend.ts +120 -0
  511. package/src/security/ces-rpc-record-backend.ts +7 -0
  512. package/src/security/untrusted-content.ts +3 -1
  513. package/src/skills/__tests__/workspace-skill-md-signature.test.ts +60 -0
  514. package/src/skills/workspace-skill-md-signature.ts +50 -0
  515. package/src/subagent/consult-context.ts +12 -25
  516. package/src/tools/__tests__/side-effect-abort-guard.test.ts +1 -0
  517. package/src/tools/acp/list-agents.test.ts +3 -1
  518. package/src/tools/acp/spawn.test.ts +146 -11
  519. package/src/tools/acp/spawn.ts +21 -3
  520. package/src/tools/acp/status.test.ts +94 -0
  521. package/src/tools/acp/status.ts +40 -3
  522. package/src/tools/app-control/skill-proxy-bridge.ts +2 -1
  523. package/src/tools/browser/browser-execution.ts +1 -1
  524. package/src/tools/browser/cdp-client/accessibility-snapshot.ts +9 -3
  525. package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +2 -1
  526. package/src/tools/browser/cdp-client/extension-cdp-client.ts +3 -2
  527. package/src/tools/browser/network-recorder.ts +2 -1
  528. package/src/tools/capability-offer.test.ts +22 -2
  529. package/src/tools/capability-offer.ts +16 -1
  530. package/src/tools/computer-use/definitions.ts +121 -2
  531. package/src/tools/computer-use/skill-proxy-bridge.ts +2 -1
  532. package/src/tools/credentials/metadata-store.ts +100 -0
  533. package/src/tools/filesystem/search.ts +2 -1
  534. package/src/tools/host-filesystem/edit.test.ts +2 -1
  535. package/src/tools/host-filesystem/edit.ts +2 -2
  536. package/src/tools/host-filesystem/read.test.ts +2 -1
  537. package/src/tools/host-filesystem/read.ts +2 -2
  538. package/src/tools/host-filesystem/transfer.test.ts +2 -1
  539. package/src/tools/host-filesystem/transfer.ts +2 -2
  540. package/src/tools/host-filesystem/write.test.ts +2 -1
  541. package/src/tools/host-filesystem/write.ts +2 -2
  542. package/src/tools/host-terminal/host-shell.test.ts +16 -0
  543. package/src/tools/host-terminal/host-shell.ts +4 -20
  544. package/src/tools/network/web-search.ts +6 -1
  545. package/src/tools/policy-context.ts +6 -7
  546. package/src/tools/skills/scaffold-managed.ts +61 -3
  547. package/src/tools/subagent/spawn.ts +5 -8
  548. package/src/tools/terminal/__tests__/sanitized-bash.test.ts +57 -0
  549. package/src/tools/terminal/sanitized-bash.ts +105 -0
  550. package/src/tools/ui-surface/channel-variants.ts +10 -59
  551. package/src/tools/watch/watch-retro-report.ts +0 -8
  552. package/src/types/host-capabilities.ts +5 -3
  553. package/src/util/__tests__/language-subtag.test.ts +23 -1
  554. package/src/util/ca-bundle.ts +13 -0
  555. package/src/util/language-subtag.ts +16 -0
  556. package/src/util/platform.ts +8 -0
  557. package/src/util/unicode.ts +15 -1
  558. package/src/watch/watch-retro.ts +6 -8
  559. package/src/watch/watch-timeline.ts +2 -2
  560. package/src/workflows/run-manager.ts +2 -1
  561. package/src/workspace/git-porcelain.ts +66 -0
  562. package/src/workspace/git-service.ts +6 -55
  563. package/src/workspace/migrations/154-repair-retired-codex-gpt-5-4-model-ids.ts +258 -0
  564. package/src/workspace/migrations/155-move-front-model-config-to-voice.ts +78 -0
  565. package/src/workspace/migrations/156-extract-workspace-mcp-json.ts +214 -0
  566. package/src/workspace/migrations/__tests__/155-move-front-model-config-to-voice.test.ts +102 -0
  567. package/src/workspace/migrations/__tests__/156-extract-workspace-mcp-json.test.ts +199 -0
  568. package/src/workspace/migrations/registry.ts +6 -0
  569. package/src/__tests__/app-source-watcher.test.ts +0 -218
  570. package/src/__tests__/config-watcher-skill-reseed.test.ts +0 -359
  571. package/src/__tests__/speaker-identification.test.ts +0 -56
  572. package/src/calls/speaker-identification.ts +0 -233
  573. package/src/daemon/app-source-watcher.ts +0 -218
  574. package/src/messaging/providers/whatsapp/client.ts +0 -29
  575. package/src/runtime/routes/debug-bash-routes.ts +0 -173
  576. package/src/util/fs-watcher-error.ts +0 -36
  577. /package/src/{live-voice → calls}/progress-phrases.ts +0 -0
@@ -0,0 +1,201 @@
1
+ /**
2
+ * Bound platform identity (assistant, organization, user).
3
+ *
4
+ * Django maps an assistant API key to these ids via
5
+ * `POST /v1/internal/assistants/validate/`. Process startup rehydrates from
6
+ * that endpoint into the in-memory overrides. Resolution reads those
7
+ * overrides (and `PLATFORM_ORGANIZATION_ID` / `PLATFORM_USER_ID` when set).
8
+ * When the in-memory assistant id is empty, the next resolve retries
9
+ * validate (single-flight, with a cooldown after a failed attempt).
10
+ */
11
+
12
+ import { credentialKey } from "../security/credential-key.js";
13
+ import { getSecureKeyAsync } from "../security/secure-keys.js";
14
+ import { getLogger } from "../util/logger.js";
15
+ import {
16
+ getPlatformAssistantId,
17
+ getPlatformBaseUrl,
18
+ getPlatformOrganizationId,
19
+ getPlatformUserId,
20
+ setPlatformAssistantId,
21
+ setPlatformOrganizationId,
22
+ setPlatformUserId,
23
+ } from "./env.js";
24
+
25
+ const log = getLogger("platform-identity");
26
+
27
+ export const PLATFORM_IDENTITY_VALIDATE_PATH =
28
+ "/v1/internal/assistants/validate/";
29
+
30
+ const VALIDATE_TIMEOUT_MS = 5_000;
31
+ const ENSURE_COOLDOWN_MS = 10_000;
32
+
33
+ export type PlatformIdentityIds = {
34
+ assistantId: string;
35
+ organizationId: string;
36
+ userId: string;
37
+ };
38
+
39
+ function asNonEmptyString(value: unknown): string | undefined {
40
+ if (typeof value !== "string") {
41
+ return undefined;
42
+ }
43
+ const trimmed = value.trim();
44
+ return trimmed.length > 0 ? trimmed : undefined;
45
+ }
46
+
47
+ export function applyPlatformIdentityIds(ids: PlatformIdentityIds): void {
48
+ if (ids.assistantId) {
49
+ setPlatformAssistantId(ids.assistantId);
50
+ }
51
+ if (ids.organizationId) {
52
+ setPlatformOrganizationId(ids.organizationId);
53
+ }
54
+ if (ids.userId) {
55
+ setPlatformUserId(ids.userId);
56
+ }
57
+ }
58
+
59
+ /**
60
+ * Trade an assistant API key for the bound platform ids.
61
+ *
62
+ * Returns null when the platform is unreachable, refuses the key, or the
63
+ * body has no ids. Never throws.
64
+ */
65
+ export async function fetchPlatformIdentityIds(
66
+ baseUrl: string,
67
+ apiKey: string,
68
+ ): Promise<PlatformIdentityIds | null> {
69
+ const url = `${baseUrl.replace(/\/+$/, "")}${PLATFORM_IDENTITY_VALIDATE_PATH}`;
70
+ try {
71
+ const res = await fetch(url, {
72
+ method: "POST",
73
+ headers: {
74
+ Authorization: `Api-Key ${apiKey}`,
75
+ "Content-Type": "application/json",
76
+ },
77
+ body: "{}",
78
+ signal: AbortSignal.timeout(VALIDATE_TIMEOUT_MS),
79
+ });
80
+ if (!res.ok) {
81
+ log.warn(
82
+ { status: res.status },
83
+ "platform identity validate returned non-2xx",
84
+ );
85
+ return null;
86
+ }
87
+ const body: unknown = await res.json();
88
+ if (!body || typeof body !== "object") {
89
+ return null;
90
+ }
91
+ const obj = body as Record<string, unknown>;
92
+ const assistantId = asNonEmptyString(obj.assistant_id);
93
+ const organizationId = asNonEmptyString(obj.organization_id);
94
+ const userId = asNonEmptyString(obj.user_id);
95
+ if (!assistantId && !organizationId && !userId) {
96
+ return null;
97
+ }
98
+ return {
99
+ assistantId: assistantId ?? "",
100
+ organizationId: organizationId ?? "",
101
+ userId: userId ?? "",
102
+ };
103
+ } catch (err) {
104
+ log.warn({ err }, "platform identity validate failed");
105
+ return null;
106
+ }
107
+ }
108
+
109
+ async function readAssistantApiKey(): Promise<string> {
110
+ try {
111
+ const stored = (
112
+ await getSecureKeyAsync(credentialKey("vellum", "assistant_api_key"))
113
+ )?.trim();
114
+ if (stored) {
115
+ return stored;
116
+ }
117
+ } catch (err) {
118
+ log.warn({ err }, "failed to read assistant API key from credential store");
119
+ }
120
+ return process.env.ASSISTANT_API_KEY?.trim() ?? "";
121
+ }
122
+
123
+ let ensureInFlight: Promise<void> | null = null;
124
+ let nextEnsureAttemptAt = 0;
125
+
126
+ export function _resetPlatformIdentityEnsureForTests(): void {
127
+ ensureInFlight = null;
128
+ nextEnsureAttemptAt = 0;
129
+ }
130
+
131
+ /**
132
+ * Load in-memory platform ids from validate when they are missing.
133
+ *
134
+ * No-ops when the assistant id is already set, when auth prerequisites are
135
+ * missing, or when a failed attempt is still inside the cooldown window.
136
+ * Concurrent callers share one in-flight request.
137
+ */
138
+ export async function ensurePlatformIdentityIds(): Promise<void> {
139
+ if (getPlatformAssistantId().trim()) {
140
+ return;
141
+ }
142
+ if (Date.now() < nextEnsureAttemptAt) {
143
+ return;
144
+ }
145
+ if (!ensureInFlight) {
146
+ ensureInFlight = (async () => {
147
+ try {
148
+ const apiKey = await readAssistantApiKey();
149
+ const baseUrl = getPlatformBaseUrl();
150
+ if (!apiKey || !baseUrl) {
151
+ return;
152
+ }
153
+ const ids = await fetchPlatformIdentityIds(baseUrl, apiKey);
154
+ if (!ids) {
155
+ nextEnsureAttemptAt = Date.now() + ENSURE_COOLDOWN_MS;
156
+ return;
157
+ }
158
+ applyPlatformIdentityIds(ids);
159
+ nextEnsureAttemptAt = 0;
160
+ log.info("Loaded platform identity from platform validate");
161
+ } finally {
162
+ ensureInFlight = null;
163
+ }
164
+ })();
165
+ }
166
+ await ensureInFlight;
167
+ }
168
+
169
+ export async function resolvePlatformAssistantId(): Promise<string> {
170
+ const existing = getPlatformAssistantId().trim();
171
+ if (existing) {
172
+ return existing;
173
+ }
174
+ await ensurePlatformIdentityIds();
175
+ return getPlatformAssistantId().trim();
176
+ }
177
+
178
+ export async function resolvePlatformAssistantIdOrNull(): Promise<
179
+ string | null
180
+ > {
181
+ const id = await resolvePlatformAssistantId();
182
+ return id || null;
183
+ }
184
+
185
+ export async function resolvePlatformOrganizationId(): Promise<string> {
186
+ const existing = getPlatformOrganizationId().trim();
187
+ if (existing) {
188
+ return existing;
189
+ }
190
+ await ensurePlatformIdentityIds();
191
+ return getPlatformOrganizationId().trim();
192
+ }
193
+
194
+ export async function resolvePlatformUserId(): Promise<string> {
195
+ const existing = getPlatformUserId().trim();
196
+ if (existing) {
197
+ return existing;
198
+ }
199
+ await ensurePlatformIdentityIds();
200
+ return getPlatformUserId().trim();
201
+ }
@@ -1,6 +1,5 @@
1
1
  /**
2
- * Rehydrate the in-memory platform identity/URL overrides from the credential
3
- * store at process startup.
2
+ * Rehydrate the in-memory platform identity/URL overrides at process startup.
4
3
  *
5
4
  * These overrides (`setPlatformBaseUrl` and the platform ID setters in
6
5
  * `config/env.ts`) are normally only populated at runtime by the secret-routes
@@ -10,18 +9,17 @@
10
9
  * environment default (e.g. dev-platform) while the credential store holds the
11
10
  * real production values, and requests are sent to the wrong environment.
12
11
  *
12
+ * Bound platform ids come from `POST /v1/internal/assistants/validate/`,
13
+ * which trades the assistant API key for assistant/organization/user ids.
14
+ *
13
15
  * Each field is best-effort: a credential-store read failure is logged and
14
16
  * skipped so a single missing value never blocks startup.
15
17
  */
16
18
  import { credentialKey } from "../security/credential-key.js";
17
19
  import { getSecureKeyAsync } from "../security/secure-keys.js";
18
20
  import { getLogger } from "../util/logger.js";
19
- import {
20
- setPlatformAssistantId,
21
- setPlatformBaseUrl,
22
- setPlatformOrganizationId,
23
- setPlatformUserId,
24
- } from "./env.js";
21
+ import { setPlatformBaseUrl } from "./env.js";
22
+ import { ensurePlatformIdentityIds } from "./platform-identity.js";
25
23
 
26
24
  const log = getLogger("platform-rehydration");
27
25
 
@@ -47,30 +45,16 @@ async function rehydrateField(
47
45
 
48
46
  /**
49
47
  * Rehydrate the platform base URL and the related platform IDs (assistant,
50
- * organization, user) from the credential store into their in-memory
51
- * overrides. Safe to call more than once.
48
+ * organization, user). Safe to call more than once.
52
49
  */
53
50
  export async function rehydratePlatformCredentials(): Promise<void> {
54
- // Base URL first so managed proxy activation resolves the correct
55
- // environment for the ID lookups and every request that follows.
51
+ // Base URL first so managed proxy activation and identity validate resolve
52
+ // the correct environment for every request that follows.
56
53
  await rehydrateField(
57
54
  "platform_base_url",
58
55
  setPlatformBaseUrl,
59
56
  "platform base URL",
60
57
  );
61
- await rehydrateField(
62
- "platform_assistant_id",
63
- setPlatformAssistantId,
64
- "platform assistant ID",
65
- );
66
- await rehydrateField(
67
- "platform_organization_id",
68
- setPlatformOrganizationId,
69
- "platform organization ID",
70
- );
71
- await rehydrateField(
72
- "platform_user_id",
73
- setPlatformUserId,
74
- "platform user ID",
75
- );
58
+
59
+ await ensurePlatformIdentityIds();
76
60
  }
@@ -41,7 +41,6 @@ import {
41
41
  AuditLogConfigSchema,
42
42
  LogFileConfigSchema,
43
43
  } from "./schemas/logging.js";
44
- import { McpConfigSchema } from "./schemas/mcp.js";
45
44
  import { MemoryConfigSchema } from "./schemas/memory.js";
46
45
  import { MigrationsConfigSchema } from "./schemas/migrations.js";
47
46
  import { MonitoringConfigSchema } from "./schemas/monitoring.js";
@@ -62,6 +61,7 @@ import {
62
61
  } from "./schemas/timeouts.js";
63
62
  import { ToolsConfigSchema } from "./schemas/tools.js";
64
63
  import { UserRoutesConfigSchema } from "./schemas/user-routes.js";
64
+ import { VoiceConfigSchema } from "./schemas/voice.js";
65
65
  import { WorkflowsConfigSchema } from "./schemas/workflows.js";
66
66
  import { WorkspaceGitConfigSchema } from "./schemas/workspace-git.js";
67
67
 
@@ -108,7 +108,6 @@ export const AssistantConfigSchema = z.object({
108
108
  ),
109
109
  journal: JournalConfigSchema.default(JournalConfigSchema.parse({})),
110
110
  backup: BackupConfigSchema.default(BackupConfigSchema.parse({})),
111
- mcp: McpConfigSchema.default(McpConfigSchema.parse({})),
112
111
  acp: AcpConfigSchema.default(AcpConfigSchema.parse({})),
113
112
  skills: SkillsConfigSchema.default(SkillsConfigSchema.parse({})),
114
113
  workspaceGit: WorkspaceGitConfigSchema.default(
@@ -119,6 +118,7 @@ export const AssistantConfigSchema = z.object({
119
118
  twilio: TwilioConfigSchema.default(TwilioConfigSchema.parse({})),
120
119
  calls: CallsConfigSchema.default(CallsConfigSchema.parse({})),
121
120
  liveVoice: LiveVoiceConfigSchema.default(LiveVoiceConfigSchema.parse({})),
121
+ voice: VoiceConfigSchema.default(VoiceConfigSchema.parse({})),
122
122
  sight: SightConfigSchema.default(SightConfigSchema.parse({})),
123
123
  whatsapp: WhatsAppConfigSchema.default(WhatsAppConfigSchema.parse({})),
124
124
  telegram: TelegramConfigSchema.default(TelegramConfigSchema.parse({})),
@@ -3,28 +3,9 @@ import { describe, expect, test } from "bun:test";
3
3
  import {
4
4
  LiveVoiceConfigSchema,
5
5
  LiveVoiceFluxConfigSchema,
6
- LiveVoiceFrontModelConfigSchema,
7
6
  LiveVoiceVadConfigSchema,
8
- VALID_LIVE_VOICE_MODES,
9
7
  } from "../live-voice.js";
10
8
 
11
- const PROGRESS_DEFAULTS = {
12
- enabled: true,
13
- opsThreshold: 3,
14
- idleIntervalMs: 5_000,
15
- maxSilenceMs: 35_000,
16
- longOpMs: 15_000,
17
- minGapMs: 6_000,
18
- generationTimeoutMs: 1_500,
19
- };
20
-
21
- const FRONT_MODEL_DEFAULTS = {
22
- endpointDecisionTimeoutMs: 1200,
23
- endpointExtensionMs: 1500,
24
- endpointMaxExtensions: 2,
25
- progress: PROGRESS_DEFAULTS,
26
- };
27
-
28
9
  // `eagerEotThreshold` is deliberately absent: it has no default, and leaving it
29
10
  // unset is what keeps Deepgram from emitting speculative turn events. `model`
30
11
  // is absent for a related reason: unset means the spoken language selects the
@@ -130,117 +111,6 @@ describe("LiveVoiceVadConfigSchema", () => {
130
111
  });
131
112
  });
132
113
 
133
- describe("LiveVoiceFrontModelConfigSchema", () => {
134
- test("empty object parses to defaults", () => {
135
- const parsed = LiveVoiceFrontModelConfigSchema.parse({});
136
- expect(parsed).toEqual(FRONT_MODEL_DEFAULTS);
137
- });
138
-
139
- test("accepts overrides", () => {
140
- const parsed = LiveVoiceFrontModelConfigSchema.parse({
141
- endpointDecisionTimeoutMs: 400,
142
- endpointMaxExtensions: 0,
143
- });
144
- expect(parsed.endpointDecisionTimeoutMs).toBe(400);
145
- expect(parsed.endpointMaxExtensions).toBe(0);
146
- // Unspecified fields still get defaults
147
- expect(parsed.endpointExtensionMs).toBe(1500);
148
- });
149
-
150
- test("strips retired generated-ack settings", () => {
151
- const parsed = LiveVoiceFrontModelConfigSchema.parse({
152
- ackFirstDeltaTimeoutMs: 2500,
153
- ackGenerationTimeoutMs: 600,
154
- });
155
- expect(parsed).not.toHaveProperty("ackFirstDeltaTimeoutMs");
156
- expect(parsed).not.toHaveProperty("ackGenerationTimeoutMs");
157
- });
158
-
159
- test("rejects non-positive endpointDecisionTimeoutMs", () => {
160
- const result = LiveVoiceFrontModelConfigSchema.safeParse({
161
- endpointDecisionTimeoutMs: 0,
162
- });
163
- expect(result.success).toBe(false);
164
- });
165
-
166
- test("rejects negative endpointMaxExtensions", () => {
167
- const result = LiveVoiceFrontModelConfigSchema.safeParse({
168
- endpointMaxExtensions: -1,
169
- });
170
- expect(result.success).toBe(false);
171
- });
172
-
173
- test("absent progress namespace parses to full progress defaults", () => {
174
- const parsed = LiveVoiceFrontModelConfigSchema.parse({});
175
- expect(parsed.progress).toEqual(PROGRESS_DEFAULTS);
176
- });
177
-
178
- test("partial progress overrides merge with defaults", () => {
179
- const parsed = LiveVoiceFrontModelConfigSchema.parse({
180
- progress: { enabled: false, opsThreshold: 5 },
181
- });
182
- expect(parsed.progress.enabled).toBe(false);
183
- expect(parsed.progress.opsThreshold).toBe(5);
184
- // Unspecified progress fields still get defaults
185
- expect(parsed.progress.idleIntervalMs).toBe(5_000);
186
- expect(parsed.progress.maxSilenceMs).toBe(35_000);
187
- expect(parsed.progress.longOpMs).toBe(15_000);
188
- expect(parsed.progress.minGapMs).toBe(6_000);
189
- expect(parsed.progress.generationTimeoutMs).toBe(1_500);
190
- });
191
-
192
- test("a stale maxPerTurn key is stripped, not rejected", () => {
193
- // The cap was removed; configs written while it existed must still parse.
194
- const parsed = LiveVoiceFrontModelConfigSchema.parse({
195
- progress: { maxPerTurn: 3 },
196
- });
197
- expect("maxPerTurn" in parsed.progress).toBe(false);
198
- });
199
-
200
- test("rejects non-positive progress.opsThreshold", () => {
201
- const result = LiveVoiceFrontModelConfigSchema.safeParse({
202
- progress: { opsThreshold: 0 },
203
- });
204
- expect(result.success).toBe(false);
205
- });
206
-
207
- test("rejects a heartbeat ceiling shorter than the idle tick interval", () => {
208
- // The heartbeat is only checked on the idle tick, so a shorter ceiling
209
- // could be overshot by a full interval.
210
- const result = LiveVoiceFrontModelConfigSchema.safeParse({
211
- progress: { idleIntervalMs: 60_000, maxSilenceMs: 10_000 },
212
- });
213
- expect(result.success).toBe(false);
214
- if (!result.success) {
215
- const msgs = result.error.issues.map((issue) => issue.message);
216
- expect(
217
- msgs.some((msg) =>
218
- msg.includes("liveVoice.frontModel.progress.maxSilenceMs"),
219
- ),
220
- ).toBe(true);
221
- }
222
- // The floor itself is allowed: the tick is exactly on the ceiling.
223
- expect(
224
- LiveVoiceFrontModelConfigSchema.safeParse({
225
- progress: { idleIntervalMs: 60_000, maxSilenceMs: 60_000 },
226
- }).success,
227
- ).toBe(true);
228
- });
229
-
230
- test("rejects a non-boolean progress.enabled", () => {
231
- const result = LiveVoiceFrontModelConfigSchema.safeParse({
232
- progress: { enabled: "yes" },
233
- });
234
- expect(result.success).toBe(false);
235
- if (!result.success) {
236
- const msgs = result.error.issues.map((i) => i.message);
237
- expect(
238
- msgs.some((m) => m.includes("liveVoice.frontModel.progress.enabled")),
239
- ).toBe(true);
240
- }
241
- });
242
- });
243
-
244
114
  describe("LiveVoiceFluxConfigSchema", () => {
245
115
  test("empty object parses to defaults, with turn-end on", () => {
246
116
  expect(LiveVoiceFluxConfigSchema.parse({})).toEqual(FLUX_DEFAULTS);
@@ -326,7 +196,6 @@ describe("LiveVoiceConfigSchema", () => {
326
196
  test("empty object parses to defaults", () => {
327
197
  const parsed = LiveVoiceConfigSchema.parse({});
328
198
  expect(parsed).toEqual({
329
- mode: "open-mic",
330
199
  vad: {
331
200
  speechEnergyThreshold: 800,
332
201
  noiseFloorMargin: 3,
@@ -337,11 +206,9 @@ describe("LiveVoiceConfigSchema", () => {
337
206
  echoEmaHalfLifeMs: 400,
338
207
  echoDrainSlackMs: 300,
339
208
  },
340
- frontModel: FRONT_MODEL_DEFAULTS,
341
209
  // Off by default: Flux turn detection is opt-in, so the front-door hold
342
210
  // verdict keeps committing turns until it is enabled.
343
211
  flux: FLUX_DEFAULTS,
344
- maxSessionDurationSeconds: 1800,
345
212
  // Off by default: voice turns carry only their transcript, no audio
346
213
  // artifacts on the conversation messages (JARVIS-1283).
347
214
  archiveAudio: false,
@@ -365,43 +232,26 @@ describe("LiveVoiceConfigSchema", () => {
365
232
 
366
233
  test("accepts overrides", () => {
367
234
  const parsed = LiveVoiceConfigSchema.parse({
368
- mode: "ptt",
369
235
  vad: { silenceThresholdMs: 900 },
370
- frontModel: { endpointDecisionTimeoutMs: 300 },
371
236
  flux: { turnEnd: { enabled: true } },
372
- maxSessionDurationSeconds: 600,
373
237
  });
374
- expect(parsed.mode).toBe("ptt");
375
238
  expect(parsed.vad.silenceThresholdMs).toBe(900);
376
239
  // Unspecified vad fields still get defaults
377
240
  expect(parsed.vad.speechEnergyThreshold).toBe(800);
378
241
  expect(parsed.vad.maxTurnDurationMs).toBe(30_000);
379
- // Partial frontModel overrides merge with defaults
380
- expect(parsed.frontModel.endpointDecisionTimeoutMs).toBe(300);
381
- expect(parsed.frontModel.endpointExtensionMs).toBe(1500);
382
242
  // Partial flux overrides merge with defaults
383
243
  expect(parsed.flux.turnEnd.enabled).toBe(true);
384
244
  expect(parsed.flux.eotThreshold).toBe(0.7);
385
- expect(parsed.maxSessionDurationSeconds).toBe(600);
386
- });
387
-
388
- test("rejects invalid mode", () => {
389
- const result = LiveVoiceConfigSchema.safeParse({ mode: "always-on" });
390
- expect(result.success).toBe(false);
391
- if (!result.success) {
392
- const msgs = result.error.issues.map((i) => i.message);
393
- expect(msgs.some((m) => m.includes("liveVoice.mode"))).toBe(true);
394
- }
395
245
  });
396
246
 
397
- test("rejects non-positive maxSessionDurationSeconds", () => {
398
- const result = LiveVoiceConfigSchema.safeParse({
399
- maxSessionDurationSeconds: -1,
247
+ test("persisted configs carrying retired keys keep parsing", () => {
248
+ // Zod strips unrecognized keys, so a persisted config that still carries
249
+ // `mode` or `maxSessionDurationSeconds` parses.
250
+ const parsed = LiveVoiceConfigSchema.parse({
251
+ mode: "ptt",
252
+ maxSessionDurationSeconds: 600,
400
253
  });
401
- expect(result.success).toBe(false);
402
- });
403
-
404
- test("VALID_LIVE_VOICE_MODES lists ptt and open-mic", () => {
405
- expect(VALID_LIVE_VOICE_MODES).toEqual(["ptt", "open-mic"]);
254
+ expect(parsed).not.toHaveProperty("mode");
255
+ expect(parsed).not.toHaveProperty("maxSessionDurationSeconds");
406
256
  });
407
257
  });
@@ -0,0 +1,148 @@
1
+ import { describe, expect, test } from "bun:test";
2
+
3
+ import { VoiceConfigSchema, VoiceFrontModelConfigSchema } from "../voice.js";
4
+
5
+ const PROGRESS_DEFAULTS = {
6
+ enabled: true,
7
+ opsThreshold: 3,
8
+ idleIntervalMs: 5_000,
9
+ maxSilenceMs: 35_000,
10
+ longOpMs: 15_000,
11
+ minGapMs: 6_000,
12
+ generationTimeoutMs: 1_500,
13
+ };
14
+
15
+ const FRONT_MODEL_DEFAULTS = {
16
+ endpointDecisionTimeoutMs: 1200,
17
+ endpointExtensionMs: 1500,
18
+ endpointMaxExtensions: 2,
19
+ progress: PROGRESS_DEFAULTS,
20
+ };
21
+
22
+ describe("VoiceFrontModelConfigSchema", () => {
23
+ test("empty object parses to defaults", () => {
24
+ const parsed = VoiceFrontModelConfigSchema.parse({});
25
+ expect(parsed).toEqual(FRONT_MODEL_DEFAULTS);
26
+ });
27
+
28
+ test("accepts overrides", () => {
29
+ const parsed = VoiceFrontModelConfigSchema.parse({
30
+ endpointDecisionTimeoutMs: 400,
31
+ endpointMaxExtensions: 0,
32
+ });
33
+ expect(parsed.endpointDecisionTimeoutMs).toBe(400);
34
+ expect(parsed.endpointMaxExtensions).toBe(0);
35
+ // Unspecified fields still get defaults
36
+ expect(parsed.endpointExtensionMs).toBe(1500);
37
+ });
38
+
39
+ test("strips retired generated-ack settings", () => {
40
+ const parsed = VoiceFrontModelConfigSchema.parse({
41
+ ackFirstDeltaTimeoutMs: 2500,
42
+ ackGenerationTimeoutMs: 600,
43
+ });
44
+ expect(parsed).not.toHaveProperty("ackFirstDeltaTimeoutMs");
45
+ expect(parsed).not.toHaveProperty("ackGenerationTimeoutMs");
46
+ });
47
+
48
+ test("rejects non-positive endpointDecisionTimeoutMs", () => {
49
+ const result = VoiceFrontModelConfigSchema.safeParse({
50
+ endpointDecisionTimeoutMs: 0,
51
+ });
52
+ expect(result.success).toBe(false);
53
+ });
54
+
55
+ test("rejects negative endpointMaxExtensions", () => {
56
+ const result = VoiceFrontModelConfigSchema.safeParse({
57
+ endpointMaxExtensions: -1,
58
+ });
59
+ expect(result.success).toBe(false);
60
+ });
61
+
62
+ test("absent progress namespace parses to full progress defaults", () => {
63
+ const parsed = VoiceFrontModelConfigSchema.parse({});
64
+ expect(parsed.progress).toEqual(PROGRESS_DEFAULTS);
65
+ });
66
+
67
+ test("partial progress overrides merge with defaults", () => {
68
+ const parsed = VoiceFrontModelConfigSchema.parse({
69
+ progress: { enabled: false, opsThreshold: 5 },
70
+ });
71
+ expect(parsed.progress.enabled).toBe(false);
72
+ expect(parsed.progress.opsThreshold).toBe(5);
73
+ // Unspecified progress fields still get defaults
74
+ expect(parsed.progress.idleIntervalMs).toBe(5_000);
75
+ expect(parsed.progress.maxSilenceMs).toBe(35_000);
76
+ expect(parsed.progress.longOpMs).toBe(15_000);
77
+ expect(parsed.progress.minGapMs).toBe(6_000);
78
+ expect(parsed.progress.generationTimeoutMs).toBe(1_500);
79
+ });
80
+
81
+ test("a stale maxPerTurn key is stripped, not rejected", () => {
82
+ // The cap was removed; configs written while it existed must still parse.
83
+ const parsed = VoiceFrontModelConfigSchema.parse({
84
+ progress: { maxPerTurn: 3 },
85
+ });
86
+ expect("maxPerTurn" in parsed.progress).toBe(false);
87
+ });
88
+
89
+ test("rejects non-positive progress.opsThreshold", () => {
90
+ const result = VoiceFrontModelConfigSchema.safeParse({
91
+ progress: { opsThreshold: 0 },
92
+ });
93
+ expect(result.success).toBe(false);
94
+ });
95
+
96
+ test("rejects a heartbeat ceiling shorter than the idle tick interval", () => {
97
+ // The heartbeat is only checked on the idle tick, so a shorter ceiling
98
+ // could be overshot by a full interval.
99
+ const result = VoiceFrontModelConfigSchema.safeParse({
100
+ progress: { idleIntervalMs: 60_000, maxSilenceMs: 10_000 },
101
+ });
102
+ expect(result.success).toBe(false);
103
+ if (!result.success) {
104
+ const msgs = result.error.issues.map((issue) => issue.message);
105
+ expect(
106
+ msgs.some((msg) =>
107
+ msg.includes("voice.frontModel.progress.maxSilenceMs"),
108
+ ),
109
+ ).toBe(true);
110
+ }
111
+ // The floor itself is allowed: the tick is exactly on the ceiling.
112
+ expect(
113
+ VoiceFrontModelConfigSchema.safeParse({
114
+ progress: { idleIntervalMs: 60_000, maxSilenceMs: 60_000 },
115
+ }).success,
116
+ ).toBe(true);
117
+ });
118
+
119
+ test("rejects a non-boolean progress.enabled", () => {
120
+ const result = VoiceFrontModelConfigSchema.safeParse({
121
+ progress: { enabled: "yes" },
122
+ });
123
+ expect(result.success).toBe(false);
124
+ if (!result.success) {
125
+ const msgs = result.error.issues.map((i) => i.message);
126
+ expect(
127
+ msgs.some((m) => m.includes("voice.frontModel.progress.enabled")),
128
+ ).toBe(true);
129
+ }
130
+ });
131
+ });
132
+
133
+ describe("VoiceConfigSchema", () => {
134
+ test("empty object parses to front-model defaults", () => {
135
+ expect(VoiceConfigSchema.parse({})).toEqual({
136
+ frontModel: FRONT_MODEL_DEFAULTS,
137
+ });
138
+ });
139
+
140
+ test("partial frontModel overrides merge with defaults", () => {
141
+ const parsed = VoiceConfigSchema.parse({
142
+ frontModel: { endpointDecisionTimeoutMs: 300 },
143
+ });
144
+ expect(parsed.frontModel.endpointDecisionTimeoutMs).toBe(300);
145
+ expect(parsed.frontModel.endpointExtensionMs).toBe(1500);
146
+ expect(parsed.frontModel.progress).toEqual(PROGRESS_DEFAULTS);
147
+ });
148
+ });