@vellumai/assistant 0.10.12 → 0.11.0-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 (809) hide show
  1. package/AGENTS.md +1 -1
  2. package/ARCHITECTURE.md +4 -5
  3. package/README.md +1 -1
  4. package/docs/architecture/memory.md +60 -23
  5. package/docs/error-handling.md +3 -3
  6. package/docs/guardian-request-flow.md +112 -0
  7. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/package.json +1 -0
  8. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/channels.test.ts +9 -1
  9. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/secret-detection.test.ts +449 -0
  10. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/channels.ts +1 -0
  11. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/secret-detection.ts +468 -0
  12. package/node_modules/@vellumai/gateway-client/node_modules/@slack/types/dist/events/app.d.ts +28 -0
  13. package/node_modules/@vellumai/gateway-client/node_modules/@slack/types/dist/events/app.d.ts.map +1 -1
  14. package/node_modules/@vellumai/gateway-client/node_modules/@slack/types/dist/events/index.d.ts +2 -2
  15. package/node_modules/@vellumai/gateway-client/node_modules/@slack/types/dist/events/index.d.ts.map +1 -1
  16. package/node_modules/@vellumai/gateway-client/node_modules/@slack/types/dist/events/index.js.map +1 -1
  17. package/node_modules/@vellumai/gateway-client/node_modules/@slack/types/dist/events/message.d.ts +2 -0
  18. package/node_modules/@vellumai/gateway-client/node_modules/@slack/types/dist/events/message.d.ts.map +1 -1
  19. package/node_modules/@vellumai/gateway-client/node_modules/@slack/types/package.json +1 -1
  20. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json +1 -0
  21. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/channels.test.ts +9 -1
  22. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/secret-detection.test.ts +449 -0
  23. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/channels.ts +1 -0
  24. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/secret-detection.ts +468 -0
  25. package/node_modules/@vellumai/gateway-client/package.json +1 -1
  26. package/node_modules/@vellumai/gateway-client/src/admission-policy-contract.ts +6 -0
  27. package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +34 -0
  28. package/node_modules/@vellumai/service-contracts/package.json +1 -0
  29. package/node_modules/@vellumai/service-contracts/src/__tests__/channels.test.ts +9 -1
  30. package/node_modules/@vellumai/service-contracts/src/__tests__/secret-detection.test.ts +449 -0
  31. package/node_modules/@vellumai/service-contracts/src/channels.ts +1 -0
  32. package/node_modules/@vellumai/service-contracts/src/secret-detection.ts +468 -0
  33. package/openapi.yaml +264 -14
  34. package/package.json +2 -2
  35. package/src/__tests__/acp-session.test.ts +3 -3
  36. package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +3 -0
  37. package/src/__tests__/anthropic-provider.test.ts +40 -0
  38. package/src/__tests__/app-conversation-ids-backfill.test.ts +33 -2
  39. package/src/__tests__/app-conversation-ids.test.ts +204 -0
  40. package/src/__tests__/approval-cascade.test.ts +5 -5
  41. package/src/__tests__/approval-routes-http.test.ts +3 -3
  42. package/src/__tests__/ask-question-resolver.test.ts +207 -0
  43. package/src/__tests__/assistant-event-hub-self-exclusion.test.ts +16 -16
  44. package/src/__tests__/assistant-event-hub-targeted.test.ts +13 -11
  45. package/src/__tests__/assistant-event-hub.test.ts +13 -11
  46. package/src/__tests__/assistant-event.test.ts +12 -9
  47. package/src/__tests__/assistant-feature-flag-guard.test.ts +3 -3
  48. package/src/__tests__/assistant-feature-flag-guardrails.test.ts +1 -1
  49. package/src/__tests__/assistant-id-boundary-guard.test.ts +0 -58
  50. package/src/__tests__/assistant-stream-state.test.ts +17 -7
  51. package/src/__tests__/avatar-identity-sync.test.ts +3 -3
  52. package/src/__tests__/background-workers-disk-pressure.test.ts +1 -0
  53. package/src/__tests__/call-conversation-messages.test.ts +134 -18
  54. package/src/__tests__/call-start-guardian-guard.test.ts +27 -0
  55. package/src/__tests__/channel-approvals.test.ts +0 -27
  56. package/src/__tests__/channel-guardian.test.ts +85 -512
  57. package/src/__tests__/channel-readiness-slack-remote.test.ts +151 -3
  58. package/src/__tests__/channel-retry-sweep.test.ts +88 -1
  59. package/src/__tests__/channel-setup-panel-ack.test.ts +12 -9
  60. package/src/__tests__/chat-reveal-guard-priming.test.ts +31 -34
  61. package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +2 -2
  62. package/src/__tests__/compaction-events.test.ts +8 -8
  63. package/src/__tests__/config-loader-platform-defaults.test.ts +54 -28
  64. package/src/__tests__/config-model-image-provider.test.ts +20 -0
  65. package/src/__tests__/config-schema-memory-substrate.test.ts +306 -0
  66. package/src/__tests__/config-schema.test.ts +14 -2
  67. package/src/__tests__/config-set-platform-guard.test.ts +11 -12
  68. package/src/__tests__/config-sounds-sync.test.ts +4 -4
  69. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +5 -0
  70. package/src/__tests__/context-search-memory-source.test.ts +1 -1
  71. package/src/__tests__/context-search-memory-v2-source.test.ts +6 -6
  72. package/src/__tests__/context-token-estimator.test.ts +54 -1
  73. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +12 -3
  74. package/src/__tests__/conversation-agent-loop-fatal-cleanup.test.ts +13 -4
  75. package/src/__tests__/conversation-agent-loop-handlers-max-tokens.test.ts +4 -4
  76. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +10 -1
  77. package/src/__tests__/conversation-agent-loop-overflow.test.ts +22 -13
  78. package/src/__tests__/conversation-agent-loop.test.ts +39 -30
  79. package/src/__tests__/conversation-confirmation-signals.test.ts +14 -14
  80. package/src/__tests__/conversation-evictor.test.ts +49 -0
  81. package/src/__tests__/conversation-fork-crud.test.ts +1 -1
  82. package/src/__tests__/conversation-group-tools.test.ts +279 -0
  83. package/src/__tests__/conversation-history-web-search.test.ts +1 -1
  84. package/src/__tests__/conversation-key-store-disk-view.test.ts +5 -5
  85. package/src/__tests__/conversation-lineage.test.ts +235 -0
  86. package/src/__tests__/conversation-loop-db-lock-resilience.test.ts +5 -5
  87. package/src/__tests__/conversation-message-sync-tags.test.ts +4 -4
  88. package/src/__tests__/conversation-queue.test.ts +81 -81
  89. package/src/__tests__/conversation-routes-enabled-plugins.test.ts +8 -1
  90. package/src/__tests__/conversation-routes-hidden-queue.test.ts +8 -1
  91. package/src/__tests__/conversation-routes-slash-commands.test.ts +10 -1
  92. package/src/__tests__/conversation-runtime-assembly.test.ts +29 -2
  93. package/src/__tests__/conversation-slash-queue.test.ts +11 -11
  94. package/src/__tests__/conversation-slash-unknown.test.ts +2 -2
  95. package/src/__tests__/conversation-speed-override.test.ts +2 -2
  96. package/src/__tests__/conversation-summarize-route.test.ts +9 -0
  97. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +18 -16
  98. package/src/__tests__/conversation-surfaces-activation-emit.test.ts +17 -17
  99. package/src/__tests__/conversation-surfaces-app-open.test.ts +7 -4
  100. package/src/__tests__/conversation-surfaces-data-persist.test.ts +7 -7
  101. package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +9 -9
  102. package/src/__tests__/conversation-surfaces-standalone.test.ts +12 -12
  103. package/src/__tests__/conversation-surfaces-state-update.test.ts +8 -5
  104. package/src/__tests__/conversation-surfaces-table-action.test.ts +6 -3
  105. package/src/__tests__/conversation-surfaces-task-progress.test.ts +46 -46
  106. package/src/__tests__/conversation-sync-tags.test.ts +5 -5
  107. package/src/__tests__/conversation-wait-for-idle.test.ts +2 -2
  108. package/src/__tests__/conversation-workspace-cache-state.test.ts +0 -4
  109. package/src/__tests__/conversation-workspace-injection.test.ts +0 -3
  110. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +0 -3
  111. package/src/__tests__/copy-composer-tc-templates.test.ts +3 -131
  112. package/src/__tests__/credential-security-invariants.test.ts +1 -0
  113. package/src/__tests__/daemon-assistant-events.test.ts +14 -15
  114. package/src/__tests__/db-test-helpers.ts +13 -27
  115. package/src/__tests__/disk-pressure-routes.test.ts +12 -3
  116. package/src/__tests__/document-tool-security.test.ts +95 -0
  117. package/src/__tests__/drain-kick-guard.test.ts +232 -0
  118. package/src/__tests__/dynamic-skill-background-guard.test.ts +19 -6
  119. package/src/__tests__/emit-event-signal.test.ts +5 -6
  120. package/src/__tests__/encrypted-store-test-helpers.ts +10 -20
  121. package/src/__tests__/events-tail-route.test.ts +6 -4
  122. package/src/__tests__/feature-flag-test-helpers.ts +10 -20
  123. package/src/__tests__/file-edit-tool.test.ts +24 -4
  124. package/src/__tests__/file-list-tool.test.ts +2 -1
  125. package/src/__tests__/file-read-tool.test.ts +62 -10
  126. package/src/__tests__/file-write-tool.test.ts +24 -5
  127. package/src/__tests__/followup-tools.test.ts +58 -0
  128. package/src/__tests__/gateway-threshold-reader-mock.ts +99 -0
  129. package/src/__tests__/graph-extraction-event-date.test.ts +1 -1
  130. package/src/__tests__/guardian-question-mode.test.ts +42 -3
  131. package/src/__tests__/guardian-reply-router-answer-mode.test.ts +283 -0
  132. package/src/__tests__/guardian-vellum-destination-pinning.test.ts +221 -0
  133. package/src/__tests__/heartbeat-disk-pressure.test.ts +1 -0
  134. package/src/__tests__/heartbeat-service.test.ts +1 -0
  135. package/src/__tests__/helpers/native-web-search-harness.ts +10 -10
  136. package/src/__tests__/host-browser-proxy.test.ts +1 -1
  137. package/src/__tests__/host-file-edit-tool.test.ts +3 -3
  138. package/src/__tests__/host-file-read-tool.test.ts +2 -2
  139. package/src/__tests__/host-file-write-tool.test.ts +7 -3
  140. package/src/__tests__/host-shell-tool.test.ts +17 -5
  141. package/src/__tests__/host-transfer-pending-interactions.test.ts +8 -8
  142. package/src/__tests__/image-credentials.test.ts +65 -8
  143. package/src/__tests__/injection-block.test.ts +4 -4
  144. package/src/__tests__/injector-chain.test.ts +1 -1
  145. package/src/__tests__/injector-pkb-v2-silenced.test.ts +2 -2
  146. package/src/__tests__/injector-registry-order-guard.test.ts +4 -9
  147. package/src/__tests__/injector-v3-suppression.test.ts +6 -4
  148. package/src/__tests__/lifecycle-memory-v2-seed.test.ts +27 -28
  149. package/src/__tests__/list-messages-page-latest.test.ts +2 -2
  150. package/src/__tests__/llm-usage-store.test.ts +67 -4
  151. package/src/__tests__/log-redact.test.ts +18 -0
  152. package/src/__tests__/managed-profile-guard.test.ts +27 -0
  153. package/src/__tests__/managed-skill-lifecycle.test.ts +2 -2
  154. package/src/__tests__/media-generate-image.test.ts +62 -11
  155. package/src/__tests__/memory-identity-context-parity.test.ts +1 -1
  156. package/src/__tests__/memory-jobs-worker-lanes.test.ts +9 -1
  157. package/src/__tests__/memory-retrieval-hook.test.ts +4 -4
  158. package/src/__tests__/notification-decision-strategy.test.ts +115 -11
  159. package/src/__tests__/notification-deep-link.test.ts +12 -12
  160. package/src/__tests__/notification-vellum-adapter.test.ts +8 -8
  161. package/src/__tests__/outbound-slack-persistence.test.ts +8 -8
  162. package/src/__tests__/path-policy.test.ts +315 -0
  163. package/src/__tests__/pending-interactions-resolved-event.test.ts +6 -6
  164. package/src/__tests__/persistence-layering-guard.test.ts +2 -2
  165. package/src/__tests__/pkb-autoinject.test.ts +1 -1
  166. package/src/__tests__/platform-bash-auto-approve.test.ts +1 -0
  167. package/src/__tests__/platform-callback-registration.test.ts +10 -10
  168. package/src/__tests__/plugin-event-hub-facade.test.ts +22 -15
  169. package/src/__tests__/plugin-import-boundary-guard.test.ts +15 -15
  170. package/src/__tests__/processing-flag-persist-failure.test.ts +0 -4
  171. package/src/__tests__/provider-platform-proxy-integration.test.ts +0 -1
  172. package/src/__tests__/rebuild-index-graph-nodes.test.ts +1 -1
  173. package/src/__tests__/request-secret-standalone-channel-gate.test.ts +5 -5
  174. package/src/__tests__/require-fresh-approval.test.ts +38 -38
  175. package/src/__tests__/resolve-app-id.test.ts +32 -0
  176. package/src/__tests__/run-due-schedules.test.ts +72 -1
  177. package/src/__tests__/runtime-events-sse-parity.test.ts +11 -10
  178. package/src/__tests__/runtime-events-sse.test.ts +1 -1
  179. package/src/__tests__/schedule-routes.test.ts +44 -5
  180. package/src/__tests__/schedule-store.test.ts +3 -3
  181. package/src/__tests__/schedule-tools.test.ts +176 -0
  182. package/src/__tests__/secret-prompt-log-hygiene.test.ts +3 -3
  183. package/src/__tests__/secret-prompter-channel-fallback.test.ts +3 -3
  184. package/src/__tests__/secret-response-routing.test.ts +3 -3
  185. package/src/__tests__/secret-routes-platform-proxy.test.ts +4 -7
  186. package/src/__tests__/secret-scanner.test.ts +36 -0
  187. package/src/__tests__/send-endpoint-busy.test.ts +7 -7
  188. package/src/__tests__/slack-required-scopes-mirror.test.ts +81 -0
  189. package/src/__tests__/starter-task-flow.test.ts +26 -3
  190. package/src/__tests__/subagent-call-site-routing.test.ts +2 -2
  191. package/src/__tests__/subagent-disposal.test.ts +40 -11
  192. package/src/__tests__/subagent-fork-notifications.test.ts +4 -4
  193. package/src/__tests__/subagent-fork-prompt-role.test.ts +3 -3
  194. package/src/__tests__/subagent-fork-spawn.test.ts +4 -4
  195. package/src/__tests__/subagent-manager-notify.test.ts +73 -14
  196. package/src/__tests__/subagent-notify-parent.test.ts +48 -4
  197. package/src/__tests__/subagent-spawn-and-await.test.ts +62 -14
  198. package/src/__tests__/subagent-tools.test.ts +64 -0
  199. package/src/__tests__/sync-message-contract.test.ts +3 -3
  200. package/src/__tests__/terminal-tools.test.ts +12 -3
  201. package/src/__tests__/test-preload.ts +9 -0
  202. package/src/__tests__/tool-approval-handler.test.ts +587 -43
  203. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +7 -4
  204. package/src/__tests__/tool-executor.test.ts +116 -0
  205. package/src/__tests__/tool-grant-request-escalation.test.ts +5 -0
  206. package/src/__tests__/tool-preview-lifecycle.test.ts +32 -29
  207. package/src/__tests__/tool-result-metadata-plumbing.test.ts +5 -5
  208. package/src/__tests__/tool-side-effects-slack-dm.test.ts +1 -0
  209. package/src/__tests__/tool-start-timestamp.test.ts +4 -4
  210. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +9 -22
  211. package/src/__tests__/ui-choice-copy-surfaces.test.ts +13 -13
  212. package/src/__tests__/ui-work-result-surface.test.ts +3 -3
  213. package/src/__tests__/voice-scoped-grant-consumer.test.ts +6 -6
  214. package/src/__tests__/voice-session-bridge.test.ts +44 -44
  215. package/src/__tests__/workspace-migration-134-image-generation-mode-to-provider.test.ts +284 -0
  216. package/src/__tests__/workspace-policy.test.ts +482 -2
  217. package/src/acp/__tests__/client-handler.test.ts +3 -3
  218. package/src/acp/__tests__/session-manager-persistence.test.ts +10 -10
  219. package/src/acp/__tests__/session-manager-resume.test.ts +7 -7
  220. package/src/acp/client-handler.ts +3 -3
  221. package/src/acp/session-manager.ts +8 -8
  222. package/src/api/events/subagent-event.ts +2 -2
  223. package/src/api/responses/memory-v2-activation-log.ts +2 -2
  224. package/src/api/responses/subagent-detail.ts +13 -0
  225. package/src/approvals/AGENTS.md +2 -0
  226. package/src/approvals/guardian-request-resolvers.ts +118 -24
  227. package/src/apps/app-store.ts +95 -4
  228. package/src/calls/__tests__/voice-control-protocol.test.ts +57 -0
  229. package/src/calls/__tests__/voice-session-bridge.test.ts +229 -7
  230. package/src/calls/__tests__/voice-triage-escalate.test.ts +3 -4
  231. package/src/calls/call-controller.ts +27 -27
  232. package/src/calls/call-conversation-messages.ts +58 -18
  233. package/src/calls/voice-control-protocol.ts +56 -0
  234. package/src/calls/voice-session-bridge.ts +178 -65
  235. package/src/calls/voice-triage-escalate.ts +2 -3
  236. package/src/channels/config.ts +8 -0
  237. package/src/channels/types.ts +1 -0
  238. package/src/cli/commands/config.ts +49 -13
  239. package/src/cli/commands/memory/index.help.ts +4 -2
  240. package/src/cli/commands/memory/memory-retrospective.ts +5 -0
  241. package/src/cli/commands/webhooks.help.ts +9 -4
  242. package/src/cli/lib/bundled-marketplace.json +12 -0
  243. package/src/cli.ts +2 -109
  244. package/src/config/__tests__/deployment-context-defaults.test.ts +9 -0
  245. package/src/config/__tests__/substrate-twin-shadowing.test.ts +191 -0
  246. package/src/config/assistant-feature-flags.ts +1 -1
  247. package/src/config/bundled-skills/conversation-groups/SKILL.md +29 -0
  248. package/src/config/bundled-skills/conversation-groups/TOOLS.json +58 -0
  249. package/src/config/bundled-skills/conversation-groups/tools/group-create.ts +12 -0
  250. package/src/config/bundled-skills/conversation-groups/tools/group-list.ts +12 -0
  251. package/src/config/bundled-skills/conversation-groups/tools/move-to-group.ts +12 -0
  252. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +13 -7
  253. package/src/config/bundled-skills/schedule/SKILL.md +2 -0
  254. package/src/config/feature-flag-cache.ts +6 -16
  255. package/src/config/feature-flag-registry.json +14 -38
  256. package/src/config/loader.ts +7 -1
  257. package/src/config/memory-tier.ts +15 -10
  258. package/src/config/memory-v3-gate.test.ts +278 -1
  259. package/src/config/memory-v3-gate.ts +88 -26
  260. package/src/config/raw-config-utils.ts +1 -1
  261. package/src/config/schemas/__tests__/live-voice.test.ts +27 -16
  262. package/src/config/schemas/__tests__/memory-v3.test.ts +1 -0
  263. package/src/config/schemas/live-voice.ts +33 -7
  264. package/src/config/schemas/llm.ts +1 -0
  265. package/src/config/schemas/memory-retrospective.ts +13 -0
  266. package/src/config/schemas/memory-substrate.ts +183 -0
  267. package/src/config/schemas/memory-v2.ts +3 -2
  268. package/src/config/schemas/memory-v3.ts +11 -4
  269. package/src/config/schemas/memory.ts +35 -1
  270. package/src/config/schemas/services.ts +10 -2
  271. package/src/config/substrate-twin-shadowing.ts +195 -0
  272. package/src/config/webhook-routing.ts +67 -0
  273. package/src/context/compactor.ts +10 -3
  274. package/src/context/token-estimator.ts +21 -0
  275. package/src/daemon/__tests__/channel-ui-capability.test.ts +85 -0
  276. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +3 -3
  277. package/src/daemon/__tests__/embedding-reconcile.test.ts +3 -3
  278. package/src/daemon/__tests__/inference-profile-notification.test.ts +3 -6
  279. package/src/daemon/__tests__/native-web-search-metadata.test.ts +5 -5
  280. package/src/daemon/__tests__/web-search-status-text.test.ts +2 -2
  281. package/src/daemon/channel-ui-capability.ts +70 -1
  282. package/src/daemon/conversation-agent-loop-handlers.ts +4 -4
  283. package/src/daemon/conversation-agent-loop.ts +12 -5
  284. package/src/daemon/conversation-evictor.ts +26 -4
  285. package/src/daemon/conversation-launch.ts +11 -14
  286. package/src/daemon/conversation-lineage.ts +76 -0
  287. package/src/daemon/conversation-messaging.ts +14 -14
  288. package/src/daemon/conversation-notifiers.ts +2 -2
  289. package/src/daemon/conversation-process.ts +133 -21
  290. package/src/daemon/conversation-queue-manager.ts +7 -7
  291. package/src/daemon/conversation-runtime-assembly.ts +16 -0
  292. package/src/daemon/conversation-store.ts +11 -3
  293. package/src/daemon/conversation-surfaces.ts +8 -14
  294. package/src/daemon/conversation-tool-setup.ts +13 -7
  295. package/src/daemon/conversation-usage.ts +12 -11
  296. package/src/daemon/conversation.ts +38 -15
  297. package/src/daemon/disk-pressure-guard.ts +5 -12
  298. package/src/daemon/embedding-reconcile.ts +14 -9
  299. package/src/daemon/handlers/config-channels.ts +40 -134
  300. package/src/daemon/handlers/config-model.ts +15 -7
  301. package/src/daemon/handlers/conversations.ts +4 -2
  302. package/src/daemon/handlers/shared.ts +29 -0
  303. package/src/daemon/host-proxy-base.ts +4 -4
  304. package/src/daemon/lifecycle.ts +13 -0
  305. package/src/daemon/message-protocol.ts +8 -144
  306. package/src/daemon/message-types/computer-use.ts +0 -12
  307. package/src/daemon/message-types/conversations.ts +9 -345
  308. package/src/daemon/message-types/host-app-control.ts +0 -9
  309. package/src/daemon/message-types/host-browser.ts +0 -7
  310. package/src/daemon/message-types/host-ui-snapshot.ts +0 -7
  311. package/src/daemon/message-types/messages.ts +0 -48
  312. package/src/daemon/message-types/notifications.ts +0 -7
  313. package/src/daemon/message-types/skills.ts +0 -5
  314. package/src/daemon/message-types/surfaces.ts +0 -9
  315. package/src/daemon/message-types/sync.ts +0 -2
  316. package/src/daemon/process-message.ts +5 -5
  317. package/src/daemon/skill-memory-refresh.ts +5 -5
  318. package/src/daemon/tool-side-effects.ts +32 -16
  319. package/src/daemon/trust-context.ts +1 -1
  320. package/src/daemon/wake-conversation-ops.ts +4 -4
  321. package/src/heartbeat/__tests__/heartbeat-run-store.test.ts +100 -7
  322. package/src/heartbeat/__tests__/heartbeat-service.test.ts +67 -0
  323. package/src/heartbeat/heartbeat-run-store.ts +81 -4
  324. package/src/heartbeat/heartbeat-service.ts +32 -1
  325. package/src/home/__tests__/feed-writer.test.ts +7 -7
  326. package/src/home/__tests__/home-content-refresh.test.ts +3 -0
  327. package/src/home/__tests__/suggested-prompts.test.ts +1 -0
  328. package/src/home/feed-writer.ts +6 -13
  329. package/src/home/home-content-refresh.ts +6 -9
  330. package/src/home/relationship-state-writer.ts +5 -12
  331. package/src/home/suggested-prompts-cache.ts +6 -19
  332. package/src/ipc/__tests__/events-publish-client.test.ts +67 -0
  333. package/src/ipc/events-publish-client.ts +44 -0
  334. package/src/ipc/routes/__tests__/conversation-sync-ipc-routes.test.ts +2 -2
  335. package/src/ipc/routes/__tests__/events-ipc-routes.test.ts +38 -9
  336. package/src/ipc/routes/events-ipc-routes.ts +11 -31
  337. package/src/live-voice/__tests__/front-decision.test.ts +4 -176
  338. package/src/live-voice/__tests__/live-voice-agent-turn.test.ts +276 -68
  339. package/src/live-voice/__tests__/live-voice-continuation-fork-hydration.test.ts +286 -0
  340. package/src/live-voice/__tests__/live-voice-events.test.ts +6 -3
  341. package/src/live-voice/__tests__/live-voice-progress.test.ts +185 -46
  342. package/src/live-voice/__tests__/live-voice-triage-escalate.test.ts +24 -59
  343. package/src/live-voice/__tests__/live-voice-tts-session.test.ts +38 -54
  344. package/src/live-voice/__tests__/live-voice-vad.test.ts +1154 -636
  345. package/src/live-voice/front-decision.ts +5 -155
  346. package/src/live-voice/live-voice-metrics.ts +4 -1
  347. package/src/live-voice/live-voice-session.ts +1288 -439
  348. package/src/live-voice/progress-phrases.ts +17 -0
  349. package/src/live-voice/protocol.ts +15 -0
  350. package/src/media/app-icon-generator.ts +15 -8
  351. package/src/media/avatar-router.ts +9 -5
  352. package/src/media/image-credentials.ts +42 -10
  353. package/src/media/image-service.ts +1 -13
  354. package/src/media/types.ts +26 -3
  355. package/src/notifications/AGENTS.md +2 -0
  356. package/src/notifications/__tests__/broadcaster.test.ts +95 -0
  357. package/src/notifications/adapters/macos.ts +3 -3
  358. package/src/notifications/broadcaster.ts +57 -0
  359. package/src/notifications/copy-composer.ts +0 -15
  360. package/src/notifications/decision-engine.ts +21 -22
  361. package/src/notifications/guardian-question-mode.ts +57 -2
  362. package/src/notifications/home-feed-side-effect.ts +4 -0
  363. package/src/notifications/signal.ts +6 -4
  364. package/src/notifications/trusted-contact-payloads.ts +3 -4
  365. package/src/notifications/vellum-card-affinity.ts +39 -0
  366. package/src/oauth/AGENTS.md +1 -1
  367. package/src/permissions/channel-permission-query.test.ts +69 -0
  368. package/src/permissions/channel-permission-query.ts +73 -1
  369. package/src/permissions/checker.test.ts +298 -15
  370. package/src/permissions/checker.ts +102 -36
  371. package/src/permissions/confirmation-guardian-request.test.ts +2 -2
  372. package/src/permissions/confirmation-guardian-request.ts +2 -2
  373. package/src/permissions/gateway-threshold-reader.test.ts +224 -17
  374. package/src/permissions/gateway-threshold-reader.ts +82 -10
  375. package/src/permissions/ipc-risk-types.ts +8 -0
  376. package/src/permissions/prompter.ts +5 -5
  377. package/src/permissions/question-guardian-request.test.ts +222 -0
  378. package/src/permissions/question-guardian-request.ts +179 -0
  379. package/src/permissions/question-prompter.test.ts +8 -8
  380. package/src/permissions/question-prompter.ts +18 -0
  381. package/src/permissions/secret-prompter.ts +2 -4
  382. package/src/permissions/workspace-policy.ts +269 -34
  383. package/src/persistence/__tests__/lifecycle-quiesce.test.ts +82 -0
  384. package/src/persistence/__tests__/message-content-file.test.ts +58 -3
  385. package/src/persistence/conversation-crud.ts +93 -0
  386. package/src/persistence/conversation-key-store.ts +63 -0
  387. package/src/persistence/conversation-parent.ts +27 -0
  388. package/src/persistence/db-async-query.ts +1 -1
  389. package/src/persistence/db-singleton.ts +14 -27
  390. package/src/persistence/delivery-crud.ts +45 -0
  391. package/src/persistence/embeddings/embedding-backend.ts +2 -1
  392. package/src/persistence/embeddings/embedding-identity.ts +7 -3
  393. package/src/persistence/job-utils.ts +32 -27
  394. package/src/persistence/jobs-store.ts +92 -3
  395. package/src/persistence/lifecycle-quiesce.ts +80 -0
  396. package/src/persistence/llm-usage-store.ts +45 -8
  397. package/src/persistence/message-content-file.ts +4 -4
  398. package/src/persistence/migrations/336-move-memory-v2-activation-logs-to-memory-db.ts +1 -1
  399. package/src/persistence/migrations/351-schedule-skill-script-handoff.ts +39 -0
  400. package/src/persistence/migrations/352-drop-schedule-skill-script-handoff.test.ts +83 -0
  401. package/src/persistence/migrations/352-drop-schedule-skill-script-handoff.ts +36 -0
  402. package/src/persistence/migrations/353-add-llm-usage-conversation-type.test.ts +110 -0
  403. package/src/persistence/migrations/353-add-llm-usage-conversation-type.ts +49 -0
  404. package/src/persistence/migrations/354-backfill-app-conversation-lineage.ts +41 -0
  405. package/src/persistence/schema/infrastructure.ts +11 -0
  406. package/src/persistence/steps.ts +9 -1
  407. package/src/plugin-api/__tests__/publish-event-forwarding.test.ts +113 -0
  408. package/src/plugin-api/__tests__/publish-event.test.ts +57 -0
  409. package/src/plugin-api/conversation-turn.ts +4 -4
  410. package/src/plugin-api/event-hub-facade.ts +15 -6
  411. package/src/plugin-api/index.ts +10 -4
  412. package/src/plugin-api/publish-event.ts +36 -0
  413. package/src/plugins/AGENTS.md +2 -0
  414. package/src/plugins/defaults/compaction/window-manager.ts +9 -0
  415. package/src/plugins/defaults/index.ts +9 -13
  416. package/src/plugins/defaults/memory/AGENTS.md +775 -0
  417. package/src/plugins/defaults/memory/__tests__/capability-seed-embed-gate.test.ts +186 -0
  418. package/src/plugins/defaults/memory/__tests__/fixtures/memory-v2-activation-fixtures.ts +1 -1
  419. package/src/plugins/defaults/memory/__tests__/indexer-tier-dispatch.test.ts +129 -0
  420. package/src/plugins/defaults/memory/__tests__/jobs-worker-quiesce-gate.test.ts +91 -0
  421. package/src/plugins/defaults/memory/__tests__/jobs-worker-v1-graph-substrate-noop.test.ts +342 -0
  422. package/src/plugins/defaults/memory/__tests__/jobs-worker-v2-graph-trigger-embed.test.ts +9 -1
  423. package/src/plugins/defaults/memory/__tests__/jobs-worker-v2-schedule.test.ts +36 -1
  424. package/src/plugins/defaults/memory/__tests__/jobs-worker-v2-sweep-defer.test.ts +9 -1
  425. package/src/plugins/defaults/memory/__tests__/memory-log-stores-degraded.test.ts +2 -2
  426. package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +44 -10
  427. package/src/plugins/defaults/memory/__tests__/memory-retrospective-sweep.test.ts +151 -10
  428. package/src/plugins/defaults/memory/__tests__/memory-tier-boundary-guard.test.ts +1155 -0
  429. package/src/plugins/defaults/memory/__tests__/memory-v2-activation-log-store.test.ts +1 -1
  430. package/src/plugins/defaults/memory/__tests__/memory-v2-concept-frequency.test.ts +3 -3
  431. package/src/plugins/defaults/memory/__tests__/tools-delete-memory-page.test.ts +1 -1
  432. package/src/plugins/defaults/memory/context-search/sources/memory-v2.ts +71 -35
  433. package/src/plugins/defaults/memory/context-search/sources/memory.ts +5 -1
  434. package/src/plugins/defaults/memory/fork-conversation-memory.ts +4 -4
  435. package/src/plugins/defaults/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +80 -10
  436. package/src/plugins/defaults/memory/graph/__tests__/handle-remember-v2.test.ts +1 -1
  437. package/src/plugins/defaults/memory/graph/capability-seed.ts +24 -10
  438. package/src/plugins/defaults/memory/graph/conversation-graph-memory.ts +123 -49
  439. package/src/plugins/defaults/memory/graph/graph-search.ts +6 -118
  440. package/src/plugins/defaults/memory/graph/in-context-tracker.test.ts +173 -0
  441. package/src/plugins/defaults/memory/graph/in-context-tracker.ts +109 -0
  442. package/src/plugins/defaults/memory/graph/store.ts +1 -0
  443. package/src/plugins/defaults/memory/graph/tool-handlers.test.ts +1 -1
  444. package/src/plugins/defaults/memory/graph/tool-handlers.ts +5 -2
  445. package/src/plugins/defaults/memory/graph-topology/__tests__/build-memory-graph.test.ts +1 -1
  446. package/src/plugins/defaults/memory/graph-topology/build-memory-graph.ts +6 -9
  447. package/src/plugins/defaults/memory/hooks/__tests__/user-prompt-submit.test.ts +30 -16
  448. package/src/plugins/defaults/memory/hooks/user-prompt-submit.ts +18 -14
  449. package/src/plugins/defaults/memory/indexer.ts +146 -89
  450. package/src/plugins/defaults/memory/injectors.ts +86 -41
  451. package/src/plugins/defaults/memory/job-handlers.ts +130 -39
  452. package/src/plugins/defaults/memory/jobs/__tests__/embed-concept-page.test.ts +2 -2
  453. package/src/plugins/defaults/memory/jobs/embed-concept-page.ts +22 -10
  454. package/src/plugins/defaults/memory/jobs-worker.ts +279 -208
  455. package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +4 -2
  456. package/src/plugins/defaults/memory/memory-retrospective-job.ts +40 -3
  457. package/src/plugins/defaults/memory/memory-retrospective-sweep.ts +99 -11
  458. package/src/plugins/defaults/memory/src/__tests__/memory-v2-routes.test.ts +5 -3
  459. package/src/plugins/defaults/memory/src/__tests__/memory-v2-simulate-route.test.ts +3 -4
  460. package/src/plugins/defaults/memory/src/memory-graph-routes.ts +2 -1
  461. package/src/plugins/defaults/memory/src/memory-item-routes.test.ts +7 -5
  462. package/src/plugins/defaults/memory/src/memory-item-routes.ts +51 -19
  463. package/src/plugins/defaults/memory/src/memory-v2-routes.ts +31 -20
  464. package/src/plugins/defaults/memory/startup.ts +58 -21
  465. package/src/plugins/defaults/memory/substrate/__tests__/boot-maintenance.test.ts +335 -0
  466. package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/cli-command-store.test.ts +14 -8
  467. package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/consolidation-job.test.ts +51 -21
  468. package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/edge-index.test.ts +1 -1
  469. package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/frontmatter-sweep.test.ts +3 -3
  470. package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/page-index.test.ts +1 -1
  471. package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/page-store.test.ts +1 -1
  472. package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/prompts-consolidation.test.ts +2 -2
  473. package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/qdrant.test.ts +23 -14
  474. package/src/plugins/defaults/memory/substrate/__tests__/reembed-job.test.ts +262 -0
  475. package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/sim.test.ts +9 -12
  476. package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/skill-content.test.ts +2 -2
  477. package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/skill-store.test.ts +9 -9
  478. package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/sparse-bm25.test.ts +12 -5
  479. package/src/plugins/defaults/memory/substrate/__tests__/spread.test.ts +276 -0
  480. package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/static-context.test.ts +17 -8
  481. package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/sweep-job.test.ts +9 -9
  482. package/src/plugins/defaults/memory/{v3/substrate/memory-v2-startup.ts → substrate/boot-maintenance.ts} +43 -24
  483. package/src/plugins/defaults/memory/{v3/substrate → substrate}/cli-command-store.ts +13 -8
  484. package/src/plugins/defaults/memory/{v3/substrate → substrate}/consolidation-job.ts +51 -27
  485. package/src/plugins/defaults/memory/{v3/substrate → substrate}/constants.ts +1 -0
  486. package/src/plugins/defaults/memory/{v3/substrate → substrate}/edge-index.ts +28 -9
  487. package/src/plugins/defaults/memory/{v3/substrate → substrate}/frontmatter-sweep.ts +14 -7
  488. package/src/plugins/defaults/memory/{v3/substrate → substrate}/injected-block-slugs.ts +9 -3
  489. package/src/plugins/defaults/memory/{v3/substrate → substrate}/page-index.ts +29 -10
  490. package/src/plugins/defaults/memory/{v3/substrate → substrate}/page-store.ts +35 -12
  491. package/src/plugins/defaults/memory/{v3/substrate → substrate}/prompts/consolidation.ts +14 -8
  492. package/src/plugins/defaults/memory/{v3/substrate → substrate}/prompts/sweep.ts +4 -1
  493. package/src/plugins/defaults/memory/{v3/substrate → substrate}/qdrant.ts +96 -36
  494. package/src/plugins/defaults/memory/substrate/reembed-job.ts +54 -0
  495. package/src/plugins/defaults/memory/{v3/substrate → substrate}/sim.ts +26 -15
  496. package/src/plugins/defaults/memory/{v3/substrate → substrate}/skill-content.ts +1 -1
  497. package/src/plugins/defaults/memory/{v3/substrate → substrate}/skill-store.ts +13 -8
  498. package/src/plugins/defaults/memory/{v3/substrate → substrate}/sparse-bm25.ts +27 -10
  499. package/src/plugins/defaults/memory/substrate/spread.ts +165 -0
  500. package/src/plugins/defaults/memory/{v3/substrate → substrate}/static-context.ts +12 -7
  501. package/src/plugins/defaults/memory/{v3/substrate → substrate}/sweep-job.ts +20 -15
  502. package/src/plugins/defaults/memory/substrate/tuning.ts +93 -0
  503. package/src/plugins/defaults/memory/{v3/substrate → substrate}/types.ts +7 -4
  504. package/src/plugins/defaults/memory/tools.test.ts +1 -1
  505. package/src/plugins/defaults/memory/tools.ts +1 -1
  506. package/src/plugins/defaults/memory/v1/README.md +5 -0
  507. package/src/plugins/defaults/memory/{__tests__ → v1/__tests__}/filing-jobs.test.ts +4 -4
  508. package/src/plugins/defaults/memory/{filing-jobs.ts → v1/filing-jobs.ts} +3 -3
  509. package/src/plugins/defaults/memory/{graph → v1/graph}/bootstrap.test.ts +68 -6
  510. package/src/plugins/defaults/memory/{graph → v1/graph}/bootstrap.ts +40 -21
  511. package/src/plugins/defaults/memory/{graph → v1/graph}/consolidation.ts +14 -14
  512. package/src/plugins/defaults/memory/{graph → v1/graph}/decay.test.ts +1 -1
  513. package/src/plugins/defaults/memory/{graph → v1/graph}/decay.ts +3 -3
  514. package/src/plugins/defaults/memory/{graph → v1/graph}/extraction-job.ts +5 -5
  515. package/src/plugins/defaults/memory/{graph → v1/graph}/extraction.test.ts +1 -1
  516. package/src/plugins/defaults/memory/{graph → v1/graph}/extraction.ts +20 -14
  517. package/src/plugins/defaults/memory/{graph → v1/graph}/graph-search.test.ts +3 -3
  518. package/src/plugins/defaults/memory/v1/graph/graph-search.ts +123 -0
  519. package/src/plugins/defaults/memory/{graph → v1/graph}/injection.test.ts +1 -118
  520. package/src/plugins/defaults/memory/{graph → v1/graph}/injection.ts +9 -111
  521. package/src/plugins/defaults/memory/{graph → v1/graph}/narrative.ts +6 -6
  522. package/src/plugins/defaults/memory/{graph → v1/graph}/pattern-scan.ts +5 -5
  523. package/src/plugins/defaults/memory/{graph → v1/graph}/retriever.test.ts +135 -42
  524. package/src/plugins/defaults/memory/{graph → v1/graph}/retriever.ts +40 -22
  525. package/src/plugins/defaults/memory/{graph → v1/graph}/scoring.test.ts +1 -1
  526. package/src/plugins/defaults/memory/{graph → v1/graph}/scoring.ts +1 -1
  527. package/src/plugins/defaults/memory/{graph → v1/graph}/serendipity.ts +1 -1
  528. package/src/plugins/defaults/memory/{graph → v1/graph}/triggers.test.ts +1 -1
  529. package/src/plugins/defaults/memory/{graph → v1/graph}/triggers.ts +1 -1
  530. package/src/plugins/defaults/memory/{identity-context.ts → v1/identity-context.ts} +4 -4
  531. package/src/plugins/defaults/memory/{job-handlers → v1/job-handlers}/backfill.ts +6 -6
  532. package/src/plugins/defaults/memory/{job-handlers → v1/job-handlers}/embedding.test.ts +6 -6
  533. package/src/plugins/defaults/memory/{job-handlers → v1/job-handlers}/embedding.ts +6 -6
  534. package/src/plugins/defaults/memory/{job-handlers → v1/job-handlers}/index-maintenance.ts +8 -8
  535. package/src/plugins/defaults/memory/{jobs → v1/jobs}/embed-pkb-file.test.ts +7 -7
  536. package/src/plugins/defaults/memory/{jobs → v1/jobs}/embed-pkb-file.ts +5 -5
  537. package/src/plugins/defaults/memory/{pkb → v1/pkb}/autoinject.ts +1 -1
  538. package/src/plugins/defaults/memory/{pkb → v1/pkb}/context.ts +2 -2
  539. package/src/plugins/defaults/memory/{pkb → v1/pkb}/pkb-index.test.ts +3 -3
  540. package/src/plugins/defaults/memory/{pkb → v1/pkb}/pkb-index.ts +2 -2
  541. package/src/plugins/defaults/memory/{pkb → v1/pkb}/pkb-reconcile.test.ts +3 -3
  542. package/src/plugins/defaults/memory/{pkb → v1/pkb}/pkb-reconcile.ts +2 -2
  543. package/src/plugins/defaults/memory/{pkb → v1/pkb}/pkb-search.test.ts +3 -3
  544. package/src/plugins/defaults/memory/{pkb → v1/pkb}/pkb-search.ts +6 -6
  545. package/src/plugins/defaults/memory/{pkb → v1/pkb}/types.ts +3 -5
  546. package/src/plugins/defaults/memory/{pkb-schedule.ts → v1/pkb-schedule.ts} +2 -2
  547. package/src/plugins/defaults/memory/{search/semantic.ts → v1/semantic-search.ts} +1 -1
  548. package/src/plugins/defaults/memory/v2/__tests__/activation-store.test.ts +1 -1
  549. package/src/plugins/defaults/memory/v2/__tests__/activation.test.ts +11 -274
  550. package/src/plugins/defaults/memory/v2/__tests__/backfill-jobs.test.ts +9 -131
  551. package/src/plugins/defaults/memory/v2/__tests__/harness-compare.test.ts +1 -1
  552. package/src/plugins/defaults/memory/v2/__tests__/harness-oracle.test.ts +1 -1
  553. package/src/plugins/defaults/memory/v2/__tests__/harness-replay-input.test.ts +1 -1
  554. package/src/plugins/defaults/memory/v2/__tests__/injection.test.ts +14 -10
  555. package/src/plugins/defaults/memory/v2/__tests__/reranker.test.ts +7 -5
  556. package/src/plugins/defaults/memory/v2/__tests__/router.test.ts +12 -6
  557. package/src/plugins/defaults/memory/{memory-v2-activation-log-store.ts → v2/activation-log-store.ts} +2 -2
  558. package/src/plugins/defaults/memory/v2/activation-store.ts +22 -8
  559. package/src/plugins/defaults/memory/v2/activation.ts +30 -163
  560. package/src/plugins/defaults/memory/v2/backfill-jobs.ts +7 -54
  561. package/src/plugins/defaults/memory/{memory-v2-concept-frequency.ts → v2/concept-frequency.ts} +3 -3
  562. package/src/plugins/defaults/memory/v2/harness/oracle.ts +1 -1
  563. package/src/plugins/defaults/memory/v2/harness/replay-input.ts +2 -2
  564. package/src/plugins/defaults/memory/v2/harness/retriever.ts +1 -1
  565. package/src/plugins/defaults/memory/v2/injection.ts +36 -26
  566. package/src/plugins/defaults/memory/v2/migration.ts +2 -2
  567. package/src/plugins/defaults/memory/{rerank-local.ts → v2/rerank-local.ts} +44 -18
  568. package/src/plugins/defaults/memory/v2/reranker.ts +29 -11
  569. package/src/plugins/defaults/memory/v2/router.ts +38 -15
  570. package/src/plugins/defaults/memory/v3/__tests__/candidate-match.test.ts +5 -3
  571. package/src/plugins/defaults/memory/v3/__tests__/carry-integration.test.ts +21 -8
  572. package/src/plugins/defaults/memory/v3/__tests__/edge.test.ts +1 -1
  573. package/src/plugins/defaults/memory/v3/__tests__/live-integration.test.ts +1 -1
  574. package/src/plugins/defaults/memory/v3/__tests__/maintain-job.test.ts +4 -1
  575. package/src/plugins/defaults/memory/v3/__tests__/orchestrate.test.ts +181 -3
  576. package/src/plugins/defaults/memory/v3/__tests__/selection-log-store.test.ts +3 -0
  577. package/src/plugins/defaults/memory/v3/__tests__/shadow-integration.test.ts +14 -5
  578. package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +40 -50
  579. package/src/plugins/defaults/memory/v3/__tests__/tuning-profile.test.ts +1 -0
  580. package/src/plugins/defaults/memory/v3/candidate-match.ts +2 -2
  581. package/src/plugins/defaults/memory/v3/capabilities.ts +6 -4
  582. package/src/plugins/defaults/memory/v3/card.ts +25 -9
  583. package/src/plugins/defaults/memory/v3/edge.ts +52 -18
  584. package/src/plugins/defaults/memory/v3/gate.ts +4 -4
  585. package/src/plugins/defaults/memory/v3/injector.ts +10 -3
  586. package/src/plugins/defaults/memory/v3/maintain-job.ts +3 -3
  587. package/src/plugins/defaults/memory/v3/orchestrate.ts +100 -18
  588. package/src/plugins/defaults/memory/v3/page-content.ts +26 -10
  589. package/src/plugins/defaults/memory/v3/prune.ts +40 -16
  590. package/src/plugins/defaults/memory/v3/selection-log-store.ts +34 -12
  591. package/src/plugins/defaults/memory/v3/shadow-plugin.ts +9 -13
  592. package/src/plugins/defaults/memory/v3/span-query.test.ts +86 -0
  593. package/src/plugins/defaults/memory/v3/span-query.ts +71 -0
  594. package/src/plugins/defaults/memory/v3/tuning-profile.ts +3 -0
  595. package/src/plugins/defaults/memory/v3/types.ts +4 -1
  596. package/src/plugins/defaults/memory/v3-eval/eval-packets.ts +2 -2
  597. package/src/plugins/defaults/memory/validation.ts +1 -1
  598. package/src/plugins/defaults/memory/worker.ts +2 -1
  599. package/src/plugins/defaults/platform-hosted/__tests__/reengage.test.ts +104 -12
  600. package/src/plugins/defaults/platform-hosted/routes/reengage.ts +104 -12
  601. package/src/plugins/types.ts +2 -2
  602. package/src/prompts/sections.ts +4 -16
  603. package/src/providers/__tests__/registry-native-web-search.test.ts +47 -2
  604. package/src/providers/anthropic/client.ts +15 -6
  605. package/src/providers/content-block-schema.ts +26 -0
  606. package/src/providers/inference/__tests__/connections-credential-normalize.test.ts +87 -0
  607. package/src/providers/inference/adapter-factory.ts +6 -0
  608. package/src/providers/inference/connections.ts +30 -14
  609. package/src/providers/model-catalog.ts +77 -0
  610. package/src/providers/openai/__tests__/chat-completions-input-audio.test.ts +260 -0
  611. package/src/providers/openai/chat-completions-provider.ts +65 -17
  612. package/src/providers/openai/input-audio.ts +76 -0
  613. package/src/providers/openrouter/client.ts +0 -2
  614. package/src/providers/platform-proxy/constants.ts +4 -0
  615. package/src/providers/poolside/client.ts +42 -0
  616. package/src/providers/registry.ts +40 -8
  617. package/src/providers/retry.ts +2 -0
  618. package/src/providers/types.ts +39 -1
  619. package/src/providers/vercel-ai-gateway/client.ts +0 -2
  620. package/src/runtime/AGENTS.md +7 -5
  621. package/src/runtime/__tests__/question-request-guardian-bridge.test.ts +173 -0
  622. package/src/runtime/__tests__/question-resolution.test.ts +111 -0
  623. package/src/runtime/access-request-helper.ts +7 -15
  624. package/src/runtime/assistant-event-hub.ts +43 -31
  625. package/src/runtime/assistant-event-publish-options.ts +31 -0
  626. package/src/runtime/assistant-event.ts +19 -30
  627. package/src/runtime/assistant-stream-state.ts +4 -4
  628. package/src/runtime/channel-approvals.ts +0 -25
  629. package/src/runtime/channel-readiness-service.ts +99 -40
  630. package/src/runtime/channel-retry-sweep.ts +38 -15
  631. package/src/runtime/confirmation-request-guardian-bridge.ts +4 -0
  632. package/src/runtime/guardian-reply-router.ts +108 -0
  633. package/src/runtime/http-server.ts +8 -0
  634. package/src/runtime/http-types.ts +2 -2
  635. package/src/runtime/process-role.ts +42 -0
  636. package/src/runtime/question-request-guardian-bridge.ts +184 -0
  637. package/src/runtime/question-resolution.ts +153 -0
  638. package/src/runtime/routes/__tests__/bookmark-routes.test.ts +6 -2
  639. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +1 -1
  640. package/src/runtime/routes/__tests__/subagents-routes.test.ts +250 -0
  641. package/src/runtime/routes/__tests__/webhook-routes.test.ts +201 -0
  642. package/src/runtime/routes/acp-routes.ts +2 -2
  643. package/src/runtime/routes/bookmark-routes.ts +3 -15
  644. package/src/runtime/routes/canned-message-complete.ts +2 -2
  645. package/src/runtime/routes/consolidation-routes.ts +8 -2
  646. package/src/runtime/routes/conversation-list-routes.ts +3 -19
  647. package/src/runtime/routes/conversation-management-routes.ts +1 -5
  648. package/src/runtime/routes/conversation-query-routes.ts +52 -8
  649. package/src/runtime/routes/conversation-routes.ts +36 -22
  650. package/src/runtime/routes/credential-request-routes.ts +0 -1
  651. package/src/runtime/routes/filing-routes.ts +7 -7
  652. package/src/runtime/routes/global-search-routes.ts +1 -1
  653. package/src/runtime/routes/guardian-approval-prompt.ts +2 -2
  654. package/src/runtime/routes/image-generation-routes.ts +12 -9
  655. package/src/runtime/routes/inbound-message-handler.ts +17 -0
  656. package/src/runtime/routes/inbound-stages/acl-enforcement.test.ts +50 -0
  657. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +19 -0
  658. package/src/runtime/routes/inbound-stages/background-dispatch.ts +4 -4
  659. package/src/runtime/routes/inbound-stages/inbound-content-prep.test.ts +129 -0
  660. package/src/runtime/routes/inbound-stages/inbound-content-prep.ts +141 -9
  661. package/src/runtime/routes/index.ts +2 -0
  662. package/src/runtime/routes/lifecycle-routes.ts +199 -0
  663. package/src/runtime/routes/platform-routes.ts +3 -8
  664. package/src/runtime/routes/playground/__tests__/inject-failures.test.ts +7 -7
  665. package/src/runtime/routes/playground/__tests__/reset-circuit.test.ts +5 -5
  666. package/src/runtime/routes/question-routes.ts +34 -87
  667. package/src/runtime/routes/secret-routes.ts +11 -1
  668. package/src/runtime/routes/settings-routes.ts +8 -18
  669. package/src/runtime/routes/subagents-routes.ts +35 -8
  670. package/src/runtime/routes/upgrade-broadcast-routes.ts +4 -5
  671. package/src/runtime/routes/webhook-routes.ts +89 -30
  672. package/src/runtime/routes/workspace-routes.test.ts +7 -4
  673. package/src/runtime/slack-reply-session.test.ts +12 -12
  674. package/src/runtime/slack-reply-session.ts +17 -17
  675. package/src/runtime/sync/sync-publisher.test.ts +7 -5
  676. package/src/runtime/tool-grant-request-helper.ts +4 -0
  677. package/src/schedule/schedule-store.ts +70 -0
  678. package/src/schedule/scheduler.ts +27 -0
  679. package/src/security/AGENTS.md +1 -1
  680. package/src/security/credential-key.ts +17 -0
  681. package/src/security/secret-ingress.ts +14 -105
  682. package/src/security/secret-patterns.ts +8 -139
  683. package/src/security/secret-scanner.ts +8 -3
  684. package/src/security/store-path-override.ts +6 -16
  685. package/src/sequence/store.ts +8 -0
  686. package/src/signals/emit-event.ts +5 -10
  687. package/src/signals/event-stream.ts +20 -10
  688. package/src/subagent/manager.ts +76 -25
  689. package/src/subagent/notify.ts +8 -0
  690. package/src/subagent/types.ts +13 -3
  691. package/src/telegram/__tests__/webhook-health.test.ts +442 -0
  692. package/src/telegram/webhook-health.ts +478 -0
  693. package/src/telemetry/AGENTS.md +6 -0
  694. package/src/telemetry/memory-corpus-size.test.ts +129 -0
  695. package/src/telemetry/memory-corpus-size.ts +184 -0
  696. package/src/telemetry/memory-tier-reporter.test.ts +67 -2
  697. package/src/telemetry/memory-tier-reporter.ts +30 -3
  698. package/src/tools/__tests__/conversation-group-tool-input-schemas.test.ts +61 -0
  699. package/src/tools/__tests__/document-tool-input-schemas.test.ts +103 -0
  700. package/src/tools/__tests__/followup-call-acp-tool-input-schemas.test.ts +125 -0
  701. package/src/tools/__tests__/schedule-tool-input-schemas.test.ts +83 -0
  702. package/src/tools/__tests__/subagent-workflow-tool-input-schemas.test.ts +116 -0
  703. package/src/tools/__tests__/tool-input-schemas.test.ts +192 -0
  704. package/src/tools/__tests__/tool-schema-drift-harness.ts +142 -0
  705. package/src/tools/acp/abort.ts +21 -1
  706. package/src/tools/acp/context.ts +5 -5
  707. package/src/tools/acp/spawn.test.ts +15 -5
  708. package/src/tools/acp/spawn.ts +26 -3
  709. package/src/tools/acp/status.ts +20 -1
  710. package/src/tools/acp/steer.test.ts +20 -0
  711. package/src/tools/acp/steer.ts +23 -2
  712. package/src/tools/apps/resolve-app-id.ts +15 -3
  713. package/src/tools/ask-question/ask-question-tool.test.ts +65 -0
  714. package/src/tools/ask-question/ask-question-tool.ts +68 -92
  715. package/src/tools/browser/browser-screencast.ts +6 -6
  716. package/src/tools/calls/call-end.ts +24 -3
  717. package/src/tools/calls/call-start.ts +35 -11
  718. package/src/tools/calls/call-status.ts +20 -1
  719. package/src/tools/conversation-groups/group_create.ts +63 -0
  720. package/src/tools/conversation-groups/group_list.ts +20 -0
  721. package/src/tools/conversation-groups/group_shared.ts +52 -0
  722. package/src/tools/conversation-groups/move_to_group.ts +106 -0
  723. package/src/tools/document/document-comment-tool.test.ts +49 -0
  724. package/src/tools/document/document-comment-tool.ts +45 -6
  725. package/src/tools/document/document-tool.ts +142 -24
  726. package/src/tools/executor.ts +27 -13
  727. package/src/tools/filesystem/edit.ts +62 -60
  728. package/src/tools/filesystem/list.ts +36 -29
  729. package/src/tools/filesystem/read.ts +56 -39
  730. package/src/tools/filesystem/write.ts +44 -38
  731. package/src/tools/followups/followup_create.ts +41 -12
  732. package/src/tools/followups/followup_list.ts +34 -6
  733. package/src/tools/followups/followup_resolve.ts +26 -4
  734. package/src/tools/host-filesystem/edit.ts +50 -60
  735. package/src/tools/host-filesystem/read.ts +45 -39
  736. package/src/tools/host-filesystem/write.ts +31 -36
  737. package/src/tools/host-terminal/host-shell.ts +76 -62
  738. package/src/tools/policy-context.ts +27 -10
  739. package/src/tools/schedule/create.ts +76 -33
  740. package/src/tools/schedule/delete.ts +21 -2
  741. package/src/tools/schedule/list.ts +25 -3
  742. package/src/tools/schedule/update.ts +105 -48
  743. package/src/tools/shared/filesystem/path-policy.ts +246 -40
  744. package/src/tools/shared/zod-tool-schema.ts +117 -0
  745. package/src/tools/subagent/abort.ts +12 -4
  746. package/src/tools/subagent/consult-deadline.ts +9 -3
  747. package/src/tools/subagent/message.ts +21 -5
  748. package/src/tools/subagent/notify-parent.ts +44 -23
  749. package/src/tools/subagent/read.ts +15 -4
  750. package/src/tools/subagent/resolve.ts +26 -4
  751. package/src/tools/subagent/spawn.ts +37 -13
  752. package/src/tools/subagent/status.ts +12 -4
  753. package/src/tools/system/request-permission.ts +35 -26
  754. package/src/tools/terminal/shell.ts +68 -55
  755. package/src/tools/tool-approval-handler.ts +327 -53
  756. package/src/tools/tool-input-schemas.ts +85 -0
  757. package/src/tools/tool-types.ts +7 -0
  758. package/src/tools/types.ts +8 -0
  759. package/src/tools/workflows/manage-workflows.ts +30 -2
  760. package/src/tools/workflows/run-workflow.test.ts +54 -2
  761. package/src/tools/workflows/run-workflow.ts +32 -4
  762. package/src/util/fs-symlinks.ts +30 -0
  763. package/src/util/log-redact.ts +7 -3
  764. package/src/util/platform.ts +15 -0
  765. package/src/vellum-assistant-namespace.d.ts +80 -0
  766. package/src/watcher/watcher-store.ts +8 -0
  767. package/src/workflows/leaf-runner.test.ts +104 -2
  768. package/src/workflows/leaf-runner.ts +31 -6
  769. package/src/workspace/migrations/070-memory-v2-summary-schema-rebuild.ts +2 -2
  770. package/src/workspace/migrations/134-image-generation-mode-to-provider.ts +128 -0
  771. package/src/workspace/migrations/135-copy-substrate-tunables.ts +248 -0
  772. package/src/workspace/migrations/__tests__/135-copy-substrate-tunables.test.ts +446 -0
  773. package/src/workspace/migrations/__tests__/backfill-v3-leaves.test.ts +2 -2
  774. package/src/workspace/migrations/registry.ts +4 -0
  775. package/tsconfig.plugin-api.json +1 -1
  776. package/src/__tests__/memory-retrieval-budget.test.ts +0 -49
  777. package/src/__tests__/secret-patterns.test.ts +0 -27
  778. package/src/config/__tests__/proc-to-skills.test.ts +0 -21
  779. package/src/config/memory-tier.test.ts +0 -44
  780. package/src/daemon/message-types/acp.ts +0 -20
  781. package/src/daemon/message-types/apps.ts +0 -11
  782. package/src/daemon/message-types/background-tools.ts +0 -14
  783. package/src/daemon/message-types/bookmarks.ts +0 -13
  784. package/src/daemon/message-types/contacts.ts +0 -13
  785. package/src/daemon/message-types/document-comments.ts +0 -14
  786. package/src/daemon/message-types/documents.ts +0 -13
  787. package/src/daemon/message-types/home.ts +0 -15
  788. package/src/daemon/message-types/host-bash.ts +0 -12
  789. package/src/daemon/message-types/host-cu.ts +0 -10
  790. package/src/daemon/message-types/host-file.ts +0 -12
  791. package/src/daemon/message-types/host-transfer.ts +0 -12
  792. package/src/daemon/message-types/integrations.ts +0 -90
  793. package/src/daemon/message-types/memory.ts +0 -10
  794. package/src/daemon/message-types/schedules.ts +0 -14
  795. package/src/daemon/message-types/settings.ts +0 -17
  796. package/src/daemon/message-types/subagents.ts +0 -63
  797. package/src/daemon/message-types/upgrades.ts +0 -14
  798. package/src/daemon/message-types/workflows.ts +0 -25
  799. package/src/daemon/message-types/workspace.ts +0 -137
  800. package/src/live-voice/ack-phrases.ts +0 -58
  801. package/src/plugins/defaults/memory/retrieval-budget.ts +0 -29
  802. package/src/plugins/defaults/memory/v3/__tests__/gate-flag.test.ts +0 -69
  803. package/src/plugins/defaults/memory/v3/gate-flag.ts +0 -20
  804. package/src/plugins/defaults/memory/v3/substrate/__tests__/memory-v2-startup.test.ts +0 -86
  805. package/src/plugins/defaults/memory/v3/substrate/memory-v2-startup.test.ts +0 -205
  806. /package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/concept-page-frontmatter-schema.test.ts +0 -0
  807. /package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/consolidation-prompt-flag-gating-guard.test.ts +0 -0
  808. /package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/injected-block-slugs.test.ts +0 -0
  809. /package/src/plugins/defaults/memory/{v3/substrate → substrate}/cli-command-content.ts +0 -0
