@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
|
@@ -627,8 +627,6 @@ export interface LiveVoiceMetricsServerFrame extends LiveVoiceServerFrameBase {
|
|
|
627
627
|
* condition as the two fields above.
|
|
628
628
|
*/
|
|
629
629
|
readonly endpointDecisionSource?: "front-door" | "provider";
|
|
630
|
-
/** Which floor-holding ack actually spoke during the turn, if any. */
|
|
631
|
-
readonly ackSpoken?: "first_delta" | "tool_use";
|
|
632
630
|
/**
|
|
633
631
|
* Spoken progress narrations during the turn. Present only when at least
|
|
634
632
|
* one progress update spoke (otherwise the field is absent, keeping frames
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
* The properties worth pinning are the ones a refactor could quietly
|
|
5
5
|
* break, each of which fails silently rather than loudly:
|
|
6
6
|
*
|
|
7
|
-
* 1. Plugin servers reach the manager at all. Reading only
|
|
8
|
-
* leaves a plugin's tools missing with nothing logged.
|
|
7
|
+
* 1. Plugin servers reach the manager at all. Reading only the workspace
|
|
8
|
+
* `mcp.json` leaves a plugin's tools missing with nothing logged.
|
|
9
9
|
* 2. A workspace entry of the same id wins, so a plugin cannot redirect a
|
|
10
10
|
* server the user configured by hand.
|
|
11
11
|
* 3. Every server is attributed. `source` is what `McpClient` reads to
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { beforeEach, describe, expect, test } from "bun:test";
|
|
4
|
+
|
|
5
|
+
import { AGENT_PLUGINS_MCP_SCHEMA_URL } from "../spec-schema.js";
|
|
6
|
+
import {
|
|
7
|
+
loadWorkspaceMcpConfig,
|
|
8
|
+
overlayWorkspaceMcpForConfigRead,
|
|
9
|
+
readWorkspaceMcpFile,
|
|
10
|
+
saveWorkspaceMcpConfig,
|
|
11
|
+
} from "../workspace-mcp-config.js";
|
|
12
|
+
|
|
13
|
+
function mcpPath(): string {
|
|
14
|
+
return join(process.env.VELLUM_WORKSPACE_DIR!, "mcp.json");
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
describe("workspace mcp.json", () => {
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
mkdirSync(process.env.VELLUM_WORKSPACE_DIR!, { recursive: true });
|
|
20
|
+
rmSync(mcpPath(), { force: true });
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test("missing file is an empty server map", () => {
|
|
24
|
+
expect(loadWorkspaceMcpConfig()).toEqual({ servers: {} });
|
|
25
|
+
expect(readWorkspaceMcpFile().fileState).toBe("missing");
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test("round-trips a spec document without wrapping transport", () => {
|
|
29
|
+
saveWorkspaceMcpConfig({
|
|
30
|
+
servers: {
|
|
31
|
+
remote: {
|
|
32
|
+
transport: {
|
|
33
|
+
type: "streamable-http",
|
|
34
|
+
url: "https://example.com/mcp",
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
local: {
|
|
38
|
+
transport: {
|
|
39
|
+
type: "stdio",
|
|
40
|
+
command: "npx",
|
|
41
|
+
args: ["-y", "srv"],
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
expect(JSON.parse(readFileSync(mcpPath(), "utf8"))).toEqual({
|
|
48
|
+
$schema: AGENT_PLUGINS_MCP_SCHEMA_URL,
|
|
49
|
+
mcpServers: {
|
|
50
|
+
remote: {
|
|
51
|
+
type: "streamable-http",
|
|
52
|
+
url: "https://example.com/mcp",
|
|
53
|
+
},
|
|
54
|
+
local: {
|
|
55
|
+
type: "stdio",
|
|
56
|
+
command: "npx",
|
|
57
|
+
args: ["-y", "srv"],
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
expect(loadWorkspaceMcpConfig()).toEqual({
|
|
62
|
+
servers: {
|
|
63
|
+
remote: {
|
|
64
|
+
transport: {
|
|
65
|
+
type: "streamable-http",
|
|
66
|
+
url: "https://example.com/mcp",
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
local: {
|
|
70
|
+
transport: {
|
|
71
|
+
type: "stdio",
|
|
72
|
+
command: "npx",
|
|
73
|
+
args: ["-y", "srv"],
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test("does not persist transport headers", () => {
|
|
81
|
+
saveWorkspaceMcpConfig({
|
|
82
|
+
servers: {
|
|
83
|
+
remote: {
|
|
84
|
+
transport: {
|
|
85
|
+
type: "streamable-http",
|
|
86
|
+
url: "https://example.com/mcp",
|
|
87
|
+
headers: { Authorization: "Bearer secret" },
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
const onDisk = readFileSync(mcpPath(), "utf8");
|
|
94
|
+
expect(onDisk).not.toContain("secret");
|
|
95
|
+
expect(loadWorkspaceMcpConfig().servers.remote?.transport).toEqual({
|
|
96
|
+
type: "streamable-http",
|
|
97
|
+
url: "https://example.com/mcp",
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
test("invalid file does not throw on read and refuses a later write", () => {
|
|
102
|
+
mkdirSync(process.env.VELLUM_WORKSPACE_DIR!, { recursive: true });
|
|
103
|
+
writeFileSync(mcpPath(), "{ not json");
|
|
104
|
+
|
|
105
|
+
expect(loadWorkspaceMcpConfig()).toEqual({ servers: {} });
|
|
106
|
+
expect(readWorkspaceMcpFile().fileState).toBe("invalid");
|
|
107
|
+
expect(() => saveWorkspaceMcpConfig({ servers: {} })).toThrow(/invalid JSON/);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
test("overlay projects transport-only servers for config reads", () => {
|
|
111
|
+
saveWorkspaceMcpConfig({
|
|
112
|
+
servers: {
|
|
113
|
+
remote: {
|
|
114
|
+
transport: {
|
|
115
|
+
type: "streamable-http",
|
|
116
|
+
url: "https://example.com/mcp",
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
const config: Record<string, unknown> = { llm: { activeProfile: "balanced" } };
|
|
123
|
+
overlayWorkspaceMcpForConfigRead(config);
|
|
124
|
+
expect(config.mcp).toEqual({
|
|
125
|
+
servers: {
|
|
126
|
+
remote: {
|
|
127
|
+
transport: {
|
|
128
|
+
type: "streamable-http",
|
|
129
|
+
url: "https://example.com/mcp",
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
});
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The MCP configuration the daemon actually runs.
|
|
3
3
|
*
|
|
4
|
-
* Two sources contribute servers: the workspace `
|
|
4
|
+
* Two sources contribute servers: the workspace `mcp.json`, which the
|
|
5
5
|
* user owns, and each installed plugin's `mcp.json`, which its author
|
|
6
6
|
* owns. Both must reach the MCP manager, so every consumer that starts
|
|
7
|
-
* servers builds its config through here rather than reading
|
|
8
|
-
*
|
|
7
|
+
* servers builds its config through here rather than reading the
|
|
8
|
+
* workspace file directly — a path that only reads the workspace half
|
|
9
9
|
* leaves a plugin's tools silently missing.
|
|
10
10
|
*
|
|
11
11
|
* Every server comes out attributed with its {@link McpServerSource}, so
|
|
@@ -3,18 +3,21 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Follows the same pattern as mcp-oauth-provider.ts: headers are stored in
|
|
5
5
|
* the secure credential store (CES or encrypted file fallback) rather than
|
|
6
|
-
* in plaintext
|
|
6
|
+
* in plaintext mcp.json, keeping secrets out of workspace config files.
|
|
7
7
|
*
|
|
8
8
|
* Key format: mcp:{serverId}:headers — stores JSON-serialized Record<string, string>.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import { loadRawConfig, saveRawConfig } from "../config/loader.js";
|
|
12
11
|
import {
|
|
13
12
|
deleteSecureKeyAsync,
|
|
14
13
|
getSecureKeyAsync,
|
|
15
14
|
setSecureKeyAsync,
|
|
16
15
|
} from "../security/secure-keys.js";
|
|
17
16
|
import { getLogger } from "../util/logger.js";
|
|
17
|
+
import {
|
|
18
|
+
loadWorkspaceMcpConfig,
|
|
19
|
+
saveWorkspaceMcpConfig,
|
|
20
|
+
} from "./workspace-mcp-config.js";
|
|
18
21
|
|
|
19
22
|
const log = getLogger("mcp-header-store");
|
|
20
23
|
|
|
@@ -73,64 +76,45 @@ export async function deleteMcpHeaders(serverId: string): Promise<boolean> {
|
|
|
73
76
|
}
|
|
74
77
|
|
|
75
78
|
/**
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
* Safe to call on every MCP reload — no-ops when no legacy headers remain.
|
|
79
|
+
* Move leftover plaintext headers from mcp.json into the credential store
|
|
80
|
+
* and rewrite the file without them. Safe to call on every MCP reload.
|
|
79
81
|
*/
|
|
80
82
|
export async function migrateLegacyMcpHeaders(): Promise<void> {
|
|
81
|
-
const
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
| undefined;
|
|
85
|
-
const servers = mcpConfig?.servers;
|
|
86
|
-
if (!servers) {
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
83
|
+
const config = loadWorkspaceMcpConfig();
|
|
84
|
+
const servers = config.servers;
|
|
85
|
+
let dirty = false;
|
|
89
86
|
|
|
90
|
-
let configDirty = false;
|
|
91
87
|
for (const [id, server] of Object.entries(servers)) {
|
|
92
|
-
const transport = server
|
|
93
|
-
if (
|
|
94
|
-
!transport ||
|
|
95
|
-
(transport.type !== "sse" && transport.type !== "streamable-http")
|
|
96
|
-
) {
|
|
88
|
+
const transport = server.transport;
|
|
89
|
+
if (transport.type !== "sse" && transport.type !== "streamable-http") {
|
|
97
90
|
continue;
|
|
98
91
|
}
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
| undefined;
|
|
102
|
-
if (!legacyHeaders || Object.keys(legacyHeaders).length === 0) {
|
|
92
|
+
const leftoverHeaders = transport.headers;
|
|
93
|
+
if (!leftoverHeaders || Object.keys(leftoverHeaders).length === 0) {
|
|
103
94
|
continue;
|
|
104
95
|
}
|
|
105
96
|
|
|
106
|
-
// Only migrate if credential store doesn't already have headers for
|
|
107
|
-
// this server (idempotent — safe to re-run after partial failure).
|
|
108
97
|
const existing = await getMcpHeaders(id);
|
|
109
|
-
if (existing) {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
delete transport.headers;
|
|
119
|
-
configDirty = true;
|
|
98
|
+
if (!existing) {
|
|
99
|
+
const ok = await setMcpHeaders(id, leftoverHeaders);
|
|
100
|
+
if (!ok) {
|
|
101
|
+
log.warn(
|
|
102
|
+
{ serverId: id },
|
|
103
|
+
"Skipping leftover header migration: credential store write failed; will retry on next reload",
|
|
104
|
+
);
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
120
107
|
log.info(
|
|
121
108
|
{ serverId: id },
|
|
122
|
-
"
|
|
123
|
-
);
|
|
124
|
-
} else {
|
|
125
|
-
log.warn(
|
|
126
|
-
{ serverId: id },
|
|
127
|
-
"Skipping legacy header migration — credential store write failed; will retry on next reload",
|
|
109
|
+
"Moved leftover MCP headers to credential store",
|
|
128
110
|
);
|
|
129
111
|
}
|
|
112
|
+
delete transport.headers;
|
|
113
|
+
dirty = true;
|
|
130
114
|
}
|
|
131
115
|
|
|
132
|
-
if (
|
|
133
|
-
|
|
134
|
-
log.info("
|
|
116
|
+
if (dirty) {
|
|
117
|
+
saveWorkspaceMcpConfig(config);
|
|
118
|
+
log.info("mcp.json updated: leftover MCP headers removed after migration");
|
|
135
119
|
}
|
|
136
120
|
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent Plugins 1.0.0 `mcp.json` wire schema and projections onto the
|
|
3
|
+
* assistant's in-memory `McpTransport` union.
|
|
4
|
+
*
|
|
5
|
+
* Workspace `/workspace/mcp.json` and each plugin's root `mcp.json` share
|
|
6
|
+
* this document shape. Origin (`workspace` vs `plugin`) is resolved from
|
|
7
|
+
* which file was read, never from a field in the file.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { z } from "zod";
|
|
11
|
+
|
|
12
|
+
import type { McpTransport } from "../config/schemas/mcp.js";
|
|
13
|
+
|
|
14
|
+
/** Canonical `$schema` value for an Agent Plugins 1.0.0 MCP document. */
|
|
15
|
+
export const AGENT_PLUGINS_MCP_SCHEMA_URL =
|
|
16
|
+
"https://agent-plugins.org/schemas/1.0.0/mcp.schema.json";
|
|
17
|
+
|
|
18
|
+
const SpecStdioServerSchema = z.object({
|
|
19
|
+
type: z.literal("stdio"),
|
|
20
|
+
command: z.string().min(1),
|
|
21
|
+
args: z.array(z.string()).optional(),
|
|
22
|
+
env: z.record(z.string(), z.string()).optional(),
|
|
23
|
+
cwd: z.string().optional(),
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Remote MCP entry. Agent Plugins 1.0.0 requires `type` and names no
|
|
28
|
+
* default. MCP's current remote transport is Streamable HTTP, so this
|
|
29
|
+
* host fills omitted `type` and the Claude-style `http` alias as
|
|
30
|
+
* `streamable-http`. `sse` stays explicit.
|
|
31
|
+
*/
|
|
32
|
+
const SpecHttpServerSchema = z.object({
|
|
33
|
+
type: z
|
|
34
|
+
.union([
|
|
35
|
+
z.literal("streamable-http"),
|
|
36
|
+
z.literal("sse"),
|
|
37
|
+
z.literal("http").transform(() => "streamable-http" as const),
|
|
38
|
+
])
|
|
39
|
+
.default("streamable-http"),
|
|
40
|
+
url: z.string().min(1),
|
|
41
|
+
headers: z.record(z.string(), z.string()).optional(),
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
export const SpecMcpServerSchema = z.union([
|
|
45
|
+
SpecStdioServerSchema,
|
|
46
|
+
SpecHttpServerSchema,
|
|
47
|
+
]);
|
|
48
|
+
|
|
49
|
+
export const SpecMcpDocumentSchema = z.object({
|
|
50
|
+
$schema: z.string().optional(),
|
|
51
|
+
mcpServers: z.record(z.string(), z.unknown()),
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
export type SpecMcpServer = z.infer<typeof SpecMcpServerSchema>;
|
|
55
|
+
export type SpecMcpDocument = z.infer<typeof SpecMcpDocumentSchema>;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Project one spec-shaped entry onto the assistant's transport union. The
|
|
59
|
+
* two vocabularies already agree on type names and required fields, so this
|
|
60
|
+
* is a field copy plus an optional expander for plugin path variables.
|
|
61
|
+
*/
|
|
62
|
+
export function projectSpecServerToTransport(
|
|
63
|
+
entry: SpecMcpServer,
|
|
64
|
+
expand?: (value: string) => string,
|
|
65
|
+
): McpTransport {
|
|
66
|
+
if (entry.type === "stdio") {
|
|
67
|
+
const map = expand ?? ((value: string) => value);
|
|
68
|
+
return {
|
|
69
|
+
type: "stdio",
|
|
70
|
+
command: entry.command,
|
|
71
|
+
args: (entry.args ?? []).map(map),
|
|
72
|
+
...(entry.env && {
|
|
73
|
+
env: Object.fromEntries(
|
|
74
|
+
Object.entries(entry.env).map(([key, value]) => [key, map(value)]),
|
|
75
|
+
),
|
|
76
|
+
}),
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
return {
|
|
80
|
+
type: entry.type,
|
|
81
|
+
url: entry.url,
|
|
82
|
+
...(entry.headers && { headers: entry.headers }),
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Project an in-memory transport onto a spec server entry. Headers are
|
|
88
|
+
* omitted: workspace secrets live in the credential store, and a write
|
|
89
|
+
* must not copy them into `mcp.json`.
|
|
90
|
+
*/
|
|
91
|
+
export function projectTransportToSpecServer(
|
|
92
|
+
transport: McpTransport,
|
|
93
|
+
): Record<string, unknown> {
|
|
94
|
+
if (transport.type === "stdio") {
|
|
95
|
+
const entry: Record<string, unknown> = {
|
|
96
|
+
type: "stdio",
|
|
97
|
+
command: transport.command,
|
|
98
|
+
};
|
|
99
|
+
if (transport.args && transport.args.length > 0) {
|
|
100
|
+
entry.args = transport.args;
|
|
101
|
+
}
|
|
102
|
+
if (transport.env) {
|
|
103
|
+
entry.env = transport.env;
|
|
104
|
+
}
|
|
105
|
+
return entry;
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
type: transport.type,
|
|
109
|
+
url: transport.url,
|
|
110
|
+
};
|
|
111
|
+
}
|
package/src/mcp/startup.ts
CHANGED
|
@@ -13,13 +13,14 @@
|
|
|
13
13
|
* further process that starts hosting turns has one call to make.
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
-
import {
|
|
16
|
+
import { invalidateConfigCache } from "../config/loader.js";
|
|
17
17
|
import type { McpConfig } from "../config/schemas/mcp.js";
|
|
18
18
|
import { createMcpToolsFromServer } from "../tools/mcp/mcp-tool-factory.js";
|
|
19
19
|
import { registerMcpTools, unregisterAllMcpTools } from "../tools/registry.js";
|
|
20
20
|
import { getLogger } from "../util/logger.js";
|
|
21
21
|
import { buildEffectiveMcpConfig } from "./effective-config.js";
|
|
22
22
|
import { getMcpServerManager, stopMcpServerManager } from "./manager.js";
|
|
23
|
+
import { loadWorkspaceMcpConfig } from "./workspace-mcp-config.js";
|
|
23
24
|
|
|
24
25
|
const log = getLogger("mcp-startup");
|
|
25
26
|
|
|
@@ -36,7 +37,8 @@ const log = getLogger("mcp-startup");
|
|
|
36
37
|
* is a startup step in processes whose other work must not be held hostage to
|
|
37
38
|
* a third-party server being up.
|
|
38
39
|
*
|
|
39
|
-
* @param workspaceMcpConfig The
|
|
40
|
+
* @param workspaceMcpConfig The projected workspace MCP config. Callers
|
|
41
|
+
* that omit it read `/workspace/mcp.json`.
|
|
40
42
|
* @returns The number of tools registered across all servers.
|
|
41
43
|
*/
|
|
42
44
|
export async function startConfiguredMcpServers(
|
|
@@ -44,7 +46,9 @@ export async function startConfiguredMcpServers(
|
|
|
44
46
|
): Promise<number> {
|
|
45
47
|
let registered = 0;
|
|
46
48
|
try {
|
|
47
|
-
const mcpConfig = buildEffectiveMcpConfig(
|
|
49
|
+
const mcpConfig = buildEffectiveMcpConfig(
|
|
50
|
+
workspaceMcpConfig ?? loadWorkspaceMcpConfig(),
|
|
51
|
+
);
|
|
48
52
|
if (Object.keys(mcpConfig.servers).length === 0) {
|
|
49
53
|
return 0;
|
|
50
54
|
}
|
|
@@ -97,5 +101,5 @@ export async function restartConfiguredMcpServers(): Promise<number> {
|
|
|
97
101
|
}
|
|
98
102
|
unregisterAllMcpTools();
|
|
99
103
|
invalidateConfigCache();
|
|
100
|
-
return startConfiguredMcpServers(
|
|
104
|
+
return startConfiguredMcpServers(loadWorkspaceMcpConfig());
|
|
101
105
|
}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* User-owned MCP servers in `$VELLUM_WORKSPACE_DIR/mcp.json`.
|
|
3
|
+
*
|
|
4
|
+
* The file is an Agent Plugins 1.0.0 document (`$schema` + `mcpServers`).
|
|
5
|
+
* This module is the only reader and writer of that file. Presence of a
|
|
6
|
+
* server starts it. Headers are never persisted here.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
existsSync,
|
|
11
|
+
mkdirSync,
|
|
12
|
+
readFileSync,
|
|
13
|
+
renameSync,
|
|
14
|
+
writeFileSync,
|
|
15
|
+
} from "node:fs";
|
|
16
|
+
import { dirname } from "node:path";
|
|
17
|
+
|
|
18
|
+
import type { McpConfig, McpServerConfig } from "../config/schemas/mcp.js";
|
|
19
|
+
import { McpConfigSchema } from "../config/schemas/mcp.js";
|
|
20
|
+
import { getLogger } from "../util/logger.js";
|
|
21
|
+
import { getWorkspaceMcpConfigPath } from "../util/platform.js";
|
|
22
|
+
import {
|
|
23
|
+
AGENT_PLUGINS_MCP_SCHEMA_URL,
|
|
24
|
+
projectSpecServerToTransport,
|
|
25
|
+
projectTransportToSpecServer,
|
|
26
|
+
SpecMcpDocumentSchema,
|
|
27
|
+
SpecMcpServerSchema,
|
|
28
|
+
} from "./spec-schema.js";
|
|
29
|
+
|
|
30
|
+
const log = getLogger("workspace-mcp-config");
|
|
31
|
+
|
|
32
|
+
export type WorkspaceMcpFileState = "missing" | "ok" | "invalid";
|
|
33
|
+
|
|
34
|
+
export interface WorkspaceMcpLoadResult {
|
|
35
|
+
readonly config: McpConfig;
|
|
36
|
+
readonly issues: readonly string[];
|
|
37
|
+
readonly fileState: WorkspaceMcpFileState;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Read `/workspace/mcp.json` and project it onto the in-memory MCP config.
|
|
42
|
+
*
|
|
43
|
+
* A missing file is an empty server map. An invalid file disables the
|
|
44
|
+
* workspace half only (plugin servers still load) and is reported so a
|
|
45
|
+
* write can refuse to overwrite it.
|
|
46
|
+
*/
|
|
47
|
+
export function readWorkspaceMcpFile(
|
|
48
|
+
path: string = getWorkspaceMcpConfigPath(),
|
|
49
|
+
): WorkspaceMcpLoadResult {
|
|
50
|
+
if (!existsSync(path)) {
|
|
51
|
+
return {
|
|
52
|
+
config: McpConfigSchema.parse({}),
|
|
53
|
+
issues: [],
|
|
54
|
+
fileState: "missing",
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
let raw: string;
|
|
59
|
+
try {
|
|
60
|
+
raw = readFileSync(path, "utf8");
|
|
61
|
+
} catch (err) {
|
|
62
|
+
const message = `mcp.json unreadable: ${err instanceof Error ? err.message : String(err)}`;
|
|
63
|
+
return {
|
|
64
|
+
config: McpConfigSchema.parse({}),
|
|
65
|
+
issues: [message],
|
|
66
|
+
fileState: "invalid",
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
let json: unknown;
|
|
71
|
+
try {
|
|
72
|
+
json = JSON.parse(raw);
|
|
73
|
+
} catch (err) {
|
|
74
|
+
const message = `mcp.json invalid JSON: ${err instanceof Error ? err.message : String(err)}`;
|
|
75
|
+
return {
|
|
76
|
+
config: McpConfigSchema.parse({}),
|
|
77
|
+
issues: [message],
|
|
78
|
+
fileState: "invalid",
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const document = SpecMcpDocumentSchema.safeParse(json);
|
|
83
|
+
if (!document.success) {
|
|
84
|
+
return {
|
|
85
|
+
config: McpConfigSchema.parse({}),
|
|
86
|
+
issues: ['mcp.json is missing a valid "mcpServers" object'],
|
|
87
|
+
fileState: "invalid",
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const servers: Record<string, McpServerConfig> = {};
|
|
92
|
+
const issues: string[] = [];
|
|
93
|
+
|
|
94
|
+
for (const [serverKey, rawEntry] of Object.entries(document.data.mcpServers)) {
|
|
95
|
+
if (serverKey.trim().length === 0) {
|
|
96
|
+
issues.push("server key must be a non-empty string; skipping");
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const entry = SpecMcpServerSchema.safeParse(rawEntry);
|
|
101
|
+
if (!entry.success) {
|
|
102
|
+
issues.push(
|
|
103
|
+
`invalid server entry "${serverKey}": ${entry.error.issues
|
|
104
|
+
.map((issue) => `${issue.path.join(".") || "<root>"}: ${issue.message}`)
|
|
105
|
+
.join("; ")}`,
|
|
106
|
+
);
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (entry.data.type === "stdio" && entry.data.cwd) {
|
|
111
|
+
issues.push(
|
|
112
|
+
`"${serverKey}": "cwd" is not supported by this host and was ignored; the server runs in the assistant's working directory`,
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
servers[serverKey] = {
|
|
117
|
+
transport: projectSpecServerToTransport(entry.data),
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return {
|
|
122
|
+
config: { servers },
|
|
123
|
+
issues,
|
|
124
|
+
fileState: "ok",
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** Load the workspace MCP config, logging any declaration problems. */
|
|
129
|
+
export function loadWorkspaceMcpConfig(): McpConfig {
|
|
130
|
+
const result = readWorkspaceMcpFile();
|
|
131
|
+
for (const issue of result.issues) {
|
|
132
|
+
log.warn(issue);
|
|
133
|
+
}
|
|
134
|
+
return result.config;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Refuse a write when `mcp.json` exists but is not a usable document, so a
|
|
139
|
+
* later add/remove cannot clobber a hand-edit the user still means to fix.
|
|
140
|
+
*/
|
|
141
|
+
export function assertWorkspaceMcpWritable(): void {
|
|
142
|
+
const result = readWorkspaceMcpFile();
|
|
143
|
+
if (result.fileState === "invalid") {
|
|
144
|
+
throw new Error(result.issues[0] ?? "mcp.json is not a valid document");
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/** Persist the in-memory workspace MCP config as a spec-pure document. */
|
|
149
|
+
export function saveWorkspaceMcpConfig(config: McpConfig): void {
|
|
150
|
+
assertWorkspaceMcpWritable();
|
|
151
|
+
const path = getWorkspaceMcpConfigPath();
|
|
152
|
+
const mcpServers: Record<string, unknown> = {};
|
|
153
|
+
for (const [id, server] of Object.entries(config.servers)) {
|
|
154
|
+
mcpServers[id] = projectTransportToSpecServer(server.transport);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const document = {
|
|
158
|
+
$schema: AGENT_PLUGINS_MCP_SCHEMA_URL,
|
|
159
|
+
mcpServers,
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
163
|
+
const tmpPath = `${path}.tmp`;
|
|
164
|
+
writeFileSync(tmpPath, JSON.stringify(document, null, 2) + "\n");
|
|
165
|
+
renameSync(tmpPath, path);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Replace `config.mcp` on a settings/export payload with the projected
|
|
170
|
+
* workspace servers (transport only, headers stripped). Old clients that
|
|
171
|
+
* still read `mcp.servers` from `/v1/config` keep seeing the same shape.
|
|
172
|
+
*/
|
|
173
|
+
export function overlayWorkspaceMcpForConfigRead(config: unknown): void {
|
|
174
|
+
if (config === null || typeof config !== "object" || Array.isArray(config)) {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
const root = config as Record<string, unknown>;
|
|
178
|
+
const { servers } = loadWorkspaceMcpConfig();
|
|
179
|
+
const projected: Record<string, unknown> = {};
|
|
180
|
+
for (const [id, server] of Object.entries(servers)) {
|
|
181
|
+
const { headers: _headers, ...safeTransport } = server.transport as Record<
|
|
182
|
+
string,
|
|
183
|
+
unknown
|
|
184
|
+
>;
|
|
185
|
+
projected[id] = { transport: safeTransport };
|
|
186
|
+
}
|
|
187
|
+
root.mcp = { servers: projected };
|
|
188
|
+
}
|
|
@@ -56,15 +56,22 @@ export interface MessagingProvider {
|
|
|
56
56
|
query: string,
|
|
57
57
|
options?: SearchOptions,
|
|
58
58
|
): Promise<SearchResult>;
|
|
59
|
-
|
|
59
|
+
|
|
60
|
+
// ── Optional operations (platforms implement what they support) ───
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Send through the provider's own API. A provider whose channel has a
|
|
64
|
+
* direct transport omits this: its sends go through the transport, which
|
|
65
|
+
* is the one send implementation per addressable channel, and the
|
|
66
|
+
* messaging tool routes there before it looks for this method.
|
|
67
|
+
*/
|
|
68
|
+
sendMessage?(
|
|
60
69
|
connection: OAuthConnection | undefined,
|
|
61
70
|
conversationId: string,
|
|
62
71
|
text: string,
|
|
63
72
|
options?: SendOptions,
|
|
64
73
|
): Promise<SendResult>;
|
|
65
74
|
|
|
66
|
-
// ── Optional operations (platforms implement what they support) ───
|
|
67
|
-
|
|
68
75
|
getThreadReplies?(
|
|
69
76
|
connection: OAuthConnection | undefined,
|
|
70
77
|
conversationId: string,
|