codex-copilot-dx 0.6.10 → 0.6.11
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/responses-request.mjs +113 -18
package/package.json
CHANGED
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
rememberResponseHistoryNode,
|
|
7
7
|
responseHistoryRootId,
|
|
8
8
|
} from "./response-history.mjs";
|
|
9
|
+
import { isResponsesToolOutputItem, readResponsesToolOutputParts } from "./responses-content.mjs";
|
|
9
10
|
import { enforceResponsesImageLimit } from "./responses-image-limit.mjs";
|
|
10
11
|
import { status } from "./status.mjs";
|
|
11
12
|
|
|
@@ -69,27 +70,39 @@ function isEncryptedContentPart(value) {
|
|
|
69
70
|
return value && typeof value === "object" && value.type === "encrypted_content";
|
|
70
71
|
}
|
|
71
72
|
|
|
73
|
+
const OMIT_ENCRYPTED_CONTENT = Symbol("omit-encrypted-content");
|
|
74
|
+
const ENCRYPTED_HISTORY_REBASE = Symbol("encrypted-history-rebase");
|
|
75
|
+
const ENCRYPTED_TOOL_OUTPUT_MARKER = "[CCDX: encrypted tool output omitted because upstream could not decrypt it.]";
|
|
76
|
+
|
|
72
77
|
function stripEncryptedReasoningValue(value, state) {
|
|
78
|
+
if (isEncryptedContentPart(value)) {
|
|
79
|
+
state.changed = true;
|
|
80
|
+
return OMIT_ENCRYPTED_CONTENT;
|
|
81
|
+
}
|
|
82
|
+
|
|
73
83
|
if (Array.isArray(value)) {
|
|
74
84
|
const out = [];
|
|
75
85
|
for (const item of value) {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
continue;
|
|
79
|
-
}
|
|
80
|
-
out.push(stripEncryptedReasoningValue(item, state));
|
|
86
|
+
const stripped = stripEncryptedReasoningValue(item, state);
|
|
87
|
+
if (stripped !== OMIT_ENCRYPTED_CONTENT) out.push(stripped);
|
|
81
88
|
}
|
|
82
89
|
return out;
|
|
83
90
|
}
|
|
84
91
|
|
|
85
92
|
if (value && typeof value === "object") {
|
|
93
|
+
const keys = Object.keys(value);
|
|
94
|
+
if (keys.length === 1 && keys[0] === "encrypted_content") {
|
|
95
|
+
state.changed = true;
|
|
96
|
+
return OMIT_ENCRYPTED_CONTENT;
|
|
97
|
+
}
|
|
86
98
|
const out = {};
|
|
87
99
|
for (const [key, child] of Object.entries(value)) {
|
|
88
100
|
if (key === "encrypted_content") {
|
|
89
101
|
state.changed = true;
|
|
90
102
|
continue;
|
|
91
103
|
}
|
|
92
|
-
|
|
104
|
+
const stripped = stripEncryptedReasoningValue(child, state);
|
|
105
|
+
if (stripped !== OMIT_ENCRYPTED_CONTENT) out[key] = stripped;
|
|
93
106
|
}
|
|
94
107
|
return out;
|
|
95
108
|
}
|
|
@@ -97,28 +110,82 @@ function stripEncryptedReasoningValue(value, state) {
|
|
|
97
110
|
return value;
|
|
98
111
|
}
|
|
99
112
|
|
|
113
|
+
function stripEncryptedReasoningInputValue(item, state) {
|
|
114
|
+
if (!isResponsesToolOutputItem(item)) {
|
|
115
|
+
const out = stripEncryptedReasoningValue(item, state);
|
|
116
|
+
if (item?.type === "message"
|
|
117
|
+
&& Array.isArray(item.content)
|
|
118
|
+
&& item.content.length > 0
|
|
119
|
+
&& Array.isArray(out?.content)
|
|
120
|
+
&& out.content.length === 0) {
|
|
121
|
+
return OMIT_ENCRYPTED_CONTENT;
|
|
122
|
+
}
|
|
123
|
+
return out;
|
|
124
|
+
}
|
|
125
|
+
const hasOutput = Object.prototype.hasOwnProperty.call(item, "output");
|
|
126
|
+
const withoutOutput = { ...item };
|
|
127
|
+
delete withoutOutput.output;
|
|
128
|
+
const out = stripEncryptedReasoningValue(withoutOutput, state);
|
|
129
|
+
if (!hasOutput) return out;
|
|
130
|
+
if (isEncryptedContentPart(item.output)) {
|
|
131
|
+
state.changed = true;
|
|
132
|
+
out.output = ENCRYPTED_TOOL_OUTPUT_MARKER;
|
|
133
|
+
return out;
|
|
134
|
+
}
|
|
135
|
+
out.output = typeof item.output === "string" ? item.output : cloneJson(item.output);
|
|
136
|
+
if (typeof item.output === "string" && !item.output.includes("encrypted_content")) return out;
|
|
137
|
+
const parsed = readResponsesToolOutputParts(out);
|
|
138
|
+
if (parsed) {
|
|
139
|
+
const outputState = { changed: false };
|
|
140
|
+
const parts = stripEncryptedReasoningValue(parsed.parts, outputState);
|
|
141
|
+
if (!outputState.changed) return out;
|
|
142
|
+
state.changed = true;
|
|
143
|
+
if (!parts.length) {
|
|
144
|
+
out.output = ENCRYPTED_TOOL_OUTPUT_MARKER;
|
|
145
|
+
return out;
|
|
146
|
+
}
|
|
147
|
+
parsed.parts.splice(0, parsed.parts.length, ...parts);
|
|
148
|
+
parsed.commit?.();
|
|
149
|
+
return out;
|
|
150
|
+
}
|
|
151
|
+
const output = stripEncryptedReasoningValue(out.output, state);
|
|
152
|
+
out.output = output === OMIT_ENCRYPTED_CONTENT ? ENCRYPTED_TOOL_OUTPUT_MARKER : output;
|
|
153
|
+
return out;
|
|
154
|
+
}
|
|
155
|
+
|
|
100
156
|
function isEncryptedReasoningInputItem(item) {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
157
|
+
if (!item || typeof item !== "object") return false;
|
|
158
|
+
if (["reasoning", "encrypted_content", "compaction"].includes(item.type)) return true;
|
|
159
|
+
const keys = Object.keys(item);
|
|
160
|
+
return keys.length === 1 && keys[0] === "encrypted_content";
|
|
105
161
|
}
|
|
106
162
|
|
|
107
163
|
export function sanitizeEncryptedReasoningRequest(reqContext) {
|
|
108
164
|
const state = { changed: false };
|
|
109
165
|
let body = cloneJson(reqContext.body);
|
|
110
166
|
let currentInputStart = reqContext.currentInputStart;
|
|
167
|
+
let historicalInputChanged = false;
|
|
111
168
|
if (Array.isArray(body.input)) {
|
|
112
169
|
const input = [];
|
|
113
170
|
let retainedBeforeCurrent = 0;
|
|
114
171
|
for (let index = 0; index < body.input.length; index += 1) {
|
|
115
172
|
const item = body.input[index];
|
|
173
|
+
const historical = Number.isFinite(currentInputStart) && index < currentInputStart;
|
|
116
174
|
if (isEncryptedReasoningInputItem(item)) {
|
|
117
175
|
state.changed = true;
|
|
176
|
+
if (historical) historicalInputChanged = true;
|
|
118
177
|
continue;
|
|
119
178
|
}
|
|
120
|
-
|
|
121
|
-
|
|
179
|
+
const itemState = { changed: false };
|
|
180
|
+
const stripped = stripEncryptedReasoningInputValue(item, itemState);
|
|
181
|
+
if (stripped !== OMIT_ENCRYPTED_CONTENT) input.push(stripped);
|
|
182
|
+
if (itemState.changed) {
|
|
183
|
+
state.changed = true;
|
|
184
|
+
if (historical) historicalInputChanged = true;
|
|
185
|
+
}
|
|
186
|
+
if (stripped !== OMIT_ENCRYPTED_CONTENT
|
|
187
|
+
&& Number.isFinite(currentInputStart)
|
|
188
|
+
&& index < currentInputStart) retainedBeforeCurrent += 1;
|
|
122
189
|
}
|
|
123
190
|
body.input = input;
|
|
124
191
|
if (Number.isFinite(currentInputStart)) currentInputStart = retainedBeforeCurrent;
|
|
@@ -127,12 +194,19 @@ export function sanitizeEncryptedReasoningRequest(reqContext) {
|
|
|
127
194
|
}
|
|
128
195
|
if (!state.changed) return null;
|
|
129
196
|
const historyInputItems = Array.isArray(reqContext.historyInputItems)
|
|
130
|
-
? reqContext.historyInputItems
|
|
131
|
-
|
|
132
|
-
|
|
197
|
+
? reqContext.historyInputItems.flatMap((item) => {
|
|
198
|
+
if (isEncryptedReasoningInputItem(item)) return [];
|
|
199
|
+
const stripped = stripEncryptedReasoningInputValue(item, { changed: false });
|
|
200
|
+
return stripped === OMIT_ENCRYPTED_CONTENT ? [] : [stripped];
|
|
201
|
+
})
|
|
133
202
|
: reqContext.historyInputItems;
|
|
203
|
+
const shouldRebaseHistory = historicalInputChanged
|
|
204
|
+
&& Number.isFinite(reqContext.currentInputStart)
|
|
205
|
+
&& reqContext.historyParentId !== undefined
|
|
206
|
+
&& reqContext.historyParentId !== null;
|
|
134
207
|
return {
|
|
135
208
|
...reqContext,
|
|
209
|
+
...(shouldRebaseHistory ? { [ENCRYPTED_HISTORY_REBASE]: true } : {}),
|
|
136
210
|
body,
|
|
137
211
|
currentInputStart,
|
|
138
212
|
inputItems: Array.isArray(body.input) ? body.input : reqContext.inputItems,
|
|
@@ -140,12 +214,30 @@ export function sanitizeEncryptedReasoningRequest(reqContext) {
|
|
|
140
214
|
};
|
|
141
215
|
}
|
|
142
216
|
|
|
217
|
+
function finalizeEncryptedHistoryRebase(reqContext) {
|
|
218
|
+
if (!reqContext[ENCRYPTED_HISTORY_REBASE]) return reqContext;
|
|
219
|
+
const finalized = {
|
|
220
|
+
...reqContext,
|
|
221
|
+
historyParentId: null,
|
|
222
|
+
historyRootId: null,
|
|
223
|
+
historyInputItems: reqContext.body.input,
|
|
224
|
+
inputItems: reqContext.body.input,
|
|
225
|
+
};
|
|
226
|
+
delete finalized[ENCRYPTED_HISTORY_REBASE];
|
|
227
|
+
return finalized;
|
|
228
|
+
}
|
|
229
|
+
|
|
143
230
|
export function isEncryptedContentVerificationError(statusCode, text) {
|
|
144
231
|
if (statusCode < 400 || !text) return false;
|
|
145
232
|
const lower = String(text).toLowerCase();
|
|
146
|
-
|
|
233
|
+
const reasoningFailure = lower.includes("encrypted content")
|
|
147
234
|
&& lower.includes("could not be verified")
|
|
148
235
|
&& (lower.includes("could not be decrypted") || lower.includes("could not be parsed"));
|
|
236
|
+
const functionOutputFailure = lower.includes("encrypted function output content")
|
|
237
|
+
&& lower.includes("could not be decrypted or decoded");
|
|
238
|
+
const missingEncryptedContent = statusCode < 500
|
|
239
|
+
&& /missing required parameter:\s*(['"]?)input\[\d+\](?:\.[a-z0-9_]+|\[\d+\])*\.encrypted_content\1(?=\.?(?:\s|$|["},\]]))/.test(lower);
|
|
240
|
+
return reasoningFailure || functionOutputFailure || missingEncryptedContent;
|
|
149
241
|
}
|
|
150
242
|
|
|
151
243
|
export function isImageNamespaceCollisionError(statusCode, text) {
|
|
@@ -192,7 +284,10 @@ export async function openCopilotResponse(reqContext, upstream = copilotResponse
|
|
|
192
284
|
});
|
|
193
285
|
options.assertPrepareActive?.();
|
|
194
286
|
payloadPrepared = true;
|
|
195
|
-
if (resp.ok)
|
|
287
|
+
if (resp.ok) {
|
|
288
|
+
reqContext = finalizeEncryptedHistoryRebase(reqContext);
|
|
289
|
+
return { resp, reqContext };
|
|
290
|
+
}
|
|
196
291
|
|
|
197
292
|
const errorText = await resp.text();
|
|
198
293
|
options.assertPrepareActive?.();
|
|
@@ -210,7 +305,7 @@ export async function openCopilotResponse(reqContext, upstream = copilotResponse
|
|
|
210
305
|
if (retryContext) {
|
|
211
306
|
encryptedRetried = true;
|
|
212
307
|
reqContext = retryContext;
|
|
213
|
-
console.warn(status("warn", "encrypted
|
|
308
|
+
console.warn(status("warn", "encrypted replay content rejected by upstream; retrying without unavailable encrypted content"));
|
|
214
309
|
continue;
|
|
215
310
|
}
|
|
216
311
|
}
|