@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
|
@@ -27,24 +27,59 @@ export interface McpToolMetadata {
|
|
|
27
27
|
annotations?: McpToolAnnotations;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
/** Risk levels ordered low to high, so a hint can step one place along it. */
|
|
31
|
+
const RISK_LADDER: readonly RiskLevel[] = [
|
|
32
|
+
RiskLevel.Low,
|
|
33
|
+
RiskLevel.Medium,
|
|
34
|
+
RiskLevel.High,
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
function stepRisk(risk: RiskLevel, direction: -1 | 1): RiskLevel {
|
|
38
|
+
const index = RISK_LADDER.indexOf(risk);
|
|
39
|
+
if (index === -1) {
|
|
40
|
+
return risk;
|
|
41
|
+
}
|
|
42
|
+
const next = Math.min(Math.max(index + direction, 0), RISK_LADDER.length - 1);
|
|
43
|
+
return RISK_LADDER[next] ?? risk;
|
|
44
|
+
}
|
|
45
|
+
|
|
30
46
|
/**
|
|
31
47
|
* Resolve the risk level a tool carries.
|
|
32
48
|
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
49
|
+
* The server's configured level is the anchor, and a tool's own MCP
|
|
50
|
+
* annotations move it at most one step along {@link RISK_LADDER}.
|
|
51
|
+
*
|
|
52
|
+
* `destructiveHint` steps up. Raising is the safe direction, so it applies at
|
|
53
|
+
* any server level.
|
|
54
|
+
*
|
|
55
|
+
* `readOnlyHint` steps down, and only from {@link RiskLevel.Medium}. Medium is
|
|
56
|
+
* the schema default, so a server sitting at High got there because the user
|
|
57
|
+
* put it there. A hint is self-reported by the server, and letting one lower a
|
|
58
|
+
* deliberately pinned server would hand a lying server the auto-approval the
|
|
59
|
+
* user withheld: under the Relaxed preset Medium carries no prompt at all. High
|
|
60
|
+
* is therefore the floor for downward hints.
|
|
61
|
+
*
|
|
62
|
+
* `destructiveHint` wins when a server sends both, since a tool that both reads
|
|
63
|
+
* and destroys is a tool that destroys.
|
|
64
|
+
*
|
|
65
|
+
* The MCP spec defaults `destructiveHint` to true when it is absent. This
|
|
66
|
+
* deliberately does not, because the server's configured level already answers
|
|
67
|
+
* that question: choosing a level for a server is the user's statement about
|
|
68
|
+
* what its unlabeled tools are worth. Honoring the spec default instead would
|
|
69
|
+
* put every tool of every unannotated server back at High, which is the
|
|
70
|
+
* behavior this replaces.
|
|
37
71
|
*/
|
|
38
72
|
function resolveRiskLevel(
|
|
39
73
|
metadata: McpToolMetadata,
|
|
40
74
|
serverConfig: McpServerConfig,
|
|
41
75
|
): RiskLevel {
|
|
42
76
|
const serverRisk = riskMap[serverConfig.defaultRiskLevel] ?? RiskLevel.High;
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
serverRisk
|
|
46
|
-
|
|
47
|
-
|
|
77
|
+
const annotations = metadata.annotations;
|
|
78
|
+
if (annotations?.destructiveHint === true) {
|
|
79
|
+
return stepRisk(serverRisk, 1);
|
|
80
|
+
}
|
|
81
|
+
if (annotations?.readOnlyHint === true && serverRisk === RiskLevel.Medium) {
|
|
82
|
+
return stepRisk(serverRisk, -1);
|
|
48
83
|
}
|
|
49
84
|
return serverRisk;
|
|
50
85
|
}
|
|
@@ -25,11 +25,15 @@ import type {
|
|
|
25
25
|
import { RiskLevel } from "../permissions/types.js";
|
|
26
26
|
import { resolveCapabilities } from "../runtime/capabilities.js";
|
|
27
27
|
import {
|
|
28
|
+
type PermissionPromptOutcome,
|
|
29
|
+
type PermissionPromptTelemetry,
|
|
28
30
|
recordToolDenied,
|
|
31
|
+
recordToolPermissionDecided,
|
|
29
32
|
recordToolPermissionPrompted,
|
|
30
33
|
} from "../telemetry/tool-audit.js";
|
|
31
34
|
import { getLogger } from "../util/logger.js";
|
|
32
35
|
import { resolveExecutionTarget } from "./execution-target.js";
|
|
36
|
+
import { getHostShell } from "./host-shell.js";
|
|
33
37
|
import { buildPolicyContext } from "./policy-context.js";
|
|
34
38
|
import { isSideEffectTool } from "./side-effects.js";
|
|
35
39
|
import type { Tool, ToolContext } from "./types.js";
|
|
@@ -124,6 +128,7 @@ export class PermissionChecker {
|
|
|
124
128
|
undefined,
|
|
125
129
|
undefined,
|
|
126
130
|
context.signal,
|
|
131
|
+
getHostShell(context, input),
|
|
127
132
|
);
|
|
128
133
|
const { level: risk, reason: riskReason } = classification;
|
|
129
134
|
const riskLevel: string = risk;
|
|
@@ -168,6 +173,7 @@ export class PermissionChecker {
|
|
|
168
173
|
policyContext.conversationId,
|
|
169
174
|
policyContext.executionContext,
|
|
170
175
|
cellQuery,
|
|
176
|
+
policyContext.requesterContactId,
|
|
171
177
|
);
|
|
172
178
|
const riskThreshold = conversationThreshold as RiskThreshold;
|
|
173
179
|
|
|
@@ -210,7 +216,6 @@ export class PermissionChecker {
|
|
|
210
216
|
reason: result.reason,
|
|
211
217
|
riskLevel,
|
|
212
218
|
durationMs: Date.now() - startTime,
|
|
213
|
-
wasPrompted: false,
|
|
214
219
|
});
|
|
215
220
|
const provenance = mapApprovalProvenance("denied", {});
|
|
216
221
|
return {
|
|
@@ -249,7 +254,6 @@ export class PermissionChecker {
|
|
|
249
254
|
"Inline-command skill load requires human approval; no interactive client connected",
|
|
250
255
|
riskLevel,
|
|
251
256
|
durationMs: Date.now() - startTime,
|
|
252
|
-
wasPrompted: false,
|
|
253
257
|
});
|
|
254
258
|
return {
|
|
255
259
|
allowed: false,
|
|
@@ -316,6 +320,7 @@ export class PermissionChecker {
|
|
|
316
320
|
context.conversationId,
|
|
317
321
|
"background",
|
|
318
322
|
cellQuery,
|
|
323
|
+
context.requesterContactId,
|
|
319
324
|
);
|
|
320
325
|
const thresholdOrdinal: Record<string, number> = {
|
|
321
326
|
none: -1,
|
|
@@ -361,7 +366,6 @@ export class PermissionChecker {
|
|
|
361
366
|
reason: "Non-interactive session: no client to approve prompt",
|
|
362
367
|
riskLevel,
|
|
363
368
|
durationMs: Date.now() - startTime,
|
|
364
|
-
wasPrompted: false,
|
|
365
369
|
});
|
|
366
370
|
return {
|
|
367
371
|
allowed: false,
|
|
@@ -385,27 +389,76 @@ export class PermissionChecker {
|
|
|
385
389
|
persistentDecisionsAllowed: !context.requireFreshApproval,
|
|
386
390
|
};
|
|
387
391
|
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
392
|
+
// Grouping dimensions shared by this prompt and its decision, so the
|
|
393
|
+
// two series can be diffed by risk and by access preset without
|
|
394
|
+
// parsing anything out of the event name.
|
|
395
|
+
const promptTelemetry: PermissionPromptTelemetry = {
|
|
396
|
+
toolName: name,
|
|
393
397
|
riskLevel,
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
398
|
+
riskThreshold,
|
|
399
|
+
surface: context.executionChannel,
|
|
400
|
+
conversationId: context.conversationId,
|
|
401
|
+
};
|
|
402
|
+
// An already-aborted signal makes the prompter return without
|
|
403
|
+
// registering or sending a confirmation request, so nothing reaches
|
|
404
|
+
// the user and neither half of the pair is recorded. `prompt()` reads
|
|
405
|
+
// the same signal synchronously on entry, with no await in between, so
|
|
406
|
+
// this check and its check always agree.
|
|
407
|
+
const promptIsSurfaced = context.signal?.aborted !== true;
|
|
408
|
+
if (promptIsSurfaced) {
|
|
409
|
+
recordToolPermissionPrompted(promptTelemetry);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
let response: Awaited<ReturnType<PermissionPrompter["prompt"]>>;
|
|
413
|
+
try {
|
|
414
|
+
response = await this.prompter.prompt(
|
|
415
|
+
name,
|
|
416
|
+
input,
|
|
417
|
+
riskLevel,
|
|
418
|
+
promptOptions.allowlistOptions,
|
|
419
|
+
promptOptions.scopeOptions,
|
|
420
|
+
previewDiff,
|
|
421
|
+
context.conversationId,
|
|
422
|
+
executionTarget,
|
|
423
|
+
promptOptions.persistentDecisionsAllowed,
|
|
424
|
+
context.signal,
|
|
425
|
+
context.toolUseId,
|
|
426
|
+
riskReason,
|
|
427
|
+
getIsContainerized(),
|
|
428
|
+
classification.directoryScopeOptions,
|
|
429
|
+
);
|
|
430
|
+
} catch (err) {
|
|
431
|
+
// The prompter rejected rather than resolved: it was disposed while
|
|
432
|
+
// this prompt was outstanding (client disconnect, conversation
|
|
433
|
+
// teardown). Nobody answered, so the prompt is abandoned.
|
|
434
|
+
if (promptIsSurfaced) {
|
|
435
|
+
recordToolPermissionDecided(promptTelemetry, "abandoned");
|
|
436
|
+
}
|
|
437
|
+
throw err;
|
|
438
|
+
}
|
|
406
439
|
|
|
407
440
|
const decision = response.decision;
|
|
408
441
|
|
|
442
|
+
// A prompt that ends without a human answer is abandoned, not denied.
|
|
443
|
+
// The daemon resolves a timeout, a superseding user message, and a
|
|
444
|
+
// turn abort to `deny` so the agent loop stops, but none of the three
|
|
445
|
+
// is a decision the user made.
|
|
446
|
+
let outcome: PermissionPromptOutcome;
|
|
447
|
+
if (
|
|
448
|
+
response.wasTimeout === true ||
|
|
449
|
+
response.wasSystemCancel === true ||
|
|
450
|
+
response.wasAbort === true
|
|
451
|
+
) {
|
|
452
|
+
outcome = "abandoned";
|
|
453
|
+
} else if (decision === "deny") {
|
|
454
|
+
outcome = "deny";
|
|
455
|
+
} else {
|
|
456
|
+
outcome = "allow";
|
|
457
|
+
}
|
|
458
|
+
if (promptIsSurfaced) {
|
|
459
|
+
recordToolPermissionDecided(promptTelemetry, outcome);
|
|
460
|
+
}
|
|
461
|
+
|
|
409
462
|
if (decision === "deny") {
|
|
410
463
|
const contextualDenial =
|
|
411
464
|
typeof response.decisionContext === "string"
|
|
@@ -426,7 +479,6 @@ export class PermissionChecker {
|
|
|
426
479
|
reason: denialReason,
|
|
427
480
|
riskLevel,
|
|
428
481
|
durationMs: Date.now() - startTime,
|
|
429
|
-
wasPrompted: true,
|
|
430
482
|
});
|
|
431
483
|
return {
|
|
432
484
|
allowed: false,
|
|
@@ -62,6 +62,7 @@ export function buildPolicyContext(
|
|
|
62
62
|
const originSignals = {
|
|
63
63
|
requestOrigin: context?.requestOrigin,
|
|
64
64
|
...channelCoordinatesFromToolContext(context),
|
|
65
|
+
requesterContactId: context?.requesterContactId,
|
|
65
66
|
// Precompute the proc-to-skills gate — the v3 tier being active, i.e.
|
|
66
67
|
// memory on AND v3 live — here so the permission checker, a leaf module
|
|
67
68
|
// that must not read config, can deny the memory-retrospective
|
|
@@ -76,7 +76,7 @@ async function buildImageToolResult(
|
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
// Detect actual format from magic bytes - never trust the file extension
|
|
79
|
-
// alone, since
|
|
79
|
+
// alone, since conversion produces JPEG and files can be misnamed.
|
|
80
80
|
const detectedType = detectMediaType(buffer);
|
|
81
81
|
if (!detectedType) {
|
|
82
82
|
return {
|
|
@@ -3,6 +3,10 @@ import { loadSkillCatalog } from "../../config/skills.js";
|
|
|
3
3
|
import { nearestExistingSkills } from "../../plugins/defaults/memory/v3/candidate-match.js";
|
|
4
4
|
import { readInstallMeta } from "../../skills/install-meta.js";
|
|
5
5
|
import { getManagedSkillDir } from "../../skills/managed-store.js";
|
|
6
|
+
import {
|
|
7
|
+
filterSkillsByPlatform,
|
|
8
|
+
type SkillPlatform,
|
|
9
|
+
} from "../../skills/platform-compatibility.js";
|
|
6
10
|
import type { OwnerInfo, ToolContext, ToolExecutionResult } from "../types.js";
|
|
7
11
|
|
|
8
12
|
/**
|
|
@@ -45,6 +49,7 @@ export async function executeFindSimilarSkills(
|
|
|
45
49
|
description: string;
|
|
46
50
|
source: SkillSource;
|
|
47
51
|
owner?: OwnerInfo;
|
|
52
|
+
platforms?: SkillPlatform[];
|
|
48
53
|
}[];
|
|
49
54
|
} = {},
|
|
50
55
|
): Promise<ToolExecutionResult> {
|
|
@@ -92,8 +97,11 @@ export async function executeFindSimilarSkills(
|
|
|
92
97
|
// slices only those. Filtering after the limit would let out-of-scope
|
|
93
98
|
// high-rank matches consume slots and starve usable in-scope skills out of
|
|
94
99
|
// the result. `null` set = pass the catalog through unchanged.
|
|
95
|
-
const scopedCatalog =
|
|
96
|
-
enabledPluginSet === null
|
|
100
|
+
const scopedCatalog = filterSkillsByPlatform(
|
|
101
|
+
enabledPluginSet === null
|
|
102
|
+
? catalog
|
|
103
|
+
: catalog.filter((skill) => !outOfScope(skill)),
|
|
104
|
+
);
|
|
97
105
|
|
|
98
106
|
const hits = await findNearest(goal, {
|
|
99
107
|
limit,
|
package/src/tools/skills/load.ts
CHANGED
|
@@ -21,10 +21,15 @@ import {
|
|
|
21
21
|
validateIncludeCycles,
|
|
22
22
|
} from "../../skills/include-graph.js";
|
|
23
23
|
import { renderInlineCommands } from "../../skills/inline-command-render.js";
|
|
24
|
+
import {
|
|
25
|
+
isSkillCompatibleWithPlatform,
|
|
26
|
+
skillPlatformUnavailableMessage,
|
|
27
|
+
} from "../../skills/platform-compatibility.js";
|
|
24
28
|
import { parseToolManifestFile } from "../../skills/tool-manifest.js";
|
|
25
29
|
import { computeSkillVersionHash } from "../../skills/version-hash.js";
|
|
26
30
|
import { getLogger } from "../../util/logger.js";
|
|
27
31
|
import { getWorkspaceDirDisplay } from "../../util/platform.js";
|
|
32
|
+
import { supportsClientOsForSkillTool } from "../client-os.js";
|
|
28
33
|
import type {
|
|
29
34
|
ToolContext,
|
|
30
35
|
ToolDefinition,
|
|
@@ -83,8 +88,23 @@ function loadToolManifest(
|
|
|
83
88
|
*/
|
|
84
89
|
function formatToolSchemas(
|
|
85
90
|
manifest: SkillToolManifest,
|
|
91
|
+
context: Pick<
|
|
92
|
+
ToolContext,
|
|
93
|
+
"clientOs" | "transportInterface" | "sourceActorPrincipalId"
|
|
94
|
+
>,
|
|
86
95
|
childSkillName?: string,
|
|
87
|
-
): string {
|
|
96
|
+
): string | undefined {
|
|
97
|
+
const tools = manifest.tools.filter((tool) =>
|
|
98
|
+
supportsClientOsForSkillTool(tool.supported_client_os, tool.name, {
|
|
99
|
+
clientOs: context.clientOs,
|
|
100
|
+
transportInterface: context.transportInterface,
|
|
101
|
+
sourceActorPrincipalId: context.sourceActorPrincipalId,
|
|
102
|
+
}),
|
|
103
|
+
);
|
|
104
|
+
if (tools.length === 0) {
|
|
105
|
+
return undefined;
|
|
106
|
+
}
|
|
107
|
+
|
|
88
108
|
const lines: string[] = childSkillName
|
|
89
109
|
? [`### Tools from ${childSkillName}`, ""]
|
|
90
110
|
: [
|
|
@@ -96,7 +116,7 @@ function formatToolSchemas(
|
|
|
96
116
|
|
|
97
117
|
const toolHeadingLevel = childSkillName ? "####" : "###";
|
|
98
118
|
|
|
99
|
-
for (const tool of
|
|
119
|
+
for (const tool of tools) {
|
|
100
120
|
lines.push(`${toolHeadingLevel} ${tool.name}`);
|
|
101
121
|
lines.push(
|
|
102
122
|
tool.description.replaceAll("{workspaceDir}", getWorkspaceDirDisplay()),
|
|
@@ -211,6 +231,13 @@ export const skillLoadTool = {
|
|
|
211
231
|
|
|
212
232
|
const skill = loaded.skill;
|
|
213
233
|
|
|
234
|
+
if (!isSkillCompatibleWithPlatform(skill)) {
|
|
235
|
+
return {
|
|
236
|
+
content: `Error: ${skillPlatformUnavailableMessage(skill.id, skill)}`,
|
|
237
|
+
isError: true,
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
|
|
214
241
|
// Per-chat plugin scope gate: a plugin-owned skill whose owning plugin is
|
|
215
242
|
// outside the conversation's effective set must not have its instructions
|
|
216
243
|
// loaded. Mirrors the projection/tools filter's owner-id lookup
|
|
@@ -403,7 +430,7 @@ export const skillLoadTool = {
|
|
|
403
430
|
// Load tool schemas for the main skill
|
|
404
431
|
const mainManifest = loadToolManifest(skill.directoryPath);
|
|
405
432
|
const toolSchemasSection = mainManifest
|
|
406
|
-
? formatToolSchemas(mainManifest)
|
|
433
|
+
? formatToolSchemas(mainManifest, context)
|
|
407
434
|
: undefined;
|
|
408
435
|
|
|
409
436
|
// Build immediate children metadata section and load included skill bodies
|
|
@@ -422,6 +449,9 @@ export const skillLoadTool = {
|
|
|
422
449
|
if (childOutOfPluginScope(child)) {
|
|
423
450
|
continue;
|
|
424
451
|
}
|
|
452
|
+
if (!isSkillCompatibleWithPlatform(child)) {
|
|
453
|
+
continue;
|
|
454
|
+
}
|
|
425
455
|
const childFlagKey = skillFlagKey(child);
|
|
426
456
|
if (
|
|
427
457
|
childFlagKey &&
|
|
@@ -518,10 +548,15 @@ export const skillLoadTool = {
|
|
|
518
548
|
childLoaded.skill.directoryPath,
|
|
519
549
|
);
|
|
520
550
|
if (childManifest) {
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
551
|
+
const childSchemas = formatToolSchemas(
|
|
552
|
+
childManifest,
|
|
553
|
+
context,
|
|
554
|
+
childLoaded.skill.displayName,
|
|
524
555
|
);
|
|
556
|
+
if (childSchemas) {
|
|
557
|
+
anyChildHasTools = true;
|
|
558
|
+
includedBodies.push(childSchemas);
|
|
559
|
+
}
|
|
525
560
|
}
|
|
526
561
|
}
|
|
527
562
|
}
|
|
@@ -5,6 +5,10 @@ import { join, resolve } from "node:path";
|
|
|
5
5
|
|
|
6
6
|
import { conversationRevealNonce } from "../../runtime/reveal-nonce.js";
|
|
7
7
|
import { computeSkillVersionHash } from "../../skills/version-hash.js";
|
|
8
|
+
import {
|
|
9
|
+
buildShellInvocation,
|
|
10
|
+
terminateProcessTree,
|
|
11
|
+
} from "../../util/host-process.js";
|
|
8
12
|
import { safeStringSlice } from "../../util/unicode.js";
|
|
9
13
|
import { buildSanitizedEnv } from "../terminal/safe-env.js";
|
|
10
14
|
import type { ToolContext, ToolExecutionResult } from "../types.js";
|
|
@@ -139,7 +143,7 @@ function spawnRunner(
|
|
|
139
143
|
let timedOut = false;
|
|
140
144
|
|
|
141
145
|
const bunRunCmd = "bun run __skill_runner.ts";
|
|
142
|
-
const wrapped =
|
|
146
|
+
const wrapped = buildShellInvocation(bunRunCmd);
|
|
143
147
|
|
|
144
148
|
const env = buildSanitizedEnv();
|
|
145
149
|
env.__SKILL_INPUT_JSON = JSON.stringify(input);
|
|
@@ -161,28 +165,16 @@ function spawnRunner(
|
|
|
161
165
|
|
|
162
166
|
const timer = setTimeout(() => {
|
|
163
167
|
timedOut = true;
|
|
164
|
-
|
|
165
|
-
process.kill(-child.pid!, "SIGKILL");
|
|
166
|
-
} catch {
|
|
167
|
-
// Process group may have already exited.
|
|
168
|
-
}
|
|
168
|
+
terminateProcessTree(child);
|
|
169
169
|
}, timeoutMs);
|
|
170
170
|
|
|
171
171
|
// Cooperative cancellation via AbortSignal
|
|
172
172
|
const onAbort = () => {
|
|
173
|
-
|
|
174
|
-
process.kill(-child.pid!, "SIGKILL");
|
|
175
|
-
} catch {
|
|
176
|
-
// Process group may have already exited.
|
|
177
|
-
}
|
|
173
|
+
terminateProcessTree(child);
|
|
178
174
|
};
|
|
179
175
|
if (context.signal) {
|
|
180
176
|
if (context.signal.aborted) {
|
|
181
|
-
|
|
182
|
-
process.kill(-child.pid!, "SIGKILL");
|
|
183
|
-
} catch {
|
|
184
|
-
// Process group may have already exited.
|
|
185
|
-
}
|
|
177
|
+
terminateProcessTree(child);
|
|
186
178
|
} else {
|
|
187
179
|
context.signal.addEventListener("abort", onAbort, { once: true });
|
|
188
180
|
}
|
|
@@ -39,6 +39,7 @@ export function createSkillTool(
|
|
|
39
39
|
category: entry.category,
|
|
40
40
|
defaultRiskLevel: riskMap[entry.risk],
|
|
41
41
|
executionTarget: entry.execution_target as ExecutionTarget,
|
|
42
|
+
supportedClientOs: entry.supported_client_os,
|
|
42
43
|
|
|
43
44
|
input_schema: entry.input_schema as object,
|
|
44
45
|
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { afterEach, describe, expect, test } from "bun:test";
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
buildSanitizedEnv,
|
|
5
|
+
SAFE_ENV_VARS,
|
|
6
|
+
WINDOWS_SAFE_ENV_VARS,
|
|
7
|
+
} from "../safe-env.js";
|
|
4
8
|
|
|
5
9
|
describe("safe-env Qdrant forwarding", () => {
|
|
6
10
|
const priorPort = process.env.QDRANT_HTTP_PORT;
|
|
@@ -35,3 +39,62 @@ describe("safe-env Qdrant forwarding", () => {
|
|
|
35
39
|
expect(env.QDRANT_URL).toBeUndefined();
|
|
36
40
|
});
|
|
37
41
|
});
|
|
42
|
+
|
|
43
|
+
describe("safe-env Windows forwarding", () => {
|
|
44
|
+
test("forwards runtime paths only to Windows children", () => {
|
|
45
|
+
const keys = [
|
|
46
|
+
"SystemRoot",
|
|
47
|
+
"COMSPEC",
|
|
48
|
+
"USERPROFILE",
|
|
49
|
+
"LOCALAPPDATA",
|
|
50
|
+
"PATHEXT",
|
|
51
|
+
] as const;
|
|
52
|
+
const previous = Object.fromEntries(
|
|
53
|
+
keys.map((key) => [key, process.env[key]]),
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
process.env.SystemRoot = "C:\\Windows";
|
|
58
|
+
process.env.COMSPEC = "C:\\Windows\\System32\\cmd.exe";
|
|
59
|
+
process.env.USERPROFILE = "C:\\Users\\Alice";
|
|
60
|
+
process.env.LOCALAPPDATA = "C:\\Users\\Alice\\AppData\\Local";
|
|
61
|
+
process.env.PATHEXT = ".COM;.EXE;.BAT;.CMD";
|
|
62
|
+
|
|
63
|
+
expect(WINDOWS_SAFE_ENV_VARS).toContain("SystemRoot");
|
|
64
|
+
const windowsEnv = buildSanitizedEnv("win32");
|
|
65
|
+
expect(windowsEnv.SystemRoot).toBe("C:\\Windows");
|
|
66
|
+
expect(windowsEnv.COMSPEC).toBe("C:\\Windows\\System32\\cmd.exe");
|
|
67
|
+
expect(windowsEnv.USERPROFILE).toBe("C:\\Users\\Alice");
|
|
68
|
+
expect(windowsEnv.LOCALAPPDATA).toBe("C:\\Users\\Alice\\AppData\\Local");
|
|
69
|
+
expect(windowsEnv.PATHEXT).toBe(".COM;.EXE;.BAT;.CMD");
|
|
70
|
+
|
|
71
|
+
const posixEnv = buildSanitizedEnv("linux");
|
|
72
|
+
expect(posixEnv.SystemRoot).toBeUndefined();
|
|
73
|
+
expect(posixEnv.COMSPEC).toBeUndefined();
|
|
74
|
+
expect(posixEnv.USERPROFILE).toBeUndefined();
|
|
75
|
+
} finally {
|
|
76
|
+
for (const key of keys) {
|
|
77
|
+
const value = previous[key];
|
|
78
|
+
if (value == null) {
|
|
79
|
+
delete process.env[key];
|
|
80
|
+
} else {
|
|
81
|
+
process.env[key] = value;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test("matches Windows environment names case-insensitively", () => {
|
|
88
|
+
const windowsEnv = buildSanitizedEnv("win32", {
|
|
89
|
+
Path: "C:\\Windows\\System32;C:\\Program Files\\Vellum",
|
|
90
|
+
systemroot: "C:\\Windows",
|
|
91
|
+
ComSpec: "C:\\Windows\\System32\\cmd.exe",
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
expect(windowsEnv.PATH).toBe(
|
|
95
|
+
"C:\\Windows\\System32;C:\\Program Files\\Vellum",
|
|
96
|
+
);
|
|
97
|
+
expect(windowsEnv.SystemRoot).toBe("C:\\Windows");
|
|
98
|
+
expect(windowsEnv.COMSPEC).toBe("C:\\Windows\\System32\\cmd.exe");
|
|
99
|
+
});
|
|
100
|
+
});
|
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { readdirSync } from "node:fs";
|
|
9
9
|
|
|
10
|
+
import { pathListDelimiter } from "@vellumai/environments/shell";
|
|
11
|
+
|
|
10
12
|
import { getGatewayInternalBaseUrl } from "../../config/env.js";
|
|
11
13
|
import { getDataDir, getWorkspaceDir } from "../../util/platform.js";
|
|
12
14
|
|
|
@@ -76,6 +78,18 @@ export const SAFE_ENV_VARS = [
|
|
|
76
78
|
"VELLUM_BACKUP_KEY_PATH",
|
|
77
79
|
] as const;
|
|
78
80
|
|
|
81
|
+
export const WINDOWS_SAFE_ENV_VARS = [
|
|
82
|
+
"SystemRoot",
|
|
83
|
+
"COMSPEC",
|
|
84
|
+
"USERPROFILE",
|
|
85
|
+
"APPDATA",
|
|
86
|
+
"LOCALAPPDATA",
|
|
87
|
+
"TEMP",
|
|
88
|
+
"TMP",
|
|
89
|
+
"PATHEXT",
|
|
90
|
+
"SystemDrive",
|
|
91
|
+
] as const;
|
|
92
|
+
|
|
79
93
|
export const KATA_SAFE_ENV_VARS = [
|
|
80
94
|
"VELLUM_APT_DATA_ROOT",
|
|
81
95
|
"VELLUM_APT_DATA_SUITE",
|
|
@@ -165,26 +179,42 @@ export const ALWAYS_INJECTED_ENV_VARS = [
|
|
|
165
179
|
function appendUniquePathEntries(
|
|
166
180
|
value: string | undefined,
|
|
167
181
|
entries: readonly string[],
|
|
182
|
+
separator = pathListDelimiter(),
|
|
168
183
|
): string {
|
|
169
|
-
const parts = value ? value.split(
|
|
184
|
+
const parts = value ? value.split(separator).filter(Boolean) : [];
|
|
170
185
|
for (const entry of entries) {
|
|
171
186
|
if (!parts.includes(entry)) {
|
|
172
187
|
parts.push(entry);
|
|
173
188
|
}
|
|
174
189
|
}
|
|
175
|
-
return parts.join(
|
|
190
|
+
return parts.join(separator);
|
|
176
191
|
}
|
|
177
192
|
|
|
178
|
-
export function buildSanitizedEnv(
|
|
193
|
+
export function buildSanitizedEnv(
|
|
194
|
+
hostPlatform: NodeJS.Platform = process.platform,
|
|
195
|
+
sourceEnv: NodeJS.ProcessEnv = process.env,
|
|
196
|
+
): Record<string, string> {
|
|
179
197
|
const env: Record<string, string> = {};
|
|
180
|
-
const isKataRuntime = isKataFamilyRuntime(
|
|
198
|
+
const isKataRuntime = isKataFamilyRuntime(sourceEnv.VELLUM_SANDBOX_RUNTIME);
|
|
199
|
+
const platformVars =
|
|
200
|
+
hostPlatform === "win32" ? WINDOWS_SAFE_ENV_VARS : ([] as const);
|
|
181
201
|
const safeEnvVars = isKataRuntime
|
|
182
|
-
? [...SAFE_ENV_VARS, ...KATA_SAFE_ENV_VARS]
|
|
183
|
-
: SAFE_ENV_VARS;
|
|
202
|
+
? [...SAFE_ENV_VARS, ...platformVars, ...KATA_SAFE_ENV_VARS]
|
|
203
|
+
: [...SAFE_ENV_VARS, ...platformVars];
|
|
184
204
|
|
|
205
|
+
const windowsEnv =
|
|
206
|
+
hostPlatform === "win32"
|
|
207
|
+
? new Map(
|
|
208
|
+
Object.entries(sourceEnv).map(([key, value]) => [
|
|
209
|
+
key.toLowerCase(),
|
|
210
|
+
value,
|
|
211
|
+
]),
|
|
212
|
+
)
|
|
213
|
+
: null;
|
|
185
214
|
for (const key of safeEnvVars) {
|
|
186
|
-
|
|
187
|
-
|
|
215
|
+
const value = windowsEnv?.get(key.toLowerCase()) ?? sourceEnv[key];
|
|
216
|
+
if (value != null) {
|
|
217
|
+
env[key] = value;
|
|
188
218
|
}
|
|
189
219
|
}
|
|
190
220
|
if (isKataRuntime) {
|
|
@@ -207,7 +237,7 @@ export function buildSanitizedEnv(): Record<string, string> {
|
|
|
207
237
|
env.BUN_INSTALL = `${env.HOME}/.bun`;
|
|
208
238
|
env.PATH = appendUniquePathEntries(
|
|
209
239
|
`${env.PYTHONUSERBASE}/bin:${env.BUN_INSTALL}/bin`,
|
|
210
|
-
env.PATH.split(
|
|
240
|
+
env.PATH.split(pathListDelimiter()).filter(Boolean),
|
|
211
241
|
);
|
|
212
242
|
}
|
|
213
243
|
}
|