@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
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
import { randomUUID } from "node:crypto";
|
|
7
7
|
import { basename } from "node:path";
|
|
8
8
|
|
|
9
|
-
import type { SessionConfigOption } from "@agentclientprotocol/sdk";
|
|
10
9
|
import { eq, inArray } from "drizzle-orm";
|
|
11
10
|
|
|
12
11
|
import type { AcpSessionUpdateEvent } from "../api/events/acp-session-update.js";
|
|
@@ -27,27 +26,17 @@ import { AcpAgentProcess } from "./agent-process.js";
|
|
|
27
26
|
import {
|
|
28
27
|
ACP_AUTH_RECOVERY_GUIDANCE,
|
|
29
28
|
ACP_CLAUDE_AUTH_REQUIRED_CODE,
|
|
30
|
-
AcpAuthRequiredError,
|
|
31
29
|
CLAUDE_ACP_COMMAND,
|
|
32
30
|
isAcpAuthRequired,
|
|
33
31
|
isClaudeAuthFailureMessage,
|
|
34
|
-
requestErrorReason,
|
|
35
32
|
} from "./auth-required.js";
|
|
36
33
|
import { resolveAgentWithAutoInstall } from "./auto-install.js";
|
|
37
34
|
import { VellumAcpClientHandler } from "./client-handler.js";
|
|
38
35
|
import { deriveFailureError } from "./failure-error.js";
|
|
39
|
-
import type { AcpModelInfo } from "./model-config.js";
|
|
40
|
-
import { deriveModelInfo, resolveAcpModel } from "./model-config.js";
|
|
41
36
|
import { prepareAgentEnv } from "./prepare-agent-env.js";
|
|
42
37
|
import { formatResolveFailure } from "./resolve-agent.js";
|
|
43
38
|
import { claudeResumeHint } from "./resume-hint.js";
|
|
44
|
-
import {
|
|
45
|
-
ACP_LIVE_STATUSES,
|
|
46
|
-
type AcpAgentConfig,
|
|
47
|
-
AcpConfigOptionRefusedError,
|
|
48
|
-
type AcpSessionState,
|
|
49
|
-
isLiveAcpStatus,
|
|
50
|
-
} from "./types.js";
|
|
39
|
+
import type { AcpAgentConfig, AcpSessionState } from "./types.js";
|
|
51
40
|
|
|
52
41
|
const log = getLogger("acp:session-manager");
|
|
53
42
|
|
|
@@ -57,9 +46,7 @@ const log = getLogger("acp:session-manager");
|
|
|
57
46
|
* and the marker promises a repair only the Connect Claude flow can perform.
|
|
58
47
|
* Checks both auth-failure shapes (see `auth-required.ts`) against both the
|
|
59
48
|
* raw rejection and the derived failure message, since `deriveFailureError`
|
|
60
|
-
* may replace one with the other.
|
|
61
|
-
* message and its decoded reason, because an adapter's words travel in either
|
|
62
|
-
* depending on how it raised the error.
|
|
49
|
+
* may replace one with the other.
|
|
63
50
|
*/
|
|
64
51
|
function claudeAuthRequiredCode(
|
|
65
52
|
err: unknown,
|
|
@@ -72,7 +59,6 @@ function claudeAuthRequiredCode(
|
|
|
72
59
|
const rawMessage = err instanceof Error ? err.message : String(err);
|
|
73
60
|
return isAcpAuthRequired(err) ||
|
|
74
61
|
isClaudeAuthFailureMessage(rawMessage) ||
|
|
75
|
-
isClaudeAuthFailureMessage(requestErrorReason(err)) ||
|
|
76
62
|
isClaudeAuthFailureMessage(failureMessage)
|
|
77
63
|
? ACP_CLAUDE_AUTH_REQUIRED_CODE
|
|
78
64
|
: undefined;
|
|
@@ -139,28 +125,6 @@ interface SessionEntry {
|
|
|
139
125
|
* rather than needing a sweep to retire it. Absent for agents that use no
|
|
140
126
|
* Claude credential. */
|
|
141
127
|
credentialDigest?: string;
|
|
142
|
-
/** Config id of the adapter's model selector, absent when it advertises
|
|
143
|
-
* none. Both the id to write a model back through and the flag that says
|
|
144
|
-
* this session has a model to publish at all. */
|
|
145
|
-
modelConfigId?: string;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/** What a spawn or resume pin did about the model it was asked for. */
|
|
149
|
-
interface ModelPinResult {
|
|
150
|
-
/** False when the session is not on the model asked for: the adapter
|
|
151
|
-
* refused it, or advertises no selector at all. */
|
|
152
|
-
applied: boolean;
|
|
153
|
-
/** Message to relay, present only when the caller named the model. */
|
|
154
|
-
warning?: string;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
/**
|
|
158
|
-
* The sentence a caller who named a model gets when the session has no model
|
|
159
|
-
* selector to put it through, whether the adapter never advertised one or its
|
|
160
|
-
* answer to the pin dropped the one it had.
|
|
161
|
-
*/
|
|
162
|
-
function noModelSelectionWarning(agentId: string): string {
|
|
163
|
-
return `Agent "${agentId}" does not support model selection, so the session is running on the agent's own model.`;
|
|
164
128
|
}
|
|
165
129
|
|
|
166
130
|
/**
|
|
@@ -185,10 +149,6 @@ export interface AcpCancellationOptions {
|
|
|
185
149
|
|
|
186
150
|
export class AcpSessionManager {
|
|
187
151
|
private sessions = new Map<string, SessionEntry>();
|
|
188
|
-
/** Scopes process-local model revisions across assistant restarts. */
|
|
189
|
-
private readonly modelRevisionEpoch = randomUUID();
|
|
190
|
-
/** Orders model snapshots within this assistant process. */
|
|
191
|
-
private modelRevision = 0;
|
|
192
152
|
/**
|
|
193
153
|
* Per-session ring buffer of wire-shaped update events forwarded to
|
|
194
154
|
* clients. Bounded by event count and aggregate JSON byte size; oldest
|
|
@@ -241,7 +201,7 @@ export class AcpSessionManager {
|
|
|
241
201
|
stopReason: "daemon_restarted",
|
|
242
202
|
completedAt: Date.now(),
|
|
243
203
|
})
|
|
244
|
-
.where(inArray(acpSessionHistory.status, [
|
|
204
|
+
.where(inArray(acpSessionHistory.status, ["running", "initializing"]))
|
|
245
205
|
.run();
|
|
246
206
|
} catch (err) {
|
|
247
207
|
log.error(
|
|
@@ -283,12 +243,6 @@ export class AcpSessionManager {
|
|
|
283
243
|
*
|
|
284
244
|
* The prompt is fired in the background — results stream via sessionUpdate
|
|
285
245
|
* callbacks and completion/error messages are sent when the prompt finishes.
|
|
286
|
-
*
|
|
287
|
-
* `requestedModel` is the normalized explicit request this spawn acted on.
|
|
288
|
-
* `effectiveModel` is the model the adapter reports for the live session.
|
|
289
|
-
* `modelWarning` comes back when the adapter refused the model the session
|
|
290
|
-
* was asked for: the caller relays the reason rather than treating the spawn
|
|
291
|
-
* as failed.
|
|
292
246
|
*/
|
|
293
247
|
async spawn(
|
|
294
248
|
agentId: string,
|
|
@@ -297,17 +251,11 @@ export class AcpSessionManager {
|
|
|
297
251
|
cwd: string,
|
|
298
252
|
parentConversationId: string,
|
|
299
253
|
sendToVellum: (msg: AssistantEvent) => void,
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
): Promise<{
|
|
303
|
-
acpSessionId: string;
|
|
304
|
-
protocolSessionId: string;
|
|
305
|
-
requestedModel?: string;
|
|
306
|
-
effectiveModel?: string;
|
|
307
|
-
modelWarning?: string;
|
|
308
|
-
}> {
|
|
254
|
+
parentToolUseId?: string,
|
|
255
|
+
opts?: AcpCancellationOptions,
|
|
256
|
+
): Promise<{ acpSessionId: string; protocolSessionId: string }> {
|
|
309
257
|
this.assertCapacity();
|
|
310
|
-
|
|
258
|
+
opts?.signal?.throwIfAborted();
|
|
311
259
|
|
|
312
260
|
const acpSessionId = randomUUID();
|
|
313
261
|
log.info(
|
|
@@ -329,20 +277,11 @@ export class AcpSessionManager {
|
|
|
329
277
|
cwd,
|
|
330
278
|
startedAt: Date.now(),
|
|
331
279
|
sendToVellum,
|
|
332
|
-
parentToolUseId
|
|
280
|
+
parentToolUseId,
|
|
333
281
|
task,
|
|
334
282
|
});
|
|
335
283
|
const { process: agentProcess, state } = entry;
|
|
336
284
|
|
|
337
|
-
const requestedModel = options?.model?.trim() || undefined;
|
|
338
|
-
// Resolved before the adapter is asked for anything, so the ladder is
|
|
339
|
-
// walked once against the config the spawn was made under.
|
|
340
|
-
const resolvedModel = resolveAcpModel({
|
|
341
|
-
requestedModel,
|
|
342
|
-
agentModel: agentConfig.model,
|
|
343
|
-
});
|
|
344
|
-
|
|
345
|
-
let configOptions: SessionConfigOption[] = [];
|
|
346
285
|
try {
|
|
347
286
|
log.info({ acpSessionId, agentId }, "ACP spawning child process");
|
|
348
287
|
agentProcess.spawn(cwd);
|
|
@@ -352,9 +291,7 @@ export class AcpSessionManager {
|
|
|
352
291
|
);
|
|
353
292
|
await agentProcess.initialize();
|
|
354
293
|
log.info({ acpSessionId, agentId }, "ACP creating session");
|
|
355
|
-
const
|
|
356
|
-
const acpProtocolSessionId = created.sessionId;
|
|
357
|
-
configOptions = created.configOptions;
|
|
294
|
+
const acpProtocolSessionId = await agentProcess.createSession(cwd);
|
|
358
295
|
state.acpSessionId = acpProtocolSessionId;
|
|
359
296
|
state.status = "running";
|
|
360
297
|
log.info(
|
|
@@ -380,47 +317,16 @@ export class AcpSessionManager {
|
|
|
380
317
|
// awaits, and the child process is already running. A turn stopped in that
|
|
381
318
|
// window must not be told a session started, nor have the agent handed the
|
|
382
319
|
// task, so the process is torn down and the abort let out instead.
|
|
383
|
-
if (
|
|
320
|
+
if (opts?.signal?.aborted) {
|
|
384
321
|
log.info(
|
|
385
322
|
{ acpSessionId, agentId },
|
|
386
323
|
"ACP spawn cancelled during setup; tearing the session down",
|
|
387
324
|
);
|
|
388
325
|
this.teardownSession(acpSessionId, entry);
|
|
389
|
-
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
let modelWarning: string | undefined;
|
|
393
|
-
try {
|
|
394
|
-
({ warning: modelWarning } = await this.pinSessionModel(
|
|
395
|
-
entry,
|
|
396
|
-
configOptions,
|
|
397
|
-
requestedModel,
|
|
398
|
-
resolvedModel,
|
|
399
|
-
));
|
|
400
|
-
} catch (err) {
|
|
401
|
-
log.error(
|
|
402
|
-
{ acpSessionId, agentId, err },
|
|
403
|
-
"ACP spawn failed while applying the model",
|
|
404
|
-
);
|
|
405
|
-
const failure = this.classifyPinFailure(err, agentId, agentConfig);
|
|
406
|
-
// No prompt has fired yet, so no permissions can be pending.
|
|
407
|
-
this.teardownSession(acpSessionId, entry);
|
|
408
|
-
throw failure;
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
// Recheck: the model pin is an await too, and everything below it either
|
|
412
|
-
// announces the session or hands the agent the task.
|
|
413
|
-
if (cancellation?.signal?.aborted) {
|
|
414
|
-
log.info(
|
|
415
|
-
{ acpSessionId, agentId },
|
|
416
|
-
"ACP spawn cancelled during the model pin; tearing the session down",
|
|
417
|
-
);
|
|
418
|
-
this.teardownSession(acpSessionId, entry);
|
|
419
|
-
cancellation.signal.throwIfAborted();
|
|
326
|
+
opts.signal.throwIfAborted();
|
|
420
327
|
}
|
|
421
328
|
|
|
422
329
|
this.sendSpawnedEvent(acpSessionId, entry);
|
|
423
|
-
this.sendModelEvent(acpSessionId, entry);
|
|
424
330
|
|
|
425
331
|
// Fire prompt in the background, do not await
|
|
426
332
|
entry.currentPrompt = this.firePromptInBackground(
|
|
@@ -430,179 +336,7 @@ export class AcpSessionManager {
|
|
|
430
336
|
task,
|
|
431
337
|
);
|
|
432
338
|
|
|
433
|
-
return {
|
|
434
|
-
acpSessionId,
|
|
435
|
-
protocolSessionId: state.acpSessionId,
|
|
436
|
-
requestedModel,
|
|
437
|
-
effectiveModel: state.model,
|
|
438
|
-
...(modelWarning ? { modelWarning } : {}),
|
|
439
|
-
};
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
/** Records what a config-option set says about the session's model. */
|
|
443
|
-
private applyModelInfo(
|
|
444
|
-
entry: SessionEntry,
|
|
445
|
-
configOptions: SessionConfigOption[],
|
|
446
|
-
): void {
|
|
447
|
-
this.recordModelInfo(entry, deriveModelInfo(configOptions));
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
/**
|
|
451
|
-
* Records the model state a `session/new`, `session/load`, or
|
|
452
|
-
* `session/resume` response reports. The response is authoritative when it
|
|
453
|
-
* names a selector, or when nothing has announced one yet; otherwise the
|
|
454
|
-
* selector a `config_option_update` announced while the call was open
|
|
455
|
-
* stands, because an omitted optional field is not a removal.
|
|
456
|
-
*/
|
|
457
|
-
private applyOpeningModelInfo(
|
|
458
|
-
entry: SessionEntry,
|
|
459
|
-
configOptions: SessionConfigOption[],
|
|
460
|
-
): void {
|
|
461
|
-
const info = deriveModelInfo(configOptions);
|
|
462
|
-
if (info.modelConfigId || entry.modelConfigId === undefined) {
|
|
463
|
-
this.recordModelInfo(entry, info);
|
|
464
|
-
}
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
/**
|
|
468
|
-
* Writes what the adapter reports onto the entry. `modelConfigId` doubles
|
|
469
|
-
* as the "this adapter has a model selector" flag, so state is left
|
|
470
|
-
* untouched when there is none: an adapter that reports no selector cannot
|
|
471
|
-
* wipe a model another response already named.
|
|
472
|
-
*/
|
|
473
|
-
private recordModelInfo(entry: SessionEntry, info: AcpModelInfo): void {
|
|
474
|
-
entry.modelConfigId = info.modelConfigId;
|
|
475
|
-
if (info.modelConfigId) {
|
|
476
|
-
entry.state.model = info.model;
|
|
477
|
-
entry.state.availableModels = info.availableModels;
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
/**
|
|
482
|
-
* Records what the opening response says about the model, then puts the
|
|
483
|
-
* session on `resolvedModel` if that is somewhere else. The value crosses
|
|
484
|
-
* the wire unvalidated: the adapter's own resolver accepts aliases and full
|
|
485
|
-
* ids and rejects everything else, and duplicating that judgement here
|
|
486
|
-
* would refuse values it would have taken. A refusal leaves the run on the
|
|
487
|
-
* adapter's default and comes back as the message to relay, because a
|
|
488
|
-
* session that is live and working is worth more than one that never
|
|
489
|
-
* started over a model name.
|
|
490
|
-
*
|
|
491
|
-
* An answer that carries no model selector is not a pin that landed: the
|
|
492
|
-
* session is left with no model rather than one only the superseded
|
|
493
|
-
* response ever named, and the empty picker goes out so no client is left
|
|
494
|
-
* holding a selector the answer retired.
|
|
495
|
-
*
|
|
496
|
-
* A caller who named the model is owed the news when it was not applied,
|
|
497
|
-
* whether the adapter has no selector or refused the value; an inherited
|
|
498
|
-
* rung (the per-agent config model) is only logged, so a value the caller
|
|
499
|
-
* never named does not surface as a warning on every spawn.
|
|
500
|
-
*/
|
|
501
|
-
/**
|
|
502
|
-
* A model pin that fails for authentication is the same failure a session
|
|
503
|
-
* that could not open: Claude refused the configured credential, so it is
|
|
504
|
-
* written down before teardown and surfaced in the shape the spawn boundary
|
|
505
|
-
* recognises, whether the adapter answered with a structured
|
|
506
|
-
* `auth_required` or with Claude's message-shaped 401.
|
|
507
|
-
*/
|
|
508
|
-
private classifyPinFailure(
|
|
509
|
-
err: unknown,
|
|
510
|
-
agentId: string,
|
|
511
|
-
agentConfig: { command: string; credentialDigest?: string },
|
|
512
|
-
): unknown {
|
|
513
|
-
const message = requestErrorReason(err);
|
|
514
|
-
const authCode = claudeAuthRequiredCode(err, message, {
|
|
515
|
-
command: basename(agentConfig.command),
|
|
516
|
-
});
|
|
517
|
-
if (authCode === undefined && !isAcpAuthRequired(err)) {
|
|
518
|
-
return err;
|
|
519
|
-
}
|
|
520
|
-
noteClaudeTokenRefused(agentConfig.credentialDigest, Date.now());
|
|
521
|
-
return isAcpAuthRequired(err)
|
|
522
|
-
? err
|
|
523
|
-
: new AcpAuthRequiredError(agentId, message);
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
private async pinSessionModel(
|
|
527
|
-
entry: SessionEntry,
|
|
528
|
-
configOptions: SessionConfigOption[],
|
|
529
|
-
requestedModel: string | undefined,
|
|
530
|
-
resolvedModel: string | undefined,
|
|
531
|
-
options?: { keepReportedModel?: boolean },
|
|
532
|
-
): Promise<ModelPinResult> {
|
|
533
|
-
const { state } = entry;
|
|
534
|
-
this.applyOpeningModelInfo(entry, configOptions);
|
|
535
|
-
|
|
536
|
-
// A resume reattaches a session the adapter restored from its own
|
|
537
|
-
// transcript, so any model it has named by now is the model the run stays
|
|
538
|
-
// on, whether the opening reply carried it or a replayed
|
|
539
|
-
// `config_option_update` did.
|
|
540
|
-
if (options?.keepReportedModel && state.model) {
|
|
541
|
-
log.info(
|
|
542
|
-
{ acpSessionId: state.id, agentId: state.agentId, model: state.model },
|
|
543
|
-
"ACP agent reported the resumed session's model; leaving it there",
|
|
544
|
-
);
|
|
545
|
-
return { applied: true };
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
if (!resolvedModel || resolvedModel === state.model) {
|
|
549
|
-
return { applied: true };
|
|
550
|
-
}
|
|
551
|
-
if (!entry.modelConfigId) {
|
|
552
|
-
log.info(
|
|
553
|
-
{ acpSessionId: state.id, agentId: state.agentId, resolvedModel },
|
|
554
|
-
"ACP agent advertises no model selector; running on its own model",
|
|
555
|
-
);
|
|
556
|
-
return {
|
|
557
|
-
applied: false,
|
|
558
|
-
...(requestedModel
|
|
559
|
-
? { warning: noModelSelectionWarning(state.agentId) }
|
|
560
|
-
: {}),
|
|
561
|
-
};
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
try {
|
|
565
|
-
const refreshed = await entry.process.setConfigOption(
|
|
566
|
-
state.acpSessionId,
|
|
567
|
-
entry.modelConfigId,
|
|
568
|
-
resolvedModel,
|
|
569
|
-
);
|
|
570
|
-
this.applyModelInfo(entry, refreshed);
|
|
571
|
-
if (!entry.modelConfigId) {
|
|
572
|
-
// Published, not just recorded: a `config_option_update` that arrived
|
|
573
|
-
// while the round trip was open has already put its selector in front
|
|
574
|
-
// of clients, and a session no client holds yet drops the frame.
|
|
575
|
-
this.clearVanishedModelSelector(state.id, entry);
|
|
576
|
-
log.warn(
|
|
577
|
-
{ acpSessionId: state.id, agentId: state.agentId, resolvedModel },
|
|
578
|
-
"ACP agent dropped its model selector while applying the model; running on its own model",
|
|
579
|
-
);
|
|
580
|
-
return {
|
|
581
|
-
applied: false,
|
|
582
|
-
...(requestedModel
|
|
583
|
-
? { warning: noModelSelectionWarning(state.agentId) }
|
|
584
|
-
: {}),
|
|
585
|
-
};
|
|
586
|
-
}
|
|
587
|
-
return { applied: true };
|
|
588
|
-
} catch (err) {
|
|
589
|
-
// Only the adapter's own answer is a refusal. A closed connection, an
|
|
590
|
-
// exited process, or a failed authentication is the caller's failure
|
|
591
|
-
// to tear down, not a model to fall back from.
|
|
592
|
-
if (!(err instanceof AcpConfigOptionRefusedError)) {
|
|
593
|
-
throw err;
|
|
594
|
-
}
|
|
595
|
-
log.warn(
|
|
596
|
-
{ acpSessionId: state.id, agentId: state.agentId, resolvedModel, err },
|
|
597
|
-
"ACP agent refused the requested model; running on its own model",
|
|
598
|
-
);
|
|
599
|
-
return {
|
|
600
|
-
applied: false,
|
|
601
|
-
...(requestedModel
|
|
602
|
-
? { warning: err instanceof Error ? err.message : String(err) }
|
|
603
|
-
: {}),
|
|
604
|
-
};
|
|
605
|
-
}
|
|
339
|
+
return { acpSessionId, protocolSessionId: state.acpSessionId };
|
|
606
340
|
}
|
|
607
341
|
|
|
608
342
|
/**
|
|
@@ -628,20 +362,10 @@ export class AcpSessionManager {
|
|
|
628
362
|
// Initialize the per-session ring buffer before any update can fire.
|
|
629
363
|
this.eventBuffers.set(acpSessionId, []);
|
|
630
364
|
|
|
631
|
-
// The map is the evidence, as everywhere else in this class: an id whose
|
|
632
|
-
// entry is no longer this one was cancelled and resumed, so this
|
|
633
|
-
// process's late frames must not reach the buffer, the state, or the
|
|
634
|
-
// clients the replacement now owns. `entry` is declared below and read
|
|
635
|
-
// only from inside closures the process cannot fire until it exists.
|
|
636
|
-
const ownsSession = () => this.sessions.get(acpSessionId) === entry;
|
|
637
|
-
|
|
638
365
|
// Wrap the sender so every emitted message is mirrored into the buffer
|
|
639
366
|
// when it's an `acp_session_update`. The wrapper preserves the original
|
|
640
367
|
// call semantics: it forwards every message unchanged.
|
|
641
368
|
const wrappedSend = (msg: AssistantEvent) => {
|
|
642
|
-
if (!ownsSession()) {
|
|
643
|
-
return;
|
|
644
|
-
}
|
|
645
369
|
if (msg.type === "acp_session_update") {
|
|
646
370
|
this.appendToBuffer(acpSessionId, msg);
|
|
647
371
|
} else if (msg.type === "acp_session_usage") {
|
|
@@ -662,15 +386,10 @@ export class AcpSessionManager {
|
|
|
662
386
|
opts.sendToVellum(msg);
|
|
663
387
|
};
|
|
664
388
|
|
|
665
|
-
// The callback runs once the agent is connected and sending
|
|
666
|
-
// notifications, which is after agentProcess is constructed just below.
|
|
667
389
|
const clientHandler = new VellumAcpClientHandler(
|
|
668
390
|
acpSessionId,
|
|
669
391
|
wrappedSend,
|
|
670
392
|
opts.parentConversationId,
|
|
671
|
-
(configOptions) => {
|
|
672
|
-
this.applyUnsolicitedConfigOptions(acpSessionId, entry, configOptions);
|
|
673
|
-
},
|
|
674
393
|
);
|
|
675
394
|
|
|
676
395
|
const agentProcess = new AcpAgentProcess(
|
|
@@ -725,98 +444,6 @@ export class AcpSessionManager {
|
|
|
725
444
|
});
|
|
726
445
|
}
|
|
727
446
|
|
|
728
|
-
/**
|
|
729
|
-
* Publishes the session's model selection. Silent for adapters with no
|
|
730
|
-
* model selector: the client renders nothing rather than an empty picker.
|
|
731
|
-
* A separate frame from `acp_session_spawned`, which carries a fixed subset
|
|
732
|
-
* older clients parse.
|
|
733
|
-
*/
|
|
734
|
-
private sendModelEvent(acpSessionId: string, entry: SessionEntry): void {
|
|
735
|
-
if (!entry.modelConfigId) {
|
|
736
|
-
return;
|
|
737
|
-
}
|
|
738
|
-
this.sendModelSnapshot(acpSessionId, entry);
|
|
739
|
-
}
|
|
740
|
-
|
|
741
|
-
/** Publishes model state with the same revision exposed by session reads. */
|
|
742
|
-
private sendModelSnapshot(acpSessionId: string, entry: SessionEntry): void {
|
|
743
|
-
const modelRevision = ++this.modelRevision;
|
|
744
|
-
entry.state.modelRevisionEpoch = this.modelRevisionEpoch;
|
|
745
|
-
entry.state.modelRevision = modelRevision;
|
|
746
|
-
entry.sendToVellum({
|
|
747
|
-
type: "acp_session_model_update",
|
|
748
|
-
acpSessionId,
|
|
749
|
-
modelRevisionEpoch: this.modelRevisionEpoch,
|
|
750
|
-
modelRevision,
|
|
751
|
-
model: entry.state.model,
|
|
752
|
-
availableModels: entry.state.availableModels ?? [],
|
|
753
|
-
});
|
|
754
|
-
}
|
|
755
|
-
|
|
756
|
-
/**
|
|
757
|
-
* Whether `entry` is still this manager's live entry for `acpSessionId`.
|
|
758
|
-
* A notification that lands after close, cancellation, or prompt completion
|
|
759
|
-
* has nothing left to update: the terminal row is already written, so
|
|
760
|
-
* applying it would leave history on one model and clients on another.
|
|
761
|
-
*/
|
|
762
|
-
private isEntryLive(acpSessionId: string, entry: SessionEntry): boolean {
|
|
763
|
-
return (
|
|
764
|
-
this.sessions.get(acpSessionId) === entry &&
|
|
765
|
-
isLiveAcpStatus(entry.state.status)
|
|
766
|
-
);
|
|
767
|
-
}
|
|
768
|
-
|
|
769
|
-
/**
|
|
770
|
-
* Drops the live model snapshot a vanished selector took with it and
|
|
771
|
-
* publishes the empty picker. `applyModelInfo` keeps the model already
|
|
772
|
-
* recorded for adapters that never had a selector, which here would leave
|
|
773
|
-
* the client holding options the session cannot reach. Returns whether it
|
|
774
|
-
* fired, so callers skip the publish that assumes a model is still there.
|
|
775
|
-
*
|
|
776
|
-
* A published picker is what says the selector was there to vanish: only an
|
|
777
|
-
* adapter that advertised one ever populates it, so an adapter that never
|
|
778
|
-
* had one publishes nothing.
|
|
779
|
-
*/
|
|
780
|
-
private clearVanishedModelSelector(
|
|
781
|
-
acpSessionId: string,
|
|
782
|
-
entry: SessionEntry,
|
|
783
|
-
): boolean {
|
|
784
|
-
if (entry.modelConfigId || entry.state.availableModels === undefined) {
|
|
785
|
-
return false;
|
|
786
|
-
}
|
|
787
|
-
entry.state.model = undefined;
|
|
788
|
-
entry.state.availableModels = [];
|
|
789
|
-
this.sendModelSnapshot(acpSessionId, entry);
|
|
790
|
-
return true;
|
|
791
|
-
}
|
|
792
|
-
|
|
793
|
-
/**
|
|
794
|
-
* Takes in a `config_option_update` the adapter sent unprompted, which is
|
|
795
|
-
* how a `/model` the user typed straight into the transcript reaches the
|
|
796
|
-
* daemon: record what it reports, publish it, and drop the live snapshot
|
|
797
|
-
* when the selector it announced has vanished.
|
|
798
|
-
*
|
|
799
|
-
* A session past its terminal transition takes nothing from a late
|
|
800
|
-
* notification at all: its history row is already written, so mutating
|
|
801
|
-
* state or emitting here would leave clients and history disagreeing about
|
|
802
|
-
* the same run.
|
|
803
|
-
*/
|
|
804
|
-
private applyUnsolicitedConfigOptions(
|
|
805
|
-
acpSessionId: string,
|
|
806
|
-
entry: SessionEntry,
|
|
807
|
-
configOptions: SessionConfigOption[],
|
|
808
|
-
): void {
|
|
809
|
-
if (!this.isEntryLive(acpSessionId, entry)) {
|
|
810
|
-
return;
|
|
811
|
-
}
|
|
812
|
-
this.applyModelInfo(entry, configOptions);
|
|
813
|
-
|
|
814
|
-
if (this.clearVanishedModelSelector(acpSessionId, entry)) {
|
|
815
|
-
return;
|
|
816
|
-
}
|
|
817
|
-
this.sendModelEvent(acpSessionId, entry);
|
|
818
|
-
}
|
|
819
|
-
|
|
820
447
|
/**
|
|
821
448
|
* Resumes a terminal-state session from its persisted
|
|
822
449
|
* `acp_session_history` row, reattaching to the agent's stored
|
|
@@ -996,7 +623,6 @@ export class AcpSessionManager {
|
|
|
996
623
|
// Seed before the child process spawns so no live update can fire first.
|
|
997
624
|
entry.clientHandler.seedSeq(maxSeq);
|
|
998
625
|
|
|
999
|
-
let configOptions: SessionConfigOption[] = [];
|
|
1000
626
|
try {
|
|
1001
627
|
log.info(
|
|
1002
628
|
{ acpSessionId, agentId: row.agentId },
|
|
@@ -1006,20 +632,14 @@ export class AcpSessionManager {
|
|
|
1006
632
|
await agentProcess.initialize();
|
|
1007
633
|
if (agentProcess.supportsSessionResume) {
|
|
1008
634
|
// session/resume reattaches without replaying history.
|
|
1009
|
-
|
|
1010
|
-
row.acpSessionId,
|
|
1011
|
-
row.cwd,
|
|
1012
|
-
));
|
|
635
|
+
await agentProcess.resumeSession(row.acpSessionId, row.cwd);
|
|
1013
636
|
} else if (agentProcess.supportsLoadSession) {
|
|
1014
637
|
// session/load replays the full history as session/update
|
|
1015
638
|
// notifications before resolving; suppress forwarding so the
|
|
1016
639
|
// conversation and ring buffer don't receive duplicates.
|
|
1017
640
|
entry.clientHandler.beginReplaySuppression();
|
|
1018
641
|
try {
|
|
1019
|
-
|
|
1020
|
-
row.acpSessionId,
|
|
1021
|
-
row.cwd,
|
|
1022
|
-
));
|
|
642
|
+
await agentProcess.loadSession(row.acpSessionId, row.cwd);
|
|
1023
643
|
} finally {
|
|
1024
644
|
entry.clientHandler.endReplaySuppression();
|
|
1025
645
|
}
|
|
@@ -1048,43 +668,7 @@ export class AcpSessionManager {
|
|
|
1048
668
|
throw err;
|
|
1049
669
|
}
|
|
1050
670
|
|
|
1051
|
-
// The ladder a spawn walks, for the case the adapter reports no model of
|
|
1052
|
-
// its own; a reattached session that comes back naming one keeps it.
|
|
1053
|
-
const resolvedModel = resolveAcpModel({ agentModel: agentConfig.model });
|
|
1054
|
-
let applied: boolean;
|
|
1055
|
-
try {
|
|
1056
|
-
({ applied } = await this.pinSessionModel(
|
|
1057
|
-
entry,
|
|
1058
|
-
configOptions,
|
|
1059
|
-
undefined,
|
|
1060
|
-
resolvedModel,
|
|
1061
|
-
{ keepReportedModel: true },
|
|
1062
|
-
));
|
|
1063
|
-
} catch (err) {
|
|
1064
|
-
log.error(
|
|
1065
|
-
{ acpSessionId, agentId: row.agentId, err },
|
|
1066
|
-
"ACP resume failed while applying the model",
|
|
1067
|
-
);
|
|
1068
|
-
const failure = this.classifyPinFailure(err, row.agentId, agentConfig);
|
|
1069
|
-
this.teardownSession(acpSessionId, entry);
|
|
1070
|
-
throw failure;
|
|
1071
|
-
}
|
|
1072
|
-
if (resolvedModel && !applied) {
|
|
1073
|
-
// State keeps the adapter's own answer, so the model event, the status
|
|
1074
|
-
// projection and the panel all name the model the run is really on.
|
|
1075
|
-
log.warn(
|
|
1076
|
-
{
|
|
1077
|
-
acpSessionId,
|
|
1078
|
-
agentId: row.agentId,
|
|
1079
|
-
resolvedModel,
|
|
1080
|
-
model: state.model,
|
|
1081
|
-
},
|
|
1082
|
-
"ACP resume could not apply the configured model; running on the agent's own model",
|
|
1083
|
-
);
|
|
1084
|
-
}
|
|
1085
|
-
|
|
1086
671
|
this.sendSpawnedEvent(acpSessionId, entry);
|
|
1087
|
-
this.sendModelEvent(acpSessionId, entry);
|
|
1088
672
|
}
|
|
1089
673
|
|
|
1090
674
|
/**
|
|
@@ -1345,7 +929,10 @@ export class AcpSessionManager {
|
|
|
1345
929
|
if (!entry) {
|
|
1346
930
|
throw new AcpSessionNotFoundError(acpSessionId);
|
|
1347
931
|
}
|
|
1348
|
-
if (
|
|
932
|
+
if (
|
|
933
|
+
entry.state.status === "running" ||
|
|
934
|
+
entry.state.status === "initializing"
|
|
935
|
+
) {
|
|
1349
936
|
entry.state.status = "cancelled";
|
|
1350
937
|
entry.state.completedAt = Date.now();
|
|
1351
938
|
}
|
|
@@ -1355,13 +942,6 @@ export class AcpSessionManager {
|
|
|
1355
942
|
|
|
1356
943
|
/**
|
|
1357
944
|
* Denies pending ACP permissions, kills the process, and removes the session.
|
|
1358
|
-
*
|
|
1359
|
-
* The permissions and the process belong to `entry`, so they are always
|
|
1360
|
-
* torn down; the map slot and its buffer belong to whichever entry holds
|
|
1361
|
-
* the id, so they are only cleared while that is still this one. A caller
|
|
1362
|
-
* whose entry the map has since replaced (a cancel that persisted a
|
|
1363
|
-
* resumable row, then a resume of the same id, while an await was pending)
|
|
1364
|
-
* would otherwise evict the live session that took its place.
|
|
1365
945
|
*/
|
|
1366
946
|
private teardownSession(acpSessionId: string, entry: SessionEntry): void {
|
|
1367
947
|
for (const requestId of entry.clientHandler.pendingRequestIds) {
|
|
@@ -1371,9 +951,6 @@ export class AcpSessionManager {
|
|
|
1371
951
|
}
|
|
1372
952
|
}
|
|
1373
953
|
entry.process.kill();
|
|
1374
|
-
if (this.sessions.get(acpSessionId) !== entry) {
|
|
1375
|
-
return;
|
|
1376
|
-
}
|
|
1377
954
|
this.sessions.delete(acpSessionId);
|
|
1378
955
|
// Free the buffer in case persistTerminal hasn't already (e.g. close()
|
|
1379
956
|
// before terminal transition).
|