@vellumai/assistant 0.10.12 → 0.11.0-staging.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 (776) hide show
  1. package/AGENTS.md +1 -1
  2. package/ARCHITECTURE.md +4 -5
  3. package/README.md +1 -1
  4. package/docs/architecture/memory.md +60 -23
  5. package/docs/error-handling.md +3 -3
  6. package/docs/guardian-request-flow.md +112 -0
  7. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/package.json +1 -0
  8. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/channels.test.ts +9 -1
  9. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/secret-detection.test.ts +449 -0
  10. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/channels.ts +1 -0
  11. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/secret-detection.ts +468 -0
  12. package/node_modules/@vellumai/gateway-client/node_modules/@slack/types/dist/events/app.d.ts +28 -0
  13. package/node_modules/@vellumai/gateway-client/node_modules/@slack/types/dist/events/app.d.ts.map +1 -1
  14. package/node_modules/@vellumai/gateway-client/node_modules/@slack/types/dist/events/index.d.ts +2 -2
  15. package/node_modules/@vellumai/gateway-client/node_modules/@slack/types/dist/events/index.d.ts.map +1 -1
  16. package/node_modules/@vellumai/gateway-client/node_modules/@slack/types/dist/events/index.js.map +1 -1
  17. package/node_modules/@vellumai/gateway-client/node_modules/@slack/types/dist/events/message.d.ts +2 -0
  18. package/node_modules/@vellumai/gateway-client/node_modules/@slack/types/dist/events/message.d.ts.map +1 -1
  19. package/node_modules/@vellumai/gateway-client/node_modules/@slack/types/package.json +1 -1
  20. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json +1 -0
  21. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/channels.test.ts +9 -1
  22. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/secret-detection.test.ts +449 -0
  23. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/channels.ts +1 -0
  24. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/secret-detection.ts +468 -0
  25. package/node_modules/@vellumai/gateway-client/package.json +1 -1
  26. package/node_modules/@vellumai/gateway-client/src/admission-policy-contract.ts +6 -0
  27. package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +34 -0
  28. package/node_modules/@vellumai/service-contracts/package.json +1 -0
  29. package/node_modules/@vellumai/service-contracts/src/__tests__/channels.test.ts +9 -1
  30. package/node_modules/@vellumai/service-contracts/src/__tests__/secret-detection.test.ts +449 -0
  31. package/node_modules/@vellumai/service-contracts/src/channels.ts +1 -0
  32. package/node_modules/@vellumai/service-contracts/src/secret-detection.ts +468 -0
  33. package/openapi.yaml +246 -10
  34. package/package.json +2 -2
  35. package/src/__tests__/acp-session.test.ts +3 -3
  36. package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +3 -0
  37. package/src/__tests__/anthropic-provider.test.ts +40 -0
  38. package/src/__tests__/approval-cascade.test.ts +5 -5
  39. package/src/__tests__/approval-routes-http.test.ts +3 -3
  40. package/src/__tests__/ask-question-resolver.test.ts +207 -0
  41. package/src/__tests__/assistant-event-hub-self-exclusion.test.ts +16 -16
  42. package/src/__tests__/assistant-event-hub-targeted.test.ts +13 -11
  43. package/src/__tests__/assistant-event-hub.test.ts +13 -11
  44. package/src/__tests__/assistant-event.test.ts +12 -9
  45. package/src/__tests__/assistant-feature-flag-guard.test.ts +3 -3
  46. package/src/__tests__/assistant-feature-flag-guardrails.test.ts +1 -1
  47. package/src/__tests__/assistant-id-boundary-guard.test.ts +0 -58
  48. package/src/__tests__/assistant-stream-state.test.ts +17 -7
  49. package/src/__tests__/avatar-identity-sync.test.ts +3 -3
  50. package/src/__tests__/background-workers-disk-pressure.test.ts +1 -0
  51. package/src/__tests__/call-conversation-messages.test.ts +134 -18
  52. package/src/__tests__/call-start-guardian-guard.test.ts +27 -0
  53. package/src/__tests__/channel-approvals.test.ts +0 -27
  54. package/src/__tests__/channel-guardian.test.ts +85 -512
  55. package/src/__tests__/channel-readiness-slack-remote.test.ts +151 -3
  56. package/src/__tests__/channel-retry-sweep.test.ts +88 -1
  57. package/src/__tests__/channel-setup-panel-ack.test.ts +12 -9
  58. package/src/__tests__/chat-reveal-guard-priming.test.ts +31 -34
  59. package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +2 -2
  60. package/src/__tests__/compaction-events.test.ts +8 -8
  61. package/src/__tests__/config-loader-platform-defaults.test.ts +54 -28
  62. package/src/__tests__/config-model-image-provider.test.ts +20 -0
  63. package/src/__tests__/config-schema-memory-substrate.test.ts +306 -0
  64. package/src/__tests__/config-schema.test.ts +14 -2
  65. package/src/__tests__/config-set-platform-guard.test.ts +11 -12
  66. package/src/__tests__/config-sounds-sync.test.ts +4 -4
  67. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +5 -0
  68. package/src/__tests__/context-search-memory-source.test.ts +1 -1
  69. package/src/__tests__/context-search-memory-v2-source.test.ts +6 -6
  70. package/src/__tests__/context-token-estimator.test.ts +54 -1
  71. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +12 -3
  72. package/src/__tests__/conversation-agent-loop-fatal-cleanup.test.ts +13 -4
  73. package/src/__tests__/conversation-agent-loop-handlers-max-tokens.test.ts +4 -4
  74. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +10 -1
  75. package/src/__tests__/conversation-agent-loop-overflow.test.ts +22 -13
  76. package/src/__tests__/conversation-agent-loop.test.ts +39 -30
  77. package/src/__tests__/conversation-confirmation-signals.test.ts +14 -14
  78. package/src/__tests__/conversation-evictor.test.ts +49 -0
  79. package/src/__tests__/conversation-fork-crud.test.ts +1 -1
  80. package/src/__tests__/conversation-history-web-search.test.ts +1 -1
  81. package/src/__tests__/conversation-key-store-disk-view.test.ts +5 -5
  82. package/src/__tests__/conversation-loop-db-lock-resilience.test.ts +5 -5
  83. package/src/__tests__/conversation-message-sync-tags.test.ts +4 -4
  84. package/src/__tests__/conversation-queue.test.ts +81 -81
  85. package/src/__tests__/conversation-routes-enabled-plugins.test.ts +8 -1
  86. package/src/__tests__/conversation-routes-hidden-queue.test.ts +8 -1
  87. package/src/__tests__/conversation-routes-slash-commands.test.ts +10 -1
  88. package/src/__tests__/conversation-runtime-assembly.test.ts +29 -2
  89. package/src/__tests__/conversation-slash-queue.test.ts +11 -11
  90. package/src/__tests__/conversation-slash-unknown.test.ts +2 -2
  91. package/src/__tests__/conversation-speed-override.test.ts +2 -2
  92. package/src/__tests__/conversation-summarize-route.test.ts +9 -0
  93. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +18 -16
  94. package/src/__tests__/conversation-surfaces-activation-emit.test.ts +17 -17
  95. package/src/__tests__/conversation-surfaces-app-open.test.ts +7 -4
  96. package/src/__tests__/conversation-surfaces-data-persist.test.ts +7 -7
  97. package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +9 -9
  98. package/src/__tests__/conversation-surfaces-standalone.test.ts +12 -12
  99. package/src/__tests__/conversation-surfaces-state-update.test.ts +8 -5
  100. package/src/__tests__/conversation-surfaces-table-action.test.ts +6 -3
  101. package/src/__tests__/conversation-surfaces-task-progress.test.ts +46 -46
  102. package/src/__tests__/conversation-sync-tags.test.ts +5 -5
  103. package/src/__tests__/conversation-wait-for-idle.test.ts +2 -2
  104. package/src/__tests__/conversation-workspace-cache-state.test.ts +0 -4
  105. package/src/__tests__/conversation-workspace-injection.test.ts +0 -3
  106. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +0 -3
  107. package/src/__tests__/copy-composer-tc-templates.test.ts +3 -131
  108. package/src/__tests__/credential-security-invariants.test.ts +1 -0
  109. package/src/__tests__/daemon-assistant-events.test.ts +14 -15
  110. package/src/__tests__/db-test-helpers.ts +13 -27
  111. package/src/__tests__/disk-pressure-routes.test.ts +12 -3
  112. package/src/__tests__/document-tool-security.test.ts +95 -0
  113. package/src/__tests__/drain-kick-guard.test.ts +232 -0
  114. package/src/__tests__/dynamic-skill-background-guard.test.ts +19 -6
  115. package/src/__tests__/emit-event-signal.test.ts +5 -6
  116. package/src/__tests__/encrypted-store-test-helpers.ts +10 -20
  117. package/src/__tests__/events-tail-route.test.ts +6 -4
  118. package/src/__tests__/feature-flag-test-helpers.ts +10 -20
  119. package/src/__tests__/file-edit-tool.test.ts +24 -4
  120. package/src/__tests__/file-list-tool.test.ts +2 -1
  121. package/src/__tests__/file-read-tool.test.ts +62 -10
  122. package/src/__tests__/file-write-tool.test.ts +24 -5
  123. package/src/__tests__/followup-tools.test.ts +58 -0
  124. package/src/__tests__/gateway-threshold-reader-mock.ts +99 -0
  125. package/src/__tests__/graph-extraction-event-date.test.ts +1 -1
  126. package/src/__tests__/guardian-question-mode.test.ts +42 -3
  127. package/src/__tests__/guardian-reply-router-answer-mode.test.ts +283 -0
  128. package/src/__tests__/guardian-vellum-destination-pinning.test.ts +221 -0
  129. package/src/__tests__/heartbeat-disk-pressure.test.ts +1 -0
  130. package/src/__tests__/heartbeat-service.test.ts +1 -0
  131. package/src/__tests__/helpers/native-web-search-harness.ts +10 -10
  132. package/src/__tests__/host-browser-proxy.test.ts +1 -1
  133. package/src/__tests__/host-file-edit-tool.test.ts +3 -3
  134. package/src/__tests__/host-file-read-tool.test.ts +2 -2
  135. package/src/__tests__/host-file-write-tool.test.ts +7 -3
  136. package/src/__tests__/host-shell-tool.test.ts +17 -5
  137. package/src/__tests__/host-transfer-pending-interactions.test.ts +8 -8
  138. package/src/__tests__/image-credentials.test.ts +65 -8
  139. package/src/__tests__/injection-block.test.ts +4 -4
  140. package/src/__tests__/injector-chain.test.ts +1 -1
  141. package/src/__tests__/injector-pkb-v2-silenced.test.ts +2 -2
  142. package/src/__tests__/injector-registry-order-guard.test.ts +4 -9
  143. package/src/__tests__/injector-v3-suppression.test.ts +6 -4
  144. package/src/__tests__/lifecycle-memory-v2-seed.test.ts +27 -28
  145. package/src/__tests__/list-messages-page-latest.test.ts +2 -2
  146. package/src/__tests__/llm-usage-store.test.ts +67 -4
  147. package/src/__tests__/log-redact.test.ts +18 -0
  148. package/src/__tests__/managed-profile-guard.test.ts +27 -0
  149. package/src/__tests__/managed-skill-lifecycle.test.ts +2 -2
  150. package/src/__tests__/media-generate-image.test.ts +62 -11
  151. package/src/__tests__/memory-identity-context-parity.test.ts +1 -1
  152. package/src/__tests__/memory-jobs-worker-lanes.test.ts +9 -1
  153. package/src/__tests__/memory-retrieval-hook.test.ts +4 -4
  154. package/src/__tests__/notification-decision-strategy.test.ts +115 -11
  155. package/src/__tests__/notification-deep-link.test.ts +12 -12
  156. package/src/__tests__/notification-vellum-adapter.test.ts +8 -8
  157. package/src/__tests__/outbound-slack-persistence.test.ts +8 -8
  158. package/src/__tests__/path-policy.test.ts +315 -0
  159. package/src/__tests__/pending-interactions-resolved-event.test.ts +6 -6
  160. package/src/__tests__/persistence-layering-guard.test.ts +2 -2
  161. package/src/__tests__/pkb-autoinject.test.ts +1 -1
  162. package/src/__tests__/platform-bash-auto-approve.test.ts +1 -0
  163. package/src/__tests__/plugin-event-hub-facade.test.ts +22 -15
  164. package/src/__tests__/plugin-import-boundary-guard.test.ts +15 -15
  165. package/src/__tests__/processing-flag-persist-failure.test.ts +0 -4
  166. package/src/__tests__/provider-platform-proxy-integration.test.ts +0 -1
  167. package/src/__tests__/rebuild-index-graph-nodes.test.ts +1 -1
  168. package/src/__tests__/request-secret-standalone-channel-gate.test.ts +5 -5
  169. package/src/__tests__/require-fresh-approval.test.ts +38 -38
  170. package/src/__tests__/run-due-schedules.test.ts +72 -1
  171. package/src/__tests__/runtime-events-sse-parity.test.ts +11 -10
  172. package/src/__tests__/runtime-events-sse.test.ts +1 -1
  173. package/src/__tests__/schedule-routes.test.ts +44 -5
  174. package/src/__tests__/schedule-store.test.ts +3 -3
  175. package/src/__tests__/schedule-tools.test.ts +176 -0
  176. package/src/__tests__/secret-prompt-log-hygiene.test.ts +3 -3
  177. package/src/__tests__/secret-prompter-channel-fallback.test.ts +3 -3
  178. package/src/__tests__/secret-response-routing.test.ts +3 -3
  179. package/src/__tests__/secret-routes-platform-proxy.test.ts +4 -7
  180. package/src/__tests__/secret-scanner.test.ts +36 -0
  181. package/src/__tests__/send-endpoint-busy.test.ts +7 -7
  182. package/src/__tests__/slack-required-scopes-mirror.test.ts +81 -0
  183. package/src/__tests__/starter-task-flow.test.ts +6 -3
  184. package/src/__tests__/subagent-call-site-routing.test.ts +2 -2
  185. package/src/__tests__/subagent-disposal.test.ts +40 -11
  186. package/src/__tests__/subagent-fork-notifications.test.ts +4 -4
  187. package/src/__tests__/subagent-fork-prompt-role.test.ts +3 -3
  188. package/src/__tests__/subagent-fork-spawn.test.ts +4 -4
  189. package/src/__tests__/subagent-manager-notify.test.ts +73 -14
  190. package/src/__tests__/subagent-notify-parent.test.ts +48 -4
  191. package/src/__tests__/subagent-spawn-and-await.test.ts +62 -14
  192. package/src/__tests__/subagent-tools.test.ts +64 -0
  193. package/src/__tests__/sync-message-contract.test.ts +3 -3
  194. package/src/__tests__/terminal-tools.test.ts +12 -3
  195. package/src/__tests__/test-preload.ts +9 -0
  196. package/src/__tests__/tool-approval-handler.test.ts +583 -43
  197. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +7 -4
  198. package/src/__tests__/tool-executor.test.ts +116 -0
  199. package/src/__tests__/tool-grant-request-escalation.test.ts +5 -0
  200. package/src/__tests__/tool-preview-lifecycle.test.ts +32 -29
  201. package/src/__tests__/tool-result-metadata-plumbing.test.ts +5 -5
  202. package/src/__tests__/tool-start-timestamp.test.ts +4 -4
  203. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +9 -22
  204. package/src/__tests__/ui-choice-copy-surfaces.test.ts +13 -13
  205. package/src/__tests__/ui-work-result-surface.test.ts +3 -3
  206. package/src/__tests__/voice-scoped-grant-consumer.test.ts +6 -6
  207. package/src/__tests__/voice-session-bridge.test.ts +44 -44
  208. package/src/__tests__/workspace-migration-134-image-generation-mode-to-provider.test.ts +284 -0
  209. package/src/__tests__/workspace-policy.test.ts +399 -0
  210. package/src/acp/__tests__/client-handler.test.ts +3 -3
  211. package/src/acp/__tests__/session-manager-persistence.test.ts +10 -10
  212. package/src/acp/__tests__/session-manager-resume.test.ts +7 -7
  213. package/src/acp/client-handler.ts +3 -3
  214. package/src/acp/session-manager.ts +8 -8
  215. package/src/api/events/subagent-event.ts +2 -2
  216. package/src/api/responses/memory-v2-activation-log.ts +2 -2
  217. package/src/approvals/AGENTS.md +2 -0
  218. package/src/approvals/guardian-request-resolvers.ts +118 -24
  219. package/src/calls/__tests__/voice-control-protocol.test.ts +57 -0
  220. package/src/calls/__tests__/voice-session-bridge.test.ts +158 -4
  221. package/src/calls/__tests__/voice-triage-escalate.test.ts +3 -4
  222. package/src/calls/call-controller.ts +27 -27
  223. package/src/calls/call-conversation-messages.ts +58 -18
  224. package/src/calls/voice-control-protocol.ts +56 -0
  225. package/src/calls/voice-session-bridge.ts +156 -58
  226. package/src/calls/voice-triage-escalate.ts +2 -3
  227. package/src/channels/config.ts +8 -0
  228. package/src/channels/types.ts +1 -0
  229. package/src/cli/commands/config.ts +49 -13
  230. package/src/cli/commands/memory/index.help.ts +4 -2
  231. package/src/cli/commands/memory/memory-retrospective.ts +5 -0
  232. package/src/cli/lib/bundled-marketplace.json +12 -0
  233. package/src/cli.ts +2 -109
  234. package/src/config/__tests__/deployment-context-defaults.test.ts +9 -0
  235. package/src/config/__tests__/substrate-twin-shadowing.test.ts +191 -0
  236. package/src/config/assistant-feature-flags.ts +1 -1
  237. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +13 -7
  238. package/src/config/feature-flag-cache.ts +6 -16
  239. package/src/config/feature-flag-registry.json +14 -30
  240. package/src/config/loader.ts +7 -1
  241. package/src/config/memory-tier.ts +15 -10
  242. package/src/config/memory-v3-gate.test.ts +278 -1
  243. package/src/config/memory-v3-gate.ts +88 -26
  244. package/src/config/raw-config-utils.ts +1 -1
  245. package/src/config/schemas/__tests__/live-voice.test.ts +27 -16
  246. package/src/config/schemas/__tests__/memory-v3.test.ts +1 -0
  247. package/src/config/schemas/live-voice.ts +33 -7
  248. package/src/config/schemas/llm.ts +1 -0
  249. package/src/config/schemas/memory-retrospective.ts +13 -0
  250. package/src/config/schemas/memory-substrate.ts +183 -0
  251. package/src/config/schemas/memory-v2.ts +3 -2
  252. package/src/config/schemas/memory-v3.ts +11 -4
  253. package/src/config/schemas/memory.ts +35 -1
  254. package/src/config/schemas/services.ts +10 -2
  255. package/src/config/substrate-twin-shadowing.ts +195 -0
  256. package/src/config/webhook-routing.ts +67 -0
  257. package/src/context/compactor.ts +10 -3
  258. package/src/context/token-estimator.ts +21 -0
  259. package/src/daemon/__tests__/channel-ui-capability.test.ts +85 -0
  260. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +3 -3
  261. package/src/daemon/__tests__/embedding-reconcile.test.ts +3 -3
  262. package/src/daemon/__tests__/inference-profile-notification.test.ts +3 -6
  263. package/src/daemon/__tests__/native-web-search-metadata.test.ts +5 -5
  264. package/src/daemon/__tests__/web-search-status-text.test.ts +2 -2
  265. package/src/daemon/channel-ui-capability.ts +70 -1
  266. package/src/daemon/conversation-agent-loop-handlers.ts +4 -4
  267. package/src/daemon/conversation-agent-loop.ts +12 -5
  268. package/src/daemon/conversation-evictor.ts +26 -4
  269. package/src/daemon/conversation-launch.ts +11 -14
  270. package/src/daemon/conversation-messaging.ts +14 -14
  271. package/src/daemon/conversation-notifiers.ts +2 -2
  272. package/src/daemon/conversation-process.ts +133 -21
  273. package/src/daemon/conversation-queue-manager.ts +7 -7
  274. package/src/daemon/conversation-runtime-assembly.ts +16 -0
  275. package/src/daemon/conversation-store.ts +11 -3
  276. package/src/daemon/conversation-surfaces.ts +4 -4
  277. package/src/daemon/conversation-tool-setup.ts +13 -7
  278. package/src/daemon/conversation-usage.ts +12 -11
  279. package/src/daemon/conversation.ts +38 -15
  280. package/src/daemon/disk-pressure-guard.ts +5 -12
  281. package/src/daemon/embedding-reconcile.ts +14 -9
  282. package/src/daemon/handlers/config-channels.ts +40 -134
  283. package/src/daemon/handlers/config-model.ts +15 -7
  284. package/src/daemon/handlers/conversations.ts +4 -2
  285. package/src/daemon/handlers/shared.ts +29 -0
  286. package/src/daemon/host-proxy-base.ts +4 -4
  287. package/src/daemon/lifecycle.ts +13 -0
  288. package/src/daemon/message-protocol.ts +8 -144
  289. package/src/daemon/message-types/computer-use.ts +0 -12
  290. package/src/daemon/message-types/conversations.ts +9 -345
  291. package/src/daemon/message-types/host-app-control.ts +0 -9
  292. package/src/daemon/message-types/host-browser.ts +0 -7
  293. package/src/daemon/message-types/host-ui-snapshot.ts +0 -7
  294. package/src/daemon/message-types/messages.ts +0 -48
  295. package/src/daemon/message-types/notifications.ts +0 -7
  296. package/src/daemon/message-types/skills.ts +0 -5
  297. package/src/daemon/message-types/surfaces.ts +0 -9
  298. package/src/daemon/message-types/sync.ts +0 -2
  299. package/src/daemon/process-message.ts +5 -5
  300. package/src/daemon/skill-memory-refresh.ts +5 -5
  301. package/src/daemon/tool-side-effects.ts +13 -13
  302. package/src/daemon/trust-context.ts +1 -1
  303. package/src/daemon/wake-conversation-ops.ts +4 -4
  304. package/src/heartbeat/__tests__/heartbeat-run-store.test.ts +100 -7
  305. package/src/heartbeat/__tests__/heartbeat-service.test.ts +67 -0
  306. package/src/heartbeat/heartbeat-run-store.ts +81 -4
  307. package/src/heartbeat/heartbeat-service.ts +32 -1
  308. package/src/home/__tests__/feed-writer.test.ts +7 -7
  309. package/src/home/__tests__/home-content-refresh.test.ts +3 -0
  310. package/src/home/__tests__/suggested-prompts.test.ts +1 -0
  311. package/src/home/feed-writer.ts +6 -13
  312. package/src/home/home-content-refresh.ts +6 -9
  313. package/src/home/relationship-state-writer.ts +5 -12
  314. package/src/home/suggested-prompts-cache.ts +6 -19
  315. package/src/ipc/__tests__/events-publish-client.test.ts +67 -0
  316. package/src/ipc/events-publish-client.ts +44 -0
  317. package/src/ipc/routes/__tests__/conversation-sync-ipc-routes.test.ts +2 -2
  318. package/src/ipc/routes/__tests__/events-ipc-routes.test.ts +38 -9
  319. package/src/ipc/routes/events-ipc-routes.ts +11 -31
  320. package/src/live-voice/__tests__/front-decision.test.ts +4 -176
  321. package/src/live-voice/__tests__/live-voice-agent-turn.test.ts +276 -68
  322. package/src/live-voice/__tests__/live-voice-events.test.ts +6 -3
  323. package/src/live-voice/__tests__/live-voice-progress.test.ts +185 -46
  324. package/src/live-voice/__tests__/live-voice-triage-escalate.test.ts +24 -59
  325. package/src/live-voice/__tests__/live-voice-tts-session.test.ts +38 -54
  326. package/src/live-voice/__tests__/live-voice-vad.test.ts +218 -422
  327. package/src/live-voice/front-decision.ts +5 -155
  328. package/src/live-voice/live-voice-metrics.ts +4 -1
  329. package/src/live-voice/live-voice-session.ts +650 -333
  330. package/src/live-voice/progress-phrases.ts +17 -0
  331. package/src/live-voice/protocol.ts +15 -0
  332. package/src/media/app-icon-generator.ts +15 -8
  333. package/src/media/avatar-router.ts +9 -5
  334. package/src/media/image-credentials.ts +42 -10
  335. package/src/media/image-service.ts +1 -13
  336. package/src/media/types.ts +26 -3
  337. package/src/notifications/AGENTS.md +2 -0
  338. package/src/notifications/__tests__/broadcaster.test.ts +95 -0
  339. package/src/notifications/adapters/macos.ts +3 -3
  340. package/src/notifications/broadcaster.ts +57 -0
  341. package/src/notifications/copy-composer.ts +0 -15
  342. package/src/notifications/decision-engine.ts +21 -22
  343. package/src/notifications/guardian-question-mode.ts +57 -2
  344. package/src/notifications/home-feed-side-effect.ts +4 -0
  345. package/src/notifications/signal.ts +6 -4
  346. package/src/notifications/trusted-contact-payloads.ts +3 -4
  347. package/src/notifications/vellum-card-affinity.ts +39 -0
  348. package/src/oauth/AGENTS.md +1 -1
  349. package/src/permissions/channel-permission-query.test.ts +69 -0
  350. package/src/permissions/channel-permission-query.ts +73 -1
  351. package/src/permissions/checker.test.ts +85 -9
  352. package/src/permissions/checker.ts +17 -22
  353. package/src/permissions/confirmation-guardian-request.test.ts +2 -2
  354. package/src/permissions/confirmation-guardian-request.ts +2 -2
  355. package/src/permissions/gateway-threshold-reader.test.ts +224 -17
  356. package/src/permissions/gateway-threshold-reader.ts +82 -10
  357. package/src/permissions/ipc-risk-types.ts +8 -0
  358. package/src/permissions/prompter.ts +5 -5
  359. package/src/permissions/question-guardian-request.test.ts +222 -0
  360. package/src/permissions/question-guardian-request.ts +179 -0
  361. package/src/permissions/question-prompter.test.ts +8 -8
  362. package/src/permissions/question-prompter.ts +18 -0
  363. package/src/permissions/secret-prompter.ts +2 -4
  364. package/src/permissions/workspace-policy.ts +236 -34
  365. package/src/persistence/__tests__/lifecycle-quiesce.test.ts +82 -0
  366. package/src/persistence/__tests__/message-content-file.test.ts +58 -3
  367. package/src/persistence/conversation-crud.ts +93 -0
  368. package/src/persistence/conversation-key-store.ts +63 -0
  369. package/src/persistence/db-async-query.ts +1 -1
  370. package/src/persistence/db-singleton.ts +14 -27
  371. package/src/persistence/delivery-crud.ts +45 -0
  372. package/src/persistence/embeddings/embedding-backend.ts +2 -1
  373. package/src/persistence/embeddings/embedding-identity.ts +7 -3
  374. package/src/persistence/job-utils.ts +32 -27
  375. package/src/persistence/jobs-store.ts +92 -3
  376. package/src/persistence/lifecycle-quiesce.ts +80 -0
  377. package/src/persistence/llm-usage-store.ts +45 -8
  378. package/src/persistence/message-content-file.ts +4 -4
  379. package/src/persistence/migrations/336-move-memory-v2-activation-logs-to-memory-db.ts +1 -1
  380. package/src/persistence/migrations/351-schedule-skill-script-handoff.ts +39 -0
  381. package/src/persistence/migrations/352-drop-schedule-skill-script-handoff.test.ts +83 -0
  382. package/src/persistence/migrations/352-drop-schedule-skill-script-handoff.ts +36 -0
  383. package/src/persistence/migrations/353-add-llm-usage-conversation-type.test.ts +110 -0
  384. package/src/persistence/migrations/353-add-llm-usage-conversation-type.ts +49 -0
  385. package/src/persistence/schema/infrastructure.ts +11 -0
  386. package/src/persistence/steps.ts +7 -1
  387. package/src/plugin-api/__tests__/publish-event-forwarding.test.ts +113 -0
  388. package/src/plugin-api/__tests__/publish-event.test.ts +57 -0
  389. package/src/plugin-api/conversation-turn.ts +4 -4
  390. package/src/plugin-api/event-hub-facade.ts +15 -6
  391. package/src/plugin-api/index.ts +10 -4
  392. package/src/plugin-api/publish-event.ts +36 -0
  393. package/src/plugins/AGENTS.md +2 -0
  394. package/src/plugins/defaults/compaction/window-manager.ts +9 -0
  395. package/src/plugins/defaults/index.ts +9 -13
  396. package/src/plugins/defaults/memory/AGENTS.md +775 -0
  397. package/src/plugins/defaults/memory/__tests__/capability-seed-embed-gate.test.ts +186 -0
  398. package/src/plugins/defaults/memory/__tests__/fixtures/memory-v2-activation-fixtures.ts +1 -1
  399. package/src/plugins/defaults/memory/__tests__/indexer-tier-dispatch.test.ts +129 -0
  400. package/src/plugins/defaults/memory/__tests__/jobs-worker-quiesce-gate.test.ts +91 -0
  401. package/src/plugins/defaults/memory/__tests__/jobs-worker-v1-graph-substrate-noop.test.ts +342 -0
  402. package/src/plugins/defaults/memory/__tests__/jobs-worker-v2-graph-trigger-embed.test.ts +9 -1
  403. package/src/plugins/defaults/memory/__tests__/jobs-worker-v2-schedule.test.ts +36 -1
  404. package/src/plugins/defaults/memory/__tests__/jobs-worker-v2-sweep-defer.test.ts +9 -1
  405. package/src/plugins/defaults/memory/__tests__/memory-log-stores-degraded.test.ts +2 -2
  406. package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +44 -10
  407. package/src/plugins/defaults/memory/__tests__/memory-retrospective-sweep.test.ts +151 -10
  408. package/src/plugins/defaults/memory/__tests__/memory-tier-boundary-guard.test.ts +1155 -0
  409. package/src/plugins/defaults/memory/__tests__/memory-v2-activation-log-store.test.ts +1 -1
  410. package/src/plugins/defaults/memory/__tests__/memory-v2-concept-frequency.test.ts +3 -3
  411. package/src/plugins/defaults/memory/__tests__/tools-delete-memory-page.test.ts +1 -1
  412. package/src/plugins/defaults/memory/context-search/sources/memory-v2.ts +71 -35
  413. package/src/plugins/defaults/memory/context-search/sources/memory.ts +5 -1
  414. package/src/plugins/defaults/memory/fork-conversation-memory.ts +4 -4
  415. package/src/plugins/defaults/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +80 -10
  416. package/src/plugins/defaults/memory/graph/__tests__/handle-remember-v2.test.ts +1 -1
  417. package/src/plugins/defaults/memory/graph/capability-seed.ts +24 -10
  418. package/src/plugins/defaults/memory/graph/conversation-graph-memory.ts +123 -49
  419. package/src/plugins/defaults/memory/graph/graph-search.ts +6 -118
  420. package/src/plugins/defaults/memory/graph/in-context-tracker.test.ts +173 -0
  421. package/src/plugins/defaults/memory/graph/in-context-tracker.ts +109 -0
  422. package/src/plugins/defaults/memory/graph/store.ts +1 -0
  423. package/src/plugins/defaults/memory/graph/tool-handlers.test.ts +1 -1
  424. package/src/plugins/defaults/memory/graph/tool-handlers.ts +5 -2
  425. package/src/plugins/defaults/memory/graph-topology/__tests__/build-memory-graph.test.ts +1 -1
  426. package/src/plugins/defaults/memory/graph-topology/build-memory-graph.ts +6 -9
  427. package/src/plugins/defaults/memory/hooks/__tests__/user-prompt-submit.test.ts +30 -16
  428. package/src/plugins/defaults/memory/hooks/user-prompt-submit.ts +18 -14
  429. package/src/plugins/defaults/memory/indexer.ts +146 -89
  430. package/src/plugins/defaults/memory/injectors.ts +86 -41
  431. package/src/plugins/defaults/memory/job-handlers.ts +130 -39
  432. package/src/plugins/defaults/memory/jobs/__tests__/embed-concept-page.test.ts +2 -2
  433. package/src/plugins/defaults/memory/jobs/embed-concept-page.ts +22 -10
  434. package/src/plugins/defaults/memory/jobs-worker.ts +279 -208
  435. package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +4 -2
  436. package/src/plugins/defaults/memory/memory-retrospective-job.ts +40 -3
  437. package/src/plugins/defaults/memory/memory-retrospective-sweep.ts +99 -11
  438. package/src/plugins/defaults/memory/src/__tests__/memory-v2-routes.test.ts +5 -3
  439. package/src/plugins/defaults/memory/src/__tests__/memory-v2-simulate-route.test.ts +3 -4
  440. package/src/plugins/defaults/memory/src/memory-graph-routes.ts +2 -1
  441. package/src/plugins/defaults/memory/src/memory-item-routes.test.ts +7 -5
  442. package/src/plugins/defaults/memory/src/memory-item-routes.ts +51 -19
  443. package/src/plugins/defaults/memory/src/memory-v2-routes.ts +31 -20
  444. package/src/plugins/defaults/memory/startup.ts +58 -21
  445. package/src/plugins/defaults/memory/substrate/__tests__/boot-maintenance.test.ts +335 -0
  446. package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/cli-command-store.test.ts +14 -8
  447. package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/consolidation-job.test.ts +51 -21
  448. package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/edge-index.test.ts +1 -1
  449. package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/frontmatter-sweep.test.ts +3 -3
  450. package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/page-index.test.ts +1 -1
  451. package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/page-store.test.ts +1 -1
  452. package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/prompts-consolidation.test.ts +2 -2
  453. package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/qdrant.test.ts +23 -14
  454. package/src/plugins/defaults/memory/substrate/__tests__/reembed-job.test.ts +262 -0
  455. package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/sim.test.ts +9 -12
  456. package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/skill-content.test.ts +2 -2
  457. package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/skill-store.test.ts +9 -9
  458. package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/sparse-bm25.test.ts +12 -5
  459. package/src/plugins/defaults/memory/substrate/__tests__/spread.test.ts +276 -0
  460. package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/static-context.test.ts +17 -8
  461. package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/sweep-job.test.ts +9 -9
  462. package/src/plugins/defaults/memory/{v3/substrate/memory-v2-startup.ts → substrate/boot-maintenance.ts} +43 -24
  463. package/src/plugins/defaults/memory/{v3/substrate → substrate}/cli-command-store.ts +13 -8
  464. package/src/plugins/defaults/memory/{v3/substrate → substrate}/consolidation-job.ts +51 -27
  465. package/src/plugins/defaults/memory/{v3/substrate → substrate}/constants.ts +1 -0
  466. package/src/plugins/defaults/memory/{v3/substrate → substrate}/edge-index.ts +28 -9
  467. package/src/plugins/defaults/memory/{v3/substrate → substrate}/frontmatter-sweep.ts +14 -7
  468. package/src/plugins/defaults/memory/{v3/substrate → substrate}/injected-block-slugs.ts +9 -3
  469. package/src/plugins/defaults/memory/{v3/substrate → substrate}/page-index.ts +29 -10
  470. package/src/plugins/defaults/memory/{v3/substrate → substrate}/page-store.ts +35 -12
  471. package/src/plugins/defaults/memory/{v3/substrate → substrate}/prompts/consolidation.ts +14 -8
  472. package/src/plugins/defaults/memory/{v3/substrate → substrate}/prompts/sweep.ts +4 -1
  473. package/src/plugins/defaults/memory/{v3/substrate → substrate}/qdrant.ts +96 -36
  474. package/src/plugins/defaults/memory/substrate/reembed-job.ts +54 -0
  475. package/src/plugins/defaults/memory/{v3/substrate → substrate}/sim.ts +26 -15
  476. package/src/plugins/defaults/memory/{v3/substrate → substrate}/skill-content.ts +1 -1
  477. package/src/plugins/defaults/memory/{v3/substrate → substrate}/skill-store.ts +13 -8
  478. package/src/plugins/defaults/memory/{v3/substrate → substrate}/sparse-bm25.ts +27 -10
  479. package/src/plugins/defaults/memory/substrate/spread.ts +165 -0
  480. package/src/plugins/defaults/memory/{v3/substrate → substrate}/static-context.ts +12 -7
  481. package/src/plugins/defaults/memory/{v3/substrate → substrate}/sweep-job.ts +20 -15
  482. package/src/plugins/defaults/memory/substrate/tuning.ts +93 -0
  483. package/src/plugins/defaults/memory/{v3/substrate → substrate}/types.ts +7 -4
  484. package/src/plugins/defaults/memory/tools.test.ts +1 -1
  485. package/src/plugins/defaults/memory/tools.ts +1 -1
  486. package/src/plugins/defaults/memory/v1/README.md +5 -0
  487. package/src/plugins/defaults/memory/{__tests__ → v1/__tests__}/filing-jobs.test.ts +4 -4
  488. package/src/plugins/defaults/memory/{filing-jobs.ts → v1/filing-jobs.ts} +3 -3
  489. package/src/plugins/defaults/memory/{graph → v1/graph}/bootstrap.test.ts +68 -6
  490. package/src/plugins/defaults/memory/{graph → v1/graph}/bootstrap.ts +40 -21
  491. package/src/plugins/defaults/memory/{graph → v1/graph}/consolidation.ts +14 -14
  492. package/src/plugins/defaults/memory/{graph → v1/graph}/decay.test.ts +1 -1
  493. package/src/plugins/defaults/memory/{graph → v1/graph}/decay.ts +3 -3
  494. package/src/plugins/defaults/memory/{graph → v1/graph}/extraction-job.ts +5 -5
  495. package/src/plugins/defaults/memory/{graph → v1/graph}/extraction.test.ts +1 -1
  496. package/src/plugins/defaults/memory/{graph → v1/graph}/extraction.ts +20 -14
  497. package/src/plugins/defaults/memory/{graph → v1/graph}/graph-search.test.ts +3 -3
  498. package/src/plugins/defaults/memory/v1/graph/graph-search.ts +123 -0
  499. package/src/plugins/defaults/memory/{graph → v1/graph}/injection.test.ts +1 -118
  500. package/src/plugins/defaults/memory/{graph → v1/graph}/injection.ts +9 -111
  501. package/src/plugins/defaults/memory/{graph → v1/graph}/narrative.ts +6 -6
  502. package/src/plugins/defaults/memory/{graph → v1/graph}/pattern-scan.ts +5 -5
  503. package/src/plugins/defaults/memory/{graph → v1/graph}/retriever.test.ts +135 -42
  504. package/src/plugins/defaults/memory/{graph → v1/graph}/retriever.ts +40 -22
  505. package/src/plugins/defaults/memory/{graph → v1/graph}/scoring.test.ts +1 -1
  506. package/src/plugins/defaults/memory/{graph → v1/graph}/scoring.ts +1 -1
  507. package/src/plugins/defaults/memory/{graph → v1/graph}/serendipity.ts +1 -1
  508. package/src/plugins/defaults/memory/{graph → v1/graph}/triggers.test.ts +1 -1
  509. package/src/plugins/defaults/memory/{graph → v1/graph}/triggers.ts +1 -1
  510. package/src/plugins/defaults/memory/{identity-context.ts → v1/identity-context.ts} +4 -4
  511. package/src/plugins/defaults/memory/{job-handlers → v1/job-handlers}/backfill.ts +6 -6
  512. package/src/plugins/defaults/memory/{job-handlers → v1/job-handlers}/embedding.test.ts +6 -6
  513. package/src/plugins/defaults/memory/{job-handlers → v1/job-handlers}/embedding.ts +6 -6
  514. package/src/plugins/defaults/memory/{job-handlers → v1/job-handlers}/index-maintenance.ts +8 -8
  515. package/src/plugins/defaults/memory/{jobs → v1/jobs}/embed-pkb-file.test.ts +7 -7
  516. package/src/plugins/defaults/memory/{jobs → v1/jobs}/embed-pkb-file.ts +5 -5
  517. package/src/plugins/defaults/memory/{pkb → v1/pkb}/autoinject.ts +1 -1
  518. package/src/plugins/defaults/memory/{pkb → v1/pkb}/context.ts +2 -2
  519. package/src/plugins/defaults/memory/{pkb → v1/pkb}/pkb-index.test.ts +3 -3
  520. package/src/plugins/defaults/memory/{pkb → v1/pkb}/pkb-index.ts +2 -2
  521. package/src/plugins/defaults/memory/{pkb → v1/pkb}/pkb-reconcile.test.ts +3 -3
  522. package/src/plugins/defaults/memory/{pkb → v1/pkb}/pkb-reconcile.ts +2 -2
  523. package/src/plugins/defaults/memory/{pkb → v1/pkb}/pkb-search.test.ts +3 -3
  524. package/src/plugins/defaults/memory/{pkb → v1/pkb}/pkb-search.ts +6 -6
  525. package/src/plugins/defaults/memory/{pkb → v1/pkb}/types.ts +3 -5
  526. package/src/plugins/defaults/memory/{pkb-schedule.ts → v1/pkb-schedule.ts} +2 -2
  527. package/src/plugins/defaults/memory/{search/semantic.ts → v1/semantic-search.ts} +1 -1
  528. package/src/plugins/defaults/memory/v2/__tests__/activation-store.test.ts +1 -1
  529. package/src/plugins/defaults/memory/v2/__tests__/activation.test.ts +11 -274
  530. package/src/plugins/defaults/memory/v2/__tests__/backfill-jobs.test.ts +9 -131
  531. package/src/plugins/defaults/memory/v2/__tests__/harness-compare.test.ts +1 -1
  532. package/src/plugins/defaults/memory/v2/__tests__/harness-oracle.test.ts +1 -1
  533. package/src/plugins/defaults/memory/v2/__tests__/harness-replay-input.test.ts +1 -1
  534. package/src/plugins/defaults/memory/v2/__tests__/injection.test.ts +14 -10
  535. package/src/plugins/defaults/memory/v2/__tests__/reranker.test.ts +7 -5
  536. package/src/plugins/defaults/memory/v2/__tests__/router.test.ts +12 -6
  537. package/src/plugins/defaults/memory/{memory-v2-activation-log-store.ts → v2/activation-log-store.ts} +2 -2
  538. package/src/plugins/defaults/memory/v2/activation-store.ts +22 -8
  539. package/src/plugins/defaults/memory/v2/activation.ts +30 -163
  540. package/src/plugins/defaults/memory/v2/backfill-jobs.ts +7 -54
  541. package/src/plugins/defaults/memory/{memory-v2-concept-frequency.ts → v2/concept-frequency.ts} +3 -3
  542. package/src/plugins/defaults/memory/v2/harness/oracle.ts +1 -1
  543. package/src/plugins/defaults/memory/v2/harness/replay-input.ts +2 -2
  544. package/src/plugins/defaults/memory/v2/harness/retriever.ts +1 -1
  545. package/src/plugins/defaults/memory/v2/injection.ts +36 -26
  546. package/src/plugins/defaults/memory/v2/migration.ts +2 -2
  547. package/src/plugins/defaults/memory/{rerank-local.ts → v2/rerank-local.ts} +44 -18
  548. package/src/plugins/defaults/memory/v2/reranker.ts +29 -11
  549. package/src/plugins/defaults/memory/v2/router.ts +38 -15
  550. package/src/plugins/defaults/memory/v3/__tests__/candidate-match.test.ts +5 -3
  551. package/src/plugins/defaults/memory/v3/__tests__/carry-integration.test.ts +21 -8
  552. package/src/plugins/defaults/memory/v3/__tests__/edge.test.ts +1 -1
  553. package/src/plugins/defaults/memory/v3/__tests__/live-integration.test.ts +1 -1
  554. package/src/plugins/defaults/memory/v3/__tests__/maintain-job.test.ts +4 -1
  555. package/src/plugins/defaults/memory/v3/__tests__/orchestrate.test.ts +181 -3
  556. package/src/plugins/defaults/memory/v3/__tests__/selection-log-store.test.ts +3 -0
  557. package/src/plugins/defaults/memory/v3/__tests__/shadow-integration.test.ts +14 -5
  558. package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +40 -50
  559. package/src/plugins/defaults/memory/v3/__tests__/tuning-profile.test.ts +1 -0
  560. package/src/plugins/defaults/memory/v3/candidate-match.ts +2 -2
  561. package/src/plugins/defaults/memory/v3/capabilities.ts +6 -4
  562. package/src/plugins/defaults/memory/v3/card.ts +25 -9
  563. package/src/plugins/defaults/memory/v3/edge.ts +52 -18
  564. package/src/plugins/defaults/memory/v3/gate.ts +4 -4
  565. package/src/plugins/defaults/memory/v3/injector.ts +10 -3
  566. package/src/plugins/defaults/memory/v3/maintain-job.ts +3 -3
  567. package/src/plugins/defaults/memory/v3/orchestrate.ts +100 -18
  568. package/src/plugins/defaults/memory/v3/page-content.ts +26 -10
  569. package/src/plugins/defaults/memory/v3/prune.ts +40 -16
  570. package/src/plugins/defaults/memory/v3/selection-log-store.ts +34 -12
  571. package/src/plugins/defaults/memory/v3/shadow-plugin.ts +9 -13
  572. package/src/plugins/defaults/memory/v3/span-query.test.ts +86 -0
  573. package/src/plugins/defaults/memory/v3/span-query.ts +71 -0
  574. package/src/plugins/defaults/memory/v3/tuning-profile.ts +3 -0
  575. package/src/plugins/defaults/memory/v3/types.ts +4 -1
  576. package/src/plugins/defaults/memory/v3-eval/eval-packets.ts +2 -2
  577. package/src/plugins/defaults/memory/validation.ts +1 -1
  578. package/src/plugins/defaults/memory/worker.ts +2 -1
  579. package/src/plugins/defaults/platform-hosted/__tests__/reengage.test.ts +104 -12
  580. package/src/plugins/defaults/platform-hosted/routes/reengage.ts +104 -12
  581. package/src/plugins/types.ts +2 -2
  582. package/src/providers/__tests__/registry-native-web-search.test.ts +47 -2
  583. package/src/providers/anthropic/client.ts +15 -6
  584. package/src/providers/content-block-schema.ts +26 -0
  585. package/src/providers/inference/__tests__/connections-credential-normalize.test.ts +87 -0
  586. package/src/providers/inference/adapter-factory.ts +6 -0
  587. package/src/providers/inference/connections.ts +30 -14
  588. package/src/providers/model-catalog.ts +77 -0
  589. package/src/providers/openai/__tests__/chat-completions-input-audio.test.ts +260 -0
  590. package/src/providers/openai/chat-completions-provider.ts +65 -17
  591. package/src/providers/openai/input-audio.ts +76 -0
  592. package/src/providers/openrouter/client.ts +0 -2
  593. package/src/providers/platform-proxy/constants.ts +4 -0
  594. package/src/providers/poolside/client.ts +42 -0
  595. package/src/providers/registry.ts +40 -8
  596. package/src/providers/retry.ts +2 -0
  597. package/src/providers/types.ts +39 -1
  598. package/src/providers/vercel-ai-gateway/client.ts +0 -2
  599. package/src/runtime/AGENTS.md +7 -5
  600. package/src/runtime/__tests__/question-request-guardian-bridge.test.ts +173 -0
  601. package/src/runtime/__tests__/question-resolution.test.ts +111 -0
  602. package/src/runtime/access-request-helper.ts +7 -15
  603. package/src/runtime/assistant-event-hub.ts +43 -31
  604. package/src/runtime/assistant-event-publish-options.ts +31 -0
  605. package/src/runtime/assistant-event.ts +19 -30
  606. package/src/runtime/assistant-stream-state.ts +4 -4
  607. package/src/runtime/channel-approvals.ts +0 -25
  608. package/src/runtime/channel-readiness-service.ts +99 -40
  609. package/src/runtime/channel-retry-sweep.ts +38 -15
  610. package/src/runtime/confirmation-request-guardian-bridge.ts +4 -0
  611. package/src/runtime/guardian-reply-router.ts +108 -0
  612. package/src/runtime/http-server.ts +8 -0
  613. package/src/runtime/http-types.ts +2 -2
  614. package/src/runtime/process-role.ts +42 -0
  615. package/src/runtime/question-request-guardian-bridge.ts +184 -0
  616. package/src/runtime/question-resolution.ts +153 -0
  617. package/src/runtime/routes/__tests__/bookmark-routes.test.ts +6 -2
  618. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +1 -1
  619. package/src/runtime/routes/acp-routes.ts +2 -2
  620. package/src/runtime/routes/bookmark-routes.ts +3 -15
  621. package/src/runtime/routes/canned-message-complete.ts +2 -2
  622. package/src/runtime/routes/consolidation-routes.ts +8 -2
  623. package/src/runtime/routes/conversation-list-routes.ts +3 -19
  624. package/src/runtime/routes/conversation-management-routes.ts +1 -5
  625. package/src/runtime/routes/conversation-query-routes.ts +52 -8
  626. package/src/runtime/routes/conversation-routes.ts +36 -22
  627. package/src/runtime/routes/credential-request-routes.ts +0 -1
  628. package/src/runtime/routes/filing-routes.ts +7 -7
  629. package/src/runtime/routes/global-search-routes.ts +1 -1
  630. package/src/runtime/routes/guardian-approval-prompt.ts +2 -2
  631. package/src/runtime/routes/image-generation-routes.ts +12 -9
  632. package/src/runtime/routes/inbound-message-handler.ts +17 -0
  633. package/src/runtime/routes/inbound-stages/acl-enforcement.test.ts +50 -0
  634. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +19 -0
  635. package/src/runtime/routes/inbound-stages/background-dispatch.ts +4 -4
  636. package/src/runtime/routes/inbound-stages/inbound-content-prep.test.ts +129 -0
  637. package/src/runtime/routes/inbound-stages/inbound-content-prep.ts +141 -9
  638. package/src/runtime/routes/index.ts +2 -0
  639. package/src/runtime/routes/lifecycle-routes.ts +199 -0
  640. package/src/runtime/routes/platform-routes.ts +3 -8
  641. package/src/runtime/routes/playground/__tests__/inject-failures.test.ts +7 -7
  642. package/src/runtime/routes/playground/__tests__/reset-circuit.test.ts +5 -5
  643. package/src/runtime/routes/question-routes.ts +34 -87
  644. package/src/runtime/routes/secret-routes.ts +11 -1
  645. package/src/runtime/routes/settings-routes.ts +8 -18
  646. package/src/runtime/routes/upgrade-broadcast-routes.ts +4 -5
  647. package/src/runtime/routes/workspace-routes.test.ts +7 -4
  648. package/src/runtime/slack-reply-session.test.ts +12 -12
  649. package/src/runtime/slack-reply-session.ts +17 -17
  650. package/src/runtime/sync/sync-publisher.test.ts +7 -5
  651. package/src/runtime/tool-grant-request-helper.ts +4 -0
  652. package/src/schedule/schedule-store.ts +70 -0
  653. package/src/schedule/scheduler.ts +27 -0
  654. package/src/security/AGENTS.md +1 -1
  655. package/src/security/credential-key.ts +17 -0
  656. package/src/security/secret-ingress.ts +14 -105
  657. package/src/security/secret-patterns.ts +8 -139
  658. package/src/security/secret-scanner.ts +8 -3
  659. package/src/security/store-path-override.ts +6 -16
  660. package/src/sequence/store.ts +8 -0
  661. package/src/signals/emit-event.ts +5 -10
  662. package/src/signals/event-stream.ts +20 -10
  663. package/src/subagent/manager.ts +76 -25
  664. package/src/subagent/notify.ts +8 -0
  665. package/src/subagent/types.ts +13 -3
  666. package/src/telegram/__tests__/webhook-health.test.ts +442 -0
  667. package/src/telegram/webhook-health.ts +478 -0
  668. package/src/telemetry/AGENTS.md +6 -0
  669. package/src/telemetry/memory-corpus-size.test.ts +129 -0
  670. package/src/telemetry/memory-corpus-size.ts +184 -0
  671. package/src/telemetry/memory-tier-reporter.test.ts +67 -2
  672. package/src/telemetry/memory-tier-reporter.ts +30 -3
  673. package/src/tools/__tests__/document-tool-input-schemas.test.ts +103 -0
  674. package/src/tools/__tests__/followup-call-acp-tool-input-schemas.test.ts +125 -0
  675. package/src/tools/__tests__/schedule-tool-input-schemas.test.ts +83 -0
  676. package/src/tools/__tests__/subagent-workflow-tool-input-schemas.test.ts +116 -0
  677. package/src/tools/__tests__/tool-input-schemas.test.ts +192 -0
  678. package/src/tools/__tests__/tool-schema-drift-harness.ts +142 -0
  679. package/src/tools/acp/abort.ts +21 -1
  680. package/src/tools/acp/context.ts +5 -5
  681. package/src/tools/acp/spawn.test.ts +15 -5
  682. package/src/tools/acp/spawn.ts +26 -3
  683. package/src/tools/acp/status.ts +20 -1
  684. package/src/tools/acp/steer.test.ts +20 -0
  685. package/src/tools/acp/steer.ts +23 -2
  686. package/src/tools/ask-question/ask-question-tool.test.ts +65 -0
  687. package/src/tools/ask-question/ask-question-tool.ts +68 -92
  688. package/src/tools/browser/browser-screencast.ts +6 -6
  689. package/src/tools/calls/call-end.ts +24 -3
  690. package/src/tools/calls/call-start.ts +35 -11
  691. package/src/tools/calls/call-status.ts +20 -1
  692. package/src/tools/document/document-comment-tool.test.ts +49 -0
  693. package/src/tools/document/document-comment-tool.ts +45 -6
  694. package/src/tools/document/document-tool.ts +142 -24
  695. package/src/tools/executor.ts +27 -13
  696. package/src/tools/filesystem/edit.ts +62 -60
  697. package/src/tools/filesystem/list.ts +36 -29
  698. package/src/tools/filesystem/read.ts +56 -39
  699. package/src/tools/filesystem/write.ts +44 -38
  700. package/src/tools/followups/followup_create.ts +41 -12
  701. package/src/tools/followups/followup_list.ts +34 -6
  702. package/src/tools/followups/followup_resolve.ts +26 -4
  703. package/src/tools/host-filesystem/edit.ts +50 -60
  704. package/src/tools/host-filesystem/read.ts +45 -39
  705. package/src/tools/host-filesystem/write.ts +31 -36
  706. package/src/tools/host-terminal/host-shell.ts +76 -62
  707. package/src/tools/policy-context.ts +27 -10
  708. package/src/tools/schedule/create.ts +76 -33
  709. package/src/tools/schedule/delete.ts +21 -2
  710. package/src/tools/schedule/list.ts +25 -3
  711. package/src/tools/schedule/update.ts +105 -48
  712. package/src/tools/shared/filesystem/path-policy.ts +246 -40
  713. package/src/tools/shared/zod-tool-schema.ts +117 -0
  714. package/src/tools/subagent/abort.ts +12 -4
  715. package/src/tools/subagent/consult-deadline.ts +9 -3
  716. package/src/tools/subagent/message.ts +21 -5
  717. package/src/tools/subagent/notify-parent.ts +44 -23
  718. package/src/tools/subagent/read.ts +15 -4
  719. package/src/tools/subagent/resolve.ts +26 -4
  720. package/src/tools/subagent/spawn.ts +37 -13
  721. package/src/tools/subagent/status.ts +12 -4
  722. package/src/tools/system/request-permission.ts +35 -26
  723. package/src/tools/terminal/shell.ts +68 -55
  724. package/src/tools/tool-approval-handler.ts +327 -53
  725. package/src/tools/tool-input-schemas.ts +85 -0
  726. package/src/tools/tool-types.ts +7 -0
  727. package/src/tools/types.ts +8 -0
  728. package/src/tools/workflows/manage-workflows.ts +30 -2
  729. package/src/tools/workflows/run-workflow.test.ts +54 -2
  730. package/src/tools/workflows/run-workflow.ts +32 -4
  731. package/src/util/fs-symlinks.ts +30 -0
  732. package/src/util/log-redact.ts +7 -3
  733. package/src/vellum-assistant-namespace.d.ts +80 -0
  734. package/src/watcher/watcher-store.ts +8 -0
  735. package/src/workflows/leaf-runner.test.ts +104 -2
  736. package/src/workflows/leaf-runner.ts +31 -6
  737. package/src/workspace/migrations/070-memory-v2-summary-schema-rebuild.ts +2 -2
  738. package/src/workspace/migrations/134-image-generation-mode-to-provider.ts +128 -0
  739. package/src/workspace/migrations/135-copy-substrate-tunables.ts +248 -0
  740. package/src/workspace/migrations/__tests__/135-copy-substrate-tunables.test.ts +446 -0
  741. package/src/workspace/migrations/__tests__/backfill-v3-leaves.test.ts +2 -2
  742. package/src/workspace/migrations/registry.ts +4 -0
  743. package/src/__tests__/memory-retrieval-budget.test.ts +0 -49
  744. package/src/__tests__/secret-patterns.test.ts +0 -27
  745. package/src/config/__tests__/proc-to-skills.test.ts +0 -21
  746. package/src/config/memory-tier.test.ts +0 -44
  747. package/src/daemon/message-types/acp.ts +0 -20
  748. package/src/daemon/message-types/apps.ts +0 -11
  749. package/src/daemon/message-types/background-tools.ts +0 -14
  750. package/src/daemon/message-types/bookmarks.ts +0 -13
  751. package/src/daemon/message-types/contacts.ts +0 -13
  752. package/src/daemon/message-types/document-comments.ts +0 -14
  753. package/src/daemon/message-types/documents.ts +0 -13
  754. package/src/daemon/message-types/home.ts +0 -15
  755. package/src/daemon/message-types/host-bash.ts +0 -12
  756. package/src/daemon/message-types/host-cu.ts +0 -10
  757. package/src/daemon/message-types/host-file.ts +0 -12
  758. package/src/daemon/message-types/host-transfer.ts +0 -12
  759. package/src/daemon/message-types/integrations.ts +0 -90
  760. package/src/daemon/message-types/memory.ts +0 -10
  761. package/src/daemon/message-types/schedules.ts +0 -14
  762. package/src/daemon/message-types/settings.ts +0 -17
  763. package/src/daemon/message-types/subagents.ts +0 -63
  764. package/src/daemon/message-types/upgrades.ts +0 -14
  765. package/src/daemon/message-types/workflows.ts +0 -25
  766. package/src/daemon/message-types/workspace.ts +0 -137
  767. package/src/live-voice/ack-phrases.ts +0 -58
  768. package/src/plugins/defaults/memory/retrieval-budget.ts +0 -29
  769. package/src/plugins/defaults/memory/v3/__tests__/gate-flag.test.ts +0 -69
  770. package/src/plugins/defaults/memory/v3/gate-flag.ts +0 -20
  771. package/src/plugins/defaults/memory/v3/substrate/__tests__/memory-v2-startup.test.ts +0 -86
  772. package/src/plugins/defaults/memory/v3/substrate/memory-v2-startup.test.ts +0 -205
  773. /package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/concept-page-frontmatter-schema.test.ts +0 -0
  774. /package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/consolidation-prompt-flag-gating-guard.test.ts +0 -0
  775. /package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/injected-block-slugs.test.ts +0 -0
  776. /package/src/plugins/defaults/memory/{v3/substrate → substrate}/cli-command-content.ts +0 -0
