@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
|
@@ -194,3 +194,64 @@ describe("compactor records llm_request_logs with call_site=compactionAgent", ()
|
|
|
194
194
|
expect(recordRequestLogCalls[0]!.callSite).toBe("compactionAgent");
|
|
195
195
|
});
|
|
196
196
|
});
|
|
197
|
+
|
|
198
|
+
describe("compaction result reports what actually served the summary", () => {
|
|
199
|
+
beforeEach(() => {
|
|
200
|
+
recordRequestLogCalls.length = 0;
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
test("rerouted summary carries the backup provider and profile", async () => {
|
|
204
|
+
// `RetryProvider` escalated the summary call to a backup profile on
|
|
205
|
+
// another provider and stamped the response, so the result must report
|
|
206
|
+
// the backup even though the compactor resolved `primaryProfile`.
|
|
207
|
+
const provider: Provider = {
|
|
208
|
+
name: "openai",
|
|
209
|
+
sendMessage: async () => ({
|
|
210
|
+
content: [{ type: "text", text: compactionResponse }],
|
|
211
|
+
model: "claude-sonnet-5",
|
|
212
|
+
actualProvider: "anthropic",
|
|
213
|
+
actualInferenceProfile: "backupProfile",
|
|
214
|
+
usage: { inputTokens: 100, outputTokens: 50 },
|
|
215
|
+
stopReason: "end_turn",
|
|
216
|
+
rawRequest: RAW_REQUEST,
|
|
217
|
+
rawResponse: RAW_RESPONSE,
|
|
218
|
+
}),
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
const result = await runAssistantDrivenCompaction({
|
|
222
|
+
...args(provider),
|
|
223
|
+
overrideProfile: "primaryProfile",
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
expect(result.summaryModel).toBe("claude-sonnet-5");
|
|
227
|
+
expect(result.summaryActualProvider).toBe("anthropic");
|
|
228
|
+
expect(result.summaryActualInferenceProfile).toBe("backupProfile");
|
|
229
|
+
// The primary's resolution is still reported, just outranked downstream.
|
|
230
|
+
expect(result.summaryOverrideProfile).toBe("primaryProfile");
|
|
231
|
+
// Same value the request log recorded: one source of truth, not two.
|
|
232
|
+
expect(recordRequestLogCalls[0]!.provider).toBe("anthropic");
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
test("non-rerouted summary carries the configured provider and no profile", async () => {
|
|
236
|
+
const provider: Provider = {
|
|
237
|
+
name: "openai",
|
|
238
|
+
sendMessage: async () => ({
|
|
239
|
+
content: [{ type: "text", text: compactionResponse }],
|
|
240
|
+
model: "mock-model",
|
|
241
|
+
usage: { inputTokens: 100, outputTokens: 50 },
|
|
242
|
+
stopReason: "end_turn",
|
|
243
|
+
rawRequest: RAW_REQUEST,
|
|
244
|
+
rawResponse: RAW_RESPONSE,
|
|
245
|
+
}),
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
const result = await runAssistantDrivenCompaction({
|
|
249
|
+
...args(provider),
|
|
250
|
+
overrideProfile: "primaryProfile",
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
expect(result.summaryActualProvider).toBe("openai");
|
|
254
|
+
expect(result.summaryActualInferenceProfile).toBeUndefined();
|
|
255
|
+
expect(result.summaryOverrideProfile).toBe("primaryProfile");
|
|
256
|
+
});
|
|
257
|
+
});
|
|
@@ -1411,6 +1411,71 @@ describe("loadConfig startup behavior", () => {
|
|
|
1411
1411
|
expect(effective.balanced?.provider_connection).toBe("anthropic-personal");
|
|
1412
1412
|
});
|
|
1413
1413
|
|
|
1414
|
+
test("an active managed backup profile survives a reload", () => {
|
|
1415
|
+
// A managed backup resolves from the code catalog without being
|
|
1416
|
+
// materialized in `llm.profiles`, so selecting one persists nothing but
|
|
1417
|
+
// the reference. If the schema did not treat backup keys as
|
|
1418
|
+
// always-available references, the strip-and-reparse in `loadConfig`
|
|
1419
|
+
// would drop `activeProfile` and the seeder would reset the user's
|
|
1420
|
+
// chat-model selection to `balanced` on the next boot.
|
|
1421
|
+
writeConfig({
|
|
1422
|
+
llm: {
|
|
1423
|
+
defaultProvider: { provider: "vellum" },
|
|
1424
|
+
profiles: {},
|
|
1425
|
+
activeProfile: "balanced-backup",
|
|
1426
|
+
},
|
|
1427
|
+
});
|
|
1428
|
+
|
|
1429
|
+
const config = loadConfig();
|
|
1430
|
+
expect(config.llm.activeProfile).toBe("balanced-backup");
|
|
1431
|
+
|
|
1432
|
+
mergeDefaultConfigAndSeedInferenceProfiles();
|
|
1433
|
+
|
|
1434
|
+
const raw = JSON.parse(readFileSync(CONFIG_PATH, "utf-8"));
|
|
1435
|
+
expect(raw.llm.activeProfile).toBe("balanced-backup");
|
|
1436
|
+
expect(raw.llm.profiles["balanced-backup"]).toBeUndefined();
|
|
1437
|
+
});
|
|
1438
|
+
|
|
1439
|
+
test("an active managed backup profile is repaired on a BYOK install", () => {
|
|
1440
|
+
// Backups live on the managed column alone, so under a BYOK default
|
|
1441
|
+
// provider the selection names a profile that resolves to nothing. The
|
|
1442
|
+
// schema rejects the reference on load and the seeder, which resolves
|
|
1443
|
+
// availability through the same provider-aware view, repairs the stored
|
|
1444
|
+
// selection instead of preserving a dead pointer.
|
|
1445
|
+
writeConfig({
|
|
1446
|
+
llm: {
|
|
1447
|
+
defaultProvider: { provider: "anthropic" },
|
|
1448
|
+
profiles: {},
|
|
1449
|
+
activeProfile: "balanced-backup",
|
|
1450
|
+
},
|
|
1451
|
+
});
|
|
1452
|
+
|
|
1453
|
+
const config = loadConfig();
|
|
1454
|
+
expect(config.llm.activeProfile).toBeUndefined();
|
|
1455
|
+
|
|
1456
|
+
mergeDefaultConfigAndSeedInferenceProfiles();
|
|
1457
|
+
|
|
1458
|
+
const raw = JSON.parse(readFileSync(CONFIG_PATH, "utf-8"));
|
|
1459
|
+
expect(raw.llm.activeProfile).toBe("balanced");
|
|
1460
|
+
});
|
|
1461
|
+
|
|
1462
|
+
test("a call site pinned to a managed backup profile survives a reload", () => {
|
|
1463
|
+
writeConfig({
|
|
1464
|
+
llm: {
|
|
1465
|
+
defaultProvider: { provider: "vellum" },
|
|
1466
|
+
profiles: {},
|
|
1467
|
+
activeProfile: "balanced",
|
|
1468
|
+
callSites: { recall: { profile: "cost-optimized-backup" } },
|
|
1469
|
+
},
|
|
1470
|
+
});
|
|
1471
|
+
|
|
1472
|
+
const config = loadConfig();
|
|
1473
|
+
expect(config.llm.callSites.recall?.profile).toBe("cost-optimized-backup");
|
|
1474
|
+
|
|
1475
|
+
const raw = JSON.parse(readFileSync(CONFIG_PATH, "utf-8"));
|
|
1476
|
+
expect(raw.llm.callSites.recall.profile).toBe("cost-optimized-backup");
|
|
1477
|
+
});
|
|
1478
|
+
|
|
1414
1479
|
test("still quarantines corrupt JSON", () => {
|
|
1415
1480
|
// Corrupt-config quarantine is a recovery path: the broken file is
|
|
1416
1481
|
// renamed to `config.json.corrupt-<ts>.json` and the daemon proceeds
|
|
@@ -1800,6 +1865,10 @@ describe("seedInferenceProfiles BYOK-mode default profiles", () => {
|
|
|
1800
1865
|
"quality-optimized",
|
|
1801
1866
|
"cost-optimized",
|
|
1802
1867
|
"latency-optimized",
|
|
1868
|
+
"balanced-backup",
|
|
1869
|
+
"quality-optimized-backup",
|
|
1870
|
+
"cost-optimized-backup",
|
|
1871
|
+
"latency-optimized-backup",
|
|
1803
1872
|
"my-custom",
|
|
1804
1873
|
]);
|
|
1805
1874
|
});
|
|
@@ -959,8 +959,7 @@ describe("AssistantConfigSchema", () => {
|
|
|
959
959
|
},
|
|
960
960
|
},
|
|
961
961
|
flux: {
|
|
962
|
-
turnEnd: { enabled:
|
|
963
|
-
model: "flux-general-en",
|
|
962
|
+
turnEnd: { enabled: true },
|
|
964
963
|
eotThreshold: 0.7,
|
|
965
964
|
eotTimeoutMs: 5000,
|
|
966
965
|
},
|
|
@@ -989,7 +988,7 @@ describe("AssistantConfigSchema", () => {
|
|
|
989
988
|
expect(result.liveVoice.vad.maxTurnDurationMs).toBe(30000);
|
|
990
989
|
expect(result.liveVoice.maxSessionDurationSeconds).toBe(900);
|
|
991
990
|
// A partial liveVoice override leaves Flux turn-end disabled by default.
|
|
992
|
-
expect(result.liveVoice.flux.turnEnd.enabled).toBe(
|
|
991
|
+
expect(result.liveVoice.flux.turnEnd.enabled).toBe(true);
|
|
993
992
|
});
|
|
994
993
|
|
|
995
994
|
test("accepts a liveVoice.vad.bargeInMinSpeechMs of 0 (guard disabled)", () => {
|
|
@@ -23,6 +23,10 @@ import { resetPluginRegistryAndRegisterDefaults } from "../plugins/defaults/inde
|
|
|
23
23
|
import { registerPlugin } from "../plugins/registry.js";
|
|
24
24
|
import type { Message, Provider, ToolDefinition } from "../providers/types.js";
|
|
25
25
|
import { ContextOverflowError } from "../providers/types.js";
|
|
26
|
+
import {
|
|
27
|
+
resolveUsageAttribution,
|
|
28
|
+
type UsageAttributionInput,
|
|
29
|
+
} from "../usage/attribution.js";
|
|
26
30
|
import { getWorkspaceDir } from "../util/platform.js";
|
|
27
31
|
import { setConfig } from "./helpers/set-config.js";
|
|
28
32
|
|
|
@@ -1763,6 +1767,108 @@ describe("session-agent-loop", () => {
|
|
|
1763
1767
|
expect(mainAgentCall?.[3]).toBe("gpt-4.1-2026-03-01");
|
|
1764
1768
|
});
|
|
1765
1769
|
|
|
1770
|
+
test("attributes a fallback serve to the profile that actually served", async () => {
|
|
1771
|
+
// The turn resolved to the primary managed profile, the request hit an
|
|
1772
|
+
// outage-shaped failure, and `RetryProvider` escalated to a backup
|
|
1773
|
+
// profile on a different provider, stamping `actualProvider` and
|
|
1774
|
+
// `actualInferenceProfile` on the response it returns.
|
|
1775
|
+
seedLlmConfig({
|
|
1776
|
+
profiles: {
|
|
1777
|
+
...disabledCatalogDefaultProfiles,
|
|
1778
|
+
backupProfile: { provider: "anthropic", model: "claude-sonnet-5" },
|
|
1779
|
+
},
|
|
1780
|
+
});
|
|
1781
|
+
|
|
1782
|
+
const ctx = makeCtx({
|
|
1783
|
+
providerResponses: [
|
|
1784
|
+
{
|
|
1785
|
+
content: [{ type: "text", text: "Hi there." }],
|
|
1786
|
+
model: "claude-sonnet-5",
|
|
1787
|
+
usage: { inputTokens: 12, outputTokens: 3 },
|
|
1788
|
+
stopReason: "end_turn",
|
|
1789
|
+
actualProvider: "anthropic",
|
|
1790
|
+
actualInferenceProfile: "backupProfile",
|
|
1791
|
+
},
|
|
1792
|
+
],
|
|
1793
|
+
provider: {
|
|
1794
|
+
name: "openai",
|
|
1795
|
+
sendMessage: async () => ({
|
|
1796
|
+
content: [{ type: "text", text: "title" }],
|
|
1797
|
+
model: "mock",
|
|
1798
|
+
usage: { inputTokens: 0, outputTokens: 0 },
|
|
1799
|
+
stopReason: "end_turn",
|
|
1800
|
+
}),
|
|
1801
|
+
} as unknown as Conversation["provider"],
|
|
1802
|
+
});
|
|
1803
|
+
|
|
1804
|
+
await runAgentLoopImpl(ctx, "hello", "msg-1", () => {});
|
|
1805
|
+
|
|
1806
|
+
const mainAgentCall = recordUsageMock.mock.calls.find(
|
|
1807
|
+
(call) => (call as unknown[])[5] === "main_agent",
|
|
1808
|
+
) as unknown[] | undefined;
|
|
1809
|
+
|
|
1810
|
+
expect(mainAgentCall).toBeDefined();
|
|
1811
|
+
// All three attribution facets of the row must describe the same call.
|
|
1812
|
+
expect(mainAgentCall?.[0]).toMatchObject({ providerName: "anthropic" });
|
|
1813
|
+
expect(mainAgentCall?.[3]).toBe("claude-sonnet-5");
|
|
1814
|
+
const attribution = mainAgentCall?.[12] as UsageAttributionInput;
|
|
1815
|
+
expect(attribution.callSite).toBe("mainAgent");
|
|
1816
|
+
expect(attribution.overrideProfile).toBe("backupProfile");
|
|
1817
|
+
expect(attribution.forceOverrideProfile).toBe(true);
|
|
1818
|
+
// `inference_profile` on the persisted row is this snapshot's applied
|
|
1819
|
+
// profile, so resolve it the way `recordUsage` does and assert the
|
|
1820
|
+
// column value itself rather than only the input that feeds it.
|
|
1821
|
+
expect(resolveUsageAttribution(attribution).appliedProfile).toBe(
|
|
1822
|
+
"backupProfile",
|
|
1823
|
+
);
|
|
1824
|
+
});
|
|
1825
|
+
|
|
1826
|
+
test("leaves a non-fallback turn attributed to the conversation's own profile", async () => {
|
|
1827
|
+
// No reroute: the response carries no `actualInferenceProfile`, so the
|
|
1828
|
+
// attribution input stays exactly what the conversation resolved and
|
|
1829
|
+
// carries no forced override.
|
|
1830
|
+
seedLlmConfig({
|
|
1831
|
+
profiles: {
|
|
1832
|
+
...disabledCatalogDefaultProfiles,
|
|
1833
|
+
backupProfile: { provider: "anthropic", model: "claude-sonnet-5" },
|
|
1834
|
+
},
|
|
1835
|
+
});
|
|
1836
|
+
|
|
1837
|
+
const ctx = makeCtx({
|
|
1838
|
+
providerResponses: [
|
|
1839
|
+
{
|
|
1840
|
+
content: [{ type: "text", text: "Hi there." }],
|
|
1841
|
+
model: "gpt-4.1-2026-03-01",
|
|
1842
|
+
usage: { inputTokens: 12, outputTokens: 3 },
|
|
1843
|
+
stopReason: "end_turn",
|
|
1844
|
+
},
|
|
1845
|
+
],
|
|
1846
|
+
provider: {
|
|
1847
|
+
name: "openai",
|
|
1848
|
+
sendMessage: async () => ({
|
|
1849
|
+
content: [{ type: "text", text: "title" }],
|
|
1850
|
+
model: "mock",
|
|
1851
|
+
usage: { inputTokens: 0, outputTokens: 0 },
|
|
1852
|
+
stopReason: "end_turn",
|
|
1853
|
+
}),
|
|
1854
|
+
} as unknown as Conversation["provider"],
|
|
1855
|
+
});
|
|
1856
|
+
|
|
1857
|
+
await runAgentLoopImpl(ctx, "hello", "msg-1", () => {});
|
|
1858
|
+
|
|
1859
|
+
const mainAgentCall = recordUsageMock.mock.calls.find(
|
|
1860
|
+
(call) => (call as unknown[])[5] === "main_agent",
|
|
1861
|
+
) as unknown[] | undefined;
|
|
1862
|
+
|
|
1863
|
+
expect(mainAgentCall).toBeDefined();
|
|
1864
|
+
expect(mainAgentCall?.[0]).toMatchObject({ providerName: "openai" });
|
|
1865
|
+
expect(mainAgentCall?.[3]).toBe("gpt-4.1-2026-03-01");
|
|
1866
|
+
expect(mainAgentCall?.[12]).toEqual({
|
|
1867
|
+
callSite: "mainAgent",
|
|
1868
|
+
overrideProfile: null,
|
|
1869
|
+
});
|
|
1870
|
+
});
|
|
1871
|
+
|
|
1766
1872
|
test("persists the served model onto the assistant row's metadata at finalize", async () => {
|
|
1767
1873
|
const events: AssistantEvent[] = [];
|
|
1768
1874
|
|
|
@@ -4037,6 +4143,138 @@ describe("session-agent-loop", () => {
|
|
|
4037
4143
|
);
|
|
4038
4144
|
});
|
|
4039
4145
|
|
|
4146
|
+
test("rerouted compaction records usage under the profile that actually served", async () => {
|
|
4147
|
+
const ctx = makeCtx();
|
|
4148
|
+
|
|
4149
|
+
await applyCompactionResult(
|
|
4150
|
+
ctx,
|
|
4151
|
+
{
|
|
4152
|
+
messages: [
|
|
4153
|
+
{ role: "user", content: [{ type: "text", text: "summary" }] },
|
|
4154
|
+
],
|
|
4155
|
+
compactedPersistedMessages: 4,
|
|
4156
|
+
previousEstimatedInputTokens: 12000,
|
|
4157
|
+
estimatedInputTokens: 3000,
|
|
4158
|
+
maxInputTokens: 100000,
|
|
4159
|
+
thresholdTokens: 80000,
|
|
4160
|
+
compactedMessages: 4,
|
|
4161
|
+
summaryCalls: 1,
|
|
4162
|
+
summaryInputTokens: 100,
|
|
4163
|
+
summaryOutputTokens: 20,
|
|
4164
|
+
summaryModel: "claude-sonnet-5",
|
|
4165
|
+
summaryText: "summary",
|
|
4166
|
+
summaryCallSite: "compactionAgent",
|
|
4167
|
+
// What the compactor resolved before the call...
|
|
4168
|
+
summaryOverrideProfile: "primaryProfile",
|
|
4169
|
+
// ...and what the reroute actually served.
|
|
4170
|
+
summaryActualProvider: "anthropic",
|
|
4171
|
+
summaryActualInferenceProfile: "backupProfile",
|
|
4172
|
+
},
|
|
4173
|
+
() => {},
|
|
4174
|
+
"req-1",
|
|
4175
|
+
);
|
|
4176
|
+
|
|
4177
|
+
const compactorCall = recordUsageMock.mock.calls.find(
|
|
4178
|
+
(call) => (call as unknown[])[5] === "context_compactor",
|
|
4179
|
+
) as unknown[] | undefined;
|
|
4180
|
+
|
|
4181
|
+
expect(compactorCall).toBeDefined();
|
|
4182
|
+
// All three attribution facets of the row describe the same call.
|
|
4183
|
+
expect(compactorCall?.[0]).toMatchObject({ providerName: "anthropic" });
|
|
4184
|
+
expect(compactorCall?.[3]).toBe("claude-sonnet-5");
|
|
4185
|
+
expect(compactorCall?.[12]).toEqual({
|
|
4186
|
+
callSite: "compactionAgent",
|
|
4187
|
+
overrideProfile: "backupProfile",
|
|
4188
|
+
forceOverrideProfile: true,
|
|
4189
|
+
});
|
|
4190
|
+
});
|
|
4191
|
+
|
|
4192
|
+
test("non-rerouted compaction keeps the compactor's own attribution", async () => {
|
|
4193
|
+
const ctx = makeCtx();
|
|
4194
|
+
|
|
4195
|
+
await applyCompactionResult(
|
|
4196
|
+
ctx,
|
|
4197
|
+
{
|
|
4198
|
+
messages: [
|
|
4199
|
+
{ role: "user", content: [{ type: "text", text: "summary" }] },
|
|
4200
|
+
],
|
|
4201
|
+
compactedPersistedMessages: 4,
|
|
4202
|
+
previousEstimatedInputTokens: 12000,
|
|
4203
|
+
estimatedInputTokens: 3000,
|
|
4204
|
+
maxInputTokens: 100000,
|
|
4205
|
+
thresholdTokens: 80000,
|
|
4206
|
+
compactedMessages: 4,
|
|
4207
|
+
summaryCalls: 1,
|
|
4208
|
+
summaryInputTokens: 100,
|
|
4209
|
+
summaryOutputTokens: 20,
|
|
4210
|
+
summaryModel: "mock-model",
|
|
4211
|
+
summaryText: "summary",
|
|
4212
|
+
summaryCallSite: "compactionAgent",
|
|
4213
|
+
summaryOverrideProfile: "primaryProfile",
|
|
4214
|
+
summaryActualProvider: "openai",
|
|
4215
|
+
},
|
|
4216
|
+
() => {},
|
|
4217
|
+
"req-1",
|
|
4218
|
+
);
|
|
4219
|
+
|
|
4220
|
+
const compactorCall = recordUsageMock.mock.calls.find(
|
|
4221
|
+
(call) => (call as unknown[])[5] === "context_compactor",
|
|
4222
|
+
) as unknown[] | undefined;
|
|
4223
|
+
|
|
4224
|
+
expect(compactorCall).toBeDefined();
|
|
4225
|
+
expect(compactorCall?.[0]).toMatchObject({ providerName: "openai" });
|
|
4226
|
+
expect(compactorCall?.[3]).toBe("mock-model");
|
|
4227
|
+
expect(compactorCall?.[12]).toEqual({
|
|
4228
|
+
callSite: "compactionAgent",
|
|
4229
|
+
overrideProfile: "primaryProfile",
|
|
4230
|
+
});
|
|
4231
|
+
});
|
|
4232
|
+
|
|
4233
|
+
test("compaction that served no call falls back to the conversation's provider", async () => {
|
|
4234
|
+
// The degraded shape: `emptyResult` paths record no served attribution
|
|
4235
|
+
// because no call happened. Provider must fall back to the
|
|
4236
|
+
// conversation's own, and the attribution input must be byte-identical
|
|
4237
|
+
// to what it was before served attribution existed.
|
|
4238
|
+
const ctx = makeCtx();
|
|
4239
|
+
|
|
4240
|
+
await applyCompactionResult(
|
|
4241
|
+
ctx,
|
|
4242
|
+
{
|
|
4243
|
+
messages: [
|
|
4244
|
+
{ role: "user", content: [{ type: "text", text: "summary" }] },
|
|
4245
|
+
],
|
|
4246
|
+
compactedPersistedMessages: 4,
|
|
4247
|
+
previousEstimatedInputTokens: 12000,
|
|
4248
|
+
estimatedInputTokens: 3000,
|
|
4249
|
+
maxInputTokens: 100000,
|
|
4250
|
+
thresholdTokens: 80000,
|
|
4251
|
+
compactedMessages: 4,
|
|
4252
|
+
summaryCalls: 1,
|
|
4253
|
+
summaryInputTokens: 100,
|
|
4254
|
+
summaryOutputTokens: 20,
|
|
4255
|
+
summaryModel: "mock-model",
|
|
4256
|
+
summaryText: "summary",
|
|
4257
|
+
summaryCallSite: "compactionAgent",
|
|
4258
|
+
summaryOverrideProfile: "primaryProfile",
|
|
4259
|
+
},
|
|
4260
|
+
() => {},
|
|
4261
|
+
"req-1",
|
|
4262
|
+
);
|
|
4263
|
+
|
|
4264
|
+
const compactorCall = recordUsageMock.mock.calls.find(
|
|
4265
|
+
(call) => (call as unknown[])[5] === "context_compactor",
|
|
4266
|
+
) as unknown[] | undefined;
|
|
4267
|
+
|
|
4268
|
+
expect(compactorCall).toBeDefined();
|
|
4269
|
+
expect(compactorCall?.[0]).toMatchObject({
|
|
4270
|
+
providerName: "mock-provider",
|
|
4271
|
+
});
|
|
4272
|
+
expect(compactorCall?.[12]).toEqual({
|
|
4273
|
+
callSite: "compactionAgent",
|
|
4274
|
+
overrideProfile: "primaryProfile",
|
|
4275
|
+
});
|
|
4276
|
+
});
|
|
4277
|
+
|
|
4040
4278
|
test("applyCompactionResult advances the persisted count from the trusted in-context boundary", async () => {
|
|
4041
4279
|
// Trusted views slice past the already-compacted prefix, so a further
|
|
4042
4280
|
// compaction advances the persisted count from the mirrored DB boundary.
|
|
@@ -1136,6 +1136,50 @@ describe("classifyConversationError", () => {
|
|
|
1136
1136
|
});
|
|
1137
1137
|
|
|
1138
1138
|
describe("reason-driven classification (ProviderError.reason)", () => {
|
|
1139
|
+
it("classifies reason=request_shape_unsupported as a friendly capability-mismatch message", () => {
|
|
1140
|
+
// GIVEN a chat-template 400 that the provider stamped with the
|
|
1141
|
+
// semantic reason (Together serving MiniMax M3)
|
|
1142
|
+
const err = new ProviderError(
|
|
1143
|
+
"Together AI API error (400): Failed to apply chat template: invalid operation: object is not callable (in chat:22)",
|
|
1144
|
+
"together",
|
|
1145
|
+
400,
|
|
1146
|
+
{ reason: "request_shape_unsupported" },
|
|
1147
|
+
);
|
|
1148
|
+
|
|
1149
|
+
// WHEN it is classified
|
|
1150
|
+
const result = classifyConversationError(err, baseCtx);
|
|
1151
|
+
|
|
1152
|
+
// THEN the user sees the capability-mismatch copy, not the raw
|
|
1153
|
+
// template error
|
|
1154
|
+
expect(result.code).toBe("PROVIDER_API");
|
|
1155
|
+
expect(result.errorCategory).toBe("request_shape_unsupported");
|
|
1156
|
+
expect(result.retryable).toBe(false);
|
|
1157
|
+
expect(result.userMessage).toContain(
|
|
1158
|
+
"couldn't process the request format",
|
|
1159
|
+
);
|
|
1160
|
+
expect(result.userMessage).not.toContain("chat template");
|
|
1161
|
+
});
|
|
1162
|
+
|
|
1163
|
+
it("classifies a reason-less chat-template 400 via the message ladder", () => {
|
|
1164
|
+
// GIVEN a chat-template 400 whose ProviderError carries no semantic
|
|
1165
|
+
// reason (e.g. thrown by a non-OpenAI-compatible wrapper)
|
|
1166
|
+
const err = new ProviderError(
|
|
1167
|
+
"Together AI API error (400): Failed to apply chat template: invalid operation: object is not callable (in chat:22)",
|
|
1168
|
+
"together",
|
|
1169
|
+
400,
|
|
1170
|
+
);
|
|
1171
|
+
|
|
1172
|
+
// WHEN it is classified
|
|
1173
|
+
const result = classifyConversationError(err, baseCtx);
|
|
1174
|
+
|
|
1175
|
+
// THEN the regex ladder still lands on the capability-mismatch copy
|
|
1176
|
+
expect(result.code).toBe("PROVIDER_API");
|
|
1177
|
+
expect(result.errorCategory).toBe("request_shape_unsupported");
|
|
1178
|
+
expect(result.userMessage).toContain(
|
|
1179
|
+
"couldn't process the request format",
|
|
1180
|
+
);
|
|
1181
|
+
});
|
|
1182
|
+
|
|
1139
1183
|
it("classifies reason=model_restricted on the skew-safe PROVIDER_API code with a specific errorCategory", () => {
|
|
1140
1184
|
const err = new ProviderError(
|
|
1141
1185
|
"Vercel AI Gateway API error (403): Model claude-opus-4 is restricted on your plan [type=no_providers_available]",
|
|
@@ -9,6 +9,13 @@ mock.module("../subagent/index.js", () => ({
|
|
|
9
9
|
getSubagentManager: () => ({
|
|
10
10
|
abortAllForParent: (id: string) => abortedParents.push(id),
|
|
11
11
|
getChildrenOf: () => protectedChildren,
|
|
12
|
+
hasActiveChildren: () =>
|
|
13
|
+
protectedChildren.some(
|
|
14
|
+
(c) =>
|
|
15
|
+
c.status === "running" ||
|
|
16
|
+
c.status === "pending" ||
|
|
17
|
+
c.status === "awaiting_input",
|
|
18
|
+
),
|
|
12
19
|
}),
|
|
13
20
|
}));
|
|
14
21
|
|
|
@@ -236,6 +243,18 @@ describe("ConversationEvictor", () => {
|
|
|
236
243
|
expect(sessions.has("a")).toBe(true);
|
|
237
244
|
expect(s1.disposed).toBe(false);
|
|
238
245
|
});
|
|
246
|
+
|
|
247
|
+
test("skips conversations with awaiting_input subagents", () => {
|
|
248
|
+
protectedChildren = [{ status: "awaiting_input" }];
|
|
249
|
+
const s1 = createMockSession();
|
|
250
|
+
sessions.set("a", s1);
|
|
251
|
+
|
|
252
|
+
const result = evictor.sweep();
|
|
253
|
+
|
|
254
|
+
expect(result.skipped).toBe(1);
|
|
255
|
+
expect(sessions.has("a")).toBe(true);
|
|
256
|
+
expect(s1.disposed).toBe(false);
|
|
257
|
+
});
|
|
239
258
|
});
|
|
240
259
|
|
|
241
260
|
describe("remove()", () => {
|