@vellumai/assistant 0.12.1-dev.202609141913.5e16607 → 0.12.1-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 (439) hide show
  1. package/AGENTS.md +0 -4
  2. package/ARCHITECTURE.md +12 -11
  3. package/docs/architecture/turn-actor.md +8 -15
  4. package/docs/guardian-request-flow.md +0 -16
  5. package/openapi.yaml +49 -291
  6. package/package.json +1 -1
  7. package/src/__tests__/always-loaded-tools-guard.test.ts +10 -18
  8. package/src/__tests__/app-compiler.test.ts +0 -6
  9. package/src/__tests__/app-control-tool-schemas.test.ts +2 -5
  10. package/src/__tests__/app-dir-path-guard.test.ts +3 -1
  11. package/src/__tests__/app-source-watcher.test.ts +218 -0
  12. package/src/{calls/__tests__ → __tests__}/call-controller.test.ts +82 -503
  13. package/src/__tests__/computer-use-skill-proxy-bridge.test.ts +2 -5
  14. package/src/__tests__/computer-use-tools.test.ts +5 -17
  15. package/src/__tests__/config-schema-cmd.test.ts +2 -2
  16. package/src/__tests__/config-schema.test.ts +29 -28
  17. package/src/__tests__/config-watcher-skill-reseed.test.ts +359 -0
  18. package/src/__tests__/config-watcher.test.ts +8 -23
  19. package/src/__tests__/connection-model-compat.test.ts +2 -2
  20. package/src/__tests__/conversation-key-store-disk-view.test.ts +1 -1
  21. package/src/__tests__/conversation-pairing.test.ts +0 -26
  22. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +10 -4
  23. package/src/__tests__/conversation-queue.test.ts +207 -48
  24. package/src/__tests__/conversation-retry-discard.test.ts +1 -54
  25. package/src/__tests__/conversation-skill-tools.test.ts +0 -27
  26. package/src/__tests__/conversation-surfaces-app-control.test.ts +3 -8
  27. package/src/__tests__/conversation-surfaces-task-progress.test.ts +0 -67
  28. package/src/__tests__/conversation-title-service.test.ts +10 -14
  29. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +5 -3
  30. package/src/__tests__/conversation-tool-setup-attribution.test.ts +0 -51
  31. package/src/__tests__/credential-record-retire.test.ts +1 -171
  32. package/src/__tests__/credential-routes.test.ts +3 -212
  33. package/src/__tests__/credential-security-invariants.test.ts +2 -4
  34. package/src/__tests__/cu-unified-flow.test.ts +2 -8
  35. package/src/__tests__/document-sync-tags.test.ts +0 -25
  36. package/src/__tests__/drain-kick-guard.test.ts +2 -0
  37. package/src/__tests__/drain-requeue-on-contention.test.ts +6 -0
  38. package/src/__tests__/evict-conversations-for-reload.test.ts +1 -1
  39. package/src/__tests__/host-bash-proxy.test.ts +45 -26
  40. package/src/__tests__/host-browser-proxy.test.ts +17 -6
  41. package/src/__tests__/host-file-proxy-targeted.test.ts +0 -23
  42. package/src/__tests__/host-file-proxy.test.ts +23 -11
  43. package/src/__tests__/host-transfer-proxy-targeted.test.ts +9 -30
  44. package/src/__tests__/llm-catalog-parity.test.ts +0 -25
  45. package/src/__tests__/mcp-auth-routes.test.ts +9 -6
  46. package/src/__tests__/mcp-config-secret-boundary.test.ts +136 -78
  47. package/src/__tests__/mcp-health-check.test.ts +11 -8
  48. package/src/__tests__/mcp-list-plugin-servers.test.ts +10 -8
  49. package/src/{calls/__tests__ → __tests__}/media-stream-audio-transcode.test.ts +1 -1
  50. package/src/{calls/__tests__ → __tests__}/media-stream-output.test.ts +8 -37
  51. package/src/{calls/__tests__ → __tests__}/media-stream-parser.test.ts +1 -1
  52. package/src/{calls/__tests__ → __tests__}/media-stream-server-integration.test.ts +57 -150
  53. package/src/{calls/__tests__ → __tests__}/media-stream-stt-session.test.ts +5 -5
  54. package/src/{calls/__tests__ → __tests__}/media-turn-detector.test.ts +1 -1
  55. package/src/__tests__/messages-after-tiebreaker.test.ts +0 -91
  56. package/src/__tests__/messages-read-boundary-guard.test.ts +1 -1
  57. package/src/__tests__/messaging-send-tool.test.ts +548 -355
  58. package/src/__tests__/model-intents.test.ts +0 -3
  59. package/src/__tests__/notification-vellum-adapter.test.ts +2 -85
  60. package/src/__tests__/outlook-messaging-provider.test.ts +6 -6
  61. package/src/__tests__/platform-callback-registration.test.ts +16 -8
  62. package/src/__tests__/platform-client-verify-credential.test.ts +0 -2
  63. package/src/__tests__/plugin-import-boundary-guard.test.ts +0 -13
  64. package/src/__tests__/script-proxy-certs.test.ts +0 -35
  65. package/src/__tests__/skill-feature-flags-integration.test.ts +1 -1
  66. package/src/__tests__/skill-feature-flags.test.ts +2 -2
  67. package/src/__tests__/speaker-identification.test.ts +56 -0
  68. package/src/__tests__/subagent-tool-gate-mode.test.ts +3 -19
  69. package/src/__tests__/subagent-tools.test.ts +2 -3
  70. package/src/__tests__/telephony-tts-guard.test.ts +3 -38
  71. package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +0 -4
  72. package/src/{calls/__tests__/voice-session-bridge-turn-flow.test.ts → __tests__/voice-session-bridge.test.ts} +12 -20
  73. package/src/__tests__/watch-retro-tool-availability.test.ts +17 -8
  74. package/src/__tests__/workspace-git-service.test.ts +0 -26
  75. package/src/acp/__tests__/agent-process.test.ts +4 -258
  76. package/src/acp/__tests__/auth-required.test.ts +0 -96
  77. package/src/acp/__tests__/client-handler.test.ts +2 -90
  78. package/src/acp/__tests__/session-manager-persistence.test.ts +1 -5
  79. package/src/acp/__tests__/session-manager-resume.test.ts +6 -270
  80. package/src/acp/__tests__/session-manager-usage.test.ts +2 -4
  81. package/src/acp/__tests__/session-manager.test.ts +4 -895
  82. package/src/acp/agent-process.test.ts +1 -4
  83. package/src/acp/agent-process.ts +9 -94
  84. package/src/acp/auth-required.ts +1 -50
  85. package/src/acp/auto-install.test.ts +7 -9
  86. package/src/acp/client-handler.ts +3 -16
  87. package/src/acp/resolve-agent.test.ts +6 -220
  88. package/src/acp/resolve-agent.ts +23 -88
  89. package/src/acp/session-manager.test.ts +3 -196
  90. package/src/acp/session-manager.ts +18 -441
  91. package/src/acp/types.ts +5 -50
  92. package/src/api/events/app-files-changed.ts +3 -4
  93. package/src/api/events/notification-intent.ts +0 -2
  94. package/src/api/index.ts +0 -6
  95. package/src/apps/app-store.ts +1 -1
  96. package/src/bundler/app-compiler.ts +0 -15
  97. package/src/calls/__tests__/voice-control-protocol.test.ts +25 -58
  98. package/src/calls/__tests__/voice-session-bridge.test.ts +1 -37
  99. package/src/calls/__tests__/voice-triage-escalate.test.ts +0 -105
  100. package/src/calls/call-constants.ts +4 -24
  101. package/src/calls/call-controller.ts +199 -499
  102. package/src/calls/call-transport.ts +0 -16
  103. package/src/calls/media-stream-output.ts +0 -32
  104. package/src/calls/media-stream-server.ts +30 -82
  105. package/src/calls/media-stream-stt-session.ts +0 -15
  106. package/src/calls/resolve-call-tts-provider.ts +2 -33
  107. package/src/calls/speaker-identification.ts +233 -0
  108. package/src/calls/tts-call-strategy.ts +5 -9
  109. package/src/calls/types.ts +0 -2
  110. package/src/calls/voice-control-protocol.ts +21 -53
  111. package/src/calls/voice-session-bridge.ts +46 -59
  112. package/src/calls/voice-triage-escalate.ts +56 -175
  113. package/src/cli/commands/apps.help.ts +13 -67
  114. package/src/cli/commands/apps.ts +0 -183
  115. package/src/cli/commands/bash.help.ts +13 -8
  116. package/src/cli/commands/bash.ts +25 -7
  117. package/src/cli/commands/channels/index.help.ts +2 -70
  118. package/src/cli/commands/channels/index.ts +3 -16
  119. package/src/cli/commands/credentials.help.ts +2 -6
  120. package/src/cli/commands/credentials.ts +2 -2
  121. package/src/cli/commands/mcp.help.ts +8 -8
  122. package/src/cli/commands/mcp.ts +1 -1
  123. package/src/cli/commands/roadmap.help.ts +1 -5
  124. package/src/cli/commands/roadmap.ts +1 -1
  125. package/src/cli/lib/bundled-marketplace.json +90 -103
  126. package/src/config/__tests__/default-profile-catalog.test.ts +0 -22
  127. package/src/config/__tests__/memory-retrospective-schema.test.ts +1 -15
  128. package/src/config/__tests__/platform-rehydration.test.ts +9 -99
  129. package/src/config/acp-defaults.test.ts +3 -13
  130. package/src/config/acp-defaults.ts +6 -23
  131. package/src/config/acp-schema.ts +2 -30
  132. package/src/config/bundled-skills/acp/SKILL.md +14 -22
  133. package/src/config/bundled-skills/acp/TOOLS.json +1 -5
  134. package/src/config/bundled-skills/app-builder/SKILL.md +9 -12
  135. package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +1 -1
  136. package/src/config/bundled-skills/messaging/SKILL.md +3 -3
  137. package/src/config/bundled-skills/messaging/TOOLS.json +3 -3
  138. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +139 -67
  139. package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +1 -1
  140. package/src/config/bundled-skills/schedule/SKILL.md +4 -3
  141. package/src/config/bundled-skills/screen-annotation/SKILL.md +1 -8
  142. package/src/config/bundled-skills/screen-annotation/tools/screen-annotation.test.ts +1 -4
  143. package/src/config/bundled-skills/skill-management/TOOLS.json +2 -2
  144. package/src/config/default-profile-catalog.ts +1 -1
  145. package/src/config/env.ts +2 -2
  146. package/src/config/feature-flag-registry.json +12 -24
  147. package/src/config/loader.ts +0 -1
  148. package/src/config/memory-v3-gate.test.ts +0 -31
  149. package/src/config/memory-v3-gate.ts +16 -20
  150. package/src/config/platform-rehydration.ts +27 -11
  151. package/src/config/schema.ts +2 -2
  152. package/src/config/schemas/__tests__/live-voice.test.ts +158 -8
  153. package/src/config/schemas/calls.ts +28 -1
  154. package/src/config/schemas/live-voice.ts +173 -1
  155. package/src/config/schemas/mcp.ts +1 -1
  156. package/src/config/schemas/memory-retrospective.ts +0 -10
  157. package/src/config/schemas/services.ts +0 -8
  158. package/src/config/schemas/skills.ts +14 -0
  159. package/src/credential-execution/process-manager.test.ts +19 -18
  160. package/src/daemon/__tests__/conversation-tool-setup.test.ts +121 -66
  161. package/src/daemon/__tests__/plugin-mcp-reconcile.test.ts +1 -4
  162. package/src/daemon/app-source-watcher.ts +218 -0
  163. package/src/daemon/config-watcher.ts +194 -13
  164. package/src/daemon/conversation-process.ts +64 -35
  165. package/src/daemon/conversation-skill-tools.ts +31 -13
  166. package/src/daemon/conversation-store.ts +1 -1
  167. package/src/daemon/conversation-surfaces.ts +8 -10
  168. package/src/daemon/conversation-tool-setup.ts +43 -37
  169. package/src/daemon/conversation.ts +3 -0
  170. package/src/daemon/host-bash-proxy.ts +12 -8
  171. package/src/daemon/host-browser-proxy.ts +1 -15
  172. package/src/daemon/host-file-proxy.ts +10 -8
  173. package/src/daemon/host-transfer-proxy.ts +18 -14
  174. package/src/daemon/lifecycle.ts +5 -0
  175. package/src/daemon/mcp-reload-service.ts +5 -5
  176. package/src/daemon/message-types/sync.ts +0 -1
  177. package/src/daemon/orphan-reaper.ts +1 -1
  178. package/src/daemon/providers-setup.ts +1 -1
  179. package/src/daemon/shutdown-handlers.ts +9 -1
  180. package/src/daemon/skill-memory-refresh.ts +0 -64
  181. package/src/daemon/tool-side-effects.ts +18 -7
  182. package/src/home/__tests__/feed-writer.test.ts +0 -22
  183. package/src/home/feed-writer.ts +2 -11
  184. package/src/hooks/types.ts +0 -33
  185. package/src/inbound/platform-callback-registration.ts +5 -4
  186. package/src/live-voice/__tests__/live-voice-metrics.test.ts +3 -0
  187. package/src/live-voice/__tests__/live-voice-progress.test.ts +15 -15
  188. package/src/live-voice/__tests__/live-voice-vad.test.ts +2 -2
  189. package/src/{calls → live-voice}/__tests__/progress-narration.test.ts +4 -4
  190. package/src/{calls → live-voice}/__tests__/progress-phrases.test.ts +1 -1
  191. package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +69 -13
  192. package/src/live-voice/live-voice-session.ts +693 -213
  193. package/src/{calls → live-voice}/progress-narration.ts +2 -2
  194. package/src/live-voice/protocol.ts +2 -0
  195. package/src/mcp/__tests__/effective-config.test.ts +2 -2
  196. package/src/mcp/effective-config.ts +3 -3
  197. package/src/mcp/mcp-header-store.ts +45 -29
  198. package/src/mcp/startup.ts +4 -8
  199. package/src/messaging/provider.ts +3 -10
  200. package/src/messaging/providers/callback-routing.ts +0 -18
  201. package/src/messaging/providers/channel-transport.ts +0 -56
  202. package/src/messaging/providers/discord/transport.ts +0 -17
  203. package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +17 -6
  204. package/src/messaging/providers/slack/adapter.ts +44 -7
  205. package/src/messaging/providers/slack/transport.ts +0 -16
  206. package/src/messaging/providers/telegram-bot/adapter.ts +48 -1
  207. package/src/messaging/providers/telegram-bot/client.ts +26 -0
  208. package/src/messaging/providers/telegram-bot/transport.ts +0 -20
  209. package/src/messaging/providers/whatsapp/adapter.ts +48 -1
  210. package/src/messaging/providers/whatsapp/client.ts +29 -0
  211. package/src/messaging/providers/whatsapp/transport.ts +0 -18
  212. package/src/monitoring/__tests__/workspace-git-heartbeat.test.ts +0 -8
  213. package/src/notifications/__tests__/assistant-reply-producer.test.ts +0 -55
  214. package/src/notifications/__tests__/guardian-feed-projection.test.ts +2 -158
  215. package/src/notifications/__tests__/home-feed-side-effect.test.ts +3 -153
  216. package/src/notifications/__tests__/schedule-result-producer.test.ts +3 -99
  217. package/src/notifications/adapters/macos.ts +0 -3
  218. package/src/notifications/assistant-reply-producer.ts +3 -8
  219. package/src/notifications/conversation-pairing.ts +16 -36
  220. package/src/notifications/delivered-post-record.ts +1 -11
  221. package/src/notifications/guardian-feed-projection.ts +5 -125
  222. package/src/notifications/home-feed-side-effect.ts +7 -55
  223. package/src/notifications/schedule-result-producer.ts +22 -66
  224. package/src/oauth/__tests__/seed-providers-managed.test.ts +0 -54
  225. package/src/oauth/byo-connection.test.ts +0 -45
  226. package/src/oauth/byo-connection.ts +6 -36
  227. package/src/oauth/connection-resolver.test.ts +0 -73
  228. package/src/oauth/connection-resolver.ts +2 -95
  229. package/src/oauth/seed-providers.ts +0 -165
  230. package/src/outbound-proxy/certs.ts +5 -3
  231. package/src/permissions/checker.test.ts +6 -11
  232. package/src/persistence/compaction-log-store-clickhouse.ts +3 -3
  233. package/src/persistence/conversation-crud.ts +63 -71
  234. package/src/persistence/conversation-lineage.ts +4 -12
  235. package/src/persistence/conversation-title-service.ts +26 -9
  236. package/src/persistence/llm-request-log-sink-clickhouse.ts +3 -3
  237. package/src/persistence/llm-request-log-source-clickhouse.ts +3 -3
  238. package/src/persistence/message-reads.ts +0 -30
  239. package/src/persistence/migrations/157-invite-contact-id.ts +6 -18
  240. package/src/persistence/schema/memory-core.ts +0 -5
  241. package/src/platform/client.test.ts +2 -4
  242. package/src/platform/client.ts +10 -2
  243. package/src/plugin-api/constants.ts +0 -2
  244. package/src/plugin-api/index.ts +0 -1
  245. package/src/plugin-api/types.ts +0 -2
  246. package/src/plugins/defaults/index.ts +0 -2
  247. package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +0 -46
  248. package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +5 -9
  249. package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +10 -20
  250. package/src/plugins/defaults/memory/__tests__/memory-retrospective-state.test.ts +0 -219
  251. package/src/plugins/defaults/memory/__tests__/memory-retrospective-sweep.test.ts +0 -31
  252. package/src/plugins/defaults/memory/__tests__/memory-retrospective-trigger-check.test.ts +0 -63
  253. package/src/plugins/defaults/memory/__tests__/shutdown-hook.test.ts +0 -4
  254. package/src/plugins/defaults/memory/graph/capability-seed.ts +6 -4
  255. package/src/plugins/defaults/memory/hooks/init.ts +0 -10
  256. package/src/plugins/defaults/memory/hooks/shutdown.ts +0 -2
  257. package/src/plugins/defaults/memory/jobs-worker.ts +0 -13
  258. package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +60 -34
  259. package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +5 -3
  260. package/src/plugins/defaults/memory/memory-retrospective-job.ts +3 -12
  261. package/src/plugins/defaults/memory/memory-retrospective-state.ts +71 -312
  262. package/src/plugins/defaults/memory/memory-retrospective-sweep.ts +1 -2
  263. package/src/plugins/defaults/memory/memory-retrospective-trigger-check.ts +3 -5
  264. package/src/plugins/defaults/memory/startup.ts +1 -6
  265. package/src/plugins/defaults/memory/substrate/__tests__/skill-content.test.ts +9 -7
  266. package/src/plugins/defaults/memory/substrate/skill-content.ts +2 -2
  267. package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +0 -89
  268. package/src/plugins/defaults/memory/worker.ts +0 -6
  269. package/src/plugins/mcp-servers.ts +75 -15
  270. package/src/providers/__tests__/connection-model-compat.test.ts +3 -3
  271. package/src/providers/gemini/client.ts +23 -9
  272. package/src/providers/inference/auth.ts +3 -2
  273. package/src/providers/inference/connection-availability.ts +8 -27
  274. package/src/providers/inference/resolve-auth.ts +4 -22
  275. package/src/providers/model-catalog.ts +0 -34
  276. package/src/providers/model-intents.ts +5 -10
  277. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +18 -146
  278. package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +1 -83
  279. package/src/providers/openai/chat-completions-provider.ts +78 -192
  280. package/src/providers/openai/codex-models.ts +4 -0
  281. package/src/runtime/AGENTS.md +0 -2
  282. package/src/runtime/auth/same-actor.ts +3 -38
  283. package/src/runtime/http-server.ts +151 -160
  284. package/src/runtime/routes/__tests__/acp-routes.test.ts +4 -159
  285. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +1 -108
  286. package/src/runtime/routes/__tests__/default-provider-routes.test.ts +2 -2
  287. package/src/runtime/routes/__tests__/mcp-add-default-risk.test.ts +12 -16
  288. package/src/runtime/routes/acp-routes.test.ts +0 -15
  289. package/src/runtime/routes/acp-routes.ts +9 -72
  290. package/src/runtime/routes/app-management-routes.ts +0 -73
  291. package/src/runtime/routes/app-routes.ts +1 -1
  292. package/src/runtime/routes/conversation-list-routes.ts +3 -14
  293. package/src/runtime/routes/conversation-query-routes.ts +0 -40
  294. package/src/runtime/routes/credential-routes.ts +10 -19
  295. package/src/runtime/routes/debug-bash-routes.ts +173 -0
  296. package/src/runtime/routes/diagnostics-routes.ts +3 -55
  297. package/src/runtime/routes/index.ts +2 -4
  298. package/src/runtime/routes/log-export-routes.ts +0 -2
  299. package/src/runtime/routes/mcp-auth-routes.ts +47 -39
  300. package/src/runtime/routes/migration-routes.ts +20 -6
  301. package/src/runtime/routes/platform-routes.ts +15 -18
  302. package/src/runtime/services/__tests__/conversation-serializer.test.ts +0 -28
  303. package/src/runtime/services/conversation-serializer.ts +3 -16
  304. package/src/runtime/sync/resource-sync-events.ts +1 -2
  305. package/src/schedule/worker.ts +1 -1
  306. package/src/security/ces-rpc-record-backend.ts +0 -7
  307. package/src/subagent/consult-context.ts +25 -12
  308. package/src/tools/acp/list-agents.test.ts +1 -3
  309. package/src/tools/acp/spawn.test.ts +11 -146
  310. package/src/tools/acp/spawn.ts +3 -21
  311. package/src/tools/acp/status.ts +3 -40
  312. package/src/tools/app-control/skill-proxy-bridge.ts +1 -2
  313. package/src/tools/capability-offer.test.ts +2 -22
  314. package/src/tools/capability-offer.ts +1 -16
  315. package/src/tools/computer-use/definitions.ts +2 -4
  316. package/src/tools/computer-use/skill-proxy-bridge.ts +1 -2
  317. package/src/tools/credentials/metadata-store.ts +0 -100
  318. package/src/tools/host-filesystem/edit.test.ts +1 -2
  319. package/src/tools/host-filesystem/edit.ts +2 -2
  320. package/src/tools/host-filesystem/read.test.ts +1 -2
  321. package/src/tools/host-filesystem/read.ts +2 -2
  322. package/src/tools/host-filesystem/transfer.test.ts +1 -2
  323. package/src/tools/host-filesystem/transfer.ts +2 -2
  324. package/src/tools/host-filesystem/write.test.ts +1 -2
  325. package/src/tools/host-filesystem/write.ts +2 -2
  326. package/src/tools/host-terminal/host-shell.test.ts +0 -16
  327. package/src/tools/host-terminal/host-shell.ts +20 -4
  328. package/src/tools/policy-context.ts +7 -6
  329. package/src/tools/subagent/spawn.ts +8 -5
  330. package/src/tools/watch/watch-retro-report.ts +8 -0
  331. package/src/util/__tests__/language-subtag.test.ts +1 -23
  332. package/src/util/fs-watcher-error.ts +36 -0
  333. package/src/util/language-subtag.ts +0 -16
  334. package/src/util/platform.ts +0 -8
  335. package/src/workspace/git-service.ts +54 -5
  336. package/src/workspace/migrations/registry.ts +0 -6
  337. package/scripts/smoke-desktop-window-theme.ts +0 -186
  338. package/src/__tests__/git-porcelain.test.ts +0 -47
  339. package/src/__tests__/helpers/set-workspace-mcp.ts +0 -11
  340. package/src/__tests__/message-deleted-hook-dispatch.test.ts +0 -119
  341. package/src/__tests__/resolve-app.test.ts +0 -135
  342. package/src/__tests__/service-account-token.test.ts +0 -247
  343. package/src/__tests__/source-fingerprint.test.ts +0 -184
  344. package/src/__tests__/workspace-migration-154-repair-retired-codex-gpt-5-4-model-ids.test.ts +0 -336
  345. package/src/acp/__tests__/helpers/acp-model-option.ts +0 -51
  346. package/src/acp/model-config.test.ts +0 -166
  347. package/src/acp/model-config.ts +0 -138
  348. package/src/api/events/acp-session-model-update.test.ts +0 -88
  349. package/src/api/events/acp-session-model-update.ts +0 -46
  350. package/src/apps/resolve-app.ts +0 -81
  351. package/src/apps/source-fingerprint.ts +0 -266
  352. package/src/calls/__tests__/barge-in-guard.test.ts +0 -81
  353. package/src/calls/__tests__/voice-leg-coordinator.test.ts +0 -229
  354. package/src/calls/__tests__/voice-progress-cadence.test.ts +0 -274
  355. package/src/calls/barge-in-guard.ts +0 -118
  356. package/src/calls/voice-leg-coordinator.ts +0 -189
  357. package/src/calls/voice-progress-cadence.ts +0 -423
  358. package/src/cli/commands/__tests__/apps.test.ts +0 -245
  359. package/src/cli/commands/__tests__/bash.test.ts +0 -53
  360. package/src/cli/commands/__tests__/roadmap.help.test.ts +0 -19
  361. package/src/cli/commands/channels/__tests__/send.test.ts +0 -218
  362. package/src/cli/commands/channels/send.ts +0 -128
  363. package/src/config/__tests__/platform-identity.test.ts +0 -255
  364. package/src/config/acp-agent-write.ts +0 -43
  365. package/src/config/acp-schema.test.ts +0 -81
  366. package/src/config/platform-identity.ts +0 -201
  367. package/src/config/schemas/__tests__/voice.test.ts +0 -148
  368. package/src/config/schemas/voice.ts +0 -150
  369. package/src/daemon/__tests__/skill-memory-refresh-mtime.test.ts +0 -133
  370. package/src/daemon/app-change-notify.ts +0 -45
  371. package/src/desktop/__tests__/fake-desktop.ts +0 -119
  372. package/src/desktop/desktop-chrome-policy.test.ts +0 -71
  373. package/src/desktop/desktop-chrome-policy.ts +0 -42
  374. package/src/desktop/desktop-dependencies.test.ts +0 -102
  375. package/src/desktop/desktop-dependencies.ts +0 -306
  376. package/src/desktop/desktop-feature.test.ts +0 -18
  377. package/src/desktop/desktop-feature.ts +0 -19
  378. package/src/desktop/desktop-panel-config.test.ts +0 -135
  379. package/src/desktop/desktop-panel-config.ts +0 -149
  380. package/src/desktop/desktop-session-manager.test.ts +0 -627
  381. package/src/desktop/desktop-session-manager.ts +0 -814
  382. package/src/desktop/desktop-stream-bridge.test.ts +0 -395
  383. package/src/desktop/desktop-stream-bridge.ts +0 -214
  384. package/src/desktop/desktop-wallpaper.test.ts +0 -191
  385. package/src/desktop/desktop-wallpaper.ts +0 -113
  386. package/src/desktop/desktop-window-theme.ts +0 -170
  387. package/src/i18n/AGENTS.md +0 -22
  388. package/src/i18n/__tests__/i18n.test.ts +0 -108
  389. package/src/i18n/index.ts +0 -16
  390. package/src/i18n/locales.ts +0 -93
  391. package/src/i18n/messages.ts +0 -61
  392. package/src/i18n/title.ts +0 -25
  393. package/src/mcp/__tests__/workspace-mcp-config.test.ts +0 -135
  394. package/src/mcp/spec-schema.ts +0 -111
  395. package/src/mcp/workspace-mcp-config.ts +0 -188
  396. package/src/messaging/providers/__tests__/proactive-address.test.ts +0 -95
  397. package/src/persistence/conversation-title-placeholders.ts +0 -23
  398. package/src/persistence/message-cursor.ts +0 -78
  399. package/src/persistence/migrations/__tests__/157-invite-contact-id.test.ts +0 -149
  400. package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-backfill.test.ts +0 -118
  401. package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-preserve.test.ts +0 -83
  402. package/src/plugins/defaults/memory/hooks/message-deleted.ts +0 -21
  403. package/src/plugins/defaults/memory/memory-retrospective-cursor-backfill.ts +0 -126
  404. package/src/plugins/defaults/memory/memory-retrospective-cursor-preserve.ts +0 -79
  405. package/src/plugins/defaults/memory/memory-retrospective-cursor.ts +0 -28
  406. package/src/plugins/defaults/memory/skill-md-mtime-poll.ts +0 -9
  407. package/src/providers/gemini-thought-signature.test.ts +0 -63
  408. package/src/providers/gemini-thought-signature.ts +0 -44
  409. package/src/providers/inference/service-account-token.ts +0 -248
  410. package/src/providers/openai/__tests__/chat-completions-provider-thought-signature.test.ts +0 -613
  411. package/src/providers/openai/__tests__/google-thought-signature.test.ts +0 -235
  412. package/src/providers/openai/google-thought-signature.ts +0 -320
  413. package/src/runtime/__tests__/channel-send.test.ts +0 -443
  414. package/src/runtime/__tests__/desktop-stream-upgrade.test.ts +0 -89
  415. package/src/runtime/__tests__/live-voice-upgrade.test.ts +0 -58
  416. package/src/runtime/__tests__/runtime-ws-test-utils.ts +0 -72
  417. package/src/runtime/__tests__/watch-stream-upgrade.test.ts +0 -59
  418. package/src/runtime/channel-send.ts +0 -316
  419. package/src/runtime/routes/__tests__/apps-refresh-route.test.ts +0 -205
  420. package/src/runtime/routes/__tests__/channel-send-routes.test.ts +0 -127
  421. package/src/runtime/routes/__tests__/dictation-cleanup-guard.test.ts +0 -107
  422. package/src/runtime/routes/channel-send-routes.ts +0 -85
  423. package/src/runtime/routes/desktop-setup-routes.test.ts +0 -74
  424. package/src/runtime/routes/desktop-setup-routes.ts +0 -35
  425. package/src/security/__tests__/ces-http-record-backend.test.ts +0 -137
  426. package/src/security/ces-http-record-backend.ts +0 -120
  427. package/src/skills/__tests__/workspace-skill-md-signature.test.ts +0 -60
  428. package/src/skills/workspace-skill-md-signature.ts +0 -50
  429. package/src/tools/acp/status.test.ts +0 -94
  430. package/src/tools/terminal/__tests__/sanitized-bash.test.ts +0 -57
  431. package/src/tools/terminal/sanitized-bash.ts +0 -105
  432. package/src/util/ca-bundle.ts +0 -13
  433. package/src/workspace/git-porcelain.ts +0 -66
  434. package/src/workspace/migrations/154-repair-retired-codex-gpt-5-4-model-ids.ts +0 -258
  435. package/src/workspace/migrations/155-move-front-model-config-to-voice.ts +0 -78
  436. package/src/workspace/migrations/156-extract-workspace-mcp-json.ts +0 -214
  437. package/src/workspace/migrations/__tests__/155-move-front-model-config-to-voice.test.ts +0 -102
  438. package/src/workspace/migrations/__tests__/156-extract-workspace-mcp-json.test.ts +0 -199
  439. /package/src/{calls → live-voice}/progress-phrases.ts +0 -0
