codex-copilot-dx 0.4.42 → 0.4.43

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,3 +1,5 @@
1
+ import { readResponsesImagePart, readResponsesToolOutputParts } from "./responses-content.mjs";
2
+
1
3
  export const MAX_UPSTREAM_IMAGES = 50;
2
4
 
3
5
  function isImagePart(part) {
@@ -8,13 +10,13 @@ function isImagePart(part) {
8
10
  function imageIdentity(part) {
9
11
  if (!isImagePart(part)) return null;
10
12
 
13
+ const inlineIdentity = readResponsesImagePart(part)?.identity;
14
+ if (inlineIdentity) return { kind: "inline:data", value: inlineIdentity };
15
+
11
16
  if (part.type === "image") {
12
17
  const source = part.source;
13
- if (typeof source?.data === "string") {
14
- return { kind: `image:${source.type || "base64"}:${source.media_type || ""}`, value: source.data };
15
- }
16
18
  if (typeof source?.url === "string") {
17
- return { kind: `image:url:${part.detail || ""}`, value: source.url };
19
+ return { kind: `url:${part.detail || ""}`, value: source.url };
18
20
  }
19
21
  }
20
22
 
@@ -22,10 +24,10 @@ function imageIdentity(part) {
22
24
  const url = typeof raw === "string" ? raw : raw?.url;
23
25
  if (typeof url === "string") {
24
26
  const detail = part.detail ?? raw?.detail ?? "";
25
- return { kind: `${part.type}:url:${detail}`, value: url };
27
+ return { kind: `url:${detail}`, value: url };
26
28
  }
27
29
  if (typeof part.file_id === "string") {
28
- return { kind: `${part.type}:file`, value: part.file_id };
30
+ return { kind: "file", value: part.file_id };
29
31
  }
30
32
  return null;
31
33
  }
@@ -100,26 +102,13 @@ function collectImages(inputItems) {
100
102
  });
101
103
  }
102
104
 
103
- if (item.type !== "function_call_output") continue;
104
- if (Array.isArray(item.output)) {
105
- addPartContainer(records, refs, item.output, {
106
- kind: "tool-output",
107
- itemIndex,
108
- });
109
- continue;
110
- }
111
- if (typeof item.output !== "string" || !item.output.trim().startsWith("[")) continue;
112
- try {
113
- const parsed = JSON.parse(item.output);
114
- if (!Array.isArray(parsed)) continue;
115
- addPartContainer(records, refs, parsed, {
116
- kind: "tool-output",
117
- itemIndex,
118
- commit: () => { item.output = JSON.stringify(parsed); },
119
- });
120
- } catch {
121
- // Preserve non-JSON tool output unchanged.
122
- }
105
+ const toolOutput = readResponsesToolOutputParts(item);
106
+ if (!toolOutput) continue;
107
+ addPartContainer(records, refs, toolOutput.parts, {
108
+ kind: "tool-output",
109
+ itemIndex,
110
+ commit: toolOutput.commit,
111
+ });
123
112
  }
124
113
 
125
114
  if (hasTopLevelImages) records.push(topLevelRecord);
