codex-copilot-dx 0.6.5 → 0.6.6

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 CHANGED
@@ -120,8 +120,8 @@ ccdx auth login claude --reauth --github-login <personal-github-login>
120
120
  ccdx pms setup
121
121
  ```
122
122
 
123
- Inspect the installed version, exact patch integrity, isolated Claude profile,
124
- and relay without changing files:
123
+ Inspect the installed version, exact patch manifest plus executable/signing
124
+ integrity, isolated Claude profile, and live relay routing without changing files:
125
125
 
126
126
  ```bash
127
127
  ccdx pms status
package/bin/cli.mjs CHANGED
@@ -316,6 +316,7 @@ try {
316
316
  codexModelRegistry: profileModels.codexRegistry,
317
317
  claudeModelRegistry: profileModels.claudeRegistry,
318
318
  claudeMode: profileRuntime.claudeMode,
319
+ isClaudeProfileCurrent: profileRuntime.isClaudeProfileCurrent,
319
320
  showRequestId: CLI.showRequestId,
320
321
  upstreamTimeoutMs: RUNTIME.upstreamTimeoutMs,
321
322
  streamHandshakeTimeoutMs: RUNTIME.streamHandshakeTimeoutMs,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codex-copilot-dx",
3
- "version": "0.6.5",
3
+ "version": "0.6.6",
4
4
  "description": "Use Codex Desktop, Claude Code, and Claude App with GitHub Copilot",
5
5
  "author": "Dale Xiao",
6
6
  "type": "module",
@@ -23,6 +23,14 @@ function runProbe(source) {
23
23
  return JSON.parse(result.stdout.trim());
24
24
  }
25
25
 
26
+ function median(values) {
27
+ const ordered = [...values].sort((left, right) => left - right);
28
+ const middle = Math.floor(ordered.length / 2);
29
+ return ordered.length % 2 === 0
30
+ ? (ordered[middle - 1] + ordered[middle]) / 2
31
+ : ordered[middle];
32
+ }
33
+
26
34
  function deterministicPixels(byteLength, initialSeed) {
27
35
  const pixels = Buffer.alloc(byteLength);
28
36
  let seed = initialSeed;
@@ -154,7 +162,7 @@ function largePayloadProbe(targetMiB, concurrency) {
154
162
  }
155
163
 
156
164
  function largeTokenCountProbe(mode) {
157
- return runProbe(`
165
+ const samples = Array.from({ length: 3 }, () => runProbe(`
158
166
  const text = "a ".repeat(2 * 1024 * 1024);
159
167
  const started = performance.now();
160
168
  let tokens;
@@ -174,6 +182,51 @@ function largeTokenCountProbe(mode) {
174
182
  rss_mib: +(memory.rss / 1048576).toFixed(1),
175
183
  heap_used_mib: +(memory.heapUsed / 1048576).toFixed(1),
176
184
  }));
185
+ `));
186
+ return {
187
+ input_mib: samples[0].input_mib,
188
+ tokens: samples[0].tokens,
189
+ elapsed_ms: +median(samples.map((sample) => sample.elapsed_ms)).toFixed(1),
190
+ rss_mib: +median(samples.map((sample) => sample.rss_mib)).toFixed(1),
191
+ heap_used_mib: +median(samples.map((sample) => sample.heap_used_mib)).toFixed(1),
192
+ samples: samples.length,
193
+ };
194
+ }
195
+
196
+ function toolOutputParseCacheProbe() {
197
+ return runProbe(`
198
+ import { enforceResponsesImageLimit } from "./src/responses-image-limit.mjs";
199
+ import { responsesHistoricalImageStats } from "./src/responses-byte-budget.mjs";
200
+ import { prepareResponsesChatPayload } from "./src/responses-chat-payload.mjs";
201
+
202
+ const item = {
203
+ type: "function_call_output",
204
+ call_id: "call_benchmark",
205
+ output: JSON.stringify([{ type: "input_text", text: "x".repeat(8 * 1024 * 1024) }]),
206
+ };
207
+ const source = item.output;
208
+ const originalParse = JSON.parse;
209
+ let parseCalls = 0;
210
+ JSON.parse = function countedParse(value, ...args) {
211
+ if (value === source) parseCalls += 1;
212
+ return originalParse.call(this, value, ...args);
213
+ };
214
+ const started = performance.now();
215
+ try {
216
+ enforceResponsesImageLimit([item]);
217
+ responsesHistoricalImageStats([item], 1);
218
+ await prepareResponsesChatPayload({
219
+ body: { model: "gpt-4o", input: [item] },
220
+ currentInputStart: 1,
221
+ }, { payloadOptions: { maxBytes: 30 * 1024 * 1024 } });
222
+ } finally {
223
+ JSON.parse = originalParse;
224
+ }
225
+ process.stdout.write(JSON.stringify({
226
+ output_mib: +(Buffer.byteLength(source) / 1048576).toFixed(1),
227
+ parse_calls: parseCalls,
228
+ elapsed_ms: +(performance.now() - started).toFixed(1),
229
+ }));
177
230
  `);
178
231
  }
179
232
 
@@ -373,6 +426,7 @@ if (checkMode) {
373
426
  proxy: largeTokenCountProbe("proxy"),
374
427
  materialized_array: largeTokenCountProbe("array"),
375
428
  };
429
+ report.tool_output_parse_cache = toolOutputParseCacheProbe();
376
430
  report.large_payload_peak = [
377
431
  largePayloadProbe(5, 4),
378
432
  largePayloadProbe(30, 1),
@@ -409,6 +463,9 @@ if (checkMode) {
409
463
  if (proxy.heap_used_mib > array.heap_used_mib * 0.75) {
410
464
  failures.push(`token counter heap regression: proxy=${proxy.heap_used_mib}MiB array=${array.heap_used_mib}MiB`);
411
465
  }
466
+ if (report.tool_output_parse_cache.parse_calls !== 1) {
467
+ failures.push(`stringified tool output parsed ${report.tool_output_parse_cache.parse_calls} times instead of once`);
468
+ }
412
469
  for (const payload of report.large_payload_peak) {
413
470
  if (payload.admission_max_active_mib > payload.admission_budget_mib) {
414
471
  failures.push(`request admission exceeded its budget at ${payload.target_request_mib}MiB x${payload.concurrency}`);
package/src/adapter.mjs CHANGED
@@ -215,6 +215,7 @@ export function createAdapterHandler(options = {}) {
215
215
  codexModelRegistry,
216
216
  claudeModelRegistry,
217
217
  claudeMode,
218
+ isClaudeProfileCurrent: options.isClaudeProfileCurrent,
218
219
  })));
219
220
  return;
220
221
  }
package/src/cli-auth.mjs CHANGED
@@ -21,6 +21,7 @@ import {
21
21
  normalizeGithubIdentity,
22
22
  } from "./github-identity.mjs";
23
23
  import { saveModelCache } from "./model-cache.mjs";
24
+ import { isClaudeCopilotModel } from "./models.mjs";
24
25
  import { profileRouting } from "./profile-routing.mjs";
25
26
  import { status } from "./status.mjs";
26
27
 
@@ -145,17 +146,7 @@ async function codexIdentity({ home, fetchImpl, signal }) {
145
146
  function claudeModels(payload) {
146
147
  const models = Array.isArray(payload) ? payload : payload?.data;
147
148
  if (!Array.isArray(models)) throw new Error("Claude account model catalog contained no model list");
148
- return models.filter((model) => {
149
- const id = String(model?.id || "").trim();
150
- const vendor = String(model?.vendor || model?.owned_by || "").trim().toLowerCase();
151
- const policy = String(model?.policy?.state || "").trim().toLowerCase();
152
- const endpoints = Array.isArray(model?.supported_endpoints) ? model.supported_endpoints : [];
153
- return Boolean(id)
154
- && (id.toLowerCase().startsWith("claude-") || vendor === "anthropic")
155
- && model?.model_picker_enabled === true
156
- && (!policy || policy === "enabled")
157
- && (endpoints.includes("/chat/completions") || endpoints.includes("/v1/messages"));
158
- });
149
+ return models.filter(isClaudeCopilotModel);
159
150
  }
160
151
 
161
152
  function catalogModels(payload) {
@@ -6,6 +6,10 @@ import {
6
6
  readAuthProfileCredentials,
7
7
  } from "./auth-profile.mjs";
8
8
  import { buildHeaders, DEFAULT_API_BASE, FALLBACK_VSCODE_VERSION } from "./copilot.mjs";
9
+ import {
10
+ isClaudeCopilotCatalogEntry,
11
+ isClaudeCopilotModel,
12
+ } from "./models.mjs";
9
13
  import { status } from "./status.mjs";
10
14
 
11
15
  const DEFAULT_MODELS_TIMEOUT_MS = 10000;
@@ -37,11 +41,15 @@ export function selectableCopilotModels(payload) {
37
41
  const models = [];
38
42
  const seen = new Set();
39
43
  for (const model of data) {
40
- if (model?.model_picker_enabled !== true) continue;
41
- const policyState = safeText(model?.policy?.state).toLowerCase();
42
- if (policyState && policyState !== "enabled") continue;
43
- const capabilityType = safeText(model?.capabilities?.type).toLowerCase();
44
- if (capabilityType && capabilityType !== "chat") continue;
44
+ if (isClaudeCopilotCatalogEntry(model)) {
45
+ if (!isClaudeCopilotModel(model)) continue;
46
+ } else {
47
+ if (model?.model_picker_enabled !== true) continue;
48
+ const policyState = safeText(model?.policy?.state).toLowerCase();
49
+ if (policyState && policyState !== "enabled") continue;
50
+ const capabilityType = safeText(model?.capabilities?.type).toLowerCase();
51
+ if (capabilityType && capabilityType !== "chat") continue;
52
+ }
45
53
 
46
54
  const id = safeText(model?.id);
47
55
  const endpoints = endpointLabels(model);
@@ -163,8 +171,7 @@ export function formatLiveCopilotModels(catalog, { commandName = "ccdx" } = {})
163
171
  const profile = catalog?.profile === AUTH_PROFILE_CLAUDE ? AUTH_PROFILE_CLAUDE : AUTH_PROFILE_CODEX;
164
172
  const responses = models.filter((model) => model.endpoints.includes("responses")).length;
165
173
  const chat = models.filter((model) => model.endpoints.includes("chat")).length;
166
- const claude = models.filter((model) => model.id.toLowerCase().startsWith("claude-")
167
- || model.vendor.toLowerCase() === "anthropic").length;
174
+ const claude = models.filter(isClaudeCopilotCatalogEntry).length;
168
175
  const lines = [
169
176
  `${commandName} models${profile === AUTH_PROFILE_CLAUDE ? " --profile claude" : ""}`,
170
177
  status("ok", `Live catalog from ${safeText(catalog?.upstreamHost, "GitHub Copilot")}: ${models.length} selectable of ${Number(catalog?.advertised) || 0} advertised`),
@@ -150,6 +150,7 @@ export function createCopilotClientRuntime({
150
150
  }
151
151
 
152
152
  async function responses(reqBody, {
153
+ assertActive,
153
154
  signal,
154
155
  fetchImpl,
155
156
  retryOptions,
@@ -160,13 +161,14 @@ export function createCopilotClientRuntime({
160
161
  } = {}) {
161
162
  const token = await tokenSession.getToken({ signal });
162
163
  const preparedPayload = await prepareResponsesPayload(reqBody, {
164
+ assertActive,
163
165
  ...payloadOptions,
164
166
  currentInputStart,
165
167
  profiles: payloadPrepared ? [] : payloadOptions.profiles,
166
168
  skipInitialOptimization: payloadPrepared || payloadOptions.skipInitialOptimization,
167
169
  signal,
168
170
  });
169
- const finalizedPayload = enforceResponsesPayloadByteBudget(reqBody, preparedPayload);
171
+ const finalizedPayload = enforceResponsesPayloadByteBudget(reqBody, preparedPayload, { assertActive });
170
172
  const { bodyText, bodyBytes, stage, adapted } = finalizedPayload;
171
173
  const summary = finalizedPayload.stage === preparedPayload.stage
172
174
  ? preparedPayload.summary
package/src/doctor.mjs CHANGED
@@ -12,7 +12,7 @@ import { claudeDesktopPaths } from "./claude-desktop-config.mjs";
12
12
  import { status } from "./status.mjs";
13
13
  import { buildHeaders, DEFAULT_API_BASE, FALLBACK_VSCODE_VERSION } from "./copilot.mjs";
14
14
  import { adapterBaseUrl, checkRunningAdapter } from "./running-adapter.mjs";
15
- import { CODEX_AUTO_REVIEW_MODEL } from "./models.mjs";
15
+ import { CODEX_AUTO_REVIEW_MODEL, isClaudeCopilotModel } from "./models.mjs";
16
16
  import { loadModelCache } from "./model-cache.mjs";
17
17
 
18
18
  function localGatewayBaseUrl(host, port) {
@@ -66,7 +66,7 @@ function modelEndpoints(model) {
66
66
 
67
67
  export function selectCompatibilityModels(models, { claudeModels = models } = {}) {
68
68
  const data = copilotModelData(models).filter((model) => model?.model_picker_enabled !== false);
69
- const claudeData = copilotModelData(claudeModels).filter((model) => model?.model_picker_enabled !== false);
69
+ const claudeData = copilotModelData(claudeModels);
70
70
  const responsesCandidates = data.filter((model) => {
71
71
  const id = String(model?.id || "");
72
72
  const endpoints = modelEndpoints(model);
@@ -74,12 +74,7 @@ export function selectCompatibilityModels(models, { claudeModels = models } = {}
74
74
  });
75
75
  const responsesOnly = [...responsesCandidates].reverse()
76
76
  .find((model) => !modelEndpoints(model).includes("/chat/completions"));
77
- const claude = claudeData.find((model) => {
78
- const id = String(model?.id || "");
79
- const vendor = String(model?.vendor || "").toLowerCase();
80
- return (id.startsWith("claude-") || vendor === "anthropic")
81
- && modelEndpoints(model).includes("/chat/completions");
82
- });
77
+ const claude = claudeData.find(isClaudeCopilotModel);
83
78
  return {
84
79
  responsesModel: String((responsesOnly || responsesCandidates[0])?.id || ""),
85
80
  claudeModel: String(claude?.id || ""),
@@ -588,6 +583,12 @@ export async function collectDoctorChecks({
588
583
  }
589
584
  if (!pm.adapter?.ok) {
590
585
  checks.push({ kind: "warn", message: "PM Studio relay is not currently available", fix: "ccdx start" });
586
+ } else if (!pm.runtime?.ok) {
587
+ checks.push({
588
+ kind: "warn",
589
+ message: "PM Studio relay is running, but its isolated routing is not ready",
590
+ fix: "stop the running adapter, then run ccdx start",
591
+ });
591
592
  }
592
593
  } else if (pm.app.state === "clean") {
593
594
  checks.push({
@@ -47,29 +47,56 @@ function admissionAbortError(signal) {
47
47
  return error;
48
48
  }
49
49
 
50
- function createSupplementalAdmission({ maxWeight, maxQueued, waitTimeoutMs, label }) {
50
+ function createSupplementalAdmission({ maxWeight, maxQueued, waitTimeoutMs, label, blockBehindExclusive = false }) {
51
51
  const queue = [];
52
52
  let activeWeight = 0;
53
+ let activeRequestedWeight = 0;
53
54
  let activeRequests = 0;
54
55
 
55
56
  const activate = (entry) => {
56
57
  entry.cleanup();
57
58
  activeWeight += entry.weight;
59
+ activeRequestedWeight += entry.requestedWeight;
58
60
  activeRequests += 1;
59
61
  let released = false;
60
- entry.resolve(() => {
62
+ const release = () => {
61
63
  if (released) return;
62
64
  released = true;
63
65
  activeWeight = Math.max(0, activeWeight - entry.weight);
66
+ activeRequestedWeight = Math.max(0, activeRequestedWeight - entry.requestedWeight);
64
67
  activeRequests = Math.max(0, activeRequests - 1);
65
68
  drain();
69
+ };
70
+ release.resize = (requestedWeight) => {
71
+ if (released) return 0;
72
+ const parsedWeight = Number.isFinite(requestedWeight) && requestedWeight > 0
73
+ ? Math.ceil(requestedWeight)
74
+ : maxWeight;
75
+ const requested = Math.max(1, parsedWeight);
76
+ const weight = Math.min(requested, maxWeight);
77
+ if (weight > entry.weight) {
78
+ throw new RangeError(`${label} admission weight cannot grow`);
79
+ }
80
+ activeWeight = Math.max(0, activeWeight - (entry.weight - weight));
81
+ activeRequestedWeight = Math.max(0, activeRequestedWeight + requested - entry.requestedWeight);
82
+ entry.weight = weight;
83
+ entry.requestedWeight = requested;
84
+ drain();
85
+ return weight;
86
+ };
87
+ Object.defineProperties(release, {
88
+ weight: { get: () => released ? 0 : entry.weight },
89
+ requestedWeight: { get: () => released ? 0 : entry.requestedWeight },
90
+ maxWeight: { value: maxWeight },
66
91
  });
92
+ entry.resolve(release);
67
93
  };
68
94
 
69
95
  const drain = () => {
70
96
  for (let index = 0; index < queue.length;) {
71
97
  const entry = queue[index];
72
98
  if (activeWeight + entry.weight > maxWeight) {
99
+ if (blockBehindExclusive && entry.weight === maxWeight) break;
73
100
  index += 1;
74
101
  continue;
75
102
  }
@@ -83,8 +110,12 @@ function createSupplementalAdmission({ maxWeight, maxQueued, waitTimeoutMs, labe
83
110
  const parsedWeight = Number.isFinite(requestedWeight) && requestedWeight > 0
84
111
  ? Math.ceil(requestedWeight)
85
112
  : maxWeight;
86
- const weight = Math.max(1, Math.min(parsedWeight, maxWeight));
87
- if (queue.length >= maxQueued && activeWeight + weight > maxWeight) {
113
+ const requested = Math.max(1, parsedWeight);
114
+ const weight = Math.min(requested, maxWeight);
115
+ const blockedByExclusive = blockBehindExclusive
116
+ && queue.some((entry) => entry.weight === maxWeight);
117
+ const mustWait = blockedByExclusive || activeWeight + weight > maxWeight;
118
+ if (queue.length >= maxQueued && mustWait) {
88
119
  return Promise.reject(httpError(`${label} queue is full (${maxQueued} waiting)`, 503));
89
120
  }
90
121
 
@@ -92,6 +123,7 @@ function createSupplementalAdmission({ maxWeight, maxQueued, waitTimeoutMs, labe
92
123
  let timer;
93
124
  const entry = {
94
125
  weight,
126
+ requestedWeight: requested,
95
127
  resolve,
96
128
  cleanup: () => {
97
129
  if (timer) clearTimeout(timer);
@@ -120,7 +152,13 @@ function createSupplementalAdmission({ maxWeight, maxQueued, waitTimeoutMs, labe
120
152
  });
121
153
  };
122
154
 
123
- acquire.stats = () => ({ activeWeight, activeRequests, queued: queue.length, maxWeight });
155
+ acquire.stats = () => ({
156
+ activeWeight,
157
+ activeRequestedWeight,
158
+ activeRequests,
159
+ queued: queue.length,
160
+ maxWeight,
161
+ });
124
162
  return acquire;
125
163
  }
126
164
 
@@ -143,6 +181,7 @@ export function createRequestAdmission({
143
181
  maxQueued: queueLimit,
144
182
  waitTimeoutMs: timeoutMs,
145
183
  label: "Decoded request body",
184
+ blockBehindExclusive: true,
146
185
  });
147
186
  const acquireResponseHistory = createSupplementalAdmission({
148
187
  maxWeight: byteLimit,
@@ -194,10 +233,30 @@ export function createRequestAdmission({
194
233
  releaseSupplemental();
195
234
  throw admissionAbortError(options?.signal);
196
235
  }
197
- supplementalReleases.add(releaseSupplemental);
236
+ let supplementalReleased = false;
237
+ const releaseReservation = () => {
238
+ if (supplementalReleased) return;
239
+ supplementalReleased = true;
240
+ supplementalReleases.delete(releaseReservation);
241
+ releaseSupplemental();
242
+ };
243
+ releaseReservation.resize = (nextWeight) => {
244
+ if (supplementalReleased) return 0;
245
+ return releaseSupplemental.resize(nextWeight);
246
+ };
247
+ Object.defineProperties(releaseReservation, {
248
+ weight: { get: () => supplementalReleased ? 0 : releaseSupplemental.weight },
249
+ requestedWeight: {
250
+ get: () => supplementalReleased ? 0 : releaseSupplemental.requestedWeight,
251
+ },
252
+ maxWeight: { value: releaseSupplemental.maxWeight },
253
+ });
254
+ supplementalReleases.add(releaseReservation);
255
+ return releaseReservation;
198
256
  };
199
257
  release.acquireDecompression = (options) => acquireDecompression(1, options);
200
258
  release.reserveDecodedBody = (bytes, options) => reserve(acquireDecodedBody, bytes, options);
259
+ release.reserveDecodedBody.supportsResize = true;
201
260
  release.reserveResponseHistory = (bytes, options) => reserve(acquireResponseHistory, bytes, options);
202
261
  entry.resolve(release);
203
262
  };
@@ -285,7 +344,8 @@ export function createRequestAdmission({
285
344
  waitMsMax: counters.waitMsMax,
286
345
  decompressionsActive: acquireDecompression.stats().activeRequests,
287
346
  decompressionsQueued: acquireDecompression.stats().queued,
288
- decodedBodyBytes: acquireDecodedBody.stats().activeWeight,
347
+ decodedBodyBytes: acquireDecodedBody.stats().activeRequestedWeight,
348
+ decodedBodyAdmissionBytes: acquireDecodedBody.stats().activeWeight,
289
349
  decodedBodiesActive: acquireDecodedBody.stats().activeRequests,
290
350
  decodedBodiesQueued: acquireDecodedBody.stats().queued,
291
351
  responseHistoryBytes: acquireResponseHistory.stats().activeWeight,
@@ -445,21 +505,62 @@ export async function readJsonBody(req, {
445
505
  return parseRequestJson(await readIdentityText(req, maxBodyBytes, maxDecodedBodyBytes, signal));
446
506
  }
447
507
  const buffer = await readRequestBuffer(req, maxBodyBytes, signal);
448
- const releaseDecompression = await admission?.acquireDecompression?.({ signal }) || (() => {});
508
+ let releaseDecompression = () => {};
509
+ let decodedReservation;
510
+ let retainDecodedReservation = false;
449
511
  try {
450
512
  let decoded;
513
+ const supportsResizableReservation = admission?.reserveDecodedBody?.supportsResize === true;
514
+ // Most compressed bodies fit the existing 4x admission estimate. High-ratio bodies retry
515
+ // only after obtaining the pool's exclusive reservation, so a full decoded body never waits
516
+ // outside the decoded-body budget.
517
+ const estimatedDecodedBytes = Math.max(
518
+ 1,
519
+ Math.min(buffer.length * COMPRESSED_BODY_WEIGHT_MULTIPLIER, maxDecodedBodyBytes),
520
+ );
521
+ if (supportsResizableReservation) {
522
+ decodedReservation = await admission.reserveDecodedBody(estimatedDecodedBytes, { signal });
523
+ }
524
+ releaseDecompression = await admission?.acquireDecompression?.({ signal }) || (() => {});
525
+ const reservedBytes = decodedReservation?.requestedWeight;
526
+ const reservedAdmissionBytes = decodedReservation?.weight;
527
+ const decodedPoolBytes = decodedReservation?.maxWeight;
528
+ const tentativeDecodeLimit = Number.isFinite(reservedBytes)
529
+ && Number.isFinite(reservedAdmissionBytes)
530
+ && Number.isFinite(decodedPoolBytes)
531
+ && reservedAdmissionBytes < decodedPoolBytes
532
+ ? Math.min(reservedBytes, maxDecodedBodyBytes)
533
+ : maxDecodedBodyBytes;
451
534
  try {
452
- decoded = await decodeRequestBuffer(buffer, req.headers?.["content-encoding"], maxDecodedBodyBytes);
535
+ decoded = await decodeRequestBuffer(buffer, req.headers?.["content-encoding"], tentativeDecodeLimit);
453
536
  } catch (e) {
454
- if (e?.statusCode) throw e;
455
- throw httpError(`Invalid compressed request body: ${e.message}`, 400);
537
+ if (e?.statusCode === 413 && tentativeDecodeLimit < maxDecodedBodyBytes && supportsResizableReservation) {
538
+ releaseDecompression();
539
+ releaseDecompression = () => {};
540
+ decodedReservation();
541
+ decodedReservation = await admission.reserveDecodedBody(maxDecodedBodyBytes, { signal });
542
+ releaseDecompression = await admission.acquireDecompression?.({ signal }) || (() => {});
543
+ try {
544
+ decoded = await decodeRequestBuffer(buffer, req.headers?.["content-encoding"], maxDecodedBodyBytes);
545
+ } catch (retryError) {
546
+ if (retryError?.statusCode) throw retryError;
547
+ throw httpError(`Invalid compressed request body: ${retryError.message}`, 400);
548
+ }
549
+ } else {
550
+ if (e?.statusCode) throw e;
551
+ throw httpError(`Invalid compressed request body: ${e.message}`, 400);
552
+ }
456
553
  }
457
554
  if (signal?.aborted) throw admissionAbortError(signal);
458
555
  if (decoded.length > 0) {
459
- await admission?.reserveDecodedBody?.(decoded.length, { signal });
556
+ if (supportsResizableReservation) decodedReservation.resize(decoded.length);
557
+ else decodedReservation = await admission?.reserveDecodedBody?.(decoded.length, { signal });
460
558
  }
461
- return parseRequestJson(decoded.toString("utf8"));
559
+ const parsed = parseRequestJson(decoded.toString("utf8"));
560
+ retainDecodedReservation = true;
561
+ return parsed;
462
562
  } finally {
563
+ if (!retainDecodedReservation) decodedReservation?.();
463
564
  releaseDecompression();
464
565
  }
465
566
  }
@@ -273,9 +273,11 @@ export async function runWithConcurrency(taskFns, concurrency) {
273
273
  }));
274
274
  }
275
275
 
276
- function visitImageParts(parts, references, commit = null) {
276
+ function visitImageParts(parts, references, commit = null, assertActive) {
277
277
  if (!Array.isArray(parts)) return;
278
- for (const part of parts) {
278
+ for (let index = 0; index < parts.length; index += 1) {
279
+ if ((index & 63) === 0) assertActive?.();
280
+ const part = parts[index];
279
281
  const image = readResponsesImagePart(part);
280
282
  if (!image?.identity) continue;
281
283
  references.push({
@@ -286,22 +288,24 @@ function visitImageParts(parts, references, commit = null) {
286
288
  }
287
289
  }
288
290
 
289
- function collectImageReferences(reqBody) {
291
+ function collectImageReferences(reqBody, assertActive) {
290
292
  const references = [];
291
293
  const dirtyCommits = new Set();
292
294
  if (!Array.isArray(reqBody?.input)) {
293
295
  return { references, markDirty() {}, commitDirty() {} };
294
296
  }
295
297
 
296
- for (const item of reqBody.input) {
298
+ for (let index = 0; index < reqBody.input.length; index += 1) {
299
+ if ((index & 63) === 0) assertActive?.();
300
+ const item = reqBody.input[index];
297
301
  if (!item) continue;
298
- visitImageParts([item], references);
302
+ visitImageParts([item], references, null, assertActive);
299
303
  if (item.type === "message" && Array.isArray(item.content)) {
300
- visitImageParts(item.content, references);
304
+ visitImageParts(item.content, references, null, assertActive);
301
305
  }
302
306
  const toolOutput = readResponsesToolOutputParts(item);
303
307
  if (toolOutput) {
304
- visitImageParts(toolOutput.parts, references, toolOutput.commit);
308
+ visitImageParts(toolOutput.parts, references, toolOutput.commit, assertActive);
305
309
  }
306
310
  }
307
311
 
@@ -320,6 +324,7 @@ function collectImageReferences(reqBody) {
320
324
  }
321
325
 
322
326
  async function optimizeImageReferences(collection, {
327
+ assertActive,
323
328
  concurrency,
324
329
  model,
325
330
  optimizeImage,
@@ -334,7 +339,9 @@ async function optimizeImageReferences(collection, {
334
339
  return optimizedByDataUrl.get(identity);
335
340
  };
336
341
  const tasks = collection.references.map((reference) => async () => {
342
+ assertActive?.();
337
343
  if (reference.set(await optimizeOnce(reference.get()))) collection.markDirty([reference]);
344
+ assertActive?.();
338
345
  });
339
346
  if (tasks.length) await runWithConcurrency(tasks, concurrency);
340
347
  collection.commitDirty();
@@ -346,9 +353,11 @@ function imageInputBytes(dataUrl) {
346
353
  return match ? Buffer.byteLength(match[1], "base64") : 0;
347
354
  }
348
355
 
349
- function uniqueOriginalImages(references) {
356
+ function uniqueOriginalImages(references, assertActive) {
350
357
  const unique = new Map();
351
- for (const reference of references) {
358
+ for (let index = 0; index < references.length; index += 1) {
359
+ if ((index & 63) === 0) assertActive?.();
360
+ const reference = references[index];
352
361
  const dataUrl = reference.get();
353
362
  const identity = canonicalInlineImageIdentity(dataUrl);
354
363
  if (!identity) continue;
@@ -422,6 +431,7 @@ export async function optimizeImagesInBody(reqBody, {
422
431
  }
423
432
 
424
433
  export async function prepareResponsesPayload(reqBody, {
434
+ assertActive,
425
435
  currentInputStart = 0,
426
436
  maxBytes = MAX_UPSTREAM_BODY_BYTES,
427
437
  optimizeImage = optimizeImageDataUrl,
@@ -429,18 +439,22 @@ export async function prepareResponsesPayload(reqBody, {
429
439
  skipInitialOptimization = false,
430
440
  signal,
431
441
  } = {}) {
442
+ assertActive?.();
432
443
  const shouldOptimizeImages = !IMG_OPT_DISABLED && !skipInitialOptimization;
433
- const collection = shouldOptimizeImages ? collectImageReferences(reqBody) : null;
434
- const originalImages = collection ? uniqueOriginalImages(collection.references) : [];
444
+ const collection = shouldOptimizeImages ? collectImageReferences(reqBody, assertActive) : null;
445
+ const originalImages = collection ? uniqueOriginalImages(collection.references, assertActive) : [];
435
446
  if (collection) {
436
447
  await optimizeImageReferences(collection, {
448
+ assertActive,
437
449
  concurrency: IMG_CONCURRENCY,
438
450
  model: reqBody.model,
439
451
  optimizeImage: (dataUrl, options) => optimizeImage(dataUrl, { ...options, quality: IMG_QUALITY }),
440
452
  signal,
441
453
  });
442
454
  }
455
+ assertActive?.();
443
456
  let bodyText = JSON.stringify(reqBody);
457
+ assertActive?.();
444
458
  let bodyBytes = Buffer.byteLength(bodyText);
445
459
  const summary = summarizeReqBody(reqBody);
446
460
  const targetBytes = positiveInt(maxBytes, MAX_UPSTREAM_BODY_BYTES);
@@ -450,11 +464,13 @@ export async function prepareResponsesPayload(reqBody, {
450
464
  if (collection && originalImages.length > 0 && bodyBytes > targetBytes) {
451
465
  const batchSize = Math.max(1, IMG_CONCURRENCY);
452
466
  for (const profile of Array.isArray(profiles) ? profiles : []) {
467
+ assertActive?.();
453
468
  const beforeBytes = bodyBytes;
454
469
  let processed = 0;
455
470
  stage = `q${positiveInt(profile.quality, IMG_QUALITY, 100)}`;
456
471
  const orderedImages = orderImagesByCurrentWireBytes(originalImages);
457
472
  for (let index = 0; index < orderedImages.length && bodyBytes > targetBytes; index += batchSize) {
473
+ assertActive?.();
458
474
  const batch = orderedImages.slice(index, index + batchSize);
459
475
  const candidates = await Promise.all(batch.map((image) => optimizeImage(image.dataUrl, {
460
476
  ...profile,
@@ -462,6 +478,7 @@ export async function prepareResponsesPayload(reqBody, {
462
478
  model: reqBody.model,
463
479
  signal,
464
480
  })));
481
+ assertActive?.();
465
482
  for (let offset = 0; offset < batch.length && bodyBytes > targetBytes; offset += 1) {
466
483
  processed += 1;
467
484
  const applied = applyImageCandidate(collection, batch[offset], candidates[offset], bodyBytes);
@@ -469,19 +486,24 @@ export async function prepareResponsesPayload(reqBody, {
469
486
  adapted ||= applied.changed;
470
487
  if (bodyBytes <= targetBytes) {
471
488
  collection.commitDirty();
489
+ assertActive?.();
472
490
  bodyText = JSON.stringify(reqBody);
491
+ assertActive?.();
473
492
  bodyBytes = Buffer.byteLength(bodyText);
474
493
  }
475
494
  }
476
495
  }
477
496
  collection.commitDirty();
497
+ assertActive?.();
478
498
  bodyText = JSON.stringify(reqBody);
499
+ assertActive?.();
479
500
  bodyBytes = Buffer.byteLength(bodyText);
480
501
  console.warn(status("warn", `responses payload ${beforeBytes}b exceeds ${targetBytes}b; image profile max_dim=${profile.maxDim} quality=${profile.quality} processed=${processed}/${orderedImages.length} -> ${bodyBytes}b`));
481
502
  if (bodyBytes <= targetBytes) break;
482
503
  }
483
504
  }
484
505
 
506
+ assertActive?.();
485
507
  return {
486
508
  bodyText,
487
509
  bodyBytes,
@@ -30,8 +30,9 @@ export function shouldShowLegacyCommandWarning({
30
30
  }
31
31
  try {
32
32
  atomicWriteFileSync(filePath, `${now}\n`, { mode: 0o600 });
33
+ return true;
33
34
  } catch {
34
- // The warning remains best effort when the cache directory is read-only.
35
+ // Stay silent when the throttle cannot persist, otherwise every invocation would warn.
36
+ return false;
35
37
  }
36
- return true;
37
38
  }