@vellumai/assistant 0.12.3-staging.1 → 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 (560) hide show
  1. package/ARCHITECTURE.md +43 -29
  2. package/Dockerfile +63 -0
  3. package/docs/architecture/security.md +10 -5
  4. package/docs/architecture/turn-actor.md +2 -0
  5. package/docs/desktop-browser-cli.md +45 -4
  6. package/docs/live-voice-task-outcomes.md +100 -0
  7. package/docs/mode-sessions.md +183 -0
  8. package/docs/voice-input-diagnostics.md +58 -0
  9. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/package.json +2 -1
  10. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
  11. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/integration-categories.ts +36 -0
  12. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json +2 -1
  13. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
  14. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/integration-categories.ts +36 -0
  15. package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +20 -0
  16. package/node_modules/@vellumai/gateway-client/src/index.ts +4 -1
  17. package/node_modules/@vellumai/gateway-client/src/outbound-contract.ts +38 -7
  18. package/node_modules/@vellumai/gateway-client/src/trust-verdict-contract.ts +5 -6
  19. package/node_modules/@vellumai/service-contracts/package.json +2 -1
  20. package/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
  21. package/node_modules/@vellumai/service-contracts/src/integration-categories.ts +36 -0
  22. package/node_modules/@vellumai/slack-text/src/index.ts +4 -8
  23. package/openapi.yaml +665 -14
  24. package/package.json +1 -1
  25. package/scripts/smoke-desktop-browser-cli.ts +10 -26
  26. package/scripts/smoke-desktop-window-theme.ts +2 -3
  27. package/src/__tests__/agent-loop-output-hooks.test.ts +150 -1
  28. package/src/__tests__/agent-loop-provider-error-recording.test.ts +87 -19
  29. package/src/__tests__/annotate-activity-metadata.test.ts +3 -1
  30. package/src/__tests__/annotate-risk-options.test.ts +2 -0
  31. package/src/__tests__/answered-question-persistence.test.ts +2 -0
  32. package/src/__tests__/anthropic-gateway-shared.test.ts +22 -0
  33. package/src/__tests__/approval-message-composer.test.ts +1 -118
  34. package/src/__tests__/approval-routes-http.test.ts +3 -0
  35. package/src/__tests__/assistant-event-hub-connection-history.test.ts +111 -0
  36. package/src/__tests__/assistant-event-hub-targeted.test.ts +50 -0
  37. package/src/__tests__/assistant-event-targeting.test.ts +186 -0
  38. package/src/__tests__/assistant-stream-state.test.ts +29 -9
  39. package/src/__tests__/browser-fill-credential.test.ts +11 -22
  40. package/src/__tests__/btw-routes.test.ts +4 -1
  41. package/src/__tests__/build-persisted-content.test.ts +1 -1
  42. package/src/__tests__/call-conversation-messages.test.ts +5 -0
  43. package/src/__tests__/call-recovery.test.ts +7 -2
  44. package/src/__tests__/call-routes-http.test.ts +12 -0
  45. package/src/__tests__/call-store.test.ts +7 -2
  46. package/src/__tests__/channel-retry-sweep.test.ts +9 -0
  47. package/src/__tests__/chat-reveal-guard-priming.test.ts +2 -0
  48. package/src/__tests__/checker.test.ts +11 -3
  49. package/src/__tests__/client-os-metadata-persistence.test.ts +35 -1
  50. package/src/__tests__/computer-use-skill-manifest-regression.test.ts +0 -25
  51. package/src/__tests__/computer-use-tools.test.ts +1 -233
  52. package/src/__tests__/config-model-image-provider.test.ts +39 -0
  53. package/src/__tests__/config-schema.test.ts +16 -0
  54. package/src/__tests__/config-watcher.test.ts +24 -0
  55. package/src/__tests__/context-search-agent-runner.test.ts +260 -1
  56. package/src/__tests__/context-search-fanout.test.ts +1 -1
  57. package/src/__tests__/context-search-types.test.ts +7 -7
  58. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +8 -2
  59. package/src/__tests__/conversation-agent-loop-fatal-cleanup.test.ts +2 -0
  60. package/src/__tests__/conversation-agent-loop.test.ts +546 -16
  61. package/src/__tests__/conversation-app-control-lifecycle.test.ts +53 -1
  62. package/src/__tests__/conversation-evictor.test.ts +62 -37
  63. package/src/__tests__/conversation-history-boundary-repair-failure.test.ts +174 -0
  64. package/src/__tests__/conversation-loop-db-lock-resilience.test.ts +2 -0
  65. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +2 -0
  66. package/src/__tests__/conversation-retry-route.test.ts +22 -0
  67. package/src/__tests__/conversation-routes-disk-view.test.ts +2 -0
  68. package/src/__tests__/conversation-routes-enabled-plugins.test.ts +2 -0
  69. package/src/__tests__/conversation-routes-guardian-reply.test.ts +10 -0
  70. package/src/__tests__/conversation-routes-hidden-queue.test.ts +2 -0
  71. package/src/__tests__/conversation-routes-slash-commands.test.ts +2 -0
  72. package/src/__tests__/conversation-runtime-assembly.test.ts +29 -0
  73. package/src/__tests__/conversation-surface-snapshots.test.ts +543 -0
  74. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +151 -0
  75. package/src/__tests__/conversation-surfaces-point-at-budget.test.ts +63 -1
  76. package/src/__tests__/conversation-tool-setup-attribution.test.ts +23 -0
  77. package/src/__tests__/conversation-undo.test.ts +61 -0
  78. package/src/__tests__/credential-execution-client.test.ts +6 -6
  79. package/src/__tests__/credential-prompt-route.test.ts +35 -3
  80. package/src/__tests__/cu-unified-flow.test.ts +82 -1
  81. package/src/__tests__/db-mode-sessions-migration.test.ts +175 -0
  82. package/src/__tests__/deterministic-verification-control-plane.test.ts +2 -0
  83. package/src/__tests__/dm-backfill.test.ts +91 -1
  84. package/src/__tests__/document-create-route.test.ts +88 -0
  85. package/src/__tests__/document-sync-tags.test.ts +27 -0
  86. package/src/__tests__/emit-signal-routing-intent.test.ts +142 -2
  87. package/src/__tests__/events-tail-route.test.ts +25 -0
  88. package/src/__tests__/evict-conversations-for-reload.test.ts +57 -8
  89. package/src/__tests__/guardian-action-sweep.test.ts +16 -0
  90. package/src/__tests__/guardian-dispatch.test.ts +13 -0
  91. package/src/__tests__/headless-browser-interactions.test.ts +64 -20
  92. package/src/__tests__/heartbeat-service.test.ts +2 -59
  93. package/src/__tests__/helpers/mock-conversation.ts +21 -0
  94. package/src/__tests__/helpers/native-web-search-harness.ts +2 -0
  95. package/src/__tests__/helpers/verification-sessions-ipc-sim.ts +5 -0
  96. package/src/__tests__/host-cu-proxy.test.ts +46 -2
  97. package/src/__tests__/http-user-message-parity.test.ts +2 -0
  98. package/src/__tests__/image-credentials.test.ts +49 -0
  99. package/src/__tests__/image-service-dispatcher.test.ts +45 -0
  100. package/src/__tests__/inference-profile-session-handler.test.ts +1 -1
  101. package/src/__tests__/interrupt-on-send.test.ts +57 -43
  102. package/src/__tests__/interrupt-turn-note.test.ts +5 -6
  103. package/src/__tests__/llm-catalog-parity.test.ts +2 -2
  104. package/src/__tests__/media-generate-image.test.ts +49 -1
  105. package/src/__tests__/message-provenance-contact-id.test.ts +69 -0
  106. package/src/__tests__/migration-export-to-gcs.test.ts +142 -0
  107. package/src/__tests__/mode-session-history.test.ts +245 -0
  108. package/src/__tests__/mode-session-recovery.test.ts +222 -0
  109. package/src/__tests__/mode-session-wire.test.ts +141 -0
  110. package/src/__tests__/notification-guardian-path.test.ts +7 -0
  111. package/src/__tests__/notification-telegram-adapter.test.ts +2 -2
  112. package/src/__tests__/notification-vellum-adapter.test.ts +34 -0
  113. package/src/__tests__/oauth-provider-serializer.test.ts +12 -0
  114. package/src/__tests__/oauth-providers-routes.test.ts +1 -0
  115. package/src/__tests__/outbound-slack-persistence.test.ts +2 -0
  116. package/src/__tests__/persistence-secret-redaction.test.ts +2 -0
  117. package/src/__tests__/plugin-api-provider.test.ts +5 -0
  118. package/src/__tests__/plugin-api-resolve-credential.test.ts +97 -0
  119. package/src/__tests__/plugin-import-boundary-guard.test.ts +9 -0
  120. package/src/__tests__/process-message-display-content.test.ts +56 -0
  121. package/src/__tests__/reaction-persistence.test.ts +5 -0
  122. package/src/__tests__/request-secret-standalone-channel-gate.test.ts +25 -9
  123. package/src/__tests__/runtime-events-sse-reconnect.test.ts +1 -1
  124. package/src/__tests__/scripted-turn-metadata-persistence.test.ts +73 -0
  125. package/src/__tests__/secret-ingress-http.test.ts +2 -0
  126. package/src/__tests__/secure-keys.test.ts +43 -1
  127. package/src/__tests__/send-endpoint-busy.test.ts +163 -1
  128. package/src/__tests__/server-history-render.test.ts +91 -1
  129. package/src/__tests__/settings-routes.test.ts +50 -1
  130. package/src/__tests__/skill-script-runner-sandbox.test.ts +32 -0
  131. package/src/__tests__/subagent-fork-notifications.test.ts +1 -2
  132. package/src/__tests__/subagent-manager-notify.test.ts +1 -2
  133. package/src/__tests__/subagent-notify-parent.test.ts +146 -39
  134. package/src/__tests__/subagent-notify-stale-rebuild.test.ts +2 -4
  135. package/src/__tests__/surface-completion-in-flight-snapshot.test.ts +2 -0
  136. package/src/__tests__/thread-backfill.test.ts +53 -0
  137. package/src/__tests__/tool-preview-lifecycle.test.ts +2 -0
  138. package/src/__tests__/tool-result-metadata-plumbing.test.ts +3 -1
  139. package/src/__tests__/tool-start-timestamp.test.ts +2 -0
  140. package/src/__tests__/twilio-routes.test.ts +1 -0
  141. package/src/__tests__/web-search-backend-failure.test.ts +1 -1
  142. package/src/activation/progress-store.test.ts +12 -7
  143. package/src/agent/loop.ts +99 -41
  144. package/src/api/events/assistant-turn-start.ts +3 -0
  145. package/src/api/events/generation-handoff.ts +2 -0
  146. package/src/api/events/message-complete.ts +2 -0
  147. package/src/api/events/notification-conversation-created.ts +14 -16
  148. package/src/api/events/notification-intent.ts +4 -2
  149. package/src/api/events/tool-result.ts +85 -0
  150. package/src/api/events/tool-use-start.ts +3 -0
  151. package/src/api/events/user-message-echo.ts +3 -0
  152. package/src/api/index.ts +31 -6
  153. package/src/api/mode-session.test.ts +131 -0
  154. package/src/api/mode-session.ts +148 -0
  155. package/src/api/remember-tool.ts +15 -0
  156. package/src/api/responses/conversation-message.ts +6 -0
  157. package/src/approvals/AGENTS.md +4 -2
  158. package/src/browser/__tests__/operations.test.ts +29 -0
  159. package/src/browser/operation-meta.ts +2 -1
  160. package/src/browser/operations.ts +29 -0
  161. package/src/browser/virtual-desktop-target.ts +2 -8
  162. package/src/bundler/bundle-signer.ts +1 -3
  163. package/src/calls/__tests__/barge-in-guard.test.ts +3 -0
  164. package/src/calls/__tests__/call-controller.test.ts +94 -0
  165. package/src/calls/__tests__/call-funnel-telemetry.test.ts +204 -0
  166. package/src/calls/__tests__/voice-control-protocol.test.ts +15 -0
  167. package/src/calls/__tests__/voice-escalation-judge.test.ts +209 -0
  168. package/src/calls/__tests__/voice-leg-coordinator.test.ts +168 -0
  169. package/src/{live-voice/__tests__/live-voice-metrics.test.ts → calls/__tests__/voice-metrics.test.ts} +32 -38
  170. package/src/calls/__tests__/voice-session-bridge.test.ts +576 -122
  171. package/src/calls/barge-in-guard.ts +5 -0
  172. package/src/calls/call-controller.ts +196 -1
  173. package/src/calls/call-domain.ts +4 -0
  174. package/src/calls/call-store.ts +87 -2
  175. package/src/calls/guardian-action-sweep.ts +22 -8
  176. package/src/calls/media-stream-audio-transcode.ts +1 -3
  177. package/src/calls/typesafe-noul.ts +156 -0
  178. package/src/calls/voice-control-protocol.ts +6 -0
  179. package/src/calls/voice-escalation-judge.ts +145 -0
  180. package/src/calls/voice-escalation-target.ts +10 -0
  181. package/src/calls/voice-leg-coordinator.ts +121 -3
  182. package/src/{live-voice/live-voice-metrics.ts → calls/voice-metrics.ts} +94 -87
  183. package/src/calls/voice-session-bridge.ts +256 -112
  184. package/src/calls/voice-triage-escalate.ts +7 -0
  185. package/src/cli/__tests__/roadmap-cli.test.ts +4 -41
  186. package/src/cli/commands/__tests__/notifications.test.ts +35 -10
  187. package/src/cli/commands/browser.help.ts +4 -5
  188. package/src/cli/commands/browser.ts +0 -1
  189. package/src/cli/commands/clients.help.ts +46 -0
  190. package/src/cli/commands/clients.ts +116 -0
  191. package/src/cli/commands/credentials.help.ts +6 -1
  192. package/src/cli/commands/notifications.help.ts +16 -2
  193. package/src/cli/commands/notifications.ts +30 -8
  194. package/src/cli/commands/roadmap.help.ts +18 -21
  195. package/src/cli/commands/roadmap.ts +4 -21
  196. package/src/cli/lib/__tests__/list-installed-plugins.test.ts +5 -2
  197. package/src/cli/lib/bundled-marketplace.json +84 -34
  198. package/src/cli/lib/plugin-marketplace.ts +8 -0
  199. package/src/cli/lib/publish-plugin.ts +1 -2
  200. package/src/config/__tests__/default-provider.test.ts +3 -1
  201. package/src/config/__tests__/memory-retrospective-schema.test.ts +14 -1
  202. package/src/config/bundled-skills/computer-use/SKILL.md +1 -1
  203. package/src/config/bundled-skills/computer-use/TOOLS.json +89 -1
  204. package/src/config/bundled-skills/image-studio/SKILL.md +17 -2
  205. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +7 -11
  206. package/src/config/bundled-skills/sequences/TOOLS.json +1 -5
  207. package/src/config/call-site-defaults.ts +9 -0
  208. package/src/config/feature-flag-registry.json +16 -0
  209. package/src/config/profile-text-generation.test.ts +2 -2
  210. package/src/config/schemas/__tests__/tools.test.ts +31 -0
  211. package/src/config/schemas/call-site-catalog.ts +14 -0
  212. package/src/config/schemas/channels.ts +0 -18
  213. package/src/config/schemas/llm.ts +3 -1
  214. package/src/config/schemas/mcp.ts +2 -1
  215. package/src/config/schemas/memory-retrospective.ts +11 -2
  216. package/src/config/schemas/services.ts +15 -6
  217. package/src/config/schemas/tools.ts +10 -0
  218. package/src/config/session-groups-gate.ts +21 -0
  219. package/src/config/skills.ts +2 -6
  220. package/src/conversations/__tests__/message-consolidation.test.ts +149 -0
  221. package/src/conversations/message-consolidation.ts +102 -5
  222. package/src/credential-execution/ces-connect.test.ts +69 -0
  223. package/src/credential-execution/ces-connect.ts +115 -0
  224. package/src/credential-execution/ces-runtime.ts +55 -93
  225. package/src/credential-execution/executable-discovery.ts +1 -11
  226. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +10 -1
  227. package/src/daemon/__tests__/inference-profile-notification.test.ts +2 -0
  228. package/src/daemon/browser-mode-session.test.ts +402 -0
  229. package/src/daemon/browser-mode-session.ts +186 -0
  230. package/src/daemon/computer-use-mode-session.test.ts +210 -0
  231. package/src/daemon/computer-use-mode-session.ts +105 -0
  232. package/src/daemon/config-watcher.ts +10 -0
  233. package/src/daemon/conversation-agent-loop-handlers.ts +58 -10
  234. package/src/daemon/conversation-agent-loop.ts +219 -25
  235. package/src/daemon/conversation-evictor.ts +5 -24
  236. package/src/daemon/conversation-history.ts +10 -0
  237. package/src/daemon/conversation-interrupt-repair.ts +4 -43
  238. package/src/daemon/conversation-interrupt.ts +7 -11
  239. package/src/daemon/conversation-messaging.ts +58 -5
  240. package/src/daemon/conversation-mode-session.test.ts +1667 -0
  241. package/src/daemon/conversation-mode-session.ts +1008 -0
  242. package/src/daemon/conversation-process.ts +5 -0
  243. package/src/daemon/conversation-runtime-assembly.ts +10 -20
  244. package/src/daemon/conversation-skill-tools.ts +1 -0
  245. package/src/daemon/conversation-store.ts +4 -4
  246. package/src/daemon/conversation-surface-snapshots.ts +135 -0
  247. package/src/daemon/conversation-surfaces.ts +113 -5
  248. package/src/daemon/conversation-tool-setup.ts +8 -3
  249. package/src/daemon/conversation.ts +143 -41
  250. package/src/daemon/handlers/config-model.ts +7 -5
  251. package/src/daemon/handlers/shared.ts +55 -16
  252. package/src/daemon/host-cu-proxy.ts +51 -22
  253. package/src/daemon/lifecycle.ts +30 -13
  254. package/src/daemon/message-protocol.ts +0 -1
  255. package/src/daemon/message-provenance.ts +28 -4
  256. package/src/daemon/mode-session-startup-recovery.ts +23 -0
  257. package/src/daemon/mode-session-tracking.ts +54 -0
  258. package/src/daemon/process-message.ts +13 -4
  259. package/src/daemon/virtual-desktop-context.ts +21 -0
  260. package/src/desktop/__tests__/fake-desktop.ts +1 -0
  261. package/src/desktop/desktop-accessibility-script.ts +88 -0
  262. package/src/desktop/desktop-accessibility.test.ts +160 -0
  263. package/src/desktop/desktop-accessibility.ts +169 -0
  264. package/src/desktop/desktop-automation-lease.test.ts +76 -68
  265. package/src/desktop/desktop-automation-lease.ts +41 -50
  266. package/src/desktop/desktop-browser-endpoint.ts +4 -0
  267. package/src/desktop/desktop-computer-use-routing.test.ts +330 -0
  268. package/src/desktop/desktop-computer-use.test.ts +449 -0
  269. package/src/desktop/desktop-computer-use.ts +502 -0
  270. package/src/desktop/desktop-dependencies.ts +47 -293
  271. package/src/desktop/desktop-display.ts +2 -2
  272. package/src/desktop/desktop-panel-config.test.ts +13 -3
  273. package/src/desktop/desktop-panel-config.ts +109 -15
  274. package/src/desktop/desktop-session-manager.test.ts +69 -21
  275. package/src/desktop/desktop-session-manager.ts +76 -22
  276. package/src/desktop/desktop-stream-bridge.test.ts +11 -122
  277. package/src/desktop/desktop-stream-bridge.ts +6 -8
  278. package/src/desktop/desktop-window-theme.ts +5 -25
  279. package/src/desktop/virtual-desktop-feature.ts +52 -2
  280. package/src/desktop/virtual-desktop-platform.test.ts +5 -9
  281. package/src/documents/document-store.ts +23 -0
  282. package/src/heartbeat/__tests__/heartbeat-service.test.ts +22 -26
  283. package/src/heartbeat/heartbeat-service.ts +5 -33
  284. package/src/ipc/__tests__/browser-ipc.test.ts +215 -6
  285. package/src/ipc/assistant-server.ts +2 -0
  286. package/src/ipc/routes/__tests__/channel-reply-ipc-routes.test.ts +111 -0
  287. package/src/ipc/routes/channel-reply-ipc-routes.ts +73 -0
  288. package/src/live-voice/__tests__/continuation-judge.test.ts +151 -0
  289. package/src/live-voice/__tests__/live-activity-reporter.test.ts +154 -15
  290. package/src/live-voice/__tests__/live-voice-flux-turn-end.test.ts +743 -31
  291. package/src/live-voice/__tests__/live-voice-look-follow-up.test.ts +191 -10
  292. package/src/live-voice/__tests__/live-voice-session-groups-gate.test.ts +87 -0
  293. package/src/live-voice/__tests__/live-voice-session-manager.test.ts +5 -1
  294. package/src/live-voice/__tests__/live-voice-sight-frame-echo.test.ts +41 -0
  295. package/src/live-voice/__tests__/live-voice-sight-frame.test.ts +1017 -9
  296. package/src/live-voice/__tests__/live-voice-triage-escalate.test.ts +224 -1
  297. package/src/live-voice/__tests__/live-voice-tts-session.test.ts +101 -0
  298. package/src/live-voice/__tests__/live-voice-tts.test.ts +44 -0
  299. package/src/live-voice/__tests__/live-voice-vad.test.ts +939 -75
  300. package/src/live-voice/__tests__/microphone-idle-gate.test.ts +87 -0
  301. package/src/live-voice/__tests__/session-controls.test.ts +23 -0
  302. package/src/live-voice/__tests__/task-outcomes.test.ts +117 -0
  303. package/src/live-voice/__tests__/voice-input-diagnostics.test.ts +117 -0
  304. package/src/live-voice/camera-mode-session.test.ts +265 -0
  305. package/src/live-voice/camera-mode-session.ts +277 -0
  306. package/src/live-voice/continuation-judge.ts +123 -0
  307. package/src/live-voice/live-activity-reporter.ts +115 -6
  308. package/src/live-voice/live-voice-manager.ts +36 -1
  309. package/src/live-voice/live-voice-photo.ts +88 -7
  310. package/src/live-voice/live-voice-session-manager.ts +15 -0
  311. package/src/live-voice/live-voice-session.ts +1443 -747
  312. package/src/live-voice/live-voice-tts.ts +22 -4
  313. package/src/live-voice/microphone-idle-gate.ts +56 -0
  314. package/src/live-voice/protocol.ts +171 -4
  315. package/src/live-voice/session-controls.ts +11 -3
  316. package/src/live-voice/task-outcomes.ts +131 -0
  317. package/src/live-voice/voice-input-diagnostics.ts +117 -0
  318. package/src/mcp/__tests__/manager-tool-caps.test.ts +63 -18
  319. package/src/mcp/__tests__/tool-caps.test.ts +28 -1
  320. package/src/mcp/manager.ts +3 -0
  321. package/src/mcp/tool-caps.ts +12 -4
  322. package/src/media/__tests__/image-models.test.ts +58 -0
  323. package/src/media/image-credentials.ts +5 -0
  324. package/src/media/image-models.ts +52 -0
  325. package/src/media/image-service.ts +8 -0
  326. package/src/media/openrouter-image-service.test.ts +133 -0
  327. package/src/media/openrouter-image-service.ts +153 -0
  328. package/src/media/types.ts +1 -1
  329. package/src/messaging/providers/__tests__/transport-dispatch.test.ts +65 -11
  330. package/src/messaging/providers/discord/send.ts +4 -17
  331. package/src/messaging/providers/discord/transport.ts +2 -2
  332. package/src/messaging/providers/outlook/client.ts +16 -22
  333. package/src/messaging/providers/telegram-bot/send.test.ts +236 -1
  334. package/src/messaging/providers/telegram-bot/send.ts +10 -7
  335. package/src/messaging/providers/telegram-bot/transport.ts +3 -10
  336. package/src/messaging/providers/whatsapp/send.test.ts +169 -4
  337. package/src/monitoring/__tests__/control.test.ts +17 -5
  338. package/src/monitoring/control.ts +15 -5
  339. package/src/notifications/AGENTS.md +1 -1
  340. package/src/notifications/README.md +83 -99
  341. package/src/notifications/__tests__/broadcaster.test.ts +1 -1
  342. package/src/notifications/__tests__/channel-allowlist.test.ts +39 -0
  343. package/src/notifications/__tests__/copy-composer.test.ts +1 -1
  344. package/src/notifications/__tests__/decision-engine.test.ts +79 -2
  345. package/src/notifications/__tests__/deterministic-checks.test.ts +10 -10
  346. package/src/notifications/__tests__/home-feed-side-effect.test.ts +93 -4
  347. package/src/notifications/__tests__/notification-utils.test.ts +18 -0
  348. package/src/notifications/adapters/macos.ts +37 -46
  349. package/src/notifications/broadcaster.ts +5 -6
  350. package/src/notifications/channel-allowlist.ts +44 -0
  351. package/src/notifications/copy-composer.ts +0 -23
  352. package/src/notifications/decision-engine.ts +24 -14
  353. package/src/notifications/deterministic-checks.ts +1 -1
  354. package/src/notifications/emit-signal.ts +123 -37
  355. package/src/notifications/home-feed-side-effect.ts +9 -7
  356. package/src/notifications/notification-utils.ts +19 -0
  357. package/src/notifications/signal.ts +4 -19
  358. package/src/notifications/types.ts +15 -0
  359. package/src/oauth/connection-resolver.ts +1 -2
  360. package/src/oauth/oauth-store.ts +1 -3
  361. package/src/oauth/provider-serializer.ts +7 -0
  362. package/src/oauth/seed-providers.ts +30 -0
  363. package/src/onboarding/onboarding-events-store.ts +99 -12
  364. package/src/persistence/__tests__/client-connection-events-store.test.ts +322 -0
  365. package/src/persistence/client-connection-events-store.ts +377 -0
  366. package/src/persistence/conversation-crud.ts +55 -6
  367. package/src/persistence/conversation-mode-sessions.test.ts +424 -0
  368. package/src/persistence/conversation-mode-sessions.ts +460 -0
  369. package/src/persistence/conversation-queries.ts +6 -19
  370. package/src/persistence/llm-usage-store.ts +3 -9
  371. package/src/persistence/message-metadata.ts +25 -0
  372. package/src/persistence/migrations/381-create-conversation-mode-sessions.ts +41 -0
  373. package/src/persistence/migrations/382-create-client-connection-events.ts +40 -0
  374. package/src/persistence/migrations/__tests__/382-create-client-connection-events.test.ts +56 -0
  375. package/src/persistence/schema/conversations.ts +67 -0
  376. package/src/persistence/schema/infrastructure.ts +29 -0
  377. package/src/persistence/steps.ts +17 -0
  378. package/src/plugin-api/__tests__/plugin-name-env.test.ts +249 -0
  379. package/src/plugin-api/effective-context-window.ts +33 -0
  380. package/src/plugin-api/index.ts +24 -5
  381. package/src/plugin-api/plugin-name-env.ts +125 -0
  382. package/src/plugin-api/resolve-credential.ts +79 -20
  383. package/src/plugin-api/vision-support.test.ts +22 -0
  384. package/src/plugin-api/vision-support.ts +17 -5
  385. package/src/plugins/__tests__/active-plugin-env.test.ts +136 -0
  386. package/src/plugins/active-plugin-env.ts +51 -0
  387. package/src/plugins/defaults/index.ts +24 -0
  388. package/src/plugins/defaults/memory/__tests__/archive-removal-framing.test.ts +64 -0
  389. package/src/plugins/defaults/memory/__tests__/worker-control.test.ts +19 -6
  390. package/src/plugins/defaults/memory/context-search/agent-protocol.ts +9 -12
  391. package/src/plugins/defaults/memory/context-search/agent-runner.ts +78 -22
  392. package/src/plugins/defaults/memory/context-search/format.ts +51 -7
  393. package/src/plugins/defaults/memory/context-search/limits.ts +10 -12
  394. package/src/plugins/defaults/memory/context-search/recall-input.ts +38 -0
  395. package/src/plugins/defaults/memory/context-search/search.ts +8 -14
  396. package/src/plugins/defaults/memory/context-search/sources/conversations.ts +1 -0
  397. package/src/plugins/defaults/memory/context-search/types.ts +1 -11
  398. package/src/plugins/defaults/memory/graph/__tests__/handle-remember-v2.test.ts +11 -4
  399. package/src/plugins/defaults/memory/graph/tool-handlers.ts +12 -21
  400. package/src/plugins/defaults/memory/graph/tools.ts +35 -64
  401. package/src/plugins/defaults/memory/src/memory-eval-routes.ts +2 -3
  402. package/src/plugins/defaults/memory/src/memory-item-routes.test.ts +5 -0
  403. package/src/plugins/defaults/memory/src/memory-item-routes.ts +2 -1
  404. package/src/plugins/defaults/memory/substrate/prompts/consolidation.ts +2 -2
  405. package/src/plugins/defaults/memory/tools.test.ts +124 -9
  406. package/src/plugins/defaults/memory/tools.ts +38 -11
  407. package/src/plugins/defaults/memory/v3/__tests__/pool-log-store.test.ts +165 -27
  408. package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +626 -27
  409. package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +46 -27
  410. package/src/plugins/defaults/memory/v3/orchestrate.ts +78 -38
  411. package/src/plugins/defaults/memory/v3/pool-log-store.ts +65 -79
  412. package/src/plugins/defaults/memory/v3/pool-select.test.ts +14 -5
  413. package/src/plugins/defaults/memory/v3/pool-select.ts +683 -44
  414. package/src/plugins/defaults/memory/worker-control.ts +15 -5
  415. package/src/plugins/defaults/task-progress/__tests__/registration.test.ts +70 -0
  416. package/src/plugins/defaults/task-progress/__tests__/task-progress-context.test.ts +380 -0
  417. package/src/plugins/defaults/task-progress/hooks/post-compact.ts +19 -0
  418. package/src/plugins/defaults/task-progress/hooks/user-prompt-submit.ts +24 -0
  419. package/src/plugins/defaults/task-progress/package.json +14 -0
  420. package/src/plugins/defaults/task-progress/src/apply-task-progress-context.ts +93 -0
  421. package/src/plugins/defaults/task-progress/src/format-task-progress-context.ts +67 -0
  422. package/src/providers/__tests__/context-overflow-error.test.ts +10 -0
  423. package/src/providers/__tests__/search-provider-catalog.test.ts +18 -0
  424. package/src/providers/anthropic/client.ts +10 -1
  425. package/src/providers/anthropic-gateway-shared.ts +6 -0
  426. package/src/providers/fetch-provider-catalog.ts +11 -0
  427. package/src/providers/gemini/client.ts +25 -8
  428. package/src/providers/inference/adapter-factory.ts +1 -1
  429. package/src/providers/jev/client.test.ts +10 -0
  430. package/src/providers/jev/client.ts +20 -4
  431. package/src/providers/model-catalog.ts +4 -3
  432. package/src/providers/openai/__tests__/chat-completions-extra-body.test.ts +94 -1
  433. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +106 -1
  434. package/src/providers/openai/__tests__/chat-template-flatten.test.ts +24 -0
  435. package/src/providers/openai/__tests__/google-thought-signature.test.ts +1 -2
  436. package/src/providers/openai/chat-completions-provider.ts +100 -29
  437. package/src/providers/openai/google-thought-signature.ts +0 -6
  438. package/src/providers/openai/responses-provider.ts +23 -1
  439. package/src/providers/ratelimit.ts +1 -3
  440. package/src/providers/search-provider-catalog.ts +24 -0
  441. package/src/providers/speech-to-text/__tests__/deepgram-flux-frames.test.ts +24 -0
  442. package/src/providers/speech-to-text/__tests__/deepgram-flux-realtime.test.ts +7 -1
  443. package/src/providers/speech-to-text/__tests__/vellum-managed-realtime.test.ts +50 -1
  444. package/src/providers/speech-to-text/deepgram-flux-frames.ts +11 -0
  445. package/src/providers/speech-to-text/vellum-managed-flux-realtime.ts +13 -0
  446. package/src/providers/types.ts +5 -0
  447. package/src/routes/control.ts +7 -5
  448. package/src/runtime/AGENTS.md +4 -2
  449. package/src/runtime/actor-trust-resolver.ts +3 -3
  450. package/src/runtime/agent-wake.ts +1 -1
  451. package/src/runtime/approval-message-composer.ts +3 -69
  452. package/src/runtime/assistant-event-hub.ts +73 -75
  453. package/src/runtime/assistant-event-publish-options.ts +7 -0
  454. package/src/runtime/assistant-event-targeting.ts +79 -0
  455. package/src/runtime/assistant-stream-state.ts +13 -100
  456. package/src/runtime/background-job-runner.ts +5 -5
  457. package/src/runtime/channel-retry-sweep.ts +1 -0
  458. package/src/runtime/guardian-action-message-composer.ts +8 -161
  459. package/src/runtime/guardian-reply-router.ts +28 -32
  460. package/src/runtime/http-types.ts +6 -0
  461. package/src/runtime/message-composer-types.ts +3 -61
  462. package/src/runtime/migrations/__tests__/vbundle-import-parity.test.ts +67 -1
  463. package/src/runtime/migrations/vbundle-builder.ts +24 -1
  464. package/src/runtime/migrations/vbundle-import-analyzer.ts +13 -4
  465. package/src/runtime/migrations/vbundle-import-policy.ts +10 -0
  466. package/src/runtime/migrations/vbundle-importer.ts +4 -1
  467. package/src/runtime/migrations/vbundle-streaming-importer.ts +4 -1
  468. package/src/runtime/migrations/vbundle-validator.ts +1 -0
  469. package/src/runtime/pending-interactions-mode-session.test.ts +62 -0
  470. package/src/runtime/pending-interactions.ts +29 -0
  471. package/src/runtime/routes/__tests__/client-history-routes.test.ts +104 -0
  472. package/src/runtime/routes/__tests__/image-generation-routes.test.ts +97 -0
  473. package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +1 -1
  474. package/src/runtime/routes/__tests__/roadmap-routes.test.ts +29 -6
  475. package/src/runtime/routes/browser-context.ts +31 -11
  476. package/src/runtime/routes/browser-routes.ts +43 -4
  477. package/src/runtime/routes/btw-routes.ts +9 -4
  478. package/src/runtime/routes/client-routes.ts +108 -0
  479. package/src/runtime/routes/conversation-management-routes.ts +4 -1
  480. package/src/runtime/routes/conversation-routes.ts +113 -27
  481. package/src/runtime/routes/credential-prompt-routes.ts +4 -3
  482. package/src/runtime/routes/credential-routes.ts +1 -2
  483. package/src/runtime/routes/desktop-setup-routes.test.ts +9 -18
  484. package/src/runtime/routes/desktop-setup-routes.ts +4 -6
  485. package/src/runtime/routes/documents-routes.ts +51 -0
  486. package/src/runtime/routes/email-routes.ts +3 -7
  487. package/src/runtime/routes/events-routes.ts +75 -54
  488. package/src/runtime/routes/identity-routes.ts +1 -5
  489. package/src/runtime/routes/image-generation-routes.ts +8 -14
  490. package/src/runtime/routes/inbound-message-handler.ts +35 -38
  491. package/src/runtime/routes/inbound-stages/backfill-sender-provenance.test.ts +168 -0
  492. package/src/runtime/routes/inbound-stages/backfill-sender-provenance.ts +96 -0
  493. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +38 -9
  494. package/src/runtime/routes/inbound-stages/background-dispatch.ts +2 -5
  495. package/src/runtime/routes/inbound-stages/reaction-intercept.ts +2 -0
  496. package/src/runtime/routes/llm-context-normalization.ts +1 -7
  497. package/src/runtime/routes/migration-routes.ts +73 -2
  498. package/src/runtime/routes/notification-routes.ts +10 -0
  499. package/src/runtime/routes/oauth-providers.ts +7 -0
  500. package/src/runtime/routes/plugins-routes.ts +6 -0
  501. package/src/runtime/routes/roadmap-routes.ts +47 -8
  502. package/src/runtime/routes/secret-routes.ts +1 -1
  503. package/src/runtime/routes/settings-routes.ts +10 -4
  504. package/src/runtime/routes/watcher-routes.ts +1 -3
  505. package/src/runtime/trust-class.test.ts +7 -6
  506. package/src/runtime/trust-class.ts +9 -17
  507. package/src/schedule/__tests__/worker-control.test.ts +36 -5
  508. package/src/schedule/worker-control.ts +15 -5
  509. package/src/security/secure-keys.ts +53 -103
  510. package/src/skills/clawhub.ts +3 -8
  511. package/src/skills/managed-store.ts +1 -5
  512. package/src/stt/types.ts +4 -0
  513. package/src/subagent/notify.ts +54 -34
  514. package/src/subagent/parent-notification.ts +6 -0
  515. package/src/telemetry/__tests__/phone-call-funnel.test.ts +72 -0
  516. package/src/telemetry/phone-call-funnel.ts +144 -0
  517. package/src/tools/__tests__/tool-schema-root-combinator-guard.test.ts +81 -0
  518. package/src/tools/ask-question/ask-question-tool.ts +6 -4
  519. package/src/tools/browser/browser-execution.ts +22 -59
  520. package/src/tools/browser/cdp-client/__tests__/cdp-dom-helpers.test.ts +561 -39
  521. package/src/tools/browser/cdp-client/cdp-dom-helpers.ts +435 -39
  522. package/src/tools/browser/cdp-client/cdp-inspect-client.ts +1 -8
  523. package/src/tools/client-os.ts +15 -0
  524. package/src/tools/computer-use/skill-proxy-bridge.ts +1 -1
  525. package/src/tools/computer-use/target.ts +42 -0
  526. package/src/tools/document/document-tool.ts +13 -18
  527. package/src/tools/execution-target.ts +14 -16
  528. package/src/tools/host-terminal/host-shell.ts +4 -1
  529. package/src/tools/network/__tests__/web-fetch-tinyfish.test.ts +250 -0
  530. package/src/tools/network/__tests__/web-search.test.ts +203 -0
  531. package/src/tools/network/web-fetch.ts +274 -7
  532. package/src/tools/network/web-search.ts +468 -6
  533. package/src/tools/permission-checker.ts +3 -3
  534. package/src/tools/policy-context.ts +3 -1
  535. package/src/tools/skills/load.ts +2 -1
  536. package/src/tools/skills/sandbox-runner.ts +11 -2
  537. package/src/tools/skills/skill-script-runner.ts +7 -0
  538. package/src/tools/skills/skill-tool-factory.ts +15 -1
  539. package/src/tools/terminal/__tests__/safe-env.test.ts +11 -0
  540. package/src/tools/terminal/safe-env.ts +4 -0
  541. package/src/tools/terminal/shell.ts +2 -0
  542. package/src/tools/tool-approval-handler.ts +5 -1
  543. package/src/tools/tool-types.ts +3 -3
  544. package/src/tools/types.ts +8 -2
  545. package/src/util/__tests__/worker-process-command.test.ts +5 -4
  546. package/src/util/abort-reasons.ts +18 -26
  547. package/src/util/canonicalize-identity.test.ts +21 -0
  548. package/src/util/canonicalize-identity.ts +16 -0
  549. package/src/util/errors.ts +13 -3
  550. package/src/util/provider-error-patterns.ts +3 -0
  551. package/src/util/worker-process.ts +77 -35
  552. package/src/workspace/migrations/157-add-desktop-files-launcher.ts +94 -0
  553. package/src/workspace/migrations/__tests__/156-extract-workspace-mcp-json.test.ts +4 -10
  554. package/src/workspace/migrations/__tests__/157-add-desktop-files-launcher.test.ts +113 -0
  555. package/src/workspace/migrations/registry.ts +2 -0
  556. package/src/__tests__/computer-use-window-schema.test.ts +0 -24
  557. package/src/api/events/heartbeat-alert.ts +0 -20
  558. package/src/daemon/message-types/web-activity.ts +0 -65
  559. package/src/desktop/desktop-dependencies.test.ts +0 -131
  560. package/src/tools/computer-use/definitions.ts +0 -607
