@vellumai/assistant 0.11.2 → 0.11.3-staging.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (720) hide show
  1. package/ARCHITECTURE.md +58 -3
  2. package/docs/trusted-contact-access.md +9 -5
  3. package/docs/vellum-doctor.md +195 -0
  4. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/channels.ts +39 -0
  5. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/ingress.ts +10 -0
  6. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +6 -0
  7. package/node_modules/@vellumai/ces-client/src/index.ts +1 -0
  8. package/node_modules/@vellumai/ces-client/src/rpc-client.ts +19 -2
  9. package/node_modules/@vellumai/environments/package.json +2 -1
  10. package/node_modules/@vellumai/environments/src/__tests__/cloud-assistant-hub-url.test.ts +38 -0
  11. package/node_modules/@vellumai/environments/src/__tests__/install-layout.test.ts +98 -0
  12. package/node_modules/@vellumai/environments/src/__tests__/package-boundary.test.ts +5 -5
  13. package/node_modules/@vellumai/environments/src/index.ts +17 -6
  14. package/node_modules/@vellumai/environments/src/install-layout.ts +49 -0
  15. package/node_modules/@vellumai/environments/src/seeds.ts +29 -0
  16. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/channels.ts +39 -0
  17. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/ingress.ts +10 -0
  18. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +6 -0
  19. package/node_modules/@vellumai/gateway-client/src/index.ts +4 -0
  20. package/node_modules/@vellumai/gateway-client/src/verification-session-contract.ts +91 -6
  21. package/node_modules/@vellumai/ipc-server-utils/src/endpoint.test.ts +36 -0
  22. package/node_modules/@vellumai/ipc-server-utils/src/endpoint.ts +142 -0
  23. package/node_modules/@vellumai/ipc-server-utils/src/index.ts +2 -0
  24. package/node_modules/@vellumai/ipc-server-utils/src/listen-options.ts +3 -0
  25. package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.test.ts +15 -1
  26. package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.ts +17 -2
  27. package/node_modules/@vellumai/service-contracts/src/channels.ts +39 -0
  28. package/node_modules/@vellumai/service-contracts/src/ingress.ts +10 -0
  29. package/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +6 -0
  30. package/node_modules/@vellumai/slack-text/src/index.ts +16 -2
  31. package/openapi.yaml +589 -30
  32. package/package.json +1 -1
  33. package/src/__tests__/agent-image-optimize.test.ts +4 -4
  34. package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +113 -66
  35. package/src/__tests__/agent-loop-override-profile.test.ts +38 -48
  36. package/src/__tests__/agent-loop-subagent-billing-headers.test.ts +166 -0
  37. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +1 -0
  38. package/src/__tests__/agent-wake-override-profile.test.ts +1 -0
  39. package/src/__tests__/answered-question-persistence.test.ts +240 -0
  40. package/src/__tests__/anthropic-provider.test.ts +165 -30
  41. package/src/__tests__/app-control-flow.test.ts +9 -8
  42. package/src/__tests__/assistant-event-hub.test.ts +48 -0
  43. package/src/__tests__/attachments-store-heic-normalize.test.ts +16 -12
  44. package/src/__tests__/attachments-store.test.ts +130 -68
  45. package/src/__tests__/attachments.test.ts +36 -36
  46. package/src/__tests__/background-workers-disk-pressure.test.ts +1 -1
  47. package/src/__tests__/call-start-guardian-guard.test.ts +18 -4
  48. package/src/__tests__/canned-reply-release.test.ts +86 -0
  49. package/src/__tests__/channel-guardian.test.ts +59 -29
  50. package/src/__tests__/channel-readiness-telegram-remote.test.ts +154 -0
  51. package/src/__tests__/channel-setup-panel-ack.test.ts +6 -5
  52. package/src/__tests__/client-os-metadata-persistence.test.ts +103 -4
  53. package/src/__tests__/compactor-image-manifest-trust.test.ts +1 -1
  54. package/src/__tests__/consult-deadline.test.ts +65 -0
  55. package/src/__tests__/conversation-agent-loop.test.ts +438 -2
  56. package/src/__tests__/conversation-disk-view-integration.test.ts +5 -1
  57. package/src/__tests__/conversation-disk-view.test.ts +4 -4
  58. package/src/__tests__/conversation-error.test.ts +22 -3
  59. package/src/__tests__/conversation-fork-crud.test.ts +5 -1
  60. package/src/__tests__/conversation-fork-referential.test.ts +352 -0
  61. package/src/__tests__/conversation-fork-retrospective.test.ts +5 -1
  62. package/src/__tests__/conversation-group-tools.test.ts +32 -2
  63. package/src/__tests__/conversation-inference-profile-route.test.ts +27 -1
  64. package/src/__tests__/conversation-initial-prompt.test.ts +51 -22
  65. package/src/__tests__/conversation-key-store-bootstrap-cleanup.test.ts +119 -0
  66. package/src/__tests__/conversation-key-store-origin.test.ts +105 -0
  67. package/src/__tests__/conversation-list-recency-ordering.test.ts +104 -0
  68. package/src/__tests__/conversation-origin-at-creation.test.ts +153 -0
  69. package/src/__tests__/conversation-origin-channel-filter.test.ts +95 -0
  70. package/src/__tests__/conversation-placement-promotion.test.ts +172 -0
  71. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +123 -0
  72. package/src/__tests__/conversation-queue.test.ts +433 -1
  73. package/src/__tests__/conversation-retry-route.test.ts +3 -1
  74. package/src/__tests__/conversation-routes-hidden-queue.test.ts +64 -1
  75. package/src/__tests__/conversation-store-ephemeral.test.ts +4 -0
  76. package/src/__tests__/conversation-store.test.ts +26 -10
  77. package/src/__tests__/conversation-summarize-route.test.ts +1 -1
  78. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +15 -6
  79. package/src/__tests__/conversation-surfaces-activation-emit.test.ts +6 -5
  80. package/src/__tests__/conversation-surfaces-app-control.test.ts +12 -11
  81. package/src/__tests__/conversation-surfaces-app-open.test.ts +11 -5
  82. package/src/__tests__/conversation-surfaces-data-persist.test.ts +5 -4
  83. package/src/__tests__/conversation-surfaces-history-restored-completion.test.ts +967 -0
  84. package/src/__tests__/conversation-surfaces-persisted-info.test.ts +312 -0
  85. package/src/__tests__/conversation-surfaces-queued-emit.test.ts +6 -5
  86. package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +12 -8
  87. package/src/__tests__/conversation-surfaces-standalone.test.ts +21 -11
  88. package/src/__tests__/conversation-surfaces-state-update.test.ts +7 -11
  89. package/src/__tests__/conversation-surfaces-table-action.test.ts +7 -6
  90. package/src/__tests__/conversation-surfaces-task-progress.test.ts +11 -6
  91. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +9 -6
  92. package/src/__tests__/conversation-tool-setup-attribution.test.ts +70 -7
  93. package/src/__tests__/conversation-wait-for-idle.test.ts +6 -11
  94. package/src/__tests__/credential-prompt-route.test.ts +57 -7
  95. package/src/__tests__/credential-security-invariants.test.ts +1 -0
  96. package/src/__tests__/cu-unified-flow.test.ts +89 -28
  97. package/src/__tests__/custom-group-listing-visibility.test.ts +1 -1
  98. package/src/__tests__/db-schedule-syntax-migration.test.ts +4 -1
  99. package/src/__tests__/discord-access-request-privacy.test.ts +366 -0
  100. package/src/__tests__/discord-requester-notice-privacy.test.ts +87 -0
  101. package/src/__tests__/disk-pressure-guard.test.ts +55 -55
  102. package/src/__tests__/disk-pressure-routes.test.ts +10 -10
  103. package/src/__tests__/disk-pressure-tools.test.ts +2 -2
  104. package/src/__tests__/disk-usage.test.ts +35 -20
  105. package/src/__tests__/document-sync-tags.test.ts +379 -0
  106. package/src/__tests__/document-update-default-surface.test.ts +28 -0
  107. package/src/__tests__/dynamic-skill-background-guard.test.ts +2 -2
  108. package/src/__tests__/emit-signal-routing-intent.test.ts +3 -4
  109. package/src/__tests__/ensure-conversation-exists.test.ts +8 -5
  110. package/src/__tests__/events-dev-bypass-actor.test.ts +183 -1
  111. package/src/__tests__/evict-conversations-for-reload.test.ts +90 -0
  112. package/src/__tests__/file-list-tool.test.ts +12 -10
  113. package/src/__tests__/file-ops-service.test.ts +151 -44
  114. package/src/__tests__/file-read-container-boundary.test.ts +137 -0
  115. package/src/__tests__/file-read-tool.test.ts +21 -9
  116. package/src/__tests__/file-url-path-guard.test.ts +83 -0
  117. package/src/__tests__/filesystem-tools.test.ts +82 -66
  118. package/src/__tests__/guardian-outbound-http.test.ts +8 -8
  119. package/src/__tests__/helpers/gateway-verification-sessions-stub.ts +14 -1
  120. package/src/__tests__/helpers/hub-clients.ts +42 -0
  121. package/src/__tests__/helpers/mock-conversation.ts +46 -0
  122. package/src/__tests__/helpers/verification-sessions-ipc-sim.ts +61 -57
  123. package/src/__tests__/history-repair.test.ts +112 -12
  124. package/src/__tests__/host-bash-proxy.test.ts +10 -5
  125. package/src/__tests__/host-bash-routes.test.ts +67 -0
  126. package/src/__tests__/host-browser-proxy.test.ts +2 -2
  127. package/src/__tests__/host-cu-proxy.test.ts +3 -7
  128. package/src/__tests__/host-file-proxy.test.ts +3 -7
  129. package/src/__tests__/host-file-read-tool.test.ts +5 -1
  130. package/src/__tests__/host-transfer-proxy-targeted.test.ts +6 -4
  131. package/src/__tests__/http-conversation-lineage.test.ts +2 -5
  132. package/src/__tests__/http-user-message-parity.test.ts +44 -0
  133. package/src/__tests__/identity-routes.test.ts +13 -13
  134. package/src/__tests__/image-conversion.test.ts +29 -26
  135. package/src/__tests__/image-source-path-reinject.test.ts +2 -2
  136. package/src/__tests__/inference-profile-session-handler.test.ts +95 -0
  137. package/src/__tests__/inference-profile-session-ipc.test.ts +25 -0
  138. package/src/__tests__/introduction-card-resolver.test.ts +6 -1
  139. package/src/__tests__/list-messages-attachments.test.ts +56 -32
  140. package/src/__tests__/list-messages-background-tool-completion.test.ts +4 -4
  141. package/src/__tests__/list-messages-client-message-id.test.ts +2 -2
  142. package/src/__tests__/list-messages-hidden-metadata.test.ts +16 -16
  143. package/src/__tests__/list-messages-page-latest.test.ts +61 -61
  144. package/src/__tests__/list-messages-provider-error.test.ts +6 -6
  145. package/src/__tests__/list-messages-queued.test.ts +49 -6
  146. package/src/__tests__/list-messages-system-card.test.ts +4 -4
  147. package/src/__tests__/list-messages-tool-merge.test.ts +9 -9
  148. package/src/__tests__/llm-resolver-override-or-default.test.ts +141 -0
  149. package/src/__tests__/llm-usage-store.test.ts +55 -0
  150. package/src/__tests__/manual-token-reconciliation.test.ts +45 -0
  151. package/src/__tests__/mcp-tool-annotations-risk.test.ts +133 -0
  152. package/src/__tests__/media-stream-server-integration.test.ts +36 -0
  153. package/src/__tests__/migration-export-http.test.ts +1 -1
  154. package/src/__tests__/migration-import-commit-http.test.ts +1 -1
  155. package/src/__tests__/migration-import-preflight-http.test.ts +1 -1
  156. package/src/__tests__/migration-validate-http.test.ts +1 -1
  157. package/src/__tests__/mtime-cache.test.ts +65 -3
  158. package/src/__tests__/notification-platform-adapter.test.ts +104 -0
  159. package/src/__tests__/notification-schedule-notify-dedup.test.ts +1 -1
  160. package/src/__tests__/oauth-provider-profiles.test.ts +4 -2
  161. package/src/__tests__/oauth-provider-seed-logos.test.ts +65 -0
  162. package/src/__tests__/oauth-provider-serializer.test.ts +17 -0
  163. package/src/__tests__/oauth-providers-routes.test.ts +1 -0
  164. package/src/__tests__/openai-responses-prompt-cache.test.ts +5 -2
  165. package/src/__tests__/path-policy.test.ts +164 -1
  166. package/src/__tests__/persist-media-references.test.ts +2 -2
  167. package/src/__tests__/persist-unsendable-image-downscale.test.ts +5 -5
  168. package/src/__tests__/persist-unsendable-image.test.ts +23 -23
  169. package/src/__tests__/persona-resolver.test.ts +154 -0
  170. package/src/__tests__/plugin-api-webhook-url.test.ts +170 -0
  171. package/src/__tests__/plugin-import-boundary-guard.test.ts +6 -0
  172. package/src/__tests__/processing-flag-persist-failure.test.ts +15 -10
  173. package/src/__tests__/profile-availability-incomplete.test.ts +113 -0
  174. package/src/__tests__/profiler-routes.test.ts +13 -13
  175. package/src/__tests__/prompt-cache-cross-turn-stability.test.ts +593 -0
  176. package/src/__tests__/proxy-approval-callback.test.ts +3 -3
  177. package/src/__tests__/queued-message-cancel-and-steer.test.ts +189 -0
  178. package/src/__tests__/queued-message-steer-actor-scoping.test.ts +164 -0
  179. package/src/__tests__/resolve-trust-class.test.ts +20 -0
  180. package/src/__tests__/run-conversation-turn-persistence.test.ts +1 -1
  181. package/src/__tests__/run-due-schedules.test.ts +174 -0
  182. package/src/__tests__/runtime-attachment-metadata.test.ts +15 -7
  183. package/src/__tests__/scaffold-managed-skill-tool.test.ts +166 -0
  184. package/src/__tests__/schedule-routes.test.ts +730 -8
  185. package/src/__tests__/schedule-store.test.ts +540 -0
  186. package/src/__tests__/schedule-tools.test.ts +103 -0
  187. package/src/__tests__/scheduler-wake.test.ts +5 -0
  188. package/src/__tests__/secret-routes-platform-proxy.test.ts +29 -1
  189. package/src/__tests__/secure-keys.test.ts +91 -0
  190. package/src/__tests__/server-history-render.test.ts +97 -7
  191. package/src/__tests__/size-guard.test.ts +8 -8
  192. package/src/__tests__/skill-docs-sync-guard.test.ts +3 -2
  193. package/src/__tests__/skill-tool-factory.test.ts +247 -0
  194. package/src/__tests__/skills-install-staging.test.ts +15 -6
  195. package/src/__tests__/slack-edit-ordering-characterization.test.ts +103 -0
  196. package/src/__tests__/slack-inbound-verification.test.ts +4 -1
  197. package/src/__tests__/slack-mention-binding-invariance.test.ts +103 -0
  198. package/src/__tests__/slack-mention-provider-leak-audit.test.ts +135 -0
  199. package/src/__tests__/starter-task-flow.test.ts +5 -4
  200. package/src/__tests__/steer-on-enqueue-question.test.ts +3 -2
  201. package/src/__tests__/stt-language-catalog-parity.test.ts +38 -0
  202. package/src/__tests__/subagent-allowlist-validation.test.ts +26 -4
  203. package/src/__tests__/subagent-cron-run-attribution.test.ts +301 -0
  204. package/src/__tests__/subagent-disposal.test.ts +10 -0
  205. package/src/__tests__/subagent-fork-notifications.test.ts +10 -0
  206. package/src/__tests__/subagent-fork-prompt-role.test.ts +85 -11
  207. package/src/__tests__/subagent-fork-spawn.test.ts +15 -7
  208. package/src/__tests__/subagent-manager-notify.test.ts +148 -0
  209. package/src/__tests__/subagent-notify-parent.test.ts +1 -1
  210. package/src/__tests__/subagent-persistence.test.ts +141 -0
  211. package/src/__tests__/subagent-role-registry.test.ts +172 -63
  212. package/src/__tests__/subagent-role-resolution.test.ts +88 -0
  213. package/src/__tests__/subagent-spawn-and-await.test.ts +48 -0
  214. package/src/__tests__/subagent-spawn-tool-fork.test.ts +111 -10
  215. package/src/__tests__/subagent-terminal-message.test.ts +88 -1
  216. package/src/__tests__/subagent-tool-gate-mode.test.ts +225 -10
  217. package/src/__tests__/subagent-tools.test.ts +1901 -108
  218. package/src/__tests__/surface-completion-compaction-boundary.test.ts +124 -0
  219. package/src/__tests__/surface-completion-in-flight-snapshot.test.ts +298 -0
  220. package/src/__tests__/system-prompt.test.ts +82 -7
  221. package/src/__tests__/tool-grant-request-escalation.test.ts +40 -1
  222. package/src/__tests__/tool-result-spool.test.ts +74 -18
  223. package/src/__tests__/tool-side-effects-documents.test.ts +174 -0
  224. package/src/__tests__/tool-side-effects-hook-failures.test.ts +129 -0
  225. package/src/__tests__/tools-audio-read.test.ts +10 -10
  226. package/src/__tests__/tools-get-route.test.ts +85 -12
  227. package/src/__tests__/tools-list-cli.test.ts +171 -0
  228. package/src/__tests__/top-level-renderer.test.ts +54 -1
  229. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +68 -1
  230. package/src/__tests__/trusted-contact-multichannel.test.ts +4 -1
  231. package/src/__tests__/turn-tail-chain.test.ts +135 -0
  232. package/src/__tests__/ui-choice-copy-surfaces.test.ts +6 -4
  233. package/src/__tests__/ui-visual-surface.test.ts +5 -4
  234. package/src/__tests__/ui-work-result-surface.test.ts +5 -4
  235. package/src/__tests__/unified-turn-context-visible-app.test.ts +7 -8
  236. package/src/__tests__/usage-routes.test.ts +62 -0
  237. package/src/__tests__/verification-outbound-delivery.test.ts +176 -0
  238. package/src/__tests__/visible-app-context.test.ts +20 -4
  239. package/src/__tests__/web-search-history.test.ts +96 -0
  240. package/src/__tests__/workspace-migration-139-clear-renamed-cost-profile-label.test.ts +6 -6
  241. package/src/__tests__/workspace-migration-140-repair-seed-pinned-memory-v3-live.test.ts +330 -0
  242. package/src/acp/failure-error.ts +3 -5
  243. package/src/agent/attachments.ts +26 -24
  244. package/src/agent/history-repair/history-repair.ts +27 -26
  245. package/src/agent/image-optimize.ts +6 -6
  246. package/src/agent/loop.ts +40 -22
  247. package/src/agent/message-types.ts +3 -3
  248. package/src/api/constants/document-tools.ts +55 -0
  249. package/src/api/events/question-answered.ts +53 -0
  250. package/src/api/events/tool-result.ts +7 -0
  251. package/src/api/index.ts +13 -0
  252. package/src/api/responses/conversation-message.ts +11 -0
  253. package/src/api/surfaces-oauth-connect.test.ts +96 -0
  254. package/src/api/surfaces.ts +36 -8
  255. package/src/approvals/guardian-channel-delivery.ts +83 -0
  256. package/src/approvals/guardian-expiry-notifier.ts +13 -11
  257. package/src/approvals/guardian-request-resolvers.ts +222 -135
  258. package/src/calls/__tests__/voice-session-bridge.test.ts +50 -0
  259. package/src/calls/media-stream-server.ts +9 -1
  260. package/src/calls/voice-session-bridge.ts +38 -0
  261. package/src/channels/__tests__/gateway-verification-sessions.test.ts +3 -3
  262. package/src/channels/config.ts +7 -2
  263. package/src/channels/gateway-verification-sessions.ts +7 -2
  264. package/src/channels/types.ts +7 -1
  265. package/src/cli/commands/__tests__/cache.test.ts +7 -4
  266. package/src/cli/commands/__tests__/cli-test-harness.ts +82 -0
  267. package/src/cli/commands/__tests__/conversations-wake.test.ts +51 -6
  268. package/src/cli/commands/__tests__/inference-models.test.ts +14 -0
  269. package/src/cli/commands/__tests__/inference-profiles.test.ts +44 -1
  270. package/src/cli/commands/__tests__/inference-providers.test.ts +7 -0
  271. package/src/cli/commands/__tests__/plugins.test.ts +692 -0
  272. package/src/cli/commands/__tests__/schedules.test.ts +136 -37
  273. package/src/cli/commands/conversations.ts +7 -3
  274. package/src/cli/commands/credentials.help.ts +7 -0
  275. package/src/cli/commands/inference-profiles.ts +9 -3
  276. package/src/cli/commands/inference.help.ts +16 -2
  277. package/src/cli/commands/mcp.help.ts +4 -2
  278. package/src/cli/commands/memory/memory-retrospective.ts +18 -0
  279. package/src/cli/commands/notifications.ts +3 -6
  280. package/src/cli/commands/oauth/connect-surface-guidance.test.ts +16 -0
  281. package/src/cli/commands/oauth/connect-surface-guidance.ts +25 -7
  282. package/src/cli/commands/oauth/connect.test.ts +26 -1
  283. package/src/cli/commands/oauth/connect.ts +4 -1
  284. package/src/cli/commands/oauth/index.help.ts +3 -1
  285. package/src/cli/commands/oauth/providers.ts +3 -0
  286. package/src/cli/commands/plugins.help.ts +25 -2
  287. package/src/cli/commands/plugins.ts +289 -10
  288. package/src/cli/commands/schedules.help.ts +14 -12
  289. package/src/cli/commands/schedules.ts +19 -5
  290. package/src/cli/commands/tools.help.ts +4 -2
  291. package/src/cli/commands/tools.ts +38 -0
  292. package/src/cli/lib/__tests__/inspect-plugin.test.ts +1 -0
  293. package/src/cli/lib/__tests__/install-from-github.test.ts +79 -0
  294. package/src/cli/lib/__tests__/install-from-platform.test.ts +47 -1
  295. package/src/cli/lib/__tests__/plugin-surfaces.test.ts +86 -7
  296. package/src/cli/lib/__tests__/upgrade-plugin.test.ts +122 -0
  297. package/src/cli/lib/install-from-github.ts +61 -0
  298. package/src/cli/lib/install-from-platform.ts +13 -2
  299. package/src/cli/lib/plugin-surfaces.ts +124 -4
  300. package/src/cli/lib/upgrade-plugin.ts +18 -0
  301. package/src/cli.ts +1 -1
  302. package/src/config/__tests__/deployment-context-defaults.test.ts +73 -0
  303. package/src/config/__tests__/memory-retrospective-schema.test.ts +40 -0
  304. package/src/config/__tests__/plugin-updates-schema.test.ts +43 -0
  305. package/src/config/__tests__/profile-tool-support.test.ts +131 -0
  306. package/src/config/bundled-skills/AGENTS.md +1 -1
  307. package/src/config/bundled-skills/app-builder/SKILL.md +1 -1
  308. package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +3 -3
  309. package/src/config/bundled-skills/schedule/SKILL.md +5 -1
  310. package/src/config/bundled-skills/schedule/TOOLS.json +2 -2
  311. package/src/config/bundled-skills/settings/TOOLS.json +2 -2
  312. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +1 -0
  313. package/src/config/bundled-skills/subagent/SKILL.md +53 -22
  314. package/src/config/bundled-skills/subagent/TOOLS.json +14 -13
  315. package/src/config/bundled-skills/visualize/SKILL.md +1 -1
  316. package/src/config/default-profile-catalog.ts +6 -2
  317. package/src/config/feature-flag-registry.json +25 -17
  318. package/src/config/llm-resolver.ts +45 -15
  319. package/src/config/loader.ts +70 -1
  320. package/src/config/profile-tool-support.ts +110 -0
  321. package/src/config/sanitize-for-transfer.ts +11 -0
  322. package/src/config/schema.ts +4 -0
  323. package/src/config/schemas/__tests__/stt.test.ts +20 -5
  324. package/src/config/schemas/channels.ts +13 -0
  325. package/src/config/schemas/memory-retrospective.ts +25 -0
  326. package/src/config/schemas/plugin-updates.ts +55 -0
  327. package/src/config/schemas/services.ts +8 -4
  328. package/src/config/schemas/stt.ts +17 -5
  329. package/src/config/schemas/timeouts.ts +1 -1
  330. package/src/contacts/__tests__/guardian-delivery-reader.test.ts +16 -0
  331. package/src/contacts/guardian-delivery-reader.ts +29 -2
  332. package/src/context/compactor.ts +4 -4
  333. package/src/context/post-turn-tool-result-truncation.ts +32 -16
  334. package/src/context/strip-injections.ts +42 -0
  335. package/src/context/tool-result-spool.ts +32 -18
  336. package/src/credential-execution/ces-runtime.ts +3 -0
  337. package/src/credential-execution/client.ts +22 -1
  338. package/src/credential-execution/process-manager.test.ts +99 -0
  339. package/src/credential-execution/process-manager.ts +62 -16
  340. package/src/daemon/__tests__/abort-null-controller.test.ts +141 -5
  341. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +8 -12
  342. package/src/daemon/__tests__/install-assistant-command.test.ts +234 -0
  343. package/src/daemon/__tests__/turn-tail-assistant-reply-notify.test.ts +2 -4
  344. package/src/daemon/__tests__/turn-tail-deleted-conversation.test.ts +20 -29
  345. package/src/daemon/channel-ui-capability.ts +4 -4
  346. package/src/daemon/conversation-agent-loop-handlers.ts +117 -61
  347. package/src/daemon/conversation-agent-loop.ts +318 -148
  348. package/src/daemon/conversation-attachments.ts +1 -1
  349. package/src/daemon/conversation-error.ts +8 -1
  350. package/src/daemon/conversation-initial-prompt.ts +16 -38
  351. package/src/daemon/conversation-lifecycle.ts +154 -32
  352. package/src/daemon/conversation-messaging.ts +106 -43
  353. package/src/daemon/conversation-process.ts +131 -33
  354. package/src/daemon/conversation-queue-manager.ts +10 -0
  355. package/src/daemon/conversation-runtime-assembly.ts +9 -4
  356. package/src/daemon/conversation-store.ts +52 -3
  357. package/src/daemon/conversation-surface-state.ts +9 -0
  358. package/src/daemon/conversation-surfaces.ts +403 -210
  359. package/src/daemon/conversation-tool-setup.ts +56 -7
  360. package/src/daemon/conversation-turn-finalize.ts +134 -89
  361. package/src/daemon/conversation.ts +111 -27
  362. package/src/daemon/disk-pressure-guard-lifecycle.ts +2 -2
  363. package/src/daemon/disk-pressure-guard.ts +3 -3
  364. package/src/daemon/doordash-steps.ts +5 -5
  365. package/src/daemon/external-plugins-bootstrap.ts +7 -1
  366. package/src/daemon/handlers/config-channels.ts +48 -5
  367. package/src/daemon/handlers/conversation-history.ts +1 -1
  368. package/src/daemon/handlers/conversations.ts +62 -17
  369. package/src/daemon/handlers/shared.ts +12 -0
  370. package/src/daemon/handlers/skills.ts +11 -1
  371. package/src/daemon/identity-helpers.ts +38 -10
  372. package/src/daemon/install-assistant-command.ts +322 -0
  373. package/src/daemon/lifecycle.ts +14 -3
  374. package/src/daemon/message-types/conversations.ts +6 -7
  375. package/src/daemon/message-types/sync.ts +1 -0
  376. package/src/daemon/persist-media-references.ts +23 -14
  377. package/src/daemon/process-message.ts +6 -6
  378. package/src/daemon/tool-setup-types.ts +19 -122
  379. package/src/daemon/tool-side-effects.ts +17 -94
  380. package/src/daemon/trust-context-types.ts +39 -0
  381. package/src/daemon/trust-context.ts +14 -6
  382. package/src/daemon/turn-tail-chain.ts +73 -0
  383. package/src/daemon/web-search-history.ts +47 -33
  384. package/src/heartbeat/heartbeat-service.ts +6 -6
  385. package/src/ipc/__tests__/socket-path.test.ts +21 -0
  386. package/src/ipc/assistant-server.ts +17 -10
  387. package/src/ipc/cli-client.ts +10 -1
  388. package/src/ipc/routes/__tests__/documents-sync-ipc-routes.test.ts +56 -0
  389. package/src/ipc/routes/documents-sync-ipc-routes.ts +42 -0
  390. package/src/ipc/socket-cleanup.ts +9 -7
  391. package/src/ipc/socket-path.ts +4 -96
  392. package/src/live-voice/__tests__/live-voice-archive.test.ts +16 -16
  393. package/src/live-voice/__tests__/live-voice-attach-image.test.ts +209 -0
  394. package/src/live-voice/__tests__/live-voice-connection.test.ts +20 -0
  395. package/src/live-voice/__tests__/live-voice-integration.test.ts +7 -5
  396. package/src/live-voice/__tests__/live-voice-session-telemetry.test.ts +265 -0
  397. package/src/live-voice/__tests__/live-voice-stt.test.ts +6 -1
  398. package/src/live-voice/__tests__/live-voice-vad.test.ts +35 -15
  399. package/src/live-voice/__tests__/protocol.test.ts +52 -0
  400. package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +3 -0
  401. package/src/live-voice/live-voice-archive.ts +13 -13
  402. package/src/live-voice/live-voice-connection.ts +7 -1
  403. package/src/live-voice/live-voice-photo.ts +169 -0
  404. package/src/live-voice/live-voice-session.ts +108 -4
  405. package/src/live-voice/protocol.ts +89 -1
  406. package/src/mcp/client.ts +10 -0
  407. package/src/messaging/providers/__tests__/callback-routing.test.ts +5 -1
  408. package/src/messaging/providers/__tests__/transport-dispatch.test.ts +66 -4
  409. package/src/messaging/providers/callback-routing.ts +1 -0
  410. package/src/messaging/providers/discord/api.ts +312 -0
  411. package/src/messaging/providers/discord/dm-delivery.test.ts +204 -0
  412. package/src/messaging/providers/discord/render.test.ts +204 -0
  413. package/src/messaging/providers/discord/render.ts +195 -0
  414. package/src/messaging/providers/discord/send.test.ts +179 -0
  415. package/src/messaging/providers/discord/send.ts +212 -0
  416. package/src/messaging/providers/discord/transport.ts +81 -0
  417. package/src/messaging/providers/index.ts +3 -1
  418. package/src/messaging/providers/retry-policy.test.ts +247 -0
  419. package/src/messaging/providers/retry-policy.ts +206 -0
  420. package/src/messaging/providers/slack/mention-source.test.ts +285 -0
  421. package/src/messaging/providers/slack/mention-source.ts +287 -0
  422. package/src/messaging/providers/telegram-bot/api.ts +47 -130
  423. package/src/messaging/providers/whatsapp/api.ts +47 -136
  424. package/src/monitoring/__tests__/db-integrity-sample.test.ts +4 -1
  425. package/src/monitoring/__tests__/plugin-auto-update.test.ts +332 -0
  426. package/src/monitoring/db-integrity-sample.ts +6 -1
  427. package/src/monitoring/plugin-auto-update.ts +359 -0
  428. package/src/monitoring/resource-sampler.ts +21 -5
  429. package/src/monitoring/worker.ts +13 -0
  430. package/src/notifications/__tests__/assistant-reply-producer.test.ts +375 -2
  431. package/src/notifications/__tests__/broadcaster.test.ts +37 -1
  432. package/src/notifications/__tests__/copy-composer.test.ts +165 -0
  433. package/src/notifications/__tests__/emit-signal-pipeline-failure.test.ts +205 -0
  434. package/src/notifications/__tests__/notification-utils.test.ts +249 -0
  435. package/src/notifications/adapters/platform.ts +31 -2
  436. package/src/notifications/assistant-reply-producer.ts +79 -32
  437. package/src/notifications/broadcaster.ts +30 -10
  438. package/src/notifications/copy-composer.ts +69 -0
  439. package/src/notifications/emit-signal.ts +82 -15
  440. package/src/notifications/events-store.ts +10 -2
  441. package/src/notifications/home-feed-side-effect.ts +6 -3
  442. package/src/notifications/notification-utils.ts +173 -5
  443. package/src/notifications/signal.ts +12 -0
  444. package/src/oauth/AGENTS.md +4 -2
  445. package/src/oauth/byo-connection.ts +6 -1
  446. package/src/oauth/credential-token-resolver.ts +3 -10
  447. package/src/oauth/manual-token-connection.ts +29 -64
  448. package/src/oauth/manual-token-providers.ts +62 -0
  449. package/src/oauth/provider-serializer.ts +13 -0
  450. package/src/oauth/seed-providers.ts +62 -11
  451. package/src/onboarding/onboarding-events-store.ts +91 -1
  452. package/src/permissions/question-prompter.test.ts +4 -0
  453. package/src/permissions/question-prompter.ts +21 -4
  454. package/src/persistence/__tests__/conversation-lineage.test.ts +248 -0
  455. package/src/persistence/attachments-store.ts +15 -15
  456. package/src/persistence/conversation-attention-store.ts +30 -10
  457. package/src/persistence/conversation-bootstrap.ts +17 -0
  458. package/src/persistence/conversation-crud.ts +363 -97
  459. package/src/persistence/conversation-group-migration.ts +1 -1
  460. package/src/persistence/conversation-key-store.ts +38 -10
  461. package/src/persistence/conversation-lineage.ts +226 -0
  462. package/src/persistence/conversation-plugin-facade.ts +28 -3
  463. package/src/persistence/conversation-queries.ts +207 -33
  464. package/src/persistence/conversation-types.ts +107 -1
  465. package/src/persistence/jobs-store.ts +32 -8
  466. package/src/persistence/llm-usage-store.ts +22 -0
  467. package/src/persistence/migrations/361-normalize-managed-connection-rows.test.ts +168 -0
  468. package/src/persistence/migrations/361-normalize-managed-connection-rows.ts +76 -0
  469. package/src/persistence/migrations/362-add-conversation-subagent-kind.test.ts +187 -0
  470. package/src/persistence/migrations/362-add-conversation-subagent-kind.ts +93 -0
  471. package/src/persistence/migrations/363-backfill-schedule-inference-profile.test.ts +229 -0
  472. package/src/persistence/migrations/363-backfill-schedule-inference-profile.ts +127 -0
  473. package/src/persistence/migrations/364-add-schedule-source-key.test.ts +120 -0
  474. package/src/persistence/migrations/364-add-schedule-source-key.ts +43 -0
  475. package/src/persistence/migrations/365-add-conversation-fork-strategy.ts +27 -0
  476. package/src/persistence/schema/conversations.ts +32 -0
  477. package/src/persistence/schema/infrastructure.ts +3 -0
  478. package/src/persistence/steps.ts +30 -0
  479. package/src/persistence/subagent-store.ts +148 -6
  480. package/src/platform/managed-speech.test.ts +20 -0
  481. package/src/platform/managed-speech.ts +9 -0
  482. package/src/plugin-api/index.ts +8 -0
  483. package/src/plugin-api/webhook-url.ts +151 -0
  484. package/src/plugins/__tests__/installed-plugin-dirs.test.ts +82 -0
  485. package/src/plugins/__tests__/source-fingerprint.test.ts +5 -1
  486. package/src/plugins/collect-source-versions.ts +4 -27
  487. package/src/plugins/defaults/exploration-drift/hooks/post-tool-use.ts +5 -5
  488. package/src/plugins/defaults/exploration-drift/package.json +1 -1
  489. package/src/plugins/defaults/image-recovery/hooks/post-model-call.ts +4 -2
  490. package/src/plugins/defaults/image-recovery/recover.ts +67 -57
  491. package/src/plugins/defaults/index.ts +1 -1
  492. package/src/plugins/defaults/memory/__tests__/conversation-queries.test.ts +64 -18
  493. package/src/plugins/defaults/memory/__tests__/jobs-worker-outcome-contract.test.ts +242 -0
  494. package/src/plugins/defaults/memory/__tests__/jobs-worker-retrospective-sweep.test.ts +24 -1
  495. package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +48 -3
  496. package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +511 -19
  497. package/src/plugins/defaults/memory/__tests__/memory-retrospective-prompt.test.ts +39 -3
  498. package/src/plugins/defaults/memory/__tests__/memory-retrospective-provider-path.test.ts +493 -0
  499. package/src/plugins/defaults/memory/__tests__/memory-retrospective-wake-chain.test.ts +5 -4
  500. package/src/plugins/defaults/memory/__tests__/memory-tier-boundary-guard.test.ts +5 -1
  501. package/src/plugins/defaults/memory/job-handlers.ts +87 -5
  502. package/src/plugins/defaults/memory/jobs-worker.ts +60 -6
  503. package/src/plugins/defaults/memory/memory-retrospective-constants.ts +11 -0
  504. package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +29 -0
  505. package/src/plugins/defaults/memory/memory-retrospective-job.ts +342 -60
  506. package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +23 -3
  507. package/src/plugins/defaults/memory/substrate/consolidation-lock.ts +9 -4
  508. package/src/plugins/defaults/memory/substrate/sweep-job.ts +7 -0
  509. package/src/plugins/defaults/memory/v1/graph/injection.ts +1 -1
  510. package/src/plugins/defaults/memory/v2/router.ts +8 -0
  511. package/src/plugins/defaults/turn-context/unified-turn-context.ts +1 -2
  512. package/src/plugins/external-plugin-loader.ts +17 -8
  513. package/src/plugins/installed-plugin-dirs.ts +101 -0
  514. package/src/plugins/mtime-cache.ts +34 -22
  515. package/src/plugins/types.ts +69 -8
  516. package/src/prompts/persona-resolver.ts +6 -2
  517. package/src/prompts/system-prompt.ts +72 -17
  518. package/src/providers/__tests__/inference.test.ts +40 -0
  519. package/src/providers/__tests__/preflight-resolved-config.test.ts +58 -1
  520. package/src/providers/__tests__/retry-callsite.test.ts +67 -0
  521. package/src/providers/__tests__/vellum-connection-routing.test.ts +42 -11
  522. package/src/providers/anthropic/client.ts +112 -95
  523. package/src/providers/connection-resolution.ts +35 -1
  524. package/src/providers/gemini/client.ts +5 -5
  525. package/src/providers/inference/adapter-factory.ts +16 -10
  526. package/src/providers/inference/auth.ts +16 -0
  527. package/src/providers/inference/connection-availability.ts +164 -12
  528. package/src/providers/inference/connections.ts +25 -1
  529. package/src/providers/media-resolve.ts +21 -11
  530. package/src/providers/model-catalog.ts +15 -0
  531. package/src/providers/openai/chat-completions-provider.ts +4 -4
  532. package/src/providers/openai/responses-provider.ts +12 -25
  533. package/src/providers/registry.ts +9 -4
  534. package/src/providers/retry.ts +25 -0
  535. package/src/providers/server-tool-pairing.ts +134 -0
  536. package/src/providers/speech-to-text/__tests__/resolve.test.ts +187 -6
  537. package/src/providers/speech-to-text/deepgram.ts +1 -0
  538. package/src/providers/speech-to-text/resolve.ts +80 -10
  539. package/src/providers/speech-to-text/vellum-managed.ts +7 -1
  540. package/src/providers/speech-to-text/vellum-speech-relay-connection.ts +3 -1
  541. package/src/providers/types.ts +26 -6
  542. package/src/providers/vellum-model-routing.ts +6 -7
  543. package/src/runtime/AGENTS.md +2 -0
  544. package/src/runtime/__tests__/agent-wake.test.ts +16 -16
  545. package/src/runtime/__tests__/background-job-runner.test.ts +0 -144
  546. package/src/runtime/__tests__/desktop-presence.test.ts +162 -0
  547. package/src/runtime/agent-wake.ts +63 -24
  548. package/src/runtime/assistant-event-hub.ts +87 -12
  549. package/src/runtime/auth/__tests__/guard-tests.test.ts +2 -0
  550. package/src/runtime/auth/__tests__/scopes.test.ts +20 -0
  551. package/src/runtime/auth/same-actor.ts +56 -8
  552. package/src/runtime/auth/scopes.ts +12 -0
  553. package/src/runtime/auth/types.ts +2 -0
  554. package/src/runtime/background-job-runner.ts +5 -27
  555. package/src/runtime/channel-readiness-service.ts +59 -3
  556. package/src/runtime/channel-readiness-types.ts +18 -0
  557. package/src/runtime/desktop-presence.ts +69 -0
  558. package/src/runtime/http-server.ts +12 -0
  559. package/src/runtime/local-actor-identity.ts +11 -9
  560. package/src/runtime/migrations/__tests__/vbundle-symlink-streaming-importer.test.ts +0 -0
  561. package/src/runtime/routes/__tests__/channel-verification-routes.test.ts +1 -1
  562. package/src/runtime/routes/__tests__/client-routes.test.ts +186 -14
  563. package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +547 -1
  564. package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +162 -7
  565. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +14 -10
  566. package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +340 -3
  567. package/src/runtime/routes/__tests__/llm-call-sites-routes.test.ts +28 -1
  568. package/src/runtime/routes/__tests__/plugins-routes.test.ts +173 -1
  569. package/src/runtime/routes/__tests__/question-routes.test.ts +64 -0
  570. package/src/runtime/routes/__tests__/retrospective-routes.test.ts +20 -0
  571. package/src/runtime/routes/__tests__/skill-history-route.test.ts +116 -0
  572. package/src/runtime/routes/__tests__/wake-conversation-routes.test.ts +60 -2
  573. package/src/runtime/routes/archive-utils.ts +16 -12
  574. package/src/runtime/routes/attachment-routes.ts +13 -6
  575. package/src/runtime/routes/canned-reply-release.ts +54 -0
  576. package/src/runtime/routes/channel-verification-routes.ts +2 -27
  577. package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +5 -0
  578. package/src/runtime/routes/client-routes.ts +114 -2
  579. package/src/runtime/routes/conversation-cli-routes.ts +10 -12
  580. package/src/runtime/routes/conversation-list-routes.ts +59 -10
  581. package/src/runtime/routes/conversation-management-routes.ts +47 -18
  582. package/src/runtime/routes/conversation-query-routes.ts +32 -53
  583. package/src/runtime/routes/conversation-routes.ts +360 -293
  584. package/src/runtime/routes/credential-prompt-routes.ts +23 -2
  585. package/src/runtime/routes/debug-routes.ts +4 -1
  586. package/src/runtime/routes/default-provider-routes.ts +7 -11
  587. package/src/runtime/routes/documents-routes.ts +44 -1
  588. package/src/runtime/routes/events-routes.ts +28 -1
  589. package/src/runtime/routes/host-app-control-routes.ts +2 -0
  590. package/src/runtime/routes/host-bash-routes.ts +2 -0
  591. package/src/runtime/routes/host-browser-routes.ts +2 -0
  592. package/src/runtime/routes/host-cu-routes.ts +2 -0
  593. package/src/runtime/routes/host-file-routes.ts +2 -0
  594. package/src/runtime/routes/host-transfer-routes.ts +4 -0
  595. package/src/runtime/routes/identity-routes.ts +6 -6
  596. package/src/runtime/routes/inbound-message-handler.ts +5 -5
  597. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +7 -3
  598. package/src/runtime/routes/inbound-stages/background-dispatch.ts +14 -0
  599. package/src/runtime/routes/inbound-stages/bootstrap-intercept.ts +2 -2
  600. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +14 -11
  601. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +14 -17
  602. package/src/runtime/routes/inference-profile-availability-guard.ts +171 -0
  603. package/src/runtime/routes/inference-profile-session-handler.ts +26 -2
  604. package/src/runtime/routes/inference-profile-session-routes.ts +2 -1
  605. package/src/runtime/routes/inference-profiles-routes.ts +152 -73
  606. package/src/runtime/routes/inference-provider-connection-routes.ts +0 -16
  607. package/src/runtime/routes/llm-call-sites-routes.ts +13 -0
  608. package/src/runtime/routes/log-export-routes.ts +1 -1
  609. package/src/runtime/routes/notification-routes.ts +0 -14
  610. package/src/runtime/routes/oauth-providers.ts +1 -0
  611. package/src/runtime/routes/plugins-routes.ts +106 -1
  612. package/src/runtime/routes/profiler-routes.ts +4 -2
  613. package/src/runtime/routes/question-routes.ts +28 -10
  614. package/src/runtime/routes/retrospective-routes.ts +9 -3
  615. package/src/runtime/routes/schedule-routes.ts +210 -12
  616. package/src/runtime/routes/secret-routes.ts +40 -23
  617. package/src/runtime/routes/settings-routes.ts +41 -13
  618. package/src/runtime/routes/skills-routes.ts +79 -0
  619. package/src/runtime/routes/ui-snapshot-routes.ts +1 -0
  620. package/src/runtime/routes/wake-conversation-routes.ts +23 -0
  621. package/src/runtime/services/__tests__/conversation-serializer.test.ts +1 -0
  622. package/src/runtime/services/conversation-serializer.ts +24 -9
  623. package/src/runtime/sync/documents-sidecar-publish.test.ts +101 -0
  624. package/src/runtime/sync/resource-sync-events.ts +79 -1
  625. package/src/runtime/sync/worker-daemon-notify.test.ts +34 -0
  626. package/src/runtime/sync/worker-daemon-notify.ts +44 -6
  627. package/src/runtime/verification-outbound-actions.ts +250 -222
  628. package/src/runtime/verification-templates.ts +41 -30
  629. package/src/schedule/__tests__/plugin-schedule-declarations.test.ts +614 -0
  630. package/src/schedule/__tests__/plugin-schedule-reconciler.test.ts +1066 -0
  631. package/src/schedule/inference-profile.ts +64 -3
  632. package/src/schedule/plugin-schedule-declarations.ts +518 -0
  633. package/src/schedule/plugin-schedule-reconciler.ts +605 -0
  634. package/src/schedule/plugin-schedules-gate.ts +23 -0
  635. package/src/schedule/schedule-store.ts +520 -26
  636. package/src/schedule/scheduler.ts +38 -0
  637. package/src/security/credential-backend.ts +51 -4
  638. package/src/security/encrypted-store.ts +64 -42
  639. package/src/security/secure-keys.ts +55 -7
  640. package/src/skills/catalog-install.ts +20 -7
  641. package/src/skills/inline-command-runner.ts +2 -7
  642. package/src/skills/skill-history.test.ts +269 -0
  643. package/src/skills/skill-history.ts +250 -0
  644. package/src/skills/skillssh-registry.ts +1 -1
  645. package/src/stt/daemon-batch-transcriber.ts +8 -6
  646. package/src/subagent/__tests__/consult-prompt.test.ts +25 -0
  647. package/src/subagent/consult-prompt.ts +3 -1
  648. package/src/subagent/consult-transcript.ts +1 -1
  649. package/src/subagent/manager.ts +304 -44
  650. package/src/subagent/progress-events.ts +35 -0
  651. package/src/subagent/role-resolution.ts +88 -0
  652. package/src/subagent/types.ts +302 -67
  653. package/src/subagent/validate-allowlists.ts +3 -4
  654. package/src/telegram/__tests__/webhook-health.test.ts +73 -1
  655. package/src/telegram/webhook-health.ts +101 -3
  656. package/src/telemetry/__tests__/telemetry-event-fixtures.ts +2 -0
  657. package/src/telemetry/activation-funnel.ts +6 -1
  658. package/src/telemetry/live-voice-funnel.ts +75 -0
  659. package/src/telemetry/telemetry-event-sources.ts +6 -0
  660. package/src/telemetry/telemetry-wire-source.json +1 -1
  661. package/src/telemetry/telemetry-wire.generated.ts +2 -0
  662. package/src/telemetry/types.ts +23 -4
  663. package/src/telemetry/usage-telemetry-reporter.test.ts +4 -0
  664. package/src/tools/__tests__/subagent-workflow-tool-input-schemas.test.ts +1 -1
  665. package/src/tools/ask-question/ask-question-tool.test.ts +118 -3
  666. package/src/tools/ask-question/ask-question-tool.ts +56 -2
  667. package/src/tools/browser/browser-execution.ts +1 -1
  668. package/src/tools/calls/call-start.ts +8 -5
  669. package/src/tools/conversation-groups/move_to_group.ts +10 -12
  670. package/src/tools/document/document-tool.ts +11 -4
  671. package/src/tools/execution-timeout.ts +9 -2
  672. package/src/tools/executor.ts +7 -6
  673. package/src/tools/filesystem/edit.ts +1 -1
  674. package/src/tools/filesystem/list.ts +1 -1
  675. package/src/tools/filesystem/read.ts +7 -13
  676. package/src/tools/filesystem/search.ts +23 -21
  677. package/src/tools/filesystem/write.ts +4 -1
  678. package/src/tools/host-filesystem/edit.ts +7 -2
  679. package/src/tools/host-filesystem/read.ts +21 -8
  680. package/src/tools/host-filesystem/write.ts +10 -2
  681. package/src/tools/host-terminal/host-shell.ts +2 -2
  682. package/src/tools/mcp/mcp-tool-factory.ts +25 -1
  683. package/src/tools/permission-checker.ts +8 -3
  684. package/src/tools/schedule/delete.ts +13 -1
  685. package/src/tools/schedule/list.ts +14 -3
  686. package/src/tools/schedule/update.ts +56 -33
  687. package/src/tools/shared/filesystem/audio-read.ts +6 -4
  688. package/src/tools/shared/filesystem/file-ops-service.ts +176 -116
  689. package/src/tools/shared/filesystem/image-read.ts +12 -12
  690. package/src/tools/shared/filesystem/path-policy.ts +70 -5
  691. package/src/tools/shared/filesystem/size-guard.ts +6 -6
  692. package/src/tools/shared/input-misuse.ts +72 -0
  693. package/src/tools/skills/scaffold-managed.ts +82 -0
  694. package/src/tools/skills/skill-tool-factory.ts +14 -1
  695. package/src/tools/subagent/consult-deadline.ts +12 -7
  696. package/src/tools/subagent/message.ts +3 -1
  697. package/src/tools/subagent/read.ts +66 -4
  698. package/src/tools/subagent/spawn.ts +500 -78
  699. package/src/tools/tool-approval-handler.ts +45 -4
  700. package/src/tools/types.ts +22 -3
  701. package/src/tools/ui-surface/surface-shape-docs.ts +1 -1
  702. package/src/tools/ui-surface/visual-validation.ts +2 -0
  703. package/src/usage/__tests__/subagent-attribution.test.ts +109 -0
  704. package/src/usage/subagent-attribution.ts +108 -0
  705. package/src/util/__tests__/sqlite-retry.test.ts +112 -0
  706. package/src/util/abort-reasons.ts +16 -0
  707. package/src/util/ansi.ts +44 -0
  708. package/src/util/disk-usage.ts +17 -13
  709. package/src/util/image-conversion.ts +20 -13
  710. package/src/util/schedule-source-key.ts +23 -0
  711. package/src/util/worker-process.ts +7 -1
  712. package/src/workspace/git-service.ts +33 -9
  713. package/src/workspace/migrations/140-repair-seed-pinned-memory-v3-live.ts +260 -0
  714. package/src/workspace/migrations/141-stt-english-default-to-multilingual.ts +111 -0
  715. package/src/workspace/migrations/__tests__/141-stt-english-default-to-multilingual.test.ts +126 -0
  716. package/src/workspace/migrations/registry.ts +4 -0
  717. package/src/workspace/top-level-renderer.ts +15 -2
  718. package/src/__tests__/tool-side-effects-slack-dm.test.ts +0 -357
  719. package/src/daemon/install-symlink.ts +0 -207
  720. package/src/notifications/deferred-emit.ts +0 -147
