@bitkyc08/opencodex 2.60.0 → 2.61.0-preview.20260922
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/AGENTS_INSTALL.md +64 -0
- package/README.md +28 -1
- package/bin/ocx.mjs +382 -209
- package/gui/dist/assets/App-E64Rzjap.js +50 -0
- package/gui/dist/assets/Tray-_nfzD8k4.js +1 -0
- package/gui/dist/assets/index-DpdfZWMK.js +86 -0
- package/gui/dist/assets/index-_bpvxJu0.css +1 -0
- package/gui/dist/assets/usage-companion-chart-DtoK7T6h.js +1 -0
- package/gui/dist/favicon.png +0 -0
- package/gui/dist/index.html +2 -2
- package/gui/dist/provider-icons/stepfun-color.svg +1 -0
- package/package.json +5 -1
- package/src/adapters/anthropic.ts +16 -0
- package/src/adapters/coding-agent/protocol.ts +36 -6
- package/src/adapters/coding-agent/turn.ts +10 -2
- package/src/adapters/command-code.ts +2 -1
- package/src/adapters/cursor/catalog.ts +51 -7
- package/src/adapters/cursor/protobuf-request.ts +6 -3
- package/src/adapters/cursor/request-builder.ts +13 -3
- package/src/adapters/cursor.ts +11 -2
- package/src/adapters/declaration-carrier.ts +45 -0
- package/src/adapters/devin.ts +75 -23
- package/src/adapters/google-antigravity-wire.ts +5 -2
- package/src/adapters/google-errors.ts +7 -1
- package/src/adapters/google.ts +29 -5
- package/src/adapters/image.ts +4 -1
- package/src/adapters/input-media-guard.ts +21 -9
- package/src/adapters/kiro/usage.ts +3 -2
- package/src/adapters/kiro-tool-fallback.ts +1 -1
- package/src/adapters/ollama-native.ts +6 -0
- package/src/adapters/openai-chat/developer-role.ts +61 -0
- package/src/adapters/openai-chat/messages.ts +46 -27
- package/src/adapters/openai-chat/parallel-tool-calls.ts +32 -0
- package/src/adapters/openai-chat/passthrough.ts +33 -9
- package/src/adapters/openai-chat/reasoning-wire.ts +89 -0
- package/src/adapters/openai-chat.ts +18 -57
- package/src/adapters/openai-responses/passthrough.ts +2 -0
- package/src/adapters/registry.ts +3 -2
- package/src/adapters/run-turn-queue.ts +178 -29
- package/src/adapters/xai-web-search.ts +16 -1
- package/src/bridge/errors.ts +8 -2
- package/src/bridge/response-json.ts +9 -1
- package/src/bridge/sse.ts +10 -0
- package/src/chat/inbound.ts +141 -5
- package/src/claude/desktop-3p.ts +7 -1
- package/src/claude/desktop-first-party.ts +183 -0
- package/src/claude/desktop-gateway-state.ts +41 -0
- package/src/claude/inbound-content-options.ts +6 -0
- package/src/claude/inbound.ts +32 -6
- package/src/claude/intercept/connect-proxy.ts +179 -0
- package/src/claude/intercept/listener.ts +122 -0
- package/src/claude/intercept/local-ca.ts +298 -0
- package/src/claude/intercept/runtime.ts +98 -0
- package/src/claude/intercept/settings.ts +189 -0
- package/src/cli/access.ts +87 -0
- package/src/cli/account-auth.ts +19 -0
- package/src/cli/capabilities.ts +31 -0
- package/src/cli/claude-desktop.ts +206 -16
- package/src/cli/codex-shim-autorestore.ts +3 -0
- package/src/cli/companion.ts +56 -0
- package/src/cli/dispatch.ts +43 -4
- package/src/cli/ensure-desired-integrations.ts +43 -5
- package/src/cli/help.ts +7 -9
- package/src/cli/index.ts +200 -61
- package/src/cli/init.ts +8 -0
- package/src/cli/integrations.ts +7 -1
- package/src/cli/registry.ts +41 -2
- package/src/cli/resolve.ts +230 -0
- package/src/cli/root.ts +24 -1
- package/src/cli/start-ownership-publication.ts +56 -0
- package/src/cli/status-probes.ts +2 -18
- package/src/cli/status.ts +62 -0
- package/src/cli/stop-report.ts +143 -0
- package/src/cli/uninstall-plan.ts +9 -0
- package/src/client/machine-listener.ts +2 -5
- package/src/clients/aside-profiles.ts +4 -0
- package/src/clients/config-export/zcode-store.ts +157 -0
- package/src/clients/config-export.ts +36 -0
- package/src/codex/app-server-processes.ts +72 -40
- package/src/codex/auth-api/login-flow.ts +6 -1
- package/src/codex/autostart-health.ts +28 -0
- package/src/codex/catalog/build-entries.ts +2 -2
- package/src/codex/catalog/effort.ts +3 -3
- package/src/codex/catalog/provider-models.ts +24 -15
- package/src/codex/catalog/retained-sync.ts +2 -2
- package/src/codex/convergence.ts +2 -2
- package/src/codex/history-provider.ts +12 -1
- package/src/codex/inject/config-toml.ts +41 -6
- package/src/codex/inject/paginated-openai-compat.ts +90 -0
- package/src/codex/inject.ts +18 -15
- package/src/codex/injected-marker.ts +18 -0
- package/src/codex/main-account.ts +6 -0
- package/src/codex/model-cache.ts +52 -6
- package/src/codex/model-entitlement-admission.ts +59 -0
- package/src/codex/model-entitlements.ts +87 -44
- package/src/codex/native-main-admission.ts +83 -0
- package/src/codex/routing/health-store.ts +39 -0
- package/src/codex/routing/selection.ts +37 -1
- package/src/codex/routing.ts +5 -41
- package/src/codex/shim-templates.ts +29 -3
- package/src/companion/settings.ts +132 -0
- package/src/config/atomic-write.ts +117 -5
- package/src/config/load-degrade.ts +34 -7
- package/src/config/process-state.ts +1 -1
- package/src/config/schema/config-schema.ts +27 -1
- package/src/config/schema/leaf-validators.ts +47 -0
- package/src/config.ts +1 -1
- package/src/generated/compatibility-version.json +418 -174
- package/src/integrations/config-io.ts +44 -10
- package/src/integrations/merge.ts +120 -13
- package/src/integrations/mutation-plan.ts +124 -18
- package/src/integrations/registry.ts +38 -0
- package/src/integrations/state.ts +78 -45
- package/src/integrations/target.ts +208 -0
- package/src/integrations/writer.ts +49 -11
- package/src/lab/conformance/fixture-provider.ts +5 -0
- package/src/lib/browser-launch-notice.ts +59 -0
- package/src/lib/bun-runtime.ts +6 -2
- package/src/lib/debug.ts +40 -0
- package/src/lib/open-url.ts +51 -7
- package/src/lib/package-tree-integrity.ts +2 -1
- package/src/lib/package-version.ts +8 -0
- package/src/lib/provider-egress.ts +310 -0
- package/src/lib/provider-outbound.ts +59 -14
- package/src/lib/proxy-env.ts +82 -7
- package/src/lib/request-execution-budget.ts +72 -0
- package/src/lib/request-failure-attribution.ts +183 -0
- package/src/lib/request-failure-model.ts +236 -0
- package/src/lib/request-resend-gate.ts +138 -0
- package/src/lib/standalone.ts +16 -0
- package/src/lib/upstream-retry.ts +167 -16
- package/src/lib/winsw.ts +2 -2
- package/src/oauth/index.ts +24 -1
- package/src/oauth/login-cli.ts +80 -29
- package/src/providers/api-key-resolve.ts +133 -0
- package/src/providers/api-key-selection.ts +5 -1
- package/src/providers/key-failover.ts +31 -1
- package/src/providers/key-store.ts +34 -110
- package/src/providers/model-rename-fields.ts +147 -0
- package/src/providers/model-rename-migration.ts +124 -37
- package/src/providers/quota/vendor-probes-key.ts +37 -22
- package/src/providers/reasoning-metadata.ts +43 -18
- package/src/providers/registry/entries-core.ts +9 -4
- package/src/providers/registry/entries-extended.ts +29 -4
- package/src/providers/registry/model-seeds.ts +47 -10
- package/src/providers/xai-transport.ts +12 -1
- package/src/reasoning-effort.ts +8 -0
- package/src/responses/function-call-compat.ts +38 -1
- package/src/responses/inline-document.ts +65 -0
- package/src/responses/input-media.ts +42 -8
- package/src/responses/muse-tool-name-alias.ts +19 -0
- package/src/responses/parser-content.ts +8 -2
- package/src/responses/parser-tools.ts +3 -0
- package/src/responses/parser.ts +3 -1
- package/src/responses/schema.ts +3 -0
- package/src/router.ts +17 -2
- package/src/server/admission-model-scope.ts +219 -0
- package/src/server/audio-live.ts +9 -3
- package/src/server/audio-upstream.ts +18 -0
- package/src/server/auth-cors.ts +26 -0
- package/src/server/chat-completions.ts +55 -2
- package/src/server/chat-native.ts +19 -4
- package/src/server/claude-messages.ts +55 -17
- package/src/server/grok-responses-snapshot-repair.ts +113 -11
- package/src/server/gui-freshness.ts +103 -0
- package/src/server/gui-static.ts +7 -9
- package/src/server/images.ts +59 -6
- package/src/server/index/claude-intercept-lifecycle.ts +49 -0
- package/src/server/index/serve-options.ts +56 -10
- package/src/server/index/spend-ledger-lifecycle.ts +34 -8
- package/src/server/index/startup-warnings.ts +24 -0
- package/src/server/index.ts +21 -28
- package/src/server/lifecycle.ts +4 -4
- package/src/server/live-call-bindings.ts +6 -0
- package/src/server/live.ts +88 -3
- package/src/server/management/agent-settings-routes.ts +121 -36
- package/src/server/management/companion-routes.ts +77 -0
- package/src/server/management/logs-usage-routes.ts +19 -0
- package/src/server/management/native-integration-routes.ts +103 -6
- package/src/server/management/oauth-account-routes.ts +45 -7
- package/src/server/management/route-registry.ts +6 -0
- package/src/server/management/shared.ts +18 -1
- package/src/server/management/usage-timeline-routes.ts +44 -0
- package/src/server/management-api.ts +8 -9
- package/src/server/proxy-liveness.ts +75 -0
- package/src/server/relay.ts +19 -2
- package/src/server/request-log-failure-attribution.ts +99 -0
- package/src/server/request-log.ts +114 -0
- package/src/server/request-metrics.ts +92 -30
- package/src/server/responses/codex-ws-wire.ts +34 -8
- package/src/server/responses/combo-stream-preflight.ts +168 -6
- package/src/server/responses/compact.ts +11 -0
- package/src/server/responses/core-opaque-recovery.ts +90 -0
- package/src/server/responses/fetch-helpers.ts +124 -8
- package/src/server/responses/input-admission.ts +10 -0
- package/src/server/responses/passthrough-delivery.ts +14 -1
- package/src/server/responses/passthrough-dispatch.ts +179 -35
- package/src/server/responses/passthrough-error.ts +27 -8
- package/src/server/responses/request-prepare.ts +42 -1
- package/src/server/responses/request-send-budget.ts +12 -0
- package/src/server/responses/request-transport.ts +24 -4
- package/src/server/responses/reset-replay.ts +108 -0
- package/src/server/responses-request-tool-scope.ts +214 -0
- package/src/server/responses-undeclared-tool-guard.ts +4 -1
- package/src/server/search.ts +25 -1
- package/src/server/usage-ledger-retention.ts +73 -0
- package/src/service/cli.ts +48 -2
- package/src/service/health.ts +3 -2
- package/src/service/install-state-contract.d.mts +27 -0
- package/src/service/install-state-contract.mjs +34 -0
- package/src/service/launchd.ts +1 -1
- package/src/service/orchestration.ts +2 -4
- package/src/service/ownership-compatibility.ts +164 -0
- package/src/service/ownership-mutation-lease.d.mts +32 -0
- package/src/service/ownership-mutation-lease.mjs +211 -0
- package/src/service/repair.ts +45 -1
- package/src/service/state-lock.ts +269 -0
- package/src/service/state-record.d.mts +36 -0
- package/src/service/state-record.mjs +138 -0
- package/src/service/state.ts +582 -68
- package/src/service/windows-taskxml.ts +11 -10
- package/src/service.ts +7 -3
- package/src/tray/windows-tray.ps1 +1 -1
- package/src/types/config.ts +37 -0
- package/src/types/provider.ts +73 -0
- package/src/types/request.ts +28 -2
- package/src/types/tools.ts +19 -0
- package/src/types.ts +3 -0
- package/src/update/index.ts +207 -63
- package/src/update/job.ts +9 -5
- package/src/update/ownership-transaction.ts +47 -0
- package/src/update/restart-ownership.ts +54 -0
- package/src/update/runtime-ownership.d.mts +40 -0
- package/src/update/runtime-ownership.mjs +122 -0
- package/src/usage/attempt-delivery.ts +198 -0
- package/src/usage/cache-diagnostic.ts +305 -0
- package/src/usage/failure-fingerprint.ts +118 -0
- package/src/usage/failure-projection-cache.ts +174 -0
- package/src/usage/failure-projection.ts +174 -0
- package/src/usage/ledger-retention.ts +165 -0
- package/src/usage/log.ts +126 -79
- package/src/usage/request-outcome.ts +150 -0
- package/src/usage/retention-contract.ts +28 -0
- package/src/usage/summary.ts +2 -2
- package/src/usage/telemetry-contract.ts +237 -0
- package/src/usage/timeline.ts +236 -0
- package/src/web-search/alpha-search.ts +21 -1
- package/gui/dist/assets/index-BTuCbqQd.css +0 -1
- package/gui/dist/assets/index-DoBVdPHP.js +0 -134
|
@@ -72,10 +72,10 @@ import {
|
|
|
72
72
|
VOLCENGINE_PLAN_TEXT_ONLY_MODELS,
|
|
73
73
|
ALIBABA_INTL_TOKEN_PLAN_INPUT_MODALITIES,
|
|
74
74
|
KIMI_API_MODELS,
|
|
75
|
-
KIMI_CODING_MODELS,
|
|
76
75
|
KIMI_THINKING_MODELS,
|
|
77
76
|
KIMI_CODING_NO_REASONING_MODELS,
|
|
78
77
|
KIMI_API_NO_REASONING_MODELS,
|
|
78
|
+
KIMI_CODING_LIVE_MODELS,
|
|
79
79
|
KIMI_CODING_REASONING_EFFORTS,
|
|
80
80
|
KIMI_CODING_DEFAULT_REASONING_EFFORTS,
|
|
81
81
|
KIMI_CODING_REASONING_EFFORT_MAPS,
|
|
@@ -102,6 +102,11 @@ import {
|
|
|
102
102
|
OPPER_MODEL_CONTEXT_WINDOWS,
|
|
103
103
|
OPPER_MODEL_MAX_OUTPUT_TOKENS,
|
|
104
104
|
OPPER_MODEL_INPUT_MODALITIES,
|
|
105
|
+
STEPFUN_MODELS,
|
|
106
|
+
STEPFUN_MODEL_CONTEXT_WINDOWS,
|
|
107
|
+
STEPFUN_MODEL_INPUT_MODALITIES,
|
|
108
|
+
STEPFUN_NO_VISION_MODELS,
|
|
109
|
+
STEPFUN_REASONING_EFFORTS,
|
|
105
110
|
} from "./model-seeds";
|
|
106
111
|
|
|
107
112
|
export const PROVIDER_REGISTRY_EXTENDED: readonly ProviderRegistryEntry[] = [
|
|
@@ -1008,13 +1013,17 @@ export const PROVIDER_REGISTRY_EXTENDED: readonly ProviderRegistryEntry[] = [
|
|
|
1008
1013
|
},
|
|
1009
1014
|
{
|
|
1010
1015
|
id: "kimi-code", label: "Kimi (coding)", baseUrl: "https://api.kimi.com/coding/v1", adapter: "openai-chat", authKind: "key",
|
|
1011
|
-
|
|
1016
|
+
// 260921: kimi-k2.7-code was retired from the coding endpoint; the kimi-for-coding alias
|
|
1017
|
+
// is the stable ID and currently routes to K2.8 Preview (same as the OAuth preset).
|
|
1018
|
+
dashboardUrl: "https://platform.moonshot.cn/console/api-keys", defaultModel: "kimi-for-coding",
|
|
1012
1019
|
modelSuffixBracketStrip: true,
|
|
1013
1020
|
// API-key form of the same Kimi Code Plan transport; keep cache affinity identical to OAuth.
|
|
1014
1021
|
promptCacheKey: true,
|
|
1015
1022
|
// Keep Responses tool-result adjacency aligned with the OAuth preset (#4726).
|
|
1016
1023
|
requiresAdjacentResponsesToolResults: true,
|
|
1017
|
-
|
|
1024
|
+
// 260921: same live-id picker as the OAuth preset — the retired k2.x ids are repaired
|
|
1025
|
+
// in saved configs by MODEL_RENAMES, not offered on fresh installs.
|
|
1026
|
+
models: KIMI_CODING_LIVE_MODELS,
|
|
1018
1027
|
modelContextWindows: KIMI_CODING_MODEL_CONTEXT_WINDOWS,
|
|
1019
1028
|
modelInputModalities: KIMI_CODING_MODEL_INPUT_MODALITIES,
|
|
1020
1029
|
noReasoningModels: KIMI_CODING_NO_REASONING_MODELS,
|
|
@@ -1361,5 +1370,21 @@ export const PROVIDER_REGISTRY_EXTENDED: readonly ProviderRegistryEntry[] = [
|
|
|
1361
1370
|
noVisionModels: CODEBUDDY_CN_NO_VISION_MODELS,
|
|
1362
1371
|
note: "Official CodeBuddy Code CLI (Tencent Cloud), China/internal environment. Uses the documented CODEBUDDY_API_KEY + headless CLI surface; never reads desktop sessions or private console endpoints. Region-isolated from codebuddy (Global); credentials are never exchanged across regions. v1 disables CLI tools (--tools \"\"): text/reasoning only for now. Requires `npm i -g @tencent-ai/codebuddy-code`. AUP/routing authorization flagged for maintainer security review.",
|
|
1363
1372
|
},
|
|
1373
|
+
{
|
|
1374
|
+
id: "stepfun",
|
|
1375
|
+
label: "StepFun",
|
|
1376
|
+
baseUrl: "https://api.stepfun.com/v1",
|
|
1377
|
+
adapter: "openai-chat",
|
|
1378
|
+
authKind: "key",
|
|
1379
|
+
dashboardUrl: "https://platform.stepfun.com",
|
|
1380
|
+
defaultModel: "step-5-preview",
|
|
1381
|
+
models: STEPFUN_MODELS,
|
|
1382
|
+
liveModels: true,
|
|
1383
|
+
preserveCustomDestination: true,
|
|
1384
|
+
modelContextWindows: STEPFUN_MODEL_CONTEXT_WINDOWS,
|
|
1385
|
+
modelInputModalities: STEPFUN_MODEL_INPUT_MODALITIES,
|
|
1386
|
+
noVisionModels: STEPFUN_NO_VISION_MODELS,
|
|
1387
|
+
reasoningEfforts: STEPFUN_REASONING_EFFORTS,
|
|
1388
|
+
note: "StepFun (阶跃星辰) official OpenAI-compatible API.",
|
|
1389
|
+
},
|
|
1364
1390
|
];
|
|
1365
|
-
|
|
@@ -642,11 +642,26 @@ export const ALIBABA_TOKEN_PLAN_PRESERVE_REASONING = [
|
|
|
642
642
|
export const KIMI_K3_STANDARD_CONTEXT_WINDOW = 262_144;
|
|
643
643
|
export const KIMI_K3_1M_CONTEXT_WINDOW = 1_048_576;
|
|
644
644
|
export const KIMI_CODING_K3_MODELS = ["k3", "k3[1m]"];
|
|
645
|
+
// 260921 Kimi K2.8: `kimi-for-coding` is the stable subscription alias Moonshot re-points
|
|
646
|
+
// at each coding release. Live GET /coding/v1/models lists only kimi-for-coding[-highspeed],
|
|
647
|
+
// k3, k3-256k — the k2.x ids are retired from the subscription endpoint. Since K2.8 Preview
|
|
648
|
+
// the alias serves an adjustable low/high/max thinking ladder (same wire map as k3) and a
|
|
649
|
+
// 1M context ceiling. Verified live 260921: 350K-token request accepted; upstream rejects
|
|
650
|
+
// with "model token limit: 1048576" beyond that.
|
|
651
|
+
// Evidence: https://www.kimi.com/code/docs/en/kimi-code/models.html
|
|
652
|
+
export const KIMI_CODING_K28_MODELS = ["kimi-for-coding"];
|
|
653
|
+
export const KIMI_CODING_LIVE_MODELS = [...KIMI_CODING_K3_MODELS, ...KIMI_CODING_K28_MODELS];
|
|
654
|
+
export const KIMI_CODING_ADJUSTABLE_THINKING_MODELS = [...KIMI_CODING_LIVE_MODELS];
|
|
645
655
|
export const KIMI_LEGACY_API_MODELS = ["kimi-k2.7-code", "kimi-k2.7-code-highspeed", "kimi-k2.6", "kimi-k2.5"];
|
|
646
656
|
export const KIMI_API_MODELS = ["kimi-k3", ...KIMI_LEGACY_API_MODELS];
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
657
|
+
// Every kimi coding preset record - picker, context windows, locked-parameter lists -
|
|
658
|
+
// derives from the live ids only. seeding a retired id in a metadata list would re-arm
|
|
659
|
+
// the model-rename migration on every boot (#5066): the list holds the retired id but
|
|
660
|
+
// not the live alias, so the residue guard cannot skip it. The retired ids survive only
|
|
661
|
+
// in KIMI_LEGACY_API_MODELS (moonshot platform API records); model-rename-migration
|
|
662
|
+
// repairs saved rows still naming them.
|
|
663
|
+
export const KIMI_THINKING_MODELS = KIMI_CODING_LIVE_MODELS;
|
|
664
|
+
export const KIMI_CODING_NO_REASONING_MODELS = KIMI_CODING_LIVE_MODELS.filter(id => !KIMI_CODING_ADJUSTABLE_THINKING_MODELS.includes(id));
|
|
650
665
|
export const KIMI_API_NO_REASONING_MODELS = KIMI_API_MODELS.filter(id => id !== "kimi-k3");
|
|
651
666
|
export const KIMI_CODING_K3_REASONING_EFFORTS = ["low", "high", "max"];
|
|
652
667
|
export const KIMI_CODING_K3_REASONING_EFFORT_MAP: Record<string, string> = {
|
|
@@ -658,19 +673,19 @@ export const KIMI_CODING_K3_REASONING_EFFORT_MAP: Record<string, string> = {
|
|
|
658
673
|
max: "max",
|
|
659
674
|
};
|
|
660
675
|
export const KIMI_CODING_REASONING_EFFORTS = Object.fromEntries(
|
|
661
|
-
|
|
676
|
+
KIMI_CODING_LIVE_MODELS.map(id => [id, KIMI_CODING_ADJUSTABLE_THINKING_MODELS.includes(id) ? KIMI_CODING_K3_REASONING_EFFORTS : []]),
|
|
662
677
|
);
|
|
663
678
|
export const KIMI_CODING_DEFAULT_REASONING_EFFORTS = Object.fromEntries(
|
|
664
|
-
|
|
679
|
+
KIMI_CODING_ADJUSTABLE_THINKING_MODELS.map(id => [id, "max"]),
|
|
665
680
|
);
|
|
666
681
|
export const KIMI_CODING_REASONING_EFFORT_MAPS = Object.fromEntries(
|
|
667
|
-
|
|
682
|
+
KIMI_CODING_ADJUSTABLE_THINKING_MODELS.map(id => [id, KIMI_CODING_K3_REASONING_EFFORT_MAP]),
|
|
668
683
|
);
|
|
669
684
|
export const KIMI_API_REASONING_EFFORTS = Object.fromEntries(
|
|
670
685
|
KIMI_API_MODELS.map(id => [id, id === "kimi-k3" ? ["max"] : []]),
|
|
671
686
|
);
|
|
672
|
-
export const KIMI_LOCKED_PARAMETER_MODELS =
|
|
673
|
-
export const KIMI_AUTO_TOOL_CHOICE_ONLY_MODELS = ["kimi-
|
|
687
|
+
export const KIMI_LOCKED_PARAMETER_MODELS = KIMI_CODING_LIVE_MODELS;
|
|
688
|
+
export const KIMI_AUTO_TOOL_CHOICE_ONLY_MODELS = ["kimi-for-coding"];
|
|
674
689
|
export const KIMI_API_MODEL_CONTEXT_WINDOWS: Record<string, number> = Object.fromEntries(
|
|
675
690
|
KIMI_API_MODELS.map(id => [id, id === "kimi-k3" ? KIMI_K3_1M_CONTEXT_WINDOW : 262_144]),
|
|
676
691
|
);
|
|
@@ -758,10 +773,10 @@ export const NVIDIA_NIM_NO_VISION_MODELS = [
|
|
|
758
773
|
"poolside/laguna-xs-2.1", "z-ai/glm-5.3", "z-ai/glm-5.2",
|
|
759
774
|
];
|
|
760
775
|
export const KIMI_CODING_MODEL_CONTEXT_WINDOWS: Record<string, number> = Object.fromEntries(
|
|
761
|
-
|
|
776
|
+
KIMI_CODING_LIVE_MODELS.map(id => [id, (id === "k3[1m]" || KIMI_CODING_K28_MODELS.includes(id)) ? KIMI_K3_1M_CONTEXT_WINDOW : KIMI_K3_STANDARD_CONTEXT_WINDOW]),
|
|
762
777
|
);
|
|
763
778
|
export const KIMI_CODING_MODEL_INPUT_MODALITIES = Object.fromEntries(
|
|
764
|
-
|
|
779
|
+
KIMI_CODING_ADJUSTABLE_THINKING_MODELS.map(id => [id, ["text", "image"]]),
|
|
765
780
|
);
|
|
766
781
|
export const NEURALWATT_REASONING_HISTORY_MODELS = [
|
|
767
782
|
"glm-5.3", "glm-5.3-short", "glm-5.3-flash",
|
|
@@ -1026,3 +1041,25 @@ export const OPPER_TEXT_ONLY_MODELS = ["deepseek-v4-pro", "kimi-k3"];
|
|
|
1026
1041
|
export const OPPER_MODEL_INPUT_MODALITIES: Record<string, string[]> = Object.fromEntries(
|
|
1027
1042
|
OPPER_MODELS.map(id => [id, OPPER_TEXT_ONLY_MODELS.includes(id) ? ["text"] : ["text", "image"]]),
|
|
1028
1043
|
);
|
|
1044
|
+
|
|
1045
|
+
export const STEPFUN_MODELS = [
|
|
1046
|
+
"step-5-preview",
|
|
1047
|
+
"step-3.5-flash",
|
|
1048
|
+
"step-3.7-flash",
|
|
1049
|
+
];
|
|
1050
|
+
|
|
1051
|
+
export const STEPFUN_MODEL_CONTEXT_WINDOWS: Record<string, number> = {
|
|
1052
|
+
"step-5-preview": 1_000_000,
|
|
1053
|
+
"step-3.5-flash": 256_000,
|
|
1054
|
+
"step-3.7-flash": 256_000,
|
|
1055
|
+
};
|
|
1056
|
+
|
|
1057
|
+
export const STEPFUN_MODEL_INPUT_MODALITIES: Record<string, string[]> = {
|
|
1058
|
+
"step-5-preview": ["text", "image"],
|
|
1059
|
+
"step-3.5-flash": ["text"],
|
|
1060
|
+
"step-3.7-flash": ["text", "image"],
|
|
1061
|
+
};
|
|
1062
|
+
|
|
1063
|
+
export const STEPFUN_NO_VISION_MODELS = ["step-3.5-flash"];
|
|
1064
|
+
|
|
1065
|
+
export const STEPFUN_REASONING_EFFORTS = ["low", "medium", "high"];
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { createHash, randomUUID } from "node:crypto";
|
|
2
2
|
import type { OcxProviderConfig } from "../types";
|
|
3
|
+
import { isEgressTransparentExecutor, markEgressTransparentExecutor } from "../lib/provider-egress";
|
|
4
|
+
import { configuredOutboundFetch } from "../lib/proxy-env";
|
|
3
5
|
import { resolveGithubCopilotTransport } from "./github-copilot-transport";
|
|
4
6
|
|
|
5
7
|
export const XAI_GROK_CLI_BASE_URL = "https://cli-chat-proxy.grok.com/v1";
|
|
@@ -156,9 +158,18 @@ export function resolveProviderTransport(
|
|
|
156
158
|
// transient retries reuse one id so the upstream can dedupe them. A rotated key resolves a
|
|
157
159
|
// fresh transport, which gets its own id.
|
|
158
160
|
const requestId = configuredRequestId ?? randomUUID();
|
|
159
|
-
|
|
161
|
+
// Without a configured executor the default routes through `configuredOutboundFetch` rather
|
|
162
|
+
// than the bare global fetch, so a per-provider SOCKS5 route reaches the SOCKS transport.
|
|
163
|
+
// Bare Bun fetch ignores a socks5 value, which would have sent the request unproxied while
|
|
164
|
+
// the configuration named a proxy.
|
|
165
|
+
const baseFetch = provider.fetch
|
|
166
|
+
?? markEgressTransparentExecutor(((input, init) => configuredOutboundFetch(input, init)) as typeof globalThis.fetch);
|
|
160
167
|
const attemptFetch = ((input, init) =>
|
|
161
168
|
baseFetch(input, withGeneratedRequestId(init, requestId, stableHeaders))) as typeof globalThis.fetch;
|
|
169
|
+
// This wrapper only adds a header and forwards the init, so it carries a request-scoped proxy
|
|
170
|
+
// option through to whatever it wraps — but only if what it wraps carries it too. A
|
|
171
|
+
// configured executor owns its own routing and is not assumed to.
|
|
172
|
+
if (isEgressTransparentExecutor(baseFetch)) markEgressTransparentExecutor(attemptFetch);
|
|
162
173
|
|
|
163
174
|
return {
|
|
164
175
|
...provider,
|
package/src/reasoning-effort.ts
CHANGED
|
@@ -12,6 +12,14 @@ export const CODEX_REASONING_LEVELS: { effort: string; description: string }[] =
|
|
|
12
12
|
{ effort: "ultra", description: "Maximum reasoning with automatic task delegation" },
|
|
13
13
|
];
|
|
14
14
|
|
|
15
|
+
/**
|
|
16
|
+
* One canonical rung. Catalog writers that hold a partially populated ladder derive their
|
|
17
|
+
* element type from this instead of restating `{ effort?: string }`: those restatements
|
|
18
|
+
* silently excluded `description`, so pushing a canonical rung into them is a type error
|
|
19
|
+
* that only surfaced once an unrelated import cycle through this module was broken.
|
|
20
|
+
*/
|
|
21
|
+
export type CodexReasoningLevel = (typeof CODEX_REASONING_LEVELS)[number];
|
|
22
|
+
|
|
15
23
|
const CODEX_REASONING_ORDER = CODEX_REASONING_LEVELS.map(l => l.effort);
|
|
16
24
|
const CODEX_REASONING_SET = new Set(CODEX_REASONING_ORDER);
|
|
17
25
|
|
|
@@ -33,6 +33,38 @@ function namespaceOf(value: unknown): string | undefined {
|
|
|
33
33
|
return typeof value === "string" && value !== "functions" ? value : undefined;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
/**
|
|
37
|
+
* Repair one provider spelling drift for Codex's flat shell bridge.
|
|
38
|
+
*
|
|
39
|
+
* This is schema-bound rather than a general alias: `input` is also a legitimate
|
|
40
|
+
* argument name for arbitrary caller and MCP tools. Only the exact bare
|
|
41
|
+
* `exec_command` declaration can establish that its sole string input has one faithful
|
|
42
|
+
* reading as the required string `cmd` member.
|
|
43
|
+
*/
|
|
44
|
+
function repairExecCommandInput(
|
|
45
|
+
argumentsText: string,
|
|
46
|
+
schema: FunctionCallRepairSchema,
|
|
47
|
+
): string {
|
|
48
|
+
if (schema.namespace !== undefined || schema.name !== "exec_command") return argumentsText;
|
|
49
|
+
const parameters = schema.parameters;
|
|
50
|
+
if (!isObject(parameters) || parameters.type !== "object"
|
|
51
|
+
|| !Array.isArray(parameters.required) || !parameters.required.includes("cmd")
|
|
52
|
+
|| !isObject(parameters.properties) || !isObject(parameters.properties.cmd)
|
|
53
|
+
|| parameters.properties.cmd.type !== "string") return argumentsText;
|
|
54
|
+
let parsed: unknown;
|
|
55
|
+
try {
|
|
56
|
+
parsed = JSON.parse(argumentsText);
|
|
57
|
+
} catch {
|
|
58
|
+
return argumentsText;
|
|
59
|
+
}
|
|
60
|
+
if (!isObject(parsed)) return argumentsText;
|
|
61
|
+
const keys = Object.keys(parsed);
|
|
62
|
+
if (keys.length !== 1 || keys[0] !== "input" || typeof parsed.input !== "string") {
|
|
63
|
+
return argumentsText;
|
|
64
|
+
}
|
|
65
|
+
return JSON.stringify({ cmd: parsed.input });
|
|
66
|
+
}
|
|
67
|
+
|
|
36
68
|
function selectorAllows(
|
|
37
69
|
selector: unknown,
|
|
38
70
|
lowered: unknown,
|
|
@@ -131,7 +163,12 @@ function repairItem(item: unknown, schemas: FunctionCallRepairSchemas, completed
|
|
|
131
163
|
if (unsafe) return item;
|
|
132
164
|
} catch { return item; }
|
|
133
165
|
}
|
|
134
|
-
const
|
|
166
|
+
const integerRepaired = coerceIntegerToolArguments(
|
|
167
|
+
raw || "{}",
|
|
168
|
+
schema.parameters,
|
|
169
|
+
schema.namespace ? undefined : schema.name,
|
|
170
|
+
);
|
|
171
|
+
const argumentsText = repairExecCommandInput(integerRepaired, schema);
|
|
135
172
|
return argumentsText === raw ? item : { ...item, arguments: argumentsText };
|
|
136
173
|
}
|
|
137
174
|
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { OcxDocumentContent } from "../types";
|
|
2
|
+
|
|
3
|
+
const DATA_URL = /^data:([^;,]+)((?:;[^;,]*)*),(.*)$/s;
|
|
4
|
+
const BASE64_PAYLOAD = /^[A-Za-z0-9+/]+={0,2}$/;
|
|
5
|
+
|
|
6
|
+
function usableBase64(payload: string): boolean {
|
|
7
|
+
if (!BASE64_PAYLOAD.test(payload)) return false;
|
|
8
|
+
// Preserve valid unpadded encodings, but a single sextet cannot represent any byte.
|
|
9
|
+
// Padding, when present, must complete a four-character quantum. No payload allocation.
|
|
10
|
+
return payload.endsWith("=") ? payload.length % 4 === 0 : payload.length % 4 !== 1;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** The one marker vocabulary for an attached document, derived from what the part knows. */
|
|
14
|
+
export function inlineDocumentMarker(filename: string | undefined): string {
|
|
15
|
+
return filename !== undefined && filename.length > 0 ? `[document: ${filename}]` : "[document]";
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* An inline document part from a `data:` URL, or `undefined` when there are no usable bytes.
|
|
20
|
+
*
|
|
21
|
+
* A reference with no payload — a `file_id`, a bare filename — is deliberately not a document:
|
|
22
|
+
* there is nothing to carry, and minting a part for it would claim an attachment the request
|
|
23
|
+
* never contained. Those keep the marker path they already had.
|
|
24
|
+
*/
|
|
25
|
+
export function inlineDocumentFromDataUrl(
|
|
26
|
+
fileData: string | undefined,
|
|
27
|
+
filename: string | undefined,
|
|
28
|
+
): OcxDocumentContent | undefined {
|
|
29
|
+
if (typeof fileData !== "string" || fileData.length === 0) return undefined;
|
|
30
|
+
const match = DATA_URL.exec(fileData);
|
|
31
|
+
if (!match) return undefined;
|
|
32
|
+
const mediaType = match[1]!;
|
|
33
|
+
const payload = match[3]!;
|
|
34
|
+
if (!hasBase64Parameter(match[2] ?? "") || !usableBase64(payload)) return undefined;
|
|
35
|
+
return {
|
|
36
|
+
type: "document",
|
|
37
|
+
text: inlineDocumentMarker(filename),
|
|
38
|
+
mediaType,
|
|
39
|
+
data: payload,
|
|
40
|
+
...(filename !== undefined && filename.length > 0 ? { filename } : {}),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** The `data:` URL spelling of a document part, for wires whose counterpart takes one. */
|
|
45
|
+
export function inlineDocumentDataUrl(part: OcxDocumentContent): string {
|
|
46
|
+
return `data:${part.mediaType};base64,${part.data}`;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Whether this string is a `data:` URL this module would actually decode.
|
|
51
|
+
*
|
|
52
|
+
* The media-type scanner and this parser have to agree exactly. A looser scanner would exempt an
|
|
53
|
+
* attachment from the untranslated-media refusal that the parser then reduces to a marker, which
|
|
54
|
+
* is the silent drop the refusal exists to prevent. `;base64` must be a whole parameter token,
|
|
55
|
+
* not a substring of one: `;notbase64,` and `;x=base64,` are not base64 payloads.
|
|
56
|
+
*/
|
|
57
|
+
export function isInlineDocumentDataUrl(value: unknown): boolean {
|
|
58
|
+
if (typeof value !== "string" || value.length === 0) return false;
|
|
59
|
+
const match = DATA_URL.exec(value);
|
|
60
|
+
return match !== null && hasBase64Parameter(match[2] ?? "") && usableBase64(match[3]!);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function hasBase64Parameter(parameters: string): boolean {
|
|
64
|
+
return parameters.split(";").includes("base64");
|
|
65
|
+
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { isInlineDocumentDataUrl } from "./inline-document";
|
|
2
|
+
|
|
1
3
|
/** Input kinds for which the normalized request has no lossless content carrier. */
|
|
2
4
|
export type UntranslatedInputMedia = "audio" | "file";
|
|
3
5
|
|
|
@@ -7,20 +9,47 @@ function isRecord(value: unknown): value is RecordValue {
|
|
|
7
9
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
8
10
|
}
|
|
9
11
|
|
|
10
|
-
|
|
12
|
+
/**
|
|
13
|
+
* Whether the position being scanned has a converter that builds a document part.
|
|
14
|
+
*
|
|
15
|
+
* Only user content does. A tool output, a system or assistant message, and a Chat `developer`
|
|
16
|
+
* message are all flattened to text by their converters, so exempting an attachment there would
|
|
17
|
+
* turn today's explicit refusal into the silent drop this scanner exists to prevent.
|
|
18
|
+
*/
|
|
19
|
+
type CarrierPosition = "user-content" | "flattened";
|
|
20
|
+
|
|
21
|
+
function mediaKind(value: unknown, position: CarrierPosition): UntranslatedInputMedia | undefined {
|
|
11
22
|
if (!isRecord(value)) return undefined;
|
|
12
23
|
if (value.type === "input_audio" || value.type === "audio") return "audio";
|
|
13
|
-
if (value.type === "input_file" || value.type === "file" || value.type === "document")
|
|
24
|
+
if (value.type === "input_file" || value.type === "file" || value.type === "document") {
|
|
25
|
+
// A document that carries its own bytes has a lossless carrier in user content, so refusing
|
|
26
|
+
// it there would reject the very request #5212 exists to preserve. A reference with no
|
|
27
|
+
// payload has none anywhere: translated adapters cannot dereference a file_id or a remote
|
|
28
|
+
// source.
|
|
29
|
+
return position === "user-content" && carriesInlineDocumentBytes(value) ? undefined : "file";
|
|
30
|
+
}
|
|
14
31
|
// A file-id-only image is not pixels: translated adapters cannot dereference it.
|
|
15
32
|
if (value.type === "input_image" && typeof value.file_id === "string" && value.file_id.length > 0
|
|
16
33
|
&& !(typeof value.image_url === "string" && value.image_url.length > 0)) return "file";
|
|
17
34
|
return undefined;
|
|
18
35
|
}
|
|
19
36
|
|
|
20
|
-
|
|
37
|
+
/** Presence of a base64 payload only. No payload is read, decoded, copied or returned. */
|
|
38
|
+
function carriesInlineDocumentBytes(value: RecordValue): boolean {
|
|
39
|
+
if (isInlineDocumentDataUrl(value.file_data)) return true;
|
|
40
|
+
// Chat Completions nests the payload under `file`.
|
|
41
|
+
if (isRecord(value.file) && isInlineDocumentDataUrl(value.file.file_data)) return true;
|
|
42
|
+
// Anthropic nests it under a base64 `source`.
|
|
43
|
+
return isRecord(value.source)
|
|
44
|
+
&& value.source.type === "base64"
|
|
45
|
+
&& typeof value.source.data === "string"
|
|
46
|
+
&& value.source.data.length > 0;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function contentMedia(content: unknown, position: CarrierPosition): UntranslatedInputMedia | undefined {
|
|
21
50
|
if (!Array.isArray(content)) return undefined;
|
|
22
51
|
for (const part of content) {
|
|
23
|
-
const kind = mediaKind(part);
|
|
52
|
+
const kind = mediaKind(part, position);
|
|
24
53
|
if (kind) return kind;
|
|
25
54
|
}
|
|
26
55
|
return undefined;
|
|
@@ -35,13 +64,16 @@ export function untranslatedResponsesInputMedia(body: unknown): UntranslatedInpu
|
|
|
35
64
|
if (!isRecord(body) || !Array.isArray(body.input)) return undefined;
|
|
36
65
|
for (const item of body.input) {
|
|
37
66
|
if (!isRecord(item)) continue;
|
|
38
|
-
const direct = mediaKind(item);
|
|
67
|
+
const direct = mediaKind(item, "flattened");
|
|
39
68
|
if (direct) return direct;
|
|
40
69
|
if (item.type === "function_call_output" || item.type === "custom_tool_call_output") {
|
|
41
|
-
const kind = contentMedia(item.output);
|
|
70
|
+
const kind = contentMedia(item.output, "flattened");
|
|
42
71
|
if (kind) return kind;
|
|
43
72
|
} else if (item.type === "message" || item.type === undefined) {
|
|
44
|
-
|
|
73
|
+
// `inputContentParts` runs for user and developer messages only; a system message is
|
|
74
|
+
// flattened to text by the parser.
|
|
75
|
+
const role = item.role;
|
|
76
|
+
const kind = contentMedia(item.content, role === "user" || role === "developer" ? "user-content" : "flattened");
|
|
45
77
|
if (kind) return kind;
|
|
46
78
|
}
|
|
47
79
|
}
|
|
@@ -53,7 +85,9 @@ export function untranslatedChatInputMedia(body: unknown): UntranslatedInputMedi
|
|
|
53
85
|
if (!isRecord(body) || !Array.isArray(body.messages)) return undefined;
|
|
54
86
|
for (const message of body.messages) {
|
|
55
87
|
if (!isRecord(message)) continue;
|
|
56
|
-
|
|
88
|
+
// Only the `user` branch of the Chat projection builds content blocks. `system`,
|
|
89
|
+
// `developer`, `assistant` and `tool` all reduce their content to a string.
|
|
90
|
+
const kind = contentMedia(message.content, message.role === "user" ? "user-content" : "flattened");
|
|
57
91
|
if (kind) return kind;
|
|
58
92
|
}
|
|
59
93
|
return undefined;
|
|
@@ -8,6 +8,25 @@ const MUSE_SAFE_NAME = /^[a-zA-Z0-9_-]+$/;
|
|
|
8
8
|
|
|
9
9
|
export type MuseToolNameAliases = ReadonlyMap<string, string>;
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Final wire name for an identity before the Muse length rewrite.
|
|
13
|
+
*
|
|
14
|
+
* The alias map is request-local and already narrowed by `tool_choice`. Returning undefined for
|
|
15
|
+
* a contradictory map keeps downstream identity checks fail-closed instead of choosing one alias.
|
|
16
|
+
*/
|
|
17
|
+
export function museWireNameForOriginal(
|
|
18
|
+
originalName: string,
|
|
19
|
+
aliases: MuseToolNameAliases,
|
|
20
|
+
): string | undefined {
|
|
21
|
+
let wireName: string | undefined;
|
|
22
|
+
for (const [candidate, original] of aliases) {
|
|
23
|
+
if (original !== originalName) continue;
|
|
24
|
+
if (wireName !== undefined && wireName !== candidate) return undefined;
|
|
25
|
+
wireName = candidate;
|
|
26
|
+
}
|
|
27
|
+
return wireName ?? originalName;
|
|
28
|
+
}
|
|
29
|
+
|
|
11
30
|
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
12
31
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
13
32
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { OcxContentPart, OcxTextContent } from "../types";
|
|
2
|
+
import { inlineDocumentFromDataUrl } from "./inline-document";
|
|
2
3
|
|
|
3
4
|
export function isObj(v: unknown): v is Record<string, unknown> {
|
|
4
5
|
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
@@ -90,8 +91,13 @@ export function inputContentParts(blocks: unknown): string | OcxContentPart[] {
|
|
|
90
91
|
if (fileId) {
|
|
91
92
|
parts.push({ type: "text", text: `[file: ${fileId}]` });
|
|
92
93
|
} else if (fileData) {
|
|
93
|
-
// Inline
|
|
94
|
-
|
|
94
|
+
// Inline bytes used to be reduced to a name here, which meant no adapter could forward
|
|
95
|
+
// the attachment even to a target that has a representation for it, and a title-only
|
|
96
|
+
// forward came back as a confident answer about a document the model never saw (#5212).
|
|
97
|
+
// The marker survives on the part for every wire that still cannot carry one. Bytes with
|
|
98
|
+
// no declared media type are not decodable into a document and keep the marker they had.
|
|
99
|
+
const document = inlineDocumentFromDataUrl(fileData, filename);
|
|
100
|
+
parts.push(document ?? { type: "text", text: filename ? `[file: ${filename}]` : "[file: inline data]" });
|
|
95
101
|
}
|
|
96
102
|
// A bare filename is not a file resource in the Responses schema, so omit it rather than
|
|
97
103
|
// fabricating a "[file: ...]" marker for an attachment that was never sent.
|
|
@@ -61,6 +61,9 @@ export function buildTools(tools: unknown[] | undefined): OcxTool[] | undefined
|
|
|
61
61
|
parameters: normalizeParameters(t.parameters),
|
|
62
62
|
};
|
|
63
63
|
if (t.strict !== undefined) tool.strict = t.strict as boolean;
|
|
64
|
+
if (Array.isArray(t.allowed_callers)) {
|
|
65
|
+
tool.allowedCallers = (t.allowed_callers as unknown[]).filter((c): c is string => typeof c === "string");
|
|
66
|
+
}
|
|
64
67
|
if (namespace) tool.namespace = namespace;
|
|
65
68
|
out.push(tool);
|
|
66
69
|
};
|
package/src/responses/parser.ts
CHANGED
|
@@ -245,7 +245,9 @@ export function parseRequest(
|
|
|
245
245
|
case "system": {
|
|
246
246
|
pendingReasoning.length = 0;
|
|
247
247
|
const text = inputContentParts(msg.content);
|
|
248
|
-
const flat = typeof text === "string"
|
|
248
|
+
const flat = typeof text === "string"
|
|
249
|
+
? text
|
|
250
|
+
: text.map(p => (p.type === "text" || p.type === "document" ? p.text : "")).join("");
|
|
249
251
|
if (flat.length > 0) systemPrompt.push(flat);
|
|
250
252
|
break;
|
|
251
253
|
}
|
package/src/responses/schema.ts
CHANGED
|
@@ -122,6 +122,9 @@ export const toolSchema = z.object({
|
|
|
122
122
|
description: z.string().optional(),
|
|
123
123
|
parameters: z.record(z.string(), z.unknown()).optional(),
|
|
124
124
|
strict: z.boolean().optional(),
|
|
125
|
+
// Unknown keys are stripped here, so a field the parser is expected to read has to be
|
|
126
|
+
// declared: an undeclared allowed_callers never reached buildTools at all (#5210).
|
|
127
|
+
allowed_callers: z.array(z.string()).optional(),
|
|
125
128
|
});
|
|
126
129
|
|
|
127
130
|
const builtinToolSchema = z.object({ type: z.string() }).loose();
|
package/src/router.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { CodexAccountMode, OcxConfig, OcxProviderConfig } from "./types";
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
import { peekAuthStore } from "./oauth/store";
|
|
2
4
|
import {
|
|
3
5
|
getCombo,
|
|
4
6
|
isComboTargetInCooldown,
|
|
@@ -36,7 +38,7 @@ import {
|
|
|
36
38
|
import { decodeRoutedModelIdOrThrow, encodeRoutedModelId } from "./providers/slug-codec";
|
|
37
39
|
import { effectiveProviderAliasDecision, resolveModelAlias } from "./providers/default-aliases";
|
|
38
40
|
import { resolveBlockedModelRedirect } from "./lib/shadow-call";
|
|
39
|
-
import {
|
|
41
|
+
import { getRoutingCached } from "./codex/model-cache";
|
|
40
42
|
import { codexAccountNamespaceEntries } from "./codex/account-namespaces";
|
|
41
43
|
import {
|
|
42
44
|
buildRouteDecisionTrace,
|
|
@@ -149,7 +151,20 @@ export function knownModelIdsForProvider(
|
|
|
149
151
|
// only in a map this function forgot is no longer undecodable, and a new model-keyed field
|
|
150
152
|
// fails typecheck until its keys are given a meaning.
|
|
151
153
|
for (const id of registry ? registryModelIdKeys(registry) : []) ids.add(id);
|
|
152
|
-
|
|
154
|
+
const cachedModels = getRoutingCached(provName, () => {
|
|
155
|
+
// This callback runs only for a scoped entry, not for each provider in an alias scan.
|
|
156
|
+
const routed = routedProviderConfig(provName, prov);
|
|
157
|
+
let key = routed.apiKey;
|
|
158
|
+
if (routed.authMode === "oauth") {
|
|
159
|
+
const set = peekAuthStore()[provName];
|
|
160
|
+
const account = set?.accounts.find(row => row.id === set.activeAccountId);
|
|
161
|
+
if (!account || account.needsReauth || !Number.isFinite(account.credential.expires)
|
|
162
|
+
|| account.credential.expires <= Date.now()) return undefined;
|
|
163
|
+
key = account.credential.access;
|
|
164
|
+
}
|
|
165
|
+
return key ? createHash("sha256").update(key).digest("hex") : undefined;
|
|
166
|
+
});
|
|
167
|
+
for (const cached of cachedModels ?? []) ids.add(cached.id);
|
|
153
168
|
for (const model of config?.customModels ?? []) {
|
|
154
169
|
if (model.provider === provName && model.modelId) ids.add(model.modelId);
|
|
155
170
|
}
|