@@ -1,28 +1,42 @@
1
1
  /**
2
2
  * Plugin-facing credential resolution.
3
3
  *
4
- * {@link resolveCredential} returns a stored credential's plaintext value — the
5
- * same value `assistant credentials reveal` prints — resolving a reference that
6
- * is either a credential UUID or a `"service/field"` string, exactly as the
7
- * reveal path does (see {@link ../tools/credentials/resolve.resolveCredentialRef}
8
- * and the `credentials/reveal` route).
4
+ * {@link resolveCredential} returns a stored credential's plaintext value
5
+ * (the same value `assistant credentials reveal` prints), resolving a
6
+ * reference that is either a credential UUID or a `"service/field"` string,
7
+ * exactly as the reveal path does (see
8
+ * {@link ../tools/credentials/resolve.resolveCredentialRef} and the
9
+ * `credentials/reveal` route).
9
10
  *
10
11
  * ## Plugin scoping
11
12
  *
12
- * When a plugin is in context (its hook, tool, or one of its own
13
- * `/x/plugins/<name>/` routes is executing, tracked by
14
- * {@link ../plugins/plugin-execution-context.getCurrentPluginName}), resolution
15
- * is restricted: the plugin may only resolve credentials whose `service`
16
- * equals its manifest name (`imessage/api_key` for plugin `imessage`). It
17
- * cannot read another service (`openai/api_key`, `openai/imessage`). Outside
18
- * any plugin context (host-internal callers, CLI, tests) the resolver is
19
- * unscoped and behaves like a direct reveal.
13
+ * When a plugin is in context, resolution is restricted to credentials
14
+ * whose `service` equals the plugin's runtime install-directory name
15
+ * (`sms/account_sid` for plugin `sms`). Context is:
16
+ *
17
+ * 1. In-process AsyncLocalStorage from `runInPluginContext` (hooks, plugin
18
+ * tools, plugin routes).
19
+ * 2. `VELLUM_PLUGIN_NAME` on a bash or skill-sandbox child.
20
+ * 3. The process entry path, when it sits under
21
+ * `plugins/<service>/skills/<skill>/{scripts,tools}/`.
22
+ *
23
+ * Outside any plugin context the resolver is unscoped and behaves like a
24
+ * direct reveal. A standalone child has an empty in-process metadata
25
+ * cache; when plugin context is set, a cache miss consults the live
26
+ * credential catalog so an empty cache is not reported as "not found".
20
27
  */
