@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
@@ -42,6 +42,11 @@ const MAX_HISTORY_ENTRIES = 10;
42
42
  const LOOP_DETECTION_WINDOW = 3;
43
43
  const CONSECUTIVE_UNCHANGED_WARNING_THRESHOLD = 2;
44
44
 
45
+ const SEQUENCE_TOOL = "computer_use_sequence";
46
+
47
+ const SCREENSHOT_OMITTED_MESSAGE =
48
+ "Screenshot omitted: the accessibility tree above is current. Pass include_screenshot: true on computer_use_observe to see the screen.";
49
+
45
50
  // computer_use_key combos that change only selection/cursor/clipboard state.
46
51
  // The AX tree models none of these, so they always produce an empty diff —
47
52
  // exempt them from the "NO VISIBLE EFFECT" signal (mirrors computer_use_wait).
@@ -113,6 +118,8 @@ export interface CuObservationResult {
113
118
  executionResult?: string;
114
119
  executionError?: string;
115
120
  userGuidance?: string;
121
+ /** Per-phase helper timings in milliseconds, absent on older helpers. */
122
+ timings?: Record<string, number>;
116
123
  }
117
124
 
118
125
  export interface ActionRecord {
@@ -161,6 +168,40 @@ function actionSignature(record: ActionRecord): string {
161
168
  return `${record.toolName}:${JSON.stringify(record.input)}`;
162
169
  }
163
170
 
171
+ /**
172
+ * The key a desktop's first look is tracked under. An untargeted request has
173
+ * no client id, so it gets a key no real id can take.
174
+ */
175
+ function observedTargetKey(targetClientId: string | undefined): string {
176
+ return targetClientId === undefined
177
+ ? "\u0000untargeted"
178
+ : `client:${targetClientId}`;
179
+ }
180
+
181
+ /** Whether `input` scopes the observation to one window or display. */
182
+ function hasCaptureTarget(input: Record<string, unknown>): boolean {
183
+ return (
184
+ Object.hasOwn(input, "capture_window_id") ||
185
+ Object.hasOwn(input, "captureWindowId") ||
186
+ Object.hasOwn(input, "captureDisplayId")
187
+ );
188
+ }
189
+
190
+ /**
191
+ * Whether the resolved client claimed `capability` on its connection. An
192
+ * untargeted request has no client to vouch for it, so it never qualifies.
193
+ */
194
+ function clientAdvertises(
195
+ targetClientId: string | undefined,
196
+ capability: "host_cu_window_capture" | "host_cu_sequence",
197
+ ): boolean {
198
+ const client =
199
+ targetClientId == null
200
+ ? undefined
201
+ : assistantEventHub.getClientById(targetClientId);
202
+ return client?.capabilities.includes(capability) ?? false;
203
+ }
204
+
164
205
  // ---------------------------------------------------------------------------
165
206
  // HostCuProxy
166
207
  // ---------------------------------------------------------------------------
@@ -172,8 +213,41 @@ export class HostCuProxy {
172
213
  private _previousAXTree: string | undefined;
173
214
  private _consecutiveUnchangedSteps = 0;
174
215
  private _actionHistory: ActionRecord[] = [];
175
- /** Owned request IDs mapped to whether their observation is scoped. */
176
- private _ownedRequests = new Map<string, boolean>();
216
+ /**
217
+ * Desktops an unscoped observation has come back from since the last reset,
218
+ * keyed by `observedTargetKey`. A first look is per desktop: having seen one
219
+ * machine says nothing about another the conversation switches to.
220
+ */
221
+ private _observedTargets = new Set<string>();
222
+ /**
223
+ * Bumped on every reset. A request carries the value it was dispatched
224
+ * under, so an observation that lands after a reset cannot restore state
225
+ * that reset cleared.
226
+ */
227
+ private _resetGeneration = 0;
228
+ /**
229
+ * Desktops a request has been sent to since the last reset, keyed by
230
+ * `observedTargetKey`, so the end of a task can reach each one.
231
+ */
232
+ private _dispatchedTargets = new Map<string, string | undefined>();
233
+ /**
234
+ * Owned request IDs mapped to whether their observation is scoped, whether
235
+ * the helper was told to skip the screenshot, and when the request was
236
+ * dispatched. The dispatch time gives the round trip, which is the part of
237
+ * a step the helper's own timings cannot see.
238
+ */
239
+ private _ownedRequests = new Map<
240
+ string,
241
+ {
242
+ scoped: boolean;
243
+ screenshotSkipped: boolean;
244
+ targetKey: string;
245
+ resetGeneration: number;
246
+ dispatchedAt: number;
247
+ toolName: string;
248
+ step: number;
249
+ }
250
+ >();
177
251
 
178
252
  constructor(maxSteps = loadConfig().maxStepsPerSession) {
179
253
  this._maxSteps = maxSteps;
@@ -288,11 +362,7 @@ export class HostCuProxy {
288
362
  }
289
363
  // Never dispatch this option to a legacy executor: it would forward the
290
364
  // unknown snake-case key and silently capture the entire desktop.
291
- const client =
292
- resolvedTargetClientId == null
293
- ? undefined
294
- : assistantEventHub.getClientById(resolvedTargetClientId);
295
- if (!client?.capabilities.includes("host_cu_window_capture")) {
365
+ if (!clientAdvertises(resolvedTargetClientId, "host_cu_window_capture")) {
296
366
  return Promise.resolve({
297
367
  content:
298
368
  "Window-only observation requires a connected client advertising host_cu_window_capture; update the desktop app before retrying. No capture was requested.",
@@ -300,14 +370,40 @@ export class HostCuProxy {
300
370
  });
301
371
  }
302
372
  }
303
- const scopedObservation =
304
- hasWindowTarget ||
305
- Object.hasOwn(input, "captureWindowId") ||
306
- Object.hasOwn(input, "captureDisplayId");
373
+ // An older helper answers an unknown tool name as done, which would end
374
+ // the session with nothing run.
375
+ if (
376
+ toolName === SEQUENCE_TOOL &&
377
+ !clientAdvertises(resolvedTargetClientId, "host_cu_sequence")
378
+ ) {
379
+ return Promise.resolve({
380
+ content:
381
+ "Batched actions require a desktop app that supports computer_use_sequence; update the desktop app, or send the actions one at a time. Nothing was run.",
382
+ isError: true,
383
+ });
384
+ }
385
+ const scopedObservation = hasCaptureTarget(input);
307
386
  if (scopedObservation) {
308
387
  this._previousAXTree = undefined;
309
388
  this._consecutiveUnchangedSteps = 0;
310
389
  }
390
+ const targetKey = observedTargetKey(resolvedTargetClientId);
391
+ // Pointing never reaches the helper's capture path, so its input goes out
392
+ // as given. Every other request carries the screenshot decision made now,
393
+ // at dispatch, and never the model-facing snake_case key.
394
+ let dispatchInput = input;
395
+ let screenshotSkipped = false;
396
+ if (toolName !== POINT_AT_PROXY_TOOL) {
397
+ const { include_screenshot: _includeScreenshot, ...rest } = input;
398
+ screenshotSkipped = !this.shouldAttachScreenshot(
399
+ toolName,
400
+ input,
401
+ targetKey,
402
+ );
403
+ dispatchInput = screenshotSkipped
404
+ ? { ...rest, includeScreenshot: false }
405
+ : rest;
406
+ }
311
407
  const requestId = uuid();
312
408
 
313
409
  return new Promise<ToolExecutionResult>((resolve, reject) => {
@@ -351,7 +447,16 @@ export class HostCuProxy {
351
447
  detachAbort = () => signal.removeEventListener("abort", onAbort);
352
448
  }
353
449
 
354
- this._ownedRequests.set(requestId, scopedObservation);
450
+ this._dispatchedTargets.set(targetKey, resolvedTargetClientId);
451
+ this._ownedRequests.set(requestId, {
452
+ scoped: scopedObservation,
453
+ screenshotSkipped,
454
+ targetKey,
455
+ resetGeneration: this._resetGeneration,
456
+ dispatchedAt: Date.now(),
457
+ toolName,
458
+ step: stepNumber,
459
+ });
355
460
 
356
461
  pendingInteractions.register(requestId, {
357
462
  conversationId,
@@ -375,7 +480,7 @@ export class HostCuProxy {
375
480
  requestId,
376
481
  conversationId,
377
482
  toolName,
378
- input,
483
+ input: dispatchInput,
379
484
  stepNumber,
380
485
  reasoning,
381
486
  ...(resolvedTargetClientId != null
@@ -403,7 +508,8 @@ export class HostCuProxy {
403
508
  requestId: string,
404
509
  observation: CuObservationResult,
405
510
  ): ToolExecutionResult | undefined {
406
- const scopedObservation = this._ownedRequests.get(requestId) ?? false;
511
+ const owned = this._ownedRequests.get(requestId);
512
+ const scopedObservation = owned?.scoped ?? false;
407
513
  this._ownedRequests.delete(requestId);
408
514
  const interaction = pendingInteractions.resolve(requestId, "answered");
409
515
  if (!interaction?.rpcResolve) {
@@ -411,9 +517,33 @@ export class HostCuProxy {
411
517
  return undefined;
412
518
  }
413
519
 
520
+ // Label the line from what this request was dispatched with, never from
521
+ // current proxy state. One model response can dispatch several CU tools,
522
+ // and the agent loop runs them concurrently, so a later call can advance
523
+ // the history and the step count before an earlier observation lands.
524
+ // Reading them here would file each measurement under whichever tool was
525
+ // dispatched last, and would mislabel computer_use_point_at, which never
526
+ // records an action at all.
527
+ if (owned) {
528
+ log.info(
529
+ {
530
+ requestId,
531
+ toolName: owned.toolName,
532
+ step: owned.step,
533
+ roundTripMs: Date.now() - owned.dispatchedAt,
534
+ ...(observation.timings ? { helper: observation.timings } : {}),
535
+ },
536
+ "Host CU step timings",
537
+ );
538
+ }
539
+
414
540
  // A targeted snapshot has no comparable action/diff baseline; neither it
415
541
  // nor the first desktop observation after it can imply "no visible effect".
416
- if (scopedObservation) {
542
+ // A response dispatched before the last reset belongs to a finished run,
543
+ // so it must not change any state the new run has started building.
544
+ const fromCurrentRun =
545
+ owned === undefined || owned.resetGeneration === this._resetGeneration;
546
+ if (scopedObservation && fromCurrentRun) {
417
547
  this._previousAXTree = undefined;
418
548
  this._consecutiveUnchangedSteps = 0;
419
549
  }
@@ -421,10 +551,24 @@ export class HostCuProxy {
421
551
  const comparableObservation = scopedObservation
422
552
  ? { ...observation, axDiff: undefined, secondaryWindows: undefined }
423
553
  : observation;
424
- if (!scopedObservation) {
554
+ // Pointing observes nothing, so its response leaves the screen state alone.
555
+ if (
556
+ fromCurrentRun &&
557
+ !scopedObservation &&
558
+ owned?.toolName !== POINT_AT_PROXY_TOOL
559
+ ) {
425
560
  this.updateStateFromObservation(comparableObservation);
561
+ // A desktop has had its first look only once pixels from it arrived,
562
+ // so a failed capture leaves the next request asking again.
563
+ if (owned && observation.screenshot) {
564
+ this._observedTargets.add(owned.targetKey);
565
+ }
426
566
  }
427
- const result = this.formatObservation(comparableObservation, prevAXTree);
567
+ const result = this.formatObservation(
568
+ comparableObservation,
569
+ prevAXTree,
570
+ owned?.screenshotSkipped ?? false,
571
+ );
428
572
  interaction.rpcResolve(result);
429
573
  return result;
430
574
  }
@@ -454,12 +598,65 @@ export class HostCuProxy {
454
598
  }
455
599
  }
456
600
 
601
+ /**
602
+ * Finish the task: tell every desktop this task drove that it is over, then
603
+ * reset. The notice rides `host_cu_cancel` with a fresh request ID, which
604
+ * matches nothing in flight; the helper treats any cancel as the run
605
+ * stopping and puts the pointer back where the user left it, instead of
606
+ * waiting out its idle fallback.
607
+ */
608
+ endTask(conversationId: string): void {
609
+ for (const targetClientId of this._dispatchedTargets.values()) {
610
+ try {
611
+ broadcastMessage(
612
+ {
613
+ type: "host_cu_cancel",
614
+ requestId: uuid(),
615
+ conversationId,
616
+ ...(targetClientId != null ? { targetClientId } : {}),
617
+ },
618
+ conversationId,
619
+ { targetClientId },
620
+ );
621
+ } catch {
622
+ // Best-effort: the helper still returns the pointer after its idle delay.
623
+ }
624
+ }
625
+ this.reset();
626
+ }
627
+
457
628
  /** Reset all CU state. Called on terminal tools (computer_use_done, etc.). */
458
629
  reset(): void {
630
+ this._dispatchedTargets.clear();
459
631
  this._stepCount = 0;
460
632
  this._previousAXTree = undefined;
461
633
  this._consecutiveUnchangedSteps = 0;
462
634
  this._actionHistory = [];
635
+ this._observedTargets.clear();
636
+ this._resetGeneration++;
637
+ }
638
+
639
+ /**
640
+ * Whether the request about to be dispatched should carry a screenshot. The
641
+ * accessibility tree comes back every step. Pixels come back after every
642
+ * action, so the model sees what its action did, on the first look since
643
+ * the last reset, for a window- or display-scoped capture, and when the
644
+ * model asks for them. Only a plain observation leaves whether the tree is
645
+ * enough to the model.
646
+ */
647
+ private shouldAttachScreenshot(
648
+ toolName: string,
649
+ input: Record<string, unknown>,
650
+ targetKey: string,
651
+ ): boolean {
652
+ if (toolName !== "computer_use_observe") {
653
+ return true;
654
+ }
655
+ return (
656
+ !this._observedTargets.has(targetKey) ||
657
+ hasCaptureTarget(input) ||
658
+ input.include_screenshot === true
659
+ );
463
660
  }
464
661
 
465
662
  // ---------------------------------------------------------------------------
@@ -474,6 +671,7 @@ export class HostCuProxy {
474
671
  formatObservation(
475
672
  obs: CuObservationResult,
476
673
  previousAXTree?: string,
674
+ screenshotSkipped = false,
477
675
  ): ToolExecutionResult {
478
676
  const prevTree = previousAXTree;
479
677
  const parts: string[] = [];
@@ -552,6 +750,15 @@ export class HostCuProxy {
552
750
  parts.push(...screenshotMeta);
553
751
  }
554
752
 
753
+ // Only a deliberate omission is announced, and only beside a tree the line
754
+ // can point at. A step that asked for pixels and got none is a capture
755
+ // failure, which this line must not disguise. A refused or failed step
756
+ // still carries the line, since that is where asking for pixels helps most.
757
+ const isError = obs.executionError != null;
758
+ if (screenshotSkipped && !obs.screenshot && obs.axTree) {
759
+ parts.push("", SCREENSHOT_OMITTED_MESSAGE);
760
+ }
761
+
555
762
  const content = parts.join("\n").trim() || "Action executed";
556
763
 
557
764
  const contentBlocks: ContentBlock[] = [];
@@ -566,8 +773,6 @@ export class HostCuProxy {
566
773
  });
567
774
  }
568
775
 
569
- const isError = obs.executionError != null;
570
-
571
776
  return {
572
777
  content: isError
573
778
  ? `Action failed: ${obs.executionError}\n\n${content}`
@@ -6,10 +6,10 @@ import {
6
6
  broadcastMessage,
7
7
  } from "../runtime/assistant-event-hub.js";
8
8
  import {
9
- ambiguousSameUserError,
10
9
  enforceSameActorOrErrorResult,
11
- pickSameUserAutoResolve,
10
+ pickMostRecentSameUserClient,
12
11
  snapshotHostProxyActorPrincipalId,
12
+ unavailableSameUserClientError,
13
13
  } from "../runtime/auth/same-actor.js";
14
14
  import * as pendingInteractions from "../runtime/pending-interactions.js";
15
15
  import { readAudioBase64 } from "../tools/shared/filesystem/audio-read.js";
@@ -102,19 +102,17 @@ export class HostFileProxy {
102
102
  });
103
103
  }
104
104
  } else {
105
- // Auto-resolve to the unique same-user client. Reject ambiguous
106
- // (multi-machine) cases so a single targeted-style request cannot
107
- // fan out across the user's machines.
108
- const resolved = pickSameUserAutoResolve({
105
+ resolvedTargetClientId = pickMostRecentSameUserClient({
109
106
  hub: assistantEventHub,
110
107
  capability: "host_file",
111
108
  sourceActorPrincipalId,
112
109
  });
113
- if (resolved.kind === "ambiguous") {
114
- return Promise.resolve(ambiguousSameUserError("host_file"));
110
+ if (
111
+ resolvedTargetClientId === undefined &&
112
+ sourceActorPrincipalId !== undefined
113
+ ) {
114
+ return Promise.resolve(unavailableSameUserClientError("host_file"));
115
115
  }
116
- resolvedTargetClientId =
117
- resolved.kind === "match" ? resolved.clientId : undefined;
118
116
  }
119
117
 
120
118
  // Same-user check: targeted host_file requests must be bound to the same
@@ -105,12 +105,13 @@ export const HOST_PROXY_SKILL_PREACTIVATIONS: ReadonlyArray<{
105
105
  */
106
106
  /**
107
107
  * Capabilities a client asks for on its connection rather than getting from
108
- * what it is. Both ride the host_cu transport and are answered only by a
108
+ * what it is. Each rides the host_cu transport and is answered only by a
109
109
  * macOS build new enough to send the header (`events-routes.ts`), so the
110
110
  * interface alone cannot say whether one is really there.
111
111
  */
112
112
  const NEGOTIATED_CAPABILITIES: ReadonlySet<HostProxyCapability> = new Set([
113
113
  "host_cu_window_capture",
114
+ "host_cu_sequence",
114
115
  "host_cu_annotate",
115
116
  ]);
116
117
 
@@ -10,10 +10,10 @@ import {
10
10
  broadcastMessage,
11
11
  } from "../runtime/assistant-event-hub.js";
12
12
  import {
13
- ambiguousSameUserError,
14
13
  enforceSameActorOrErrorResult,
15
- pickSameUserAutoResolve,
14
+ pickMostRecentSameUserClient,
16
15
  snapshotHostProxyActorPrincipalId,
16
+ unavailableSameUserClientError,
17
17
  } from "../runtime/auth/same-actor.js";
18
18
  import * as pendingInteractions from "../runtime/pending-interactions.js";
19
19
  import type { ToolExecutionResult } from "../tools/types.js";
@@ -162,19 +162,17 @@ export class HostTransferProxy {
162
162
  });
163
163
  }
164
164
  } else {
165
- // Auto-resolve to the unique same-user client; reject ambiguous
166
- // (multi-machine) cases so a single targeted-style transfer cannot
167
- // fan out across the user's machines.
168
- const resolved = pickSameUserAutoResolve({
165
+ resolvedTargetClientId = pickMostRecentSameUserClient({
169
166
  hub: assistantEventHub,
170
167
  capability: "host_file",
171
168
  sourceActorPrincipalId,
172
169
  });
173
- if (resolved.kind === "ambiguous") {
174
- return Promise.resolve(ambiguousSameUserError("host_file"));
170
+ if (
171
+ resolvedTargetClientId === undefined &&
172
+ sourceActorPrincipalId !== undefined
173
+ ) {
174
+ return Promise.resolve(unavailableSameUserClientError("host_file"));
175
175
  }
176
- resolvedTargetClientId =
177
- resolved.kind === "match" ? resolved.clientId : undefined;
178
176
  }
179
177
 
180
178
  if (resolvedTargetClientId != null) {
@@ -364,19 +362,17 @@ export class HostTransferProxy {
364
362
  });
365
363
  }
366
364
  } else {
367
- // Auto-resolve to the unique same-user client; reject ambiguous
368
- // (multi-machine) cases so a single targeted-style transfer cannot
369
- // fan out across the user's machines.
370
- const resolved = pickSameUserAutoResolve({
365
+ resolvedTargetClientId = pickMostRecentSameUserClient({
371
366
  hub: assistantEventHub,
372
367
  capability: "host_file",
373
368
  sourceActorPrincipalId,
374
369
  });
375
- if (resolved.kind === "ambiguous") {
376
- return Promise.resolve(ambiguousSameUserError("host_file"));
370
+ if (
371
+ resolvedTargetClientId === undefined &&
372
+ sourceActorPrincipalId !== undefined
373
+ ) {
374
+ return Promise.resolve(unavailableSameUserClientError("host_file"));
377
375
  }
378
- resolvedTargetClientId =
379
- resolved.kind === "match" ? resolved.clientId : undefined;
380
376
  }
381
377
 
382
378
  if (resolvedTargetClientId != null) {
@@ -71,7 +71,6 @@ import { ensureCompleteCustomProfiles } from "../workspace/custom-profile-ensure
71
71
  import { ensureDefaultProvider } from "../workspace/default-provider-ensure.js";
72
72
  import { WORKSPACE_MIGRATIONS } from "../workspace/migrations/registry.js";
73
73
  import { runWorkspaceMigrations } from "../workspace/migrations/runner.js";
74
- import { startAppSourceWatcher } from "./app-source-watcher.js";
75
74
  import { startConfigWatcher } from "./config-watcher.js";
76
75
  import { startConversationEvictor } from "./conversation-evictor.js";
77
76
  import { writePid } from "./daemon-control.js";
@@ -706,10 +705,6 @@ export async function runDaemon(): Promise<void> {
706
705
  // config, and broadcast the relevant resource-changed events to clients.
707
706
  startConfigWatcher();
708
707
 
709
- // Watch app source directories so edits recompile + refresh surfaces across
710
- // all conversations.
711
- startAppSourceWatcher();
712
-
713
708
  // Start the CLI IPC server. Throws on EADDRINUSE to abort startup when another
714
709
  // daemon already holds the socket, so this process never runs background jobs
715
710
  // against the shared database as an unmanageable duplicate.
@@ -1,11 +1,11 @@
1
1
  /**
2
2
  * Shared MCP reload business logic.
3
3
  *
4
- * Called by the ConfigWatcher when config.json changes or a reload signal
4
+ * Called by the ConfigWatcher when mcp.json changes or a reload signal
5
5
  * file is detected, so the daemon automatically reconnects MCP servers.
6
6
  */
7
7
 
8
- import { getConfig, invalidateConfigCache } from "../config/loader.js";
8
+ import { invalidateConfigCache } from "../config/loader.js";
9
9
  import {
10
10
  buildEffectiveMcpConfig,
11
11
  pluginMcpServersChangedSinceLastBuild,
@@ -13,6 +13,7 @@ import {
13
13
  import { getMcpServerManager } from "../mcp/manager.js";
14
14
  import { migrateLegacyMcpHeaders } from "../mcp/mcp-header-store.js";
15
15
  import { signalMcpReloaded } from "../mcp/reload-signal.js";
16
+ import { loadWorkspaceMcpConfig } from "../mcp/workspace-mcp-config.js";
16
17
  import { createMcpToolsFromServer } from "../tools/mcp/mcp-tool-factory.js";
17
18
  import { registerMcpTools, unregisterAllMcpTools } from "../tools/registry.js";
18
19
  import { getLogger } from "../util/logger.js";
@@ -95,16 +96,15 @@ async function doReload(): Promise<McpReloadResult> {
95
96
  // If the config is broken we abort early, preserving the current
96
97
  // working MCP setup instead of leaving zero servers.
97
98
  invalidateConfigCache();
98
- const config = getConfig();
99
99
 
100
100
  // 2. Stop existing MCP servers + unregister their tools
101
101
  await manager.stop();
102
102
  unregisterAllMcpTools();
103
103
 
104
104
  // Plugins are re-read here too: installing or removing one changes the
105
- // server set exactly like editing config.json does, and both arrive
105
+ // server set exactly like editing mcp.json does, and both arrive
106
106
  // through this same reload.
107
- const mcpConfig = buildEffectiveMcpConfig(config.mcp);
107
+ const mcpConfig = buildEffectiveMcpConfig(loadWorkspaceMcpConfig());
108
108
  const serverIds = Object.keys(mcpConfig.servers);
109
109
 
110
110
  // 3. Restart MCP servers
@@ -13,6 +13,7 @@ export const SYNC_TAGS = {
13
13
  assistantConfig: "assistant:self:config",
14
14
  assistantSounds: "assistant:self:sounds",
15
15
  assistantSchedules: "assistant:self:schedules",
16
+ assistantDesktop: "assistant:self:desktop",
16
17
  assistantTheme: "assistant:self:theme",
17
18
  appsList: "apps:list",
18
19
  documentsList: "documents:list",
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Tools run commands in their own process group (`detached: true`) and, on
5
5
  * timeout/abort, group-kill with `process.kill(-pgid, SIGKILL)` (the bash and
6
- * host_bash tools, the skill sandbox runner, and the debug-bash route). The
6
+ * host_bash tools, and the skill sandbox runner). The
7
7
  * immediate child is reaped by Bun/libuv, but its descendants — e.g. git's
8
8
  * transport helpers or a skill runner's `bun` process — were never spawned by
9
9
  * the daemon, so when the group dies they reparent to PID 1. When the daemon
@@ -63,7 +63,7 @@ export async function initializeProvidersAndTools(
63
63
  // Start MCP servers — workspace-configured and plugin-declared alike —
64
64
  // and register their tools. Shared with the schedule worker, which hosts
65
65
  // agent turns in its own process and so needs its own connections.
66
- await startConfiguredMcpServers(config.mcp);
66
+ await startConfiguredMcpServers();
67
67
 
68
68
  log.info("Daemon startup: providers and tools initialized");
69
69
  }
@@ -28,6 +28,7 @@ import {
28
28
  unwrapExternalContentForDisplay,
29
29
  wrapUntrustedContent,
30
30
  } from "../security/untrusted-content.js";
31
+ import { safeStringSlice } from "../util/unicode.js";
31
32
 
32
33
  /**
33
34
  * Slack-style emoji names render in colon form. Anything else (a unicode
@@ -53,7 +54,7 @@ function snippetOf(text: string): string {
53
54
  if (flat.length <= TARGET_SNIPPET_MAX_CHARS) {
54
55
  return flat;
55
56
  }
56
- return `${flat.slice(0, TARGET_SNIPPET_MAX_CHARS)}...`;
57
+ return `${safeStringSlice(flat, 0, TARGET_SNIPPET_MAX_CHARS)}...`;
57
58
  }
58
59
 
59
60
  /**
@@ -24,11 +24,7 @@ import { browserManager } from "../tools/browser/browser-manager.js";
24
24
  import { getLogger } from "../util/logger.js";
25
25
  import { APP_VERSION } from "../version.js";
26
26
  import { getEnrichmentService } from "../workspace/commit-message-enrichment-service.js";
27
- import {
28
- commitAllPendingWorkspaceChanges,
29
- stopWorkspaceHeartbeatService,
30
- } from "../workspace/heartbeat-service.js";
31
- import { stopAppSourceWatcher } from "./app-source-watcher.js";
27
+ import { commitAllPendingWorkspaceChanges } from "../workspace/heartbeat-service.js";
32
28
  import { stopConfigWatcher } from "./config-watcher.js";
33
29
  import { stopConversationEvictor } from "./conversation-evictor.js";
34
30
  import { stopConversations } from "./conversation-store.js";
@@ -98,9 +94,6 @@ async function shutdown(): Promise<void> {
98
94
  }, 30_000);
99
95
  forceTimer.unref();
100
96
 
101
- // Workspace git heartbeat timer lives in the monitor process. This
102
- // in-process stop is a no-op unless the singleton was started here.
103
- await stopWorkspaceHeartbeatService();
104
97
  await stopHeartbeatService();
105
98
 
106
99
  // Stop the periodic consent-cache refresh (a daemon-owned interval).
@@ -134,7 +127,6 @@ async function shutdown(): Promise<void> {
134
127
  disposeAcpSessionManager();
135
128
  stopConversationEvictor();
136
129
  stopConfigWatcher();
137
- stopAppSourceWatcher();
138
130
  stopCliIpcServer();
139
131
  stopConversations();
140
132
  await stopCes();