@@ -2,7 +2,8 @@
2
2
  * Shared outbound verification action logic.
3
3
  *
4
4
  * These functions encapsulate the business logic for starting, resending,
5
- * and cancelling outbound verification flows (Telegram, voice, Slack, email).
5
+ * and cancelling outbound verification flows (Telegram, voice, Slack, Discord,
6
+ * email).
6
7
  * They return transport-agnostic result objects and are consumed by both the
7
8
  * message handler (config-channels.ts) and the HTTP route layer (channel-verification-routes.ts).
8
9
  *
@@ -24,6 +25,8 @@ import {
24
25
  updateSessionStatus,
25
26
  } from "../channels/gateway-verification-sessions.js";
26
27
  import type { ChannelId } from "../channels/types.js";
28
+ import { openDiscordDmChannel } from "../messaging/providers/discord/api.js";
29
+ import { sendDiscordReply } from "../messaging/providers/discord/send.js";
27
30
  import { sendSlackReply } from "../messaging/providers/slack/send.js";
28
31
  import { sendTelegramReply } from "../messaging/providers/telegram-bot/send.js";
29
32
  import { getTelegramBotUsername } from "../telegram/bot-username.js";
@@ -32,10 +35,9 @@ import { normalizePhoneNumber } from "../util/phone.js";
32
35
  import { DAEMON_INTERNAL_ASSISTANT_ID } from "./assistant-scope.js";
