@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
|
@@ -11,6 +11,7 @@ let promptDecision: "allow" | "deny" = "allow";
|
|
|
11
11
|
let fakeToolResult: ToolExecutionResult = { content: "ok", isError: false };
|
|
12
12
|
let toolThrow: Error | null = null;
|
|
13
13
|
let grantConsumeOk = false;
|
|
14
|
+
let abortDuringCheck: AbortController | null = null;
|
|
14
15
|
|
|
15
16
|
// ── audit-terminal captures ───────────────────────────────
|
|
16
17
|
// The executor and its permission/approval collaborators no longer emit
|
|
@@ -31,7 +32,6 @@ interface ExecutedCapture {
|
|
|
31
32
|
matchedTrustRuleId?: string;
|
|
32
33
|
durationMs: number;
|
|
33
34
|
attribution: UsageAttributionSnapshot | null;
|
|
34
|
-
wasPrompted: boolean;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
interface ErrorCapture {
|
|
@@ -57,13 +57,28 @@ interface DeniedCapture {
|
|
|
57
57
|
riskLevel: string;
|
|
58
58
|
matchedTrustRuleId?: string;
|
|
59
59
|
durationMs: number;
|
|
60
|
-
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
interface PromptTelemetryCapture {
|
|
63
|
+
toolName: string;
|
|
64
|
+
riskLevel: string;
|
|
65
|
+
riskThreshold?: string;
|
|
66
|
+
surface?: string;
|
|
67
|
+
conversationId?: string;
|
|
61
68
|
}
|
|
62
69
|
|
|
63
70
|
const executedCaptures: ExecutedCapture[] = [];
|
|
64
71
|
const errorCaptures: ErrorCapture[] = [];
|
|
65
72
|
const deniedCaptures: DeniedCapture[] = [];
|
|
66
|
-
const promptedCaptures:
|
|
73
|
+
const promptedCaptures: PromptTelemetryCapture[] = [];
|
|
74
|
+
const decidedCaptures: Array<{
|
|
75
|
+
entry: PromptTelemetryCapture;
|
|
76
|
+
outcome: string;
|
|
77
|
+
}> = [];
|
|
78
|
+
const promptedToolNames = (): string[] =>
|
|
79
|
+
promptedCaptures.map((entry) => entry.toolName);
|
|
80
|
+
const decidedOutcomes = (): string[] =>
|
|
81
|
+
decidedCaptures.map((decided) => decided.outcome);
|
|
67
82
|
|
|
68
83
|
mock.module("../telemetry/tool-audit.js", () => ({
|
|
69
84
|
recordToolExecuted: (entry: ExecutedCapture) => {
|
|
@@ -75,8 +90,14 @@ mock.module("../telemetry/tool-audit.js", () => ({
|
|
|
75
90
|
recordToolDenied: (entry: DeniedCapture) => {
|
|
76
91
|
deniedCaptures.push(entry);
|
|
77
92
|
},
|
|
78
|
-
recordToolPermissionPrompted: (
|
|
79
|
-
promptedCaptures.push(
|
|
93
|
+
recordToolPermissionPrompted: (entry: PromptTelemetryCapture) => {
|
|
94
|
+
promptedCaptures.push(entry);
|
|
95
|
+
},
|
|
96
|
+
recordToolPermissionDecided: (
|
|
97
|
+
entry: PromptTelemetryCapture,
|
|
98
|
+
outcome: string,
|
|
99
|
+
) => {
|
|
100
|
+
decidedCaptures.push({ entry, outcome });
|
|
80
101
|
},
|
|
81
102
|
}));
|
|
82
103
|
|
|
@@ -105,7 +126,12 @@ mock.module("../permissions/checker.js", () => ({
|
|
|
105
126
|
}
|
|
106
127
|
return { level: checkerRisk };
|
|
107
128
|
},
|
|
108
|
-
check: async () =>
|
|
129
|
+
check: async () => {
|
|
130
|
+
// Lets a test abort the turn after risk classification (which rethrows an
|
|
131
|
+
// aborted signal) but before the prompt is surfaced.
|
|
132
|
+
abortDuringCheck?.abort();
|
|
133
|
+
return { decision: checkerDecision, reason: checkerReason };
|
|
134
|
+
},
|
|
109
135
|
generateScopeOptions: () => [{ label: "/tmp", scope: "/tmp" }],
|
|
110
136
|
}));
|
|
111
137
|
|
|
@@ -267,6 +293,12 @@ function makePrompter(
|
|
|
267
293
|
promptImpl?: () => Promise<{
|
|
268
294
|
decision: "allow" | "deny";
|
|
269
295
|
decisionContext?: string;
|
|
296
|
+
/** Set by the real prompter when nobody answered within the timeout. */
|
|
297
|
+
wasTimeout?: boolean;
|
|
298
|
+
/** Set when a new user message superseded the outstanding prompt. */
|
|
299
|
+
wasSystemCancel?: boolean;
|
|
300
|
+
/** Set when the turn was cancelled while the prompt was outstanding. */
|
|
301
|
+
wasAbort?: boolean;
|
|
270
302
|
}>,
|
|
271
303
|
) {
|
|
272
304
|
return {
|
|
@@ -287,10 +319,12 @@ describe("ToolExecutor audit terminals", () => {
|
|
|
287
319
|
fakeToolResult = { content: "ok", isError: false };
|
|
288
320
|
toolThrow = null;
|
|
289
321
|
grantConsumeOk = false;
|
|
322
|
+
abortDuringCheck = null;
|
|
290
323
|
executedCaptures.length = 0;
|
|
291
324
|
errorCaptures.length = 0;
|
|
292
325
|
deniedCaptures.length = 0;
|
|
293
326
|
promptedCaptures.length = 0;
|
|
327
|
+
decidedCaptures.length = 0;
|
|
294
328
|
});
|
|
295
329
|
|
|
296
330
|
test("records executed terminal for allowed execution", async () => {
|
|
@@ -314,16 +348,15 @@ describe("ToolExecutor audit terminals", () => {
|
|
|
314
348
|
expect(executed.riskLevel).toBe("low");
|
|
315
349
|
expect(executed.resultContent).toBe("ok");
|
|
316
350
|
expect(executed.resultBytes).toBe(Buffer.byteLength("ok", "utf8"));
|
|
317
|
-
expect(executed.wasPrompted).toBe(false);
|
|
318
351
|
expect(executed.durationMs).toBeGreaterThanOrEqual(0);
|
|
319
352
|
|
|
320
353
|
disposeToolProfiler("conversation-1");
|
|
321
354
|
});
|
|
322
355
|
|
|
323
|
-
test("
|
|
356
|
+
test("records permission telemetry per-invocation, not from the turn-level flag", async () => {
|
|
324
357
|
// A prior tool this turn was interactively approved (turn-level
|
|
325
|
-
// `approvedViaPrompt` already true), but THIS call is auto-approved
|
|
326
|
-
//
|
|
358
|
+
// `approvedViaPrompt` already true), but THIS call is auto-approved, so it
|
|
359
|
+
// surfaces no prompt and records neither half of the pair.
|
|
327
360
|
const executor = new ToolExecutor(makePrompter());
|
|
328
361
|
|
|
329
362
|
const result = await executor.execute(
|
|
@@ -334,7 +367,8 @@ describe("ToolExecutor audit terminals", () => {
|
|
|
334
367
|
|
|
335
368
|
expect(result).toMatchObject({ isError: false });
|
|
336
369
|
expect(executedCaptures).toHaveLength(1);
|
|
337
|
-
expect(
|
|
370
|
+
expect(promptedCaptures).toHaveLength(0);
|
|
371
|
+
expect(decidedCaptures).toHaveLength(0);
|
|
338
372
|
});
|
|
339
373
|
|
|
340
374
|
test("records denied terminal when user denies prompt", async () => {
|
|
@@ -355,14 +389,193 @@ describe("ToolExecutor audit terminals", () => {
|
|
|
355
389
|
expect(result.content).toContain("Permission denied");
|
|
356
390
|
|
|
357
391
|
// A prompt was surfaced, then the user's denial recorded a denied terminal.
|
|
358
|
-
expect(
|
|
392
|
+
expect(promptedToolNames()).toEqual(["bash"]);
|
|
393
|
+
expect(decidedOutcomes()).toEqual(["deny"]);
|
|
359
394
|
expect(executedCaptures).toHaveLength(0);
|
|
360
395
|
expect(deniedCaptures).toHaveLength(1);
|
|
361
396
|
const denied = deniedCaptures[0];
|
|
362
397
|
expect(denied.toolName).toBe("bash");
|
|
363
398
|
expect(denied.riskLevel).toBe("medium");
|
|
364
399
|
expect(denied.reason).toBe("Permission denied by user");
|
|
365
|
-
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
test("records an allow decision when the user approves the prompt", async () => {
|
|
403
|
+
checkerDecision = "prompt";
|
|
404
|
+
checkerReason = "medium risk: requires approval";
|
|
405
|
+
checkerRisk = "medium";
|
|
406
|
+
promptDecision = "allow";
|
|
407
|
+
|
|
408
|
+
const executor = new ToolExecutor(makePrompter());
|
|
409
|
+
|
|
410
|
+
await executor.execute(
|
|
411
|
+
"bash",
|
|
412
|
+
{ command: "ls -la" },
|
|
413
|
+
makeContext({ forcePromptSideEffects: true }),
|
|
414
|
+
);
|
|
415
|
+
|
|
416
|
+
expect(promptedToolNames()).toEqual(["bash"]);
|
|
417
|
+
expect(decidedOutcomes()).toEqual(["allow"]);
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
test("carries risk level and access preset onto both halves of the pair", async () => {
|
|
421
|
+
checkerDecision = "prompt";
|
|
422
|
+
checkerReason = "high risk: requires approval";
|
|
423
|
+
checkerRisk = "high";
|
|
424
|
+
promptDecision = "deny";
|
|
425
|
+
|
|
426
|
+
const executor = new ToolExecutor(makePrompter());
|
|
427
|
+
|
|
428
|
+
await executor.execute(
|
|
429
|
+
"bash",
|
|
430
|
+
{ command: "rm -rf /tmp/x" },
|
|
431
|
+
makeContext({ forcePromptSideEffects: true, executionChannel: "slack" }),
|
|
432
|
+
);
|
|
433
|
+
|
|
434
|
+
const expected = {
|
|
435
|
+
toolName: "bash",
|
|
436
|
+
riskLevel: "high",
|
|
437
|
+
// The gateway is unreachable under test, so the reader falls back to the
|
|
438
|
+
// strict preset. What matters is that the resolved value is recorded.
|
|
439
|
+
riskThreshold: "none",
|
|
440
|
+
surface: "slack",
|
|
441
|
+
conversationId: "conversation-1",
|
|
442
|
+
};
|
|
443
|
+
expect(promptedCaptures[0]).toMatchObject(expected);
|
|
444
|
+
expect(decidedCaptures[0].entry).toMatchObject(expected);
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
// ── abandoned prompts ───────────────────────────────
|
|
448
|
+
// Every one of these resolves to a deny for the agent loop, so the only
|
|
449
|
+
// thing separating them from a real user denial is the decision outcome.
|
|
450
|
+
test.each([
|
|
451
|
+
["timed out", { decision: "deny" as const, wasTimeout: true }],
|
|
452
|
+
[
|
|
453
|
+
"superseded by a new user message",
|
|
454
|
+
{ decision: "deny" as const, wasSystemCancel: true },
|
|
455
|
+
],
|
|
456
|
+
["cancelled with the turn", { decision: "deny" as const, wasAbort: true }],
|
|
457
|
+
])(
|
|
458
|
+
"records an abandoned decision when the prompt %s",
|
|
459
|
+
async (_label, response) => {
|
|
460
|
+
checkerDecision = "prompt";
|
|
461
|
+
checkerReason = "medium risk: requires approval";
|
|
462
|
+
checkerRisk = "medium";
|
|
463
|
+
|
|
464
|
+
const executor = new ToolExecutor(makePrompter(async () => response));
|
|
465
|
+
|
|
466
|
+
const result = await executor.execute(
|
|
467
|
+
"bash",
|
|
468
|
+
{ command: "ls -la" },
|
|
469
|
+
makeContext({ forcePromptSideEffects: true }),
|
|
470
|
+
);
|
|
471
|
+
|
|
472
|
+
expect(result.isError).toBe(true);
|
|
473
|
+
expect(promptedToolNames()).toEqual(["bash"]);
|
|
474
|
+
expect(decidedOutcomes()).toEqual(["abandoned"]);
|
|
475
|
+
},
|
|
476
|
+
);
|
|
477
|
+
|
|
478
|
+
test("records nothing when the turn aborts before the prompt is surfaced", async () => {
|
|
479
|
+
checkerDecision = "prompt";
|
|
480
|
+
checkerReason = "medium risk: requires approval";
|
|
481
|
+
checkerRisk = "medium";
|
|
482
|
+
|
|
483
|
+
// The real prompter returns immediately on an already-aborted signal
|
|
484
|
+
// without registering or sending a confirmation request, so the user never
|
|
485
|
+
// sees a prompt and neither half of the pair may be recorded.
|
|
486
|
+
const controller = new AbortController();
|
|
487
|
+
abortDuringCheck = controller;
|
|
488
|
+
const executor = new ToolExecutor(
|
|
489
|
+
makePrompter(async () => ({ decision: "deny", wasAbort: true })),
|
|
490
|
+
);
|
|
491
|
+
|
|
492
|
+
const result = await executor.execute(
|
|
493
|
+
"bash",
|
|
494
|
+
{ command: "ls -la" },
|
|
495
|
+
makeContext({ forcePromptSideEffects: true, signal: controller.signal }),
|
|
496
|
+
);
|
|
497
|
+
|
|
498
|
+
expect(result.isError).toBe(true);
|
|
499
|
+
// The invocation did reach the prompt branch and was denied there, so the
|
|
500
|
+
// empty telemetry below is the guard working, not the branch being skipped.
|
|
501
|
+
expect(deniedCaptures).toHaveLength(1);
|
|
502
|
+
expect(promptedCaptures).toHaveLength(0);
|
|
503
|
+
expect(decidedCaptures).toHaveLength(0);
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
test("records an abandoned decision when the prompter is disposed mid-prompt", async () => {
|
|
507
|
+
checkerDecision = "prompt";
|
|
508
|
+
checkerReason = "medium risk: requires approval";
|
|
509
|
+
checkerRisk = "medium";
|
|
510
|
+
|
|
511
|
+
const executor = new ToolExecutor(
|
|
512
|
+
makePrompter(async () => {
|
|
513
|
+
throw new Error("Prompter disposed");
|
|
514
|
+
}),
|
|
515
|
+
);
|
|
516
|
+
|
|
517
|
+
const result = await executor.execute(
|
|
518
|
+
"bash",
|
|
519
|
+
{ command: "ls -la" },
|
|
520
|
+
makeContext({ forcePromptSideEffects: true }),
|
|
521
|
+
);
|
|
522
|
+
|
|
523
|
+
expect(result.isError).toBe(true);
|
|
524
|
+
expect(promptedToolNames()).toEqual(["bash"]);
|
|
525
|
+
expect(decidedOutcomes()).toEqual(["abandoned"]);
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
test("every prompt reconciles against exactly one decision", async () => {
|
|
529
|
+
checkerDecision = "prompt";
|
|
530
|
+
checkerReason = "medium risk: requires approval";
|
|
531
|
+
checkerRisk = "medium";
|
|
532
|
+
|
|
533
|
+
// One of each terminal shape a prompted invocation can reach: approved and
|
|
534
|
+
// the tool runs, approved and the tool throws, denied, and abandoned.
|
|
535
|
+
const runs: Array<() => Promise<unknown>> = [
|
|
536
|
+
async () => {
|
|
537
|
+
promptDecision = "allow";
|
|
538
|
+
toolThrow = null;
|
|
539
|
+
return new ToolExecutor(makePrompter()).execute(
|
|
540
|
+
"bash",
|
|
541
|
+
{ command: "ls" },
|
|
542
|
+
makeContext({ forcePromptSideEffects: true }),
|
|
543
|
+
);
|
|
544
|
+
},
|
|
545
|
+
async () => {
|
|
546
|
+
promptDecision = "allow";
|
|
547
|
+
toolThrow = new Error("boom");
|
|
548
|
+
return new ToolExecutor(makePrompter()).execute(
|
|
549
|
+
"bash",
|
|
550
|
+
{ command: "ls" },
|
|
551
|
+
makeContext({ forcePromptSideEffects: true }),
|
|
552
|
+
);
|
|
553
|
+
},
|
|
554
|
+
async () => {
|
|
555
|
+
promptDecision = "deny";
|
|
556
|
+
toolThrow = null;
|
|
557
|
+
return new ToolExecutor(makePrompter()).execute(
|
|
558
|
+
"bash",
|
|
559
|
+
{ command: "ls" },
|
|
560
|
+
makeContext({ forcePromptSideEffects: true }),
|
|
561
|
+
);
|
|
562
|
+
},
|
|
563
|
+
async () =>
|
|
564
|
+
new ToolExecutor(
|
|
565
|
+
makePrompter(async () => ({ decision: "deny", wasTimeout: true })),
|
|
566
|
+
).execute(
|
|
567
|
+
"bash",
|
|
568
|
+
{ command: "ls" },
|
|
569
|
+
makeContext({ forcePromptSideEffects: true }),
|
|
570
|
+
),
|
|
571
|
+
];
|
|
572
|
+
for (const run of runs) {
|
|
573
|
+
await run();
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
expect(promptedCaptures).toHaveLength(runs.length);
|
|
577
|
+
expect(decidedCaptures).toHaveLength(runs.length);
|
|
578
|
+
expect(decidedOutcomes()).toEqual(["allow", "allow", "deny", "abandoned"]);
|
|
366
579
|
});
|
|
367
580
|
|
|
368
581
|
test("uses contextual deny messaging when provided by prompter", async () => {
|
|
@@ -392,7 +605,6 @@ describe("ToolExecutor audit terminals", () => {
|
|
|
392
605
|
expect(deniedCaptures[0].reason).toBe(
|
|
393
606
|
"Permission denied (bash): contextual policy",
|
|
394
607
|
);
|
|
395
|
-
expect(deniedCaptures[0].wasPrompted).toBe(true);
|
|
396
608
|
});
|
|
397
609
|
|
|
398
610
|
// executionTarget is no longer carried on any audit-terminal payload, so
|
|
@@ -435,7 +647,6 @@ describe("ToolExecutor audit terminals", () => {
|
|
|
435
647
|
expect(promptedCaptures).toHaveLength(0);
|
|
436
648
|
expect(deniedCaptures).toHaveLength(1);
|
|
437
649
|
expect(deniedCaptures[0].reason).toBe("Blocked by deny rule: rm *");
|
|
438
|
-
expect(deniedCaptures[0].wasPrompted).toBe(false);
|
|
439
650
|
});
|
|
440
651
|
|
|
441
652
|
test("records error terminal when tool execution throws", async () => {
|
|
@@ -766,7 +977,7 @@ describe("ToolExecutor audit terminals", () => {
|
|
|
766
977
|
);
|
|
767
978
|
|
|
768
979
|
// forcePromptSideEffects promotes the auto-allow to an interactive prompt.
|
|
769
|
-
expect(
|
|
980
|
+
expect(promptedToolNames()).toEqual(["bash"]);
|
|
770
981
|
expect(executedCaptures).toHaveLength(1);
|
|
771
982
|
expect(executedCaptures[0].toolName).toBe("bash");
|
|
772
983
|
});
|
|
@@ -811,7 +1022,7 @@ describe("ToolExecutor audit terminals", () => {
|
|
|
811
1022
|
);
|
|
812
1023
|
|
|
813
1024
|
expect(result).toMatchObject({ content: "ok", isError: false });
|
|
814
|
-
expect(
|
|
1025
|
+
expect(promptedToolNames()).toEqual(["file_edit"]);
|
|
815
1026
|
expect(executedCaptures).toHaveLength(1);
|
|
816
1027
|
expect(executedCaptures[0].toolName).toBe("file_edit");
|
|
817
1028
|
});
|
|
@@ -836,7 +1047,6 @@ describe("ToolExecutor audit terminals", () => {
|
|
|
836
1047
|
expect(result.isError).toBe(true);
|
|
837
1048
|
expect(deniedCaptures).toHaveLength(1);
|
|
838
1049
|
expect(deniedCaptures[0].riskLevel).toBe("high");
|
|
839
|
-
expect(deniedCaptures[0].wasPrompted).toBe(false);
|
|
840
1050
|
});
|
|
841
1051
|
|
|
842
1052
|
test("a gate-errored call records the classified risk", async () => {
|
|
@@ -159,7 +159,7 @@ describe("ui_show missing-content teaching", () => {
|
|
|
159
159
|
{
|
|
160
160
|
surfaceType: "channel_setup",
|
|
161
161
|
data: { channel: "email" },
|
|
162
|
-
expectInError: '"slack", "telegram", "phone"',
|
|
162
|
+
expectInError: '"slack", "telegram", "discord", "phone"',
|
|
163
163
|
},
|
|
164
164
|
{
|
|
165
165
|
// Snake_case constraint keys are stripped by the schema, so without the
|
|
@@ -42,13 +42,16 @@ const auditCalls = {
|
|
|
42
42
|
denied: [] as any[],
|
|
43
43
|
error: [] as any[],
|
|
44
44
|
executed: [] as any[],
|
|
45
|
-
prompted: [] as
|
|
45
|
+
prompted: [] as any[],
|
|
46
|
+
decided: [] as any[],
|
|
46
47
|
};
|
|
47
48
|
mock.module("../telemetry/tool-audit.js", () => ({
|
|
48
49
|
recordToolDenied: (e: any) => auditCalls.denied.push(e),
|
|
49
50
|
recordToolError: (e: any) => auditCalls.error.push(e),
|
|
50
51
|
recordToolExecuted: (e: any) => auditCalls.executed.push(e),
|
|
51
|
-
recordToolPermissionPrompted: (
|
|
52
|
+
recordToolPermissionPrompted: (e: any) => auditCalls.prompted.push(e),
|
|
53
|
+
recordToolPermissionDecided: (e: any, outcome: string) =>
|
|
54
|
+
auditCalls.decided.push({ entry: e, outcome }),
|
|
52
55
|
}));
|
|
53
56
|
|
|
54
57
|
function resetAuditCalls(): void {
|
|
@@ -56,6 +59,7 @@ function resetAuditCalls(): void {
|
|
|
56
59
|
auditCalls.error.length = 0;
|
|
57
60
|
auditCalls.executed.length = 0;
|
|
58
61
|
auditCalls.prompted.length = 0;
|
|
62
|
+
auditCalls.decided.length = 0;
|
|
59
63
|
}
|
|
60
64
|
|
|
61
65
|
mock.module("../tools/registry.js", () => ({
|
|
@@ -561,6 +561,49 @@ describe("voice_config_update — stt_provider", () => {
|
|
|
561
561
|
expect((readConfig().services as any)?.stt?.provider).toBeUndefined();
|
|
562
562
|
});
|
|
563
563
|
|
|
564
|
+
test("stt_model persists under the active provider", async () => {
|
|
565
|
+
// Flux stays reachable from this surface after ceasing to be a provider
|
|
566
|
+
// id: it is a model family on whichever provider is active.
|
|
567
|
+
writeConfig({ services: { stt: { provider: "deepgram" } } });
|
|
568
|
+
invalidateConfigCache();
|
|
569
|
+
|
|
570
|
+
const result = await run(
|
|
571
|
+
{ setting: "stt_model", value: "flux" },
|
|
572
|
+
makeContext(),
|
|
573
|
+
);
|
|
574
|
+
|
|
575
|
+
expect(result.isError).toBe(false);
|
|
576
|
+
expect(
|
|
577
|
+
(readConfig().services as any)?.stt?.providers?.deepgram?.model,
|
|
578
|
+
).toBe("flux");
|
|
579
|
+
});
|
|
580
|
+
|
|
581
|
+
test("stt_model rejects a family the active provider cannot serve", async () => {
|
|
582
|
+
writeConfig({ services: { stt: { provider: "openai-whisper" } } });
|
|
583
|
+
invalidateConfigCache();
|
|
584
|
+
|
|
585
|
+
const result = await run(
|
|
586
|
+
{ setting: "stt_model", value: "flux" },
|
|
587
|
+
makeContext(),
|
|
588
|
+
);
|
|
589
|
+
|
|
590
|
+
expect(result.isError).toBe(true);
|
|
591
|
+
expect(result.content).toContain("single model");
|
|
592
|
+
});
|
|
593
|
+
|
|
594
|
+
test("stt_model rejects an unknown family", async () => {
|
|
595
|
+
writeConfig({ services: { stt: { provider: "deepgram" } } });
|
|
596
|
+
invalidateConfigCache();
|
|
597
|
+
|
|
598
|
+
const result = await run(
|
|
599
|
+
{ setting: "stt_model", value: "flx" },
|
|
600
|
+
makeContext(),
|
|
601
|
+
);
|
|
602
|
+
|
|
603
|
+
expect(result.isError).toBe(true);
|
|
604
|
+
expect(result.content).toContain("stt_model must be one of");
|
|
605
|
+
});
|
|
606
|
+
|
|
564
607
|
test("switching away from vellum takes effect", async () => {
|
|
565
608
|
writeConfig({ services: { stt: { provider: "vellum" } } });
|
|
566
609
|
invalidateConfigCache();
|