@vellumai/assistant 0.12.1 → 0.12.2-staging.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (577) hide show
  1. package/AGENTS.md +4 -0
  2. package/ARCHITECTURE.md +20 -21
  3. package/docs/architecture/turn-actor.md +15 -8
  4. package/docs/guardian-request-flow.md +16 -0
  5. package/node_modules/@vellumai/slack-text/src/index.ts +13 -8
  6. package/node_modules/@vellumai/slack-text/src/label-resolution-entities.test.ts +95 -0
  7. package/openapi.yaml +360 -51
  8. package/package.json +3 -1
  9. package/scripts/smoke-desktop-window-theme.ts +186 -0
  10. package/src/__tests__/always-loaded-tools-guard.test.ts +18 -10
  11. package/src/__tests__/anthropic-provider.test.ts +0 -63
  12. package/src/__tests__/app-compiler.test.ts +6 -0
  13. package/src/__tests__/app-control-tool-schemas.test.ts +5 -2
  14. package/src/__tests__/app-dir-path-guard.test.ts +1 -3
  15. package/src/__tests__/assistant-event-hub-machine-name.test.ts +6 -7
  16. package/src/__tests__/assistant-event-hub.test.ts +44 -0
  17. package/src/__tests__/computer-use-skill-manifest-regression.test.ts +1 -1
  18. package/src/__tests__/computer-use-skill-proxy-bridge.test.ts +5 -2
  19. package/src/__tests__/computer-use-tools.test.ts +19 -7
  20. package/src/__tests__/config-schema-cmd.test.ts +2 -2
  21. package/src/__tests__/config-schema.test.ts +28 -29
  22. package/src/__tests__/config-watcher.test.ts +23 -8
  23. package/src/__tests__/connection-model-compat.test.ts +2 -2
  24. package/src/__tests__/conversation-key-store-disk-view.test.ts +1 -1
  25. package/src/__tests__/conversation-pairing.test.ts +26 -0
  26. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +4 -10
  27. package/src/__tests__/conversation-queue.test.ts +48 -207
  28. package/src/__tests__/conversation-retry-discard.test.ts +54 -1
  29. package/src/__tests__/conversation-runtime-assembly.test.ts +28 -0
  30. package/src/__tests__/conversation-skill-tools.test.ts +27 -0
  31. package/src/__tests__/conversation-surfaces-app-control.test.ts +8 -3
  32. package/src/__tests__/conversation-surfaces-point-at-budget.test.ts +1 -0
  33. package/src/__tests__/conversation-surfaces-point-at-capability.test.ts +1 -0
  34. package/src/__tests__/conversation-surfaces-task-progress.test.ts +67 -0
  35. package/src/__tests__/conversation-title-service.test.ts +14 -10
  36. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +3 -5
  37. package/src/__tests__/conversation-tool-setup-attribution.test.ts +51 -0
  38. package/src/__tests__/credential-record-retire.test.ts +171 -1
  39. package/src/__tests__/credential-routes.test.ts +250 -3
  40. package/src/__tests__/credential-security-invariants.test.ts +5 -3
  41. package/src/__tests__/cu-unified-flow.test.ts +14 -4
  42. package/src/__tests__/document-sync-tags.test.ts +25 -0
  43. package/src/__tests__/drain-kick-guard.test.ts +0 -2
  44. package/src/__tests__/drain-requeue-on-contention.test.ts +0 -6
  45. package/src/__tests__/evict-conversations-for-reload.test.ts +1 -1
  46. package/src/__tests__/git-porcelain.test.ts +47 -0
  47. package/src/__tests__/heartbeat-service.test.ts +56 -40
  48. package/src/__tests__/helpers/set-workspace-mcp.ts +11 -0
  49. package/src/__tests__/host-bash-proxy.test.ts +26 -45
  50. package/src/__tests__/host-browser-proxy.test.ts +6 -17
  51. package/src/__tests__/host-cu-proxy.test.ts +596 -0
  52. package/src/__tests__/host-file-proxy-targeted.test.ts +23 -0
  53. package/src/__tests__/host-file-proxy.test.ts +11 -23
  54. package/src/__tests__/host-transfer-proxy-targeted.test.ts +30 -9
  55. package/src/__tests__/list-messages-camera-frame.test.ts +121 -0
  56. package/src/__tests__/llm-catalog-parity.test.ts +25 -0
  57. package/src/__tests__/mcp-auth-routes.test.ts +6 -9
  58. package/src/__tests__/mcp-config-secret-boundary.test.ts +78 -136
  59. package/src/__tests__/mcp-health-check.test.ts +8 -11
  60. package/src/__tests__/mcp-list-plugin-servers.test.ts +8 -10
  61. package/src/__tests__/message-deleted-hook-dispatch.test.ts +119 -0
  62. package/src/__tests__/messages-after-tiebreaker.test.ts +91 -0
  63. package/src/__tests__/messages-read-boundary-guard.test.ts +1 -1
  64. package/src/__tests__/messaging-send-tool.test.ts +355 -548
  65. package/src/__tests__/model-intents.test.ts +3 -0
  66. package/src/__tests__/notification-vellum-adapter.test.ts +85 -2
  67. package/src/__tests__/oauth-commands-routes.test.ts +168 -0
  68. package/src/__tests__/oauth-provider-profiles.test.ts +18 -0
  69. package/src/__tests__/oauth-provider-serializer.test.ts +22 -0
  70. package/src/__tests__/oauth-providers-routes.test.ts +1 -0
  71. package/src/__tests__/openai-provider.test.ts +1 -0
  72. package/src/__tests__/outlook-messaging-provider.test.ts +6 -6
  73. package/src/__tests__/platform-callback-registration.test.ts +8 -16
  74. package/src/__tests__/platform-client-verify-credential.test.ts +2 -0
  75. package/src/__tests__/plugin-import-boundary-guard.test.ts +14 -0
  76. package/src/__tests__/qdrant-manager.test.ts +132 -0
  77. package/src/__tests__/resolve-app.test.ts +135 -0
  78. package/src/__tests__/scaffold-managed-skill-tool.test.ts +229 -6
  79. package/src/__tests__/script-proxy-certs.test.ts +35 -0
  80. package/src/__tests__/secret-routes-acp-guard.test.ts +59 -1
  81. package/src/__tests__/service-account-token.test.ts +247 -0
  82. package/src/__tests__/skill-feature-flags-integration.test.ts +1 -1
  83. package/src/__tests__/skill-feature-flags.test.ts +2 -2
  84. package/src/__tests__/skills.test.ts +9 -4
  85. package/src/__tests__/source-fingerprint.test.ts +184 -0
  86. package/src/__tests__/subagent-tool-gate-mode.test.ts +70 -3
  87. package/src/__tests__/subagent-tools.test.ts +3 -2
  88. package/src/__tests__/telephony-tts-guard.test.ts +38 -3
  89. package/src/__tests__/test-support/computer-use-skill-harness.ts +3 -2
  90. package/src/__tests__/ui-channel-variants.test.ts +1 -56
  91. package/src/__tests__/unicode.test.ts +13 -0
  92. package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +4 -0
  93. package/src/__tests__/watch-retro-tool-availability.test.ts +8 -17
  94. package/src/__tests__/workspace-git-service.test.ts +26 -0
  95. package/src/__tests__/workspace-migration-154-repair-retired-codex-gpt-5-4-model-ids.test.ts +336 -0
  96. package/src/acp/__tests__/acp-claude-oauth.test.ts +257 -6
  97. package/src/acp/__tests__/acp-credentials.test.ts +13 -0
  98. package/src/acp/__tests__/agent-process.test.ts +258 -4
  99. package/src/acp/__tests__/auth-required.test.ts +96 -0
  100. package/src/acp/__tests__/claude-token-refresh.test.ts +257 -0
  101. package/src/acp/__tests__/client-handler.test.ts +90 -2
  102. package/src/acp/__tests__/helpers/acp-model-option.ts +51 -0
  103. package/src/acp/__tests__/prepare-agent-env.test.ts +60 -1
  104. package/src/acp/__tests__/session-manager-persistence.test.ts +5 -1
  105. package/src/acp/__tests__/session-manager-resume.test.ts +270 -6
  106. package/src/acp/__tests__/session-manager-usage.test.ts +4 -2
  107. package/src/acp/__tests__/session-manager.test.ts +895 -4
  108. package/src/acp/acp-claude-oauth.ts +328 -14
  109. package/src/acp/acp-credentials.ts +19 -0
  110. package/src/acp/agent-process.test.ts +4 -1
  111. package/src/acp/agent-process.ts +96 -10
  112. package/src/acp/auth-required.ts +50 -1
  113. package/src/acp/auto-install.test.ts +9 -7
  114. package/src/acp/claude-token-refresh.ts +150 -0
  115. package/src/acp/client-handler.ts +16 -3
  116. package/src/acp/model-config.test.ts +166 -0
  117. package/src/acp/model-config.ts +138 -0
  118. package/src/acp/prepare-agent-env.ts +21 -6
  119. package/src/acp/resolve-agent.test.ts +220 -6
  120. package/src/acp/resolve-agent.ts +88 -23
  121. package/src/acp/session-manager.test.ts +196 -3
  122. package/src/acp/session-manager.ts +441 -18
  123. package/src/acp/types.ts +50 -5
  124. package/src/agent/loop.ts +1 -0
  125. package/src/api/events/acp-session-model-update.test.ts +88 -0
  126. package/src/api/events/acp-session-model-update.ts +46 -0
  127. package/src/api/events/app-files-changed.ts +4 -3
  128. package/src/api/events/notification-intent.ts +2 -0
  129. package/src/api/events/user-message-echo.ts +7 -1
  130. package/src/api/index.ts +6 -0
  131. package/src/api/responses/conversation-message.ts +5 -0
  132. package/src/apps/app-store.ts +1 -1
  133. package/src/apps/resolve-app.ts +81 -0
  134. package/src/apps/source-fingerprint.ts +266 -0
  135. package/src/avatar/identity-avatar.ts +7 -2
  136. package/src/bundler/app-compiler.ts +15 -0
  137. package/src/calls/__tests__/barge-in-guard.test.ts +81 -0
  138. package/src/{__tests__ → calls/__tests__}/call-controller.test.ts +503 -82
  139. package/src/{__tests__ → calls/__tests__}/media-stream-audio-transcode.test.ts +1 -1
  140. package/src/{__tests__ → calls/__tests__}/media-stream-output.test.ts +37 -8
  141. package/src/{__tests__ → calls/__tests__}/media-stream-parser.test.ts +1 -1
  142. package/src/{__tests__ → calls/__tests__}/media-stream-server-integration.test.ts +150 -57
  143. package/src/{__tests__ → calls/__tests__}/media-stream-stt-session.test.ts +5 -5
  144. package/src/{__tests__ → calls/__tests__}/media-turn-detector.test.ts +1 -1
  145. package/src/{live-voice → calls}/__tests__/progress-narration.test.ts +4 -4
  146. package/src/{live-voice → calls}/__tests__/progress-phrases.test.ts +1 -1
  147. package/src/calls/__tests__/voice-control-protocol.test.ts +120 -25
  148. package/src/calls/__tests__/voice-leg-coordinator.test.ts +229 -0
  149. package/src/calls/__tests__/voice-progress-cadence.test.ts +274 -0
  150. package/src/{__tests__/voice-session-bridge.test.ts → calls/__tests__/voice-session-bridge-turn-flow.test.ts} +20 -12
  151. package/src/calls/__tests__/voice-session-bridge.test.ts +56 -1
  152. package/src/calls/__tests__/voice-triage-escalate.test.ts +105 -0
  153. package/src/calls/barge-in-guard.ts +118 -0
  154. package/src/calls/call-constants.ts +24 -4
  155. package/src/calls/call-controller.ts +499 -199
  156. package/src/calls/call-transport.ts +16 -0
  157. package/src/calls/media-stream-output.ts +32 -0
  158. package/src/calls/media-stream-server.ts +82 -30
  159. package/src/calls/media-stream-stt-session.ts +15 -0
  160. package/src/{live-voice → calls}/progress-narration.ts +2 -2
  161. package/src/calls/resolve-call-tts-provider.ts +33 -2
  162. package/src/calls/tts-call-strategy.ts +9 -5
  163. package/src/calls/types.ts +2 -0
  164. package/src/calls/voice-control-protocol.ts +155 -21
  165. package/src/calls/voice-leg-coordinator.ts +189 -0
  166. package/src/calls/voice-progress-cadence.ts +423 -0
  167. package/src/calls/voice-session-bridge.ts +92 -72
  168. package/src/calls/voice-triage-escalate.ts +178 -59
  169. package/src/cli/commands/__tests__/apps.test.ts +245 -0
  170. package/src/cli/commands/__tests__/bash.test.ts +53 -0
  171. package/src/cli/commands/__tests__/roadmap.help.test.ts +19 -0
  172. package/src/cli/commands/apps.help.ts +67 -13
  173. package/src/cli/commands/apps.ts +183 -0
  174. package/src/cli/commands/bash.help.ts +8 -13
  175. package/src/cli/commands/bash.ts +7 -25
  176. package/src/cli/commands/channels/__tests__/send.test.ts +218 -0
  177. package/src/cli/commands/channels/index.help.ts +70 -2
  178. package/src/cli/commands/channels/index.ts +16 -3
  179. package/src/cli/commands/channels/send.ts +128 -0
  180. package/src/cli/commands/credentials.help.ts +6 -2
  181. package/src/cli/commands/credentials.ts +2 -2
  182. package/src/cli/commands/mcp.help.ts +8 -8
  183. package/src/cli/commands/mcp.ts +1 -1
  184. package/src/cli/commands/oauth/status.ts +5 -0
  185. package/src/cli/commands/roadmap.help.ts +5 -1
  186. package/src/cli/commands/roadmap.ts +1 -1
  187. package/src/cli/lib/bundled-marketplace.json +103 -90
  188. package/src/config/__tests__/default-profile-catalog.test.ts +22 -0
  189. package/src/config/__tests__/memory-retrospective-schema.test.ts +15 -1
  190. package/src/config/__tests__/platform-identity.test.ts +255 -0
  191. package/src/config/__tests__/platform-rehydration.test.ts +99 -9
  192. package/src/config/acp-agent-write.ts +43 -0
  193. package/src/config/acp-defaults.test.ts +13 -3
  194. package/src/config/acp-defaults.ts +23 -6
  195. package/src/config/acp-schema.test.ts +81 -0
  196. package/src/config/acp-schema.ts +30 -2
  197. package/src/config/bundled-skills/acp/SKILL.md +22 -14
  198. package/src/config/bundled-skills/acp/TOOLS.json +5 -1
  199. package/src/config/bundled-skills/app-builder/SKILL.md +12 -9
  200. package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +1 -1
  201. package/src/config/bundled-skills/computer-use/SKILL.md +29 -2
  202. package/src/config/bundled-skills/computer-use/TOOLS.json +99 -0
  203. package/src/config/bundled-skills/computer-use/tools/computer-use-sequence.ts +12 -0
  204. package/src/config/bundled-skills/media-processing/services/preprocess.ts +2 -1
  205. package/src/config/bundled-skills/media-processing/services/processing-pipeline.ts +2 -1
  206. package/src/config/bundled-skills/media-processing/tools/generate-clip.ts +2 -1
  207. package/src/config/bundled-skills/messaging/SKILL.md +3 -3
  208. package/src/config/bundled-skills/messaging/TOOLS.json +3 -3
  209. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +67 -139
  210. package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +1 -1
  211. package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +2 -1
  212. package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +2 -1
  213. package/src/config/bundled-skills/schedule/SKILL.md +3 -4
  214. package/src/config/bundled-skills/screen-annotation/SKILL.md +8 -1
  215. package/src/config/bundled-skills/screen-annotation/tools/screen-annotation.test.ts +4 -1
  216. package/src/config/bundled-skills/skill-management/TOOLS.json +6 -2
  217. package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +5 -2
  218. package/src/config/default-profile-catalog.ts +1 -1
  219. package/src/config/env.ts +2 -2
  220. package/src/config/feature-flag-registry.json +33 -13
  221. package/src/config/loader.ts +2 -0
  222. package/src/config/memory-v3-gate.test.ts +31 -0
  223. package/src/config/memory-v3-gate.ts +20 -16
  224. package/src/config/platform-identity.ts +201 -0
  225. package/src/config/platform-rehydration.ts +11 -27
  226. package/src/config/schema.ts +2 -2
  227. package/src/config/schemas/__tests__/live-voice.test.ts +8 -158
  228. package/src/config/schemas/__tests__/voice.test.ts +148 -0
  229. package/src/config/schemas/calls.ts +1 -28
  230. package/src/config/schemas/live-voice.ts +1 -173
  231. package/src/config/schemas/mcp.ts +1 -1
  232. package/src/config/schemas/memory-retrospective.ts +10 -0
  233. package/src/config/schemas/services.ts +18 -0
  234. package/src/config/schemas/skills.ts +0 -14
  235. package/src/config/schemas/voice.ts +150 -0
  236. package/src/credential-execution/process-manager.test.ts +18 -19
  237. package/src/credential-health/credential-health-service.ts +11 -17
  238. package/src/daemon/__tests__/conversation-tool-setup.test.ts +66 -121
  239. package/src/daemon/__tests__/plugin-mcp-reconcile.test.ts +4 -1
  240. package/src/daemon/__tests__/skill-memory-refresh-mtime.test.ts +133 -0
  241. package/src/daemon/app-change-notify.ts +45 -0
  242. package/src/daemon/config-watcher.ts +13 -194
  243. package/src/daemon/conversation-error.ts +3 -2
  244. package/src/daemon/conversation-media-retry.ts +2 -1
  245. package/src/daemon/conversation-process.ts +35 -64
  246. package/src/daemon/conversation-runtime-assembly.ts +20 -7
  247. package/src/daemon/conversation-skill-tools.ts +13 -31
  248. package/src/daemon/conversation-store.ts +1 -1
  249. package/src/daemon/conversation-surfaces.ts +21 -10
  250. package/src/daemon/conversation-tool-setup.ts +45 -59
  251. package/src/daemon/conversation.ts +0 -3
  252. package/src/daemon/dictation-profile-store.ts +2 -1
  253. package/src/daemon/handlers/shared.ts +2 -1
  254. package/src/daemon/handlers/skills.ts +6 -3
  255. package/src/daemon/host-bash-proxy.ts +8 -12
  256. package/src/daemon/host-browser-proxy.ts +15 -1
  257. package/src/daemon/host-cu-proxy.ts +224 -19
  258. package/src/daemon/host-file-proxy.ts +8 -10
  259. package/src/daemon/host-proxy-preactivation.ts +2 -1
  260. package/src/daemon/host-transfer-proxy.ts +14 -18
  261. package/src/daemon/lifecycle.ts +0 -5
  262. package/src/daemon/mcp-reload-service.ts +5 -5
  263. package/src/daemon/message-types/sync.ts +1 -0
  264. package/src/daemon/orphan-reaper.ts +1 -1
  265. package/src/daemon/providers-setup.ts +1 -1
  266. package/src/daemon/reaction-history-render.ts +2 -1
  267. package/src/daemon/shutdown-handlers.ts +1 -9
  268. package/src/daemon/skill-memory-refresh.ts +64 -0
  269. package/src/daemon/tool-side-effects.ts +7 -18
  270. package/src/desktop/__tests__/fake-desktop.ts +122 -0
  271. package/src/desktop/desktop-chrome-policy.test.ts +79 -0
  272. package/src/desktop/desktop-chrome-policy.ts +47 -0
  273. package/src/desktop/desktop-chrome-session.ts +37 -0
  274. package/src/desktop/desktop-dependencies.test.ts +102 -0
  275. package/src/desktop/desktop-dependencies.ts +308 -0
  276. package/src/desktop/desktop-feature.test.ts +18 -0
  277. package/src/desktop/desktop-feature.ts +19 -0
  278. package/src/desktop/desktop-panel-config.test.ts +135 -0
  279. package/src/desktop/desktop-panel-config.ts +149 -0
  280. package/src/desktop/desktop-session-manager.test.ts +730 -0
  281. package/src/desktop/desktop-session-manager.ts +850 -0
  282. package/src/desktop/desktop-stream-bridge.test.ts +395 -0
  283. package/src/desktop/desktop-stream-bridge.ts +214 -0
  284. package/src/desktop/desktop-wallpaper.test.ts +191 -0
  285. package/src/desktop/desktop-wallpaper.ts +113 -0
  286. package/src/desktop/desktop-window-drag.ts +177 -0
  287. package/src/desktop/desktop-window-manager-config.test.ts +230 -0
  288. package/src/desktop/desktop-window-manager-config.ts +238 -0
  289. package/src/desktop/desktop-window-theme.ts +170 -0
  290. package/src/heartbeat/heartbeat-service.ts +21 -3
  291. package/src/home/__tests__/feed-writer.test.ts +22 -0
  292. package/src/home/feed-writer.ts +11 -2
  293. package/src/hooks/types.ts +33 -0
  294. package/src/i18n/AGENTS.md +22 -0
  295. package/src/i18n/__tests__/i18n.test.ts +108 -0
  296. package/src/i18n/index.ts +16 -0
  297. package/src/i18n/locales.ts +93 -0
  298. package/src/i18n/messages.ts +61 -0
  299. package/src/i18n/title.ts +25 -0
  300. package/src/inbound/platform-callback-registration.ts +4 -5
  301. package/src/live-voice/__tests__/live-voice-agent-turn.test.ts +121 -0
  302. package/src/live-voice/__tests__/live-voice-events.test.ts +8 -7
  303. package/src/live-voice/__tests__/live-voice-metrics.test.ts +0 -3
  304. package/src/live-voice/__tests__/live-voice-progress.test.ts +94 -15
  305. package/src/live-voice/__tests__/live-voice-sight-frame-echo.test.ts +238 -0
  306. package/src/live-voice/__tests__/live-voice-vad.test.ts +2 -2
  307. package/src/live-voice/__tests__/protocol.test.ts +39 -0
  308. package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +13 -69
  309. package/src/live-voice/__tests__/session-controls.test.ts +79 -0
  310. package/src/live-voice/live-voice-photo.ts +25 -6
  311. package/src/live-voice/live-voice-session.ts +297 -699
  312. package/src/live-voice/protocol.ts +60 -2
  313. package/src/live-voice/session-controls.ts +111 -0
  314. package/src/mcp/__tests__/effective-config.test.ts +2 -2
  315. package/src/mcp/__tests__/workspace-mcp-config.test.ts +135 -0
  316. package/src/mcp/effective-config.ts +3 -3
  317. package/src/mcp/mcp-header-store.ts +29 -45
  318. package/src/mcp/spec-schema.ts +111 -0
  319. package/src/mcp/startup.ts +8 -4
  320. package/src/mcp/workspace-mcp-config.ts +188 -0
  321. package/src/messaging/provider.ts +10 -3
  322. package/src/messaging/providers/__tests__/proactive-address.test.ts +95 -0
  323. package/src/messaging/providers/callback-routing.ts +18 -0
  324. package/src/messaging/providers/channel-transport.ts +56 -0
  325. package/src/messaging/providers/discord/transport.ts +17 -0
  326. package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +6 -17
  327. package/src/messaging/providers/slack/adapter.ts +7 -44
  328. package/src/messaging/providers/slack/transport.ts +16 -0
  329. package/src/messaging/providers/telegram-bot/adapter.ts +1 -48
  330. package/src/messaging/providers/telegram-bot/client.ts +0 -26
  331. package/src/messaging/providers/telegram-bot/transport.ts +20 -0
  332. package/src/messaging/providers/whatsapp/adapter.ts +1 -48
  333. package/src/messaging/providers/whatsapp/transport.ts +18 -0
  334. package/src/monitoring/__tests__/workspace-git-heartbeat.test.ts +8 -0
  335. package/src/notifications/__tests__/assistant-reply-producer.test.ts +55 -0
  336. package/src/notifications/__tests__/guardian-feed-projection.test.ts +158 -2
  337. package/src/notifications/__tests__/home-feed-side-effect.test.ts +153 -3
  338. package/src/notifications/__tests__/schedule-result-producer.test.ts +99 -3
  339. package/src/notifications/adapters/macos.ts +3 -0
  340. package/src/notifications/assistant-reply-producer.ts +8 -3
  341. package/src/notifications/conversation-pairing.ts +36 -16
  342. package/src/notifications/delivered-post-record.ts +11 -1
  343. package/src/notifications/guardian-feed-projection.ts +125 -5
  344. package/src/notifications/home-feed-side-effect.ts +55 -7
  345. package/src/notifications/schedule-result-producer.ts +66 -22
  346. package/src/oauth/AGENTS.md +2 -0
  347. package/src/oauth/__tests__/seed-providers-managed.test.ts +149 -0
  348. package/src/oauth/byo-connection.test.ts +105 -0
  349. package/src/oauth/byo-connection.ts +36 -6
  350. package/src/oauth/connection-resolver.test.ts +100 -0
  351. package/src/oauth/connection-resolver.ts +120 -3
  352. package/src/oauth/provider-serializer.ts +9 -0
  353. package/src/oauth/scope-utils.ts +24 -0
  354. package/src/oauth/seed-providers.ts +314 -27
  355. package/src/outbound-proxy/certs.ts +3 -5
  356. package/src/permissions/checker.test.ts +11 -6
  357. package/src/persistence/compaction-log-store-clickhouse.ts +3 -3
  358. package/src/persistence/conversation-crud.ts +71 -63
  359. package/src/persistence/conversation-lineage.ts +12 -4
  360. package/src/persistence/conversation-queries.ts +2 -2
  361. package/src/persistence/conversation-title-placeholders.ts +23 -0
  362. package/src/persistence/conversation-title-service.ts +9 -26
  363. package/src/persistence/embeddings/qdrant-manager.ts +163 -18
  364. package/src/persistence/llm-request-log-sink-clickhouse.ts +3 -3
  365. package/src/persistence/llm-request-log-source-clickhouse.ts +3 -3
  366. package/src/persistence/message-cursor.ts +78 -0
  367. package/src/persistence/message-reads.ts +30 -0
  368. package/src/persistence/migrations/157-invite-contact-id.ts +18 -6
  369. package/src/persistence/migrations/__tests__/157-invite-contact-id.test.ts +149 -0
  370. package/src/persistence/schema/memory-core.ts +5 -0
  371. package/src/platform/client.test.ts +4 -2
  372. package/src/platform/client.ts +2 -10
  373. package/src/plugin-api/constants.ts +2 -0
  374. package/src/plugin-api/index.ts +1 -0
  375. package/src/plugin-api/types.ts +2 -0
  376. package/src/plugins/defaults/index.ts +2 -0
  377. package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +46 -0
  378. package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-backfill.test.ts +118 -0
  379. package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-preserve.test.ts +83 -0
  380. package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +9 -5
  381. package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +20 -10
  382. package/src/plugins/defaults/memory/__tests__/memory-retrospective-prompt.test.ts +5 -0
  383. package/src/plugins/defaults/memory/__tests__/memory-retrospective-state.test.ts +219 -0
  384. package/src/plugins/defaults/memory/__tests__/memory-retrospective-sweep.test.ts +31 -0
  385. package/src/plugins/defaults/memory/__tests__/memory-retrospective-trigger-check.test.ts +63 -0
  386. package/src/plugins/defaults/memory/__tests__/shutdown-hook.test.ts +4 -0
  387. package/src/plugins/defaults/memory/context-search/agent-runner.ts +3 -2
  388. package/src/plugins/defaults/memory/context-search/format.ts +3 -2
  389. package/src/plugins/defaults/memory/context-search/sources/memory-v2.ts +6 -3
  390. package/src/plugins/defaults/memory/context-search/sources/workspace.ts +3 -2
  391. package/src/plugins/defaults/memory/graph/capability-seed.ts +6 -7
  392. package/src/plugins/defaults/memory/hooks/init.ts +10 -0
  393. package/src/plugins/defaults/memory/hooks/message-deleted.ts +21 -0
  394. package/src/plugins/defaults/memory/hooks/shutdown.ts +2 -0
  395. package/src/plugins/defaults/memory/host-utils.ts +10 -0
  396. package/src/plugins/defaults/memory/jobs-worker.ts +13 -0
  397. package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +34 -60
  398. package/src/plugins/defaults/memory/memory-retrospective-cursor-backfill.ts +126 -0
  399. package/src/plugins/defaults/memory/memory-retrospective-cursor-preserve.ts +79 -0
  400. package/src/plugins/defaults/memory/memory-retrospective-cursor.ts +28 -0
  401. package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +3 -5
  402. package/src/plugins/defaults/memory/memory-retrospective-job.ts +12 -3
  403. package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +1 -1
  404. package/src/plugins/defaults/memory/memory-retrospective-state.ts +312 -71
  405. package/src/plugins/defaults/memory/memory-retrospective-sweep.ts +2 -1
  406. package/src/plugins/defaults/memory/memory-retrospective-trigger-check.ts +5 -3
  407. package/src/plugins/defaults/memory/skill-md-mtime-poll.ts +9 -0
  408. package/src/plugins/defaults/memory/startup.ts +28 -2
  409. package/src/plugins/defaults/memory/substrate/__tests__/skill-content.test.ts +7 -9
  410. package/src/plugins/defaults/memory/substrate/page-index.ts +6 -1
  411. package/src/plugins/defaults/memory/substrate/skill-content.ts +2 -2
  412. package/src/plugins/defaults/memory/v1/graph/consolidation.ts +4 -2
  413. package/src/plugins/defaults/memory/v1/graph/extraction.ts +4 -2
  414. package/src/plugins/defaults/memory/v1/graph/retriever.ts +4 -1
  415. package/src/plugins/defaults/memory/v2/reranker.ts +6 -1
  416. package/src/plugins/defaults/memory/v3/__tests__/card.test.ts +24 -0
  417. package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +125 -0
  418. package/src/plugins/defaults/memory/v3/__tests__/sections.test.ts +26 -0
  419. package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +30 -0
  420. package/src/plugins/defaults/memory/v3/card.ts +3 -2
  421. package/src/plugins/defaults/memory/v3/pool-select.ts +5 -3
  422. package/src/plugins/defaults/memory/v3/sections.ts +9 -3
  423. package/src/plugins/defaults/memory/v3/shadow-plugin.ts +5 -2
  424. package/src/plugins/defaults/memory/worker.ts +6 -0
  425. package/src/plugins/mcp-servers.ts +15 -75
  426. package/src/providers/__tests__/connection-model-compat.test.ts +3 -3
  427. package/src/providers/__tests__/retry-callsite.test.ts +41 -0
  428. package/src/providers/__tests__/retry-outbound-sanitize.test.ts +176 -0
  429. package/src/providers/anthropic/client.ts +0 -47
  430. package/src/providers/gemini/client.ts +9 -23
  431. package/src/providers/gemini-thought-signature.test.ts +63 -0
  432. package/src/providers/gemini-thought-signature.ts +44 -0
  433. package/src/providers/inference/__tests__/endpoint-probe.test.ts +42 -3
  434. package/src/providers/inference/auth.ts +2 -3
  435. package/src/providers/inference/connection-availability.ts +27 -8
  436. package/src/providers/inference/endpoint-probe.ts +10 -1
  437. package/src/providers/inference/resolve-auth.ts +22 -4
  438. package/src/providers/inference/service-account-token.ts +248 -0
  439. package/src/providers/model-catalog.ts +34 -0
  440. package/src/providers/model-intents.ts +10 -5
  441. package/src/providers/openai/__tests__/chat-completions-output-token-limit.test.ts +141 -0
  442. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +146 -18
  443. package/src/providers/openai/__tests__/chat-completions-provider-thought-signature.test.ts +613 -0
  444. package/src/providers/openai/__tests__/google-thought-signature.test.ts +235 -0
  445. package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +83 -1
  446. package/src/providers/openai/chat-completions-provider.ts +212 -79
  447. package/src/providers/openai/codex-models.ts +0 -4
  448. package/src/providers/openai/google-thought-signature.ts +320 -0
  449. package/src/providers/opencode/client.test.ts +73 -4
  450. package/src/providers/opencode/client.ts +60 -3
  451. package/src/providers/openrouter/client.ts +20 -0
  452. package/src/providers/outbound-request-sanitize.ts +96 -0
  453. package/src/providers/retry.ts +30 -19
  454. package/src/providers/unparseable-tool-args.ts +5 -1
  455. package/src/runtime/AGENTS.md +3 -1
  456. package/src/runtime/__tests__/channel-send.test.ts +443 -0
  457. package/src/runtime/__tests__/desktop-stream-upgrade.test.ts +89 -0
  458. package/src/runtime/__tests__/host-observe.test.ts +40 -1
  459. package/src/runtime/__tests__/live-voice-upgrade.test.ts +58 -0
  460. package/src/runtime/__tests__/runtime-ws-test-utils.ts +72 -0
  461. package/src/runtime/__tests__/watch-stream-upgrade.test.ts +59 -0
  462. package/src/runtime/agent-wake.ts +2 -1
  463. package/src/runtime/assistant-event-hub.ts +21 -3
  464. package/src/runtime/auth/same-actor.ts +38 -3
  465. package/src/runtime/channel-send.ts +316 -0
  466. package/src/runtime/host-observe.ts +11 -1
  467. package/src/runtime/http-server.ts +160 -151
  468. package/src/runtime/routes/__tests__/acp-claude-auth-routes.test.ts +16 -5
  469. package/src/runtime/routes/__tests__/acp-routes.test.ts +159 -4
  470. package/src/runtime/routes/__tests__/apps-refresh-route.test.ts +275 -0
  471. package/src/runtime/routes/__tests__/channel-send-routes.test.ts +127 -0
  472. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +108 -1
  473. package/src/runtime/routes/__tests__/default-provider-routes.test.ts +2 -2
  474. package/src/runtime/routes/__tests__/dictation-cleanup-guard.test.ts +107 -0
  475. package/src/runtime/routes/__tests__/mcp-add-default-risk.test.ts +16 -12
  476. package/src/runtime/routes/acp-claude-auth-routes.ts +11 -3
  477. package/src/runtime/routes/acp-routes.test.ts +15 -0
  478. package/src/runtime/routes/acp-routes.ts +72 -9
  479. package/src/runtime/routes/app-management-routes.ts +90 -2
  480. package/src/runtime/routes/app-routes.ts +1 -1
  481. package/src/runtime/routes/channel-send-routes.ts +85 -0
  482. package/src/runtime/routes/conversation-list-routes.ts +14 -3
  483. package/src/runtime/routes/conversation-query-routes.ts +40 -0
  484. package/src/runtime/routes/conversation-routes.ts +16 -3
  485. package/src/runtime/routes/credential-routes.ts +20 -14
  486. package/src/runtime/routes/desktop-setup-routes.test.ts +74 -0
  487. package/src/runtime/routes/desktop-setup-routes.ts +35 -0
  488. package/src/runtime/routes/diagnostics-routes.ts +61 -4
  489. package/src/runtime/routes/events-routes.ts +4 -0
  490. package/src/runtime/routes/host-cu-routes.ts +6 -0
  491. package/src/runtime/routes/index.ts +4 -2
  492. package/src/runtime/routes/log-export-routes.ts +2 -0
  493. package/src/runtime/routes/mcp-auth-routes.ts +39 -47
  494. package/src/runtime/routes/migration-routes.ts +6 -20
  495. package/src/runtime/routes/oauth-commands-routes.ts +50 -0
  496. package/src/runtime/routes/oauth-providers.ts +7 -0
  497. package/src/runtime/routes/platform-routes.ts +18 -15
  498. package/src/runtime/routes/workspace-routes.test.ts +55 -26
  499. package/src/runtime/routes/workspace-routes.ts +46 -63
  500. package/src/runtime/routes/workspace-tree-walk.test.ts +244 -0
  501. package/src/runtime/routes/workspace-tree-walk.ts +240 -0
  502. package/src/runtime/routes/workspace-utils.ts +17 -0
  503. package/src/runtime/services/__tests__/conversation-serializer.test.ts +28 -0
  504. package/src/runtime/services/conversation-serializer.ts +16 -3
  505. package/src/runtime/sync/resource-sync-events.ts +2 -1
  506. package/src/schedule/run-script.ts +2 -1
  507. package/src/schedule/schedule-store.ts +9 -2
  508. package/src/schedule/worker.ts +1 -1
  509. package/src/security/__tests__/ces-http-record-backend.test.ts +137 -0
  510. package/src/security/ces-http-record-backend.ts +120 -0
  511. package/src/security/ces-rpc-record-backend.ts +7 -0
  512. package/src/security/untrusted-content.ts +3 -1
  513. package/src/skills/__tests__/workspace-skill-md-signature.test.ts +60 -0
  514. package/src/skills/workspace-skill-md-signature.ts +50 -0
  515. package/src/subagent/consult-context.ts +12 -25
  516. package/src/tools/__tests__/side-effect-abort-guard.test.ts +1 -0
  517. package/src/tools/acp/list-agents.test.ts +3 -1
  518. package/src/tools/acp/spawn.test.ts +146 -11
  519. package/src/tools/acp/spawn.ts +21 -3
  520. package/src/tools/acp/status.test.ts +94 -0
  521. package/src/tools/acp/status.ts +40 -3
  522. package/src/tools/app-control/skill-proxy-bridge.ts +2 -1
  523. package/src/tools/browser/browser-execution.ts +1 -1
  524. package/src/tools/browser/cdp-client/accessibility-snapshot.ts +9 -3
  525. package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +2 -1
  526. package/src/tools/browser/cdp-client/extension-cdp-client.ts +3 -2
  527. package/src/tools/browser/network-recorder.ts +2 -1
  528. package/src/tools/capability-offer.test.ts +22 -2
  529. package/src/tools/capability-offer.ts +16 -1
  530. package/src/tools/computer-use/definitions.ts +121 -2
  531. package/src/tools/computer-use/skill-proxy-bridge.ts +2 -1
  532. package/src/tools/credentials/metadata-store.ts +100 -0
  533. package/src/tools/filesystem/search.ts +2 -1
  534. package/src/tools/host-filesystem/edit.test.ts +2 -1
  535. package/src/tools/host-filesystem/edit.ts +2 -2
  536. package/src/tools/host-filesystem/read.test.ts +2 -1
  537. package/src/tools/host-filesystem/read.ts +2 -2
  538. package/src/tools/host-filesystem/transfer.test.ts +2 -1
  539. package/src/tools/host-filesystem/transfer.ts +2 -2
  540. package/src/tools/host-filesystem/write.test.ts +2 -1
  541. package/src/tools/host-filesystem/write.ts +2 -2
  542. package/src/tools/host-terminal/host-shell.test.ts +16 -0
  543. package/src/tools/host-terminal/host-shell.ts +4 -20
  544. package/src/tools/network/web-search.ts +6 -1
  545. package/src/tools/policy-context.ts +6 -7
  546. package/src/tools/skills/scaffold-managed.ts +61 -3
  547. package/src/tools/subagent/spawn.ts +5 -8
  548. package/src/tools/terminal/__tests__/sanitized-bash.test.ts +57 -0
  549. package/src/tools/terminal/sanitized-bash.ts +105 -0
  550. package/src/tools/ui-surface/channel-variants.ts +10 -59
  551. package/src/tools/watch/watch-retro-report.ts +0 -8
  552. package/src/types/host-capabilities.ts +5 -3
  553. package/src/util/__tests__/language-subtag.test.ts +23 -1
  554. package/src/util/ca-bundle.ts +13 -0
  555. package/src/util/language-subtag.ts +16 -0
  556. package/src/util/platform.ts +8 -0
  557. package/src/util/unicode.ts +15 -1
  558. package/src/watch/watch-retro.ts +6 -8
  559. package/src/watch/watch-timeline.ts +2 -2
  560. package/src/workflows/run-manager.ts +2 -1
  561. package/src/workspace/git-porcelain.ts +66 -0
  562. package/src/workspace/git-service.ts +6 -55
  563. package/src/workspace/migrations/154-repair-retired-codex-gpt-5-4-model-ids.ts +258 -0
  564. package/src/workspace/migrations/155-move-front-model-config-to-voice.ts +78 -0
  565. package/src/workspace/migrations/156-extract-workspace-mcp-json.ts +214 -0
  566. package/src/workspace/migrations/__tests__/155-move-front-model-config-to-voice.test.ts +102 -0
  567. package/src/workspace/migrations/__tests__/156-extract-workspace-mcp-json.test.ts +199 -0
  568. package/src/workspace/migrations/registry.ts +6 -0
  569. package/src/__tests__/app-source-watcher.test.ts +0 -218
  570. package/src/__tests__/config-watcher-skill-reseed.test.ts +0 -359
  571. package/src/__tests__/speaker-identification.test.ts +0 -56
  572. package/src/calls/speaker-identification.ts +0 -233
  573. package/src/daemon/app-source-watcher.ts +0 -218
  574. package/src/messaging/providers/whatsapp/client.ts +0 -29
  575. package/src/runtime/routes/debug-bash-routes.ts +0 -173
  576. package/src/util/fs-watcher-error.ts +0 -36
  577. /package/src/{live-voice → calls}/progress-phrases.ts +0 -0
