@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
@@ -383,6 +383,32 @@ describe("setInferenceProfileSession", () => {
383
383
  );
384
384
  });
385
385
 
386
+ test("open with a non-text catalog model — refuses the pin", async () => {
387
+ const conv = createConversation("sess-handler-jev-profile");
388
+ setConfig("llm", {
389
+ profiles: {
390
+ jev: {
391
+ source: "user",
392
+ provider: "typesafe",
393
+ model: "jev-latest",
394
+ status: "active",
395
+ },
396
+ },
397
+ });
398
+ try {
399
+ const promise = setInferenceProfileSession({
400
+ conversationId: conv.id,
401
+ profile: "jev",
402
+ });
403
+ await expect(promise).rejects.toThrow(
404
+ /structured answers rather than chat text/,
405
+ );
406
+ expect(getConversation(conv.id)?.inferenceProfile).toBeNull();
407
+ } finally {
408
+ setConfig("llm", {});
409
+ }
410
+ });
411
+
386
412
  test("open with an unavailable profile — refuses the pin and names the fix", async () => {
387
413
  const conv = createConversation("sess-handler-unavailable-profile");
388
414
  managedProxyEnabled = false;
@@ -21,6 +21,7 @@ import {
21
21
  abortedToolResultText,
22
22
  CANCELLED_TOOL_RESULT_TEXT,
23
23
  createAbortReason,
24
+ INTERRUPTED_TURN_NOTE_TEXT,
24
25
  PREEMPTED_TOOL_RESULT_TEXT,
25
26
  } from "../util/abort-reasons.js";
26
27
 
@@ -378,8 +379,8 @@ describe("interruptRunningTurn", () => {
378
379
 
379
380
  test("arms the interrupted-turn note when the abort caught no tool call", async () => {
380
381
  // The abort landed during the provider call, so there is no `tool_use` to
381
- // answer and nothing in the history says the turn was cut off. The note
382
- // rides on the interrupting user message instead.
382
+ // answer. The note on the interrupting user message is the only thing that
383
+ // tells the model what happened.
383
384
  const turn = registerBusyTurn({
384
385
  messages: [
385
386
  { role: "user", content: [{ type: "text", text: "research this" }] },
@@ -393,9 +394,9 @@ describe("interruptRunningTurn", () => {
393
394
  expect(persisted).toEqual([]);
394
395
  });
395
396
 
396
- test("leaves the note disarmed when the repair answered a tool call", async () => {
397
- // The preempted `tool_result` already tells the model a message cut it
398
- // off, so repeating that in a note on the message is noise.
397
+ test("arms the note alongside the fact-only result of a stopped tool call", async () => {
398
+ // The two texts do different jobs: the `tool_result` states what happened
399
+ // to that one call, the note says what to do about the interruption.
399
400
  const turn = registerBusyTurn({
400
401
  messages: [assistantWithToolUse("tool-1")],
401
402
  });
@@ -404,13 +405,36 @@ describe("interruptRunningTurn", () => {
404
405
 
405
406
  expect(persisted).toHaveLength(1);
406
407
  expect(persisted[0].content).toContain(PREEMPTED_TOOL_RESULT_TEXT);
407
- expect(turn.conversation.pendingInterruptNote).toBe(false);
408
+ expect(turn.conversation.pendingInterruptNote).toBe(true);
409
+ });
410
+
411
+ test("arms exactly one note however many parallel calls were stopped", async () => {
412
+ // Every stopped call gets its own fact-only result; the behavior
413
+ // instruction is read once, off the interrupting message.
414
+ const turn = registerBusyTurn({
415
+ messages: [assistantWithToolUse("tool-1", "tool-2", "tool-3")],
416
+ });
417
+
418
+ await interruptRunningTurn(turn.conversation, { origin: "test" });
419
+
420
+ const results = JSON.parse(persisted[0].content) as Array<{
421
+ tool_use_id: string;
422
+ content: string;
423
+ }>;
424
+ expect(results.map((block) => block.tool_use_id)).toEqual([
425
+ "tool-1",
426
+ "tool-2",
427
+ "tool-3",
428
+ ]);
429
+ for (const block of results) {
430
+ expect(block.content).toBe(PREEMPTED_TOOL_RESULT_TEXT);
431
+ }
432
+ expect(turn.conversation.pendingInterruptNote).toBe(true);
408
433
  });
409
434
 
410
- test("leaves the note disarmed when the loop wrote its own preempted result", async () => {
435
+ test("arms the note when the loop wrote its own preempted result", async () => {
411
436
  // The agent loop's abort handler unwound cleanly and answered the batch
412
- // itself, so the repair finds nothing to do and the notice is already
413
- // there.
437
+ // itself, so the repair finds nothing to do. The note is owed all the same.
414
438
  const turn = registerBusyTurn({
415
439
  messages: [
416
440
  assistantWithToolUse("tool-1"),
@@ -421,12 +445,10 @@ describe("interruptRunningTurn", () => {
421
445
  await interruptRunningTurn(turn.conversation, { origin: "test" });
422
446
 
423
447
  expect(persisted).toEqual([]);
424
- expect(turn.conversation.pendingInterruptNote).toBe(false);
448
+ expect(turn.conversation.pendingInterruptNote).toBe(true);
425
449
  });
426
450
 
427
451
  test("arms the note when the cut-off tool call was an ordinary cancel", async () => {
428
- // A tail answered with the plain cancel wording is a stop the user made
429
- // earlier, not this interrupt's notice, so the note is still owed.
430
452
  const turn = registerBusyTurn({
431
453
  messages: [
432
454
  assistantWithToolUse("tool-1"),
@@ -633,6 +655,9 @@ describe("interruptRunningTurn", () => {
633
655
  // Armed instead, so the drain that runs the queued message repairs it.
634
656
  expect(turn.conversation.pendingInterruptRepair).toBe(true);
635
657
  expect(turn.activityEvents).toEqual([]);
658
+ // The message joins the queue rather than replacing the turn, so it is not
659
+ // the message that interrupted anything and carries no note.
660
+ expect(turn.conversation.pendingInterruptNote).toBe(false);
636
661
  });
637
662
 
638
663
  test("a second interrupt stops the turn the first one started", async () => {
@@ -932,7 +957,7 @@ describe("repairInterruptedToolUseBlocks", () => {
932
957
  } as unknown as Conversation;
933
958
  }
934
959
 
935
- test("writes one result per abandoned call, worded for a preemption", async () => {
960
+ test("writes one fact-only result per abandoned call", async () => {
936
961
  const messages: Message[] = [assistantWithToolUse("tool-1", "tool-2")];
937
962
 
938
963
  await repairInterruptedToolUseBlocks(fakeConversation(messages), {
@@ -961,31 +986,6 @@ describe("repairInterruptedToolUseBlocks", () => {
961
986
  );
962
987
  });
963
988
 
964
- test("reports the preempted result it wrote", async () => {
965
- const messages: Message[] = [assistantWithToolUse("tool-1")];
966
-
967
- const result = await repairInterruptedToolUseBlocks(
968
- fakeConversation(messages),
969
- { force: true },
970
- );
971
-
972
- expect(result.preemptedToolResultOnTail).toBe(true);
973
- });
974
-
975
- test("reports no preempted result on a history that ends on plain text", async () => {
976
- const messages: Message[] = [
977
- { role: "user", content: [{ type: "text", text: "hi" }] },
978
- { role: "assistant", content: [{ type: "text", text: "hello" }] },
979
- ];
980
-
981
- const result = await repairInterruptedToolUseBlocks(
982
- fakeConversation(messages),
983
- { force: true },
984
- );
985
-
986
- expect(result.preemptedToolResultOnTail).toBe(false);
987
- });
988
-
989
989
  test("adds nothing when the loop already wrote its own results", async () => {
990
990
  // The agent loop's abort handler synthesizes results of its own when it
991
991
  // unwinds cleanly, so exactly one result per call exists either way.
@@ -994,14 +994,12 @@ describe("repairInterruptedToolUseBlocks", () => {
994
994
  toolResult("tool-1", PREEMPTED_TOOL_RESULT_TEXT),
995
995
  ];
996
996
 
997
- const result = await repairInterruptedToolUseBlocks(
998
- fakeConversation(messages),
999
- { force: true },
1000
- );
997
+ await repairInterruptedToolUseBlocks(fakeConversation(messages), {
998
+ force: true,
999
+ });
1001
1000
 
1002
1001
  expect(messages).toHaveLength(2);
1003
1002
  expect(persisted).toEqual([]);
1004
- expect(result.preemptedToolResultOnTail).toBe(true);
1005
1003
  });
1006
1004
 
1007
1005
  test("does nothing on a history that ends on an ordinary assistant reply", async () => {
@@ -1083,3 +1081,19 @@ describe("abortedToolResultText", () => {
1083
1081
  );
1084
1082
  });
1085
1083
  });
1084
+
1085
+ describe("the interrupt texts", () => {
1086
+ test("the tool result states the fact and nothing more", () => {
1087
+ // Repeated once per stopped call in a parallel batch, so an instruction
1088
+ // here is read as many times as the batch was wide.
1089
+ expect(PREEMPTED_TOOL_RESULT_TEXT).toBe(
1090
+ "Stopped early because the user sent a new message. This call may have completed anyway; check before repeating it.",
1091
+ );
1092
+ });
1093
+
1094
+ test("the note carries the behavior and leads with the reply", () => {
1095
+ expect(INTERRUPTED_TURN_NOTE_TEXT).toBe(
1096
+ "<interrupted_turn>The user sent this while you were still working, so that work stopped. Reply right away, in a line or two, before any more thinking or tool calls, so the user knows you heard them. Then pick the earlier work back up only if it is still wanted. Never apologize for or mention the interruption.</interrupted_turn>",
1097
+ );
1098
+ });
1099
+ });
@@ -1,10 +1,9 @@
1
1
  /**
2
- * An interrupt whose abort landed during the provider call leaves no
3
- * `tool_use` to answer, so nothing in the history tells the model its turn was
4
- * cut off. The interrupting user message carries the notice instead, on its
5
- * LLM-facing content only: the persisted row stays exactly what the user
6
- * typed, so no client renders it, and `interruptedPriorTurn` in the row's
7
- * metadata is what rebuilds the identical block on every later load.
2
+ * The message that interrupted a turn carries the note saying what to do about
3
+ * the interruption, on its LLM-facing content only: the persisted row stays
4
+ * exactly what the user typed, so no client renders it, and
5
+ * `interruptedPriorTurn` in the row's metadata is what rebuilds the identical
6
+ * block on every later load.
8
7
  */
9
8
  import { beforeEach, describe, expect, test } from "bun:test";
10
9
 
@@ -14,10 +14,17 @@ import { setConfig } from "./helpers/set-config.js";
14
14
  // background indexing kicks in.
15
15
  setConfig("memory", { enabled: false, v2: { enabled: false } });
16
16
 
17
+ import {
18
+ linkAttachmentToMessage,
19
+ uploadAttachment,
20
+ } from "../persistence/attachments-store.js";
17
21
  import {
18
22
  addMessage,
19
23
  createConversation,
24
+ getMessageById,
25
+ updateMessageMetadata,
20
26
  } from "../persistence/conversation-crud.js";
27
+ import { COMPUTER_USE_SCREENSHOT_ATTACHMENT_IDS_KEY } from "../persistence/conversation-types.js";
21
28
  import { getDb } from "../persistence/db-connection.js";
22
29
  import { initializeDb } from "../persistence/db-init.js";
23
30
  import { handleListMessages } from "../runtime/routes/conversation-routes.js";
@@ -50,7 +57,15 @@ interface MessagePayload {
50
57
  toolCalls?: ToolCallPayload[];
51
58
  textSegments?: string[];
52
59
  contentOrder?: string[];
53
- contentBlocks?: Array<{ type: string; [key: string]: unknown }>;
60
+ contentBlocks?: Array<{
61
+ type: string;
62
+ attachment?: { id: string; computerUseScreenshot?: boolean };
63
+ [key: string]: unknown;
64
+ }>;
65
+ attachments?: Array<{
66
+ id: string;
67
+ computerUseScreenshot?: boolean;
68
+ }>;
54
69
  }
55
70
 
56
71
  describe("handleListMessages tool_result merging", () => {
@@ -217,6 +232,75 @@ describe("handleListMessages tool_result merging", () => {
217
232
  });
218
233
  });
219
234
 
235
+ test("preserves donor automatic screenshot provenance during consolidation", async () => {
236
+ const conv = createConversation();
237
+ await addMessage(
238
+ conv.id,
239
+ "user",
240
+ JSON.stringify([{ type: "text", text: "inspect this" }]),
241
+ );
242
+ const anchor = await addMessage(
243
+ conv.id,
244
+ "assistant",
245
+ JSON.stringify([{ type: "text", text: "Working." }]),
246
+ { metadata: { anchorContext: "kept" } },
247
+ );
248
+ const explicit = await uploadAttachment(
249
+ "notes.pdf",
250
+ "application/pdf",
251
+ "JVBERg==",
252
+ );
253
+ linkAttachmentToMessage(anchor.id, explicit.id, 0);
254
+
255
+ const donor = await addMessage(
256
+ conv.id,
257
+ "assistant",
258
+ JSON.stringify([{ type: "text", text: "Done." }]),
259
+ );
260
+ const screenshot = await uploadAttachment(
261
+ "computer-use-click.png",
262
+ "image/png",
263
+ "c2NyZWVuc2hvdA==",
264
+ );
265
+ linkAttachmentToMessage(donor.id, screenshot.id, 0);
266
+ updateMessageMetadata(donor.id, {
267
+ [COMPUTER_USE_SCREENSHOT_ATTACHMENT_IDS_KEY]: [screenshot.id],
268
+ });
269
+
270
+ const anchorBefore = getMessageById(anchor.id)?.metadata;
271
+ const donorBefore = getMessageById(donor.id)?.metadata;
272
+ const response = await handleListMessages(createTestArgs(conv.id));
273
+ const body = response as { messages: MessagePayload[] };
274
+ const merged = body.messages[1]!;
275
+
276
+ expect(merged.attachments?.map((attachment) => attachment.id)).toEqual([
277
+ explicit.id,
278
+ screenshot.id,
279
+ ]);
280
+ expect(merged.attachments?.[0]?.computerUseScreenshot).toBeUndefined();
281
+ expect(merged.attachments?.[1]?.computerUseScreenshot).toBe(true);
282
+ const attachmentBlocks = merged.contentBlocks?.filter(
283
+ (block) => block.type === "attachment",
284
+ );
285
+ expect(
286
+ attachmentBlocks?.map((block) =>
287
+ block.type === "attachment" ? block.attachment?.id : undefined,
288
+ ),
289
+ ).toEqual([explicit.id, screenshot.id]);
290
+ expect(
291
+ attachmentBlocks?.[0]?.type === "attachment"
292
+ ? attachmentBlocks[0].attachment?.computerUseScreenshot
293
+ : undefined,
294
+ ).toBeUndefined();
295
+ expect(
296
+ attachmentBlocks?.[1]?.type === "attachment"
297
+ ? attachmentBlocks[1].attachment?.computerUseScreenshot
298
+ : undefined,
299
+ ).toBe(true);
300
+ expect(getMessageById(anchor.id)?.metadata).toBe(anchorBefore);
301
+ expect(getMessageById(donor.id)?.metadata).toBe(donorBefore);
302
+ });
303
+
220
304
  test("orphan tool_result at pagination boundary is suppressed entirely", async () => {
221
305
  const conv = createConversation();
222
306
  // Orphan tool_result with no preceding assistant in this page. The
@@ -2,7 +2,9 @@ import { readFileSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
  import { describe, expect, test } from "bun:test";
4
4
 
5
+ import { DEFAULT_PROVIDER_CHOICES } from "../config/schemas/llm.js";
5
6
  import {
7
+ catalogModelSupportsText,
6
8
  getCatalogProviderForModel,
7
9
  isModelInCatalog,
8
10
  PROVIDER_CATALOG,
@@ -56,6 +58,7 @@ interface ClientCatalogModel {
56
58
  supportsCaching?: boolean;
57
59
  supportsVision?: boolean;
58
60
  supportsToolUse?: boolean;
61
+ supportsText?: boolean;
59
62
  pricing?: {
60
63
  inputPer1mTokens: number;
61
64
  outputPer1mTokens: number;
@@ -204,6 +207,7 @@ describe("LLM catalog parity: daemon vs client", () => {
204
207
  expect(clientModel.supportsCaching).toBe(daemonModel.supportsCaching);
205
208
  expect(clientModel.supportsVision).toBe(daemonModel.supportsVision);
206
209
  expect(clientModel.supportsToolUse).toBe(daemonModel.supportsToolUse);
210
+ expect(clientModel.supportsText).toBe(daemonModel.supportsText);
207
211
  expect(clientModel.pricing).toEqual(daemonModel.pricing);
208
212
  expect(clientModel.featureFlag).toBe(daemonModel.featureFlag);
209
213
  }
@@ -229,6 +233,16 @@ describe("LLM catalog parity: daemon vs client", () => {
229
233
  }
230
234
  });
231
235
 
236
+ test("jev-latest opts out of chat text generation", () => {
237
+ expect(catalogModelSupportsText("typesafe", "jev-latest")).toBe(false);
238
+ expect(catalogModelSupportsText("anthropic", "claude-opus-4-8")).toBe(true);
239
+ expect(catalogModelSupportsText("openai-compatible", "local-model")).toBe(
240
+ true,
241
+ );
242
+ expect(DEFAULT_PROVIDER_CHOICES).not.toContain("typesafe");
243
+ expect(DEFAULT_PROVIDER_CHOICES).toContain("poolside");
244
+ });
245
+
232
246
  test("cache pricing rates are positive when defined", () => {
233
247
  for (const entry of PROVIDER_CATALOG) {
234
248
  for (const model of entry.models) {
@@ -252,7 +266,9 @@ describe("LLM catalog parity: daemon vs client", () => {
252
266
  });
253
267
 
254
268
  test("OpenRouter supportsCaching requires cache-read pricing", () => {
255
- const openrouter = PROVIDER_CATALOG.find((entry) => entry.id === "openrouter");
269
+ const openrouter = PROVIDER_CATALOG.find(
270
+ (entry) => entry.id === "openrouter",
271
+ );
256
272
  expect(openrouter).toBeDefined();
257
273
 
258
274
  for (const model of openrouter!.models) {
@@ -266,7 +282,9 @@ describe("LLM catalog parity: daemon vs client", () => {
266
282
  });
267
283
 
268
284
  test("OpenRouter cache-read pricing implies supportsCaching except xAI", () => {
269
- const openrouter = PROVIDER_CATALOG.find((entry) => entry.id === "openrouter");
285
+ const openrouter = PROVIDER_CATALOG.find(
286
+ (entry) => entry.id === "openrouter",
287
+ );
270
288
  expect(openrouter).toBeDefined();
271
289
 
272
290
  for (const model of openrouter!.models) {
@@ -288,7 +306,9 @@ describe("LLM catalog parity: daemon vs client", () => {
288
306
  });
289
307
 
290
308
  test("OpenRouter catalog drops ids OpenRouter no longer serves", () => {
291
- const openrouter = PROVIDER_CATALOG.find((entry) => entry.id === "openrouter");
309
+ const openrouter = PROVIDER_CATALOG.find(
310
+ (entry) => entry.id === "openrouter",
311
+ );
292
312
  expect(openrouter).toBeDefined();
293
313
  const ids = new Set(openrouter!.models.map((model) => model.id));
294
314
  expect(ids.has("deepseek/deepseek-v3.2-speciale")).toBe(false);
@@ -554,7 +574,10 @@ describe("LLM catalog parity: daemon vs client", () => {
554
574
 
555
575
  test("forced tool choice with thinking is scoped to OpenRouter Kimi K2.6", () => {
556
576
  expect(
557
- supportsForcedToolChoiceWithThinking("openrouter", "moonshotai/kimi-k2.6"),
577
+ supportsForcedToolChoiceWithThinking(
578
+ "openrouter",
579
+ "moonshotai/kimi-k2.6",
580
+ ),
558
581
  ).toBe(false);
559
582
  expect(
560
583
  supportsForcedToolChoiceWithThinking(
@@ -21,6 +21,7 @@ import { setConfig } from "./helpers/set-config.js";
21
21
 
22
22
  let mockGeminiKey: string | undefined = "test-gemini-key";
23
23
  let mockOpenAIKey: string | undefined = "test-openai-key";
24
+ let mockOpenRouterKey: string | undefined = "test-openrouter-key";
24
25
  let mockGenerateResult = {
25
26
  images: [{ mimeType: "image/png", dataBase64: "generated-data" }],
26
27
  text: "A beautiful image",
@@ -40,7 +41,7 @@ let mockGenerateDelayMs = 0;
40
41
  */
41
42
  function seedImageGenService(
42
43
  overrides: {
43
- provider?: "vellum" | "gemini" | "openai";
44
+ provider?: "vellum" | "gemini" | "openai" | "openrouter";
44
45
  model?: string;
45
46
  } = {},
46
47
  ): void {
@@ -64,6 +65,9 @@ mock.module("../security/secure-keys.js", () => ({
64
65
  if (provider === "openai") {
65
66
  return mockOpenAIKey;
66
67
  }
68
+ if (provider === "openrouter") {
69
+ return mockOpenRouterKey;
70
+ }
67
71
  return undefined;
68
72
  },
69
73
  }));
@@ -143,6 +147,7 @@ const CONFIG_DIR = join(
143
147
  beforeEach(() => {
144
148
  mockGeminiKey = "test-gemini-key";
145
149
  mockOpenAIKey = "test-openai-key";
150
+ mockOpenRouterKey = "test-openrouter-key";
146
151
  seedImageGenService();
147
152
  mockGenerateResult = {
148
153
  images: [{ mimeType: "image/png", dataBase64: "generated-data" }],
@@ -308,6 +313,49 @@ describe("image-studio skill script wrapper", () => {
308
313
  expect(result.content).not.toContain("No Gemini API key");
309
314
  });
310
315
 
316
+ test("OpenRouter accepts an arbitrary slug and keeps OpenRouter ownership", async () => {
317
+ seedImageGenService({
318
+ provider: "openrouter",
319
+ model: "google/gemini-3.1-flash-image-preview",
320
+ });
321
+
322
+ const result = await run(
323
+ { prompt: "a nebula", model: "black-forest-labs/flux" },
324
+ fakeContext,
325
+ );
326
+
327
+ expect(result.isError).toBe(false);
328
+ expect(lastGenerateProvider).toBe("openrouter");
329
+ expect(lastGenerateCredentials).toEqual({
330
+ type: "direct",
331
+ apiKey: "test-openrouter-key",
332
+ });
333
+ expect(lastGenerateRequest?.model).toBe("black-forest-labs/flux");
334
+ });
335
+
336
+ test("OpenRouter qualifies a built-in alias and keeps OpenRouter ownership", async () => {
337
+ seedImageGenService({ provider: "openrouter" });
338
+
339
+ const result = await run({ prompt: "a robot", model: "openai" }, fakeContext);
340
+
341
+ expect(result.isError).toBe(false);
342
+ expect(lastGenerateProvider).toBe("openrouter");
343
+ expect(lastGenerateRequest?.model).toBe("openai/gpt-image-2");
344
+ });
345
+
346
+ test("OpenRouter returns a missing-key hint without falling back to Gemini", async () => {
347
+ seedImageGenService({ provider: "openrouter" });
348
+ mockOpenRouterKey = undefined;
349
+ mockGeminiKey = "gemini-key-should-not-be-used";
350
+
351
+ const result = await run({ prompt: "a cat" }, fakeContext);
352
+
353
+ expect(result.isError).toBe(true);
354
+ expect(result.content).toContain("OpenRouter API key");
355
+ expect(result.content).not.toContain("No Gemini API key");
356
+ expect(lastGenerateProvider).toBeNull();
357
+ });
358
+
311
359
  test("explicit model override routes to owning provider (gemini config → openai call)", async () => {
312
360
  // Config says the user's default provider is gemini, but the LLM
313
361
  // explicitly requests a gpt-* model. The tool must dispatch to OpenAI
@@ -0,0 +1,69 @@
1
+ import { beforeEach, describe, expect, test } from "bun:test";
2
+
3
+ import { actorAuthorProvenance } from "../daemon/message-provenance.js";
4
+ import type { TrustContext } from "../daemon/trust-context-types.js";
5
+ import {
6
+ addMessage,
7
+ createConversation,
8
+ getMessageById,
9
+ parseMessageMetadata,
10
+ provenanceFromTrustContext,
11
+ } from "../persistence/conversation-crud.js";
12
+ import { getDb } from "../persistence/db-connection.js";
13
+ import { initializeDb } from "../persistence/db-init.js";
14
+
15
+ await initializeDb();
16
+
17
+ const ALICE: TrustContext = {
18
+ sourceChannel: "slack",
19
+ trustClass: "trusted_contact",
20
+ requesterIdentifier: "@alice",
21
+ requesterContactId: "contact-alice",
22
+ };
23
+
24
+ async function persistWith(
25
+ role: "user" | "assistant",
26
+ metadata: Record<string, unknown>,
27
+ ): Promise<ReturnType<typeof parseMessageMetadata>> {
28
+ const conversation = createConversation("provenance-contact-id");
29
+ const row = await addMessage(conversation.id, role, "hello", { metadata });
30
+ const stored = getMessageById(row.id, conversation.id);
31
+ return parseMessageMetadata(stored?.metadata ?? null);
32
+ }
33
+
34
+ describe("message provenance contact id", () => {
35
+ beforeEach(() => {
36
+ const db = getDb();
37
+ db.run(`DELETE FROM messages`);
38
+ db.run(`DELETE FROM conversations`);
39
+ });
40
+
41
+ test("a person's own row names them as its author", async () => {
42
+ const metadata = await persistWith("user", {
43
+ ...provenanceFromTrustContext(ALICE),
44
+ ...actorAuthorProvenance(ALICE),
45
+ });
46
+
47
+ expect(metadata?.provenanceTrustClass).toBe("trusted_contact");
48
+ expect(metadata?.provenanceContactId).toBe("contact-alice");
49
+ });
50
+
51
+ test("a row written during their turn carries the turn's trust but no author", async () => {
52
+ // The assistant's replies, tool results, and notices are stamped with the
53
+ // turn's provenance alone.
54
+ const metadata = await persistWith(
55
+ "assistant",
56
+ provenanceFromTrustContext(ALICE),
57
+ );
58
+
59
+ expect(metadata?.provenanceTrustClass).toBe("trusted_contact");
60
+ expect(metadata?.provenanceContactId).toBeUndefined();
61
+ });
62
+
63
+ test("an actor with no resolved contact names no author", () => {
64
+ expect(
65
+ actorAuthorProvenance({ sourceChannel: "slack", trustClass: "unknown" }),
66
+ ).toEqual({});
67
+ expect(actorAuthorProvenance(undefined)).toEqual({});
68
+ });
69
+ });