@vellumai/assistant 0.12.1-dev.202609141913.5e16607 → 0.12.1-staging.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (439) hide show
  1. package/AGENTS.md +0 -4
  2. package/ARCHITECTURE.md +12 -11
  3. package/docs/architecture/turn-actor.md +8 -15
  4. package/docs/guardian-request-flow.md +0 -16
  5. package/openapi.yaml +49 -291
  6. package/package.json +1 -1
  7. package/src/__tests__/always-loaded-tools-guard.test.ts +10 -18
  8. package/src/__tests__/app-compiler.test.ts +0 -6
  9. package/src/__tests__/app-control-tool-schemas.test.ts +2 -5
  10. package/src/__tests__/app-dir-path-guard.test.ts +3 -1
  11. package/src/__tests__/app-source-watcher.test.ts +218 -0
  12. package/src/{calls/__tests__ → __tests__}/call-controller.test.ts +82 -503
  13. package/src/__tests__/computer-use-skill-proxy-bridge.test.ts +2 -5
  14. package/src/__tests__/computer-use-tools.test.ts +5 -17
  15. package/src/__tests__/config-schema-cmd.test.ts +2 -2
  16. package/src/__tests__/config-schema.test.ts +29 -28
  17. package/src/__tests__/config-watcher-skill-reseed.test.ts +359 -0
  18. package/src/__tests__/config-watcher.test.ts +8 -23
  19. package/src/__tests__/connection-model-compat.test.ts +2 -2
  20. package/src/__tests__/conversation-key-store-disk-view.test.ts +1 -1
  21. package/src/__tests__/conversation-pairing.test.ts +0 -26
  22. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +10 -4
  23. package/src/__tests__/conversation-queue.test.ts +207 -48
  24. package/src/__tests__/conversation-retry-discard.test.ts +1 -54
  25. package/src/__tests__/conversation-skill-tools.test.ts +0 -27
  26. package/src/__tests__/conversation-surfaces-app-control.test.ts +3 -8
  27. package/src/__tests__/conversation-surfaces-task-progress.test.ts +0 -67
  28. package/src/__tests__/conversation-title-service.test.ts +10 -14
  29. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +5 -3
  30. package/src/__tests__/conversation-tool-setup-attribution.test.ts +0 -51
  31. package/src/__tests__/credential-record-retire.test.ts +1 -171
  32. package/src/__tests__/credential-routes.test.ts +3 -212
  33. package/src/__tests__/credential-security-invariants.test.ts +2 -4
  34. package/src/__tests__/cu-unified-flow.test.ts +2 -8
  35. package/src/__tests__/document-sync-tags.test.ts +0 -25
  36. package/src/__tests__/drain-kick-guard.test.ts +2 -0
  37. package/src/__tests__/drain-requeue-on-contention.test.ts +6 -0
  38. package/src/__tests__/evict-conversations-for-reload.test.ts +1 -1
  39. package/src/__tests__/host-bash-proxy.test.ts +45 -26
  40. package/src/__tests__/host-browser-proxy.test.ts +17 -6
  41. package/src/__tests__/host-file-proxy-targeted.test.ts +0 -23
  42. package/src/__tests__/host-file-proxy.test.ts +23 -11
  43. package/src/__tests__/host-transfer-proxy-targeted.test.ts +9 -30
  44. package/src/__tests__/llm-catalog-parity.test.ts +0 -25
  45. package/src/__tests__/mcp-auth-routes.test.ts +9 -6
  46. package/src/__tests__/mcp-config-secret-boundary.test.ts +136 -78
  47. package/src/__tests__/mcp-health-check.test.ts +11 -8
  48. package/src/__tests__/mcp-list-plugin-servers.test.ts +10 -8
  49. package/src/{calls/__tests__ → __tests__}/media-stream-audio-transcode.test.ts +1 -1
  50. package/src/{calls/__tests__ → __tests__}/media-stream-output.test.ts +8 -37
  51. package/src/{calls/__tests__ → __tests__}/media-stream-parser.test.ts +1 -1
  52. package/src/{calls/__tests__ → __tests__}/media-stream-server-integration.test.ts +57 -150
  53. package/src/{calls/__tests__ → __tests__}/media-stream-stt-session.test.ts +5 -5
  54. package/src/{calls/__tests__ → __tests__}/media-turn-detector.test.ts +1 -1
  55. package/src/__tests__/messages-after-tiebreaker.test.ts +0 -91
  56. package/src/__tests__/messages-read-boundary-guard.test.ts +1 -1
  57. package/src/__tests__/messaging-send-tool.test.ts +548 -355
  58. package/src/__tests__/model-intents.test.ts +0 -3
  59. package/src/__tests__/notification-vellum-adapter.test.ts +2 -85
  60. package/src/__tests__/outlook-messaging-provider.test.ts +6 -6
  61. package/src/__tests__/platform-callback-registration.test.ts +16 -8
  62. package/src/__tests__/platform-client-verify-credential.test.ts +0 -2
  63. package/src/__tests__/plugin-import-boundary-guard.test.ts +0 -13
  64. package/src/__tests__/script-proxy-certs.test.ts +0 -35
  65. package/src/__tests__/skill-feature-flags-integration.test.ts +1 -1
  66. package/src/__tests__/skill-feature-flags.test.ts +2 -2
  67. package/src/__tests__/speaker-identification.test.ts +56 -0
  68. package/src/__tests__/subagent-tool-gate-mode.test.ts +3 -19
  69. package/src/__tests__/subagent-tools.test.ts +2 -3
  70. package/src/__tests__/telephony-tts-guard.test.ts +3 -38
  71. package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +0 -4
  72. package/src/{calls/__tests__/voice-session-bridge-turn-flow.test.ts → __tests__/voice-session-bridge.test.ts} +12 -20
  73. package/src/__tests__/watch-retro-tool-availability.test.ts +17 -8
  74. package/src/__tests__/workspace-git-service.test.ts +0 -26
  75. package/src/acp/__tests__/agent-process.test.ts +4 -258
  76. package/src/acp/__tests__/auth-required.test.ts +0 -96
  77. package/src/acp/__tests__/client-handler.test.ts +2 -90
  78. package/src/acp/__tests__/session-manager-persistence.test.ts +1 -5
  79. package/src/acp/__tests__/session-manager-resume.test.ts +6 -270
  80. package/src/acp/__tests__/session-manager-usage.test.ts +2 -4
  81. package/src/acp/__tests__/session-manager.test.ts +4 -895
  82. package/src/acp/agent-process.test.ts +1 -4
  83. package/src/acp/agent-process.ts +9 -94
  84. package/src/acp/auth-required.ts +1 -50
  85. package/src/acp/auto-install.test.ts +7 -9
  86. package/src/acp/client-handler.ts +3 -16
  87. package/src/acp/resolve-agent.test.ts +6 -220
  88. package/src/acp/resolve-agent.ts +23 -88
  89. package/src/acp/session-manager.test.ts +3 -196
  90. package/src/acp/session-manager.ts +18 -441
  91. package/src/acp/types.ts +5 -50
  92. package/src/api/events/app-files-changed.ts +3 -4
  93. package/src/api/events/notification-intent.ts +0 -2
  94. package/src/api/index.ts +0 -6
  95. package/src/apps/app-store.ts +1 -1
  96. package/src/bundler/app-compiler.ts +0 -15
  97. package/src/calls/__tests__/voice-control-protocol.test.ts +25 -58
  98. package/src/calls/__tests__/voice-session-bridge.test.ts +1 -37
  99. package/src/calls/__tests__/voice-triage-escalate.test.ts +0 -105
  100. package/src/calls/call-constants.ts +4 -24
  101. package/src/calls/call-controller.ts +199 -499
  102. package/src/calls/call-transport.ts +0 -16
  103. package/src/calls/media-stream-output.ts +0 -32
  104. package/src/calls/media-stream-server.ts +30 -82
  105. package/src/calls/media-stream-stt-session.ts +0 -15
  106. package/src/calls/resolve-call-tts-provider.ts +2 -33
  107. package/src/calls/speaker-identification.ts +233 -0
  108. package/src/calls/tts-call-strategy.ts +5 -9
  109. package/src/calls/types.ts +0 -2
  110. package/src/calls/voice-control-protocol.ts +21 -53
  111. package/src/calls/voice-session-bridge.ts +46 -59
  112. package/src/calls/voice-triage-escalate.ts +56 -175
  113. package/src/cli/commands/apps.help.ts +13 -67
  114. package/src/cli/commands/apps.ts +0 -183
  115. package/src/cli/commands/bash.help.ts +13 -8
  116. package/src/cli/commands/bash.ts +25 -7
  117. package/src/cli/commands/channels/index.help.ts +2 -70
  118. package/src/cli/commands/channels/index.ts +3 -16
  119. package/src/cli/commands/credentials.help.ts +2 -6
  120. package/src/cli/commands/credentials.ts +2 -2
  121. package/src/cli/commands/mcp.help.ts +8 -8
  122. package/src/cli/commands/mcp.ts +1 -1
  123. package/src/cli/commands/roadmap.help.ts +1 -5
  124. package/src/cli/commands/roadmap.ts +1 -1
  125. package/src/cli/lib/bundled-marketplace.json +90 -103
  126. package/src/config/__tests__/default-profile-catalog.test.ts +0 -22
  127. package/src/config/__tests__/memory-retrospective-schema.test.ts +1 -15
  128. package/src/config/__tests__/platform-rehydration.test.ts +9 -99
  129. package/src/config/acp-defaults.test.ts +3 -13
  130. package/src/config/acp-defaults.ts +6 -23
  131. package/src/config/acp-schema.ts +2 -30
  132. package/src/config/bundled-skills/acp/SKILL.md +14 -22
  133. package/src/config/bundled-skills/acp/TOOLS.json +1 -5
  134. package/src/config/bundled-skills/app-builder/SKILL.md +9 -12
  135. package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +1 -1
  136. package/src/config/bundled-skills/messaging/SKILL.md +3 -3
  137. package/src/config/bundled-skills/messaging/TOOLS.json +3 -3
  138. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +139 -67
  139. package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +1 -1
  140. package/src/config/bundled-skills/schedule/SKILL.md +4 -3
  141. package/src/config/bundled-skills/screen-annotation/SKILL.md +1 -8
  142. package/src/config/bundled-skills/screen-annotation/tools/screen-annotation.test.ts +1 -4
  143. package/src/config/bundled-skills/skill-management/TOOLS.json +2 -2
  144. package/src/config/default-profile-catalog.ts +1 -1
  145. package/src/config/env.ts +2 -2
  146. package/src/config/feature-flag-registry.json +12 -24
  147. package/src/config/loader.ts +0 -1
  148. package/src/config/memory-v3-gate.test.ts +0 -31
  149. package/src/config/memory-v3-gate.ts +16 -20
  150. package/src/config/platform-rehydration.ts +27 -11
  151. package/src/config/schema.ts +2 -2
  152. package/src/config/schemas/__tests__/live-voice.test.ts +158 -8
  153. package/src/config/schemas/calls.ts +28 -1
  154. package/src/config/schemas/live-voice.ts +173 -1
  155. package/src/config/schemas/mcp.ts +1 -1
  156. package/src/config/schemas/memory-retrospective.ts +0 -10
  157. package/src/config/schemas/services.ts +0 -8
  158. package/src/config/schemas/skills.ts +14 -0
  159. package/src/credential-execution/process-manager.test.ts +19 -18
  160. package/src/daemon/__tests__/conversation-tool-setup.test.ts +121 -66
  161. package/src/daemon/__tests__/plugin-mcp-reconcile.test.ts +1 -4
  162. package/src/daemon/app-source-watcher.ts +218 -0
  163. package/src/daemon/config-watcher.ts +194 -13
  164. package/src/daemon/conversation-process.ts +64 -35
  165. package/src/daemon/conversation-skill-tools.ts +31 -13
  166. package/src/daemon/conversation-store.ts +1 -1
  167. package/src/daemon/conversation-surfaces.ts +8 -10
  168. package/src/daemon/conversation-tool-setup.ts +43 -37
  169. package/src/daemon/conversation.ts +3 -0
  170. package/src/daemon/host-bash-proxy.ts +12 -8
  171. package/src/daemon/host-browser-proxy.ts +1 -15
  172. package/src/daemon/host-file-proxy.ts +10 -8
  173. package/src/daemon/host-transfer-proxy.ts +18 -14
  174. package/src/daemon/lifecycle.ts +5 -0
  175. package/src/daemon/mcp-reload-service.ts +5 -5
  176. package/src/daemon/message-types/sync.ts +0 -1
  177. package/src/daemon/orphan-reaper.ts +1 -1
  178. package/src/daemon/providers-setup.ts +1 -1
  179. package/src/daemon/shutdown-handlers.ts +9 -1
  180. package/src/daemon/skill-memory-refresh.ts +0 -64
  181. package/src/daemon/tool-side-effects.ts +18 -7
  182. package/src/home/__tests__/feed-writer.test.ts +0 -22
  183. package/src/home/feed-writer.ts +2 -11
  184. package/src/hooks/types.ts +0 -33
  185. package/src/inbound/platform-callback-registration.ts +5 -4
  186. package/src/live-voice/__tests__/live-voice-metrics.test.ts +3 -0
  187. package/src/live-voice/__tests__/live-voice-progress.test.ts +15 -15
  188. package/src/live-voice/__tests__/live-voice-vad.test.ts +2 -2
  189. package/src/{calls → live-voice}/__tests__/progress-narration.test.ts +4 -4
  190. package/src/{calls → live-voice}/__tests__/progress-phrases.test.ts +1 -1
  191. package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +69 -13
  192. package/src/live-voice/live-voice-session.ts +693 -213
  193. package/src/{calls → live-voice}/progress-narration.ts +2 -2
  194. package/src/live-voice/protocol.ts +2 -0
  195. package/src/mcp/__tests__/effective-config.test.ts +2 -2
  196. package/src/mcp/effective-config.ts +3 -3
  197. package/src/mcp/mcp-header-store.ts +45 -29
  198. package/src/mcp/startup.ts +4 -8
  199. package/src/messaging/provider.ts +3 -10
  200. package/src/messaging/providers/callback-routing.ts +0 -18
  201. package/src/messaging/providers/channel-transport.ts +0 -56
  202. package/src/messaging/providers/discord/transport.ts +0 -17
  203. package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +17 -6
  204. package/src/messaging/providers/slack/adapter.ts +44 -7
  205. package/src/messaging/providers/slack/transport.ts +0 -16
  206. package/src/messaging/providers/telegram-bot/adapter.ts +48 -1
  207. package/src/messaging/providers/telegram-bot/client.ts +26 -0
  208. package/src/messaging/providers/telegram-bot/transport.ts +0 -20
  209. package/src/messaging/providers/whatsapp/adapter.ts +48 -1
  210. package/src/messaging/providers/whatsapp/client.ts +29 -0
  211. package/src/messaging/providers/whatsapp/transport.ts +0 -18
  212. package/src/monitoring/__tests__/workspace-git-heartbeat.test.ts +0 -8
  213. package/src/notifications/__tests__/assistant-reply-producer.test.ts +0 -55
  214. package/src/notifications/__tests__/guardian-feed-projection.test.ts +2 -158
  215. package/src/notifications/__tests__/home-feed-side-effect.test.ts +3 -153
  216. package/src/notifications/__tests__/schedule-result-producer.test.ts +3 -99
  217. package/src/notifications/adapters/macos.ts +0 -3
  218. package/src/notifications/assistant-reply-producer.ts +3 -8
  219. package/src/notifications/conversation-pairing.ts +16 -36
  220. package/src/notifications/delivered-post-record.ts +1 -11
  221. package/src/notifications/guardian-feed-projection.ts +5 -125
  222. package/src/notifications/home-feed-side-effect.ts +7 -55
  223. package/src/notifications/schedule-result-producer.ts +22 -66
  224. package/src/oauth/__tests__/seed-providers-managed.test.ts +0 -54
  225. package/src/oauth/byo-connection.test.ts +0 -45
  226. package/src/oauth/byo-connection.ts +6 -36
  227. package/src/oauth/connection-resolver.test.ts +0 -73
  228. package/src/oauth/connection-resolver.ts +2 -95
  229. package/src/oauth/seed-providers.ts +0 -165
  230. package/src/outbound-proxy/certs.ts +5 -3
  231. package/src/permissions/checker.test.ts +6 -11
  232. package/src/persistence/compaction-log-store-clickhouse.ts +3 -3
  233. package/src/persistence/conversation-crud.ts +63 -71
  234. package/src/persistence/conversation-lineage.ts +4 -12
  235. package/src/persistence/conversation-title-service.ts +26 -9
  236. package/src/persistence/llm-request-log-sink-clickhouse.ts +3 -3
  237. package/src/persistence/llm-request-log-source-clickhouse.ts +3 -3
  238. package/src/persistence/message-reads.ts +0 -30
  239. package/src/persistence/migrations/157-invite-contact-id.ts +6 -18
  240. package/src/persistence/schema/memory-core.ts +0 -5
  241. package/src/platform/client.test.ts +2 -4
  242. package/src/platform/client.ts +10 -2
  243. package/src/plugin-api/constants.ts +0 -2
  244. package/src/plugin-api/index.ts +0 -1
  245. package/src/plugin-api/types.ts +0 -2
  246. package/src/plugins/defaults/index.ts +0 -2
  247. package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +0 -46
  248. package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +5 -9
  249. package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +10 -20
  250. package/src/plugins/defaults/memory/__tests__/memory-retrospective-state.test.ts +0 -219
  251. package/src/plugins/defaults/memory/__tests__/memory-retrospective-sweep.test.ts +0 -31
  252. package/src/plugins/defaults/memory/__tests__/memory-retrospective-trigger-check.test.ts +0 -63
  253. package/src/plugins/defaults/memory/__tests__/shutdown-hook.test.ts +0 -4
  254. package/src/plugins/defaults/memory/graph/capability-seed.ts +6 -4
  255. package/src/plugins/defaults/memory/hooks/init.ts +0 -10
  256. package/src/plugins/defaults/memory/hooks/shutdown.ts +0 -2
  257. package/src/plugins/defaults/memory/jobs-worker.ts +0 -13
  258. package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +60 -34
  259. package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +5 -3
  260. package/src/plugins/defaults/memory/memory-retrospective-job.ts +3 -12
  261. package/src/plugins/defaults/memory/memory-retrospective-state.ts +71 -312
  262. package/src/plugins/defaults/memory/memory-retrospective-sweep.ts +1 -2
  263. package/src/plugins/defaults/memory/memory-retrospective-trigger-check.ts +3 -5
  264. package/src/plugins/defaults/memory/startup.ts +1 -6
  265. package/src/plugins/defaults/memory/substrate/__tests__/skill-content.test.ts +9 -7
  266. package/src/plugins/defaults/memory/substrate/skill-content.ts +2 -2
  267. package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +0 -89
  268. package/src/plugins/defaults/memory/worker.ts +0 -6
  269. package/src/plugins/mcp-servers.ts +75 -15
  270. package/src/providers/__tests__/connection-model-compat.test.ts +3 -3
  271. package/src/providers/gemini/client.ts +23 -9
  272. package/src/providers/inference/auth.ts +3 -2
  273. package/src/providers/inference/connection-availability.ts +8 -27
  274. package/src/providers/inference/resolve-auth.ts +4 -22
  275. package/src/providers/model-catalog.ts +0 -34
  276. package/src/providers/model-intents.ts +5 -10
  277. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +18 -146
  278. package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +1 -83
  279. package/src/providers/openai/chat-completions-provider.ts +78 -192
  280. package/src/providers/openai/codex-models.ts +4 -0
  281. package/src/runtime/AGENTS.md +0 -2
  282. package/src/runtime/auth/same-actor.ts +3 -38
  283. package/src/runtime/http-server.ts +151 -160
  284. package/src/runtime/routes/__tests__/acp-routes.test.ts +4 -159
  285. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +1 -108
  286. package/src/runtime/routes/__tests__/default-provider-routes.test.ts +2 -2
  287. package/src/runtime/routes/__tests__/mcp-add-default-risk.test.ts +12 -16
  288. package/src/runtime/routes/acp-routes.test.ts +0 -15
  289. package/src/runtime/routes/acp-routes.ts +9 -72
  290. package/src/runtime/routes/app-management-routes.ts +0 -73
  291. package/src/runtime/routes/app-routes.ts +1 -1
  292. package/src/runtime/routes/conversation-list-routes.ts +3 -14
  293. package/src/runtime/routes/conversation-query-routes.ts +0 -40
  294. package/src/runtime/routes/credential-routes.ts +10 -19
  295. package/src/runtime/routes/debug-bash-routes.ts +173 -0
  296. package/src/runtime/routes/diagnostics-routes.ts +3 -55
  297. package/src/runtime/routes/index.ts +2 -4
  298. package/src/runtime/routes/log-export-routes.ts +0 -2
  299. package/src/runtime/routes/mcp-auth-routes.ts +47 -39
  300. package/src/runtime/routes/migration-routes.ts +20 -6
  301. package/src/runtime/routes/platform-routes.ts +15 -18
  302. package/src/runtime/services/__tests__/conversation-serializer.test.ts +0 -28
  303. package/src/runtime/services/conversation-serializer.ts +3 -16
  304. package/src/runtime/sync/resource-sync-events.ts +1 -2
  305. package/src/schedule/worker.ts +1 -1
  306. package/src/security/ces-rpc-record-backend.ts +0 -7
  307. package/src/subagent/consult-context.ts +25 -12
  308. package/src/tools/acp/list-agents.test.ts +1 -3
  309. package/src/tools/acp/spawn.test.ts +11 -146
  310. package/src/tools/acp/spawn.ts +3 -21
  311. package/src/tools/acp/status.ts +3 -40
  312. package/src/tools/app-control/skill-proxy-bridge.ts +1 -2
  313. package/src/tools/capability-offer.test.ts +2 -22
  314. package/src/tools/capability-offer.ts +1 -16
  315. package/src/tools/computer-use/definitions.ts +2 -4
  316. package/src/tools/computer-use/skill-proxy-bridge.ts +1 -2
  317. package/src/tools/credentials/metadata-store.ts +0 -100
  318. package/src/tools/host-filesystem/edit.test.ts +1 -2
  319. package/src/tools/host-filesystem/edit.ts +2 -2
  320. package/src/tools/host-filesystem/read.test.ts +1 -2
  321. package/src/tools/host-filesystem/read.ts +2 -2
  322. package/src/tools/host-filesystem/transfer.test.ts +1 -2
  323. package/src/tools/host-filesystem/transfer.ts +2 -2
  324. package/src/tools/host-filesystem/write.test.ts +1 -2
  325. package/src/tools/host-filesystem/write.ts +2 -2
  326. package/src/tools/host-terminal/host-shell.test.ts +0 -16
  327. package/src/tools/host-terminal/host-shell.ts +20 -4
  328. package/src/tools/policy-context.ts +7 -6
  329. package/src/tools/subagent/spawn.ts +8 -5
  330. package/src/tools/watch/watch-retro-report.ts +8 -0
  331. package/src/util/__tests__/language-subtag.test.ts +1 -23
  332. package/src/util/fs-watcher-error.ts +36 -0
  333. package/src/util/language-subtag.ts +0 -16
  334. package/src/util/platform.ts +0 -8
  335. package/src/workspace/git-service.ts +54 -5
  336. package/src/workspace/migrations/registry.ts +0 -6
  337. package/scripts/smoke-desktop-window-theme.ts +0 -186
  338. package/src/__tests__/git-porcelain.test.ts +0 -47
  339. package/src/__tests__/helpers/set-workspace-mcp.ts +0 -11
  340. package/src/__tests__/message-deleted-hook-dispatch.test.ts +0 -119
  341. package/src/__tests__/resolve-app.test.ts +0 -135
  342. package/src/__tests__/service-account-token.test.ts +0 -247
  343. package/src/__tests__/source-fingerprint.test.ts +0 -184
  344. package/src/__tests__/workspace-migration-154-repair-retired-codex-gpt-5-4-model-ids.test.ts +0 -336
  345. package/src/acp/__tests__/helpers/acp-model-option.ts +0 -51
  346. package/src/acp/model-config.test.ts +0 -166
  347. package/src/acp/model-config.ts +0 -138
  348. package/src/api/events/acp-session-model-update.test.ts +0 -88
  349. package/src/api/events/acp-session-model-update.ts +0 -46
  350. package/src/apps/resolve-app.ts +0 -81
  351. package/src/apps/source-fingerprint.ts +0 -266
  352. package/src/calls/__tests__/barge-in-guard.test.ts +0 -81
  353. package/src/calls/__tests__/voice-leg-coordinator.test.ts +0 -229
  354. package/src/calls/__tests__/voice-progress-cadence.test.ts +0 -274
  355. package/src/calls/barge-in-guard.ts +0 -118
  356. package/src/calls/voice-leg-coordinator.ts +0 -189
  357. package/src/calls/voice-progress-cadence.ts +0 -423
  358. package/src/cli/commands/__tests__/apps.test.ts +0 -245
  359. package/src/cli/commands/__tests__/bash.test.ts +0 -53
  360. package/src/cli/commands/__tests__/roadmap.help.test.ts +0 -19
  361. package/src/cli/commands/channels/__tests__/send.test.ts +0 -218
  362. package/src/cli/commands/channels/send.ts +0 -128
  363. package/src/config/__tests__/platform-identity.test.ts +0 -255
  364. package/src/config/acp-agent-write.ts +0 -43
  365. package/src/config/acp-schema.test.ts +0 -81
  366. package/src/config/platform-identity.ts +0 -201
  367. package/src/config/schemas/__tests__/voice.test.ts +0 -148
  368. package/src/config/schemas/voice.ts +0 -150
  369. package/src/daemon/__tests__/skill-memory-refresh-mtime.test.ts +0 -133
  370. package/src/daemon/app-change-notify.ts +0 -45
  371. package/src/desktop/__tests__/fake-desktop.ts +0 -119
  372. package/src/desktop/desktop-chrome-policy.test.ts +0 -71
  373. package/src/desktop/desktop-chrome-policy.ts +0 -42
  374. package/src/desktop/desktop-dependencies.test.ts +0 -102
  375. package/src/desktop/desktop-dependencies.ts +0 -306
  376. package/src/desktop/desktop-feature.test.ts +0 -18
  377. package/src/desktop/desktop-feature.ts +0 -19
  378. package/src/desktop/desktop-panel-config.test.ts +0 -135
  379. package/src/desktop/desktop-panel-config.ts +0 -149
  380. package/src/desktop/desktop-session-manager.test.ts +0 -627
  381. package/src/desktop/desktop-session-manager.ts +0 -814
  382. package/src/desktop/desktop-stream-bridge.test.ts +0 -395
  383. package/src/desktop/desktop-stream-bridge.ts +0 -214
  384. package/src/desktop/desktop-wallpaper.test.ts +0 -191
  385. package/src/desktop/desktop-wallpaper.ts +0 -113
  386. package/src/desktop/desktop-window-theme.ts +0 -170
  387. package/src/i18n/AGENTS.md +0 -22
  388. package/src/i18n/__tests__/i18n.test.ts +0 -108
  389. package/src/i18n/index.ts +0 -16
  390. package/src/i18n/locales.ts +0 -93
  391. package/src/i18n/messages.ts +0 -61
  392. package/src/i18n/title.ts +0 -25
  393. package/src/mcp/__tests__/workspace-mcp-config.test.ts +0 -135
  394. package/src/mcp/spec-schema.ts +0 -111
  395. package/src/mcp/workspace-mcp-config.ts +0 -188
  396. package/src/messaging/providers/__tests__/proactive-address.test.ts +0 -95
  397. package/src/persistence/conversation-title-placeholders.ts +0 -23
  398. package/src/persistence/message-cursor.ts +0 -78
  399. package/src/persistence/migrations/__tests__/157-invite-contact-id.test.ts +0 -149
  400. package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-backfill.test.ts +0 -118
  401. package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-preserve.test.ts +0 -83
  402. package/src/plugins/defaults/memory/hooks/message-deleted.ts +0 -21
  403. package/src/plugins/defaults/memory/memory-retrospective-cursor-backfill.ts +0 -126
  404. package/src/plugins/defaults/memory/memory-retrospective-cursor-preserve.ts +0 -79
  405. package/src/plugins/defaults/memory/memory-retrospective-cursor.ts +0 -28
  406. package/src/plugins/defaults/memory/skill-md-mtime-poll.ts +0 -9
  407. package/src/providers/gemini-thought-signature.test.ts +0 -63
  408. package/src/providers/gemini-thought-signature.ts +0 -44
  409. package/src/providers/inference/service-account-token.ts +0 -248
  410. package/src/providers/openai/__tests__/chat-completions-provider-thought-signature.test.ts +0 -613
  411. package/src/providers/openai/__tests__/google-thought-signature.test.ts +0 -235
  412. package/src/providers/openai/google-thought-signature.ts +0 -320
  413. package/src/runtime/__tests__/channel-send.test.ts +0 -443
  414. package/src/runtime/__tests__/desktop-stream-upgrade.test.ts +0 -89
  415. package/src/runtime/__tests__/live-voice-upgrade.test.ts +0 -58
  416. package/src/runtime/__tests__/runtime-ws-test-utils.ts +0 -72
  417. package/src/runtime/__tests__/watch-stream-upgrade.test.ts +0 -59
  418. package/src/runtime/channel-send.ts +0 -316
  419. package/src/runtime/routes/__tests__/apps-refresh-route.test.ts +0 -205
  420. package/src/runtime/routes/__tests__/channel-send-routes.test.ts +0 -127
  421. package/src/runtime/routes/__tests__/dictation-cleanup-guard.test.ts +0 -107
  422. package/src/runtime/routes/channel-send-routes.ts +0 -85
  423. package/src/runtime/routes/desktop-setup-routes.test.ts +0 -74
  424. package/src/runtime/routes/desktop-setup-routes.ts +0 -35
  425. package/src/security/__tests__/ces-http-record-backend.test.ts +0 -137
  426. package/src/security/ces-http-record-backend.ts +0 -120
  427. package/src/skills/__tests__/workspace-skill-md-signature.test.ts +0 -60
  428. package/src/skills/workspace-skill-md-signature.ts +0 -50
  429. package/src/tools/acp/status.test.ts +0 -94
  430. package/src/tools/terminal/__tests__/sanitized-bash.test.ts +0 -57
  431. package/src/tools/terminal/sanitized-bash.ts +0 -105
  432. package/src/util/ca-bundle.ts +0 -13
  433. package/src/workspace/git-porcelain.ts +0 -66
  434. package/src/workspace/migrations/154-repair-retired-codex-gpt-5-4-model-ids.ts +0 -258
  435. package/src/workspace/migrations/155-move-front-model-config-to-voice.ts +0 -78
  436. package/src/workspace/migrations/156-extract-workspace-mcp-json.ts +0 -214
  437. package/src/workspace/migrations/__tests__/155-move-front-model-config-to-voice.test.ts +0 -102
  438. package/src/workspace/migrations/__tests__/156-extract-workspace-mcp-json.test.ts +0 -199
  439. /package/src/{calls → live-voice}/progress-phrases.ts +0 -0
