@vellumai/assistant 0.12.2 → 0.12.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 (772) hide show
  1. package/.env.example +2 -0
  2. package/AGENTS.md +1 -1
  3. package/ARCHITECTURE.md +43 -29
  4. package/Dockerfile +63 -0
  5. package/docs/architecture/integrations.md +7 -0
  6. package/docs/architecture/memory.md +17 -2
  7. package/docs/architecture/security.md +10 -5
  8. package/docs/architecture/turn-actor.md +2 -0
  9. package/docs/credential-execution-service.md +1 -1
  10. package/docs/desktop-browser-cli.md +50 -5
  11. package/docs/live-voice-task-outcomes.md +100 -0
  12. package/docs/mode-sessions.md +183 -0
  13. package/docs/voice-input-diagnostics.md +58 -0
  14. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/package.json +2 -1
  15. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/secret-detection.test.ts +1 -0
  16. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
  17. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/integration-categories.ts +36 -0
  18. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/secret-detection.ts +4 -1
  19. package/node_modules/@vellumai/environments/src/shell.test.ts +21 -0
  20. package/node_modules/@vellumai/environments/src/shell.ts +24 -0
  21. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json +2 -1
  22. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/secret-detection.test.ts +1 -0
  23. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
  24. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/integration-categories.ts +36 -0
  25. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/secret-detection.ts +4 -1
  26. package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +20 -0
  27. package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +8 -2
  28. package/node_modules/@vellumai/gateway-client/src/index.ts +4 -1
  29. package/node_modules/@vellumai/gateway-client/src/outbound-contract.ts +38 -7
  30. package/node_modules/@vellumai/gateway-client/src/trust-verdict-contract.ts +5 -6
  31. package/node_modules/@vellumai/service-contracts/package.json +2 -1
  32. package/node_modules/@vellumai/service-contracts/src/__tests__/secret-detection.test.ts +1 -0
  33. package/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
  34. package/node_modules/@vellumai/service-contracts/src/integration-categories.ts +36 -0
  35. package/node_modules/@vellumai/service-contracts/src/secret-detection.ts +4 -1
  36. package/node_modules/@vellumai/slack-text/src/index.ts +4 -8
  37. package/openapi.yaml +716 -16
  38. package/package.json +1 -1
  39. package/scripts/smoke-desktop-browser-cli.ts +11 -26
  40. package/scripts/smoke-desktop-window-theme.ts +2 -3
  41. package/scripts/sync-llm-catalog.ts +3 -0
  42. package/scripts/voice-ttft-spike.ts +2 -2
  43. package/src/__tests__/agent-loop-output-hooks.test.ts +150 -1
  44. package/src/__tests__/agent-loop-provider-error-recording.test.ts +87 -19
  45. package/src/__tests__/agent-loop.test.ts +259 -0
  46. package/src/__tests__/agent-wake-delegation-prompt.test.ts +64 -2
  47. package/src/__tests__/annotate-activity-metadata.test.ts +3 -1
  48. package/src/__tests__/annotate-risk-options.test.ts +2 -0
  49. package/src/__tests__/answered-question-persistence.test.ts +2 -0
  50. package/src/__tests__/anthropic-gateway-shared.test.ts +22 -0
  51. package/src/__tests__/anthropic-provider.test.ts +55 -0
  52. package/src/__tests__/approval-interception-trust-gates.test.ts +40 -0
  53. package/src/__tests__/approval-message-composer.test.ts +1 -118
  54. package/src/__tests__/approval-routes-http.test.ts +3 -0
  55. package/src/__tests__/assistant-event-hub-connection-history.test.ts +111 -0
  56. package/src/__tests__/assistant-event-hub-targeted.test.ts +50 -0
  57. package/src/__tests__/assistant-event-targeting.test.ts +186 -0
  58. package/src/__tests__/assistant-stream-state.test.ts +29 -9
  59. package/src/__tests__/attachments-store.test.ts +22 -3
  60. package/src/__tests__/browser-fill-credential.test.ts +11 -22
  61. package/src/__tests__/btw-routes.test.ts +4 -1
  62. package/src/__tests__/build-persisted-content.test.ts +1 -1
  63. package/src/__tests__/call-conversation-messages.test.ts +5 -0
  64. package/src/__tests__/call-recovery.test.ts +7 -2
  65. package/src/__tests__/call-routes-http.test.ts +12 -0
  66. package/src/__tests__/call-store.test.ts +7 -2
  67. package/src/__tests__/channel-approval.test.ts +9 -14
  68. package/src/__tests__/channel-reply-delivery.test.ts +56 -0
  69. package/src/__tests__/channel-retry-sweep.test.ts +9 -0
  70. package/src/__tests__/chat-credential-redaction.test.ts +23 -0
  71. package/src/__tests__/chat-reveal-guard-priming.test.ts +2 -0
  72. package/src/__tests__/checker.test.ts +11 -3
  73. package/src/__tests__/client-os-metadata-persistence.test.ts +35 -1
  74. package/src/__tests__/computer-use-screenshot-attachments.test.ts +411 -0
  75. package/src/__tests__/computer-use-screenshot-selection.test.ts +50 -0
  76. package/src/__tests__/computer-use-skill-manifest-regression.test.ts +0 -25
  77. package/src/__tests__/computer-use-tools.test.ts +1 -233
  78. package/src/__tests__/config-model-image-provider.test.ts +39 -0
  79. package/src/__tests__/config-schema.test.ts +16 -0
  80. package/src/__tests__/config-watcher.test.ts +24 -0
  81. package/src/__tests__/context-search-agent-runner.test.ts +260 -1
  82. package/src/__tests__/context-search-fanout.test.ts +1 -1
  83. package/src/__tests__/context-search-types.test.ts +7 -7
  84. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +8 -2
  85. package/src/__tests__/conversation-agent-loop-fatal-cleanup.test.ts +2 -0
  86. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +2 -0
  87. package/src/__tests__/conversation-agent-loop-overflow.test.ts +2 -0
  88. package/src/__tests__/conversation-agent-loop.test.ts +797 -16
  89. package/src/__tests__/conversation-app-control-lifecycle.test.ts +53 -1
  90. package/src/__tests__/conversation-attachments.test.ts +106 -0
  91. package/src/__tests__/conversation-evictor.test.ts +62 -37
  92. package/src/__tests__/conversation-fork-crud.test.ts +191 -0
  93. package/src/__tests__/conversation-history-boundary-repair-failure.test.ts +174 -0
  94. package/src/__tests__/conversation-loop-db-lock-resilience.test.ts +2 -0
  95. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +2 -0
  96. package/src/__tests__/conversation-rendered-delegation-state.test.ts +276 -0
  97. package/src/__tests__/conversation-retry-route.test.ts +22 -0
  98. package/src/__tests__/conversation-routes-disk-view.test.ts +2 -0
  99. package/src/__tests__/conversation-routes-enabled-plugins.test.ts +2 -0
  100. package/src/__tests__/conversation-routes-guardian-reply.test.ts +10 -0
  101. package/src/__tests__/conversation-routes-hidden-queue.test.ts +2 -0
  102. package/src/__tests__/conversation-routes-slash-commands.test.ts +2 -0
  103. package/src/__tests__/conversation-runtime-assembly.test.ts +29 -0
  104. package/src/__tests__/conversation-surface-snapshots.test.ts +543 -0
  105. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +151 -0
  106. package/src/__tests__/conversation-surfaces-point-at-budget.test.ts +63 -1
  107. package/src/__tests__/conversation-tool-setup-attribution.test.ts +23 -0
  108. package/src/__tests__/conversation-undo.test.ts +61 -0
  109. package/src/__tests__/credential-execution-client.test.ts +72 -55
  110. package/src/__tests__/credential-prompt-route.test.ts +35 -3
  111. package/src/__tests__/cu-unified-flow.test.ts +82 -1
  112. package/src/__tests__/db-conversation-tool-surface.test.ts +221 -0
  113. package/src/__tests__/db-mode-sessions-migration.test.ts +175 -0
  114. package/src/__tests__/deterministic-verification-control-plane.test.ts +2 -0
  115. package/src/__tests__/dm-backfill.test.ts +91 -1
  116. package/src/__tests__/document-create-route.test.ts +88 -0
  117. package/src/__tests__/document-sync-tags.test.ts +27 -0
  118. package/src/__tests__/emit-signal-routing-intent.test.ts +142 -2
  119. package/src/__tests__/events-tail-route.test.ts +58 -0
  120. package/src/__tests__/evict-conversations-for-reload.test.ts +57 -8
  121. package/src/__tests__/guardian-action-sweep.test.ts +16 -0
  122. package/src/__tests__/guardian-dispatch.test.ts +13 -0
  123. package/src/__tests__/headless-browser-interactions.test.ts +64 -20
  124. package/src/__tests__/heartbeat-service.test.ts +2 -59
  125. package/src/__tests__/helpers/mock-conversation.ts +21 -0
  126. package/src/__tests__/helpers/native-web-search-harness.ts +2 -0
  127. package/src/__tests__/helpers/verification-sessions-ipc-sim.ts +5 -0
  128. package/src/__tests__/history-repair.test.ts +53 -0
  129. package/src/__tests__/host-cu-proxy.test.ts +46 -2
  130. package/src/__tests__/http-user-message-parity.test.ts +2 -0
  131. package/src/__tests__/image-credentials.test.ts +49 -0
  132. package/src/__tests__/image-service-dispatcher.test.ts +45 -0
  133. package/src/__tests__/inference-profile-session-handler.test.ts +26 -0
  134. package/src/__tests__/interrupt-on-send.test.ts +57 -43
  135. package/src/__tests__/interrupt-turn-note.test.ts +5 -6
  136. package/src/__tests__/list-messages-tool-merge.test.ts +85 -1
  137. package/src/__tests__/llm-catalog-parity.test.ts +27 -4
  138. package/src/__tests__/media-generate-image.test.ts +49 -1
  139. package/src/__tests__/message-provenance-contact-id.test.ts +69 -0
  140. package/src/__tests__/migration-export-to-gcs.test.ts +142 -0
  141. package/src/__tests__/mode-session-history.test.ts +245 -0
  142. package/src/__tests__/mode-session-recovery.test.ts +222 -0
  143. package/src/__tests__/mode-session-wire.test.ts +141 -0
  144. package/src/__tests__/notification-guardian-path.test.ts +7 -0
  145. package/src/__tests__/notification-telegram-adapter.test.ts +2 -2
  146. package/src/__tests__/notification-vellum-adapter.test.ts +34 -0
  147. package/src/__tests__/oauth-apps-routes.test.ts +1 -0
  148. package/src/__tests__/oauth-commands-routes.test.ts +114 -101
  149. package/src/__tests__/oauth-connect-orchestrator.test.ts +2 -0
  150. package/src/__tests__/oauth-provider-serializer.test.ts +13 -0
  151. package/src/__tests__/oauth-providers-routes.test.ts +3 -0
  152. package/src/__tests__/outbound-slack-persistence.test.ts +2 -0
  153. package/src/__tests__/persist-media-references.test.ts +50 -0
  154. package/src/__tests__/persistence-secret-redaction.test.ts +2 -0
  155. package/src/__tests__/plugin-api-provider.test.ts +5 -0
  156. package/src/__tests__/plugin-api-resolve-credential.test.ts +97 -0
  157. package/src/__tests__/plugin-import-boundary-guard.test.ts +9 -1
  158. package/src/__tests__/process-message-display-content.test.ts +56 -0
  159. package/src/__tests__/reaction-persistence.test.ts +5 -0
  160. package/src/__tests__/request-secret-standalone-channel-gate.test.ts +25 -9
  161. package/src/__tests__/run-conversation-turn-persistence.test.ts +138 -1
  162. package/src/__tests__/runtime-events-sse-reconnect.test.ts +1 -1
  163. package/src/__tests__/schedule-routes.test.ts +20 -0
  164. package/src/__tests__/scheduler-result-notification.test.ts +23 -4
  165. package/src/__tests__/script-proxy-certs.test.ts +1 -1
  166. package/src/__tests__/scripted-turn-metadata-persistence.test.ts +73 -0
  167. package/src/__tests__/secret-ingress-http.test.ts +2 -0
  168. package/src/__tests__/secret-routes-platform-proxy.test.ts +86 -31
  169. package/src/__tests__/secret-routes-scrub.test.ts +22 -27
  170. package/src/__tests__/secret-scanner.test.ts +20 -0
  171. package/src/__tests__/secure-keys-managed-failover.test.ts +8 -0
  172. package/src/__tests__/secure-keys.test.ts +50 -4
  173. package/src/__tests__/send-endpoint-busy.test.ts +163 -1
  174. package/src/__tests__/server-history-render.test.ts +91 -1
  175. package/src/__tests__/server-tool-pairing.test.ts +107 -0
  176. package/src/__tests__/settings-routes.test.ts +50 -1
  177. package/src/__tests__/skill-script-runner-sandbox.test.ts +32 -0
  178. package/src/__tests__/skills.test.ts +5 -4
  179. package/src/__tests__/subagent-fork-notifications.test.ts +1 -2
  180. package/src/__tests__/subagent-manager-notify.test.ts +1 -2
  181. package/src/__tests__/subagent-notify-parent.test.ts +146 -39
  182. package/src/__tests__/subagent-notify-stale-rebuild.test.ts +2 -4
  183. package/src/__tests__/subagent-tool-gate-mode.test.ts +226 -0
  184. package/src/__tests__/surface-completion-in-flight-snapshot.test.ts +2 -0
  185. package/src/__tests__/terminal-tools.test.ts +8 -0
  186. package/src/__tests__/thread-backfill.test.ts +53 -0
  187. package/src/__tests__/tool-preview-lifecycle.test.ts +2 -0
  188. package/src/__tests__/tool-result-follow-up.test.ts +99 -0
  189. package/src/__tests__/tool-result-metadata-plumbing.test.ts +66 -1
  190. package/src/__tests__/tool-start-timestamp.test.ts +2 -0
  191. package/src/__tests__/twilio-routes.test.ts +1 -0
  192. package/src/__tests__/unicode.test.ts +36 -0
  193. package/src/__tests__/web-search-backend-failure.test.ts +1 -1
  194. package/src/activation/progress-store.test.ts +12 -7
  195. package/src/agent/loop.ts +136 -53
  196. package/src/agent/tool-result-follow-up.ts +70 -0
  197. package/src/api/attachment-provenance.test.ts +68 -0
  198. package/src/api/computer-use-tool.test.ts +55 -0
  199. package/src/api/computer-use-tool.ts +27 -0
  200. package/src/api/events/assistant-outbound-attachment.ts +3 -0
  201. package/src/api/events/assistant-turn-start.ts +3 -0
  202. package/src/api/events/desktop-activity-changed.ts +10 -0
  203. package/src/api/events/generation-handoff.ts +2 -0
  204. package/src/api/events/message-complete.ts +2 -0
  205. package/src/api/events/notification-conversation-created.ts +14 -16
  206. package/src/api/events/notification-intent.ts +4 -2
  207. package/src/api/events/question-request.ts +1 -0
  208. package/src/api/events/tool-result.ts +85 -0
  209. package/src/api/events/tool-use-start.ts +3 -0
  210. package/src/api/events/user-message-echo.ts +3 -0
  211. package/src/api/index.ts +42 -6
  212. package/src/api/mode-session.test.ts +131 -0
  213. package/src/api/mode-session.ts +148 -0
  214. package/src/api/remember-tool.ts +15 -0
  215. package/src/api/responses/conversation-message.ts +9 -0
  216. package/src/approvals/AGENTS.md +4 -2
  217. package/src/approvals/approval-primitive.ts +5 -2
  218. package/src/approvals/scoped-approval-grants.ts +6 -2
  219. package/src/browser/__tests__/operations.test.ts +29 -0
  220. package/src/browser/operation-meta.ts +2 -1
  221. package/src/browser/operations.ts +29 -0
  222. package/src/browser/virtual-desktop-target.ts +2 -9
  223. package/src/bundler/bundle-signer.ts +1 -3
  224. package/src/calls/__tests__/barge-in-guard.test.ts +3 -0
  225. package/src/calls/__tests__/call-controller.test.ts +94 -0
  226. package/src/calls/__tests__/call-funnel-telemetry.test.ts +204 -0
  227. package/src/calls/__tests__/voice-control-protocol.test.ts +15 -0
  228. package/src/calls/__tests__/voice-escalation-judge.test.ts +209 -0
  229. package/src/calls/__tests__/voice-leg-coordinator.test.ts +168 -0
  230. package/src/{live-voice/__tests__/live-voice-metrics.test.ts → calls/__tests__/voice-metrics.test.ts} +32 -38
  231. package/src/calls/__tests__/voice-session-bridge.test.ts +576 -122
  232. package/src/calls/barge-in-guard.ts +5 -0
  233. package/src/calls/call-controller.ts +196 -1
  234. package/src/calls/call-domain.ts +4 -0
  235. package/src/calls/call-store.ts +87 -2
  236. package/src/calls/guardian-action-sweep.ts +22 -8
  237. package/src/calls/media-stream-audio-transcode.ts +1 -3
  238. package/src/calls/typesafe-noul.ts +156 -0
  239. package/src/calls/voice-control-protocol.ts +6 -0
  240. package/src/calls/voice-escalation-judge.ts +145 -0
  241. package/src/calls/voice-escalation-target.ts +10 -0
  242. package/src/calls/voice-leg-coordinator.ts +121 -3
  243. package/src/{live-voice/live-voice-metrics.ts → calls/voice-metrics.ts} +94 -87
  244. package/src/calls/voice-session-bridge.ts +256 -112
  245. package/src/calls/voice-triage-escalate.ts +7 -0
  246. package/src/cli/__tests__/roadmap-cli.test.ts +4 -41
  247. package/src/cli/commands/__tests__/cli-test-harness.ts +21 -3
  248. package/src/cli/commands/__tests__/notifications.test.ts +35 -10
  249. package/src/cli/commands/__tests__/plugins.test.ts +20 -1
  250. package/src/cli/commands/__tests__/schedules.test.ts +14 -0
  251. package/src/cli/commands/bash.help.ts +4 -3
  252. package/src/cli/commands/browser.help.ts +9 -6
  253. package/src/cli/commands/browser.ts +0 -1
  254. package/src/cli/commands/clients.help.ts +46 -0
  255. package/src/cli/commands/clients.ts +116 -0
  256. package/src/cli/commands/credentials.help.ts +9 -4
  257. package/src/cli/commands/notifications.help.ts +16 -2
  258. package/src/cli/commands/notifications.ts +30 -8
  259. package/src/cli/commands/oauth/index.help.ts +10 -0
  260. package/src/cli/commands/oauth/providers.ts +12 -0
  261. package/src/cli/commands/oauth/request.test.ts +179 -2
  262. package/src/cli/commands/oauth/request.ts +41 -10
  263. package/src/cli/commands/plugins.ts +12 -5
  264. package/src/cli/commands/roadmap.help.ts +18 -21
  265. package/src/cli/commands/roadmap.ts +4 -21
  266. package/src/cli/commands/schedules.ts +2 -0
  267. package/src/cli/lib/__tests__/inspect-plugin.test.ts +54 -0
  268. package/src/cli/lib/__tests__/install-from-github.test.ts +41 -0
  269. package/src/cli/lib/__tests__/list-installed-plugins.test.ts +5 -2
  270. package/src/cli/lib/__tests__/local-plugin-upgrade.test.ts +133 -51
  271. package/src/cli/lib/__tests__/plugin-catalog-cache.test.ts +8 -2
  272. package/src/cli/lib/bundled-marketplace.json +906 -1
  273. package/src/cli/lib/bundled-plugin-packages.json +783 -1
  274. package/src/cli/lib/inspect-plugin.ts +11 -4
  275. package/src/cli/lib/plugin-marketplace.ts +8 -0
  276. package/src/cli/lib/publish-plugin.ts +1 -2
  277. package/src/cli/lib/upgrade-plugin.ts +19 -11
  278. package/src/cli/output.ts +18 -4
  279. package/src/config/__tests__/default-provider.test.ts +6 -0
  280. package/src/config/__tests__/memory-retrospective-schema.test.ts +14 -1
  281. package/src/config/bundled-skills/computer-use/SKILL.md +1 -1
  282. package/src/config/bundled-skills/computer-use/TOOLS.json +89 -1
  283. package/src/config/bundled-skills/image-studio/SKILL.md +17 -2
  284. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +7 -11
  285. package/src/config/bundled-skills/schedule/SKILL.md +6 -6
  286. package/src/config/bundled-skills/sequences/TOOLS.json +1 -5
  287. package/src/config/call-site-defaults.ts +9 -0
  288. package/src/config/feature-flag-registry.json +16 -0
  289. package/src/config/loader.ts +9 -17
  290. package/src/config/platform-identity.ts +8 -8
  291. package/src/config/profile-text-generation.test.ts +51 -0
  292. package/src/config/profile-text-generation.ts +51 -0
  293. package/src/config/schemas/__tests__/memory-v3.test.ts +12 -0
  294. package/src/config/schemas/__tests__/tools.test.ts +31 -0
  295. package/src/config/schemas/call-site-catalog.ts +14 -0
  296. package/src/config/schemas/channels.ts +0 -18
  297. package/src/config/schemas/llm.ts +9 -2
  298. package/src/config/schemas/mcp.ts +6 -1
  299. package/src/config/schemas/memory-retrospective.ts +11 -2
  300. package/src/config/schemas/memory-v3.ts +12 -0
  301. package/src/config/schemas/services.ts +15 -6
  302. package/src/config/schemas/tools.ts +10 -0
  303. package/src/config/session-groups-gate.ts +21 -0
  304. package/src/config/skills.ts +2 -6
  305. package/src/conversations/__tests__/message-consolidation.test.ts +149 -0
  306. package/src/conversations/message-consolidation.ts +102 -5
  307. package/src/credential-execution/ces-connect.test.ts +69 -0
  308. package/src/credential-execution/ces-connect.ts +115 -0
  309. package/src/credential-execution/ces-runtime.ts +55 -93
  310. package/src/credential-execution/executable-discovery.ts +34 -142
  311. package/src/credential-execution/process-manager.test.ts +20 -24
  312. package/src/credential-execution/process-manager.ts +24 -19
  313. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +10 -1
  314. package/src/daemon/__tests__/conversation-tool-setup.test.ts +43 -0
  315. package/src/daemon/__tests__/inference-profile-notification.test.ts +2 -0
  316. package/src/daemon/__tests__/turn-tail-deleted-conversation.test.ts +36 -0
  317. package/src/daemon/assistant-attachments.ts +20 -12
  318. package/src/daemon/browser-mode-session.test.ts +402 -0
  319. package/src/daemon/browser-mode-session.ts +186 -0
  320. package/src/daemon/chat-credential-redaction.ts +16 -1
  321. package/src/daemon/computer-use-mode-session.test.ts +210 -0
  322. package/src/daemon/computer-use-mode-session.ts +105 -0
  323. package/src/daemon/config-watcher.ts +10 -0
  324. package/src/daemon/conversation-agent-loop-handlers.ts +132 -13
  325. package/src/daemon/conversation-agent-loop.ts +231 -25
  326. package/src/daemon/conversation-attachments.ts +93 -12
  327. package/src/daemon/conversation-evictor.ts +5 -24
  328. package/src/daemon/conversation-history.ts +10 -0
  329. package/src/daemon/conversation-interrupt-repair.ts +4 -43
  330. package/src/daemon/conversation-interrupt.ts +7 -11
  331. package/src/daemon/conversation-messaging.ts +58 -5
  332. package/src/daemon/conversation-mode-session.test.ts +1667 -0
  333. package/src/daemon/conversation-mode-session.ts +1008 -0
  334. package/src/daemon/conversation-process.ts +5 -0
  335. package/src/daemon/conversation-runtime-assembly.ts +10 -20
  336. package/src/daemon/conversation-skill-tools.ts +1 -0
  337. package/src/daemon/conversation-store.ts +4 -4
  338. package/src/daemon/conversation-surface-snapshots.ts +135 -0
  339. package/src/daemon/conversation-surfaces.ts +113 -5
  340. package/src/daemon/conversation-tool-setup.ts +79 -6
  341. package/src/daemon/conversation-turn-finalize.ts +26 -13
  342. package/src/daemon/conversation.ts +215 -58
  343. package/src/daemon/daemon-control.ts +3 -7
  344. package/src/daemon/handlers/config-model.ts +7 -5
  345. package/src/daemon/handlers/shared.ts +55 -16
  346. package/src/daemon/host-cu-proxy.ts +51 -22
  347. package/src/daemon/lifecycle.ts +30 -13
  348. package/src/daemon/mcp-reload-service.ts +1 -1
  349. package/src/daemon/message-protocol.ts +0 -1
  350. package/src/daemon/message-provenance.ts +28 -4
  351. package/src/daemon/message-types/shared.ts +1 -0
  352. package/src/daemon/mode-session-startup-recovery.ts +23 -0
  353. package/src/daemon/mode-session-tracking.ts +54 -0
  354. package/src/daemon/orphan-reaper.ts +4 -3
  355. package/src/daemon/persist-media-references.ts +20 -3
  356. package/src/daemon/process-message.ts +13 -4
  357. package/src/daemon/tool-setup-types.ts +6 -0
  358. package/src/daemon/virtual-desktop-context.ts +21 -0
  359. package/src/daemon/wake-conversation-ops.ts +50 -15
  360. package/src/desktop/__tests__/fake-desktop.ts +1 -0
  361. package/src/desktop/desktop-accessibility-script.ts +88 -0
  362. package/src/desktop/desktop-accessibility.test.ts +160 -0
  363. package/src/desktop/desktop-accessibility.ts +169 -0
  364. package/src/desktop/desktop-automation-lease.test.ts +302 -64
  365. package/src/desktop/desktop-automation-lease.ts +129 -54
  366. package/src/desktop/desktop-browser-endpoint.ts +4 -0
  367. package/src/desktop/desktop-computer-use-routing.test.ts +330 -0
  368. package/src/desktop/desktop-computer-use.test.ts +449 -0
  369. package/src/desktop/desktop-computer-use.ts +502 -0
  370. package/src/desktop/desktop-dependencies.ts +47 -293
  371. package/src/desktop/desktop-display.ts +2 -2
  372. package/src/desktop/desktop-help.ts +15 -0
  373. package/src/desktop/desktop-panel-config.test.ts +13 -3
  374. package/src/desktop/desktop-panel-config.ts +109 -15
  375. package/src/desktop/desktop-session-manager.test.ts +69 -21
  376. package/src/desktop/desktop-session-manager.ts +76 -22
  377. package/src/desktop/desktop-stream-bridge.test.ts +76 -132
  378. package/src/desktop/desktop-stream-bridge.ts +9 -11
  379. package/src/desktop/desktop-wallpaper-renderer.ts +132 -0
  380. package/src/desktop/desktop-wallpaper-worker.ts +17 -0
  381. package/src/desktop/desktop-wallpaper.test.ts +48 -7
  382. package/src/desktop/desktop-wallpaper.ts +34 -102
  383. package/src/desktop/desktop-window-theme.ts +5 -25
  384. package/src/desktop/virtual-desktop-feature.ts +53 -3
  385. package/src/desktop/virtual-desktop-platform.test.ts +5 -9
  386. package/src/documents/document-store.ts +23 -0
  387. package/src/heartbeat/__tests__/heartbeat-service.test.ts +22 -26
  388. package/src/heartbeat/heartbeat-service.ts +5 -33
  389. package/src/ipc/__tests__/browser-ipc.test.ts +215 -6
  390. package/src/ipc/assistant-server.ts +2 -0
  391. package/src/ipc/routes/__tests__/channel-reply-ipc-routes.test.ts +111 -0
  392. package/src/ipc/routes/channel-reply-ipc-routes.ts +73 -0
  393. package/src/live-voice/__tests__/continuation-judge.test.ts +151 -0
  394. package/src/live-voice/__tests__/live-activity-reporter.test.ts +154 -15
  395. package/src/live-voice/__tests__/live-voice-flux-turn-end.test.ts +743 -31
  396. package/src/live-voice/__tests__/live-voice-look-follow-up.test.ts +191 -10
  397. package/src/live-voice/__tests__/live-voice-session-groups-gate.test.ts +87 -0
  398. package/src/live-voice/__tests__/live-voice-session-manager.test.ts +5 -1
  399. package/src/live-voice/__tests__/live-voice-sight-frame-echo.test.ts +41 -0
  400. package/src/live-voice/__tests__/live-voice-sight-frame.test.ts +1017 -9
  401. package/src/live-voice/__tests__/live-voice-triage-escalate.test.ts +224 -1
  402. package/src/live-voice/__tests__/live-voice-tts-session.test.ts +101 -0
  403. package/src/live-voice/__tests__/live-voice-tts.test.ts +44 -0
  404. package/src/live-voice/__tests__/live-voice-vad.test.ts +939 -75
  405. package/src/live-voice/__tests__/microphone-idle-gate.test.ts +87 -0
  406. package/src/live-voice/__tests__/session-controls.test.ts +23 -0
  407. package/src/live-voice/__tests__/task-outcomes.test.ts +117 -0
  408. package/src/live-voice/__tests__/voice-input-diagnostics.test.ts +117 -0
  409. package/src/live-voice/camera-mode-session.test.ts +265 -0
  410. package/src/live-voice/camera-mode-session.ts +277 -0
  411. package/src/live-voice/continuation-judge.ts +123 -0
  412. package/src/live-voice/live-activity-reporter.ts +115 -6
  413. package/src/live-voice/live-voice-manager.ts +36 -1
  414. package/src/live-voice/live-voice-photo.ts +88 -7
  415. package/src/live-voice/live-voice-session-manager.ts +15 -0
  416. package/src/live-voice/live-voice-session.ts +1443 -747
  417. package/src/live-voice/live-voice-tts.ts +22 -4
  418. package/src/live-voice/microphone-idle-gate.ts +56 -0
  419. package/src/live-voice/protocol.ts +171 -4
  420. package/src/live-voice/session-controls.ts +11 -3
  421. package/src/live-voice/task-outcomes.ts +131 -0
  422. package/src/live-voice/voice-input-diagnostics.ts +117 -0
  423. package/src/mcp/__tests__/manager-tool-caps.test.ts +156 -0
  424. package/src/mcp/__tests__/startup.test.ts +30 -7
  425. package/src/mcp/__tests__/tool-caps.test.ts +134 -0
  426. package/src/mcp/manager.ts +171 -101
  427. package/src/mcp/startup.ts +33 -13
  428. package/src/mcp/tool-caps.ts +184 -0
  429. package/src/media/__tests__/image-models.test.ts +58 -0
  430. package/src/media/image-credentials.ts +5 -0
  431. package/src/media/image-models.ts +52 -0
  432. package/src/media/image-service.ts +8 -0
  433. package/src/media/openrouter-image-service.test.ts +133 -0
  434. package/src/media/openrouter-image-service.ts +153 -0
  435. package/src/media/types.ts +1 -1
  436. package/src/messaging/provider-message-metadata.ts +3 -3
  437. package/src/messaging/providers/__tests__/transport-dispatch.test.ts +65 -11
  438. package/src/messaging/providers/discord/send.ts +4 -17
  439. package/src/messaging/providers/discord/transport.ts +2 -2
  440. package/src/messaging/providers/outlook/client.ts +16 -22
  441. package/src/messaging/providers/telegram-bot/send.test.ts +236 -1
  442. package/src/messaging/providers/telegram-bot/send.ts +10 -7
  443. package/src/messaging/providers/telegram-bot/transport.ts +3 -10
  444. package/src/messaging/providers/whatsapp/send.test.ts +169 -4
  445. package/src/monitoring/__tests__/control.test.ts +17 -5
  446. package/src/monitoring/control.ts +15 -5
  447. package/src/notifications/AGENTS.md +1 -1
  448. package/src/notifications/README.md +83 -99
  449. package/src/notifications/__tests__/broadcaster.test.ts +1 -1
  450. package/src/notifications/__tests__/channel-allowlist.test.ts +39 -0
  451. package/src/notifications/__tests__/copy-composer.test.ts +71 -1
  452. package/src/notifications/__tests__/decision-engine.test.ts +79 -2
  453. package/src/notifications/__tests__/deterministic-checks.test.ts +10 -10
  454. package/src/notifications/__tests__/home-feed-side-effect.test.ts +93 -4
  455. package/src/notifications/__tests__/notification-utils.test.ts +18 -0
  456. package/src/notifications/adapters/macos.ts +37 -46
  457. package/src/notifications/broadcaster.ts +5 -6
  458. package/src/notifications/channel-allowlist.ts +44 -0
  459. package/src/notifications/copy-composer.ts +11 -26
  460. package/src/notifications/decision-engine.ts +24 -14
  461. package/src/notifications/deterministic-checks.ts +1 -1
  462. package/src/notifications/emit-signal.ts +123 -37
  463. package/src/notifications/home-feed-side-effect.ts +9 -7
  464. package/src/notifications/notification-utils.ts +19 -0
  465. package/src/notifications/schedule-result-producer.ts +4 -6
  466. package/src/notifications/signal.ts +4 -19
  467. package/src/notifications/types.ts +15 -0
  468. package/src/oauth/AGENTS.md +2 -0
  469. package/src/oauth/__tests__/identity-verifier.test.ts +36 -1
  470. package/src/oauth/connection-resolver.ts +1 -2
  471. package/src/oauth/identity-verifier.ts +24 -0
  472. package/src/oauth/oauth-store.ts +12 -4
  473. package/src/oauth/provider-serializer.ts +8 -0
  474. package/src/oauth/seed-providers.ts +42 -0
  475. package/src/onboarding/onboarding-events-store.ts +99 -12
  476. package/src/permissions/confirmation-guardian-request.test.ts +16 -0
  477. package/src/permissions/confirmation-guardian-request.ts +2 -3
  478. package/src/permissions/question-prompter.test.ts +31 -0
  479. package/src/permissions/question-prompter.ts +2 -0
  480. package/src/persistence/__tests__/client-connection-events-store.test.ts +322 -0
  481. package/src/persistence/attachments-store.ts +84 -44
  482. package/src/persistence/client-connection-events-store.ts +377 -0
  483. package/src/persistence/conversation-crud.ts +166 -26
  484. package/src/persistence/conversation-mode-sessions.test.ts +424 -0
  485. package/src/persistence/conversation-mode-sessions.ts +460 -0
  486. package/src/persistence/conversation-plugin-facade.ts +30 -0
  487. package/src/persistence/conversation-queries.ts +6 -19
  488. package/src/persistence/conversation-tool-surface.ts +135 -0
  489. package/src/persistence/conversation-types.test.ts +32 -0
  490. package/src/persistence/conversation-types.ts +26 -4
  491. package/src/persistence/llm-usage-store.ts +3 -9
  492. package/src/persistence/message-metadata.ts +25 -0
  493. package/src/persistence/migrations/378-create-conversation-tool-surfaces.test.ts +78 -0
  494. package/src/persistence/migrations/378-create-conversation-tool-surfaces.ts +29 -0
  495. package/src/persistence/migrations/379-oauth-providers-response-ok-field.test.ts +92 -0
  496. package/src/persistence/migrations/379-oauth-providers-response-ok-field.ts +32 -0
  497. package/src/persistence/migrations/380-conversation-tool-surfaces-delegate-independent-tasks.test.ts +90 -0
  498. package/src/persistence/migrations/380-conversation-tool-surfaces-delegate-independent-tasks.ts +34 -0
  499. package/src/persistence/migrations/381-create-conversation-mode-sessions.ts +41 -0
  500. package/src/persistence/migrations/382-create-client-connection-events.ts +40 -0
  501. package/src/persistence/migrations/__tests__/382-create-client-connection-events.test.ts +56 -0
  502. package/src/persistence/schema/conversation-tool-surfaces.ts +32 -0
  503. package/src/persistence/schema/conversations.ts +67 -0
  504. package/src/persistence/schema/index.ts +1 -0
  505. package/src/persistence/schema/infrastructure.ts +29 -0
  506. package/src/persistence/schema/oauth.ts +1 -0
  507. package/src/persistence/steps.ts +41 -3
  508. package/src/plugin-api/__tests__/plugin-name-env.test.ts +249 -0
  509. package/src/plugin-api/conversation-turn.ts +31 -7
  510. package/src/plugin-api/effective-context-window.ts +33 -0
  511. package/src/plugin-api/index.ts +35 -6
  512. package/src/plugin-api/plugin-channel-turn-trust.test.ts +133 -0
  513. package/src/plugin-api/plugin-channel-turn-trust.ts +71 -0
  514. package/src/plugin-api/plugin-name-env.ts +125 -0
  515. package/src/plugin-api/resolve-credential.ts +79 -20
  516. package/src/plugin-api/vision-support.test.ts +22 -0
  517. package/src/plugin-api/vision-support.ts +17 -5
  518. package/src/plugins/__tests__/active-plugin-env.test.ts +136 -0
  519. package/src/plugins/active-plugin-env.ts +51 -0
  520. package/src/plugins/defaults/index.ts +24 -0
  521. package/src/plugins/defaults/memory/AGENTS.md +39 -5
  522. package/src/plugins/defaults/memory/__tests__/archive-removal-framing.test.ts +64 -0
  523. package/src/plugins/defaults/memory/__tests__/buffer-file.test.ts +365 -0
  524. package/src/plugins/defaults/memory/__tests__/buffer-format.test.ts +43 -0
  525. package/src/plugins/defaults/memory/__tests__/conversation-memory-purge.test.ts +1 -0
  526. package/src/plugins/defaults/memory/__tests__/db-memory-attach.test.ts +2 -0
  527. package/src/plugins/defaults/memory/__tests__/fixtures/buffer-appender.ts +17 -0
  528. package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +74 -0
  529. package/src/plugins/defaults/memory/__tests__/memory-run-evidence.test.ts +161 -0
  530. package/src/plugins/defaults/memory/__tests__/relocated-memory-test-rows.ts +10 -0
  531. package/src/plugins/defaults/memory/__tests__/worker-control.test.ts +19 -6
  532. package/src/plugins/defaults/memory/buffer-file.ts +354 -0
  533. package/src/plugins/defaults/memory/buffer-format.ts +40 -0
  534. package/src/plugins/defaults/memory/context-search/agent-protocol.ts +9 -12
  535. package/src/plugins/defaults/memory/context-search/agent-runner.ts +77 -22
  536. package/src/plugins/defaults/memory/context-search/format.ts +53 -8
  537. package/src/plugins/defaults/memory/context-search/limits.ts +10 -12
  538. package/src/plugins/defaults/memory/context-search/recall-input.ts +38 -0
  539. package/src/plugins/defaults/memory/context-search/search.ts +8 -14
  540. package/src/plugins/defaults/memory/context-search/sources/conversations.ts +1 -0
  541. package/src/plugins/defaults/memory/context-search/sources/memory-v2.ts +2 -1
  542. package/src/plugins/defaults/memory/context-search/sources/workspace.ts +2 -1
  543. package/src/plugins/defaults/memory/context-search/types.ts +1 -11
  544. package/src/plugins/defaults/memory/conversation-memory-purge.ts +4 -0
  545. package/src/plugins/defaults/memory/graph/__tests__/handle-remember-v2.test.ts +11 -4
  546. package/src/plugins/defaults/memory/graph/capability-seed.ts +1 -2
  547. package/src/plugins/defaults/memory/graph/tool-handlers.ts +13 -63
  548. package/src/plugins/defaults/memory/graph/tools.ts +35 -64
  549. package/src/plugins/defaults/memory/host-utils.ts +0 -10
  550. package/src/plugins/defaults/memory/injectors.ts +4 -3
  551. package/src/plugins/defaults/memory/memory-retrospective-job.ts +72 -183
  552. package/src/plugins/defaults/memory/memory-run-evidence.ts +213 -0
  553. package/src/plugins/defaults/memory/src/memory-eval-routes.ts +2 -3
  554. package/src/plugins/defaults/memory/src/memory-item-routes.test.ts +6 -1
  555. package/src/plugins/defaults/memory/src/memory-item-routes.ts +2 -1
  556. package/src/plugins/defaults/memory/substrate/__tests__/consolidation-job.test.ts +407 -99
  557. package/src/plugins/defaults/memory/substrate/__tests__/consolidation-prompt-flag-gating-guard.test.ts +10 -0
  558. package/src/plugins/defaults/memory/substrate/__tests__/prompts-consolidation.test.ts +107 -7
  559. package/src/plugins/defaults/memory/substrate/consolidation-job.ts +307 -86
  560. package/src/plugins/defaults/memory/substrate/consolidation-tool-surface.ts +34 -0
  561. package/src/plugins/defaults/memory/substrate/page-index.ts +2 -1
  562. package/src/plugins/defaults/memory/substrate/prompts/consolidation.ts +91 -51
  563. package/src/plugins/defaults/memory/substrate/sweep-job.ts +1 -1
  564. package/src/plugins/defaults/memory/tools.test.ts +124 -9
  565. package/src/plugins/defaults/memory/tools.ts +38 -11
  566. package/src/plugins/defaults/memory/v1/graph/consolidation.ts +2 -2
  567. package/src/plugins/defaults/memory/v1/graph/extraction.ts +2 -1
  568. package/src/plugins/defaults/memory/v1/graph/retriever.ts +1 -1
  569. package/src/plugins/defaults/memory/v2/__tests__/migration.test.ts +5 -0
  570. package/src/plugins/defaults/memory/v2/__tests__/reranker.test.ts +5 -2
  571. package/src/plugins/defaults/memory/v2/reranker.ts +2 -1
  572. package/src/plugins/defaults/memory/v3/__tests__/injection.test.ts +81 -1
  573. package/src/plugins/defaults/memory/v3/__tests__/orchestrate.test.ts +87 -0
  574. package/src/plugins/defaults/memory/v3/__tests__/plugin-schema.test.ts +12 -0
  575. package/src/plugins/defaults/memory/v3/__tests__/pool-log-store.test.ts +332 -15
  576. package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +626 -27
  577. package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +148 -28
  578. package/src/plugins/defaults/memory/v3/card.ts +2 -1
  579. package/src/plugins/defaults/memory/v3/injector.ts +212 -178
  580. package/src/plugins/defaults/memory/v3/orchestrate.ts +159 -32
  581. package/src/plugins/defaults/memory/v3/plugin-schema.ts +54 -4
  582. package/src/plugins/defaults/memory/v3/pool-log-store.ts +275 -37
  583. package/src/plugins/defaults/memory/v3/pool-select.test.ts +47 -5
  584. package/src/plugins/defaults/memory/v3/pool-select.ts +710 -59
  585. package/src/plugins/defaults/memory/v3/sections.ts +2 -1
  586. package/src/plugins/defaults/memory/v3/shadow-plugin.ts +41 -14
  587. package/src/plugins/defaults/memory/worker-control.ts +15 -5
  588. package/src/plugins/defaults/task-progress/__tests__/registration.test.ts +70 -0
  589. package/src/plugins/defaults/task-progress/__tests__/task-progress-context.test.ts +380 -0
  590. package/src/plugins/defaults/task-progress/hooks/post-compact.ts +19 -0
  591. package/src/plugins/defaults/task-progress/hooks/user-prompt-submit.ts +24 -0
  592. package/src/plugins/defaults/task-progress/package.json +14 -0
  593. package/src/plugins/defaults/task-progress/src/apply-task-progress-context.ts +93 -0
  594. package/src/plugins/defaults/task-progress/src/format-task-progress-context.ts +67 -0
  595. package/src/plugins/defaults/tool-error/hooks/post-tool-use.ts +4 -1
  596. package/src/plugins/defaults/tool-result-truncate/terminal.ts +1 -46
  597. package/src/prompts/__tests__/parallel-tasks-section.test.ts +25 -0
  598. package/src/prompts/delegation-gate.ts +57 -0
  599. package/src/prompts/system-prompt.ts +14 -32
  600. package/src/providers/__tests__/context-overflow-error.test.ts +10 -0
  601. package/src/providers/__tests__/search-provider-catalog.test.ts +18 -0
  602. package/src/providers/anthropic/client.ts +10 -1
  603. package/src/providers/anthropic-gateway-shared.ts +6 -0
  604. package/src/providers/fetch-provider-catalog.ts +11 -0
  605. package/src/providers/gemini/client.ts +25 -8
  606. package/src/providers/inference/adapter-factory.ts +6 -0
  607. package/src/providers/jev/client.test.ts +270 -0
  608. package/src/providers/jev/client.ts +534 -0
  609. package/src/providers/model-catalog.ts +59 -3
  610. package/src/providers/openai/__tests__/chat-completions-extra-body.test.ts +94 -1
  611. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +106 -1
  612. package/src/providers/openai/__tests__/chat-template-flatten.test.ts +24 -0
  613. package/src/providers/openai/__tests__/google-thought-signature.test.ts +1 -2
  614. package/src/providers/openai/chat-completions-provider.ts +100 -29
  615. package/src/providers/openai/google-thought-signature.ts +0 -6
  616. package/src/providers/openai/responses-provider.ts +23 -1
  617. package/src/providers/ratelimit.ts +1 -3
  618. package/src/providers/search-provider-catalog.ts +24 -0
  619. package/src/providers/server-tool-pairing.ts +16 -7
  620. package/src/providers/speech-to-text/__tests__/deepgram-flux-frames.test.ts +24 -0
  621. package/src/providers/speech-to-text/__tests__/deepgram-flux-realtime.test.ts +7 -1
  622. package/src/providers/speech-to-text/__tests__/vellum-managed-realtime.test.ts +50 -1
  623. package/src/providers/speech-to-text/deepgram-flux-frames.ts +11 -0
  624. package/src/providers/speech-to-text/vellum-managed-flux-realtime.ts +13 -0
  625. package/src/providers/types.ts +5 -0
  626. package/src/routes/control.ts +7 -5
  627. package/src/runtime/AGENTS.md +6 -4
  628. package/src/runtime/__tests__/agent-wake.test.ts +94 -1
  629. package/src/runtime/actor-trust-resolver.ts +3 -3
  630. package/src/runtime/agent-wake.ts +32 -5
  631. package/src/runtime/approval-message-composer.ts +3 -69
  632. package/src/runtime/assistant-event-hub.ts +73 -75
  633. package/src/runtime/assistant-event-publish-options.ts +7 -0
  634. package/src/runtime/assistant-event-targeting.ts +79 -0
  635. package/src/runtime/assistant-stream-state.ts +13 -100
  636. package/src/runtime/background-job-runner.ts +5 -5
  637. package/src/runtime/channel-retry-sweep.ts +1 -0
  638. package/src/runtime/guardian-action-message-composer.ts +8 -161
  639. package/src/runtime/guardian-action-service.ts +2 -17
  640. package/src/runtime/guardian-reply-router.ts +29 -40
  641. package/src/runtime/http-server.ts +2 -2
  642. package/src/runtime/http-types.ts +6 -0
  643. package/src/runtime/message-composer-types.ts +3 -61
  644. package/src/runtime/migrations/__tests__/vbundle-import-parity.test.ts +67 -1
  645. package/src/runtime/migrations/__tests__/vbundle-import-policy.test.ts +89 -0
  646. package/src/runtime/migrations/vbundle-builder.ts +24 -1
  647. package/src/runtime/migrations/vbundle-import-analyzer.ts +13 -4
  648. package/src/runtime/migrations/vbundle-import-policy.ts +43 -11
  649. package/src/runtime/migrations/vbundle-importer.ts +4 -1
  650. package/src/runtime/migrations/vbundle-streaming-importer.ts +4 -1
  651. package/src/runtime/migrations/vbundle-validator.ts +1 -0
  652. package/src/runtime/pending-interactions-mode-session.test.ts +62 -0
  653. package/src/runtime/pending-interactions.ts +29 -0
  654. package/src/runtime/routes/__tests__/client-history-routes.test.ts +104 -0
  655. package/src/runtime/routes/__tests__/image-generation-routes.test.ts +97 -0
  656. package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +24 -2
  657. package/src/runtime/routes/__tests__/roadmap-routes.test.ts +29 -6
  658. package/src/runtime/routes/browser-context.ts +31 -11
  659. package/src/runtime/routes/browser-routes.ts +43 -4
  660. package/src/runtime/routes/btw-routes.ts +9 -4
  661. package/src/runtime/routes/channel-route-shared.ts +1 -9
  662. package/src/runtime/routes/client-routes.ts +108 -0
  663. package/src/runtime/routes/conversation-management-routes.ts +4 -1
  664. package/src/runtime/routes/conversation-routes.ts +125 -14
  665. package/src/runtime/routes/credential-prompt-routes.ts +4 -3
  666. package/src/runtime/routes/credential-routes.ts +1 -2
  667. package/src/runtime/routes/desktop-setup-routes.test.ts +9 -18
  668. package/src/runtime/routes/desktop-setup-routes.ts +8 -7
  669. package/src/runtime/routes/documents-routes.ts +51 -0
  670. package/src/runtime/routes/email-routes.ts +3 -7
  671. package/src/runtime/routes/events-routes.ts +75 -54
  672. package/src/runtime/routes/guardian-approval-interception.ts +24 -0
  673. package/src/runtime/routes/identity-routes.ts +1 -5
  674. package/src/runtime/routes/image-generation-routes.ts +8 -14
  675. package/src/runtime/routes/inbound-message-handler.ts +37 -41
  676. package/src/runtime/routes/inbound-stages/backfill-sender-provenance.test.ts +168 -0
  677. package/src/runtime/routes/inbound-stages/backfill-sender-provenance.ts +96 -0
  678. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +39 -10
  679. package/src/runtime/routes/inbound-stages/background-dispatch.ts +9 -9
  680. package/src/runtime/routes/inbound-stages/reaction-intercept.ts +2 -0
  681. package/src/runtime/routes/inference-profile-session-handler.ts +11 -1
  682. package/src/runtime/routes/inference-profiles-routes.ts +9 -0
  683. package/src/runtime/routes/llm-context-normalization.ts +1 -7
  684. package/src/runtime/routes/migration-routes.ts +73 -2
  685. package/src/runtime/routes/notification-routes.ts +10 -0
  686. package/src/runtime/routes/oauth-commands-routes.ts +57 -54
  687. package/src/runtime/routes/oauth-providers.ts +11 -0
  688. package/src/runtime/routes/oauth-request-hints.test.ts +246 -0
  689. package/src/runtime/routes/oauth-request-hints.ts +149 -0
  690. package/src/runtime/routes/plugins-routes.ts +6 -0
  691. package/src/runtime/routes/roadmap-routes.ts +47 -8
  692. package/src/runtime/routes/schedule-routes.ts +2 -0
  693. package/src/runtime/routes/secret-routes.ts +99 -119
  694. package/src/runtime/routes/settings-routes.ts +10 -4
  695. package/src/runtime/routes/watcher-routes.ts +1 -3
  696. package/src/runtime/trust-class.test.ts +7 -6
  697. package/src/runtime/trust-class.ts +9 -17
  698. package/src/schedule/__tests__/worker-control.test.ts +36 -5
  699. package/src/schedule/__tests__/worker-mcp-bootstrap.test.ts +107 -0
  700. package/src/schedule/__tests__/worker-mcp-readiness.test.ts +173 -0
  701. package/src/schedule/__tests__/worker-mcp-tools.test.ts +13 -47
  702. package/src/schedule/run-script.ts +2 -2
  703. package/src/schedule/scheduler.ts +47 -11
  704. package/src/schedule/tool-surface-readiness.ts +65 -0
  705. package/src/schedule/worker-control.ts +15 -5
  706. package/src/schedule/worker-mcp.ts +85 -0
  707. package/src/schedule/worker.ts +9 -31
  708. package/src/security/secure-keys.ts +74 -112
  709. package/src/skills/clawhub.ts +3 -8
  710. package/src/skills/managed-store.ts +1 -5
  711. package/src/stt/types.ts +4 -0
  712. package/src/subagent/notify.ts +54 -34
  713. package/src/subagent/parent-notification.ts +6 -0
  714. package/src/telemetry/__tests__/phone-call-funnel.test.ts +72 -0
  715. package/src/telemetry/phone-call-funnel.ts +144 -0
  716. package/src/tools/__tests__/tool-schema-root-combinator-guard.test.ts +81 -0
  717. package/src/tools/ask-question/ask-question-tool.test.ts +167 -5
  718. package/src/tools/ask-question/ask-question-tool.ts +120 -22
  719. package/src/tools/browser/browser-execution.ts +29 -60
  720. package/src/tools/browser/cdp-client/__tests__/cdp-dom-helpers.test.ts +561 -39
  721. package/src/tools/browser/cdp-client/cdp-dom-helpers.ts +435 -39
  722. package/src/tools/browser/cdp-client/cdp-inspect-client.ts +1 -8
  723. package/src/tools/client-os.ts +15 -0
  724. package/src/tools/computer-use/skill-proxy-bridge.ts +1 -1
  725. package/src/tools/computer-use/target.ts +42 -0
  726. package/src/tools/document/document-tool.ts +13 -18
  727. package/src/tools/execution-target.ts +14 -16
  728. package/src/tools/host-terminal/host-shell.ts +16 -7
  729. package/src/tools/network/__tests__/web-fetch-tinyfish.test.ts +250 -0
  730. package/src/tools/network/__tests__/web-search.test.ts +203 -0
  731. package/src/tools/network/web-fetch.ts +274 -7
  732. package/src/tools/network/web-search.ts +468 -6
  733. package/src/tools/permission-checker.ts +3 -3
  734. package/src/tools/policy-context.ts +3 -1
  735. package/src/tools/shared/filesystem/file-ops-service.ts +1 -31
  736. package/src/tools/shared/shell-output.test.ts +10 -0
  737. package/src/tools/shared/shell-output.ts +14 -2
  738. package/src/tools/skills/load.ts +2 -1
  739. package/src/tools/skills/sandbox-runner.ts +24 -4
  740. package/src/tools/skills/scaffold-managed.ts +2 -1
  741. package/src/tools/skills/skill-script-runner.ts +7 -0
  742. package/src/tools/skills/skill-tool-factory.ts +15 -1
  743. package/src/tools/terminal/__tests__/safe-env.test.ts +44 -4
  744. package/src/tools/terminal/__tests__/sanitized-bash.test.ts +0 -14
  745. package/src/tools/terminal/safe-env.ts +39 -24
  746. package/src/tools/terminal/sanitized-bash.ts +15 -2
  747. package/src/tools/terminal/shell-launch.test.ts +162 -0
  748. package/src/tools/terminal/shell.test.ts +29 -0
  749. package/src/tools/terminal/shell.ts +15 -7
  750. package/src/tools/tool-approval-handler.ts +5 -1
  751. package/src/tools/tool-types.ts +3 -3
  752. package/src/tools/types.ts +8 -2
  753. package/src/util/__tests__/worker-process-command.test.ts +5 -4
  754. package/src/util/abort-reasons.ts +18 -26
  755. package/src/util/browser-human-verification.ts +14 -0
  756. package/src/util/canonicalize-identity.test.ts +21 -0
  757. package/src/util/canonicalize-identity.ts +16 -0
  758. package/src/util/errors.ts +13 -3
  759. package/src/util/host-process.test.ts +17 -1
  760. package/src/util/host-process.ts +24 -0
  761. package/src/util/provider-error-patterns.ts +3 -0
  762. package/src/util/unicode.ts +29 -0
  763. package/src/util/worker-process.ts +77 -35
  764. package/src/workspace/migrations/157-add-desktop-files-launcher.ts +94 -0
  765. package/src/workspace/migrations/__tests__/156-extract-workspace-mcp-json.test.ts +4 -10
  766. package/src/workspace/migrations/__tests__/157-add-desktop-files-launcher.test.ts +113 -0
  767. package/src/workspace/migrations/registry.ts +2 -0
  768. package/src/__tests__/computer-use-window-schema.test.ts +0 -24
  769. package/src/api/events/heartbeat-alert.ts +0 -20
  770. package/src/daemon/message-types/web-activity.ts +0 -65
  771. package/src/desktop/desktop-dependencies.test.ts +0 -131
  772. package/src/tools/computer-use/definitions.ts +0 -607
