@vellumai/assistant 0.12.1 → 0.12.2-staging.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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/node_modules/@vellumai/slack-text/src/index.ts +13 -8
- package/node_modules/@vellumai/slack-text/src/label-resolution-entities.test.ts +95 -0
- package/openapi.yaml +360 -51
- 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-runtime-assembly.test.ts +28 -0
- 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-point-at-budget.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-point-at-capability.test.ts +1 -0
- 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 +250 -3
- package/src/__tests__/credential-security-invariants.test.ts +5 -3
- package/src/__tests__/cu-unified-flow.test.ts +14 -4
- 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 +596 -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 +168 -0
- package/src/__tests__/oauth-provider-profiles.test.ts +18 -0
- package/src/__tests__/oauth-provider-serializer.test.ts +22 -0
- package/src/__tests__/oauth-providers-routes.test.ts +1 -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__/secret-routes-acp-guard.test.ts +59 -1
- 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 +70 -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__/ui-channel-variants.test.ts +1 -56
- 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__/acp-claude-oauth.test.ts +257 -6
- package/src/acp/__tests__/acp-credentials.test.ts +13 -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__/claude-token-refresh.test.ts +257 -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__/prepare-agent-env.test.ts +60 -1
- 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/acp-claude-oauth.ts +328 -14
- package/src/acp/acp-credentials.ts +19 -0
- 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/claude-token-refresh.ts +150 -0
- 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/prepare-agent-env.ts +21 -6
- 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 +120 -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 +56 -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 +155 -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 +92 -72
- package/src/calls/voice-triage-escalate.ts +178 -59
- 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/oauth/status.ts +5 -0
- 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 +29 -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 +33 -13
- package/src/config/loader.ts +2 -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 +18 -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 +20 -7
- package/src/daemon/conversation-skill-tools.ts +13 -31
- package/src/daemon/conversation-store.ts +1 -1
- package/src/daemon/conversation-surfaces.ts +21 -10
- package/src/daemon/conversation-tool-setup.ts +45 -59
- 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 +224 -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-agent-turn.test.ts +121 -0
- package/src/live-voice/__tests__/live-voice-events.test.ts +8 -7
- package/src/live-voice/__tests__/live-voice-metrics.test.ts +0 -3
- package/src/live-voice/__tests__/live-voice-progress.test.ts +94 -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__/protocol.test.ts +39 -0
- package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +13 -69
- package/src/live-voice/__tests__/session-controls.test.ts +79 -0
- package/src/live-voice/live-voice-photo.ts +25 -6
- package/src/live-voice/live-voice-session.ts +297 -699
- package/src/live-voice/protocol.ts +60 -2
- package/src/live-voice/session-controls.ts +111 -0
- 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 +149 -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 +100 -0
- package/src/oauth/connection-resolver.ts +120 -3
- package/src/oauth/provider-serializer.ts +9 -0
- package/src/oauth/scope-utils.ts +24 -0
- package/src/oauth/seed-providers.ts +314 -27
- 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-claude-auth-routes.test.ts +16 -5
- package/src/runtime/routes/__tests__/acp-routes.test.ts +159 -4
- package/src/runtime/routes/__tests__/apps-refresh-route.test.ts +275 -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-claude-auth-routes.ts +11 -3
- 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 +90 -2
- 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 +20 -14
- 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 +50 -0
- package/src/runtime/routes/oauth-providers.ts +7 -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/ui-surface/channel-variants.ts +10 -59
- 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-retro.ts +6 -8
- 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
|
@@ -124,11 +124,22 @@ const spawnMock = mock(
|
|
|
124
124
|
_cwd: string,
|
|
125
125
|
_parentConversationId: string,
|
|
126
126
|
_sendToVellum: (msg: unknown) => void,
|
|
127
|
-
|
|
128
|
-
)
|
|
129
|
-
acpSessionId:
|
|
130
|
-
protocolSessionId:
|
|
131
|
-
|
|
127
|
+
_options?: { parentToolUseId?: string; model?: string },
|
|
128
|
+
): Promise<{
|
|
129
|
+
acpSessionId: string;
|
|
130
|
+
protocolSessionId: string;
|
|
131
|
+
requestedModel?: string;
|
|
132
|
+
effectiveModel?: string;
|
|
133
|
+
modelWarning?: string;
|
|
134
|
+
}> => {
|
|
135
|
+
const requestedModel = _options?.model?.trim() || undefined;
|
|
136
|
+
return {
|
|
137
|
+
acpSessionId: "acp-session-test",
|
|
138
|
+
protocolSessionId: "proto-session-test",
|
|
139
|
+
requestedModel,
|
|
140
|
+
effectiveModel: "opus",
|
|
141
|
+
};
|
|
142
|
+
},
|
|
132
143
|
);
|
|
133
144
|
|
|
134
145
|
// Spread the real module's exports so transitive importers that pull other
|
|
@@ -201,6 +212,8 @@ describe("executeAcpSpawn - happy path", () => {
|
|
|
201
212
|
const payload = JSON.parse(result.content);
|
|
202
213
|
expect(payload.acpSessionId).toBe("acp-session-test");
|
|
203
214
|
expect(payload.status).toBe("running");
|
|
215
|
+
expect(payload.requestedModel).toBeNull();
|
|
216
|
+
expect(payload.effectiveModel).toBe("opus");
|
|
204
217
|
// The real adapter binary is spawned (no `bun x` wrapper).
|
|
205
218
|
const agentConfigArg = spawnMock.mock.calls[0][1] as { command: string };
|
|
206
219
|
expect(agentConfigArg.command).toBe("claude-agent-acp");
|
|
@@ -214,8 +227,10 @@ describe("executeAcpSpawn - happy path", () => {
|
|
|
214
227
|
|
|
215
228
|
expect(result.isError).toBe(false);
|
|
216
229
|
expect(spawnMock).toHaveBeenCalledTimes(1);
|
|
217
|
-
//
|
|
218
|
-
expect(spawnMock.mock.calls[0][6]).
|
|
230
|
+
// The options bag is the 7th positional arg to spawn().
|
|
231
|
+
expect(spawnMock.mock.calls[0][6]).toEqual({
|
|
232
|
+
parentToolUseId: "toolu_abc123",
|
|
233
|
+
});
|
|
219
234
|
});
|
|
220
235
|
|
|
221
236
|
test("default-profile fallback when user config is empty", async () => {
|
|
@@ -272,7 +287,7 @@ describe("executeAcpSpawn — input validation", () => {
|
|
|
272
287
|
expect(result.isError).toBe(true);
|
|
273
288
|
expect(result.content).toContain("claude-agent-acp is not on PATH");
|
|
274
289
|
expect(result.content).toContain(
|
|
275
|
-
"bun add -g @agentclientprotocol/claude-agent-acp",
|
|
290
|
+
"bun add -g @agentclientprotocol/claude-agent-acp@0.75.1",
|
|
276
291
|
);
|
|
277
292
|
expect(execFileMock).not.toHaveBeenCalled();
|
|
278
293
|
expect(spawnMock).not.toHaveBeenCalled();
|
|
@@ -309,7 +324,7 @@ describe("executeAcpSpawn: sandboxed bun auto-install on missing binary", () =>
|
|
|
309
324
|
expect(spawnMock).toHaveBeenCalledTimes(1);
|
|
310
325
|
const payload = JSON.parse(result.content);
|
|
311
326
|
expect(payload.message).toContain(
|
|
312
|
-
"Installed @agentclientprotocol/claude-agent-acp automatically.",
|
|
327
|
+
"Installed @agentclientprotocol/claude-agent-acp@0.75.1 automatically.",
|
|
313
328
|
);
|
|
314
329
|
// The real binary was spawned with cwd = the project dir and token
|
|
315
330
|
// injected (trusted-binary config, no resolution at spawn).
|
|
@@ -330,7 +345,7 @@ describe("executeAcpSpawn: sandboxed bun auto-install on missing binary", () =>
|
|
|
330
345
|
expect(args).toEqual([
|
|
331
346
|
"add",
|
|
332
347
|
"--global",
|
|
333
|
-
"@agentclientprotocol/claude-agent-acp",
|
|
348
|
+
"@agentclientprotocol/claude-agent-acp@0.75.1",
|
|
334
349
|
]);
|
|
335
350
|
});
|
|
336
351
|
|
|
@@ -419,7 +434,7 @@ describe("executeAcpSpawn: sandboxed bun auto-install on missing binary", () =>
|
|
|
419
434
|
expect(result.isError).toBe(true);
|
|
420
435
|
expect(result.content).toContain("claude-agent-acp is not on PATH");
|
|
421
436
|
expect(result.content).toContain(
|
|
422
|
-
"bun add -g @agentclientprotocol/claude-agent-acp",
|
|
437
|
+
"bun add -g @agentclientprotocol/claude-agent-acp@0.75.1",
|
|
423
438
|
);
|
|
424
439
|
expect(result.content).toContain("auto-install failed");
|
|
425
440
|
expect(result.content).toContain("EACCES");
|
|
@@ -458,6 +473,126 @@ describe("executeAcpSpawn — per-agent resume hint", () => {
|
|
|
458
473
|
});
|
|
459
474
|
});
|
|
460
475
|
|
|
476
|
+
describe("executeAcpSpawn - model selection", () => {
|
|
477
|
+
test("threads the requested model into manager.spawn's options", async () => {
|
|
478
|
+
const result = await executeAcpSpawn(
|
|
479
|
+
{ agent: "claude", task: "do something", model: "opus" },
|
|
480
|
+
{ ...makeContext(), toolUseId: "toolu_model" },
|
|
481
|
+
);
|
|
482
|
+
|
|
483
|
+
expect(result.isError).toBe(false);
|
|
484
|
+
expect(spawnMock.mock.calls[0][6]).toEqual({
|
|
485
|
+
parentToolUseId: "toolu_model",
|
|
486
|
+
model: "opus",
|
|
487
|
+
});
|
|
488
|
+
const payload = JSON.parse(result.content);
|
|
489
|
+
expect(payload.requestedModel).toBe("opus");
|
|
490
|
+
expect(payload.effectiveModel).toBe("opus");
|
|
491
|
+
expect(payload.message).toContain(
|
|
492
|
+
'The top-level ACP session reports "opus" as its effective model.',
|
|
493
|
+
);
|
|
494
|
+
});
|
|
495
|
+
|
|
496
|
+
test("reports the manager-normalized requested model", async () => {
|
|
497
|
+
const result = await executeAcpSpawn(
|
|
498
|
+
{ agent: "claude", task: "do something", model: " opus " },
|
|
499
|
+
makeContext(),
|
|
500
|
+
);
|
|
501
|
+
|
|
502
|
+
expect(spawnMock.mock.calls[0][6]).toEqual({
|
|
503
|
+
parentToolUseId: undefined,
|
|
504
|
+
model: " opus ",
|
|
505
|
+
});
|
|
506
|
+
expect(JSON.parse(result.content).requestedModel).toBe("opus");
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
test("a null model is treated as omitted", async () => {
|
|
510
|
+
const result = await executeAcpSpawn(
|
|
511
|
+
{ agent: "claude", task: "do something", model: null },
|
|
512
|
+
makeContext(),
|
|
513
|
+
);
|
|
514
|
+
|
|
515
|
+
expect(result.isError).toBe(false);
|
|
516
|
+
const options = spawnMock.mock.calls[0][6] as { model?: string };
|
|
517
|
+
expect(options.model).toBeUndefined();
|
|
518
|
+
});
|
|
519
|
+
|
|
520
|
+
test("a modelWarning from the spawn is relayed in the result message", async () => {
|
|
521
|
+
spawnMock.mockImplementationOnce(async () => ({
|
|
522
|
+
acpSessionId: "acp-session-test",
|
|
523
|
+
protocolSessionId: "proto-session-test",
|
|
524
|
+
requestedModel: "nope",
|
|
525
|
+
effectiveModel: "opus",
|
|
526
|
+
modelWarning: "Unknown model: nope",
|
|
527
|
+
}));
|
|
528
|
+
|
|
529
|
+
const result = await executeAcpSpawn(
|
|
530
|
+
{ agent: "claude", task: "do something", model: "nope" },
|
|
531
|
+
makeContext(),
|
|
532
|
+
);
|
|
533
|
+
|
|
534
|
+
expect(result.isError).toBe(false);
|
|
535
|
+
const payload = JSON.parse(result.content);
|
|
536
|
+
expect(payload.message).toContain(
|
|
537
|
+
"The requested model was not applied: Unknown model: nope",
|
|
538
|
+
);
|
|
539
|
+
expect(payload.requestedModel).toBe("nope");
|
|
540
|
+
expect(payload.effectiveModel).toBe("opus");
|
|
541
|
+
});
|
|
542
|
+
|
|
543
|
+
test("the note relays a no-selector warning without calling it a refusal", async () => {
|
|
544
|
+
spawnMock.mockImplementationOnce(async () => ({
|
|
545
|
+
acpSessionId: "acp-session-test",
|
|
546
|
+
protocolSessionId: "proto-session-test",
|
|
547
|
+
requestedModel: "opus",
|
|
548
|
+
modelWarning:
|
|
549
|
+
'Agent "claude" does not support model selection, so the session is running on the agent\'s own model.',
|
|
550
|
+
}));
|
|
551
|
+
|
|
552
|
+
const result = await executeAcpSpawn(
|
|
553
|
+
{ agent: "claude", task: "do something", model: "opus" },
|
|
554
|
+
makeContext(),
|
|
555
|
+
);
|
|
556
|
+
|
|
557
|
+
const payload = JSON.parse(result.content);
|
|
558
|
+
expect(payload.message).toContain("does not support model selection");
|
|
559
|
+
expect(payload.message).not.toContain("refused");
|
|
560
|
+
});
|
|
561
|
+
|
|
562
|
+
test("no model note when the spawn reports no warning", async () => {
|
|
563
|
+
const result = await executeAcpSpawn(
|
|
564
|
+
{ agent: "claude", task: "do something", model: "opus" },
|
|
565
|
+
makeContext(),
|
|
566
|
+
);
|
|
567
|
+
|
|
568
|
+
expect(result.isError).toBe(false);
|
|
569
|
+
const payload = JSON.parse(result.content);
|
|
570
|
+
expect(payload.message).not.toContain(
|
|
571
|
+
"The requested model was not applied",
|
|
572
|
+
);
|
|
573
|
+
});
|
|
574
|
+
|
|
575
|
+
test("reports when the adapter does not identify the effective model", async () => {
|
|
576
|
+
spawnMock.mockImplementationOnce(async () => ({
|
|
577
|
+
acpSessionId: "acp-session-test",
|
|
578
|
+
protocolSessionId: "proto-session-test",
|
|
579
|
+
requestedModel: "sonnet",
|
|
580
|
+
}));
|
|
581
|
+
|
|
582
|
+
const result = await executeAcpSpawn(
|
|
583
|
+
{ agent: "claude", task: "do something", model: "sonnet" },
|
|
584
|
+
makeContext(),
|
|
585
|
+
);
|
|
586
|
+
|
|
587
|
+
const payload = JSON.parse(result.content);
|
|
588
|
+
expect(payload.requestedModel).toBe("sonnet");
|
|
589
|
+
expect(payload.effectiveModel).toBeNull();
|
|
590
|
+
expect(payload.message).toContain(
|
|
591
|
+
"The top-level ACP session did not report an effective model.",
|
|
592
|
+
);
|
|
593
|
+
});
|
|
594
|
+
});
|
|
595
|
+
|
|
461
596
|
// ---------------------------------------------------------------------------
|
|
462
597
|
// executeAcpSpawn — CLAUDE_CODE_OAUTH_TOKEN env injection + preflight
|
|
463
598
|
//
|
package/src/tools/acp/spawn.ts
CHANGED
|
@@ -37,6 +37,7 @@ export const acpSpawnInputSchema = z.looseObject({
|
|
|
37
37
|
agent: nullAsOmitted(z.string()),
|
|
38
38
|
task: nullAsOmitted(z.string()),
|
|
39
39
|
cwd: nullAsOmitted(z.string()),
|
|
40
|
+
model: nullAsOmitted(z.string()),
|
|
40
41
|
});
|
|
41
42
|
|
|
42
43
|
/**
|
|
@@ -66,6 +67,7 @@ export async function executeAcpSpawn(
|
|
|
66
67
|
}
|
|
67
68
|
const agent = parsedInput.data.agent || "claude";
|
|
68
69
|
const task = parsedInput.data.task;
|
|
70
|
+
const model = parsedInput.data.model;
|
|
69
71
|
|
|
70
72
|
if (!task) {
|
|
71
73
|
return { content: '"task" is required.', isError: true };
|
|
@@ -124,14 +126,20 @@ export async function executeAcpSpawn(
|
|
|
124
126
|
// Recheck: the auto-install and the agent-env preparation above are both
|
|
125
127
|
// awaits, and this is the point a long-lived subprocess starts.
|
|
126
128
|
throwIfCancelled(context);
|
|
127
|
-
const {
|
|
129
|
+
const {
|
|
130
|
+
acpSessionId,
|
|
131
|
+
protocolSessionId,
|
|
132
|
+
requestedModel,
|
|
133
|
+
effectiveModel,
|
|
134
|
+
modelWarning,
|
|
135
|
+
} = await manager.spawn(
|
|
128
136
|
agent,
|
|
129
137
|
agentConfig,
|
|
130
138
|
task,
|
|
131
139
|
cwd,
|
|
132
140
|
context.conversationId,
|
|
133
141
|
sendToClient,
|
|
134
|
-
context.toolUseId,
|
|
142
|
+
{ parentToolUseId: context.toolUseId, model },
|
|
135
143
|
// The manager rechecks after its own protocol handshake and session
|
|
136
144
|
// creation, so a turn stopped in that window tears the child process
|
|
137
145
|
// down instead of handing it the task.
|
|
@@ -150,16 +158,26 @@ export async function executeAcpSpawn(
|
|
|
150
158
|
const installNote = autoInstalledPackage
|
|
151
159
|
? ` Installed ${autoInstalledPackage} automatically.`
|
|
152
160
|
: "";
|
|
161
|
+
// Relayed verbatim: the warning already distinguishes an adapter with no
|
|
162
|
+
// selector from one that refused the value.
|
|
163
|
+
const modelNote = modelWarning
|
|
164
|
+
? ` The requested model was not applied: ${modelWarning}`
|
|
165
|
+
: "";
|
|
166
|
+
const effectiveModelNote = effectiveModel
|
|
167
|
+
? ` The top-level ACP session reports "${effectiveModel}" as its effective model.`
|
|
168
|
+
: " The top-level ACP session did not report an effective model.";
|
|
153
169
|
const payload = JSON.stringify({
|
|
154
170
|
acpSessionId,
|
|
155
171
|
protocolSessionId,
|
|
156
172
|
agent,
|
|
157
173
|
cwd,
|
|
174
|
+
requestedModel: requestedModel ?? null,
|
|
175
|
+
effectiveModel: effectiveModel ?? null,
|
|
158
176
|
status: "running",
|
|
159
177
|
message:
|
|
160
178
|
`ACP agent "${agent}" spawned (session: ${protocolSessionId}). ` +
|
|
161
179
|
`Results stream back via SSE. You will be notified when it completes.` +
|
|
162
|
-
`${installNote}${resumeHint}`,
|
|
180
|
+
`${installNote}${modelNote}${effectiveModelNote}${resumeHint}`,
|
|
163
181
|
});
|
|
164
182
|
|
|
165
183
|
return { content: payload, isError: false };
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the `acp_status` tool's projection of session state.
|
|
3
|
+
*
|
|
4
|
+
* The state the manager tracks feeds three surfaces with different budgets:
|
|
5
|
+
* the HTTP route, the SSE picker, and this tool, whose output is spent on
|
|
6
|
+
* LLM context. Only the fields an agent can act on belong here.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { describe, expect, mock, test } from "bun:test";
|
|
10
|
+
|
|
11
|
+
import type { AcpSessionState } from "../../acp/types.js";
|
|
12
|
+
import type { ToolContext } from "../types.js";
|
|
13
|
+
|
|
14
|
+
const RUNNING_STATE: AcpSessionState = {
|
|
15
|
+
id: "acp-1",
|
|
16
|
+
agentId: "claude",
|
|
17
|
+
acpSessionId: "proto-1",
|
|
18
|
+
parentConversationId: "conv-1",
|
|
19
|
+
status: "running",
|
|
20
|
+
startedAt: 1234,
|
|
21
|
+
task: "refactor the parser",
|
|
22
|
+
model: "claude-opus-4-5",
|
|
23
|
+
availableModels: [
|
|
24
|
+
{ value: "sonnet", label: "Sonnet" },
|
|
25
|
+
{ value: "opus", label: "Opus", description: "Most capable" },
|
|
26
|
+
],
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
let statusResult: AcpSessionState | AcpSessionState[] = RUNNING_STATE;
|
|
30
|
+
|
|
31
|
+
const realAcpModule = await import("../../acp/index.js");
|
|
32
|
+
mock.module("../../acp/index.js", () => ({
|
|
33
|
+
...realAcpModule,
|
|
34
|
+
getAcpSessionManager: () => ({ getStatus: () => statusResult }),
|
|
35
|
+
}));
|
|
36
|
+
|
|
37
|
+
const { executeAcpStatus } = await import("./status.js");
|
|
38
|
+
|
|
39
|
+
const context = {
|
|
40
|
+
conversationId: "conv-1",
|
|
41
|
+
workingDir: "/tmp",
|
|
42
|
+
} as unknown as ToolContext;
|
|
43
|
+
|
|
44
|
+
describe("executeAcpStatus", () => {
|
|
45
|
+
test("a single session reports its model but not the picker", async () => {
|
|
46
|
+
statusResult = RUNNING_STATE;
|
|
47
|
+
|
|
48
|
+
const result = await executeAcpStatus({ acp_session_id: "acp-1" }, context);
|
|
49
|
+
|
|
50
|
+
expect(result.isError).toBe(false);
|
|
51
|
+
const payload = JSON.parse(result.content);
|
|
52
|
+
expect(payload).toEqual({
|
|
53
|
+
id: "acp-1",
|
|
54
|
+
agentId: "claude",
|
|
55
|
+
acpSessionId: "proto-1",
|
|
56
|
+
parentConversationId: "conv-1",
|
|
57
|
+
status: "running",
|
|
58
|
+
startedAt: 1234,
|
|
59
|
+
task: "refactor the parser",
|
|
60
|
+
model: "claude-opus-4-5",
|
|
61
|
+
});
|
|
62
|
+
expect(result.content).not.toContain("Most capable");
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test("the listing drops the picker from every entry", async () => {
|
|
66
|
+
statusResult = [
|
|
67
|
+
RUNNING_STATE,
|
|
68
|
+
{ ...RUNNING_STATE, id: "acp-2", model: "sonnet" },
|
|
69
|
+
];
|
|
70
|
+
|
|
71
|
+
const result = await executeAcpStatus({}, context);
|
|
72
|
+
|
|
73
|
+
const payload = JSON.parse(result.content) as Array<
|
|
74
|
+
Record<string, unknown>
|
|
75
|
+
>;
|
|
76
|
+
expect(payload).toHaveLength(2);
|
|
77
|
+
expect(payload.map((entry) => entry.model)).toEqual([
|
|
78
|
+
"claude-opus-4-5",
|
|
79
|
+
"sonnet",
|
|
80
|
+
]);
|
|
81
|
+
for (const entry of payload) {
|
|
82
|
+
expect(entry).not.toHaveProperty("availableModels");
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test("an empty listing says so rather than rendering an empty array", async () => {
|
|
87
|
+
statusResult = [];
|
|
88
|
+
|
|
89
|
+
const result = await executeAcpStatus({}, context);
|
|
90
|
+
|
|
91
|
+
expect(result.content).toBe("No ACP sessions found.");
|
|
92
|
+
expect(result.isError).toBe(false);
|
|
93
|
+
});
|
|
94
|
+
});
|
package/src/tools/acp/status.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
|
|
3
3
|
import { getAcpSessionManager } from "../../acp/index.js";
|
|
4
|
+
import type { AcpSessionState } from "../../acp/types.js";
|
|
4
5
|
import {
|
|
5
6
|
invalidToolInputResult,
|
|
6
7
|
nullAsOmitted,
|
|
@@ -16,6 +17,40 @@ export const acpStatusInputSchema = z.looseObject({
|
|
|
16
17
|
acp_session_id: nullAsOmitted(z.string()),
|
|
17
18
|
});
|
|
18
19
|
|
|
20
|
+
/**
|
|
21
|
+
* The session fields worth spending LLM context on. An allowlist rather than
|
|
22
|
+
* an omission: `availableModels` is a picker for the human surfaces (HTTP and
|
|
23
|
+
* SSE) that says nothing the agent can act on, and a field added to
|
|
24
|
+
* `AcpSessionState` for those surfaces should not reach this one by default.
|
|
25
|
+
* `model` stays: which model a session is running on is answerable.
|
|
26
|
+
*/
|
|
27
|
+
function projectSession(state: AcpSessionState) {
|
|
28
|
+
return {
|
|
29
|
+
id: state.id,
|
|
30
|
+
agentId: state.agentId,
|
|
31
|
+
acpSessionId: state.acpSessionId,
|
|
32
|
+
parentConversationId: state.parentConversationId,
|
|
33
|
+
status: state.status,
|
|
34
|
+
startedAt: state.startedAt,
|
|
35
|
+
completedAt: state.completedAt,
|
|
36
|
+
error: state.error,
|
|
37
|
+
stopReason: state.stopReason,
|
|
38
|
+
task: state.task,
|
|
39
|
+
parentToolUseId: state.parentToolUseId,
|
|
40
|
+
authErrorCode: state.authErrorCode,
|
|
41
|
+
authErrorCredential: state.authErrorCredential,
|
|
42
|
+
latestUsage: state.latestUsage,
|
|
43
|
+
model: state.model,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Projects either shape `getStatus` answers with. */
|
|
48
|
+
function projectStatus(status: AcpSessionState | AcpSessionState[]): unknown {
|
|
49
|
+
return Array.isArray(status)
|
|
50
|
+
? status.map(projectSession)
|
|
51
|
+
: projectSession(status);
|
|
52
|
+
}
|
|
53
|
+
|
|
19
54
|
export async function executeAcpStatus(
|
|
20
55
|
input: Record<string, unknown>,
|
|
21
56
|
_context: ToolContext,
|
|
@@ -29,9 +64,8 @@ export async function executeAcpStatus(
|
|
|
29
64
|
|
|
30
65
|
try {
|
|
31
66
|
if (acpSessionId) {
|
|
32
|
-
const state = manager.getStatus(acpSessionId);
|
|
33
67
|
return {
|
|
34
|
-
content: JSON.stringify(
|
|
68
|
+
content: JSON.stringify(projectStatus(manager.getStatus(acpSessionId))),
|
|
35
69
|
isError: false,
|
|
36
70
|
};
|
|
37
71
|
}
|
|
@@ -42,7 +76,10 @@ export async function executeAcpStatus(
|
|
|
42
76
|
return { content: "No ACP sessions found.", isError: false };
|
|
43
77
|
}
|
|
44
78
|
|
|
45
|
-
return {
|
|
79
|
+
return {
|
|
80
|
+
content: JSON.stringify(projectStatus(allStates)),
|
|
81
|
+
isError: false,
|
|
82
|
+
};
|
|
46
83
|
} catch (err) {
|
|
47
84
|
const msg = err instanceof Error ? err.message : String(err);
|
|
48
85
|
return { content: msg, isError: true };
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* the proxy resolver, which forwards the call to the connected client.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
import { formatDesktopAppRequired } from "../capability-offer.js";
|
|
8
9
|
import { throwIfCancelled } from "../shared/abort.js";
|
|
9
10
|
import type { ToolContext, ToolExecutionResult } from "../types.js";
|
|
10
11
|
|
|
@@ -32,7 +33,7 @@ export function forwardAppControlProxyTool(
|
|
|
32
33
|
}
|
|
33
34
|
if (!context.proxyToolResolver) {
|
|
34
35
|
return Promise.resolve({
|
|
35
|
-
content:
|
|
36
|
+
content: formatDesktopAppRequired("apps"),
|
|
36
37
|
isError: true,
|
|
37
38
|
});
|
|
38
39
|
}
|
|
@@ -262,7 +262,7 @@ export const EXTRACT_LINKS_EXPRESSION = `
|
|
|
262
262
|
(() => {
|
|
263
263
|
const anchors = Array.from(document.querySelectorAll('a[href]'));
|
|
264
264
|
return anchors.slice(0, 200).map(a => ({
|
|
265
|
-
text: (a.textContent || '').trim().slice(0, 80),
|
|
265
|
+
text: Array.from((a.textContent || '').trim()).slice(0, 80).join(''),
|
|
266
266
|
href: a.href,
|
|
267
267
|
}));
|
|
268
268
|
})()
|
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
* a later PR is a direct drop-in replacement.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
+
import { safeStringSlice } from "../../../util/unicode.js";
|
|
18
|
+
|
|
17
19
|
// ── Types ─────────────────────────────────────────────────────────────
|
|
18
20
|
|
|
19
21
|
/**
|
|
@@ -288,7 +290,11 @@ function extractAttrs(node: RawAxNode): Record<string, string> {
|
|
|
288
290
|
if (!PROPERTY_ALLOWLIST.has(name)) {
|
|
289
291
|
continue;
|
|
290
292
|
}
|
|
291
|
-
attrs[name] =
|
|
293
|
+
attrs[name] = safeStringSlice(
|
|
294
|
+
stringifyAxValue(prop.value?.value),
|
|
295
|
+
0,
|
|
296
|
+
MAX_VALUE_CHARS,
|
|
297
|
+
);
|
|
292
298
|
}
|
|
293
299
|
return attrs;
|
|
294
300
|
}
|
|
@@ -346,12 +352,12 @@ export function transformAxTree(
|
|
|
346
352
|
}
|
|
347
353
|
|
|
348
354
|
const rawName = typeof node.name?.value === "string" ? node.name.value : "";
|
|
349
|
-
const name = rawName.trim()
|
|
355
|
+
const name = safeStringSlice(rawName.trim(), 0, MAX_NAME_CHARS);
|
|
350
356
|
|
|
351
357
|
const rawValue = node.value?.value;
|
|
352
358
|
const value =
|
|
353
359
|
typeof rawValue === "string" && rawValue.length > 0
|
|
354
|
-
? rawValue
|
|
360
|
+
? safeStringSlice(rawValue, 0, MAX_VALUE_CHARS)
|
|
355
361
|
: undefined;
|
|
356
362
|
|
|
357
363
|
const attrs = extractAttrs(node);
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
* value.
|
|
26
26
|
*/
|
|
27
27
|
|
|
28
|
+
import { safeStringSlice } from "../../../../util/unicode.js";
|
|
28
29
|
import type { CdpWsTransport } from "./ws-transport.js";
|
|
29
30
|
|
|
30
31
|
/**
|
|
@@ -336,7 +337,7 @@ function parseJsonText(text: string, endpoint: string): unknown {
|
|
|
336
337
|
} catch (err) {
|
|
337
338
|
throw new DevToolsDiscoveryError(
|
|
338
339
|
"invalid_response",
|
|
339
|
-
`Expected JSON from ${endpoint} but got: ${text
|
|
340
|
+
`Expected JSON from ${endpoint} but got: ${safeStringSlice(text, 0, 200)}`,
|
|
340
341
|
err,
|
|
341
342
|
);
|
|
342
343
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { HostBrowserProxy } from "../../../daemon/host-browser-proxy.js";
|
|
2
2
|
import { getLogger } from "../../../util/logger.js";
|
|
3
|
+
import { safeStringSlice } from "../../../util/unicode.js";
|
|
3
4
|
import type { CdpErrorCode } from "./errors.js";
|
|
4
5
|
import { CdpError } from "./errors.js";
|
|
5
6
|
import type { CdpClientKind, ScopedCdpClient, TabInfo } from "./types.js";
|
|
@@ -121,7 +122,7 @@ export class ExtensionCdpClient implements ScopedCdpClient {
|
|
|
121
122
|
// and mask the extension path as the true failing hop.
|
|
122
123
|
throw new CdpError(
|
|
123
124
|
"cdp_error",
|
|
124
|
-
result.content
|
|
125
|
+
safeStringSlice(result.content, 0, 200) || `CDP error for ${method}`,
|
|
125
126
|
{
|
|
126
127
|
cdpMethod: method,
|
|
127
128
|
cdpParams: params,
|
|
@@ -164,7 +165,7 @@ export class ExtensionCdpClient implements ScopedCdpClient {
|
|
|
164
165
|
} catch (err) {
|
|
165
166
|
throw new CdpError(
|
|
166
167
|
"transport_error",
|
|
167
|
-
`Non-JSON content from host_browser_result: ${result.content
|
|
168
|
+
`Non-JSON content from host_browser_result: ${safeStringSlice(result.content, 0, 200)}`,
|
|
168
169
|
{
|
|
169
170
|
cdpMethod: method,
|
|
170
171
|
cdpParams: params,
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import { getLogger } from "../../util/logger.js";
|
|
9
9
|
import { truncate } from "../../util/truncate.js";
|
|
10
|
+
import { safeStringSlice } from "../../util/unicode.js";
|
|
10
11
|
import type {
|
|
11
12
|
ExtractedCredential,
|
|
12
13
|
NetworkRecordedEntry,
|
|
@@ -457,7 +458,7 @@ export class NetworkRecorder {
|
|
|
457
458
|
|
|
458
459
|
entry.response.body =
|
|
459
460
|
body.length > MAX_BODY_SIZE
|
|
460
|
-
? body
|
|
461
|
+
? safeStringSlice(body, 0, MAX_BODY_SIZE) + "...[truncated]"
|
|
461
462
|
: body;
|
|
462
463
|
}
|
|
463
464
|
} catch {
|
|
@@ -4,11 +4,26 @@ import {
|
|
|
4
4
|
appendLoggedInBrowserOffer,
|
|
5
5
|
CHROME_WEB_STORE_INSTALL_URL,
|
|
6
6
|
DESKTOP_APP_DOWNLOAD_URL,
|
|
7
|
+
formatDesktopAppRequired,
|
|
7
8
|
formatLoggedInBrowserOffer,
|
|
8
9
|
isPhoneSurface,
|
|
9
10
|
shouldOfferLoggedInBrowser,
|
|
10
11
|
} from "./capability-offer.js";
|
|
11
12
|
|
|
13
|
+
describe("formatDesktopAppRequired", () => {
|
|
14
|
+
test("offers a direct desktop-app download and retry step", () => {
|
|
15
|
+
const message = formatDesktopAppRequired("shell");
|
|
16
|
+
|
|
17
|
+
expect(message).toContain(
|
|
18
|
+
"The Vellum desktop app is needed to run commands on your computer",
|
|
19
|
+
);
|
|
20
|
+
expect(message).toContain(DESKTOP_APP_DOWNLOAD_URL);
|
|
21
|
+
expect(message).toContain("Open it, then retry.");
|
|
22
|
+
expect(message).not.toContain("client");
|
|
23
|
+
expect(message).not.toContain("daemon");
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
|
|
12
27
|
describe("isPhoneSurface", () => {
|
|
13
28
|
test("is true for iOS transport and mobile client OS", () => {
|
|
14
29
|
expect(isPhoneSurface({ transportInterface: "ios" })).toBe(true);
|
|
@@ -49,7 +64,9 @@ describe("shouldOfferLoggedInBrowser", () => {
|
|
|
49
64
|
expect(shouldOfferLoggedInBrowser('Invalid browser_mode "bogus"')).toBe(
|
|
50
65
|
false,
|
|
51
66
|
);
|
|
52
|
-
expect(shouldOfferLoggedInBrowser("url must use http or https")).toBe(
|
|
67
|
+
expect(shouldOfferLoggedInBrowser("url must use http or https")).toBe(
|
|
68
|
+
false,
|
|
69
|
+
);
|
|
53
70
|
});
|
|
54
71
|
});
|
|
55
72
|
|
|
@@ -71,6 +88,7 @@ describe("formatLoggedInBrowserOffer", () => {
|
|
|
71
88
|
});
|
|
72
89
|
expect(offer).toContain("no in-app browser");
|
|
73
90
|
expect(offer).toContain("Do not describe a browser panel");
|
|
91
|
+
expect(offer).toContain("Mac or Windows PC");
|
|
74
92
|
expect(offer).toContain(DESKTOP_APP_DOWNLOAD_URL);
|
|
75
93
|
expect(offer).toContain(CHROME_WEB_STORE_INSTALL_URL);
|
|
76
94
|
});
|
|
@@ -78,7 +96,9 @@ describe("formatLoggedInBrowserOffer", () => {
|
|
|
78
96
|
|
|
79
97
|
describe("appendLoggedInBrowserOffer", () => {
|
|
80
98
|
test("appends the offer once", () => {
|
|
81
|
-
const once = appendLoggedInBrowserOffer(
|
|
99
|
+
const once = appendLoggedInBrowserOffer(
|
|
100
|
+
"Error: Navigation failed: timeout",
|
|
101
|
+
);
|
|
82
102
|
expect(once).toContain("Error: Navigation failed: timeout");
|
|
83
103
|
expect(once).toContain(DESKTOP_APP_DOWNLOAD_URL);
|
|
84
104
|
const twice = appendLoggedInBrowserOffer(once);
|
|
@@ -7,7 +7,22 @@ export const DESKTOP_APP_DOWNLOAD_URL = "https://www.vellum.ai/downloads";
|
|
|
7
7
|
export const CHROME_WEB_STORE_INSTALL_URL =
|
|
8
8
|
"https://chromewebstore.google.com/detail/vellum-assistant-browser/hphbdmpffeigpcdjkckleobjmhhokpne";
|
|
9
9
|
|
|
10
|
-
export const DESKTOP_APP_INSTALL_HINT = `
|
|
10
|
+
export const DESKTOP_APP_INSTALL_HINT = `Download the Vellum desktop app: ${DESKTOP_APP_DOWNLOAD_URL}. Open it, then retry.`;
|
|
11
|
+
|
|
12
|
+
export type DesktopAppCapability = "files" | "screen" | "shell" | "apps";
|
|
13
|
+
|
|
14
|
+
const DESKTOP_APP_CAPABILITY_ACTION: Record<DesktopAppCapability, string> = {
|
|
15
|
+
files: "access files on your computer",
|
|
16
|
+
screen: "view or control your screen",
|
|
17
|
+
shell: "run commands on your computer",
|
|
18
|
+
apps: "control apps on your computer",
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export function formatDesktopAppRequired(
|
|
22
|
+
capability: DesktopAppCapability,
|
|
23
|
+
): string {
|
|
24
|
+
return `Error: The Vellum desktop app is needed to ${DESKTOP_APP_CAPABILITY_ACTION[capability]}. ${DESKTOP_APP_INSTALL_HINT}`;
|
|
25
|
+
}
|
|
11
26
|
|
|
12
27
|
/** User-facing install step for status userActions and command-failure hints. */
|
|
13
28
|
export const CHROME_EXTENSION_INSTALL_HINT = `Install the Vellum Assistant Chrome extension from the Chrome Web Store: ${CHROME_WEB_STORE_INSTALL_URL}`;
|