codex-copilot-dx 0.7.7 → 0.7.9
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 +30 -2
- package/bin/cli.mjs +38 -2
- package/package.json +1 -1
- package/src/adapter.mjs +12 -0
- package/src/cli-image.mjs +230 -0
- package/src/cli-options.mjs +18 -0
- package/src/cli-status.mjs +12 -0
- package/src/config.mjs +88 -6
- package/src/copilot-responses-compat.mjs +28 -18
- package/src/image-mcp.mjs +171 -0
- package/src/image-provider-config.mjs +190 -0
- package/src/image-provider.mjs +268 -0
- package/src/image-readiness.mjs +55 -0
- package/src/image-skill.mjs +143 -0
- package/src/image-tool-client.mjs +141 -0
- package/src/observability.mjs +2 -0
- package/src/response-failures.mjs +100 -0
- package/src/responses-handler.mjs +2 -0
- package/src/responses-proxy.mjs +181 -35
- package/src/responses-request.mjs +23 -0
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { terminalCell } from "./cli-table.mjs";
|
|
2
|
+
|
|
3
|
+
const DEFAULT_RECENT_FAILURES = 10;
|
|
4
|
+
|
|
5
|
+
function safeIdentifier(value, fallback = "unknown") {
|
|
6
|
+
const text = terminalCell(value, { fallback });
|
|
7
|
+
return text.slice(0, 200);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function safeFailureMessage(value) {
|
|
11
|
+
let text = terminalCell(value, { fallback: "Response failed without an error message" });
|
|
12
|
+
text = text
|
|
13
|
+
.replace(/(encrypted(?: function output)? content\s+)[A-Za-z0-9+/_=-]{24,}/gi, "$1[redacted]")
|
|
14
|
+
.replace(/\b[A-Za-z0-9+/_=-]{96,}\b/g, "[redacted]");
|
|
15
|
+
return text.slice(0, 500);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function upstreamRequestId(headers) {
|
|
19
|
+
if (!headers?.get) return null;
|
|
20
|
+
for (const name of ["x-request-id", "x-github-request-id", "x-ms-request-id", "request-id", "x-correlation-id"]) {
|
|
21
|
+
const value = headers.get(name);
|
|
22
|
+
if (value) return safeIdentifier(value, "");
|
|
23
|
+
}
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function responseFailureDetails(event, eventType = event?.type, {
|
|
28
|
+
model,
|
|
29
|
+
headers,
|
|
30
|
+
retried = false,
|
|
31
|
+
retryPolicy = null,
|
|
32
|
+
retrySkipped = null,
|
|
33
|
+
} = {}) {
|
|
34
|
+
const response = event?.response && typeof event.response === "object" ? event.response : event;
|
|
35
|
+
const error = response?.error && typeof response.error === "object"
|
|
36
|
+
? response.error
|
|
37
|
+
: event?.error && typeof event.error === "object"
|
|
38
|
+
? event.error
|
|
39
|
+
: event;
|
|
40
|
+
return {
|
|
41
|
+
at: new Date().toISOString(),
|
|
42
|
+
event_type: safeIdentifier(eventType || "response.failed"),
|
|
43
|
+
model: safeIdentifier(response?.model || model),
|
|
44
|
+
code: safeIdentifier(error?.code || "unknown_error"),
|
|
45
|
+
message: safeFailureMessage(error?.message),
|
|
46
|
+
response_id: response?.id ? safeIdentifier(response.id, "") : null,
|
|
47
|
+
upstream_request_id: upstreamRequestId(headers),
|
|
48
|
+
retried: Boolean(retried),
|
|
49
|
+
retry_policy: retryPolicy ? safeIdentifier(retryPolicy, "") : null,
|
|
50
|
+
retry_skipped: retrySkipped ? safeIdentifier(retrySkipped, "") : null,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function responseFailureText(event, eventType = event?.type) {
|
|
55
|
+
const response = event?.response && typeof event.response === "object" ? event.response : event;
|
|
56
|
+
const error = response?.error && typeof response.error === "object"
|
|
57
|
+
? response.error
|
|
58
|
+
: event?.error && typeof event.error === "object"
|
|
59
|
+
? event.error
|
|
60
|
+
: event;
|
|
61
|
+
return JSON.stringify({ error: { code: error?.code, message: error?.message } });
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function formatResponseFailureLog(details) {
|
|
65
|
+
const fields = [
|
|
66
|
+
`Responses ${details.event_type}`,
|
|
67
|
+
`model=${details.model}`,
|
|
68
|
+
`code=${details.code}`,
|
|
69
|
+
details.response_id ? `response_id=${details.response_id}` : null,
|
|
70
|
+
details.upstream_request_id ? `upstream_request_id=${details.upstream_request_id}` : null,
|
|
71
|
+
details.retried ? `retry=${details.retry_policy || "yes"}` : null,
|
|
72
|
+
details.retry_skipped ? `retry_skipped=${details.retry_skipped}` : null,
|
|
73
|
+
`message=${details.message}`,
|
|
74
|
+
].filter(Boolean);
|
|
75
|
+
return fields.join(" ");
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function createResponseFailureDiagnostics({
|
|
79
|
+
maxEntries = DEFAULT_RECENT_FAILURES,
|
|
80
|
+
now = () => new Date().toISOString(),
|
|
81
|
+
} = {}) {
|
|
82
|
+
const recent = [];
|
|
83
|
+
let total = 0;
|
|
84
|
+
let retried = 0;
|
|
85
|
+
return {
|
|
86
|
+
record(details) {
|
|
87
|
+
total += 1;
|
|
88
|
+
if (details.retried) retried += 1;
|
|
89
|
+
recent.push({ ...details, at: now() });
|
|
90
|
+
if (recent.length > maxEntries) recent.splice(0, recent.length - maxEntries);
|
|
91
|
+
},
|
|
92
|
+
snapshot() {
|
|
93
|
+
return {
|
|
94
|
+
total,
|
|
95
|
+
retried,
|
|
96
|
+
recent: recent.map((entry) => ({ ...entry })),
|
|
97
|
+
};
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
}
|
|
@@ -109,6 +109,7 @@ export function createResponsesHandler(options) {
|
|
|
109
109
|
requestBodyTimeoutMs,
|
|
110
110
|
responsesPayloadOptions,
|
|
111
111
|
responsesFn,
|
|
112
|
+
responseFailures,
|
|
112
113
|
streamHandshakeTimeoutMs,
|
|
113
114
|
streamIdleTimeoutMs,
|
|
114
115
|
upstreamTimeoutMs,
|
|
@@ -255,6 +256,7 @@ export function createResponsesHandler(options) {
|
|
|
255
256
|
abort,
|
|
256
257
|
onUpstreamStart: startUpstreamTimeout,
|
|
257
258
|
releaseRequest: releaseUpstreamPayload,
|
|
259
|
+
responseFailures,
|
|
258
260
|
streamIdleTimeoutMs,
|
|
259
261
|
});
|
|
260
262
|
if (result?.successful) imagePressure?.markSuccess?.(responseHistoryPressureRootId(prepared));
|
package/src/responses-proxy.mjs
CHANGED
|
@@ -8,8 +8,18 @@ import {
|
|
|
8
8
|
sendUpstreamError,
|
|
9
9
|
writeOrDrain,
|
|
10
10
|
} from "./http-transport.mjs";
|
|
11
|
-
import { openCopilotResponse } from "./copilot-responses-compat.mjs";
|
|
12
|
-
import {
|
|
11
|
+
import { openCopilotResponse, selectCopilotResponseRetry } from "./copilot-responses-compat.mjs";
|
|
12
|
+
import {
|
|
13
|
+
dropMaterializedResponseHistory,
|
|
14
|
+
hasEncryptedResponseState,
|
|
15
|
+
rememberResponseHistory,
|
|
16
|
+
restoreMaterializedResponseHistoryForRetry,
|
|
17
|
+
} from "./responses-request.mjs";
|
|
18
|
+
import {
|
|
19
|
+
formatResponseFailureLog,
|
|
20
|
+
responseFailureDetails,
|
|
21
|
+
responseFailureText,
|
|
22
|
+
} from "./response-failures.mjs";
|
|
13
23
|
import {
|
|
14
24
|
compactionInputWithoutTrigger,
|
|
15
25
|
parseResponsesCompactionResult,
|
|
@@ -30,9 +40,11 @@ import {
|
|
|
30
40
|
} from "./stream-performance.mjs";
|
|
31
41
|
import { safeUpstreamResponseHeaders } from "./upstream-headers.mjs";
|
|
32
42
|
import { recordResponsesUsage } from "./usage.mjs";
|
|
43
|
+
import { status } from "./status.mjs";
|
|
33
44
|
|
|
34
45
|
const MAX_SSE_BUFFER_BYTES = loadRuntimeConfig().maxSseBufferBytes;
|
|
35
46
|
const SSE_WRITE_BATCH_BYTES = 64 * 1024;
|
|
47
|
+
const STREAM_RETRY_PRELUDE_MAX_BYTES = 256 * 1024;
|
|
36
48
|
const RESPONSES_TERMINAL_EVENT_TYPES = new Set([
|
|
37
49
|
"response.completed",
|
|
38
50
|
"response.incomplete",
|
|
@@ -124,7 +136,9 @@ function inspectResponseSseEvent(state, eventName, data) {
|
|
|
124
136
|
);
|
|
125
137
|
}
|
|
126
138
|
state.sawTerminal = true;
|
|
127
|
-
if (eventType === "response.failed" || eventType === "error")
|
|
139
|
+
if (eventType === "response.failed" || eventType === "error") {
|
|
140
|
+
state.failure = { event, eventType };
|
|
141
|
+
}
|
|
128
142
|
if (eventType === "response.completed") {
|
|
129
143
|
const response = event.response;
|
|
130
144
|
state.completed = { response, event };
|
|
@@ -292,68 +306,182 @@ function storeCompletedResponse(reqContext, completed) {
|
|
|
292
306
|
});
|
|
293
307
|
}
|
|
294
308
|
|
|
295
|
-
|
|
296
|
-
|
|
309
|
+
function streamHeaders(resp) {
|
|
310
|
+
return {
|
|
311
|
+
...safeUpstreamResponseHeaders(resp.headers, { defaultContentType: "text/event-stream" }),
|
|
312
|
+
"Cache-Control": "no-cache",
|
|
313
|
+
Connection: "keep-alive",
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
function selectStreamFailureRetry(reqContext, failure, usedRetryPolicies) {
|
|
318
|
+
if (!failure) return null;
|
|
297
319
|
try {
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
320
|
+
const restored = restoreMaterializedResponseHistoryForRetry(reqContext);
|
|
321
|
+
return {
|
|
322
|
+
retry: selectCopilotResponseRetry(
|
|
323
|
+
restored,
|
|
324
|
+
400,
|
|
325
|
+
responseFailureText(failure.event, failure.eventType),
|
|
326
|
+
usedRetryPolicies,
|
|
327
|
+
),
|
|
328
|
+
retrySkipped: null,
|
|
329
|
+
};
|
|
330
|
+
} catch {
|
|
331
|
+
return { retry: null, retrySkipped: "history_unavailable" };
|
|
307
332
|
}
|
|
333
|
+
}
|
|
308
334
|
|
|
309
|
-
|
|
335
|
+
function recordResponseFailure(options, reqContext, resp, failure, {
|
|
336
|
+
retry,
|
|
337
|
+
retrySkipped,
|
|
338
|
+
} = {}) {
|
|
339
|
+
if (!failure) return;
|
|
340
|
+
const details = responseFailureDetails(failure.event, failure.eventType, {
|
|
341
|
+
model: reqContext.body?.model,
|
|
342
|
+
headers: resp.headers,
|
|
343
|
+
retried: Boolean(retry),
|
|
344
|
+
retryPolicy: retry?.policyId,
|
|
345
|
+
retrySkipped,
|
|
346
|
+
});
|
|
347
|
+
options.responseFailures?.record?.(details);
|
|
348
|
+
console.warn(status("warn", formatResponseFailureLog(details)));
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
async function proxyStreamingResponses(opened, res, upstream, options) {
|
|
352
|
+
while (true) {
|
|
353
|
+
let {
|
|
354
|
+
resp,
|
|
355
|
+
errorText,
|
|
356
|
+
reqContext,
|
|
357
|
+
streamRetryEligible = false,
|
|
358
|
+
usedRetryPolicies = new Set(),
|
|
359
|
+
} = opened;
|
|
360
|
+
if (errorText !== undefined) {
|
|
361
|
+
sendUpstreamError(res, resp, errorText);
|
|
362
|
+
return { successful: false, compacted: false, upstreamStatus: resp.status };
|
|
363
|
+
}
|
|
310
364
|
await requireUpstreamEventStream(resp);
|
|
311
365
|
options.abort?.setTimeout(options.streamIdleTimeoutMs, "stream_idle_timeout");
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
366
|
+
const headers = streamHeaders(resp);
|
|
367
|
+
let headersSent = false;
|
|
368
|
+
const writeHeaders = () => {
|
|
369
|
+
if (headersSent || res.headersSent) return;
|
|
370
|
+
headersSent = true;
|
|
371
|
+
res.writeHead(resp.status, headers);
|
|
372
|
+
};
|
|
317
373
|
const reader = resp.body.getReader();
|
|
318
374
|
const streamState = {
|
|
319
375
|
completed: null,
|
|
376
|
+
failure: null,
|
|
320
377
|
sawTerminal: false,
|
|
321
378
|
sawOutput: false,
|
|
322
379
|
toolArgumentGuard: new ToolArgumentDeltaGuard(),
|
|
323
380
|
messageIds: new Map(),
|
|
324
381
|
};
|
|
325
382
|
const transformer = createResponseSseTransformer(streamState);
|
|
383
|
+
let holdPrelude = streamRetryEligible;
|
|
384
|
+
let retryDisabledReason = holdPrelude ? null : "request_not_eligible";
|
|
385
|
+
const prelude = [];
|
|
386
|
+
let preludeBytes = 0;
|
|
387
|
+
const flushPrelude = async () => {
|
|
388
|
+
if (!preludeBytes) return true;
|
|
389
|
+
writeHeaders();
|
|
390
|
+
const body = prelude.length === 1 ? prelude[0] : Buffer.concat(prelude, preludeBytes);
|
|
391
|
+
prelude.length = 0;
|
|
392
|
+
preludeBytes = 0;
|
|
393
|
+
return writeOrDrain(res, body);
|
|
394
|
+
};
|
|
395
|
+
const forwardFrame = async (frame) => {
|
|
396
|
+
if (!frame) return true;
|
|
397
|
+
if (!holdPrelude) {
|
|
398
|
+
writeHeaders();
|
|
399
|
+
return writeOrDrain(res, frame);
|
|
400
|
+
}
|
|
401
|
+
prelude.push(frame);
|
|
402
|
+
preludeBytes += frame.byteLength;
|
|
403
|
+
if (!streamState.sawOutput && preludeBytes <= STREAM_RETRY_PRELUDE_MAX_BYTES) return true;
|
|
404
|
+
retryDisabledReason = streamState.sawOutput ? "output_started" : "prelude_limit";
|
|
405
|
+
holdPrelude = false;
|
|
406
|
+
return flushPrelude();
|
|
407
|
+
};
|
|
408
|
+
|
|
326
409
|
try {
|
|
327
410
|
while (true) {
|
|
328
411
|
const { done, value } = await reader.read();
|
|
329
412
|
if (done) {
|
|
330
413
|
if (!streamState.sawTerminal) throw incompleteUpstreamStream("a terminal Responses event");
|
|
331
|
-
|
|
332
|
-
res.end();
|
|
333
|
-
return { successful: Boolean(streamState.completed), compacted: false };
|
|
414
|
+
break;
|
|
334
415
|
}
|
|
335
416
|
options.abort?.setTimeout(options.streamIdleTimeoutMs, "stream_idle_timeout");
|
|
336
|
-
// Batch within this read, not across reads, and await drain before processing more.
|
|
337
417
|
for (let offset = 0; offset < value.byteLength; offset += SSE_WRITE_BATCH_BYTES) {
|
|
338
418
|
const frame = transformer.push(value.subarray(offset, offset + SSE_WRITE_BATCH_BYTES));
|
|
339
|
-
if (
|
|
419
|
+
if (!await forwardFrame(frame)) return { successful: false, compacted: false };
|
|
340
420
|
if (streamState.sawTerminal) break;
|
|
341
421
|
}
|
|
342
|
-
if (streamState.sawTerminal)
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
422
|
+
if (streamState.sawTerminal) break;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
let retry = null;
|
|
426
|
+
if (streamState.failure && holdPrelude && !streamState.sawOutput) {
|
|
427
|
+
const selected = selectStreamFailureRetry(reqContext, streamState.failure, usedRetryPolicies);
|
|
428
|
+
retry = selected?.retry || null;
|
|
429
|
+
if (!retry) retryDisabledReason = selected?.retrySkipped || "not_applicable";
|
|
430
|
+
}
|
|
431
|
+
if (streamState.failure) {
|
|
432
|
+
recordResponseFailure(options, reqContext, resp, streamState.failure, {
|
|
433
|
+
retry,
|
|
434
|
+
retrySkipped: retry ? null : retryDisabledReason,
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
if (retry) {
|
|
438
|
+
console.warn(status("warn", retry.warning));
|
|
439
|
+
opened = await openCopilotResponse(retry.reqContext, upstream, {
|
|
440
|
+
...options,
|
|
441
|
+
usedRetryPolicies: retry.usedPolicies,
|
|
442
|
+
});
|
|
443
|
+
opened.streamRetryEligible = hasEncryptedResponseState(opened.reqContext.body?.input);
|
|
444
|
+
dropMaterializedResponseHistory(opened.reqContext);
|
|
445
|
+
continue;
|
|
347
446
|
}
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
447
|
+
if (streamState.failure) markStreamFailure();
|
|
448
|
+
if (holdPrelude && !await flushPrelude()) return { successful: false, compacted: false };
|
|
449
|
+
storeCompletedResponse(reqContext, streamState.completed);
|
|
450
|
+
if (!res.writableEnded) res.end();
|
|
451
|
+
return { successful: Boolean(streamState.completed), compacted: false };
|
|
452
|
+
} catch (error) {
|
|
453
|
+
logRequestFailure("Responses", error, options.abort);
|
|
454
|
+
writeHeaders();
|
|
455
|
+
await endStreamWithError(res, error, options.abort);
|
|
351
456
|
return { successful: false, compacted: false };
|
|
352
457
|
} finally {
|
|
353
458
|
await reader.cancel().catch(() => {});
|
|
354
459
|
reader.releaseLock();
|
|
355
460
|
}
|
|
356
|
-
}
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
export async function proxyCopilotResponses(reqContext, req, res, upstream = copilotResponses, options = {}) {
|
|
465
|
+
let opened;
|
|
466
|
+
try {
|
|
467
|
+
opened = await openCopilotResponse(reqContext, upstream, options);
|
|
468
|
+
opened.streamRetryEligible = opened.reqContext.body.stream === true
|
|
469
|
+
&& hasEncryptedResponseState(opened.reqContext.body?.input);
|
|
470
|
+
} finally {
|
|
471
|
+
options.releaseRequest?.(opened?.reqContext);
|
|
472
|
+
}
|
|
473
|
+
reqContext = opened.reqContext;
|
|
474
|
+
if (reqContext.body.stream) {
|
|
475
|
+
return proxyStreamingResponses(opened, res, upstream, options);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
while (true) {
|
|
479
|
+
const { resp, errorText, usedRetryPolicies = new Set() } = opened;
|
|
480
|
+
reqContext = opened.reqContext;
|
|
481
|
+
if (errorText !== undefined) {
|
|
482
|
+
sendUpstreamError(res, resp, errorText);
|
|
483
|
+
return { successful: false, compacted: false, upstreamStatus: resp.status };
|
|
484
|
+
}
|
|
357
485
|
const data = resp.ok
|
|
358
486
|
? await readBoundedResponseText(resp, {
|
|
359
487
|
maxBytes: MAX_UPSTREAM_RESPONSES_SUCCESS_BODY_BYTES,
|
|
@@ -385,6 +513,24 @@ export async function proxyCopilotResponses(reqContext, req, res, upstream = cop
|
|
|
385
513
|
}
|
|
386
514
|
if (resp.ok) {
|
|
387
515
|
const response = parseSuccessfulResponsesResult(data);
|
|
516
|
+
if (response.status === "failed") {
|
|
517
|
+
const failure = { event: { type: "response.failed", response }, eventType: "response.failed" };
|
|
518
|
+
const selected = selectStreamFailureRetry(reqContext, failure, usedRetryPolicies);
|
|
519
|
+
const retry = selected?.retry || null;
|
|
520
|
+
recordResponseFailure(options, reqContext, resp, failure, {
|
|
521
|
+
retry,
|
|
522
|
+
retrySkipped: retry ? null : selected?.retrySkipped || "not_applicable",
|
|
523
|
+
});
|
|
524
|
+
if (retry) {
|
|
525
|
+
console.warn(status("warn", retry.warning));
|
|
526
|
+
opened = await openCopilotResponse(retry.reqContext, upstream, {
|
|
527
|
+
...options,
|
|
528
|
+
usedRetryPolicies: retry.usedPolicies,
|
|
529
|
+
});
|
|
530
|
+
dropMaterializedResponseHistory(opened.reqContext);
|
|
531
|
+
continue;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
388
534
|
rememberResponseHistory(reqContext, response);
|
|
389
535
|
recordResponsesUsage({
|
|
390
536
|
surface: reqContext.surface,
|
|
@@ -397,7 +543,7 @@ export async function proxyCopilotResponses(reqContext, req, res, upstream = cop
|
|
|
397
543
|
contentType: "application/json",
|
|
398
544
|
}));
|
|
399
545
|
res.end(data);
|
|
400
|
-
return { successful:
|
|
546
|
+
return { successful: response.status !== "failed" && response.status !== "incomplete", compacted: false };
|
|
401
547
|
}
|
|
402
548
|
res.writeHead(resp.status, safeUpstreamResponseHeaders(resp.headers, {
|
|
403
549
|
contentType: "application/json",
|
|
@@ -84,6 +84,7 @@ function isEncryptedContentPart(value) {
|
|
|
84
84
|
const OMIT_ENCRYPTED_CONTENT = Symbol("omit-encrypted-content");
|
|
85
85
|
const ENCRYPTED_HISTORY_REBASE = Symbol("encrypted-history-rebase");
|
|
86
86
|
const RELEASE_HISTORY_INPUT = Symbol("release-history-input");
|
|
87
|
+
const RELEASED_MATERIALIZED_HISTORY = Symbol("released-materialized-history");
|
|
87
88
|
const HISTORY_PRESSURE_ROOT = Symbol("history-pressure-root");
|
|
88
89
|
const ENCRYPTED_TOOL_OUTPUT_MARKER = "[CCDX: encrypted tool output omitted because upstream could not decrypt it.]";
|
|
89
90
|
|
|
@@ -187,6 +188,10 @@ function responseValueHasOpaqueState(value) {
|
|
|
187
188
|
return Object.values(value).some(responseValueHasOpaqueState);
|
|
188
189
|
}
|
|
189
190
|
|
|
191
|
+
export function hasEncryptedResponseState(value) {
|
|
192
|
+
return responseValueHasOpaqueState(value);
|
|
193
|
+
}
|
|
194
|
+
|
|
190
195
|
export function sanitizeEncryptedReasoningRequest(reqContext, { historicalOnly = false } = {}) {
|
|
191
196
|
const state = { changed: false };
|
|
192
197
|
let body = cloneJson(reqContext.body);
|
|
@@ -379,10 +384,28 @@ export function dropMaterializedResponseHistory(reqContext) {
|
|
|
379
384
|
reqContext.body = { ...reqContext.body, input: releaseInput };
|
|
380
385
|
reqContext.inputItems = releaseInput;
|
|
381
386
|
delete reqContext[RELEASE_HISTORY_INPUT];
|
|
387
|
+
reqContext[RELEASED_MATERIALIZED_HISTORY] = true;
|
|
382
388
|
reqContext.currentInputStart = 0;
|
|
383
389
|
return true;
|
|
384
390
|
}
|
|
385
391
|
|
|
392
|
+
export function restoreMaterializedResponseHistoryForRetry(reqContext, { assertActive } = {}) {
|
|
393
|
+
if (!reqContext?.[RELEASED_MATERIALIZED_HISTORY] || !reqContext.historyParentId) return reqContext;
|
|
394
|
+
const body = cloneJson(reqContext.body);
|
|
395
|
+
body.previous_response_id = reqContext.historyParentId;
|
|
396
|
+
body.input = cloneJson(reqContext.historyInputItems || reqContext.body.input);
|
|
397
|
+
let restored = prepareResponsesRequest(body, {
|
|
398
|
+
assertActive,
|
|
399
|
+
copilotBoundary: false,
|
|
400
|
+
mutate: true,
|
|
401
|
+
});
|
|
402
|
+
restored.surface = reqContext.surface;
|
|
403
|
+
if (reqContext.routePlan) {
|
|
404
|
+
restored = applyResponseHistoryRoutePlan(restored, reqContext.routePlan);
|
|
405
|
+
}
|
|
406
|
+
return restored;
|
|
407
|
+
}
|
|
408
|
+
|
|
386
409
|
export function rememberResponseHistory(reqContext, responseJson) {
|
|
387
410
|
if (!responseJson?.id || !Array.isArray(reqContext?.historyInputItems || reqContext?.inputItems)) return;
|
|
388
411
|
const compacted = isSuccessfulCompactionResponse(responseJson);
|