@vellumai/assistant 0.10.7 → 0.10.8-dev.202607102228.5945895

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 (1558) hide show
  1. package/AGENTS.md +3 -1
  2. package/ARCHITECTURE.md +9 -9
  3. package/README.md +1 -1
  4. package/docs/architecture/memory.md +1 -3
  5. package/docs/credential-execution-service.md +9 -96
  6. package/docs/runbook-trusted-contacts.md +132 -93
  7. package/docs/trusted-contact-access.md +125 -115
  8. package/docs/workspace-tools.md +4 -2
  9. package/eslint-rules/__tests__/cli-no-daemon-internals.test.ts +62 -360
  10. package/eslint-rules/cli-no-daemon-internals.js +91 -269
  11. package/eslint.config.mjs +4 -4
  12. package/knip.json +2 -1
  13. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/package.json +1 -2
  14. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/attachment-naming.test.ts +104 -0
  15. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/contracts.test.ts +0 -2
  16. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/attachment-naming.ts +118 -0
  17. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/credential-rpc.ts +3 -5
  18. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/index.ts +2 -4
  19. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/rpc.ts +4 -447
  20. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json +1 -2
  21. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/attachment-naming.test.ts +104 -0
  22. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/contracts.test.ts +0 -2
  23. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/attachment-naming.ts +118 -0
  24. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/credential-rpc.ts +3 -5
  25. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/index.ts +2 -4
  26. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/rpc.ts +4 -447
  27. package/node_modules/@vellumai/gateway-client/src/__tests__/invite-contract.test.ts +0 -18
  28. package/node_modules/@vellumai/gateway-client/src/__tests__/trust-verdict-contract.test.ts +40 -6
  29. package/node_modules/@vellumai/gateway-client/src/channel-permission-contract.ts +13 -0
  30. package/node_modules/@vellumai/gateway-client/src/index.ts +24 -2
  31. package/node_modules/@vellumai/gateway-client/src/invite-contract.ts +146 -16
  32. package/node_modules/@vellumai/gateway-client/src/trust-verdict-contract.ts +22 -4
  33. package/node_modules/@vellumai/service-contracts/package.json +1 -2
  34. package/node_modules/@vellumai/service-contracts/src/__tests__/attachment-naming.test.ts +104 -0
  35. package/node_modules/@vellumai/service-contracts/src/__tests__/contracts.test.ts +0 -2
  36. package/node_modules/@vellumai/service-contracts/src/attachment-naming.ts +118 -0
  37. package/node_modules/@vellumai/service-contracts/src/credential-rpc.ts +3 -5
  38. package/node_modules/@vellumai/service-contracts/src/index.ts +2 -4
  39. package/node_modules/@vellumai/service-contracts/src/rpc.ts +4 -447
  40. package/openapi.yaml +1648 -676
  41. package/package.json +4 -5
  42. package/scripts/build-test-fixtures.ts +105 -0
  43. package/scripts/memory-inspect.ts +2 -8
  44. package/scripts/test.ts +597 -0
  45. package/src/__tests__/access-request-card-view.test.ts +38 -0
  46. package/src/__tests__/activation-early-marking.test.ts +1 -8
  47. package/src/__tests__/actor-token-service.test.ts +1 -22
  48. package/src/__tests__/add-message-preassigned-id.test.ts +1 -1
  49. package/src/__tests__/agent-image-optimize.test.ts +104 -1
  50. package/src/__tests__/agent-loop-callsite-precedence.test.ts +0 -5
  51. package/src/__tests__/agent-loop-compaction-events.test.ts +1 -1
  52. package/src/__tests__/agent-loop-compaction-strip.test.ts +1 -1
  53. package/src/__tests__/agent-loop-exit-reason.test.ts +1 -1
  54. package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +0 -6
  55. package/src/__tests__/agent-loop-override-profile.test.ts +66 -5
  56. package/src/__tests__/agent-loop-pushes-post-hook-prompt.test.ts +1 -9
  57. package/src/__tests__/agent-loop-regrowth-guard.test.ts +1 -1
  58. package/src/__tests__/agent-loop-set-system-prompt.test.ts +1 -7
  59. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +1 -0
  60. package/src/__tests__/agent-wake-override-profile.test.ts +21 -1
  61. package/src/__tests__/annotate-activity-metadata.test.ts +4 -10
  62. package/src/__tests__/annotate-risk-options.test.ts +4 -10
  63. package/src/__tests__/anthropic-error-formatting.test.ts +193 -2
  64. package/src/__tests__/anthropic-gateway-shared.test.ts +140 -0
  65. package/src/__tests__/anthropic-provider.test.ts +58 -0
  66. package/src/__tests__/app-bundler.test.ts +0 -8
  67. package/src/__tests__/app-conversation-ids-backfill.test.ts +1 -8
  68. package/src/__tests__/app-routes-csp.test.ts +0 -8
  69. package/src/__tests__/approval-cascade.test.ts +0 -21
  70. package/src/__tests__/approval-interception-trust-gates.test.ts +0 -7
  71. package/src/__tests__/approval-primitive.test.ts +1 -9
  72. package/src/__tests__/approval-routes-http.test.ts +1 -16
  73. package/src/__tests__/assistant-attachment-directive.test.ts +1 -0
  74. package/src/__tests__/assistant-attachments.test.ts +211 -0
  75. package/src/__tests__/assistant-event-hub-machine-name.test.ts +0 -7
  76. package/src/__tests__/assistant-events-sse-hardening.test.ts +0 -7
  77. package/src/__tests__/assistant-feature-flag-guard.test.ts +3 -3
  78. package/src/__tests__/assistant-feature-flag-guardrails.test.ts +1 -1
  79. package/src/__tests__/attachment-stored-path-annotation.test.ts +0 -7
  80. package/src/__tests__/attachment-upload-trusted-source.test.ts +0 -7
  81. package/src/__tests__/attachments-store-heic-normalize.test.ts +0 -7
  82. package/src/__tests__/attachments-store.test.ts +43 -8
  83. package/src/__tests__/attachments.test.ts +77 -0
  84. package/src/__tests__/audit-log-rotation.test.ts +1 -9
  85. package/src/__tests__/auth-fallback-events-store.test.ts +0 -7
  86. package/src/__tests__/available-skills.test.ts +266 -0
  87. package/src/__tests__/avatar-generator.test.ts +0 -9
  88. package/src/__tests__/background-shell-host-bash.test.ts +0 -7
  89. package/src/__tests__/background-workers-disk-pressure.test.ts +4 -3
  90. package/src/__tests__/browser-fill-credential.test.ts +0 -7
  91. package/src/__tests__/browser-manager.test.ts +1 -8
  92. package/src/__tests__/btw-routes.test.ts +0 -7
  93. package/src/__tests__/build-persisted-content.test.ts +0 -8
  94. package/src/__tests__/call-constants.test.ts +1 -1
  95. package/src/__tests__/call-controller.test.ts +783 -34
  96. package/src/__tests__/call-conversation-messages.test.ts +2 -9
  97. package/src/__tests__/call-domain.test.ts +7 -24
  98. package/src/__tests__/call-pointer-message-composer.test.ts +0 -8
  99. package/src/__tests__/call-pointer-messages.test.ts +1 -8
  100. package/src/__tests__/call-recovery.test.ts +1 -8
  101. package/src/__tests__/call-routes-http.test.ts +0 -7
  102. package/src/__tests__/call-setup-flow-invite.test.ts +1 -1
  103. package/src/__tests__/call-setup-flow-midcall-trust.test.ts +76 -97
  104. package/src/__tests__/call-setup-flow-name-capture.test.ts +1 -1
  105. package/src/__tests__/call-setup-flow-verification.test.ts +1 -1
  106. package/src/__tests__/call-setup-flow.test.ts +2 -2
  107. package/src/__tests__/call-site-routing-provider.test.ts +1 -5
  108. package/src/__tests__/call-state.test.ts +1 -8
  109. package/src/__tests__/call-store.test.ts +1 -8
  110. package/src/__tests__/cancel-resolves-conversation-key.test.ts +0 -7
  111. package/src/__tests__/canonical-guardian-store.test.ts +1 -8
  112. package/src/__tests__/catalog-cache.test.ts +0 -8
  113. package/src/__tests__/catalog-files.test.ts +0 -8
  114. package/src/__tests__/catalog-install-normalize.test.ts +0 -7
  115. package/src/__tests__/ces-rpc-credential-backend.test.ts +0 -7
  116. package/src/__tests__/channel-approval-routes.test.ts +0 -7
  117. package/src/__tests__/channel-approvals.test.ts +0 -7
  118. package/src/__tests__/channel-delivery-store.test.ts +0 -7
  119. package/src/__tests__/channel-guardian.test.ts +29 -16
  120. package/src/__tests__/channel-inbound-disk-pressure.test.ts +0 -7
  121. package/src/__tests__/channel-reply-delivery.test.ts +18 -5
  122. package/src/__tests__/channel-retry-sweep.test.ts +0 -7
  123. package/src/__tests__/clawhub-files.test.ts +0 -8
  124. package/src/__tests__/clawhub.test.ts +0 -7
  125. package/src/__tests__/clear-all-lexical.test.ts +0 -6
  126. package/src/__tests__/client-os-metadata-persistence.test.ts +0 -5
  127. package/src/__tests__/compaction-direct.test.ts +47 -0
  128. package/src/__tests__/compaction-events.test.ts +14 -29
  129. package/src/__tests__/compaction-ledger-store.test.ts +1 -7
  130. package/src/__tests__/compaction-summary-head-persisted-count.test.ts +417 -0
  131. package/src/__tests__/compaction-trail-store.test.ts +2 -8
  132. package/src/__tests__/compaction.benchmark.test.ts +0 -5
  133. package/src/__tests__/compactor-call-site-logging.test.ts +0 -21
  134. package/src/__tests__/compactor-fixed-boundary.test.ts +334 -0
  135. package/src/__tests__/compactor-low-watermark-cut.test.ts +8 -23
  136. package/src/__tests__/compactor-media-strip.test.ts +0 -21
  137. package/src/__tests__/compactor-preserved-tail-count.test.ts +0 -21
  138. package/src/__tests__/compactor-summary-call-truncation.test.ts +0 -21
  139. package/src/__tests__/compactor-web-search-strip.test.ts +21 -25
  140. package/src/__tests__/config-get-vision-flag.test.ts +0 -6
  141. package/src/__tests__/config-loader-backfill.test.ts +332 -316
  142. package/src/__tests__/config-loader-platform-defaults.test.ts +0 -21
  143. package/src/__tests__/config-loader-validation-reset-notice.test.ts +183 -0
  144. package/src/__tests__/config-schema.test.ts +85 -30
  145. package/src/__tests__/config-set-route.test.ts +0 -6
  146. package/src/__tests__/config-watcher-cleanup-throttle.test.ts +12 -24
  147. package/src/__tests__/config-watcher-skill-reseed.test.ts +1 -8
  148. package/src/__tests__/config-watcher.test.ts +22 -12
  149. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +1 -9
  150. package/src/__tests__/contact-store-interaction-info.test.ts +1 -8
  151. package/src/__tests__/contact-store-user-file.test.ts +1 -14
  152. package/src/__tests__/contacts-tools.test.ts +0 -7
  153. package/src/__tests__/contacts-write.test.ts +1 -8
  154. package/src/__tests__/context-search-memory-source.test.ts +0 -1
  155. package/src/__tests__/context-search-memory-v2-source.test.ts +0 -6
  156. package/src/__tests__/context-window-manager-compact-retry.test.ts +118 -21
  157. package/src/__tests__/context-window-manager-overflow-rung.test.ts +0 -21
  158. package/src/__tests__/conversation-abort-tool-results.test.ts +0 -5
  159. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +0 -12
  160. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +5 -12
  161. package/src/__tests__/conversation-agent-loop-overflow.test.ts +45 -21
  162. package/src/__tests__/conversation-agent-loop.test.ts +192 -70
  163. package/src/__tests__/conversation-app-control-instantiation.test.ts +20 -8
  164. package/src/__tests__/conversation-app-control-lifecycle.test.ts +0 -9
  165. package/src/__tests__/conversation-attachments.test.ts +1 -7
  166. package/src/__tests__/conversation-attention-store.test.ts +1 -9
  167. package/src/__tests__/conversation-attention-telegram.test.ts +0 -8
  168. package/src/__tests__/conversation-clear-safety.test.ts +0 -7
  169. package/src/__tests__/conversation-confirmation-signals.test.ts +0 -21
  170. package/src/__tests__/conversation-crud-enabled-plugins.test.ts +1 -7
  171. package/src/__tests__/conversation-crud-inference-profile.test.ts +1 -7
  172. package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +0 -7
  173. package/src/__tests__/conversation-disk-view-integration.test.ts +0 -7
  174. package/src/__tests__/conversation-disk-view.test.ts +0 -7
  175. package/src/__tests__/conversation-enabledplugins-route.test.ts +2 -8
  176. package/src/__tests__/conversation-error.test.ts +299 -3
  177. package/src/__tests__/conversation-fork-crud.test.ts +52 -37
  178. package/src/__tests__/conversation-fork-retrospective.test.ts +486 -7
  179. package/src/__tests__/conversation-fork-route.test.ts +0 -7
  180. package/src/__tests__/conversation-history-web-search.test.ts +39 -17
  181. package/src/__tests__/conversation-inference-profile-list.test.ts +0 -6
  182. package/src/__tests__/conversation-inference-profile-route.test.ts +0 -5
  183. package/src/__tests__/conversation-init.benchmark.test.ts +6 -2
  184. package/src/__tests__/conversation-initial-prompt.test.ts +1 -1
  185. package/src/__tests__/conversation-key-store-disk-view.test.ts +0 -7
  186. package/src/__tests__/conversation-lifecycle.test.ts +108 -62
  187. package/src/__tests__/conversation-list-source.test.ts +0 -7
  188. package/src/__tests__/conversation-load-history-repair.test.ts +39 -53
  189. package/src/__tests__/conversation-load-history-stripped.test.ts +12 -19
  190. package/src/__tests__/conversation-loop-db-lock-resilience.test.ts +10 -15
  191. package/src/__tests__/conversation-message-id-uuidv7.test.ts +73 -0
  192. package/src/__tests__/conversation-pairing.test.ts +0 -7
  193. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +0 -5
  194. package/src/__tests__/conversation-process-callsite.test.ts +14 -7
  195. package/src/__tests__/conversation-provider-retry-repair.test.ts +0 -5
  196. package/src/__tests__/conversation-queue.test.ts +91 -21
  197. package/src/__tests__/conversation-routes-disk-view.test.ts +0 -11
  198. package/src/__tests__/conversation-routes-enabled-plugins.test.ts +3 -11
  199. package/src/__tests__/conversation-routes-guardian-reply.test.ts +3 -11
  200. package/src/__tests__/conversation-routes-hidden-queue.test.ts +3 -11
  201. package/src/__tests__/conversation-routes-slash-commands.test.ts +24 -13
  202. package/src/__tests__/conversation-runtime-assembly.test.ts +37 -9
  203. package/src/__tests__/conversation-skill-tools.test.ts +31 -15
  204. package/src/__tests__/conversation-slash-commands.test.ts +5 -4
  205. package/src/__tests__/conversation-slash-queue.test.ts +0 -5
  206. package/src/__tests__/conversation-slash-unknown.test.ts +0 -5
  207. package/src/__tests__/conversation-speed-override.test.ts +13 -23
  208. package/src/__tests__/conversation-starter-routes.test.ts +0 -7
  209. package/src/__tests__/conversation-store.test.ts +9 -22
  210. package/src/__tests__/conversation-summarize-route.test.ts +445 -0
  211. package/src/__tests__/conversation-summarize-up-to.test.ts +711 -0
  212. package/src/__tests__/conversation-surfaces-activation-emit.test.ts +0 -8
  213. package/src/__tests__/conversation-surfaces-data-persist.test.ts +4 -2
  214. package/src/__tests__/conversation-title-service.test.ts +0 -7
  215. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +0 -19
  216. package/src/__tests__/conversation-tool-setup-attribution.test.ts +4 -8
  217. package/src/__tests__/conversation-unread-route.test.ts +0 -7
  218. package/src/__tests__/conversation-usage.test.ts +10 -8
  219. package/src/__tests__/conversation-wait-for-idle.test.ts +391 -0
  220. package/src/__tests__/conversation-workspace-cache-state.test.ts +0 -5
  221. package/src/__tests__/conversation-workspace-injection.test.ts +0 -5
  222. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +0 -5
  223. package/src/__tests__/conversations-import-system-filter.test.ts +0 -7
  224. package/src/__tests__/credential-broker-browser-fill.test.ts +0 -9
  225. package/src/__tests__/credential-broker-server-use.test.ts +0 -9
  226. package/src/__tests__/credential-execution-api-key-propagation.test.ts +0 -40
  227. package/src/__tests__/credential-execution-client.test.ts +3 -38
  228. package/src/__tests__/credential-execution-managed-contract.test.ts +10 -106
  229. package/src/__tests__/credential-health-service.test.ts +0 -9
  230. package/src/__tests__/credential-metadata-store.test.ts +0 -8
  231. package/src/__tests__/credential-prompt-route.test.ts +80 -9
  232. package/src/__tests__/credential-request-route.test.ts +76 -0
  233. package/src/__tests__/credential-resolve.test.ts +0 -8
  234. package/src/__tests__/credential-security-invariants.test.ts +3 -10
  235. package/src/__tests__/credential-token-resolver.test.ts +0 -9
  236. package/src/__tests__/custom-profile-ensure.test.ts +232 -0
  237. package/src/__tests__/daemon-assistant-events.test.ts +1 -9
  238. package/src/__tests__/daemon-credential-client.test.ts +0 -7
  239. package/src/__tests__/db-conversation-fork-lineage-migration.test.ts +1 -8
  240. package/src/__tests__/db-conversation-inference-profile-migration.test.ts +1 -7
  241. package/src/__tests__/db-llm-request-log-provider-migration.test.ts +0 -8
  242. package/src/__tests__/db-processing-resume-attempts-migration.test.ts +116 -0
  243. package/src/__tests__/db-readiness-http-gate.test.ts +0 -7
  244. package/src/__tests__/db-rename-inference-profile-snake-case-migration.test.ts +1 -7
  245. package/src/__tests__/default-provider-ensure.test.ts +343 -0
  246. package/src/__tests__/delete-managed-skill-tool.test.ts +0 -7
  247. package/src/__tests__/delete-propagation.test.ts +0 -7
  248. package/src/__tests__/deterministic-verification-control-plane.test.ts +0 -7
  249. package/src/__tests__/device-id.test.ts +1 -9
  250. package/src/__tests__/disk-pressure-tools.test.ts +0 -4
  251. package/src/__tests__/dm-backfill.test.ts +0 -7
  252. package/src/__tests__/dm-persistence.test.ts +0 -5
  253. package/src/__tests__/docker-signing-key-bootstrap.test.ts +1 -8
  254. package/src/__tests__/drain-requeue-on-contention.test.ts +202 -0
  255. package/src/__tests__/dynamic-skill-background-guard.test.ts +174 -0
  256. package/src/__tests__/edit-propagation.test.ts +1 -8
  257. package/src/__tests__/embedding-managed-proxy-selection.test.ts +0 -8
  258. package/src/__tests__/emit-signal-routing-intent.test.ts +175 -7
  259. package/src/__tests__/encrypted-store.test.ts +0 -9
  260. package/src/__tests__/ensure-conversation-exists.test.ts +85 -0
  261. package/src/__tests__/events-client-registration.test.ts +0 -7
  262. package/src/__tests__/events-dev-bypass-actor.test.ts +2 -9
  263. package/src/__tests__/events-tail-route.test.ts +172 -0
  264. package/src/__tests__/file-write-tool.test.ts +9 -13
  265. package/src/__tests__/followup-tools.test.ts +0 -7
  266. package/src/__tests__/gateway-flag-listener.test.ts +2 -23
  267. package/src/__tests__/gemini-inline-media.test.ts +2 -2
  268. package/src/__tests__/gemini-provider.test.ts +140 -1
  269. package/src/__tests__/get-skill-detail-audit.test.ts +89 -1
  270. package/src/__tests__/graph-extraction-event-date.test.ts +0 -9
  271. package/src/__tests__/guardian-action-sweep.test.ts +0 -7
  272. package/src/__tests__/guardian-binding-drift-heal.test.ts +0 -7
  273. package/src/__tests__/guardian-card-withdrawal.test.ts +0 -8
  274. package/src/__tests__/guardian-decision-primitive-canonical.test.ts +1 -9
  275. package/src/__tests__/guardian-dispatch.test.ts +0 -7
  276. package/src/__tests__/guardian-expiry-notifier.test.ts +1 -9
  277. package/src/__tests__/guardian-outbound-http.test.ts +0 -7
  278. package/src/__tests__/guardian-principal-id-roundtrip.test.ts +1 -8
  279. package/src/__tests__/guardian-routing-invariants.test.ts +0 -8
  280. package/src/__tests__/guardian-routing-state.test.ts +1 -9
  281. package/src/__tests__/guardian-verification-voice-binding.test.ts +0 -7
  282. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +0 -2
  283. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +4 -5
  284. package/src/__tests__/headless-browser-interactions.test.ts +0 -7
  285. package/src/__tests__/headless-browser-mode.test.ts +0 -9
  286. package/src/__tests__/headless-browser-navigate.test.ts +91 -13
  287. package/src/__tests__/headless-browser-read-tools.test.ts +0 -7
  288. package/src/__tests__/headless-browser-snapshot.test.ts +0 -7
  289. package/src/__tests__/heartbeat-disk-pressure.test.ts +0 -9
  290. package/src/__tests__/helpers/set-config.ts +40 -0
  291. package/src/__tests__/host-bash-proxy.test.ts +3 -3
  292. package/src/__tests__/host-browser-proxy.test.ts +15 -20
  293. package/src/__tests__/host-cu-routes-targeted.test.ts +0 -1
  294. package/src/__tests__/host-shell-tool.test.ts +5 -69
  295. package/src/__tests__/http-conversation-lineage.test.ts +0 -7
  296. package/src/__tests__/http-user-message-parity.test.ts +0 -7
  297. package/src/__tests__/identity-routes.test.ts +1 -9
  298. package/src/__tests__/image-recovery-hook.test.ts +36 -0
  299. package/src/__tests__/inbound-admitted-introduction-nudge.test.ts +296 -0
  300. package/src/__tests__/inbound-invite-redemption.test.ts +0 -7
  301. package/src/__tests__/inbound-slack-persistence.test.ts +0 -5
  302. package/src/__tests__/inbound-trust-verdict.test.ts +1 -8
  303. package/src/__tests__/inference-profile-reaper.test.ts +1 -7
  304. package/src/__tests__/inference-profile-session-handler.test.ts +0 -6
  305. package/src/__tests__/inference-profile-session-ipc.test.ts +0 -6
  306. package/src/__tests__/ingress-url-consistency.test.ts +1 -23
  307. package/src/__tests__/injection-block.test.ts +0 -1
  308. package/src/__tests__/injector-chain.test.ts +17 -0
  309. package/src/__tests__/injector-config-validation-reset-notice.test.ts +121 -0
  310. package/src/__tests__/injector-pkb-v2-silenced.test.ts +8 -0
  311. package/src/__tests__/injector-registry-order-guard.test.ts +1 -0
  312. package/src/__tests__/inline-command-runner.test.ts +0 -7
  313. package/src/__tests__/inline-skill-load-permissions.test.ts +29 -31
  314. package/src/__tests__/internal-telemetry-routes.test.ts +72 -7
  315. package/src/__tests__/interrupted-turn-reconciler.test.ts +126 -0
  316. package/src/__tests__/introduction-card-resolver.test.ts +0 -9
  317. package/src/__tests__/invite-redeemed-ipc.test.ts +0 -7
  318. package/src/__tests__/invite-routes-http.test.ts +46 -11
  319. package/src/__tests__/job-handler-registration-guard.test.ts +1 -1
  320. package/src/__tests__/jobs-store-qdrant-breaker.test.ts +0 -7
  321. package/src/__tests__/jobs-store-upsert-debounced.test.ts +0 -7
  322. package/src/__tests__/lexical-index-dual-write.test.ts +2 -8
  323. package/src/__tests__/list-messages-attachments.test.ts +0 -7
  324. package/src/__tests__/list-messages-background-tool-completion.test.ts +0 -7
  325. package/src/__tests__/list-messages-client-message-id.test.ts +0 -7
  326. package/src/__tests__/list-messages-hidden-metadata.test.ts +0 -7
  327. package/src/__tests__/list-messages-page-latest.test.ts +0 -7
  328. package/src/__tests__/list-messages-queued.test.ts +9 -10
  329. package/src/__tests__/list-messages-tool-merge.test.ts +0 -7
  330. package/src/__tests__/llm-catalog-parity.test.ts +20 -0
  331. package/src/__tests__/llm-context-resolution.test.ts +26 -1
  332. package/src/__tests__/llm-context-route-provider.test.ts +1 -8
  333. package/src/__tests__/llm-request-log-agent-loop-exit-reason.test.ts +9 -16
  334. package/src/__tests__/llm-request-log-call-site.test.ts +5 -12
  335. package/src/__tests__/llm-request-log-clickhouse-routing.test.ts +117 -0
  336. package/src/__tests__/llm-request-log-clickhouse-writer-guards.test.ts +115 -0
  337. package/src/__tests__/llm-request-log-disabled-writes.test.ts +72 -0
  338. package/src/__tests__/llm-request-log-sink-clickhouse.test.ts +211 -0
  339. package/src/__tests__/llm-request-log-source-clickhouse.test.ts +1 -22
  340. package/src/__tests__/llm-request-log-source-factory.test.ts +0 -21
  341. package/src/__tests__/llm-request-log-turn-query.test.ts +0 -7
  342. package/src/__tests__/llm-resolver-override-or-default.test.ts +563 -0
  343. package/src/__tests__/llm-resolver.test.ts +167 -6
  344. package/src/__tests__/llm-usage-store.test.ts +1 -7
  345. package/src/__tests__/log-export-routes.test.ts +0 -7
  346. package/src/__tests__/log-export-workspace.test.ts +0 -7
  347. package/src/__tests__/managed-profile-guard.test.ts +240 -6
  348. package/src/__tests__/managed-skill-lifecycle.test.ts +0 -7
  349. package/src/__tests__/managed-store.test.ts +1 -16
  350. package/src/__tests__/mcp-auth-routes.test.ts +1 -8
  351. package/src/__tests__/mcp-config-secret-boundary.test.ts +17 -5
  352. package/src/__tests__/media-generate-image.test.ts +20 -7
  353. package/src/__tests__/media-stream-output.test.ts +755 -71
  354. package/src/__tests__/media-stream-server-integration.test.ts +290 -60
  355. package/src/__tests__/media-stream-stt-session.test.ts +26 -13
  356. package/src/__tests__/memory-config-file-parity.test.ts +187 -0
  357. package/src/__tests__/memory-identity-context-parity.test.ts +103 -0
  358. package/src/__tests__/memory-jobs-worker-backoff.test.ts +8 -7
  359. package/src/__tests__/memory-jobs-worker-cleanup-cadence.test.ts +296 -0
  360. package/src/__tests__/memory-jobs-worker-lanes.test.ts +0 -7
  361. package/src/__tests__/memory-recall-log-store.test.ts +0 -7
  362. package/src/__tests__/memory-upsert-concurrency.test.ts +0 -7
  363. package/src/__tests__/message-queue.test.ts +40 -0
  364. package/src/__tests__/messages-after-tiebreaker.test.ts +1 -8
  365. package/src/__tests__/messaging-send-tool.test.ts +0 -7
  366. package/src/__tests__/migration-cross-version-compatibility.test.ts +0 -7
  367. package/src/__tests__/migration-export-http.test.ts +10 -12
  368. package/src/__tests__/migration-export-streaming.test.ts +0 -7
  369. package/src/__tests__/migration-export-to-gcs.test.ts +60 -7
  370. package/src/__tests__/migration-import-commit-http.test.ts +11 -12
  371. package/src/__tests__/migration-import-from-gcs.test.ts +0 -7
  372. package/src/__tests__/migration-import-from-url.test.ts +0 -7
  373. package/src/__tests__/migration-import-preflight-http.test.ts +10 -11
  374. package/src/__tests__/migration-jobs-status.test.ts +8 -11
  375. package/src/__tests__/migration-validate-http.test.ts +2 -8
  376. package/src/__tests__/model-intents.test.ts +18 -0
  377. package/src/__tests__/mtime-cache.test.ts +102 -9
  378. package/src/__tests__/non-member-access-request.test.ts +145 -7
  379. package/src/__tests__/notification-broadcaster.test.ts +0 -7
  380. package/src/__tests__/notification-candidate-guardian-context.test.ts +1 -8
  381. package/src/__tests__/notification-decision-fallback.test.ts +0 -7
  382. package/src/__tests__/notification-decision-identity.test.ts +0 -7
  383. package/src/__tests__/notification-decision-recipient-context.test.ts +0 -7
  384. package/src/__tests__/notification-deep-link.test.ts +415 -11
  385. package/src/__tests__/notification-guardian-path.test.ts +0 -7
  386. package/src/__tests__/notification-platform-adapter.test.ts +0 -7
  387. package/src/__tests__/notification-routing-intent.test.ts +1 -9
  388. package/src/__tests__/notification-schedule-notify-dedup.test.ts +1 -9
  389. package/src/__tests__/notification-telegram-adapter.test.ts +0 -7
  390. package/src/__tests__/oauth-cli.test.ts +0 -1
  391. package/src/__tests__/oauth-connect-orchestrator.test.ts +0 -8
  392. package/src/__tests__/oauth-connect-routes.test.ts +19 -18
  393. package/src/__tests__/oauth-provider-profiles.test.ts +0 -7
  394. package/src/__tests__/oauth-provider-visibility.test.ts +0 -7
  395. package/src/__tests__/oauth-store.test.ts +0 -7
  396. package/src/__tests__/openai-provider.test.ts +11 -0
  397. package/src/__tests__/openrouter-provider-only.test.ts +16 -5
  398. package/src/__tests__/openrouter-token-estimation.test.ts +2 -2
  399. package/src/__tests__/outbound-slack-persistence.test.ts +9 -7
  400. package/src/__tests__/outlook-email-watcher.test.ts +0 -9
  401. package/src/__tests__/path-policy.test.ts +0 -7
  402. package/src/__tests__/pending-interactions-resolved-event.test.ts +26 -4
  403. package/src/__tests__/persist-media-references.test.ts +112 -0
  404. package/src/__tests__/persist-unsendable-image-downscale.test.ts +9 -4
  405. package/src/__tests__/persist-unsendable-image.test.ts +39 -7
  406. package/src/__tests__/persist-user-message-set-processing-failure.test.ts +41 -0
  407. package/src/__tests__/persistence-secret-redaction.test.ts +7 -7
  408. package/src/__tests__/persona-resolver.test.ts +1 -1
  409. package/src/__tests__/personal-memory-auth-bypass.test.ts +2 -4
  410. package/src/__tests__/platform-bash-auto-approve.test.ts +20 -10
  411. package/src/__tests__/platform-callback-registration.test.ts +10 -9
  412. package/src/__tests__/platform-proxy-context.test.ts +0 -8
  413. package/src/__tests__/playbook-execution.test.ts +2 -33
  414. package/src/__tests__/playbook-tools.test.ts +0 -7
  415. package/src/__tests__/plugin-api-model-profiles.test.ts +25 -4
  416. package/src/__tests__/plugin-effective-enabled-set.test.ts +29 -29
  417. package/src/__tests__/plugin-hook-pipeline-hardening.test.ts +249 -0
  418. package/src/__tests__/plugin-import-boundary-guard.test.ts +13 -25
  419. package/src/__tests__/plugin-import-boundary-reverse-guard.test.ts +316 -0
  420. package/src/__tests__/plugin-types.test.ts +1 -1
  421. package/src/__tests__/pointer-conversation-trust.test.ts +2 -4
  422. package/src/__tests__/pricing.test.ts +85 -2
  423. package/src/__tests__/process-message-background-slack.test.ts +4 -5
  424. package/src/__tests__/process-message-display-content.test.ts +102 -10
  425. package/src/__tests__/processing-flag-persist-failure.test.ts +0 -5
  426. package/src/__tests__/provider-commit-message-generator.test.ts +15 -8
  427. package/src/__tests__/provider-error-scenarios.test.ts +68 -5
  428. package/src/__tests__/provider-platform-proxy-integration.test.ts +17 -2
  429. package/src/__tests__/provider-registry-ollama.test.ts +14 -1
  430. package/src/__tests__/provider-send-message-override-profile.test.ts +13 -5
  431. package/src/__tests__/provider-streaming.benchmark.test.ts +1 -6
  432. package/src/__tests__/provider-usage-tracking.test.ts +9 -6
  433. package/src/__tests__/proxy-approval-callback.test.ts +0 -6
  434. package/src/__tests__/prune-old-conversations-job.test.ts +1 -9
  435. package/src/__tests__/prune-old-tool-invocations-job.test.ts +1 -9
  436. package/src/__tests__/public-ingress-urls.test.ts +1 -24
  437. package/src/__tests__/qdrant-collection-migration.test.ts +0 -7
  438. package/src/__tests__/qdrant-manager.test.ts +0 -8
  439. package/src/__tests__/ratelimit.test.ts +1 -8
  440. package/src/__tests__/reaction-persistence.test.ts +0 -7
  441. package/src/__tests__/rebuild-index-graph-nodes.test.ts +0 -11
  442. package/src/__tests__/registry.test.ts +39 -12
  443. package/src/__tests__/request-secret-standalone-channel-gate.test.ts +270 -0
  444. package/src/__tests__/require-fresh-approval.test.ts +47 -182
  445. package/src/__tests__/resolve-trust-class.test.ts +1 -1
  446. package/src/__tests__/retry-openrouter-only-normalization.test.ts +9 -5
  447. package/src/__tests__/retry-thinking-adaptive-only.test.ts +19 -5
  448. package/src/__tests__/retry-thinking-tool-choice.test.ts +65 -5
  449. package/src/__tests__/retry-verbosity-normalization.test.ts +9 -5
  450. package/src/__tests__/runtime-attachment-metadata.test.ts +0 -7
  451. package/src/__tests__/runtime-events-sse-bilingual.test.ts +0 -7
  452. package/src/__tests__/runtime-events-sse-parity.test.ts +0 -7
  453. package/src/__tests__/runtime-events-sse-reconnect.test.ts +0 -7
  454. package/src/__tests__/runtime-events-sse.test.ts +0 -7
  455. package/src/__tests__/scaffold-managed-skill-tool.test.ts +131 -9
  456. package/src/__tests__/schedule-retry.test.ts +0 -8
  457. package/src/__tests__/schedule-routes-workflow-validation.test.ts +0 -7
  458. package/src/__tests__/schedule-routes.test.ts +0 -7
  459. package/src/__tests__/schedule-store.test.ts +0 -8
  460. package/src/__tests__/schedule-tools.test.ts +0 -7
  461. package/src/__tests__/scheduler-disk-pressure.test.ts +0 -10
  462. package/src/__tests__/scheduler-recurrence.test.ts +19 -96
  463. package/src/__tests__/scheduler-reuse-conversation.test.ts +75 -88
  464. package/src/__tests__/scheduler-wake.test.ts +12 -60
  465. package/src/__tests__/scheduler-worker-standdown.test.ts +0 -8
  466. package/src/__tests__/scoped-approval-grants.test.ts +1 -9
  467. package/src/__tests__/scoped-grant-security-matrix.test.ts +1 -9
  468. package/src/__tests__/secret-allowlist.test.ts +1 -8
  469. package/src/__tests__/secret-ingress-channel.test.ts +0 -7
  470. package/src/__tests__/secret-ingress-cli.test.ts +0 -7
  471. package/src/__tests__/secret-ingress-http.test.ts +3 -11
  472. package/src/__tests__/secret-patterns.test.ts +27 -0
  473. package/src/__tests__/secret-prompter-channel-fallback.test.ts +35 -9
  474. package/src/__tests__/secret-response-routing.test.ts +1 -1
  475. package/src/__tests__/secret-routes-platform-proxy.test.ts +0 -7
  476. package/src/__tests__/secure-keys-managed-failover.test.ts +0 -7
  477. package/src/__tests__/secure-keys.test.ts +142 -33
  478. package/src/__tests__/send-endpoint-busy.test.ts +0 -19
  479. package/src/__tests__/sequence-store.test.ts +0 -9
  480. package/src/__tests__/server-history-render.test.ts +61 -8
  481. package/src/__tests__/service-contracts-import-guard.test.ts +4 -9
  482. package/src/__tests__/set-permission-mode.test.ts +0 -7
  483. package/src/__tests__/settings-routes.test.ts +0 -7
  484. package/src/__tests__/shell-credential-ref.test.ts +0 -8
  485. package/src/__tests__/shell-tool-proxy-mode.test.ts +0 -7
  486. package/src/__tests__/skill-projection-feature-flag.test.ts +35 -17
  487. package/src/__tests__/skill-projection.benchmark.test.ts +7 -7
  488. package/src/__tests__/skill-script-runner-sandbox.test.ts +0 -7
  489. package/src/__tests__/skills-files-catalog-fallback.test.ts +0 -7
  490. package/src/__tests__/slack-channels-routes.test.ts +34 -16
  491. package/src/__tests__/slack-inbound-verification.test.ts +0 -7
  492. package/src/__tests__/slack-reaction-canonical-approval.test.ts +0 -8
  493. package/src/__tests__/slack-users-routes.test.ts +148 -0
  494. package/src/__tests__/slim-skill-category.test.ts +0 -7
  495. package/src/__tests__/stale-approval-dedup.test.ts +0 -7
  496. package/src/__tests__/subagent-call-site-routing.test.ts +45 -11
  497. package/src/__tests__/subagent-detail.test.ts +2 -1
  498. package/src/__tests__/subagent-fork-prompt-role.test.ts +0 -5
  499. package/src/__tests__/subagent-fork-spawn.test.ts +0 -37
  500. package/src/__tests__/subagent-spawn-and-await.test.ts +0 -5
  501. package/src/__tests__/subagent-tool-gate-mode.test.ts +1 -7
  502. package/src/__tests__/subagent-tools.test.ts +77 -36
  503. package/src/__tests__/suggestion-routes.test.ts +28 -42
  504. package/src/__tests__/summarize-boundary.test.ts +169 -0
  505. package/src/__tests__/task-compiler.test.ts +0 -7
  506. package/src/__tests__/task-management-tools.test.ts +0 -7
  507. package/src/__tests__/task-memory-cleanup.test.ts +0 -18
  508. package/src/__tests__/task-scheduler.test.ts +40 -9
  509. package/src/__tests__/telegram-bot-username-resolution.test.ts +0 -8
  510. package/src/__tests__/telephony-tts-guard.test.ts +118 -26
  511. package/src/__tests__/test-preload.ts +11 -1
  512. package/src/__tests__/thread-backfill.test.ts +2 -8
  513. package/src/__tests__/token-manager.test.ts +0 -9
  514. package/src/__tests__/tool-approval-handler.test.ts +57 -88
  515. package/src/__tests__/{tool-audit-listener.test.ts → tool-audit.test.ts} +230 -255
  516. package/src/__tests__/tool-execution-abort-cleanup.test.ts +5 -33
  517. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +1 -8
  518. package/src/__tests__/tool-executor-lifecycle-events.test.ts +308 -411
  519. package/src/__tests__/tool-executor.test.ts +37 -26
  520. package/src/__tests__/tool-grant-request-escalation.test.ts +17 -31
  521. package/src/__tests__/tool-preview-lifecycle.test.ts +2 -8
  522. package/src/__tests__/tool-profiler.test.ts +139 -0
  523. package/src/__tests__/tool-result-metadata-plumbing.test.ts +0 -8
  524. package/src/__tests__/tool-start-timestamp.test.ts +4 -10
  525. package/src/__tests__/tools-get-route.test.ts +75 -3
  526. package/src/__tests__/trust-context-guards.test.ts +1 -1
  527. package/src/__tests__/trusted-contact-approval-notifier.test.ts +1 -1
  528. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +9 -26
  529. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +0 -7
  530. package/src/__tests__/trusted-contact-multichannel.test.ts +0 -7
  531. package/src/__tests__/trusted-contact-verification.test.ts +6 -139
  532. package/src/__tests__/turn-boundary-resolution.test.ts +0 -7
  533. package/src/__tests__/turn-events-store.test.ts +51 -7
  534. package/src/__tests__/turn-outcome-read.test.ts +75 -0
  535. package/src/__tests__/twilio-config.test.ts +0 -7
  536. package/src/__tests__/twilio-provider.test.ts +0 -7
  537. package/src/__tests__/twilio-routes-twiml.test.ts +1 -8
  538. package/src/__tests__/twilio-routes.test.ts +12 -5
  539. package/src/__tests__/twilio-validation.test.ts +0 -7
  540. package/src/__tests__/usage-attribution.test.ts +91 -1
  541. package/src/__tests__/usage-cache-backfill-migration.test.ts +0 -7
  542. package/src/__tests__/usage-routes.test.ts +1 -8
  543. package/src/__tests__/vbundle-pax-and-symlink.test.ts +0 -7
  544. package/src/__tests__/vercel-ai-gateway-provider.test.ts +283 -0
  545. package/src/__tests__/vercel-config.test.ts +0 -7
  546. package/src/__tests__/verification-control-plane-policy.test.ts +60 -31
  547. package/src/__tests__/voice-approval-resolver-relay.test.ts +3 -9
  548. package/src/__tests__/voice-config-update.test.ts +1 -22
  549. package/src/__tests__/voice-guardian-cold-cache-warm.test.ts +8 -17
  550. package/src/__tests__/voice-scoped-grant-consumer.test.ts +1 -10
  551. package/src/__tests__/voice-session-bridge-processing-wait.test.ts +0 -7
  552. package/src/__tests__/voice-session-bridge.test.ts +116 -68
  553. package/src/__tests__/workspace-git-service.test.ts +207 -2
  554. package/src/__tests__/workspace-migration-009-backfill-conversation-disk-view.test.ts +0 -8
  555. package/src/__tests__/workspace-migration-013-repair-conversation-disk-view.test.ts +0 -8
  556. package/src/__tests__/workspace-migration-028-recover-conversations-from-disk-view.test.ts +0 -8
  557. package/src/__tests__/workspace-migration-126-strip-managed-profile-bodies.test.ts +192 -0
  558. package/src/__tests__/workspace-migration-127-backfill-default-provider.test.ts +277 -0
  559. package/src/__tests__/workspace-migration-128-repair-stale-openrouter-grok-model-ids.test.ts +135 -0
  560. package/src/__tests__/workspace-migration-129-remove-analyze-conversation-config.test.ts +190 -0
  561. package/src/__tests__/workspace-tool-loader.test.ts +5 -1
  562. package/src/a2a/__tests__/e2e-a2a-channel.test.ts +1 -10
  563. package/src/a2a/__tests__/task-store.test.ts +0 -9
  564. package/src/acp/__tests__/session-manager-persistence.test.ts +1 -8
  565. package/src/acp/__tests__/session-manager-resume.test.ts +0 -7
  566. package/src/acp/__tests__/session-manager-startup.test.ts +1 -8
  567. package/src/agent/attachments.ts +57 -0
  568. package/src/agent/image-optimize.ts +80 -0
  569. package/src/agent/loop-exclusive-tool.test.ts +28 -5
  570. package/src/agent/loop.ts +25 -63
  571. package/src/api/responses/conversation-message.ts +9 -15
  572. package/src/approvals/guardian-expiry-notifier.ts +6 -0
  573. package/src/approvals/guardian-request-resolvers.ts +42 -21
  574. package/src/background-wake/wake-intent-hooks.test.ts +2 -10
  575. package/src/browser/operation-meta.ts +493 -0
  576. package/src/browser/operations.ts +2 -477
  577. package/src/calls/__tests__/call-setup-router.test.ts +341 -321
  578. package/src/calls/__tests__/channel-admission-reader.test.ts +50 -24
  579. package/src/calls/__tests__/fish-audio-client.test.ts +35 -11
  580. package/src/calls/__tests__/inbound-trust-reader.test.ts +157 -34
  581. package/src/calls/__tests__/tts-text-sanitizer.test.ts +22 -0
  582. package/src/calls/__tests__/voice-session-bridge.test.ts +884 -0
  583. package/src/calls/access-request-wait.ts +3 -1
  584. package/src/calls/audio-store.ts +6 -6
  585. package/src/calls/call-constants.ts +1 -1
  586. package/src/calls/call-controller.ts +242 -64
  587. package/src/calls/call-setup-flow-types.ts +2 -2
  588. package/src/calls/call-setup-flow.ts +19 -57
  589. package/src/calls/call-setup-router.ts +63 -39
  590. package/src/calls/call-speech-output.ts +39 -39
  591. package/src/calls/call-transport.ts +17 -5
  592. package/src/calls/channel-admission-reader.ts +38 -28
  593. package/src/calls/fish-audio-client.ts +24 -52
  594. package/src/calls/gateway-invite-reader.ts +15 -19
  595. package/src/calls/inbound-trust-reader.ts +74 -25
  596. package/src/calls/media-stream-audio-transcode.ts +1 -1
  597. package/src/calls/media-stream-output.ts +482 -64
  598. package/src/calls/media-stream-server.ts +156 -83
  599. package/src/calls/media-stream-stt-session.ts +1 -0
  600. package/src/calls/resolve-call-tts-provider.ts +60 -17
  601. package/src/calls/telephony-tts-capability.ts +13 -13
  602. package/src/calls/tts-text-sanitizer.ts +5 -3
  603. package/src/calls/unresolved-caller-trust.ts +36 -0
  604. package/src/calls/voice-session-bridge.ts +397 -52
  605. package/src/channels/__tests__/gateway-invites.test.ts +257 -0
  606. package/src/channels/gateway-invites.ts +142 -0
  607. package/src/channels/gateway-verification-sessions.ts +4 -8
  608. package/src/cli/AGENTS.md +60 -51
  609. package/src/cli/commands/__tests__/inference-models.test.ts +86 -0
  610. package/src/cli/commands/__tests__/inference-profiles.test.ts +254 -0
  611. package/src/cli/commands/__tests__/inference-providers.test.ts +171 -0
  612. package/src/cli/commands/__tests__/schedules.test.ts +133 -1
  613. package/src/cli/commands/__tests__/usage.test.ts +131 -0
  614. package/src/cli/commands/attachment.help.ts +100 -0
  615. package/src/cli/commands/attachment.ts +92 -157
  616. package/src/cli/commands/audit.help.ts +40 -0
  617. package/src/cli/commands/audit.ts +66 -86
  618. package/src/cli/commands/auth.help.ts +46 -0
  619. package/src/cli/commands/auth.ts +10 -43
  620. package/src/cli/commands/avatar.help.ts +236 -0
  621. package/src/cli/commands/avatar.ts +66 -250
  622. package/src/cli/commands/backup.help.ts +190 -0
  623. package/src/cli/commands/backup.ts +109 -276
  624. package/src/cli/commands/bash.help.ts +45 -0
  625. package/src/cli/commands/bash.ts +55 -75
  626. package/src/cli/commands/browser.help.ts +196 -0
  627. package/src/cli/commands/browser.ts +241 -352
  628. package/src/cli/commands/config.ts +166 -164
  629. package/src/cli/commands/contacts.ts +13 -22
  630. package/src/cli/commands/credentials.ts +38 -28
  631. package/src/cli/commands/db/repair-step-conversation-backfill.ts +19 -10
  632. package/src/cli/commands/db/repair.ts +20 -8
  633. package/src/cli/commands/default-action.ts +8 -3
  634. package/src/cli/commands/domain.ts +36 -22
  635. package/src/cli/commands/email.ts +77 -36
  636. package/src/cli/commands/inference-callsites.ts +158 -0
  637. package/src/cli/commands/inference-models.ts +84 -0
  638. package/src/cli/commands/inference-profiles.ts +371 -0
  639. package/src/cli/commands/inference-providers-default.ts +89 -0
  640. package/src/cli/commands/inference-providers.ts +117 -24
  641. package/src/cli/commands/inference.ts +6 -0
  642. package/src/cli/commands/keys.ts +27 -38
  643. package/src/cli/commands/memory/index.ts +8 -0
  644. package/src/cli/commands/memory/memory-retrospective.ts +7 -5
  645. package/src/cli/commands/memory/memory-v2-compare-render.ts +2 -2
  646. package/src/cli/commands/memory/nodes.ts +301 -0
  647. package/src/cli/commands/oauth/shared.ts +1 -1
  648. package/src/cli/commands/oauth/status.test.ts +6 -6
  649. package/src/cli/commands/oauth/status.ts +12 -6
  650. package/src/cli/commands/oauth/token.ts +1 -26
  651. package/src/cli/commands/plugins.ts +210 -116
  652. package/src/cli/commands/schedules.ts +17 -2
  653. package/src/cli/commands/tools-run.ts +4 -4
  654. package/src/cli/commands/tools.ts +23 -7
  655. package/src/cli/commands/ui.ts +2 -3
  656. package/src/cli/commands/usage.ts +59 -21
  657. package/src/cli/index.help.ts +31 -0
  658. package/src/cli/lib/__tests__/cli-output.test.ts +20 -0
  659. package/src/cli/lib/__tests__/diff-plugin.test.ts +2 -5
  660. package/src/cli/lib/__tests__/inspect-plugin.test.ts +1 -1
  661. package/src/cli/lib/__tests__/install-from-github.test.ts +37 -5
  662. package/src/cli/lib/__tests__/install-from-platform.test.ts +497 -0
  663. package/src/cli/lib/__tests__/list-installed-plugins.test.ts +36 -0
  664. package/src/cli/lib/__tests__/plugin-artifact.test.ts +54 -1
  665. package/src/cli/lib/__tests__/plugin-details.test.ts +137 -1
  666. package/src/cli/lib/__tests__/plugin-icon-file.test.ts +142 -0
  667. package/src/cli/lib/__tests__/plugin-marketplace.test.ts +1 -1
  668. package/src/cli/lib/__tests__/plugin-pin-history.test.ts +1 -1
  669. package/src/cli/lib/__tests__/search-plugins.test.ts +1 -1
  670. package/src/cli/lib/__tests__/uninstall-plugin.test.ts +82 -21
  671. package/src/cli/lib/__tests__/upgrade-plugin.test.ts +1 -1
  672. package/src/cli/lib/cli-command-help.ts +119 -0
  673. package/src/cli/lib/cli-output.ts +55 -0
  674. package/src/cli/lib/diff-plugin.ts +2 -2
  675. package/src/cli/lib/fetch-like.ts +11 -0
  676. package/src/cli/lib/inspect-plugin.ts +2 -2
  677. package/src/cli/lib/install-from-github.ts +61 -15
  678. package/src/cli/lib/install-from-platform.ts +618 -0
  679. package/src/cli/lib/list-installed-plugins.ts +39 -5
  680. package/src/cli/lib/plugin-artifact.ts +30 -0
  681. package/src/cli/lib/plugin-constants.ts +27 -0
  682. package/src/cli/lib/plugin-details.ts +51 -11
  683. package/src/cli/lib/plugin-icon-file.ts +101 -0
  684. package/src/cli/lib/plugin-marketplace.ts +1 -4
  685. package/src/cli/lib/plugin-pin-history.ts +27 -12
  686. package/src/cli/lib/publish-plugin.ts +3 -3
  687. package/src/cli/lib/search-plugins.ts +2 -6
  688. package/src/cli/lib/uninstall-plugin.ts +24 -2
  689. package/src/cli/lib/upgrade-plugin.ts +8 -22
  690. package/src/cli/program.ts +0 -2
  691. package/src/cli.ts +22 -13
  692. package/src/config/__tests__/default-profile-catalog.test.ts +384 -0
  693. package/src/config/__tests__/default-provider.test.ts +245 -0
  694. package/src/config/__tests__/deployment-context-defaults.test.ts +0 -21
  695. package/src/config/__tests__/loader-callsite-strip-fallback.test.ts +0 -21
  696. package/src/config/__tests__/loader-last-known-good.test.ts +208 -0
  697. package/src/config/__tests__/loader-sparse-array-cleanup.test.ts +145 -0
  698. package/src/config/__tests__/profile-materialization.test.ts +297 -0
  699. package/src/config/__tests__/seed-default-provider.test.ts +210 -0
  700. package/src/config/__tests__/sync-gated-profiles.test.ts +27 -34
  701. package/src/config/assistant-feature-flags.ts +1 -1
  702. package/src/config/bundled-skills/_shared/CLI_RETRIEVAL_PATTERN.md +9 -8
  703. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +0 -6
  704. package/src/config/bundled-skills/phone-calls/references/CONFIG.md +2 -2
  705. package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +0 -3
  706. package/src/config/bundled-skills/playbooks/tools/playbook-delete.ts +0 -3
  707. package/src/config/bundled-skills/playbooks/tools/playbook-list.ts +1 -3
  708. package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +0 -4
  709. package/src/config/bundled-skills/schedule/SKILL.md +17 -0
  710. package/src/config/bundled-skills/transcribe/tools/transcribe-media.test.ts +0 -7
  711. package/src/config/call-site-defaults.ts +5 -2
  712. package/src/config/default-profile-catalog.ts +481 -0
  713. package/src/config/default-profile-names.ts +44 -0
  714. package/src/config/default-provider-resolution.ts +33 -0
  715. package/src/config/default-provider.ts +38 -0
  716. package/src/config/env-registry.ts +2 -0
  717. package/src/config/env.ts +0 -6
  718. package/src/config/feature-flag-registry.json +42 -50
  719. package/src/config/inference-profile-validation.ts +5 -4
  720. package/src/config/llm-resolver.ts +447 -24
  721. package/src/config/loader.ts +345 -10
  722. package/src/config/profile-materialization.ts +151 -0
  723. package/src/config/schema.ts +2 -52
  724. package/src/config/schemas/__tests__/live-voice.test.ts +108 -0
  725. package/src/config/schemas/__tests__/llm-request-logs.test.ts +33 -5
  726. package/src/config/schemas/call-site-catalog.ts +0 -6
  727. package/src/config/schemas/calls.ts +9 -0
  728. package/src/config/schemas/conversations.ts +1 -1
  729. package/src/config/schemas/live-voice.ts +77 -0
  730. package/src/config/schemas/llm-request-logs.ts +78 -27
  731. package/src/config/schemas/llm.ts +53 -2
  732. package/src/config/schemas/memory-lifecycle.ts +0 -6
  733. package/src/config/schemas/memory.ts +57 -1
  734. package/src/config/seed-inference-profiles.ts +100 -289
  735. package/src/config/sync-gated-profiles.ts +17 -23
  736. package/src/contacts/canonical-guardian-store.ts +6 -0
  737. package/src/contacts/contact-store.ts +272 -51
  738. package/src/contacts/contacts-write.ts +0 -22
  739. package/src/contacts/guardian-contact-reader.ts +3 -2
  740. package/src/contacts/member-write-relay.ts +2 -19
  741. package/src/contacts/types.ts +2 -1
  742. package/src/context/compactor.ts +230 -38
  743. package/src/context/image-dimensions.ts +25 -3
  744. package/src/context/token-estimator.ts +11 -7
  745. package/src/conversations/__tests__/message-consolidation.test.ts +6 -5
  746. package/src/conversations/job-handlers/summarization.ts +0 -2
  747. package/src/conversations/message-consolidation.ts +34 -112
  748. package/src/credential-execution/executable-discovery.ts +16 -144
  749. package/src/credential-execution/process-manager.test.ts +1 -112
  750. package/src/credential-execution/process-manager.ts +13 -379
  751. package/src/credential-execution/prompted-credential.ts +1 -1
  752. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +1 -1
  753. package/src/daemon/__tests__/inference-profile-notification.test.ts +0 -8
  754. package/src/daemon/__tests__/inflight-message-content.test.ts +219 -0
  755. package/src/daemon/__tests__/native-web-search-metadata.test.ts +0 -8
  756. package/src/daemon/__tests__/wake-conversation-ops-completion.test.ts +0 -6
  757. package/src/daemon/__tests__/web-search-status-text.test.ts +0 -8
  758. package/src/daemon/approval-generators.ts +17 -11
  759. package/src/daemon/assistant-attachments.ts +86 -87
  760. package/src/daemon/config-watcher.ts +67 -21
  761. package/src/daemon/conversation-agent-loop-handlers.ts +146 -50
  762. package/src/daemon/conversation-agent-loop.ts +136 -34
  763. package/src/daemon/conversation-error.ts +291 -109
  764. package/src/daemon/conversation-history.ts +6 -12
  765. package/src/daemon/conversation-initial-prompt.ts +1 -1
  766. package/src/daemon/conversation-launch.ts +1 -1
  767. package/src/daemon/conversation-lifecycle.ts +5 -22
  768. package/src/daemon/conversation-media-retry.ts +3 -2
  769. package/src/daemon/conversation-messaging.ts +296 -66
  770. package/src/daemon/conversation-notifiers.ts +1 -1
  771. package/src/daemon/conversation-process.ts +162 -12
  772. package/src/daemon/conversation-queue-manager.ts +12 -0
  773. package/src/daemon/conversation-runtime-assembly.ts +197 -53
  774. package/src/daemon/conversation-skill-tools.ts +27 -9
  775. package/src/daemon/conversation-slash.ts +2 -4
  776. package/src/daemon/conversation-store.ts +11 -13
  777. package/src/daemon/conversation-surfaces.ts +145 -176
  778. package/src/daemon/conversation-tool-setup.ts +18 -12
  779. package/src/daemon/conversation-turn-finalize.ts +18 -1
  780. package/src/daemon/conversation.ts +265 -85
  781. package/src/daemon/disk-pressure-policy.ts +1 -1
  782. package/src/daemon/event-loop-watchdog.test.ts +1 -6
  783. package/src/daemon/event-loop-watchdog.ts +2 -20
  784. package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +0 -7
  785. package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +1 -8
  786. package/src/daemon/handlers/__tests__/config-a2a-invite.test.ts +1 -8
  787. package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +1 -8
  788. package/src/daemon/handlers/__tests__/config-a2a.test.ts +1 -8
  789. package/src/daemon/handlers/config-a2a.ts +0 -3
  790. package/src/daemon/handlers/conversation-history.ts +6 -4
  791. package/src/daemon/handlers/shared.ts +224 -79
  792. package/src/daemon/handlers/skills.ts +8 -0
  793. package/src/daemon/inflight-message-content.ts +228 -0
  794. package/src/daemon/interrupted-turn-reconciler.ts +134 -0
  795. package/src/daemon/lifecycle.ts +120 -64
  796. package/src/daemon/main.ts +1 -5
  797. package/src/daemon/message-types/conversations.ts +2 -0
  798. package/src/daemon/message-types/host-bash.ts +1 -1
  799. package/src/daemon/message-types/skills.ts +6 -0
  800. package/src/daemon/message-types/sync.ts +1 -0
  801. package/src/daemon/orphan-reaper.test.ts +1 -6
  802. package/src/daemon/persist-media-references.ts +151 -0
  803. package/src/daemon/pointer-conversation-trust.ts +2 -4
  804. package/src/daemon/process-message.ts +82 -8
  805. package/src/daemon/providers-setup.ts +1 -4
  806. package/src/daemon/shutdown-handlers.ts +0 -4
  807. package/src/daemon/sqlite-corruption-watchdog.test.ts +0 -13
  808. package/src/daemon/sqlite-corruption-watchdog.ts +2 -15
  809. package/src/daemon/summarize-boundary.ts +62 -0
  810. package/src/daemon/tool-setup-types.ts +1 -1
  811. package/src/daemon/trust-context-types.ts +61 -0
  812. package/src/daemon/trust-context.ts +5 -55
  813. package/src/daemon/wake-conversation-ops.ts +4 -1
  814. package/src/documents/document-comments-store.test.ts +1 -7
  815. package/src/export/__tests__/transcript-formatter.test.ts +13 -8
  816. package/src/export/formatter.ts +8 -17
  817. package/src/export/transcript-formatter.ts +13 -40
  818. package/src/heartbeat/__tests__/heartbeat-run-store.test.ts +0 -9
  819. package/src/heartbeat/__tests__/heartbeat-service.test.ts +0 -2
  820. package/src/home/__tests__/home-content-refresh.test.ts +0 -7
  821. package/src/home/__tests__/suggested-prompts.test.ts +4 -12
  822. package/src/home/conversation-starter-checkpoints.ts +9 -5
  823. package/src/home/job-handlers/conversation-starters.ts +24 -54
  824. package/src/home/suggested-prompts-cache.ts +91 -0
  825. package/src/home/suggested-prompts.ts +4 -77
  826. package/src/hooks/__tests__/hook-live-reload.test.ts +62 -48
  827. package/src/hooks/hook-loader.ts +215 -194
  828. package/src/hooks/types.ts +2 -2
  829. package/src/ipc/__tests__/contacts-info-ipc.test.ts +66 -11
  830. package/src/ipc/assistant-server.ts +14 -34
  831. package/src/ipc/gateway-flag-listener.ts +14 -25
  832. package/src/ipc/gateway-validated-call.ts +32 -0
  833. package/src/ipc/routes/__tests__/contacts-mirror-ipc-routes.test.ts +149 -17
  834. package/src/ipc/routes/__tests__/contacts-mirror-merge-contact.test.ts +197 -0
  835. package/src/ipc/routes/__tests__/contacts-mirror-upsert-full.test.ts +368 -0
  836. package/src/ipc/routes/__tests__/guardian-label-ipc-routes.test.ts +74 -0
  837. package/src/ipc/routes/__tests__/invite-ipc-routes.test.ts +0 -7
  838. package/src/ipc/routes/__tests__/memory-v2-backfill.test.ts +5 -7
  839. package/src/ipc/routes/contacts-info-ipc-routes.ts +5 -2
  840. package/src/ipc/routes/contacts-mirror-ipc-routes.ts +78 -1
  841. package/src/ipc/routes/db-proxy.ts +9 -15
  842. package/src/ipc/routes/guardian-label-ipc-routes.ts +42 -0
  843. package/src/live-voice/__tests__/live-voice-archive.test.ts +1 -8
  844. package/src/live-voice/__tests__/live-voice-credential-preflight.test.ts +275 -0
  845. package/src/live-voice/__tests__/live-voice-integration.test.ts +124 -0
  846. package/src/live-voice/__tests__/live-voice-metrics.test.ts +67 -0
  847. package/src/live-voice/__tests__/live-voice-session-preflight.test.ts +156 -0
  848. package/src/live-voice/__tests__/live-voice-stt.test.ts +464 -19
  849. package/src/live-voice/__tests__/live-voice-tts-session.test.ts +350 -4
  850. package/src/live-voice/__tests__/live-voice-tts.test.ts +186 -10
  851. package/src/live-voice/__tests__/live-voice-vad.test.ts +372 -10
  852. package/src/live-voice/__tests__/protocol.test.ts +2 -0
  853. package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +61 -9
  854. package/src/live-voice/live-voice-credential-preflight.ts +205 -0
  855. package/src/live-voice/live-voice-metrics.ts +44 -0
  856. package/src/live-voice/live-voice-session-manager.ts +19 -0
  857. package/src/live-voice/live-voice-session.ts +1015 -276
  858. package/src/live-voice/live-voice-tts.ts +50 -4
  859. package/src/live-voice/protocol.ts +8 -0
  860. package/src/mcp/__tests__/mcp-auth-orchestrator.test.ts +3 -7
  861. package/src/messaging/channel-binding-schema.ts +15 -7
  862. package/src/messaging/providers/a2a/__tests__/deliver.test.ts +0 -9
  863. package/src/messaging/providers/slack/__tests__/download.test.ts +1 -8
  864. package/src/messaging/providers/slack/binding-metadata.test.ts +63 -0
  865. package/src/messaging/providers/slack/binding-metadata.ts +6 -0
  866. package/src/messaging/providers/slack/conversation-utils.ts +7 -4
  867. package/src/messaging/providers/slack/send.test.ts +0 -7
  868. package/src/messaging/providers/slack/types.ts +2 -0
  869. package/src/messaging/providers/slack/withdraw.test.ts +0 -6
  870. package/src/messaging/providers/telegram-bot/send.test.ts +0 -7
  871. package/src/monitoring/__tests__/resource-sampler.test.ts +2 -4
  872. package/src/monitoring/__tests__/stall-capture.test.ts +1 -1
  873. package/src/monitoring/process-memory.ts +6 -1
  874. package/src/monitoring/resource-sample-types.ts +71 -0
  875. package/src/monitoring/resource-sampler.ts +7 -69
  876. package/src/monitoring/stall-capture.ts +1 -1
  877. package/src/notifications/__tests__/broadcaster.test.ts +100 -9
  878. package/src/notifications/__tests__/connected-channels.test.ts +11 -8
  879. package/src/notifications/__tests__/decision-engine.test.ts +0 -7
  880. package/src/notifications/__tests__/destination-resolver.test.ts +18 -12
  881. package/src/notifications/__tests__/deterministic-checks.test.ts +1 -9
  882. package/src/notifications/access-request-copy.ts +33 -1
  883. package/src/notifications/adapters/slack.ts +7 -2
  884. package/src/notifications/approval-card-data.ts +4 -2
  885. package/src/notifications/broadcaster.ts +118 -67
  886. package/src/notifications/copy-composer.ts +6 -2
  887. package/src/notifications/emit-signal.ts +23 -1
  888. package/src/notifications/signal.ts +5 -4
  889. package/src/notifications/types.ts +1 -1
  890. package/src/notifications/urgency.ts +5 -0
  891. package/src/oauth/byo-connection.test.ts +1 -10
  892. package/src/oauth/connect-orchestrator.ts +1 -1
  893. package/src/oauth/connection-resolver.test.ts +140 -7
  894. package/src/oauth/connection-resolver.ts +48 -12
  895. package/src/oauth/oauth-store.ts +4 -11
  896. package/src/onboarding/__tests__/onboarding-events-store.test.ts +0 -8
  897. package/src/permissions/approval-policy.test.ts +0 -314
  898. package/src/permissions/approval-policy.ts +38 -90
  899. package/src/permissions/checker.test.ts +3 -14
  900. package/src/permissions/checker.ts +95 -19
  901. package/src/permissions/confirmation-canonical-request.ts +118 -0
  902. package/src/permissions/gateway-threshold-reader.test.ts +0 -8
  903. package/src/permissions/ipc-risk-types.ts +2 -0
  904. package/src/permissions/prompter.ts +16 -3
  905. package/src/permissions/secret-prompt-types.ts +34 -0
  906. package/src/permissions/secret-prompter.ts +21 -19
  907. package/src/permissions/types.ts +0 -14
  908. package/src/persistence/__tests__/bulk-write-gate.test.ts +159 -0
  909. package/src/persistence/__tests__/conversation-queries-search.test.ts +0 -7
  910. package/src/persistence/__tests__/immediate-transaction-guard.test.ts +70 -0
  911. package/src/persistence/__tests__/message-content-file.test.ts +259 -0
  912. package/src/persistence/__tests__/slow-query-log.test.ts +125 -0
  913. package/src/persistence/attachments-store.ts +90 -26
  914. package/src/persistence/auto-analysis-constants.ts +4 -13
  915. package/src/persistence/bulk-write-gate.ts +82 -0
  916. package/src/persistence/cleanup-schedule-state.ts +51 -21
  917. package/src/persistence/conversation-bootstrap.ts +3 -3
  918. package/src/persistence/conversation-crud.ts +382 -105
  919. package/src/persistence/conversation-disk-view.ts +32 -15
  920. package/src/persistence/conversation-group-migration.test.ts +1 -8
  921. package/src/persistence/conversation-key-store.ts +0 -2
  922. package/src/persistence/conversation-plugin-facade.ts +190 -0
  923. package/src/persistence/conversation-row-batch-delete.ts +16 -1
  924. package/src/persistence/conversation-title-service.ts +59 -22
  925. package/src/persistence/conversation-types.ts +19 -4
  926. package/src/persistence/db-async-query.ts +18 -9
  927. package/src/persistence/db-connection.ts +20 -4
  928. package/src/persistence/db-init.ts +16 -173
  929. package/src/persistence/db-maintenance.ts +78 -6
  930. package/src/persistence/embeddings/__tests__/messages-lexical-index.test.ts +0 -7
  931. package/src/persistence/embeddings/plugin-facade.ts +37 -0
  932. package/src/persistence/embeddings/qdrant-client.ts +2 -18
  933. package/src/persistence/fork-message-copy.ts +18 -4
  934. package/src/persistence/job-handlers/__tests__/message-lexical-backfill.test.ts +0 -7
  935. package/src/persistence/job-handlers/__tests__/message-lexical-enqueue.test.ts +0 -6
  936. package/src/persistence/job-handlers/__tests__/message-lexical.test.ts +0 -7
  937. package/src/persistence/job-handlers/message-lexical-backfill.ts +8 -5
  938. package/src/persistence/job-handlers/message-lexical.ts +2 -2
  939. package/src/persistence/jobs-store.ts +48 -85
  940. package/src/persistence/llm-request-log-sink-clickhouse.ts +325 -0
  941. package/src/persistence/llm-request-log-store.ts +213 -100
  942. package/src/persistence/llm-request-log-writer-types.ts +80 -0
  943. package/src/persistence/message-content-file.ts +301 -0
  944. package/src/persistence/message-content.ts +33 -72
  945. package/src/persistence/migrations/321-canonical-guardian-request-trigger.ts +27 -0
  946. package/src/persistence/migrations/322-add-processing-resume-attempts.ts +31 -0
  947. package/src/persistence/migrations/323-delete-non-default-memory-scopes.test.ts +148 -0
  948. package/src/persistence/migrations/323-delete-non-default-memory-scopes.ts +35 -0
  949. package/src/persistence/migrations/324-message-finalized-column.ts +26 -0
  950. package/src/persistence/migrations/325-create-config-setting-events.ts +40 -0
  951. package/src/persistence/migrations/326-move-injection-events-to-memory-db.ts +104 -0
  952. package/src/persistence/migrations/327-create-flush-checkpoints.ts +86 -0
  953. package/src/persistence/migrations/__tests__/297-move-llm-request-logs.test.ts +2 -2
  954. package/src/persistence/migrations/__tests__/327-create-flush-checkpoints.test.ts +124 -0
  955. package/src/persistence/schema/conversations.ts +25 -1
  956. package/src/persistence/schema/guardian.ts +5 -0
  957. package/src/persistence/schema/infrastructure.ts +33 -0
  958. package/src/persistence/schema/memory-core.ts +17 -28
  959. package/src/persistence/schema/memory-graph.ts +0 -4
  960. package/src/persistence/schema/memory-injection.ts +3 -1
  961. package/src/persistence/schema/tasks.ts +0 -1
  962. package/src/persistence/slow-query-log.ts +58 -21
  963. package/src/persistence/steps.ts +14 -0
  964. package/src/platform/consent-cache.test.ts +0 -7
  965. package/src/platform/managed-speech.test.ts +218 -0
  966. package/src/platform/managed-speech.ts +195 -0
  967. package/src/playbooks/playbook-compiler.ts +2 -10
  968. package/src/plugin-api/index.ts +79 -0
  969. package/src/plugin-api/model-profiles.test.ts +31 -3
  970. package/src/plugin-api/model-profiles.ts +3 -1
  971. package/src/plugin-api/types.ts +12 -3
  972. package/src/plugin-api/vision-support.ts +4 -3
  973. package/src/plugins/AGENTS.md +19 -0
  974. package/src/plugins/__tests__/plugin-state-boundary-guard.test.ts +65 -0
  975. package/src/plugins/defaults/compaction/compact.ts +13 -0
  976. package/src/plugins/defaults/compaction/window-manager.ts +207 -43
  977. package/src/plugins/defaults/image-fallback/__tests__/caption-cache-persistence.test.ts +216 -0
  978. package/src/plugins/defaults/image-fallback/__tests__/image-fallback.test.ts +281 -2
  979. package/src/plugins/defaults/image-fallback/__tests__/vision-recovery.test.ts +268 -0
  980. package/src/plugins/defaults/image-fallback/hooks/conversation-deleted.ts +27 -0
  981. package/src/plugins/defaults/image-fallback/hooks/init.ts +16 -0
  982. package/src/plugins/defaults/image-fallback/hooks/post-compact.ts +52 -0
  983. package/src/plugins/defaults/image-fallback/hooks/post-model-call.ts +80 -0
  984. package/src/plugins/defaults/image-fallback/hooks/post-tool-use.ts +7 -4
  985. package/src/plugins/defaults/image-fallback/hooks/shutdown.ts +14 -0
  986. package/src/plugins/defaults/image-fallback/hooks/stop.ts +23 -0
  987. package/src/plugins/defaults/image-fallback/hooks/user-prompt-submit.ts +12 -12
  988. package/src/plugins/defaults/image-fallback/src/caption-blocks.ts +73 -15
  989. package/src/plugins/defaults/image-fallback/src/caption-cache.ts +206 -20
  990. package/src/plugins/defaults/image-fallback/src/recovery-state.ts +49 -0
  991. package/src/plugins/defaults/image-fallback/src/vision-caption.ts +11 -3
  992. package/src/plugins/defaults/image-fallback/src/vision-error.ts +19 -0
  993. package/src/plugins/defaults/image-recovery/detect.ts +32 -10
  994. package/src/plugins/defaults/image-recovery/hooks/post-model-call.ts +20 -18
  995. package/src/plugins/defaults/image-recovery/recover.ts +97 -61
  996. package/src/plugins/defaults/index.ts +29 -5
  997. package/src/plugins/defaults/injector-order.ts +1 -0
  998. package/src/plugins/defaults/memory/__tests__/activation-session-store.test.ts +1 -8
  999. package/src/plugins/defaults/memory/__tests__/clear-stale-processing-flags.test.ts +1 -8
  1000. package/src/plugins/defaults/memory/__tests__/conversation-queries.test.ts +1 -8
  1001. package/src/plugins/defaults/memory/__tests__/conversation-row-batch-delete.test.ts +0 -6
  1002. package/src/plugins/defaults/memory/__tests__/db-maintenance.test.ts +105 -11
  1003. package/src/plugins/defaults/memory/__tests__/db-memory-attach.test.ts +22 -18
  1004. package/src/plugins/defaults/memory/__tests__/embedding-cache.test.ts +0 -6
  1005. package/src/plugins/defaults/memory/__tests__/find-most-recent-retrospective-for.test.ts +1 -8
  1006. package/src/plugins/defaults/memory/__tests__/fork-message-copy.test.ts +0 -6
  1007. package/src/plugins/defaults/memory/__tests__/jobs-store-enqueue-gate.test.ts +7 -70
  1008. package/src/plugins/defaults/memory/__tests__/jobs-worker-memory-disabled.test.ts +0 -7
  1009. package/src/plugins/defaults/memory/__tests__/jobs-worker-pkb-schedule.test.ts +7 -0
  1010. package/src/plugins/defaults/memory/__tests__/jobs-worker-v2-graph-trigger-embed.test.ts +0 -5
  1011. package/src/plugins/defaults/memory/__tests__/jobs-worker-v2-schedule.test.ts +7 -0
  1012. package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +0 -7
  1013. package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +186 -19
  1014. package/src/plugins/defaults/memory/__tests__/memory-retrospective-skill-card.test.ts +926 -0
  1015. package/src/plugins/defaults/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +136 -82
  1016. package/src/plugins/defaults/memory/__tests__/memory-retrospective-state.test.ts +0 -6
  1017. package/src/plugins/defaults/memory/__tests__/memory-retrospective-trigger-check.test.ts +217 -2
  1018. package/src/plugins/defaults/memory/__tests__/memory-v2-activation-log-store.test.ts +0 -7
  1019. package/src/plugins/defaults/memory/__tests__/memory-v2-concept-frequency.test.ts +0 -7
  1020. package/src/plugins/defaults/memory/__tests__/table-relocation.test.ts +49 -0
  1021. package/src/plugins/defaults/memory/activation-session-store.ts +1 -1
  1022. package/src/plugins/defaults/memory/anisotropy.ts +1 -1
  1023. package/src/plugins/defaults/memory/config.ts +155 -6
  1024. package/src/plugins/defaults/memory/context-search/agent-protocol.ts +1 -1
  1025. package/src/plugins/defaults/memory/context-search/search.ts +1 -1
  1026. package/src/plugins/defaults/memory/context-search/sources/conversations.ts +26 -23
  1027. package/src/plugins/defaults/memory/context-search/sources/memory-v2.ts +1 -1
  1028. package/src/plugins/defaults/memory/context-search/sources/memory.ts +1 -1
  1029. package/src/plugins/defaults/memory/embeddings.ts +8 -40
  1030. package/src/plugins/defaults/memory/filing-jobs.ts +1 -1
  1031. package/src/plugins/defaults/memory/fork-conversation-memory.ts +8 -4
  1032. package/src/plugins/defaults/memory/frontmatter.ts +1 -1
  1033. package/src/plugins/defaults/memory/graph/__tests__/handle-remember-v2.test.ts +4 -25
  1034. package/src/plugins/defaults/memory/graph/bootstrap.ts +13 -19
  1035. package/src/plugins/defaults/memory/graph/capability-seed.ts +62 -33
  1036. package/src/plugins/defaults/memory/graph/consolidation.ts +8 -15
  1037. package/src/plugins/defaults/memory/graph/conversation-graph-memory.ts +12 -4
  1038. package/src/plugins/defaults/memory/graph/decay.ts +5 -10
  1039. package/src/plugins/defaults/memory/graph/extraction-job.ts +2 -3
  1040. package/src/plugins/defaults/memory/graph/extraction.test.ts +1 -9
  1041. package/src/plugins/defaults/memory/graph/extraction.ts +149 -64
  1042. package/src/plugins/defaults/memory/graph/graph-search.test.ts +0 -6
  1043. package/src/plugins/defaults/memory/graph/graph-search.ts +33 -15
  1044. package/src/plugins/defaults/memory/graph/image-ref-utils.ts +7 -3
  1045. package/src/plugins/defaults/memory/graph/injection.test.ts +0 -1
  1046. package/src/plugins/defaults/memory/graph/injection.ts +1 -1
  1047. package/src/plugins/defaults/memory/graph/narrative.ts +2 -4
  1048. package/src/plugins/defaults/memory/graph/pattern-scan.ts +2 -5
  1049. package/src/plugins/defaults/memory/graph/retriever.test.ts +27 -28
  1050. package/src/plugins/defaults/memory/graph/retriever.ts +89 -42
  1051. package/src/plugins/defaults/memory/graph/scoring.test.ts +0 -1
  1052. package/src/plugins/defaults/memory/graph/store.test.ts +6 -58
  1053. package/src/plugins/defaults/memory/graph/store.ts +5 -43
  1054. package/src/plugins/defaults/memory/graph/tool-handlers.test.ts +318 -0
  1055. package/src/plugins/defaults/memory/graph/tool-handlers.ts +244 -8
  1056. package/src/plugins/defaults/memory/graph/tools.ts +1 -1
  1057. package/src/plugins/defaults/memory/graph/types.ts +0 -3
  1058. package/src/plugins/defaults/memory/graph-topology/__tests__/build-memory-graph.test.ts +130 -0
  1059. package/src/plugins/defaults/memory/graph-topology/build-memory-graph.ts +303 -0
  1060. package/src/plugins/defaults/memory/graph-topology/flag.ts +14 -0
  1061. package/src/plugins/defaults/memory/graph-topology/types.ts +84 -0
  1062. package/src/plugins/defaults/memory/hooks/init.ts +1 -1
  1063. package/src/plugins/defaults/memory/hooks/user-prompt-submit.ts +10 -10
  1064. package/src/plugins/defaults/memory/host-utils.ts +80 -0
  1065. package/src/plugins/defaults/memory/identity-context.ts +39 -0
  1066. package/src/plugins/defaults/memory/indexer.ts +18 -54
  1067. package/src/plugins/defaults/memory/injectors.ts +5 -8
  1068. package/src/plugins/defaults/memory/job-handler-registration.ts +0 -4
  1069. package/src/plugins/defaults/memory/job-handlers/backfill.ts +10 -8
  1070. package/src/plugins/defaults/memory/job-handlers/embedding.test.ts +0 -8
  1071. package/src/plugins/defaults/memory/job-handlers/embedding.ts +31 -17
  1072. package/src/plugins/defaults/memory/job-handlers/index-maintenance.ts +13 -6
  1073. package/src/plugins/defaults/memory/job-handlers.ts +10 -9
  1074. package/src/plugins/defaults/memory/jobs/__tests__/embed-concept-page.test.ts +0 -7
  1075. package/src/plugins/defaults/memory/jobs/embed-concept-page.ts +3 -3
  1076. package/src/plugins/defaults/memory/jobs/embed-pkb-file.test.ts +41 -28
  1077. package/src/plugins/defaults/memory/jobs/embed-pkb-file.ts +10 -5
  1078. package/src/plugins/defaults/memory/jobs-worker.ts +161 -50
  1079. package/src/plugins/defaults/memory/logging.ts +17 -0
  1080. package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +143 -0
  1081. package/src/plugins/defaults/memory/memory-retrospective-constants.ts +19 -7
  1082. package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +5 -6
  1083. package/src/plugins/defaults/memory/memory-retrospective-fork-boundary.ts +57 -20
  1084. package/src/plugins/defaults/memory/memory-retrospective-job.ts +220 -79
  1085. package/src/plugins/defaults/memory/memory-retrospective-skill-card.ts +448 -0
  1086. package/src/plugins/defaults/memory/memory-retrospective-startup-cleanup.ts +52 -39
  1087. package/src/plugins/defaults/memory/memory-retrospective-state.ts +14 -10
  1088. package/src/plugins/defaults/memory/memory-retrospective-trigger-check.ts +8 -3
  1089. package/src/plugins/defaults/memory/message-media.ts +80 -0
  1090. package/src/plugins/defaults/memory/paths.ts +48 -0
  1091. package/src/plugins/defaults/memory/pkb/autoinject.ts +1 -1
  1092. package/src/plugins/defaults/memory/pkb/context.ts +2 -2
  1093. package/src/plugins/defaults/memory/pkb/pkb-index.test.ts +25 -44
  1094. package/src/plugins/defaults/memory/pkb/pkb-index.ts +24 -25
  1095. package/src/plugins/defaults/memory/pkb/pkb-reconcile.test.ts +46 -34
  1096. package/src/plugins/defaults/memory/pkb/pkb-reconcile.ts +4 -9
  1097. package/src/plugins/defaults/memory/pkb/pkb-search.test.ts +5 -17
  1098. package/src/plugins/defaults/memory/pkb/pkb-search.ts +2 -9
  1099. package/src/plugins/defaults/memory/pkb/types.ts +1 -12
  1100. package/src/plugins/defaults/memory/pkb-schedule.ts +2 -2
  1101. package/src/plugins/defaults/memory/rerank-local.ts +3 -4
  1102. package/src/plugins/defaults/memory/routes/__tests__/memory-v2-simulate-route.test.ts +9 -17
  1103. package/src/plugins/defaults/memory/routes/memory-eval-routes.ts +2 -2
  1104. package/src/plugins/defaults/memory/routes/memory-graph-routes.ts +71 -0
  1105. package/src/plugins/defaults/memory/routes/memory-item-routes.test.ts +149 -9
  1106. package/src/plugins/defaults/memory/routes/memory-item-routes.ts +147 -12
  1107. package/src/plugins/defaults/memory/routes/memory-v2-routes.ts +7 -11
  1108. package/src/plugins/defaults/memory/routes/memory-v3-routes.ts +1 -1
  1109. package/src/plugins/defaults/memory/startup.ts +3 -4
  1110. package/src/plugins/defaults/memory/task-memory-cleanup.ts +1 -1
  1111. package/src/plugins/defaults/memory/tools.test.ts +1 -9
  1112. package/src/plugins/defaults/memory/tools.ts +0 -1
  1113. package/src/plugins/defaults/memory/v2/__tests__/activation.test.ts +0 -5
  1114. package/src/plugins/defaults/memory/v2/__tests__/backfill-jobs.test.ts +0 -6
  1115. package/src/plugins/defaults/memory/v2/__tests__/cli-command-store.test.ts +6 -6
  1116. package/src/plugins/defaults/memory/v2/__tests__/consolidation-job.test.ts +48 -6
  1117. package/src/plugins/defaults/memory/v2/__tests__/harness-compare.test.ts +0 -5
  1118. package/src/plugins/defaults/memory/v2/__tests__/harness-oracle.test.ts +0 -5
  1119. package/src/plugins/defaults/memory/v2/__tests__/harness-replay-input.test.ts +0 -5
  1120. package/src/plugins/defaults/memory/v2/__tests__/injection-events.test.ts +61 -69
  1121. package/src/plugins/defaults/memory/v2/__tests__/injection.test.ts +0 -5
  1122. package/src/plugins/defaults/memory/v2/__tests__/migration.test.ts +0 -6
  1123. package/src/plugins/defaults/memory/v2/__tests__/page-index.test.ts +0 -5
  1124. package/src/plugins/defaults/memory/v2/__tests__/prompts-consolidation.test.ts +28 -0
  1125. package/src/plugins/defaults/memory/v2/__tests__/qdrant.test.ts +3 -6
  1126. package/src/plugins/defaults/memory/v2/__tests__/reranker.test.ts +3 -5
  1127. package/src/plugins/defaults/memory/v2/__tests__/router.test.ts +16 -27
  1128. package/src/plugins/defaults/memory/v2/__tests__/sim.test.ts +0 -5
  1129. package/src/plugins/defaults/memory/v2/__tests__/skill-content.test.ts +1 -1
  1130. package/src/plugins/defaults/memory/v2/__tests__/skill-store.test.ts +56 -22
  1131. package/src/plugins/defaults/memory/v2/__tests__/static-context.test.ts +9 -6
  1132. package/src/plugins/defaults/memory/v2/__tests__/sweep-job.test.ts +6 -6
  1133. package/src/plugins/defaults/memory/v2/backfill-jobs.ts +31 -16
  1134. package/src/plugins/defaults/memory/v2/cli-command-content.ts +78 -0
  1135. package/src/plugins/defaults/memory/v2/cli-command-store.ts +32 -11
  1136. package/src/plugins/defaults/memory/v2/consolidation-job.ts +45 -5
  1137. package/src/plugins/defaults/memory/v2/frontmatter-sweep.ts +1 -1
  1138. package/src/plugins/defaults/memory/v2/harness/harness-types.ts +12 -0
  1139. package/src/plugins/defaults/memory/v2/harness/retriever.ts +1 -8
  1140. package/src/plugins/defaults/memory/v2/harness/router-retriever.ts +1 -8
  1141. package/src/plugins/defaults/memory/v2/harness/trace.ts +1 -1
  1142. package/src/plugins/defaults/memory/v2/injection-events.ts +34 -15
  1143. package/src/plugins/defaults/memory/v2/injection.ts +3 -4
  1144. package/src/plugins/defaults/memory/v2/memory-v2-startup.test.ts +0 -5
  1145. package/src/plugins/defaults/memory/v2/memory-v2-startup.ts +2 -2
  1146. package/src/plugins/defaults/memory/v2/migration.ts +1 -1
  1147. package/src/plugins/defaults/memory/v2/page-index.ts +1 -1
  1148. package/src/plugins/defaults/memory/v2/prompts/consolidation.ts +6 -2
  1149. package/src/plugins/defaults/memory/v2/prompts/router.ts +1 -1
  1150. package/src/plugins/defaults/memory/v2/qdrant.ts +2 -2
  1151. package/src/plugins/defaults/memory/v2/reranker.ts +2 -2
  1152. package/src/plugins/defaults/memory/v2/router.ts +10 -20
  1153. package/src/plugins/defaults/memory/v2/skill-content.ts +23 -4
  1154. package/src/plugins/defaults/memory/v2/skill-store.ts +44 -43
  1155. package/src/plugins/defaults/memory/v2/static-context.ts +7 -12
  1156. package/src/plugins/defaults/memory/v2/sweep-job.ts +24 -13
  1157. package/src/plugins/defaults/memory/v3/__tests__/carry-integration.test.ts +19 -0
  1158. package/src/plugins/defaults/memory/v3/__tests__/dense.test.ts +0 -5
  1159. package/src/plugins/defaults/memory/v3/__tests__/injection.test.ts +17 -0
  1160. package/src/plugins/defaults/memory/v3/__tests__/live-integration.test.ts +14 -4
  1161. package/src/plugins/defaults/memory/v3/__tests__/maintain-job.test.ts +23 -17
  1162. package/src/plugins/defaults/memory/v3/__tests__/orchestrate.test.ts +20 -6
  1163. package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +2 -0
  1164. package/src/plugins/defaults/memory/v3/__tests__/section-dense-store.test.ts +0 -5
  1165. package/src/plugins/defaults/memory/v3/candidate-match.ts +20 -11
  1166. package/src/plugins/defaults/memory/v3/dense.ts +1 -1
  1167. package/src/plugins/defaults/memory/v3/injector.ts +1 -1
  1168. package/src/plugins/defaults/memory/v3/learned-edges.test.ts +14 -0
  1169. package/src/plugins/defaults/memory/v3/learned-edges.ts +6 -1
  1170. package/src/plugins/defaults/memory/v3/maintain-job.ts +39 -31
  1171. package/src/plugins/defaults/memory/v3/orchestrate.ts +1 -1
  1172. package/src/plugins/defaults/memory/v3/page-content.ts +1 -1
  1173. package/src/plugins/defaults/memory/v3/pool-select.ts +2 -3
  1174. package/src/plugins/defaults/memory/v3/prune.test.ts +10 -0
  1175. package/src/plugins/defaults/memory/v3/prune.ts +1 -1
  1176. package/src/plugins/defaults/memory/v3/section-dense-store.ts +1 -1
  1177. package/src/plugins/defaults/memory/v3/selection-log-store.ts +1 -1
  1178. package/src/plugins/defaults/memory/v3/shadow-plugin.ts +47 -25
  1179. package/src/plugins/defaults/memory/v3-eval/eval-packets.ts +31 -11
  1180. package/src/plugins/defaults/memory/worker.ts +2 -2
  1181. package/src/plugins/defaults/workspace/injectors.ts +116 -5
  1182. package/src/plugins/mtime-cache.ts +207 -105
  1183. package/src/plugins/pipeline.ts +195 -5
  1184. package/src/plugins/surface-import.ts +52 -14
  1185. package/src/plugins/types.ts +10 -15
  1186. package/src/prompts/persona-resolver.ts +1 -1
  1187. package/src/prompts/system-prompt.ts +2 -4
  1188. package/src/prompts/templates/system-sections.ts +12 -0
  1189. package/src/providers/__tests__/connection-model-compat.test.ts +0 -5
  1190. package/src/providers/__tests__/count-tokens-forwarding.test.ts +1 -21
  1191. package/src/providers/__tests__/dispatch-connection-routing.test.ts +12 -6
  1192. package/src/providers/__tests__/preflight-resolved-config.test.ts +189 -0
  1193. package/src/providers/__tests__/registry-native-web-search.test.ts +21 -3
  1194. package/src/providers/__tests__/retry-callsite.test.ts +207 -8
  1195. package/src/providers/__tests__/satellite-connection-routing.test.ts +12 -6
  1196. package/src/providers/__tests__/vellum-mismatch-routing.test.ts +0 -5
  1197. package/src/providers/anthropic/client.ts +109 -7
  1198. package/src/providers/anthropic-gateway-shared.ts +63 -0
  1199. package/src/providers/call-site-routing.ts +1 -0
  1200. package/src/providers/connection-resolution.ts +192 -6
  1201. package/src/providers/content-block-schema.ts +149 -0
  1202. package/src/providers/gemini/client.ts +84 -22
  1203. package/src/providers/gemini/inline-media.ts +2 -3
  1204. package/src/providers/inference/__tests__/base-url-route-validation.test.ts +5 -0
  1205. package/src/providers/inference/__tests__/codex-token-refresh.test.ts +1 -15
  1206. package/src/providers/inference/adapter-factory.ts +13 -0
  1207. package/src/providers/inference/connection-availability.ts +193 -0
  1208. package/src/providers/inference/connections.ts +6 -10
  1209. package/src/providers/media-resolve.ts +193 -0
  1210. package/src/providers/model-catalog.ts +337 -17
  1211. package/src/providers/model-intents.ts +6 -0
  1212. package/src/providers/openai/__tests__/api-error-normalization.test.ts +98 -0
  1213. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +112 -0
  1214. package/src/providers/openai/api-error-normalization.ts +61 -0
  1215. package/src/providers/openai/chat-completions-provider.ts +73 -10
  1216. package/src/providers/openai/responses-provider.ts +10 -3
  1217. package/src/providers/openrouter/client.ts +6 -39
  1218. package/src/providers/platform-proxy/constants.ts +4 -0
  1219. package/src/providers/provider-availability.ts +23 -1
  1220. package/src/providers/provider-send-message.ts +19 -3
  1221. package/src/providers/registry.ts +5 -1
  1222. package/src/providers/retry.ts +91 -29
  1223. package/src/providers/speech-to-text/__tests__/resolve.test.ts +31 -0
  1224. package/src/providers/speech-to-text/deepgram-realtime.test.ts +393 -1
  1225. package/src/providers/speech-to-text/deepgram-realtime.ts +213 -13
  1226. package/src/providers/speech-to-text/resolve.ts +33 -5
  1227. package/src/providers/types.ts +69 -24
  1228. package/src/providers/vellum-model-routing.ts +11 -0
  1229. package/src/providers/vercel-ai-gateway/client.ts +142 -0
  1230. package/src/runtime/__tests__/actor-trust-resolver.test.ts +132 -0
  1231. package/src/runtime/__tests__/agent-wake.test.ts +232 -16
  1232. package/src/runtime/__tests__/background-job-runner.test.ts +71 -2
  1233. package/src/runtime/__tests__/guardian-vellum-migration.test.ts +5 -15
  1234. package/src/runtime/__tests__/local-principal-trust.test.ts +0 -6
  1235. package/src/runtime/__tests__/member-verdict-cache.test.ts +2 -15
  1236. package/src/runtime/__tests__/trust-verdict-consumer.test.ts +91 -30
  1237. package/src/runtime/access-request-helper.ts +75 -3
  1238. package/src/runtime/actor-trust-resolver.ts +33 -108
  1239. package/src/runtime/agent-wake.ts +45 -25
  1240. package/src/runtime/assistant-event-hub.ts +0 -105
  1241. package/src/runtime/auth/__tests__/middleware.test.ts +1 -10
  1242. package/src/runtime/auth/__tests__/route-policy.test.ts +0 -7
  1243. package/src/runtime/auth/__tests__/token-service.test.ts +1 -8
  1244. package/src/runtime/background-job-runner.ts +57 -2
  1245. package/src/runtime/capabilities.ts +3 -3
  1246. package/src/runtime/channel-reply-delivery.ts +38 -19
  1247. package/src/runtime/channel-retry-sweep.ts +1 -1
  1248. package/src/runtime/confirmation-request-guardian-bridge.ts +4 -3
  1249. package/src/runtime/effective-capabilities.test.ts +0 -36
  1250. package/src/runtime/effective-capabilities.ts +8 -23
  1251. package/src/runtime/guardian-vellum-migration.ts +12 -16
  1252. package/src/runtime/http-router-types.ts +119 -0
  1253. package/src/runtime/http-router.ts +2 -115
  1254. package/src/runtime/http-server.ts +11 -2
  1255. package/src/runtime/http-types.ts +1 -1
  1256. package/src/runtime/introduction-policy.ts +89 -0
  1257. package/src/runtime/local-actor-identity.ts +4 -50
  1258. package/src/runtime/local-principal-trust.ts +1 -1
  1259. package/src/runtime/member-verdict-cache.ts +0 -0
  1260. package/src/runtime/migrations/__tests__/gcs-signed-url.test.ts +51 -1
  1261. package/src/runtime/migrations/__tests__/origin-mode.test.ts +62 -0
  1262. package/src/runtime/migrations/gcs-signed-url.ts +21 -7
  1263. package/src/runtime/migrations/origin-mode.ts +16 -11
  1264. package/src/runtime/pending-interactions.ts +8 -3
  1265. package/src/runtime/routes/__tests__/acp-routes.test.ts +0 -7
  1266. package/src/runtime/routes/__tests__/backup-routes.test.ts +0 -7
  1267. package/src/runtime/routes/__tests__/bookmark-routes.test.ts +0 -1
  1268. package/src/runtime/routes/__tests__/channel-verification-revoke.test.ts +0 -16
  1269. package/src/runtime/routes/__tests__/client-routes.test.ts +0 -7
  1270. package/src/runtime/routes/__tests__/connection-routes-vs-cli-parity.test.ts +4 -5
  1271. package/src/runtime/routes/__tests__/consolidation-routes.test.ts +8 -8
  1272. package/src/runtime/routes/__tests__/contact-routes.test.ts +2 -3
  1273. package/src/runtime/routes/__tests__/content-source-routes.test.ts +0 -7
  1274. package/src/runtime/routes/__tests__/conversation-compaction-routes.test.ts +31 -9
  1275. package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +0 -7
  1276. package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +0 -8
  1277. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +260 -11
  1278. package/src/runtime/routes/__tests__/conversation-surface-routes.test.ts +0 -7
  1279. package/src/runtime/routes/__tests__/default-provider-routes.test.ts +499 -0
  1280. package/src/runtime/routes/__tests__/global-search-routes.test.ts +0 -1
  1281. package/src/runtime/routes/__tests__/inference-callsites-routes.test.ts +54 -0
  1282. package/src/runtime/routes/__tests__/inference-models-routes.test.ts +54 -0
  1283. package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +403 -0
  1284. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +219 -6
  1285. package/src/runtime/routes/__tests__/inference-routes-registration.test.ts +42 -0
  1286. package/src/runtime/routes/__tests__/invite-relay-routes.test.ts +70 -15
  1287. package/src/runtime/routes/__tests__/monitoring-routes.test.ts +1 -1
  1288. package/src/runtime/routes/__tests__/plugins-routes.test.ts +182 -38
  1289. package/src/runtime/routes/__tests__/retrospective-routes.test.ts +1 -8
  1290. package/src/runtime/routes/__tests__/sanity-routes.test.ts +0 -7
  1291. package/src/runtime/routes/__tests__/stt-routes.test.ts +0 -9
  1292. package/src/runtime/routes/__tests__/suggest-trust-rule-routes.test.ts +0 -7
  1293. package/src/runtime/routes/__tests__/task-routes.test.ts +2 -2
  1294. package/src/runtime/routes/__tests__/theme-routes.test.ts +66 -0
  1295. package/src/runtime/routes/__tests__/tts-routes.test.ts +0 -7
  1296. package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +1 -9
  1297. package/src/runtime/routes/__tests__/wake-conversation-routes.test.ts +0 -7
  1298. package/src/runtime/routes/acp-routes.ts +20 -14
  1299. package/src/runtime/routes/approval-routes.ts +21 -2
  1300. package/src/runtime/routes/canned-message-complete.ts +82 -0
  1301. package/src/runtime/routes/channel-metadata.ts +49 -0
  1302. package/src/runtime/routes/contact-routes.ts +75 -43
  1303. package/src/runtime/routes/conversation-attention-routes.ts +1 -1
  1304. package/src/runtime/routes/conversation-cli-routes.ts +2 -2
  1305. package/src/runtime/routes/conversation-compaction-routes.ts +5 -0
  1306. package/src/runtime/routes/conversation-management-routes.ts +147 -2
  1307. package/src/runtime/routes/conversation-query-routes.ts +378 -43
  1308. package/src/runtime/routes/conversation-routes.ts +89 -181
  1309. package/src/runtime/routes/conversation-starter-routes.ts +12 -23
  1310. package/src/runtime/routes/credential-prompt-routes.ts +48 -4
  1311. package/src/runtime/routes/credential-request-routes.ts +85 -0
  1312. package/src/runtime/routes/credential-routes.ts +40 -5
  1313. package/src/runtime/routes/default-provider-routes.ts +132 -0
  1314. package/src/runtime/routes/errors.ts +17 -0
  1315. package/src/runtime/routes/events-routes.ts +158 -39
  1316. package/src/runtime/routes/guardian-approval-interception.ts +1 -1
  1317. package/src/runtime/routes/http-adapter.ts +1 -1
  1318. package/src/runtime/routes/inbound-message-handler.ts +61 -6
  1319. package/src/runtime/routes/inbound-stages/acl-enforcement.test.ts +58 -26
  1320. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +73 -87
  1321. package/src/runtime/routes/inbound-stages/admission-policy.ts +2 -1
  1322. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +1 -8
  1323. package/src/runtime/routes/inbound-stages/background-dispatch.ts +3 -2
  1324. package/src/runtime/routes/inbound-stages/bootstrap-intercept.test.ts +0 -5
  1325. package/src/runtime/routes/inbound-stages/edit-intercept.ts +5 -1
  1326. package/src/runtime/routes/inbound-stages/escalation-intercept.test.ts +0 -5
  1327. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +0 -9
  1328. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +0 -9
  1329. package/src/runtime/routes/inbound-stages/reaction-intercept.test.ts +302 -0
  1330. package/src/runtime/routes/inbound-stages/reaction-intercept.ts +27 -37
  1331. package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +1 -1
  1332. package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +0 -9
  1333. package/src/runtime/routes/index.ts +16 -4
  1334. package/src/runtime/routes/inference-callsites-routes.ts +211 -0
  1335. package/src/runtime/routes/inference-models-routes.ts +104 -0
  1336. package/src/runtime/routes/inference-profile-session-handler.ts +2 -1
  1337. package/src/runtime/routes/inference-profiles-routes.ts +717 -0
  1338. package/src/runtime/routes/inference-provider-connection-routes.ts +38 -1
  1339. package/src/runtime/routes/inference-send-routes.ts +2 -1
  1340. package/src/runtime/routes/integrations/slack/channels.ts +26 -4
  1341. package/src/runtime/routes/integrations/slack/users.ts +78 -0
  1342. package/src/runtime/routes/internal-telemetry-routes.ts +44 -0
  1343. package/src/runtime/routes/llm-call-sites-routes.ts +2 -1
  1344. package/src/runtime/routes/llm-request-logs-access.ts +19 -0
  1345. package/src/runtime/routes/migration-routes.ts +94 -47
  1346. package/src/runtime/routes/monitoring-routes.ts +1 -1
  1347. package/src/runtime/routes/notification-routes.ts +1 -1
  1348. package/src/runtime/routes/playground/__tests__/reset-circuit.test.ts +0 -7
  1349. package/src/runtime/routes/plugins-routes.ts +169 -16
  1350. package/src/runtime/routes/schedule-routes.ts +38 -1
  1351. package/src/runtime/routes/secret-routes.ts +0 -7
  1352. package/src/runtime/routes/settings-routes.ts +160 -22
  1353. package/src/runtime/routes/skills-routes.ts +6 -0
  1354. package/src/runtime/routes/subagents-routes.ts +17 -18
  1355. package/src/runtime/routes/surface-action-routes.ts +4 -2
  1356. package/src/runtime/routes/theme-routes.ts +36 -0
  1357. package/src/runtime/routes/types.ts +2 -1
  1358. package/src/runtime/routes/workflow-routes.ts +1 -1
  1359. package/src/runtime/services/__tests__/auto-analysis-guard.test.ts +2 -16
  1360. package/src/runtime/services/__tests__/conversation-serializer.test.ts +0 -1
  1361. package/src/runtime/services/auto-analysis-guard.ts +5 -5
  1362. package/src/runtime/services/conversation-serializer.ts +2 -3
  1363. package/src/runtime/sync/resource-sync-events.ts +4 -0
  1364. package/src/runtime/trust-context-resolver.ts +9 -9
  1365. package/src/runtime/trust-verdict-consumer.ts +46 -5
  1366. package/src/schedule/scheduler.ts +61 -18
  1367. package/src/security/__tests__/oauth2-device-code.test.ts +12 -60
  1368. package/src/security/__tests__/provider-key-env-fallback.test.ts +0 -10
  1369. package/src/security/__tests__/secret-normalize.test.ts +96 -0
  1370. package/src/security/secret-normalize.ts +41 -0
  1371. package/src/security/secret-patterns.ts +3 -0
  1372. package/src/security/secure-keys.ts +222 -49
  1373. package/src/sequence/engine.ts +15 -6
  1374. package/src/signals/user-message.ts +3 -1
  1375. package/src/skills/available-skills.ts +134 -0
  1376. package/src/skills/install-meta.ts +8 -0
  1377. package/src/skills/managed-store.ts +10 -0
  1378. package/src/stt/types.ts +33 -2
  1379. package/src/subagent/manager.ts +5 -6
  1380. package/src/tasks/SPEC.md +10 -17
  1381. package/src/tasks/tool-sanitizer.ts +3 -1
  1382. package/src/telemetry/__tests__/config-setting-events-store.test.ts +126 -0
  1383. package/src/telemetry/__tests__/flush-checkpoints.test.ts +56 -0
  1384. package/src/telemetry/__tests__/watchdog-events-store.test.ts +0 -8
  1385. package/src/telemetry/config-setting-events-store.ts +97 -0
  1386. package/src/telemetry/flush-checkpoints.ts +70 -0
  1387. package/src/telemetry/skill-loaded-events-store.test.ts +0 -8
  1388. package/src/telemetry/telemetry-event-sources.ts +571 -0
  1389. package/src/telemetry/tool-audit.ts +285 -0
  1390. package/src/telemetry/tool-executed-events-store.test.ts +19 -27
  1391. package/src/telemetry/tool-executed-events-store.ts +5 -5
  1392. package/src/telemetry/turn-events-store.ts +36 -0
  1393. package/src/telemetry/turn-outcome.ts +116 -0
  1394. package/src/telemetry/turn-trace-store.test.ts +156 -8
  1395. package/src/telemetry/turn-trace-store.ts +53 -4
  1396. package/src/telemetry/types.ts +91 -2
  1397. package/src/telemetry/usage-telemetry-reporter.test.ts +373 -119
  1398. package/src/telemetry/usage-telemetry-reporter.ts +110 -675
  1399. package/src/telemetry/watchdog-direct-emit.test.ts +0 -5
  1400. package/src/theme/workspace-theme.test.ts +269 -0
  1401. package/src/theme/workspace-theme.ts +304 -0
  1402. package/src/tools/AGENTS.md +0 -26
  1403. package/src/tools/acp/spawn.test.ts +0 -7
  1404. package/src/tools/browser/browser-execution.ts +99 -75
  1405. package/src/tools/browser/cdp-client/__tests__/cdp-inspect-client.test.ts +1 -11
  1406. package/src/tools/browser/cdp-client/__tests__/local-cdp-client.test.ts +0 -10
  1407. package/src/tools/browser/network-recorder.test.ts +0 -11
  1408. package/src/tools/document/document-comment-tool.test.ts +1 -7
  1409. package/src/tools/execution-target.test.ts +31 -0
  1410. package/src/tools/execution-target.ts +1 -1
  1411. package/src/tools/executor.ts +63 -275
  1412. package/src/tools/filesystem/write.ts +0 -2
  1413. package/src/tools/host-terminal/host-shell.test.ts +0 -7
  1414. package/src/tools/host-terminal/host-shell.ts +6 -49
  1415. package/src/tools/permission-checker.ts +33 -81
  1416. package/src/tools/registry.ts +261 -166
  1417. package/src/tools/run-standalone.ts +6 -13
  1418. package/src/tools/side-effects.ts +0 -1
  1419. package/src/tools/skills/find-similar-skills.test.ts +5 -9
  1420. package/src/tools/skills/scaffold-managed.ts +31 -4
  1421. package/src/tools/skills/skill-script-runner.ts +17 -31
  1422. package/src/tools/skills/skill-tool-factory.ts +2 -6
  1423. package/src/tools/subagent/read.ts +6 -4
  1424. package/src/tools/subagent/spawn.ts +28 -29
  1425. package/src/tools/terminal/safe-env.ts +8 -3
  1426. package/src/tools/terminal/shell.test.ts +0 -6
  1427. package/src/tools/terminal/shell.ts +1 -50
  1428. package/src/tools/tool-approval-handler.ts +99 -166
  1429. package/src/tools/tool-defaults.ts +2 -6
  1430. package/src/tools/tool-manifest.ts +0 -49
  1431. package/src/{events/tool-profiling-listener.ts → tools/tool-profiler.ts} +64 -32
  1432. package/src/tools/tool-types.ts +4 -95
  1433. package/src/tools/types.ts +27 -140
  1434. package/src/tools/workflows/manage-workflows.ts +2 -1
  1435. package/src/tools/workflows/run-workflow.test.ts +6 -1
  1436. package/src/tools/workflows/run-workflow.ts +2 -4
  1437. package/src/tools/workspace-tools/loader.ts +26 -14
  1438. package/src/tts/__tests__/pcm-chunk-aligner.test.ts +48 -0
  1439. package/src/tts/__tests__/pcm-sample-rates.test.ts +59 -0
  1440. package/src/tts/__tests__/provider-adapters.test.ts +891 -22
  1441. package/src/tts/__tests__/provider-catalog.test.ts +43 -10
  1442. package/src/tts/__tests__/speakable-segments.test.ts +427 -0
  1443. package/src/tts/__tests__/synthesis-stream.test.ts +5 -1
  1444. package/src/tts/pcm-chunk-aligner.ts +29 -0
  1445. package/src/tts/pcm-sample-rates.ts +47 -0
  1446. package/src/tts/provider-definition.ts +6 -7
  1447. package/src/tts/providers/__tests__/xai-tts-socket.test.ts +406 -0
  1448. package/src/tts/providers/deepgram-provider.ts +160 -102
  1449. package/src/tts/providers/elevenlabs-provider.ts +182 -105
  1450. package/src/tts/providers/fish-audio-provider.ts +72 -105
  1451. package/src/tts/providers/xai-provider.ts +131 -22
  1452. package/src/tts/providers/xai-tts-socket.ts +311 -0
  1453. package/src/tts/speakable-segments.ts +188 -0
  1454. package/src/tts/stream-read.ts +136 -0
  1455. package/src/tts/synthesis-stream.ts +9 -5
  1456. package/src/tts/synthesize-text.ts +15 -5
  1457. package/src/tts/types.ts +21 -0
  1458. package/src/usage/attribution.ts +43 -9
  1459. package/src/util/errors.ts +26 -0
  1460. package/src/util/image-conversion.ts +23 -5
  1461. package/src/util/logger.ts +67 -68
  1462. package/src/util/platform.ts +19 -60
  1463. package/src/util/pricing.ts +14 -11
  1464. package/src/util/process-tree.ts +25 -13
  1465. package/src/util/provider-error-patterns.ts +21 -0
  1466. package/src/util/sqlite-query-label.ts +51 -0
  1467. package/src/util/sqlite-retry.ts +14 -2
  1468. package/src/util/unicode.ts +1 -1
  1469. package/src/watcher/__tests__/engine-auth-notify.test.ts +316 -0
  1470. package/src/watcher/engine.ts +87 -2
  1471. package/src/workflows/capabilities.test.ts +2 -32
  1472. package/src/workflows/capabilities.ts +18 -25
  1473. package/src/workflows/engine-integration.test.ts +1 -7
  1474. package/src/workflows/engine.test.ts +2 -10
  1475. package/src/workflows/engine.ts +1 -1
  1476. package/src/workflows/fanout-load.test.ts +1 -1
  1477. package/src/workflows/journal-store.test.ts +1 -9
  1478. package/src/workflows/leaf-runner.test.ts +0 -6
  1479. package/src/workflows/leaf-runner.ts +4 -5
  1480. package/src/workflows/library.test.ts +1 -9
  1481. package/src/workflows/run-manager.test.ts +2 -10
  1482. package/src/workflows/run-manager.ts +2 -4
  1483. package/src/workspace/adaptive-thinking-repair.ts +1 -1
  1484. package/src/workspace/custom-profile-ensure.ts +133 -0
  1485. package/src/workspace/default-provider-ensure.ts +162 -0
  1486. package/src/workspace/git-service.ts +160 -15
  1487. package/src/workspace/migrations/028-recover-conversations-from-disk-view.ts +0 -1
  1488. package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +1 -1
  1489. package/src/workspace/migrations/126-strip-managed-profile-bodies.ts +92 -0
  1490. package/src/workspace/migrations/127-backfill-default-provider.ts +176 -0
  1491. package/src/workspace/migrations/128-repair-stale-openrouter-grok-model-ids.ts +112 -0
  1492. package/src/workspace/migrations/129-remove-analyze-conversation-config.ts +59 -0
  1493. package/src/workspace/migrations/registry.ts +8 -0
  1494. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/grants.test.ts +0 -686
  1495. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/grants.ts +0 -184
  1496. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/rendering.ts +0 -135
  1497. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/grants.test.ts +0 -686
  1498. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/grants.ts +0 -184
  1499. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/rendering.ts +0 -135
  1500. package/node_modules/@vellumai/service-contracts/src/__tests__/grants.test.ts +0 -686
  1501. package/node_modules/@vellumai/service-contracts/src/grants.ts +0 -184
  1502. package/node_modules/@vellumai/service-contracts/src/rendering.ts +0 -135
  1503. package/scripts/generate-bundled-tool-registry.ts +0 -223
  1504. package/scripts/test.sh +0 -494
  1505. package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +0 -255
  1506. package/src/__tests__/agent-loop-sentry-hygiene.test.ts +0 -137
  1507. package/src/__tests__/auto-analysis-end-to-end.test.ts +0 -621
  1508. package/src/__tests__/auto-analysis-prompt.test.ts +0 -50
  1509. package/src/__tests__/config-analysis.test.ts +0 -83
  1510. package/src/__tests__/conversation-analysis-routes.test.ts +0 -156
  1511. package/src/__tests__/credential-execution-admin-cli.test.ts +0 -504
  1512. package/src/__tests__/credential-execution-approval-bridge.test.ts +0 -297
  1513. package/src/__tests__/credential-execution-feature-gates.test.ts +0 -185
  1514. package/src/__tests__/credential-execution-shell-lockdown.test.ts +0 -120
  1515. package/src/__tests__/credential-execution-tools.test.ts +0 -289
  1516. package/src/__tests__/db-proxy-transaction.test.ts +0 -206
  1517. package/src/__tests__/event-bus.test.ts +0 -222
  1518. package/src/__tests__/reaction-intercept-cold-cache-warm.test.ts +0 -141
  1519. package/src/__tests__/reaction-intercept-member-verdict-warm.test.ts +0 -157
  1520. package/src/__tests__/skill-memory.test.ts +0 -85
  1521. package/src/__tests__/skill-runtime-path.test.ts +0 -63
  1522. package/src/__tests__/tool-domain-event-publisher.test.ts +0 -216
  1523. package/src/__tests__/tool-metrics-listener.test.ts +0 -194
  1524. package/src/__tests__/tool-profiling-listener.test.ts +0 -219
  1525. package/src/cli/commands/credential-execution.ts +0 -362
  1526. package/src/config/__tests__/bundled-tool-registry-guard.test.ts +0 -120
  1527. package/src/config/bundled-tool-registry.ts +0 -281
  1528. package/src/config/schemas/analysis.ts +0 -32
  1529. package/src/credential-execution/approval-bridge.ts +0 -320
  1530. package/src/credential-execution/feature-gates.ts +0 -61
  1531. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +0 -449
  1532. package/src/events/bus.ts +0 -177
  1533. package/src/events/domain-events.ts +0 -68
  1534. package/src/events/tool-audit-listener.ts +0 -184
  1535. package/src/events/tool-domain-event-publisher.ts +0 -90
  1536. package/src/events/tool-metrics-listener.ts +0 -134
  1537. package/src/events/tool-permission-telemetry-listener.ts +0 -44
  1538. package/src/instrument.ts +0 -213
  1539. package/src/ipc/routes/db-proxy-transaction.ts +0 -151
  1540. package/src/plugins/defaults/memory/__tests__/find-analysis-conversation.test.ts +0 -197
  1541. package/src/runtime/routes/conversation-analysis-routes.ts +0 -75
  1542. package/src/runtime/routes/work-items-routes.test.ts +0 -74
  1543. package/src/runtime/routes/work-items-routes.ts +0 -881
  1544. package/src/runtime/services/__tests__/analyze-conversation.test.ts +0 -477
  1545. package/src/runtime/services/__tests__/auto-analysis-enqueue.test.ts +0 -386
  1546. package/src/runtime/services/__tests__/auto-analysis-prompt.test.ts +0 -53
  1547. package/src/runtime/services/__tests__/conversation-analyze-job.test.ts +0 -213
  1548. package/src/runtime/services/__tests__/manual-analysis-prompt.test.ts +0 -41
  1549. package/src/runtime/services/analyze-conversation.ts +0 -332
  1550. package/src/runtime/services/auto-analysis-enqueue.ts +0 -130
  1551. package/src/runtime/services/auto-analysis-prompt.ts +0 -55
  1552. package/src/runtime/services/conversation-analyze-job.ts +0 -53
  1553. package/src/skills/skill-memory.ts +0 -44
  1554. package/src/tools/credential-execution/make-authenticated-request.ts +0 -198
  1555. package/src/tools/credential-execution/manage-secure-command-tool.ts +0 -371
  1556. package/src/tools/credential-execution/run-authenticated-command.ts +0 -261
  1557. package/src/tools/flag-gated-tools.test.ts +0 -76
  1558. package/src/util/sentry-log-stream.ts +0 -51
