@vellumai/assistant 0.12.1 → 0.12.2-staging.1

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 (548) 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/openapi.yaml +327 -52
  6. package/package.json +3 -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 +122 -0
  253. package/src/desktop/desktop-chrome-policy.test.ts +79 -0
  254. package/src/desktop/desktop-chrome-policy.ts +47 -0
  255. package/src/desktop/desktop-chrome-session.ts +37 -0
  256. package/src/desktop/desktop-dependencies.test.ts +102 -0
  257. package/src/desktop/desktop-dependencies.ts +308 -0
  258. package/src/desktop/desktop-feature.test.ts +18 -0
  259. package/src/desktop/desktop-feature.ts +19 -0
  260. package/src/desktop/desktop-panel-config.test.ts +135 -0
  261. package/src/desktop/desktop-panel-config.ts +149 -0
  262. package/src/desktop/desktop-session-manager.test.ts +730 -0
  263. package/src/desktop/desktop-session-manager.ts +850 -0
  264. package/src/desktop/desktop-stream-bridge.test.ts +395 -0
  265. package/src/desktop/desktop-stream-bridge.ts +214 -0
  266. package/src/desktop/desktop-wallpaper.test.ts +191 -0
  267. package/src/desktop/desktop-wallpaper.ts +113 -0
  268. package/src/desktop/desktop-window-drag.ts +177 -0
  269. package/src/desktop/desktop-window-manager-config.test.ts +230 -0
  270. package/src/desktop/desktop-window-manager-config.ts +238 -0
  271. package/src/desktop/desktop-window-theme.ts +170 -0
  272. package/src/heartbeat/heartbeat-service.ts +21 -3
  273. package/src/home/__tests__/feed-writer.test.ts +22 -0
  274. package/src/home/feed-writer.ts +11 -2
  275. package/src/hooks/types.ts +33 -0
  276. package/src/i18n/AGENTS.md +22 -0
  277. package/src/i18n/__tests__/i18n.test.ts +108 -0
  278. package/src/i18n/index.ts +16 -0
  279. package/src/i18n/locales.ts +93 -0
  280. package/src/i18n/messages.ts +61 -0
  281. package/src/i18n/title.ts +25 -0
  282. package/src/inbound/platform-callback-registration.ts +4 -5
  283. package/src/live-voice/__tests__/live-voice-metrics.test.ts +0 -3
  284. package/src/live-voice/__tests__/live-voice-progress.test.ts +15 -15
  285. package/src/live-voice/__tests__/live-voice-sight-frame-echo.test.ts +238 -0
  286. package/src/live-voice/__tests__/live-voice-vad.test.ts +2 -2
  287. package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +13 -69
  288. package/src/live-voice/live-voice-photo.ts +25 -6
  289. package/src/live-voice/live-voice-session.ts +213 -693
  290. package/src/live-voice/protocol.ts +0 -2
  291. package/src/mcp/__tests__/effective-config.test.ts +2 -2
  292. package/src/mcp/__tests__/workspace-mcp-config.test.ts +135 -0
  293. package/src/mcp/effective-config.ts +3 -3
  294. package/src/mcp/mcp-header-store.ts +29 -45
  295. package/src/mcp/spec-schema.ts +111 -0
  296. package/src/mcp/startup.ts +8 -4
  297. package/src/mcp/workspace-mcp-config.ts +188 -0
  298. package/src/messaging/provider.ts +10 -3
  299. package/src/messaging/providers/__tests__/proactive-address.test.ts +95 -0
  300. package/src/messaging/providers/callback-routing.ts +18 -0
  301. package/src/messaging/providers/channel-transport.ts +56 -0
  302. package/src/messaging/providers/discord/transport.ts +17 -0
  303. package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +6 -17
  304. package/src/messaging/providers/slack/adapter.ts +7 -44
  305. package/src/messaging/providers/slack/transport.ts +16 -0
  306. package/src/messaging/providers/telegram-bot/adapter.ts +1 -48
  307. package/src/messaging/providers/telegram-bot/client.ts +0 -26
  308. package/src/messaging/providers/telegram-bot/transport.ts +20 -0
  309. package/src/messaging/providers/whatsapp/adapter.ts +1 -48
  310. package/src/messaging/providers/whatsapp/transport.ts +18 -0
  311. package/src/monitoring/__tests__/workspace-git-heartbeat.test.ts +8 -0
  312. package/src/notifications/__tests__/assistant-reply-producer.test.ts +55 -0
  313. package/src/notifications/__tests__/guardian-feed-projection.test.ts +158 -2
  314. package/src/notifications/__tests__/home-feed-side-effect.test.ts +153 -3
  315. package/src/notifications/__tests__/schedule-result-producer.test.ts +99 -3
  316. package/src/notifications/adapters/macos.ts +3 -0
  317. package/src/notifications/assistant-reply-producer.ts +8 -3
  318. package/src/notifications/conversation-pairing.ts +36 -16
  319. package/src/notifications/delivered-post-record.ts +11 -1
  320. package/src/notifications/guardian-feed-projection.ts +125 -5
  321. package/src/notifications/home-feed-side-effect.ts +55 -7
  322. package/src/notifications/schedule-result-producer.ts +66 -22
  323. package/src/oauth/AGENTS.md +2 -0
  324. package/src/oauth/__tests__/seed-providers-managed.test.ts +54 -0
  325. package/src/oauth/byo-connection.test.ts +105 -0
  326. package/src/oauth/byo-connection.ts +36 -6
  327. package/src/oauth/connection-resolver.test.ts +73 -0
  328. package/src/oauth/connection-resolver.ts +95 -2
  329. package/src/oauth/scope-utils.ts +24 -0
  330. package/src/oauth/seed-providers.ts +204 -25
  331. package/src/outbound-proxy/certs.ts +3 -5
  332. package/src/permissions/checker.test.ts +11 -6
  333. package/src/persistence/compaction-log-store-clickhouse.ts +3 -3
  334. package/src/persistence/conversation-crud.ts +71 -63
  335. package/src/persistence/conversation-lineage.ts +12 -4
  336. package/src/persistence/conversation-queries.ts +2 -2
  337. package/src/persistence/conversation-title-placeholders.ts +23 -0
  338. package/src/persistence/conversation-title-service.ts +9 -26
  339. package/src/persistence/embeddings/qdrant-manager.ts +163 -18
  340. package/src/persistence/llm-request-log-sink-clickhouse.ts +3 -3
  341. package/src/persistence/llm-request-log-source-clickhouse.ts +3 -3
  342. package/src/persistence/message-cursor.ts +78 -0
  343. package/src/persistence/message-reads.ts +30 -0
  344. package/src/persistence/migrations/157-invite-contact-id.ts +18 -6
  345. package/src/persistence/migrations/__tests__/157-invite-contact-id.test.ts +149 -0
  346. package/src/persistence/schema/memory-core.ts +5 -0
  347. package/src/platform/client.test.ts +4 -2
  348. package/src/platform/client.ts +2 -10
  349. package/src/plugin-api/constants.ts +2 -0
  350. package/src/plugin-api/index.ts +1 -0
  351. package/src/plugin-api/types.ts +2 -0
  352. package/src/plugins/defaults/index.ts +2 -0
  353. package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +46 -0
  354. package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-backfill.test.ts +118 -0
  355. package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-preserve.test.ts +83 -0
  356. package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +9 -5
  357. package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +20 -10
  358. package/src/plugins/defaults/memory/__tests__/memory-retrospective-prompt.test.ts +5 -0
  359. package/src/plugins/defaults/memory/__tests__/memory-retrospective-state.test.ts +219 -0
  360. package/src/plugins/defaults/memory/__tests__/memory-retrospective-sweep.test.ts +31 -0
  361. package/src/plugins/defaults/memory/__tests__/memory-retrospective-trigger-check.test.ts +63 -0
  362. package/src/plugins/defaults/memory/__tests__/shutdown-hook.test.ts +4 -0
  363. package/src/plugins/defaults/memory/context-search/agent-runner.ts +3 -2
  364. package/src/plugins/defaults/memory/context-search/format.ts +3 -2
  365. package/src/plugins/defaults/memory/context-search/sources/memory-v2.ts +6 -3
  366. package/src/plugins/defaults/memory/context-search/sources/workspace.ts +3 -2
  367. package/src/plugins/defaults/memory/graph/capability-seed.ts +6 -7
  368. package/src/plugins/defaults/memory/hooks/init.ts +10 -0
  369. package/src/plugins/defaults/memory/hooks/message-deleted.ts +21 -0
  370. package/src/plugins/defaults/memory/hooks/shutdown.ts +2 -0
  371. package/src/plugins/defaults/memory/host-utils.ts +10 -0
  372. package/src/plugins/defaults/memory/jobs-worker.ts +13 -0
  373. package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +34 -60
  374. package/src/plugins/defaults/memory/memory-retrospective-cursor-backfill.ts +126 -0
  375. package/src/plugins/defaults/memory/memory-retrospective-cursor-preserve.ts +79 -0
  376. package/src/plugins/defaults/memory/memory-retrospective-cursor.ts +28 -0
  377. package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +3 -5
  378. package/src/plugins/defaults/memory/memory-retrospective-job.ts +12 -3
  379. package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +1 -1
  380. package/src/plugins/defaults/memory/memory-retrospective-state.ts +312 -71
  381. package/src/plugins/defaults/memory/memory-retrospective-sweep.ts +2 -1
  382. package/src/plugins/defaults/memory/memory-retrospective-trigger-check.ts +5 -3
  383. package/src/plugins/defaults/memory/skill-md-mtime-poll.ts +9 -0
  384. package/src/plugins/defaults/memory/startup.ts +28 -2
  385. package/src/plugins/defaults/memory/substrate/__tests__/skill-content.test.ts +7 -9
  386. package/src/plugins/defaults/memory/substrate/page-index.ts +6 -1
  387. package/src/plugins/defaults/memory/substrate/skill-content.ts +2 -2
  388. package/src/plugins/defaults/memory/v1/graph/consolidation.ts +4 -2
  389. package/src/plugins/defaults/memory/v1/graph/extraction.ts +4 -2
  390. package/src/plugins/defaults/memory/v1/graph/retriever.ts +4 -1
  391. package/src/plugins/defaults/memory/v2/reranker.ts +6 -1
  392. package/src/plugins/defaults/memory/v3/__tests__/card.test.ts +24 -0
  393. package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +125 -0
  394. package/src/plugins/defaults/memory/v3/__tests__/sections.test.ts +26 -0
  395. package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +30 -0
  396. package/src/plugins/defaults/memory/v3/card.ts +3 -2
  397. package/src/plugins/defaults/memory/v3/pool-select.ts +5 -3
  398. package/src/plugins/defaults/memory/v3/sections.ts +9 -3
  399. package/src/plugins/defaults/memory/v3/shadow-plugin.ts +5 -2
  400. package/src/plugins/defaults/memory/worker.ts +6 -0
  401. package/src/plugins/mcp-servers.ts +15 -75
  402. package/src/providers/__tests__/connection-model-compat.test.ts +3 -3
  403. package/src/providers/__tests__/retry-callsite.test.ts +41 -0
  404. package/src/providers/__tests__/retry-outbound-sanitize.test.ts +176 -0
  405. package/src/providers/anthropic/client.ts +0 -47
  406. package/src/providers/gemini/client.ts +9 -23
  407. package/src/providers/gemini-thought-signature.test.ts +63 -0
  408. package/src/providers/gemini-thought-signature.ts +44 -0
  409. package/src/providers/inference/__tests__/endpoint-probe.test.ts +42 -3
  410. package/src/providers/inference/auth.ts +2 -3
  411. package/src/providers/inference/connection-availability.ts +27 -8
  412. package/src/providers/inference/endpoint-probe.ts +10 -1
  413. package/src/providers/inference/resolve-auth.ts +22 -4
  414. package/src/providers/inference/service-account-token.ts +248 -0
  415. package/src/providers/model-catalog.ts +34 -0
  416. package/src/providers/model-intents.ts +10 -5
  417. package/src/providers/openai/__tests__/chat-completions-output-token-limit.test.ts +141 -0
  418. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +146 -18
  419. package/src/providers/openai/__tests__/chat-completions-provider-thought-signature.test.ts +613 -0
  420. package/src/providers/openai/__tests__/google-thought-signature.test.ts +235 -0
  421. package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +83 -1
  422. package/src/providers/openai/chat-completions-provider.ts +212 -79
  423. package/src/providers/openai/codex-models.ts +0 -4
  424. package/src/providers/openai/google-thought-signature.ts +320 -0
  425. package/src/providers/opencode/client.test.ts +73 -4
  426. package/src/providers/opencode/client.ts +60 -3
  427. package/src/providers/openrouter/client.ts +20 -0
  428. package/src/providers/outbound-request-sanitize.ts +96 -0
  429. package/src/providers/retry.ts +30 -19
  430. package/src/providers/unparseable-tool-args.ts +5 -1
  431. package/src/runtime/AGENTS.md +3 -1
  432. package/src/runtime/__tests__/channel-send.test.ts +443 -0
  433. package/src/runtime/__tests__/desktop-stream-upgrade.test.ts +89 -0
  434. package/src/runtime/__tests__/host-observe.test.ts +40 -1
  435. package/src/runtime/__tests__/live-voice-upgrade.test.ts +58 -0
  436. package/src/runtime/__tests__/runtime-ws-test-utils.ts +72 -0
  437. package/src/runtime/__tests__/watch-stream-upgrade.test.ts +59 -0
  438. package/src/runtime/agent-wake.ts +2 -1
  439. package/src/runtime/assistant-event-hub.ts +21 -3
  440. package/src/runtime/auth/same-actor.ts +38 -3
  441. package/src/runtime/channel-send.ts +316 -0
  442. package/src/runtime/host-observe.ts +11 -1
  443. package/src/runtime/http-server.ts +160 -151
  444. package/src/runtime/routes/__tests__/acp-routes.test.ts +159 -4
  445. package/src/runtime/routes/__tests__/apps-refresh-route.test.ts +205 -0
  446. package/src/runtime/routes/__tests__/channel-send-routes.test.ts +127 -0
  447. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +108 -1
  448. package/src/runtime/routes/__tests__/default-provider-routes.test.ts +2 -2
  449. package/src/runtime/routes/__tests__/dictation-cleanup-guard.test.ts +107 -0
  450. package/src/runtime/routes/__tests__/mcp-add-default-risk.test.ts +16 -12
  451. package/src/runtime/routes/acp-routes.test.ts +15 -0
  452. package/src/runtime/routes/acp-routes.ts +72 -9
  453. package/src/runtime/routes/app-management-routes.ts +73 -0
  454. package/src/runtime/routes/app-routes.ts +1 -1
  455. package/src/runtime/routes/channel-send-routes.ts +85 -0
  456. package/src/runtime/routes/conversation-list-routes.ts +14 -3
  457. package/src/runtime/routes/conversation-query-routes.ts +40 -0
  458. package/src/runtime/routes/conversation-routes.ts +16 -3
  459. package/src/runtime/routes/credential-routes.ts +19 -10
  460. package/src/runtime/routes/desktop-setup-routes.test.ts +74 -0
  461. package/src/runtime/routes/desktop-setup-routes.ts +35 -0
  462. package/src/runtime/routes/diagnostics-routes.ts +61 -4
  463. package/src/runtime/routes/events-routes.ts +4 -0
  464. package/src/runtime/routes/host-cu-routes.ts +6 -0
  465. package/src/runtime/routes/index.ts +4 -2
  466. package/src/runtime/routes/log-export-routes.ts +2 -0
  467. package/src/runtime/routes/mcp-auth-routes.ts +39 -47
  468. package/src/runtime/routes/migration-routes.ts +6 -20
  469. package/src/runtime/routes/oauth-commands-routes.ts +36 -0
  470. package/src/runtime/routes/platform-routes.ts +18 -15
  471. package/src/runtime/routes/workspace-routes.test.ts +55 -26
  472. package/src/runtime/routes/workspace-routes.ts +46 -63
  473. package/src/runtime/routes/workspace-tree-walk.test.ts +244 -0
  474. package/src/runtime/routes/workspace-tree-walk.ts +240 -0
  475. package/src/runtime/routes/workspace-utils.ts +17 -0
  476. package/src/runtime/services/__tests__/conversation-serializer.test.ts +28 -0
  477. package/src/runtime/services/conversation-serializer.ts +16 -3
  478. package/src/runtime/sync/resource-sync-events.ts +2 -1
  479. package/src/schedule/run-script.ts +2 -1
  480. package/src/schedule/schedule-store.ts +9 -2
  481. package/src/schedule/worker.ts +1 -1
  482. package/src/security/__tests__/ces-http-record-backend.test.ts +137 -0
  483. package/src/security/ces-http-record-backend.ts +120 -0
  484. package/src/security/ces-rpc-record-backend.ts +7 -0
  485. package/src/security/untrusted-content.ts +3 -1
  486. package/src/skills/__tests__/workspace-skill-md-signature.test.ts +60 -0
  487. package/src/skills/workspace-skill-md-signature.ts +50 -0
  488. package/src/subagent/consult-context.ts +12 -25
  489. package/src/tools/__tests__/side-effect-abort-guard.test.ts +1 -0
  490. package/src/tools/acp/list-agents.test.ts +3 -1
  491. package/src/tools/acp/spawn.test.ts +146 -11
  492. package/src/tools/acp/spawn.ts +21 -3
  493. package/src/tools/acp/status.test.ts +94 -0
  494. package/src/tools/acp/status.ts +40 -3
  495. package/src/tools/app-control/skill-proxy-bridge.ts +2 -1
  496. package/src/tools/browser/browser-execution.ts +1 -1
  497. package/src/tools/browser/cdp-client/accessibility-snapshot.ts +9 -3
  498. package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +2 -1
  499. package/src/tools/browser/cdp-client/extension-cdp-client.ts +3 -2
  500. package/src/tools/browser/network-recorder.ts +2 -1
  501. package/src/tools/capability-offer.test.ts +22 -2
  502. package/src/tools/capability-offer.ts +16 -1
  503. package/src/tools/computer-use/definitions.ts +121 -2
  504. package/src/tools/computer-use/skill-proxy-bridge.ts +2 -1
  505. package/src/tools/credentials/metadata-store.ts +100 -0
  506. package/src/tools/filesystem/search.ts +2 -1
  507. package/src/tools/host-filesystem/edit.test.ts +2 -1
  508. package/src/tools/host-filesystem/edit.ts +2 -2
  509. package/src/tools/host-filesystem/read.test.ts +2 -1
  510. package/src/tools/host-filesystem/read.ts +2 -2
  511. package/src/tools/host-filesystem/transfer.test.ts +2 -1
  512. package/src/tools/host-filesystem/transfer.ts +2 -2
  513. package/src/tools/host-filesystem/write.test.ts +2 -1
  514. package/src/tools/host-filesystem/write.ts +2 -2
  515. package/src/tools/host-terminal/host-shell.test.ts +16 -0
  516. package/src/tools/host-terminal/host-shell.ts +4 -20
  517. package/src/tools/network/web-search.ts +6 -1
  518. package/src/tools/policy-context.ts +6 -7
  519. package/src/tools/skills/scaffold-managed.ts +61 -3
  520. package/src/tools/subagent/spawn.ts +5 -8
  521. package/src/tools/terminal/__tests__/sanitized-bash.test.ts +57 -0
  522. package/src/tools/terminal/sanitized-bash.ts +105 -0
  523. package/src/tools/watch/watch-retro-report.ts +0 -8
  524. package/src/types/host-capabilities.ts +5 -3
  525. package/src/util/__tests__/language-subtag.test.ts +23 -1
  526. package/src/util/ca-bundle.ts +13 -0
  527. package/src/util/language-subtag.ts +16 -0
  528. package/src/util/platform.ts +8 -0
  529. package/src/util/unicode.ts +15 -1
  530. package/src/watch/watch-timeline.ts +2 -2
  531. package/src/workflows/run-manager.ts +2 -1
  532. package/src/workspace/git-porcelain.ts +66 -0
  533. package/src/workspace/git-service.ts +6 -55
  534. package/src/workspace/migrations/154-repair-retired-codex-gpt-5-4-model-ids.ts +258 -0
  535. package/src/workspace/migrations/155-move-front-model-config-to-voice.ts +78 -0
  536. package/src/workspace/migrations/156-extract-workspace-mcp-json.ts +214 -0
  537. package/src/workspace/migrations/__tests__/155-move-front-model-config-to-voice.test.ts +102 -0
  538. package/src/workspace/migrations/__tests__/156-extract-workspace-mcp-json.test.ts +199 -0
  539. package/src/workspace/migrations/registry.ts +6 -0
  540. package/src/__tests__/app-source-watcher.test.ts +0 -218
  541. package/src/__tests__/config-watcher-skill-reseed.test.ts +0 -359
  542. package/src/__tests__/speaker-identification.test.ts +0 -56
  543. package/src/calls/speaker-identification.ts +0 -233
  544. package/src/daemon/app-source-watcher.ts +0 -218
  545. package/src/messaging/providers/whatsapp/client.ts +0 -29
  546. package/src/runtime/routes/debug-bash-routes.ts +0 -173
  547. package/src/util/fs-watcher-error.ts +0 -36
  548. /package/src/{live-voice → calls}/progress-phrases.ts +0 -0
