@vellumai/assistant 0.12.1-staging.1 → 0.12.1

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 (404) hide show
  1. package/AGENTS.md +0 -4
  2. package/ARCHITECTURE.md +12 -11
  3. package/docs/architecture/turn-actor.md +8 -15
  4. package/openapi.yaml +1 -291
  5. package/package.json +1 -1
  6. package/src/__tests__/always-loaded-tools-guard.test.ts +10 -18
  7. package/src/__tests__/app-compiler.test.ts +0 -6
  8. package/src/__tests__/app-control-tool-schemas.test.ts +2 -5
  9. package/src/__tests__/app-dir-path-guard.test.ts +3 -1
  10. package/src/__tests__/app-source-watcher.test.ts +218 -0
  11. package/src/{calls/__tests__ → __tests__}/call-controller.test.ts +82 -503
  12. package/src/__tests__/computer-use-skill-proxy-bridge.test.ts +2 -5
  13. package/src/__tests__/computer-use-tools.test.ts +5 -17
  14. package/src/__tests__/config-schema-cmd.test.ts +2 -2
  15. package/src/__tests__/config-schema.test.ts +29 -28
  16. package/src/__tests__/config-watcher-skill-reseed.test.ts +359 -0
  17. package/src/__tests__/config-watcher.test.ts +8 -23
  18. package/src/__tests__/connection-model-compat.test.ts +2 -2
  19. package/src/__tests__/conversation-key-store-disk-view.test.ts +1 -1
  20. package/src/__tests__/conversation-pairing.test.ts +0 -26
  21. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +10 -4
  22. package/src/__tests__/conversation-queue.test.ts +207 -48
  23. package/src/__tests__/conversation-retry-discard.test.ts +1 -54
  24. package/src/__tests__/conversation-skill-tools.test.ts +0 -27
  25. package/src/__tests__/conversation-surfaces-app-control.test.ts +3 -8
  26. package/src/__tests__/conversation-surfaces-task-progress.test.ts +0 -67
  27. package/src/__tests__/conversation-title-service.test.ts +10 -14
  28. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +5 -3
  29. package/src/__tests__/conversation-tool-setup-attribution.test.ts +0 -51
  30. package/src/__tests__/credential-record-retire.test.ts +1 -171
  31. package/src/__tests__/credential-routes.test.ts +3 -212
  32. package/src/__tests__/credential-security-invariants.test.ts +1 -2
  33. package/src/__tests__/cu-unified-flow.test.ts +2 -8
  34. package/src/__tests__/drain-kick-guard.test.ts +2 -0
  35. package/src/__tests__/drain-requeue-on-contention.test.ts +6 -0
  36. package/src/__tests__/evict-conversations-for-reload.test.ts +1 -1
  37. package/src/__tests__/host-bash-proxy.test.ts +45 -26
  38. package/src/__tests__/host-browser-proxy.test.ts +17 -6
  39. package/src/__tests__/host-file-proxy-targeted.test.ts +0 -23
  40. package/src/__tests__/host-file-proxy.test.ts +23 -11
  41. package/src/__tests__/host-transfer-proxy-targeted.test.ts +9 -30
  42. package/src/__tests__/llm-catalog-parity.test.ts +0 -25
  43. package/src/__tests__/mcp-auth-routes.test.ts +9 -6
  44. package/src/__tests__/mcp-config-secret-boundary.test.ts +136 -78
  45. package/src/__tests__/mcp-health-check.test.ts +11 -8
  46. package/src/__tests__/mcp-list-plugin-servers.test.ts +10 -8
  47. package/src/{calls/__tests__ → __tests__}/media-stream-audio-transcode.test.ts +1 -1
  48. package/src/{calls/__tests__ → __tests__}/media-stream-output.test.ts +8 -37
  49. package/src/{calls/__tests__ → __tests__}/media-stream-parser.test.ts +1 -1
  50. package/src/{calls/__tests__ → __tests__}/media-stream-server-integration.test.ts +57 -150
  51. package/src/{calls/__tests__ → __tests__}/media-stream-stt-session.test.ts +5 -5
  52. package/src/{calls/__tests__ → __tests__}/media-turn-detector.test.ts +1 -1
  53. package/src/__tests__/messages-after-tiebreaker.test.ts +0 -91
  54. package/src/__tests__/messages-read-boundary-guard.test.ts +1 -1
  55. package/src/__tests__/messaging-send-tool.test.ts +548 -355
  56. package/src/__tests__/model-intents.test.ts +0 -3
  57. package/src/__tests__/outlook-messaging-provider.test.ts +6 -6
  58. package/src/__tests__/platform-client-verify-credential.test.ts +0 -2
  59. package/src/__tests__/plugin-import-boundary-guard.test.ts +0 -13
  60. package/src/__tests__/script-proxy-certs.test.ts +0 -35
  61. package/src/__tests__/skill-feature-flags-integration.test.ts +1 -1
  62. package/src/__tests__/skill-feature-flags.test.ts +2 -2
  63. package/src/__tests__/speaker-identification.test.ts +56 -0
  64. package/src/__tests__/subagent-tool-gate-mode.test.ts +3 -19
  65. package/src/__tests__/subagent-tools.test.ts +2 -3
  66. package/src/__tests__/telephony-tts-guard.test.ts +3 -38
  67. package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +0 -4
  68. package/src/{calls/__tests__/voice-session-bridge-turn-flow.test.ts → __tests__/voice-session-bridge.test.ts} +12 -20
  69. package/src/__tests__/watch-retro-tool-availability.test.ts +17 -8
  70. package/src/__tests__/workspace-git-service.test.ts +0 -26
  71. package/src/acp/__tests__/agent-process.test.ts +4 -258
  72. package/src/acp/__tests__/auth-required.test.ts +0 -96
  73. package/src/acp/__tests__/client-handler.test.ts +2 -90
  74. package/src/acp/__tests__/session-manager-persistence.test.ts +1 -5
  75. package/src/acp/__tests__/session-manager-resume.test.ts +6 -270
  76. package/src/acp/__tests__/session-manager-usage.test.ts +2 -4
  77. package/src/acp/__tests__/session-manager.test.ts +4 -895
  78. package/src/acp/agent-process.test.ts +1 -4
  79. package/src/acp/agent-process.ts +9 -94
  80. package/src/acp/auth-required.ts +1 -50
  81. package/src/acp/auto-install.test.ts +7 -9
  82. package/src/acp/client-handler.ts +3 -16
  83. package/src/acp/resolve-agent.test.ts +6 -220
  84. package/src/acp/resolve-agent.ts +23 -88
  85. package/src/acp/session-manager.test.ts +3 -196
  86. package/src/acp/session-manager.ts +18 -441
  87. package/src/acp/types.ts +5 -50
  88. package/src/api/events/app-files-changed.ts +3 -4
  89. package/src/api/index.ts +0 -6
  90. package/src/apps/app-store.ts +1 -1
  91. package/src/bundler/app-compiler.ts +0 -15
  92. package/src/calls/__tests__/voice-control-protocol.test.ts +25 -58
  93. package/src/calls/__tests__/voice-session-bridge.test.ts +1 -37
  94. package/src/calls/__tests__/voice-triage-escalate.test.ts +0 -105
  95. package/src/calls/call-constants.ts +4 -24
  96. package/src/calls/call-controller.ts +199 -499
  97. package/src/calls/call-transport.ts +0 -16
  98. package/src/calls/media-stream-output.ts +0 -32
  99. package/src/calls/media-stream-server.ts +30 -82
  100. package/src/calls/media-stream-stt-session.ts +0 -15
  101. package/src/calls/resolve-call-tts-provider.ts +2 -33
  102. package/src/calls/speaker-identification.ts +233 -0
  103. package/src/calls/tts-call-strategy.ts +5 -9
  104. package/src/calls/types.ts +0 -2
  105. package/src/calls/voice-control-protocol.ts +21 -53
  106. package/src/calls/voice-session-bridge.ts +46 -59
  107. package/src/calls/voice-triage-escalate.ts +56 -175
  108. package/src/cli/commands/apps.help.ts +13 -67
  109. package/src/cli/commands/apps.ts +0 -183
  110. package/src/cli/commands/channels/index.help.ts +2 -70
  111. package/src/cli/commands/channels/index.ts +3 -16
  112. package/src/cli/commands/credentials.help.ts +2 -6
  113. package/src/cli/commands/credentials.ts +2 -2
  114. package/src/cli/commands/mcp.help.ts +8 -8
  115. package/src/cli/commands/mcp.ts +1 -1
  116. package/src/cli/commands/roadmap.help.ts +1 -5
  117. package/src/cli/commands/roadmap.ts +1 -1
  118. package/src/cli/lib/bundled-marketplace.json +90 -103
  119. package/src/config/__tests__/default-profile-catalog.test.ts +0 -22
  120. package/src/config/__tests__/memory-retrospective-schema.test.ts +1 -15
  121. package/src/config/__tests__/platform-rehydration.test.ts +0 -93
  122. package/src/config/acp-defaults.test.ts +3 -13
  123. package/src/config/acp-defaults.ts +6 -23
  124. package/src/config/acp-schema.ts +2 -30
  125. package/src/config/bundled-skills/acp/SKILL.md +14 -22
  126. package/src/config/bundled-skills/acp/TOOLS.json +1 -5
  127. package/src/config/bundled-skills/app-builder/SKILL.md +9 -12
  128. package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +1 -1
  129. package/src/config/bundled-skills/messaging/TOOLS.json +3 -3
  130. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +139 -67
  131. package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +1 -1
  132. package/src/config/bundled-skills/screen-annotation/SKILL.md +1 -8
  133. package/src/config/bundled-skills/screen-annotation/tools/screen-annotation.test.ts +1 -4
  134. package/src/config/bundled-skills/skill-management/TOOLS.json +2 -2
  135. package/src/config/default-profile-catalog.ts +1 -1
  136. package/src/config/env.ts +2 -2
  137. package/src/config/feature-flag-registry.json +12 -8
  138. package/src/config/memory-v3-gate.test.ts +0 -31
  139. package/src/config/memory-v3-gate.ts +16 -20
  140. package/src/config/platform-rehydration.ts +21 -75
  141. package/src/config/schema.ts +2 -2
  142. package/src/config/schemas/__tests__/live-voice.test.ts +158 -8
  143. package/src/config/schemas/calls.ts +28 -1
  144. package/src/config/schemas/live-voice.ts +173 -1
  145. package/src/config/schemas/mcp.ts +1 -1
  146. package/src/config/schemas/memory-retrospective.ts +0 -10
  147. package/src/config/schemas/skills.ts +14 -0
  148. package/src/credential-execution/process-manager.test.ts +19 -18
  149. package/src/daemon/__tests__/conversation-tool-setup.test.ts +121 -66
  150. package/src/daemon/__tests__/plugin-mcp-reconcile.test.ts +1 -4
  151. package/src/daemon/app-source-watcher.ts +218 -0
  152. package/src/daemon/config-watcher.ts +194 -13
  153. package/src/daemon/conversation-process.ts +64 -35
  154. package/src/daemon/conversation-skill-tools.ts +31 -13
  155. package/src/daemon/conversation-store.ts +1 -1
  156. package/src/daemon/conversation-surfaces.ts +8 -10
  157. package/src/daemon/conversation-tool-setup.ts +43 -37
  158. package/src/daemon/conversation.ts +3 -0
  159. package/src/daemon/host-bash-proxy.ts +12 -8
  160. package/src/daemon/host-browser-proxy.ts +1 -15
  161. package/src/daemon/host-file-proxy.ts +10 -8
  162. package/src/daemon/host-transfer-proxy.ts +18 -14
  163. package/src/daemon/lifecycle.ts +5 -0
  164. package/src/daemon/mcp-reload-service.ts +5 -5
  165. package/src/daemon/message-types/sync.ts +0 -1
  166. package/src/daemon/providers-setup.ts +1 -1
  167. package/src/daemon/shutdown-handlers.ts +9 -1
  168. package/src/daemon/skill-memory-refresh.ts +0 -64
  169. package/src/daemon/tool-side-effects.ts +18 -7
  170. package/src/hooks/types.ts +0 -33
  171. package/src/inbound/platform-callback-registration.ts +5 -4
  172. package/src/live-voice/__tests__/live-voice-metrics.test.ts +3 -0
  173. package/src/live-voice/__tests__/live-voice-progress.test.ts +15 -15
  174. package/src/live-voice/__tests__/live-voice-vad.test.ts +2 -2
  175. package/src/{calls → live-voice}/__tests__/progress-narration.test.ts +4 -4
  176. package/src/{calls → live-voice}/__tests__/progress-phrases.test.ts +1 -1
  177. package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +69 -13
  178. package/src/live-voice/live-voice-session.ts +693 -213
  179. package/src/{calls → live-voice}/progress-narration.ts +2 -2
  180. package/src/live-voice/protocol.ts +2 -0
  181. package/src/mcp/__tests__/effective-config.test.ts +2 -2
  182. package/src/mcp/effective-config.ts +3 -3
  183. package/src/mcp/mcp-header-store.ts +45 -29
  184. package/src/mcp/startup.ts +4 -8
  185. package/src/messaging/provider.ts +3 -10
  186. package/src/messaging/providers/callback-routing.ts +0 -18
  187. package/src/messaging/providers/channel-transport.ts +0 -56
  188. package/src/messaging/providers/discord/transport.ts +0 -17
  189. package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +17 -6
  190. package/src/messaging/providers/slack/adapter.ts +44 -7
  191. package/src/messaging/providers/slack/transport.ts +0 -16
  192. package/src/messaging/providers/telegram-bot/adapter.ts +48 -1
  193. package/src/messaging/providers/telegram-bot/client.ts +26 -0
  194. package/src/messaging/providers/telegram-bot/transport.ts +0 -20
  195. package/src/messaging/providers/whatsapp/adapter.ts +48 -1
  196. package/src/messaging/providers/whatsapp/client.ts +29 -0
  197. package/src/messaging/providers/whatsapp/transport.ts +0 -18
  198. package/src/monitoring/__tests__/workspace-git-heartbeat.test.ts +0 -8
  199. package/src/notifications/__tests__/assistant-reply-producer.test.ts +0 -55
  200. package/src/notifications/__tests__/home-feed-side-effect.test.ts +3 -153
  201. package/src/notifications/assistant-reply-producer.ts +3 -8
  202. package/src/notifications/conversation-pairing.ts +16 -36
  203. package/src/notifications/delivered-post-record.ts +1 -11
  204. package/src/notifications/home-feed-side-effect.ts +7 -55
  205. package/src/outbound-proxy/certs.ts +5 -3
  206. package/src/permissions/checker.test.ts +6 -11
  207. package/src/persistence/compaction-log-store-clickhouse.ts +3 -3
  208. package/src/persistence/conversation-crud.ts +63 -71
  209. package/src/persistence/conversation-lineage.ts +4 -12
  210. package/src/persistence/conversation-title-service.ts +26 -9
  211. package/src/persistence/llm-request-log-sink-clickhouse.ts +3 -3
  212. package/src/persistence/llm-request-log-source-clickhouse.ts +3 -3
  213. package/src/persistence/message-reads.ts +0 -30
  214. package/src/persistence/migrations/157-invite-contact-id.ts +6 -18
  215. package/src/persistence/schema/memory-core.ts +0 -5
  216. package/src/platform/client.test.ts +0 -2
  217. package/src/platform/client.ts +10 -2
  218. package/src/plugin-api/constants.ts +0 -2
  219. package/src/plugin-api/index.ts +0 -1
  220. package/src/plugin-api/types.ts +0 -2
  221. package/src/plugins/defaults/index.ts +0 -2
  222. package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +0 -46
  223. package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +5 -9
  224. package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +10 -20
  225. package/src/plugins/defaults/memory/__tests__/memory-retrospective-state.test.ts +0 -219
  226. package/src/plugins/defaults/memory/__tests__/memory-retrospective-sweep.test.ts +0 -31
  227. package/src/plugins/defaults/memory/__tests__/memory-retrospective-trigger-check.test.ts +0 -63
  228. package/src/plugins/defaults/memory/__tests__/shutdown-hook.test.ts +0 -4
  229. package/src/plugins/defaults/memory/graph/capability-seed.ts +6 -4
  230. package/src/plugins/defaults/memory/hooks/init.ts +0 -10
  231. package/src/plugins/defaults/memory/hooks/shutdown.ts +0 -2
  232. package/src/plugins/defaults/memory/jobs-worker.ts +0 -13
  233. package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +60 -34
  234. package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +5 -3
  235. package/src/plugins/defaults/memory/memory-retrospective-job.ts +3 -12
  236. package/src/plugins/defaults/memory/memory-retrospective-state.ts +71 -312
  237. package/src/plugins/defaults/memory/memory-retrospective-sweep.ts +1 -2
  238. package/src/plugins/defaults/memory/memory-retrospective-trigger-check.ts +3 -5
  239. package/src/plugins/defaults/memory/startup.ts +1 -6
  240. package/src/plugins/defaults/memory/substrate/__tests__/skill-content.test.ts +9 -7
  241. package/src/plugins/defaults/memory/substrate/skill-content.ts +2 -2
  242. package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +0 -89
  243. package/src/plugins/defaults/memory/worker.ts +0 -6
  244. package/src/plugins/mcp-servers.ts +75 -15
  245. package/src/providers/__tests__/connection-model-compat.test.ts +3 -3
  246. package/src/providers/gemini/client.ts +23 -9
  247. package/src/providers/model-catalog.ts +0 -34
  248. package/src/providers/model-intents.ts +5 -10
  249. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +18 -146
  250. package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +1 -83
  251. package/src/providers/openai/chat-completions-provider.ts +78 -192
  252. package/src/providers/openai/codex-models.ts +4 -0
  253. package/src/runtime/AGENTS.md +0 -2
  254. package/src/runtime/auth/same-actor.ts +3 -38
  255. package/src/runtime/http-server.ts +151 -160
  256. package/src/runtime/routes/__tests__/acp-routes.test.ts +4 -159
  257. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +1 -108
  258. package/src/runtime/routes/__tests__/mcp-add-default-risk.test.ts +12 -16
  259. package/src/runtime/routes/acp-routes.test.ts +0 -15
  260. package/src/runtime/routes/acp-routes.ts +9 -72
  261. package/src/runtime/routes/app-management-routes.ts +0 -73
  262. package/src/runtime/routes/app-routes.ts +1 -1
  263. package/src/runtime/routes/conversation-list-routes.ts +3 -14
  264. package/src/runtime/routes/conversation-query-routes.ts +0 -40
  265. package/src/runtime/routes/credential-routes.ts +10 -19
  266. package/src/runtime/routes/diagnostics-routes.ts +3 -55
  267. package/src/runtime/routes/index.ts +0 -4
  268. package/src/runtime/routes/log-export-routes.ts +0 -2
  269. package/src/runtime/routes/mcp-auth-routes.ts +47 -39
  270. package/src/runtime/routes/migration-routes.ts +21 -8
  271. package/src/runtime/routes/platform-routes.ts +15 -18
  272. package/src/runtime/services/__tests__/conversation-serializer.test.ts +0 -28
  273. package/src/runtime/services/conversation-serializer.ts +3 -16
  274. package/src/runtime/sync/resource-sync-events.ts +1 -2
  275. package/src/schedule/worker.ts +1 -1
  276. package/src/security/ces-rpc-record-backend.ts +0 -7
  277. package/src/subagent/consult-context.ts +25 -12
  278. package/src/tools/acp/list-agents.test.ts +1 -3
  279. package/src/tools/acp/spawn.test.ts +11 -146
  280. package/src/tools/acp/spawn.ts +3 -21
  281. package/src/tools/acp/status.ts +3 -40
  282. package/src/tools/app-control/skill-proxy-bridge.ts +1 -2
  283. package/src/tools/capability-offer.test.ts +2 -22
  284. package/src/tools/capability-offer.ts +1 -16
  285. package/src/tools/computer-use/definitions.ts +2 -4
  286. package/src/tools/computer-use/skill-proxy-bridge.ts +1 -2
  287. package/src/tools/credentials/metadata-store.ts +0 -100
  288. package/src/tools/host-filesystem/edit.test.ts +1 -2
  289. package/src/tools/host-filesystem/edit.ts +2 -2
  290. package/src/tools/host-filesystem/read.test.ts +1 -2
  291. package/src/tools/host-filesystem/read.ts +2 -2
  292. package/src/tools/host-filesystem/transfer.test.ts +1 -2
  293. package/src/tools/host-filesystem/transfer.ts +2 -2
  294. package/src/tools/host-filesystem/write.test.ts +1 -2
  295. package/src/tools/host-filesystem/write.ts +2 -2
  296. package/src/tools/host-terminal/host-shell.test.ts +0 -16
  297. package/src/tools/host-terminal/host-shell.ts +20 -4
  298. package/src/tools/policy-context.ts +7 -6
  299. package/src/tools/subagent/spawn.ts +8 -5
  300. package/src/tools/watch/watch-retro-report.ts +8 -0
  301. package/src/util/__tests__/language-subtag.test.ts +1 -23
  302. package/src/util/fs-watcher-error.ts +36 -0
  303. package/src/util/language-subtag.ts +0 -16
  304. package/src/util/platform.ts +0 -8
  305. package/src/workspace/git-service.ts +54 -5
  306. package/src/workspace/migrations/registry.ts +0 -6
  307. package/scripts/smoke-desktop-window-theme.ts +0 -186
  308. package/src/__tests__/git-porcelain.test.ts +0 -47
  309. package/src/__tests__/helpers/set-workspace-mcp.ts +0 -11
  310. package/src/__tests__/message-deleted-hook-dispatch.test.ts +0 -119
  311. package/src/__tests__/resolve-app.test.ts +0 -135
  312. package/src/__tests__/source-fingerprint.test.ts +0 -184
  313. package/src/__tests__/workspace-migration-154-repair-retired-codex-gpt-5-4-model-ids.test.ts +0 -336
  314. package/src/acp/__tests__/helpers/acp-model-option.ts +0 -51
  315. package/src/acp/model-config.test.ts +0 -166
  316. package/src/acp/model-config.ts +0 -138
  317. package/src/api/events/acp-session-model-update.test.ts +0 -88
  318. package/src/api/events/acp-session-model-update.ts +0 -46
  319. package/src/apps/resolve-app.ts +0 -81
  320. package/src/apps/source-fingerprint.ts +0 -266
  321. package/src/calls/__tests__/barge-in-guard.test.ts +0 -81
  322. package/src/calls/__tests__/voice-leg-coordinator.test.ts +0 -229
  323. package/src/calls/__tests__/voice-progress-cadence.test.ts +0 -274
  324. package/src/calls/barge-in-guard.ts +0 -118
  325. package/src/calls/voice-leg-coordinator.ts +0 -189
  326. package/src/calls/voice-progress-cadence.ts +0 -423
  327. package/src/cli/commands/__tests__/apps.test.ts +0 -245
  328. package/src/cli/commands/__tests__/roadmap.help.test.ts +0 -19
  329. package/src/cli/commands/channels/__tests__/send.test.ts +0 -218
  330. package/src/cli/commands/channels/send.ts +0 -128
  331. package/src/config/__tests__/platform-identity.test.ts +0 -117
  332. package/src/config/acp-agent-write.ts +0 -43
  333. package/src/config/acp-schema.test.ts +0 -81
  334. package/src/config/platform-identity.ts +0 -144
  335. package/src/config/schemas/__tests__/voice.test.ts +0 -148
  336. package/src/config/schemas/voice.ts +0 -150
  337. package/src/daemon/__tests__/skill-memory-refresh-mtime.test.ts +0 -133
  338. package/src/daemon/app-change-notify.ts +0 -45
  339. package/src/desktop/__tests__/fake-desktop.ts +0 -119
  340. package/src/desktop/desktop-chrome-policy.test.ts +0 -71
  341. package/src/desktop/desktop-chrome-policy.ts +0 -42
  342. package/src/desktop/desktop-dependencies.test.ts +0 -102
  343. package/src/desktop/desktop-dependencies.ts +0 -306
  344. package/src/desktop/desktop-feature.test.ts +0 -18
  345. package/src/desktop/desktop-feature.ts +0 -19
  346. package/src/desktop/desktop-panel-config.test.ts +0 -135
  347. package/src/desktop/desktop-panel-config.ts +0 -149
  348. package/src/desktop/desktop-session-manager.test.ts +0 -627
  349. package/src/desktop/desktop-session-manager.ts +0 -814
  350. package/src/desktop/desktop-stream-bridge.test.ts +0 -395
  351. package/src/desktop/desktop-stream-bridge.ts +0 -214
  352. package/src/desktop/desktop-wallpaper.test.ts +0 -191
  353. package/src/desktop/desktop-wallpaper.ts +0 -113
  354. package/src/desktop/desktop-window-theme.ts +0 -170
  355. package/src/i18n/AGENTS.md +0 -22
  356. package/src/i18n/__tests__/i18n.test.ts +0 -108
  357. package/src/i18n/index.ts +0 -16
  358. package/src/i18n/locales.ts +0 -93
  359. package/src/i18n/messages.ts +0 -61
  360. package/src/i18n/title.ts +0 -25
  361. package/src/mcp/__tests__/workspace-mcp-config.test.ts +0 -135
  362. package/src/mcp/spec-schema.ts +0 -111
  363. package/src/mcp/workspace-mcp-config.ts +0 -188
  364. package/src/messaging/providers/__tests__/proactive-address.test.ts +0 -95
  365. package/src/persistence/conversation-title-placeholders.ts +0 -23
  366. package/src/persistence/message-cursor.ts +0 -78
  367. package/src/persistence/migrations/__tests__/157-invite-contact-id.test.ts +0 -149
  368. package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-backfill.test.ts +0 -118
  369. package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-preserve.test.ts +0 -83
  370. package/src/plugins/defaults/memory/hooks/message-deleted.ts +0 -21
  371. package/src/plugins/defaults/memory/memory-retrospective-cursor-backfill.ts +0 -126
  372. package/src/plugins/defaults/memory/memory-retrospective-cursor-preserve.ts +0 -79
  373. package/src/plugins/defaults/memory/memory-retrospective-cursor.ts +0 -28
  374. package/src/plugins/defaults/memory/skill-md-mtime-poll.ts +0 -9
  375. package/src/providers/gemini-thought-signature.test.ts +0 -63
  376. package/src/providers/gemini-thought-signature.ts +0 -44
  377. package/src/providers/openai/__tests__/chat-completions-provider-thought-signature.test.ts +0 -613
  378. package/src/providers/openai/__tests__/google-thought-signature.test.ts +0 -235
  379. package/src/providers/openai/google-thought-signature.ts +0 -320
  380. package/src/runtime/__tests__/channel-send.test.ts +0 -443
  381. package/src/runtime/__tests__/desktop-stream-upgrade.test.ts +0 -89
  382. package/src/runtime/__tests__/live-voice-upgrade.test.ts +0 -58
  383. package/src/runtime/__tests__/runtime-ws-test-utils.ts +0 -72
  384. package/src/runtime/__tests__/watch-stream-upgrade.test.ts +0 -59
  385. package/src/runtime/channel-send.ts +0 -316
  386. package/src/runtime/routes/__tests__/apps-refresh-route.test.ts +0 -205
  387. package/src/runtime/routes/__tests__/channel-send-routes.test.ts +0 -127
  388. package/src/runtime/routes/__tests__/dictation-cleanup-guard.test.ts +0 -107
  389. package/src/runtime/routes/channel-send-routes.ts +0 -85
  390. package/src/runtime/routes/desktop-setup-routes.test.ts +0 -74
  391. package/src/runtime/routes/desktop-setup-routes.ts +0 -35
  392. package/src/security/__tests__/ces-http-record-backend.test.ts +0 -137
  393. package/src/security/ces-http-record-backend.ts +0 -120
  394. package/src/skills/__tests__/workspace-skill-md-signature.test.ts +0 -60
  395. package/src/skills/workspace-skill-md-signature.ts +0 -50
  396. package/src/tools/acp/status.test.ts +0 -94
  397. package/src/util/ca-bundle.ts +0 -13
  398. package/src/workspace/git-porcelain.ts +0 -66
  399. package/src/workspace/migrations/154-repair-retired-codex-gpt-5-4-model-ids.ts +0 -258
  400. package/src/workspace/migrations/155-move-front-model-config-to-voice.ts +0 -78
  401. package/src/workspace/migrations/156-extract-workspace-mcp-json.ts +0 -214
  402. package/src/workspace/migrations/__tests__/155-move-front-model-config-to-voice.test.ts +0 -102
  403. package/src/workspace/migrations/__tests__/156-extract-workspace-mcp-json.test.ts +0 -199
  404. /package/src/{calls → live-voice}/progress-phrases.ts +0 -0