@@ -4,15 +4,6 @@ import { afterEach, describe, expect, jest, mock, test } from "bun:test";
4
4
  // Module mocks
5
5
  // ---------------------------------------------------------------------------
6
6
 
7
- mock.module("../util/logger.js", () => ({
8
- getLogger: () => ({
9
- info: () => {},
10
- warn: () => {},
11
- error: () => {},
12
- debug: () => {},
13
- }),
14
- }));
15
-
16
7
  // Mock TTS provider for synthesis tests
17
8
  const mockSynthesize = jest.fn();
18
9
  const mockProvider = {
@@ -25,11 +16,14 @@ mock.module("../calls/resolve-call-tts-provider.js", () => ({
25
16
  resolveCallTtsProvider: jest.fn(() => ({
26
17
  provider: mockProvider,
27
18
  useSynthesizedPath: false,
28
- audioFormat: "wav" as const,
19
+ audioFormat: "pcm" as const,
29
20
  })),
30
21
  }));
31
22
 
32
- import { mulawToPcm16 } from "../calls/media-stream-audio-transcode.js";
23
+ import {
24
+ mulawToPcm16,
25
+ pcm16ToMulaw,
26
+ } from "../calls/media-stream-audio-transcode.js";
33
27
  import { MediaStreamOutput } from "../calls/media-stream-output.js";
34
28
  import { resolveCallTtsProvider } from "../calls/resolve-call-tts-provider.js";
35
29
 
@@ -78,10 +72,22 @@ function createMockWs() {
78
72
  // Helpers
79
73
  // ---------------------------------------------------------------------------
80
74
 
81
- /** Wait for async playback queue to drain. */
82
- async function drain(): Promise<void> {
83
- // Allow microtasks and the drain loop to run
84
- await new Promise((resolve) => setTimeout(resolve, 10));
75
+ /**
76
+ * Wait for async playback to progress. With `until`, polls (up to 2 s) for
77
+ * the condition so assertions don't race the queue on slow CI machines;
78
+ * without it, a single fixed tick (for negative assertions).
79
+ */
80
+ async function drain(until?: () => boolean): Promise<void> {
81
+ const deadline = Date.now() + 2000;
82
+ for (;;) {
83
+ await new Promise((resolve) => setTimeout(resolve, 10));
84
+ if (!until || until() || Date.now() > deadline) return;
85
+ }
86
+ }
87
+
88
+ /** Number of sent messages with the given event type. */
89
+ function countEvents(sent: string[], event: string): number {
90
+ return sent.filter((s) => JSON.parse(s).event === event).length;
85
91
  }
86
92
 
87
93
  /** Generate a minimal valid WAV buffer with PCM data. */
@@ -144,6 +150,47 @@ function concatMulawPayloads(sent: string[]): Buffer {
144
150
  );
145
151
  }
146
152
 
153
+ /** Encode samples as a raw PCM16 LE buffer. */
154
+ function pcm16Buffer(samples: number[]): Buffer {
155
+ const buf = Buffer.alloc(samples.length * 2);
156
+ for (let i = 0; i < samples.length; i++) {
157
+ buf.writeInt16LE(samples[i], i * 2);
158
+ }
159
+ return buf;
160
+ }
161
+
162
+ /** Keep every other PCM16 sample (16 kHz -> 8 kHz decimation). */
163
+ function decimateByTwo(pcm: Buffer): Buffer {
164
+ const outCount = Math.floor(pcm.length / 2 / 2);
165
+ const out = Buffer.alloc(outCount * 2);
166
+ for (let i = 0; i < outCount; i++) {
167
+ out.writeInt16LE(pcm.readInt16LE(i * 4), i * 2);
168
+ }
169
+ return out;
170
+ }
171
+
172
+ /** Outputs created during the current test, closed in afterEach. */
173
+ const activeOutputs: MediaStreamOutput[] = [];
174
+
175
+ /** Construct a MediaStreamOutput tracked for afterEach teardown. */
176
+ function makeOutput(
177
+ ws: import("bun").ServerWebSocket<unknown>,
178
+ streamSid: string,
179
+ ): MediaStreamOutput {
180
+ const output = new MediaStreamOutput(ws, streamSid);
181
+ activeOutputs.push(output);
182
+ return output;
183
+ }
184
+
185
+ /** Install a resolveCallTtsProvider mock returning the given provider. */
186
+ function useProvider(provider: unknown): void {
187
+ mockResolveCallTtsProvider.mockImplementation(() => ({
188
+ provider,
189
+ useSynthesizedPath: false,
190
+ audioFormat: "pcm" as const,
191
+ }));
192
+ }
193
+
147
194
  /** Count sign changes in a PCM16 LE buffer (proxy for tone frequency). */
148
195
  function countZeroCrossings(pcm: Buffer): number {
149
196
  const samples = Math.floor(pcm.length / 2);
@@ -162,12 +209,19 @@ function countZeroCrossings(pcm: Buffer): number {
162
209
  // ---------------------------------------------------------------------------
163
210
 
164
211
  afterEach(() => {
212
+ // Close every output so an in-flight synthesize item from a slow test
213
+ // bails on its staleness check instead of calling the next test's
214
+ // (re-)mocked provider.
215
+ for (const output of activeOutputs) {
216
+ output.markClosed();
217
+ }
218
+ activeOutputs.length = 0;
165
219
  mockSynthesize.mockReset();
166
220
  // Restore the default resolveCallTtsProvider mock
167
221
  mockResolveCallTtsProvider.mockImplementation(() => ({
168
222
  provider: mockProvider,
169
223
  useSynthesizedPath: false,
170
- audioFormat: "wav" as const,
224
+ audioFormat: "pcm" as const,
171
225
  }));
172
226
  });
173
227
 
@@ -185,11 +239,11 @@ describe("MediaStreamOutput", () => {
185
239
  });
186
240
 
187
241
  const { ws, sent } = createMockWs();
188
- const output = new MediaStreamOutput(ws, "stream-1");
242
+ const output = makeOutput(ws, "stream-1");
189
243
  output.sendTextToken("hello ", false);
190
244
  output.sendTextToken("world", true);
191
245
 
192
- await drain();
246
+ await drain(() => countEvents(sent, "mark") > 0);
193
247
 
194
248
  // Should have sent media frames (from synthesis) and a mark
195
249
  const events = sent.map((s) => JSON.parse(s).event);
@@ -203,10 +257,10 @@ describe("MediaStreamOutput", () => {
203
257
 
204
258
  test("empty token with last: true sends only end-of-turn mark (no synthesis)", async () => {
205
259
  const { ws, sent } = createMockWs();
206
- const output = new MediaStreamOutput(ws, "stream-1");
260
+ const output = makeOutput(ws, "stream-1");
207
261
  output.sendTextToken("", true);
208
262
 
209
- await drain();
263
+ await drain(() => countEvents(sent, "mark") > 0);
210
264
 
211
265
  // Should send only a mark, no media frames
212
266
  expect(sent).toHaveLength(1);
@@ -220,7 +274,7 @@ describe("MediaStreamOutput", () => {
220
274
 
221
275
  test("non-last tokens accumulate without sending", () => {
222
276
  const { ws, sent } = createMockWs();
223
- const output = new MediaStreamOutput(ws, "stream-1");
277
+ const output = makeOutput(ws, "stream-1");
224
278
  output.sendTextToken("hello ", false);
225
279
  output.sendTextToken("world ", false);
226
280
  expect(sent).toHaveLength(0);
@@ -228,7 +282,7 @@ describe("MediaStreamOutput", () => {
228
282
 
229
283
  test("does not send when closed", () => {
230
284
  const { ws, sent } = createMockWs();
231
- const output = new MediaStreamOutput(ws, "stream-1");
285
+ const output = makeOutput(ws, "stream-1");
232
286
  output.endSession();
233
287
  output.sendTextToken("hello", true);
234
288
  expect(sent).toHaveLength(0);
@@ -238,7 +292,7 @@ describe("MediaStreamOutput", () => {
238
292
  describe("CallTransport interface — sendPlayUrl", () => {
239
293
  test("enqueues a fetch-url item in the playback queue", () => {
240
294
  const { ws } = createMockWs();
241
- const output = new MediaStreamOutput(ws, "stream-1");
295
+ const output = makeOutput(ws, "stream-1");
242
296
  output.sendPlayUrl("https://example.com/audio.mp3");
243
297
  // The queue should have one item (the fetch will fail since
244
298
  // there's no real server, but the enqueueing is synchronous)
@@ -247,7 +301,7 @@ describe("MediaStreamOutput", () => {
247
301
 
248
302
  test("does not enqueue when closed", () => {
249
303
  const { ws, sent } = createMockWs();
250
- const output = new MediaStreamOutput(ws, "stream-1");
304
+ const output = makeOutput(ws, "stream-1");
251
305
  output.endSession();
252
306
  output.sendPlayUrl("https://example.com/audio.mp3");
253
307
  expect(sent).toHaveLength(0);
@@ -257,7 +311,7 @@ describe("MediaStreamOutput", () => {
257
311
  describe("CallTransport interface — endSession", () => {
258
312
  test("closes the WebSocket with code 1000", () => {
259
313
  const mock = createMockWs();
260
- const output = new MediaStreamOutput(mock.ws, "stream-1");
314
+ const output = makeOutput(mock.ws, "stream-1");
261
315
  output.endSession("test-reason");
262
316
  expect(mock.closed).toBe(true);
263
317
  expect(mock.closeCode).toBe(1000);
@@ -266,7 +320,7 @@ describe("MediaStreamOutput", () => {
266
320
 
267
321
  test("uses default reason when none provided", () => {
268
322
  const mock = createMockWs();
269
- const output = new MediaStreamOutput(mock.ws, "stream-1");
323
+ const output = makeOutput(mock.ws, "stream-1");
270
324
  output.endSession();
271
325
  expect(mock.closed).toBe(true);
272
326
  expect(mock.closeReason).toBe("session-ended");
@@ -274,7 +328,7 @@ describe("MediaStreamOutput", () => {
274
328
 
275
329
  test("is idempotent", () => {
276
330
  const mock = createMockWs();
277
- const output = new MediaStreamOutput(mock.ws, "stream-1");
331
+ const output = makeOutput(mock.ws, "stream-1");
278
332
  output.endSession("first");
279
333
  // Second call should not throw (ws.close would throw on already-closed)
280
334
  output.endSession("second");
@@ -285,7 +339,7 @@ describe("MediaStreamOutput", () => {
285
339
  describe("sendAudioPayload", () => {
286
340
  test("sends a media command with the base64 payload", () => {
287
341
  const { ws, sent } = createMockWs();
288
- const output = new MediaStreamOutput(ws, "MZ-stream-1");
342
+ const output = makeOutput(ws, "MZ-stream-1");
289
343
  output.sendAudioPayload("dGVzdA==");
290
344
 
291
345
  expect(sent).toHaveLength(1);
@@ -299,7 +353,7 @@ describe("MediaStreamOutput", () => {
299
353
 
300
354
  test("does not send when closed", () => {
301
355
  const { ws, sent } = createMockWs();
302
- const output = new MediaStreamOutput(ws, "MZ-stream-1");
356
+ const output = makeOutput(ws, "MZ-stream-1");
303
357
  output.endSession();
304
358
  output.sendAudioPayload("dGVzdA==");
305
359
  // Only the close would have happened, no media sent
@@ -310,7 +364,7 @@ describe("MediaStreamOutput", () => {
310
364
  describe("sendMark", () => {
311
365
  test("sends a mark command with the given name", () => {
312
366
  const { ws, sent } = createMockWs();
313
- const output = new MediaStreamOutput(ws, "MZ-stream-1");
367
+ const output = makeOutput(ws, "MZ-stream-1");
314
368
  output.sendMark("end-of-turn");
315
369
 
316
370
  expect(sent).toHaveLength(1);
@@ -324,7 +378,7 @@ describe("MediaStreamOutput", () => {
324
378
 
325
379
  test("does not send when closed", () => {
326
380
  const { ws, sent } = createMockWs();
327
- const output = new MediaStreamOutput(ws, "MZ-stream-1");
381
+ const output = makeOutput(ws, "MZ-stream-1");
328
382
  output.endSession();
329
383
  output.sendMark("end-of-turn");
330
384
  expect(sent).toHaveLength(0);
@@ -334,7 +388,7 @@ describe("MediaStreamOutput", () => {
334
388
  describe("clearAudio — barge-in", () => {
335
389
  test("sends a clear command to Twilio", () => {
336
390
  const { ws, sent } = createMockWs();
337
- const output = new MediaStreamOutput(ws, "MZ-stream-1");
391
+ const output = makeOutput(ws, "MZ-stream-1");
338
392
  output.clearAudio();
339
393
 
340
394
  expect(sent).toHaveLength(1);
@@ -359,7 +413,7 @@ describe("MediaStreamOutput", () => {
359
413
  );
360
414
 
361
415
  const { ws, sent } = createMockWs();
362
- const output = new MediaStreamOutput(ws, "MZ-stream-1");
416
+ const output = makeOutput(ws, "MZ-stream-1");
363
417
 
364
418
  // Queue synthesis
365
419
  output.sendTextToken("hello world", true);
@@ -379,7 +433,7 @@ describe("MediaStreamOutput", () => {
379
433
 
380
434
  test("does not send when closed", () => {
381
435
  const { ws, sent } = createMockWs();
382
- const output = new MediaStreamOutput(ws, "MZ-stream-1");
436
+ const output = makeOutput(ws, "MZ-stream-1");
383
437
  output.endSession();
384
438
  output.clearAudio();
385
439
  expect(sent).toHaveLength(0);
@@ -389,7 +443,7 @@ describe("MediaStreamOutput", () => {
389
443
  describe("clearBufferedAudio — rejected barge-in", () => {
390
444
  test("sends a clear command to Twilio", () => {
391
445
  const { ws, sent } = createMockWs();
392
- const output = new MediaStreamOutput(ws, "MZ-stream-1");
446
+ const output = makeOutput(ws, "MZ-stream-1");
393
447
  output.clearBufferedAudio();
394
448
 
395
449
  expect(sent).toHaveLength(1);
@@ -413,12 +467,12 @@ describe("MediaStreamOutput", () => {
413
467
  );
414
468
 
415
469
  const { ws, sent } = createMockWs();
416
- const output = new MediaStreamOutput(ws, "MZ-stream-1");
470
+ const output = makeOutput(ws, "MZ-stream-1");
417
471
 
418
472
  output.sendTextToken("hello world", true);
419
473
  output.clearBufferedAudio();
420
474
 
421
- await new Promise((resolve) => setTimeout(resolve, 60));
475
+ await drain(() => countEvents(sent, "media") > 0);
422
476
 
423
477
  // Unlike clearAudio, the in-flight synthesis is not aborted:
424
478
  // its frames go out once ready.
@@ -439,7 +493,7 @@ describe("MediaStreamOutput", () => {
439
493
  );
440
494
 
441
495
  const { ws } = createMockWs();
442
- const output = new MediaStreamOutput(ws, "MZ-stream-1");
496
+ const output = makeOutput(ws, "MZ-stream-1");
443
497
 
444
498
  let fired = 0;
445
499
  output.setAudioStartCallback(() => {
@@ -448,20 +502,76 @@ describe("MediaStreamOutput", () => {
448
502
  output.sendTextToken("hello world", true);
449
503
  output.clearBufferedAudio();
450
504
 
451
- await new Promise((resolve) => setTimeout(resolve, 60));
505
+ await drain(() => fired === 1);
452
506
 
453
507
  expect(fired).toBe(1);
454
508
  });
455
509
 
456
510
  test("does not send when closed", () => {
457
511
  const { ws, sent } = createMockWs();
458
- const output = new MediaStreamOutput(ws, "MZ-stream-1");
512
+ const output = makeOutput(ws, "MZ-stream-1");
459
513
  output.endSession();
460
514
  output.clearBufferedAudio();
461
515
  expect(sent).toHaveLength(0);
462
516
  });
463
517
  });
464
518
 
519
+ describe("cancelPendingSpeech — turn abort", () => {
520
+ test("queued-but-unplayed synthesis never produces frames; the next turn plays normally", async () => {
521
+ const wav = makeWavBuffer(sineSamples(400, 440, 8000));
522
+ // Slow synthesis so the abort lands while the first segment is
523
+ // in flight and the second is still queued.
524
+ mockSynthesize.mockImplementation(
525
+ () =>
526
+ new Promise((resolve) =>
527
+ setTimeout(
528
+ () => resolve({ audio: wav, contentType: "audio/wav" }),
529
+ 200,
530
+ ),
531
+ ),
532
+ );
533
+
534
+ const { ws, sent } = createMockWs();
535
+ const output = makeOutput(ws, "MZ-stream-1");
536
+
537
+ // Aborted turn: first segment in flight, second still queued.
538
+ output.sendTextToken("Sentence one. Sentence two.", true);
539
+ await drain(() => mockSynthesize.mock.calls.length === 1);
540
+ expect(output.getPlaybackQueueLength()).toBeGreaterThan(0);
541
+
542
+ output.cancelPendingSpeech();
543
+
544
+ // Twilio's buffered audio was flushed too.
545
+ expect(countEvents(sent, "clear")).toBe(1);
546
+
547
+ // Next turn: fast synthesis so its audio goes out.
548
+ mockSynthesize.mockResolvedValue({
549
+ audio: wav,
550
+ contentType: "audio/wav",
551
+ });
552
+ output.sendTextToken("Next turn reply.", true);
553
+ await drain(() => countEvents(sent, "mark") > 0);
554
+
555
+ // The aborted turn's queued segment never synthesized, its
556
+ // in-flight segment produced no frames, and the next turn's
557
+ // audio is the only audio sent.
558
+ const synthesizedTexts = mockSynthesize.mock.calls.map(
559
+ (c) => (c[0] as { text: string }).text,
560
+ );
561
+ expect(synthesizedTexts).toEqual(["Sentence one.", "Next turn reply."]);
562
+ expect(countEvents(sent, "media")).toBeGreaterThan(0);
563
+ expect(totalMulawBytes(sent)).toBe(400);
564
+ });
565
+
566
+ test("does not send when closed", () => {
567
+ const { ws, sent } = createMockWs();
568
+ const output = makeOutput(ws, "MZ-stream-1");
569
+ output.endSession();
570
+ output.cancelPendingSpeech();
571
+ expect(sent).toHaveLength(0);
572
+ });
573
+ });
574
+
465
575
  describe("audio-start signal", () => {
466
576
  function makePlayableWav(): Buffer {
467
577
  const samples = Array.from({ length: 400 }, (_, i) =>
@@ -477,7 +587,7 @@ describe("MediaStreamOutput", () => {
477
587
  });
478
588
 
479
589
  const { ws, sent } = createMockWs();
480
- const output = new MediaStreamOutput(ws, "stream-1");
590
+ const output = makeOutput(ws, "stream-1");
481
591
 
482
592
  let fired = 0;
483
593
  let mediaSentWhenFired = -1;
@@ -489,7 +599,7 @@ describe("MediaStreamOutput", () => {
489
599
  });
490
600
 
491
601
  output.sendTextToken("hello world", true);
492
- await drain();
602
+ await drain(() => countEvents(sent, "mark") > 0);
493
603
 
494
604
  const mediaMessages = sent.filter((s) => JSON.parse(s).event === "media");
495
605
  expect(mediaMessages.length).toBeGreaterThan(1);
@@ -504,25 +614,25 @@ describe("MediaStreamOutput", () => {
504
614
  contentType: "audio/wav",
505
615
  });
506
616
 
507
- const { ws } = createMockWs();
508
- const output = new MediaStreamOutput(ws, "stream-1");
617
+ const { ws, sent } = createMockWs();
618
+ const output = makeOutput(ws, "stream-1");
509
619
 
510
620
  let fired = 0;
511
621
  output.setAudioStartCallback(() => fired++);
512
622
 
513
623
  output.sendTextToken("first", true);
514
- await drain();
624
+ await drain(() => countEvents(sent, "mark") >= 1);
515
625
  expect(fired).toBe(1);
516
626
 
517
627
  // Second item without re-arming — signal already consumed.
518
628
  output.sendTextToken("second", true);
519
- await drain();
629
+ await drain(() => countEvents(sent, "mark") >= 2);
520
630
  expect(fired).toBe(1);
521
631
 
522
632
  // Re-armed — fires again for the next item.
523
633
  output.setAudioStartCallback(() => fired++);
524
634
  output.sendTextToken("third", true);
525
- await drain();
635
+ await drain(() => countEvents(sent, "mark") >= 3);
526
636
  expect(fired).toBe(2);
527
637
  });
528
638
 
@@ -540,7 +650,7 @@ describe("MediaStreamOutput", () => {
540
650
  );
541
651
 
542
652
  const { ws } = createMockWs();
543
- const output = new MediaStreamOutput(ws, "stream-1");
653
+ const output = makeOutput(ws, "stream-1");
544
654
 
545
655
  let fired = 0;
546
656
  output.setAudioStartCallback(() => fired++);
@@ -553,14 +663,14 @@ describe("MediaStreamOutput", () => {
553
663
  });
554
664
 
555
665
  test("an empty end-of-turn (mark only) does not fire the signal", async () => {
556
- const { ws } = createMockWs();
557
- const output = new MediaStreamOutput(ws, "stream-1");
666
+ const { ws, sent } = createMockWs();
667
+ const output = makeOutput(ws, "stream-1");
558
668
 
559
669
  let fired = 0;
560
670
  output.setAudioStartCallback(() => fired++);
561
671
 
562
672
  output.sendTextToken("", true);
563
- await drain();
673
+ await drain(() => countEvents(sent, "mark") > 0);
564
674
  expect(fired).toBe(0);
565
675
  });
566
676
  });
@@ -573,8 +683,8 @@ describe("MediaStreamOutput", () => {
573
683
  contentType: "audio/wav",
574
684
  });
575
685
 
576
- const { ws } = createMockWs();
577
- const output = new MediaStreamOutput(ws, "stream-1");
686
+ const { ws, sent } = createMockWs();
687
+ const output = makeOutput(ws, "stream-1");
578
688
 
579
689
  // Tokens buffered mid-turn; a barge-in signal the controller ignores
580
690
  // (turn still processing) flushes queued audio but must not truncate
@@ -582,7 +692,7 @@ describe("MediaStreamOutput", () => {
582
692
  output.sendTextToken("hello ", false);
583
693
  output.clearAudio();
584
694
  output.sendTextToken("world", true);
585
- await drain();
695
+ await drain(() => countEvents(sent, "mark") > 0);
586
696
 
587
697
  expect(mockSynthesize).toHaveBeenCalledTimes(1);
588
698
  expect(mockSynthesize.mock.calls[0][0].text).toBe("hello world");
@@ -590,12 +700,12 @@ describe("MediaStreamOutput", () => {
590
700
 
591
701
  test("discardPendingText drops accumulated text so no synthesis occurs", async () => {
592
702
  const { ws, sent } = createMockWs();
593
- const output = new MediaStreamOutput(ws, "stream-1");
703
+ const output = makeOutput(ws, "stream-1");
594
704
 
595
705
  output.sendTextToken("stale partial response", false);
596
706
  output.discardPendingText();
597
707
  output.sendTextToken("", true);
598
- await drain();
708
+ await drain(() => countEvents(sent, "mark") > 0);
599
709
 
600
710
  expect(mockSynthesize).not.toHaveBeenCalled();
601
711
  // Only the end-of-turn mark goes out.
@@ -607,7 +717,7 @@ describe("MediaStreamOutput", () => {
607
717
  describe("setStreamSid / getStreamSid", () => {
608
718
  test("updates the stream SID used in subsequent commands", () => {
609
719
  const { ws, sent } = createMockWs();
610
- const output = new MediaStreamOutput(ws, "old-sid");
720
+ const output = makeOutput(ws, "old-sid");
611
721
  expect(output.getStreamSid()).toBe("old-sid");
612
722
 
613
723
  output.setStreamSid("new-sid");
@@ -622,7 +732,7 @@ describe("MediaStreamOutput", () => {
622
732
  describe("markClosed", () => {
623
733
  test("transitions to closed state without sending a close frame", () => {
624
734
  const mock = createMockWs();
625
- const output = new MediaStreamOutput(mock.ws, "stream-1");
735
+ const output = makeOutput(mock.ws, "stream-1");
626
736
  output.markClosed();
627
737
  expect(mock.closed).toBe(false); // WebSocket not actually closed
628
738
  output.sendAudioPayload("dGVzdA=="); // Should be suppressed
@@ -639,7 +749,7 @@ describe("MediaStreamOutput", () => {
639
749
  close() {},
640
750
  } as unknown as import("bun").ServerWebSocket<unknown>;
641
751
 
642
- const output = new MediaStreamOutput(ws, "stream-1");
752
+ const output = makeOutput(ws, "stream-1");
643
753
  // Should not throw
644
754
  expect(() => output.sendAudioPayload("dGVzdA==")).not.toThrow();
645
755
  });
@@ -652,7 +762,7 @@ describe("MediaStreamOutput", () => {
652
762
  },
653
763
  } as unknown as import("bun").ServerWebSocket<unknown>;
654
764
 
655
- const output = new MediaStreamOutput(ws, "stream-1");
765
+ const output = makeOutput(ws, "stream-1");
656
766
  // Should not throw
657
767
  expect(() => output.endSession()).not.toThrow();
658
768
  });
@@ -671,10 +781,10 @@ describe("MediaStreamOutput", () => {
671
781
  });
672
782
 
673
783
  const { ws, sent } = createMockWs();
674
- const output = new MediaStreamOutput(ws, "stream-1");
784
+ const output = makeOutput(ws, "stream-1");
675
785
  output.sendTextToken("test synthesis", true);
676
786
 
677
- await drain();
787
+ await drain(() => countEvents(sent, "mark") > 0);
678
788
 
679
789
  // Should have sent at least one media frame and an end-of-turn mark
680
790
  const mediaMessages = sent.filter((s) => JSON.parse(s).event === "media");
@@ -693,7 +803,7 @@ describe("MediaStreamOutput", () => {
693
803
 
694
804
  test("getPlaybackQueueLength reflects queue state", () => {
695
805
  const { ws } = createMockWs();
696
- const output = new MediaStreamOutput(ws, "stream-1");
806
+ const output = makeOutput(ws, "stream-1");
697
807
  // Initially empty
698
808
  expect(output.getPlaybackQueueLength()).toBe(0);
699
809
  });
@@ -721,10 +831,10 @@ describe("MediaStreamOutput", () => {
721
831
  });
722
832
 
723
833
  const { ws, sent } = createMockWs();
724
- const output = new MediaStreamOutput(ws, "stream-1");
834
+ const output = makeOutput(ws, "stream-1");
725
835
  output.sendTextToken("test", true);
726
836
 
727
- await drain();
837
+ await drain(() => countEvents(sent, "mark") > 0);
728
838
 
729
839
  // The audioBufferToFrames magic-byte detection should detect mp3
730
840
  // sync bytes when format is "wav" and return silence (no media
@@ -754,10 +864,10 @@ describe("MediaStreamOutput", () => {
754
864
  });
755
865
 
756
866
  const { ws, sent } = createMockWs();
757
- const output = new MediaStreamOutput(ws, "stream-1");
867
+ const output = makeOutput(ws, "stream-1");
758
868
  output.sendTextToken("test", true);
759
869
 
760
- await drain();
870
+ await drain(() => countEvents(sent, "mark") > 0);
761
871
 
762
872
  // processSynthesizeItem derives actualFormat from content-type:
763
873
  // "audio/pcm" -> "pcm". audioBufferToFrames handles raw PCM by
@@ -789,10 +899,10 @@ describe("MediaStreamOutput", () => {
789
899
  });
790
900
 
791
901
  const { ws, sent } = createMockWs();
792
- const output = new MediaStreamOutput(ws, "stream-1");
902
+ const output = makeOutput(ws, "stream-1");
793
903
  output.sendTextToken("test", true);
794
904
 
795
- await drain();
905
+ await drain(() => countEvents(sent, "mark") > 0);
796
906
 
797
907
  // processSynthesizeItem detects "audio/x-raw" -> "pcm" format.
798
908
  // audioBufferToFrames converts raw PCM to mu-law frames.
@@ -818,9 +928,9 @@ describe("MediaStreamOutput", () => {
818
928
  contentType: "audio/wav",
819
929
  });
820
930
  const { ws, sent } = createMockWs();
821
- const output = new MediaStreamOutput(ws, "stream-1");
931
+ const output = makeOutput(ws, "stream-1");
822
932
  output.sendTextToken("test", true);
823
- await drain();
933
+ await drain(() => countEvents(sent, "mark") > 0);
824
934
  return sent;
825
935
  }
826
936
 
@@ -883,4 +993,578 @@ describe("MediaStreamOutput", () => {
883
993
  expect(totalMulawBytes(sent)).toBe(400);
884
994
  });
885
995
  });
996
+
997
+ // ---------------------------------------------------------------------------
998
+ // Incremental sentence-bounded synthesis
999
+ // ---------------------------------------------------------------------------
1000
+
1001
+ describe("incremental sentence-bounded synthesis", () => {
1002
+ test("complete sentences synthesize and play before last: true arrives", async () => {
1003
+ const wav = makeWavBuffer(sineSamples(400, 440, 8000));
1004
+ mockSynthesize.mockResolvedValue({
1005
+ audio: wav,
1006
+ contentType: "audio/wav",
1007
+ });
1008
+
1009
+ const { ws, sent } = createMockWs();
1010
+ const output = makeOutput(ws, "stream-1");
1011
+
1012
+ output.sendTextToken("First sentence. Sec", false);
1013
+ await drain(() => countEvents(sent, "media") > 0);
1014
+
1015
+ // The completed first sentence synthesizes (and its frames go out)
1016
+ // while the turn is still streaming.
1017
+ expect(mockSynthesize).toHaveBeenCalledTimes(1);
1018
+ expect(mockSynthesize.mock.calls[0][0].text).toBe("First sentence.");
1019
+ expect(countEvents(sent, "media")).toBeGreaterThan(0);
1020
+
1021
+ output.sendTextToken("ond sentence.", true);
1022
+ await drain(() => countEvents(sent, "mark") > 0);
1023
+
1024
+ expect(mockSynthesize).toHaveBeenCalledTimes(2);
1025
+ expect(mockSynthesize.mock.calls[1][0].text).toBe("Second sentence.");
1026
+ });
1027
+
1028
+ test("end-of-turn mark follows the final segment's frames", async () => {
1029
+ const wav = makeWavBuffer(sineSamples(400, 440, 8000));
1030
+ mockSynthesize.mockResolvedValue({
1031
+ audio: wav,
1032
+ contentType: "audio/wav",
1033
+ });
1034
+
1035
+ const { ws, sent } = createMockWs();
1036
+ const output = makeOutput(ws, "stream-1");
1037
+ output.sendTextToken("One. Two.", true);
1038
+ await drain(() => countEvents(sent, "mark") > 0);
1039
+
1040
+ expect(mockSynthesize).toHaveBeenCalledTimes(2);
1041
+ const events = sent.map((s) => JSON.parse(s).event);
1042
+ const markIndex = events.indexOf("mark");
1043
+ const lastMediaIndex = events.lastIndexOf("media");
1044
+ expect(lastMediaIndex).toBeGreaterThanOrEqual(0);
1045
+ expect(markIndex).toBeGreaterThan(lastMediaIndex);
1046
+ });
1047
+ });
1048
+
1049
+ describe("eager first segment", () => {
1050
+ function usePlayableWav(): void {
1051
+ mockSynthesize.mockResolvedValue({
1052
+ audio: makeWavBuffer([1000, 2000, 3000, 4000]),
1053
+ contentType: "audio/wav",
1054
+ });
1055
+ }
1056
+
1057
+ test("the turn's first segment synthesizes at a clause boundary before any sentence ends", async () => {
1058
+ usePlayableWav();
1059
+ const { ws, sent } = createMockWs();
1060
+ const output = makeOutput(ws, "stream-1");
1061
+
1062
+ // No sentence has ended yet — full-sentence rules would still be
1063
+ // buffering, but the clause-bounded prefix synthesizes immediately.
1064
+ output.sendTextToken("Let me take a look at that, ", false);
1065
+ await drain(() => mockSynthesize.mock.calls.length > 0);
1066
+
1067
+ expect(mockSynthesize).toHaveBeenCalledTimes(1);
1068
+ expect(mockSynthesize.mock.calls[0][0].text).toBe(
1069
+ "Let me take a look at that,",
1070
+ );
1071
+
1072
+ output.sendTextToken("and get right back to you.", true);
1073
+ await drain(() => countEvents(sent, "mark") > 0);
1074
+
1075
+ expect(mockSynthesize).toHaveBeenCalledTimes(2);
1076
+ expect(mockSynthesize.mock.calls[1][0].text).toBe(
1077
+ "and get right back to you.",
1078
+ );
1079
+ });
1080
+
1081
+ test("eager mode applies only to the first segment — later clauses wait for sentence ends", async () => {
1082
+ usePlayableWav();
1083
+ const { ws, sent } = createMockWs();
1084
+ const output = makeOutput(ws, "stream-1");
1085
+
1086
+ output.sendTextToken(
1087
+ "Let me take a look at that, and then, after checking, I will confirm.",
1088
+ false,
1089
+ );
1090
+ output.sendTextToken("", true);
1091
+ await drain(() => countEvents(sent, "mark") > 0);
1092
+
1093
+ expect(mockSynthesize.mock.calls.map((c) => c[0].text)).toEqual([
1094
+ "Let me take a look at that,",
1095
+ "and then, after checking, I will confirm.",
1096
+ ]);
1097
+ });
1098
+
1099
+ test("a long unpunctuated opening flushes at the eager 60-char cap", async () => {
1100
+ usePlayableWav();
1101
+ const { ws, sent } = createMockWs();
1102
+ const output = makeOutput(ws, "stream-1");
1103
+
1104
+ // 69 chars, no punctuation: below the default 180-char cap but past
1105
+ // the eager 60-char cap, so the opening splits at the last whitespace
1106
+ // under 60 chars.
1107
+ output.sendTextToken(
1108
+ "The quick brown fox jumps over the lazy dog near the quiet river bank",
1109
+ false,
1110
+ );
1111
+ await drain(() => mockSynthesize.mock.calls.length > 0);
1112
+
1113
+ expect(mockSynthesize).toHaveBeenCalledTimes(1);
1114
+ expect(mockSynthesize.mock.calls[0][0].text).toBe(
1115
+ "The quick brown fox jumps over the lazy dog near the quiet",
1116
+ );
1117
+
1118
+ output.sendTextToken("", true);
1119
+ await drain(() => countEvents(sent, "mark") > 0);
1120
+
1121
+ // The short remainder never reaches a boundary and force-flushes on
1122
+ // last: true.
1123
+ expect(mockSynthesize).toHaveBeenCalledTimes(2);
1124
+ expect(mockSynthesize.mock.calls[1][0].text).toBe("river bank");
1125
+ });
1126
+
1127
+ test("the next turn's first segment is eager again", async () => {
1128
+ usePlayableWav();
1129
+ const { ws, sent } = createMockWs();
1130
+ const output = makeOutput(ws, "stream-1");
1131
+
1132
+ output.sendTextToken("Let me take a look at that, one moment.", true);
1133
+ await drain(() => countEvents(sent, "mark") >= 1);
1134
+
1135
+ output.sendTextToken("Here is the second answer, coming right up.", true);
1136
+ await drain(() => countEvents(sent, "mark") >= 2);
1137
+
1138
+ expect(mockSynthesize.mock.calls.map((c) => c[0].text)).toEqual([
1139
+ "Let me take a look at that,",
1140
+ "one moment.",
1141
+ "Here is the second answer,",
1142
+ "coming right up.",
1143
+ ]);
1144
+ });
1145
+
1146
+ test("discardPendingText re-arms eager segmentation for the next turn", async () => {
1147
+ usePlayableWav();
1148
+ const { ws } = createMockWs();
1149
+ const output = makeOutput(ws, "stream-1");
1150
+
1151
+ output.sendTextToken("Let me take a look at that, plus extra", false);
1152
+ await drain(() => mockSynthesize.mock.calls.length > 0);
1153
+
1154
+ // Turn aborted: the unsegmented remainder is dropped and the next
1155
+ // turn's first segment is eager again.
1156
+ output.discardPendingText();
1157
+ output.sendTextToken("Here is the second answer, plus more", false);
1158
+ await drain(() => mockSynthesize.mock.calls.length > 1);
1159
+
1160
+ expect(mockSynthesize.mock.calls.map((c) => c[0].text)).toEqual([
1161
+ "Let me take a look at that,",
1162
+ "Here is the second answer,",
1163
+ ]);
1164
+ });
1165
+ });
1166
+
1167
+ // ---------------------------------------------------------------------------
1168
+ // Streaming PCM synthesis (incremental transcode)
1169
+ // ---------------------------------------------------------------------------
1170
+
1171
+ describe("streaming PCM synthesis", () => {
1172
+ test("frames are sent before the provider stream completes", async () => {
1173
+ let releaseStream!: () => void;
1174
+ const gate = new Promise<void>((resolve) => {
1175
+ releaseStream = resolve;
1176
+ });
1177
+ // 640 samples at 16 kHz -> 320 samples at 8 kHz = two whole frames.
1178
+ const chunk = pcm16Buffer(sineSamples(640, 440, 16000));
1179
+ useProvider({
1180
+ id: "streaming-pcm",
1181
+ capabilities: { supportsStreaming: true, supportedFormats: ["pcm"] },
1182
+ synthesize: jest.fn(),
1183
+ async synthesizeStream(
1184
+ _request: unknown,
1185
+ onChunk: (c: Uint8Array) => void,
1186
+ ) {
1187
+ onChunk(chunk);
1188
+ await gate;
1189
+ onChunk(chunk);
1190
+ return {
1191
+ audio: Buffer.concat([chunk, chunk]),
1192
+ contentType: "audio/pcm",
1193
+ };
1194
+ },
1195
+ });
1196
+
1197
+ const { ws, sent } = createMockWs();
1198
+ const output = makeOutput(ws, "stream-1");
1199
+ output.sendTextToken("Hello there.", true);
1200
+ await drain(() => countEvents(sent, "media") >= 2);
1201
+
1202
+ // First chunk's frames went out while the stream is still open,
1203
+ // and the end-of-turn mark has not been sent yet.
1204
+ const framesBefore = countEvents(sent, "media");
1205
+ expect(framesBefore).toBe(2);
1206
+ expect(sent.some((s) => JSON.parse(s).event === "mark")).toBe(false);
1207
+
1208
+ releaseStream();
1209
+ await drain(() => countEvents(sent, "mark") > 0);
1210
+
1211
+ expect(countEvents(sent, "media")).toBe(4);
1212
+ expect(sent.some((s) => JSON.parse(s).event === "mark")).toBe(true);
1213
+ });
1214
+
1215
+ test("odd-byte and partial-frame chunk boundaries produce well-formed frames", async () => {
1216
+ // 800 samples at 16 kHz (1600 bytes), split at deliberately awkward
1217
+ // boundaries: odd bytes, non-frame-aligned sizes.
1218
+ const full = pcm16Buffer(sineSamples(800, 440, 16000));
1219
+ const cuts = [0, 3, 251, 640, 1001, full.length];
1220
+ const chunks = cuts
1221
+ .slice(0, -1)
1222
+ .map((start, i) => full.subarray(start, cuts[i + 1]));
1223
+ useProvider({
1224
+ id: "streaming-pcm",
1225
+ capabilities: { supportsStreaming: true, supportedFormats: ["pcm"] },
1226
+ synthesize: jest.fn(),
1227
+ async synthesizeStream(
1228
+ _request: unknown,
1229
+ onChunk: (c: Uint8Array) => void,
1230
+ ) {
1231
+ for (const c of chunks) {
1232
+ onChunk(c);
1233
+ }
1234
+ return { audio: full, contentType: "audio/pcm" };
1235
+ },
1236
+ });
1237
+
1238
+ const { ws, sent } = createMockWs();
1239
+ const output = makeOutput(ws, "stream-1");
1240
+ output.sendTextToken("Test.", true);
1241
+ await drain(() => countEvents(sent, "mark") > 0);
1242
+
1243
+ // The concatenated mu-law output must be byte-identical to a
1244
+ // whole-buffer transcode: no dropped or torn samples at chunk seams.
1245
+ const expected = pcm16ToMulaw(decimateByTwo(full));
1246
+ expect(expected.length).toBe(400);
1247
+ expect(concatMulawPayloads(sent).equals(expected)).toBe(true);
1248
+ });
1249
+
1250
+ test("playbackVersion bump mid-stream stops further frames", async () => {
1251
+ let releaseStream!: () => void;
1252
+ const gate = new Promise<void>((resolve) => {
1253
+ releaseStream = resolve;
1254
+ });
1255
+ const chunk = pcm16Buffer(sineSamples(640, 440, 16000));
1256
+ useProvider({
1257
+ id: "streaming-pcm",
1258
+ capabilities: { supportsStreaming: true, supportedFormats: ["pcm"] },
1259
+ synthesize: jest.fn(),
1260
+ async synthesizeStream(
1261
+ _request: unknown,
1262
+ onChunk: (c: Uint8Array) => void,
1263
+ ) {
1264
+ onChunk(chunk);
1265
+ await gate;
1266
+ // Stale chunk emitted after barge-in must never become frames.
1267
+ onChunk(chunk);
1268
+ return {
1269
+ audio: Buffer.concat([chunk, chunk]),
1270
+ contentType: "audio/pcm",
1271
+ };
1272
+ },
1273
+ });
1274
+
1275
+ const { ws, sent } = createMockWs();
1276
+ const output = makeOutput(ws, "stream-1");
1277
+ output.sendTextToken("Hello there.", true);
1278
+ await drain(() => countEvents(sent, "media") > 0);
1279
+
1280
+ const framesBefore = countEvents(sent, "media");
1281
+ expect(framesBefore).toBeGreaterThan(0);
1282
+
1283
+ output.clearAudio();
1284
+ releaseStream();
1285
+ await drain();
1286
+
1287
+ expect(countEvents(sent, "media")).toBe(framesBefore);
1288
+ });
1289
+
1290
+ test("streaming provider without PCM support falls back to the whole-buffer path", async () => {
1291
+ const wav = makeWavBuffer(sineSamples(400, 440, 8000));
1292
+ const half = Math.floor(wav.length / 2);
1293
+ let midStreamFrames = -1;
1294
+ const sentRef: { sent: string[] } = { sent: [] };
1295
+ useProvider({
1296
+ id: "streaming-wav",
1297
+ capabilities: { supportsStreaming: true, supportedFormats: ["wav"] },
1298
+ synthesize: jest.fn(),
1299
+ async synthesizeStream(
1300
+ _request: unknown,
1301
+ onChunk: (c: Uint8Array) => void,
1302
+ ) {
1303
+ onChunk(wav.subarray(0, half));
1304
+ await new Promise((resolve) => setTimeout(resolve, 5));
1305
+ midStreamFrames = countEvents(sentRef.sent, "media");
1306
+ onChunk(wav.subarray(half));
1307
+ return { audio: wav, contentType: "audio/wav" };
1308
+ },
1309
+ });
1310
+
1311
+ const { ws, sent } = createMockWs();
1312
+ sentRef.sent = sent;
1313
+ const output = makeOutput(ws, "stream-1");
1314
+ output.sendTextToken("Hello.", true);
1315
+ await drain(() => countEvents(sent, "mark") > 0);
1316
+
1317
+ // Nothing streamed mid-flight; the whole accumulated WAV is
1318
+ // transcoded once at the end.
1319
+ expect(midStreamFrames).toBe(0);
1320
+ expect(totalMulawBytes(sent)).toBe(400);
1321
+ });
1322
+ });
1323
+
1324
+ // ---------------------------------------------------------------------------
1325
+ // Streaming play-URL playback (processFetchUrlItem)
1326
+ // ---------------------------------------------------------------------------
1327
+
1328
+ describe("sendPlayUrl streaming", () => {
1329
+ const originalFetch = globalThis.fetch;
1330
+
1331
+ afterEach(() => {
1332
+ globalThis.fetch = originalFetch;
1333
+ });
1334
+
1335
+ /** Mock global fetch to resolve with the given response for any URL. */
1336
+ function mockFetch(response: Response): void {
1337
+ globalThis.fetch = (async () => response) as unknown as typeof fetch;
1338
+ }
1339
+
1340
+ /**
1341
+ * A fetch response whose body is a manually driven stream, so tests
1342
+ * control exactly when bytes arrive and when the body completes.
1343
+ * `arrayBuffer()` throws: the streaming path must never buffer the
1344
+ * response wholesale.
1345
+ */
1346
+ function fakeStreamingResponse(contentType: string): {
1347
+ response: Response;
1348
+ push: (bytes: Buffer) => void;
1349
+ close: () => void;
1350
+ cancelled: () => boolean;
1351
+ } {
1352
+ let controller!: ReadableStreamDefaultController<Uint8Array>;
1353
+ let cancelled = false;
1354
+ const stream = new ReadableStream<Uint8Array>({
1355
+ start(c) {
1356
+ controller = c;
1357
+ },
1358
+ cancel() {
1359
+ cancelled = true;
1360
+ },
1361
+ });
1362
+ const response = {
1363
+ ok: true,
1364
+ headers: new Headers({ "content-type": contentType }),
1365
+ body: stream,
1366
+ arrayBuffer: () => {
1367
+ throw new Error(
1368
+ "arrayBuffer must not be called on the streaming path",
1369
+ );
1370
+ },
1371
+ } as unknown as Response;
1372
+ return {
1373
+ response,
1374
+ push: (bytes) => {
1375
+ try {
1376
+ controller.enqueue(new Uint8Array(bytes));
1377
+ } catch {
1378
+ // Stream already cancelled/closed — stale producer push.
1379
+ }
1380
+ },
1381
+ close: () => {
1382
+ try {
1383
+ controller.close();
1384
+ } catch {
1385
+ // Already cancelled.
1386
+ }
1387
+ },
1388
+ cancelled: () => cancelled,
1389
+ };
1390
+ }
1391
+
1392
+ test("WAV play-URL frames go out before the response stream completes", async () => {
1393
+ // 1280 samples at 16 kHz -> 640 at 8 kHz = four whole frames.
1394
+ const wav = makeWavBuffer(sineSamples(1280, 440, 16000), {
1395
+ sampleRate: 16000,
1396
+ });
1397
+ // Header + 320 samples: exactly one 20 ms frame after 2x decimation.
1398
+ const firstPart = wav.subarray(0, 44 + 320 * 2);
1399
+ const rest = wav.subarray(44 + 320 * 2);
1400
+
1401
+ const fake = fakeStreamingResponse("audio/wav");
1402
+ mockFetch(fake.response);
1403
+
1404
+ const { ws, sent } = createMockWs();
1405
+ const output = makeOutput(ws, "stream-1");
1406
+ output.sendPlayUrl("http://127.0.0.1/audio/test.wav");
1407
+
1408
+ fake.push(firstPart);
1409
+ await drain(() => countEvents(sent, "media") >= 1);
1410
+
1411
+ // The first frame went out while the body is still open.
1412
+ expect(countEvents(sent, "media")).toBe(1);
1413
+
1414
+ fake.push(rest);
1415
+ fake.close();
1416
+ await drain(() => countEvents(sent, "media") >= 4);
1417
+
1418
+ // Byte-identical to the whole-buffer transcode.
1419
+ const expected = pcm16ToMulaw(decimateByTwo(wav.subarray(44)));
1420
+ expect(concatMulawPayloads(sent).equals(expected)).toBe(true);
1421
+ });
1422
+
1423
+ test("audio/pcm play-URL streams raw PCM incrementally", async () => {
1424
+ const pcm = pcm16Buffer(sineSamples(1280, 440, 16000));
1425
+ const fake = fakeStreamingResponse("audio/pcm");
1426
+ mockFetch(fake.response);
1427
+
1428
+ const { ws, sent } = createMockWs();
1429
+ const output = makeOutput(ws, "stream-1");
1430
+ output.sendPlayUrl("http://127.0.0.1/audio/test.pcm");
1431
+
1432
+ fake.push(pcm.subarray(0, 320 * 2)); // 320 samples -> one whole frame
1433
+ await drain(() => countEvents(sent, "media") >= 1);
1434
+ expect(countEvents(sent, "media")).toBe(1);
1435
+
1436
+ fake.push(pcm.subarray(320 * 2));
1437
+ fake.close();
1438
+ await drain(() => countEvents(sent, "media") >= 4);
1439
+
1440
+ const expected = pcm16ToMulaw(decimateByTwo(pcm));
1441
+ expect(concatMulawPayloads(sent).equals(expected)).toBe(true);
1442
+ });
1443
+
1444
+ test("clearAudio mid-stream cancels the body read and sends clear", async () => {
1445
+ const wav = makeWavBuffer(sineSamples(1280, 440, 16000), {
1446
+ sampleRate: 16000,
1447
+ });
1448
+ const fake = fakeStreamingResponse("audio/wav");
1449
+ mockFetch(fake.response);
1450
+
1451
+ const { ws, sent } = createMockWs();
1452
+ const output = makeOutput(ws, "stream-1");
1453
+ output.sendPlayUrl("http://127.0.0.1/audio/test.wav");
1454
+
1455
+ fake.push(wav.subarray(0, 44 + 320 * 2));
1456
+ await drain(() => countEvents(sent, "media") >= 1);
1457
+ const framesBefore = countEvents(sent, "media");
1458
+ expect(framesBefore).toBeGreaterThan(0);
1459
+
1460
+ output.clearAudio();
1461
+ await drain(() => fake.cancelled());
1462
+
1463
+ // The in-flight body read was cancelled and Twilio's buffer cleared.
1464
+ expect(fake.cancelled()).toBe(true);
1465
+ expect(countEvents(sent, "clear")).toBe(1);
1466
+
1467
+ // Bytes arriving after the abort never become frames.
1468
+ fake.push(wav.subarray(44 + 320 * 2));
1469
+ await drain();
1470
+ expect(countEvents(sent, "media")).toBe(framesBefore);
1471
+ });
1472
+
1473
+ test("audio/mpeg keeps the whole-buffer path (no body streaming)", async () => {
1474
+ const mp3Bytes = Buffer.alloc(256, 0x80);
1475
+ mp3Bytes[0] = 0xff; // MPEG sync
1476
+ mp3Bytes[1] = 0xfb; // MPEG Layer 3
1477
+
1478
+ let bodyAccessed = false;
1479
+ let arrayBufferCalled = false;
1480
+ const response = {
1481
+ ok: true,
1482
+ headers: new Headers({ "content-type": "audio/mpeg" }),
1483
+ get body(): ReadableStream<Uint8Array> {
1484
+ bodyAccessed = true;
1485
+ throw new Error("body must not be streamed for compressed formats");
1486
+ },
1487
+ arrayBuffer: async () => {
1488
+ arrayBufferCalled = true;
1489
+ return mp3Bytes.buffer.slice(
1490
+ mp3Bytes.byteOffset,
1491
+ mp3Bytes.byteOffset + mp3Bytes.byteLength,
1492
+ );
1493
+ },
1494
+ } as unknown as Response;
1495
+ mockFetch(response);
1496
+
1497
+ const { ws, sent } = createMockWs();
1498
+ const output = makeOutput(ws, "stream-1");
1499
+ output.sendPlayUrl("http://127.0.0.1/audio/test.mp3");
1500
+
1501
+ await drain(() => arrayBufferCalled);
1502
+
1503
+ expect(arrayBufferCalled).toBe(true);
1504
+ expect(bodyAccessed).toBe(false);
1505
+ // mp3 cannot be transcoded in this path, so no media frames go out.
1506
+ expect(countEvents(sent, "media")).toBe(0);
1507
+ });
1508
+
1509
+ test("non-canonical WAV (stereo) falls back to the whole-buffer path", async () => {
1510
+ // Stereo is not streamable; the buffered path keeps the left channel.
1511
+ const left = sineSamples(400, 440, 8000);
1512
+ const interleaved: number[] = [];
1513
+ for (const s of left) {
1514
+ interleaved.push(s, -s);
1515
+ }
1516
+ const wav = makeWavBuffer(interleaved, { sampleRate: 8000, channels: 2 });
1517
+
1518
+ const fake = fakeStreamingResponse("audio/wav");
1519
+ mockFetch(fake.response);
1520
+
1521
+ const { ws, sent } = createMockWs();
1522
+ const output = makeOutput(ws, "stream-1");
1523
+ output.sendPlayUrl("http://127.0.0.1/audio/stereo.wav");
1524
+
1525
+ fake.push(wav.subarray(0, 100));
1526
+ // No frames while the fallback drains the body.
1527
+ await drain();
1528
+ expect(countEvents(sent, "media")).toBe(0);
1529
+
1530
+ fake.push(wav.subarray(100));
1531
+ fake.close();
1532
+ await drain(() => countEvents(sent, "media") > 0);
1533
+
1534
+ // Left channel of 400 pairs at 8 kHz -> 400 mu-law bytes.
1535
+ expect(totalMulawBytes(sent)).toBe(400);
1536
+ });
1537
+
1538
+ test("malformed WAV header falls back without throwing", async () => {
1539
+ // RIFF magic but garbage after it: not streamable, and the buffered
1540
+ // path's fixed-offset reads produce no usable frames.
1541
+ const junk = Buffer.alloc(64, 0x42);
1542
+ junk.write("RIFF", 0);
1543
+
1544
+ const fake = fakeStreamingResponse("audio/wav");
1545
+ mockFetch(fake.response);
1546
+
1547
+ const { ws, sent } = createMockWs();
1548
+ const output = makeOutput(ws, "stream-1");
1549
+ output.sendPlayUrl("http://127.0.0.1/audio/junk.wav");
1550
+
1551
+ fake.push(junk);
1552
+ fake.close();
1553
+ await drain(() => output.getPlaybackQueueLength() === 0);
1554
+ await drain();
1555
+
1556
+ // No crash; a malformed WAV yields no usable frames from the
1557
+ // buffered transcode's fixed-offset reads.
1558
+ expect(countEvents(sent, "media")).toBe(0);
1559
+
1560
+ // The output still works for a subsequent playable item.
1561
+ const good = fakeStreamingResponse("audio/pcm");
1562
+ mockFetch(good.response);
1563
+ output.sendPlayUrl("http://127.0.0.1/audio/good.pcm");
1564
+ good.push(pcm16Buffer(sineSamples(320, 440, 16000)));
1565
+ good.close();
1566
+ await drain(() => countEvents(sent, "media") > 0);
1567
+ expect(countEvents(sent, "media")).toBe(1);
1568
+ });
1569
+ });
886
1570
  });