@vellumai/assistant 0.8.3 → 0.8.5
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 +2 -2
- package/docker-entrypoint.sh +0 -1
- package/docs/browser-use-architecture-phase2.md +1 -1
- package/knip.json +2 -1
- package/node_modules/@vellumai/gateway-client/src/types.ts +2 -0
- package/openapi.yaml +1492 -100
- package/package.json +1 -1
- package/src/__tests__/agent-loop-exit-reason.test.ts +4 -5
- package/src/__tests__/agent-loop-override-profile.test.ts +1 -1
- package/src/__tests__/agent-loop.test.ts +88 -3
- package/src/__tests__/anthropic-provider.test.ts +302 -33
- package/src/__tests__/approval-cascade.test.ts +1 -1
- package/src/__tests__/assistant-event-hub-self-exclusion.test.ts +293 -0
- package/src/__tests__/assistant-feature-flags-integration.test.ts +3 -3
- package/src/__tests__/audit-log-rotation.test.ts +70 -16
- package/src/__tests__/background-workers-disk-pressure.test.ts +4 -3
- package/src/__tests__/btw-routes.test.ts +2 -3
- package/src/__tests__/call-controller.test.ts +0 -1
- package/src/__tests__/cancel-resolves-conversation-key.test.ts +1 -1
- package/src/__tests__/channel-delivery-store.test.ts +193 -0
- package/src/__tests__/channel-guardian.test.ts +3 -3
- package/src/__tests__/channel-reply-delivery.test.ts +284 -5
- package/src/__tests__/channel-retry-sweep.test.ts +274 -1
- package/src/__tests__/checker.test.ts +6 -15
- package/src/__tests__/compaction-events.test.ts +2 -1
- package/src/__tests__/compactor-call-site-logging.test.ts +214 -0
- package/src/__tests__/compactor-preserved-tail-count.test.ts +110 -0
- package/src/__tests__/computer-use-skill-manifest-regression.test.ts +5 -11
- package/src/__tests__/computer-use-tools.test.ts +2 -4
- package/src/__tests__/config-watcher.test.ts +1 -1
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +0 -1
- package/src/__tests__/context-token-estimator.test.ts +91 -1
- package/src/__tests__/conversation-abort-tool-results.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +55 -4
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +228 -8
- package/src/__tests__/conversation-agent-loop.test.ts +188 -129
- package/src/__tests__/conversation-app-control-instantiation.test.ts +2 -5
- package/src/__tests__/conversation-app-control-lifecycle.test.ts +1 -1
- package/src/__tests__/conversation-clean-command.test.ts +137 -0
- package/src/__tests__/conversation-clear-safety.test.ts +25 -25
- package/src/__tests__/conversation-confirmation-signals.test.ts +1 -1
- package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +1 -1
- package/src/__tests__/conversation-disk-view-integration.test.ts +2 -2
- package/src/__tests__/conversation-error.test.ts +31 -0
- package/src/__tests__/conversation-fork-crud.test.ts +324 -0
- package/src/__tests__/conversation-lifecycle.test.ts +53 -12
- package/src/__tests__/conversation-load-history-repair.test.ts +1 -1
- package/src/__tests__/conversation-load-history-stripped.test.ts +279 -0
- package/src/__tests__/conversation-pairing.test.ts +2 -2
- package/src/__tests__/conversation-process-callsite.test.ts +1 -1
- package/src/__tests__/conversation-provider-retry-repair.test.ts +2 -1
- package/src/__tests__/conversation-queue.test.ts +1 -1
- package/src/__tests__/conversation-routes-disk-view.test.ts +109 -0
- package/src/__tests__/conversation-routes-slash-commands.test.ts +35 -0
- package/src/__tests__/conversation-runtime-assembly.test.ts +264 -81
- package/src/__tests__/conversation-seed-composer.test.ts +66 -4
- package/src/__tests__/conversation-skill-tools.test.ts +2 -5
- package/src/__tests__/conversation-slash-commands.test.ts +36 -8
- package/src/__tests__/conversation-slash-queue.test.ts +1 -1
- package/src/__tests__/conversation-slash-unknown.test.ts +1 -1
- package/src/__tests__/conversation-speed-override.test.ts +1 -1
- package/src/__tests__/conversation-store.test.ts +1 -1
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +220 -0
- package/src/__tests__/conversation-sync-tags.test.ts +99 -32
- package/src/__tests__/conversation-workspace-cache-state.test.ts +2 -1
- 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-execution-feature-gates.test.ts +9 -7
- package/src/__tests__/credential-execution-tools.test.ts +6 -6
- package/src/__tests__/credential-security-invariants.test.ts +7 -0
- package/src/__tests__/credential-vault-unit.test.ts +2 -2
- package/src/__tests__/cu-unified-flow.test.ts +10 -1
- package/src/__tests__/dm-backfill.test.ts +64 -0
- package/src/__tests__/dm-persistence.test.ts +33 -0
- package/src/__tests__/document-find-replace.test.ts +501 -0
- package/src/__tests__/dynamic-page-surface.test.ts +2 -2
- package/src/__tests__/email-html-renderer.test.ts +12 -0
- package/src/__tests__/first-greeting.test.ts +23 -2
- package/src/__tests__/gateway-flag-listener.test.ts +237 -0
- package/src/__tests__/gemini-provider.test.ts +78 -0
- package/src/__tests__/guardian-dispatch.test.ts +0 -1
- package/src/__tests__/guardian-outbound-http.test.ts +7 -5
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +1 -1
- package/src/__tests__/headless-browser-navigate.test.ts +172 -0
- package/src/__tests__/heartbeat-disk-pressure.test.ts +4 -0
- package/src/__tests__/heartbeat-service.test.ts +4 -0
- package/src/__tests__/host-bash-proxy.test.ts +6 -0
- package/src/__tests__/host-browser-proxy.test.ts +10 -0
- package/src/__tests__/host-cu-proxy.test.ts +8 -1
- package/src/__tests__/host-file-proxy.test.ts +8 -1
- package/src/__tests__/host-shell-tool.test.ts +1 -1
- package/src/__tests__/host-transfer-proxy.test.ts +8 -1
- package/src/__tests__/identity-routes.test.ts +57 -0
- package/src/__tests__/inbound-slack-persistence.test.ts +3 -0
- package/src/__tests__/init-feature-flag-overrides.test.ts +5 -6
- package/src/__tests__/injector-chain.test.ts +2 -0
- package/src/__tests__/injector-document-comments.test.ts +378 -0
- package/src/__tests__/injector-pkb-v2-silenced.test.ts +4 -25
- package/src/__tests__/list-messages-attachments.test.ts +21 -17
- package/src/__tests__/list-messages-hidden-metadata.test.ts +217 -0
- package/src/__tests__/list-messages-page-latest.test.ts +130 -14
- package/src/__tests__/list-messages-tool-merge.test.ts +77 -17
- package/src/__tests__/llm-context-normalization.test.ts +0 -2
- package/src/__tests__/llm-request-log-call-site.test.ts +136 -0
- package/src/__tests__/llm-request-log-source-clickhouse.test.ts +26 -0
- package/src/__tests__/llm-resolver.test.ts +161 -9
- package/src/__tests__/llm-usage-store.test.ts +66 -0
- package/src/__tests__/log-export-routes.test.ts +99 -2
- package/src/__tests__/logger.test.ts +89 -0
- package/src/__tests__/mcp-abort-signal.test.ts +2 -2
- package/src/__tests__/media-generate-image.test.ts +31 -0
- package/src/__tests__/memory-v2-static-injector.test.ts +7 -7
- package/src/__tests__/message-queue-steer.test.ts +114 -0
- package/src/__tests__/model-intents.test.ts +2 -4
- package/src/__tests__/notification-guardian-path.test.ts +0 -1
- package/src/__tests__/onboarding-template-contract.test.ts +1 -1
- package/src/__tests__/openai-provider.test.ts +151 -0
- package/src/__tests__/openai-responses-provider.test.ts +118 -16
- package/src/__tests__/outbound-slack-persistence.test.ts +187 -20
- package/src/__tests__/pending-interactions-resolved-event.test.ts +189 -0
- package/src/__tests__/platform-bash-auto-approve.test.ts +2 -2
- package/src/__tests__/platform.test.ts +2 -5
- package/src/__tests__/plugin-api-tool-definition.test.ts +92 -0
- package/src/__tests__/plugin-bootstrap.test.ts +2 -2
- package/src/__tests__/plugin-source-watcher.test.ts +302 -0
- package/src/__tests__/plugin-tool-contribution.test.ts +13 -6
- package/src/__tests__/plugin-types.test.ts +3 -2
- package/src/__tests__/prechat-onboarding-contract.test.ts +131 -98
- package/src/__tests__/pricing.test.ts +12 -0
- package/src/__tests__/process-message-background-slack.test.ts +1 -51
- package/src/__tests__/process-message-display-content.test.ts +21 -16
- package/src/__tests__/prune-jobs-changes-parser.test.ts +61 -0
- package/src/__tests__/registry.test.ts +2 -8
- package/src/__tests__/require-fresh-approval.test.ts +2 -2
- package/src/__tests__/runtime-events-sse-bilingual.test.ts +154 -0
- package/src/__tests__/server-history-render.test.ts +83 -4
- package/src/__tests__/shell-tool-proxy-mode.test.ts +1 -1
- package/src/__tests__/skill-feature-flags.test.ts +2 -2
- package/src/__tests__/skill-projection-feature-flag.test.ts +4 -7
- package/src/__tests__/skill-projection.benchmark.test.ts +2 -6
- package/src/__tests__/skill-tool-factory.test.ts +1 -1
- package/src/__tests__/steer-tool-repair.test.ts +249 -0
- package/src/__tests__/subagent-notify-parent.test.ts +1 -1
- package/src/__tests__/suggestion-routes.test.ts +1 -0
- package/src/__tests__/sync-message-contract.test.ts +59 -0
- package/src/__tests__/system-prompt.test.ts +161 -124
- package/src/__tests__/terminal-tools.test.ts +12 -2
- package/src/__tests__/thinking-block-replay.test.ts +113 -0
- package/src/__tests__/thread-backfill.test.ts +370 -22
- package/src/__tests__/tool-approval-handler.test.ts +1 -5
- package/src/__tests__/tool-execute-pipeline.test.ts +2 -2
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +2 -5
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +15 -5
- package/src/__tests__/tool-executor.test.ts +89 -53
- package/src/__tests__/tool-grant-request-escalation.test.ts +1 -6
- package/src/__tests__/tool-result-metadata-plumbing.test.ts +167 -0
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +0 -1
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +1 -6
- package/src/__tests__/trusted-contact-multichannel.test.ts +0 -1
- package/src/__tests__/twilio-routes.test.ts +1 -1
- package/src/__tests__/ui-file-upload-surface.test.ts +2 -2
- package/src/__tests__/usage-routes.test.ts +3 -0
- package/src/__tests__/verification-control-plane-policy.test.ts +2 -2
- package/src/__tests__/web-fetch.test.ts +2 -2
- package/src/__tests__/workspace-git-service.test.ts +94 -10
- package/src/__tests__/workspace-migration-088-deprecate-background-conversation-override.test.ts +158 -0
- package/src/__tests__/workspace-migration-089-move-memory-tree-out-of-v3.test.ts +86 -0
- package/src/acp/__tests__/prepare-agent-env.test.ts +146 -0
- package/src/acp/prepare-agent-env.ts +78 -0
- package/src/acp/session-manager.ts +1 -1
- package/src/agent/attachments.ts +1 -0
- package/src/agent/loop.ts +65 -20
- package/src/api/README.md +5 -0
- package/src/api/index.ts +4 -0
- package/src/api/package.json +10 -0
- package/src/background-wake/background-wake-routes.test.ts +233 -0
- package/src/background-wake/next-wake.test.ts +289 -0
- package/src/background-wake/next-wake.ts +172 -0
- package/src/background-wake/runtime-registry.ts +24 -0
- package/src/browser/operations.ts +15 -0
- package/src/cli/commands/__tests__/browser.test.ts +23 -5
- package/src/cli/commands/__tests__/conversations-slack.test.ts +572 -0
- package/src/cli/commands/__tests__/domain-register.test.ts +110 -0
- package/src/cli/commands/__tests__/domain-status.test.ts +33 -33
- package/src/cli/commands/__tests__/inference-send.test.ts +108 -5
- package/src/cli/commands/__tests__/memory-v2-compare-render.test.ts +98 -0
- package/src/cli/commands/__tests__/memory-v2.test.ts +10 -12
- package/src/cli/commands/__tests__/memory-v3-render.test.ts +340 -0
- package/src/cli/commands/browser.ts +247 -0
- package/src/cli/commands/conversations.ts +128 -1
- package/src/cli/commands/domain.ts +91 -41
- package/src/cli/commands/inference-providers.ts +147 -1
- package/src/cli/commands/inference.ts +93 -40
- package/src/cli/commands/memory-v2-compare-render.ts +115 -0
- package/src/cli/commands/memory-v2.ts +483 -0
- package/src/cli/commands/memory-v3-render.ts +344 -0
- package/src/cli/commands/memory-v3.ts +316 -0
- package/src/cli/commands/notifications.ts +24 -2
- package/src/cli/program.ts +2 -0
- package/src/cli/utils/conversation-id.ts +17 -5
- package/src/config/assistant-feature-flags.ts +21 -9
- package/src/config/bundled-skills/app-builder/SKILL.md +2 -2
- package/src/config/bundled-skills/document-editor/SKILL.md +124 -0
- package/src/config/bundled-skills/document-editor/TOOLS.json +258 -0
- package/src/config/bundled-skills/document-editor/tools/comment-list.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/comment-reply.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/comment-resolve.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/document-find.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/document-open.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/document-replace-text.ts +12 -0
- package/src/config/bundled-skills/image-studio/SKILL.md +4 -0
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +2 -2
- package/src/config/bundled-skills/media-processing/SKILL.md +8 -0
- package/src/config/bundled-skills/media-processing/tools/ingest-media.ts +13 -8
- package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +10 -3
- package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +16 -14
- package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +7 -2
- package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +7 -2
- package/src/config/bundled-skills/schedule/SKILL.md +8 -0
- package/src/config/bundled-tool-registry.ts +24 -12
- package/src/config/call-site-defaults.ts +20 -0
- package/src/config/feature-flag-registry.json +115 -3
- package/src/config/llm-resolver.ts +16 -2
- package/src/config/schemas/__tests__/memory-v2.test.ts +217 -1
- package/src/config/schemas/call-site-catalog.ts +35 -0
- package/src/config/schemas/llm.ts +14 -0
- package/src/config/schemas/memory-v2.ts +294 -1
- package/src/config/schemas/memory.ts +2 -1
- package/src/context/compactor.ts +60 -1
- package/src/context/token-estimator.ts +47 -4
- package/src/context/window-manager.ts +25 -0
- package/src/conversations/__tests__/message-consolidation.test.ts +350 -0
- package/src/conversations/message-consolidation.ts +404 -0
- package/src/credential-health/credential-health-service.ts +34 -19
- package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +1 -1
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +66 -6
- package/src/daemon/__tests__/meet-manifest-loader.test.ts +1 -1
- package/src/daemon/__tests__/native-web-search-metadata.test.ts +357 -0
- package/src/daemon/__tests__/web-search-status-text.test.ts +287 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +155 -36
- package/src/daemon/conversation-agent-loop.ts +307 -88
- package/src/daemon/conversation-error.ts +31 -1
- package/src/daemon/conversation-lifecycle.ts +149 -118
- package/src/daemon/conversation-messaging.ts +3 -0
- package/src/daemon/conversation-process.ts +273 -0
- package/src/daemon/conversation-queue-manager.ts +14 -0
- package/src/daemon/conversation-runtime-assembly.ts +145 -84
- package/src/daemon/conversation-slash.ts +37 -5
- package/src/daemon/conversation-surfaces.ts +45 -2
- package/src/daemon/conversation-tool-setup.ts +70 -3
- package/src/daemon/conversation-usage.ts +2 -0
- package/src/daemon/conversation.ts +54 -32
- package/src/daemon/disk-pressure-guard.ts +14 -2
- package/src/daemon/first-greeting.ts +10 -0
- package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +498 -0
- package/src/daemon/handlers/config-a2a.ts +160 -0
- package/src/daemon/handlers/config-model.test.ts +2 -0
- package/src/daemon/handlers/conversations.ts +90 -3
- package/src/daemon/handlers/shared.ts +92 -29
- package/src/daemon/host-bash-proxy.ts +1 -1
- package/src/daemon/host-browser-proxy.ts +5 -5
- package/src/daemon/host-cu-proxy.ts +5 -5
- package/src/daemon/host-file-proxy.ts +5 -5
- package/src/daemon/host-proxy-base.ts +4 -4
- package/src/daemon/host-transfer-proxy.ts +11 -11
- package/src/daemon/lifecycle.ts +40 -23
- package/src/daemon/meet-manifest-loader.ts +1 -7
- package/src/daemon/message-protocol.ts +4 -0
- package/src/daemon/message-types/conversations.ts +14 -9
- package/src/daemon/message-types/document-comments.ts +50 -0
- package/src/daemon/message-types/home.ts +1 -13
- package/src/daemon/message-types/messages.ts +66 -7
- package/src/daemon/message-types/surfaces.ts +3 -1
- package/src/daemon/message-types/sync.ts +14 -0
- package/src/daemon/message-types/web-activity.ts +57 -0
- package/src/daemon/plugin-source-watcher.ts +135 -3
- package/src/daemon/process-message.ts +69 -12
- package/src/daemon/shutdown-handlers.ts +24 -5
- package/src/daemon/switch-inference-profile-tool.ts +52 -0
- package/src/daemon/tool-setup-types.ts +13 -0
- package/src/daemon/trust-context.ts +6 -0
- package/src/documents/document-comments-store.test.ts +338 -0
- package/src/documents/document-comments-store.ts +237 -0
- package/src/documents/document-store.ts +202 -0
- package/src/events/relationship-state-updated.ts +25 -0
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +1 -2
- package/src/heartbeat/heartbeat-service.ts +1 -0
- package/src/home/__tests__/suggested-prompts.test.ts +33 -2
- package/src/home/feed-types.ts +6 -1
- package/src/home/home-content-refresh.ts +52 -0
- package/src/home/home-greeting-cache.ts +69 -0
- package/src/home/home-greeting.ts +85 -0
- package/src/home/suggested-prompts.ts +168 -9
- package/src/ipc/gateway-flag-listener.ts +123 -0
- package/src/ipc/skill-routes/registries.ts +8 -12
- package/src/memory/__tests__/db-async-query.test.ts +165 -0
- package/src/memory/__tests__/db-maintenance.test.ts +115 -0
- package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +241 -0
- package/src/memory/__tests__/jobs-store-job-classes.test.ts +28 -1
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +135 -2
- package/src/memory/__tests__/memory-retrospective-job.test.ts +327 -6
- package/src/memory/auto-analysis-enqueue.ts +5 -1
- package/src/memory/conversation-crud.ts +191 -100
- package/src/memory/conversation-starters-cadence.ts +3 -1
- package/src/memory/conversation-title-service.ts +19 -3
- package/src/memory/db-async-query.ts +214 -0
- package/src/memory/db-init.ts +26 -0
- package/src/memory/db-maintenance.ts +30 -21
- package/src/memory/delivery-crud.ts +41 -0
- package/src/memory/delivery-status.ts +141 -15
- package/src/memory/external-conversation-store.ts +32 -1
- package/src/memory/graph/bootstrap.ts +8 -1
- package/src/memory/graph/capability-seed.ts +7 -3
- package/src/memory/graph/conversation-graph-memory.ts +100 -17
- package/src/memory/graph/extraction.ts +1 -5
- package/src/memory/graph/graph-search.ts +7 -1
- package/src/memory/indexer.ts +28 -18
- package/src/memory/job-handlers/cleanup.ts +76 -18
- package/src/memory/job-handlers/conversation-starters.ts +1 -4
- package/src/memory/jobs/embed-pkb-file.ts +6 -1
- package/src/memory/jobs-store.ts +14 -0
- package/src/memory/jobs-worker.ts +68 -15
- package/src/memory/llm-request-log-source-clickhouse.ts +42 -2
- package/src/memory/llm-request-log-source-local.ts +7 -0
- package/src/memory/llm-request-log-source.ts +9 -2
- package/src/memory/llm-request-log-store.ts +43 -1
- package/src/memory/llm-usage-store.ts +24 -0
- package/src/memory/memory-retrospective-constants.ts +28 -0
- package/src/memory/memory-retrospective-enqueue.ts +11 -3
- package/src/memory/memory-retrospective-job.ts +413 -18
- package/src/memory/memory-retrospective-startup-cleanup.ts +3 -3
- package/src/memory/memory-v2-activation-log-store.ts +41 -14
- package/src/memory/migrations/100-core-tables.ts +1 -0
- package/src/memory/migrations/109-external-conversation-bindings.ts +1 -0
- package/src/memory/migrations/253-conversation-last-notified-profile.ts +15 -0
- package/src/memory/migrations/253-document-comments.ts +47 -0
- package/src/memory/migrations/254-external-conversation-binding-chat-name.ts +43 -0
- package/src/memory/migrations/255-channel-inbound-delivery-attempts.ts +24 -0
- package/src/memory/migrations/256-memory-v2-injection-events.ts +113 -0
- package/src/memory/migrations/257-strip-base-url-non-openai-compatible.ts +22 -0
- package/src/memory/migrations/258-onboarding-events-prior-assistants.ts +13 -0
- package/src/memory/migrations/259-conversation-cleaned-at.ts +33 -0
- package/src/memory/migrations/260-rename-cleaned-at.ts +44 -0
- package/src/memory/migrations/261-llm-usage-add-raw-usage.ts +36 -0
- package/src/memory/migrations/262-memory-v3-coactivation.ts +57 -0
- package/src/memory/migrations/263-memory-v3-auto-edges.ts +50 -0
- package/src/memory/migrations/264-llm-request-log-call-site.ts +29 -0
- package/src/memory/migrations/index.ts +34 -0
- package/src/memory/migrations/registry.ts +58 -0
- package/src/memory/onboarding-events-store.ts +7 -0
- package/src/memory/schema/calls.ts +1 -0
- package/src/memory/schema/conversations.ts +3 -0
- package/src/memory/schema/infrastructure.ts +22 -0
- package/src/memory/tool-usage-store.ts +36 -8
- package/src/memory/v2/__tests__/consolidation-job.test.ts +1 -0
- package/src/memory/v2/__tests__/harness-compare.test.ts +186 -0
- package/src/memory/v2/__tests__/harness-metrics.test.ts +74 -0
- package/src/memory/v2/__tests__/harness-oracle.test.ts +257 -0
- package/src/memory/v2/__tests__/harness-replay-input.test.ts +225 -0
- package/src/memory/v2/__tests__/harness-runner.test.ts +109 -0
- package/src/memory/v2/__tests__/injection-events.test.ts +318 -0
- package/src/memory/v2/__tests__/injection.test.ts +158 -112
- package/src/memory/v2/__tests__/page-index.test.ts +365 -1
- package/src/memory/v2/__tests__/qdrant.test.ts +36 -0
- package/src/memory/v2/__tests__/router.test.ts +660 -4
- package/src/memory/v2/consolidation-job.ts +14 -0
- package/src/memory/v2/harness/compare.ts +57 -0
- package/src/memory/v2/harness/metrics.ts +124 -0
- package/src/memory/v2/harness/oracle.ts +145 -0
- package/src/memory/v2/harness/replay-input.ts +224 -0
- package/src/memory/v2/harness/retriever.ts +74 -0
- package/src/memory/v2/harness/router-retriever.ts +43 -0
- package/src/memory/v2/harness/runner.ts +106 -0
- package/src/memory/v2/harness/trace.ts +58 -0
- package/src/memory/v2/injection-events.ts +101 -0
- package/src/memory/v2/injection.ts +42 -25
- package/src/memory/v2/page-index.ts +209 -7
- package/src/memory/v2/page-store.ts +18 -0
- package/src/memory/v2/prompts/router.ts +26 -1
- package/src/memory/v2/qdrant.ts +14 -2
- package/src/memory/v2/router.ts +369 -62
- package/src/memory/v3/__tests__/coactivation-store.test.ts +422 -0
- package/src/memory/v3/__tests__/consolidation-job.test.ts +468 -0
- package/src/memory/v3/__tests__/edge-learning-job.test.ts +324 -0
- package/src/memory/v3/__tests__/edges.test.ts +563 -0
- package/src/memory/v3/__tests__/filter.test.ts +512 -0
- package/src/memory/v3/__tests__/gate.test.ts +574 -0
- package/src/memory/v3/__tests__/index-composition.test.ts +233 -0
- package/src/memory/v3/__tests__/loop.test.ts +530 -0
- package/src/memory/v3/__tests__/retriever.test.ts +226 -0
- package/src/memory/v3/__tests__/scouts.test.ts +440 -0
- package/src/memory/v3/__tests__/shadow-middleware.test.ts +312 -0
- package/src/memory/v3/__tests__/system-prompts.test.ts +154 -0
- package/src/memory/v3/__tests__/traversal.test.ts +469 -0
- package/src/memory/v3/__tests__/tree-index.test.ts +280 -0
- package/src/memory/v3/__tests__/tree-store.test.ts +529 -0
- package/src/memory/v3/__tests__/tree-walk.test.ts +707 -0
- package/src/memory/v3/__tests__/validate.test.ts +245 -0
- package/src/memory/v3/auto-edges.ts +223 -0
- package/src/memory/v3/coactivation-store.ts +124 -0
- package/src/memory/v3/consolidation-job.ts +323 -0
- package/src/memory/v3/edge-learning-job.ts +160 -0
- package/src/memory/v3/edges.ts +249 -0
- package/src/memory/v3/filter.ts +281 -0
- package/src/memory/v3/gate.ts +334 -0
- package/src/memory/v3/index-composition.ts +113 -0
- package/src/memory/v3/llm-capture.ts +46 -0
- package/src/memory/v3/loop.ts +382 -0
- package/src/memory/v3/maintenance.ts +144 -0
- package/src/memory/v3/prompt-context.ts +33 -0
- package/src/memory/v3/prompts/consolidation.ts +458 -0
- package/src/memory/v3/prompts/system-prompts.ts +196 -0
- package/src/memory/v3/retriever.ts +33 -0
- package/src/memory/v3/scouts.ts +420 -0
- package/src/memory/v3/shadow-middleware.ts +305 -0
- package/src/memory/v3/traversal.ts +206 -0
- package/src/memory/v3/tree-index.ts +237 -0
- package/src/memory/v3/tree-store.ts +394 -0
- package/src/memory/v3/tree-walk.ts +351 -0
- package/src/memory/v3/types.ts +65 -0
- package/src/memory/v3/validate.ts +300 -0
- package/src/messaging/providers/index.ts +7 -1
- package/src/messaging/providers/slack/__tests__/adapter-mention-rendering.test.ts +329 -3
- package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +34 -1
- package/src/messaging/providers/slack/adapter.ts +178 -25
- package/src/messaging/providers/slack/api.test.ts +54 -0
- package/src/messaging/providers/slack/api.ts +119 -3
- package/src/messaging/providers/slack/client.ts +12 -0
- package/src/messaging/providers/slack/deep-link.ts +20 -1
- package/src/messaging/providers/slack/message-metadata.test.ts +48 -0
- package/src/messaging/providers/slack/message-metadata.ts +156 -0
- package/src/messaging/providers/slack/render-transcript.test.ts +107 -75
- package/src/messaging/providers/slack/render-transcript.ts +176 -49
- package/src/messaging/providers/slack/send.test.ts +77 -0
- package/src/messaging/providers/slack/send.ts +8 -2
- package/src/messaging/providers/slack/types.ts +14 -0
- package/src/notifications/__tests__/emit-signal-home-feed.test.ts +4 -1
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +116 -54
- package/src/notifications/adapters/macos.ts +18 -1
- package/src/notifications/adapters/platform.ts +1 -1
- package/src/notifications/conversation-seed-composer.ts +14 -2
- package/src/notifications/decision-engine.ts +1 -4
- package/src/notifications/deferred-emit.ts +135 -0
- package/src/notifications/emit-signal.ts +38 -50
- package/src/notifications/home-feed-side-effect.ts +60 -30
- package/src/oauth/connect-orchestrator.ts +3 -0
- package/src/oauth/credential-token-resolver.ts +2 -0
- package/src/oauth/manual-token-connection.ts +19 -0
- package/src/oauth/oauth-store.ts +12 -0
- package/src/oauth/seed-providers.ts +22 -0
- package/src/permissions/prompter.ts +8 -5
- package/src/permissions/question-prompter.ts +5 -2
- package/src/permissions/secret-prompter.ts +6 -3
- package/src/plugin-api/index.ts +4 -0
- package/src/plugin-api/types.ts +7 -33
- package/src/plugins/defaults/index.ts +6 -0
- package/src/plugins/defaults/injectors.ts +100 -20
- package/src/plugins/external-plugin-loader.ts +5 -68
- package/src/plugins/types.ts +11 -16
- package/src/proactive-artifact/aux-message-injector.ts +17 -4
- package/src/prompts/__tests__/system-prompt.test.ts +46 -2
- package/src/prompts/__tests__/task-progress-hint-section.test.ts +3 -9
- package/src/prompts/normalize-onboarding.ts +40 -0
- package/src/prompts/persona-resolver.ts +36 -21
- package/src/prompts/sections.ts +69 -19
- package/src/prompts/system-prompt.ts +118 -216
- package/src/prompts/template-detection.ts +37 -0
- package/src/prompts/templates/BOOTSTRAP-CONTENT-AUTOMATION.md +141 -0
- package/src/prompts/templates/BOOTSTRAP.md +10 -2
- package/src/prompts/templates/VOICE.md +3 -0
- package/src/prompts/templates/system-sections.ts +281 -9
- package/src/providers/__tests__/connection-model-compat.test.ts +234 -0
- package/src/providers/__tests__/retry-callsite.test.ts +85 -5
- package/src/providers/anthropic/client.ts +159 -66
- package/src/providers/call-site-routing.ts +14 -2
- package/src/providers/connection-model-compat.ts +38 -0
- package/src/providers/connection-resolution.ts +16 -2
- package/src/providers/fireworks/client.ts +20 -2
- package/src/providers/gemini/client.ts +49 -6
- package/src/providers/inference/__tests__/base-url-route-validation.test.ts +342 -0
- package/src/providers/inference/__tests__/base-url-security.test.ts +189 -0
- package/src/providers/inference/__tests__/codex-token-refresh.test.ts +254 -0
- package/src/providers/inference/adapter-factory.ts +18 -1
- package/src/providers/inference/auth.ts +3 -3
- package/src/providers/inference/codex-token-refresh.ts +128 -0
- package/src/providers/inference/resolve-auth.ts +49 -6
- package/src/providers/minimax/client.ts +106 -0
- package/src/providers/model-catalog.ts +91 -1
- package/src/providers/model-intents.ts +1 -1
- package/src/providers/openai/chat-completions-provider.ts +63 -23
- package/src/providers/openai/codex-models.ts +18 -0
- package/src/providers/openai/responses-provider.ts +86 -23
- package/src/providers/openrouter/client.ts +5 -1
- package/src/providers/provider-send-message.ts +7 -1
- package/src/providers/retry.ts +34 -3
- package/src/providers/thinking-config.ts +26 -1
- package/src/providers/types.ts +25 -0
- package/src/providers/usage-tracking.ts +2 -0
- package/src/runtime/AGENTS.md +2 -2
- package/src/runtime/__tests__/agent-wake.test.ts +214 -0
- package/src/runtime/__tests__/background-job-runner.test.ts +128 -0
- package/src/runtime/agent-wake.ts +152 -56
- package/src/runtime/assistant-event-hub.ts +76 -6
- package/src/runtime/auth/route-policy.ts +43 -3
- package/src/runtime/background-job-runner.ts +26 -0
- package/src/runtime/btw-sidechain.ts +0 -6
- package/src/runtime/channel-reply-delivery.ts +182 -47
- package/src/runtime/channel-retry-sweep.ts +141 -16
- package/src/runtime/http-types.ts +7 -6
- package/src/runtime/migrations/vbundle-builder.ts +10 -3
- package/src/runtime/pending-interactions.ts +50 -8
- package/src/runtime/routes/__tests__/content-source-routes.test.ts +162 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +161 -1
- package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +14 -0
- package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +290 -0
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +512 -0
- package/src/runtime/routes/__tests__/sanity-routes.test.ts +280 -0
- package/src/runtime/routes/__tests__/slack-channel-routes.test.ts +266 -0
- package/src/runtime/routes/acp-routes.test.ts +255 -6
- package/src/runtime/routes/acp-routes.ts +8 -1
- package/src/runtime/routes/approval-routes.ts +4 -1
- package/src/runtime/routes/avatar-routes.ts +10 -10
- package/src/runtime/routes/background-wake-routes.ts +188 -0
- package/src/runtime/routes/browser-tabs-routes.ts +200 -0
- package/src/runtime/routes/btw-routes.ts +0 -6
- package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +246 -0
- package/src/runtime/routes/content-source-routes.ts +78 -0
- package/src/runtime/routes/conversation-cli-routes.ts +147 -2
- package/src/runtime/routes/conversation-list-routes.ts +12 -4
- package/src/runtime/routes/conversation-management-routes.ts +77 -20
- package/src/runtime/routes/conversation-query-routes.ts +196 -31
- package/src/runtime/routes/conversation-routes.ts +472 -425
- package/src/runtime/routes/conversation-starter-routes.ts +6 -3
- package/src/runtime/routes/disk-pressure-routes.ts +1 -1
- package/src/runtime/routes/document-comments-routes.ts +287 -0
- package/src/runtime/routes/documents-routes.ts +33 -0
- package/src/runtime/routes/domain-routes.ts +60 -10
- package/src/runtime/routes/email-routes.ts +5 -2
- package/src/runtime/routes/events-routes.ts +54 -10
- package/src/runtime/routes/group-routes.ts +24 -8
- package/src/runtime/routes/home-feed-routes.ts +6 -3
- package/src/runtime/routes/host-app-control-routes.ts +1 -1
- package/src/runtime/routes/host-browser-routes.ts +17 -2
- package/src/runtime/routes/host-cu-routes.ts +2 -2
- package/src/runtime/routes/identity-routes.ts +21 -0
- package/src/runtime/routes/inbound-message-handler.ts +288 -58
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +96 -3
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +365 -6
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +283 -82
- package/src/runtime/routes/index.ts +20 -4
- package/src/runtime/routes/inference-profile-session-handler.ts +22 -12
- package/src/runtime/routes/inference-profile-session-routes.ts +7 -1
- package/src/runtime/routes/inference-provider-connection-routes.ts +63 -7
- package/src/runtime/routes/integrations/a2a.ts +60 -1
- package/src/runtime/routes/llm-call-sites-routes.ts +32 -5
- package/src/runtime/routes/log-export-routes.ts +39 -0
- package/src/runtime/routes/memory-item-routes.ts +8 -3
- package/src/runtime/routes/memory-v2-routes.ts +427 -0
- package/src/runtime/routes/memory-v3-routes.ts +316 -0
- package/src/runtime/routes/migration-routes.ts +21 -24
- package/src/runtime/routes/notification-routes.ts +19 -2
- package/src/runtime/routes/plugins-routes.ts +337 -0
- package/src/runtime/routes/question-routes.ts +4 -1
- package/src/runtime/routes/rename-conversation-routes.ts +6 -2
- package/src/runtime/routes/sanity-routes.ts +159 -0
- package/src/runtime/routes/secret-routes.ts +25 -5
- package/src/runtime/routes/settings-routes.ts +12 -11
- package/src/runtime/routes/slack-channel-routes.ts +188 -0
- package/src/runtime/routes/workspace-routes.ts +25 -10
- package/src/runtime/services/conversation-serializer.ts +30 -4
- package/src/runtime/sync/resource-sync-events.ts +106 -38
- package/src/runtime/sync/sync-publisher.test.ts +49 -0
- package/src/runtime/sync/sync-publisher.ts +2 -1
- package/src/runtime/verification-outbound-actions.ts +73 -1
- package/src/schedule/integration-status.ts +3 -1
- package/src/security/__tests__/oauth2-device-code.test.ts +479 -0
- package/src/security/oauth2-device-code.ts +307 -0
- package/src/security/oauth2.ts +26 -9
- package/src/security/secure-keys.ts +5 -0
- package/src/skills/catalog-install.ts +6 -2
- package/src/telemetry/types.ts +12 -0
- package/src/telemetry/usage-telemetry-reporter.test.ts +48 -0
- package/src/telemetry/usage-telemetry-reporter.ts +1 -0
- package/src/tools/acp/spawn.test.ts +119 -0
- package/src/tools/acp/spawn.ts +15 -2
- package/src/tools/apps/definitions.ts +2 -8
- package/src/tools/ask-question/ask-question-tool.test.ts +3 -3
- package/src/tools/ask-question/ask-question-tool.ts +38 -45
- package/src/tools/browser/__tests__/pinned-tabs.test.ts +150 -0
- package/src/tools/browser/browser-execution.ts +106 -0
- package/src/tools/browser/cdp-client/__tests__/browser-tabs-factory.test.ts +402 -0
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +28 -0
- package/src/tools/browser/cdp-client/__tests__/types.test.ts +4 -0
- package/src/tools/browser/cdp-client/cdp-inspect-client.ts +22 -0
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +42 -2
- package/src/tools/browser/cdp-client/factory.ts +171 -4
- package/src/tools/browser/cdp-client/local-cdp-client.ts +21 -0
- package/src/tools/browser/cdp-client/types.ts +101 -0
- package/src/tools/browser/pinned-tabs.ts +146 -0
- package/src/tools/computer-use/definitions.ts +22 -78
- package/src/tools/credential-execution/make-authenticated-request.ts +3 -9
- package/src/tools/credential-execution/manage-secure-command-tool.ts +3 -9
- package/src/tools/credential-execution/run-authenticated-command.ts +3 -9
- package/src/tools/credentials/vault.ts +3 -9
- package/src/tools/document/document-comment-tool.test.ts +379 -0
- package/src/tools/document/document-comment-tool.ts +156 -0
- package/src/tools/document/document-tool.ts +187 -2
- package/src/tools/execution-target.ts +21 -23
- package/src/tools/executor.ts +6 -1
- package/src/tools/filesystem/edit.ts +3 -9
- package/src/tools/filesystem/list.ts +3 -9
- package/src/tools/filesystem/read.ts +3 -9
- package/src/tools/filesystem/write.ts +3 -9
- package/src/tools/host-filesystem/edit.ts +3 -9
- package/src/tools/host-filesystem/read.ts +3 -9
- package/src/tools/host-filesystem/transfer.ts +3 -9
- package/src/tools/host-filesystem/write.ts +3 -9
- package/src/tools/host-terminal/host-shell.ts +3 -9
- package/src/tools/mcp/mcp-tool-factory.ts +1 -8
- package/src/tools/memory/register.test.ts +1 -1
- package/src/tools/memory/register.ts +4 -9
- package/src/tools/network/__tests__/web-fetch-metadata.test.ts +229 -0
- package/src/tools/network/__tests__/web-search-metadata.test.ts +346 -0
- package/src/tools/network/domain-normalize.ts +17 -0
- package/src/tools/network/web-fetch.ts +216 -73
- package/src/tools/network/web-search.ts +216 -98
- package/src/tools/registry.ts +7 -23
- package/src/tools/schema-transforms.ts +1 -1
- package/src/tools/skills/execute.ts +3 -9
- package/src/tools/skills/load.ts +3 -9
- package/src/tools/skills/skill-tool-factory.ts +1 -8
- package/src/tools/subagent/notify-parent.ts +3 -9
- package/src/tools/system/request-permission.ts +3 -9
- package/src/tools/terminal/safe-env.ts +3 -2
- package/src/tools/terminal/shell.ts +3 -9
- package/src/tools/tool-approval-handler.ts +19 -12
- package/src/tools/tool-defaults.ts +94 -0
- package/src/tools/types.ts +31 -98
- package/src/tools/ui-surface/definitions.ts +9 -23
- package/src/types/onboarding-context.ts +4 -0
- package/src/usage/pricing.ts +23 -0
- package/src/usage/types.ts +12 -0
- package/src/util/__tests__/favicon.test.ts +84 -0
- package/src/util/favicon.ts +40 -0
- package/src/util/logger.ts +16 -7
- package/src/util/platform.ts +7 -7
- package/src/util/sqlite3-runtime.ts +65 -0
- package/src/workspace/git-service.ts +75 -4
- package/src/workspace/migrations/086-revert-stale-gemini-mis-rewrites.ts +1 -0
- package/src/workspace/migrations/088-deprecate-background-conversation-override.ts +103 -0
- package/src/workspace/migrations/089-move-memory-tree-out-of-v3.ts +86 -0
- package/src/workspace/migrations/registry.ts +4 -0
- package/src/__tests__/compaction-strip-metadata-clear.test.ts +0 -206
- package/src/__tests__/message-complete-display-id.test.ts +0 -175
- package/src/config/bundled-skills/document/SKILL.md +0 -54
- package/src/config/bundled-skills/document/TOOLS.json +0 -106
- package/src/daemon/seed-files.ts +0 -18
- package/src/prompts/cache-boundary.ts +0 -8
- package/src/runtime/routes/interface-routes.ts +0 -43
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-create.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-delete.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-list.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-read.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-update.ts +0 -0
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import {
|
|
2
|
+
afterEach,
|
|
3
|
+
beforeEach,
|
|
4
|
+
describe,
|
|
5
|
+
expect,
|
|
6
|
+
mock,
|
|
7
|
+
test,
|
|
8
|
+
} from "bun:test";
|
|
9
|
+
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
// Mocks
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
|
|
14
|
+
mock.module("../../../util/logger.js", () => ({
|
|
15
|
+
getLogger: () =>
|
|
16
|
+
new Proxy({} as Record<string, unknown>, {
|
|
17
|
+
get: () => () => {},
|
|
18
|
+
}),
|
|
19
|
+
}));
|
|
20
|
+
|
|
21
|
+
const mockGetSecureKey = mock<(key: string) => Promise<string | undefined>>(
|
|
22
|
+
async () => undefined,
|
|
23
|
+
);
|
|
24
|
+
const mockSetSecureKey = mock<(key: string, value: string) => Promise<boolean>>(
|
|
25
|
+
async () => true,
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
mock.module("../../../security/secure-keys.js", () => ({
|
|
29
|
+
getSecureKeyAsync: mockGetSecureKey,
|
|
30
|
+
setSecureKeyAsync: mockSetSecureKey,
|
|
31
|
+
}));
|
|
32
|
+
|
|
33
|
+
const mockRefreshOAuth2Token = mock<
|
|
34
|
+
(...args: unknown[]) => Promise<{
|
|
35
|
+
accessToken: string;
|
|
36
|
+
refreshToken?: string;
|
|
37
|
+
expiresIn?: number;
|
|
38
|
+
}>
|
|
39
|
+
>(async () => ({
|
|
40
|
+
accessToken: "new-access-token",
|
|
41
|
+
refreshToken: "new-refresh-token",
|
|
42
|
+
expiresIn: 3600,
|
|
43
|
+
}));
|
|
44
|
+
|
|
45
|
+
mock.module("../../../security/oauth2.js", () => ({
|
|
46
|
+
refreshOAuth2Token: mockRefreshOAuth2Token,
|
|
47
|
+
}));
|
|
48
|
+
|
|
49
|
+
// ---------------------------------------------------------------------------
|
|
50
|
+
// Import under test (after mocks)
|
|
51
|
+
// ---------------------------------------------------------------------------
|
|
52
|
+
|
|
53
|
+
import {
|
|
54
|
+
_resetRefreshMutex,
|
|
55
|
+
getValidCodexAccessToken,
|
|
56
|
+
} from "../codex-token-refresh.js";
|
|
57
|
+
|
|
58
|
+
const PREFIX = "credential/openai-codex";
|
|
59
|
+
|
|
60
|
+
// ---------------------------------------------------------------------------
|
|
61
|
+
// Helpers
|
|
62
|
+
// ---------------------------------------------------------------------------
|
|
63
|
+
|
|
64
|
+
function setSecureKeyMap(map: Record<string, string>): void {
|
|
65
|
+
mockGetSecureKey.mockImplementation(async (key: string) => map[key]);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function futureTimestamp(secondsFromNow: number): string {
|
|
69
|
+
return String(Math.floor(Date.now() / 1000) + secondsFromNow);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function pastTimestamp(secondsAgo: number): string {
|
|
73
|
+
return String(Math.floor(Date.now() / 1000) - secondsAgo);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// ---------------------------------------------------------------------------
|
|
77
|
+
// Tests
|
|
78
|
+
// ---------------------------------------------------------------------------
|
|
79
|
+
|
|
80
|
+
describe("getValidCodexAccessToken", () => {
|
|
81
|
+
beforeEach(() => {
|
|
82
|
+
mockGetSecureKey.mockReset();
|
|
83
|
+
mockSetSecureKey.mockReset();
|
|
84
|
+
mockRefreshOAuth2Token.mockReset();
|
|
85
|
+
_resetRefreshMutex();
|
|
86
|
+
|
|
87
|
+
mockSetSecureKey.mockImplementation(async () => true);
|
|
88
|
+
mockRefreshOAuth2Token.mockImplementation(async () => ({
|
|
89
|
+
accessToken: "new-access-token",
|
|
90
|
+
refreshToken: "new-refresh-token",
|
|
91
|
+
expiresIn: 3600,
|
|
92
|
+
}));
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
afterEach(() => {
|
|
96
|
+
_resetRefreshMutex();
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test("returns null when no access token is stored", async () => {
|
|
100
|
+
setSecureKeyMap({});
|
|
101
|
+
const result = await getValidCodexAccessToken(PREFIX);
|
|
102
|
+
expect(result).toBeNull();
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test("returns access token when no expires_at is stored (graceful degradation)", async () => {
|
|
106
|
+
setSecureKeyMap({
|
|
107
|
+
[`${PREFIX}/access_token`]: "my-token",
|
|
108
|
+
});
|
|
109
|
+
const result = await getValidCodexAccessToken(PREFIX);
|
|
110
|
+
expect(result).toBe("my-token");
|
|
111
|
+
expect(mockRefreshOAuth2Token).not.toHaveBeenCalled();
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
test("returns access token when not expired", async () => {
|
|
115
|
+
setSecureKeyMap({
|
|
116
|
+
[`${PREFIX}/access_token`]: "my-token",
|
|
117
|
+
[`${PREFIX}/expires_at`]: futureTimestamp(600), // 10 minutes from now
|
|
118
|
+
});
|
|
119
|
+
const result = await getValidCodexAccessToken(PREFIX);
|
|
120
|
+
expect(result).toBe("my-token");
|
|
121
|
+
expect(mockRefreshOAuth2Token).not.toHaveBeenCalled();
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
test("refreshes token when expired", async () => {
|
|
125
|
+
const keys: Record<string, string> = {
|
|
126
|
+
[`${PREFIX}/access_token`]: "old-token",
|
|
127
|
+
[`${PREFIX}/refresh_token`]: "old-refresh",
|
|
128
|
+
[`${PREFIX}/expires_at`]: pastTimestamp(60), // expired 1 minute ago
|
|
129
|
+
};
|
|
130
|
+
setSecureKeyMap(keys);
|
|
131
|
+
|
|
132
|
+
const result = await getValidCodexAccessToken(PREFIX);
|
|
133
|
+
|
|
134
|
+
expect(result).toBe("new-access-token");
|
|
135
|
+
expect(mockRefreshOAuth2Token).toHaveBeenCalledTimes(1);
|
|
136
|
+
expect(mockRefreshOAuth2Token).toHaveBeenCalledWith(
|
|
137
|
+
"https://auth.openai.com/oauth/token",
|
|
138
|
+
"app_EMoamEEZ73f0CkXaXp7hrann",
|
|
139
|
+
"old-refresh",
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
// Verify new tokens are stored
|
|
143
|
+
expect(mockSetSecureKey).toHaveBeenCalledWith(
|
|
144
|
+
`${PREFIX}/access_token`,
|
|
145
|
+
"new-access-token",
|
|
146
|
+
);
|
|
147
|
+
expect(mockSetSecureKey).toHaveBeenCalledWith(
|
|
148
|
+
`${PREFIX}/refresh_token`,
|
|
149
|
+
"new-refresh-token",
|
|
150
|
+
);
|
|
151
|
+
// expires_at should be stored as well
|
|
152
|
+
const expiresAtCall = mockSetSecureKey.mock.calls.find(
|
|
153
|
+
(c) => c[0] === `${PREFIX}/expires_at`,
|
|
154
|
+
);
|
|
155
|
+
expect(expiresAtCall).toBeDefined();
|
|
156
|
+
const storedExpiresAt = Number(expiresAtCall![1]);
|
|
157
|
+
const now = Math.floor(Date.now() / 1000);
|
|
158
|
+
// Should be approximately now + 3600 (within 5 seconds tolerance)
|
|
159
|
+
expect(storedExpiresAt).toBeGreaterThan(now + 3590);
|
|
160
|
+
expect(storedExpiresAt).toBeLessThanOrEqual(now + 3610);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
test("refreshes token when about to expire (within 5-minute margin)", async () => {
|
|
164
|
+
setSecureKeyMap({
|
|
165
|
+
[`${PREFIX}/access_token`]: "old-token",
|
|
166
|
+
[`${PREFIX}/refresh_token`]: "old-refresh",
|
|
167
|
+
[`${PREFIX}/expires_at`]: futureTimestamp(60), // only 1 minute left
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
const result = await getValidCodexAccessToken(PREFIX);
|
|
171
|
+
|
|
172
|
+
expect(result).toBe("new-access-token");
|
|
173
|
+
expect(mockRefreshOAuth2Token).toHaveBeenCalledTimes(1);
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
test("concurrent refresh calls are deduplicated (mutex)", async () => {
|
|
177
|
+
setSecureKeyMap({
|
|
178
|
+
[`${PREFIX}/access_token`]: "old-token",
|
|
179
|
+
[`${PREFIX}/refresh_token`]: "old-refresh",
|
|
180
|
+
[`${PREFIX}/expires_at`]: pastTimestamp(60),
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
// Use a deferred promise to control when the refresh completes.
|
|
184
|
+
// We create it upfront so the mock captures it synchronously.
|
|
185
|
+
let resolveRefresh!: (v: {
|
|
186
|
+
accessToken: string;
|
|
187
|
+
refreshToken: string;
|
|
188
|
+
expiresIn: number;
|
|
189
|
+
}) => void;
|
|
190
|
+
const refreshPromise = new Promise<{
|
|
191
|
+
accessToken: string;
|
|
192
|
+
refreshToken: string;
|
|
193
|
+
expiresIn: number;
|
|
194
|
+
}>((resolve) => {
|
|
195
|
+
resolveRefresh = resolve;
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
mockRefreshOAuth2Token.mockImplementation(() => refreshPromise);
|
|
199
|
+
|
|
200
|
+
// Fire two concurrent refreshes
|
|
201
|
+
const p1 = getValidCodexAccessToken(PREFIX);
|
|
202
|
+
const p2 = getValidCodexAccessToken(PREFIX);
|
|
203
|
+
|
|
204
|
+
// Allow the async get-key calls to settle before resolving refresh
|
|
205
|
+
await new Promise((r) => setTimeout(r, 10));
|
|
206
|
+
|
|
207
|
+
// Resolve the single in-flight refresh
|
|
208
|
+
resolveRefresh({
|
|
209
|
+
accessToken: "shared-new-token",
|
|
210
|
+
refreshToken: "shared-new-refresh",
|
|
211
|
+
expiresIn: 3600,
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
const [r1, r2] = await Promise.all([p1, p2]);
|
|
215
|
+
|
|
216
|
+
expect(r1).toBe("shared-new-token");
|
|
217
|
+
expect(r2).toBe("shared-new-token");
|
|
218
|
+
// Only one refresh call should have been made
|
|
219
|
+
expect(mockRefreshOAuth2Token).toHaveBeenCalledTimes(1);
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
test("falls back to existing token when refresh fails", async () => {
|
|
223
|
+
setSecureKeyMap({
|
|
224
|
+
[`${PREFIX}/access_token`]: "old-token",
|
|
225
|
+
[`${PREFIX}/refresh_token`]: "old-refresh",
|
|
226
|
+
[`${PREFIX}/expires_at`]: pastTimestamp(60),
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
mockRefreshOAuth2Token.mockImplementation(async () => {
|
|
230
|
+
throw new Error("OAuth2 token refresh failed (HTTP 400: invalid_grant)");
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
const result = await getValidCodexAccessToken(PREFIX);
|
|
234
|
+
|
|
235
|
+
// Should fall back to the existing access token
|
|
236
|
+
expect(result).toBe("old-token");
|
|
237
|
+
expect(mockRefreshOAuth2Token).toHaveBeenCalledTimes(1);
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
test("falls back to existing token when no refresh token available", async () => {
|
|
241
|
+
setSecureKeyMap({
|
|
242
|
+
[`${PREFIX}/access_token`]: "old-token",
|
|
243
|
+
// no refresh_token
|
|
244
|
+
[`${PREFIX}/expires_at`]: pastTimestamp(60),
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
const result = await getValidCodexAccessToken(PREFIX);
|
|
248
|
+
|
|
249
|
+
// Should return the existing access token
|
|
250
|
+
expect(result).toBe("old-token");
|
|
251
|
+
// Should not attempt a refresh
|
|
252
|
+
expect(mockRefreshOAuth2Token).not.toHaveBeenCalled();
|
|
253
|
+
});
|
|
254
|
+
});
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
import { AnthropicProvider } from "../anthropic/client.js";
|
|
22
22
|
import { FireworksProvider } from "../fireworks/client.js";
|
|
23
23
|
import { GeminiProvider } from "../gemini/client.js";
|
|
24
|
+
import { MinimaxProvider } from "../minimax/client.js";
|
|
24
25
|
import { PROVIDER_CATALOG } from "../model-catalog.js";
|
|
25
26
|
import { OllamaProvider } from "../ollama/client.js";
|
|
26
27
|
import { OpenAIChatCompletionsProvider } from "../openai/chat-completions-provider.js";
|
|
@@ -41,6 +42,8 @@ export interface AdapterCreateOpts {
|
|
|
41
42
|
baseURL?: string;
|
|
42
43
|
/** Forwarded to providers that wire native provider-side web search. */
|
|
43
44
|
useNativeWebSearch: boolean;
|
|
45
|
+
/** When true, the OpenAI adapter targets the Codex subscription endpoint. */
|
|
46
|
+
codexSubscription?: boolean;
|
|
44
47
|
}
|
|
45
48
|
|
|
46
49
|
type AdapterFactory = (opts: AdapterCreateOpts) => Provider;
|
|
@@ -65,10 +68,18 @@ const ADAPTER_FACTORIES: Record<string, AdapterFactory> = {
|
|
|
65
68
|
streamTimeoutMs,
|
|
66
69
|
...(baseURL ? { baseURL } : {}),
|
|
67
70
|
}),
|
|
68
|
-
openai: ({
|
|
71
|
+
openai: ({
|
|
72
|
+
apiKey,
|
|
73
|
+
model,
|
|
74
|
+
streamTimeoutMs,
|
|
75
|
+
baseURL,
|
|
76
|
+
useNativeWebSearch,
|
|
77
|
+
codexSubscription,
|
|
78
|
+
}) =>
|
|
69
79
|
new OpenAIResponsesProvider(apiKey, model, {
|
|
70
80
|
useNativeWebSearch,
|
|
71
81
|
streamTimeoutMs,
|
|
82
|
+
codexSubscription,
|
|
72
83
|
...(baseURL ? { baseURL } : {}),
|
|
73
84
|
}),
|
|
74
85
|
gemini: ({ apiKey, model, streamTimeoutMs, baseURL }) =>
|
|
@@ -101,6 +112,8 @@ const ADAPTER_FACTORIES: Record<string, AdapterFactory> = {
|
|
|
101
112
|
streamTimeoutMs,
|
|
102
113
|
...(baseURL ? { baseURL } : {}),
|
|
103
114
|
}),
|
|
115
|
+
minimax: ({ apiKey, model, streamTimeoutMs }) =>
|
|
116
|
+
new MinimaxProvider(apiKey, model, { streamTimeoutMs }),
|
|
104
117
|
};
|
|
105
118
|
|
|
106
119
|
/**
|
|
@@ -176,12 +189,16 @@ export function createAdapterFromConnection(
|
|
|
176
189
|
const baseURL =
|
|
177
190
|
resolvedAuth.kind === "header" ? resolvedAuth.baseUrl : undefined;
|
|
178
191
|
|
|
192
|
+
const codexSubscription =
|
|
193
|
+
connection.auth.type === "oauth_subscription" && provider === "openai";
|
|
194
|
+
|
|
179
195
|
const adapter = buildProviderAdapter(provider, {
|
|
180
196
|
apiKey,
|
|
181
197
|
model: opts.model,
|
|
182
198
|
streamTimeoutMs: opts.streamTimeoutMs ?? 1_800_000,
|
|
183
199
|
baseURL,
|
|
184
200
|
useNativeWebSearch: opts.useNativeWebSearch ?? false,
|
|
201
|
+
codexSubscription,
|
|
185
202
|
});
|
|
186
203
|
if (!adapter) return null;
|
|
187
204
|
|
|
@@ -9,13 +9,13 @@ import { PROVIDER_CATALOG } from "../model-catalog.js";
|
|
|
9
9
|
/**
|
|
10
10
|
* Auth configuration stored in the `provider_connections` table.
|
|
11
11
|
*
|
|
12
|
-
*
|
|
12
|
+
* Runtime-supported variants:
|
|
13
13
|
* - api_key: look up `credential` in vault, inject as bearer/provider header.
|
|
14
14
|
* - platform: route via Vellum managed proxy; no client-side credential.
|
|
15
15
|
* - none: no auth (e.g. Ollama running locally).
|
|
16
|
+
* - oauth_subscription: OAuth-based subscription auth (e.g. ChatGPT Codex).
|
|
16
17
|
*
|
|
17
|
-
*
|
|
18
|
-
* - oauth_subscription: OAuth-based subscription auth.
|
|
18
|
+
* Schema-accepted variants (runtime rejects with a clear "not yet shipped" error):
|
|
19
19
|
* - service_account: service-account credentials (Vertex AI, Bedrock).
|
|
20
20
|
*/
|
|
21
21
|
export const AuthSchema = z.discriminatedUnion("type", [
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Automatic token refresh for ChatGPT Codex OAuth (subscription auth).
|
|
3
|
+
*
|
|
4
|
+
* OpenAI rotates refresh tokens on every use — concurrent refreshes will
|
|
5
|
+
* invalidate one token. A module-level mutex prevents this race.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { refreshOAuth2Token } from "../../security/oauth2.js";
|
|
9
|
+
import {
|
|
10
|
+
getSecureKeyAsync,
|
|
11
|
+
setSecureKeyAsync,
|
|
12
|
+
} from "../../security/secure-keys.js";
|
|
13
|
+
import { getLogger } from "../../util/logger.js";
|
|
14
|
+
|
|
15
|
+
const log = getLogger("codex-token-refresh");
|
|
16
|
+
|
|
17
|
+
const CODEX_TOKEN_URL = "https://auth.openai.com/oauth/token";
|
|
18
|
+
const CODEX_CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
|
|
19
|
+
|
|
20
|
+
/** Refresh 5 minutes before expiry to avoid using a nearly-expired token. */
|
|
21
|
+
const REFRESH_MARGIN_SECONDS = 300;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Module-level mutex to prevent concurrent refresh races.
|
|
25
|
+
* OpenAI rotates refresh tokens on every use — two concurrent refreshes
|
|
26
|
+
* will invalidate one token.
|
|
27
|
+
*/
|
|
28
|
+
let refreshInFlight: Promise<string | null> | null = null;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Return a valid Codex access token, refreshing transparently if expired.
|
|
32
|
+
*
|
|
33
|
+
* @param credentialPrefix - Credential key prefix, e.g. `"credential/chatgpt"`.
|
|
34
|
+
* The function reads `<prefix>/access_token`, `<prefix>/refresh_token`,
|
|
35
|
+
* and `<prefix>/expires_at` from the credential store.
|
|
36
|
+
* @returns The access token string, or `null` if no token is stored.
|
|
37
|
+
*/
|
|
38
|
+
export async function getValidCodexAccessToken(
|
|
39
|
+
credentialPrefix: string,
|
|
40
|
+
): Promise<string | null> {
|
|
41
|
+
const accessToken = await getSecureKeyAsync(
|
|
42
|
+
`${credentialPrefix}/access_token`,
|
|
43
|
+
);
|
|
44
|
+
if (!accessToken) return null;
|
|
45
|
+
|
|
46
|
+
const expiresAtStr = await getSecureKeyAsync(
|
|
47
|
+
`${credentialPrefix}/expires_at`,
|
|
48
|
+
);
|
|
49
|
+
if (!expiresAtStr) return accessToken; // no expiry info — use token as-is
|
|
50
|
+
|
|
51
|
+
const expiresAt = Number(expiresAtStr);
|
|
52
|
+
const now = Date.now() / 1000;
|
|
53
|
+
|
|
54
|
+
if (now < expiresAt - REFRESH_MARGIN_SECONDS) {
|
|
55
|
+
return accessToken; // token is still fresh
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Token is expired or about to expire — refresh it.
|
|
59
|
+
// Use mutex to prevent concurrent refresh races.
|
|
60
|
+
if (refreshInFlight) {
|
|
61
|
+
return await refreshInFlight;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
refreshInFlight = doRefresh(credentialPrefix);
|
|
65
|
+
try {
|
|
66
|
+
return await refreshInFlight;
|
|
67
|
+
} finally {
|
|
68
|
+
refreshInFlight = null;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async function doRefresh(credentialPrefix: string): Promise<string | null> {
|
|
73
|
+
const refreshToken = await getSecureKeyAsync(
|
|
74
|
+
`${credentialPrefix}/refresh_token`,
|
|
75
|
+
);
|
|
76
|
+
if (!refreshToken) {
|
|
77
|
+
log.warn("No refresh token available for Codex OAuth");
|
|
78
|
+
// Return the existing access token — it might still work even if expired
|
|
79
|
+
return (
|
|
80
|
+
(await getSecureKeyAsync(`${credentialPrefix}/access_token`)) ?? null
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
const result = await refreshOAuth2Token(
|
|
86
|
+
CODEX_TOKEN_URL,
|
|
87
|
+
CODEX_CLIENT_ID,
|
|
88
|
+
refreshToken,
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
// Store the new tokens
|
|
92
|
+
await setSecureKeyAsync(
|
|
93
|
+
`${credentialPrefix}/access_token`,
|
|
94
|
+
result.accessToken,
|
|
95
|
+
);
|
|
96
|
+
if (result.refreshToken) {
|
|
97
|
+
await setSecureKeyAsync(
|
|
98
|
+
`${credentialPrefix}/refresh_token`,
|
|
99
|
+
result.refreshToken,
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
if (result.expiresIn) {
|
|
103
|
+
const newExpiresAt = Math.floor(Date.now() / 1000 + result.expiresIn);
|
|
104
|
+
await setSecureKeyAsync(
|
|
105
|
+
`${credentialPrefix}/expires_at`,
|
|
106
|
+
String(newExpiresAt),
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
log.info("Codex OAuth token refreshed successfully");
|
|
111
|
+
return result.accessToken;
|
|
112
|
+
} catch (err) {
|
|
113
|
+
log.error({ err }, "Codex OAuth token refresh failed");
|
|
114
|
+
// Return the existing access token as fallback
|
|
115
|
+
return (
|
|
116
|
+
(await getSecureKeyAsync(`${credentialPrefix}/access_token`)) ?? null
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// ---------------------------------------------------------------------------
|
|
122
|
+
// Test helpers
|
|
123
|
+
// ---------------------------------------------------------------------------
|
|
124
|
+
|
|
125
|
+
/** @internal Test-only: reset the in-flight refresh mutex. */
|
|
126
|
+
export function _resetRefreshMutex(): void {
|
|
127
|
+
refreshInFlight = null;
|
|
128
|
+
}
|
|
@@ -2,10 +2,11 @@
|
|
|
2
2
|
* Resolves an `Auth` config into a `ResolvedAuth` that adapters consume.
|
|
3
3
|
*
|
|
4
4
|
* Resolution rules:
|
|
5
|
-
* - api_key
|
|
6
|
-
* - platform
|
|
7
|
-
* - none
|
|
8
|
-
* - oauth_subscription
|
|
5
|
+
* - api_key → fetch credential from vault → inject as bearer header
|
|
6
|
+
* - platform → build managed proxy URL and fetch the platform API key
|
|
7
|
+
* - none → pass through with no auth headers
|
|
8
|
+
* - oauth_subscription → fetch OAuth token from vault (with auto-refresh) → inject as bearer header
|
|
9
|
+
* - service_account → reject (v2 not yet shipped)
|
|
9
10
|
*/
|
|
10
11
|
|
|
11
12
|
import {
|
|
@@ -13,7 +14,11 @@ import {
|
|
|
13
14
|
resolveManagedProxyContext,
|
|
14
15
|
} from "../../providers/platform-proxy/context.js";
|
|
15
16
|
import { getSecureKeyAsync } from "../../security/secure-keys.js";
|
|
17
|
+
import { getLogger } from "../../util/logger.js";
|
|
16
18
|
import type { Auth, ResolvedAuth } from "./auth.js";
|
|
19
|
+
import { PROVIDERS_REQUIRING_BASE_URL_AND_MODELS } from "./connections.js";
|
|
20
|
+
|
|
21
|
+
const log = getLogger("resolve-auth");
|
|
17
22
|
|
|
18
23
|
export type ResolveAuthError =
|
|
19
24
|
| { code: "credential_not_found"; credential: string }
|
|
@@ -27,6 +32,19 @@ export async function resolveAuth(
|
|
|
27
32
|
): Promise<
|
|
28
33
|
{ ok: true; resolved: ResolvedAuth } | { ok: false; error: ResolveAuthError }
|
|
29
34
|
> {
|
|
35
|
+
// Defense-in-depth: strip baseUrl for providers that should not accept one.
|
|
36
|
+
// The route layer rejects base_url for non-openai-compatible providers, but
|
|
37
|
+
// this guard catches any code path that bypasses route validation (e.g.
|
|
38
|
+
// corrupted DB rows, direct calls from internal code).
|
|
39
|
+
let safeBaseUrl = opts.baseUrl;
|
|
40
|
+
if (safeBaseUrl && !PROVIDERS_REQUIRING_BASE_URL_AND_MODELS.has(provider)) {
|
|
41
|
+
log.warn(
|
|
42
|
+
{ provider, baseUrl: safeBaseUrl },
|
|
43
|
+
`Stripping baseUrl for provider "${provider}" — base_url is only valid for openai-compatible providers.`,
|
|
44
|
+
);
|
|
45
|
+
safeBaseUrl = null;
|
|
46
|
+
}
|
|
47
|
+
|
|
30
48
|
switch (auth.type) {
|
|
31
49
|
case "api_key": {
|
|
32
50
|
const value = await getSecureKeyAsync(auth.credential);
|
|
@@ -41,7 +59,7 @@ export async function resolveAuth(
|
|
|
41
59
|
resolved: {
|
|
42
60
|
kind: "header",
|
|
43
61
|
headers: { Authorization: `Bearer ${value}` },
|
|
44
|
-
...(
|
|
62
|
+
...(safeBaseUrl ? { baseUrl: safeBaseUrl } : {}),
|
|
45
63
|
},
|
|
46
64
|
};
|
|
47
65
|
}
|
|
@@ -65,7 +83,32 @@ export async function resolveAuth(
|
|
|
65
83
|
case "none":
|
|
66
84
|
return { ok: true, resolved: { kind: "none" } };
|
|
67
85
|
|
|
68
|
-
case "oauth_subscription":
|
|
86
|
+
case "oauth_subscription": {
|
|
87
|
+
// Extract the credential prefix from the credential key.
|
|
88
|
+
// The credential field stores "credential/openai-codex/access_token";
|
|
89
|
+
// we need the prefix "credential/openai-codex" for the refresh logic.
|
|
90
|
+
const credentialPrefix = auth.credential.replace(/\/access_token$/, "");
|
|
91
|
+
|
|
92
|
+
const { getValidCodexAccessToken } = await import(
|
|
93
|
+
"./codex-token-refresh.js"
|
|
94
|
+
);
|
|
95
|
+
const token = await getValidCodexAccessToken(credentialPrefix);
|
|
96
|
+
|
|
97
|
+
if (!token) {
|
|
98
|
+
return {
|
|
99
|
+
ok: false,
|
|
100
|
+
error: { code: "credential_not_found", credential: auth.credential },
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
return {
|
|
104
|
+
ok: true,
|
|
105
|
+
resolved: {
|
|
106
|
+
kind: "header",
|
|
107
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
69
112
|
case "service_account":
|
|
70
113
|
return {
|
|
71
114
|
ok: false,
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import OpenAI from "openai";
|
|
2
|
+
|
|
3
|
+
import { getLogger } from "../../util/logger.js";
|
|
4
|
+
import { OpenAIChatCompletionsProvider } from "../openai/chat-completions-provider.js";
|
|
5
|
+
|
|
6
|
+
const log = getLogger("minimax-client");
|
|
7
|
+
|
|
8
|
+
/** Validation-specific timeout (10s) so a stalled network doesn't block key submission. */
|
|
9
|
+
const VALIDATION_TIMEOUT_MS = 10_000;
|
|
10
|
+
|
|
11
|
+
export interface MinimaxProviderOptions {
|
|
12
|
+
apiKey?: string;
|
|
13
|
+
baseURL?: string;
|
|
14
|
+
streamTimeoutMs?: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const DEFAULT_MINIMAX_BASE_URL = "https://api.minimax.io/v1";
|
|
18
|
+
const FALLBACK_MINIMAX_BASE_URL = "https://api.minimaxi.com/v1";
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Validate a MiniMax API key by testing against the default URL first,
|
|
22
|
+
* then the fallback URL if the default fails. Both URLs must fail with
|
|
23
|
+
* definitive errors (401/403) for the key to be rejected. Transient errors
|
|
24
|
+
* (429, 5xx, network) allow the key to be stored.
|
|
25
|
+
*/
|
|
26
|
+
export async function validateMinimaxApiKey(
|
|
27
|
+
apiKey: string,
|
|
28
|
+
): Promise<{ valid: true } | { valid: false; reason: string }> {
|
|
29
|
+
// Try default URL first
|
|
30
|
+
const defaultResult = await tryValidate(apiKey, DEFAULT_MINIMAX_BASE_URL);
|
|
31
|
+
if (defaultResult.valid && !defaultResult.transient) {
|
|
32
|
+
return { valid: true };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Default failed or was transient — try fallback URL
|
|
36
|
+
const fallbackResult = await tryValidate(apiKey, FALLBACK_MINIMAX_BASE_URL);
|
|
37
|
+
if (fallbackResult.valid) {
|
|
38
|
+
return { valid: true };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Both URLs failed definitively — reject the key
|
|
42
|
+
return { valid: false, reason: fallbackResult.reason };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async function tryValidate(
|
|
46
|
+
apiKey: string,
|
|
47
|
+
baseURL: string,
|
|
48
|
+
): Promise<
|
|
49
|
+
| { valid: true; transient: false }
|
|
50
|
+
| { valid: true; transient: true }
|
|
51
|
+
| { valid: false; reason: string }
|
|
52
|
+
> {
|
|
53
|
+
try {
|
|
54
|
+
const client = new OpenAI({
|
|
55
|
+
apiKey,
|
|
56
|
+
baseURL,
|
|
57
|
+
timeout: VALIDATION_TIMEOUT_MS,
|
|
58
|
+
maxRetries: 0,
|
|
59
|
+
});
|
|
60
|
+
await client.models.list();
|
|
61
|
+
return { valid: true, transient: false };
|
|
62
|
+
} catch (error) {
|
|
63
|
+
if (error instanceof OpenAI.APIError) {
|
|
64
|
+
if (error.status === 401) {
|
|
65
|
+
return { valid: false, reason: "API key is invalid or expired." };
|
|
66
|
+
}
|
|
67
|
+
if (error.status === 403) {
|
|
68
|
+
return {
|
|
69
|
+
valid: false,
|
|
70
|
+
reason: `MiniMax API error (${error.status}): ${error.message}`,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
// Transient errors (429, 5xx, etc.) — try the other URL
|
|
74
|
+
log.warn(
|
|
75
|
+
{ status: error.status, baseURL },
|
|
76
|
+
"MiniMax API returned a transient error during key validation — trying fallback",
|
|
77
|
+
);
|
|
78
|
+
return { valid: true, transient: true };
|
|
79
|
+
}
|
|
80
|
+
// Network errors — try the other URL
|
|
81
|
+
log.warn(
|
|
82
|
+
{
|
|
83
|
+
error: error instanceof Error ? error.message : String(error),
|
|
84
|
+
baseURL,
|
|
85
|
+
},
|
|
86
|
+
"Network error during MiniMax key validation — trying fallback",
|
|
87
|
+
);
|
|
88
|
+
return { valid: true, transient: true };
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export class MinimaxProvider extends OpenAIChatCompletionsProvider {
|
|
93
|
+
constructor(
|
|
94
|
+
apiKey: string,
|
|
95
|
+
model: string,
|
|
96
|
+
options: MinimaxProviderOptions = {},
|
|
97
|
+
) {
|
|
98
|
+
const baseURL = options.baseURL?.trim() || DEFAULT_MINIMAX_BASE_URL;
|
|
99
|
+
super(apiKey, model, {
|
|
100
|
+
baseURL,
|
|
101
|
+
providerName: "minimax",
|
|
102
|
+
providerLabel: "MiniMax",
|
|
103
|
+
streamTimeoutMs: options.streamTimeoutMs,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
}
|