@vellumai/assistant 0.12.0 → 0.12.1-dev.202609141913.5e16607
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 +5 -1
- package/ARCHITECTURE.md +11 -12
- package/docs/architecture/turn-actor.md +15 -8
- package/docs/guardian-request-flow.md +16 -0
- package/openapi.yaml +292 -50
- package/package.json +1 -1
- package/scripts/smoke-desktop-window-theme.ts +186 -0
- package/src/__tests__/always-loaded-tools-guard.test.ts +18 -10
- 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__/computer-use-skill-proxy-bridge.test.ts +5 -2
- package/src/__tests__/computer-use-tools.test.ts +17 -5
- 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__/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-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__/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__/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 +13 -0
- package/src/__tests__/resolve-app.test.ts +135 -0
- 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__/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__/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 +94 -9
- 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/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/index.ts +6 -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/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/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/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 +2 -2
- package/src/config/default-profile-catalog.ts +1 -1
- package/src/config/env.ts +2 -2
- package/src/config/feature-flag-registry.json +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/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-process.ts +35 -64
- package/src/daemon/conversation-skill-tools.ts +13 -31
- package/src/daemon/conversation-store.ts +1 -1
- package/src/daemon/conversation-surfaces.ts +10 -8
- package/src/daemon/conversation-tool-setup.ts +37 -43
- package/src/daemon/conversation.ts +0 -3
- package/src/daemon/host-bash-proxy.ts +8 -12
- package/src/daemon/host-browser-proxy.ts +15 -1
- package/src/daemon/host-file-proxy.ts +8 -10
- 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/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 +119 -0
- package/src/desktop/desktop-chrome-policy.test.ts +71 -0
- package/src/desktop/desktop-chrome-policy.ts +42 -0
- package/src/desktop/desktop-dependencies.test.ts +102 -0
- package/src/desktop/desktop-dependencies.ts +306 -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 +627 -0
- package/src/desktop/desktop-session-manager.ts +814 -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-theme.ts +170 -0
- 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-vad.test.ts +2 -2
- package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +13 -69
- 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/__tests__/seed-providers-managed.test.ts +54 -0
- package/src/oauth/byo-connection.test.ts +45 -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/seed-providers.ts +165 -0
- 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-title-placeholders.ts +23 -0
- package/src/persistence/conversation-title-service.ts +9 -26
- 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-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/graph/capability-seed.ts +4 -6
- 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/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-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 +6 -1
- package/src/plugins/defaults/memory/substrate/__tests__/skill-content.test.ts +7 -9
- package/src/plugins/defaults/memory/substrate/skill-content.ts +2 -2
- package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +89 -0
- 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/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/auth.ts +2 -3
- package/src/providers/inference/connection-availability.ts +27 -8
- 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-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 +192 -78
- package/src/providers/openai/codex-models.ts +0 -4
- package/src/providers/openai/google-thought-signature.ts +320 -0
- package/src/runtime/AGENTS.md +2 -0
- 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__/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/auth/same-actor.ts +38 -3
- package/src/runtime/channel-send.ts +316 -0
- 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/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 +55 -3
- 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/platform-routes.ts +18 -15
- 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/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/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/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/capability-offer.test.ts +22 -2
- package/src/tools/capability-offer.ts +16 -1
- package/src/tools/computer-use/definitions.ts +4 -2
- package/src/tools/computer-use/skill-proxy-bridge.ts +2 -1
- package/src/tools/credentials/metadata-store.ts +100 -0
- 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/policy-context.ts +6 -7
- package/src/tools/subagent/spawn.ts +5 -8
- package/src/tools/terminal/__tests__/safe-env.test.ts +6 -6
- package/src/tools/terminal/__tests__/sanitized-bash.test.ts +57 -0
- package/src/tools/terminal/safe-env.ts +23 -3
- package/src/tools/terminal/sanitized-bash.ts +105 -0
- package/src/tools/watch/watch-retro-report.ts +0 -8
- 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/workspace/git-porcelain.ts +66 -0
- package/src/workspace/git-service.ts +5 -54
- 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
|
@@ -224,7 +224,10 @@ describe("surfaceProxyResolver — CU tool routing", () => {
|
|
|
224
224
|
);
|
|
225
225
|
|
|
226
226
|
expect(result.isError).toBe(true);
|
|
227
|
-
expect(result.content).toContain(
|
|
227
|
+
expect(result.content).toContain(
|
|
228
|
+
"The Vellum desktop app is needed to view or control your screen",
|
|
229
|
+
);
|
|
230
|
+
expect(result.content).toContain("https://www.vellum.ai/downloads");
|
|
228
231
|
});
|
|
229
232
|
|
|
230
233
|
test("returns error when proxy exists but client not connected", async () => {
|
|
@@ -237,7 +240,10 @@ describe("surfaceProxyResolver — CU tool routing", () => {
|
|
|
237
240
|
});
|
|
238
241
|
|
|
239
242
|
expect(result.isError).toBe(true);
|
|
240
|
-
expect(result.content).toContain(
|
|
243
|
+
expect(result.content).toContain(
|
|
244
|
+
"The Vellum desktop app is needed to view or control your screen",
|
|
245
|
+
);
|
|
246
|
+
expect(result.content).toContain("https://www.vellum.ai/downloads");
|
|
241
247
|
proxyObj.dispose();
|
|
242
248
|
});
|
|
243
249
|
|
|
@@ -143,6 +143,31 @@ describe("document write routes publish documents:list", () => {
|
|
|
143
143
|
expectDocumentsChanged(events);
|
|
144
144
|
});
|
|
145
145
|
|
|
146
|
+
test("an existing document with a missing conversation can save before relinking", async () => {
|
|
147
|
+
await saveViaRoute();
|
|
148
|
+
const db = getDb();
|
|
149
|
+
db.run("PRAGMA foreign_keys = OFF");
|
|
150
|
+
try {
|
|
151
|
+
db.run("DELETE FROM conversations");
|
|
152
|
+
} finally {
|
|
153
|
+
db.run("PRAGMA foreign_keys = ON");
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
await expect(
|
|
157
|
+
saveViaRoute({ title: "Renamed notes", content: "Edited body" }),
|
|
158
|
+
).resolves.toMatchObject({ success: true, surfaceId: "doc-sync" });
|
|
159
|
+
createConversation({ id: OTHER_CONVERSATION_ID });
|
|
160
|
+
await expect(
|
|
161
|
+
linkViaRoute("doc-sync", OTHER_CONVERSATION_ID),
|
|
162
|
+
).resolves.toMatchObject({ success: true });
|
|
163
|
+
await expect(
|
|
164
|
+
invoke("getDocument", { pathParams: { id: "doc-sync" } }),
|
|
165
|
+
).resolves.toMatchObject({
|
|
166
|
+
title: "Renamed notes",
|
|
167
|
+
content: "Edited body",
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
|
|
146
171
|
test("a content-only save publishes", async () => {
|
|
147
172
|
// The save moves `updated_at` and the word count, which order and fill the
|
|
148
173
|
// Library and the assets list on every other client. The saving client
|
|
@@ -79,8 +79,6 @@ function makeFakeConversation(
|
|
|
79
79
|
getTurnInterfaceContext: () => null,
|
|
80
80
|
setTurnInterfaceContext: () => {},
|
|
81
81
|
setTransportHints: () => {},
|
|
82
|
-
setTrustContext: () => {},
|
|
83
|
-
ensureActorScopedHistory: async () => {},
|
|
84
82
|
emitActivityState: () => {
|
|
85
83
|
if (activityFailures > 0) {
|
|
86
84
|
activityFailures -= 1;
|
|
@@ -78,12 +78,6 @@ function makeFakeConversation(options: {
|
|
|
78
78
|
setTransportHints: () => {
|
|
79
79
|
mutationCalls.push("setTransportHints");
|
|
80
80
|
},
|
|
81
|
-
setTrustContext: () => {
|
|
82
|
-
mutationCalls.push("setTrustContext");
|
|
83
|
-
},
|
|
84
|
-
ensureActorScopedHistory: async () => {
|
|
85
|
-
mutationCalls.push("ensureActorScopedHistory");
|
|
86
|
-
},
|
|
87
81
|
emitActivityState: () => {
|
|
88
82
|
mutationCalls.push("emitActivityState");
|
|
89
83
|
},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `evictConversationsForReload` drops in-memory conversations after a
|
|
3
|
-
* config/prompt
|
|
3
|
+
* config/prompt reload so the next turn rebuilds them against the new
|
|
4
4
|
* config. Queued messages live only on the instance being disposed, so the
|
|
5
5
|
* same "not idle while a queue is pending" rule the periodic evictor applies
|
|
6
6
|
* has to hold here: `isProcessing()` reads false in the window between a turn
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
collectDirtyPathsFromPorcelain,
|
|
5
|
+
parsePorcelainZ,
|
|
6
|
+
} from "../workspace/git-porcelain.js";
|
|
7
|
+
|
|
8
|
+
describe("parsePorcelainZ", () => {
|
|
9
|
+
test("parses ordinary modified and untracked records", () => {
|
|
10
|
+
expect(parsePorcelainZ("M tracked.txt\0?? new.txt\0")).toEqual([
|
|
11
|
+
{ status: "M ", path: "tracked.txt" },
|
|
12
|
+
{ status: "??", path: "new.txt" },
|
|
13
|
+
]);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
test("captures rename origin on the same record", () => {
|
|
17
|
+
expect(parsePorcelainZ("R dest.txt\0src.txt\0")).toEqual([
|
|
18
|
+
{ status: "R ", path: "dest.txt", origin: "src.txt" },
|
|
19
|
+
]);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test("captures copy origin on the same record", () => {
|
|
23
|
+
expect(parsePorcelainZ("C dest.txt\0src.txt\0")).toEqual([
|
|
24
|
+
{ status: "C ", path: "dest.txt", origin: "src.txt" },
|
|
25
|
+
]);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test("skips short leftover NUL entries", () => {
|
|
29
|
+
expect(parsePorcelainZ("M a.txt\0\0")).toEqual([
|
|
30
|
+
{ status: "M ", path: "a.txt" },
|
|
31
|
+
]);
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
describe("collectDirtyPathsFromPorcelain", () => {
|
|
36
|
+
test("flattens rename origin so both paths are staged", () => {
|
|
37
|
+
expect(
|
|
38
|
+
collectDirtyPathsFromPorcelain("R dest.txt\0src.txt\0?? extra.txt\0"),
|
|
39
|
+
).toEqual(["dest.txt", "src.txt", "extra.txt"]);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test("dedupes a path that appears as both origin and destination", () => {
|
|
43
|
+
expect(collectDirtyPathsFromPorcelain("R same.txt\0same.txt\0")).toEqual([
|
|
44
|
+
"same.txt",
|
|
45
|
+
]);
|
|
46
|
+
});
|
|
47
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Seed `$VELLUM_WORKSPACE_DIR/mcp.json` for tests that go through the
|
|
3
|
+
* real workspace MCP loader.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { McpConfig } from "../../config/schemas/mcp.js";
|
|
7
|
+
import { saveWorkspaceMcpConfig } from "../../mcp/workspace-mcp-config.js";
|
|
8
|
+
|
|
9
|
+
export function setWorkspaceMcp(servers: McpConfig["servers"]): void {
|
|
10
|
+
saveWorkspaceMcpConfig({ servers });
|
|
11
|
+
}
|
|
@@ -669,23 +669,31 @@ describe("HostBashProxy", () => {
|
|
|
669
669
|
expect(sentMessages).toHaveLength(0);
|
|
670
670
|
});
|
|
671
671
|
|
|
672
|
-
test("
|
|
673
|
-
// Regression: previously fell through to untargeted broadcast, fanning
|
|
674
|
-
// a single targeted-style request out across every same-user machine.
|
|
672
|
+
test("selects the most recently active same-user client when several are connected", async () => {
|
|
675
673
|
setup();
|
|
676
674
|
setupMultipleClients(["client-1", "client-2", "client-3"]);
|
|
677
675
|
|
|
678
|
-
const
|
|
676
|
+
const resultPromise = proxy.request(
|
|
679
677
|
{ command: "echo hello" },
|
|
680
678
|
"session-1",
|
|
681
679
|
undefined,
|
|
682
680
|
"user-A",
|
|
683
681
|
);
|
|
684
682
|
|
|
685
|
-
|
|
686
|
-
expect(
|
|
687
|
-
|
|
688
|
-
|
|
683
|
+
const sent = sentMessages[0] as Record<string, unknown>;
|
|
684
|
+
expect(sent.targetClientId).toBe("client-1");
|
|
685
|
+
const options = sentMessageOptions[0] as
|
|
686
|
+
| Record<string, unknown>
|
|
687
|
+
| undefined;
|
|
688
|
+
expect(options?.targetClientId).toBe("client-1");
|
|
689
|
+
|
|
690
|
+
proxy.resolveResult(sent.requestId as string, {
|
|
691
|
+
stdout: "hello\n",
|
|
692
|
+
stderr: "",
|
|
693
|
+
exitCode: 0,
|
|
694
|
+
timedOut: false,
|
|
695
|
+
});
|
|
696
|
+
expect((await resultPromise).isError).toBe(false);
|
|
689
697
|
});
|
|
690
698
|
|
|
691
699
|
test("falls through to broadcast when zero capable clients (existing timeout path)", async () => {
|
|
@@ -832,30 +840,19 @@ describe("HostBashProxy", () => {
|
|
|
832
840
|
expect(sentMessages).toHaveLength(0);
|
|
833
841
|
});
|
|
834
842
|
|
|
835
|
-
test("
|
|
843
|
+
test("rejects when the actor has no capable client", async () => {
|
|
836
844
|
setup();
|
|
837
|
-
// No capable clients connected — untargeted path runs.
|
|
838
845
|
|
|
839
|
-
const
|
|
846
|
+
const result = await proxy.request(
|
|
840
847
|
{ command: "echo hello" },
|
|
841
848
|
"session-1",
|
|
842
849
|
undefined,
|
|
843
850
|
"user-A",
|
|
844
851
|
);
|
|
845
852
|
|
|
846
|
-
expect(
|
|
847
|
-
|
|
848
|
-
expect(
|
|
849
|
-
expect(sent.targetClientId).toBeUndefined();
|
|
850
|
-
|
|
851
|
-
const requestId = sent.requestId as string;
|
|
852
|
-
proxy.resolveResult(requestId, {
|
|
853
|
-
stdout: "hello\n",
|
|
854
|
-
stderr: "",
|
|
855
|
-
exitCode: 0,
|
|
856
|
-
timedOut: false,
|
|
857
|
-
});
|
|
858
|
-
await resultPromise;
|
|
853
|
+
expect(result).toMatchObject({ isError: true });
|
|
854
|
+
expect(result.content).toContain("belongs to this user");
|
|
855
|
+
expect(sentMessages).toHaveLength(0);
|
|
859
856
|
});
|
|
860
857
|
|
|
861
858
|
test("auto-resolve to same-user client succeeds", async () => {
|
|
@@ -883,36 +880,20 @@ describe("HostBashProxy", () => {
|
|
|
883
880
|
await resultPromise;
|
|
884
881
|
});
|
|
885
882
|
|
|
886
|
-
test("
|
|
883
|
+
test("rejects when only a different user has a capable client", async () => {
|
|
887
884
|
setup();
|
|
888
|
-
// Single capable client owned by user-B; caller is user-A.
|
|
889
885
|
setupSingleClient("client-abc", "user-B");
|
|
890
886
|
|
|
891
|
-
const
|
|
887
|
+
const result = await proxy.request(
|
|
892
888
|
{ command: "echo hello" },
|
|
893
889
|
"session-1",
|
|
894
890
|
undefined,
|
|
895
891
|
"user-A",
|
|
896
892
|
);
|
|
897
893
|
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
expect(sentMessages).toHaveLength(
|
|
901
|
-
const sent = sentMessages[0] as Record<string, unknown>;
|
|
902
|
-
expect(sent.type).toBe("host_bash_request");
|
|
903
|
-
expect(sent.targetClientId).toBeUndefined();
|
|
904
|
-
|
|
905
|
-
const opts = sentMessageOptions[0] as Record<string, unknown> | undefined;
|
|
906
|
-
expect(opts?.targetClientId).toBeUndefined();
|
|
907
|
-
|
|
908
|
-
const requestId = sent.requestId as string;
|
|
909
|
-
proxy.resolveResult(requestId, {
|
|
910
|
-
stdout: "hello\n",
|
|
911
|
-
stderr: "",
|
|
912
|
-
exitCode: 0,
|
|
913
|
-
timedOut: false,
|
|
914
|
-
});
|
|
915
|
-
await resultPromise;
|
|
894
|
+
expect(result).toMatchObject({ isError: true });
|
|
895
|
+
expect(result.content).toContain("belongs to this user");
|
|
896
|
+
expect(sentMessages).toHaveLength(0);
|
|
916
897
|
});
|
|
917
898
|
});
|
|
918
899
|
});
|
|
@@ -608,22 +608,17 @@ describe("HostBrowserProxy", () => {
|
|
|
608
608
|
},
|
|
609
609
|
];
|
|
610
610
|
|
|
611
|
-
const
|
|
611
|
+
const result = await proxy.request(
|
|
612
612
|
{ cdpMethod: "Page.navigate", cdpParams: { url: "https://a.test" } },
|
|
613
613
|
"session-1",
|
|
614
614
|
undefined,
|
|
615
615
|
"user-1",
|
|
616
616
|
);
|
|
617
617
|
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
// rejection — we never broadcast to a different actor's client.
|
|
621
|
-
await expect(resultPromise).rejects.toThrow(
|
|
622
|
-
"no active extension connection",
|
|
623
|
-
);
|
|
618
|
+
expect(result).toMatchObject({ isError: true });
|
|
619
|
+
expect(result.content).toContain("belongs to this user");
|
|
624
620
|
expect(getPublishedMessages()).toHaveLength(0);
|
|
625
621
|
});
|
|
626
|
-
|
|
627
622
|
test("prefers the chrome-extension client over a more-recently-active macOS bridge", async () => {
|
|
628
623
|
// Mock `listClientsByCapability` returns mockClients in array
|
|
629
624
|
// order, which mirrors production's `lastActiveAt`-desc ordering.
|
|
@@ -720,11 +715,6 @@ describe("HostBrowserProxy", () => {
|
|
|
720
715
|
});
|
|
721
716
|
|
|
722
717
|
test("rejects when caller has actor but no host_browser-capable client is connected for that actor", async () => {
|
|
723
|
-
// Same-user filter returns empty even though listClientsByCapability
|
|
724
|
-
// would return a non-empty list (because that list is for a
|
|
725
|
-
// different actor). The unfiltered fallback path runs only when
|
|
726
|
-
// the caller has no actor — we don't silently broadcast to anyone
|
|
727
|
-
// when the caller IS authenticated to a specific actor.
|
|
728
718
|
mockClients = [
|
|
729
719
|
{
|
|
730
720
|
clientId: "other-user-ext",
|
|
@@ -734,16 +724,15 @@ describe("HostBrowserProxy", () => {
|
|
|
734
724
|
},
|
|
735
725
|
];
|
|
736
726
|
|
|
737
|
-
const
|
|
727
|
+
const result = await proxy.request(
|
|
738
728
|
{ cdpMethod: "Page.navigate", cdpParams: { url: "https://a.test" } },
|
|
739
729
|
"session-1",
|
|
740
730
|
undefined,
|
|
741
731
|
"user-1",
|
|
742
732
|
);
|
|
743
733
|
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
);
|
|
734
|
+
expect(result).toMatchObject({ isError: true });
|
|
735
|
+
expect(result.content).toContain("belongs to this user");
|
|
747
736
|
expect(getPublishedMessages()).toHaveLength(0);
|
|
748
737
|
});
|
|
749
738
|
});
|
|
@@ -274,6 +274,29 @@ describe("HostFileProxy — targetClientId (Phase 2)", () => {
|
|
|
274
274
|
});
|
|
275
275
|
});
|
|
276
276
|
|
|
277
|
+
test("selects the most recently active same-user client when several are connected", async () => {
|
|
278
|
+
setup();
|
|
279
|
+
setupMultipleClients(["client-1", "client-2"]);
|
|
280
|
+
|
|
281
|
+
const resultPromise = proxy.request(
|
|
282
|
+
{ operation: "read", path: "/tmp/file.txt" },
|
|
283
|
+
"session-1",
|
|
284
|
+
undefined,
|
|
285
|
+
undefined,
|
|
286
|
+
TEST_PRINCIPAL,
|
|
287
|
+
);
|
|
288
|
+
|
|
289
|
+
const sent = sentMessages[0] as Record<string, unknown>;
|
|
290
|
+
expect(sent.targetClientId).toBe("client-1");
|
|
291
|
+
const options = sentMessageOptions[0] as
|
|
292
|
+
| Record<string, unknown>
|
|
293
|
+
| undefined;
|
|
294
|
+
expect(options?.targetClientId).toBe("client-1");
|
|
295
|
+
|
|
296
|
+
proxy.resolve(sent.requestId as string, { content: "ok", isError: false });
|
|
297
|
+
expect((await resultPromise).isError).toBe(false);
|
|
298
|
+
});
|
|
299
|
+
|
|
277
300
|
// ── targetClientId in cancel (abort signal) ──────────────────────────
|
|
278
301
|
|
|
279
302
|
describe("targetClientId in cancel — abort signal", () => {
|
|
@@ -746,9 +746,8 @@ describe("HostFileProxy", () => {
|
|
|
746
746
|
expect(sentMessages).toHaveLength(0);
|
|
747
747
|
});
|
|
748
748
|
|
|
749
|
-
test("
|
|
749
|
+
test("rejects when the actor has no capable client", async () => {
|
|
750
750
|
setup();
|
|
751
|
-
// No matching same-user clients available.
|
|
752
751
|
mockClients = [
|
|
753
752
|
{
|
|
754
753
|
clientId: "client-A",
|
|
@@ -757,23 +756,18 @@ describe("HostFileProxy", () => {
|
|
|
757
756
|
},
|
|
758
757
|
];
|
|
759
758
|
|
|
760
|
-
const
|
|
759
|
+
const result = await proxy.request(
|
|
761
760
|
{ operation: "read", path: "/tmp/test.txt" },
|
|
762
761
|
"session-1",
|
|
763
762
|
undefined,
|
|
764
763
|
undefined,
|
|
765
|
-
"user-B",
|
|
764
|
+
"user-B",
|
|
766
765
|
);
|
|
767
766
|
|
|
768
|
-
expect(
|
|
769
|
-
|
|
770
|
-
expect(
|
|
771
|
-
const requestId = sent.requestId as string;
|
|
772
|
-
|
|
773
|
-
proxy.resolve(requestId, { content: "ok", isError: false });
|
|
774
|
-
await resultPromise;
|
|
767
|
+
expect(result).toMatchObject({ isError: true });
|
|
768
|
+
expect(result.content).toContain("belongs to this user");
|
|
769
|
+
expect(sentMessages).toHaveLength(0);
|
|
775
770
|
});
|
|
776
|
-
|
|
777
771
|
test("auto-resolve picks the same-user client when there's exactly one", async () => {
|
|
778
772
|
setup();
|
|
779
773
|
mockClients = [
|
|
@@ -807,7 +801,7 @@ describe("HostFileProxy", () => {
|
|
|
807
801
|
await resultPromise;
|
|
808
802
|
});
|
|
809
803
|
|
|
810
|
-
test("
|
|
804
|
+
test("rejects when only a different user has a capable client", async () => {
|
|
811
805
|
setup();
|
|
812
806
|
mockClients = [
|
|
813
807
|
{
|
|
@@ -817,7 +811,7 @@ describe("HostFileProxy", () => {
|
|
|
817
811
|
},
|
|
818
812
|
];
|
|
819
813
|
|
|
820
|
-
const
|
|
814
|
+
const result = await proxy.request(
|
|
821
815
|
{ operation: "read", path: "/tmp/test.txt" },
|
|
822
816
|
"session-1",
|
|
823
817
|
undefined,
|
|
@@ -825,16 +819,10 @@ describe("HostFileProxy", () => {
|
|
|
825
819
|
"user-C",
|
|
826
820
|
);
|
|
827
821
|
|
|
828
|
-
|
|
829
|
-
expect(
|
|
830
|
-
|
|
831
|
-
expect(sent.targetClientId).toBeUndefined();
|
|
832
|
-
const requestId = sent.requestId as string;
|
|
833
|
-
|
|
834
|
-
proxy.resolve(requestId, { content: "ok", isError: false });
|
|
835
|
-
await resultPromise;
|
|
822
|
+
expect(result).toMatchObject({ isError: true });
|
|
823
|
+
expect(result.content).toContain("belongs to this user");
|
|
824
|
+
expect(sentMessages).toHaveLength(0);
|
|
836
825
|
});
|
|
837
|
-
|
|
838
826
|
test("legacy embedded targetClientId in input still goes through the same-user gate", async () => {
|
|
839
827
|
setup();
|
|
840
828
|
mockClients = [
|
|
@@ -812,7 +812,7 @@ describe("HostTransferProxy — targetClientId", () => {
|
|
|
812
812
|
await resultPromise;
|
|
813
813
|
});
|
|
814
814
|
|
|
815
|
-
test("requestToHost:
|
|
815
|
+
test("requestToHost: rejects when no client matches the source user", async () => {
|
|
816
816
|
setup();
|
|
817
817
|
mockClientRegistry.set("client-A", {
|
|
818
818
|
clientId: "client-A",
|
|
@@ -824,7 +824,7 @@ describe("HostTransferProxy — targetClientId", () => {
|
|
|
824
824
|
const srcPath = `/tmp/htp-auto-no-match-${Date.now()}.txt`;
|
|
825
825
|
await globalThis.Bun.write(srcPath, "data");
|
|
826
826
|
|
|
827
|
-
const
|
|
827
|
+
const result = await proxy.requestToHost(
|
|
828
828
|
{
|
|
829
829
|
sourcePath: srcPath,
|
|
830
830
|
destPath: "/host/dest.txt",
|
|
@@ -835,13 +835,9 @@ describe("HostTransferProxy — targetClientId", () => {
|
|
|
835
835
|
"user-C",
|
|
836
836
|
);
|
|
837
837
|
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
expect(
|
|
841
|
-
|
|
842
|
-
const requestId = sent.requestId as string;
|
|
843
|
-
proxy.resolveTransferResult(requestId, { isError: false });
|
|
844
|
-
await resultPromise;
|
|
838
|
+
expect(result).toMatchObject({ isError: true });
|
|
839
|
+
expect(result.content).toContain("belongs to this user");
|
|
840
|
+
expect(sentMessages).toHaveLength(0);
|
|
845
841
|
});
|
|
846
842
|
|
|
847
843
|
test("requestToSandbox: targeted request from a different user is rejected", async () => {
|
|
@@ -897,6 +893,31 @@ describe("HostTransferProxy — targetClientId", () => {
|
|
|
897
893
|
await resultPromise;
|
|
898
894
|
});
|
|
899
895
|
|
|
896
|
+
test("requestToSandbox: rejects when no client matches the source user", async () => {
|
|
897
|
+
setup();
|
|
898
|
+
const a: MockClient = {
|
|
899
|
+
clientId: "client-A",
|
|
900
|
+
capabilities: ["host_file"],
|
|
901
|
+
actorPrincipalId: "user-A",
|
|
902
|
+
};
|
|
903
|
+
mockCapableClients = [a];
|
|
904
|
+
mockClientRegistry.set("client-A", a);
|
|
905
|
+
|
|
906
|
+
const result = await proxy.requestToSandbox(
|
|
907
|
+
{
|
|
908
|
+
sourcePath: "/host/source.txt",
|
|
909
|
+
destPath: "/sandbox/dest.txt",
|
|
910
|
+
conversationId: "conv-same-9-no-match",
|
|
911
|
+
},
|
|
912
|
+
undefined,
|
|
913
|
+
"user-C",
|
|
914
|
+
);
|
|
915
|
+
|
|
916
|
+
expect(result).toMatchObject({ isError: true });
|
|
917
|
+
expect(result.content).toContain("belongs to this user");
|
|
918
|
+
expect(sentMessages).toHaveLength(0);
|
|
919
|
+
});
|
|
920
|
+
|
|
900
921
|
test("requestToSandbox: auto-resolve picks the same-user client when there's exactly one", async () => {
|
|
901
922
|
setup();
|
|
902
923
|
const a: MockClient = {
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
getCatalogProviderForModel,
|
|
7
7
|
isModelInCatalog,
|
|
8
8
|
PROVIDER_CATALOG,
|
|
9
|
+
supportsForcedToolChoiceWithThinking,
|
|
9
10
|
} from "../providers/model-catalog.js";
|
|
10
11
|
import { PLATFORM_PROVIDER_META } from "../providers/platform-proxy/constants.js";
|
|
11
12
|
import { resolvePricing, resolvePricingForUsage } from "../util/pricing.js";
|
|
@@ -551,6 +552,30 @@ describe("LLM catalog parity: daemon vs client", () => {
|
|
|
551
552
|
expect(getCatalogProviderForModel("unknown/model")).toBeUndefined();
|
|
552
553
|
});
|
|
553
554
|
|
|
555
|
+
test("forced tool choice with thinking is scoped to OpenRouter Kimi K2.6", () => {
|
|
556
|
+
expect(
|
|
557
|
+
supportsForcedToolChoiceWithThinking("openrouter", "moonshotai/kimi-k2.6"),
|
|
558
|
+
).toBe(false);
|
|
559
|
+
expect(
|
|
560
|
+
supportsForcedToolChoiceWithThinking(
|
|
561
|
+
"openrouter",
|
|
562
|
+
"moonshotai/kimi-k2.6-20260420",
|
|
563
|
+
),
|
|
564
|
+
).toBe(false);
|
|
565
|
+
expect(
|
|
566
|
+
supportsForcedToolChoiceWithThinking(
|
|
567
|
+
"vercel-ai-gateway",
|
|
568
|
+
"moonshotai/kimi-k2.6",
|
|
569
|
+
),
|
|
570
|
+
).toBe(true);
|
|
571
|
+
expect(
|
|
572
|
+
supportsForcedToolChoiceWithThinking("openrouter", "unknown/model"),
|
|
573
|
+
).toBe(true);
|
|
574
|
+
expect(
|
|
575
|
+
supportsForcedToolChoiceWithThinking("unknown-provider", "unknown/model"),
|
|
576
|
+
).toBe(true);
|
|
577
|
+
});
|
|
578
|
+
|
|
554
579
|
test("Gemini 2.5 Pro catalog context matches provider limits", () => {
|
|
555
580
|
const gemini = PROVIDER_CATALOG.find((entry) => entry.id === "gemini");
|
|
556
581
|
expect(
|
|
@@ -24,15 +24,12 @@ mock.module("../mcp/mcp-auth-state.js", () => ({
|
|
|
24
24
|
getMcpAuthState: mockGetMcpAuthState,
|
|
25
25
|
}));
|
|
26
26
|
|
|
27
|
-
import {
|
|
28
|
-
|
|
29
|
-
// Seed the MCP server the routes look up via
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
"my-server": {
|
|
34
|
-
transport: { type: "sse", url: "https://mcp.example.com" },
|
|
35
|
-
},
|
|
27
|
+
import { setWorkspaceMcp } from "./helpers/set-workspace-mcp.js";
|
|
28
|
+
|
|
29
|
+
// Seed the MCP server the routes look up via workspace mcp.json.
|
|
30
|
+
setWorkspaceMcp({
|
|
31
|
+
"my-server": {
|
|
32
|
+
transport: { type: "sse", url: "https://mcp.example.com" },
|
|
36
33
|
},
|
|
37
34
|
});
|
|
38
35
|
|