@@ -7,22 +7,7 @@ export const DESKTOP_APP_DOWNLOAD_URL = "https://www.vellum.ai/downloads";
7
7
  export const CHROME_WEB_STORE_INSTALL_URL =
8
8
  "https://chromewebstore.google.com/detail/vellum-assistant-browser/hphbdmpffeigpcdjkckleobjmhhokpne";
9
9
 
10
- export const DESKTOP_APP_INSTALL_HINT = `Download the Vellum desktop app: ${DESKTOP_APP_DOWNLOAD_URL}. Open it, then retry.`;
11
-
12
- export type DesktopAppCapability = "files" | "screen" | "shell" | "apps";
13
-
14
- const DESKTOP_APP_CAPABILITY_ACTION: Record<DesktopAppCapability, string> = {
15
- files: "access files on your computer",
16
- screen: "view or control your screen",
17
- shell: "run commands on your computer",
18
- apps: "control apps on your computer",
19
- };
20
-
21
- export function formatDesktopAppRequired(
22
- capability: DesktopAppCapability,
23
- ): string {
24
- return `Error: The Vellum desktop app is needed to ${DESKTOP_APP_CAPABILITY_ACTION[capability]}. ${DESKTOP_APP_INSTALL_HINT}`;
25
- }
10
+ export const DESKTOP_APP_INSTALL_HINT = `Install the Vellum desktop app: ${DESKTOP_APP_DOWNLOAD_URL}`;
26
11
 
