@bitkyc08/opencodex 2.19.0 → 2.21.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/gui/dist/assets/index-BOFeam5a.js +76 -0
- package/gui/dist/assets/index-Xq49CY8F.css +1 -0
- package/gui/dist/index.html +2 -2
- package/package.json +1 -1
- package/src/adapters/anthropic.ts +2 -28
- package/src/adapters/google.ts +31 -3
- package/src/adapters/mimo-free.ts +17 -0
- package/src/adapters/openai-chat.ts +392 -40
- package/src/adapters/registry.ts +144 -0
- package/src/adapters/responses-tool-schema.ts +67 -0
- package/src/bridge.ts +15 -2
- package/src/chat/inbound.ts +13 -7
- package/src/claude/gateway-cache.ts +41 -4
- package/src/cli/claude.ts +3 -2
- package/src/cli/combo.ts +3 -0
- package/src/cli/dispatch.ts +8 -0
- package/src/cli/export-command.ts +2 -2
- package/src/cli/help.ts +2 -0
- package/src/cli/index.ts +3 -2
- package/src/cli/lab.ts +135 -1
- package/src/cli/minimax.ts +491 -0
- package/src/cli/models-runtime.ts +22 -1
- package/src/cli/models.ts +67 -2
- package/src/cli/opencode.ts +2 -1
- package/src/cli/registry.ts +22 -2
- package/src/clients/config-export.ts +125 -7
- package/src/codex/app-server-processes.ts +57 -2
- package/src/codex/app-server-restart-service.ts +232 -0
- package/src/codex/catalog/aggregation.ts +10 -1
- package/src/codex/catalog/effort.ts +15 -3
- package/src/codex/catalog/parsing.ts +3 -1
- package/src/codex/catalog/provider-fetch.ts +45 -5
- package/src/codex/catalog/sync.ts +74 -4
- package/src/codex/convergence.ts +2 -0
- package/src/combos/index.ts +1 -0
- package/src/combos/request.ts +30 -0
- package/src/combos/types.ts +6 -0
- package/src/config.ts +52 -0
- package/src/generated/compatibility-version.json +241 -85
- package/src/images/loop.ts +26 -6
- package/src/integrations/registry.ts +7 -0
- package/src/lab/conformance/jcs.ts +42 -2
- package/src/lab/conformance/negative-controls.ts +6 -2
- package/src/lab/conformance/runner.ts +16 -5
- package/src/lab/fabric/observe.ts +49 -14
- package/src/lab/index.ts +16 -0
- package/src/lab/ledger/purge.ts +152 -83
- package/src/lab/ledger/store.ts +168 -54
- package/src/lab/observe/from-conformance.ts +8 -6
- package/src/lab/observe/from-live.ts +8 -2
- package/src/lab/paths.ts +23 -0
- package/src/lab/public/bundle.ts +217 -0
- package/src/lab/public/community-authority.ts +175 -0
- package/src/lab/public/community-files.ts +29 -0
- package/src/lab/public/community.ts +479 -0
- package/src/lab/public/file-safety.ts +155 -0
- package/src/lab/public/ids.ts +26 -0
- package/src/lab/public/index.ts +16 -0
- package/src/lab/public/mutation-lock.ts +424 -0
- package/src/lab/public/operator.ts +353 -0
- package/src/lab/public/origin-purge.ts +79 -0
- package/src/lab/public/origin.ts +203 -0
- package/src/lab/public/privacy.ts +143 -0
- package/src/lab/public/private-file.ts +261 -0
- package/src/lab/public/project.ts +124 -0
- package/src/lab/public/purge-test-fault.ts +21 -0
- package/src/lab/public/purge.ts +223 -0
- package/src/lab/public/registry.ts +44 -0
- package/src/lab/public/revocation.ts +252 -0
- package/src/lab/public/signature.ts +219 -0
- package/src/lab/public/storage.ts +105 -0
- package/src/lab/public/strict-json.ts +206 -0
- package/src/lab/public/time.ts +26 -0
- package/src/lab/public/types.ts +172 -0
- package/src/lab/public/validate.ts +391 -0
- package/src/lib/codex-restart-contract.ts +120 -0
- package/src/lib/lab-activation.ts +109 -47
- package/src/lib/lab-live-pinned-sender.ts +16 -5
- package/src/lib/pinned-http.ts +70 -16
- package/src/lib/self-launch-argv.ts +15 -0
- package/src/lib/state-store-registrations.ts +2 -0
- package/src/lib/upstream-reachability.ts +4 -0
- package/src/lib/windows-elevation.ts +10 -1
- package/src/providers/derive.ts +24 -4
- package/src/providers/registry.ts +14 -4
- package/src/providers/request-pacing.ts +310 -0
- package/src/providers/service-tier.ts +143 -0
- package/src/providers/static-model-discovery.ts +86 -0
- package/src/reasoning-effort.ts +27 -1
- package/src/responses/custom-tool-compat.ts +4 -1
- package/src/responses/parser.ts +7 -1
- package/src/responses/provider-opaque-metadata.ts +73 -0
- package/src/responses/schema.ts +6 -0
- package/src/router.ts +23 -6
- package/src/routing/capability.ts +4 -2
- package/src/routing/compatibility/behavior.ts +5 -1
- package/src/server/adapter-resolve.ts +2 -32
- package/src/server/auth-cors.ts +50 -6
- package/src/server/chat-completions.ts +74 -36
- package/src/server/chat-native-sse.ts +331 -0
- package/src/server/chat-native.ts +371 -0
- package/src/server/management/combo-routes.ts +16 -2
- package/src/server/management/config-routes.ts +6 -4
- package/src/server/management/context.ts +17 -0
- package/src/server/management/lab-routes.ts +181 -19
- package/src/server/management/model-routes.ts +76 -2
- package/src/server/management/model-rows.ts +8 -0
- package/src/server/management/provider-capability-config.ts +48 -0
- package/src/server/management/provider-routes.ts +76 -4
- package/src/server/management/system-restart.ts +4 -2
- package/src/server/management/system-routes.ts +38 -0
- package/src/server/relay.ts +17 -3
- package/src/server/responses/compact.ts +4 -1
- package/src/server/responses/core.ts +257 -46
- package/src/server/responses/empty-completion-guard.ts +276 -0
- package/src/server/responses/fetch-helpers.ts +35 -4
- package/src/server/responses/pacing-overload.ts +13 -0
- package/src/server/responses/policy-fallback.ts +16 -2
- package/src/server/responses/terminal-guard.ts +1 -1
- package/src/server/responses/upstream-error.ts +5 -0
- package/src/server/responses.ts +17 -2
- package/src/server/system-env.ts +1 -1
- package/src/types.ts +84 -5
- package/src/update/index.ts +6 -5
- package/src/update/job.ts +5 -6
- package/src/update/notify.ts +5 -3
- package/src/usage/log.ts +11 -1
- package/src/web-search/loop.ts +21 -5
- package/gui/dist/assets/index-CQ7bIKee.css +0 -1
- package/gui/dist/assets/index-D_JUZLEC.js +0 -76
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
normalizeNonBlankStringArray,
|
|
15
15
|
providerBaseUrlConfigError,
|
|
16
16
|
providerHeadersConfigError,
|
|
17
|
+
requestPacingConfigError,
|
|
17
18
|
readConfigAdmissionSnapshot,
|
|
18
19
|
saveConfigPreservingClaudeCode,
|
|
19
20
|
withConfigMutationLockSync,
|
|
@@ -44,6 +45,7 @@ import {
|
|
|
44
45
|
import { routedSlug, slugEquals } from "../../providers/slug-codec";
|
|
45
46
|
import { clearAccountQuotaCache, clearProviderQuotaCache, fetchProviderQuotaReports } from "../../providers/quota";
|
|
46
47
|
import { clearKeyCooldowns } from "../../providers/key-failover";
|
|
48
|
+
import { providerRequestPacingStatus } from "../../providers/request-pacing";
|
|
47
49
|
import { CODEX_FORWARD_BASE_URL, isCanonicalOpenAiForwardProvider } from "../../providers/openai-tiers";
|
|
48
50
|
import { codexAccountNamespaceProviderCollisionError } from "../../codex/account-namespace-match";
|
|
49
51
|
import { clearThreadAccountMap } from "../../codex/routing";
|
|
@@ -71,6 +73,7 @@ import { filterRequestLogs, getRequestLogEntries, type RequestLogEntry } from ".
|
|
|
71
73
|
import { estimateComboCost, estimateRequestCost, normalizeCostTokens, tokensPerSecond } from "../../usage/cost";
|
|
72
74
|
import type { PersistedUsageAttempt } from "../../usage/log";
|
|
73
75
|
import { isAllowedRequestOrigin, jsonResponse, providerManagementConfigError, publicProviderBaseUrl, safeConfigDTO } from "../auth-cors";
|
|
76
|
+
import { providerServiceTierConfigError } from "./provider-capability-config";
|
|
74
77
|
import { applySystemEnvToggle } from "../system-env";
|
|
75
78
|
import {
|
|
76
79
|
LOCAL_PROVIDER_RELOAD_NAME_HEADER,
|
|
@@ -177,6 +180,20 @@ function applyProviderPatchFields(
|
|
|
177
180
|
next.liveModels = rawBody.liveModels;
|
|
178
181
|
touched = true;
|
|
179
182
|
}
|
|
183
|
+
if (Object.hasOwn(rawBody, "requestPacing")) {
|
|
184
|
+
const value = rawBody.requestPacing;
|
|
185
|
+
if (value === null) {
|
|
186
|
+
delete next.requestPacing;
|
|
187
|
+
} else {
|
|
188
|
+
if (!isPlainRecord(value)) return { error: "requestPacing must be a plain object or null" };
|
|
189
|
+
const pacingError = requestPacingConfigError(value);
|
|
190
|
+
if (pacingError) return { error: pacingError };
|
|
191
|
+
// `requestPacingConfigError` is the runtime narrowing boundary above; keep the
|
|
192
|
+
// assertion explicit because a generic plain record cannot express `enabled`.
|
|
193
|
+
next.requestPacing = structuredClone(value) as unknown as OcxProviderConfig["requestPacing"];
|
|
194
|
+
}
|
|
195
|
+
touched = true;
|
|
196
|
+
}
|
|
180
197
|
// The Models page edits the catalog hints in place; keep them on the existing
|
|
181
198
|
// provider mutation path so validation, cache invalidation, and convergence stay unified (#1073).
|
|
182
199
|
if (Object.hasOwn(rawBody, "contextWindow")) {
|
|
@@ -216,6 +233,29 @@ function applyProviderPatchFields(
|
|
|
216
233
|
}
|
|
217
234
|
touched = true;
|
|
218
235
|
}
|
|
236
|
+
if (Object.hasOwn(rawBody, "modelSupportsServiceTier")) {
|
|
237
|
+
const value = rawBody.modelSupportsServiceTier;
|
|
238
|
+
if (value === null) {
|
|
239
|
+
delete next.modelSupportsServiceTier;
|
|
240
|
+
} else {
|
|
241
|
+
if (!isPlainRecord(value)) return { error: "modelSupportsServiceTier must be a plain object or null" };
|
|
242
|
+
const capabilities: Record<string, boolean> = { ...(next.modelSupportsServiceTier ?? {}) };
|
|
243
|
+
for (const [model, supported] of Object.entries(value)) {
|
|
244
|
+
if (!model.trim()) return { error: "modelSupportsServiceTier keys must be nonblank model ids" };
|
|
245
|
+
if (supported === null) {
|
|
246
|
+
delete capabilities[model];
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
if (typeof supported !== "boolean") {
|
|
250
|
+
return { error: "modelSupportsServiceTier values must be booleans or null" };
|
|
251
|
+
}
|
|
252
|
+
capabilities[model] = supported;
|
|
253
|
+
}
|
|
254
|
+
if (Object.keys(capabilities).length > 0) next.modelSupportsServiceTier = capabilities;
|
|
255
|
+
else delete next.modelSupportsServiceTier;
|
|
256
|
+
}
|
|
257
|
+
touched = true;
|
|
258
|
+
}
|
|
219
259
|
if (Object.hasOwn(rawBody, "noStructuredOutputModels")) {
|
|
220
260
|
const value = rawBody.noStructuredOutputModels;
|
|
221
261
|
if (value === null) {
|
|
@@ -304,6 +344,22 @@ export async function handleProviderRoutes(ctx: ManagementContext): Promise<Resp
|
|
|
304
344
|
return jsonResponse(await fetchProviderQuotaReports(config, forceRefresh));
|
|
305
345
|
}
|
|
306
346
|
|
|
347
|
+
if (url.pathname === "/api/provider-request-pacing" && req.method === "GET") {
|
|
348
|
+
const name = url.searchParams.get("name")?.trim();
|
|
349
|
+
if (name) {
|
|
350
|
+
if (!isValidProviderName(name) || !hasOwnProvider(config.providers, name)) {
|
|
351
|
+
return jsonResponse({ error: "unknown provider" }, 404);
|
|
352
|
+
}
|
|
353
|
+
return jsonResponse(providerRequestPacingStatus(name, config.providers[name]!));
|
|
354
|
+
}
|
|
355
|
+
return jsonResponse(Object.fromEntries(
|
|
356
|
+
Object.entries(config.providers).map(([providerName, provider]) => [
|
|
357
|
+
providerName,
|
|
358
|
+
providerRequestPacingStatus(providerName, provider),
|
|
359
|
+
]),
|
|
360
|
+
));
|
|
361
|
+
}
|
|
362
|
+
|
|
307
363
|
if (url.pathname === "/api/providers" && req.method === "GET") {
|
|
308
364
|
return jsonResponse(Object.entries(config.providers).map(([name, p]) => ({
|
|
309
365
|
name, adapter: p.adapter, baseUrl: publicProviderBaseUrl(p.baseUrl), defaultModel: p.defaultModel,
|
|
@@ -312,9 +368,11 @@ export async function handleProviderRoutes(ctx: ManagementContext): Promise<Resp
|
|
|
312
368
|
hasHeaders: !!p.headers && Object.keys(p.headers).length > 0,
|
|
313
369
|
allowPrivateNetwork: p.allowPrivateNetwork === true,
|
|
314
370
|
liveModels: p.liveModels !== false,
|
|
371
|
+
requestPacing: p.requestPacing,
|
|
315
372
|
models: p.models ?? [],
|
|
316
373
|
contextWindow: p.contextWindow,
|
|
317
374
|
modelContextWindows: p.modelContextWindows,
|
|
375
|
+
modelSupportsServiceTier: p.modelSupportsServiceTier,
|
|
318
376
|
noStructuredOutputModels: p.noStructuredOutputModels,
|
|
319
377
|
authMode: p.authMode,
|
|
320
378
|
apiKeyTransport: p.apiKeyTransport,
|
|
@@ -405,6 +463,8 @@ export async function handleProviderRoutes(ctx: ManagementContext): Promise<Resp
|
|
|
405
463
|
const name = typeof body.name === "string" ? body.name.trim() : "";
|
|
406
464
|
const providerError = providerManagementConfigError(name, body.provider);
|
|
407
465
|
if (providerError) return jsonResponse({ error: providerError }, 400);
|
|
466
|
+
const serviceTierError = providerServiceTierConfigError(name, body.provider);
|
|
467
|
+
if (serviceTierError) return jsonResponse({ error: serviceTierError }, 400);
|
|
408
468
|
const prov = body.provider ? stripCodexRuntimeProviderFields(body.provider as OcxProviderConfig) : undefined;
|
|
409
469
|
if (!name || !prov?.adapter || !prov?.baseUrl) {
|
|
410
470
|
return jsonResponse({ error: "name, provider.adapter and provider.baseUrl are required" }, 400);
|
|
@@ -437,6 +497,7 @@ export async function handleProviderRoutes(ctx: ManagementContext): Promise<Resp
|
|
|
437
497
|
// call can never fire.
|
|
438
498
|
const submittedContextWindow = Object.hasOwn(prov, "contextWindow");
|
|
439
499
|
const submittedModelContextWindows = Object.hasOwn(prov, "modelContextWindows");
|
|
500
|
+
const submittedRequestPacing = Object.hasOwn(prov, "requestPacing");
|
|
440
501
|
enrichProviderFromCatalog(name, prov);
|
|
441
502
|
const { saveConfigPreservingClaudeCode: save } = await import("../../config");
|
|
442
503
|
// Overwriting an existing provider must not drop its multi-key pool: carry it over, then
|
|
@@ -453,6 +514,9 @@ export async function handleProviderRoutes(ctx: ManagementContext): Promise<Resp
|
|
|
453
514
|
// absence in the request means "not carried", never "the user deleted it". Deletion goes
|
|
454
515
|
// through PATCH with an explicit null (#1409).
|
|
455
516
|
const existing = config.providers[name];
|
|
517
|
+
if (!submittedRequestPacing && existing?.requestPacing) {
|
|
518
|
+
prov.requestPacing = structuredClone(existing.requestPacing);
|
|
519
|
+
}
|
|
456
520
|
if (!submittedContextWindow && existing?.contextWindow !== undefined) {
|
|
457
521
|
prov.contextWindow = existing.contextWindow;
|
|
458
522
|
}
|
|
@@ -548,9 +612,12 @@ export async function handleProviderRoutes(ctx: ManagementContext): Promise<Resp
|
|
|
548
612
|
if ("error" in applied) return jsonResponse({ error: applied.error }, 400);
|
|
549
613
|
const next = applied.next;
|
|
550
614
|
|
|
551
|
-
|
|
615
|
+
const pacingOnly = keys.every(key => key === "requestPacing");
|
|
616
|
+
if (applied.editorTouched && !pacingOnly) {
|
|
552
617
|
const providerError = providerManagementConfigError(name, next);
|
|
553
618
|
if (providerError) return jsonResponse({ error: providerError }, 400);
|
|
619
|
+
const serviceTierError = providerServiceTierConfigError(name, next);
|
|
620
|
+
if (serviceTierError) return jsonResponse({ error: serviceTierError }, 400);
|
|
554
621
|
const resolvedError = await providerDestinationResolvedError(name, next);
|
|
555
622
|
if (resolvedError) return jsonResponse({ error: resolvedError }, 400);
|
|
556
623
|
} else if (applied.enablingOpenAi) {
|
|
@@ -575,12 +642,17 @@ export async function handleProviderRoutes(ctx: ManagementContext): Promise<Resp
|
|
|
575
642
|
replayError = replay.error;
|
|
576
643
|
return;
|
|
577
644
|
}
|
|
578
|
-
if (replay.editorTouched) {
|
|
645
|
+
if (replay.editorTouched && !pacingOnly) {
|
|
579
646
|
const syncError = providerManagementConfigError(name, replay.next);
|
|
580
647
|
if (syncError) {
|
|
581
648
|
replayError = syncError;
|
|
582
649
|
return;
|
|
583
650
|
}
|
|
651
|
+
const serviceTierError = providerServiceTierConfigError(name, replay.next);
|
|
652
|
+
if (serviceTierError) {
|
|
653
|
+
replayError = serviceTierError;
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
584
656
|
} else if (replay.enablingOpenAi && !isCanonicalOpenAiForwardProvider(replay.next)) {
|
|
585
657
|
replayError = "provider openai must be the canonical built-in provider";
|
|
586
658
|
return;
|
|
@@ -592,11 +664,11 @@ export async function handleProviderRoutes(ctx: ManagementContext): Promise<Resp
|
|
|
592
664
|
});
|
|
593
665
|
if (replayError !== undefined) return jsonResponse({ error: replayError }, 409);
|
|
594
666
|
reconcileLiveStateStores();
|
|
595
|
-
if (applied.editorTouched) {
|
|
667
|
+
if (applied.editorTouched && !pacingOnly) {
|
|
596
668
|
const { clearModelCache } = await import("../../codex/model-cache");
|
|
597
669
|
clearModelCache(name);
|
|
598
670
|
}
|
|
599
|
-
const catalogRefresh = await convergeCodexCatalog();
|
|
671
|
+
const catalogRefresh = pacingOnly ? null : await convergeCodexCatalog();
|
|
600
672
|
return jsonResponse({
|
|
601
673
|
success: true,
|
|
602
674
|
name,
|
|
@@ -35,6 +35,7 @@ import {
|
|
|
35
35
|
import { isServiceViable } from "../../service";
|
|
36
36
|
import { readRuntimePort } from "../../config";
|
|
37
37
|
import { withProcessRuntimeProvenance } from "../../lib/bun-runtime";
|
|
38
|
+
import { selfLaunchArgv } from "../../lib/self-launch-argv";
|
|
38
39
|
import {
|
|
39
40
|
MEMORY_DRAIN_RESTART_MS,
|
|
40
41
|
REPLACEMENT_READY_TIMEOUT_MS,
|
|
@@ -213,19 +214,20 @@ function spawnDetachedStart(
|
|
|
213
214
|
port?: number,
|
|
214
215
|
waitForHealthBeforeParentExit = true,
|
|
215
216
|
): Promise<void> {
|
|
216
|
-
const args = [
|
|
217
|
+
const args = ["start"];
|
|
217
218
|
const expectedPort = typeof port === "number" && Number.isFinite(port) && port > 0 && port <= 65535
|
|
218
219
|
? Math.trunc(port)
|
|
219
220
|
: undefined;
|
|
220
221
|
if (expectedPort !== undefined) {
|
|
221
222
|
args.push("--port", String(expectedPort));
|
|
222
223
|
}
|
|
224
|
+
const launchArgs = selfLaunchArgv(args);
|
|
223
225
|
return new Promise<void>((resolve, reject) => {
|
|
224
226
|
let child: ReturnType<typeof spawn>;
|
|
225
227
|
try {
|
|
226
228
|
const env: NodeJS.ProcessEnv = { ...process.env };
|
|
227
229
|
delete env.OCX_SERVICE;
|
|
228
|
-
child = spawn(process.execPath,
|
|
230
|
+
child = spawn(process.execPath, launchArgs, {
|
|
229
231
|
detached: true,
|
|
230
232
|
stdio: "ignore",
|
|
231
233
|
windowsHide: true,
|
|
@@ -31,8 +31,16 @@ import {
|
|
|
31
31
|
SYSTEM_RESTART_EXPECTED_PID_HEADER,
|
|
32
32
|
parseExpectedSystemRestartPid,
|
|
33
33
|
} from "../../lib/system-restart-contract";
|
|
34
|
+
import {
|
|
35
|
+
CODEX_APP_SERVER_STATE_PATH,
|
|
36
|
+
CODEX_RESTART_PATH,
|
|
37
|
+
} from "../../lib/codex-restart-contract";
|
|
34
38
|
import { jsonResponse } from "../auth-cors";
|
|
35
39
|
import { getInspectionCounters } from "../relay";
|
|
40
|
+
import type {
|
|
41
|
+
performCodexRestart,
|
|
42
|
+
readCodexAppServerState,
|
|
43
|
+
} from "../../codex/app-server-restart-service";
|
|
36
44
|
import type { ManagementContext } from "./context";
|
|
37
45
|
import { acceptSystemRestart } from "./system-restart";
|
|
38
46
|
|
|
@@ -137,5 +145,35 @@ export async function handleSystemRoutes(ctx: ManagementContext): Promise<Respon
|
|
|
137
145
|
}, 202, req, config);
|
|
138
146
|
}
|
|
139
147
|
|
|
148
|
+
if (
|
|
149
|
+
(url.pathname === CODEX_APP_SERVER_STATE_PATH && req.method === "GET")
|
|
150
|
+
|| (url.pathname === CODEX_RESTART_PATH && req.method === "POST")
|
|
151
|
+
) {
|
|
152
|
+
// Resolved inside the path check, not at the top of this function: every
|
|
153
|
+
// /api/system/* request runs through here, and an unconditional import would
|
|
154
|
+
// pull the platform process-enumeration helpers into requests that never
|
|
155
|
+
// touch them.
|
|
156
|
+
// An explicit branch rather than `??`: the seam is an optional property, and
|
|
157
|
+
// narrowing through a nullish default keeps its `undefined` in the union.
|
|
158
|
+
let service: {
|
|
159
|
+
readState: typeof readCodexAppServerState;
|
|
160
|
+
performRestart: typeof performCodexRestart;
|
|
161
|
+
};
|
|
162
|
+
const injected = ctx.deps.codexRestartService;
|
|
163
|
+
if (injected) {
|
|
164
|
+
service = injected;
|
|
165
|
+
} else {
|
|
166
|
+
const module = await import("../../codex/app-server-restart-service");
|
|
167
|
+
service = {
|
|
168
|
+
readState: module.readCodexAppServerState,
|
|
169
|
+
performRestart: module.performCodexRestart,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
if (req.method === "GET") {
|
|
173
|
+
return jsonResponse(service.readState(), 200, req, config);
|
|
174
|
+
}
|
|
175
|
+
return jsonResponse(await service.performRestart(), 200, req, config);
|
|
176
|
+
}
|
|
177
|
+
|
|
140
178
|
return null;
|
|
141
179
|
}
|
package/src/server/relay.ts
CHANGED
|
@@ -366,6 +366,7 @@ export function trackSseForRequestLog(
|
|
|
366
366
|
): ReadableStream<Uint8Array> {
|
|
367
367
|
const reader = body.getReader();
|
|
368
368
|
let terminalReported = false;
|
|
369
|
+
let cancelled = false;
|
|
369
370
|
|
|
370
371
|
const reportTerminal = (status: ResponsesTerminalStatus) => {
|
|
371
372
|
if (terminalReported) return;
|
|
@@ -385,8 +386,10 @@ export function trackSseForRequestLog(
|
|
|
385
386
|
try {
|
|
386
387
|
const { done, value } = await reader.read();
|
|
387
388
|
if (done) {
|
|
388
|
-
|
|
389
|
-
|
|
389
|
+
if (!cancelled) {
|
|
390
|
+
inspector.finish();
|
|
391
|
+
if (!terminalReported) reportTerminal("incomplete");
|
|
392
|
+
}
|
|
390
393
|
inspector.dispose();
|
|
391
394
|
controller.close();
|
|
392
395
|
return;
|
|
@@ -394,12 +397,19 @@ export function trackSseForRequestLog(
|
|
|
394
397
|
inspector.feed(value);
|
|
395
398
|
controller.enqueue(value);
|
|
396
399
|
} catch (err) {
|
|
397
|
-
|
|
400
|
+
// The upstream read rejected: the 200 body died mid-flight. Client
|
|
401
|
+
// cancellation is the caller's separate 499 path, so a cancel-drained
|
|
402
|
+
// pending read (cancelled=true) must not carry the truncation marker.
|
|
403
|
+
if (!cancelled && !terminalReported && logCtx?.activeAttempt) {
|
|
404
|
+
logCtx.activeAttempt.streamAborted = true;
|
|
405
|
+
}
|
|
406
|
+
if (!cancelled && !terminalReported) reportTerminal("incomplete");
|
|
398
407
|
inspector.dispose();
|
|
399
408
|
try { controller.error(err); } catch { /* already torn down */ }
|
|
400
409
|
}
|
|
401
410
|
},
|
|
402
411
|
cancel(reason) {
|
|
412
|
+
cancelled = true;
|
|
403
413
|
inspector.dispose();
|
|
404
414
|
onCancel();
|
|
405
415
|
reader.cancel(reason).catch(() => {});
|
|
@@ -1122,6 +1132,10 @@ export function consumeForInspection(
|
|
|
1122
1132
|
if (logCtx) {
|
|
1123
1133
|
logCtx.transportPhase = "mid_stream";
|
|
1124
1134
|
logCtx.terminalSource = "synthetic";
|
|
1135
|
+
// A truncated 200 body must not meter as a success the client never
|
|
1136
|
+
// received; the router's equivalent turn carries 502 + streamAborted
|
|
1137
|
+
// (codex-router #139).
|
|
1138
|
+
if (logCtx.activeAttempt) logCtx.activeAttempt.streamAborted = true;
|
|
1125
1139
|
}
|
|
1126
1140
|
onTerminal("failed", 502);
|
|
1127
1141
|
}
|
|
@@ -490,7 +490,10 @@ export async function handleResponsesCompact(
|
|
|
490
490
|
req.signal,
|
|
491
491
|
connectMs,
|
|
492
492
|
false,
|
|
493
|
-
providerFetch(sendProvider
|
|
493
|
+
providerFetch(sendProvider, undefined, {
|
|
494
|
+
providerName: route.providerName,
|
|
495
|
+
modelId: route.modelId,
|
|
496
|
+
}),
|
|
494
497
|
// Every credential-bearing forward send gets manual redirects, not only
|
|
495
498
|
// pool sends: direct mode carries the caller's credential too (#914).
|
|
496
499
|
sendProvider.authMode === "forward",
|