@vellumai/assistant 0.11.2 → 0.11.3-staging.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (720) hide show
  1. package/ARCHITECTURE.md +58 -3
  2. package/docs/trusted-contact-access.md +9 -5
  3. package/docs/vellum-doctor.md +195 -0
  4. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/channels.ts +39 -0
  5. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/ingress.ts +10 -0
  6. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +6 -0
  7. package/node_modules/@vellumai/ces-client/src/index.ts +1 -0
  8. package/node_modules/@vellumai/ces-client/src/rpc-client.ts +19 -2
  9. package/node_modules/@vellumai/environments/package.json +2 -1
  10. package/node_modules/@vellumai/environments/src/__tests__/cloud-assistant-hub-url.test.ts +38 -0
  11. package/node_modules/@vellumai/environments/src/__tests__/install-layout.test.ts +98 -0
  12. package/node_modules/@vellumai/environments/src/__tests__/package-boundary.test.ts +5 -5
  13. package/node_modules/@vellumai/environments/src/index.ts +17 -6
  14. package/node_modules/@vellumai/environments/src/install-layout.ts +49 -0
  15. package/node_modules/@vellumai/environments/src/seeds.ts +29 -0
  16. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/channels.ts +39 -0
  17. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/ingress.ts +10 -0
  18. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +6 -0
  19. package/node_modules/@vellumai/gateway-client/src/index.ts +4 -0
  20. package/node_modules/@vellumai/gateway-client/src/verification-session-contract.ts +91 -6
  21. package/node_modules/@vellumai/ipc-server-utils/src/endpoint.test.ts +36 -0
  22. package/node_modules/@vellumai/ipc-server-utils/src/endpoint.ts +142 -0
  23. package/node_modules/@vellumai/ipc-server-utils/src/index.ts +2 -0
  24. package/node_modules/@vellumai/ipc-server-utils/src/listen-options.ts +3 -0
  25. package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.test.ts +15 -1
  26. package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.ts +17 -2
  27. package/node_modules/@vellumai/service-contracts/src/channels.ts +39 -0
  28. package/node_modules/@vellumai/service-contracts/src/ingress.ts +10 -0
  29. package/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +6 -0
  30. package/node_modules/@vellumai/slack-text/src/index.ts +16 -2
  31. package/openapi.yaml +589 -30
  32. package/package.json +1 -1
  33. package/src/__tests__/agent-image-optimize.test.ts +4 -4
  34. package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +113 -66
  35. package/src/__tests__/agent-loop-override-profile.test.ts +38 -48
  36. package/src/__tests__/agent-loop-subagent-billing-headers.test.ts +166 -0
  37. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +1 -0
  38. package/src/__tests__/agent-wake-override-profile.test.ts +1 -0
  39. package/src/__tests__/answered-question-persistence.test.ts +240 -0
  40. package/src/__tests__/anthropic-provider.test.ts +165 -30
  41. package/src/__tests__/app-control-flow.test.ts +9 -8
  42. package/src/__tests__/assistant-event-hub.test.ts +48 -0
  43. package/src/__tests__/attachments-store-heic-normalize.test.ts +16 -12
  44. package/src/__tests__/attachments-store.test.ts +130 -68
  45. package/src/__tests__/attachments.test.ts +36 -36
  46. package/src/__tests__/background-workers-disk-pressure.test.ts +1 -1
  47. package/src/__tests__/call-start-guardian-guard.test.ts +18 -4
  48. package/src/__tests__/canned-reply-release.test.ts +86 -0
  49. package/src/__tests__/channel-guardian.test.ts +59 -29
  50. package/src/__tests__/channel-readiness-telegram-remote.test.ts +154 -0
  51. package/src/__tests__/channel-setup-panel-ack.test.ts +6 -5
  52. package/src/__tests__/client-os-metadata-persistence.test.ts +103 -4
  53. package/src/__tests__/compactor-image-manifest-trust.test.ts +1 -1
  54. package/src/__tests__/consult-deadline.test.ts +65 -0
  55. package/src/__tests__/conversation-agent-loop.test.ts +438 -2
  56. package/src/__tests__/conversation-disk-view-integration.test.ts +5 -1
  57. package/src/__tests__/conversation-disk-view.test.ts +4 -4
  58. package/src/__tests__/conversation-error.test.ts +22 -3
  59. package/src/__tests__/conversation-fork-crud.test.ts +5 -1
  60. package/src/__tests__/conversation-fork-referential.test.ts +352 -0
  61. package/src/__tests__/conversation-fork-retrospective.test.ts +5 -1
  62. package/src/__tests__/conversation-group-tools.test.ts +32 -2
  63. package/src/__tests__/conversation-inference-profile-route.test.ts +27 -1
  64. package/src/__tests__/conversation-initial-prompt.test.ts +51 -22
  65. package/src/__tests__/conversation-key-store-bootstrap-cleanup.test.ts +119 -0
  66. package/src/__tests__/conversation-key-store-origin.test.ts +105 -0
  67. package/src/__tests__/conversation-list-recency-ordering.test.ts +104 -0
  68. package/src/__tests__/conversation-origin-at-creation.test.ts +153 -0
  69. package/src/__tests__/conversation-origin-channel-filter.test.ts +95 -0
  70. package/src/__tests__/conversation-placement-promotion.test.ts +172 -0
  71. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +123 -0
  72. package/src/__tests__/conversation-queue.test.ts +433 -1
  73. package/src/__tests__/conversation-retry-route.test.ts +3 -1
  74. package/src/__tests__/conversation-routes-hidden-queue.test.ts +64 -1
  75. package/src/__tests__/conversation-store-ephemeral.test.ts +4 -0
  76. package/src/__tests__/conversation-store.test.ts +26 -10
  77. package/src/__tests__/conversation-summarize-route.test.ts +1 -1
  78. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +15 -6
  79. package/src/__tests__/conversation-surfaces-activation-emit.test.ts +6 -5
  80. package/src/__tests__/conversation-surfaces-app-control.test.ts +12 -11
  81. package/src/__tests__/conversation-surfaces-app-open.test.ts +11 -5
  82. package/src/__tests__/conversation-surfaces-data-persist.test.ts +5 -4
  83. package/src/__tests__/conversation-surfaces-history-restored-completion.test.ts +967 -0
  84. package/src/__tests__/conversation-surfaces-persisted-info.test.ts +312 -0
  85. package/src/__tests__/conversation-surfaces-queued-emit.test.ts +6 -5
  86. package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +12 -8
  87. package/src/__tests__/conversation-surfaces-standalone.test.ts +21 -11
  88. package/src/__tests__/conversation-surfaces-state-update.test.ts +7 -11
  89. package/src/__tests__/conversation-surfaces-table-action.test.ts +7 -6
  90. package/src/__tests__/conversation-surfaces-task-progress.test.ts +11 -6
  91. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +9 -6
  92. package/src/__tests__/conversation-tool-setup-attribution.test.ts +70 -7
  93. package/src/__tests__/conversation-wait-for-idle.test.ts +6 -11
  94. package/src/__tests__/credential-prompt-route.test.ts +57 -7
  95. package/src/__tests__/credential-security-invariants.test.ts +1 -0
  96. package/src/__tests__/cu-unified-flow.test.ts +89 -28
  97. package/src/__tests__/custom-group-listing-visibility.test.ts +1 -1
  98. package/src/__tests__/db-schedule-syntax-migration.test.ts +4 -1
  99. package/src/__tests__/discord-access-request-privacy.test.ts +366 -0
  100. package/src/__tests__/discord-requester-notice-privacy.test.ts +87 -0
  101. package/src/__tests__/disk-pressure-guard.test.ts +55 -55
  102. package/src/__tests__/disk-pressure-routes.test.ts +10 -10
  103. package/src/__tests__/disk-pressure-tools.test.ts +2 -2
  104. package/src/__tests__/disk-usage.test.ts +35 -20
  105. package/src/__tests__/document-sync-tags.test.ts +379 -0
  106. package/src/__tests__/document-update-default-surface.test.ts +28 -0
  107. package/src/__tests__/dynamic-skill-background-guard.test.ts +2 -2
  108. package/src/__tests__/emit-signal-routing-intent.test.ts +3 -4
  109. package/src/__tests__/ensure-conversation-exists.test.ts +8 -5
  110. package/src/__tests__/events-dev-bypass-actor.test.ts +183 -1
  111. package/src/__tests__/evict-conversations-for-reload.test.ts +90 -0
  112. package/src/__tests__/file-list-tool.test.ts +12 -10
  113. package/src/__tests__/file-ops-service.test.ts +151 -44
  114. package/src/__tests__/file-read-container-boundary.test.ts +137 -0
  115. package/src/__tests__/file-read-tool.test.ts +21 -9
  116. package/src/__tests__/file-url-path-guard.test.ts +83 -0
  117. package/src/__tests__/filesystem-tools.test.ts +82 -66
  118. package/src/__tests__/guardian-outbound-http.test.ts +8 -8
  119. package/src/__tests__/helpers/gateway-verification-sessions-stub.ts +14 -1
  120. package/src/__tests__/helpers/hub-clients.ts +42 -0
  121. package/src/__tests__/helpers/mock-conversation.ts +46 -0
  122. package/src/__tests__/helpers/verification-sessions-ipc-sim.ts +61 -57
  123. package/src/__tests__/history-repair.test.ts +112 -12
  124. package/src/__tests__/host-bash-proxy.test.ts +10 -5
  125. package/src/__tests__/host-bash-routes.test.ts +67 -0
  126. package/src/__tests__/host-browser-proxy.test.ts +2 -2
  127. package/src/__tests__/host-cu-proxy.test.ts +3 -7
  128. package/src/__tests__/host-file-proxy.test.ts +3 -7
  129. package/src/__tests__/host-file-read-tool.test.ts +5 -1
  130. package/src/__tests__/host-transfer-proxy-targeted.test.ts +6 -4
  131. package/src/__tests__/http-conversation-lineage.test.ts +2 -5
  132. package/src/__tests__/http-user-message-parity.test.ts +44 -0
  133. package/src/__tests__/identity-routes.test.ts +13 -13
  134. package/src/__tests__/image-conversion.test.ts +29 -26
  135. package/src/__tests__/image-source-path-reinject.test.ts +2 -2
  136. package/src/__tests__/inference-profile-session-handler.test.ts +95 -0
  137. package/src/__tests__/inference-profile-session-ipc.test.ts +25 -0
  138. package/src/__tests__/introduction-card-resolver.test.ts +6 -1
  139. package/src/__tests__/list-messages-attachments.test.ts +56 -32
  140. package/src/__tests__/list-messages-background-tool-completion.test.ts +4 -4
  141. package/src/__tests__/list-messages-client-message-id.test.ts +2 -2
  142. package/src/__tests__/list-messages-hidden-metadata.test.ts +16 -16
  143. package/src/__tests__/list-messages-page-latest.test.ts +61 -61
  144. package/src/__tests__/list-messages-provider-error.test.ts +6 -6
  145. package/src/__tests__/list-messages-queued.test.ts +49 -6
  146. package/src/__tests__/list-messages-system-card.test.ts +4 -4
  147. package/src/__tests__/list-messages-tool-merge.test.ts +9 -9
  148. package/src/__tests__/llm-resolver-override-or-default.test.ts +141 -0
  149. package/src/__tests__/llm-usage-store.test.ts +55 -0
  150. package/src/__tests__/manual-token-reconciliation.test.ts +45 -0
  151. package/src/__tests__/mcp-tool-annotations-risk.test.ts +133 -0
  152. package/src/__tests__/media-stream-server-integration.test.ts +36 -0
  153. package/src/__tests__/migration-export-http.test.ts +1 -1
  154. package/src/__tests__/migration-import-commit-http.test.ts +1 -1
  155. package/src/__tests__/migration-import-preflight-http.test.ts +1 -1
  156. package/src/__tests__/migration-validate-http.test.ts +1 -1
  157. package/src/__tests__/mtime-cache.test.ts +65 -3
  158. package/src/__tests__/notification-platform-adapter.test.ts +104 -0
  159. package/src/__tests__/notification-schedule-notify-dedup.test.ts +1 -1
  160. package/src/__tests__/oauth-provider-profiles.test.ts +4 -2
  161. package/src/__tests__/oauth-provider-seed-logos.test.ts +65 -0
  162. package/src/__tests__/oauth-provider-serializer.test.ts +17 -0
  163. package/src/__tests__/oauth-providers-routes.test.ts +1 -0
  164. package/src/__tests__/openai-responses-prompt-cache.test.ts +5 -2
  165. package/src/__tests__/path-policy.test.ts +164 -1
  166. package/src/__tests__/persist-media-references.test.ts +2 -2
  167. package/src/__tests__/persist-unsendable-image-downscale.test.ts +5 -5
  168. package/src/__tests__/persist-unsendable-image.test.ts +23 -23
  169. package/src/__tests__/persona-resolver.test.ts +154 -0
  170. package/src/__tests__/plugin-api-webhook-url.test.ts +170 -0
  171. package/src/__tests__/plugin-import-boundary-guard.test.ts +6 -0
  172. package/src/__tests__/processing-flag-persist-failure.test.ts +15 -10
  173. package/src/__tests__/profile-availability-incomplete.test.ts +113 -0
  174. package/src/__tests__/profiler-routes.test.ts +13 -13
  175. package/src/__tests__/prompt-cache-cross-turn-stability.test.ts +593 -0
  176. package/src/__tests__/proxy-approval-callback.test.ts +3 -3
  177. package/src/__tests__/queued-message-cancel-and-steer.test.ts +189 -0
  178. package/src/__tests__/queued-message-steer-actor-scoping.test.ts +164 -0
  179. package/src/__tests__/resolve-trust-class.test.ts +20 -0
  180. package/src/__tests__/run-conversation-turn-persistence.test.ts +1 -1
  181. package/src/__tests__/run-due-schedules.test.ts +174 -0
  182. package/src/__tests__/runtime-attachment-metadata.test.ts +15 -7
  183. package/src/__tests__/scaffold-managed-skill-tool.test.ts +166 -0
  184. package/src/__tests__/schedule-routes.test.ts +730 -8
  185. package/src/__tests__/schedule-store.test.ts +540 -0
  186. package/src/__tests__/schedule-tools.test.ts +103 -0
  187. package/src/__tests__/scheduler-wake.test.ts +5 -0
  188. package/src/__tests__/secret-routes-platform-proxy.test.ts +29 -1
  189. package/src/__tests__/secure-keys.test.ts +91 -0
  190. package/src/__tests__/server-history-render.test.ts +97 -7
  191. package/src/__tests__/size-guard.test.ts +8 -8
  192. package/src/__tests__/skill-docs-sync-guard.test.ts +3 -2
  193. package/src/__tests__/skill-tool-factory.test.ts +247 -0
  194. package/src/__tests__/skills-install-staging.test.ts +15 -6
  195. package/src/__tests__/slack-edit-ordering-characterization.test.ts +103 -0
  196. package/src/__tests__/slack-inbound-verification.test.ts +4 -1
  197. package/src/__tests__/slack-mention-binding-invariance.test.ts +103 -0
  198. package/src/__tests__/slack-mention-provider-leak-audit.test.ts +135 -0
  199. package/src/__tests__/starter-task-flow.test.ts +5 -4
  200. package/src/__tests__/steer-on-enqueue-question.test.ts +3 -2
  201. package/src/__tests__/stt-language-catalog-parity.test.ts +38 -0
  202. package/src/__tests__/subagent-allowlist-validation.test.ts +26 -4
  203. package/src/__tests__/subagent-cron-run-attribution.test.ts +301 -0
  204. package/src/__tests__/subagent-disposal.test.ts +10 -0
  205. package/src/__tests__/subagent-fork-notifications.test.ts +10 -0
  206. package/src/__tests__/subagent-fork-prompt-role.test.ts +85 -11
  207. package/src/__tests__/subagent-fork-spawn.test.ts +15 -7
  208. package/src/__tests__/subagent-manager-notify.test.ts +148 -0
  209. package/src/__tests__/subagent-notify-parent.test.ts +1 -1
  210. package/src/__tests__/subagent-persistence.test.ts +141 -0
  211. package/src/__tests__/subagent-role-registry.test.ts +172 -63
  212. package/src/__tests__/subagent-role-resolution.test.ts +88 -0
  213. package/src/__tests__/subagent-spawn-and-await.test.ts +48 -0
  214. package/src/__tests__/subagent-spawn-tool-fork.test.ts +111 -10
  215. package/src/__tests__/subagent-terminal-message.test.ts +88 -1
  216. package/src/__tests__/subagent-tool-gate-mode.test.ts +225 -10
  217. package/src/__tests__/subagent-tools.test.ts +1901 -108
  218. package/src/__tests__/surface-completion-compaction-boundary.test.ts +124 -0
  219. package/src/__tests__/surface-completion-in-flight-snapshot.test.ts +298 -0
  220. package/src/__tests__/system-prompt.test.ts +82 -7
  221. package/src/__tests__/tool-grant-request-escalation.test.ts +40 -1
  222. package/src/__tests__/tool-result-spool.test.ts +74 -18
  223. package/src/__tests__/tool-side-effects-documents.test.ts +174 -0
  224. package/src/__tests__/tool-side-effects-hook-failures.test.ts +129 -0
  225. package/src/__tests__/tools-audio-read.test.ts +10 -10
  226. package/src/__tests__/tools-get-route.test.ts +85 -12
  227. package/src/__tests__/tools-list-cli.test.ts +171 -0
  228. package/src/__tests__/top-level-renderer.test.ts +54 -1
  229. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +68 -1
  230. package/src/__tests__/trusted-contact-multichannel.test.ts +4 -1
  231. package/src/__tests__/turn-tail-chain.test.ts +135 -0
  232. package/src/__tests__/ui-choice-copy-surfaces.test.ts +6 -4
  233. package/src/__tests__/ui-visual-surface.test.ts +5 -4
  234. package/src/__tests__/ui-work-result-surface.test.ts +5 -4
  235. package/src/__tests__/unified-turn-context-visible-app.test.ts +7 -8
  236. package/src/__tests__/usage-routes.test.ts +62 -0
  237. package/src/__tests__/verification-outbound-delivery.test.ts +176 -0
  238. package/src/__tests__/visible-app-context.test.ts +20 -4
  239. package/src/__tests__/web-search-history.test.ts +96 -0
  240. package/src/__tests__/workspace-migration-139-clear-renamed-cost-profile-label.test.ts +6 -6
  241. package/src/__tests__/workspace-migration-140-repair-seed-pinned-memory-v3-live.test.ts +330 -0
  242. package/src/acp/failure-error.ts +3 -5
  243. package/src/agent/attachments.ts +26 -24
  244. package/src/agent/history-repair/history-repair.ts +27 -26
  245. package/src/agent/image-optimize.ts +6 -6
  246. package/src/agent/loop.ts +40 -22
  247. package/src/agent/message-types.ts +3 -3
  248. package/src/api/constants/document-tools.ts +55 -0
  249. package/src/api/events/question-answered.ts +53 -0
  250. package/src/api/events/tool-result.ts +7 -0
  251. package/src/api/index.ts +13 -0
  252. package/src/api/responses/conversation-message.ts +11 -0
  253. package/src/api/surfaces-oauth-connect.test.ts +96 -0
  254. package/src/api/surfaces.ts +36 -8
  255. package/src/approvals/guardian-channel-delivery.ts +83 -0
  256. package/src/approvals/guardian-expiry-notifier.ts +13 -11
  257. package/src/approvals/guardian-request-resolvers.ts +222 -135
  258. package/src/calls/__tests__/voice-session-bridge.test.ts +50 -0
  259. package/src/calls/media-stream-server.ts +9 -1
  260. package/src/calls/voice-session-bridge.ts +38 -0
  261. package/src/channels/__tests__/gateway-verification-sessions.test.ts +3 -3
  262. package/src/channels/config.ts +7 -2
  263. package/src/channels/gateway-verification-sessions.ts +7 -2
  264. package/src/channels/types.ts +7 -1
  265. package/src/cli/commands/__tests__/cache.test.ts +7 -4
  266. package/src/cli/commands/__tests__/cli-test-harness.ts +82 -0
  267. package/src/cli/commands/__tests__/conversations-wake.test.ts +51 -6
  268. package/src/cli/commands/__tests__/inference-models.test.ts +14 -0
  269. package/src/cli/commands/__tests__/inference-profiles.test.ts +44 -1
  270. package/src/cli/commands/__tests__/inference-providers.test.ts +7 -0
  271. package/src/cli/commands/__tests__/plugins.test.ts +692 -0
  272. package/src/cli/commands/__tests__/schedules.test.ts +136 -37
  273. package/src/cli/commands/conversations.ts +7 -3
  274. package/src/cli/commands/credentials.help.ts +7 -0
  275. package/src/cli/commands/inference-profiles.ts +9 -3
  276. package/src/cli/commands/inference.help.ts +16 -2
  277. package/src/cli/commands/mcp.help.ts +4 -2
  278. package/src/cli/commands/memory/memory-retrospective.ts +18 -0
  279. package/src/cli/commands/notifications.ts +3 -6
  280. package/src/cli/commands/oauth/connect-surface-guidance.test.ts +16 -0
  281. package/src/cli/commands/oauth/connect-surface-guidance.ts +25 -7
  282. package/src/cli/commands/oauth/connect.test.ts +26 -1
  283. package/src/cli/commands/oauth/connect.ts +4 -1
  284. package/src/cli/commands/oauth/index.help.ts +3 -1
  285. package/src/cli/commands/oauth/providers.ts +3 -0
  286. package/src/cli/commands/plugins.help.ts +25 -2
  287. package/src/cli/commands/plugins.ts +289 -10
  288. package/src/cli/commands/schedules.help.ts +14 -12
  289. package/src/cli/commands/schedules.ts +19 -5
  290. package/src/cli/commands/tools.help.ts +4 -2
  291. package/src/cli/commands/tools.ts +38 -0
  292. package/src/cli/lib/__tests__/inspect-plugin.test.ts +1 -0
  293. package/src/cli/lib/__tests__/install-from-github.test.ts +79 -0
  294. package/src/cli/lib/__tests__/install-from-platform.test.ts +47 -1
  295. package/src/cli/lib/__tests__/plugin-surfaces.test.ts +86 -7
  296. package/src/cli/lib/__tests__/upgrade-plugin.test.ts +122 -0
  297. package/src/cli/lib/install-from-github.ts +61 -0
  298. package/src/cli/lib/install-from-platform.ts +13 -2
  299. package/src/cli/lib/plugin-surfaces.ts +124 -4
  300. package/src/cli/lib/upgrade-plugin.ts +18 -0
  301. package/src/cli.ts +1 -1
  302. package/src/config/__tests__/deployment-context-defaults.test.ts +73 -0
  303. package/src/config/__tests__/memory-retrospective-schema.test.ts +40 -0
  304. package/src/config/__tests__/plugin-updates-schema.test.ts +43 -0
  305. package/src/config/__tests__/profile-tool-support.test.ts +131 -0
  306. package/src/config/bundled-skills/AGENTS.md +1 -1
  307. package/src/config/bundled-skills/app-builder/SKILL.md +1 -1
  308. package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +3 -3
  309. package/src/config/bundled-skills/schedule/SKILL.md +5 -1
  310. package/src/config/bundled-skills/schedule/TOOLS.json +2 -2
  311. package/src/config/bundled-skills/settings/TOOLS.json +2 -2
  312. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +1 -0
  313. package/src/config/bundled-skills/subagent/SKILL.md +53 -22
  314. package/src/config/bundled-skills/subagent/TOOLS.json +14 -13
  315. package/src/config/bundled-skills/visualize/SKILL.md +1 -1
  316. package/src/config/default-profile-catalog.ts +6 -2
  317. package/src/config/feature-flag-registry.json +25 -17
  318. package/src/config/llm-resolver.ts +45 -15
  319. package/src/config/loader.ts +70 -1
  320. package/src/config/profile-tool-support.ts +110 -0
  321. package/src/config/sanitize-for-transfer.ts +11 -0
  322. package/src/config/schema.ts +4 -0
  323. package/src/config/schemas/__tests__/stt.test.ts +20 -5
  324. package/src/config/schemas/channels.ts +13 -0
  325. package/src/config/schemas/memory-retrospective.ts +25 -0
  326. package/src/config/schemas/plugin-updates.ts +55 -0
  327. package/src/config/schemas/services.ts +8 -4
  328. package/src/config/schemas/stt.ts +17 -5
  329. package/src/config/schemas/timeouts.ts +1 -1
  330. package/src/contacts/__tests__/guardian-delivery-reader.test.ts +16 -0
  331. package/src/contacts/guardian-delivery-reader.ts +29 -2
  332. package/src/context/compactor.ts +4 -4
  333. package/src/context/post-turn-tool-result-truncation.ts +32 -16
  334. package/src/context/strip-injections.ts +42 -0
  335. package/src/context/tool-result-spool.ts +32 -18
  336. package/src/credential-execution/ces-runtime.ts +3 -0
  337. package/src/credential-execution/client.ts +22 -1
  338. package/src/credential-execution/process-manager.test.ts +99 -0
  339. package/src/credential-execution/process-manager.ts +62 -16
  340. package/src/daemon/__tests__/abort-null-controller.test.ts +141 -5
  341. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +8 -12
  342. package/src/daemon/__tests__/install-assistant-command.test.ts +234 -0
  343. package/src/daemon/__tests__/turn-tail-assistant-reply-notify.test.ts +2 -4
  344. package/src/daemon/__tests__/turn-tail-deleted-conversation.test.ts +20 -29
  345. package/src/daemon/channel-ui-capability.ts +4 -4
  346. package/src/daemon/conversation-agent-loop-handlers.ts +117 -61
  347. package/src/daemon/conversation-agent-loop.ts +318 -148
  348. package/src/daemon/conversation-attachments.ts +1 -1
  349. package/src/daemon/conversation-error.ts +8 -1
  350. package/src/daemon/conversation-initial-prompt.ts +16 -38
  351. package/src/daemon/conversation-lifecycle.ts +154 -32
  352. package/src/daemon/conversation-messaging.ts +106 -43
  353. package/src/daemon/conversation-process.ts +131 -33
  354. package/src/daemon/conversation-queue-manager.ts +10 -0
  355. package/src/daemon/conversation-runtime-assembly.ts +9 -4
  356. package/src/daemon/conversation-store.ts +52 -3
  357. package/src/daemon/conversation-surface-state.ts +9 -0
  358. package/src/daemon/conversation-surfaces.ts +403 -210
  359. package/src/daemon/conversation-tool-setup.ts +56 -7
  360. package/src/daemon/conversation-turn-finalize.ts +134 -89
  361. package/src/daemon/conversation.ts +111 -27
  362. package/src/daemon/disk-pressure-guard-lifecycle.ts +2 -2
  363. package/src/daemon/disk-pressure-guard.ts +3 -3
  364. package/src/daemon/doordash-steps.ts +5 -5
  365. package/src/daemon/external-plugins-bootstrap.ts +7 -1
  366. package/src/daemon/handlers/config-channels.ts +48 -5
  367. package/src/daemon/handlers/conversation-history.ts +1 -1
  368. package/src/daemon/handlers/conversations.ts +62 -17
  369. package/src/daemon/handlers/shared.ts +12 -0
  370. package/src/daemon/handlers/skills.ts +11 -1
  371. package/src/daemon/identity-helpers.ts +38 -10
  372. package/src/daemon/install-assistant-command.ts +322 -0
  373. package/src/daemon/lifecycle.ts +14 -3
  374. package/src/daemon/message-types/conversations.ts +6 -7
  375. package/src/daemon/message-types/sync.ts +1 -0
  376. package/src/daemon/persist-media-references.ts +23 -14
  377. package/src/daemon/process-message.ts +6 -6
  378. package/src/daemon/tool-setup-types.ts +19 -122
  379. package/src/daemon/tool-side-effects.ts +17 -94
  380. package/src/daemon/trust-context-types.ts +39 -0
  381. package/src/daemon/trust-context.ts +14 -6
  382. package/src/daemon/turn-tail-chain.ts +73 -0
  383. package/src/daemon/web-search-history.ts +47 -33
  384. package/src/heartbeat/heartbeat-service.ts +6 -6
  385. package/src/ipc/__tests__/socket-path.test.ts +21 -0
  386. package/src/ipc/assistant-server.ts +17 -10
  387. package/src/ipc/cli-client.ts +10 -1
  388. package/src/ipc/routes/__tests__/documents-sync-ipc-routes.test.ts +56 -0
  389. package/src/ipc/routes/documents-sync-ipc-routes.ts +42 -0
  390. package/src/ipc/socket-cleanup.ts +9 -7
  391. package/src/ipc/socket-path.ts +4 -96
  392. package/src/live-voice/__tests__/live-voice-archive.test.ts +16 -16
  393. package/src/live-voice/__tests__/live-voice-attach-image.test.ts +209 -0
  394. package/src/live-voice/__tests__/live-voice-connection.test.ts +20 -0
  395. package/src/live-voice/__tests__/live-voice-integration.test.ts +7 -5
  396. package/src/live-voice/__tests__/live-voice-session-telemetry.test.ts +265 -0
  397. package/src/live-voice/__tests__/live-voice-stt.test.ts +6 -1
  398. package/src/live-voice/__tests__/live-voice-vad.test.ts +35 -15
  399. package/src/live-voice/__tests__/protocol.test.ts +52 -0
  400. package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +3 -0
  401. package/src/live-voice/live-voice-archive.ts +13 -13
  402. package/src/live-voice/live-voice-connection.ts +7 -1
  403. package/src/live-voice/live-voice-photo.ts +169 -0
  404. package/src/live-voice/live-voice-session.ts +108 -4
  405. package/src/live-voice/protocol.ts +89 -1
  406. package/src/mcp/client.ts +10 -0
  407. package/src/messaging/providers/__tests__/callback-routing.test.ts +5 -1
  408. package/src/messaging/providers/__tests__/transport-dispatch.test.ts +66 -4
  409. package/src/messaging/providers/callback-routing.ts +1 -0
  410. package/src/messaging/providers/discord/api.ts +312 -0
  411. package/src/messaging/providers/discord/dm-delivery.test.ts +204 -0
  412. package/src/messaging/providers/discord/render.test.ts +204 -0
  413. package/src/messaging/providers/discord/render.ts +195 -0
  414. package/src/messaging/providers/discord/send.test.ts +179 -0
  415. package/src/messaging/providers/discord/send.ts +212 -0
  416. package/src/messaging/providers/discord/transport.ts +81 -0
  417. package/src/messaging/providers/index.ts +3 -1
  418. package/src/messaging/providers/retry-policy.test.ts +247 -0
  419. package/src/messaging/providers/retry-policy.ts +206 -0
  420. package/src/messaging/providers/slack/mention-source.test.ts +285 -0
  421. package/src/messaging/providers/slack/mention-source.ts +287 -0
  422. package/src/messaging/providers/telegram-bot/api.ts +47 -130
  423. package/src/messaging/providers/whatsapp/api.ts +47 -136
  424. package/src/monitoring/__tests__/db-integrity-sample.test.ts +4 -1
  425. package/src/monitoring/__tests__/plugin-auto-update.test.ts +332 -0
  426. package/src/monitoring/db-integrity-sample.ts +6 -1
  427. package/src/monitoring/plugin-auto-update.ts +359 -0
  428. package/src/monitoring/resource-sampler.ts +21 -5
  429. package/src/monitoring/worker.ts +13 -0
  430. package/src/notifications/__tests__/assistant-reply-producer.test.ts +375 -2
  431. package/src/notifications/__tests__/broadcaster.test.ts +37 -1
  432. package/src/notifications/__tests__/copy-composer.test.ts +165 -0
  433. package/src/notifications/__tests__/emit-signal-pipeline-failure.test.ts +205 -0
  434. package/src/notifications/__tests__/notification-utils.test.ts +249 -0
  435. package/src/notifications/adapters/platform.ts +31 -2
  436. package/src/notifications/assistant-reply-producer.ts +79 -32
  437. package/src/notifications/broadcaster.ts +30 -10
  438. package/src/notifications/copy-composer.ts +69 -0
  439. package/src/notifications/emit-signal.ts +82 -15
  440. package/src/notifications/events-store.ts +10 -2
  441. package/src/notifications/home-feed-side-effect.ts +6 -3
  442. package/src/notifications/notification-utils.ts +173 -5
  443. package/src/notifications/signal.ts +12 -0
  444. package/src/oauth/AGENTS.md +4 -2
  445. package/src/oauth/byo-connection.ts +6 -1
  446. package/src/oauth/credential-token-resolver.ts +3 -10
  447. package/src/oauth/manual-token-connection.ts +29 -64
  448. package/src/oauth/manual-token-providers.ts +62 -0
  449. package/src/oauth/provider-serializer.ts +13 -0
  450. package/src/oauth/seed-providers.ts +62 -11
  451. package/src/onboarding/onboarding-events-store.ts +91 -1
  452. package/src/permissions/question-prompter.test.ts +4 -0
  453. package/src/permissions/question-prompter.ts +21 -4
  454. package/src/persistence/__tests__/conversation-lineage.test.ts +248 -0
  455. package/src/persistence/attachments-store.ts +15 -15
  456. package/src/persistence/conversation-attention-store.ts +30 -10
  457. package/src/persistence/conversation-bootstrap.ts +17 -0
  458. package/src/persistence/conversation-crud.ts +363 -97
  459. package/src/persistence/conversation-group-migration.ts +1 -1
  460. package/src/persistence/conversation-key-store.ts +38 -10
  461. package/src/persistence/conversation-lineage.ts +226 -0
  462. package/src/persistence/conversation-plugin-facade.ts +28 -3
  463. package/src/persistence/conversation-queries.ts +207 -33
  464. package/src/persistence/conversation-types.ts +107 -1
  465. package/src/persistence/jobs-store.ts +32 -8
  466. package/src/persistence/llm-usage-store.ts +22 -0
  467. package/src/persistence/migrations/361-normalize-managed-connection-rows.test.ts +168 -0
  468. package/src/persistence/migrations/361-normalize-managed-connection-rows.ts +76 -0
  469. package/src/persistence/migrations/362-add-conversation-subagent-kind.test.ts +187 -0
  470. package/src/persistence/migrations/362-add-conversation-subagent-kind.ts +93 -0
  471. package/src/persistence/migrations/363-backfill-schedule-inference-profile.test.ts +229 -0
  472. package/src/persistence/migrations/363-backfill-schedule-inference-profile.ts +127 -0
  473. package/src/persistence/migrations/364-add-schedule-source-key.test.ts +120 -0
  474. package/src/persistence/migrations/364-add-schedule-source-key.ts +43 -0
  475. package/src/persistence/migrations/365-add-conversation-fork-strategy.ts +27 -0
  476. package/src/persistence/schema/conversations.ts +32 -0
  477. package/src/persistence/schema/infrastructure.ts +3 -0
  478. package/src/persistence/steps.ts +30 -0
  479. package/src/persistence/subagent-store.ts +148 -6
  480. package/src/platform/managed-speech.test.ts +20 -0
  481. package/src/platform/managed-speech.ts +9 -0
  482. package/src/plugin-api/index.ts +8 -0
  483. package/src/plugin-api/webhook-url.ts +151 -0
  484. package/src/plugins/__tests__/installed-plugin-dirs.test.ts +82 -0
  485. package/src/plugins/__tests__/source-fingerprint.test.ts +5 -1
  486. package/src/plugins/collect-source-versions.ts +4 -27
  487. package/src/plugins/defaults/exploration-drift/hooks/post-tool-use.ts +5 -5
  488. package/src/plugins/defaults/exploration-drift/package.json +1 -1
  489. package/src/plugins/defaults/image-recovery/hooks/post-model-call.ts +4 -2
  490. package/src/plugins/defaults/image-recovery/recover.ts +67 -57
  491. package/src/plugins/defaults/index.ts +1 -1
  492. package/src/plugins/defaults/memory/__tests__/conversation-queries.test.ts +64 -18
  493. package/src/plugins/defaults/memory/__tests__/jobs-worker-outcome-contract.test.ts +242 -0
  494. package/src/plugins/defaults/memory/__tests__/jobs-worker-retrospective-sweep.test.ts +24 -1
  495. package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +48 -3
  496. package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +511 -19
  497. package/src/plugins/defaults/memory/__tests__/memory-retrospective-prompt.test.ts +39 -3
  498. package/src/plugins/defaults/memory/__tests__/memory-retrospective-provider-path.test.ts +493 -0
  499. package/src/plugins/defaults/memory/__tests__/memory-retrospective-wake-chain.test.ts +5 -4
  500. package/src/plugins/defaults/memory/__tests__/memory-tier-boundary-guard.test.ts +5 -1
  501. package/src/plugins/defaults/memory/job-handlers.ts +87 -5
  502. package/src/plugins/defaults/memory/jobs-worker.ts +60 -6
  503. package/src/plugins/defaults/memory/memory-retrospective-constants.ts +11 -0
  504. package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +29 -0
  505. package/src/plugins/defaults/memory/memory-retrospective-job.ts +342 -60
  506. package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +23 -3
  507. package/src/plugins/defaults/memory/substrate/consolidation-lock.ts +9 -4
  508. package/src/plugins/defaults/memory/substrate/sweep-job.ts +7 -0
  509. package/src/plugins/defaults/memory/v1/graph/injection.ts +1 -1
  510. package/src/plugins/defaults/memory/v2/router.ts +8 -0
  511. package/src/plugins/defaults/turn-context/unified-turn-context.ts +1 -2
  512. package/src/plugins/external-plugin-loader.ts +17 -8
  513. package/src/plugins/installed-plugin-dirs.ts +101 -0
  514. package/src/plugins/mtime-cache.ts +34 -22
  515. package/src/plugins/types.ts +69 -8
  516. package/src/prompts/persona-resolver.ts +6 -2
  517. package/src/prompts/system-prompt.ts +72 -17
  518. package/src/providers/__tests__/inference.test.ts +40 -0
  519. package/src/providers/__tests__/preflight-resolved-config.test.ts +58 -1
  520. package/src/providers/__tests__/retry-callsite.test.ts +67 -0
  521. package/src/providers/__tests__/vellum-connection-routing.test.ts +42 -11
  522. package/src/providers/anthropic/client.ts +112 -95
  523. package/src/providers/connection-resolution.ts +35 -1
  524. package/src/providers/gemini/client.ts +5 -5
  525. package/src/providers/inference/adapter-factory.ts +16 -10
  526. package/src/providers/inference/auth.ts +16 -0
  527. package/src/providers/inference/connection-availability.ts +164 -12
  528. package/src/providers/inference/connections.ts +25 -1
  529. package/src/providers/media-resolve.ts +21 -11
  530. package/src/providers/model-catalog.ts +15 -0
  531. package/src/providers/openai/chat-completions-provider.ts +4 -4
  532. package/src/providers/openai/responses-provider.ts +12 -25
  533. package/src/providers/registry.ts +9 -4
  534. package/src/providers/retry.ts +25 -0
  535. package/src/providers/server-tool-pairing.ts +134 -0
  536. package/src/providers/speech-to-text/__tests__/resolve.test.ts +187 -6
  537. package/src/providers/speech-to-text/deepgram.ts +1 -0
  538. package/src/providers/speech-to-text/resolve.ts +80 -10
  539. package/src/providers/speech-to-text/vellum-managed.ts +7 -1
  540. package/src/providers/speech-to-text/vellum-speech-relay-connection.ts +3 -1
  541. package/src/providers/types.ts +26 -6
  542. package/src/providers/vellum-model-routing.ts +6 -7
  543. package/src/runtime/AGENTS.md +2 -0
  544. package/src/runtime/__tests__/agent-wake.test.ts +16 -16
  545. package/src/runtime/__tests__/background-job-runner.test.ts +0 -144
  546. package/src/runtime/__tests__/desktop-presence.test.ts +162 -0
  547. package/src/runtime/agent-wake.ts +63 -24
  548. package/src/runtime/assistant-event-hub.ts +87 -12
  549. package/src/runtime/auth/__tests__/guard-tests.test.ts +2 -0
  550. package/src/runtime/auth/__tests__/scopes.test.ts +20 -0
  551. package/src/runtime/auth/same-actor.ts +56 -8
  552. package/src/runtime/auth/scopes.ts +12 -0
  553. package/src/runtime/auth/types.ts +2 -0
  554. package/src/runtime/background-job-runner.ts +5 -27
  555. package/src/runtime/channel-readiness-service.ts +59 -3
  556. package/src/runtime/channel-readiness-types.ts +18 -0
  557. package/src/runtime/desktop-presence.ts +69 -0
  558. package/src/runtime/http-server.ts +12 -0
  559. package/src/runtime/local-actor-identity.ts +11 -9
  560. package/src/runtime/migrations/__tests__/vbundle-symlink-streaming-importer.test.ts +0 -0
  561. package/src/runtime/routes/__tests__/channel-verification-routes.test.ts +1 -1
  562. package/src/runtime/routes/__tests__/client-routes.test.ts +186 -14
  563. package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +547 -1
  564. package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +162 -7
  565. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +14 -10
  566. package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +340 -3
  567. package/src/runtime/routes/__tests__/llm-call-sites-routes.test.ts +28 -1
  568. package/src/runtime/routes/__tests__/plugins-routes.test.ts +173 -1
  569. package/src/runtime/routes/__tests__/question-routes.test.ts +64 -0
  570. package/src/runtime/routes/__tests__/retrospective-routes.test.ts +20 -0
  571. package/src/runtime/routes/__tests__/skill-history-route.test.ts +116 -0
  572. package/src/runtime/routes/__tests__/wake-conversation-routes.test.ts +60 -2
  573. package/src/runtime/routes/archive-utils.ts +16 -12
  574. package/src/runtime/routes/attachment-routes.ts +13 -6
  575. package/src/runtime/routes/canned-reply-release.ts +54 -0
  576. package/src/runtime/routes/channel-verification-routes.ts +2 -27
  577. package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +5 -0
  578. package/src/runtime/routes/client-routes.ts +114 -2
  579. package/src/runtime/routes/conversation-cli-routes.ts +10 -12
  580. package/src/runtime/routes/conversation-list-routes.ts +59 -10
  581. package/src/runtime/routes/conversation-management-routes.ts +47 -18
  582. package/src/runtime/routes/conversation-query-routes.ts +32 -53
  583. package/src/runtime/routes/conversation-routes.ts +360 -293
  584. package/src/runtime/routes/credential-prompt-routes.ts +23 -2
  585. package/src/runtime/routes/debug-routes.ts +4 -1
  586. package/src/runtime/routes/default-provider-routes.ts +7 -11
  587. package/src/runtime/routes/documents-routes.ts +44 -1
  588. package/src/runtime/routes/events-routes.ts +28 -1
  589. package/src/runtime/routes/host-app-control-routes.ts +2 -0
  590. package/src/runtime/routes/host-bash-routes.ts +2 -0
  591. package/src/runtime/routes/host-browser-routes.ts +2 -0
  592. package/src/runtime/routes/host-cu-routes.ts +2 -0
  593. package/src/runtime/routes/host-file-routes.ts +2 -0
  594. package/src/runtime/routes/host-transfer-routes.ts +4 -0
  595. package/src/runtime/routes/identity-routes.ts +6 -6
  596. package/src/runtime/routes/inbound-message-handler.ts +5 -5
  597. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +7 -3
  598. package/src/runtime/routes/inbound-stages/background-dispatch.ts +14 -0
  599. package/src/runtime/routes/inbound-stages/bootstrap-intercept.ts +2 -2
  600. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +14 -11
  601. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +14 -17
  602. package/src/runtime/routes/inference-profile-availability-guard.ts +171 -0
  603. package/src/runtime/routes/inference-profile-session-handler.ts +26 -2
  604. package/src/runtime/routes/inference-profile-session-routes.ts +2 -1
  605. package/src/runtime/routes/inference-profiles-routes.ts +152 -73
  606. package/src/runtime/routes/inference-provider-connection-routes.ts +0 -16
  607. package/src/runtime/routes/llm-call-sites-routes.ts +13 -0
  608. package/src/runtime/routes/log-export-routes.ts +1 -1
  609. package/src/runtime/routes/notification-routes.ts +0 -14
  610. package/src/runtime/routes/oauth-providers.ts +1 -0
  611. package/src/runtime/routes/plugins-routes.ts +106 -1
  612. package/src/runtime/routes/profiler-routes.ts +4 -2
  613. package/src/runtime/routes/question-routes.ts +28 -10
  614. package/src/runtime/routes/retrospective-routes.ts +9 -3
  615. package/src/runtime/routes/schedule-routes.ts +210 -12
  616. package/src/runtime/routes/secret-routes.ts +40 -23
  617. package/src/runtime/routes/settings-routes.ts +41 -13
  618. package/src/runtime/routes/skills-routes.ts +79 -0
  619. package/src/runtime/routes/ui-snapshot-routes.ts +1 -0
  620. package/src/runtime/routes/wake-conversation-routes.ts +23 -0
  621. package/src/runtime/services/__tests__/conversation-serializer.test.ts +1 -0
  622. package/src/runtime/services/conversation-serializer.ts +24 -9
  623. package/src/runtime/sync/documents-sidecar-publish.test.ts +101 -0
  624. package/src/runtime/sync/resource-sync-events.ts +79 -1
  625. package/src/runtime/sync/worker-daemon-notify.test.ts +34 -0
  626. package/src/runtime/sync/worker-daemon-notify.ts +44 -6
  627. package/src/runtime/verification-outbound-actions.ts +250 -222
  628. package/src/runtime/verification-templates.ts +41 -30
  629. package/src/schedule/__tests__/plugin-schedule-declarations.test.ts +614 -0
  630. package/src/schedule/__tests__/plugin-schedule-reconciler.test.ts +1066 -0
  631. package/src/schedule/inference-profile.ts +64 -3
  632. package/src/schedule/plugin-schedule-declarations.ts +518 -0
  633. package/src/schedule/plugin-schedule-reconciler.ts +605 -0
  634. package/src/schedule/plugin-schedules-gate.ts +23 -0
  635. package/src/schedule/schedule-store.ts +520 -26
  636. package/src/schedule/scheduler.ts +38 -0
  637. package/src/security/credential-backend.ts +51 -4
  638. package/src/security/encrypted-store.ts +64 -42
  639. package/src/security/secure-keys.ts +55 -7
  640. package/src/skills/catalog-install.ts +20 -7
  641. package/src/skills/inline-command-runner.ts +2 -7
  642. package/src/skills/skill-history.test.ts +269 -0
  643. package/src/skills/skill-history.ts +250 -0
  644. package/src/skills/skillssh-registry.ts +1 -1
  645. package/src/stt/daemon-batch-transcriber.ts +8 -6
  646. package/src/subagent/__tests__/consult-prompt.test.ts +25 -0
  647. package/src/subagent/consult-prompt.ts +3 -1
  648. package/src/subagent/consult-transcript.ts +1 -1
  649. package/src/subagent/manager.ts +304 -44
  650. package/src/subagent/progress-events.ts +35 -0
  651. package/src/subagent/role-resolution.ts +88 -0
  652. package/src/subagent/types.ts +302 -67
  653. package/src/subagent/validate-allowlists.ts +3 -4
  654. package/src/telegram/__tests__/webhook-health.test.ts +73 -1
  655. package/src/telegram/webhook-health.ts +101 -3
  656. package/src/telemetry/__tests__/telemetry-event-fixtures.ts +2 -0
  657. package/src/telemetry/activation-funnel.ts +6 -1
  658. package/src/telemetry/live-voice-funnel.ts +75 -0
  659. package/src/telemetry/telemetry-event-sources.ts +6 -0
  660. package/src/telemetry/telemetry-wire-source.json +1 -1
  661. package/src/telemetry/telemetry-wire.generated.ts +2 -0
  662. package/src/telemetry/types.ts +23 -4
  663. package/src/telemetry/usage-telemetry-reporter.test.ts +4 -0
  664. package/src/tools/__tests__/subagent-workflow-tool-input-schemas.test.ts +1 -1
  665. package/src/tools/ask-question/ask-question-tool.test.ts +118 -3
  666. package/src/tools/ask-question/ask-question-tool.ts +56 -2
  667. package/src/tools/browser/browser-execution.ts +1 -1
  668. package/src/tools/calls/call-start.ts +8 -5
  669. package/src/tools/conversation-groups/move_to_group.ts +10 -12
  670. package/src/tools/document/document-tool.ts +11 -4
  671. package/src/tools/execution-timeout.ts +9 -2
  672. package/src/tools/executor.ts +7 -6
  673. package/src/tools/filesystem/edit.ts +1 -1
  674. package/src/tools/filesystem/list.ts +1 -1
  675. package/src/tools/filesystem/read.ts +7 -13
  676. package/src/tools/filesystem/search.ts +23 -21
  677. package/src/tools/filesystem/write.ts +4 -1
  678. package/src/tools/host-filesystem/edit.ts +7 -2
  679. package/src/tools/host-filesystem/read.ts +21 -8
  680. package/src/tools/host-filesystem/write.ts +10 -2
  681. package/src/tools/host-terminal/host-shell.ts +2 -2
  682. package/src/tools/mcp/mcp-tool-factory.ts +25 -1
  683. package/src/tools/permission-checker.ts +8 -3
  684. package/src/tools/schedule/delete.ts +13 -1
  685. package/src/tools/schedule/list.ts +14 -3
  686. package/src/tools/schedule/update.ts +56 -33
  687. package/src/tools/shared/filesystem/audio-read.ts +6 -4
  688. package/src/tools/shared/filesystem/file-ops-service.ts +176 -116
  689. package/src/tools/shared/filesystem/image-read.ts +12 -12
  690. package/src/tools/shared/filesystem/path-policy.ts +70 -5
  691. package/src/tools/shared/filesystem/size-guard.ts +6 -6
  692. package/src/tools/shared/input-misuse.ts +72 -0
  693. package/src/tools/skills/scaffold-managed.ts +82 -0
  694. package/src/tools/skills/skill-tool-factory.ts +14 -1
  695. package/src/tools/subagent/consult-deadline.ts +12 -7
  696. package/src/tools/subagent/message.ts +3 -1
  697. package/src/tools/subagent/read.ts +66 -4
  698. package/src/tools/subagent/spawn.ts +500 -78
  699. package/src/tools/tool-approval-handler.ts +45 -4
  700. package/src/tools/types.ts +22 -3
  701. package/src/tools/ui-surface/surface-shape-docs.ts +1 -1
  702. package/src/tools/ui-surface/visual-validation.ts +2 -0
  703. package/src/usage/__tests__/subagent-attribution.test.ts +109 -0
  704. package/src/usage/subagent-attribution.ts +108 -0
  705. package/src/util/__tests__/sqlite-retry.test.ts +112 -0
  706. package/src/util/abort-reasons.ts +16 -0
  707. package/src/util/ansi.ts +44 -0
  708. package/src/util/disk-usage.ts +17 -13
  709. package/src/util/image-conversion.ts +20 -13
  710. package/src/util/schedule-source-key.ts +23 -0
  711. package/src/util/worker-process.ts +7 -1
  712. package/src/workspace/git-service.ts +33 -9
  713. package/src/workspace/migrations/140-repair-seed-pinned-memory-v3-live.ts +260 -0
  714. package/src/workspace/migrations/141-stt-english-default-to-multilingual.ts +111 -0
  715. package/src/workspace/migrations/__tests__/141-stt-english-default-to-multilingual.test.ts +126 -0
  716. package/src/workspace/migrations/registry.ts +4 -0
  717. package/src/workspace/top-level-renderer.ts +15 -2
  718. package/src/__tests__/tool-side-effects-slack-dm.test.ts +0 -357
  719. package/src/daemon/install-symlink.ts +0 -207
  720. package/src/notifications/deferred-emit.ts +0 -147