27
12
  /** User-facing install step for status userActions and command-failure hints. */
28
13
  export const CHROME_EXTENSION_INSTALL_HINT = `Install the Vellum Assistant Chrome extension from the Chrome Web Store: ${CHROME_WEB_STORE_INSTALL_URL}`;
@@ -8,7 +8,6 @@
8
8
  */
9
9
 
10
10
  import { RiskLevel } from "../../permissions/types.js";
11
- import { formatDesktopAppRequired } from "../capability-offer.js";
12
11
  import type {
13
12
  ToolContext,
14
13
  ToolDefinition,
@@ -32,7 +31,7 @@ function proxyExecute(toolName: string) {
32
31
  ): Promise<ToolExecutionResult> => {
33
32
  if (!context.proxyToolResolver) {
34
33
  return {
35
- content: formatDesktopAppRequired("screen"),
34
+ content: `No proxy resolver configured for proxy tool "${toolName}". This tool requires a connected desktop client.`,
36
35
  isError: true,
37
36
  };
38
37
  }
@@ -457,8 +456,7 @@ export const computerUseObserveTool = {
457
456
  type: "integer",
458
457
  minimum: 1,
459
458
  maximum: 4294967295,
460
- description:
461
- "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.",
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.",
462
460
  },
463
461
  target_client_id: {
464
462
  type: "string",
@@ -5,7 +5,6 @@
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";
9
8
  import { throwIfCancelled } from "../shared/abort.js";
10
9
  import type { ToolContext, ToolExecutionResult } from "../types.js";
11
10
 
@@ -46,7 +45,7 @@ export function forwardComputerUseProxyTool(
46
45
  }
47
46
  if (!context.proxyToolResolver) {
48
47
  return Promise.resolve({
49
- content: formatDesktopAppRequired("screen"),
48
+ content: `Cannot execute ${toolName}: no proxy resolver available. This tool requires a connected desktop client.`,
50
49
  isError: true,
51
50
  });
52
51
  }
@@ -20,7 +20,6 @@ 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";
24
23
  import type { CredentialRecordBackend } from "../../security/ces-rpc-record-backend.js";
25
24
  import { getLogger } from "../../util/logger.js";
26
25
  import type { CredentialInjectionTemplate } from "./policy-types.js";
@@ -56,11 +55,6 @@ const CES_MEMORY_PATH = "ces-in-memory-credential-metadata";
56
55
  let _store: StaticCredentialMetadataStore | undefined;
57
56
  let _overridePath: string | null = null;
58
57
  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;
64
58
  let _cesRecordsLoaded = false;
65
59
  let _cesLoad: Promise<void> | undefined;
66
60
  let _cesLoadGeneration = 0;
@@ -261,93 +255,6 @@ export function listCredentialMetadata(): CredentialMetadata[] {
261
255
  return getStore().list() as CredentialMetadata[];
262
256
  }
263
257
 
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
-
351
258
  /**
352
259
  * Delete metadata for a credential.
353
260
  */
@@ -380,10 +287,3 @@ export function _setMetadataPath(path: string | null): void {
380
287
  }
381
288
  }
382
289
  }
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
- }
@@ -71,9 +71,8 @@ describe("host_file_edit cross-client guards", () => {
71
71
  );
72
72
  expect(result.isError).toBe(true);
73
73
  expect(result.content).toContain(
74
- "The Vellum desktop app is needed to access files on your computer",
74
+ "no client with host_file capability is connected",
75
75
  );
76
- expect(result.content).toContain("https://www.vellum.ai/downloads");
77
76
  });