21
28
 
22
- import { getCurrentPluginName } from "../plugins/plugin-execution-context.js";
29
+ import { credentialKey } from "@vellumai/credential-storage";
30
+
23
31
  import { getSecureKeyResultAsync } from "../security/secure-keys.js";
32
+ import {
33
+ type CredentialMetadata,
34
+ listCredentialRecordsLive,
35
+ } from "../tools/credentials/metadata-store.js";
36
+ import { parseServiceFieldRef } from "../tools/credentials/ref-parse.js";
24
37
  import { resolveCredentialRef } from "../tools/credentials/resolve.js";
25
38
  import { credentialInPluginScope } from "./credential-scope.js";
39
+ import { resolveCallingPluginName } from "./plugin-name-env.js";
26
40
 
27
41
  /**
28
42
  * Raised when a credential cannot be resolved: the reference does not match a
@@ -36,6 +50,50 @@ export class CredentialResolutionError extends Error {
36
50
  }
37
51
  }
38
52
 
53
+ interface ResolvedRef {
54
+ service: string;
55
+ field: string;
56
+ storageKey: string;
57
+ }
58
+
59
+ function findLiveRecord(
60
+ records: CredentialMetadata[],
61
+ ref: string,
62
+ ): CredentialMetadata | undefined {
63
+ const byId = records.find((record) => record.credentialId === ref);
64
+ if (byId) {
65
+ return byId;
66
+ }
67
+ const parsed = parseServiceFieldRef(ref);
68
+ if (!parsed) {
69
+ return undefined;
70
+ }
71
+ return records.find(
72
+ (record) =>
73
+ record.service === parsed.service && record.field === parsed.field,
74
+ );
75
+ }
76
+
77
+ async function resolveCredentialRefLive(
78
+ ref: string,
79
+ ): Promise<ResolvedRef | undefined> {
80
+ const live = await listCredentialRecordsLive();
81
+ if (live.unreachable) {
82
+ throw new CredentialResolutionError(
83
+ "Credential store is unreachable.",
84
+ );
85
+ }
86
+ const record = findLiveRecord(live.records, ref);
87
+ if (!record) {
88
+ return undefined;
89
+ }
90
+ return {
91
+ service: record.service,
92
+ field: record.field,
93
+ storageKey: credentialKey(record.service, record.field),
94
+ };
95
+ }
96
+
39
97
  /**
40
98
  * Resolve a credential reference to its plaintext value.
41
99
  *
@@ -45,15 +103,16 @@ export class CredentialResolutionError extends Error {
45
103
  * store is unreachable, or a plugin in context is not scoped to the credential.
46
104
  */
