@vellumai/assistant 0.12.1 → 0.12.2-staging.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +4 -0
- package/ARCHITECTURE.md +20 -21
- package/docs/architecture/turn-actor.md +15 -8
- package/docs/guardian-request-flow.md +16 -0
- package/node_modules/@vellumai/slack-text/src/index.ts +13 -8
- package/node_modules/@vellumai/slack-text/src/label-resolution-entities.test.ts +95 -0
- package/openapi.yaml +360 -51
- package/package.json +3 -1
- package/scripts/smoke-desktop-window-theme.ts +186 -0
- package/src/__tests__/always-loaded-tools-guard.test.ts +18 -10
- package/src/__tests__/anthropic-provider.test.ts +0 -63
- package/src/__tests__/app-compiler.test.ts +6 -0
- package/src/__tests__/app-control-tool-schemas.test.ts +5 -2
- package/src/__tests__/app-dir-path-guard.test.ts +1 -3
- package/src/__tests__/assistant-event-hub-machine-name.test.ts +6 -7
- package/src/__tests__/assistant-event-hub.test.ts +44 -0
- package/src/__tests__/computer-use-skill-manifest-regression.test.ts +1 -1
- package/src/__tests__/computer-use-skill-proxy-bridge.test.ts +5 -2
- package/src/__tests__/computer-use-tools.test.ts +19 -7
- package/src/__tests__/config-schema-cmd.test.ts +2 -2
- package/src/__tests__/config-schema.test.ts +28 -29
- package/src/__tests__/config-watcher.test.ts +23 -8
- package/src/__tests__/connection-model-compat.test.ts +2 -2
- package/src/__tests__/conversation-key-store-disk-view.test.ts +1 -1
- package/src/__tests__/conversation-pairing.test.ts +26 -0
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +4 -10
- package/src/__tests__/conversation-queue.test.ts +48 -207
- package/src/__tests__/conversation-retry-discard.test.ts +54 -1
- package/src/__tests__/conversation-runtime-assembly.test.ts +28 -0
- package/src/__tests__/conversation-skill-tools.test.ts +27 -0
- package/src/__tests__/conversation-surfaces-app-control.test.ts +8 -3
- package/src/__tests__/conversation-surfaces-point-at-budget.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-point-at-capability.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +67 -0
- package/src/__tests__/conversation-title-service.test.ts +14 -10
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +3 -5
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +51 -0
- package/src/__tests__/credential-record-retire.test.ts +171 -1
- package/src/__tests__/credential-routes.test.ts +250 -3
- package/src/__tests__/credential-security-invariants.test.ts +5 -3
- package/src/__tests__/cu-unified-flow.test.ts +14 -4
- package/src/__tests__/document-sync-tags.test.ts +25 -0
- package/src/__tests__/drain-kick-guard.test.ts +0 -2
- package/src/__tests__/drain-requeue-on-contention.test.ts +0 -6
- package/src/__tests__/evict-conversations-for-reload.test.ts +1 -1
- package/src/__tests__/git-porcelain.test.ts +47 -0
- package/src/__tests__/heartbeat-service.test.ts +56 -40
- package/src/__tests__/helpers/set-workspace-mcp.ts +11 -0
- package/src/__tests__/host-bash-proxy.test.ts +26 -45
- package/src/__tests__/host-browser-proxy.test.ts +6 -17
- package/src/__tests__/host-cu-proxy.test.ts +596 -0
- package/src/__tests__/host-file-proxy-targeted.test.ts +23 -0
- package/src/__tests__/host-file-proxy.test.ts +11 -23
- package/src/__tests__/host-transfer-proxy-targeted.test.ts +30 -9
- package/src/__tests__/list-messages-camera-frame.test.ts +121 -0
- package/src/__tests__/llm-catalog-parity.test.ts +25 -0
- package/src/__tests__/mcp-auth-routes.test.ts +6 -9
- package/src/__tests__/mcp-config-secret-boundary.test.ts +78 -136
- package/src/__tests__/mcp-health-check.test.ts +8 -11
- package/src/__tests__/mcp-list-plugin-servers.test.ts +8 -10
- package/src/__tests__/message-deleted-hook-dispatch.test.ts +119 -0
- package/src/__tests__/messages-after-tiebreaker.test.ts +91 -0
- package/src/__tests__/messages-read-boundary-guard.test.ts +1 -1
- package/src/__tests__/messaging-send-tool.test.ts +355 -548
- package/src/__tests__/model-intents.test.ts +3 -0
- package/src/__tests__/notification-vellum-adapter.test.ts +85 -2
- package/src/__tests__/oauth-commands-routes.test.ts +168 -0
- package/src/__tests__/oauth-provider-profiles.test.ts +18 -0
- package/src/__tests__/oauth-provider-serializer.test.ts +22 -0
- package/src/__tests__/oauth-providers-routes.test.ts +1 -0
- package/src/__tests__/openai-provider.test.ts +1 -0
- package/src/__tests__/outlook-messaging-provider.test.ts +6 -6
- package/src/__tests__/platform-callback-registration.test.ts +8 -16
- package/src/__tests__/platform-client-verify-credential.test.ts +2 -0
- package/src/__tests__/plugin-import-boundary-guard.test.ts +14 -0
- package/src/__tests__/qdrant-manager.test.ts +132 -0
- package/src/__tests__/resolve-app.test.ts +135 -0
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +229 -6
- package/src/__tests__/script-proxy-certs.test.ts +35 -0
- package/src/__tests__/secret-routes-acp-guard.test.ts +59 -1
- package/src/__tests__/service-account-token.test.ts +247 -0
- package/src/__tests__/skill-feature-flags-integration.test.ts +1 -1
- package/src/__tests__/skill-feature-flags.test.ts +2 -2
- package/src/__tests__/skills.test.ts +9 -4
- package/src/__tests__/source-fingerprint.test.ts +184 -0
- package/src/__tests__/subagent-tool-gate-mode.test.ts +70 -3
- package/src/__tests__/subagent-tools.test.ts +3 -2
- package/src/__tests__/telephony-tts-guard.test.ts +38 -3
- package/src/__tests__/test-support/computer-use-skill-harness.ts +3 -2
- package/src/__tests__/ui-channel-variants.test.ts +1 -56
- package/src/__tests__/unicode.test.ts +13 -0
- package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +4 -0
- package/src/__tests__/watch-retro-tool-availability.test.ts +8 -17
- package/src/__tests__/workspace-git-service.test.ts +26 -0
- package/src/__tests__/workspace-migration-154-repair-retired-codex-gpt-5-4-model-ids.test.ts +336 -0
- package/src/acp/__tests__/acp-claude-oauth.test.ts +257 -6
- package/src/acp/__tests__/acp-credentials.test.ts +13 -0
- package/src/acp/__tests__/agent-process.test.ts +258 -4
- package/src/acp/__tests__/auth-required.test.ts +96 -0
- package/src/acp/__tests__/claude-token-refresh.test.ts +257 -0
- package/src/acp/__tests__/client-handler.test.ts +90 -2
- package/src/acp/__tests__/helpers/acp-model-option.ts +51 -0
- package/src/acp/__tests__/prepare-agent-env.test.ts +60 -1
- package/src/acp/__tests__/session-manager-persistence.test.ts +5 -1
- package/src/acp/__tests__/session-manager-resume.test.ts +270 -6
- package/src/acp/__tests__/session-manager-usage.test.ts +4 -2
- package/src/acp/__tests__/session-manager.test.ts +895 -4
- package/src/acp/acp-claude-oauth.ts +328 -14
- package/src/acp/acp-credentials.ts +19 -0
- package/src/acp/agent-process.test.ts +4 -1
- package/src/acp/agent-process.ts +96 -10
- package/src/acp/auth-required.ts +50 -1
- package/src/acp/auto-install.test.ts +9 -7
- package/src/acp/claude-token-refresh.ts +150 -0
- package/src/acp/client-handler.ts +16 -3
- package/src/acp/model-config.test.ts +166 -0
- package/src/acp/model-config.ts +138 -0
- package/src/acp/prepare-agent-env.ts +21 -6
- package/src/acp/resolve-agent.test.ts +220 -6
- package/src/acp/resolve-agent.ts +88 -23
- package/src/acp/session-manager.test.ts +196 -3
- package/src/acp/session-manager.ts +441 -18
- package/src/acp/types.ts +50 -5
- package/src/agent/loop.ts +1 -0
- package/src/api/events/acp-session-model-update.test.ts +88 -0
- package/src/api/events/acp-session-model-update.ts +46 -0
- package/src/api/events/app-files-changed.ts +4 -3
- package/src/api/events/notification-intent.ts +2 -0
- package/src/api/events/user-message-echo.ts +7 -1
- package/src/api/index.ts +6 -0
- package/src/api/responses/conversation-message.ts +5 -0
- package/src/apps/app-store.ts +1 -1
- package/src/apps/resolve-app.ts +81 -0
- package/src/apps/source-fingerprint.ts +266 -0
- package/src/avatar/identity-avatar.ts +7 -2
- package/src/bundler/app-compiler.ts +15 -0
- package/src/calls/__tests__/barge-in-guard.test.ts +81 -0
- package/src/{__tests__ → calls/__tests__}/call-controller.test.ts +503 -82
- package/src/{__tests__ → calls/__tests__}/media-stream-audio-transcode.test.ts +1 -1
- package/src/{__tests__ → calls/__tests__}/media-stream-output.test.ts +37 -8
- package/src/{__tests__ → calls/__tests__}/media-stream-parser.test.ts +1 -1
- package/src/{__tests__ → calls/__tests__}/media-stream-server-integration.test.ts +150 -57
- package/src/{__tests__ → calls/__tests__}/media-stream-stt-session.test.ts +5 -5
- package/src/{__tests__ → calls/__tests__}/media-turn-detector.test.ts +1 -1
- package/src/{live-voice → calls}/__tests__/progress-narration.test.ts +4 -4
- package/src/{live-voice → calls}/__tests__/progress-phrases.test.ts +1 -1
- package/src/calls/__tests__/voice-control-protocol.test.ts +120 -25
- package/src/calls/__tests__/voice-leg-coordinator.test.ts +229 -0
- package/src/calls/__tests__/voice-progress-cadence.test.ts +274 -0
- package/src/{__tests__/voice-session-bridge.test.ts → calls/__tests__/voice-session-bridge-turn-flow.test.ts} +20 -12
- package/src/calls/__tests__/voice-session-bridge.test.ts +56 -1
- package/src/calls/__tests__/voice-triage-escalate.test.ts +105 -0
- package/src/calls/barge-in-guard.ts +118 -0
- package/src/calls/call-constants.ts +24 -4
- package/src/calls/call-controller.ts +499 -199
- package/src/calls/call-transport.ts +16 -0
- package/src/calls/media-stream-output.ts +32 -0
- package/src/calls/media-stream-server.ts +82 -30
- package/src/calls/media-stream-stt-session.ts +15 -0
- package/src/{live-voice → calls}/progress-narration.ts +2 -2
- package/src/calls/resolve-call-tts-provider.ts +33 -2
- package/src/calls/tts-call-strategy.ts +9 -5
- package/src/calls/types.ts +2 -0
- package/src/calls/voice-control-protocol.ts +155 -21
- package/src/calls/voice-leg-coordinator.ts +189 -0
- package/src/calls/voice-progress-cadence.ts +423 -0
- package/src/calls/voice-session-bridge.ts +92 -72
- package/src/calls/voice-triage-escalate.ts +178 -59
- package/src/cli/commands/__tests__/apps.test.ts +245 -0
- package/src/cli/commands/__tests__/bash.test.ts +53 -0
- package/src/cli/commands/__tests__/roadmap.help.test.ts +19 -0
- package/src/cli/commands/apps.help.ts +67 -13
- package/src/cli/commands/apps.ts +183 -0
- package/src/cli/commands/bash.help.ts +8 -13
- package/src/cli/commands/bash.ts +7 -25
- package/src/cli/commands/channels/__tests__/send.test.ts +218 -0
- package/src/cli/commands/channels/index.help.ts +70 -2
- package/src/cli/commands/channels/index.ts +16 -3
- package/src/cli/commands/channels/send.ts +128 -0
- package/src/cli/commands/credentials.help.ts +6 -2
- package/src/cli/commands/credentials.ts +2 -2
- package/src/cli/commands/mcp.help.ts +8 -8
- package/src/cli/commands/mcp.ts +1 -1
- package/src/cli/commands/oauth/status.ts +5 -0
- package/src/cli/commands/roadmap.help.ts +5 -1
- package/src/cli/commands/roadmap.ts +1 -1
- package/src/cli/lib/bundled-marketplace.json +103 -90
- package/src/config/__tests__/default-profile-catalog.test.ts +22 -0
- package/src/config/__tests__/memory-retrospective-schema.test.ts +15 -1
- package/src/config/__tests__/platform-identity.test.ts +255 -0
- package/src/config/__tests__/platform-rehydration.test.ts +99 -9
- package/src/config/acp-agent-write.ts +43 -0
- package/src/config/acp-defaults.test.ts +13 -3
- package/src/config/acp-defaults.ts +23 -6
- package/src/config/acp-schema.test.ts +81 -0
- package/src/config/acp-schema.ts +30 -2
- package/src/config/bundled-skills/acp/SKILL.md +22 -14
- package/src/config/bundled-skills/acp/TOOLS.json +5 -1
- package/src/config/bundled-skills/app-builder/SKILL.md +12 -9
- package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +1 -1
- package/src/config/bundled-skills/computer-use/SKILL.md +29 -2
- package/src/config/bundled-skills/computer-use/TOOLS.json +99 -0
- package/src/config/bundled-skills/computer-use/tools/computer-use-sequence.ts +12 -0
- package/src/config/bundled-skills/media-processing/services/preprocess.ts +2 -1
- package/src/config/bundled-skills/media-processing/services/processing-pipeline.ts +2 -1
- package/src/config/bundled-skills/media-processing/tools/generate-clip.ts +2 -1
- package/src/config/bundled-skills/messaging/SKILL.md +3 -3
- package/src/config/bundled-skills/messaging/TOOLS.json +3 -3
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +67 -139
- package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +1 -1
- package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +2 -1
- package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +2 -1
- package/src/config/bundled-skills/schedule/SKILL.md +3 -4
- package/src/config/bundled-skills/screen-annotation/SKILL.md +8 -1
- package/src/config/bundled-skills/screen-annotation/tools/screen-annotation.test.ts +4 -1
- package/src/config/bundled-skills/skill-management/TOOLS.json +6 -2
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +5 -2
- package/src/config/default-profile-catalog.ts +1 -1
- package/src/config/env.ts +2 -2
- package/src/config/feature-flag-registry.json +33 -13
- package/src/config/loader.ts +2 -0
- package/src/config/memory-v3-gate.test.ts +31 -0
- package/src/config/memory-v3-gate.ts +20 -16
- package/src/config/platform-identity.ts +201 -0
- package/src/config/platform-rehydration.ts +11 -27
- package/src/config/schema.ts +2 -2
- package/src/config/schemas/__tests__/live-voice.test.ts +8 -158
- package/src/config/schemas/__tests__/voice.test.ts +148 -0
- package/src/config/schemas/calls.ts +1 -28
- package/src/config/schemas/live-voice.ts +1 -173
- package/src/config/schemas/mcp.ts +1 -1
- package/src/config/schemas/memory-retrospective.ts +10 -0
- package/src/config/schemas/services.ts +18 -0
- package/src/config/schemas/skills.ts +0 -14
- package/src/config/schemas/voice.ts +150 -0
- package/src/credential-execution/process-manager.test.ts +18 -19
- package/src/credential-health/credential-health-service.ts +11 -17
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +66 -121
- package/src/daemon/__tests__/plugin-mcp-reconcile.test.ts +4 -1
- package/src/daemon/__tests__/skill-memory-refresh-mtime.test.ts +133 -0
- package/src/daemon/app-change-notify.ts +45 -0
- package/src/daemon/config-watcher.ts +13 -194
- package/src/daemon/conversation-error.ts +3 -2
- package/src/daemon/conversation-media-retry.ts +2 -1
- package/src/daemon/conversation-process.ts +35 -64
- package/src/daemon/conversation-runtime-assembly.ts +20 -7
- package/src/daemon/conversation-skill-tools.ts +13 -31
- package/src/daemon/conversation-store.ts +1 -1
- package/src/daemon/conversation-surfaces.ts +21 -10
- package/src/daemon/conversation-tool-setup.ts +45 -59
- package/src/daemon/conversation.ts +0 -3
- package/src/daemon/dictation-profile-store.ts +2 -1
- package/src/daemon/handlers/shared.ts +2 -1
- package/src/daemon/handlers/skills.ts +6 -3
- package/src/daemon/host-bash-proxy.ts +8 -12
- package/src/daemon/host-browser-proxy.ts +15 -1
- package/src/daemon/host-cu-proxy.ts +224 -19
- package/src/daemon/host-file-proxy.ts +8 -10
- package/src/daemon/host-proxy-preactivation.ts +2 -1
- package/src/daemon/host-transfer-proxy.ts +14 -18
- package/src/daemon/lifecycle.ts +0 -5
- package/src/daemon/mcp-reload-service.ts +5 -5
- package/src/daemon/message-types/sync.ts +1 -0
- package/src/daemon/orphan-reaper.ts +1 -1
- package/src/daemon/providers-setup.ts +1 -1
- package/src/daemon/reaction-history-render.ts +2 -1
- package/src/daemon/shutdown-handlers.ts +1 -9
- package/src/daemon/skill-memory-refresh.ts +64 -0
- package/src/daemon/tool-side-effects.ts +7 -18
- package/src/desktop/__tests__/fake-desktop.ts +122 -0
- package/src/desktop/desktop-chrome-policy.test.ts +79 -0
- package/src/desktop/desktop-chrome-policy.ts +47 -0
- package/src/desktop/desktop-chrome-session.ts +37 -0
- package/src/desktop/desktop-dependencies.test.ts +102 -0
- package/src/desktop/desktop-dependencies.ts +308 -0
- package/src/desktop/desktop-feature.test.ts +18 -0
- package/src/desktop/desktop-feature.ts +19 -0
- package/src/desktop/desktop-panel-config.test.ts +135 -0
- package/src/desktop/desktop-panel-config.ts +149 -0
- package/src/desktop/desktop-session-manager.test.ts +730 -0
- package/src/desktop/desktop-session-manager.ts +850 -0
- package/src/desktop/desktop-stream-bridge.test.ts +395 -0
- package/src/desktop/desktop-stream-bridge.ts +214 -0
- package/src/desktop/desktop-wallpaper.test.ts +191 -0
- package/src/desktop/desktop-wallpaper.ts +113 -0
- package/src/desktop/desktop-window-drag.ts +177 -0
- package/src/desktop/desktop-window-manager-config.test.ts +230 -0
- package/src/desktop/desktop-window-manager-config.ts +238 -0
- package/src/desktop/desktop-window-theme.ts +170 -0
- package/src/heartbeat/heartbeat-service.ts +21 -3
- package/src/home/__tests__/feed-writer.test.ts +22 -0
- package/src/home/feed-writer.ts +11 -2
- package/src/hooks/types.ts +33 -0
- package/src/i18n/AGENTS.md +22 -0
- package/src/i18n/__tests__/i18n.test.ts +108 -0
- package/src/i18n/index.ts +16 -0
- package/src/i18n/locales.ts +93 -0
- package/src/i18n/messages.ts +61 -0
- package/src/i18n/title.ts +25 -0
- package/src/inbound/platform-callback-registration.ts +4 -5
- package/src/live-voice/__tests__/live-voice-agent-turn.test.ts +121 -0
- package/src/live-voice/__tests__/live-voice-events.test.ts +8 -7
- package/src/live-voice/__tests__/live-voice-metrics.test.ts +0 -3
- package/src/live-voice/__tests__/live-voice-progress.test.ts +94 -15
- package/src/live-voice/__tests__/live-voice-sight-frame-echo.test.ts +238 -0
- package/src/live-voice/__tests__/live-voice-vad.test.ts +2 -2
- package/src/live-voice/__tests__/protocol.test.ts +39 -0
- package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +13 -69
- package/src/live-voice/__tests__/session-controls.test.ts +79 -0
- package/src/live-voice/live-voice-photo.ts +25 -6
- package/src/live-voice/live-voice-session.ts +297 -699
- package/src/live-voice/protocol.ts +60 -2
- package/src/live-voice/session-controls.ts +111 -0
- package/src/mcp/__tests__/effective-config.test.ts +2 -2
- package/src/mcp/__tests__/workspace-mcp-config.test.ts +135 -0
- package/src/mcp/effective-config.ts +3 -3
- package/src/mcp/mcp-header-store.ts +29 -45
- package/src/mcp/spec-schema.ts +111 -0
- package/src/mcp/startup.ts +8 -4
- package/src/mcp/workspace-mcp-config.ts +188 -0
- package/src/messaging/provider.ts +10 -3
- package/src/messaging/providers/__tests__/proactive-address.test.ts +95 -0
- package/src/messaging/providers/callback-routing.ts +18 -0
- package/src/messaging/providers/channel-transport.ts +56 -0
- package/src/messaging/providers/discord/transport.ts +17 -0
- package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +6 -17
- package/src/messaging/providers/slack/adapter.ts +7 -44
- package/src/messaging/providers/slack/transport.ts +16 -0
- package/src/messaging/providers/telegram-bot/adapter.ts +1 -48
- package/src/messaging/providers/telegram-bot/client.ts +0 -26
- package/src/messaging/providers/telegram-bot/transport.ts +20 -0
- package/src/messaging/providers/whatsapp/adapter.ts +1 -48
- package/src/messaging/providers/whatsapp/transport.ts +18 -0
- package/src/monitoring/__tests__/workspace-git-heartbeat.test.ts +8 -0
- package/src/notifications/__tests__/assistant-reply-producer.test.ts +55 -0
- package/src/notifications/__tests__/guardian-feed-projection.test.ts +158 -2
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +153 -3
- package/src/notifications/__tests__/schedule-result-producer.test.ts +99 -3
- package/src/notifications/adapters/macos.ts +3 -0
- package/src/notifications/assistant-reply-producer.ts +8 -3
- package/src/notifications/conversation-pairing.ts +36 -16
- package/src/notifications/delivered-post-record.ts +11 -1
- package/src/notifications/guardian-feed-projection.ts +125 -5
- package/src/notifications/home-feed-side-effect.ts +55 -7
- package/src/notifications/schedule-result-producer.ts +66 -22
- package/src/oauth/AGENTS.md +2 -0
- package/src/oauth/__tests__/seed-providers-managed.test.ts +149 -0
- package/src/oauth/byo-connection.test.ts +105 -0
- package/src/oauth/byo-connection.ts +36 -6
- package/src/oauth/connection-resolver.test.ts +100 -0
- package/src/oauth/connection-resolver.ts +120 -3
- package/src/oauth/provider-serializer.ts +9 -0
- package/src/oauth/scope-utils.ts +24 -0
- package/src/oauth/seed-providers.ts +314 -27
- package/src/outbound-proxy/certs.ts +3 -5
- package/src/permissions/checker.test.ts +11 -6
- package/src/persistence/compaction-log-store-clickhouse.ts +3 -3
- package/src/persistence/conversation-crud.ts +71 -63
- package/src/persistence/conversation-lineage.ts +12 -4
- package/src/persistence/conversation-queries.ts +2 -2
- package/src/persistence/conversation-title-placeholders.ts +23 -0
- package/src/persistence/conversation-title-service.ts +9 -26
- package/src/persistence/embeddings/qdrant-manager.ts +163 -18
- package/src/persistence/llm-request-log-sink-clickhouse.ts +3 -3
- package/src/persistence/llm-request-log-source-clickhouse.ts +3 -3
- package/src/persistence/message-cursor.ts +78 -0
- package/src/persistence/message-reads.ts +30 -0
- package/src/persistence/migrations/157-invite-contact-id.ts +18 -6
- package/src/persistence/migrations/__tests__/157-invite-contact-id.test.ts +149 -0
- package/src/persistence/schema/memory-core.ts +5 -0
- package/src/platform/client.test.ts +4 -2
- package/src/platform/client.ts +2 -10
- package/src/plugin-api/constants.ts +2 -0
- package/src/plugin-api/index.ts +1 -0
- package/src/plugin-api/types.ts +2 -0
- package/src/plugins/defaults/index.ts +2 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +46 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-backfill.test.ts +118 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-preserve.test.ts +83 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +9 -5
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +20 -10
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-prompt.test.ts +5 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-state.test.ts +219 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-sweep.test.ts +31 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-trigger-check.test.ts +63 -0
- package/src/plugins/defaults/memory/__tests__/shutdown-hook.test.ts +4 -0
- package/src/plugins/defaults/memory/context-search/agent-runner.ts +3 -2
- package/src/plugins/defaults/memory/context-search/format.ts +3 -2
- package/src/plugins/defaults/memory/context-search/sources/memory-v2.ts +6 -3
- package/src/plugins/defaults/memory/context-search/sources/workspace.ts +3 -2
- package/src/plugins/defaults/memory/graph/capability-seed.ts +6 -7
- package/src/plugins/defaults/memory/hooks/init.ts +10 -0
- package/src/plugins/defaults/memory/hooks/message-deleted.ts +21 -0
- package/src/plugins/defaults/memory/hooks/shutdown.ts +2 -0
- package/src/plugins/defaults/memory/host-utils.ts +10 -0
- package/src/plugins/defaults/memory/jobs-worker.ts +13 -0
- package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +34 -60
- package/src/plugins/defaults/memory/memory-retrospective-cursor-backfill.ts +126 -0
- package/src/plugins/defaults/memory/memory-retrospective-cursor-preserve.ts +79 -0
- package/src/plugins/defaults/memory/memory-retrospective-cursor.ts +28 -0
- package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +3 -5
- package/src/plugins/defaults/memory/memory-retrospective-job.ts +12 -3
- package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +1 -1
- package/src/plugins/defaults/memory/memory-retrospective-state.ts +312 -71
- package/src/plugins/defaults/memory/memory-retrospective-sweep.ts +2 -1
- package/src/plugins/defaults/memory/memory-retrospective-trigger-check.ts +5 -3
- package/src/plugins/defaults/memory/skill-md-mtime-poll.ts +9 -0
- package/src/plugins/defaults/memory/startup.ts +28 -2
- package/src/plugins/defaults/memory/substrate/__tests__/skill-content.test.ts +7 -9
- package/src/plugins/defaults/memory/substrate/page-index.ts +6 -1
- package/src/plugins/defaults/memory/substrate/skill-content.ts +2 -2
- package/src/plugins/defaults/memory/v1/graph/consolidation.ts +4 -2
- package/src/plugins/defaults/memory/v1/graph/extraction.ts +4 -2
- package/src/plugins/defaults/memory/v1/graph/retriever.ts +4 -1
- package/src/plugins/defaults/memory/v2/reranker.ts +6 -1
- package/src/plugins/defaults/memory/v3/__tests__/card.test.ts +24 -0
- package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +125 -0
- package/src/plugins/defaults/memory/v3/__tests__/sections.test.ts +26 -0
- package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +30 -0
- package/src/plugins/defaults/memory/v3/card.ts +3 -2
- package/src/plugins/defaults/memory/v3/pool-select.ts +5 -3
- package/src/plugins/defaults/memory/v3/sections.ts +9 -3
- package/src/plugins/defaults/memory/v3/shadow-plugin.ts +5 -2
- package/src/plugins/defaults/memory/worker.ts +6 -0
- package/src/plugins/mcp-servers.ts +15 -75
- package/src/providers/__tests__/connection-model-compat.test.ts +3 -3
- package/src/providers/__tests__/retry-callsite.test.ts +41 -0
- package/src/providers/__tests__/retry-outbound-sanitize.test.ts +176 -0
- package/src/providers/anthropic/client.ts +0 -47
- package/src/providers/gemini/client.ts +9 -23
- package/src/providers/gemini-thought-signature.test.ts +63 -0
- package/src/providers/gemini-thought-signature.ts +44 -0
- package/src/providers/inference/__tests__/endpoint-probe.test.ts +42 -3
- package/src/providers/inference/auth.ts +2 -3
- package/src/providers/inference/connection-availability.ts +27 -8
- package/src/providers/inference/endpoint-probe.ts +10 -1
- package/src/providers/inference/resolve-auth.ts +22 -4
- package/src/providers/inference/service-account-token.ts +248 -0
- package/src/providers/model-catalog.ts +34 -0
- package/src/providers/model-intents.ts +10 -5
- package/src/providers/openai/__tests__/chat-completions-output-token-limit.test.ts +141 -0
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +146 -18
- package/src/providers/openai/__tests__/chat-completions-provider-thought-signature.test.ts +613 -0
- package/src/providers/openai/__tests__/google-thought-signature.test.ts +235 -0
- package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +83 -1
- package/src/providers/openai/chat-completions-provider.ts +212 -79
- package/src/providers/openai/codex-models.ts +0 -4
- package/src/providers/openai/google-thought-signature.ts +320 -0
- package/src/providers/opencode/client.test.ts +73 -4
- package/src/providers/opencode/client.ts +60 -3
- package/src/providers/openrouter/client.ts +20 -0
- package/src/providers/outbound-request-sanitize.ts +96 -0
- package/src/providers/retry.ts +30 -19
- package/src/providers/unparseable-tool-args.ts +5 -1
- package/src/runtime/AGENTS.md +3 -1
- package/src/runtime/__tests__/channel-send.test.ts +443 -0
- package/src/runtime/__tests__/desktop-stream-upgrade.test.ts +89 -0
- package/src/runtime/__tests__/host-observe.test.ts +40 -1
- package/src/runtime/__tests__/live-voice-upgrade.test.ts +58 -0
- package/src/runtime/__tests__/runtime-ws-test-utils.ts +72 -0
- package/src/runtime/__tests__/watch-stream-upgrade.test.ts +59 -0
- package/src/runtime/agent-wake.ts +2 -1
- package/src/runtime/assistant-event-hub.ts +21 -3
- package/src/runtime/auth/same-actor.ts +38 -3
- package/src/runtime/channel-send.ts +316 -0
- package/src/runtime/host-observe.ts +11 -1
- package/src/runtime/http-server.ts +160 -151
- package/src/runtime/routes/__tests__/acp-claude-auth-routes.test.ts +16 -5
- package/src/runtime/routes/__tests__/acp-routes.test.ts +159 -4
- package/src/runtime/routes/__tests__/apps-refresh-route.test.ts +275 -0
- package/src/runtime/routes/__tests__/channel-send-routes.test.ts +127 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +108 -1
- package/src/runtime/routes/__tests__/default-provider-routes.test.ts +2 -2
- package/src/runtime/routes/__tests__/dictation-cleanup-guard.test.ts +107 -0
- package/src/runtime/routes/__tests__/mcp-add-default-risk.test.ts +16 -12
- package/src/runtime/routes/acp-claude-auth-routes.ts +11 -3
- package/src/runtime/routes/acp-routes.test.ts +15 -0
- package/src/runtime/routes/acp-routes.ts +72 -9
- package/src/runtime/routes/app-management-routes.ts +90 -2
- package/src/runtime/routes/app-routes.ts +1 -1
- package/src/runtime/routes/channel-send-routes.ts +85 -0
- package/src/runtime/routes/conversation-list-routes.ts +14 -3
- package/src/runtime/routes/conversation-query-routes.ts +40 -0
- package/src/runtime/routes/conversation-routes.ts +16 -3
- package/src/runtime/routes/credential-routes.ts +20 -14
- package/src/runtime/routes/desktop-setup-routes.test.ts +74 -0
- package/src/runtime/routes/desktop-setup-routes.ts +35 -0
- package/src/runtime/routes/diagnostics-routes.ts +61 -4
- package/src/runtime/routes/events-routes.ts +4 -0
- package/src/runtime/routes/host-cu-routes.ts +6 -0
- package/src/runtime/routes/index.ts +4 -2
- package/src/runtime/routes/log-export-routes.ts +2 -0
- package/src/runtime/routes/mcp-auth-routes.ts +39 -47
- package/src/runtime/routes/migration-routes.ts +6 -20
- package/src/runtime/routes/oauth-commands-routes.ts +50 -0
- package/src/runtime/routes/oauth-providers.ts +7 -0
- package/src/runtime/routes/platform-routes.ts +18 -15
- package/src/runtime/routes/workspace-routes.test.ts +55 -26
- package/src/runtime/routes/workspace-routes.ts +46 -63
- package/src/runtime/routes/workspace-tree-walk.test.ts +244 -0
- package/src/runtime/routes/workspace-tree-walk.ts +240 -0
- package/src/runtime/routes/workspace-utils.ts +17 -0
- package/src/runtime/services/__tests__/conversation-serializer.test.ts +28 -0
- package/src/runtime/services/conversation-serializer.ts +16 -3
- package/src/runtime/sync/resource-sync-events.ts +2 -1
- package/src/schedule/run-script.ts +2 -1
- package/src/schedule/schedule-store.ts +9 -2
- package/src/schedule/worker.ts +1 -1
- package/src/security/__tests__/ces-http-record-backend.test.ts +137 -0
- package/src/security/ces-http-record-backend.ts +120 -0
- package/src/security/ces-rpc-record-backend.ts +7 -0
- package/src/security/untrusted-content.ts +3 -1
- package/src/skills/__tests__/workspace-skill-md-signature.test.ts +60 -0
- package/src/skills/workspace-skill-md-signature.ts +50 -0
- package/src/subagent/consult-context.ts +12 -25
- package/src/tools/__tests__/side-effect-abort-guard.test.ts +1 -0
- package/src/tools/acp/list-agents.test.ts +3 -1
- package/src/tools/acp/spawn.test.ts +146 -11
- package/src/tools/acp/spawn.ts +21 -3
- package/src/tools/acp/status.test.ts +94 -0
- package/src/tools/acp/status.ts +40 -3
- package/src/tools/app-control/skill-proxy-bridge.ts +2 -1
- package/src/tools/browser/browser-execution.ts +1 -1
- package/src/tools/browser/cdp-client/accessibility-snapshot.ts +9 -3
- package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +2 -1
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +3 -2
- package/src/tools/browser/network-recorder.ts +2 -1
- package/src/tools/capability-offer.test.ts +22 -2
- package/src/tools/capability-offer.ts +16 -1
- package/src/tools/computer-use/definitions.ts +121 -2
- package/src/tools/computer-use/skill-proxy-bridge.ts +2 -1
- package/src/tools/credentials/metadata-store.ts +100 -0
- package/src/tools/filesystem/search.ts +2 -1
- package/src/tools/host-filesystem/edit.test.ts +2 -1
- package/src/tools/host-filesystem/edit.ts +2 -2
- package/src/tools/host-filesystem/read.test.ts +2 -1
- package/src/tools/host-filesystem/read.ts +2 -2
- package/src/tools/host-filesystem/transfer.test.ts +2 -1
- package/src/tools/host-filesystem/transfer.ts +2 -2
- package/src/tools/host-filesystem/write.test.ts +2 -1
- package/src/tools/host-filesystem/write.ts +2 -2
- package/src/tools/host-terminal/host-shell.test.ts +16 -0
- package/src/tools/host-terminal/host-shell.ts +4 -20
- package/src/tools/network/web-search.ts +6 -1
- package/src/tools/policy-context.ts +6 -7
- package/src/tools/skills/scaffold-managed.ts +61 -3
- package/src/tools/subagent/spawn.ts +5 -8
- package/src/tools/terminal/__tests__/sanitized-bash.test.ts +57 -0
- package/src/tools/terminal/sanitized-bash.ts +105 -0
- package/src/tools/ui-surface/channel-variants.ts +10 -59
- package/src/tools/watch/watch-retro-report.ts +0 -8
- package/src/types/host-capabilities.ts +5 -3
- package/src/util/__tests__/language-subtag.test.ts +23 -1
- package/src/util/ca-bundle.ts +13 -0
- package/src/util/language-subtag.ts +16 -0
- package/src/util/platform.ts +8 -0
- package/src/util/unicode.ts +15 -1
- package/src/watch/watch-retro.ts +6 -8
- package/src/watch/watch-timeline.ts +2 -2
- package/src/workflows/run-manager.ts +2 -1
- package/src/workspace/git-porcelain.ts +66 -0
- package/src/workspace/git-service.ts +6 -55
- package/src/workspace/migrations/154-repair-retired-codex-gpt-5-4-model-ids.ts +258 -0
- package/src/workspace/migrations/155-move-front-model-config-to-voice.ts +78 -0
- package/src/workspace/migrations/156-extract-workspace-mcp-json.ts +214 -0
- package/src/workspace/migrations/__tests__/155-move-front-model-config-to-voice.test.ts +102 -0
- package/src/workspace/migrations/__tests__/156-extract-workspace-mcp-json.test.ts +199 -0
- package/src/workspace/migrations/registry.ts +6 -0
- package/src/__tests__/app-source-watcher.test.ts +0 -218
- package/src/__tests__/config-watcher-skill-reseed.test.ts +0 -359
- package/src/__tests__/speaker-identification.test.ts +0 -56
- package/src/calls/speaker-identification.ts +0 -233
- package/src/daemon/app-source-watcher.ts +0 -218
- package/src/messaging/providers/whatsapp/client.ts +0 -29
- package/src/runtime/routes/debug-bash-routes.ts +0 -173
- package/src/util/fs-watcher-error.ts +0 -36
- /package/src/{live-voice → calls}/progress-phrases.ts +0 -0
|
@@ -22,6 +22,7 @@ import { join } from "node:path";
|
|
|
22
22
|
import { and, desc, eq, gte, lte } from "drizzle-orm";
|
|
23
23
|
import { z } from "zod";
|
|
24
24
|
|
|
25
|
+
import { overlayWorkspaceMcpForConfigRead } from "../../mcp/workspace-mcp-config.js";
|
|
25
26
|
import { getDb, getLogsDb } from "../../persistence/db-connection.js";
|
|
26
27
|
import {
|
|
27
28
|
llmRequestLogs,
|
|
@@ -568,6 +569,7 @@ function readSanitizedConfig(): Record<string, unknown> | undefined {
|
|
|
568
569
|
}
|
|
569
570
|
}
|
|
570
571
|
|
|
572
|
+
overlayWorkspaceMcpForConfigRead(config);
|
|
571
573
|
const mcpServers = asRecord(asRecord(config.mcp)?.servers);
|
|
572
574
|
for (const server of Object.values(mcpServers ?? {})) {
|
|
573
575
|
const transport = asRecord(asRecord(server)?.transport);
|
|
@@ -14,8 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
import { z } from "zod";
|
|
16
16
|
|
|
17
|
-
import {
|
|
18
|
-
import type { McpConfig, McpServerConfig } from "../../config/schemas/mcp.js";
|
|
17
|
+
import type { McpServerConfig } from "../../config/schemas/mcp.js";
|
|
19
18
|
import { estimateToolDefinitionTokens } from "../../context/token-estimator.js";
|
|
20
19
|
import { reloadMcpServers } from "../../daemon/mcp-reload-service.js";
|
|
21
20
|
import { McpClient } from "../../mcp/client.js";
|
|
@@ -31,6 +30,10 @@ import {
|
|
|
31
30
|
deleteMcpOAuthCredentials,
|
|
32
31
|
hasMcpOAuthTokens,
|
|
33
32
|
} from "../../mcp/mcp-oauth-provider.js";
|
|
33
|
+
import {
|
|
34
|
+
loadWorkspaceMcpConfig,
|
|
35
|
+
saveWorkspaceMcpConfig,
|
|
36
|
+
} from "../../mcp/workspace-mcp-config.js";
|
|
34
37
|
import { readPluginMcpServers } from "../../plugins/mcp-servers.js";
|
|
35
38
|
import { getMcpToolsByServer } from "../../tools/registry.js";
|
|
36
39
|
import { getLogger } from "../../util/logger.js";
|
|
@@ -65,6 +68,18 @@ const McpAddParams = z.object({
|
|
|
65
68
|
|
|
66
69
|
const McpRemoveParams = z.object({ name: z.string() });
|
|
67
70
|
|
|
71
|
+
function persistWorkspaceMcpConfig(config: {
|
|
72
|
+
servers: Record<string, McpServerConfig>;
|
|
73
|
+
}): void {
|
|
74
|
+
try {
|
|
75
|
+
saveWorkspaceMcpConfig(config);
|
|
76
|
+
} catch (err) {
|
|
77
|
+
throw new InternalError(
|
|
78
|
+
err instanceof Error ? err.message : String(err),
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
68
83
|
async function handleMcpAuthStart({
|
|
69
84
|
body,
|
|
70
85
|
}: {
|
|
@@ -76,8 +91,7 @@ async function handleMcpAuthStart({
|
|
|
76
91
|
}> {
|
|
77
92
|
const { serverId } = parseBody(McpServerIdParams, body);
|
|
78
93
|
|
|
79
|
-
const
|
|
80
|
-
const servers = (raw.mcp as Partial<McpConfig> | undefined)?.servers ?? {};
|
|
94
|
+
const servers = loadWorkspaceMcpConfig().servers;
|
|
81
95
|
const serverConfig = servers[serverId];
|
|
82
96
|
|
|
83
97
|
if (!serverConfig) {
|
|
@@ -253,9 +267,7 @@ function detectAuthType(headers: Record<string, string>): "bearer" | "api-key" {
|
|
|
253
267
|
async function handleMcpList(_args: {
|
|
254
268
|
body?: Record<string, unknown>;
|
|
255
269
|
}): Promise<{ servers: McpServerEntry[] }> {
|
|
256
|
-
const
|
|
257
|
-
const mcpConfig = raw.mcp as Partial<McpConfig> | undefined;
|
|
258
|
-
const servers = mcpConfig?.servers ?? {};
|
|
270
|
+
const servers = loadWorkspaceMcpConfig().servers;
|
|
259
271
|
const configEntries = (
|
|
260
272
|
Object.entries(servers) as [string, McpServerConfig][]
|
|
261
273
|
).filter(([, config]) => config && typeof config === "object");
|
|
@@ -434,18 +446,13 @@ async function handleMcpUpdate({
|
|
|
434
446
|
}): Promise<{ updated: true }> {
|
|
435
447
|
const { name, headers } = parseBody(McpUpdateParams, body);
|
|
436
448
|
|
|
437
|
-
const
|
|
438
|
-
const
|
|
439
|
-
const serverMap = mcpConfig?.servers as
|
|
440
|
-
| Record<string, Record<string, unknown>>
|
|
441
|
-
| undefined;
|
|
449
|
+
const servers = loadWorkspaceMcpConfig().servers;
|
|
450
|
+
const server = servers[name];
|
|
442
451
|
|
|
443
|
-
if (!
|
|
452
|
+
if (!server) {
|
|
444
453
|
throw new NotFoundError(`MCP server "${name}" not found.`);
|
|
445
454
|
}
|
|
446
455
|
|
|
447
|
-
const server = serverMap[name];
|
|
448
|
-
|
|
449
456
|
if (headers !== undefined) {
|
|
450
457
|
const transport = server.transport as Record<string, unknown> | undefined;
|
|
451
458
|
if (
|
|
@@ -474,7 +481,7 @@ async function handleMcpUpdate({
|
|
|
474
481
|
}
|
|
475
482
|
}
|
|
476
483
|
|
|
477
|
-
|
|
484
|
+
persistWorkspaceMcpConfig({ servers });
|
|
478
485
|
triggerReload("internal_mcp_update");
|
|
479
486
|
|
|
480
487
|
return { updated: true };
|
|
@@ -494,7 +501,7 @@ async function handleMcpAdd({
|
|
|
494
501
|
body,
|
|
495
502
|
);
|
|
496
503
|
|
|
497
|
-
let transport:
|
|
504
|
+
let transport: McpServerConfig["transport"];
|
|
498
505
|
switch (transportType) {
|
|
499
506
|
case "stdio":
|
|
500
507
|
if (!command) {
|
|
@@ -517,27 +524,16 @@ async function handleMcpAdd({
|
|
|
517
524
|
);
|
|
518
525
|
}
|
|
519
526
|
|
|
520
|
-
const
|
|
521
|
-
if (!raw.mcp) {
|
|
522
|
-
raw.mcp = { servers: {} };
|
|
523
|
-
}
|
|
524
|
-
const mcpConfig = raw.mcp as Record<string, unknown>;
|
|
525
|
-
if (!mcpConfig.servers) {
|
|
526
|
-
mcpConfig.servers = {};
|
|
527
|
-
}
|
|
528
|
-
const serverMap = mcpConfig.servers as Record<string, unknown>;
|
|
527
|
+
const servers = { ...loadWorkspaceMcpConfig().servers };
|
|
529
528
|
|
|
530
|
-
if (
|
|
529
|
+
if (servers[name]) {
|
|
531
530
|
throw new BadRequestError(
|
|
532
531
|
`MCP server "${name}" already exists. Remove it first with: assistant mcp remove ${name}`,
|
|
533
532
|
);
|
|
534
533
|
}
|
|
535
534
|
|
|
536
|
-
|
|
537
|
-
transport,
|
|
538
|
-
};
|
|
535
|
+
servers[name] = { transport };
|
|
539
536
|
|
|
540
|
-
// Store auth headers in credential store, not config
|
|
541
537
|
if (headers && Object.keys(headers).length > 0) {
|
|
542
538
|
const ok = await setMcpHeaders(name, headers);
|
|
543
539
|
if (!ok) {
|
|
@@ -547,7 +543,7 @@ async function handleMcpAdd({
|
|
|
547
543
|
}
|
|
548
544
|
}
|
|
549
545
|
|
|
550
|
-
|
|
546
|
+
persistWorkspaceMcpConfig({ servers });
|
|
551
547
|
triggerReload("internal_mcp_add");
|
|
552
548
|
|
|
553
549
|
return { added: true };
|
|
@@ -564,8 +560,7 @@ async function handleMcpAuthRevoke({
|
|
|
564
560
|
}): Promise<{ revoked: true }> {
|
|
565
561
|
const { serverId } = parseBody(McpServerIdParams, body);
|
|
566
562
|
|
|
567
|
-
const
|
|
568
|
-
const servers = (raw.mcp as Partial<McpConfig> | undefined)?.servers ?? {};
|
|
563
|
+
const servers = loadWorkspaceMcpConfig().servers;
|
|
569
564
|
const serverConfig = servers[serverId];
|
|
570
565
|
|
|
571
566
|
if (!serverConfig) {
|
|
@@ -602,32 +597,29 @@ async function handleMcpRemove({
|
|
|
602
597
|
}): Promise<{ removed: true }> {
|
|
603
598
|
const { name } = parseBody(McpRemoveParams, body);
|
|
604
599
|
|
|
605
|
-
const
|
|
606
|
-
const
|
|
607
|
-
const serverMap = mcpConfig?.servers as Record<string, unknown> | undefined;
|
|
600
|
+
const servers = { ...loadWorkspaceMcpConfig().servers };
|
|
601
|
+
const serverConfig = servers[name];
|
|
608
602
|
|
|
609
|
-
if (!
|
|
603
|
+
if (!serverConfig) {
|
|
610
604
|
throw new NotFoundError(`MCP server "${name}" not found.`);
|
|
611
605
|
}
|
|
612
606
|
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
| undefined;
|
|
618
|
-
if (transport?.type === "sse" || transport?.type === "streamable-http") {
|
|
607
|
+
if (
|
|
608
|
+
serverConfig.transport.type === "sse" ||
|
|
609
|
+
serverConfig.transport.type === "streamable-http"
|
|
610
|
+
) {
|
|
619
611
|
try {
|
|
620
612
|
await Promise.all([
|
|
621
613
|
deleteMcpOAuthCredentials(name),
|
|
622
614
|
deleteMcpHeaders(name),
|
|
623
615
|
]);
|
|
624
616
|
} catch {
|
|
625
|
-
//
|
|
617
|
+
// Credentials may not exist.
|
|
626
618
|
}
|
|
627
619
|
}
|
|
628
620
|
|
|
629
|
-
delete
|
|
630
|
-
|
|
621
|
+
delete servers[name];
|
|
622
|
+
persistWorkspaceMcpConfig({ servers });
|
|
631
623
|
triggerReload("internal_mcp_remove");
|
|
632
624
|
|
|
633
625
|
return { removed: true };
|
|
@@ -22,8 +22,8 @@ import { PassThrough, Readable } from "node:stream";
|
|
|
22
22
|
|
|
23
23
|
import { z } from "zod";
|
|
24
24
|
|
|
25
|
-
import { getPlatformAssistantId } from "../../config/env.js";
|
|
26
25
|
import { invalidateConfigCache } from "../../config/loader.js";
|
|
26
|
+
import { resolvePlatformAssistantId } from "../../config/platform-identity.js";
|
|
27
27
|
import { getAssistantName } from "../../daemon/identity-helpers.js";
|
|
28
28
|
import { runAsyncSqlite } from "../../persistence/db-async-query.js";
|
|
29
29
|
import {
|
|
@@ -236,28 +236,14 @@ interface ExportManifestInputs {
|
|
|
236
236
|
/**
|
|
237
237
|
* Resolve the `assistant.id` for an export.
|
|
238
238
|
*
|
|
239
|
-
*
|
|
240
|
-
*
|
|
241
|
-
* fallback. The schema requires `id` to be non-empty, so we fall back to
|
|
239
|
+
* In-memory identity (validate rehydration or secret-routes). The schema
|
|
240
|
+
* requires `id` to be non-empty, so we fall back to
|
|
242
241
|
* `DAEMON_INTERNAL_ASSISTANT_ID` rather than the empty string.
|
|
243
242
|
*/
|
|
244
243
|
async function resolveAssistantId(): Promise<string> {
|
|
245
|
-
const
|
|
246
|
-
if (
|
|
247
|
-
return
|
|
248
|
-
}
|
|
249
|
-
try {
|
|
250
|
-
const stored = await getSecureKeyAsync(
|
|
251
|
-
credentialKey("vellum", "platform_assistant_id"),
|
|
252
|
-
);
|
|
253
|
-
if (stored) {
|
|
254
|
-
return stored;
|
|
255
|
-
}
|
|
256
|
-
} catch (err) {
|
|
257
|
-
log.warn(
|
|
258
|
-
{ err },
|
|
259
|
-
"Failed to read platform_assistant_id from credential store; falling back to daemon-internal id",
|
|
260
|
-
);
|
|
244
|
+
const resolved = await resolvePlatformAssistantId();
|
|
245
|
+
if (resolved) {
|
|
246
|
+
return resolved;
|
|
261
247
|
}
|
|
262
248
|
return DAEMON_INTERNAL_ASSISTANT_ID;
|
|
263
249
|
}
|
|
@@ -41,9 +41,11 @@ import {
|
|
|
41
41
|
listConnections,
|
|
42
42
|
type OAuthProviderRow,
|
|
43
43
|
} from "../../oauth/oauth-store.js";
|
|
44
|
+
import { missingScopesForStoredToken } from "../../oauth/scope-utils.js";
|
|
44
45
|
import { VellumPlatformClient } from "../../platform/client.js";
|
|
45
46
|
import { withValidToken } from "../../security/token-manager.js";
|
|
46
47
|
import { matchHostPattern } from "../../tools/credentials/host-pattern-match.js";
|
|
48
|
+
import { parseJsonSafe } from "../../util/json.js";
|
|
47
49
|
import { getLogger } from "../../util/logger.js";
|
|
48
50
|
import {
|
|
49
51
|
findContentTypeHeader,
|
|
@@ -64,6 +66,7 @@ interface PlatformConnectionEntry {
|
|
|
64
66
|
id: string;
|
|
65
67
|
account_label?: string;
|
|
66
68
|
scopes_granted?: string[];
|
|
69
|
+
provider_params?: Record<string, string> | null;
|
|
67
70
|
status?: string;
|
|
68
71
|
}
|
|
69
72
|
|
|
@@ -230,6 +233,31 @@ function assertOAuthRequestUrlAllowed(
|
|
|
230
233
|
}
|
|
231
234
|
}
|
|
232
235
|
|
|
236
|
+
/**
|
|
237
|
+
* Required scopes the resolved connection's stored grant lacks. Credential
|
|
238
|
+
* health measures the same thing on the heartbeat; measuring it on the
|
|
239
|
+
* request names the gap at the moment a call may depend on it, since a
|
|
240
|
+
* connection made before a scope was required keeps working for every call
|
|
241
|
+
* that does not need it. A managed connection has no local row and reports
|
|
242
|
+
* nothing.
|
|
243
|
+
*/
|
|
244
|
+
function missingScopesForConnection(
|
|
245
|
+
providerRow: OAuthProviderRow,
|
|
246
|
+
connectionId: string,
|
|
247
|
+
): string[] {
|
|
248
|
+
const row = getConnection(connectionId);
|
|
249
|
+
if (!row) {
|
|
250
|
+
return [];
|
|
251
|
+
}
|
|
252
|
+
return missingScopesForStoredToken(
|
|
253
|
+
parseJsonSafe<string[]>(providerRow.defaultScopes ?? "[]") ?? [],
|
|
254
|
+
parseJsonSafe<Record<string, string>>(providerRow.authorizeParams ?? "") ??
|
|
255
|
+
undefined,
|
|
256
|
+
providerRow.scopeSeparator,
|
|
257
|
+
parseJsonSafe<string[]>(row.grantedScopes ?? "[]") ?? [],
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
|
|
233
261
|
// ---------------------------------------------------------------------------
|
|
234
262
|
// Disconnect handler
|
|
235
263
|
// ---------------------------------------------------------------------------
|
|
@@ -547,6 +575,9 @@ async function handleStatus({ queryParams = {} }: RouteHandlerArgs) {
|
|
|
547
575
|
account: c.account_label ?? null,
|
|
548
576
|
grantedScopes: c.scopes_granted ?? [],
|
|
549
577
|
status: c.status ?? "ACTIVE",
|
|
578
|
+
// Values the provider scopes the connection by (QuickBooks' realm id),
|
|
579
|
+
// so a caller can address resources the proxy's base URL does not.
|
|
580
|
+
providerParams: c.provider_params ?? {},
|
|
550
581
|
}));
|
|
551
582
|
|
|
552
583
|
return {
|
|
@@ -1002,6 +1033,15 @@ export async function handleRequest({ body = {} }: RouteHandlerArgs) {
|
|
|
1002
1033
|
`(e.g. https://host/full/path) so the host and full path are set explicitly.`;
|
|
1003
1034
|
}
|
|
1004
1035
|
|
|
1036
|
+
const missingScopes = missingScopesForConnection(providerRow, connection.id);
|
|
1037
|
+
if (missingScopes.length > 0) {
|
|
1038
|
+
const scopeHint =
|
|
1039
|
+
`The ${b.provider} connection is missing required scopes: ${missingScopes.join(", ")}. ` +
|
|
1040
|
+
`It was connected before they were required, so calls that need them fail. ` +
|
|
1041
|
+
`Reconnect it from Integrations, or run 'assistant oauth connect ${b.provider}', to grant them.`;
|
|
1042
|
+
result.hint = result.hint ? `${scopeHint}\n\n${result.hint}` : scopeHint;
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1005
1045
|
return result;
|
|
1006
1046
|
}
|
|
1007
1047
|
|
|
@@ -1024,6 +1064,8 @@ async function handleManagedConnect({ body = {} }: RouteHandlerArgs) {
|
|
|
1024
1064
|
provider: string;
|
|
1025
1065
|
scopes?: string[];
|
|
1026
1066
|
redirect_after_connect?: string;
|
|
1067
|
+
/** Per-tenant providers (Shopify): the customer's own host. */
|
|
1068
|
+
tenant_host?: string;
|
|
1027
1069
|
};
|
|
1028
1070
|
|
|
1029
1071
|
if (!b.provider) {
|
|
@@ -1040,6 +1082,13 @@ async function handleManagedConnect({ body = {} }: RouteHandlerArgs) {
|
|
|
1040
1082
|
}
|
|
1041
1083
|
reqBody.redirect_after_connect =
|
|
1042
1084
|
b.redirect_after_connect ?? "/account/oauth/complete";
|
|
1085
|
+
// Only forwarded when present: the platform validates it against the
|
|
1086
|
+
// provider's pattern and rejects per-tenant providers that omit it.
|
|
1087
|
+
const tenantHost =
|
|
1088
|
+
typeof b.tenant_host === "string" ? b.tenant_host.trim() : "";
|
|
1089
|
+
if (tenantHost) {
|
|
1090
|
+
reqBody.tenant_host = tenantHost;
|
|
1091
|
+
}
|
|
1043
1092
|
|
|
1044
1093
|
const response = await client.fetch(startPath, {
|
|
1045
1094
|
method: "POST",
|
|
@@ -1086,6 +1135,7 @@ async function handleManagedConnectPoll({
|
|
|
1086
1135
|
id: e.id,
|
|
1087
1136
|
account_label: e.account_label ?? null,
|
|
1088
1137
|
scopes_granted: e.scopes_granted ?? [],
|
|
1138
|
+
provider_params: e.provider_params ?? {},
|
|
1089
1139
|
})),
|
|
1090
1140
|
};
|
|
1091
1141
|
}
|
|
@@ -397,6 +397,13 @@ export const oauthProviderSummarySchema = z.object({
|
|
|
397
397
|
supports_managed_mode: z.boolean(),
|
|
398
398
|
managed_service_is_paid: z.boolean(),
|
|
399
399
|
feature_flag: z.string().nullable(),
|
|
400
|
+
tenant_host: z
|
|
401
|
+
.object({
|
|
402
|
+
pattern: z.string(),
|
|
403
|
+
label: z.string(),
|
|
404
|
+
placeholder: z.string(),
|
|
405
|
+
})
|
|
406
|
+
.nullable(),
|
|
400
407
|
acts_as: z.enum(["user", "assistant"]),
|
|
401
408
|
});
|
|
402
409
|
|
|
@@ -39,7 +39,17 @@ import {
|
|
|
39
39
|
} from "@vellumai/service-contracts/platform-credential";
|
|
40
40
|
import { z } from "zod";
|
|
41
41
|
|
|
42
|
+
import {
|
|
43
|
+
setPlatformAssistantId,
|
|
44
|
+
setPlatformBaseUrl,
|
|
45
|
+
setPlatformOrganizationId,
|
|
46
|
+
setPlatformUserId,
|
|
47
|
+
} from "../../config/env.js";
|
|
42
48
|
import { isPlatformRemote } from "../../config/env-registry.js";
|
|
49
|
+
import {
|
|
50
|
+
resolvePlatformOrganizationId,
|
|
51
|
+
resolvePlatformUserId,
|
|
52
|
+
} from "../../config/platform-identity.js";
|
|
43
53
|
import {
|
|
44
54
|
registerCallbackRoute,
|
|
45
55
|
resolvePlatformCallbackRegistrationContext,
|
|
@@ -289,24 +299,12 @@ async function handlePlatformStatus(
|
|
|
289
299
|
): Promise<PlatformStatusResponse> {
|
|
290
300
|
const context = await resolvePlatformCallbackRegistrationContext();
|
|
291
301
|
|
|
292
|
-
const [
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
CREDENTIAL_KEYS.organizationId.service,
|
|
296
|
-
CREDENTIAL_KEYS.organizationId.field,
|
|
297
|
-
),
|
|
298
|
-
),
|
|
299
|
-
getSecureKeyAsync(
|
|
300
|
-
credentialKey(
|
|
301
|
-
CREDENTIAL_KEYS.userId.service,
|
|
302
|
-
CREDENTIAL_KEYS.userId.field,
|
|
303
|
-
),
|
|
304
|
-
),
|
|
302
|
+
const [organizationId, userId, webhookSecretRaw] = await Promise.all([
|
|
303
|
+
resolvePlatformOrganizationId(),
|
|
304
|
+
resolvePlatformUserId(),
|
|
305
305
|
getSecureKeyAsync(credentialKey("vellum", "webhook_secret")),
|
|
306
306
|
]);
|
|
307
307
|
|
|
308
|
-
const organizationId = orgIdRaw?.trim() ?? "";
|
|
309
|
-
const userId = userIdRaw?.trim() ?? "";
|
|
310
308
|
const hasWebhookSecret = !!webhookSecretRaw;
|
|
311
309
|
|
|
312
310
|
return {
|
|
@@ -467,6 +465,11 @@ async function handlePlatformDisconnect(
|
|
|
467
465
|
);
|
|
468
466
|
}
|
|
469
467
|
|
|
468
|
+
setPlatformBaseUrl(undefined);
|
|
469
|
+
setPlatformAssistantId(undefined);
|
|
470
|
+
setPlatformOrganizationId(undefined);
|
|
471
|
+
setPlatformUserId(undefined);
|
|
472
|
+
|
|
470
473
|
// Notify connected clients
|
|
471
474
|
broadcastMessage({ type: "platform_disconnected" });
|
|
472
475
|
|
|
@@ -211,8 +211,8 @@ describe("isTextMimeType", () => {
|
|
|
211
211
|
describe("GET /v1/workspace/tree", () => {
|
|
212
212
|
const { handler } = getRoute("workspace_tree");
|
|
213
213
|
|
|
214
|
-
test("root listing returns entries", () => {
|
|
215
|
-
const result = handler({ queryParams: {} }) as {
|
|
214
|
+
test("root listing returns entries", async () => {
|
|
215
|
+
const result = (await handler({ queryParams: {} })) as {
|
|
216
216
|
path: string;
|
|
217
217
|
entries: Array<{ name: string; type: string }>;
|
|
218
218
|
};
|
|
@@ -222,8 +222,8 @@ describe("GET /v1/workspace/tree", () => {
|
|
|
222
222
|
expect(names).toContain("subdir");
|
|
223
223
|
});
|
|
224
224
|
|
|
225
|
-
test("subdirectory listing returns child entries", () => {
|
|
226
|
-
const result = handler({ queryParams: { path: "subdir" } }) as {
|
|
225
|
+
test("subdirectory listing returns child entries", async () => {
|
|
226
|
+
const result = (await handler({ queryParams: { path: "subdir" } })) as {
|
|
227
227
|
path: string;
|
|
228
228
|
entries: Array<{ name: string; type: string }>;
|
|
229
229
|
};
|
|
@@ -232,20 +232,20 @@ describe("GET /v1/workspace/tree", () => {
|
|
|
232
232
|
expect(result.entries[0].type).toBe("file");
|
|
233
233
|
});
|
|
234
234
|
|
|
235
|
-
test("non-existent directory throws NotFoundError", () => {
|
|
236
|
-
expect(
|
|
235
|
+
test("non-existent directory throws NotFoundError", async () => {
|
|
236
|
+
await expect(handler({ queryParams: { path: "nope" } })).rejects.toThrow(
|
|
237
237
|
NotFoundError,
|
|
238
238
|
);
|
|
239
239
|
});
|
|
240
240
|
|
|
241
|
-
test("path traversal attempt throws BadRequestError", () => {
|
|
242
|
-
expect(
|
|
243
|
-
|
|
244
|
-
);
|
|
241
|
+
test("path traversal attempt throws BadRequestError", async () => {
|
|
242
|
+
await expect(
|
|
243
|
+
handler({ queryParams: { path: "../../etc" } }),
|
|
244
|
+
).rejects.toThrow(BadRequestError);
|
|
245
245
|
});
|
|
246
246
|
|
|
247
|
-
test("entries have correct type field", () => {
|
|
248
|
-
const result = handler({ queryParams: {} }) as {
|
|
247
|
+
test("entries have correct type field", async () => {
|
|
248
|
+
const result = (await handler({ queryParams: {} })) as {
|
|
249
249
|
entries: Array<{ name: string; type: "file" | "directory" }>;
|
|
250
250
|
};
|
|
251
251
|
const subdirEntry = result.entries.find((e) => e.name === "subdir");
|
|
@@ -254,8 +254,8 @@ describe("GET /v1/workspace/tree", () => {
|
|
|
254
254
|
expect(fileEntry?.type).toBe("file");
|
|
255
255
|
});
|
|
256
256
|
|
|
257
|
-
test("dotfiles and dot-directories are excluded", () => {
|
|
258
|
-
const result = handler({ queryParams: {} }) as {
|
|
257
|
+
test("dotfiles and dot-directories are excluded", async () => {
|
|
258
|
+
const result = (await handler({ queryParams: {} })) as {
|
|
259
259
|
entries: Array<{ name: string }>;
|
|
260
260
|
};
|
|
261
261
|
const names = result.entries.map((e) => e.name);
|
|
@@ -263,8 +263,8 @@ describe("GET /v1/workspace/tree", () => {
|
|
|
263
263
|
expect(names).not.toContain(".hidden");
|
|
264
264
|
});
|
|
265
265
|
|
|
266
|
-
test("directory entries have null size and mimeType", () => {
|
|
267
|
-
const result = handler({ queryParams: {} }) as {
|
|
266
|
+
test("directory entries have null size and mimeType", async () => {
|
|
267
|
+
const result = (await handler({ queryParams: {} })) as {
|
|
268
268
|
entries: Array<{
|
|
269
269
|
name: string;
|
|
270
270
|
type: string;
|
|
@@ -278,8 +278,8 @@ describe("GET /v1/workspace/tree", () => {
|
|
|
278
278
|
expect(dirEntry!.mimeType).toBeNull();
|
|
279
279
|
});
|
|
280
280
|
|
|
281
|
-
test("directories sorted before files", () => {
|
|
282
|
-
const result = handler({ queryParams: {} }) as {
|
|
281
|
+
test("directories sorted before files", async () => {
|
|
282
|
+
const result = (await handler({ queryParams: {} })) as {
|
|
283
283
|
entries: Array<{ type: string }>;
|
|
284
284
|
};
|
|
285
285
|
const firstFileIdx = result.entries.findIndex((e) => e.type === "file");
|
|
@@ -295,8 +295,10 @@ describe("GET /v1/workspace/tree", () => {
|
|
|
295
295
|
}
|
|
296
296
|
});
|
|
297
297
|
|
|
298
|
-
test("includeDirSizes=true returns recursive byte size for directories", () => {
|
|
299
|
-
const result = handler({
|
|
298
|
+
test("includeDirSizes=true returns recursive byte size for directories", async () => {
|
|
299
|
+
const result = (await handler({
|
|
300
|
+
queryParams: { includeDirSizes: "true" },
|
|
301
|
+
})) as {
|
|
300
302
|
entries: Array<{ name: string; type: string; size: number | null }>;
|
|
301
303
|
};
|
|
302
304
|
const subdirEntry = result.entries.find((e) => e.name === "subdir");
|
|
@@ -307,8 +309,10 @@ describe("GET /v1/workspace/tree", () => {
|
|
|
307
309
|
expect(subdirEntry!.size).toBe("nested content".length);
|
|
308
310
|
});
|
|
309
311
|
|
|
310
|
-
test("includeDirSizes=true preserves accurate file sizes", () => {
|
|
311
|
-
const result = handler({
|
|
312
|
+
test("includeDirSizes=true preserves accurate file sizes", async () => {
|
|
313
|
+
const result = (await handler({
|
|
314
|
+
queryParams: { includeDirSizes: "true" },
|
|
315
|
+
})) as {
|
|
312
316
|
entries: Array<{ name: string; type: string; size: number | null }>;
|
|
313
317
|
};
|
|
314
318
|
const fileEntry = result.entries.find((e) => e.name === "hello.txt");
|
|
@@ -316,8 +320,8 @@ describe("GET /v1/workspace/tree", () => {
|
|
|
316
320
|
expect(fileEntry!.size).toBe("Hello, world!".length);
|
|
317
321
|
});
|
|
318
322
|
|
|
319
|
-
test("includeDirSizes omitted preserves null size for directories", () => {
|
|
320
|
-
const result = handler({ queryParams: {} }) as {
|
|
323
|
+
test("includeDirSizes omitted preserves null size for directories", async () => {
|
|
324
|
+
const result = (await handler({ queryParams: {} })) as {
|
|
321
325
|
entries: Array<{ type: string; size: number | null }>;
|
|
322
326
|
};
|
|
323
327
|
const dirEntry = result.entries.find((e) => e.type === "directory");
|
|
@@ -325,11 +329,36 @@ describe("GET /v1/workspace/tree", () => {
|
|
|
325
329
|
expect(dirEntry!.size).toBeNull();
|
|
326
330
|
});
|
|
327
331
|
|
|
328
|
-
test("
|
|
332
|
+
test("recursive=true lists nested entries and the bound fields", async () => {
|
|
333
|
+
const result = (await handler({ queryParams: { recursive: "true" } })) as {
|
|
334
|
+
entries: Array<{ path: string }>;
|
|
335
|
+
truncated: boolean;
|
|
336
|
+
skipped: string[];
|
|
337
|
+
};
|
|
338
|
+
expect(result.entries.map((e) => e.path)).toContain("subdir/nested.txt");
|
|
339
|
+
expect(result.truncated).toBe(false);
|
|
340
|
+
expect(Array.isArray(result.skipped)).toBe(true);
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
test("a non-recursive listing carries the same fields, unset", async () => {
|
|
344
|
+
const result = (await handler({ queryParams: {} })) as {
|
|
345
|
+
entries: Array<{ path: string }>;
|
|
346
|
+
truncated: boolean;
|
|
347
|
+
skipped: string[];
|
|
348
|
+
};
|
|
349
|
+
expect(result.entries.map((e) => e.path)).not.toContain(
|
|
350
|
+
"subdir/nested.txt",
|
|
351
|
+
);
|
|
352
|
+
expect(result).toMatchObject({ truncated: false, skipped: [] });
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
test("includeDirSizes=true returns 0 for empty directory", async () => {
|
|
329
356
|
const emptyDir = join(testWorkspaceDir, "empty-for-size");
|
|
330
357
|
mkdirSync(emptyDir, { recursive: true });
|
|
331
358
|
|
|
332
|
-
const result = handler({
|
|
359
|
+
const result = (await handler({
|
|
360
|
+
queryParams: { includeDirSizes: "true" },
|
|
361
|
+
})) as {
|
|
333
362
|
entries: Array<{ name: string; size: number | null }>;
|
|
334
363
|
};
|
|
335
364
|
const emptyEntry = result.entries.find((e) => e.name === "empty-for-size");
|