78
77
 
79
78
  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 { formatDesktopAppRequired } from "../capability-offer.js";
7
+ import { desktopClientName } from "../client-os.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: formatDesktopAppRequired("files"),
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.`,
119
119
  isError: true,
120
120
  };
121
121
  }
@@ -67,9 +67,8 @@ describe("host_file_read cross-client guards", () => {
67
67
  );
68
68
  expect(result.isError).toBe(true);
69
69
  expect(result.content).toContain(
70
- "The Vellum desktop app is needed to access files on your computer",
70
+ "no client with host_file capability is connected",
71
71
  );
72
- expect(result.content).toContain("https://www.vellum.ai/downloads");
73
72
  });
74
73
 
75
74
  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 { formatDesktopAppRequired } from "../capability-offer.js";
9
+ import { desktopClientName } from "../client-os.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: formatDesktopAppRequired("files"),
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.`,
125
125
  isError: true,
126
126
  };
127
127
  }
@@ -335,9 +335,8 @@ describe("host_file_transfer cross-client guards", () => {
335
335
 
336
336
  expect(result.isError).toBe(true);
337
337
  expect(result.content).toContain(
338
- "The Vellum desktop app is needed to access files on your computer",
338
+ "no client with host_file capability is connected",
339
339
  );
340
- expect(result.content).toContain("https://www.vellum.ai/downloads");
341
340
  expect(toSandboxCalls.length).toBe(0);
342
341
  });