47
105
  export async function resolveCredential(ref: string): Promise<string> {
48
- const resolved = resolveCredentialRef(ref);
106
+ const pluginName = resolveCallingPluginName();
107
+
108
+ let resolved: ResolvedRef | undefined = resolveCredentialRef(ref);
109
+ if (!resolved && pluginName !== undefined) {
110
+ resolved = await resolveCredentialRefLive(ref);
111
+ }
49
112
  if (!resolved) {
50
113
  throw new CredentialResolutionError(`Credential not found: ${ref}`);
51
114
  }
52
115
 
53
- // Scope the resolution to the plugin in context, if any. The ownership gate
54
- // is enforced before the plaintext is read so an out-of-scope plugin never
55
- // touches the secure backend.
56
- const pluginName = getCurrentPluginName();
57
116
  if (
58
117
  pluginName !== undefined &&
59
118
  !credentialInPluginScope(pluginName, resolved.service)
@@ -70,7 +129,7 @@ export async function resolveCredential(ref: string): Promise<string> {
70
129
  if (value == null || value.length === 0) {
71
130
  if (unreachable) {
72
131
  throw new CredentialResolutionError(
73
- "Credential store is unreachable — ensure the assistant is running",
132
+ "Credential store is unreachable.",
74
133
  );
75
134
  }
76
135
  throw new CredentialResolutionError(`Credential not found: ${ref}`);
@@ -397,3 +397,25 @@ describe("doesSupportVision with a bare string", () => {
397
397
  expect(doesSupportVision("qwen2.5-vl")).toBe(true);
398
398
  });
399
399
  });
400
+
401
+ describe("doesSupportVision with a resolved target", () => {
402
+ test("profile policy can enable images for a catalog text model", () => {
403
+ expect(
404
+ doesSupportVision({
405
+ provider: "fireworks",
406
+ model: "accounts/fireworks/models/glm-5p2",
407
+ inputModalities: { image: { enabled: true, supported: true } },
408
+ }),
409
+ ).toBe(true);
410
+ });
411
+
412
+ test("profile policy can disable images for a catalog vision model", () => {
413
+ expect(
414
+ doesSupportVision({
415
+ provider: "anthropic",
416
+ model: "claude-opus-4-6",
417
+ inputModalities: { image: { enabled: false } },
418
+ }),
419
+ ).toBe(false);
420
+ });
421
+ });
@@ -37,15 +37,24 @@ import {
37
37
  } from "../providers/model-catalog.js";
38
38
  import type { ModelProfileInfo } from "./types.js";
39
39
 
40
+ /** A fully resolved provider target plus the winning profile's policy. */
41
+ export interface ResolvedVisionTarget {
42
+ readonly provider?: string;
43
+ readonly model: string;
44
+ readonly inputModalities?: InputModalities | null;
45
+ }
46
+
40
47
  /**
41
48
  * Whether the given model or profile can process image input.
42
49
  *
43
- * `modelOrProfile` may be a concrete model id, a profile key, or a
44
- * {@link ModelProfileInfo}. A bare string is resolved as a model id first and,
45
- * failing that, as a profile key. Returns `false` when nothing resolves.
50
+ * `modelOrProfile` may be a concrete model id, a profile key, a
51
+ * {@link ModelProfileInfo}, or a fully resolved target. A resolved target
52
+ * combines the final provider/model with the winning profile's modality
53
+ * policy. A bare string is resolved as a model id first and, failing that, as
54
+ * a profile key. Returns `false` when nothing resolves.
46
55
  */
47
56
  export function doesSupportVision(
48
- modelOrProfile: ModelProfileInfo | string,
57
+ modelOrProfile: ModelProfileInfo | ResolvedVisionTarget | string,
49
58
  ): boolean {
50
59
  if (typeof modelOrProfile === "string") {
51
60
  // Concrete catalog model id first, then a profile key, then any profile
@@ -58,7 +67,10 @@ export function doesSupportVision(
58
67
  false
59
68
  );
60
69
  }
61
- return profileVision(modelOrProfile.key) ?? false;
70
+ if ("key" in modelOrProfile) {
71
+ return profileVision(modelOrProfile.key) ?? false;
72
+ }
73
+ return resolveEntryVision(modelOrProfile) ?? false;
62
74
  }
63
75
 
64
76
  /**
@@ -0,0 +1,136 @@
1
+ import { afterEach, describe, expect, spyOn, test } from "bun:test";
2
+
3
+ import type { SkillSummary } from "../../config/skills.js";
4
+ import * as skills from "../../config/skills.js";
5
+ import type { Conversation } from "../../daemon/conversation.js";
6
+ import * as conversationRegistry from "../../daemon/conversation-registry.js";
7
+ import { PLUGIN_NAME_ENV } from "../../plugin-api/plugin-name-env.js";
8
+ import {
9
+ applyActivePluginName,
10
+ uniqueActivePluginOwner,
11
+ } from "../active-plugin-env.js";
12
+
13
+ function pluginSkill(id: string, pluginId: string): SkillSummary {
14
+ return {
15
+ id,
16
+ name: id,
17
+ displayName: id,
18
+ description: id,
19
+ directoryPath: `/tmp/${id}`,
20
+ skillFilePath: `/tmp/${id}/SKILL.md`,
21
+ source: "plugin",
22
+ owner: { kind: "plugin", id: pluginId },
23
+ };
24
+ }
25
+
26
+ function bundledSkill(id: string): SkillSummary {
27
+ return {
28
+ id,
29
+ name: id,
30
+ displayName: id,
31
+ description: id,
32
+ directoryPath: `/tmp/${id}`,
33
+ skillFilePath: `/tmp/${id}/SKILL.md`,
34
+ source: "bundled",
35
+ };
36
+ }
37
+
38
+ function conversationWithSkills(skillIds: string[]): Conversation {
39
+ return {
40
+ skillProjectionState: new Map(skillIds.map((id) => [id, "v1:hash"])),
41
+ } as Conversation;
42
+ }
43
+
44
+ describe("uniqueActivePluginOwner", () => {
45
+ let catalogSpy: ReturnType<typeof spyOn>;
46
+ let conversationSpy: ReturnType<typeof spyOn>;
47
+
48
+ afterEach(() => {
49
+ catalogSpy?.mockRestore();
50
+ conversationSpy?.mockRestore();
51
+ });
52
+
53
+ function stub(skillIds: string[], catalog: SkillSummary[]): void {
54
+ conversationSpy = spyOn(
55
+ conversationRegistry,
56
+ "findConversationOrSubagent",
57
+ ).mockReturnValue(conversationWithSkills(skillIds));
58
+ catalogSpy = spyOn(skills, "loadSkillCatalog").mockReturnValue(catalog);
59
+ }
60
+
61
+ test("returns the owner when exactly one plugin skill is active", () => {
62
+ stub(["sms-setup", "bash"], [
63
+ pluginSkill("sms-setup", "sms"),
64
+ bundledSkill("bash"),
65
+ ]);
66
+ expect(uniqueActivePluginOwner("conv-1")).toBe("sms");
67
+ });
68
+
69
+ test("returns undefined when no plugin skill is active", () => {
70
+ stub(["bash"], [bundledSkill("bash")]);
71
+ expect(uniqueActivePluginOwner("conv-1")).toBeUndefined();
72
+ });
73
+
74
+ test("returns undefined when two plugin owners are active", () => {
75
+ stub(
76
+ ["sms-setup", "linear-inbox"],
77
+ [
78
+ pluginSkill("sms-setup", "sms"),
79
+ pluginSkill("linear-inbox", "linear"),
80
+ ],
81
+ );
82
+ expect(uniqueActivePluginOwner("conv-1")).toBeUndefined();
83
+ });
84
+
85
+ test("treats two skills from the same plugin as a single owner", () => {
86
+ stub(
87
+ ["sms-setup", "sms-send"],
88
+ [pluginSkill("sms-setup", "sms"), pluginSkill("sms-send", "sms")],
89
+ );
90
+ expect(uniqueActivePluginOwner("conv-1")).toBe("sms");
91
+ });
92
+
93
+ test("returns undefined without a conversation id", () => {
94
+ expect(uniqueActivePluginOwner(undefined)).toBeUndefined();
95
+ expect(uniqueActivePluginOwner("")).toBeUndefined();
96
+ });
97
+ });
98
+
99
+ describe("applyActivePluginName", () => {
100
+ let catalogSpy: ReturnType<typeof spyOn>;
101
+ let conversationSpy: ReturnType<typeof spyOn>;
102
+
103
+ afterEach(() => {
104
+ catalogSpy?.mockRestore();
105
+ conversationSpy?.mockRestore();
106
+ });
107
+
108
+ test("sets VELLUM_PLUGIN_NAME when a unique plugin owner is active", () => {
109
+ conversationSpy = spyOn(
110
+ conversationRegistry,
111
+ "findConversationOrSubagent",
112
+ ).mockReturnValue(conversationWithSkills(["sms-setup"]));
113
+ catalogSpy = spyOn(skills, "loadSkillCatalog").mockReturnValue([
114
+ pluginSkill("sms-setup", "sms"),
115
+ ]);
116
+
117
+ const env: NodeJS.ProcessEnv = {};
118
+ applyActivePluginName(env, "conv-1");
119
+ expect(env[PLUGIN_NAME_ENV]).toBe("sms");
120
+ });
121
+
122
+ test("leaves the env unchanged when no unique plugin owner is active", () => {
123
+ conversationSpy = spyOn(
124
+ conversationRegistry,
125
+ "findConversationOrSubagent",
126
+ ).mockReturnValue(conversationWithSkills(["sms-setup", "linear-inbox"]));
127
+ catalogSpy = spyOn(skills, "loadSkillCatalog").mockReturnValue([
128
+ pluginSkill("sms-setup", "sms"),
129
+ pluginSkill("linear-inbox", "linear"),
130
+ ]);
131
+
132
+ const env: NodeJS.ProcessEnv = {};
133
+ applyActivePluginName(env, "conv-1");
134
+ expect(env[PLUGIN_NAME_ENV]).toBeUndefined();
135
+ });
136
+ });
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Inject the owning plugin name into a sanitized child environment when
3
+ * exactly one plugin-resident skill is active in the conversation.
4
+ *
5
+ * Identity comes from the skill catalog `owner` (install-directory
6
+ * basename), not from the child command or package.json `name`.
7
+ */
8
+
9
+ import { loadSkillCatalog } from "../config/skills.js";
10
+ import { findConversationOrSubagent } from "../daemon/conversation-registry.js";
11
+ import { PLUGIN_NAME_ENV } from "../plugin-api/plugin-name-env.js";
12
+
13
+ /**
14
+ * Unique catalog owner id among plugin-resident skills active in this
15
+ * conversation, or undefined when none or more than one plugin is active.
16
+ */
17
+ export function uniqueActivePluginOwner(
18
+ conversationId: string | undefined,
19
+ ): string | undefined {
20
+ if (conversationId === undefined || conversationId.length === 0) {
21
+ return undefined;
22
+ }
23
+ const conversation = findConversationOrSubagent(conversationId);
24
+ if (!conversation) {
25
+ return undefined;
26
+ }
27
+ const owners = new Set<string>();
28
+ const catalog = loadSkillCatalog();
29
+ for (const skillId of conversation.skillProjectionState.keys()) {
30
+ const skill = catalog.find((entry) => entry.id === skillId);
31
+ if (skill?.owner?.kind === "plugin" && skill.owner.id.length > 0) {
32
+ owners.add(skill.owner.id);
33
+ }
34
+ }
35
+ if (owners.size !== 1) {
36
+ return undefined;
37
+ }
38
+ return [...owners][0];
39
+ }
40
+
41
+ /** Set `VELLUM_PLUGIN_NAME` on `env` when a single plugin owner is active. */
42
+ export function applyActivePluginName(
43
+ env: NodeJS.ProcessEnv,
44
+ conversationId: string | undefined,
45
+ ): void {
46
+ const owner = uniqueActivePluginOwner(conversationId);
47
+ if (owner === undefined) {
48
+ return;
49
+ }
50
+ env[PLUGIN_NAME_ENV] = owner;
51
+ }
@@ -78,6 +78,9 @@ import surfaceCompletionNudgePostModelCall from "./surface-completion-nudge/hook
78
78
  import surfaceCompletionNudgeStop from "./surface-completion-nudge/hooks/stop.js";
79
79
  import { resetSurfaceCompletionNudgeStoreForTests } from "./surface-completion-nudge/nudge-state-store.js";
80
80
  import surfaceCompletionNudgePkg from "./surface-completion-nudge/package.json" with { type: "json" };
81
+ import taskProgressPostCompact from "./task-progress/hooks/post-compact.js";
82
+ import taskProgressUserPromptSubmit from "./task-progress/hooks/user-prompt-submit.js";
83
+ import taskProgressPkg from "./task-progress/package.json" with { type: "json" };
81
84
  import taskProgressNudgePostToolUse, {
82
85
  resetTaskProgressNudgeStateForTests,
83
86
  } from "./task-progress-nudge/hooks/post-tool-use.js";
@@ -371,6 +374,26 @@ export const defaultExplorationDriftPlugin: Plugin = {
371
374
  },
372
375
  };
373
376
 
377
+ /**
378
+ * `task-progress`: a `user-prompt-submit` and `post-compact` hook pair that
379
+ * injects a compact provider-only snapshot of active `task_progress` cards.
380
+ * Reads canonical conversation surface snapshots from the host helper.
381
+ * Owns no durable state. Registered after `empty-response` so memory/runtime
382
+ * injection and refusal quarantine settle first. Built-in history repair
383
+ * still runs after the user-prompt hook chain. The snapshot is small enough
384
+ * to re-apply in minimal injection mode.
385
+ */
386
+ export const defaultTaskProgressPlugin: Plugin = {
387
+ manifest: {
388
+ name: taskProgressPkg.name,
389
+ version: taskProgressPkg.version,
390
+ },
391
+ hooks: {
392
+ "user-prompt-submit": taskProgressUserPromptSubmit,
393
+ "post-compact": taskProgressPostCompact,
394
+ },
395
+ };
396
+
374
397
  /**
375
398
  * `task-progress-nudge` — a `post-tool-use` hook that nudges the model to show
376
399
  * a `task_progress` card once an interactive turn has accumulated several
@@ -440,6 +463,7 @@ export function getAllDefaultPlugins(): readonly Plugin[] {
440
463
  defaultImageFallbackPlugin,
441
464
  defaultToolResultTruncatePlugin,
442
465
  defaultEmptyResponsePlugin,
466
+ defaultTaskProgressPlugin,
443
467
  defaultMaxTokensContinuePlugin,
444
468
  defaultToolErrorPlugin,
445
469
  defaultExplorationDriftPlugin,
@@ -0,0 +1,64 @@
1
+ /**
2
+ * The daily archive (`memory/archive/`) holds raw buffer entries only. Per-turn
3
+ * injection reads concept pages and never the archive, and text a pass
4
+ * composes or derives on a page is not in it. So nothing the consolidation
5
+ * agent reads may present the archive as making removal safe: that framing
6
+ * licenses deleting filed content that exists nowhere else.
7
+ *
8
+ * The check is sentence-level: no sentence may pair the archive with
9
+ * reassurance that removing content loses nothing. A sentence warning that
10
+ * removed text IS lost stays allowed.
11
+ */
12
+ import { describe, expect, test } from "bun:test";
13
+
14
+ import { renderConsolidationPrompt } from "../substrate/prompts/consolidation.js";
15
+ import { deleteMemoryPageTool } from "../tools.js";
16
+
17
+ function archiveSafetyClaims(text: string): string[] {
18
+ return text
19
+ .split(/(?<=[.!?])\s+/)
20
+ .filter(
21
+ (sentence) =>
22
+ /archive/i.test(sentence) &&
23
+ /don.t worry|no need to worry|never los|nothing is lost|won.t lose|safe to (?:delete|remove|cut|drop)/i.test(
24
+ sentence,
25
+ ),
26
+ );
27
+ }
28
+
29
+ describe("the archive is never framed as making removal safe", () => {
30
+ test("neither consolidation article shape makes the claim", () => {
31
+ const v2 = renderConsolidationPrompt("May 1, 12:00 PM", {
32
+ includeCorePagesSection: false,
33
+ articleShape: "v2",
34
+ bufferEntries: "",
35
+ });
36
+ // The over-long-sections step carries the one legitimate archive
37
+ // sentence, so the v3 render includes it.
38
+ const v3 = renderConsolidationPrompt("May 1, 12:00 PM", {
39
+ includeCorePagesSection: true,
40
+ articleShape: "v3",
41
+ bufferEntries: "",
42
+ overlongSections: {
43
+ windowChars: 6000,
44
+ sections: [{ slug: "journal", title: "", chars: 7200 }],
45
+ },
46
+ });
47
+
48
+ expect(v3).toContain("the archive is a record");
49
+ expect(archiveSafetyClaims(v2)).toEqual([]);
50
+ expect(archiveSafetyClaims(v3)).toEqual([]);
51
+ });
52
+
53
+ test("a warning that removed text is lost is not the claim", () => {
54
+ expect(
55
+ archiveSafetyClaims(
56
+ "Text you cut from a page is lost to retrieval: the archive does not bring it back.",
57
+ ),
58
+ ).toEqual([]);
59
+ });
60
+
61
+ test("the delete_memory_page description does not make the claim", () => {
62
+ expect(archiveSafetyClaims(deleteMemoryPageTool.description)).toEqual([]);
63
+ });
64
+ });
@@ -231,18 +231,26 @@ describe("spawnMemoryWorkerProcess", () => {
231
231
  }
232
232
  });
