@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
@@ -1,229 +0,0 @@
1
- import { describe, expect, test } from "bun:test";
2
-
3
- import {
4
- createFrontDoorLegCoordinator,
5
- type EscalatedLeg,
6
- escalatedLegFor,
7
- type FrontDoorLegHost,
8
- type SpokenEscalationBridge,
9
- } from "../voice-leg-coordinator.js";
10
- import {
11
- ESCALATION_CONTINUATION_CONTENT,
12
- FALLBACK_ESCALATION_BRIDGE,
13
- FALLBACK_ESCALATION_BRIDGE_BY_LANGUAGE,
14
- } from "../voice-triage-escalate.js";
15
-
16
- interface Recorded {
17
- events: string[];
18
- answers: string[];
19
- bridges: SpokenEscalationBridge[];
20
- escalated: EscalatedLeg[];
21
- holds: number;
22
- commits: number;
23
- }
24
-
25
- function harness(opts?: {
26
- holdEnabled?: boolean;
27
- live?: () => boolean;
28
- language?: string;
29
- commit?: () => boolean;
30
- withProgress?: boolean;
31
- withHold?: boolean;
32
- }) {
33
- const recorded: Recorded = {
34
- events: [],
35
- answers: [],
36
- bridges: [],
37
- escalated: [],
38
- holds: 0,
39
- commits: 0,
40
- };
41
- const host: FrontDoorLegHost = {
42
- isLive: opts?.live ?? (() => true),
43
- language: () => opts?.language,
44
- ...(opts?.withProgress === false
45
- ? {}
46
- : {
47
- progress: {
48
- clear: () => recorded.events.push("progress.clear"),
49
- noteFloorHolder: () => recorded.events.push("progress.floor"),
50
- arm: () => recorded.events.push("progress.arm"),
51
- },
52
- }),
53
- ...(opts?.withHold
54
- ? {
55
- onHold: () => {
56
- recorded.holds += 1;
57
- recorded.events.push("hold");
58
- },
59
- }
60
- : {}),
61
- ...(opts?.commit
62
- ? {
63
- commit: () => {
64
- recorded.commits += 1;
65
- recorded.events.push("commit");
66
- return opts.commit!();
67
- },
68
- }
69
- : {}),
70
- onAnswerText: (text) => {
71
- recorded.answers.push(text);
72
- recorded.events.push(`answer:${text}`);
73
- },
74
- abortLeg: () => recorded.events.push("abort"),
75
- speakBridge: (bridge) => {
76
- recorded.bridges.push(bridge);
77
- recorded.events.push(`speak:${bridge.spokenBridge}`);
78
- },
79
- startEscalatedLeg: (leg) => {
80
- recorded.escalated.push(leg);
81
- recorded.events.push("start-escalated");
82
- },
83
- };
84
- const coordinator = createFrontDoorLegCoordinator({
85
- holdEnabled: opts?.holdEnabled ?? false,
86
- host,
87
- });
88
- return { coordinator, recorded };
89
- }
90
-
91
- describe("escalatedLegFor", () => {
92
- test("continues on the escalated leg with the spoken bridge quoted", () => {
93
- expect(escalatedLegFor("One moment.")).toEqual({
94
- content: ESCALATION_CONTINUATION_CONTENT,
95
- routingLeg: "escalated",
96
- spokenEscalationBridge: "One moment.",
97
- });
98
- });
99
- });
100
-
101
- describe("createFrontDoorLegCoordinator", () => {
102
- test("an answer releases the held leading text, then streams", () => {
103
- const { coordinator, recorded } = harness();
104
- coordinator.push("[");
105
- expect(recorded.answers).toEqual([]);
106
- coordinator.push("END_CALL] Bye");
107
- coordinator.push(" now.");
108
-
109
- expect(recorded.answers).toEqual(["[END_CALL] Bye", " now."]);
110
- expect(coordinator.complete()).toBe(false);
111
- expect(coordinator.handedOff).toBe(false);
112
- expect(recorded.escalated).toEqual([]);
113
- });
114
-
115
- test("an escalate verdict hands off in order: pause, abort, speak, floor, start, re-arm", () => {
116
- const { coordinator, recorded } = harness();
117
- coordinator.push("[1] Let me check");
118
- expect(coordinator.handedOff).toBe(false);
119
- coordinator.push(" that.");
120
-
121
- expect(coordinator.handedOff).toBe(true);
122
- expect(recorded.events).toEqual([
123
- "progress.clear",
124
- "abort",
125
- "speak:Let me check that.",
126
- "progress.floor",
127
- "start-escalated",
128
- "progress.arm",
129
- ]);
130
- expect(recorded.bridges[0]).toEqual({
131
- spokenBridge: "Let me check that.",
132
- usesFallback: false,
133
- });
134
- expect(recorded.escalated).toEqual([escalatedLegFor("Let me check that.")]);
135
- });
136
-
137
- test("after the hand-off the leg's stream and completion are ignored", () => {
138
- const { coordinator, recorded } = harness();
139
- coordinator.push("[1] One moment.");
140
- coordinator.push(" Ignored past the cap.");
141
- expect(coordinator.complete()).toBe(true);
142
-
143
- expect(recorded.answers).toEqual([]);
144
- expect(recorded.escalated.length).toBe(1);
145
- });
146
-
147
- test("a leg that stops mid-bridge hands off on completion with the canned bridge", () => {
148
- const { coordinator, recorded } = harness({ language: "es" });
149
- coordinator.push("[1]");
150
- expect(coordinator.handedOff).toBe(false);
151
-
152
- expect(coordinator.complete()).toBe(true);
153
- expect(recorded.bridges[0]).toEqual({
154
- spokenBridge: FALLBACK_ESCALATION_BRIDGE_BY_LANGUAGE.es,
155
- usesFallback: true,
156
- });
157
- expect(recorded.escalated[0].spokenEscalationBridge).toBe(
158
- FALLBACK_ESCALATION_BRIDGE_BY_LANGUAGE.es,
159
- );
160
- });
161
-
162
- test("the canned bridge for a language the table lacks is English and says so", () => {
163
- const { coordinator, recorded } = harness({ language: "ko" });
164
- coordinator.push("[1]");
165
- coordinator.complete();
166
-
167
- expect(recorded.bridges[0]).toEqual({
168
- spokenBridge: FALLBACK_ESCALATION_BRIDGE,
169
- usesFallback: true,
170
- language: "en",
171
- });
172
- });
173
-
174
- test("a dead turn never hands off", () => {
175
- let live = true;
176
- const { coordinator, recorded } = harness({ live: () => live });
177
- coordinator.push("[1] One");
178
- live = false;
179
- coordinator.push(" moment.");
180
- expect(coordinator.handedOff).toBe(false);
181
- expect(coordinator.complete()).toBe(false);
182
-
183
- expect(recorded.escalated).toEqual([]);
184
- expect(recorded.events).toEqual([]);
185
- });
186
-
187
- test("a hold verdict on a speculative leg discards the leg", () => {
188
- const { coordinator, recorded } = harness({
189
- holdEnabled: true,
190
- withHold: true,
191
- });
192
- coordinator.push("[0]");
193
-
194
- expect(recorded.holds).toBe(1);
195
- expect(recorded.answers).toEqual([]);
196
- expect(coordinator.handedOff).toBe(false);
197
- });
198
-
199
- test("a leg that does not teach the hold token reads [0] as an answer", () => {
200
- const { coordinator, recorded } = harness({ withHold: true });
201
- coordinator.push("[0] Sure.");
202
-
203
- expect(recorded.holds).toBe(0);
204
- expect(recorded.answers).toEqual(["[0] Sure."]);
205
- });
206
-
207
- test("answer and escalate verdicts commit a speculative turn first; a failed commit stops the verdict", () => {
208
- const committed = harness({ commit: () => true });
209
- committed.coordinator.push("Hello.");
210
- expect(committed.recorded.events).toEqual(["commit", "answer:Hello."]);
211
-
212
- const gone = harness({ commit: () => false });
213
- gone.coordinator.push("[1] One moment.");
214
- expect(gone.recorded.commits).toBe(1);
215
- expect(gone.coordinator.handedOff).toBe(false);
216
- expect(gone.recorded.escalated).toEqual([]);
217
- });
218
-
219
- test("works without a progress cadence", () => {
220
- const { coordinator, recorded } = harness({ withProgress: false });
221
- coordinator.push("[1] One moment.");
222
-
223
- expect(recorded.events).toEqual([
224
- "abort",
225
- "speak:One moment.",
226
- "start-escalated",
227
- ]);
228
- });
229
- });
@@ -1,274 +0,0 @@
1
- import { describe, expect, test } from "bun:test";
2
-
3
- import { VoiceProgressConfigSchema } from "../../config/schemas/voice.js";
4
- import type { VoiceProgressNarrator } from "../progress-narration.js";
5
- import {
6
- PROGRESS_FALLBACK_PHRASES,
7
- PROGRESS_FALLBACK_PHRASES_BY_LANGUAGE,
8
- } from "../progress-phrases.js";
9
- import {
10
- createProgressCadence,
11
- type ProgressCadence,
12
- type ProgressCadenceHost,
13
- } from "../voice-progress-cadence.js";
14
-
15
- const sleep = (ms: number) => new Promise<void>((r) => setTimeout(r, ms));
16
-
17
- async function waitFor(predicate: () => boolean, attempts = 60): Promise<void> {
18
- for (let i = 0; i < attempts; i += 1) {
19
- if (predicate()) {
20
- return;
21
- }
22
- await sleep(5);
23
- }
24
- throw new Error("waitFor timed out");
25
- }
26
-
27
- interface Harness {
28
- cadence: ProgressCadence;
29
- spoken: Array<{ phrase: string; language: string | undefined }>;
30
- host: {
31
- live: boolean;
32
- audioIdle: boolean;
33
- tailUntilMs: number;
34
- language: string | undefined;
35
- deltaEpoch: number;
36
- narrationsSpoken: number;
37
- };
38
- }
39
-
40
- function harness(opts: {
41
- config?: Partial<ReturnType<typeof VoiceProgressConfigSchema.parse>>;
42
- narrator?: VoiceProgressNarrator | null;
43
- language?: string;
44
- }): Harness {
45
- const state = {
46
- live: true,
47
- audioIdle: true,
48
- tailUntilMs: 0,
49
- language: opts.language,
50
- deltaEpoch: 0,
51
- narrationsSpoken: 0,
52
- };
53
- const spoken: Harness["spoken"] = [];
54
- const host: ProgressCadenceHost = {
55
- turnId: "turn-1",
56
- launchedAtMs: Date.now(),
57
- signal: new AbortController().signal,
58
- canNarrate: () => state.live,
59
- isAudioIdle: () => state.audioIdle,
60
- playbackTailUntilMs: () => state.tailUntilMs,
61
- transcriptSoFar: () => "what is on my calendar",
62
- language: () => state.language,
63
- deltaEpoch: () => state.deltaEpoch,
64
- speak: (phrase, language) => {
65
- spoken.push({ phrase, language });
66
- return true;
67
- },
68
- onNarrationSpoken: () => {
69
- state.narrationsSpoken += 1;
70
- },
71
- };
72
- let counter = 0;
73
- const cadence = createProgressCadence({
74
- config: VoiceProgressConfigSchema.parse({
75
- opsThreshold: 3,
76
- idleIntervalMs: 60_000,
77
- maxSilenceMs: 60_000,
78
- longOpMs: 15_000,
79
- minGapMs: 10,
80
- generationTimeoutMs: 1_500,
81
- ...opts.config,
82
- }),
83
- narrator:
84
- opts.narrator === undefined
85
- ? { generateProgressText: async () => "Checking your calendar now." }
86
- : opts.narrator,
87
- host,
88
- nextFallbackPhraseIndex: () => counter++,
89
- });
90
- return { cadence, spoken, host: state };
91
- }
92
-
93
- describe("createProgressCadence", () => {
94
- test("the ops trigger narrates once the threshold of started tools is reached", async () => {
95
- const h = harness({});
96
- h.cadence.toolStarted("web_search", "t1");
97
- h.cadence.toolStarted("web_fetch", "t2");
98
- await sleep(15);
99
- expect(h.spoken).toEqual([]);
100
-
101
- h.cadence.toolStarted("file_read", "t3");
102
- await waitFor(() => h.spoken.length === 1);
103
-
104
- expect(h.spoken[0]).toEqual({
105
- phrase: "Checking your calendar now.",
106
- language: undefined,
107
- });
108
- expect(h.cadence.updatesSpoken).toBe(1);
109
- expect(h.host.narrationsSpoken).toBe(1);
110
- h.cadence.clear();
111
- });
112
-
113
- test("a long operation completing narrates on its own beat", async () => {
114
- const h = harness({ config: { longOpMs: 20 } });
115
- h.cadence.toolStarted("web_search", "t1");
116
- await sleep(30);
117
- h.cadence.toolFinished({
118
- toolName: "web_search",
119
- toolUseId: "t1",
120
- resultPreview: "three results",
121
- });
122
- await waitFor(() => h.spoken.length === 1);
123
-
124
- expect(h.cadence.ops[0].completedAtMs).toBeDefined();
125
- expect(h.cadence.ops[0].resultPreview).toBe("three results");
126
- h.cadence.clear();
127
- });
128
-
129
- test("a quick operation completing is only news, not a narration", async () => {
130
- const h = harness({});
131
- h.cadence.toolStarted("web_search", "t1");
132
- h.cadence.toolFinished({ toolName: "web_search", toolUseId: "t1" });
133
- await sleep(20);
134
-
135
- expect(h.spoken).toEqual([]);
136
- h.cadence.clear();
137
- });
138
-
139
- test("a tool_result with no id matches the newest incomplete op of that name", () => {
140
- const h = harness({});
141
- h.cadence.toolStarted("web_search", "t1");
142
- h.cadence.toolStarted("web_search", "t2");
143
- h.cadence.toolFinished({ toolName: "web_search", isError: true });
144
-
145
- expect(h.cadence.ops[1].completedAtMs).toBeDefined();
146
- expect(h.cadence.ops[1].isError).toBe(true);
147
- expect(h.cadence.ops[0].completedAtMs).toBeUndefined();
148
- h.cadence.clear();
149
- });
150
-
151
- test("the idle tick narrates news since the last update, and the static fallback covers a null generation", async () => {
152
- const h = harness({
153
- config: { idleIntervalMs: 30, maxSilenceMs: 60_000 },
154
- narrator: { generateProgressText: async () => null },
155
- language: "es",
156
- });
157
- h.cadence.arm();
158
- await sleep(60);
159
- // Nothing observable happened: the tick stays quiet.
160
- expect(h.spoken).toEqual([]);
161
-
162
- h.cadence.toolStarted("web_search", "t1");
163
- await waitFor(() => h.spoken.length === 1);
164
-
165
- expect(
166
- PROGRESS_FALLBACK_PHRASES_BY_LANGUAGE.es.map((p) => p.trim()),
167
- ).toContain(h.spoken[0].phrase.trim());
168
- // The Spanish table has an entry: the phrase rides the turn's language.
169
- expect(h.spoken[0].language).toBeUndefined();
170
- h.cadence.clear();
171
- });
172
-
173
- test("a static fallback the table lacks in the turn's language is pinned to English", async () => {
174
- const h = harness({
175
- config: { idleIntervalMs: 30 },
176
- narrator: { generateProgressText: async () => null },
177
- language: "ko",
178
- });
179
- h.cadence.arm();
180
- h.cadence.toolStarted("web_search", "t1");
181
- await waitFor(() => h.spoken.length === 1);
182
-
183
- expect(PROGRESS_FALLBACK_PHRASES).toContain(h.spoken[0].phrase);
184
- expect(h.spoken[0].language).toBe("en");
185
- h.cadence.clear();
186
- });
187
-
188
- test("the heartbeat speaks after maxSilenceMs even with nothing new", async () => {
189
- const h = harness({
190
- config: { idleIntervalMs: 30, maxSilenceMs: 30 },
191
- });
192
- h.cadence.arm();
193
- await waitFor(() => h.spoken.length === 1);
194
- h.cadence.clear();
195
- });
196
-
197
- test("nothing speaks while audio is still playing; the countdown restarts once it drains", async () => {
198
- const h = harness({ config: { idleIntervalMs: 30, maxSilenceMs: 30 } });
199
- h.host.audioIdle = false;
200
- h.cadence.arm();
201
- await sleep(80);
202
- expect(h.spoken).toEqual([]);
203
-
204
- h.host.audioIdle = true;
205
- h.cadence.noteAudioSettled();
206
- await waitFor(() => h.spoken.length === 1);
207
- h.cadence.clear();
208
- });
209
-
210
- test("a narration generated while the model resumed speaking is dropped", async () => {
211
- let release: () => void = () => {};
212
- const h = harness({
213
- config: { opsThreshold: 1 },
214
- narrator: {
215
- generateProgressText: () =>
216
- new Promise<string | null>((resolve) => {
217
- release = () => resolve("Still on it.");
218
- }),
219
- },
220
- });
221
- h.cadence.toolStarted("web_search", "t1");
222
- await sleep(10);
223
- h.host.deltaEpoch += 1;
224
- release();
225
- await sleep(20);
226
-
227
- expect(h.spoken).toEqual([]);
228
- h.cadence.clear();
229
- });
230
-
231
- test("minGapMs spaces narration from another floor holder", async () => {
232
- const h = harness({ config: { opsThreshold: 1, minGapMs: 200 } });
233
- h.cadence.noteFloorHolder();
234
- h.cadence.toolStarted("web_search", "t1");
235
- await sleep(30);
236
-
237
- expect(h.spoken).toEqual([]);
238
- h.cadence.clear();
239
- });
240
-
241
- test("a null narrator or a disabled config never speaks or arms", async () => {
242
- const off = harness({ narrator: null, config: { idleIntervalMs: 20 } });
243
- off.cadence.arm();
244
- off.cadence.toolStarted("a", "1");
245
- off.cadence.toolStarted("b", "2");
246
- off.cadence.toolStarted("c", "3");
247
- const disabled = harness({
248
- config: { enabled: false, idleIntervalMs: 20 },
249
- });
250
- disabled.cadence.arm();
251
- disabled.cadence.toolStarted("a", "1");
252
- disabled.cadence.toolStarted("b", "2");
253
- disabled.cadence.toolStarted("c", "3");
254
- await sleep(60);
255
-
256
- expect(off.spoken).toEqual([]);
257
- expect(disabled.spoken).toEqual([]);
258
- });
259
-
260
- test("clear stops the dead-air timer and a dead turn is never narrated", async () => {
261
- const h = harness({ config: { idleIntervalMs: 20, maxSilenceMs: 20 } });
262
- h.cadence.arm();
263
- h.cadence.clear();
264
- await sleep(60);
265
- expect(h.spoken).toEqual([]);
266
-
267
- h.host.live = false;
268
- h.cadence.toolStarted("a", "1");
269
- h.cadence.toolStarted("b", "2");
270
- h.cadence.toolStarted("c", "3");
271
- await sleep(20);
272
- expect(h.spoken).toEqual([]);
273
- });
274
- });
@@ -1,118 +0,0 @@
1
- /**
2
- * The sustained-speech barge-in guard, shared by the live-voice session and
3
- * the phone media-stream path.
4
- *
5
- * A single above-gate audio chunk is not an interruption: brief TTS bleed
6
- * through imperfect echo cancellation, a cough, a line click. Speech has to
7
- * accumulate to `minSpeechMs` before the assistant is cut off. Short gaps
8
- * inside a run are tolerated (a browser's half-duplex echo canceller ducks
9
- * the near-end voice into intermittent above-gate chunks; a phone line
10
- * drops frames), but a longer continuous silence, or a run that is mostly
11
- * silence, resets it, so sparse periodic blips can never sum into a
12
- * barge-in.
13
- *
14
- * The guard is pure accounting over classified chunks. What arms it, what
15
- * classifies a chunk, and what happens when it fires belong to the driver.
16
- */
17
-
18
- /** Sustained speech required before a barge-in fires. */
19
- export const DEFAULT_BARGE_IN_MIN_SPEECH_MS = 250;
20
-
21
- /**
22
- * Longest continuous sub-threshold gap tolerated inside a run. A gap of
23
- * exactly this length still counts as inside the run: the web client
24
- * batches PCM into 50 ms frames, so a run of ducked frames lands on the
25
- * boundary exactly (four frames = 200 ms).
26
- */
27
- export const BARGE_IN_GAP_TOLERANCE_MS = 200;
28
-
29
- /**
30
- * Ceiling on cumulative sub-threshold time across a whole run, as a
31
- * multiple of `minSpeechMs`. Per-gap tolerance alone lets sparse isolated
32
- * blips (a 10 ms echo spike every 200 ms) each clear the consecutive-gap
33
- * timer while retaining prior speech, so they would sum to the guard over
34
- * several seconds. Capping total tolerated silence imposes a minimum
35
- * above-gate duty cycle (1 / (1 + ratio), about 20%): once the run is mostly
36
- * silence it resets, so genuine choppy speech still lands but periodic noise
37
- * cannot accumulate into one.
38
- */
39
- export const BARGE_IN_MAX_TOLERATED_SILENCE_RATIO = 4;
40
-
41
- /**
42
- * How a chunk reads to the guard. `echo` is the assistant's own playback
43
- * heard back through the microphone, which live voice classifies; a phone
44
- * line carries only the caller's track and never produces it.
45
- */
46
- export type BargeInChunkClassification = "speech" | "silence" | "echo";
47
-
48
- /**
49
- * What one chunk did to the run: `pending` (still accumulating, or a
50
- * tolerated gap), `reset` (echo, or a gap past the limits, dropped the run),
51
- * `fired` (sustained speech reached the threshold; the guard is spent).
52
- */
53
- export type BargeInGuardStep = "pending" | "reset" | "fired";
54
-
55
- export interface BargeInGuard {
56
- /** Speech accumulated in the current run. */
57
- readonly speechMs: number;
58
- /** Feed one classified chunk of `chunkMs` audio. */
59
- track(
60
- classification: BargeInChunkClassification,
61
- chunkMs: number,
62
- ): BargeInGuardStep;
63
- }
64
-
65
- /**
66
- * Build a guard that fires after `minSpeechMs` of sustained speech. A zero
67
- * threshold fires on the first speech chunk (instant barge-in).
68
- */
69
- export function createBargeInGuard(minSpeechMs: number): BargeInGuard {
70
- let speechMs = 0;
71
- // Consecutive sub-threshold time since the last speech chunk.
72
- let silenceMs = 0;
73
- // Cumulative sub-threshold time over the whole run, never reset by speech.
74
- let toleratedSilenceMs = 0;
75
- let fired = false;
76
-
77
- const reset = (): BargeInGuardStep => {
78
- speechMs = 0;
79
- silenceMs = 0;
80
- toleratedSilenceMs = 0;
81
- return "reset";
82
- };
83
-
84
- return {
85
- get speechMs() {
86
- return speechMs;
87
- },
88
- track(classification, chunkMs) {
89
- if (fired) {
90
- return "fired";
91
- }
92
- if (classification === "echo") {
93
- return reset();
94
- }
95
- if (classification === "silence") {
96
- silenceMs += chunkMs;
97
- toleratedSilenceMs += chunkMs;
98
- // Strictly greater on the per-gap check: a gap of exactly the
99
- // tolerance is still inside the run.
100
- if (
101
- silenceMs > BARGE_IN_GAP_TOLERANCE_MS ||
102
- toleratedSilenceMs >
103
- minSpeechMs * BARGE_IN_MAX_TOLERATED_SILENCE_RATIO
104
- ) {
105
- return reset();
106
- }
107
- return "pending";
108
- }
109
- silenceMs = 0;
110
- speechMs += chunkMs;
111
- if (speechMs < minSpeechMs) {
112
- return "pending";
113
- }
114
- fired = true;
115
- return "fired";
116
- },
117
- };
118
- }