@bitkyc08/opencodex 2.48.0-preview.20260908 → 2.49.0
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/README.md +8 -5
- package/SPONSORS.md +1 -1
- package/assets/sponsors/orcarouter.png +0 -0
- package/assets/sponsors/packycode.png +0 -0
- package/gui/dist/assets/index-BoBRSehJ.css +1 -0
- package/gui/dist/assets/index-BtyONQrZ.js +115 -0
- package/gui/dist/index.html +2 -2
- package/gui/dist/provider-icons/packycode.svg +19 -0
- package/gui/dist/provider-icons/qoder.svg +5 -0
- package/package.json +5 -3
- package/src/adapters/anthropic.ts +31 -16
- package/src/adapters/codebuddy/adapter.ts +85 -0
- package/src/adapters/codebuddy/profiles.ts +52 -0
- package/src/adapters/coding-agent/profile.ts +100 -0
- package/src/adapters/coding-agent/protocol.ts +463 -0
- package/src/adapters/coding-agent/turn.ts +353 -0
- package/src/adapters/google.ts +15 -11
- package/src/adapters/mimo-free.ts +3 -0
- package/src/adapters/openai-chat.ts +2 -2
- package/src/adapters/openai-responses.ts +18 -11
- package/src/adapters/qoder/adapter.ts +70 -0
- package/src/adapters/qoder/live-models.ts +89 -0
- package/src/adapters/qoder/profiles.ts +36 -0
- package/src/adapters/registry.ts +12 -0
- package/src/adapters/responses-tool-schema.ts +113 -8
- package/src/cli/dispatch.ts +2 -0
- package/src/cli/doctor.ts +40 -0
- package/src/cli/effort.ts +24 -8
- package/src/cli/help.ts +2 -0
- package/src/cli/index.ts +24 -1
- package/src/cli/provider-runtime.ts +2 -1
- package/src/cli/registry.ts +2 -2
- package/src/cli/system-command.ts +10 -3
- package/src/cli/usage-report.ts +9 -5
- package/src/codex/account-lifecycle.ts +35 -2
- package/src/codex/auth-api.ts +6 -3
- package/src/codex/auth-collision.ts +12 -2
- package/src/codex/auth-context.ts +75 -7
- package/src/codex/catalog/provider-fetch.ts +73 -9
- package/src/codex/inject.ts +127 -30
- package/src/codex/internal/catalog-writer.ts +3 -0
- package/src/codex/journal.ts +61 -12
- package/src/codex/model-cache.ts +11 -4
- package/src/codex/native-profile-startup.ts +72 -5
- package/src/codex/native-profile-store.ts +2 -2
- package/src/codex/ocx-compaction-history.ts +226 -0
- package/src/codex/project-config-warnings.ts +3 -1
- package/src/codex/quota.ts +18 -8
- package/src/codex/reserve-availability.ts +21 -5
- package/src/codex/runtime.ts +45 -1
- package/src/codex/sync.ts +5 -0
- package/src/config.ts +1 -0
- package/src/generated/compatibility-version.json +126 -74
- package/src/images/loop.ts +1 -0
- package/src/images/xai-video-client.ts +2 -0
- package/src/integrations/registry.ts +1 -0
- package/src/lib/process-control.ts +52 -8
- package/src/lib/upstream-retry.ts +1 -0
- package/src/oauth/chatgpt.ts +83 -0
- package/src/oauth/xai.ts +151 -8
- package/src/providers/api-key-selection-capture.ts +10 -0
- package/src/providers/api-key-selection.ts +2 -7
- package/src/providers/caller-authorization.ts +36 -0
- package/src/providers/codebuddy-models.ts +184 -0
- package/src/providers/derive.ts +5 -0
- package/src/providers/free-directory.ts +26 -2
- package/src/providers/openai-sidecar.ts +35 -11
- package/src/providers/qoder-models.ts +25 -0
- package/src/providers/registry.ts +139 -0
- package/src/responses/compaction.ts +4 -0
- package/src/responses/task-input.ts +21 -1
- package/src/router.ts +1 -1
- package/src/server/chat-completions.ts +26 -9
- package/src/server/claude-messages.ts +12 -2
- package/src/server/images.ts +1 -0
- package/src/server/management/account-selection-stream.ts +13 -4
- package/src/server/management/config-routes.ts +24 -5
- package/src/server/management/logs-usage-routes.ts +1 -0
- package/src/server/management/native-integration-routes.ts +2 -1
- package/src/server/management/provider-routes.ts +24 -0
- package/src/server/management-api.ts +14 -0
- package/src/server/port-reclaim.ts +11 -26
- package/src/server/request-log.ts +6 -0
- package/src/server/responses/collaboration.ts +4 -9
- package/src/server/responses/core.ts +141 -19
- package/src/server/responses/fetch-helpers.ts +18 -7
- package/src/server/responses/policy-fallback.ts +12 -0
- package/src/service.ts +36 -2
- package/src/storage/cleanup.ts +77 -45
- package/src/types/config.ts +7 -1
- package/src/usage/log.ts +24 -0
- package/src/vision/anthropic-describe.ts +1 -0
- package/src/web-search/anthropic-executor.ts +1 -0
- package/src/web-search/loop.ts +1 -0
- package/src/web-search/progress-stream.ts +4 -0
- package/gui/dist/assets/index-B6EFoatP.js +0 -115
- package/gui/dist/assets/index-D5SiRo8X.css +0 -1
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Curated CodeBuddy model catalogs, transcribed from the OFFICIAL model manifest bundled with the
|
|
3
|
+
* vendor CLI (`@tencent-ai/codebuddy-code` v2.143.0: `product.json` for the global/`public`
|
|
4
|
+
* environment, `product.internal.json` for the China/`internal` environment) and cross-checked
|
|
5
|
+
* against the CLI's own `--model` accept-list. Verified 2026-09-03.
|
|
6
|
+
*
|
|
7
|
+
* Global and CN are deliberately NOT the same roster (§八). Context windows, output caps, vision
|
|
8
|
+
* and reasoning ladders are filled ONLY where the official manifest states them; a model with no
|
|
9
|
+
* published figure is omitted rather than guessed (§二十八/§二十九). CodeBuddy exposes no documented
|
|
10
|
+
* third-party live `/v1/models` endpoint, so these providers seed a static catalog
|
|
11
|
+
* (`liveModels: false`) exactly like the Kiro and Command Code entries.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/** Global (`public`) session models accepted by `codebuddy --model`. */
|
|
15
|
+
export const CODEBUDDY_GLOBAL_MODELS = [
|
|
16
|
+
"default-model",
|
|
17
|
+
"fast-model",
|
|
18
|
+
"balanced-model",
|
|
19
|
+
"primary-model",
|
|
20
|
+
"deep-model",
|
|
21
|
+
"gpt-5.6-sol",
|
|
22
|
+
"gpt-5.6-terra",
|
|
23
|
+
"gpt-5.6-luna",
|
|
24
|
+
"gpt-5.5",
|
|
25
|
+
"gpt-5.4",
|
|
26
|
+
"gpt-5.3-codex",
|
|
27
|
+
"gemini-3.5-flash",
|
|
28
|
+
"glm-5.3",
|
|
29
|
+
"glm-5.2",
|
|
30
|
+
"kimi-k3",
|
|
31
|
+
"kimi-k2.6",
|
|
32
|
+
"minimax-m3",
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
/** China (`internal`) session models from the official internal manifest (text/chat models only). */
|
|
36
|
+
export const CODEBUDDY_CN_MODELS = [
|
|
37
|
+
"default",
|
|
38
|
+
"deepseek-v4-pro",
|
|
39
|
+
"deepseek-v4-flash",
|
|
40
|
+
"minimax-m3",
|
|
41
|
+
"minimax-m2.7",
|
|
42
|
+
"glm-5.2",
|
|
43
|
+
"glm-5.1",
|
|
44
|
+
"glm-5.0",
|
|
45
|
+
"glm-5.0-turbo",
|
|
46
|
+
"glm-5v-turbo",
|
|
47
|
+
"glm-4.7",
|
|
48
|
+
"kimi-k3-1",
|
|
49
|
+
"kimi-k2.7",
|
|
50
|
+
"kimi-k2.6",
|
|
51
|
+
"kimi-k2.5",
|
|
52
|
+
"deepseek-v3-2-volc",
|
|
53
|
+
"hy3",
|
|
54
|
+
"hunyuan-chat",
|
|
55
|
+
];
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* The CLI documents a single `--effort` ladder (minimal, low, medium, high, xhigh, max). The Codex
|
|
59
|
+
* reasoning ladder overlaps it at low..max; `minimal`/`none` are Codex sentinels normalized by
|
|
60
|
+
* `mapReasoningEffort`, and `ultra` folds to `max`. Declared provider-wide, then narrowed per model
|
|
61
|
+
* where the official manifest publishes a smaller `supportedEfforts`.
|
|
62
|
+
*/
|
|
63
|
+
export const CODEBUDDY_REASONING_EFFORTS = ["low", "medium", "high", "xhigh", "max"];
|
|
64
|
+
|
|
65
|
+
export const CODEBUDDY_GLOBAL_MODEL_CONTEXT_WINDOWS: Record<string, number> = {
|
|
66
|
+
"default-model": 176_000,
|
|
67
|
+
"fast-model": 200_000,
|
|
68
|
+
"balanced-model": 256_000,
|
|
69
|
+
"primary-model": 272_000,
|
|
70
|
+
"deep-model": 176_000,
|
|
71
|
+
"gpt-5.6-sol": 1_000_000,
|
|
72
|
+
"gpt-5.6-terra": 1_000_000,
|
|
73
|
+
"gpt-5.6-luna": 1_000_000,
|
|
74
|
+
"gpt-5.5": 1_000_000,
|
|
75
|
+
"gpt-5.4": 272_000,
|
|
76
|
+
"gpt-5.3-codex": 272_000,
|
|
77
|
+
"gemini-3.5-flash": 1_000_000,
|
|
78
|
+
"glm-5.3": 1_000_000,
|
|
79
|
+
"glm-5.2": 1_000_000,
|
|
80
|
+
"kimi-k3": 1_000_000,
|
|
81
|
+
"kimi-k2.6": 256_000,
|
|
82
|
+
"minimax-m3": 512_000,
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export const CODEBUDDY_GLOBAL_MODEL_MAX_OUTPUT_TOKENS: Record<string, number> = {
|
|
86
|
+
"default-model": 24_000,
|
|
87
|
+
"fast-model": 32_000,
|
|
88
|
+
"balanced-model": 32_000,
|
|
89
|
+
"primary-model": 72_000,
|
|
90
|
+
"deep-model": 24_000,
|
|
91
|
+
"gpt-5.6-sol": 128_000,
|
|
92
|
+
"gpt-5.6-terra": 128_000,
|
|
93
|
+
"gpt-5.6-luna": 128_000,
|
|
94
|
+
"gpt-5.5": 72_000,
|
|
95
|
+
"gpt-5.4": 128_000,
|
|
96
|
+
"gpt-5.3-codex": 128_000,
|
|
97
|
+
"gemini-3.5-flash": 65_536,
|
|
98
|
+
"glm-5.3": 48_000,
|
|
99
|
+
"glm-5.2": 48_000,
|
|
100
|
+
"kimi-k3": 32_000,
|
|
101
|
+
"kimi-k2.6": 32_000,
|
|
102
|
+
"minimax-m3": 128_000,
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
/** Per-model ladders narrowed from the official manifest's `reasoning.supportedEfforts`. */
|
|
106
|
+
export const CODEBUDDY_GLOBAL_MODEL_REASONING_EFFORTS: Record<string, string[]> = {
|
|
107
|
+
"gpt-5.6-sol": ["low", "medium", "high", "xhigh"],
|
|
108
|
+
"gpt-5.6-terra": ["low", "medium", "high", "xhigh"],
|
|
109
|
+
"gpt-5.6-luna": ["low", "medium", "high", "xhigh"],
|
|
110
|
+
"glm-5.3": ["low", "high", "max"],
|
|
111
|
+
"glm-5.2": ["high", "xhigh"],
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
export const CODEBUDDY_GLOBAL_MODEL_DEFAULT_REASONING_EFFORTS: Record<string, string> = {
|
|
115
|
+
"gpt-5.6-sol": "high",
|
|
116
|
+
"gpt-5.6-terra": "high",
|
|
117
|
+
"gpt-5.6-luna": "high",
|
|
118
|
+
"glm-5.3": "high",
|
|
119
|
+
"glm-5.2": "high",
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
export const CODEBUDDY_CN_MODEL_CONTEXT_WINDOWS: Record<string, number> = {
|
|
123
|
+
"default": 200_000,
|
|
124
|
+
"deepseek-v4-pro": 1_000_000,
|
|
125
|
+
"deepseek-v4-flash": 1_000_000,
|
|
126
|
+
"minimax-m3": 512_000,
|
|
127
|
+
"minimax-m2.7": 200_000,
|
|
128
|
+
"glm-5.2": 1_000_000,
|
|
129
|
+
"glm-5.1": 200_000,
|
|
130
|
+
"glm-5.0": 200_000,
|
|
131
|
+
"glm-5.0-turbo": 200_000,
|
|
132
|
+
"glm-5v-turbo": 200_000,
|
|
133
|
+
"glm-4.7": 200_000,
|
|
134
|
+
"kimi-k3-1": 1_000_000,
|
|
135
|
+
"kimi-k2.7": 256_000,
|
|
136
|
+
"kimi-k2.6": 256_000,
|
|
137
|
+
"kimi-k2.5": 164_000,
|
|
138
|
+
"deepseek-v3-2-volc": 96_000,
|
|
139
|
+
"hy3": 192_000,
|
|
140
|
+
"hunyuan-chat": 200_000,
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
export const CODEBUDDY_CN_MODEL_MAX_OUTPUT_TOKENS: Record<string, number> = {
|
|
144
|
+
"default": 24_000,
|
|
145
|
+
"deepseek-v4-pro": 50_000,
|
|
146
|
+
"deepseek-v4-flash": 50_000,
|
|
147
|
+
"minimax-m3": 128_000,
|
|
148
|
+
"minimax-m2.7": 48_000,
|
|
149
|
+
"glm-5.2": 48_000,
|
|
150
|
+
"glm-5.1": 48_000,
|
|
151
|
+
"glm-5.0": 48_000,
|
|
152
|
+
"glm-5.0-turbo": 48_000,
|
|
153
|
+
"glm-5v-turbo": 64_000,
|
|
154
|
+
"glm-4.7": 48_000,
|
|
155
|
+
"kimi-k3-1": 32_000,
|
|
156
|
+
"kimi-k2.7": 32_000,
|
|
157
|
+
"kimi-k2.6": 32_000,
|
|
158
|
+
"kimi-k2.5": 32_000,
|
|
159
|
+
"deepseek-v3-2-volc": 32_000,
|
|
160
|
+
"hy3": 64_000,
|
|
161
|
+
"hunyuan-chat": 8_192,
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
export const CODEBUDDY_CN_MODEL_REASONING_EFFORTS: Record<string, string[]> = {
|
|
165
|
+
"hy3": ["low", "high"],
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
export const CODEBUDDY_CN_MODEL_DEFAULT_REASONING_EFFORTS: Record<string, string> = {
|
|
169
|
+
"hy3": "high",
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Text-only models (official manifest `supportsImages: false`). Images for any OTHER model are
|
|
174
|
+
* passed through natively; a model listed here has its images routed through the proxy's vision
|
|
175
|
+
* sidecar rather than being silently dropped (§二十九).
|
|
176
|
+
*/
|
|
177
|
+
export const CODEBUDDY_CN_NO_VISION_MODELS = [
|
|
178
|
+
"default",
|
|
179
|
+
"glm-5.0",
|
|
180
|
+
"glm-5.0-turbo",
|
|
181
|
+
"glm-4.7",
|
|
182
|
+
"deepseek-v3-2-volc",
|
|
183
|
+
"hunyuan-chat",
|
|
184
|
+
];
|
package/src/providers/derive.ts
CHANGED
|
@@ -80,6 +80,10 @@ export interface DerivedProviderPreset {
|
|
|
80
80
|
keyOptional?: boolean;
|
|
81
81
|
/** Free pricing (may still require a key). */
|
|
82
82
|
freeTier?: boolean;
|
|
83
|
+
/** Sponsor tier from SPONSORS.md; the picker pins and labels these rows. */
|
|
84
|
+
sponsor?: "main" | "standard";
|
|
85
|
+
/** Sponsor landing URL (with its tracking parameters), for the picker's row link. */
|
|
86
|
+
sponsorUrl?: string;
|
|
83
87
|
/**
|
|
84
88
|
* Endpoint picker rows (token plan / payg / custom). When present, the add-provider
|
|
85
89
|
* form shows a dropdown; `custom` reveals a free-text base URL field.
|
|
@@ -607,6 +611,7 @@ function entryToPreset(entry: ProviderRegistryEntry): DerivedProviderPreset {
|
|
|
607
611
|
...(entry.note ? { note: entry.note } : {}),
|
|
608
612
|
...(entry.keyOptional ? { keyOptional: true } : {}),
|
|
609
613
|
...(entry.freeTier ? { freeTier: true } : {}),
|
|
614
|
+
...(entry.sponsor ? { sponsor: entry.sponsor.tier, sponsorUrl: entry.sponsor.url } : {}),
|
|
610
615
|
...(entry.baseUrlChoices ? { baseUrlChoices: entry.baseUrlChoices.map(c => ({ ...c })) } : {}),
|
|
611
616
|
};
|
|
612
617
|
}
|
|
@@ -19,7 +19,7 @@ export const FREE_PROVIDER_ACCESS_GROUPS = {
|
|
|
19
19
|
"recurring-credit": ["bytez", "nous-research"],
|
|
20
20
|
"signup-credit": [
|
|
21
21
|
"agentrouter", "ai21", "baichuan", "baseten", "deepinfra", "deepseek", "doubao", "fireworks", "freemodel-dev", "glm-cn",
|
|
22
|
-
"hyperbolic", "longcat", "monsterapi", "nebius", "novita", "nscale", "nvidia", "predibase", "publicai", "qoder",
|
|
22
|
+
"hyperbolic", "longcat", "monsterapi", "nebius", "novita", "nscale", "nvidia", "predibase", "publicai", "qoder", "qoder-cn",
|
|
23
23
|
"scaleway", "sensenova", "stepfun", "together", "vertex",
|
|
24
24
|
],
|
|
25
25
|
} as const satisfies Record<ProviderAccessGroup, readonly string[]>;
|
|
@@ -140,6 +140,30 @@ const CONNECTABLE: Record<string, ConnectableOverride> = {
|
|
|
140
140
|
nscale: openAi("https://inference.api.nscale.com/v1", "https://console.nscale.com", { supportLevel: "supported", verification: "official", documentationUrl: "https://docs.nscale.com/docs/use-cases/chat", modelsUrl: "https://inference.api.nscale.com/v1/models", lastVerified: "2026-08-03" }),
|
|
141
141
|
nvidia: openAi("https://integrate.api.nvidia.com/v1", "https://build.nvidia.com", { supportLevel: "supported", verification: "official", documentationUrl: "https://docs.api.nvidia.com/nim/reference/llm-apis" }),
|
|
142
142
|
publicai: openAi("https://api.publicai.co/v1", "https://publicai.co"),
|
|
143
|
+
qoder: {
|
|
144
|
+
baseUrl: "https://qoder.com",
|
|
145
|
+
dashboardUrl: "https://qoder.com/account/integrations",
|
|
146
|
+
adapter: "qoder",
|
|
147
|
+
authKind: "key",
|
|
148
|
+
supportLevel: "supported",
|
|
149
|
+
verification: "official",
|
|
150
|
+
documentationUrl: "https://docs.qoder.com/cli/authentication",
|
|
151
|
+
discovery: "live",
|
|
152
|
+
liveModels: true,
|
|
153
|
+
lastVerified: "2026-09-03",
|
|
154
|
+
},
|
|
155
|
+
"qoder-cn": {
|
|
156
|
+
baseUrl: "https://qoder.cn",
|
|
157
|
+
dashboardUrl: "https://qoder.cn/account/integrations",
|
|
158
|
+
adapter: "qoder",
|
|
159
|
+
authKind: "key",
|
|
160
|
+
supportLevel: "supported",
|
|
161
|
+
verification: "official",
|
|
162
|
+
documentationUrl: "https://docs.qoder.cn/en/cli/authentication",
|
|
163
|
+
discovery: "live",
|
|
164
|
+
liveModels: true,
|
|
165
|
+
lastVerified: "2026-09-03",
|
|
166
|
+
},
|
|
143
167
|
scaleway: openAi("https://api.scaleway.ai/v1", "https://console.scaleway.com/generative-api", { supportLevel: "supported", verification: "official", documentationUrl: "https://www.scaleway.com/en/docs/generative-apis/api-cli/using-generative-apis/", modelsUrl: "https://api.scaleway.ai/v1/models", lastVerified: "2026-08-01" }),
|
|
144
168
|
sensenova: openAi("https://token.sensenova.cn/v1", "https://console.sensenova.cn", { verification: "official" }),
|
|
145
169
|
stepfun: openAi("https://api.stepfun.com/v1", "https://platform.stepfun.com", { verification: "official" }),
|
|
@@ -160,7 +184,7 @@ const LABELS: Record<string, string> = {
|
|
|
160
184
|
ai21: "AI21", baichuan: "Baichuan", deepinfra: "DeepInfra", deepseek: "DeepSeek", doubao: "Doubao",
|
|
161
185
|
"freemodel-dev": "FreeModel.dev", sambanova: "SambaNova Cloud", nebius: "Nebius Token Factory",
|
|
162
186
|
novita: "Novita", nscale: "Nscale", nvidia: "NVIDIA NIM",
|
|
163
|
-
publicai: "PublicAI", qoder: "Qoder", sensenova: "SenseNova", stepfun: "StepFun", vertex: "Google Vertex AI",
|
|
187
|
+
publicai: "PublicAI", qoder: "Qoder", "qoder-cn": "Qoder CN", sensenova: "SenseNova", stepfun: "StepFun", vertex: "Google Vertex AI",
|
|
164
188
|
};
|
|
165
189
|
|
|
166
190
|
const referenceNote = "Reference entry only: no safe documented API integration is enabled. Configure it manually only with provider documentation; consumer-web cookies and anti-bot bypasses are intentionally unsupported.";
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
type CodexAuthPolicyConfig,
|
|
11
11
|
} from "../codex/auth-context";
|
|
12
12
|
import { recordCodexUpstreamOutcome, type CodexUpstreamOutcome } from "../codex/routing";
|
|
13
|
-
import {
|
|
13
|
+
import { inspectChatGptDomainClaim } from "../oauth/chatgpt";
|
|
14
14
|
import { ForwardAdmissionCredentialError, validateForwardAdmissionCredential, type DataPlaneAdmission } from "../server/auth-cors";
|
|
15
15
|
import type { CodexAccountMode, OcxConfig, OcxProviderConfig } from "../types";
|
|
16
16
|
import {
|
|
@@ -77,21 +77,45 @@ export function listOpenAiForwardSidecarCandidates(config: OcxConfig): OpenAiFor
|
|
|
77
77
|
}];
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
const
|
|
88
|
-
if (!
|
|
80
|
+
/** An explicit caller bearer/account pair for canonical OpenAI destinations; never persist. */
|
|
81
|
+
export type ExplicitOpenAiCallerAuth = Readonly<{ authorization: string; chatgptAccountId: string }>;
|
|
82
|
+
|
|
83
|
+
function explicitSidecarAuth(incomingHeaders: Headers): ExplicitOpenAiCallerAuth | null {
|
|
84
|
+
// Combined Authorization values must not smuggle a second credential into a snapshot,
|
|
85
|
+
// and only a well-formed ChatGPT-specific account marker is domain evidence — a generic
|
|
86
|
+
// organizations claim is not.
|
|
87
|
+
const bearer = /^Bearer[\t ]+([^\s,]+)$/i.exec(incomingHeaders.get("authorization")?.trim() ?? "")?.[1];
|
|
88
|
+
if (!bearer) return null;
|
|
89
|
+
const claim = inspectChatGptDomainClaim(bearer);
|
|
90
|
+
if (claim.kind !== "valid") return null;
|
|
91
|
+
const derivedAccountId = claim.accountId;
|
|
89
92
|
const requestedAccountId = incomingHeaders.get("chatgpt-account-id")?.trim();
|
|
90
93
|
// JWT payloads are decoded locally but not signature-verified. Requiring the caller's
|
|
91
94
|
// explicit account header, and checking it against the token claim, makes forwarding an
|
|
92
95
|
// intentional ChatGPT-auth operation instead of silently reclassifying any JWT-shaped
|
|
93
96
|
// provider credential as a Codex bearer.
|
|
94
|
-
if (!requestedAccountId || requestedAccountId !== derivedAccountId) return
|
|
97
|
+
if (!requestedAccountId || requestedAccountId !== derivedAccountId) return null;
|
|
98
|
+
return { authorization: incomingHeaders.get("authorization")!, chatgptAccountId: requestedAccountId };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function captureExplicitOpenAiCallerAuth(incomingHeaders: Headers, config: OcxConfig): ExplicitOpenAiCallerAuth | null {
|
|
102
|
+
const auth = explicitSidecarAuth(incomingHeaders);
|
|
103
|
+
if (!auth) return null;
|
|
104
|
+
try {
|
|
105
|
+
validateForwardAdmissionCredential(incomingHeaders, config);
|
|
106
|
+
} catch (error) {
|
|
107
|
+
if (error instanceof ForwardAdmissionCredentialError) return null;
|
|
108
|
+
throw error;
|
|
109
|
+
}
|
|
110
|
+
return auth;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function directSidecarHeaders(
|
|
114
|
+
incomingHeaders: Headers,
|
|
115
|
+
config: CodexAuthPolicyConfig,
|
|
116
|
+
admission?: Pick<DataPlaneAdmission, "source">,
|
|
117
|
+
): Headers | undefined {
|
|
118
|
+
if (!explicitSidecarAuth(incomingHeaders)) return undefined;
|
|
95
119
|
const selected = headersForCodexAuthContext(incomingHeaders, { kind: "main", accountId: null }, config, undefined, admission);
|
|
96
120
|
return selected;
|
|
97
121
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cold-start fallback from the official Qoder Global model documentation, verified 2026-09-03.
|
|
3
|
+
* The account-specific `qoder --list-models` result is authoritative whenever discovery succeeds.
|
|
4
|
+
*/
|
|
5
|
+
export const QODER_GLOBAL_MODELS = [
|
|
6
|
+
"Qwen3.8-Max",
|
|
7
|
+
"Qwen3.7-Max",
|
|
8
|
+
"Qwen3.7-Plus",
|
|
9
|
+
"Kimi-K3",
|
|
10
|
+
"Kimi-K2.7-Code",
|
|
11
|
+
"GLM-5.3",
|
|
12
|
+
"GLM-5.2",
|
|
13
|
+
"DeepSeek-V4-Pro",
|
|
14
|
+
] as const;
|
|
15
|
+
|
|
16
|
+
/** Live Qoder CN roster captured from the official CLI on 2026-09-03. */
|
|
17
|
+
export const QODER_CN_MODELS = [
|
|
18
|
+
"Qwen3.8-Max",
|
|
19
|
+
"Qwen3.8-Flash",
|
|
20
|
+
"Qwen3.7-Max",
|
|
21
|
+
"Qwen3.7-Plus",
|
|
22
|
+
"Qwen3.7-Flash",
|
|
23
|
+
] as const;
|
|
24
|
+
|
|
25
|
+
export const QODER_REASONING_EFFORTS = ["low", "medium", "high", "xhigh", "max"] as const;
|
|
@@ -21,6 +21,21 @@ import {
|
|
|
21
21
|
import { cursorFastCapableBases } from "../adapters/cursor/catalog";
|
|
22
22
|
import { COMMAND_CODE_MODEL_REASONING_EFFORTS } from "./command-code-efforts";
|
|
23
23
|
import { isCanonicalOpenRouterTarget } from "./openrouter-routing";
|
|
24
|
+
import {
|
|
25
|
+
CODEBUDDY_CN_MODELS,
|
|
26
|
+
CODEBUDDY_CN_MODEL_CONTEXT_WINDOWS,
|
|
27
|
+
CODEBUDDY_CN_MODEL_DEFAULT_REASONING_EFFORTS,
|
|
28
|
+
CODEBUDDY_CN_MODEL_MAX_OUTPUT_TOKENS,
|
|
29
|
+
CODEBUDDY_CN_MODEL_REASONING_EFFORTS,
|
|
30
|
+
CODEBUDDY_CN_NO_VISION_MODELS,
|
|
31
|
+
CODEBUDDY_GLOBAL_MODELS,
|
|
32
|
+
CODEBUDDY_GLOBAL_MODEL_CONTEXT_WINDOWS,
|
|
33
|
+
CODEBUDDY_GLOBAL_MODEL_DEFAULT_REASONING_EFFORTS,
|
|
34
|
+
CODEBUDDY_GLOBAL_MODEL_MAX_OUTPUT_TOKENS,
|
|
35
|
+
CODEBUDDY_GLOBAL_MODEL_REASONING_EFFORTS,
|
|
36
|
+
CODEBUDDY_REASONING_EFFORTS,
|
|
37
|
+
} from "./codebuddy-models";
|
|
38
|
+
import { QODER_CN_MODELS, QODER_GLOBAL_MODELS, QODER_REASONING_EFFORTS } from "./qoder-models";
|
|
24
39
|
|
|
25
40
|
export type ProviderAuthKind = "forward" | "oauth" | "key" | "local";
|
|
26
41
|
export type MetadataModelIdNormalize = "case-insensitive";
|
|
@@ -160,6 +175,13 @@ export interface ProviderRegistryEntry {
|
|
|
160
175
|
staticHeaders?: Record<string, string>;
|
|
161
176
|
modelSuffixBracketStrip?: boolean;
|
|
162
177
|
featured?: boolean;
|
|
178
|
+
/**
|
|
179
|
+
* Paid provider sponsorship under SPONSORS.md. `main` is reserved for model developers,
|
|
180
|
+
* `standard` for relays and gateways. The picker pins sponsor rows first (alphabetical among
|
|
181
|
+
* themselves) and labels them; nothing else reads this field. Routing, failover, quota, and
|
|
182
|
+
* defaults never consult it — that boundary is what SPONSORS.md promises users.
|
|
183
|
+
*/
|
|
184
|
+
sponsor?: { tier: "main" | "standard"; url: string };
|
|
163
185
|
dashboardPreset?: boolean;
|
|
164
186
|
note?: string;
|
|
165
187
|
dashboardUrl?: string;
|
|
@@ -1903,6 +1925,9 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
|
|
|
1903
1925
|
authKind: "key", dashboardUrl: "https://www.orcarouter.ai/console",
|
|
1904
1926
|
// The catalog is public, so a successful /models probe cannot validate a submitted key.
|
|
1905
1927
|
apiKeyValidation: "unknown",
|
|
1928
|
+
// Standard sponsor under SPONSORS.md (agreement signed 2026-09-07). Pins the row in the
|
|
1929
|
+
// picker and adds the chip; nothing about routing or defaults changes.
|
|
1930
|
+
sponsor: { tier: "standard", url: "https://www.orcarouter.ai/?utm_source=opencodex&utm_medium=readme" },
|
|
1906
1931
|
defaultModel: "openai/gpt-5.5",
|
|
1907
1932
|
models: ORCAROUTER_MODELS,
|
|
1908
1933
|
liveModels: true,
|
|
@@ -1915,6 +1940,25 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
|
|
|
1915
1940
|
preserveReasoningContentModels: ORCAROUTER_TEXT_ONLY_MODELS,
|
|
1916
1941
|
note: "OpenAI-compatible adaptive router. Models and multimodal capabilities are discovered live from the public chat catalog. Use the OrcaRouter account entry for PKCE login.",
|
|
1917
1942
|
},
|
|
1943
|
+
{
|
|
1944
|
+
// PackyCode: API relay (packyapi.com) for Claude Code, Codex, Gemini and more. Codex traffic
|
|
1945
|
+
// uses the OpenAI-compatible host from their Codex/Kimi Code guides (docs.packyapi.com):
|
|
1946
|
+
// https://cf.api.fan/v1 — GET /v1/models answers 401 without a key, so the host is live and
|
|
1947
|
+
// discovery narrows to what the key's token group allows. Model ids are bare OpenAI-style
|
|
1948
|
+
// ids (the Codex token group lists gpt-5.5 / gpt-5.1-codex).
|
|
1949
|
+
// Standard sponsor under SPONSORS.md; the dashboardUrl carries their affiliate code.
|
|
1950
|
+
id: "packycode", label: "PackyCode", adapter: "openai-chat", baseUrl: "https://cf.api.fan/v1",
|
|
1951
|
+
authKind: "key", dashboardUrl: "https://www.packyapi.com/register?aff=k5KT",
|
|
1952
|
+
sponsor: { tier: "standard", url: "https://www.packyapi.com/register?aff=k5KT" },
|
|
1953
|
+
defaultModel: "gpt-5.5",
|
|
1954
|
+
models: ["gpt-5.5", "gpt-5.1-codex"],
|
|
1955
|
+
liveModels: true,
|
|
1956
|
+
// New key preset: opt into collision preservation so a row named `packycode` that a user
|
|
1957
|
+
// points at a different PackyCode host keeps its own destination instead of being pulled
|
|
1958
|
+
// back onto the Codex endpoint below.
|
|
1959
|
+
preserveCustomDestination: true,
|
|
1960
|
+
note: "API relay for Claude Code, Codex, Gemini and more. Create a Codex-group token at packyapi.com; live discovery lists what the token group allows.",
|
|
1961
|
+
},
|
|
1918
1962
|
{
|
|
1919
1963
|
// BizRouter: Korean enterprise LLM gateway (api.bizrouter.ai). Model ids are
|
|
1920
1964
|
// vendor-namespaced (`<vendor>/<model>`) and pass through to the upstream as-is.
|
|
@@ -3140,6 +3184,101 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
|
|
|
3140
3184
|
},
|
|
3141
3185
|
// FREEZE 2026-07-10: no public OpenAI-compatible endpoint is documented. Evidence: devlog/_plan/260710_provider_hardening/003_research_aggregators.md.
|
|
3142
3186
|
{ id: "gitlab-duo", label: "GitLab Duo", baseUrl: "https://cloud.gitlab.com/ai/v1/proxy/openai/v1", adapter: "openai-chat", authKind: "key", dashboardUrl: "https://gitlab.com/-/user_settings/personal_access_tokens" },
|
|
3187
|
+
{
|
|
3188
|
+
// Official Qoder Global CLI automation surface. The canonical URL is an identity boundary;
|
|
3189
|
+
// inference and model discovery are performed only by the installed vendor CLI. Authentication
|
|
3190
|
+
// uses the documented PAT environment variable and never imports desktop/session credentials.
|
|
3191
|
+
id: "qoder",
|
|
3192
|
+
label: "Qoder (Global)",
|
|
3193
|
+
adapter: "qoder",
|
|
3194
|
+
baseUrl: "https://qoder.com",
|
|
3195
|
+
authKind: "key",
|
|
3196
|
+
apiKeyValidation: "unknown",
|
|
3197
|
+
preserveCustomDestination: true,
|
|
3198
|
+
dashboardUrl: "https://qoder.com/account/integrations",
|
|
3199
|
+
defaultModel: "Qwen3.8-Max",
|
|
3200
|
+
models: [...QODER_GLOBAL_MODELS],
|
|
3201
|
+
liveModels: true,
|
|
3202
|
+
reasoningEfforts: [...QODER_REASONING_EFFORTS],
|
|
3203
|
+
noVisionModels: [...QODER_GLOBAL_MODELS],
|
|
3204
|
+
note: "Official Qoder Global CLI using QODER_PERSONAL_ACCESS_TOKEN. Models are discovered per account with `qoder --list-models`; the documented roster is a degraded fallback. The CLI runs single-turn with tools, MCP, settings hooks, and session persistence disabled. Requires `npm install -g @qoder-ai/qodercli`.",
|
|
3205
|
+
},
|
|
3206
|
+
{
|
|
3207
|
+
// Qoder CN is a separate credential, executable, destination, entitlement cache, and health
|
|
3208
|
+
// domain. It deliberately does not reuse the OAuth/private-protocol design from #3010.
|
|
3209
|
+
id: "qoder-cn",
|
|
3210
|
+
label: "Qoder CN",
|
|
3211
|
+
adapter: "qoder",
|
|
3212
|
+
baseUrl: "https://qoder.cn",
|
|
3213
|
+
authKind: "key",
|
|
3214
|
+
apiKeyValidation: "unknown",
|
|
3215
|
+
preserveCustomDestination: true,
|
|
3216
|
+
dashboardUrl: "https://qoder.cn/account/integrations",
|
|
3217
|
+
defaultModel: "Qwen3.8-Max",
|
|
3218
|
+
models: [...QODER_CN_MODELS],
|
|
3219
|
+
liveModels: true,
|
|
3220
|
+
reasoningEfforts: [...QODER_REASONING_EFFORTS],
|
|
3221
|
+
noVisionModels: [...QODER_CN_MODELS],
|
|
3222
|
+
note: "Official Qoder CN CLI using QODERCN_PERSONAL_ACCESS_TOKEN. Models are discovered per account with `qodercn --list-models`; the verified roster is a degraded fallback. The CLI runs single-turn with tools, MCP, settings hooks, and session persistence disabled. Requires `npm install -g @qodercn-ai/qoderclicn`.",
|
|
3223
|
+
},
|
|
3224
|
+
{
|
|
3225
|
+
// Official CodeBuddy Code CLI provider (Tencent Cloud), GLOBAL / `public` environment.
|
|
3226
|
+
// Transport is the vendor-documented headless CLI automation surface
|
|
3227
|
+
// (`codebuddy -p --output-format stream-json --tools ""`) authenticated with the official
|
|
3228
|
+
// `CODEBUDDY_API_KEY` (https://www.codebuddy.ai/profile/keys). It does NOT read desktop
|
|
3229
|
+
// session files, import desktop bearer tokens, impersonate the desktop client, or call the
|
|
3230
|
+
// private console endpoint — the approach closed in #687 and left in draft in #2244.
|
|
3231
|
+
// baseUrl is the canonical region identity: the adapter fails closed if it is overridden, so a
|
|
3232
|
+
// global key is never sent to the CN environment (that is the separate `codebuddy-cn` entry).
|
|
3233
|
+
// v1 runs tools-disabled so Codex keeps tool ownership; this provider is text/reasoning only
|
|
3234
|
+
// until the control-protocol tool bridge lands (see docs). Free/trial/promotional/subscription
|
|
3235
|
+
// credits draw from the same official API-key pool. Requires the CLI: `npm i -g @tencent-ai/codebuddy-code`.
|
|
3236
|
+
// GOVERNANCE: whether routing this vendor automation surface behind a proxy for a third-party
|
|
3237
|
+
// agent satisfies CodeBuddy's AUP is an open question flagged for maintainer security review.
|
|
3238
|
+
id: "codebuddy",
|
|
3239
|
+
label: "CodeBuddy (Global)",
|
|
3240
|
+
adapter: "codebuddy",
|
|
3241
|
+
baseUrl: "https://www.codebuddy.ai",
|
|
3242
|
+
authKind: "key",
|
|
3243
|
+
apiKeyValidation: "unknown",
|
|
3244
|
+
preserveCustomDestination: true,
|
|
3245
|
+
dashboardUrl: "https://www.codebuddy.ai/profile/keys",
|
|
3246
|
+
defaultModel: "default-model",
|
|
3247
|
+
models: CODEBUDDY_GLOBAL_MODELS,
|
|
3248
|
+
liveModels: false,
|
|
3249
|
+
modelContextWindows: CODEBUDDY_GLOBAL_MODEL_CONTEXT_WINDOWS,
|
|
3250
|
+
modelMaxOutputTokens: CODEBUDDY_GLOBAL_MODEL_MAX_OUTPUT_TOKENS,
|
|
3251
|
+
defaultMaxOutputTokens: 32_000,
|
|
3252
|
+
reasoningEfforts: CODEBUDDY_REASONING_EFFORTS,
|
|
3253
|
+
modelReasoningEfforts: CODEBUDDY_GLOBAL_MODEL_REASONING_EFFORTS,
|
|
3254
|
+
modelDefaultReasoningEfforts: CODEBUDDY_GLOBAL_MODEL_DEFAULT_REASONING_EFFORTS,
|
|
3255
|
+
note: "Official CodeBuddy Code CLI (Tencent Cloud), global/public environment. Uses the documented CODEBUDDY_API_KEY + headless CLI surface; never reads desktop sessions or private console endpoints. Region-isolated from codebuddy-cn. v1 disables CLI tools (--tools \"\") so Codex retains tool ownership: text/reasoning only for now. Requires `npm i -g @tencent-ai/codebuddy-code`. AUP/routing authorization flagged for maintainer security review.",
|
|
3256
|
+
},
|
|
3257
|
+
{
|
|
3258
|
+
// Official CodeBuddy Code CLI provider, CHINA / `internal` environment. Identical adapter and
|
|
3259
|
+
// binary as `codebuddy`; the region is fixed by the profile's CODEBUDDY_INTERNET_ENVIRONMENT
|
|
3260
|
+
// and this canonical baseUrl. CN key: https://copilot.tencent.com/profile/keys. The CN model
|
|
3261
|
+
// roster differs from Global (see codebuddy-models.ts) and is seeded separately (§八).
|
|
3262
|
+
id: "codebuddy-cn",
|
|
3263
|
+
label: "CodeBuddy (CN)",
|
|
3264
|
+
adapter: "codebuddy",
|
|
3265
|
+
baseUrl: "https://www.codebuddy.cn",
|
|
3266
|
+
authKind: "key",
|
|
3267
|
+
apiKeyValidation: "unknown",
|
|
3268
|
+
preserveCustomDestination: true,
|
|
3269
|
+
dashboardUrl: "https://copilot.tencent.com/profile/keys",
|
|
3270
|
+
defaultModel: "default",
|
|
3271
|
+
models: CODEBUDDY_CN_MODELS,
|
|
3272
|
+
liveModels: false,
|
|
3273
|
+
modelContextWindows: CODEBUDDY_CN_MODEL_CONTEXT_WINDOWS,
|
|
3274
|
+
modelMaxOutputTokens: CODEBUDDY_CN_MODEL_MAX_OUTPUT_TOKENS,
|
|
3275
|
+
defaultMaxOutputTokens: 32_000,
|
|
3276
|
+
reasoningEfforts: CODEBUDDY_REASONING_EFFORTS,
|
|
3277
|
+
modelReasoningEfforts: CODEBUDDY_CN_MODEL_REASONING_EFFORTS,
|
|
3278
|
+
modelDefaultReasoningEfforts: CODEBUDDY_CN_MODEL_DEFAULT_REASONING_EFFORTS,
|
|
3279
|
+
noVisionModels: CODEBUDDY_CN_NO_VISION_MODELS,
|
|
3280
|
+
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.",
|
|
3281
|
+
},
|
|
3143
3282
|
];
|
|
3144
3283
|
|
|
3145
3284
|
export function providerRegistryFastWireError(
|
|
@@ -17,6 +17,10 @@
|
|
|
17
17
|
|
|
18
18
|
export const OCX_COMPACTION_PREFIX = "ocx1:";
|
|
19
19
|
|
|
20
|
+
export const OCX_NATIVE_REPLAY_RECOVERY_NOTE =
|
|
21
|
+
"Threads compacted through a routed provider can contain OpenCodeX-owned ocx1 state. "
|
|
22
|
+
+ "Before resuming one through native Codex, run `ocx recover-history --ocx-compaction <thread-id> --yes`.";
|
|
23
|
+
|
|
20
24
|
/** Mirrors codex-rs core/templates/compact/prompt.md (the local-compaction instruction). */
|
|
21
25
|
export const COMPACT_PROMPT = `You are performing a CONTEXT CHECKPOINT COMPACTION. Create a handoff summary for another LLM that will resume the task.
|
|
22
26
|
|
|
@@ -20,9 +20,29 @@ function supportedBlock(value: unknown): value is TaskInputBlock {
|
|
|
20
20
|
return value.detail === undefined || (typeof value.detail === "string" && imageDetails.has(value.detail));
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
/**
|
|
24
|
+
* Does this item carry a pairing key? A tool result is paired by `call_id`; a seed is not.
|
|
25
|
+
*
|
|
26
|
+
* Presence of the FIELD is not presence of a KEY (#3807). Codex desktop seeds a sub-agent
|
|
27
|
+
* thread with a lone `function_call_output` that some client builds emit with an explicit
|
|
28
|
+
* `call_id: null` or `""` rather than omitting it. Those values can never pair with a
|
|
29
|
+
* `function_call`, so treating them as a paired result sent the item to the guard in
|
|
30
|
+
* core.ts and answered 400 for a turn that is really external task input.
|
|
31
|
+
*
|
|
32
|
+
* A wrong-typed key (number, object) is NOT relaxed: that is malformed input rather than
|
|
33
|
+
* the absent-pairing seed shape, and it keeps the #3259 rejection.
|
|
34
|
+
*/
|
|
35
|
+
function hasPairingKey(item: Record<string, unknown>): boolean {
|
|
36
|
+
if (!("call_id" in item)) return false;
|
|
37
|
+
const callId = item.call_id;
|
|
38
|
+
if (callId === null) return false;
|
|
39
|
+
if (typeof callId === "string") return callId.trim().length > 0;
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
|
|
23
43
|
/** Recognize Codex external task input without repairing ordinary orphaned tool results. */
|
|
24
44
|
export function externalTaskInputContent(item: unknown): string | OcxContentPart[] | undefined {
|
|
25
|
-
if (!isObj(item) || item.type !== "function_call_output" ||
|
|
45
|
+
if (!isObj(item) || item.type !== "function_call_output" || hasPairingKey(item)) return undefined;
|
|
26
46
|
if (!nonBlank(item.id) || !nonBlank(item.name) || !nonBlank(item.namespace)) return undefined;
|
|
27
47
|
const output = item.output;
|
|
28
48
|
if (typeof output === "string") return nonBlank(output) ? output : undefined;
|
package/src/router.ts
CHANGED
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
import type { NormalizedComboConfig } from "./combos/types";
|
|
11
11
|
import { hasOwnProvider } from "./config/provider-name";
|
|
12
12
|
import { providerUsesKeyAuthOverride, resolveProviderApiKey } from "./providers/key-store";
|
|
13
|
-
import { captureProviderApiKeySelection } from "./providers/api-key-selection";
|
|
13
|
+
import { captureProviderApiKeySelection } from "./providers/api-key-selection-capture";
|
|
14
14
|
import { assertProviderDestinationAllowed } from "./lib/destination-policy";
|
|
15
15
|
import { redactSecretString, redactUrlForLog } from "./lib/redact";
|
|
16
16
|
import {
|