@@ -106,7 +106,10 @@ import { markProcessed } from "../../persistence/delivery-status.js";
106
106
  import { upsertBinding } from "../../persistence/external-conversation-store.js";
107
107
  import type { ContentBlock } from "../../providers/types.js";
108
108
  import { checkIngressForSecrets } from "../../security/secret-ingress.js";
109
- import { canonicalizeInboundIdentity } from "../../util/canonicalize-identity.js";
109
+ import {
110
+ canonicalizeInboundIdentity,
111
+ inboundIdentitiesMatch,
112
+ } from "../../util/canonicalize-identity.js";
110
113
  import { safeParseRecord } from "../../util/json.js";
111
114
  import { getLogger } from "../../util/logger.js";
112
115
  import { truncate } from "../../util/truncate.js";
@@ -126,6 +129,11 @@ import {
126
129
  enforceIngressAcl,
127
130
  } from "./inbound-stages/acl-enforcement.js";
128
131
  import { enforceAdmissionPolicy } from "./inbound-stages/admission-policy.js";
132
+ import {
133
+ type BackfilledSenderProvenance,
134
+ type BackfilledSenderProvenanceResolver,
135
+ createBackfilledSenderProvenanceResolver,
136
+ } from "./inbound-stages/backfill-sender-provenance.js";
129
137
  import { processChannelMessageInBackground } from "./inbound-stages/background-dispatch.js";
