@vellumai/assistant 0.12.1 → 0.12.2-staging.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +4 -0
- package/ARCHITECTURE.md +20 -21
- package/docs/architecture/turn-actor.md +15 -8
- package/docs/guardian-request-flow.md +16 -0
- package/openapi.yaml +327 -52
- package/package.json +3 -1
- package/scripts/smoke-desktop-window-theme.ts +186 -0
- package/src/__tests__/always-loaded-tools-guard.test.ts +18 -10
- package/src/__tests__/anthropic-provider.test.ts +0 -63
- package/src/__tests__/app-compiler.test.ts +6 -0
- package/src/__tests__/app-control-tool-schemas.test.ts +5 -2
- package/src/__tests__/app-dir-path-guard.test.ts +1 -3
- package/src/__tests__/assistant-event-hub-machine-name.test.ts +6 -7
- package/src/__tests__/assistant-event-hub.test.ts +44 -0
- package/src/__tests__/computer-use-skill-manifest-regression.test.ts +1 -1
- package/src/__tests__/computer-use-skill-proxy-bridge.test.ts +5 -2
- package/src/__tests__/computer-use-tools.test.ts +19 -7
- package/src/__tests__/config-schema-cmd.test.ts +2 -2
- package/src/__tests__/config-schema.test.ts +28 -29
- package/src/__tests__/config-watcher.test.ts +23 -8
- package/src/__tests__/connection-model-compat.test.ts +2 -2
- package/src/__tests__/conversation-key-store-disk-view.test.ts +1 -1
- package/src/__tests__/conversation-pairing.test.ts +26 -0
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +4 -10
- package/src/__tests__/conversation-queue.test.ts +48 -207
- package/src/__tests__/conversation-retry-discard.test.ts +54 -1
- package/src/__tests__/conversation-skill-tools.test.ts +27 -0
- package/src/__tests__/conversation-surfaces-app-control.test.ts +8 -3
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +67 -0
- package/src/__tests__/conversation-title-service.test.ts +14 -10
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +3 -5
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +51 -0
- package/src/__tests__/credential-record-retire.test.ts +171 -1
- package/src/__tests__/credential-routes.test.ts +212 -3
- package/src/__tests__/credential-security-invariants.test.ts +4 -2
- package/src/__tests__/cu-unified-flow.test.ts +8 -2
- package/src/__tests__/document-sync-tags.test.ts +25 -0
- package/src/__tests__/drain-kick-guard.test.ts +0 -2
- package/src/__tests__/drain-requeue-on-contention.test.ts +0 -6
- package/src/__tests__/evict-conversations-for-reload.test.ts +1 -1
- package/src/__tests__/git-porcelain.test.ts +47 -0
- package/src/__tests__/heartbeat-service.test.ts +56 -40
- package/src/__tests__/helpers/set-workspace-mcp.ts +11 -0
- package/src/__tests__/host-bash-proxy.test.ts +26 -45
- package/src/__tests__/host-browser-proxy.test.ts +6 -17
- package/src/__tests__/host-cu-proxy.test.ts +539 -0
- package/src/__tests__/host-file-proxy-targeted.test.ts +23 -0
- package/src/__tests__/host-file-proxy.test.ts +11 -23
- package/src/__tests__/host-transfer-proxy-targeted.test.ts +30 -9
- package/src/__tests__/list-messages-camera-frame.test.ts +121 -0
- package/src/__tests__/llm-catalog-parity.test.ts +25 -0
- package/src/__tests__/mcp-auth-routes.test.ts +6 -9
- package/src/__tests__/mcp-config-secret-boundary.test.ts +78 -136
- package/src/__tests__/mcp-health-check.test.ts +8 -11
- package/src/__tests__/mcp-list-plugin-servers.test.ts +8 -10
- package/src/__tests__/message-deleted-hook-dispatch.test.ts +119 -0
- package/src/__tests__/messages-after-tiebreaker.test.ts +91 -0
- package/src/__tests__/messages-read-boundary-guard.test.ts +1 -1
- package/src/__tests__/messaging-send-tool.test.ts +355 -548
- package/src/__tests__/model-intents.test.ts +3 -0
- package/src/__tests__/notification-vellum-adapter.test.ts +85 -2
- package/src/__tests__/oauth-commands-routes.test.ts +113 -0
- package/src/__tests__/oauth-provider-profiles.test.ts +18 -0
- package/src/__tests__/openai-provider.test.ts +1 -0
- package/src/__tests__/outlook-messaging-provider.test.ts +6 -6
- package/src/__tests__/platform-callback-registration.test.ts +8 -16
- package/src/__tests__/platform-client-verify-credential.test.ts +2 -0
- package/src/__tests__/plugin-import-boundary-guard.test.ts +14 -0
- package/src/__tests__/qdrant-manager.test.ts +132 -0
- package/src/__tests__/resolve-app.test.ts +135 -0
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +229 -6
- package/src/__tests__/script-proxy-certs.test.ts +35 -0
- package/src/__tests__/service-account-token.test.ts +247 -0
- package/src/__tests__/skill-feature-flags-integration.test.ts +1 -1
- package/src/__tests__/skill-feature-flags.test.ts +2 -2
- package/src/__tests__/skills.test.ts +9 -4
- package/src/__tests__/source-fingerprint.test.ts +184 -0
- package/src/__tests__/subagent-tool-gate-mode.test.ts +19 -3
- package/src/__tests__/subagent-tools.test.ts +3 -2
- package/src/__tests__/telephony-tts-guard.test.ts +38 -3
- package/src/__tests__/test-support/computer-use-skill-harness.ts +3 -2
- package/src/__tests__/unicode.test.ts +13 -0
- package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +4 -0
- package/src/__tests__/watch-retro-tool-availability.test.ts +8 -17
- package/src/__tests__/workspace-git-service.test.ts +26 -0
- package/src/__tests__/workspace-migration-154-repair-retired-codex-gpt-5-4-model-ids.test.ts +336 -0
- package/src/acp/__tests__/agent-process.test.ts +258 -4
- package/src/acp/__tests__/auth-required.test.ts +96 -0
- package/src/acp/__tests__/client-handler.test.ts +90 -2
- package/src/acp/__tests__/helpers/acp-model-option.ts +51 -0
- package/src/acp/__tests__/session-manager-persistence.test.ts +5 -1
- package/src/acp/__tests__/session-manager-resume.test.ts +270 -6
- package/src/acp/__tests__/session-manager-usage.test.ts +4 -2
- package/src/acp/__tests__/session-manager.test.ts +895 -4
- package/src/acp/agent-process.test.ts +4 -1
- package/src/acp/agent-process.ts +96 -10
- package/src/acp/auth-required.ts +50 -1
- package/src/acp/auto-install.test.ts +9 -7
- package/src/acp/client-handler.ts +16 -3
- package/src/acp/model-config.test.ts +166 -0
- package/src/acp/model-config.ts +138 -0
- package/src/acp/resolve-agent.test.ts +220 -6
- package/src/acp/resolve-agent.ts +88 -23
- package/src/acp/session-manager.test.ts +196 -3
- package/src/acp/session-manager.ts +441 -18
- package/src/acp/types.ts +50 -5
- package/src/agent/loop.ts +1 -0
- package/src/api/events/acp-session-model-update.test.ts +88 -0
- package/src/api/events/acp-session-model-update.ts +46 -0
- package/src/api/events/app-files-changed.ts +4 -3
- package/src/api/events/notification-intent.ts +2 -0
- package/src/api/events/user-message-echo.ts +7 -1
- package/src/api/index.ts +6 -0
- package/src/api/responses/conversation-message.ts +5 -0
- package/src/apps/app-store.ts +1 -1
- package/src/apps/resolve-app.ts +81 -0
- package/src/apps/source-fingerprint.ts +266 -0
- package/src/avatar/identity-avatar.ts +7 -2
- package/src/bundler/app-compiler.ts +15 -0
- package/src/calls/__tests__/barge-in-guard.test.ts +81 -0
- package/src/{__tests__ → calls/__tests__}/call-controller.test.ts +503 -82
- package/src/{__tests__ → calls/__tests__}/media-stream-audio-transcode.test.ts +1 -1
- package/src/{__tests__ → calls/__tests__}/media-stream-output.test.ts +37 -8
- package/src/{__tests__ → calls/__tests__}/media-stream-parser.test.ts +1 -1
- package/src/{__tests__ → calls/__tests__}/media-stream-server-integration.test.ts +150 -57
- package/src/{__tests__ → calls/__tests__}/media-stream-stt-session.test.ts +5 -5
- package/src/{__tests__ → calls/__tests__}/media-turn-detector.test.ts +1 -1
- package/src/{live-voice → calls}/__tests__/progress-narration.test.ts +4 -4
- package/src/{live-voice → calls}/__tests__/progress-phrases.test.ts +1 -1
- package/src/calls/__tests__/voice-control-protocol.test.ts +58 -25
- package/src/calls/__tests__/voice-leg-coordinator.test.ts +229 -0
- package/src/calls/__tests__/voice-progress-cadence.test.ts +274 -0
- package/src/{__tests__/voice-session-bridge.test.ts → calls/__tests__/voice-session-bridge-turn-flow.test.ts} +20 -12
- package/src/calls/__tests__/voice-session-bridge.test.ts +37 -1
- package/src/calls/__tests__/voice-triage-escalate.test.ts +105 -0
- package/src/calls/barge-in-guard.ts +118 -0
- package/src/calls/call-constants.ts +24 -4
- package/src/calls/call-controller.ts +499 -199
- package/src/calls/call-transport.ts +16 -0
- package/src/calls/media-stream-output.ts +32 -0
- package/src/calls/media-stream-server.ts +82 -30
- package/src/calls/media-stream-stt-session.ts +15 -0
- package/src/{live-voice → calls}/progress-narration.ts +2 -2
- package/src/calls/resolve-call-tts-provider.ts +33 -2
- package/src/calls/tts-call-strategy.ts +9 -5
- package/src/calls/types.ts +2 -0
- package/src/calls/voice-control-protocol.ts +53 -21
- package/src/calls/voice-leg-coordinator.ts +189 -0
- package/src/calls/voice-progress-cadence.ts +423 -0
- package/src/calls/voice-session-bridge.ts +59 -46
- package/src/calls/voice-triage-escalate.ts +175 -56
- package/src/cli/commands/__tests__/apps.test.ts +245 -0
- package/src/cli/commands/__tests__/bash.test.ts +53 -0
- package/src/cli/commands/__tests__/roadmap.help.test.ts +19 -0
- package/src/cli/commands/apps.help.ts +67 -13
- package/src/cli/commands/apps.ts +183 -0
- package/src/cli/commands/bash.help.ts +8 -13
- package/src/cli/commands/bash.ts +7 -25
- package/src/cli/commands/channels/__tests__/send.test.ts +218 -0
- package/src/cli/commands/channels/index.help.ts +70 -2
- package/src/cli/commands/channels/index.ts +16 -3
- package/src/cli/commands/channels/send.ts +128 -0
- package/src/cli/commands/credentials.help.ts +6 -2
- package/src/cli/commands/credentials.ts +2 -2
- package/src/cli/commands/mcp.help.ts +8 -8
- package/src/cli/commands/mcp.ts +1 -1
- package/src/cli/commands/roadmap.help.ts +5 -1
- package/src/cli/commands/roadmap.ts +1 -1
- package/src/cli/lib/bundled-marketplace.json +103 -90
- package/src/config/__tests__/default-profile-catalog.test.ts +22 -0
- package/src/config/__tests__/memory-retrospective-schema.test.ts +15 -1
- package/src/config/__tests__/platform-identity.test.ts +255 -0
- package/src/config/__tests__/platform-rehydration.test.ts +99 -9
- package/src/config/acp-agent-write.ts +43 -0
- package/src/config/acp-defaults.test.ts +13 -3
- package/src/config/acp-defaults.ts +23 -6
- package/src/config/acp-schema.test.ts +81 -0
- package/src/config/acp-schema.ts +30 -2
- package/src/config/bundled-skills/acp/SKILL.md +22 -14
- package/src/config/bundled-skills/acp/TOOLS.json +5 -1
- package/src/config/bundled-skills/app-builder/SKILL.md +12 -9
- package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +1 -1
- package/src/config/bundled-skills/computer-use/SKILL.md +20 -2
- package/src/config/bundled-skills/computer-use/TOOLS.json +99 -0
- package/src/config/bundled-skills/computer-use/tools/computer-use-sequence.ts +12 -0
- package/src/config/bundled-skills/media-processing/services/preprocess.ts +2 -1
- package/src/config/bundled-skills/media-processing/services/processing-pipeline.ts +2 -1
- package/src/config/bundled-skills/media-processing/tools/generate-clip.ts +2 -1
- package/src/config/bundled-skills/messaging/SKILL.md +3 -3
- package/src/config/bundled-skills/messaging/TOOLS.json +3 -3
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +67 -139
- package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +1 -1
- package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +2 -1
- package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +2 -1
- package/src/config/bundled-skills/schedule/SKILL.md +3 -4
- package/src/config/bundled-skills/screen-annotation/SKILL.md +8 -1
- package/src/config/bundled-skills/screen-annotation/tools/screen-annotation.test.ts +4 -1
- package/src/config/bundled-skills/skill-management/TOOLS.json +6 -2
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +5 -2
- package/src/config/default-profile-catalog.ts +1 -1
- package/src/config/env.ts +2 -2
- package/src/config/feature-flag-registry.json +24 -12
- package/src/config/loader.ts +1 -0
- package/src/config/memory-v3-gate.test.ts +31 -0
- package/src/config/memory-v3-gate.ts +20 -16
- package/src/config/platform-identity.ts +201 -0
- package/src/config/platform-rehydration.ts +11 -27
- package/src/config/schema.ts +2 -2
- package/src/config/schemas/__tests__/live-voice.test.ts +8 -158
- package/src/config/schemas/__tests__/voice.test.ts +148 -0
- package/src/config/schemas/calls.ts +1 -28
- package/src/config/schemas/live-voice.ts +1 -173
- package/src/config/schemas/mcp.ts +1 -1
- package/src/config/schemas/memory-retrospective.ts +10 -0
- package/src/config/schemas/services.ts +8 -0
- package/src/config/schemas/skills.ts +0 -14
- package/src/config/schemas/voice.ts +150 -0
- package/src/credential-execution/process-manager.test.ts +18 -19
- package/src/credential-health/credential-health-service.ts +11 -17
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +66 -121
- package/src/daemon/__tests__/plugin-mcp-reconcile.test.ts +4 -1
- package/src/daemon/__tests__/skill-memory-refresh-mtime.test.ts +133 -0
- package/src/daemon/app-change-notify.ts +45 -0
- package/src/daemon/config-watcher.ts +13 -194
- package/src/daemon/conversation-error.ts +3 -2
- package/src/daemon/conversation-media-retry.ts +2 -1
- package/src/daemon/conversation-process.ts +35 -64
- package/src/daemon/conversation-runtime-assembly.ts +2 -1
- package/src/daemon/conversation-skill-tools.ts +13 -31
- package/src/daemon/conversation-store.ts +1 -1
- package/src/daemon/conversation-surfaces.ts +15 -9
- package/src/daemon/conversation-tool-setup.ts +37 -43
- package/src/daemon/conversation.ts +0 -3
- package/src/daemon/dictation-profile-store.ts +2 -1
- package/src/daemon/handlers/shared.ts +2 -1
- package/src/daemon/handlers/skills.ts +6 -3
- package/src/daemon/host-bash-proxy.ts +8 -12
- package/src/daemon/host-browser-proxy.ts +15 -1
- package/src/daemon/host-cu-proxy.ts +186 -19
- package/src/daemon/host-file-proxy.ts +8 -10
- package/src/daemon/host-proxy-preactivation.ts +2 -1
- package/src/daemon/host-transfer-proxy.ts +14 -18
- package/src/daemon/lifecycle.ts +0 -5
- package/src/daemon/mcp-reload-service.ts +5 -5
- package/src/daemon/message-types/sync.ts +1 -0
- package/src/daemon/orphan-reaper.ts +1 -1
- package/src/daemon/providers-setup.ts +1 -1
- package/src/daemon/reaction-history-render.ts +2 -1
- package/src/daemon/shutdown-handlers.ts +1 -9
- package/src/daemon/skill-memory-refresh.ts +64 -0
- package/src/daemon/tool-side-effects.ts +7 -18
- package/src/desktop/__tests__/fake-desktop.ts +122 -0
- package/src/desktop/desktop-chrome-policy.test.ts +79 -0
- package/src/desktop/desktop-chrome-policy.ts +47 -0
- package/src/desktop/desktop-chrome-session.ts +37 -0
- package/src/desktop/desktop-dependencies.test.ts +102 -0
- package/src/desktop/desktop-dependencies.ts +308 -0
- package/src/desktop/desktop-feature.test.ts +18 -0
- package/src/desktop/desktop-feature.ts +19 -0
- package/src/desktop/desktop-panel-config.test.ts +135 -0
- package/src/desktop/desktop-panel-config.ts +149 -0
- package/src/desktop/desktop-session-manager.test.ts +730 -0
- package/src/desktop/desktop-session-manager.ts +850 -0
- package/src/desktop/desktop-stream-bridge.test.ts +395 -0
- package/src/desktop/desktop-stream-bridge.ts +214 -0
- package/src/desktop/desktop-wallpaper.test.ts +191 -0
- package/src/desktop/desktop-wallpaper.ts +113 -0
- package/src/desktop/desktop-window-drag.ts +177 -0
- package/src/desktop/desktop-window-manager-config.test.ts +230 -0
- package/src/desktop/desktop-window-manager-config.ts +238 -0
- package/src/desktop/desktop-window-theme.ts +170 -0
- package/src/heartbeat/heartbeat-service.ts +21 -3
- package/src/home/__tests__/feed-writer.test.ts +22 -0
- package/src/home/feed-writer.ts +11 -2
- package/src/hooks/types.ts +33 -0
- package/src/i18n/AGENTS.md +22 -0
- package/src/i18n/__tests__/i18n.test.ts +108 -0
- package/src/i18n/index.ts +16 -0
- package/src/i18n/locales.ts +93 -0
- package/src/i18n/messages.ts +61 -0
- package/src/i18n/title.ts +25 -0
- package/src/inbound/platform-callback-registration.ts +4 -5
- package/src/live-voice/__tests__/live-voice-metrics.test.ts +0 -3
- package/src/live-voice/__tests__/live-voice-progress.test.ts +15 -15
- package/src/live-voice/__tests__/live-voice-sight-frame-echo.test.ts +238 -0
- package/src/live-voice/__tests__/live-voice-vad.test.ts +2 -2
- package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +13 -69
- package/src/live-voice/live-voice-photo.ts +25 -6
- package/src/live-voice/live-voice-session.ts +213 -693
- package/src/live-voice/protocol.ts +0 -2
- package/src/mcp/__tests__/effective-config.test.ts +2 -2
- package/src/mcp/__tests__/workspace-mcp-config.test.ts +135 -0
- package/src/mcp/effective-config.ts +3 -3
- package/src/mcp/mcp-header-store.ts +29 -45
- package/src/mcp/spec-schema.ts +111 -0
- package/src/mcp/startup.ts +8 -4
- package/src/mcp/workspace-mcp-config.ts +188 -0
- package/src/messaging/provider.ts +10 -3
- package/src/messaging/providers/__tests__/proactive-address.test.ts +95 -0
- package/src/messaging/providers/callback-routing.ts +18 -0
- package/src/messaging/providers/channel-transport.ts +56 -0
- package/src/messaging/providers/discord/transport.ts +17 -0
- package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +6 -17
- package/src/messaging/providers/slack/adapter.ts +7 -44
- package/src/messaging/providers/slack/transport.ts +16 -0
- package/src/messaging/providers/telegram-bot/adapter.ts +1 -48
- package/src/messaging/providers/telegram-bot/client.ts +0 -26
- package/src/messaging/providers/telegram-bot/transport.ts +20 -0
- package/src/messaging/providers/whatsapp/adapter.ts +1 -48
- package/src/messaging/providers/whatsapp/transport.ts +18 -0
- package/src/monitoring/__tests__/workspace-git-heartbeat.test.ts +8 -0
- package/src/notifications/__tests__/assistant-reply-producer.test.ts +55 -0
- package/src/notifications/__tests__/guardian-feed-projection.test.ts +158 -2
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +153 -3
- package/src/notifications/__tests__/schedule-result-producer.test.ts +99 -3
- package/src/notifications/adapters/macos.ts +3 -0
- package/src/notifications/assistant-reply-producer.ts +8 -3
- package/src/notifications/conversation-pairing.ts +36 -16
- package/src/notifications/delivered-post-record.ts +11 -1
- package/src/notifications/guardian-feed-projection.ts +125 -5
- package/src/notifications/home-feed-side-effect.ts +55 -7
- package/src/notifications/schedule-result-producer.ts +66 -22
- package/src/oauth/AGENTS.md +2 -0
- package/src/oauth/__tests__/seed-providers-managed.test.ts +54 -0
- package/src/oauth/byo-connection.test.ts +105 -0
- package/src/oauth/byo-connection.ts +36 -6
- package/src/oauth/connection-resolver.test.ts +73 -0
- package/src/oauth/connection-resolver.ts +95 -2
- package/src/oauth/scope-utils.ts +24 -0
- package/src/oauth/seed-providers.ts +204 -25
- package/src/outbound-proxy/certs.ts +3 -5
- package/src/permissions/checker.test.ts +11 -6
- package/src/persistence/compaction-log-store-clickhouse.ts +3 -3
- package/src/persistence/conversation-crud.ts +71 -63
- package/src/persistence/conversation-lineage.ts +12 -4
- package/src/persistence/conversation-queries.ts +2 -2
- package/src/persistence/conversation-title-placeholders.ts +23 -0
- package/src/persistence/conversation-title-service.ts +9 -26
- package/src/persistence/embeddings/qdrant-manager.ts +163 -18
- package/src/persistence/llm-request-log-sink-clickhouse.ts +3 -3
- package/src/persistence/llm-request-log-source-clickhouse.ts +3 -3
- package/src/persistence/message-cursor.ts +78 -0
- package/src/persistence/message-reads.ts +30 -0
- package/src/persistence/migrations/157-invite-contact-id.ts +18 -6
- package/src/persistence/migrations/__tests__/157-invite-contact-id.test.ts +149 -0
- package/src/persistence/schema/memory-core.ts +5 -0
- package/src/platform/client.test.ts +4 -2
- package/src/platform/client.ts +2 -10
- package/src/plugin-api/constants.ts +2 -0
- package/src/plugin-api/index.ts +1 -0
- package/src/plugin-api/types.ts +2 -0
- package/src/plugins/defaults/index.ts +2 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +46 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-backfill.test.ts +118 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-preserve.test.ts +83 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +9 -5
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +20 -10
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-prompt.test.ts +5 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-state.test.ts +219 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-sweep.test.ts +31 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-trigger-check.test.ts +63 -0
- package/src/plugins/defaults/memory/__tests__/shutdown-hook.test.ts +4 -0
- package/src/plugins/defaults/memory/context-search/agent-runner.ts +3 -2
- package/src/plugins/defaults/memory/context-search/format.ts +3 -2
- package/src/plugins/defaults/memory/context-search/sources/memory-v2.ts +6 -3
- package/src/plugins/defaults/memory/context-search/sources/workspace.ts +3 -2
- package/src/plugins/defaults/memory/graph/capability-seed.ts +6 -7
- package/src/plugins/defaults/memory/hooks/init.ts +10 -0
- package/src/plugins/defaults/memory/hooks/message-deleted.ts +21 -0
- package/src/plugins/defaults/memory/hooks/shutdown.ts +2 -0
- package/src/plugins/defaults/memory/host-utils.ts +10 -0
- package/src/plugins/defaults/memory/jobs-worker.ts +13 -0
- package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +34 -60
- package/src/plugins/defaults/memory/memory-retrospective-cursor-backfill.ts +126 -0
- package/src/plugins/defaults/memory/memory-retrospective-cursor-preserve.ts +79 -0
- package/src/plugins/defaults/memory/memory-retrospective-cursor.ts +28 -0
- package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +3 -5
- package/src/plugins/defaults/memory/memory-retrospective-job.ts +12 -3
- package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +1 -1
- package/src/plugins/defaults/memory/memory-retrospective-state.ts +312 -71
- package/src/plugins/defaults/memory/memory-retrospective-sweep.ts +2 -1
- package/src/plugins/defaults/memory/memory-retrospective-trigger-check.ts +5 -3
- package/src/plugins/defaults/memory/skill-md-mtime-poll.ts +9 -0
- package/src/plugins/defaults/memory/startup.ts +28 -2
- package/src/plugins/defaults/memory/substrate/__tests__/skill-content.test.ts +7 -9
- package/src/plugins/defaults/memory/substrate/page-index.ts +6 -1
- package/src/plugins/defaults/memory/substrate/skill-content.ts +2 -2
- package/src/plugins/defaults/memory/v1/graph/consolidation.ts +4 -2
- package/src/plugins/defaults/memory/v1/graph/extraction.ts +4 -2
- package/src/plugins/defaults/memory/v1/graph/retriever.ts +4 -1
- package/src/plugins/defaults/memory/v2/reranker.ts +6 -1
- package/src/plugins/defaults/memory/v3/__tests__/card.test.ts +24 -0
- package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +125 -0
- package/src/plugins/defaults/memory/v3/__tests__/sections.test.ts +26 -0
- package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +30 -0
- package/src/plugins/defaults/memory/v3/card.ts +3 -2
- package/src/plugins/defaults/memory/v3/pool-select.ts +5 -3
- package/src/plugins/defaults/memory/v3/sections.ts +9 -3
- package/src/plugins/defaults/memory/v3/shadow-plugin.ts +5 -2
- package/src/plugins/defaults/memory/worker.ts +6 -0
- package/src/plugins/mcp-servers.ts +15 -75
- package/src/providers/__tests__/connection-model-compat.test.ts +3 -3
- package/src/providers/__tests__/retry-callsite.test.ts +41 -0
- package/src/providers/__tests__/retry-outbound-sanitize.test.ts +176 -0
- package/src/providers/anthropic/client.ts +0 -47
- package/src/providers/gemini/client.ts +9 -23
- package/src/providers/gemini-thought-signature.test.ts +63 -0
- package/src/providers/gemini-thought-signature.ts +44 -0
- package/src/providers/inference/__tests__/endpoint-probe.test.ts +42 -3
- package/src/providers/inference/auth.ts +2 -3
- package/src/providers/inference/connection-availability.ts +27 -8
- package/src/providers/inference/endpoint-probe.ts +10 -1
- package/src/providers/inference/resolve-auth.ts +22 -4
- package/src/providers/inference/service-account-token.ts +248 -0
- package/src/providers/model-catalog.ts +34 -0
- package/src/providers/model-intents.ts +10 -5
- package/src/providers/openai/__tests__/chat-completions-output-token-limit.test.ts +141 -0
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +146 -18
- package/src/providers/openai/__tests__/chat-completions-provider-thought-signature.test.ts +613 -0
- package/src/providers/openai/__tests__/google-thought-signature.test.ts +235 -0
- package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +83 -1
- package/src/providers/openai/chat-completions-provider.ts +212 -79
- package/src/providers/openai/codex-models.ts +0 -4
- package/src/providers/openai/google-thought-signature.ts +320 -0
- package/src/providers/opencode/client.test.ts +73 -4
- package/src/providers/opencode/client.ts +60 -3
- package/src/providers/openrouter/client.ts +20 -0
- package/src/providers/outbound-request-sanitize.ts +96 -0
- package/src/providers/retry.ts +30 -19
- package/src/providers/unparseable-tool-args.ts +5 -1
- package/src/runtime/AGENTS.md +3 -1
- package/src/runtime/__tests__/channel-send.test.ts +443 -0
- package/src/runtime/__tests__/desktop-stream-upgrade.test.ts +89 -0
- package/src/runtime/__tests__/host-observe.test.ts +40 -1
- package/src/runtime/__tests__/live-voice-upgrade.test.ts +58 -0
- package/src/runtime/__tests__/runtime-ws-test-utils.ts +72 -0
- package/src/runtime/__tests__/watch-stream-upgrade.test.ts +59 -0
- package/src/runtime/agent-wake.ts +2 -1
- package/src/runtime/assistant-event-hub.ts +21 -3
- package/src/runtime/auth/same-actor.ts +38 -3
- package/src/runtime/channel-send.ts +316 -0
- package/src/runtime/host-observe.ts +11 -1
- package/src/runtime/http-server.ts +160 -151
- package/src/runtime/routes/__tests__/acp-routes.test.ts +159 -4
- package/src/runtime/routes/__tests__/apps-refresh-route.test.ts +205 -0
- package/src/runtime/routes/__tests__/channel-send-routes.test.ts +127 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +108 -1
- package/src/runtime/routes/__tests__/default-provider-routes.test.ts +2 -2
- package/src/runtime/routes/__tests__/dictation-cleanup-guard.test.ts +107 -0
- package/src/runtime/routes/__tests__/mcp-add-default-risk.test.ts +16 -12
- package/src/runtime/routes/acp-routes.test.ts +15 -0
- package/src/runtime/routes/acp-routes.ts +72 -9
- package/src/runtime/routes/app-management-routes.ts +73 -0
- package/src/runtime/routes/app-routes.ts +1 -1
- package/src/runtime/routes/channel-send-routes.ts +85 -0
- package/src/runtime/routes/conversation-list-routes.ts +14 -3
- package/src/runtime/routes/conversation-query-routes.ts +40 -0
- package/src/runtime/routes/conversation-routes.ts +16 -3
- package/src/runtime/routes/credential-routes.ts +19 -10
- package/src/runtime/routes/desktop-setup-routes.test.ts +74 -0
- package/src/runtime/routes/desktop-setup-routes.ts +35 -0
- package/src/runtime/routes/diagnostics-routes.ts +61 -4
- package/src/runtime/routes/events-routes.ts +4 -0
- package/src/runtime/routes/host-cu-routes.ts +6 -0
- package/src/runtime/routes/index.ts +4 -2
- package/src/runtime/routes/log-export-routes.ts +2 -0
- package/src/runtime/routes/mcp-auth-routes.ts +39 -47
- package/src/runtime/routes/migration-routes.ts +6 -20
- package/src/runtime/routes/oauth-commands-routes.ts +36 -0
- package/src/runtime/routes/platform-routes.ts +18 -15
- package/src/runtime/routes/workspace-routes.test.ts +55 -26
- package/src/runtime/routes/workspace-routes.ts +46 -63
- package/src/runtime/routes/workspace-tree-walk.test.ts +244 -0
- package/src/runtime/routes/workspace-tree-walk.ts +240 -0
- package/src/runtime/routes/workspace-utils.ts +17 -0
- package/src/runtime/services/__tests__/conversation-serializer.test.ts +28 -0
- package/src/runtime/services/conversation-serializer.ts +16 -3
- package/src/runtime/sync/resource-sync-events.ts +2 -1
- package/src/schedule/run-script.ts +2 -1
- package/src/schedule/schedule-store.ts +9 -2
- package/src/schedule/worker.ts +1 -1
- package/src/security/__tests__/ces-http-record-backend.test.ts +137 -0
- package/src/security/ces-http-record-backend.ts +120 -0
- package/src/security/ces-rpc-record-backend.ts +7 -0
- package/src/security/untrusted-content.ts +3 -1
- package/src/skills/__tests__/workspace-skill-md-signature.test.ts +60 -0
- package/src/skills/workspace-skill-md-signature.ts +50 -0
- package/src/subagent/consult-context.ts +12 -25
- package/src/tools/__tests__/side-effect-abort-guard.test.ts +1 -0
- package/src/tools/acp/list-agents.test.ts +3 -1
- package/src/tools/acp/spawn.test.ts +146 -11
- package/src/tools/acp/spawn.ts +21 -3
- package/src/tools/acp/status.test.ts +94 -0
- package/src/tools/acp/status.ts +40 -3
- package/src/tools/app-control/skill-proxy-bridge.ts +2 -1
- package/src/tools/browser/browser-execution.ts +1 -1
- package/src/tools/browser/cdp-client/accessibility-snapshot.ts +9 -3
- package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +2 -1
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +3 -2
- package/src/tools/browser/network-recorder.ts +2 -1
- package/src/tools/capability-offer.test.ts +22 -2
- package/src/tools/capability-offer.ts +16 -1
- package/src/tools/computer-use/definitions.ts +121 -2
- package/src/tools/computer-use/skill-proxy-bridge.ts +2 -1
- package/src/tools/credentials/metadata-store.ts +100 -0
- package/src/tools/filesystem/search.ts +2 -1
- package/src/tools/host-filesystem/edit.test.ts +2 -1
- package/src/tools/host-filesystem/edit.ts +2 -2
- package/src/tools/host-filesystem/read.test.ts +2 -1
- package/src/tools/host-filesystem/read.ts +2 -2
- package/src/tools/host-filesystem/transfer.test.ts +2 -1
- package/src/tools/host-filesystem/transfer.ts +2 -2
- package/src/tools/host-filesystem/write.test.ts +2 -1
- package/src/tools/host-filesystem/write.ts +2 -2
- package/src/tools/host-terminal/host-shell.test.ts +16 -0
- package/src/tools/host-terminal/host-shell.ts +4 -20
- package/src/tools/network/web-search.ts +6 -1
- package/src/tools/policy-context.ts +6 -7
- package/src/tools/skills/scaffold-managed.ts +61 -3
- package/src/tools/subagent/spawn.ts +5 -8
- package/src/tools/terminal/__tests__/sanitized-bash.test.ts +57 -0
- package/src/tools/terminal/sanitized-bash.ts +105 -0
- package/src/tools/watch/watch-retro-report.ts +0 -8
- package/src/types/host-capabilities.ts +5 -3
- package/src/util/__tests__/language-subtag.test.ts +23 -1
- package/src/util/ca-bundle.ts +13 -0
- package/src/util/language-subtag.ts +16 -0
- package/src/util/platform.ts +8 -0
- package/src/util/unicode.ts +15 -1
- package/src/watch/watch-timeline.ts +2 -2
- package/src/workflows/run-manager.ts +2 -1
- package/src/workspace/git-porcelain.ts +66 -0
- package/src/workspace/git-service.ts +6 -55
- package/src/workspace/migrations/154-repair-retired-codex-gpt-5-4-model-ids.ts +258 -0
- package/src/workspace/migrations/155-move-front-model-config-to-voice.ts +78 -0
- package/src/workspace/migrations/156-extract-workspace-mcp-json.ts +214 -0
- package/src/workspace/migrations/__tests__/155-move-front-model-config-to-voice.test.ts +102 -0
- package/src/workspace/migrations/__tests__/156-extract-workspace-mcp-json.test.ts +199 -0
- package/src/workspace/migrations/registry.ts +6 -0
- package/src/__tests__/app-source-watcher.test.ts +0 -218
- package/src/__tests__/config-watcher-skill-reseed.test.ts +0 -359
- package/src/__tests__/speaker-identification.test.ts +0 -56
- package/src/calls/speaker-identification.ts +0 -233
- package/src/daemon/app-source-watcher.ts +0 -218
- package/src/messaging/providers/whatsapp/client.ts +0 -29
- package/src/runtime/routes/debug-bash-routes.ts +0 -173
- package/src/util/fs-watcher-error.ts +0 -36
- /package/src/{live-voice → calls}/progress-phrases.ts +0 -0
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
BadRequestError,
|
|
5
|
+
ForbiddenError,
|
|
6
|
+
InternalError,
|
|
7
|
+
} from "../runtime/routes/errors.js";
|
|
4
8
|
import type { CredentialMetadata } from "../tools/credentials/metadata-store.js";
|
|
5
9
|
|
|
6
10
|
// ---------------------------------------------------------------------------
|
|
@@ -15,6 +19,8 @@ let disconnectedProviders: string[];
|
|
|
15
19
|
let credentialIdCounter: number;
|
|
16
20
|
let scrubbedValues: string[];
|
|
17
21
|
let scrubRejects: boolean;
|
|
22
|
+
let liveRecordsUnreachable: boolean;
|
|
23
|
+
let secretGetUnreachable: boolean;
|
|
18
24
|
|
|
19
25
|
function metaKey(service: string, field: string): string {
|
|
20
26
|
return `${service}:${field}`;
|
|
@@ -49,7 +55,7 @@ mock.module("../security/secure-keys.js", () => ({
|
|
|
49
55
|
getSecureKeyAsync: mock(async (key: string) => secureStore.get(key)),
|
|
50
56
|
getSecureKeyResultAsync: mock(async (key: string) => ({
|
|
51
57
|
value: secureStore.get(key),
|
|
52
|
-
unreachable:
|
|
58
|
+
unreachable: secretGetUnreachable,
|
|
53
59
|
})),
|
|
54
60
|
deleteSecureKeyAsync: mock(async (key: string) =>
|
|
55
61
|
secureStore.delete(key) ? "deleted" : "not-found",
|
|
@@ -60,7 +66,10 @@ mock.module("../security/secure-keys.js", () => ({
|
|
|
60
66
|
|
|
61
67
|
mock.module("../tools/credentials/metadata-store.js", () => ({
|
|
62
68
|
assertMetadataWritable: () => {},
|
|
63
|
-
|
|
69
|
+
listCredentialRecordsLive: mock(async () => ({
|
|
70
|
+
records: Array.from(metadataStore.values()),
|
|
71
|
+
unreachable: liveRecordsUnreachable,
|
|
72
|
+
})),
|
|
64
73
|
getCredentialMetadata: mock((service: string, field: string) =>
|
|
65
74
|
metadataStore.get(metaKey(service, field)),
|
|
66
75
|
),
|
|
@@ -201,6 +210,8 @@ describe("credentials routes", () => {
|
|
|
201
210
|
credentialIdCounter = 0;
|
|
202
211
|
scrubbedValues = [];
|
|
203
212
|
scrubRejects = false;
|
|
213
|
+
liveRecordsUnreachable = false;
|
|
214
|
+
secretGetUnreachable = false;
|
|
204
215
|
_resetRevealSuccessRegistryForTest();
|
|
205
216
|
resetForChatMintRegistryForTest();
|
|
206
217
|
chatCredentialRevealFlag = false;
|
|
@@ -799,6 +810,204 @@ describe("credentials routes", () => {
|
|
|
799
810
|
expect(result.credentials).toHaveLength(1);
|
|
800
811
|
expect(result.credentials[0].service).toBe("github");
|
|
801
812
|
});
|
|
813
|
+
|
|
814
|
+
test("fails immediately when the credential vault is unreachable", async () => {
|
|
815
|
+
liveRecordsUnreachable = true;
|
|
816
|
+
await setRoute!.handler({
|
|
817
|
+
body: { service: "vercel", field: "api_token", value: SECRET_VALUE },
|
|
818
|
+
});
|
|
819
|
+
|
|
820
|
+
await expect(listRoute!.handler({ body: {} })).rejects.toThrow(
|
|
821
|
+
InternalError,
|
|
822
|
+
);
|
|
823
|
+
await expect(listRoute!.handler({ body: {} })).rejects.toThrow(
|
|
824
|
+
/Credential store is unreachable/,
|
|
825
|
+
);
|
|
826
|
+
});
|
|
827
|
+
|
|
828
|
+
test("fails immediately when a secret fetch reports the vault unreachable", async () => {
|
|
829
|
+
await setRoute!.handler({
|
|
830
|
+
body: { service: "vercel", field: "api_token", value: SECRET_VALUE },
|
|
831
|
+
});
|
|
832
|
+
secretGetUnreachable = true;
|
|
833
|
+
|
|
834
|
+
await expect(listRoute!.handler({ body: {} })).rejects.toThrow(
|
|
835
|
+
InternalError,
|
|
836
|
+
);
|
|
837
|
+
});
|
|
838
|
+
});
|
|
839
|
+
|
|
840
|
+
describe("platform-managed credentials", () => {
|
|
841
|
+
const PLATFORM_KEY = "vk-platform-provisioned-key";
|
|
842
|
+
const PLATFORM_BASE_URL = "https://platform.example";
|
|
843
|
+
|
|
844
|
+
/**
|
|
845
|
+
* The platform provisions the pod's own credentials by POSTing them to
|
|
846
|
+
* `/v1/secrets` as `credential` entries, which land in the same secure
|
|
847
|
+
* store and metadata table as a user's own credentials.
|
|
848
|
+
*/
|
|
849
|
+
function seedPlatformCredential(field: string, value: string): string {
|
|
850
|
+
const key = `vellum:${field}`;
|
|
851
|
+
secureStore.set(key, value);
|
|
852
|
+
const now = Date.now();
|
|
853
|
+
metadataStore.set(key, {
|
|
854
|
+
credentialId: `cred-${++credentialIdCounter}`,
|
|
855
|
+
service: "vellum",
|
|
856
|
+
field,
|
|
857
|
+
allowedTools: [],
|
|
858
|
+
allowedDomains: [],
|
|
859
|
+
createdAt: now,
|
|
860
|
+
updatedAt: now,
|
|
861
|
+
});
|
|
862
|
+
return metadataStore.get(key)!.credentialId;
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
test("the listing omits platform credentials and keeps the user's own", async () => {
|
|
866
|
+
// GIVEN the platform's provisioned credentials alongside a user's
|
|
867
|
+
seedPlatformCredential("assistant_api_key", PLATFORM_KEY);
|
|
868
|
+
seedPlatformCredential("platform_base_url", PLATFORM_BASE_URL);
|
|
869
|
+
await setRoute!.handler({
|
|
870
|
+
body: { service: "vercel", field: "api_token", value: SECRET_VALUE },
|
|
871
|
+
});
|
|
872
|
+
|
|
873
|
+
// WHEN the credentials are listed
|
|
874
|
+
const result = (await listRoute!.handler({ body: {} })) as ListResponse;
|
|
875
|
+
|
|
876
|
+
// THEN only the user's credential is a row, and no platform value leaks
|
|
877
|
+
expect(result.credentials.map((c) => `${c.service}:${c.field}`)).toEqual([
|
|
878
|
+
"vercel:api_token",
|
|
879
|
+
]);
|
|
880
|
+
expect(JSON.stringify(result)).not.toContain(PLATFORM_KEY);
|
|
881
|
+
});
|
|
882
|
+
|
|
883
|
+
test("reveal refuses the platform API key for every principal", async () => {
|
|
884
|
+
/**
|
|
885
|
+
* Both farming paths land on this handler: the assistant's own tool
|
|
886
|
+
* shell arrives as `local`, a Settings click as `user`.
|
|
887
|
+
*/
|
|
888
|
+
seedPlatformCredential("assistant_api_key", PLATFORM_KEY);
|
|
889
|
+
|
|
890
|
+
for (const principal of ["local", "user", "svc_gateway"]) {
|
|
891
|
+
await expect(
|
|
892
|
+
revealRoute!.handler({
|
|
893
|
+
body: { service: "vellum", field: "assistant_api_key" },
|
|
894
|
+
headers: { "x-vellum-principal-type": principal },
|
|
895
|
+
}),
|
|
896
|
+
).rejects.toThrow(ForbiddenError);
|
|
897
|
+
}
|
|
898
|
+
});
|
|
899
|
+
|
|
900
|
+
test("reveal refuses a platform credential looked up by UUID", async () => {
|
|
901
|
+
const credentialId = seedPlatformCredential(
|
|
902
|
+
"assistant_api_key",
|
|
903
|
+
PLATFORM_KEY,
|
|
904
|
+
);
|
|
905
|
+
|
|
906
|
+
await expect(
|
|
907
|
+
revealRoute!.handler({
|
|
908
|
+
body: { id: credentialId },
|
|
909
|
+
headers: { "x-vellum-principal-type": "local" },
|
|
910
|
+
}),
|
|
911
|
+
).rejects.toThrow(/owned by the Vellum platform/);
|
|
912
|
+
});
|
|
913
|
+
|
|
914
|
+
test("inspect refuses platform credentials", async () => {
|
|
915
|
+
seedPlatformCredential("assistant_api_key", PLATFORM_KEY);
|
|
916
|
+
seedPlatformCredential("platform_base_url", PLATFORM_BASE_URL);
|
|
917
|
+
|
|
918
|
+
for (const field of ["assistant_api_key", "platform_base_url"]) {
|
|
919
|
+
await expect(
|
|
920
|
+
inspectRoute!.handler({
|
|
921
|
+
body: { service: "vellum", field },
|
|
922
|
+
}),
|
|
923
|
+
).rejects.toThrow(ForbiddenError);
|
|
924
|
+
}
|
|
925
|
+
});
|
|
926
|
+
|
|
927
|
+
test("set refuses platform credentials", async () => {
|
|
928
|
+
/**
|
|
929
|
+
* A write is a read by another name: repointing `platform_base_url`
|
|
930
|
+
* sends the next platform call, bearing the API key, to a host the
|
|
931
|
+
* writer chose. The platform's own provisioning does not come through
|
|
932
|
+
* this route; it writes over `POST /v1/secrets`.
|
|
933
|
+
*/
|
|
934
|
+
for (const field of ["assistant_api_key", "platform_base_url"]) {
|
|
935
|
+
await expect(
|
|
936
|
+
setRoute!.handler({
|
|
937
|
+
body: { service: "vellum", field, value: "https://attacker.test" },
|
|
938
|
+
}),
|
|
939
|
+
).rejects.toThrow(ForbiddenError);
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
expect(secureStore.has("vellum:assistant_api_key")).toBe(false);
|
|
943
|
+
expect(secureStore.has("vellum:platform_base_url")).toBe(false);
|
|
944
|
+
});
|
|
945
|
+
|
|
946
|
+
test("set does not overwrite an already-provisioned platform credential", async () => {
|
|
947
|
+
seedPlatformCredential("platform_base_url", PLATFORM_BASE_URL);
|
|
948
|
+
|
|
949
|
+
await expect(
|
|
950
|
+
setRoute!.handler({
|
|
951
|
+
body: {
|
|
952
|
+
service: "vellum",
|
|
953
|
+
field: "platform_base_url",
|
|
954
|
+
value: "https://attacker.test",
|
|
955
|
+
},
|
|
956
|
+
}),
|
|
957
|
+
).rejects.toThrow(/owned by the Vellum platform/);
|
|
958
|
+
|
|
959
|
+
expect(secureStore.get("vellum:platform_base_url")).toBe(
|
|
960
|
+
PLATFORM_BASE_URL,
|
|
961
|
+
);
|
|
962
|
+
});
|
|
963
|
+
|
|
964
|
+
test("delete refuses platform credentials", async () => {
|
|
965
|
+
seedPlatformCredential("assistant_api_key", PLATFORM_KEY);
|
|
966
|
+
seedPlatformCredential("platform_base_url", PLATFORM_BASE_URL);
|
|
967
|
+
|
|
968
|
+
for (const field of ["assistant_api_key", "platform_base_url"]) {
|
|
969
|
+
await expect(
|
|
970
|
+
deleteRoute!.handler({ body: { service: "vellum", field } }),
|
|
971
|
+
).rejects.toThrow(ForbiddenError);
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
expect(secureStore.get("vellum:assistant_api_key")).toBe(PLATFORM_KEY);
|
|
975
|
+
expect(secureStore.get("vellum:platform_base_url")).toBe(
|
|
976
|
+
PLATFORM_BASE_URL,
|
|
977
|
+
);
|
|
978
|
+
});
|
|
979
|
+
|
|
980
|
+
test("a user credential is still writable and deletable", async () => {
|
|
981
|
+
const stored = (await setRoute!.handler({
|
|
982
|
+
body: { service: "vercel", field: "api_token", value: SECRET_VALUE },
|
|
983
|
+
})) as SetResponse;
|
|
984
|
+
expect(stored.service).toBe("vercel");
|
|
985
|
+
expect(secureStore.get("vercel:api_token")).toBe(SECRET_VALUE);
|
|
986
|
+
|
|
987
|
+
const deleted = (await deleteRoute!.handler({
|
|
988
|
+
body: { service: "vercel", field: "api_token" },
|
|
989
|
+
})) as DeleteResponse;
|
|
990
|
+
expect(deleted.field).toBe("api_token");
|
|
991
|
+
expect(secureStore.has("vercel:api_token")).toBe(false);
|
|
992
|
+
});
|
|
993
|
+
|
|
994
|
+
test("a user credential still inspects and reveals", async () => {
|
|
995
|
+
seedPlatformCredential("assistant_api_key", PLATFORM_KEY);
|
|
996
|
+
await setRoute!.handler({
|
|
997
|
+
body: { service: "vercel", field: "api_token", value: SECRET_VALUE },
|
|
998
|
+
});
|
|
999
|
+
|
|
1000
|
+
const inspected = (await inspectRoute!.handler({
|
|
1001
|
+
body: { service: "vercel", field: "api_token" },
|
|
1002
|
+
})) as { scrubbedValue: string };
|
|
1003
|
+
expect(inspected.scrubbedValue).toBe("supe****");
|
|
1004
|
+
|
|
1005
|
+
const revealed = (await revealRoute!.handler({
|
|
1006
|
+
body: { service: "vercel", field: "api_token" },
|
|
1007
|
+
headers: { "x-vellum-principal-type": "user" },
|
|
1008
|
+
})) as { value: string };
|
|
1009
|
+
expect(revealed.value).toBe(SECRET_VALUE);
|
|
1010
|
+
});
|
|
802
1011
|
});
|
|
803
1012
|
|
|
804
1013
|
describe("platform-managed credentials", () => {
|
|
@@ -204,6 +204,7 @@ describe("Invariant 2: no generic plaintext secret read API", () => {
|
|
|
204
204
|
"providers/registry.ts", // provider registry API key lookup for initialization
|
|
205
205
|
"providers/inference/resolve-auth.ts", // provider_connection auth resolver (api_key path reads vault, mirrors registry.ts)
|
|
206
206
|
"providers/inference/codex-token-refresh.ts", // Codex OAuth token refresh (reads/writes access_token, refresh_token, expires_at)
|
|
207
|
+
"providers/inference/service-account-token.ts", // Google service-account JWT exchange (reads service-account JSON, writes token_cache blob)
|
|
207
208
|
"providers/inference/chatgpt-subscription-credentials.ts", // ChatGPT subscription sign-in token storage, shared by the daemon routes and the CLI (setSecureKeyAsync only; no reads)
|
|
208
209
|
"providers/provider-availability.ts", // provider availability API key check
|
|
209
210
|
"media/image-credentials.ts", // shared image-gen credential resolver (provider API key lookup)
|
|
@@ -211,7 +212,8 @@ describe("Invariant 2: no generic plaintext secret read API", () => {
|
|
|
211
212
|
"persistence/llm-request-log-source-clickhouse.ts", // ClickHouse read source — lazy lookup of clickhouse:url + clickhouse:password + vellum:platform_assistant_id for self-scoped mirror reads
|
|
212
213
|
"persistence/llm-request-log-sink-clickhouse.ts", // ClickHouse write sink — lazy lookup of clickhouse:url + clickhouse:password + vellum:platform_assistant_id for self-scoped log writes
|
|
213
214
|
"persistence/compaction-log-store-clickhouse.ts", // ClickHouse compaction log writer — lazy lookup of clickhouse:url + clickhouse:password + vellum:platform_assistant_id for self-scoped event writes
|
|
214
|
-
"config/platform-
|
|
215
|
+
"config/platform-identity.ts", // assistant API key lookup for platform identity validate
|
|
216
|
+
"config/platform-rehydration.ts", // startup rehydration of platform base URL from the credential store and ids from platform validate
|
|
215
217
|
"workspace/migrations/006-services-config.ts", // services config migration reads provider API keys
|
|
216
218
|
"workspace/migrations/018-rekey-compound-credential-keys.ts", // re-key compound credential storage keys
|
|
217
219
|
"daemon/conversation-process.ts", // masked provider key display
|
|
@@ -224,7 +226,7 @@ describe("Invariant 2: no generic plaintext secret read API", () => {
|
|
|
224
226
|
"runtime/routes/sanity-routes.ts", // Sanity connect/discover routes (reads stored api_token from credential store)
|
|
225
227
|
"runtime/routes/platform-routes.ts", // CLI platform connect/disconnect/status routes (CLI-migrated to IPC)
|
|
226
228
|
"runtime/routes/roadmap-routes.ts", // signs public-roadmap calls with the assistant API key (outbound Authorization header only; the value never reaches a response, a log, or the CLI process)
|
|
227
|
-
"inbound/platform-callback-registration.ts", // managed credential lookup for platform base URL
|
|
229
|
+
"inbound/platform-callback-registration.ts", // managed credential lookup for platform base URL and API key
|
|
228
230
|
"tts/providers/elevenlabs-provider.ts", // ElevenLabs TTS API key lookup
|
|
229
231
|
"tts/providers/deepgram-provider.ts", // Deepgram TTS API key lookup
|
|
230
232
|
"tts/providers/xai-provider.ts", // xAI TTS API key lookup
|
|
@@ -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
|
+
});
|
|
@@ -557,52 +557,52 @@ describe("HeartbeatService", () => {
|
|
|
557
557
|
expect(processMessageCalls).toHaveLength(1);
|
|
558
558
|
});
|
|
559
559
|
|
|
560
|
-
|
|
561
|
-
|
|
560
|
+
test("active hours handles overnight window", async () => {
|
|
561
|
+
setHeartbeatConfig({ activeHoursStart: 22, activeHoursEnd: 6 });
|
|
562
562
|
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
563
|
+
// 23:00 should be within the window
|
|
564
|
+
const service = createService({ getCurrentHour: () => 23 });
|
|
565
|
+
await service.runOnce();
|
|
566
|
+
expect(processMessageCalls).toHaveLength(1);
|
|
567
567
|
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
568
|
+
// 10:00 should be outside the window
|
|
569
|
+
processMessageCalls.length = 0;
|
|
570
|
+
createdConversations.length = 0;
|
|
571
|
+
const service2 = createService({ getCurrentHour: () => 10 });
|
|
572
|
+
await service2.runOnce();
|
|
573
|
+
expect(processMessageCalls).toHaveLength(0);
|
|
574
|
+
});
|
|
575
575
|
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
576
|
+
test("active hours guard uses user timezone in interval mode", async () => {
|
|
577
|
+
setConfig("ui", { detectedTimezone: "America/Los_Angeles" });
|
|
578
|
+
setHeartbeatConfig({
|
|
579
|
+
cronExpression: null,
|
|
580
|
+
timezone: null,
|
|
581
|
+
activeHoursStart: 8,
|
|
582
|
+
activeHoursEnd: 22,
|
|
583
|
+
});
|
|
584
584
|
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
585
|
+
// 23:00 UTC is 16:00 Pacific (inside 8-22) and 23:00 host-local
|
|
586
|
+
// (outside 8-22). If the guard still used the host clock this would skip.
|
|
587
|
+
const inside = createService({
|
|
588
|
+
now: () => new Date("2026-09-08T23:00:00Z"),
|
|
589
|
+
getCurrentHour: () => 23,
|
|
590
|
+
});
|
|
591
|
+
expect(await inside.runOnce()).toBe(true);
|
|
592
|
+
expect(processMessageCalls).toHaveLength(1);
|
|
593
593
|
|
|
594
|
-
|
|
595
|
-
|
|
594
|
+
processMessageCalls.length = 0;
|
|
595
|
+
createdConversations.length = 0;
|
|
596
596
|
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
});
|
|
603
|
-
expect(await outside.runOnce()).toBe(false);
|
|
604
|
-
expect(processMessageCalls).toHaveLength(0);
|
|
597
|
+
// 06:00 UTC is 23:00 Pacific, outside 8-22, even though 6 is inside
|
|
598
|
+
// a naive UTC 8-22 window.
|
|
599
|
+
const outside = createService({
|
|
600
|
+
now: () => new Date("2026-09-09T06:00:00Z"),
|
|
601
|
+
getCurrentHour: () => 6,
|
|
605
602
|
});
|
|
603
|
+
expect(await outside.runOnce()).toBe(false);
|
|
604
|
+
expect(processMessageCalls).toHaveLength(0);
|
|
605
|
+
});
|
|
606
606
|
|
|
607
607
|
test("overlap prevention works", async () => {
|
|
608
608
|
let resolveFirst: () => void;
|
|
@@ -1004,7 +1004,23 @@ describe("HeartbeatService", () => {
|
|
|
1004
1004
|
"slack",
|
|
1005
1005
|
]);
|
|
1006
1006
|
|
|
1007
|
-
expect(prompt).toContain(
|
|
1007
|
+
expect(prompt).toContain(
|
|
1008
|
+
"google (integration, acts as the connected person), slack (integration, acts as the connected person)",
|
|
1009
|
+
);
|
|
1010
|
+
});
|
|
1011
|
+
|
|
1012
|
+
test("prompt tells an integration apart from the channel bot of the same name", () => {
|
|
1013
|
+
const service = createService();
|
|
1014
|
+
const { prompt } = service.buildPrompt("- Check things", ["slack"]);
|
|
1015
|
+
|
|
1016
|
+
expect(prompt).toContain(
|
|
1017
|
+
"slack (integration, acts as the connected person)",
|
|
1018
|
+
);
|
|
1019
|
+
expect(prompt).not.toContain("slack_channel");
|
|
1020
|
+
expect(prompt).toContain("A channel bot is the assistant's own identity");
|
|
1021
|
+
|
|
1022
|
+
const bot = service.buildPrompt("- Check things", ["slack_channel"]);
|
|
1023
|
+
expect(bot.prompt).toContain("slack_channel (the slack channel bot)");
|
|
1008
1024
|
});
|
|
1009
1025
|
});
|
|
1010
1026
|
|
|
@@ -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
|
});
|