@vellumai/assistant 0.12.1 → 0.12.2-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 (577) hide show
  1. package/AGENTS.md +4 -0
  2. package/ARCHITECTURE.md +20 -21
  3. package/docs/architecture/turn-actor.md +15 -8
  4. package/docs/guardian-request-flow.md +16 -0
  5. package/node_modules/@vellumai/slack-text/src/index.ts +13 -8
  6. package/node_modules/@vellumai/slack-text/src/label-resolution-entities.test.ts +95 -0
  7. package/openapi.yaml +360 -51
  8. package/package.json +3 -1
  9. package/scripts/smoke-desktop-window-theme.ts +186 -0
  10. package/src/__tests__/always-loaded-tools-guard.test.ts +18 -10
  11. package/src/__tests__/anthropic-provider.test.ts +0 -63
  12. package/src/__tests__/app-compiler.test.ts +6 -0
  13. package/src/__tests__/app-control-tool-schemas.test.ts +5 -2
  14. package/src/__tests__/app-dir-path-guard.test.ts +1 -3
  15. package/src/__tests__/assistant-event-hub-machine-name.test.ts +6 -7
  16. package/src/__tests__/assistant-event-hub.test.ts +44 -0
  17. package/src/__tests__/computer-use-skill-manifest-regression.test.ts +1 -1
  18. package/src/__tests__/computer-use-skill-proxy-bridge.test.ts +5 -2
  19. package/src/__tests__/computer-use-tools.test.ts +19 -7
  20. package/src/__tests__/config-schema-cmd.test.ts +2 -2
  21. package/src/__tests__/config-schema.test.ts +28 -29
  22. package/src/__tests__/config-watcher.test.ts +23 -8
  23. package/src/__tests__/connection-model-compat.test.ts +2 -2
  24. package/src/__tests__/conversation-key-store-disk-view.test.ts +1 -1
  25. package/src/__tests__/conversation-pairing.test.ts +26 -0
  26. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +4 -10
  27. package/src/__tests__/conversation-queue.test.ts +48 -207
  28. package/src/__tests__/conversation-retry-discard.test.ts +54 -1
  29. package/src/__tests__/conversation-runtime-assembly.test.ts +28 -0
  30. package/src/__tests__/conversation-skill-tools.test.ts +27 -0
  31. package/src/__tests__/conversation-surfaces-app-control.test.ts +8 -3
  32. package/src/__tests__/conversation-surfaces-point-at-budget.test.ts +1 -0
  33. package/src/__tests__/conversation-surfaces-point-at-capability.test.ts +1 -0
  34. package/src/__tests__/conversation-surfaces-task-progress.test.ts +67 -0
  35. package/src/__tests__/conversation-title-service.test.ts +14 -10
  36. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +3 -5
  37. package/src/__tests__/conversation-tool-setup-attribution.test.ts +51 -0
  38. package/src/__tests__/credential-record-retire.test.ts +171 -1
  39. package/src/__tests__/credential-routes.test.ts +250 -3
  40. package/src/__tests__/credential-security-invariants.test.ts +5 -3
  41. package/src/__tests__/cu-unified-flow.test.ts +14 -4
  42. package/src/__tests__/document-sync-tags.test.ts +25 -0
  43. package/src/__tests__/drain-kick-guard.test.ts +0 -2
  44. package/src/__tests__/drain-requeue-on-contention.test.ts +0 -6
  45. package/src/__tests__/evict-conversations-for-reload.test.ts +1 -1
  46. package/src/__tests__/git-porcelain.test.ts +47 -0
  47. package/src/__tests__/heartbeat-service.test.ts +56 -40
  48. package/src/__tests__/helpers/set-workspace-mcp.ts +11 -0
  49. package/src/__tests__/host-bash-proxy.test.ts +26 -45
  50. package/src/__tests__/host-browser-proxy.test.ts +6 -17
  51. package/src/__tests__/host-cu-proxy.test.ts +596 -0
  52. package/src/__tests__/host-file-proxy-targeted.test.ts +23 -0
  53. package/src/__tests__/host-file-proxy.test.ts +11 -23
  54. package/src/__tests__/host-transfer-proxy-targeted.test.ts +30 -9
  55. package/src/__tests__/list-messages-camera-frame.test.ts +121 -0
  56. package/src/__tests__/llm-catalog-parity.test.ts +25 -0
  57. package/src/__tests__/mcp-auth-routes.test.ts +6 -9
  58. package/src/__tests__/mcp-config-secret-boundary.test.ts +78 -136
  59. package/src/__tests__/mcp-health-check.test.ts +8 -11
  60. package/src/__tests__/mcp-list-plugin-servers.test.ts +8 -10
  61. package/src/__tests__/message-deleted-hook-dispatch.test.ts +119 -0
  62. package/src/__tests__/messages-after-tiebreaker.test.ts +91 -0
  63. package/src/__tests__/messages-read-boundary-guard.test.ts +1 -1
  64. package/src/__tests__/messaging-send-tool.test.ts +355 -548
  65. package/src/__tests__/model-intents.test.ts +3 -0
  66. package/src/__tests__/notification-vellum-adapter.test.ts +85 -2
  67. package/src/__tests__/oauth-commands-routes.test.ts +168 -0
  68. package/src/__tests__/oauth-provider-profiles.test.ts +18 -0
  69. package/src/__tests__/oauth-provider-serializer.test.ts +22 -0
  70. package/src/__tests__/oauth-providers-routes.test.ts +1 -0
  71. package/src/__tests__/openai-provider.test.ts +1 -0
  72. package/src/__tests__/outlook-messaging-provider.test.ts +6 -6
  73. package/src/__tests__/platform-callback-registration.test.ts +8 -16
  74. package/src/__tests__/platform-client-verify-credential.test.ts +2 -0
  75. package/src/__tests__/plugin-import-boundary-guard.test.ts +14 -0
  76. package/src/__tests__/qdrant-manager.test.ts +132 -0
  77. package/src/__tests__/resolve-app.test.ts +135 -0
  78. package/src/__tests__/scaffold-managed-skill-tool.test.ts +229 -6
  79. package/src/__tests__/script-proxy-certs.test.ts +35 -0
  80. package/src/__tests__/secret-routes-acp-guard.test.ts +59 -1
  81. package/src/__tests__/service-account-token.test.ts +247 -0
  82. package/src/__tests__/skill-feature-flags-integration.test.ts +1 -1
  83. package/src/__tests__/skill-feature-flags.test.ts +2 -2
  84. package/src/__tests__/skills.test.ts +9 -4
  85. package/src/__tests__/source-fingerprint.test.ts +184 -0
  86. package/src/__tests__/subagent-tool-gate-mode.test.ts +70 -3
  87. package/src/__tests__/subagent-tools.test.ts +3 -2
  88. package/src/__tests__/telephony-tts-guard.test.ts +38 -3
  89. package/src/__tests__/test-support/computer-use-skill-harness.ts +3 -2
  90. package/src/__tests__/ui-channel-variants.test.ts +1 -56
  91. package/src/__tests__/unicode.test.ts +13 -0
  92. package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +4 -0
  93. package/src/__tests__/watch-retro-tool-availability.test.ts +8 -17
  94. package/src/__tests__/workspace-git-service.test.ts +26 -0
  95. package/src/__tests__/workspace-migration-154-repair-retired-codex-gpt-5-4-model-ids.test.ts +336 -0
  96. package/src/acp/__tests__/acp-claude-oauth.test.ts +257 -6
  97. package/src/acp/__tests__/acp-credentials.test.ts +13 -0
  98. package/src/acp/__tests__/agent-process.test.ts +258 -4
  99. package/src/acp/__tests__/auth-required.test.ts +96 -0
  100. package/src/acp/__tests__/claude-token-refresh.test.ts +257 -0
  101. package/src/acp/__tests__/client-handler.test.ts +90 -2
  102. package/src/acp/__tests__/helpers/acp-model-option.ts +51 -0
  103. package/src/acp/__tests__/prepare-agent-env.test.ts +60 -1
  104. package/src/acp/__tests__/session-manager-persistence.test.ts +5 -1
  105. package/src/acp/__tests__/session-manager-resume.test.ts +270 -6
  106. package/src/acp/__tests__/session-manager-usage.test.ts +4 -2
  107. package/src/acp/__tests__/session-manager.test.ts +895 -4
  108. package/src/acp/acp-claude-oauth.ts +328 -14
  109. package/src/acp/acp-credentials.ts +19 -0
  110. package/src/acp/agent-process.test.ts +4 -1
  111. package/src/acp/agent-process.ts +96 -10
  112. package/src/acp/auth-required.ts +50 -1
  113. package/src/acp/auto-install.test.ts +9 -7
  114. package/src/acp/claude-token-refresh.ts +150 -0
  115. package/src/acp/client-handler.ts +16 -3
  116. package/src/acp/model-config.test.ts +166 -0
  117. package/src/acp/model-config.ts +138 -0
  118. package/src/acp/prepare-agent-env.ts +21 -6
  119. package/src/acp/resolve-agent.test.ts +220 -6
  120. package/src/acp/resolve-agent.ts +88 -23
  121. package/src/acp/session-manager.test.ts +196 -3
  122. package/src/acp/session-manager.ts +441 -18
  123. package/src/acp/types.ts +50 -5
  124. package/src/agent/loop.ts +1 -0
  125. package/src/api/events/acp-session-model-update.test.ts +88 -0
  126. package/src/api/events/acp-session-model-update.ts +46 -0
  127. package/src/api/events/app-files-changed.ts +4 -3
  128. package/src/api/events/notification-intent.ts +2 -0
  129. package/src/api/events/user-message-echo.ts +7 -1
  130. package/src/api/index.ts +6 -0
  131. package/src/api/responses/conversation-message.ts +5 -0
  132. package/src/apps/app-store.ts +1 -1
  133. package/src/apps/resolve-app.ts +81 -0
  134. package/src/apps/source-fingerprint.ts +266 -0
  135. package/src/avatar/identity-avatar.ts +7 -2
  136. package/src/bundler/app-compiler.ts +15 -0
  137. package/src/calls/__tests__/barge-in-guard.test.ts +81 -0
  138. package/src/{__tests__ → calls/__tests__}/call-controller.test.ts +503 -82
  139. package/src/{__tests__ → calls/__tests__}/media-stream-audio-transcode.test.ts +1 -1
  140. package/src/{__tests__ → calls/__tests__}/media-stream-output.test.ts +37 -8
  141. package/src/{__tests__ → calls/__tests__}/media-stream-parser.test.ts +1 -1
  142. package/src/{__tests__ → calls/__tests__}/media-stream-server-integration.test.ts +150 -57
  143. package/src/{__tests__ → calls/__tests__}/media-stream-stt-session.test.ts +5 -5
  144. package/src/{__tests__ → calls/__tests__}/media-turn-detector.test.ts +1 -1
  145. package/src/{live-voice → calls}/__tests__/progress-narration.test.ts +4 -4
  146. package/src/{live-voice → calls}/__tests__/progress-phrases.test.ts +1 -1
  147. package/src/calls/__tests__/voice-control-protocol.test.ts +120 -25
  148. package/src/calls/__tests__/voice-leg-coordinator.test.ts +229 -0
  149. package/src/calls/__tests__/voice-progress-cadence.test.ts +274 -0
  150. package/src/{__tests__/voice-session-bridge.test.ts → calls/__tests__/voice-session-bridge-turn-flow.test.ts} +20 -12
  151. package/src/calls/__tests__/voice-session-bridge.test.ts +56 -1
  152. package/src/calls/__tests__/voice-triage-escalate.test.ts +105 -0
  153. package/src/calls/barge-in-guard.ts +118 -0
  154. package/src/calls/call-constants.ts +24 -4
  155. package/src/calls/call-controller.ts +499 -199
  156. package/src/calls/call-transport.ts +16 -0
  157. package/src/calls/media-stream-output.ts +32 -0
  158. package/src/calls/media-stream-server.ts +82 -30
  159. package/src/calls/media-stream-stt-session.ts +15 -0
  160. package/src/{live-voice → calls}/progress-narration.ts +2 -2
  161. package/src/calls/resolve-call-tts-provider.ts +33 -2
  162. package/src/calls/tts-call-strategy.ts +9 -5
  163. package/src/calls/types.ts +2 -0
  164. package/src/calls/voice-control-protocol.ts +155 -21
  165. package/src/calls/voice-leg-coordinator.ts +189 -0
  166. package/src/calls/voice-progress-cadence.ts +423 -0
  167. package/src/calls/voice-session-bridge.ts +92 -72
  168. package/src/calls/voice-triage-escalate.ts +178 -59
  169. package/src/cli/commands/__tests__/apps.test.ts +245 -0
  170. package/src/cli/commands/__tests__/bash.test.ts +53 -0
  171. package/src/cli/commands/__tests__/roadmap.help.test.ts +19 -0
  172. package/src/cli/commands/apps.help.ts +67 -13
  173. package/src/cli/commands/apps.ts +183 -0
  174. package/src/cli/commands/bash.help.ts +8 -13
  175. package/src/cli/commands/bash.ts +7 -25
  176. package/src/cli/commands/channels/__tests__/send.test.ts +218 -0
  177. package/src/cli/commands/channels/index.help.ts +70 -2
  178. package/src/cli/commands/channels/index.ts +16 -3
  179. package/src/cli/commands/channels/send.ts +128 -0
  180. package/src/cli/commands/credentials.help.ts +6 -2
  181. package/src/cli/commands/credentials.ts +2 -2
  182. package/src/cli/commands/mcp.help.ts +8 -8
  183. package/src/cli/commands/mcp.ts +1 -1
  184. package/src/cli/commands/oauth/status.ts +5 -0
  185. package/src/cli/commands/roadmap.help.ts +5 -1
  186. package/src/cli/commands/roadmap.ts +1 -1
  187. package/src/cli/lib/bundled-marketplace.json +103 -90
  188. package/src/config/__tests__/default-profile-catalog.test.ts +22 -0
  189. package/src/config/__tests__/memory-retrospective-schema.test.ts +15 -1
  190. package/src/config/__tests__/platform-identity.test.ts +255 -0
  191. package/src/config/__tests__/platform-rehydration.test.ts +99 -9
  192. package/src/config/acp-agent-write.ts +43 -0
  193. package/src/config/acp-defaults.test.ts +13 -3
  194. package/src/config/acp-defaults.ts +23 -6
  195. package/src/config/acp-schema.test.ts +81 -0
  196. package/src/config/acp-schema.ts +30 -2
  197. package/src/config/bundled-skills/acp/SKILL.md +22 -14
  198. package/src/config/bundled-skills/acp/TOOLS.json +5 -1
  199. package/src/config/bundled-skills/app-builder/SKILL.md +12 -9
  200. package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +1 -1
  201. package/src/config/bundled-skills/computer-use/SKILL.md +29 -2
  202. package/src/config/bundled-skills/computer-use/TOOLS.json +99 -0
  203. package/src/config/bundled-skills/computer-use/tools/computer-use-sequence.ts +12 -0
  204. package/src/config/bundled-skills/media-processing/services/preprocess.ts +2 -1
  205. package/src/config/bundled-skills/media-processing/services/processing-pipeline.ts +2 -1
  206. package/src/config/bundled-skills/media-processing/tools/generate-clip.ts +2 -1
  207. package/src/config/bundled-skills/messaging/SKILL.md +3 -3
  208. package/src/config/bundled-skills/messaging/TOOLS.json +3 -3
  209. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +67 -139
  210. package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +1 -1
  211. package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +2 -1
  212. package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +2 -1
  213. package/src/config/bundled-skills/schedule/SKILL.md +3 -4
  214. package/src/config/bundled-skills/screen-annotation/SKILL.md +8 -1
  215. package/src/config/bundled-skills/screen-annotation/tools/screen-annotation.test.ts +4 -1
  216. package/src/config/bundled-skills/skill-management/TOOLS.json +6 -2
  217. package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +5 -2
  218. package/src/config/default-profile-catalog.ts +1 -1
  219. package/src/config/env.ts +2 -2
  220. package/src/config/feature-flag-registry.json +33 -13
  221. package/src/config/loader.ts +2 -0
  222. package/src/config/memory-v3-gate.test.ts +31 -0
  223. package/src/config/memory-v3-gate.ts +20 -16
  224. package/src/config/platform-identity.ts +201 -0
  225. package/src/config/platform-rehydration.ts +11 -27
  226. package/src/config/schema.ts +2 -2
  227. package/src/config/schemas/__tests__/live-voice.test.ts +8 -158
  228. package/src/config/schemas/__tests__/voice.test.ts +148 -0
  229. package/src/config/schemas/calls.ts +1 -28
  230. package/src/config/schemas/live-voice.ts +1 -173
  231. package/src/config/schemas/mcp.ts +1 -1
  232. package/src/config/schemas/memory-retrospective.ts +10 -0
  233. package/src/config/schemas/services.ts +18 -0
  234. package/src/config/schemas/skills.ts +0 -14
  235. package/src/config/schemas/voice.ts +150 -0
  236. package/src/credential-execution/process-manager.test.ts +18 -19
  237. package/src/credential-health/credential-health-service.ts +11 -17
  238. package/src/daemon/__tests__/conversation-tool-setup.test.ts +66 -121
  239. package/src/daemon/__tests__/plugin-mcp-reconcile.test.ts +4 -1
  240. package/src/daemon/__tests__/skill-memory-refresh-mtime.test.ts +133 -0
  241. package/src/daemon/app-change-notify.ts +45 -0
  242. package/src/daemon/config-watcher.ts +13 -194
  243. package/src/daemon/conversation-error.ts +3 -2
  244. package/src/daemon/conversation-media-retry.ts +2 -1
  245. package/src/daemon/conversation-process.ts +35 -64
  246. package/src/daemon/conversation-runtime-assembly.ts +20 -7
  247. package/src/daemon/conversation-skill-tools.ts +13 -31
  248. package/src/daemon/conversation-store.ts +1 -1
  249. package/src/daemon/conversation-surfaces.ts +21 -10
  250. package/src/daemon/conversation-tool-setup.ts +45 -59
  251. package/src/daemon/conversation.ts +0 -3
  252. package/src/daemon/dictation-profile-store.ts +2 -1
  253. package/src/daemon/handlers/shared.ts +2 -1
  254. package/src/daemon/handlers/skills.ts +6 -3
  255. package/src/daemon/host-bash-proxy.ts +8 -12
  256. package/src/daemon/host-browser-proxy.ts +15 -1
  257. package/src/daemon/host-cu-proxy.ts +224 -19
  258. package/src/daemon/host-file-proxy.ts +8 -10
  259. package/src/daemon/host-proxy-preactivation.ts +2 -1
  260. package/src/daemon/host-transfer-proxy.ts +14 -18
  261. package/src/daemon/lifecycle.ts +0 -5
  262. package/src/daemon/mcp-reload-service.ts +5 -5
  263. package/src/daemon/message-types/sync.ts +1 -0
  264. package/src/daemon/orphan-reaper.ts +1 -1
  265. package/src/daemon/providers-setup.ts +1 -1
  266. package/src/daemon/reaction-history-render.ts +2 -1
  267. package/src/daemon/shutdown-handlers.ts +1 -9
  268. package/src/daemon/skill-memory-refresh.ts +64 -0
  269. package/src/daemon/tool-side-effects.ts +7 -18
  270. package/src/desktop/__tests__/fake-desktop.ts +122 -0
  271. package/src/desktop/desktop-chrome-policy.test.ts +79 -0
  272. package/src/desktop/desktop-chrome-policy.ts +47 -0
  273. package/src/desktop/desktop-chrome-session.ts +37 -0
  274. package/src/desktop/desktop-dependencies.test.ts +102 -0
  275. package/src/desktop/desktop-dependencies.ts +308 -0
  276. package/src/desktop/desktop-feature.test.ts +18 -0
  277. package/src/desktop/desktop-feature.ts +19 -0
  278. package/src/desktop/desktop-panel-config.test.ts +135 -0
  279. package/src/desktop/desktop-panel-config.ts +149 -0
  280. package/src/desktop/desktop-session-manager.test.ts +730 -0
  281. package/src/desktop/desktop-session-manager.ts +850 -0
  282. package/src/desktop/desktop-stream-bridge.test.ts +395 -0
  283. package/src/desktop/desktop-stream-bridge.ts +214 -0
  284. package/src/desktop/desktop-wallpaper.test.ts +191 -0
  285. package/src/desktop/desktop-wallpaper.ts +113 -0
  286. package/src/desktop/desktop-window-drag.ts +177 -0
  287. package/src/desktop/desktop-window-manager-config.test.ts +230 -0
  288. package/src/desktop/desktop-window-manager-config.ts +238 -0
  289. package/src/desktop/desktop-window-theme.ts +170 -0
  290. package/src/heartbeat/heartbeat-service.ts +21 -3
  291. package/src/home/__tests__/feed-writer.test.ts +22 -0
  292. package/src/home/feed-writer.ts +11 -2
  293. package/src/hooks/types.ts +33 -0
  294. package/src/i18n/AGENTS.md +22 -0
  295. package/src/i18n/__tests__/i18n.test.ts +108 -0
  296. package/src/i18n/index.ts +16 -0
  297. package/src/i18n/locales.ts +93 -0
  298. package/src/i18n/messages.ts +61 -0
  299. package/src/i18n/title.ts +25 -0
  300. package/src/inbound/platform-callback-registration.ts +4 -5
  301. package/src/live-voice/__tests__/live-voice-agent-turn.test.ts +121 -0
  302. package/src/live-voice/__tests__/live-voice-events.test.ts +8 -7
  303. package/src/live-voice/__tests__/live-voice-metrics.test.ts +0 -3
  304. package/src/live-voice/__tests__/live-voice-progress.test.ts +94 -15
  305. package/src/live-voice/__tests__/live-voice-sight-frame-echo.test.ts +238 -0
  306. package/src/live-voice/__tests__/live-voice-vad.test.ts +2 -2
  307. package/src/live-voice/__tests__/protocol.test.ts +39 -0
  308. package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +13 -69
  309. package/src/live-voice/__tests__/session-controls.test.ts +79 -0
  310. package/src/live-voice/live-voice-photo.ts +25 -6
  311. package/src/live-voice/live-voice-session.ts +297 -699
  312. package/src/live-voice/protocol.ts +60 -2
  313. package/src/live-voice/session-controls.ts +111 -0
  314. package/src/mcp/__tests__/effective-config.test.ts +2 -2
  315. package/src/mcp/__tests__/workspace-mcp-config.test.ts +135 -0
  316. package/src/mcp/effective-config.ts +3 -3
  317. package/src/mcp/mcp-header-store.ts +29 -45
  318. package/src/mcp/spec-schema.ts +111 -0
  319. package/src/mcp/startup.ts +8 -4
  320. package/src/mcp/workspace-mcp-config.ts +188 -0
  321. package/src/messaging/provider.ts +10 -3
  322. package/src/messaging/providers/__tests__/proactive-address.test.ts +95 -0
  323. package/src/messaging/providers/callback-routing.ts +18 -0
  324. package/src/messaging/providers/channel-transport.ts +56 -0
  325. package/src/messaging/providers/discord/transport.ts +17 -0
  326. package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +6 -17
  327. package/src/messaging/providers/slack/adapter.ts +7 -44
  328. package/src/messaging/providers/slack/transport.ts +16 -0
  329. package/src/messaging/providers/telegram-bot/adapter.ts +1 -48
  330. package/src/messaging/providers/telegram-bot/client.ts +0 -26
  331. package/src/messaging/providers/telegram-bot/transport.ts +20 -0
  332. package/src/messaging/providers/whatsapp/adapter.ts +1 -48
  333. package/src/messaging/providers/whatsapp/transport.ts +18 -0
  334. package/src/monitoring/__tests__/workspace-git-heartbeat.test.ts +8 -0
  335. package/src/notifications/__tests__/assistant-reply-producer.test.ts +55 -0
  336. package/src/notifications/__tests__/guardian-feed-projection.test.ts +158 -2
  337. package/src/notifications/__tests__/home-feed-side-effect.test.ts +153 -3
  338. package/src/notifications/__tests__/schedule-result-producer.test.ts +99 -3
  339. package/src/notifications/adapters/macos.ts +3 -0
  340. package/src/notifications/assistant-reply-producer.ts +8 -3
  341. package/src/notifications/conversation-pairing.ts +36 -16
  342. package/src/notifications/delivered-post-record.ts +11 -1
  343. package/src/notifications/guardian-feed-projection.ts +125 -5
  344. package/src/notifications/home-feed-side-effect.ts +55 -7
  345. package/src/notifications/schedule-result-producer.ts +66 -22
  346. package/src/oauth/AGENTS.md +2 -0
  347. package/src/oauth/__tests__/seed-providers-managed.test.ts +149 -0
  348. package/src/oauth/byo-connection.test.ts +105 -0
  349. package/src/oauth/byo-connection.ts +36 -6
  350. package/src/oauth/connection-resolver.test.ts +100 -0
  351. package/src/oauth/connection-resolver.ts +120 -3
  352. package/src/oauth/provider-serializer.ts +9 -0
  353. package/src/oauth/scope-utils.ts +24 -0
  354. package/src/oauth/seed-providers.ts +314 -27
  355. package/src/outbound-proxy/certs.ts +3 -5
  356. package/src/permissions/checker.test.ts +11 -6
  357. package/src/persistence/compaction-log-store-clickhouse.ts +3 -3
  358. package/src/persistence/conversation-crud.ts +71 -63
  359. package/src/persistence/conversation-lineage.ts +12 -4
  360. package/src/persistence/conversation-queries.ts +2 -2
  361. package/src/persistence/conversation-title-placeholders.ts +23 -0
  362. package/src/persistence/conversation-title-service.ts +9 -26
  363. package/src/persistence/embeddings/qdrant-manager.ts +163 -18
  364. package/src/persistence/llm-request-log-sink-clickhouse.ts +3 -3
  365. package/src/persistence/llm-request-log-source-clickhouse.ts +3 -3
  366. package/src/persistence/message-cursor.ts +78 -0
  367. package/src/persistence/message-reads.ts +30 -0
  368. package/src/persistence/migrations/157-invite-contact-id.ts +18 -6
  369. package/src/persistence/migrations/__tests__/157-invite-contact-id.test.ts +149 -0
  370. package/src/persistence/schema/memory-core.ts +5 -0
  371. package/src/platform/client.test.ts +4 -2
  372. package/src/platform/client.ts +2 -10
  373. package/src/plugin-api/constants.ts +2 -0
  374. package/src/plugin-api/index.ts +1 -0
  375. package/src/plugin-api/types.ts +2 -0
  376. package/src/plugins/defaults/index.ts +2 -0
  377. package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +46 -0
  378. package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-backfill.test.ts +118 -0
  379. package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-preserve.test.ts +83 -0
  380. package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +9 -5
  381. package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +20 -10
  382. package/src/plugins/defaults/memory/__tests__/memory-retrospective-prompt.test.ts +5 -0
  383. package/src/plugins/defaults/memory/__tests__/memory-retrospective-state.test.ts +219 -0
  384. package/src/plugins/defaults/memory/__tests__/memory-retrospective-sweep.test.ts +31 -0
  385. package/src/plugins/defaults/memory/__tests__/memory-retrospective-trigger-check.test.ts +63 -0
  386. package/src/plugins/defaults/memory/__tests__/shutdown-hook.test.ts +4 -0
  387. package/src/plugins/defaults/memory/context-search/agent-runner.ts +3 -2
  388. package/src/plugins/defaults/memory/context-search/format.ts +3 -2
  389. package/src/plugins/defaults/memory/context-search/sources/memory-v2.ts +6 -3
  390. package/src/plugins/defaults/memory/context-search/sources/workspace.ts +3 -2
  391. package/src/plugins/defaults/memory/graph/capability-seed.ts +6 -7
  392. package/src/plugins/defaults/memory/hooks/init.ts +10 -0
  393. package/src/plugins/defaults/memory/hooks/message-deleted.ts +21 -0
  394. package/src/plugins/defaults/memory/hooks/shutdown.ts +2 -0
  395. package/src/plugins/defaults/memory/host-utils.ts +10 -0
  396. package/src/plugins/defaults/memory/jobs-worker.ts +13 -0
  397. package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +34 -60
  398. package/src/plugins/defaults/memory/memory-retrospective-cursor-backfill.ts +126 -0
  399. package/src/plugins/defaults/memory/memory-retrospective-cursor-preserve.ts +79 -0
  400. package/src/plugins/defaults/memory/memory-retrospective-cursor.ts +28 -0
  401. package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +3 -5
  402. package/src/plugins/defaults/memory/memory-retrospective-job.ts +12 -3
  403. package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +1 -1
  404. package/src/plugins/defaults/memory/memory-retrospective-state.ts +312 -71
  405. package/src/plugins/defaults/memory/memory-retrospective-sweep.ts +2 -1
  406. package/src/plugins/defaults/memory/memory-retrospective-trigger-check.ts +5 -3
  407. package/src/plugins/defaults/memory/skill-md-mtime-poll.ts +9 -0
  408. package/src/plugins/defaults/memory/startup.ts +28 -2
  409. package/src/plugins/defaults/memory/substrate/__tests__/skill-content.test.ts +7 -9
  410. package/src/plugins/defaults/memory/substrate/page-index.ts +6 -1
  411. package/src/plugins/defaults/memory/substrate/skill-content.ts +2 -2
  412. package/src/plugins/defaults/memory/v1/graph/consolidation.ts +4 -2
  413. package/src/plugins/defaults/memory/v1/graph/extraction.ts +4 -2
  414. package/src/plugins/defaults/memory/v1/graph/retriever.ts +4 -1
  415. package/src/plugins/defaults/memory/v2/reranker.ts +6 -1
  416. package/src/plugins/defaults/memory/v3/__tests__/card.test.ts +24 -0
  417. package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +125 -0
  418. package/src/plugins/defaults/memory/v3/__tests__/sections.test.ts +26 -0
  419. package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +30 -0
  420. package/src/plugins/defaults/memory/v3/card.ts +3 -2
  421. package/src/plugins/defaults/memory/v3/pool-select.ts +5 -3
  422. package/src/plugins/defaults/memory/v3/sections.ts +9 -3
  423. package/src/plugins/defaults/memory/v3/shadow-plugin.ts +5 -2
  424. package/src/plugins/defaults/memory/worker.ts +6 -0
  425. package/src/plugins/mcp-servers.ts +15 -75
  426. package/src/providers/__tests__/connection-model-compat.test.ts +3 -3
  427. package/src/providers/__tests__/retry-callsite.test.ts +41 -0
  428. package/src/providers/__tests__/retry-outbound-sanitize.test.ts +176 -0
  429. package/src/providers/anthropic/client.ts +0 -47
  430. package/src/providers/gemini/client.ts +9 -23
  431. package/src/providers/gemini-thought-signature.test.ts +63 -0
  432. package/src/providers/gemini-thought-signature.ts +44 -0
  433. package/src/providers/inference/__tests__/endpoint-probe.test.ts +42 -3
  434. package/src/providers/inference/auth.ts +2 -3
  435. package/src/providers/inference/connection-availability.ts +27 -8
  436. package/src/providers/inference/endpoint-probe.ts +10 -1
  437. package/src/providers/inference/resolve-auth.ts +22 -4
  438. package/src/providers/inference/service-account-token.ts +248 -0
  439. package/src/providers/model-catalog.ts +34 -0
  440. package/src/providers/model-intents.ts +10 -5
  441. package/src/providers/openai/__tests__/chat-completions-output-token-limit.test.ts +141 -0
  442. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +146 -18
  443. package/src/providers/openai/__tests__/chat-completions-provider-thought-signature.test.ts +613 -0
  444. package/src/providers/openai/__tests__/google-thought-signature.test.ts +235 -0
  445. package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +83 -1
  446. package/src/providers/openai/chat-completions-provider.ts +212 -79
  447. package/src/providers/openai/codex-models.ts +0 -4
  448. package/src/providers/openai/google-thought-signature.ts +320 -0
  449. package/src/providers/opencode/client.test.ts +73 -4
  450. package/src/providers/opencode/client.ts +60 -3
  451. package/src/providers/openrouter/client.ts +20 -0
  452. package/src/providers/outbound-request-sanitize.ts +96 -0
  453. package/src/providers/retry.ts +30 -19
  454. package/src/providers/unparseable-tool-args.ts +5 -1
  455. package/src/runtime/AGENTS.md +3 -1
  456. package/src/runtime/__tests__/channel-send.test.ts +443 -0
  457. package/src/runtime/__tests__/desktop-stream-upgrade.test.ts +89 -0
  458. package/src/runtime/__tests__/host-observe.test.ts +40 -1
  459. package/src/runtime/__tests__/live-voice-upgrade.test.ts +58 -0
  460. package/src/runtime/__tests__/runtime-ws-test-utils.ts +72 -0
  461. package/src/runtime/__tests__/watch-stream-upgrade.test.ts +59 -0
  462. package/src/runtime/agent-wake.ts +2 -1
  463. package/src/runtime/assistant-event-hub.ts +21 -3
  464. package/src/runtime/auth/same-actor.ts +38 -3
  465. package/src/runtime/channel-send.ts +316 -0
  466. package/src/runtime/host-observe.ts +11 -1
  467. package/src/runtime/http-server.ts +160 -151
  468. package/src/runtime/routes/__tests__/acp-claude-auth-routes.test.ts +16 -5
  469. package/src/runtime/routes/__tests__/acp-routes.test.ts +159 -4
  470. package/src/runtime/routes/__tests__/apps-refresh-route.test.ts +275 -0
  471. package/src/runtime/routes/__tests__/channel-send-routes.test.ts +127 -0
  472. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +108 -1
  473. package/src/runtime/routes/__tests__/default-provider-routes.test.ts +2 -2
  474. package/src/runtime/routes/__tests__/dictation-cleanup-guard.test.ts +107 -0
  475. package/src/runtime/routes/__tests__/mcp-add-default-risk.test.ts +16 -12
  476. package/src/runtime/routes/acp-claude-auth-routes.ts +11 -3
  477. package/src/runtime/routes/acp-routes.test.ts +15 -0
  478. package/src/runtime/routes/acp-routes.ts +72 -9
  479. package/src/runtime/routes/app-management-routes.ts +90 -2
  480. package/src/runtime/routes/app-routes.ts +1 -1
  481. package/src/runtime/routes/channel-send-routes.ts +85 -0
  482. package/src/runtime/routes/conversation-list-routes.ts +14 -3
  483. package/src/runtime/routes/conversation-query-routes.ts +40 -0
  484. package/src/runtime/routes/conversation-routes.ts +16 -3
  485. package/src/runtime/routes/credential-routes.ts +20 -14
  486. package/src/runtime/routes/desktop-setup-routes.test.ts +74 -0
  487. package/src/runtime/routes/desktop-setup-routes.ts +35 -0
  488. package/src/runtime/routes/diagnostics-routes.ts +61 -4
  489. package/src/runtime/routes/events-routes.ts +4 -0
  490. package/src/runtime/routes/host-cu-routes.ts +6 -0
  491. package/src/runtime/routes/index.ts +4 -2
  492. package/src/runtime/routes/log-export-routes.ts +2 -0
  493. package/src/runtime/routes/mcp-auth-routes.ts +39 -47
  494. package/src/runtime/routes/migration-routes.ts +6 -20
  495. package/src/runtime/routes/oauth-commands-routes.ts +50 -0
  496. package/src/runtime/routes/oauth-providers.ts +7 -0
  497. package/src/runtime/routes/platform-routes.ts +18 -15
  498. package/src/runtime/routes/workspace-routes.test.ts +55 -26
  499. package/src/runtime/routes/workspace-routes.ts +46 -63
  500. package/src/runtime/routes/workspace-tree-walk.test.ts +244 -0
  501. package/src/runtime/routes/workspace-tree-walk.ts +240 -0
  502. package/src/runtime/routes/workspace-utils.ts +17 -0
  503. package/src/runtime/services/__tests__/conversation-serializer.test.ts +28 -0
  504. package/src/runtime/services/conversation-serializer.ts +16 -3
  505. package/src/runtime/sync/resource-sync-events.ts +2 -1
  506. package/src/schedule/run-script.ts +2 -1
  507. package/src/schedule/schedule-store.ts +9 -2
  508. package/src/schedule/worker.ts +1 -1
  509. package/src/security/__tests__/ces-http-record-backend.test.ts +137 -0
  510. package/src/security/ces-http-record-backend.ts +120 -0
  511. package/src/security/ces-rpc-record-backend.ts +7 -0
  512. package/src/security/untrusted-content.ts +3 -1
  513. package/src/skills/__tests__/workspace-skill-md-signature.test.ts +60 -0
  514. package/src/skills/workspace-skill-md-signature.ts +50 -0
  515. package/src/subagent/consult-context.ts +12 -25
  516. package/src/tools/__tests__/side-effect-abort-guard.test.ts +1 -0
  517. package/src/tools/acp/list-agents.test.ts +3 -1
  518. package/src/tools/acp/spawn.test.ts +146 -11
  519. package/src/tools/acp/spawn.ts +21 -3
  520. package/src/tools/acp/status.test.ts +94 -0
  521. package/src/tools/acp/status.ts +40 -3
  522. package/src/tools/app-control/skill-proxy-bridge.ts +2 -1
  523. package/src/tools/browser/browser-execution.ts +1 -1
  524. package/src/tools/browser/cdp-client/accessibility-snapshot.ts +9 -3
  525. package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +2 -1
  526. package/src/tools/browser/cdp-client/extension-cdp-client.ts +3 -2
  527. package/src/tools/browser/network-recorder.ts +2 -1
  528. package/src/tools/capability-offer.test.ts +22 -2
  529. package/src/tools/capability-offer.ts +16 -1
  530. package/src/tools/computer-use/definitions.ts +121 -2
  531. package/src/tools/computer-use/skill-proxy-bridge.ts +2 -1
  532. package/src/tools/credentials/metadata-store.ts +100 -0
  533. package/src/tools/filesystem/search.ts +2 -1
  534. package/src/tools/host-filesystem/edit.test.ts +2 -1
  535. package/src/tools/host-filesystem/edit.ts +2 -2
  536. package/src/tools/host-filesystem/read.test.ts +2 -1
  537. package/src/tools/host-filesystem/read.ts +2 -2
  538. package/src/tools/host-filesystem/transfer.test.ts +2 -1
  539. package/src/tools/host-filesystem/transfer.ts +2 -2
  540. package/src/tools/host-filesystem/write.test.ts +2 -1
  541. package/src/tools/host-filesystem/write.ts +2 -2
  542. package/src/tools/host-terminal/host-shell.test.ts +16 -0
  543. package/src/tools/host-terminal/host-shell.ts +4 -20
  544. package/src/tools/network/web-search.ts +6 -1
  545. package/src/tools/policy-context.ts +6 -7
  546. package/src/tools/skills/scaffold-managed.ts +61 -3
  547. package/src/tools/subagent/spawn.ts +5 -8
  548. package/src/tools/terminal/__tests__/sanitized-bash.test.ts +57 -0
  549. package/src/tools/terminal/sanitized-bash.ts +105 -0
  550. package/src/tools/ui-surface/channel-variants.ts +10 -59
  551. package/src/tools/watch/watch-retro-report.ts +0 -8
  552. package/src/types/host-capabilities.ts +5 -3
  553. package/src/util/__tests__/language-subtag.test.ts +23 -1
  554. package/src/util/ca-bundle.ts +13 -0
  555. package/src/util/language-subtag.ts +16 -0
  556. package/src/util/platform.ts +8 -0
  557. package/src/util/unicode.ts +15 -1
  558. package/src/watch/watch-retro.ts +6 -8
  559. package/src/watch/watch-timeline.ts +2 -2
  560. package/src/workflows/run-manager.ts +2 -1
  561. package/src/workspace/git-porcelain.ts +66 -0
  562. package/src/workspace/git-service.ts +6 -55
  563. package/src/workspace/migrations/154-repair-retired-codex-gpt-5-4-model-ids.ts +258 -0
  564. package/src/workspace/migrations/155-move-front-model-config-to-voice.ts +78 -0
  565. package/src/workspace/migrations/156-extract-workspace-mcp-json.ts +214 -0
  566. package/src/workspace/migrations/__tests__/155-move-front-model-config-to-voice.test.ts +102 -0
  567. package/src/workspace/migrations/__tests__/156-extract-workspace-mcp-json.test.ts +199 -0
  568. package/src/workspace/migrations/registry.ts +6 -0
  569. package/src/__tests__/app-source-watcher.test.ts +0 -218
  570. package/src/__tests__/config-watcher-skill-reseed.test.ts +0 -359
  571. package/src/__tests__/speaker-identification.test.ts +0 -56
  572. package/src/calls/speaker-identification.ts +0 -233
  573. package/src/daemon/app-source-watcher.ts +0 -218
  574. package/src/messaging/providers/whatsapp/client.ts +0 -29
  575. package/src/runtime/routes/debug-bash-routes.ts +0 -173
  576. package/src/util/fs-watcher-error.ts +0 -36
  577. /package/src/{live-voice → calls}/progress-phrases.ts +0 -0
