@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
@@ -50,6 +50,20 @@ const CredentialPromptParams = z.object({
50
50
  // Response type (shared with CLI consumer)
51
51
  // ---------------------------------------------------------------------------
52
52
 
53
+ /**
54
+ * Appended to every terminal success message.
55
+ *
56
+ * The prompt blocks until the user answers, so by the time a caller reads the
57
+ * result the secure input is already gone from the UI. A bare `ok: true` reads
58
+ * as "the prompt opened successfully", which leads a model to follow up with
59
+ * "the prompt is open, paste your value there" — pointing the user at a surface
60
+ * that has already closed. The user, having just submitted, re-answers a prompt
61
+ * that never reappears, and the flow loops. State the close explicitly so the
62
+ * only available reading is the correct one.
63
+ */
64
+ const PROMPT_CLOSED_NOTICE =
65
+ "The secure prompt has closed — do not ask the user to enter this value again.";
66
+
53
67
  export type CredentialPromptResult = {
54
68
  ok: boolean;
55
69
  /**
@@ -204,19 +218,26 @@ async function handleCredentialPrompt({ body = {} }: RouteHandlerArgs) {
204
218
  ok: true,
205
219
  service: validated.service,
206
220
  field: validated.field,
207
- message: `One-time credential provided for ${validated.service}/${validated.field}. The value was not saved and will be consumed by the next operation.`,
221
+ message: `One-time credential provided for ${validated.service}/${validated.field}. The value was not saved and will be consumed by the next operation. ${PROMPT_CLOSED_NOTICE}`,
208
222
  };
209
223
  }
210
224
 
225
+ // A Slack channel token is routed through the channel config rather than the
226
+ // vault, so its connection status is what describes the outcome; every other
227
+ // credential reports the vault write.
211
228
  const slackStatus = persisted.slackChannel
212
229
  ? formatSlackChannelStatus(persisted.slackChannel).trim()
213
230
  : "";
231
+ const outcomeSummary =
232
+ slackStatus.length > 0
233
+ ? slackStatus
234
+ : `Credential received from the user and stored as ${validated.service}/${validated.field}.`;
214
235
 
215
236
  return {
216
237
  ok: true,
217
238
  service: validated.service,
218
239
  field: validated.field,
219
- message: slackStatus.length > 0 ? slackStatus : undefined,
240
+ message: `${outcomeSummary} ${PROMPT_CLOSED_NOTICE}`,
220
241
  };
221
242
  }
222
243
 
@@ -50,7 +50,10 @@ function getDebugInfo() {
50
50
  // Debug view counts every standard conversation, archived or not, so the
51
51
  // diagnostics report doesn't undercount after the route-level default
52
52
  // moved to "active".
53
- const conversationCount = countConversations("standard", "all");
53
+ const conversationCount = countConversations({
54
+ conversationType: "standard",
55
+ archiveStatus: "all",
56
+ });
54
57
  const memoryItemCount = getMemoryItemCount();
55
58
  const dbSizeBytes = getDatabaseSizeBytes();
56
59
 
@@ -25,22 +25,18 @@ import {
25
25
  DEFAULT_PROVIDER_CHOICES,
26
26
  DefaultProviderSchema,
27
27
  } from "../../config/schemas/llm.js";
28
- import { computeConnectionAvailability } from "../../providers/inference/connection-availability.js";
28
+ import {
29
+ computeConnectionAvailability,
30
+ CONNECTION_AVAILABILITY_STATUSES,
31
+ } from "../../providers/inference/connection-availability.js";
29
32
  import { ACTOR_PRINCIPALS } from "../auth/route-policy.js";
30
33
  import { BadRequestError } from "./errors.js";
31
34
  import type { RouteDefinition, RouteHandlerArgs } from "./types.js";
32
35
 
33
36
  const availabilitySchema = z.object({
34
- status: z.enum([
35
- "ok",
36
- "missing_default",
37
- "missing_connection",
38
- "missing_credential",
39
- "provider_mismatch",
40
- "unsupported_auth",
41
- "vellum_unauthenticated",
42
- "unknown",
43
- ]),
37
+ // `missing_default` is this route's own verdict (no default provider is
38
+ // configured at all), so it extends the shared set rather than living in it.
39
+ status: z.enum([...CONNECTION_AVAILABILITY_STATUSES, "missing_default"]),
44
40
  /** Present on every non-`ok` status: names the broken thing and the fix. */
45
41
  message: z.string().optional(),
46
42
  });
@@ -16,12 +16,17 @@ import {
16
16
  getDocumentById,
17
17
  getDocumentByWorkspacePath,
18
18
  getDocumentsForConversation,
19
+ isDocumentAssociatedWithConversation,
19
20
  refreshDocumentContentFromFile,
20
21
  saveDocument,
21
22
  } from "../../documents/document-store.js";
22
23
  import { rawAll } from "../../persistence/raw-query.js";
23
24
  import { getLogger } from "../../util/logger.js";
24
25
  import { ACTOR_PRINCIPALS } from "../auth/route-policy.js";
26
+ import {
27
+ getOriginClientId,
28
+ publishDocumentsChanged,
29
+ } from "../sync/resource-sync-events.js";
25
30
  import { renderMarkdownToPDF } from "./document-pdf-renderer.js";
26
31
  import {
27
32
  BadRequestError,
@@ -159,6 +164,12 @@ function loadDocumentPayload(surfaceId: string): Record<string, unknown> {
159
164
  * content has drifted from the bytes on disk (the file was edited outside the
160
165
  * editor), the row is refreshed from the file before it is returned. Writes go
161
166
  * the other way — the document store writes through to the file.
167
+ *
168
+ * The publishes here carry no origin client id on purpose. The only caller,
169
+ * `viewer-store.loadWorkspaceFileDocument`, opens the file and never
170
+ * invalidates its own document queries, so the client that creates the row is
171
+ * the one that most needs to hear about it: suppressing its own echo would
172
+ * leave its assets pill and Library without the document it just opened.
162
173
  */
163
174
  function handleDocumentForWorkspaceFile({
164
175
  body,
@@ -194,8 +205,13 @@ function handleDocumentForWorkspaceFile({
194
205
  if (existing) {
195
206
  // Opening the file from another conversation grants that conversation
196
207
  // access, the same association the link route writes.
208
+ const alreadyLinked = isDocumentAssociatedWithConversation(
209
+ existing.surfaceId,
210
+ conversationId,
211
+ );
197
212
  addDocumentConversation(existing.surfaceId, conversationId);
198
213
 
214
+ let refreshed = false;
199
215
  if (existing.content !== fileText) {
200
216
  const refresh = refreshDocumentContentFromFile(
201
217
  existing.surfaceId,
@@ -204,11 +220,18 @@ function handleDocumentForWorkspaceFile({
204
220
  if (!refresh.success) {
205
221
  throw new InternalError(refresh.error);
206
222
  }
223
+ refreshed = true;
207
224
  log.info(
208
225
  { surfaceId: existing.surfaceId, workspacePath: relativePath },
209
226
  "Refreshed file-backed document from disk",
210
227
  );
211
228
  }
229
+
230
+ // Reopening an unchanged, already-linked file writes nothing, so it must
231
+ // not invalidate every client's document list on each open.
232
+ if (!alreadyLinked || refreshed) {
233
+ publishDocumentsChanged();
234
+ }
212
235
  return loadDocumentPayload(existing.surfaceId);
213
236
  }
214
237
 
@@ -227,7 +250,14 @@ function handleDocumentForWorkspaceFile({
227
250
  if (!raced) {
228
251
  throw new InternalError(created.error);
229
252
  }
253
+ const racedAlreadyLinked = isDocumentAssociatedWithConversation(
254
+ raced.surfaceId,
255
+ conversationId,
256
+ );
230
257
  addDocumentConversation(raced.surfaceId, conversationId);
258
+ if (!racedAlreadyLinked) {
259
+ publishDocumentsChanged();
260
+ }
231
261
  return loadDocumentPayload(raced.surfaceId);
232
262
  }
233
263
 
@@ -235,6 +265,7 @@ function handleDocumentForWorkspaceFile({
235
265
  { surfaceId, workspacePath: relativePath, conversationId },
236
266
  "Bound workspace file to a new document",
237
267
  );
268
+ publishDocumentsChanged();
238
269
  return loadDocumentPayload(surfaceId);
239
270
  }
240
271
 
@@ -325,7 +356,7 @@ export const ROUTES: RouteDefinition[] = [
325
356
  success: z.literal(true),
326
357
  surfaceId: z.string(),
327
358
  }),
328
- handler: ({ body }) => {
359
+ handler: ({ body, headers }) => {
329
360
  const { surfaceId, conversationId, title, content, wordCount } = (body ??
330
361
  {}) as {
331
362
  surfaceId?: string;
@@ -362,6 +393,12 @@ export const ROUTES: RouteDefinition[] = [
362
393
  if (!result.success) {
363
394
  throw new InternalError(result.error);
364
395
  }
396
+ // Every save moves `updated_at`, which both document lists order by and
397
+ // the Library renders next to the word count, so a content-only save
398
+ // changes what the list surfaces show. The publish coalesces, which
399
+ // bounds the web editor's per-keystroke autosave to one broadcast per
400
+ // second, and the saving client suppresses its own echo by origin id.
401
+ publishDocumentsChanged(getOriginClientId(headers));
365
402
  return result;
366
403
  },
367
404
  },
@@ -426,6 +463,12 @@ export const ROUTES: RouteDefinition[] = [
426
463
  { surfaceId: pathParams!.id, conversationId },
427
464
  "Linked document to conversation",
428
465
  );
466
+ // No origin client id: the only caller, `document-viewer-page`, links the
467
+ // document and then navigates to the conversation whose assets pill must
468
+ // now list it, without invalidating anything itself. Suppressing its own
469
+ // echo would land it on a conversation whose pill is missing the document
470
+ // it just linked.
471
+ publishDocumentsChanged();
429
472
  return { success: true as const };
430
473
  },
431
474
  },
@@ -42,7 +42,10 @@ import type { ReplaySubscriber } from "../assistant-stream-state.js";
42
42
  import { getReplayWindow } from "../assistant-stream-state.js";
43
43
  import { ACTOR_PRINCIPALS, GATEWAY_PRINCIPALS } from "../auth/route-policy.js";
44
44
  import { DEFAULT_HEARTBEAT_INTERVAL_MS } from "../client-health.js";
45
- import { resolveActorPrincipalIdForLocalGuardianSync } from "../local-actor-identity.js";
45
+ import {
46
+ resolveActorPrincipalIdForLocalGuardian,
47
+ resolveActorPrincipalIdForLocalGuardianSync,
48
+ } from "../local-actor-identity.js";
46
49
  import {
47
50
  BadRequestError,
48
51
  NotFoundError,
@@ -452,6 +455,30 @@ export function handleSubscribeAssistantEvents(
452
455
  throw err;
453
456
  }
454
457
 
458
+ // Self-heal for dev-bypass connections: the sync resolution above reads
459
+ // only the guardian-delivery cache, which can be cold at connect time.
460
+ // Without this, the subscription would carry no principal for its whole
461
+ // lifetime and every host-proxy result would 403. Fire-and-forget so the
462
+ // stream is not delayed; keyed by connectionId so a reconnect race cannot
463
+ // patch the subscription that replaced this one.
464
+ if (
465
+ clientId &&
466
+ interfaceId &&
467
+ actorPrincipalId == null &&
468
+ rawActorPrincipalId?.trim() === "dev-bypass"
469
+ ) {
470
+ void resolveActorPrincipalIdForLocalGuardian("dev-bypass")
471
+ .then((resolved) => {
472
+ if (resolved) {
473
+ hub.fillClientActorPrincipalId(sub.connectionId, resolved);
474
+ }
475
+ })
476
+ .catch(() => {
477
+ // Best-effort: an unreachable gateway leaves the record unhealed;
478
+ // the next reconnect retries.
479
+ });
480
+ }
481
+
455
482
  const stream = new ReadableStream<Uint8Array>(
456
483
  {
457
484
  start(controller) {
@@ -19,6 +19,7 @@ import type {
19
19
  HostAppControlResultPayload,
20
20
  HostAppControlState,
21
21
  } from "../../daemon/message-types/host-app-control.js";
22
+ import { assistantEventHub } from "../assistant-event-hub.js";
22
23
  import { ACTOR_PRINCIPALS } from "../auth/route-policy.js";
23
24
  import {
24
25
  enforceSameActorOrThrow,
@@ -118,6 +119,7 @@ async function handleHostAppControlResult({ body, headers }: RouteHandlerArgs) {
118
119
  targetActorPrincipalId: peeked.targetActorPrincipalId,
119
120
  targetClientId: peeked.targetClientId,
120
121
  op: "host_app_control",
122
+ hubForMissingTarget: assistantEventHub,
121
123
  });
122
124
  }
123
125
 
@@ -7,6 +7,7 @@
7
7
  import { z } from "zod";
8
8
 
9
9
  import { HostBashProxy } from "../../daemon/host-bash-proxy.js";
10
+ import { assistantEventHub } from "../assistant-event-hub.js";
10
11
  import { ACTOR_PRINCIPALS } from "../auth/route-policy.js";
11
12
  import {
12
13
  enforceSameActorOrThrow,
@@ -99,6 +100,7 @@ async function handleHostBashResult({ body, headers }: RouteHandlerArgs) {
99
100
  targetActorPrincipalId: peeked.targetActorPrincipalId,
100
101
  targetClientId,
101
102
  op: "host_bash",
103
+ hubForMissingTarget: assistantEventHub,
102
104
  });
103
105
  }
104
106
 
@@ -11,6 +11,7 @@ import {
11
11
  publishCdpEvent,
12
12
  } from "../../browser-session/events.js";
13
13
  import { clearPinnedTabByTabId } from "../../tools/browser/pinned-tabs.js";
14
+ import { assistantEventHub } from "../assistant-event-hub.js";
14
15
  import { ACTOR_PRINCIPALS } from "../auth/route-policy.js";
15
16
  import {
16
17
  enforceSameActorOrThrow,
@@ -137,6 +138,7 @@ export async function resolveHostBrowserResultByRequestId(
137
138
  targetActorPrincipalId: peeked.targetActorPrincipalId,
138
139
  targetClientId: peeked.targetClientId,
139
140
  op: "host_browser",
141
+ hubForMissingTarget: assistantEventHub,
140
142
  });
141
143
  } catch (err) {
142
144
  // enforceSameActorOrThrow throws ForbiddenError on rejection.
@@ -7,6 +7,7 @@
7
7
  import { z } from "zod";
8
8
 
9
9
  import { findConversation } from "../../daemon/conversation-registry.js";
10
+ import { assistantEventHub } from "../assistant-event-hub.js";
10
11
  import { ACTOR_PRINCIPALS } from "../auth/route-policy.js";
11
12
  import {
12
13
  enforceSameActorOrThrow,
@@ -107,6 +108,7 @@ async function handleHostCuResult({ body, headers }: RouteHandlerArgs) {
107
108
  targetActorPrincipalId: peeked.targetActorPrincipalId,
108
109
  targetClientId: peeked.targetClientId,
109
110
  op: "host_cu",
111
+ hubForMissingTarget: assistantEventHub,
110
112
  });
111
113
  }
112
114
 
@@ -7,6 +7,7 @@
7
7
  import { z } from "zod";
8
8
 
9
9
  import { HostFileProxy } from "../../daemon/host-file-proxy.js";
10
+ import { assistantEventHub } from "../assistant-event-hub.js";
10
11
  import { ACTOR_PRINCIPALS } from "../auth/route-policy.js";
11
12
  import {
12
13
  enforceSameActorOrThrow,
@@ -97,6 +98,7 @@ async function handleHostFileResult({ body, headers }: RouteHandlerArgs) {
97
98
  targetActorPrincipalId: peeked.targetActorPrincipalId,
98
99
  targetClientId: peeked.targetClientId,
99
100
  op: "host_file",
101
+ hubForMissingTarget: assistantEventHub,
100
102
  });
101
103
  }
102
104
 
@@ -8,6 +8,7 @@
8
8
  import { z } from "zod";
9
9
 
10
10
  import { HostTransferProxy } from "../../daemon/host-transfer-proxy.js";
11
+ import { assistantEventHub } from "../assistant-event-hub.js";
11
12
  import { ACTOR_PRINCIPALS } from "../auth/route-policy.js";
12
13
  import {
13
14
  enforceSameActorOrThrow,
@@ -83,6 +84,7 @@ async function handleTransferContentGet({
83
84
  match.proxy.getTargetActorPrincipalIdForTransfer(transferId),
84
85
  targetClientId,
85
86
  op: "host_transfer",
87
+ hubForMissingTarget: assistantEventHub,
86
88
  });
87
89
  }
88
90
 
@@ -168,6 +170,7 @@ async function handleTransferContentPut({
168
170
  match.proxy.getTargetActorPrincipalIdForTransfer(transferId),
169
171
  targetClientId,
170
172
  op: "host_transfer",
173
+ hubForMissingTarget: assistantEventHub,
171
174
  });
172
175
  }
173
176
 
@@ -240,6 +243,7 @@ async function handleTransferResult({ body, headers }: RouteHandlerArgs) {
240
243
  targetActorPrincipalId: peeked.targetActorPrincipalId,
241
244
  targetClientId: peeked.targetClientId,
242
245
  op: "host_transfer",
246
+ hubForMissingTarget: assistantEventHub,
243
247
  });
244
248
  }
245
249
 
@@ -230,15 +230,15 @@ export function handleHealth(): Response {
230
230
  }
231
231
 
232
232
  /** Disk usage for the health payload; null when it can't be measured. */
233
- function sampleDiskUsageInfo(): ReturnType<typeof getDiskUsageInfo> {
233
+ async function sampleDiskUsageInfo(): ReturnType<typeof getDiskUsageInfo> {
234
234
  try {
235
- return getDiskUsageInfo();
235
+ return await getDiskUsageInfo();
236
236
  } catch {
237
237
  return null;
238
238
  }
239
239
  }
240
240
 
241
- function getDetailedHealth() {
241
+ async function getDetailedHealth() {
242
242
  let profiler: ReturnType<typeof getProfilerRuntimeStatus> | undefined;
243
243
  try {
244
244
  profiler = getProfilerRuntimeStatus();
@@ -260,7 +260,7 @@ function getDetailedHealth() {
260
260
  status: "healthy",
261
261
  timestamp: new Date().toISOString(),
262
262
  version: APP_VERSION,
263
- disk: sampleDiskUsageInfo(),
263
+ disk: await sampleDiskUsageInfo(),
264
264
  memory: getMemoryInfo(),
265
265
  cpu: getCpuInfo(),
266
266
  migrations: {
@@ -280,8 +280,8 @@ function getDetailedHealth() {
280
280
  };
281
281
  }
282
282
 
283
- export function handleDetailedHealth(): Response {
284
- return Response.json(getDetailedHealth());
283
+ export async function handleDetailedHealth(): Promise<Response> {
284
+ return Response.json(await getDetailedHealth());
285
285
  }
286
286
 
287
287
  type UnreadyDbMigrationReadiness = Extract<
@@ -1816,7 +1816,7 @@ async function persistBackfilledSlackMessage(params: {
1816
1816
  );
1817
1817
  continue;
1818
1818
  }
1819
- attachInlineAttachmentToMessage(
1819
+ await attachInlineAttachmentToMessage(
1820
1820
  persisted.id,
1821
1821
  i,
1822
1822
  downloaded.filename,
@@ -1863,21 +1863,21 @@ async function persistBackfilledSlackMessage(params: {
1863
1863
  updateMessageContent(
1864
1864
  persisted.id,
1865
1865
  JSON.stringify(
1866
- buildBackfilledSlackContentBlocks(rawText, hydratedAttachments),
1866
+ await buildBackfilledSlackContentBlocks(rawText, hydratedAttachments),
1867
1867
  ),
1868
1868
  );
1869
1869
  }
1870
1870
  }
1871
1871
 
1872
- function buildBackfilledSlackContentBlocks(
1872
+ async function buildBackfilledSlackContentBlocks(
1873
1873
  text: string,
1874
1874
  attachments: MessageAttachmentInput[],
1875
- ): ContentBlock[] {
1875
+ ): Promise<ContentBlock[]> {
1876
1876
  const blocks: ContentBlock[] = [];
1877
1877
  if (text.trim().length > 0) {
1878
1878
  blocks.push({ type: "text", text });
1879
1879
  }
1880
- blocks.push(...attachmentsToContentBlocks(attachments));
1880
+ blocks.push(...(await attachmentsToContentBlocks(attachments)));
1881
1881
  return blocks;
1882
1882
  }
1883
1883
 
@@ -980,7 +980,12 @@ async function initiateVerificationChallenge(params: {
980
980
  try {
981
981
  [existingChallenge, existingSession] = await Promise.all([
982
982
  getPendingSession(sourceChannel),
983
- findActiveSession(sourceChannel),
983
+ // Scoped to this sender. Reading the channel's latest and comparing
984
+ // its `expectedExternalUserId` misses their session whenever somebody
985
+ // else started more recently.
986
+ findActiveSession(sourceChannel, {
987
+ expectedExternalUserId: senderUserId,
988
+ }),
984
989
  ]);
985
990
  } catch (err) {
986
991
  log.warn(
@@ -992,8 +997,7 @@ async function initiateVerificationChallenge(params: {
992
997
  const senderHasPending =
993
998
  (existingChallenge &&
994
999
  existingChallenge.expectedExternalUserId === senderUserId) ||
995
- (existingSession &&
996
- existingSession.expectedExternalUserId === senderUserId);
1000
+ existingSession != null;
997
1001
  if (senderHasPending) {
998
1002
  log.debug(
999
1003
  {
@@ -386,6 +386,20 @@ export function processChannelMessageInBackground(
386
386
  // Telegram typing heartbeat
387
387
  // ---------------------------------------------------------------------------
388
388
 
389
+ /**
390
+ * How often the typing status is re-sent while a turn runs.
391
+ *
392
+ * `sendChatAction` sets a status that expires on its own after a few seconds,
393
+ * and the Bot API offers no stop/start pair to hold one open, so showing
394
+ * typing across a multi-second turn means re-sending on a timer. The interval
395
+ * has to stay under that expiry or the indicator blinks off between beats;
396
+ * anyone changing it should check the current window first.
397
+ *
398
+ * No explicit stop is needed on the delivery path: Telegram clears the status
399
+ * as soon as the bot sends a message.
400
+ *
401
+ * https://core.telegram.org/bots/api#sendchataction
402
+ */
389
403
  const TELEGRAM_TYPING_INTERVAL_MS = 4_000;
390
404
 
391
405
  function shouldEmitTelegramTyping(
@@ -26,7 +26,7 @@ import { sendTelegramReply } from "../../../messaging/providers/telegram-bot/sen
26
26
  import { getLogger } from "../../../util/logger.js";
27
27
  import { RESEND_COOLDOWN_MS } from "../../verification-outbound-actions.js";
28
28
  import {
29
- composeVerificationTelegram,
29
+ composeVerificationText,
30
30
  GUARDIAN_VERIFY_TEMPLATE_KEYS,
31
31
  } from "../../verification-templates.js";
32
32
 
@@ -194,7 +194,7 @@ export async function handleBootstrapIntercept(
194
194
  const newSession: CreateOutboundSessionResult = minted;
195
195
 
196
196
  // Compose and send the verification prompt via Telegram
197
- const telegramBody = composeVerificationTelegram(
197
+ const telegramBody = composeVerificationText(
198
198
  GUARDIAN_VERIFY_TEMPLATE_KEYS.TELEGRAM_CHALLENGE_REQUEST,
199
199
  {
200
200
  code: newSession.secret,
@@ -122,8 +122,9 @@ describe("handleGuardianActivationIntercept", () => {
122
122
  identityBindingStatus: "bound",
123
123
  destinationAddress: "chat-123",
124
124
  verificationPurpose: "guardian",
125
- // No session was read: the create is a gateway-side create-if-absent.
126
- ifNoneActive: true,
125
+ // No session was read for this sender, so the create is a
126
+ // sender-scoped create-if-absent.
127
+ ifNoneActiveForExternalUserId: "user-42",
127
128
  });
128
129
 
129
130
  // Verify deliverChannelReply was called with the welcome/verify message
@@ -243,7 +244,11 @@ describe("handleGuardianActivationIntercept", () => {
243
244
  expect(emitNotificationSignalCalls).toHaveLength(0);
244
245
  });
245
246
 
246
- test("existing active session from different sender allows superseding", async () => {
247
+ test("someone else verifying does not turn this sender away", async () => {
248
+ // The read is scoped to the sender, so another person's live session is
249
+ // invisible here and this activation proceeds as a first one. Without the
250
+ // scope it would return the stranger's session and this sender would be
251
+ // told a verification is "already in progress" that is not theirs.
247
252
  gatewaySessions.state.activeSession = {
248
253
  id: "existing-sess",
249
254
  channel: "telegram",
@@ -254,16 +259,14 @@ describe("handleGuardianActivationIntercept", () => {
254
259
 
255
260
  const result = await handleGuardianActivationIntercept(makeParams());
256
261
 
257
- // Should proceed and create a new session (superseding the stale one)
258
- expect(result).not.toBeNull();
259
- const body = result!;
260
- expect(body).toEqual({ accepted: true, guardianActivation: true });
262
+ expect(result).toEqual({ accepted: true, guardianActivation: true });
261
263
  expect(gatewaySessions.calls.create).toHaveLength(1);
262
- // Deliberate supersede: the create-if-absent guard is omitted so the
263
- // stale session gets revoked.
264
+ // Carrying the guard is what keeps the stranger's code alive: the mint
265
+ // supersedes by bound identity, and this sender is not that identity.
264
266
  expect(
265
- (gatewaySessions.calls.create[0] as Record<string, unknown>).ifNoneActive,
266
- ).toBeUndefined();
267
+ (gatewaySessions.calls.create[0] as Record<string, unknown>)
268
+ .ifNoneActiveForExternalUserId,
269
+ ).toBe("user-42");
267
270
  expect(emitNotificationSignalCalls).toHaveLength(1);
268
271
  });
269
272
 
@@ -133,7 +133,11 @@ export async function handleGuardianActivationIntercept(
133
133
  // normal pipeline handle the message.
134
134
  let existingSession: VerificationSessionWire | null;
135
135
  try {
136
- existingSession = await findActiveSession(sourceChannel);
136
+ // This sender's own session. The channel may carry others, and what the
137
+ // guard below needs to know is whether THIS activation is a duplicate.
138
+ existingSession = await findActiveSession(sourceChannel, {
139
+ expectedExternalUserId: rawSenderId,
140
+ });
137
141
  } catch (err) {
138
142
  log.warn(
139
143
  { err, sourceChannel },
@@ -158,25 +162,18 @@ export async function handleGuardianActivationIntercept(
158
162
  return { accepted: true, guardianActivationPending: true };
159
163
  };
160
164
 
165
+ // The read is scoped to this sender, so anything it returned is this
166
+ // sender's own activation already in flight.
161
167
  if (existingSession) {
162
- // Only block if the session belongs to the same sender. If a different
163
- // user triggered the session, let this sender proceed (they'll supersede
164
- // the stale session via createOutboundSession's revocation logic).
165
- const sessionOwner =
166
- existingSession.expectedExternalUserId ?? existingSession.expectedChatId;
167
- if (
168
- sessionOwner === rawSenderId ||
169
- sessionOwner === conversationExternalId
170
- ) {
171
- return respondActivationPending();
172
- }
168
+ return respondActivationPending();
173
169
  }
174
170
 
175
171
  // ── Create verification session ──
176
- // When the read above saw no active session, ifNoneActive makes the create
177
- // a gateway-side create-if-absent: a concurrent activation that minted in
178
- // between conflicts here instead of revoking that first code. A supersede
179
- // (stale session from a different sender) deliberately omits the guard.
172
+ // The read above saw no session for this sender, so the sender-scoped
173
+ // create-if-absent makes the mint atomic: a concurrent activation from the
174
+ // same sender that minted in between conflicts here instead of revoking that
175
+ // first code. Scoped to the sender rather than the channel, because another
176
+ // person verifying at the same time is ordinary and must not block this.
180
177
  let sessionResult: Awaited<
181
178
  ReturnType<typeof createOutboundSessionConditional>
182
179
  >;
@@ -188,7 +185,7 @@ export async function handleGuardianActivationIntercept(
188
185
  identityBindingStatus: "bound",
189
186
  destinationAddress: conversationExternalId,
190
187
  verificationPurpose: "guardian",
191
- ...(existingSession ? {} : { ifNoneActive: true }),
188
+ ifNoneActiveForExternalUserId: rawSenderId,
192
189
  });
193
190
  } catch (err) {
194
191
  log.warn(