@vellumai/assistant 0.12.1 → 0.12.2-staging.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +4 -0
- package/ARCHITECTURE.md +20 -21
- package/docs/architecture/turn-actor.md +15 -8
- package/docs/guardian-request-flow.md +16 -0
- package/openapi.yaml +327 -52
- package/package.json +3 -1
- package/scripts/smoke-desktop-window-theme.ts +186 -0
- package/src/__tests__/always-loaded-tools-guard.test.ts +18 -10
- package/src/__tests__/anthropic-provider.test.ts +0 -63
- package/src/__tests__/app-compiler.test.ts +6 -0
- package/src/__tests__/app-control-tool-schemas.test.ts +5 -2
- package/src/__tests__/app-dir-path-guard.test.ts +1 -3
- package/src/__tests__/assistant-event-hub-machine-name.test.ts +6 -7
- package/src/__tests__/assistant-event-hub.test.ts +44 -0
- package/src/__tests__/computer-use-skill-manifest-regression.test.ts +1 -1
- package/src/__tests__/computer-use-skill-proxy-bridge.test.ts +5 -2
- package/src/__tests__/computer-use-tools.test.ts +19 -7
- package/src/__tests__/config-schema-cmd.test.ts +2 -2
- package/src/__tests__/config-schema.test.ts +28 -29
- package/src/__tests__/config-watcher.test.ts +23 -8
- 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 +26 -0
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +4 -10
- package/src/__tests__/conversation-queue.test.ts +48 -207
- package/src/__tests__/conversation-retry-discard.test.ts +54 -1
- package/src/__tests__/conversation-skill-tools.test.ts +27 -0
- package/src/__tests__/conversation-surfaces-app-control.test.ts +8 -3
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +67 -0
- package/src/__tests__/conversation-title-service.test.ts +14 -10
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +3 -5
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +51 -0
- package/src/__tests__/credential-record-retire.test.ts +171 -1
- package/src/__tests__/credential-routes.test.ts +212 -3
- package/src/__tests__/credential-security-invariants.test.ts +4 -2
- package/src/__tests__/cu-unified-flow.test.ts +8 -2
- package/src/__tests__/document-sync-tags.test.ts +25 -0
- package/src/__tests__/drain-kick-guard.test.ts +0 -2
- package/src/__tests__/drain-requeue-on-contention.test.ts +0 -6
- package/src/__tests__/evict-conversations-for-reload.test.ts +1 -1
- package/src/__tests__/git-porcelain.test.ts +47 -0
- package/src/__tests__/heartbeat-service.test.ts +56 -40
- package/src/__tests__/helpers/set-workspace-mcp.ts +11 -0
- package/src/__tests__/host-bash-proxy.test.ts +26 -45
- package/src/__tests__/host-browser-proxy.test.ts +6 -17
- package/src/__tests__/host-cu-proxy.test.ts +539 -0
- package/src/__tests__/host-file-proxy-targeted.test.ts +23 -0
- package/src/__tests__/host-file-proxy.test.ts +11 -23
- package/src/__tests__/host-transfer-proxy-targeted.test.ts +30 -9
- package/src/__tests__/list-messages-camera-frame.test.ts +121 -0
- package/src/__tests__/llm-catalog-parity.test.ts +25 -0
- package/src/__tests__/mcp-auth-routes.test.ts +6 -9
- package/src/__tests__/mcp-config-secret-boundary.test.ts +78 -136
- package/src/__tests__/mcp-health-check.test.ts +8 -11
- package/src/__tests__/mcp-list-plugin-servers.test.ts +8 -10
- package/src/__tests__/message-deleted-hook-dispatch.test.ts +119 -0
- package/src/__tests__/messages-after-tiebreaker.test.ts +91 -0
- package/src/__tests__/messages-read-boundary-guard.test.ts +1 -1
- package/src/__tests__/messaging-send-tool.test.ts +355 -548
- package/src/__tests__/model-intents.test.ts +3 -0
- package/src/__tests__/notification-vellum-adapter.test.ts +85 -2
- package/src/__tests__/oauth-commands-routes.test.ts +113 -0
- package/src/__tests__/oauth-provider-profiles.test.ts +18 -0
- package/src/__tests__/openai-provider.test.ts +1 -0
- package/src/__tests__/outlook-messaging-provider.test.ts +6 -6
- package/src/__tests__/platform-callback-registration.test.ts +8 -16
- package/src/__tests__/platform-client-verify-credential.test.ts +2 -0
- package/src/__tests__/plugin-import-boundary-guard.test.ts +14 -0
- package/src/__tests__/qdrant-manager.test.ts +132 -0
- package/src/__tests__/resolve-app.test.ts +135 -0
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +229 -6
- package/src/__tests__/script-proxy-certs.test.ts +35 -0
- package/src/__tests__/service-account-token.test.ts +247 -0
- package/src/__tests__/skill-feature-flags-integration.test.ts +1 -1
- package/src/__tests__/skill-feature-flags.test.ts +2 -2
- package/src/__tests__/skills.test.ts +9 -4
- package/src/__tests__/source-fingerprint.test.ts +184 -0
- package/src/__tests__/subagent-tool-gate-mode.test.ts +19 -3
- package/src/__tests__/subagent-tools.test.ts +3 -2
- package/src/__tests__/telephony-tts-guard.test.ts +38 -3
- package/src/__tests__/test-support/computer-use-skill-harness.ts +3 -2
- package/src/__tests__/unicode.test.ts +13 -0
- package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +4 -0
- package/src/__tests__/watch-retro-tool-availability.test.ts +8 -17
- package/src/__tests__/workspace-git-service.test.ts +26 -0
- package/src/__tests__/workspace-migration-154-repair-retired-codex-gpt-5-4-model-ids.test.ts +336 -0
- package/src/acp/__tests__/agent-process.test.ts +258 -4
- package/src/acp/__tests__/auth-required.test.ts +96 -0
- package/src/acp/__tests__/client-handler.test.ts +90 -2
- package/src/acp/__tests__/helpers/acp-model-option.ts +51 -0
- package/src/acp/__tests__/session-manager-persistence.test.ts +5 -1
- package/src/acp/__tests__/session-manager-resume.test.ts +270 -6
- package/src/acp/__tests__/session-manager-usage.test.ts +4 -2
- package/src/acp/__tests__/session-manager.test.ts +895 -4
- package/src/acp/agent-process.test.ts +4 -1
- package/src/acp/agent-process.ts +96 -10
- package/src/acp/auth-required.ts +50 -1
- package/src/acp/auto-install.test.ts +9 -7
- package/src/acp/client-handler.ts +16 -3
- package/src/acp/model-config.test.ts +166 -0
- package/src/acp/model-config.ts +138 -0
- package/src/acp/resolve-agent.test.ts +220 -6
- package/src/acp/resolve-agent.ts +88 -23
- package/src/acp/session-manager.test.ts +196 -3
- package/src/acp/session-manager.ts +441 -18
- package/src/acp/types.ts +50 -5
- package/src/agent/loop.ts +1 -0
- package/src/api/events/acp-session-model-update.test.ts +88 -0
- package/src/api/events/acp-session-model-update.ts +46 -0
- package/src/api/events/app-files-changed.ts +4 -3
- package/src/api/events/notification-intent.ts +2 -0
- package/src/api/events/user-message-echo.ts +7 -1
- package/src/api/index.ts +6 -0
- package/src/api/responses/conversation-message.ts +5 -0
- package/src/apps/app-store.ts +1 -1
- package/src/apps/resolve-app.ts +81 -0
- package/src/apps/source-fingerprint.ts +266 -0
- package/src/avatar/identity-avatar.ts +7 -2
- package/src/bundler/app-compiler.ts +15 -0
- package/src/calls/__tests__/barge-in-guard.test.ts +81 -0
- package/src/{__tests__ → calls/__tests__}/call-controller.test.ts +503 -82
- package/src/{__tests__ → calls/__tests__}/media-stream-audio-transcode.test.ts +1 -1
- package/src/{__tests__ → calls/__tests__}/media-stream-output.test.ts +37 -8
- package/src/{__tests__ → calls/__tests__}/media-stream-parser.test.ts +1 -1
- package/src/{__tests__ → calls/__tests__}/media-stream-server-integration.test.ts +150 -57
- package/src/{__tests__ → calls/__tests__}/media-stream-stt-session.test.ts +5 -5
- package/src/{__tests__ → calls/__tests__}/media-turn-detector.test.ts +1 -1
- package/src/{live-voice → calls}/__tests__/progress-narration.test.ts +4 -4
- package/src/{live-voice → calls}/__tests__/progress-phrases.test.ts +1 -1
- package/src/calls/__tests__/voice-control-protocol.test.ts +58 -25
- package/src/calls/__tests__/voice-leg-coordinator.test.ts +229 -0
- package/src/calls/__tests__/voice-progress-cadence.test.ts +274 -0
- package/src/{__tests__/voice-session-bridge.test.ts → calls/__tests__/voice-session-bridge-turn-flow.test.ts} +20 -12
- package/src/calls/__tests__/voice-session-bridge.test.ts +37 -1
- package/src/calls/__tests__/voice-triage-escalate.test.ts +105 -0
- package/src/calls/barge-in-guard.ts +118 -0
- package/src/calls/call-constants.ts +24 -4
- package/src/calls/call-controller.ts +499 -199
- package/src/calls/call-transport.ts +16 -0
- package/src/calls/media-stream-output.ts +32 -0
- package/src/calls/media-stream-server.ts +82 -30
- package/src/calls/media-stream-stt-session.ts +15 -0
- package/src/{live-voice → calls}/progress-narration.ts +2 -2
- package/src/calls/resolve-call-tts-provider.ts +33 -2
- package/src/calls/tts-call-strategy.ts +9 -5
- package/src/calls/types.ts +2 -0
- package/src/calls/voice-control-protocol.ts +53 -21
- package/src/calls/voice-leg-coordinator.ts +189 -0
- package/src/calls/voice-progress-cadence.ts +423 -0
- package/src/calls/voice-session-bridge.ts +59 -46
- package/src/calls/voice-triage-escalate.ts +175 -56
- package/src/cli/commands/__tests__/apps.test.ts +245 -0
- package/src/cli/commands/__tests__/bash.test.ts +53 -0
- package/src/cli/commands/__tests__/roadmap.help.test.ts +19 -0
- package/src/cli/commands/apps.help.ts +67 -13
- package/src/cli/commands/apps.ts +183 -0
- package/src/cli/commands/bash.help.ts +8 -13
- package/src/cli/commands/bash.ts +7 -25
- package/src/cli/commands/channels/__tests__/send.test.ts +218 -0
- package/src/cli/commands/channels/index.help.ts +70 -2
- package/src/cli/commands/channels/index.ts +16 -3
- package/src/cli/commands/channels/send.ts +128 -0
- package/src/cli/commands/credentials.help.ts +6 -2
- 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 +5 -1
- package/src/cli/commands/roadmap.ts +1 -1
- package/src/cli/lib/bundled-marketplace.json +103 -90
- package/src/config/__tests__/default-profile-catalog.test.ts +22 -0
- package/src/config/__tests__/memory-retrospective-schema.test.ts +15 -1
- package/src/config/__tests__/platform-identity.test.ts +255 -0
- package/src/config/__tests__/platform-rehydration.test.ts +99 -9
- package/src/config/acp-agent-write.ts +43 -0
- package/src/config/acp-defaults.test.ts +13 -3
- package/src/config/acp-defaults.ts +23 -6
- package/src/config/acp-schema.test.ts +81 -0
- package/src/config/acp-schema.ts +30 -2
- package/src/config/bundled-skills/acp/SKILL.md +22 -14
- package/src/config/bundled-skills/acp/TOOLS.json +5 -1
- package/src/config/bundled-skills/app-builder/SKILL.md +12 -9
- package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +1 -1
- package/src/config/bundled-skills/computer-use/SKILL.md +20 -2
- package/src/config/bundled-skills/computer-use/TOOLS.json +99 -0
- package/src/config/bundled-skills/computer-use/tools/computer-use-sequence.ts +12 -0
- package/src/config/bundled-skills/media-processing/services/preprocess.ts +2 -1
- package/src/config/bundled-skills/media-processing/services/processing-pipeline.ts +2 -1
- package/src/config/bundled-skills/media-processing/tools/generate-clip.ts +2 -1
- package/src/config/bundled-skills/messaging/SKILL.md +3 -3
- package/src/config/bundled-skills/messaging/TOOLS.json +3 -3
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +67 -139
- package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +1 -1
- package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +2 -1
- package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +2 -1
- package/src/config/bundled-skills/schedule/SKILL.md +3 -4
- package/src/config/bundled-skills/screen-annotation/SKILL.md +8 -1
- package/src/config/bundled-skills/screen-annotation/tools/screen-annotation.test.ts +4 -1
- package/src/config/bundled-skills/skill-management/TOOLS.json +6 -2
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +5 -2
- package/src/config/default-profile-catalog.ts +1 -1
- package/src/config/env.ts +2 -2
- package/src/config/feature-flag-registry.json +24 -12
- package/src/config/loader.ts +1 -0
- package/src/config/memory-v3-gate.test.ts +31 -0
- package/src/config/memory-v3-gate.ts +20 -16
- package/src/config/platform-identity.ts +201 -0
- package/src/config/platform-rehydration.ts +11 -27
- package/src/config/schema.ts +2 -2
- package/src/config/schemas/__tests__/live-voice.test.ts +8 -158
- package/src/config/schemas/__tests__/voice.test.ts +148 -0
- package/src/config/schemas/calls.ts +1 -28
- package/src/config/schemas/live-voice.ts +1 -173
- package/src/config/schemas/mcp.ts +1 -1
- package/src/config/schemas/memory-retrospective.ts +10 -0
- package/src/config/schemas/services.ts +8 -0
- package/src/config/schemas/skills.ts +0 -14
- package/src/config/schemas/voice.ts +150 -0
- package/src/credential-execution/process-manager.test.ts +18 -19
- package/src/credential-health/credential-health-service.ts +11 -17
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +66 -121
- package/src/daemon/__tests__/plugin-mcp-reconcile.test.ts +4 -1
- package/src/daemon/__tests__/skill-memory-refresh-mtime.test.ts +133 -0
- package/src/daemon/app-change-notify.ts +45 -0
- package/src/daemon/config-watcher.ts +13 -194
- package/src/daemon/conversation-error.ts +3 -2
- package/src/daemon/conversation-media-retry.ts +2 -1
- package/src/daemon/conversation-process.ts +35 -64
- package/src/daemon/conversation-runtime-assembly.ts +2 -1
- package/src/daemon/conversation-skill-tools.ts +13 -31
- package/src/daemon/conversation-store.ts +1 -1
- package/src/daemon/conversation-surfaces.ts +15 -9
- package/src/daemon/conversation-tool-setup.ts +37 -43
- package/src/daemon/conversation.ts +0 -3
- package/src/daemon/dictation-profile-store.ts +2 -1
- package/src/daemon/handlers/shared.ts +2 -1
- package/src/daemon/handlers/skills.ts +6 -3
- package/src/daemon/host-bash-proxy.ts +8 -12
- package/src/daemon/host-browser-proxy.ts +15 -1
- package/src/daemon/host-cu-proxy.ts +186 -19
- package/src/daemon/host-file-proxy.ts +8 -10
- package/src/daemon/host-proxy-preactivation.ts +2 -1
- package/src/daemon/host-transfer-proxy.ts +14 -18
- package/src/daemon/lifecycle.ts +0 -5
- package/src/daemon/mcp-reload-service.ts +5 -5
- package/src/daemon/message-types/sync.ts +1 -0
- package/src/daemon/orphan-reaper.ts +1 -1
- package/src/daemon/providers-setup.ts +1 -1
- package/src/daemon/reaction-history-render.ts +2 -1
- package/src/daemon/shutdown-handlers.ts +1 -9
- package/src/daemon/skill-memory-refresh.ts +64 -0
- package/src/daemon/tool-side-effects.ts +7 -18
- package/src/desktop/__tests__/fake-desktop.ts +122 -0
- package/src/desktop/desktop-chrome-policy.test.ts +79 -0
- package/src/desktop/desktop-chrome-policy.ts +47 -0
- package/src/desktop/desktop-chrome-session.ts +37 -0
- package/src/desktop/desktop-dependencies.test.ts +102 -0
- package/src/desktop/desktop-dependencies.ts +308 -0
- package/src/desktop/desktop-feature.test.ts +18 -0
- package/src/desktop/desktop-feature.ts +19 -0
- package/src/desktop/desktop-panel-config.test.ts +135 -0
- package/src/desktop/desktop-panel-config.ts +149 -0
- package/src/desktop/desktop-session-manager.test.ts +730 -0
- package/src/desktop/desktop-session-manager.ts +850 -0
- package/src/desktop/desktop-stream-bridge.test.ts +395 -0
- package/src/desktop/desktop-stream-bridge.ts +214 -0
- package/src/desktop/desktop-wallpaper.test.ts +191 -0
- package/src/desktop/desktop-wallpaper.ts +113 -0
- package/src/desktop/desktop-window-drag.ts +177 -0
- package/src/desktop/desktop-window-manager-config.test.ts +230 -0
- package/src/desktop/desktop-window-manager-config.ts +238 -0
- package/src/desktop/desktop-window-theme.ts +170 -0
- package/src/heartbeat/heartbeat-service.ts +21 -3
- package/src/home/__tests__/feed-writer.test.ts +22 -0
- package/src/home/feed-writer.ts +11 -2
- package/src/hooks/types.ts +33 -0
- package/src/i18n/AGENTS.md +22 -0
- package/src/i18n/__tests__/i18n.test.ts +108 -0
- package/src/i18n/index.ts +16 -0
- package/src/i18n/locales.ts +93 -0
- package/src/i18n/messages.ts +61 -0
- package/src/i18n/title.ts +25 -0
- package/src/inbound/platform-callback-registration.ts +4 -5
- package/src/live-voice/__tests__/live-voice-metrics.test.ts +0 -3
- package/src/live-voice/__tests__/live-voice-progress.test.ts +15 -15
- package/src/live-voice/__tests__/live-voice-sight-frame-echo.test.ts +238 -0
- package/src/live-voice/__tests__/live-voice-vad.test.ts +2 -2
- package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +13 -69
- package/src/live-voice/live-voice-photo.ts +25 -6
- package/src/live-voice/live-voice-session.ts +213 -693
- package/src/live-voice/protocol.ts +0 -2
- package/src/mcp/__tests__/effective-config.test.ts +2 -2
- package/src/mcp/__tests__/workspace-mcp-config.test.ts +135 -0
- package/src/mcp/effective-config.ts +3 -3
- package/src/mcp/mcp-header-store.ts +29 -45
- package/src/mcp/spec-schema.ts +111 -0
- package/src/mcp/startup.ts +8 -4
- package/src/mcp/workspace-mcp-config.ts +188 -0
- package/src/messaging/provider.ts +10 -3
- package/src/messaging/providers/__tests__/proactive-address.test.ts +95 -0
- package/src/messaging/providers/callback-routing.ts +18 -0
- package/src/messaging/providers/channel-transport.ts +56 -0
- package/src/messaging/providers/discord/transport.ts +17 -0
- package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +6 -17
- package/src/messaging/providers/slack/adapter.ts +7 -44
- package/src/messaging/providers/slack/transport.ts +16 -0
- package/src/messaging/providers/telegram-bot/adapter.ts +1 -48
- package/src/messaging/providers/telegram-bot/client.ts +0 -26
- package/src/messaging/providers/telegram-bot/transport.ts +20 -0
- package/src/messaging/providers/whatsapp/adapter.ts +1 -48
- package/src/messaging/providers/whatsapp/transport.ts +18 -0
- package/src/monitoring/__tests__/workspace-git-heartbeat.test.ts +8 -0
- package/src/notifications/__tests__/assistant-reply-producer.test.ts +55 -0
- package/src/notifications/__tests__/guardian-feed-projection.test.ts +158 -2
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +153 -3
- package/src/notifications/__tests__/schedule-result-producer.test.ts +99 -3
- package/src/notifications/adapters/macos.ts +3 -0
- package/src/notifications/assistant-reply-producer.ts +8 -3
- package/src/notifications/conversation-pairing.ts +36 -16
- package/src/notifications/delivered-post-record.ts +11 -1
- package/src/notifications/guardian-feed-projection.ts +125 -5
- package/src/notifications/home-feed-side-effect.ts +55 -7
- package/src/notifications/schedule-result-producer.ts +66 -22
- package/src/oauth/AGENTS.md +2 -0
- package/src/oauth/__tests__/seed-providers-managed.test.ts +54 -0
- package/src/oauth/byo-connection.test.ts +105 -0
- package/src/oauth/byo-connection.ts +36 -6
- package/src/oauth/connection-resolver.test.ts +73 -0
- package/src/oauth/connection-resolver.ts +95 -2
- package/src/oauth/scope-utils.ts +24 -0
- package/src/oauth/seed-providers.ts +204 -25
- package/src/outbound-proxy/certs.ts +3 -5
- package/src/permissions/checker.test.ts +11 -6
- package/src/persistence/compaction-log-store-clickhouse.ts +3 -3
- package/src/persistence/conversation-crud.ts +71 -63
- package/src/persistence/conversation-lineage.ts +12 -4
- package/src/persistence/conversation-queries.ts +2 -2
- package/src/persistence/conversation-title-placeholders.ts +23 -0
- package/src/persistence/conversation-title-service.ts +9 -26
- package/src/persistence/embeddings/qdrant-manager.ts +163 -18
- 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-cursor.ts +78 -0
- package/src/persistence/message-reads.ts +30 -0
- package/src/persistence/migrations/157-invite-contact-id.ts +18 -6
- package/src/persistence/migrations/__tests__/157-invite-contact-id.test.ts +149 -0
- package/src/persistence/schema/memory-core.ts +5 -0
- package/src/platform/client.test.ts +4 -2
- package/src/platform/client.ts +2 -10
- package/src/plugin-api/constants.ts +2 -0
- package/src/plugin-api/index.ts +1 -0
- package/src/plugin-api/types.ts +2 -0
- package/src/plugins/defaults/index.ts +2 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +46 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-backfill.test.ts +118 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-preserve.test.ts +83 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +9 -5
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +20 -10
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-prompt.test.ts +5 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-state.test.ts +219 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-sweep.test.ts +31 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-trigger-check.test.ts +63 -0
- package/src/plugins/defaults/memory/__tests__/shutdown-hook.test.ts +4 -0
- package/src/plugins/defaults/memory/context-search/agent-runner.ts +3 -2
- package/src/plugins/defaults/memory/context-search/format.ts +3 -2
- package/src/plugins/defaults/memory/context-search/sources/memory-v2.ts +6 -3
- package/src/plugins/defaults/memory/context-search/sources/workspace.ts +3 -2
- package/src/plugins/defaults/memory/graph/capability-seed.ts +6 -7
- package/src/plugins/defaults/memory/hooks/init.ts +10 -0
- package/src/plugins/defaults/memory/hooks/message-deleted.ts +21 -0
- package/src/plugins/defaults/memory/hooks/shutdown.ts +2 -0
- package/src/plugins/defaults/memory/host-utils.ts +10 -0
- package/src/plugins/defaults/memory/jobs-worker.ts +13 -0
- package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +34 -60
- package/src/plugins/defaults/memory/memory-retrospective-cursor-backfill.ts +126 -0
- package/src/plugins/defaults/memory/memory-retrospective-cursor-preserve.ts +79 -0
- package/src/plugins/defaults/memory/memory-retrospective-cursor.ts +28 -0
- package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +3 -5
- package/src/plugins/defaults/memory/memory-retrospective-job.ts +12 -3
- package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +1 -1
- package/src/plugins/defaults/memory/memory-retrospective-state.ts +312 -71
- package/src/plugins/defaults/memory/memory-retrospective-sweep.ts +2 -1
- package/src/plugins/defaults/memory/memory-retrospective-trigger-check.ts +5 -3
- package/src/plugins/defaults/memory/skill-md-mtime-poll.ts +9 -0
- package/src/plugins/defaults/memory/startup.ts +28 -2
- package/src/plugins/defaults/memory/substrate/__tests__/skill-content.test.ts +7 -9
- package/src/plugins/defaults/memory/substrate/page-index.ts +6 -1
- package/src/plugins/defaults/memory/substrate/skill-content.ts +2 -2
- package/src/plugins/defaults/memory/v1/graph/consolidation.ts +4 -2
- package/src/plugins/defaults/memory/v1/graph/extraction.ts +4 -2
- package/src/plugins/defaults/memory/v1/graph/retriever.ts +4 -1
- package/src/plugins/defaults/memory/v2/reranker.ts +6 -1
- package/src/plugins/defaults/memory/v3/__tests__/card.test.ts +24 -0
- package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +125 -0
- package/src/plugins/defaults/memory/v3/__tests__/sections.test.ts +26 -0
- package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +30 -0
- package/src/plugins/defaults/memory/v3/card.ts +3 -2
- package/src/plugins/defaults/memory/v3/pool-select.ts +5 -3
- package/src/plugins/defaults/memory/v3/sections.ts +9 -3
- package/src/plugins/defaults/memory/v3/shadow-plugin.ts +5 -2
- package/src/plugins/defaults/memory/worker.ts +6 -0
- package/src/plugins/mcp-servers.ts +15 -75
- package/src/providers/__tests__/connection-model-compat.test.ts +3 -3
- package/src/providers/__tests__/retry-callsite.test.ts +41 -0
- package/src/providers/__tests__/retry-outbound-sanitize.test.ts +176 -0
- package/src/providers/anthropic/client.ts +0 -47
- package/src/providers/gemini/client.ts +9 -23
- package/src/providers/gemini-thought-signature.test.ts +63 -0
- package/src/providers/gemini-thought-signature.ts +44 -0
- package/src/providers/inference/__tests__/endpoint-probe.test.ts +42 -3
- package/src/providers/inference/auth.ts +2 -3
- package/src/providers/inference/connection-availability.ts +27 -8
- package/src/providers/inference/endpoint-probe.ts +10 -1
- package/src/providers/inference/resolve-auth.ts +22 -4
- package/src/providers/inference/service-account-token.ts +248 -0
- package/src/providers/model-catalog.ts +34 -0
- package/src/providers/model-intents.ts +10 -5
- package/src/providers/openai/__tests__/chat-completions-output-token-limit.test.ts +141 -0
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +146 -18
- package/src/providers/openai/__tests__/chat-completions-provider-thought-signature.test.ts +613 -0
- package/src/providers/openai/__tests__/google-thought-signature.test.ts +235 -0
- package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +83 -1
- package/src/providers/openai/chat-completions-provider.ts +212 -79
- package/src/providers/openai/codex-models.ts +0 -4
- package/src/providers/openai/google-thought-signature.ts +320 -0
- package/src/providers/opencode/client.test.ts +73 -4
- package/src/providers/opencode/client.ts +60 -3
- package/src/providers/openrouter/client.ts +20 -0
- package/src/providers/outbound-request-sanitize.ts +96 -0
- package/src/providers/retry.ts +30 -19
- package/src/providers/unparseable-tool-args.ts +5 -1
- package/src/runtime/AGENTS.md +3 -1
- package/src/runtime/__tests__/channel-send.test.ts +443 -0
- package/src/runtime/__tests__/desktop-stream-upgrade.test.ts +89 -0
- package/src/runtime/__tests__/host-observe.test.ts +40 -1
- package/src/runtime/__tests__/live-voice-upgrade.test.ts +58 -0
- package/src/runtime/__tests__/runtime-ws-test-utils.ts +72 -0
- package/src/runtime/__tests__/watch-stream-upgrade.test.ts +59 -0
- package/src/runtime/agent-wake.ts +2 -1
- package/src/runtime/assistant-event-hub.ts +21 -3
- package/src/runtime/auth/same-actor.ts +38 -3
- package/src/runtime/channel-send.ts +316 -0
- package/src/runtime/host-observe.ts +11 -1
- package/src/runtime/http-server.ts +160 -151
- package/src/runtime/routes/__tests__/acp-routes.test.ts +159 -4
- package/src/runtime/routes/__tests__/apps-refresh-route.test.ts +205 -0
- package/src/runtime/routes/__tests__/channel-send-routes.test.ts +127 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +108 -1
- package/src/runtime/routes/__tests__/default-provider-routes.test.ts +2 -2
- package/src/runtime/routes/__tests__/dictation-cleanup-guard.test.ts +107 -0
- package/src/runtime/routes/__tests__/mcp-add-default-risk.test.ts +16 -12
- package/src/runtime/routes/acp-routes.test.ts +15 -0
- package/src/runtime/routes/acp-routes.ts +72 -9
- package/src/runtime/routes/app-management-routes.ts +73 -0
- package/src/runtime/routes/app-routes.ts +1 -1
- package/src/runtime/routes/channel-send-routes.ts +85 -0
- package/src/runtime/routes/conversation-list-routes.ts +14 -3
- package/src/runtime/routes/conversation-query-routes.ts +40 -0
- package/src/runtime/routes/conversation-routes.ts +16 -3
- package/src/runtime/routes/credential-routes.ts +19 -10
- package/src/runtime/routes/desktop-setup-routes.test.ts +74 -0
- package/src/runtime/routes/desktop-setup-routes.ts +35 -0
- package/src/runtime/routes/diagnostics-routes.ts +61 -4
- package/src/runtime/routes/events-routes.ts +4 -0
- package/src/runtime/routes/host-cu-routes.ts +6 -0
- package/src/runtime/routes/index.ts +4 -2
- package/src/runtime/routes/log-export-routes.ts +2 -0
- package/src/runtime/routes/mcp-auth-routes.ts +39 -47
- package/src/runtime/routes/migration-routes.ts +6 -20
- package/src/runtime/routes/oauth-commands-routes.ts +36 -0
- package/src/runtime/routes/platform-routes.ts +18 -15
- package/src/runtime/routes/workspace-routes.test.ts +55 -26
- package/src/runtime/routes/workspace-routes.ts +46 -63
- package/src/runtime/routes/workspace-tree-walk.test.ts +244 -0
- package/src/runtime/routes/workspace-tree-walk.ts +240 -0
- package/src/runtime/routes/workspace-utils.ts +17 -0
- package/src/runtime/services/__tests__/conversation-serializer.test.ts +28 -0
- package/src/runtime/services/conversation-serializer.ts +16 -3
- package/src/runtime/sync/resource-sync-events.ts +2 -1
- package/src/schedule/run-script.ts +2 -1
- package/src/schedule/schedule-store.ts +9 -2
- package/src/schedule/worker.ts +1 -1
- package/src/security/__tests__/ces-http-record-backend.test.ts +137 -0
- package/src/security/ces-http-record-backend.ts +120 -0
- package/src/security/ces-rpc-record-backend.ts +7 -0
- package/src/security/untrusted-content.ts +3 -1
- package/src/skills/__tests__/workspace-skill-md-signature.test.ts +60 -0
- package/src/skills/workspace-skill-md-signature.ts +50 -0
- package/src/subagent/consult-context.ts +12 -25
- package/src/tools/__tests__/side-effect-abort-guard.test.ts +1 -0
- package/src/tools/acp/list-agents.test.ts +3 -1
- package/src/tools/acp/spawn.test.ts +146 -11
- package/src/tools/acp/spawn.ts +21 -3
- package/src/tools/acp/status.test.ts +94 -0
- package/src/tools/acp/status.ts +40 -3
- package/src/tools/app-control/skill-proxy-bridge.ts +2 -1
- package/src/tools/browser/browser-execution.ts +1 -1
- package/src/tools/browser/cdp-client/accessibility-snapshot.ts +9 -3
- package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +2 -1
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +3 -2
- package/src/tools/browser/network-recorder.ts +2 -1
- package/src/tools/capability-offer.test.ts +22 -2
- package/src/tools/capability-offer.ts +16 -1
- package/src/tools/computer-use/definitions.ts +121 -2
- package/src/tools/computer-use/skill-proxy-bridge.ts +2 -1
- package/src/tools/credentials/metadata-store.ts +100 -0
- package/src/tools/filesystem/search.ts +2 -1
- package/src/tools/host-filesystem/edit.test.ts +2 -1
- package/src/tools/host-filesystem/edit.ts +2 -2
- package/src/tools/host-filesystem/read.test.ts +2 -1
- package/src/tools/host-filesystem/read.ts +2 -2
- package/src/tools/host-filesystem/transfer.test.ts +2 -1
- package/src/tools/host-filesystem/transfer.ts +2 -2
- package/src/tools/host-filesystem/write.test.ts +2 -1
- package/src/tools/host-filesystem/write.ts +2 -2
- package/src/tools/host-terminal/host-shell.test.ts +16 -0
- package/src/tools/host-terminal/host-shell.ts +4 -20
- package/src/tools/network/web-search.ts +6 -1
- package/src/tools/policy-context.ts +6 -7
- package/src/tools/skills/scaffold-managed.ts +61 -3
- package/src/tools/subagent/spawn.ts +5 -8
- package/src/tools/terminal/__tests__/sanitized-bash.test.ts +57 -0
- package/src/tools/terminal/sanitized-bash.ts +105 -0
- package/src/tools/watch/watch-retro-report.ts +0 -8
- package/src/types/host-capabilities.ts +5 -3
- package/src/util/__tests__/language-subtag.test.ts +23 -1
- package/src/util/ca-bundle.ts +13 -0
- package/src/util/language-subtag.ts +16 -0
- package/src/util/platform.ts +8 -0
- package/src/util/unicode.ts +15 -1
- package/src/watch/watch-timeline.ts +2 -2
- package/src/workflows/run-manager.ts +2 -1
- package/src/workspace/git-porcelain.ts +66 -0
- package/src/workspace/git-service.ts +6 -55
- package/src/workspace/migrations/154-repair-retired-codex-gpt-5-4-model-ids.ts +258 -0
- package/src/workspace/migrations/155-move-front-model-config-to-voice.ts +78 -0
- package/src/workspace/migrations/156-extract-workspace-mcp-json.ts +214 -0
- package/src/workspace/migrations/__tests__/155-move-front-model-config-to-voice.test.ts +102 -0
- package/src/workspace/migrations/__tests__/156-extract-workspace-mcp-json.test.ts +199 -0
- package/src/workspace/migrations/registry.ts +6 -0
- package/src/__tests__/app-source-watcher.test.ts +0 -218
- package/src/__tests__/config-watcher-skill-reseed.test.ts +0 -359
- package/src/__tests__/speaker-identification.test.ts +0 -56
- package/src/calls/speaker-identification.ts +0 -233
- package/src/daemon/app-source-watcher.ts +0 -218
- package/src/messaging/providers/whatsapp/client.ts +0 -29
- package/src/runtime/routes/debug-bash-routes.ts +0 -173
- package/src/util/fs-watcher-error.ts +0 -36
- /package/src/{live-voice → calls}/progress-phrases.ts +0 -0
|
@@ -8,9 +8,16 @@
|
|
|
8
8
|
|
|
9
9
|
import { describe, expect, mock, test } from "bun:test";
|
|
10
10
|
|
|
11
|
-
import type {
|
|
11
|
+
import type {
|
|
12
|
+
AuthMethod,
|
|
13
|
+
InitializeResponse,
|
|
14
|
+
SessionConfigOption,
|
|
15
|
+
} from "@agentclientprotocol/sdk";
|
|
16
|
+
import * as acp from "@agentclientprotocol/sdk";
|
|
12
17
|
|
|
13
18
|
import { AcpAgentProcess } from "../agent-process.js";
|
|
19
|
+
import { AcpConfigOptionRefusedError } from "../types.js";
|
|
20
|
+
import { modelOption } from "./helpers/acp-model-option.js";
|
|
14
21
|
|
|
15
22
|
function makeProcess(): AcpAgentProcess {
|
|
16
23
|
return new AcpAgentProcess(
|
|
@@ -160,6 +167,139 @@ describe("AcpAgentProcess loadSession/resumeSession", () => {
|
|
|
160
167
|
});
|
|
161
168
|
});
|
|
162
169
|
|
|
170
|
+
describe("AcpAgentProcess config options", () => {
|
|
171
|
+
/** Injects a connection whose session calls report `configOptions`. */
|
|
172
|
+
function stubSessionResponses(
|
|
173
|
+
proc: AcpAgentProcess,
|
|
174
|
+
configOptions: SessionConfigOption[] | null | undefined,
|
|
175
|
+
): void {
|
|
176
|
+
(proc as unknown as { connection: unknown }).connection = {
|
|
177
|
+
newSession: () =>
|
|
178
|
+
Promise.resolve({ sessionId: "session-1", configOptions }),
|
|
179
|
+
loadSession: () => Promise.resolve({ configOptions }),
|
|
180
|
+
resumeSession: () => Promise.resolve({ configOptions }),
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
test("createSession returns the reported options", async () => {
|
|
185
|
+
const proc = makeProcess();
|
|
186
|
+
const options = [modelOption("sonnet")];
|
|
187
|
+
stubSessionResponses(proc, options);
|
|
188
|
+
|
|
189
|
+
await expect(proc.createSession("/tmp/project")).resolves.toEqual({
|
|
190
|
+
sessionId: "session-1",
|
|
191
|
+
configOptions: options,
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
test("loadSession returns the reported options", async () => {
|
|
196
|
+
const proc = makeProcess();
|
|
197
|
+
const options = [modelOption("sonnet")];
|
|
198
|
+
stubSessionResponses(proc, options);
|
|
199
|
+
|
|
200
|
+
await expect(
|
|
201
|
+
proc.loadSession("session-1", "/tmp/project"),
|
|
202
|
+
).resolves.toEqual({ configOptions: options });
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
test("resumeSession returns the reported options", async () => {
|
|
206
|
+
const proc = makeProcess();
|
|
207
|
+
const options = [modelOption("opus")];
|
|
208
|
+
stubSessionResponses(proc, options);
|
|
209
|
+
|
|
210
|
+
await expect(
|
|
211
|
+
proc.resumeSession("session-1", "/tmp/project"),
|
|
212
|
+
).resolves.toEqual({ configOptions: options });
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
test("a null or absent configOptions normalizes to an empty array", async () => {
|
|
216
|
+
const created = makeProcess();
|
|
217
|
+
stubSessionResponses(created, null);
|
|
218
|
+
await expect(created.createSession("/tmp/project")).resolves.toEqual({
|
|
219
|
+
sessionId: "session-1",
|
|
220
|
+
configOptions: [],
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
const loaded = makeProcess();
|
|
224
|
+
stubSessionResponses(loaded, null);
|
|
225
|
+
await expect(
|
|
226
|
+
loaded.loadSession("session-1", "/tmp/project"),
|
|
227
|
+
).resolves.toEqual({ configOptions: [] });
|
|
228
|
+
|
|
229
|
+
const resumed = makeProcess();
|
|
230
|
+
stubSessionResponses(resumed, undefined);
|
|
231
|
+
await expect(
|
|
232
|
+
resumed.resumeSession("session-1", "/tmp/project"),
|
|
233
|
+
).resolves.toEqual({ configOptions: [] });
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
test("setConfigOption forwards the value and returns the refreshed set", async () => {
|
|
237
|
+
const proc = makeProcess();
|
|
238
|
+
const calls: unknown[] = [];
|
|
239
|
+
const refreshed = [modelOption("opus")];
|
|
240
|
+
(proc as unknown as { connection: unknown }).connection = {
|
|
241
|
+
setSessionConfigOption: (params: unknown) => {
|
|
242
|
+
calls.push(params);
|
|
243
|
+
return Promise.resolve({ configOptions: refreshed });
|
|
244
|
+
},
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
await expect(
|
|
248
|
+
proc.setConfigOption("session-1", "model", "opus"),
|
|
249
|
+
).resolves.toEqual(refreshed);
|
|
250
|
+
|
|
251
|
+
expect(calls).toEqual([
|
|
252
|
+
{ sessionId: "session-1", configId: "model", value: "opus" },
|
|
253
|
+
]);
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
test("setConfigOption sends the boolean request variant", async () => {
|
|
257
|
+
const proc = makeProcess();
|
|
258
|
+
const calls: unknown[] = [];
|
|
259
|
+
const refreshed = [modelOption("opus")];
|
|
260
|
+
(proc as unknown as { connection: unknown }).connection = {
|
|
261
|
+
setSessionConfigOption: (params: unknown) => {
|
|
262
|
+
calls.push(params);
|
|
263
|
+
return Promise.resolve({ configOptions: refreshed });
|
|
264
|
+
},
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
await proc.setConfigOption("session-1", "thinking", true);
|
|
268
|
+
|
|
269
|
+
expect(calls).toEqual([
|
|
270
|
+
{
|
|
271
|
+
sessionId: "session-1",
|
|
272
|
+
configId: "thinking",
|
|
273
|
+
type: "boolean",
|
|
274
|
+
value: true,
|
|
275
|
+
},
|
|
276
|
+
]);
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
test("setConfigOption omits the discriminator for string values", async () => {
|
|
280
|
+
const proc = makeProcess();
|
|
281
|
+
const calls: unknown[] = [];
|
|
282
|
+
(proc as unknown as { connection: unknown }).connection = {
|
|
283
|
+
setSessionConfigOption: (params: unknown) => {
|
|
284
|
+
calls.push(params);
|
|
285
|
+
return Promise.resolve({ configOptions: [modelOption("opus")] });
|
|
286
|
+
},
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
await proc.setConfigOption("session-1", "model", "opus");
|
|
290
|
+
|
|
291
|
+
expect(calls[0]).not.toHaveProperty("type");
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
test("setConfigOption throws when the process is not spawned", async () => {
|
|
295
|
+
const proc = makeProcess();
|
|
296
|
+
|
|
297
|
+
await expect(
|
|
298
|
+
proc.setConfigOption("session-1", "model", "opus"),
|
|
299
|
+
).rejects.toThrow('ACP agent "test-agent" is not spawned');
|
|
300
|
+
});
|
|
301
|
+
});
|
|
302
|
+
|
|
163
303
|
describe("AcpAgentProcess auth_required retry", () => {
|
|
164
304
|
// spawnedEnv is injected the way spawn() builds it ({ ...process.env,
|
|
165
305
|
// ...config.env }), so the advertised var names use a VELLUM_TEST_ prefix
|
|
@@ -196,17 +336,19 @@ describe("AcpAgentProcess auth_required retry", () => {
|
|
|
196
336
|
* initialize() so the advertised authMethods are captured.
|
|
197
337
|
*/
|
|
198
338
|
async function setupAuthProcess(options: {
|
|
339
|
+
command?: string;
|
|
199
340
|
env?: Record<string, string>;
|
|
200
341
|
authMethods?: AuthMethod[];
|
|
201
342
|
/** Rejections to throw from successive newSession calls before succeeding. */
|
|
202
343
|
newSessionRejections?: unknown[];
|
|
203
344
|
loadSessionRejections?: unknown[];
|
|
204
345
|
resumeSessionRejections?: unknown[];
|
|
346
|
+
setConfigOptionRejections?: unknown[];
|
|
205
347
|
promptRejections?: unknown[];
|
|
206
348
|
}) {
|
|
207
349
|
const proc = new AcpAgentProcess(
|
|
208
350
|
"codex",
|
|
209
|
-
{ command: "echo", args: [], env: options.env },
|
|
351
|
+
{ command: options.command ?? "echo", args: [], env: options.env },
|
|
210
352
|
() => {
|
|
211
353
|
throw new Error("client factory should not be called in this test");
|
|
212
354
|
},
|
|
@@ -234,6 +376,10 @@ describe("AcpAgentProcess auth_required retry", () => {
|
|
|
234
376
|
options.resumeSessionRejections ?? [],
|
|
235
377
|
{},
|
|
236
378
|
);
|
|
379
|
+
const setSessionConfigOption = failThenSucceed(
|
|
380
|
+
options.setConfigOptionRejections ?? [],
|
|
381
|
+
{ configOptions: [] },
|
|
382
|
+
);
|
|
237
383
|
const prompt = failThenSucceed(options.promptRejections ?? [], {
|
|
238
384
|
stopReason: "end_turn",
|
|
239
385
|
});
|
|
@@ -252,6 +398,7 @@ describe("AcpAgentProcess auth_required retry", () => {
|
|
|
252
398
|
newSession,
|
|
253
399
|
loadSession,
|
|
254
400
|
resumeSession,
|
|
401
|
+
setSessionConfigOption,
|
|
255
402
|
prompt,
|
|
256
403
|
authenticate,
|
|
257
404
|
};
|
|
@@ -264,18 +411,112 @@ describe("AcpAgentProcess auth_required retry", () => {
|
|
|
264
411
|
newSession,
|
|
265
412
|
loadSession,
|
|
266
413
|
resumeSession,
|
|
414
|
+
setSessionConfigOption,
|
|
267
415
|
prompt,
|
|
268
416
|
authenticate,
|
|
269
417
|
internals,
|
|
270
418
|
};
|
|
271
419
|
}
|
|
272
420
|
|
|
421
|
+
// An adapter that throws a plain Error reaches the client as a generic
|
|
422
|
+
// "Internal error" carrying the real sentence in `data.details`, which is
|
|
423
|
+
// the shape these fixtures reproduce.
|
|
424
|
+
test("setConfigOption surfaces the adapter's error answer as a refusal", async () => {
|
|
425
|
+
const { proc } = await setupAuthProcess({
|
|
426
|
+
setConfigOptionRejections: [
|
|
427
|
+
new acp.RequestError(-32603, "Internal error", {
|
|
428
|
+
details: "Invalid value for config option model: nope",
|
|
429
|
+
}),
|
|
430
|
+
],
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
const refusal = await proc
|
|
434
|
+
.setConfigOption("session-1", "model", "nope")
|
|
435
|
+
.catch((err: unknown) => err);
|
|
436
|
+
|
|
437
|
+
expect(refusal).toBeInstanceOf(AcpConfigOptionRefusedError);
|
|
438
|
+
// The adapter's sentence, not the message the SDK framed it in.
|
|
439
|
+
expect((refusal as Error).message).toBe(
|
|
440
|
+
"Invalid value for config option model: nope",
|
|
441
|
+
);
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
test("setConfigOption serializes a payload that names no reason", async () => {
|
|
445
|
+
const { proc } = await setupAuthProcess({
|
|
446
|
+
setConfigOptionRejections: [
|
|
447
|
+
new acp.RequestError(-32603, "Internal error", { code: 7 }),
|
|
448
|
+
],
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
await expect(
|
|
452
|
+
proc.setConfigOption("session-1", "model", "nope"),
|
|
453
|
+
).rejects.toThrow('{"code":7}');
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
test("setConfigOption falls back to the message when there is no payload", async () => {
|
|
457
|
+
const { proc } = await setupAuthProcess({
|
|
458
|
+
setConfigOptionRejections: [
|
|
459
|
+
new acp.RequestError(-32603, "Session not found"),
|
|
460
|
+
],
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
await expect(
|
|
464
|
+
proc.setConfigOption("session-1", "model", "nope"),
|
|
465
|
+
).rejects.toThrow("Session not found");
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
test.each(["claude-agent-acp", "/usr/local/bin/claude-agent-acp"])(
|
|
469
|
+
"setConfigOption leaves Claude's message-shaped auth failure untyped (%s)",
|
|
470
|
+
async (command) => {
|
|
471
|
+
const expired = new acp.RequestError(-32603, "Internal error", {
|
|
472
|
+
details: "Not logged in",
|
|
473
|
+
});
|
|
474
|
+
const { proc } = await setupAuthProcess({
|
|
475
|
+
command,
|
|
476
|
+
setConfigOptionRejections: [expired],
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
await expect(
|
|
480
|
+
proc.setConfigOption("session-1", "model", "opus"),
|
|
481
|
+
).rejects.toBe(expired);
|
|
482
|
+
},
|
|
483
|
+
);
|
|
484
|
+
|
|
485
|
+
test("setConfigOption reads Claude's login wording from another adapter as a refusal", async () => {
|
|
486
|
+
const { proc } = await setupAuthProcess({
|
|
487
|
+
command: "codex-acp",
|
|
488
|
+
setConfigOptionRejections: [
|
|
489
|
+
new acp.RequestError(-32603, "Internal error", {
|
|
490
|
+
details: "Not logged in",
|
|
491
|
+
}),
|
|
492
|
+
],
|
|
493
|
+
});
|
|
494
|
+
|
|
495
|
+
const refusal = await proc
|
|
496
|
+
.setConfigOption("session-1", "model", "gpt-5")
|
|
497
|
+
.catch((err: unknown) => err);
|
|
498
|
+
|
|
499
|
+
expect(refusal).toBeInstanceOf(AcpConfigOptionRefusedError);
|
|
500
|
+
expect((refusal as Error).message).toBe("Not logged in");
|
|
501
|
+
});
|
|
502
|
+
|
|
503
|
+
test("setConfigOption leaves a transport failure untyped", async () => {
|
|
504
|
+
const closed = new Error("ACP connection closed");
|
|
505
|
+
const { proc } = await setupAuthProcess({
|
|
506
|
+
setConfigOptionRejections: [closed],
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
await expect(
|
|
510
|
+
proc.setConfigOption("session-1", "model", "opus"),
|
|
511
|
+
).rejects.toBe(closed);
|
|
512
|
+
});
|
|
513
|
+
|
|
273
514
|
test("createSession does not authenticate when newSession succeeds", async () => {
|
|
274
515
|
const { proc, newSession, authenticate } = await setupAuthProcess({
|
|
275
516
|
env: { [OPENAI_VAR]: "sk-test" },
|
|
276
517
|
});
|
|
277
518
|
|
|
278
|
-
const sessionId = await proc.createSession("/tmp/project");
|
|
519
|
+
const { sessionId } = await proc.createSession("/tmp/project");
|
|
279
520
|
|
|
280
521
|
expect(sessionId).toBe("session-1");
|
|
281
522
|
expect(newSession).toHaveBeenCalledTimes(1);
|
|
@@ -288,7 +529,7 @@ describe("AcpAgentProcess auth_required retry", () => {
|
|
|
288
529
|
newSessionRejections: [authRequiredError],
|
|
289
530
|
});
|
|
290
531
|
|
|
291
|
-
const sessionId = await proc.createSession("/tmp/project");
|
|
532
|
+
const { sessionId } = await proc.createSession("/tmp/project");
|
|
292
533
|
|
|
293
534
|
expect(sessionId).toBe("session-1");
|
|
294
535
|
expect(authenticate).toHaveBeenCalledTimes(1);
|
|
@@ -387,6 +628,19 @@ describe("AcpAgentProcess auth_required retry", () => {
|
|
|
387
628
|
expect(resumeSession).toHaveBeenCalledTimes(2);
|
|
388
629
|
});
|
|
389
630
|
|
|
631
|
+
test("setConfigOption authenticates and retries on auth_required", async () => {
|
|
632
|
+
const { proc, setSessionConfigOption, authenticate } =
|
|
633
|
+
await setupAuthProcess({
|
|
634
|
+
env: { [OPENAI_VAR]: "sk-test" },
|
|
635
|
+
setConfigOptionRejections: [authRequiredError],
|
|
636
|
+
});
|
|
637
|
+
|
|
638
|
+
await proc.setConfigOption("session-1", "model", "opus");
|
|
639
|
+
|
|
640
|
+
expect(authenticate).toHaveBeenCalledWith({ methodId: "openai-api-key" });
|
|
641
|
+
expect(setSessionConfigOption).toHaveBeenCalledTimes(2);
|
|
642
|
+
});
|
|
643
|
+
|
|
390
644
|
test("prompt authenticates and retries on auth_required, returning the response", async () => {
|
|
391
645
|
const { proc, prompt, authenticate } = await setupAuthProcess({
|
|
392
646
|
env: { [OPENAI_VAR]: "sk-test" },
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
|
|
7
7
|
import { describe, expect, test } from "bun:test";
|
|
8
8
|
|
|
9
|
+
import { RequestError } from "@agentclientprotocol/sdk";
|
|
10
|
+
|
|
9
11
|
import { AcpAuthRequiredEventSchema } from "../../api/events/acp-auth-required.js";
|
|
10
12
|
import { AcpSessionErrorEventSchema } from "../../api/events/acp-session-error.js";
|
|
11
13
|
import {
|
|
@@ -15,6 +17,7 @@ import {
|
|
|
15
17
|
CLAUDE_ACP_COMMAND,
|
|
16
18
|
isAcpAuthRequired,
|
|
17
19
|
isClaudeAuthFailureMessage,
|
|
20
|
+
requestErrorReason,
|
|
18
21
|
} from "../auth-required.js";
|
|
19
22
|
|
|
20
23
|
describe("isAcpAuthRequired", () => {
|
|
@@ -89,6 +92,99 @@ describe("isClaudeAuthFailureMessage", () => {
|
|
|
89
92
|
});
|
|
90
93
|
});
|
|
91
94
|
|
|
95
|
+
describe("requestErrorReason", () => {
|
|
96
|
+
test("decodes the sentence the SDK moved into the payload", () => {
|
|
97
|
+
const rejection = new RequestError(-32603, "Internal error", {
|
|
98
|
+
details: "Failed to authenticate. Please run /login",
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
expect(requestErrorReason(rejection)).toBe(
|
|
102
|
+
"Failed to authenticate. Please run /login",
|
|
103
|
+
);
|
|
104
|
+
// The point of decoding: the generic message never matches.
|
|
105
|
+
expect(isClaudeAuthFailureMessage(rejection.message)).toBe(false);
|
|
106
|
+
expect(isClaudeAuthFailureMessage(requestErrorReason(rejection))).toBe(
|
|
107
|
+
true,
|
|
108
|
+
);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
test("reads the message off a rejection that carries no payload", () => {
|
|
112
|
+
// Straight off the wire: a plain object, not an instance of any class we
|
|
113
|
+
// control, and with the auth text where the SDK leaves a framed one.
|
|
114
|
+
const rejection = {
|
|
115
|
+
code: -32603,
|
|
116
|
+
message: "Failed to authenticate. Please run /login",
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
expect(requestErrorReason(rejection)).toBe(
|
|
120
|
+
"Failed to authenticate. Please run /login",
|
|
121
|
+
);
|
|
122
|
+
expect(isClaudeAuthFailureMessage(requestErrorReason(rejection))).toBe(
|
|
123
|
+
true,
|
|
124
|
+
);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
test("keeps the rejection's own message when the payload names no reason", () => {
|
|
128
|
+
expect(requestErrorReason(new RequestError(-32603, "boom", { c: 7 }))).toBe(
|
|
129
|
+
"boom",
|
|
130
|
+
);
|
|
131
|
+
expect(
|
|
132
|
+
requestErrorReason(
|
|
133
|
+
RequestError.methodNotFound("session/set_config_option"),
|
|
134
|
+
),
|
|
135
|
+
).toBe('"Method not found": session/set_config_option');
|
|
136
|
+
expect(requestErrorReason(new Error("Not logged in"))).toBe(
|
|
137
|
+
"Not logged in",
|
|
138
|
+
);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
test("keeps a specific rejection message ahead of supplemental details", () => {
|
|
142
|
+
expect(
|
|
143
|
+
requestErrorReason(
|
|
144
|
+
new RequestError(-32603, "Model unavailable", {
|
|
145
|
+
details: "The selected deployment is temporarily unavailable",
|
|
146
|
+
}),
|
|
147
|
+
),
|
|
148
|
+
).toBe("Model unavailable");
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
test("serializes the payload behind a bare Internal error that names no reason", () => {
|
|
152
|
+
expect(
|
|
153
|
+
requestErrorReason(new RequestError(-32603, "Internal error", { c: 7 })),
|
|
154
|
+
).toBe('{"c":7}');
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
test("serializes the payload behind a generic Invalid params", () => {
|
|
158
|
+
// How the agent-side SDK answers a request its schema rejects.
|
|
159
|
+
expect(
|
|
160
|
+
requestErrorReason(
|
|
161
|
+
RequestError.invalidParams({ _errors: ["model: Invalid option"] }),
|
|
162
|
+
),
|
|
163
|
+
).toBe('{"_errors":["model: Invalid option"]}');
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
test("decodes claude-agent-acp's prompt-time 401 from its message", () => {
|
|
167
|
+
// RequestError.internalError({ errorKind }, cliText) as the adapter raises
|
|
168
|
+
// it: the CLI's text rides the message and the payload only names a kind.
|
|
169
|
+
const rejection = new RequestError(
|
|
170
|
+
-32603,
|
|
171
|
+
"Internal error: Failed to authenticate. API Error: 401 OAuth access token has expired.",
|
|
172
|
+
{ errorKind: "authentication_failed" },
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
expect(requestErrorReason(rejection)).toBe(rejection.message);
|
|
176
|
+
expect(isClaudeAuthFailureMessage(requestErrorReason(rejection))).toBe(
|
|
177
|
+
true,
|
|
178
|
+
);
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
test("stringifies a rejection that is not an object at all", () => {
|
|
182
|
+
expect(requestErrorReason("plain string")).toBe("plain string");
|
|
183
|
+
expect(requestErrorReason(-32603)).toBe("-32603");
|
|
184
|
+
expect(requestErrorReason(null)).toBe("null");
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
|
|
92
188
|
describe("AcpAuthRequiredError", () => {
|
|
93
189
|
test("is an Error that carries the agent id and its message", () => {
|
|
94
190
|
const err = new AcpAuthRequiredError("claude", "needs a reconnect");
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
2
|
|
|
3
|
-
import type {
|
|
3
|
+
import type {
|
|
4
|
+
SessionConfigOption,
|
|
5
|
+
SessionNotification,
|
|
6
|
+
} from "@agentclientprotocol/sdk";
|
|
4
7
|
|
|
5
8
|
import type { AssistantEvent } from "../../api/index.js";
|
|
6
9
|
import { VellumAcpClientHandler } from "../client-handler.js";
|
|
10
|
+
import { modelOption } from "./helpers/acp-model-option.js";
|
|
7
11
|
|
|
8
12
|
const ACP_SESSION_ID = "acp-session-abc";
|
|
9
13
|
const PARENT_CONVERSATION_ID = "conv-xyz";
|
|
@@ -11,16 +15,21 @@ const PARENT_CONVERSATION_ID = "conv-xyz";
|
|
|
11
15
|
function makeHandler(): {
|
|
12
16
|
handler: VellumAcpClientHandler;
|
|
13
17
|
sent: AssistantEvent[];
|
|
18
|
+
configOptionUpdates: SessionConfigOption[][];
|
|
14
19
|
} {
|
|
15
20
|
const sent: AssistantEvent[] = [];
|
|
21
|
+
const configOptionUpdates: SessionConfigOption[][] = [];
|
|
16
22
|
const handler = new VellumAcpClientHandler(
|
|
17
23
|
ACP_SESSION_ID,
|
|
18
24
|
(msg) => {
|
|
19
25
|
sent.push(msg);
|
|
20
26
|
},
|
|
21
27
|
PARENT_CONVERSATION_ID,
|
|
28
|
+
(configOptions) => {
|
|
29
|
+
configOptionUpdates.push(configOptions);
|
|
30
|
+
},
|
|
22
31
|
);
|
|
23
|
-
return { handler, sent };
|
|
32
|
+
return { handler, sent, configOptionUpdates };
|
|
24
33
|
}
|
|
25
34
|
|
|
26
35
|
describe("VellumAcpClientHandler.sessionUpdate", () => {
|
|
@@ -782,3 +791,82 @@ describe("VellumAcpClientHandler seq + enriched fields", () => {
|
|
|
782
791
|
expect(msg.toolTitle).toContain("<redacted");
|
|
783
792
|
});
|
|
784
793
|
});
|
|
794
|
+
|
|
795
|
+
describe("VellumAcpClientHandler config_option_update", () => {
|
|
796
|
+
const MODEL_OPTION = modelOption("sonnet");
|
|
797
|
+
|
|
798
|
+
const THOUGHT_OPTION: SessionConfigOption = {
|
|
799
|
+
type: "boolean",
|
|
800
|
+
id: "thinking",
|
|
801
|
+
name: "Extended thinking",
|
|
802
|
+
currentValue: true,
|
|
803
|
+
};
|
|
804
|
+
|
|
805
|
+
function configOptionUpdate(
|
|
806
|
+
configOptions: SessionConfigOption[],
|
|
807
|
+
): SessionNotification {
|
|
808
|
+
return {
|
|
809
|
+
sessionId: ACP_SESSION_ID,
|
|
810
|
+
update: { sessionUpdate: "config_option_update", configOptions },
|
|
811
|
+
};
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
test("invokes the callback with the full option array", async () => {
|
|
815
|
+
const { handler, sent, configOptionUpdates } = makeHandler();
|
|
816
|
+
|
|
817
|
+
await handler.sessionUpdate(
|
|
818
|
+
configOptionUpdate([MODEL_OPTION, THOUGHT_OPTION]),
|
|
819
|
+
);
|
|
820
|
+
|
|
821
|
+
expect(configOptionUpdates).toEqual([[MODEL_OPTION, THOUGHT_OPTION]]);
|
|
822
|
+
expect(sent).toHaveLength(0);
|
|
823
|
+
});
|
|
824
|
+
|
|
825
|
+
test("callback fires during replay suppression", async () => {
|
|
826
|
+
const { handler, sent, configOptionUpdates } = makeHandler();
|
|
827
|
+
|
|
828
|
+
handler.beginReplaySuppression();
|
|
829
|
+
await handler.sessionUpdate(configOptionUpdate([MODEL_OPTION]));
|
|
830
|
+
|
|
831
|
+
expect(configOptionUpdates).toEqual([[MODEL_OPTION]]);
|
|
832
|
+
expect(sent).toHaveLength(0);
|
|
833
|
+
});
|
|
834
|
+
|
|
835
|
+
test("a handler without the callback tolerates the update", async () => {
|
|
836
|
+
const sent: AssistantEvent[] = [];
|
|
837
|
+
const handler = new VellumAcpClientHandler(
|
|
838
|
+
ACP_SESSION_ID,
|
|
839
|
+
(msg) => {
|
|
840
|
+
sent.push(msg);
|
|
841
|
+
},
|
|
842
|
+
PARENT_CONVERSATION_ID,
|
|
843
|
+
);
|
|
844
|
+
|
|
845
|
+
await handler.sessionUpdate(configOptionUpdate([MODEL_OPTION]));
|
|
846
|
+
|
|
847
|
+
expect(sent).toHaveLength(0);
|
|
848
|
+
});
|
|
849
|
+
|
|
850
|
+
test("the remaining unhandled update types stay unforwarded", async () => {
|
|
851
|
+
const { handler, sent, configOptionUpdates } = makeHandler();
|
|
852
|
+
|
|
853
|
+
await handler.sessionUpdate({
|
|
854
|
+
sessionId: ACP_SESSION_ID,
|
|
855
|
+
update: {
|
|
856
|
+
sessionUpdate: "available_commands_update",
|
|
857
|
+
availableCommands: [{ name: "create_plan", description: "Plan work" }],
|
|
858
|
+
},
|
|
859
|
+
});
|
|
860
|
+
await handler.sessionUpdate({
|
|
861
|
+
sessionId: ACP_SESSION_ID,
|
|
862
|
+
update: { sessionUpdate: "current_mode_update", currentModeId: "ask" },
|
|
863
|
+
});
|
|
864
|
+
await handler.sessionUpdate({
|
|
865
|
+
sessionId: ACP_SESSION_ID,
|
|
866
|
+
update: { sessionUpdate: "session_info_update", title: "Refactor" },
|
|
867
|
+
});
|
|
868
|
+
|
|
869
|
+
expect(sent).toHaveLength(0);
|
|
870
|
+
expect(configOptionUpdates).toHaveLength(0);
|
|
871
|
+
});
|
|
872
|
+
});
|
|
@@ -0,0 +1,51 @@
|
|
|
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
|
+
];
|
|
@@ -66,7 +66,11 @@ function buildSessionWithFakeProcess(opts: {
|
|
|
66
66
|
kill: () => {},
|
|
67
67
|
spawn: () => {},
|
|
68
68
|
initialize: () => Promise.resolve(),
|
|
69
|
-
createSession: () =>
|
|
69
|
+
createSession: () =>
|
|
70
|
+
Promise.resolve({
|
|
71
|
+
sessionId: opts.protocolSessionId,
|
|
72
|
+
configOptions: [],
|
|
73
|
+
}),
|
|
70
74
|
cancel: () => Promise.resolve(),
|
|
71
75
|
markStderr: () => 0,
|
|
72
76
|
stderrSince: () => "",
|