@vellumai/assistant 0.11.6 → 0.11.7-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/ARCHITECTURE.md +31 -19
- package/Dockerfile +1 -0
- package/docs/architecture/security.md +3 -3
- package/docs/flux-turn-detection-spike.md +8 -7
- package/docs/runbook-trusted-contacts.md +14 -0
- package/docs/stt-provider-onboarding.md +8 -4
- package/knip.json +1 -0
- package/node_modules/@vellumai/avatar-manifest/package.json +18 -0
- package/node_modules/@vellumai/avatar-manifest/src/__tests__/manifest.test.ts +173 -0
- package/node_modules/@vellumai/avatar-manifest/src/__tests__/read.test.ts +114 -0
- package/node_modules/@vellumai/avatar-manifest/src/index.ts +29 -0
- package/node_modules/@vellumai/avatar-manifest/src/layout.ts +18 -0
- package/node_modules/@vellumai/avatar-manifest/src/manifest.ts +156 -0
- package/node_modules/@vellumai/avatar-manifest/src/read.ts +109 -0
- package/node_modules/@vellumai/avatar-manifest/tsconfig.json +20 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/ingress.test.ts +37 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/remote-web-pairing.test.ts +389 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/ingress.ts +22 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +508 -18
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/rpc.ts +174 -0
- package/node_modules/@vellumai/ces-client/src/http-credentials.ts +161 -0
- package/node_modules/@vellumai/ces-client/src/index.ts +1 -0
- package/node_modules/@vellumai/environments/package.json +2 -1
- package/node_modules/@vellumai/environments/src/shell.test.ts +56 -0
- package/node_modules/@vellumai/environments/src/shell.ts +65 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/ingress.test.ts +37 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/remote-web-pairing.test.ts +389 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/ingress.ts +22 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +508 -18
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/rpc.ts +174 -0
- package/node_modules/@vellumai/gateway-client/src/__tests__/contact-read-contracts.test.ts +32 -0
- package/node_modules/@vellumai/gateway-client/src/__tests__/gateway-client.test.ts +1 -0
- package/node_modules/@vellumai/gateway-client/src/__tests__/inbound-event-kind.test.ts +82 -0
- package/node_modules/@vellumai/gateway-client/src/__tests__/plugin-admission-denied-contract.test.ts +56 -0
- package/node_modules/@vellumai/gateway-client/src/__tests__/trust-verdict-contract.test.ts +18 -0
- package/node_modules/@vellumai/gateway-client/src/admission-policy-contract.ts +5 -11
- package/node_modules/@vellumai/gateway-client/src/channel-permission-contract.ts +3 -1
- package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +9 -0
- package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +4 -0
- package/node_modules/@vellumai/gateway-client/src/inbound-event-kind.ts +75 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +17 -0
- package/node_modules/@vellumai/gateway-client/src/outbound-contract.ts +8 -0
- package/node_modules/@vellumai/gateway-client/src/plugin-admission-denied-contract.ts +47 -0
- package/node_modules/@vellumai/gateway-client/src/trust-verdict-contract.ts +11 -0
- package/node_modules/@vellumai/service-contracts/src/__tests__/ingress.test.ts +37 -0
- package/node_modules/@vellumai/service-contracts/src/__tests__/remote-web-pairing.test.ts +389 -0
- package/node_modules/@vellumai/service-contracts/src/ingress.ts +22 -0
- package/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +508 -18
- package/node_modules/@vellumai/service-contracts/src/rpc.ts +174 -0
- package/openapi.yaml +419 -55
- package/package.json +4 -1
- package/scripts/sync-llm-catalog.ts +6 -0
- package/src/__tests__/approval-card-edit-inbound-handoff.test.ts +199 -0
- package/src/__tests__/approval-card-edit.test.ts +153 -0
- package/src/__tests__/attachments-store-heic-normalize.test.ts +1 -1
- package/src/__tests__/available-skills.test.ts +54 -0
- package/src/__tests__/avatar-identity-sync.test.ts +5 -1
- package/src/__tests__/catalog-install-normalize.test.ts +9 -1
- package/src/__tests__/channel-policy.test.ts +1 -3
- package/src/__tests__/channel-readiness-discord.test.ts +9 -70
- package/src/__tests__/compactor-call-site-logging.test.ts +61 -0
- package/src/__tests__/config-loader-backfill.test.ts +69 -0
- package/src/__tests__/config-schema.test.ts +2 -3
- package/src/__tests__/conversation-agent-loop.test.ts +238 -0
- package/src/__tests__/conversation-attachments.test.ts +0 -1
- package/src/__tests__/conversation-error.test.ts +44 -0
- package/src/__tests__/conversation-evictor.test.ts +19 -0
- package/src/__tests__/conversation-fork-referential.test.ts +73 -63
- package/src/__tests__/conversation-fork-retrospective.test.ts +99 -74
- package/src/__tests__/conversation-runtime-assembly.test.ts +23 -0
- package/src/__tests__/conversation-slash.test.ts +9 -0
- package/src/__tests__/conversation-title-service.test.ts +97 -0
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +16 -0
- package/src/__tests__/credential-broker-server-use.test.ts +0 -218
- package/src/__tests__/credential-broker.test.ts +43 -420
- package/src/__tests__/credential-record-write-through.test.ts +78 -0
- package/src/__tests__/credential-routes.test.ts +17 -1
- package/src/__tests__/credential-security-invariants.test.ts +8 -9
- package/src/__tests__/default-profile-catalog-fallback.test.ts +289 -0
- package/src/__tests__/delete-propagation.test.ts +92 -2
- package/src/__tests__/discord-callback-round-trip.test.ts +93 -0
- package/src/__tests__/discord-channel-config.test.ts +237 -0
- package/src/__tests__/edit-propagation.test.ts +42 -2
- package/src/__tests__/evict-conversations-for-reload.test.ts +51 -1
- package/src/__tests__/fallback-breaker.test.ts +1335 -0
- package/src/__tests__/gateway-threshold-reader-mock.ts +6 -0
- package/src/__tests__/guardian-card-withdrawal.test.ts +30 -0
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +0 -2
- package/src/__tests__/image-conversion.test.ts +38 -4
- package/src/__tests__/install-skill-routing.test.ts +25 -0
- package/src/__tests__/llm-catalog-parity.test.ts +4 -0
- package/src/__tests__/llm-resolver.test.ts +74 -0
- package/src/__tests__/llm-schema-fallback-profile.test.ts +359 -0
- package/src/__tests__/managed-fallback-dispatch.test.ts +430 -0
- package/src/__tests__/managed-profile-guard.test.ts +28 -0
- package/src/__tests__/managed-speech-defaults.test.ts +196 -1
- package/src/__tests__/mcp-cli.test.ts +23 -1
- package/src/__tests__/mcp-tool-annotations-risk.test.ts +43 -34
- package/src/__tests__/media-stream-stt-session.test.ts +2 -0
- package/src/__tests__/notification-decision-fallback.test.ts +106 -0
- package/src/__tests__/notification-discord-adapter.test.ts +162 -0
- package/src/__tests__/oauth-connect-orchestrator.test.ts +95 -0
- package/src/__tests__/persist-unsendable-image-downscale.test.ts +6 -8
- package/src/__tests__/persist-unsendable-image.test.ts +3 -3
- package/src/__tests__/platform.test.ts +19 -1
- package/src/__tests__/plugin-import-boundary-guard.test.ts +5 -1
- package/src/__tests__/provider-catalog-visibility.test.ts +17 -0
- package/src/__tests__/provider-platform-proxy-integration.test.ts +8 -1
- package/src/__tests__/provider-usage-tracking.test.ts +56 -0
- package/src/__tests__/require-fresh-approval.test.ts +5 -2
- package/src/__tests__/retry-fallback-escalation.test.ts +1093 -0
- package/src/__tests__/schedule-retry.test.ts +183 -4
- package/src/__tests__/scheduler-reuse-conversation.test.ts +6 -6
- package/src/__tests__/secret-routes-platform-proxy.test.ts +55 -0
- package/src/__tests__/skill-load-tool.test.ts +34 -0
- package/src/__tests__/skill-tool-factory.test.ts +6 -1
- package/src/__tests__/skill-tool-manifest.test.ts +20 -0
- package/src/__tests__/skills.test.ts +25 -0
- package/src/__tests__/subagent-manager-notify.test.ts +38 -0
- package/src/__tests__/terminal-tools.test.ts +2 -0
- package/src/__tests__/tool-approval-handler.test.ts +146 -2
- package/src/__tests__/tool-approval-seed-content-blocks.test.ts +108 -2
- package/src/__tests__/tool-audit.test.ts +111 -40
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +228 -18
- package/src/__tests__/ui-shape-teaching.test.ts +1 -1
- package/src/__tests__/verification-control-plane-policy.test.ts +6 -2
- package/src/__tests__/voice-config-update.test.ts +43 -0
- package/src/__tests__/workspace-migration-147-rename-colliding-backup-profile-names.test.ts +480 -0
- package/src/__tests__/workspace-migration-148-strip-unsupported-fallback-profiles.test.ts +167 -0
- package/src/__tests__/workspace-migration-149-repoint-backup-profile-selections.test.ts +144 -0
- package/src/agent/loop.ts +31 -2
- package/src/approvals/guardian-card-withdrawal.ts +22 -0
- package/src/avatar/__tests__/ensure-raster.test.ts +291 -0
- package/src/avatar/avatar-manifest.ts +48 -100
- package/src/avatar/avatar-store.ts +7 -7
- package/src/avatar/ensure-raster.ts +146 -0
- package/src/avatar/resvg-lazy.test.ts +0 -1
- package/src/avatar/traits-png-sync.ts +9 -10
- package/src/background-wake/wake-intent-hooks.test.ts +0 -1
- package/src/backup/__tests__/paths.test.ts +6 -2
- package/src/backup/paths.ts +6 -4
- package/src/bundler/app-compiler.ts +2 -2
- package/src/bundler/compiler-tools.test.ts +36 -0
- package/src/bundler/compiler-tools.ts +54 -18
- package/src/calls/__tests__/telephony-synthesis-language.test.ts +81 -0
- package/src/calls/__tests__/voice-session-bridge.test.ts +57 -0
- package/src/calls/media-stream-stt-session.ts +2 -1
- package/src/calls/telephony-synthesis-language.ts +13 -5
- package/src/calls/voice-session-bridge.ts +19 -4
- package/src/channels/config.ts +23 -12
- package/src/cli/AGENTS.md +3 -1
- package/src/cli/commands/backup.help.ts +10 -13
- package/src/cli/commands/contacts.help.ts +16 -4
- package/src/cli/commands/contacts.ts +5 -1
- package/src/cli/commands/credentials.help.ts +1 -1
- package/src/cli/commands/inference-providers.ts +18 -53
- package/src/cli/commands/mcp.help.ts +6 -5
- package/src/cli/commands/mcp.ts +1 -1
- package/src/cli/commands/memory/memory-retrospective.ts +1 -0
- package/src/cli/commands/stt.help.ts +2 -2
- package/src/config/__tests__/backup-schema.test.ts +1 -1
- package/src/config/__tests__/default-profile-catalog.test.ts +83 -2
- package/src/config/__tests__/loader-sparse-array-cleanup.test.ts +1 -1
- package/src/config/__tests__/memory-retrospective-schema.test.ts +12 -15
- package/src/config/bundled-skills/computer-use/SKILL.md +6 -6
- package/src/config/bundled-skills/computer-use/TOOLS.json +9 -6
- package/src/config/bundled-skills/media-processing/services/audio-transcribe.ts +3 -1
- package/src/config/bundled-skills/media-processing/services/preprocess.ts +1 -1
- package/src/config/bundled-skills/phone-calls/SKILL.md +1 -1
- package/src/config/bundled-skills/phone-calls/references/CONFIG.md +16 -15
- package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +2 -1
- package/src/config/bundled-skills/settings/TOOLS.json +11 -5
- package/src/config/bundled-skills/settings/tools/open-system-settings.test.ts +85 -0
- package/src/config/bundled-skills/settings/tools/open-system-settings.ts +33 -8
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +54 -3
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +1 -1
- package/src/config/default-profile-catalog.ts +195 -10
- package/src/config/default-profile-names.ts +41 -0
- package/src/config/feature-flag-registry.json +34 -2
- package/src/config/inference-profile-validation.ts +11 -2
- package/src/config/llm-resolver.ts +25 -0
- package/src/config/loader.ts +3 -0
- package/src/config/managed-speech-defaults.ts +160 -10
- package/src/config/schema.ts +2 -0
- package/src/config/schemas/__tests__/live-voice.test.ts +7 -6
- package/src/config/schemas/__tests__/stt.test.ts +94 -0
- package/src/config/schemas/backup.ts +1 -1
- package/src/config/schemas/channels.ts +32 -0
- package/src/config/schemas/live-voice.ts +13 -4
- package/src/config/schemas/llm.ts +263 -15
- package/src/config/schemas/mcp.ts +10 -2
- package/src/config/schemas/memory-retrospective.ts +1 -19
- package/src/config/schemas/services.ts +24 -0
- package/src/config/schemas/stt.ts +128 -5
- package/src/config/seed-inference-profiles.ts +32 -3
- package/src/config/skill-state.ts +5 -0
- package/src/config/skills.ts +21 -0
- package/src/contacts/notify-contacts-changed.ts +5 -2
- package/src/context/compactor.ts +74 -1
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +138 -1
- package/src/daemon/config-watcher.ts +2 -1
- package/src/daemon/conversation-agent-loop-handlers.ts +27 -0
- package/src/daemon/conversation-agent-loop.ts +86 -10
- package/src/daemon/conversation-error.ts +25 -0
- package/src/daemon/conversation-evictor.ts +2 -4
- package/src/daemon/conversation-runtime-assembly.ts +19 -7
- package/src/daemon/conversation-skill-tools.ts +5 -1
- package/src/daemon/conversation-slash.ts +2 -2
- package/src/daemon/conversation-store.ts +8 -1
- package/src/daemon/conversation-tool-setup.ts +37 -0
- package/src/daemon/conversation.ts +0 -4
- package/src/daemon/handlers/channel-config-result.ts +30 -0
- package/src/daemon/handlers/config-discord-channel.ts +301 -0
- package/src/daemon/handlers/config-ingress.ts +9 -0
- package/src/daemon/handlers/config-slack-channel.ts +13 -15
- package/src/daemon/handlers/config-telegram.ts +2 -5
- package/src/daemon/handlers/conversations.ts +0 -2
- package/src/daemon/handlers/skills.ts +25 -7
- package/src/daemon/lifecycle.ts +3 -1
- package/src/daemon/message-types/messages.ts +0 -2
- package/src/daemon/tool-setup-types.ts +5 -3
- package/src/daemon/trust-context-types.test.ts +33 -0
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +0 -1
- package/src/live-voice/__tests__/live-voice-flux-turn-end.test.ts +58 -4
- package/src/live-voice/__tests__/live-voice-managed-speech-fallback.test.ts +8 -3
- package/src/live-voice/__tests__/live-voice-session-manager.test.ts +73 -11
- package/src/live-voice/__tests__/live-voice-session-preflight.test.ts +148 -2
- package/src/live-voice/__tests__/live-voice-session-telemetry.test.ts +64 -0
- package/src/live-voice/__tests__/live-voice-stt.test.ts +3 -0
- package/src/live-voice/__tests__/live-voice-text-turn.test.ts +391 -0
- package/src/live-voice/__tests__/protocol.test.ts +224 -0
- package/src/live-voice/live-voice-credential-preflight.ts +5 -1
- package/src/live-voice/live-voice-session-manager.ts +39 -2
- package/src/live-voice/live-voice-session.ts +224 -9
- package/src/live-voice/protocol.ts +187 -1
- package/src/messaging/providers/discord/api.ts +2 -1
- package/src/messaging/providers/discord/withdraw.ts +62 -0
- package/src/messaging/read-provider-metadata.ts +32 -0
- package/src/monitoring/__tests__/file-descriptors.test.ts +32 -0
- package/src/monitoring/__tests__/process-memory.test.ts +34 -1
- package/src/monitoring/db-integrity-sample.ts +1 -0
- package/src/monitoring/file-descriptors.ts +39 -6
- package/src/monitoring/process-memory.ts +37 -5
- package/src/monitoring/resource-sampler.ts +25 -8
- package/src/notifications/__tests__/activity-failed-dedupe.test.ts +161 -0
- package/src/notifications/__tests__/background-failure-signal.test.ts +107 -0
- package/src/notifications/__tests__/connected-channels.test.ts +12 -0
- package/src/notifications/__tests__/copy-composer.test.ts +83 -0
- package/src/notifications/__tests__/destination-resolver.test.ts +38 -0
- package/src/notifications/activity-failed-dedupe.ts +95 -0
- package/src/notifications/adapters/discord.ts +132 -0
- package/src/notifications/adapters/shared.ts +16 -0
- package/src/notifications/adapters/slack.ts +6 -1
- package/src/notifications/adapters/telegram.ts +5 -8
- package/src/notifications/approval-card-data.ts +94 -1
- package/src/notifications/background-failure-signal.ts +116 -0
- package/src/notifications/broadcaster.ts +3 -9
- package/src/notifications/copy-composer.ts +41 -8
- package/src/notifications/decision-engine.ts +55 -0
- package/src/notifications/destination-resolver.ts +45 -5
- package/src/notifications/emit-signal.ts +19 -5
- package/src/notifications/guardian-question-mode.ts +57 -0
- package/src/notifications/types.ts +2 -8
- package/src/oauth/__tests__/seed-providers-managed.test.ts +18 -0
- package/src/oauth/connect-orchestrator.ts +16 -0
- package/src/oauth/seed-providers.ts +262 -0
- package/src/permissions/checker.test.ts +21 -0
- package/src/permissions/checker.ts +6 -0
- package/src/permissions/gateway-threshold-reader.test.ts +184 -0
- package/src/permissions/gateway-threshold-reader.ts +144 -10
- package/src/permissions/prompter.ts +0 -6
- package/src/permissions/types.ts +5 -4
- package/src/persistence/__tests__/bulk-write-gate.test.ts +0 -38
- package/src/persistence/bulk-write-gate.ts +9 -10
- package/src/persistence/conversation-crud.ts +88 -134
- package/src/persistence/conversation-title-service.ts +43 -29
- package/src/persistence/embeddings/embedding-local.ts +14 -85
- package/src/persistence/embeddings/embedding-runtime-manager.ts +1 -0
- package/src/persistence/lifecycle-events-store.test.ts +50 -0
- package/src/persistence/lifecycle-events-store.ts +75 -5
- package/src/platform/platform-patch-queue.test.ts +368 -0
- package/src/platform/platform-patch-queue.ts +190 -0
- package/src/platform/sync-avatar.test.ts +527 -0
- package/src/platform/sync-avatar.ts +209 -0
- package/src/platform/sync-identity.test.ts +75 -0
- package/src/platform/sync-identity.ts +17 -110
- package/src/plugin-api/model-profiles.test.ts +2 -2
- package/src/plugin-api/model-profiles.ts +7 -7
- package/src/plugin-api/transcription-session.ts +1 -1
- package/src/plugins/defaults/compaction/window-manager.ts +4 -0
- package/src/plugins/defaults/image-recovery/recover.ts +2 -2
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +133 -34
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-prompt.test.ts +21 -31
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-provider-path.test.ts +13 -9
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-wake-chain.test.ts +31 -4
- package/src/plugins/defaults/memory/memory-retrospective-constants.ts +0 -11
- package/src/plugins/defaults/memory/memory-retrospective-fork-boundary.ts +27 -13
- package/src/plugins/defaults/memory/memory-retrospective-job.ts +183 -113
- package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +13 -13
- package/src/plugins/defaults/memory/src/memory-v2-routes.ts +2 -2
- package/src/plugins/defaults/memory/substrate/__tests__/consolidation-job.test.ts +1 -1
- package/src/plugins/defaults/memory/substrate/consolidation-job.ts +4 -2
- package/src/plugins/defaults/memory/substrate/sweep-job.ts +9 -42
- package/src/plugins/defaults/memory/v2/rerank-local.ts +1 -0
- package/src/plugins/defaults/memory/worker.ts +7 -0
- package/src/providers/__tests__/provider-secret-catalog.test.ts +1 -0
- package/src/providers/__tests__/retry-callsite.test.ts +55 -0
- package/src/providers/__tests__/vellum-connection-routing.test.ts +21 -3
- package/src/providers/connection-resolution.ts +2 -17
- package/src/providers/fallback-breaker.ts +389 -0
- package/src/providers/inference/__tests__/adapter-factory-opencode.test.ts +92 -0
- package/src/providers/inference/__tests__/base-url-route-validation.test.ts +16 -0
- package/src/providers/inference/__tests__/base-url-security.test.ts +12 -0
- package/src/providers/inference/adapter-factory.ts +246 -3
- package/src/providers/inference/auth.ts +15 -14
- package/src/providers/inference/chatgpt-subscription-auth.ts +70 -0
- package/src/providers/inference/chatgpt-subscription-credentials.ts +84 -0
- package/src/providers/inference/resolve-auth.ts +1 -1
- package/src/providers/media-resolve.ts +2 -3
- package/src/providers/model-catalog.ts +100 -0
- package/src/providers/openai/__tests__/api-error-normalization.test.ts +22 -0
- package/src/providers/openai/__tests__/chat-template-flatten.test.ts +198 -0
- package/src/providers/openai/api-error-normalization.ts +13 -0
- package/src/providers/openai/chat-completions-provider.ts +84 -0
- package/src/providers/opencode/client.test.ts +150 -0
- package/src/providers/opencode/client.ts +61 -0
- package/src/providers/platform-proxy/constants.ts +5 -0
- package/src/providers/provider-resolvability.ts +20 -0
- package/src/providers/provider-secret-catalog.ts +3 -2
- package/src/providers/retry.ts +956 -68
- package/src/providers/speech-to-text/__tests__/deepgram-flux-frames.test.ts +42 -0
- package/src/providers/speech-to-text/__tests__/deepgram-flux-realtime.test.ts +136 -0
- package/src/providers/speech-to-text/__tests__/provider-catalog.test.ts +120 -0
- package/src/providers/speech-to-text/__tests__/resolve.test.ts +316 -12
- package/src/providers/speech-to-text/deepgram-flux-frames.ts +83 -3
- package/src/providers/speech-to-text/deepgram-flux-realtime.ts +132 -4
- package/src/providers/speech-to-text/provider-catalog.ts +191 -3
- package/src/providers/speech-to-text/resolve.ts +132 -24
- package/src/providers/speech-to-text/vellum-managed-flux-realtime.ts +296 -0
- package/src/providers/types.ts +39 -0
- package/src/providers/usage-tracking.ts +12 -1
- package/src/providers/vellum/client.ts +29 -0
- package/src/providers/vellum-model-routing.test.ts +2 -0
- package/src/providers/vellum-model-routing.ts +3 -3
- package/src/runtime/AGENTS.md +1 -2
- package/src/runtime/__tests__/agent-wake.test.ts +105 -3
- package/src/runtime/__tests__/background-job-runner.test.ts +89 -3
- package/src/runtime/agent-wake.ts +44 -5
- package/src/runtime/background-job-runner.ts +68 -50
- package/src/runtime/channel-readiness-service.ts +0 -39
- package/src/runtime/http-server.ts +12 -4
- package/src/runtime/routes/__tests__/avatar-state-routes.test.ts +18 -18
- package/src/runtime/routes/__tests__/backup-routes.test.ts +9 -5
- package/src/runtime/routes/__tests__/chatgpt-subscription-auth-routes.test.ts +372 -0
- package/src/runtime/routes/__tests__/contact-routes.test.ts +7 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +96 -0
- package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +37 -0
- package/src/runtime/routes/__tests__/ingress-status-routes.test.ts +2 -1
- package/src/runtime/routes/__tests__/mcp-add-default-risk.test.ts +87 -0
- package/src/runtime/routes/acp-claude-auth-routes.ts +25 -54
- package/src/runtime/routes/approval-routes.ts +0 -12
- package/src/runtime/routes/avatar-routes.ts +17 -30
- package/src/runtime/routes/backup-routes.ts +2 -2
- package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +296 -113
- package/src/runtime/routes/contact-prompt-routes.ts +65 -6
- package/src/runtime/routes/contact-routes.ts +20 -15
- package/src/runtime/routes/conversation-query-routes.ts +98 -10
- package/src/runtime/routes/conversation-routes.ts +2 -2
- package/src/runtime/routes/credential-routes.ts +2 -1
- package/src/runtime/routes/guardian-approval-interception.ts +12 -9
- package/src/runtime/routes/inbound-message-handler.ts +47 -48
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +18 -13
- package/src/runtime/routes/inbound-stages/admission-policy.ts +2 -2
- package/src/runtime/routes/inbound-stages/edit-intercept.ts +20 -2
- package/src/runtime/routes/inbound-stages/reaction-intercept.ts +9 -8
- package/src/runtime/routes/inbound-stages/transcribe-audio.ts +1 -1
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/inference-profile-session-handler.ts +2 -2
- package/src/runtime/routes/inference-profiles-routes.ts +12 -7
- package/src/runtime/routes/inference-provider-connection-routes.ts +2 -2
- package/src/runtime/routes/inference-send-routes.ts +2 -2
- package/src/runtime/routes/ingress-status-routes.ts +2 -2
- package/src/runtime/routes/integrations/discord.ts +119 -0
- package/src/runtime/routes/llm-call-sites-routes.ts +2 -2
- package/src/runtime/routes/mcp-auth-routes.ts +12 -6
- package/src/runtime/routes/migration-routes.ts +3 -1
- package/src/runtime/routes/oauth-pending-flows.ts +136 -0
- package/src/runtime/routes/secret-routes.ts +7 -0
- package/src/runtime/routes/stt-routes.ts +38 -6
- package/src/runtime/routes/wake-conversation-routes.ts +8 -1
- package/src/runtime/routes/watch-routes.ts +9 -5
- package/src/runtime/sync/resource-sync-events.ts +2 -0
- package/src/runtime/trust-verdict-consumer.ts +3 -3
- package/src/schedule/__tests__/worker-feature-flags.test.ts +58 -0
- package/src/schedule/retry-policy.ts +8 -8
- package/src/schedule/run-script.ts +14 -23
- package/src/schedule/schedule-recovery.ts +0 -1
- package/src/schedule/scheduler.ts +89 -51
- package/src/schedule/worker.ts +57 -0
- package/src/security/__tests__/openai-device-auth.test.ts +355 -0
- package/src/security/ces-rpc-record-backend.ts +123 -0
- package/src/security/oauth2.ts +7 -0
- package/src/security/openai-device-auth.ts +364 -0
- package/src/security/secure-keys.ts +15 -0
- package/src/skills/available-skills.ts +10 -1
- package/src/skills/catalog-install.ts +19 -1
- package/src/skills/inline-command-runner.ts +6 -2
- package/src/skills/platform-compatibility.test.ts +49 -0
- package/src/skills/platform-compatibility.ts +65 -0
- package/src/skills/tool-manifest.ts +20 -0
- package/src/stt/__tests__/roles.test.ts +152 -0
- package/src/stt/daemon-batch-transcriber.ts +4 -1
- package/src/stt/roles.ts +180 -0
- package/src/stt/stt-stream-session.ts +3 -2
- package/src/stt/types.ts +33 -3
- package/src/subagent/manager.ts +12 -0
- package/src/telemetry/__tests__/live-voice-funnel.test.ts +34 -0
- package/src/telemetry/live-voice-funnel.ts +12 -2
- package/src/telemetry/telemetry-wire.generated.ts +6 -0
- package/src/telemetry/tool-audit.ts +83 -30
- package/src/telemetry/tool-executed-events-store.test.ts +0 -1
- package/src/telemetry/turn-outcome.ts +75 -4
- package/src/tools/client-os.ts +115 -0
- package/src/tools/computer-use/definitions.ts +11 -7
- package/src/tools/computer-use/skill-proxy-bridge.ts +2 -2
- package/src/tools/credentials/broker-types.ts +0 -71
- package/src/tools/credentials/broker.ts +6 -201
- package/src/tools/credentials/metadata-store.ts +65 -16
- package/src/tools/credentials/store.ts +2 -0
- package/src/tools/executor.ts +2 -8
- package/src/tools/host-shell.test.ts +75 -0
- package/src/tools/host-shell.ts +24 -0
- package/src/tools/host-terminal/host-shell.ts +15 -44
- package/src/tools/mcp/mcp-tool-factory.ts +44 -9
- package/src/tools/permission-checker.ts +73 -21
- package/src/tools/policy-context.ts +1 -0
- package/src/tools/shared/filesystem/image-read.ts +1 -1
- package/src/tools/skills/find-similar-skills.ts +10 -2
- package/src/tools/skills/load.ts +41 -6
- package/src/tools/skills/sandbox-runner.ts +8 -16
- package/src/tools/skills/skill-tool-factory.ts +1 -0
- package/src/tools/terminal/__tests__/safe-env.test.ts +64 -1
- package/src/tools/terminal/safe-env.ts +39 -9
- package/src/tools/terminal/shell.ts +12 -31
- package/src/tools/tool-approval-handler.ts +64 -10
- package/src/tools/tool-types.ts +10 -0
- package/src/tools/types.ts +19 -3
- package/src/tools/ui-surface/surface-shape-docs.ts +4 -3
- package/src/tools/workflows/manage-workflows.ts +2 -2
- package/src/tools/workflows/run-workflow.test.ts +2 -1
- package/src/util/__tests__/file-use.test.ts +61 -0
- package/src/util/__tests__/process-table.test.ts +66 -0
- package/src/util/__tests__/process-tree.test.ts +46 -0
- package/src/util/errors.ts +1 -0
- package/src/util/file-use.ts +91 -0
- package/src/util/host-process.test.ts +17 -0
- package/src/util/host-process.ts +75 -0
- package/src/util/image-conversion.ts +39 -7
- package/src/util/platform.ts +32 -13
- package/src/util/process-table.ts +249 -0
- package/src/util/process-tree.ts +85 -129
- package/src/util/provider-error-patterns.ts +20 -0
- package/src/util/spawn.ts +9 -2
- package/src/watch/__tests__/watch-retro.test.ts +48 -1
- package/src/watch/watch-retro.ts +46 -4
- package/src/workspace/git-service.ts +4 -14
- package/src/workspace/migrations/147-rename-colliding-backup-profile-names.ts +360 -0
- package/src/workspace/migrations/148-strip-unsupported-fallback-profiles.ts +117 -0
- package/src/workspace/migrations/149-repoint-backup-profile-selections.ts +204 -0
- package/src/workspace/migrations/150-stt-flux-provider-to-model-family.ts +139 -0
- package/src/workspace/migrations/__tests__/150-stt-flux-provider-to-model-family.test.ts +160 -0
- package/src/workspace/migrations/registry.ts +8 -0
- package/src/__tests__/permission-types.test.ts +0 -17
- package/src/channels/gateway-discord-admission.ts +0 -32
- package/src/persistence/fork-message-copy.ts +0 -239
- package/src/plugins/defaults/memory/__tests__/fork-message-copy.test.ts +0 -221
- package/src/security/__tests__/oauth2-device-code.test.ts +0 -431
- package/src/security/oauth2-device-code.ts +0 -312
|
@@ -1,23 +1,14 @@
|
|
|
1
|
-
import { v4 as uuid } from "uuid";
|
|
2
|
-
|
|
3
1
|
import { credentialKey } from "../../security/credential-key.js";
|
|
4
2
|
import { getSecureKeyAsync } from "../../security/secure-keys.js";
|
|
5
3
|
import { getLogger } from "../../util/logger.js";
|
|
6
4
|
import type {
|
|
7
|
-
AuthorizeRequest,
|
|
8
|
-
AuthorizeResult,
|
|
9
5
|
BrowserFillRequest,
|
|
10
6
|
BrowserFillResult,
|
|
11
|
-
ConsumeResult,
|
|
12
|
-
ServerUseByIdRequest,
|
|
13
|
-
ServerUseByIdResult,
|
|
14
7
|
ServerUseRequest,
|
|
15
8
|
ServerUseResult,
|
|
16
|
-
UsageToken,
|
|
17
9
|
} from "./broker-types.js";
|
|
18
10
|
import { isDomainAllowed } from "./domain-policy.js";
|
|
19
11
|
import { getCredentialMetadata } from "./metadata-store.js";
|
|
20
|
-
import { resolveById } from "./resolve.js";
|
|
21
12
|
import {
|
|
22
13
|
isToolAllowed,
|
|
23
14
|
serverUseDenialReason,
|
|
@@ -26,21 +17,17 @@ import {
|
|
|
26
17
|
|
|
27
18
|
const log = getLogger("credential-broker");
|
|
28
19
|
|
|
29
|
-
/** Tokens expire after 5 minutes to limit the window for using stale/revoked credentials. */
|
|
30
|
-
const TOKEN_TTL_MS = 5 * 60 * 1000;
|
|
31
|
-
|
|
32
20
|
/**
|
|
33
|
-
* Credential broker that
|
|
21
|
+
* Credential broker that mediates policy-checked credential access.
|
|
34
22
|
*
|
|
35
|
-
* The broker never exposes plaintext secret values.
|
|
23
|
+
* The broker never exposes plaintext secret values to callers. It:
|
|
36
24
|
* 1. Checks that a credential exists and has metadata
|
|
37
|
-
* 2.
|
|
38
|
-
* 3.
|
|
25
|
+
* 2. Enforces tool and domain policy
|
|
26
|
+
* 3. Reads the secret internally and passes it to an opaque callback
|
|
39
27
|
*
|
|
40
|
-
* Tool policy is enforced at
|
|
28
|
+
* Tool policy is enforced at fill/use time; domain policy is enforced at fill time.
|
|
41
29
|
*/
|
|
42
30
|
export class CredentialBroker {
|
|
43
|
-
private tokens = new Map<string, UsageToken>();
|
|
44
31
|
/** Transient values for one-time send: consumed on first read, never persisted.
|
|
45
32
|
* Values are wrapped in objects so post-await guards use reference identity
|
|
46
33
|
* (not string value equality) to detect concurrent replacements. */
|
|
@@ -48,7 +35,7 @@ export class CredentialBroker {
|
|
|
48
35
|
|
|
49
36
|
/**
|
|
50
37
|
* Inject a value for one-time use. The value is consumed on the next
|
|
51
|
-
* browserFill or
|
|
38
|
+
* browserFill or serverUse call for this service/field pair, then discarded.
|
|
52
39
|
*/
|
|
53
40
|
injectTransient(service: string, field: string, value: string): void {
|
|
54
41
|
const key = credentialKey(service, field);
|
|
@@ -59,113 +46,6 @@ export class CredentialBroker {
|
|
|
59
46
|
);
|
|
60
47
|
}
|
|
61
48
|
|
|
62
|
-
/**
|
|
63
|
-
* Authorize the use of a credential for a specific tool and optional domain.
|
|
64
|
-
* Returns a single-use token on success, or a denial reason on failure.
|
|
65
|
-
*/
|
|
66
|
-
authorize(request: AuthorizeRequest): AuthorizeResult {
|
|
67
|
-
const metadata = getCredentialMetadata(request.service, request.field);
|
|
68
|
-
if (!metadata) {
|
|
69
|
-
return {
|
|
70
|
-
authorized: false,
|
|
71
|
-
reason: `No credential found for ${request.service}/${request.field}`,
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// Tool policy enforcement - deny if tool is not in the credential's allowed list
|
|
76
|
-
if (!isToolAllowed(request.toolName, metadata.allowedTools)) {
|
|
77
|
-
return {
|
|
78
|
-
authorized: false,
|
|
79
|
-
reason: toolNotAllowedReason(
|
|
80
|
-
request.toolName,
|
|
81
|
-
request.service,
|
|
82
|
-
request.field,
|
|
83
|
-
metadata.allowedTools,
|
|
84
|
-
),
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const token: UsageToken = {
|
|
89
|
-
tokenId: uuid(),
|
|
90
|
-
credentialId: metadata.credentialId,
|
|
91
|
-
service: request.service,
|
|
92
|
-
field: request.field,
|
|
93
|
-
toolName: request.toolName,
|
|
94
|
-
createdAt: Date.now(),
|
|
95
|
-
consumed: false,
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
this.tokens.set(token.tokenId, token);
|
|
99
|
-
log.info(
|
|
100
|
-
{
|
|
101
|
-
tokenId: token.tokenId,
|
|
102
|
-
service: request.service,
|
|
103
|
-
field: request.field,
|
|
104
|
-
tool: request.toolName,
|
|
105
|
-
},
|
|
106
|
-
"Usage token issued",
|
|
107
|
-
);
|
|
108
|
-
|
|
109
|
-
return { authorized: true, token: { ...token } };
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* Consume a previously issued token. Returns the storage key on success.
|
|
114
|
-
* Each token can only be consumed once.
|
|
115
|
-
*/
|
|
116
|
-
consume(tokenId: string): ConsumeResult {
|
|
117
|
-
const token = this.tokens.get(tokenId);
|
|
118
|
-
if (!token) {
|
|
119
|
-
return { success: false, reason: "Token not found or already revoked" };
|
|
120
|
-
}
|
|
121
|
-
if (token.consumed) {
|
|
122
|
-
return { success: false, reason: "Token already consumed" };
|
|
123
|
-
}
|
|
124
|
-
if (Date.now() - token.createdAt > TOKEN_TTL_MS) {
|
|
125
|
-
this.tokens.delete(tokenId);
|
|
126
|
-
log.info({ tokenId }, "Token expired (TTL exceeded)");
|
|
127
|
-
return { success: false, reason: "Token expired" };
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
token.consumed = true;
|
|
131
|
-
const storageKey = credentialKey(token.service, token.field);
|
|
132
|
-
// Check for transient value first (one-time send) - consume and return the value
|
|
133
|
-
// directly since transient values are never persisted to secure storage.
|
|
134
|
-
const transient = this.transientValues.get(storageKey);
|
|
135
|
-
if (transient !== undefined) {
|
|
136
|
-
this.transientValues.delete(storageKey);
|
|
137
|
-
log.info(
|
|
138
|
-
{ tokenId, storageKey, transient: true },
|
|
139
|
-
"Usage token consumed (transient)",
|
|
140
|
-
);
|
|
141
|
-
return { success: true, storageKey, value: transient.value };
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
log.info({ tokenId, storageKey }, "Usage token consumed");
|
|
145
|
-
return { success: true, storageKey };
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* Revoke a token, removing it from the active set.
|
|
150
|
-
* Returns true if the token existed and was revoked.
|
|
151
|
-
*/
|
|
152
|
-
revoke(tokenId: string): boolean {
|
|
153
|
-
const existed = this.tokens.delete(tokenId);
|
|
154
|
-
if (existed) {
|
|
155
|
-
log.info({ tokenId }, "Usage token revoked");
|
|
156
|
-
}
|
|
157
|
-
return existed;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
/** Revoke all tokens (e.g. on conversation teardown). */
|
|
161
|
-
revokeAll(): void {
|
|
162
|
-
const count = this.tokens.size;
|
|
163
|
-
this.tokens.clear();
|
|
164
|
-
if (count > 0) {
|
|
165
|
-
log.info({ count }, "All usage tokens revoked");
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
49
|
/**
|
|
170
50
|
* Fill a browser field using a credential without exposing plaintext to the caller.
|
|
171
51
|
*
|
|
@@ -317,81 +197,6 @@ export class CredentialBroker {
|
|
|
317
197
|
return { success: false, reason: "Credential use failed" };
|
|
318
198
|
}
|
|
319
199
|
}
|
|
320
|
-
|
|
321
|
-
/**
|
|
322
|
-
* Look up a credential by its opaque ID for proxy injection.
|
|
323
|
-
*
|
|
324
|
-
* Returns metadata and injection templates so the proxy knows how to
|
|
325
|
-
* inject the credential into outbound requests. The secret value is
|
|
326
|
-
* never included in the result - the proxy reads it separately via
|
|
327
|
-
* the secure key backend at injection time.
|
|
328
|
-
*/
|
|
329
|
-
async serverUseById(
|
|
330
|
-
request: ServerUseByIdRequest,
|
|
331
|
-
): Promise<ServerUseByIdResult> {
|
|
332
|
-
const resolved = resolveById(request.credentialId);
|
|
333
|
-
if (!resolved) {
|
|
334
|
-
return {
|
|
335
|
-
success: false,
|
|
336
|
-
reason: `No credential found for id "${request.credentialId}"`,
|
|
337
|
-
};
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
const { metadata } = resolved;
|
|
341
|
-
|
|
342
|
-
// Shared server-use policy: the ID lookup above is the only by-ID-specific
|
|
343
|
-
// gate, so tool and domain enforcement come from the same helper serverUse
|
|
344
|
-
// uses.
|
|
345
|
-
const denialReason = serverUseDenialReason(
|
|
346
|
-
metadata,
|
|
347
|
-
request.requestingTool,
|
|
348
|
-
metadata.service,
|
|
349
|
-
metadata.field,
|
|
350
|
-
);
|
|
351
|
-
if (denialReason) {
|
|
352
|
-
return { success: false, reason: denialReason };
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
// Fail-closed: verify the secret value actually exists in secure storage.
|
|
356
|
-
// Without this, downstream proxy code would attempt unauthenticated requests.
|
|
357
|
-
const value = await getSecureKeyAsync(resolved.storageKey);
|
|
358
|
-
if (!value) {
|
|
359
|
-
return {
|
|
360
|
-
success: false,
|
|
361
|
-
reason: `Credential metadata exists but no stored value for ${metadata.service}/${metadata.field}`,
|
|
362
|
-
};
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
log.info(
|
|
366
|
-
{
|
|
367
|
-
credentialId: request.credentialId,
|
|
368
|
-
service: metadata.service,
|
|
369
|
-
field: metadata.field,
|
|
370
|
-
tool: request.requestingTool,
|
|
371
|
-
},
|
|
372
|
-
"Server-side credential lookup by ID completed",
|
|
373
|
-
);
|
|
374
|
-
|
|
375
|
-
return {
|
|
376
|
-
success: true,
|
|
377
|
-
credentialId: resolved.credentialId,
|
|
378
|
-
service: resolved.service,
|
|
379
|
-
field: resolved.field,
|
|
380
|
-
injectionTemplates: resolved.injectionTemplates,
|
|
381
|
-
};
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
/** Return the number of active (non-consumed, non-revoked, non-expired) tokens. */
|
|
385
|
-
get activeTokenCount(): number {
|
|
386
|
-
const now = Date.now();
|
|
387
|
-
let count = 0;
|
|
388
|
-
for (const token of this.tokens.values()) {
|
|
389
|
-
if (!token.consumed && now - token.createdAt <= TOKEN_TTL_MS) {
|
|
390
|
-
count++;
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
return count;
|
|
394
|
-
}
|
|
395
200
|
}
|
|
396
201
|
|
|
397
202
|
/** Shared singleton broker instance used by vault and browser tools. */
|
|
@@ -1,24 +1,29 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Credential metadata store.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* sites throughout the assistant daemon do not need to change.
|
|
4
|
+
* Reads stay on the workspace `metadata.json` file (sync). When a CES
|
|
5
|
+
* record backend is injected, upserts and deletes also write-through to
|
|
6
|
+
* CES so the CES catalog stays current.
|
|
8
7
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* exclusively managed by the SQLite oauth-store and have been removed
|
|
12
|
-
* from this interface as of v5.
|
|
8
|
+
* Tests: `_setMetadataPath` keeps the file-backed store and skips CES
|
|
9
|
+
* write-through.
|
|
13
10
|
*/
|
|
14
11
|
|
|
15
12
|
import { join } from "node:path";
|
|
16
13
|
|
|
17
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
credentialKey,
|
|
16
|
+
StaticCredentialMetadataStore,
|
|
17
|
+
} from "@vellumai/credential-storage";
|
|
18
|
+
import type { CredentialRecord } from "@vellumai/service-contracts/credential-rpc";
|
|
18
19
|
|
|
20
|
+
import type { CredentialRecordBackend } from "../../security/ces-rpc-record-backend.js";
|
|
21
|
+
import { getLogger } from "../../util/logger.js";
|
|
19
22
|
import { getDataDir } from "../../util/platform.js";
|
|
20
23
|
import type { CredentialInjectionTemplate } from "./policy-types.js";
|
|
21
24
|
|
|
25
|
+
const log = getLogger("credential-metadata-store");
|
|
26
|
+
|
|
22
27
|
/**
|
|
23
28
|
* CredentialMetadata extends the shared StaticCredentialRecord with
|
|
24
29
|
* assistant-specific injection template fields (composeWith, valueTransform).
|
|
@@ -43,12 +48,9 @@ export interface CredentialMetadata {
|
|
|
43
48
|
// Singleton store instance
|
|
44
49
|
// ---------------------------------------------------------------------------
|
|
45
50
|
|
|
46
|
-
/**
|
|
47
|
-
* Lazily initialised store instance. The path is determined on first access
|
|
48
|
-
* (or overridden via `_setMetadataPath` for tests).
|
|
49
|
-
*/
|
|
50
51
|
let _store: StaticCredentialMetadataStore | undefined;
|
|
51
52
|
let _overridePath: string | null = null;
|
|
53
|
+
let _recordBackend: CredentialRecordBackend | undefined;
|
|
52
54
|
|
|
53
55
|
function getStore(): StaticCredentialMetadataStore {
|
|
54
56
|
if (!_store) {
|
|
@@ -59,6 +61,48 @@ function getStore(): StaticCredentialMetadataStore {
|
|
|
59
61
|
return _store;
|
|
60
62
|
}
|
|
61
63
|
|
|
64
|
+
function toRecord(metadata: CredentialMetadata): CredentialRecord {
|
|
65
|
+
return {
|
|
66
|
+
credentialId: metadata.credentialId,
|
|
67
|
+
service: metadata.service,
|
|
68
|
+
field: metadata.field,
|
|
69
|
+
allowedTools: metadata.allowedTools,
|
|
70
|
+
allowedDomains: metadata.allowedDomains,
|
|
71
|
+
usageDescription: metadata.usageDescription,
|
|
72
|
+
alias: metadata.alias,
|
|
73
|
+
injectionTemplates: metadata.injectionTemplates,
|
|
74
|
+
createdAt: metadata.createdAt,
|
|
75
|
+
updatedAt: metadata.updatedAt,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async function persistRecord(metadata: CredentialMetadata): Promise<void> {
|
|
80
|
+
if (!_recordBackend || _overridePath) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
const account = credentialKey(metadata.service, metadata.field);
|
|
84
|
+
const ok = await _recordBackend.set(account, toRecord(metadata));
|
|
85
|
+
if (!ok) {
|
|
86
|
+
log.warn(
|
|
87
|
+
{ service: metadata.service, field: metadata.field },
|
|
88
|
+
"Failed to persist credential record to CES",
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** Await CES write-through for a record already in the local file store. */
|
|
94
|
+
export async function persistCredentialMetadata(
|
|
95
|
+
metadata: CredentialMetadata,
|
|
96
|
+
): Promise<void> {
|
|
97
|
+
await persistRecord(metadata);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function setCredentialRecordBackend(
|
|
101
|
+
backend: CredentialRecordBackend | undefined,
|
|
102
|
+
): void {
|
|
103
|
+
_recordBackend = backend;
|
|
104
|
+
}
|
|
105
|
+
|
|
62
106
|
// ---------------------------------------------------------------------------
|
|
63
107
|
// Public API - unchanged signatures, delegates to shared store
|
|
64
108
|
// ---------------------------------------------------------------------------
|
|
@@ -89,7 +133,9 @@ export function upsertCredentialMetadata(
|
|
|
89
133
|
injectionTemplates?: CredentialInjectionTemplate[] | null;
|
|
90
134
|
},
|
|
91
135
|
): CredentialMetadata {
|
|
92
|
-
|
|
136
|
+
const record = getStore().upsert(service, field, policy) as CredentialMetadata;
|
|
137
|
+
void persistRecord(record);
|
|
138
|
+
return record;
|
|
93
139
|
}
|
|
94
140
|
|
|
95
141
|
/**
|
|
@@ -127,13 +173,16 @@ export function deleteCredentialMetadata(
|
|
|
127
173
|
service: string,
|
|
128
174
|
field: string,
|
|
129
175
|
): boolean {
|
|
130
|
-
|
|
176
|
+
const deleted = getStore().delete(service, field);
|
|
177
|
+
if (deleted && _recordBackend && !_overridePath) {
|
|
178
|
+
void _recordBackend.delete(credentialKey(service, field));
|
|
179
|
+
}
|
|
180
|
+
return deleted;
|
|
131
181
|
}
|
|
132
182
|
|
|
133
183
|
/** @internal Test-only: override the metadata file path. */
|
|
134
184
|
export function _setMetadataPath(path: string | null): void {
|
|
135
185
|
_overridePath = path;
|
|
136
|
-
// Reset the store so it picks up the new path
|
|
137
186
|
if (_store) {
|
|
138
187
|
if (path) {
|
|
139
188
|
_store.setPath(path);
|
|
@@ -37,6 +37,7 @@ import {
|
|
|
37
37
|
import { getLogger } from "../../util/logger.js";
|
|
38
38
|
import {
|
|
39
39
|
assertMetadataWritable,
|
|
40
|
+
persistCredentialMetadata,
|
|
40
41
|
upsertCredentialMetadata,
|
|
41
42
|
} from "./metadata-store.js";
|
|
42
43
|
import type { CredentialInjectionTemplate } from "./policy-types.js";
|
|
@@ -161,6 +162,7 @@ export async function storeCredentialValue(
|
|
|
161
162
|
allowedDomains: input.allowedDomains,
|
|
162
163
|
injectionTemplates: input.injectionTemplates,
|
|
163
164
|
});
|
|
165
|
+
await persistCredentialMetadata(metadata);
|
|
164
166
|
const { syncManualTokenConnection } =
|
|
165
167
|
await import("../../oauth/manual-token-connection.js");
|
|
166
168
|
await syncManualTokenConnection(service);
|
package/src/tools/executor.ts
CHANGED
|
@@ -33,6 +33,7 @@ import { getWorkflowRunManager } from "../workflows/run-manager.js";
|
|
|
33
33
|
import { executeWithTimeout, safeTimeoutMs } from "./execution-timeout.js";
|
|
34
34
|
import { fileEditInputSchema } from "./filesystem/edit.js";
|
|
35
35
|
import { fileWriteInputSchema } from "./filesystem/write.js";
|
|
36
|
+
import { getHostShell } from "./host-shell.js";
|
|
36
37
|
import { PermissionChecker } from "./permission-checker.js";
|
|
37
38
|
import { getToolOwner } from "./registry.js";
|
|
38
39
|
import { extractAndSanitize } from "./sensitive-output-placeholders.js";
|
|
@@ -89,6 +90,7 @@ export class ToolExecutor {
|
|
|
89
90
|
undefined,
|
|
90
91
|
undefined,
|
|
91
92
|
context.signal,
|
|
93
|
+
getHostShell(context, input),
|
|
92
94
|
);
|
|
93
95
|
} catch {
|
|
94
96
|
// Stays undefined; the audit row records RISK_LEVEL_UNCLASSIFIED.
|
|
@@ -108,12 +110,6 @@ export class ToolExecutor {
|
|
|
108
110
|
let permApprovalMode: string | undefined;
|
|
109
111
|
let permApprovalReason: string | undefined;
|
|
110
112
|
let permRiskThreshold: string | undefined;
|
|
111
|
-
// Whether THIS invocation was interactively prompted. Distinct from the
|
|
112
|
-
// turn-level `context.approvedViaPrompt` (seeded from
|
|
113
|
-
// `approvedViaPromptThisTurn`, which stays true for later auto-approved
|
|
114
|
-
// tools in the same turn): the `permission_decided` telemetry must reflect
|
|
115
|
-
// a prompt for this specific call, not any prompt earlier in the turn.
|
|
116
|
-
let wasPromptedThisInvocation = false;
|
|
117
113
|
// Run pre-execution approval gates (abort, guardian policy,
|
|
118
114
|
// allowed-tool-set, task-run preflight, tool registry lookup). The gate
|
|
119
115
|
// resolves the tool's sandbox/host target internally.
|
|
@@ -253,7 +249,6 @@ export class ToolExecutor {
|
|
|
253
249
|
|
|
254
250
|
if (permResult.wasPrompted) {
|
|
255
251
|
context.approvedViaPrompt = true;
|
|
256
|
-
wasPromptedThisInvocation = true;
|
|
257
252
|
}
|
|
258
253
|
} else {
|
|
259
254
|
// Grant consumed — permission check was skipped. Set provenance explicitly
|
|
@@ -324,7 +319,6 @@ export class ToolExecutor {
|
|
|
324
319
|
matchedTrustRuleId: permMatchedTrustRuleId,
|
|
325
320
|
durationMs,
|
|
326
321
|
attribution: context.attribution ?? null,
|
|
327
|
-
wasPrompted: wasPromptedThisInvocation,
|
|
328
322
|
});
|
|
329
323
|
recordToolCompletion(
|
|
330
324
|
context.conversationId,
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { afterEach, describe, expect, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import { assistantEventHub } from "../runtime/assistant-event-hub.js";
|
|
4
|
+
import { getHostShell } from "./host-shell.js";
|
|
5
|
+
|
|
6
|
+
const subscriptions: Array<{ dispose(): void }> = [];
|
|
7
|
+
|
|
8
|
+
afterEach(() => {
|
|
9
|
+
for (const subscription of subscriptions.splice(0)) {
|
|
10
|
+
subscription.dispose();
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
function connectHostBashClient(
|
|
15
|
+
clientId: string,
|
|
16
|
+
interfaceId: "macos" | "windows",
|
|
17
|
+
): void {
|
|
18
|
+
subscriptions.push(
|
|
19
|
+
assistantEventHub.subscribe({
|
|
20
|
+
type: "client",
|
|
21
|
+
clientId,
|
|
22
|
+
interfaceId,
|
|
23
|
+
capabilities: ["host_bash"],
|
|
24
|
+
actorPrincipalId: "actor-1",
|
|
25
|
+
callback: () => {},
|
|
26
|
+
}),
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
describe("getHostShell", () => {
|
|
31
|
+
test("uses the explicitly targeted Windows client's shell", () => {
|
|
32
|
+
connectHostBashClient("windows-client", "windows");
|
|
33
|
+
|
|
34
|
+
expect(
|
|
35
|
+
getHostShell(
|
|
36
|
+
{
|
|
37
|
+
clientOs: "web",
|
|
38
|
+
transportInterface: "web",
|
|
39
|
+
sourceActorPrincipalId: "actor-1",
|
|
40
|
+
},
|
|
41
|
+
{ target_client_id: "windows-client" },
|
|
42
|
+
),
|
|
43
|
+
).toBe("powershell");
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test("uses the single same-user Windows client's shell", () => {
|
|
47
|
+
connectHostBashClient("windows-client", "windows");
|
|
48
|
+
|
|
49
|
+
expect(
|
|
50
|
+
getHostShell(
|
|
51
|
+
{
|
|
52
|
+
clientOs: "web",
|
|
53
|
+
transportInterface: "web",
|
|
54
|
+
sourceActorPrincipalId: "actor-1",
|
|
55
|
+
},
|
|
56
|
+
{},
|
|
57
|
+
),
|
|
58
|
+
).toBe("powershell");
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test("keeps Bash classification for a targeted macOS client", () => {
|
|
62
|
+
connectHostBashClient("mac-client", "macos");
|
|
63
|
+
|
|
64
|
+
expect(
|
|
65
|
+
getHostShell(
|
|
66
|
+
{
|
|
67
|
+
clientOs: "web",
|
|
68
|
+
transportInterface: "web",
|
|
69
|
+
sourceActorPrincipalId: "actor-1",
|
|
70
|
+
},
|
|
71
|
+
{ target_client_id: "mac-client" },
|
|
72
|
+
),
|
|
73
|
+
).toBeUndefined();
|
|
74
|
+
});
|
|
75
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { ClassifyRiskIpcParams } from "@vellumai/gateway-client";
|
|
2
|
+
|
|
3
|
+
import { resolveInvocationHostClientOs } from "./client-os.js";
|
|
4
|
+
import type { ToolContext } from "./types.js";
|
|
5
|
+
|
|
6
|
+
export function getHostShell(
|
|
7
|
+
context: Pick<
|
|
8
|
+
ToolContext,
|
|
9
|
+
"clientOs" | "transportInterface" | "sourceActorPrincipalId"
|
|
10
|
+
>,
|
|
11
|
+
input: Record<string, unknown>,
|
|
12
|
+
): ClassifyRiskIpcParams["shell"] {
|
|
13
|
+
const targetClientOs = resolveInvocationHostClientOs(
|
|
14
|
+
"host_bash",
|
|
15
|
+
input,
|
|
16
|
+
context,
|
|
17
|
+
);
|
|
18
|
+
return targetClientOs === "windows" ||
|
|
19
|
+
(targetClientOs === undefined &&
|
|
20
|
+
(context.clientOs === "windows" ||
|
|
21
|
+
context.transportInterface === "windows"))
|
|
22
|
+
? "powershell"
|
|
23
|
+
: undefined;
|
|
24
|
+
}
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
import { spawn } from "node:child_process";
|
|
10
10
|
import { existsSync } from "node:fs";
|
|
11
11
|
import { homedir } from "node:os";
|
|
12
|
-
import { isAbsolute } from "node:path";
|
|
12
|
+
import { isAbsolute, join } from "node:path";
|
|
13
13
|
|
|
14
14
|
import { z } from "zod";
|
|
15
15
|
|
|
@@ -24,6 +24,11 @@ import {
|
|
|
24
24
|
} from "../../runtime/assistant-event-hub.js";
|
|
25
25
|
import { conversationRevealNonce } from "../../runtime/reveal-nonce.js";
|
|
26
26
|
import { redactSecrets } from "../../security/secret-scanner.js";
|
|
27
|
+
import {
|
|
28
|
+
buildShellInvocation,
|
|
29
|
+
prependUniquePathEntries,
|
|
30
|
+
terminateProcessTree,
|
|
31
|
+
} from "../../util/host-process.js";
|
|
27
32
|
import { getLogger } from "../../util/logger.js";
|
|
28
33
|
import type { CompletedBackgroundTool } from "../background-tool-registry.js";
|
|
29
34
|
import {
|
|
@@ -67,15 +72,10 @@ const HOST_BASH_PROXY_ENV_KEYS = [
|
|
|
67
72
|
function buildHostShellEnv(): Record<string, string> {
|
|
68
73
|
const env = buildSanitizedEnv();
|
|
69
74
|
// Ensure ~/.local/bin and ~/.bun/bin are in PATH so `vellum` and `bun` are
|
|
70
|
-
//
|
|
71
|
-
// bundle that inherits a minimal PATH.
|
|
75
|
+
// reachable when a desktop app launches the assistant with a minimal PATH.
|
|
72
76
|
const home = homedir();
|
|
73
|
-
const extraDirs = [
|
|
74
|
-
|
|
75
|
-
const missing = extraDirs.filter((d) => !currentPath.split(":").includes(d));
|
|
76
|
-
if (missing.length > 0) {
|
|
77
|
-
env.PATH = [...missing, currentPath].filter(Boolean).join(":");
|
|
78
|
-
}
|
|
77
|
+
const extraDirs = [join(home, ".local", "bin"), join(home, ".bun", "bin")];
|
|
78
|
+
env.PATH = prependUniquePathEntries(env.PATH, extraDirs);
|
|
79
79
|
return env;
|
|
80
80
|
}
|
|
81
81
|
|
|
@@ -466,7 +466,8 @@ export const hostShellTool = {
|
|
|
466
466
|
const bgId = generateBackgroundToolId();
|
|
467
467
|
const startedAt = Date.now();
|
|
468
468
|
|
|
469
|
-
const
|
|
469
|
+
const wrapped = buildShellInvocation(command);
|
|
470
|
+
const child = spawn(wrapped.command, wrapped.args, {
|
|
470
471
|
cwd: workingDir,
|
|
471
472
|
env: hostEnv,
|
|
472
473
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -480,21 +481,7 @@ export const hostShellTool = {
|
|
|
480
481
|
// event reports "cancelled" rather than "failed".
|
|
481
482
|
let aborted = false;
|
|
482
483
|
|
|
483
|
-
const killTree = () =>
|
|
484
|
-
if (child.pid != null) {
|
|
485
|
-
try {
|
|
486
|
-
process.kill(-child.pid, "SIGKILL");
|
|
487
|
-
return;
|
|
488
|
-
} catch {
|
|
489
|
-
// Process group may have already exited — fall through.
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
try {
|
|
493
|
-
child.kill("SIGKILL");
|
|
494
|
-
} catch {
|
|
495
|
-
// Child may have already exited.
|
|
496
|
-
}
|
|
497
|
-
};
|
|
484
|
+
const killTree = () => terminateProcessTree(child);
|
|
498
485
|
|
|
499
486
|
const timer = setTimeout(() => {
|
|
500
487
|
timedOut = true;
|
|
@@ -666,31 +653,15 @@ export const hostShellTool = {
|
|
|
666
653
|
const stderrChunks: Buffer[] = [];
|
|
667
654
|
let timedOut = false;
|
|
668
655
|
|
|
669
|
-
const
|
|
656
|
+
const wrapped = buildShellInvocation(command);
|
|
657
|
+
const child = spawn(wrapped.command, wrapped.args, {
|
|
670
658
|
cwd: workingDir,
|
|
671
659
|
env: hostEnv,
|
|
672
660
|
stdio: ["ignore", "pipe", "pipe"],
|
|
673
661
|
detached: true,
|
|
674
662
|
});
|
|
675
663
|
|
|
676
|
-
|
|
677
|
-
// (negative PID), then falls back to killing the direct child if the
|
|
678
|
-
// PID is unavailable or the group kill fails.
|
|
679
|
-
const killTree = () => {
|
|
680
|
-
if (child.pid != null) {
|
|
681
|
-
try {
|
|
682
|
-
process.kill(-child.pid, "SIGKILL");
|
|
683
|
-
return;
|
|
684
|
-
} catch {
|
|
685
|
-
// Process group may have already exited — fall through.
|
|
686
|
-
}
|
|
687
|
-
}
|
|
688
|
-
try {
|
|
689
|
-
child.kill("SIGKILL");
|
|
690
|
-
} catch {
|
|
691
|
-
// Child may have already exited.
|
|
692
|
-
}
|
|
693
|
-
};
|
|
664
|
+
const killTree = () => terminateProcessTree(child);
|
|
694
665
|
|
|
695
666
|
const timer = setTimeout(() => {
|
|
696
667
|
timedOut = true;
|