@@ -1,336 +0,0 @@
1
- import {
2
- existsSync,
3
- mkdirSync,
4
- readFileSync,
5
- rmSync,
6
- writeFileSync,
7
- } from "node:fs";
8
- import { tmpdir } from "node:os";
9
- import { join } from "node:path";
10
- import { Database } from "bun:sqlite";
11
- import { afterEach, beforeEach, describe, expect, test } from "bun:test";
12
-
13
- import { repairRetiredCodexGpt54ModelIdsMigration } from "../workspace/migrations/154-repair-retired-codex-gpt-5-4-model-ids.js";
14
- import { WORKSPACE_MIGRATIONS } from "../workspace/migrations/registry.js";
15
- import { assertNotLiveDb } from "./assert-not-live-db.js";
16
-
17
- const STALE = "gpt-5.4";
18
- const STALE_MINI = "gpt-5.4-mini";
19
- const REPLACEMENT = "gpt-5.5";
20
- const REPLACEMENT_MINI = "gpt-5.6-luna";
21
-
22
- const API_KEY_AUTH = '{"type":"api_key","credential":"credential/openai"}';
23
- const SUBSCRIPTION_AUTH =
24
- '{"type":"oauth_subscription","credential":"credential/chatgpt"}';
25
-
26
- let workspaceDir: string;
27
-
28
- function freshWorkspace(): void {
29
- workspaceDir = join(
30
- tmpdir(),
31
- `vellum-migration-154-test-${Date.now()}-${Math.random().toString(36).slice(2)}`,
32
- );
33
- mkdirSync(workspaceDir, { recursive: true });
34
- }
35
-
36
- function writeConfig(data: Record<string, unknown>): void {
37
- writeFileSync(
38
- join(workspaceDir, "config.json"),
39
- JSON.stringify(data, null, 2) + "\n",
40
- );
41
- }
42
-
43
- function readConfig(): Record<string, unknown> {
44
- return JSON.parse(readFileSync(join(workspaceDir, "config.json"), "utf-8"));
45
- }
46
-
47
- function readLlm(): Record<string, any> {
48
- return readConfig().llm as Record<string, any>;
49
- }
50
-
51
- function seedRows(
52
- rows: Array<{ name: string; provider: string; auth: string }>,
53
- ): void {
54
- mkdirSync(join(workspaceDir, "data", "db"), { recursive: true });
55
- const db = new Database(join(workspaceDir, "data", "db", "assistant.db"));
56
- db.run(`CREATE TABLE IF NOT EXISTS provider_connections (
57
- name TEXT PRIMARY KEY,
58
- provider TEXT NOT NULL,
59
- auth TEXT NOT NULL,
60
- created_at INTEGER NOT NULL,
61
- updated_at INTEGER NOT NULL
62
- )`);
63
- for (const row of rows) {
64
- db.query(
65
- `INSERT INTO provider_connections (name, provider, auth, created_at, updated_at) VALUES (?, ?, ?, 1, 1)`,
66
- ).run(row.name, row.provider, row.auth);
67
- }
68
- db.close();
69
- }
70
-
71
- const SUBSCRIPTION_ROW = {
72
- name: "chatgpt-subscription",
73
- provider: "chatgpt",
74
- auth: SUBSCRIPTION_AUTH,
75
- };
76
- const API_KEY_ROW = {
77
- name: "openai-key",
78
- provider: "openai",
79
- auth: API_KEY_AUTH,
80
- };
81
-
82
- beforeEach(() => {
83
- freshWorkspace();
84
- });
85
-
86
- afterEach(() => {
87
- if (existsSync(workspaceDir)) {
88
- assertNotLiveDb(workspaceDir);
89
- rmSync(workspaceDir, { recursive: true, force: true });
90
- }
91
- });
92
-
93
- describe("154-repair-retired-codex-gpt-5-4-model-ids migration", () => {
94
- test("has correct migration id and is registered", () => {
95
- expect(repairRetiredCodexGpt54ModelIdsMigration.id).toBe(
96
- "154-repair-retired-codex-gpt-5-4-model-ids",
97
- );
98
- expect(WORKSPACE_MIGRATIONS.map((m) => m.id)).toContain(
99
- "154-repair-retired-codex-gpt-5-4-model-ids",
100
- );
101
- });
102
-
103
- test("repairs chatgpt fragments in default, call sites, and profiles", () => {
104
- writeConfig({
105
- llm: {
106
- default: { provider: "chatgpt", model: STALE },
107
- callSites: {
108
- recall: { provider: "chatgpt", model: STALE_MINI, maxTokens: 4096 },
109
- heartbeat: { provider: "chatgpt", model: `${STALE}-tuned` },
110
- malformed: STALE,
111
- },
112
- profiles: {
113
- codex: { provider: "chatgpt", model: STALE, source: "user" },
114
- budget: { provider: "chatgpt", model: STALE_MINI },
115
- current: { provider: "chatgpt", model: "gpt-5.6-terra" },
116
- // The identity fails schema validation regardless of a stray
117
- // binding, so it is repaired without consulting the rows.
118
- strayBinding: {
119
- provider: "chatgpt",
120
- provider_connection: "openai-key",
121
- model: STALE,
122
- },
123
- },
124
- },
125
- });
126
-
127
- repairRetiredCodexGpt54ModelIdsMigration.run(workspaceDir);
128
-
129
- const llm = readLlm();
130
- expect(llm.default.model).toBe(REPLACEMENT);
131
- expect(llm.callSites.recall.model).toBe(REPLACEMENT_MINI);
132
- expect(llm.callSites.recall.maxTokens).toBe(4096);
133
- // Non-exact matches and malformed leaves are untouched.
134
- expect(llm.callSites.heartbeat.model).toBe(`${STALE}-tuned`);
135
- expect(llm.callSites.malformed).toBe(STALE);
136
- expect(llm.profiles.codex.model).toBe(REPLACEMENT);
137
- expect(llm.profiles.codex.source).toBe("user");
138
- expect(llm.profiles.budget.model).toBe(REPLACEMENT_MINI);
139
- expect(llm.profiles.current.model).toBe("gpt-5.6-terra");
140
- expect(llm.profiles.strayBinding.model).toBe(REPLACEMENT);
141
- });
142
-
143
- test("repairs entry-bound fragments whose row is the subscription", () => {
144
- seedRows([
145
- // Post-DB-migration-366 row shape.
146
- SUBSCRIPTION_ROW,
147
- // Pre-366 row shape: provider still openai, auth proves the route.
148
- { name: "legacy-sub", provider: "openai", auth: SUBSCRIPTION_AUTH },
149
- ]);
150
- writeConfig({
151
- llm: {
152
- default: {
153
- provider: "openai",
154
- provider_connection: "chatgpt-subscription",
155
- model: STALE,
156
- },
157
- callSites: {
158
- recall: { provider: "chatgpt-subscription", model: STALE_MINI },
159
- },
160
- profiles: {
161
- bound: { provider: "chatgpt-subscription", model: STALE },
162
- legacy: { provider: "legacy-sub", model: STALE_MINI },
163
- // Legacy binding: dispatch honors it ahead of the declared
164
- // provider and accepts the ChatGPT row for openai.
165
- pinned: {
166
- provider: "openai",
167
- provider_connection: "chatgpt-subscription",
168
- model: STALE,
169
- },
170
- },
171
- },
172
- });
173
-
174
- repairRetiredCodexGpt54ModelIdsMigration.run(workspaceDir);
175
-
176
- const llm = readLlm();
177
- expect(llm.default.model).toBe(REPLACEMENT);
178
- expect(llm.callSites.recall.model).toBe(REPLACEMENT_MINI);
179
- expect(llm.profiles.bound.model).toBe(REPLACEMENT);
180
- expect(llm.profiles.legacy.model).toBe(REPLACEMENT_MINI);
181
- expect(llm.profiles.pinned.model).toBe(REPLACEMENT);
182
- });
183
-
184
- test("judges a binding as a row even when named like a vendor", () => {
185
- seedRows([
186
- { name: "openai", provider: "openai", auth: SUBSCRIPTION_AUTH },
187
- { name: "chatgpt", provider: "openai", auth: API_KEY_AUTH },
188
- ]);
189
- writeConfig({
190
- llm: {
191
- profiles: {
192
- subRow: {
193
- provider: "openai",
194
- provider_connection: "openai",
195
- model: STALE,
196
- },
197
- keyRow: {
198
- provider: "chatgpt-subscription",
199
- provider_connection: "chatgpt",
200
- model: STALE_MINI,
201
- },
202
- },
203
- },
204
- });
205
-
206
- repairRetiredCodexGpt54ModelIdsMigration.run(workspaceDir);
207
-
208
- const llm = readLlm();
209
- expect(llm.profiles.subRow.model).toBe(REPLACEMENT);
210
- expect(llm.profiles.keyRow.model).toBe(STALE_MINI);
211
- });
212
-
213
- test("leaves API-key, other-vendor, providerless, and dangling fragments untouched", () => {
214
- seedRows([API_KEY_ROW, SUBSCRIPTION_ROW]);
215
- const config = {
216
- llm: {
217
- // Explicit vendors keep the model: API-key OpenAI still serves it
218
- // and an openai-compatible endpoint may too.
219
- default: { provider: "openai", model: STALE },
220
- defaultProvider: { provider: "chatgpt" },
221
- callSites: {
222
- vision: { provider: "openai-compatible", model: STALE_MINI },
223
- recall: { provider: "openai", model: STALE },
224
- // A providerless pin is out of scope: it serves the model on an
225
- // API-key or managed winner, and on a subscription winner the
226
- // request already fails upstream with no config loss.
227
- heartbeatAgent: { model: STALE },
228
- // A raw call-site binding is stripped by the schema.
229
- filingAgent: {
230
- provider: "openai-key",
231
- provider_connection: "chatgpt-subscription",
232
- model: STALE,
233
- },
234
- },
235
- profiles: {
236
- byok: { provider: "openai", model: STALE_MINI },
237
- keyBound: { provider: "openai-key", model: STALE },
238
- // Entry name with no row: nothing proves it is the subscription.
239
- dangling: { provider: "ghost", model: STALE },
240
- // Dispatch honors the explicit binding ahead of the provider.
241
- keyOverride: {
242
- provider: "chatgpt-subscription",
243
- provider_connection: "openai-key",
244
- model: STALE,
245
- },
246
- },
247
- },
248
- };
249
- writeConfig(config);
250
- const before = readFileSync(join(workspaceDir, "config.json"), "utf-8");
251
-
252
- repairRetiredCodexGpt54ModelIdsMigration.run(workspaceDir);
253
-
254
- expect(readFileSync(join(workspaceDir, "config.json"), "utf-8")).toBe(
255
- before,
256
- );
257
- });
258
-
259
- test("needs no DB for identity or vendor fragments", () => {
260
- writeConfig({
261
- llm: {
262
- default: { provider: "chatgpt", model: STALE },
263
- profiles: {
264
- byok: { provider: "openai", model: STALE },
265
- bound: { provider: "chatgpt-subscription", model: STALE },
266
- },
267
- },
268
- });
269
-
270
- repairRetiredCodexGpt54ModelIdsMigration.run(workspaceDir);
271
-
272
- const llm = readLlm();
273
- expect(llm.default.model).toBe(REPLACEMENT);
274
- expect(llm.profiles.byok.model).toBe(STALE);
275
- // An entry name with no DB file is dangling.
276
- expect(llm.profiles.bound.model).toBe(STALE);
277
- });
278
-
279
- test("throws when an entry name needs rows and the DB is unreadable", () => {
280
- // A DB file without the provider_connections table is unqueryable, so
281
- // the run must fail (and retry later) instead of skipping the profile.
282
- mkdirSync(join(workspaceDir, "data", "db"), { recursive: true });
283
- writeFileSync(join(workspaceDir, "data", "db", "assistant.db"), "");
284
- writeConfig({
285
- llm: {
286
- profiles: { bound: { provider: "chatgpt-subscription", model: STALE } },
287
- },
288
- });
289
-
290
- expect(() =>
291
- repairRetiredCodexGpt54ModelIdsMigration.run(workspaceDir),
292
- ).toThrow();
293
- expect(readLlm().profiles.bound.model).toBe(STALE);
294
-
295
- // The identity form never needs the rows, so the same broken DB does
296
- // not block its repair.
297
- writeConfig({
298
- llm: { default: { provider: "chatgpt", model: STALE } },
299
- });
300
- repairRetiredCodexGpt54ModelIdsMigration.run(workspaceDir);
301
- expect(readLlm().default.model).toBe(REPLACEMENT);
302
- });
303
-
304
- test("is idempotent and a no-op without the stale IDs", () => {
305
- writeConfig({
306
- llm: {
307
- default: { provider: "chatgpt", model: STALE },
308
- profiles: { fine: { provider: "chatgpt", model: REPLACEMENT } },
309
- },
310
- });
311
-
312
- repairRetiredCodexGpt54ModelIdsMigration.run(workspaceDir);
313
- const first = readConfig();
314
- repairRetiredCodexGpt54ModelIdsMigration.run(workspaceDir);
315
- expect(readConfig()).toEqual(first);
316
-
317
- const llm = first.llm as Record<string, any>;
318
- expect(llm.default.model).toBe(REPLACEMENT);
319
- expect(llm.profiles.fine.model).toBe(REPLACEMENT);
320
- });
321
-
322
- test("handles missing config, missing llm block, and invalid JSON", () => {
323
- expect(() =>
324
- repairRetiredCodexGpt54ModelIdsMigration.run(workspaceDir),
325
- ).not.toThrow();
326
-
327
- writeConfig({ theme: "dark" });
328
- repairRetiredCodexGpt54ModelIdsMigration.run(workspaceDir);
329
- expect(readConfig()).toEqual({ theme: "dark" });
330
-
331
- writeFileSync(join(workspaceDir, "config.json"), "{not json");
332
- expect(() =>
333
- repairRetiredCodexGpt54ModelIdsMigration.run(workspaceDir),
334
- ).not.toThrow();
335
- });
336
- });
@@ -1,51 +0,0 @@
1
- /**
2
- * Shared test fixture: the `SessionConfigOption` an adapter advertises for
3
- * model selection, shaped the way claude-agent-acp reports one (a `select`
4
- * with id and category `model`).
5
- */
6
-
7
- import type { SessionConfigOption } from "@agentclientprotocol/sdk";
8
-
9
- /** A model selector currently sitting on `currentValue`. */
10
- export function modelOption(currentValue: string): SessionConfigOption {
11
- return {
12
- type: "select",
13
- id: "model",
14
- name: "Model",
15
- category: "model",
16
- currentValue,
17
- options: [
18
- { value: "sonnet", name: "Sonnet" },
19
- { value: "opus", name: "Opus", description: "Most capable" },
20
- ],
21
- };
22
- }
23
-
24
- /**
25
- * A model selector sitting on nothing. `deriveModelInfo` reads an empty
26
- * `currentValue` as "no model reported", which is the only shape that leaves a
27
- * resumed session with a selector to pin through.
28
- */
29
- export function modelOptionWithoutCurrent(): SessionConfigOption {
30
- return modelOption("");
31
- }
32
-
33
- /**
34
- * A selector that is not the model one, so a set carrying only this reads as
35
- * an adapter that advertises no model selection.
36
- */
37
- export function nonModelOption(): SessionConfigOption {
38
- return {
39
- type: "select",
40
- id: "mode",
41
- name: "Mode",
42
- currentValue: "default",
43
- options: [{ value: "default", name: "Default" }],
44
- };
45
- }
46
-
47
- /** `modelOption`'s options as `deriveModelInfo` flattens them. */
48
- export const MODEL_OPTION_MODELS = [
49
- { value: "sonnet", label: "Sonnet" },
50
- { value: "opus", label: "Opus", description: "Most capable" },
51
- ];
@@ -1,166 +0,0 @@
1
- /**
2
- * Tests for the pure ACP model-config helpers: flattening an adapter's model
3
- * `SessionConfigOption` into publishable model info, and the spawn-time
4
- * precedence ladder.
5
- */
6
-
7
- import { describe, expect, test } from "bun:test";
8
-
9
- import type { SessionConfigOption } from "@agentclientprotocol/sdk";
10
-
11
- import {
12
- MODEL_OPTION_MODELS,
13
- modelOption,
14
- } from "./__tests__/helpers/acp-model-option.js";
15
- import { deriveModelInfo, resolveAcpModel } from "./model-config.js";
16
-
17
- const flatModelOption = modelOption("sonnet");
18
-
19
- describe("deriveModelInfo", () => {
20
- test("flattens flat options and reports the current value", () => {
21
- expect(deriveModelInfo([flatModelOption])).toEqual({
22
- model: "sonnet",
23
- modelConfigId: "model",
24
- availableModels: MODEL_OPTION_MODELS,
25
- });
26
- });
27
-
28
- test("flattens grouped options and carries the group name", () => {
29
- const grouped: SessionConfigOption = {
30
- type: "select",
31
- id: "model",
32
- name: "Model",
33
- category: "model",
34
- currentValue: "opus",
35
- options: [
36
- {
37
- group: "anthropic",
38
- name: "Anthropic",
39
- options: [
40
- { value: "opus", name: "Opus" },
41
- { value: "haiku", name: "Haiku", description: "Fast" },
42
- ],
43
- },
44
- {
45
- group: "aliases",
46
- name: "Aliases",
47
- options: [{ value: "best", name: "Best available" }],
48
- },
49
- ],
50
- };
51
-
52
- expect(deriveModelInfo([grouped])).toEqual({
53
- model: "opus",
54
- modelConfigId: "model",
55
- availableModels: [
56
- { value: "opus", label: "Opus", group: "Anthropic" },
57
- {
58
- value: "haiku",
59
- label: "Haiku",
60
- description: "Fast",
61
- group: "Anthropic",
62
- },
63
- { value: "best", label: "Best available", group: "Aliases" },
64
- ],
65
- });
66
- });
67
-
68
- test("matches on id alone when no category is reported", () => {
69
- const uncategorized: SessionConfigOption = {
70
- type: "select",
71
- id: "model",
72
- name: "Model",
73
- currentValue: "haiku",
74
- options: [{ value: "haiku", name: "Haiku" }],
75
- };
76
-
77
- const info = deriveModelInfo([uncategorized]);
78
-
79
- expect(info.modelConfigId).toBe("model");
80
- expect(info.model).toBe("haiku");
81
- });
82
-
83
- test("matches on category alone when the id differs", () => {
84
- const byCategory: SessionConfigOption = {
85
- type: "select",
86
- id: "llm",
87
- name: "LLM",
88
- category: "model",
89
- currentValue: "gpt-5",
90
- options: [{ value: "gpt-5", name: "GPT-5" }],
91
- };
92
-
93
- expect(deriveModelInfo([byCategory]).modelConfigId).toBe("llm");
94
- });
95
-
96
- test("ignores a boolean option even when its category is model", () => {
97
- const booleanOption: SessionConfigOption = {
98
- type: "boolean",
99
- id: "model",
100
- name: "Model",
101
- category: "model",
102
- currentValue: true,
103
- };
104
-
105
- expect(deriveModelInfo([booleanOption])).toEqual({ availableModels: [] });
106
- });
107
-
108
- test("skips non-model select options and picks the first match", () => {
109
- const mode: SessionConfigOption = {
110
- type: "select",
111
- id: "mode",
112
- name: "Mode",
113
- category: "mode",
114
- currentValue: "ask",
115
- options: [{ value: "ask", name: "Ask" }],
116
- };
117
-
118
- expect(deriveModelInfo([mode, flatModelOption]).modelConfigId).toBe(
119
- "model",
120
- );
121
- });
122
-
123
- test("returns no model info for an empty array", () => {
124
- expect(deriveModelInfo([])).toEqual({ availableModels: [] });
125
- });
126
-
127
- test("returns no model info for null", () => {
128
- expect(deriveModelInfo(null)).toEqual({ availableModels: [] });
129
- });
130
-
131
- test("returns no model info for undefined", () => {
132
- expect(deriveModelInfo(undefined)).toEqual({ availableModels: [] });
133
- });
134
- });
135
-
136
- describe("resolveAcpModel", () => {
137
- test("prefers the requested model over the per-agent one", () => {
138
- expect(
139
- resolveAcpModel({ requestedModel: "opus", agentModel: "haiku" }),
140
- ).toBe("opus");
141
- });
142
-
143
- test("falls back to the per-agent model", () => {
144
- expect(resolveAcpModel({ agentModel: "haiku" })).toBe("haiku");
145
- });
146
-
147
- test("returns undefined when every rung is unset", () => {
148
- expect(resolveAcpModel({})).toBeUndefined();
149
- });
150
-
151
- test("treats blank and whitespace-only values as unset", () => {
152
- expect(
153
- resolveAcpModel({ requestedModel: "\t\n", agentModel: "haiku" }),
154
- ).toBe("haiku");
155
- });
156
-
157
- test("returns undefined when every rung is whitespace", () => {
158
- expect(
159
- resolveAcpModel({ requestedModel: " ", agentModel: " " }),
160
- ).toBeUndefined();
161
- });
162
-
163
- test("trims the value it returns", () => {
164
- expect(resolveAcpModel({ requestedModel: " opus " })).toBe("opus");
165
- });
166
- });
@@ -1,138 +0,0 @@
1
- /**
2
- * Pure helpers for ACP session model selection.
3
- *
4
- * ACP 0.25.0 has no `session/set_model`: a model is one `SessionConfigOption`
5
- * among the set an adapter reports from `session/new`, `session/load`,
6
- * `session/resume`, and `session/set_config_option`. `deriveModelInfo` turns
7
- * that raw set into the trio the daemon publishes and persists (current model,
8
- * selectable options, and the config id to write back through
9
- * `setSessionConfigOption`), flattening the grouped `options` variant so
10
- * callers never branch on it.
11
- *
12
- * The model option is identified by `type === "select"` and `id === "model"`
13
- * or `category === "model"` only. Option `name` is a human label an adapter
14
- * may localize or restyle at will, so it is never matched against.
15
- *
16
- * `resolveAcpModel` is the precedence ladder a spawn or a resume walks before
17
- * it talks to the adapter: an explicit request beats the per-agent default from
18
- * `acp.agents.<id>.model`, and nothing named there leaves the adapter on its
19
- * own. Values are adapter-reported aliases (`opus`, `sonnet`), never Assistant
20
- * catalog ids, and are passed through unvalidated: only the adapter knows what
21
- * it accepts.
22
- *
23
- * Both helpers are synchronous and side-effect free.
24
- */
25
-
26
- import type {
27
- SessionConfigOption,
28
- SessionConfigSelectGroup,
29
- SessionConfigSelectOption,
30
- } from "@agentclientprotocol/sdk";
31
- import type { z } from "zod";
32
-
33
- import type { AcpSessionModelUpdateEventSchema } from "../api/events/acp-session-model-update.js";
34
-
35
- /**
36
- * A selectable model as reported by the adapter, flattened out of its group.
37
- * Derived from the event schema so the published wire shape and the shape the
38
- * daemon carries in memory cannot drift apart.
39
- */
40
- export type AcpModelOption = z.infer<
41
- typeof AcpSessionModelUpdateEventSchema
42
- >["availableModels"][number];
43
-
44
- /** What an adapter's config-option set says about the session's model. */
45
- export type AcpModelInfo = {
46
- model?: string;
47
- availableModels: AcpModelOption[];
48
- modelConfigId?: string;
49
- };
50
-
51
- type ResolveAcpModelInput = {
52
- requestedModel?: string;
53
- agentModel?: string;
54
- };
55
-
56
- type ModelSelectOption = Extract<SessionConfigOption, { type: "select" }>;
57
-
58
- function isGroup(
59
- entry: SessionConfigSelectOption | SessionConfigSelectGroup,
60
- ): entry is SessionConfigSelectGroup {
61
- return "group" in entry;
62
- }
63
-
64
- function toModelOption(
65
- option: SessionConfigSelectOption,
66
- group?: string,
67
- ): AcpModelOption {
68
- return {
69
- value: option.value,
70
- label: option.name,
71
- ...(option.description ? { description: option.description } : {}),
72
- ...(group ? { group } : {}),
73
- };
74
- }
75
-
76
- /**
77
- * The adapter's model selector, if it advertises one. `category` is UX-only
78
- * per the ACP spec, so a `model` id counts too; non-select options never do.
79
- */
80
- function findModelConfigOption(
81
- configOptions: SessionConfigOption[] | null | undefined,
82
- ): ModelSelectOption | undefined {
83
- return configOptions?.find(
84
- (option): option is ModelSelectOption =>
85
- option.type === "select" &&
86
- (option.id === "model" || option.category === "model"),
87
- );
88
- }
89
-
90
- /**
91
- * Extract current model, selectable models, and the config id to write back.
92
- * Returns `{ availableModels: [] }` when the adapter advertises no model
93
- * selector, which is how the whole feature degrades to invisible.
94
- */
95
- export function deriveModelInfo(
96
- configOptions: SessionConfigOption[] | null | undefined,
97
- ): AcpModelInfo {
98
- const modelOption = findModelConfigOption(configOptions);
99
-
100
- if (!modelOption) {
101
- return { availableModels: [] };
102
- }
103
-
104
- const availableModels: AcpModelOption[] = [];
105
- for (const entry of modelOption.options) {
106
- if (isGroup(entry)) {
107
- for (const option of entry.options) {
108
- availableModels.push(toModelOption(option, entry.name));
109
- }
110
- continue;
111
- }
112
- availableModels.push(toModelOption(entry));
113
- }
114
-
115
- return {
116
- ...(modelOption.currentValue ? { model: modelOption.currentValue } : {}),
117
- availableModels,
118
- modelConfigId: modelOption.id,
119
- };
120
- }
121
-
122
- /**
123
- * Pick the model a session should start on. Blank and whitespace-only values
124
- * count as unset so an empty config field never pins anything.
125
- */
126
- export function resolveAcpModel(
127
- input: ResolveAcpModelInput,
128
- ): string | undefined {
129
- const ladder = [input.requestedModel, input.agentModel];
130
-
131
- for (const candidate of ladder) {
132
- const trimmed = candidate?.trim();
133
- if (trimmed) {
134
- return trimmed;
135
- }
136
- }
137
- return undefined;
138
- }