343
342
 
@@ -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 { formatDesktopAppRequired } from "../capability-offer.js";
9
+ import { desktopClientName } from "../client-os.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: formatDesktopAppRequired("files"),
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.`,
130
130
  isError: true,
131
131
  };
132
132
  }
@@ -67,9 +67,8 @@ describe("host_file_write cross-client guards", () => {
67
67
  );
68
68
  expect(result.isError).toBe(true);
69
69
  expect(result.content).toContain(
70
- "The Vellum desktop app is needed to access files on your computer",
70
+ "no client with host_file capability is connected",
71
71
  );
72
- expect(result.content).toContain("https://www.vellum.ai/downloads");
73
72
  });
74
73
 
75
74
  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 { formatDesktopAppRequired } from "../capability-offer.js";
7
+ import { desktopClientName } from "../client-os.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: formatDesktopAppRequired("files"),
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.`,
94
94
  isError: true,
95
95
  };
96
96
  }
@@ -170,22 +170,6 @@ 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
-
189
173
  // ---------------------------------------------------------------------------
190
174
  // Proxy path — background: true
191
175
  // ---------------------------------------------------------------------------
@@ -18,7 +18,10 @@ 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 { broadcastMessage } from "../../runtime/assistant-event-hub.js";
21
+ import {
22
+ assistantEventHub,
23
+ broadcastMessage,
24
+ } from "../../runtime/assistant-event-hub.js";
22
25
  import { conversationRevealNonce } from "../../runtime/reveal-nonce.js";
