@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
@@ -0,0 +1,135 @@
1
+ /**
2
+ * Tests for resolving an app query by name, slug, or id.
3
+ */
4
+
5
+ import { mkdirSync, rmSync, writeFileSync } from "node:fs";
6
+ import { tmpdir } from "node:os";
7
+ import { join } from "node:path";
8
+ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
9
+
10
+ import { createApp } from "../apps/app-store.js";
11
+ import { resolveAppQuery } from "../apps/resolve-app.js";
12
+ import { getWorkspacePluginsDir } from "../util/platform.js";
13
+
14
+ let workspaceDir: string;
15
+ let previousWorkspaceDir: string | undefined;
16
+
17
+ function freshWorkspace(): string {
18
+ return join(
19
+ tmpdir(),
20
+ `vellum-resolve-app-${Date.now()}-${Math.random().toString(36).slice(2)}`,
21
+ );
22
+ }
23
+
24
+ function installPlugin(name: string): string {
25
+ const pluginDir = join(getWorkspacePluginsDir(), name);
26
+ mkdirSync(pluginDir, { recursive: true });
27
+ writeFileSync(
28
+ join(pluginDir, "package.json"),
29
+ JSON.stringify({ name, version: "1.0.0" }),
30
+ );
31
+ return pluginDir;
32
+ }
33
+
34
+ function bundleApp(pluginDir: string, app: string): void {
35
+ const appDir = join(pluginDir, "apps", app);
36
+ mkdirSync(appDir, { recursive: true });
37
+ writeFileSync(join(appDir, "index.html"), "<h1>Plugin app</h1>");
38
+ }
39
+
40
+ beforeEach(() => {
41
+ previousWorkspaceDir = process.env.VELLUM_WORKSPACE_DIR;
42
+ workspaceDir = freshWorkspace();
43
+ process.env.VELLUM_WORKSPACE_DIR = workspaceDir;
44
+ });
45
+
46
+ afterEach(() => {
47
+ rmSync(workspaceDir, { recursive: true, force: true });
48
+ if (previousWorkspaceDir === undefined) {
49
+ delete process.env.VELLUM_WORKSPACE_DIR;
50
+ } else {
51
+ process.env.VELLUM_WORKSPACE_DIR = previousWorkspaceDir;
52
+ }
53
+ });
54
+
55
+ describe("resolveAppQuery", () => {
56
+ test("resolves a workspace app by display name, slug, and id", () => {
57
+ const created = createApp({
58
+ name: "Budget",
59
+ schemaJson: "{}",
60
+ htmlDefinition: "<h1>Budget</h1>",
61
+ });
62
+
63
+ const byName = resolveAppQuery("Budget");
64
+ expect(byName.ok).toBe(true);
65
+ if (byName.ok) {
66
+ expect(byName.app.id).toBe(created.id);
67
+ }
68
+
69
+ const byNameCase = resolveAppQuery("budget");
70
+ expect(byNameCase.ok).toBe(true);
71
+ if (byNameCase.ok) {
72
+ expect(byNameCase.app.id).toBe(created.id);
73
+ }
74
+
75
+ const slug = created.dirName ?? "";
76
+ expect(slug.length).toBeGreaterThan(0);
77
+ const bySlug = resolveAppQuery(slug);
78
+ expect(bySlug.ok).toBe(true);
79
+ if (bySlug.ok) {
80
+ expect(bySlug.app.id).toBe(created.id);
81
+ }
82
+
83
+ const byId = resolveAppQuery(created.id);
84
+ expect(byId.ok).toBe(true);
85
+ if (byId.ok) {
86
+ expect(byId.app.id).toBe(created.id);
87
+ }
88
+ });
89
+
90
+ test("returns not_found for an unknown name", () => {
91
+ const result = resolveAppQuery("missing");
92
+ expect(result).toEqual({
93
+ ok: false,
94
+ reason: "not_found",
95
+ query: "missing",
96
+ });
97
+ });
98
+
99
+ test("returns ambiguous when two apps share a display name", () => {
100
+ const first = createApp({
101
+ name: "Notes",
102
+ schemaJson: "{}",
103
+ htmlDefinition: "<h1>One</h1>",
104
+ });
105
+ const second = createApp({
106
+ name: "Notes",
107
+ schemaJson: "{}",
108
+ htmlDefinition: "<h1>Two</h1>",
109
+ });
110
+
111
+ const result = resolveAppQuery("Notes");
112
+ expect(result.ok).toBe(false);
113
+ if (!result.ok && result.reason === "ambiguous") {
114
+ const ids = result.matches.map((m) => m.id).sort();
115
+ expect(ids).toEqual([first.id, second.id].sort());
116
+ } else {
117
+ throw new Error("expected ambiguous resolution");
118
+ }
119
+ });
120
+
121
+ test("resolves a plugin app by directory name", () => {
122
+ const pluginDir = installPlugin("charts");
123
+ bundleApp(pluginDir, "viewer");
124
+
125
+ const result = resolveAppQuery("viewer");
126
+ expect(result.ok).toBe(true);
127
+ if (result.ok) {
128
+ expect(result.app.origin).toEqual({
129
+ kind: "plugin",
130
+ pluginName: "charts",
131
+ });
132
+ expect(result.app.id).toBe("plugins~charts~viewer");
133
+ }
134
+ });
135
+ });
@@ -1103,6 +1103,7 @@ describe("scaffold_managed_skill tool", () => {
1103
1103
  activation_hints: HINTS,
1104
1104
  overwrite: true,
1105
1105
  files: [{ path: "references/failure-modes.md", content: "gotchas" }],
1106
+ change_summary: "Added the failure modes reference.",
1106
1107
  },
1107
1108
  makeRetrospectiveContext(),
1108
1109
  );