33
36
  import { isGuardianBoundForChannel } from "./channel-verification-service.js";
34
37
  import {
35
- composeVerificationEmail,
36
- composeVerificationSlack,
37
- composeVerificationTelegram,
38
+ composeVerificationText,
38
39
  GUARDIAN_VERIFY_TEMPLATE_KEYS,
40
+ type TextVerifyTemplateKey,
39
41
  } from "./verification-templates.js";
40
42
 
41
43
  const log = getLogger("verification-outbound-actions");
@@ -113,7 +115,7 @@ interface CancelOutboundParams {
113
115
  * Maps 1:1 with the fields in ChannelVerificationSessionResponse minus the
114
116
  * `type` discriminant.
115
117
  */
116
- interface OutboundActionResult {
118
+ export interface OutboundActionResult {
117
119
  success: boolean;
118
120
  error?: string;
119
121
  message?: string;
@@ -127,19 +129,6 @@ interface OutboundActionResult {
127
129
  pendingBootstrap?: boolean;
128
130
  /** Echoed back so consumers know which conversation to target for pointers. */
129
131
  originConversationId?: string;
130
- /** Internal: Slack DM delivery payload for the caller to dispatch.
131
- * The shared startOutbound/resendOutbound functions no longer fire the
132
- * delivery themselves because CLI subprocesses are sandboxed and cannot
133
- * reach the gateway. The daemon HTTP route handler calls
134
- * deliverVerificationSlack() after receiving this payload. */
135
- _pendingSlackDm?: { userId: string; text: string; assistantId: string };
136
- /** Internal: email delivery payload for the caller to dispatch (same pattern as Slack). */
137
- _pendingEmail?: {
138
- to: string;
139
- text: string;
140
- subject: string;
141
- assistantId: string;
142
- };
143
132
  }
144
133
 
145
134
  // ---------------------------------------------------------------------------
@@ -244,16 +233,12 @@ export async function startOutbound(
244
233
  params.rebind,
245
234
  originConversationId,
246
235
  );
247
- } else if (channel === "slack") {
248
- return await startOutboundSlack(
249
- params.destination,
250
- assistantId,
251
- channel,
252
- params.rebind,
253
- originConversationId,
254
- );
255
- } else if (channel === "email") {
256
- return await startOutboundEmail(
236
+ }
237
+
238
+ const spec = textChannelSpec(channel);
239
+ if (spec) {
240
+ return await startOutboundTextChannel(
241
+ spec,
257
242
  params.destination,
258
243
  assistantId,
259
244
  channel,
@@ -265,7 +250,7 @@ export async function startOutbound(
265
250
  return {
266
251
  success: false,
267
252
  error: "unsupported_channel",
268
- message: `Outbound verification is not supported for ${channel}. Supported channels: Telegram, phone, Slack, email.`,
253
+ message: `Outbound verification is not supported for ${channel}. Supported channels: Telegram, phone, Slack, Discord, email.`,
269
254
  channel,
270
255
  };
271
256
  }
@@ -335,7 +320,7 @@ async function startOutboundTelegram(
335
320
  verificationPurpose: "guardian",
336
321
  });
337
322
 
338
- const telegramBody = composeVerificationTelegram(
323
+ const telegramBody = composeVerificationText(
339
324
  GUARDIAN_VERIFY_TEMPLATE_KEYS.TELEGRAM_CHALLENGE_REQUEST,
340
325
  {
341
326
  code: sessionResult.secret,
@@ -533,6 +518,38 @@ export function deliverVerificationSlack(
533
518
  deliverVerificationSlackAsync(userId, text, assistantId);
534
519
  }
535
520
 
521
+ // ---------------------------------------------------------------------------
522
+ // Discord delivery helper
523
+ // ---------------------------------------------------------------------------
524
+
525
+ /**
526
+ * Deliver a verification Discord DM via the Discord REST API directly.
527
+ * Fire-and-forget wrapper for use in the daemon process (HTTP route handlers).
528
+ *
529
+ * The recipient is a Discord *user* snowflake. Discord has no route that looks
530
+ * up an existing DM by recipient, so the channel is opened on the way through
531
+ * (see `openDiscordDmChannel`), and a user with DMs closed to server members
532
+ * fails here rather than at the gate.
533
+ */
534
+ export function deliverVerificationDiscord(
535
+ userId: string,
536
+ text: string,
537
+ assistantId: string,
538
+ ): void {
539
+ (async () => {
540
+ try {
541
+ const channelId = await openDiscordDmChannel(userId);
542
+ await sendDiscordReply({ channelId }, text);
543
+ log.info({ userId, assistantId }, "Verification Discord DM delivered");
544
+ } catch (err) {
545
+ log.error(
546
+ { err, userId, assistantId },
547
+ "Failed to deliver verification Discord DM",
548
+ );
549
+ }
550
+ })();
551
+ }
552
+
536
553
  // ---------------------------------------------------------------------------
537
554
  // Email delivery helper
538
555
  // ---------------------------------------------------------------------------
@@ -610,106 +627,196 @@ export function deliverVerificationEmail(
610
627
  })();
611
628
  }
612
629
 
613
- async function startOutboundSlack(
614
- destination: string | undefined,
615
- assistantId: string,
616
- channel: ChannelId,
617
- rebind?: boolean,
618
- originConversationId?: string,
619
- ): Promise<OutboundActionResult> {
620
- if (!destination) {
621
- return {
622
- success: false,
623
- error: "missing_destination",
624
- message: "A Slack user ID is required for outbound Slack verification.",
625
- channel,
626
- };
627
- }
630
+ /**
631
+ * The per-channel differences between the text-channel verification starts.
632
+ *
633
+ * Slack, Discord and email run the identical sequence: refuse without a
634
+ * destination, refuse when a guardian is already bound, check the destination
635
+ * rate window, mint a session, compose from a template, stamp the delivery,
636
+ * send. The spec carries only the per-channel fields; the sequence itself
637
+ * lives once in `startOutboundTextChannel`.
638
+ *
639
+ * Telegram and voice are deliberately absent. Telegram forks on whether the
640
+ * destination is a chat id or a handle, and a handle mints a bootstrap session
641
+ * carrying no code at all; voice normalizes a phone number and places a call
642
+ * rather than composing a message. Folding either in would mean a spec field
643
+ * that only one channel ever sets.
644
+ */
645
+ interface TextChannelVerificationSpec {
646
+ /** Rejection copy when the caller supplied no destination. */
647
+ missingDestination: string;
648
+ /** Rejection copy when the destination's rate window is exhausted. */
649
+ rateLimited: string;
650
+ /** Canonical form of the destination, for rate-limit keying and delivery. */
651
+ normalizeDestination?: (destination: string) => string;
652
+ /**
653
+ * Identity the consume path will require.
654
+ *
655
+ * Where a channel sets `expectedChatId`, it does no work: `checkIdentityMatch`
656
+ * requires the `expectedExternalUserId` match whenever both are set, and the
657
+ * resend path resolves its destination from `destinationAddress` first. See
658
+ * LUM-3110 for the wider question of session fields that record something
659
+ * nothing reads.
660
+ */
661
+ sessionIdentity: (destination: string) => {
662
+ expectedExternalUserId: string;
663
+ expectedChatId?: string;
664
+ };
665
+ challengeTemplateKey: TextVerifyTemplateKey;
666
+ resendTemplateKey: TextVerifyTemplateKey;
667
+ /** Fire-and-forget delivery. Throwing is the transport's business, not this file's. */
668
+ deliver: (destination: string, text: string, assistantId: string) => void;
669
+ }
628
670
 
629
- const alreadyBound = await isGuardianBoundForChannel(channel);
630
- if (alreadyBound && !rebind) {
631
- return {
632
- success: false,
633
- error: "already_bound",
634
- message:
635
- "A guardian is already bound for this channel. Set rebind: true to replace.",
636
- channel,
637
- };
638
- }
671
+ const TEXT_CHANNEL_VERIFICATION: Partial<
672
+ Record<ChannelId, TextChannelVerificationSpec>
673
+ > = {
674
+ slack: {
675
+ missingDestination:
676
+ "A Slack user ID is required for outbound Slack verification.",
677
+ rateLimited:
678
+ "Too many verification attempts to this Slack user. Please try again later.",
679
+ sessionIdentity: (destination) => ({
680
+ expectedExternalUserId: destination,
681
+ expectedChatId: destination,
682
+ }),
683
+ challengeTemplateKey: GUARDIAN_VERIFY_TEMPLATE_KEYS.SLACK_CHALLENGE_REQUEST,
684
+ resendTemplateKey: GUARDIAN_VERIFY_TEMPLATE_KEYS.SLACK_RESEND,
685
+ deliver: deliverVerificationSlack,
686
+ },
687
+ discord: {
688
+ missingDestination:
689
+ "A Discord user ID is required for outbound Discord verification.",
690
+ rateLimited:
691
+ "Too many verification attempts to this Discord user. Please try again later.",
692
+ // A Discord user snowflake is the session's only expected identity. There
693
+ // is no chat id to record: the DM channel does not exist until it is
694
+ // opened, and the guild channel the requester was seen in is a room rather
695
+ // than a person, so binding to it would accept the code from anyone
696
+ // standing in it.
697
+ sessionIdentity: (destination) => ({
698
+ expectedExternalUserId: destination,
699
+ }),
700
+ challengeTemplateKey:
701
+ GUARDIAN_VERIFY_TEMPLATE_KEYS.DISCORD_CHALLENGE_REQUEST,
702
+ resendTemplateKey: GUARDIAN_VERIFY_TEMPLATE_KEYS.DISCORD_RESEND,
703
+ deliver: deliverVerificationDiscord,
704
+ },
705
+ email: {
706
+ missingDestination:
707
+ "An email address is required for outbound email verification.",
708
+ rateLimited:
709
+ "Too many verification attempts to this email address. Please try again later.",
710
+ normalizeDestination: (destination) => destination.trim().toLowerCase(),
711
+ sessionIdentity: (destination) => ({
712
+ expectedExternalUserId: destination,
713
+ expectedChatId: destination,
714
+ }),
715
+ challengeTemplateKey: GUARDIAN_VERIFY_TEMPLATE_KEYS.EMAIL_CHALLENGE_REQUEST,
716
+ resendTemplateKey: GUARDIAN_VERIFY_TEMPLATE_KEYS.EMAIL_RESEND,
717
+ deliver: (to, text, assistantId) =>
718
+ deliverVerificationEmail(
719
+ to,
720
+ text,
721
+ EMAIL_VERIFICATION_SUBJECT,
722
+ assistantId,
723
+ ),
724
+ },
725
+ };
639
726
 
640
- const recentSendCount = await countRecentSendsToDestination(
727
+ /**
728
+ * The spec for a channel, or undefined when it has none.
729
+ *
730
+ * An own-property check rather than a bare index read: `channel` reaches here
731
+ * as a plain string, so `"constructor"`, `"toString"` and `"__proto__"` would
732
+ * otherwise resolve to inherited values, pass a truthy guard, and throw on the
733
+ * first spec field instead of falling through to `unsupported_channel`. Same
734
+ * fail-closed shape as `resolveCapabilities`.
735
+ */
736
+ function textChannelSpec(
737
+ channel: ChannelId,
738
+ ): TextChannelVerificationSpec | undefined {
739
+ return Object.prototype.hasOwnProperty.call(
740
+ TEXT_CHANNEL_VERIFICATION,
641
741
  channel,
642
- destination,
643
- DESTINATION_RATE_WINDOW_MS,
644
- );
645
- if (recentSendCount >= MAX_SENDS_PER_DESTINATION_WINDOW) {
646
- return {
647
- success: false,
648
- error: "rate_limited",
649
- message:
650
- "Too many verification attempts to this Slack user. Please try again later.",
651
- channel,
652
- };
653
- }
742
+ )
743
+ ? TEXT_CHANNEL_VERIFICATION[channel]
744
+ : undefined;
745
+ }
654
746
 
655
- const sessionResult = await createOutboundSession({
747
+ /**
748
+ * Mint a session, compose the message, stamp the delivery, and send it.
749
+ *
750
+ * Shared by the start and resend paths, which differ only in the template and
751
+ * in how the send counters advance.
752
+ */
753
+ interface MintedSend {
754
+ sessionId: string;
755
+ secret: string;
756
+ expiresAt: number;
757
+ nextResendAt: number;
758
+ }
759
+
760
+ async function mintAndSend(params: {
761
+ spec: TextChannelVerificationSpec;
762
+ channel: ChannelId;
763
+ destination: string;
764
+ templateKey: TextVerifyTemplateKey;
765
+ sendCount: number;
766
+ assistantId: string;
767
+ }): Promise<MintedSend> {
768
+ const { spec, channel, destination, templateKey, sendCount, assistantId } =
769
+ params;
770
+
771
+ const session = await createOutboundSession({
656
772
  channel,
657
- expectedExternalUserId: destination,
658
- expectedChatId: destination,
773
+ ...spec.sessionIdentity(destination),
659
774
  identityBindingStatus: "bound",
660
775
  destinationAddress: destination,
661
776
  verificationPurpose: "guardian",
662
777
  });
663
778
 
664
- const slackBody = composeVerificationSlack(
665
- GUARDIAN_VERIFY_TEMPLATE_KEYS.SLACK_CHALLENGE_REQUEST,
666
- {
667
- code: sessionResult.secret,
668
- expiresInMinutes: Math.floor(SESSION_TTL_SECONDS / 60),
669
- },
670
- );
779
+ const body = composeVerificationText(templateKey, {
780
+ code: session.secret,
781
+ expiresInMinutes: Math.floor(SESSION_TTL_SECONDS / 60),
782
+ });
671
783
 
672
784
  const now = Date.now();
673
785
  const nextResendAt = now + RESEND_COOLDOWN_MS;
674
- const sendCount = 1;
675
-
676
- await updateSessionDelivery(
677
- sessionResult.sessionId,
678
- now,
679
- sendCount,
680
- nextResendAt,
681
- );
786
+ await updateSessionDelivery(session.sessionId, now, sendCount, nextResendAt);
787
+ spec.deliver(destination, body, assistantId);
682
788
 
683
789
  return {
684
- success: true,
685
- verificationSessionId: sessionResult.sessionId,
686
- secret: sessionResult.secret,
687
- expiresAt: sessionResult.expiresAt,
790
+ sessionId: session.sessionId,
791
+ secret: session.secret,
792
+ expiresAt: session.expiresAt,
688
793
  nextResendAt,
689
- sendCount,
690
- channel,
691
- originConversationId,
692
- _pendingSlackDm: { userId: destination, text: slackBody, assistantId },
693
794
  };
694
795
  }
695
796
 
696
- async function startOutboundEmail(
697
- destination: string | undefined,
797
+ /**
798
+ * Start outbound guardian verification on a channel that reaches its
799
+ * destination with one text message.
800
+ */
801
+ async function startOutboundTextChannel(
802
+ spec: TextChannelVerificationSpec,
803
+ rawDestination: string | undefined,
698
804
  assistantId: string,
699
805
  channel: ChannelId,
700
806
  rebind?: boolean,
701
807
  originConversationId?: string,
702
808
  ): Promise<OutboundActionResult> {
703
- if (!destination) {
809
+ if (!rawDestination) {
704
810
  return {
705
811
  success: false,
706
812
  error: "missing_destination",
707
- message: "An email address is required for outbound email verification.",
813
+ message: spec.missingDestination,
708
814
  channel,
709
815
  };
710
816
  }
711
-
712
- const normalizedEmail = destination.trim().toLowerCase();
817
+ const destination = spec.normalizeDestination
818
+ ? spec.normalizeDestination(rawDestination)
819
+ : rawDestination;
713
820
 
714
821
  const alreadyBound = await isGuardianBoundForChannel(channel);
715
822
  if (alreadyBound && !rebind) {
@@ -724,62 +831,36 @@ async function startOutboundEmail(
724
831
 
725
832
  const recentSendCount = await countRecentSendsToDestination(
726
833
  channel,
727
- normalizedEmail,
834
+ destination,
728
835
  DESTINATION_RATE_WINDOW_MS,
729
836
  );
730
837
  if (recentSendCount >= MAX_SENDS_PER_DESTINATION_WINDOW) {
731
838
  return {
732
839
  success: false,
733
840
  error: "rate_limited",
734
- message:
735
- "Too many verification attempts to this email address. Please try again later.",
841
+ message: spec.rateLimited,
736
842
  channel,
737
843
  };
738
844
  }
739
845
 
740
- const sessionResult = await createOutboundSession({
846
+ const sent = await mintAndSend({
847
+ spec,
741
848
  channel,
742
- expectedExternalUserId: normalizedEmail,
743
- expectedChatId: normalizedEmail,
744
- identityBindingStatus: "bound",
745
- destinationAddress: normalizedEmail,
746
- verificationPurpose: "guardian",
849
+ destination,
850
+ templateKey: spec.challengeTemplateKey,
851
+ sendCount: 1,
852
+ assistantId,
747
853
  });
748
854
 
749
- const emailBody = composeVerificationEmail(
750
- GUARDIAN_VERIFY_TEMPLATE_KEYS.EMAIL_CHALLENGE_REQUEST,
751
- {
752
- code: sessionResult.secret,
753
- expiresInMinutes: Math.floor(SESSION_TTL_SECONDS / 60),
754
- },
755
- );
756
-
757
- const now = Date.now();
758
- const nextResendAt = now + RESEND_COOLDOWN_MS;
759
- const sendCount = 1;
760
-
761
- await updateSessionDelivery(
762
- sessionResult.sessionId,
763
- now,
764
- sendCount,
765
- nextResendAt,
766
- );
767
-
768
855
  return {
769
856
  success: true,
770
- verificationSessionId: sessionResult.sessionId,
771
- secret: sessionResult.secret,
772
- expiresAt: sessionResult.expiresAt,
773
- nextResendAt,
774
- sendCount,
857
+ verificationSessionId: sent.sessionId,
858
+ secret: sent.secret,
859
+ expiresAt: sent.expiresAt,
860
+ nextResendAt: sent.nextResendAt,
861
+ sendCount: 1,
775
862
  channel,
776
863
  originConversationId,
777
- _pendingEmail: {
778
- to: normalizedEmail,
779
- text: emailBody,
780
- subject: EMAIL_VERIFICATION_SUBJECT,
781
- assistantId,
782
- },
783
864
  };
784
865
  }
785
866
 
@@ -794,7 +875,13 @@ export async function resendOutbound(
794
875
  const channel = params.channel;
795
876
  const originConversationId = params.originConversationId;
796
877
 
797
- const session = await findActiveSession(channel);
878
+ // Scoped to the guardian's own flow. A channel can carry a live session per
879
+ // person verifying, so an unscoped lookup returns whoever started most
880
+ // recently, which on a busy channel is a requester rather than the guardian
881
+ // this resend is for.
882
+ const session = await findActiveSession(channel, {
883
+ verificationPurpose: "guardian",
884
+ });
798
885
  if (!session) {
799
886
  return {
800
887
  success: false,
@@ -833,14 +920,15 @@ export async function resendOutbound(
833
920
  };
834
921
  }
835
922
 
836
- const resendDestination =
923
+ const destination =
837
924
  session.destinationAddress ??
838
925
  session.expectedPhoneE164 ??
839
926
  session.expectedChatId;
840
- if (resendDestination) {
927
+
928
+ if (destination) {
841
929
  const recentDestSends = await countRecentSendsToDestination(
842
930
  channel,
843
- resendDestination,
931
+ destination,
844
932
  DESTINATION_RATE_WINDOW_MS,
845
933
  );
846
934
  if (recentDestSends >= MAX_SENDS_PER_DESTINATION_WINDOW) {
@@ -854,10 +942,6 @@ export async function resendOutbound(
854
942
  }
855
943
  }
856
944
 
857
- const destination =
858
- session.destinationAddress ??
859
- session.expectedPhoneE164 ??
860
- session.expectedChatId;
861
945
  if (!destination) {
862
946
  return {
863
947
  success: false,
@@ -876,7 +960,7 @@ export async function resendOutbound(
876
960
  verificationPurpose: "guardian",
877
961
  });
878
962
 
879
- const telegramBody = composeVerificationTelegram(
963
+ const telegramBody = composeVerificationText(
880
964
  GUARDIAN_VERIFY_TEMPLATE_KEYS.TELEGRAM_RESEND,
881
965
  {
882
966
  code: newSession.secret,
@@ -941,95 +1025,35 @@ export async function resendOutbound(
941
1025
  channel,
942
1026
  originConversationId,
943
1027
  };
944
- } else if (channel === "slack") {
945
- const newSession = await createOutboundSession({
946
- channel,
947
- expectedExternalUserId: destination,
948
- expectedChatId: destination,
949
- identityBindingStatus: "bound",
950
- destinationAddress: destination,
951
- verificationPurpose: "guardian",
952
- });
953
-
954
- const slackBody = composeVerificationSlack(
955
- GUARDIAN_VERIFY_TEMPLATE_KEYS.SLACK_RESEND,
956
- {
957
- code: newSession.secret,
958
- expiresInMinutes: Math.floor(SESSION_TTL_SECONDS / 60),
959
- },
960
- );
1028
+ }
961
1029
 
962
- const now = Date.now();
1030
+ const spec = textChannelSpec(channel);
1031
+ if (spec) {
963
1032
  const newSendCount = currentSendCount + 1;
964
- const nextResendAt = now + RESEND_COOLDOWN_MS;
965
-
966
- await updateSessionDelivery(
967
- newSession.sessionId,
968
- now,
969
- newSendCount,
970
- nextResendAt,
971
- );
972
-
973
- return {
974
- success: true,
975
- verificationSessionId: newSession.sessionId,
976
- secret: newSession.secret,
977
- nextResendAt,
978
- sendCount: newSendCount,
979
- channel,
980
- originConversationId,
981
- _pendingSlackDm: { userId: destination, text: slackBody, assistantId },
982
- };
983
- } else if (channel === "email") {
984
- const newSession = await createOutboundSession({
1033
+ const sent = await mintAndSend({
1034
+ spec,
985
1035
  channel,
986
- expectedExternalUserId: destination,
987
- expectedChatId: destination,
988
- identityBindingStatus: "bound",
989
- destinationAddress: destination,
990
- verificationPurpose: "guardian",
1036
+ destination,
1037
+ templateKey: spec.resendTemplateKey,
1038
+ sendCount: newSendCount,
1039
+ assistantId,
991
1040
  });
992
1041
 
993
- const emailBody = composeVerificationEmail(
994
- GUARDIAN_VERIFY_TEMPLATE_KEYS.EMAIL_RESEND,
995
- {
996
- code: newSession.secret,
997
- expiresInMinutes: Math.floor(SESSION_TTL_SECONDS / 60),
998
- },
999
- );
1000
-
1001
- const now = Date.now();
1002
- const newSendCount = currentSendCount + 1;
1003
- const nextResendAt = now + RESEND_COOLDOWN_MS;
1004
-
1005
- await updateSessionDelivery(
1006
- newSession.sessionId,
1007
- now,
1008
- newSendCount,
1009
- nextResendAt,
1010
- );
1011
-
1012
1042
  return {
1013
1043
  success: true,
1014
- verificationSessionId: newSession.sessionId,
1015
- secret: newSession.secret,
1016
- nextResendAt,
1044
+ verificationSessionId: sent.sessionId,
1045
+ secret: sent.secret,
1046
+ nextResendAt: sent.nextResendAt,
1017
1047
  sendCount: newSendCount,
1018
1048
  channel,
1019
1049
  originConversationId,
1020
- _pendingEmail: {
1021
- to: destination,
1022
- text: emailBody,
1023
- subject: EMAIL_VERIFICATION_SUBJECT,
1024
- assistantId,
1025
- },
1026
1050
  };
1027
1051
  }
1028
1052
 
1029
1053
  return {
1030
1054
  success: false,
1031
1055
  error: "unsupported_channel",
1032
- message: `Resend is only supported for Telegram, phone, Slack, and email. Got: ${channel}`,
1056
+ message: `Resend is only supported for Telegram, phone, Slack, Discord, and email. Got: ${channel}`,
1033
1057
  channel,
1034
1058
  };
1035
1059
  }
@@ -1043,7 +1067,11 @@ export async function cancelOutbound(
1043
1067
  ): Promise<OutboundActionResult> {
1044
1068
  const channel = params.channel;
1045
1069
 
1046
- const session = await findActiveSession(channel);
1070
+ // Scoped the same way as resend: cancelling the guardian's verification must
1071
+ // not revoke a requester's live session.
1072
+ const session = await findActiveSession(channel, {
1073
+ verificationPurpose: "guardian",
1074
+ });
1047
1075
  if (!session) {
1048
1076
  return {
1049
1077
  success: false,