chat-recall 0.7.17 → 0.7.19
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 +2 -2
- package/dist/cli.js +1402 -538
- package/dist/cli.js.map +1 -1
- package/dist/mcp-relay.js +136 -40
- package/dist/mcp-relay.js.map +1 -1
- package/dist/mcp.js +1160 -290
- package/dist/mcp.js.map +1 -1
- package/dist/raw-archive-parts.d.ts +46 -0
- package/dist/raw-archive-parts.d.ts.map +1 -0
- package/dist/raw-archive-parts.js +175 -0
- package/dist/raw-archive-parts.js.map +1 -0
- package/dist/relay-lifecycle.d.ts +23 -0
- package/dist/relay-lifecycle.d.ts.map +1 -1
- package/dist/relay-lifecycle.js +173 -0
- package/dist/relay-lifecycle.js.map +1 -1
- package/dist/repair.d.ts.map +1 -1
- package/dist/repair.js +15 -7
- package/dist/repair.js.map +1 -1
- package/dist/scan-worker.js +617 -31
- package/dist/sync-client.d.ts +8 -0
- package/dist/sync-client.d.ts.map +1 -1
- package/dist/sync-client.js +107 -12
- package/dist/sync-client.js.map +1 -1
- package/dist/sync-ledger.d.ts +37 -1
- package/dist/sync-ledger.d.ts.map +1 -1
- package/dist/sync-ledger.js +50 -1
- package/dist/sync-ledger.js.map +1 -1
- package/dist/watch.js +1221 -348
- package/package.json +1 -1
package/dist/scan-worker.js
CHANGED
|
@@ -4524,6 +4524,7 @@ function defaultSourcesEnabled() {
|
|
|
4524
4524
|
commands: true
|
|
4525
4525
|
},
|
|
4526
4526
|
cursor: { sessions: true, skills: true, agents: true, commands: true },
|
|
4527
|
+
hermes: { sessions: true },
|
|
4527
4528
|
shared: { skills: true },
|
|
4528
4529
|
common: { mcps: true, agentMd: true }
|
|
4529
4530
|
};
|
|
@@ -4594,6 +4595,7 @@ function mergeSources(base, partial) {
|
|
|
4594
4595
|
codex: { ...base.enabled.codex, ...partial.enabled?.codex ?? {} },
|
|
4595
4596
|
agy: { ...base.enabled.agy, ...inherited, ...partial.enabled?.agy ?? {} },
|
|
4596
4597
|
cursor: { ...base.enabled.cursor, ...partial.enabled?.cursor ?? {} },
|
|
4598
|
+
hermes: { ...base.enabled.hermes, ...partial.enabled?.hermes ?? {} },
|
|
4597
4599
|
shared: { ...base.enabled.shared, ...partial.enabled?.shared ?? {} },
|
|
4598
4600
|
common: { ...base.enabled.common, ...partial.enabled?.common ?? {} }
|
|
4599
4601
|
};
|
|
@@ -4605,6 +4607,7 @@ function mergeSources(base, partial) {
|
|
|
4605
4607
|
agyHome: partial.agyHome ?? base.agyHome,
|
|
4606
4608
|
cursorHome: partial.cursorHome ?? base.cursorHome,
|
|
4607
4609
|
cursorIdeHome: partial.cursorIdeHome ?? base.cursorIdeHome,
|
|
4610
|
+
hermesHome: partial.hermesHome ?? base.hermesHome,
|
|
4608
4611
|
extraClaudeHomes: partial.extraClaudeHomes ?? base.extraClaudeHomes,
|
|
4609
4612
|
approvedHomes: partial.approvedHomes ?? base.approvedHomes,
|
|
4610
4613
|
declinedHomes: partial.declinedHomes ?? base.declinedHomes,
|
|
@@ -5284,6 +5287,8 @@ function declaredOrigin(value) {
|
|
|
5284
5287
|
function userRecordOrigin(obj, text) {
|
|
5285
5288
|
if (obj.isSidechain === true)
|
|
5286
5289
|
return "subagent";
|
|
5290
|
+
if (obj.isCompactSummary === true)
|
|
5291
|
+
return "compact-summary";
|
|
5287
5292
|
const declared = declaredOrigin(obj.origin);
|
|
5288
5293
|
if (declared && declared !== "human")
|
|
5289
5294
|
return declared;
|
|
@@ -5430,6 +5435,7 @@ function redactContainer(c, redact) {
|
|
|
5430
5435
|
}))
|
|
5431
5436
|
};
|
|
5432
5437
|
}
|
|
5438
|
+
var RAW_PARSE_MAX_BYTES;
|
|
5433
5439
|
var init_raw = __esm({
|
|
5434
5440
|
"../engine/dist/transcript/raw.js"() {
|
|
5435
5441
|
"use strict";
|
|
@@ -5437,6 +5443,7 @@ var init_raw = __esm({
|
|
|
5437
5443
|
init_claude();
|
|
5438
5444
|
init_codex();
|
|
5439
5445
|
init_from_events();
|
|
5446
|
+
RAW_PARSE_MAX_BYTES = 40 * 1024 * 1024;
|
|
5440
5447
|
}
|
|
5441
5448
|
});
|
|
5442
5449
|
|
|
@@ -5524,6 +5531,13 @@ function grandfatherLegacyHomes() {
|
|
|
5524
5531
|
}
|
|
5525
5532
|
return { seeded };
|
|
5526
5533
|
}
|
|
5534
|
+
function defaultHermesHome() {
|
|
5535
|
+
if (process.platform === "win32") {
|
|
5536
|
+
const local = process.env.LOCALAPPDATA?.trim() || join3(homedir2(), "AppData", "Local");
|
|
5537
|
+
return join3(local, "hermes");
|
|
5538
|
+
}
|
|
5539
|
+
return join3(homedir2(), ".hermes");
|
|
5540
|
+
}
|
|
5527
5541
|
function primaryHomes() {
|
|
5528
5542
|
const home = homedir2();
|
|
5529
5543
|
const env = process.env;
|
|
@@ -5540,7 +5554,8 @@ function primaryHomes() {
|
|
|
5540
5554
|
env.CHAT_RECALL_CODEX_HOME || s?.codexHome || join3(home, ".codex"),
|
|
5541
5555
|
env.CHAT_RECALL_AGY_HOME || s?.agyHome || join3(home, ".gemini", "antigravity-cli"),
|
|
5542
5556
|
env.CHAT_RECALL_CURSOR_HOME || s?.cursorHome || join3(home, ".cursor"),
|
|
5543
|
-
dirOf(opencodeDb)
|
|
5557
|
+
dirOf(opencodeDb),
|
|
5558
|
+
env.CHAT_RECALL_HERMES_HOME || s?.hermesHome || env.HERMES_HOME?.trim() || defaultHermesHome()
|
|
5544
5559
|
];
|
|
5545
5560
|
return candidates.map(normalizeHomePath);
|
|
5546
5561
|
}
|
|
@@ -5619,6 +5634,9 @@ function geminiHomeDir() {
|
|
|
5619
5634
|
function opencodeDbPath() {
|
|
5620
5635
|
return process.env.CHAT_RECALL_OPENCODE_DB || sources().opencodeDbPath || join4(homedir3(), ".local", "share", "opencode", "opencode.db");
|
|
5621
5636
|
}
|
|
5637
|
+
function hermesHomeDir() {
|
|
5638
|
+
return process.env.CHAT_RECALL_HERMES_HOME || sources().hermesHome || process.env.HERMES_HOME?.trim() || defaultHermesHome();
|
|
5639
|
+
}
|
|
5622
5640
|
function codexHomeDir() {
|
|
5623
5641
|
return process.env.CHAT_RECALL_CODEX_HOME || sources().codexHome || join4(homedir3(), ".codex");
|
|
5624
5642
|
}
|
|
@@ -5724,6 +5742,22 @@ function opencodeDbPaths(opts = {}) {
|
|
|
5724
5742
|
dbs.push(primary);
|
|
5725
5743
|
return applyExclusions(dbs, opts.includeExcluded);
|
|
5726
5744
|
}
|
|
5745
|
+
function hermesDbPaths(opts = {}) {
|
|
5746
|
+
const root = hermesHomeDir();
|
|
5747
|
+
const dbs = [join4(root, "state.db")];
|
|
5748
|
+
try {
|
|
5749
|
+
const profiles = join4(root, "profiles");
|
|
5750
|
+
for (const entry of readdirSync2(profiles, { withFileTypes: true })) {
|
|
5751
|
+
if (!entry.isDirectory())
|
|
5752
|
+
continue;
|
|
5753
|
+
const p = join4(profiles, entry.name, "state.db");
|
|
5754
|
+
if (existsSync3(p))
|
|
5755
|
+
dbs.push(p);
|
|
5756
|
+
}
|
|
5757
|
+
} catch {
|
|
5758
|
+
}
|
|
5759
|
+
return applyExclusions(dbs, opts.includeExcluded);
|
|
5760
|
+
}
|
|
5727
5761
|
var sources, _sourceExclusionFilter;
|
|
5728
5762
|
var init_tool_paths = __esm({
|
|
5729
5763
|
"../engine/dist/core/tool-paths.js"() {
|
|
@@ -7001,6 +7035,71 @@ function readTailFromOffset(path, offset, maxBytes = TAIL_READ_MAX_BYTES) {
|
|
|
7001
7035
|
}
|
|
7002
7036
|
}
|
|
7003
7037
|
}
|
|
7038
|
+
function readSourcesFromOffset(paths, offset, maxBytes = TAIL_READ_MAX_BYTES) {
|
|
7039
|
+
let start = 0;
|
|
7040
|
+
let linesBefore = 0;
|
|
7041
|
+
for (let i = 0; i < paths.length; i++) {
|
|
7042
|
+
const path = paths[i];
|
|
7043
|
+
let size = 0;
|
|
7044
|
+
try {
|
|
7045
|
+
size = statSync3(path).size;
|
|
7046
|
+
} catch {
|
|
7047
|
+
size = 0;
|
|
7048
|
+
}
|
|
7049
|
+
const last = i === paths.length - 1;
|
|
7050
|
+
if (!last && offset >= start + size) {
|
|
7051
|
+
const lines = linesBeforeOffset(path, size);
|
|
7052
|
+
if (lines === void 0)
|
|
7053
|
+
return { text: "", newOffset: offset };
|
|
7054
|
+
linesBefore += lines + (size > 0 && !endsWithNewline(path, size) ? 1 : 0);
|
|
7055
|
+
start += size;
|
|
7056
|
+
continue;
|
|
7057
|
+
}
|
|
7058
|
+
const local = offset - start;
|
|
7059
|
+
const read = readTailFromOffset(path, local, maxBytes);
|
|
7060
|
+
const shift = (r) => ({
|
|
7061
|
+
...r,
|
|
7062
|
+
newOffset: start + r.newOffset,
|
|
7063
|
+
...r.baseLine !== void 0 ? { baseLine: linesBefore + r.baseLine } : {}
|
|
7064
|
+
});
|
|
7065
|
+
if (last || read.newOffset > local || local >= size)
|
|
7066
|
+
return shift(read);
|
|
7067
|
+
if (size - local > maxBytes)
|
|
7068
|
+
return { text: "", newOffset: start + size, skippedBytes: size - local };
|
|
7069
|
+
let fd = null;
|
|
7070
|
+
try {
|
|
7071
|
+
fd = openSync(path, "r");
|
|
7072
|
+
const text = readAt(fd, local, size - local).toString("utf-8");
|
|
7073
|
+
return shift({ text, newOffset: size, baseLine: linesBeforeOffset(path, local) });
|
|
7074
|
+
} catch {
|
|
7075
|
+
return { text: "", newOffset: offset };
|
|
7076
|
+
} finally {
|
|
7077
|
+
if (fd !== null) {
|
|
7078
|
+
try {
|
|
7079
|
+
closeSync(fd);
|
|
7080
|
+
} catch {
|
|
7081
|
+
}
|
|
7082
|
+
}
|
|
7083
|
+
}
|
|
7084
|
+
}
|
|
7085
|
+
return { text: "", newOffset: offset };
|
|
7086
|
+
}
|
|
7087
|
+
function endsWithNewline(path, size) {
|
|
7088
|
+
let fd = null;
|
|
7089
|
+
try {
|
|
7090
|
+
fd = openSync(path, "r");
|
|
7091
|
+
return readAt(fd, size - 1, 1)[0] === 10;
|
|
7092
|
+
} catch {
|
|
7093
|
+
return true;
|
|
7094
|
+
} finally {
|
|
7095
|
+
if (fd !== null) {
|
|
7096
|
+
try {
|
|
7097
|
+
closeSync(fd);
|
|
7098
|
+
} catch {
|
|
7099
|
+
}
|
|
7100
|
+
}
|
|
7101
|
+
}
|
|
7102
|
+
}
|
|
7004
7103
|
function findNewline(fd, from, size) {
|
|
7005
7104
|
const block = Buffer.allocUnsafe(SKIP_SCAN_BYTES);
|
|
7006
7105
|
let pos = from;
|
|
@@ -9479,6 +9578,473 @@ var init_cursor = __esm({
|
|
|
9479
9578
|
}
|
|
9480
9579
|
});
|
|
9481
9580
|
|
|
9581
|
+
// ../engine/dist/core/backends/hermes.js
|
|
9582
|
+
import { existsSync as existsSync10 } from "fs";
|
|
9583
|
+
function firstUserPrompt(db, sessionId) {
|
|
9584
|
+
try {
|
|
9585
|
+
const row = db.prepare(`
|
|
9586
|
+
SELECT substr(content, 1, 400) AS text FROM messages
|
|
9587
|
+
WHERE session_id = ? AND role = 'user' AND active = 1 AND _compressed_summary = 0
|
|
9588
|
+
AND (display_kind IS NULL OR display_kind = 'steer')
|
|
9589
|
+
AND length(trim(content)) > 0
|
|
9590
|
+
ORDER BY id LIMIT 1
|
|
9591
|
+
`).get(sessionId);
|
|
9592
|
+
return row?.text ? flatString(row.text.slice(0, 200)) : "";
|
|
9593
|
+
} catch {
|
|
9594
|
+
return "";
|
|
9595
|
+
}
|
|
9596
|
+
}
|
|
9597
|
+
function messageRowsToEvents(rows) {
|
|
9598
|
+
const events = [];
|
|
9599
|
+
let line = 0;
|
|
9600
|
+
for (const r of rows) {
|
|
9601
|
+
line++;
|
|
9602
|
+
if (r.active === 0)
|
|
9603
|
+
continue;
|
|
9604
|
+
if (r.display_kind === "failed_turn" || r.display_kind === "hidden")
|
|
9605
|
+
continue;
|
|
9606
|
+
const ts = Math.round((r.timestamp || 0) * 1e3);
|
|
9607
|
+
const tsIso = ts ? new Date(ts).toISOString() : void 0;
|
|
9608
|
+
const text = (r.content ?? "").trim();
|
|
9609
|
+
if (r._compressed_summary === 1) {
|
|
9610
|
+
if (text)
|
|
9611
|
+
events.push({ kind: "summary", ts, tsIso, line, text });
|
|
9612
|
+
continue;
|
|
9613
|
+
}
|
|
9614
|
+
if (r.role === "user") {
|
|
9615
|
+
if (!text)
|
|
9616
|
+
continue;
|
|
9617
|
+
events.push({
|
|
9618
|
+
kind: "user",
|
|
9619
|
+
ts,
|
|
9620
|
+
tsIso,
|
|
9621
|
+
line,
|
|
9622
|
+
text,
|
|
9623
|
+
...r.display_kind === "process_complete" ? { origin: "task-notification" } : {}
|
|
9624
|
+
});
|
|
9625
|
+
continue;
|
|
9626
|
+
}
|
|
9627
|
+
if (r.role === "assistant") {
|
|
9628
|
+
if (text)
|
|
9629
|
+
events.push({ kind: "assistant_text", ts, tsIso, line, text });
|
|
9630
|
+
for (const call of parseToolCalls(r.tool_calls)) {
|
|
9631
|
+
const toolName = call.function?.name ?? "";
|
|
9632
|
+
const toolInput = parseArguments(call.function?.arguments);
|
|
9633
|
+
const command = toolName === "terminal" && typeof toolInput?.command === "string" ? toolInput.command : void 0;
|
|
9634
|
+
events.push({
|
|
9635
|
+
kind: "tool_use",
|
|
9636
|
+
ts,
|
|
9637
|
+
tsIso,
|
|
9638
|
+
line,
|
|
9639
|
+
toolName,
|
|
9640
|
+
toolUseId: call.id || call.call_id || `${r.id}:${toolName}`,
|
|
9641
|
+
toolInput,
|
|
9642
|
+
...command ? { command } : {}
|
|
9643
|
+
});
|
|
9644
|
+
}
|
|
9645
|
+
continue;
|
|
9646
|
+
}
|
|
9647
|
+
if (r.role === "tool") {
|
|
9648
|
+
const body = r.content ?? "";
|
|
9649
|
+
const { exitCode, isError } = toolResultStatus(body);
|
|
9650
|
+
events.push({
|
|
9651
|
+
kind: "tool_result",
|
|
9652
|
+
ts,
|
|
9653
|
+
tsIso,
|
|
9654
|
+
line,
|
|
9655
|
+
toolUseId: r.tool_call_id ?? void 0,
|
|
9656
|
+
toolName: r.tool_name ?? void 0,
|
|
9657
|
+
resultBody: body,
|
|
9658
|
+
resultIsError: isError,
|
|
9659
|
+
...exitCode !== void 0 ? { resultExitCode: exitCode } : {},
|
|
9660
|
+
resultBytes: Buffer.byteLength(body)
|
|
9661
|
+
});
|
|
9662
|
+
}
|
|
9663
|
+
}
|
|
9664
|
+
return events;
|
|
9665
|
+
}
|
|
9666
|
+
function parseToolCalls(json) {
|
|
9667
|
+
if (!json)
|
|
9668
|
+
return [];
|
|
9669
|
+
try {
|
|
9670
|
+
const calls = JSON.parse(json);
|
|
9671
|
+
return Array.isArray(calls) ? calls : [];
|
|
9672
|
+
} catch {
|
|
9673
|
+
return [];
|
|
9674
|
+
}
|
|
9675
|
+
}
|
|
9676
|
+
function parseArguments(args) {
|
|
9677
|
+
if (typeof args !== "string")
|
|
9678
|
+
return args ?? {};
|
|
9679
|
+
try {
|
|
9680
|
+
return JSON.parse(args);
|
|
9681
|
+
} catch {
|
|
9682
|
+
return { raw: args };
|
|
9683
|
+
}
|
|
9684
|
+
}
|
|
9685
|
+
function toolResultStatus(body) {
|
|
9686
|
+
if (!body.startsWith("{"))
|
|
9687
|
+
return { isError: false };
|
|
9688
|
+
try {
|
|
9689
|
+
const r = JSON.parse(body);
|
|
9690
|
+
const exitCode = typeof r.exit_code === "number" ? r.exit_code : void 0;
|
|
9691
|
+
const hasError = r.error !== void 0 && r.error !== null && r.error !== "";
|
|
9692
|
+
return { exitCode, isError: hasError || exitCode !== void 0 && exitCode !== 0 };
|
|
9693
|
+
} catch {
|
|
9694
|
+
return { isError: false };
|
|
9695
|
+
}
|
|
9696
|
+
}
|
|
9697
|
+
var PREFIX5, MESSAGE_COLUMNS, ARCHIVED_SESSION_COLUMNS, SESSION_MTIME_SQL, HermesBackend, hermesBackend;
|
|
9698
|
+
var init_hermes = __esm({
|
|
9699
|
+
"../engine/dist/core/backends/hermes.js"() {
|
|
9700
|
+
"use strict";
|
|
9701
|
+
init_sqlite_reader();
|
|
9702
|
+
init_tool_paths();
|
|
9703
|
+
init_session_outcome();
|
|
9704
|
+
init_session_git();
|
|
9705
|
+
init_generic_engine();
|
|
9706
|
+
init_flat_string();
|
|
9707
|
+
PREFIX5 = "hermes_";
|
|
9708
|
+
MESSAGE_COLUMNS = "id, role, content, tool_calls, tool_call_id, tool_name, timestamp, display_kind, active, _compressed_summary";
|
|
9709
|
+
ARCHIVED_SESSION_COLUMNS = [
|
|
9710
|
+
"id",
|
|
9711
|
+
"source",
|
|
9712
|
+
"title",
|
|
9713
|
+
"model",
|
|
9714
|
+
"cwd",
|
|
9715
|
+
"git_branch",
|
|
9716
|
+
"git_repo_root",
|
|
9717
|
+
"parent_session_id",
|
|
9718
|
+
"started_at",
|
|
9719
|
+
"ended_at",
|
|
9720
|
+
"end_reason",
|
|
9721
|
+
"message_count",
|
|
9722
|
+
"tool_call_count",
|
|
9723
|
+
"input_tokens",
|
|
9724
|
+
"output_tokens",
|
|
9725
|
+
"cache_read_tokens",
|
|
9726
|
+
"cache_write_tokens",
|
|
9727
|
+
"reasoning_tokens",
|
|
9728
|
+
"estimated_cost_usd",
|
|
9729
|
+
"actual_cost_usd"
|
|
9730
|
+
];
|
|
9731
|
+
SESSION_MTIME_SQL = `MAX(
|
|
9732
|
+
s.started_at,
|
|
9733
|
+
COALESCE(s.ended_at, 0),
|
|
9734
|
+
COALESCE(s.last_activity_at, 0),
|
|
9735
|
+
COALESCE((SELECT MAX(m.timestamp) FROM messages m WHERE m.session_id = s.id), 0)
|
|
9736
|
+
)`;
|
|
9737
|
+
HermesBackend = class {
|
|
9738
|
+
id = "hermes";
|
|
9739
|
+
idPrefix = PREFIX5;
|
|
9740
|
+
displayName = "Hermes";
|
|
9741
|
+
homeDir() {
|
|
9742
|
+
return hermesHomeDir();
|
|
9743
|
+
}
|
|
9744
|
+
/** Every Hermes database on this machine, the root profile first. */
|
|
9745
|
+
dbPaths() {
|
|
9746
|
+
return hermesDbPaths();
|
|
9747
|
+
}
|
|
9748
|
+
isAvailable() {
|
|
9749
|
+
return this.dbPaths().some((p) => existsSync10(p));
|
|
9750
|
+
}
|
|
9751
|
+
/** The database that holds this session. A session id is unique within a
|
|
9752
|
+
* database, and each named profile has its own. */
|
|
9753
|
+
dbPathFor(rawId) {
|
|
9754
|
+
const cached = this._dbForSession.get(rawId);
|
|
9755
|
+
if (cached)
|
|
9756
|
+
return cached;
|
|
9757
|
+
for (const path of this.dbPaths()) {
|
|
9758
|
+
const db = openSqliteReadonly(path);
|
|
9759
|
+
if (!db)
|
|
9760
|
+
continue;
|
|
9761
|
+
try {
|
|
9762
|
+
if (db.prepare("SELECT 1 AS ok FROM sessions WHERE id = ?").get(rawId)) {
|
|
9763
|
+
this._dbForSession.set(rawId, path);
|
|
9764
|
+
return path;
|
|
9765
|
+
}
|
|
9766
|
+
} catch {
|
|
9767
|
+
} finally {
|
|
9768
|
+
db.close();
|
|
9769
|
+
}
|
|
9770
|
+
}
|
|
9771
|
+
return null;
|
|
9772
|
+
}
|
|
9773
|
+
_dbForSession = /* @__PURE__ */ new Map();
|
|
9774
|
+
/** Tests, and anything that adds a profile mid-process. */
|
|
9775
|
+
_clearDbRouting() {
|
|
9776
|
+
this._dbForSession.clear();
|
|
9777
|
+
}
|
|
9778
|
+
// ── ID handling ────────────────────────────────────────────────
|
|
9779
|
+
matchesId(id) {
|
|
9780
|
+
return id.startsWith(PREFIX5);
|
|
9781
|
+
}
|
|
9782
|
+
toRawId(id) {
|
|
9783
|
+
return id.startsWith(PREFIX5) ? id.slice(PREFIX5.length) : id;
|
|
9784
|
+
}
|
|
9785
|
+
toPrefixedId(rawId) {
|
|
9786
|
+
return rawId.startsWith(PREFIX5) ? rawId : PREFIX5 + rawId;
|
|
9787
|
+
}
|
|
9788
|
+
// ── Location ───────────────────────────────────────────────────
|
|
9789
|
+
findSession(id) {
|
|
9790
|
+
const rawId = this.toRawId(id);
|
|
9791
|
+
const path = this.dbPathFor(rawId);
|
|
9792
|
+
if (!path)
|
|
9793
|
+
return null;
|
|
9794
|
+
const db = openSqliteReadonly(path);
|
|
9795
|
+
if (!db)
|
|
9796
|
+
return null;
|
|
9797
|
+
try {
|
|
9798
|
+
const row = db.prepare(`
|
|
9799
|
+
SELECT s.cwd, s.git_repo_root, ${SESSION_MTIME_SQL} AS mtime
|
|
9800
|
+
FROM sessions s WHERE s.id = ?
|
|
9801
|
+
`).get(rawId);
|
|
9802
|
+
if (!row)
|
|
9803
|
+
return null;
|
|
9804
|
+
return {
|
|
9805
|
+
path,
|
|
9806
|
+
format: "sqlite",
|
|
9807
|
+
projectDir: "",
|
|
9808
|
+
projectPath: row.git_repo_root || row.cwd || "",
|
|
9809
|
+
mtime: Math.round((row.mtime || 0) * 1e3)
|
|
9810
|
+
};
|
|
9811
|
+
} catch {
|
|
9812
|
+
return null;
|
|
9813
|
+
} finally {
|
|
9814
|
+
db.close();
|
|
9815
|
+
}
|
|
9816
|
+
}
|
|
9817
|
+
listSessions(opts = {}) {
|
|
9818
|
+
const byId = /* @__PURE__ */ new Map();
|
|
9819
|
+
for (const dbPath of this.dbPaths()) {
|
|
9820
|
+
if (!existsSync10(dbPath))
|
|
9821
|
+
continue;
|
|
9822
|
+
for (const ref of this.listSessionsInDb(dbPath, opts)) {
|
|
9823
|
+
const prior = byId.get(ref.rawId);
|
|
9824
|
+
if (!prior || ref.mtime > prior.mtime)
|
|
9825
|
+
byId.set(ref.rawId, ref);
|
|
9826
|
+
}
|
|
9827
|
+
}
|
|
9828
|
+
const all = [...byId.values()].sort((a, b) => b.mtime - a.mtime);
|
|
9829
|
+
return opts.limit ? all.slice(0, opts.limit) : all;
|
|
9830
|
+
}
|
|
9831
|
+
listSessionsInDb(dbPath, opts) {
|
|
9832
|
+
const cutoffSec = (opts.sinceMs ?? 0) / 1e3;
|
|
9833
|
+
const filter = opts.projectFilter?.toLowerCase();
|
|
9834
|
+
const db = openSqliteReadonly(dbPath);
|
|
9835
|
+
if (!db)
|
|
9836
|
+
return [];
|
|
9837
|
+
try {
|
|
9838
|
+
const rows = db.prepare(`
|
|
9839
|
+
SELECT * FROM (
|
|
9840
|
+
SELECT s.id, s.cwd, s.git_repo_root, s.started_at, s.message_count,
|
|
9841
|
+
${SESSION_MTIME_SQL} AS mtime
|
|
9842
|
+
FROM sessions s
|
|
9843
|
+
) WHERE mtime >= ? ORDER BY mtime DESC
|
|
9844
|
+
`).all(cutoffSec);
|
|
9845
|
+
const out = [];
|
|
9846
|
+
for (const row of rows) {
|
|
9847
|
+
const projectPath = row.git_repo_root || row.cwd || "";
|
|
9848
|
+
if (filter && !projectPath.toLowerCase().includes(filter))
|
|
9849
|
+
continue;
|
|
9850
|
+
const mtime = Math.round(row.mtime * 1e3);
|
|
9851
|
+
this._dbForSession.set(row.id, dbPath);
|
|
9852
|
+
out.push({
|
|
9853
|
+
toolId: "hermes",
|
|
9854
|
+
rawId: row.id,
|
|
9855
|
+
prefixedId: this.toPrefixedId(row.id),
|
|
9856
|
+
projectPath,
|
|
9857
|
+
projectDir: "",
|
|
9858
|
+
fullPath: dbPath,
|
|
9859
|
+
created: new Date(Math.round(row.started_at * 1e3)).toISOString(),
|
|
9860
|
+
modified: new Date(mtime).toISOString(),
|
|
9861
|
+
mtime,
|
|
9862
|
+
firstPrompt: opts.previews === false ? "" : firstUserPrompt(db, row.id),
|
|
9863
|
+
messageCount: opts.previews === false ? 0 : row.message_count ?? 0
|
|
9864
|
+
});
|
|
9865
|
+
if (opts.limit && out.length >= opts.limit)
|
|
9866
|
+
break;
|
|
9867
|
+
}
|
|
9868
|
+
return out;
|
|
9869
|
+
} catch {
|
|
9870
|
+
return [];
|
|
9871
|
+
} finally {
|
|
9872
|
+
db.close();
|
|
9873
|
+
}
|
|
9874
|
+
}
|
|
9875
|
+
/** Hermes writes a title for each session (a model-written one, or the
|
|
9876
|
+
* first prompt for a one-shot run). */
|
|
9877
|
+
getNativeTitle(rawId) {
|
|
9878
|
+
const path = this.dbPathFor(this.toRawId(rawId));
|
|
9879
|
+
if (!path)
|
|
9880
|
+
return null;
|
|
9881
|
+
const db = openSqliteReadonly(path);
|
|
9882
|
+
if (!db)
|
|
9883
|
+
return null;
|
|
9884
|
+
try {
|
|
9885
|
+
const row = db.prepare("SELECT title FROM sessions WHERE id = ?").get(this.toRawId(rawId));
|
|
9886
|
+
const t = row?.title?.trim();
|
|
9887
|
+
return t ? t.slice(0, 200) : null;
|
|
9888
|
+
} catch {
|
|
9889
|
+
return null;
|
|
9890
|
+
} finally {
|
|
9891
|
+
db.close();
|
|
9892
|
+
}
|
|
9893
|
+
}
|
|
9894
|
+
// ── Generic-engine inputs ───────────────────────────────────────
|
|
9895
|
+
fileToolMap = {
|
|
9896
|
+
write_file: "write",
|
|
9897
|
+
patch: "edit",
|
|
9898
|
+
read_file: "read"
|
|
9899
|
+
};
|
|
9900
|
+
/**
|
|
9901
|
+
* `write_file` carries `{ path, content }`. `patch` carries
|
|
9902
|
+
* `{ mode: 'replace', path, old_string, new_string }`; its other mode,
|
|
9903
|
+
* `patch`, is a multi-file V4A patch with no single before and after.
|
|
9904
|
+
*/
|
|
9905
|
+
extractEditDelta(toolName, input) {
|
|
9906
|
+
if (input == null || typeof input !== "object")
|
|
9907
|
+
return null;
|
|
9908
|
+
const inp = input;
|
|
9909
|
+
if (toolName === "write_file") {
|
|
9910
|
+
return { before: "", after: typeof inp.content === "string" ? inp.content : null };
|
|
9911
|
+
}
|
|
9912
|
+
if (toolName === "patch" && (inp.mode === void 0 || inp.mode === "replace")) {
|
|
9913
|
+
const before = typeof inp.old_string === "string" ? inp.old_string : null;
|
|
9914
|
+
const after = typeof inp.new_string === "string" ? inp.new_string : null;
|
|
9915
|
+
if (before === null && after === null)
|
|
9916
|
+
return null;
|
|
9917
|
+
return { before, after };
|
|
9918
|
+
}
|
|
9919
|
+
return null;
|
|
9920
|
+
}
|
|
9921
|
+
readEvents(rawId) {
|
|
9922
|
+
const id = this.toRawId(rawId);
|
|
9923
|
+
const path = this.dbPathFor(id);
|
|
9924
|
+
if (!path)
|
|
9925
|
+
return [];
|
|
9926
|
+
const db = openSqliteReadonly(path);
|
|
9927
|
+
if (!db)
|
|
9928
|
+
return [];
|
|
9929
|
+
try {
|
|
9930
|
+
const rows = db.prepare(`SELECT ${MESSAGE_COLUMNS} FROM messages WHERE session_id = ? ORDER BY id`).all(id);
|
|
9931
|
+
return messageRowsToEvents(rows);
|
|
9932
|
+
} catch {
|
|
9933
|
+
return [];
|
|
9934
|
+
} finally {
|
|
9935
|
+
db.close();
|
|
9936
|
+
}
|
|
9937
|
+
}
|
|
9938
|
+
/**
|
|
9939
|
+
* Events from the dump `exportRawSession` writes, for a session rebuilt from
|
|
9940
|
+
* the server's archive. The archive joins the lines of every dump it got, so
|
|
9941
|
+
* a row that changed (a rewind sets `active` to 0) is there more than once.
|
|
9942
|
+
* The last copy of each row id is the newest.
|
|
9943
|
+
*/
|
|
9944
|
+
readEventsFromText(text) {
|
|
9945
|
+
const rows = /* @__PURE__ */ new Map();
|
|
9946
|
+
for (const line of text.split("\n")) {
|
|
9947
|
+
if (!line.trim())
|
|
9948
|
+
continue;
|
|
9949
|
+
try {
|
|
9950
|
+
const rec = JSON.parse(line);
|
|
9951
|
+
if (rec.kind === "message" && rec.row) {
|
|
9952
|
+
rows.delete(rec.row.id);
|
|
9953
|
+
rows.set(rec.row.id, rec.row);
|
|
9954
|
+
}
|
|
9955
|
+
} catch {
|
|
9956
|
+
}
|
|
9957
|
+
}
|
|
9958
|
+
return messageRowsToEvents([...rows.values()].sort((a, b) => a.id - b.id));
|
|
9959
|
+
}
|
|
9960
|
+
// ── Per-session operations — all delegate to the generic engine ─
|
|
9961
|
+
extractTurns(id, opts = {}) {
|
|
9962
|
+
return extractTurnsFromEvents(this.toPrefixedId(id), this.readEvents(id), opts);
|
|
9963
|
+
}
|
|
9964
|
+
liveScanEdits(id) {
|
|
9965
|
+
const located = this.findSession(id);
|
|
9966
|
+
if (!located) {
|
|
9967
|
+
return { found: false, projectPath: "", projectDir: "", edits: [], fileMtime: 0, tool: "hermes" };
|
|
9968
|
+
}
|
|
9969
|
+
return liveScanEditsFromEvents(this.readEvents(id), this.fileToolMap, {
|
|
9970
|
+
sessionId: this.toPrefixedId(id),
|
|
9971
|
+
tool: "hermes",
|
|
9972
|
+
projectPath: located.projectPath,
|
|
9973
|
+
projectDir: located.projectDir,
|
|
9974
|
+
fileMtime: located.mtime,
|
|
9975
|
+
found: true
|
|
9976
|
+
});
|
|
9977
|
+
}
|
|
9978
|
+
replay(id) {
|
|
9979
|
+
const located = this.findSession(id);
|
|
9980
|
+
if (!located) {
|
|
9981
|
+
return { sessionId: this.toPrefixedId(id), found: false, projectPath: "", files: [], totalLinesAdded: 0, totalLinesRemoved: 0 };
|
|
9982
|
+
}
|
|
9983
|
+
return replayFromEvents(this.toPrefixedId(id), this.readEvents(id), this.fileToolMap, this.extractEditDelta.bind(this), {
|
|
9984
|
+
projectPath: located.projectPath,
|
|
9985
|
+
found: true
|
|
9986
|
+
});
|
|
9987
|
+
}
|
|
9988
|
+
computeOutcome(id, opts) {
|
|
9989
|
+
return computeOutcome(this.toPrefixedId(id), opts);
|
|
9990
|
+
}
|
|
9991
|
+
/** File edits since `sinceMs` across the sessions active since then. */
|
|
9992
|
+
collectRecentEdits(opts) {
|
|
9993
|
+
const refs = this.listSessions({ sinceMs: opts.sinceMs, projectFilter: opts.projectFilter, previews: false });
|
|
9994
|
+
const edits = [];
|
|
9995
|
+
for (const ref of opts.limitSessions ? refs.slice(0, opts.limitSessions) : refs) {
|
|
9996
|
+
for (const e of this.liveScanEdits(ref.rawId).edits) {
|
|
9997
|
+
if (e.ts >= opts.sinceMs)
|
|
9998
|
+
edits.push(e);
|
|
9999
|
+
}
|
|
10000
|
+
}
|
|
10001
|
+
return edits.sort((a, b) => b.ts - a.ts);
|
|
10002
|
+
}
|
|
10003
|
+
getCommits(id, files, startMs, endMs, bufferMinutes) {
|
|
10004
|
+
return getSessionCommits(this.toPrefixedId(id), files, startMs, endMs, bufferMinutes);
|
|
10005
|
+
}
|
|
10006
|
+
/**
|
|
10007
|
+
* The session row and its messages as JSONL, one row per line in id order.
|
|
10008
|
+
* Hermes rewrites and compacts its database, so the archive keeps the rows
|
|
10009
|
+
* as they were synced.
|
|
10010
|
+
*/
|
|
10011
|
+
exportRawSession(id) {
|
|
10012
|
+
const rawId = this.toRawId(id);
|
|
10013
|
+
const path = this.dbPathFor(rawId);
|
|
10014
|
+
if (!path)
|
|
10015
|
+
return null;
|
|
10016
|
+
const db = openSqliteReadonly(path);
|
|
10017
|
+
if (!db)
|
|
10018
|
+
return null;
|
|
10019
|
+
try {
|
|
10020
|
+
const have = new Set(db.prepare(`SELECT name FROM pragma_table_info('sessions')`).all().map((c) => c.name));
|
|
10021
|
+
const cols = ARCHIVED_SESSION_COLUMNS.filter((c) => have.has(c)).map((c) => `s.${c}`);
|
|
10022
|
+
const session = db.prepare(`
|
|
10023
|
+
SELECT ${[...cols, `${SESSION_MTIME_SQL} AS mtime`].join(", ")}
|
|
10024
|
+
FROM sessions s WHERE s.id = ?
|
|
10025
|
+
`).get(rawId);
|
|
10026
|
+
if (!session)
|
|
10027
|
+
return null;
|
|
10028
|
+
const rows = db.prepare(`SELECT ${MESSAGE_COLUMNS} FROM messages WHERE session_id = ? ORDER BY id`).all(rawId);
|
|
10029
|
+
const lines = [JSON.stringify({ kind: "session", row: session })];
|
|
10030
|
+
for (const r of rows)
|
|
10031
|
+
lines.push(JSON.stringify({ kind: "message", row: r }));
|
|
10032
|
+
return {
|
|
10033
|
+
tool: "hermes",
|
|
10034
|
+
mtime: Math.round(Number(session.mtime || 0) * 1e3),
|
|
10035
|
+
files: [{ name: `${rawId}.dump.jsonl`, bytes: Buffer.from(lines.join("\n") + "\n", "utf-8") }]
|
|
10036
|
+
};
|
|
10037
|
+
} catch {
|
|
10038
|
+
return null;
|
|
10039
|
+
} finally {
|
|
10040
|
+
db.close();
|
|
10041
|
+
}
|
|
10042
|
+
}
|
|
10043
|
+
};
|
|
10044
|
+
hermesBackend = new HermesBackend();
|
|
10045
|
+
}
|
|
10046
|
+
});
|
|
10047
|
+
|
|
9482
10048
|
// ../engine/dist/core/backends/index.js
|
|
9483
10049
|
function bootstrapBackends() {
|
|
9484
10050
|
registerBackend(claudeBackend);
|
|
@@ -9486,6 +10052,7 @@ function bootstrapBackends() {
|
|
|
9486
10052
|
registerBackend(codexBackend);
|
|
9487
10053
|
registerBackend(agyBackend);
|
|
9488
10054
|
registerBackend(cursorBackend);
|
|
10055
|
+
registerBackend(hermesBackend);
|
|
9489
10056
|
}
|
|
9490
10057
|
var init_backends = __esm({
|
|
9491
10058
|
"../engine/dist/core/backends/index.js"() {
|
|
@@ -9496,6 +10063,7 @@ var init_backends = __esm({
|
|
|
9496
10063
|
init_codex2();
|
|
9497
10064
|
init_agy();
|
|
9498
10065
|
init_cursor();
|
|
10066
|
+
init_hermes();
|
|
9499
10067
|
init_tool_backend();
|
|
9500
10068
|
_setRegistryBootstrapper(bootstrapBackends);
|
|
9501
10069
|
}
|
|
@@ -9708,7 +10276,7 @@ var init_session_outcome = __esm({
|
|
|
9708
10276
|
});
|
|
9709
10277
|
|
|
9710
10278
|
// ../engine/dist/core/first-prompt.js
|
|
9711
|
-
import { createReadStream, existsSync as
|
|
10279
|
+
import { createReadStream, existsSync as existsSync11, openSync as openSync3, readSync as readSync3, closeSync as closeSync3 } from "fs";
|
|
9712
10280
|
import { StringDecoder } from "string_decoder";
|
|
9713
10281
|
function stripBanners(s) {
|
|
9714
10282
|
let out = s;
|
|
@@ -9877,7 +10445,7 @@ var init_project_dir_name = __esm({
|
|
|
9877
10445
|
});
|
|
9878
10446
|
|
|
9879
10447
|
// ../engine/dist/core/backends/claude.js
|
|
9880
|
-
import { existsSync as
|
|
10448
|
+
import { existsSync as existsSync12, readdirSync as readdirSync8, readFileSync as readFileSync7, statSync as statSync9, openSync as openSync4, readSync as readSync4, closeSync as closeSync4 } from "fs";
|
|
9881
10449
|
import { join as join11, basename as basename6 } from "path";
|
|
9882
10450
|
function stripReminders(text) {
|
|
9883
10451
|
return text.replace(/<system-reminder>[\s\S]*?<\/system-reminder>/g, "").trim();
|
|
@@ -9947,7 +10515,7 @@ var init_claude2 = __esm({
|
|
|
9947
10515
|
return join11(this.homeDir(), "history.jsonl");
|
|
9948
10516
|
}
|
|
9949
10517
|
isAvailable() {
|
|
9950
|
-
return
|
|
10518
|
+
return existsSync12(this.projectsDir());
|
|
9951
10519
|
}
|
|
9952
10520
|
// ── ID handling ────────────────────────────────────────────────
|
|
9953
10521
|
/**
|
|
@@ -10053,7 +10621,7 @@ var init_claude2 = __esm({
|
|
|
10053
10621
|
prior.modified = ref.modified;
|
|
10054
10622
|
}
|
|
10055
10623
|
};
|
|
10056
|
-
if (!
|
|
10624
|
+
if (!existsSync12(root))
|
|
10057
10625
|
return;
|
|
10058
10626
|
for (const proj of readdirSync8(root, { withFileTypes: true })) {
|
|
10059
10627
|
if (!proj.isDirectory())
|
|
@@ -10064,12 +10632,12 @@ var init_claude2 = __esm({
|
|
|
10064
10632
|
const projectPath = resolveProjectDirName(proj.name);
|
|
10065
10633
|
const indexPath = join11(projPath, "sessions-index.json");
|
|
10066
10634
|
const indexed = /* @__PURE__ */ new Set();
|
|
10067
|
-
if (
|
|
10635
|
+
if (existsSync12(indexPath)) {
|
|
10068
10636
|
try {
|
|
10069
10637
|
const indexData = JSON.parse(readFileSync7(indexPath, "utf-8"));
|
|
10070
10638
|
const entries = indexData.entries || [];
|
|
10071
10639
|
for (const entry of entries) {
|
|
10072
|
-
if (!entry.fullPath || !
|
|
10640
|
+
if (!entry.fullPath || !existsSync12(entry.fullPath))
|
|
10073
10641
|
continue;
|
|
10074
10642
|
indexed.add(entry.sessionId);
|
|
10075
10643
|
add({
|
|
@@ -10394,7 +10962,7 @@ var init_claude2 = __esm({
|
|
|
10394
10962
|
collectRecentEdits(opts) {
|
|
10395
10963
|
const candidates = [];
|
|
10396
10964
|
for (const root of claudeProjectDirs()) {
|
|
10397
|
-
if (!
|
|
10965
|
+
if (!existsSync12(root))
|
|
10398
10966
|
continue;
|
|
10399
10967
|
for (const projEntry of readdirSync8(root, { withFileTypes: true })) {
|
|
10400
10968
|
if (!projEntry.isDirectory())
|
|
@@ -10416,7 +10984,7 @@ var init_claude2 = __esm({
|
|
|
10416
10984
|
const st = statSync9(fp);
|
|
10417
10985
|
let mtime = st.mtimeMs;
|
|
10418
10986
|
const subDir = join11(projPath, f.slice(0, -6), "subagents");
|
|
10419
|
-
if (
|
|
10987
|
+
if (existsSync12(subDir)) {
|
|
10420
10988
|
for (const sub of readdirSync8(subDir)) {
|
|
10421
10989
|
try {
|
|
10422
10990
|
const subSt = statSync9(join11(subDir, sub));
|
|
@@ -10493,27 +11061,45 @@ var init_claude2 = __esm({
|
|
|
10493
11061
|
/**
|
|
10494
11062
|
* True when this session exists in MORE THAN ONE home.
|
|
10495
11063
|
*
|
|
10496
|
-
* Such a session cannot be tail-appended: `fileSize()` is the SUM across
|
|
10497
|
-
* copies while a byte offset addresses ONE file, so the two disagree by
|
|
10498
|
-
* construction and the "tail" is either empty or the wrong bytes. The caller
|
|
10499
|
-
* uses this to force a FULL sync, which ships the unioned container and is
|
|
10500
|
-
* correct regardless of how the halves are arranged.
|
|
10501
|
-
*
|
|
10502
11064
|
* Measured on ec05f266: ~/.claude 1715 records, ~/.claude-work 205 records,
|
|
10503
|
-
* zero overlap.
|
|
10504
|
-
*
|
|
11065
|
+
* zero overlap. `fileSize()` is the sum across the copies, and
|
|
11066
|
+
* `readFromOffset()` reads them joined end to end, primary first.
|
|
10505
11067
|
*/
|
|
10506
11068
|
spansMultipleSources(prefixedId) {
|
|
10507
11069
|
return findSessionFiles(this.toRawId(prefixedId)).length > 1;
|
|
10508
11070
|
}
|
|
11071
|
+
/**
|
|
11072
|
+
* The copies before the last one, each with its size. Claude Code writes to
|
|
11073
|
+
* the home of the profile that runs the session, so the live copy can grow,
|
|
11074
|
+
* appear, vanish or merge back into the primary. Each of those moves bytes
|
|
11075
|
+
* under an offset into the joined copies; this key changes with all of them.
|
|
11076
|
+
*/
|
|
11077
|
+
sourceLayout(prefixedId) {
|
|
11078
|
+
const copies = findSessionFiles(this.toRawId(prefixedId));
|
|
11079
|
+
if (copies.length < 2)
|
|
11080
|
+
return "";
|
|
11081
|
+
return copies.slice(0, -1).map((c) => {
|
|
11082
|
+
let size = -1;
|
|
11083
|
+
try {
|
|
11084
|
+
size = statSync9(c.path).size;
|
|
11085
|
+
} catch {
|
|
11086
|
+
}
|
|
11087
|
+
return `${c.path}:${size}`;
|
|
11088
|
+
}).join("|");
|
|
11089
|
+
}
|
|
11090
|
+
/**
|
|
11091
|
+
* Session 8de13d0a had 31 MB in ~/.claude and 2 MB in ~/.claude-work. Over
|
|
11092
|
+
* the 24 MB build ceiling a sync ships the transcript in chunks read here,
|
|
11093
|
+
* and this returned no text for a session in two homes. Each sync shipped
|
|
11094
|
+
* nothing, and the server stayed three hours behind.
|
|
11095
|
+
*/
|
|
10509
11096
|
async readFromOffset(prefixedId, offset, maxBytes) {
|
|
10510
|
-
const
|
|
10511
|
-
const copies = findSessionFiles(rawId);
|
|
11097
|
+
const copies = findSessionFiles(this.toRawId(prefixedId));
|
|
10512
11098
|
if (copies.length === 0)
|
|
10513
11099
|
return { text: "", newOffset: offset };
|
|
10514
|
-
if (copies.length
|
|
10515
|
-
return
|
|
10516
|
-
return
|
|
11100
|
+
if (copies.length === 1)
|
|
11101
|
+
return readTailFromOffset(copies[0].path, offset, maxBytes);
|
|
11102
|
+
return readSourcesFromOffset(copies.map((c) => c.path), offset, maxBytes);
|
|
10517
11103
|
}
|
|
10518
11104
|
};
|
|
10519
11105
|
claudeBackend = new ClaudeBackend();
|
|
@@ -10528,11 +11114,11 @@ var init_model_pricing = __esm({
|
|
|
10528
11114
|
});
|
|
10529
11115
|
|
|
10530
11116
|
// ../engine/dist/parsers/session.js
|
|
10531
|
-
import { createReadStream as createReadStream2, existsSync as
|
|
11117
|
+
import { createReadStream as createReadStream2, existsSync as existsSync13, readdirSync as readdirSync9, readFileSync as readFileSync8, statSync as statSync10, openSync as openSync5, readSync as readSync5, closeSync as closeSync5 } from "fs";
|
|
10532
11118
|
import { join as join12, basename as basename7 } from "path";
|
|
10533
11119
|
function hasSubagentsDir(sessionPath) {
|
|
10534
11120
|
const dir = sessionPath.slice(0, -6);
|
|
10535
|
-
return
|
|
11121
|
+
return existsSync13(join12(dir, "subagents"));
|
|
10536
11122
|
}
|
|
10537
11123
|
var init_session = __esm({
|
|
10538
11124
|
"../engine/dist/parsers/session.js"() {
|
|
@@ -10631,7 +11217,7 @@ __export(live_session_scan_exports, {
|
|
|
10631
11217
|
withSessionReadCache: () => withSessionReadCache,
|
|
10632
11218
|
withSessionScanScope: () => withSessionScanScope
|
|
10633
11219
|
});
|
|
10634
|
-
import { existsSync as
|
|
11220
|
+
import { existsSync as existsSync14, readdirSync as readdirSync10, readFileSync as readFileSync9, statSync as statSync11, openSync as openSync6, readSync as readSync6, closeSync as closeSync6 } from "fs";
|
|
10635
11221
|
import { join as join13, basename as basename8 } from "path";
|
|
10636
11222
|
import { createHash as createHash2 } from "crypto";
|
|
10637
11223
|
function lazyGeminiProjectsJson() {
|
|
@@ -10692,7 +11278,7 @@ function sessionFileIndex() {
|
|
|
10692
11278
|
return p;
|
|
10693
11279
|
};
|
|
10694
11280
|
for (const root of roots) {
|
|
10695
|
-
if (seenRoots.has(root) || !
|
|
11281
|
+
if (seenRoots.has(root) || !existsSync14(root))
|
|
10696
11282
|
continue;
|
|
10697
11283
|
seenRoots.add(root);
|
|
10698
11284
|
dirs.set(root, dirMtime(root));
|
|
@@ -10818,7 +11404,7 @@ function resolveSessionContentPaths(sessionFile) {
|
|
|
10818
11404
|
if (!hasSubagentsDir(sessionFile))
|
|
10819
11405
|
return [sessionFile];
|
|
10820
11406
|
const subDir = join13(sessionFile.slice(0, -6), "subagents");
|
|
10821
|
-
if (!
|
|
11407
|
+
if (!existsSync14(subDir))
|
|
10822
11408
|
return [sessionFile];
|
|
10823
11409
|
const subPaths = readdirSync10(subDir).filter((f) => f.endsWith(".jsonl")).sort().map((f) => join13(subDir, f));
|
|
10824
11410
|
return [sessionFile, ...subPaths];
|
|
@@ -10835,7 +11421,7 @@ function loadGeminiProjectMap() {
|
|
|
10835
11421
|
return geminiProjectMapCache;
|
|
10836
11422
|
const map = /* @__PURE__ */ new Map();
|
|
10837
11423
|
const projectsPath = lazyGeminiProjectsJson();
|
|
10838
|
-
if (
|
|
11424
|
+
if (existsSync14(projectsPath)) {
|
|
10839
11425
|
try {
|
|
10840
11426
|
const data = JSON.parse(readFileSync9(projectsPath, "utf-8"));
|
|
10841
11427
|
for (const path of Object.keys(data.projects || {})) {
|
|
@@ -10871,7 +11457,7 @@ function geminiIndex() {
|
|
|
10871
11457
|
const projMap = loadGeminiProjectMap();
|
|
10872
11458
|
const isGeminiChat = (f) => f.startsWith("session-") && (f.endsWith(".json") || f.endsWith(".jsonl"));
|
|
10873
11459
|
for (const tmpRoot of geminiTmpDirs()) {
|
|
10874
|
-
if (!
|
|
11460
|
+
if (!existsSync14(tmpRoot))
|
|
10875
11461
|
continue;
|
|
10876
11462
|
let projs;
|
|
10877
11463
|
try {
|
|
@@ -10883,7 +11469,7 @@ function geminiIndex() {
|
|
|
10883
11469
|
if (!proj.isDirectory())
|
|
10884
11470
|
continue;
|
|
10885
11471
|
const chats = join13(tmpRoot, proj.name, "chats");
|
|
10886
|
-
if (!
|
|
11472
|
+
if (!existsSync14(chats))
|
|
10887
11473
|
continue;
|
|
10888
11474
|
let files;
|
|
10889
11475
|
try {
|
|
@@ -11004,7 +11590,7 @@ function findCodexSessionFile(sessionId) {
|
|
|
11004
11590
|
return null;
|
|
11005
11591
|
}
|
|
11006
11592
|
function findCodexSessionFileIn(root, sessionId) {
|
|
11007
|
-
if (!
|
|
11593
|
+
if (!existsSync14(root))
|
|
11008
11594
|
return null;
|
|
11009
11595
|
for (const year of readdirSync10(root, { withFileTypes: true })) {
|
|
11010
11596
|
if (!year.isDirectory())
|