@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,5 +1,5 @@
|
|
|
1
1
|
import { randomBytes } from "node:crypto";
|
|
2
|
-
import { existsSync, mkdirSync,
|
|
2
|
+
import { existsSync, mkdirSync, rmSync } from "node:fs";
|
|
3
3
|
import { tmpdir } from "node:os";
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
@@ -20,231 +20,6 @@ const TEST_DIR = join(
|
|
|
20
20
|
);
|
|
21
21
|
const STORE_PATH = join(TEST_DIR, "keys.enc");
|
|
22
22
|
|
|
23
|
-
describe("CredentialBroker", () => {
|
|
24
|
-
let broker: CredentialBroker;
|
|
25
|
-
let tmpDir: string;
|
|
26
|
-
|
|
27
|
-
beforeEach(() => {
|
|
28
|
-
tmpDir = mkdtempSync(join(tmpdir(), "broker-test-"));
|
|
29
|
-
_setMetadataPath(join(tmpDir, "metadata.json"));
|
|
30
|
-
broker = new CredentialBroker();
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
afterEach(() => {
|
|
34
|
-
_setMetadataPath(null);
|
|
35
|
-
rmSync(tmpDir, { recursive: true, force: true });
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
describe("authorize", () => {
|
|
39
|
-
test("denies when no credential metadata exists", () => {
|
|
40
|
-
const result = broker.authorize({
|
|
41
|
-
service: "github",
|
|
42
|
-
field: "token",
|
|
43
|
-
toolName: "browser_fill_credential",
|
|
44
|
-
});
|
|
45
|
-
expect(result.authorized).toBe(false);
|
|
46
|
-
if (!result.authorized) {
|
|
47
|
-
expect(result.reason).toContain("No credential found");
|
|
48
|
-
}
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
test("authorizes when tool is in allowedTools", () => {
|
|
52
|
-
upsertCredentialMetadata("github", "token", {
|
|
53
|
-
allowedTools: ["browser_fill_credential"],
|
|
54
|
-
});
|
|
55
|
-
const result = broker.authorize({
|
|
56
|
-
service: "github",
|
|
57
|
-
field: "token",
|
|
58
|
-
toolName: "browser_fill_credential",
|
|
59
|
-
});
|
|
60
|
-
expect(result.authorized).toBe(true);
|
|
61
|
-
if (result.authorized) {
|
|
62
|
-
expect(result.token.service).toBe("github");
|
|
63
|
-
expect(result.token.field).toBe("token");
|
|
64
|
-
expect(result.token.toolName).toBe("browser_fill_credential");
|
|
65
|
-
expect(result.token.consumed).toBe(false);
|
|
66
|
-
expect(result.token.tokenId).toBeTruthy();
|
|
67
|
-
}
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
test("denies when tool is not in allowedTools", () => {
|
|
71
|
-
upsertCredentialMetadata("github", "token", {
|
|
72
|
-
allowedTools: ["other_tool"],
|
|
73
|
-
});
|
|
74
|
-
const result = broker.authorize({
|
|
75
|
-
service: "github",
|
|
76
|
-
field: "token",
|
|
77
|
-
toolName: "browser_fill_credential",
|
|
78
|
-
});
|
|
79
|
-
expect(result.authorized).toBe(false);
|
|
80
|
-
if (!result.authorized) {
|
|
81
|
-
expect(result.reason).toContain("not allowed");
|
|
82
|
-
expect(result.reason).toContain("browser_fill_credential");
|
|
83
|
-
expect(result.reason).toContain("Allowed tools: other_tool");
|
|
84
|
-
}
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
test("denies when allowedTools is empty (fail-closed)", () => {
|
|
88
|
-
upsertCredentialMetadata("github", "token");
|
|
89
|
-
const result = broker.authorize({
|
|
90
|
-
service: "github",
|
|
91
|
-
field: "token",
|
|
92
|
-
toolName: "browser_fill_credential",
|
|
93
|
-
});
|
|
94
|
-
expect(result.authorized).toBe(false);
|
|
95
|
-
if (!result.authorized) {
|
|
96
|
-
expect(result.reason).toContain("not allowed");
|
|
97
|
-
expect(result.reason).toContain("No tools are currently allowed");
|
|
98
|
-
expect(result.reason).toContain("assistant credentials prompt");
|
|
99
|
-
}
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
test("issues unique token IDs", () => {
|
|
103
|
-
upsertCredentialMetadata("github", "token", {
|
|
104
|
-
allowedTools: ["tool1", "tool2"],
|
|
105
|
-
});
|
|
106
|
-
const r1 = broker.authorize({
|
|
107
|
-
service: "github",
|
|
108
|
-
field: "token",
|
|
109
|
-
toolName: "tool1",
|
|
110
|
-
});
|
|
111
|
-
const r2 = broker.authorize({
|
|
112
|
-
service: "github",
|
|
113
|
-
field: "token",
|
|
114
|
-
toolName: "tool2",
|
|
115
|
-
});
|
|
116
|
-
expect(r1.authorized).toBe(true);
|
|
117
|
-
expect(r2.authorized).toBe(true);
|
|
118
|
-
if (r1.authorized && r2.authorized) {
|
|
119
|
-
expect(r1.token.tokenId).not.toBe(r2.token.tokenId);
|
|
120
|
-
}
|
|
121
|
-
});
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
describe("consume", () => {
|
|
125
|
-
test("returns storage key on first consumption", () => {
|
|
126
|
-
upsertCredentialMetadata("github", "token", {
|
|
127
|
-
allowedTools: ["browser_fill_credential"],
|
|
128
|
-
});
|
|
129
|
-
const auth = broker.authorize({
|
|
130
|
-
service: "github",
|
|
131
|
-
field: "token",
|
|
132
|
-
toolName: "browser_fill_credential",
|
|
133
|
-
});
|
|
134
|
-
expect(auth.authorized).toBe(true);
|
|
135
|
-
if (!auth.authorized) {
|
|
136
|
-
return;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
const result = broker.consume(auth.token.tokenId);
|
|
140
|
-
expect(result.success).toBe(true);
|
|
141
|
-
expect(result.storageKey).toBe(credentialKey("github", "token"));
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
test("rejects double consumption", () => {
|
|
145
|
-
upsertCredentialMetadata("github", "token", {
|
|
146
|
-
allowedTools: ["browser_fill_credential"],
|
|
147
|
-
});
|
|
148
|
-
const auth = broker.authorize({
|
|
149
|
-
service: "github",
|
|
150
|
-
field: "token",
|
|
151
|
-
toolName: "browser_fill_credential",
|
|
152
|
-
});
|
|
153
|
-
if (!auth.authorized) {
|
|
154
|
-
return;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
broker.consume(auth.token.tokenId);
|
|
158
|
-
const result = broker.consume(auth.token.tokenId);
|
|
159
|
-
expect(result.success).toBe(false);
|
|
160
|
-
expect(result.reason).toContain("already consumed");
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
test("rejects unknown token ID", () => {
|
|
164
|
-
const result = broker.consume("nonexistent-token");
|
|
165
|
-
expect(result.success).toBe(false);
|
|
166
|
-
expect(result.reason).toContain("not found");
|
|
167
|
-
});
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
describe("revoke", () => {
|
|
171
|
-
test("revokes existing token", () => {
|
|
172
|
-
upsertCredentialMetadata("github", "token", {
|
|
173
|
-
allowedTools: ["browser_fill_credential"],
|
|
174
|
-
});
|
|
175
|
-
const auth = broker.authorize({
|
|
176
|
-
service: "github",
|
|
177
|
-
field: "token",
|
|
178
|
-
toolName: "browser_fill_credential",
|
|
179
|
-
});
|
|
180
|
-
if (!auth.authorized) {
|
|
181
|
-
return;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
expect(broker.revoke(auth.token.tokenId)).toBe(true);
|
|
185
|
-
// After revocation, consume should fail
|
|
186
|
-
const result = broker.consume(auth.token.tokenId);
|
|
187
|
-
expect(result.success).toBe(false);
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
test("returns false for unknown token", () => {
|
|
191
|
-
expect(broker.revoke("nonexistent")).toBe(false);
|
|
192
|
-
});
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
describe("revokeAll", () => {
|
|
196
|
-
test("clears all active tokens", () => {
|
|
197
|
-
upsertCredentialMetadata("github", "token", {
|
|
198
|
-
allowedTools: ["tool1", "tool2"],
|
|
199
|
-
});
|
|
200
|
-
broker.authorize({
|
|
201
|
-
service: "github",
|
|
202
|
-
field: "token",
|
|
203
|
-
toolName: "tool1",
|
|
204
|
-
});
|
|
205
|
-
broker.authorize({
|
|
206
|
-
service: "github",
|
|
207
|
-
field: "token",
|
|
208
|
-
toolName: "tool2",
|
|
209
|
-
});
|
|
210
|
-
expect(broker.activeTokenCount).toBe(2);
|
|
211
|
-
|
|
212
|
-
broker.revokeAll();
|
|
213
|
-
expect(broker.activeTokenCount).toBe(0);
|
|
214
|
-
});
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
describe("activeTokenCount", () => {
|
|
218
|
-
test("counts only unconsumed tokens", () => {
|
|
219
|
-
upsertCredentialMetadata("github", "token", {
|
|
220
|
-
allowedTools: ["tool1", "tool2"],
|
|
221
|
-
});
|
|
222
|
-
const auth1 = broker.authorize({
|
|
223
|
-
service: "github",
|
|
224
|
-
field: "token",
|
|
225
|
-
toolName: "tool1",
|
|
226
|
-
});
|
|
227
|
-
broker.authorize({
|
|
228
|
-
service: "github",
|
|
229
|
-
field: "token",
|
|
230
|
-
toolName: "tool2",
|
|
231
|
-
});
|
|
232
|
-
expect(broker.activeTokenCount).toBe(2);
|
|
233
|
-
|
|
234
|
-
if (auth1.authorized) {
|
|
235
|
-
broker.consume(auth1.token.tokenId);
|
|
236
|
-
}
|
|
237
|
-
expect(broker.activeTokenCount).toBe(1);
|
|
238
|
-
});
|
|
239
|
-
});
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
// ---------------------------------------------------------------------------
|
|
243
|
-
// Behavior that reads stored secret values, exercised against the encrypted
|
|
244
|
-
// store backend (transient injection, multi-template serverUseById, revokeAll,
|
|
245
|
-
// and canonical/legacy capability-key resolution).
|
|
246
|
-
// ---------------------------------------------------------------------------
|
|
247
|
-
|
|
248
23
|
describe("CredentialBroker (encrypted store backend)", () => {
|
|
249
24
|
let broker: CredentialBroker;
|
|
250
25
|
|
|
@@ -267,51 +42,12 @@ describe("CredentialBroker (encrypted store backend)", () => {
|
|
|
267
42
|
});
|
|
268
43
|
|
|
269
44
|
describe("transient credentials", () => {
|
|
270
|
-
test("consume returns transient value and deletes it", () => {
|
|
271
|
-
// GIVEN a credential with metadata and an injected one-time transient value
|
|
272
|
-
upsertCredentialMetadata("svc", "key", { allowedTools: ["tool1"] });
|
|
273
|
-
broker.injectTransient("svc", "key", "one-time-secret");
|
|
274
|
-
|
|
275
|
-
// WHEN the credential is authorized and consumed
|
|
276
|
-
const auth = broker.authorize({
|
|
277
|
-
service: "svc",
|
|
278
|
-
field: "key",
|
|
279
|
-
toolName: "tool1",
|
|
280
|
-
});
|
|
281
|
-
expect(auth.authorized).toBe(true);
|
|
282
|
-
if (!auth.authorized) {
|
|
283
|
-
return;
|
|
284
|
-
}
|
|
285
|
-
const result = broker.consume(auth.token.tokenId);
|
|
286
|
-
|
|
287
|
-
// THEN the transient value is returned alongside the storage key
|
|
288
|
-
expect(result.success).toBe(true);
|
|
289
|
-
expect(result.value).toBe("one-time-secret");
|
|
290
|
-
expect(result.storageKey).toBe(credentialKey("svc", "key"));
|
|
291
|
-
|
|
292
|
-
// AND a subsequent authorize+consume no longer has the transient value
|
|
293
|
-
const auth2 = broker.authorize({
|
|
294
|
-
service: "svc",
|
|
295
|
-
field: "key",
|
|
296
|
-
toolName: "tool1",
|
|
297
|
-
});
|
|
298
|
-
expect(auth2.authorized).toBe(true);
|
|
299
|
-
if (!auth2.authorized) {
|
|
300
|
-
return;
|
|
301
|
-
}
|
|
302
|
-
const result2 = broker.consume(auth2.token.tokenId);
|
|
303
|
-
expect(result2.success).toBe(true);
|
|
304
|
-
expect(result2.value).toBeUndefined();
|
|
305
|
-
});
|
|
306
|
-
|
|
307
45
|
test("browserFill uses transient value when available", async () => {
|
|
308
|
-
// GIVEN a credential with a transient value and no stored value
|
|
309
46
|
upsertCredentialMetadata("github", "token", {
|
|
310
47
|
allowedTools: ["browser_fill_credential"],
|
|
311
48
|
});
|
|
312
49
|
broker.injectTransient("github", "token", "transient-ghp-123");
|
|
313
50
|
|
|
314
|
-
// WHEN browserFill runs
|
|
315
51
|
let filledValue: string | undefined;
|
|
316
52
|
const result = await broker.browserFill({
|
|
317
53
|
service: "github",
|
|
@@ -322,20 +58,17 @@ describe("CredentialBroker (encrypted store backend)", () => {
|
|
|
322
58
|
},
|
|
323
59
|
});
|
|
324
60
|
|
|
325
|
-
// THEN the transient value is filled
|
|
326
61
|
expect(result.success).toBe(true);
|
|
327
62
|
expect(filledValue).toBe("transient-ghp-123");
|
|
328
63
|
});
|
|
329
64
|
|
|
330
65
|
test("browserFill consumes transient value — second fill falls back to stored", async () => {
|
|
331
|
-
// GIVEN a credential with both a stored value and a transient value
|
|
332
66
|
upsertCredentialMetadata("github", "token", {
|
|
333
67
|
allowedTools: ["browser_fill_credential"],
|
|
334
68
|
});
|
|
335
69
|
await setSecureKeyAsync(credentialKey("github", "token"), "stored-value");
|
|
336
70
|
broker.injectTransient("github", "token", "transient-value");
|
|
337
71
|
|
|
338
|
-
// WHEN browserFill runs twice
|
|
339
72
|
let filled1: string | undefined;
|
|
340
73
|
await broker.browserFill({
|
|
341
74
|
service: "github",
|
|
@@ -355,19 +88,16 @@ describe("CredentialBroker (encrypted store backend)", () => {
|
|
|
355
88
|
},
|
|
356
89
|
});
|
|
357
90
|
|
|
358
|
-
// THEN the first fill uses the transient and the second falls back to stored
|
|
359
91
|
expect(filled1).toBe("transient-value");
|
|
360
92
|
expect(filled2).toBe("stored-value");
|
|
361
93
|
});
|
|
362
94
|
|
|
363
95
|
test("browserFill preserves transient value on fill failure", async () => {
|
|
364
|
-
// GIVEN a credential with a transient value
|
|
365
96
|
upsertCredentialMetadata("github", "token", {
|
|
366
97
|
allowedTools: ["browser_fill_credential"],
|
|
367
98
|
});
|
|
368
99
|
broker.injectTransient("github", "token", "transient-preserved");
|
|
369
100
|
|
|
370
|
-
// WHEN the first fill throws
|
|
371
101
|
const result1 = await broker.browserFill({
|
|
372
102
|
service: "github",
|
|
373
103
|
field: "token",
|
|
@@ -387,19 +117,16 @@ describe("CredentialBroker (encrypted store backend)", () => {
|
|
|
387
117
|
},
|
|
388
118
|
});
|
|
389
119
|
|
|
390
|
-
// THEN the transient value survives for the second fill
|
|
391
120
|
expect(result2.success).toBe(true);
|
|
392
121
|
expect(filled).toBe("transient-preserved");
|
|
393
122
|
});
|
|
394
123
|
|
|
395
124
|
test("serverUse uses transient value when available", async () => {
|
|
396
|
-
// GIVEN a credential with a transient value
|
|
397
125
|
upsertCredentialMetadata("vercel", "api_token", {
|
|
398
126
|
allowedTools: ["deploy"],
|
|
399
127
|
});
|
|
400
128
|
broker.injectTransient("vercel", "api_token", "transient-vercel-tok");
|
|
401
129
|
|
|
402
|
-
// WHEN serverUse runs
|
|
403
130
|
const result = await broker.serverUse({
|
|
404
131
|
service: "vercel",
|
|
405
132
|
field: "api_token",
|
|
@@ -410,19 +137,16 @@ describe("CredentialBroker (encrypted store backend)", () => {
|
|
|
410
137
|
},
|
|
411
138
|
});
|
|
412
139
|
|
|
413
|
-
// THEN the transient value is used for execution
|
|
414
140
|
expect(result.success).toBe(true);
|
|
415
141
|
expect(result.result).toBe("deployed");
|
|
416
142
|
});
|
|
417
143
|
|
|
418
144
|
test("serverUse consumes transient — subsequent call has no value without stored key", async () => {
|
|
419
|
-
// GIVEN a credential with only a transient value (no stored value)
|
|
420
145
|
upsertCredentialMetadata("vercel", "api_token", {
|
|
421
146
|
allowedTools: ["deploy"],
|
|
422
147
|
});
|
|
423
148
|
broker.injectTransient("vercel", "api_token", "transient-only");
|
|
424
149
|
|
|
425
|
-
// WHEN serverUse runs twice
|
|
426
150
|
await broker.serverUse({
|
|
427
151
|
service: "vercel",
|
|
428
152
|
field: "api_token",
|
|
@@ -438,202 +162,102 @@ describe("CredentialBroker (encrypted store backend)", () => {
|
|
|
438
162
|
},
|
|
439
163
|
});
|
|
440
164
|
|
|
441
|
-
// THEN the second call fails because no stored value remains
|
|
442
165
|
expect(result.success).toBe(false);
|
|
443
166
|
expect(result.reason).toContain("no stored value");
|
|
444
167
|
});
|
|
445
168
|
|
|
446
|
-
test("injectTransient replaces previous transient for same key", () => {
|
|
447
|
-
// GIVEN two transient values injected for the same credential
|
|
169
|
+
test("injectTransient replaces previous transient for same key", async () => {
|
|
448
170
|
upsertCredentialMetadata("svc", "key", { allowedTools: ["t"] });
|
|
449
171
|
broker.injectTransient("svc", "key", "first");
|
|
450
172
|
broker.injectTransient("svc", "key", "second");
|
|
451
173
|
|
|
452
|
-
|
|
453
|
-
const auth = broker.authorize({
|
|
174
|
+
const result = await broker.serverUse({
|
|
454
175
|
service: "svc",
|
|
455
176
|
field: "key",
|
|
456
177
|
toolName: "t",
|
|
178
|
+
execute: async (v) => v,
|
|
457
179
|
});
|
|
458
|
-
if (!auth.authorized) {
|
|
459
|
-
return;
|
|
460
|
-
}
|
|
461
|
-
const result = broker.consume(auth.token.tokenId);
|
|
462
180
|
|
|
463
|
-
|
|
464
|
-
expect(result.
|
|
181
|
+
expect(result.success).toBe(true);
|
|
182
|
+
expect(result.result).toBe("second");
|
|
465
183
|
});
|
|
466
184
|
|
|
467
|
-
test("transient value for one credential does not affect another", () => {
|
|
468
|
-
// GIVEN a transient value injected for svcA only
|
|
185
|
+
test("transient value for one credential does not affect another", async () => {
|
|
469
186
|
upsertCredentialMetadata("svcA", "key", { allowedTools: ["t"] });
|
|
470
187
|
upsertCredentialMetadata("svcB", "key", { allowedTools: ["t"] });
|
|
188
|
+
await setSecureKeyAsync(credentialKey("svcB", "key"), "stored-b");
|
|
471
189
|
broker.injectTransient("svcA", "key", "val-a");
|
|
472
190
|
|
|
473
|
-
|
|
474
|
-
const authB = broker.authorize({
|
|
191
|
+
const resultB = await broker.serverUse({
|
|
475
192
|
service: "svcB",
|
|
476
193
|
field: "key",
|
|
477
194
|
toolName: "t",
|
|
195
|
+
execute: async (v) => v,
|
|
478
196
|
});
|
|
479
|
-
|
|
480
|
-
return;
|
|
481
|
-
}
|
|
482
|
-
const resultB = broker.consume(authB.token.tokenId);
|
|
483
|
-
const authA = broker.authorize({
|
|
197
|
+
const resultA = await broker.serverUse({
|
|
484
198
|
service: "svcA",
|
|
485
199
|
field: "key",
|
|
486
200
|
toolName: "t",
|
|
201
|
+
execute: async (v) => v,
|
|
487
202
|
});
|
|
488
|
-
if (!authA.authorized) {
|
|
489
|
-
return;
|
|
490
|
-
}
|
|
491
|
-
const resultA = broker.consume(authA.token.tokenId);
|
|
492
203
|
|
|
493
|
-
// THEN only svcA carries the transient value
|
|
494
204
|
expect(resultB.success).toBe(true);
|
|
495
|
-
expect(resultB.
|
|
496
|
-
expect(resultA.
|
|
497
|
-
|
|
498
|
-
});
|
|
499
|
-
|
|
500
|
-
describe("serverUseById edge cases", () => {
|
|
501
|
-
test("serverUseById with multiple injection templates returns all", async () => {
|
|
502
|
-
// GIVEN a credential with two injection templates and a stored value
|
|
503
|
-
const meta = upsertCredentialMetadata("multi", "api_key", {
|
|
504
|
-
allowedTools: ["proxy"],
|
|
505
|
-
injectionTemplates: [
|
|
506
|
-
{
|
|
507
|
-
hostPattern: "*.fal.ai",
|
|
508
|
-
injectionType: "header",
|
|
509
|
-
headerName: "Authorization",
|
|
510
|
-
valuePrefix: "Key ",
|
|
511
|
-
},
|
|
512
|
-
{
|
|
513
|
-
hostPattern: "gateway.fal.ai",
|
|
514
|
-
injectionType: "header",
|
|
515
|
-
headerName: "X-Fal-Key",
|
|
516
|
-
},
|
|
517
|
-
],
|
|
518
|
-
});
|
|
519
|
-
await setSecureKeyAsync(
|
|
520
|
-
credentialKey("multi", "api_key"),
|
|
521
|
-
"multi-secret",
|
|
522
|
-
);
|
|
523
|
-
|
|
524
|
-
// WHEN serverUseById resolves the credential by id
|
|
525
|
-
const result = await broker.serverUseById({
|
|
526
|
-
credentialId: meta.credentialId,
|
|
527
|
-
requestingTool: "proxy",
|
|
528
|
-
});
|
|
529
|
-
|
|
530
|
-
// THEN both injection templates are returned without leaking the secret
|
|
531
|
-
expect(result.success).toBe(true);
|
|
532
|
-
if (!result.success) {
|
|
533
|
-
return;
|
|
534
|
-
}
|
|
535
|
-
expect(result.injectionTemplates).toHaveLength(2);
|
|
536
|
-
expect(result.injectionTemplates[0].hostPattern).toBe("*.fal.ai");
|
|
537
|
-
expect(result.injectionTemplates[1].hostPattern).toBe("gateway.fal.ai");
|
|
538
|
-
expect(JSON.stringify(result)).not.toContain("multi-secret");
|
|
539
|
-
});
|
|
540
|
-
});
|
|
541
|
-
|
|
542
|
-
describe("revokeAll", () => {
|
|
543
|
-
test("revokeAll clears all tokens and subsequent consume fails", () => {
|
|
544
|
-
// GIVEN two active tokens for a credential
|
|
545
|
-
upsertCredentialMetadata("svc", "key", { allowedTools: ["t1", "t2"] });
|
|
546
|
-
const a1 = broker.authorize({
|
|
547
|
-
service: "svc",
|
|
548
|
-
field: "key",
|
|
549
|
-
toolName: "t1",
|
|
550
|
-
});
|
|
551
|
-
const a2 = broker.authorize({
|
|
552
|
-
service: "svc",
|
|
553
|
-
field: "key",
|
|
554
|
-
toolName: "t2",
|
|
555
|
-
});
|
|
556
|
-
expect(broker.activeTokenCount).toBe(2);
|
|
557
|
-
|
|
558
|
-
// WHEN revokeAll is called
|
|
559
|
-
broker.revokeAll();
|
|
560
|
-
|
|
561
|
-
// THEN the token count is zero and consuming either token fails
|
|
562
|
-
expect(broker.activeTokenCount).toBe(0);
|
|
563
|
-
if (a1.authorized) {
|
|
564
|
-
expect(broker.consume(a1.token.tokenId).success).toBe(false);
|
|
565
|
-
}
|
|
566
|
-
if (a2.authorized) {
|
|
567
|
-
expect(broker.consume(a2.token.tokenId).success).toBe(false);
|
|
568
|
-
}
|
|
569
|
-
});
|
|
570
|
-
|
|
571
|
-
test("revokeAll on empty broker is a no-op", () => {
|
|
572
|
-
// GIVEN a broker with no active tokens
|
|
573
|
-
expect(broker.activeTokenCount).toBe(0);
|
|
574
|
-
|
|
575
|
-
// WHEN revokeAll is called
|
|
576
|
-
broker.revokeAll();
|
|
577
|
-
|
|
578
|
-
// THEN the token count remains zero
|
|
579
|
-
expect(broker.activeTokenCount).toBe(0);
|
|
205
|
+
expect(resultB.result).toBe("stored-b");
|
|
206
|
+
expect(resultA.success).toBe(true);
|
|
207
|
+
expect(resultA.result).toBe("val-a");
|
|
580
208
|
});
|
|
581
209
|
});
|
|
582
210
|
|
|
583
211
|
describe("canonical capability key", () => {
|
|
584
|
-
test("
|
|
585
|
-
// GIVEN metadata allowing the canonical browser-fill capability key
|
|
212
|
+
test("browserFill succeeds with canonical key when metadata has canonical key", async () => {
|
|
586
213
|
upsertCredentialMetadata("github", "token", {
|
|
587
214
|
allowedTools: [BROWSER_FILL_CAPABILITY],
|
|
588
215
|
});
|
|
216
|
+
await setSecureKeyAsync(credentialKey("github", "token"), "ghp_secret");
|
|
589
217
|
|
|
590
|
-
|
|
591
|
-
const result = broker.authorize({
|
|
218
|
+
const result = await broker.browserFill({
|
|
592
219
|
service: "github",
|
|
593
220
|
field: "token",
|
|
594
221
|
toolName: BROWSER_FILL_CAPABILITY,
|
|
222
|
+
fill: async () => {},
|
|
595
223
|
});
|
|
596
224
|
|
|
597
|
-
|
|
598
|
-
expect(result.authorized).toBe(true);
|
|
225
|
+
expect(result.success).toBe(true);
|
|
599
226
|
});
|
|
600
227
|
|
|
601
|
-
test("
|
|
602
|
-
// GIVEN metadata stored under the legacy browser_fill_credential alias
|
|
228
|
+
test("browserFill succeeds with canonical key when metadata has legacy alias", async () => {
|
|
603
229
|
upsertCredentialMetadata("github", "token", {
|
|
604
230
|
allowedTools: ["browser_fill_credential"],
|
|
605
231
|
});
|
|
232
|
+
await setSecureKeyAsync(credentialKey("github", "token"), "ghp_secret");
|
|
606
233
|
|
|
607
|
-
|
|
608
|
-
const result = broker.authorize({
|
|
234
|
+
const result = await broker.browserFill({
|
|
609
235
|
service: "github",
|
|
610
236
|
field: "token",
|
|
611
237
|
toolName: BROWSER_FILL_CAPABILITY,
|
|
238
|
+
fill: async () => {},
|
|
612
239
|
});
|
|
613
240
|
|
|
614
|
-
|
|
615
|
-
expect(result.authorized).toBe(true);
|
|
241
|
+
expect(result.success).toBe(true);
|
|
616
242
|
});
|
|
617
243
|
|
|
618
|
-
test("
|
|
619
|
-
// GIVEN metadata allowing the canonical capability key
|
|
244
|
+
test("browserFill succeeds with legacy alias when metadata has canonical key", async () => {
|
|
620
245
|
upsertCredentialMetadata("github", "token", {
|
|
621
246
|
allowedTools: [BROWSER_FILL_CAPABILITY],
|
|
622
247
|
});
|
|
248
|
+
await setSecureKeyAsync(credentialKey("github", "token"), "ghp_secret");
|
|
623
249
|
|
|
624
|
-
|
|
625
|
-
const result = broker.authorize({
|
|
250
|
+
const result = await broker.browserFill({
|
|
626
251
|
service: "github",
|
|
627
252
|
field: "token",
|
|
628
253
|
toolName: "browser_fill_credential",
|
|
254
|
+
fill: async () => {},
|
|
629
255
|
});
|
|
630
256
|
|
|
631
|
-
|
|
632
|
-
expect(result.authorized).toBe(true);
|
|
257
|
+
expect(result.success).toBe(true);
|
|
633
258
|
});
|
|
634
259
|
|
|
635
260
|
test("serverUse with canonical key works when metadata has legacy alias", async () => {
|
|
636
|
-
// GIVEN metadata stored under the legacy alias with a stored value
|
|
637
261
|
upsertCredentialMetadata("vercel", "api_token", {
|
|
638
262
|
allowedTools: ["browser_fill_credential"],
|
|
639
263
|
});
|
|
@@ -642,7 +266,6 @@ describe("CredentialBroker (encrypted store backend)", () => {
|
|
|
642
266
|
"vercel-tok",
|
|
643
267
|
);
|
|
644
268
|
|
|
645
|
-
// WHEN serverUse runs with the canonical key
|
|
646
269
|
const result = await broker.serverUse({
|
|
647
270
|
service: "vercel",
|
|
648
271
|
field: "api_token",
|
|
@@ -650,43 +273,43 @@ describe("CredentialBroker (encrypted store backend)", () => {
|
|
|
650
273
|
execute: async (v) => v,
|
|
651
274
|
});
|
|
652
275
|
|
|
653
|
-
// THEN execution succeeds with the stored value
|
|
654
276
|
expect(result.success).toBe(true);
|
|
655
277
|
expect(result.result).toBe("vercel-tok");
|
|
656
278
|
});
|
|
657
279
|
|
|
658
|
-
test("non-aliased tool names are unaffected by alias resolution", () => {
|
|
659
|
-
// GIVEN metadata allowing a non-aliased custom tool
|
|
280
|
+
test("non-aliased tool names are unaffected by alias resolution", async () => {
|
|
660
281
|
upsertCredentialMetadata("svc", "key", {
|
|
661
282
|
allowedTools: ["custom_tool"],
|
|
662
283
|
});
|
|
284
|
+
await setSecureKeyAsync(credentialKey("svc", "key"), "secret");
|
|
663
285
|
|
|
664
|
-
|
|
665
|
-
const result = broker.authorize({
|
|
286
|
+
const result = await broker.serverUse({
|
|
666
287
|
service: "svc",
|
|
667
288
|
field: "key",
|
|
668
289
|
toolName: "custom_tool",
|
|
290
|
+
execute: async (v) => v,
|
|
669
291
|
});
|
|
670
292
|
|
|
671
|
-
|
|
672
|
-
expect(result.
|
|
293
|
+
expect(result.success).toBe(true);
|
|
294
|
+
expect(result.result).toBe("secret");
|
|
673
295
|
});
|
|
674
296
|
|
|
675
|
-
test("non-aliased tool denied when only canonical key is allowed", () => {
|
|
676
|
-
// GIVEN metadata allowing only the canonical capability key
|
|
297
|
+
test("non-aliased tool denied when only canonical key is allowed", async () => {
|
|
677
298
|
upsertCredentialMetadata("svc", "key", {
|
|
678
299
|
allowedTools: [BROWSER_FILL_CAPABILITY],
|
|
679
300
|
});
|
|
301
|
+
await setSecureKeyAsync(credentialKey("svc", "key"), "secret");
|
|
680
302
|
|
|
681
|
-
|
|
682
|
-
const result = broker.authorize({
|
|
303
|
+
const result = await broker.serverUse({
|
|
683
304
|
service: "svc",
|
|
684
305
|
field: "key",
|
|
685
306
|
toolName: "unrelated_tool",
|
|
307
|
+
execute: async () => {
|
|
308
|
+
throw new Error("should not be called");
|
|
309
|
+
},
|
|
686
310
|
});
|
|
687
311
|
|
|
688
|
-
|
|
689
|
-
expect(result.authorized).toBe(false);
|
|
312
|
+
expect(result.success).toBe(false);
|
|
690
313
|
});
|
|
691
314
|
});
|
|
692
315
|
});
|