@vellumai/assistant 0.12.0 → 0.12.1-dev.202609142113.d1590bc

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 (541) hide show
  1. package/AGENTS.md +5 -1
  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/openapi.yaml +327 -52
  6. package/package.json +2 -1
  7. package/scripts/smoke-desktop-window-theme.ts +186 -0
  8. package/src/__tests__/always-loaded-tools-guard.test.ts +18 -10
  9. package/src/__tests__/anthropic-provider.test.ts +0 -63
  10. package/src/__tests__/app-compiler.test.ts +6 -0
  11. package/src/__tests__/app-control-tool-schemas.test.ts +5 -2
  12. package/src/__tests__/app-dir-path-guard.test.ts +1 -3
  13. package/src/__tests__/assistant-event-hub-machine-name.test.ts +6 -7
  14. package/src/__tests__/assistant-event-hub.test.ts +44 -0
  15. package/src/__tests__/computer-use-skill-manifest-regression.test.ts +1 -1
  16. package/src/__tests__/computer-use-skill-proxy-bridge.test.ts +5 -2
  17. package/src/__tests__/computer-use-tools.test.ts +19 -7
  18. package/src/__tests__/config-schema-cmd.test.ts +2 -2
  19. package/src/__tests__/config-schema.test.ts +28 -29
  20. package/src/__tests__/config-watcher.test.ts +23 -8
  21. package/src/__tests__/connection-model-compat.test.ts +2 -2
  22. package/src/__tests__/conversation-key-store-disk-view.test.ts +1 -1
  23. package/src/__tests__/conversation-pairing.test.ts +26 -0
  24. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +4 -10
  25. package/src/__tests__/conversation-queue.test.ts +48 -207
  26. package/src/__tests__/conversation-retry-discard.test.ts +54 -1
  27. package/src/__tests__/conversation-skill-tools.test.ts +27 -0
  28. package/src/__tests__/conversation-surfaces-app-control.test.ts +8 -3
  29. package/src/__tests__/conversation-surfaces-task-progress.test.ts +67 -0
  30. package/src/__tests__/conversation-title-service.test.ts +14 -10
  31. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +3 -5
  32. package/src/__tests__/conversation-tool-setup-attribution.test.ts +51 -0
  33. package/src/__tests__/credential-record-retire.test.ts +171 -1
  34. package/src/__tests__/credential-routes.test.ts +212 -3
  35. package/src/__tests__/credential-security-invariants.test.ts +4 -2
  36. package/src/__tests__/cu-unified-flow.test.ts +8 -2
  37. package/src/__tests__/document-sync-tags.test.ts +25 -0
  38. package/src/__tests__/drain-kick-guard.test.ts +0 -2
  39. package/src/__tests__/drain-requeue-on-contention.test.ts +0 -6
  40. package/src/__tests__/evict-conversations-for-reload.test.ts +1 -1
  41. package/src/__tests__/git-porcelain.test.ts +47 -0
  42. package/src/__tests__/heartbeat-service.test.ts +56 -40
  43. package/src/__tests__/helpers/set-workspace-mcp.ts +11 -0
  44. package/src/__tests__/host-bash-proxy.test.ts +26 -45
  45. package/src/__tests__/host-browser-proxy.test.ts +6 -17
  46. package/src/__tests__/host-cu-proxy.test.ts +539 -0
  47. package/src/__tests__/host-file-proxy-targeted.test.ts +23 -0
  48. package/src/__tests__/host-file-proxy.test.ts +11 -23
  49. package/src/__tests__/host-transfer-proxy-targeted.test.ts +30 -9
  50. package/src/__tests__/list-messages-camera-frame.test.ts +121 -0
  51. package/src/__tests__/llm-catalog-parity.test.ts +25 -0
  52. package/src/__tests__/mcp-auth-routes.test.ts +6 -9
  53. package/src/__tests__/mcp-config-secret-boundary.test.ts +78 -136
  54. package/src/__tests__/mcp-health-check.test.ts +8 -11
  55. package/src/__tests__/mcp-list-plugin-servers.test.ts +8 -10
  56. package/src/__tests__/message-deleted-hook-dispatch.test.ts +119 -0
  57. package/src/__tests__/messages-after-tiebreaker.test.ts +91 -0
  58. package/src/__tests__/messages-read-boundary-guard.test.ts +1 -1
  59. package/src/__tests__/messaging-send-tool.test.ts +355 -548
  60. package/src/__tests__/model-intents.test.ts +3 -0
  61. package/src/__tests__/notification-vellum-adapter.test.ts +85 -2
  62. package/src/__tests__/oauth-commands-routes.test.ts +113 -0
  63. package/src/__tests__/oauth-provider-profiles.test.ts +18 -0
  64. package/src/__tests__/openai-provider.test.ts +1 -0
  65. package/src/__tests__/outlook-messaging-provider.test.ts +6 -6
  66. package/src/__tests__/platform-callback-registration.test.ts +8 -16
  67. package/src/__tests__/platform-client-verify-credential.test.ts +2 -0
  68. package/src/__tests__/plugin-import-boundary-guard.test.ts +14 -0
  69. package/src/__tests__/qdrant-manager.test.ts +132 -0
  70. package/src/__tests__/resolve-app.test.ts +135 -0
  71. package/src/__tests__/scaffold-managed-skill-tool.test.ts +229 -6
  72. package/src/__tests__/script-proxy-certs.test.ts +35 -0
  73. package/src/__tests__/service-account-token.test.ts +247 -0
  74. package/src/__tests__/skill-feature-flags-integration.test.ts +1 -1
  75. package/src/__tests__/skill-feature-flags.test.ts +2 -2
  76. package/src/__tests__/skills.test.ts +9 -4
  77. package/src/__tests__/source-fingerprint.test.ts +184 -0
  78. package/src/__tests__/subagent-tool-gate-mode.test.ts +19 -3
  79. package/src/__tests__/subagent-tools.test.ts +3 -2
  80. package/src/__tests__/telephony-tts-guard.test.ts +38 -3
  81. package/src/__tests__/test-support/computer-use-skill-harness.ts +3 -2
  82. package/src/__tests__/unicode.test.ts +13 -0
  83. package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +4 -0
  84. package/src/__tests__/watch-retro-tool-availability.test.ts +8 -17
  85. package/src/__tests__/workspace-git-service.test.ts +26 -0
  86. package/src/__tests__/workspace-migration-154-repair-retired-codex-gpt-5-4-model-ids.test.ts +336 -0
  87. package/src/acp/__tests__/agent-process.test.ts +258 -4
  88. package/src/acp/__tests__/auth-required.test.ts +96 -0
  89. package/src/acp/__tests__/client-handler.test.ts +90 -2
  90. package/src/acp/__tests__/helpers/acp-model-option.ts +51 -0
  91. package/src/acp/__tests__/session-manager-persistence.test.ts +5 -1
  92. package/src/acp/__tests__/session-manager-resume.test.ts +270 -6
  93. package/src/acp/__tests__/session-manager-usage.test.ts +4 -2
  94. package/src/acp/__tests__/session-manager.test.ts +895 -4
  95. package/src/acp/agent-process.test.ts +4 -1
  96. package/src/acp/agent-process.ts +96 -10
  97. package/src/acp/auth-required.ts +50 -1
  98. package/src/acp/auto-install.test.ts +9 -7
  99. package/src/acp/client-handler.ts +16 -3
  100. package/src/acp/model-config.test.ts +166 -0
  101. package/src/acp/model-config.ts +138 -0
  102. package/src/acp/resolve-agent.test.ts +220 -6
  103. package/src/acp/resolve-agent.ts +88 -23
  104. package/src/acp/session-manager.test.ts +196 -3
  105. package/src/acp/session-manager.ts +441 -18
  106. package/src/acp/types.ts +50 -5
  107. package/src/agent/loop.ts +1 -0
  108. package/src/api/events/acp-session-model-update.test.ts +88 -0
  109. package/src/api/events/acp-session-model-update.ts +46 -0
  110. package/src/api/events/app-files-changed.ts +4 -3
  111. package/src/api/events/notification-intent.ts +2 -0
  112. package/src/api/events/user-message-echo.ts +7 -1
  113. package/src/api/index.ts +6 -0
  114. package/src/api/responses/conversation-message.ts +5 -0
  115. package/src/apps/app-store.ts +1 -1
  116. package/src/apps/resolve-app.ts +81 -0
  117. package/src/apps/source-fingerprint.ts +266 -0
  118. package/src/avatar/identity-avatar.ts +7 -2
  119. package/src/bundler/app-compiler.ts +15 -0
  120. package/src/calls/__tests__/barge-in-guard.test.ts +81 -0
  121. package/src/{__tests__ → calls/__tests__}/call-controller.test.ts +503 -82
  122. package/src/{__tests__ → calls/__tests__}/media-stream-audio-transcode.test.ts +1 -1
  123. package/src/{__tests__ → calls/__tests__}/media-stream-output.test.ts +37 -8
  124. package/src/{__tests__ → calls/__tests__}/media-stream-parser.test.ts +1 -1
  125. package/src/{__tests__ → calls/__tests__}/media-stream-server-integration.test.ts +150 -57
  126. package/src/{__tests__ → calls/__tests__}/media-stream-stt-session.test.ts +5 -5
  127. package/src/{__tests__ → calls/__tests__}/media-turn-detector.test.ts +1 -1
  128. package/src/{live-voice → calls}/__tests__/progress-narration.test.ts +4 -4
  129. package/src/{live-voice → calls}/__tests__/progress-phrases.test.ts +1 -1
  130. package/src/calls/__tests__/voice-control-protocol.test.ts +58 -25
  131. package/src/calls/__tests__/voice-leg-coordinator.test.ts +229 -0
  132. package/src/calls/__tests__/voice-progress-cadence.test.ts +274 -0
  133. package/src/{__tests__/voice-session-bridge.test.ts → calls/__tests__/voice-session-bridge-turn-flow.test.ts} +20 -12
  134. package/src/calls/__tests__/voice-session-bridge.test.ts +37 -1
  135. package/src/calls/__tests__/voice-triage-escalate.test.ts +105 -0
  136. package/src/calls/barge-in-guard.ts +118 -0
  137. package/src/calls/call-constants.ts +24 -4
  138. package/src/calls/call-controller.ts +499 -199
  139. package/src/calls/call-transport.ts +16 -0
  140. package/src/calls/media-stream-output.ts +32 -0
  141. package/src/calls/media-stream-server.ts +82 -30
  142. package/src/calls/media-stream-stt-session.ts +15 -0
  143. package/src/{live-voice → calls}/progress-narration.ts +2 -2
  144. package/src/calls/resolve-call-tts-provider.ts +33 -2
  145. package/src/calls/tts-call-strategy.ts +9 -5
  146. package/src/calls/types.ts +2 -0
  147. package/src/calls/voice-control-protocol.ts +53 -21
  148. package/src/calls/voice-leg-coordinator.ts +189 -0
  149. package/src/calls/voice-progress-cadence.ts +423 -0
  150. package/src/calls/voice-session-bridge.ts +59 -46
  151. package/src/calls/voice-triage-escalate.ts +175 -56
  152. package/src/cli/commands/__tests__/apps.test.ts +245 -0
  153. package/src/cli/commands/__tests__/bash.test.ts +53 -0
  154. package/src/cli/commands/__tests__/roadmap.help.test.ts +19 -0
  155. package/src/cli/commands/apps.help.ts +67 -13
  156. package/src/cli/commands/apps.ts +183 -0
  157. package/src/cli/commands/bash.help.ts +8 -13
  158. package/src/cli/commands/bash.ts +7 -25
  159. package/src/cli/commands/channels/__tests__/send.test.ts +218 -0
  160. package/src/cli/commands/channels/index.help.ts +70 -2
  161. package/src/cli/commands/channels/index.ts +16 -3
  162. package/src/cli/commands/channels/send.ts +128 -0
  163. package/src/cli/commands/credentials.help.ts +6 -2
  164. package/src/cli/commands/credentials.ts +2 -2
  165. package/src/cli/commands/mcp.help.ts +8 -8
  166. package/src/cli/commands/mcp.ts +1 -1
  167. package/src/cli/commands/roadmap.help.ts +5 -1
  168. package/src/cli/commands/roadmap.ts +1 -1
  169. package/src/cli/lib/bundled-marketplace.json +103 -90
  170. package/src/config/__tests__/default-profile-catalog.test.ts +22 -0
  171. package/src/config/__tests__/memory-retrospective-schema.test.ts +15 -1
  172. package/src/config/__tests__/platform-identity.test.ts +255 -0
  173. package/src/config/__tests__/platform-rehydration.test.ts +99 -9
  174. package/src/config/acp-agent-write.ts +43 -0
  175. package/src/config/acp-defaults.test.ts +13 -3
  176. package/src/config/acp-defaults.ts +23 -6
  177. package/src/config/acp-schema.test.ts +81 -0
  178. package/src/config/acp-schema.ts +30 -2
  179. package/src/config/bundled-skills/acp/SKILL.md +22 -14
  180. package/src/config/bundled-skills/acp/TOOLS.json +5 -1
  181. package/src/config/bundled-skills/app-builder/SKILL.md +12 -9
  182. package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +1 -1
  183. package/src/config/bundled-skills/computer-use/SKILL.md +20 -2
  184. package/src/config/bundled-skills/computer-use/TOOLS.json +99 -0
  185. package/src/config/bundled-skills/computer-use/tools/computer-use-sequence.ts +12 -0
  186. package/src/config/bundled-skills/media-processing/services/preprocess.ts +2 -1
  187. package/src/config/bundled-skills/media-processing/services/processing-pipeline.ts +2 -1
  188. package/src/config/bundled-skills/media-processing/tools/generate-clip.ts +2 -1
  189. package/src/config/bundled-skills/messaging/SKILL.md +3 -3
  190. package/src/config/bundled-skills/messaging/TOOLS.json +3 -3
  191. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +67 -139
  192. package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +1 -1
  193. package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +2 -1
  194. package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +2 -1
  195. package/src/config/bundled-skills/schedule/SKILL.md +3 -4
  196. package/src/config/bundled-skills/screen-annotation/SKILL.md +8 -1
  197. package/src/config/bundled-skills/screen-annotation/tools/screen-annotation.test.ts +4 -1
  198. package/src/config/bundled-skills/skill-management/TOOLS.json +6 -2
  199. package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +5 -2
  200. package/src/config/default-profile-catalog.ts +1 -1
  201. package/src/config/env.ts +2 -2
  202. package/src/config/feature-flag-registry.json +24 -12
  203. package/src/config/loader.ts +1 -0
  204. package/src/config/memory-v3-gate.test.ts +31 -0
  205. package/src/config/memory-v3-gate.ts +20 -16
  206. package/src/config/platform-identity.ts +201 -0
  207. package/src/config/platform-rehydration.ts +11 -27
  208. package/src/config/schema.ts +2 -2
  209. package/src/config/schemas/__tests__/live-voice.test.ts +8 -158
  210. package/src/config/schemas/__tests__/voice.test.ts +148 -0
  211. package/src/config/schemas/calls.ts +1 -28
  212. package/src/config/schemas/live-voice.ts +1 -173
  213. package/src/config/schemas/mcp.ts +1 -1
  214. package/src/config/schemas/memory-retrospective.ts +10 -0
  215. package/src/config/schemas/services.ts +8 -0
  216. package/src/config/schemas/skills.ts +0 -14
  217. package/src/config/schemas/voice.ts +150 -0
  218. package/src/credential-execution/process-manager.test.ts +18 -19
  219. package/src/credential-health/credential-health-service.ts +11 -17
  220. package/src/daemon/__tests__/conversation-tool-setup.test.ts +66 -121
  221. package/src/daemon/__tests__/plugin-mcp-reconcile.test.ts +4 -1
  222. package/src/daemon/__tests__/skill-memory-refresh-mtime.test.ts +133 -0
  223. package/src/daemon/app-change-notify.ts +45 -0
  224. package/src/daemon/config-watcher.ts +13 -194
  225. package/src/daemon/conversation-error.ts +3 -2
  226. package/src/daemon/conversation-media-retry.ts +2 -1
  227. package/src/daemon/conversation-process.ts +35 -64
  228. package/src/daemon/conversation-runtime-assembly.ts +2 -1
  229. package/src/daemon/conversation-skill-tools.ts +13 -31
  230. package/src/daemon/conversation-store.ts +1 -1
  231. package/src/daemon/conversation-surfaces.ts +15 -9
  232. package/src/daemon/conversation-tool-setup.ts +37 -43
  233. package/src/daemon/conversation.ts +0 -3
  234. package/src/daemon/dictation-profile-store.ts +2 -1
  235. package/src/daemon/handlers/shared.ts +2 -1
  236. package/src/daemon/handlers/skills.ts +6 -3
  237. package/src/daemon/host-bash-proxy.ts +8 -12
  238. package/src/daemon/host-browser-proxy.ts +15 -1
  239. package/src/daemon/host-cu-proxy.ts +186 -19
  240. package/src/daemon/host-file-proxy.ts +8 -10
  241. package/src/daemon/host-proxy-preactivation.ts +2 -1
  242. package/src/daemon/host-transfer-proxy.ts +14 -18
  243. package/src/daemon/lifecycle.ts +0 -5
  244. package/src/daemon/mcp-reload-service.ts +5 -5
  245. package/src/daemon/message-types/sync.ts +1 -0
  246. package/src/daemon/orphan-reaper.ts +1 -1
  247. package/src/daemon/providers-setup.ts +1 -1
  248. package/src/daemon/reaction-history-render.ts +2 -1
  249. package/src/daemon/shutdown-handlers.ts +1 -9
  250. package/src/daemon/skill-memory-refresh.ts +64 -0
  251. package/src/daemon/tool-side-effects.ts +7 -18
  252. package/src/desktop/__tests__/fake-desktop.ts +119 -0
  253. package/src/desktop/desktop-chrome-policy.test.ts +71 -0
  254. package/src/desktop/desktop-chrome-policy.ts +42 -0
  255. package/src/desktop/desktop-dependencies.test.ts +102 -0
  256. package/src/desktop/desktop-dependencies.ts +306 -0
  257. package/src/desktop/desktop-feature.test.ts +18 -0
  258. package/src/desktop/desktop-feature.ts +19 -0
  259. package/src/desktop/desktop-panel-config.test.ts +135 -0
  260. package/src/desktop/desktop-panel-config.ts +149 -0
  261. package/src/desktop/desktop-session-manager.test.ts +627 -0
  262. package/src/desktop/desktop-session-manager.ts +814 -0
  263. package/src/desktop/desktop-stream-bridge.test.ts +395 -0
  264. package/src/desktop/desktop-stream-bridge.ts +214 -0
  265. package/src/desktop/desktop-wallpaper.test.ts +191 -0
  266. package/src/desktop/desktop-wallpaper.ts +113 -0
  267. package/src/desktop/desktop-window-theme.ts +170 -0
  268. package/src/heartbeat/heartbeat-service.ts +21 -3
  269. package/src/home/__tests__/feed-writer.test.ts +22 -0
  270. package/src/home/feed-writer.ts +11 -2
  271. package/src/hooks/types.ts +33 -0
  272. package/src/i18n/AGENTS.md +22 -0
  273. package/src/i18n/__tests__/i18n.test.ts +108 -0
  274. package/src/i18n/index.ts +16 -0
  275. package/src/i18n/locales.ts +93 -0
  276. package/src/i18n/messages.ts +61 -0
  277. package/src/i18n/title.ts +25 -0
  278. package/src/inbound/platform-callback-registration.ts +4 -5
  279. package/src/live-voice/__tests__/live-voice-metrics.test.ts +0 -3
  280. package/src/live-voice/__tests__/live-voice-progress.test.ts +15 -15
  281. package/src/live-voice/__tests__/live-voice-sight-frame-echo.test.ts +238 -0
  282. package/src/live-voice/__tests__/live-voice-vad.test.ts +2 -2
  283. package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +13 -69
  284. package/src/live-voice/live-voice-photo.ts +25 -6
  285. package/src/live-voice/live-voice-session.ts +213 -693
  286. package/src/live-voice/protocol.ts +0 -2
  287. package/src/mcp/__tests__/effective-config.test.ts +2 -2
  288. package/src/mcp/__tests__/workspace-mcp-config.test.ts +135 -0
  289. package/src/mcp/effective-config.ts +3 -3
  290. package/src/mcp/mcp-header-store.ts +29 -45
  291. package/src/mcp/spec-schema.ts +111 -0
  292. package/src/mcp/startup.ts +8 -4
  293. package/src/mcp/workspace-mcp-config.ts +188 -0
  294. package/src/messaging/provider.ts +10 -3
  295. package/src/messaging/providers/__tests__/proactive-address.test.ts +95 -0
  296. package/src/messaging/providers/callback-routing.ts +18 -0
  297. package/src/messaging/providers/channel-transport.ts +56 -0
  298. package/src/messaging/providers/discord/transport.ts +17 -0
  299. package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +6 -17
  300. package/src/messaging/providers/slack/adapter.ts +7 -44
  301. package/src/messaging/providers/slack/transport.ts +16 -0
  302. package/src/messaging/providers/telegram-bot/adapter.ts +1 -48
  303. package/src/messaging/providers/telegram-bot/client.ts +0 -26
  304. package/src/messaging/providers/telegram-bot/transport.ts +20 -0
  305. package/src/messaging/providers/whatsapp/adapter.ts +1 -48
  306. package/src/messaging/providers/whatsapp/transport.ts +18 -0
  307. package/src/monitoring/__tests__/workspace-git-heartbeat.test.ts +8 -0
  308. package/src/notifications/__tests__/assistant-reply-producer.test.ts +55 -0
  309. package/src/notifications/__tests__/guardian-feed-projection.test.ts +158 -2
  310. package/src/notifications/__tests__/home-feed-side-effect.test.ts +153 -3
  311. package/src/notifications/__tests__/schedule-result-producer.test.ts +99 -3
  312. package/src/notifications/adapters/macos.ts +3 -0
  313. package/src/notifications/assistant-reply-producer.ts +8 -3
  314. package/src/notifications/conversation-pairing.ts +36 -16
  315. package/src/notifications/delivered-post-record.ts +11 -1
  316. package/src/notifications/guardian-feed-projection.ts +125 -5
  317. package/src/notifications/home-feed-side-effect.ts +55 -7
  318. package/src/notifications/schedule-result-producer.ts +66 -22
  319. package/src/oauth/AGENTS.md +2 -0
  320. package/src/oauth/__tests__/seed-providers-managed.test.ts +54 -0
  321. package/src/oauth/byo-connection.test.ts +105 -0
  322. package/src/oauth/byo-connection.ts +36 -6
  323. package/src/oauth/connection-resolver.test.ts +73 -0
  324. package/src/oauth/connection-resolver.ts +95 -2
  325. package/src/oauth/scope-utils.ts +24 -0
  326. package/src/oauth/seed-providers.ts +204 -25
  327. package/src/outbound-proxy/certs.ts +3 -5
  328. package/src/permissions/checker.test.ts +11 -6
  329. package/src/persistence/compaction-log-store-clickhouse.ts +3 -3
  330. package/src/persistence/conversation-crud.ts +71 -63
  331. package/src/persistence/conversation-lineage.ts +12 -4
  332. package/src/persistence/conversation-queries.ts +2 -2
  333. package/src/persistence/conversation-title-placeholders.ts +23 -0
  334. package/src/persistence/conversation-title-service.ts +9 -26
  335. package/src/persistence/embeddings/qdrant-manager.ts +163 -18
  336. package/src/persistence/llm-request-log-sink-clickhouse.ts +3 -3
  337. package/src/persistence/llm-request-log-source-clickhouse.ts +3 -3
  338. package/src/persistence/message-cursor.ts +78 -0
  339. package/src/persistence/message-reads.ts +30 -0
  340. package/src/persistence/migrations/157-invite-contact-id.ts +18 -6
  341. package/src/persistence/migrations/__tests__/157-invite-contact-id.test.ts +149 -0
  342. package/src/persistence/schema/memory-core.ts +5 -0
  343. package/src/platform/client.test.ts +4 -2
  344. package/src/platform/client.ts +2 -10
  345. package/src/plugin-api/constants.ts +2 -0
  346. package/src/plugin-api/index.ts +1 -0
  347. package/src/plugin-api/types.ts +2 -0
  348. package/src/plugins/defaults/index.ts +2 -0
  349. package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +46 -0
  350. package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-backfill.test.ts +118 -0
  351. package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-preserve.test.ts +83 -0
  352. package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +9 -5
  353. package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +20 -10
  354. package/src/plugins/defaults/memory/__tests__/memory-retrospective-prompt.test.ts +5 -0
  355. package/src/plugins/defaults/memory/__tests__/memory-retrospective-state.test.ts +219 -0
  356. package/src/plugins/defaults/memory/__tests__/memory-retrospective-sweep.test.ts +31 -0
  357. package/src/plugins/defaults/memory/__tests__/memory-retrospective-trigger-check.test.ts +63 -0
  358. package/src/plugins/defaults/memory/__tests__/shutdown-hook.test.ts +4 -0
  359. package/src/plugins/defaults/memory/context-search/agent-runner.ts +3 -2
  360. package/src/plugins/defaults/memory/context-search/format.ts +3 -2
  361. package/src/plugins/defaults/memory/context-search/sources/memory-v2.ts +6 -3
  362. package/src/plugins/defaults/memory/context-search/sources/workspace.ts +3 -2
  363. package/src/plugins/defaults/memory/graph/capability-seed.ts +6 -7
  364. package/src/plugins/defaults/memory/hooks/init.ts +10 -0
  365. package/src/plugins/defaults/memory/hooks/message-deleted.ts +21 -0
  366. package/src/plugins/defaults/memory/hooks/shutdown.ts +2 -0
  367. package/src/plugins/defaults/memory/host-utils.ts +10 -0
  368. package/src/plugins/defaults/memory/jobs-worker.ts +13 -0
  369. package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +34 -60
  370. package/src/plugins/defaults/memory/memory-retrospective-cursor-backfill.ts +126 -0
  371. package/src/plugins/defaults/memory/memory-retrospective-cursor-preserve.ts +79 -0
  372. package/src/plugins/defaults/memory/memory-retrospective-cursor.ts +28 -0
  373. package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +3 -5
  374. package/src/plugins/defaults/memory/memory-retrospective-job.ts +12 -3
  375. package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +1 -1
  376. package/src/plugins/defaults/memory/memory-retrospective-state.ts +312 -71
  377. package/src/plugins/defaults/memory/memory-retrospective-sweep.ts +2 -1
  378. package/src/plugins/defaults/memory/memory-retrospective-trigger-check.ts +5 -3
  379. package/src/plugins/defaults/memory/skill-md-mtime-poll.ts +9 -0
  380. package/src/plugins/defaults/memory/startup.ts +28 -2
  381. package/src/plugins/defaults/memory/substrate/__tests__/skill-content.test.ts +7 -9
  382. package/src/plugins/defaults/memory/substrate/page-index.ts +6 -1
  383. package/src/plugins/defaults/memory/substrate/skill-content.ts +2 -2
  384. package/src/plugins/defaults/memory/v1/graph/consolidation.ts +4 -2
  385. package/src/plugins/defaults/memory/v1/graph/extraction.ts +4 -2
  386. package/src/plugins/defaults/memory/v1/graph/retriever.ts +4 -1
  387. package/src/plugins/defaults/memory/v2/reranker.ts +6 -1
  388. package/src/plugins/defaults/memory/v3/__tests__/card.test.ts +24 -0
  389. package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +125 -0
  390. package/src/plugins/defaults/memory/v3/__tests__/sections.test.ts +26 -0
  391. package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +30 -0
  392. package/src/plugins/defaults/memory/v3/card.ts +3 -2
  393. package/src/plugins/defaults/memory/v3/pool-select.ts +5 -3
  394. package/src/plugins/defaults/memory/v3/sections.ts +9 -3
  395. package/src/plugins/defaults/memory/v3/shadow-plugin.ts +5 -2
  396. package/src/plugins/defaults/memory/worker.ts +6 -0
  397. package/src/plugins/mcp-servers.ts +15 -75
  398. package/src/providers/__tests__/connection-model-compat.test.ts +3 -3
  399. package/src/providers/__tests__/retry-outbound-sanitize.test.ts +176 -0
  400. package/src/providers/anthropic/client.ts +0 -47
  401. package/src/providers/gemini/client.ts +9 -23
  402. package/src/providers/gemini-thought-signature.test.ts +63 -0
  403. package/src/providers/gemini-thought-signature.ts +44 -0
  404. package/src/providers/inference/auth.ts +2 -3
  405. package/src/providers/inference/connection-availability.ts +27 -8
  406. package/src/providers/inference/resolve-auth.ts +22 -4
  407. package/src/providers/inference/service-account-token.ts +248 -0
  408. package/src/providers/model-catalog.ts +34 -0
  409. package/src/providers/model-intents.ts +10 -5
  410. package/src/providers/openai/__tests__/chat-completions-output-token-limit.test.ts +141 -0
  411. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +146 -18
  412. package/src/providers/openai/__tests__/chat-completions-provider-thought-signature.test.ts +613 -0
  413. package/src/providers/openai/__tests__/google-thought-signature.test.ts +235 -0
  414. package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +83 -1
  415. package/src/providers/openai/chat-completions-provider.ts +212 -79
  416. package/src/providers/openai/codex-models.ts +0 -4
  417. package/src/providers/openai/google-thought-signature.ts +320 -0
  418. package/src/providers/openrouter/client.ts +20 -0
  419. package/src/providers/outbound-request-sanitize.ts +96 -0
  420. package/src/providers/retry.ts +24 -3
  421. package/src/providers/unparseable-tool-args.ts +5 -1
  422. package/src/runtime/AGENTS.md +3 -1
  423. package/src/runtime/__tests__/channel-send.test.ts +443 -0
  424. package/src/runtime/__tests__/desktop-stream-upgrade.test.ts +89 -0
  425. package/src/runtime/__tests__/host-observe.test.ts +40 -1
  426. package/src/runtime/__tests__/live-voice-upgrade.test.ts +58 -0
  427. package/src/runtime/__tests__/runtime-ws-test-utils.ts +72 -0
  428. package/src/runtime/__tests__/watch-stream-upgrade.test.ts +59 -0
  429. package/src/runtime/agent-wake.ts +2 -1
  430. package/src/runtime/assistant-event-hub.ts +21 -3
  431. package/src/runtime/auth/same-actor.ts +38 -3
  432. package/src/runtime/channel-send.ts +316 -0
  433. package/src/runtime/host-observe.ts +11 -1
  434. package/src/runtime/http-server.ts +160 -151
  435. package/src/runtime/routes/__tests__/acp-routes.test.ts +159 -4
  436. package/src/runtime/routes/__tests__/apps-refresh-route.test.ts +205 -0
  437. package/src/runtime/routes/__tests__/channel-send-routes.test.ts +127 -0
  438. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +108 -1
  439. package/src/runtime/routes/__tests__/default-provider-routes.test.ts +2 -2
  440. package/src/runtime/routes/__tests__/dictation-cleanup-guard.test.ts +107 -0
  441. package/src/runtime/routes/__tests__/mcp-add-default-risk.test.ts +16 -12
  442. package/src/runtime/routes/acp-routes.test.ts +15 -0
  443. package/src/runtime/routes/acp-routes.ts +72 -9
  444. package/src/runtime/routes/app-management-routes.ts +73 -0
  445. package/src/runtime/routes/app-routes.ts +1 -1
  446. package/src/runtime/routes/channel-send-routes.ts +85 -0
  447. package/src/runtime/routes/conversation-list-routes.ts +14 -3
  448. package/src/runtime/routes/conversation-query-routes.ts +40 -0
  449. package/src/runtime/routes/conversation-routes.ts +16 -3
  450. package/src/runtime/routes/credential-routes.ts +19 -10
  451. package/src/runtime/routes/desktop-setup-routes.test.ts +74 -0
  452. package/src/runtime/routes/desktop-setup-routes.ts +35 -0
  453. package/src/runtime/routes/diagnostics-routes.ts +61 -4
  454. package/src/runtime/routes/events-routes.ts +4 -0
  455. package/src/runtime/routes/host-cu-routes.ts +6 -0
  456. package/src/runtime/routes/index.ts +4 -2
  457. package/src/runtime/routes/log-export-routes.ts +2 -0
  458. package/src/runtime/routes/mcp-auth-routes.ts +39 -47
  459. package/src/runtime/routes/migration-routes.ts +6 -20
  460. package/src/runtime/routes/oauth-commands-routes.ts +36 -0
  461. package/src/runtime/routes/platform-routes.ts +18 -15
  462. package/src/runtime/routes/workspace-routes.test.ts +55 -26
  463. package/src/runtime/routes/workspace-routes.ts +46 -63
  464. package/src/runtime/routes/workspace-tree-walk.test.ts +244 -0
  465. package/src/runtime/routes/workspace-tree-walk.ts +240 -0
  466. package/src/runtime/routes/workspace-utils.ts +17 -0
  467. package/src/runtime/services/__tests__/conversation-serializer.test.ts +28 -0
  468. package/src/runtime/services/conversation-serializer.ts +16 -3
  469. package/src/runtime/sync/resource-sync-events.ts +2 -1
  470. package/src/schedule/run-script.ts +2 -1
  471. package/src/schedule/schedule-store.ts +9 -2
  472. package/src/schedule/worker.ts +1 -1
  473. package/src/security/__tests__/ces-http-record-backend.test.ts +137 -0
  474. package/src/security/ces-http-record-backend.ts +120 -0
  475. package/src/security/ces-rpc-record-backend.ts +7 -0
  476. package/src/security/untrusted-content.ts +3 -1
  477. package/src/skills/__tests__/workspace-skill-md-signature.test.ts +60 -0
  478. package/src/skills/workspace-skill-md-signature.ts +50 -0
  479. package/src/subagent/consult-context.ts +12 -25
  480. package/src/tools/__tests__/side-effect-abort-guard.test.ts +1 -0
  481. package/src/tools/acp/list-agents.test.ts +3 -1
  482. package/src/tools/acp/spawn.test.ts +146 -11
  483. package/src/tools/acp/spawn.ts +21 -3
  484. package/src/tools/acp/status.test.ts +94 -0
  485. package/src/tools/acp/status.ts +40 -3
  486. package/src/tools/app-control/skill-proxy-bridge.ts +2 -1
  487. package/src/tools/browser/browser-execution.ts +1 -1
  488. package/src/tools/browser/cdp-client/accessibility-snapshot.ts +9 -3
  489. package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +2 -1
  490. package/src/tools/browser/cdp-client/extension-cdp-client.ts +3 -2
  491. package/src/tools/browser/network-recorder.ts +2 -1
  492. package/src/tools/capability-offer.test.ts +22 -2
  493. package/src/tools/capability-offer.ts +16 -1
  494. package/src/tools/computer-use/definitions.ts +121 -2
  495. package/src/tools/computer-use/skill-proxy-bridge.ts +2 -1
  496. package/src/tools/credentials/metadata-store.ts +100 -0
  497. package/src/tools/filesystem/search.ts +2 -1
  498. package/src/tools/host-filesystem/edit.test.ts +2 -1
  499. package/src/tools/host-filesystem/edit.ts +2 -2
  500. package/src/tools/host-filesystem/read.test.ts +2 -1
  501. package/src/tools/host-filesystem/read.ts +2 -2
  502. package/src/tools/host-filesystem/transfer.test.ts +2 -1
  503. package/src/tools/host-filesystem/transfer.ts +2 -2
  504. package/src/tools/host-filesystem/write.test.ts +2 -1
  505. package/src/tools/host-filesystem/write.ts +2 -2
  506. package/src/tools/host-terminal/host-shell.test.ts +16 -0
  507. package/src/tools/host-terminal/host-shell.ts +4 -20
  508. package/src/tools/network/web-search.ts +6 -1
  509. package/src/tools/policy-context.ts +6 -7
  510. package/src/tools/skills/scaffold-managed.ts +61 -3
  511. package/src/tools/subagent/spawn.ts +5 -8
  512. package/src/tools/terminal/__tests__/safe-env.test.ts +6 -6
  513. package/src/tools/terminal/__tests__/sanitized-bash.test.ts +57 -0
  514. package/src/tools/terminal/safe-env.ts +23 -3
  515. package/src/tools/terminal/sanitized-bash.ts +105 -0
  516. package/src/tools/watch/watch-retro-report.ts +0 -8
  517. package/src/types/host-capabilities.ts +5 -3
  518. package/src/util/__tests__/language-subtag.test.ts +23 -1
  519. package/src/util/ca-bundle.ts +13 -0
  520. package/src/util/language-subtag.ts +16 -0
  521. package/src/util/platform.ts +8 -0
  522. package/src/util/unicode.ts +15 -1
  523. package/src/watch/watch-timeline.ts +2 -2
  524. package/src/workflows/run-manager.ts +2 -1
  525. package/src/workspace/git-porcelain.ts +66 -0
  526. package/src/workspace/git-service.ts +6 -55
  527. package/src/workspace/migrations/154-repair-retired-codex-gpt-5-4-model-ids.ts +258 -0
  528. package/src/workspace/migrations/155-move-front-model-config-to-voice.ts +78 -0
  529. package/src/workspace/migrations/156-extract-workspace-mcp-json.ts +214 -0
  530. package/src/workspace/migrations/__tests__/155-move-front-model-config-to-voice.test.ts +102 -0
  531. package/src/workspace/migrations/__tests__/156-extract-workspace-mcp-json.test.ts +199 -0
  532. package/src/workspace/migrations/registry.ts +6 -0
  533. package/src/__tests__/app-source-watcher.test.ts +0 -218
  534. package/src/__tests__/config-watcher-skill-reseed.test.ts +0 -359
  535. package/src/__tests__/speaker-identification.test.ts +0 -56
  536. package/src/calls/speaker-identification.ts +0 -233
  537. package/src/daemon/app-source-watcher.ts +0 -218
  538. package/src/messaging/providers/whatsapp/client.ts +0 -29
  539. package/src/runtime/routes/debug-bash-routes.ts +0 -173
  540. package/src/util/fs-watcher-error.ts +0 -36
  541. /package/src/{live-voice → calls}/progress-phrases.ts +0 -0
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Spawn a shell command with the same sanitized environment the bash tool
3
+ * forwards to tool-call subprocesses.
4
+ *
5
+ * Used by `assistant bash` so a CLI invocation sees the same allowlisted
6
+ * env, workspace cwd, and platform shell as a bash tool call, without
7
+ * round-tripping through the assistant HTTP or IPC API.
8
+ */
9
+
10
+ import { spawn } from "node:child_process";
11
+
12
+ import {
13
+ buildShellInvocation,
14
+ terminateProcessTree,
15
+ } from "../../util/host-process.js";
16
+ import { getWorkspaceDir } from "../../util/platform.js";
17
+ import { buildSanitizedEnv } from "./safe-env.js";
18
+
19
+ export const DEFAULT_SANITIZED_BASH_TIMEOUT_MS = 30_000;
20
+
21
+ export interface SanitizedBashResult {
22
+ stdout: string;
23
+ stderr: string;
24
+ exitCode: number | null;
25
+ timedOut: boolean;
26
+ error?: string;
27
+ }
28
+
29
+ export function runSanitizedBash(
30
+ command: string,
31
+ timeoutMs: number = DEFAULT_SANITIZED_BASH_TIMEOUT_MS,
32
+ ): Promise<SanitizedBashResult> {
33
+ if (!command) {
34
+ return Promise.resolve({
35
+ stdout: "",
36
+ stderr: "",
37
+ exitCode: null,
38
+ timedOut: false,
39
+ error: "command is required",
40
+ });
41
+ }
42
+
43
+ const effectiveTimeout =
44
+ typeof timeoutMs === "number" && timeoutMs > 0
45
+ ? timeoutMs
46
+ : DEFAULT_SANITIZED_BASH_TIMEOUT_MS;
47
+
48
+ return new Promise<SanitizedBashResult>((resolve) => {
49
+ const stdoutChunks: Buffer[] = [];
50
+ const stderrChunks: Buffer[] = [];
51
+ let timedOut = false;
52
+ let settled = false;
53
+
54
+ const finish = (result: SanitizedBashResult) => {
55
+ if (settled) {
56
+ return;
57
+ }
58
+ settled = true;
59
+ resolve(result);
60
+ };
61
+
62
+ const wrapped = buildShellInvocation(command);
63
+ const child = spawn(wrapped.command, wrapped.args, {
64
+ cwd: getWorkspaceDir(),
65
+ stdio: ["ignore", "pipe", "pipe"],
66
+ detached: true,
67
+ env: buildSanitizedEnv(),
68
+ windowsHide: true,
69
+ });
70
+
71
+ const timer = setTimeout(() => {
72
+ timedOut = true;
73
+ terminateProcessTree(child);
74
+ }, effectiveTimeout);
75
+
76
+ child.stdout.on("data", (data: Buffer) => {
77
+ stdoutChunks.push(data);
78
+ });
79
+
80
+ child.stderr.on("data", (data: Buffer) => {
81
+ stderrChunks.push(data);
82
+ });
83
+
84
+ child.on("close", (code) => {
85
+ clearTimeout(timer);
86
+ finish({
87
+ stdout: Buffer.concat(stdoutChunks).toString(),
88
+ stderr: Buffer.concat(stderrChunks).toString(),
89
+ exitCode: code,
90
+ timedOut,
91
+ });
92
+ });
93
+
94
+ child.on("error", (err) => {
95
+ clearTimeout(timer);
96
+ finish({
97
+ stdout: "",
98
+ stderr: "",
99
+ exitCode: null,
100
+ timedOut: false,
101
+ error: err.message,
102
+ });
103
+ });
104
+ });
105
+ }
@@ -14,14 +14,6 @@ import type {
14
14
  /**
15
15
  * How a watch retrospective hands its report to the daemon.
16
16
  *
17
- * **The card cannot come from `ui_show`.** A retrospective runs as a
18
- * `clientless` wake, which pins the turn non-interactive, and
19
- * `conversation-tool-setup` gates the whole `ui_surface` tool family on a
20
- * client being present (`return channelCapabilities?.supportsDynamicUi ??
21
- * !hasNoClient`). So `ui_show` is not merely denied in this turn, it is absent
22
- * from the tool set, and a retrospective told to call it can only report that
23
- * it cannot. This tool is an ordinary one and passes that gate untouched.
24
- *
25
17
  * **It records; it does not render.** The executor validates the payload and
26
18
  * returns, and nothing here writes to the conversation. What makes the card is
27
19
  * `watch-retro.ts` reading this call back out of the turn's own history once
@@ -16,6 +16,7 @@ export const HOST_PROXY_CAPABILITIES = [
16
16
  "host_file",
17
17
  "host_cu",
18
18
  "host_cu_window_capture",
19
+ "host_cu_sequence",
19
20
  "host_cu_annotate",
20
21
  "host_browser",
21
22
  "host_app_control",
@@ -24,11 +25,12 @@ export const HOST_PROXY_CAPABILITIES = [
24
25
 
25
26
  export type HostProxyCapability = (typeof HOST_PROXY_CAPABILITIES)[number];
26
27
 
27
- /** Every host capability except the three macOS-only ones. */
28
+ /** Every host capability except the macOS-only ones. */
28
29
  const DESKTOP_SHARED_CAPABILITIES = HOST_PROXY_CAPABILITIES.filter(
29
30
  (capability) =>
30
31
  capability !== "host_app_control" &&
31
32
  capability !== "host_cu_window_capture" &&
33
+ capability !== "host_cu_sequence" &&
32
34
  capability !== "host_cu_annotate",
33
35
  );
34
36
 
@@ -47,8 +49,8 @@ export type HostProxyInterfaceId = "macos" | "windows" | "linux";
47
49
  * (web, ios, android, and every messaging transport) provide none.
48
50
  *
49
51
  * Windows and Linux run the same host proxy as macOS minus app control. The
50
- * two window-scoped CU capabilities ride the host_cu transport and are
51
- * negotiated per connection; only the macOS native helper answers them, so a
52
+ * negotiated CU capabilities ride the host_cu transport and are claimed per
53
+ * connection; only the macOS native helper answers them, so a
52
54
  * request elsewhere would reach that helper as an unknown action.
53
55
  *
54
56
  * macOS additionally provisions its `host_browser` proxy via the assistant
@@ -1,6 +1,10 @@
1
1
  import { describe, expect, test } from "bun:test";
2
2
 
3
- import { baseLanguageSubtag, localizedOrDefault } from "../language-subtag.js";
3
+ import {
4
+ baseLanguageSubtag,
5
+ fixedPhraseLanguage,
6
+ localizedOrDefault,
7
+ } from "../language-subtag.js";
4
8
 
5
9
  describe("baseLanguageSubtag", () => {
6
10
  test("lowercases a bare tag", () => {
@@ -52,3 +56,21 @@ describe("localizedOrDefault", () => {
52
56
  expect(localizedOrDefault(table, "__proto__", "fallback")).toBe("fallback");
53
57
  });
54
58
  });
59
+
60
+ describe("fixedPhraseLanguage", () => {
61
+ const table = { en: "one", es: "uno" };
62
+
63
+ test("pins English when the speaker's language has no table entry", () => {
64
+ expect(fixedPhraseLanguage(table, "ko")).toBe("en");
65
+ expect(fixedPhraseLanguage(table, "ar-EG")).toBe("en");
66
+ });
67
+
68
+ test("rides the speaker's language when the table covers it", () => {
69
+ expect(fixedPhraseLanguage(table, "es")).toBeUndefined();
70
+ expect(fixedPhraseLanguage(table, "es-419")).toBeUndefined();
71
+ });
72
+
73
+ test("carries no hint when the language is unknown", () => {
74
+ expect(fixedPhraseLanguage(table, undefined)).toBeUndefined();
75
+ });
76
+ });
@@ -0,0 +1,13 @@
1
+ import { readFile, writeFile } from "node:fs/promises";
2
+
3
+ export async function writeCombinedCABundle(
4
+ systemBundlePath: string,
5
+ extraCAPath: string,
6
+ outputPath: string,
7
+ ): Promise<void> {
8
+ const [systemCAs, extraCAs] = await Promise.all([
9
+ readFile(systemBundlePath, "utf-8"),
10
+ readFile(extraCAPath, "utf-8"),
11
+ ]);
12
+ await writeFile(outputPath, `${systemCAs}\n${extraCAs}`, { mode: 0o644 });
13
+ }
@@ -41,3 +41,19 @@ export function localizedOrDefault<T>(
41
41
  ? (table[baseLanguageSubtag(language)!] as T)
42
42
  : fallback;
43
43
  }
44
+
45
+ /**
46
+ * The TTS language hint for a fixed phrase picked from a localized table:
47
+ * "en" when the speaker has a language the table does not cover (the
48
+ * picker fell back to English text, which must not be synthesized under
49
+ * an ar/ko/ta hint), undefined otherwise (the phrase rides the speaker's
50
+ * language, or carries no hint at all when the language is unknown).
51
+ */
52
+ export function fixedPhraseLanguage(
53
+ table: Readonly<Record<string, unknown>>,
54
+ language: string | undefined,
55
+ ): string | undefined {
56
+ return language !== undefined && !hasLocalizedEntry(table, language)
57
+ ? "en"
58
+ : undefined;
59
+ }
@@ -470,6 +470,14 @@ export function getWorkspaceConfigPath(): string {
470
470
  return join(getWorkspaceDir(), "config.json");
471
471
  }
472
472
 
473
+ /** Filename for the user-owned Agent Plugins MCP document. */
474
+ export const WORKSPACE_MCP_FILENAME = "mcp.json";
475
+
476
+ /** Returns $VELLUM_WORKSPACE_DIR/mcp.json */
477
+ export function getWorkspaceMcpConfigPath(): string {
478
+ return join(getWorkspaceDir(), WORKSPACE_MCP_FILENAME);
479
+ }
480
+
473
481
  /** Returns $VELLUM_WORKSPACE_DIR/skills */
474
482
  export function getWorkspaceSkillsDir(): string {
475
483
  return join(getWorkspaceDir(), "skills");
@@ -126,6 +126,15 @@ export interface DeepSanitizeResult<T> {
126
126
  fixedStringCount: number;
127
127
  }
128
128
 
129
+ export interface DeepSanitizeOptions {
130
+ /**
131
+ * Skip a plain-object property entirely: its value is neither scanned nor
132
+ * copied. For fields known to hold ASCII (base64 payloads), scanning is
133
+ * pure cost.
134
+ */
135
+ skipKey?: (key: string, parent: Record<string, unknown>) => boolean;
136
+ }
137
+
129
138
  /**
130
139
  * Recursively walk arrays and plain objects, replacing orphaned surrogates in
131
140
  * every string value. Non-plain objects (class instances, Date, Buffer, Map,
@@ -136,8 +145,10 @@ export interface DeepSanitizeResult<T> {
136
145
  */
137
146
  export function stripOrphanedSurrogatesDeep<T>(
138
147
  input: T,
148
+ options: DeepSanitizeOptions = {},
139
149
  ): DeepSanitizeResult<T> {
140
150
  let fixedStringCount = 0;
151
+ const { skipKey } = options;
141
152
 
142
153
  const walk = (value: unknown): { value: unknown; changed: boolean } => {
143
154
  if (typeof value === "string") {
@@ -178,7 +189,10 @@ export function stripOrphanedSurrogatesDeep<T>(
178
189
  let next: Record<string, unknown> | null = null;
179
190
  for (let i = 0; i < keys.length; i++) {
180
191
  const key = keys[i]!;
181
- const result = walk(source[key]);
192
+ const result =
193
+ skipKey?.(key, source) === true
194
+ ? { value: source[key], changed: false }
195
+ : walk(source[key]);
182
196
  if (result.changed && next === null) {
183
197
  next = {};
184
198
  for (let j = 0; j < i; j++) {
@@ -366,8 +366,8 @@ export function appendNarration(
366
366
  * timeline of a session where observation stalled is simply a sparser one.
367
367
  *
368
368
  * The screenshot is stored only when `attachScreenshot` asks for it, and
369
- * carrying one is not asking. The host captures a screenshot on every observe
370
- * with no opt-out, so an observation always has pixels available and a policy
369
+ * carrying one is not asking. Watch requests a screenshot on every observe, so
370
+ * an observation normally has pixels available and a policy
371
371
  * of "store what arrives" is a policy of storing every frame. Which frames are
372
372
  * worth an image is a cadence decision, and it belongs to the caller driving
373
373
  * the session rather than to the row writer.
@@ -33,6 +33,7 @@ import type { TrustContext } from "../daemon/trust-context-types.js";
33
33
  import { wakeAgentForOpportunity } from "../runtime/agent-wake.js";
34
34
  import { broadcastMessage } from "../runtime/assistant-event-hub.js";
35
35
  import { getLogger } from "../util/logger.js";
36
+ import { safeStringSlice } from "../util/unicode.js";
36
37
  import type { CapabilityManifest } from "./capabilities.js";
37
38
  import {
38
39
  normalizeCapabilityManifest,
@@ -611,7 +612,7 @@ function truncateForSummary(s: string, runId: string): string {
611
612
  }
612
613
  const omitted = s.length - MAX_SUMMARY_RESULT_CHARS;
613
614
  return (
614
- `${s.slice(0, MAX_SUMMARY_RESULT_CHARS)}… ` +
615
+ `${safeStringSlice(s, 0, MAX_SUMMARY_RESULT_CHARS)}… ` +
615
616
  `[truncated ${omitted} chars — fetch the full result with manage_workflows ` +
616
617
  `{ action: "get_result", run_id: "${runId}" }]`
617
618
  );
@@ -0,0 +1,66 @@
1
+ /**
2
+ * NUL-terminated `git status --porcelain -z` parsing.
3
+ *
4
+ * Status reporting and batched staging share this parser so rename/copy
5
+ * origin paths cannot drift between the two readers.
6
+ */
7
+
8
+ export interface PorcelainEntry {
9
+ /** Two-character XY status from porcelain v1. */
10
+ status: string;
11
+ /** Path git reports for this record (the destination for rename/copy). */
12
+ path: string;
13
+ /** Pre-rename/copy path. Present when the record is a rename or copy. */
14
+ origin?: string;
15
+ }
16
+
17
+ /**
18
+ * Parse NUL-terminated `git status --porcelain -z` output into status/path
19
+ * records. NUL termination is required so paths with special characters
20
+ * (non-ASCII, quotes, newlines) arrive verbatim instead of C-style quoted.
21
+ *
22
+ * A rename/copy record is followed by a bare origin-path entry, captured on
23
+ * {@link PorcelainEntry.origin}.
24
+ */
25
+ export function parsePorcelainZ(stdout: string): PorcelainEntry[] {
26
+ const entries = stdout.split("\0");
27
+ const parsed: PorcelainEntry[] = [];
28
+ for (let i = 0; i < entries.length; i++) {
29
+ const entry = entries[i] ?? "";
30
+ if (entry.length < 4) {
31
+ continue;
32
+ }
33
+ const status = entry.substring(0, 2);
34
+ const path = entry.substring(3);
35
+ if (status[0] === "R" || status[0] === "C") {
36
+ i++;
37
+ const origin = entries[i];
38
+ if (origin && origin.length > 0) {
39
+ parsed.push({ status, path, origin });
40
+ continue;
41
+ }
42
+ }
43
+ parsed.push({ status, path });
44
+ }
45
+ return parsed;
46
+ }
47
+
48
+ /**
49
+ * Every path git status knows about, including rename/copy origins.
50
+ * Origins still need to be staged so a deletion is not left behind when a
51
+ * rename is split across add batches.
52
+ */
53
+ export function collectDirtyPathsFromPorcelain(stdout: string): string[] {
54
+ const paths: string[] = [];
55
+ const seen = new Set<string>();
56
+ for (const entry of parsePorcelainZ(stdout)) {
57
+ for (const path of [entry.path, entry.origin]) {
58
+ if (!path || seen.has(path)) {
59
+ continue;
60
+ }
61
+ seen.add(path);
62
+ paths.push(path);
63
+ }
64
+ }
65
+ return paths;
66
+ }
@@ -17,6 +17,10 @@ import { getLogger } from "../util/logger.js";
17
17
  import { Mutex } from "../util/mutex.js";
18
18
  import { addToPathEnv, getExtraToolPathDirs } from "../util/platform.js";
19
19
  import { PromiseGuard } from "../util/promise-guard.js";
20
+ import {
21
+ collectDirtyPathsFromPorcelain,
22
+ parsePorcelainZ,
23
+ } from "./git-porcelain.js";
20
24
 
21
25
  const execFileAsync = promisify(execFile);
22
26
  const log = getLogger("workspace-git");
@@ -51,7 +55,7 @@ function cleanGitEnv(workspaceDir: string): Record<string, string> {
51
55
  * Patterns excluded from workspace git tracking.
52
56
  * These are written to .gitignore on init and appended to existing .gitignore files.
53
57
  */
54
- const WORKSPACE_GITIGNORE_RULES = [
58
+ export const WORKSPACE_GITIGNORE_RULES = [
55
59
  // Runtime state directories
56
60
  "data/db/",
57
61
  "data/qdrant/",
@@ -226,59 +230,6 @@ done
226
230
  exit 0
227
231
  `;
228
232
 
229
- /**
230
- * Parse NUL-terminated `git status --porcelain -z` output into status/path
231
- * pairs. NUL termination is required so paths with special characters
232
- * (non-ASCII, quotes, newlines) arrive verbatim instead of C-style quoted.
233
- * A rename/copy record is followed by a bare origin-path entry, which is
234
- * skipped.
235
- */
236
- function parsePorcelainZ(
237
- stdout: string,
238
- ): Array<{ status: string; path: string }> {
239
- const entries = stdout.split("\0");
240
- const parsed: Array<{ status: string; path: string }> = [];
241
- for (let i = 0; i < entries.length; i++) {
242
- const entry = entries[i] ?? "";
243
- if (entry.length < 4) {
244
- continue;
245
- }
246
- const status = entry.substring(0, 2);
247
- parsed.push({ status, path: entry.substring(3) });
248
- if (status[0] === "R" || status[0] === "C") {
249
- i++;
250
- }
251
- }
252
- return parsed;
253
- }
254
-
255
- /**
256
- * Every path git status knows about, including rename/copy origins.
257
- * Origins are skipped by {@link parsePorcelainZ} (they have no status
258
- * prefix) but still need to be staged so a deletion is not left behind
259
- * when a rename is split across add batches.
260
- */
261
- function collectDirtyPathsFromPorcelain(stdout: string): string[] {
262
- const entries = stdout.split("\0");
263
- const paths: string[] = [];
264
- for (let i = 0; i < entries.length; i++) {
265
- const entry = entries[i] ?? "";
266
- if (entry.length < 4) {
267
- continue;
268
- }
269
- const status = entry.substring(0, 2);
270
- paths.push(entry.substring(3));
271
- if (status[0] === "R" || status[0] === "C") {
272
- i++;
273
- const origin = entries[i];
274
- if (origin && origin.length > 0) {
275
- paths.push(origin);
276
- }
277
- }
278
- }
279
- return paths;
280
- }
281
-
282
233
  /** Properties added by Node's child_process errors. */
283
234
  interface ExecError extends Error {
284
235
  killed?: boolean;
@@ -433,7 +384,7 @@ export class WorkspaceGitService {
433
384
  *
434
385
  * This method is always called inside the mutex, so no git operation from
435
386
  * our code can be concurrently holding the lock. However, an external git
436
- * process (user running `git add`, IDE tooling, etc.) could legitimately
387
+ * git process (user running `git add`, IDE tooling, etc.) could legitimately
437
388
  * hold the lock. We use `lsof` to check — if any process has the file
438
389
  * open, we leave it alone. If no process holds it, it's stale (crashed
439
390
  * process) and safe to remove.