codex-copilot-dx 0.4.42 → 0.4.44
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 +28 -16
- package/bin/cli.mjs +4 -4
- package/package.json +2 -1
- package/src/adapter.mjs +17 -8
- package/src/anthropic-handler.mjs +13 -5
- package/src/anthropic.mjs +40 -5
- package/src/auth.mjs +2 -2
- package/src/bounded-result-cache.mjs +180 -0
- package/src/claude-config.mjs +1 -1
- package/src/cli-options.mjs +7 -5
- package/src/copilot.mjs +42 -8
- package/src/doctor.mjs +11 -2
- package/src/http-transport.mjs +5 -2
- package/src/image-optimization.mjs +312 -116
- package/src/observability.mjs +2 -1
- package/src/responses-bridge.mjs +76 -20
- package/src/responses-byte-budget.mjs +314 -0
- package/src/responses-chat-payload.mjs +129 -0
- package/src/responses-compaction.mjs +113 -0
- package/src/responses-content.mjs +86 -0
- package/src/responses-handler.mjs +67 -20
- package/src/responses-image-limit.mjs +15 -26
- package/src/responses-proxy.mjs +123 -23
- package/src/responses-request.mjs +43 -6
- package/src/stream-contract.mjs +102 -0
- package/src/stream-errors.mjs +3 -1
- package/src/stream-performance.mjs +177 -0
- package/src/upstream-headers.mjs +67 -0
package/src/copilot.mjs
CHANGED
|
@@ -17,7 +17,10 @@ import {
|
|
|
17
17
|
githubTokenFingerprint,
|
|
18
18
|
normalizeGithubIdentity,
|
|
19
19
|
} from "./github-identity.mjs";
|
|
20
|
-
import { prepareResponsesPayload } from "./image-optimization.mjs";
|
|
20
|
+
import { prepareResponsesPayload, summarizeReqBody } from "./image-optimization.mjs";
|
|
21
|
+
import { enforceResponsesPayloadByteBudget } from "./responses-byte-budget.mjs";
|
|
22
|
+
import { withChatStreamUsage } from "./stream-contract.mjs";
|
|
23
|
+
import { markUpstreamStarted } from "./stream-performance.mjs";
|
|
21
24
|
|
|
22
25
|
export {
|
|
23
26
|
optimizeImageDataUrl,
|
|
@@ -316,7 +319,7 @@ export function copilotRuntimeStatus(now = Date.now()) {
|
|
|
316
319
|
function getGithubToken({ home = os.homedir() } = {}) {
|
|
317
320
|
const GITHUB_TOKEN_PATH = githubTokenPath(home);
|
|
318
321
|
if (!fs.existsSync(GITHUB_TOKEN_PATH)) {
|
|
319
|
-
throw new Error("GitHub token not found. Run
|
|
322
|
+
throw new Error("GitHub token not found. Run ccdx again to log in.");
|
|
320
323
|
}
|
|
321
324
|
const token = fs.readFileSync(GITHUB_TOKEN_PATH, "utf-8").trim();
|
|
322
325
|
if (!token) throw new Error(githubReauthMessage("GitHub token file is empty."));
|
|
@@ -342,7 +345,7 @@ function cacheCopilotTokenData(data, {
|
|
|
342
345
|
const tokenChanged = Boolean(copilotTokenGithubFingerprint && fingerprint !== copilotTokenGithubFingerprint);
|
|
343
346
|
if (!allowAccountSwitch && copilotTokenGithubIdentity && identity
|
|
344
347
|
&& !githubIdentitiesEqual(copilotTokenGithubIdentity, identity)) {
|
|
345
|
-
throw new Error(`Refusing to switch GitHub Copilot account from ${githubIdentityLabel(copilotTokenGithubIdentity)} to ${githubIdentityLabel(identity)} while the adapter is running. Restart
|
|
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.`);
|
|
346
349
|
}
|
|
347
350
|
if (!allowAccountSwitch && copilotTokenGithubIdentity && tokenChanged && !identity) {
|
|
348
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.");
|
|
@@ -500,19 +503,30 @@ export function resetCopilotTokenForTests() {
|
|
|
500
503
|
}
|
|
501
504
|
|
|
502
505
|
// chatReq is already converted by the adapter. The caller parses the raw Response.
|
|
503
|
-
export async function chatCompletions(chatReq, {
|
|
506
|
+
export async function chatCompletions(chatReq, {
|
|
507
|
+
signal,
|
|
508
|
+
fetchImpl,
|
|
509
|
+
retryOptions,
|
|
510
|
+
bodyText,
|
|
511
|
+
} = {}) {
|
|
504
512
|
const token = await getCopilotToken({ signal });
|
|
505
|
-
const
|
|
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);
|
|
506
518
|
const headers = buildHeaders({
|
|
507
519
|
token,
|
|
508
520
|
version: getVSCodeVersion(),
|
|
509
521
|
initiator: computeInitiator(messages),
|
|
510
522
|
vision: computeVision(messages),
|
|
511
523
|
});
|
|
524
|
+
headers["Content-Length"] = String(Buffer.byteLength(serializedBody));
|
|
525
|
+
if (upstreamReq.stream === true) markUpstreamStarted();
|
|
512
526
|
return fetchCopilotUpstream(`${getApiBase()}/chat/completions`, {
|
|
513
527
|
method: "POST",
|
|
514
528
|
headers,
|
|
515
|
-
body:
|
|
529
|
+
body: serializedBody,
|
|
516
530
|
signal,
|
|
517
531
|
}, { fetchImpl, ...retryOptions });
|
|
518
532
|
}
|
|
@@ -582,10 +596,29 @@ export function resetModelListSingleflightForTests() {
|
|
|
582
596
|
}
|
|
583
597
|
|
|
584
598
|
// Responses-only models go directly to Copilot's /responses endpoint.
|
|
585
|
-
export async function responses(reqBody, {
|
|
599
|
+
export async function responses(reqBody, {
|
|
600
|
+
signal,
|
|
601
|
+
fetchImpl,
|
|
602
|
+
retryOptions,
|
|
603
|
+
currentInputStart = 0,
|
|
604
|
+
payloadPrepared = false,
|
|
605
|
+
payloadOptions = {},
|
|
606
|
+
} = {}) {
|
|
586
607
|
const token = await getCopilotToken({ signal });
|
|
587
|
-
const
|
|
608
|
+
const preparedPayload = await prepareResponsesPayload(reqBody, {
|
|
609
|
+
...payloadOptions,
|
|
610
|
+
currentInputStart,
|
|
611
|
+
profiles: payloadPrepared ? [] : payloadOptions.profiles,
|
|
612
|
+
skipInitialOptimization: payloadPrepared || payloadOptions.skipInitialOptimization,
|
|
613
|
+
signal,
|
|
614
|
+
});
|
|
615
|
+
const finalizedPayload = enforceResponsesPayloadByteBudget(reqBody, preparedPayload);
|
|
616
|
+
const { bodyText, bodyBytes, stage, adapted } = finalizedPayload;
|
|
617
|
+
const summary = finalizedPayload.stage === preparedPayload.stage
|
|
618
|
+
? preparedPayload.summary
|
|
619
|
+
: summarizeReqBody(reqBody);
|
|
588
620
|
console.log(status("info", `responses payload bytes=${bodyBytes} input_items=${summary.items} images=${summary.images}`));
|
|
621
|
+
if (adapted) debugLog(`responses payload adapted stage=${stage} bytes=${bodyBytes}/${preparedPayload.targetBytes}`);
|
|
589
622
|
const headers = buildHeaders({
|
|
590
623
|
token,
|
|
591
624
|
version: getVSCodeVersion(),
|
|
@@ -597,6 +630,7 @@ export async function responses(reqBody, { signal, fetchImpl, retryOptions } = {
|
|
|
597
630
|
headers["Accept"] = reqBody.stream ? "text/event-stream" : "application/json";
|
|
598
631
|
|
|
599
632
|
try {
|
|
633
|
+
if (reqBody.stream === true) markUpstreamStarted();
|
|
600
634
|
return await fetchCopilotUpstream(`${getApiBase()}${responsesEndpointPath()}`, {
|
|
601
635
|
method: "POST",
|
|
602
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
|
-
|
|
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(`
|
|
458
|
+
log(`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;
|
package/src/http-transport.mjs
CHANGED
|
@@ -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);
|
|
@@ -291,12 +292,14 @@ export function sendJsonError(res, err, fallbackStatus = 400) {
|
|
|
291
292
|
return;
|
|
292
293
|
}
|
|
293
294
|
res.writeHead(err?.statusCode || fallbackStatus, { "Content-Type": "application/json" });
|
|
294
|
-
res.end(JSON.stringify({ error: err?.message || "Request failed" }));
|
|
295
|
+
res.end(JSON.stringify(err?.jsonBody || { error: err?.message || "Request failed" }));
|
|
295
296
|
}
|
|
296
297
|
|
|
297
298
|
export function sendUpstreamError(res, response, text) {
|
|
298
299
|
if (!res.headersSent) {
|
|
299
|
-
res.writeHead(response.status || 502,
|
|
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
|
}
|