@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
@@ -55,6 +55,7 @@ import {
55
55
  isActivationMomentParam,
56
56
  } from "../telemetry/activation-funnel.js";
57
57
  import { resolveAppId } from "../tools/apps/resolve-app-id.js";
58
+ import { formatDesktopAppRequired } from "../tools/capability-offer.js";
58
59
  import { POINT_AT_PROXY_TOOL } from "../tools/computer-use/skill-proxy-bridge.js";
59
60
  import type { ToolExecutionResult } from "../tools/types.js";
60
61
  import { getLogger } from "../util/logger.js";
@@ -100,6 +101,7 @@ export {
100
101
  type SurfaceShowPair,
101
102
  type SurfaceStateEntry,
102
103
  } from "./conversation-surface-state.js";
104
+ import { safeStringSlice } from "../util/unicode.js";
103
105
  import type { HostAppControlInput } from "./message-types/host-app-control.js";
104
106
  import type { UserMessageAttachment } from "./message-types/shared.js";
105
107
 
@@ -1486,7 +1488,10 @@ function handleDocumentContentChanged(
1486
1488
  updateApp(appId, {
1487
1489
  name: title || app.name,
1488
1490
  description: `Document with ${wordCount ?? 0} words`,
1489
- preview: content?.slice(0, 200),
1491
+ preview:
1492
+ content === undefined
1493
+ ? undefined
1494
+ : safeStringSlice(content, 0, 200),
1490
1495
  htmlDefinition: updatedHtml,
1491
1496
  });
1492
1497
 
@@ -2936,7 +2941,7 @@ export function buildAppOpenPreview(
2936
2941
  function describeComputerUseUnavailable(ctx: Conversation): string {
2937
2942
  const capable = assistantEventHub.listClientsByCapability("host_cu");
2938
2943
  if (capable.length === 0) {
2939
- return "Computer use is not available — no desktop client connected. Open the Vellum desktop app on the machine you want to control, then retry.";
2944
+ return formatDesktopAppRequired("screen");
2940
2945
  }
2941
2946
  return `Computer use is not available for this conversation — ${capable.length} desktop client(s) advertise host_cu, but none of them can be driven from this conversation's interface (${ctx.transportInterface ?? "unknown"}) as its current user.`;
2942
2947
  }
@@ -3175,7 +3180,7 @@ export async function surfaceProxyResolver(
3175
3180
  : typeof input.answer === "string"
3176
3181
  ? input.answer
3177
3182
  : "Task complete";
3178
- hostCuProxy.reset();
3183
+ hostCuProxy.endTask(ctx.conversationId);
3179
3184
  return { content: summary, isError: false };
3180
3185
  }
3181
3186
 
@@ -3251,8 +3256,7 @@ export async function surfaceProxyResolver(
3251
3256
 
3252
3257
  if (!ctx.hostAppControlProxy || !ctx.hostAppControlProxy.isAvailable()) {
3253
3258
  return {
3254
- content:
3255
- "App control is not available — enable the `app-control` feature flag and connect a macOS client.",
3259
+ content: formatDesktopAppRequired("apps"),
3256
3260
  isError: true,
3257
3261
  };
3258
3262
  }
@@ -3342,8 +3346,14 @@ export async function surfaceProxyResolver(
3342
3346
 
3343
3347
  if (toolName === "ui_show" || toolName === "ui_update") {
3344
3348
  const caps = ctx.channelCapabilities;
3349
+ // Live non-dynamic channels reject unsupported surfaces. Clientless turns
3350
+ // skip this gate so surfaces persist for a later capable client.
3351
+ // `canShowInteractiveUi` fails closed on clientless turns because
3352
+ // standalone surfaces have nobody to answer them, so it is not the
3353
+ // predicate here.
3345
3354
  if (
3346
3355
  caps &&
3356
+ !ctx.hasNoClient &&
3347
3357
  !caps.supportsDynamicUi &&
3348
3358
  !isSlackTaskProgressUiException(ctx, toolName, input)
3349
3359
  ) {
@@ -3552,11 +3562,12 @@ export async function surfaceProxyResolver(
3552
3562
  : surfaceType === "table"
3553
3563
  ? hasActions
3554
3564
  : INTERACTIVE_SURFACE_TYPES.includes(surfaceType);
3555
- // An explicit `await_action: true` is honored for every other type; an
3556
- // actionless surface has nothing to await, so it is forced false.
3557
- const awaitAction = isActionless
3558
- ? false
3559
- : ((input.await_action as boolean) ?? isInteractive);
3565
+ // Background turns persist surfaces for a later conversation open and
3566
+ // return immediately. An actionless surface also has nothing to await.
3567
+ const awaitAction =
3568
+ !ctx.hasNoClient &&
3569
+ !isActionless &&
3570
+ ((input.await_action as boolean) ?? isInteractive);
3560
3571
 
3561
3572
  // Only one non-persistent interactive surface at a time. If another
3562
3573
  // surface is already awaiting user input, reject this one so the LLM
@@ -28,7 +28,6 @@ import { getAllDefaultPluginNames } from "../plugins/defaults/main.js";
28
28
  import { isActivationSession } from "../plugins/defaults/memory/activation-session-store.js";
29
29
  import { isPluginDisabled } from "../plugins/disabled-state.js";
30
30
  import type { Message, ToolDefinition } from "../providers/types.js";
31
- import { assistantEventHub } from "../runtime/assistant-event-hub.js";
32
31
  import { registerConversationSender } from "../tools/browser/browser-screencast.js";
33
32
  import { supportsClientOsForSkillTool } from "../tools/client-os.js";
34
33
  import type { ToolExecutor } from "../tools/executor.js";
@@ -63,10 +62,7 @@ import {
63
62
  type ToolContext,
64
63
  type ToolExecutionResult,
65
64
  } from "../tools/types.js";
66
- import {
67
- injectActivationMomentParam,
68
- projectUiToolsForChannel,
69
- } from "../tools/ui-surface/channel-variants.js";
65
+ import { injectActivationMomentParam } from "../tools/ui-surface/channel-variants.js";
70
66
  import { loadWorkspaceTools } from "../tools/workspace-tools/loader.js";
71
67
  import {
72
68
  resolveUsageAttribution,
@@ -260,6 +256,24 @@ export function createToolExecutor(
260
256
  // see {@link SubagentToolGateMode}): rejects non-allowlisted calls BEFORE
261
257
  // any executor dispatch, so a non-allowlisted tool's executor never runs.
262
258
  // The error tool_result lets the model continue or finish.
259
+ const rejectUnattendedHostTool = (
260
+ toolName: string,
261
+ ): ToolExecutionResult | null => {
262
+ const { transportInterface } = resolveTurnClientOs(ctx);
263
+ const isUnattended = ctx.currentTurnIsNonInteractive ?? ctx.hasNoClient;
264
+ if (
265
+ HOST_TOOL_NAMES.has(toolName) &&
266
+ isUnattended &&
267
+ transportInterface !== "chrome-extension"
268
+ ) {
269
+ return {
270
+ content: `The "${toolName}" tool requires an interactive user turn and cannot run in the background.`,
271
+ isError: true,
272
+ };
273
+ }
274
+ return null;
275
+ };
276
+
263
277
  const rejectNonAllowlistedTool = (
264
278
  toolName: string,
265
279
  ): ToolExecutionResult | null => {
@@ -387,6 +401,10 @@ export function createToolExecutor(
387
401
  if (rejection) {
388
402
  return rejection;
389
403
  }
404
+ const unattendedRejection = rejectUnattendedHostTool(executionName);
405
+ if (unattendedRejection) {
406
+ return unattendedRejection;
407
+ }
390
408
  }
391
409
 
392
410
  if (isDoordashCommand(executionName, executionInput)) {
@@ -550,6 +568,10 @@ export function createToolExecutor(
550
568
  if (innerRejection) {
551
569
  return innerRejection;
552
570
  }
571
+ const unattendedRejection = rejectUnattendedHostTool(toolName);
572
+ if (unattendedRejection) {
573
+ return unattendedRejection;
574
+ }
553
575
 
554
576
  // Per-chat plugin scope: reject the resolved inner tool when it belongs
555
577
  // to a plugin outside the conversation's effective set.
@@ -628,7 +650,6 @@ export const DEFAULT_PREACTIVATED_SKILL_IDS = ["notifications", "subagent"];
628
650
  // ── Conditional tool sets ────────────────────────────────────────────
629
651
 
630
652
  const UI_SURFACE_TOOL_NAMES = new Set(["ui_show", "ui_update", "ui_dismiss"]);
631
- const SLACK_TASK_PROGRESS_UI_TOOL_NAMES = new Set(["ui_show", "ui_update"]);
632
653
  /**
633
654
  * Single source of truth for which tools are host tools and the capability
634
655
  * each one requires from the connected client interface. Adding a tool here
@@ -856,56 +877,26 @@ export function isToolActiveForContext(
856
877
  return supportsChannelReaction(turnChannel);
857
878
  }
858
879
  if (UI_SURFACE_TOOL_NAMES.has(name)) {
859
- if (
860
- channelCapabilities?.channel === "slack" &&
861
- SLACK_TASK_PROGRESS_UI_TOOL_NAMES.has(name)
862
- ) {
863
- return !hasNoClient;
864
- }
865
- return channelCapabilities?.supportsDynamicUi ?? !hasNoClient;
880
+ // Surface calls write conversation content. Background turns persist that
881
+ // content for the next client that opens the conversation, so presence
882
+ // cannot change whether the model receives these definitions.
883
+ return true;
866
884
  }
867
885
  if (HOST_TOOL_NAMES.has(name)) {
868
886
  const capability = HOST_TOOL_TO_CAPABILITY.get(name);
869
887
  const transport = transportInterface;
870
888
 
871
- // Per-capability check is authoritative for structural support: if the
872
- // transport cannot service this capability, the tool is filtered out.
889
+ // A transport that does not implement a capability can invoke it through
890
+ // an eligible same-user client. Client selection happens when the call
891
+ // runs, so the wire schema stays stable across live and background turns.
873
892
  if (transport && capability && !supportsHostProxy(transport, capability)) {
874
- // Cross-client exception: allow host tools whose capabilities have
875
- // cross-client routing infrastructure (Phases 1–3 plus host_browser
876
- // via PR #27489) to be exposed for non-host-proxy transports (e.g.
877
- // "web", "ios") when at least one capable client is connected via
878
- // the event hub. Members of CROSS_CLIENT_EXPOSED_CAPABILITIES
879
- // (host_bash, host_file, host_browser) qualify.
880
- // chrome-extension transport is excluded as a security boundary
881
- // (extension only gets host_browser via its own executor path);
882
- // hasNoClient turns are excluded (no interactive approval UI
883
- // available).
884
- if (
885
- capability &&
893
+ return (
886
894
  CROSS_CLIENT_EXPOSED_CAPABILITIES.has(capability) &&
887
- transport !== "chrome-extension" &&
888
- !hasNoClient &&
889
- assistantEventHub.listClientsByCapability(capability).length > 0
890
- ) {
891
- return true;
892
- }
893
- return false;
894
- }
895
-
896
- // chrome-extension is its own executor — the extension's popup gates
897
- // commands via its own UI, and the transport does not use an SSE-level
898
- // interactive approval channel. hasNoClient is intentionally `true` for
899
- // chrome-extension turns (chrome-extension is not in INTERACTIVE_INTERFACES)
900
- // and must not gate host_browser. Trust the per-capability check.
901
- if (transport === "chrome-extension") {
902
- return true;
895
+ transport !== "chrome-extension"
896
+ );
903
897
  }
904
898
 
905
- // For transports that surface approvals over SSE (macos, backwards-compat
906
- // fallback), deny when no client is present so the guardian auto-approve
907
- // path cannot execute host commands unattended.
908
- return !hasNoClient;
899
+ return true;
909
900
  }
910
901
  if (CLIENT_CAPABILITY_TOOL_NAMES.has(name)) {
911
902
  if (name === "ask_question" && channelCapabilities?.clientOS === "macos") {
@@ -1153,18 +1144,13 @@ export function createResolveToolsCallback(
1153
1144
  : currentWorkspaceDefs
1154
1145
  ).filter((d) => !readOnlyHidesFromWire(d.name));
1155
1146
  const excluded = new Set(getConfig().tools.exclude);
1156
- // Swap UI surface tools for channel-appropriate variants (e.g. Slack's
1157
- // task_progress-only ui_show). Mirrors the pin handling in
1158
- // `isToolActiveForContext`: execution-gate-mode wakes pin channel
1159
- // capabilities to undefined, which resolves to the unprojected defs.
1160
- const channelForUiTools = ctx.toolContextPin
1161
- ? undefined
1162
- : ctx.channelCapabilities?.channel;
1163
- let allBaseDefs = projectUiToolsForChannel(
1164
- [...scopedCoreDefs, ...scopedWorkspaceDefs, ...scopedMcpDefs].filter(
1165
- (d) => !excluded.has(d.name),
1166
- ),
1167
- channelForUiTools,
1147
+ // UI definitions stay identical across channel and background turns.
1148
+ // Channel renderers enforce their supported surface subset at execution,
1149
+ // while background calls persist the full surface content for the next
1150
+ // capable client that opens the conversation. Skill tools stay off this
1151
+ // list (`skill_execute` dispatch) and are a separate disclosure path.
1152
+ let allBaseDefs = [...scopedCoreDefs, ...scopedWorkspaceDefs, ...scopedMcpDefs].filter(
1153
+ (d) => !excluded.has(d.name),
1168
1154
  );
1169
1155
  // Activation-rail conversations carry the optional `activation_moment`
1170
1156
  // telemetry param on ui_show. The marker is written before the first
@@ -2366,9 +2366,6 @@ export class Conversation {
2366
2366
 
2367
2367
  markStale(): void {
2368
2368
  this.stale = true;
2369
- // Invalidate the cached skill catalog so the next projection picks up
2370
- // filesystem changes (e.g. a skill created during this run).
2371
- this.skillProjectionCache.catalog = undefined;
2372
2369
  }
2373
2370
 
2374
2371
  isStale(): boolean {
@@ -10,6 +10,7 @@ import { join } from "node:path";
10
10
 
11
11
  import { getLogger } from "../util/logger.js";
12
12
  import { getWorkspaceDir } from "../util/platform.js";
13
+ import { safeStringSlice } from "../util/unicode.js";
13
14
 
14
15
  const log = getLogger("dictation-profile-store");
15
16
 
@@ -130,7 +131,7 @@ function validateAndClampConfig(raw: unknown): DictationProfilesConfig {
130
131
  { profileId: profile.id },
131
132
  `stylePrompt exceeds ${MAX_STYLE_PROMPT_LENGTH} chars, truncating`,
132
133
  );
133
- stylePrompt = stylePrompt.slice(0, MAX_STYLE_PROMPT_LENGTH);
134
+ stylePrompt = safeStringSlice(stylePrompt, 0, MAX_STYLE_PROMPT_LENGTH);
134
135
  }
135
136
 
136
137
  // Validate dictionary entries
@@ -32,6 +32,7 @@ import { unwrapExternalContentForDisplay } from "../../security/untrusted-conten
32
32
  import type { CredentialInjectionTemplate } from "../../tools/credentials/policy-types.js";
33
33
  import { getLogger } from "../../util/logger.js";
34
34
  import { joinWithSpacing } from "../../util/text-spacing.js";
35
+ import { safeStringSlice } from "../../util/unicode.js";
35
36
  import { estimateBase64Bytes } from "../assistant-attachments.js";
36
37
  import { conversationSupportsDynamicUi } from "../channel-ui-capability.js";
37
38
  import { findConversation } from "../conversation-registry.js";
@@ -264,7 +265,7 @@ function clampAttachmentText(text: string): string {
264
265
  if (text.length <= HISTORY_ATTACHMENT_TEXT_LIMIT) {
265
266
  return text;
266
267
  }
267
- return `${text.slice(0, HISTORY_ATTACHMENT_TEXT_LIMIT)}<truncated />`;
268
+ return `${safeStringSlice(text, 0, HISTORY_ATTACHMENT_TEXT_LIMIT)}<truncated />`;
268
269
  }
269
270
 
270
271
  interface FileBlockMetadata {
@@ -82,6 +82,7 @@ import {
82
82
  searchSkillsRegistry,
83
83
  } from "../../skills/skillssh-registry.js";
84
84
  import { getWorkspaceSkillsDir } from "../../util/platform.js";
85
+ import { safeStringSlice } from "../../util/unicode.js";
85
86
  import { getConfigWatcher } from "../config-watcher.js";
86
87
  import type {
87
88
  SkillDetailResponse,
@@ -237,9 +238,11 @@ function heuristicDraft(body: string): {
237
238
  const lines = body.split("\n").filter((l) => l.trim());
238
239
  const firstLine = lines[0]?.trim() ?? "";
239
240
  const name =
240
- firstLine.replace(/^#+\s*/, "").slice(0, 100) || "Untitled Skill";
241
+ safeStringSlice(firstLine.replace(/^#+\s*/, ""), 0, 100) ||
242
+ "Untitled Skill";
241
243
  const skillId = toSkillSlug(name) || "untitled-skill";
242
- const description = body.trim().slice(0, 200) || "No description provided";
244
+ const description =
245
+ safeStringSlice(body.trim(), 0, 200) || "No description provided";
243
246
  return { skillId, name, description, emoji: "\u{1F4DD}" };
244
247
  }
245
248
 
@@ -1732,7 +1735,7 @@ export async function draftSkill(params: {
1732
1735
  "- emoji: a single emoji character representing the skill",
1733
1736
  "",
1734
1737
  "Skill body:",
1735
- body.slice(0, 2000),
1738
+ safeStringSlice(body, 0, 2000),
1736
1739
  ].join("\n");
1737
1740
 
1738
1741
  const response = await provider.sendMessage([userMessage(prompt)], {
@@ -1,9 +1,9 @@
1
1
  import { getConfig } from "../config/loader.js";
2
2
  import { assistantEventHub } from "../runtime/assistant-event-hub.js";
3
3
  import {
4
- ambiguousSameUserError,
5
4
  enforceSameActorOrErrorResult,
6
- pickSameUserAutoResolve,
5
+ pickMostRecentSameUserClient,
6
+ unavailableSameUserClientError,
7
7
  } from "../runtime/auth/same-actor.js";
8
8
  import * as pendingInteractions from "../runtime/pending-interactions.js";
9
9
  import { formatShellOutput } from "../tools/shared/shell-output.js";
@@ -93,21 +93,17 @@ export class HostBashProxy extends HostProxyBase<
93
93
  }
94
94
  resolvedTargetClientId = input.targetClientId;
95
95
  } else {
96
- // Auto-resolve to the unique same-user client. Reject (rather than
97
- // broadcast) when multiple same-user clients are connected so that
98
- // a single targeted-style request cannot fan out across every one
99
- // of the user's machines. Zero same-user matches falls through to
100
- // the existing untargeted code path.
101
- const resolved = pickSameUserAutoResolve({
96
+ resolvedTargetClientId = pickMostRecentSameUserClient({
102
97
  hub: assistantEventHub,
103
98
  capability: "host_bash",
104
99
  sourceActorPrincipalId,
105
100
  });
106
- if (resolved.kind === "ambiguous") {
107
- return ambiguousSameUserError("host_bash");
101
+ if (
102
+ resolvedTargetClientId === undefined &&
103
+ sourceActorPrincipalId !== undefined
104
+ ) {
105
+ return unavailableSameUserClientError("host_bash");
108
106
  }
109
- resolvedTargetClientId =
110
- resolved.kind === "match" ? resolved.clientId : undefined;
111
107
  }
112
108
 
113
109
  // Targeted requests must be bound to the same authenticated user as the
@@ -5,7 +5,10 @@ import {
5
5
  assistantEventHub,
6
6
  broadcastMessage,
7
7
  } from "../runtime/assistant-event-hub.js";
8
- import { enforceSameActorOrErrorResult } from "../runtime/auth/same-actor.js";
8
+ import {
9
+ enforceSameActorOrErrorResult,
10
+ unavailableSameUserClientError,
11
+ } from "../runtime/auth/same-actor.js";
9
12
  import * as pendingInteractions from "../runtime/pending-interactions.js";
10
13
  import type { ToolExecutionResult } from "../tools/types.js";
11
14
  import { AssistantError, ErrorCode } from "../util/errors.js";
@@ -284,6 +287,17 @@ export class HostBrowserProxy {
284
287
  targetClientId,
285
288
  );
286
289
 
290
+ if (
291
+ preferredClient === undefined &&
292
+ sourceActorPrincipalId !== undefined &&
293
+ targetClientId === undefined
294
+ ) {
295
+ if (isExtensionOnlyMethod(input.cdpMethod)) {
296
+ return Promise.resolve(extensionRequiredResult(input.cdpMethod));
297
+ }
298
+ return Promise.resolve(unavailableSameUserClientError("host_browser"));
299
+ }
300
+
287
301
  // Same-user enforcement: when the caller's actor is known, refuse to
288
302
  // dispatch to a client owned by a different actor. This covers the
289
303
  // cross-client exposure case where a web/iOS turn for actor A would