@@ -31,7 +31,24 @@ export function stripInternalResponsesInputFields(inputItems) {
31
31
 
32
32
  function responsesOutputItems(output) {
33
33
  if (!Array.isArray(output)) return [];
34
- return output.filter((item) => item?.type === "message" || item?.type === "function_call");
34
+ return output.filter((item) => ["message", "function_call", "custom_tool_call"].includes(item?.type));
35
+ }
36
+
37
+ function isCompactedResponsesOutput(output) {
38
+ return Array.isArray(output) && output.some((item) => item?.type === "compaction");
39
+ }
40
+
41
+ function isSuccessfulCompactionResponse(response) {
42
+ if (!isCompactedResponsesOutput(response?.output)) return false;
43
+ if (Object.prototype.hasOwnProperty.call(response, "status") && response.status !== "completed") {
44
+ return false;
45
+ }
46
+ return response.object === "response.compaction" || response.status === "completed";
47
+ }
48
+
49
+ function replayableResponsesOutputItems(output) {
50
+ if (!Array.isArray(output)) return [];
51
+ return output.filter((item) => item && typeof item === "object");
35
52
  }
36
53
 
37
54
  function stripEncryptedReasoningValue(value, state) {
@@ -62,16 +79,21 @@ function isEncryptedReasoningInputItem(item) {
62
79
  export function sanitizeEncryptedReasoningRequest(reqContext) {
63
80
  const state = { changed: false };
64
81
  let body = cloneJson(reqContext.body);
82
+ let currentInputStart = reqContext.currentInputStart;
65
83
  if (Array.isArray(body.input)) {
66
84
  const input = [];
67
- for (const item of body.input) {
85
+ let retainedBeforeCurrent = 0;
86
+ for (let index = 0; index < body.input.length; index += 1) {
87
+ const item = body.input[index];
68
88
  if (isEncryptedReasoningInputItem(item)) {
69
89
  state.changed = true;
70
90
  continue;
71
91
  }
72
92
  input.push(stripEncryptedReasoningValue(item, state));
93
+ if (Number.isFinite(currentInputStart) && index < currentInputStart) retainedBeforeCurrent += 1;
73
94
  }
74
95
  body.input = input;
96
+ if (Number.isFinite(currentInputStart)) currentInputStart = retainedBeforeCurrent;
75
97
  } else {
76
98
  body = stripEncryptedReasoningValue(body, state);
77
99
  }
@@ -84,6 +106,7 @@ export function sanitizeEncryptedReasoningRequest(reqContext) {
84
106
  return {
85
107
  ...reqContext,
86
108
  body,
109
+ currentInputStart,
87
110
  inputItems: Array.isArray(body.input) ? body.input : reqContext.inputItems,
88
111
  historyInputItems,
89
112
  };
@@ -129,8 +152,14 @@ export function sanitizeImageNamespaceCollisionRequest(reqContext) {
129
152
  export async function openCopilotResponse(reqContext, upstream = copilotResponses, options = {}) {
130
153
  let encryptedRetried = false;
131
154
  let imageNamespaceRetried = false;
155
+ let payloadPrepared = false;
132
156
  for (let attempt = 0; attempt < 3; attempt += 1) {
133
- const resp = await upstream(reqContext.body, { signal: options.signal });
157
+ const resp = await upstream(reqContext.body, {
158
+ signal: options.signal,
159
+ currentInputStart: reqContext.currentInputStart,
160
+ payloadPrepared,
161
+ });
162
+ payloadPrepared = true;
134
163
  if (resp.ok) return { resp, reqContext };
135
164
 
136
165
  const errorText = await resp.text();
@@ -164,6 +193,9 @@ function isBuiltinImageTool(tool) {
164
193
  export function prepareResponsesRequest(reqBody, { mutate = false } = {}) {
165
194
  const body = mutate ? reqBody : cloneJson(reqBody);
166
195
  const currentInputItems = responsesInputItems(body.input, { clone: !mutate });
196
+ const currentInputRefs = new Set(
197
+ currentInputItems.filter((item) => item && typeof item === "object"),
198
+ );
167
199
  const previousId = body.previous_response_id;
168
200
  let historyItems = [];
169
201
 
@@ -193,10 +225,12 @@ export function prepareResponsesRequest(reqBody, { mutate = false } = {}) {
193
225
  }
194
226
  stripInternalResponsesInputFields(body.input);
195
227
  stripInternalResponsesInputFields(historyInputItems);
228
+ const retainedCurrentInputStart = body.input.findIndex((item) => currentInputRefs.has(item));
196
229
 
197
230
  return {
198
231
  body,
199
232
  inputItems: body.input,
233
+ currentInputStart: retainedCurrentInputStart < 0 ? body.input.length : retainedCurrentInputStart,
200
234
  historyParentId: previousId ?? null,
201
235
  historyInputItems,
202
236
  takeHistoryOwnership: mutate,
@@ -205,11 +239,14 @@ export function prepareResponsesRequest(reqBody, { mutate = false } = {}) {
205
239
 
206
240
  export function rememberResponseHistory(reqContext, responseJson) {
207
241
  if (!responseJson?.id || !Array.isArray(reqContext?.historyInputItems || reqContext?.inputItems)) return;
208
- const sourceInputItems = reqContext.historyInputItems || reqContext.inputItems;
209
- const sourceOutputItems = responsesOutputItems(responseJson.output);
242
+ const compacted = isSuccessfulCompactionResponse(responseJson);
243
+ const sourceInputItems = compacted ? [] : reqContext.historyInputItems || reqContext.inputItems;
244
+ const sourceOutputItems = compacted
245
+ ? replayableResponsesOutputItems(responseJson.output)
246
+ : responsesOutputItems(responseJson.output);
210
247
  rememberResponseHistoryNode({
211
248
  id: responseJson.id,
212
- parentId: reqContext.historyParentId,
249
+ parentId: compacted ? null : reqContext.historyParentId,
213
250
  inputItems: sourceInputItems,
214
251
  outputItems: sourceOutputItems,
215
252
  takeOwnership: reqContext.takeHistoryOwnership,