@jingyi0605/codingns 0.1.5 → 0.2.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/dist/public/assets/{TerminalPage-4p6EBqrR.js → TerminalPage-BlbQuWi1.js} +1 -1
- package/dist/public/assets/index-1VIm8lVL.css +1 -0
- package/dist/public/assets/index-Dti93O2S.js +109 -0
- package/dist/public/index.html +2 -2
- package/dist/server/config/env.d.ts +1 -0
- package/dist/server/config/env.js +5 -1
- package/dist/server/config/env.js.map +1 -1
- package/dist/server/modules/file/file-controller.d.ts +12 -1
- package/dist/server/modules/file/file-controller.js +72 -1
- package/dist/server/modules/file/file-controller.js.map +1 -1
- package/dist/server/modules/file/file-preview-link-service.d.ts +22 -0
- package/dist/server/modules/file/file-preview-link-service.js +160 -0
- package/dist/server/modules/file/file-preview-link-service.js.map +1 -0
- package/dist/server/modules/sessions/codex-app-server-helper-client.d.ts +2 -1
- package/dist/server/modules/sessions/codex-app-server-helper-client.js +103 -0
- package/dist/server/modules/sessions/codex-app-server-helper-client.js.map +1 -1
- package/dist/server/modules/sessions/codex-app-server-helper-process.js +106 -1
- package/dist/server/modules/sessions/codex-app-server-helper-process.js.map +1 -1
- package/dist/server/modules/sessions/session-controller.d.ts +21 -0
- package/dist/server/modules/sessions/session-controller.js +23 -1
- package/dist/server/modules/sessions/session-controller.js.map +1 -1
- package/dist/server/modules/sessions/session-history-service.d.ts +34 -2
- package/dist/server/modules/sessions/session-history-service.js +591 -27
- package/dist/server/modules/sessions/session-history-service.js.map +1 -1
- package/dist/server/modules/sessions/session-live-runtime-service.d.ts +5 -0
- package/dist/server/modules/sessions/session-live-runtime-service.js +59 -2
- package/dist/server/modules/sessions/session-live-runtime-service.js.map +1 -1
- package/dist/server/modules/sessions/session-provider-error-mapper.js +66 -0
- package/dist/server/modules/sessions/session-provider-error-mapper.js.map +1 -1
- package/dist/server/routes/files.js +2 -0
- package/dist/server/routes/files.js.map +1 -1
- package/dist/server/routes/sessions.js +1 -0
- package/dist/server/routes/sessions.js.map +1 -1
- package/dist/server/server/create-server.d.ts +4 -0
- package/dist/server/server/create-server.js +7 -2
- package/dist/server/server/create-server.js.map +1 -1
- package/dist/server/shared/utils/command-availability.d.ts +1 -0
- package/dist/server/shared/utils/command-availability.js +83 -0
- package/dist/server/shared/utils/command-availability.js.map +1 -0
- package/dist/server/storage/repositories/session-fork-repository.d.ts +8 -0
- package/dist/server/storage/repositories/session-fork-repository.js +69 -0
- package/dist/server/storage/repositories/session-fork-repository.js.map +1 -0
- package/dist/server/storage/repositories/session-index-repository.js +40 -2
- package/dist/server/storage/repositories/session-index-repository.js.map +1 -1
- package/dist/server/storage/sqlite/client.js +107 -0
- package/dist/server/storage/sqlite/client.js.map +1 -1
- package/dist/server/storage/sqlite/schema.sql +30 -0
- package/dist/server/types/domain.d.ts +25 -1
- package/node_modules/@codingns/session-sync-core/dist/providers/claude-code.d.ts +6 -1
- package/node_modules/@codingns/session-sync-core/dist/providers/claude-code.js +228 -7
- package/node_modules/@codingns/session-sync-core/dist/providers/claude-code.js.map +1 -1
- package/node_modules/@codingns/session-sync-core/dist/providers/codex.d.ts +26 -1
- package/node_modules/@codingns/session-sync-core/dist/providers/codex.js +495 -2
- package/node_modules/@codingns/session-sync-core/dist/providers/codex.js.map +1 -1
- package/node_modules/@codingns/session-sync-core/dist/providers/gemini.js +94 -5
- package/node_modules/@codingns/session-sync-core/dist/providers/gemini.js.map +1 -1
- package/node_modules/@codingns/session-sync-core/dist/providers/opencode.d.ts +5 -1
- package/node_modules/@codingns/session-sync-core/dist/providers/opencode.js +269 -3
- package/node_modules/@codingns/session-sync-core/dist/providers/opencode.js.map +1 -1
- package/node_modules/@codingns/session-sync-core/dist/providers/utils.d.ts +1 -0
- package/node_modules/@codingns/session-sync-core/dist/providers/utils.js +117 -17
- package/node_modules/@codingns/session-sync-core/dist/providers/utils.js.map +1 -1
- package/node_modules/@codingns/session-sync-core/dist/runtime/codex-runtime.d.ts +10 -0
- package/node_modules/@codingns/session-sync-core/dist/runtime/codex-runtime.js +128 -8
- package/node_modules/@codingns/session-sync-core/dist/runtime/codex-runtime.js.map +1 -1
- package/node_modules/@codingns/session-sync-core/dist/services.d.ts +2 -1
- package/node_modules/@codingns/session-sync-core/dist/services.js +55 -8
- package/node_modules/@codingns/session-sync-core/dist/services.js.map +1 -1
- package/node_modules/@codingns/session-sync-core/dist/types.d.ts +27 -0
- package/package.json +1 -1
- package/dist/public/assets/index-CxeghocY.css +0 -1
- package/dist/public/assets/index-DXusStl0.js +0 -108
|
@@ -317,6 +317,80 @@ export class CodexAdapter {
|
|
|
317
317
|
initialCursor: encodeCursor(options.initialPrompt ? 1 : 0)
|
|
318
318
|
};
|
|
319
319
|
}
|
|
320
|
+
async forkSession(providerSessionId, workspacePath, options) {
|
|
321
|
+
const transportFactory = this.options.forkTransportFactory;
|
|
322
|
+
if (!transportFactory) {
|
|
323
|
+
throw new Error("CODEX_FORK_TRANSPORT_NOT_CONFIGURED");
|
|
324
|
+
}
|
|
325
|
+
const transport = transportFactory();
|
|
326
|
+
try {
|
|
327
|
+
await transport.initialize();
|
|
328
|
+
if (options.sourceType === "session") {
|
|
329
|
+
const forked = await transport.forkThread(providerSessionId);
|
|
330
|
+
return await this.buildForkResultFromTransport({
|
|
331
|
+
providerSessionId: forked.providerSessionId,
|
|
332
|
+
rawStoreRef: forked.rawStoreRef,
|
|
333
|
+
workspacePath,
|
|
334
|
+
fallbackParentProviderSessionId: providerSessionId,
|
|
335
|
+
forkMethod: "native_session_fork",
|
|
336
|
+
forkSourceType: "session",
|
|
337
|
+
providerSourceMessageId: null
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
const targetMessageId = options.sourceMessageId?.trim();
|
|
341
|
+
if (!targetMessageId) {
|
|
342
|
+
throw new Error("FORK_SOURCE_MESSAGE_ID_REQUIRED");
|
|
343
|
+
}
|
|
344
|
+
if (options.strategy === "reconstruct-only") {
|
|
345
|
+
throw new Error("CODEX_RECONSTRUCTED_MESSAGE_FORK_NOT_SUPPORTED");
|
|
346
|
+
}
|
|
347
|
+
const resolvedStoreRef = this.resolveSessionFilePath(options.rawStoreRef, providerSessionId);
|
|
348
|
+
const parsedMessages = this.getParsedMessages(resolvedStoreRef, providerSessionId);
|
|
349
|
+
const targetMessage = parsedMessages.find((message) => message.messageId === targetMessageId);
|
|
350
|
+
if (!targetMessage) {
|
|
351
|
+
throw new Error("FORK_SOURCE_MESSAGE_NOT_FOUND");
|
|
352
|
+
}
|
|
353
|
+
const threadReadResult = await transport.readThread(providerSessionId);
|
|
354
|
+
const threadSnapshot = extractCodexThreadHistorySnapshot(threadReadResult);
|
|
355
|
+
if (threadSnapshot.kind === "turns") {
|
|
356
|
+
const rollbackPlan = buildCodexTurnRollbackPlan(threadSnapshot, parsedMessages, targetMessage);
|
|
357
|
+
const forked = await transport.forkThread(providerSessionId);
|
|
358
|
+
const finalized = rollbackPlan.numTurnsToRollback > 0
|
|
359
|
+
? await transport.rollbackThread(forked.providerSessionId, rollbackPlan.numTurnsToRollback)
|
|
360
|
+
: forked;
|
|
361
|
+
return await this.buildForkResultFromTransport({
|
|
362
|
+
providerSessionId: finalized.providerSessionId,
|
|
363
|
+
rawStoreRef: finalized.rawStoreRef,
|
|
364
|
+
workspacePath,
|
|
365
|
+
fallbackParentProviderSessionId: providerSessionId,
|
|
366
|
+
forkMethod: "native_message_fork",
|
|
367
|
+
forkSourceType: "message",
|
|
368
|
+
providerSourceMessageId: null
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
const truncatedHistory = truncateCodexThreadHistory(threadSnapshot.value, parsedMessages, targetMessage);
|
|
372
|
+
if (truncatedHistory.length === 0) {
|
|
373
|
+
throw new Error("CODEX_FORK_HISTORY_EMPTY");
|
|
374
|
+
}
|
|
375
|
+
const resumed = await transport.resumeThreadFromHistory({
|
|
376
|
+
providerSessionId: null,
|
|
377
|
+
workspacePath,
|
|
378
|
+
history: truncatedHistory
|
|
379
|
+
});
|
|
380
|
+
return await this.buildForkResultFromTransport({
|
|
381
|
+
providerSessionId: resumed.providerSessionId,
|
|
382
|
+
rawStoreRef: resumed.rawStoreRef,
|
|
383
|
+
workspacePath,
|
|
384
|
+
fallbackParentProviderSessionId: providerSessionId,
|
|
385
|
+
forkMethod: "native_message_fork",
|
|
386
|
+
forkSourceType: "message",
|
|
387
|
+
providerSourceMessageId: null
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
finally {
|
|
391
|
+
transport.close();
|
|
392
|
+
}
|
|
393
|
+
}
|
|
320
394
|
async sendMessage(providerSessionId, rawStoreRef, content, clientRequestId, _permissionMode) {
|
|
321
395
|
const resolvedStoreRef = this.resolveSessionFilePath(rawStoreRef, providerSessionId);
|
|
322
396
|
const lineNumber = readJsonLines(resolvedStoreRef).length + 1;
|
|
@@ -439,6 +513,7 @@ export class CodexAdapter {
|
|
|
439
513
|
supportsAttachments: true,
|
|
440
514
|
supportsPermissionPrompt: true,
|
|
441
515
|
supportsCheckpoint: false,
|
|
516
|
+
supportsSessionFork: true,
|
|
442
517
|
limitations: [
|
|
443
518
|
"Codex 产品原生支持将指导加入队列,但当前 SDK 0.116.0 仍未向宿主暴露运行中 queue/steer 提交入口。",
|
|
444
519
|
"当前实现只维护原生会话文件,不负责直接驱动 Codex CLI 进程执行。"
|
|
@@ -693,6 +768,37 @@ export class CodexAdapter {
|
|
|
693
768
|
this.historyCache.delete(oldestKey);
|
|
694
769
|
}
|
|
695
770
|
}
|
|
771
|
+
async buildForkResultFromTransport(input) {
|
|
772
|
+
const resolvedStoreRef = input.rawStoreRef
|
|
773
|
+
? this.resolveSessionFilePath(input.rawStoreRef, input.providerSessionId)
|
|
774
|
+
: this.findSessionFileByThreadId(input.providerSessionId)
|
|
775
|
+
?? buildCodexActiveSessionPath(this.options.homeDir, `${input.providerSessionId}.jsonl`);
|
|
776
|
+
const messages = existsSync(resolvedStoreRef)
|
|
777
|
+
? this.getParsedMessages(resolvedStoreRef, input.providerSessionId)
|
|
778
|
+
: [];
|
|
779
|
+
const threadMetadataIndex = this.readThreadMetadataIndex();
|
|
780
|
+
const threadMetadata = threadMetadataIndex.get(input.providerSessionId) ?? null;
|
|
781
|
+
const title = this.resolveIndexedTitle(threadMetadataIndex, input.providerSessionId)
|
|
782
|
+
?? resolveCodexFallbackTitle(messages)
|
|
783
|
+
?? "";
|
|
784
|
+
return {
|
|
785
|
+
session: {
|
|
786
|
+
provider: this.providerId,
|
|
787
|
+
providerSessionId: input.providerSessionId,
|
|
788
|
+
title,
|
|
789
|
+
workspacePath: input.workspacePath,
|
|
790
|
+
rawStoreRef: resolvedStoreRef,
|
|
791
|
+
isArchived: resolveCodexArchivedState(threadMetadata, resolvedStoreRef),
|
|
792
|
+
lastMessageAt: messages.at(-1)?.timestamp ?? nextTimestamp(),
|
|
793
|
+
messageCount: messages.length,
|
|
794
|
+
parentProviderSessionId: input.fallbackParentProviderSessionId
|
|
795
|
+
},
|
|
796
|
+
forkMethod: input.forkMethod,
|
|
797
|
+
forkSourceType: input.forkSourceType,
|
|
798
|
+
inheritedPrefixMessageCount: messages.length,
|
|
799
|
+
providerSourceMessageId: input.providerSourceMessageId
|
|
800
|
+
};
|
|
801
|
+
}
|
|
696
802
|
touchSessionSummaryCache(filePath, entry) {
|
|
697
803
|
this.sessionSummaryCache.delete(filePath);
|
|
698
804
|
this.sessionSummaryCache.set(filePath, entry);
|
|
@@ -862,6 +968,7 @@ export class CodexAdapter {
|
|
|
862
968
|
return this.parseMessagesFromEntries(filePath, records, providerSessionId);
|
|
863
969
|
}
|
|
864
970
|
parseMessagesFromEntries(filePath, records, providerSessionId) {
|
|
971
|
+
const effectiveRecords = filterRolledBackCodexRecords(records);
|
|
865
972
|
const messages = [];
|
|
866
973
|
const messageIndexesByKey = new Map();
|
|
867
974
|
const toolNameById = new Map();
|
|
@@ -900,8 +1007,8 @@ export class CodexAdapter {
|
|
|
900
1007
|
}
|
|
901
1008
|
});
|
|
902
1009
|
};
|
|
903
|
-
|
|
904
|
-
const rawRef = createRawRef(this.providerId, filePath, lineNumber);
|
|
1010
|
+
effectiveRecords.forEach(({ lineNumber, partIndex, data: record }) => {
|
|
1011
|
+
const rawRef = createRawRef(this.providerId, filePath, lineNumber, partIndex || undefined);
|
|
905
1012
|
if (record.type === "event_msg") {
|
|
906
1013
|
const payload = (record.payload ?? {});
|
|
907
1014
|
const eventType = ensureText(payload.type);
|
|
@@ -1050,6 +1157,81 @@ export class CodexAdapter {
|
|
|
1050
1157
|
return messages.map((entry) => entry.message);
|
|
1051
1158
|
}
|
|
1052
1159
|
}
|
|
1160
|
+
function filterRolledBackCodexRecords(records) {
|
|
1161
|
+
const completedTurnSegments = [];
|
|
1162
|
+
let activeTurnStartLineNumber = null;
|
|
1163
|
+
let sawRollbackEvent = false;
|
|
1164
|
+
for (const recordEntry of records) {
|
|
1165
|
+
const record = recordEntry.data;
|
|
1166
|
+
if (record.type !== "event_msg") {
|
|
1167
|
+
continue;
|
|
1168
|
+
}
|
|
1169
|
+
const payload = (record.payload ?? {});
|
|
1170
|
+
const eventType = ensureText(payload.type).trim();
|
|
1171
|
+
if (eventType === "task_started") {
|
|
1172
|
+
activeTurnStartLineNumber = recordEntry.lineNumber;
|
|
1173
|
+
continue;
|
|
1174
|
+
}
|
|
1175
|
+
if (eventType === "task_complete" || eventType === "task_failed") {
|
|
1176
|
+
if (activeTurnStartLineNumber !== null) {
|
|
1177
|
+
completedTurnSegments.push({
|
|
1178
|
+
startLineNumber: activeTurnStartLineNumber,
|
|
1179
|
+
endLineNumber: recordEntry.lineNumber,
|
|
1180
|
+
rolledBack: false
|
|
1181
|
+
});
|
|
1182
|
+
}
|
|
1183
|
+
activeTurnStartLineNumber = null;
|
|
1184
|
+
continue;
|
|
1185
|
+
}
|
|
1186
|
+
if (eventType !== "thread_rolled_back") {
|
|
1187
|
+
continue;
|
|
1188
|
+
}
|
|
1189
|
+
sawRollbackEvent = true;
|
|
1190
|
+
const requestedTurnCount = Math.max(0, Math.trunc(typeof payload.num_turns === "number"
|
|
1191
|
+
? payload.num_turns
|
|
1192
|
+
: Number.parseInt(ensureText(payload.num_turns), 10)) || 0);
|
|
1193
|
+
if (requestedTurnCount <= 0) {
|
|
1194
|
+
continue;
|
|
1195
|
+
}
|
|
1196
|
+
let remainingTurnsToRollback = requestedTurnCount;
|
|
1197
|
+
for (let index = completedTurnSegments.length - 1; index >= 0; index -= 1) {
|
|
1198
|
+
const segment = completedTurnSegments[index];
|
|
1199
|
+
if (!segment || segment.rolledBack) {
|
|
1200
|
+
continue;
|
|
1201
|
+
}
|
|
1202
|
+
segment.rolledBack = true;
|
|
1203
|
+
remainingTurnsToRollback -= 1;
|
|
1204
|
+
if (remainingTurnsToRollback <= 0) {
|
|
1205
|
+
break;
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
if (!sawRollbackEvent) {
|
|
1210
|
+
return records;
|
|
1211
|
+
}
|
|
1212
|
+
const rolledBackSegments = completedTurnSegments
|
|
1213
|
+
.filter((segment) => segment.rolledBack)
|
|
1214
|
+
.sort((left, right) => left.startLineNumber - right.startLineNumber);
|
|
1215
|
+
if (rolledBackSegments.length === 0) {
|
|
1216
|
+
return records;
|
|
1217
|
+
}
|
|
1218
|
+
const filteredRecords = [];
|
|
1219
|
+
let segmentIndex = 0;
|
|
1220
|
+
for (const recordEntry of records) {
|
|
1221
|
+
while (segmentIndex < rolledBackSegments.length
|
|
1222
|
+
&& recordEntry.lineNumber > rolledBackSegments[segmentIndex].endLineNumber) {
|
|
1223
|
+
segmentIndex += 1;
|
|
1224
|
+
}
|
|
1225
|
+
const activeSegment = segmentIndex < rolledBackSegments.length ? rolledBackSegments[segmentIndex] : null;
|
|
1226
|
+
if (activeSegment
|
|
1227
|
+
&& recordEntry.lineNumber >= activeSegment.startLineNumber
|
|
1228
|
+
&& recordEntry.lineNumber <= activeSegment.endLineNumber) {
|
|
1229
|
+
continue;
|
|
1230
|
+
}
|
|
1231
|
+
filteredRecords.push(recordEntry);
|
|
1232
|
+
}
|
|
1233
|
+
return filteredRecords;
|
|
1234
|
+
}
|
|
1053
1235
|
function buildRecentHistoryPage(messages, totalMessageCount, limit) {
|
|
1054
1236
|
const effectiveTotal = Math.max(totalMessageCount, messages.length);
|
|
1055
1237
|
const pageMessages = messages.slice(-Math.min(limit, messages.length)).map((message, index, items) => ({
|
|
@@ -1365,4 +1547,315 @@ function normalizeCodexMessageTitle(content) {
|
|
|
1365
1547
|
const normalized = normalizeCodexIndexedTitle(content);
|
|
1366
1548
|
return normalized ? normalized.slice(0, CODEX_SESSION_TITLE_MAX_LENGTH) : null;
|
|
1367
1549
|
}
|
|
1550
|
+
function extractCodexThreadHistory(result) {
|
|
1551
|
+
const snapshot = extractCodexThreadHistorySnapshot(result);
|
|
1552
|
+
return snapshot.value;
|
|
1553
|
+
}
|
|
1554
|
+
function extractCodexThreadHistorySnapshot(result) {
|
|
1555
|
+
const directHistory = pickCodexHistoryArray(result)
|
|
1556
|
+
?? pickCodexHistoryArray(toRecord(result.thread))
|
|
1557
|
+
?? pickCodexHistoryArray(toRecord(result.data));
|
|
1558
|
+
if (directHistory) {
|
|
1559
|
+
return {
|
|
1560
|
+
kind: "entries",
|
|
1561
|
+
value: directHistory,
|
|
1562
|
+
comparableEntries: directHistory.flatMap((entry, entryIndex) => {
|
|
1563
|
+
const signature = buildCodexThreadHistorySignature(entry);
|
|
1564
|
+
return signature ? [{ signature, entryIndex }] : [];
|
|
1565
|
+
})
|
|
1566
|
+
};
|
|
1567
|
+
}
|
|
1568
|
+
const turns = pickCodexTurnArray(result.turns)
|
|
1569
|
+
?? pickCodexTurnArray(toRecord(result.thread)?.turns)
|
|
1570
|
+
?? pickCodexTurnArray(toRecord(result.data)?.turns);
|
|
1571
|
+
if (!turns) {
|
|
1572
|
+
throw new Error("CODEX_THREAD_HISTORY_MISSING");
|
|
1573
|
+
}
|
|
1574
|
+
const comparableEntries = turns.flatMap((turn, turnIndex) => collectCodexTurnComparableEntries(turn, turnIndex));
|
|
1575
|
+
if (comparableEntries.length === 0) {
|
|
1576
|
+
throw new Error("CODEX_THREAD_HISTORY_MISSING");
|
|
1577
|
+
}
|
|
1578
|
+
return {
|
|
1579
|
+
kind: "turns",
|
|
1580
|
+
value: turns,
|
|
1581
|
+
comparableEntries
|
|
1582
|
+
};
|
|
1583
|
+
}
|
|
1584
|
+
function isSyntheticCodexSessionTitle(title) {
|
|
1585
|
+
const normalizedTitle = ensureText(title).trim();
|
|
1586
|
+
if (normalizedTitle.length === 0) {
|
|
1587
|
+
return false;
|
|
1588
|
+
}
|
|
1589
|
+
return (/^rollout-\d{4}-\d{2}-\d{2}t/i.test(normalizedTitle) ||
|
|
1590
|
+
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(normalizedTitle));
|
|
1591
|
+
}
|
|
1592
|
+
function pickCodexHistoryArray(value) {
|
|
1593
|
+
const record = toRecord(value);
|
|
1594
|
+
if (!record) {
|
|
1595
|
+
return null;
|
|
1596
|
+
}
|
|
1597
|
+
for (const key of ["history", "items"]) {
|
|
1598
|
+
const candidate = record[key];
|
|
1599
|
+
if (Array.isArray(candidate)
|
|
1600
|
+
&& candidate.some((entry) => buildCodexThreadHistorySignature(entry) !== null)) {
|
|
1601
|
+
return candidate;
|
|
1602
|
+
}
|
|
1603
|
+
}
|
|
1604
|
+
return null;
|
|
1605
|
+
}
|
|
1606
|
+
function pickCodexTurnArray(value) {
|
|
1607
|
+
return Array.isArray(value) ? value : null;
|
|
1608
|
+
}
|
|
1609
|
+
function collectCodexTurnComparableEntries(value, turnIndex, parentPath = []) {
|
|
1610
|
+
const record = toRecord(value);
|
|
1611
|
+
if (!record) {
|
|
1612
|
+
return [];
|
|
1613
|
+
}
|
|
1614
|
+
for (const key of ["history", "items"]) {
|
|
1615
|
+
const candidate = record[key];
|
|
1616
|
+
if (Array.isArray(candidate)
|
|
1617
|
+
&& candidate.some((entry) => buildCodexThreadHistorySignature(entry) !== null)) {
|
|
1618
|
+
const containerPath = [...parentPath, key];
|
|
1619
|
+
return candidate.flatMap((entry, entryIndex) => {
|
|
1620
|
+
const signature = buildCodexThreadHistorySignature(entry);
|
|
1621
|
+
return signature
|
|
1622
|
+
? [{
|
|
1623
|
+
signature,
|
|
1624
|
+
turnIndex,
|
|
1625
|
+
containerPath,
|
|
1626
|
+
entryIndex
|
|
1627
|
+
}]
|
|
1628
|
+
: [];
|
|
1629
|
+
});
|
|
1630
|
+
}
|
|
1631
|
+
}
|
|
1632
|
+
return [
|
|
1633
|
+
["input", record.input],
|
|
1634
|
+
["output", record.output],
|
|
1635
|
+
["turn", record.turn],
|
|
1636
|
+
["data", record.data],
|
|
1637
|
+
["result", record.result]
|
|
1638
|
+
].flatMap(([key, candidate]) => collectCodexTurnComparableEntries(candidate, turnIndex, [...parentPath, key]));
|
|
1639
|
+
}
|
|
1640
|
+
function toRecord(value) {
|
|
1641
|
+
return typeof value === "object" && value !== null
|
|
1642
|
+
? value
|
|
1643
|
+
: null;
|
|
1644
|
+
}
|
|
1645
|
+
function truncateCodexThreadHistory(history, parsedMessages, targetMessage) {
|
|
1646
|
+
const snapshot = normalizeCodexThreadHistorySnapshot(history);
|
|
1647
|
+
if (snapshot.kind === "entries") {
|
|
1648
|
+
const targetEntry = resolveCodexThreadHistoryTargetEntry(snapshot, parsedMessages, targetMessage);
|
|
1649
|
+
return snapshot.value.slice(0, targetEntry.entryIndex + 1);
|
|
1650
|
+
}
|
|
1651
|
+
const targetEntry = resolveCodexThreadHistoryTargetEntry(snapshot, parsedMessages, targetMessage);
|
|
1652
|
+
const truncatedTurns = snapshot.value.slice(0, targetEntry.turnIndex + 1);
|
|
1653
|
+
const lastTurn = truncatedTurns.at(-1);
|
|
1654
|
+
if (!lastTurn) {
|
|
1655
|
+
throw new Error("CODEX_FORK_SOURCE_MESSAGE_UNMAPPABLE");
|
|
1656
|
+
}
|
|
1657
|
+
return [
|
|
1658
|
+
...flattenCodexTurnHistory(truncatedTurns.slice(0, -1)),
|
|
1659
|
+
...collectCodexTurnHistoryItems(truncateCodexTurnAtPath(lastTurn, targetEntry.containerPath, targetEntry.entryIndex))
|
|
1660
|
+
];
|
|
1661
|
+
}
|
|
1662
|
+
function normalizeCodexThreadHistorySnapshot(history) {
|
|
1663
|
+
const directComparableEntries = history.flatMap((entry, entryIndex) => {
|
|
1664
|
+
const signature = buildCodexThreadHistorySignature(entry);
|
|
1665
|
+
return signature ? [{ signature, entryIndex }] : [];
|
|
1666
|
+
});
|
|
1667
|
+
if (directComparableEntries.length > 0) {
|
|
1668
|
+
return {
|
|
1669
|
+
kind: "entries",
|
|
1670
|
+
value: history,
|
|
1671
|
+
comparableEntries: directComparableEntries
|
|
1672
|
+
};
|
|
1673
|
+
}
|
|
1674
|
+
const turnComparableEntries = history.flatMap((turn, turnIndex) => collectCodexTurnComparableEntries(turn, turnIndex));
|
|
1675
|
+
if (turnComparableEntries.length > 0) {
|
|
1676
|
+
return {
|
|
1677
|
+
kind: "turns",
|
|
1678
|
+
value: history,
|
|
1679
|
+
comparableEntries: turnComparableEntries
|
|
1680
|
+
};
|
|
1681
|
+
}
|
|
1682
|
+
throw new Error("CODEX_THREAD_HISTORY_MISSING");
|
|
1683
|
+
}
|
|
1684
|
+
function resolveCodexThreadHistoryTargetEntry(snapshot, parsedMessages, targetMessage) {
|
|
1685
|
+
const targetSignature = buildCodexThreadHistorySignature(targetMessage);
|
|
1686
|
+
if (!targetSignature) {
|
|
1687
|
+
throw new Error("CODEX_FORK_SOURCE_MESSAGE_UNMAPPABLE");
|
|
1688
|
+
}
|
|
1689
|
+
const matchingEntries = snapshot.comparableEntries.filter((entry) => entry.signature === targetSignature);
|
|
1690
|
+
if (matchingEntries.length === 0) {
|
|
1691
|
+
throw new Error("CODEX_FORK_SOURCE_MESSAGE_UNMAPPABLE");
|
|
1692
|
+
}
|
|
1693
|
+
const targetOccurrence = resolveCodexMessageSignatureOccurrence(parsedMessages, targetMessage);
|
|
1694
|
+
return matchingEntries[Math.min(targetOccurrence, matchingEntries.length) - 1] ?? matchingEntries.at(-1);
|
|
1695
|
+
}
|
|
1696
|
+
function resolveCodexMessageSignatureOccurrence(parsedMessages, targetMessage) {
|
|
1697
|
+
const targetSignature = buildCodexThreadHistorySignature(targetMessage);
|
|
1698
|
+
if (!targetSignature) {
|
|
1699
|
+
return 1;
|
|
1700
|
+
}
|
|
1701
|
+
let occurrence = 0;
|
|
1702
|
+
for (const message of parsedMessages) {
|
|
1703
|
+
if (buildCodexThreadHistorySignature(message) !== targetSignature) {
|
|
1704
|
+
continue;
|
|
1705
|
+
}
|
|
1706
|
+
occurrence += 1;
|
|
1707
|
+
if (message.messageId === targetMessage.messageId) {
|
|
1708
|
+
return occurrence;
|
|
1709
|
+
}
|
|
1710
|
+
}
|
|
1711
|
+
return Math.max(1, occurrence);
|
|
1712
|
+
}
|
|
1713
|
+
function buildCodexTurnRollbackPlan(snapshot, parsedMessages, targetMessage) {
|
|
1714
|
+
const targetEntry = resolveCodexThreadHistoryTargetEntry(snapshot, parsedMessages, targetMessage);
|
|
1715
|
+
const turnEntries = snapshot.comparableEntries.filter((entry) => entry.turnIndex === targetEntry.turnIndex);
|
|
1716
|
+
const lastComparableEntryInTurn = turnEntries.at(-1) ?? null;
|
|
1717
|
+
if (!lastComparableEntryInTurn) {
|
|
1718
|
+
throw new Error("CODEX_FORK_SOURCE_MESSAGE_UNMAPPABLE");
|
|
1719
|
+
}
|
|
1720
|
+
if (lastComparableEntryInTurn.containerPath.join("/") !== targetEntry.containerPath.join("/")
|
|
1721
|
+
|| lastComparableEntryInTurn.entryIndex !== targetEntry.entryIndex) {
|
|
1722
|
+
throw new Error("CODEX_MESSAGE_FORK_TURN_BOUNDARY_REQUIRED");
|
|
1723
|
+
}
|
|
1724
|
+
return {
|
|
1725
|
+
targetTurnIndex: targetEntry.turnIndex,
|
|
1726
|
+
numTurnsToRollback: Math.max(0, snapshot.value.length - targetEntry.turnIndex - 1)
|
|
1727
|
+
};
|
|
1728
|
+
}
|
|
1729
|
+
function truncateCodexTurnAtPath(turn, containerPath, entryIndex) {
|
|
1730
|
+
if (containerPath.length === 0) {
|
|
1731
|
+
return turn;
|
|
1732
|
+
}
|
|
1733
|
+
const record = toRecord(turn);
|
|
1734
|
+
if (!record) {
|
|
1735
|
+
return turn;
|
|
1736
|
+
}
|
|
1737
|
+
const [head, ...rest] = containerPath;
|
|
1738
|
+
const current = record[head];
|
|
1739
|
+
if (rest.length === 0) {
|
|
1740
|
+
if (!Array.isArray(current)) {
|
|
1741
|
+
return turn;
|
|
1742
|
+
}
|
|
1743
|
+
return {
|
|
1744
|
+
...record,
|
|
1745
|
+
[head]: current.slice(0, entryIndex + 1)
|
|
1746
|
+
};
|
|
1747
|
+
}
|
|
1748
|
+
return {
|
|
1749
|
+
...record,
|
|
1750
|
+
[head]: truncateCodexTurnAtPath(current, rest, entryIndex)
|
|
1751
|
+
};
|
|
1752
|
+
}
|
|
1753
|
+
function flattenCodexTurnHistory(turns) {
|
|
1754
|
+
return turns.flatMap((turn) => collectCodexTurnHistoryItems(turn));
|
|
1755
|
+
}
|
|
1756
|
+
function collectCodexTurnHistoryItems(value) {
|
|
1757
|
+
const direct = pickCodexHistoryArray(value);
|
|
1758
|
+
if (direct) {
|
|
1759
|
+
return direct;
|
|
1760
|
+
}
|
|
1761
|
+
const record = toRecord(value);
|
|
1762
|
+
if (!record) {
|
|
1763
|
+
return [];
|
|
1764
|
+
}
|
|
1765
|
+
return [
|
|
1766
|
+
record.input,
|
|
1767
|
+
record.output,
|
|
1768
|
+
record.turn,
|
|
1769
|
+
record.data,
|
|
1770
|
+
record.result
|
|
1771
|
+
].flatMap((candidate) => collectCodexTurnHistoryItems(candidate));
|
|
1772
|
+
}
|
|
1773
|
+
function buildCodexThreadHistorySignature(value) {
|
|
1774
|
+
if (!value || typeof value !== "object") {
|
|
1775
|
+
return null;
|
|
1776
|
+
}
|
|
1777
|
+
const item = value;
|
|
1778
|
+
const normalizedKind = ensureText(item.kind).trim();
|
|
1779
|
+
const normalizedRole = ensureText(item.role).trim();
|
|
1780
|
+
const normalizedContent = ensureText(item.content).trim();
|
|
1781
|
+
if (normalizedKind && normalizedRole) {
|
|
1782
|
+
return `${normalizedRole}:${normalizedKind}:${normalizedContent}`;
|
|
1783
|
+
}
|
|
1784
|
+
const type = ensureText(item.type).trim();
|
|
1785
|
+
if (type === "userMessage") {
|
|
1786
|
+
const content = stringifyCodexThreadMessageContent(item.content);
|
|
1787
|
+
return content ? `user:text:${content}` : null;
|
|
1788
|
+
}
|
|
1789
|
+
if (type === "agentMessage") {
|
|
1790
|
+
const content = ensureText(item.text).trim() || stringifyCodexThreadMessageContent(item.content);
|
|
1791
|
+
return content ? `assistant:text:${content}` : null;
|
|
1792
|
+
}
|
|
1793
|
+
if (type === "message") {
|
|
1794
|
+
const role = ensureText(item.role).trim();
|
|
1795
|
+
const content = stringifyCodexThreadMessageContent(item.content);
|
|
1796
|
+
if (!role || !content) {
|
|
1797
|
+
return null;
|
|
1798
|
+
}
|
|
1799
|
+
return `${role}:text:${content}`;
|
|
1800
|
+
}
|
|
1801
|
+
if (type === "reasoning") {
|
|
1802
|
+
const content = stringifyCodexReasoningContent(item.summary ?? item.content);
|
|
1803
|
+
return content ? `assistant:thinking:${content}` : null;
|
|
1804
|
+
}
|
|
1805
|
+
if (type === "function_call" || type === "tool_call") {
|
|
1806
|
+
const name = ensureText(item.name).trim();
|
|
1807
|
+
const inputValue = item.arguments ?? item.input;
|
|
1808
|
+
const content = stringifyStructuredValue(inputValue);
|
|
1809
|
+
return name || content ? `assistant:tool_call:${name}:${content}` : null;
|
|
1810
|
+
}
|
|
1811
|
+
if (type === "function_call_output" || type === "tool_result") {
|
|
1812
|
+
const content = ensureText(item.output ?? item.content).trim();
|
|
1813
|
+
return content ? `tool:tool_result:${content}` : null;
|
|
1814
|
+
}
|
|
1815
|
+
if (normalizedRole && normalizedContent) {
|
|
1816
|
+
return `${normalizedRole}:text:${normalizedContent}`;
|
|
1817
|
+
}
|
|
1818
|
+
return null;
|
|
1819
|
+
}
|
|
1820
|
+
function stringifyCodexThreadMessageContent(content) {
|
|
1821
|
+
if (typeof content === "string") {
|
|
1822
|
+
return content.trim();
|
|
1823
|
+
}
|
|
1824
|
+
if (!Array.isArray(content)) {
|
|
1825
|
+
return "";
|
|
1826
|
+
}
|
|
1827
|
+
return content
|
|
1828
|
+
.map((part) => {
|
|
1829
|
+
if (!part || typeof part !== "object") {
|
|
1830
|
+
return "";
|
|
1831
|
+
}
|
|
1832
|
+
const record = part;
|
|
1833
|
+
return ensureText(record.text
|
|
1834
|
+
?? record.input_text
|
|
1835
|
+
?? record.output_text
|
|
1836
|
+
?? (ensureText(record.type).trim() === "text" ? record.text : null)).trim();
|
|
1837
|
+
})
|
|
1838
|
+
.filter((value) => value.length > 0)
|
|
1839
|
+
.join("\n")
|
|
1840
|
+
.trim();
|
|
1841
|
+
}
|
|
1842
|
+
function stringifyCodexReasoningContent(content) {
|
|
1843
|
+
if (typeof content === "string") {
|
|
1844
|
+
return content.trim();
|
|
1845
|
+
}
|
|
1846
|
+
if (!Array.isArray(content)) {
|
|
1847
|
+
return "";
|
|
1848
|
+
}
|
|
1849
|
+
return content
|
|
1850
|
+
.map((part) => {
|
|
1851
|
+
if (!part || typeof part !== "object") {
|
|
1852
|
+
return "";
|
|
1853
|
+
}
|
|
1854
|
+
const record = part;
|
|
1855
|
+
return ensureText(record.text ?? record.summary_text).trim();
|
|
1856
|
+
})
|
|
1857
|
+
.filter((value) => value.length > 0)
|
|
1858
|
+
.join("\n")
|
|
1859
|
+
.trim();
|
|
1860
|
+
}
|
|
1368
1861
|
//# sourceMappingURL=codex.js.map
|