@bitkyc08/opencodex 2.48.0 → 2.50.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/AGENTS_INSTALL.md +9 -1
- package/README.md +11 -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-C39tnjXO.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/claude/inbound.ts +17 -5
- package/src/cli/account-api.ts +18 -3
- package/src/cli/account-auth.ts +8 -1
- package/src/cli/account-extended.ts +2 -1
- package/src/cli/account.ts +1 -0
- package/src/cli/capabilities.ts +15 -1
- 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 +29 -2
- package/src/cli/models-runtime.ts +8 -3
- package/src/cli/observe.ts +13 -3
- 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/clients/config-export/zcode.ts +24 -0
- package/src/codex/account-lifecycle.ts +35 -2
- package/src/codex/account-runtime-state.ts +6 -1
- package/src/codex/account-store.ts +72 -9
- package/src/codex/account-usability.ts +3 -2
- package/src/codex/auth-api.ts +113 -26
- package/src/codex/auth-collision.ts +12 -2
- package/src/codex/auth-context.ts +96 -7
- package/src/codex/catalog/parsing.ts +23 -0
- package/src/codex/catalog/provider-fetch.ts +144 -11
- package/src/codex/catalog/sync.ts +14 -0
- package/src/codex/inject.ts +128 -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-auto-refresh.ts +6 -1
- package/src/codex/quota.ts +71 -15
- package/src/codex/reserve-availability.ts +21 -5
- package/src/codex/runtime.ts +45 -1
- package/src/codex/sync.ts +5 -0
- package/src/combos/index.ts +2 -0
- package/src/combos/resolve.ts +52 -0
- package/src/config.ts +59 -0
- package/src/generated/compatibility-version.json +178 -114
- 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/errors.ts +8 -0
- package/src/lib/privacy.ts +25 -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/health.ts +47 -12
- package/src/oauth/index.ts +46 -8
- package/src/oauth/token-guardian.ts +32 -6
- 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/google-ai-studio-model-discovery.ts +74 -0
- package/src/providers/openai-sidecar.ts +35 -11
- package/src/providers/opencode-zen-rate-limit.ts +75 -0
- package/src/providers/qoder-models.ts +25 -0
- package/src/providers/quota.ts +15 -0
- package/src/providers/registry.ts +140 -1
- package/src/responses/compaction.ts +4 -0
- package/src/responses/task-input.ts +21 -1
- package/src/router.ts +1 -1
- package/src/server/auth-cors.ts +6 -0
- package/src/server/chat-completions.ts +30 -13
- package/src/server/chat-native.ts +10 -1
- package/src/server/claude-messages.ts +17 -7
- package/src/server/images.ts +3 -2
- package/src/server/index.ts +25 -2
- 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 +5 -1
- package/src/server/management/model-rows.ts +16 -1
- package/src/server/management/native-integration-routes.ts +2 -1
- package/src/server/management/oauth-account-routes.ts +6 -2
- package/src/server/management/provider-routes.ts +33 -2
- package/src/server/management/request-history-routes.ts +4 -2
- package/src/server/management/route-registry.ts +5 -4
- package/src/server/management/shared.ts +66 -3
- package/src/server/management-api.ts +15 -1
- package/src/server/port-reclaim.ts +11 -26
- package/src/server/request-decompress.ts +91 -3
- package/src/server/request-log.ts +16 -0
- package/src/server/responses/codex-ws-wire.ts +1 -1
- package/src/server/responses/collaboration.ts +4 -9
- package/src/server/responses/compact.ts +8 -2
- package/src/server/responses/context-overflow.ts +11 -0
- package/src/server/responses/core.ts +285 -57
- package/src/server/responses/fetch-helpers.ts +18 -7
- package/src/server/responses/policy-fallback.ts +18 -2
- package/src/server/search.ts +2 -2
- package/src/service.ts +128 -9
- package/src/storage/cleanup.ts +77 -45
- package/src/types/accounts.ts +18 -0
- package/src/types/config.ts +43 -1
- package/src/types/provider.ts +56 -0
- package/src/types.ts +4 -0
- 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/ollama-executor.ts +127 -0
- package/src/web-search/passthrough-bridge.ts +761 -0
- package/src/web-search/progress-stream.ts +4 -0
- package/gui/dist/assets/index-B5r7LNHN.js +0 -115
- package/gui/dist/assets/index-D5SiRo8X.css +0 -1
|
@@ -2,11 +2,9 @@
|
|
|
2
2
|
* Reclaim a listen port after stop/update so restart can stay on the configured
|
|
3
3
|
* port instead of hopping to an ephemeral one (Windows CLOSE_WAIT / leftover ocx).
|
|
4
4
|
*
|
|
5
|
-
* Killing is never the default.
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* or enables `killAllOcxOnPort` for revalidated ocx listeners. Unknown foreign
|
|
9
|
-
* (non-ocx, non-allowlisted) processes are never killed.
|
|
5
|
+
* Killing is never the default. It requires `killOcxHolders`, an allowed PID or
|
|
6
|
+
* `killAllOcxOnPort`, and successful ocx verification. A historical PID allowlist
|
|
7
|
+
* never overrides a rejected verifier result; rejected live holders stay protected.
|
|
10
8
|
*/
|
|
11
9
|
import { execFileSync } from "node:child_process";
|
|
12
10
|
import { verifyPidIdentity } from "../config/process-state";
|
|
@@ -29,6 +27,9 @@ export type ReclaimListenPortOptions = WaitForPortOptions & {
|
|
|
29
27
|
/**
|
|
30
28
|
* Explicit PIDs the caller just stopped / hard-killed. An omitted or empty
|
|
31
29
|
* list means no process may be killed — unless {@link killAllOcxOnPort} is set.
|
|
30
|
+
* The allowlist only narrows kill candidates: every candidate, allowlisted or
|
|
31
|
+
* not, still requires verifier acceptance (`verifyOcxFn(pid) === pid`) on each
|
|
32
|
+
* scan, and a rejected live holder is never killed or TCP-row dropped.
|
|
32
33
|
*/
|
|
33
34
|
onlyKillPids?: number[];
|
|
34
35
|
/**
|
|
@@ -36,8 +37,7 @@ export type ReclaimListenPortOptions = WaitForPortOptions & {
|
|
|
36
37
|
* killed (re-checked each scan). Used by post-update restart so a Windows
|
|
37
38
|
* service wrapper that respawns a *new* bun PID mid-reclaim cannot stay
|
|
38
39
|
* protected just because it was absent from the pre-wait allowlist snapshot.
|
|
39
|
-
*
|
|
40
|
-
* and revalidated ocx listeners.
|
|
40
|
+
* Every candidate still requires ocx verifier acceptance before termination.
|
|
41
41
|
*/
|
|
42
42
|
killAllOcxOnPort?: boolean;
|
|
43
43
|
/**
|
|
@@ -174,8 +174,8 @@ export function listListenPids(port: number): number[] {
|
|
|
174
174
|
* Never kills a process unless `killOcxHolders === true` and either
|
|
175
175
|
* `onlyKillPids` is a non-empty allowlist or `killAllOcxOnPort` is set — then
|
|
176
176
|
* revalidates immediately before each kill.
|
|
177
|
-
* Never
|
|
178
|
-
* protected ocx listener owns the port, or when the
|
|
177
|
+
* Never overrides a rejected ocx verifier result. Never drops TCP rows while a
|
|
178
|
+
* rejected live or protected ocx listener owns the port, or when the scan failed.
|
|
179
179
|
*/
|
|
180
180
|
export async function reclaimListenPort(
|
|
181
181
|
port: number,
|
|
@@ -231,24 +231,9 @@ export async function reclaimListenPort(
|
|
|
231
231
|
}
|
|
232
232
|
const isOcx = verifyOcxFn(pid) === pid;
|
|
233
233
|
const allowlisted = allowedKillPids.has(pid);
|
|
234
|
-
// Pre-update PIDs can fail verify while still LISTENing (dead owner still
|
|
235
|
-
// listed, or cmdline probe raced). Allowlisted teardown PIDs may be killed;
|
|
236
|
-
// unknown foreign claimants must remain fail-closed.
|
|
237
234
|
if (!isOcx) {
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
try {
|
|
241
|
-
killFn(pid);
|
|
242
|
-
killed.add(pid);
|
|
243
|
-
} catch {
|
|
244
|
-
// Kill failed: never SetTcpEntry while the process may still own the port.
|
|
245
|
-
protectedOcxListener = true;
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
if (!isAliveFn(pid)) killed.delete(pid);
|
|
249
|
-
else protectedOcxListener = true;
|
|
250
|
-
continue;
|
|
251
|
-
}
|
|
235
|
+
// A saved PID narrows eligible candidates; it cannot override verifier rejection.
|
|
236
|
+
// Dead ghost owners have already been skipped by the liveness check above.
|
|
252
237
|
foreignLive = true;
|
|
253
238
|
continue;
|
|
254
239
|
}
|
|
@@ -21,6 +21,58 @@ import type { TranslatorBudget } from "../lib/translator-budget";
|
|
|
21
21
|
*/
|
|
22
22
|
export const MAX_DECOMPRESSED_BODY_BYTES = 256 * 1024 * 1024;
|
|
23
23
|
|
|
24
|
+
/**
|
|
25
|
+
* Hard ceiling on the opt-in `maxInboundBodyBytes` (#3573).
|
|
26
|
+
*
|
|
27
|
+
* The opt-in exists because a 922k-token session serializes past the 256 MiB default, and the
|
|
28
|
+
* request that crosses it is the compaction request itself — so the session can no longer
|
|
29
|
+
* shrink and is stuck. An UNBOUNDED inbound cap is not an acceptable answer: this admission
|
|
30
|
+
* limit is the only thing standing between one request and the process heap, and
|
|
31
|
+
* `readBoundedJsonRequestBody` materializes the body several times over (retained wire bytes,
|
|
32
|
+
* decoded bytes, the decoded string, the re-encoded measurement copies, and the parsed object
|
|
33
|
+
* graph), so peak RSS is a MULTIPLE of whatever is admitted here. 512 MiB is the largest value
|
|
34
|
+
* that keeps that multiple survivable on an ordinary machine, and it is what #3573 asked for.
|
|
35
|
+
*/
|
|
36
|
+
export const MAX_CONFIGURABLE_INBOUND_BODY_BYTES = 512 * 1024 * 1024;
|
|
37
|
+
|
|
38
|
+
/** Floor for the opt-in. Below this an ordinary multi-image turn cannot be admitted at all. */
|
|
39
|
+
export const MIN_CONFIGURABLE_INBOUND_BODY_BYTES = 1024 * 1024;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Resolve the configured inbound admission limit, clamped to the supported range.
|
|
43
|
+
*
|
|
44
|
+
* Pure and total on purpose: the schema in `src/config.ts` degrades an invalid hand edit to
|
|
45
|
+
* `undefined` rather than failing the parse, so the schema cannot be the place the ceiling is
|
|
46
|
+
* enforced. Every caller resolves through here, which makes this the single auditable bound
|
|
47
|
+
* regardless of how the config object was produced.
|
|
48
|
+
*
|
|
49
|
+
* Omitted, zero, or non-finite = the 256 MiB default, so an unconfigured proxy admits exactly
|
|
50
|
+
* what it admits today.
|
|
51
|
+
*/
|
|
52
|
+
export function resolveInboundBodyLimitBytes(configured: number | undefined): number {
|
|
53
|
+
if (configured === undefined || !Number.isFinite(configured) || configured <= 0) {
|
|
54
|
+
return MAX_DECOMPRESSED_BODY_BYTES;
|
|
55
|
+
}
|
|
56
|
+
return Math.min(
|
|
57
|
+
Math.max(Math.floor(configured), MIN_CONFIGURABLE_INBOUND_BODY_BYTES),
|
|
58
|
+
MAX_CONFIGURABLE_INBOUND_BODY_BYTES,
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Render a byte count, or nothing at all. `DecompressedBodyTooLargeError` accepts non-finite
|
|
64
|
+
* and untyped values from legacy callers and deliberately keeps them out of its own message;
|
|
65
|
+
* the client-facing message inherits that rule rather than printing `NaN MB`.
|
|
66
|
+
*/
|
|
67
|
+
function megabytes(bytes: number): string | null {
|
|
68
|
+
return Number.isFinite(bytes) && bytes >= 0 && bytes <= Number.MAX_SAFE_INTEGER
|
|
69
|
+
? (bytes / (1024 * 1024)).toFixed(1)
|
|
70
|
+
: null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const INBOUND_CEILING_MB = (MAX_CONFIGURABLE_INBOUND_BODY_BYTES / (1024 * 1024)).toFixed(1);
|
|
74
|
+
|
|
75
|
+
|
|
24
76
|
export class UnsupportedContentEncodingError extends Error {
|
|
25
77
|
constructor(readonly encoding: string) {
|
|
26
78
|
super(`Unsupported content-encoding: ${encoding}`);
|
|
@@ -54,6 +106,33 @@ export class DecompressedBodyTooLargeError extends Error {
|
|
|
54
106
|
}
|
|
55
107
|
}
|
|
56
108
|
|
|
109
|
+
/**
|
|
110
|
+
* Name OpenCodex as the refuser, and name the lever.
|
|
111
|
+
*
|
|
112
|
+
* #4112 gave the UPSTREAM context refusal on `/v1/responses` its own HTTP 413 with
|
|
113
|
+
* `context_length_exceeded`. That makes the two 413s on this surface look alike to a client
|
|
114
|
+
* while having opposite remedies: the upstream one means the provider will not take the turn,
|
|
115
|
+
* this one means the proxy never read it and a config key would have let it through. The
|
|
116
|
+
* wording deliberately avoids "context window"/"context length", which `classifyError` treats
|
|
117
|
+
* as evidence of an upstream context verdict.
|
|
118
|
+
*/
|
|
119
|
+
export function describeInboundBodyRefusal(error: DecompressedBodyTooLargeError): string {
|
|
120
|
+
// A lower-bound measurement stopped counting at the cap; reporting it as exact would be a lie.
|
|
121
|
+
const approximate = error.measurement === "declared_wire" || error.measurement === "decoded_exact"
|
|
122
|
+
? "" : "at least ";
|
|
123
|
+
const observed = megabytes(error.bytes);
|
|
124
|
+
const limit = megabytes(error.limit);
|
|
125
|
+
const sizes = limit === null
|
|
126
|
+
? "the body is above the inbound admission limit"
|
|
127
|
+
: observed === null
|
|
128
|
+
? `the body is above the ${limit} MB inbound admission limit`
|
|
129
|
+
: `the body is ${approximate}${observed} MB, above the ${limit} MB inbound admission limit`;
|
|
130
|
+
return `OpenCodex refused this request before reading it: ${sizes}. `
|
|
131
|
+
+ "This is a local proxy limit, not a provider refusal. Raise \"maxInboundBodyBytes\" in "
|
|
132
|
+
+ `config.json (ceiling ${INBOUND_CEILING_MB} MB) and restart the proxy, or compact the `
|
|
133
|
+
+ "conversation earlier.";
|
|
134
|
+
}
|
|
135
|
+
|
|
57
136
|
function assertBodySizeWithinLimit(
|
|
58
137
|
body: Uint8Array,
|
|
59
138
|
maxBytes: number,
|
|
@@ -259,7 +338,16 @@ export async function readBoundedJsonRequestBody(
|
|
|
259
338
|
}
|
|
260
339
|
}
|
|
261
340
|
|
|
262
|
-
/**
|
|
263
|
-
|
|
264
|
-
|
|
341
|
+
/**
|
|
342
|
+
* Parse a JSON data-plane body using the shared admission cap.
|
|
343
|
+
*
|
|
344
|
+
* `maxBytes` is the resolved per-deployment limit from `resolveInboundBodyLimitBytes()`;
|
|
345
|
+
* omitting it keeps the 256 MiB default for callers with no config in scope.
|
|
346
|
+
*/
|
|
347
|
+
export function readJsonRequestBody(
|
|
348
|
+
req: Request,
|
|
349
|
+
budget?: TranslatorBudget,
|
|
350
|
+
maxBytes: number = MAX_DECOMPRESSED_BODY_BYTES,
|
|
351
|
+
): Promise<unknown> {
|
|
352
|
+
return readBoundedJsonRequestBody(req, maxBytes, budget);
|
|
265
353
|
}
|
|
@@ -22,6 +22,8 @@ import {
|
|
|
22
22
|
appendUsageEntry,
|
|
23
23
|
isKnownAdmissionKind,
|
|
24
24
|
isKnownInboundProtocol,
|
|
25
|
+
isKnownTerminalSource,
|
|
26
|
+
isKnownTransportPhase,
|
|
25
27
|
isKnownUsageSurface,
|
|
26
28
|
isCodexUsageAccountLogLabel,
|
|
27
29
|
isValidReasoningWireValue,
|
|
@@ -320,6 +322,8 @@ export function requestLogEntryFromPersistedUsage(entry: PersistedUsageEntry): R
|
|
|
320
322
|
...(entry.usage ? { usage: entry.usage } : {}),
|
|
321
323
|
...(entry.totalTokens !== undefined ? { totalTokens: entry.totalTokens } : {}),
|
|
322
324
|
...(entry.attempts !== undefined ? { attempts: entry.attempts } : {}),
|
|
325
|
+
...(isKnownTransportPhase(entry.transportPhase) ? { transportPhase: entry.transportPhase } : {}),
|
|
326
|
+
...(isKnownTerminalSource(entry.terminalSource) ? { terminalSource: entry.terminalSource } : {}),
|
|
323
327
|
...(routeDecision ? { routeDecision } : {}),
|
|
324
328
|
...(claudeCompatibility ? { claudeCompatibility } : {}),
|
|
325
329
|
};
|
|
@@ -441,6 +445,8 @@ export function addRequestLog(entry: RequestLogEntry) {
|
|
|
441
445
|
...(entry.usage ? { usage: entry.usage } : {}),
|
|
442
446
|
...(entry.totalTokens !== undefined ? { totalTokens: entry.totalTokens } : {}),
|
|
443
447
|
...(entry.attempts !== undefined ? { attempts: entry.attempts } : {}),
|
|
448
|
+
...(isKnownTransportPhase(entry.transportPhase) ? { transportPhase: entry.transportPhase } : {}),
|
|
449
|
+
...(isKnownTerminalSource(entry.terminalSource) ? { terminalSource: entry.terminalSource } : {}),
|
|
444
450
|
...failureDiagnostics,
|
|
445
451
|
...(entry.routeDecision ? { routeDecision: entry.routeDecision } : {}),
|
|
446
452
|
...(entry.claudeCompatibility ? { claudeCompatibility: entry.claudeCompatibility } : {}),
|
|
@@ -1113,6 +1119,16 @@ export function filterRequestLogs(logs: RequestLogEntry[], params: URLSearchPara
|
|
|
1113
1119
|
filtered = filtered.filter(entry => entry.model === model
|
|
1114
1120
|
|| entry.attempts?.some(attempt => attempt.model === model));
|
|
1115
1121
|
}
|
|
1122
|
+
// #4057: "which account served this request" is the first question asked when one provider
|
|
1123
|
+
// holds several accounts, and until now the only way to answer it was to grep usage.jsonl by
|
|
1124
|
+
// hand. Attempts are matched for the same reason `provider` and `model` match them: when a
|
|
1125
|
+
// request failed over between pool accounts, a search for the account that finally served it
|
|
1126
|
+
// has to find that request, not only the account that first refused it.
|
|
1127
|
+
const account = params.get("account")?.trim();
|
|
1128
|
+
if (account) {
|
|
1129
|
+
filtered = filtered.filter(entry => entry.accountLogLabel === account
|
|
1130
|
+
|| entry.attempts?.some(attempt => attempt.accountLogLabel === account));
|
|
1131
|
+
}
|
|
1116
1132
|
const status = params.get("status")?.trim().toLowerCase();
|
|
1117
1133
|
if (status) {
|
|
1118
1134
|
filtered = /^[1-5]xx$/.test(status)
|
|
@@ -2,7 +2,7 @@ import { MAX_CLIENT_SSE_FRAME_BYTES } from "../sse-frame-buffer";
|
|
|
2
2
|
// If the 101 never arrives (network black hole), give SSE a chance well before
|
|
3
3
|
// the caller's connect timeout (default 200s) would fire.
|
|
4
4
|
export const UPGRADE_DEADLINE_MS = 10_000;
|
|
5
|
-
export const CODEX_WS_RESPONSE_PRELUDE_TIMEOUT_MS =
|
|
5
|
+
export const CODEX_WS_RESPONSE_PRELUDE_TIMEOUT_MS = 90_000;
|
|
6
6
|
// Keep the push-based WS transport inside the same memory envelope as the
|
|
7
7
|
// bounded SSE relays that consume this response. Unlike fetch response bodies,
|
|
8
8
|
// a WebSocket cannot be paused when a ReadableStream applies backpressure, so
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
} from "../../config";
|
|
9
9
|
import { parseRequest } from "../../responses/parser";
|
|
10
10
|
import { externalTaskInputContent } from "../../responses/task-input";
|
|
11
|
+
import { MULTI_AGENT_MODE_HINT_RECOMMENDATION } from "../../codex/multi-agent-mode-policy";
|
|
11
12
|
import { buildCompactV1Output, COMPACT_PROMPT, decodeCompactionSummary, extractCompactUserMessages } from "../../responses/compaction";
|
|
12
13
|
import { FORWARD_HEADERS, sanitizeReasoningInputContent } from "../../adapters/openai-responses";
|
|
13
14
|
import { expandPreviousResponseInput, previousResponseProviderState, rememberResponseState } from "../../responses/state";
|
|
@@ -233,13 +234,7 @@ export function buildToolBridgeMaps(parsed: OcxParsedRequest, budget?: Translato
|
|
|
233
234
|
|
|
234
235
|
|
|
235
236
|
|
|
236
|
-
export const PROACTIVE_MULTI_AGENT_MODE_TEXT =
|
|
237
|
-
"Proactive multi-agent delegation is active.",
|
|
238
|
-
"Any earlier instruction requiring an explicit user request before spawning sub-agents no longer applies.",
|
|
239
|
-
"Delegate independent sub-tasks to sub-agents whenever parallel work would materially improve speed or quality — do not serialize work that can run concurrently.",
|
|
240
|
-
"Each sub-agent runs in its own context and can use all available tools; prefer spawning specialists over doing everything yourself.",
|
|
241
|
-
"This mode remains active until a later multi-agent mode developer message changes it.",
|
|
242
|
-
].join(" ");
|
|
237
|
+
export const PROACTIVE_MULTI_AGENT_MODE_TEXT = MULTI_AGENT_MODE_HINT_RECOMMENDATION.text;
|
|
243
238
|
|
|
244
239
|
const OPENCODEX_SUBAGENT_GUIDANCE_OPEN_TAG = "<opencodex_subagent_guidance>";
|
|
245
240
|
const OPENCODEX_SUBAGENT_GUIDANCE_CLOSE_TAG = "</opencodex_subagent_guidance>";
|
|
@@ -491,8 +486,8 @@ export async function multiAgentGuidanceText(
|
|
|
491
486
|
}
|
|
492
487
|
|
|
493
488
|
const effort = parsed.options.reasoning;
|
|
494
|
-
// v1
|
|
495
|
-
//
|
|
489
|
+
// v1 changes only the delegation trigger at the top tier; other rules still apply.
|
|
490
|
+
// Ultra arrives as max on the wire. No designation/roster payload here.
|
|
496
491
|
if (effort !== "max" && effort !== "ultra") return null;
|
|
497
492
|
return `<multi_agent_mode>${PROACTIVE_MULTI_AGENT_MODE_TEXT}</multi_agent_mode>`;
|
|
498
493
|
}
|
|
@@ -104,7 +104,12 @@ import { fastPolicyForModel } from "../../providers/service-tier";
|
|
|
104
104
|
import { parseFastOnlyRowId } from "../fast-row";
|
|
105
105
|
import { applyOpenAiVirtualModel, resolveOpenAiCompactModel } from "../../providers/openai-virtual-models";
|
|
106
106
|
import { isUsageDebugEnabled } from "../../usage/debug";
|
|
107
|
-
import {
|
|
107
|
+
import {
|
|
108
|
+
readJsonRequestBody,
|
|
109
|
+
resolveInboundBodyLimitBytes,
|
|
110
|
+
DecompressedBodyTooLargeError,
|
|
111
|
+
UnsupportedContentEncodingError,
|
|
112
|
+
} from "../request-decompress";
|
|
108
113
|
import { resolveAdapter, resolveWireProtocolOverride } from "../adapter-resolve";
|
|
109
114
|
import { hasKeyPoolFailover, rotateProviderTransportOn429 } from "../../providers/key-failover";
|
|
110
115
|
import { shouldAttemptImageTierRetry } from "../image-retry";
|
|
@@ -522,7 +527,7 @@ export async function handleResponsesCompact(
|
|
|
522
527
|
): Promise<Response> {
|
|
523
528
|
let body: unknown;
|
|
524
529
|
try {
|
|
525
|
-
body = await readJsonRequestBody(req);
|
|
530
|
+
body = await readJsonRequestBody(req, undefined, resolveInboundBodyLimitBytes(config.maxInboundBodyBytes));
|
|
526
531
|
} catch (err) {
|
|
527
532
|
return decodeRequestErrorResponse(err, "responses-compact");
|
|
528
533
|
}
|
|
@@ -1015,6 +1020,7 @@ export async function handleResponsesCompact(
|
|
|
1015
1020
|
upstream.headers,
|
|
1016
1021
|
authCtx.writerGeneration,
|
|
1017
1022
|
authCtx.kind === "main-pool" ? authCtx.mainQuotaWriter : undefined,
|
|
1023
|
+
{ modelId: route.modelId },
|
|
1018
1024
|
);
|
|
1019
1025
|
}
|
|
1020
1026
|
recordCompactPoolOutcome(authCtx, upstream.status, {
|
|
@@ -5,6 +5,17 @@ import type { AdapterEvent } from "../../types";
|
|
|
5
5
|
export const PROVIDER_INPUT_TOO_LARGE_MESSAGE =
|
|
6
6
|
"The provider rejected this turn because its input exceeds the provider size or context limit. Reduce the current input or compact the conversation before retrying.";
|
|
7
7
|
|
|
8
|
+
/** Preserve non-streaming HTTP failure semantics without exposing an upstream body. */
|
|
9
|
+
export function jsonContextOverflowResponse(): Response {
|
|
10
|
+
return Response.json({
|
|
11
|
+
error: {
|
|
12
|
+
message: PROVIDER_INPUT_TOO_LARGE_MESSAGE,
|
|
13
|
+
type: "invalid_request_error",
|
|
14
|
+
code: "context_length_exceeded",
|
|
15
|
+
},
|
|
16
|
+
}, { status: 413, headers: { "Cache-Control": "no-store" } });
|
|
17
|
+
}
|
|
18
|
+
|
|
8
19
|
async function* contextOverflowEvents(): AsyncGenerator<AdapterEvent> {
|
|
9
20
|
yield {
|
|
10
21
|
type: "error",
|