@@ -8,6 +8,7 @@
8
8
  */
9
9
 
10
10
  import { RiskLevel } from "../../permissions/types.js";
11
+ import { formatDesktopAppRequired } from "../capability-offer.js";
11
12
  import type {
12
13
  ToolContext,
13
14
  ToolDefinition,
@@ -31,7 +32,7 @@ function proxyExecute(toolName: string) {
31
32
  ): Promise<ToolExecutionResult> => {
32
33
  if (!context.proxyToolResolver) {
33
34
  return {
34
- content: `No proxy resolver configured for proxy tool "${toolName}". This tool requires a connected desktop client.`,
35
+ content: formatDesktopAppRequired("screen"),
35
36
  isError: true,
36
37
  };
37
38
  }
@@ -381,6 +382,112 @@ export const computerUseRunAppleScriptTool = {
381
382
  execute: proxyExecute("computer_use_run_applescript"),
382
383
  } satisfies ToolDefinition;
383
384
 
385
+ // ---------------------------------------------------------------------------
386
+ // sequence
387
+ // ---------------------------------------------------------------------------
388
+
389
+ export const computerUseSequenceTool = {
390
+ name: "computer_use_sequence",
391
+ description:
392
+ "Run several computer-use actions you already know, in order, in one step, for example open an app, press cmd+n, type a URL and press enter. Use it when no action depends on seeing the result of the one before; otherwise act one step at a time. Element IDs refer to the latest observation, so use them only for actions that act before the screen changes. Stops at the first action that is refused or fails and reports which one. Returns one observation after the last action.",
393
+ category: "computer-use",
394
+ defaultRiskLevel: RiskLevel.Low,
395
+ executionTarget: "host",
396
+ supportedClientOs: ["macos"],
397
+
398
+ input_schema: {
399
+ type: "object",
400
+ properties: {
401
+ actions: {
402
+ type: "array",
403
+ minItems: 1,
404
+ maxItems: 10,
405
+ description: "The actions to run, in order",
406
+ items: {
407
+ type: "object",
408
+ properties: {
409
+ action: {
410
+ type: "string",
411
+ enum: [
412
+ "key",
413
+ "type_text",
414
+ "type",
415
+ "click",
416
+ "double_click",
417
+ "right_click",
418
+ "scroll",
419
+ "wait",
420
+ "open_app",
421
+ ],
422
+ description: "The action to run",
423
+ },
424
+ key: {
425
+ type: "string",
426
+ description:
427
+ "key: key or shortcut to press (e.g. enter, tab, cmd+n)",
428
+ },
429
+ text: {
430
+ type: "string",
431
+ description: "type_text (or type): the text to type",
432
+ },
433
+ element_id: {
434
+ type: "integer",
435
+ description:
436
+ "click, double_click, right_click, scroll: the [ID] of the element from the latest accessibility tree (preferred)",
437
+ },
438
+ x: {
439
+ type: "integer",
440
+ description:
441
+ "click, double_click, right_click, scroll: X coordinate on screen (fallback when no element_id)",
442
+ },
443
+ y: {
444
+ type: "integer",
445
+ description:
446
+ "click, double_click, right_click, scroll: Y coordinate on screen (fallback when no element_id)",
447
+ },
448
+ direction: {
449
+ type: "string",
450
+ enum: ["up", "down", "left", "right"],
451
+ description: "scroll: scroll direction",
452
+ },
453
+ amount: {
454
+ type: "integer",
455
+ description: "scroll: scroll amount (1-10)",
456
+ },
457
+ duration_ms: {
458
+ type: "integer",
459
+ description: "wait: milliseconds to wait",
460
+ },
461
+ app_name: {
462
+ type: "string",
463
+ description:
464
+ 'open_app: the name of the application to open (e.g. "Google Chrome")',
465
+ },
466
+ reasoning: {
467
+ type: "string",
468
+ description: "Optional: why this action",
469
+ },
470
+ },
471
+ required: ["action"],
472
+ },
473
+ },
474
+ reasoning: {
475
+ type: "string",
476
+ description:
477
+ "Optional: what these actions do together. Reasoning on each action works too",
478
+ },
479
+ target_client_id: {
480
+ type: "string",
481
+ description:
482
+ "ID of the specific client to target. Required when multiple clients support host_cu; omit when only one is connected. Obtain IDs from `assistant clients list --capability host_cu`.",
483
+ },
484
+ },
485
+ required: ["actions"],
486
+ },
487
+
488
+ execute: proxyExecute("computer_use_sequence"),
489
+ } satisfies ToolDefinition;
490
+
384
491
  // ---------------------------------------------------------------------------
385
492
  // done
386
493
  // ---------------------------------------------------------------------------
@@ -456,7 +563,18 @@ export const computerUseObserveTool = {
456
563
  type: "integer",
457
564
  minimum: 1,
458
565
  maximum: 4294967295,
459
- description: "macOS only: capture this native CGWindowID instead of the desktop, including only its accessibility tree. Obtain a current native window ID first; do not guess or use a browser tab ID. Applies to this observation only, not subsequent actions. Requires a desktop helper with window-capture support. Screenshot coordinates are window-relative; use accessibility element IDs for later actions, not desktop scaling.",
566
+ description:
567
+ "macOS only: capture this native CGWindowID instead of the desktop, including only its accessibility tree. Obtain a current native window ID first; do not guess or use a browser tab ID. Applies to this observation only, not subsequent actions. Requires a desktop helper with window-capture support. Screenshot coordinates are window-relative; use accessibility element IDs for later actions, not desktop scaling.",
568
+ },
569
+ include_screenshot: {
570
+ type: "boolean",
571
+ description:
572
+ "Force a screenshot with this observation. The accessibility tree is returned every step and actions always return a screenshot of their result; ask for pixels on an observation whenever the tree is not enough to act on, such as a canvas, a game, a custom-drawn view, a window with few or unlabeled controls, or a question about layout.",
573
+ },
574
+ full_tree: {
575
+ type: "boolean",
576
+ description:
577
+ "Walk the accessibility tree to full depth. Observations list the tree to a limited depth and say when it was cut off; pass this when the element you need is not in the tree.",
460
578
  },
461
579
  target_client_id: {
462
580
  type: "string",
@@ -484,6 +602,7 @@ export const allComputerUseTools: ToolDefinition[] = [
484
602
  computerUseWaitTool,
485
603
  computerUseOpenAppTool,
486
604
  computerUseRunAppleScriptTool,
605
+ computerUseSequenceTool,
487
606
  computerUseDoneTool,
488
607
  computerUseRespondTool,
489
608
  ];
@@ -5,6 +5,7 @@
5
5
  * the proxy resolver, which forwards the call to the connected desktop client.
6
6
  */
7
7
 
8
+ import { formatDesktopAppRequired } from "../capability-offer.js";
8
9
  import { throwIfCancelled } from "../shared/abort.js";
9
10
  import type { ToolContext, ToolExecutionResult } from "../types.js";
10
11
 
@@ -45,7 +46,7 @@ export function forwardComputerUseProxyTool(
45
46
  }
46
47
  if (!context.proxyToolResolver) {
47
48
  return Promise.resolve({
48
- content: `Cannot execute ${toolName}: no proxy resolver available. This tool requires a connected desktop client.`,
49
+ content: formatDesktopAppRequired("screen"),
49
50
  isError: true,
50
51
  });
51
52
  }
@@ -20,6 +20,7 @@ import {
20
20
  } from "@vellumai/credential-storage";
21
21
  import type { CredentialRecord } from "@vellumai/service-contracts/credential-rpc";
22
22
 
23
+ import { createCesHttpRecordBackendIfConfigured } from "../../security/ces-http-record-backend.js";
23
24
  import type { CredentialRecordBackend } from "../../security/ces-rpc-record-backend.js";
24
25
  import { getLogger } from "../../util/logger.js";
25
26
  import type { CredentialInjectionTemplate } from "./policy-types.js";
@@ -55,6 +56,11 @@ const CES_MEMORY_PATH = "ces-in-memory-credential-metadata";
55
56
  let _store: StaticCredentialMetadataStore | undefined;
56
57
  let _overridePath: string | null = null;
57
58
  let _recordBackend: CredentialRecordBackend | undefined;
59
+ /**
60
+ * Test override for the CES HTTP listing fallback. `undefined` uses the
61
+ * production factory. `null` disables HTTP fallback.
62
+ */
63
+ let _httpRecordBackendOverride: CredentialRecordBackend | null | undefined;
58
64
  let _cesRecordsLoaded = false;
59
65
  let _cesLoad: Promise<void> | undefined;
60
66
  let _cesLoadGeneration = 0;
@@ -255,6 +261,93 @@ export function listCredentialMetadata(): CredentialMetadata[] {
255
261
  return getStore().list() as CredentialMetadata[];
256
262
  }
257
263
 
264
+ export type CredentialRecordListResult = {
265
+ records: CredentialMetadata[];
266
+ unreachable: boolean;
267
+ };
268
+
269
+ type ListedCredentialRecords = Array<{
270
+ account: string;
271
+ record: CredentialRecord;
272
+ }>;
273
+
274
+ function backendIsAvailable(backend: CredentialRecordBackend): boolean {
275
+ try {
276
+ return backend.isAvailable();
277
+ } catch {
278
+ return false;
279
+ }
280
+ }
281
+
282
+ async function listFromRecordBackend(
283
+ backend: CredentialRecordBackend,
284
+ ): Promise<ListedCredentialRecords | null> {
285
+ if (!backendIsAvailable(backend)) {
286
+ return null;
287
+ }
288
+ return backend.list();
289
+ }
290
+
291
+ function resolveHttpRecordBackend(): CredentialRecordBackend | null {
292
+ if (_httpRecordBackendOverride !== undefined) {
293
+ return _httpRecordBackendOverride;
294
+ }
295
+ return createCesHttpRecordBackendIfConfigured();
296
+ }
297
+
298
+ async function listFromHttpFallback(): Promise<ListedCredentialRecords | null> {
299
+ const httpBackend = resolveHttpRecordBackend();
300
+ if (!httpBackend) {
301
+ return null;
302
+ }
303
+ return listFromRecordBackend(httpBackend);
304
+ }
305
+
306
+ /**
307
+ * Live catalog from the credential record backend. Does not read the
308
+ * daemon in-process cache, so an outage cannot be mistaken for an empty
309
+ * vault. `unreachable` is true when the backend is missing, down, or
310
+ * the list RPC fails, and CES HTTP failover is also unavailable.
311
+ *
312
+ * When the attached backend is CES RPC (or none is attached), listing
313
+ * fails over to CES HTTP in containerized deployments so a down RPC
314
+ * socket does not hide a healthy vault sidecar. A successful empty
315
+ * catalog (`[]`) does not fall through to HTTP.
316
+ *
317
+ * The file-backed test store is the catalog when a test override path is set.
318
+ */
319
+ export async function listCredentialRecordsLive(): Promise<CredentialRecordListResult> {
320
+ if (_overridePath) {
321
+ return {
322
+ records: getStore().list() as CredentialMetadata[],
323
+ unreachable: false,
324
+ };
325
+ }
326
+ if (_recordBackend) {
327
+ const remote = await listFromRecordBackend(_recordBackend);
328
+ if (remote !== null) {
329
+ return {
330
+ records: remote.map((entry) => entry.record as CredentialMetadata),
331
+ unreachable: false,
332
+ };
333
+ }
334
+ if (_recordBackend.name !== "ces-rpc") {
335
+ return { records: [], unreachable: true };
336
+ }
337
+ }
338
+ const httpRemote = await listFromHttpFallback();
339
+ if (httpRemote !== null) {
340
+ log.warn(
341
+ "CES RPC credential record list unavailable. Failing over to CES HTTP.",
342
+ );
343
+ return {
344
+ records: httpRemote.map((entry) => entry.record as CredentialMetadata),
345
+ unreachable: false,
346
+ };
347
+ }
348
+ return { records: [], unreachable: true };
349
+ }
350
+
258
351
  /**
259
352
  * Delete metadata for a credential.
260
353
  */
@@ -287,3 +380,10 @@ export function _setMetadataPath(path: string | null): void {
287
380
  }
288
381
  }
289
382
  }
383
+
384
+ /** @internal Test-only: override the CES HTTP listing fallback. */
385
+ export function _setHttpRecordBackendForTests(
386
+ backend: CredentialRecordBackend | null | undefined,
387
+ ): void {
388
+ _httpRecordBackendOverride = backend;
389
+ }
@@ -5,6 +5,7 @@ import { minimatch } from "minimatch";
5
5
  import { RE2JS } from "re2js";
6
6
 
7
7
  import { RiskLevel } from "../../permissions/types.js";
8
+ import { safeStringSlice } from "../../util/unicode.js";
8
9
  import { declareDaemonActivityField } from "../schema-transforms.js";
9
10
  import {
10
11
  isDeniedBasename,
@@ -246,7 +247,7 @@ export const codeSearchTool = {
246
247
  // emitting a multi-megabyte output line. Normal lines pass through verbatim.
247
248
  const truncateForDisplay = (text: string): string =>
248
249
  text.length > MAX_DISPLAY_LINE_LENGTH
249
- ? `${text.slice(0, MAX_DISPLAY_LINE_LENGTH)} …[line truncated]`
250
+ ? `${safeStringSlice(text, 0, MAX_DISPLAY_LINE_LENGTH)} …[line truncated]`
250
251
  : text;
251
252
 
252
253
  // Append an output line, tracking its byte cost. Returns false once the
@@ -71,8 +71,9 @@ describe("host_file_edit cross-client guards", () => {
71
71
  );
72
72
  expect(result.isError).toBe(true);
73
73
  expect(result.content).toContain(
74
- "no client with host_file capability is connected",
74
+ "The Vellum desktop app is needed to access files on your computer",
75
75
  );
76
+ expect(result.content).toContain("https://www.vellum.ai/downloads");
76
77
  });
77
78
 
78
79
  test("returns 'specified client disconnected' error when targetClientId set but proxy unavailable on web", async () => {
@@ -4,7 +4,7 @@ import { supportsHostProxy } from "../../channels/types.js";
4
4
  import { HostFileProxy } from "../../daemon/host-file-proxy.js";
5
5
  import { RiskLevel } from "../../permissions/types.js";
6
6
  import { assistantEventHub } from "../../runtime/assistant-event-hub.js";
7
- import { desktopClientName } from "../client-os.js";
7
+ import { formatDesktopAppRequired } from "../capability-offer.js";
8
8
  import { FileSystemOps } from "../shared/filesystem/file-ops-service.js";
9
9
  import { formatEditDiff } from "../shared/filesystem/format-diff.js";
10
10
  import { hostPolicy } from "../shared/filesystem/path-policy.js";
@@ -115,7 +115,7 @@ export const hostFileEditTool = {
115
115
  !HostFileProxy.instance.isAvailable()
116
116
  ) {
117
117
  return {
118
- content: `Error: no client with host_file capability is connected. Connect a ${desktopClientName(context)} client to use host_file from a non-desktop interface.`,
118
+ content: formatDesktopAppRequired("files"),
119
119
  isError: true,
120
120
  };
121
121
  }
@@ -67,8 +67,9 @@ describe("host_file_read cross-client guards", () => {
67
67
  );
68
68
  expect(result.isError).toBe(true);
69
69
  expect(result.content).toContain(
70
- "no client with host_file capability is connected",
70
+ "The Vellum desktop app is needed to access files on your computer",
71
71
  );
72
+ expect(result.content).toContain("https://www.vellum.ai/downloads");
72
73
  });
73
74
 
74
75
  test("returns 'specified client disconnected' error when targetClientId set but proxy unavailable on web", async () => {
@@ -6,7 +6,7 @@ import { supportsHostProxy } from "../../channels/types.js";
6
6
  import { HostFileProxy } from "../../daemon/host-file-proxy.js";
7
7
  import { RiskLevel } from "../../permissions/types.js";
8
8
  import { assistantEventHub } from "../../runtime/assistant-event-hub.js";
9
- import { desktopClientName } from "../client-os.js";
9
+ import { formatDesktopAppRequired } from "../capability-offer.js";
10
10
  import {
11
11
  AUDIO_EXTENSIONS,
12
12
  readAudioFile,
@@ -121,7 +121,7 @@ export const hostFileReadTool = {
121
121
  !HostFileProxy.instance.isAvailable()
122
122
  ) {
123
123
  return {
124
- content: `Error: no client with host_file capability is connected. Connect a ${desktopClientName(context)} client to use host_file from a non-desktop interface.`,
124
+ content: formatDesktopAppRequired("files"),
125
125
  isError: true,
126
126
  };
127
127
  }
@@ -335,8 +335,9 @@ describe("host_file_transfer cross-client guards", () => {
335
335
 
336
336
  expect(result.isError).toBe(true);
337
337
  expect(result.content).toContain(
338
- "no client with host_file capability is connected",
338
+ "The Vellum desktop app is needed to access files on your computer",
339
339
  );
340
+ expect(result.content).toContain("https://www.vellum.ai/downloads");
340
341
  expect(toSandboxCalls.length).toBe(0);
341
342
  });
342
343
 
@@ -6,7 +6,7 @@ import { supportsHostProxy } from "../../channels/types.js";
6
6
  import { HostTransferProxy } from "../../daemon/host-transfer-proxy.js";
7
7
  import { RiskLevel } from "../../permissions/types.js";
8
8
  import { assistantEventHub } from "../../runtime/assistant-event-hub.js";
9
- import { desktopClientName } from "../client-os.js";
9
+ import { formatDesktopAppRequired } from "../capability-offer.js";
10
10
  import { declareDaemonActivityField } from "../schema-transforms.js";
11
11
  import { sandboxPolicy } from "../shared/filesystem/path-policy.js";
12
12
  import type {
@@ -126,7 +126,7 @@ export const hostFileTransferTool = {
126
126
  !HostTransferProxy.instance.isAvailable()
127
127
  ) {
128
128
  return {
129
- content: `Error: no client with host_file capability is connected. Connect a ${desktopClientName(context)} client to use host_file from a non-desktop interface.`,
129
+ content: formatDesktopAppRequired("files"),
130
130
  isError: true,
131
131
  };
132
132
  }
@@ -67,8 +67,9 @@ describe("host_file_write cross-client guards", () => {
67
67
  );
68
68
  expect(result.isError).toBe(true);
69
69
  expect(result.content).toContain(
70
- "no client with host_file capability is connected",
70
+ "The Vellum desktop app is needed to access files on your computer",
71
71
  );
72
+ expect(result.content).toContain("https://www.vellum.ai/downloads");
72
73
  });
73
74
 
74
75
  test("returns 'specified client disconnected' error when targetClientId set but proxy unavailable on web", async () => {
@@ -4,7 +4,7 @@ import { supportsHostProxy } from "../../channels/types.js";
4
4
  import { HostFileProxy } from "../../daemon/host-file-proxy.js";
5
5
  import { RiskLevel } from "../../permissions/types.js";
6
6
  import { assistantEventHub } from "../../runtime/assistant-event-hub.js";
7
- import { desktopClientName } from "../client-os.js";
7
+ import { formatDesktopAppRequired } from "../capability-offer.js";
8
8
  import { FileSystemOps } from "../shared/filesystem/file-ops-service.js";
9
9
  import { formatWriteSummary } from "../shared/filesystem/format-diff.js";
10
10
  import { hostPolicy } from "../shared/filesystem/path-policy.js";
@@ -90,7 +90,7 @@ export const hostFileWriteTool = {
90
90
  !HostFileProxy.instance.isAvailable()
91
91
  ) {
92
92
  return {
93
- content: `Error: no client with host_file capability is connected. Connect a ${desktopClientName(context)} client to use host_file from a non-desktop interface.`,
93
+ content: formatDesktopAppRequired("files"),
94
94
  isError: true,
95
95
  };
96
96
  }
@@ -170,6 +170,22 @@ afterEach(() => {
170
170
  mockProxyAvailable = false;
171
171
  });
172
172
 
173
+ describe("host_bash unavailable desktop-app recovery", () => {
174
+ test("offers the desktop app download when a web turn has no host connection", async () => {
175
+ const result = await hostShellTool.execute(
176
+ { command: "pwd" },
177
+ makeContext({ transportInterface: "web" }),
178
+ );
179
+
180
+ expect(result.isError).toBe(true);
181
+ expect(result.content).toContain(
182
+ "The Vellum desktop app is needed to run commands on your computer",
183
+ );
184
+ expect(result.content).toContain("https://www.vellum.ai/downloads");
185
+ expect(result.content).toContain("Open it, then retry.");
186
+ });
187
+ });
188
+
173
189
  // ---------------------------------------------------------------------------
174
190
  // Proxy path — background: true
175
191
  // ---------------------------------------------------------------------------
@@ -18,10 +18,7 @@ import { getConfig } from "../../config/loader.js";
18
18
  import { HostBashProxy } from "../../daemon/host-bash-proxy.js";
19
19
  import { RiskLevel } from "../../permissions/types.js";
20
20
  import { wakeAgentForOpportunity } from "../../runtime/agent-wake.js";
21
- import {
22
- assistantEventHub,
23
- broadcastMessage,
24
- } from "../../runtime/assistant-event-hub.js";
21
+ import { broadcastMessage } from "../../runtime/assistant-event-hub.js";
25
22
  import { conversationRevealNonce } from "../../runtime/reveal-nonce.js";
26
23
  import { redactSecrets } from "../../security/secret-scanner.js";
27
24
  import {
@@ -39,7 +36,7 @@ import {
39
36
  registerBackgroundTool,
40
37
  removeBackgroundTool,
41
38
  } from "../background-tool-registry.js";
42
- import { desktopClientName } from "../client-os.js";
39
+ import { formatDesktopAppRequired } from "../capability-offer.js";
43
40
  import {
44
41
  attachBoundedStdio,
45
42
  MAX_OUTPUT_LENGTH,
@@ -142,7 +139,7 @@ export const hostShellInputSchema = z.looseObject({
142
139
  target_client_id: z
143
140
  .string()
144
141
  .describe(
145
- "ID of the specific client to execute this command on. Required when multiple clients support host_bash; omit when only one client is connected. Obtain IDs from `assistant clients list --capability host_bash`.",
142
+ "Optional ID of the specific client to execute this command on. Without it, the most recently active eligible client is used. Obtain IDs from `assistant clients list --capability host_bash`.",
146
143
  )
147
144
  .optional()
148
145
  .catch(undefined),
@@ -203,20 +200,7 @@ export const hostShellTool = {
203
200
  const config = getConfig();
204
201
  const { shellDefaultTimeoutSec, shellMaxTimeoutSec } = config.timeouts;
205
202
 
206
- // Guard: non-host-proxy interfaces need an explicit target when multiple
207
- // capable clients are connected to avoid ambiguous untargeted broadcasts.
208
203
  const transportInterface = context.transportInterface;
209
- if (
210
- targetClientId == null &&
211
- transportInterface != null &&
212
- !supportsHostProxy(transportInterface) &&
213
- assistantEventHub.listClientsByCapability("host_bash").length > 1
214
- ) {
215
- return {
216
- content: `Error: multiple clients support host_bash. Specify which client to use with \`target_client_id\`. Run \`assistant clients list --capability host_bash\` to see client IDs and labels.`,
217
- isError: true,
218
- };
219
- }
220
204
 
221
205
  // Guard: non-host-proxy interfaces with no capable clients connected.
222
206
  if (
@@ -226,7 +210,7 @@ export const hostShellTool = {
226
210
  !HostBashProxy.instance.isAvailable()
227
211
  ) {
228
212
  return {
229
- content: `Error: no client with host_bash capability is connected. Connect a ${desktopClientName(context)} client to use host_bash from a non-desktop interface.`,
213
+ content: formatDesktopAppRequired("shell"),
230
214
  isError: true,
231
215
  };
232
216
  }
@@ -15,6 +15,7 @@ import {
15
15
  getHttpRetryDelay,
16
16
  sleep,
17
17
  } from "../../util/retry.js";
18
+ import { safeStringSlice } from "../../util/unicode.js";
18
19
  import type {
19
20
  ToolContext,
20
21
  ToolDefinition,
@@ -509,7 +510,11 @@ const KEENABLE_SNIPPET_MAX_LENGTH = 500;
509
510
  * present rather than nullish. */
510
511
  function keenableSnippet(result: KeenableSearchResult): string {
511
512
  const text = result.snippet || result.description || "";
512
- return text.replace(/\s+/g, " ").trim().slice(0, KEENABLE_SNIPPET_MAX_LENGTH);
513
+ return safeStringSlice(
514
+ text.replace(/\s+/g, " ").trim(),
515
+ 0,
516
+ KEENABLE_SNIPPET_MAX_LENGTH,
517
+ );
513
518
  }
514
519
 
515
520
  function formatKeenableResults(
@@ -1,5 +1,5 @@
1
1
  import { getConfig } from "../config/loader.js";
2
- import { isV3TierActive } from "../config/memory-v3-gate.js";
2
+ import { isSkillImprovementActive } from "../config/memory-v3-gate.js";
3
3
  import type { ExecutionContext } from "../permissions/approval-policy.js";
4
4
  import type { ChannelPermissionCoordinates } from "../permissions/channel-permission-query.js";
5
5
  import type { PolicyContext } from "../permissions/types.js";
@@ -63,12 +63,11 @@ export function buildPolicyContext(
63
63
  requestOrigin: context?.requestOrigin,
64
64
  ...channelCoordinatesFromToolContext(context),
65
65
  requesterContactId: context?.requesterContactId,
66
- // Precompute the proc-to-skills gate the v3 tier being active, i.e.
67
- // memory on AND v3 live here so the permission checker, a leaf module
68
- // that must not read config, can deny the memory-retrospective
69
- // skill-authoring grant whenever the feature is inactive just by reading
70
- // this boolean.
71
- procToSkillsActive: isV3TierActive(getConfig()),
66
+ // Precompute the retrospective skill-improvement gate here so the
67
+ // permission checker, a leaf module that must not read config, can deny
68
+ // the memory-retrospective skill-authoring grant whenever the feature is
69
+ // inactive just by reading this boolean.
70
+ procToSkillsActive: isSkillImprovementActive(getConfig()),
72
71
  };
73
72
 
74
73
  const ownerKind = getToolOwner(tool.name)?.kind;