@@ -4,7 +4,7 @@ import {
4
4
  mulawToPcm16,
5
5
  pcm16ToMulaw,
6
6
  resamplePcm16,
7
- } from "../calls/media-stream-audio-transcode.js";
7
+ } from "../media-stream-audio-transcode.js";
8
8
 
9
9
  // ---------------------------------------------------------------------------
10
10
  // Helpers
@@ -12,7 +12,7 @@ const mockProvider = {
12
12
  synthesize: mockSynthesize,
13
13
  };
14
14
 
15
- mock.module("../calls/resolve-call-tts-provider.js", () => ({
15
+ mock.module("../resolve-call-tts-provider.js", () => ({
16
16
  resolveCallTtsProvider: jest.fn(() => ({
17
17
  provider: mockProvider,
18
18
  useSynthesizedPath: false,
@@ -20,13 +20,10 @@ mock.module("../calls/resolve-call-tts-provider.js", () => ({
20
20
  })),
21
21
  }));
22
22
 
23
- import {
24
- mulawToPcm16,
25
- pcm16ToMulaw,
26
- } from "../calls/media-stream-audio-transcode.js";
27
- import { MediaStreamOutput } from "../calls/media-stream-output.js";
28
- import { resolveCallTtsProvider } from "../calls/resolve-call-tts-provider.js";
29
- import { setConfig } from "./helpers/set-config.js";
23
+ import { setConfig } from "../../__tests__/helpers/set-config.js";
24
+ import { mulawToPcm16, pcm16ToMulaw } from "../media-stream-audio-transcode.js";
25
+ import { MediaStreamOutput } from "../media-stream-output.js";
26
+ import { resolveCallTtsProvider } from "../resolve-call-tts-provider.js";
30
27
 
31
28
  const mockResolveCallTtsProvider = resolveCallTtsProvider as ReturnType<
32
29
  typeof jest.fn
@@ -857,6 +854,38 @@ describe("MediaStreamOutput", () => {
857
854
  // Initially empty
858
855
  expect(output.getPlaybackQueueLength()).toBe(0);
859
856
  });
857
+
858
+ test("isPlaybackIdle tracks buffered text, the queue, and the sent-audio tail", async () => {
859
+ const samples = Array.from({ length: 4000 }, (_, i) =>
860
+ Math.round(Math.sin(i * 0.1) * 10000),
861
+ );
862
+ mockSynthesize.mockResolvedValue({
863
+ audio: makeWavBuffer(samples),
864
+ contentType: "audio/wav",
865
+ });
866
+
867
+ const { ws, sent } = createMockWs();
868
+ const output = makeOutput(ws, "stream-1");
869
+ expect(output.isPlaybackIdle()).toBe(true);
870
+ expect(output.playbackTailUntilMs()).toBe(0);
871
+
872
+ // Text buffered toward a sentence boundary is pending speech.
873
+ output.sendTextToken("Still thinking", false);
874
+ expect(output.isPlaybackIdle()).toBe(false);
875
+
876
+ output.sendTextToken(" about it.", true);
877
+ await drain(() => countEvents(sent, "mark") > 0);
878
+
879
+ // Frames left faster than real time: the tail estimate still covers
880
+ // the audio Twilio is playing out.
881
+ expect(output.playbackTailUntilMs()).toBeGreaterThan(Date.now());
882
+ expect(output.isPlaybackIdle()).toBe(false);
883
+
884
+ // A barge-in clear drops Twilio's buffer, so nothing is audible.
885
+ output.clearAudio();
886
+ expect(output.playbackTailUntilMs()).toBe(0);
887
+ expect(output.isPlaybackIdle()).toBe(true);
888
+ });
860
889
  });
861
890
 
862
891
  // ---------------------------------------------------------------------------
@@ -1,6 +1,6 @@
1
1
  import { describe, expect, test } from "bun:test";
2
2
 
3
- import { parseMediaStreamFrame } from "../calls/media-stream-parser.js";
3
+ import { parseMediaStreamFrame } from "../media-stream-parser.js";
4
4
 
5
5
  // ---------------------------------------------------------------------------
6
6
  // Fixture factories
@@ -15,7 +15,7 @@ import {
15
15
  // Mock the STT resolve module (used by MediaStreamSttSession).
16
16
  // resolveStreamingTranscriber yields no transcriber, so sessions settle on
17
17
  // the batch path regardless of the calls.voice.telephonyStreaming default.
18
- mock.module("../providers/speech-to-text/resolve.js", () => ({
18
+ mock.module("../../providers/speech-to-text/resolve.js", () => ({
19
19
  resolveTelephonySttCapability: jest.fn(),
20
20
  resolveBatchTranscriber: jest.fn(),
21
21
  resolveStreamingTranscriber: jest.fn(async () => null),
@@ -29,7 +29,7 @@ const mockEvents: Array<{
29
29
  data: unknown;
30
30
  }> = [];
31
31
 
32
- mock.module("../calls/call-store.js", () => ({
32
+ mock.module("../call-store.js", () => ({
33
33
  getCallSession: jest.fn((id: string) => mockSessions.get(id) ?? null),
34
34
  updateCallSession: jest.fn((id: string, updates: Record<string, unknown>) => {
35
35
  const session = mockSessions.get(id);
@@ -52,7 +52,7 @@ mock.module("../calls/call-store.js", () => ({
52
52
  }));
53
53
 
54
54
  // Mock the call state machine
55
- mock.module("../calls/call-state-machine.js", () => ({
55
+ mock.module("../call-state-machine.js", () => ({
56
56
  isTerminalState: jest.fn(
57
57
  (status: string) =>
58
58
  status === "completed" || status === "failed" || status === "cancelled",
@@ -61,7 +61,7 @@ mock.module("../calls/call-state-machine.js", () => ({
61
61
 
62
62
  // Mock the call state (controller registry)
63
63
  const mockControllers = new Map<string, unknown>();
64
- mock.module("../calls/call-state.js", () => ({
64
+ mock.module("../call-state.js", () => ({
65
65
  registerCallController: jest.fn(
66
66
  (callSessionId: string, controller: unknown) => {
67
67
  mockControllers.set(callSessionId, controller);
@@ -85,12 +85,12 @@ mock.module("../calls/call-state.js", () => ({
85
85
  }));
86
86
 
87
87
  // Mock the finalize-call module
88
- mock.module("../calls/finalize-call.js", () => ({
88
+ mock.module("../finalize-call.js", () => ({
89
89
  finalizeCall: jest.fn(),
90
90
  }));
91
91
 
92
92
  // Mock the call pointer messages
93
- mock.module("../calls/call-pointer-messages.js", () => ({
93
+ mock.module("../call-pointer-messages.js", () => ({
94
94
  postPointerMessageSafe: jest.fn(),
95
95
  formatDuration: jest.fn((ms: number) => `${Math.round(ms / 1000)}s`),
96
96
  }));
@@ -104,10 +104,13 @@ const mockHandleInterrupt = jest.fn();
104
104
  const mockDestroy = jest.fn();
105
105
 
106
106
  // Mirrors CallController.handleBargeIn: invokes onAccepted only when the
107
- // barge-in passes the speaking gate.
107
+ // barge-in passes the controller's gate.
108
108
  const mockHandleBargeIn = jest.fn((_onAccepted?: () => void) => false);
109
+ // What the mocked controller reports as its state; the media-stream
110
+ // session arms the barge-in guard only while a turn is in flight.
111
+ let mockControllerState: "idle" | "processing" | "speaking" = "idle";
109
112
 
110
- mock.module("../calls/call-controller.js", () => ({
113
+ mock.module("../call-controller.js", () => ({
111
114
  CallController: jest.fn().mockImplementation(() => ({
112
115
  startInitialGreeting: mockStartInitialGreeting,
113
116
  startPostVerificationGreeting: mockStartPostVerificationGreeting,
@@ -116,7 +119,7 @@ mock.module("../calls/call-controller.js", () => ({
116
119
  handleInterrupt: mockHandleInterrupt,
117
120
  handleBargeIn: mockHandleBargeIn,
118
121
  destroy: mockDestroy,
119
- getState: jest.fn(() => "idle"),
122
+ getState: jest.fn(() => mockControllerState),
120
123
  setTrustContext: jest.fn(),
121
124
  getPendingConsultationQuestionId: jest.fn(),
122
125
  handleUserAnswer: jest.fn(),
@@ -125,7 +128,7 @@ mock.module("../calls/call-controller.js", () => ({
125
128
  }));
126
129
 
127
130
  // Mock the assistant scope
128
- mock.module("../runtime/assistant-scope.js", () => ({
131
+ mock.module("../../runtime/assistant-scope.js", () => ({
129
132
  DAEMON_INTERNAL_ASSISTANT_ID: "self",
130
133
  }));
131
134
 
@@ -157,7 +160,7 @@ let mockRouteSetupResult: {
157
160
  // teardown (e.g. the outbound unusable-verdict abort).
158
161
  let mockRouteSetupError: Error | null = null;
159
162
 
160
- mock.module("../calls/call-setup-router.js", () => ({
163
+ mock.module("../call-setup-router.js", () => ({
161
164
  routeSetup: jest.fn(() => {
162
165
  if (mockRouteSetupError) {
163
166
  throw mockRouteSetupError;
@@ -221,7 +224,7 @@ const mockReadPhoneCallerTrust = jest.fn(
221
224
  };
222
225
  },
223
226
  );
224
- mock.module("../calls/inbound-trust-reader.js", () => ({
227
+ mock.module("../inbound-trust-reader.js", () => ({
225
228
  readPhoneCallerTrust: mockReadPhoneCallerTrust,
226
229
  getPhoneCallerVerdict: (otherPartyNumber: string | undefined) =>
227
230
  mockGetInboundTrustVerdict({
@@ -231,7 +234,7 @@ mock.module("../calls/inbound-trust-reader.js", () => ({
231
234
  }));
232
235
 
233
236
  // Mock the actor trust resolver (used by handleStart to derive trust context)
234
- mock.module("../runtime/actor-trust-resolver.js", () => ({
237
+ mock.module("../../runtime/actor-trust-resolver.js", () => ({
235
238
  toTrustContext: jest.fn(() => ({
236
239
  sourceChannel: "phone",
237
240
  trustClass: "guardian",
@@ -243,19 +246,19 @@ mock.module("../runtime/actor-trust-resolver.js", () => ({
243
246
  }));
244
247
 
245
248
  // Mock the call speech output (speakSystemPrompt used in deny/unsupported paths)
246
- mock.module("../calls/call-speech-output.js", () => ({
249
+ mock.module("../call-speech-output.js", () => ({
247
250
  speakSystemPrompt: jest.fn(async () => {}),
248
251
  }));
249
252
 
250
253
  // Mock scoped approval grants (used in handleTransportClosed and early teardown)
251
- mock.module("../approvals/scoped-approval-grants.js", () => ({
254
+ mock.module("../../approvals/scoped-approval-grants.js", () => ({
252
255
  revokeScopedApprovalGrantsForContext: jest.fn(),
253
256
  }));
254
257
 
255
258
  // Mock the TTS provider resolution so that the dynamic import inside
256
259
  // MediaStreamOutput.processSynthesizeItem() doesn't pull in the real
257
260
  // config/provider chain (which would hang or error in a test environment).
258
- mock.module("../calls/resolve-call-tts-provider.js", () => ({
261
+ mock.module("../resolve-call-tts-provider.js", () => ({
259
262
  resolveCallTtsProvider: jest.fn(() => ({
260
263
  provider: null,
261
264
  useSynthesizedPath: false,
@@ -274,7 +277,10 @@ mock.module("../calls/resolve-call-tts-provider.js", () => ({
274
277
  let mockTtsPlaybackDelayMs = 5;
275
278
  let mockAccessRequestPollIntervalMs = 5;
276
279
  let mockUserConsultationTimeoutMs = 500;
277
- mock.module("../calls/call-constants.js", () => ({
280
+ import * as realCallConstants from "../call-constants.js";
281
+
282
+ mock.module("../call-constants.js", () => ({
283
+ ...realCallConstants,
278
284
  isDeniedNumber: jest.fn(() => false),
279
285
  getMaxCallDurationMs: jest.fn(() => 3_600_000),
280
286
  getUserConsultationTimeoutMs: jest.fn(() => mockUserConsultationTimeoutMs),
@@ -298,7 +304,7 @@ const mockGetGuardianDelivery = jest.fn(async () => [
298
304
  { channelType: "phone", status: "active", displayName: "Alex" },
299
305
  ]);
300
306
  const mockGetGuardianDeliveryFresh = jest.fn(async () => []);
301
- mock.module("../contacts/guardian-delivery-reader.js", () => ({
307
+ mock.module("../../contacts/guardian-delivery-reader.js", () => ({
302
308
  getGuardianDelivery: mockGetGuardianDelivery,
303
309
  getGuardianDeliveryFresh: mockGetGuardianDeliveryFresh,
304
310
  peekCachedGuardianDelivery: jest.fn(() => null),
@@ -309,20 +315,20 @@ mock.module("../contacts/guardian-delivery-reader.js", () => ({
309
315
  }));
310
316
 
311
317
  // Guardian/assistant display labels (filesystem-backed).
312
- mock.module("../prompts/user-reference.js", () => ({
318
+ mock.module("../../prompts/user-reference.js", () => ({
313
319
  DEFAULT_USER_REFERENCE: "my human",
314
320
  resolveGuardianName: jest.fn(
315
321
  (primed?: string | null) => primed ?? "my human",
316
322
  ),
317
323
  }));
318
- mock.module("../daemon/identity-helpers.js", () => ({
324
+ mock.module("../../daemon/identity-helpers.js", () => ({
319
325
  getAssistantName: jest.fn(() => "Aria"),
320
326
  resolveUserName: jest.fn(() => null),
321
327
  }));
322
328
 
323
329
  // Conversation persistence (used by callee verification code posting).
324
330
  const mockAddMessage = jest.fn(async () => ({}));
325
- mock.module("../persistence/conversation-crud.js", () => ({
331
+ mock.module("../../persistence/conversation-crud.js", () => ({
326
332
  addMessage: mockAddMessage,
327
333
  }));
328
334
 
@@ -362,7 +368,7 @@ let mockInviteResult: MockInviteResult = {
362
368
  type: "trusted_contact",
363
369
  };
364
370
  const mockAttemptInviteCodeRedemption = jest.fn(async () => mockInviteResult);
365
- mock.module("../calls/call-verification.js", () => ({
371
+ mock.module("../call-verification.js", () => ({
366
372
  attemptVerificationCode: mockAttemptVerificationCode,
367
373
  attemptInviteCodeRedemption: mockAttemptInviteCodeRedemption,
368
374
  parseDigitsFromSpeech: jest.fn((text: string) => text.replace(/\D+/g, "")),
@@ -378,7 +384,7 @@ let mockNotifyResult: MockNotifyResult = {
378
384
  requestId: "req-1",
379
385
  };
380
386
  const mockNotifyGuardianOfAccessRequest = jest.fn(async () => mockNotifyResult);
381
- mock.module("../runtime/access-request-helper.js", () => ({
387
+ mock.module("../../runtime/access-request-helper.js", () => ({
382
388
  notifyGuardianOfAccessRequest: mockNotifyGuardianOfAccessRequest,
383
389
  }));
384
390
 
@@ -389,8 +395,8 @@ const mockGetGuardianRequest = jest.fn(async () => mockGuardianRequest);
389
395
  // factory does not stub keep resolving (a partial factory breaks at
390
396
  // import time when the graph gains a new named import).
391
397
  const actualGatewayGuardianRequests =
392
- await import("../channels/gateway-guardian-requests.js");
393
- mock.module("../channels/gateway-guardian-requests.js", () => ({
398
+ await import("../../channels/gateway-guardian-requests.js");
399
+ mock.module("../../channels/gateway-guardian-requests.js", () => ({
394
400
  ...actualGatewayGuardianRequests,
395
401
  getGuardianRequestOrNull: mockGetGuardianRequest,
396
402
  }));
@@ -402,7 +408,7 @@ const mockEmitCallbackHandoff = jest.fn(
402
408
  notified: false,
403
409
  }),
404
410
  );
405
- mock.module("../calls/access-request-wait.js", () => ({
411
+ mock.module("../access-request-wait.js", () => ({
406
412
  classifyWaitUtterance: jest.fn(() => "neutral"),
407
413
  scheduleNextHeartbeat: jest.fn(() => null),
408
414
  emitAccessRequestCallbackHandoff: mockEmitCallbackHandoff,
@@ -414,21 +420,21 @@ mock.module("../calls/access-request-wait.js", () => ({
414
420
 
415
421
  import { GATEWAY_TUNNEL_LOST_WS_CLOSE_CODE } from "@vellumai/service-contracts/ingress";
416
422
 
417
- import { revokeScopedApprovalGrantsForContext } from "../approvals/scoped-approval-grants.js";
418
- import { CallController } from "../calls/call-controller.js";
419
- import { postPointerMessageSafe } from "../calls/call-pointer-messages.js";
420
- import { routeSetup } from "../calls/call-setup-router.js";
421
- import { speakSystemPrompt } from "../calls/call-speech-output.js";
423
+ import { revokeScopedApprovalGrantsForContext } from "../../approvals/scoped-approval-grants.js";
424
+ import { CallController } from "../call-controller.js";
425
+ import { postPointerMessageSafe } from "../call-pointer-messages.js";
426
+ import { routeSetup } from "../call-setup-router.js";
427
+ import { speakSystemPrompt } from "../call-speech-output.js";
422
428
  import {
423
429
  fireCallTranscriptNotifier,
424
430
  registerCallController,
425
- } from "../calls/call-state.js";
426
- import { recordCallEvent, updateCallSession } from "../calls/call-store.js";
427
- import { finalizeCall } from "../calls/finalize-call.js";
431
+ } from "../call-state.js";
432
+ import { recordCallEvent, updateCallSession } from "../call-store.js";
433
+ import { finalizeCall } from "../finalize-call.js";
428
434
  import {
429
435
  activeMediaStreamSessions,
430
436
  MediaStreamCallSession,
431
- } from "../calls/media-stream-server.js";
437
+ } from "../media-stream-server.js";
432
438
 
433
439
  // ---------------------------------------------------------------------------
434
440
  // Mock WebSocket factory
@@ -557,6 +563,7 @@ beforeEach(() => {
557
563
  mockHandleInterrupt.mockClear();
558
564
  mockHandleBargeIn.mockClear();
559
565
  mockHandleBargeIn.mockReturnValue(false);
566
+ mockControllerState = "idle";
560
567
  mockDestroy.mockClear();
561
568
  (CallController as unknown as jest.Mock).mockClear();
562
569
  (registerCallController as jest.Mock).mockClear();
@@ -1308,28 +1315,23 @@ describe("media-stream setup outcome scenarios", () => {
1308
1315
  await session.whenSetupSettled();
1309
1316
  expect(mockStartInitialGreeting).toHaveBeenCalled();
1310
1317
 
1311
- // Immediate inbound audio (speech-like payloads) before the
1312
- // assistant has spoken. The speech detector classifies these as
1313
- // speech, so onSpeechStart fires and calls handleBargeIn. Since
1314
- // the controller mock returns false (not speaking), handleInterrupt
1315
- // should NOT be called.
1318
+ // Immediate inbound audio (speech-like payloads) before the assistant
1319
+ // has spoken. The controller is idle and nothing is playing, so there
1320
+ // is nothing to interrupt: the guard never arms, however long the
1321
+ // caller talks, and the controller is never consulted.
1316
1322
  const speechPayload = Buffer.alloc(160, 0x00).toString("base64");
1317
- session.handleMessage(makeMediaMessage(speechPayload, "1"));
1318
- session.handleMessage(makeMediaMessage(speechPayload, "2"));
1319
- session.handleMessage(makeMediaMessage(speechPayload, "3"));
1320
-
1321
- // handleBargeIn was called but returned false
1322
- expect(mockHandleBargeIn).toHaveBeenCalled();
1323
+ for (let i = 1; i <= 16; i++) {
1324
+ session.handleMessage(makeMediaMessage(speechPayload, String(i)));
1325
+ }
1326
+ expect(mockHandleBargeIn).not.toHaveBeenCalled();
1323
1327
  expect(mockHandleInterrupt).not.toHaveBeenCalled();
1324
1328
 
1325
- // An ignored barge-in flushes only Twilio's buffered audio (to
1326
- // stop a completed turn's tail talking over the caller). The
1327
- // internal playback queue is untouched, so the queued greeting
1328
- // still plays — handleInterrupt must not have run.
1329
+ // Nothing was audible, so nothing was cleared: the queued greeting
1330
+ // still plays.
1329
1331
  const clearCommands = mockWs.sent.filter(
1330
1332
  (s) => JSON.parse(s).event === "clear",
1331
1333
  );
1332
- expect(clearCommands.length).toBeGreaterThan(0);
1334
+ expect(clearCommands.length).toBe(0);
1333
1335
 
1334
1336
  // voice_session_aborted should NOT appear in recorded events
1335
1337
  const abortEvents = mockEvents.filter(
@@ -1349,6 +1351,7 @@ describe("media-stream setup outcome scenarios", () => {
1349
1351
  onAccepted?.();
1350
1352
  return true;
1351
1353
  });
1354
+ mockControllerState = "speaking";
1352
1355
 
1353
1356
  const mockWs = createMockWs();
1354
1357
  mockSessions.set("call-bargein-2", {
@@ -1364,14 +1367,17 @@ describe("media-stream setup outcome scenarios", () => {
1364
1367
  session.handleMessage(makeStartMessage());
1365
1368
  await session.whenSetupSettled();
1366
1369
 
1367
- // Simulate inbound speech audio while assistant is speaking.
1368
- // Use a high-amplitude mu-law payload so speech detection triggers.
1370
+ // Sustained inbound speech while the assistant is speaking. A
1371
+ // high-amplitude mu-law payload trips speech detection on every
1372
+ // 20 ms frame; the guard fires once 250 ms have accumulated.
1369
1373
  const speechPayload = Buffer.alloc(160, 0x00).toString("base64");
1370
- session.handleMessage(makeMediaMessage(speechPayload, "1"));
1374
+ for (let i = 1; i <= 13; i++) {
1375
+ session.handleMessage(makeMediaMessage(speechPayload, String(i)));
1376
+ }
1371
1377
 
1372
- // handleBargeIn should have been called (returning true), and the
1373
- // accepted barge-in flushes outbound audio via the onAccepted hook.
1374
- expect(mockHandleBargeIn).toHaveBeenCalled();
1378
+ // handleBargeIn was called once (returning true), and the accepted
1379
+ // barge-in flushed outbound audio via the onAccepted hook.
1380
+ expect(mockHandleBargeIn).toHaveBeenCalledTimes(1);
1375
1381
  const clearCommands = mockWs.sent.filter(
1376
1382
  (s) => JSON.parse(s).event === "clear",
1377
1383
  );
@@ -1379,6 +1385,93 @@ describe("media-stream setup outcome scenarios", () => {
1379
1385
 
1380
1386
  session.destroy();
1381
1387
  });
1388
+
1389
+ test("speech from the caller's own utterance does not carry into a turn that starts mid-utterance", async () => {
1390
+ mockHandleBargeIn.mockImplementation((onAccepted?: () => void) => {
1391
+ onAccepted?.();
1392
+ return true;
1393
+ });
1394
+
1395
+ const mockWs = createMockWs();
1396
+ mockSessions.set("call-bargein-4", {
1397
+ id: "call-bargein-4",
1398
+ conversationId: "conv-bargein-4",
1399
+ status: "in_progress",
1400
+ task: null,
1401
+ startedAt: Date.now() - 5000,
1402
+ toNumber: "+15555550198",
1403
+ });
1404
+
1405
+ const session = new MediaStreamCallSession(mockWs.ws, "call-bargein-4");
1406
+ session.handleMessage(makeStartMessage());
1407
+ await session.whenSetupSettled();
1408
+
1409
+ // 240 ms of the caller's own utterance while the controller is idle.
1410
+ const speechPayload = Buffer.alloc(160, 0x00).toString("base64");
1411
+ let chunk = 1;
1412
+ for (let i = 0; i < 12; i++) {
1413
+ session.handleMessage(makeMediaMessage(speechPayload, String(chunk++)));
1414
+ }
1415
+ // A streaming final starts the assistant turn before the local VAD
1416
+ // ends the utterance. One more speech frame must not cut it off.
1417
+ mockControllerState = "processing";
1418
+ session.handleMessage(makeMediaMessage(speechPayload, String(chunk++)));
1419
+ expect(mockHandleBargeIn).not.toHaveBeenCalled();
1420
+
1421
+ // Sustained speech during the turn still does.
1422
+ for (let i = 0; i < 12; i++) {
1423
+ session.handleMessage(makeMediaMessage(speechPayload, String(chunk++)));
1424
+ }
1425
+ expect(mockHandleBargeIn).toHaveBeenCalledTimes(1);
1426
+
1427
+ session.destroy();
1428
+ });
1429
+
1430
+ test("speech that stops short of the guard never reaches the controller", async () => {
1431
+ mockHandleBargeIn.mockImplementation((onAccepted?: () => void) => {
1432
+ onAccepted?.();
1433
+ return true;
1434
+ });
1435
+ mockControllerState = "processing";
1436
+
1437
+ const mockWs = createMockWs();
1438
+ mockSessions.set("call-bargein-3", {
1439
+ id: "call-bargein-3",
1440
+ conversationId: "conv-bargein-3",
1441
+ status: "in_progress",
1442
+ task: null,
1443
+ startedAt: Date.now() - 5000,
1444
+ toNumber: "+15555550199",
1445
+ });
1446
+
1447
+ const session = new MediaStreamCallSession(mockWs.ws, "call-bargein-3");
1448
+ session.handleMessage(makeStartMessage());
1449
+ await session.whenSetupSettled();
1450
+
1451
+ // 100 ms of speech, then a silence gap past the tolerance: the run
1452
+ // resets, and another 100 ms of speech is still short of the guard.
1453
+ const speechPayload = Buffer.alloc(160, 0x00).toString("base64");
1454
+ const silencePayload = Buffer.alloc(160, 0xff).toString("base64");
1455
+ let chunk = 1;
1456
+ for (let i = 0; i < 5; i++) {
1457
+ session.handleMessage(makeMediaMessage(speechPayload, String(chunk++)));
1458
+ }
1459
+ for (let i = 0; i < 15; i++) {
1460
+ session.handleMessage(
1461
+ makeMediaMessage(silencePayload, String(chunk++)),
1462
+ );
1463
+ }
1464
+ for (let i = 0; i < 5; i++) {
1465
+ session.handleMessage(makeMediaMessage(speechPayload, String(chunk++)));
1466
+ }
1467
+
1468
+ expect(mockHandleBargeIn).not.toHaveBeenCalled();
1469
+ expect(
1470
+ mockWs.sent.filter((s) => JSON.parse(s).event === "clear").length,
1471
+ ).toBe(0);
1472
+
1473
+ session.destroy();
1474
+ });
1382
1475
  });
1383
1476
 
1384
1477
  // ── E2E regression scenario ──────────────────────────────────────
@@ -13,27 +13,27 @@ import type {
13
13
  StreamingTranscriber,
14
14
  SttStreamServerEvent,
15
15
  SttTranscribeRequest,
16
- } from "../stt/types.js";
16
+ } from "../../stt/types.js";
17
17
 
18
18
  // ---------------------------------------------------------------------------
19
19
  // Module mocks — must be declared before the module under test is imported.
20
20
  // ---------------------------------------------------------------------------
21
21
 
22
22
  // Mock the STT resolve module
23
- mock.module("../providers/speech-to-text/resolve.js", () => ({
23
+ mock.module("../../providers/speech-to-text/resolve.js", () => ({
24
24
  resolveTelephonySttCapability: jest.fn(),
25
25
  resolveBatchTranscriber: jest.fn(),
26
26
  resolveStreamingTranscriber: jest.fn(),
27
27
  }));
28
28
 
29
29
  // Now import the mocked modules and the module under test.
30
- import { MediaStreamSttSession } from "../calls/media-stream-stt-session.js";
30
+ import { setConfig } from "../../__tests__/helpers/set-config.js";
31
31
  import {
32
32
  resolveBatchTranscriber,
33
33
  resolveStreamingTranscriber,
34
34
  resolveTelephonySttCapability,
35
- } from "../providers/speech-to-text/resolve.js";
36
- import { setConfig } from "./helpers/set-config.js";
35
+ } from "../../providers/speech-to-text/resolve.js";
36
+ import { MediaStreamSttSession } from "../media-stream-stt-session.js";
37
37
 
38
38
  /**
39
39
  * Seed the session's `calls.voice` settings in the real workspace config.
@@ -1,6 +1,6 @@
1
1
  import { afterEach, beforeEach, describe, expect, jest, test } from "bun:test";
2
2
 
3
- import { MediaTurnDetector } from "../calls/media-turn-detector.js";
3
+ import { MediaTurnDetector } from "../media-turn-detector.js";
4
4
 
5
5
  // ---------------------------------------------------------------------------
6
6
  // Helpers
@@ -1,6 +1,6 @@
1
1
  import { describe, expect, test } from "bun:test";
2
2
 
3
- import { LiveVoiceProgressConfigSchema } from "../../config/schemas/live-voice.js";
3
+ import { VoiceProgressConfigSchema } from "../../config/schemas/voice.js";
4
4
  import type { Provider, ProviderResponse } from "../../providers/types.js";
5
5
  import {
6
6
  createVoiceProgressNarrator,
@@ -8,7 +8,7 @@ import {
8
8
  type VoiceProgressTextInput,
9
9
  } from "../progress-narration.js";
10
10
 
11
- const config = LiveVoiceProgressConfigSchema.parse({});
11
+ const config = VoiceProgressConfigSchema.parse({});
12
12
  const progressInput: VoiceProgressTextInput = {
13
13
  transcriptSoFar: "compare flight prices for next month",
14
14
  completedOps: [
@@ -177,7 +177,7 @@ describe("createVoiceProgressNarrator", () => {
177
177
 
178
178
  test("bounds provider resolution by the configured timeout", async () => {
179
179
  const narrator = createVoiceProgressNarrator({
180
- config: LiveVoiceProgressConfigSchema.parse({ generationTimeoutMs: 20 }),
180
+ config: VoiceProgressConfigSchema.parse({ generationTimeoutMs: 20 }),
181
181
  getProvider: () => new Promise<Provider | null>(() => {}),
182
182
  });
183
183
  const startedAt = Date.now();
@@ -187,7 +187,7 @@ describe("createVoiceProgressNarrator", () => {
187
187
 
188
188
  test("a caller abort settles promptly", async () => {
189
189
  const narrator = createVoiceProgressNarrator({
190
- config: LiveVoiceProgressConfigSchema.parse({
190
+ config: VoiceProgressConfigSchema.parse({
191
191
  generationTimeoutMs: 60_000,
192
192
  }),
193
193
  getProvider: async () =>
@@ -8,7 +8,6 @@
8
8
 
9
9
  import { describe, expect, test } from "bun:test";
10
10
 
11
- import { BRIDGE_SENTENCE_END_REGEX } from "../../calls/voice-triage-escalate.js";
12
11
  import { DEEPGRAM_MULTI_LANGUAGE_CODES } from "../../providers/speech-to-text/deepgram.js";
13
12
  import {
14
13
  APPROVAL_PENDING_PHRASE,
@@ -18,6 +17,7 @@ import {
18
17
  PROGRESS_FALLBACK_PHRASES,
19
18
  PROGRESS_FALLBACK_PHRASES_BY_LANGUAGE,
20
19
  } from "../progress-phrases.js";
20
+ import { BRIDGE_SENTENCE_END_REGEX } from "../voice-triage-escalate.js";
21
21
 
22
22
  // Scripts without space-delimited words, where a word budget is
23
23
  // meaningless and length is asserted instead.