@ccpocket/bridge 1.58.1 → 1.59.1
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-process.d.ts +3 -0
- package/dist/codex-process.js +50 -14
- package/dist/codex-process.js.map +1 -1
- package/dist/parser.d.ts +5 -0
- package/dist/parser.js +9 -0
- package/dist/parser.js.map +1 -1
- package/dist/session.d.ts +2 -0
- package/dist/session.js +8 -2
- package/dist/session.js.map +1 -1
- package/dist/sessions-index.d.ts +5 -0
- package/dist/sessions-index.js +150 -10
- package/dist/sessions-index.js.map +1 -1
- package/dist/websocket.js +174 -56
- package/dist/websocket.js.map +1 -1
- package/package.json +1 -1
package/dist/sessions-index.js
CHANGED
|
@@ -85,6 +85,8 @@ const PARALLEL_FILE_READ_LIMIT = 32;
|
|
|
85
85
|
/** Head/Tail byte sizes for partial JSONL reads. */
|
|
86
86
|
const HEAD_BYTES = 16384; // 16KB — covers first user entry + metadata
|
|
87
87
|
const TAIL_BYTES = 8192; // 8KB — covers last entries for modified/lastPrompt
|
|
88
|
+
const CODEX_HEAD_BYTES = 131072; // 128KB — Codex turn_context can be large
|
|
89
|
+
const CODEX_TAIL_BYTES = 16384;
|
|
88
90
|
/**
|
|
89
91
|
* Run async tasks with a concurrency limit.
|
|
90
92
|
* Returns results in the same order as the input tasks.
|
|
@@ -112,6 +114,10 @@ const RE_IS_SIDECHAIN = /"isSidechain"\s*:\s*true/;
|
|
|
112
114
|
const RE_PERMISSION_MODE = /"permissionMode"\s*:\s*"([^"]+)"/;
|
|
113
115
|
const RE_TYPE_CUSTOM_TITLE = /"type"\s*:\s*"custom-title"/;
|
|
114
116
|
const RE_CUSTOM_TITLE = /"customTitle"\s*:\s*"([^"]+)"/;
|
|
117
|
+
const RE_CODEX_PARTIAL_TIMESTAMP = /"timestamp"\s*:\s*"([^"]+)"/;
|
|
118
|
+
const RE_CODEX_PARTIAL_USER_MESSAGE = /"type"\s*:\s*"event_msg"[\s\S]*"payload"\s*:\s*\{[\s\S]*"type"\s*:\s*"user_message"[\s\S]*"message"\s*:\s*"((?:\\.|[^"\\])*)/;
|
|
119
|
+
const RE_CODEX_PARTIAL_AGENT_MESSAGE = /"type"\s*:\s*"event_msg"[\s\S]*"payload"\s*:\s*\{[\s\S]*"type"\s*:\s*"agent_message"[\s\S]*"message"\s*:\s*"((?:\\.|[^"\\])*)/;
|
|
120
|
+
const RE_CODEX_PARTIAL_OUTPUT_TEXT = /"type"\s*:\s*"response_item"[\s\S]*"role"\s*:\s*"assistant"[\s\S]*"type"\s*:\s*"output_text"[\s\S]*"text"\s*:\s*"((?:\\.|[^"\\])*)/;
|
|
115
121
|
/**
|
|
116
122
|
* Detect system-injected messages that should be skipped when determining
|
|
117
123
|
* the user's first/last prompt text (e.g. local-command-caveat, stderr/stdout
|
|
@@ -124,6 +130,43 @@ function isSystemInjectedText(text) {
|
|
|
124
130
|
function isCodexAutoRenameSession(firstPrompt, model) {
|
|
125
131
|
return model === CODEX_ASSIST_MODEL && isAutoRenamePromptText(firstPrompt);
|
|
126
132
|
}
|
|
133
|
+
function decodeJsonStringPrefix(fragment) {
|
|
134
|
+
let candidate = fragment;
|
|
135
|
+
while (candidate.length > 0) {
|
|
136
|
+
try {
|
|
137
|
+
return JSON.parse(`"${candidate}"`);
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
candidate = candidate.slice(0, -1);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return "";
|
|
144
|
+
}
|
|
145
|
+
function parsePartialCodexLine(line) {
|
|
146
|
+
const timestamp = line.match(RE_CODEX_PARTIAL_TIMESTAMP)?.[1];
|
|
147
|
+
const userMessage = line.match(RE_CODEX_PARTIAL_USER_MESSAGE)?.[1];
|
|
148
|
+
if (userMessage !== undefined) {
|
|
149
|
+
return {
|
|
150
|
+
timestamp,
|
|
151
|
+
userMessage: decodeJsonStringPrefix(userMessage),
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
const agentMessage = line.match(RE_CODEX_PARTIAL_AGENT_MESSAGE)?.[1];
|
|
155
|
+
if (agentMessage !== undefined) {
|
|
156
|
+
return {
|
|
157
|
+
timestamp,
|
|
158
|
+
assistantText: decodeJsonStringPrefix(agentMessage),
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
const outputText = line.match(RE_CODEX_PARTIAL_OUTPUT_TEXT)?.[1];
|
|
162
|
+
if (outputText !== undefined) {
|
|
163
|
+
return {
|
|
164
|
+
timestamp,
|
|
165
|
+
assistantText: decodeJsonStringPrefix(outputText),
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
return timestamp ? { timestamp } : {};
|
|
169
|
+
}
|
|
127
170
|
/** Extract user prompt text from a parsed JSONL entry. */
|
|
128
171
|
function extractUserPromptText(entry) {
|
|
129
172
|
const message = entry.message;
|
|
@@ -919,6 +962,22 @@ function parseCodexSessionJsonl(raw, fallbackSessionId) {
|
|
|
919
962
|
entry = JSON.parse(line);
|
|
920
963
|
}
|
|
921
964
|
catch {
|
|
965
|
+
const partial = parsePartialCodexLine(line);
|
|
966
|
+
if (partial.timestamp) {
|
|
967
|
+
if (!created)
|
|
968
|
+
created = partial.timestamp;
|
|
969
|
+
modified = partial.timestamp;
|
|
970
|
+
}
|
|
971
|
+
if (partial.userMessage !== undefined) {
|
|
972
|
+
hasMessages = true;
|
|
973
|
+
if (!firstPrompt)
|
|
974
|
+
firstPrompt = partial.userMessage;
|
|
975
|
+
lastPrompt = partial.userMessage;
|
|
976
|
+
}
|
|
977
|
+
if (partial.assistantText) {
|
|
978
|
+
hasMessages = true;
|
|
979
|
+
lastAssistantText = partial.assistantText;
|
|
980
|
+
}
|
|
922
981
|
continue;
|
|
923
982
|
}
|
|
924
983
|
const timestamp = entry.timestamp;
|
|
@@ -994,6 +1053,12 @@ function parseCodexSessionJsonl(raw, fallbackSessionId) {
|
|
|
994
1053
|
firstPrompt = payload.message;
|
|
995
1054
|
lastPrompt = payload.message;
|
|
996
1055
|
}
|
|
1056
|
+
else if (payload?.type === "agent_message"
|
|
1057
|
+
&& typeof payload.message === "string"
|
|
1058
|
+
&& payload.message.trim().length > 0) {
|
|
1059
|
+
hasMessages = true;
|
|
1060
|
+
lastAssistantText = payload.message;
|
|
1061
|
+
}
|
|
997
1062
|
continue;
|
|
998
1063
|
}
|
|
999
1064
|
if (entry.type === "response_item") {
|
|
@@ -1059,6 +1124,43 @@ function parseCodexSessionJsonl(raw, fallbackSessionId) {
|
|
|
1059
1124
|
},
|
|
1060
1125
|
};
|
|
1061
1126
|
}
|
|
1127
|
+
/**
|
|
1128
|
+
* Fast parse a Codex JSONL file for recent-session list metadata.
|
|
1129
|
+
* The first chunk contains session_meta / first prompt; the tail chunk contains
|
|
1130
|
+
* the latest prompt, latest assistant summary, and modified timestamp.
|
|
1131
|
+
*/
|
|
1132
|
+
async function parseCodexSessionJsonlFast(filePath, fallbackSessionId) {
|
|
1133
|
+
let fh;
|
|
1134
|
+
try {
|
|
1135
|
+
fh = await open(filePath, "r");
|
|
1136
|
+
}
|
|
1137
|
+
catch {
|
|
1138
|
+
return null;
|
|
1139
|
+
}
|
|
1140
|
+
try {
|
|
1141
|
+
const fileStat = await fh.stat();
|
|
1142
|
+
const fileSize = fileStat.size;
|
|
1143
|
+
if (fileSize === 0)
|
|
1144
|
+
return null;
|
|
1145
|
+
if (fileSize <= CODEX_HEAD_BYTES + CODEX_TAIL_BYTES) {
|
|
1146
|
+
const buf = Buffer.alloc(fileSize);
|
|
1147
|
+
await fh.read(buf, 0, fileSize, 0);
|
|
1148
|
+
return parseCodexSessionJsonl(buf.toString("utf-8"), fallbackSessionId);
|
|
1149
|
+
}
|
|
1150
|
+
const headBuf = Buffer.alloc(CODEX_HEAD_BYTES);
|
|
1151
|
+
await fh.read(headBuf, 0, CODEX_HEAD_BYTES, 0);
|
|
1152
|
+
const tailBuf = Buffer.alloc(CODEX_TAIL_BYTES);
|
|
1153
|
+
await fh.read(tailBuf, 0, CODEX_TAIL_BYTES, fileSize - CODEX_TAIL_BYTES);
|
|
1154
|
+
const tailRaw = tailBuf.toString("utf-8");
|
|
1155
|
+
const firstNewline = tailRaw.indexOf("\n");
|
|
1156
|
+
const cleanTail = firstNewline >= 0 ? tailRaw.slice(firstNewline + 1) : "";
|
|
1157
|
+
const partialRaw = `${headBuf.toString("utf-8")}\n${cleanTail}`;
|
|
1158
|
+
return parseCodexSessionJsonl(partialRaw, fallbackSessionId);
|
|
1159
|
+
}
|
|
1160
|
+
finally {
|
|
1161
|
+
await fh.close();
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1062
1164
|
function isCodexInternalSessionSource(source) {
|
|
1063
1165
|
const sourceObj = asObject(source);
|
|
1064
1166
|
return sourceObj?.subagent !== undefined;
|
|
@@ -1343,17 +1445,12 @@ async function getAllRecentCodexSessions(options = {}) {
|
|
|
1343
1445
|
const threadNames = await loadCodexSessionNames();
|
|
1344
1446
|
const threadProfiles = await loadCodexSessionProfiles();
|
|
1345
1447
|
const threadAdditionalWritableRoots = await loadCodexSessionAdditionalWritableRoots();
|
|
1346
|
-
|
|
1347
|
-
let raw;
|
|
1348
|
-
try {
|
|
1349
|
-
raw = await readFile(filePath, "utf-8");
|
|
1350
|
-
}
|
|
1351
|
-
catch {
|
|
1352
|
-
continue;
|
|
1353
|
-
}
|
|
1354
|
-
options.perfStats && (options.perfStats.filesRead += 1);
|
|
1448
|
+
const parsedResults = await parallelMap(files, PARALLEL_FILE_READ_LIMIT, async (filePath) => {
|
|
1355
1449
|
const fallbackSessionId = basename(filePath, ".jsonl");
|
|
1356
|
-
|
|
1450
|
+
return parseCodexSessionJsonlFast(filePath, fallbackSessionId);
|
|
1451
|
+
});
|
|
1452
|
+
for (const parsed of parsedResults) {
|
|
1453
|
+
options.perfStats && (options.perfStats.filesRead += 1);
|
|
1357
1454
|
if (!parsed)
|
|
1358
1455
|
continue;
|
|
1359
1456
|
if (normalizedProjectPath && parsed.entry.projectPath !== normalizedProjectPath) {
|
|
@@ -1383,6 +1480,49 @@ async function getAllRecentCodexSessions(options = {}) {
|
|
|
1383
1480
|
}
|
|
1384
1481
|
return entries;
|
|
1385
1482
|
}
|
|
1483
|
+
function matchingCodexThreadIdFromFilePath(filePath, wantedThreadIds) {
|
|
1484
|
+
const fallbackSessionId = basename(filePath, ".jsonl");
|
|
1485
|
+
if (wantedThreadIds.has(fallbackSessionId))
|
|
1486
|
+
return fallbackSessionId;
|
|
1487
|
+
for (const threadId of wantedThreadIds) {
|
|
1488
|
+
if (fallbackSessionId.endsWith(`-${threadId}`))
|
|
1489
|
+
return threadId;
|
|
1490
|
+
}
|
|
1491
|
+
return null;
|
|
1492
|
+
}
|
|
1493
|
+
export async function getCodexSessionIndexMetadata(threadIds) {
|
|
1494
|
+
const wantedThreadIds = new Set(threadIds.filter((id) => id.length > 0));
|
|
1495
|
+
const result = new Map();
|
|
1496
|
+
if (wantedThreadIds.size === 0)
|
|
1497
|
+
return result;
|
|
1498
|
+
const files = await listCodexSessionFiles();
|
|
1499
|
+
const targets = [];
|
|
1500
|
+
const matchedThreadIds = new Set();
|
|
1501
|
+
for (const filePath of files) {
|
|
1502
|
+
const threadId = matchingCodexThreadIdFromFilePath(filePath, wantedThreadIds);
|
|
1503
|
+
if (!threadId || matchedThreadIds.has(threadId))
|
|
1504
|
+
continue;
|
|
1505
|
+
targets.push(filePath);
|
|
1506
|
+
matchedThreadIds.add(threadId);
|
|
1507
|
+
if (matchedThreadIds.size === wantedThreadIds.size)
|
|
1508
|
+
break;
|
|
1509
|
+
}
|
|
1510
|
+
const parsedResults = await parallelMap(targets, PARALLEL_FILE_READ_LIMIT, async (filePath) => {
|
|
1511
|
+
const fallbackSessionId = basename(filePath, ".jsonl");
|
|
1512
|
+
return parseCodexSessionJsonlFast(filePath, fallbackSessionId);
|
|
1513
|
+
});
|
|
1514
|
+
for (const parsed of parsedResults) {
|
|
1515
|
+
if (!parsed || !wantedThreadIds.has(parsed.threadId))
|
|
1516
|
+
continue;
|
|
1517
|
+
result.set(parsed.threadId, {
|
|
1518
|
+
...(parsed.entry.codexSettings
|
|
1519
|
+
? { codexSettings: parsed.entry.codexSettings }
|
|
1520
|
+
: {}),
|
|
1521
|
+
...(parsed.entry.resumeCwd ? { resumeCwd: parsed.entry.resumeCwd } : {}),
|
|
1522
|
+
});
|
|
1523
|
+
}
|
|
1524
|
+
return result;
|
|
1525
|
+
}
|
|
1386
1526
|
export function codexUserTurnUuid(ordinal) {
|
|
1387
1527
|
return `codex:user-turn:${ordinal}`;
|
|
1388
1528
|
}
|