130
138
  import { handleBootstrapIntercept } from "./inbound-stages/bootstrap-intercept.js";
131
139
  import { handleEditIntercept } from "./inbound-stages/edit-intercept.js";
@@ -1531,8 +1539,7 @@ export async function handleChannelInbound({
1531
1539
  sourceMetadata.account.length > 0
1532
1540
  ? sourceMetadata.account
1533
1541
  : undefined;
1534
- const slackBotMentioned =
1535
- sourceChannel === "slack" && sourceMetadata?.slackBotMentioned === true;
1542
+ const botMentioned = sourceMetadata?.botMentioned === true;
1536
1543
 
1537
1544
  // ── DM cold-start backfill ──
1538
1545
  // First time a Slack DM without thread_ts lands in a conversation that
@@ -1625,7 +1632,7 @@ export async function handleChannelInbound({
1625
1632
  approvalCopyGenerator,
1626
1633
  chatType: sourceChatType,
1627
1634
  clientTimezone: inboundClientTimezone,
1628
- slackBotMentioned,
1635
+ botMentioned,
1629
1636
  slackInbound,
1630
1637
  channelInbound,
1631
1638
  });
@@ -1836,6 +1843,7 @@ async function persistBackfilledSlackMessage(params: {
1836
1843
  message: ProviderMessage;
1837
1844
  account?: string;
1838
1845
  guardianExternalUserId?: string;
1846
+ resolveSenderProvenance: BackfilledSenderProvenanceResolver;
1839
1847
  }): Promise<boolean> {
1840
1848
  const { message } = params;
1841
1849
 
@@ -1873,10 +1881,19 @@ async function persistBackfilledSlackMessage(params: {
1873
1881
  message.metadata,
1874
1882
  "actorTimezoneLabel",
1875
1883
  );
1876
- const isGuardian = isBackfilledSlackGuardianMessage(
1884
+ const role = (await isBackfilledSlackAssistantMessage(
1877
1885
  message,
1878
- params.guardianExternalUserId,
1879
- );
1886
+ params.account,
1887
+ ))
1888
+ ? "assistant"
1889
+ : "user";
1890
+ // The assistant's own posts are replayed as assistant history, not as any
1891
+ // person's words, so only a person's row asks who its sender is.
1892
+ const provenance: BackfilledSenderProvenance =
1893
+ role === "user"
1894
+ ? await params.resolveSenderProvenance(actorExternalUserId)
1895
+ : { provenanceTrustClass: "unknown" };
1896
+ const isGuardian = provenance.provenanceTrustClass === "guardian";
1880
1897
  const slackTranscriptTimestampTimezone =
1881
1898
  resolveSlackTranscriptTimestampTimezone();
1882
1899
  const slackTimezoneFields = buildSlackTimezoneMetadata({
@@ -1900,13 +1917,6 @@ async function persistBackfilledSlackMessage(params: {
1900
1917
  ...(slackFiles.length > 0 ? { slackFiles } : {}),
1901
1918
  };
1902
1919
 
1903
- const role = (await isBackfilledSlackAssistantMessage(
1904
- message,
1905
- params.account,
1906
- ))
1907
- ? "assistant"
1908
- : "user";
1909
-
1910
1920
  const rawText = message.text ?? "";
1911
1921
 
1912
1922
  // `sentAt` is when the message was sent; the row's `createdAt` is when the
@@ -1945,7 +1955,7 @@ async function persistBackfilledSlackMessage(params: {
1945
1955
  metadata: {
1946
1956
  ...envelope,
1947
1957
  ...(sentAt !== undefined ? { sentAt } : {}),
1948
- provenanceTrustClass: isGuardian ? "guardian" : "unknown",
1958
+ ...provenance,
1949
1959
  provenanceSourceChannel: "slack",
1950
1960
  ...(params.guardianExternalUserId
1951
1961
  ? { provenanceGuardianExternalUserId: params.guardianExternalUserId }
@@ -2064,22 +2074,6 @@ async function buildBackfilledSlackContentBlocks(
2064
2074
  return blocks;
2065
2075
  }
2066
2076
 
2067
- function isBackfilledSlackGuardianMessage(
2068
- message: ProviderMessage,
2069
- guardianExternalUserId: string | undefined,
2070
- ): boolean {
2071
- const rawSenderId = message.sender?.id?.trim();
2072
- if (!rawSenderId || !guardianExternalUserId) {
2073
- return false;
2074
- }
2075
- const normalizedSender =
2076
- canonicalizeInboundIdentity("slack", rawSenderId) ?? rawSenderId;
2077
- const normalizedGuardian =
2078
- canonicalizeInboundIdentity("slack", guardianExternalUserId) ??
2079
- guardianExternalUserId.trim();
2080
- return normalizedSender === normalizedGuardian;
2081
- }
2082
-
2083
2077
  const SLACK_ASSISTANT_THREAD_PLACEHOLDER_TEXT = "New Assistant Thread";
2084
2078
 
2085
2079
  async function isSlackAssistantThreadPlaceholder(
@@ -2116,7 +2110,7 @@ async function isBackfilledSlackAssistantMessage(
2116
2110
  return false;
2117
2111
  }
2118
2112
 
2119
- if (rawSenderId && slackIdentityMatches(rawSenderId, botUserId)) {
2113
+ if (rawSenderId && inboundIdentitiesMatch("slack", rawSenderId, botUserId)) {
2120
2114
  return true;
2121
2115
  }
2122
2116
 
@@ -2132,7 +2126,7 @@ async function isBackfilledSlackAssistantMessage(
2132
2126
  const resolvedBotUserId = await resolveSlackBotUserId(account, rawBotId);
2133
2127
  return (
2134
2128
  typeof resolvedBotUserId === "string" &&
2135
- slackIdentityMatches(resolvedBotUserId, botUserId)
2129
+ inboundIdentitiesMatch("slack", resolvedBotUserId, botUserId)
2136
2130
  );
2137
2131
  } catch (err) {
2138
2132
  log.warn(
@@ -2143,14 +2137,6 @@ async function isBackfilledSlackAssistantMessage(
2143
2137
  }
2144
2138
  }
2145
2139
 
2146
- function slackIdentityMatches(left: string, right: string): boolean {
2147
- const canonicalSender =
2148
- canonicalizeInboundIdentity("slack", left) ?? left.trim();
2149
- const canonicalBot =
2150
- canonicalizeInboundIdentity("slack", right) ?? right.trim();
2151
- return canonicalSender === canonicalBot;
2152
- }
2153
-
2154
2140
  /**
2155
2141
  * Transient view of `slackFiles` that preserves the download URLs added by
2156
2142
  * `mapSlackFiles` on the in-flight `ProviderMessage`. These URLs never reach
@@ -2291,6 +2277,10 @@ async function runBackfillSlackDmIfCold(params: {
2291
2277
  // and a transcript that reads correctly when the renderer joins on
2292
2278
  // monotonic createdAt.
2293
2279
  const ordered = [...fetched].reverse();
2280
+ const resolveSenderProvenance = createBackfilledSenderProvenanceResolver(
2281
+ params.guardianExternalUserId,
2282
+ ordered,
2283
+ );
2294
2284
  for (const message of ordered) {
2295
2285
  if (seen.has(message.id)) {
2296
2286
  continue;
@@ -2310,6 +2300,7 @@ async function runBackfillSlackDmIfCold(params: {
2310
2300
  ...(params.guardianExternalUserId
2311
2301
  ? { guardianExternalUserId: params.guardianExternalUserId }
2312
2302
  : {}),
2303
+ resolveSenderProvenance,
2313
2304
  });
2314
2305
  seen.add(message.id);
2315
2306
  if (stored) {
@@ -2891,6 +2882,10 @@ export async function triggerSlackThreadBackfillIfNeeded(params: {
2891
2882
  }
2892
2883
 
2893
2884
  let persisted = 0;
2885
+ const resolveSenderProvenance = createBackfilledSenderProvenanceResolver(
2886
+ guardianExternalUserId,
2887
+ fetched,
2888
+ );
2894
2889
  for (const message of fetched) {
2895
2890
  if (!message.id) {
2896
2891
  continue;
@@ -2908,6 +2903,7 @@ export async function triggerSlackThreadBackfillIfNeeded(params: {
2908
2903
  message,
2909
2904
  ...(account ? { account } : {}),
2910
2905
  ...(guardianExternalUserId ? { guardianExternalUserId } : {}),
2906
+ resolveSenderProvenance,
2911
2907
  });
2912
2908
  threadState.storedChannelTs.add(message.id);
2913
2909
  if (stored) {
@@ -0,0 +1,168 @@
1
+ import { afterAll, beforeEach, describe, expect, spyOn, test } from "bun:test";
2
+
3
+ import type { TrustVerdict } from "@vellumai/gateway-client";
4
+
5
+ import type { InboundTrustReadResult } from "../../../calls/inbound-trust-reader.js";
6
+ import * as inboundTrustReader from "../../../calls/inbound-trust-reader.js";
7
+ import type { Message as ProviderMessage } from "../../../messaging/provider-types.js";
8
+ import { createBackfilledSenderProvenanceResolver } from "./backfill-sender-provenance.js";
9
+
10
+ const readsBySender = new Map<string, InboundTrustReadResult>();
11
+ const readInboundTrustMock = spyOn(
12
+ inboundTrustReader,
13
+ "readInboundTrust",
14
+ ).mockImplementation(
15
+ async (input) =>
16
+ readsBySender.get(input.actorExternalId ?? "") ?? { ok: false },
17
+ );
18
+
19
+ afterAll(() => {
20
+ readInboundTrustMock.mockRestore();
21
+ });
22
+
23
+ const GUARDIAN_ID = "U_GUARDIAN";
24
+
25
+ function memberVerdict(
26
+ trustClass: TrustVerdict["trustClass"],
27
+ contactId: string,
28
+ status: string,
29
+ ): TrustVerdict {
30
+ return {
31
+ trustClass,
32
+ canonicalSenderId: `U_${contactId}`,
33
+ contactId,
34
+ channelId: `ch-${contactId}`,
35
+ status,
36
+ policy: "allow",
37
+ };
38
+ }
39
+
40
+ function readOf(verdict: TrustVerdict): InboundTrustReadResult {
41
+ return { ok: true, verdict, admissionPolicy: null };
42
+ }
43
+
44
+ beforeEach(() => {
45
+ readsBySender.clear();
46
+ readInboundTrustMock.mockClear();
47
+ });
48
+
49
+ describe("createBackfilledSenderProvenanceResolver", () => {
50
+ test("stamps the gateway verdict's trust class and contact id", async () => {
51
+ readsBySender.set(
52
+ "U_ALICE",
53
+ readOf(memberVerdict("trusted_contact", "c-alice", "active")),
54
+ );
55
+ readsBySender.set(
56
+ "U_BOB",
57
+ readOf(memberVerdict("unverified_contact", "c-bob", "unverified")),
58
+ );
59
+ const resolve = createBackfilledSenderProvenanceResolver(GUARDIAN_ID, []);
60
+
61
+ expect(await resolve("U_ALICE")).toEqual({
62
+ provenanceTrustClass: "trusted_contact",
63
+ provenanceContactId: "c-alice",
64
+ });
65
+ expect(await resolve("U_BOB")).toEqual({
66
+ provenanceTrustClass: "unverified_contact",
67
+ provenanceContactId: "c-bob",
68
+ });
69
+ expect(readInboundTrustMock).toHaveBeenCalledWith({
70
+ channelType: "slack",
71
+ actorExternalId: "U_ALICE",
72
+ });
73
+ });
74
+
75
+ test("a stranger verdict carries no contact id and no lookup-failed marker", async () => {
76
+ readsBySender.set(
77
+ "U_STRANGER",
78
+ readOf({ trustClass: "unknown", canonicalSenderId: "U_STRANGER" }),
79
+ );
80
+ const resolve = createBackfilledSenderProvenanceResolver(GUARDIAN_ID, []);
81
+
82
+ expect(await resolve("U_STRANGER")).toEqual({
83
+ provenanceTrustClass: "unknown",
84
+ });
85
+ });
86
+
87
+ test("an unreadable verdict falls back to the guardian address comparison and marks the failed lookup", async () => {
88
+ const resolve = createBackfilledSenderProvenanceResolver(GUARDIAN_ID, []);
89
+
90
+ expect(await resolve(GUARDIAN_ID)).toEqual({
91
+ provenanceTrustClass: "guardian",
92
+ provenanceLookupFailed: true,
93
+ });
94
+ expect(await resolve("U_ALICE")).toEqual({
95
+ provenanceTrustClass: "unknown",
96
+ provenanceLookupFailed: true,
97
+ });
98
+ });
99
+
100
+ test("a resolver-failure verdict is not trusted for class or contact id", async () => {
101
+ readsBySender.set(
102
+ "U_ALICE",
103
+ readOf({
104
+ ...memberVerdict("trusted_contact", "c-alice", "active"),
105
+ resolutionFailed: true,
106
+ }),
107
+ );
108
+ const resolve = createBackfilledSenderProvenanceResolver(GUARDIAN_ID, []);
109
+
110
+ expect(await resolve("U_ALICE")).toEqual({
111
+ provenanceTrustClass: "unknown",
112
+ provenanceLookupFailed: true,
113
+ });
114
+ });
115
+
116
+ test("reads the gateway once per distinct sender", async () => {
117
+ readsBySender.set(
118
+ "U_ALICE",
119
+ readOf(memberVerdict("trusted_contact", "c-alice", "active")),
120
+ );
121
+ const resolve = createBackfilledSenderProvenanceResolver(GUARDIAN_ID, []);
122
+
123
+ await Promise.all([
124
+ resolve("U_ALICE"),
125
+ resolve(" U_ALICE "),
126
+ resolve("U_BOB"),
127
+ ]);
128
+ await resolve("U_ALICE");
129
+
130
+ expect(readInboundTrustMock).toHaveBeenCalledTimes(2);
131
+ });
132
+
133
+ test("a row with no sender id is unknown without a gateway read or a lookup-failed marker", async () => {
134
+ const resolve = createBackfilledSenderProvenanceResolver(GUARDIAN_ID, []);
135
+
136
+ expect(await resolve(undefined)).toEqual({
137
+ provenanceTrustClass: "unknown",
138
+ });
139
+ expect(readInboundTrustMock).not.toHaveBeenCalled();
140
+ });
141
+
142
+ test("starts every sender's read up front and together, bots included", () => {
143
+ const message = (id: string, senderId: string, isBot = false) =>
144
+ ({
145
+ id,
146
+ conversationId: "C1",
147
+ sender: { id: senderId, name: senderId },
148
+ text: "hi",
149
+ timestamp: 0,
150
+ platform: "slack",
151
+ ...(isBot ? { metadata: { isBot: true } } : {}),
152
+ }) satisfies ProviderMessage;
153
+
154
+ createBackfilledSenderProvenanceResolver(GUARDIAN_ID, [
155
+ message("1", "U_ALICE"),
156
+ message("2", "B_THIRD_PARTY", true),
157
+ message("3", "U_ALICE"),
158
+ message("4", "U_BOB"),
159
+ ]);
160
+
161
+ // Every read is in flight before any of them has settled.
162
+ expect(readInboundTrustMock.mock.calls.map(([input]) => input)).toEqual([
163
+ { channelType: "slack", actorExternalId: "U_ALICE" },
164
+ { channelType: "slack", actorExternalId: "B_THIRD_PARTY" },
165
+ { channelType: "slack", actorExternalId: "U_BOB" },
166
+ ]);
167
+ });
168
+ });
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Provenance for backfilled Slack rows, resolved per sender.
3
+ *
4
+ * A backfilled row was written by someone other than the live inbound actor,
5
+ * so the live turn's trust context says nothing about it. Each sender is
6
+ * classified by the same gateway verdict live ingress uses
7
+ * (`resolve_inbound_trust`), so a backfilled row and a live row from the same
8
+ * person carry the same trust class and contact id. The verdict answers for
9
+ * the contact list at import time, which is when the row is written.
10
+ *
11
+ * When the verdict is unreadable or unusable, the row falls back to the
12
+ * guardian-address comparison, carries no contact id, and is marked
13
+ * `provenanceLookupFailed` so it stays distinguishable from a stranger.
14
+ *
15
+ * A read goes through `readInboundTrust`, so it also refreshes the
16
+ * member-verdict cache for that sender, as a live inbound read does.
17
+ */
18
+
19
+ import { readInboundTrust } from "../../../calls/inbound-trust-reader.js";
20
+ import type { ActorAuthorProvenance } from "../../../daemon/message-provenance.js";
21
+ import type { Message as ProviderMessage } from "../../../messaging/provider-types.js";
22
+ import { inboundIdentitiesMatch } from "../../../util/canonicalize-identity.js";
23
+ import type { TrustClass } from "../../trust-class.js";
24
+ import {
25
+ verdictMemberFromVerdict,
26
+ verdictUsability,
27
+ } from "../../trust-verdict-consumer.js";
28
+
29
+ export interface BackfilledSenderProvenance extends ActorAuthorProvenance {
30
+ provenanceTrustClass: TrustClass;
31
+ /** The lookup ran and returned no usable verdict; see `messageMetadataSchema`. */
32
+ provenanceLookupFailed?: true;
33
+ }
34
+
35
+ /**
36
+ * Resolves a backfilled message's sender provenance. One resolver serves one
37
+ * backfill pass and reads the gateway at most once per distinct sender.
38
+ */
39
+ export type BackfilledSenderProvenanceResolver = (
40
+ senderId: string | undefined,
41
+ ) => Promise<BackfilledSenderProvenance>;
42
+
43
+ export function createBackfilledSenderProvenanceResolver(
44
+ guardianExternalUserId: string | undefined,
45
+ messages: readonly ProviderMessage[],
46
+ ): BackfilledSenderProvenanceResolver {
47
+ const bySender = new Map<string, Promise<BackfilledSenderProvenance>>();
48
+ const resolve: BackfilledSenderProvenanceResolver = (senderId) => {
49
+ const trimmed = senderId?.trim();
50
+ if (!trimmed) {
51
+ return Promise.resolve({ provenanceTrustClass: "unknown" });
52
+ }
53
+ let pending = bySender.get(trimmed);
54
+ if (!pending) {
55
+ pending = resolveSenderProvenance(trimmed, guardianExternalUserId);
56
+ bySender.set(trimmed, pending);
57
+ }
58
+ return pending;
59
+ };
60
+ // Every sender's read starts now, so the pass waits on the slowest read
61
+ // rather than their sum. Bots included: a third-party bot's post is a user
62
+ // row, and singling out this assistant's own posts needs an async lookup.
63
+ // A read reports failure as a result, never a rejection.
64
+ for (const message of messages) {
65
+ void resolve(message.sender?.id);
66
+ }
67
+ return resolve;
68
+ }
69
+
70
+ async function resolveSenderProvenance(
71
+ senderId: string,
72
+ guardianExternalUserId: string | undefined,
73
+ ): Promise<BackfilledSenderProvenance> {
74
+ const read = await readInboundTrust({
75
+ channelType: "slack",
76
+ actorExternalId: senderId,
77
+ });
78
+ if (read.ok) {
79
+ const usability = verdictUsability(read.verdict);
80
+ if (usability.usable) {
81
+ const member = verdictMemberFromVerdict(usability.verdict);
82
+ return {
83
+ provenanceTrustClass: usability.verdict.trustClass,
84
+ ...(member ? { provenanceContactId: member.contactId } : {}),
85
+ };
86
+ }
87
+ }
88
+ return {
89
+ provenanceTrustClass:
90
+ guardianExternalUserId !== undefined &&
91
+ inboundIdentitiesMatch("slack", senderId, guardianExternalUserId)
92
+ ? "guardian"
93
+ : "unknown",
94
+ provenanceLookupFailed: true,
95
+ };
96
+ }
@@ -173,10 +173,10 @@ import {
173
173
  } from "../../../daemon/conversation-registry.js";
174
174
  import type { TrustContext } from "../../../daemon/trust-context-types.js";
175
175
  import type { MessageProcessor } from "../../http-types.js";
176
+ import { hasDeliverableAssistantText } from "../../no-response.js";
176
177
  import {
177
178
  isBoundGuardianActor,
178
179
  processChannelMessageInBackground,
179
- shouldShowActivityForText,
180
180
  shouldShowActivityImmediately,
181
181
  } from "./background-dispatch.js";
182
182
  import { __resetChannelTurnAdmissionForTests } from "./channel-turn-admission.js";
@@ -260,6 +260,35 @@ describe("processChannelMessageInBackground — reply delivery", () => {
260
260
  const flush = (): Promise<void> =>
261
261
  new Promise((resolve) => setTimeout(resolve, 10));
262
262
 
263
+ test("names the inbound sender as the persisted message's author", async () => {
264
+ let capturedAuthor: TrustContext | undefined;
265
+ const processMessage: MessageProcessor = async (
266
+ _conversationId,
267
+ _content,
268
+ options,
269
+ ) => {
270
+ capturedAuthor = options?.author;
271
+ return { messageId: "user-msg-author" };
272
+ };
273
+
274
+ processChannelMessageInBackground({
275
+ processMessage,
276
+ conversationId: "conv-author",
277
+ eventId: "evt-author",
278
+ content: "the export endpoint needs a scoped token",
279
+ sourceChannel: "slack",
280
+ sourceInterface: "slack",
281
+ externalChatId: "C-AUTHOR",
282
+ trustCtx,
283
+ metadataHints: [],
284
+ replyCallbackUrl: "https://example.test/deliver/slack?channel=C-AUTHOR",
285
+ });
286
+
287
+ await flush();
288
+
289
+ expect(capturedAuthor).toBe(trustCtx);
290
+ });
291
+
263
292
  test("records callback delivery failures without failing processing", async () => {
264
293
  const conversationId = "conv-delivery-failure";
265
294
  const channelId = "C-DELIVERY-FAILURE";
@@ -1070,14 +1099,14 @@ describe("channel activity timing", () => {
1070
1099
  });
1071
1100
 
1072
1101
  test("recognizes only deliverable text as a reason to show activity", () => {
1073
- expect(shouldShowActivityForText("")).toBe(false);
1074
- expect(shouldShowActivityForText(" ")).toBe(false);
1075
- expect(shouldShowActivityForText("<")).toBe(false);
1076
- expect(shouldShowActivityForText("<no_response")).toBe(false);
1077
- expect(shouldShowActivityForText("<no_response/>")).toBe(false);
1078
- expect(shouldShowActivityForText(" <no_response /> ")).toBe(false);
1079
- expect(shouldShowActivityForText("Real response.")).toBe(true);
1080
- expect(shouldShowActivityForText("<no_response/>\nReal response.")).toBe(
1102
+ expect(hasDeliverableAssistantText("")).toBe(false);
1103
+ expect(hasDeliverableAssistantText(" ")).toBe(false);
1104
+ expect(hasDeliverableAssistantText("<")).toBe(false);
1105
+ expect(hasDeliverableAssistantText("<no_response")).toBe(false);
1106
+ expect(hasDeliverableAssistantText("<no_response/>")).toBe(false);
1107
+ expect(hasDeliverableAssistantText(" <no_response /> ")).toBe(false);
1108
+ expect(hasDeliverableAssistantText("Real response.")).toBe(true);
1109
+ expect(hasDeliverableAssistantText("<no_response/>\nReal response.")).toBe(
1081
1110
  true,
1082
1111
  );
1083
1112
  });
@@ -1169,7 +1198,7 @@ describe("channel activity timing", () => {
1169
1198
  externalChatId: channelId,
1170
1199
  trustCtx,
1171
1200
  metadataHints: [],
1172
- slackBotMentioned: true,
1201
+ botMentioned: true,
1173
1202
  replyCallbackUrl: `https://example.test/deliver/slack?channel=${channelId}&threadTs=${threadTs}`,
1174
1203
  });
1175
1204
 
@@ -105,8 +105,11 @@ export interface BackgroundProcessingParams {
105
105
  chatType?: string;
106
106
  /** IANA timezone reported by the active client for the current turn. */
107
107
  clientTimezone?: string;
108
- /** Slack app_mention/direct bot mention signal from the gateway. */
109
- slackBotMentioned?: boolean;
108
+ /**
109
+ * The message addresses the assistant by name, as stated by the channel
110
+ * (`sourceMetadata.botMentioned`). Absent means not established.
111
+ */
112
+ botMentioned?: boolean;
110
113
  /**
111
114
  * Slack-specific inbound metadata extracted at the HTTP boundary. Threaded
112
115
  * through to `persistUserMessage` so the row can be tagged with a
@@ -166,7 +169,7 @@ export function processChannelMessageInBackground(
166
169
  sourceLanguageCode,
167
170
  chatType,
168
171
  clientTimezone,
169
- slackBotMentioned,
172
+ botMentioned,
170
173
  slackInbound,
171
174
  channelInbound,
172
175
  slackReactionRowMeta,
@@ -204,7 +207,7 @@ export function processChannelMessageInBackground(
204
207
  initiatorUserId: slackInbound?.actorExternalUserId,
205
208
  startImmediately: shouldShowActivityImmediately({
206
209
  chatType,
207
- botMentioned: slackBotMentioned,
210
+ botMentioned,
208
211
  }),
209
212
  });
210
213
  const stopApprovalWatcher = replyCallbackUrl
@@ -283,6 +286,7 @@ export function processChannelMessageInBackground(
283
286
  },
284
287
  assistantId,
285
288
  trustContext: trustCtx,
289
+ author: trustCtx,
286
290
  isInteractive: resolveRoutingState(trustCtx).promptWaitingAllowed,
287
291
  ...(displayContent !== undefined ? { displayContent } : {}),
288
292
  ...(cmdIntent ? { commandIntent: cmdIntent } : {}),
@@ -449,10 +453,6 @@ type ChannelActivityController = {
449
453
  stop: () => void;
450
454
  };
451
455
 
452
- export function shouldShowActivityForText(text: string): boolean {
453
- return hasDeliverableAssistantText(text);
454
- }
455
-
456
456
  /**
457
457
  * Room shapes with one other participant, in each channel's own word for it.
458
458
  *
@@ -610,7 +610,7 @@ function startChannelActivity(params: {
610
610
  return;
611
611
  }
612
612
  observedAssistantText += msg.text;
613
- if (shouldShowActivityForText(observedAssistantText)) {
613
+ if (hasDeliverableAssistantText(observedAssistantText)) {
614
614
  show();
615
615
  }
616
616
  },
@@ -35,6 +35,7 @@ import { createApprovalCopyGenerator } from "../../../daemon/approval-generators
35
35
  import { findConversation } from "../../../daemon/conversation-registry.js";
36
36
  import { getDiskPressureStatus } from "../../../daemon/disk-pressure-guard.js";
37
37
  import { classifyDiskPressureTurnPolicy } from "../../../daemon/disk-pressure-policy.js";
38
+ import { actorAuthorProvenance } from "../../../daemon/message-provenance.js";
38
39
  import { processMessage } from "../../../daemon/process-message.js";
39
40
  import { renderReactionHistoryText } from "../../../daemon/reaction-history-render.js";
40
41
  import type { TrustContext } from "../../../daemon/trust-context-types.js";
@@ -544,6 +545,7 @@ async function persistReactionAsMessage(params: {
544
545
  {
545
546
  metadata: {
546
547
  ...provenanceFromTrustContext(params.trustCtx),
548
+ ...actorAuthorProvenance(params.trustCtx),
547
549
  ...buildReactionRowEnvelope(facts),
548
550
  },
549
551
  skipIndexing: true,
@@ -16,6 +16,10 @@ import { randomUUID } from "node:crypto";
16
16
 
17
17
  import { getUserSelectableProfilesForProvider } from "../../config/default-profile-catalog.js";
18
18
  import { loadConfig } from "../../config/loader.js";
19
+ import {
20
+ nonTextConversationProfileMessage,
21
+ profileSupportsTextGeneration,
22
+ } from "../../config/profile-text-generation.js";
19
23
  import { findConversation } from "../../daemon/conversation-registry.js";
20
24
  import {
21
25
  getConversation,
@@ -163,10 +167,16 @@ export async function setInferenceProfileSession({
163
167
  );
164
168
  }
165
169
 
170
+ const selectable = profiles as Record<string, unknown>;
171
+ const selected = selectable[profile] as Record<string, unknown>;
172
+ if (!profileSupportsTextGeneration(selected, selectable)) {
173
+ throw new BadRequestError(nonTextConversationProfileMessage(profile));
174
+ }
175
+
166
176
  // Pinning a profile that provably cannot dispatch turns the next turn in
167
177
  // this conversation into a hard failure, so refuse the pin the same way the
168
178
  // active-profile setter does.
169
- const entry = profiles[profile] as Record<string, unknown>;
179
+ const entry = selected;
170
180
  const availability = await computeProfileAvailability(entry);
171
181
  if (isUnavailable(availability)) {
172
182
  throw new BadRequestError(
@@ -29,6 +29,10 @@ import {
29
29
  getConfigReadOnly,
30
30
  loadRawConfig,
31
31
  } from "../../config/loader.js";
32
+ import {
33
+ nonTextConversationProfileMessage,
34
+ profileSupportsTextGeneration,
35
+ } from "../../config/profile-text-generation.js";
32
36
  import {
33
37
  ProfileEntry,
34
38
  routingIdentityModelIssue,
@@ -891,6 +895,11 @@ async function handleSetActiveProfile({ body = {} }: RouteHandlerArgs) {
891
895
  `Profile "${name}" is disabled and cannot be set as the active profile. Enable it first, or pick another.`,
892
896
  );
893
897
  }
898
+ if (
899
+ !profileSupportsTextGeneration(entry, effective as Record<string, unknown>)
900
+ ) {
901
+ throw new BadRequestError(nonTextConversationProfileMessage(name));
902
+ }
894
903
  // No escape hatch here: an active profile that cannot dispatch locks the
895
904
  // user out of chat entirely, and nothing about the write signals that.
896
905
  await guardProfileAvailability({
@@ -1025,7 +1025,7 @@ function anthropicMessageSections(
1025
1025
  toolName: asString(block.name) ?? asString(block.tool_use_id),
1026
1026
  data:
1027
1027
  type === "web_search_tool_result"
1028
- ? sanitizeAnthropicWebSearchToolResultData(block)
1028
+ ? sanitizeAnthropicStructuredValue(block)
1029
1029
  : undefined,
1030
1030
  text: collectAnthropicToolResultText(block),
1031
1031
  });
@@ -1397,12 +1397,6 @@ function collectAnthropicToolResultText(
1397
1397
  return collectAnthropicText(block.content);
1398
1398
  }
1399
1399
 
1400
- function sanitizeAnthropicWebSearchToolResultData(
1401
- block: Record<string, unknown>,
1402
- ): unknown {
1403
- return sanitizeAnthropicStructuredValue(block);
1404
- }
1405
-
1406
1400
  function sanitizeAnthropicStructuredValue(value: unknown): unknown {
1407
1401
  if (Array.isArray(value)) {
1408
1402
  return value.map((entry) => sanitizeAnthropicStructuredValue(entry));