@ccpocket/bridge 1.52.2 → 1.53.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.
@@ -3,6 +3,8 @@ import { createReadStream } from "node:fs";
3
3
  import { createInterface } from "node:readline";
4
4
  import { basename, join } from "node:path";
5
5
  import { homedir } from "node:os";
6
+ import { isAutoRenamePromptText } from "./auto-rename.js";
7
+ import { CODEX_ASSIST_MODEL } from "./codex-assist.js";
6
8
  function createRecentSessionsPerfStats() {
7
9
  return {
8
10
  claudeProjectDirs: 0,
@@ -119,6 +121,9 @@ const RE_SYSTEM_INJECTED = /^<(?:local-command-caveat|local-command-std(?:err|ou
119
121
  function isSystemInjectedText(text) {
120
122
  return RE_SYSTEM_INJECTED.test(text) || text.startsWith("Base directory for this skill:");
121
123
  }
124
+ function isCodexAutoRenameSession(firstPrompt, model) {
125
+ return model === CODEX_ASSIST_MODEL && isAutoRenamePromptText(firstPrompt);
126
+ }
122
127
  /** Extract user prompt text from a parsed JSONL entry. */
123
128
  function extractUserPromptText(entry) {
124
129
  const message = entry.message;
@@ -147,6 +152,7 @@ function parseFromChunks(sessionId, head, tail) {
147
152
  let headFoundFirstPrompt = false;
148
153
  let headFoundProjectPath = false;
149
154
  let headFoundGitBranch = false;
155
+ let isInternalAutoRename = false;
150
156
  // --- Scan head lines ---
151
157
  const headLines = head.split("\n");
152
158
  for (const line of headLines) {
@@ -200,6 +206,10 @@ function parseFromChunks(sessionId, head, tail) {
200
206
  const entry = JSON.parse(line);
201
207
  const text = extractUserPromptText(entry);
202
208
  if (text && !isSystemInjectedText(text)) {
209
+ if (isAutoRenamePromptText(text)) {
210
+ isInternalAutoRename = true;
211
+ break;
212
+ }
203
213
  firstPrompt = text;
204
214
  headFoundFirstPrompt = true;
205
215
  }
@@ -207,6 +217,14 @@ function parseFromChunks(sessionId, head, tail) {
207
217
  catch { /* skip */ }
208
218
  }
209
219
  }
220
+ if (isInternalAutoRename) {
221
+ return {
222
+ entry: null,
223
+ headFoundFirstPrompt,
224
+ headFoundProjectPath,
225
+ headFoundGitBranch,
226
+ };
227
+ }
210
228
  // --- Scan tail lines (if separate from head) ---
211
229
  if (tail) {
212
230
  const tailLines = tail.split("\n");
@@ -288,6 +306,14 @@ function parseFromChunks(sessionId, head, tail) {
288
306
  headFoundGitBranch,
289
307
  };
290
308
  }
309
+ if (isAutoRenamePromptText(firstPrompt)) {
310
+ return {
311
+ entry: null,
312
+ headFoundFirstPrompt,
313
+ headFoundProjectPath,
314
+ headFoundGitBranch,
315
+ };
316
+ }
291
317
  return {
292
318
  entry: {
293
319
  sessionId,
@@ -361,6 +387,9 @@ async function parseClaudeJsonlFileFast(sessionId, filePath) {
361
387
  if (!result.firstPrompt && missing.firstPrompt) {
362
388
  result.firstPrompt = missing.firstPrompt;
363
389
  }
390
+ if (isAutoRenamePromptText(result.firstPrompt)) {
391
+ return null;
392
+ }
364
393
  if (missing.projectPath) {
365
394
  result.projectPath = missing.projectPath;
366
395
  if (missing.rawCwd && missing.rawCwd !== missing.projectPath) {
@@ -377,6 +406,8 @@ async function parseClaudeJsonlFileFast(sessionId, filePath) {
377
406
  return result;
378
407
  }
379
408
  async function hydrateClaudeIndexedEntry(dirPath, entry) {
409
+ if (isAutoRenamePromptText(entry.firstPrompt ?? ""))
410
+ return null;
380
411
  const rawProjectPath = entry.projectPath ?? "";
381
412
  const normalizedPath = normalizeWorktreePath(rawProjectPath);
382
413
  const base = {
@@ -404,6 +435,8 @@ async function hydrateClaudeIndexedEntry(dirPath, entry) {
404
435
  const parsed = await parseClaudeJsonlFileFast(entry.sessionId, fallbackPath);
405
436
  if (!parsed)
406
437
  return base;
438
+ if (isAutoRenamePromptText(parsed.firstPrompt))
439
+ return null;
407
440
  return {
408
441
  ...base,
409
442
  firstPrompt: base.firstPrompt || parsed.firstPrompt,
@@ -677,8 +710,11 @@ export async function getAllRecentSessions(options = {}) {
677
710
  continue;
678
711
  }
679
712
  indexedIds.add(entry.sessionId);
680
- result.entries.push(await hydrateClaudeIndexedEntry(dirPath, entry));
681
- result.indexEntries += 1;
713
+ const hydrated = await hydrateClaudeIndexedEntry(dirPath, entry);
714
+ if (hydrated) {
715
+ result.entries.push(hydrated);
716
+ result.indexEntries += 1;
717
+ }
682
718
  }
683
719
  if (!includeOnlyNamedClaude) {
684
720
  const scanned = await scanJsonlDir(dirPath, {
@@ -981,6 +1017,8 @@ function parseCodexSessionJsonl(raw, fallbackSessionId) {
981
1017
  }
982
1018
  if (model === "codex-auto-review")
983
1019
  return null;
1020
+ if (isCodexAutoRenameSession(firstPrompt, model))
1021
+ return null;
984
1022
  if (!projectPath || !hasMessages)
985
1023
  return null;
986
1024
  summary = lastAssistantText || summary;
@@ -1348,6 +1386,216 @@ async function getAllRecentCodexSessions(options = {}) {
1348
1386
  export function codexUserTurnUuid(ordinal) {
1349
1387
  return `codex:user-turn:${ordinal}`;
1350
1388
  }
1389
+ function numberToIsoTimestamp(value) {
1390
+ return typeof value === "number" && Number.isFinite(value)
1391
+ ? new Date(value * 1000).toISOString()
1392
+ : undefined;
1393
+ }
1394
+ function stringValue(value) {
1395
+ return typeof value === "string" ? value : undefined;
1396
+ }
1397
+ function arrayValue(value) {
1398
+ return Array.isArray(value) ? value : [];
1399
+ }
1400
+ function codexToolResultContent(value) {
1401
+ if (value == null)
1402
+ return "";
1403
+ if (typeof value === "string")
1404
+ return value;
1405
+ try {
1406
+ return JSON.stringify(value, null, 2);
1407
+ }
1408
+ catch {
1409
+ return String(value);
1410
+ }
1411
+ }
1412
+ function codexUserInputTextAndImages(content) {
1413
+ const textParts = [];
1414
+ let imageCount = 0;
1415
+ for (const entry of arrayValue(content)) {
1416
+ const item = asObject(entry);
1417
+ if (!item)
1418
+ continue;
1419
+ if (item.type === "text" && typeof item.text === "string") {
1420
+ textParts.push(item.text);
1421
+ }
1422
+ else if (item.type === "image" || item.type === "localImage") {
1423
+ imageCount += 1;
1424
+ }
1425
+ }
1426
+ return { text: textParts.join("\n"), imageCount };
1427
+ }
1428
+ function appendCodexThinkingMessage(messages, text, timestamp) {
1429
+ const normalized = text.trim();
1430
+ if (!normalized)
1431
+ return;
1432
+ messages.push({
1433
+ role: "assistant",
1434
+ content: [{ type: "thinking", thinking: normalized }],
1435
+ ...(timestamp ? { timestamp } : {}),
1436
+ });
1437
+ }
1438
+ function appendCodexOfficialToolResult(messages, id, name, content, timestamp) {
1439
+ appendToolResultMessage(messages, id, name, content, {
1440
+ ...(timestamp ? { timestamp } : {}),
1441
+ });
1442
+ }
1443
+ export function codexThreadToSessionHistory(thread) {
1444
+ const messages = [];
1445
+ const turns = arrayValue(asObject(thread)?.turns);
1446
+ let userTurnOrdinal = 0;
1447
+ for (const rawTurn of turns) {
1448
+ const turn = asObject(rawTurn);
1449
+ if (!turn)
1450
+ continue;
1451
+ const turnStartedAt = numberToIsoTimestamp(turn.startedAt);
1452
+ const turnCompletedAt = numberToIsoTimestamp(turn.completedAt);
1453
+ for (const rawItem of arrayValue(turn.items)) {
1454
+ const item = asObject(rawItem);
1455
+ if (!item || typeof item.type !== "string")
1456
+ continue;
1457
+ const itemId = stringValue(item.id) ?? `codex-item-${messages.length}`;
1458
+ const itemTimestamp = turnCompletedAt ?? turnStartedAt;
1459
+ switch (item.type) {
1460
+ case "userMessage": {
1461
+ const { text, imageCount } = codexUserInputTextAndImages(item.content);
1462
+ const displayText = text.trim().length > 0
1463
+ ? text
1464
+ : imageCount > 0
1465
+ ? `[Image attached${imageCount > 1 ? ` x${imageCount}` : ""}]`
1466
+ : "";
1467
+ if (displayText.trim().length === 0)
1468
+ break;
1469
+ userTurnOrdinal += 1;
1470
+ messages.push({
1471
+ role: "user",
1472
+ uuid: codexUserTurnUuid(userTurnOrdinal),
1473
+ content: [{ type: "text", text: displayText }],
1474
+ ...(imageCount > 0 ? { imageCount } : {}),
1475
+ ...(turnStartedAt ? { timestamp: turnStartedAt } : {}),
1476
+ });
1477
+ break;
1478
+ }
1479
+ case "agentMessage": {
1480
+ appendTextMessage(messages, "assistant", stringValue(item.text) ?? "", itemTimestamp);
1481
+ break;
1482
+ }
1483
+ case "plan": {
1484
+ appendTextMessage(messages, "assistant", stringValue(item.text) ?? "", itemTimestamp);
1485
+ break;
1486
+ }
1487
+ case "reasoning": {
1488
+ const summary = arrayValue(item.summary)
1489
+ .filter((value) => typeof value === "string");
1490
+ const content = arrayValue(item.content)
1491
+ .filter((value) => typeof value === "string");
1492
+ appendCodexThinkingMessage(messages, [...summary, ...content].join("\n"), itemTimestamp);
1493
+ break;
1494
+ }
1495
+ case "commandExecution": {
1496
+ const command = stringValue(item.command) ?? "";
1497
+ appendToolUseMessage(messages, itemId, "Bash", {
1498
+ command,
1499
+ ...(typeof item.cwd === "string" ? { cwd: item.cwd } : {}),
1500
+ });
1501
+ const outputParts = [];
1502
+ if (typeof item.status === "string") {
1503
+ outputParts.push(`status: ${item.status}`);
1504
+ }
1505
+ if (typeof item.exitCode === "number") {
1506
+ outputParts.push(`exitCode: ${item.exitCode}`);
1507
+ }
1508
+ if (typeof item.aggregatedOutput === "string") {
1509
+ outputParts.push(item.aggregatedOutput);
1510
+ }
1511
+ appendCodexOfficialToolResult(messages, itemId, "Bash", outputParts.join("\n").trim(), itemTimestamp);
1512
+ break;
1513
+ }
1514
+ case "fileChange": {
1515
+ appendToolUseMessage(messages, itemId, "FileChange", {
1516
+ changes: Array.isArray(item.changes) ? item.changes : [],
1517
+ ...(typeof item.status === "string" ? { status: item.status } : {}),
1518
+ });
1519
+ break;
1520
+ }
1521
+ case "mcpToolCall": {
1522
+ const server = stringValue(item.server) ?? "mcp";
1523
+ const tool = stringValue(item.tool) ?? "tool";
1524
+ appendToolUseMessage(messages, itemId, `mcp:${server}/${tool}`, {
1525
+ arguments: item.arguments ?? {},
1526
+ ...(typeof item.status === "string" ? { status: item.status } : {}),
1527
+ });
1528
+ if (item.result != null || item.error != null) {
1529
+ const normalized = normalizeCodexMcpResult(item.result ?? item.error);
1530
+ appendToolResultMessage(messages, itemId, `mcp:${server}/${tool}`, normalized.content, {
1531
+ imageBase64: normalized.imageBase64,
1532
+ ...(itemTimestamp ? { timestamp: itemTimestamp } : {}),
1533
+ });
1534
+ }
1535
+ break;
1536
+ }
1537
+ case "dynamicToolCall": {
1538
+ const tool = stringValue(item.tool) ?? "tool";
1539
+ appendToolUseMessage(messages, itemId, tool, {
1540
+ arguments: item.arguments ?? {},
1541
+ ...(typeof item.status === "string" ? { status: item.status } : {}),
1542
+ });
1543
+ const contentItems = arrayValue(item.contentItems);
1544
+ const resultText = contentItems
1545
+ .map((entry) => {
1546
+ const contentItem = asObject(entry);
1547
+ if (!contentItem)
1548
+ return "";
1549
+ if (contentItem.type === "inputText" &&
1550
+ typeof contentItem.text === "string") {
1551
+ return contentItem.text;
1552
+ }
1553
+ if (contentItem.type === "inputImage" &&
1554
+ typeof contentItem.imageUrl === "string") {
1555
+ return contentItem.imageUrl;
1556
+ }
1557
+ return codexToolResultContent(contentItem);
1558
+ })
1559
+ .filter(Boolean)
1560
+ .join("\n");
1561
+ appendCodexOfficialToolResult(messages, itemId, tool, resultText, itemTimestamp);
1562
+ break;
1563
+ }
1564
+ case "webSearch": {
1565
+ appendToolUseMessage(messages, itemId, "WebSearch", {
1566
+ query: stringValue(item.query) ?? "",
1567
+ ...(item.action != null ? { action: item.action } : {}),
1568
+ });
1569
+ break;
1570
+ }
1571
+ case "imageGeneration": {
1572
+ appendToolUseMessage(messages, itemId, "ImageGeneration", {
1573
+ ...(typeof item.status === "string" ? { status: item.status } : {}),
1574
+ ...(typeof item.revisedPrompt === "string"
1575
+ ? { revisedPrompt: item.revisedPrompt }
1576
+ : {}),
1577
+ });
1578
+ appendImageGenerationResult(messages, {
1579
+ id: itemId,
1580
+ status: item.status,
1581
+ revisedPrompt: item.revisedPrompt,
1582
+ savedPath: item.savedPath,
1583
+ result: item.result,
1584
+ }, itemId, itemTimestamp);
1585
+ break;
1586
+ }
1587
+ case "enteredReviewMode":
1588
+ case "exitedReviewMode": {
1589
+ appendTextMessage(messages, "assistant", stringValue(item.review) ?? "", itemTimestamp);
1590
+ break;
1591
+ }
1592
+ default:
1593
+ break;
1594
+ }
1595
+ }
1596
+ }
1597
+ return messages;
1598
+ }
1351
1599
  function asObject(value) {
1352
1600
  if (!value || typeof value !== "object" || Array.isArray(value)) {
1353
1601
  return null;