@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
@@ -88,8 +88,8 @@ describe("attachmentsToReferenceBlocks", () => {
88
88
  // ---------------------------------------------------------------------------
89
89
 
90
90
  describe("attachmentsToContentBlocks", () => {
91
- test("creates image content block for image/jpeg", () => {
92
- const blocks = attachmentsToContentBlocks([
91
+ test("creates image content block for image/jpeg", async () => {
92
+ const blocks = await attachmentsToContentBlocks([
93
93
  { filename: "photo.jpg", mimeType: "image/jpeg", data: "base64data" },
94
94
  ]);
95
95
 
@@ -104,8 +104,8 @@ describe("attachmentsToContentBlocks", () => {
104
104
  expect(block.source.data).toBe("base64data");
105
105
  });
106
106
 
107
- test("creates image content block for image/png", () => {
108
- const blocks = attachmentsToContentBlocks([
107
+ test("creates image content block for image/png", async () => {
108
+ const blocks = await attachmentsToContentBlocks([
109
109
  { filename: "screenshot.png", mimeType: "image/png", data: "pngdata" },
110
110
  ]);
111
111
 
@@ -113,8 +113,8 @@ describe("attachmentsToContentBlocks", () => {
113
113
  expect(blocks[0].type).toBe("image");
114
114
  });
115
115
 
116
- test("creates image content block for image/webp", () => {
117
- const blocks = attachmentsToContentBlocks([
116
+ test("creates image content block for image/webp", async () => {
117
+ const blocks = await attachmentsToContentBlocks([
118
118
  { filename: "sticker.webp", mimeType: "image/webp", data: "webpdata" },
119
119
  ]);
120
120
 
@@ -122,8 +122,8 @@ describe("attachmentsToContentBlocks", () => {
122
122
  expect(blocks[0].type).toBe("image");
123
123
  });
124
124
 
125
- test("creates file content block for non-image mime types", () => {
126
- const blocks = attachmentsToContentBlocks([
125
+ test("creates file content block for non-image mime types", async () => {
126
+ const blocks = await attachmentsToContentBlocks([
127
127
  { filename: "doc.pdf", mimeType: "application/pdf", data: "pdfdata" },
128
128
  ]);
129
129
 
@@ -137,8 +137,8 @@ describe("attachmentsToContentBlocks", () => {
137
137
  expect(block.source.media_type).toBe("application/pdf");
138
138
  });
139
139
 
140
- test("handles multiple attachments including mixed types", () => {
141
- const blocks = attachmentsToContentBlocks([
140
+ test("handles multiple attachments including mixed types", async () => {
141
+ const blocks = await attachmentsToContentBlocks([
142
142
  { filename: "photo.jpg", mimeType: "image/jpeg", data: "imgdata" },
143
143
  { filename: "notes.txt", mimeType: "text/plain", data: "txtdata" },
144
144
  ]);
@@ -148,8 +148,8 @@ describe("attachmentsToContentBlocks", () => {
148
148
  expect(blocks[1].type).toBe("file");
149
149
  });
150
150
 
151
- test("returns empty array for no attachments", () => {
152
- const blocks = attachmentsToContentBlocks([]);
151
+ test("returns empty array for no attachments", async () => {
152
+ const blocks = await attachmentsToContentBlocks([]);
153
153
  expect(blocks).toHaveLength(0);
154
154
  });
155
155
  });
@@ -159,8 +159,8 @@ describe("attachmentsToContentBlocks", () => {
159
159
  // ---------------------------------------------------------------------------
160
160
 
161
161
  describe("createUserMessage with image attachments", () => {
162
- test("includes both text and image blocks", () => {
163
- const msg = createUserMessage("what is this?", [
162
+ test("includes both text and image blocks", async () => {
163
+ const msg = await createUserMessage("what is this?", [
164
164
  { filename: "photo.jpg", mimeType: "image/jpeg", data: "base64img" },
165
165
  ]);
166
166
 
@@ -173,8 +173,8 @@ describe("createUserMessage with image attachments", () => {
173
173
  expect(msg.content[1].type).toBe("image");
174
174
  });
175
175
 
176
- test("includes only image block when text is empty", () => {
177
- const msg = createUserMessage("", [
176
+ test("includes only image block when text is empty", async () => {
177
+ const msg = await createUserMessage("", [
178
178
  { filename: "photo.jpg", mimeType: "image/jpeg", data: "base64img" },
179
179
  ]);
180
180
 
@@ -183,8 +183,8 @@ describe("createUserMessage with image attachments", () => {
183
183
  expect(msg.content[0].type).toBe("image");
184
184
  });
185
185
 
186
- test("includes only image block when text is whitespace", () => {
187
- const msg = createUserMessage(" ", [
186
+ test("includes only image block when text is whitespace", async () => {
187
+ const msg = await createUserMessage(" ", [
188
188
  { filename: "photo.jpg", mimeType: "image/jpeg", data: "base64img" },
189
189
  ]);
190
190
 
@@ -193,8 +193,8 @@ describe("createUserMessage with image attachments", () => {
193
193
  expect(msg.content[0].type).toBe("image");
194
194
  });
195
195
 
196
- test("includes multiple image blocks", () => {
197
- const msg = createUserMessage("compare these", [
196
+ test("includes multiple image blocks", async () => {
197
+ const msg = await createUserMessage("compare these", [
198
198
  { filename: "a.jpg", mimeType: "image/jpeg", data: "img1" },
199
199
  { filename: "b.png", mimeType: "image/png", data: "img2" },
200
200
  ]);
@@ -206,9 +206,9 @@ describe("createUserMessage with image attachments", () => {
206
206
  expect(msg.content[2].type).toBe("image");
207
207
  });
208
208
 
209
- test("preserves base64 data in image content block", () => {
209
+ test("preserves base64 data in image content block", async () => {
210
210
  const base64 = "dGVzdC1pbWFnZS1kYXRh";
211
- const msg = createUserMessage("test", [
211
+ const msg = await createUserMessage("test", [
212
212
  { filename: "photo.jpg", mimeType: "image/jpeg", data: base64 },
213
213
  ]);
214
214
 
@@ -227,7 +227,7 @@ describe("createUserMessage with image attachments", () => {
227
227
  // ---------------------------------------------------------------------------
228
228
 
229
229
  describe("enrichMessageWithSourcePaths", () => {
230
- test("appends a source path annotation for images with filePath", () => {
230
+ test("appends a source path annotation for images with filePath", async () => {
231
231
  const attachments = [
232
232
  {
233
233
  filename: "photo.jpg",
@@ -236,7 +236,7 @@ describe("enrichMessageWithSourcePaths", () => {
236
236
  filePath: "/Users/me/Desktop/photo.jpg",
237
237
  },
238
238
  ];
239
- const original = createUserMessage("what is this?", attachments);
239
+ const original = await createUserMessage("what is this?", attachments);
240
240
  const enriched = enrichMessageWithSourcePaths(original, attachments);
241
241
 
242
242
  expect(enriched).not.toBe(original);
@@ -249,7 +249,7 @@ describe("enrichMessageWithSourcePaths", () => {
249
249
  );
250
250
  });
251
251
 
252
- test("returns the original message (same reference) when no images have filePath", () => {
252
+ test("returns the original message (same reference) when no images have filePath", async () => {
253
253
  const attachments = [
254
254
  {
255
255
  filename: "photo.jpg",
@@ -257,13 +257,13 @@ describe("enrichMessageWithSourcePaths", () => {
257
257
  data: "base64img",
258
258
  },
259
259
  ];
260
- const original = createUserMessage("what is this?", attachments);
260
+ const original = await createUserMessage("what is this?", attachments);
261
261
  const result = enrichMessageWithSourcePaths(original, attachments);
262
262
 
263
263
  expect(result).toBe(original);
264
264
  });
265
265
 
266
- test("skips non-image attachments with filePath", () => {
266
+ test("skips non-image attachments with filePath", async () => {
267
267
  const attachments = [
268
268
  {
269
269
  filename: "doc.pdf",
@@ -272,14 +272,14 @@ describe("enrichMessageWithSourcePaths", () => {
272
272
  filePath: "/Users/me/Documents/doc.pdf",
273
273
  },
274
274
  ];
275
- const original = createUserMessage("review this", attachments);
275
+ const original = await createUserMessage("review this", attachments);
276
276
  const result = enrichMessageWithSourcePaths(original, attachments);
277
277
 
278
278
  // Non-image attachments are not annotated, so we get back the same ref
279
279
  expect(result).toBe(original);
280
280
  });
281
281
 
282
- test("handles multiple images with file paths", () => {
282
+ test("handles multiple images with file paths", async () => {
283
283
  const attachments = [
284
284
  {
285
285
  filename: "a.jpg",
@@ -294,7 +294,7 @@ describe("enrichMessageWithSourcePaths", () => {
294
294
  filePath: "/path/to/b.png",
295
295
  },
296
296
  ];
297
- const original = createUserMessage("compare", attachments);
297
+ const original = await createUserMessage("compare", attachments);
298
298
  const enriched = enrichMessageWithSourcePaths(original, attachments);
299
299
 
300
300
  expect(enriched).not.toBe(original);
@@ -307,7 +307,7 @@ describe("enrichMessageWithSourcePaths", () => {
307
307
  );
308
308
  });
309
309
 
310
- test("only annotates images that have filePath, skips those without", () => {
310
+ test("only annotates images that have filePath, skips those without", async () => {
311
311
  const attachments = [
312
312
  {
313
313
  filename: "a.jpg",
@@ -322,7 +322,7 @@ describe("enrichMessageWithSourcePaths", () => {
322
322
  // no filePath — e.g. pasted screenshot
323
323
  },
324
324
  ];
325
- const original = createUserMessage("compare", attachments);
325
+ const original = await createUserMessage("compare", attachments);
326
326
  const enriched = enrichMessageWithSourcePaths(original, attachments);
327
327
 
328
328
  expect(enriched).not.toBe(original);
@@ -332,7 +332,7 @@ describe("enrichMessageWithSourcePaths", () => {
332
332
  expect(annotation.text).toBe("[Attached image source: /path/to/a.jpg]");
333
333
  });
334
334
 
335
- test("annotates non-image attachments that have storedPath", () => {
335
+ test("annotates non-image attachments that have storedPath", async () => {
336
336
  const attachments = [
337
337
  {
338
338
  filename: "report.pdf",
@@ -342,7 +342,7 @@ describe("enrichMessageWithSourcePaths", () => {
342
342
  storedPath: "/conv/attachments/report-2.pdf",
343
343
  },
344
344
  ];
345
- const original = createUserMessage("review my edits", attachments);
345
+ const original = await createUserMessage("review my edits", attachments);
346
346
  const enriched = enrichMessageWithSourcePaths(original, attachments);
347
347
 
348
348
  expect(enriched).not.toBe(original);
@@ -355,7 +355,7 @@ describe("enrichMessageWithSourcePaths", () => {
355
355
  );
356
356
  });
357
357
 
358
- test("emits image source lines before stored path lines in one block", () => {
358
+ test("emits image source lines before stored path lines in one block", async () => {
359
359
  const attachments = [
360
360
  {
361
361
  filename: "data.csv",
@@ -371,7 +371,7 @@ describe("enrichMessageWithSourcePaths", () => {
371
371
  storedPath: "/conv/attachments/photo.jpg",
372
372
  },
373
373
  ];
374
- const original = createUserMessage("compare", attachments);
374
+ const original = await createUserMessage("compare", attachments);
375
375
  const enriched = enrichMessageWithSourcePaths(original, attachments);
376
376
 
377
377
  const annotation = enriched.content.at(-1) as {
@@ -61,7 +61,7 @@ mock.module("../persistence/conversation-crud.js", () => ({
61
61
  getConversationPersistedSeq: () => null,
62
62
  addMessage: mock(() => ({ id: "msg-1" })),
63
63
  archiveConversation: mock(() => true),
64
- batchSetDisplayOrders: mock(() => {}),
64
+ batchSetConversationPlacement: mock(() => {}),
65
65
  createConversation: (opts: { conversationType: string }) => {
66
66
  createdConversations.push(opts);
67
67
  return { id: "conv-1", ...opts };
@@ -40,10 +40,24 @@ const realGatewaySessionsModule = {
40
40
  };
41
41
  mock.module("../channels/gateway-verification-sessions.js", () => ({
42
42
  ...realGatewaySessionsModule,
43
- findActiveSession: async (channel: string) =>
44
- findActiveSessionMockActive
45
- ? activeVoiceSession
46
- : realGatewaySessionsModule.findActiveSession(channel),
43
+ // Honours the caller's filter: the guard asks for the number it is about
44
+ // to dial, so a session for a different number must read as absent.
45
+ findActiveSession: async (
46
+ channel: string,
47
+ filter?: { expectedExternalUserId?: string },
48
+ ) => {
49
+ if (!findActiveSessionMockActive) {
50
+ return realGatewaySessionsModule.findActiveSession(channel, filter);
51
+ }
52
+ const wanted = filter?.expectedExternalUserId;
53
+ if (
54
+ wanted !== undefined &&
55
+ activeVoiceSession?.expectedPhoneE164 !== wanted
56
+ ) {
57
+ return null;
58
+ }
59
+ return activeVoiceSession;
60
+ },
47
61
  }));
48
62
 
49
63
  const { executeCallStart } = await import("../tools/calls/call-start.js");
@@ -0,0 +1,86 @@
1
+ /**
2
+ * The canned-reply routes (canned first greeting, unknown slash command) hold
3
+ * the processing lock themselves and release it from a deferred callback that
4
+ * also publishes their client event burst. That release is the only one those
5
+ * turns get: no agent loop runs, so a throw escaping the burst would latch the
6
+ * conversation "processing" for the life of the daemon and queue every later
7
+ * send behind an idle conversation.
8
+ */
9
+ import { describe, expect, test } from "bun:test";
10
+
11
+ import type { QueueDrainReason } from "../daemon/conversation-queue-manager.js";
12
+ import { scheduleCannedReplyRelease } from "../runtime/routes/canned-reply-release.js";
13
+
14
+ function makeConversation() {
15
+ let processing = true;
16
+ const drainOrigins: string[] = [];
17
+ return {
18
+ isProcessing: () => processing,
19
+ drainOrigins,
20
+ target: {
21
+ setProcessing: (value: boolean) => {
22
+ processing = value;
23
+ },
24
+ kickDrainQueue: (_reason?: QueueDrainReason, origin?: string) => {
25
+ drainOrigins.push(origin ?? "");
26
+ return Promise.resolve();
27
+ },
28
+ },
29
+ };
30
+ }
31
+
32
+ /** Let the scheduled `setTimeout(..., 0)` callback run. */
33
+ function nextTick(): Promise<void> {
34
+ return new Promise((resolve) => setTimeout(resolve, 0));
35
+ }
36
+
37
+ describe("scheduleCannedReplyRelease", () => {
38
+ test("releases and drains after the deferred event burst", async () => {
39
+ const conversation = makeConversation();
40
+ const order: string[] = [];
41
+
42
+ scheduleCannedReplyRelease({
43
+ conversation: {
44
+ setProcessing: (value: boolean) => {
45
+ order.push("release");
46
+ conversation.target.setProcessing(value);
47
+ },
48
+ kickDrainQueue: conversation.target.kickDrainQueue,
49
+ },
50
+ origin: "canned_greeting",
51
+ emit: () => {
52
+ order.push("emit");
53
+ },
54
+ afterRelease: () => {
55
+ order.push("after");
56
+ },
57
+ });
58
+
59
+ // Nothing runs synchronously: the HTTP response has to reach the client
60
+ // before its events do.
61
+ expect(order).toEqual([]);
62
+
63
+ await nextTick();
64
+
65
+ expect(order).toEqual(["emit", "release", "after"]);
66
+ expect(conversation.isProcessing()).toBe(false);
67
+ expect(conversation.drainOrigins).toEqual(["canned_greeting"]);
68
+ });
69
+
70
+ test("still releases and drains when a broadcast throws", async () => {
71
+ const conversation = makeConversation();
72
+
73
+ scheduleCannedReplyRelease({
74
+ conversation: conversation.target,
75
+ origin: "slash_command",
76
+ emit: () => {
77
+ throw new Error("broadcast exploded");
78
+ },
79
+ });
80
+
81
+ await nextTick();
82
+
83
+ expect(conversation.isProcessing()).toBe(false);
84
+ expect(conversation.drainOrigins).toEqual(["slash_command"]);
85
+ });
86
+ });
@@ -137,7 +137,7 @@ import {
137
137
  startOutbound,
138
138
  } from "../runtime/verification-outbound-actions.js";
139
139
  import {
140
- composeVerificationTelegram,
140
+ composeVerificationText,
141
141
  GUARDIAN_VERIFY_TEMPLATE_KEYS,
142
142
  } from "../runtime/verification-templates.js";
143
143
  import { resetDbForTesting } from "./db-test-helpers.js";
@@ -1640,32 +1640,42 @@ describe("outbound verification sessions", () => {
1640
1640
 
1641
1641
  // ── Auto-revoke of prior sessions ──
1642
1642
 
1643
- test("creating a new outbound session auto-revokes prior pending/awaiting sessions", async () => {
1643
+ test("a second code for the same number revokes the first", async () => {
1644
1644
  const { sessionId: firstId } = await createOutboundSession({
1645
1645
  channel: "phone",
1646
- expectedPhoneE164: "+15551234567",
1647
- destinationAddress: "+15551234567",
1646
+ expectedPhoneE164: "+15555550101",
1647
+ destinationAddress: "+15555550101",
1648
+ });
1649
+ const { sessionId: secondId } = await createOutboundSession({
1650
+ channel: "phone",
1651
+ expectedPhoneE164: "+15555550101",
1652
+ destinationAddress: "+15555550101",
1648
1653
  });
1649
1654
 
1650
- const first = await serviceFindActiveSession("phone");
1651
- expect(first).not.toBeNull();
1652
- expect(first!.id).toBe(firstId);
1655
+ // Read by id rather than through the unfiltered lookup: two live
1656
+ // sessions minted in the same millisecond have no defined order, and
1657
+ // what matters here is each row's status, not which one is "latest".
1658
+ expect(getSessionById(firstId)!.status).toBe("revoked");
1659
+ expect(getSessionById(secondId)!.status).toBe("awaiting_response");
1660
+ });
1653
1661
 
1654
- // Create a second session first should be revoked
1662
+ test("a code for a different number leaves the first alone", async () => {
1663
+ // Two numbers are two people. Verifying one must not silently kill the
1664
+ // other's code, which they would learn about only by trying to use it
1665
+ // and being told it is "invalid or has expired".
1666
+ const { sessionId: firstId } = await createOutboundSession({
1667
+ channel: "phone",
1668
+ expectedPhoneE164: "+15555550101",
1669
+ destinationAddress: "+15555550101",
1670
+ });
1655
1671
  const { sessionId: secondId } = await createOutboundSession({
1656
1672
  channel: "phone",
1657
- expectedPhoneE164: "+15559876543",
1658
- destinationAddress: "+15559876543",
1673
+ expectedPhoneE164: "+15555550102",
1674
+ destinationAddress: "+15555550102",
1659
1675
  });
1660
1676
 
1661
- const second = await serviceFindActiveSession("phone");
1662
- expect(second).not.toBeNull();
1663
- expect(second!.id).toBe(secondId);
1664
-
1665
- // First session should no longer be findable as active
1666
- const firstRow = getSessionById(firstId);
1667
- expect(firstRow).not.toBeNull();
1668
- expect(firstRow!.status).toBe("revoked");
1677
+ expect(getSessionById(firstId)!.status).toBe("awaiting_response");
1678
+ expect(getSessionById(secondId)!.status).toBe("awaiting_response");
1669
1679
  });
1670
1680
 
1671
1681
  // ── findActiveSession returns correct session ──
@@ -1772,26 +1782,46 @@ describe("outbound verification sessions", () => {
1772
1782
  expect(result.success).toBe(true);
1773
1783
  });
1774
1784
 
1775
- // ── Voice identity match via expectedExternalUserId ──
1785
+ // ── Voice identity is the number ──
1776
1786
 
1777
- test("Voice identity match succeeds via expectedExternalUserId", async () => {
1787
+ test("a voice session is redeemed by the number it was sent to", async () => {
1788
+ // Voice knows the caller by one thing, so the consume path receives that
1789
+ // number as both actor axes. Every voice mint records it in both
1790
+ // identity columns, and either way the session is bound to the number.
1778
1791
  const { secret } = await createOutboundSession({
1779
1792
  channel: "phone",
1780
- expectedExternalUserId: "voice-user-42",
1781
- expectedPhoneE164: "+15551234567",
1782
- destinationAddress: "+15551234567",
1793
+ expectedExternalUserId: "+15555550101",
1794
+ expectedPhoneE164: "+15555550101",
1795
+ destinationAddress: "+15555550101",
1783
1796
  });
1784
1797
 
1785
- // Actor matches expectedExternalUserId
1786
1798
  const result = await validateAndConsumeVerification(
1787
1799
  "phone",
1788
1800
  secret,
1789
- "voice-user-42",
1790
- "voice-chat-1",
1801
+ "+15555550101",
1802
+ "+15555550101",
1791
1803
  );
1792
1804
 
1793
1805
  expect(result.success).toBe(true);
1794
1806
  });
1807
+
1808
+ test("a voice session refuses a caller on a different number", async () => {
1809
+ const { secret } = await createOutboundSession({
1810
+ channel: "phone",
1811
+ expectedExternalUserId: "+15555550101",
1812
+ expectedPhoneE164: "+15555550101",
1813
+ destinationAddress: "+15555550101",
1814
+ });
1815
+
1816
+ const result = await validateAndConsumeVerification(
1817
+ "phone",
1818
+ secret,
1819
+ "+15555550102",
1820
+ "+15555550102",
1821
+ );
1822
+
1823
+ expect(result.success).toBe(false);
1824
+ });
1795
1825
  });
1796
1826
 
1797
1827
  // ═══════════════════════════════════════════════════════════════════════════
@@ -2344,7 +2374,7 @@ describe("outbound Telegram verification", () => {
2344
2374
  });
2345
2375
 
2346
2376
  test("telegram template does not include verification code in message", async () => {
2347
- const msg = composeVerificationTelegram(
2377
+ const msg = composeVerificationText(
2348
2378
  GUARDIAN_VERIFY_TEMPLATE_KEYS.TELEGRAM_CHALLENGE_REQUEST,
2349
2379
  { code: "abc123", expiresInMinutes: 10 },
2350
2380
  );
@@ -2353,7 +2383,7 @@ describe("outbound Telegram verification", () => {
2353
2383
  });
2354
2384
 
2355
2385
  test("telegram resend template does not include code and includes (resent) suffix", async () => {
2356
- const msg = composeVerificationTelegram(
2386
+ const msg = composeVerificationText(
2357
2387
  GUARDIAN_VERIFY_TEMPLATE_KEYS.TELEGRAM_RESEND,
2358
2388
  { code: "xyz789", expiresInMinutes: 5 },
2359
2389
  );
@@ -2363,7 +2393,7 @@ describe("outbound Telegram verification", () => {
2363
2393
  });
2364
2394
 
2365
2395
  test("telegram template includes Vellum assistant prefix", async () => {
2366
- const msg = composeVerificationTelegram(
2396
+ const msg = composeVerificationText(
2367
2397
  GUARDIAN_VERIFY_TEMPLATE_KEYS.TELEGRAM_CHALLENGE_REQUEST,
2368
2398
  { code: "999999", expiresInMinutes: 10, assistantName: "MyBot" },
2369
2399
  );
@@ -0,0 +1,154 @@
1
+ /**
2
+ * The Telegram probe's remote check.
3
+ *
4
+ * Local checks establish that credentials exist. That is a different claim
5
+ * from "messages arrive": a registration that never landed leaves every
6
+ * credential in place and the channel dark, which is how the readiness
7
+ * indicator went green on a channel nobody could reach.
8
+ *
9
+ * The classification lives in `checkTelegramWebhookHealth`, which already
10
+ * handles error recency and the managed-versus-self-hosted recovery text.
11
+ * These tests pin how the probe maps its verdicts across three outcomes, not
12
+ * two: verified pass, hard failure, and the no-evidence statuses that are
13
+ * neither. A probe that cannot see the answer must not report a fault, and
14
+ * must not be counted as proof either.
15
+ */
16
+ import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
17
+
18
+ import type { TelegramWebhookHealthResult } from "../telegram/webhook-health.js";
19
+
20
+ let health: TelegramWebhookHealthResult;
21
+
22
+ mock.module("../telegram/webhook-health.js", () => ({
23
+ checkTelegramWebhookHealth: async () => health,
24
+ }));
25
+
26
+ mock.module("../calls/twilio-rest.js", () => ({
27
+ hasTwilioCredentials: () => false,
28
+ }));
29
+ mock.module("../calls/twilio-config.js", () => ({
30
+ resolveTwilioPhoneNumber: () => undefined,
31
+ }));
32
+ mock.module("../runtime/channel-invite-transports/whatsapp.js", () => ({
33
+ resolveWhatsAppDisplayNumber: () => undefined,
34
+ }));
35
+
36
+ async function runTelegramRemoteProbe() {
37
+ const { createReadinessService } =
38
+ await import("../runtime/channel-readiness-service.js");
39
+ const service = createReadinessService();
40
+ return service.getReadiness("telegram", true);
41
+ }
42
+
43
+ async function deliveryCheck() {
44
+ const [snapshot] = await runTelegramRemoteProbe();
45
+ const remote = snapshot.remoteChecks ?? [];
46
+ const result = remote.find((c) => c.name === "webhook_delivery");
47
+ if (!result) {
48
+ throw new Error(
49
+ `no webhook_delivery check ran; got: ${remote.map((c) => c.name).join(", ") || "(none)"}`,
50
+ );
51
+ }
52
+ return result;
53
+ }
54
+
55
+ beforeEach(() => {
56
+ health = { status: "healthy", detail: "ok" };
57
+ });
58
+
59
+ afterEach(() => {
60
+ mock.restore();
61
+ });
62
+
63
+ describe("telegram remote probe (webhook delivery)", () => {
64
+ test("passes when Telegram reports a healthy webhook", async () => {
65
+ health = { status: "healthy", detail: "delivering" };
66
+
67
+ expect((await deliveryCheck()).passed).toBe(true);
68
+ });
69
+
70
+ test("fails when no webhook is registered, carrying the recovery text", async () => {
71
+ health = {
72
+ status: "not_registered",
73
+ detail: "Telegram has no webhook registered. Contact support.",
74
+ };
75
+
76
+ const result = await deliveryCheck();
77
+ expect(result.passed).toBe(false);
78
+ // The detail carries fixPath()'s managed-versus-self-hosted guidance, so
79
+ // the probe relays it rather than substituting wording of its own.
80
+ expect(result.message).toBe(health.detail);
81
+ });
82
+
83
+ test("fails when Telegram reports a recent delivery error", async () => {
84
+ health = {
85
+ status: "delivery_failing",
86
+ detail: 'Telegram reported "Connection refused"',
87
+ };
88
+
89
+ const result = await deliveryCheck();
90
+ expect(result.passed).toBe(false);
91
+ expect(result.message).toBe(health.detail);
92
+ });
93
+
94
+ test("fails when Telegram holds a webhook this deployment did not register", async () => {
95
+ // The case last_error_date cannot see: on a quiet channel a stale tunnel
96
+ // address or another deployment's callback never produces a delivery
97
+ // error, so before the URL comparison this read as healthy.
98
+ health = {
99
+ status: "url_mismatch",
100
+ detail: "Telegram is registered at a different address",
101
+ registeredUrl: "https://stale.example/webhooks/telegram",
102
+ expectedUrl: "https://current.example/webhooks/telegram",
103
+ };
104
+
105
+ const result = await deliveryCheck();
106
+ expect(result.passed).toBe(false);
107
+ expect(result.message).toBe(health.detail);
108
+ });
109
+
110
+ // The three indeterminate statuses. Each must satisfy BOTH halves: no fault
111
+ // reported (so a network blip does not paint a working channel broken), and
112
+ // not counted as evidence (so nothing can claim delivery from a check that
113
+ // never established it). Asserting only `passed` would let a regression that
114
+ // drops `indeterminate` through, which is exactly the false-success this
115
+ // contract exists to prevent.
116
+ const indeterminateCases = [
117
+ ["skipped", "no bot token stored"],
118
+ ["unknown", "could not reach Telegram"],
119
+ ["unverified", "no record of registering this webhook"],
120
+ ] as const;
121
+
122
+ for (const [status, detail] of indeterminateCases) {
123
+ test(`${status} is indeterminate: no fault reported, but not proof of delivery`, async () => {
124
+ health = { status, detail };
125
+
126
+ const result = await deliveryCheck();
127
+ expect(result.passed).toBe(true);
128
+ expect(result.indeterminate).toBe(true);
129
+ });
130
+ }
131
+
132
+ test("an untouched install stays not_configured rather than incomplete", async () => {
133
+ // setupStatus separates not_configured from incomplete by asking whether
134
+ // any check passed. Counting an indeterminate check there reported a
135
+ // workspace with no Telegram credentials at all as half-finished, which is
136
+ // what routes the Channels UI to the "finish setup" prompt instead of the
137
+ // normal setup flow.
138
+ health = { status: "skipped", detail: "no bot token stored" };
139
+
140
+ const [snapshot] = await runTelegramRemoteProbe();
141
+ expect(snapshot.setupStatus).toBe("not_configured");
142
+ expect(snapshot.ready).toBe(false);
143
+ });
144
+
145
+ test("a verified healthy result is not marked indeterminate", async () => {
146
+ // Sensitivity check for the cases above: if `indeterminate` were set
147
+ // unconditionally they would pass while proving nothing.
148
+ health = { status: "healthy", detail: "registered where we set it" };
149
+
150
+ const result = await deliveryCheck();
151
+ expect(result.passed).toBe(true);
152
+ expect(result.indeterminate).toBeFalsy();
153
+ });
154
+ });