codesesh 0.18.0 → 0.19.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/README.md +4 -2
- package/dist/{chunk-HIYT2ZJL.js → chunk-G2BTNW3C.js} +1678 -217
- package/dist/chunk-G2BTNW3C.js.map +1 -0
- package/dist/{chunk-DHNP33L4.js → chunk-KD3DGWZY.js} +2 -2
- package/dist/{dist-DMGLFBW6.js → dist-ALXOBKV2.js} +14 -2
- package/dist/index.js +168 -43
- package/dist/index.js.map +1 -1
- package/dist/scan-refresh-worker.js +243 -24
- package/dist/scan-refresh-worker.js.map +1 -1
- package/dist/search-index-worker.js +2 -2
- package/dist/smart-tag-worker.js +2 -2
- package/dist/web/assets/{AgentIcon-DcUJM198.js → AgentIcon-CEj7HO1m.js} +1 -1
- package/dist/web/assets/BookmarkButton-DLEnIyeE.js +1 -0
- package/dist/web/assets/{Dashboard-wjTLT17d.js → Dashboard-DUG0IFcy.js} +2 -2
- package/dist/web/assets/DialogTitle-Dv6DQPIb.js +1 -0
- package/dist/web/assets/Projects-BMaZc6J5.js +1 -0
- package/dist/web/assets/SearchFilterBar-iP6pzlZo.js +1 -0
- package/dist/web/assets/SearchResultsPanel-RuiLPkbP.js +1 -0
- package/dist/web/assets/{SessionDetail-CgR7ipmi.js → SessionDetail-De1Wl3mu.js} +35 -35
- package/dist/web/assets/{index-DuB6tGoe.css → index-B-CV7pFu.css} +1 -1
- package/dist/web/assets/index-CQI8F_8X.js +1930 -0
- package/dist/web/assets/session-indexes-BuxBafvh.js +4 -0
- package/dist/web/index.html +7 -8
- package/package.json +1 -1
- package/dist/chunk-HIYT2ZJL.js.map +0 -1
- package/dist/web/assets/BookmarkButton-CJjTjyae.js +0 -1
- package/dist/web/assets/DialogTitle-Bz0eXTGu.js +0 -1
- package/dist/web/assets/Projects-DBxubQuM.js +0 -1
- package/dist/web/assets/SearchFilterBar-BG9ah_Po.js +0 -1
- package/dist/web/assets/SearchResultsPanel-Clmf2xKw.js +0 -1
- package/dist/web/assets/index-DR07vs5V.js +0 -1890
- package/dist/web/assets/preload-helper-CmKXJxR3.js +0 -1
- package/dist/web/assets/session-indexes-EHJdYE4j.js +0 -4
- /package/dist/{chunk-DHNP33L4.js.map → chunk-KD3DGWZY.js.map} +0 -0
- /package/dist/{dist-DMGLFBW6.js.map → dist-ALXOBKV2.js.map} +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
// ../core/dist/chunk-
|
|
3
|
+
// ../core/dist/chunk-B67H5WZJ.mjs
|
|
4
4
|
function toRecord(value) {
|
|
5
5
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
6
6
|
}
|
|
@@ -123,14 +123,14 @@ function normalizeMessageParts(value) {
|
|
|
123
123
|
function mergeSessionsUpdatedEvents(previous, next) {
|
|
124
124
|
const changedSessionHeads = /* @__PURE__ */ new Map();
|
|
125
125
|
const removedSessionRefs = /* @__PURE__ */ new Map();
|
|
126
|
-
const
|
|
126
|
+
const sessionKey2 = (agentName, sessionId) => `${agentName}\0${sessionId}`;
|
|
127
127
|
const addChanged = (item) => {
|
|
128
|
-
const key =
|
|
128
|
+
const key = sessionKey2(item.reference.agentName, item.reference.sessionId);
|
|
129
129
|
removedSessionRefs.delete(key);
|
|
130
130
|
changedSessionHeads.set(key, item);
|
|
131
131
|
};
|
|
132
132
|
const addRemoved = (item) => {
|
|
133
|
-
const key =
|
|
133
|
+
const key = sessionKey2(item.agentName, item.sessionId);
|
|
134
134
|
changedSessionHeads.delete(key);
|
|
135
135
|
removedSessionRefs.set(key, item);
|
|
136
136
|
};
|
|
@@ -253,6 +253,44 @@ function mergeSortedSessions(shards) {
|
|
|
253
253
|
}
|
|
254
254
|
return merged;
|
|
255
255
|
}
|
|
256
|
+
function referenceKey(reference) {
|
|
257
|
+
return `${reference.agentName.trim().toLowerCase()}\0${reference.sessionId}`;
|
|
258
|
+
}
|
|
259
|
+
function sessionKey(session) {
|
|
260
|
+
return `${getSessionAgentKey(session)}\0${session.id}`;
|
|
261
|
+
}
|
|
262
|
+
function hasActivityInWindow(session, from, to) {
|
|
263
|
+
const activity = session.time_updated ?? session.time_created;
|
|
264
|
+
return (from == null || activity >= from) && (to == null || activity <= to);
|
|
265
|
+
}
|
|
266
|
+
function isChildSession(session) {
|
|
267
|
+
return session.parent_reference != null;
|
|
268
|
+
}
|
|
269
|
+
function getRootSessions(sessions) {
|
|
270
|
+
return sessions.filter((session) => !session.parent_reference);
|
|
271
|
+
}
|
|
272
|
+
function filterSessionTreeByActivityWindow(sessions, from, to) {
|
|
273
|
+
if (from == null && to == null) return sessions;
|
|
274
|
+
const available = new Set(sessions.map(sessionKey));
|
|
275
|
+
const childrenByParent = /* @__PURE__ */ new Map();
|
|
276
|
+
for (const session of sessions) {
|
|
277
|
+
const parent = session.parent_reference;
|
|
278
|
+
if (!parent || !available.has(referenceKey(parent))) continue;
|
|
279
|
+
const parentKey = referenceKey(parent);
|
|
280
|
+
const children = childrenByParent.get(parentKey);
|
|
281
|
+
if (children) children.push(sessionKey(session));
|
|
282
|
+
else childrenByParent.set(parentKey, [sessionKey(session)]);
|
|
283
|
+
}
|
|
284
|
+
const visible = /* @__PURE__ */ new Set();
|
|
285
|
+
const pending2 = getRootSessions(sessions).filter((session) => hasActivityInWindow(session, from, to)).map(sessionKey);
|
|
286
|
+
while (pending2.length > 0) {
|
|
287
|
+
const key = pending2.pop();
|
|
288
|
+
if (visible.has(key)) continue;
|
|
289
|
+
visible.add(key);
|
|
290
|
+
for (const childKey of childrenByParent.get(key) ?? []) pending2.push(childKey);
|
|
291
|
+
}
|
|
292
|
+
return sessions.filter((session) => visible.has(sessionKey(session)));
|
|
293
|
+
}
|
|
256
294
|
var SAMPLE_SESSION_HEAD = {
|
|
257
295
|
id: "session-1",
|
|
258
296
|
slug: "claudecode/session-1",
|
|
@@ -352,28 +390,38 @@ import { createRequire } from "module";
|
|
|
352
390
|
import { createHash } from "crypto";
|
|
353
391
|
import { existsSync as existsSync7, readFileSync as readFileSync3, readdirSync as readdirSync4, statSync as statSync4 } from "fs";
|
|
354
392
|
import { join as join8, basename as basename5, dirname as dirname4 } from "path";
|
|
355
|
-
import {
|
|
356
|
-
import {
|
|
357
|
-
import {
|
|
393
|
+
import { existsSync as existsSync8, readFileSync as readFileSync4, readdirSync as readdirSync5, statSync as statSync5 } from "fs";
|
|
394
|
+
import { basename as basename6, dirname as dirname5, join as join9, resolve } from "path";
|
|
395
|
+
import {
|
|
396
|
+
closeSync as closeSync2,
|
|
397
|
+
existsSync as existsSync9,
|
|
398
|
+
openSync as openSync2,
|
|
399
|
+
readdirSync as readdirSync6,
|
|
400
|
+
readFileSync as readFileSync5,
|
|
401
|
+
readSync as readSync2,
|
|
402
|
+
statSync as statSync6
|
|
403
|
+
} from "fs";
|
|
404
|
+
import { join as join10, basename as basename7 } from "path";
|
|
405
|
+
import { existsSync as existsSync10, readdirSync as readdirSync7, readFileSync as readFileSync6, statSync as statSync7 } from "fs";
|
|
358
406
|
import { homedir as homedir3, platform as platform2 } from "os";
|
|
359
|
-
import { join as
|
|
360
|
-
import { existsSync as
|
|
361
|
-
import { basename as
|
|
407
|
+
import { join as join11, normalize } from "path";
|
|
408
|
+
import { existsSync as existsSync11 } from "fs";
|
|
409
|
+
import { basename as basename8, join as join12 } from "path";
|
|
362
410
|
import { homedir as homedir4, platform as platform3 } from "os";
|
|
363
|
-
import { join as
|
|
411
|
+
import { join as join13 } from "path";
|
|
364
412
|
import { availableParallelism } from "os";
|
|
365
413
|
import { Worker } from "worker_threads";
|
|
366
|
-
import { existsSync as
|
|
414
|
+
import { existsSync as existsSync12, readFileSync as readFileSync7 } from "fs";
|
|
367
415
|
import { spawnSync } from "child_process";
|
|
368
416
|
import * as os from "os";
|
|
369
417
|
import * as path from "path";
|
|
370
|
-
import { resolve, sep } from "path";
|
|
371
|
-
import { existsSync as
|
|
372
|
-
import { existsSync as
|
|
373
|
-
import { join as
|
|
418
|
+
import { resolve as resolve2, sep } from "path";
|
|
419
|
+
import { existsSync as existsSync14, rmSync as rmSync2, unlinkSync } from "fs";
|
|
420
|
+
import { existsSync as existsSync13 } from "fs";
|
|
421
|
+
import { join as join14 } from "path";
|
|
374
422
|
import { homedir as homedir6 } from "os";
|
|
375
423
|
import { homedir as homedir7, platform as platform4 } from "os";
|
|
376
|
-
import { join as
|
|
424
|
+
import { join as join15 } from "path";
|
|
377
425
|
var registrations = [];
|
|
378
426
|
function registerAgent(reg) {
|
|
379
427
|
registrations.push(reg);
|
|
@@ -468,6 +516,9 @@ function diffSessionSources(refs, cachedSessions, cachedMeta, options) {
|
|
|
468
516
|
return { changedIds, removedIds };
|
|
469
517
|
}
|
|
470
518
|
var BaseAgent = class {
|
|
519
|
+
filterCachedSessions(sessions) {
|
|
520
|
+
return sessions;
|
|
521
|
+
}
|
|
471
522
|
getUri(sessionId) {
|
|
472
523
|
return `${this.name}://${sessionId}`;
|
|
473
524
|
}
|
|
@@ -475,6 +526,14 @@ var BaseAgent = class {
|
|
|
475
526
|
var FileSystemSessionSource = class extends BaseAgent {
|
|
476
527
|
sessionMetaMap = /* @__PURE__ */ new Map();
|
|
477
528
|
sourceFileStats = /* @__PURE__ */ new Map();
|
|
529
|
+
/**
|
|
530
|
+
* 变更集合扩展:当某些会话变更会影响其他会话的派生数据时
|
|
531
|
+
* (如 subagent 文件变更需要父会话重新聚合 token 统计),
|
|
532
|
+
* 子类返回需要一并重解析的会话 ID 集合。默认无关联,原样返回。
|
|
533
|
+
*/
|
|
534
|
+
expandChangedSessionIds(changedIds, _refs) {
|
|
535
|
+
return changedIds;
|
|
536
|
+
}
|
|
478
537
|
scan(options) {
|
|
479
538
|
const sources = this.listSessionSources(options);
|
|
480
539
|
const sessions = [];
|
|
@@ -572,10 +631,11 @@ var FileSystemSessionSource = class extends BaseAgent {
|
|
|
572
631
|
* refs 未传时回退为自行枚举,供独立调用方(如测试)沿用旧行为。
|
|
573
632
|
*/
|
|
574
633
|
incrementalScan(cachedSessions, changedIds, refs) {
|
|
634
|
+
const sources = refs ?? this.listSessionSources();
|
|
575
635
|
const sessionMap = new Map(cachedSessions.map((session) => [session.id, session]));
|
|
576
|
-
const changedSet = new Set(changedIds);
|
|
636
|
+
const changedSet = new Set(this.expandChangedSessionIds(changedIds, sources));
|
|
577
637
|
const currentIds = /* @__PURE__ */ new Set();
|
|
578
|
-
for (const ref of
|
|
638
|
+
for (const ref of sources) {
|
|
579
639
|
currentIds.add(ref.sessionId);
|
|
580
640
|
if (!changedSet.has(ref.sessionId)) continue;
|
|
581
641
|
const head = this.scanSessionSource(ref.sourcePath);
|
|
@@ -1527,7 +1587,7 @@ var TranscriptBuilder = class {
|
|
|
1527
1587
|
};
|
|
1528
1588
|
}
|
|
1529
1589
|
};
|
|
1530
|
-
var HEAD_INDEX_VERSION = "claudecode-head-
|
|
1590
|
+
var HEAD_INDEX_VERSION = "claudecode-head-v4";
|
|
1531
1591
|
function resolveClaudeCodeDataRoot() {
|
|
1532
1592
|
return resolveHomePath("CLAUDE_CONFIG_DIR", ".claude");
|
|
1533
1593
|
}
|
|
@@ -1583,6 +1643,10 @@ var ClaudeCodeAgent = class extends SingleFileSessionSource {
|
|
|
1583
1643
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1584
1644
|
sessionsIndexCache = {};
|
|
1585
1645
|
sessionsIndexMtime = {};
|
|
1646
|
+
childContextsBySource = /* @__PURE__ */ new Map();
|
|
1647
|
+
childContextCache = /* @__PURE__ */ new Map();
|
|
1648
|
+
childSessionIdByToolUseId = /* @__PURE__ */ new Map();
|
|
1649
|
+
childIndexReady = false;
|
|
1586
1650
|
findBasePath() {
|
|
1587
1651
|
return firstExisting(join5(resolveClaudeCodeDataRoot(), "projects"), "data/claudecode");
|
|
1588
1652
|
}
|
|
@@ -1615,18 +1679,97 @@ var ClaudeCodeAgent = class extends SingleFileSessionSource {
|
|
|
1615
1679
|
const indexPath = this.getSessionsIndexPath(projectDir);
|
|
1616
1680
|
indexMtimes.set(projectDir, this.readFileMtimeMs(indexPath));
|
|
1617
1681
|
}
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
})
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1682
|
+
const projectDirSet = new Set(projectDirs);
|
|
1683
|
+
const allSources = this.walkFiles(projectDirs, (entry) => entry.name.endsWith(".jsonl"), {
|
|
1684
|
+
recursive: true
|
|
1685
|
+
});
|
|
1686
|
+
this.indexChildContexts(allSources, projectDirs);
|
|
1687
|
+
const indexedSources = allSources.flatMap((source) => {
|
|
1688
|
+
const child = this.childContextsBySource.get(source.file);
|
|
1689
|
+
if (!child && !projectDirSet.has(dirname2(source.file))) return [];
|
|
1690
|
+
return [
|
|
1691
|
+
{
|
|
1692
|
+
source,
|
|
1693
|
+
sessionId: child?.sessionId ?? basename3(source.file, ".jsonl"),
|
|
1694
|
+
child
|
|
1695
|
+
}
|
|
1696
|
+
];
|
|
1697
|
+
});
|
|
1698
|
+
let selectedSources = indexedSources.filter(
|
|
1699
|
+
({ source }) => matchesScanWindow(source.stat.mtimeMs, options)
|
|
1700
|
+
);
|
|
1701
|
+
if (options?.from != null || options?.to != null) {
|
|
1702
|
+
const childrenByParent = /* @__PURE__ */ new Map();
|
|
1703
|
+
for (const indexed of indexedSources) {
|
|
1704
|
+
const parentId = indexed.child?.parentSessionId;
|
|
1705
|
+
if (!parentId) continue;
|
|
1706
|
+
const children = childrenByParent.get(parentId);
|
|
1707
|
+
if (children) children.push(indexed);
|
|
1708
|
+
else childrenByParent.set(parentId, [indexed]);
|
|
1709
|
+
}
|
|
1710
|
+
const windowSelectedById = new Map(
|
|
1711
|
+
selectedSources.map((indexed) => [indexed.sessionId, indexed])
|
|
1712
|
+
);
|
|
1713
|
+
const connectedSelected = /* @__PURE__ */ new Map();
|
|
1714
|
+
const acceptedIds = /* @__PURE__ */ new Set();
|
|
1715
|
+
const visitingIds = /* @__PURE__ */ new Set();
|
|
1716
|
+
const hasSelectedParent = (sessionId) => {
|
|
1717
|
+
if (acceptedIds.has(sessionId)) return true;
|
|
1718
|
+
if (visitingIds.has(sessionId)) return false;
|
|
1719
|
+
const indexed = windowSelectedById.get(sessionId);
|
|
1720
|
+
if (!indexed) return false;
|
|
1721
|
+
const parentId = indexed.child?.parentSessionId;
|
|
1722
|
+
if (!parentId) {
|
|
1723
|
+
acceptedIds.add(sessionId);
|
|
1724
|
+
return true;
|
|
1725
|
+
}
|
|
1726
|
+
visitingIds.add(sessionId);
|
|
1727
|
+
const connected = hasSelectedParent(parentId);
|
|
1728
|
+
visitingIds.delete(sessionId);
|
|
1729
|
+
if (connected) acceptedIds.add(sessionId);
|
|
1730
|
+
return connected;
|
|
1731
|
+
};
|
|
1732
|
+
for (const indexed of selectedSources) {
|
|
1733
|
+
if (hasSelectedParent(indexed.sessionId)) {
|
|
1734
|
+
connectedSelected.set(indexed.sessionId, indexed);
|
|
1735
|
+
}
|
|
1736
|
+
}
|
|
1737
|
+
if (options?.includeRelatedSessions !== false) {
|
|
1738
|
+
const pending2 = [...connectedSelected.values()].filter(({ child }) => !child?.parentSessionId).map(({ sessionId }) => sessionId);
|
|
1739
|
+
while (pending2.length > 0) {
|
|
1740
|
+
const parentId = pending2.pop();
|
|
1741
|
+
for (const child of childrenByParent.get(parentId) ?? []) {
|
|
1742
|
+
if (connectedSelected.has(child.sessionId)) continue;
|
|
1743
|
+
connectedSelected.set(child.sessionId, child);
|
|
1744
|
+
pending2.push(child.sessionId);
|
|
1745
|
+
}
|
|
1746
|
+
}
|
|
1747
|
+
}
|
|
1748
|
+
selectedSources = [...connectedSelected.values()];
|
|
1749
|
+
}
|
|
1750
|
+
return selectedSources.map(({ source: { file, stat }, sessionId, child }) => {
|
|
1751
|
+
const projectDir = child?.projectDir ?? dirname2(file);
|
|
1752
|
+
return {
|
|
1753
|
+
sessionId,
|
|
1754
|
+
sourcePath: file,
|
|
1755
|
+
fingerprint: this.sourceFingerprint(
|
|
1756
|
+
stat,
|
|
1757
|
+
indexMtimes.get(projectDir) ?? null,
|
|
1758
|
+
child?.metaMtimeMs
|
|
1759
|
+
)
|
|
1760
|
+
};
|
|
1761
|
+
});
|
|
1762
|
+
}
|
|
1763
|
+
setSessionMetaMap(meta) {
|
|
1764
|
+
super.setSessionMetaMap(meta);
|
|
1765
|
+
this.childContextsBySource.clear();
|
|
1766
|
+
this.childSessionIdByToolUseId.clear();
|
|
1767
|
+
this.childIndexReady = false;
|
|
1626
1768
|
}
|
|
1627
1769
|
parseFileSessionHead(sourcePath) {
|
|
1628
|
-
const
|
|
1629
|
-
|
|
1770
|
+
const child = this.getChildContext(sourcePath);
|
|
1771
|
+
const projectDir = child?.projectDir ?? dirname2(sourcePath);
|
|
1772
|
+
return getParsedSession(this.parseSessionHeadResult(sourcePath, projectDir, child));
|
|
1630
1773
|
}
|
|
1631
1774
|
getSessionData(sessionId) {
|
|
1632
1775
|
const meta = this.sessionMetaMap.get(sessionId);
|
|
@@ -1636,12 +1779,19 @@ var ClaudeCodeAgent = class extends SingleFileSessionSource {
|
|
|
1636
1779
|
if (!existsSync5(meta.sourcePath)) {
|
|
1637
1780
|
throw new Error(`Session file missing: ${meta.sourcePath}`);
|
|
1638
1781
|
}
|
|
1782
|
+
this.ensureChildIndex();
|
|
1639
1783
|
const builder = new TranscriptBuilder();
|
|
1640
1784
|
const assistantUuidToToolCalls = /* @__PURE__ */ new Map();
|
|
1641
1785
|
const countedUsageKeys = /* @__PURE__ */ new Set();
|
|
1642
1786
|
for (const record of readJsonlFile(meta.sourcePath)) {
|
|
1643
1787
|
try {
|
|
1644
|
-
this.convertRecord(
|
|
1788
|
+
this.convertRecord(
|
|
1789
|
+
record,
|
|
1790
|
+
builder,
|
|
1791
|
+
assistantUuidToToolCalls,
|
|
1792
|
+
countedUsageKeys,
|
|
1793
|
+
this.childSessionIdByToolUseId
|
|
1794
|
+
);
|
|
1645
1795
|
} catch {
|
|
1646
1796
|
}
|
|
1647
1797
|
}
|
|
@@ -1652,6 +1802,7 @@ var ClaudeCodeAgent = class extends SingleFileSessionSource {
|
|
|
1652
1802
|
title: meta.title,
|
|
1653
1803
|
slug: `claudecode/${meta.id}`,
|
|
1654
1804
|
directory: meta.directory,
|
|
1805
|
+
parent_reference: meta.parentSessionId == null ? void 0 : { agentName: this.name, sessionId: meta.parentSessionId },
|
|
1655
1806
|
version: void 0,
|
|
1656
1807
|
time_created: meta.createdAt,
|
|
1657
1808
|
time_updated: meta.updatedAt,
|
|
@@ -1668,25 +1819,125 @@ var ClaudeCodeAgent = class extends SingleFileSessionSource {
|
|
|
1668
1819
|
return [];
|
|
1669
1820
|
}
|
|
1670
1821
|
}
|
|
1822
|
+
ensureChildIndex() {
|
|
1823
|
+
if (this.childIndexReady) return;
|
|
1824
|
+
this.basePath ??= this.findBasePath();
|
|
1825
|
+
if (!this.basePath) {
|
|
1826
|
+
this.childIndexReady = true;
|
|
1827
|
+
return;
|
|
1828
|
+
}
|
|
1829
|
+
const projectDirs = this.listProjectDirs();
|
|
1830
|
+
this.indexChildContexts(
|
|
1831
|
+
this.walkFiles(projectDirs, (entry) => entry.name.endsWith(".jsonl"), {
|
|
1832
|
+
recursive: true
|
|
1833
|
+
}),
|
|
1834
|
+
projectDirs
|
|
1835
|
+
);
|
|
1836
|
+
}
|
|
1837
|
+
indexChildContexts(sources, projectDirs) {
|
|
1838
|
+
this.childContextsBySource.clear();
|
|
1839
|
+
this.childSessionIdByToolUseId.clear();
|
|
1840
|
+
const projectDirSet = new Set(projectDirs);
|
|
1841
|
+
const contexts = /* @__PURE__ */ new Map();
|
|
1842
|
+
const knownSessionIds = /* @__PURE__ */ new Set();
|
|
1843
|
+
for (const source of sources) {
|
|
1844
|
+
const child = this.readChildContext(source.file);
|
|
1845
|
+
if (!child) {
|
|
1846
|
+
if (projectDirSet.has(dirname2(source.file))) {
|
|
1847
|
+
knownSessionIds.add(basename3(source.file, ".jsonl"));
|
|
1848
|
+
}
|
|
1849
|
+
continue;
|
|
1850
|
+
}
|
|
1851
|
+
contexts.set(source.file, child);
|
|
1852
|
+
knownSessionIds.add(child.sessionId);
|
|
1853
|
+
}
|
|
1854
|
+
for (const [sourcePath, child] of contexts) {
|
|
1855
|
+
const parentSessionId = child.parentSessionId && knownSessionIds.has(child.parentSessionId) ? child.parentSessionId : null;
|
|
1856
|
+
const normalized = parentSessionId === child.parentSessionId ? child : { ...child, parentSessionId };
|
|
1857
|
+
this.childContextsBySource.set(sourcePath, normalized);
|
|
1858
|
+
if (child.toolUseId) {
|
|
1859
|
+
this.childSessionIdByToolUseId.set(child.toolUseId, normalized.sessionId);
|
|
1860
|
+
}
|
|
1861
|
+
}
|
|
1862
|
+
this.childIndexReady = true;
|
|
1863
|
+
}
|
|
1864
|
+
getChildContext(sourcePath) {
|
|
1865
|
+
const cached = this.childContextsBySource.get(sourcePath);
|
|
1866
|
+
if (cached) return cached;
|
|
1867
|
+
const child = this.readChildContext(sourcePath);
|
|
1868
|
+
if (!child) return null;
|
|
1869
|
+
this.childContextsBySource.set(sourcePath, child);
|
|
1870
|
+
if (child.toolUseId) {
|
|
1871
|
+
this.childSessionIdByToolUseId.set(child.toolUseId, child.sessionId);
|
|
1872
|
+
}
|
|
1873
|
+
return child;
|
|
1874
|
+
}
|
|
1875
|
+
readChildContext(sourcePath) {
|
|
1876
|
+
const subagentsDir = dirname2(sourcePath);
|
|
1877
|
+
if (basename3(subagentsDir) !== "subagents") return null;
|
|
1878
|
+
const parentDir = dirname2(subagentsDir);
|
|
1879
|
+
const projectDir = dirname2(parentDir);
|
|
1880
|
+
const fileStem = basename3(sourcePath, ".jsonl");
|
|
1881
|
+
const metaPath = join5(subagentsDir, fileStem + ".meta.json");
|
|
1882
|
+
const metaMtimeMs = this.readFileMtimeMs(metaPath);
|
|
1883
|
+
const cached = this.childContextCache.get(sourcePath);
|
|
1884
|
+
if (cached?.metaMtimeMs === metaMtimeMs) return cached.context;
|
|
1885
|
+
let metadata = null;
|
|
1886
|
+
if (metaMtimeMs !== null) {
|
|
1887
|
+
try {
|
|
1888
|
+
metadata = asRecord(JSON.parse(readFileSync2(metaPath, "utf-8"))) ?? null;
|
|
1889
|
+
} catch {
|
|
1890
|
+
}
|
|
1891
|
+
}
|
|
1892
|
+
const sessionId = asString(metadata?.["agentId"])?.trim() || fileStem.replace(/^agent-/, "");
|
|
1893
|
+
if (!sessionId) return null;
|
|
1894
|
+
const parentAgentId = asString(metadata?.["parentAgentId"])?.trim();
|
|
1895
|
+
const candidateParentId = parentAgentId || basename3(parentDir) || null;
|
|
1896
|
+
const parentSessionId = candidateParentId && this.hasChildParent(sourcePath, projectDir, candidateParentId) ? candidateParentId : null;
|
|
1897
|
+
const name = asString(metadata?.["name"])?.trim();
|
|
1898
|
+
const description = asString(metadata?.["description"])?.trim();
|
|
1899
|
+
const context = {
|
|
1900
|
+
sessionId,
|
|
1901
|
+
projectDir,
|
|
1902
|
+
parentSessionId,
|
|
1903
|
+
explicitTitle: name || description || null,
|
|
1904
|
+
metaMtimeMs,
|
|
1905
|
+
toolUseId: asString(metadata?.["toolUseId"])?.trim() || null
|
|
1906
|
+
};
|
|
1907
|
+
this.childContextCache.set(sourcePath, { metaMtimeMs, context });
|
|
1908
|
+
return context;
|
|
1909
|
+
}
|
|
1910
|
+
hasChildParent(sourcePath, projectDir, parentSessionId) {
|
|
1911
|
+
const subagentsDir = dirname2(sourcePath);
|
|
1912
|
+
return [
|
|
1913
|
+
join5(projectDir, parentSessionId + ".jsonl"),
|
|
1914
|
+
join5(subagentsDir, "agent-" + parentSessionId + ".jsonl"),
|
|
1915
|
+
join5(subagentsDir, parentSessionId + ".jsonl")
|
|
1916
|
+
].some((path2) => existsSync5(path2));
|
|
1917
|
+
}
|
|
1671
1918
|
createFileSessionMeta(head, source) {
|
|
1672
|
-
const
|
|
1919
|
+
const child = this.getChildContext(source.file);
|
|
1920
|
+
const projectDir = child?.projectDir ?? dirname2(source.file);
|
|
1673
1921
|
const indexPath = this.getSessionsIndexPath(projectDir);
|
|
1674
1922
|
const indexMtime = this.readFileMtimeMs(indexPath);
|
|
1675
1923
|
return this.buildFileSessionMeta({
|
|
1676
1924
|
head,
|
|
1677
1925
|
source,
|
|
1678
|
-
fingerprint: this.sourceFingerprint(source.stat, indexMtime),
|
|
1926
|
+
fingerprint: this.sourceFingerprint(source.stat, indexMtime, child?.metaMtimeMs),
|
|
1679
1927
|
extras: {
|
|
1680
1928
|
indexPath: indexMtime === null ? null : indexPath,
|
|
1681
1929
|
indexMtimeMs: indexMtime,
|
|
1682
1930
|
headIndexVersion: HEAD_INDEX_VERSION,
|
|
1683
|
-
model: head.stats.total_tokens ? "unknown" : void 0
|
|
1931
|
+
model: head.stats.total_tokens ? "unknown" : void 0,
|
|
1932
|
+
parentSessionId: head.parent_reference?.sessionId ?? null
|
|
1684
1933
|
}
|
|
1685
1934
|
});
|
|
1686
1935
|
}
|
|
1687
1936
|
/** Fingerprint depends on an already-fetched stat to avoid re-statting the same file. */
|
|
1688
|
-
sourceFingerprint(stat, indexMtime) {
|
|
1689
|
-
|
|
1937
|
+
sourceFingerprint(stat, indexMtime, metaMtime) {
|
|
1938
|
+
const fingerprint = [HEAD_INDEX_VERSION, stat.mtimeMs, stat.size, indexMtime];
|
|
1939
|
+
if (metaMtime !== void 0) fingerprint.push(metaMtime);
|
|
1940
|
+
return JSON.stringify(fingerprint);
|
|
1690
1941
|
}
|
|
1691
1942
|
getSessionsIndexPath(projectDir) {
|
|
1692
1943
|
return join5(projectDir, "sessions-index.json");
|
|
@@ -1716,11 +1967,11 @@ var ClaudeCodeAgent = class extends SingleFileSessionSource {
|
|
|
1716
1967
|
this.sessionsIndexMtime[cacheKey] = mtime;
|
|
1717
1968
|
return map;
|
|
1718
1969
|
}
|
|
1719
|
-
parseSessionHeadResult(filePath, projectDir) {
|
|
1720
|
-
const sessionId = basename3(filePath, ".jsonl");
|
|
1970
|
+
parseSessionHeadResult(filePath, projectDir, child) {
|
|
1971
|
+
const sessionId = child?.sessionId ?? basename3(filePath, ".jsonl");
|
|
1721
1972
|
const index = this.loadSessionsIndex(projectDir);
|
|
1722
1973
|
const indexEntry = index.get(sessionId);
|
|
1723
|
-
const explicitTitle = indexEntry?.summary ? String(indexEntry.summary) : null;
|
|
1974
|
+
const explicitTitle = child?.explicitTitle ?? (indexEntry?.summary ? String(indexEntry.summary) : null);
|
|
1724
1975
|
let createdAt = 0;
|
|
1725
1976
|
let updatedAt = 0;
|
|
1726
1977
|
let lineIndex = 0;
|
|
@@ -1819,6 +2070,7 @@ var ClaudeCodeAgent = class extends SingleFileSessionSource {
|
|
|
1819
2070
|
slug: `claudecode/${sessionId}`,
|
|
1820
2071
|
title,
|
|
1821
2072
|
directory,
|
|
2073
|
+
parent_reference: child?.parentSessionId == null ? void 0 : { agentName: this.name, sessionId: child.parentSessionId },
|
|
1822
2074
|
time_created: createdAt,
|
|
1823
2075
|
time_updated: updatedAt,
|
|
1824
2076
|
stats: {
|
|
@@ -1851,19 +2103,25 @@ var ClaudeCodeAgent = class extends SingleFileSessionSource {
|
|
|
1851
2103
|
return null;
|
|
1852
2104
|
}
|
|
1853
2105
|
// --- Record conversion ---
|
|
1854
|
-
convertRecord(data, builder, assistantUuidToToolCalls, countedUsageKeys) {
|
|
2106
|
+
convertRecord(data, builder, assistantUuidToToolCalls, countedUsageKeys, childSessionIdByToolUseId) {
|
|
1855
2107
|
if (data["isMeta"] === true) return;
|
|
1856
2108
|
const msgType = String(data["type"] ?? "");
|
|
1857
2109
|
if (isInternalEventType(msgType)) return;
|
|
1858
2110
|
if (msgType === "assistant") {
|
|
1859
|
-
this.convertAssistantRecord(
|
|
2111
|
+
this.convertAssistantRecord(
|
|
2112
|
+
data,
|
|
2113
|
+
builder,
|
|
2114
|
+
assistantUuidToToolCalls,
|
|
2115
|
+
countedUsageKeys,
|
|
2116
|
+
childSessionIdByToolUseId
|
|
2117
|
+
);
|
|
1860
2118
|
} else if (msgType === "user") {
|
|
1861
2119
|
this.convertUserRecord(data, builder, assistantUuidToToolCalls);
|
|
1862
2120
|
} else if (msgType === "tool_result") {
|
|
1863
2121
|
this.convertToolResultRecord(data, builder);
|
|
1864
2122
|
}
|
|
1865
2123
|
}
|
|
1866
|
-
convertAssistantRecord(data, builder, assistantUuidToToolCalls, countedUsageKeys) {
|
|
2124
|
+
convertAssistantRecord(data, builder, assistantUuidToToolCalls, countedUsageKeys, childSessionIdByToolUseId) {
|
|
1867
2125
|
const msg = asRecord(data["message"]) ?? {};
|
|
1868
2126
|
const timestampMs = parseTimestampMs(data);
|
|
1869
2127
|
const rawContent = asArray(msg["content"]) ?? [];
|
|
@@ -1903,12 +2161,14 @@ var ClaudeCodeAgent = class extends SingleFileSessionSource {
|
|
|
1903
2161
|
}
|
|
1904
2162
|
if (partType !== "tool_use") continue;
|
|
1905
2163
|
const toolCallId = String(part["id"] ?? "").trim();
|
|
1906
|
-
const
|
|
2164
|
+
const subagentId = childSessionIdByToolUseId.get(toolCallId);
|
|
2165
|
+
const toolPart2 = this.buildToolPart(part, timestampMs);
|
|
1907
2166
|
const message = builder.appendToolCall(
|
|
1908
|
-
|
|
1909
|
-
{ id: uuid, timestampMs, agent: "claude" },
|
|
2167
|
+
toolPart2,
|
|
2168
|
+
{ id: uuid, timestampMs, agent: "claude", subagentId },
|
|
1910
2169
|
{ modeOnCreate: "tool" }
|
|
1911
2170
|
);
|
|
2171
|
+
if (subagentId) message.subagent_id = subagentId;
|
|
1912
2172
|
this.applyAssistantMetadata(message, data, msg, countedUsageKeys);
|
|
1913
2173
|
if (toolCallId) {
|
|
1914
2174
|
toolCallIds.push(toolCallId);
|
|
@@ -2305,6 +2565,24 @@ function isSqliteAvailable() {
|
|
|
2305
2565
|
return DatabaseConstructor !== null;
|
|
2306
2566
|
}
|
|
2307
2567
|
var MESSAGE_ROLES = /* @__PURE__ */ new Set(["user", "assistant", "tool"]);
|
|
2568
|
+
var SESSION_ID_QUERY_CHUNK_SIZE = 500;
|
|
2569
|
+
function compareSessionRowsByActivityDesc(left, right) {
|
|
2570
|
+
const leftUpdated = Number(left.time_updated ?? left.time_created ?? 0);
|
|
2571
|
+
const rightUpdated = Number(right.time_updated ?? right.time_created ?? 0);
|
|
2572
|
+
return rightUpdated - leftUpdated || Number(right.time_created ?? 0) - Number(left.time_created ?? 0) || String(right.id ?? "").localeCompare(String(left.id ?? ""));
|
|
2573
|
+
}
|
|
2574
|
+
function accumulateTokenStats(stats, msgData, agentName) {
|
|
2575
|
+
const cost = Number(msgData.cost ?? 0);
|
|
2576
|
+
const tokens = parseTokens(msgData.tokens, agentName);
|
|
2577
|
+
const inputTokens = Number(tokens?.input ?? 0);
|
|
2578
|
+
const outputTokens = Number(tokens?.output ?? 0);
|
|
2579
|
+
const model = parseModel(msgData.modelID, agentName);
|
|
2580
|
+
const estimatedCost = cost > 0 ? null : estimateTokenCost(model, { input: inputTokens, output: outputTokens });
|
|
2581
|
+
if (estimatedCost !== null) stats.cost_source = "estimated";
|
|
2582
|
+
stats.total_cost += cost || estimatedCost || 0;
|
|
2583
|
+
stats.total_input_tokens += inputTokens;
|
|
2584
|
+
stats.total_output_tokens += outputTokens;
|
|
2585
|
+
}
|
|
2308
2586
|
function parseJsonRecord(raw, agentName, field) {
|
|
2309
2587
|
const parsed = asRecord(JSON.parse(String(raw ?? "{}")));
|
|
2310
2588
|
if (parsed) return parsed;
|
|
@@ -2360,28 +2638,48 @@ var OpenCodeSqliteAgent = class extends DatabaseSessionSource {
|
|
|
2360
2638
|
const hasMessageTable = Boolean(
|
|
2361
2639
|
db.prepare("SELECT name FROM sqlite_master WHERE type = 'table' AND name = 'message'").get()
|
|
2362
2640
|
);
|
|
2641
|
+
const hasTaskType = columnExists(db, "session", "task_type");
|
|
2642
|
+
const hasParentId = columnExists(db, "session", "parent_id");
|
|
2643
|
+
const childPredicate = hasParentId ? "AND s.parent_id IS NULL" : hasTaskType ? "AND (s.task_type IS NULL OR s.task_type != 'subagent_child')" : "";
|
|
2363
2644
|
let rows;
|
|
2645
|
+
const parentIdSelect = hasParentId ? ", s.parent_id" : "";
|
|
2364
2646
|
if (hasMessageTable) {
|
|
2365
2647
|
rows = db.prepare(`
|
|
2366
2648
|
SELECT
|
|
2367
2649
|
s.id, s.title, s.time_created, s.time_updated, s.slug, s.directory,
|
|
2368
|
-
s.version, s.summary_files
|
|
2650
|
+
s.version, s.summary_files${parentIdSelect}
|
|
2369
2651
|
FROM session s
|
|
2370
2652
|
WHERE COALESCE(s.time_updated, s.time_created) >= ?
|
|
2371
|
-
|
|
2653
|
+
${childPredicate}
|
|
2654
|
+
ORDER BY COALESCE(s.time_updated, s.time_created) DESC, s.time_created DESC, s.id DESC
|
|
2372
2655
|
`).all(cutoffTime);
|
|
2373
2656
|
} else {
|
|
2374
2657
|
rows = db.prepare(`
|
|
2375
2658
|
SELECT s.id, s.title, s.time_created, s.time_updated, s.slug, s.directory,
|
|
2376
|
-
s.version, s.summary_files, 0 AS message_count, NULL AS model_message_data
|
|
2659
|
+
s.version, s.summary_files, 0 AS message_count, NULL AS model_message_data${parentIdSelect}
|
|
2377
2660
|
FROM session s
|
|
2378
2661
|
WHERE COALESCE(s.time_updated, s.time_created) >= ?
|
|
2379
|
-
|
|
2662
|
+
${childPredicate}
|
|
2663
|
+
ORDER BY COALESCE(s.time_updated, s.time_created) DESC, s.time_created DESC, s.id DESC
|
|
2380
2664
|
`).all(cutoffTime);
|
|
2381
2665
|
}
|
|
2666
|
+
if (hasParentId && options?.includeRelatedSessions !== false) {
|
|
2667
|
+
const rootIds = rows.map((row) => String(row.id ?? "")).filter(Boolean);
|
|
2668
|
+
const relatedRows = this.readRelatedSessionRows(db, rootIds);
|
|
2669
|
+
const knownIds = new Set(rootIds);
|
|
2670
|
+
rows.push(...relatedRows.filter((row) => !knownIds.has(String(row.id ?? ""))));
|
|
2671
|
+
}
|
|
2672
|
+
rows.sort(compareSessionRowsByActivityDesc);
|
|
2673
|
+
const sessionIds = new Set(rows.map((row) => String(row.id ?? "")).filter(Boolean));
|
|
2382
2674
|
const headContexts = hasMessageTable ? this.buildHeadContexts(
|
|
2383
|
-
this.readHeadMessageRows(
|
|
2384
|
-
|
|
2675
|
+
this.readHeadMessageRows(
|
|
2676
|
+
db,
|
|
2677
|
+
cutoffTime,
|
|
2678
|
+
hasParentId,
|
|
2679
|
+
sessionIds.size > 0 ? sessionIds : void 0
|
|
2680
|
+
),
|
|
2681
|
+
this.readHeadPartRows(db, cutoffTime, sessionIds.size > 0 ? sessionIds : void 0),
|
|
2682
|
+
hasParentId
|
|
2385
2683
|
) : /* @__PURE__ */ new Map();
|
|
2386
2684
|
const heads = [];
|
|
2387
2685
|
options?.onProgress?.({ total: rows.length, processed: 0, sessions: 0 });
|
|
@@ -2423,6 +2721,7 @@ var OpenCodeSqliteAgent = class extends DatabaseSessionSource {
|
|
|
2423
2721
|
slug: `${this.name}/${id}`,
|
|
2424
2722
|
title: resolveSessionTitle(String(row.title ?? ""), messageTitle, null),
|
|
2425
2723
|
directory: String(row.directory ?? ""),
|
|
2724
|
+
parent_reference: row.parent_id == null || String(row.parent_id) === "" ? void 0 : { agentName: this.name, sessionId: String(row.parent_id) },
|
|
2426
2725
|
time_created: timeCreated,
|
|
2427
2726
|
time_updated: timeUpdated,
|
|
2428
2727
|
stats: {
|
|
@@ -2434,28 +2733,103 @@ var OpenCodeSqliteAgent = class extends DatabaseSessionSource {
|
|
|
2434
2733
|
}
|
|
2435
2734
|
});
|
|
2436
2735
|
}
|
|
2437
|
-
readHeadMessageRows(db, cutoffTime) {
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
JOIN session s ON s.id = m.session_id
|
|
2443
|
-
WHERE COALESCE(s.time_updated, s.time_created) >= ?
|
|
2444
|
-
ORDER BY m.session_id, m.time_created ASC
|
|
2736
|
+
readHeadMessageRows(db, cutoffTime, withParentId, sessionIds) {
|
|
2737
|
+
const parentIdSelect = withParentId ? ", s.parent_id" : "";
|
|
2738
|
+
const ids = sessionIds ? [...sessionIds] : [];
|
|
2739
|
+
if (ids.length === 0) {
|
|
2740
|
+
return db.prepare(
|
|
2445
2741
|
`
|
|
2446
|
-
|
|
2742
|
+
SELECT m.id, m.session_id, m.data, m.time_created${parentIdSelect}
|
|
2743
|
+
FROM message m
|
|
2744
|
+
JOIN session s ON s.id = m.session_id
|
|
2745
|
+
WHERE COALESCE(s.time_updated, s.time_created) >= ?
|
|
2746
|
+
ORDER BY m.session_id, m.time_created ASC
|
|
2747
|
+
`
|
|
2748
|
+
).all(cutoffTime);
|
|
2749
|
+
}
|
|
2750
|
+
const rows = [];
|
|
2751
|
+
for (let offset = 0; offset < ids.length; offset += SESSION_ID_QUERY_CHUNK_SIZE) {
|
|
2752
|
+
const chunk = ids.slice(offset, offset + SESSION_ID_QUERY_CHUNK_SIZE);
|
|
2753
|
+
rows.push(
|
|
2754
|
+
...db.prepare(
|
|
2755
|
+
`
|
|
2756
|
+
SELECT m.id, m.session_id, m.data, m.time_created${parentIdSelect}
|
|
2757
|
+
FROM message m
|
|
2758
|
+
JOIN session s ON s.id = m.session_id
|
|
2759
|
+
WHERE s.id IN (${chunk.map(() => "?").join(",")})
|
|
2760
|
+
ORDER BY m.session_id, m.time_created ASC
|
|
2761
|
+
`
|
|
2762
|
+
).all(...chunk)
|
|
2763
|
+
);
|
|
2764
|
+
}
|
|
2765
|
+
return rows;
|
|
2447
2766
|
}
|
|
2448
|
-
readHeadPartRows(db, cutoffTime) {
|
|
2449
|
-
|
|
2767
|
+
readHeadPartRows(db, cutoffTime, sessionIds) {
|
|
2768
|
+
const ids = sessionIds ? [...sessionIds] : [];
|
|
2769
|
+
if (ids.length === 0) {
|
|
2770
|
+
return db.prepare(
|
|
2771
|
+
`
|
|
2772
|
+
SELECT p.message_id, p.data, p.time_created
|
|
2773
|
+
FROM part p
|
|
2774
|
+
JOIN message m ON m.id = p.message_id
|
|
2775
|
+
JOIN session s ON s.id = m.session_id
|
|
2776
|
+
WHERE COALESCE(s.time_updated, s.time_created) >= ?
|
|
2777
|
+
ORDER BY p.message_id, p.time_created ASC, p.id ASC
|
|
2778
|
+
`
|
|
2779
|
+
).all(cutoffTime);
|
|
2780
|
+
}
|
|
2781
|
+
const rows = [];
|
|
2782
|
+
for (let offset = 0; offset < ids.length; offset += SESSION_ID_QUERY_CHUNK_SIZE) {
|
|
2783
|
+
const chunk = ids.slice(offset, offset + SESSION_ID_QUERY_CHUNK_SIZE);
|
|
2784
|
+
rows.push(
|
|
2785
|
+
...db.prepare(
|
|
2786
|
+
`
|
|
2787
|
+
SELECT p.message_id, p.data, p.time_created
|
|
2788
|
+
FROM part p
|
|
2789
|
+
JOIN message m ON m.id = p.message_id
|
|
2790
|
+
JOIN session s ON s.id = m.session_id
|
|
2791
|
+
WHERE s.id IN (${chunk.map(() => "?").join(",")})
|
|
2792
|
+
ORDER BY p.message_id, p.time_created ASC, p.id ASC
|
|
2793
|
+
`
|
|
2794
|
+
).all(...chunk)
|
|
2795
|
+
);
|
|
2796
|
+
}
|
|
2797
|
+
return rows;
|
|
2798
|
+
}
|
|
2799
|
+
readRelatedSessionRows(db, rootIds) {
|
|
2800
|
+
if (rootIds.length === 0) return [];
|
|
2801
|
+
const rows = db.prepare(
|
|
2450
2802
|
`
|
|
2451
|
-
SELECT
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
WHERE
|
|
2456
|
-
ORDER BY
|
|
2803
|
+
SELECT
|
|
2804
|
+
s.id, s.title, s.time_created, s.time_updated, s.slug, s.directory,
|
|
2805
|
+
s.version, s.summary_files, s.parent_id
|
|
2806
|
+
FROM session s
|
|
2807
|
+
WHERE s.parent_id IS NOT NULL
|
|
2808
|
+
ORDER BY s.time_created ASC
|
|
2457
2809
|
`
|
|
2458
|
-
).all(
|
|
2810
|
+
).all();
|
|
2811
|
+
const childrenByParent = /* @__PURE__ */ new Map();
|
|
2812
|
+
for (const row of rows) {
|
|
2813
|
+
const parentId = String(row.parent_id ?? "");
|
|
2814
|
+
if (!parentId) continue;
|
|
2815
|
+
const children = childrenByParent.get(parentId);
|
|
2816
|
+
if (children) children.push(row);
|
|
2817
|
+
else childrenByParent.set(parentId, [row]);
|
|
2818
|
+
}
|
|
2819
|
+
const result = [];
|
|
2820
|
+
const pending2 = [...rootIds];
|
|
2821
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2822
|
+
while (pending2.length > 0) {
|
|
2823
|
+
const parentId = pending2.pop();
|
|
2824
|
+
for (const row of childrenByParent.get(parentId) ?? []) {
|
|
2825
|
+
const id = String(row.id ?? "");
|
|
2826
|
+
if (!id || seen.has(id)) continue;
|
|
2827
|
+
seen.add(id);
|
|
2828
|
+
result.push(row);
|
|
2829
|
+
pending2.push(id);
|
|
2830
|
+
}
|
|
2831
|
+
}
|
|
2832
|
+
return result;
|
|
2459
2833
|
}
|
|
2460
2834
|
parsePartRow(partRow) {
|
|
2461
2835
|
const partData = parseJsonRecord(partRow.data, this.name, "part.data");
|
|
@@ -2498,16 +2872,10 @@ var OpenCodeSqliteAgent = class extends DatabaseSessionSource {
|
|
|
2498
2872
|
}
|
|
2499
2873
|
return partsByMessage;
|
|
2500
2874
|
}
|
|
2501
|
-
buildHeadContexts(messageRows, partRows) {
|
|
2875
|
+
buildHeadContexts(messageRows, partRows, withParentId) {
|
|
2502
2876
|
const partsByMessage = this.buildPartsByMessage(partRows);
|
|
2503
2877
|
const contexts = /* @__PURE__ */ new Map();
|
|
2504
|
-
|
|
2505
|
-
const sessionId = String(row.session_id ?? "");
|
|
2506
|
-
if (!sessionId) continue;
|
|
2507
|
-
const msgData = parseJsonRecord(row.data, this.name, "message.data");
|
|
2508
|
-
if (isInternalEventType(msgData.type)) continue;
|
|
2509
|
-
const parts = partsByMessage.get(String(row.id ?? "")) ?? [];
|
|
2510
|
-
if (parts.length === 0) continue;
|
|
2878
|
+
const ensureContext = (sessionId) => {
|
|
2511
2879
|
let context = contexts.get(sessionId);
|
|
2512
2880
|
if (!context) {
|
|
2513
2881
|
context = {
|
|
@@ -2521,16 +2889,41 @@ var OpenCodeSqliteAgent = class extends DatabaseSessionSource {
|
|
|
2521
2889
|
};
|
|
2522
2890
|
contexts.set(sessionId, context);
|
|
2523
2891
|
}
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
const
|
|
2528
|
-
|
|
2529
|
-
const
|
|
2530
|
-
if (
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2892
|
+
return context;
|
|
2893
|
+
};
|
|
2894
|
+
for (const row of messageRows) {
|
|
2895
|
+
const sessionId = String(row.session_id ?? "");
|
|
2896
|
+
if (!sessionId) continue;
|
|
2897
|
+
const msgData = parseJsonRecord(row.data, this.name, "message.data");
|
|
2898
|
+
if (isInternalEventType(msgData.type)) continue;
|
|
2899
|
+
const parentId = withParentId ? String(row.parent_id ?? "") : "";
|
|
2900
|
+
const isChild = parentId !== "";
|
|
2901
|
+
if (isChild) {
|
|
2902
|
+
const childContext = ensureContext(sessionId);
|
|
2903
|
+
const parts2 = partsByMessage.get(String(row.id ?? "")) ?? [];
|
|
2904
|
+
if (parts2.length > 0) {
|
|
2905
|
+
accumulateTokenStats(childContext.stats, msgData, this.name);
|
|
2906
|
+
childContext.stats.message_count += 1;
|
|
2907
|
+
if (!childContext.messageTitle && String(msgData.role ?? "") === "user") {
|
|
2908
|
+
childContext.messageTitle = firstUserMessageTitle([
|
|
2909
|
+
{
|
|
2910
|
+
id: String(row.id ?? ""),
|
|
2911
|
+
role: "user",
|
|
2912
|
+
agent: null,
|
|
2913
|
+
time_created: Number(row.time_created ?? 0),
|
|
2914
|
+
parts: parts2
|
|
2915
|
+
}
|
|
2916
|
+
]);
|
|
2917
|
+
}
|
|
2918
|
+
}
|
|
2919
|
+
const parentContext = ensureContext(parentId);
|
|
2920
|
+
accumulateTokenStats(parentContext.stats, msgData, this.name);
|
|
2921
|
+
continue;
|
|
2922
|
+
}
|
|
2923
|
+
const parts = partsByMessage.get(String(row.id ?? "")) ?? [];
|
|
2924
|
+
if (parts.length === 0) continue;
|
|
2925
|
+
const context = ensureContext(sessionId);
|
|
2926
|
+
accumulateTokenStats(context.stats, msgData, this.name);
|
|
2534
2927
|
context.stats.message_count += 1;
|
|
2535
2928
|
if (!context.messageTitle && String(msgData.role ?? "") === "user") {
|
|
2536
2929
|
context.messageTitle = firstUserMessageTitle([
|
|
@@ -2551,6 +2944,29 @@ var OpenCodeSqliteAgent = class extends DatabaseSessionSource {
|
|
|
2551
2944
|
}
|
|
2552
2945
|
return contexts;
|
|
2553
2946
|
}
|
|
2947
|
+
sumChildTokenStats(db, parentSessionId) {
|
|
2948
|
+
if (!columnExists(db, "session", "parent_id")) return [];
|
|
2949
|
+
const childRows = db.prepare("SELECT id FROM session WHERE parent_id = ?").all(parentSessionId);
|
|
2950
|
+
const results = [];
|
|
2951
|
+
for (const child of childRows) {
|
|
2952
|
+
const childId = String(child.id ?? "");
|
|
2953
|
+
if (!childId) continue;
|
|
2954
|
+
const msgRows = db.prepare("SELECT data FROM message WHERE session_id = ? ORDER BY time_created ASC, id ASC").all(childId);
|
|
2955
|
+
const stats = {
|
|
2956
|
+
message_count: 0,
|
|
2957
|
+
total_input_tokens: 0,
|
|
2958
|
+
total_output_tokens: 0,
|
|
2959
|
+
total_cost: 0
|
|
2960
|
+
};
|
|
2961
|
+
for (const row of msgRows) {
|
|
2962
|
+
const msgData = parseJsonRecord(row.data, this.name, "message.data");
|
|
2963
|
+
if (isInternalEventType(msgData.type)) continue;
|
|
2964
|
+
accumulateTokenStats(stats, msgData, this.name);
|
|
2965
|
+
}
|
|
2966
|
+
results.push(stats);
|
|
2967
|
+
}
|
|
2968
|
+
return results;
|
|
2969
|
+
}
|
|
2554
2970
|
getSessionData(sessionId) {
|
|
2555
2971
|
if (!this.dbPath) {
|
|
2556
2972
|
this.dbPath = this.findDbPath();
|
|
@@ -2573,9 +2989,6 @@ var OpenCodeSqliteAgent = class extends DatabaseSessionSource {
|
|
|
2573
2989
|
const timeCreated = Number(sessionRow.time_created ?? 0);
|
|
2574
2990
|
const timeUpdated = Number(sessionRow.time_updated ?? timeCreated);
|
|
2575
2991
|
const messages = [];
|
|
2576
|
-
let totalCost = 0;
|
|
2577
|
-
let totalInputTokens = 0;
|
|
2578
|
-
let totalOutputTokens = 0;
|
|
2579
2992
|
let hasEstimatedCost = false;
|
|
2580
2993
|
const msgRows = db.prepare("SELECT * FROM message WHERE session_id = ? ORDER BY time_created ASC, id ASC").all(sessionId);
|
|
2581
2994
|
const partsByMessage = this.buildPartsByMessage(this.readSessionPartRows(db, sessionId));
|
|
@@ -2611,28 +3024,41 @@ var OpenCodeSqliteAgent = class extends DatabaseSessionSource {
|
|
|
2611
3024
|
firstUserMessageTitle(cleanedMessages),
|
|
2612
3025
|
null
|
|
2613
3026
|
);
|
|
3027
|
+
const stats = {
|
|
3028
|
+
message_count: cleanedMessages.length,
|
|
3029
|
+
total_input_tokens: 0,
|
|
3030
|
+
total_output_tokens: 0,
|
|
3031
|
+
total_cost: 0
|
|
3032
|
+
};
|
|
2614
3033
|
for (const message of cleanedMessages) {
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
3034
|
+
stats.total_cost += message.cost ?? 0;
|
|
3035
|
+
stats.total_input_tokens += message.tokens?.input ?? 0;
|
|
3036
|
+
stats.total_output_tokens += message.tokens?.output ?? 0;
|
|
2618
3037
|
if (message.cost_source === "estimated") hasEstimatedCost = true;
|
|
2619
3038
|
}
|
|
3039
|
+
for (const childStats of this.sumChildTokenStats(db, sessionId)) {
|
|
3040
|
+
stats.total_cost += childStats.total_cost;
|
|
3041
|
+
stats.total_input_tokens += childStats.total_input_tokens;
|
|
3042
|
+
stats.total_output_tokens += childStats.total_output_tokens;
|
|
3043
|
+
if (childStats.cost_source === "estimated") hasEstimatedCost = true;
|
|
3044
|
+
}
|
|
2620
3045
|
return {
|
|
2621
3046
|
reference: { agentName: this.name, sessionId: id },
|
|
2622
3047
|
id,
|
|
2623
3048
|
title,
|
|
2624
3049
|
slug,
|
|
2625
3050
|
directory,
|
|
3051
|
+
parent_reference: sessionRow.parent_id == null || String(sessionRow.parent_id) === "" ? void 0 : { agentName: this.name, sessionId: String(sessionRow.parent_id) },
|
|
2626
3052
|
version: asString(sessionRow.version) ?? void 0,
|
|
2627
3053
|
time_created: timeCreated,
|
|
2628
3054
|
time_updated: timeUpdated,
|
|
2629
3055
|
summary_files: sessionRow.summary_files ?? void 0,
|
|
2630
3056
|
stats: {
|
|
2631
|
-
message_count:
|
|
2632
|
-
total_input_tokens:
|
|
2633
|
-
total_output_tokens:
|
|
2634
|
-
total_cost:
|
|
2635
|
-
cost_source:
|
|
3057
|
+
message_count: stats.message_count,
|
|
3058
|
+
total_input_tokens: stats.total_input_tokens,
|
|
3059
|
+
total_output_tokens: stats.total_output_tokens,
|
|
3060
|
+
total_cost: stats.total_cost,
|
|
3061
|
+
cost_source: stats.total_cost > 0 ? hasEstimatedCost ? "estimated" : "recorded" : void 0
|
|
2636
3062
|
},
|
|
2637
3063
|
messages: cleanedMessages
|
|
2638
3064
|
};
|
|
@@ -3076,7 +3502,7 @@ var KimiAgent = class extends FileSystemSessionSource {
|
|
|
3076
3502
|
const rawArgs = function_.arguments;
|
|
3077
3503
|
const normalizedArgs = normalizeToolArguments(rawArgs);
|
|
3078
3504
|
const buffer = typeof rawArgs === "string" && typeof normalizedArgs !== "string" ? rawArgs : null;
|
|
3079
|
-
const
|
|
3505
|
+
const toolPart2 = {
|
|
3080
3506
|
type: "tool",
|
|
3081
3507
|
tool: toolName,
|
|
3082
3508
|
callID: callId,
|
|
@@ -3085,7 +3511,7 @@ var KimiAgent = class extends FileSystemSessionSource {
|
|
|
3085
3511
|
time_created: timestampMs
|
|
3086
3512
|
};
|
|
3087
3513
|
builder.appendToolCall(
|
|
3088
|
-
|
|
3514
|
+
toolPart2,
|
|
3089
3515
|
{ id: `wire-${seq}`, timestampMs: 0, agent: "kimi" },
|
|
3090
3516
|
{ markModeAsTool: true, target: "current" }
|
|
3091
3517
|
);
|
|
@@ -3265,22 +3691,592 @@ var KimiAgent = class extends FileSystemSessionSource {
|
|
|
3265
3691
|
if (stats.total_cost > 0) {
|
|
3266
3692
|
stats.cost_source = "estimated";
|
|
3267
3693
|
}
|
|
3268
|
-
return stats;
|
|
3694
|
+
return stats;
|
|
3695
|
+
}
|
|
3696
|
+
buildSessionData(meta, builder, stats) {
|
|
3697
|
+
const transcript = builder.finish(stats);
|
|
3698
|
+
return {
|
|
3699
|
+
reference: { agentName: this.name, sessionId: meta.id },
|
|
3700
|
+
id: meta.id,
|
|
3701
|
+
title: meta.title,
|
|
3702
|
+
slug: `kimi/${meta.id}`,
|
|
3703
|
+
directory: meta.cwd,
|
|
3704
|
+
time_created: meta.createdAt,
|
|
3705
|
+
time_updated: meta.createdAt,
|
|
3706
|
+
stats: transcript.stats,
|
|
3707
|
+
messages: transcript.messages
|
|
3708
|
+
};
|
|
3709
|
+
}
|
|
3710
|
+
};
|
|
3711
|
+
var KIMI_CODE_TOOL_TITLE_MAP = {
|
|
3712
|
+
Read: "read",
|
|
3713
|
+
Write: "write",
|
|
3714
|
+
Edit: "edit",
|
|
3715
|
+
Bash: "bash",
|
|
3716
|
+
TodoList: "todo",
|
|
3717
|
+
AskUserQuestion: "ask",
|
|
3718
|
+
EnterPlanMode: "plan mode",
|
|
3719
|
+
ExitPlanMode: "plan approved",
|
|
3720
|
+
ReadFile: "read",
|
|
3721
|
+
Glob: "glob",
|
|
3722
|
+
StrReplaceFile: "edit",
|
|
3723
|
+
Grep: "grep",
|
|
3724
|
+
WriteFile: "write",
|
|
3725
|
+
Shell: "bash"
|
|
3726
|
+
};
|
|
3727
|
+
var KIMI_CODE_IGNORED_TOOLS = /* @__PURE__ */ new Set(["SetTodoList"]);
|
|
3728
|
+
function resolveKimiCodeDataRoot() {
|
|
3729
|
+
return resolveHomePath("KIMI_CODE_HOME", ".kimi-code");
|
|
3730
|
+
}
|
|
3731
|
+
function mapToolTitle2(toolName) {
|
|
3732
|
+
return KIMI_CODE_TOOL_TITLE_MAP[toolName] ?? toolName;
|
|
3733
|
+
}
|
|
3734
|
+
function normalizeToolArguments2(raw) {
|
|
3735
|
+
if (typeof raw !== "string") return raw;
|
|
3736
|
+
try {
|
|
3737
|
+
return JSON.parse(raw);
|
|
3738
|
+
} catch {
|
|
3739
|
+
return raw;
|
|
3740
|
+
}
|
|
3741
|
+
}
|
|
3742
|
+
function parseTimestamp(raw) {
|
|
3743
|
+
if (typeof raw === "number") return Number.isFinite(raw) ? raw : null;
|
|
3744
|
+
if (typeof raw !== "string" || raw.trim() === "") return null;
|
|
3745
|
+
const numeric = Number(raw);
|
|
3746
|
+
if (Number.isFinite(numeric)) return numeric;
|
|
3747
|
+
const parsed = Date.parse(raw);
|
|
3748
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
3749
|
+
}
|
|
3750
|
+
function timestampFromRecord(record) {
|
|
3751
|
+
return parseTimestamp(record.time) ?? 0;
|
|
3752
|
+
}
|
|
3753
|
+
function contentText(content) {
|
|
3754
|
+
if (typeof content === "string") return content;
|
|
3755
|
+
if (!Array.isArray(content)) {
|
|
3756
|
+
const record = asRecord(content);
|
|
3757
|
+
return record ? String(record.text ?? "") : "";
|
|
3758
|
+
}
|
|
3759
|
+
return content.map((item) => {
|
|
3760
|
+
if (typeof item === "string") return item;
|
|
3761
|
+
const record = asRecord(item);
|
|
3762
|
+
return record ? String(record.text ?? "") : "";
|
|
3763
|
+
}).join(" ");
|
|
3764
|
+
}
|
|
3765
|
+
function contentParts(content, timestampMs) {
|
|
3766
|
+
const values = Array.isArray(content) ? content : [content];
|
|
3767
|
+
const parts = [];
|
|
3768
|
+
for (const value of values) {
|
|
3769
|
+
if (typeof value === "string") {
|
|
3770
|
+
const text = cleanInternalText(value);
|
|
3771
|
+
if (text) parts.push({ type: "text", text, time_created: timestampMs });
|
|
3772
|
+
continue;
|
|
3773
|
+
}
|
|
3774
|
+
const record = asRecord(value);
|
|
3775
|
+
if (!record) continue;
|
|
3776
|
+
const type = asString(record.type) ?? "";
|
|
3777
|
+
if (type === "text") {
|
|
3778
|
+
const text = cleanInternalText(asString(record.text) ?? "");
|
|
3779
|
+
if (text) parts.push({ type: "text", text, time_created: timestampMs });
|
|
3780
|
+
continue;
|
|
3781
|
+
}
|
|
3782
|
+
if (type === "think") {
|
|
3783
|
+
const text = cleanInternalText(asString(record.think) ?? "");
|
|
3784
|
+
if (text) parts.push({ type: "reasoning", text, time_created: timestampMs });
|
|
3785
|
+
continue;
|
|
3786
|
+
}
|
|
3787
|
+
if (type === "plan") {
|
|
3788
|
+
const text = cleanInternalText(asString(record.text) ?? "");
|
|
3789
|
+
if (text) {
|
|
3790
|
+
parts.push({
|
|
3791
|
+
type: "plan",
|
|
3792
|
+
text,
|
|
3793
|
+
approval_status: record.approved === false ? "fail" : "success",
|
|
3794
|
+
time_created: timestampMs
|
|
3795
|
+
});
|
|
3796
|
+
}
|
|
3797
|
+
continue;
|
|
3798
|
+
}
|
|
3799
|
+
if (type === "image") {
|
|
3800
|
+
const source = asRecord(record.source);
|
|
3801
|
+
const imageUrl = asRecord(record.imageUrl);
|
|
3802
|
+
const url = asString(record.url) ?? asString(imageUrl?.url) ?? (source?.kind === "url" ? asString(source.url) : void 0);
|
|
3803
|
+
const data = asString(record.data) ?? (source?.kind === "base64" ? asString(source.data) : void 0);
|
|
3804
|
+
const mimeType = asString(record.mime_type) ?? asString(record.media_type) ?? (source?.kind === "base64" ? asString(source.media_type) : void 0) ?? "application/octet-stream";
|
|
3805
|
+
if (url) parts.push({ type: "image", url, mime_type: mimeType, time_created: timestampMs });
|
|
3806
|
+
else if (data) {
|
|
3807
|
+
parts.push({
|
|
3808
|
+
type: "image",
|
|
3809
|
+
data,
|
|
3810
|
+
mime_type: mimeType,
|
|
3811
|
+
time_created: timestampMs
|
|
3812
|
+
});
|
|
3813
|
+
}
|
|
3814
|
+
continue;
|
|
3815
|
+
}
|
|
3816
|
+
if (type === "image_url") {
|
|
3817
|
+
const imageUrl = asRecord(record.imageUrl);
|
|
3818
|
+
const url = asString(imageUrl?.url) ?? asString(record.url);
|
|
3819
|
+
if (url) parts.push({ type: "image", url, time_created: timestampMs });
|
|
3820
|
+
}
|
|
3821
|
+
}
|
|
3822
|
+
return parts;
|
|
3823
|
+
}
|
|
3824
|
+
function toolOutputParts(output, timestampMs) {
|
|
3825
|
+
if (typeof output === "string") {
|
|
3826
|
+
const text2 = cleanInternalText(output);
|
|
3827
|
+
return text2 ? [{ type: "text", text: text2, time_created: timestampMs }] : [];
|
|
3828
|
+
}
|
|
3829
|
+
if (Array.isArray(output)) return contentParts(output, timestampMs);
|
|
3830
|
+
if (output == null) return [];
|
|
3831
|
+
const record = asRecord(output);
|
|
3832
|
+
if (record?.type === "text") return contentParts([record], timestampMs);
|
|
3833
|
+
const text = cleanInternalText(JSON.stringify(output, null, 2));
|
|
3834
|
+
return text ? [{ type: "text", text, time_created: timestampMs }] : [];
|
|
3835
|
+
}
|
|
3836
|
+
function toolPart(toolName, callId, input, timestampMs) {
|
|
3837
|
+
return {
|
|
3838
|
+
type: "tool",
|
|
3839
|
+
tool: toolName,
|
|
3840
|
+
callID: callId,
|
|
3841
|
+
title: mapToolTitle2(toolName),
|
|
3842
|
+
state: {
|
|
3843
|
+
status: "running",
|
|
3844
|
+
...input === void 0 ? {} : { input },
|
|
3845
|
+
output: null
|
|
3846
|
+
},
|
|
3847
|
+
time_created: timestampMs
|
|
3848
|
+
};
|
|
3849
|
+
}
|
|
3850
|
+
function toolCallParts(message, timestampMs, ignoredToolCallIds) {
|
|
3851
|
+
const calls = asArray(message.toolCalls) ?? [];
|
|
3852
|
+
const parts = [];
|
|
3853
|
+
for (const call of calls) {
|
|
3854
|
+
const callRecord = asRecord(call);
|
|
3855
|
+
const functionRecord = asRecord(callRecord?.function) ?? callRecord;
|
|
3856
|
+
const toolName = asString(functionRecord?.name)?.trim() ?? "";
|
|
3857
|
+
const callId = asString(callRecord?.id)?.trim() ?? "";
|
|
3858
|
+
if (!toolName || !callId) continue;
|
|
3859
|
+
if (KIMI_CODE_IGNORED_TOOLS.has(toolName)) {
|
|
3860
|
+
ignoredToolCallIds.add(callId);
|
|
3861
|
+
continue;
|
|
3862
|
+
}
|
|
3863
|
+
const rawArguments = functionRecord?.arguments ?? callRecord?.arguments;
|
|
3864
|
+
parts.push(toolPart(toolName, callId, normalizeToolArguments2(rawArguments), timestampMs));
|
|
3865
|
+
}
|
|
3866
|
+
return parts;
|
|
3867
|
+
}
|
|
3868
|
+
function addToolResolution(builder, callId, output, timestampMs, isError = false, note) {
|
|
3869
|
+
const outputParts = toolOutputParts(output, timestampMs);
|
|
3870
|
+
return builder.resolveToolCall(callId, {
|
|
3871
|
+
output: outputParts,
|
|
3872
|
+
status: isError ? "error" : "completed",
|
|
3873
|
+
...note ? { metadata: { note: cleanInternalText(note) } } : {}
|
|
3874
|
+
});
|
|
3875
|
+
}
|
|
3876
|
+
function usageNumber(usage, field) {
|
|
3877
|
+
return asNumber(usage[field]) ?? 0;
|
|
3878
|
+
}
|
|
3879
|
+
function emptyStats() {
|
|
3880
|
+
return {
|
|
3881
|
+
message_count: 0,
|
|
3882
|
+
total_input_tokens: 0,
|
|
3883
|
+
total_output_tokens: 0,
|
|
3884
|
+
total_cost: 0
|
|
3885
|
+
};
|
|
3886
|
+
}
|
|
3887
|
+
function buildUsageTotals() {
|
|
3888
|
+
return {
|
|
3889
|
+
totalCost: 0,
|
|
3890
|
+
totalInputTokens: 0,
|
|
3891
|
+
totalOutputTokens: 0,
|
|
3892
|
+
totalCacheReadTokens: 0,
|
|
3893
|
+
totalCacheCreateTokens: 0,
|
|
3894
|
+
modelUsage: {}
|
|
3895
|
+
};
|
|
3896
|
+
}
|
|
3897
|
+
function applyUsage(builder, record, activeModel, totals) {
|
|
3898
|
+
const usage = asRecord(record.usage);
|
|
3899
|
+
if (!usage) return;
|
|
3900
|
+
const cacheRead = usageNumber(usage, "inputCacheRead");
|
|
3901
|
+
const cacheCreate = usageNumber(usage, "inputCacheCreation");
|
|
3902
|
+
const inputOther = usageNumber(usage, "inputOther");
|
|
3903
|
+
const output = usageNumber(usage, "output");
|
|
3904
|
+
const input = inputOther + cacheRead + cacheCreate;
|
|
3905
|
+
const model = asString(record.model) ?? activeModel;
|
|
3906
|
+
const tokens = {
|
|
3907
|
+
input,
|
|
3908
|
+
output,
|
|
3909
|
+
cache_read: cacheRead,
|
|
3910
|
+
cache_create: cacheCreate
|
|
3911
|
+
};
|
|
3912
|
+
const cost = estimateTokenCost(model, tokens);
|
|
3913
|
+
totals.totalInputTokens += input;
|
|
3914
|
+
totals.totalOutputTokens += output;
|
|
3915
|
+
totals.totalCacheReadTokens += cacheRead;
|
|
3916
|
+
totals.totalCacheCreateTokens += cacheCreate;
|
|
3917
|
+
if (model) totals.modelUsage[model] = (totals.modelUsage[model] ?? 0) + input + output;
|
|
3918
|
+
if (cost !== null) totals.totalCost += cost;
|
|
3919
|
+
builder.attachUsageToLatestAssistant(tokens, {
|
|
3920
|
+
model,
|
|
3921
|
+
cost: cost ?? void 0,
|
|
3922
|
+
costSource: cost === null ? void 0 : "estimated"
|
|
3923
|
+
});
|
|
3924
|
+
}
|
|
3925
|
+
function readState(stateFile) {
|
|
3926
|
+
try {
|
|
3927
|
+
return asRecord(JSON.parse(readFileSync4(stateFile, "utf-8"))) ?? null;
|
|
3928
|
+
} catch {
|
|
3929
|
+
return null;
|
|
3930
|
+
}
|
|
3931
|
+
}
|
|
3932
|
+
var KimiCodeAgent = class extends FileSystemSessionSource {
|
|
3933
|
+
name = "kimi-code";
|
|
3934
|
+
displayName = "Kimi-Code";
|
|
3935
|
+
basePath = null;
|
|
3936
|
+
workDirBySessionPath = /* @__PURE__ */ new Map();
|
|
3937
|
+
findBasePath() {
|
|
3938
|
+
const sessionsPath = join9(resolveKimiCodeDataRoot(), "sessions");
|
|
3939
|
+
return existsSync8(sessionsPath) ? sessionsPath : null;
|
|
3940
|
+
}
|
|
3941
|
+
getSessionWatchPlan() {
|
|
3942
|
+
const dataRoot = resolveKimiCodeDataRoot();
|
|
3943
|
+
return {
|
|
3944
|
+
status: "supported",
|
|
3945
|
+
targets: [
|
|
3946
|
+
{ root: dataRoot, path: join9(dataRoot, "sessions") },
|
|
3947
|
+
{ root: dataRoot, path: join9(dataRoot, "session_index.jsonl") }
|
|
3948
|
+
]
|
|
3949
|
+
};
|
|
3950
|
+
}
|
|
3951
|
+
isAvailable() {
|
|
3952
|
+
this.basePath = this.findBasePath();
|
|
3953
|
+
if (!this.basePath) return false;
|
|
3954
|
+
try {
|
|
3955
|
+
return this.listSessionDirs().length > 0;
|
|
3956
|
+
} catch {
|
|
3957
|
+
return false;
|
|
3958
|
+
}
|
|
3959
|
+
}
|
|
3960
|
+
loadSessionIndex() {
|
|
3961
|
+
this.workDirBySessionPath.clear();
|
|
3962
|
+
if (!this.basePath) return;
|
|
3963
|
+
const indexPath = join9(dirname5(this.basePath), "session_index.jsonl");
|
|
3964
|
+
if (!existsSync8(indexPath)) return;
|
|
3965
|
+
for (const record of readJsonlFile(indexPath)) {
|
|
3966
|
+
const sessionPath = asString(record.sessionDir);
|
|
3967
|
+
const workDir = asString(record.workDir);
|
|
3968
|
+
if (!sessionPath || !workDir) continue;
|
|
3969
|
+
this.workDirBySessionPath.set(resolve(sessionPath), workDir);
|
|
3970
|
+
}
|
|
3971
|
+
}
|
|
3972
|
+
listSessionDirs() {
|
|
3973
|
+
if (!this.basePath) return [];
|
|
3974
|
+
const dirs = [];
|
|
3975
|
+
try {
|
|
3976
|
+
for (const bucket of readdirSync5(this.basePath, { withFileTypes: true })) {
|
|
3977
|
+
if (!bucket.isDirectory()) continue;
|
|
3978
|
+
const bucketPath = join9(this.basePath, bucket.name);
|
|
3979
|
+
for (const session of readdirSync5(bucketPath, { withFileTypes: true })) {
|
|
3980
|
+
if (!session.isDirectory()) continue;
|
|
3981
|
+
const sessionPath = join9(bucketPath, session.name);
|
|
3982
|
+
if (existsSync8(join9(sessionPath, "state.json")) && existsSync8(join9(sessionPath, "agents", "main", "wire.jsonl"))) {
|
|
3983
|
+
dirs.push(sessionPath);
|
|
3984
|
+
}
|
|
3985
|
+
}
|
|
3986
|
+
}
|
|
3987
|
+
} catch {
|
|
3988
|
+
return dirs;
|
|
3989
|
+
}
|
|
3990
|
+
return dirs;
|
|
3991
|
+
}
|
|
3992
|
+
resolveSessionSourceResult(sessionDir) {
|
|
3993
|
+
try {
|
|
3994
|
+
const stateFile = join9(sessionDir, "state.json");
|
|
3995
|
+
const wireFile = join9(sessionDir, "agents", "main", "wire.jsonl");
|
|
3996
|
+
if (!existsSync8(stateFile) || !existsSync8(wireFile)) return skippedSession("missing wire");
|
|
3997
|
+
const state = readState(stateFile);
|
|
3998
|
+
if (!state) return skippedSession("malformed state");
|
|
3999
|
+
const stateMtime = statSync5(stateFile).mtimeMs;
|
|
4000
|
+
const wireMtime = statSync5(wireFile).mtimeMs;
|
|
4001
|
+
const createdAt = parseTimestamp(state.createdAt) ?? parseTimestamp(state.created_at) ?? stateMtime;
|
|
4002
|
+
const updatedAt = Math.max(
|
|
4003
|
+
parseTimestamp(state.updatedAt) ?? parseTimestamp(state.updated_at) ?? createdAt,
|
|
4004
|
+
wireMtime
|
|
4005
|
+
);
|
|
4006
|
+
const custom = asRecord(state.custom);
|
|
4007
|
+
const workDir = asString(state.workDir) ?? asString(custom?.cwd) ?? this.workDirBySessionPath.get(resolve(sessionDir)) ?? "";
|
|
4008
|
+
const explicitTitle = asString(state.title) ?? asString(state.customTitle) ?? "";
|
|
4009
|
+
return parsedSession({
|
|
4010
|
+
id: basename6(sessionDir),
|
|
4011
|
+
sourcePath: sessionDir,
|
|
4012
|
+
stateFile,
|
|
4013
|
+
wireFile,
|
|
4014
|
+
workDir,
|
|
4015
|
+
createdAt,
|
|
4016
|
+
updatedAt,
|
|
4017
|
+
explicitTitle
|
|
4018
|
+
});
|
|
4019
|
+
} catch {
|
|
4020
|
+
return skippedSession("malformed session");
|
|
4021
|
+
}
|
|
4022
|
+
}
|
|
4023
|
+
sourceFingerprint(source) {
|
|
4024
|
+
return JSON.stringify([
|
|
4025
|
+
this.readFileMtimeMs(source.stateFile),
|
|
4026
|
+
this.readFileMtimeMs(source.wireFile),
|
|
4027
|
+
source.workDir
|
|
4028
|
+
]);
|
|
4029
|
+
}
|
|
4030
|
+
listSessionSources(options) {
|
|
4031
|
+
if (!this.basePath) return [];
|
|
4032
|
+
this.loadSessionIndex();
|
|
4033
|
+
const refs = [];
|
|
4034
|
+
for (const sessionDir of this.listSessionDirs()) {
|
|
4035
|
+
const source = getParsedSession(this.resolveSessionSourceResult(sessionDir));
|
|
4036
|
+
if (!source || !matchesScanWindow(source.createdAt, options)) continue;
|
|
4037
|
+
refs.push({
|
|
4038
|
+
sessionId: source.id,
|
|
4039
|
+
sourcePath: source.sourcePath,
|
|
4040
|
+
fingerprint: this.sourceFingerprint(source)
|
|
4041
|
+
});
|
|
4042
|
+
}
|
|
4043
|
+
return refs;
|
|
4044
|
+
}
|
|
4045
|
+
checkForChanges(sinceTimestamp, cachedSessions) {
|
|
4046
|
+
const result = super.checkForChanges(sinceTimestamp, cachedSessions);
|
|
4047
|
+
const emptySessionIds = cachedSessions.filter((session) => !this.hasMessages(session)).map((session) => session.id);
|
|
4048
|
+
if (emptySessionIds.length === 0) return result;
|
|
4049
|
+
return {
|
|
4050
|
+
...result,
|
|
4051
|
+
hasChanges: true,
|
|
4052
|
+
changedIds: [.../* @__PURE__ */ new Set([...result.changedIds ?? [], ...emptySessionIds])]
|
|
4053
|
+
};
|
|
4054
|
+
}
|
|
4055
|
+
filterCachedSessions(sessions) {
|
|
4056
|
+
return this.removeEmptyCachedSessions(sessions);
|
|
4057
|
+
}
|
|
4058
|
+
incrementalScan(cachedSessions, changedIds, refs) {
|
|
4059
|
+
const visibleSessions = this.removeEmptyCachedSessions(cachedSessions);
|
|
4060
|
+
return super.incrementalScan(visibleSessions, changedIds, refs);
|
|
4061
|
+
}
|
|
4062
|
+
hasMessages(session) {
|
|
4063
|
+
return session.stats.message_count > 0;
|
|
4064
|
+
}
|
|
4065
|
+
removeEmptyCachedSessions(sessions) {
|
|
4066
|
+
return sessions.filter((session) => {
|
|
4067
|
+
if (this.hasMessages(session)) return true;
|
|
4068
|
+
this.sessionMetaMap.delete(session.id);
|
|
4069
|
+
return false;
|
|
4070
|
+
});
|
|
4071
|
+
}
|
|
4072
|
+
scanSessionSource(sourcePath) {
|
|
4073
|
+
this.loadSessionIndex();
|
|
4074
|
+
const source = getParsedSession(this.resolveSessionSourceResult(sourcePath));
|
|
4075
|
+
if (!source) return null;
|
|
4076
|
+
const parsed = this.parseWire(source);
|
|
4077
|
+
const transcript = parsed.builder.finish(parsed.stats);
|
|
4078
|
+
if (transcript.messages.length === 0) {
|
|
4079
|
+
this.sessionMetaMap.delete(source.id);
|
|
4080
|
+
return null;
|
|
4081
|
+
}
|
|
4082
|
+
const title = resolveSessionTitle(source.explicitTitle, parsed.firstUserTitle, null);
|
|
4083
|
+
const meta = {
|
|
4084
|
+
...source,
|
|
4085
|
+
title,
|
|
4086
|
+
sourceMtimeMs: source.createdAt,
|
|
4087
|
+
sourceFingerprint: this.sourceFingerprint(source)
|
|
4088
|
+
};
|
|
4089
|
+
this.sessionMetaMap.set(meta.id, meta);
|
|
4090
|
+
return {
|
|
4091
|
+
id: meta.id,
|
|
4092
|
+
slug: `${this.name}/${meta.id}`,
|
|
4093
|
+
title: meta.title,
|
|
4094
|
+
directory: meta.workDir,
|
|
4095
|
+
time_created: meta.createdAt,
|
|
4096
|
+
time_updated: meta.updatedAt,
|
|
4097
|
+
stats: transcript.stats,
|
|
4098
|
+
...Object.keys(parsed.modelUsage).length > 0 ? { model_usage: parsed.modelUsage } : {}
|
|
4099
|
+
};
|
|
3269
4100
|
}
|
|
3270
|
-
|
|
3271
|
-
const
|
|
4101
|
+
getSessionData(sessionId) {
|
|
4102
|
+
const meta = this.sessionMetaMap.get(sessionId);
|
|
4103
|
+
if (!meta) throw new Error(`Session not found: ${sessionId}`);
|
|
4104
|
+
const parsed = this.parseWire(meta);
|
|
4105
|
+
const transcript = parsed.builder.finish(parsed.stats);
|
|
3272
4106
|
return {
|
|
3273
4107
|
reference: { agentName: this.name, sessionId: meta.id },
|
|
3274
4108
|
id: meta.id,
|
|
3275
4109
|
title: meta.title,
|
|
3276
|
-
slug:
|
|
3277
|
-
directory: meta.
|
|
4110
|
+
slug: `${this.name}/${meta.id}`,
|
|
4111
|
+
directory: meta.workDir,
|
|
3278
4112
|
time_created: meta.createdAt,
|
|
3279
|
-
time_updated: meta.
|
|
4113
|
+
time_updated: meta.updatedAt,
|
|
3280
4114
|
stats: transcript.stats,
|
|
3281
4115
|
messages: transcript.messages
|
|
3282
4116
|
};
|
|
3283
4117
|
}
|
|
4118
|
+
parseWire(source) {
|
|
4119
|
+
const builder = new TranscriptBuilder();
|
|
4120
|
+
const totals = buildUsageTotals();
|
|
4121
|
+
const ignoredToolCallIds = /* @__PURE__ */ new Set();
|
|
4122
|
+
let activeModel = null;
|
|
4123
|
+
let activeProvider = null;
|
|
4124
|
+
let firstUserTitle = null;
|
|
4125
|
+
let sequence = 0;
|
|
4126
|
+
for (const record of readJsonlFile(source.wireFile)) {
|
|
4127
|
+
sequence += 1;
|
|
4128
|
+
try {
|
|
4129
|
+
const timestampMs = timestampFromRecord(record);
|
|
4130
|
+
const recordType = asString(record.type) ?? "";
|
|
4131
|
+
if (recordType === "llm.request") {
|
|
4132
|
+
activeModel = asString(record.model) ?? activeModel;
|
|
4133
|
+
activeProvider = asString(record.provider) ?? activeProvider;
|
|
4134
|
+
continue;
|
|
4135
|
+
}
|
|
4136
|
+
if (recordType === "config.update") {
|
|
4137
|
+
activeModel = asString(record.modelAlias) ?? activeModel;
|
|
4138
|
+
continue;
|
|
4139
|
+
}
|
|
4140
|
+
if (recordType === "usage.record") {
|
|
4141
|
+
applyUsage(builder, record, activeModel, totals);
|
|
4142
|
+
continue;
|
|
4143
|
+
}
|
|
4144
|
+
if (recordType === "context.append_message") {
|
|
4145
|
+
const message = asRecord(record.message);
|
|
4146
|
+
if (!message) continue;
|
|
4147
|
+
const role = asString(message.role) ?? "";
|
|
4148
|
+
const messageTimestamp = timestampMs;
|
|
4149
|
+
if (role === "user") {
|
|
4150
|
+
const text = normalizeTitleText(contentText(message.content));
|
|
4151
|
+
if (!firstUserTitle && text) firstUserTitle = text;
|
|
4152
|
+
}
|
|
4153
|
+
if (role === "tool") {
|
|
4154
|
+
const callId = asString(message.toolCallId)?.trim() ?? "";
|
|
4155
|
+
const output = message.content;
|
|
4156
|
+
if (callId && ignoredToolCallIds.has(callId)) continue;
|
|
4157
|
+
if (callId && addToolResolution(builder, callId, output, messageTimestamp)) continue;
|
|
4158
|
+
const outputParts = toolOutputParts(output, messageTimestamp);
|
|
4159
|
+
if (outputParts.length > 0) {
|
|
4160
|
+
builder.appendMessage({
|
|
4161
|
+
id: `wire-${sequence}`,
|
|
4162
|
+
role: "tool",
|
|
4163
|
+
timestampMs: messageTimestamp,
|
|
4164
|
+
parts: outputParts
|
|
4165
|
+
});
|
|
4166
|
+
}
|
|
4167
|
+
continue;
|
|
4168
|
+
}
|
|
4169
|
+
if (role !== "user" && role !== "assistant") continue;
|
|
4170
|
+
const parts = [
|
|
4171
|
+
...contentParts(message.content, messageTimestamp),
|
|
4172
|
+
...role === "assistant" ? toolCallParts(message, messageTimestamp, ignoredToolCallIds) : []
|
|
4173
|
+
];
|
|
4174
|
+
if (parts.length === 0) continue;
|
|
4175
|
+
const allTools = parts.every((part) => part.type === "tool");
|
|
4176
|
+
const input = {
|
|
4177
|
+
id: `wire-${sequence}`,
|
|
4178
|
+
role,
|
|
4179
|
+
timestampMs: messageTimestamp,
|
|
4180
|
+
parts,
|
|
4181
|
+
...role === "assistant" ? {
|
|
4182
|
+
agent: this.name,
|
|
4183
|
+
mode: allTools ? "tool" : void 0,
|
|
4184
|
+
model: activeModel,
|
|
4185
|
+
provider: activeProvider
|
|
4186
|
+
} : {}
|
|
4187
|
+
};
|
|
4188
|
+
builder.appendMessage(input);
|
|
4189
|
+
continue;
|
|
4190
|
+
}
|
|
4191
|
+
if (recordType === "context.append_loop_event") {
|
|
4192
|
+
const event = asRecord(record.event);
|
|
4193
|
+
if (!event) continue;
|
|
4194
|
+
const eventType = asString(event.type) ?? "";
|
|
4195
|
+
const metadata = {
|
|
4196
|
+
id: `wire-${sequence}`,
|
|
4197
|
+
timestampMs,
|
|
4198
|
+
agent: this.name,
|
|
4199
|
+
model: activeModel,
|
|
4200
|
+
provider: activeProvider
|
|
4201
|
+
};
|
|
4202
|
+
if (eventType === "step.begin") {
|
|
4203
|
+
builder.beginTurn();
|
|
4204
|
+
continue;
|
|
4205
|
+
}
|
|
4206
|
+
if (eventType === "content.part") {
|
|
4207
|
+
const part = asRecord(event.part);
|
|
4208
|
+
if (!part) continue;
|
|
4209
|
+
const parts = contentParts([part], timestampMs);
|
|
4210
|
+
for (const contentPart of parts) {
|
|
4211
|
+
if (contentPart.type === "tool") continue;
|
|
4212
|
+
if (contentPart.type === "image" && builder.appendToCurrentAssistant(contentPart)) {
|
|
4213
|
+
continue;
|
|
4214
|
+
}
|
|
4215
|
+
builder.appendAssistantPart(contentPart, metadata, { grouping: "current" });
|
|
4216
|
+
}
|
|
4217
|
+
continue;
|
|
4218
|
+
}
|
|
4219
|
+
if (eventType === "tool.call") {
|
|
4220
|
+
const toolName = asString(event.name)?.trim() ?? "";
|
|
4221
|
+
const callId = asString(event.toolCallId)?.trim() ?? "";
|
|
4222
|
+
if (!toolName || !callId) continue;
|
|
4223
|
+
if (KIMI_CODE_IGNORED_TOOLS.has(toolName)) {
|
|
4224
|
+
ignoredToolCallIds.add(callId);
|
|
4225
|
+
continue;
|
|
4226
|
+
}
|
|
4227
|
+
builder.appendToolCall(toolPart(toolName, callId, event.args, timestampMs), metadata, {
|
|
4228
|
+
markModeAsTool: true,
|
|
4229
|
+
target: "current"
|
|
4230
|
+
});
|
|
4231
|
+
continue;
|
|
4232
|
+
}
|
|
4233
|
+
if (eventType === "tool.result") {
|
|
4234
|
+
const callId = asString(event.toolCallId)?.trim() ?? "";
|
|
4235
|
+
const result = asRecord(event.result);
|
|
4236
|
+
if (!callId || ignoredToolCallIds.has(callId)) continue;
|
|
4237
|
+
const output = result?.output;
|
|
4238
|
+
const isError = result?.isError === true;
|
|
4239
|
+
const note = asString(result?.note);
|
|
4240
|
+
if (addToolResolution(builder, callId, output, timestampMs, isError, note)) continue;
|
|
4241
|
+
const outputParts = toolOutputParts(output, timestampMs);
|
|
4242
|
+
if (outputParts.length > 0) {
|
|
4243
|
+
builder.appendMessage({
|
|
4244
|
+
id: `wire-${sequence}`,
|
|
4245
|
+
role: "tool",
|
|
4246
|
+
timestampMs,
|
|
4247
|
+
parts: outputParts
|
|
4248
|
+
});
|
|
4249
|
+
}
|
|
4250
|
+
}
|
|
4251
|
+
continue;
|
|
4252
|
+
}
|
|
4253
|
+
if (recordType === "context.apply_compaction") {
|
|
4254
|
+
const summary = cleanInternalText(asString(record.summary) ?? "");
|
|
4255
|
+
if (summary) {
|
|
4256
|
+
builder.appendMessage({
|
|
4257
|
+
id: `wire-${sequence}`,
|
|
4258
|
+
role: "user",
|
|
4259
|
+
timestampMs,
|
|
4260
|
+
parts: [{ type: "text", text: summary, time_created: timestampMs }]
|
|
4261
|
+
});
|
|
4262
|
+
}
|
|
4263
|
+
}
|
|
4264
|
+
} catch {
|
|
4265
|
+
continue;
|
|
4266
|
+
}
|
|
4267
|
+
}
|
|
4268
|
+
const stats = {
|
|
4269
|
+
...emptyStats(),
|
|
4270
|
+
total_input_tokens: totals.totalInputTokens,
|
|
4271
|
+
total_output_tokens: totals.totalOutputTokens,
|
|
4272
|
+
total_cost: Number(totals.totalCost.toFixed(8)),
|
|
4273
|
+
total_tokens: totals.totalInputTokens + totals.totalOutputTokens,
|
|
4274
|
+
...totals.totalCacheReadTokens > 0 ? { total_cache_read_tokens: totals.totalCacheReadTokens } : {},
|
|
4275
|
+
...totals.totalCacheCreateTokens > 0 ? { total_cache_create_tokens: totals.totalCacheCreateTokens } : {},
|
|
4276
|
+
...totals.totalCost > 0 ? { cost_source: "estimated" } : {}
|
|
4277
|
+
};
|
|
4278
|
+
return { builder, stats, firstUserTitle, modelUsage: totals.modelUsage };
|
|
4279
|
+
}
|
|
3284
4280
|
};
|
|
3285
4281
|
var PARSE_FAIL = /* @__PURE__ */ Symbol("parse-fail");
|
|
3286
4282
|
var EXEC_OUTPUT_ENVELOPE_RE = /^Script completed\nWall time [^\n]*\nOutput:\n?/;
|
|
@@ -3543,7 +4539,7 @@ var PROPOSED_PLAN_PATTERN = /<proposed_plan>\s*([\s\S]*?)\s*<\/proposed_plan>/;
|
|
|
3543
4539
|
var PLAN_APPROVAL_PREFIX = "PLEASE IMPLEMENT THIS PLAN";
|
|
3544
4540
|
var SUBAGENT_NOTIFICATION_PATTERN = /<subagent_notification>\s*([\s\S]*?)\s*<\/subagent_notification>/;
|
|
3545
4541
|
var HEAD_INDEX_VERSION2 = "codex-head-v1";
|
|
3546
|
-
var PARSER_VERSION = "codex-parser-
|
|
4542
|
+
var PARSER_VERSION = "codex-parser-v6";
|
|
3547
4543
|
function resolveCodexDataRoot() {
|
|
3548
4544
|
return resolveHomePath("CODEX_HOME", ".codex");
|
|
3549
4545
|
}
|
|
@@ -3566,7 +4562,7 @@ var CODEX_TOOL_TITLE_MAP = {
|
|
|
3566
4562
|
subagent: "subagent"
|
|
3567
4563
|
};
|
|
3568
4564
|
function extractSessionId(filename) {
|
|
3569
|
-
const stem =
|
|
4565
|
+
const stem = basename7(filename, ".jsonl");
|
|
3570
4566
|
const parts = stem.split("-");
|
|
3571
4567
|
if (parts.length >= 5) {
|
|
3572
4568
|
return parts.slice(-5).join("-");
|
|
@@ -3595,6 +4591,14 @@ function narrowRecordField(value, field) {
|
|
|
3595
4591
|
function extractPayload(data) {
|
|
3596
4592
|
return narrowRecordField(data["payload"], "payload") ?? {};
|
|
3597
4593
|
}
|
|
4594
|
+
function extractThreadMeta(firstRecord) {
|
|
4595
|
+
if (firstRecord["type"] !== "session_meta") return null;
|
|
4596
|
+
const payload = extractPayload(firstRecord);
|
|
4597
|
+
const threadSource = asString(payload["thread_source"]) ?? "";
|
|
4598
|
+
const parentThreadId = asString(payload["parent_thread_id"]) ?? null;
|
|
4599
|
+
const agentNickname = asString(payload["agent_nickname"]) ?? null;
|
|
4600
|
+
return { threadSource, parentThreadId, agentNickname };
|
|
4601
|
+
}
|
|
3598
4602
|
function extractTokenUsage(payload) {
|
|
3599
4603
|
const info = narrowRecordField(payload["info"], "token_count.info");
|
|
3600
4604
|
return {
|
|
@@ -3620,7 +4624,7 @@ function resolveToolIdentity(name, namespace) {
|
|
|
3620
4624
|
metadata: { name, namespace: namespaceText }
|
|
3621
4625
|
};
|
|
3622
4626
|
}
|
|
3623
|
-
function
|
|
4627
|
+
function normalizeToolArguments3(raw) {
|
|
3624
4628
|
if (typeof raw === "string") {
|
|
3625
4629
|
try {
|
|
3626
4630
|
return JSON.parse(raw);
|
|
@@ -3647,6 +4651,16 @@ function flattenOutputText(output) {
|
|
|
3647
4651
|
}
|
|
3648
4652
|
return "";
|
|
3649
4653
|
}
|
|
4654
|
+
function extractAssistantOutputText(payload) {
|
|
4655
|
+
const content = payload["content"];
|
|
4656
|
+
if (Array.isArray(content)) {
|
|
4657
|
+
return content.map((item) => {
|
|
4658
|
+
const record = asRecord(item);
|
|
4659
|
+
return record && String(record["type"] ?? "") === "output_text" ? String(record["text"] ?? "") : "";
|
|
4660
|
+
}).filter(Boolean).join("\n");
|
|
4661
|
+
}
|
|
4662
|
+
return "";
|
|
4663
|
+
}
|
|
3650
4664
|
var PATCH_BEGIN_RE = /\*\*\* Begin Patch/;
|
|
3651
4665
|
var PATCH_END_RE = /\*\*\* End Patch/;
|
|
3652
4666
|
var PATCH_HEADER_RE = /\*\*\*\s+(Add|Delete|Update|Move)\s+File:\s*(.+)/;
|
|
@@ -3729,6 +4743,18 @@ function extractPatchContent(lines, startIndex) {
|
|
|
3729
4743
|
}
|
|
3730
4744
|
return { text: contentLines.join("\n"), nextLineIndex: i };
|
|
3731
4745
|
}
|
|
4746
|
+
function compareSourceActivityDesc(left, right) {
|
|
4747
|
+
const leftTimestamp = sourceTimestamp(left.file, left.stat.mtimeMs);
|
|
4748
|
+
const rightTimestamp = sourceTimestamp(right.file, right.stat.mtimeMs);
|
|
4749
|
+
return rightTimestamp - leftTimestamp || left.file.localeCompare(right.file);
|
|
4750
|
+
}
|
|
4751
|
+
function sourceTimestamp(filePath, fallback) {
|
|
4752
|
+
const match = basename7(filePath).match(/^rollout-(\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2})-/);
|
|
4753
|
+
if (!match) return fallback;
|
|
4754
|
+
const timestamp = match[1].replace(/-(\d{2})-(\d{2})$/, ":$1:$2");
|
|
4755
|
+
const parsed = Date.parse(timestamp);
|
|
4756
|
+
return Number.isFinite(parsed) ? parsed : fallback;
|
|
4757
|
+
}
|
|
3732
4758
|
var CodexAgent = class extends SingleFileSessionSource {
|
|
3733
4759
|
name = "codex";
|
|
3734
4760
|
displayName = "Codex";
|
|
@@ -3736,17 +4762,19 @@ var CodexAgent = class extends SingleFileSessionSource {
|
|
|
3736
4762
|
sessionIndexCache = /* @__PURE__ */ new Map();
|
|
3737
4763
|
sessionIndexMtime;
|
|
3738
4764
|
sessionIndexPath;
|
|
4765
|
+
subagentIndex = null;
|
|
4766
|
+
subagentStatsByParent = /* @__PURE__ */ new Map();
|
|
3739
4767
|
// ---- BaseAgent implementation ----
|
|
3740
4768
|
findBasePath() {
|
|
3741
|
-
return firstExisting(
|
|
4769
|
+
return firstExisting(join10(resolveCodexDataRoot(), "sessions"));
|
|
3742
4770
|
}
|
|
3743
4771
|
getSessionWatchPlan() {
|
|
3744
4772
|
const dataRoot = resolveCodexDataRoot();
|
|
3745
4773
|
return {
|
|
3746
4774
|
status: "supported",
|
|
3747
4775
|
targets: [
|
|
3748
|
-
{ path:
|
|
3749
|
-
{ path:
|
|
4776
|
+
{ path: join10(dataRoot, "sessions") },
|
|
4777
|
+
{ path: join10(dataRoot, "session_index.jsonl") }
|
|
3750
4778
|
]
|
|
3751
4779
|
};
|
|
3752
4780
|
}
|
|
@@ -3758,16 +4786,119 @@ var CodexAgent = class extends SingleFileSessionSource {
|
|
|
3758
4786
|
listSessionSources(options) {
|
|
3759
4787
|
if (!this.basePath) return [];
|
|
3760
4788
|
this.loadSessionIndex();
|
|
3761
|
-
return this.
|
|
4789
|
+
return this.listScanSources(options).map(({ file, stat }) => ({
|
|
3762
4790
|
sessionId: extractSessionId(file),
|
|
3763
4791
|
sourcePath: file,
|
|
3764
4792
|
fingerprint: this.sourceFingerprint(file, stat)
|
|
3765
4793
|
}));
|
|
3766
4794
|
}
|
|
4795
|
+
setSessionMetaMap(meta) {
|
|
4796
|
+
super.setSessionMetaMap(meta);
|
|
4797
|
+
this.subagentIndex = null;
|
|
4798
|
+
this.subagentStatsByParent.clear();
|
|
4799
|
+
}
|
|
4800
|
+
/**
|
|
4801
|
+
* A changed subagent file leaves its parent's aggregated token stats stale,
|
|
4802
|
+
* so the parent must re-parse alongside the child.
|
|
4803
|
+
*/
|
|
4804
|
+
expandChangedSessionIds(changedIds, refs) {
|
|
4805
|
+
if (changedIds.length === 0) return changedIds;
|
|
4806
|
+
const pathById = new Map((refs ?? []).map((ref) => [ref.sessionId, ref.sourcePath]));
|
|
4807
|
+
const expanded = new Set(changedIds);
|
|
4808
|
+
for (const id of changedIds) {
|
|
4809
|
+
const sourcePath = pathById.get(id) ?? this.sessionMetaMap.get(id)?.sourcePath;
|
|
4810
|
+
const threadMeta = sourcePath ? this.readThreadMeta(sourcePath) : null;
|
|
4811
|
+
const parentId = threadMeta ? threadMeta.threadSource === "subagent" ? threadMeta.parentThreadId : null : this.sessionMetaMap.get(id)?.parentThreadId ?? null;
|
|
4812
|
+
if (!parentId) continue;
|
|
4813
|
+
expanded.add(parentId);
|
|
4814
|
+
this.subagentIndex = null;
|
|
4815
|
+
this.subagentStatsByParent.delete(parentId);
|
|
4816
|
+
}
|
|
4817
|
+
return [...expanded];
|
|
4818
|
+
}
|
|
4819
|
+
readThreadMeta(filePath) {
|
|
4820
|
+
try {
|
|
4821
|
+
const firstLine = this.readFilePrefix(filePath).split("\n").filter((l) => l.trim())[0];
|
|
4822
|
+
if (!firstLine) return null;
|
|
4823
|
+
return extractThreadMeta(JSON.parse(firstLine));
|
|
4824
|
+
} catch {
|
|
4825
|
+
return null;
|
|
4826
|
+
}
|
|
4827
|
+
}
|
|
4828
|
+
parseTokenStats(filePath) {
|
|
4829
|
+
let totalInputTokens = 0;
|
|
4830
|
+
let totalOutputTokens = 0;
|
|
4831
|
+
let totalCacheReadTokens = 0;
|
|
4832
|
+
let totalCost = 0;
|
|
4833
|
+
let activeModel = null;
|
|
4834
|
+
let prevCumulativeTotal = 0;
|
|
4835
|
+
let prevInput = 0;
|
|
4836
|
+
let prevOutput = 0;
|
|
4837
|
+
let prevReasoning = 0;
|
|
4838
|
+
let prevCachedInput = 0;
|
|
4839
|
+
for (const line of readJsonlFileLines(filePath)) {
|
|
4840
|
+
try {
|
|
4841
|
+
const data = JSON.parse(line);
|
|
4842
|
+
const recordType = String(data["type"] ?? "");
|
|
4843
|
+
const payload = extractPayload(data);
|
|
4844
|
+
if (recordType === "session_meta" || recordType === "turn_context") {
|
|
4845
|
+
const nextModel = extractModelName(payload["model"]);
|
|
4846
|
+
if (nextModel) activeModel = nextModel;
|
|
4847
|
+
continue;
|
|
4848
|
+
}
|
|
4849
|
+
if (recordType === "event_msg" && String(payload["type"] ?? "") === "token_count") {
|
|
4850
|
+
const { totalUsage, lastUsage } = extractTokenUsage(payload);
|
|
4851
|
+
const cumulativeTotal = Number(totalUsage?.["total_tokens"] ?? 0);
|
|
4852
|
+
if (cumulativeTotal <= 0 || cumulativeTotal === prevCumulativeTotal) continue;
|
|
4853
|
+
prevCumulativeTotal = cumulativeTotal;
|
|
4854
|
+
let inputTokens = 0;
|
|
4855
|
+
let outputTokens = 0;
|
|
4856
|
+
let reasoningTokens = 0;
|
|
4857
|
+
let cacheReadTokens = 0;
|
|
4858
|
+
if (lastUsage) {
|
|
4859
|
+
inputTokens = Number(lastUsage["input_tokens"] ?? 0);
|
|
4860
|
+
outputTokens = Number(lastUsage["output_tokens"] ?? 0);
|
|
4861
|
+
reasoningTokens = Number(lastUsage["reasoning_output_tokens"] ?? 0);
|
|
4862
|
+
cacheReadTokens = extractCachedInputTokens(lastUsage);
|
|
4863
|
+
} else if (totalUsage) {
|
|
4864
|
+
inputTokens = Number(totalUsage["input_tokens"] ?? 0) - prevInput;
|
|
4865
|
+
outputTokens = Number(totalUsage["output_tokens"] ?? 0) - prevOutput;
|
|
4866
|
+
reasoningTokens = Number(totalUsage["reasoning_output_tokens"] ?? 0) - prevReasoning;
|
|
4867
|
+
cacheReadTokens = extractCachedInputTokens(totalUsage) - prevCachedInput;
|
|
4868
|
+
prevInput = Number(totalUsage["input_tokens"] ?? 0);
|
|
4869
|
+
prevOutput = Number(totalUsage["output_tokens"] ?? 0);
|
|
4870
|
+
prevReasoning = Number(totalUsage["reasoning_output_tokens"] ?? 0);
|
|
4871
|
+
prevCachedInput = extractCachedInputTokens(totalUsage);
|
|
4872
|
+
}
|
|
4873
|
+
const totalInput = Math.max(0, inputTokens);
|
|
4874
|
+
const totalCacheRead = Math.max(0, cacheReadTokens);
|
|
4875
|
+
totalInputTokens += totalInput;
|
|
4876
|
+
totalOutputTokens += outputTokens + reasoningTokens;
|
|
4877
|
+
totalCacheReadTokens += totalCacheRead;
|
|
4878
|
+
totalCost += estimateTokenCost(activeModel, {
|
|
4879
|
+
input: totalInput,
|
|
4880
|
+
output: outputTokens,
|
|
4881
|
+
reasoning: reasoningTokens || void 0,
|
|
4882
|
+
cache_read: totalCacheRead || void 0
|
|
4883
|
+
}) ?? 0;
|
|
4884
|
+
}
|
|
4885
|
+
} catch {
|
|
4886
|
+
}
|
|
4887
|
+
}
|
|
4888
|
+
return {
|
|
4889
|
+
message_count: 0,
|
|
4890
|
+
total_input_tokens: totalInputTokens,
|
|
4891
|
+
total_output_tokens: totalOutputTokens,
|
|
4892
|
+
total_cache_read_tokens: totalCacheReadTokens || void 0,
|
|
4893
|
+
total_cost: totalCost,
|
|
4894
|
+
cost_source: totalCost > 0 ? "estimated" : void 0
|
|
4895
|
+
};
|
|
4896
|
+
}
|
|
3767
4897
|
getSessionData(sessionId) {
|
|
3768
4898
|
const meta = this.sessionMetaMap.get(sessionId);
|
|
3769
4899
|
if (!meta) throw new Error(`Session not found: ${sessionId}`);
|
|
3770
|
-
if (!
|
|
4900
|
+
if (!existsSync9(meta.sourcePath)) throw new Error(`Session file missing: ${meta.sourcePath}`);
|
|
4901
|
+
this.basePath ??= this.findBasePath();
|
|
3771
4902
|
const transcript = new TranscriptBuilder();
|
|
3772
4903
|
let totalInputTokens = 0;
|
|
3773
4904
|
let totalOutputTokens = 0;
|
|
@@ -3850,18 +4981,119 @@ var CodexAgent = class extends SingleFileSessionSource {
|
|
|
3850
4981
|
total_cost: totalCost,
|
|
3851
4982
|
cost_source: totalCost > 0 ? "estimated" : void 0
|
|
3852
4983
|
});
|
|
4984
|
+
this.applyChildStats(result.stats, meta.id);
|
|
4985
|
+
const childMessages = this.collectChildMessages(meta.id);
|
|
4986
|
+
for (const message of childMessages) {
|
|
4987
|
+
const messageText = message.parts.find((part) => part.type === "text")?.text;
|
|
4988
|
+
const alreadyVisible = result.messages.some(
|
|
4989
|
+
(existing) => message.subagent_id !== void 0 && existing.subagent_id === message.subagent_id || existing.subagent_id === void 0 && message.nickname !== void 0 && messageText !== void 0 && existing.nickname === message.nickname && existing.parts.some((part) => part.type === "text" && part.text === messageText)
|
|
4990
|
+
);
|
|
4991
|
+
if (!alreadyVisible) result.messages.push(message);
|
|
4992
|
+
}
|
|
4993
|
+
result.stats.message_count = result.messages.length;
|
|
3853
4994
|
return {
|
|
3854
4995
|
reference: { agentName: this.name, sessionId: meta.id },
|
|
3855
4996
|
id: meta.id,
|
|
3856
4997
|
title: meta.title,
|
|
3857
4998
|
slug: `codex/${meta.id}`,
|
|
3858
4999
|
directory: meta.directory,
|
|
5000
|
+
parent_reference: meta.parentThreadId == null ? void 0 : { agentName: this.name, sessionId: meta.parentThreadId },
|
|
3859
5001
|
time_created: meta.createdAt,
|
|
3860
5002
|
time_updated: meta.updatedAt,
|
|
3861
5003
|
stats: result.stats,
|
|
3862
5004
|
messages: result.messages
|
|
3863
5005
|
};
|
|
3864
5006
|
}
|
|
5007
|
+
/**
|
|
5008
|
+
* Builds the complete parent→children map in one prefix sweep. A cache miss
|
|
5009
|
+
* afterwards means "no children", so per-session directory rescans (the old
|
|
5010
|
+
* O(N²) finalization hotspot) never happen.
|
|
5011
|
+
*/
|
|
5012
|
+
ensureSubagentIndex() {
|
|
5013
|
+
if (this.subagentIndex) return this.subagentIndex;
|
|
5014
|
+
this.basePath ??= this.findBasePath();
|
|
5015
|
+
const index = { childFilesByParent: /* @__PURE__ */ new Map(), subagentFiles: /* @__PURE__ */ new Set() };
|
|
5016
|
+
for (const file of this.listRolloutFilePaths()) {
|
|
5017
|
+
const threadMeta = this.readThreadMeta(file);
|
|
5018
|
+
if (threadMeta?.threadSource !== "subagent") continue;
|
|
5019
|
+
index.subagentFiles.add(file);
|
|
5020
|
+
if (!threadMeta.parentThreadId) continue;
|
|
5021
|
+
const files = index.childFilesByParent.get(threadMeta.parentThreadId);
|
|
5022
|
+
if (files) files.push(file);
|
|
5023
|
+
else index.childFilesByParent.set(threadMeta.parentThreadId, [file]);
|
|
5024
|
+
}
|
|
5025
|
+
this.subagentIndex = index;
|
|
5026
|
+
return index;
|
|
5027
|
+
}
|
|
5028
|
+
applyChildStats(target, sessionId) {
|
|
5029
|
+
for (const stats of this.collectChildStats(sessionId)) {
|
|
5030
|
+
target.total_input_tokens += stats.total_input_tokens ?? 0;
|
|
5031
|
+
target.total_output_tokens += stats.total_output_tokens ?? 0;
|
|
5032
|
+
target.total_cost += stats.total_cost ?? 0;
|
|
5033
|
+
if (stats.total_cache_read_tokens) {
|
|
5034
|
+
target.total_cache_read_tokens = (target.total_cache_read_tokens ?? 0) + stats.total_cache_read_tokens;
|
|
5035
|
+
}
|
|
5036
|
+
}
|
|
5037
|
+
}
|
|
5038
|
+
collectChildStats(parentSessionId) {
|
|
5039
|
+
const files = this.collectChildFiles(parentSessionId);
|
|
5040
|
+
if (files.length === 0) return [];
|
|
5041
|
+
const cached = this.subagentStatsByParent.get(parentSessionId);
|
|
5042
|
+
if (cached) return cached;
|
|
5043
|
+
const stats = files.map((file) => this.parseTokenStats(file));
|
|
5044
|
+
this.subagentStatsByParent.set(parentSessionId, stats);
|
|
5045
|
+
return stats;
|
|
5046
|
+
}
|
|
5047
|
+
collectChildFiles(parentSessionId) {
|
|
5048
|
+
return this.ensureSubagentIndex().childFilesByParent.get(parentSessionId) ?? [];
|
|
5049
|
+
}
|
|
5050
|
+
collectChildMessages(parentSessionId) {
|
|
5051
|
+
return this.collectChildFiles(parentSessionId).flatMap((file) => {
|
|
5052
|
+
const message = this.parseChildFinalMessage(file);
|
|
5053
|
+
return message ? [message] : [];
|
|
5054
|
+
}).sort((left, right) => left.time_created - right.time_created);
|
|
5055
|
+
}
|
|
5056
|
+
parseChildFinalMessage(filePath) {
|
|
5057
|
+
const sessionId = extractSessionId(filePath);
|
|
5058
|
+
const threadMeta = this.readThreadMeta(filePath);
|
|
5059
|
+
let latestOutput = null;
|
|
5060
|
+
let finalOutput = null;
|
|
5061
|
+
for (const record of readJsonlFile(filePath)) {
|
|
5062
|
+
try {
|
|
5063
|
+
const recordType = String(record["type"] ?? "");
|
|
5064
|
+
if (recordType !== "response_item") continue;
|
|
5065
|
+
const payload = extractPayload(record);
|
|
5066
|
+
if (String(payload["type"] ?? "") !== "message") continue;
|
|
5067
|
+
if (String(payload["role"] ?? "") !== "assistant") continue;
|
|
5068
|
+
const text = cleanInternalText(extractAssistantOutputText(payload));
|
|
5069
|
+
if (!text) continue;
|
|
5070
|
+
const candidate = {
|
|
5071
|
+
id: asString(payload["id"]) ?? `codex-subagent-${sessionId}`,
|
|
5072
|
+
text,
|
|
5073
|
+
timestampMs: parseTimestampMs2(record) || parseTimestampMs2(payload) || statSync6(filePath).mtimeMs,
|
|
5074
|
+
isFinal: String(record["phase"] ?? "") === "final_answer" || String(payload["phase"] ?? "") === "final_answer"
|
|
5075
|
+
};
|
|
5076
|
+
latestOutput = candidate;
|
|
5077
|
+
if (candidate.isFinal) finalOutput = candidate;
|
|
5078
|
+
} catch {
|
|
5079
|
+
}
|
|
5080
|
+
}
|
|
5081
|
+
const selected = finalOutput ?? latestOutput;
|
|
5082
|
+
if (!selected) return null;
|
|
5083
|
+
return {
|
|
5084
|
+
id: selected.id,
|
|
5085
|
+
role: "assistant",
|
|
5086
|
+
agent: "codex",
|
|
5087
|
+
time_created: selected.timestampMs,
|
|
5088
|
+
mode: null,
|
|
5089
|
+
model: null,
|
|
5090
|
+
provider: null,
|
|
5091
|
+
cost: 0,
|
|
5092
|
+
subagent_id: sessionId,
|
|
5093
|
+
nickname: threadMeta?.agentNickname ?? void 0,
|
|
5094
|
+
parts: [{ type: "text", text: selected.text, time_created: selected.timestampMs }]
|
|
5095
|
+
};
|
|
5096
|
+
}
|
|
3865
5097
|
// ---- File listing ----
|
|
3866
5098
|
listRolloutFiles(options) {
|
|
3867
5099
|
if (!this.basePath) return [];
|
|
@@ -3871,6 +5103,54 @@ var CodexAgent = class extends SingleFileSessionSource {
|
|
|
3871
5103
|
{ scanWindow: options }
|
|
3872
5104
|
);
|
|
3873
5105
|
}
|
|
5106
|
+
/** Path-only listing for the subagent index: no per-file stat needed. */
|
|
5107
|
+
listRolloutFilePaths() {
|
|
5108
|
+
if (!this.basePath) return [];
|
|
5109
|
+
const paths = [];
|
|
5110
|
+
const walk = (directory) => {
|
|
5111
|
+
let entries;
|
|
5112
|
+
try {
|
|
5113
|
+
entries = readdirSync6(directory, { withFileTypes: true });
|
|
5114
|
+
} catch {
|
|
5115
|
+
return;
|
|
5116
|
+
}
|
|
5117
|
+
for (const entry of entries) {
|
|
5118
|
+
const filePath = join10(directory, entry.name);
|
|
5119
|
+
if (entry.isDirectory()) walk(filePath);
|
|
5120
|
+
else if (entry.name.startsWith("rollout-") && entry.name.endsWith(".jsonl")) {
|
|
5121
|
+
paths.push(filePath);
|
|
5122
|
+
}
|
|
5123
|
+
}
|
|
5124
|
+
};
|
|
5125
|
+
walk(this.basePath);
|
|
5126
|
+
return paths;
|
|
5127
|
+
}
|
|
5128
|
+
listScanSources(options) {
|
|
5129
|
+
const windowed = this.listRolloutFiles(options).sort(compareSourceActivityDesc);
|
|
5130
|
+
if (options?.from == null && options?.to == null) return windowed;
|
|
5131
|
+
const { childFilesByParent, subagentFiles } = this.ensureSubagentIndex();
|
|
5132
|
+
const rootFiles = windowed.filter((source) => !subagentFiles.has(source.file));
|
|
5133
|
+
const rootIds = new Set(rootFiles.map(({ file }) => extractSessionId(file)));
|
|
5134
|
+
if (rootIds.size === 0) return rootFiles;
|
|
5135
|
+
const selected = new Map(rootFiles.map((source) => [source.file, source]));
|
|
5136
|
+
const pending2 = [...rootIds];
|
|
5137
|
+
const seenParents = /* @__PURE__ */ new Set();
|
|
5138
|
+
while (pending2.length > 0) {
|
|
5139
|
+
const parentId = pending2.pop();
|
|
5140
|
+
if (seenParents.has(parentId)) continue;
|
|
5141
|
+
seenParents.add(parentId);
|
|
5142
|
+
for (const file of childFilesByParent.get(parentId) ?? []) {
|
|
5143
|
+
if (selected.has(file)) continue;
|
|
5144
|
+
try {
|
|
5145
|
+
selected.set(file, this.sessionSourceFile(file));
|
|
5146
|
+
} catch {
|
|
5147
|
+
continue;
|
|
5148
|
+
}
|
|
5149
|
+
pending2.push(extractSessionId(file));
|
|
5150
|
+
}
|
|
5151
|
+
}
|
|
5152
|
+
return [...selected.values()].sort(compareSourceActivityDesc);
|
|
5153
|
+
}
|
|
3874
5154
|
createFileSessionMeta(head, source) {
|
|
3875
5155
|
const indexPath = this.getSessionIndexPath();
|
|
3876
5156
|
const indexMtime = this.sessionIndexMtime ?? null;
|
|
@@ -3883,7 +5163,8 @@ var CodexAgent = class extends SingleFileSessionSource {
|
|
|
3883
5163
|
indexMtimeMs: indexMtime,
|
|
3884
5164
|
headIndexVersion: HEAD_INDEX_VERSION2,
|
|
3885
5165
|
parserVersion: PARSER_VERSION,
|
|
3886
|
-
model: null
|
|
5166
|
+
model: null,
|
|
5167
|
+
parentThreadId: head.parent_reference?.sessionId ?? null
|
|
3887
5168
|
}
|
|
3888
5169
|
});
|
|
3889
5170
|
}
|
|
@@ -3899,7 +5180,7 @@ var CodexAgent = class extends SingleFileSessionSource {
|
|
|
3899
5180
|
]);
|
|
3900
5181
|
}
|
|
3901
5182
|
getSessionIndexPath() {
|
|
3902
|
-
this.sessionIndexPath ??=
|
|
5183
|
+
this.sessionIndexPath ??= join10(resolveCodexDataRoot(), "session_index.jsonl");
|
|
3903
5184
|
return this.sessionIndexPath;
|
|
3904
5185
|
}
|
|
3905
5186
|
// ---- Session index ----
|
|
@@ -3913,7 +5194,7 @@ var CodexAgent = class extends SingleFileSessionSource {
|
|
|
3913
5194
|
return;
|
|
3914
5195
|
}
|
|
3915
5196
|
try {
|
|
3916
|
-
const content =
|
|
5197
|
+
const content = readFileSync5(indexPath, "utf-8");
|
|
3917
5198
|
const cache = /* @__PURE__ */ new Map();
|
|
3918
5199
|
for (const record of parseJsonlLines(content)) {
|
|
3919
5200
|
const sid = String(record["id"] ?? "").trim();
|
|
@@ -3945,7 +5226,9 @@ var CodexAgent = class extends SingleFileSessionSource {
|
|
|
3945
5226
|
}
|
|
3946
5227
|
parseFileSessionHead(filePath, options) {
|
|
3947
5228
|
this.loadSessionIndex();
|
|
3948
|
-
|
|
5229
|
+
const head = this.parseSessionHead(filePath, options);
|
|
5230
|
+
if (head) this.applyChildStats(head.stats, head.id);
|
|
5231
|
+
return head;
|
|
3949
5232
|
}
|
|
3950
5233
|
parseSessionHead(filePath, options) {
|
|
3951
5234
|
return getParsedSession(this.parseSessionHeadResult(filePath, options));
|
|
@@ -3956,6 +5239,7 @@ var CodexAgent = class extends SingleFileSessionSource {
|
|
|
3956
5239
|
}
|
|
3957
5240
|
const sessionId = extractSessionId(filePath);
|
|
3958
5241
|
let firstPayload = {};
|
|
5242
|
+
let parentThreadId = null;
|
|
3959
5243
|
let createdAt = 0;
|
|
3960
5244
|
let lineCount = 0;
|
|
3961
5245
|
let messageTitle = null;
|
|
@@ -3985,7 +5269,9 @@ var CodexAgent = class extends SingleFileSessionSource {
|
|
|
3985
5269
|
return skippedSession("malformed first record");
|
|
3986
5270
|
}
|
|
3987
5271
|
firstPayload = extractPayload(firstRecord);
|
|
3988
|
-
|
|
5272
|
+
const threadMeta = extractThreadMeta(firstRecord);
|
|
5273
|
+
parentThreadId = threadMeta?.threadSource === "subagent" ? threadMeta.parentThreadId : null;
|
|
5274
|
+
createdAt = parseTimestampMs2(firstRecord) || parseTimestampMs2(firstPayload) || statSync6(filePath).mtimeMs;
|
|
3989
5275
|
updatedAt = createdAt;
|
|
3990
5276
|
}
|
|
3991
5277
|
try {
|
|
@@ -4079,6 +5365,7 @@ var CodexAgent = class extends SingleFileSessionSource {
|
|
|
4079
5365
|
slug: `codex/${sessionId}`,
|
|
4080
5366
|
title,
|
|
4081
5367
|
directory,
|
|
5368
|
+
parent_reference: parentThreadId == null ? void 0 : { agentName: this.name, sessionId: parentThreadId },
|
|
4082
5369
|
time_created: createdAt,
|
|
4083
5370
|
time_updated: updatedAt,
|
|
4084
5371
|
stats: {
|
|
@@ -4092,9 +5379,6 @@ var CodexAgent = class extends SingleFileSessionSource {
|
|
|
4092
5379
|
model_usage: Object.keys(modelUsageMap).length > 0 ? modelUsageMap : void 0
|
|
4093
5380
|
});
|
|
4094
5381
|
}
|
|
4095
|
-
parseFastSessionHead(filePath) {
|
|
4096
|
-
return getParsedSession(this.parseFastSessionHeadResult(filePath));
|
|
4097
|
-
}
|
|
4098
5382
|
parseFastSessionHeadResult(filePath) {
|
|
4099
5383
|
const prefix = this.readFilePrefix(filePath);
|
|
4100
5384
|
const lines = prefix.split("\n").filter((l) => l.trim());
|
|
@@ -4106,8 +5390,10 @@ var CodexAgent = class extends SingleFileSessionSource {
|
|
|
4106
5390
|
} catch {
|
|
4107
5391
|
return skippedSession("malformed first record");
|
|
4108
5392
|
}
|
|
5393
|
+
const threadMeta = extractThreadMeta(firstRecord);
|
|
5394
|
+
const parentThreadId = threadMeta?.threadSource === "subagent" ? threadMeta.parentThreadId : null;
|
|
4109
5395
|
const payload = extractPayload(firstRecord);
|
|
4110
|
-
const stat =
|
|
5396
|
+
const stat = statSync6(filePath);
|
|
4111
5397
|
const createdAt = parseTimestampMs2(firstRecord) || parseTimestampMs2(payload) || stat.mtimeMs;
|
|
4112
5398
|
const indexTitle = this.getTitleForSession(sessionId);
|
|
4113
5399
|
const messageTitle = this.extractTitleFromLines(lines);
|
|
@@ -4119,6 +5405,7 @@ var CodexAgent = class extends SingleFileSessionSource {
|
|
|
4119
5405
|
slug: `codex/${sessionId}`,
|
|
4120
5406
|
title,
|
|
4121
5407
|
directory,
|
|
5408
|
+
parent_reference: parentThreadId == null ? void 0 : { agentName: this.name, sessionId: parentThreadId },
|
|
4122
5409
|
time_created: createdAt,
|
|
4123
5410
|
time_updated: stat.mtimeMs,
|
|
4124
5411
|
stats: {
|
|
@@ -4214,19 +5501,8 @@ var CodexAgent = class extends SingleFileSessionSource {
|
|
|
4214
5501
|
}
|
|
4215
5502
|
// ---- Assistant message ----
|
|
4216
5503
|
convertAssistantMessage(payload, transcript, timestampMs, pendingPlan, activeModel) {
|
|
4217
|
-
const
|
|
4218
|
-
if (!
|
|
4219
|
-
const textParts = [];
|
|
4220
|
-
for (const item of content) {
|
|
4221
|
-
const ci = asRecord(item);
|
|
4222
|
-
if (!ci) continue;
|
|
4223
|
-
if (String(ci["type"] ?? "") === "output_text") {
|
|
4224
|
-
const text = String(ci["text"] ?? "");
|
|
4225
|
-
if (text.trim()) textParts.push(text);
|
|
4226
|
-
}
|
|
4227
|
-
}
|
|
4228
|
-
if (textParts.length === 0) return pendingPlan;
|
|
4229
|
-
const fullText = textParts.join("\n");
|
|
5504
|
+
const fullText = extractAssistantOutputText(payload);
|
|
5505
|
+
if (!fullText) return pendingPlan;
|
|
4230
5506
|
const planMatch = fullText.match(PROPOSED_PLAN_PATTERN);
|
|
4231
5507
|
if (planMatch) {
|
|
4232
5508
|
const planText2 = planMatch[1].trim();
|
|
@@ -4341,8 +5617,8 @@ var CodexAgent = class extends SingleFileSessionSource {
|
|
|
4341
5617
|
const name = String(payload["name"] ?? "").trim();
|
|
4342
5618
|
if (!name) return;
|
|
4343
5619
|
const toolIdentity = resolveToolIdentity(name, payload["namespace"]);
|
|
4344
|
-
const arguments_ =
|
|
4345
|
-
const
|
|
5620
|
+
const arguments_ = normalizeToolArguments3(payload["arguments"]);
|
|
5621
|
+
const toolPart2 = {
|
|
4346
5622
|
type: "tool",
|
|
4347
5623
|
tool: toolIdentity.tool,
|
|
4348
5624
|
callID: callId,
|
|
@@ -4356,7 +5632,7 @@ var CodexAgent = class extends SingleFileSessionSource {
|
|
|
4356
5632
|
time_created: timestampMs
|
|
4357
5633
|
};
|
|
4358
5634
|
transcript.appendToolCall(
|
|
4359
|
-
|
|
5635
|
+
toolPart2,
|
|
4360
5636
|
{ id: "", timestampMs, agent: "codex", model: activeModel },
|
|
4361
5637
|
{ markModeAsTool: true }
|
|
4362
5638
|
);
|
|
@@ -4388,7 +5664,7 @@ var CodexAgent = class extends SingleFileSessionSource {
|
|
|
4388
5664
|
const toolIdentity = resolveToolIdentity(name, payload["namespace"]);
|
|
4389
5665
|
const rawInput = payload["input"];
|
|
4390
5666
|
const normalizedInput = normalizeCustomToolArguments(name, rawInput);
|
|
4391
|
-
const
|
|
5667
|
+
const toolPart2 = {
|
|
4392
5668
|
type: "tool",
|
|
4393
5669
|
tool: toolIdentity.tool,
|
|
4394
5670
|
callID: callId,
|
|
@@ -4402,7 +5678,7 @@ var CodexAgent = class extends SingleFileSessionSource {
|
|
|
4402
5678
|
time_created: timestampMs
|
|
4403
5679
|
};
|
|
4404
5680
|
transcript.appendToolCall(
|
|
4405
|
-
|
|
5681
|
+
toolPart2,
|
|
4406
5682
|
{ id: "", timestampMs, agent: "codex", model: activeModel },
|
|
4407
5683
|
{ markModeAsTool: true }
|
|
4408
5684
|
);
|
|
@@ -4419,7 +5695,7 @@ var CodexAgent = class extends SingleFileSessionSource {
|
|
|
4419
5695
|
const { name, namespace } = splitExecToolName(call.name);
|
|
4420
5696
|
const toolIdentity = resolveToolIdentity(name, namespace);
|
|
4421
5697
|
const arguments_ = name === "apply_patch" ? parseApplyPatchInput(getExecPatchText(call.args)) : call.args;
|
|
4422
|
-
const
|
|
5698
|
+
const toolPart2 = {
|
|
4423
5699
|
type: "tool",
|
|
4424
5700
|
tool: toolIdentity.tool,
|
|
4425
5701
|
callID: callId,
|
|
@@ -4433,7 +5709,7 @@ var CodexAgent = class extends SingleFileSessionSource {
|
|
|
4433
5709
|
time_created: timestampMs
|
|
4434
5710
|
};
|
|
4435
5711
|
transcript.appendToolCall(
|
|
4436
|
-
|
|
5712
|
+
toolPart2,
|
|
4437
5713
|
{ id: "", timestampMs, agent: "codex", model: activeModel },
|
|
4438
5714
|
{ markModeAsTool: true }
|
|
4439
5715
|
);
|
|
@@ -4518,15 +5794,15 @@ function resolveCursorDataRoot() {
|
|
|
4518
5794
|
if (override) return override;
|
|
4519
5795
|
const currentPlatform = platform2();
|
|
4520
5796
|
if (currentPlatform === "darwin") {
|
|
4521
|
-
return firstExisting(
|
|
5797
|
+
return firstExisting(join11(homedir3(), "Library", "Application Support", "Cursor", "User"));
|
|
4522
5798
|
}
|
|
4523
5799
|
if (currentPlatform === "linux") {
|
|
4524
|
-
const configRoot = readEnvPath("XDG_CONFIG_HOME") ??
|
|
4525
|
-
return firstExisting(
|
|
5800
|
+
const configRoot = readEnvPath("XDG_CONFIG_HOME") ?? join11(homedir3(), ".config");
|
|
5801
|
+
return firstExisting(join11(configRoot, "Cursor", "User"));
|
|
4526
5802
|
}
|
|
4527
5803
|
if (currentPlatform === "win32") {
|
|
4528
|
-
const appData = readEnvPath("APPDATA") ??
|
|
4529
|
-
return firstExisting(
|
|
5804
|
+
const appData = readEnvPath("APPDATA") ?? join11(homedir3(), "AppData", "Roaming");
|
|
5805
|
+
return firstExisting(join11(appData, "Cursor", "User"));
|
|
4530
5806
|
}
|
|
4531
5807
|
return null;
|
|
4532
5808
|
}
|
|
@@ -4655,7 +5931,7 @@ var CURSOR_TOOL_TITLE_MAP = {
|
|
|
4655
5931
|
ripgrep_raw_search: "grep",
|
|
4656
5932
|
glob_file_search: "glob"
|
|
4657
5933
|
};
|
|
4658
|
-
function
|
|
5934
|
+
function mapToolTitle3(toolName) {
|
|
4659
5935
|
return CURSOR_TOOL_TITLE_MAP[toolName] ?? toolName;
|
|
4660
5936
|
}
|
|
4661
5937
|
function normalizeToolOutputParts2(output, timestampMs) {
|
|
@@ -4716,9 +5992,9 @@ function buildToolPart(action, timestampMs) {
|
|
|
4716
5992
|
const toolName = action.tool ?? "unknown";
|
|
4717
5993
|
return {
|
|
4718
5994
|
type: "tool",
|
|
4719
|
-
tool:
|
|
5995
|
+
tool: mapToolTitle3(toolName),
|
|
4720
5996
|
callID: action.type ? `${action.type}:${String(action.input?.id ?? "")}` : "",
|
|
4721
|
-
title: `Tool: ${
|
|
5997
|
+
title: `Tool: ${mapToolTitle3(toolName)}`,
|
|
4722
5998
|
state: buildToolState(action),
|
|
4723
5999
|
time_created: timestampMs
|
|
4724
6000
|
};
|
|
@@ -4764,7 +6040,7 @@ var CursorAgent = class extends DatabaseSessionSource {
|
|
|
4764
6040
|
if (!isSqliteAvailable()) return null;
|
|
4765
6041
|
const dataPath = resolveCursorDataRoot();
|
|
4766
6042
|
if (!dataPath) return null;
|
|
4767
|
-
return
|
|
6043
|
+
return join11(dataPath, "globalStorage", "state.vscdb");
|
|
4768
6044
|
}
|
|
4769
6045
|
getSessionWatchPlan() {
|
|
4770
6046
|
const dataPath = resolveCursorDataRoot();
|
|
@@ -4773,9 +6049,9 @@ var CursorAgent = class extends DatabaseSessionSource {
|
|
|
4773
6049
|
targets: dataPath ? [
|
|
4774
6050
|
{
|
|
4775
6051
|
root: dataPath,
|
|
4776
|
-
path:
|
|
6052
|
+
path: join11(dataPath, "globalStorage", "state.vscdb")
|
|
4777
6053
|
},
|
|
4778
|
-
{ root: dataPath, path:
|
|
6054
|
+
{ root: dataPath, path: join11(dataPath, "workspaceStorage") }
|
|
4779
6055
|
] : []
|
|
4780
6056
|
};
|
|
4781
6057
|
}
|
|
@@ -4787,34 +6063,34 @@ var CursorAgent = class extends DatabaseSessionSource {
|
|
|
4787
6063
|
const map = /* @__PURE__ */ new Map();
|
|
4788
6064
|
const dataPath = resolveCursorDataRoot();
|
|
4789
6065
|
if (!dataPath) return map;
|
|
4790
|
-
const wsStoragePath =
|
|
4791
|
-
if (!
|
|
6066
|
+
const wsStoragePath = join11(dataPath, "workspaceStorage");
|
|
6067
|
+
if (!existsSync10(wsStoragePath)) return map;
|
|
4792
6068
|
let entryNames;
|
|
4793
6069
|
try {
|
|
4794
|
-
entryNames =
|
|
6070
|
+
entryNames = readdirSync7(wsStoragePath);
|
|
4795
6071
|
} catch {
|
|
4796
6072
|
return map;
|
|
4797
6073
|
}
|
|
4798
6074
|
for (const name of entryNames) {
|
|
4799
|
-
const wsDir =
|
|
6075
|
+
const wsDir = join11(wsStoragePath, name);
|
|
4800
6076
|
try {
|
|
4801
|
-
if (!
|
|
6077
|
+
if (!statSync7(wsDir).isDirectory()) continue;
|
|
4802
6078
|
} catch {
|
|
4803
6079
|
continue;
|
|
4804
6080
|
}
|
|
4805
|
-
const wsJsonPath =
|
|
4806
|
-
if (!
|
|
6081
|
+
const wsJsonPath = join11(wsDir, "workspace.json");
|
|
6082
|
+
if (!existsSync10(wsJsonPath)) continue;
|
|
4807
6083
|
let workspacePath;
|
|
4808
6084
|
try {
|
|
4809
|
-
const data = asRecord(JSON.parse(
|
|
6085
|
+
const data = asRecord(JSON.parse(readFileSync6(wsJsonPath, "utf-8")));
|
|
4810
6086
|
const uri = narrowString("workspaceJson.folder", data?.folder) ?? narrowString("workspaceJson.workspace", data?.workspace) ?? "";
|
|
4811
6087
|
if (!uri) continue;
|
|
4812
6088
|
workspacePath = normalize(decodeURIComponent(uri.replace(/^file:\/\//, "")));
|
|
4813
6089
|
} catch {
|
|
4814
6090
|
continue;
|
|
4815
6091
|
}
|
|
4816
|
-
const wsDbPath =
|
|
4817
|
-
if (!
|
|
6092
|
+
const wsDbPath = join11(wsDir, "state.vscdb");
|
|
6093
|
+
if (!existsSync10(wsDbPath)) continue;
|
|
4818
6094
|
const wsDb = openDbReadOnly(wsDbPath);
|
|
4819
6095
|
if (!wsDb) continue;
|
|
4820
6096
|
try {
|
|
@@ -4837,7 +6113,7 @@ var CursorAgent = class extends DatabaseSessionSource {
|
|
|
4837
6113
|
}
|
|
4838
6114
|
isAvailable() {
|
|
4839
6115
|
this.dbPath = this.findDbPath();
|
|
4840
|
-
return this.dbPath !== null &&
|
|
6116
|
+
return this.dbPath !== null && existsSync10(this.dbPath);
|
|
4841
6117
|
}
|
|
4842
6118
|
scan(options) {
|
|
4843
6119
|
if (!this.dbPath) return [];
|
|
@@ -5192,9 +6468,9 @@ var CursorAgent = class extends DatabaseSessionSource {
|
|
|
5192
6468
|
parts.push({ type: "text", text, time_created: timestampMs });
|
|
5193
6469
|
}
|
|
5194
6470
|
if (bubble.toolFormerData) {
|
|
5195
|
-
const
|
|
5196
|
-
if (
|
|
5197
|
-
parts.push(
|
|
6471
|
+
const toolPart2 = this.convertToolFormerData(bubble.toolFormerData, timestampMs);
|
|
6472
|
+
if (toolPart2) {
|
|
6473
|
+
parts.push(toolPart2);
|
|
5198
6474
|
}
|
|
5199
6475
|
}
|
|
5200
6476
|
if (parts.length === 0) continue;
|
|
@@ -5225,7 +6501,7 @@ var CursorAgent = class extends DatabaseSessionSource {
|
|
|
5225
6501
|
convertToolFormerData(toolData, timestampMs) {
|
|
5226
6502
|
if (!toolData || !toolData.name) return null;
|
|
5227
6503
|
const toolName = toolData.name;
|
|
5228
|
-
const normalizedName = toolName === "create_plan" ? "plan" :
|
|
6504
|
+
const normalizedName = toolName === "create_plan" ? "plan" : mapToolTitle3(toolName);
|
|
5229
6505
|
const state = {
|
|
5230
6506
|
status: toolData.status === "completed" ? "completed" : "running"
|
|
5231
6507
|
};
|
|
@@ -5353,7 +6629,7 @@ function narrowTimestampMs(field, value) {
|
|
|
5353
6629
|
return shaped === void 0 ? 0 : parseTimestampMs3(shaped);
|
|
5354
6630
|
}
|
|
5355
6631
|
function extractSessionIdFromFilename(filePath) {
|
|
5356
|
-
const stem =
|
|
6632
|
+
const stem = basename8(filePath, ".jsonl");
|
|
5357
6633
|
const underscore = stem.indexOf("_");
|
|
5358
6634
|
return underscore >= 0 ? stem.slice(underscore + 1) || stem : stem;
|
|
5359
6635
|
}
|
|
@@ -5409,14 +6685,14 @@ var PiAgent = class extends SingleFileSessionSource {
|
|
|
5409
6685
|
displayName = "Pi";
|
|
5410
6686
|
basePath = null;
|
|
5411
6687
|
findBasePath() {
|
|
5412
|
-
return firstExisting(
|
|
6688
|
+
return firstExisting(join12(resolvePiDataRoot(), "agent", "sessions"), "data/pi");
|
|
5413
6689
|
}
|
|
5414
6690
|
getSessionWatchPlan() {
|
|
5415
6691
|
const dataRoot = resolvePiDataRoot();
|
|
5416
6692
|
return {
|
|
5417
6693
|
status: "supported",
|
|
5418
6694
|
targets: [
|
|
5419
|
-
{ root: dataRoot, path:
|
|
6695
|
+
{ root: dataRoot, path: join12(dataRoot, "agent", "sessions") },
|
|
5420
6696
|
{ root: "data/pi", path: "data/pi" }
|
|
5421
6697
|
]
|
|
5422
6698
|
};
|
|
@@ -5437,7 +6713,7 @@ var PiAgent = class extends SingleFileSessionSource {
|
|
|
5437
6713
|
getSessionData(sessionId) {
|
|
5438
6714
|
const meta = this.sessionMetaMap.get(sessionId);
|
|
5439
6715
|
if (!meta) throw new Error(`Session not found: ${sessionId}`);
|
|
5440
|
-
if (!
|
|
6716
|
+
if (!existsSync11(meta.sourcePath)) throw new Error(`Session file missing: ${meta.sourcePath}`);
|
|
5441
6717
|
const parsed = this.parsePiFile(meta.sourcePath);
|
|
5442
6718
|
const state = this.convertEntries(parsed.pathEntries);
|
|
5443
6719
|
return {
|
|
@@ -5530,7 +6806,7 @@ var PiAgent = class extends SingleFileSessionSource {
|
|
|
5530
6806
|
const sessionId = String(header["id"] ?? extractSessionIdFromFilename(filePath)).trim();
|
|
5531
6807
|
if (!sessionId) throw new Error("missing session id");
|
|
5532
6808
|
const stat = this.sessionSourceFile(filePath).stat;
|
|
5533
|
-
const directory = String(header["cwd"] ?? "").trim() ||
|
|
6809
|
+
const directory = String(header["cwd"] ?? "").trim() || basename8(filePath, ".jsonl");
|
|
5534
6810
|
const createdAt = narrowTimestampMs("session.timestamp", header["timestamp"]) || stat.mtimeMs;
|
|
5535
6811
|
const updatedAt = pathEntries.reduce(
|
|
5536
6812
|
(max, entry) => Math.max(max, getEntryTimestamp(entry)),
|
|
@@ -5673,7 +6949,7 @@ var PiAgent = class extends SingleFileSessionSource {
|
|
|
5673
6949
|
if (type === "toolCall") {
|
|
5674
6950
|
const callId = String(item["id"] ?? "").trim();
|
|
5675
6951
|
const toolName = String(item["name"] ?? "").trim() || "tool";
|
|
5676
|
-
const
|
|
6952
|
+
const toolPart2 = {
|
|
5677
6953
|
type: "tool",
|
|
5678
6954
|
tool: toolName,
|
|
5679
6955
|
title: `Tool: ${toolName}`,
|
|
@@ -5684,7 +6960,7 @@ var PiAgent = class extends SingleFileSessionSource {
|
|
|
5684
6960
|
input: item["arguments"] ?? {}
|
|
5685
6961
|
}
|
|
5686
6962
|
};
|
|
5687
|
-
parts.push(
|
|
6963
|
+
parts.push(toolPart2);
|
|
5688
6964
|
}
|
|
5689
6965
|
}
|
|
5690
6966
|
return parts;
|
|
@@ -5780,19 +7056,19 @@ var PiAgent = class extends SingleFileSessionSource {
|
|
|
5780
7056
|
function resolveZCodeDataRoot() {
|
|
5781
7057
|
const currentPlatform = platform3();
|
|
5782
7058
|
if (currentPlatform !== "darwin" && currentPlatform !== "win32") return null;
|
|
5783
|
-
return
|
|
7059
|
+
return join13(homedir4(), ".zcode");
|
|
5784
7060
|
}
|
|
5785
7061
|
function findZCodeDbPath() {
|
|
5786
7062
|
if (!isSqliteAvailable()) return null;
|
|
5787
7063
|
const dataRoot = resolveZCodeDataRoot();
|
|
5788
7064
|
if (!dataRoot) return null;
|
|
5789
|
-
return firstExisting(
|
|
7065
|
+
return firstExisting(join13(dataRoot, "cli", "db", "db.sqlite"));
|
|
5790
7066
|
}
|
|
5791
7067
|
function getZCodeSessionWatchPlan() {
|
|
5792
7068
|
const dataRoot = resolveZCodeDataRoot();
|
|
5793
7069
|
return {
|
|
5794
7070
|
status: "supported",
|
|
5795
|
-
targets: dataRoot ? [{ root: dataRoot, path:
|
|
7071
|
+
targets: dataRoot ? [{ root: dataRoot, path: join13(dataRoot, "cli", "db", "db.sqlite") }] : []
|
|
5796
7072
|
};
|
|
5797
7073
|
}
|
|
5798
7074
|
var ZCodeAgent = class extends OpenCodeSqliteAgent {
|
|
@@ -5834,6 +7110,13 @@ registerAgent({
|
|
|
5834
7110
|
toolStrategy: "custom",
|
|
5835
7111
|
create: () => new KimiAgent()
|
|
5836
7112
|
});
|
|
7113
|
+
registerAgent({
|
|
7114
|
+
icon: "/icon/agent/kimi.svg",
|
|
7115
|
+
resolveDataRoot: resolveKimiCodeDataRoot,
|
|
7116
|
+
resumeCommandPrefix: "kimi -r",
|
|
7117
|
+
toolStrategy: "custom",
|
|
7118
|
+
create: () => new KimiCodeAgent()
|
|
7119
|
+
});
|
|
5837
7120
|
registerAgent({
|
|
5838
7121
|
icon: "/icon/agent/codex.svg",
|
|
5839
7122
|
resolveDataRoot: resolveCodexDataRoot,
|
|
@@ -5863,11 +7146,11 @@ function fallbackDisplayName(input) {
|
|
|
5863
7146
|
}
|
|
5864
7147
|
var realFs = {
|
|
5865
7148
|
exists(path2) {
|
|
5866
|
-
return
|
|
7149
|
+
return existsSync12(path2);
|
|
5867
7150
|
},
|
|
5868
7151
|
readText(path2) {
|
|
5869
7152
|
try {
|
|
5870
|
-
return
|
|
7153
|
+
return readFileSync7(path2, "utf8");
|
|
5871
7154
|
} catch {
|
|
5872
7155
|
return null;
|
|
5873
7156
|
}
|
|
@@ -6038,6 +7321,7 @@ function parseManifestName(file, text) {
|
|
|
6038
7321
|
function buildProjectGroups(sessions) {
|
|
6039
7322
|
const groups = /* @__PURE__ */ new Map();
|
|
6040
7323
|
for (const session of sessions) {
|
|
7324
|
+
if (isChildSession(session)) continue;
|
|
6041
7325
|
const identity = session.project_identity;
|
|
6042
7326
|
if (!identity) continue;
|
|
6043
7327
|
const activity = session.time_updated ?? session.time_created;
|
|
@@ -6090,7 +7374,7 @@ function isPathScopeMatch(queryPath, sessionPath) {
|
|
|
6090
7374
|
return session === queryPath || session.startsWith(queryPath + "/") || queryPath.startsWith(session + "/");
|
|
6091
7375
|
}
|
|
6092
7376
|
function normalizeScopePath(path2) {
|
|
6093
|
-
return
|
|
7377
|
+
return resolve2(path2).replaceAll(sep, "/");
|
|
6094
7378
|
}
|
|
6095
7379
|
var TAG_ORDER = [
|
|
6096
7380
|
"bugfix",
|
|
@@ -6365,16 +7649,16 @@ function setSchemaEnsuredPath(path2) {
|
|
|
6365
7649
|
schemaEnsuredPath = path2;
|
|
6366
7650
|
}
|
|
6367
7651
|
function getCacheDir2() {
|
|
6368
|
-
return
|
|
7652
|
+
return join14(homedir6(), ".cache", "codesesh");
|
|
6369
7653
|
}
|
|
6370
7654
|
function getCachePath2() {
|
|
6371
|
-
return
|
|
7655
|
+
return join14(getCacheDir2(), CACHE_FILENAME);
|
|
6372
7656
|
}
|
|
6373
7657
|
function getLegacyCachePath() {
|
|
6374
|
-
return
|
|
7658
|
+
return join14(getCacheDir2(), LEGACY_CACHE_FILENAME);
|
|
6375
7659
|
}
|
|
6376
7660
|
function hasCacheStorage() {
|
|
6377
|
-
return
|
|
7661
|
+
return existsSync13(getCachePath2());
|
|
6378
7662
|
}
|
|
6379
7663
|
function likePattern(value) {
|
|
6380
7664
|
return `%${value.trim().toLowerCase().replace(/[\\%_]/g, "\\$&")}%`;
|
|
@@ -6415,6 +7699,8 @@ function prepareUpsertSession(db) {
|
|
|
6415
7699
|
title,
|
|
6416
7700
|
source_path,
|
|
6417
7701
|
directory,
|
|
7702
|
+
parent_agent_name,
|
|
7703
|
+
parent_session_id,
|
|
6418
7704
|
project_identity_kind,
|
|
6419
7705
|
project_identity_key,
|
|
6420
7706
|
project_display_name,
|
|
@@ -6433,13 +7719,15 @@ function prepareUpsertSession(db) {
|
|
|
6433
7719
|
smart_tags_json,
|
|
6434
7720
|
smart_tags_source_updated_at,
|
|
6435
7721
|
meta_json
|
|
6436
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
7722
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
6437
7723
|
ON CONFLICT(agent_name, session_id) DO UPDATE SET
|
|
6438
7724
|
sort_index = excluded.sort_index,
|
|
6439
7725
|
slug = excluded.slug,
|
|
6440
7726
|
title = excluded.title,
|
|
6441
7727
|
source_path = excluded.source_path,
|
|
6442
7728
|
directory = excluded.directory,
|
|
7729
|
+
parent_agent_name = excluded.parent_agent_name,
|
|
7730
|
+
parent_session_id = excluded.parent_session_id,
|
|
6443
7731
|
project_identity_kind = excluded.project_identity_kind,
|
|
6444
7732
|
project_identity_key = excluded.project_identity_key,
|
|
6445
7733
|
project_display_name = excluded.project_display_name,
|
|
@@ -6470,6 +7758,8 @@ function prepareUpsertIndexedSession(db) {
|
|
|
6470
7758
|
title,
|
|
6471
7759
|
source_path,
|
|
6472
7760
|
directory,
|
|
7761
|
+
parent_agent_name,
|
|
7762
|
+
parent_session_id,
|
|
6473
7763
|
project_identity_kind,
|
|
6474
7764
|
project_identity_key,
|
|
6475
7765
|
project_display_name,
|
|
@@ -6488,11 +7778,13 @@ function prepareUpsertIndexedSession(db) {
|
|
|
6488
7778
|
smart_tags_json,
|
|
6489
7779
|
smart_tags_source_updated_at,
|
|
6490
7780
|
meta_json
|
|
6491
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
7781
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
6492
7782
|
ON CONFLICT(agent_name, session_id) DO UPDATE SET
|
|
6493
7783
|
slug = excluded.slug,
|
|
6494
7784
|
title = excluded.title,
|
|
6495
7785
|
directory = excluded.directory,
|
|
7786
|
+
parent_agent_name = excluded.parent_agent_name,
|
|
7787
|
+
parent_session_id = excluded.parent_session_id,
|
|
6496
7788
|
project_identity_kind = excluded.project_identity_kind,
|
|
6497
7789
|
project_identity_key = excluded.project_identity_key,
|
|
6498
7790
|
project_display_name = excluded.project_display_name,
|
|
@@ -6523,6 +7815,8 @@ function upsertSessionRow(statement, agentName, session, metaJson, sortIndex, so
|
|
|
6523
7815
|
session.title,
|
|
6524
7816
|
sourcePath,
|
|
6525
7817
|
session.directory,
|
|
7818
|
+
session.parent_reference?.agentName ?? null,
|
|
7819
|
+
session.parent_reference?.sessionId ?? null,
|
|
6526
7820
|
identity.kind,
|
|
6527
7821
|
identity.key,
|
|
6528
7822
|
identity.displayName,
|
|
@@ -6600,6 +7894,12 @@ function sessionFromRow(row) {
|
|
|
6600
7894
|
displayName: String(row.project_display_name ?? "")
|
|
6601
7895
|
};
|
|
6602
7896
|
}
|
|
7897
|
+
if (row.parent_agent_name && row.parent_session_id) {
|
|
7898
|
+
session.parent_reference = {
|
|
7899
|
+
agentName: String(row.parent_agent_name),
|
|
7900
|
+
sessionId: String(row.parent_session_id)
|
|
7901
|
+
};
|
|
7902
|
+
}
|
|
6603
7903
|
if (row.time_updated != null) {
|
|
6604
7904
|
session.time_updated = Number(row.time_updated);
|
|
6605
7905
|
}
|
|
@@ -6817,7 +8117,7 @@ function buildSessionContentFromMessages(title, messages) {
|
|
|
6817
8117
|
}
|
|
6818
8118
|
return chunks.join("\n");
|
|
6819
8119
|
}
|
|
6820
|
-
var CACHE_SCHEMA_VERSION =
|
|
8120
|
+
var CACHE_SCHEMA_VERSION = 18;
|
|
6821
8121
|
function withCacheDb(fn) {
|
|
6822
8122
|
const cachePath = getCachePath2();
|
|
6823
8123
|
const db = openDb(cachePath);
|
|
@@ -6917,6 +8217,8 @@ function createSessionTables(db) {
|
|
|
6917
8217
|
title TEXT NOT NULL,
|
|
6918
8218
|
source_path TEXT,
|
|
6919
8219
|
directory TEXT NOT NULL,
|
|
8220
|
+
parent_agent_name TEXT,
|
|
8221
|
+
parent_session_id TEXT,
|
|
6920
8222
|
project_identity_kind TEXT NOT NULL,
|
|
6921
8223
|
project_identity_key TEXT NOT NULL,
|
|
6922
8224
|
project_display_name TEXT NOT NULL,
|
|
@@ -6944,6 +8246,9 @@ function createSessionTables(db) {
|
|
|
6944
8246
|
CREATE INDEX IF NOT EXISTS idx_sessions_project
|
|
6945
8247
|
ON sessions(project_identity_kind, project_identity_key, activity_time);
|
|
6946
8248
|
|
|
8249
|
+
CREATE INDEX IF NOT EXISTS idx_sessions_parent
|
|
8250
|
+
ON sessions(parent_agent_name, parent_session_id);
|
|
8251
|
+
|
|
6947
8252
|
CREATE TABLE IF NOT EXISTS messages (
|
|
6948
8253
|
agent_name TEXT NOT NULL,
|
|
6949
8254
|
session_id TEXT NOT NULL,
|
|
@@ -7233,6 +8538,7 @@ function createProjectGroupsView(db) {
|
|
|
7233
8538
|
`);
|
|
7234
8539
|
return;
|
|
7235
8540
|
}
|
|
8541
|
+
const hasParentReference = columnExists(db, "sessions", "parent_agent_name") && columnExists(db, "sessions", "parent_session_id");
|
|
7236
8542
|
db.exec(`
|
|
7237
8543
|
CREATE VIEW IF NOT EXISTS project_groups_v AS
|
|
7238
8544
|
SELECT
|
|
@@ -7243,6 +8549,7 @@ function createProjectGroupsView(db) {
|
|
|
7243
8549
|
COUNT(*) AS session_count,
|
|
7244
8550
|
MAX(activity_time) AS last_activity
|
|
7245
8551
|
FROM sessions
|
|
8552
|
+
${hasParentReference ? "WHERE parent_agent_name IS NULL OR parent_session_id IS NULL" : ""}
|
|
7246
8553
|
GROUP BY project_identity_kind, project_identity_key;
|
|
7247
8554
|
`);
|
|
7248
8555
|
}
|
|
@@ -7640,6 +8947,19 @@ function addMessagePartsFormatVersion(db) {
|
|
|
7640
8947
|
}
|
|
7641
8948
|
db.exec("ALTER TABLE messages ADD COLUMN parts_format_version INTEGER NOT NULL DEFAULT 0");
|
|
7642
8949
|
}
|
|
8950
|
+
function addSessionParentReference(db) {
|
|
8951
|
+
if (!tableExists(db, "sessions")) return;
|
|
8952
|
+
if (!columnExists(db, "sessions", "parent_agent_name")) {
|
|
8953
|
+
db.exec("ALTER TABLE sessions ADD COLUMN parent_agent_name TEXT");
|
|
8954
|
+
}
|
|
8955
|
+
if (!columnExists(db, "sessions", "parent_session_id")) {
|
|
8956
|
+
db.exec("ALTER TABLE sessions ADD COLUMN parent_session_id TEXT");
|
|
8957
|
+
}
|
|
8958
|
+
db.exec(
|
|
8959
|
+
"CREATE INDEX IF NOT EXISTS idx_sessions_parent ON sessions(parent_agent_name, parent_session_id)"
|
|
8960
|
+
);
|
|
8961
|
+
recreateProjectGroupsView(db);
|
|
8962
|
+
}
|
|
7643
8963
|
var CODEX_EXEC_DECODE_MIGRATION_KEY = "codex_exec_decode_migrated_v3";
|
|
7644
8964
|
function migrateCodexExecDecode(db) {
|
|
7645
8965
|
if (!tableExists(db, "cache_meta")) return;
|
|
@@ -7654,6 +8974,30 @@ function migrateCodexExecDecode(db) {
|
|
|
7654
8974
|
"INSERT INTO cache_meta(key, value) VALUES (?, '1') ON CONFLICT(key) DO UPDATE SET value = '1'"
|
|
7655
8975
|
).run(CODEX_EXEC_DECODE_MIGRATION_KEY);
|
|
7656
8976
|
}
|
|
8977
|
+
var OPENCODE_SUBAGENT_FOLD_KEY = "opencode_subagent_fold_v1";
|
|
8978
|
+
var SUBAGENT_TREE_KEY = "subagent_tree_v1";
|
|
8979
|
+
function migrateOpenCodeSubagentFold(db) {
|
|
8980
|
+
if (!tableExists(db, "cache_meta")) return;
|
|
8981
|
+
const done = db.prepare("SELECT value FROM cache_meta WHERE key = ?").get(OPENCODE_SUBAGENT_FOLD_KEY);
|
|
8982
|
+
if (done) return;
|
|
8983
|
+
if (tableExists(db, "agent_cache")) {
|
|
8984
|
+
db.prepare("DELETE FROM agent_cache WHERE agent_name IN ('zcode', 'opencode')").run();
|
|
8985
|
+
}
|
|
8986
|
+
db.prepare(
|
|
8987
|
+
"INSERT INTO cache_meta(key, value) VALUES (?, '1') ON CONFLICT(key) DO UPDATE SET value = '1'"
|
|
8988
|
+
).run(OPENCODE_SUBAGENT_FOLD_KEY);
|
|
8989
|
+
}
|
|
8990
|
+
function migrateSubagentTree(db) {
|
|
8991
|
+
if (!tableExists(db, "cache_meta")) return;
|
|
8992
|
+
const done = db.prepare("SELECT value FROM cache_meta WHERE key = ?").get(SUBAGENT_TREE_KEY);
|
|
8993
|
+
if (done) return;
|
|
8994
|
+
if (tableExists(db, "agent_cache")) {
|
|
8995
|
+
db.prepare("DELETE FROM agent_cache WHERE agent_name IN ('codex', 'zcode', 'opencode')").run();
|
|
8996
|
+
}
|
|
8997
|
+
db.prepare(
|
|
8998
|
+
"INSERT INTO cache_meta(key, value) VALUES (?, '1') ON CONFLICT(key) DO UPDATE SET value = '1'"
|
|
8999
|
+
).run(SUBAGENT_TREE_KEY);
|
|
9000
|
+
}
|
|
7657
9001
|
function rebuildSearchIndex(db) {
|
|
7658
9002
|
if (!tableExists(db, "session_documents_fts")) {
|
|
7659
9003
|
return;
|
|
@@ -7763,6 +9107,8 @@ function ensureSchema(db, dbPath) {
|
|
|
7763
9107
|
createLatestCacheSchema(db);
|
|
7764
9108
|
setCacheSchemaVersion(db);
|
|
7765
9109
|
migrateCodexExecDecode(db);
|
|
9110
|
+
migrateOpenCodeSubagentFold(db);
|
|
9111
|
+
migrateSubagentTree(db);
|
|
7766
9112
|
return;
|
|
7767
9113
|
}
|
|
7768
9114
|
runSchemaMigrations(db, {
|
|
@@ -7794,7 +9140,13 @@ function ensureSchema(db, dbPath) {
|
|
|
7794
9140
|
invalidateSearchContentHashes(db2);
|
|
7795
9141
|
}
|
|
7796
9142
|
},
|
|
7797
|
-
{
|
|
9143
|
+
{
|
|
9144
|
+
version: 7,
|
|
9145
|
+
migrate(db2) {
|
|
9146
|
+
addSessionParentReference(db2);
|
|
9147
|
+
backfillStructuredSessions(db2);
|
|
9148
|
+
}
|
|
9149
|
+
},
|
|
7798
9150
|
{ version: 8, migrate: backfillFileActivity },
|
|
7799
9151
|
{
|
|
7800
9152
|
version: 9,
|
|
@@ -7825,7 +9177,8 @@ function ensureSchema(db, dbPath) {
|
|
|
7825
9177
|
{ version: 13, migrate: createCacheTables },
|
|
7826
9178
|
{ version: 14, migrate: addIndexedMessageCount },
|
|
7827
9179
|
{ version: 15, destructive: true, migrate: compactSessionDocuments },
|
|
7828
|
-
{ version: 17, migrate: addMessagePartsFormatVersion }
|
|
9180
|
+
{ version: 17, migrate: addMessagePartsFormatVersion },
|
|
9181
|
+
{ version: 18, migrate: addSessionParentReference }
|
|
7829
9182
|
]
|
|
7830
9183
|
});
|
|
7831
9184
|
createLatestCacheSchema(db);
|
|
@@ -7833,6 +9186,8 @@ function ensureSchema(db, dbPath) {
|
|
|
7833
9186
|
setCacheSchemaVersion(db);
|
|
7834
9187
|
}
|
|
7835
9188
|
migrateCodexExecDecode(db);
|
|
9189
|
+
migrateOpenCodeSubagentFold(db);
|
|
9190
|
+
migrateSubagentTree(db);
|
|
7836
9191
|
}
|
|
7837
9192
|
function escapeFtsTerm(value) {
|
|
7838
9193
|
return value.replaceAll('"', '""');
|
|
@@ -7963,6 +9318,7 @@ function readPendingReindexIds(db, agentName) {
|
|
|
7963
9318
|
return new Set(rows.map((row) => String(row.session_id)));
|
|
7964
9319
|
}
|
|
7965
9320
|
var SEARCH_INDEX_STATE_BATCH_SIZE = 900;
|
|
9321
|
+
var SEARCH_INDEX_COMMIT_CHUNK_SIZE = 64;
|
|
7966
9322
|
function shouldBulkSyncSearchIndex(options, changedCount) {
|
|
7967
9323
|
if (options.isBulk != null) {
|
|
7968
9324
|
return options.isBulk;
|
|
@@ -8222,6 +9578,32 @@ function syncSessionSearchIndex(agentName, sessions, loadSessionData, options =
|
|
|
8222
9578
|
sortIndex: sessionSortIndexMap.get(session.id) ?? 0
|
|
8223
9579
|
}));
|
|
8224
9580
|
let indexed = 0;
|
|
9581
|
+
if (changes.length > SEARCH_INDEX_COMMIT_CHUNK_SIZE) {
|
|
9582
|
+
runSearchIndexWrite(db, false, () => {
|
|
9583
|
+
indexed += writeSearchIndexRows(db, agentName, toDelete, []);
|
|
9584
|
+
});
|
|
9585
|
+
for (let offset = 0; offset < changes.length; offset += SEARCH_INDEX_COMMIT_CHUNK_SIZE) {
|
|
9586
|
+
const chunk = changes.slice(offset, offset + SEARCH_INDEX_COMMIT_CHUNK_SIZE);
|
|
9587
|
+
runSearchIndexWrite(db, false, () => {
|
|
9588
|
+
indexed += writeSearchIndexRows(
|
|
9589
|
+
db,
|
|
9590
|
+
agentName,
|
|
9591
|
+
[],
|
|
9592
|
+
loadSearchIndexEntries(agentName, chunk, loadSessionData)
|
|
9593
|
+
);
|
|
9594
|
+
});
|
|
9595
|
+
}
|
|
9596
|
+
return {
|
|
9597
|
+
agentName,
|
|
9598
|
+
mode: "incremental",
|
|
9599
|
+
sessions: sessions.length,
|
|
9600
|
+
changed: toUpsert.length,
|
|
9601
|
+
deleted: toDelete.length,
|
|
9602
|
+
indexed,
|
|
9603
|
+
skipped: toUpsert.length - indexed,
|
|
9604
|
+
durationMs: performance.now() - startedAt
|
|
9605
|
+
};
|
|
9606
|
+
}
|
|
8225
9607
|
const writeRows = () => {
|
|
8226
9608
|
indexed = writeSearchIndexRows(
|
|
8227
9609
|
db,
|
|
@@ -8847,6 +10229,7 @@ function searchFileActivitySessions(query, options = {}) {
|
|
|
8847
10229
|
}));
|
|
8848
10230
|
}
|
|
8849
10231
|
var CACHE_INITIALIZATION_VERSION = "session-cache-v2";
|
|
10232
|
+
var FULL_SYNC_CURSOR_PREFIX = "full_sync_cursor:";
|
|
8850
10233
|
function parseCachedSessionMeta(value) {
|
|
8851
10234
|
if (!value) return null;
|
|
8852
10235
|
try {
|
|
@@ -8857,7 +10240,7 @@ function parseCachedSessionMeta(value) {
|
|
|
8857
10240
|
}
|
|
8858
10241
|
function deleteLegacyCacheFile() {
|
|
8859
10242
|
const legacyPath = getLegacyCachePath();
|
|
8860
|
-
if (!
|
|
10243
|
+
if (!existsSync14(legacyPath)) {
|
|
8861
10244
|
return;
|
|
8862
10245
|
}
|
|
8863
10246
|
try {
|
|
@@ -8884,6 +10267,8 @@ function loadCachedSessions(agentName) {
|
|
|
8884
10267
|
title,
|
|
8885
10268
|
source_path,
|
|
8886
10269
|
directory,
|
|
10270
|
+
parent_agent_name,
|
|
10271
|
+
parent_session_id,
|
|
8887
10272
|
project_identity_kind,
|
|
8888
10273
|
project_identity_key,
|
|
8889
10274
|
project_display_name,
|
|
@@ -8946,6 +10331,43 @@ function markAgentCacheInitialized(agentName, indexVersion = CACHE_INITIALIZATIO
|
|
|
8946
10331
|
).run(agentName, Date.now(), indexVersion);
|
|
8947
10332
|
});
|
|
8948
10333
|
}
|
|
10334
|
+
function markAgentFullSyncStarted(agentName) {
|
|
10335
|
+
withCacheDb((db) => {
|
|
10336
|
+
db.prepare(
|
|
10337
|
+
`
|
|
10338
|
+
UPDATE cache_initialization
|
|
10339
|
+
SET last_sync_at = 0
|
|
10340
|
+
WHERE agent_name = ?
|
|
10341
|
+
`
|
|
10342
|
+
).run(agentName);
|
|
10343
|
+
});
|
|
10344
|
+
}
|
|
10345
|
+
function getAgentFullSyncCursor(agentName) {
|
|
10346
|
+
if (!hasCacheStorage()) return null;
|
|
10347
|
+
return withCacheDbReadOnly((db) => {
|
|
10348
|
+
const row = db.prepare("SELECT value FROM cache_meta WHERE key = ?").get(`${FULL_SYNC_CURSOR_PREFIX}${agentName}`);
|
|
10349
|
+
return row?.value || null;
|
|
10350
|
+
}) ?? null;
|
|
10351
|
+
}
|
|
10352
|
+
function markAgentFullSyncProgress(agentName, cursor) {
|
|
10353
|
+
if (!cursor) return;
|
|
10354
|
+
withCacheDb((db) => {
|
|
10355
|
+
db.prepare(
|
|
10356
|
+
`
|
|
10357
|
+
INSERT INTO cache_meta(key, value)
|
|
10358
|
+
VALUES (?, ?)
|
|
10359
|
+
ON CONFLICT(key) DO UPDATE SET value = excluded.value
|
|
10360
|
+
`
|
|
10361
|
+
).run(`${FULL_SYNC_CURSOR_PREFIX}${agentName}`, cursor);
|
|
10362
|
+
});
|
|
10363
|
+
}
|
|
10364
|
+
function clearAgentFullSyncCursor(agentName) {
|
|
10365
|
+
withCacheDb((db) => {
|
|
10366
|
+
db.prepare("DELETE FROM cache_meta WHERE key = ?").run(
|
|
10367
|
+
`${FULL_SYNC_CURSOR_PREFIX}${agentName}`
|
|
10368
|
+
);
|
|
10369
|
+
});
|
|
10370
|
+
}
|
|
8949
10371
|
function getAgentLastFullSyncAt(agentName) {
|
|
8950
10372
|
if (!hasCacheStorage()) {
|
|
8951
10373
|
return null;
|
|
@@ -8972,6 +10394,7 @@ function markAgentFullSyncCompleted(agentName) {
|
|
|
8972
10394
|
`
|
|
8973
10395
|
).run(Date.now(), agentName);
|
|
8974
10396
|
});
|
|
10397
|
+
clearAgentFullSyncCursor(agentName);
|
|
8975
10398
|
}
|
|
8976
10399
|
function loadCachedSessionRawEntry(agentName, sessionId) {
|
|
8977
10400
|
if (!hasCacheStorage()) {
|
|
@@ -8987,6 +10410,8 @@ function loadCachedSessionRawEntry(agentName, sessionId) {
|
|
|
8987
10410
|
title,
|
|
8988
10411
|
source_path,
|
|
8989
10412
|
directory,
|
|
10413
|
+
parent_agent_name,
|
|
10414
|
+
parent_session_id,
|
|
8990
10415
|
project_identity_kind,
|
|
8991
10416
|
project_identity_key,
|
|
8992
10417
|
project_display_name,
|
|
@@ -9114,9 +10539,6 @@ function saveCachedSessions(agentName, sessions, meta = {}) {
|
|
|
9114
10539
|
return persisted ?? false;
|
|
9115
10540
|
}
|
|
9116
10541
|
function saveCachedSessionChanges(agentName, changes, removedSessionIds, meta = {}) {
|
|
9117
|
-
if (changes.length === 0 && removedSessionIds.length === 0) {
|
|
9118
|
-
return true;
|
|
9119
|
-
}
|
|
9120
10542
|
const persisted = withCacheDb((db) => {
|
|
9121
10543
|
const deleteSession = db.prepare(
|
|
9122
10544
|
"DELETE FROM sessions WHERE agent_name = ? AND session_id = ?"
|
|
@@ -9191,7 +10613,7 @@ function clearCache() {
|
|
|
9191
10613
|
const walPath = `${cachePath}-wal`;
|
|
9192
10614
|
const shmPath = `${cachePath}-shm`;
|
|
9193
10615
|
for (const filePath of [walPath, shmPath]) {
|
|
9194
|
-
if (!
|
|
10616
|
+
if (!existsSync14(filePath)) {
|
|
9195
10617
|
continue;
|
|
9196
10618
|
}
|
|
9197
10619
|
try {
|
|
@@ -9220,6 +10642,8 @@ function sessionSignature(session) {
|
|
|
9220
10642
|
return JSON.stringify([
|
|
9221
10643
|
session.title,
|
|
9222
10644
|
session.directory,
|
|
10645
|
+
session.parent_reference?.agentName ?? null,
|
|
10646
|
+
session.parent_reference?.sessionId ?? null,
|
|
9223
10647
|
session.time_created,
|
|
9224
10648
|
session.time_updated ?? session.time_created,
|
|
9225
10649
|
session.stats.message_count,
|
|
@@ -9273,13 +10697,7 @@ function filterSessions(sessions, options) {
|
|
|
9273
10697
|
if (options.cwd) {
|
|
9274
10698
|
result = filterSessionsByProjectScope(result, options.cwd);
|
|
9275
10699
|
}
|
|
9276
|
-
|
|
9277
|
-
result = result.filter((s) => (s.time_updated ?? s.time_created) >= options.from);
|
|
9278
|
-
}
|
|
9279
|
-
if (options.to != null) {
|
|
9280
|
-
result = result.filter((s) => (s.time_updated ?? s.time_created) <= options.to);
|
|
9281
|
-
}
|
|
9282
|
-
return result;
|
|
10700
|
+
return filterSessionTreeByActivityWindow(result, options.from, options.to);
|
|
9283
10701
|
}
|
|
9284
10702
|
function saveCachedSessionDiff(agent, cachedSessions, updatedSessions, changedIds = []) {
|
|
9285
10703
|
const diff = computeSessionDiff(cachedSessions, updatedSessions, changedIds, sessionSignature);
|
|
@@ -9301,17 +10719,47 @@ function chunkSessions(items, chunkCount) {
|
|
|
9301
10719
|
});
|
|
9302
10720
|
return chunks.filter((chunk) => chunk.length > 0);
|
|
9303
10721
|
}
|
|
9304
|
-
function ensureSessionTagsSync(agent, sessions) {
|
|
10722
|
+
function ensureSessionTagsSync(agent, sessions, onProgress) {
|
|
9305
10723
|
let changed = false;
|
|
10724
|
+
let processed = 0;
|
|
10725
|
+
const total = sessions.length;
|
|
10726
|
+
const timing = {
|
|
10727
|
+
sessions: total,
|
|
10728
|
+
cacheHits: 0,
|
|
10729
|
+
staleSessions: 0,
|
|
10730
|
+
failedSessions: 0,
|
|
10731
|
+
getSessionDataCalls: 0,
|
|
10732
|
+
getSessionDataMs: 0,
|
|
10733
|
+
classifySessionTagsCalls: 0,
|
|
10734
|
+
classifySessionTagsMs: 0
|
|
10735
|
+
};
|
|
9306
10736
|
const tagged = sessions.map((session) => {
|
|
9307
10737
|
const sourceUpdatedAt = session.time_updated ?? session.time_created;
|
|
9308
10738
|
const currentTags = Array.isArray(session.smart_tags) ? session.smart_tags : null;
|
|
9309
10739
|
if (currentTags && session.smart_tags_source_updated_at === sourceUpdatedAt) {
|
|
10740
|
+
timing.cacheHits += 1;
|
|
10741
|
+
processed += 1;
|
|
10742
|
+
onProgress?.(processed, total);
|
|
9310
10743
|
return session;
|
|
9311
10744
|
}
|
|
10745
|
+
timing.staleSessions += 1;
|
|
9312
10746
|
try {
|
|
9313
|
-
|
|
9314
|
-
const
|
|
10747
|
+
timing.getSessionDataCalls += 1;
|
|
10748
|
+
const getSessionDataStartedAt = performance.now();
|
|
10749
|
+
let data;
|
|
10750
|
+
try {
|
|
10751
|
+
data = agent.getSessionData(session.id);
|
|
10752
|
+
} finally {
|
|
10753
|
+
timing.getSessionDataMs += performance.now() - getSessionDataStartedAt;
|
|
10754
|
+
}
|
|
10755
|
+
timing.classifySessionTagsCalls += 1;
|
|
10756
|
+
const classifySessionTagsStartedAt = performance.now();
|
|
10757
|
+
let tags;
|
|
10758
|
+
try {
|
|
10759
|
+
tags = classifySessionTags(data);
|
|
10760
|
+
} finally {
|
|
10761
|
+
timing.classifySessionTagsMs += performance.now() - classifySessionTagsStartedAt;
|
|
10762
|
+
}
|
|
9315
10763
|
changed = true;
|
|
9316
10764
|
return {
|
|
9317
10765
|
...session,
|
|
@@ -9319,10 +10767,14 @@ function ensureSessionTagsSync(agent, sessions) {
|
|
|
9319
10767
|
smart_tags_source_updated_at: getSmartTagSourceTimestamp(data)
|
|
9320
10768
|
};
|
|
9321
10769
|
} catch {
|
|
10770
|
+
timing.failedSessions += 1;
|
|
9322
10771
|
return session;
|
|
10772
|
+
} finally {
|
|
10773
|
+
processed += 1;
|
|
10774
|
+
onProgress?.(processed, total);
|
|
9323
10775
|
}
|
|
9324
10776
|
});
|
|
9325
|
-
return { sessions: tagged, changed };
|
|
10777
|
+
return { sessions: tagged, changed, timing };
|
|
9326
10778
|
}
|
|
9327
10779
|
async function classifySessionTagsInWorker(workerUrl, agentName, sessionIds, meta) {
|
|
9328
10780
|
return new Promise((resolveWorker, rejectWorker) => {
|
|
@@ -9434,12 +10886,13 @@ async function scanAgentSmart(agent, options, onProgress) {
|
|
|
9434
10886
|
}
|
|
9435
10887
|
agent.setSessionMetaMap(metaMap);
|
|
9436
10888
|
if (options.cacheOnly) {
|
|
10889
|
+
const visibleSessions = agent.filterCachedSessions(cached.sessions);
|
|
9437
10890
|
onProgress?.({
|
|
9438
10891
|
agent: agent.name,
|
|
9439
10892
|
phase: "cache",
|
|
9440
|
-
cachedCount:
|
|
10893
|
+
cachedCount: visibleSessions.length
|
|
9441
10894
|
});
|
|
9442
|
-
return finalizeAgentScan(agent,
|
|
10895
|
+
return finalizeAgentScan(agent, visibleSessions, {
|
|
9443
10896
|
finalization: { kind: "cache-only", cached },
|
|
9444
10897
|
options,
|
|
9445
10898
|
timing,
|
|
@@ -9515,6 +10968,7 @@ async function scanAgentFull(agent, options, onProgress, timing = { total: 0 },
|
|
|
9515
10968
|
from: options.from,
|
|
9516
10969
|
to: options.to,
|
|
9517
10970
|
fast: options.fast,
|
|
10971
|
+
includeRelatedSessions: true,
|
|
9518
10972
|
onProgress: (progress) => {
|
|
9519
10973
|
onProgress?.({
|
|
9520
10974
|
agent: agent.name,
|
|
@@ -9750,16 +11204,16 @@ function getStateDir() {
|
|
|
9750
11204
|
if (process.env.CODESESH_STATE_DIR) return process.env.CODESESH_STATE_DIR;
|
|
9751
11205
|
const currentPlatform = platform4();
|
|
9752
11206
|
if (currentPlatform === "darwin") {
|
|
9753
|
-
return
|
|
11207
|
+
return join15(homedir7(), "Library", "Application Support", "codesesh");
|
|
9754
11208
|
}
|
|
9755
11209
|
if (currentPlatform === "win32") {
|
|
9756
11210
|
const appData = process.env.APPDATA ?? process.env.LOCALAPPDATA;
|
|
9757
|
-
return
|
|
11211
|
+
return join15(appData ?? join15(homedir7(), "AppData", "Roaming"), "codesesh");
|
|
9758
11212
|
}
|
|
9759
|
-
return
|
|
11213
|
+
return join15(process.env.XDG_DATA_HOME ?? join15(homedir7(), ".local", "share"), "codesesh");
|
|
9760
11214
|
}
|
|
9761
11215
|
function getStateDbPath() {
|
|
9762
|
-
return
|
|
11216
|
+
return join15(getStateDir(), STATE_DB_FILENAME);
|
|
9763
11217
|
}
|
|
9764
11218
|
function useMemoryStateStore() {
|
|
9765
11219
|
return process.env.CODESESH_STATE_STORE === MEMORY_STATE_STORE;
|
|
@@ -10203,7 +11657,9 @@ function buildDashboard(sessions, options) {
|
|
|
10203
11657
|
dailyTokenMap.set(key, { date: key, input: 0, output: 0, cache_read: 0, cache_create: 0 });
|
|
10204
11658
|
}
|
|
10205
11659
|
}
|
|
10206
|
-
|
|
11660
|
+
const visibleSessions = filterSessionTreeByActivityWindow(sessions, from, to);
|
|
11661
|
+
for (const session of visibleSessions) {
|
|
11662
|
+
if (isChildSession(session)) continue;
|
|
10207
11663
|
const agentName = getSessionAgentName(session);
|
|
10208
11664
|
if (scope.agent && agentName !== scope.agent) continue;
|
|
10209
11665
|
if (scope.projectKind || scope.projectKey) {
|
|
@@ -10213,8 +11669,6 @@ function buildDashboard(sessions, options) {
|
|
|
10213
11669
|
}
|
|
10214
11670
|
}
|
|
10215
11671
|
const activity = getSessionActivityTime(session);
|
|
10216
|
-
if (from != null && activity < from) continue;
|
|
10217
|
-
if (activity > to) continue;
|
|
10218
11672
|
const messageCount = session.stats.message_count;
|
|
10219
11673
|
const sessionTokens = getTotalTokens(session.stats);
|
|
10220
11674
|
totalSessions += 1;
|
|
@@ -10319,6 +11773,7 @@ function emptyMetrics() {
|
|
|
10319
11773
|
function attachProjectMetrics(projects, sessions) {
|
|
10320
11774
|
const metrics = /* @__PURE__ */ new Map();
|
|
10321
11775
|
for (const session of sessions) {
|
|
11776
|
+
if (isChildSession(session)) continue;
|
|
10322
11777
|
const identity = session.project_identity;
|
|
10323
11778
|
if (!identity) continue;
|
|
10324
11779
|
const key = getProjectIdentityKey(identity);
|
|
@@ -10501,6 +11956,9 @@ export {
|
|
|
10501
11956
|
getSessionAgentKey,
|
|
10502
11957
|
sessionRoutePath,
|
|
10503
11958
|
mergeSortedSessions,
|
|
11959
|
+
isChildSession,
|
|
11960
|
+
getRootSessions,
|
|
11961
|
+
filterSessionTreeByActivityWindow,
|
|
10504
11962
|
registerAgent,
|
|
10505
11963
|
createRegisteredAgents,
|
|
10506
11964
|
getRegisteredAgents,
|
|
@@ -10529,6 +11987,9 @@ export {
|
|
|
10529
11987
|
loadCachedSessions,
|
|
10530
11988
|
isAgentCacheInitialized,
|
|
10531
11989
|
markAgentCacheInitialized,
|
|
11990
|
+
markAgentFullSyncStarted,
|
|
11991
|
+
getAgentFullSyncCursor,
|
|
11992
|
+
markAgentFullSyncProgress,
|
|
10532
11993
|
getAgentLastFullSyncAt,
|
|
10533
11994
|
markAgentFullSyncCompleted,
|
|
10534
11995
|
saveCachedSessions,
|
|
@@ -10558,4 +12019,4 @@ export {
|
|
|
10558
12019
|
executeSessionSearch,
|
|
10559
12020
|
filterSessionSearchCandidates
|
|
10560
12021
|
};
|
|
10561
|
-
//# sourceMappingURL=chunk-
|
|
12022
|
+
//# sourceMappingURL=chunk-G2BTNW3C.js.map
|