@vellumai/assistant 0.12.1-staging.1 → 0.12.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 (404) 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/openapi.yaml +1 -291
  5. package/package.json +1 -1
  6. package/src/__tests__/always-loaded-tools-guard.test.ts +10 -18
  7. package/src/__tests__/app-compiler.test.ts +0 -6
  8. package/src/__tests__/app-control-tool-schemas.test.ts +2 -5
  9. package/src/__tests__/app-dir-path-guard.test.ts +3 -1
  10. package/src/__tests__/app-source-watcher.test.ts +218 -0
  11. package/src/{calls/__tests__ → __tests__}/call-controller.test.ts +82 -503
  12. package/src/__tests__/computer-use-skill-proxy-bridge.test.ts +2 -5
  13. package/src/__tests__/computer-use-tools.test.ts +5 -17
  14. package/src/__tests__/config-schema-cmd.test.ts +2 -2
  15. package/src/__tests__/config-schema.test.ts +29 -28
  16. package/src/__tests__/config-watcher-skill-reseed.test.ts +359 -0
  17. package/src/__tests__/config-watcher.test.ts +8 -23
  18. package/src/__tests__/connection-model-compat.test.ts +2 -2
  19. package/src/__tests__/conversation-key-store-disk-view.test.ts +1 -1
  20. package/src/__tests__/conversation-pairing.test.ts +0 -26
  21. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +10 -4
  22. package/src/__tests__/conversation-queue.test.ts +207 -48
  23. package/src/__tests__/conversation-retry-discard.test.ts +1 -54
  24. package/src/__tests__/conversation-skill-tools.test.ts +0 -27
  25. package/src/__tests__/conversation-surfaces-app-control.test.ts +3 -8
  26. package/src/__tests__/conversation-surfaces-task-progress.test.ts +0 -67
  27. package/src/__tests__/conversation-title-service.test.ts +10 -14
  28. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +5 -3
  29. package/src/__tests__/conversation-tool-setup-attribution.test.ts +0 -51
  30. package/src/__tests__/credential-record-retire.test.ts +1 -171
  31. package/src/__tests__/credential-routes.test.ts +3 -212
  32. package/src/__tests__/credential-security-invariants.test.ts +1 -2
  33. package/src/__tests__/cu-unified-flow.test.ts +2 -8
  34. package/src/__tests__/drain-kick-guard.test.ts +2 -0
  35. package/src/__tests__/drain-requeue-on-contention.test.ts +6 -0
  36. package/src/__tests__/evict-conversations-for-reload.test.ts +1 -1
  37. package/src/__tests__/host-bash-proxy.test.ts +45 -26
  38. package/src/__tests__/host-browser-proxy.test.ts +17 -6
  39. package/src/__tests__/host-file-proxy-targeted.test.ts +0 -23
  40. package/src/__tests__/host-file-proxy.test.ts +23 -11
  41. package/src/__tests__/host-transfer-proxy-targeted.test.ts +9 -30
  42. package/src/__tests__/llm-catalog-parity.test.ts +0 -25
  43. package/src/__tests__/mcp-auth-routes.test.ts +9 -6
  44. package/src/__tests__/mcp-config-secret-boundary.test.ts +136 -78
  45. package/src/__tests__/mcp-health-check.test.ts +11 -8
  46. package/src/__tests__/mcp-list-plugin-servers.test.ts +10 -8
  47. package/src/{calls/__tests__ → __tests__}/media-stream-audio-transcode.test.ts +1 -1
  48. package/src/{calls/__tests__ → __tests__}/media-stream-output.test.ts +8 -37
  49. package/src/{calls/__tests__ → __tests__}/media-stream-parser.test.ts +1 -1
  50. package/src/{calls/__tests__ → __tests__}/media-stream-server-integration.test.ts +57 -150
  51. package/src/{calls/__tests__ → __tests__}/media-stream-stt-session.test.ts +5 -5
  52. package/src/{calls/__tests__ → __tests__}/media-turn-detector.test.ts +1 -1
  53. package/src/__tests__/messages-after-tiebreaker.test.ts +0 -91
  54. package/src/__tests__/messages-read-boundary-guard.test.ts +1 -1
  55. package/src/__tests__/messaging-send-tool.test.ts +548 -355
  56. package/src/__tests__/model-intents.test.ts +0 -3
  57. package/src/__tests__/outlook-messaging-provider.test.ts +6 -6
  58. package/src/__tests__/platform-client-verify-credential.test.ts +0 -2
  59. package/src/__tests__/plugin-import-boundary-guard.test.ts +0 -13
  60. package/src/__tests__/script-proxy-certs.test.ts +0 -35
  61. package/src/__tests__/skill-feature-flags-integration.test.ts +1 -1
  62. package/src/__tests__/skill-feature-flags.test.ts +2 -2
  63. package/src/__tests__/speaker-identification.test.ts +56 -0
  64. package/src/__tests__/subagent-tool-gate-mode.test.ts +3 -19
  65. package/src/__tests__/subagent-tools.test.ts +2 -3
  66. package/src/__tests__/telephony-tts-guard.test.ts +3 -38
  67. package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +0 -4
  68. package/src/{calls/__tests__/voice-session-bridge-turn-flow.test.ts → __tests__/voice-session-bridge.test.ts} +12 -20
  69. package/src/__tests__/watch-retro-tool-availability.test.ts +17 -8
  70. package/src/__tests__/workspace-git-service.test.ts +0 -26
  71. package/src/acp/__tests__/agent-process.test.ts +4 -258
  72. package/src/acp/__tests__/auth-required.test.ts +0 -96
  73. package/src/acp/__tests__/client-handler.test.ts +2 -90
  74. package/src/acp/__tests__/session-manager-persistence.test.ts +1 -5
  75. package/src/acp/__tests__/session-manager-resume.test.ts +6 -270
  76. package/src/acp/__tests__/session-manager-usage.test.ts +2 -4
  77. package/src/acp/__tests__/session-manager.test.ts +4 -895
  78. package/src/acp/agent-process.test.ts +1 -4
  79. package/src/acp/agent-process.ts +9 -94
  80. package/src/acp/auth-required.ts +1 -50
  81. package/src/acp/auto-install.test.ts +7 -9
  82. package/src/acp/client-handler.ts +3 -16
  83. package/src/acp/resolve-agent.test.ts +6 -220
  84. package/src/acp/resolve-agent.ts +23 -88
  85. package/src/acp/session-manager.test.ts +3 -196
  86. package/src/acp/session-manager.ts +18 -441
  87. package/src/acp/types.ts +5 -50
  88. package/src/api/events/app-files-changed.ts +3 -4
  89. package/src/api/index.ts +0 -6
  90. package/src/apps/app-store.ts +1 -1
  91. package/src/bundler/app-compiler.ts +0 -15
  92. package/src/calls/__tests__/voice-control-protocol.test.ts +25 -58
  93. package/src/calls/__tests__/voice-session-bridge.test.ts +1 -37
  94. package/src/calls/__tests__/voice-triage-escalate.test.ts +0 -105
  95. package/src/calls/call-constants.ts +4 -24
  96. package/src/calls/call-controller.ts +199 -499
  97. package/src/calls/call-transport.ts +0 -16
  98. package/src/calls/media-stream-output.ts +0 -32
  99. package/src/calls/media-stream-server.ts +30 -82
  100. package/src/calls/media-stream-stt-session.ts +0 -15
  101. package/src/calls/resolve-call-tts-provider.ts +2 -33
  102. package/src/calls/speaker-identification.ts +233 -0
  103. package/src/calls/tts-call-strategy.ts +5 -9
  104. package/src/calls/types.ts +0 -2
  105. package/src/calls/voice-control-protocol.ts +21 -53
  106. package/src/calls/voice-session-bridge.ts +46 -59
  107. package/src/calls/voice-triage-escalate.ts +56 -175
  108. package/src/cli/commands/apps.help.ts +13 -67
  109. package/src/cli/commands/apps.ts +0 -183
  110. package/src/cli/commands/channels/index.help.ts +2 -70
  111. package/src/cli/commands/channels/index.ts +3 -16
  112. package/src/cli/commands/credentials.help.ts +2 -6
  113. package/src/cli/commands/credentials.ts +2 -2
  114. package/src/cli/commands/mcp.help.ts +8 -8
  115. package/src/cli/commands/mcp.ts +1 -1
  116. package/src/cli/commands/roadmap.help.ts +1 -5
  117. package/src/cli/commands/roadmap.ts +1 -1
  118. package/src/cli/lib/bundled-marketplace.json +90 -103
  119. package/src/config/__tests__/default-profile-catalog.test.ts +0 -22
  120. package/src/config/__tests__/memory-retrospective-schema.test.ts +1 -15
  121. package/src/config/__tests__/platform-rehydration.test.ts +0 -93
  122. package/src/config/acp-defaults.test.ts +3 -13
  123. package/src/config/acp-defaults.ts +6 -23
  124. package/src/config/acp-schema.ts +2 -30
  125. package/src/config/bundled-skills/acp/SKILL.md +14 -22
  126. package/src/config/bundled-skills/acp/TOOLS.json +1 -5
  127. package/src/config/bundled-skills/app-builder/SKILL.md +9 -12
  128. package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +1 -1
  129. package/src/config/bundled-skills/messaging/TOOLS.json +3 -3
  130. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +139 -67
  131. package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +1 -1
  132. package/src/config/bundled-skills/screen-annotation/SKILL.md +1 -8
  133. package/src/config/bundled-skills/screen-annotation/tools/screen-annotation.test.ts +1 -4
  134. package/src/config/bundled-skills/skill-management/TOOLS.json +2 -2
  135. package/src/config/default-profile-catalog.ts +1 -1
  136. package/src/config/env.ts +2 -2
  137. package/src/config/feature-flag-registry.json +12 -8
  138. package/src/config/memory-v3-gate.test.ts +0 -31
  139. package/src/config/memory-v3-gate.ts +16 -20
  140. package/src/config/platform-rehydration.ts +21 -75
  141. package/src/config/schema.ts +2 -2
  142. package/src/config/schemas/__tests__/live-voice.test.ts +158 -8
  143. package/src/config/schemas/calls.ts +28 -1
  144. package/src/config/schemas/live-voice.ts +173 -1
  145. package/src/config/schemas/mcp.ts +1 -1
  146. package/src/config/schemas/memory-retrospective.ts +0 -10
  147. package/src/config/schemas/skills.ts +14 -0
  148. package/src/credential-execution/process-manager.test.ts +19 -18
  149. package/src/daemon/__tests__/conversation-tool-setup.test.ts +121 -66
  150. package/src/daemon/__tests__/plugin-mcp-reconcile.test.ts +1 -4
  151. package/src/daemon/app-source-watcher.ts +218 -0
  152. package/src/daemon/config-watcher.ts +194 -13
  153. package/src/daemon/conversation-process.ts +64 -35
  154. package/src/daemon/conversation-skill-tools.ts +31 -13
  155. package/src/daemon/conversation-store.ts +1 -1
  156. package/src/daemon/conversation-surfaces.ts +8 -10
  157. package/src/daemon/conversation-tool-setup.ts +43 -37
  158. package/src/daemon/conversation.ts +3 -0
  159. package/src/daemon/host-bash-proxy.ts +12 -8
  160. package/src/daemon/host-browser-proxy.ts +1 -15
  161. package/src/daemon/host-file-proxy.ts +10 -8
  162. package/src/daemon/host-transfer-proxy.ts +18 -14
  163. package/src/daemon/lifecycle.ts +5 -0
  164. package/src/daemon/mcp-reload-service.ts +5 -5
  165. package/src/daemon/message-types/sync.ts +0 -1
  166. package/src/daemon/providers-setup.ts +1 -1
  167. package/src/daemon/shutdown-handlers.ts +9 -1
  168. package/src/daemon/skill-memory-refresh.ts +0 -64
  169. package/src/daemon/tool-side-effects.ts +18 -7
  170. package/src/hooks/types.ts +0 -33
  171. package/src/inbound/platform-callback-registration.ts +5 -4
  172. package/src/live-voice/__tests__/live-voice-metrics.test.ts +3 -0
  173. package/src/live-voice/__tests__/live-voice-progress.test.ts +15 -15
  174. package/src/live-voice/__tests__/live-voice-vad.test.ts +2 -2
  175. package/src/{calls → live-voice}/__tests__/progress-narration.test.ts +4 -4
  176. package/src/{calls → live-voice}/__tests__/progress-phrases.test.ts +1 -1
  177. package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +69 -13
  178. package/src/live-voice/live-voice-session.ts +693 -213
  179. package/src/{calls → live-voice}/progress-narration.ts +2 -2
  180. package/src/live-voice/protocol.ts +2 -0
  181. package/src/mcp/__tests__/effective-config.test.ts +2 -2
  182. package/src/mcp/effective-config.ts +3 -3
  183. package/src/mcp/mcp-header-store.ts +45 -29
  184. package/src/mcp/startup.ts +4 -8
  185. package/src/messaging/provider.ts +3 -10
  186. package/src/messaging/providers/callback-routing.ts +0 -18
  187. package/src/messaging/providers/channel-transport.ts +0 -56
  188. package/src/messaging/providers/discord/transport.ts +0 -17
  189. package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +17 -6
  190. package/src/messaging/providers/slack/adapter.ts +44 -7
  191. package/src/messaging/providers/slack/transport.ts +0 -16
  192. package/src/messaging/providers/telegram-bot/adapter.ts +48 -1
  193. package/src/messaging/providers/telegram-bot/client.ts +26 -0
  194. package/src/messaging/providers/telegram-bot/transport.ts +0 -20
  195. package/src/messaging/providers/whatsapp/adapter.ts +48 -1
  196. package/src/messaging/providers/whatsapp/client.ts +29 -0
  197. package/src/messaging/providers/whatsapp/transport.ts +0 -18
  198. package/src/monitoring/__tests__/workspace-git-heartbeat.test.ts +0 -8
  199. package/src/notifications/__tests__/assistant-reply-producer.test.ts +0 -55
  200. package/src/notifications/__tests__/home-feed-side-effect.test.ts +3 -153
  201. package/src/notifications/assistant-reply-producer.ts +3 -8
  202. package/src/notifications/conversation-pairing.ts +16 -36
  203. package/src/notifications/delivered-post-record.ts +1 -11
  204. package/src/notifications/home-feed-side-effect.ts +7 -55
  205. package/src/outbound-proxy/certs.ts +5 -3
  206. package/src/permissions/checker.test.ts +6 -11
  207. package/src/persistence/compaction-log-store-clickhouse.ts +3 -3
  208. package/src/persistence/conversation-crud.ts +63 -71
  209. package/src/persistence/conversation-lineage.ts +4 -12
  210. package/src/persistence/conversation-title-service.ts +26 -9
  211. package/src/persistence/llm-request-log-sink-clickhouse.ts +3 -3
  212. package/src/persistence/llm-request-log-source-clickhouse.ts +3 -3
  213. package/src/persistence/message-reads.ts +0 -30
  214. package/src/persistence/migrations/157-invite-contact-id.ts +6 -18
  215. package/src/persistence/schema/memory-core.ts +0 -5
  216. package/src/platform/client.test.ts +0 -2
  217. package/src/platform/client.ts +10 -2
  218. package/src/plugin-api/constants.ts +0 -2
  219. package/src/plugin-api/index.ts +0 -1
  220. package/src/plugin-api/types.ts +0 -2
  221. package/src/plugins/defaults/index.ts +0 -2
  222. package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +0 -46
  223. package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +5 -9
  224. package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +10 -20
  225. package/src/plugins/defaults/memory/__tests__/memory-retrospective-state.test.ts +0 -219
  226. package/src/plugins/defaults/memory/__tests__/memory-retrospective-sweep.test.ts +0 -31
  227. package/src/plugins/defaults/memory/__tests__/memory-retrospective-trigger-check.test.ts +0 -63
  228. package/src/plugins/defaults/memory/__tests__/shutdown-hook.test.ts +0 -4
  229. package/src/plugins/defaults/memory/graph/capability-seed.ts +6 -4
  230. package/src/plugins/defaults/memory/hooks/init.ts +0 -10
  231. package/src/plugins/defaults/memory/hooks/shutdown.ts +0 -2
  232. package/src/plugins/defaults/memory/jobs-worker.ts +0 -13
  233. package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +60 -34
  234. package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +5 -3
  235. package/src/plugins/defaults/memory/memory-retrospective-job.ts +3 -12
  236. package/src/plugins/defaults/memory/memory-retrospective-state.ts +71 -312
  237. package/src/plugins/defaults/memory/memory-retrospective-sweep.ts +1 -2
  238. package/src/plugins/defaults/memory/memory-retrospective-trigger-check.ts +3 -5
  239. package/src/plugins/defaults/memory/startup.ts +1 -6
  240. package/src/plugins/defaults/memory/substrate/__tests__/skill-content.test.ts +9 -7
  241. package/src/plugins/defaults/memory/substrate/skill-content.ts +2 -2
  242. package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +0 -89
  243. package/src/plugins/defaults/memory/worker.ts +0 -6
  244. package/src/plugins/mcp-servers.ts +75 -15
  245. package/src/providers/__tests__/connection-model-compat.test.ts +3 -3
  246. package/src/providers/gemini/client.ts +23 -9
  247. package/src/providers/model-catalog.ts +0 -34
  248. package/src/providers/model-intents.ts +5 -10
  249. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +18 -146
  250. package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +1 -83
  251. package/src/providers/openai/chat-completions-provider.ts +78 -192
  252. package/src/providers/openai/codex-models.ts +4 -0
  253. package/src/runtime/AGENTS.md +0 -2
  254. package/src/runtime/auth/same-actor.ts +3 -38
  255. package/src/runtime/http-server.ts +151 -160
  256. package/src/runtime/routes/__tests__/acp-routes.test.ts +4 -159
  257. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +1 -108
  258. package/src/runtime/routes/__tests__/mcp-add-default-risk.test.ts +12 -16
  259. package/src/runtime/routes/acp-routes.test.ts +0 -15
  260. package/src/runtime/routes/acp-routes.ts +9 -72
  261. package/src/runtime/routes/app-management-routes.ts +0 -73
  262. package/src/runtime/routes/app-routes.ts +1 -1
  263. package/src/runtime/routes/conversation-list-routes.ts +3 -14
  264. package/src/runtime/routes/conversation-query-routes.ts +0 -40
  265. package/src/runtime/routes/credential-routes.ts +10 -19
  266. package/src/runtime/routes/diagnostics-routes.ts +3 -55
  267. package/src/runtime/routes/index.ts +0 -4
  268. package/src/runtime/routes/log-export-routes.ts +0 -2
  269. package/src/runtime/routes/mcp-auth-routes.ts +47 -39
  270. package/src/runtime/routes/migration-routes.ts +21 -8
  271. package/src/runtime/routes/platform-routes.ts +15 -18
  272. package/src/runtime/services/__tests__/conversation-serializer.test.ts +0 -28
  273. package/src/runtime/services/conversation-serializer.ts +3 -16
  274. package/src/runtime/sync/resource-sync-events.ts +1 -2
  275. package/src/schedule/worker.ts +1 -1
  276. package/src/security/ces-rpc-record-backend.ts +0 -7
  277. package/src/subagent/consult-context.ts +25 -12
  278. package/src/tools/acp/list-agents.test.ts +1 -3
  279. package/src/tools/acp/spawn.test.ts +11 -146
  280. package/src/tools/acp/spawn.ts +3 -21
  281. package/src/tools/acp/status.ts +3 -40
  282. package/src/tools/app-control/skill-proxy-bridge.ts +1 -2
  283. package/src/tools/capability-offer.test.ts +2 -22
  284. package/src/tools/capability-offer.ts +1 -16
  285. package/src/tools/computer-use/definitions.ts +2 -4
  286. package/src/tools/computer-use/skill-proxy-bridge.ts +1 -2
  287. package/src/tools/credentials/metadata-store.ts +0 -100
  288. package/src/tools/host-filesystem/edit.test.ts +1 -2
  289. package/src/tools/host-filesystem/edit.ts +2 -2
  290. package/src/tools/host-filesystem/read.test.ts +1 -2
  291. package/src/tools/host-filesystem/read.ts +2 -2
  292. package/src/tools/host-filesystem/transfer.test.ts +1 -2
  293. package/src/tools/host-filesystem/transfer.ts +2 -2
  294. package/src/tools/host-filesystem/write.test.ts +1 -2
  295. package/src/tools/host-filesystem/write.ts +2 -2
  296. package/src/tools/host-terminal/host-shell.test.ts +0 -16
  297. package/src/tools/host-terminal/host-shell.ts +20 -4
  298. package/src/tools/policy-context.ts +7 -6
  299. package/src/tools/subagent/spawn.ts +8 -5
  300. package/src/tools/watch/watch-retro-report.ts +8 -0
  301. package/src/util/__tests__/language-subtag.test.ts +1 -23
  302. package/src/util/fs-watcher-error.ts +36 -0
  303. package/src/util/language-subtag.ts +0 -16
  304. package/src/util/platform.ts +0 -8
  305. package/src/workspace/git-service.ts +54 -5
  306. package/src/workspace/migrations/registry.ts +0 -6
  307. package/scripts/smoke-desktop-window-theme.ts +0 -186
  308. package/src/__tests__/git-porcelain.test.ts +0 -47
  309. package/src/__tests__/helpers/set-workspace-mcp.ts +0 -11
  310. package/src/__tests__/message-deleted-hook-dispatch.test.ts +0 -119
  311. package/src/__tests__/resolve-app.test.ts +0 -135
  312. package/src/__tests__/source-fingerprint.test.ts +0 -184
  313. package/src/__tests__/workspace-migration-154-repair-retired-codex-gpt-5-4-model-ids.test.ts +0 -336
  314. package/src/acp/__tests__/helpers/acp-model-option.ts +0 -51
  315. package/src/acp/model-config.test.ts +0 -166
  316. package/src/acp/model-config.ts +0 -138
  317. package/src/api/events/acp-session-model-update.test.ts +0 -88
  318. package/src/api/events/acp-session-model-update.ts +0 -46
  319. package/src/apps/resolve-app.ts +0 -81
  320. package/src/apps/source-fingerprint.ts +0 -266
  321. package/src/calls/__tests__/barge-in-guard.test.ts +0 -81
  322. package/src/calls/__tests__/voice-leg-coordinator.test.ts +0 -229
  323. package/src/calls/__tests__/voice-progress-cadence.test.ts +0 -274
  324. package/src/calls/barge-in-guard.ts +0 -118
  325. package/src/calls/voice-leg-coordinator.ts +0 -189
  326. package/src/calls/voice-progress-cadence.ts +0 -423
  327. package/src/cli/commands/__tests__/apps.test.ts +0 -245
  328. package/src/cli/commands/__tests__/roadmap.help.test.ts +0 -19
  329. package/src/cli/commands/channels/__tests__/send.test.ts +0 -218
  330. package/src/cli/commands/channels/send.ts +0 -128
  331. package/src/config/__tests__/platform-identity.test.ts +0 -117
  332. package/src/config/acp-agent-write.ts +0 -43
  333. package/src/config/acp-schema.test.ts +0 -81
  334. package/src/config/platform-identity.ts +0 -144
  335. package/src/config/schemas/__tests__/voice.test.ts +0 -148
  336. package/src/config/schemas/voice.ts +0 -150
  337. package/src/daemon/__tests__/skill-memory-refresh-mtime.test.ts +0 -133
  338. package/src/daemon/app-change-notify.ts +0 -45
  339. package/src/desktop/__tests__/fake-desktop.ts +0 -119
  340. package/src/desktop/desktop-chrome-policy.test.ts +0 -71
  341. package/src/desktop/desktop-chrome-policy.ts +0 -42
  342. package/src/desktop/desktop-dependencies.test.ts +0 -102
  343. package/src/desktop/desktop-dependencies.ts +0 -306
  344. package/src/desktop/desktop-feature.test.ts +0 -18
  345. package/src/desktop/desktop-feature.ts +0 -19
  346. package/src/desktop/desktop-panel-config.test.ts +0 -135
  347. package/src/desktop/desktop-panel-config.ts +0 -149
  348. package/src/desktop/desktop-session-manager.test.ts +0 -627
  349. package/src/desktop/desktop-session-manager.ts +0 -814
  350. package/src/desktop/desktop-stream-bridge.test.ts +0 -395
  351. package/src/desktop/desktop-stream-bridge.ts +0 -214
  352. package/src/desktop/desktop-wallpaper.test.ts +0 -191
  353. package/src/desktop/desktop-wallpaper.ts +0 -113
  354. package/src/desktop/desktop-window-theme.ts +0 -170
  355. package/src/i18n/AGENTS.md +0 -22
  356. package/src/i18n/__tests__/i18n.test.ts +0 -108
  357. package/src/i18n/index.ts +0 -16
  358. package/src/i18n/locales.ts +0 -93
  359. package/src/i18n/messages.ts +0 -61
  360. package/src/i18n/title.ts +0 -25
  361. package/src/mcp/__tests__/workspace-mcp-config.test.ts +0 -135
  362. package/src/mcp/spec-schema.ts +0 -111
  363. package/src/mcp/workspace-mcp-config.ts +0 -188
  364. package/src/messaging/providers/__tests__/proactive-address.test.ts +0 -95
  365. package/src/persistence/conversation-title-placeholders.ts +0 -23
  366. package/src/persistence/message-cursor.ts +0 -78
  367. package/src/persistence/migrations/__tests__/157-invite-contact-id.test.ts +0 -149
  368. package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-backfill.test.ts +0 -118
  369. package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-preserve.test.ts +0 -83
  370. package/src/plugins/defaults/memory/hooks/message-deleted.ts +0 -21
  371. package/src/plugins/defaults/memory/memory-retrospective-cursor-backfill.ts +0 -126
  372. package/src/plugins/defaults/memory/memory-retrospective-cursor-preserve.ts +0 -79
  373. package/src/plugins/defaults/memory/memory-retrospective-cursor.ts +0 -28
  374. package/src/plugins/defaults/memory/skill-md-mtime-poll.ts +0 -9
  375. package/src/providers/gemini-thought-signature.test.ts +0 -63
  376. package/src/providers/gemini-thought-signature.ts +0 -44
  377. package/src/providers/openai/__tests__/chat-completions-provider-thought-signature.test.ts +0 -613
  378. package/src/providers/openai/__tests__/google-thought-signature.test.ts +0 -235
  379. package/src/providers/openai/google-thought-signature.ts +0 -320
  380. package/src/runtime/__tests__/channel-send.test.ts +0 -443
  381. package/src/runtime/__tests__/desktop-stream-upgrade.test.ts +0 -89
  382. package/src/runtime/__tests__/live-voice-upgrade.test.ts +0 -58
  383. package/src/runtime/__tests__/runtime-ws-test-utils.ts +0 -72
  384. package/src/runtime/__tests__/watch-stream-upgrade.test.ts +0 -59
  385. package/src/runtime/channel-send.ts +0 -316
  386. package/src/runtime/routes/__tests__/apps-refresh-route.test.ts +0 -205
  387. package/src/runtime/routes/__tests__/channel-send-routes.test.ts +0 -127
  388. package/src/runtime/routes/__tests__/dictation-cleanup-guard.test.ts +0 -107
  389. package/src/runtime/routes/channel-send-routes.ts +0 -85
  390. package/src/runtime/routes/desktop-setup-routes.test.ts +0 -74
  391. package/src/runtime/routes/desktop-setup-routes.ts +0 -35
  392. package/src/security/__tests__/ces-http-record-backend.test.ts +0 -137
  393. package/src/security/ces-http-record-backend.ts +0 -120
  394. package/src/skills/__tests__/workspace-skill-md-signature.test.ts +0 -60
  395. package/src/skills/workspace-skill-md-signature.ts +0 -50
  396. package/src/tools/acp/status.test.ts +0 -94
  397. package/src/util/ca-bundle.ts +0 -13
  398. package/src/workspace/git-porcelain.ts +0 -66
  399. package/src/workspace/migrations/154-repair-retired-codex-gpt-5-4-model-ids.ts +0 -258
  400. package/src/workspace/migrations/155-move-front-model-config-to-voice.ts +0 -78
  401. package/src/workspace/migrations/156-extract-workspace-mcp-json.ts +0 -214
  402. package/src/workspace/migrations/__tests__/155-move-front-model-config-to-voice.test.ts +0 -102
  403. package/src/workspace/migrations/__tests__/156-extract-workspace-mcp-json.test.ts +0 -199
  404. /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
- }