@@ -1550,6 +1551,7 @@ describe("scaffold_managed_skill tool", () => {
1550
1551
  body_markdown: "V2 procedure.",
1551
1552
  activation_hints: HINTS,
1552
1553
  overwrite: true,
1554
+ change_summary: "Refined the procedure.",
1553
1555
  },
1554
1556
  makeRetrospectiveContext({ conversationId: "retro-run-conv" }),
1555
1557
  lineageSeam(),
@@ -1702,6 +1704,7 @@ describe("background skill update notification", () => {
1702
1704
  body_markdown: "1. Refined steps.",
1703
1705
  activation_hints: HINTS,
1704
1706
  overwrite: true,
1707
+ change_summary: "Refined the steps.",
1705
1708
  },
1706
1709
  makeRetrospectiveContext({ conversationId: "retro-run-conv" }),
1707
1710
  lineage(),
@@ -1715,9 +1718,7 @@ describe("background skill update notification", () => {
1715
1718
  // must be the conversation the work came from, not the hidden fork.
1716
1719
  expect(signal.sourceContextId).toBe("source-conv");
1717
1720
  expect(signal.contextPayload?.skillId).toBe("weekly-export");
1718
- expect(String(signal.contextPayload?.summary)).toContain(
1719
- "Weekly Report Export",
1720
- );
1721
+ expect(signal.contextPayload?.summary).toBe("Refined the steps.");
1721
1722
  // The home feed falls back to `title`/`body` when no channel copy was
1722
1723
  // rendered, which is the intended quiet shape for this signal. Without
1723
1724
  // them a suppressed delivery would leave the feed item unwritten.
@@ -1725,15 +1726,236 @@ describe("background skill update notification", () => {
1725
1726
  expect(signal.contextPayload?.title).toBe(
1726
1727
  "Skill updated: Weekly Report Export",
1727
1728
  );
1728
- expect(String(signal.contextPayload?.body)).toContain(
1729
- "Weekly Report Export",
1730
- );
1729
+ expect(signal.contextPayload?.body).toBe("Refined the steps.");
1731
1730
  // Deduped per skill per day so repeated refinements cannot flood the feed.
1732
1731
  expect(signal.dedupeKey).toBe(
1733
1732
  `skill-updated:weekly-export:${new Date().toISOString().slice(0, 10)}`,
1734
1733
  );
1735
1734
  });
1736
1735
 
1736
+ test("the notice body is the pass's change_summary, so the feed says what changed", async () => {
1737
+ await seedAssistantSkill("weekly-export", "Old body.");
1738
+
1739
+ const result = await executeScaffoldManagedSkill(
1740
+ {
1741
+ skill_id: "weekly-export",
1742
+ name: "Weekly Report Export",
1743
+ description: "export the weekly usage report",
1744
+ body_markdown: "1. Refined steps.",
1745
+ activation_hints: HINTS,
1746
+ overwrite: true,
1747
+ change_summary:
1748
+ "Added the retry after an expired session and the export endpoint that held steady.",
1749
+ },
1750
+ makeRetrospectiveContext({ conversationId: "retro-run-conv" }),
1751
+ lineage(),
1752
+ );
1753
+
1754
+ expect(result.isError).toBe(false);
1755
+ expect(emittedSignals).toHaveLength(1);
1756
+ const payload = emittedSignals[0]!.contextPayload;
1757
+ // The title still names the skill; the body is the change itself.
1758
+ expect(payload?.title).toBe("Skill updated: Weekly Report Export");
1759
+ expect(payload?.body).toBe(
1760
+ "Added the retry after an expired session and the export endpoint that held steady.",
1761
+ );
1762
+ expect(payload?.summary).toBe(payload?.body);
1763
+ });
1764
+
1765
+ test("a background overwrite without a change_summary is refused before the write", async () => {
1766
+ await seedAssistantSkill("weekly-export", "Old body.");
1767
+ const skillFile = join(TEST_DIR, "skills", "weekly-export", "SKILL.md");
1768
+ const before = readFileSync(skillFile, "utf-8");
1769
+
1770
+ for (const input of [
1771
+ {},
1772
+ // Whitespace-only reads as missing, not as an empty notice.
1773
+ { change_summary: " \n " },
1774
+ ]) {
1775
+ const result = await executeScaffoldManagedSkill(
1776
+ {
1777
+ skill_id: "weekly-export",
1778
+ name: "Weekly Report Export",
1779
+ description: "export the weekly usage report",
1780
+ body_markdown: "1. Refined steps.",
1781
+ activation_hints: HINTS,
1782
+ overwrite: true,
1783
+ ...input,
1784
+ },
1785
+ makeRetrospectiveContext({ conversationId: "retro-run-conv" }),
1786
+ lineage(),
1787
+ );
1788
+
1789
+ // Self-correcting: the error returns to the pass in the same turn, so
1790
+ // the retry carries the field and nothing was lost meanwhile.
1791
+ expect(result.isError).toBe(true);
1792
+ expect(result.content).toBe(
1793
+ 'Error: change_summary is required when updating an existing skill: pass one or two short sentences (under 200 characters) for the person who reads the "Skill updated" notice, naming what you changed and what in the trace prompted it (for example "Added the retry after an expired session and the export endpoint that held steady.").',
1794
+ );
1795
+ expect(readFileSync(skillFile, "utf-8")).toBe(before);
1796
+ expect(emittedSignals).toHaveLength(0);
1797
+ }
1798
+ });
1799
+
1800
+ test("a background call on an existing skill without overwrite hears the overwrite error, not the summary requirement", async () => {
1801
+ await seedAssistantSkill("weekly-export", "Old body.");
1802
+
1803
+ const result = await executeScaffoldManagedSkill(
1804
+ {
1805
+ skill_id: "weekly-export",
1806
+ name: "Weekly Report Export",
1807
+ description: "export the weekly usage report",
1808
+ body_markdown: "1. Refined steps.",
1809
+ activation_hints: HINTS,
1810
+ },
1811
+ makeRetrospectiveContext({ conversationId: "retro-run-conv" }),
1812
+ lineage(),
1813
+ );
1814
+
1815
+ // The flag is what actually blocks this call, so that is the error it
1816
+ // gets: a retry that only adds change_summary would still fail.
1817
+ expect(result.isError).toBe(true);
1818
+ expect(result.content).toContain("Set overwrite=true to replace it.");
1819
+ expect(result.content).not.toContain("change_summary");
1820
+ expect(emittedSignals).toHaveLength(0);
1821
+ });
1822
+
1823
+ test("the ownership backstop speaks before the change_summary requirement", async () => {
1824
+ await executeScaffoldManagedSkill(
1825
+ {
1826
+ skill_id: "user-owned",
1827
+ name: "User Owned",
1828
+ description: "a person wrote this",
1829
+ body_markdown: "V1.",
1830
+ activation_hints: HINTS,
1831
+ },
1832
+ makeContext(),
1833
+ );
1834
+
1835
+ const result = await executeScaffoldManagedSkill(
1836
+ {
1837
+ skill_id: "user-owned",
1838
+ name: "User Owned",
1839
+ description: "a person wrote this",
1840
+ body_markdown: "V2.",
1841
+ activation_hints: HINTS,
1842
+ overwrite: true,
1843
+ },
1844
+ makeRetrospectiveContext({ conversationId: "retro-run-conv" }),
1845
+ lineage(),
1846
+ );
1847
+
1848
+ expect(result.isError).toBe(true);
1849
+ expect(result.content).toContain("not verifiably assistant-authored");
1850
+ });
1851
+
1852
+ test("a user-directed overwrite needs no change_summary: it never notifies", async () => {
1853
+ await executeScaffoldManagedSkill(
1854
+ {
1855
+ skill_id: "user-skill",
1856
+ name: "User Skill",
1857
+ description: "asked for",
1858
+ body_markdown: "V1.",
1859
+ activation_hints: HINTS,
1860
+ },
1861
+ makeContext(),
1862
+ );
1863
+
1864
+ const result = await executeScaffoldManagedSkill(
1865
+ {
1866
+ skill_id: "user-skill",
1867
+ name: "User Skill",
1868
+ description: "asked for",
1869
+ body_markdown: "V2.",
1870
+ activation_hints: HINTS,
1871
+ overwrite: true,
1872
+ },
1873
+ makeContext(),
1874
+ );
1875
+
1876
+ expect(result.isError).toBe(false);
1877
+ });
1878
+
1879
+ test("change_summary is sanitized like any other notification body", async () => {
1880
+ await seedAssistantSkill("weekly-export", "Old body.");
1881
+
1882
+ await executeScaffoldManagedSkill(
1883
+ {
1884
+ skill_id: "weekly-export",
1885
+ name: "Weekly Report Export",
1886
+ description: "export the weekly usage report",
1887
+ body_markdown: "1. Refined steps.",
1888
+ activation_hints: HINTS,
1889
+ overwrite: true,
1890
+ change_summary: `Added\u0007 the retry.\r\n\n\n\nDropped the login step. ${"x".repeat(300)}`,
1891
+ },
1892
+ makeRetrospectiveContext({ conversationId: "retro-run-conv" }),
1893
+ lineage(),
1894
+ );
1895
+
1896
+ const body = String(emittedSignals[0]!.contextPayload?.body);
1897
+ // Control characters go, horizontal whitespace collapses, blank-line runs
1898
+ // collapse to one paragraph break, and the whole thing is clamped to the
1899
+ // shared notification preview budget.
1900
+ expect(
1901
+ body.startsWith("Added the retry.\n\nDropped the login step. xxx"),
1902
+ ).toBe(true);
1903
+ expect(body).not.toMatch(/[\u0000-\u0008\u000b\u000c\u000e-\u001f]/);
1904
+ expect(body.length).toBe(200);
1905
+ expect(body.endsWith("\u2026")).toBe(true);
1906
+ });
1907
+
1908
+ test("a non-string change_summary is a self-correcting error", async () => {
1909
+ await seedAssistantSkill("weekly-export", "Old body.");
1910
+
1911
+ const result = await executeScaffoldManagedSkill(
1912
+ {
1913
+ skill_id: "weekly-export",
1914
+ name: "Weekly Report Export",
1915
+ description: "export the weekly usage report",
1916
+ body_markdown: "1. Refined steps.",
1917
+ activation_hints: HINTS,
1918
+ overwrite: true,
1919
+ change_summary: ["not", "a", "string"],
1920
+ },
1921
+ makeRetrospectiveContext({ conversationId: "retro-run-conv" }),
1922
+ lineage(),
1923
+ );
1924
+
1925
+ expect(result.isError).toBe(true);
1926
+ expect(result.content).toBe("Error: change_summary must be a string");
1927
+ expect(emittedSignals).toHaveLength(0);
1928
+ });
1929
+
1930
+ test("the registered tool's schema declares change_summary as an optional string", () => {
1931
+ // A call through the registered tool is validated against TOOLS.json
1932
+ // first, and the validator rejects undeclared keys, so the field has to
1933
+ // be in the schema for the executor to ever see it. It stays optional
1934
+ // there: the requirement holds only for a background overwrite of an
1935
+ // existing skill, which the schema cannot express, so the executor
1936
+ // enforces it.
1937
+ const scaffoldTool = readScaffoldToolEntry();
1938
+ expect(scaffoldTool.input_schema.properties.change_summary).toMatchObject({
1939
+ type: "string",
1940
+ });
1941
+ expect(scaffoldTool.input_schema.required).not.toContain("change_summary");
1942
+
1943
+ const result = validateInputAgainstSchema(
1944
+ "scaffold_managed_skill",
1945
+ {
1946
+ skill_id: "s",
1947
+ name: "N",
1948
+ description: "D",
1949
+ body_markdown: "B",
1950
+ activation_hints: HINTS,
1951
+ overwrite: true,
1952
+ change_summary: "Added the retry step.",
1953
+ },
1954
+ scaffoldTool.input_schema,
1955
+ );
1956
+ expect(result.ok).toBe(true);
1957
+ });
1958
+
1737
1959
  test("it falls back to the run conversation when fork lineage does not resolve", async () => {
1738
1960
  await seedAssistantSkill("weekly-export", "Old body.");
1739
1961
 
@@ -1745,6 +1967,7 @@ describe("background skill update notification", () => {
1745
1967
  body_markdown: "1. Refined steps.",
1746
1968
  activation_hints: HINTS,
1747
1969
  overwrite: true,
1970
+ change_summary: "Refined the steps.",
1748
1971
  },
1749
1972
  makeRetrospectiveContext({ conversationId: "retro-run-conv" }),
1750
1973
  { getConversation: () => null },
@@ -8,6 +8,7 @@ import {
8
8
  getCAPath,
9
9
  issueLeafCert,
10
10
  } from "../outbound-proxy/index.js";
11
+ import { writeCombinedCABundle } from "../util/ca-bundle.js";
11
12
 
12
13
  let dataDir: string;
13
14
 
@@ -19,6 +20,40 @@ afterAll(async () => {
19
20
  await rm(dataDir, { recursive: true, force: true });
20
21
  });
21
22
 
23
+ test("combined trust verifies certificates from both CA sources", async () => {
24
+ const roots = [join(dataDir, "system"), join(dataDir, "extra")];
25
+ const leaves: string[] = [];
26
+ for (const root of roots) {
27
+ await ensureLocalCA(root);
28
+ const caDir = join(root, "proxy-ca");
29
+ await issueLeafCert(caDir, "example.com");
30
+ leaves.push(join(caDir, "issued", "example.com.pem"));
31
+ }
32
+ const bundle = join(dataDir, "combined.pem");
33
+ await writeCombinedCABundle(
34
+ getCAPath(roots[0]!),
35
+ getCAPath(roots[1]!),
36
+ bundle,
37
+ );
38
+ const verify = Bun.spawn(
39
+ ["openssl", "verify", "-CAfile", bundle, ...leaves],
40
+ {
41
+ stdout: "pipe",
42
+ stderr: "pipe",
43
+ windowsHide: true,
44
+ },
45
+ );
46
+ const [code, output, error] = await Promise.all([
47
+ verify.exited,
48
+ new Response(verify.stdout).text(),
49
+ new Response(verify.stderr).text(),
50
+ ]);
51
+ expect({ code, error }).toEqual({ code: 0, error: "" });
52
+ for (const leaf of leaves) {
53
+ expect(output).toContain(`${leaf}: OK`);
54
+ }
55
+ });
56
+
22
57
  describe("ensureLocalCA", () => {
23
58
  test("creates CA cert and key with correct permissions", async () => {
24
59
  await ensureLocalCA(dataDir);