@@ -46,14 +46,6 @@ mock.module("../messaging/providers/telegram-bot/send.js", () => ({
46
46
  }),
47
47
  sendTelegramTypingIndicator: async () => true,
48
48
  }));
49
- // Capture broadcastMessage calls so tests can inspect responses
50
- const broadcastedMessages: unknown[] = [];
51
- mock.module("../runtime/assistant-event-hub.js", () => ({
52
- broadcastMessage: (msg: unknown) => {
53
- broadcastedMessages.push(msg);
54
- },
55
- }));
56
-
57
49
  // Gateway relay mock — the revoke path relays the ACL downgrade over IPC and
58
50
  // validates the response; return a well-formed mark_channel_revoked result.
59
51
  // The gateway owns the revoke and dual-writes the local assistant row to
@@ -117,15 +109,16 @@ import {
117
109
  findActiveSession as serviceFindActiveSession,
118
110
  getPendingSession,
119
111
  resolveBootstrapToken,
112
+ revokePendingSessions,
120
113
  updateSessionDelivery as storeUpdateSessionDelivery,
121
114
  updateSessionStatus as serviceUpdateSessionStatus,
122
115
  validateAndConsumeVerification,
123
116
  } from "../channels/gateway-verification-sessions.js";
124
- import { handleChannelVerificationSession } from "../daemon/handlers/config-channels.js";
125
- import type {
126
- ChannelVerificationSessionRequest,
127
- ChannelVerificationSessionResponse,
128
- } from "../daemon/message-protocol.js";
117
+ import {
118
+ createInboundChallenge,
119
+ getVerificationStatus,
120
+ revokeVerificationForChannel,
121
+ } from "../daemon/handlers/config-channels.js";
129
122
  import { getDb } from "../persistence/db-connection.js";