@@ -163,6 +163,26 @@ describe("createLiveVoiceConnection", () => {
163
163
  });
164
164
  });
165
165
 
166
+ test("names the rejected frame on an unknown_type error", async () => {
167
+ // A client that sends more than one optional frame gets the same
168
+ // `unknown_type` for any of them. Without the name it has to guess which,
169
+ // and guessing wrong is silent: a photo is dropped with nothing said, or
170
+ // the voice-room settings quietly stop applying for the session.
171
+ installFakeManager();
172
+ const { connection, frames } = createConnection();
173
+
174
+ await connection.handleMessage(START_MESSAGE);
175
+ await connection.handleMessage(
176
+ JSON.stringify({ type: "attach_image_from_the_future" }),
177
+ );
178
+
179
+ expect(frames.at(-1)).toMatchObject({
180
+ type: "error",
181
+ code: "unknown_type",
182
+ frameType: "attach_image_from_the_future",
183
+ });
184
+ });
185
+
166
186
  test("errors on a non-start frame before start", async () => {
167
187
  installFakeManager();
168
188
  const { connection, frames } = createConnection();
@@ -736,9 +736,9 @@ describe("live-voice audio archiving default (JARVIS-1283)", () => {
736
736
 
737
737
  test("liveVoice.archiveAudio=true wires the default archiver through the factory", async () => {
738
738
  // The real linkers write to the DB; these are role-less inputs, so stub
739
- // them (synchronously the linkers are sync) with a valid archived result
740
- // per role. The point under test is the config→default-archiver wiring, not
741
- // the DB write, which the injected-archiver tests above already cover.
739
+ // them with a valid archived result per role. The point under test is the
740
+ // config→default-archiver wiring, not the DB write, which the
741
+ // injected-archiver tests above already cover.
742
742
  const audioInput = {
743
743
  sessionId: "session-123",
744
744
  turnId: "live-turn-1",
@@ -751,11 +751,13 @@ describe("live-voice audio archiving default (JARVIS-1283)", () => {
751
751
  const userSpy = spyOn(
752
752
  liveVoiceArchive,
753
753
  "linkLiveVoiceUserUtteranceAudioToMessage",
754
- ).mockReturnValue(makeArchiveResult({ ...audioInput, role: "user" }));
754
+ ).mockResolvedValue(makeArchiveResult({ ...audioInput, role: "user" }));
755
755
  const assistantSpy = spyOn(
756
756
  liveVoiceArchive,
757
757
  "linkLiveVoiceAssistantResponseAudioToMessage",
758
- ).mockReturnValue(makeArchiveResult({ ...audioInput, role: "assistant" }));
758
+ ).mockResolvedValue(
759
+ makeArchiveResult({ ...audioInput, role: "assistant" }),
760
+ );
759
761
  const originalRaw = loadRawConfig();
760
762
  saveRawConfig({ ...originalRaw, liveVoice: { archiveAudio: true } });
761
763
 
@@ -0,0 +1,265 @@
1
+ /**
2
+ * Live-voice session telemetry: the started/ended funnel rows a session
3
+ * records, and the attribution carried on its turns.
4
+ *
5
+ * The recorder is mocked: the outbox and its consent gate are covered by
6
+ * `telemetry-events-outbox.test.ts`. What these tests pin is the wiring that
7
+ * decides whether a row exists at all and what it says, which is where the
8
+ * measurement can silently go wrong: a session that fails before `ready` still
9
+ * has to count, a failed session still has to record an end, and a session's
10
+ * turns have to carry its id or the turn count has nothing to group on.
11
+ */
12
+
13
+ import { beforeEach, describe, expect, mock, test } from "bun:test";
14
+
15
+ import type { VoiceTurnOptions } from "../../calls/voice-session-bridge.js";
16
+ import type {
17
+ StreamingTranscriber,
18
+ SttStreamServerEvent,
19
+ } from "../../stt/types.js";
20
+
21
+ const recordLiveVoiceSessionStarted = mock(() => null);
22
+ const recordLiveVoiceSessionEnded = mock(() => null);
23
+
24
+ // Spread the real module rather than replacing it: other importers pull
25
+ // `recordActivationEvent` and friends out of it, and a bare replacement makes
26
+ // those imports fail to resolve.
27
+ const onboardingEventsStore =
28
+ await import("../../onboarding/onboarding-events-store.js");
29
+ mock.module("../../onboarding/onboarding-events-store.js", () => ({
30
+ ...onboardingEventsStore,
31
+ recordLiveVoiceSessionStarted,
32
+ recordLiveVoiceSessionEnded,
33
+ }));
34
+
35
+ // Types are erased, so they import statically regardless of the mock ordering
36
+ // the values below depend on.
37
+ import type { LiveVoiceCredentialReadiness } from "../live-voice-credential-preflight.js";
38
+ import type { LiveVoiceSessionFactoryContext } from "../live-voice-session-manager.js";
39
+ import type {
40
+ LiveVoiceClientStartFrame,
41
+ LiveVoiceServerFrame,
42
+ } from "../protocol.js";
43
+
44
+ // Values load after `mock.module` so the session picks up the mocked recorder.
45
+ const { LiveVoiceSession } = await import("../live-voice-session.js");
46
+ const { LiveVoiceSessionStartupError } =
47
+ await import("../live-voice-session-manager.js");
48
+ const { createLiveVoiceServerFrameSequencer } = await import("../protocol.js");
49
+
50
+ const START_FRAME = {
51
+ type: "start",
52
+ conversationId: "conversation-123",
53
+ audio: { mimeType: "audio/pcm", sampleRate: 24_000, channels: 1 },
54
+ } as const satisfies LiveVoiceClientStartFrame;
55
+
56
+ const NOT_READY: LiveVoiceCredentialReadiness = {
57
+ status: "not-ready",
58
+ missing: [
59
+ {
60
+ kind: "tts",
61
+ providerId: "fish-audio",
62
+ reason: 'TTS provider "fish-audio" is missing credentials',
63
+ },
64
+ ],
65
+ userMessage: "Live voice is unavailable because it requires an API key.",
66
+ };
67
+
68
+ class MockStreamingTranscriber implements StreamingTranscriber {
69
+ readonly providerId = "deepgram" as const;
70
+ readonly boundaryId = "daemon-streaming" as const;
71
+ private onEvent: ((event: SttStreamServerEvent) => void) | null = null;
72
+
73
+ constructor(private readonly stopEvents: SttStreamServerEvent[] = []) {}
74
+
75
+ async start(onEvent: (event: SttStreamServerEvent) => void): Promise<void> {
76
+ this.onEvent = onEvent;
77
+ }
78
+
79
+ sendAudio(): void {}
80
+
81
+ stop(): void {
82
+ for (const event of this.stopEvents) {
83
+ this.onEvent?.(event);
84
+ }
85
+ }
86
+ }
87
+
88
+ function createContext(
89
+ startFrame: LiveVoiceClientStartFrame = START_FRAME,
90
+ ): LiveVoiceSessionFactoryContext {
91
+ const sequencer = createLiveVoiceServerFrameSequencer();
92
+ const frames: LiveVoiceServerFrame[] = [];
93
+ return {
94
+ sessionId: "session-123",
95
+ startFrame,
96
+ sendFrame: mock(async (payload) => {
97
+ const frame = sequencer.next(payload);
98
+ frames.push(frame);
99
+ return frame;
100
+ }),
101
+ };
102
+ }
103
+
104
+ function readySession(startFrame?: LiveVoiceClientStartFrame) {
105
+ return new LiveVoiceSession(createContext(startFrame), {
106
+ resolveTranscriber: mock(async () => new MockStreamingTranscriber()),
107
+ resolveCredentialReadiness: mock(
108
+ async (): Promise<LiveVoiceCredentialReadiness> => ({ status: "ready" }),
109
+ ),
110
+ });
111
+ }
112
+
113
+ describe("live-voice session telemetry", () => {
114
+ beforeEach(() => {
115
+ recordLiveVoiceSessionStarted.mockClear();
116
+ recordLiveVoiceSessionEnded.mockClear();
117
+ });
118
+
119
+ test("records a started row for a session that reaches ready", async () => {
120
+ const session = readySession();
121
+
122
+ await session.start();
123
+
124
+ expect(recordLiveVoiceSessionStarted).toHaveBeenCalledWith("session-123");
125
+
126
+ await session.close("client_end");
127
+ });
128
+
129
+ test("records a started row even when the preflight rejects the session", async () => {
130
+ const session = new LiveVoiceSession(createContext(), {
131
+ resolveTranscriber: mock(async () => new MockStreamingTranscriber()),
132
+ resolveCredentialReadiness: mock(async () => NOT_READY),
133
+ });
134
+
135
+ await expect(session.start()).rejects.toBeInstanceOf(
136
+ LiveVoiceSessionStartupError,
137
+ );
138
+
139
+ // The denominator of the failure rate: a session rejected before `ready`
140
+ // is exactly the one that must not go missing from the started count.
141
+ expect(recordLiveVoiceSessionStarted).toHaveBeenCalledWith("session-123");
142
+ });
143
+
144
+ test("records the close reason and a completed outcome on a clean end", async () => {
145
+ const session = readySession();
146
+ await session.start();
147
+
148
+ await session.close("client_end");
149
+
150
+ expect(recordLiveVoiceSessionEnded).toHaveBeenCalledWith({
151
+ sessionId: "session-123",
152
+ screen: "ended_client_end",
153
+ outcome: "completed",
154
+ });
155
+ });
156
+
157
+ test("a dropped socket ends as completed, not failed", async () => {
158
+ const session = readySession();
159
+ await session.start();
160
+
161
+ await session.close("websocket_close");
162
+
163
+ // The session ran and then stopped; only an error makes it `failed`, and
164
+ // the reason on `screen` is what distinguishes a drop from a hangup.
165
+ expect(recordLiveVoiceSessionEnded).toHaveBeenCalledWith({
166
+ sessionId: "session-123",
167
+ screen: "ended_websocket_close",
168
+ outcome: "completed",
169
+ });
170
+ });
171
+
172
+ test("a failed session records an ended row carrying the failure code", async () => {
173
+ const session = new LiveVoiceSession(createContext(), {
174
+ resolveTranscriber: mock(async () => new MockStreamingTranscriber()),
175
+ resolveCredentialReadiness: mock(async () => NOT_READY),
176
+ });
177
+ await expect(session.start()).rejects.toBeInstanceOf(
178
+ LiveVoiceSessionStartupError,
179
+ );
180
+
181
+ // What the manager does after a startup failure.
182
+ await session.close("error");
183
+
184
+ // A failed session suppresses the client-facing `metrics` frame; the end
185
+ // row must not be suppressed with it.
186
+ expect(recordLiveVoiceSessionEnded).toHaveBeenCalledWith({
187
+ sessionId: "session-123",
188
+ screen: "ended_error:credentials_unavailable",
189
+ outcome: "failed",
190
+ });
191
+ });
192
+
193
+ test("records exactly one ended row when close is called twice", async () => {
194
+ const session = readySession();
195
+ await session.start();
196
+
197
+ await session.close("client_end");
198
+ await session.close("websocket_close");
199
+
200
+ expect(recordLiveVoiceSessionEnded).toHaveBeenCalledTimes(1);
201
+ });
202
+ });
203
+
204
+ describe("live-voice turn attribution", () => {
205
+ /** Drives one push-to-talk turn and returns the options the bridge saw. */
206
+ async function captureTurnOptions(
207
+ startFrame: LiveVoiceClientStartFrame,
208
+ ): Promise<VoiceTurnOptions> {
209
+ let captured: VoiceTurnOptions | undefined;
210
+ const startVoiceTurn = mock(async (options: VoiceTurnOptions) => {
211
+ captured = options;
212
+ return { turnId: "bridge-turn-1", abort: mock() };
213
+ });
214
+ const session = new LiveVoiceSession(createContext(startFrame), {
215
+ resolveTranscriber: mock(
216
+ async () =>
217
+ new MockStreamingTranscriber([
218
+ { type: "final", text: "hello" },
219
+ { type: "closed" },
220
+ ]),
221
+ ),
222
+ startVoiceTurn,
223
+ emitMetrics: false,
224
+ });
225
+
226
+ await session.start();
227
+ await session.handleClientFrame({ type: "ptt_release" });
228
+ for (let attempt = 0; attempt < 40 && !captured; attempt += 1) {
229
+ await new Promise((resolve) => setTimeout(resolve, 5));
230
+ }
231
+ await session.close("client_end");
232
+
233
+ if (!captured) {
234
+ throw new Error("the live-voice session never started a bridge turn");
235
+ }
236
+ return captured;
237
+ }
238
+
239
+ test("carries the session id and originating client onto the turn", async () => {
240
+ const options = await captureTurnOptions({ ...START_FRAME, client: "ios" });
241
+
242
+ expect(options.voiceTelemetry).toEqual({
243
+ sessionId: "session-123",
244
+ client: "ios",
245
+ });
246
+ });
247
+
248
+ test("leaves the turn's interface id alone while attributing the client", async () => {
249
+ const options = await captureTurnOptions({ ...START_FRAME, client: "ios" });
250
+
251
+ // The load-bearing half of the attribution fix. `userMessageInterface`
252
+ // resolves the turn's channel capabilities, where `macos` is what grants a
253
+ // live-voice turn its dynamic surfaces; reporting the true client here
254
+ // would strip them from every iOS session. Attribution rides
255
+ // `voiceTelemetry` precisely so this can stay put.
256
+ expect(options.userMessageInterface).toBe("macos");
257
+ expect(options.assistantMessageInterface).toBe("macos");
258
+ });
259
+
260
+ test("omits the client when the start frame declares none", async () => {
261
+ const options = await captureTurnOptions(START_FRAME);
262
+
263
+ expect(options.voiceTelemetry).toEqual({ sessionId: "session-123" });
264
+ });
265
+ });
@@ -203,7 +203,12 @@ describe("LiveVoiceSession STT", () => {
203
203
 
204
204
  await session.start();
205
205
 
206
- expect(resolver).toHaveBeenCalledWith({ sampleRate: 24_000 });
206
+ // The config default is "multi", so the session carries a language into
207
+ // the resolver rather than leaving it to be inferred downstream.
208
+ expect(resolver).toHaveBeenCalledWith({
209
+ sampleRate: 24_000,
210
+ language: "multi",
211
+ });
207
212
  expect(transcriber.started).toBe(true);
208
213
  expect(frames).toEqual([
209
214
  {
@@ -103,9 +103,13 @@ class MockStreamingTranscriber implements StreamingTranscriber {
103
103
  readonly received: Buffer[] = [];
104
104
  stopped = false;
105
105
  private onEvent: ((event: SttStreamServerEvent) => void) | null = null;
106
+ private flushed = false;
107
+ // Claimed on the first audio chunk, so the scripted transcript is spent in
108
+ // the order cycles hear speech (see takeScriptedFinal).
109
+ private scriptedFinal: string | null = null;
106
110
 
107
111
  constructor(
108
- private readonly stopEvents: SttStreamServerEvent[],
112
+ private readonly takeScriptedFinal: () => string,
109
113
  private readonly holdStopEvents = false,
110
114
  ) {}
111
115
 
@@ -114,6 +118,7 @@ class MockStreamingTranscriber implements StreamingTranscriber {
114
118
  }
115
119
 
116
120
  sendAudio(chunk: Buffer): void {
121
+ this.scriptedFinal ??= this.takeScriptedFinal();
117
122
  this.received.push(Buffer.from(chunk));
118
123
  }
119
124
 
@@ -128,19 +133,20 @@ class MockStreamingTranscriber implements StreamingTranscriber {
128
133
  }
129
134
 
130
135
  flushStopEvents(): void {
136
+ if (this.flushed) {
137
+ return;
138
+ }
139
+ this.flushed = true;
131
140
  // A stream that was never sent audio has nothing to transcribe, so a real
132
- // provider closes without a final. Scripting one anyway let a cycle that
133
- // armed and tore down in the same breath a client interrupt racing the
134
- // post-cancel re-arm hand the session a transcript it never heard, which
135
- // launched a turn nothing would ever finalize and wedged the
141
+ // provider closes without a final. Scripting one anyway lets a cycle that
142
+ // arms and tears down in the same breath (a client interrupt racing the
143
+ // post-cancel re-arm) hand the session a transcript it never heard, which
144
+ // launches a turn nothing will ever finalize and wedges the
136
145
  // one-turn-at-a-time gate for every later utterance.
137
- const events =
138
- this.received.length > 0
139
- ? this.stopEvents
140
- : this.stopEvents.filter((event) => event.type !== "final");
141
- for (const event of events) {
142
- this.onEvent?.(event);
146
+ if (this.scriptedFinal !== null) {
147
+ this.onEvent?.({ type: "final", text: this.scriptedFinal });
143
148
  }
149
+ this.onEvent?.({ type: "closed" });
144
150
  }
145
151
 
146
152
  // Provider-initiated event (e.g. an idle-timeout close), no stop() needed.
@@ -192,12 +198,23 @@ function createHarness(options: {
192
198
  };
193
199
 
194
200
  const finals = options.finals ?? ["hello world"];
201
+ // Scripted finals are handed out in the order cycles first receive audio,
202
+ // not in the order they arm. Server VAD arms and discards audio-free cycles
203
+ // on wall-clock timers (the trailing-silence timer, the post-cancel re-arm),
204
+ // so how many cycles have armed by a given point depends on how fast the
205
+ // event loop is, while the order the test feeds audio in does not. Keying
206
+ // off audio keeps `finals` reading as "what the user says, in order".
207
+ let scriptedFinalIndex = 0;
208
+ const takeScriptedFinal = (): string => {
209
+ const text =
210
+ finals[scriptedFinalIndex] ?? `utterance ${scriptedFinalIndex + 1}`;
211
+ scriptedFinalIndex += 1;
212
+ return text;
213
+ };
195
214
  const transcribers: MockStreamingTranscriber[] = [];
196
215
  const resolveTranscriber = mock(async () => {
197
- const text =
198
- finals[transcribers.length] ?? `utterance ${transcribers.length + 1}`;
199
216
  const transcriber = new MockStreamingTranscriber(
200
- [{ type: "final", text }, { type: "closed" }],
217
+ takeScriptedFinal,
201
218
  options.holdStopEventsFor?.includes(transcribers.length) ?? false,
202
219
  );
203
220
  transcribers.push(transcriber);
@@ -2929,8 +2946,11 @@ describe("LiveVoiceSession server VAD", () => {
2929
2946
 
2930
2947
  test("an idle transcriber close before speech re-arms capture for the next utterance", async () => {
2931
2948
  const { startVoiceTurn, calls } = makeAutoCompletingTurnStarter(["Hi."]);
2949
+ // The first transcriber closes before it is sent any audio, so it never
2950
+ // claims a scripted final: the one entry belongs to the cycle that speech
2951
+ // actually reaches.
2932
2952
  const { frames, session, transcribers } = createHarness({
2933
- finals: ["never spoken", "hello after close"],
2953
+ finals: ["hello after close"],
2934
2954
  startVoiceTurn,
2935
2955
  });
2936
2956
 
@@ -305,6 +305,58 @@ describe("parseLiveVoiceClientTextFrame", () => {
305
305
  expect("turnDetection" in result.frame).toBe(false);
306
306
  });
307
307
 
308
+ test("parses the originating client from the start frame", () => {
309
+ const result = parseLiveVoiceClientTextFrame(
310
+ JSON.stringify({
311
+ type: "start",
312
+ client: "ios",
313
+ audio: { mimeType: "audio/pcm", sampleRate: 24000, channels: 1 },
314
+ }),
315
+ );
316
+
317
+ expect(result.ok).toBe(true);
318
+ if (!result.ok) {
319
+ return;
320
+ }
321
+
322
+ expect(result.frame).toMatchObject({ type: "start", client: "ios" });
323
+ });
324
+
325
+ test("omits client when absent from the start frame", () => {
326
+ const result = parseLiveVoiceClientTextFrame(
327
+ JSON.stringify({
328
+ type: "start",
329
+ audio: { mimeType: "audio/pcm", sampleRate: 24000, channels: 1 },
330
+ }),
331
+ );
332
+
333
+ expect(result.ok).toBe(true);
334
+ if (!result.ok) {
335
+ return;
336
+ }
337
+
338
+ expect("client" in result.frame).toBe(false);
339
+ });
340
+
341
+ test("drops an unrecognized client rather than rejecting the session", () => {
342
+ const result = parseLiveVoiceClientTextFrame(
343
+ JSON.stringify({
344
+ type: "start",
345
+ client: "commodore-64",
346
+ audio: { mimeType: "audio/pcm", sampleRate: 24000, channels: 1 },
347
+ }),
348
+ );
349
+
350
+ // The field is an analytics dimension. Failing startup over it would
351
+ // trade a gap in a chart for a user who cannot talk to their assistant.
352
+ expect(result.ok).toBe(true);
353
+ if (!result.ok) {
354
+ return;
355
+ }
356
+
357
+ expect("client" in result.frame).toBe(false);
358
+ });
359
+
308
360
  test("returns typed protocol errors for invalid turnDetection values", () => {
309
361
  const result = validateLiveVoiceClientFrame({
310
362
  type: "start",
@@ -327,9 +327,12 @@ describe("RuntimeHttpServer live voice WebSocket shell", () => {
327
327
  conversationId: "conversation-ready",
328
328
  });
329
329
  expect(typeof ready.sessionId).toBe("string");
330
+ // The config default is "multi", so the session carries a language into
331
+ // the resolver rather than leaving it to be inferred downstream.
330
332
  expect(resolveStreamingTranscriberMock).toHaveBeenCalledWith({
331
333
  sampleRate: 24_000,
332
334
  providerId: "deepgram",
335
+ language: "multi",
333
336
  });
334
337
  expect(resolvedTranscribers).toHaveLength(1);
335
338
  expect(resolvedTranscribers[0]?.started).toBe(true);
@@ -468,9 +468,9 @@ function artifactFromAttachment(input: {
468
468
  };
469
469
  }
470
470
 
471
- export function archiveLiveVoiceAudioArtifact(
471
+ export async function archiveLiveVoiceAudioArtifact(
472
472
  input: ArchiveLiveVoiceAudioInput,
473
- ): LiveVoiceAudioArchiveResult {
473
+ ): Promise<LiveVoiceAudioArchiveResult> {
474
474
  const validated = validateInput(input);
475
475
  if ("type" in validated) {
476
476
  return validated;
@@ -529,7 +529,7 @@ export function archiveLiveVoiceAudioArtifact(
529
529
  const position = input.position ?? nextAttachmentPosition(input.messageId);
530
530
  const stored =
531
531
  input.audio.type === "base64"
532
- ? attachInlineAttachmentToMessage(
532
+ ? await attachInlineAttachmentToMessage(
533
533
  input.messageId,
534
534
  position,
535
535
  validated.filename,
@@ -614,15 +614,15 @@ interface LinkLiveVoiceAudioArtifactInput {
614
614
  position?: number;
615
615
  }
616
616
 
617
- export function archiveLiveVoiceUserUtteranceAudio(
617
+ export async function archiveLiveVoiceUserUtteranceAudio(
618
618
  input: ArchiveLiveVoiceRolelessAudioInput,
619
- ): LiveVoiceAudioArchiveResult {
619
+ ): Promise<LiveVoiceAudioArchiveResult> {
620
620
  return archiveLiveVoiceAudioArtifact({ ...input, role: "user" });
621
621
  }
622
622
 
623
- export function archiveLiveVoiceAssistantResponseAudio(
623
+ export async function archiveLiveVoiceAssistantResponseAudio(
624
624
  input: ArchiveLiveVoiceRolelessAudioInput,
625
- ): LiveVoiceAudioArchiveResult {
625
+ ): Promise<LiveVoiceAudioArchiveResult> {
626
626
  return archiveLiveVoiceAudioArtifact({ ...input, role: "assistant" });
627
627
  }
628
628
 
@@ -630,11 +630,11 @@ function normalizeMessageId(messageId: string | null | undefined): string {
630
630
  return messageId?.trim() ?? "";
631
631
  }
632
632
 
633
- function linkLiveVoiceAudioToMessage(
633
+ async function linkLiveVoiceAudioToMessage(
634
634
  input: LinkLiveVoiceRolelessAudioInput & {
635
635
  role: LiveVoiceAudioArchiveRole;
636
636
  },
637
- ): LiveVoiceAudioArchiveResult {
637
+ ): Promise<LiveVoiceAudioArchiveResult> {
638
638
  const messageId = normalizeMessageId(input.messageId);
639
639
  if (!messageId) {
640
640
  return resultUnlinked(
@@ -651,15 +651,15 @@ function linkLiveVoiceAudioToMessage(
651
651
  });
652
652
  }
653
653
 
654
- export function linkLiveVoiceUserUtteranceAudioToMessage(
654
+ export async function linkLiveVoiceUserUtteranceAudioToMessage(
655
655
  input: LinkLiveVoiceRolelessAudioInput,
656
- ): LiveVoiceAudioArchiveResult {
656
+ ): Promise<LiveVoiceAudioArchiveResult> {
657
657
  return linkLiveVoiceAudioToMessage({ ...input, role: "user" });
658
658
  }
659
659
 
660
- export function linkLiveVoiceAssistantResponseAudioToMessage(
660
+ export async function linkLiveVoiceAssistantResponseAudioToMessage(
661
661
  input: LinkLiveVoiceRolelessAudioInput,
662
- ): LiveVoiceAudioArchiveResult {
662
+ ): Promise<LiveVoiceAudioArchiveResult> {
663
663
  return linkLiveVoiceAudioToMessage({ ...input, role: "assistant" });
664
664
  }
665
665
 
@@ -207,13 +207,19 @@ class LiveVoiceConnectionImpl implements LiveVoiceConnection {
207
207
  }
208
208
 
209
209
  private sendError(
210
- error: Pick<LiveVoiceProtocolError, "code" | "message">,
210
+ error: Pick<LiveVoiceProtocolError, "code" | "message" | "frameType">,
211
211
  ): void {
212
212
  this.sendFrame({
213
213
  type: "error",
214
214
  seq: this.lastSeq + 1,
215
215
  code: error.code,
216
216
  message: error.message,
217
+ // Which frame was rejected, when the parser knows. Without it an
218
+ // `unknown_type` is unattributable: a client that sends more than one
219
+ // optional frame cannot tell which of them an older daemon refused, and
220
+ // has to guess. Guessing wrong is silent, which for a rejected photo
221
+ // means the user is told nothing at all.
222
+ ...(error.frameType ? { frameType: error.frameType } : {}),
217
223
  });
218
224
  }
219
225