remote-codex 0.11.52 → 0.11.53
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/apps/supervisor-api/dist/index.js +511 -71
- package/apps/supervisor-web/dist/assets/{index-BBq6mr8o.js → index-Bnury8uP.js} +6 -6
- package/apps/supervisor-web/dist/index.html +1 -1
- package/bin/remote-codex.mjs +13 -2
- package/package.json +1 -1
- package/packages/codex/src/historyItems.test.ts +9 -0
- package/packages/codex/src/historyItems.ts +20 -8
- package/packages/codex/src/local-session-store.test.ts +455 -0
- package/packages/codex/src/local-session-store.ts +505 -40
- package/scripts/windows/build-device-manager.ps1 +0 -7
|
@@ -8595,15 +8595,23 @@ var DEFERRED_COMMAND_DETAIL_TITLE = "Command Output";
|
|
|
8595
8595
|
var DEFERRED_TOOL_DETAIL_TITLE = "Tool Call Details";
|
|
8596
8596
|
var DEFERRED_AGENT_TOOL_DETAIL_TITLE = "Agent Details";
|
|
8597
8597
|
function parseUuidV7Timestamp(id) {
|
|
8598
|
-
const
|
|
8599
|
-
|
|
8600
|
-
|
|
8601
|
-
|
|
8602
|
-
|
|
8603
|
-
|
|
8604
|
-
|
|
8598
|
+
const candidates = [
|
|
8599
|
+
id,
|
|
8600
|
+
...id.match(
|
|
8601
|
+
/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}|[0-9a-f]{32}/gi
|
|
8602
|
+
) ?? []
|
|
8603
|
+
];
|
|
8604
|
+
for (const candidate of candidates) {
|
|
8605
|
+
const normalized = candidate.replace(/-/g, "");
|
|
8606
|
+
if (!/^[0-9a-f]{32}$/i.test(normalized) || normalized[12]?.toLowerCase() !== "7") {
|
|
8607
|
+
continue;
|
|
8608
|
+
}
|
|
8609
|
+
const millis = Number.parseInt(normalized.slice(0, 12), 16);
|
|
8610
|
+
if (Number.isFinite(millis)) {
|
|
8611
|
+
return new Date(millis).toISOString();
|
|
8612
|
+
}
|
|
8605
8613
|
}
|
|
8606
|
-
return
|
|
8614
|
+
return null;
|
|
8607
8615
|
}
|
|
8608
8616
|
function isRecord4(value) {
|
|
8609
8617
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -8911,6 +8919,31 @@ function formatToolCallHistoryItem(item, deferredDetails, kind = "toolCall") {
|
|
|
8911
8919
|
}
|
|
8912
8920
|
return historyItem;
|
|
8913
8921
|
}
|
|
8922
|
+
function deferLargeHistoryItemDetails(turn, deferredDetails) {
|
|
8923
|
+
return {
|
|
8924
|
+
...turn,
|
|
8925
|
+
items: turn.items.map(
|
|
8926
|
+
(item) => item.kind === "commandExecution" ? deferCommandHistoryItem(
|
|
8927
|
+
item,
|
|
8928
|
+
deferredDetails
|
|
8929
|
+
) : item.kind === "toolCall" || item.kind === "agentToolCall" || item.kind === "skillToolCall" ? deferToolCallHistoryItem(
|
|
8930
|
+
item,
|
|
8931
|
+
deferredDetails
|
|
8932
|
+
) : item
|
|
8933
|
+
)
|
|
8934
|
+
};
|
|
8935
|
+
}
|
|
8936
|
+
function visibleRuntimeTurnItems(items) {
|
|
8937
|
+
const hasFinalAgentMessage = items.some(
|
|
8938
|
+
(item) => item.kind === "agentMessage" && !isTransientAgentHistoryItem(item)
|
|
8939
|
+
);
|
|
8940
|
+
if (!hasFinalAgentMessage) {
|
|
8941
|
+
return items;
|
|
8942
|
+
}
|
|
8943
|
+
return items.filter(
|
|
8944
|
+
(item) => !(item.kind === "agentMessage" && isTransientAgentHistoryItem(item))
|
|
8945
|
+
);
|
|
8946
|
+
}
|
|
8914
8947
|
function extractWebSearchQueries(item) {
|
|
8915
8948
|
const action = isRecord4(item.action) ? item.action : null;
|
|
8916
8949
|
const result = isRecord4(item.result) ? item.result : null;
|
|
@@ -9360,6 +9393,18 @@ function liveCodexItemToHistoryItem(item, phase) {
|
|
|
9360
9393
|
status: historyItem.status ?? (phase === "started" ? "running" : "completed")
|
|
9361
9394
|
};
|
|
9362
9395
|
}
|
|
9396
|
+
function agentTurnToThreadTurnDto(turn, deferredDetails) {
|
|
9397
|
+
const baseTurn = {
|
|
9398
|
+
id: turn.providerTurnId,
|
|
9399
|
+
startedAt: turn.startedAt ?? parseUuidV7Timestamp(turn.providerTurnId),
|
|
9400
|
+
status: turn.status,
|
|
9401
|
+
error: turn.error?.message ?? null,
|
|
9402
|
+
items: visibleRuntimeTurnItems(turn.items).map(
|
|
9403
|
+
(item, transcriptIndex) => item.transcriptOrder === transcriptIndex ? item : { ...item, transcriptOrder: transcriptIndex }
|
|
9404
|
+
)
|
|
9405
|
+
};
|
|
9406
|
+
return deferredDetails ? deferLargeHistoryItemDetails(baseTurn, deferredDetails) : baseTurn;
|
|
9407
|
+
}
|
|
9363
9408
|
function codexTurnToAgentTurn(turn) {
|
|
9364
9409
|
return {
|
|
9365
9410
|
providerTurnId: turn.id,
|
|
@@ -9374,6 +9419,10 @@ function codexTurnToAgentTurn(turn) {
|
|
|
9374
9419
|
}
|
|
9375
9420
|
|
|
9376
9421
|
// ../../packages/codex/src/local-session-store.ts
|
|
9422
|
+
import {
|
|
9423
|
+
unwatchFile,
|
|
9424
|
+
watchFile
|
|
9425
|
+
} from "fs";
|
|
9377
9426
|
import fs6 from "fs/promises";
|
|
9378
9427
|
import path7 from "path";
|
|
9379
9428
|
import Database2 from "better-sqlite3";
|
|
@@ -9391,6 +9440,76 @@ function summarizeTitleFromTurns(turns) {
|
|
|
9391
9440
|
function createHistoryItemId(turnId, prefix, index) {
|
|
9392
9441
|
return `${turnId}-${prefix}-${index}`;
|
|
9393
9442
|
}
|
|
9443
|
+
function transcriptMessageText(payload) {
|
|
9444
|
+
if (!Array.isArray(payload?.content)) {
|
|
9445
|
+
return null;
|
|
9446
|
+
}
|
|
9447
|
+
const text2 = payload.content.filter(
|
|
9448
|
+
(content) => (content?.type === "input_text" || content?.type === "output_text") && typeof content.text === "string" && content.text.trim()
|
|
9449
|
+
).map((content) => content.text).join("\n\n");
|
|
9450
|
+
return text2 || null;
|
|
9451
|
+
}
|
|
9452
|
+
function camelCaseKey(key) {
|
|
9453
|
+
return key.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
|
|
9454
|
+
}
|
|
9455
|
+
function camelCaseValue(value) {
|
|
9456
|
+
if (Array.isArray(value)) {
|
|
9457
|
+
return value.map(camelCaseValue);
|
|
9458
|
+
}
|
|
9459
|
+
if (!value || typeof value !== "object") {
|
|
9460
|
+
return value;
|
|
9461
|
+
}
|
|
9462
|
+
return Object.fromEntries(
|
|
9463
|
+
Object.entries(value).map(([key, entry]) => [
|
|
9464
|
+
camelCaseKey(key),
|
|
9465
|
+
camelCaseValue(entry)
|
|
9466
|
+
])
|
|
9467
|
+
);
|
|
9468
|
+
}
|
|
9469
|
+
function completedRolloutHistoryItem(payload, timestamp) {
|
|
9470
|
+
if (!payload?.item || typeof payload.item !== "object") {
|
|
9471
|
+
return null;
|
|
9472
|
+
}
|
|
9473
|
+
const normalized = camelCaseValue(payload.item);
|
|
9474
|
+
if (typeof normalized.id !== "string" || typeof normalized.type !== "string") {
|
|
9475
|
+
return null;
|
|
9476
|
+
}
|
|
9477
|
+
const normalizedType = normalized.type.charAt(0).toLowerCase() + normalized.type.slice(1);
|
|
9478
|
+
if (normalizedType === "userMessage" || normalizedType === "agentMessage") {
|
|
9479
|
+
return null;
|
|
9480
|
+
}
|
|
9481
|
+
if (normalizedType === "reasoning") {
|
|
9482
|
+
const summaryText = normalized.summaryText;
|
|
9483
|
+
normalized.summary = Array.isArray(summaryText) ? summaryText.filter((entry) => typeof entry === "string") : typeof summaryText === "string" && summaryText.trim() ? [summaryText] : [];
|
|
9484
|
+
const rawContent = normalized.rawContent;
|
|
9485
|
+
normalized.text = Array.isArray(rawContent) ? rawContent.map(
|
|
9486
|
+
(entry) => typeof entry === "string" ? entry : entry && typeof entry === "object" && typeof entry.text === "string" ? entry.text : ""
|
|
9487
|
+
).filter(Boolean).join("\n") : "";
|
|
9488
|
+
}
|
|
9489
|
+
const createdAtCandidate = typeof payload.started_at_ms === "number" ? payload.started_at_ms : timestamp ?? null;
|
|
9490
|
+
const agentTurn = codexTurnToAgentTurn({
|
|
9491
|
+
id: typeof payload.turn_id === "string" ? payload.turn_id : "rollout-turn",
|
|
9492
|
+
status: "inProgress",
|
|
9493
|
+
error: null,
|
|
9494
|
+
items: [
|
|
9495
|
+
{
|
|
9496
|
+
...normalized,
|
|
9497
|
+
id: normalized.id,
|
|
9498
|
+
type: normalizedType,
|
|
9499
|
+
createdAt: createdAtCandidate
|
|
9500
|
+
}
|
|
9501
|
+
]
|
|
9502
|
+
});
|
|
9503
|
+
return agentTurn.items[0] ?? null;
|
|
9504
|
+
}
|
|
9505
|
+
function appendUniqueTurnItem(turn, item) {
|
|
9506
|
+
const existingIndex = turn.items.findIndex((entry) => entry.id === item.id);
|
|
9507
|
+
if (existingIndex >= 0) {
|
|
9508
|
+
turn.items[existingIndex] = item;
|
|
9509
|
+
return;
|
|
9510
|
+
}
|
|
9511
|
+
turn.items.push(item);
|
|
9512
|
+
}
|
|
9394
9513
|
function finalizeTurn(turn, turns) {
|
|
9395
9514
|
if (!turn || turn.items.length === 0) {
|
|
9396
9515
|
return;
|
|
@@ -9403,7 +9522,98 @@ function finalizeTurn(turn, turns) {
|
|
|
9403
9522
|
items: turn.items
|
|
9404
9523
|
});
|
|
9405
9524
|
}
|
|
9525
|
+
function isoTimestampFromEpochSeconds(value) {
|
|
9526
|
+
return value === null || !Number.isFinite(value) ? null : new Date(value * 1e3).toISOString();
|
|
9527
|
+
}
|
|
9528
|
+
function normalizePaginatedTurnStatus(status) {
|
|
9529
|
+
switch (status) {
|
|
9530
|
+
case "completed":
|
|
9531
|
+
case "interrupted":
|
|
9532
|
+
case "failed":
|
|
9533
|
+
case "inProgress":
|
|
9534
|
+
return status;
|
|
9535
|
+
default:
|
|
9536
|
+
return "inProgress";
|
|
9537
|
+
}
|
|
9538
|
+
}
|
|
9539
|
+
function parsePaginatedTurnError(value) {
|
|
9540
|
+
if (!value) {
|
|
9541
|
+
return null;
|
|
9542
|
+
}
|
|
9543
|
+
try {
|
|
9544
|
+
const parsed = JSON.parse(value);
|
|
9545
|
+
if (parsed && typeof parsed === "object" && typeof parsed.message === "string") {
|
|
9546
|
+
return parsed;
|
|
9547
|
+
}
|
|
9548
|
+
return { message: value };
|
|
9549
|
+
} catch {
|
|
9550
|
+
return { message: value };
|
|
9551
|
+
}
|
|
9552
|
+
}
|
|
9553
|
+
function mergeSessionTurns(paginatedTurns, transcriptTurns) {
|
|
9554
|
+
if (paginatedTurns.length === 0) {
|
|
9555
|
+
return transcriptTurns;
|
|
9556
|
+
}
|
|
9557
|
+
const transcriptById = new Map(
|
|
9558
|
+
transcriptTurns.map((turn) => [turn.id, turn])
|
|
9559
|
+
);
|
|
9560
|
+
const merged = paginatedTurns.map((turn) => {
|
|
9561
|
+
const transcriptTurn = transcriptById.get(turn.id);
|
|
9562
|
+
if (!transcriptTurn) {
|
|
9563
|
+
return turn;
|
|
9564
|
+
}
|
|
9565
|
+
transcriptById.delete(turn.id);
|
|
9566
|
+
const paginatedItemIds = new Set(turn.items.map((item) => item.id));
|
|
9567
|
+
const missingTranscriptItems = transcriptTurn.items.filter(
|
|
9568
|
+
(item) => !paginatedItemIds.has(item.id)
|
|
9569
|
+
);
|
|
9570
|
+
const items = [...turn.items, ...missingTranscriptItems].map((item, index) => ({ item, index })).sort((left, right) => {
|
|
9571
|
+
const leftMillis = Date.parse(left.item.createdAt ?? "");
|
|
9572
|
+
const rightMillis = Date.parse(right.item.createdAt ?? "");
|
|
9573
|
+
if (Number.isFinite(leftMillis) && Number.isFinite(rightMillis)) {
|
|
9574
|
+
const delta = leftMillis - rightMillis;
|
|
9575
|
+
return delta === 0 ? left.index - right.index : delta;
|
|
9576
|
+
}
|
|
9577
|
+
if (Number.isFinite(leftMillis)) return -1;
|
|
9578
|
+
if (Number.isFinite(rightMillis)) return 1;
|
|
9579
|
+
return left.index - right.index;
|
|
9580
|
+
}).map((entry, transcriptOrder) => ({
|
|
9581
|
+
...entry.item,
|
|
9582
|
+
transcriptOrder
|
|
9583
|
+
}));
|
|
9584
|
+
return {
|
|
9585
|
+
...turn,
|
|
9586
|
+
startedAt: turn.startedAt ?? transcriptTurn.startedAt,
|
|
9587
|
+
status: transcriptTurn.status,
|
|
9588
|
+
error: transcriptTurn.error ?? turn.error,
|
|
9589
|
+
items
|
|
9590
|
+
};
|
|
9591
|
+
});
|
|
9592
|
+
merged.push(...transcriptById.values());
|
|
9593
|
+
return merged.sort(
|
|
9594
|
+
(left, right) => (left.startedAt ?? "").localeCompare(right.startedAt ?? "")
|
|
9595
|
+
);
|
|
9596
|
+
}
|
|
9406
9597
|
function parseTranscript(contents) {
|
|
9598
|
+
const entries = contents.split("\n").filter((line) => line.trim()).flatMap((line) => {
|
|
9599
|
+
try {
|
|
9600
|
+
return [JSON.parse(line)];
|
|
9601
|
+
} catch {
|
|
9602
|
+
return [];
|
|
9603
|
+
}
|
|
9604
|
+
});
|
|
9605
|
+
let transcriptSegmentIndex = -1;
|
|
9606
|
+
const indexedEntries = entries.map((entry) => {
|
|
9607
|
+
if (entry.type === "event_msg" && entry.payload?.type === "task_started") {
|
|
9608
|
+
transcriptSegmentIndex += 1;
|
|
9609
|
+
}
|
|
9610
|
+
return { entry, segmentIndex: transcriptSegmentIndex };
|
|
9611
|
+
});
|
|
9612
|
+
const legacyMessageSegments = new Set(
|
|
9613
|
+
indexedEntries.filter(
|
|
9614
|
+
({ entry }) => entry.type === "event_msg" && (entry.payload?.type === "user_message" || entry.payload?.type === "agent_message")
|
|
9615
|
+
).map(({ segmentIndex }) => segmentIndex)
|
|
9616
|
+
);
|
|
9407
9617
|
const turns = [];
|
|
9408
9618
|
let cwd = null;
|
|
9409
9619
|
let currentTurn = null;
|
|
@@ -9426,16 +9636,7 @@ function parseTranscript(contents) {
|
|
|
9426
9636
|
userItemCount = 0;
|
|
9427
9637
|
return currentTurn;
|
|
9428
9638
|
};
|
|
9429
|
-
for (const
|
|
9430
|
-
if (!line.trim()) {
|
|
9431
|
-
continue;
|
|
9432
|
-
}
|
|
9433
|
-
let entry;
|
|
9434
|
-
try {
|
|
9435
|
-
entry = JSON.parse(line);
|
|
9436
|
-
} catch {
|
|
9437
|
-
continue;
|
|
9438
|
-
}
|
|
9639
|
+
for (const { entry, segmentIndex } of indexedEntries) {
|
|
9439
9640
|
if (entry.type === "session_meta") {
|
|
9440
9641
|
const payload2 = entry.payload ?? {};
|
|
9441
9642
|
if (typeof payload2.cwd === "string" && payload2.cwd.trim()) {
|
|
@@ -9443,6 +9644,33 @@ function parseTranscript(contents) {
|
|
|
9443
9644
|
}
|
|
9444
9645
|
continue;
|
|
9445
9646
|
}
|
|
9647
|
+
if (!legacyMessageSegments.has(segmentIndex) && entry.type === "response_item" && entry.payload?.type === "message") {
|
|
9648
|
+
const payload2 = entry.payload;
|
|
9649
|
+
const text2 = transcriptMessageText(payload2);
|
|
9650
|
+
if (!text2 || payload2.role !== "user" && payload2.role !== "assistant") {
|
|
9651
|
+
continue;
|
|
9652
|
+
}
|
|
9653
|
+
const turn = ensureCurrentTurn(entry.timestamp);
|
|
9654
|
+
if (payload2.role === "user") {
|
|
9655
|
+
userItemCount += 1;
|
|
9656
|
+
turn.items.push({
|
|
9657
|
+
id: typeof payload2.id === "string" && payload2.id.trim() ? payload2.id : createHistoryItemId(turn.id, "user", userItemCount),
|
|
9658
|
+
kind: "userMessage",
|
|
9659
|
+
text: text2,
|
|
9660
|
+
createdAt: entry.timestamp ?? null
|
|
9661
|
+
});
|
|
9662
|
+
} else {
|
|
9663
|
+
agentItemCount += 1;
|
|
9664
|
+
turn.items.push({
|
|
9665
|
+
id: typeof payload2.id === "string" && payload2.id.trim() ? payload2.id : createHistoryItemId(turn.id, "agent", agentItemCount),
|
|
9666
|
+
kind: "agentMessage",
|
|
9667
|
+
text: text2,
|
|
9668
|
+
status: typeof payload2.phase === "string" ? payload2.phase : null,
|
|
9669
|
+
createdAt: entry.timestamp ?? null
|
|
9670
|
+
});
|
|
9671
|
+
}
|
|
9672
|
+
continue;
|
|
9673
|
+
}
|
|
9446
9674
|
if (entry.type !== "event_msg") {
|
|
9447
9675
|
continue;
|
|
9448
9676
|
}
|
|
@@ -9467,7 +9695,8 @@ function parseTranscript(contents) {
|
|
|
9467
9695
|
turn.items.push({
|
|
9468
9696
|
id: createHistoryItemId(turn.id, "user", userItemCount),
|
|
9469
9697
|
kind: "userMessage",
|
|
9470
|
-
text: payload.message
|
|
9698
|
+
text: payload.message,
|
|
9699
|
+
createdAt: entry.timestamp ?? null
|
|
9471
9700
|
});
|
|
9472
9701
|
continue;
|
|
9473
9702
|
}
|
|
@@ -9478,10 +9707,21 @@ function parseTranscript(contents) {
|
|
|
9478
9707
|
id: createHistoryItemId(turn.id, "agent", agentItemCount),
|
|
9479
9708
|
kind: "agentMessage",
|
|
9480
9709
|
text: payload.message,
|
|
9481
|
-
status: typeof payload.phase === "string" ? payload.phase : null
|
|
9710
|
+
status: typeof payload.phase === "string" ? payload.phase : null,
|
|
9711
|
+
createdAt: entry.timestamp ?? null
|
|
9482
9712
|
});
|
|
9483
9713
|
continue;
|
|
9484
9714
|
}
|
|
9715
|
+
if (payloadType === "item_completed") {
|
|
9716
|
+
const item = completedRolloutHistoryItem(
|
|
9717
|
+
payload,
|
|
9718
|
+
typeof entry.timestamp === "string" ? entry.timestamp : null
|
|
9719
|
+
);
|
|
9720
|
+
if (item) {
|
|
9721
|
+
appendUniqueTurnItem(ensureCurrentTurn(entry.timestamp), item);
|
|
9722
|
+
}
|
|
9723
|
+
continue;
|
|
9724
|
+
}
|
|
9485
9725
|
if (payloadType === "task_complete") {
|
|
9486
9726
|
const turn = ensureCurrentTurn(entry.timestamp);
|
|
9487
9727
|
turn.status = turn.error ? "failed" : "completed";
|
|
@@ -9511,10 +9751,14 @@ async function fileExists(filePath) {
|
|
|
9511
9751
|
}
|
|
9512
9752
|
}
|
|
9513
9753
|
var LocalCodexSessionStore = class {
|
|
9514
|
-
constructor(codexHome) {
|
|
9754
|
+
constructor(codexHome, options = {}) {
|
|
9515
9755
|
this.codexHome = codexHome;
|
|
9756
|
+
this.watchIntervalMs = options.watchIntervalMs ?? 500;
|
|
9757
|
+
this.watchThrottleMs = options.watchThrottleMs ?? 1500;
|
|
9516
9758
|
}
|
|
9517
9759
|
codexHome;
|
|
9760
|
+
watchIntervalMs;
|
|
9761
|
+
watchThrottleMs;
|
|
9518
9762
|
async findSession(sessionId) {
|
|
9519
9763
|
const stateRecord = await this.findSessionInStateDatabases(sessionId);
|
|
9520
9764
|
const transcriptPath = await this.resolveTranscriptPath(
|
|
@@ -9522,6 +9766,7 @@ var LocalCodexSessionStore = class {
|
|
|
9522
9766
|
sessionId
|
|
9523
9767
|
);
|
|
9524
9768
|
const transcript = transcriptPath ? parseTranscript(await fs6.readFile(transcriptPath, "utf8")) : null;
|
|
9769
|
+
const paginatedTurns = await this.findPaginatedTurns(sessionId);
|
|
9525
9770
|
const cwd = stateRecord?.cwd ?? transcript?.cwd ?? null;
|
|
9526
9771
|
if (!cwd) {
|
|
9527
9772
|
return null;
|
|
@@ -9532,7 +9777,57 @@ var LocalCodexSessionStore = class {
|
|
|
9532
9777
|
title: stateRecord?.title?.trim() || transcript?.title?.trim() || basenameFromPath(cwd),
|
|
9533
9778
|
model: stateRecord?.model ?? null,
|
|
9534
9779
|
rolloutPath: transcriptPath,
|
|
9535
|
-
turns: transcript?.turns ?? []
|
|
9780
|
+
turns: mergeSessionTurns(paginatedTurns, transcript?.turns ?? [])
|
|
9781
|
+
};
|
|
9782
|
+
}
|
|
9783
|
+
async watchSession(sessionId, onChange) {
|
|
9784
|
+
const stateRecord = await this.findSessionInStateDatabases(sessionId);
|
|
9785
|
+
const transcriptPath = await this.resolveTranscriptPath(
|
|
9786
|
+
stateRecord?.rolloutPath ?? null,
|
|
9787
|
+
sessionId
|
|
9788
|
+
);
|
|
9789
|
+
if (!transcriptPath) {
|
|
9790
|
+
return () => {
|
|
9791
|
+
};
|
|
9792
|
+
}
|
|
9793
|
+
let stopped = false;
|
|
9794
|
+
let lastEmittedAt = 0;
|
|
9795
|
+
let pending = null;
|
|
9796
|
+
const emit = () => {
|
|
9797
|
+
pending = null;
|
|
9798
|
+
if (stopped) {
|
|
9799
|
+
return;
|
|
9800
|
+
}
|
|
9801
|
+
lastEmittedAt = Date.now();
|
|
9802
|
+
onChange();
|
|
9803
|
+
};
|
|
9804
|
+
const schedule = () => {
|
|
9805
|
+
if (stopped || pending) {
|
|
9806
|
+
return;
|
|
9807
|
+
}
|
|
9808
|
+
const delay = Math.max(
|
|
9809
|
+
0,
|
|
9810
|
+
this.watchThrottleMs - (Date.now() - lastEmittedAt)
|
|
9811
|
+
);
|
|
9812
|
+
pending = setTimeout(emit, delay);
|
|
9813
|
+
};
|
|
9814
|
+
const listener = (current, previous) => {
|
|
9815
|
+
if (current.size !== previous.size || current.mtimeMs !== previous.mtimeMs) {
|
|
9816
|
+
schedule();
|
|
9817
|
+
}
|
|
9818
|
+
};
|
|
9819
|
+
watchFile(
|
|
9820
|
+
transcriptPath,
|
|
9821
|
+
{ persistent: false, interval: this.watchIntervalMs },
|
|
9822
|
+
listener
|
|
9823
|
+
);
|
|
9824
|
+
return () => {
|
|
9825
|
+
stopped = true;
|
|
9826
|
+
if (pending) {
|
|
9827
|
+
clearTimeout(pending);
|
|
9828
|
+
pending = null;
|
|
9829
|
+
}
|
|
9830
|
+
unwatchFile(transcriptPath, listener);
|
|
9536
9831
|
};
|
|
9537
9832
|
}
|
|
9538
9833
|
async findImportSession(sessionId, input) {
|
|
@@ -9557,29 +9852,36 @@ var LocalCodexSessionStore = class {
|
|
|
9557
9852
|
};
|
|
9558
9853
|
}
|
|
9559
9854
|
async findSessionInStateDatabases(sessionId) {
|
|
9560
|
-
|
|
9561
|
-
|
|
9562
|
-
|
|
9563
|
-
|
|
9564
|
-
|
|
9565
|
-
|
|
9566
|
-
|
|
9567
|
-
|
|
9568
|
-
|
|
9569
|
-
|
|
9570
|
-
|
|
9571
|
-
|
|
9572
|
-
|
|
9573
|
-
|
|
9574
|
-
|
|
9575
|
-
|
|
9855
|
+
const stateFiles = (await Promise.all(
|
|
9856
|
+
[this.codexHome, path7.join(this.codexHome, "sqlite")].map(
|
|
9857
|
+
async (directory) => {
|
|
9858
|
+
let entries;
|
|
9859
|
+
try {
|
|
9860
|
+
entries = await fs6.readdir(directory);
|
|
9861
|
+
} catch {
|
|
9862
|
+
return [];
|
|
9863
|
+
}
|
|
9864
|
+
return Promise.all(
|
|
9865
|
+
entries.filter((entry) => /^state_\d+\.sqlite$/i.test(entry)).map(async (entry) => {
|
|
9866
|
+
const absPath = path7.join(directory, entry);
|
|
9867
|
+
const stats = await fs6.stat(absPath);
|
|
9868
|
+
return {
|
|
9869
|
+
absPath,
|
|
9870
|
+
mtimeMs: stats.mtimeMs
|
|
9871
|
+
};
|
|
9872
|
+
})
|
|
9873
|
+
);
|
|
9874
|
+
}
|
|
9875
|
+
)
|
|
9876
|
+
)).flat();
|
|
9576
9877
|
stateFiles.sort((left, right) => right.mtimeMs - left.mtimeMs);
|
|
9577
9878
|
for (const stateFile of stateFiles) {
|
|
9578
|
-
|
|
9579
|
-
readonly: true,
|
|
9580
|
-
fileMustExist: true
|
|
9581
|
-
});
|
|
9879
|
+
let sqlite = null;
|
|
9582
9880
|
try {
|
|
9881
|
+
sqlite = new Database2(stateFile.absPath, {
|
|
9882
|
+
readonly: true,
|
|
9883
|
+
fileMustExist: true
|
|
9884
|
+
});
|
|
9583
9885
|
const row = sqlite.prepare(
|
|
9584
9886
|
`
|
|
9585
9887
|
SELECT
|
|
@@ -9598,11 +9900,91 @@ var LocalCodexSessionStore = class {
|
|
|
9598
9900
|
}
|
|
9599
9901
|
} catch {
|
|
9600
9902
|
} finally {
|
|
9601
|
-
sqlite
|
|
9903
|
+
sqlite?.close();
|
|
9602
9904
|
}
|
|
9603
9905
|
}
|
|
9604
9906
|
return null;
|
|
9605
9907
|
}
|
|
9908
|
+
async findPaginatedTurns(sessionId) {
|
|
9909
|
+
const databasePath = path7.join(this.codexHome, "thread_history_1.sqlite");
|
|
9910
|
+
let sqlite = null;
|
|
9911
|
+
try {
|
|
9912
|
+
sqlite = new Database2(databasePath, {
|
|
9913
|
+
readonly: true,
|
|
9914
|
+
fileMustExist: true
|
|
9915
|
+
});
|
|
9916
|
+
const turnRows = sqlite.prepare(
|
|
9917
|
+
`
|
|
9918
|
+
SELECT
|
|
9919
|
+
turn_id AS turnId,
|
|
9920
|
+
rollout_ordinal AS rolloutOrdinal,
|
|
9921
|
+
status,
|
|
9922
|
+
error_json AS errorJson,
|
|
9923
|
+
started_at AS startedAt
|
|
9924
|
+
FROM thread_turns
|
|
9925
|
+
WHERE thread_id = ?
|
|
9926
|
+
ORDER BY rollout_ordinal ASC
|
|
9927
|
+
`
|
|
9928
|
+
).all(sessionId);
|
|
9929
|
+
if (turnRows.length === 0) {
|
|
9930
|
+
return [];
|
|
9931
|
+
}
|
|
9932
|
+
const itemRows = sqlite.prepare(
|
|
9933
|
+
`
|
|
9934
|
+
SELECT
|
|
9935
|
+
turn_id AS turnId,
|
|
9936
|
+
rollout_ordinal AS rolloutOrdinal,
|
|
9937
|
+
created_at_ms AS createdAtMs,
|
|
9938
|
+
item_type AS itemType,
|
|
9939
|
+
item_json AS itemJson
|
|
9940
|
+
FROM thread_items
|
|
9941
|
+
WHERE thread_id = ?
|
|
9942
|
+
ORDER BY rollout_ordinal ASC
|
|
9943
|
+
`
|
|
9944
|
+
).all(sessionId);
|
|
9945
|
+
const itemsByTurnId = /* @__PURE__ */ new Map();
|
|
9946
|
+
for (const row of itemRows) {
|
|
9947
|
+
let parsed;
|
|
9948
|
+
try {
|
|
9949
|
+
parsed = JSON.parse(row.itemJson);
|
|
9950
|
+
} catch {
|
|
9951
|
+
continue;
|
|
9952
|
+
}
|
|
9953
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
9954
|
+
continue;
|
|
9955
|
+
}
|
|
9956
|
+
const parsedRecord = parsed;
|
|
9957
|
+
if (typeof parsedRecord.id !== "string" || !parsedRecord.id.trim()) {
|
|
9958
|
+
continue;
|
|
9959
|
+
}
|
|
9960
|
+
const item = {
|
|
9961
|
+
...parsedRecord,
|
|
9962
|
+
id: parsedRecord.id,
|
|
9963
|
+
type: typeof parsedRecord.type === "string" ? parsedRecord.type : row.itemType,
|
|
9964
|
+
createdAt: row.createdAtMs
|
|
9965
|
+
};
|
|
9966
|
+
const turnItems = itemsByTurnId.get(row.turnId) ?? [];
|
|
9967
|
+
turnItems.push(item);
|
|
9968
|
+
itemsByTurnId.set(row.turnId, turnItems);
|
|
9969
|
+
}
|
|
9970
|
+
return turnRows.map((row) => {
|
|
9971
|
+
const agentTurn = codexTurnToAgentTurn({
|
|
9972
|
+
id: row.turnId,
|
|
9973
|
+
status: normalizePaginatedTurnStatus(row.status),
|
|
9974
|
+
error: parsePaginatedTurnError(row.errorJson),
|
|
9975
|
+
items: itemsByTurnId.get(row.turnId) ?? []
|
|
9976
|
+
});
|
|
9977
|
+
return agentTurnToThreadTurnDto({
|
|
9978
|
+
...agentTurn,
|
|
9979
|
+
startedAt: isoTimestampFromEpochSeconds(row.startedAt)
|
|
9980
|
+
});
|
|
9981
|
+
});
|
|
9982
|
+
} catch {
|
|
9983
|
+
return [];
|
|
9984
|
+
} finally {
|
|
9985
|
+
sqlite?.close();
|
|
9986
|
+
}
|
|
9987
|
+
}
|
|
9606
9988
|
async resolveTranscriptPath(rolloutPath, sessionId) {
|
|
9607
9989
|
if (rolloutPath?.trim()) {
|
|
9608
9990
|
const absolutePath = path7.isAbsolute(rolloutPath) ? rolloutPath : path7.resolve(this.codexHome, rolloutPath);
|
|
@@ -25206,24 +25588,30 @@ var DEFERRED_FILE_READ_DETAIL_TITLE = "File Read Details";
|
|
|
25206
25588
|
var DEFERRED_WEB_SEARCH_DETAIL_TITLE = "Web Search Details";
|
|
25207
25589
|
var DEFERRED_HOOK_DETAIL_TITLE = "Hook Details";
|
|
25208
25590
|
function parseUuidV7Timestamp2(id) {
|
|
25209
|
-
const
|
|
25210
|
-
|
|
25211
|
-
|
|
25212
|
-
|
|
25213
|
-
|
|
25214
|
-
|
|
25215
|
-
|
|
25591
|
+
const candidates = [
|
|
25592
|
+
id,
|
|
25593
|
+
...id.match(
|
|
25594
|
+
/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}|[0-9a-f]{32}/gi
|
|
25595
|
+
) ?? []
|
|
25596
|
+
];
|
|
25597
|
+
for (const candidate of candidates) {
|
|
25598
|
+
const normalized = candidate.replace(/-/g, "");
|
|
25599
|
+
if (!/^[0-9a-f]{32}$/i.test(normalized) || normalized[12]?.toLowerCase() !== "7") {
|
|
25600
|
+
continue;
|
|
25601
|
+
}
|
|
25602
|
+
const millis = Number.parseInt(normalized.slice(0, 12), 16);
|
|
25603
|
+
if (Number.isFinite(millis)) {
|
|
25604
|
+
return new Date(millis).toISOString();
|
|
25605
|
+
}
|
|
25216
25606
|
}
|
|
25217
|
-
return
|
|
25607
|
+
return null;
|
|
25218
25608
|
}
|
|
25219
|
-
function normalizeHistoryItemCreatedAt(item
|
|
25609
|
+
function normalizeHistoryItemCreatedAt(item) {
|
|
25220
25610
|
if (item.createdAt) {
|
|
25221
25611
|
return item;
|
|
25222
25612
|
}
|
|
25223
|
-
|
|
25224
|
-
|
|
25225
|
-
createdAt: parseUuidV7Timestamp2(item.id) ?? fallback
|
|
25226
|
-
};
|
|
25613
|
+
const inferredCreatedAt = parseUuidV7Timestamp2(item.id);
|
|
25614
|
+
return inferredCreatedAt ? { ...item, createdAt: inferredCreatedAt } : item;
|
|
25227
25615
|
}
|
|
25228
25616
|
function summarizeText(text2, fallback) {
|
|
25229
25617
|
const lines = text2.replace(/\r\n/g, "\n").split("\n");
|
|
@@ -25362,7 +25750,7 @@ function deferHistoryItemDetail(item, deferredDetails) {
|
|
|
25362
25750
|
function deferHistoryItemDetailForTransport(item) {
|
|
25363
25751
|
return deferHistoryItemDetail(item, /* @__PURE__ */ new Map());
|
|
25364
25752
|
}
|
|
25365
|
-
function
|
|
25753
|
+
function deferLargeHistoryItemDetails2(turn, deferredDetails) {
|
|
25366
25754
|
return {
|
|
25367
25755
|
...turn,
|
|
25368
25756
|
items: turn.items.map((item) => deferHistoryItemDetail(item, deferredDetails))
|
|
@@ -25380,7 +25768,7 @@ function shouldPersistRuntimeFinalHistoryItem(item) {
|
|
|
25380
25768
|
}
|
|
25381
25769
|
return shouldPersistFinalHistoryItem(item);
|
|
25382
25770
|
}
|
|
25383
|
-
function
|
|
25771
|
+
function visibleRuntimeTurnItems2(items) {
|
|
25384
25772
|
const hasFinalAgentMessage = items.some(
|
|
25385
25773
|
(item) => item.kind === "agentMessage" && !isTransientAgentHistoryItem(item)
|
|
25386
25774
|
);
|
|
@@ -25413,7 +25801,7 @@ function historyItemTranscriptOrder(item) {
|
|
|
25413
25801
|
}
|
|
25414
25802
|
function copyPersistedOrderingHints(item, persistedItem, turnStartedAt) {
|
|
25415
25803
|
let nextItem = item;
|
|
25416
|
-
if (persistedItem.createdAt && (!nextItem.createdAt || nextItem.
|
|
25804
|
+
if (persistedItem.createdAt && (!nextItem.createdAt || nextItem.createdAt === turnStartedAt)) {
|
|
25417
25805
|
nextItem = { ...nextItem, createdAt: persistedItem.createdAt };
|
|
25418
25806
|
}
|
|
25419
25807
|
if (hasHistoryItemSequence(persistedItem)) {
|
|
@@ -25587,21 +25975,20 @@ function mergePersistedHistoryItemsIntoTurns(turns, persistedItemsByTurnId, defe
|
|
|
25587
25975
|
};
|
|
25588
25976
|
});
|
|
25589
25977
|
}
|
|
25590
|
-
function
|
|
25978
|
+
function agentTurnToThreadTurnDto2(turn, deferredDetails) {
|
|
25591
25979
|
const startedAt = turn.startedAt ?? parseUuidV7Timestamp2(turn.providerTurnId);
|
|
25592
25980
|
const baseTurn = {
|
|
25593
25981
|
id: turn.providerTurnId,
|
|
25594
25982
|
startedAt,
|
|
25595
25983
|
status: turn.status,
|
|
25596
25984
|
error: turn.error?.message ?? null,
|
|
25597
|
-
items:
|
|
25985
|
+
items: visibleRuntimeTurnItems2(turn.items).map(
|
|
25598
25986
|
(item, transcriptIndex) => normalizeHistoryItemCreatedAt(
|
|
25599
|
-
item.transcriptOrder === transcriptIndex ? item : { ...item, transcriptOrder: transcriptIndex }
|
|
25600
|
-
startedAt
|
|
25987
|
+
item.transcriptOrder === transcriptIndex ? item : { ...item, transcriptOrder: transcriptIndex }
|
|
25601
25988
|
)
|
|
25602
25989
|
)
|
|
25603
25990
|
};
|
|
25604
|
-
return deferredDetails ?
|
|
25991
|
+
return deferredDetails ? deferLargeHistoryItemDetails2(baseTurn, deferredDetails) : baseTurn;
|
|
25605
25992
|
}
|
|
25606
25993
|
function applyRecordedTurnItemOrder(turn, turnItemOrder) {
|
|
25607
25994
|
const itemOrder = turnItemOrder.get(turn.id);
|
|
@@ -27245,10 +27632,7 @@ var ThreadDetailAssembler = class {
|
|
|
27245
27632
|
if (cached) {
|
|
27246
27633
|
return cached;
|
|
27247
27634
|
}
|
|
27248
|
-
let remoteSession = await this.input.callbacks.readRemoteSession(
|
|
27249
|
-
input.record,
|
|
27250
|
-
options
|
|
27251
|
-
);
|
|
27635
|
+
let remoteSession = input.record.source === "local_codex_import" && input.record.isConnected === false ? null : await this.input.callbacks.readRemoteSession(input.record, options);
|
|
27252
27636
|
if (!remoteSession) {
|
|
27253
27637
|
return this.buildLocalFallbackEntry({
|
|
27254
27638
|
...input,
|
|
@@ -27317,7 +27701,7 @@ var ThreadDetailAssembler = class {
|
|
|
27317
27701
|
input.localThreadId,
|
|
27318
27702
|
remoteSession.turns
|
|
27319
27703
|
);
|
|
27320
|
-
const visibleTurns = this.input.liveState.visibleRemoteTurns(input.localThreadId, remoteSession.turns).map((turn) =>
|
|
27704
|
+
const visibleTurns = this.input.liveState.visibleRemoteTurns(input.localThreadId, remoteSession.turns).map((turn) => agentTurnToThreadTurnDto2(turn, deferredDetails));
|
|
27321
27705
|
const visibleTurnsWithActiveLiveTurn = appendActiveLiveTurnIfMissing(
|
|
27322
27706
|
visibleTurns,
|
|
27323
27707
|
input.localThreadId,
|
|
@@ -27404,7 +27788,7 @@ var ThreadDetailAssembler = class {
|
|
|
27404
27788
|
deferredDetails
|
|
27405
27789
|
).map(
|
|
27406
27790
|
(turn) => buildTurnDto(
|
|
27407
|
-
|
|
27791
|
+
deferLargeHistoryItemDetails2(turn, deferredDetails),
|
|
27408
27792
|
input.turnMetadataById.get(turn.id) ?? fallbackMetadata
|
|
27409
27793
|
)
|
|
27410
27794
|
);
|
|
@@ -31040,6 +31424,7 @@ var ThreadService = class {
|
|
|
31040
31424
|
constructor(db, agentRuntimes, eventBus, localSessionStore, workspaceRoot, providerManagement, pluginService, config) {
|
|
31041
31425
|
this.db = db;
|
|
31042
31426
|
this.eventBus = eventBus;
|
|
31427
|
+
this.localSessionStore = localSessionStore;
|
|
31043
31428
|
this.workspaceRoot = workspaceRoot;
|
|
31044
31429
|
this.pluginService = pluginService;
|
|
31045
31430
|
this.config = config;
|
|
@@ -31260,6 +31645,7 @@ var ThreadService = class {
|
|
|
31260
31645
|
}
|
|
31261
31646
|
db;
|
|
31262
31647
|
eventBus;
|
|
31648
|
+
localSessionStore;
|
|
31263
31649
|
workspaceRoot;
|
|
31264
31650
|
pluginService;
|
|
31265
31651
|
config;
|
|
@@ -31283,6 +31669,8 @@ var ThreadService = class {
|
|
|
31283
31669
|
forkCoordinator;
|
|
31284
31670
|
attachmentCoordinator;
|
|
31285
31671
|
importCoordinator;
|
|
31672
|
+
localImportWatcherStops = /* @__PURE__ */ new Map();
|
|
31673
|
+
localImportWatcherStarts = /* @__PURE__ */ new Map();
|
|
31286
31674
|
normalizeProvider(provider2) {
|
|
31287
31675
|
return this.providerRuntime.normalizeProvider(provider2);
|
|
31288
31676
|
}
|
|
@@ -31330,7 +31718,7 @@ var ThreadService = class {
|
|
|
31330
31718
|
if (displayTurnId === turn.providerTurnId) {
|
|
31331
31719
|
continue;
|
|
31332
31720
|
}
|
|
31333
|
-
const dto =
|
|
31721
|
+
const dto = agentTurnToThreadTurnDto2(
|
|
31334
31722
|
turn,
|
|
31335
31723
|
/* @__PURE__ */ new Map()
|
|
31336
31724
|
);
|
|
@@ -31458,6 +31846,11 @@ var ThreadService = class {
|
|
|
31458
31846
|
async getThreadDetail(localThreadId, options = {}) {
|
|
31459
31847
|
const record2 = this.requireThreadRecord(localThreadId);
|
|
31460
31848
|
const workspace = this.requireWorkspaceForThread(record2);
|
|
31849
|
+
if (record2.source === "local_codex_import" && record2.isConnected === false) {
|
|
31850
|
+
await this.ensureLocalImportWatcher(record2);
|
|
31851
|
+
} else {
|
|
31852
|
+
this.stopLocalImportWatcher(record2.id);
|
|
31853
|
+
}
|
|
31461
31854
|
this.requireProviderSessionId(record2);
|
|
31462
31855
|
const loadedIds = await this.listLoadedProviderSessionIds(record2.provider);
|
|
31463
31856
|
const workspacePathStatus = await pathExists3(workspace.absPath) ? "present" : "missing";
|
|
@@ -32081,6 +32474,52 @@ var ThreadService = class {
|
|
|
32081
32474
|
async handleRuntimeEvent(event) {
|
|
32082
32475
|
await this.runtimeEventProjector.handleRuntimeEvent(event);
|
|
32083
32476
|
}
|
|
32477
|
+
close() {
|
|
32478
|
+
for (const stop of this.localImportWatcherStops.values()) {
|
|
32479
|
+
stop();
|
|
32480
|
+
}
|
|
32481
|
+
this.localImportWatcherStops.clear();
|
|
32482
|
+
this.localImportWatcherStarts.clear();
|
|
32483
|
+
}
|
|
32484
|
+
async ensureLocalImportWatcher(record2) {
|
|
32485
|
+
if (!record2.providerSessionId || !this.localSessionStore.watchSession || this.localImportWatcherStops.has(record2.id)) {
|
|
32486
|
+
return;
|
|
32487
|
+
}
|
|
32488
|
+
const existingStart = this.localImportWatcherStarts.get(record2.id);
|
|
32489
|
+
if (existingStart) {
|
|
32490
|
+
await existingStart;
|
|
32491
|
+
return;
|
|
32492
|
+
}
|
|
32493
|
+
const start = this.localSessionStore.watchSession(
|
|
32494
|
+
record2.providerSessionId,
|
|
32495
|
+
() => {
|
|
32496
|
+
const current = getThreadRecordById(this.db, record2.id);
|
|
32497
|
+
if (!current || current.source !== "local_codex_import" || current.isConnected !== false) {
|
|
32498
|
+
this.stopLocalImportWatcher(record2.id);
|
|
32499
|
+
return;
|
|
32500
|
+
}
|
|
32501
|
+
this.invalidateThreadDetailCache(record2.id);
|
|
32502
|
+
this.emitThreadEvent("thread.updated", record2.id, {
|
|
32503
|
+
reason: "external_session_updated"
|
|
32504
|
+
});
|
|
32505
|
+
}
|
|
32506
|
+
).then((stop) => {
|
|
32507
|
+
const current = getThreadRecordById(this.db, record2.id);
|
|
32508
|
+
if (!current || current.source !== "local_codex_import" || current.isConnected !== false) {
|
|
32509
|
+
stop();
|
|
32510
|
+
return;
|
|
32511
|
+
}
|
|
32512
|
+
this.localImportWatcherStops.set(record2.id, stop);
|
|
32513
|
+
}).finally(() => {
|
|
32514
|
+
this.localImportWatcherStarts.delete(record2.id);
|
|
32515
|
+
});
|
|
32516
|
+
this.localImportWatcherStarts.set(record2.id, start);
|
|
32517
|
+
await start;
|
|
32518
|
+
}
|
|
32519
|
+
stopLocalImportWatcher(localThreadId) {
|
|
32520
|
+
this.localImportWatcherStops.get(localThreadId)?.();
|
|
32521
|
+
this.localImportWatcherStops.delete(localThreadId);
|
|
32522
|
+
}
|
|
32084
32523
|
shouldPreserveCompletedPendingSteer(localThreadId) {
|
|
32085
32524
|
const record2 = getThreadRecordById(this.db, localThreadId);
|
|
32086
32525
|
if (!record2) {
|
|
@@ -38147,6 +38586,7 @@ function buildApp(options = {}) {
|
|
|
38147
38586
|
});
|
|
38148
38587
|
app2.addHook("onClose", async () => {
|
|
38149
38588
|
cleanupRelayActivity?.();
|
|
38589
|
+
threadService.close();
|
|
38150
38590
|
await shellService.stop();
|
|
38151
38591
|
relayTunnelClient?.stop();
|
|
38152
38592
|
await Promise.all(agentRuntimes.all().map((runtime) => runtime.stop()));
|