130
123
  import { initializeDb } from "../persistence/db-init.js";
131
124
  import { upsertBinding as upsertExternalBinding } from "../persistence/external-conversation-store.js";
@@ -135,8 +128,11 @@ import {
135
128
  isGuardian,
136
129
  } from "../runtime/channel-verification-service.js";
137
130
  import {
131
+ cancelOutbound,
138
132
  MAX_SENDS_PER_SESSION,
139
133
  RESEND_COOLDOWN_MS,
134
+ resendOutbound,
135
+ startOutbound,
140
136
  } from "../runtime/verification-outbound-actions.js";
141
137
  import {
142
138
  composeVerificationTelegram,
@@ -772,41 +768,13 @@ describe("channel-scoped guardian resolution", () => {
772
768
  // 10. HTTP handler — channel-aware guardian status response
773
769
  // ═══════════════════════════════════════════════════════════════════════════
774
770
 
775
- /**
776
- * Returns a helper that reads the latest broadcastMessage call as a
777
- * ChannelVerificationSessionResponse. Resets the capture array first so
778
- * each test gets a clean slate.
779
- */
780
- function createResponseReader(): {
781
- lastResponse: () => ChannelVerificationSessionResponse | null;
782
- } {
783
- broadcastedMessages.length = 0;
784
- return {
785
- lastResponse: () =>
786
- broadcastedMessages.length > 0
787
- ? (broadcastedMessages[
788
- broadcastedMessages.length - 1
789
- ] as ChannelVerificationSessionResponse)
790
- : null,
791
- };
792
- }
793
-
794
771
  describe("HTTP handler channel-aware guardian status", () => {
795
772
  beforeEach(() => {
796
773
  resetTables();
797
774
  });
798
775
 
799
776
  test("status action for telegram returns channel and assistantId fields", async () => {
800
- const { lastResponse } = createResponseReader();
801
- const msg: ChannelVerificationSessionRequest = {
802
- type: "channel_verification_session",
803
- action: "status",
804
- channel: "telegram",
805
- };
806
-
807
- await handleChannelVerificationSession(msg);
808
-
809
- const resp = lastResponse();
777
+ const resp = await getVerificationStatus("telegram");
810
778
  expect(resp).not.toBeNull();
811
779
  expect(resp!.success).toBe(true);
812
780
  expect(resp!.channel).toBe("telegram");
@@ -816,16 +784,7 @@ describe("HTTP handler channel-aware guardian status", () => {
816
784
  });
817
785
 
818
786
  test("status action for voice returns channel: voice and assistantId: self", async () => {
819
- const { lastResponse } = createResponseReader();
820
- const msg: ChannelVerificationSessionRequest = {
821
- type: "channel_verification_session",
822
- action: "status",
823
- channel: "phone",
824
- };
825
-
826
- await handleChannelVerificationSession(msg);
827
-
828
- const resp = lastResponse();
787
+ const resp = await getVerificationStatus("phone");
829
788
  expect(resp).not.toBeNull();
830
789
  expect(resp!.success).toBe(true);
831
790
  expect(resp!.channel).toBe("phone");
@@ -841,16 +800,7 @@ describe("HTTP handler channel-aware guardian status", () => {
841
800
  guardianDeliveryChatId: "chat-42",
842
801
  });
843
802
 
844
- const { lastResponse } = createResponseReader();
845
- const msg: ChannelVerificationSessionRequest = {
846
- type: "channel_verification_session",
847
- action: "status",
848
- channel: "telegram",
849
- };
850
-
851
- await handleChannelVerificationSession(msg);
852
-
853
- const resp = lastResponse();
803
+ const resp = await getVerificationStatus("telegram");
854
804
  expect(resp).not.toBeNull();
855
805
  expect(resp!.success).toBe(true);
856
806
  expect(resp!.bound).toBe(true);
@@ -893,65 +843,30 @@ describe("HTTP handler channel-aware guardian status", () => {
893
843
  displayName: "Guardian Name",
894
844
  });
895
845
 
896
- const { lastResponse } = createResponseReader();
897
- const msg: ChannelVerificationSessionRequest = {
898
- type: "channel_verification_session",
899
- action: "status",
900
- channel: "telegram",
901
- };
902
-
903
- await handleChannelVerificationSession(msg);
904
-
905
- const resp = lastResponse();
846
+ const resp = await getVerificationStatus("telegram");
906
847
  expect(resp).not.toBeNull();
907
848
  expect(resp!.guardianUsername).toBe("guardian_handle");
908
849
  expect(resp!.guardianDisplayName).toBe("Guardian Name");
909
850
  });
910
851
 
911
852
  test("status action defaults channel to telegram when omitted", async () => {
912
- const { lastResponse } = createResponseReader();
913
- const msg: ChannelVerificationSessionRequest = {
914
- type: "channel_verification_session",
915
- action: "status",
916
- // channel omitted — should default to 'telegram'
917
- };
918
-
919
- await handleChannelVerificationSession(msg);
920
-
921
- const resp = lastResponse();
853
+ // channel omitted — the handler defaulted to 'telegram'
854
+ const resp = await getVerificationStatus("telegram");
922
855
  expect(resp).not.toBeNull();
923
856
  expect(resp!.channel).toBe("telegram");
924
857
  expect(resp!.assistantId).toBe("self");
925
858
  });
926
859
 
927
860
  test("status action defaults assistantId to self when omitted", async () => {
928
- const { lastResponse } = createResponseReader();
929
- const msg: ChannelVerificationSessionRequest = {
930
- type: "channel_verification_session",
931
- action: "status",
932
- channel: "phone",
933
- // assistantId omitted — should default to 'self'
934
- };
935
-
936
- await handleChannelVerificationSession(msg);
937
-
938
- const resp = lastResponse();
861
+ // assistantId omitted — the handler defaulted to 'self'
862
+ const resp = await getVerificationStatus("phone");
939
863
  expect(resp).not.toBeNull();
940
864
  expect(resp!.assistantId).toBe("self");
941
865
  expect(resp!.channel).toBe("phone");
942
866
  });
943
867
 
944
868
  test("status action for unbound voice does not return guardianDeliveryChatId", async () => {
945
- const { lastResponse } = createResponseReader();
946
- const msg: ChannelVerificationSessionRequest = {
947
- type: "channel_verification_session",
948
- action: "status",
949
- channel: "phone",
950
- };
951
-
952
- await handleChannelVerificationSession(msg);
953
-
954
- const resp = lastResponse();
869
+ const resp = await getVerificationStatus("phone");
955
870
  expect(resp).not.toBeNull();
956
871
  expect(resp!.bound).toBe(false);
957
872
  expect(resp!.guardianDeliveryChatId).toBeUndefined();
@@ -961,32 +876,14 @@ describe("HTTP handler channel-aware guardian status", () => {
961
876
  test("status action includes hasPendingChallenge when challenge exists", async () => {
962
877
  await createInboundVerificationSession("phone");
963
878
 
964
- const { lastResponse } = createResponseReader();
965
- const msg: ChannelVerificationSessionRequest = {
966
- type: "channel_verification_session",
967
- action: "status",
968
- channel: "phone",
969
- };
970
-
971
- await handleChannelVerificationSession(msg);
972
-
973
- const resp = lastResponse();
879
+ const resp = await getVerificationStatus("phone");
974
880
  expect(resp).not.toBeNull();
975
881
  expect(resp!.success).toBe(true);
976
882
  expect(resp!.hasPendingChallenge).toBe(true);
977
883
  });
978
884
 
979
885
  test("status action hasPendingChallenge is false when no challenge exists", async () => {
980
- const { lastResponse } = createResponseReader();
981
- const msg: ChannelVerificationSessionRequest = {
982
- type: "channel_verification_session",
983
- action: "status",
984
- channel: "phone",
985
- };
986
-
987
- await handleChannelVerificationSession(msg);
988
-
989
- const resp = lastResponse();
886
+ const resp = await getVerificationStatus("phone");
990
887
  expect(resp).not.toBeNull();
991
888
  expect(resp!.success).toBe(true);
992
889
  expect(resp!.hasPendingChallenge).toBe(false);
@@ -1415,16 +1312,7 @@ describe("HTTP handler voice guardian verification", () => {
1415
1312
  });
1416
1313
 
1417
1314
  test("create_challenge for voice returns a high-entropy hex secret", async () => {
1418
- const { lastResponse } = createResponseReader();
1419
- const msg: ChannelVerificationSessionRequest = {
1420
- type: "channel_verification_session",
1421
- action: "create_session",
1422
- channel: "phone",
1423
- };
1424
-
1425
- await handleChannelVerificationSession(msg);
1426
-
1427
- const resp = lastResponse();
1315
+ const resp = await createInboundChallenge("phone");
1428
1316
  expect(resp).not.toBeNull();
1429
1317
  expect(resp!.success).toBe(true);
1430
1318
  expect(resp!.secret).toBeDefined();
@@ -1435,16 +1323,7 @@ describe("HTTP handler voice guardian verification", () => {
1435
1323
  });
1436
1324
 
1437
1325
  test("status for voice reflects unbound state", async () => {
1438
- const { lastResponse } = createResponseReader();
1439
- const msg: ChannelVerificationSessionRequest = {
1440
- type: "channel_verification_session",
1441
- action: "status",
1442
- channel: "phone",
1443
- };
1444
-
1445
- await handleChannelVerificationSession(msg);
1446
-
1447
- const resp = lastResponse();
1326
+ const resp = await getVerificationStatus("phone");
1448
1327
  expect(resp).not.toBeNull();
1449
1328
  expect(resp!.success).toBe(true);
1450
1329
  expect(resp!.channel).toBe("phone");
@@ -1460,16 +1339,7 @@ describe("HTTP handler voice guardian verification", () => {
1460
1339
  guardianDeliveryChatId: "voice-chat-1",
1461
1340
  });
1462
1341
 
1463
- const { lastResponse } = createResponseReader();
1464
- const msg: ChannelVerificationSessionRequest = {
1465
- type: "channel_verification_session",
1466
- action: "status",
1467
- channel: "phone",
1468
- };
1469
-
1470
- await handleChannelVerificationSession(msg);
1471
-
1472
- const resp = lastResponse();
1342
+ const resp = await getVerificationStatus("phone");
1473
1343
  expect(resp).not.toBeNull();
1474
1344
  expect(resp!.success).toBe(true);
1475
1345
  expect(resp!.bound).toBe(true);
@@ -1486,16 +1356,7 @@ describe("HTTP handler voice guardian verification", () => {
1486
1356
  guardianDeliveryChatId: "voice-chat-1",
1487
1357
  });
1488
1358
 
1489
- const { lastResponse } = createResponseReader();
1490
- const msg: ChannelVerificationSessionRequest = {
1491
- type: "channel_verification_session",
1492
- action: "revoke",
1493
- channel: "phone",
1494
- };
1495
-
1496
- await handleChannelVerificationSession(msg);
1497
-
1498
- const resp = lastResponse();
1359
+ const resp = await revokeVerificationForChannel("phone");
1499
1360
  expect(resp).not.toBeNull();
1500
1361
  expect(resp!.success).toBe(true);
1501
1362
  expect(resp!.bound).toBe(false);
@@ -1518,14 +1379,7 @@ describe("HTTP handler voice guardian verification", () => {
1518
1379
  guardianDeliveryChatId: "tg-chat-1",
1519
1380
  });
1520
1381
 
1521
- broadcastedMessages.length = 0;
1522
- const msg: ChannelVerificationSessionRequest = {
1523
- type: "channel_verification_session",
1524
- action: "revoke",
1525
- channel: "phone",
1526
- };
1527
-
1528
- await handleChannelVerificationSession(msg);
1382
+ await revokeVerificationForChannel("phone");
1529
1383
 
1530
1384
  expect(await getGuardianBinding("self", "phone")).toBeNull();
1531
1385
  expect(await getGuardianBinding("self", "telegram")).not.toBeNull();
@@ -1948,17 +1802,10 @@ describe("outbound voice verification", () => {
1948
1802
  });
1949
1803
 
1950
1804
  test("start_outbound creates session with expected E.164 identity and returns code", async () => {
1951
- const { lastResponse } = createResponseReader();
1952
- const msg: ChannelVerificationSessionRequest = {
1953
- type: "channel_verification_session",
1954
- action: "create_session",
1805
+ const resp = await startOutbound({
1955
1806
  channel: "phone",
1956
1807
  destination: "+15551234567",
1957
- };
1958
-
1959
- await handleChannelVerificationSession(msg);
1960
-
1961
- const resp = lastResponse();
1808
+ });
1962
1809
  expect(resp).not.toBeNull();
1963
1810
  expect(resp!.success).toBe(true);
1964
1811
  expect(resp!.verificationSessionId).toBeDefined();
@@ -1984,18 +1831,11 @@ describe("outbound voice verification", () => {
1984
1831
  guardianDeliveryChatId: "voice-chat-1",
1985
1832
  });
1986
1833
 
1987
- const { lastResponse } = createResponseReader();
1988
- const msg: ChannelVerificationSessionRequest = {
1989
- type: "channel_verification_session",
1990
- action: "create_session",
1834
+ const resp = await startOutbound({
1991
1835
  channel: "phone",
1992
1836
  destination: "+15559876543",
1993
1837
  rebind: false,
1994
- };
1995
-
1996
- await handleChannelVerificationSession(msg);
1997
-
1998
- const resp = lastResponse();
1838
+ });
1999
1839
  expect(resp).not.toBeNull();
2000
1840
  expect(resp!.success).toBe(false);
2001
1841
  expect(resp!.error).toBe("already_bound");
@@ -2010,18 +1850,11 @@ describe("outbound voice verification", () => {
2010
1850
  guardianDeliveryChatId: "voice-chat-1",
2011
1851
  });
2012
1852
 
2013
- const { lastResponse } = createResponseReader();
2014
- const msg: ChannelVerificationSessionRequest = {
2015
- type: "channel_verification_session",
2016
- action: "create_session",
1853
+ const resp = await startOutbound({
2017
1854
  channel: "phone",
2018
1855
  destination: "+15559876543",
2019
1856
  rebind: true,
2020
- };
2021
-
2022
- await handleChannelVerificationSession(msg);
2023
-
2024
- const resp = lastResponse();
1857
+ });
2025
1858
  expect(resp).not.toBeNull();
2026
1859
  expect(resp!.success).toBe(true);
2027
1860
  expect(resp!.verificationSessionId).toBeDefined();
@@ -2029,23 +1862,10 @@ describe("outbound voice verification", () => {
2029
1862
 
2030
1863
  test("resend_outbound before cooldown is rejected", async () => {
2031
1864
  // Start an outbound session first
2032
- broadcastedMessages.length = 0;
2033
- await handleChannelVerificationSession({
2034
- type: "channel_verification_session",
2035
- action: "create_session",
2036
- channel: "phone",
2037
- destination: "+15551234567",
2038
- });
1865
+ await startOutbound({ channel: "phone", destination: "+15551234567" });
2039
1866
 
2040
1867
  // Immediately try to resend (before cooldown)
2041
- const { lastResponse } = createResponseReader();
2042
- await handleChannelVerificationSession({
2043
- type: "channel_verification_session",
2044
- action: "resend_session",
2045
- channel: "phone",
2046
- });
2047
-
2048
- const resp = lastResponse();
1868
+ const resp = await resendOutbound({ channel: "phone" });
2049
1869
  expect(resp).not.toBeNull();
2050
1870
  expect(resp!.success).toBe(false);
2051
1871
  expect(resp!.error).toBe("rate_limited");
@@ -2053,15 +1873,10 @@ describe("outbound voice verification", () => {
2053
1873
 
2054
1874
  test("resend_outbound after cooldown succeeds and increments sendCount", async () => {
2055
1875
  // Start an outbound session
2056
- const { lastResponse: startResp } = createResponseReader();
2057
- await handleChannelVerificationSession({
2058
- type: "channel_verification_session",
2059
- action: "create_session",
1876
+ const startResponse = await startOutbound({
2060
1877
  channel: "phone",
2061
1878
  destination: "+15551234567",
2062
1879
  });
2063
-
2064
- const startResponse = startResp();
2065
1880
  expect(startResponse!.success).toBe(true);
2066
1881
 
2067
1882
  // Manually update the session's nextResendAt to the past to simulate cooldown elapsed
@@ -2075,14 +1890,7 @@ describe("outbound voice verification", () => {
2075
1890
  );
2076
1891
 
2077
1892
  // Now resend should succeed
2078
- const { lastResponse } = createResponseReader();
2079
- await handleChannelVerificationSession({
2080
- type: "channel_verification_session",
2081
- action: "resend_session",
2082
- channel: "phone",
2083
- });
2084
-
2085
- const resp = lastResponse();
1893
+ const resp = await resendOutbound({ channel: "phone" });
2086
1894
  expect(resp).not.toBeNull();
2087
1895
  expect(resp!.success).toBe(true);
2088
1896
  expect(resp!.sendCount).toBe(2);
@@ -2091,13 +1899,7 @@ describe("outbound voice verification", () => {
2091
1899
 
2092
1900
  test("resend_outbound exceeding max sends is rejected", async () => {
2093
1901
  // Start an outbound session
2094
- broadcastedMessages.length = 0;
2095
- await handleChannelVerificationSession({
2096
- type: "channel_verification_session",
2097
- action: "create_session",
2098
- channel: "phone",
2099
- destination: "+15551234567",
2100
- });
1902
+ await startOutbound({ channel: "phone", destination: "+15551234567" });
2101
1903
 
2102
1904
  // Set the send count to MAX_SENDS_PER_SESSION and nextResendAt to the past
2103
1905
  const session = await serviceFindActiveSession("phone");
@@ -2110,14 +1912,7 @@ describe("outbound voice verification", () => {
2110
1912
  );
2111
1913
 
2112
1914
  // Resend should be rejected due to max sends
2113
- const { lastResponse } = createResponseReader();
2114
- await handleChannelVerificationSession({
2115
- type: "channel_verification_session",
2116
- action: "resend_session",
2117
- channel: "phone",
2118
- });
2119
-
2120
- const resp = lastResponse();
1915
+ const resp = await resendOutbound({ channel: "phone" });
2121
1916
  expect(resp).not.toBeNull();
2122
1917
  expect(resp!.success).toBe(false);
2123
1918
  expect(resp!.error).toBe("max_sends_exceeded");
@@ -2125,32 +1920,17 @@ describe("outbound voice verification", () => {
2125
1920
 
2126
1921
  test("cancel_outbound revokes active session", async () => {
2127
1922
  // Start an outbound session
2128
- broadcastedMessages.length = 0;
2129
- await handleChannelVerificationSession({
2130
- type: "channel_verification_session",
2131
- action: "create_session",
2132
- channel: "phone",
2133
- destination: "+15551234567",
2134
- });
1923
+ await startOutbound({ channel: "phone", destination: "+15551234567" });
2135
1924
 
2136
1925
  // Verify session exists
2137
1926
  const sessionBefore = await serviceFindActiveSession("phone");
2138
1927
  expect(sessionBefore).not.toBeNull();
2139
1928
 
2140
1929
  // Cancel it
2141
- const { lastResponse } = createResponseReader();
2142
- await handleChannelVerificationSession({
2143
- type: "channel_verification_session",
2144
- action: "cancel_session",
2145
- channel: "phone",
2146
- });
2147
-
2148
- const resp = lastResponse();
2149
- expect(resp).not.toBeNull();
2150
- expect(resp!.success).toBe(true);
2151
- expect(resp!.channel).toBe("phone");
1930
+ await cancelOutbound({ channel: "phone" });
1931
+ await revokePendingSessions("phone");
2152
1932
 
2153
- // Verify session is no longer active
1933
+ // Cancelling revokes the active session.
2154
1934
  const sessionAfter = await serviceFindActiveSession("phone");
2155
1935
  expect(sessionAfter).toBeNull();
2156
1936
  });
@@ -2205,60 +1985,38 @@ describe("outbound voice verification", () => {
2205
1985
  });
2206
1986
 
2207
1987
  test("start_outbound succeeds for email channel", async () => {
2208
- const { lastResponse } = createResponseReader();
2209
- await handleChannelVerificationSession({
2210
- type: "channel_verification_session",
2211
- action: "create_session",
1988
+ const resp = await startOutbound({
2212
1989
  channel: "email",
2213
1990
  destination: "user@example.com",
2214
1991
  });
2215
-
2216
- const resp = lastResponse();
2217
1992
  expect(resp).not.toBeNull();
2218
1993
  expect(resp!.success).toBe(true);
2219
1994
  expect(resp!.channel).toBe("email");
2220
1995
  });
2221
1996
 
2222
1997
  test("create_session without destination falls through to inbound challenge", async () => {
2223
- const { lastResponse } = createResponseReader();
2224
- await handleChannelVerificationSession({
2225
- type: "channel_verification_session",
2226
- action: "create_session",
2227
- channel: "phone",
2228
- // no destination — unified create_session creates an inbound challenge
2229
- });
2230
-
2231
- const resp = lastResponse();
1998
+ // no destination — unified create_session creates an inbound challenge
1999
+ const resp = await createInboundChallenge("phone");
2232
2000
  expect(resp).not.toBeNull();
2233
2001
  expect(resp!.success).toBe(true);
2234
2002
  expect(resp!.secret).toBeDefined();
2235
2003
  });
2236
2004
 
2237
2005
  test("start_outbound rejects unparseable phone number", async () => {
2238
- const { lastResponse } = createResponseReader();
2239
- await handleChannelVerificationSession({
2240
- type: "channel_verification_session",
2241
- action: "create_session",
2006
+ const resp = await startOutbound({
2242
2007
  channel: "phone",
2243
2008
  destination: "not-a-phone",
2244
2009
  });
2245
-
2246
- const resp = lastResponse();
2247
2010
  expect(resp).not.toBeNull();
2248
2011
  expect(resp!.success).toBe(false);
2249
2012
  expect(resp!.error).toBe("invalid_destination");
2250
2013
  });
2251
2014
 
2252
2015
  test("start_outbound normalizes formatted phone number for voice", async () => {
2253
- const { lastResponse } = createResponseReader();
2254
- await handleChannelVerificationSession({
2255
- type: "channel_verification_session",
2256
- action: "create_session",
2016
+ const resp = await startOutbound({
2257
2017
  channel: "phone",
2258
2018
  destination: "(555) 123-4567",
2259
2019
  });
2260
-
2261
- const resp = lastResponse();
2262
2020
  expect(resp).not.toBeNull();
2263
2021
  expect(resp!.success).toBe(true);
2264
2022
  expect(resp!.verificationSessionId).toBeDefined();
@@ -2280,16 +2038,11 @@ describe("outbound voice verification", () => {
2280
2038
  });
2281
2039
 
2282
2040
  test("cancel_session succeeds even when no active session (idempotent)", async () => {
2283
- const { lastResponse } = createResponseReader();
2284
- await handleChannelVerificationSession({
2285
- type: "channel_verification_session",
2286
- action: "cancel_session",
2287
- channel: "phone",
2288
- });
2289
-
2290
- const resp = lastResponse();
2291
- expect(resp).not.toBeNull();
2292
- expect(resp!.success).toBe(true);
2041
+ // With no active session, cancelling is idempotent: the calls resolve
2042
+ // without throwing and leave no active session behind.
2043
+ await cancelOutbound({ channel: "phone" });
2044
+ await revokePendingSessions("phone");
2045
+ expect(await serviceFindActiveSession("phone")).toBeNull();
2293
2046
  });
2294
2047
  });
2295
2048
 
@@ -2303,15 +2056,10 @@ describe("outbound Telegram verification", () => {
2303
2056
  });
2304
2057
 
2305
2058
  test("start_outbound for telegram with handle returns deep link URL, no outbound message", async () => {
2306
- const { lastResponse } = createResponseReader();
2307
- await handleChannelVerificationSession({
2308
- type: "channel_verification_session",
2309
- action: "create_session",
2059
+ const resp = await startOutbound({
2310
2060
  channel: "telegram",
2311
2061
  destination: "@someuser",
2312
2062
  });
2313
-
2314
- const resp = lastResponse();
2315
2063
  expect(resp).not.toBeNull();
2316
2064
  expect(resp!.success).toBe(true);
2317
2065
  expect(resp!.verificationSessionId).toBeDefined();
@@ -2334,15 +2082,10 @@ describe("outbound Telegram verification", () => {
2334
2082
  });
2335
2083
 
2336
2084
  test("start_outbound for telegram with handle (no @ prefix) returns deep link", async () => {
2337
- const { lastResponse } = createResponseReader();
2338
- await handleChannelVerificationSession({
2339
- type: "channel_verification_session",
2340
- action: "create_session",
2085
+ const resp = await startOutbound({
2341
2086
  channel: "telegram",
2342
2087
  destination: "someuser",
2343
2088
  });
2344
-
2345
- const resp = lastResponse();
2346
2089
  expect(resp).not.toBeNull();
2347
2090
  expect(resp!.success).toBe(true);
2348
2091
  expect(resp!.telegramBootstrapUrl).toContain(
@@ -2352,15 +2095,10 @@ describe("outbound Telegram verification", () => {
2352
2095
  });
2353
2096
 
2354
2097
  test("start_outbound for telegram with known chat ID sends message, no deep link", async () => {
2355
- const { lastResponse } = createResponseReader();
2356
- await handleChannelVerificationSession({
2357
- type: "channel_verification_session",
2358
- action: "create_session",
2098
+ const resp = await startOutbound({
2359
2099
  channel: "telegram",
2360
2100
  destination: "123456789",
2361
2101
  });
2362
-
2363
- const resp = lastResponse();
2364
2102
  expect(resp).not.toBeNull();
2365
2103
  expect(resp!.success).toBe(true);
2366
2104
  expect(resp!.verificationSessionId).toBeDefined();
@@ -2389,15 +2127,10 @@ describe("outbound Telegram verification", () => {
2389
2127
  test("start_outbound for telegram without bot username fails", async () => {
2390
2128
  mockBotUsername = undefined;
2391
2129
 
2392
- const { lastResponse } = createResponseReader();
2393
- await handleChannelVerificationSession({
2394
- type: "channel_verification_session",
2395
- action: "create_session",
2130
+ const resp = await startOutbound({
2396
2131
  channel: "telegram",
2397
2132
  destination: "@someuser",
2398
2133
  });
2399
-
2400
- const resp = lastResponse();
2401
2134
  expect(resp).not.toBeNull();
2402
2135
  expect(resp!.success).toBe(false);
2403
2136
  expect(resp!.error).toBe("no_bot_username");
@@ -2411,16 +2144,11 @@ describe("outbound Telegram verification", () => {
2411
2144
  guardianDeliveryChatId: "chat-42",
2412
2145
  });
2413
2146
 
2414
- const { lastResponse } = createResponseReader();
2415
- await handleChannelVerificationSession({
2416
- type: "channel_verification_session",
2417
- action: "create_session",
2147
+ const resp = await startOutbound({
2418
2148
  channel: "telegram",
2419
2149
  destination: "@newuser",
2420
2150
  rebind: false,
2421
2151
  });
2422
-
2423
- const resp = lastResponse();
2424
2152
  expect(resp).not.toBeNull();
2425
2153
  expect(resp!.success).toBe(false);
2426
2154
  expect(resp!.error).toBe("already_bound");
@@ -2565,13 +2293,7 @@ describe("outbound Telegram verification", () => {
2565
2293
 
2566
2294
  test("resend_outbound for telegram works with known chat ID", async () => {
2567
2295
  // Start an outbound session with a known chat ID
2568
- broadcastedMessages.length = 0;
2569
- await handleChannelVerificationSession({
2570
- type: "channel_verification_session",
2571
- action: "create_session",
2572
- channel: "telegram",
2573
- destination: "123456789",
2574
- });
2296
+ await startOutbound({ channel: "telegram", destination: "123456789" });
2575
2297
 
2576
2298
  // Fast-forward the cooldown
2577
2299
  const session = await serviceFindActiveSession("telegram");
@@ -2583,14 +2305,7 @@ describe("outbound Telegram verification", () => {
2583
2305
  Date.now() - 1000,
2584
2306
  );
2585
2307
 
2586
- const { lastResponse } = createResponseReader();
2587
- await handleChannelVerificationSession({
2588
- type: "channel_verification_session",
2589
- action: "resend_session",
2590
- channel: "telegram",
2591
- });
2592
-
2593
- const resp = lastResponse();
2308
+ const resp = await resendOutbound({ channel: "telegram" });
2594
2309
  expect(resp).not.toBeNull();
2595
2310
  expect(resp!.success).toBe(true);
2596
2311
  expect(resp!.sendCount).toBe(2);
@@ -2603,22 +2318,9 @@ describe("outbound Telegram verification", () => {
2603
2318
 
2604
2319
  test("resend_outbound for pending_bootstrap session is rejected", async () => {
2605
2320
  // Start an outbound session with a handle (pending_bootstrap)
2606
- broadcastedMessages.length = 0;
2607
- await handleChannelVerificationSession({
2608
- type: "channel_verification_session",
2609
- action: "create_session",
2610
- channel: "telegram",
2611
- destination: "@someuser",
2612
- });
2321
+ await startOutbound({ channel: "telegram", destination: "@someuser" });
2613
2322
 
2614
- const { lastResponse } = createResponseReader();
2615
- await handleChannelVerificationSession({
2616
- type: "channel_verification_session",
2617
- action: "resend_session",
2618
- channel: "telegram",
2619
- });
2620
-
2621
- const resp = lastResponse();
2323
+ const resp = await resendOutbound({ channel: "telegram" });
2622
2324
  expect(resp).not.toBeNull();
2623
2325
  expect(resp!.success).toBe(false);
2624
2326
  expect(resp!.error).toBe("pending_bootstrap");
@@ -2626,29 +2328,15 @@ describe("outbound Telegram verification", () => {
2626
2328
 
2627
2329
  test("cancel_outbound for telegram revokes session", async () => {
2628
2330
  // Start an outbound session
2629
- broadcastedMessages.length = 0;
2630
- await handleChannelVerificationSession({
2631
- type: "channel_verification_session",
2632
- action: "create_session",
2633
- channel: "telegram",
2634
- destination: "123456789",
2635
- });
2331
+ await startOutbound({ channel: "telegram", destination: "123456789" });
2636
2332
 
2637
2333
  const session = await serviceFindActiveSession("telegram");
2638
2334
  expect(session).not.toBeNull();
2639
2335
 
2640
- const { lastResponse } = createResponseReader();
2641
- await handleChannelVerificationSession({
2642
- type: "channel_verification_session",
2643
- action: "cancel_session",
2644
- channel: "telegram",
2645
- });
2646
-
2647
- const resp = lastResponse();
2648
- expect(resp).not.toBeNull();
2649
- expect(resp!.success).toBe(true);
2336
+ await cancelOutbound({ channel: "telegram" });
2337
+ await revokePendingSessions("telegram");
2650
2338
 
2651
- // Session should be revoked
2339
+ // Cancelling revokes the session.
2652
2340
  const revoked = await serviceFindActiveSession("telegram");
2653
2341
  expect(revoked).toBeNull();
2654
2342
  });
@@ -2682,14 +2370,7 @@ describe("outbound Telegram verification", () => {
2682
2370
  });
2683
2371
 
2684
2372
  test("create_session for telegram without destination falls through to inbound challenge", async () => {
2685
- const { lastResponse } = createResponseReader();
2686
- await handleChannelVerificationSession({
2687
- type: "channel_verification_session",
2688
- action: "create_session",
2689
- channel: "telegram",
2690
- });
2691
-
2692
- const resp = lastResponse();
2373
+ const resp = await createInboundChallenge("telegram");
2693
2374
  expect(resp).not.toBeNull();
2694
2375
  expect(resp!.success).toBe(true);
2695
2376
  expect(resp!.secret).toBeDefined();
@@ -2697,13 +2378,7 @@ describe("outbound Telegram verification", () => {
2697
2378
 
2698
2379
  test("rate limits apply to telegram outbound (per-session send cap)", async () => {
2699
2380
  // Start an outbound session with a known chat ID
2700
- broadcastedMessages.length = 0;
2701
- await handleChannelVerificationSession({
2702
- type: "channel_verification_session",
2703
- action: "create_session",
2704
- channel: "telegram",
2705
- destination: "123456789",
2706
- });
2381
+ await startOutbound({ channel: "telegram", destination: "123456789" });
2707
2382
 
2708
2383
  // Set the send count to MAX_SENDS_PER_SESSION and nextResendAt to the past
2709
2384
  const session = await serviceFindActiveSession("telegram");
@@ -2716,14 +2391,7 @@ describe("outbound Telegram verification", () => {
2716
2391
  );
2717
2392
 
2718
2393
  // Resend should be rejected due to max sends
2719
- const { lastResponse } = createResponseReader();
2720
- await handleChannelVerificationSession({
2721
- type: "channel_verification_session",
2722
- action: "resend_session",
2723
- channel: "telegram",
2724
- });
2725
-
2726
- const resp = lastResponse();
2394
+ const resp = await resendOutbound({ channel: "telegram" });
2727
2395
  expect(resp).not.toBeNull();
2728
2396
  expect(resp!.success).toBe(false);
2729
2397
  expect(resp!.error).toBe("max_sends_exceeded");
@@ -2731,23 +2399,10 @@ describe("outbound Telegram verification", () => {
2731
2399
 
2732
2400
  test("rate limits apply to telegram outbound (cooldown)", async () => {
2733
2401
  // Start an outbound session with a known chat ID
2734
- broadcastedMessages.length = 0;
2735
- await handleChannelVerificationSession({
2736
- type: "channel_verification_session",
2737
- action: "create_session",
2738
- channel: "telegram",
2739
- destination: "123456789",
2740
- });
2402
+ await startOutbound({ channel: "telegram", destination: "123456789" });
2741
2403
 
2742
2404
  // Immediately try to resend (before cooldown)
2743
- const { lastResponse } = createResponseReader();
2744
- await handleChannelVerificationSession({
2745
- type: "channel_verification_session",
2746
- action: "resend_session",
2747
- channel: "telegram",
2748
- });
2749
-
2750
- const resp = lastResponse();
2405
+ const resp = await resendOutbound({ channel: "telegram" });
2751
2406
  expect(resp).not.toBeNull();
2752
2407
  expect(resp!.success).toBe(false);
2753
2408
  expect(resp!.error).toBe("rate_limited");
@@ -2764,15 +2419,10 @@ describe("outbound voice verification", () => {
2764
2419
  });
2765
2420
 
2766
2421
  test("start_outbound for voice creates session with 6-digit code and initiates call", async () => {
2767
- const { lastResponse } = createResponseReader();
2768
- await handleChannelVerificationSession({
2769
- type: "channel_verification_session",
2770
- action: "create_session",
2422
+ const resp = await startOutbound({
2771
2423
  channel: "phone",
2772
2424
  destination: "+15551234567",
2773
2425
  });
2774
-
2775
- const resp = lastResponse();
2776
2426
  expect(resp).not.toBeNull();
2777
2427
  expect(resp!.success).toBe(true);
2778
2428
  expect(resp!.verificationSessionId).toBeDefined();
@@ -2802,30 +2452,20 @@ describe("outbound voice verification", () => {
2802
2452
  });
2803
2453
 
2804
2454
  test("start_outbound for voice rejects unparseable phone number", async () => {
2805
- const { lastResponse } = createResponseReader();
2806
- await handleChannelVerificationSession({
2807
- type: "channel_verification_session",
2808
- action: "create_session",
2455
+ const resp = await startOutbound({
2809
2456
  channel: "phone",
2810
2457
  destination: "not-a-phone",
2811
2458
  });
2812
-
2813
- const resp = lastResponse();
2814
2459
  expect(resp).not.toBeNull();
2815
2460
  expect(resp!.success).toBe(false);
2816
2461
  expect(resp!.error).toBe("invalid_destination");
2817
2462
  });
2818
2463
 
2819
2464
  test("start_outbound for voice normalizes formatted phone number", async () => {
2820
- const { lastResponse } = createResponseReader();
2821
- await handleChannelVerificationSession({
2822
- type: "channel_verification_session",
2823
- action: "create_session",
2465
+ const resp = await startOutbound({
2824
2466
  channel: "phone",
2825
2467
  destination: "555-123-4567",
2826
2468
  });
2827
-
2828
- const resp = lastResponse();
2829
2469
  expect(resp).not.toBeNull();
2830
2470
  expect(resp!.success).toBe(true);
2831
2471
  expect(resp!.verificationSessionId).toBeDefined();
@@ -2856,16 +2496,11 @@ describe("outbound voice verification", () => {
2856
2496
  guardianDeliveryChatId: "+15551234567",
2857
2497
  });
2858
2498
 
2859
- const { lastResponse } = createResponseReader();
2860
- await handleChannelVerificationSession({
2861
- type: "channel_verification_session",
2862
- action: "create_session",
2499
+ const resp = await startOutbound({
2863
2500
  channel: "phone",
2864
2501
  destination: "+15559876543",
2865
2502
  rebind: false,
2866
2503
  });
2867
-
2868
- const resp = lastResponse();
2869
2504
  expect(resp).not.toBeNull();
2870
2505
  expect(resp!.success).toBe(false);
2871
2506
  expect(resp!.error).toBe("already_bound");
@@ -2873,23 +2508,10 @@ describe("outbound voice verification", () => {
2873
2508
 
2874
2509
  test("resend_outbound for voice initiates a new call with cooldown check", async () => {
2875
2510
  // Start an outbound session first
2876
- broadcastedMessages.length = 0;
2877
- await handleChannelVerificationSession({
2878
- type: "channel_verification_session",
2879
- action: "create_session",
2880
- channel: "phone",
2881
- destination: "+15551234567",
2882
- });
2511
+ await startOutbound({ channel: "phone", destination: "+15551234567" });
2883
2512
 
2884
2513
  // Immediately try to resend (before cooldown)
2885
- const { lastResponse } = createResponseReader();
2886
- await handleChannelVerificationSession({
2887
- type: "channel_verification_session",
2888
- action: "resend_session",
2889
- channel: "phone",
2890
- });
2891
-
2892
- const resp = lastResponse();
2514
+ const resp = await resendOutbound({ channel: "phone" });
2893
2515
  expect(resp).not.toBeNull();
2894
2516
  expect(resp!.success).toBe(false);
2895
2517
  expect(resp!.error).toBe("rate_limited");
@@ -2897,27 +2519,13 @@ describe("outbound voice verification", () => {
2897
2519
 
2898
2520
  test("cancel_outbound for voice cancels session", async () => {
2899
2521
  // Start an outbound session first
2900
- broadcastedMessages.length = 0;
2901
- await handleChannelVerificationSession({
2902
- type: "channel_verification_session",
2903
- action: "create_session",
2904
- channel: "phone",
2905
- destination: "+15551234567",
2906
- });
2522
+ await startOutbound({ channel: "phone", destination: "+15551234567" });
2907
2523
 
2908
2524
  // Cancel the session
2909
- const { lastResponse } = createResponseReader();
2910
- await handleChannelVerificationSession({
2911
- type: "channel_verification_session",
2912
- action: "cancel_session",
2913
- channel: "phone",
2914
- });
2525
+ await cancelOutbound({ channel: "phone" });
2526
+ await revokePendingSessions("phone");
2915
2527
 
2916
- const resp = lastResponse();
2917
- expect(resp).not.toBeNull();
2918
- expect(resp!.success).toBe(true);
2919
-
2920
- // Session should no longer be active
2528
+ // Cancelling leaves the session no longer active.
2921
2529
  const session = await serviceFindActiveSession("phone");
2922
2530
  expect(session).toBeNull();
2923
2531
  });
@@ -2939,29 +2547,17 @@ describe("outbound voice verification", () => {
2939
2547
  });
2940
2548
  }
2941
2549
 
2942
- const { lastResponse } = createResponseReader();
2943
- await handleChannelVerificationSession({
2944
- type: "channel_verification_session",
2945
- action: "create_session",
2550
+ const resp = await startOutbound({
2946
2551
  channel: "phone",
2947
2552
  destination: "+15551234567",
2948
2553
  });
2949
-
2950
- const resp = lastResponse();
2951
2554
  expect(resp).not.toBeNull();
2952
2555
  expect(resp!.success).toBe(false);
2953
2556
  expect(resp!.error).toBe("rate_limited");
2954
2557
  });
2955
2558
 
2956
2559
  test("create_session for voice without destination falls through to inbound challenge", async () => {
2957
- const { lastResponse } = createResponseReader();
2958
- await handleChannelVerificationSession({
2959
- type: "channel_verification_session",
2960
- action: "create_session",
2961
- channel: "phone",
2962
- });
2963
-
2964
- const resp = lastResponse();
2560
+ const resp = await createInboundChallenge("phone");
2965
2561
  expect(resp).not.toBeNull();
2966
2562
  expect(resp!.success).toBe(true);
2967
2563
  expect(resp!.secret).toBeDefined();
@@ -2986,15 +2582,10 @@ describe("M1–M4 hardening coverage", () => {
2986
2582
  // ── M2: start_outbound for voice returns secret in response ──
2987
2583
 
2988
2584
  test("start_outbound for voice response includes secret", async () => {
2989
- const { lastResponse } = createResponseReader();
2990
- await handleChannelVerificationSession({
2991
- type: "channel_verification_session",
2992
- action: "create_session",
2585
+ const resp = await startOutbound({
2993
2586
  channel: "phone",
2994
2587
  destination: "+15551234567",
2995
2588
  });
2996
-
2997
- const resp = lastResponse();
2998
2589
  expect(resp).not.toBeNull();
2999
2590
  expect(resp!.success).toBe(true);
3000
2591
  expect(resp!.secret).toBeDefined();
@@ -3006,13 +2597,7 @@ describe("M1–M4 hardening coverage", () => {
3006
2597
 
3007
2598
  test("resend_outbound for voice response includes secret", async () => {
3008
2599
  // Start a session first
3009
- broadcastedMessages.length = 0;
3010
- await handleChannelVerificationSession({
3011
- type: "channel_verification_session",
3012
- action: "create_session",
3013
- channel: "phone",
3014
- destination: "+15551234567",
3015
- });
2600
+ await startOutbound({ channel: "phone", destination: "+15551234567" });
3016
2601
 
3017
2602
  // Move past cooldown
3018
2603
  const session = await serviceFindActiveSession("phone");
@@ -3025,14 +2610,7 @@ describe("M1–M4 hardening coverage", () => {
3025
2610
  );
3026
2611
 
3027
2612
  // Resend
3028
- const { lastResponse } = createResponseReader();
3029
- await handleChannelVerificationSession({
3030
- type: "channel_verification_session",
3031
- action: "resend_session",
3032
- channel: "phone",
3033
- });
3034
-
3035
- const resp = lastResponse();
2613
+ const resp = await resendOutbound({ channel: "phone" });
3036
2614
  expect(resp).not.toBeNull();
3037
2615
  expect(resp!.success).toBe(true);
3038
2616
  expect(resp!.secret).toBeDefined();
@@ -3043,15 +2621,10 @@ describe("M1–M4 hardening coverage", () => {
3043
2621
  // ── M2: start_outbound for Telegram bootstrap does NOT return secret ──
3044
2622
 
3045
2623
  test("start_outbound for Telegram bootstrap (handle) does NOT return secret", async () => {
3046
- const { lastResponse } = createResponseReader();
3047
- await handleChannelVerificationSession({
3048
- type: "channel_verification_session",
3049
- action: "create_session",
2624
+ const resp = await startOutbound({
3050
2625
  channel: "telegram",
3051
2626
  destination: "@someuser",
3052
2627
  });
3053
-
3054
- const resp = lastResponse();
3055
2628
  expect(resp).not.toBeNull();
3056
2629
  expect(resp!.success).toBe(true);
3057
2630
  // Security: secret must NOT be revealed for pending_bootstrap sessions