@@ -24,8 +24,11 @@ import {
24
24
  AcpResumeError,
25
25
  AcpSessionNotFoundError,
26
26
  } from "../../acp/session-manager.js";
27
- import type { AcpSessionState } from "../../acp/types.js";
28
- import type { AssistantEvent } from "../../api/index.js";
27
+ import { type AcpSessionState, isLiveAcpStatus } from "../../acp/types.js";
28
+ import {
29
+ AcpSessionModelUpdateEventSchema,
30
+ type AssistantEvent,
31
+ } from "../../api/index.js";
29
32
  import { getConfig } from "../../config/loader.js";
30
33
  import { createGuardianRequestForConfirmation } from "../../permissions/confirmation-guardian-request.js";
31
34
  import type { UserDecision } from "../../permissions/types.js";
@@ -53,6 +56,10 @@ const log = getLogger("acp-routes");
53
56
  const DEFAULT_SESSION_LIMIT = 50;
54
57
  const MAX_SESSION_LIMIT = 500;
55
58
 
59
+ /** The option shape the `acp_session_model_update` event already publishes. */
60
+ const acpModelOptionsSchema =
61
+ AcpSessionModelUpdateEventSchema.shape.availableModels;
62
+
56
63
  const sessionEntrySchema = z.object({
57
64
  id: z.string(),
58
65
  agentId: z.string(),
@@ -68,6 +75,15 @@ const sessionEntrySchema = z.object({
68
75
  /** Credential failure that ended the run, when one did. Drives the inline
69
76
  * Connect card on reopen; cleared when a replacement token is stored. */
70
77
  authErrorCode: z.string().optional(),
78
+ /** Model a live session is running on. Absent for history rows, which have
79
+ * no live process to ask. */
80
+ model: z.string().optional(),
81
+ /** Models a live session could run on. Absent for history rows. */
82
+ availableModels: acpModelOptionsSchema.optional(),
83
+ /** Scopes live model revisions to one assistant process. */
84
+ modelRevisionEpoch: z.string().uuid().optional(),
85
+ /** Orders live model state within `modelRevisionEpoch`. */
86
+ modelRevision: z.number().int().nonnegative().optional(),
71
87
  usedTokens: z.number().optional(),
72
88
  contextSize: z.number().optional(),
73
89
  costAmount: z.number().optional(),
@@ -214,17 +230,26 @@ async function spawnSession({ body, abortSignal }: RouteHandlerArgs) {
214
230
  const task = body?.task as string | undefined;
215
231
  const conversationId = body?.conversationId as string | undefined;
216
232
  const cwd = (body?.cwd as string | undefined) ?? process.cwd();
233
+ const model = body?.model ?? undefined;
217
234
 
218
235
  if (!agent || !task || !conversationId) {
219
236
  throw new BadRequestError("agent, task, and conversationId are required");
220
237
  }
238
+ if (model !== undefined && typeof model !== "string") {
239
+ throw new BadRequestError("model must be a string when provided");
240
+ }
221
241
 
222
242
  // High-risk approval gate. Block BEFORE any side effects — resolution can
223
243
  // trigger a `bun` global install (auto-install.ts) and `manager.spawn`
224
244
  // launches the host subprocess — so an unapproved request mutates nothing.
225
245
  const decision = await awaitRouteApproval({
226
246
  toolName: "acp_spawn",
227
- input: { agent, task, cwd },
247
+ input: {
248
+ agent,
249
+ task,
250
+ cwd,
251
+ ...(model !== undefined ? { model } : {}),
252
+ },
228
253
  conversationId,
229
254
  signal: abortSignal,
230
255
  });
@@ -262,17 +287,33 @@ async function spawnSession({ body, abortSignal }: RouteHandlerArgs) {
262
287
  );
263
288
 
264
289
  const manager = getAcpSessionManager();
265
- const { acpSessionId, protocolSessionId } = await manager.spawn(
290
+ const {
291
+ acpSessionId,
292
+ protocolSessionId,
293
+ requestedModel,
294
+ effectiveModel,
295
+ modelWarning,
296
+ } = await manager.spawn(
266
297
  agent,
267
298
  agentConfig,
268
299
  task,
269
300
  cwd,
270
301
  conversationId,
271
302
  broadcastMessage,
303
+ { model },
272
304
  );
273
305
 
274
306
  log.info({ acpSessionId, protocolSessionId, agent }, "ACP spawn succeeded");
275
- return { acpSessionId, protocolSessionId, agent };
307
+ // A refused model is a warning, not a failed spawn: the session is live on
308
+ // the agent's own model.
309
+ return {
310
+ acpSessionId,
311
+ protocolSessionId,
312
+ agent,
313
+ requestedModel: requestedModel ?? null,
314
+ effectiveModel: effectiveModel ?? null,
315
+ ...(modelWarning ? { modelWarning } : {}),
316
+ };
276
317
  }
277
318
 
278
319
  async function steerSession({ pathParams, body }: RouteHandlerArgs) {
@@ -588,10 +629,7 @@ function deleteSession({ pathParams }: RouteHandlerArgs) {
588
629
 
589
630
  try {
590
631
  const state = manager.getStatus(id);
591
- if (
592
- !Array.isArray(state) &&
593
- (state.status === "running" || state.status === "initializing")
594
- ) {
632
+ if (!Array.isArray(state) && isLiveAcpStatus(state.status)) {
595
633
  throw new ConflictError(
596
634
  `ACP session "${id}" is still ${state.status}. Cancel or close it before deleting.`,
597
635
  );
@@ -640,11 +678,32 @@ export const ROUTES: RouteDefinition[] = [
640
678
  task: z.string().describe("Task description"),
641
679
  conversationId: z.string(),
642
680
  cwd: z.string().describe("Working directory").optional(),
681
+ model: z
682
+ .string()
683
+ .optional()
684
+ .describe("Optional model id or alias to request for the session."),
643
685
  }),
644
686
  responseBody: z.object({
645
687
  acpSessionId: z.string(),
646
688
  protocolSessionId: z.string(),
647
689
  agent: z.string(),
690
+ requestedModel: z
691
+ .string()
692
+ .nullable()
693
+ .describe("The model explicitly requested for this spawn, if any."),
694
+ effectiveModel: z
695
+ .string()
696
+ .nullable()
697
+ .describe(
698
+ "The top-level session model reported by the ACP adapter, if any.",
699
+ ),
700
+ modelWarning: z
701
+ .string()
702
+ .optional()
703
+ .describe(
704
+ "Why the requested model was not applied. The session is running " +
705
+ "on the agent's own model.",
706
+ ),
648
707
  }),
649
708
  },
650
709
  {
@@ -852,6 +911,10 @@ function listMergedSessions(opts: { limit: number; conversationId?: string }): {
852
911
  parentToolUseId: s.parentToolUseId,
853
912
  authErrorCode: s.authErrorCode,
854
913
  authErrorCredential: s.authErrorCredential,
914
+ model: s.model,
915
+ availableModels: s.availableModels,
916
+ modelRevisionEpoch: s.modelRevisionEpoch,
917
+ modelRevision: s.modelRevision,
855
918
  usedTokens: s.latestUsage?.usedTokens,
856
919
  contextSize: s.latestUsage?.contextSize,
857
920
  costAmount: s.latestUsage?.costAmount,
@@ -51,6 +51,7 @@ import { compileApp, runCompile } from "../../bundler/app-compiler.js";
51
51
  import { scanBundle } from "../../bundler/bundle-scanner.js";
52
52
  import type { SignatureJson } from "../../bundler/bundle-signer.js";
53
53
  import { verifyBundleSignature } from "../../bundler/signature-verifier.js";
54
+ import { notifyAppSurfacesChanged } from "../../daemon/app-change-notify.js";
54
55
  import { compareSemver } from "../../daemon/handlers/shared.js";
55
56
  import { computeContentId } from "../../util/content-id.js";
56
57
  import { getLogger } from "../../util/logger.js";
@@ -128,6 +129,17 @@ const appListItemSchema = z.object({
128
129
 
129
130
  type AppListItem = z.infer<typeof appListItemSchema>;
130
131
 
132
+ const compileDiagnosticSchema = z.object({
133
+ text: z.string(),
134
+ location: z
135
+ .object({
136
+ file: z.string(),
137
+ line: z.number(),
138
+ column: z.number(),
139
+ })
140
+ .optional(),
141
+ });
142
+
131
143
  /**
132
144
  * The pin route's body. `RouteDefinition.requestBody` is a codegen signal and
133
145
  * does not validate, so the handler parses against this too; declaring it once
@@ -896,6 +908,44 @@ async function handleOpenApp({ pathParams }: RouteHandlerArgs) {
896
908
  return { ...result, html };
897
909
  }
898
910
 
911
+ /**
912
+ * Compile an app's source into dist/ and refresh open surfaces.
913
+ * Runs in the assistant process so open surfaces, client broadcasts, and
914
+ * published-app redeploy see the new build. Workspace and plugin apps both
915
+ * compile in place so local plugin edits can be rebuilt without waiting for
916
+ * the plugin source watcher.
917
+ */
918
+ async function handleRefreshApp({ pathParams }: RouteHandlerArgs) {
919
+ const appId = pathParams?.id;
920
+ if (!appId) {
921
+ throw new BadRequestError("App id is required.");
922
+ }
923
+
924
+ const source = resolveAppSource(appId);
925
+ if (!source) {
926
+ throw new NotFoundError(
927
+ `App "${appId}" not found. Run 'assistant apps list' to see available apps.`,
928
+ );
929
+ }
930
+
931
+ const compileResult = await compileApp(source.sourceDir);
932
+ notifyAppSurfacesChanged(source.id, { fileChange: true });
933
+
934
+ return {
935
+ ok: true as const,
936
+ appId: source.id,
937
+ name: source.name,
938
+ compiled: compileResult.ok,
939
+ compile_duration_ms: compileResult.durationMs,
940
+ ...(compileResult.ok
941
+ ? {}
942
+ : {
943
+ compile_errors: compileResult.errors,
944
+ compile_warnings: compileResult.warnings,
945
+ }),
946
+ };
947
+ }
948
+
899
949
  /**
900
950
  * Reject a mutation targeting a plugin-bundled app. Plugin apps are owned by
901
951
  * their plugin and are read-only over this surface — their source is not
@@ -1253,6 +1303,29 @@ export const ROUTES: RouteDefinition[] = [
1253
1303
  origin: z.string(),
1254
1304
  }),
1255
1305
  },
1306
+ {
1307
+ operationId: "apps_refresh",
1308
+ endpoint: "apps/:id/refresh",
1309
+ method: "POST",
1310
+ policy: {
1311
+ requiredScopes: ["settings.write"],
1312
+ allowedPrincipalTypes: ACTOR_PRINCIPALS,
1313
+ },
1314
+ handler: handleRefreshApp,
1315
+ summary: "Compile an app",
1316
+ description:
1317
+ "Compile workspace or plugin app source into dist/ and refresh open surfaces.",
1318
+ tags: ["apps"],
1319
+ responseBody: z.object({
1320
+ ok: z.literal(true),
1321
+ appId: z.string(),
1322
+ name: z.string(),
1323
+ compiled: z.boolean(),
1324
+ compile_duration_ms: z.number(),
1325
+ compile_errors: z.array(compileDiagnosticSchema).optional(),
1326
+ compile_warnings: z.array(compileDiagnosticSchema).optional(),
1327
+ }),
1328
+ },
1256
1329
  {
1257
1330
  operationId: "apps_delete",
1258
1331
  endpoint: "apps/:id/delete",
@@ -92,7 +92,7 @@ function serveCompiledApp(
92
92
  );
93
93
  const body = isLegacySingleFileDir(appDir)
94
94
  ? UNSUPPORTED_LEGACY_APP_HTML
95
- : `<p>App has not been compiled yet. Edit a source file to trigger a build.</p>`;
95
+ : `<p>App has not been compiled yet. Run <code>assistant apps refresh</code> to build.</p>`;
96
96
  return (
97
97
  `<!DOCTYPE html><html><head><title>${escapedName}</title></head>` +
98
98
  `<body>${body}</body></html>`
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Route handler for a model-directed text send to a channel chat.
3
+ *
4
+ * POST /v1/channels/send: deliver text to a chat on a channel through the
5
+ * channel's transport, recorded after acknowledgement. The
6
+ * messaging tool's channel branch runs the same function in-process; this
7
+ * route is the door for the CLI and scripts, so the two doors share one
8
+ * seam and one record.
9
+ */
10
+
11
+ import { z } from "zod";
12
+
13
+ import { CHANNEL_IDS } from "../../channels/types.js";
14
+ import { ACTOR_PRINCIPALS } from "../auth/route-policy.js";
15
+ import {
16
+ ChannelNotAddressableError,
17
+ ChannelSendFailedError,
18
+ sendChannelText,
19
+ } from "../channel-send.js";
20
+ import { BadGatewayError, BadRequestError } from "./errors.js";
21
+ import { parseBody } from "./parse-body.js";
22
+ import type { RouteDefinition, RouteHandlerArgs } from "./types.js";
23
+
24
+ const ProactiveTargetSchema = z.object({
25
+ kind: z.literal("chat"),
26
+ chatId: z.string().min(1).describe("Chat id in the channel's own id space"),
27
+ threadId: z
28
+ .string()
29
+ .min(1)
30
+ .optional()
31
+ .describe("Thread within the chat, in the channel's own id space"),
32
+ });
33
+
34
+ const ChannelSendRequestSchema = z.object({
35
+ channel: z.enum(CHANNEL_IDS).describe("Channel to send on"),
36
+ target: ProactiveTargetSchema,
37
+ text: z.string().min(1).describe("Text to send"),
38
+ renderRichly: z
39
+ .boolean()
40
+ .optional()
41
+ .describe("Ask the channel for its rich rendering, where it has one"),
42
+ });
43
+
44
+ const ChannelSendResponseSchema = z.object({
45
+ channel: z.enum(CHANNEL_IDS),
46
+ chatId: z.string(),
47
+ threadId: z.string().optional(),
48
+ messageIds: z.array(z.string()),
49
+ lastMessageId: z.string(),
50
+ recordedIn: z.string().optional(),
51
+ });
52
+
53
+ async function handleChannelSend({ body }: RouteHandlerArgs) {
54
+ const request = parseBody(ChannelSendRequestSchema, body);
55
+ try {
56
+ return await sendChannelText(request);
57
+ } catch (e) {
58
+ if (e instanceof ChannelNotAddressableError) {
59
+ throw new BadRequestError(e.message);
60
+ }
61
+ if (e instanceof ChannelSendFailedError) {
62
+ throw new BadGatewayError(e.message);
63
+ }
64
+ throw e;
65
+ }
66
+ }
67
+
68
+ export const ROUTES: RouteDefinition[] = [
69
+ {
70
+ operationId: "channels_send_post",
71
+ endpoint: "channels/send",
72
+ method: "POST",
73
+ policy: {
74
+ requiredScopes: ["chat.write"],
75
+ allowedPrincipalTypes: ACTOR_PRINCIPALS,
76
+ },
77
+ summary: "Send text to a channel chat",
78
+ description:
79
+ "Deliver text to a chat on a channel through the channel's transport, recorded after the channel acknowledges it.",
80
+ tags: ["channels"],
81
+ handler: handleChannelSend,
82
+ requestBody: ChannelSendRequestSchema,
83
+ responseBody: ChannelSendResponseSchema,
84
+ },
85
+ ];
@@ -14,6 +14,7 @@ import { z } from "zod";
14
14
 
15
15
  import { CHANNEL_IDS } from "../../channels/types.js";
16
16
  import { isAssistantInitiatedThreadsEnabled } from "../../config/assistant-initiated-threads-gate.js";
17
+ import { localeFromAcceptLanguage } from "../../i18n/index.js";
17
18
  import { channelBindingSchema } from "../../messaging/channel-binding-schema.js";
18
19
  import {
19
20
  type Confidence,
@@ -244,7 +245,10 @@ function parseEnumQueryParam<const T extends readonly string[]>(
244
245
  );
245
246
  }
246
247
 
247
- function handleListConversations({ queryParams = {} }: RouteHandlerArgs) {
248
+ function handleListConversations({
249
+ queryParams = {},
250
+ headers,
251
+ }: RouteHandlerArgs) {
248
252
  const limit = Number(queryParams.limit ?? 50);
249
253
  const offset = Number(queryParams.offset ?? 0);
250
254
  // "background" is the back-compat umbrella (background + scheduled); newer
@@ -365,6 +369,7 @@ function handleListConversations({ queryParams = {} }: RouteHandlerArgs) {
365
369
  // Checks in-memory flag first (hot path), falls back to the
366
370
  // persisted `processing_started_at` column for cold conversations.
367
371
  isProcessing: isConversationProcessing(conversation.id),
372
+ locale: localeFromAcceptLanguage(headers?.["accept-language"]),
368
373
  }),
369
374
  ),
370
375
  nextOffset,
@@ -579,8 +584,14 @@ function handleMarkUnread({ body = {}, headers }: RouteHandlerArgs) {
579
584
  }
580
585
  }
581
586
 
582
- function handleGetConversation({ pathParams = {} }: RouteHandlerArgs) {
583
- const detail = buildConversationDetailResponse(pathParams.id!);
587
+ function handleGetConversation({
588
+ pathParams = {},
589
+ headers,
590
+ }: RouteHandlerArgs) {
591
+ const detail = buildConversationDetailResponse(
592
+ pathParams.id!,
593
+ localeFromAcceptLanguage(headers?.["accept-language"]),
594
+ );
584
595
  if (!detail) {
585
596
  throw new NotFoundError(`Conversation ${pathParams.id} not found`);
586
597
  }
@@ -29,6 +29,7 @@ import {
29
29
  LatencyBreakdownSchema,
30
30
  LLMRequestLogEntrySchema,
31
31
  } from "../../api/responses/llm-request-log-entry.js";
32
+ import { scrubNulledAcpAgentLeaves } from "../../config/acp-agent-write.js";
32
33
  import {
33
34
  catalogEntryFor,
34
35
  type InputModalities,
@@ -92,6 +93,7 @@ import {
92
93
  log,
93
94
  } from "../../daemon/handlers/shared.js";
94
95
  import { rescheduleHeartbeatIfTimezoneChanged } from "../../heartbeat/heartbeat-service.js";
96
+ import { overlayWorkspaceMcpForConfigRead } from "../../mcp/workspace-mcp-config.js";
95
97
  import {
96
98
  getAssistantMessageIdsInTurn,
97
99
  getConversation,
@@ -687,6 +689,38 @@ function rejectMcpTransportHeaderWrite(patch: unknown): void {
687
689
  );
688
690
  }
689
691
 
692
+ const MCP_CONFIG_WRITE_MESSAGE =
693
+ "MCP servers are stored in mcp.json. Use assistant mcp add or assistant mcp remove.";
694
+
695
+ function stripMcpFromConfigWrite(patch: Record<string, unknown>): void {
696
+ if (Object.hasOwn(patch, "mcp")) {
697
+ delete patch.mcp;
698
+ }
699
+ }
700
+
701
+ function rejectMcpConfigSetPath(path: string, value: unknown): void {
702
+ if (path !== "mcp" && !path.startsWith("mcp.")) {
703
+ return;
704
+ }
705
+ const relative =
706
+ path === "mcp" ? value : nestPath(path.slice("mcp.".length), value);
707
+ if (patchContainsMcpTransportHeaders({ mcp: relative })) {
708
+ throw new BadRequestError(
709
+ "MCP authentication headers must be managed through MCP server add/update APIs, not generic config writes.",
710
+ );
711
+ }
712
+ throw new BadRequestError(MCP_CONFIG_WRITE_MESSAGE);
713
+ }
714
+
715
+ function nestPath(path: string, value: unknown): unknown {
716
+ const segments = path.split(".");
717
+ let current: unknown = value;
718
+ for (let i = segments.length - 1; i >= 0; i--) {
719
+ current = { [segments[i]!]: current };
720
+ }
721
+ return current;
722
+ }
723
+
690
724
  const WireProfileEntry = ProfileEntry.extend({
691
725
  supportsVision: z.boolean().optional(),
692
726
  invariant: z.boolean().optional(),
@@ -893,6 +927,7 @@ const ConfigPatchRequestSchema = z
893
927
  function handleGetConfig() {
894
928
  try {
895
929
  const config = applyContextDefaultsToRawConfig(loadRawConfig());
930
+ overlayWorkspaceMcpForConfigRead(config);
896
931
  sanitizeMcpTransportHeadersForSettingsRead(config);
897
932
  overlayEffectiveProfilesForWire(config);
898
933
  enrichProfilesForWire(config);
@@ -1594,6 +1629,7 @@ async function handlePatchConfig({ body }: RouteHandlerArgs) {
1594
1629
  normalizeManagedProfileWrites(body);
1595
1630
  rejectManagedProfileDeletion(body as Record<string, unknown>);
1596
1631
  rejectMcpTransportHeaderWrite(body);
1632
+ stripMcpFromConfigWrite(body as Record<string, unknown>);
1597
1633
 
1598
1634
  const raw = loadRawConfig();
1599
1635
  const patch = body as Record<string, unknown>;
@@ -1603,11 +1639,13 @@ async function handlePatchConfig({ body }: RouteHandlerArgs) {
1603
1639
  }
1604
1640
  deepMergeOverwrite(raw, patch);
1605
1641
  scrubRemovedServiceModes(raw);
1642
+ scrubNulledAcpAgentLeaves(raw);
1606
1643
  seedSttProviderForSparseBlock(raw);
1607
1644
 
1608
1645
  await commitConfigWrite(raw, "patch");
1609
1646
 
1610
1647
  const merged = applyContextDefaultsToRawConfig(loadRawConfig());
1648
+ overlayWorkspaceMcpForConfigRead(merged);
1611
1649
  sanitizeMcpTransportHeadersForSettingsRead(merged);
1612
1650
  overlayEffectiveProfilesForWire(merged);
1613
1651
  enrichProfilesForWire(merged);
@@ -1670,6 +1708,7 @@ async function handleSetConfig({ body }: RouteHandlerArgs) {
1670
1708
  stripWireOnlyProfileKeys(patchShape);
1671
1709
  normalizeManagedProfileWrites(patchShape);
1672
1710
  rejectManagedProfileDeletion(patchShape);
1711
+ rejectMcpConfigSetPath(path, value);
1673
1712
  rejectMcpTransportHeaderWrite(patchShape);
1674
1713
 
1675
1714
  const raw = loadRawConfig();
@@ -1708,6 +1747,7 @@ async function handleSetConfig({ body }: RouteHandlerArgs) {
1708
1747
  written.source = "managed";
1709
1748
  }
1710
1749
  }
1750
+ scrubNulledAcpAgentLeaves(raw);
1711
1751
  // A SET can create `services.stt` with a leaf like `language` and no
1712
1752
  // `provider`, which SttServiceSchema requires whenever the block exists;
1713
1753
  // the same seeding that guards PATCH keeps this write's persisted block
@@ -140,7 +140,10 @@ import {
140
140
  getOrCreateConversation,
141
141
  } from "../../persistence/conversation-key-store.js";
142
142
  import { searchConversations } from "../../persistence/conversation-queries.js";
143
- import { isNoResponseMetadata } from "../../persistence/conversation-types.js";
143
+ import {
144
+ isNoResponseMetadata,
145
+ messageMetadataIsAmbientSightKeep,
146
+ } from "../../persistence/conversation-types.js";
144
147
  import { linkRequestLogsToMessage } from "../../persistence/llm-request-log-store.js";
145
148
  import { assistantTextVisibilityOf } from "../../persistence/user-facing-content.js";
146
149
  import { MEMORY_RETROSPECTIVE_FORK_SOURCE } from "../../plugins/defaults/memory/memory-retrospective-constants.js";
@@ -159,6 +162,7 @@ import {
159
162
  getWorkspaceDir,
160
163
  getWorkspacePromptPath,
161
164
  } from "../../util/platform.js";
165
+ import { safeStringSlice } from "../../util/unicode.js";
162
166
  import { assistantEventHub, broadcastMessage } from "../assistant-event-hub.js";
163
167
  import { getCurrentSeq } from "../assistant-stream-state.js";
164
168
  import { ACTOR_PRINCIPALS } from "../auth/route-policy.js";
@@ -1037,6 +1041,9 @@ export async function handleListMessages({
1037
1041
  let backgroundToolCompletion: ConversationMessage["backgroundToolCompletion"];
1038
1042
  let systemCard: boolean | undefined;
1039
1043
  let noResponse: boolean | undefined;
1044
+ const cameraFrame = messageMetadataIsAmbientSightKeep(msg.metadata)
1045
+ ? true
1046
+ : undefined;
1040
1047
  let reaction: ConversationMessage["reaction"];
1041
1048
  let providerError: ConversationMessage["providerError"];
1042
1049
  let deletedAt: number | undefined;
@@ -1136,6 +1143,7 @@ export async function handleListMessages({
1136
1143
  backgroundToolCompletion,
1137
1144
  systemCard,
1138
1145
  noResponse,
1146
+ cameraFrame,
1139
1147
  reaction,
1140
1148
  providerError,
1141
1149
  slackMessage,
@@ -1353,6 +1361,7 @@ export async function handleListMessages({
1353
1361
  : {}),
1354
1362
  ...(m.systemCard ? { systemCard: true } : {}),
1355
1363
  ...(m.noResponse ? { noResponse: true } : {}),
1364
+ ...(m.cameraFrame ? { cameraFrame: true as const } : {}),
1356
1365
  // The row's own marker, so a client gates per-row presentation on what
1357
1366
  // this row was written with rather than on the live flag.
1358
1367
  ...(m.assistantTextVisibility
@@ -3289,11 +3298,15 @@ async function generateLlmSuggestion(
3289
3298
  ): Promise<string | null> {
3290
3299
  const log = (await import("../../util/logger.js")).getLogger("runtime-http");
3291
3300
  const truncatedAssistant = escapeXmlContent(
3292
- assistantText.length > 2000 ? assistantText.slice(-2000) : assistantText,
3301
+ assistantText.length > 2000
3302
+ ? safeStringSlice(assistantText, assistantText.length - 2000)
3303
+ : assistantText,
3293
3304
  );
3294
3305
  const truncatedUser =
3295
3306
  priorUserText && priorUserText.length > 500
3296
- ? escapeXmlContent(priorUserText.slice(-500))
3307
+ ? escapeXmlContent(
3308
+ safeStringSlice(priorUserText, priorUserText.length - 500),
3309
+ )
3297
3310
  : priorUserText
3298
3311
  ? escapeXmlContent(priorUserText)
3299
3312
  : priorUserText;
@@ -43,7 +43,7 @@ import {
43
43
  deleteCredentialMetadata,
44
44
  getCredentialMetadata,
45
45
  getCredentialMetadataById,
46
- listCredentialMetadata,
46
+ listCredentialRecordsLive,
47
47
  } from "../../tools/credentials/metadata-store.js";
48
48
  import type { CredentialInjectionTemplate } from "../../tools/credentials/policy-types.js";
49
49
  import {
@@ -228,6 +228,9 @@ function assertNotPlatformManaged(
228
228
  }
229
229
  }
230
230
 
231
+ const CREDENTIAL_STORE_UNREACHABLE =
232
+ "Credential store is unreachable. Retry in a moment, or check that the credential vault is running.";
233
+
231
234
  // ---------------------------------------------------------------------------
232
235
  // Handlers
233
236
  // ---------------------------------------------------------------------------
@@ -235,9 +238,14 @@ function assertNotPlatformManaged(
235
238
  async function handleCredentialsList({ body }: RouteHandlerArgs) {
236
239
  const search = (body as { search?: string } | undefined)?.search;
237
240
 
241
+ const live = await listCredentialRecordsLive();
242
+ if (live.unreachable) {
243
+ throw new InternalError(CREDENTIAL_STORE_UNREACHABLE);
244
+ }
245
+
238
246
  // Platform-provisioned credentials are not the user's to act on, so they
239
247
  // never become a Settings row (and never a click-to-reveal).
240
- let allMetadata = listCredentialMetadata().filter(
248
+ let allMetadata = live.records.filter(
241
249
  (m) => !isPlatformManagedCredential(m.service, m.field),
242
250
  );
243
251
 
@@ -272,7 +280,12 @@ async function handleCredentialsList({ body }: RouteHandlerArgs) {
272
280
 
273
281
  const credentials = await Promise.all(
274
282
  allMetadata.map(async (m) => {
275
- const secret = await getSecureKeyAsync(credentialKey(m.service, m.field));
283
+ const { value: secret, unreachable } = await getSecureKeyResultAsync(
284
+ credentialKey(m.service, m.field),
285
+ );
286
+ if (unreachable) {
287
+ throw new InternalError(CREDENTIAL_STORE_UNREACHABLE);
288
+ }
276
289
  const connection = connectionsByProvider.get(m.service);
277
290
  return buildCredentialOutput(m, secret, connection);
278
291
  }),
@@ -310,9 +323,7 @@ async function handleCredentialsInspect({ body }: RouteHandlerArgs) {
310
323
 
311
324
  if (!lookup.metadata && (secret == null || secret.length === 0)) {
312
325
  if (unreachable) {
313
- throw new InternalError(
314
- "Credential store is unreachable — ensure the assistant is running",
315
- );
326
+ throw new InternalError(CREDENTIAL_STORE_UNREACHABLE);
316
327
  }
317
328
  throw new BadRequestError("Credential not found");
318
329
  }
@@ -374,9 +385,7 @@ async function handleCredentialsReveal({ body, headers }: RouteHandlerArgs) {
374
385
 
375
386
  if (secret == null || secret.length === 0) {
376
387
  if (unreachable) {
377
- throw new InternalError(
378
- "Credential store is unreachable — ensure the assistant is running",
379
- );
388
+ throw new InternalError(CREDENTIAL_STORE_UNREACHABLE);
380
389
  }
381
390
  throw new BadRequestError("Credential not found");
382
391
  }
@@ -620,7 +629,7 @@ export const ROUTES: RouteDefinition[] = [
620
629
  },
621
630
  summary: "List all credentials with metadata",
622
631
  description:
623
- "Return all stored credentials with metadata, OAuth connection info, and platform-managed credentials.",
632
+ "Return credentials from the live credential vault, with OAuth connection info and platform-managed credentials. Fails if the vault is unreachable.",
624
633
  tags: ["credentials"],
625
634
  requestBody: z.object({
626
635
  search: z.string().optional().describe("Filter by substring match"),