@iislee/opencodex 2.31.0 → 2.35.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 +3 -3
- package/bin/ocx.mjs +99 -70
- package/gui/dist/assets/index-Be1sDpWw.css +1 -0
- package/gui/dist/assets/index-D4OKgUV4.js +114 -0
- package/gui/dist/index.html +2 -2
- package/package.json +3 -1
- package/src/adapters/anthropic.ts +104 -15
- package/src/adapters/base.ts +29 -1
- package/src/adapters/command-code.ts +42 -6
- package/src/adapters/cursor/call-id.ts +44 -0
- package/src/adapters/cursor/catalog.ts +541 -0
- package/src/adapters/cursor/checkpoint-store.ts +15 -10
- package/src/adapters/cursor/cursor-errors.ts +15 -0
- package/src/adapters/cursor/discovery.ts +69 -18
- package/src/adapters/cursor/effort-map.ts +79 -1
- package/src/adapters/cursor/envelope-echo.ts +290 -0
- package/src/adapters/cursor/live-models.ts +7 -2
- package/src/adapters/cursor/live-transport.ts +14 -0
- package/src/adapters/cursor/message-mapper.ts +4 -1
- package/src/adapters/cursor/native-exec-desktop.ts +23 -0
- package/src/adapters/cursor/native-exec-fs.ts +2 -2
- package/src/adapters/cursor/native-exec-network.ts +1 -1
- package/src/adapters/cursor/native-exec-shell.ts +5 -2
- package/src/adapters/cursor/native-exec.ts +20 -0
- package/src/adapters/cursor/protobuf-request.ts +68 -14
- package/src/adapters/cursor/request-builder.ts +40 -10
- package/src/adapters/cursor/tool-definitions.ts +14 -4
- package/src/adapters/cursor/tool-result-normalize.ts +28 -5
- package/src/adapters/cursor/types.ts +20 -0
- package/src/adapters/cursor.ts +209 -38
- package/src/adapters/exec-tool-result-normalize.ts +99 -0
- package/src/adapters/google-antigravity-replay.ts +173 -25
- package/src/adapters/google-antigravity-wire.ts +5 -0
- package/src/adapters/google-errors.ts +44 -12
- package/src/adapters/google.ts +136 -24
- package/src/adapters/image.ts +1 -1
- package/src/adapters/kiro-constants.ts +27 -0
- package/src/adapters/kiro-tools.ts +55 -16
- package/src/adapters/kiro.ts +182 -23
- package/src/adapters/openai-chat.ts +300 -242
- package/src/adapters/openai-responses.ts +353 -27
- package/src/adapters/run-turn-queue.ts +36 -1
- package/src/adapters/tool-catalog-nudge.ts +4 -3
- package/src/adapters/xai-tool-schema.ts +436 -0
- package/src/adapters/xai-web-search.ts +17 -16
- package/src/bridge.ts +90 -37
- package/src/chat/inbound.ts +14 -1
- package/src/chat/outbound.ts +15 -7
- package/src/claude/agents-inject.ts +8 -1
- package/src/claude/context-windows.ts +16 -9
- package/src/claude/outbound.ts +24 -11
- package/src/cli/access.ts +46 -3
- package/src/cli/account-api.ts +116 -19
- package/src/cli/account-extended.ts +262 -29
- package/src/cli/account-main.ts +12 -12
- package/src/cli/account.ts +69 -15
- package/src/cli/agent.ts +8 -1
- package/src/cli/alias.ts +66 -0
- package/src/cli/capabilities-command.ts +94 -0
- package/src/cli/capabilities.ts +496 -0
- package/src/cli/claude-desktop.ts +31 -11
- package/src/cli/claude.ts +26 -1
- package/src/cli/dispatch.ts +256 -28
- package/src/cli/doctor.ts +126 -14
- package/src/cli/help.ts +17 -6
- package/src/cli/index.ts +39 -9
- package/src/cli/init.ts +1 -0
- package/src/cli/inspect.ts +230 -0
- package/src/cli/models-runtime.ts +95 -0
- package/src/cli/models.ts +26 -10
- package/src/cli/observe.ts +31 -8
- package/src/cli/provider-runtime.ts +16 -2
- package/src/cli/provider.ts +2 -1
- package/src/cli/registry.ts +49 -10
- package/src/cli/runtime-api.ts +51 -7
- package/src/cli/status.ts +41 -1
- package/src/cli/storage.ts +234 -0
- package/src/cli/system-command.ts +16 -0
- package/src/cli/system-restart-client.ts +1 -1
- package/src/cli/usage-report.ts +184 -0
- package/src/cli/v2.ts +34 -10
- package/src/cli/version-skew.ts +46 -0
- package/src/clients/config-export.ts +85 -3
- package/src/clients/effective-status.ts +30 -2
- package/src/clients/probes/cline.ts +81 -0
- package/src/codex/account-label.ts +21 -0
- package/src/codex/account-pause.ts +2 -1
- package/src/codex/account-priority.ts +3 -2
- package/src/codex/app-server-processes.ts +83 -7
- package/src/codex/auth-api.ts +50 -10
- package/src/codex/auth-context.ts +21 -18
- package/src/codex/autostart-health.ts +16 -0
- package/src/codex/catalog/aggregation.ts +18 -0
- package/src/codex/catalog/effort.ts +18 -3
- package/src/codex/catalog/metadata.ts +27 -1
- package/src/codex/catalog/native-models.ts +5 -2
- package/src/codex/catalog/parsing.ts +72 -27
- package/src/codex/catalog/provider-fetch.ts +187 -31
- package/src/codex/catalog/sync.ts +131 -5
- package/src/codex/catalog.ts +1 -1
- package/src/codex/codex-write-lock.ts +3 -1
- package/src/codex/convergence-types.ts +1 -1
- package/src/codex/convergence.ts +27 -2
- package/src/codex/desired-state.ts +2 -2
- package/src/codex/desktop-app-restart.ts +355 -0
- package/src/codex/history-job.ts +32 -3
- package/src/codex/history-manifest.ts +112 -0
- package/src/codex/history-migration-guardian.ts +5 -5
- package/src/codex/history-provider.ts +825 -247
- package/src/codex/history-worker.ts +8 -5
- package/src/codex/inject-coordination.ts +83 -0
- package/src/codex/inject.ts +63 -22
- package/src/codex/injected-marker.ts +1 -1
- package/src/codex/internal/history-writer.ts +4 -3
- package/src/codex/log-guard/inspect.ts +22 -4
- package/src/codex/model-entitlements.ts +9 -2
- package/src/codex/native-profile-startup.ts +157 -27
- package/src/codex/native-residue.ts +26 -33
- package/src/codex/prompt-layers.ts +371 -25
- package/src/codex/prompt-text-probe.ts +238 -0
- package/src/codex/quota.ts +91 -10
- package/src/codex/routing.ts +9 -0
- package/src/codex/shim.ts +56 -3
- package/src/codex/subagent-model-fallback.ts +104 -25
- package/src/codex/transition-state.ts +116 -8
- package/src/combos/failover.ts +27 -0
- package/src/combos/types.ts +10 -0
- package/src/compatibility/index.ts +26 -0
- package/src/compatibility/manifest.ts +253 -0
- package/src/compatibility/openai-responses.ts +121 -0
- package/src/config/atomic-write.ts +219 -0
- package/src/config/paths.ts +40 -0
- package/src/config/process-state.ts +309 -0
- package/src/config/provider-validation.ts +177 -0
- package/src/config/rebase-provenance.ts +68 -0
- package/src/config.ts +206 -824
- package/src/generated/compatibility-version.json +373 -213
- package/src/images/loop.ts +5 -4
- package/src/images/plan.ts +5 -4
- package/src/integrations/ownership-policy.ts +160 -0
- package/src/integrations/ownership.ts +46 -2
- package/src/integrations/registry.ts +7 -0
- package/src/integrations/state.ts +79 -7
- package/src/integrations/writer.ts +26 -2
- package/src/lab/conformance/fixtures/protocol-v1-cases.json +1 -1
- package/src/lab/fabric/producer-child.ts +1 -1
- package/src/lib/admin-secrets.ts +24 -0
- package/src/lib/bounded-body.ts +14 -2
- package/src/lib/config-ownership.ts +20 -0
- package/src/lib/errors.ts +36 -3
- package/src/lib/package-tree-integrity.ts +101 -0
- package/src/lib/process-control.ts +2 -1
- package/src/lib/service-secrets.ts +15 -0
- package/src/lib/tool-argument-integers.ts +56 -5
- package/src/oauth/callback-server.ts +22 -2
- package/src/oauth/cline.ts +203 -0
- package/src/oauth/generic-account-failover.ts +231 -0
- package/src/oauth/health.ts +1 -1
- package/src/oauth/index.ts +65 -6
- package/src/oauth/key-providers.ts +10 -1
- package/src/oauth/kimi.ts +9 -1
- package/src/oauth/kiro.ts +45 -0
- package/src/oauth/local-token-detect.ts +2 -0
- package/src/oauth/open-browser-choice.ts +26 -0
- package/src/oauth/store.ts +14 -5
- package/src/oauth/types.ts +15 -0
- package/src/providers/auto-compact-budget.ts +65 -0
- package/src/providers/command-code-efforts.ts +38 -6
- package/src/providers/context-cap.ts +4 -3
- package/src/providers/default-aliases.ts +65 -0
- package/src/providers/derive.ts +28 -0
- package/src/providers/fastwire.ts +7 -1
- package/src/providers/label.ts +34 -1
- package/src/providers/model-presets.ts +119 -0
- package/src/providers/new-model-policy.ts +146 -0
- package/src/providers/provider-id-rewrite.ts +2 -1
- package/src/providers/quota.ts +65 -7
- package/src/providers/registry.ts +184 -69
- package/src/providers/slug-codec.ts +52 -0
- package/src/providers/xai-transport.ts +21 -0
- package/src/reasoning-effort.ts +19 -2
- package/src/responses/apply-patch-envelope.ts +63 -0
- package/src/responses/code-mode-helper-compat.ts +50 -0
- package/src/responses/custom-tool-compat.ts +157 -39
- package/src/responses/namespace-tool-compat.ts +84 -4
- package/src/responses/parser.ts +7 -2
- package/src/responses/reasoning-replay-cache.ts +81 -3
- package/src/responses/schema.ts +5 -1
- package/src/responses/state.ts +94 -4
- package/src/responses/thought-signature-replay.ts +17 -0
- package/src/responses/turn-termination.ts +107 -0
- package/src/router.ts +40 -2
- package/src/routing/analytics.ts +1 -0
- package/src/routing/quota.ts +10 -0
- package/src/server/auth-cors.ts +27 -7
- package/src/server/chat-completions.ts +24 -16
- package/src/server/chat-native-sse.ts +3 -3
- package/src/server/chat-native.ts +30 -11
- package/src/server/claude-messages.ts +1 -1
- package/src/server/index.ts +179 -32
- package/src/server/lifecycle.ts +52 -1
- package/src/server/local-management-read-client.ts +1 -1
- package/src/server/local-provider-reload-client.ts +1 -1
- package/src/server/management/agent-settings-routes.ts +45 -23
- package/src/server/management/codex-prompt-routes.ts +570 -0
- package/src/server/management/combo-routes.ts +2 -1
- package/src/server/management/config-routes.ts +59 -12
- package/src/server/management/context.ts +10 -1
- package/src/server/management/logs-usage-routes.ts +30 -24
- package/src/server/management/model-routes.ts +274 -4
- package/src/server/management/model-rows.ts +4 -0
- package/src/server/management/native-integration-routes.ts +2 -1
- package/src/server/management/oauth-account-routes.ts +10 -4
- package/src/server/management/provider-capability-config.ts +1 -1
- package/src/server/management/provider-routes.ts +81 -11
- package/src/server/management/route-registry.ts +332 -0
- package/src/server/management/routing-profile-routes.ts +2 -2
- package/src/server/management/system-restart.ts +1 -1
- package/src/server/management-api.ts +18 -16
- package/src/server/port-reclaim.ts +1 -1
- package/src/server/proxy-liveness.ts +29 -5
- package/src/server/relay-eager.ts +147 -20
- package/src/server/relay.ts +251 -19
- package/src/server/request-log-conversation.ts +51 -0
- package/src/server/request-log.ts +70 -16
- package/src/server/responses/codex-auth-error.ts +55 -0
- package/src/server/responses/collaboration.ts +37 -3
- package/src/server/responses/combo-stream-preflight.ts +178 -0
- package/src/server/responses/compact.ts +6 -21
- package/src/server/responses/core.ts +810 -208
- package/src/server/responses/empty-completion-guard.ts +63 -6
- package/src/server/responses/fetch-helpers.ts +17 -102
- package/src/server/responses/input-admission.ts +3 -1
- package/src/server/responses/passthrough-error.ts +33 -9
- package/src/server/responses/responses-field-backfill.ts +105 -13
- package/src/server/responses/ws-upstream.ts +75 -2
- package/src/server/responses-custom-tool-repair.ts +87 -6
- package/src/server/responses-terminal-repair.ts +25 -4
- package/src/server/responses-undeclared-tool-guard.ts +241 -18
- package/src/server/sse-frame-buffer.ts +31 -4
- package/src/server/ws-bridge.ts +14 -2
- package/src/service.ts +42 -4
- package/src/storage/policy-job.ts +14 -4
- package/src/storage/policy.ts +88 -23
- package/src/types/config.ts +66 -0
- package/src/types/provider.ts +69 -0
- package/src/types/request.ts +17 -4
- package/src/types/tools.ts +116 -11
- package/src/types.ts +3 -1
- package/src/update/index.ts +5 -4
- package/src/update/job.ts +3 -1
- package/src/update/transactional-install.mjs +8 -1
- package/src/usage/expected-prices.ts +43 -24
- package/src/usage/log.ts +63 -12
- package/src/usage/summary.ts +211 -8
- package/src/vision/describe.ts +18 -13
- package/src/web-search/exa-executor.ts +40 -9
- package/src/web-search/executor.ts +10 -3
- package/src/web-search/loop.ts +5 -4
- package/gui/dist/assets/index-BF38heuV.js +0 -104
- package/gui/dist/assets/index-DMiI18Kv.css +0 -1
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { formatErrorResponse } from "../../bridge";
|
|
2
|
+
import {
|
|
3
|
+
CodexAccountCooldownError,
|
|
4
|
+
codexMainProfileDrainingResponse,
|
|
5
|
+
cooldownErrorResponse,
|
|
6
|
+
CodexAuthContextError,
|
|
7
|
+
CodexDirectAuthenticationError,
|
|
8
|
+
CodexMainProfileDrainingError,
|
|
9
|
+
CodexMainSubstitutionUnavailableError,
|
|
10
|
+
CodexPoolAuthenticationError,
|
|
11
|
+
CodexThreadAffinityExpiredError,
|
|
12
|
+
} from "../../codex/auth-context";
|
|
13
|
+
|
|
14
|
+
export interface CodexAuthContextErrorResponseOptions {
|
|
15
|
+
accountSelector?: string;
|
|
16
|
+
now: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Shared HTTP contract for Codex auth-context failures on Responses surfaces. */
|
|
20
|
+
export function mapCodexAuthContextErrorToResponse(
|
|
21
|
+
error: unknown,
|
|
22
|
+
options: CodexAuthContextErrorResponseOptions,
|
|
23
|
+
): Response | undefined {
|
|
24
|
+
if (error instanceof CodexAccountCooldownError) {
|
|
25
|
+
return cooldownErrorResponse(error, options.now, options.accountSelector);
|
|
26
|
+
}
|
|
27
|
+
if (error instanceof CodexMainProfileDrainingError) {
|
|
28
|
+
return codexMainProfileDrainingResponse();
|
|
29
|
+
}
|
|
30
|
+
if (error instanceof CodexThreadAffinityExpiredError) {
|
|
31
|
+
return formatErrorResponse(
|
|
32
|
+
409,
|
|
33
|
+
"invalid_request_error",
|
|
34
|
+
"Codex thread account affinity expired; start a new session",
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
if (error instanceof CodexAuthContextError) {
|
|
38
|
+
return formatErrorResponse(
|
|
39
|
+
401,
|
|
40
|
+
"authentication_error",
|
|
41
|
+
"Selected Codex account needs reauthentication",
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
if (error instanceof CodexPoolAuthenticationError || error instanceof CodexDirectAuthenticationError) {
|
|
45
|
+
return formatErrorResponse(401, "authentication_error", error.message);
|
|
46
|
+
}
|
|
47
|
+
if (error instanceof CodexMainSubstitutionUnavailableError) {
|
|
48
|
+
return formatErrorResponse(
|
|
49
|
+
401,
|
|
50
|
+
"authentication_error",
|
|
51
|
+
"No usable Codex main credential to serve this request",
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
@@ -2,6 +2,7 @@ import type { Server } from "bun";
|
|
|
2
2
|
import { bridgeToResponsesSSE, buildResponseJSON, formatErrorResponse, type ResponsesTerminalStatus } from "../../bridge";
|
|
3
3
|
import {
|
|
4
4
|
getConfigPath,
|
|
5
|
+
loadConfig,
|
|
5
6
|
multiAgentGuidanceEnabled,
|
|
6
7
|
resolveEnvValue,
|
|
7
8
|
} from "../../config";
|
|
@@ -10,6 +11,7 @@ import { buildCompactV1Output, COMPACT_PROMPT, decodeCompactionSummary, extractC
|
|
|
10
11
|
import { FORWARD_HEADERS, sanitizeReasoningInputContent } from "../../adapters/openai-responses";
|
|
11
12
|
import { expandPreviousResponseInput, previousResponseProviderState, rememberResponseState } from "../../responses/state";
|
|
12
13
|
import { routeModel } from "../../router";
|
|
14
|
+
import type { RawEntry } from "../../codex/catalog/parsing";
|
|
13
15
|
import {
|
|
14
16
|
advanceComboAfterFailure,
|
|
15
17
|
comboDefaultEffort,
|
|
@@ -243,15 +245,47 @@ export async function resolveEffectiveSubagentRoster(
|
|
|
243
245
|
surface: SpawnAgentSurface,
|
|
244
246
|
): Promise<EffectiveSubagentRoster> {
|
|
245
247
|
const { effectiveSubagentRoster } = await import("../../codex/catalog");
|
|
246
|
-
return effectiveSubagentRoster(configuredModels, surface);
|
|
248
|
+
return effectiveSubagentRoster(configuredModels, surface, await freshSubagentCatalogEntries());
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* The persisted Codex catalog, with native context metadata re-derived from the CURRENT config.
|
|
253
|
+
*
|
|
254
|
+
* The subagent roster reads the catalog FILE, which is only as fresh as the last sync. A row
|
|
255
|
+
* written before the operator widened the native window keeps its old `context_window`, so a
|
|
256
|
+
* child was planning and compacting against a narrower budget than its parent — measured at
|
|
257
|
+
* 272,000 x 95% = 258,400 while the request path resolved 922,000 (#2574).
|
|
258
|
+
*
|
|
259
|
+
* Re-applying the override is cheap and idempotent: it is the same function the writer uses,
|
|
260
|
+
* so a fresh catalog is unchanged and a stale one is repaired in memory rather than silently
|
|
261
|
+
* believed. It does not rewrite the file — the row on disk stays whatever the last sync wrote,
|
|
262
|
+
* and `ocx sync` remains what refreshes it.
|
|
263
|
+
*/
|
|
264
|
+
async function freshSubagentCatalogEntries(): Promise<RawEntry[]> {
|
|
265
|
+
const { readCatalog, readCodexCatalogPath, nativeContextLimits } = await import("../../codex/catalog");
|
|
266
|
+
const { applyNativeOpenAiContextOverride } = await import("../../codex/catalog/parsing");
|
|
267
|
+
const entries = readCatalog(readCodexCatalogPath())?.models ?? [];
|
|
268
|
+
if (entries.length === 0) return [];
|
|
269
|
+
let limits: ReturnType<typeof nativeContextLimits>;
|
|
270
|
+
try {
|
|
271
|
+
limits = nativeContextLimits(loadConfig());
|
|
272
|
+
} catch {
|
|
273
|
+
// An unreadable config must not cost the caller its roster; serve the file as written.
|
|
274
|
+
return entries;
|
|
275
|
+
}
|
|
276
|
+
return entries.map(entry => {
|
|
277
|
+
const clone = { ...entry };
|
|
278
|
+
applyNativeOpenAiContextOverride(clone, limits);
|
|
279
|
+
return clone;
|
|
280
|
+
});
|
|
247
281
|
}
|
|
248
282
|
|
|
249
283
|
/** Reuse one parsed catalog snapshot across every roster projection for this request. */
|
|
250
284
|
async function createRequestScopedSubagentRosterResolver(): Promise<NonNullable<
|
|
251
285
|
MultiAgentGuidanceDeps["resolveEffectiveSubagentRoster"]
|
|
252
286
|
>> {
|
|
253
|
-
const { effectiveSubagentRoster
|
|
254
|
-
const catalogEntries =
|
|
287
|
+
const { effectiveSubagentRoster } = await import("../../codex/catalog");
|
|
288
|
+
const catalogEntries = await freshSubagentCatalogEntries();
|
|
255
289
|
return (configuredModels, surface) =>
|
|
256
290
|
effectiveSubagentRoster(configuredModels, surface, catalogEntries);
|
|
257
291
|
}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import type { ResponsesTerminalStatus } from "../../bridge";
|
|
2
|
+
import type { RequestLogContext } from "../request-log";
|
|
3
|
+
import { createSseInspector } from "../relay";
|
|
4
|
+
import { MAX_CLIENT_SSE_FRAME_BYTES } from "../sse-frame-buffer";
|
|
5
|
+
|
|
6
|
+
const COMBO_STREAM_PREFLIGHT_MAX_BYTES = MAX_CLIENT_SSE_FRAME_BYTES;
|
|
7
|
+
// Keep retained object count proportional to the same byte budget used by the
|
|
8
|
+
// shared SSE framer. Tiny or empty upstream reads must not bypass the byte cap.
|
|
9
|
+
const COMBO_STREAM_PREFLIGHT_MAX_CHUNKS = Math.max(
|
|
10
|
+
1,
|
|
11
|
+
Math.ceil(COMBO_STREAM_PREFLIGHT_MAX_BYTES / 1024),
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
const PRE_OUTPUT_CONTROL_EVENTS = new Set([
|
|
15
|
+
"response.created",
|
|
16
|
+
"response.in_progress",
|
|
17
|
+
"response.queued",
|
|
18
|
+
"response.heartbeat",
|
|
19
|
+
]);
|
|
20
|
+
|
|
21
|
+
const TERMINAL_EVENTS = new Set([
|
|
22
|
+
"response.completed",
|
|
23
|
+
"response.failed",
|
|
24
|
+
"response.incomplete",
|
|
25
|
+
]);
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Decide when replaying the request on another combo target would risk duplicating
|
|
29
|
+
* client-visible output or a tool-side effect. Unknown event types commit the child
|
|
30
|
+
* conservatively; only the small Responses lifecycle preamble remains replayable.
|
|
31
|
+
*/
|
|
32
|
+
export function comboStreamPayloadCommitsOutput(payload: unknown): boolean {
|
|
33
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload)) return true;
|
|
34
|
+
const type = (payload as { type?: unknown }).type;
|
|
35
|
+
if (typeof type !== "string") return true;
|
|
36
|
+
return !PRE_OUTPUT_CONTROL_EVENTS.has(type) && !TERMINAL_EVENTS.has(type);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function replayBufferedResponse(
|
|
40
|
+
response: Response,
|
|
41
|
+
reader: ReadableStreamDefaultReader<Uint8Array>,
|
|
42
|
+
buffered: Uint8Array[],
|
|
43
|
+
): Response {
|
|
44
|
+
let index = 0;
|
|
45
|
+
const body = new ReadableStream<Uint8Array>({
|
|
46
|
+
async pull(controller) {
|
|
47
|
+
if (index < buffered.length) {
|
|
48
|
+
controller.enqueue(buffered[index++]!);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
try {
|
|
52
|
+
const next = await reader.read();
|
|
53
|
+
if (next.done) controller.close();
|
|
54
|
+
else controller.enqueue(next.value);
|
|
55
|
+
} catch (error) {
|
|
56
|
+
try { controller.error(error); } catch { /* consumer already closed */ }
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
cancel(reason) {
|
|
60
|
+
reader.cancel(reason).catch(() => undefined);
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
return new Response(body, {
|
|
64
|
+
status: response.status,
|
|
65
|
+
statusText: response.statusText,
|
|
66
|
+
headers: response.headers,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function failedTerminalResponse(
|
|
71
|
+
response: Response,
|
|
72
|
+
terminalPayload: Record<string, unknown>,
|
|
73
|
+
logCtx: RequestLogContext,
|
|
74
|
+
): Response {
|
|
75
|
+
const nested = terminalPayload.response;
|
|
76
|
+
const terminalResponse = nested && typeof nested === "object" && !Array.isArray(nested)
|
|
77
|
+
? nested as Record<string, unknown>
|
|
78
|
+
: {};
|
|
79
|
+
const nestedError = terminalResponse.error;
|
|
80
|
+
const error = nestedError && typeof nestedError === "object" && !Array.isArray(nestedError)
|
|
81
|
+
? nestedError as Record<string, unknown>
|
|
82
|
+
: {
|
|
83
|
+
type: "upstream_error",
|
|
84
|
+
code: "upstream_server_error",
|
|
85
|
+
message: logCtx.upstreamError ?? "Provider stream failed before producing output",
|
|
86
|
+
};
|
|
87
|
+
const headers = new Headers(response.headers);
|
|
88
|
+
headers.set("content-type", "application/json");
|
|
89
|
+
headers.delete("content-length");
|
|
90
|
+
headers.delete("content-encoding");
|
|
91
|
+
const usage = terminalResponse.usage;
|
|
92
|
+
return new Response(JSON.stringify({
|
|
93
|
+
error,
|
|
94
|
+
// The combo classifier needs only the error and optional usage. Do not carry
|
|
95
|
+
// response ids, provider metadata, or future terminal fields into the client
|
|
96
|
+
// error envelope merely because they shared the terminal snapshot.
|
|
97
|
+
response: {
|
|
98
|
+
error,
|
|
99
|
+
...(usage && typeof usage === "object" && !Array.isArray(usage) ? { usage } : {}),
|
|
100
|
+
},
|
|
101
|
+
}), {
|
|
102
|
+
status: logCtx.terminalHttpStatus ?? 502,
|
|
103
|
+
headers,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export type ComboStreamPreflightResult =
|
|
108
|
+
| { kind: "accepted"; response: Response }
|
|
109
|
+
| { kind: "failed"; response: Response };
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Buffer a combo child's downstream SSE only until the request becomes unsafe to
|
|
113
|
+
* replay or reaches a terminal. This owns exactly one body reader. The aggregate
|
|
114
|
+
* buffer is capped by bytes and retained chunks; hitting either cap commits the
|
|
115
|
+
* current target instead of growing memory or guessing that replay is safe.
|
|
116
|
+
*/
|
|
117
|
+
export async function preflightComboStreamResponse(
|
|
118
|
+
response: Response,
|
|
119
|
+
logCtx: RequestLogContext,
|
|
120
|
+
): Promise<ComboStreamPreflightResult> {
|
|
121
|
+
const contentType = response.headers.get("content-type")?.toLowerCase() ?? "";
|
|
122
|
+
if (!response.ok || !response.body || !contentType.includes("text/event-stream")) {
|
|
123
|
+
return { kind: "accepted", response };
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const reader = response.body.getReader();
|
|
127
|
+
const buffered: Uint8Array[] = [];
|
|
128
|
+
let bufferedBytes = 0;
|
|
129
|
+
let outputCommitted = false;
|
|
130
|
+
let terminalStatus: ResponsesTerminalStatus | undefined;
|
|
131
|
+
let failedPayload: Record<string, unknown> | undefined;
|
|
132
|
+
const inspector = createSseInspector({
|
|
133
|
+
logCtx,
|
|
134
|
+
onParsedPayload: payload => {
|
|
135
|
+
if (comboStreamPayloadCommitsOutput(payload)) outputCommitted = true;
|
|
136
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload)) return;
|
|
137
|
+
if ((payload as { type?: unknown }).type === "response.failed") {
|
|
138
|
+
failedPayload = payload as Record<string, unknown>;
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
onTerminal: status => { terminalStatus = status; },
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
try {
|
|
145
|
+
for (;;) {
|
|
146
|
+
const next = await reader.read();
|
|
147
|
+
if (next.done) {
|
|
148
|
+
inspector.finish();
|
|
149
|
+
} else {
|
|
150
|
+
if (bufferedBytes + next.value.byteLength > COMBO_STREAM_PREFLIGHT_MAX_BYTES) {
|
|
151
|
+
// Keep the cap about memory the preflight allocates. The upstream chunk already exists;
|
|
152
|
+
// copying it before committing would transiently exceed the boundary for no
|
|
153
|
+
// replay benefit. Preserve it unsliced behind the already-bounded prefix.
|
|
154
|
+
return {
|
|
155
|
+
kind: "accepted",
|
|
156
|
+
response: replayBufferedResponse(response, reader, [...buffered, next.value]),
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
const retained = next.value.slice();
|
|
160
|
+
buffered.push(retained);
|
|
161
|
+
bufferedBytes += retained.byteLength;
|
|
162
|
+
inspector.feed(retained);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (terminalStatus === "failed" && !outputCommitted && failedPayload) {
|
|
166
|
+
await reader.cancel("retrying zero-output combo stream failure").catch(() => undefined);
|
|
167
|
+
return { kind: "failed", response: failedTerminalResponse(response, failedPayload, logCtx) };
|
|
168
|
+
}
|
|
169
|
+
if (next.done || terminalStatus !== undefined || outputCommitted
|
|
170
|
+
|| bufferedBytes >= COMBO_STREAM_PREFLIGHT_MAX_BYTES
|
|
171
|
+
|| buffered.length >= COMBO_STREAM_PREFLIGHT_MAX_CHUNKS) {
|
|
172
|
+
return { kind: "accepted", response: replayBufferedResponse(response, reader, buffered) };
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
} finally {
|
|
176
|
+
inspector.dispose();
|
|
177
|
+
}
|
|
178
|
+
}
|
|
@@ -42,17 +42,9 @@ import { describeImagesInPlace, planVisionSidecar, shouldResolveOpenAiVisionSide
|
|
|
42
42
|
import { createAdapterEventQueue, preflightAdapterEvents } from "../../adapters/run-turn-queue";
|
|
43
43
|
import {
|
|
44
44
|
applyCodexAuthContextToProvider,
|
|
45
|
-
CodexAccountCooldownError,
|
|
46
|
-
codexMainProfileDrainingResponse,
|
|
47
|
-
cooldownErrorResponse,
|
|
48
|
-
CodexAuthContextError,
|
|
49
|
-
CodexDirectAuthenticationError,
|
|
50
45
|
CodexMainProfileDrainingError,
|
|
51
|
-
CodexPoolAuthenticationError,
|
|
52
|
-
CodexThreadAffinityExpiredError,
|
|
53
46
|
headersForCodexAuthContext,
|
|
54
47
|
materializeCodexUpstreamAuth,
|
|
55
|
-
CodexMainSubstitutionUnavailableError,
|
|
56
48
|
isCodexAuthContextUsable,
|
|
57
49
|
resolveCodexAuthContext,
|
|
58
50
|
codexProbeLeaseId,
|
|
@@ -134,6 +126,7 @@ import {
|
|
|
134
126
|
usesCodexForwardPoolAuth,
|
|
135
127
|
} from "./core";
|
|
136
128
|
import { fetchWithHeaderTimeout, providerFetch, safeHostLabel, safeOriginLabel } from "./fetch-helpers";
|
|
129
|
+
import { mapCodexAuthContextErrorToResponse } from "./codex-auth-error";
|
|
137
130
|
|
|
138
131
|
export const COMPACT_RESPONSE_MAX_BYTES = 32 * 1024 * 1024;
|
|
139
132
|
|
|
@@ -395,19 +388,11 @@ export async function handleResponsesCompact(
|
|
|
395
388
|
}
|
|
396
389
|
}
|
|
397
390
|
} catch (err) {
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
if (
|
|
403
|
-
return formatErrorResponse(409, "invalid_request_error", "Codex thread account affinity expired; start a new session");
|
|
404
|
-
}
|
|
405
|
-
if (err instanceof CodexAuthContextError) {
|
|
406
|
-
return formatErrorResponse(401, "authentication_error", "Selected Codex account needs reauthentication");
|
|
407
|
-
}
|
|
408
|
-
if (err instanceof CodexPoolAuthenticationError || err instanceof CodexDirectAuthenticationError) {
|
|
409
|
-
return formatErrorResponse(401, "authentication_error", err.message);
|
|
410
|
-
}
|
|
391
|
+
const response = mapCodexAuthContextErrorToResponse(err, {
|
|
392
|
+
accountSelector: route.codexAccountNamespace,
|
|
393
|
+
now: Date.now(),
|
|
394
|
+
});
|
|
395
|
+
if (response) return response;
|
|
411
396
|
throw err;
|
|
412
397
|
}
|
|
413
398
|
const base = isCanonicalOpenAiForwardProvider(compactProvider)
|