@vellumai/assistant 0.10.9 → 0.10.10-dev.202607162206.d08e98e
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 +1 -1
- package/Dockerfile +8 -0
- package/docs/activation-funnel-telemetry.md +13 -7
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/redacted-credential.test.ts +200 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/redacted-credential.ts +226 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/redacted-credential.test.ts +200 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/redacted-credential.ts +226 -0
- package/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/service-contracts/src/__tests__/redacted-credential.test.ts +200 -0
- package/node_modules/@vellumai/service-contracts/src/redacted-credential.ts +226 -0
- package/openapi.yaml +530 -107
- package/package.json +1 -1
- package/scripts/generate-openapi.ts +8 -0
- package/src/__tests__/activation-early-marking.test.ts +6 -5
- package/src/__tests__/agent-loop-override-profile.test.ts +22 -25
- package/src/__tests__/agent-wake-override-profile.test.ts +21 -48
- package/src/__tests__/app-builder-tool-scripts.test.ts +0 -1
- package/src/__tests__/app-bundler.test.ts +4 -12
- package/src/__tests__/app-executors.test.ts +2 -49
- package/src/__tests__/app-routes-csp.test.ts +114 -146
- package/src/__tests__/auth-fallback-events-store.test.ts +8 -1
- package/src/__tests__/build-persisted-content.test.ts +96 -0
- package/src/__tests__/bundle-scanner.test.ts +27 -1
- package/src/__tests__/call-controller.test.ts +291 -0
- package/src/__tests__/call-site-routing-connection-auto-resolve.test.ts +165 -0
- package/src/__tests__/chat-credential-redaction.test.ts +1395 -0
- package/src/__tests__/chat-reveal-guard-priming.test.ts +791 -0
- package/src/__tests__/compaction.benchmark.test.ts +2 -1
- package/src/__tests__/compactor-image-manifest-trust.test.ts +50 -0
- package/src/__tests__/config-loader-backfill.test.ts +9 -4
- package/src/__tests__/config-schema-cmd.test.ts +10 -11
- package/src/__tests__/config-schema.test.ts +190 -257
- package/src/__tests__/conversation-agent-loop-fatal-cleanup.test.ts +208 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +5 -19
- package/src/__tests__/conversation-agent-loop.test.ts +22 -9
- package/src/__tests__/conversation-error.test.ts +31 -0
- package/src/__tests__/conversation-load-history-repair.test.ts +110 -0
- package/src/__tests__/conversation-process-callsite.test.ts +12 -19
- package/src/__tests__/conversation-routes-slash-commands.test.ts +7 -21
- package/src/__tests__/conversation-summarize-route.test.ts +36 -44
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-activation-emit.test.ts +7 -7
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +0 -1
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +0 -1
- package/src/__tests__/conversation-usage.test.ts +4 -12
- package/src/__tests__/credential-routes.test.ts +250 -0
- package/src/__tests__/credential-security-invariants.test.ts +3 -0
- package/src/__tests__/db-migration-rollback.test.ts +22 -0
- package/src/__tests__/empty-response-hook.test.ts +186 -1
- package/src/__tests__/external-plugin-loader.test.ts +11 -5
- package/src/__tests__/heartbeat-service.test.ts +0 -28
- package/src/__tests__/host-shell-tool.test.ts +2 -0
- package/src/__tests__/inactive-tool-error-messages.test.ts +2 -2
- package/src/__tests__/inference-no-mode-boot-e2e.test.ts +52 -8
- package/src/__tests__/internal-telemetry-routes.test.ts +23 -4
- package/src/__tests__/invite-routes-http.test.ts +12 -16
- package/src/__tests__/list-all-apps.test.ts +0 -4
- package/src/__tests__/llm-context-resolution.test.ts +32 -56
- package/src/__tests__/llm-request-log-turn-query.test.ts +109 -0
- package/src/__tests__/llm-resolver-override-or-default.test.ts +3 -52
- package/src/__tests__/llm-resolver.test.ts +342 -602
- package/src/__tests__/llm-schema.test.ts +79 -37
- package/src/__tests__/max-tokens-continue-hook.test.ts +19 -0
- package/src/__tests__/media-stream-output.test.ts +259 -3
- package/src/__tests__/media-stream-server-integration.test.ts +22 -1
- package/src/__tests__/media-stream-stt-session.test.ts +47 -0
- package/src/__tests__/memory-jobs-worker-cleanup-cadence.test.ts +33 -0
- package/src/__tests__/memory-recall-log-store.test.ts +47 -13
- package/src/__tests__/mock-gateway-ipc.ts +46 -1
- package/src/__tests__/mtime-cache.test.ts +61 -0
- package/src/__tests__/navigate-settings-tab.test.ts +2 -0
- package/src/__tests__/normalize-onboarding.test.ts +33 -0
- package/src/__tests__/onboarding-persona-write.test.ts +26 -0
- package/src/__tests__/plugin-api-resolve-credential.test.ts +140 -0
- package/src/__tests__/plugin-app-serve-routes.test.ts +166 -14
- package/src/__tests__/plugin-import-boundary-reverse-guard.test.ts +2 -0
- package/src/__tests__/post-turn-tool-result-truncation.test.ts +38 -0
- package/src/__tests__/provider-commit-message-generator.test.ts +27 -20
- package/src/__tests__/provider-connections-backfill.test.ts +138 -0
- package/src/__tests__/provider-platform-proxy-integration.test.ts +10 -31
- package/src/__tests__/provider-registry-ollama.test.ts +8 -18
- package/src/__tests__/provider-send-message-override-profile.test.ts +23 -23
- package/src/__tests__/provider-usage-tracking.test.ts +9 -19
- package/src/__tests__/prune-old-conversations-job.test.ts +12 -0
- package/src/__tests__/published-app-updater.test.ts +22 -16
- package/src/__tests__/registry.test.ts +5 -16
- package/src/__tests__/retry-openrouter-only-normalization.test.ts +22 -23
- package/src/__tests__/retry-thinking-adaptive-only.test.ts +43 -48
- package/src/__tests__/retry-thinking-tool-choice.test.ts +57 -66
- package/src/__tests__/retry-verbosity-normalization.test.ts +24 -23
- package/src/__tests__/reveal-success-registry.test.ts +123 -0
- package/src/__tests__/run-conversation-turn-persistence.test.ts +130 -0
- package/src/__tests__/secret-fixtures.ts +9 -0
- package/src/__tests__/server-history-render.test.ts +28 -0
- package/src/__tests__/skills.test.ts +9 -4
- package/src/__tests__/slack-share-routes.test.ts +0 -1
- package/src/__tests__/stt-stream-session.test.ts +6 -5
- package/src/__tests__/subagent-call-site-routing.test.ts +69 -95
- package/src/__tests__/subagent-disposal.test.ts +2 -0
- package/src/__tests__/subagent-fork-notifications.test.ts +2 -0
- package/src/__tests__/subagent-fork-spawn.test.ts +2 -0
- package/src/__tests__/subagent-manager-notify.test.ts +2 -0
- package/src/__tests__/subagent-role-registry.test.ts +37 -0
- package/src/__tests__/subagent-spawn-and-await.test.ts +1 -0
- package/src/__tests__/subagent-terminal-message.test.ts +50 -0
- package/src/__tests__/subagent-tool-gate-mode.test.ts +78 -5
- package/src/__tests__/surface-completion-nudge-hook.test.ts +19 -0
- package/src/__tests__/telemetry-routes.test.ts +99 -19
- package/src/__tests__/tool-audit.test.ts +34 -4
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -1
- package/src/__tests__/tool-profiler.test.ts +72 -1
- package/src/__tests__/tool-result-spool.test.ts +49 -4
- package/src/__tests__/tool-side-effects-slack-dm.test.ts +0 -1
- package/src/__tests__/ui-channel-variants.test.ts +108 -0
- package/src/__tests__/ui-shape-teaching.test.ts +255 -0
- package/src/__tests__/usage-attribution.test.ts +18 -41
- package/src/__tests__/user-plugin-loader.test.ts +4 -4
- package/src/__tests__/voice-config-update.test.ts +46 -0
- package/src/__tests__/voice-session-bridge.test.ts +216 -84
- package/src/__tests__/workspace-migration-131-drop-web-fetch-mode.test.ts +120 -0
- package/src/agent/loop.ts +4 -3
- package/src/api/events/open-conversation.test.ts +64 -0
- package/src/api/events/open-conversation.ts +33 -0
- package/src/api/index.ts +6 -0
- package/src/api/responses/conversation-message.ts +5 -0
- package/src/apps/app-store.ts +25 -35
- package/src/bundler/app-bundler.ts +34 -48
- package/src/bundler/app-compiler.ts +39 -4
- package/src/bundler/bundle-scanner.ts +13 -0
- package/src/bundler/manifest.ts +1 -1
- package/src/calls/__tests__/voice-session-bridge.test.ts +47 -0
- package/src/calls/call-constants.ts +5 -0
- package/src/calls/call-controller.ts +100 -32
- package/src/calls/call-transport.ts +9 -0
- package/src/calls/media-stream-output.ts +107 -4
- package/src/calls/media-stream-server.ts +29 -9
- package/src/calls/media-stream-stt-session.ts +7 -1
- package/src/calls/media-turn-detector.ts +11 -1
- package/src/calls/voice-session-bridge.ts +84 -63
- package/src/cli/commands/__tests__/inference-providers.test.ts +270 -33
- package/src/cli/commands/__tests__/notifications.test.ts +24 -3
- package/src/cli/commands/config.help.ts +6 -6
- package/src/cli/commands/credentials.help.ts +13 -0
- package/src/cli/commands/credentials.ts +6 -1
- package/src/cli/commands/email.help.ts +7 -0
- package/src/cli/commands/email.ts +35 -1
- package/src/cli/commands/inference-providers.ts +168 -107
- package/src/cli/commands/inference.help.ts +118 -46
- package/src/cli/commands/memory/index.help.ts +23 -0
- package/src/cli/commands/memory/nodes.ts +146 -0
- package/src/cli/commands/notifications.help.ts +10 -10
- package/src/cli/commands/oauth/connect-surface-guidance.test.ts +40 -0
- package/src/cli/commands/oauth/connect-surface-guidance.ts +54 -0
- package/src/cli/commands/oauth/connect.test.ts +126 -0
- package/src/cli/commands/oauth/connect.ts +29 -6
- package/src/cli/commands/oauth/index.help.ts +7 -1
- package/src/cli/commands/oauth/status.test.ts +69 -3
- package/src/cli/commands/oauth/status.ts +50 -12
- package/src/cli/commands/plugins.help.ts +13 -2
- package/src/cli/commands/plugins.ts +61 -7
- package/src/cli/commands/telemetry.help.ts +13 -0
- package/src/cli/commands/telemetry.ts +45 -5
- package/src/cli/lib/__tests__/plugin-catalog-local.test.ts +8 -2
- package/src/cli/lib/__tests__/upgrade-plugin.test.ts +213 -9
- package/src/cli/lib/bundled-marketplace.json +93 -36
- package/src/cli/lib/inspect-plugin.ts +5 -1
- package/src/cli/lib/install-from-github.ts +68 -4
- package/src/cli/lib/plugin-catalog-local.ts +6 -2
- package/src/cli/lib/plugin-fingerprint.ts +3 -3
- package/src/cli/lib/upgrade-plugin.ts +236 -35
- package/src/config/__tests__/default-profile-catalog.test.ts +8 -12
- package/src/config/__tests__/plugin-resident-skill-discovery.test.ts +137 -0
- package/src/config/__tests__/profile-materialization.test.ts +1 -88
- package/src/config/bundled-skills/AGENTS.md +3 -30
- package/src/config/bundled-skills/app-builder/SKILL.md +5 -3
- package/src/config/bundled-skills/app-builder/TOOLS.json +23 -0
- package/src/config/bundled-skills/app-builder/tools/app-open.ts +32 -0
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +1 -1
- package/src/config/bundled-skills/phone-calls/references/CONFIG.md +7 -7
- package/src/config/bundled-skills/schedule/SKILL.md +1 -1
- package/src/config/bundled-skills/schedule/TOOLS.json +1 -1
- package/src/config/bundled-skills/settings/TOOLS.json +3 -1
- package/src/config/bundled-skills/settings/tools/navigate-settings-tab.ts +2 -0
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +18 -10
- package/src/config/bundled-skills/subagent/SKILL.md +2 -0
- package/src/config/bundled-skills/subagent/TOOLS.json +1 -1
- package/src/config/call-site-defaults.ts +3 -3
- package/src/config/feature-flag-registry.json +24 -39
- package/src/config/llm-resolver.ts +88 -475
- package/src/config/profile-materialization.ts +11 -11
- package/src/config/schema.ts +93 -127
- package/src/config/schemas/__tests__/live-voice.test.ts +24 -6
- package/src/config/schemas/__tests__/stt.test.ts +31 -3
- package/src/config/schemas/live-voice.ts +10 -4
- package/src/config/schemas/llm.ts +52 -15
- package/src/config/schemas/memory-lifecycle.ts +1 -1
- package/src/config/schemas/memory-retrospective.ts +8 -0
- package/src/config/schemas/memory-v2.ts +2 -2
- package/src/config/schemas/memory-v3.ts +1 -1
- package/src/config/schemas/services.ts +6 -3
- package/src/config/schemas/stt.ts +38 -21
- package/src/config/schemas/tts.ts +13 -18
- package/src/config/skills.ts +31 -21
- package/src/context/compactor.ts +44 -7
- package/src/context/post-turn-tool-result-truncation.ts +4 -2
- package/src/context/tool-result-spool.ts +26 -5
- package/src/conversations/__tests__/message-consolidation.test.ts +48 -0
- package/src/conversations/message-consolidation.ts +22 -2
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +5 -12
- package/src/daemon/app-source-watcher.ts +17 -23
- package/src/daemon/chat-credential-redaction.ts +1365 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +627 -34
- package/src/daemon/conversation-agent-loop.ts +71 -31
- package/src/daemon/conversation-error.ts +36 -14
- package/src/daemon/conversation-process.ts +22 -0
- package/src/daemon/conversation-store.ts +35 -0
- package/src/daemon/conversation-surfaces.ts +25 -25
- package/src/daemon/conversation-tool-setup.ts +33 -7
- package/src/daemon/conversation.ts +55 -5
- package/src/daemon/handlers/shared.ts +33 -2
- package/src/daemon/lifecycle.ts +4 -4
- package/src/daemon/message-types/conversations.ts +6 -17
- package/src/daemon/providers-setup.ts +8 -0
- package/src/daemon/tool-setup-types.ts +7 -1
- package/src/daemon/wake-conversation-ops.ts +10 -1
- package/src/hooks/types.ts +9 -0
- package/src/ipc/__tests__/email-ipc.test.ts +90 -0
- package/src/ipc/gateway-client.test.ts +59 -0
- package/src/ipc/gateway-client.ts +70 -27
- package/src/live-voice/__tests__/live-voice-events.test.ts +14 -2
- package/src/live-voice/__tests__/live-voice-integration.test.ts +116 -2
- package/src/live-voice/__tests__/live-voice-vad.test.ts +804 -13
- package/src/live-voice/__tests__/protocol.test.ts +122 -0
- package/src/live-voice/live-voice-session.ts +443 -40
- package/src/live-voice/protocol.ts +143 -1
- package/src/monitoring/__tests__/plugin-source-watch.test.ts +3 -1
- package/src/monitoring/plugin-source-watch.ts +3 -62
- package/src/notifications/README.md +1 -1
- package/src/permissions/checker.ts +8 -4
- package/src/persistence/__tests__/db-init-migrations-ok.test.ts +26 -0
- package/src/persistence/conversation-crud.ts +104 -2
- package/src/persistence/db-init.ts +14 -3
- package/src/persistence/job-handlers/cleanup.ts +15 -7
- package/src/persistence/llm-request-log-store.ts +88 -52
- package/src/persistence/migrations/298-move-memory-jobs-to-memory-db.ts +7 -31
- package/src/persistence/migrations/305-drop-contact-acl-columns.ts +3 -2
- package/src/persistence/migrations/326-move-injection-events-to-memory-db.ts +8 -34
- package/src/persistence/migrations/336-move-memory-v2-activation-logs-to-memory-db.ts +90 -0
- package/src/persistence/migrations/337-move-memory-recall-logs-to-memory-db.ts +114 -0
- package/src/persistence/migrations/338-move-memory-v3-selections-to-memory-db.ts +84 -0
- package/src/persistence/migrations/339-move-activation-sessions-to-memory-db.ts +52 -0
- package/src/persistence/migrations/__tests__/run-migrations.test.ts +155 -0
- package/src/persistence/migrations/helpers/relocation.ts +44 -1
- package/src/persistence/migrations/run-migrations.ts +25 -1
- package/src/persistence/schema/infrastructure.ts +9 -0
- package/src/persistence/schema/memory-core.ts +3 -0
- package/src/persistence/schema/memory-injection.ts +2 -0
- package/src/persistence/steps.ts +36 -0
- package/src/platform/client.test.ts +1 -44
- package/src/platform/client.ts +10 -20
- package/src/platform/consent-cache.test.ts +89 -23
- package/src/platform/consent-cache.ts +71 -33
- package/src/plugin-api/constants.ts +12 -0
- package/src/plugin-api/conversation-turn.ts +37 -14
- package/src/plugin-api/index.ts +11 -1
- package/src/plugin-api/resolve-credential.ts +75 -0
- package/src/plugin-api/vision-support.test.ts +8 -19
- package/src/plugin-api/vision-support.ts +26 -17
- package/src/plugins/collect-source-versions.ts +77 -0
- package/src/plugins/defaults/compaction/compact.ts +6 -0
- package/src/plugins/defaults/compaction/window-manager.ts +9 -0
- package/src/plugins/defaults/empty-response/hooks/post-model-call.ts +18 -24
- package/src/plugins/defaults/empty-response/hooks/user-prompt-submit.ts +38 -0
- package/src/plugins/defaults/empty-response/refusal-quarantine.ts +99 -0
- package/src/plugins/defaults/image-fallback/__tests__/caption-cache-persistence.test.ts +7 -3
- package/src/plugins/defaults/index.ts +8 -1
- package/src/plugins/defaults/max-tokens-continue/hooks/post-model-call.ts +4 -1
- package/src/plugins/defaults/memory/__tests__/activation-session-store.test.ts +48 -6
- package/src/plugins/defaults/memory/__tests__/db-memory-attach.test.ts +28 -22
- package/src/plugins/defaults/memory/__tests__/memory-log-stores-degraded.test.ts +148 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +80 -8
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-prompt.test.ts +202 -0
- package/src/plugins/defaults/memory/__tests__/memory-v2-activation-log-store.test.ts +64 -25
- package/src/plugins/defaults/memory/__tests__/memory-v2-concept-frequency.test.ts +24 -11
- package/src/plugins/defaults/memory/__tests__/prompt-override.test.ts +70 -6
- package/src/plugins/defaults/memory/__tests__/table-relocation.test.ts +278 -0
- package/src/plugins/defaults/memory/activation-session-store.ts +27 -20
- package/src/plugins/defaults/memory/context-search/sources/memory-v2.ts +2 -9
- package/src/plugins/defaults/memory/context-search/sources/workspace.ts +1 -8
- package/src/plugins/defaults/memory/graph/retriever.test.ts +6 -6
- package/src/plugins/defaults/memory/graph/store.ts +114 -0
- package/src/plugins/defaults/memory/graph/tool-handlers.ts +36 -1
- package/src/plugins/defaults/memory/graph/tools.ts +47 -13
- package/src/plugins/defaults/memory/graph-topology/build-memory-graph.ts +16 -35
- package/src/plugins/defaults/memory/jobs-worker.ts +10 -1
- package/src/plugins/defaults/memory/memory-db.ts +3 -2
- package/src/plugins/defaults/memory/memory-recall-log-store.ts +129 -66
- package/src/plugins/defaults/memory/memory-retrospective-constants.ts +8 -0
- package/src/plugins/defaults/memory/memory-retrospective-job.ts +11 -122
- package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +216 -0
- package/src/plugins/defaults/memory/memory-v2-activation-log-store.ts +84 -46
- package/src/plugins/defaults/memory/memory-v2-concept-frequency.ts +42 -32
- package/src/plugins/defaults/memory/path-containment.ts +21 -0
- package/src/plugins/defaults/memory/prompt-override.ts +51 -13
- package/src/plugins/defaults/memory/tools.test.ts +34 -0
- package/src/plugins/defaults/memory/tools.ts +12 -2
- package/src/plugins/defaults/memory/v2/__tests__/harness-compare.test.ts +19 -15
- package/src/plugins/defaults/memory/v2/__tests__/harness-oracle.test.ts +24 -19
- package/src/plugins/defaults/memory/v2/__tests__/harness-replay-input.test.ts +19 -15
- package/src/plugins/defaults/memory/v2/__tests__/injection.test.ts +22 -2
- package/src/plugins/defaults/memory/v2/__tests__/prompts-consolidation.test.ts +5 -3
- package/src/plugins/defaults/memory/v2/harness/oracle.ts +59 -41
- package/src/plugins/defaults/memory/v2/harness/replay-input.ts +29 -25
- package/src/plugins/defaults/memory/v2/migration.ts +46 -16
- package/src/plugins/defaults/memory/v2/prompts/consolidation.ts +4 -0
- package/src/plugins/defaults/memory/v3/__tests__/carry-integration.test.ts +17 -15
- package/src/plugins/defaults/memory/v3/__tests__/gate.test.ts +24 -22
- package/src/plugins/defaults/memory/v3/__tests__/injection.test.ts +9 -5
- package/src/plugins/defaults/memory/v3/__tests__/orchestrate.test.ts +277 -10
- package/src/plugins/defaults/memory/v3/__tests__/selection-log-store.test.ts +57 -5
- package/src/plugins/defaults/memory/v3/__tests__/shadow-integration.test.ts +9 -7
- package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +38 -7
- package/src/plugins/defaults/memory/v3/hot-set.test.ts +36 -18
- package/src/plugins/defaults/memory/v3/hot-set.ts +12 -14
- package/src/plugins/defaults/memory/v3/learned-edges.test.ts +47 -27
- package/src/plugins/defaults/memory/v3/learned-edges.ts +12 -14
- package/src/plugins/defaults/memory/v3/orchestrate.ts +139 -22
- package/src/plugins/defaults/memory/v3/prune.test.ts +10 -3
- package/src/plugins/defaults/memory/v3/prune.ts +17 -11
- package/src/plugins/defaults/memory/v3/selection-log-store.ts +23 -11
- package/src/plugins/defaults/memory/v3/shadow-plugin.ts +70 -53
- package/src/plugins/defaults/surface-completion-nudge/hooks/post-model-call.ts +9 -6
- package/src/plugins/external-plugin-loader.ts +15 -15
- package/src/plugins/mtime-cache.ts +47 -0
- package/src/plugins/pipeline.ts +9 -1
- package/src/plugins/plugin-execution-context.ts +44 -0
- package/src/plugins/plugin-tree-walk.ts +31 -24
- package/src/plugins/source-fingerprint.ts +3 -4
- package/src/prompts/normalize-onboarding.ts +12 -0
- package/src/prompts/persona-resolver.ts +8 -0
- package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +1 -1
- package/src/providers/__tests__/dispatch-connection-routing.test.ts +6 -9
- package/src/providers/__tests__/registry-native-web-search.test.ts +4 -10
- package/src/providers/__tests__/retry-callsite.test.ts +235 -215
- package/src/providers/__tests__/satellite-connection-routing.test.ts +8 -16
- package/src/providers/atlascloud/client.ts +10 -49
- package/src/providers/baseten/client.ts +43 -0
- package/src/providers/call-site-routing.ts +26 -13
- package/src/providers/connection-resolution.ts +9 -11
- package/src/providers/fetch-provider-catalog.ts +4 -2
- package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +29 -4
- package/src/providers/inference/__tests__/connection-availability-keyless.test.ts +78 -0
- package/src/providers/inference/adapter-factory.ts +27 -6
- package/src/providers/inference/auth.ts +29 -1
- package/src/providers/inference/backfill.ts +61 -10
- package/src/providers/inference/connection-availability.ts +3 -2
- package/src/providers/inference/resolve-auth.ts +9 -1
- package/src/providers/model-catalog.ts +37 -0
- package/src/providers/openai/__tests__/api-error-normalization.test.ts +24 -2
- package/src/providers/openai/api-key-validation.ts +70 -0
- package/src/providers/retry.ts +2 -0
- package/src/providers/types.ts +11 -4
- package/src/providers/vellum-model-routing.test.ts +26 -0
- package/src/providers/vellum-model-routing.ts +30 -0
- package/src/providers/voice-error-copy.ts +47 -0
- package/src/runtime/agent-wake.ts +40 -24
- package/src/runtime/for-chat-mint-registry.ts +118 -0
- package/src/runtime/reveal-nonce.ts +49 -0
- package/src/runtime/reveal-success-registry.ts +306 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +88 -3
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +161 -30
- package/src/runtime/routes/__tests__/migration-vellum-metadata-reconcile.test.ts +7 -0
- package/src/runtime/routes/__tests__/schedule-worker-routes.test.ts +15 -0
- package/src/runtime/routes/app-management-routes.ts +152 -45
- package/src/runtime/routes/app-routes.ts +16 -77
- package/src/runtime/routes/canned-message-complete.ts +16 -16
- package/src/runtime/routes/conversation-management-routes.ts +17 -14
- package/src/runtime/routes/conversation-query-routes.ts +54 -6
- package/src/runtime/routes/conversation-routes.ts +48 -2
- package/src/runtime/routes/credential-routes.ts +116 -3
- package/src/runtime/routes/email-routes.ts +17 -1
- package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +33 -5
- package/src/runtime/routes/inbound-stages/transcribe-audio.ts +6 -5
- package/src/runtime/routes/inference-provider-connection-routes.ts +77 -28
- package/src/runtime/routes/inference-send-routes.ts +1 -1
- package/src/runtime/routes/internal-telemetry-routes.ts +7 -6
- package/src/runtime/routes/plugins-routes.ts +37 -6
- package/src/runtime/routes/publish-routes.ts +15 -18
- package/src/runtime/routes/schedule-worker-routes.ts +9 -0
- package/src/runtime/routes/secret-routes.ts +10 -0
- package/src/runtime/routes/telemetry-routes.ts +149 -45
- package/src/schedule/__tests__/schedule-timezone.test.ts +101 -0
- package/src/schedule/__tests__/worker-watchdog.test.ts +209 -0
- package/src/schedule/schedule-store.ts +12 -1
- package/src/schedule/schedule-timezone.ts +63 -0
- package/src/schedule/scheduler.ts +100 -6
- package/src/schedule/worker-control.ts +19 -0
- package/src/security/auth-fallback-events-store.ts +7 -6
- package/src/security/secret-scanner.ts +26 -1
- package/src/services/published-app-updater.ts +6 -11
- package/src/stt/stt-stream-session.ts +36 -16
- package/src/subagent/manager.ts +34 -8
- package/src/telemetry/AGENTS.md +30 -1
- package/src/telemetry/__tests__/config-setting-snapshot.test.ts +18 -0
- package/src/telemetry/__tests__/outbox-test-harness.ts +5 -3
- package/src/telemetry/config-setting-snapshot.ts +44 -10
- package/src/telemetry/telemetry-event-sources.test.ts +124 -30
- package/src/telemetry/telemetry-event-sources.ts +137 -83
- package/src/telemetry/telemetry-events-outbox.test.ts +46 -1
- package/src/telemetry/telemetry-events-outbox.ts +60 -9
- package/src/telemetry/telemetry-wire-source.json +1 -1
- package/src/telemetry/telemetry-wire-validation.ts +39 -2
- package/src/telemetry/telemetry-wire.generated.ts +8 -0
- package/src/telemetry/tool-audit.ts +15 -9
- package/src/telemetry/tool-executed-events-store.test.ts +1 -1
- package/src/telemetry/turn-events-store.ts +20 -0
- package/src/telemetry/types.ts +45 -12
- package/src/telemetry/usage-telemetry-reporter.test.ts +294 -25
- package/src/telemetry/usage-telemetry-reporter.ts +117 -18
- package/src/telemetry/watchdog-direct-emit.test.ts +11 -3
- package/src/telemetry/watchdog-direct-emit.ts +11 -6
- package/src/tools/apps/executors.ts +11 -36
- package/src/tools/executor.ts +14 -1
- package/src/tools/host-terminal/host-shell.ts +6 -2
- package/src/tools/network/__tests__/web-fetch-firecrawl.test.ts +1 -1
- package/src/tools/network/__tests__/web-fetch-metadata.test.ts +25 -0
- package/src/tools/network/web-fetch.ts +6 -2
- package/src/tools/skills/sandbox-runner.ts +5 -2
- package/src/tools/subagent/spawn.ts +7 -11
- package/src/tools/terminal/shell.ts +5 -2
- package/src/tools/tool-manifest.ts +0 -2
- package/src/tools/tool-profiler.ts +37 -6
- package/src/tools/ui-surface/channel-variants.ts +101 -0
- package/src/tools/ui-surface/definitions.ts +23 -67
- package/src/tools/ui-surface/surface-shape-docs.ts +229 -0
- package/src/tts/__tests__/provider-adapters.test.ts +18 -0
- package/src/tts/provider-catalog.ts +2 -4
- package/src/tts/providers/deepgram-provider.ts +10 -1
- package/src/types/onboarding-context.ts +8 -0
- package/src/usage/attribution.ts +18 -112
- package/src/{config/bundled-skills/messaging/tools/gmail-mime-helpers.ts → util/mime-type.ts} +4 -1
- package/src/util/provider-error-patterns.ts +7 -1
- package/src/util/worker-process.ts +105 -0
- package/src/watcher/__tests__/telemetry.test.ts +17 -4
- package/src/watcher/telemetry.ts +5 -5
- package/src/workspace/migrations/131-drop-web-fetch-mode.ts +61 -0
- package/src/workspace/migrations/registry.ts +2 -0
- package/src/workspace/provider-commit-message-generator.ts +6 -4
- package/src/__tests__/app-open-proxy.test.ts +0 -67
- package/src/onboarding/onboarding-research-events-store.test.ts +0 -230
- package/src/onboarding/onboarding-research-events-store.ts +0 -104
- package/src/runtime/routes/assets/vellum-design-system.css +0 -2236
- package/src/tools/apps/definitions.ts +0 -73
- package/src/tools/apps/open-proxy.ts +0 -43
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* `provider` + `source` model to the new `provider_connection` model.
|
|
4
4
|
*
|
|
5
5
|
* Walks three locations in `llm.*` on every boot:
|
|
6
|
-
* - `llm.default` — the base
|
|
6
|
+
* - `llm.default` — the legacy raw base blob still present in older configs
|
|
7
7
|
* - `llm.profiles.*` — named alternate profiles (fast/balanced/...)
|
|
8
8
|
* - `llm.callSites.*` — per-call-site overrides with bare `provider`
|
|
9
9
|
*
|
|
@@ -17,15 +17,18 @@
|
|
|
17
17
|
* default profile and on any legacy bare-`provider` callsite override.
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
|
+
import { MANAGED_PROFILE_NAMES } from "../../config/default-profile-catalog.js";
|
|
20
21
|
import { loadRawConfig, saveRawConfig } from "../../config/loader.js";
|
|
21
22
|
import type { DrizzleDb } from "../../persistence/db-connection.js";
|
|
22
23
|
import { credentialKey } from "../../security/credential-key.js";
|
|
23
24
|
import { getLogger } from "../../util/logger.js";
|
|
25
|
+
import { isConnectionCompatibleWithModel } from "../connection-model-compat.js";
|
|
24
26
|
import { MANAGED_ROUTABLE_PROVIDERS } from "../vellum-model-routing.js";
|
|
25
27
|
import { PROVIDERS_REQUIRING_BASE_URL_AND_MODELS } from "./auth.js";
|
|
26
28
|
import {
|
|
27
29
|
createConnection,
|
|
28
30
|
getConnection,
|
|
31
|
+
listConnections,
|
|
29
32
|
seedCanonicalConnections,
|
|
30
33
|
VELLUM_MANAGED_CONNECTION_NAME,
|
|
31
34
|
} from "./connections.js";
|
|
@@ -63,7 +66,9 @@ export function runProviderConnectionsBackfill(db: DrizzleDb): void {
|
|
|
63
66
|
function backfillConfigProfiles(db: DrizzleDb): void {
|
|
64
67
|
const raw = loadRawConfig();
|
|
65
68
|
const llm = raw.llm as Record<string, unknown> | undefined;
|
|
66
|
-
if (!llm)
|
|
69
|
+
if (!llm) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
67
72
|
|
|
68
73
|
const isPlatform =
|
|
69
74
|
process.env.IS_PLATFORM === "true" || process.env.IS_PLATFORM === "1";
|
|
@@ -87,13 +92,17 @@ function backfillConfigProfiles(db: DrizzleDb): void {
|
|
|
87
92
|
if (profiles && typeof profiles === "object") {
|
|
88
93
|
for (const [profileName, profileVal] of Object.entries(profiles)) {
|
|
89
94
|
const profile = profileVal as Record<string, unknown>;
|
|
90
|
-
if (!profile || typeof profile !== "object")
|
|
95
|
+
if (!profile || typeof profile !== "object") {
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
91
98
|
if (ensureProviderConnection(profile, profileName, db, globalMode)) {
|
|
92
99
|
profiles[profileName] = profile;
|
|
93
100
|
changed = true;
|
|
94
101
|
}
|
|
95
102
|
}
|
|
96
|
-
if (changed)
|
|
103
|
+
if (changed) {
|
|
104
|
+
llm.profiles = profiles;
|
|
105
|
+
}
|
|
97
106
|
}
|
|
98
107
|
|
|
99
108
|
// 3. Per-call-site overrides. Only legacy entries with a bare `provider`
|
|
@@ -103,11 +112,15 @@ function backfillConfigProfiles(db: DrizzleDb): void {
|
|
|
103
112
|
if (callSites && typeof callSites === "object") {
|
|
104
113
|
for (const [callSiteName, callSiteVal] of Object.entries(callSites)) {
|
|
105
114
|
const callSite = callSiteVal as Record<string, unknown>;
|
|
106
|
-
if (!callSite || typeof callSite !== "object")
|
|
115
|
+
if (!callSite || typeof callSite !== "object") {
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
107
118
|
// Only touch overrides that explicitly set `provider` — the typical
|
|
108
119
|
// case is `{profile: "fast"}`, which has no provider and inherits
|
|
109
120
|
// through `resolveCallSiteConfig` deep-merge.
|
|
110
|
-
if (callSite.provider == null)
|
|
121
|
+
if (callSite.provider == null) {
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
111
124
|
if (
|
|
112
125
|
ensureProviderConnection(
|
|
113
126
|
callSite,
|
|
@@ -120,7 +133,9 @@ function backfillConfigProfiles(db: DrizzleDb): void {
|
|
|
120
133
|
changed = true;
|
|
121
134
|
}
|
|
122
135
|
}
|
|
123
|
-
if (changed)
|
|
136
|
+
if (changed) {
|
|
137
|
+
llm.callSites = callSites;
|
|
138
|
+
}
|
|
124
139
|
}
|
|
125
140
|
|
|
126
141
|
if (changed) {
|
|
@@ -152,10 +167,14 @@ function ensureProviderConnection(
|
|
|
152
167
|
// at runtime. Self-heal those alongside null/undefined.
|
|
153
168
|
const existing = entry.provider_connection;
|
|
154
169
|
const hasValid = typeof existing === "string" && existing.trim() !== "";
|
|
155
|
-
if (hasValid)
|
|
170
|
+
if (hasValid) {
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
156
173
|
|
|
157
174
|
const provider = entry.provider as string | undefined;
|
|
158
|
-
if (!provider)
|
|
175
|
+
if (!provider) {
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
159
178
|
|
|
160
179
|
if (PROVIDERS_REQUIRING_BASE_URL_AND_MODELS.has(provider)) {
|
|
161
180
|
log.warn(
|
|
@@ -167,7 +186,39 @@ function ensureProviderConnection(
|
|
|
167
186
|
|
|
168
187
|
let connectionName: string;
|
|
169
188
|
|
|
170
|
-
|
|
189
|
+
// For user-owned entries, an existing connection for the entry's provider
|
|
190
|
+
// wins over the mode-derived default. Only user-brought connections can
|
|
191
|
+
// match — the canonical `vellum` row carries the `vellum` sentinel
|
|
192
|
+
// provider, never a concrete upstream. Without this, the managed branch
|
|
193
|
+
// would silently switch a connection-less BYOK-intent profile onto the
|
|
194
|
+
// billed managed connection, and the your-own branch would create a
|
|
195
|
+
// parallel `-personal` row (pointing at an empty credential slot) when a
|
|
196
|
+
// custom-named connection already exists.
|
|
197
|
+
//
|
|
198
|
+
// Managed-owned entries are excluded: a managed preset must stay on the
|
|
199
|
+
// platform-managed route, not start dispatching against a key the user
|
|
200
|
+
// brought for their own profiles. Managed-owned means `source: "managed"`
|
|
201
|
+
// or a canonical managed name without an explicit `source: "user"` —
|
|
202
|
+
// legacy seeders wrote canonical entries source-less, and only an explicit
|
|
203
|
+
// user source marks a shadow the user took ownership of (mirrors
|
|
204
|
+
// workspace migration 109). `entryLabel` is the profile name for the
|
|
205
|
+
// `llm.profiles.*` walk; the other walks pass bracketed labels that never
|
|
206
|
+
// collide with canonical names.
|
|
207
|
+
const isManagedOwned =
|
|
208
|
+
entry.source === "managed" ||
|
|
209
|
+
(entry.source !== "user" && MANAGED_PROFILE_NAMES.has(entryLabel));
|
|
210
|
+
const entryModel = typeof entry.model === "string" ? entry.model : undefined;
|
|
211
|
+
const existingForProvider = isManagedOwned
|
|
212
|
+
? undefined
|
|
213
|
+
: listConnections(db, { provider }).find((c) =>
|
|
214
|
+
isConnectionCompatibleWithModel(c, entryModel),
|
|
215
|
+
);
|
|
216
|
+
if (existingForProvider) {
|
|
217
|
+
connectionName = existingForProvider.name;
|
|
218
|
+
} else if (
|
|
219
|
+
globalMode === "managed" &&
|
|
220
|
+
MANAGED_ROUTABLE_PROVIDERS.has(provider)
|
|
221
|
+
) {
|
|
171
222
|
// All managed-routable providers share the single provider-agnostic
|
|
172
223
|
// `vellum` connection; the upstream is recovered per-request from the
|
|
173
224
|
// profile's `provider` field.
|
|
@@ -174,14 +174,15 @@ export async function computeConnectionAvailability(
|
|
|
174
174
|
message: `Connection "${resolvedConnectionName}" uses Vellum platform auth, which cannot serve provider "${provider}". Add an API-key connection for "${provider}" ${SETTINGS_HINT}.`,
|
|
175
175
|
};
|
|
176
176
|
case "none": {
|
|
177
|
-
// Keyless providers (catalog setupMode "keyless", e.g. ollama)
|
|
177
|
+
// Keyless providers (catalog setupMode "keyless", e.g. ollama) and
|
|
178
|
+
// openai-compatible endpoints (dual-mode: local servers are keyless)
|
|
178
179
|
// legitimately dispatch with none-auth — mirror
|
|
179
180
|
// `createAdapterFromConnection`, which only rejects none-auth for
|
|
180
181
|
// keyed catalog entries.
|
|
181
182
|
const isKeyless =
|
|
182
183
|
PROVIDER_CATALOG.find((entry) => entry.id === provider)?.setupMode ===
|
|
183
184
|
"keyless";
|
|
184
|
-
if (isKeyless) {
|
|
185
|
+
if (isKeyless || provider === "openai-compatible") {
|
|
185
186
|
return { status: "ok" };
|
|
186
187
|
}
|
|
187
188
|
return {
|
|
@@ -84,7 +84,15 @@ export async function resolveAuth(
|
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
case "none":
|
|
87
|
-
|
|
87
|
+
// Keyless endpoints still need their custom baseUrl threaded through
|
|
88
|
+
// (LM Studio / vLLM openai-compatible servers).
|
|
89
|
+
return {
|
|
90
|
+
ok: true,
|
|
91
|
+
resolved: {
|
|
92
|
+
kind: "none",
|
|
93
|
+
...(safeBaseUrl ? { baseUrl: safeBaseUrl } : {}),
|
|
94
|
+
},
|
|
95
|
+
};
|
|
88
96
|
|
|
89
97
|
case "oauth_subscription": {
|
|
90
98
|
// Extract the credential prefix from the credential key.
|
|
@@ -1945,6 +1945,43 @@ const RAW_PROVIDER_CATALOG: ProviderCatalogEntry[] = [
|
|
|
1945
1945
|
apiKeyUrl: "https://www.atlascloud.ai/console",
|
|
1946
1946
|
apiKeyPlaceholder: "apikey-...",
|
|
1947
1947
|
},
|
|
1948
|
+
{
|
|
1949
|
+
id: "baseten",
|
|
1950
|
+
displayName: "Baseten",
|
|
1951
|
+
subtitle: "Open models served by Baseten. Requires a Baseten API key.",
|
|
1952
|
+
setupMode: "api-key",
|
|
1953
|
+
setupHint: "Enter your Baseten API key to enable Baseten models.",
|
|
1954
|
+
envVar: "BASETEN_API_KEY",
|
|
1955
|
+
credentialsGuide: {
|
|
1956
|
+
description: "Sign in to the Baseten dashboard and create an API key.",
|
|
1957
|
+
url: "https://app.baseten.co/settings/api_keys",
|
|
1958
|
+
linkLabel: "Open Baseten Dashboard",
|
|
1959
|
+
},
|
|
1960
|
+
models: [
|
|
1961
|
+
{
|
|
1962
|
+
id: "thinkingmachines/inkling",
|
|
1963
|
+
displayName: "Inkling",
|
|
1964
|
+
// Baseten serves Inkling with a 1,048K-token input window.
|
|
1965
|
+
contextWindowTokens: 1048576,
|
|
1966
|
+
maxOutputTokens: 32768,
|
|
1967
|
+
supportsThinking: true,
|
|
1968
|
+
supportsCaching: true,
|
|
1969
|
+
supportsVision: true,
|
|
1970
|
+
supportsToolUse: true,
|
|
1971
|
+
// Baseten's reasoning_effort for Inkling tops out at "xhigh" (no
|
|
1972
|
+
// "max"), matching the chat-completions client's default ceiling.
|
|
1973
|
+
maxEffort: "xhigh",
|
|
1974
|
+
pricing: {
|
|
1975
|
+
inputPer1mTokens: 1.0,
|
|
1976
|
+
outputPer1mTokens: 4.05,
|
|
1977
|
+
cacheReadPer1mTokens: 0.17,
|
|
1978
|
+
},
|
|
1979
|
+
},
|
|
1980
|
+
],
|
|
1981
|
+
defaultModel: "thinkingmachines/inkling",
|
|
1982
|
+
apiKeyUrl: "https://app.baseten.co/settings/api_keys",
|
|
1983
|
+
apiKeyPlaceholder: "Your Baseten API key",
|
|
1984
|
+
},
|
|
1948
1985
|
];
|
|
1949
1986
|
|
|
1950
1987
|
export const PROVIDER_CATALOG: ProviderCatalogEntry[] =
|
|
@@ -322,7 +322,9 @@ describe("captureRawErrorBodyFetch", () => {
|
|
|
322
322
|
});
|
|
323
323
|
});
|
|
324
324
|
|
|
325
|
-
function n(
|
|
325
|
+
function n(
|
|
326
|
+
over: Partial<NormalizedOpenAIAPIError> = {},
|
|
327
|
+
): NormalizedOpenAIAPIError {
|
|
326
328
|
return { message: "boom", ...over };
|
|
327
329
|
}
|
|
328
330
|
|
|
@@ -389,6 +391,24 @@ describe("deriveReason", () => {
|
|
|
389
391
|
);
|
|
390
392
|
});
|
|
391
393
|
|
|
394
|
+
test("403 spend-cap 'Key limit exceeded' → insufficient_credits", () => {
|
|
395
|
+
// OpenRouter returns 403 with this body when a key's configured credit
|
|
396
|
+
// limit is reached — a billing condition, not a rejected key. The billing
|
|
397
|
+
// gate precedes the 401/403 credential branch, so it routes to credits.
|
|
398
|
+
expect(deriveReason(n({ message: "Key limit exceeded" }), 403)).toBe(
|
|
399
|
+
"insufficient_credits",
|
|
400
|
+
);
|
|
401
|
+
expect(
|
|
402
|
+
deriveReason(
|
|
403
|
+
n({
|
|
404
|
+
message: "Forbidden",
|
|
405
|
+
rawBody: '{"error":{"code":403,"message":"Key limit exceeded"}}',
|
|
406
|
+
}),
|
|
407
|
+
403,
|
|
408
|
+
),
|
|
409
|
+
).toBe("insufficient_credits");
|
|
410
|
+
});
|
|
411
|
+
|
|
392
412
|
test("429 → rate_limited", () => {
|
|
393
413
|
expect(deriveReason(n(), 429)).toBe("rate_limited");
|
|
394
414
|
});
|
|
@@ -414,6 +434,8 @@ describe("deriveReason", () => {
|
|
|
414
434
|
});
|
|
415
435
|
|
|
416
436
|
test("no status, no signal → unknown", () => {
|
|
417
|
-
expect(deriveReason(n({ message: "who knows" }), undefined)).toBe(
|
|
437
|
+
expect(deriveReason(n({ message: "who knows" }), undefined)).toBe(
|
|
438
|
+
"unknown",
|
|
439
|
+
);
|
|
418
440
|
});
|
|
419
441
|
});
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import OpenAI from "openai";
|
|
2
|
+
|
|
3
|
+
import { getLogger } from "../../util/logger.js";
|
|
4
|
+
|
|
5
|
+
const log = getLogger("provider-key-validation");
|
|
6
|
+
|
|
7
|
+
/** Validation-specific timeout (10s) so a stalled network doesn't block key submission. */
|
|
8
|
+
const VALIDATION_TIMEOUT_MS = 10_000;
|
|
9
|
+
|
|
10
|
+
export type ApiKeyValidationResult =
|
|
11
|
+
| { valid: true }
|
|
12
|
+
| { valid: false; reason: string };
|
|
13
|
+
|
|
14
|
+
export interface OpenAICompatibleKeyValidation {
|
|
15
|
+
apiKey: string;
|
|
16
|
+
/** The provider's OpenAI-compatible endpoint to list models against. */
|
|
17
|
+
baseURL: string;
|
|
18
|
+
/** Human-readable provider name used in error messages and logs. */
|
|
19
|
+
providerLabel: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Validate an API key by listing models against an OpenAI-compatible
|
|
24
|
+
* endpoint. Definitive auth failures (401/403) reject the key; transient
|
|
25
|
+
* errors (429, 5xx, network) allow the key to be stored so a flaky network
|
|
26
|
+
* doesn't block setup.
|
|
27
|
+
*/
|
|
28
|
+
export async function validateOpenAICompatibleApiKey({
|
|
29
|
+
apiKey,
|
|
30
|
+
baseURL,
|
|
31
|
+
providerLabel,
|
|
32
|
+
}: OpenAICompatibleKeyValidation): Promise<ApiKeyValidationResult> {
|
|
33
|
+
try {
|
|
34
|
+
const client = new OpenAI({
|
|
35
|
+
apiKey,
|
|
36
|
+
baseURL,
|
|
37
|
+
timeout: VALIDATION_TIMEOUT_MS,
|
|
38
|
+
maxRetries: 0,
|
|
39
|
+
});
|
|
40
|
+
await client.models.list();
|
|
41
|
+
return { valid: true };
|
|
42
|
+
} catch (error) {
|
|
43
|
+
if (error instanceof OpenAI.APIError) {
|
|
44
|
+
if (error.status === 401) {
|
|
45
|
+
return { valid: false, reason: "API key is invalid or expired." };
|
|
46
|
+
}
|
|
47
|
+
if (error.status === 403) {
|
|
48
|
+
return {
|
|
49
|
+
valid: false,
|
|
50
|
+
reason: `${providerLabel} API error (${error.status}): ${error.message}`,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
// Transient errors (429, 5xx, etc.) — allow the key to be stored.
|
|
54
|
+
log.warn(
|
|
55
|
+
{ provider: providerLabel, status: error.status },
|
|
56
|
+
"API returned a transient error during key validation — allowing key",
|
|
57
|
+
);
|
|
58
|
+
return { valid: true };
|
|
59
|
+
}
|
|
60
|
+
// Network errors — allow the key to be stored.
|
|
61
|
+
log.warn(
|
|
62
|
+
{
|
|
63
|
+
provider: providerLabel,
|
|
64
|
+
error: error instanceof Error ? error.message : String(error),
|
|
65
|
+
},
|
|
66
|
+
"Network error during key validation — allowing key",
|
|
67
|
+
);
|
|
68
|
+
return { valid: true };
|
|
69
|
+
}
|
|
70
|
+
}
|
package/src/providers/retry.ts
CHANGED
|
@@ -55,6 +55,7 @@ const EFFORT_SUPPORTED_PROVIDERS = new Set([
|
|
|
55
55
|
"vercel-ai-gateway",
|
|
56
56
|
"fireworks",
|
|
57
57
|
"together",
|
|
58
|
+
"baseten",
|
|
58
59
|
]);
|
|
59
60
|
|
|
60
61
|
// For these providers, disabling reasoning is encoded through the same effort
|
|
@@ -66,6 +67,7 @@ const DISABLED_THINKING_USES_EFFORT_PROVIDERS = new Set([
|
|
|
66
67
|
"together",
|
|
67
68
|
"openrouter",
|
|
68
69
|
"vercel-ai-gateway",
|
|
70
|
+
"baseten",
|
|
69
71
|
]);
|
|
70
72
|
|
|
71
73
|
// Whether a disabled `thinking` config must be encoded as `effort: "none"`
|
package/src/providers/types.ts
CHANGED
|
@@ -215,7 +215,8 @@ export interface SendMessageConfig {
|
|
|
215
215
|
* LLM call-site identifier. `RetryProvider` resolves
|
|
216
216
|
* provider/model/maxTokens/effort/speed/verbosity/temperature/thinking/
|
|
217
217
|
* contextWindow via `resolveCallSiteConfig(callSite, config.llm)`, falling
|
|
218
|
-
* back to
|
|
218
|
+
* back to the shipped call-site defaults when no callSite-specific entry
|
|
219
|
+
* is present.
|
|
219
220
|
*/
|
|
220
221
|
callSite?: LLMCallSite;
|
|
221
222
|
/**
|
|
@@ -433,11 +434,17 @@ export function extractOverflowTokensFromMessage(message: string): {
|
|
|
433
434
|
maxTokens?: number;
|
|
434
435
|
} {
|
|
435
436
|
const match = message.match(/(\d[\d,]*)\s*(?:tokens?\s*)?[>≥]\s*(\d[\d,]*)/i);
|
|
436
|
-
if (!match)
|
|
437
|
+
if (!match) {
|
|
438
|
+
return {};
|
|
439
|
+
}
|
|
437
440
|
const actual = parseInt(match[1].replace(/,/g, ""), 10);
|
|
438
441
|
const max = parseInt(match[2].replace(/,/g, ""), 10);
|
|
439
442
|
const out: { actualTokens?: number; maxTokens?: number } = {};
|
|
440
|
-
if (!isNaN(actual) && actual > 0)
|
|
441
|
-
|
|
443
|
+
if (!isNaN(actual) && actual > 0) {
|
|
444
|
+
out.actualTokens = actual;
|
|
445
|
+
}
|
|
446
|
+
if (!isNaN(max) && max > 0) {
|
|
447
|
+
out.maxTokens = max;
|
|
448
|
+
}
|
|
442
449
|
return out;
|
|
443
450
|
}
|
|
@@ -2,6 +2,7 @@ import { describe, expect, test } from "bun:test";
|
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
4
|
formatVellumModel,
|
|
5
|
+
getManagedUpstream,
|
|
5
6
|
MANAGED_ROUTABLE_PROVIDERS,
|
|
6
7
|
parseVellumModel,
|
|
7
8
|
} from "./vellum-model-routing.js";
|
|
@@ -38,6 +39,31 @@ describe("vellum-model-routing", () => {
|
|
|
38
39
|
expect(parseVellumModel("/foo")).toBeNull(); // empty provider
|
|
39
40
|
});
|
|
40
41
|
|
|
42
|
+
test("getManagedUpstream resolves a bare catalog id to its owner", () => {
|
|
43
|
+
expect(getManagedUpstream("claude-fable-5")).toBe("anthropic");
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test("getManagedUpstream resolves a routing string by its prefix", () => {
|
|
47
|
+
expect(
|
|
48
|
+
getManagedUpstream("fireworks/accounts/fireworks/models/minimax-m3"),
|
|
49
|
+
).toBe("fireworks");
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test("getManagedUpstream returns null for non-managed owners", () => {
|
|
53
|
+
expect(getManagedUpstream("MiniMax-M3")).toBeNull(); // minimax, not managed
|
|
54
|
+
expect(getManagedUpstream("openrouter/whatever")).toBeNull();
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test("getManagedUpstream returns null for unknown models", () => {
|
|
58
|
+
expect(getManagedUpstream("not-a-real-model")).toBeNull();
|
|
59
|
+
expect(getManagedUpstream("")).toBeNull();
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test("getManagedUpstream rejects managed-prefixed strings whose model is not cataloged under that provider", () => {
|
|
63
|
+
expect(getManagedUpstream("anthropic/definitely-not-a-model")).toBeNull();
|
|
64
|
+
expect(getManagedUpstream("openai/accounts/foo/bar")).toBeNull();
|
|
65
|
+
});
|
|
66
|
+
|
|
41
67
|
test("managed set matches the platform proxy table", () => {
|
|
42
68
|
// Guards against drift if PLATFORM_PROVIDER_META changes.
|
|
43
69
|
expect([...MANAGED_ROUTABLE_PROVIDERS].sort()).toEqual([
|
|
@@ -14,6 +14,10 @@
|
|
|
14
14
|
* codec — no I/O, no wiring — so it can be adopted incrementally.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
+
import {
|
|
18
|
+
getCatalogProviderForModel,
|
|
19
|
+
isModelInCatalog,
|
|
20
|
+
} from "./model-catalog.js";
|
|
17
21
|
import { PLATFORM_PROVIDER_META } from "./platform-proxy/constants.js";
|
|
18
22
|
|
|
19
23
|
/**
|
|
@@ -117,3 +121,29 @@ export function parseVellumModel(
|
|
|
117
121
|
}
|
|
118
122
|
return { provider, model };
|
|
119
123
|
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Resolve the managed upstream provider for a model referenced by a
|
|
127
|
+
* `provider: "vellum"` profile. Such a profile carries no upstream of its own,
|
|
128
|
+
* so the model alone determines it: a `<provider>/<model>` routing string
|
|
129
|
+
* names the upstream directly, and a bare id resolves to its catalog owner.
|
|
130
|
+
* Returns null when the model has no managed-routable upstream (unknown id,
|
|
131
|
+
* or owned by a non-managed provider like openrouter).
|
|
132
|
+
*
|
|
133
|
+
* A decoded routing string must also name a real catalog model under its
|
|
134
|
+
* prefix: OpenRouter/Vercel native ids share the `<provider>/<model>` shape
|
|
135
|
+
* (e.g. `anthropic/…`), so an unvalidated prefix match could route an
|
|
136
|
+
* arbitrary non-managed id through the managed proxy.
|
|
137
|
+
*/
|
|
138
|
+
export function getManagedUpstream(model: string): string | null {
|
|
139
|
+
const route = parseVellumModel(model);
|
|
140
|
+
if (route) {
|
|
141
|
+
return isModelInCatalog(route.provider, route.model)
|
|
142
|
+
? route.provider
|
|
143
|
+
: null;
|
|
144
|
+
}
|
|
145
|
+
const owner = getCatalogProviderForModel(model);
|
|
146
|
+
return owner !== undefined && MANAGED_ROUTABLE_PROVIDERS.has(owner)
|
|
147
|
+
? owner
|
|
148
|
+
: null;
|
|
149
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single source of truth for user- and model-facing voice (STT/TTS) failure
|
|
3
|
+
* copy. Provider adapters reject with raw upstream bodies (kept in logs only);
|
|
4
|
+
* these helpers turn a normalized error into friendly, actionable text that
|
|
5
|
+
* never leaks raw JSON or HTTP status noise into a conversation or the UI.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { SttError, SttProviderId } from "../stt/types.js";
|
|
9
|
+
import { getProviderEntry } from "./speech-to-text/provider-catalog.js";
|
|
10
|
+
|
|
11
|
+
function sttProviderLabel(providerId?: SttProviderId): string {
|
|
12
|
+
const displayName = providerId
|
|
13
|
+
? getProviderEntry(providerId)?.displayName
|
|
14
|
+
: undefined;
|
|
15
|
+
return displayName ?? "the speech-to-text provider";
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Friendly, model-actionable copy for a normalized STT failure. Names the
|
|
20
|
+
* provider (when known) so the model can point the user at the right fix.
|
|
21
|
+
*/
|
|
22
|
+
export function describeSttFailure(
|
|
23
|
+
err: SttError,
|
|
24
|
+
providerId?: SttProviderId,
|
|
25
|
+
): string {
|
|
26
|
+
const provider = sttProviderLabel(providerId);
|
|
27
|
+
switch (err.category) {
|
|
28
|
+
case "auth":
|
|
29
|
+
return `${provider} rejected the configured API key — it may be expired or invalid. Check the speech-to-text API key in Settings → Voice.`;
|
|
30
|
+
case "rate-limit":
|
|
31
|
+
return `${provider} is rate-limiting transcription requests right now. Try again shortly.`;
|
|
32
|
+
case "timeout":
|
|
33
|
+
return "Transcription timed out.";
|
|
34
|
+
case "invalid-audio":
|
|
35
|
+
return "The audio could not be transcribed (unsupported or corrupted format).";
|
|
36
|
+
default:
|
|
37
|
+
return `${provider} returned an error while transcribing.`;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Friendly copy for a TTS authentication failure (HTTP 401/403). Shared with
|
|
43
|
+
* the STT auth copy's "check the API key in Settings → Voice" guidance.
|
|
44
|
+
*/
|
|
45
|
+
export function describeTtsAuthFailure(providerDisplayName: string): string {
|
|
46
|
+
return `${providerDisplayName} rejected the configured API key (authentication failed) — check the ${providerDisplayName} API key in Settings → Voice.`;
|
|
47
|
+
}
|
|
@@ -70,8 +70,6 @@ import type {
|
|
|
70
70
|
import type { InterfaceId } from "../channels/types.js";
|
|
71
71
|
import { resolveEffectiveContextWindow } from "../config/llm-context-resolution.js";
|
|
72
72
|
import {
|
|
73
|
-
isOverrideOrDefaultResolutionEnabled,
|
|
74
|
-
resolveDefaultProfileKey,
|
|
75
73
|
resolveProfilelessModelKey,
|
|
76
74
|
selectWinningProfile,
|
|
77
75
|
} from "../config/llm-resolver.js";
|
|
@@ -293,6 +291,14 @@ export interface WakeOptions {
|
|
|
293
291
|
* absent.
|
|
294
292
|
*/
|
|
295
293
|
toolContextPin?: WakeToolContextPin;
|
|
294
|
+
/**
|
|
295
|
+
* Skill IDs to preactivate for the wake so their bundled tools join the
|
|
296
|
+
* turn's active set (`allowedToolNames`) without a prior `skill_load`.
|
|
297
|
+
* Applied and restored alongside `allowedTools`; ignored when `allowedTools`
|
|
298
|
+
* is absent. Used by fork-based memory retrospectives to make the
|
|
299
|
+
* skill-management authoring tools callable directly.
|
|
300
|
+
*/
|
|
301
|
+
preactivateSkillIds?: readonly string[];
|
|
296
302
|
/**
|
|
297
303
|
* Explicit persona/channel slugs for the wake's system-prompt build,
|
|
298
304
|
* applied to the conversation for the duration of the run and restored
|
|
@@ -439,7 +445,9 @@ async function defaultResolveTarget(
|
|
|
439
445
|
await import("../daemon/conversation-store.js");
|
|
440
446
|
try {
|
|
441
447
|
const existing = getConversation(conversationId);
|
|
442
|
-
if (!existing)
|
|
448
|
+
if (!existing) {
|
|
449
|
+
return null;
|
|
450
|
+
}
|
|
443
451
|
if (existing.archivedAt != null) {
|
|
444
452
|
log.info(
|
|
445
453
|
{ conversationId },
|
|
@@ -589,7 +597,9 @@ function inspectWakeOutput(
|
|
|
589
597
|
let hasVisibleText = false;
|
|
590
598
|
const toolUseNames: string[] = [];
|
|
591
599
|
for (const msg of tailMessages) {
|
|
592
|
-
if (msg.role !== "assistant")
|
|
600
|
+
if (msg.role !== "assistant") {
|
|
601
|
+
continue;
|
|
602
|
+
}
|
|
593
603
|
const blocks = Array.isArray(msg.content) ? msg.content : [];
|
|
594
604
|
for (const block of blocks) {
|
|
595
605
|
if (block.type === "text" && typeof block.text === "string") {
|
|
@@ -749,21 +759,14 @@ export async function wakeAgentForOpportunity(
|
|
|
749
759
|
overrideProfile,
|
|
750
760
|
forceOverrideProfile,
|
|
751
761
|
});
|
|
752
|
-
// Same winner-selection sourcing as the agent loop's key:
|
|
753
|
-
//
|
|
754
|
-
//
|
|
762
|
+
// Same winner-selection sourcing as the agent loop's key: a hand-mirrored
|
|
763
|
+
// chain would disagree with dispatch (a non-forced override wins on every
|
|
764
|
+
// call site).
|
|
755
765
|
const modelProfileKey =
|
|
756
|
-
(
|
|
757
|
-
?
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
}).profileName
|
|
761
|
-
: ((forceOverrideProfile || callSite === "mainAgent"
|
|
762
|
-
? overrideProfile
|
|
763
|
-
: undefined) ??
|
|
764
|
-
resolveDefaultProfileKey(callSite, config.llm) ??
|
|
765
|
-
overrideProfile ??
|
|
766
|
-
resolveDefaultProfileKey("mainAgent", config.llm))) ??
|
|
766
|
+
selectWinningProfile(callSite, config.llm, {
|
|
767
|
+
...(overrideProfile != null ? { overrideProfile } : {}),
|
|
768
|
+
selectionSeed: conversationId,
|
|
769
|
+
}).profileName ??
|
|
767
770
|
resolveProfilelessModelKey(callSite, config.llm, {
|
|
768
771
|
...(overrideProfile != null ? { overrideProfile } : {}),
|
|
769
772
|
...(forceOverrideProfile ? { forceOverrideProfile: true } : {}),
|
|
@@ -1123,7 +1126,9 @@ export async function wakeAgentForOpportunity(
|
|
|
1123
1126
|
// and renames `text_delta` → `assistant_text_delta`; bypassing it
|
|
1124
1127
|
// would ship malformed wire frames.
|
|
1125
1128
|
const goLive = (currentHistory: Message[]): void => {
|
|
1126
|
-
if (mode === "live")
|
|
1129
|
+
if (mode === "live") {
|
|
1130
|
+
return;
|
|
1131
|
+
}
|
|
1127
1132
|
if (!surfaceInjected) {
|
|
1128
1133
|
if (!opts.suppressWakeSurface) {
|
|
1129
1134
|
const tailStart = baselineLength + wakeHintMessageCount;
|
|
@@ -1186,7 +1191,9 @@ export async function wakeAgentForOpportunity(
|
|
|
1186
1191
|
currentHistory: Message[],
|
|
1187
1192
|
): Promise<void> => {
|
|
1188
1193
|
const start = baselineLength + wakeHintMessageCount + persistedTailIndex;
|
|
1189
|
-
if (start >= currentHistory.length)
|
|
1194
|
+
if (start >= currentHistory.length) {
|
|
1195
|
+
return;
|
|
1196
|
+
}
|
|
1190
1197
|
const newMessages = currentHistory.slice(start);
|
|
1191
1198
|
for (const msg of newMessages) {
|
|
1192
1199
|
conversation.messages.push(msg);
|
|
@@ -1207,9 +1214,13 @@ export async function wakeAgentForOpportunity(
|
|
|
1207
1214
|
let wakeToolScopeRestored = false;
|
|
1208
1215
|
let restoreWakeToolScope: (() => void) | null = null;
|
|
1209
1216
|
const restoreWakeAllowedTools = (): void => {
|
|
1210
|
-
if (wakeToolScopeRestored)
|
|
1217
|
+
if (wakeToolScopeRestored) {
|
|
1218
|
+
return;
|
|
1219
|
+
}
|
|
1211
1220
|
wakeToolScopeRestored = true;
|
|
1212
|
-
if (!restoreWakeToolScope)
|
|
1221
|
+
if (!restoreWakeToolScope) {
|
|
1222
|
+
return;
|
|
1223
|
+
}
|
|
1213
1224
|
try {
|
|
1214
1225
|
restoreWakeToolScope();
|
|
1215
1226
|
} catch (err) {
|
|
@@ -1220,13 +1231,16 @@ export async function wakeAgentForOpportunity(
|
|
|
1220
1231
|
}
|
|
1221
1232
|
};
|
|
1222
1233
|
const applyWakeAllowedTools = (): boolean => {
|
|
1223
|
-
if (!opts.allowedTools)
|
|
1234
|
+
if (!opts.allowedTools) {
|
|
1235
|
+
return true;
|
|
1236
|
+
}
|
|
1224
1237
|
try {
|
|
1225
1238
|
restoreWakeToolScope = scopeWakeAllowedTools(
|
|
1226
1239
|
conversation,
|
|
1227
1240
|
new Set(opts.allowedTools),
|
|
1228
1241
|
opts.toolGateMode,
|
|
1229
1242
|
opts.toolContextPin,
|
|
1243
|
+
opts.preactivateSkillIds,
|
|
1230
1244
|
);
|
|
1231
1245
|
return true;
|
|
1232
1246
|
} catch (err) {
|
|
@@ -1342,7 +1356,9 @@ export async function wakeAgentForOpportunity(
|
|
|
1342
1356
|
const priorTurnTrust = conversation.currentTurnTrustContext;
|
|
1343
1357
|
conversation.currentCallSite = callSite;
|
|
1344
1358
|
conversation.currentTurnOverrideProfile = overrideProfile;
|
|
1345
|
-
if (opts.clientless)
|
|
1359
|
+
if (opts.clientless) {
|
|
1360
|
+
conversation.hasNoClient = true;
|
|
1361
|
+
}
|
|
1346
1362
|
// Per-turn guardian elevation for the wake's tools, set after the pre-run
|
|
1347
1363
|
// reads so a pre-run failure can't leak it; restored in the finally.
|
|
1348
1364
|
if (opts.trustContext) {
|