@@ -53,10 +53,7 @@ describe("screen_point_at", () => {
53
53
  );
54
54
 
55
55
  expect(result.isError).toBe(true);
56
- expect(result.content).toContain(
57
- "The Vellum desktop app is needed to view or control your screen",
58
- );
59
- expect(result.content).toContain("https://www.vellum.ai/downloads");
56
+ expect(result.content).toContain("connected desktop client");
60
57
  });
61
58
 
62
59
  /** Drawing on the user's screen is a side effect a stopped turn must not have. */
@@ -3,7 +3,7 @@
3
3
  "tools": [
4
4
  {
5
5
  "name": "scaffold_managed_skill",
6
- "description": "Create or update a managed skill in {workspaceDir}/skills. The skill becomes available for skill_load when a valid top-level SKILL.md is written under the skill directory. Never persist a skill without explicit user consent. Before persisting, test the snippet: write to a temp file with bash and run with `bun run /tmp/vellum-eval/snippet.ts`. Iterate up to 3 attempts, then ask the user. Clean up temp files after. Do not use file_write for temp files outside the working directory.",
6
+ "description": "Create or update a managed skill in {workspaceDir}/skills. The skill becomes available for skill_load when a valid top-level SKILL.md is written under the skill directory. Never persist a skill without explicit user consent. Before persisting, test the snippet: write to a temp file with bash and run with `bun run /tmp/vellum-eval/snippet.ts`. Iterate up to 3 attempts, then ask the user. Clean up temp files after. Do not use file_write for temp files outside the working directory. After a skill is written, the next turn may run in a recreated conversation due to file-watcher eviction - continue normally.",
7
7
  "category": "skills",
8
8
  "risk": "high",
9
9
  "input_schema": {
@@ -92,7 +92,7 @@
92
92
  },
93
93
  {
94
94
  "name": "delete_managed_skill",
95
- "description": "Delete a managed skill directory from {workspaceDir}/skills. Never delete a skill without explicit user confirmation.",
95
+ "description": "Delete a managed skill directory from {workspaceDir}/skills. Never delete a skill without explicit user confirmation. After deletion, the next turn may run in a recreated conversation due to file-watcher eviction - continue normally.",
96
96
  "category": "skills",
97
97
  "risk": "high",
98
98
  "input_schema": {
@@ -137,7 +137,7 @@ const VELLUM_PROFILE_IMPLS: ProfileImpls = {
137
137
  // The managed latency class, also what the live-voice front model runs on.
138
138
  // Its leading tokens are the turn-taking verdict, so what this profile
139
139
  // optimizes is the tail of time-to-first-token rather than the median: a
140
- // verdict slower than `voice.frontModel.endpointDecisionTimeoutMs`
140
+ // verdict slower than `liveVoice.frontModel.endpointDecisionTimeoutMs`
141
141
  // trips the speculative fail-open commit in live-voice-session.ts, which
142
142
  // is audible dead air.
143
143
  //
package/src/config/env.ts CHANGED
@@ -229,8 +229,8 @@ export function setPlatformAssistantId(value: string | undefined): void {
229
229
  * Platform assistant ID — UUID of this assistant on the platform.
230
230
  *
231
231
  * Resolved from the in-memory override (populated by providers-setup
232
- * rehydration via platform validate, or by secret-routes when the
233
- * platform pushes the value).
232
+ * rehydration from the credential store at daemon startup, or by
233
+ * secret-routes when the platform pushes the value).
234
234
  */
235
235
  export function getPlatformAssistantId(): string {
236
236
  return _platformAssistantIdOverride ?? "";
@@ -29,6 +29,18 @@
29
29
  "description": "Enable user-hosted onboarding flow",
30
30
  "defaultEnabled": false
31
31
  },
32
+ {
33
+ "id": "proactive-tips",
34
+ "scope": "client",
35
+ "key": "proactive-tips",
36
+ "label": "Proactive Tips",
37
+ "description": "Gates the dismissible proactive tip card in the web sidebar. String-valued so future A/B arms can be added as new values.",
38
+ "defaultEnabled": "off",
39
+ "values": [
40
+ "off",
41
+ "on"
42
+ ]
43
+ },
32
44
  {
33
45
  "id": "vision-mode",
34
46
  "scope": "client",
@@ -511,14 +523,6 @@
511
523
  "label": "Send User Message Tool",
512
524
  "description": "Route all user-facing assistant text through the send_user_message tool instead of streamed assistant text",
513
525
  "defaultEnabled": false
514
- },
515
- {
516
- "id": "assistant-desktop",
517
- "scope": "assistant",
518
- "key": "assistant-desktop",
519
- "label": "Assistant Desktop",
520
- "description": "Streams an interactive desktop from a containerized assistant pod to the web client over /v1/desktop/stream. Off: no desktop affordance is rendered. On: the chat header offers a desktop panel that views and controls the pod's display.",
521
- "defaultEnabled": false
522
526
  }
523
527
  ]
524
528
  }
@@ -6,7 +6,6 @@ import {
6
6
  isMemoryV1Active,
7
7
  isMemoryV2ExplicitlyDisabled,
8
8
  isMemoryV3Live,
9
- isSkillImprovementActive,
10
9
  isV2InjectionEngineActive,
11
10
  isV3TierActive,
12
11
  usesConceptPageMemory,
@@ -56,16 +55,12 @@ function makeConfig(
56
55
  enabled: TriState,
57
56
  v2Enabled: TriState,
58
57
  v3Live: TriState,
59
- skillImprovement?: boolean,
60
58
  ): AssistantConfig {
61
59
  return {
62
60
  memory: {
63
61
  ...(enabled === undefined ? {} : { enabled }),
64
62
  ...(v2Enabled === undefined ? {} : { v2: { enabled: v2Enabled } }),
65
63
  ...(v3Live === undefined ? {} : { v3: { live: v3Live } }),
66
- ...(skillImprovement === undefined
67
- ? {}
68
- : { retrospective: { skillImprovement } }),
69
64
  },
70
65
  } as AssistantConfig;
71
66
  }
@@ -301,32 +296,6 @@ describe("v1-staleness guard is one condition", () => {
301
296
  * proc-to-skills alias of `isMemoryV3Live` (which did not). One predicate now
302
297
  * answers for both features.
303
298
  */
304
- describe("skill improvement gate", () => {
305
- test("defaults on for a live v3 tier", () => {
306
- expect(isSkillImprovementActive(makeConfig(true, undefined, true))).toBe(
307
- true,
308
- );
309
- expect(isSkillImprovementActive(makeConfig(undefined, undefined, true))).toBe(
310
- true,
311
- );
312
- });
313
-
314
- test("explicit opt-out disables skill improvement without disabling v3", () => {
315
- const config = makeConfig(true, undefined, true, false);
316
-
317
- expect(isV3TierActive(config)).toBe(true);
318
- expect(isSkillImprovementActive(config)).toBe(false);
319
- });
320
-
321
- test("requires the live v3 tier even when explicitly enabled", () => {
322
- const v2Config = makeConfig(true, true, false, true);
323
- const memoryOffConfig = makeConfig(false, undefined, true, true);
324
-
325
- expect(isSkillImprovementActive(v2Config)).toBe(false);
326
- expect(isSkillImprovementActive(memoryOffConfig)).toBe(false);
327
- });
328
- });
329
-
330
299
  describe("v3 tier is one condition", () => {
331
300
  test("memory off suppresses the v3 tier even with memory.v3.live set", () => {
332
301
  const config = makeConfig(false, undefined, true);
@@ -81,32 +81,28 @@ export function isMemoryV3Live(config: AssistantConfig): boolean {
81
81
  * FEATURE asks this predicate; only the injection-suppression checks that must
82
82
  * mirror the raw `memory.v3.live` key ask {@link isMemoryV3Live} directly.
83
83
  *
84
- * The memory concept graph is gated on it: both `GET /memory-graph`
85
- * (`supported`) and the cheap `graph_supported` bit on `GET /memory/stats`
86
- * must agree with whether the graph can build from the v3 concept-page
87
- * substrate.
84
+ * The two features gated on it, and why they must agree:
88
85
  *
89
- * Retrospective skill improvement is a separate optional v3-tier feature.
90
- * Ask {@link isSkillImprovementActive} for it so an operator can stop managed
91
- * skill authoring without turning off ordinary retrospective memory capture.
86
+ * - The memory concept graph — the single source of truth for both
87
+ * `GET /memory-graph` (`supported`) and the cheap `graph_supported` bit on
88
+ * `GET /memory/stats`, so the advertised capability and the actual build can
89
+ * never drift. The graph builds off the v3 concept-page substrate.
90
+ * - Procedural-memory-as-skills — the retrospective's skill-authoring step and
91
+ * its permission grant. Scoped to the v3 tier because skill retrieval rides
92
+ * the v3 lanes and the usage-prune stage lives in the v3 maintain job. That
93
+ * prune ships observe-first: with `memory.maintenance.skillPruneDays` at its
94
+ * default (`null`) it reports stale assistant-authored skills
95
+ * (`prunableSkills`) but deletes none — so a v3-tier assistant authors skills
96
+ * without an automatic retirement bound until a positive `skillPruneDays` is
97
+ * configured.
98
+ *
99
+ * Both write into memory on the user's behalf, so both honor the Memory
100
+ * opt-out identically: one predicate, one answer.
92
101
  */
93
102
  export function isV3TierActive(config: AssistantConfig): boolean {
94
103
  return isMemoryEnabled(config) && isMemoryV3Live(config);
95
104
  }
96
105
 
97
- /**
98
- * Whether a retrospective may turn observed procedures into managed skills.
99
- * This requires the v3 tier because skills are retrieved through its memory
100
- * substrate, and it honors the retrospective-specific opt-out without
101
- * disabling ordinary retrospective fact retention.
102
- */
103
- export function isSkillImprovementActive(config: AssistantConfig): boolean {
104
- return (
105
- isV3TierActive(config) &&
106
- config.memory?.retrospective?.skillImprovement !== false
107
- );
108
- }
109
-
110
106
  /**
111
107
  * Minimal structural view of the memory config the concept-page gate reads.
112
108
  * Accepting the shape (rather than the full `MemoryConfig`) lets call sites
@@ -1,5 +1,6 @@
1
1
  /**
2
- * Rehydrate the in-memory platform identity/URL overrides at process startup.
2
+ * Rehydrate the in-memory platform identity/URL overrides from the credential
3
+ * store at process startup.
3
4
  *
4
5
  * These overrides (`setPlatformBaseUrl` and the platform ID setters in
5
6
  * `config/env.ts`) are normally only populated at runtime by the secret-routes
@@ -9,10 +10,6 @@
9
10
  * environment default (e.g. dev-platform) while the credential store holds the
10
11
  * real production values, and requests are sent to the wrong environment.
11
12
  *
12
- * Bound platform ids come from `POST /v1/internal/assistants/validate/`,
13
- * which trades the assistant API key for assistant/organization/user ids.
14
- * If validate omits an id, that field is read from the credential store.
15
- *
16
13
  * Each field is best-effort: a credential-store read failure is logged and
17
14
  * skipped so a single missing value never blocks startup.
18
15
  */
@@ -20,19 +17,11 @@ import { credentialKey } from "../security/credential-key.js";
20
17
  import { getSecureKeyAsync } from "../security/secure-keys.js";
21
18
  import { getLogger } from "../util/logger.js";
22
19
  import {
23
- getPlatformAssistantId,
24
- getPlatformBaseUrl,
25
- getPlatformOrganizationId,
26
- getPlatformUserId,
27
20
  setPlatformAssistantId,
28
21
  setPlatformBaseUrl,
29
22
  setPlatformOrganizationId,
30
23
  setPlatformUserId,
31
24
  } from "./env.js";
32
- import {
33
- applyPlatformIdentityIds,
34
- fetchPlatformIdentityIds,
35
- } from "./platform-identity.js";
36
25
 
37
26
  const log = getLogger("platform-rehydration");
38
27
 
@@ -56,75 +45,32 @@ async function rehydrateField(
56
45
  }
57
46
  }
58
47
 
59
- async function readAssistantApiKey(): Promise<string> {
60
- try {
61
- const stored = (
62
- await getSecureKeyAsync(credentialKey("vellum", "assistant_api_key"))
63
- )?.trim();
64
- if (stored) {
65
- return stored;
66
- }
67
- } catch (err) {
68
- log.warn(
69
- { error: err instanceof Error ? err.message : String(err) },
70
- "Failed to read assistant API key from credential store (non-fatal)",
71
- );
72
- }
73
- return process.env.ASSISTANT_API_KEY?.trim() ?? "";
74
- }
75
-
76
- async function rehydrateIdentityFromValidate(): Promise<boolean> {
77
- const apiKey = await readAssistantApiKey();
78
- if (!apiKey) {
79
- return false;
80
- }
81
- const baseUrl = getPlatformBaseUrl();
82
- if (!baseUrl) {
83
- return false;
84
- }
85
- const ids = await fetchPlatformIdentityIds(baseUrl, apiKey);
86
- if (!ids) {
87
- return false;
88
- }
89
- applyPlatformIdentityIds(ids);
90
- log.info("Rehydrated platform identity from platform validate");
91
- return true;
92
- }
93
-
94
48
  /**
95
49
  * Rehydrate the platform base URL and the related platform IDs (assistant,
96
- * organization, user). Safe to call more than once.
50
+ * organization, user) from the credential store into their in-memory
51
+ * overrides. Safe to call more than once.
97
52
  */
98
53
  export async function rehydratePlatformCredentials(): Promise<void> {
99
- // Base URL first so managed proxy activation and identity validate resolve
100
- // the correct environment for every request that follows.
54
+ // Base URL first so managed proxy activation resolves the correct
55
+ // environment for the ID lookups and every request that follows.
101
56
  await rehydrateField(
102
57
  "platform_base_url",
103
58
  setPlatformBaseUrl,
104
59
  "platform base URL",
105
60
  );
106
-
107
- await rehydrateIdentityFromValidate();
108
-
109
- if (!getPlatformAssistantId()) {
110
- await rehydrateField(
111
- "platform_assistant_id",
112
- setPlatformAssistantId,
113
- "platform assistant ID",
114
- );
115
- }
116
- if (!getPlatformOrganizationId()) {
117
- await rehydrateField(
118
- "platform_organization_id",
119
- setPlatformOrganizationId,
120
- "platform organization ID",
121
- );
122
- }
123
- if (!getPlatformUserId()) {
124
- await rehydrateField(
125
- "platform_user_id",
126
- setPlatformUserId,
127
- "platform user ID",
128
- );
129
- }
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
+ );
130
76
  }
@@ -41,6 +41,7 @@ import {
41
41
  AuditLogConfigSchema,
42
42
  LogFileConfigSchema,
43
43
  } from "./schemas/logging.js";
44
+ import { McpConfigSchema } from "./schemas/mcp.js";
44
45
  import { MemoryConfigSchema } from "./schemas/memory.js";
45
46
  import { MigrationsConfigSchema } from "./schemas/migrations.js";
46
47
  import { MonitoringConfigSchema } from "./schemas/monitoring.js";
@@ -61,7 +62,6 @@ import {
61
62
  } from "./schemas/timeouts.js";
62
63
  import { ToolsConfigSchema } from "./schemas/tools.js";
63
64
  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,6 +108,7 @@ 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({})),
111
112
  acp: AcpConfigSchema.default(AcpConfigSchema.parse({})),
112
113
  skills: SkillsConfigSchema.default(SkillsConfigSchema.parse({})),
113
114
  workspaceGit: WorkspaceGitConfigSchema.default(
@@ -118,7 +119,6 @@ export const AssistantConfigSchema = z.object({
118
119
  twilio: TwilioConfigSchema.default(TwilioConfigSchema.parse({})),
119
120
  calls: CallsConfigSchema.default(CallsConfigSchema.parse({})),
120
121
  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,9 +3,28 @@ import { describe, expect, test } from "bun:test";
3
3
  import {
4
4
  LiveVoiceConfigSchema,
5
5
  LiveVoiceFluxConfigSchema,
6
+ LiveVoiceFrontModelConfigSchema,
6
7
  LiveVoiceVadConfigSchema,
8
+ VALID_LIVE_VOICE_MODES,
7
9
  } from "../live-voice.js";
8
10
 
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
+
9
28
  // `eagerEotThreshold` is deliberately absent: it has no default, and leaving it
10
29
  // unset is what keeps Deepgram from emitting speculative turn events. `model`
11
30
  // is absent for a related reason: unset means the spoken language selects the
@@ -111,6 +130,117 @@ describe("LiveVoiceVadConfigSchema", () => {
111
130
  });
112
131
  });
113
132
 
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
+
114
244
  describe("LiveVoiceFluxConfigSchema", () => {
115
245
  test("empty object parses to defaults, with turn-end on", () => {
116
246
  expect(LiveVoiceFluxConfigSchema.parse({})).toEqual(FLUX_DEFAULTS);
@@ -196,6 +326,7 @@ describe("LiveVoiceConfigSchema", () => {
196
326
  test("empty object parses to defaults", () => {
197
327
  const parsed = LiveVoiceConfigSchema.parse({});
198
328
  expect(parsed).toEqual({
329
+ mode: "open-mic",
199
330
  vad: {
200
331
  speechEnergyThreshold: 800,
201
332
  noiseFloorMargin: 3,
@@ -206,9 +337,11 @@ describe("LiveVoiceConfigSchema", () => {
206
337
  echoEmaHalfLifeMs: 400,
207
338
  echoDrainSlackMs: 300,
208
339
  },
340
+ frontModel: FRONT_MODEL_DEFAULTS,
209
341
  // Off by default: Flux turn detection is opt-in, so the front-door hold
210
342
  // verdict keeps committing turns until it is enabled.
211
343
  flux: FLUX_DEFAULTS,
344
+ maxSessionDurationSeconds: 1800,
212
345
  // Off by default: voice turns carry only their transcript, no audio
213
346
  // artifacts on the conversation messages (JARVIS-1283).
214
347
  archiveAudio: false,
@@ -232,26 +365,43 @@ describe("LiveVoiceConfigSchema", () => {
232
365
 
233
366
  test("accepts overrides", () => {
234
367
  const parsed = LiveVoiceConfigSchema.parse({
368
+ mode: "ptt",
235
369
  vad: { silenceThresholdMs: 900 },
370
+ frontModel: { endpointDecisionTimeoutMs: 300 },
236
371
  flux: { turnEnd: { enabled: true } },
372
+ maxSessionDurationSeconds: 600,
237
373
  });
374
+ expect(parsed.mode).toBe("ptt");
238
375
  expect(parsed.vad.silenceThresholdMs).toBe(900);
239
376
  // Unspecified vad fields still get defaults
240
377
  expect(parsed.vad.speechEnergyThreshold).toBe(800);
241
378
  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);
242
382
  // Partial flux overrides merge with defaults
243
383
  expect(parsed.flux.turnEnd.enabled).toBe(true);
244
384
  expect(parsed.flux.eotThreshold).toBe(0.7);
385
+ expect(parsed.maxSessionDurationSeconds).toBe(600);
245
386
  });
246
387
 
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,
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
+ });
396
+
397
+ test("rejects non-positive maxSessionDurationSeconds", () => {
398
+ const result = LiveVoiceConfigSchema.safeParse({
399
+ maxSessionDurationSeconds: -1,
253
400
  });
254
- expect(parsed).not.toHaveProperty("mode");
255
- expect(parsed).not.toHaveProperty("maxSessionDurationSeconds");
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"]);
256
406
  });
257
407
  });
@@ -26,8 +26,32 @@ const CallsDisclosureConfigSchema = z
26
26
  "Controls whether and how the assistant discloses its nature at the start of a phone call",
27
27
  );
28
28
 
29
+ const CallsSafetyConfigSchema = z
30
+ .object({
31
+ denyCategories: z
32
+ .array(
33
+ z.string({
34
+ error: "calls.safety.denyCategories values must be strings",
35
+ }),
36
+ )
37
+ .default([])
38
+ .describe(
39
+ "Categories of calls that should be denied (e.g. for safety or compliance reasons)",
40
+ ),
41
+ })
42
+ .describe("Safety guardrails for phone calls");
43
+
29
44
  const CallsVoiceConfigSchema = z
30
45
  .object({
46
+ interruptSensitivity: z
47
+ .enum(["low", "medium", "high"], {
48
+ error:
49
+ "calls.voice.interruptSensitivity must be one of: low, medium, high",
50
+ })
51
+ .default("low")
52
+ .describe(
53
+ "How aggressively the STT provider detects the start of caller speech — low reduces false interrupts from background noise",
54
+ ),
31
55
  telephonyStreaming: z
32
56
  .boolean({
33
57
  error: "calls.voice.telephonyStreaming must be a boolean",
@@ -203,6 +227,7 @@ export const CallsConfigSchema = z
203
227
  disclosure: CallsDisclosureConfigSchema.default(
204
228
  CallsDisclosureConfigSchema.parse({}),
205
229
  ),
230
+ safety: CallsSafetyConfigSchema.default(CallsSafetyConfigSchema.parse({})),
206
231
  voice: CallsVoiceConfigSchema.default(CallsVoiceConfigSchema.parse({})),
207
232
  callerIdentity: CallerIdentityConfigSchema.default(
208
233
  CallerIdentityConfigSchema.parse({}),
@@ -211,4 +236,6 @@ export const CallsConfigSchema = z
211
236
  CallsVerificationConfigSchema.parse({}),
212
237
  ),
213
238
  })
214
- .describe("Phone call configuration: telephony, voice, and call behavior");
239
+ .describe(
240
+ "Phone call configuration — controls telephony, voice, safety, and call behavior",
241
+ );