@@ -1,4 +1,4 @@
1
- import { afterEach, describe, expect, mock, test } from "bun:test";
1
+ import { afterEach, beforeAll, describe, expect, mock, test } from "bun:test";
2
2
 
3
3
  import type { TurnDetectorConfig } from "../../calls/media-turn-detector.js";
4
4
  import type {
@@ -19,10 +19,8 @@ import type {
19
19
  StreamingTranscriber,
20
20
  SttStreamServerEvent,
21
21
  } from "../../stt/types.js";
22
- import type {
23
- VoiceEndpointDecisionInput,
24
- VoiceFrontDecider,
25
- } from "../front-decision.js";
22
+ import { __resetRegistryForTesting } from "../../tools/registry.js";
23
+ import type { VoiceFrontDecider } from "../front-decision.js";
26
24
  import type { LiveVoiceAudioArchiveResult } from "../live-voice-archive.js";
27
25
  import {
28
26
  createLiveVoiceSession,
@@ -314,6 +312,11 @@ async function flushAsyncCallbacks(): Promise<void> {
314
312
  }
315
313
 
316
314
  describe("LiveVoiceSession server VAD", () => {
315
+ // The foreground-wins classification consults the tool registry's owner map
316
+ // (a tool is only provably read-only when it is the trusted built-in);
317
+ // register the core baseline so built-in names resolve like in the daemon.
318
+ beforeAll(() => __resetRegistryForTesting());
319
+
317
320
  // The voice-duplex-handoff flag is toggled via the override cache in a few
318
321
  // tests below; reset it so it never leaks into the flag-off default cases.
319
322
  afterEach(() => clearCachedOverrides());
@@ -1325,8 +1328,13 @@ describe("LiveVoiceSession server VAD", () => {
1325
1328
  await waitFor(() => calls.some((c) => c.content === "second question"));
1326
1329
  // Barge #2 is a rapid second interruption: it bumps the sequence
1327
1330
  // synchronously, so A is invalidated even before barge #2's own async detach
1328
- // runs.
1331
+ // runs — and A's run itself is ABORTED, so a still-writing older
1332
+ // continuation can never overlap the newer full-ability one.
1329
1333
  await session.handleBinaryAudio(SUSTAINED_LOUD_CHUNK);
1334
+ await waitFor(
1335
+ () =>
1336
+ spawnBackgroundContinuation.mock.calls[0]?.[0]?.signal.aborted === true,
1337
+ );
1330
1338
  // A finishes AFTER barge #2 and must be rejected. The newer continuation is
1331
1339
  // left pending so it can't mask the bug by superseding A itself.
1332
1340
  resolvers[0]?.("OLDER_RESULT");
@@ -1342,8 +1350,12 @@ describe("LiveVoiceSession server VAD", () => {
1342
1350
  "background you finished",
1343
1351
  );
1344
1352
 
1345
- // Cleanup the still-pending newer continuation.
1353
+ // The newer continuation was not collateral damage of superseding A.
1346
1354
  await waitFor(() => resolvers.length === 2);
1355
+ expect(spawnBackgroundContinuation.mock.calls[1]?.[0]?.signal.aborted).toBe(
1356
+ false,
1357
+ );
1358
+ // Cleanup the still-pending newer continuation.
1347
1359
  resolvers[1]?.("");
1348
1360
  });
1349
1361
 
@@ -1415,6 +1427,198 @@ describe("LiveVoiceSession server VAD", () => {
1415
1427
  resolvers[1]?.("");
1416
1428
  });
1417
1429
 
1430
+ test("voice-duplex-handoff on: a foreground consequential tool start aborts an in-flight continuation", async () => {
1431
+ setCachedOverrides({ "voice-duplex-handoff": true }, { fromGateway: true });
1432
+ // Hang the continuation so it is still running when the follow-up turn
1433
+ // starts mutating; resolve it when its signal aborts.
1434
+ const spawnBackgroundContinuation = mock(
1435
+ (args: {
1436
+ parentConversationId: string;
1437
+ objective: string;
1438
+ label: string;
1439
+ signal: AbortSignal;
1440
+ }): Promise<string> =>
1441
+ new Promise<string>((resolve) => {
1442
+ args.signal.addEventListener("abort", () => resolve(""), {
1443
+ once: true,
1444
+ });
1445
+ }),
1446
+ );
1447
+ const calls: VoiceTurnOptions[] = [];
1448
+ const startVoiceTurn = mock(async (options: VoiceTurnOptions) => {
1449
+ calls.push(options);
1450
+ return { turnId: `bridge-turn-${calls.length}`, abort: mock() };
1451
+ });
1452
+ const streamTtsAudio = mock(async (options: LiveVoiceTtsOptions) => {
1453
+ options.onAudioChunk(makeTtsChunk("assistant audio"));
1454
+ return makeTtsResult("assistant audio");
1455
+ });
1456
+ const { frames, session } = createHarness({
1457
+ finals: ["first question", "second question"],
1458
+ startVoiceTurn,
1459
+ streamTtsAudio,
1460
+ spawnBackgroundContinuation,
1461
+ });
1462
+
1463
+ await session.start();
1464
+ await session.handleBinaryAudio(LOUD_CHUNK);
1465
+ await waitFor(() => frames.some((frame) => frame.type === "thinking"));
1466
+
1467
+ // Barge in during thinking: the continuation detaches, the follow-up turn
1468
+ // launches while it is still in flight.
1469
+ await session.handleBinaryAudio(SUSTAINED_LOUD_CHUNK);
1470
+ await waitFor(() => spawnBackgroundContinuation.mock.calls.length === 1);
1471
+ await waitFor(() => calls.some((c) => c.content === "second question"));
1472
+
1473
+ const signal = spawnBackgroundContinuation.mock.calls[0]?.[0]?.signal;
1474
+ expect(signal?.aborted).toBe(false);
1475
+
1476
+ // The follow-up turn starts a consequential tool: foreground wins the
1477
+ // workspace, so the still-running continuation is aborted before the two
1478
+ // can race on writes. `remember` is deliberately NOT on the core
1479
+ // side-effect name list — the classification must fail closed on any
1480
+ // tool that is not provably read-only, not just named writers.
1481
+ const followUp = calls.find((c) => c.content === "second question");
1482
+ followUp?.callbacks?.tool_use_start?.("remember", {
1483
+ toolUseId: "tool-1",
1484
+ });
1485
+ expect(signal?.aborted).toBe(true);
1486
+ });
1487
+
1488
+ test("voice-duplex-handoff on: a read-only built-in leaves the continuation running; an unclassified tool fails closed", async () => {
1489
+ setCachedOverrides({ "voice-duplex-handoff": true }, { fromGateway: true });
1490
+ let abortListenerArmed = false;
1491
+ const spawnBackgroundContinuation = mock(
1492
+ (args: {
1493
+ parentConversationId: string;
1494
+ objective: string;
1495
+ label: string;
1496
+ signal: AbortSignal;
1497
+ }): Promise<string> =>
1498
+ new Promise<string>((resolve) => {
1499
+ abortListenerArmed = true;
1500
+ args.signal.addEventListener("abort", () => resolve(""), {
1501
+ once: true,
1502
+ });
1503
+ }),
1504
+ );
1505
+ const calls: VoiceTurnOptions[] = [];
1506
+ const startVoiceTurn = mock(async (options: VoiceTurnOptions) => {
1507
+ calls.push(options);
1508
+ return { turnId: `bridge-turn-${calls.length}`, abort: mock() };
1509
+ });
1510
+ const streamTtsAudio = mock(async (options: LiveVoiceTtsOptions) => {
1511
+ options.onAudioChunk(makeTtsChunk("assistant audio"));
1512
+ return makeTtsResult("assistant audio");
1513
+ });
1514
+ const { frames, session } = createHarness({
1515
+ finals: ["first question", "second question"],
1516
+ startVoiceTurn,
1517
+ streamTtsAudio,
1518
+ spawnBackgroundContinuation,
1519
+ });
1520
+
1521
+ await session.start();
1522
+ await session.handleBinaryAudio(LOUD_CHUNK);
1523
+ await waitFor(() => frames.some((frame) => frame.type === "thinking"));
1524
+ await session.handleBinaryAudio(SUSTAINED_LOUD_CHUNK);
1525
+ await waitFor(() => abortListenerArmed);
1526
+ await waitFor(() => calls.some((c) => c.content === "second question"));
1527
+
1528
+ // A provably read-only built-in cannot contend on the workspace: the
1529
+ // continuation survives (this is the topic-change case the handoff
1530
+ // exists for).
1531
+ const followUp = calls.find((c) => c.content === "second question");
1532
+ followUp?.callbacks?.tool_use_start?.("file_read", { toolUseId: "tool-1" });
1533
+ const signal = spawnBackgroundContinuation.mock.calls[0]?.[0]?.signal;
1534
+ expect(signal?.aborted).toBe(false);
1535
+
1536
+ // skill_load is non-contending too: it only registers tool definitions,
1537
+ // and it is the first call of a follow-up turn that re-enters the skill
1538
+ // the interrupted turn was using — it must not kill the continuation.
1539
+ followUp?.callbacks?.tool_use_start?.("skill_load", {
1540
+ toolUseId: "tool-2",
1541
+ });
1542
+ expect(signal?.aborted).toBe(false);
1543
+
1544
+ // web_fetch is a network read: it cannot corrupt local state, so it does
1545
+ // not contend even though it is on the core SIDE_EFFECT_TOOLS list (which
1546
+ // exists for a permission question, not a contention one). This is the
1547
+ // canonical topic-change case — "what's the weather?" over a running
1548
+ // build — and it is exactly what the handoff exists to keep alive.
1549
+ followUp?.callbacks?.tool_use_start?.("web_fetch", {
1550
+ toolUseId: "tool-3",
1551
+ });
1552
+ expect(signal?.aborted).toBe(false);
1553
+
1554
+ // A tool the registry does not know (e.g. an MCP or plugin tool) is not
1555
+ // provably non-contending: fail closed and abort.
1556
+ followUp?.callbacks?.tool_use_start?.("calendar_lookup", {
1557
+ toolUseId: "tool-4",
1558
+ });
1559
+ expect(signal?.aborted).toBe(true);
1560
+ });
1561
+
1562
+ test("voice-duplex-handoff on: a foreground-wins abort keeps an already-stashed continuation result", async () => {
1563
+ setCachedOverrides({ "voice-duplex-handoff": true }, { fromGateway: true });
1564
+ let resolveContinuation: ((result: string) => void) | undefined;
1565
+ const spawnBackgroundContinuation = mock(
1566
+ (_args: {
1567
+ parentConversationId: string;
1568
+ objective: string;
1569
+ label: string;
1570
+ signal: AbortSignal;
1571
+ }): Promise<string> =>
1572
+ new Promise<string>((resolve) => {
1573
+ resolveContinuation = resolve;
1574
+ }),
1575
+ );
1576
+ const calls: VoiceTurnOptions[] = [];
1577
+ const startVoiceTurn = mock(async (options: VoiceTurnOptions) => {
1578
+ calls.push(options);
1579
+ return { turnId: `bridge-turn-${calls.length}`, abort: mock() };
1580
+ });
1581
+ const streamTtsAudio = mock(async (options: LiveVoiceTtsOptions) => {
1582
+ options.onAudioChunk(makeTtsChunk("assistant audio"));
1583
+ return makeTtsResult("assistant audio");
1584
+ });
1585
+ const { frames, session } = createHarness({
1586
+ finals: ["first question", "second question", "third question"],
1587
+ startVoiceTurn,
1588
+ streamTtsAudio,
1589
+ spawnBackgroundContinuation,
1590
+ });
1591
+
1592
+ await session.start();
1593
+ await session.handleBinaryAudio(LOUD_CHUNK);
1594
+ await waitFor(() => frames.some((frame) => frame.type === "thinking"));
1595
+
1596
+ // Barge in; the follow-up turn launches, then the continuation finishes
1597
+ // and stashes its answer for the NEXT turn while the follow-up still runs.
1598
+ await session.handleBinaryAudio(SUSTAINED_LOUD_CHUNK);
1599
+ await waitFor(() => resolveContinuation !== undefined);
1600
+ await waitFor(() => calls.some((c) => c.content === "second question"));
1601
+ resolveContinuation?.("THE_RESULT");
1602
+ await flushAsyncCallbacks();
1603
+
1604
+ // The follow-up turn starts writing: the foreground-wins abort fires, but
1605
+ // a completed continuation's stashed answer cannot race anything and is
1606
+ // kept.
1607
+ const followUp = calls.find((c) => c.content === "second question");
1608
+ followUp?.callbacks?.tool_use_start?.("file_write", {
1609
+ toolUseId: "tool-1",
1610
+ });
1611
+ followUp?.callbacks?.assistant_text_delta?.(makeTextDelta("ok"));
1612
+ followUp?.callbacks?.message_complete?.(makeMessageComplete());
1613
+ await waitFor(() => frames.some((frame) => frame.type === "tts_done"));
1614
+
1615
+ // The next turn still folds the stashed answer in as context.
1616
+ await session.handleBinaryAudio(LOUD_CHUNK);
1617
+ await waitFor(() => calls.some((c) => c.content === "third question"));
1618
+ const resurfaced = calls.find((c) => c.content === "third question");
1619
+ expect(resurfaced?.voiceControlPrompt).toContain("THE_RESULT");
1620
+ });
1621
+
1418
1622
  test("a late assistant_text_delta after a thinking barge-in never reaches the client", async () => {
1419
1623
  let callbacks: VoiceTurnCallbacks | undefined;
1420
1624
  const abort = mock();
@@ -2691,395 +2895,7 @@ describe("LiveVoiceSession sustained-speech barge-in guard", () => {
2691
2895
  });
2692
2896
  });
2693
2897
 
2694
- // Stub semantic-endpointing decider: scripted hold/release decisions consumed
2695
- // in order, with every consulted input captured. Falls back to "release" once
2696
- // the script is exhausted, mirroring the real decider's fail-open bias.
2697
- function makeFrontDecider(decisions: Array<"hold" | "release">): {
2698
- decider: VoiceFrontDecider;
2699
- calls: VoiceEndpointDecisionInput[];
2700
- } {
2701
- const calls: VoiceEndpointDecisionInput[] = [];
2702
- return {
2703
- calls,
2704
- decider: {
2705
- decideEndpoint: async (input) => {
2706
- calls.push(input);
2707
- return { action: decisions[calls.length - 1] ?? "release" };
2708
- },
2709
- generateAckText: async () => null,
2710
- generateProgressText: async () => null,
2711
- },
2712
- };
2713
- }
2714
-
2715
- describe("LiveVoiceSession semantic endpointing", () => {
2716
- // Arms the harness session, waits for its transcriber, and seeds a partial
2717
- // so the silence boundary has transcript text for the decider to judge.
2718
- async function startWithPartial(
2719
- session: LiveVoiceSession,
2720
- transcribers: MockStreamingTranscriber[],
2721
- partialText = "hello wor",
2722
- ): Promise<void> {
2723
- await session.start();
2724
- await waitFor(() => transcribers.length === 1);
2725
- // Let the transcriber's start() wiring settle before emitting events.
2726
- await flushAsyncCallbacks();
2727
- transcribers[0]?.emit({ type: "partial", text: partialText });
2728
- }
2729
-
2730
- test("a held silence boundary sends no utterance_end and replays after the extension", async () => {
2731
- const { decider, calls } = makeFrontDecider(["hold", "release"]);
2732
- const turnStarter = makeAutoCompletingTurnStarter(["Hi there."]);
2733
- const { frames, session, transcribers } = createHarness({
2734
- finals: ["hello world"],
2735
- startVoiceTurn: turnStarter.startVoiceTurn,
2736
- frontDecider: decider,
2737
- frontModelConfig: { endpointExtensionMs: 30 },
2738
- });
2739
-
2740
- await startWithPartial(session, transcribers);
2741
- await session.handleBinaryAudio(LOUD_CHUNK);
2742
-
2743
- // First silence boundary: the decider holds — no utterance_end, no turn.
2744
- await waitFor(() => calls.length === 1);
2745
- expect(calls[0]).toEqual({
2746
- transcriptSoFar: "",
2747
- latestPartial: "hello wor",
2748
- silenceThresholdMs: 40,
2749
- extensionCount: 0,
2750
- });
2751
- expect(countType(frames, "utterance_end")).toBe(0);
2752
- expect(countType(frames, "thinking")).toBe(0);
2753
-
2754
- // The extension elapses in continued silence: the boundary replays, the
2755
- // decider releases, and the turn launches normally.
2756
- await waitFor(() => frames.some((frame) => frame.type === "tts_done"));
2757
- expect(calls).toHaveLength(2);
2758
- expect(calls[1]).toMatchObject({ extensionCount: 1 });
2759
- expect(frameTypes(frames)).toEqual([
2760
- "ready",
2761
- "stt_partial",
2762
- "speech_started",
2763
- "utterance_end",
2764
- "stt_final",
2765
- "thinking",
2766
- "assistant_text_delta",
2767
- "tts_done",
2768
- ]);
2769
- expect(
2770
- frames.find((frame) => frame.type === "utterance_end"),
2771
- ).toMatchObject({ reason: "silence" });
2772
- expect(turnStarter.calls[0]).toMatchObject({ content: "hello world" });
2773
- });
2774
-
2775
- test("ptt_release during a held pause still emits utterance_end", async () => {
2776
- const { decider, calls } = makeFrontDecider(["hold"]);
2777
- const turnStarter = makeAutoCompletingTurnStarter(["Hi."]);
2778
- const { frames, session, transcribers } = createHarness({
2779
- finals: ["hello world"],
2780
- startVoiceTurn: turnStarter.startVoiceTurn,
2781
- frontDecider: decider,
2782
- // Long extension so the pending replay cannot race the manual release.
2783
- frontModelConfig: { endpointExtensionMs: 5_000 },
2784
- });
2785
-
2786
- await startWithPartial(session, transcribers);
2787
- await session.handleBinaryAudio(LOUD_CHUNK);
2788
- await waitFor(() => calls.length === 1);
2789
- expect(countType(frames, "utterance_end")).toBe(0);
2790
-
2791
- // The detector's turn already ended (the hold suppressed its boundary),
2792
- // so this release takes the no-active-detector path — the client must
2793
- // still receive the utterance_end frame it uses to leave `listening`.
2794
- await session.handleClientFrame({ type: "ptt_release" });
2795
- await waitFor(() => frames.some((frame) => frame.type === "tts_done"));
2796
- expect(countType(frames, "utterance_end")).toBe(1);
2797
- // Manual release never re-consults the decider.
2798
- expect(calls).toHaveLength(1);
2799
- expect(turnStarter.calls[0]).toMatchObject({ content: "hello world" });
2800
- });
2801
-
2802
- test("speech resuming during a hold cancels the replay and the utterance keeps accumulating", async () => {
2803
- const { decider, calls } = makeFrontDecider(["hold", "release"]);
2804
- const turnStarter = makeAutoCompletingTurnStarter(["Okay."]);
2805
- const { frames, session, transcribers } = createHarness({
2806
- finals: ["hello there world"],
2807
- startVoiceTurn: turnStarter.startVoiceTurn,
2808
- frontDecider: decider,
2809
- // Comfortably longer than the resumed speech's own silence boundary,
2810
- // so a leaked (uncancelled) replay would surface as a third decider
2811
- // consult / second utterance_end below.
2812
- frontModelConfig: { endpointExtensionMs: 200 },
2813
- });
2814
-
2815
- await startWithPartial(session, transcribers, "hello");
2816
- await session.handleBinaryAudio(LOUD_CHUNK);
2817
- await waitFor(() => calls.length === 1);
2818
- expect(countType(frames, "utterance_end")).toBe(0);
2819
-
2820
- // Speech resumes during the hold; the fresh silence boundary after it —
2821
- // not the cancelled replay timer — drives the next decision.
2822
- await session.handleBinaryAudio(LOUD_CHUNK);
2823
- await waitFor(() => calls.length === 2);
2824
- expect(calls[1]).toMatchObject({ extensionCount: 1 });
2825
- await waitFor(() => frames.some((frame) => frame.type === "tts_done"));
2826
-
2827
- // Wait out the original extension window: the cancelled replay never
2828
- // re-fires the boundary.
2829
- await new Promise((resolve) => setTimeout(resolve, 250));
2830
- expect(calls).toHaveLength(2);
2831
- expect(countType(frames, "utterance_end")).toBe(1);
2832
- // Both speech bursts fed the same utterance's transcriber (the second
2833
- // resolves only when the completed turn re-arms), producing one released
2834
- // turn end-to-end.
2835
- expect(transcribers[0]?.received.length).toBeGreaterThanOrEqual(2);
2836
- expect(turnStarter.calls).toHaveLength(1);
2837
- expect(turnStarter.calls[0]).toMatchObject({
2838
- content: "hello there world",
2839
- });
2840
- });
2841
-
2842
- test("a release decision produces the same frame sequence as the flag-off path", async () => {
2843
- const { decider, calls } = makeFrontDecider(["release"]);
2844
- const turnStarter = makeAutoCompletingTurnStarter(["Hi there."]);
2845
- const { frames, session, transcribers } = createHarness({
2846
- finals: ["hello world"],
2847
- startVoiceTurn: turnStarter.startVoiceTurn,
2848
- frontDecider: decider,
2849
- });
2850
-
2851
- await startWithPartial(session, transcribers);
2852
- await session.handleBinaryAudio(LOUD_CHUNK);
2853
- await waitFor(() => frames.some((frame) => frame.type === "tts_done"));
2854
-
2855
- expect(calls).toHaveLength(1);
2856
- expect(frameTypes(frames)).toEqual([
2857
- "ready",
2858
- "stt_partial",
2859
- "speech_started",
2860
- "utterance_end",
2861
- "stt_final",
2862
- "thinking",
2863
- "assistant_text_delta",
2864
- "tts_done",
2865
- ]);
2866
- expect(
2867
- frames.find((frame) => frame.type === "utterance_end"),
2868
- ).toMatchObject({ reason: "silence" });
2869
- });
2870
-
2871
- test("a rejecting decider still releases the boundary with the normal frame sequence", async () => {
2872
- // The decider contract never rejects, but a buggy injected stub (or a
2873
- // future regression) must not silently drop the silence boundary: the
2874
- // session's belt catches the rejection and releases.
2875
- const calls: VoiceEndpointDecisionInput[] = [];
2876
- const decider: VoiceFrontDecider = {
2877
- decideEndpoint: async (input) => {
2878
- calls.push(input);
2879
- throw new Error("stub decider boom");
2880
- },
2881
- generateAckText: async () => null,
2882
- generateProgressText: async () => null,
2883
- };
2884
- const turnStarter = makeAutoCompletingTurnStarter(["Hi there."]);
2885
- const { frames, session, transcribers } = createHarness({
2886
- finals: ["hello world"],
2887
- startVoiceTurn: turnStarter.startVoiceTurn,
2888
- frontDecider: decider,
2889
- });
2890
-
2891
- await startWithPartial(session, transcribers);
2892
- await session.handleBinaryAudio(LOUD_CHUNK);
2893
- await waitFor(() => frames.some((frame) => frame.type === "tts_done"));
2894
-
2895
- expect(calls).toHaveLength(1);
2896
- expect(frameTypes(frames)).toEqual([
2897
- "ready",
2898
- "stt_partial",
2899
- "speech_started",
2900
- "utterance_end",
2901
- "stt_final",
2902
- "thinking",
2903
- "assistant_text_delta",
2904
- "tts_done",
2905
- ]);
2906
- expect(
2907
- frames.find((frame) => frame.type === "utterance_end"),
2908
- ).toMatchObject({ reason: "silence" });
2909
- expect(turnStarter.calls[0]).toMatchObject({ content: "hello world" });
2910
- });
2911
-
2912
- test("a slow decider adds only its own bounded delay and then releases normally", async () => {
2913
- // Resolves "release" only after a delay comfortably longer than every
2914
- // other timer in the flow — the boundary must wait it out and then run
2915
- // the unchanged release sequence.
2916
- const calls: VoiceEndpointDecisionInput[] = [];
2917
- const decider: VoiceFrontDecider = {
2918
- decideEndpoint: async (input) => {
2919
- calls.push(input);
2920
- await new Promise((resolve) => setTimeout(resolve, 150));
2921
- return { action: "release" };
2922
- },
2923
- generateAckText: async () => null,
2924
- generateProgressText: async () => null,
2925
- };
2926
- const turnStarter = makeAutoCompletingTurnStarter(["Hi there."]);
2927
- const { frames, session, transcribers } = createHarness({
2928
- finals: ["hello world"],
2929
- startVoiceTurn: turnStarter.startVoiceTurn,
2930
- frontDecider: decider,
2931
- });
2932
-
2933
- await startWithPartial(session, transcribers);
2934
- await session.handleBinaryAudio(LOUD_CHUNK);
2935
-
2936
- // While the decision is pending, the boundary stays unreleased.
2937
- await waitFor(() => calls.length === 1);
2938
- expect(countType(frames, "utterance_end")).toBe(0);
2939
- expect(countType(frames, "thinking")).toBe(0);
2940
-
2941
- await waitFor(() => frames.some((frame) => frame.type === "tts_done"));
2942
- expect(calls).toHaveLength(1);
2943
- expect(frameTypes(frames)).toEqual([
2944
- "ready",
2945
- "stt_partial",
2946
- "speech_started",
2947
- "utterance_end",
2948
- "stt_final",
2949
- "thinking",
2950
- "assistant_text_delta",
2951
- "tts_done",
2952
- ]);
2953
- expect(
2954
- frames.find((frame) => frame.type === "utterance_end"),
2955
- ).toMatchObject({ reason: "silence" });
2956
- expect(turnStarter.calls[0]).toMatchObject({ content: "hello world" });
2957
- });
2958
-
2959
- test("endpointMaxExtensions caps consecutive holds and forces the release", async () => {
2960
- const { decider, calls } = makeFrontDecider(["hold", "hold", "hold"]);
2961
- const turnStarter = makeAutoCompletingTurnStarter(["Hi there."]);
2962
- const { frames, session, transcribers } = createHarness({
2963
- finals: ["hello world"],
2964
- startVoiceTurn: turnStarter.startVoiceTurn,
2965
- frontDecider: decider,
2966
- frontModelConfig: { endpointExtensionMs: 20, endpointMaxExtensions: 1 },
2967
- });
2968
-
2969
- await startWithPartial(session, transcribers);
2970
- await session.handleBinaryAudio(LOUD_CHUNK);
2971
- await waitFor(() => frames.some((frame) => frame.type === "tts_done"));
2972
-
2973
- // The single allowed hold consumed the decider's one consult; the replay
2974
- // hit the cap and released without asking again.
2975
- expect(calls).toHaveLength(1);
2976
- expect(countType(frames, "utterance_end")).toBe(1);
2977
- expect(turnStarter.calls).toHaveLength(1);
2978
- expect(turnStarter.calls[0]).toMatchObject({ content: "hello world" });
2979
- });
2980
-
2981
- test("a max-duration boundary bypasses the decider entirely", async () => {
2982
- const { decider, calls } = makeFrontDecider(["hold"]);
2983
- const turnStarter = makeAutoCompletingTurnStarter(["Hi there."]);
2984
- const { frames, session, transcribers } = createHarness({
2985
- finals: ["hello world"],
2986
- startVoiceTurn: turnStarter.startVoiceTurn,
2987
- frontDecider: decider,
2988
- // Silence can never fire; the max-duration cap ends the turn instead.
2989
- turnDetectorConfig: { silenceThresholdMs: 10_000, maxTurnDurationMs: 40 },
2990
- });
2991
-
2992
- await startWithPartial(session, transcribers);
2993
- await session.handleBinaryAudio(LOUD_CHUNK);
2994
- await waitFor(() => frames.some((frame) => frame.type === "tts_done"));
2995
-
2996
- expect(calls).toHaveLength(0);
2997
- expect(
2998
- frames.find((frame) => frame.type === "utterance_end"),
2999
- ).toMatchObject({ reason: "max-duration" });
3000
- expect(turnStarter.calls).toHaveLength(1);
3001
- });
3002
-
3003
- test("a ptt_release forced boundary bypasses the decider and releases immediately", async () => {
3004
- // A hold-happy decider: if the manual release were routed through it,
3005
- // the boundary would be deferred and the frame sequence below would gain
3006
- // an extension delay (or stall entirely).
3007
- const { decider, calls } = makeFrontDecider(["hold", "hold"]);
3008
- const turnStarter = makeAutoCompletingTurnStarter(["Hi there."]);
3009
- const { frames, session, transcribers } = createHarness({
3010
- finals: ["hello world"],
3011
- startVoiceTurn: turnStarter.startVoiceTurn,
3012
- frontDecider: decider,
3013
- // A genuine silence boundary can never fire during the test; only the
3014
- // client's explicit release ends the turn.
3015
- turnDetectorConfig: { silenceThresholdMs: 10_000 },
3016
- });
3017
-
3018
- await startWithPartial(session, transcribers);
3019
- await session.handleBinaryAudio(LOUD_CHUNK);
3020
- await waitFor(() =>
3021
- frames.some((frame) => frame.type === "speech_started"),
3022
- );
3023
-
3024
- await session.handleClientFrame({ type: "ptt_release" });
3025
- await waitFor(() => frames.some((frame) => frame.type === "tts_done"));
3026
-
3027
- // The explicit release was never second-guessed: no decider consult, and
3028
- // the boundary produced the normal utterance_end → turn-launch sequence.
3029
- expect(calls).toHaveLength(0);
3030
- expect(frameTypes(frames)).toEqual([
3031
- "ready",
3032
- "stt_partial",
3033
- "speech_started",
3034
- "utterance_end",
3035
- "stt_final",
3036
- "thinking",
3037
- "assistant_text_delta",
3038
- "tts_done",
3039
- ]);
3040
- expect(
3041
- frames.find((frame) => frame.type === "utterance_end"),
3042
- ).toMatchObject({ reason: "silence" });
3043
- expect(turnStarter.calls).toHaveLength(1);
3044
- expect(turnStarter.calls[0]).toMatchObject({ content: "hello world" });
3045
- });
3046
-
3047
- test("session close during a hold clears the extension timer", async () => {
3048
- const { decider, calls } = makeFrontDecider(["hold", "hold"]);
3049
- const { frames, session, transcribers } = createHarness({
3050
- finals: ["hello world"],
3051
- frontDecider: decider,
3052
- frontModelConfig: { endpointExtensionMs: 30 },
3053
- });
3054
-
3055
- await startWithPartial(session, transcribers);
3056
- await session.handleBinaryAudio(LOUD_CHUNK);
3057
- await waitFor(() => calls.length === 1);
3058
- expect(countType(frames, "utterance_end")).toBe(0);
3059
-
3060
- await session.close("client_end");
3061
- const framesAtClose = frames.length;
3062
-
3063
- // Well past the extension window: the cleared timer never replays the
3064
- // boundary — no utterance_end, no second decider consult, no new frames.
3065
- await new Promise((resolve) => setTimeout(resolve, 100));
3066
- expect(calls).toHaveLength(1);
3067
- expect(countType(frames, "utterance_end")).toBe(0);
3068
- expect(frames.length).toBe(framesAtClose);
3069
- });
3070
- });
3071
-
3072
2898
  describe("LiveVoiceSession unified front-door endpointing", () => {
3073
- const enableUnifiedFlags = () =>
3074
- setCachedOverrides(
3075
- {
3076
- "voice-mode": true,
3077
- },
3078
- { fromGateway: true },
3079
- );
3080
-
3081
- afterEach(() => clearCachedOverrides());
3082
-
3083
2899
  function spokenDeltaText(frames: LiveVoiceServerFrame[]): string {
3084
2900
  return frames
3085
2901
  .filter((frame) => frame.type === "assistant_text_delta")
@@ -3114,23 +2930,12 @@ describe("LiveVoiceSession unified front-door endpointing", () => {
3114
2930
  return { startVoiceTurn, calls, discard };
3115
2931
  }
3116
2932
 
3117
- // A decider that must never be consulted once the unified flag owns
3118
- // endpointing.
3119
- function makeUntouchableDecider(): {
3120
- decider: VoiceFrontDecider;
3121
- endpointCalls: () => number;
3122
- } {
3123
- let count = 0;
2933
+ // A decider that phrases nothing: the front-door leg IS the endpoint
2934
+ // decision, so these tests only need the decider seam to exist.
2935
+ function makeSilentDecider(): VoiceFrontDecider {
3124
2936
  return {
3125
- decider: {
3126
- decideEndpoint: async () => {
3127
- count += 1;
3128
- return { action: "release" as const };
3129
- },
3130
- generateAckText: async () => null,
3131
- generateProgressText: async () => null,
3132
- },
3133
- endpointCalls: () => count,
2937
+ generateAckText: async () => null,
2938
+ generateProgressText: async () => null,
3134
2939
  };
3135
2940
  }
3136
2941
 
@@ -3146,8 +2951,7 @@ describe("LiveVoiceSession unified front-door endpointing", () => {
3146
2951
  }
3147
2952
 
3148
2953
  test("a chatty answer commits: verdict leg replaces the decider, frames follow commit order", async () => {
3149
- enableUnifiedFlags();
3150
- const { decider, endpointCalls } = makeUntouchableDecider();
2954
+ const decider = makeSilentDecider();
3151
2955
  const starter = makeVerdictTurnStarter([["Hey! Not much."]]);
3152
2956
  const { frames, session, transcribers } = createHarness({
3153
2957
  finals: ["hello world"],
@@ -3159,8 +2963,7 @@ describe("LiveVoiceSession unified front-door endpointing", () => {
3159
2963
  await session.handleBinaryAudio(LOUD_CHUNK);
3160
2964
  await waitFor(() => frames.some((frame) => frame.type === "tts_done"));
3161
2965
 
3162
- // The leg IS the endpoint decision: the decider was never consulted.
3163
- expect(endpointCalls()).toBe(0);
2966
+ // The leg IS the endpoint decision.
3164
2967
  expect(starter.calls).toHaveLength(1);
3165
2968
  // Dispatched speculatively on the pre-finalize transcript, as the
3166
2969
  // front-door leg with the verdict rule requested.
@@ -3182,7 +2985,6 @@ describe("LiveVoiceSession unified front-door endpointing", () => {
3182
2985
  });
3183
2986
 
3184
2987
  test("a hold verdict discards the leg silently and the extension replays the boundary", async () => {
3185
- enableUnifiedFlags();
3186
2988
  const starter = makeVerdictTurnStarter([["[0]"], ["Sure thing."]]);
3187
2989
  const { frames, session, transcribers } = createHarness({
3188
2990
  finals: ["hello world"],
@@ -3215,7 +3017,6 @@ describe("LiveVoiceSession unified front-door endpointing", () => {
3215
3017
  });
3216
3018
 
3217
3019
  test("a verdict that misses the deadline commits the turn (fail-open)", async () => {
3218
- enableUnifiedFlags();
3219
3020
  // The leg never produces a verdict — a provider TTFT tail. The deadline
3220
3021
  // must commit the turn so the caller gets the thinking frame (and the
3221
3022
  // ack timer arms) instead of unbounded structural silence.
@@ -3240,7 +3041,6 @@ describe("LiveVoiceSession unified front-door endpointing", () => {
3240
3041
  });
3241
3042
 
3242
3043
  test("a final that extends the held transcript replays the boundary immediately", async () => {
3243
- enableUnifiedFlags();
3244
3044
  const starter = makeVerdictTurnStarter([["[0]"], ["Got it."]]);
3245
3045
  const { frames, session, transcribers } = createHarness({
3246
3046
  finals: ["hello world how are you"],
@@ -3265,7 +3065,6 @@ describe("LiveVoiceSession unified front-door endpointing", () => {
3265
3065
  });
3266
3066
 
3267
3067
  test("speech resuming mid-verdict discards the leg and the utterance keeps accumulating", async () => {
3268
- enableUnifiedFlags();
3269
3068
  // First leg never produces a verdict (in flight); second answers.
3270
3069
  const starter = makeVerdictTurnStarter([[], ["Got it all."]]);
3271
3070
  const { frames, session, transcribers } = createHarness({
@@ -3294,7 +3093,6 @@ describe("LiveVoiceSession unified front-door endpointing", () => {
3294
3093
  });
3295
3094
 
3296
3095
  test("a discard that beats the bridge handle still rolls back the user row", async () => {
3297
- enableUnifiedFlags();
3298
3096
  const discard = mock(async () => {});
3299
3097
  const abort = mock();
3300
3098
  let resolveHandle: (() => void) | null = null;
@@ -3336,7 +3134,6 @@ describe("LiveVoiceSession unified front-door endpointing", () => {
3336
3134
  });
3337
3135
 
3338
3136
  test("a manual release during the verdict window commits the leg instead of discarding it", async () => {
3339
- enableUnifiedFlags();
3340
3137
  const discard = mock(async () => {});
3341
3138
  let callbacks: VoiceTurnCallbacks | undefined;
3342
3139
  const calls: VoiceTurnOptions[] = [];
@@ -3376,7 +3173,6 @@ describe("LiveVoiceSession unified front-door endpointing", () => {
3376
3173
  });
3377
3174
 
3378
3175
  test("a hold verdict after a manual release relaunches a fresh leg on the released utterance", async () => {
3379
- enableUnifiedFlags();
3380
3176
  const discard = mock(async () => {});
3381
3177
  let firstCallbacks: VoiceTurnCallbacks | undefined;
3382
3178
  const calls: VoiceTurnOptions[] = [];