codex-copilot-dx 0.4.43 → 0.5.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.
@@ -1,6 +1,7 @@
1
1
  const HELP_COMMANDS = new Set(["help", "--help", "-h"]);
2
2
  const VERSION_COMMANDS = new Set(["version", "--version", "-v"]);
3
3
  const START_OPTIONS = new Set(["--configure-claude-desktop", "--show-request-id"]);
4
+ const CLI_COMMANDS = new Set(["ccdx", "codex-copilot-dx"]);
4
5
 
5
6
  function unexpectedArgs(args) {
6
7
  throw new Error(`Unexpected argument${args.length === 1 ? "" : "s"}: ${args.join(" ")}`);
@@ -17,6 +18,10 @@ export function parseCliArgs(args = []) {
17
18
  if (rest.length) unexpectedArgs(rest);
18
19
  return { command: "version", configureClaudeDesktop: false, showRequestId: false, online: false, compat: false };
19
20
  }
21
+ if (command === "status" || command === "--status") {
22
+ if (rest.length) unexpectedArgs(rest);
23
+ return { command: "status", configureClaudeDesktop: false, showRequestId: false, online: false, compat: false };
24
+ }
20
25
  if (command === "usage") {
21
26
  if (rest.length) unexpectedArgs(rest);
22
27
  return { command: "usage", configureClaudeDesktop: false, showRequestId: false, online: false, compat: false };
@@ -80,11 +85,30 @@ export function parseRuntimeOptions(env = process.env) {
80
85
  };
81
86
  }
82
87
 
83
- export function cliHelp() {
88
+ export function parseAdapterProbeOptions(env = process.env) {
89
+ return {
90
+ adapterPort: integerEnv(env, "ADAPTER_PORT", 2026, { min: 1, max: 65535 }),
91
+ adapterHost: String(env.ADAPTER_HOST || "127.0.0.1").trim() || "127.0.0.1",
92
+ existingAdapterTimeoutMs: integerEnv(env, "CCDX_EXISTING_ADAPTER_TIMEOUT_MS", 500, { min: 1 }),
93
+ };
94
+ }
95
+
96
+ export function cliCommandName(executable = process.argv[1]) {
97
+ const name = String(executable || "").split(/[\\/]/).pop();
98
+ if (name === "codex-copilot-dx.mjs") return "codex-copilot-dx";
99
+ return CLI_COMMANDS.has(name) ? name : "ccdx";
100
+ }
101
+
102
+ export function cliHelp(commandName = "ccdx") {
103
+ const name = CLI_COMMANDS.has(commandName) ? commandName : "ccdx";
104
+ const alias = name === "ccdx" ? "codex-copilot-dx" : "ccdx";
84
105
  return `Usage:
85
- codex-copilot-dx [--configure-claude-desktop] [--show-request-id]
86
- codex-copilot-dx doctor [--online] [--compat]
87
- codex-copilot-dx usage
88
- codex-copilot-dx --version
89
- codex-copilot-dx --help`;
106
+ ${name} [--configure-claude-desktop] [--show-request-id]
107
+ ${name} doctor [--online] [--compat]
108
+ ${name} status
109
+ ${name} usage
110
+ ${name} --version
111
+ ${name} --help
112
+
113
+ Equivalent command: ${alias}`;
90
114
  }
@@ -0,0 +1,122 @@
1
+ import { ADAPTER_STATUS_PATH, adapterBaseUrl } from "./running-adapter.mjs";
2
+ import { status } from "./status.mjs";
3
+
4
+ function finiteNumber(value) {
5
+ const number = Number(value);
6
+ return Number.isFinite(number) ? number : null;
7
+ }
8
+
9
+ function count(value) {
10
+ const number = finiteNumber(value);
11
+ return number === null ? "n/a" : String(Math.max(0, Math.round(number)));
12
+ }
13
+
14
+ function duration(value) {
15
+ const milliseconds = finiteNumber(value);
16
+ if (milliseconds === null) return "n/a";
17
+ const seconds = Math.max(0, Math.round(milliseconds / 1000));
18
+ const days = Math.floor(seconds / 86400);
19
+ const hours = Math.floor((seconds % 86400) / 3600);
20
+ const minutes = Math.floor((seconds % 3600) / 60);
21
+ const remainder = seconds % 60;
22
+ if (days) return `${days}d ${hours}h`;
23
+ if (hours) return `${hours}h ${minutes}m`;
24
+ if (minutes) return `${minutes}m ${remainder}s`;
25
+ return `${remainder}s`;
26
+ }
27
+
28
+ function latency(value) {
29
+ const milliseconds = finiteNumber(value);
30
+ if (milliseconds === null) return "n/a";
31
+ if (milliseconds >= 1000) return `${(milliseconds / 1000).toFixed(2)}s`;
32
+ return `${milliseconds.toFixed(1)}ms`;
33
+ }
34
+
35
+ function mebibytes(value) {
36
+ const bytes = finiteNumber(value);
37
+ return bytes === null ? "n/a" : `${(bytes / (1024 * 1024)).toFixed(1)}MiB`;
38
+ }
39
+
40
+ function tpot(metric) {
41
+ const average = finiteNumber(metric?.avg);
42
+ if (average === null) return "n/a";
43
+ const milliseconds = metric?.unit === "us" ? average / 1000 : average;
44
+ return `${milliseconds.toFixed(2)}ms/token`;
45
+ }
46
+
47
+ function cacheHitRate(cache = {}) {
48
+ const hits = finiteNumber(cache.cache_hits);
49
+ const misses = finiteNumber(cache.cache_misses);
50
+ if (hits === null || misses === null || hits + misses <= 0) return "n/a";
51
+ return `${((hits / (hits + misses)) * 100).toFixed(1)}%`;
52
+ }
53
+
54
+ export async function readAdapterStatus({
55
+ host = "127.0.0.1",
56
+ port = 2026,
57
+ timeoutMs = 500,
58
+ fetchImpl = fetch,
59
+ } = {}) {
60
+ const baseUrl = adapterBaseUrl(host, port);
61
+ const ctrl = new AbortController();
62
+ const timer = setTimeout(() => ctrl.abort(), timeoutMs);
63
+ try {
64
+ const response = await fetchImpl(`${baseUrl}${ADAPTER_STATUS_PATH}`, {
65
+ headers: { Accept: "application/json" },
66
+ redirect: "error",
67
+ signal: ctrl.signal,
68
+ });
69
+ if (!response.ok) throw new Error(`returned HTTP ${response.status}`);
70
+
71
+ let data;
72
+ try {
73
+ data = await response.json();
74
+ } catch {
75
+ throw new Error("returned invalid JSON");
76
+ }
77
+ if (data?.ok !== true
78
+ || data?.name !== "codex-copilot-dx"
79
+ || typeof data.version !== "string"
80
+ || !Number.isInteger(data.pid)) {
81
+ throw new Error("returned an incompatible status payload");
82
+ }
83
+ return { baseUrl, data };
84
+ } catch (error) {
85
+ const reason = ctrl.signal.aborted
86
+ ? `timed out after ${timeoutMs}ms`
87
+ : error?.cause?.code === "ECONNREFUSED"
88
+ ? "adapter is not running"
89
+ : error?.message || String(error);
90
+ throw new Error(`Could not read adapter status at ${baseUrl}: ${reason}`);
91
+ } finally {
92
+ clearTimeout(timer);
93
+ }
94
+ }
95
+
96
+ export function formatAdapterStatus({ baseUrl, data }, { commandName = "ccdx", cliVersion = data.version } = {}) {
97
+ const requests = data.requests || {};
98
+ const performance = data.stream_performance?.by_route?.responses || {};
99
+ const admission = data.admission || {};
100
+ const history = data.response_history || {};
101
+ const images = data.image_optimization || {};
102
+ const models = data.models || {};
103
+ const copilot = data.copilot || {};
104
+ const limits = data.limits || {};
105
+ const processStats = data.process || {};
106
+ const adapterLine = data.version === cliVersion
107
+ ? status("ok", `Adapter ${data.version} is running at ${baseUrl} (PID ${data.pid}, uptime ${duration(data.uptime_ms)})`)
108
+ : status("warn", `Adapter ${data.version} is running at ${baseUrl}, but this CLI is ${cliVersion}; stop the running adapter before switching versions`);
109
+
110
+ return [
111
+ `${commandName} status`,
112
+ adapterLine,
113
+ status("info", `Requests: ${count(requests.completed)}/${count(requests.total)} completed, ${count(requests.active)} active, ${count(requests.status_4xx)} 4xx, ${count(requests.status_5xx)} 5xx, ${count(requests.aborted)} aborted`),
114
+ status("info", `Responses stream: TTFT avg ${latency(performance.ttft_ms?.avg)} (${count(performance.ttft_ms?.samples)} samples), TPOT avg ${tpot(performance.tpot_us)}`),
115
+ status("info", `Admission: ${count(admission.activeRequests)} active, ${count(admission.queued)} queued, ${count(admission.rejected)} rejected, ${count(admission.timedOut)} timed out, wait avg ${latency(admission.waitMsAvg)}`),
116
+ status("info", `Memory: RSS ${mebibytes(processStats.rss_bytes)}, heap ${mebibytes(processStats.heap_used_bytes)}`),
117
+ status("info", `History: ${mebibytes(history.bytes)} / ${mebibytes(limits.response_history_max_bytes)}, ${count(history.entries)} entries, ${count(history.evicted)} evicted`),
118
+ status("info", `Image cache: ${count(images.cache_entries)} entries, ${cacheHitRate(images)} hits, ${mebibytes(images.cache_bytes)} / ${mebibytes(images.cache_max_bytes)}`),
119
+ status("info", `Models: ${count(models.models)} total, ${count(models.claude_models)} Claude; Copilot token ${copilot.token_cached ? `cached (${duration(copilot.token_expires_in_ms)} remaining)` : "not cached"}`),
120
+ status("info", `Limits: request body ${mebibytes(limits.max_body_bytes)}, decoded body ${mebibytes(limits.max_decoded_body_bytes)}`),
121
+ ].join("\n");
122
+ }
package/src/copilot.mjs CHANGED
@@ -19,6 +19,8 @@ import {
19
19
  } from "./github-identity.mjs";
20
20
  import { prepareResponsesPayload, summarizeReqBody } from "./image-optimization.mjs";
21
21
  import { enforceResponsesPayloadByteBudget } from "./responses-byte-budget.mjs";
22
+ import { withChatStreamUsage } from "./stream-contract.mjs";
23
+ import { markUpstreamStarted } from "./stream-performance.mjs";
22
24
 
23
25
  export {
24
26
  optimizeImageDataUrl,
@@ -317,7 +319,7 @@ export function copilotRuntimeStatus(now = Date.now()) {
317
319
  function getGithubToken({ home = os.homedir() } = {}) {
318
320
  const GITHUB_TOKEN_PATH = githubTokenPath(home);
319
321
  if (!fs.existsSync(GITHUB_TOKEN_PATH)) {
320
- throw new Error("GitHub token not found. Run codex-copilot-dx again to log in.");
322
+ throw new Error("GitHub token not found. Run ccdx again to log in.");
321
323
  }
322
324
  const token = fs.readFileSync(GITHUB_TOKEN_PATH, "utf-8").trim();
323
325
  if (!token) throw new Error(githubReauthMessage("GitHub token file is empty."));
@@ -343,7 +345,7 @@ function cacheCopilotTokenData(data, {
343
345
  const tokenChanged = Boolean(copilotTokenGithubFingerprint && fingerprint !== copilotTokenGithubFingerprint);
344
346
  if (!allowAccountSwitch && copilotTokenGithubIdentity && identity
345
347
  && !githubIdentitiesEqual(copilotTokenGithubIdentity, identity)) {
346
- throw new Error(`Refusing to switch GitHub Copilot account from ${githubIdentityLabel(copilotTokenGithubIdentity)} to ${githubIdentityLabel(identity)} while the adapter is running. Restart codex-copilot-dx to switch accounts intentionally.`);
348
+ throw new Error(`Refusing to switch GitHub Copilot account from ${githubIdentityLabel(copilotTokenGithubIdentity)} to ${githubIdentityLabel(identity)} while the adapter is running. Restart ccdx to switch accounts intentionally.`);
347
349
  }
348
350
  if (!allowAccountSwitch && copilotTokenGithubIdentity && tokenChanged && !identity) {
349
351
  const error = new Error("The saved GitHub token changed, but its account could not be verified. Keeping the running adapter bound to the existing GitHub account.");
@@ -508,8 +510,11 @@ export async function chatCompletions(chatReq, {
508
510
  bodyText,
509
511
  } = {}) {
510
512
  const token = await getCopilotToken({ signal });
511
- const messages = chatReq.messages || [];
512
- const serializedBody = typeof bodyText === "string" ? bodyText : JSON.stringify(chatReq);
513
+ const upstreamReq = chatReq.stream === true ? withChatStreamUsage(chatReq) : chatReq;
514
+ const messages = upstreamReq.messages || [];
515
+ const serializedBody = typeof bodyText === "string" && upstreamReq === chatReq
516
+ ? bodyText
517
+ : JSON.stringify(upstreamReq);
513
518
  const headers = buildHeaders({
514
519
  token,
515
520
  version: getVSCodeVersion(),
@@ -517,6 +522,7 @@ export async function chatCompletions(chatReq, {
517
522
  vision: computeVision(messages),
518
523
  });
519
524
  headers["Content-Length"] = String(Buffer.byteLength(serializedBody));
525
+ if (upstreamReq.stream === true) markUpstreamStarted();
520
526
  return fetchCopilotUpstream(`${getApiBase()}/chat/completions`, {
521
527
  method: "POST",
522
528
  headers,
@@ -624,6 +630,7 @@ export async function responses(reqBody, {
624
630
  headers["Accept"] = reqBody.stream ? "text/event-stream" : "application/json";
625
631
 
626
632
  try {
633
+ if (reqBody.stream === true) markUpstreamStarted();
627
634
  return await fetchCopilotUpstream(`${getApiBase()}${responsesEndpointPath()}`, {
628
635
  method: "POST",
629
636
  headers,
package/src/doctor.mjs CHANGED
@@ -124,6 +124,15 @@ function parseResponseObject(text) {
124
124
  return response;
125
125
  }
126
126
 
127
+ function parseCompactionResponse(text) {
128
+ const response = parseResponseObject(text);
129
+ if (response.object !== "response.compaction"
130
+ || !response.output.some((item) => item?.type === "compaction")) {
131
+ throw new Error("response body is missing valid compaction state");
132
+ }
133
+ return response;
134
+ }
135
+
127
136
  export async function inspectAdapterCompatibility({
128
137
  host = "127.0.0.1",
129
138
  port = 2026,
@@ -184,7 +193,7 @@ export async function inspectAdapterCompatibility({
184
193
  stream: false,
185
194
  input: "Compact this short context.",
186
195
  }, timeoutMs);
187
- parseResponseObject(text);
196
+ parseCompactionResponse(text);
188
197
  });
189
198
 
190
199
  if (!claudeModel) {
@@ -446,7 +455,7 @@ export async function collectDoctorChecks({
446
455
  export async function runDoctor(options = {}) {
447
456
  const log = options.log || console.log;
448
457
  const flags = [options.online ? "--online" : "", options.compat ? "--compat" : ""].filter(Boolean);
449
- log(`codex-copilot-dx doctor${flags.length ? ` ${flags.join(" ")}` : ""}`);
458
+ log(`${options.commandName || "ccdx"} doctor${flags.length ? ` ${flags.join(" ")}` : ""}`);
450
459
  const checks = await collectDoctorChecks(options);
451
460
  for (const check of checks) log(status(check.kind, check.message));
452
461
  return checks;
@@ -2,6 +2,7 @@ import { promisify } from "node:util";
2
2
  import * as zlib from "node:zlib";
3
3
  import { loadRuntimeConfig, parsePositiveInteger } from "./runtime-config.mjs";
4
4
  import { status } from "./status.mjs";
5
+ import { safeUpstreamResponseHeaders } from "./upstream-headers.mjs";
5
6
 
6
7
  const COMPRESSED_BODY_WEIGHT_MULTIPLIER = 4;
7
8
  const gunzipAsync = promisify(zlib.gunzip);
@@ -296,7 +297,9 @@ export function sendJsonError(res, err, fallbackStatus = 400) {
296
297
 
297
298
  export function sendUpstreamError(res, response, text) {
298
299
  if (!res.headersSent) {
299
- res.writeHead(response.status || 502, { "Content-Type": response.headers?.get("content-type") || "application/json" });
300
+ res.writeHead(response.status || 502, safeUpstreamResponseHeaders(response.headers, {
301
+ defaultContentType: "application/json",
302
+ }));
300
303
  }
301
304
  res.end(text || JSON.stringify({ error: "Upstream request failed" }));
302
305
  }
@@ -4,17 +4,19 @@ import {
4
4
  readResponsesImagePart,
5
5
  readResponsesToolOutputParts,
6
6
  } from "./responses-content.mjs";
7
+ import { createBoundedResultCache } from "./bounded-result-cache.mjs";
7
8
  import { status } from "./status.mjs";
8
9
 
9
10
  const DEFAULT_IMG_CONCURRENCY = 2;
10
11
  const IMG_MAX_CONCURRENCY = 12;
11
12
  const DEFAULT_IMG_MAX_INPUT_PIXELS = 40 * 1000 * 1000;
13
+ const DEFAULT_IMG_CACHE_MAX_BYTES = 64 * 1024 * 1024;
12
14
  const DEFAULT_MAX_UPSTREAM_BODY_BYTES = 30 * 1024 * 1024;
13
- const MAX_OPTIMIZED_IMAGE_DIGESTS = 4096;
14
15
  const IMG_MAX_DIM = positiveInt(process.env.CCDX_IMG_MAX_DIM, 2048);
15
16
  const IMG_QUALITY = positiveInt(process.env.CCDX_IMG_QUALITY, 82, 100);
16
17
  const IMG_MIN_BYTES = nonNegativeInt(process.env.CCDX_IMG_MIN_BYTES, 100000);
17
18
  const IMG_MAX_INPUT_PIXELS = positiveInt(process.env.CCDX_IMG_MAX_INPUT_PIXELS, DEFAULT_IMG_MAX_INPUT_PIXELS);
19
+ const IMG_CACHE_MAX_BYTES = nonNegativeInt(process.env.CCDX_IMG_CACHE_MAX_BYTES, DEFAULT_IMG_CACHE_MAX_BYTES);
18
20
  const MAX_UPSTREAM_BODY_BYTES = positiveInt(process.env.CCDX_MAX_UPSTREAM_BODY_BYTES, DEFAULT_MAX_UPSTREAM_BODY_BYTES);
19
21
  const IMG_OPT_DISABLED = process.env.CCDX_DISABLE_IMG_OPT === "1";
20
22
  const IMG_CONCURRENCY = parseImageConcurrency(process.env.CCDX_IMG_CONCURRENCY);
@@ -22,7 +24,8 @@ const OVERSIZE_IMAGE_PROFILES = [
22
24
  { maxDim: Math.min(IMG_MAX_DIM, 1600), quality: Math.min(IMG_QUALITY, 75) },
23
25
  { maxDim: Math.min(IMG_MAX_DIM, 1280), quality: Math.min(IMG_QUALITY, 65) },
24
26
  ];
25
- const optimizedImageDigests = new Set();
27
+ const IMAGE_ENCODER_CONFIG = "webp:v1:effort=4:autorotate=1:fit=inside:without_enlargement=1";
28
+ const imageResultCache = createBoundedResultCache({ maxBytes: IMG_CACHE_MAX_BYTES });
26
29
  let sharpImport = null;
27
30
 
28
31
  function positiveInt(value, fallback, max = Number.MAX_SAFE_INTEGER) {
@@ -140,52 +143,50 @@ export function createTaskLimiter(concurrency) {
140
143
  const runGlobalImageTask = createTaskLimiter(IMG_CONCURRENCY);
141
144
 
142
145
  export function imageOptimizationStats() {
146
+ const cache = imageResultCache.stats();
143
147
  return {
144
148
  ...runGlobalImageTask.stats(),
145
149
  disabled: IMG_OPT_DISABLED,
146
- cache_entries: optimizedImageDigests.size,
150
+ cache_entries: cache.entries,
151
+ cache_bytes: cache.bytes,
152
+ cache_max_bytes: cache.max_bytes,
153
+ cache_hits: cache.hits,
154
+ cache_misses: cache.misses,
155
+ cache_evictions: cache.evictions,
156
+ cache_inflight: cache.inflight,
147
157
  sharp_loaded: sharpImport !== null,
148
158
  };
149
159
  }
150
160
 
161
+ export function resetImageOptimizationCacheForTests() {
162
+ imageResultCache.clear();
163
+ }
164
+
151
165
  async function sharp() {
152
166
  sharpImport ||= import("sharp");
153
167
  const mod = await sharpImport;
154
168
  return mod.default || mod;
155
169
  }
156
170
 
157
- function imageDigest(raw) {
158
- return createHash("sha256").update(raw).digest("base64url");
171
+ function imageDigest(value, encoding) {
172
+ return createHash("sha256").update(value, encoding).digest("base64url");
159
173
  }
160
174
 
161
- function imageOptimizationKey(digest, constraints, quality) {
175
+ function imageOptimizationKey(digest, mime, constraints, quality) {
162
176
  return [
163
177
  digest,
178
+ mime,
164
179
  constraints.maxLongEdge || "",
165
180
  constraints.maxShortEdge || "",
166
181
  constraints.maxArea || "",
167
182
  quality,
183
+ IMAGE_ENCODER_CONFIG,
168
184
  ].join(":");
169
185
  }
170
186
 
171
- function touchOptimizedImage(digest) {
172
- if (!optimizedImageDigests.delete(digest)) return false;
173
- optimizedImageDigests.add(digest);
174
- return true;
175
- }
176
-
177
- function rememberOptimizedImage(digest) {
178
- optimizedImageDigests.delete(digest);
179
- optimizedImageDigests.add(digest);
180
- if (optimizedImageDigests.size > MAX_OPTIMIZED_IMAGE_DIGESTS) {
181
- optimizedImageDigests.delete(optimizedImageDigests.values().next().value);
182
- }
183
- }
184
-
185
187
  export async function optimizeImageDataUrl(dataUrl, {
186
188
  maxDim = IMG_MAX_DIM,
187
189
  quality = IMG_QUALITY,
188
- force = false,
189
190
  model,
190
191
  signal,
191
192
  } = {}) {
@@ -197,52 +198,60 @@ export async function optimizeImageDataUrl(dataUrl, {
197
198
  const encoded = match[2];
198
199
  const inputBytes = Buffer.byteLength(encoded, "base64");
199
200
  if (mime.includes("gif")) return dataUrl;
201
+ if (signal?.aborted) throw abortError(signal);
202
+ const constraints = imageConstraintsForModel(model, { maxDim });
203
+ const outputQuality = positiveInt(quality, IMG_QUALITY, 100);
204
+ // Hash decoded source bytes without retaining a decoded Buffer. Cache hits
205
+ // therefore bypass both Sharp and the global image work queue.
206
+ const digest = imageDigest(encoded, "base64");
207
+ const optimizationKey = imageOptimizationKey(digest, mime, constraints, outputQuality);
200
208
 
201
209
  try {
202
- return await runGlobalImageTask(async () => {
203
- const raw = Buffer.from(encoded, "base64");
204
- const digest = imageDigest(raw);
205
- const constraints = imageConstraintsForModel(model, { maxDim });
206
- const outputQuality = positiveInt(quality, IMG_QUALITY, 100);
207
- const optimizationKey = imageOptimizationKey(digest, constraints, outputQuality);
208
- if (!force && touchOptimizedImage(optimizationKey)) return dataUrl;
209
- const resize = await sharp();
210
- const image = resize(raw, { failOn: "none", limitInputPixels: IMG_MAX_INPUT_PIXELS });
211
- const metadata = await image.metadata();
212
- const orientation = Number(metadata.orientation);
213
- const rotated = orientation >= 5 && orientation <= 8;
214
- const sourceWidth = rotated ? metadata.height : metadata.width;
215
- const sourceHeight = rotated ? metadata.width : metadata.height;
216
- const target = fitImageDimensions(sourceWidth, sourceHeight, constraints);
217
- const resizeRequired = target
218
- && (target.width < sourceWidth || target.height < sourceHeight);
219
- if (raw.length < IMG_MIN_BYTES && !resizeRequired) {
220
- rememberOptimizedImage(optimizationKey);
221
- return dataUrl;
222
- }
223
- const resizeOptions = target
224
- ? { ...target, fit: "inside", withoutEnlargement: true }
225
- : {
226
- width: constraints.maxLongEdge,
227
- height: constraints.maxLongEdge,
228
- fit: "inside",
229
- withoutEnlargement: true,
230
- };
231
- const out = await image
232
- .rotate()
233
- .resize(resizeOptions)
234
- .webp({ quality: outputQuality, effort: 4 })
235
- .toBuffer();
236
- if (signal?.aborted) throw abortError(signal);
237
- if (out.length >= raw.length) {
238
- rememberOptimizedImage(optimizationKey);
239
- return dataUrl;
240
- }
241
- rememberOptimizedImage(imageOptimizationKey(imageDigest(out), constraints, outputQuality));
242
- const ratio = ((out.length / raw.length) * 100).toFixed(1);
243
- console.log(status("info", `image ${(raw.length / 1024).toFixed(0)}KB ${mime} -> ${(out.length / 1024).toFixed(0)}KB webp (${ratio}%)`));
244
- return `data:image/webp;base64,${out.toString("base64")}`;
245
- }, { signal });
210
+ return await imageResultCache.getOrCreate(optimizationKey, (sharedSignal) => (
211
+ runGlobalImageTask(async () => {
212
+ const raw = Buffer.from(encoded, "base64");
213
+ const resize = await sharp();
214
+ const image = resize(raw, { failOn: "none", limitInputPixels: IMG_MAX_INPUT_PIXELS });
215
+ const metadata = await image.metadata();
216
+ if (sharedSignal.aborted) throw abortError(sharedSignal);
217
+ const orientation = Number(metadata.orientation);
218
+ const rotated = orientation >= 5 && orientation <= 8;
219
+ const sourceWidth = rotated ? metadata.height : metadata.width;
220
+ const sourceHeight = rotated ? metadata.width : metadata.height;
221
+ const target = fitImageDimensions(sourceWidth, sourceHeight, constraints);
222
+ const resizeRequired = target
223
+ && (target.width < sourceWidth || target.height < sourceHeight);
224
+ if (raw.length < IMG_MIN_BYTES && !resizeRequired) return dataUrl;
225
+ const resizeOptions = target
226
+ ? { ...target, fit: "inside", withoutEnlargement: true }
227
+ : {
228
+ width: constraints.maxLongEdge,
229
+ height: constraints.maxLongEdge,
230
+ fit: "inside",
231
+ withoutEnlargement: true,
232
+ };
233
+ const out = await image
234
+ .rotate()
235
+ .resize(resizeOptions)
236
+ .webp({ quality: outputQuality, effort: 4 })
237
+ .toBuffer();
238
+ if (sharedSignal.aborted) throw abortError(sharedSignal);
239
+ if (out.length >= raw.length) return dataUrl;
240
+ const outputBase64 = out.toString("base64");
241
+ const outputDataUrl = `data:image/webp;base64,${outputBase64}`;
242
+ const outputKey = imageOptimizationKey(
243
+ imageDigest(outputBase64, "base64"),
244
+ "image/webp",
245
+ constraints,
246
+ outputQuality,
247
+ );
248
+ // Preserve idempotence when optimized history is submitted again.
249
+ imageResultCache.set(outputKey, outputDataUrl);
250
+ const ratio = ((out.length / raw.length) * 100).toFixed(1);
251
+ console.log(status("info", `image ${(raw.length / 1024).toFixed(0)}KB ${mime} -> ${(out.length / 1024).toFixed(0)}KB webp (${ratio}%)`));
252
+ return outputDataUrl;
253
+ }, { signal: sharedSignal })
254
+ ), { signal });
246
255
  } catch (e) {
247
256
  if (signal?.aborted || e?.name === "AbortError") throw e;
248
257
  if (inputBytes >= IMG_MIN_BYTES) {
@@ -1,11 +1,11 @@
1
1
  import { isIP } from "node:net";
2
- import { adapterHealthPayload } from "./running-adapter.mjs";
2
+ import { ADAPTER_STATUS_PATH, adapterHealthPayload } from "./running-adapter.mjs";
3
3
  import { copilotRuntimeStatus } from "./copilot.mjs";
4
4
  import { imageOptimizationStats } from "./image-optimization.mjs";
5
5
  import { responseHistoryStats } from "./response-history.mjs";
6
6
  import { loadRuntimeConfig } from "./runtime-config.mjs";
7
7
 
8
- export const ADAPTER_STATUS_PATH = "/_ccdx/status";
8
+ export { ADAPTER_STATUS_PATH };
9
9
  const OBSERVABILITY_RUNTIME_CONFIG = loadRuntimeConfig();
10
10
 
11
11
  const ROUTE_NAMES = Object.freeze([
@@ -118,7 +118,7 @@ function modelRegistryStatus(modelRegistry) {
118
118
  };
119
119
  }
120
120
 
121
- export function runtimeStatusPayload({ metrics, admission, modelRegistry } = {}) {
121
+ export function runtimeStatusPayload({ metrics, streamPerformance, admission, modelRegistry } = {}) {
122
122
  const memory = process.memoryUsage();
123
123
  return {
124
124
  ...adapterHealthPayload(),
@@ -131,6 +131,7 @@ export function runtimeStatusPayload({ metrics, admission, modelRegistry } = {})
131
131
  array_buffers_bytes: memory.arrayBuffers,
132
132
  },
133
133
  requests: metrics?.snapshot?.() || createRequestMetrics().snapshot(),
134
+ stream_performance: streamPerformance?.snapshot?.() || null,
134
135
  admission: admission?.diagnostics?.() || admission?.stats?.() || null,
135
136
  response_history: responseHistoryStats(),
136
137
  image_optimization: imageOptimizationStats(),
@@ -2,6 +2,15 @@ import { randomUUID } from "node:crypto";
2
2
  import { chatCompletions } from "./copilot.mjs";
3
3
  import { webStreamLines } from "./stream.mjs";
4
4
  import { abortErrorStatusCode, isAbortLikeError } from "./http-transport.mjs";
5
+ import {
6
+ incompleteUpstreamStream,
7
+ invalidUpstreamStream,
8
+ requireUpstreamEventStream,
9
+ ToolArgumentDeltaGuard,
10
+ upstreamChatStreamError,
11
+ withChatStreamUsage,
12
+ } from "./stream-contract.mjs";
13
+ import { isChatOutputDelta, markFirstOutput, markOutputTokens } from "./stream-performance.mjs";
5
14
 
6
15
  function cloneJson(value) {
7
16
  return value === undefined ? undefined : structuredClone(value);
@@ -134,7 +143,7 @@ export function chatToResponses(chatResp, model) {
134
143
 
135
144
  export async function forwardToChat(chatReq, emitEvent, onDone, onError, options = {}) {
136
145
  delete chatReq.max_tokens;
137
- const upstreamReq = chatReq.stream === true ? chatReq : { ...chatReq, stream: true };
146
+ const upstreamReq = withChatStreamUsage(chatReq);
138
147
  const bodyText = upstreamReq === chatReq ? options.bodyText : undefined;
139
148
  let resp;
140
149
  try {
@@ -150,9 +159,16 @@ export async function forwardToChat(chatReq, emitEvent, onDone, onError, options
150
159
  return;
151
160
  }
152
161
  if (!resp.ok) {
153
- await onError(resp.status, await resp.text());
162
+ await onError(resp.status, await resp.text(), undefined, resp);
154
163
  return;
155
164
  }
165
+ try {
166
+ await requireUpstreamEventStream(resp);
167
+ } catch (error) {
168
+ await onError(error.statusCode, error.message, error);
169
+ return;
170
+ }
171
+ options.onUpstreamResponse?.(resp);
156
172
  options.abort?.setTimeout(options.streamIdleTimeoutMs, "stream_idle_timeout");
157
173
  const respId = `resp_${uid()}`;
158
174
  let actualModel = chatReq.model || "unknown";
@@ -161,6 +177,13 @@ export async function forwardToChat(chatReq, emitEvent, onDone, onError, options
161
177
  let nextOutputIndex = 0;
162
178
  let usage = { input_tokens: 0, output_tokens: 0, total_tokens: 0 };
163
179
  const toolCalls = new Map();
180
+ const toolArgumentGuard = new ToolArgumentDeltaGuard();
181
+ let performanceOutputSeen = false;
182
+ const markPerformanceOutput = () => {
183
+ if (performanceOutputSeen) return;
184
+ performanceOutputSeen = true;
185
+ markFirstOutput();
186
+ };
164
187
 
165
188
  await emitEvent("response.created", { response: { id: respId, object: "response", status: "in_progress", model: actualModel, output: [] } });
166
189
 
@@ -258,7 +281,16 @@ export async function forwardToChat(chatReq, emitEvent, onDone, onError, options
258
281
  const data = line.slice(6).trim();
259
282
  if (data === "[DONE]") { await emitCompleted(); onDone(); return; }
260
283
  let parsed;
261
- try { parsed = JSON.parse(data); } catch { continue; }
284
+ try {
285
+ parsed = JSON.parse(data);
286
+ } catch {
287
+ throw invalidUpstreamStream(
288
+ "Upstream Chat Completions SSE event contained invalid JSON",
289
+ "upstream_stream_json_invalid",
290
+ );
291
+ }
292
+ const upstreamError = upstreamChatStreamError(parsed);
293
+ if (upstreamError) throw upstreamError;
262
294
  if (parsed.model) actualModel = parsed.model;
263
295
  if (parsed.usage) {
264
296
  usage = {
@@ -268,9 +300,11 @@ export async function forwardToChat(chatReq, emitEvent, onDone, onError, options
268
300
  };
269
301
  const cached = parsed.usage.prompt_tokens_details?.cached_tokens;
270
302
  if (cached) usage.input_tokens_details = { cached_tokens: cached };
303
+ markOutputTokens(usage.output_tokens);
271
304
  }
272
305
  const delta = parsed.choices?.[0]?.delta;
273
306
  if (!delta) continue;
307
+ if (isChatOutputDelta(delta)) markPerformanceOutput();
274
308
  if (delta.content) {
275
309
  const message = await ensureMessageItem();
276
310
  fullText += delta.content;
@@ -281,22 +315,24 @@ export async function forwardToChat(chatReq, emitEvent, onDone, onError, options
281
315
  const { tool, created } = await ensureToolCall(tc);
282
316
  if (tc.id) tool.callId = tc.id;
283
317
  if (!created && tc.function?.name) tool.name += tc.function.name;
284
- if (tc.function?.arguments) {
285
- tool.arguments += tc.function.arguments;
318
+ const argumentDelta = tc.function?.arguments;
319
+ toolArgumentGuard.observe(tool.outputIndex, argumentDelta);
320
+ if (argumentDelta) {
321
+ tool.arguments += argumentDelta;
286
322
  await emitEvent("response.function_call_arguments.delta", {
287
323
  output_index: tool.outputIndex,
288
324
  item_id: tool.id,
289
- delta: tc.function.arguments,
325
+ delta: argumentDelta,
290
326
  });
291
327
  }
292
328
  }
293
329
  }
294
330
  }
295
331
  } catch (e) {
296
- const statusCode = isAbortLikeError(e) ? abortErrorStatusCode(options.abort?.reason) : 502;
297
- await onError(statusCode, e?.message || "upstream stream error");
332
+ const statusCode = isAbortLikeError(e) ? abortErrorStatusCode(options.abort?.reason) : (e?.statusCode || 502);
333
+ await onError(statusCode, e?.message || "upstream stream error", e);
298
334
  return;
299
335
  }
300
- await emitCompleted();
301
- onDone();
336
+ const error = incompleteUpstreamStream("[DONE]");
337
+ await onError(error.statusCode, error.message, error);
302
338
  }
@@ -5,6 +5,7 @@ import {
5
5
  } from "./responses-byte-budget.mjs";
6
6
  import { responsesToChat } from "./responses-bridge.mjs";
7
7
  import { readResponsesImagePart, readResponsesToolOutputParts } from "./responses-content.mjs";
8
+ import { withChatStreamUsage } from "./stream-contract.mjs";
8
9
 
9
10
  const DEFAULT_MAX_UPSTREAM_BODY_BYTES = 30 * 1024 * 1024;
10
11
 
@@ -30,7 +31,7 @@ function chatRequest(responsesBody, stream) {
30
31
  const chatReq = responsesToChat(responsesBody);
31
32
  chatReq.stream = Boolean(stream);
32
33
  delete chatReq.max_tokens;
33
- return chatReq;
34
+ return stream ? withChatStreamUsage(chatReq) : chatReq;
34
35
  }
35
36
 
36
37
  function adjustedResponsesTarget(limit, responsesBytes, chatBytes) {