codex-copilot-dx 0.4.5 → 0.4.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/package.json +1 -1
- package/src/adapter.mjs +89 -1
package/package.json
CHANGED
package/src/adapter.mjs
CHANGED
|
@@ -125,6 +125,13 @@ function sendJsonError(res, err, fallbackStatus = 400) {
|
|
|
125
125
|
res.end(JSON.stringify({ error: err?.message || "Request failed" }));
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
+
function sendUpstreamError(res, resp, text) {
|
|
129
|
+
if (!res.headersSent) {
|
|
130
|
+
res.writeHead(resp.status || 502, { "Content-Type": resp.headers?.get("content-type") || "application/json" });
|
|
131
|
+
}
|
|
132
|
+
res.end(text || JSON.stringify({ error: "Upstream request failed" }));
|
|
133
|
+
}
|
|
134
|
+
|
|
128
135
|
export function writeOrDrain(res, chunk) {
|
|
129
136
|
if (res.destroyed || res.writableEnded) return Promise.resolve(false);
|
|
130
137
|
if (res.write(chunk)) return Promise.resolve(true);
|
|
@@ -180,6 +187,81 @@ function responsesOutputItems(output) {
|
|
|
180
187
|
return cloneJson(output.filter((item) => item?.type === "message" || item?.type === "function_call"));
|
|
181
188
|
}
|
|
182
189
|
|
|
190
|
+
function stripEncryptedReasoningValue(value, state) {
|
|
191
|
+
if (Array.isArray(value)) {
|
|
192
|
+
return value.map((item) => stripEncryptedReasoningValue(item, state));
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (value && typeof value === "object") {
|
|
196
|
+
const out = {};
|
|
197
|
+
for (const [key, child] of Object.entries(value)) {
|
|
198
|
+
if (key === "encrypted_content") {
|
|
199
|
+
state.changed = true;
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
out[key] = stripEncryptedReasoningValue(child, state);
|
|
203
|
+
}
|
|
204
|
+
return out;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return value;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function isEncryptedReasoningInputItem(item) {
|
|
211
|
+
return item && typeof item === "object"
|
|
212
|
+
&& (item.type === "reasoning" || Object.prototype.hasOwnProperty.call(item, "encrypted_content"));
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export function sanitizeEncryptedReasoningRequest(reqContext) {
|
|
216
|
+
const state = { changed: false };
|
|
217
|
+
let body = cloneJson(reqContext.body);
|
|
218
|
+
if (Array.isArray(body.input)) {
|
|
219
|
+
const input = [];
|
|
220
|
+
for (const item of body.input) {
|
|
221
|
+
if (isEncryptedReasoningInputItem(item)) {
|
|
222
|
+
state.changed = true;
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
input.push(stripEncryptedReasoningValue(item, state));
|
|
226
|
+
}
|
|
227
|
+
body.input = input;
|
|
228
|
+
} else {
|
|
229
|
+
body = stripEncryptedReasoningValue(body, state);
|
|
230
|
+
}
|
|
231
|
+
if (!state.changed) return null;
|
|
232
|
+
return {
|
|
233
|
+
...reqContext,
|
|
234
|
+
body,
|
|
235
|
+
inputItems: Array.isArray(body.input) ? body.input : reqContext.inputItems,
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export function isEncryptedContentVerificationError(statusCode, text) {
|
|
240
|
+
if (statusCode < 400 || !text) return false;
|
|
241
|
+
const lower = String(text).toLowerCase();
|
|
242
|
+
return lower.includes("encrypted content")
|
|
243
|
+
&& lower.includes("could not be verified")
|
|
244
|
+
&& (lower.includes("could not be decrypted") || lower.includes("could not be parsed"));
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
export async function openCopilotResponse(reqContext, upstream = copilotResponses, options = {}) {
|
|
248
|
+
let resp = await upstream(reqContext.body, { signal: options.signal });
|
|
249
|
+
if (resp.ok) return { resp, reqContext };
|
|
250
|
+
|
|
251
|
+
const errorText = await resp.text();
|
|
252
|
+
if (!isEncryptedContentVerificationError(resp.status, errorText)) {
|
|
253
|
+
return { resp, reqContext, errorText };
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const retryContext = sanitizeEncryptedReasoningRequest(reqContext);
|
|
257
|
+
if (!retryContext) return { resp, reqContext, errorText };
|
|
258
|
+
|
|
259
|
+
console.warn(status("warn", "encrypted reasoning rejected by upstream; retrying without encrypted reasoning"));
|
|
260
|
+
resp = await upstream(retryContext.body, { signal: options.signal });
|
|
261
|
+
if (resp.ok) return { resp, reqContext: retryContext };
|
|
262
|
+
return { resp, reqContext: retryContext, errorText: await resp.text() };
|
|
263
|
+
}
|
|
264
|
+
|
|
183
265
|
export function clearResponseHistoryForTests() {
|
|
184
266
|
responseHistories.clear();
|
|
185
267
|
}
|
|
@@ -256,7 +338,13 @@ function readSseEvents(buffer, onData) {
|
|
|
256
338
|
}
|
|
257
339
|
|
|
258
340
|
async function proxyCopilotResponses(reqContext, req, res, upstream = copilotResponses, options = {}) {
|
|
259
|
-
const
|
|
341
|
+
const opened = await openCopilotResponse(reqContext, upstream, options);
|
|
342
|
+
const { resp, errorText } = opened;
|
|
343
|
+
reqContext = opened.reqContext;
|
|
344
|
+
if (errorText !== undefined) {
|
|
345
|
+
sendUpstreamError(res, resp, errorText);
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
260
348
|
|
|
261
349
|
if (reqContext.body.stream) {
|
|
262
350
|
res.writeHead(resp.status, {
|