@vellumai/assistant 0.8.1 → 0.8.3
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 +13 -19
- package/Dockerfile +75 -1
- package/bun.lock +11 -1
- package/docker-entrypoint.sh +17 -0
- package/docker-init-apt-root.sh +167 -0
- package/docker-kata-apt-env.sh +39 -0
- package/docs/plugins.md +88 -47
- package/docs/skills.md +9 -7
- package/examples/plugins/echo/README.md +27 -27
- package/examples/plugins/echo/package.json +3 -0
- package/examples/plugins/echo/register.ts +31 -31
- package/node_modules/@vellumai/slack-text/src/index.test.ts +114 -14
- package/node_modules/@vellumai/slack-text/src/index.ts +82 -18
- package/openapi.yaml +642 -5
- package/package.json +3 -1
- package/scripts/generate-openapi.ts +83 -10
- package/scripts/sync-llm-catalog.ts +2 -2
- package/scripts/sync-web-search-catalog.ts +47 -25
- package/src/__tests__/agent-image-optimize.test.ts +11 -3
- package/src/__tests__/agent-loop-exit-reason.test.ts +272 -0
- package/src/__tests__/agent-loop-provider-error-recording.test.ts +195 -0
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +131 -0
- package/src/__tests__/anthropic-provider.test.ts +45 -0
- package/src/__tests__/app-builder-tool-scripts.test.ts +9 -3
- package/src/__tests__/app-executors.test.ts +220 -4
- package/src/__tests__/auto-analysis-end-to-end.test.ts +35 -0
- package/src/__tests__/bundled-asset.test.ts +6 -6
- package/src/__tests__/channel-availability-routes.test.ts +206 -0
- package/src/__tests__/channel-delivery-store.test.ts +289 -1
- package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -1
- package/src/__tests__/clawhub.test.ts +75 -16
- package/src/__tests__/compactor-tail-resolution.test.ts +147 -0
- package/src/__tests__/config-get-vision-flag.test.ts +136 -0
- package/src/__tests__/config-loader-backfill.test.ts +115 -18
- package/src/__tests__/config-schema.test.ts +21 -0
- package/src/__tests__/config-set-route.test.ts +80 -0
- package/src/__tests__/config-sounds-sync.test.ts +97 -0
- package/src/__tests__/config-watcher-skill-reseed.test.ts +453 -0
- package/src/__tests__/context-search-conversations-source.test.ts +117 -2
- package/src/__tests__/context-search-memory-v2-source.test.ts +0 -1
- package/src/__tests__/context-search-workspace-source.test.ts +7 -0
- package/src/__tests__/context-token-estimator.test.ts +31 -65
- package/src/__tests__/conversation-abort-tool-results.test.ts +4 -1
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +92 -92
- package/src/__tests__/conversation-agent-loop.test.ts +59 -1
- package/src/__tests__/conversation-error.test.ts +42 -3
- package/src/__tests__/conversation-fork-crud.test.ts +82 -0
- package/src/__tests__/conversation-inference-profile-route.test.ts +40 -4
- package/src/__tests__/conversation-lifecycle.test.ts +173 -0
- package/src/__tests__/conversation-media-retry.test.ts +19 -8
- package/src/__tests__/conversation-message-sync-tags.test.ts +97 -0
- package/src/__tests__/conversation-pairing.test.ts +54 -0
- package/src/__tests__/conversation-process-callsite.test.ts +4 -1
- package/src/__tests__/conversation-provider-retry-repair.test.ts +5 -1
- package/src/__tests__/conversation-queue.test.ts +4 -1
- package/src/__tests__/conversation-runtime-assembly.test.ts +102 -13
- package/src/__tests__/conversation-slash-queue.test.ts +59 -1
- package/src/__tests__/conversation-slash-unknown.test.ts +4 -1
- package/src/__tests__/conversation-surfaces-table-action.test.ts +360 -0
- package/src/__tests__/conversation-sync-tags.test.ts +235 -0
- package/src/__tests__/conversation-workspace-injection.test.ts +5 -1
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -1
- package/src/__tests__/credential-security-invariants.test.ts +3 -2
- package/src/__tests__/date-context.test.ts +45 -0
- package/src/__tests__/db-slack-external-content-normalization.test.ts +301 -0
- package/src/__tests__/delete-managed-skill-tool.test.ts +55 -13
- package/src/__tests__/disk-pressure-tools.test.ts +1 -0
- package/src/__tests__/dm-backfill.test.ts +121 -10
- package/src/__tests__/document-tool-security.test.ts +258 -0
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +0 -1
- package/src/__tests__/edit-propagation.test.ts +33 -0
- package/src/__tests__/empty-response-pipeline.test.ts +0 -4
- package/src/__tests__/external-plugin-loader.test.ts +151 -55
- package/src/__tests__/filing-service.test.ts +140 -0
- package/src/__tests__/get-skill-detail-audit.test.ts +0 -4
- package/src/__tests__/guardian-action-no-hardcoded-copy.test.ts +0 -1
- package/src/__tests__/guardian-dispatch.test.ts +1 -0
- package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +43 -62
- package/src/__tests__/heartbeat-service.test.ts +24 -164
- package/src/__tests__/helpers/channel-test-adapter.ts +0 -2
- package/src/__tests__/helpers/tar-fixtures.ts +39 -0
- package/src/__tests__/helpers/wait-for.ts +21 -0
- package/src/__tests__/history-repair-pipeline.test.ts +0 -3
- package/src/__tests__/history-repair.test.ts +73 -0
- package/src/__tests__/host-app-control-proxy.test.ts +507 -10
- package/src/__tests__/host-proxy-preactivation.test.ts +200 -13
- package/src/__tests__/image-credentials.test.ts +1 -1
- package/src/__tests__/inbound-slack-persistence.test.ts +2 -0
- package/src/__tests__/inference-no-mode-boot-e2e.test.ts +1 -1
- package/src/__tests__/inference-profile-reaper.test.ts +4 -2
- package/src/__tests__/inference-profile-session-handler.test.ts +18 -6
- package/src/__tests__/inference-profile-session-ipc.test.ts +17 -5
- package/src/__tests__/injector-background-turn.test.ts +153 -0
- package/src/__tests__/injector-chain.test.ts +15 -8
- package/src/__tests__/install-skill-routing.test.ts +155 -37
- package/src/__tests__/lifecycle-memory-v2-seed.test.ts +99 -3
- package/src/__tests__/list-messages-page-latest.test.ts +55 -0
- package/src/__tests__/llm-call-pipeline.test.ts +0 -3
- package/src/__tests__/llm-callsite-catalog.test.ts +25 -0
- package/src/__tests__/llm-catalog-parity.test.ts +58 -13
- package/src/__tests__/llm-request-log-agent-loop-exit-reason.test.ts +116 -0
- package/src/__tests__/llm-request-log-error-payload.test.ts +138 -0
- package/src/__tests__/llm-request-log-source-clickhouse.test.ts +36 -0
- package/src/__tests__/llm-request-log-source-factory.test.ts +29 -53
- package/src/__tests__/llm-resolver.test.ts +255 -2
- package/src/__tests__/llm-usage-store.test.ts +114 -0
- package/src/__tests__/managed-profile-guard.test.ts +41 -29
- package/src/__tests__/managed-skill-lifecycle.test.ts +109 -18
- package/src/__tests__/managed-store.test.ts +84 -192
- package/src/__tests__/media-generate-image.test.ts +1 -1
- package/src/__tests__/memory-retrieval-pipeline.test.ts +0 -2
- package/src/__tests__/messages-after-tiebreaker.test.ts +122 -0
- package/src/__tests__/notification-decision-fallback.test.ts +0 -91
- package/src/__tests__/notification-decision-strategy.test.ts +14 -31
- package/src/__tests__/notification-deep-link.test.ts +15 -0
- package/src/__tests__/notification-guardian-path.test.ts +1 -2
- package/src/__tests__/notification-platform-adapter.test.ts +5 -4
- package/src/__tests__/notification-telegram-adapter.test.ts +1 -0
- package/src/__tests__/notification-vellum-adapter.test.ts +113 -0
- package/src/__tests__/oauth-commands-routes.test.ts +168 -16
- package/src/__tests__/oauth-provider-profiles.test.ts +9 -0
- package/src/__tests__/openai-provider.test.ts +242 -3
- package/src/__tests__/openai-responses-cutover-guard.test.ts +17 -9
- package/src/__tests__/openrouter-provider-only.test.ts +51 -3
- package/src/__tests__/openrouter-token-estimation.test.ts +34 -25
- package/src/__tests__/overflow-reduce-pipeline.test.ts +0 -2
- package/src/__tests__/persistence-pipeline.test.ts +0 -2
- package/src/__tests__/{managed-proxy-context.test.ts → platform-proxy-context.test.ts} +7 -2
- package/src/__tests__/platform.test.ts +2 -0
- package/src/__tests__/plugin-api-shim.test.ts +125 -0
- package/src/__tests__/plugin-bootstrap.test.ts +10 -36
- package/src/__tests__/plugin-external-api.test.ts +68 -0
- package/src/__tests__/plugin-registry.test.ts +0 -77
- package/src/__tests__/plugin-route-contribution.test.ts +0 -1
- package/src/__tests__/plugin-skill-contribution.test.ts +0 -2
- package/src/__tests__/plugin-tool-contribution.test.ts +16 -15
- package/src/__tests__/plugin-types.test.ts +3 -13
- package/src/__tests__/process-message-background-slack.test.ts +8 -1
- package/src/__tests__/process-message-display-content.test.ts +421 -0
- package/src/__tests__/provider-catalog-visibility.test.ts +158 -0
- package/src/__tests__/provider-error-scenarios.test.ts +111 -0
- package/src/__tests__/{provider-managed-proxy-integration.test.ts → provider-platform-proxy-integration.test.ts} +33 -31
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +65 -13
- package/src/__tests__/schedule-routes.test.ts +50 -3
- package/src/__tests__/schedule-store.test.ts +94 -0
- package/src/__tests__/scheduler-reuse-conversation.test.ts +54 -7
- package/src/__tests__/schema-transforms.test.ts +20 -0
- package/src/__tests__/search-skills-unified.test.ts +0 -5
- package/src/__tests__/{secret-routes-managed-proxy.test.ts → secret-routes-platform-proxy.test.ts} +1 -1
- package/src/__tests__/server-history-render.test.ts +43 -0
- package/src/__tests__/skill-load-feature-flag.test.ts +0 -12
- package/src/__tests__/skill-load-tool.test.ts +27 -89
- package/src/__tests__/skill-memory.test.ts +23 -3
- package/src/__tests__/skills-file-content-endpoint.test.ts +9 -38
- package/src/__tests__/skills-files-catalog-fallback.test.ts +0 -3
- package/src/__tests__/skills-install-extract.test.ts +49 -38
- package/src/__tests__/skills-install-staging.test.ts +159 -0
- package/src/__tests__/skills-uninstall.test.ts +9 -41
- package/src/__tests__/skills.test.ts +51 -58
- package/src/__tests__/slack-channel-config.test.ts +9 -0
- package/src/__tests__/subagent-tool-filtering.test.ts +50 -0
- package/src/__tests__/system-prompt.test.ts +670 -63
- package/src/__tests__/terminal-tools.test.ts +28 -1
- package/src/__tests__/thread-backfill.test.ts +557 -27
- package/src/__tests__/title-generate-pipeline.test.ts +0 -13
- package/src/__tests__/token-estimate-pipeline.test.ts +0 -3
- package/src/__tests__/tool-error-pipeline.test.ts +0 -3
- package/src/__tests__/tool-execute-pipeline.test.ts +0 -5
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -1
- package/src/__tests__/tool-executor.test.ts +16 -4
- package/src/__tests__/tool-result-truncate-pipeline.test.ts +0 -12
- package/src/__tests__/turn-events-store.test.ts +256 -0
- package/src/__tests__/twilio-routes.test.ts +4 -0
- package/src/__tests__/user-plugin-loader.test.ts +0 -7
- package/src/__tests__/voice-session-bridge.test.ts +198 -0
- package/src/__tests__/web-search-catalog-parity.test.ts +32 -10
- package/src/__tests__/workspace-migration-057-repair-stale-gemini-model-ids.test.ts +115 -3
- package/src/__tests__/workspace-migration-072-seed-reply-suggestion-callsite.test.ts +50 -0
- package/src/__tests__/workspace-migration-073-repair-recall-callsite-empty-profile.test.ts +153 -0
- package/src/__tests__/workspace-migration-085-memory-v2-bm25-b-reembed-disabled-v2-pages.test.ts +220 -0
- package/src/__tests__/workspace-migration-086-revert-stale-gemini-mis-rewrites.test.ts +269 -0
- package/src/__tests__/workspace-migration-087-memory-router-balanced-profile.test.ts +228 -0
- package/src/__tests__/workspace-migration-remove-legacy-skills-index.test.ts +309 -0
- package/src/__tests__/workspace-migrations-runner.test.ts +111 -3
- package/src/a2a/__tests__/agent-card.test.ts +98 -0
- package/src/a2a/__tests__/e2e-a2a-channel.test.ts +597 -0
- package/src/a2a/__tests__/protocol-helpers.test.ts +113 -0
- package/src/a2a/__tests__/task-store.test.ts +246 -0
- package/src/a2a/agent-card.ts +58 -0
- package/src/a2a/feature-gate.ts +8 -0
- package/src/a2a/protocol-constants.ts +21 -0
- package/src/a2a/protocol-errors.ts +50 -0
- package/src/a2a/protocol-types.ts +162 -0
- package/src/a2a/task-store.ts +168 -0
- package/src/acp/resolve-agent.ts +1 -1
- package/src/agent/image-optimize.ts +13 -5
- package/src/agent/loop.ts +167 -18
- package/src/calls/voice-session-bridge.ts +61 -42
- package/src/channels/config.ts +9 -0
- package/src/channels/types.ts +122 -0
- package/src/cli/__tests__/unknown-command.test.ts +24 -0
- package/src/cli/commands/__tests__/changelog.test.ts +304 -319
- package/src/cli/{__tests__ → commands/__tests__}/notifications.test.ts +201 -28
- package/src/cli/commands/__tests__/schedules.test.ts +960 -0
- package/src/cli/commands/changelog.ts +106 -42
- package/src/cli/commands/conversations.ts +102 -17
- package/src/cli/commands/default-action.ts +10 -53
- package/src/cli/commands/notifications.ts +388 -346
- package/src/cli/commands/plugins.ts +252 -0
- package/src/cli/commands/schedules.ts +683 -0
- package/src/cli/commands/telemetry.ts +40 -0
- package/src/cli/lib/__tests__/cli-colors.test.ts +48 -0
- package/src/cli/lib/__tests__/confirm-prompt.test.ts +159 -0
- package/src/cli/lib/__tests__/install-from-github.test.ts +355 -0
- package/src/cli/lib/__tests__/list-installed-plugins.test.ts +154 -0
- package/src/cli/lib/__tests__/search-plugins.test.ts +261 -0
- package/src/cli/lib/__tests__/uninstall-plugin.test.ts +124 -0
- package/src/cli/lib/__tests__/unknown-command.test.ts +106 -0
- package/src/cli/lib/cli-colors.ts +12 -0
- package/src/cli/lib/confirm-prompt.ts +79 -0
- package/src/cli/lib/install-from-github.ts +303 -0
- package/src/cli/lib/list-installed-plugins.ts +137 -0
- package/src/cli/lib/search-plugins.ts +163 -0
- package/src/cli/lib/uninstall-plugin.ts +82 -0
- package/src/cli/lib/unknown-command.ts +111 -0
- package/src/cli/program.ts +52 -2
- package/src/config/assistant-feature-flags.ts +24 -54
- package/src/config/bundled-skills/app-builder/SKILL.md +140 -22
- package/src/config/bundled-skills/app-builder/TOOLS.json +7 -0
- package/src/config/bundled-skills/computer-use/TOOLS.json +15 -52
- package/src/config/bundled-skills/document/SKILL.md +23 -3
- package/src/config/bundled-skills/document/TOOLS.json +53 -0
- package/src/config/bundled-skills/document/tools/document-delete.ts +12 -0
- package/src/config/bundled-skills/document/tools/document-list.ts +12 -0
- package/src/config/bundled-skills/document/tools/document-read.ts +12 -0
- package/src/config/bundled-skills/phone-calls/SKILL.md +1 -1
- package/src/config/bundled-skills/skill-management/SKILL.md +2 -2
- package/src/config/bundled-skills/skill-management/TOOLS.json +7 -7
- package/src/config/bundled-tool-registry.ts +6 -0
- package/src/config/call-site-defaults.ts +105 -0
- package/src/config/feature-flag-registry.json +41 -9
- package/src/config/llm-resolver.ts +52 -1
- package/src/config/loader.ts +64 -38
- package/src/config/schema.ts +9 -10
- package/src/config/schemas/__tests__/llm-request-logs.test.ts +36 -0
- package/src/config/schemas/__tests__/memory-v2.test.ts +3 -3
- package/src/config/schemas/channels.ts +17 -0
- package/src/config/schemas/compaction.ts +28 -0
- package/src/config/schemas/conversations.ts +10 -0
- package/src/config/schemas/heartbeat.ts +23 -0
- package/src/config/schemas/llm-request-logs.ts +31 -7
- package/src/config/schemas/llm.ts +1 -0
- package/src/config/schemas/memory-retrieval.ts +18 -0
- package/src/config/schemas/memory-retrospective.ts +1 -1
- package/src/config/schemas/memory-v2.ts +4 -4
- package/src/config/schemas/memory.ts +3 -1
- package/src/config/schemas/tools.ts +14 -0
- package/src/config/seed-inference-profiles.ts +99 -29
- package/src/config/skills.ts +3 -96
- package/src/context/compactor.ts +1107 -0
- package/src/context/token-estimator.ts +34 -36
- package/src/context/window-manager.ts +197 -1520
- package/src/credential-execution/managed-catalog.ts +37 -0
- package/src/credential-health/credential-health-service.ts +280 -19
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +33 -18
- package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +138 -0
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +74 -0
- package/src/daemon/approval-generators.ts +8 -6
- package/src/daemon/config-watcher.ts +94 -31
- package/src/daemon/conversation-agent-loop-handlers.ts +78 -0
- package/src/daemon/conversation-agent-loop.ts +198 -11
- package/src/daemon/conversation-error.ts +171 -37
- package/src/daemon/conversation-lifecycle.ts +53 -40
- package/src/daemon/conversation-messaging.ts +25 -6
- package/src/daemon/conversation-process.ts +49 -12
- package/src/daemon/conversation-runtime-assembly.ts +25 -1
- package/src/daemon/conversation-slash.ts +12 -5
- package/src/daemon/conversation-store.ts +11 -4
- package/src/daemon/conversation-tool-setup.ts +39 -7
- package/src/daemon/conversation.ts +33 -8
- package/src/daemon/date-context.ts +40 -0
- package/src/daemon/external-plugins-bootstrap.ts +217 -181
- package/src/daemon/first-greeting.ts +22 -2
- package/src/daemon/guardian-action-generators.ts +1 -125
- package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +248 -0
- package/src/daemon/handlers/__tests__/config-a2a-invite.test.ts +154 -0
- package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +133 -0
- package/src/daemon/handlers/__tests__/config-a2a.test.ts +95 -0
- package/src/daemon/handlers/config-a2a.ts +289 -0
- package/src/daemon/handlers/config-model.ts +6 -5
- package/src/daemon/handlers/config-slack-channel.ts +15 -3
- package/src/daemon/handlers/conversations.ts +1 -0
- package/src/daemon/handlers/shared.ts +14 -5
- package/src/daemon/handlers/skills.ts +111 -108
- package/src/daemon/history-repair.ts +28 -1
- package/src/daemon/host-app-control-proxy.ts +153 -27
- package/src/daemon/host-proxy-preactivation.ts +85 -18
- package/src/daemon/lifecycle.ts +89 -91
- package/src/daemon/meet-host-supervisor.ts +5 -4
- package/src/daemon/memory-v2-startup.ts +85 -0
- package/src/daemon/message-protocol.ts +1 -0
- package/src/daemon/message-types/conversations.ts +25 -0
- package/src/daemon/message-types/messages.ts +61 -0
- package/src/daemon/message-types/notifications.ts +21 -0
- package/src/daemon/message-types/subagents.ts +1 -0
- package/src/daemon/message-types/sync.ts +1 -0
- package/src/daemon/pkb-reminder-builder.test.ts +11 -54
- package/src/daemon/pkb-reminder-builder.ts +5 -20
- package/src/daemon/plugin-source-watcher.ts +146 -0
- package/src/daemon/process-message.ts +24 -3
- package/src/daemon/server.ts +11 -2
- package/src/daemon/skill-memory-refresh.ts +33 -0
- package/src/daemon/wake-target-adapter.ts +2 -0
- package/src/documents/document-store.ts +221 -3
- package/src/embedded/plugin-api.ts +40 -0
- package/src/export/__tests__/transcript-formatter.test.ts +121 -0
- package/src/export/transcript-formatter.ts +54 -20
- package/src/filing/filing-service.ts +39 -0
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +135 -6
- package/src/heartbeat/heartbeat-run-store.ts +2 -1
- package/src/heartbeat/heartbeat-service.ts +73 -189
- package/src/home/__tests__/feed-types.test.ts +80 -0
- package/src/home/feed-types.ts +36 -2
- package/src/home/post-connect-feed.ts +1 -0
- package/src/index.ts +18 -1
- package/src/ipc/cli-client.ts +147 -45
- package/src/live-voice/__tests__/live-voice-stt.test.ts +57 -0
- package/src/mcp/client.ts +20 -4
- package/src/media/image-credentials.ts +3 -3
- package/src/memory/__tests__/bookmark-crud.test.ts +33 -27
- package/src/memory/__tests__/conversation-queries.test.ts +483 -0
- package/src/memory/__tests__/jobs-worker-v2-graph-trigger-embed.test.ts +113 -0
- package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +2 -50
- package/src/memory/__tests__/memory-retrospective-job.test.ts +87 -4
- package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +119 -14
- package/src/memory/__tests__/message-content.test.ts +35 -0
- package/src/memory/bookmark-crud.ts +42 -10
- package/src/memory/context-search/sources/conversations.ts +62 -2
- package/src/memory/context-search/sources/workspace.ts +4 -0
- package/src/memory/conversation-crud.ts +63 -19
- package/src/memory/conversation-queries.ts +197 -11
- package/src/memory/conversation-title-service.ts +26 -4
- package/src/memory/db-init.ts +12 -0
- package/src/memory/delivery-crud.ts +152 -5
- package/src/memory/embedding-backend.ts +4 -4
- package/src/memory/external-conversation-store.ts +66 -5
- package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +150 -12
- package/src/memory/graph/conversation-graph-memory.ts +49 -21
- package/src/memory/graph/tools.ts +9 -40
- package/src/memory/indexer.ts +34 -29
- package/src/memory/invite-store.ts +53 -0
- package/src/memory/jobs/__tests__/embed-concept-page.test.ts +73 -0
- package/src/memory/jobs/embed-concept-page.ts +20 -11
- package/src/memory/jobs-worker.ts +6 -1
- package/src/memory/llm-request-log-source-clickhouse.ts +24 -12
- package/src/memory/llm-request-log-source.ts +19 -52
- package/src/memory/llm-request-log-store.ts +92 -1
- package/src/memory/llm-usage-store.ts +125 -5
- package/src/memory/memory-retrospective-enqueue.ts +1 -20
- package/src/memory/memory-retrospective-job.ts +33 -6
- package/src/memory/memory-retrospective-startup-cleanup.ts +72 -5
- package/src/memory/message-content.ts +1 -1
- package/src/memory/migrations/109-external-conversation-bindings.ts +15 -4
- package/src/memory/migrations/229-delete-private-conversations.test.ts +38 -1
- package/src/memory/migrations/229-delete-private-conversations.ts +7 -0
- package/src/memory/migrations/247-external-conversation-binding-thread-id.ts +78 -0
- package/src/memory/migrations/248-create-onboarding-events.ts +21 -0
- package/src/memory/migrations/249-normalize-slack-external-content.ts +240 -0
- package/src/memory/migrations/250-provider-connection-base-url-and-models.ts +28 -0
- package/src/memory/migrations/251-a2a-tasks.ts +49 -0
- package/src/memory/migrations/252-llm-request-log-agent-loop-exit-reason.ts +32 -0
- package/src/memory/migrations/index.ts +9 -0
- package/src/memory/migrations/registry.ts +16 -0
- package/src/memory/onboarding-events-store.ts +106 -0
- package/src/memory/schema/a2a.ts +15 -0
- package/src/memory/schema/bookmarks.ts +0 -2
- package/src/memory/schema/calls.ts +1 -0
- package/src/memory/schema/index.ts +1 -0
- package/src/memory/schema/inference.ts +3 -3
- package/src/memory/schema/infrastructure.ts +13 -0
- package/src/memory/turn-events-store.ts +127 -2
- package/src/memory/v2/__tests__/activation-store.test.ts +25 -23
- package/src/memory/v2/__tests__/activation.test.ts +0 -8
- package/src/memory/v2/__tests__/cli-command-store.test.ts +404 -0
- package/src/memory/v2/__tests__/frontmatter-sweep.test.ts +25 -4
- package/src/memory/v2/__tests__/injection.test.ts +288 -11
- package/src/memory/v2/__tests__/migration.test.ts +87 -0
- package/src/memory/v2/__tests__/page-index.test.ts +83 -0
- package/src/memory/v2/__tests__/prompts-router.test.ts +58 -6
- package/src/memory/v2/__tests__/qdrant.test.ts +66 -3
- package/src/memory/v2/__tests__/router.test.ts +15 -0
- package/src/memory/v2/__tests__/skill-store.test.ts +387 -8
- package/src/memory/v2/__tests__/static-context.test.ts +12 -1
- package/src/memory/v2/activation-store.ts +14 -16
- package/src/memory/v2/cli-command-content.ts +19 -0
- package/src/memory/v2/cli-command-store.ts +304 -0
- package/src/memory/v2/frontmatter-sweep.ts +7 -1
- package/src/memory/v2/injection.ts +81 -26
- package/src/memory/v2/migration.ts +49 -19
- package/src/memory/v2/page-index.ts +63 -8
- package/src/memory/v2/prompts/router.ts +11 -8
- package/src/memory/v2/prompts/sweep.ts +2 -2
- package/src/memory/v2/qdrant.ts +135 -7
- package/src/memory/v2/router.ts +9 -8
- package/src/memory/v2/skill-store.ts +120 -35
- package/src/memory/v2/static-context.ts +4 -4
- package/src/memory/v2/types.ts +23 -0
- package/src/messaging/providers/a2a/__tests__/deliver.test.ts +274 -0
- package/src/messaging/providers/a2a/deliver.ts +156 -0
- package/src/messaging/providers/gmail/client.ts +9 -2
- package/src/messaging/providers/index.ts +11 -2
- package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +45 -5
- package/src/messaging/providers/slack/__tests__/download.test.ts +231 -0
- package/src/messaging/providers/slack/adapter.ts +43 -5
- package/src/messaging/providers/slack/client.ts +27 -0
- package/src/messaging/providers/slack/deep-link.ts +65 -0
- package/src/messaging/providers/slack/download.ts +104 -0
- package/src/messaging/providers/slack/message-metadata.test.ts +32 -0
- package/src/messaging/providers/slack/message-metadata.ts +27 -0
- package/src/messaging/providers/slack/render-transcript.test.ts +134 -0
- package/src/messaging/providers/slack/render-transcript.ts +69 -5
- package/src/messaging/providers/slack/types.ts +20 -1
- package/src/notifications/__tests__/broadcaster.test.ts +203 -0
- package/src/notifications/__tests__/decision-engine.test.ts +283 -0
- package/src/notifications/__tests__/deterministic-checks.test.ts +286 -0
- package/src/notifications/__tests__/emit-signal-home-feed.test.ts +1 -0
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +430 -7
- package/src/notifications/adapters/macos.ts +12 -2
- package/src/notifications/broadcaster.ts +29 -4
- package/src/notifications/conversation-pairing.ts +2 -1
- package/src/notifications/copy-composer.ts +17 -64
- package/src/notifications/decision-engine.ts +113 -45
- package/src/notifications/deterministic-checks.ts +96 -0
- package/src/notifications/emit-signal.ts +21 -1
- package/src/notifications/home-feed-side-effect.ts +138 -5
- package/src/notifications/signal.ts +3 -5
- package/src/notifications/types.ts +8 -0
- package/src/oauth/connection-resolver.ts +8 -4
- package/src/oauth/platform-connection.test.ts +43 -3
- package/src/oauth/platform-connection.ts +19 -6
- package/src/oauth/seed-providers.ts +10 -1
- package/src/permissions/checker.ts +2 -0
- package/src/permissions/ipc-risk-types.ts +1 -0
- package/src/permissions/question-prompter.test.ts +416 -0
- package/src/permissions/question-prompter.ts +294 -0
- package/src/platform/client.test.ts +1 -1
- package/src/platform/client.ts +1 -1
- package/src/plugin-api/constants.ts +26 -0
- package/src/plugin-api/index.ts +34 -1
- package/src/plugin-api/types.ts +104 -22
- package/src/plugins/defaults/circuit-breaker.ts +0 -5
- package/src/plugins/defaults/compaction.ts +0 -4
- package/src/plugins/defaults/empty-response.ts +0 -2
- package/src/plugins/defaults/history-repair.ts +0 -2
- package/src/plugins/defaults/injectors.ts +74 -22
- package/src/plugins/defaults/llm-call.ts +0 -2
- package/src/plugins/defaults/memory-retrieval.ts +0 -1
- package/src/plugins/defaults/overflow-reduce.ts +0 -1
- package/src/plugins/defaults/persistence.ts +0 -2
- package/src/plugins/defaults/title-generate.ts +0 -5
- package/src/plugins/defaults/token-estimate.ts +0 -2
- package/src/plugins/defaults/tool-error.ts +0 -7
- package/src/plugins/defaults/tool-execute.ts +0 -2
- package/src/plugins/defaults/tool-result-truncate.ts +0 -4
- package/src/plugins/ensure-plugin-api-shim.ts +96 -0
- package/src/plugins/external-api.ts +104 -0
- package/src/plugins/external-plugin-loader.ts +187 -42
- package/src/plugins/feature-gate.ts +22 -0
- package/src/plugins/pipeline.ts +37 -0
- package/src/plugins/registry.ts +48 -80
- package/src/plugins/types.ts +40 -26
- package/src/plugins/user-loader.ts +21 -2
- package/src/proactive-artifact/aux-message-injector.ts +11 -0
- package/src/proactive-artifact/job.test.ts +37 -5
- package/src/prompts/__tests__/system-prompt.test.ts +10 -43
- package/src/prompts/__tests__/task-progress-hint-section.test.ts +95 -0
- package/src/prompts/normalize-onboarding.ts +27 -0
- package/src/prompts/sections.ts +302 -0
- package/src/prompts/system-prompt.ts +63 -174
- package/src/prompts/templates/BOOTSTRAP.md +17 -1
- package/src/prompts/templates/system-sections.ts +164 -0
- package/src/providers/__tests__/inference.test.ts +24 -7
- package/src/providers/anthropic/client.ts +28 -28
- package/src/providers/call-site-routing.ts +24 -6
- package/src/providers/connection-resolution.ts +68 -11
- package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +74 -0
- package/src/providers/inference/__tests__/connections-openai-compatible.test.ts +175 -0
- package/src/providers/inference/__tests__/connections-status-label.test.ts +15 -0
- package/src/providers/inference/adapter-factory.ts +32 -6
- package/src/providers/inference/auth.ts +12 -0
- package/src/providers/inference/backfill.ts +14 -1
- package/src/providers/inference/connections.ts +159 -34
- package/src/providers/inference/resolve-auth.ts +14 -4
- package/src/providers/model-catalog.ts +249 -12
- package/src/providers/model-intents.ts +3 -3
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +235 -0
- package/src/providers/openai/chat-completions-provider.ts +169 -8
- package/src/providers/openrouter/client.ts +49 -4
- package/src/providers/{managed-proxy → platform-proxy}/constants.ts +4 -2
- package/src/providers/{managed-proxy → platform-proxy}/context.ts +3 -3
- package/src/providers/provider-availability.ts +17 -2
- package/src/providers/provider-catalog-visibility.ts +38 -0
- package/src/providers/provider-send-message.ts +27 -12
- package/src/providers/registry.ts +52 -15
- package/src/providers/retry.ts +47 -1
- package/src/runtime/__tests__/agent-wake.test.ts +152 -0
- package/src/runtime/agent-wake.ts +103 -15
- package/src/runtime/auth/route-policy.ts +21 -1
- package/src/runtime/btw-sidechain.ts +2 -0
- package/src/runtime/http-server.ts +7 -16
- package/src/runtime/http-types.ts +19 -47
- package/src/runtime/migrations/origin-mode.ts +1 -1
- package/src/runtime/pending-interactions.ts +1 -0
- package/src/runtime/routes/__tests__/bookmark-routes.test.ts +17 -0
- package/src/runtime/routes/__tests__/consolidation-routes.test.ts +258 -0
- package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +5 -1
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +172 -23
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +275 -44
- package/src/runtime/routes/__tests__/llm-call-sites-routes.test.ts +12 -0
- package/src/runtime/routes/__tests__/question-routes.test.ts +395 -0
- package/src/runtime/routes/__tests__/tts-routes.test.ts +64 -1
- package/src/runtime/routes/acp-routes-list.test.ts +143 -0
- package/src/runtime/routes/acp-routes.ts +5 -3
- package/src/runtime/routes/auth-routes.ts +1 -1
- package/src/runtime/routes/bookmark-routes.ts +5 -3
- package/src/runtime/routes/btw-routes.ts +5 -1
- package/src/runtime/routes/channel-availability-routes.ts +126 -0
- package/src/runtime/routes/consolidation-routes.ts +100 -0
- package/src/runtime/routes/conversation-cli-routes.ts +44 -3
- package/src/runtime/routes/conversation-list-routes.ts +3 -20
- package/src/runtime/routes/conversation-management-routes.ts +17 -42
- package/src/runtime/routes/conversation-query-routes.ts +99 -35
- package/src/runtime/routes/conversation-routes.ts +97 -11
- package/src/runtime/routes/documents-routes.ts +25 -86
- package/src/runtime/routes/group-routes.ts +5 -0
- package/src/runtime/routes/inbound-conversation.ts +28 -8
- package/src/runtime/routes/inbound-message-handler.ts +236 -41
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +111 -0
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +32 -1
- package/src/runtime/routes/inbound-stages/edit-intercept.ts +17 -4
- package/src/runtime/routes/index.ts +8 -0
- package/src/runtime/routes/inference-profile-session-handler.ts +17 -44
- package/src/runtime/routes/inference-profile-session-reaper.ts +7 -21
- package/src/runtime/routes/inference-provider-connection-routes.ts +199 -22
- package/src/runtime/routes/integrations/a2a.ts +235 -0
- package/src/runtime/routes/integrations/slack/share.ts +4 -52
- package/src/runtime/routes/integrations/slack/token.ts +43 -0
- package/src/runtime/routes/integrations/twilio.ts +6 -13
- package/src/runtime/routes/llm-call-sites-routes.ts +11 -1
- package/src/runtime/routes/notification-routes.ts +1 -1
- package/src/runtime/routes/oauth-commands-routes.ts +105 -15
- package/src/runtime/routes/oauth-lifecycle-routes.ts +43 -0
- package/src/runtime/routes/question-routes.ts +259 -0
- package/src/runtime/routes/rename-conversation-routes.ts +2 -33
- package/src/runtime/routes/schedule-routes.ts +4 -7
- package/src/runtime/routes/subagents-routes.ts +98 -18
- package/src/runtime/routes/telemetry-routes.ts +27 -0
- package/src/runtime/routes/tts-routes.ts +27 -2
- package/src/runtime/routes/workspace-routes.test.ts +43 -0
- package/src/runtime/routes/workspace-routes.ts +28 -0
- package/src/runtime/services/conversation-serializer.ts +39 -7
- package/src/runtime/sync/resource-sync-events.ts +93 -1
- package/src/schedule/schedule-store.ts +27 -2
- package/src/schedule/scheduler.ts +9 -1
- package/src/security/__tests__/untrusted-content.test.ts +86 -0
- package/src/security/untrusted-content.ts +93 -8
- package/src/skills/catalog-files.ts +1 -1
- package/src/skills/catalog-install.ts +233 -116
- package/src/skills/clawhub.ts +70 -13
- package/src/skills/managed-store.ts +4 -119
- package/src/skills/skillssh-registry.ts +27 -48
- package/src/subagent/manager.ts +17 -7
- package/src/telemetry/types.ts +113 -1
- package/src/telemetry/usage-telemetry-reporter.test.ts +312 -5
- package/src/telemetry/usage-telemetry-reporter.ts +113 -7
- package/src/tools/apps/executors.ts +58 -7
- package/src/tools/ask-question/ask-question-tool.test.ts +509 -0
- package/src/tools/ask-question/ask-question-tool.ts +304 -0
- package/src/tools/browser/browser-execution.ts +15 -11
- package/src/tools/computer-use/definitions.ts +3 -3
- package/src/tools/credentials/vault.ts +1 -1
- package/src/tools/document/document-tool.ts +124 -1
- package/src/tools/filesystem/edit.ts +1 -1
- package/src/tools/filesystem/list.ts +1 -1
- package/src/tools/filesystem/read.ts +1 -1
- package/src/tools/filesystem/write.ts +5 -2
- package/src/tools/host-filesystem/transfer.ts +1 -1
- package/src/tools/host-terminal/host-shell.ts +1 -1
- package/src/tools/memory/register.ts +1 -9
- package/src/tools/permission-checker.ts +1 -1
- package/src/tools/registry.ts +17 -7
- package/src/tools/schedule/create.ts +2 -2
- package/src/tools/schema-transforms.ts +7 -2
- package/src/tools/side-effects.ts +1 -0
- package/src/tools/skills/delete-managed.ts +4 -4
- package/src/tools/skills/execute.ts +1 -1
- package/src/tools/skills/scaffold-managed.ts +3 -2
- package/src/tools/subagent/notify-parent.ts +1 -1
- package/src/tools/system/request-permission.ts +2 -2
- package/src/tools/terminal/safe-env.ts +60 -1
- package/src/tools/tool-manifest.ts +2 -0
- package/src/tools/types.ts +107 -21
- package/src/tools/ui-surface/definitions.ts +6 -5
- package/src/tts/__tests__/provider-adapters.test.ts +76 -2
- package/src/tts/providers/elevenlabs-provider.ts +75 -1
- package/src/types/onboarding-context.ts +2 -0
- package/src/util/errors.ts +17 -0
- package/src/util/platform.ts +10 -0
- package/src/watcher/__tests__/engine.test.ts +22 -0
- package/src/watcher/engine.ts +6 -2
- package/src/workspace/migrations/057-repair-stale-gemini-model-ids.ts +80 -15
- package/src/workspace/migrations/072-seed-reply-suggestion-callsite.ts +35 -22
- package/src/workspace/migrations/073-repair-recall-callsite-empty-profile.ts +3 -1
- package/src/workspace/migrations/083-system-prompt-prefix-to-file.ts +191 -0
- package/src/workspace/migrations/084-remove-legacy-skills-index.ts +276 -0
- package/src/workspace/migrations/085-memory-v2-bm25-b-reembed-disabled-v2-pages.ts +137 -0
- package/src/workspace/migrations/086-revert-stale-gemini-mis-rewrites.ts +198 -0
- package/src/workspace/migrations/087-memory-router-balanced-profile.ts +91 -0
- package/src/workspace/migrations/registry.ts +10 -0
- package/src/workspace/migrations/runner.ts +39 -9
- package/src/workspace/migrations/types.ts +4 -0
- package/examples/plugins/echo/bun.lock +0 -25
- package/src/__tests__/context-window-manager.test.ts +0 -2481
- package/src/__tests__/guardian-action-conversation-turn.test.ts +0 -441
- package/src/context/__tests__/compact-prompt.test.ts +0 -63
- package/src/context/prompts/compact.md +0 -26
- package/src/memory/graph/__tests__/remember-description.test.ts +0 -55
- package/src/prompts/__tests__/build-cli-reference-section.test.ts +0 -37
- package/src/runtime/guardian-action-conversation-turn.ts +0 -99
|
@@ -60,6 +60,8 @@ export interface OpenAIChatCompletionsProviderOptions {
|
|
|
60
60
|
providerName?: string;
|
|
61
61
|
providerLabel?: string;
|
|
62
62
|
streamTimeoutMs?: number;
|
|
63
|
+
/** Provider-level request headers merged into every API request. */
|
|
64
|
+
requestHeaders?: Record<string, string>;
|
|
63
65
|
/** Extra params spread into every chat.completions.create call (e.g. reasoning). */
|
|
64
66
|
extraCreateParams?: Record<string, unknown>;
|
|
65
67
|
/** Upper bound for `reasoning_effort` sent on the wire. Defaults to "xhigh"
|
|
@@ -67,6 +69,10 @@ export interface OpenAIChatCompletionsProviderOptions {
|
|
|
67
69
|
* document `low|medium|high` (e.g. Fireworks) should set this to "high" so
|
|
68
70
|
* Vellum's `xhigh`/`max` tiers don't 4xx upstream. */
|
|
69
71
|
maxReasoningEffort?: "high" | "xhigh";
|
|
72
|
+
/** Parse `<think>...</think>` tags from the content stream into thinking
|
|
73
|
+
* blocks. MiniMax and similar providers embed reasoning inside XML-style
|
|
74
|
+
* tags in the regular content field rather than using `reasoning_content`. */
|
|
75
|
+
parseThinkTags?: boolean;
|
|
70
76
|
}
|
|
71
77
|
|
|
72
78
|
/** Map our internal effort values to OpenAI's reasoning_effort parameter.
|
|
@@ -74,7 +80,7 @@ export interface OpenAIChatCompletionsProviderOptions {
|
|
|
74
80
|
* passed through explicitly because OpenAI defaults `reasoning_effort` to
|
|
75
81
|
* "medium" when the field is omitted — the user's opt-out is only honored
|
|
76
82
|
* when we send it on the wire. */
|
|
77
|
-
const EFFORT_TO_REASONING_EFFORT: Record<
|
|
83
|
+
export const EFFORT_TO_REASONING_EFFORT: Record<
|
|
78
84
|
string,
|
|
79
85
|
NonNullable<
|
|
80
86
|
OpenAI.Chat.Completions.ChatCompletionCreateParams["reasoning_effort"]
|
|
@@ -95,6 +101,13 @@ const OPENAI_SUPPORTED_IMAGE_TYPES = new Set([
|
|
|
95
101
|
"image/webp",
|
|
96
102
|
]);
|
|
97
103
|
|
|
104
|
+
function partialTagSuffix(text: string, tag: string): number {
|
|
105
|
+
for (let len = Math.min(text.length, tag.length - 1); len > 0; len--) {
|
|
106
|
+
if (text.endsWith(tag.substring(0, len))) return len;
|
|
107
|
+
}
|
|
108
|
+
return 0;
|
|
109
|
+
}
|
|
110
|
+
|
|
98
111
|
/**
|
|
99
112
|
* OpenAI-compatible chat-completions transport.
|
|
100
113
|
*
|
|
@@ -110,6 +123,8 @@ export class OpenAIChatCompletionsProvider implements Provider {
|
|
|
110
123
|
private streamTimeoutMs: number;
|
|
111
124
|
private extraCreateParams: Record<string, unknown>;
|
|
112
125
|
private maxReasoningEffort: "high" | "xhigh";
|
|
126
|
+
private requestHeaders: Record<string, string>;
|
|
127
|
+
private parseThinkTags: boolean;
|
|
113
128
|
|
|
114
129
|
constructor(
|
|
115
130
|
apiKey: string,
|
|
@@ -126,6 +141,8 @@ export class OpenAIChatCompletionsProvider implements Provider {
|
|
|
126
141
|
this.streamTimeoutMs = options.streamTimeoutMs ?? 1_800_000;
|
|
127
142
|
this.extraCreateParams = options.extraCreateParams ?? {};
|
|
128
143
|
this.maxReasoningEffort = options.maxReasoningEffort ?? "xhigh";
|
|
144
|
+
this.requestHeaders = options.requestHeaders ?? {};
|
|
145
|
+
this.parseThinkTags = options.parseThinkTags ?? false;
|
|
129
146
|
}
|
|
130
147
|
|
|
131
148
|
async sendMessage(
|
|
@@ -159,10 +176,17 @@ export class OpenAIChatCompletionsProvider implements Provider {
|
|
|
159
176
|
params.max_completion_tokens = maxTokens;
|
|
160
177
|
}
|
|
161
178
|
|
|
179
|
+
// Subclasses (OpenRouter) may already have nested effort under
|
|
180
|
+
// `reasoning.effort` via `buildExtraCreateParams`. Skip the flat
|
|
181
|
+
// `reasoning_effort` assignment in that case to avoid sending both forms,
|
|
182
|
+
// which OpenRouter rejects on reasoning models.
|
|
183
|
+
const nestedReasoningEffort = (
|
|
184
|
+
params as { reasoning?: { effort?: unknown } }
|
|
185
|
+
).reasoning?.effort;
|
|
162
186
|
const reasoningEffort = effort
|
|
163
187
|
? EFFORT_TO_REASONING_EFFORT[effort]
|
|
164
188
|
: undefined;
|
|
165
|
-
if (reasoningEffort) {
|
|
189
|
+
if (reasoningEffort && typeof nestedReasoningEffort !== "string") {
|
|
166
190
|
params.reasoning_effort =
|
|
167
191
|
reasoningEffort === "xhigh" && this.maxReasoningEffort === "high"
|
|
168
192
|
? "high"
|
|
@@ -185,6 +209,68 @@ export class OpenAIChatCompletionsProvider implements Provider {
|
|
|
185
209
|
|
|
186
210
|
// Accumulate the response from chunks
|
|
187
211
|
let contentText = "";
|
|
212
|
+
let reasoningText = "";
|
|
213
|
+
let insideThinkBlock = false;
|
|
214
|
+
let pendingContent = "";
|
|
215
|
+
|
|
216
|
+
const flushPendingContent = (final: boolean): void => {
|
|
217
|
+
while (pendingContent.length > 0) {
|
|
218
|
+
if (insideThinkBlock) {
|
|
219
|
+
const closeIdx = pendingContent.indexOf("</think>");
|
|
220
|
+
if (closeIdx >= 0) {
|
|
221
|
+
const thinking = pendingContent.substring(0, closeIdx);
|
|
222
|
+
if (thinking) {
|
|
223
|
+
reasoningText += thinking;
|
|
224
|
+
onEvent?.({ type: "thinking_delta", thinking });
|
|
225
|
+
}
|
|
226
|
+
insideThinkBlock = false;
|
|
227
|
+
pendingContent = pendingContent.substring(
|
|
228
|
+
closeIdx + "</think>".length,
|
|
229
|
+
);
|
|
230
|
+
} else {
|
|
231
|
+
const partial = final
|
|
232
|
+
? 0
|
|
233
|
+
: partialTagSuffix(pendingContent, "</think>");
|
|
234
|
+
const safeLen = pendingContent.length - partial;
|
|
235
|
+
if (safeLen > 0) {
|
|
236
|
+
const thinking = pendingContent.substring(0, safeLen);
|
|
237
|
+
reasoningText += thinking;
|
|
238
|
+
onEvent?.({ type: "thinking_delta", thinking });
|
|
239
|
+
}
|
|
240
|
+
pendingContent =
|
|
241
|
+
partial > 0 ? pendingContent.substring(safeLen) : "";
|
|
242
|
+
break;
|
|
243
|
+
}
|
|
244
|
+
} else {
|
|
245
|
+
const openIdx = pendingContent.indexOf("<think>");
|
|
246
|
+
if (openIdx >= 0) {
|
|
247
|
+
const text = pendingContent.substring(0, openIdx);
|
|
248
|
+
if (text) {
|
|
249
|
+
contentText += text;
|
|
250
|
+
onEvent?.({ type: "text_delta", text });
|
|
251
|
+
}
|
|
252
|
+
insideThinkBlock = true;
|
|
253
|
+
pendingContent = pendingContent.substring(
|
|
254
|
+
openIdx + "<think>".length,
|
|
255
|
+
);
|
|
256
|
+
} else {
|
|
257
|
+
const partial = final
|
|
258
|
+
? 0
|
|
259
|
+
: partialTagSuffix(pendingContent, "<think>");
|
|
260
|
+
const safeLen = pendingContent.length - partial;
|
|
261
|
+
if (safeLen > 0) {
|
|
262
|
+
const t = pendingContent.substring(0, safeLen);
|
|
263
|
+
contentText += t;
|
|
264
|
+
onEvent?.({ type: "text_delta", text: t });
|
|
265
|
+
}
|
|
266
|
+
pendingContent =
|
|
267
|
+
partial > 0 ? pendingContent.substring(safeLen) : "";
|
|
268
|
+
break;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
|
|
188
274
|
const toolCallMap = new Map<
|
|
189
275
|
number,
|
|
190
276
|
{ id: string; name: string; args: string }
|
|
@@ -197,10 +283,14 @@ export class OpenAIChatCompletionsProvider implements Provider {
|
|
|
197
283
|
let cachedPromptTokens = 0;
|
|
198
284
|
|
|
199
285
|
try {
|
|
286
|
+
const requestHeaders = {
|
|
287
|
+
...this.requestHeaders,
|
|
288
|
+
...(usageAttributionHeaders ?? {}),
|
|
289
|
+
};
|
|
200
290
|
const stream = await this.client.chat.completions.create(params, {
|
|
201
291
|
signal: timeoutSignal,
|
|
202
|
-
...(
|
|
203
|
-
? { headers:
|
|
292
|
+
...(Object.keys(requestHeaders).length > 0
|
|
293
|
+
? { headers: requestHeaders }
|
|
204
294
|
: {}),
|
|
205
295
|
});
|
|
206
296
|
|
|
@@ -208,8 +298,62 @@ export class OpenAIChatCompletionsProvider implements Provider {
|
|
|
208
298
|
const choice = chunk.choices[0];
|
|
209
299
|
if (choice) {
|
|
210
300
|
if (choice.delta.content) {
|
|
211
|
-
|
|
212
|
-
|
|
301
|
+
if (this.parseThinkTags) {
|
|
302
|
+
pendingContent += choice.delta.content;
|
|
303
|
+
flushPendingContent(false);
|
|
304
|
+
} else {
|
|
305
|
+
contentText += choice.delta.content;
|
|
306
|
+
onEvent?.({ type: "text_delta", text: choice.delta.content });
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Compatibility providers disagree on the field name: Fireworks /
|
|
311
|
+
// DeepSeek / Together / Groq stream `reasoning_content`; OpenRouter
|
|
312
|
+
// (per its ChatAssistantMessage spec) streams `reasoning`, and for
|
|
313
|
+
// reasoning summaries (e.g. Kimi K2.6) also populates
|
|
314
|
+
// `delta.reasoning_details[]` (entries are `reasoning.summary`,
|
|
315
|
+
// `reasoning.text`, or opaque `reasoning.encrypted`).
|
|
316
|
+
//
|
|
317
|
+
// Kimi K2.6 mirrors the same token into BOTH `delta.reasoning` and
|
|
318
|
+
// `delta.reasoning_details[].text` per chunk — prefer details when
|
|
319
|
+
// they carry visible text, otherwise fall through to the flat
|
|
320
|
+
// field. The encrypted-only case must fall through too, so the
|
|
321
|
+
// flat `reasoning` field isn't silently dropped.
|
|
322
|
+
const deltaWithReasoning = choice.delta as {
|
|
323
|
+
reasoning?: string | null;
|
|
324
|
+
reasoning_content?: string | null;
|
|
325
|
+
reasoning_details?: Array<{
|
|
326
|
+
type?: string;
|
|
327
|
+
summary?: string | null;
|
|
328
|
+
text?: string | null;
|
|
329
|
+
}> | null;
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
let sawVisibleDetail = false;
|
|
333
|
+
const reasoningDetails = deltaWithReasoning.reasoning_details;
|
|
334
|
+
if (Array.isArray(reasoningDetails)) {
|
|
335
|
+
for (const entry of reasoningDetails) {
|
|
336
|
+
if (entry.type === "reasoning.encrypted") continue;
|
|
337
|
+
const piece = entry.summary ?? entry.text;
|
|
338
|
+
if (piece) {
|
|
339
|
+
sawVisibleDetail = true;
|
|
340
|
+
reasoningText += piece;
|
|
341
|
+
onEvent?.({ type: "thinking_delta", thinking: piece });
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
if (!sawVisibleDetail) {
|
|
347
|
+
const reasoningContent =
|
|
348
|
+
deltaWithReasoning.reasoning_content ??
|
|
349
|
+
deltaWithReasoning.reasoning;
|
|
350
|
+
if (reasoningContent) {
|
|
351
|
+
reasoningText += reasoningContent;
|
|
352
|
+
onEvent?.({
|
|
353
|
+
type: "thinking_delta",
|
|
354
|
+
thinking: reasoningContent,
|
|
355
|
+
});
|
|
356
|
+
}
|
|
213
357
|
}
|
|
214
358
|
|
|
215
359
|
if (choice.delta.tool_calls) {
|
|
@@ -252,10 +396,27 @@ export class OpenAIChatCompletionsProvider implements Provider {
|
|
|
252
396
|
cleanupTimeout();
|
|
253
397
|
}
|
|
254
398
|
|
|
399
|
+
if (this.parseThinkTags && pendingContent) {
|
|
400
|
+
flushPendingContent(true);
|
|
401
|
+
}
|
|
402
|
+
|
|
255
403
|
// Build content blocks
|
|
404
|
+
const finalReasoning = this.parseThinkTags
|
|
405
|
+
? reasoningText.trim()
|
|
406
|
+
: reasoningText;
|
|
407
|
+
const finalContent = this.parseThinkTags
|
|
408
|
+
? contentText.trim()
|
|
409
|
+
: contentText;
|
|
256
410
|
const content: ContentBlock[] = [];
|
|
257
|
-
if (
|
|
258
|
-
content.push({
|
|
411
|
+
if (finalReasoning) {
|
|
412
|
+
content.push({
|
|
413
|
+
type: "thinking",
|
|
414
|
+
thinking: finalReasoning,
|
|
415
|
+
signature: "",
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
if (finalContent) {
|
|
419
|
+
content.push({ type: "text", text: finalContent });
|
|
259
420
|
}
|
|
260
421
|
for (const [, tc] of toolCallMap) {
|
|
261
422
|
let input: Record<string, unknown>;
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { ProviderError } from "../../util/errors.js";
|
|
2
2
|
import { AnthropicProvider } from "../anthropic/client.js";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
EFFORT_TO_REASONING_EFFORT,
|
|
5
|
+
OpenAIChatCompletionsProvider,
|
|
6
|
+
} from "../openai/chat-completions-provider.js";
|
|
4
7
|
import { isThinkingConfigEnabled } from "../thinking-config.js";
|
|
5
8
|
import type {
|
|
6
9
|
Message,
|
|
@@ -18,6 +21,11 @@ export interface OpenRouterProviderOptions {
|
|
|
18
21
|
}
|
|
19
22
|
|
|
20
23
|
const DEFAULT_OPENROUTER_BASE_URL = "https://openrouter.ai/api/v1";
|
|
24
|
+
const OPENROUTER_APP_ATTRIBUTION_HEADERS = {
|
|
25
|
+
"HTTP-Referer": "https://www.vellum.ai",
|
|
26
|
+
"X-OpenRouter-Title": "Vellum Assistant",
|
|
27
|
+
"X-OpenRouter-Categories": "personal-agent,cli-agent",
|
|
28
|
+
};
|
|
21
29
|
|
|
22
30
|
// Models on OpenRouter prefixed `anthropic/` are routed through OpenRouter's
|
|
23
31
|
// Anthropic-compatible Messages API at `<root>/v1/messages` (where `<root>` is
|
|
@@ -48,6 +56,25 @@ export function extractOnlyList(config: unknown): string[] {
|
|
|
48
56
|
return only.filter((x): x is string => typeof x === "string" && x.length > 0);
|
|
49
57
|
}
|
|
50
58
|
|
|
59
|
+
// OpenRouter's `reasoning.summary` field controls whether reasoning models emit
|
|
60
|
+
// a human-readable summary alongside (or instead of) encrypted reasoning blocks.
|
|
61
|
+
// Models like Kimi K2.6 return only encrypted `reasoning_details` unless a
|
|
62
|
+
// summary level is requested, so the stream carries no visible thinking content.
|
|
63
|
+
// Default to "detailed" so users see thinking by default; allow per-call
|
|
64
|
+
// override via `config.openrouter.reasoning.summary`. Per OpenRouter's
|
|
65
|
+
// ChatRequestReasoning schema, valid values are "auto" | "concise" | "detailed".
|
|
66
|
+
const VALID_REASONING_SUMMARIES = new Set(["auto", "concise", "detailed"]);
|
|
67
|
+
|
|
68
|
+
function extractReasoningSummaryOverride(config: unknown): string | undefined {
|
|
69
|
+
const cfg = config as
|
|
70
|
+
| { openrouter?: { reasoning?: { summary?: unknown } } }
|
|
71
|
+
| undefined;
|
|
72
|
+
const summary = cfg?.openrouter?.reasoning?.summary;
|
|
73
|
+
return typeof summary === "string" && VALID_REASONING_SUMMARIES.has(summary)
|
|
74
|
+
? summary
|
|
75
|
+
: undefined;
|
|
76
|
+
}
|
|
77
|
+
|
|
51
78
|
/**
|
|
52
79
|
* Rewrite `options.config` for the Anthropic-compat path so OpenRouter's
|
|
53
80
|
* `provider: { only: [...] }` body field travels through `AnthropicProvider`'s
|
|
@@ -93,6 +120,7 @@ export class OpenRouterProvider extends OpenAIChatCompletionsProvider {
|
|
|
93
120
|
providerName: "openrouter",
|
|
94
121
|
providerLabel: "OpenRouter",
|
|
95
122
|
streamTimeoutMs: options.streamTimeoutMs,
|
|
123
|
+
requestHeaders: OPENROUTER_APP_ATTRIBUTION_HEADERS,
|
|
96
124
|
});
|
|
97
125
|
this.openRouterApiKey = apiKey;
|
|
98
126
|
this.defaultModel = model;
|
|
@@ -154,14 +182,30 @@ export class OpenRouterProvider extends OpenAIChatCompletionsProvider {
|
|
|
154
182
|
// OpenRouter's unified `reasoning` parameter controls extended thinking on
|
|
155
183
|
// its OpenAI-compatible endpoint. Anthropic models skip this path entirely and
|
|
156
184
|
// go through AnthropicProvider, which receives the native `thinking` object.
|
|
185
|
+
//
|
|
186
|
+
// `effort` nests under `reasoning` here (rather than flat `reasoning_effort`)
|
|
187
|
+
// because OpenRouter's documented `ChatRequestReasoning` shape is the union of
|
|
188
|
+
// { effort, summary }. `summary` is required for models like Kimi K2.6 that
|
|
189
|
+
// would otherwise return only encrypted reasoning blocks; we default to
|
|
190
|
+
// "detailed" and let callers override via `config.openrouter.reasoning.summary`.
|
|
157
191
|
protected override buildExtraCreateParams(
|
|
158
192
|
options?: SendMessageOptions,
|
|
159
193
|
): Record<string, unknown> {
|
|
160
194
|
const config = options?.config as Record<string, unknown> | undefined;
|
|
161
195
|
const thinkingEnabled = isThinkingConfigEnabled(config?.thinking);
|
|
162
|
-
const
|
|
163
|
-
|
|
164
|
-
|
|
196
|
+
const effort = config?.effort as string | undefined;
|
|
197
|
+
const mappedEffort = effort
|
|
198
|
+
? EFFORT_TO_REASONING_EFFORT[effort]
|
|
199
|
+
: undefined;
|
|
200
|
+
const summaryOverride = extractReasoningSummaryOverride(config);
|
|
201
|
+
const reasoning: Record<string, unknown> = { enabled: thinkingEnabled };
|
|
202
|
+
if (mappedEffort) {
|
|
203
|
+
reasoning.effort = mappedEffort;
|
|
204
|
+
}
|
|
205
|
+
if (thinkingEnabled) {
|
|
206
|
+
reasoning.summary = summaryOverride ?? "detailed";
|
|
207
|
+
}
|
|
208
|
+
const extras: Record<string, unknown> = { reasoning };
|
|
165
209
|
const only = extractOnlyList(config);
|
|
166
210
|
if (only.length > 0) {
|
|
167
211
|
const existingProvider = (config?.provider ?? {}) as Record<
|
|
@@ -192,6 +236,7 @@ export class OpenRouterProvider extends OpenAIChatCompletionsProvider {
|
|
|
192
236
|
streamTimeoutMs: this.providerStreamTimeoutMs,
|
|
193
237
|
authToken: this.openRouterApiKey,
|
|
194
238
|
useNativeWebSearch: this.useNativeWebSearch,
|
|
239
|
+
requestHeaders: OPENROUTER_APP_ATTRIBUTION_HEADERS,
|
|
195
240
|
},
|
|
196
241
|
);
|
|
197
242
|
}
|
|
@@ -25,7 +25,7 @@ export interface ManagedProviderMeta {
|
|
|
25
25
|
* managed credentials are present; that policy lives in the registry/context
|
|
26
26
|
* fallback allowlists.
|
|
27
27
|
*/
|
|
28
|
-
export const
|
|
28
|
+
export const PLATFORM_PROVIDER_META: Record<string, ManagedProviderMeta> = {
|
|
29
29
|
openai: {
|
|
30
30
|
name: "openai",
|
|
31
31
|
managed: true,
|
|
@@ -43,11 +43,13 @@ export const MANAGED_PROVIDER_META: Record<string, ManagedProviderMeta> = {
|
|
|
43
43
|
},
|
|
44
44
|
fireworks: {
|
|
45
45
|
name: "fireworks",
|
|
46
|
-
managed:
|
|
46
|
+
managed: true,
|
|
47
|
+
proxyPath: "/v1/runtime-proxy/fireworks",
|
|
47
48
|
},
|
|
48
49
|
openrouter: {
|
|
49
50
|
name: "openrouter",
|
|
50
51
|
managed: false,
|
|
51
52
|
},
|
|
52
53
|
ollama: { name: "ollama", managed: false },
|
|
54
|
+
"openai-compatible": { name: "openai-compatible", managed: false },
|
|
53
55
|
};
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
import { getPlatformBaseUrl } from "../../config/env.js";
|
|
13
13
|
import { credentialKey } from "../../security/credential-key.js";
|
|
14
14
|
import { getSecureKeyAsync } from "../../security/secure-keys.js";
|
|
15
|
-
import {
|
|
15
|
+
import { PLATFORM_PROVIDER_META } from "./constants.js";
|
|
16
16
|
|
|
17
17
|
/** Storage key for the assistant API key credential. */
|
|
18
18
|
const ASSISTANT_API_KEY_STORAGE_KEY = credentialKey(
|
|
@@ -70,7 +70,7 @@ export async function hasManagedProxyPrereqs(): Promise<boolean> {
|
|
|
70
70
|
export async function buildManagedBaseUrl(
|
|
71
71
|
provider: string,
|
|
72
72
|
): Promise<string | undefined> {
|
|
73
|
-
const meta =
|
|
73
|
+
const meta = PLATFORM_PROVIDER_META[provider];
|
|
74
74
|
if (!meta?.managed || !meta.proxyPath) return undefined;
|
|
75
75
|
|
|
76
76
|
const ctx = await resolveManagedProxyContext();
|
|
@@ -88,7 +88,7 @@ export async function buildManagedBaseUrl(
|
|
|
88
88
|
export async function managedFallbackEnabledFor(
|
|
89
89
|
provider: string,
|
|
90
90
|
): Promise<boolean> {
|
|
91
|
-
const meta =
|
|
91
|
+
const meta = PLATFORM_PROVIDER_META[provider];
|
|
92
92
|
if (!meta?.managed) return false;
|
|
93
93
|
return await hasManagedProxyPrereqs();
|
|
94
94
|
}
|
|
@@ -5,9 +5,11 @@
|
|
|
5
5
|
* environment variable fallbacks, and managed proxy availability.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { API_KEY_PROVIDERS } from "../config/loader.js";
|
|
8
|
+
import { API_KEY_PROVIDERS, getConfig } from "../config/loader.js";
|
|
9
9
|
import { getProviderKeyAsync } from "../security/secure-keys.js";
|
|
10
|
-
import {
|
|
10
|
+
import { PROVIDER_CATALOG } from "./model-catalog.js";
|
|
11
|
+
import { managedFallbackEnabledFor } from "./platform-proxy/context.js";
|
|
12
|
+
import { getVisibleProviderCatalog } from "./provider-catalog-visibility.js";
|
|
11
13
|
|
|
12
14
|
/**
|
|
13
15
|
* Check whether a single provider is usable — via a user-provided key
|
|
@@ -25,11 +27,24 @@ export async function isProviderAvailable(provider: string): Promise<boolean> {
|
|
|
25
27
|
/**
|
|
26
28
|
* Build the list of providers that are usable — via a user-provided key
|
|
27
29
|
* (secure storage or env var) or via the managed proxy fallback.
|
|
30
|
+
* Feature-flagged LLM providers that are currently disabled are excluded.
|
|
28
31
|
* Ollama is always included because it does not require an API key.
|
|
29
32
|
*/
|
|
30
33
|
export async function getConfiguredProviders(): Promise<string[]> {
|
|
34
|
+
// Build the set of LLM providers hidden by feature flags so we can
|
|
35
|
+
// exclude them while leaving non-LLM providers (search, STT, TTS)
|
|
36
|
+
// in API_KEY_PROVIDERS unchanged.
|
|
37
|
+
const allLlmIds = new Set(PROVIDER_CATALOG.map((p) => p.id));
|
|
38
|
+
const visibleLlmIds = new Set(
|
|
39
|
+
getVisibleProviderCatalog(getConfig()).map((p) => p.id),
|
|
40
|
+
);
|
|
41
|
+
const hiddenLlmIds = new Set(
|
|
42
|
+
[...allLlmIds].filter((id) => !visibleLlmIds.has(id)),
|
|
43
|
+
);
|
|
44
|
+
|
|
31
45
|
const configured: string[] = [];
|
|
32
46
|
for (const p of API_KEY_PROVIDERS) {
|
|
47
|
+
if (hiddenLlmIds.has(p)) continue;
|
|
33
48
|
if (await isProviderAvailable(p)) {
|
|
34
49
|
configured.push(p);
|
|
35
50
|
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Feature-flag-aware provider catalog filtering.
|
|
3
|
+
*
|
|
4
|
+
* User-facing catalog consumers (model info, slash commands, provider
|
|
5
|
+
* availability) use `getVisibleProviderCatalog()` to hide providers and
|
|
6
|
+
* models gated behind disabled feature flags. Internal consumers
|
|
7
|
+
* (adapter-factory, pricing, auth) continue using the unfiltered
|
|
8
|
+
* `PROVIDER_CATALOG` directly.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { isAssistantFeatureFlagEnabled } from "../config/assistant-feature-flags.js";
|
|
12
|
+
import type { AssistantConfig } from "../config/schema.js";
|
|
13
|
+
import {
|
|
14
|
+
PROVIDER_CATALOG,
|
|
15
|
+
type ProviderCatalogEntry,
|
|
16
|
+
} from "./model-catalog.js";
|
|
17
|
+
|
|
18
|
+
export function getVisibleProviderCatalog(
|
|
19
|
+
config: AssistantConfig,
|
|
20
|
+
): ProviderCatalogEntry[] {
|
|
21
|
+
return PROVIDER_CATALOG.filter(
|
|
22
|
+
(entry) =>
|
|
23
|
+
!entry.featureFlag ||
|
|
24
|
+
isAssistantFeatureFlagEnabled(entry.featureFlag, config),
|
|
25
|
+
)
|
|
26
|
+
.map((entry) => {
|
|
27
|
+
const visibleModels = entry.models.filter(
|
|
28
|
+
(m) =>
|
|
29
|
+
!m.featureFlag ||
|
|
30
|
+
isAssistantFeatureFlagEnabled(m.featureFlag, config),
|
|
31
|
+
);
|
|
32
|
+
if (visibleModels.length === entry.models.length) return entry;
|
|
33
|
+
return { ...entry, models: visibleModels };
|
|
34
|
+
})
|
|
35
|
+
.filter(
|
|
36
|
+
(entry) => entry.models.length > 0 || entry.defaultModel === "",
|
|
37
|
+
);
|
|
38
|
+
}
|
|
@@ -7,8 +7,10 @@
|
|
|
7
7
|
import { resolveCallSiteConfig } from "../config/llm-resolver.js";
|
|
8
8
|
import { getConfig } from "../config/loader.js";
|
|
9
9
|
import type { LLMCallSite } from "../config/schemas/llm.js";
|
|
10
|
+
import { getDb } from "../memory/db-connection.js";
|
|
10
11
|
import { getLogger } from "../util/logger.js";
|
|
11
12
|
import { tryResolveProviderForConnectionName } from "./connection-resolution.js";
|
|
13
|
+
import { listConnections } from "./inference/connections.js";
|
|
12
14
|
import { initializeProviders, listProviders } from "./registry.js";
|
|
13
15
|
import type {
|
|
14
16
|
ContentBlock,
|
|
@@ -110,22 +112,35 @@ export async function resolveConfiguredProvider(
|
|
|
110
112
|
|
|
111
113
|
const resolved = resolveCallSiteConfig(callSite, config.llm, opts);
|
|
112
114
|
const inferenceProvider = resolved.provider;
|
|
113
|
-
|
|
115
|
+
let connectionName = resolved.provider_connection;
|
|
114
116
|
|
|
115
117
|
// Connection-aware path: every dispatch goes through `provider_connection`.
|
|
116
118
|
// The boot-time backfill ensures every profile has one in production.
|
|
117
|
-
// When unset (
|
|
118
|
-
//
|
|
119
|
-
//
|
|
120
|
-
//
|
|
121
|
-
// Hard config errors — connection lookup failure, provider mismatch —
|
|
122
|
-
// still throw via `tryResolveProviderForConnectionName` below.
|
|
119
|
+
// When unset (profile set provider with "Any active" connection, test envs
|
|
120
|
+
// that skip backfill, freshly-installed configs not yet backfilled, or
|
|
121
|
+
// users who manually cleared the field), try to auto-resolve from the
|
|
122
|
+
// provider before falling back to null.
|
|
123
123
|
if (!connectionName) {
|
|
124
|
-
|
|
125
|
-
{
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
124
|
+
if (inferenceProvider) {
|
|
125
|
+
try {
|
|
126
|
+
const candidates = listConnections(getDb(), {
|
|
127
|
+
provider: inferenceProvider,
|
|
128
|
+
});
|
|
129
|
+
const active = candidates.find((c) => c.status === "active");
|
|
130
|
+
if (active) {
|
|
131
|
+
connectionName = active.name;
|
|
132
|
+
}
|
|
133
|
+
} catch {
|
|
134
|
+
// DB not available — fall through to the existing null-return path.
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
if (!connectionName) {
|
|
138
|
+
log.debug(
|
|
139
|
+
{ callSite, inferenceProvider },
|
|
140
|
+
"resolveCallSiteConfig yielded no provider_connection — returning null so callsite can fall back",
|
|
141
|
+
);
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
129
144
|
}
|
|
130
145
|
|
|
131
146
|
const connectionProvider = await tryResolveProviderForConnectionName(
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { isAssistantFeatureFlagEnabled } from "../config/assistant-feature-flags.js";
|
|
1
2
|
import { resolveCallSiteConfig } from "../config/llm-resolver.js";
|
|
3
|
+
import type { AssistantConfig } from "../config/schema.js";
|
|
2
4
|
import { type LLMConfig } from "../config/schemas/llm.js";
|
|
3
5
|
import { getProviderKeyAsync } from "../security/secure-keys.js";
|
|
4
6
|
import { ProviderNotConfiguredError } from "../util/errors.js";
|
|
@@ -12,12 +14,12 @@ import {
|
|
|
12
14
|
// ---------------------------------------------------------------------------
|
|
13
15
|
import type { ProviderConnection } from "./inference/auth.js";
|
|
14
16
|
import { resolveAuth } from "./inference/resolve-auth.js";
|
|
17
|
+
import { isModelInCatalog, PROVIDER_CATALOG } from "./model-catalog.js";
|
|
18
|
+
import { getProviderDefaultModel } from "./model-intents.js";
|
|
15
19
|
import {
|
|
16
20
|
buildManagedBaseUrl,
|
|
17
21
|
resolveManagedProxyContext,
|
|
18
|
-
} from "./
|
|
19
|
-
import { isModelInCatalog, PROVIDER_CATALOG } from "./model-catalog.js";
|
|
20
|
-
import { getProviderDefaultModel } from "./model-intents.js";
|
|
22
|
+
} from "./platform-proxy/context.js";
|
|
21
23
|
import { RetryProvider } from "./retry.js";
|
|
22
24
|
import type { Provider } from "./types.js";
|
|
23
25
|
import { UsageTrackingProvider } from "./usage-tracking.js";
|
|
@@ -26,6 +28,7 @@ const log = getLogger("provider-registry");
|
|
|
26
28
|
|
|
27
29
|
const providers = new Map<string, Provider>();
|
|
28
30
|
const routingSources = new Map<string, "user-key" | "managed-proxy">();
|
|
31
|
+
const OPENAI_COMPATIBLE_ENDPOINTS_FLAG = "openai-compatible-endpoints";
|
|
29
32
|
|
|
30
33
|
/** Per-connection provider cache, keyed by connection name. */
|
|
31
34
|
const connectionProviders = new Map<string, Provider>();
|
|
@@ -69,6 +72,16 @@ export interface ProvidersConfig {
|
|
|
69
72
|
timeouts?: { providerStreamTimeoutSec?: number };
|
|
70
73
|
}
|
|
71
74
|
|
|
75
|
+
function isProviderFeatureFlagEnabled(
|
|
76
|
+
key: string,
|
|
77
|
+
config: ProvidersConfig,
|
|
78
|
+
): boolean {
|
|
79
|
+
return isAssistantFeatureFlagEnabled(
|
|
80
|
+
key,
|
|
81
|
+
config as unknown as AssistantConfig,
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
72
85
|
function resolveModel(config: ProvidersConfig, providerName: string): string {
|
|
73
86
|
const resolved = resolveCallSiteConfig("mainAgent", config.llm);
|
|
74
87
|
const inferenceProvider = resolved.provider;
|
|
@@ -92,9 +105,7 @@ function resolveModel(config: ProvidersConfig, providerName: string): string {
|
|
|
92
105
|
* The routing decision is now derived from credential availability rather than
|
|
93
106
|
* the removed `services.inference.mode` config field.
|
|
94
107
|
*/
|
|
95
|
-
async function resolveProviderCredentials(
|
|
96
|
-
providerName: string,
|
|
97
|
-
): Promise<{
|
|
108
|
+
async function resolveProviderCredentials(providerName: string): Promise<{
|
|
98
109
|
apiKey: string;
|
|
99
110
|
baseURL?: string;
|
|
100
111
|
source: "user-key" | "managed-proxy";
|
|
@@ -106,7 +117,11 @@ async function resolveProviderCredentials(
|
|
|
106
117
|
const managedBaseUrl = await buildManagedBaseUrl(providerName);
|
|
107
118
|
if (managedBaseUrl) {
|
|
108
119
|
const ctx = await resolveManagedProxyContext();
|
|
109
|
-
return {
|
|
120
|
+
return {
|
|
121
|
+
apiKey: ctx.assistantApiKey,
|
|
122
|
+
baseURL: managedBaseUrl,
|
|
123
|
+
source: "managed-proxy",
|
|
124
|
+
};
|
|
110
125
|
}
|
|
111
126
|
return null;
|
|
112
127
|
}
|
|
@@ -122,10 +137,19 @@ export async function initializeProviders(
|
|
|
122
137
|
(config.timeouts?.providerStreamTimeoutSec ?? 1800) * 1000;
|
|
123
138
|
const useNativeWebSearch =
|
|
124
139
|
config.services["web-search"].provider === "inference-provider-native";
|
|
125
|
-
const mainAgentProvider = resolveCallSiteConfig(
|
|
126
|
-
|
|
140
|
+
const mainAgentProvider = resolveCallSiteConfig(
|
|
141
|
+
"mainAgent",
|
|
142
|
+
config.llm,
|
|
143
|
+
).provider;
|
|
127
144
|
|
|
128
145
|
for (const entry of PROVIDER_CATALOG) {
|
|
146
|
+
if (
|
|
147
|
+
entry.featureFlag &&
|
|
148
|
+
!isProviderFeatureFlagEnabled(entry.featureFlag, config)
|
|
149
|
+
) {
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
|
|
129
153
|
const isKeyless = entry.setupMode === "keyless";
|
|
130
154
|
|
|
131
155
|
// Credential resolution: user key first, managed proxy second. Keyless
|
|
@@ -198,10 +222,19 @@ export async function resolveProviderFromConnection(
|
|
|
198
222
|
connection: ProviderConnection,
|
|
199
223
|
config: ProvidersConfig,
|
|
200
224
|
): Promise<Provider | null> {
|
|
225
|
+
if (
|
|
226
|
+
connection.provider === "openai-compatible" &&
|
|
227
|
+
!isProviderFeatureFlagEnabled(OPENAI_COMPATIBLE_ENDPOINTS_FLAG, config)
|
|
228
|
+
) {
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
231
|
+
|
|
201
232
|
const cached = connectionProviders.get(connection.name);
|
|
202
233
|
if (cached) return cached;
|
|
203
234
|
|
|
204
|
-
const authResult = await resolveAuth(connection.auth, connection.provider
|
|
235
|
+
const authResult = await resolveAuth(connection.auth, connection.provider, {
|
|
236
|
+
baseUrl: connection.baseUrl,
|
|
237
|
+
});
|
|
205
238
|
if (!authResult.ok) {
|
|
206
239
|
const err = authResult.error;
|
|
207
240
|
if (err.code === "not_implemented") {
|
|
@@ -230,11 +263,15 @@ export async function resolveProviderFromConnection(
|
|
|
230
263
|
config.services["web-search"].provider === "inference-provider-native";
|
|
231
264
|
const model = resolveModel(config, connection.provider);
|
|
232
265
|
|
|
233
|
-
const provider = createAdapterFromConnection(
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
266
|
+
const provider = createAdapterFromConnection(
|
|
267
|
+
connection,
|
|
268
|
+
authResult.resolved,
|
|
269
|
+
{
|
|
270
|
+
model,
|
|
271
|
+
streamTimeoutMs,
|
|
272
|
+
useNativeWebSearch,
|
|
273
|
+
},
|
|
274
|
+
);
|
|
238
275
|
|
|
239
276
|
if (provider) {
|
|
240
277
|
connectionProviders.set(connection.name, provider);
|