@ccpocket/bridge 1.85.0 → 1.85.2
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/dist/codex-history.d.ts +5 -0
- package/dist/codex-history.js +53 -0
- package/dist/codex-history.js.map +1 -0
- package/dist/codex-process.d.ts +5 -1
- package/dist/codex-process.js +155 -49
- package/dist/codex-process.js.map +1 -1
- package/dist/finder-reveal.d.ts +2 -0
- package/dist/finder-reveal.js +33 -0
- package/dist/finder-reveal.js.map +1 -1
- package/dist/parser.d.ts +7 -0
- package/dist/parser.js +8 -0
- package/dist/parser.js.map +1 -1
- package/dist/websocket.js +5 -2
- package/dist/websocket.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// These limits apply only to the Bridge's display copy, never to Codex's
|
|
2
|
+
// persisted history or the model's context.
|
|
3
|
+
const MAX_TOOL_ITEM_CHARS = 256 * 1024;
|
|
4
|
+
const MAX_TOOL_STRING_CHARS = 16 * 1024;
|
|
5
|
+
const OMITTED = "[Large tool detail omitted from Bridge history]";
|
|
6
|
+
const TRUNCATED = "\n[Truncated in Bridge history]";
|
|
7
|
+
const TOOL_TYPES = new Set([
|
|
8
|
+
"commandExecution", "fileChange", "mcpToolCall", "dynamicToolCall",
|
|
9
|
+
"functionCallOutput", "imageGeneration",
|
|
10
|
+
]);
|
|
11
|
+
/** Bound unusually large tool payloads before retaining all history pages. */
|
|
12
|
+
export function compactCodexHistoryItem(item) {
|
|
13
|
+
const chars = JSON.stringify(item).length;
|
|
14
|
+
if (!TOOL_TYPES.has(String(item.type)) || chars <= MAX_TOOL_ITEM_CHARS) {
|
|
15
|
+
return { item, chars };
|
|
16
|
+
}
|
|
17
|
+
const compacted = compactValue(item, 0);
|
|
18
|
+
// Generated images use a bare base64 string. Never pass truncated base64 to
|
|
19
|
+
// the image decoder; show an explicit text result instead.
|
|
20
|
+
if (item.type === "imageGeneration") {
|
|
21
|
+
delete compacted.result;
|
|
22
|
+
compacted.revisedPrompt = `${compacted.revisedPrompt ?? ""}\n${OMITTED}`;
|
|
23
|
+
}
|
|
24
|
+
return { item: compacted, chars: JSON.stringify(compacted).length };
|
|
25
|
+
}
|
|
26
|
+
function compactValue(value, depth) {
|
|
27
|
+
if (typeof value === "string") {
|
|
28
|
+
if (value.length <= MAX_TOOL_STRING_CHARS)
|
|
29
|
+
return value;
|
|
30
|
+
if (value.startsWith("data:image/"))
|
|
31
|
+
return OMITTED;
|
|
32
|
+
return value.slice(0, MAX_TOOL_STRING_CHARS) + TRUNCATED;
|
|
33
|
+
}
|
|
34
|
+
if (value == null || typeof value !== "object")
|
|
35
|
+
return value;
|
|
36
|
+
if (depth >= 20)
|
|
37
|
+
return OMITTED;
|
|
38
|
+
if (Array.isArray(value)) {
|
|
39
|
+
return value.map((entry) => compactValue(entry, depth + 1));
|
|
40
|
+
}
|
|
41
|
+
const record = value;
|
|
42
|
+
if (record.type === "image" && typeof record.data === "string"
|
|
43
|
+
&& record.data.length > MAX_TOOL_STRING_CHARS) {
|
|
44
|
+
return { type: "text", text: OMITTED };
|
|
45
|
+
}
|
|
46
|
+
if (record.type === "inputImage" && typeof record.imageUrl === "string"
|
|
47
|
+
&& record.imageUrl.length > MAX_TOOL_STRING_CHARS) {
|
|
48
|
+
return { type: "inputText", text: OMITTED };
|
|
49
|
+
}
|
|
50
|
+
return Object.fromEntries(Object.entries(record)
|
|
51
|
+
.map(([key, entry]) => [key, compactValue(entry, depth + 1)]));
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=codex-history.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codex-history.js","sourceRoot":"","sources":["../src/codex-history.ts"],"names":[],"mappings":"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"}
|
package/dist/codex-process.d.ts
CHANGED
|
@@ -146,6 +146,7 @@ export declare class CodexProcess extends EventEmitter<CodexProcessEvents> {
|
|
|
146
146
|
* copied at most once instead of repeatedly rebuilding one growing string.
|
|
147
147
|
*/
|
|
148
148
|
private stdoutLineChunks;
|
|
149
|
+
private stdoutLineChars;
|
|
149
150
|
private _approvalPolicy;
|
|
150
151
|
private _approvalsReviewer;
|
|
151
152
|
private _codexPermissionsMode;
|
|
@@ -226,6 +227,9 @@ export declare class CodexProcess extends EventEmitter<CodexProcessEvents> {
|
|
|
226
227
|
*/
|
|
227
228
|
archiveThread(threadId: string): Promise<void>;
|
|
228
229
|
readThread(threadId: string, includeTurns?: boolean): Promise<Record<string, unknown>>;
|
|
230
|
+
private checkHistorySize;
|
|
231
|
+
private readTurnPages;
|
|
232
|
+
private readHistoryPages;
|
|
229
233
|
/** Fork at a complete turn boundary without mutating the source history. */
|
|
230
234
|
forkThreadAtUserTurn(userTurnOrdinal: number): Promise<{
|
|
231
235
|
threadId: string;
|
|
@@ -341,7 +345,7 @@ export declare class CodexProcess extends EventEmitter<CodexProcessEvents> {
|
|
|
341
345
|
private emitCompletionEntitiesSnapshot;
|
|
342
346
|
private runInputLoop;
|
|
343
347
|
private handleStdoutChunk;
|
|
344
|
-
private
|
|
348
|
+
private failStdout;
|
|
345
349
|
private handleRpcEnvelope;
|
|
346
350
|
private handleRpcResponse;
|
|
347
351
|
private handleServerRequest;
|
package/dist/codex-process.js
CHANGED
|
@@ -5,6 +5,7 @@ import { tmpdir } from "node:os";
|
|
|
5
5
|
import { dirname, join } from "node:path";
|
|
6
6
|
import { rm, writeFile } from "node:fs/promises";
|
|
7
7
|
import { createCodexTransport, buildCodexSpawnSpec, } from "./codex-transport.js";
|
|
8
|
+
import { compactCodexHistoryItem } from "./codex-history.js";
|
|
8
9
|
import { codexThreadToSessionHistory } from "./sessions-index.js";
|
|
9
10
|
import { codexCliJoinTarget } from "./codex-app-server-config.js";
|
|
10
11
|
import { normalizeCodexServiceTierForClient, normalizeCodexServiceTierForRpc, } from "./codex-service-tier.js";
|
|
@@ -13,6 +14,8 @@ export { buildCodexSpawnSpec };
|
|
|
13
14
|
const DEFAULT_CODEX_MODEL = "gpt-5.5";
|
|
14
15
|
const COMPLETION_FETCH_COOLDOWN_MS = 1000;
|
|
15
16
|
const ARCHIVE_RPC_TIMEOUT_MS = 15_000;
|
|
17
|
+
const MAX_STDOUT_LINE_CHARS = 64 * 1024 * 1024;
|
|
18
|
+
const MAX_RETAINED_HISTORY_CHARS = 64 * 1024 * 1024;
|
|
16
19
|
const UNKNOWN_AGENT_ITEM_ID = "__unknown_agent_message__";
|
|
17
20
|
const CODEX_CLI_NOT_FOUND_MESSAGE = "Codex CLI is not installed or not available on PATH on the Bridge machine. Install it with `curl -fsSL https://chatgpt.com/codex/install.sh | sh`, then restart Bridge.";
|
|
18
21
|
export class CodexRpcError extends Error {
|
|
@@ -102,6 +105,7 @@ export class CodexProcess extends EventEmitter {
|
|
|
102
105
|
* copied at most once instead of repeatedly rebuilding one growing string.
|
|
103
106
|
*/
|
|
104
107
|
stdoutLineChunks = [];
|
|
108
|
+
stdoutLineChars = 0;
|
|
105
109
|
// Collaboration mode & plan completion state
|
|
106
110
|
_approvalPolicy = undefined;
|
|
107
111
|
_approvalsReviewer = undefined;
|
|
@@ -292,55 +296,133 @@ export class CodexProcess extends EventEmitter {
|
|
|
292
296
|
}
|
|
293
297
|
if (!includeTurns)
|
|
294
298
|
return thread;
|
|
299
|
+
try {
|
|
300
|
+
// A single autonomous turn can contain thousands of screenshots/tool
|
|
301
|
+
// results. Page turn metadata separately, then hydrate by item count.
|
|
302
|
+
const turns = await this.readTurnPages(threadId, "notLoaded");
|
|
303
|
+
const byId = new Map();
|
|
304
|
+
for (const turn of turns) {
|
|
305
|
+
if (typeof turn.id !== "string")
|
|
306
|
+
throw new Error("Codex turn has no id");
|
|
307
|
+
turn.items = [];
|
|
308
|
+
byId.set(turn.id, turn);
|
|
309
|
+
}
|
|
310
|
+
let retainedChars = JSON.stringify(turns).length;
|
|
311
|
+
await this.readHistoryPages("thread/items/list", {
|
|
312
|
+
threadId, limit: 10, sortDirection: "asc",
|
|
313
|
+
}, (entries) => {
|
|
314
|
+
for (const rawEntry of entries) {
|
|
315
|
+
const entry = asRecord(rawEntry);
|
|
316
|
+
const item = asRecord(entry?.item);
|
|
317
|
+
const turn = typeof entry?.turnId === "string"
|
|
318
|
+
? byId.get(entry.turnId) : undefined;
|
|
319
|
+
if (!item || !turn) {
|
|
320
|
+
throw new Error("Codex history changed while reading; retry opening the session");
|
|
321
|
+
}
|
|
322
|
+
const compacted = compactCodexHistoryItem(item);
|
|
323
|
+
retainedChars += compacted.chars;
|
|
324
|
+
this.checkHistorySize(retainedChars);
|
|
325
|
+
turn.items.push(compacted.item);
|
|
326
|
+
}
|
|
327
|
+
});
|
|
328
|
+
for (const turn of turns)
|
|
329
|
+
turn.itemsView = "full";
|
|
330
|
+
return { ...thread, turns };
|
|
331
|
+
}
|
|
332
|
+
catch (err) {
|
|
333
|
+
if (err instanceof CodexRpcError && err.code === -32600 &&
|
|
334
|
+
err.message.includes("is not materialized yet")) {
|
|
335
|
+
return { ...thread, turns: [] };
|
|
336
|
+
}
|
|
337
|
+
// Unsupported methods/views permit the compatibility path. Never retry
|
|
338
|
+
// a size/transport failure with a larger full-history response.
|
|
339
|
+
const unsupportedMetadataView = err instanceof CodexRpcError &&
|
|
340
|
+
err.code === -32602 && /unknown variant.*notLoaded/i.test(err.message);
|
|
341
|
+
if (!unsupportedMetadataView &&
|
|
342
|
+
(!(err instanceof CodexRpcError) || err.code !== -32601))
|
|
343
|
+
throw err;
|
|
344
|
+
}
|
|
345
|
+
try {
|
|
346
|
+
return { ...thread, turns: await this.readTurnPages(threadId, "full") };
|
|
347
|
+
}
|
|
348
|
+
catch (err) {
|
|
349
|
+
if (!(err instanceof CodexRpcError) || err.code !== -32601)
|
|
350
|
+
throw err;
|
|
351
|
+
}
|
|
352
|
+
const legacy = (await this.request("thread/read", {
|
|
353
|
+
threadId, includeTurns: true,
|
|
354
|
+
}));
|
|
355
|
+
const legacyThread = asRecord(legacy.thread);
|
|
356
|
+
if (!legacyThread)
|
|
357
|
+
throw new Error("thread/read returned no thread");
|
|
358
|
+
if (Array.isArray(legacyThread.turns)) {
|
|
359
|
+
let chars = 0;
|
|
360
|
+
for (const rawTurn of legacyThread.turns) {
|
|
361
|
+
const turn = asRecord(rawTurn);
|
|
362
|
+
if (!turn || !Array.isArray(turn.items))
|
|
363
|
+
continue;
|
|
364
|
+
turn.items = turn.items.map((rawItem) => {
|
|
365
|
+
const item = asRecord(rawItem);
|
|
366
|
+
if (!item)
|
|
367
|
+
return rawItem;
|
|
368
|
+
const compacted = compactCodexHistoryItem(item);
|
|
369
|
+
chars += compacted.chars;
|
|
370
|
+
this.checkHistorySize(chars);
|
|
371
|
+
return compacted.item;
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
return legacyThread;
|
|
376
|
+
}
|
|
377
|
+
checkHistorySize(chars) {
|
|
378
|
+
if (chars > MAX_RETAINED_HISTORY_CHARS) {
|
|
379
|
+
throw new Error("Codex display history exceeds the Bridge 64 Mi-character limit");
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
async readTurnPages(threadId, itemsView) {
|
|
295
383
|
const turns = [];
|
|
384
|
+
let retainedChars = 0;
|
|
385
|
+
await this.readHistoryPages("thread/turns/list", {
|
|
386
|
+
threadId, limit: itemsView === "full" ? 1 : 50,
|
|
387
|
+
sortDirection: "asc", itemsView,
|
|
388
|
+
}, (data) => {
|
|
389
|
+
for (const rawTurn of data) {
|
|
390
|
+
const turn = asRecord(rawTurn);
|
|
391
|
+
if (!turn)
|
|
392
|
+
throw new Error("thread/turns/list returned an invalid turn");
|
|
393
|
+
if (itemsView === "full" && Array.isArray(turn.items)) {
|
|
394
|
+
turn.items = turn.items.map((rawItem) => {
|
|
395
|
+
const item = asRecord(rawItem);
|
|
396
|
+
return item ? compactCodexHistoryItem(item).item : rawItem;
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
retainedChars += JSON.stringify(turn).length;
|
|
400
|
+
this.checkHistorySize(retainedChars);
|
|
401
|
+
turns.push(turn);
|
|
402
|
+
}
|
|
403
|
+
});
|
|
404
|
+
return turns;
|
|
405
|
+
}
|
|
406
|
+
async readHistoryPages(method, params, consume) {
|
|
296
407
|
const seenCursors = new Set();
|
|
297
408
|
let cursor;
|
|
298
409
|
do {
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
sortDirection: "asc",
|
|
306
|
-
itemsView: "full",
|
|
307
|
-
}));
|
|
308
|
-
}
|
|
309
|
-
catch (err) {
|
|
310
|
-
// New threads have metadata but no persisted turn history yet.
|
|
311
|
-
if (cursor === undefined &&
|
|
312
|
-
err instanceof CodexRpcError &&
|
|
313
|
-
err.code === -32600 &&
|
|
314
|
-
err.message.includes("is not materialized yet; thread/turns/list is unavailable before first user message")) {
|
|
315
|
-
return { ...thread, turns: [] };
|
|
316
|
-
}
|
|
317
|
-
// Older app-servers do not expose turn pagination.
|
|
318
|
-
if (!(err instanceof CodexRpcError) || err.code !== -32601)
|
|
319
|
-
throw err;
|
|
320
|
-
const legacy = (await this.request("thread/read", {
|
|
321
|
-
threadId,
|
|
322
|
-
includeTurns: true,
|
|
323
|
-
}));
|
|
324
|
-
if (!legacy.thread)
|
|
325
|
-
throw new Error("thread/read returned no thread");
|
|
326
|
-
return legacy.thread;
|
|
327
|
-
}
|
|
328
|
-
if (!Array.isArray(page.data)) {
|
|
329
|
-
throw new Error("thread/turns/list returned invalid data");
|
|
330
|
-
}
|
|
331
|
-
turns.push(...page.data);
|
|
410
|
+
const page = (await this.request(method, {
|
|
411
|
+
...params, ...(cursor !== undefined ? { cursor } : {}),
|
|
412
|
+
}));
|
|
413
|
+
if (!Array.isArray(page.data))
|
|
414
|
+
throw new Error(`${method} returned invalid data`);
|
|
415
|
+
consume(page.data);
|
|
332
416
|
if (page.nextCursor != null && typeof page.nextCursor !== "string") {
|
|
333
|
-
throw new Error(
|
|
417
|
+
throw new Error(`${method} returned an invalid cursor`);
|
|
334
418
|
}
|
|
335
419
|
cursor = page.nextCursor ?? undefined;
|
|
336
420
|
if (cursor !== undefined) {
|
|
337
|
-
if (seenCursors.has(cursor))
|
|
338
|
-
throw new Error(
|
|
339
|
-
}
|
|
421
|
+
if (seenCursors.has(cursor))
|
|
422
|
+
throw new Error(`${method} returned a repeated cursor`);
|
|
340
423
|
seenCursors.add(cursor);
|
|
341
424
|
}
|
|
342
425
|
} while (cursor !== undefined);
|
|
343
|
-
return { ...thread, turns };
|
|
344
426
|
}
|
|
345
427
|
/** Fork at a complete turn boundary without mutating the source history. */
|
|
346
428
|
async forkThreadAtUserTurn(userTurnOrdinal) {
|
|
@@ -520,6 +602,8 @@ export class CodexProcess extends EventEmitter {
|
|
|
520
602
|
}
|
|
521
603
|
this.pendingApprovals.clear();
|
|
522
604
|
this.pendingUserInputs.clear();
|
|
605
|
+
this.stdoutLineChunks = [];
|
|
606
|
+
this.stdoutLineChars = 0;
|
|
523
607
|
this.cleanupSteerTempPaths();
|
|
524
608
|
this.rejectAllPending(new Error("stopped"));
|
|
525
609
|
if (this.transport) {
|
|
@@ -570,6 +654,7 @@ export class CodexProcess extends EventEmitter {
|
|
|
570
654
|
this._pendingPlanInput = null;
|
|
571
655
|
this._projectPath = projectPath;
|
|
572
656
|
this.stdoutLineChunks = [];
|
|
657
|
+
this.stdoutLineChars = 0;
|
|
573
658
|
}
|
|
574
659
|
launchAppServer(projectPath, options) {
|
|
575
660
|
const sandboxLog = options?.sandboxMode ?? "config";
|
|
@@ -1566,17 +1651,37 @@ export class CodexProcess extends EventEmitter {
|
|
|
1566
1651
|
}
|
|
1567
1652
|
}
|
|
1568
1653
|
handleStdoutChunk(chunk) {
|
|
1654
|
+
if (this.stopped)
|
|
1655
|
+
return;
|
|
1569
1656
|
let lineStart = 0;
|
|
1570
1657
|
while (lineStart < chunk.length) {
|
|
1571
1658
|
const newlineIndex = chunk.indexOf("\n", lineStart);
|
|
1659
|
+
const end = newlineIndex < 0 ? chunk.length : newlineIndex;
|
|
1660
|
+
const fragment = chunk.slice(lineStart, end);
|
|
1661
|
+
this.stdoutLineChars += fragment.length;
|
|
1662
|
+
if (this.stdoutLineChars > MAX_STDOUT_LINE_CHARS) {
|
|
1663
|
+
this.failStdout(new Error("Codex app-server response exceeds the Bridge 64 Mi-character limit " +
|
|
1664
|
+
`(pending: ${[...this.pendingRpc.values()].map((rpc) => rpc.method).join(", ") || "notification"}). ` +
|
|
1665
|
+
"Only this Codex connection was closed; Bridge is still running."));
|
|
1666
|
+
return;
|
|
1667
|
+
}
|
|
1572
1668
|
if (newlineIndex < 0) {
|
|
1573
|
-
this.stdoutLineChunks.push(
|
|
1669
|
+
this.stdoutLineChunks.push(fragment);
|
|
1670
|
+
return;
|
|
1671
|
+
}
|
|
1672
|
+
let line;
|
|
1673
|
+
try {
|
|
1674
|
+
// Guard both fragmented and single-chunk records before joining, and
|
|
1675
|
+
// keep allocation errors inside the connection's failure boundary.
|
|
1676
|
+
this.stdoutLineChunks.push(fragment);
|
|
1677
|
+
line = this.stdoutLineChunks.join("").trim();
|
|
1678
|
+
}
|
|
1679
|
+
catch (err) {
|
|
1680
|
+
this.failStdout(err instanceof Error ? err : new Error(String(err)));
|
|
1574
1681
|
return;
|
|
1575
1682
|
}
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
? lineFragment.trim()
|
|
1579
|
-
: this.completeStdoutLine(lineFragment);
|
|
1683
|
+
this.stdoutLineChunks = [];
|
|
1684
|
+
this.stdoutLineChars = 0;
|
|
1580
1685
|
lineStart = newlineIndex + 1;
|
|
1581
1686
|
if (!line)
|
|
1582
1687
|
continue;
|
|
@@ -1585,7 +1690,7 @@ export class CodexProcess extends EventEmitter {
|
|
|
1585
1690
|
this.handleRpcEnvelope(envelope);
|
|
1586
1691
|
}
|
|
1587
1692
|
catch (err) {
|
|
1588
|
-
console.warn(
|
|
1693
|
+
console.warn("[codex-process] failed to parse app-server JSON line");
|
|
1589
1694
|
if (!this.stopped) {
|
|
1590
1695
|
this.emitMessage({
|
|
1591
1696
|
type: "error",
|
|
@@ -1595,11 +1700,12 @@ export class CodexProcess extends EventEmitter {
|
|
|
1595
1700
|
}
|
|
1596
1701
|
}
|
|
1597
1702
|
}
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
this.
|
|
1602
|
-
|
|
1703
|
+
failStdout(error) {
|
|
1704
|
+
console.error(`[codex-process] ${error.message}`);
|
|
1705
|
+
this.rejectReadiness(error);
|
|
1706
|
+
this.rejectAllPending(error);
|
|
1707
|
+
this.emitMessage({ type: "error", message: error.message });
|
|
1708
|
+
this.stop();
|
|
1603
1709
|
}
|
|
1604
1710
|
handleRpcEnvelope(envelope) {
|
|
1605
1711
|
if (envelope.id != null &&
|