@narumitw/pi-codex-compact 0.51.3 → 0.53.0
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 +90 -90
- package/dist/chunks/chunk-7H7JJ7ZV.js +37 -0
- package/dist/chunks/chunk-7H7JJ7ZV.js.map +7 -0
- package/dist/chunks/{settings-menu-PEDLURUH.js → settings-menu-TVWWDPBF.js} +52 -35
- package/dist/chunks/settings-menu-TVWWDPBF.js.map +7 -0
- package/dist/index.ts +541 -101
- package/dist/index.ts.map +4 -4
- package/package.json +53 -57
- package/src/checkpoint.ts +265 -218
- package/src/codex-compact.ts +252 -229
- package/src/model-api.ts +56 -5
- package/src/protocol.ts +318 -179
- package/src/remote-compact.ts +276 -0
- package/src/remote-shared.ts +16 -0
- package/src/remote-types.ts +46 -0
- package/src/remote-v2.ts +66 -0
- package/src/remote.ts +14 -116
- package/src/settings-menu.ts +213 -201
- package/src/settings.ts +228 -176
- package/src/terminal.ts +6 -0
- package/dist/chunks/chunk-6TZ2L2BM.js +0 -13
- package/dist/chunks/chunk-6TZ2L2BM.js.map +0 -7
- package/dist/chunks/settings-menu-PEDLURUH.js.map +0 -7
package/src/protocol.ts
CHANGED
|
@@ -1,224 +1,363 @@
|
|
|
1
1
|
export const MAX_SSE_BYTES = 8 * 1024 * 1024;
|
|
2
|
+
export const MAX_COMPACT_JSON_BYTES = 8 * 1024 * 1024;
|
|
2
3
|
export const MAX_COMPACTION_ITEM_BYTES = 2 * 1024 * 1024;
|
|
3
4
|
|
|
4
5
|
export type JsonObject = Record<string, unknown>;
|
|
5
6
|
|
|
6
7
|
export class CodexCompactionProtocolError extends Error {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
constructor(message: string) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.name = "CodexCompactionProtocolError";
|
|
11
|
+
}
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
function isObject(value: unknown): value is JsonObject {
|
|
14
|
-
|
|
15
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
function byteLength(value: unknown): number {
|
|
18
|
-
|
|
19
|
+
return Buffer.byteLength(JSON.stringify(value), "utf8");
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
export function isCompactionItem(value: unknown): value is JsonObject {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
return (
|
|
24
|
+
isObject(value) &&
|
|
25
|
+
value.type === "compaction" &&
|
|
26
|
+
typeof value.encrypted_content === "string" &&
|
|
27
|
+
value.encrypted_content.length > 0
|
|
28
|
+
);
|
|
28
29
|
}
|
|
29
30
|
|
|
30
|
-
export function validateCompactionItem(
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
}
|
|
39
|
-
if (byteLength(value) > maxBytes) {
|
|
40
|
-
throw new CodexCompactionProtocolError("Remote compaction item exceeded the size limit");
|
|
41
|
-
}
|
|
42
|
-
return structuredClone(value);
|
|
31
|
+
export function validateCompactionItem(value: unknown, maxBytes = MAX_COMPACTION_ITEM_BYTES): JsonObject {
|
|
32
|
+
if (!isCompactionItem(value)) {
|
|
33
|
+
throw new CodexCompactionProtocolError("Remote response did not contain a valid compaction item");
|
|
34
|
+
}
|
|
35
|
+
if (byteLength(value) > maxBytes) {
|
|
36
|
+
throw new CodexCompactionProtocolError("Remote compaction item exceeded the size limit");
|
|
37
|
+
}
|
|
38
|
+
return structuredClone(value);
|
|
43
39
|
}
|
|
44
40
|
|
|
45
41
|
export interface CollectedCompaction {
|
|
46
|
-
|
|
47
|
-
|
|
42
|
+
item: JsonObject;
|
|
43
|
+
completedResponse?: JsonObject;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface CollectedCompactResponse {
|
|
47
|
+
item: JsonObject;
|
|
48
|
+
output: JsonObject[];
|
|
49
|
+
response: JsonObject;
|
|
48
50
|
}
|
|
49
51
|
|
|
50
52
|
function compactionItemsFromEvent(event: JsonObject): unknown[] {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
53
|
+
const items: unknown[] = [];
|
|
54
|
+
if (event.type === "response.output_item.done" && isObject(event.item)) {
|
|
55
|
+
items.push(event.item);
|
|
56
|
+
}
|
|
57
|
+
if (event.type === "response.completed" && isObject(event.response)) {
|
|
58
|
+
const output = event.response.output;
|
|
59
|
+
if (Array.isArray(output)) items.push(...output);
|
|
60
|
+
}
|
|
61
|
+
return items.filter((item) => isObject(item) && item.type === "compaction");
|
|
60
62
|
}
|
|
61
63
|
|
|
62
64
|
export async function collectCompactionSse(
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
65
|
+
stream: ReadableStream<Uint8Array>,
|
|
66
|
+
options: {
|
|
67
|
+
signal?: AbortSignal;
|
|
68
|
+
maxBytes?: number;
|
|
69
|
+
maxItemBytes?: number;
|
|
70
|
+
} = {},
|
|
69
71
|
): Promise<CollectedCompaction> {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
72
|
+
const maxBytes = options.maxBytes ?? MAX_SSE_BYTES;
|
|
73
|
+
const reader = stream.getReader();
|
|
74
|
+
const onAbort = () => {
|
|
75
|
+
void reader.cancel(new DOMException("Compaction aborted", "AbortError")).catch(() => undefined);
|
|
76
|
+
};
|
|
77
|
+
options.signal?.addEventListener("abort", onAbort, { once: true });
|
|
78
|
+
const decoder = new TextDecoder();
|
|
79
|
+
let bytes = 0;
|
|
80
|
+
let pending = "";
|
|
81
|
+
let dataLines: string[] = [];
|
|
82
|
+
let completedResponse: JsonObject | undefined;
|
|
83
|
+
const items = new Map<string, JsonObject>();
|
|
84
|
+
|
|
85
|
+
const checkAbort = () => {
|
|
86
|
+
if (options.signal?.aborted) throw new DOMException("Compaction aborted", "AbortError");
|
|
87
|
+
};
|
|
88
|
+
const dispatch = () => {
|
|
89
|
+
if (dataLines.length === 0) return;
|
|
90
|
+
const data = dataLines.join("\n");
|
|
91
|
+
dataLines = [];
|
|
92
|
+
if (data === "[DONE]") return;
|
|
93
|
+
let parsed: unknown;
|
|
94
|
+
try {
|
|
95
|
+
parsed = JSON.parse(data);
|
|
96
|
+
} catch {
|
|
97
|
+
throw new CodexCompactionProtocolError("Remote compaction returned malformed SSE JSON");
|
|
98
|
+
}
|
|
99
|
+
if (!isObject(parsed)) return;
|
|
100
|
+
if (parsed.type === "response.completed") {
|
|
101
|
+
completedResponse = isObject(parsed.response) ? parsed.response : {};
|
|
102
|
+
}
|
|
103
|
+
for (const candidate of compactionItemsFromEvent(parsed)) {
|
|
104
|
+
const item = validateCompactionItem(candidate, options.maxItemBytes);
|
|
105
|
+
items.set(JSON.stringify(item), item);
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
const processLine = (line: string) => {
|
|
109
|
+
if (line === "") {
|
|
110
|
+
dispatch();
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
if (line.startsWith(":")) return;
|
|
114
|
+
if (line === "data") dataLines.push("");
|
|
115
|
+
else if (line.startsWith("data:")) dataLines.push(line.slice(5).replace(/^ /, ""));
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
try {
|
|
119
|
+
while (true) {
|
|
120
|
+
checkAbort();
|
|
121
|
+
const { done, value } = await reader.read();
|
|
122
|
+
if (done) break;
|
|
123
|
+
bytes += value.byteLength;
|
|
124
|
+
if (bytes > maxBytes) {
|
|
125
|
+
throw new CodexCompactionProtocolError("Remote compaction stream exceeded the size limit");
|
|
126
|
+
}
|
|
127
|
+
pending += decoder.decode(value, { stream: true });
|
|
128
|
+
let newline = pending.indexOf("\n");
|
|
129
|
+
while (newline !== -1) {
|
|
130
|
+
const rawLine = pending.slice(0, newline);
|
|
131
|
+
pending = pending.slice(newline + 1);
|
|
132
|
+
processLine(rawLine.endsWith("\r") ? rawLine.slice(0, -1) : rawLine);
|
|
133
|
+
newline = pending.indexOf("\n");
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
pending += decoder.decode();
|
|
137
|
+
if (pending.length > 0) processLine(pending.endsWith("\r") ? pending.slice(0, -1) : pending);
|
|
138
|
+
dispatch();
|
|
139
|
+
checkAbort();
|
|
140
|
+
} catch (error) {
|
|
141
|
+
await reader.cancel(error).catch(() => undefined);
|
|
142
|
+
throw error;
|
|
143
|
+
} finally {
|
|
144
|
+
options.signal?.removeEventListener("abort", onAbort);
|
|
145
|
+
reader.releaseLock();
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (!completedResponse) {
|
|
149
|
+
throw new CodexCompactionProtocolError("Remote compaction stream ended without response.completed");
|
|
150
|
+
}
|
|
151
|
+
if (items.size !== 1) {
|
|
152
|
+
throw new CodexCompactionProtocolError(
|
|
153
|
+
`Remote compaction returned ${items.size} distinct compaction items; expected exactly one`,
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
return { item: [...items.values()][0], completedResponse };
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function isRetainedCompactContent(value: unknown): value is JsonObject {
|
|
160
|
+
if (!isObject(value)) return false;
|
|
161
|
+
if (value.type === "input_text") return typeof value.text === "string";
|
|
162
|
+
if (value.type !== "input_image") return false;
|
|
163
|
+
if (
|
|
164
|
+
value.detail !== undefined &&
|
|
165
|
+
value.detail !== null &&
|
|
166
|
+
value.detail !== "auto" &&
|
|
167
|
+
value.detail !== "low" &&
|
|
168
|
+
value.detail !== "high" &&
|
|
169
|
+
value.detail !== "original"
|
|
170
|
+
) {
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
if (
|
|
174
|
+
(value.file_id !== undefined && value.file_id !== null && typeof value.file_id !== "string") ||
|
|
175
|
+
(value.image_url !== undefined && value.image_url !== null && typeof value.image_url !== "string")
|
|
176
|
+
) {
|
|
177
|
+
return false;
|
|
178
|
+
}
|
|
179
|
+
return (
|
|
180
|
+
(typeof value.file_id === "string" && value.file_id.length > 0) ||
|
|
181
|
+
(typeof value.image_url === "string" && value.image_url.length > 0)
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function isRetainedCompactMessage(value: unknown): value is JsonObject {
|
|
186
|
+
return (
|
|
187
|
+
isObject(value) &&
|
|
188
|
+
value.role === "user" &&
|
|
189
|
+
(value.type === undefined || value.type === "message") &&
|
|
190
|
+
Array.isArray(value.content) &&
|
|
191
|
+
value.content.length > 0 &&
|
|
192
|
+
value.content.every(isRetainedCompactContent)
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export function validateCompactedResponse(
|
|
197
|
+
value: unknown,
|
|
198
|
+
options: { maxBytes?: number; maxItemBytes?: number } = {},
|
|
199
|
+
): CollectedCompactResponse {
|
|
200
|
+
if (!isObject(value) || !Array.isArray(value.output)) {
|
|
201
|
+
throw new CodexCompactionProtocolError("Responses Compact returned an invalid response object");
|
|
202
|
+
}
|
|
203
|
+
const maxBytes = options.maxBytes ?? MAX_COMPACT_JSON_BYTES;
|
|
204
|
+
const maxItemBytes = options.maxItemBytes ?? MAX_COMPACTION_ITEM_BYTES;
|
|
205
|
+
if (byteLength(value) > maxBytes) {
|
|
206
|
+
throw new CodexCompactionProtocolError("Responses Compact response exceeded the size limit");
|
|
207
|
+
}
|
|
208
|
+
if (value.output.length === 0) {
|
|
209
|
+
throw new CodexCompactionProtocolError("Responses Compact returned no output items");
|
|
210
|
+
}
|
|
211
|
+
const output = value.output.map((item) => {
|
|
212
|
+
if (!isObject(item)) {
|
|
213
|
+
throw new CodexCompactionProtocolError("Responses Compact returned a non-object output item");
|
|
214
|
+
}
|
|
215
|
+
if (byteLength(item) > maxItemBytes) {
|
|
216
|
+
throw new CodexCompactionProtocolError("Responses Compact output item exceeded the size limit");
|
|
217
|
+
}
|
|
218
|
+
return structuredClone(item);
|
|
219
|
+
});
|
|
220
|
+
const compactionItems = output.filter((item) => item.type === "compaction");
|
|
221
|
+
if (compactionItems.length !== 1 || output.at(-1)?.type !== "compaction") {
|
|
222
|
+
throw new CodexCompactionProtocolError(
|
|
223
|
+
"Responses Compact must return retained messages followed by one compaction item",
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
for (const item of output.slice(0, -1)) {
|
|
227
|
+
if (!isRetainedCompactMessage(item)) {
|
|
228
|
+
throw new CodexCompactionProtocolError("Responses Compact returned an unsupported retained output item");
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
const item = validateCompactionItem(output.at(-1), maxItemBytes);
|
|
232
|
+
return { item, output: [...output.slice(0, -1), item], response: structuredClone(value) };
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export async function collectCompactResponse(
|
|
236
|
+
response: Response,
|
|
237
|
+
options: { signal?: AbortSignal; maxBytes?: number; maxItemBytes?: number } = {},
|
|
238
|
+
): Promise<CollectedCompactResponse> {
|
|
239
|
+
const maxBytes = options.maxBytes ?? MAX_COMPACT_JSON_BYTES;
|
|
240
|
+
const declaredLength = Number(response.headers.get("content-length"));
|
|
241
|
+
if (Number.isFinite(declaredLength) && declaredLength > maxBytes) {
|
|
242
|
+
const error = new CodexCompactionProtocolError("Responses Compact response exceeded the size limit");
|
|
243
|
+
await response.body?.cancel(error).catch(() => undefined);
|
|
244
|
+
throw error;
|
|
245
|
+
}
|
|
246
|
+
if (!response.body) {
|
|
247
|
+
throw new CodexCompactionProtocolError("Responses Compact response did not contain a body");
|
|
248
|
+
}
|
|
249
|
+
const reader = response.body.getReader();
|
|
250
|
+
const chunks: Uint8Array[] = [];
|
|
251
|
+
let bytes = 0;
|
|
252
|
+
const onAbort = () => {
|
|
253
|
+
void reader.cancel(new DOMException("Compaction aborted", "AbortError")).catch(() => undefined);
|
|
254
|
+
};
|
|
255
|
+
options.signal?.addEventListener("abort", onAbort, { once: true });
|
|
256
|
+
try {
|
|
257
|
+
while (true) {
|
|
258
|
+
if (options.signal?.aborted) throw new DOMException("Compaction aborted", "AbortError");
|
|
259
|
+
const { done, value } = await reader.read();
|
|
260
|
+
if (done) break;
|
|
261
|
+
bytes += value.byteLength;
|
|
262
|
+
if (bytes > maxBytes) {
|
|
263
|
+
throw new CodexCompactionProtocolError("Responses Compact response exceeded the size limit");
|
|
264
|
+
}
|
|
265
|
+
chunks.push(value);
|
|
266
|
+
}
|
|
267
|
+
if (options.signal?.aborted) throw new DOMException("Compaction aborted", "AbortError");
|
|
268
|
+
} catch (error) {
|
|
269
|
+
await reader.cancel(error).catch(() => undefined);
|
|
270
|
+
throw error;
|
|
271
|
+
} finally {
|
|
272
|
+
options.signal?.removeEventListener("abort", onAbort);
|
|
273
|
+
reader.releaseLock();
|
|
274
|
+
}
|
|
275
|
+
const body = new Uint8Array(bytes);
|
|
276
|
+
let offset = 0;
|
|
277
|
+
for (const chunk of chunks) {
|
|
278
|
+
body.set(chunk, offset);
|
|
279
|
+
offset += chunk.byteLength;
|
|
280
|
+
}
|
|
281
|
+
let parsed: unknown;
|
|
282
|
+
try {
|
|
283
|
+
parsed = JSON.parse(new TextDecoder().decode(body));
|
|
284
|
+
} catch {
|
|
285
|
+
throw new CodexCompactionProtocolError("Responses Compact returned malformed JSON");
|
|
286
|
+
}
|
|
287
|
+
return validateCompactedResponse(parsed, options);
|
|
157
288
|
}
|
|
158
289
|
|
|
159
290
|
function markerTextFromItem(item: unknown): string | undefined {
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
291
|
+
if (!isObject(item) || item.role !== "user" || !Array.isArray(item.content)) return undefined;
|
|
292
|
+
if (item.content.length !== 1) return undefined;
|
|
293
|
+
const content = item.content[0];
|
|
294
|
+
if (!isObject(content) || content.type !== "input_text" || typeof content.text !== "string") {
|
|
295
|
+
return undefined;
|
|
296
|
+
}
|
|
297
|
+
return content.text;
|
|
167
298
|
}
|
|
168
299
|
|
|
169
300
|
export function rewriteCheckpointMarker(
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
301
|
+
payload: unknown,
|
|
302
|
+
marker: string,
|
|
303
|
+
replacementHistory: readonly unknown[],
|
|
173
304
|
): JsonObject {
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
305
|
+
if (!isObject(payload) || !Array.isArray(payload.input)) {
|
|
306
|
+
throw new CodexCompactionProtocolError("Codex Responses payload is missing an input array");
|
|
307
|
+
}
|
|
308
|
+
const matches = payload.input
|
|
309
|
+
.map((item, index) => (markerTextFromItem(item) === marker ? index : -1))
|
|
310
|
+
.filter((index) => index >= 0);
|
|
311
|
+
if (matches.length !== 1) {
|
|
312
|
+
throw new CodexCompactionProtocolError(
|
|
313
|
+
`Provider payload contained ${matches.length} checkpoint markers; expected exactly one`,
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
const index = matches[0];
|
|
317
|
+
return {
|
|
318
|
+
...payload,
|
|
319
|
+
input: [
|
|
320
|
+
...payload.input.slice(0, index),
|
|
321
|
+
...structuredClone(replacementHistory),
|
|
322
|
+
...payload.input.slice(index + 1),
|
|
323
|
+
],
|
|
324
|
+
};
|
|
194
325
|
}
|
|
195
326
|
|
|
196
327
|
export function appendCompactionTrigger(payload: unknown): JsonObject {
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
328
|
+
if (!isObject(payload) || !Array.isArray(payload.input)) {
|
|
329
|
+
throw new CodexCompactionProtocolError("Codex Responses payload is missing an input array");
|
|
330
|
+
}
|
|
331
|
+
if (payload.input.some((item) => isObject(item) && item.type === "compaction_trigger")) {
|
|
332
|
+
throw new CodexCompactionProtocolError("Provider payload already contains a compaction trigger");
|
|
333
|
+
}
|
|
334
|
+
return { ...payload, input: [...payload.input, { type: "compaction_trigger" }] };
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
export function expandRemoteCompactionPayload(
|
|
338
|
+
payload: unknown,
|
|
339
|
+
checkpoint?: { marker: string; replacementHistory: readonly unknown[] },
|
|
340
|
+
): JsonObject {
|
|
341
|
+
if (checkpoint) {
|
|
342
|
+
return rewriteCheckpointMarker(payload, checkpoint.marker, checkpoint.replacementHistory);
|
|
343
|
+
}
|
|
344
|
+
if (!isObject(payload) || !Array.isArray(payload.input)) {
|
|
345
|
+
throw new CodexCompactionProtocolError("Responses payload is missing an input array");
|
|
346
|
+
}
|
|
347
|
+
return structuredClone(payload);
|
|
206
348
|
}
|
|
207
349
|
|
|
208
350
|
export function prepareRemoteCompactionPayload(
|
|
209
|
-
|
|
210
|
-
|
|
351
|
+
payload: unknown,
|
|
352
|
+
checkpoint?: { marker: string; replacementHistory: readonly unknown[] },
|
|
211
353
|
): JsonObject {
|
|
212
|
-
|
|
213
|
-
? rewriteCheckpointMarker(payload, checkpoint.marker, checkpoint.replacementHistory)
|
|
214
|
-
: payload;
|
|
215
|
-
return appendCompactionTrigger(expanded);
|
|
354
|
+
return appendCompactionTrigger(expandRemoteCompactionPayload(payload, checkpoint));
|
|
216
355
|
}
|
|
217
356
|
|
|
218
357
|
export function hasCheckpointMarker(payload: unknown, marker: string): boolean {
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
358
|
+
return (
|
|
359
|
+
isObject(payload) &&
|
|
360
|
+
Array.isArray(payload.input) &&
|
|
361
|
+
payload.input.some((item) => markerTextFromItem(item) === marker)
|
|
362
|
+
);
|
|
224
363
|
}
|