233
233
 
234
- test("reuses an already-running worker without spawning", async () => {
235
- // A live PID file (this test process) makes the probe report running.
234
+ test("spawns when the PID file names an unrelated live process", async () => {
236
235
  writeFileSync(pidPath, String(process.pid));
237
236
  let spawned = false;
238
237
  const restore = stubBunSpawn(() => {
239
238
  spawned = true;
240
- return { unref: () => {}, kill: () => {}, pid: 1, exited: neverExits() };
239
+ writeFileSync(pidPath, "4242");
240
+ return {
241
+ unref: () => {},
242
+ kill: () => {},
243
+ pid: 4242,
244
+ exited: neverExits(),
245
+ };
241
246
  });
242
247
  try {
243
- const result = await spawnMemoryWorkerProcess({ pidWaitTimeoutMs: 100 });
244
- expect(result).toEqual({ pid: process.pid, alreadyRunning: true });
245
- expect(spawned).toBe(false);
248
+ const result = await spawnMemoryWorkerProcess({
249
+ pidWaitTimeoutMs: 1_000,
250
+ pidPollIntervalMs: 10,
251
+ });
252
+ expect(result).toEqual({ pid: 4242, alreadyRunning: false });
253
+ expect(spawned).toBe(true);
246
254
  } finally {
247
255
  restore();
248
256
  }
@@ -250,6 +258,11 @@ describe("spawnMemoryWorkerProcess", () => {
250
258
  });
251
259
 
252
260
  describe("probeMemoryWorker", () => {
261
+ test("reports not_running when the PID file names a live process that is not this worker", () => {
262
+ writeFileSync(pidPath, String(process.pid));
263
+ expect(probeMemoryWorker()).toEqual({ status: "not_running" });
264
+ });
265
+
253
266
  test("reports running (not throws) when the process exists but is not signalable (EPERM)", () => {
254
267
  writeFileSync(pidPath, "4321");
255
268
  const restore = stubProcessKill(new Set(), new Set([4321]));
@@ -1,6 +1,10 @@
1
+ import {
2
+ type RecallSource,
3
+ RecallSourceSchema,
4
+ } from "../../../../api/events/tool-result.js";
1
5
  import { truncate } from "../host-utils.js";
2
- import { ALL_RECALL_SOURCES, normalizeRecallSources } from "./limits.js";
3
- import type { RecallEvidence, RecallSource } from "./types.js";
6
+ import { normalizeRecallSources } from "./limits.js";
7
+ import type { RecallEvidence } from "./types.js";
4
8
 
5
9
  export type RecallAgentConfidence = "high" | "medium" | "low";
6
10
 
@@ -76,7 +80,7 @@ export const SEARCH_SOURCES_TOOL_DEFINITION: RecallAgentToolDefinition = {
76
80
  sources: {
77
81
  type: "array",
78
82
  description: "Optional subset of internal sources to search.",
79
- items: { type: "string", enum: [...ALL_RECALL_SOURCES] },
83
+ items: { type: "string", enum: [...RecallSourceSchema.options] },
80
84
  uniqueItems: true,
81
85
  },
82
86
  limit: {
@@ -176,9 +180,9 @@ export function buildRecallAgentPromptBundle(
176
180
  ): RecallAgentPromptBundle {
177
181
  const availableSources = normalizeRecallSources(options.availableSources);
178
182
  const maxSearchCalls = options.maxSearchCalls ?? DEFAULT_MAX_SEARCH_CALLS;
179
- const evidence = prepareRecallAgentPromptEvidence(
183
+ const evidence = truncateRecallEvidenceToBudget(
180
184
  options.evidence,
181
- options.evidenceBudgetChars,
185
+ options.evidenceBudgetChars ?? DEFAULT_RECALL_AGENT_EVIDENCE_BUDGET_CHARS,
182
186
  );
183
187
  const citationIds = evidence.map((item) => item.id);
184
188
 
@@ -216,13 +220,6 @@ export function buildRecallAgentPromptBundle(
216
220
  return { prompt, evidence };
217
221
  }
218
222
 
219
- function prepareRecallAgentPromptEvidence(
220
- evidence: readonly RecallEvidence[],
221
- evidenceBudgetChars = DEFAULT_RECALL_AGENT_EVIDENCE_BUDGET_CHARS,
222
- ): RecallEvidence[] {
223
- return truncateRecallEvidenceToBudget(evidence, evidenceBudgetChars);
224
- }
225
-
226
223
  export function truncateRecallEvidenceToBudget(
227
224
  evidence: readonly RecallEvidence[],
228
225
  maxTextChars: number,