23
26
  import { redactSecrets } from "../../security/secret-scanner.js";
24
27
  import {
@@ -36,7 +39,7 @@ import {
36
39
  registerBackgroundTool,
37
40
  removeBackgroundTool,
38
41
  } from "../background-tool-registry.js";
39
- import { formatDesktopAppRequired } from "../capability-offer.js";
42
+ import { desktopClientName } from "../client-os.js";
40
43
  import {
41
44
  attachBoundedStdio,
42
45
  MAX_OUTPUT_LENGTH,
@@ -139,7 +142,7 @@ export const hostShellInputSchema = z.looseObject({
139
142
  target_client_id: z
140
143
  .string()
141
144
  .describe(
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`.",
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`.",
143
146
  )
144
147
  .optional()
145
148
  .catch(undefined),
@@ -200,7 +203,20 @@ export const hostShellTool = {
200
203
  const config = getConfig();
201
204
  const { shellDefaultTimeoutSec, shellMaxTimeoutSec } = config.timeouts;
202
205
 
206
+ // Guard: non-host-proxy interfaces need an explicit target when multiple
207
+ // capable clients are connected to avoid ambiguous untargeted broadcasts.
203
208
  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
+ }
204
220
 
205
221
  // Guard: non-host-proxy interfaces with no capable clients connected.
206
222
  if (
@@ -210,7 +226,7 @@ export const hostShellTool = {
210
226
  !HostBashProxy.instance.isAvailable()
211
227
  ) {
212
228
  return {
213
- content: formatDesktopAppRequired("shell"),
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.`,
214
230
  isError: true,
215
231
  };
216
232
  }
@@ -1,5 +1,5 @@
1
1
  import { getConfig } from "../config/loader.js";
2
- import { isSkillImprovementActive } from "../config/memory-v3-gate.js";
2
+ import { isV3TierActive } 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,11 +63,12 @@ export function buildPolicyContext(
63
63
  requestOrigin: context?.requestOrigin,
64
64
  ...channelCoordinatesFromToolContext(context),
65
65
  requesterContactId: context?.requesterContactId,
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()),
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()),
71
72
  };
72
73
 
73
74
  const ownerKind = getToolOwner(tool.name)?.kind;
@@ -484,9 +484,9 @@ function spawnCancelledResult(label: string): ToolExecutionResult {
484
484
  * repeat-spawn guard folds, so a consult has to record it the same way every
485
485
  * other spawn does or the guard could never match one consult against another.
486
486
  *
487
- * The context pack is best effort: each section independently falls back to
488
- * null on failure, so a missing workspace or empty catalog costs only that
489
- * section, never the consult.
487
+ * The context pack is best effort: the parent conversation is looked up only
488
+ * for its warm skill catalog, so an unresolvable one (e.g. evicted) costs the
489
+ * skills section of the pack and nothing else.
490
490
  *
491
491
  * The budget is the advisor's alone. It is the only spawn the agent may issue
492
492
  * unprompted and the only one that runs on the premium profile, so it is the
@@ -502,18 +502,21 @@ async function buildAdvisorFields(
502
502
  maxRuntimeMs: number;
503
503
  maxToolCalls: number;
504
504
  }> {
505
+ const parentConversation = findConversation(context.conversationId);
505
506
  // Situational awareness for the advisor: the parent's live tool set, the
506
507
  // full skill catalog, and its workspace. Assembled off the per-turn
507
508
  // ToolContext snapshot (trust, channel) so the personal-memory sections are
508
509
  // gated exactly like the runtime injectors. A null pack just means the
509
- // consult runs on the brief alone. Skills come from a fresh
510
- // `loadSkillCatalog()` inside the context pack, not a conversation snapshot.
510
+ // consult runs on the brief alone.
511
511
  const situationalContext = await buildAdvisorContext({
512
512
  conversationId: context.conversationId,
513
513
  workingDir: context.workingDir,
514
514
  allowedToolNames: context.allowedToolNames,
515
515
  trustClass: context.trustClass,
516
516
  enabledPluginSet: context.enabledPluginSet,
517
+ // The parent's warm per-turn catalog keeps the synchronous on-disk catalog
518
+ // scan out of the spawn path.
519
+ skillCatalog: parentConversation?.skillProjectionCache?.catalog,
517
520
  });
518
521
  return {
519
522
  requestText: advisorRequestText(objective, situationalContext),
@@ -14,6 +14,14 @@ import type {
14
14
  /**
15
15
  * How a watch retrospective hands its report to the daemon.
16
16
  *
17
+ * **The card cannot come from `ui_show`.** A retrospective runs as a
18
+ * `clientless` wake, which pins the turn non-interactive, and
19
+ * `conversation-tool-setup` gates the whole `ui_surface` tool family on a
20
+ * client being present (`return channelCapabilities?.supportsDynamicUi ??
21
+ * !hasNoClient`). So `ui_show` is not merely denied in this turn, it is absent
22
+ * from the tool set, and a retrospective told to call it can only report that
23
+ * it cannot. This tool is an ordinary one and passes that gate untouched.
24
+ *
17
25
  * **It records; it does not render.** The executor validates the payload and
18
26
  * returns, and nothing here writes to the conversation. What makes the card is
19
27
  * `watch-retro.ts` reading this call back out of the turn's own history once
@@ -1,10 +1,6 @@
1
1
  import { describe, expect, test } from "bun:test";
2
2
 
3
- import {
4
- baseLanguageSubtag,
5
- fixedPhraseLanguage,
6
- localizedOrDefault,
7
- } from "../language-subtag.js";
3
+ import { baseLanguageSubtag, localizedOrDefault } from "../language-subtag.js";
8
4
 
9
5
  describe("baseLanguageSubtag", () => {
10
6
  test("lowercases a bare tag", () => {
@@ -56,21 +52,3 @@ describe("localizedOrDefault", () => {
56
52
  expect(localizedOrDefault(table, "__proto__", "fallback")).toBe("fallback");
57
53
  });
58
54
  });
59
-
60
- describe("fixedPhraseLanguage", () => {
61
- const table = { en: "one", es: "uno" };
62
-
63
- test("pins English when the speaker's language has no table entry", () => {
64
- expect(fixedPhraseLanguage(table, "ko")).toBe("en");
65
- expect(fixedPhraseLanguage(table, "ar-EG")).toBe("en");
66
- });
67
-
68
- test("rides the speaker's language when the table covers it", () => {
69
- expect(fixedPhraseLanguage(table, "es")).toBeUndefined();
70
- expect(fixedPhraseLanguage(table, "es-419")).toBeUndefined();
71
- });
72
-
73
- test("carries no hint when the language is unknown", () => {
74
- expect(fixedPhraseLanguage(table, undefined)).toBeUndefined();
75
- });
76
- });
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Shared `'error'`-event handler for `fs.watch()` FSWatchers.
3
+ *
4
+ * An `FSWatcher` is an EventEmitter. When the underlying inotify/FSEvents
5
+ * backend fails *after* the watch was established — e.g. ENOSPC when the
6
+ * kernel's `fs.inotify.max_user_watches` limit is exhausted while walking a
7
+ * recursive watch into a large subtree (a plugin's `node_modules`), or ENXIO
8
+ * when a Unix socket file appears in a watched directory — the failure is
9
+ * delivered asynchronously as an `'error'` event rather than a synchronous
10
+ * throw from `watch()`. An emitter with no `'error'` listener rethrows, which
11
+ * surfaces as an `uncaughtException` and takes the whole daemon down (→
12
+ * CrashLoopBackOff).
13
+ *
14
+ * Attaching this handler degrades the failure to "this watcher stops
15
+ * delivering events", in line with the daemon startup philosophy: a subsystem
16
+ * failure must never crash the process.
17
+ */
18
+
19
+ import type { FSWatcher } from "node:fs";
20
+
21
+ import type { Logger } from "pino";
22
+
23
+ /**
24
+ * Attach a resilient `'error'` listener so async FSWatcher failures are logged
25
+ * instead of crashing the process. Pass the owning module's logger and the
26
+ * watched directory for diagnostic context.
27
+ */
28
+ export function attachFsWatcherErrorHandler(
29
+ watcher: FSWatcher,
30
+ log: Logger,
31
+ dir: string,
32
+ ): void {
33
+ watcher.on("error", (err) => {
34
+ log.warn({ err, dir }, "FSWatcher error (non-fatal, continuing)");
35
+ });
36
+ }
@@ -41,19 +41,3 @@ export function localizedOrDefault<T>(
41
41
  ? (table[baseLanguageSubtag(language)!] as T)
42
42
  : fallback;
43
43
  }
44
-
45
- /**
46
- * The TTS language hint for a fixed phrase picked from a localized table:
47
- * "en" when the speaker has a language the table does not cover (the
48
- * picker fell back to English text, which must not be synthesized under
49
- * an ar/ko/ta hint), undefined otherwise (the phrase rides the speaker's
50
- * language, or carries no hint at all when the language is unknown).
51
- */
52
- export function fixedPhraseLanguage(
53
- table: Readonly<Record<string, unknown>>,
54
- language: string | undefined,
55
- ): string | undefined {
56
- return language !== undefined && !hasLocalizedEntry(table, language)
57
- ? "en"
58
- : undefined;
59
- }
@@ -470,14 +470,6 @@ export function getWorkspaceConfigPath(): string {
470
470
  return join(getWorkspaceDir(), "config.json");
471
471
  }
472
472
 
473
- /** Filename for the user-owned Agent Plugins MCP document. */
474
- export const WORKSPACE_MCP_FILENAME = "mcp.json";
475
-
476
- /** Returns $VELLUM_WORKSPACE_DIR/mcp.json */
477
- export function getWorkspaceMcpConfigPath(): string {
478
- return join(getWorkspaceDir(), WORKSPACE_MCP_FILENAME);
479
- }
480
-
481
473
  /** Returns $VELLUM_WORKSPACE_DIR/skills */
482
474
  export function getWorkspaceSkillsDir(): string {
483
475
  return join(getWorkspaceDir(), "skills");