@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.
- package/AGENTS.md +0 -4
- package/ARCHITECTURE.md +12 -11
- package/docs/architecture/turn-actor.md +8 -15
- package/openapi.yaml +1 -291
- package/package.json +1 -1
- package/src/__tests__/always-loaded-tools-guard.test.ts +10 -18
- package/src/__tests__/app-compiler.test.ts +0 -6
- package/src/__tests__/app-control-tool-schemas.test.ts +2 -5
- package/src/__tests__/app-dir-path-guard.test.ts +3 -1
- package/src/__tests__/app-source-watcher.test.ts +218 -0
- package/src/{calls/__tests__ → __tests__}/call-controller.test.ts +82 -503
- package/src/__tests__/computer-use-skill-proxy-bridge.test.ts +2 -5
- package/src/__tests__/computer-use-tools.test.ts +5 -17
- package/src/__tests__/config-schema-cmd.test.ts +2 -2
- package/src/__tests__/config-schema.test.ts +29 -28
- package/src/__tests__/config-watcher-skill-reseed.test.ts +359 -0
- package/src/__tests__/config-watcher.test.ts +8 -23
- package/src/__tests__/connection-model-compat.test.ts +2 -2
- package/src/__tests__/conversation-key-store-disk-view.test.ts +1 -1
- package/src/__tests__/conversation-pairing.test.ts +0 -26
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +10 -4
- package/src/__tests__/conversation-queue.test.ts +207 -48
- package/src/__tests__/conversation-retry-discard.test.ts +1 -54
- package/src/__tests__/conversation-skill-tools.test.ts +0 -27
- package/src/__tests__/conversation-surfaces-app-control.test.ts +3 -8
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +0 -67
- package/src/__tests__/conversation-title-service.test.ts +10 -14
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +5 -3
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +0 -51
- package/src/__tests__/credential-record-retire.test.ts +1 -171
- package/src/__tests__/credential-routes.test.ts +3 -212
- package/src/__tests__/credential-security-invariants.test.ts +1 -2
- package/src/__tests__/cu-unified-flow.test.ts +2 -8
- package/src/__tests__/drain-kick-guard.test.ts +2 -0
- package/src/__tests__/drain-requeue-on-contention.test.ts +6 -0
- package/src/__tests__/evict-conversations-for-reload.test.ts +1 -1
- package/src/__tests__/host-bash-proxy.test.ts +45 -26
- package/src/__tests__/host-browser-proxy.test.ts +17 -6
- package/src/__tests__/host-file-proxy-targeted.test.ts +0 -23
- package/src/__tests__/host-file-proxy.test.ts +23 -11
- package/src/__tests__/host-transfer-proxy-targeted.test.ts +9 -30
- package/src/__tests__/llm-catalog-parity.test.ts +0 -25
- package/src/__tests__/mcp-auth-routes.test.ts +9 -6
- package/src/__tests__/mcp-config-secret-boundary.test.ts +136 -78
- package/src/__tests__/mcp-health-check.test.ts +11 -8
- package/src/__tests__/mcp-list-plugin-servers.test.ts +10 -8
- package/src/{calls/__tests__ → __tests__}/media-stream-audio-transcode.test.ts +1 -1
- package/src/{calls/__tests__ → __tests__}/media-stream-output.test.ts +8 -37
- package/src/{calls/__tests__ → __tests__}/media-stream-parser.test.ts +1 -1
- package/src/{calls/__tests__ → __tests__}/media-stream-server-integration.test.ts +57 -150
- package/src/{calls/__tests__ → __tests__}/media-stream-stt-session.test.ts +5 -5
- package/src/{calls/__tests__ → __tests__}/media-turn-detector.test.ts +1 -1
- package/src/__tests__/messages-after-tiebreaker.test.ts +0 -91
- package/src/__tests__/messages-read-boundary-guard.test.ts +1 -1
- package/src/__tests__/messaging-send-tool.test.ts +548 -355
- package/src/__tests__/model-intents.test.ts +0 -3
- package/src/__tests__/outlook-messaging-provider.test.ts +6 -6
- package/src/__tests__/platform-client-verify-credential.test.ts +0 -2
- package/src/__tests__/plugin-import-boundary-guard.test.ts +0 -13
- package/src/__tests__/script-proxy-certs.test.ts +0 -35
- package/src/__tests__/skill-feature-flags-integration.test.ts +1 -1
- package/src/__tests__/skill-feature-flags.test.ts +2 -2
- package/src/__tests__/speaker-identification.test.ts +56 -0
- package/src/__tests__/subagent-tool-gate-mode.test.ts +3 -19
- package/src/__tests__/subagent-tools.test.ts +2 -3
- package/src/__tests__/telephony-tts-guard.test.ts +3 -38
- package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +0 -4
- package/src/{calls/__tests__/voice-session-bridge-turn-flow.test.ts → __tests__/voice-session-bridge.test.ts} +12 -20
- package/src/__tests__/watch-retro-tool-availability.test.ts +17 -8
- package/src/__tests__/workspace-git-service.test.ts +0 -26
- package/src/acp/__tests__/agent-process.test.ts +4 -258
- package/src/acp/__tests__/auth-required.test.ts +0 -96
- package/src/acp/__tests__/client-handler.test.ts +2 -90
- package/src/acp/__tests__/session-manager-persistence.test.ts +1 -5
- package/src/acp/__tests__/session-manager-resume.test.ts +6 -270
- package/src/acp/__tests__/session-manager-usage.test.ts +2 -4
- package/src/acp/__tests__/session-manager.test.ts +4 -895
- package/src/acp/agent-process.test.ts +1 -4
- package/src/acp/agent-process.ts +9 -94
- package/src/acp/auth-required.ts +1 -50
- package/src/acp/auto-install.test.ts +7 -9
- package/src/acp/client-handler.ts +3 -16
- package/src/acp/resolve-agent.test.ts +6 -220
- package/src/acp/resolve-agent.ts +23 -88
- package/src/acp/session-manager.test.ts +3 -196
- package/src/acp/session-manager.ts +18 -441
- package/src/acp/types.ts +5 -50
- package/src/api/events/app-files-changed.ts +3 -4
- package/src/api/index.ts +0 -6
- package/src/apps/app-store.ts +1 -1
- package/src/bundler/app-compiler.ts +0 -15
- package/src/calls/__tests__/voice-control-protocol.test.ts +25 -58
- package/src/calls/__tests__/voice-session-bridge.test.ts +1 -37
- package/src/calls/__tests__/voice-triage-escalate.test.ts +0 -105
- package/src/calls/call-constants.ts +4 -24
- package/src/calls/call-controller.ts +199 -499
- package/src/calls/call-transport.ts +0 -16
- package/src/calls/media-stream-output.ts +0 -32
- package/src/calls/media-stream-server.ts +30 -82
- package/src/calls/media-stream-stt-session.ts +0 -15
- package/src/calls/resolve-call-tts-provider.ts +2 -33
- package/src/calls/speaker-identification.ts +233 -0
- package/src/calls/tts-call-strategy.ts +5 -9
- package/src/calls/types.ts +0 -2
- package/src/calls/voice-control-protocol.ts +21 -53
- package/src/calls/voice-session-bridge.ts +46 -59
- package/src/calls/voice-triage-escalate.ts +56 -175
- package/src/cli/commands/apps.help.ts +13 -67
- package/src/cli/commands/apps.ts +0 -183
- package/src/cli/commands/channels/index.help.ts +2 -70
- package/src/cli/commands/channels/index.ts +3 -16
- package/src/cli/commands/credentials.help.ts +2 -6
- package/src/cli/commands/credentials.ts +2 -2
- package/src/cli/commands/mcp.help.ts +8 -8
- package/src/cli/commands/mcp.ts +1 -1
- package/src/cli/commands/roadmap.help.ts +1 -5
- package/src/cli/commands/roadmap.ts +1 -1
- package/src/cli/lib/bundled-marketplace.json +90 -103
- package/src/config/__tests__/default-profile-catalog.test.ts +0 -22
- package/src/config/__tests__/memory-retrospective-schema.test.ts +1 -15
- package/src/config/__tests__/platform-rehydration.test.ts +0 -93
- package/src/config/acp-defaults.test.ts +3 -13
- package/src/config/acp-defaults.ts +6 -23
- package/src/config/acp-schema.ts +2 -30
- package/src/config/bundled-skills/acp/SKILL.md +14 -22
- package/src/config/bundled-skills/acp/TOOLS.json +1 -5
- package/src/config/bundled-skills/app-builder/SKILL.md +9 -12
- package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +1 -1
- package/src/config/bundled-skills/messaging/TOOLS.json +3 -3
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +139 -67
- package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +1 -1
- package/src/config/bundled-skills/screen-annotation/SKILL.md +1 -8
- package/src/config/bundled-skills/screen-annotation/tools/screen-annotation.test.ts +1 -4
- package/src/config/bundled-skills/skill-management/TOOLS.json +2 -2
- package/src/config/default-profile-catalog.ts +1 -1
- package/src/config/env.ts +2 -2
- package/src/config/feature-flag-registry.json +12 -8
- package/src/config/memory-v3-gate.test.ts +0 -31
- package/src/config/memory-v3-gate.ts +16 -20
- package/src/config/platform-rehydration.ts +21 -75
- package/src/config/schema.ts +2 -2
- package/src/config/schemas/__tests__/live-voice.test.ts +158 -8
- package/src/config/schemas/calls.ts +28 -1
- package/src/config/schemas/live-voice.ts +173 -1
- package/src/config/schemas/mcp.ts +1 -1
- package/src/config/schemas/memory-retrospective.ts +0 -10
- package/src/config/schemas/skills.ts +14 -0
- package/src/credential-execution/process-manager.test.ts +19 -18
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +121 -66
- package/src/daemon/__tests__/plugin-mcp-reconcile.test.ts +1 -4
- package/src/daemon/app-source-watcher.ts +218 -0
- package/src/daemon/config-watcher.ts +194 -13
- package/src/daemon/conversation-process.ts +64 -35
- package/src/daemon/conversation-skill-tools.ts +31 -13
- package/src/daemon/conversation-store.ts +1 -1
- package/src/daemon/conversation-surfaces.ts +8 -10
- package/src/daemon/conversation-tool-setup.ts +43 -37
- package/src/daemon/conversation.ts +3 -0
- package/src/daemon/host-bash-proxy.ts +12 -8
- package/src/daemon/host-browser-proxy.ts +1 -15
- package/src/daemon/host-file-proxy.ts +10 -8
- package/src/daemon/host-transfer-proxy.ts +18 -14
- package/src/daemon/lifecycle.ts +5 -0
- package/src/daemon/mcp-reload-service.ts +5 -5
- package/src/daemon/message-types/sync.ts +0 -1
- package/src/daemon/providers-setup.ts +1 -1
- package/src/daemon/shutdown-handlers.ts +9 -1
- package/src/daemon/skill-memory-refresh.ts +0 -64
- package/src/daemon/tool-side-effects.ts +18 -7
- package/src/hooks/types.ts +0 -33
- package/src/inbound/platform-callback-registration.ts +5 -4
- package/src/live-voice/__tests__/live-voice-metrics.test.ts +3 -0
- package/src/live-voice/__tests__/live-voice-progress.test.ts +15 -15
- package/src/live-voice/__tests__/live-voice-vad.test.ts +2 -2
- package/src/{calls → live-voice}/__tests__/progress-narration.test.ts +4 -4
- package/src/{calls → live-voice}/__tests__/progress-phrases.test.ts +1 -1
- package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +69 -13
- package/src/live-voice/live-voice-session.ts +693 -213
- package/src/{calls → live-voice}/progress-narration.ts +2 -2
- package/src/live-voice/protocol.ts +2 -0
- package/src/mcp/__tests__/effective-config.test.ts +2 -2
- package/src/mcp/effective-config.ts +3 -3
- package/src/mcp/mcp-header-store.ts +45 -29
- package/src/mcp/startup.ts +4 -8
- package/src/messaging/provider.ts +3 -10
- package/src/messaging/providers/callback-routing.ts +0 -18
- package/src/messaging/providers/channel-transport.ts +0 -56
- package/src/messaging/providers/discord/transport.ts +0 -17
- package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +17 -6
- package/src/messaging/providers/slack/adapter.ts +44 -7
- package/src/messaging/providers/slack/transport.ts +0 -16
- package/src/messaging/providers/telegram-bot/adapter.ts +48 -1
- package/src/messaging/providers/telegram-bot/client.ts +26 -0
- package/src/messaging/providers/telegram-bot/transport.ts +0 -20
- package/src/messaging/providers/whatsapp/adapter.ts +48 -1
- package/src/messaging/providers/whatsapp/client.ts +29 -0
- package/src/messaging/providers/whatsapp/transport.ts +0 -18
- package/src/monitoring/__tests__/workspace-git-heartbeat.test.ts +0 -8
- package/src/notifications/__tests__/assistant-reply-producer.test.ts +0 -55
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +3 -153
- package/src/notifications/assistant-reply-producer.ts +3 -8
- package/src/notifications/conversation-pairing.ts +16 -36
- package/src/notifications/delivered-post-record.ts +1 -11
- package/src/notifications/home-feed-side-effect.ts +7 -55
- package/src/outbound-proxy/certs.ts +5 -3
- package/src/permissions/checker.test.ts +6 -11
- package/src/persistence/compaction-log-store-clickhouse.ts +3 -3
- package/src/persistence/conversation-crud.ts +63 -71
- package/src/persistence/conversation-lineage.ts +4 -12
- package/src/persistence/conversation-title-service.ts +26 -9
- package/src/persistence/llm-request-log-sink-clickhouse.ts +3 -3
- package/src/persistence/llm-request-log-source-clickhouse.ts +3 -3
- package/src/persistence/message-reads.ts +0 -30
- package/src/persistence/migrations/157-invite-contact-id.ts +6 -18
- package/src/persistence/schema/memory-core.ts +0 -5
- package/src/platform/client.test.ts +0 -2
- package/src/platform/client.ts +10 -2
- package/src/plugin-api/constants.ts +0 -2
- package/src/plugin-api/index.ts +0 -1
- package/src/plugin-api/types.ts +0 -2
- package/src/plugins/defaults/index.ts +0 -2
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +0 -46
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +5 -9
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +10 -20
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-state.test.ts +0 -219
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-sweep.test.ts +0 -31
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-trigger-check.test.ts +0 -63
- package/src/plugins/defaults/memory/__tests__/shutdown-hook.test.ts +0 -4
- package/src/plugins/defaults/memory/graph/capability-seed.ts +6 -4
- package/src/plugins/defaults/memory/hooks/init.ts +0 -10
- package/src/plugins/defaults/memory/hooks/shutdown.ts +0 -2
- package/src/plugins/defaults/memory/jobs-worker.ts +0 -13
- package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +60 -34
- package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +5 -3
- package/src/plugins/defaults/memory/memory-retrospective-job.ts +3 -12
- package/src/plugins/defaults/memory/memory-retrospective-state.ts +71 -312
- package/src/plugins/defaults/memory/memory-retrospective-sweep.ts +1 -2
- package/src/plugins/defaults/memory/memory-retrospective-trigger-check.ts +3 -5
- package/src/plugins/defaults/memory/startup.ts +1 -6
- package/src/plugins/defaults/memory/substrate/__tests__/skill-content.test.ts +9 -7
- package/src/plugins/defaults/memory/substrate/skill-content.ts +2 -2
- package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +0 -89
- package/src/plugins/defaults/memory/worker.ts +0 -6
- package/src/plugins/mcp-servers.ts +75 -15
- package/src/providers/__tests__/connection-model-compat.test.ts +3 -3
- package/src/providers/gemini/client.ts +23 -9
- package/src/providers/model-catalog.ts +0 -34
- package/src/providers/model-intents.ts +5 -10
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +18 -146
- package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +1 -83
- package/src/providers/openai/chat-completions-provider.ts +78 -192
- package/src/providers/openai/codex-models.ts +4 -0
- package/src/runtime/AGENTS.md +0 -2
- package/src/runtime/auth/same-actor.ts +3 -38
- package/src/runtime/http-server.ts +151 -160
- package/src/runtime/routes/__tests__/acp-routes.test.ts +4 -159
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +1 -108
- package/src/runtime/routes/__tests__/mcp-add-default-risk.test.ts +12 -16
- package/src/runtime/routes/acp-routes.test.ts +0 -15
- package/src/runtime/routes/acp-routes.ts +9 -72
- package/src/runtime/routes/app-management-routes.ts +0 -73
- package/src/runtime/routes/app-routes.ts +1 -1
- package/src/runtime/routes/conversation-list-routes.ts +3 -14
- package/src/runtime/routes/conversation-query-routes.ts +0 -40
- package/src/runtime/routes/credential-routes.ts +10 -19
- package/src/runtime/routes/diagnostics-routes.ts +3 -55
- package/src/runtime/routes/index.ts +0 -4
- package/src/runtime/routes/log-export-routes.ts +0 -2
- package/src/runtime/routes/mcp-auth-routes.ts +47 -39
- package/src/runtime/routes/migration-routes.ts +21 -8
- package/src/runtime/routes/platform-routes.ts +15 -18
- package/src/runtime/services/__tests__/conversation-serializer.test.ts +0 -28
- package/src/runtime/services/conversation-serializer.ts +3 -16
- package/src/runtime/sync/resource-sync-events.ts +1 -2
- package/src/schedule/worker.ts +1 -1
- package/src/security/ces-rpc-record-backend.ts +0 -7
- package/src/subagent/consult-context.ts +25 -12
- package/src/tools/acp/list-agents.test.ts +1 -3
- package/src/tools/acp/spawn.test.ts +11 -146
- package/src/tools/acp/spawn.ts +3 -21
- package/src/tools/acp/status.ts +3 -40
- package/src/tools/app-control/skill-proxy-bridge.ts +1 -2
- package/src/tools/capability-offer.test.ts +2 -22
- package/src/tools/capability-offer.ts +1 -16
- package/src/tools/computer-use/definitions.ts +2 -4
- package/src/tools/computer-use/skill-proxy-bridge.ts +1 -2
- package/src/tools/credentials/metadata-store.ts +0 -100
- package/src/tools/host-filesystem/edit.test.ts +1 -2
- package/src/tools/host-filesystem/edit.ts +2 -2
- package/src/tools/host-filesystem/read.test.ts +1 -2
- package/src/tools/host-filesystem/read.ts +2 -2
- package/src/tools/host-filesystem/transfer.test.ts +1 -2
- package/src/tools/host-filesystem/transfer.ts +2 -2
- package/src/tools/host-filesystem/write.test.ts +1 -2
- package/src/tools/host-filesystem/write.ts +2 -2
- package/src/tools/host-terminal/host-shell.test.ts +0 -16
- package/src/tools/host-terminal/host-shell.ts +20 -4
- package/src/tools/policy-context.ts +7 -6
- package/src/tools/subagent/spawn.ts +8 -5
- package/src/tools/watch/watch-retro-report.ts +8 -0
- package/src/util/__tests__/language-subtag.test.ts +1 -23
- package/src/util/fs-watcher-error.ts +36 -0
- package/src/util/language-subtag.ts +0 -16
- package/src/util/platform.ts +0 -8
- package/src/workspace/git-service.ts +54 -5
- package/src/workspace/migrations/registry.ts +0 -6
- package/scripts/smoke-desktop-window-theme.ts +0 -186
- package/src/__tests__/git-porcelain.test.ts +0 -47
- package/src/__tests__/helpers/set-workspace-mcp.ts +0 -11
- package/src/__tests__/message-deleted-hook-dispatch.test.ts +0 -119
- package/src/__tests__/resolve-app.test.ts +0 -135
- package/src/__tests__/source-fingerprint.test.ts +0 -184
- package/src/__tests__/workspace-migration-154-repair-retired-codex-gpt-5-4-model-ids.test.ts +0 -336
- package/src/acp/__tests__/helpers/acp-model-option.ts +0 -51
- package/src/acp/model-config.test.ts +0 -166
- package/src/acp/model-config.ts +0 -138
- package/src/api/events/acp-session-model-update.test.ts +0 -88
- package/src/api/events/acp-session-model-update.ts +0 -46
- package/src/apps/resolve-app.ts +0 -81
- package/src/apps/source-fingerprint.ts +0 -266
- package/src/calls/__tests__/barge-in-guard.test.ts +0 -81
- package/src/calls/__tests__/voice-leg-coordinator.test.ts +0 -229
- package/src/calls/__tests__/voice-progress-cadence.test.ts +0 -274
- package/src/calls/barge-in-guard.ts +0 -118
- package/src/calls/voice-leg-coordinator.ts +0 -189
- package/src/calls/voice-progress-cadence.ts +0 -423
- package/src/cli/commands/__tests__/apps.test.ts +0 -245
- package/src/cli/commands/__tests__/roadmap.help.test.ts +0 -19
- package/src/cli/commands/channels/__tests__/send.test.ts +0 -218
- package/src/cli/commands/channels/send.ts +0 -128
- package/src/config/__tests__/platform-identity.test.ts +0 -117
- package/src/config/acp-agent-write.ts +0 -43
- package/src/config/acp-schema.test.ts +0 -81
- package/src/config/platform-identity.ts +0 -144
- package/src/config/schemas/__tests__/voice.test.ts +0 -148
- package/src/config/schemas/voice.ts +0 -150
- package/src/daemon/__tests__/skill-memory-refresh-mtime.test.ts +0 -133
- package/src/daemon/app-change-notify.ts +0 -45
- package/src/desktop/__tests__/fake-desktop.ts +0 -119
- package/src/desktop/desktop-chrome-policy.test.ts +0 -71
- package/src/desktop/desktop-chrome-policy.ts +0 -42
- package/src/desktop/desktop-dependencies.test.ts +0 -102
- package/src/desktop/desktop-dependencies.ts +0 -306
- package/src/desktop/desktop-feature.test.ts +0 -18
- package/src/desktop/desktop-feature.ts +0 -19
- package/src/desktop/desktop-panel-config.test.ts +0 -135
- package/src/desktop/desktop-panel-config.ts +0 -149
- package/src/desktop/desktop-session-manager.test.ts +0 -627
- package/src/desktop/desktop-session-manager.ts +0 -814
- package/src/desktop/desktop-stream-bridge.test.ts +0 -395
- package/src/desktop/desktop-stream-bridge.ts +0 -214
- package/src/desktop/desktop-wallpaper.test.ts +0 -191
- package/src/desktop/desktop-wallpaper.ts +0 -113
- package/src/desktop/desktop-window-theme.ts +0 -170
- package/src/i18n/AGENTS.md +0 -22
- package/src/i18n/__tests__/i18n.test.ts +0 -108
- package/src/i18n/index.ts +0 -16
- package/src/i18n/locales.ts +0 -93
- package/src/i18n/messages.ts +0 -61
- package/src/i18n/title.ts +0 -25
- package/src/mcp/__tests__/workspace-mcp-config.test.ts +0 -135
- package/src/mcp/spec-schema.ts +0 -111
- package/src/mcp/workspace-mcp-config.ts +0 -188
- package/src/messaging/providers/__tests__/proactive-address.test.ts +0 -95
- package/src/persistence/conversation-title-placeholders.ts +0 -23
- package/src/persistence/message-cursor.ts +0 -78
- package/src/persistence/migrations/__tests__/157-invite-contact-id.test.ts +0 -149
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-backfill.test.ts +0 -118
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-preserve.test.ts +0 -83
- package/src/plugins/defaults/memory/hooks/message-deleted.ts +0 -21
- package/src/plugins/defaults/memory/memory-retrospective-cursor-backfill.ts +0 -126
- package/src/plugins/defaults/memory/memory-retrospective-cursor-preserve.ts +0 -79
- package/src/plugins/defaults/memory/memory-retrospective-cursor.ts +0 -28
- package/src/plugins/defaults/memory/skill-md-mtime-poll.ts +0 -9
- package/src/providers/gemini-thought-signature.test.ts +0 -63
- package/src/providers/gemini-thought-signature.ts +0 -44
- package/src/providers/openai/__tests__/chat-completions-provider-thought-signature.test.ts +0 -613
- package/src/providers/openai/__tests__/google-thought-signature.test.ts +0 -235
- package/src/providers/openai/google-thought-signature.ts +0 -320
- package/src/runtime/__tests__/channel-send.test.ts +0 -443
- package/src/runtime/__tests__/desktop-stream-upgrade.test.ts +0 -89
- package/src/runtime/__tests__/live-voice-upgrade.test.ts +0 -58
- package/src/runtime/__tests__/runtime-ws-test-utils.ts +0 -72
- package/src/runtime/__tests__/watch-stream-upgrade.test.ts +0 -59
- package/src/runtime/channel-send.ts +0 -316
- package/src/runtime/routes/__tests__/apps-refresh-route.test.ts +0 -205
- package/src/runtime/routes/__tests__/channel-send-routes.test.ts +0 -127
- package/src/runtime/routes/__tests__/dictation-cleanup-guard.test.ts +0 -107
- package/src/runtime/routes/channel-send-routes.ts +0 -85
- package/src/runtime/routes/desktop-setup-routes.test.ts +0 -74
- package/src/runtime/routes/desktop-setup-routes.ts +0 -35
- package/src/security/__tests__/ces-http-record-backend.test.ts +0 -137
- package/src/security/ces-http-record-backend.ts +0 -120
- package/src/skills/__tests__/workspace-skill-md-signature.test.ts +0 -60
- package/src/skills/workspace-skill-md-signature.ts +0 -50
- package/src/tools/acp/status.test.ts +0 -94
- package/src/util/ca-bundle.ts +0 -13
- package/src/workspace/git-porcelain.ts +0 -66
- package/src/workspace/migrations/154-repair-retired-codex-gpt-5-4-model-ids.ts +0 -258
- package/src/workspace/migrations/155-move-front-model-config-to-voice.ts +0 -78
- package/src/workspace/migrations/156-extract-workspace-mcp-json.ts +0 -214
- package/src/workspace/migrations/__tests__/155-move-front-model-config-to-voice.test.ts +0 -102
- package/src/workspace/migrations/__tests__/156-extract-workspace-mcp-json.test.ts +0 -199
- /package/src/{calls → live-voice}/progress-phrases.ts +0 -0
package/src/__tests__/workspace-migration-154-repair-retired-codex-gpt-5-4-model-ids.test.ts
DELETED
|
@@ -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
|
-
});
|
package/src/acp/model-config.ts
DELETED
|
@@ -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
|
-
}
|