chat-recall 0.7.18 → 0.7.20
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 +15848 -6647
- package/dist/cli.js.map +1 -1
- package/dist/install-skills.d.ts.map +1 -1
- package/dist/install-skills.js +2 -0
- package/dist/install-skills.js.map +1 -1
- package/dist/intent-drain.d.ts +7 -1
- package/dist/intent-drain.d.ts.map +1 -1
- package/dist/intent-drain.js +19 -3
- package/dist/intent-drain.js.map +1 -1
- package/dist/mcp-relay.js +1 -1
- package/dist/mcp.js +17152 -8293
- package/dist/scan-worker.js +1644 -333
- package/dist/sync-client.d.ts.map +1 -1
- package/dist/sync-client.js +33 -13
- package/dist/sync-client.js.map +1 -1
- package/dist/sync-ledger.d.ts +6 -0
- package/dist/sync-ledger.d.ts.map +1 -1
- package/dist/sync-ledger.js +3 -0
- package/dist/sync-ledger.js.map +1 -1
- package/dist/watch.js +14059 -4933
- package/package.json +1 -1
package/dist/scan-worker.js
CHANGED
|
@@ -2040,7 +2040,7 @@ var require_thread_stream = __commonJS({
|
|
|
2040
2040
|
var { version } = require_package();
|
|
2041
2041
|
var { EventEmitter } = __require("events");
|
|
2042
2042
|
var { Worker } = __require("worker_threads");
|
|
2043
|
-
var { join:
|
|
2043
|
+
var { join: join15 } = __require("path");
|
|
2044
2044
|
var { pathToFileURL } = __require("url");
|
|
2045
2045
|
var { wait } = require_wait();
|
|
2046
2046
|
var {
|
|
@@ -2083,7 +2083,7 @@ var require_thread_stream = __commonJS({
|
|
|
2083
2083
|
function createWorker(stream, opts) {
|
|
2084
2084
|
const { filename, workerData } = opts;
|
|
2085
2085
|
const bundlerOverrides = "__bundlerPathsOverrides" in globalThis ? globalThis.__bundlerPathsOverrides : {};
|
|
2086
|
-
const toExecute = bundlerOverrides["thread-stream-worker"] ||
|
|
2086
|
+
const toExecute = bundlerOverrides["thread-stream-worker"] || join15(__dirname, "lib", "worker.js");
|
|
2087
2087
|
const worker = new Worker(toExecute, {
|
|
2088
2088
|
...opts.workerOpts,
|
|
2089
2089
|
trackUnmanagedFds: false,
|
|
@@ -2486,7 +2486,7 @@ var require_transport = __commonJS({
|
|
|
2486
2486
|
"use strict";
|
|
2487
2487
|
var { createRequire: createRequire3 } = __require("module");
|
|
2488
2488
|
var getCallers = require_caller();
|
|
2489
|
-
var { join:
|
|
2489
|
+
var { join: join15, isAbsolute: isAbsolute2, sep } = __require("node:path");
|
|
2490
2490
|
var sleep = require_atomic_sleep();
|
|
2491
2491
|
var onExit = require_on_exit_leak_free();
|
|
2492
2492
|
var ThreadStream = require_thread_stream();
|
|
@@ -2549,7 +2549,7 @@ var require_transport = __commonJS({
|
|
|
2549
2549
|
throw new Error("only one of target or targets can be specified");
|
|
2550
2550
|
}
|
|
2551
2551
|
if (targets) {
|
|
2552
|
-
target = bundlerOverrides["pino-worker"] ||
|
|
2552
|
+
target = bundlerOverrides["pino-worker"] || join15(__dirname, "worker.js");
|
|
2553
2553
|
options.targets = targets.filter((dest) => dest.target).map((dest) => {
|
|
2554
2554
|
return {
|
|
2555
2555
|
...dest,
|
|
@@ -2567,7 +2567,7 @@ var require_transport = __commonJS({
|
|
|
2567
2567
|
});
|
|
2568
2568
|
});
|
|
2569
2569
|
} else if (pipeline) {
|
|
2570
|
-
target = bundlerOverrides["pino-worker"] ||
|
|
2570
|
+
target = bundlerOverrides["pino-worker"] || join15(__dirname, "worker.js");
|
|
2571
2571
|
options.pipelines = [pipeline.map((dest) => {
|
|
2572
2572
|
return {
|
|
2573
2573
|
...dest,
|
|
@@ -2585,11 +2585,11 @@ var require_transport = __commonJS({
|
|
|
2585
2585
|
return buildStream(fixTarget(target), options, worker, sync);
|
|
2586
2586
|
function fixTarget(origin) {
|
|
2587
2587
|
origin = bundlerOverrides[origin] || origin;
|
|
2588
|
-
if (
|
|
2588
|
+
if (isAbsolute2(origin) || origin.indexOf("file://") === 0) {
|
|
2589
2589
|
return origin;
|
|
2590
2590
|
}
|
|
2591
2591
|
if (origin === "pino/file") {
|
|
2592
|
-
return
|
|
2592
|
+
return join15(__dirname, "..", "file.js");
|
|
2593
2593
|
}
|
|
2594
2594
|
let fixTarget2;
|
|
2595
2595
|
for (const filePath of callers) {
|
|
@@ -3578,7 +3578,7 @@ var require_safe_stable_stringify = __commonJS({
|
|
|
3578
3578
|
return circularValue;
|
|
3579
3579
|
}
|
|
3580
3580
|
let res = "";
|
|
3581
|
-
let
|
|
3581
|
+
let join15 = ",";
|
|
3582
3582
|
const originalIndentation = indentation;
|
|
3583
3583
|
if (Array.isArray(value)) {
|
|
3584
3584
|
if (value.length === 0) {
|
|
@@ -3592,7 +3592,7 @@ var require_safe_stable_stringify = __commonJS({
|
|
|
3592
3592
|
indentation += spacer;
|
|
3593
3593
|
res += `
|
|
3594
3594
|
${indentation}`;
|
|
3595
|
-
|
|
3595
|
+
join15 = `,
|
|
3596
3596
|
${indentation}`;
|
|
3597
3597
|
}
|
|
3598
3598
|
const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
|
|
@@ -3600,13 +3600,13 @@ ${indentation}`;
|
|
|
3600
3600
|
for (; i < maximumValuesToStringify - 1; i++) {
|
|
3601
3601
|
const tmp2 = stringifyFnReplacer(String(i), value, stack, replacer, spacer, indentation);
|
|
3602
3602
|
res += tmp2 !== void 0 ? tmp2 : "null";
|
|
3603
|
-
res +=
|
|
3603
|
+
res += join15;
|
|
3604
3604
|
}
|
|
3605
3605
|
const tmp = stringifyFnReplacer(String(i), value, stack, replacer, spacer, indentation);
|
|
3606
3606
|
res += tmp !== void 0 ? tmp : "null";
|
|
3607
3607
|
if (value.length - 1 > maximumBreadth) {
|
|
3608
3608
|
const removedKeys = value.length - maximumBreadth - 1;
|
|
3609
|
-
res += `${
|
|
3609
|
+
res += `${join15}"... ${getItemCount(removedKeys)} not stringified"`;
|
|
3610
3610
|
}
|
|
3611
3611
|
if (spacer !== "") {
|
|
3612
3612
|
res += `
|
|
@@ -3627,7 +3627,7 @@ ${originalIndentation}`;
|
|
|
3627
3627
|
let separator = "";
|
|
3628
3628
|
if (spacer !== "") {
|
|
3629
3629
|
indentation += spacer;
|
|
3630
|
-
|
|
3630
|
+
join15 = `,
|
|
3631
3631
|
${indentation}`;
|
|
3632
3632
|
whitespace = " ";
|
|
3633
3633
|
}
|
|
@@ -3641,13 +3641,13 @@ ${indentation}`;
|
|
|
3641
3641
|
const tmp = stringifyFnReplacer(key2, value, stack, replacer, spacer, indentation);
|
|
3642
3642
|
if (tmp !== void 0) {
|
|
3643
3643
|
res += `${separator}${strEscape(key2)}:${whitespace}${tmp}`;
|
|
3644
|
-
separator =
|
|
3644
|
+
separator = join15;
|
|
3645
3645
|
}
|
|
3646
3646
|
}
|
|
3647
3647
|
if (keyLength > maximumBreadth) {
|
|
3648
3648
|
const removedKeys = keyLength - maximumBreadth;
|
|
3649
3649
|
res += `${separator}"...":${whitespace}"${getItemCount(removedKeys)} not stringified"`;
|
|
3650
|
-
separator =
|
|
3650
|
+
separator = join15;
|
|
3651
3651
|
}
|
|
3652
3652
|
if (spacer !== "" && separator.length > 1) {
|
|
3653
3653
|
res = `
|
|
@@ -3688,7 +3688,7 @@ ${originalIndentation}`;
|
|
|
3688
3688
|
}
|
|
3689
3689
|
const originalIndentation = indentation;
|
|
3690
3690
|
let res = "";
|
|
3691
|
-
let
|
|
3691
|
+
let join15 = ",";
|
|
3692
3692
|
if (Array.isArray(value)) {
|
|
3693
3693
|
if (value.length === 0) {
|
|
3694
3694
|
return "[]";
|
|
@@ -3701,7 +3701,7 @@ ${originalIndentation}`;
|
|
|
3701
3701
|
indentation += spacer;
|
|
3702
3702
|
res += `
|
|
3703
3703
|
${indentation}`;
|
|
3704
|
-
|
|
3704
|
+
join15 = `,
|
|
3705
3705
|
${indentation}`;
|
|
3706
3706
|
}
|
|
3707
3707
|
const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
|
|
@@ -3709,13 +3709,13 @@ ${indentation}`;
|
|
|
3709
3709
|
for (; i < maximumValuesToStringify - 1; i++) {
|
|
3710
3710
|
const tmp2 = stringifyArrayReplacer(String(i), value[i], stack, replacer, spacer, indentation);
|
|
3711
3711
|
res += tmp2 !== void 0 ? tmp2 : "null";
|
|
3712
|
-
res +=
|
|
3712
|
+
res += join15;
|
|
3713
3713
|
}
|
|
3714
3714
|
const tmp = stringifyArrayReplacer(String(i), value[i], stack, replacer, spacer, indentation);
|
|
3715
3715
|
res += tmp !== void 0 ? tmp : "null";
|
|
3716
3716
|
if (value.length - 1 > maximumBreadth) {
|
|
3717
3717
|
const removedKeys = value.length - maximumBreadth - 1;
|
|
3718
|
-
res += `${
|
|
3718
|
+
res += `${join15}"... ${getItemCount(removedKeys)} not stringified"`;
|
|
3719
3719
|
}
|
|
3720
3720
|
if (spacer !== "") {
|
|
3721
3721
|
res += `
|
|
@@ -3728,7 +3728,7 @@ ${originalIndentation}`;
|
|
|
3728
3728
|
let whitespace = "";
|
|
3729
3729
|
if (spacer !== "") {
|
|
3730
3730
|
indentation += spacer;
|
|
3731
|
-
|
|
3731
|
+
join15 = `,
|
|
3732
3732
|
${indentation}`;
|
|
3733
3733
|
whitespace = " ";
|
|
3734
3734
|
}
|
|
@@ -3737,7 +3737,7 @@ ${indentation}`;
|
|
|
3737
3737
|
const tmp = stringifyArrayReplacer(key2, value[key2], stack, replacer, spacer, indentation);
|
|
3738
3738
|
if (tmp !== void 0) {
|
|
3739
3739
|
res += `${separator}${strEscape(key2)}:${whitespace}${tmp}`;
|
|
3740
|
-
separator =
|
|
3740
|
+
separator = join15;
|
|
3741
3741
|
}
|
|
3742
3742
|
}
|
|
3743
3743
|
if (spacer !== "" && separator.length > 1) {
|
|
@@ -3795,20 +3795,20 @@ ${originalIndentation}`;
|
|
|
3795
3795
|
indentation += spacer;
|
|
3796
3796
|
let res2 = `
|
|
3797
3797
|
${indentation}`;
|
|
3798
|
-
const
|
|
3798
|
+
const join16 = `,
|
|
3799
3799
|
${indentation}`;
|
|
3800
3800
|
const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
|
|
3801
3801
|
let i = 0;
|
|
3802
3802
|
for (; i < maximumValuesToStringify - 1; i++) {
|
|
3803
3803
|
const tmp2 = stringifyIndent(String(i), value[i], stack, spacer, indentation);
|
|
3804
3804
|
res2 += tmp2 !== void 0 ? tmp2 : "null";
|
|
3805
|
-
res2 +=
|
|
3805
|
+
res2 += join16;
|
|
3806
3806
|
}
|
|
3807
3807
|
const tmp = stringifyIndent(String(i), value[i], stack, spacer, indentation);
|
|
3808
3808
|
res2 += tmp !== void 0 ? tmp : "null";
|
|
3809
3809
|
if (value.length - 1 > maximumBreadth) {
|
|
3810
3810
|
const removedKeys = value.length - maximumBreadth - 1;
|
|
3811
|
-
res2 += `${
|
|
3811
|
+
res2 += `${join16}"... ${getItemCount(removedKeys)} not stringified"`;
|
|
3812
3812
|
}
|
|
3813
3813
|
res2 += `
|
|
3814
3814
|
${originalIndentation}`;
|
|
@@ -3824,16 +3824,16 @@ ${originalIndentation}`;
|
|
|
3824
3824
|
return '"[Object]"';
|
|
3825
3825
|
}
|
|
3826
3826
|
indentation += spacer;
|
|
3827
|
-
const
|
|
3827
|
+
const join15 = `,
|
|
3828
3828
|
${indentation}`;
|
|
3829
3829
|
let res = "";
|
|
3830
3830
|
let separator = "";
|
|
3831
3831
|
let maximumPropertiesToStringify = Math.min(keyLength, maximumBreadth);
|
|
3832
3832
|
if (isTypedArrayWithEntries(value)) {
|
|
3833
|
-
res += stringifyTypedArray(value,
|
|
3833
|
+
res += stringifyTypedArray(value, join15, maximumBreadth);
|
|
3834
3834
|
keys = keys.slice(value.length);
|
|
3835
3835
|
maximumPropertiesToStringify -= value.length;
|
|
3836
|
-
separator =
|
|
3836
|
+
separator = join15;
|
|
3837
3837
|
}
|
|
3838
3838
|
if (deterministic) {
|
|
3839
3839
|
keys = sort(keys, comparator);
|
|
@@ -3844,13 +3844,13 @@ ${indentation}`;
|
|
|
3844
3844
|
const tmp = stringifyIndent(key2, value[key2], stack, spacer, indentation);
|
|
3845
3845
|
if (tmp !== void 0) {
|
|
3846
3846
|
res += `${separator}${strEscape(key2)}: ${tmp}`;
|
|
3847
|
-
separator =
|
|
3847
|
+
separator = join15;
|
|
3848
3848
|
}
|
|
3849
3849
|
}
|
|
3850
3850
|
if (keyLength > maximumBreadth) {
|
|
3851
3851
|
const removedKeys = keyLength - maximumBreadth;
|
|
3852
3852
|
res += `${separator}"...": "${getItemCount(removedKeys)} not stringified"`;
|
|
3853
|
-
separator =
|
|
3853
|
+
separator = join15;
|
|
3854
3854
|
}
|
|
3855
3855
|
if (separator !== "") {
|
|
3856
3856
|
res = `
|
|
@@ -4524,6 +4524,8 @@ function defaultSourcesEnabled() {
|
|
|
4524
4524
|
commands: true
|
|
4525
4525
|
},
|
|
4526
4526
|
cursor: { sessions: true, skills: true, agents: true, commands: true },
|
|
4527
|
+
hermes: { sessions: true, skills: true },
|
|
4528
|
+
pi: { sessions: true, skills: true, commands: true },
|
|
4527
4529
|
shared: { skills: true },
|
|
4528
4530
|
common: { mcps: true, agentMd: true }
|
|
4529
4531
|
};
|
|
@@ -4594,6 +4596,8 @@ function mergeSources(base, partial) {
|
|
|
4594
4596
|
codex: { ...base.enabled.codex, ...partial.enabled?.codex ?? {} },
|
|
4595
4597
|
agy: { ...base.enabled.agy, ...inherited, ...partial.enabled?.agy ?? {} },
|
|
4596
4598
|
cursor: { ...base.enabled.cursor, ...partial.enabled?.cursor ?? {} },
|
|
4599
|
+
hermes: { ...base.enabled.hermes, ...partial.enabled?.hermes ?? {} },
|
|
4600
|
+
pi: { ...base.enabled.pi, ...partial.enabled?.pi ?? {} },
|
|
4597
4601
|
shared: { ...base.enabled.shared, ...partial.enabled?.shared ?? {} },
|
|
4598
4602
|
common: { ...base.enabled.common, ...partial.enabled?.common ?? {} }
|
|
4599
4603
|
};
|
|
@@ -4605,6 +4609,8 @@ function mergeSources(base, partial) {
|
|
|
4605
4609
|
agyHome: partial.agyHome ?? base.agyHome,
|
|
4606
4610
|
cursorHome: partial.cursorHome ?? base.cursorHome,
|
|
4607
4611
|
cursorIdeHome: partial.cursorIdeHome ?? base.cursorIdeHome,
|
|
4612
|
+
hermesHome: partial.hermesHome ?? base.hermesHome,
|
|
4613
|
+
piHome: partial.piHome ?? base.piHome,
|
|
4608
4614
|
extraClaudeHomes: partial.extraClaudeHomes ?? base.extraClaudeHomes,
|
|
4609
4615
|
approvedHomes: partial.approvedHomes ?? base.approvedHomes,
|
|
4610
4616
|
declinedHomes: partial.declinedHomes ?? base.declinedHomes,
|
|
@@ -5284,6 +5290,8 @@ function declaredOrigin(value) {
|
|
|
5284
5290
|
function userRecordOrigin(obj, text) {
|
|
5285
5291
|
if (obj.isSidechain === true)
|
|
5286
5292
|
return "subagent";
|
|
5293
|
+
if (obj.isCompactSummary === true)
|
|
5294
|
+
return "compact-summary";
|
|
5287
5295
|
const declared = declaredOrigin(obj.origin);
|
|
5288
5296
|
if (declared && declared !== "human")
|
|
5289
5297
|
return declared;
|
|
@@ -5365,6 +5373,10 @@ var init_from_events = __esm({
|
|
|
5365
5373
|
});
|
|
5366
5374
|
|
|
5367
5375
|
// ../engine/dist/transcript/raw.js
|
|
5376
|
+
import { getHeapStatistics } from "v8";
|
|
5377
|
+
function rawParseMaxBytes(heapLimit) {
|
|
5378
|
+
return Math.max(0, Math.min(RAW_PARSE_CEILING, Math.floor((heapLimit - RAW_PARSE_HEAP_RESERVE) / RAW_PARSE_HEAP_PER_BYTE)));
|
|
5379
|
+
}
|
|
5368
5380
|
function redactJsonLine(line, redact) {
|
|
5369
5381
|
if (!line.includes('"'))
|
|
5370
5382
|
return redact(line);
|
|
@@ -5430,7 +5442,7 @@ function redactContainer(c, redact) {
|
|
|
5430
5442
|
}))
|
|
5431
5443
|
};
|
|
5432
5444
|
}
|
|
5433
|
-
var RAW_PARSE_MAX_BYTES;
|
|
5445
|
+
var STREAM_PIECE_CHARS, RAW_PARSE_HEAP_PER_BYTE, RAW_PARSE_HEAP_RESERVE, RAW_PARSE_CEILING, RAW_PARSE_MAX_BYTES;
|
|
5434
5446
|
var init_raw = __esm({
|
|
5435
5447
|
"../engine/dist/transcript/raw.js"() {
|
|
5436
5448
|
"use strict";
|
|
@@ -5438,7 +5450,11 @@ var init_raw = __esm({
|
|
|
5438
5450
|
init_claude();
|
|
5439
5451
|
init_codex();
|
|
5440
5452
|
init_from_events();
|
|
5441
|
-
|
|
5453
|
+
STREAM_PIECE_CHARS = 1 << 20;
|
|
5454
|
+
RAW_PARSE_HEAP_PER_BYTE = 6;
|
|
5455
|
+
RAW_PARSE_HEAP_RESERVE = 128 * 1024 * 1024;
|
|
5456
|
+
RAW_PARSE_CEILING = 40 * 1024 * 1024;
|
|
5457
|
+
RAW_PARSE_MAX_BYTES = rawParseMaxBytes(getHeapStatistics().heap_size_limit);
|
|
5442
5458
|
}
|
|
5443
5459
|
});
|
|
5444
5460
|
|
|
@@ -5526,6 +5542,16 @@ function grandfatherLegacyHomes() {
|
|
|
5526
5542
|
}
|
|
5527
5543
|
return { seeded };
|
|
5528
5544
|
}
|
|
5545
|
+
function defaultPiHome() {
|
|
5546
|
+
return join3(homedir2(), ".pi", "agent");
|
|
5547
|
+
}
|
|
5548
|
+
function defaultHermesHome() {
|
|
5549
|
+
if (process.platform === "win32") {
|
|
5550
|
+
const local = process.env.LOCALAPPDATA?.trim() || join3(homedir2(), "AppData", "Local");
|
|
5551
|
+
return join3(local, "hermes");
|
|
5552
|
+
}
|
|
5553
|
+
return join3(homedir2(), ".hermes");
|
|
5554
|
+
}
|
|
5529
5555
|
function primaryHomes() {
|
|
5530
5556
|
const home = homedir2();
|
|
5531
5557
|
const env = process.env;
|
|
@@ -5542,7 +5568,9 @@ function primaryHomes() {
|
|
|
5542
5568
|
env.CHAT_RECALL_CODEX_HOME || s?.codexHome || join3(home, ".codex"),
|
|
5543
5569
|
env.CHAT_RECALL_AGY_HOME || s?.agyHome || join3(home, ".gemini", "antigravity-cli"),
|
|
5544
5570
|
env.CHAT_RECALL_CURSOR_HOME || s?.cursorHome || join3(home, ".cursor"),
|
|
5545
|
-
dirOf(opencodeDb)
|
|
5571
|
+
dirOf(opencodeDb),
|
|
5572
|
+
env.CHAT_RECALL_HERMES_HOME || s?.hermesHome || env.HERMES_HOME?.trim() || defaultHermesHome(),
|
|
5573
|
+
env.CHAT_RECALL_PI_HOME || s?.piHome || env.PI_CODING_AGENT_DIR?.trim() || defaultPiHome()
|
|
5546
5574
|
];
|
|
5547
5575
|
return candidates.map(normalizeHomePath);
|
|
5548
5576
|
}
|
|
@@ -5578,9 +5606,9 @@ var init_home_approval = __esm({
|
|
|
5578
5606
|
});
|
|
5579
5607
|
|
|
5580
5608
|
// ../engine/dist/core/tool-paths.js
|
|
5581
|
-
import { existsSync as existsSync3, readdirSync as readdirSync2 } from "fs";
|
|
5609
|
+
import { existsSync as existsSync3, readdirSync as readdirSync2, readFileSync as readFileSync2, statSync as statSync3, realpathSync as realpathSync2 } from "fs";
|
|
5582
5610
|
import { homedir as homedir3 } from "os";
|
|
5583
|
-
import { join as join4, dirname as dirname4, basename } from "path";
|
|
5611
|
+
import { join as join4, dirname as dirname4, basename, isAbsolute } from "path";
|
|
5584
5612
|
function claudeHomeDir() {
|
|
5585
5613
|
return process.env.CHAT_RECALL_CLAUDE_HOME || sources().claudeHome || join4(homedir3(), ".claude");
|
|
5586
5614
|
}
|
|
@@ -5621,6 +5649,9 @@ function geminiHomeDir() {
|
|
|
5621
5649
|
function opencodeDbPath() {
|
|
5622
5650
|
return process.env.CHAT_RECALL_OPENCODE_DB || sources().opencodeDbPath || join4(homedir3(), ".local", "share", "opencode", "opencode.db");
|
|
5623
5651
|
}
|
|
5652
|
+
function hermesHomeDir() {
|
|
5653
|
+
return process.env.CHAT_RECALL_HERMES_HOME || sources().hermesHome || process.env.HERMES_HOME?.trim() || defaultHermesHome();
|
|
5654
|
+
}
|
|
5624
5655
|
function codexHomeDir() {
|
|
5625
5656
|
return process.env.CHAT_RECALL_CODEX_HOME || sources().codexHome || join4(homedir3(), ".codex");
|
|
5626
5657
|
}
|
|
@@ -5633,6 +5664,98 @@ function cursorHomeDir() {
|
|
|
5633
5664
|
function cursorIdeHomeDir() {
|
|
5634
5665
|
return process.env.CHAT_RECALL_CURSOR_IDE_HOME || sources().cursorIdeHome || (process.platform === "darwin" ? join4(homedir3(), "Library", "Application Support", "Cursor") : join4(homedir3(), ".config", "Cursor"));
|
|
5635
5666
|
}
|
|
5667
|
+
function piHomeDir() {
|
|
5668
|
+
const raw = process.env.CHAT_RECALL_PI_HOME || sources().piHome || process.env.PI_CODING_AGENT_DIR?.trim();
|
|
5669
|
+
return raw ? expandTildePath(raw) : join4(homedir3(), ".pi", "agent");
|
|
5670
|
+
}
|
|
5671
|
+
function expandTildePath(value) {
|
|
5672
|
+
const t = value.trim();
|
|
5673
|
+
if (t === "~")
|
|
5674
|
+
return homedir3();
|
|
5675
|
+
if (t.startsWith("~/") || t.startsWith("~\\"))
|
|
5676
|
+
return join4(homedir3(), t.slice(2));
|
|
5677
|
+
return t;
|
|
5678
|
+
}
|
|
5679
|
+
function piSessionDir() {
|
|
5680
|
+
const override = piSessionDirEnv();
|
|
5681
|
+
if (override)
|
|
5682
|
+
return override;
|
|
5683
|
+
return piSessionDirForHome(piHomeDir());
|
|
5684
|
+
}
|
|
5685
|
+
function piSessionDirEnv() {
|
|
5686
|
+
const raw = process.env.CHAT_RECALL_PI_SESSION_DIR || process.env.PI_CODING_AGENT_SESSION_DIR?.trim();
|
|
5687
|
+
return raw && raw.trim() ? expandTildePath(raw) : null;
|
|
5688
|
+
}
|
|
5689
|
+
function piSessionDirForHome(home) {
|
|
5690
|
+
const env = piSessionDirEnv();
|
|
5691
|
+
if (env && sameDir(home, piHomeDir()))
|
|
5692
|
+
return env;
|
|
5693
|
+
return piSessionDirFromSettings(join4(home, "settings.json")) || join4(home, "sessions");
|
|
5694
|
+
}
|
|
5695
|
+
function sameDir(a, b) {
|
|
5696
|
+
if (a === b)
|
|
5697
|
+
return true;
|
|
5698
|
+
try {
|
|
5699
|
+
return realpathSync2(a) === realpathSync2(b);
|
|
5700
|
+
} catch {
|
|
5701
|
+
return false;
|
|
5702
|
+
}
|
|
5703
|
+
}
|
|
5704
|
+
function piSessionDirFromSettings(file) {
|
|
5705
|
+
let sig;
|
|
5706
|
+
try {
|
|
5707
|
+
sig = statSync3(file).mtimeMs;
|
|
5708
|
+
} catch {
|
|
5709
|
+
return null;
|
|
5710
|
+
}
|
|
5711
|
+
if (piSettingsCache && piSettingsCache.path === file && piSettingsCache.sig === sig) {
|
|
5712
|
+
return piSettingsCache.dir;
|
|
5713
|
+
}
|
|
5714
|
+
let dir = null;
|
|
5715
|
+
try {
|
|
5716
|
+
const parsed = JSON.parse(readFileSync2(file, "utf-8"));
|
|
5717
|
+
const value = typeof parsed.sessionDir === "string" ? parsed.sessionDir.trim() : "";
|
|
5718
|
+
const expanded = value ? expandTildePath(value) : "";
|
|
5719
|
+
dir = expanded && isAbsolute(expanded) ? expanded : null;
|
|
5720
|
+
} catch {
|
|
5721
|
+
}
|
|
5722
|
+
piSettingsCache = { path: file, sig, dir };
|
|
5723
|
+
return dir;
|
|
5724
|
+
}
|
|
5725
|
+
function piSessionDirs(opts = {}) {
|
|
5726
|
+
const home = piHomeDir();
|
|
5727
|
+
const dir = piSessionDir();
|
|
5728
|
+
if (!existsSync3(dir))
|
|
5729
|
+
return [];
|
|
5730
|
+
if (opts.includeExcluded)
|
|
5731
|
+
return [dir];
|
|
5732
|
+
try {
|
|
5733
|
+
grandfatherLegacyHomes();
|
|
5734
|
+
} catch {
|
|
5735
|
+
}
|
|
5736
|
+
try {
|
|
5737
|
+
if (!isHomeSynced(home))
|
|
5738
|
+
return [];
|
|
5739
|
+
} catch {
|
|
5740
|
+
}
|
|
5741
|
+
try {
|
|
5742
|
+
if (homeDecision(dir) === "declined")
|
|
5743
|
+
return [];
|
|
5744
|
+
} catch {
|
|
5745
|
+
}
|
|
5746
|
+
if (_sourceExclusionFilter && _sourceExclusionFilter(dir))
|
|
5747
|
+
return [];
|
|
5748
|
+
return [dir];
|
|
5749
|
+
}
|
|
5750
|
+
function piSkillsDir() {
|
|
5751
|
+
return join4(piHomeDir(), "skills");
|
|
5752
|
+
}
|
|
5753
|
+
function piPromptsDir() {
|
|
5754
|
+
return join4(piHomeDir(), "prompts");
|
|
5755
|
+
}
|
|
5756
|
+
function piMcpConfigPath() {
|
|
5757
|
+
return join4(piHomeDir(), "mcp.json");
|
|
5758
|
+
}
|
|
5636
5759
|
function siblingHomes(base, overridden, suffix) {
|
|
5637
5760
|
const roots = [];
|
|
5638
5761
|
const add = (home) => {
|
|
@@ -5726,7 +5849,23 @@ function opencodeDbPaths(opts = {}) {
|
|
|
5726
5849
|
dbs.push(primary);
|
|
5727
5850
|
return applyExclusions(dbs, opts.includeExcluded);
|
|
5728
5851
|
}
|
|
5729
|
-
|
|
5852
|
+
function hermesDbPaths(opts = {}) {
|
|
5853
|
+
const root = hermesHomeDir();
|
|
5854
|
+
const dbs = [join4(root, "state.db")];
|
|
5855
|
+
try {
|
|
5856
|
+
const profiles = join4(root, "profiles");
|
|
5857
|
+
for (const entry of readdirSync2(profiles, { withFileTypes: true })) {
|
|
5858
|
+
if (!entry.isDirectory())
|
|
5859
|
+
continue;
|
|
5860
|
+
const p = join4(profiles, entry.name, "state.db");
|
|
5861
|
+
if (existsSync3(p))
|
|
5862
|
+
dbs.push(p);
|
|
5863
|
+
}
|
|
5864
|
+
} catch {
|
|
5865
|
+
}
|
|
5866
|
+
return applyExclusions(dbs, opts.includeExcluded);
|
|
5867
|
+
}
|
|
5868
|
+
var sources, _sourceExclusionFilter, piSettingsCache;
|
|
5730
5869
|
var init_tool_paths = __esm({
|
|
5731
5870
|
"../engine/dist/core/tool-paths.js"() {
|
|
5732
5871
|
"use strict";
|
|
@@ -5734,6 +5873,7 @@ var init_tool_paths = __esm({
|
|
|
5734
5873
|
init_settings();
|
|
5735
5874
|
sources = loadSourceSettings;
|
|
5736
5875
|
_sourceExclusionFilter = null;
|
|
5876
|
+
piSettingsCache = null;
|
|
5737
5877
|
}
|
|
5738
5878
|
});
|
|
5739
5879
|
|
|
@@ -6899,7 +7039,7 @@ var init_opencode = __esm({
|
|
|
6899
7039
|
});
|
|
6900
7040
|
|
|
6901
7041
|
// ../engine/dist/core/backends/tail-read.js
|
|
6902
|
-
import { openSync, readSync, closeSync, statSync as
|
|
7042
|
+
import { openSync, readSync, closeSync, statSync as statSync4, fstatSync } from "node:fs";
|
|
6903
7043
|
function readAt(fd, pos, len) {
|
|
6904
7044
|
const buf = Buffer.alloc(len);
|
|
6905
7045
|
let got = 0;
|
|
@@ -6960,7 +7100,7 @@ function linesBeforeOffset(path, offset) {
|
|
|
6960
7100
|
function readTailFromOffset(path, offset, maxBytes = TAIL_READ_MAX_BYTES) {
|
|
6961
7101
|
let size = 0;
|
|
6962
7102
|
try {
|
|
6963
|
-
size =
|
|
7103
|
+
size = statSync4(path).size;
|
|
6964
7104
|
} catch {
|
|
6965
7105
|
return { text: "", newOffset: offset };
|
|
6966
7106
|
}
|
|
@@ -7003,6 +7143,71 @@ function readTailFromOffset(path, offset, maxBytes = TAIL_READ_MAX_BYTES) {
|
|
|
7003
7143
|
}
|
|
7004
7144
|
}
|
|
7005
7145
|
}
|
|
7146
|
+
function readSourcesFromOffset(paths, offset, maxBytes = TAIL_READ_MAX_BYTES) {
|
|
7147
|
+
let start = 0;
|
|
7148
|
+
let linesBefore = 0;
|
|
7149
|
+
for (let i = 0; i < paths.length; i++) {
|
|
7150
|
+
const path = paths[i];
|
|
7151
|
+
let size = 0;
|
|
7152
|
+
try {
|
|
7153
|
+
size = statSync4(path).size;
|
|
7154
|
+
} catch {
|
|
7155
|
+
size = 0;
|
|
7156
|
+
}
|
|
7157
|
+
const last = i === paths.length - 1;
|
|
7158
|
+
if (!last && offset >= start + size) {
|
|
7159
|
+
const lines = linesBeforeOffset(path, size);
|
|
7160
|
+
if (lines === void 0)
|
|
7161
|
+
return { text: "", newOffset: offset };
|
|
7162
|
+
linesBefore += lines + (size > 0 && !endsWithNewline(path, size) ? 1 : 0);
|
|
7163
|
+
start += size;
|
|
7164
|
+
continue;
|
|
7165
|
+
}
|
|
7166
|
+
const local = offset - start;
|
|
7167
|
+
const read = readTailFromOffset(path, local, maxBytes);
|
|
7168
|
+
const shift = (r) => ({
|
|
7169
|
+
...r,
|
|
7170
|
+
newOffset: start + r.newOffset,
|
|
7171
|
+
...r.baseLine !== void 0 ? { baseLine: linesBefore + r.baseLine } : {}
|
|
7172
|
+
});
|
|
7173
|
+
if (last || read.newOffset > local || local >= size)
|
|
7174
|
+
return shift(read);
|
|
7175
|
+
if (size - local > maxBytes)
|
|
7176
|
+
return { text: "", newOffset: start + size, skippedBytes: size - local };
|
|
7177
|
+
let fd = null;
|
|
7178
|
+
try {
|
|
7179
|
+
fd = openSync(path, "r");
|
|
7180
|
+
const text = readAt(fd, local, size - local).toString("utf-8");
|
|
7181
|
+
return shift({ text, newOffset: size, baseLine: linesBeforeOffset(path, local) });
|
|
7182
|
+
} catch {
|
|
7183
|
+
return { text: "", newOffset: offset };
|
|
7184
|
+
} finally {
|
|
7185
|
+
if (fd !== null) {
|
|
7186
|
+
try {
|
|
7187
|
+
closeSync(fd);
|
|
7188
|
+
} catch {
|
|
7189
|
+
}
|
|
7190
|
+
}
|
|
7191
|
+
}
|
|
7192
|
+
}
|
|
7193
|
+
return { text: "", newOffset: offset };
|
|
7194
|
+
}
|
|
7195
|
+
function endsWithNewline(path, size) {
|
|
7196
|
+
let fd = null;
|
|
7197
|
+
try {
|
|
7198
|
+
fd = openSync(path, "r");
|
|
7199
|
+
return readAt(fd, size - 1, 1)[0] === 10;
|
|
7200
|
+
} catch {
|
|
7201
|
+
return true;
|
|
7202
|
+
} finally {
|
|
7203
|
+
if (fd !== null) {
|
|
7204
|
+
try {
|
|
7205
|
+
closeSync(fd);
|
|
7206
|
+
} catch {
|
|
7207
|
+
}
|
|
7208
|
+
}
|
|
7209
|
+
}
|
|
7210
|
+
}
|
|
7006
7211
|
function findNewline(fd, from, size) {
|
|
7007
7212
|
const block = Buffer.allocUnsafe(SKIP_SCAN_BYTES);
|
|
7008
7213
|
let pos = from;
|
|
@@ -7030,7 +7235,7 @@ var init_tail_read = __esm({
|
|
|
7030
7235
|
});
|
|
7031
7236
|
|
|
7032
7237
|
// ../engine/dist/core/backends/codex.js
|
|
7033
|
-
import { readdirSync as readdirSync3, readFileSync as
|
|
7238
|
+
import { readdirSync as readdirSync3, readFileSync as readFileSync3, statSync as statSync5, openSync as openSync2, readSync as readSync2, closeSync as closeSync2 } from "fs";
|
|
7034
7239
|
import { basename as basename4, dirname as dirname7, join as join6 } from "path";
|
|
7035
7240
|
function parseShellWrites(cmd) {
|
|
7036
7241
|
const out = [];
|
|
@@ -7159,7 +7364,7 @@ function flattenContent(content) {
|
|
|
7159
7364
|
}
|
|
7160
7365
|
function readFirstLine(path) {
|
|
7161
7366
|
try {
|
|
7162
|
-
const head =
|
|
7367
|
+
const head = readFileSync3(path, "utf-8").split("\n", 1)[0] || "";
|
|
7163
7368
|
if (!head)
|
|
7164
7369
|
return null;
|
|
7165
7370
|
return JSON.parse(head);
|
|
@@ -7265,7 +7470,7 @@ var init_codex2 = __esm({
|
|
|
7265
7470
|
return null;
|
|
7266
7471
|
let mtime = 0;
|
|
7267
7472
|
try {
|
|
7268
|
-
mtime =
|
|
7473
|
+
mtime = statSync5(located.path).mtimeMs;
|
|
7269
7474
|
} catch {
|
|
7270
7475
|
}
|
|
7271
7476
|
return {
|
|
@@ -7285,7 +7490,7 @@ var init_codex2 = __esm({
|
|
|
7285
7490
|
for (const filePath of walkRollouts(root)) {
|
|
7286
7491
|
let stat;
|
|
7287
7492
|
try {
|
|
7288
|
-
stat =
|
|
7493
|
+
stat = statSync5(filePath);
|
|
7289
7494
|
} catch {
|
|
7290
7495
|
continue;
|
|
7291
7496
|
}
|
|
@@ -7305,7 +7510,7 @@ var init_codex2 = __esm({
|
|
|
7305
7510
|
let messageCount = 0;
|
|
7306
7511
|
if (opts.previews !== false)
|
|
7307
7512
|
try {
|
|
7308
|
-
const lines =
|
|
7513
|
+
const lines = readFileSync3(filePath, "utf-8").split("\n");
|
|
7309
7514
|
for (const line of lines) {
|
|
7310
7515
|
if (!line.trim())
|
|
7311
7516
|
continue;
|
|
@@ -7457,7 +7662,7 @@ var init_codex2 = __esm({
|
|
|
7457
7662
|
continue;
|
|
7458
7663
|
let firstLine = "";
|
|
7459
7664
|
try {
|
|
7460
|
-
firstLine =
|
|
7665
|
+
firstLine = readFileSync3(path, "utf-8").split("\n", 1)[0] || "";
|
|
7461
7666
|
} catch {
|
|
7462
7667
|
continue;
|
|
7463
7668
|
}
|
|
@@ -7481,8 +7686,8 @@ var init_codex2 = __esm({
|
|
|
7481
7686
|
let raw = "";
|
|
7482
7687
|
let mtime = 0;
|
|
7483
7688
|
try {
|
|
7484
|
-
raw =
|
|
7485
|
-
mtime =
|
|
7689
|
+
raw = readFileSync3(path, "utf-8");
|
|
7690
|
+
mtime = statSync5(path).mtimeMs;
|
|
7486
7691
|
} catch {
|
|
7487
7692
|
return [];
|
|
7488
7693
|
}
|
|
@@ -7582,7 +7787,7 @@ var init_codex2 = __esm({
|
|
|
7582
7787
|
const events = this.readEvents(this.toRawId(id));
|
|
7583
7788
|
let fileMtime = 0;
|
|
7584
7789
|
try {
|
|
7585
|
-
fileMtime =
|
|
7790
|
+
fileMtime = statSync5(located.path).mtimeMs;
|
|
7586
7791
|
} catch {
|
|
7587
7792
|
}
|
|
7588
7793
|
return liveScanEditsFromEvents(events, this.fileToolMap, {
|
|
@@ -7646,7 +7851,7 @@ var init_codex2 = __esm({
|
|
|
7646
7851
|
const p = join6(dir, entry.name);
|
|
7647
7852
|
let st;
|
|
7648
7853
|
try {
|
|
7649
|
-
st =
|
|
7854
|
+
st = statSync5(p);
|
|
7650
7855
|
} catch {
|
|
7651
7856
|
continue;
|
|
7652
7857
|
}
|
|
@@ -7658,7 +7863,7 @@ var init_codex2 = __esm({
|
|
|
7658
7863
|
let parentId = null;
|
|
7659
7864
|
let isSubagent = false;
|
|
7660
7865
|
try {
|
|
7661
|
-
const first =
|
|
7866
|
+
const first = readFileSync3(p, "utf-8").split("\n")[0];
|
|
7662
7867
|
if (first) {
|
|
7663
7868
|
const meta = JSON.parse(first);
|
|
7664
7869
|
const payload = meta?.payload || {};
|
|
@@ -7713,8 +7918,8 @@ var init_codex2 = __esm({
|
|
|
7713
7918
|
let mtime = 0;
|
|
7714
7919
|
const push = (path, name) => {
|
|
7715
7920
|
try {
|
|
7716
|
-
const st =
|
|
7717
|
-
files.push({ name, bytes:
|
|
7921
|
+
const st = statSync5(path);
|
|
7922
|
+
files.push({ name, bytes: readFileSync3(path) });
|
|
7718
7923
|
if (st.mtimeMs > mtime)
|
|
7719
7924
|
mtime = st.mtimeMs;
|
|
7720
7925
|
} catch {
|
|
@@ -7755,7 +7960,7 @@ var init_codex2 = __esm({
|
|
|
7755
7960
|
if (!loc)
|
|
7756
7961
|
return 0;
|
|
7757
7962
|
try {
|
|
7758
|
-
return
|
|
7963
|
+
return statSync5(loc.path).size;
|
|
7759
7964
|
} catch {
|
|
7760
7965
|
return 0;
|
|
7761
7966
|
}
|
|
@@ -7772,7 +7977,7 @@ var init_codex2 = __esm({
|
|
|
7772
7977
|
});
|
|
7773
7978
|
|
|
7774
7979
|
// ../engine/dist/core/backends/agy.js
|
|
7775
|
-
import { existsSync as existsSync6, readdirSync as readdirSync4, readFileSync as
|
|
7980
|
+
import { existsSync as existsSync6, readdirSync as readdirSync4, readFileSync as readFileSync4, statSync as statSync6 } from "fs";
|
|
7776
7981
|
import { basename as basename5, dirname as dirname8, join as join7 } from "path";
|
|
7777
7982
|
function agyTranscriptPath(logsDir) {
|
|
7778
7983
|
const full = join7(logsDir, "transcript_full.jsonl");
|
|
@@ -7792,7 +7997,7 @@ function resolveAgyProject(logsDir, homeDir) {
|
|
|
7792
7997
|
function extractProjectPathFromTranscript(path, homeDir) {
|
|
7793
7998
|
let content;
|
|
7794
7999
|
try {
|
|
7795
|
-
content =
|
|
8000
|
+
content = readFileSync4(path, "utf-8");
|
|
7796
8001
|
} catch {
|
|
7797
8002
|
return "";
|
|
7798
8003
|
}
|
|
@@ -7965,7 +8170,7 @@ var init_agy = __esm({
|
|
|
7965
8170
|
return null;
|
|
7966
8171
|
let mtime = 0;
|
|
7967
8172
|
try {
|
|
7968
|
-
mtime =
|
|
8173
|
+
mtime = statSync6(path).mtimeMs;
|
|
7969
8174
|
} catch {
|
|
7970
8175
|
}
|
|
7971
8176
|
const projectPath = resolveAgyProject(dirname8(path), this.homeDir());
|
|
@@ -7993,7 +8198,7 @@ var init_agy = __esm({
|
|
|
7993
8198
|
for (const rawId of sessionDirs) {
|
|
7994
8199
|
const sessionPath = join7(brainDir, rawId);
|
|
7995
8200
|
try {
|
|
7996
|
-
if (!
|
|
8201
|
+
if (!statSync6(sessionPath).isDirectory())
|
|
7997
8202
|
continue;
|
|
7998
8203
|
} catch {
|
|
7999
8204
|
continue;
|
|
@@ -8003,7 +8208,7 @@ var init_agy = __esm({
|
|
|
8003
8208
|
continue;
|
|
8004
8209
|
let stat;
|
|
8005
8210
|
try {
|
|
8006
|
-
stat =
|
|
8211
|
+
stat = statSync6(filePath);
|
|
8007
8212
|
} catch {
|
|
8008
8213
|
continue;
|
|
8009
8214
|
}
|
|
@@ -8017,7 +8222,7 @@ var init_agy = __esm({
|
|
|
8017
8222
|
let messageCount = 0;
|
|
8018
8223
|
if (opts.previews !== false)
|
|
8019
8224
|
try {
|
|
8020
|
-
const lines =
|
|
8225
|
+
const lines = readFileSync4(filePath, "utf-8").split("\n").filter(Boolean);
|
|
8021
8226
|
messageCount = lines.length;
|
|
8022
8227
|
for (const line of lines) {
|
|
8023
8228
|
const obj = JSON.parse(line);
|
|
@@ -8107,7 +8312,7 @@ var init_agy = __esm({
|
|
|
8107
8312
|
return [];
|
|
8108
8313
|
let rawText;
|
|
8109
8314
|
try {
|
|
8110
|
-
rawText =
|
|
8315
|
+
rawText = readFileSync4(located.path, "utf-8");
|
|
8111
8316
|
} catch {
|
|
8112
8317
|
return [];
|
|
8113
8318
|
}
|
|
@@ -8269,7 +8474,7 @@ var init_agy = __esm({
|
|
|
8269
8474
|
for (const rawId of sessionDirs) {
|
|
8270
8475
|
const sessionPath = join7(brainDir, rawId);
|
|
8271
8476
|
try {
|
|
8272
|
-
if (!
|
|
8477
|
+
if (!statSync6(sessionPath).isDirectory())
|
|
8273
8478
|
continue;
|
|
8274
8479
|
} catch {
|
|
8275
8480
|
continue;
|
|
@@ -8278,7 +8483,7 @@ var init_agy = __esm({
|
|
|
8278
8483
|
if (!filePath)
|
|
8279
8484
|
continue;
|
|
8280
8485
|
try {
|
|
8281
|
-
const st =
|
|
8486
|
+
const st = statSync6(filePath);
|
|
8282
8487
|
if (st.mtimeMs < opts.sinceMs)
|
|
8283
8488
|
continue;
|
|
8284
8489
|
candidates.push({ rawId, mtime: st.mtimeMs });
|
|
@@ -8306,8 +8511,8 @@ var init_agy = __esm({
|
|
|
8306
8511
|
try {
|
|
8307
8512
|
return {
|
|
8308
8513
|
tool: "agy",
|
|
8309
|
-
mtime:
|
|
8310
|
-
files: [{ name: basename5(loc.path), bytes:
|
|
8514
|
+
mtime: statSync6(loc.path).mtimeMs,
|
|
8515
|
+
files: [{ name: basename5(loc.path), bytes: readFileSync4(loc.path) }]
|
|
8311
8516
|
};
|
|
8312
8517
|
} catch {
|
|
8313
8518
|
return null;
|
|
@@ -8321,7 +8526,7 @@ var init_agy = __esm({
|
|
|
8321
8526
|
if (!loc)
|
|
8322
8527
|
return 0;
|
|
8323
8528
|
try {
|
|
8324
|
-
return
|
|
8529
|
+
return statSync6(loc.path).size;
|
|
8325
8530
|
} catch {
|
|
8326
8531
|
return 0;
|
|
8327
8532
|
}
|
|
@@ -8338,7 +8543,7 @@ var init_agy = __esm({
|
|
|
8338
8543
|
});
|
|
8339
8544
|
|
|
8340
8545
|
// ../engine/dist/core/backends/cursor-store.js
|
|
8341
|
-
import { existsSync as existsSync7, readdirSync as readdirSync5, readFileSync as
|
|
8546
|
+
import { existsSync as existsSync7, readdirSync as readdirSync5, readFileSync as readFileSync5, statSync as statSync7 } from "fs";
|
|
8342
8547
|
import { join as join8 } from "path";
|
|
8343
8548
|
function cursorProjectSlug(absPath) {
|
|
8344
8549
|
return absPath.replace(/[^a-zA-Z0-9]/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "");
|
|
@@ -8355,7 +8560,7 @@ function listChatsIn(chatsRoot) {
|
|
|
8355
8560
|
const hashPath = join8(chatsRoot, workspaceHash);
|
|
8356
8561
|
let chatIds;
|
|
8357
8562
|
try {
|
|
8358
|
-
if (!
|
|
8563
|
+
if (!statSync7(hashPath).isDirectory())
|
|
8359
8564
|
continue;
|
|
8360
8565
|
chatIds = readdirSync5(hashPath);
|
|
8361
8566
|
} catch {
|
|
@@ -8368,7 +8573,7 @@ function listChatsIn(chatsRoot) {
|
|
|
8368
8573
|
continue;
|
|
8369
8574
|
let meta = {};
|
|
8370
8575
|
try {
|
|
8371
|
-
meta = JSON.parse(
|
|
8576
|
+
meta = JSON.parse(readFileSync5(join8(dir, "meta.json"), "utf-8"));
|
|
8372
8577
|
} catch {
|
|
8373
8578
|
}
|
|
8374
8579
|
if (meta.hasConversation === false)
|
|
@@ -8377,7 +8582,7 @@ function listChatsIn(chatsRoot) {
|
|
|
8377
8582
|
let created = meta.createdAtMs ?? 0;
|
|
8378
8583
|
if (!mtime || !created) {
|
|
8379
8584
|
try {
|
|
8380
|
-
const st =
|
|
8585
|
+
const st = statSync7(dbPath);
|
|
8381
8586
|
mtime = mtime || st.mtimeMs;
|
|
8382
8587
|
created = created || st.birthtimeMs || st.mtimeMs;
|
|
8383
8588
|
} catch {
|
|
@@ -8413,14 +8618,14 @@ function findChat(chatsRoots, chatId) {
|
|
|
8413
8618
|
continue;
|
|
8414
8619
|
let meta = {};
|
|
8415
8620
|
try {
|
|
8416
|
-
meta = JSON.parse(
|
|
8621
|
+
meta = JSON.parse(readFileSync5(join8(dir, "meta.json"), "utf-8"));
|
|
8417
8622
|
} catch {
|
|
8418
8623
|
}
|
|
8419
8624
|
let mtime = meta.updatedAtMs ?? 0;
|
|
8420
8625
|
let created = meta.createdAtMs ?? 0;
|
|
8421
8626
|
if (!mtime || !created) {
|
|
8422
8627
|
try {
|
|
8423
|
-
const st =
|
|
8628
|
+
const st = statSync7(dbPath);
|
|
8424
8629
|
mtime = mtime || st.mtimeMs;
|
|
8425
8630
|
created = created || st.birthtimeMs || st.mtimeMs;
|
|
8426
8631
|
} catch {
|
|
@@ -8723,7 +8928,7 @@ var init_cursor_store = __esm({
|
|
|
8723
8928
|
});
|
|
8724
8929
|
|
|
8725
8930
|
// ../engine/dist/core/backends/cursor-ide.js
|
|
8726
|
-
import { existsSync as existsSync8, readFileSync as
|
|
8931
|
+
import { existsSync as existsSync8, readFileSync as readFileSync6, readdirSync as readdirSync6, statSync as statSync8 } from "fs";
|
|
8727
8932
|
import { dirname as dirname9, join as join9 } from "path";
|
|
8728
8933
|
function jsonOf(value) {
|
|
8729
8934
|
if (value == null)
|
|
@@ -8776,7 +8981,7 @@ function projectPathForWorkspace(workspaceDir) {
|
|
|
8776
8981
|
}
|
|
8777
8982
|
function safeRead(path) {
|
|
8778
8983
|
try {
|
|
8779
|
-
return
|
|
8984
|
+
return readFileSync6(path, "utf-8");
|
|
8780
8985
|
} catch {
|
|
8781
8986
|
return null;
|
|
8782
8987
|
}
|
|
@@ -8793,7 +8998,7 @@ function workspaceIndex(workspaceRoots) {
|
|
|
8793
8998
|
for (const id of entries) {
|
|
8794
8999
|
const dir = join9(root, id);
|
|
8795
9000
|
try {
|
|
8796
|
-
if (!
|
|
9001
|
+
if (!statSync8(dir).isDirectory())
|
|
8797
9002
|
continue;
|
|
8798
9003
|
} catch {
|
|
8799
9004
|
continue;
|
|
@@ -9139,7 +9344,7 @@ var init_cursor_ide = __esm({
|
|
|
9139
9344
|
});
|
|
9140
9345
|
|
|
9141
9346
|
// ../engine/dist/core/backends/cursor.js
|
|
9142
|
-
import { existsSync as existsSync9, readFileSync as
|
|
9347
|
+
import { existsSync as existsSync9, readFileSync as readFileSync7, statSync as statSync9 } from "fs";
|
|
9143
9348
|
import { join as join10 } from "path";
|
|
9144
9349
|
var PREFIX4, CursorBackend, cursorBackend;
|
|
9145
9350
|
var init_cursor = __esm({
|
|
@@ -9192,7 +9397,7 @@ var init_cursor = __esm({
|
|
|
9192
9397
|
if (r.surface === "cli") {
|
|
9193
9398
|
let mtime2 = r.chat.updatedAtMs;
|
|
9194
9399
|
try {
|
|
9195
|
-
mtime2 =
|
|
9400
|
+
mtime2 = statSync9(r.chat.dbPath).mtimeMs || mtime2;
|
|
9196
9401
|
} catch {
|
|
9197
9402
|
}
|
|
9198
9403
|
return {
|
|
@@ -9205,7 +9410,7 @@ var init_cursor = __esm({
|
|
|
9205
9410
|
}
|
|
9206
9411
|
let mtime = r.composer.updatedAtMs;
|
|
9207
9412
|
try {
|
|
9208
|
-
mtime =
|
|
9413
|
+
mtime = statSync9(r.composer.globalDbPath).mtimeMs || mtime;
|
|
9209
9414
|
} catch {
|
|
9210
9415
|
}
|
|
9211
9416
|
return {
|
|
@@ -9363,7 +9568,7 @@ var init_cursor = __esm({
|
|
|
9363
9568
|
if (!jsonl)
|
|
9364
9569
|
return [];
|
|
9365
9570
|
try {
|
|
9366
|
-
return readTranscriptEvents(
|
|
9571
|
+
return readTranscriptEvents(readFileSync7(jsonl, "utf-8"), chat.updatedAtMs);
|
|
9367
9572
|
} catch {
|
|
9368
9573
|
return [];
|
|
9369
9574
|
}
|
|
@@ -9449,9 +9654,9 @@ var init_cursor = __esm({
|
|
|
9449
9654
|
const files = [{ name: `${r.chat.chatId}.jsonl`, bytes: Buffer.from(lines, "utf8") }];
|
|
9450
9655
|
const metaPath = join10(r.chat.dir, "meta.json");
|
|
9451
9656
|
if (existsSync9(metaPath)) {
|
|
9452
|
-
files.push({ name: "meta.json", bytes:
|
|
9657
|
+
files.push({ name: "meta.json", bytes: readFileSync7(metaPath) });
|
|
9453
9658
|
}
|
|
9454
|
-
return { tool: "cursor", mtime:
|
|
9659
|
+
return { tool: "cursor", mtime: statSync9(r.chat.dbPath).mtimeMs, files };
|
|
9455
9660
|
}
|
|
9456
9661
|
const events = readComposerEvents(r.composer);
|
|
9457
9662
|
if (events.length === 0)
|
|
@@ -9459,7 +9664,7 @@ var init_cursor = __esm({
|
|
|
9459
9664
|
const body = events.map((e) => JSON.stringify(e)).join("\n") + "\n";
|
|
9460
9665
|
return {
|
|
9461
9666
|
tool: "cursor",
|
|
9462
|
-
mtime:
|
|
9667
|
+
mtime: statSync9(r.composer.globalDbPath).mtimeMs,
|
|
9463
9668
|
files: [{ name: `${r.composer.composerId}.jsonl`, bytes: Buffer.from(body, "utf8") }]
|
|
9464
9669
|
};
|
|
9465
9670
|
} catch {
|
|
@@ -9471,7 +9676,7 @@ var init_cursor = __esm({
|
|
|
9471
9676
|
if (!p)
|
|
9472
9677
|
return "";
|
|
9473
9678
|
try {
|
|
9474
|
-
return
|
|
9679
|
+
return readFileSync7(p, "utf-8");
|
|
9475
9680
|
} catch {
|
|
9476
9681
|
return "";
|
|
9477
9682
|
}
|
|
@@ -9481,159 +9686,1247 @@ var init_cursor = __esm({
|
|
|
9481
9686
|
}
|
|
9482
9687
|
});
|
|
9483
9688
|
|
|
9484
|
-
// ../engine/dist/core/backends/
|
|
9485
|
-
|
|
9486
|
-
|
|
9487
|
-
|
|
9488
|
-
|
|
9489
|
-
|
|
9490
|
-
|
|
9491
|
-
|
|
9492
|
-
|
|
9493
|
-
|
|
9494
|
-
|
|
9495
|
-
|
|
9496
|
-
|
|
9497
|
-
|
|
9498
|
-
init_codex2();
|
|
9499
|
-
init_agy();
|
|
9500
|
-
init_cursor();
|
|
9501
|
-
init_tool_backend();
|
|
9502
|
-
_setRegistryBootstrapper(bootstrapBackends);
|
|
9689
|
+
// ../engine/dist/core/backends/hermes.js
|
|
9690
|
+
import { existsSync as existsSync10 } from "fs";
|
|
9691
|
+
function firstUserPrompt(db, sessionId) {
|
|
9692
|
+
try {
|
|
9693
|
+
const row = db.prepare(`
|
|
9694
|
+
SELECT substr(content, 1, 400) AS text FROM messages
|
|
9695
|
+
WHERE session_id = ? AND role = 'user' AND active = 1 AND _compressed_summary = 0
|
|
9696
|
+
AND (display_kind IS NULL OR display_kind = 'steer')
|
|
9697
|
+
AND length(trim(content)) > 0
|
|
9698
|
+
ORDER BY id LIMIT 1
|
|
9699
|
+
`).get(sessionId);
|
|
9700
|
+
return row?.text ? flatString(row.text.slice(0, 200)) : "";
|
|
9701
|
+
} catch {
|
|
9702
|
+
return "";
|
|
9503
9703
|
}
|
|
9504
|
-
});
|
|
9505
|
-
|
|
9506
|
-
// ../engine/dist/core/session-multi-tool.js
|
|
9507
|
-
function extractTurnsAny(sessionId, opts = {}) {
|
|
9508
|
-
const backend = getBackendForId(sessionId);
|
|
9509
|
-
if (!backend)
|
|
9510
|
-
return { sessionId, found: false, turns: [], startMs: 0, endMs: 0 };
|
|
9511
|
-
return memoInSessionScope(`turns:${sessionId}:${opts.maxTurns ?? ""}:${opts.assistantMax ?? ""}:${opts.userMax ?? ""}`, () => backend.extractTurns(sessionId, opts));
|
|
9512
|
-
}
|
|
9513
|
-
function replaySessionAny(sessionId) {
|
|
9514
|
-
const backend = getBackendForId(sessionId);
|
|
9515
|
-
if (!backend)
|
|
9516
|
-
return { sessionId, found: false, projectPath: "", files: [], totalLinesAdded: 0, totalLinesRemoved: 0 };
|
|
9517
|
-
return memoInSessionScope(`replay:${sessionId}`, () => backend.replay(sessionId));
|
|
9518
9704
|
}
|
|
9519
|
-
|
|
9520
|
-
|
|
9521
|
-
|
|
9522
|
-
|
|
9523
|
-
|
|
9524
|
-
|
|
9525
|
-
|
|
9526
|
-
|
|
9527
|
-
|
|
9528
|
-
|
|
9529
|
-
|
|
9530
|
-
|
|
9531
|
-
|
|
9532
|
-
|
|
9533
|
-
|
|
9534
|
-
|
|
9535
|
-
if (m) {
|
|
9536
|
-
const text = m[0].replace(/\s+/g, " ").trim();
|
|
9537
|
-
if (text.length > 20 && text.length < 280) {
|
|
9538
|
-
out.push({ text, ts: turn.ts, tsIso: turn.tsIso, line: turn.line });
|
|
9539
|
-
break;
|
|
9540
|
-
}
|
|
9705
|
+
function messageRowsToEvents(rows) {
|
|
9706
|
+
const events = [];
|
|
9707
|
+
let line = 0;
|
|
9708
|
+
for (const r of rows) {
|
|
9709
|
+
line++;
|
|
9710
|
+
if (r.active === 0)
|
|
9711
|
+
continue;
|
|
9712
|
+
if (r.display_kind === "failed_turn" || r.display_kind === "hidden")
|
|
9713
|
+
continue;
|
|
9714
|
+
const ts = Math.round((r.timestamp || 0) * 1e3);
|
|
9715
|
+
const tsIso = ts ? new Date(ts).toISOString() : void 0;
|
|
9716
|
+
const text = (r.content ?? "").trim();
|
|
9717
|
+
if (r._compressed_summary === 1) {
|
|
9718
|
+
if (text)
|
|
9719
|
+
events.push({ kind: "summary", ts, tsIso, line, text });
|
|
9720
|
+
continue;
|
|
9541
9721
|
}
|
|
9542
|
-
|
|
9543
|
-
|
|
9544
|
-
|
|
9545
|
-
|
|
9546
|
-
|
|
9547
|
-
|
|
9548
|
-
|
|
9549
|
-
|
|
9550
|
-
|
|
9722
|
+
if (r.role === "user") {
|
|
9723
|
+
if (!text)
|
|
9724
|
+
continue;
|
|
9725
|
+
events.push({
|
|
9726
|
+
kind: "user",
|
|
9727
|
+
ts,
|
|
9728
|
+
tsIso,
|
|
9729
|
+
line,
|
|
9730
|
+
text,
|
|
9731
|
+
...r.display_kind === "process_complete" ? { origin: "task-notification" } : {}
|
|
9732
|
+
});
|
|
9733
|
+
continue;
|
|
9551
9734
|
}
|
|
9552
|
-
|
|
9553
|
-
|
|
9554
|
-
|
|
9555
|
-
|
|
9556
|
-
|
|
9557
|
-
|
|
9558
|
-
|
|
9559
|
-
|
|
9560
|
-
|
|
9735
|
+
if (r.role === "assistant") {
|
|
9736
|
+
if (text)
|
|
9737
|
+
events.push({ kind: "assistant_text", ts, tsIso, line, text });
|
|
9738
|
+
for (const call of parseToolCalls(r.tool_calls)) {
|
|
9739
|
+
const toolName = call.function?.name ?? "";
|
|
9740
|
+
const toolInput = parseArguments(call.function?.arguments);
|
|
9741
|
+
const command = toolName === "terminal" && typeof toolInput?.command === "string" ? toolInput.command : void 0;
|
|
9742
|
+
events.push({
|
|
9743
|
+
kind: "tool_use",
|
|
9744
|
+
ts,
|
|
9745
|
+
tsIso,
|
|
9746
|
+
line,
|
|
9747
|
+
toolName,
|
|
9748
|
+
toolUseId: call.id || call.call_id || `${r.id}:${toolName}`,
|
|
9749
|
+
toolInput,
|
|
9750
|
+
...command ? { command } : {}
|
|
9751
|
+
});
|
|
9752
|
+
}
|
|
9753
|
+
continue;
|
|
9561
9754
|
}
|
|
9562
|
-
|
|
9563
|
-
|
|
9564
|
-
|
|
9565
|
-
|
|
9566
|
-
|
|
9567
|
-
|
|
9568
|
-
|
|
9569
|
-
|
|
9570
|
-
|
|
9571
|
-
|
|
9572
|
-
|
|
9573
|
-
|
|
9574
|
-
|
|
9575
|
-
|
|
9576
|
-
|
|
9577
|
-
intensity: marked.intensity
|
|
9755
|
+
if (r.role === "tool") {
|
|
9756
|
+
const body = r.content ?? "";
|
|
9757
|
+
const { exitCode, isError } = toolResultStatus(body);
|
|
9758
|
+
events.push({
|
|
9759
|
+
kind: "tool_result",
|
|
9760
|
+
ts,
|
|
9761
|
+
tsIso,
|
|
9762
|
+
line,
|
|
9763
|
+
toolUseId: r.tool_call_id ?? void 0,
|
|
9764
|
+
toolName: r.tool_name ?? void 0,
|
|
9765
|
+
resultBody: body,
|
|
9766
|
+
resultIsError: isError,
|
|
9767
|
+
...exitCode !== void 0 ? { resultExitCode: exitCode } : {},
|
|
9768
|
+
resultBytes: Buffer.byteLength(body)
|
|
9769
|
+
});
|
|
9578
9770
|
}
|
|
9579
|
-
};
|
|
9580
|
-
}
|
|
9581
|
-
function classifyStatus(opts) {
|
|
9582
|
-
const lastPrompt = opts.prompts[opts.prompts.length - 1];
|
|
9583
|
-
if (lastPrompt && lastPrompt.markers.includes("interrupt")) {
|
|
9584
|
-
return { status: "interrupted", reason: "last user message was an interrupt" };
|
|
9585
|
-
}
|
|
9586
|
-
const ageHours = (Date.now() - opts.endMs) / (3600 * 1e3);
|
|
9587
|
-
if (ageHours < 2) {
|
|
9588
|
-
return { status: "in_progress", reason: "most recent activity is within the last 2 hours" };
|
|
9589
9771
|
}
|
|
9590
|
-
|
|
9591
|
-
|
|
9772
|
+
return events;
|
|
9773
|
+
}
|
|
9774
|
+
function parseToolCalls(json) {
|
|
9775
|
+
if (!json)
|
|
9776
|
+
return [];
|
|
9777
|
+
try {
|
|
9778
|
+
const calls = JSON.parse(json);
|
|
9779
|
+
return Array.isArray(calls) ? calls : [];
|
|
9780
|
+
} catch {
|
|
9781
|
+
return [];
|
|
9592
9782
|
}
|
|
9593
|
-
|
|
9594
|
-
|
|
9783
|
+
}
|
|
9784
|
+
function parseArguments(args) {
|
|
9785
|
+
if (typeof args !== "string")
|
|
9786
|
+
return args ?? {};
|
|
9787
|
+
try {
|
|
9788
|
+
return JSON.parse(args);
|
|
9789
|
+
} catch {
|
|
9790
|
+
return { raw: args };
|
|
9595
9791
|
}
|
|
9596
|
-
|
|
9597
|
-
|
|
9598
|
-
|
|
9792
|
+
}
|
|
9793
|
+
function toolResultStatus(body) {
|
|
9794
|
+
if (!body.startsWith("{"))
|
|
9795
|
+
return { isError: false };
|
|
9796
|
+
try {
|
|
9797
|
+
const r = JSON.parse(body);
|
|
9798
|
+
const exitCode = typeof r.exit_code === "number" ? r.exit_code : void 0;
|
|
9799
|
+
const hasError = r.error !== void 0 && r.error !== null && r.error !== "";
|
|
9800
|
+
return { exitCode, isError: hasError || exitCode !== void 0 && exitCode !== 0 };
|
|
9801
|
+
} catch {
|
|
9802
|
+
return { isError: false };
|
|
9599
9803
|
}
|
|
9600
|
-
return { status: "abandoned", reason: "edits made but no commits \u2014 work stayed local" };
|
|
9601
9804
|
}
|
|
9602
|
-
|
|
9603
|
-
|
|
9604
|
-
|
|
9605
|
-
|
|
9606
|
-
|
|
9607
|
-
|
|
9608
|
-
|
|
9609
|
-
|
|
9610
|
-
|
|
9611
|
-
|
|
9612
|
-
|
|
9613
|
-
|
|
9614
|
-
|
|
9615
|
-
|
|
9616
|
-
|
|
9617
|
-
|
|
9618
|
-
|
|
9619
|
-
|
|
9620
|
-
|
|
9621
|
-
|
|
9622
|
-
|
|
9623
|
-
|
|
9624
|
-
|
|
9625
|
-
|
|
9626
|
-
|
|
9627
|
-
|
|
9628
|
-
|
|
9629
|
-
|
|
9630
|
-
|
|
9631
|
-
|
|
9632
|
-
|
|
9633
|
-
|
|
9634
|
-
|
|
9635
|
-
|
|
9636
|
-
|
|
9805
|
+
var PREFIX5, MESSAGE_COLUMNS, ARCHIVED_SESSION_COLUMNS, SESSION_MTIME_SQL, HermesBackend, hermesBackend;
|
|
9806
|
+
var init_hermes = __esm({
|
|
9807
|
+
"../engine/dist/core/backends/hermes.js"() {
|
|
9808
|
+
"use strict";
|
|
9809
|
+
init_sqlite_reader();
|
|
9810
|
+
init_tool_paths();
|
|
9811
|
+
init_session_outcome();
|
|
9812
|
+
init_session_git();
|
|
9813
|
+
init_generic_engine();
|
|
9814
|
+
init_flat_string();
|
|
9815
|
+
PREFIX5 = "hermes_";
|
|
9816
|
+
MESSAGE_COLUMNS = "id, role, content, tool_calls, tool_call_id, tool_name, timestamp, display_kind, active, _compressed_summary";
|
|
9817
|
+
ARCHIVED_SESSION_COLUMNS = [
|
|
9818
|
+
"id",
|
|
9819
|
+
"source",
|
|
9820
|
+
"title",
|
|
9821
|
+
"model",
|
|
9822
|
+
"cwd",
|
|
9823
|
+
"git_branch",
|
|
9824
|
+
"git_repo_root",
|
|
9825
|
+
"parent_session_id",
|
|
9826
|
+
"started_at",
|
|
9827
|
+
"ended_at",
|
|
9828
|
+
"end_reason",
|
|
9829
|
+
"message_count",
|
|
9830
|
+
"tool_call_count",
|
|
9831
|
+
"input_tokens",
|
|
9832
|
+
"output_tokens",
|
|
9833
|
+
"cache_read_tokens",
|
|
9834
|
+
"cache_write_tokens",
|
|
9835
|
+
"reasoning_tokens",
|
|
9836
|
+
"estimated_cost_usd",
|
|
9837
|
+
"actual_cost_usd"
|
|
9838
|
+
];
|
|
9839
|
+
SESSION_MTIME_SQL = `MAX(
|
|
9840
|
+
s.started_at,
|
|
9841
|
+
COALESCE(s.ended_at, 0),
|
|
9842
|
+
COALESCE(s.last_activity_at, 0),
|
|
9843
|
+
COALESCE((SELECT MAX(m.timestamp) FROM messages m WHERE m.session_id = s.id), 0)
|
|
9844
|
+
)`;
|
|
9845
|
+
HermesBackend = class {
|
|
9846
|
+
id = "hermes";
|
|
9847
|
+
idPrefix = PREFIX5;
|
|
9848
|
+
displayName = "Hermes";
|
|
9849
|
+
homeDir() {
|
|
9850
|
+
return hermesHomeDir();
|
|
9851
|
+
}
|
|
9852
|
+
/** Every Hermes database on this machine, the root profile first. */
|
|
9853
|
+
dbPaths() {
|
|
9854
|
+
return hermesDbPaths();
|
|
9855
|
+
}
|
|
9856
|
+
isAvailable() {
|
|
9857
|
+
return this.dbPaths().some((p) => existsSync10(p));
|
|
9858
|
+
}
|
|
9859
|
+
/** The database that holds this session. A session id is unique within a
|
|
9860
|
+
* database, and each named profile has its own. */
|
|
9861
|
+
dbPathFor(rawId) {
|
|
9862
|
+
const cached = this._dbForSession.get(rawId);
|
|
9863
|
+
if (cached)
|
|
9864
|
+
return cached;
|
|
9865
|
+
for (const path of this.dbPaths()) {
|
|
9866
|
+
const db = openSqliteReadonly(path);
|
|
9867
|
+
if (!db)
|
|
9868
|
+
continue;
|
|
9869
|
+
try {
|
|
9870
|
+
if (db.prepare("SELECT 1 AS ok FROM sessions WHERE id = ?").get(rawId)) {
|
|
9871
|
+
this._dbForSession.set(rawId, path);
|
|
9872
|
+
return path;
|
|
9873
|
+
}
|
|
9874
|
+
} catch {
|
|
9875
|
+
} finally {
|
|
9876
|
+
db.close();
|
|
9877
|
+
}
|
|
9878
|
+
}
|
|
9879
|
+
return null;
|
|
9880
|
+
}
|
|
9881
|
+
_dbForSession = /* @__PURE__ */ new Map();
|
|
9882
|
+
/** Tests, and anything that adds a profile mid-process. */
|
|
9883
|
+
_clearDbRouting() {
|
|
9884
|
+
this._dbForSession.clear();
|
|
9885
|
+
}
|
|
9886
|
+
// ── ID handling ────────────────────────────────────────────────
|
|
9887
|
+
matchesId(id) {
|
|
9888
|
+
return id.startsWith(PREFIX5);
|
|
9889
|
+
}
|
|
9890
|
+
toRawId(id) {
|
|
9891
|
+
return id.startsWith(PREFIX5) ? id.slice(PREFIX5.length) : id;
|
|
9892
|
+
}
|
|
9893
|
+
toPrefixedId(rawId) {
|
|
9894
|
+
return rawId.startsWith(PREFIX5) ? rawId : PREFIX5 + rawId;
|
|
9895
|
+
}
|
|
9896
|
+
// ── Location ───────────────────────────────────────────────────
|
|
9897
|
+
findSession(id) {
|
|
9898
|
+
const rawId = this.toRawId(id);
|
|
9899
|
+
const path = this.dbPathFor(rawId);
|
|
9900
|
+
if (!path)
|
|
9901
|
+
return null;
|
|
9902
|
+
const db = openSqliteReadonly(path);
|
|
9903
|
+
if (!db)
|
|
9904
|
+
return null;
|
|
9905
|
+
try {
|
|
9906
|
+
const row = db.prepare(`
|
|
9907
|
+
SELECT s.cwd, s.git_repo_root, ${SESSION_MTIME_SQL} AS mtime
|
|
9908
|
+
FROM sessions s WHERE s.id = ?
|
|
9909
|
+
`).get(rawId);
|
|
9910
|
+
if (!row)
|
|
9911
|
+
return null;
|
|
9912
|
+
return {
|
|
9913
|
+
path,
|
|
9914
|
+
format: "sqlite",
|
|
9915
|
+
projectDir: "",
|
|
9916
|
+
projectPath: row.git_repo_root || row.cwd || "",
|
|
9917
|
+
mtime: Math.round((row.mtime || 0) * 1e3)
|
|
9918
|
+
};
|
|
9919
|
+
} catch {
|
|
9920
|
+
return null;
|
|
9921
|
+
} finally {
|
|
9922
|
+
db.close();
|
|
9923
|
+
}
|
|
9924
|
+
}
|
|
9925
|
+
listSessions(opts = {}) {
|
|
9926
|
+
const byId = /* @__PURE__ */ new Map();
|
|
9927
|
+
for (const dbPath of this.dbPaths()) {
|
|
9928
|
+
if (!existsSync10(dbPath))
|
|
9929
|
+
continue;
|
|
9930
|
+
for (const ref of this.listSessionsInDb(dbPath, opts)) {
|
|
9931
|
+
const prior = byId.get(ref.rawId);
|
|
9932
|
+
if (!prior || ref.mtime > prior.mtime)
|
|
9933
|
+
byId.set(ref.rawId, ref);
|
|
9934
|
+
}
|
|
9935
|
+
}
|
|
9936
|
+
const all = [...byId.values()].sort((a, b) => b.mtime - a.mtime);
|
|
9937
|
+
return opts.limit ? all.slice(0, opts.limit) : all;
|
|
9938
|
+
}
|
|
9939
|
+
listSessionsInDb(dbPath, opts) {
|
|
9940
|
+
const cutoffSec = (opts.sinceMs ?? 0) / 1e3;
|
|
9941
|
+
const filter = opts.projectFilter?.toLowerCase();
|
|
9942
|
+
const db = openSqliteReadonly(dbPath);
|
|
9943
|
+
if (!db)
|
|
9944
|
+
return [];
|
|
9945
|
+
try {
|
|
9946
|
+
const rows = db.prepare(`
|
|
9947
|
+
SELECT * FROM (
|
|
9948
|
+
SELECT s.id, s.cwd, s.git_repo_root, s.started_at, s.message_count,
|
|
9949
|
+
${SESSION_MTIME_SQL} AS mtime
|
|
9950
|
+
FROM sessions s
|
|
9951
|
+
) WHERE mtime >= ? ORDER BY mtime DESC
|
|
9952
|
+
`).all(cutoffSec);
|
|
9953
|
+
const out = [];
|
|
9954
|
+
for (const row of rows) {
|
|
9955
|
+
const projectPath = row.git_repo_root || row.cwd || "";
|
|
9956
|
+
if (filter && !projectPath.toLowerCase().includes(filter))
|
|
9957
|
+
continue;
|
|
9958
|
+
const mtime = Math.round(row.mtime * 1e3);
|
|
9959
|
+
this._dbForSession.set(row.id, dbPath);
|
|
9960
|
+
out.push({
|
|
9961
|
+
toolId: "hermes",
|
|
9962
|
+
rawId: row.id,
|
|
9963
|
+
prefixedId: this.toPrefixedId(row.id),
|
|
9964
|
+
projectPath,
|
|
9965
|
+
projectDir: "",
|
|
9966
|
+
fullPath: dbPath,
|
|
9967
|
+
created: new Date(Math.round(row.started_at * 1e3)).toISOString(),
|
|
9968
|
+
modified: new Date(mtime).toISOString(),
|
|
9969
|
+
mtime,
|
|
9970
|
+
firstPrompt: opts.previews === false ? "" : firstUserPrompt(db, row.id),
|
|
9971
|
+
messageCount: opts.previews === false ? 0 : row.message_count ?? 0
|
|
9972
|
+
});
|
|
9973
|
+
if (opts.limit && out.length >= opts.limit)
|
|
9974
|
+
break;
|
|
9975
|
+
}
|
|
9976
|
+
return out;
|
|
9977
|
+
} catch {
|
|
9978
|
+
return [];
|
|
9979
|
+
} finally {
|
|
9980
|
+
db.close();
|
|
9981
|
+
}
|
|
9982
|
+
}
|
|
9983
|
+
/** Hermes writes a title for each session (a model-written one, or the
|
|
9984
|
+
* first prompt for a one-shot run). */
|
|
9985
|
+
getNativeTitle(rawId) {
|
|
9986
|
+
const path = this.dbPathFor(this.toRawId(rawId));
|
|
9987
|
+
if (!path)
|
|
9988
|
+
return null;
|
|
9989
|
+
const db = openSqliteReadonly(path);
|
|
9990
|
+
if (!db)
|
|
9991
|
+
return null;
|
|
9992
|
+
try {
|
|
9993
|
+
const row = db.prepare("SELECT title FROM sessions WHERE id = ?").get(this.toRawId(rawId));
|
|
9994
|
+
const t = row?.title?.trim();
|
|
9995
|
+
return t ? t.slice(0, 200) : null;
|
|
9996
|
+
} catch {
|
|
9997
|
+
return null;
|
|
9998
|
+
} finally {
|
|
9999
|
+
db.close();
|
|
10000
|
+
}
|
|
10001
|
+
}
|
|
10002
|
+
// ── Generic-engine inputs ───────────────────────────────────────
|
|
10003
|
+
fileToolMap = {
|
|
10004
|
+
write_file: "write",
|
|
10005
|
+
patch: "edit",
|
|
10006
|
+
read_file: "read"
|
|
10007
|
+
};
|
|
10008
|
+
/**
|
|
10009
|
+
* `write_file` carries `{ path, content }`. `patch` carries
|
|
10010
|
+
* `{ mode: 'replace', path, old_string, new_string }`; its other mode,
|
|
10011
|
+
* `patch`, is a multi-file V4A patch with no single before and after.
|
|
10012
|
+
*/
|
|
10013
|
+
extractEditDelta(toolName, input) {
|
|
10014
|
+
if (input == null || typeof input !== "object")
|
|
10015
|
+
return null;
|
|
10016
|
+
const inp = input;
|
|
10017
|
+
if (toolName === "write_file") {
|
|
10018
|
+
return { before: "", after: typeof inp.content === "string" ? inp.content : null };
|
|
10019
|
+
}
|
|
10020
|
+
if (toolName === "patch" && (inp.mode === void 0 || inp.mode === "replace")) {
|
|
10021
|
+
const before = typeof inp.old_string === "string" ? inp.old_string : null;
|
|
10022
|
+
const after = typeof inp.new_string === "string" ? inp.new_string : null;
|
|
10023
|
+
if (before === null && after === null)
|
|
10024
|
+
return null;
|
|
10025
|
+
return { before, after };
|
|
10026
|
+
}
|
|
10027
|
+
return null;
|
|
10028
|
+
}
|
|
10029
|
+
readEvents(rawId) {
|
|
10030
|
+
const id = this.toRawId(rawId);
|
|
10031
|
+
const path = this.dbPathFor(id);
|
|
10032
|
+
if (!path)
|
|
10033
|
+
return [];
|
|
10034
|
+
const db = openSqliteReadonly(path);
|
|
10035
|
+
if (!db)
|
|
10036
|
+
return [];
|
|
10037
|
+
try {
|
|
10038
|
+
const rows = db.prepare(`SELECT ${MESSAGE_COLUMNS} FROM messages WHERE session_id = ? ORDER BY id`).all(id);
|
|
10039
|
+
return messageRowsToEvents(rows);
|
|
10040
|
+
} catch {
|
|
10041
|
+
return [];
|
|
10042
|
+
} finally {
|
|
10043
|
+
db.close();
|
|
10044
|
+
}
|
|
10045
|
+
}
|
|
10046
|
+
/**
|
|
10047
|
+
* Events from the dump `exportRawSession` writes, for a session rebuilt from
|
|
10048
|
+
* the server's archive. The archive joins the lines of every dump it got, so
|
|
10049
|
+
* a row that changed (a rewind sets `active` to 0) is there more than once.
|
|
10050
|
+
* The last copy of each row id is the newest.
|
|
10051
|
+
*/
|
|
10052
|
+
readEventsFromText(text) {
|
|
10053
|
+
const rows = /* @__PURE__ */ new Map();
|
|
10054
|
+
for (const line of text.split("\n")) {
|
|
10055
|
+
if (!line.trim())
|
|
10056
|
+
continue;
|
|
10057
|
+
try {
|
|
10058
|
+
const rec = JSON.parse(line);
|
|
10059
|
+
if (rec.kind === "message" && rec.row) {
|
|
10060
|
+
rows.delete(rec.row.id);
|
|
10061
|
+
rows.set(rec.row.id, rec.row);
|
|
10062
|
+
}
|
|
10063
|
+
} catch {
|
|
10064
|
+
}
|
|
10065
|
+
}
|
|
10066
|
+
return messageRowsToEvents([...rows.values()].sort((a, b) => a.id - b.id));
|
|
10067
|
+
}
|
|
10068
|
+
// ── Per-session operations — all delegate to the generic engine ─
|
|
10069
|
+
extractTurns(id, opts = {}) {
|
|
10070
|
+
return extractTurnsFromEvents(this.toPrefixedId(id), this.readEvents(id), opts);
|
|
10071
|
+
}
|
|
10072
|
+
liveScanEdits(id) {
|
|
10073
|
+
const located = this.findSession(id);
|
|
10074
|
+
if (!located) {
|
|
10075
|
+
return { found: false, projectPath: "", projectDir: "", edits: [], fileMtime: 0, tool: "hermes" };
|
|
10076
|
+
}
|
|
10077
|
+
return liveScanEditsFromEvents(this.readEvents(id), this.fileToolMap, {
|
|
10078
|
+
sessionId: this.toPrefixedId(id),
|
|
10079
|
+
tool: "hermes",
|
|
10080
|
+
projectPath: located.projectPath,
|
|
10081
|
+
projectDir: located.projectDir,
|
|
10082
|
+
fileMtime: located.mtime,
|
|
10083
|
+
found: true
|
|
10084
|
+
});
|
|
10085
|
+
}
|
|
10086
|
+
replay(id) {
|
|
10087
|
+
const located = this.findSession(id);
|
|
10088
|
+
if (!located) {
|
|
10089
|
+
return { sessionId: this.toPrefixedId(id), found: false, projectPath: "", files: [], totalLinesAdded: 0, totalLinesRemoved: 0 };
|
|
10090
|
+
}
|
|
10091
|
+
return replayFromEvents(this.toPrefixedId(id), this.readEvents(id), this.fileToolMap, this.extractEditDelta.bind(this), {
|
|
10092
|
+
projectPath: located.projectPath,
|
|
10093
|
+
found: true
|
|
10094
|
+
});
|
|
10095
|
+
}
|
|
10096
|
+
computeOutcome(id, opts) {
|
|
10097
|
+
return computeOutcome(this.toPrefixedId(id), opts);
|
|
10098
|
+
}
|
|
10099
|
+
/** File edits since `sinceMs` across the sessions active since then. */
|
|
10100
|
+
collectRecentEdits(opts) {
|
|
10101
|
+
const refs = this.listSessions({ sinceMs: opts.sinceMs, projectFilter: opts.projectFilter, previews: false });
|
|
10102
|
+
const edits = [];
|
|
10103
|
+
for (const ref of opts.limitSessions ? refs.slice(0, opts.limitSessions) : refs) {
|
|
10104
|
+
for (const e of this.liveScanEdits(ref.rawId).edits) {
|
|
10105
|
+
if (e.ts >= opts.sinceMs)
|
|
10106
|
+
edits.push(e);
|
|
10107
|
+
}
|
|
10108
|
+
}
|
|
10109
|
+
return edits.sort((a, b) => b.ts - a.ts);
|
|
10110
|
+
}
|
|
10111
|
+
getCommits(id, files, startMs, endMs, bufferMinutes) {
|
|
10112
|
+
return getSessionCommits(this.toPrefixedId(id), files, startMs, endMs, bufferMinutes);
|
|
10113
|
+
}
|
|
10114
|
+
/**
|
|
10115
|
+
* The session row and its messages as JSONL, one row per line in id order.
|
|
10116
|
+
* Hermes rewrites and compacts its database, so the archive keeps the rows
|
|
10117
|
+
* as they were synced.
|
|
10118
|
+
*/
|
|
10119
|
+
exportRawSession(id) {
|
|
10120
|
+
const rawId = this.toRawId(id);
|
|
10121
|
+
const path = this.dbPathFor(rawId);
|
|
10122
|
+
if (!path)
|
|
10123
|
+
return null;
|
|
10124
|
+
const db = openSqliteReadonly(path);
|
|
10125
|
+
if (!db)
|
|
10126
|
+
return null;
|
|
10127
|
+
try {
|
|
10128
|
+
const have = new Set(db.prepare(`SELECT name FROM pragma_table_info('sessions')`).all().map((c) => c.name));
|
|
10129
|
+
const cols = ARCHIVED_SESSION_COLUMNS.filter((c) => have.has(c)).map((c) => `s.${c}`);
|
|
10130
|
+
const session = db.prepare(`
|
|
10131
|
+
SELECT ${[...cols, `${SESSION_MTIME_SQL} AS mtime`].join(", ")}
|
|
10132
|
+
FROM sessions s WHERE s.id = ?
|
|
10133
|
+
`).get(rawId);
|
|
10134
|
+
if (!session)
|
|
10135
|
+
return null;
|
|
10136
|
+
const rows = db.prepare(`SELECT ${MESSAGE_COLUMNS} FROM messages WHERE session_id = ? ORDER BY id`).all(rawId);
|
|
10137
|
+
const lines = [JSON.stringify({ kind: "session", row: session })];
|
|
10138
|
+
for (const r of rows)
|
|
10139
|
+
lines.push(JSON.stringify({ kind: "message", row: r }));
|
|
10140
|
+
return {
|
|
10141
|
+
tool: "hermes",
|
|
10142
|
+
mtime: Math.round(Number(session.mtime || 0) * 1e3),
|
|
10143
|
+
files: [{ name: `${rawId}.dump.jsonl`, bytes: Buffer.from(lines.join("\n") + "\n", "utf-8") }]
|
|
10144
|
+
};
|
|
10145
|
+
} catch {
|
|
10146
|
+
return null;
|
|
10147
|
+
} finally {
|
|
10148
|
+
db.close();
|
|
10149
|
+
}
|
|
10150
|
+
}
|
|
10151
|
+
};
|
|
10152
|
+
hermesBackend = new HermesBackend();
|
|
10153
|
+
}
|
|
10154
|
+
});
|
|
10155
|
+
|
|
10156
|
+
// ../engine/dist/core/backends/pi.js
|
|
10157
|
+
import { readdirSync as readdirSync7, readFileSync as readFileSync8, statSync as statSync10, openSync as openSync3, readSync as readSync3, closeSync as closeSync3 } from "fs";
|
|
10158
|
+
import { createHash } from "crypto";
|
|
10159
|
+
import { basename as basename6, join as join11 } from "path";
|
|
10160
|
+
function parsePiFile(path) {
|
|
10161
|
+
let raw;
|
|
10162
|
+
try {
|
|
10163
|
+
raw = readFileSync8(path, "utf-8");
|
|
10164
|
+
} catch {
|
|
10165
|
+
return { header: null, entries: [] };
|
|
10166
|
+
}
|
|
10167
|
+
const out = { header: null, entries: [] };
|
|
10168
|
+
for (const line of raw.split("\n")) {
|
|
10169
|
+
if (!line.trim())
|
|
10170
|
+
continue;
|
|
10171
|
+
let obj;
|
|
10172
|
+
try {
|
|
10173
|
+
obj = JSON.parse(line);
|
|
10174
|
+
} catch {
|
|
10175
|
+
continue;
|
|
10176
|
+
}
|
|
10177
|
+
if (!obj || typeof obj !== "object")
|
|
10178
|
+
continue;
|
|
10179
|
+
if (obj.type === "session" && !out.header) {
|
|
10180
|
+
out.header = obj;
|
|
10181
|
+
continue;
|
|
10182
|
+
}
|
|
10183
|
+
out.entries.push(obj);
|
|
10184
|
+
}
|
|
10185
|
+
return out;
|
|
10186
|
+
}
|
|
10187
|
+
function idFromFilename(file) {
|
|
10188
|
+
if (!file.endsWith(".jsonl"))
|
|
10189
|
+
return null;
|
|
10190
|
+
const stem = file.slice(0, -".jsonl".length);
|
|
10191
|
+
const at = stem.indexOf("_");
|
|
10192
|
+
const id = at === -1 ? stem : stem.slice(at + 1);
|
|
10193
|
+
return id || null;
|
|
10194
|
+
}
|
|
10195
|
+
function* walkSessionFiles(root, depth = 2) {
|
|
10196
|
+
if (depth < 0)
|
|
10197
|
+
return;
|
|
10198
|
+
let entries;
|
|
10199
|
+
try {
|
|
10200
|
+
entries = readdirSync7(root, { withFileTypes: true });
|
|
10201
|
+
} catch {
|
|
10202
|
+
return;
|
|
10203
|
+
}
|
|
10204
|
+
for (const entry of entries) {
|
|
10205
|
+
if (entry.name.startsWith("."))
|
|
10206
|
+
continue;
|
|
10207
|
+
const path = join11(root, entry.name);
|
|
10208
|
+
if (entry.isDirectory()) {
|
|
10209
|
+
yield* walkSessionFiles(path, depth - 1);
|
|
10210
|
+
continue;
|
|
10211
|
+
}
|
|
10212
|
+
if (entry.isFile() && entry.name.endsWith(".jsonl"))
|
|
10213
|
+
yield path;
|
|
10214
|
+
}
|
|
10215
|
+
}
|
|
10216
|
+
function readPiHeader(path) {
|
|
10217
|
+
let fd = null;
|
|
10218
|
+
try {
|
|
10219
|
+
fd = openSync3(path, "r");
|
|
10220
|
+
const buf = Buffer.alloc(8192);
|
|
10221
|
+
const n = readSync3(fd, buf, 0, buf.length, 0);
|
|
10222
|
+
const firstLine = buf.toString("utf-8", 0, n).split("\n", 1)[0];
|
|
10223
|
+
if (!firstLine.trim())
|
|
10224
|
+
return null;
|
|
10225
|
+
const obj = JSON.parse(firstLine);
|
|
10226
|
+
if (!obj || typeof obj !== "object" || obj.type !== "session")
|
|
10227
|
+
return null;
|
|
10228
|
+
return obj;
|
|
10229
|
+
} catch {
|
|
10230
|
+
return null;
|
|
10231
|
+
} finally {
|
|
10232
|
+
if (fd !== null) {
|
|
10233
|
+
try {
|
|
10234
|
+
closeSync3(fd);
|
|
10235
|
+
} catch {
|
|
10236
|
+
}
|
|
10237
|
+
}
|
|
10238
|
+
}
|
|
10239
|
+
}
|
|
10240
|
+
function dirSignature(root) {
|
|
10241
|
+
const parts = [];
|
|
10242
|
+
try {
|
|
10243
|
+
parts.push(String(statSync10(root).mtimeMs));
|
|
10244
|
+
} catch {
|
|
10245
|
+
return "missing";
|
|
10246
|
+
}
|
|
10247
|
+
let entries = [];
|
|
10248
|
+
try {
|
|
10249
|
+
entries = readdirSync7(root, { withFileTypes: true });
|
|
10250
|
+
} catch {
|
|
10251
|
+
return parts.join(",");
|
|
10252
|
+
}
|
|
10253
|
+
for (const entry of entries) {
|
|
10254
|
+
if (!entry.isDirectory())
|
|
10255
|
+
continue;
|
|
10256
|
+
try {
|
|
10257
|
+
parts.push(`${entry.name}:${statSync10(join11(root, entry.name)).mtimeMs}`);
|
|
10258
|
+
} catch {
|
|
10259
|
+
}
|
|
10260
|
+
}
|
|
10261
|
+
parts.sort();
|
|
10262
|
+
return parts.join(",");
|
|
10263
|
+
}
|
|
10264
|
+
function piSessionKey(file) {
|
|
10265
|
+
return `${file.id}~${projectSuffix(file.projectPath || file.projectDir)}`;
|
|
10266
|
+
}
|
|
10267
|
+
function projectSuffix(projectDir) {
|
|
10268
|
+
return createHash("sha256").update(projectDir).digest("hex").slice(0, 8);
|
|
10269
|
+
}
|
|
10270
|
+
function mtimeOf(path, fallback) {
|
|
10271
|
+
try {
|
|
10272
|
+
return statSync10(path).mtimeMs;
|
|
10273
|
+
} catch {
|
|
10274
|
+
return fallback;
|
|
10275
|
+
}
|
|
10276
|
+
}
|
|
10277
|
+
function piText(content) {
|
|
10278
|
+
if (typeof content === "string")
|
|
10279
|
+
return content;
|
|
10280
|
+
if (!Array.isArray(content))
|
|
10281
|
+
return "";
|
|
10282
|
+
const parts = [];
|
|
10283
|
+
for (const block of content) {
|
|
10284
|
+
if (!block || typeof block !== "object")
|
|
10285
|
+
continue;
|
|
10286
|
+
const b = block;
|
|
10287
|
+
if (b.type === "text" && typeof b.text === "string")
|
|
10288
|
+
parts.push(b.text);
|
|
10289
|
+
}
|
|
10290
|
+
return parts.join("\n");
|
|
10291
|
+
}
|
|
10292
|
+
function piToolInput(args) {
|
|
10293
|
+
return args && typeof args === "object" && !Array.isArray(args) ? { ...args } : {};
|
|
10294
|
+
}
|
|
10295
|
+
var PREFIX6, PiBackend, piBackend;
|
|
10296
|
+
var init_pi = __esm({
|
|
10297
|
+
"../engine/dist/core/backends/pi.js"() {
|
|
10298
|
+
"use strict";
|
|
10299
|
+
init_tail_read();
|
|
10300
|
+
init_tool_paths();
|
|
10301
|
+
init_session_outcome();
|
|
10302
|
+
init_session_git();
|
|
10303
|
+
init_generic_engine();
|
|
10304
|
+
init_flat_string();
|
|
10305
|
+
PREFIX6 = "pi_";
|
|
10306
|
+
PiBackend = class {
|
|
10307
|
+
id = "pi";
|
|
10308
|
+
idPrefix = PREFIX6;
|
|
10309
|
+
displayName = "Pi";
|
|
10310
|
+
homeDir() {
|
|
10311
|
+
return piHomeDir();
|
|
10312
|
+
}
|
|
10313
|
+
/** Session root(s) — Pi has a single agent dir, so this is one entry. */
|
|
10314
|
+
sessionDirs() {
|
|
10315
|
+
return piSessionDirs();
|
|
10316
|
+
}
|
|
10317
|
+
skillsDir() {
|
|
10318
|
+
return piSkillsDir();
|
|
10319
|
+
}
|
|
10320
|
+
promptsDir() {
|
|
10321
|
+
return piPromptsDir();
|
|
10322
|
+
}
|
|
10323
|
+
mcpConfigPath() {
|
|
10324
|
+
return piMcpConfigPath();
|
|
10325
|
+
}
|
|
10326
|
+
isAvailable() {
|
|
10327
|
+
return this.sessionDirs().length > 0;
|
|
10328
|
+
}
|
|
10329
|
+
// ── ID handling ────────────────────────────────────────────────
|
|
10330
|
+
matchesId(id) {
|
|
10331
|
+
return id.startsWith(PREFIX6);
|
|
10332
|
+
}
|
|
10333
|
+
/**
|
|
10334
|
+
* A Pi id can itself start with `<prefix>` (`--session-id` allows `_`), so the
|
|
10335
|
+
* bare prefix does not identify a stored id. The index does.
|
|
10336
|
+
*/
|
|
10337
|
+
toRawId(id) {
|
|
10338
|
+
if (!id.startsWith(PREFIX6))
|
|
10339
|
+
return id;
|
|
10340
|
+
if (this.fileIndex().has(id))
|
|
10341
|
+
return id;
|
|
10342
|
+
return id.slice(PREFIX6.length);
|
|
10343
|
+
}
|
|
10344
|
+
toPrefixedId(rawId) {
|
|
10345
|
+
if (!rawId.startsWith(PREFIX6))
|
|
10346
|
+
return PREFIX6 + rawId;
|
|
10347
|
+
return this.fileIndex().has(rawId) ? PREFIX6 + rawId : rawId;
|
|
10348
|
+
}
|
|
10349
|
+
// ── Location ───────────────────────────────────────────────────
|
|
10350
|
+
/**
|
|
10351
|
+
* Session key → file path for every session on this machine.
|
|
10352
|
+
*
|
|
10353
|
+
* The entry list is cached on the project dirs. When two files carry one key
|
|
10354
|
+
* — a copy of a session — this stats them on every call, because an append to
|
|
10355
|
+
* the live file changes its mtime and not its folder.
|
|
10356
|
+
*/
|
|
10357
|
+
fileIndex() {
|
|
10358
|
+
const roots = this.sessionDirs();
|
|
10359
|
+
const signature = roots.map(dirSignature).join("|");
|
|
10360
|
+
if (this._index?.signature !== signature) {
|
|
10361
|
+
const entries2 = [];
|
|
10362
|
+
for (const root of roots) {
|
|
10363
|
+
for (const path of walkSessionFiles(root)) {
|
|
10364
|
+
let mtime = 0;
|
|
10365
|
+
try {
|
|
10366
|
+
mtime = statSync10(path).mtimeMs;
|
|
10367
|
+
} catch {
|
|
10368
|
+
continue;
|
|
10369
|
+
}
|
|
10370
|
+
const header = readPiHeader(path);
|
|
10371
|
+
const id = header ? header.id || idFromFilename(basename6(path)) : "";
|
|
10372
|
+
entries2.push({
|
|
10373
|
+
id: id || "",
|
|
10374
|
+
projectPath: header?.cwd || "",
|
|
10375
|
+
projectDir: basename6(join11(path, "..")),
|
|
10376
|
+
mtime,
|
|
10377
|
+
path
|
|
10378
|
+
});
|
|
10379
|
+
}
|
|
10380
|
+
}
|
|
10381
|
+
this._index = { signature, entries: entries2 };
|
|
10382
|
+
}
|
|
10383
|
+
const entries = this._index.entries;
|
|
10384
|
+
const seen = /* @__PURE__ */ new Map();
|
|
10385
|
+
for (const entry of entries) {
|
|
10386
|
+
if (!entry.id) {
|
|
10387
|
+
const header = readPiHeader(entry.path);
|
|
10388
|
+
if (!header)
|
|
10389
|
+
continue;
|
|
10390
|
+
entry.id = header.id || idFromFilename(basename6(entry.path)) || "";
|
|
10391
|
+
entry.projectPath = header.cwd || "";
|
|
10392
|
+
if (!entry.id)
|
|
10393
|
+
continue;
|
|
10394
|
+
}
|
|
10395
|
+
const key = piSessionKey(entry);
|
|
10396
|
+
const prior = seen.get(key);
|
|
10397
|
+
if (!prior) {
|
|
10398
|
+
seen.set(key, entry);
|
|
10399
|
+
continue;
|
|
10400
|
+
}
|
|
10401
|
+
const priorMtime = mtimeOf(prior.path, prior.mtime);
|
|
10402
|
+
const entryMtime = mtimeOf(entry.path, entry.mtime);
|
|
10403
|
+
if (entryMtime > priorMtime)
|
|
10404
|
+
seen.set(key, entry);
|
|
10405
|
+
}
|
|
10406
|
+
const files = /* @__PURE__ */ new Map();
|
|
10407
|
+
for (const [key, file] of seen)
|
|
10408
|
+
files.set(key, file.path);
|
|
10409
|
+
return files;
|
|
10410
|
+
}
|
|
10411
|
+
_index = null;
|
|
10412
|
+
findSession(id) {
|
|
10413
|
+
const rawId = this.toRawId(id);
|
|
10414
|
+
const path = this.fileIndex().get(rawId);
|
|
10415
|
+
if (!path)
|
|
10416
|
+
return null;
|
|
10417
|
+
const header = readPiHeader(path);
|
|
10418
|
+
if (!header)
|
|
10419
|
+
return null;
|
|
10420
|
+
let mtime = 0;
|
|
10421
|
+
try {
|
|
10422
|
+
mtime = statSync10(path).mtimeMs;
|
|
10423
|
+
} catch {
|
|
10424
|
+
}
|
|
10425
|
+
return {
|
|
10426
|
+
path,
|
|
10427
|
+
format: "jsonl",
|
|
10428
|
+
projectDir: basename6(join11(path, "..")),
|
|
10429
|
+
projectPath: header.cwd || "",
|
|
10430
|
+
mtime
|
|
10431
|
+
};
|
|
10432
|
+
}
|
|
10433
|
+
listSessions(opts = {}) {
|
|
10434
|
+
const cutoff = opts.sinceMs ?? 0;
|
|
10435
|
+
const filter = opts.projectFilter?.toLowerCase();
|
|
10436
|
+
const out = [];
|
|
10437
|
+
for (const [rawId, path] of this.fileIndex()) {
|
|
10438
|
+
let stat;
|
|
10439
|
+
try {
|
|
10440
|
+
stat = statSync10(path);
|
|
10441
|
+
} catch {
|
|
10442
|
+
continue;
|
|
10443
|
+
}
|
|
10444
|
+
if (stat.mtimeMs < cutoff)
|
|
10445
|
+
continue;
|
|
10446
|
+
const header = readPiHeader(path);
|
|
10447
|
+
if (!header)
|
|
10448
|
+
continue;
|
|
10449
|
+
const projectPath = header.cwd || "";
|
|
10450
|
+
if (filter && !projectPath.toLowerCase().includes(filter))
|
|
10451
|
+
continue;
|
|
10452
|
+
let firstPrompt = "";
|
|
10453
|
+
let messageCount = 0;
|
|
10454
|
+
if (opts.previews !== false) {
|
|
10455
|
+
const parsed = parsePiFile(path);
|
|
10456
|
+
for (const entry of parsed.entries) {
|
|
10457
|
+
if (entry.type !== "message")
|
|
10458
|
+
continue;
|
|
10459
|
+
const role = entry.message?.role;
|
|
10460
|
+
if (role !== "user" && role !== "assistant")
|
|
10461
|
+
continue;
|
|
10462
|
+
messageCount++;
|
|
10463
|
+
if (!firstPrompt && role === "user") {
|
|
10464
|
+
const text = piText(entry.message?.content).trim();
|
|
10465
|
+
if (text)
|
|
10466
|
+
firstPrompt = flatString(text.slice(0, 200));
|
|
10467
|
+
}
|
|
10468
|
+
}
|
|
10469
|
+
}
|
|
10470
|
+
out.push({
|
|
10471
|
+
toolId: "pi",
|
|
10472
|
+
rawId,
|
|
10473
|
+
prefixedId: this.toPrefixedId(rawId),
|
|
10474
|
+
projectPath,
|
|
10475
|
+
projectDir: basename6(join11(path, "..")),
|
|
10476
|
+
fullPath: path,
|
|
10477
|
+
created: header.timestamp || stat.birthtime.toISOString(),
|
|
10478
|
+
modified: stat.mtime.toISOString(),
|
|
10479
|
+
mtime: stat.mtimeMs,
|
|
10480
|
+
firstPrompt,
|
|
10481
|
+
messageCount
|
|
10482
|
+
});
|
|
10483
|
+
}
|
|
10484
|
+
out.sort((a, b) => b.mtime - a.mtime);
|
|
10485
|
+
return opts.limit ? out.slice(0, opts.limit) : out;
|
|
10486
|
+
}
|
|
10487
|
+
// ── Generic-engine inputs ───────────────────────────────────────
|
|
10488
|
+
/**
|
|
10489
|
+
* The file tools and their EditOp. A shell command carries no path, so it
|
|
10490
|
+
* produces no file entry.
|
|
10491
|
+
*/
|
|
10492
|
+
fileToolMap = {
|
|
10493
|
+
write: "write",
|
|
10494
|
+
edit: "edit",
|
|
10495
|
+
read: "read"
|
|
10496
|
+
};
|
|
10497
|
+
/**
|
|
10498
|
+
* The inline diff of a file tool.
|
|
10499
|
+
* - write: { path, content }.
|
|
10500
|
+
* - edit: { path, edits: [{ oldText, newText }] }.
|
|
10501
|
+
*
|
|
10502
|
+
* An edit holds several disjoint replacements. Join them into one pair.
|
|
10503
|
+
*/
|
|
10504
|
+
extractEditDelta(toolName, input) {
|
|
10505
|
+
if (input == null || typeof input !== "object")
|
|
10506
|
+
return null;
|
|
10507
|
+
const inp = input;
|
|
10508
|
+
if (toolName === "write") {
|
|
10509
|
+
const after = typeof inp.content === "string" ? inp.content : null;
|
|
10510
|
+
if (after === null)
|
|
10511
|
+
return null;
|
|
10512
|
+
return { before: "", after };
|
|
10513
|
+
}
|
|
10514
|
+
if (toolName === "edit") {
|
|
10515
|
+
const edits = Array.isArray(inp.edits) ? inp.edits : [];
|
|
10516
|
+
let before = "";
|
|
10517
|
+
let after = "";
|
|
10518
|
+
for (const raw of edits) {
|
|
10519
|
+
if (!raw || typeof raw !== "object")
|
|
10520
|
+
continue;
|
|
10521
|
+
const e = raw;
|
|
10522
|
+
if (typeof e.oldText === "string")
|
|
10523
|
+
before += (before ? "\n" : "") + e.oldText;
|
|
10524
|
+
if (typeof e.newText === "string")
|
|
10525
|
+
after += (after ? "\n" : "") + e.newText;
|
|
10526
|
+
}
|
|
10527
|
+
if (!before && !after)
|
|
10528
|
+
return null;
|
|
10529
|
+
return { before: before || null, after: after || null };
|
|
10530
|
+
}
|
|
10531
|
+
return null;
|
|
10532
|
+
}
|
|
10533
|
+
readEvents(rawId) {
|
|
10534
|
+
const located = this.findSession(rawId);
|
|
10535
|
+
if (!located)
|
|
10536
|
+
return [];
|
|
10537
|
+
let rawText;
|
|
10538
|
+
try {
|
|
10539
|
+
rawText = readFileSync8(located.path, "utf-8");
|
|
10540
|
+
} catch {
|
|
10541
|
+
return [];
|
|
10542
|
+
}
|
|
10543
|
+
return this.readEventsFromText(rawText, located.mtime);
|
|
10544
|
+
}
|
|
10545
|
+
/**
|
|
10546
|
+
* Parse Pi session text into canonical events. Split out from readEvents so
|
|
10547
|
+
* the same parser runs on archived/shadow container bytes.
|
|
10548
|
+
*/
|
|
10549
|
+
readEventsFromText(rawText, mtimeFallback = 0) {
|
|
10550
|
+
const events = [];
|
|
10551
|
+
let lineNum = 0;
|
|
10552
|
+
for (const line of rawText.split("\n")) {
|
|
10553
|
+
lineNum++;
|
|
10554
|
+
if (!line.trim())
|
|
10555
|
+
continue;
|
|
10556
|
+
let obj;
|
|
10557
|
+
try {
|
|
10558
|
+
obj = JSON.parse(line);
|
|
10559
|
+
} catch {
|
|
10560
|
+
continue;
|
|
10561
|
+
}
|
|
10562
|
+
const tsIso = typeof obj.timestamp === "string" ? obj.timestamp : void 0;
|
|
10563
|
+
const msgTs = typeof obj.message?.timestamp === "number" ? obj.message.timestamp : void 0;
|
|
10564
|
+
const ts = tsIso ? Date.parse(tsIso) || mtimeFallback : msgTs ?? mtimeFallback;
|
|
10565
|
+
if (obj.type === "message") {
|
|
10566
|
+
const msg = obj.message || {};
|
|
10567
|
+
const role = msg.role;
|
|
10568
|
+
if (role === "user") {
|
|
10569
|
+
const text = piText(msg.content).trim();
|
|
10570
|
+
if (text)
|
|
10571
|
+
events.push({ kind: "user", ts, tsIso, line: lineNum, text });
|
|
10572
|
+
continue;
|
|
10573
|
+
}
|
|
10574
|
+
if (role === "assistant") {
|
|
10575
|
+
const text = piText(msg.content).trim();
|
|
10576
|
+
if (text)
|
|
10577
|
+
events.push({ kind: "assistant_text", ts, tsIso, line: lineNum, text });
|
|
10578
|
+
const blocks = Array.isArray(msg.content) ? msg.content : [];
|
|
10579
|
+
for (const block of blocks) {
|
|
10580
|
+
if (!block || typeof block !== "object" || block.type !== "toolCall")
|
|
10581
|
+
continue;
|
|
10582
|
+
const toolName = String(block.name || "");
|
|
10583
|
+
if (!toolName)
|
|
10584
|
+
continue;
|
|
10585
|
+
const input = piToolInput(block.arguments);
|
|
10586
|
+
const command = toolName === "bash" || toolName === "powershell" ? typeof input.command === "string" ? input.command : void 0 : void 0;
|
|
10587
|
+
events.push({
|
|
10588
|
+
kind: "tool_use",
|
|
10589
|
+
ts,
|
|
10590
|
+
tsIso,
|
|
10591
|
+
line: lineNum,
|
|
10592
|
+
toolName,
|
|
10593
|
+
toolUseId: typeof block.id === "string" ? block.id : "",
|
|
10594
|
+
toolInput: input,
|
|
10595
|
+
command
|
|
10596
|
+
});
|
|
10597
|
+
}
|
|
10598
|
+
continue;
|
|
10599
|
+
}
|
|
10600
|
+
if (role === "toolResult") {
|
|
10601
|
+
const body = piText(msg.content);
|
|
10602
|
+
events.push({
|
|
10603
|
+
kind: "tool_result",
|
|
10604
|
+
ts,
|
|
10605
|
+
tsIso,
|
|
10606
|
+
line: lineNum,
|
|
10607
|
+
toolName: typeof msg.toolName === "string" ? msg.toolName : void 0,
|
|
10608
|
+
toolUseId: typeof msg.toolCallId === "string" ? msg.toolCallId : "",
|
|
10609
|
+
resultBody: body,
|
|
10610
|
+
resultIsError: msg.isError === true,
|
|
10611
|
+
resultBytes: body.length
|
|
10612
|
+
});
|
|
10613
|
+
continue;
|
|
10614
|
+
}
|
|
10615
|
+
if (role === "bashExecution") {
|
|
10616
|
+
const command = typeof msg.command === "string" ? msg.command : "";
|
|
10617
|
+
const output = typeof msg.output === "string" ? msg.output : "";
|
|
10618
|
+
const toolUseId = `pi_bash_${lineNum}`;
|
|
10619
|
+
events.push({
|
|
10620
|
+
kind: "tool_use",
|
|
10621
|
+
ts,
|
|
10622
|
+
tsIso,
|
|
10623
|
+
line: lineNum,
|
|
10624
|
+
toolName: "bash",
|
|
10625
|
+
toolUseId,
|
|
10626
|
+
toolInput: { command },
|
|
10627
|
+
command
|
|
10628
|
+
});
|
|
10629
|
+
events.push({
|
|
10630
|
+
kind: "tool_result",
|
|
10631
|
+
ts,
|
|
10632
|
+
tsIso,
|
|
10633
|
+
line: lineNum,
|
|
10634
|
+
toolUseId,
|
|
10635
|
+
toolName: "bash",
|
|
10636
|
+
resultBody: output,
|
|
10637
|
+
resultIsError: typeof msg.exitCode === "number" && msg.exitCode !== 0,
|
|
10638
|
+
resultExitCode: typeof msg.exitCode === "number" ? msg.exitCode : void 0,
|
|
10639
|
+
resultBytes: output.length
|
|
10640
|
+
});
|
|
10641
|
+
continue;
|
|
10642
|
+
}
|
|
10643
|
+
continue;
|
|
10644
|
+
}
|
|
10645
|
+
if (obj.type === "compaction" && typeof obj.summary === "string" && obj.summary.trim()) {
|
|
10646
|
+
events.push({ kind: "summary", ts, tsIso, line: lineNum, text: obj.summary.trim() });
|
|
10647
|
+
continue;
|
|
10648
|
+
}
|
|
10649
|
+
if (obj.type === "branch_summary" && typeof obj.summary === "string" && obj.summary.trim()) {
|
|
10650
|
+
events.push({ kind: "summary", ts, tsIso, line: lineNum, text: obj.summary.trim() });
|
|
10651
|
+
}
|
|
10652
|
+
}
|
|
10653
|
+
return events;
|
|
10654
|
+
}
|
|
10655
|
+
// ── Per-session operations — all delegate to the generic engine ─
|
|
10656
|
+
extractTurns(id, opts = {}) {
|
|
10657
|
+
const events = this.readEvents(this.toRawId(id));
|
|
10658
|
+
return extractTurnsFromEvents(this.toPrefixedId(id), events, opts);
|
|
10659
|
+
}
|
|
10660
|
+
liveScanEdits(id) {
|
|
10661
|
+
const located = this.findSession(this.toRawId(id));
|
|
10662
|
+
if (!located) {
|
|
10663
|
+
return { found: false, projectPath: "", projectDir: "", edits: [], fileMtime: 0, tool: "pi" };
|
|
10664
|
+
}
|
|
10665
|
+
const events = this.readEvents(this.toRawId(id));
|
|
10666
|
+
return liveScanEditsFromEvents(events, this.fileToolMap, {
|
|
10667
|
+
sessionId: this.toPrefixedId(id),
|
|
10668
|
+
tool: "pi",
|
|
10669
|
+
projectPath: located.projectPath,
|
|
10670
|
+
projectDir: located.projectDir,
|
|
10671
|
+
fileMtime: located.mtime,
|
|
10672
|
+
found: true
|
|
10673
|
+
}, this.extractEditDelta.bind(this));
|
|
10674
|
+
}
|
|
10675
|
+
replay(id) {
|
|
10676
|
+
const located = this.findSession(this.toRawId(id));
|
|
10677
|
+
if (!located) {
|
|
10678
|
+
return { sessionId: this.toPrefixedId(id), found: false, projectPath: "", files: [], totalLinesAdded: 0, totalLinesRemoved: 0 };
|
|
10679
|
+
}
|
|
10680
|
+
const events = this.readEvents(this.toRawId(id));
|
|
10681
|
+
return replayFromEvents(this.toPrefixedId(id), events, this.fileToolMap, this.extractEditDelta.bind(this), { projectPath: located.projectPath, found: true });
|
|
10682
|
+
}
|
|
10683
|
+
computeOutcome(id, opts) {
|
|
10684
|
+
return computeOutcome(this.toPrefixedId(id), opts);
|
|
10685
|
+
}
|
|
10686
|
+
collectRecentEdits(opts) {
|
|
10687
|
+
const candidates = [];
|
|
10688
|
+
for (const [rawId, path] of this.fileIndex()) {
|
|
10689
|
+
let stat;
|
|
10690
|
+
try {
|
|
10691
|
+
stat = statSync10(path);
|
|
10692
|
+
} catch {
|
|
10693
|
+
continue;
|
|
10694
|
+
}
|
|
10695
|
+
if (stat.mtimeMs < opts.sinceMs)
|
|
10696
|
+
continue;
|
|
10697
|
+
if (opts.projectFilter) {
|
|
10698
|
+
const header = readPiHeader(path);
|
|
10699
|
+
const projectPath = header?.cwd || "";
|
|
10700
|
+
if (!projectPath.toLowerCase().includes(opts.projectFilter.toLowerCase()))
|
|
10701
|
+
continue;
|
|
10702
|
+
}
|
|
10703
|
+
candidates.push({ rawId, mtime: stat.mtimeMs });
|
|
10704
|
+
}
|
|
10705
|
+
candidates.sort((a, b) => b.mtime - a.mtime);
|
|
10706
|
+
const limited = opts.limitSessions ? candidates.slice(0, opts.limitSessions) : candidates;
|
|
10707
|
+
const edits = [];
|
|
10708
|
+
for (const c of limited)
|
|
10709
|
+
edits.push(...this.liveScanEdits(c.rawId).edits);
|
|
10710
|
+
return edits;
|
|
10711
|
+
}
|
|
10712
|
+
getCommits(id, files, startMs, endMs, bufferMinutes) {
|
|
10713
|
+
return getSessionCommits(this.toPrefixedId(id), files, startMs, endMs, bufferMinutes);
|
|
10714
|
+
}
|
|
10715
|
+
/**
|
|
10716
|
+
* Pi's user-assigned session name (`/name`, `--name`), from the newest
|
|
10717
|
+
* `session_info` entry. Null when the session was never named.
|
|
10718
|
+
*/
|
|
10719
|
+
getNativeTitle(rawId) {
|
|
10720
|
+
const located = this.findSession(rawId);
|
|
10721
|
+
if (!located)
|
|
10722
|
+
return null;
|
|
10723
|
+
const parsed = parsePiFile(located.path);
|
|
10724
|
+
for (let i = parsed.entries.length - 1; i >= 0; i--) {
|
|
10725
|
+
const entry = parsed.entries[i];
|
|
10726
|
+
if (entry.type !== "session_info")
|
|
10727
|
+
continue;
|
|
10728
|
+
const name = typeof entry.name === "string" ? entry.name.trim() : "";
|
|
10729
|
+
if (name)
|
|
10730
|
+
return name;
|
|
10731
|
+
}
|
|
10732
|
+
return null;
|
|
10733
|
+
}
|
|
10734
|
+
exportRawSession(id) {
|
|
10735
|
+
const located = this.findSession(id);
|
|
10736
|
+
if (!located)
|
|
10737
|
+
return null;
|
|
10738
|
+
try {
|
|
10739
|
+
const st = statSync10(located.path);
|
|
10740
|
+
return {
|
|
10741
|
+
tool: "pi",
|
|
10742
|
+
mtime: st.mtimeMs,
|
|
10743
|
+
files: [{ name: basename6(located.path), bytes: readFileSync8(located.path) }]
|
|
10744
|
+
};
|
|
10745
|
+
} catch {
|
|
10746
|
+
return null;
|
|
10747
|
+
}
|
|
10748
|
+
}
|
|
10749
|
+
isAppendOnly() {
|
|
10750
|
+
return true;
|
|
10751
|
+
}
|
|
10752
|
+
fileSize(prefixedId) {
|
|
10753
|
+
const located = this.findSession(prefixedId);
|
|
10754
|
+
if (!located)
|
|
10755
|
+
return 0;
|
|
10756
|
+
try {
|
|
10757
|
+
return statSync10(located.path).size;
|
|
10758
|
+
} catch {
|
|
10759
|
+
return 0;
|
|
10760
|
+
}
|
|
10761
|
+
}
|
|
10762
|
+
async readFromOffset(prefixedId, offset, maxBytes) {
|
|
10763
|
+
const located = this.findSession(prefixedId);
|
|
10764
|
+
if (!located)
|
|
10765
|
+
return { text: "", newOffset: offset };
|
|
10766
|
+
return readTailFromOffset(located.path, offset, maxBytes);
|
|
10767
|
+
}
|
|
10768
|
+
};
|
|
10769
|
+
piBackend = new PiBackend();
|
|
10770
|
+
}
|
|
10771
|
+
});
|
|
10772
|
+
|
|
10773
|
+
// ../engine/dist/core/backends/index.js
|
|
10774
|
+
function bootstrapBackends() {
|
|
10775
|
+
registerBackend(claudeBackend);
|
|
10776
|
+
registerBackend(opencodeBackend);
|
|
10777
|
+
registerBackend(codexBackend);
|
|
10778
|
+
registerBackend(agyBackend);
|
|
10779
|
+
registerBackend(cursorBackend);
|
|
10780
|
+
registerBackend(hermesBackend);
|
|
10781
|
+
registerBackend(piBackend);
|
|
10782
|
+
}
|
|
10783
|
+
var init_backends = __esm({
|
|
10784
|
+
"../engine/dist/core/backends/index.js"() {
|
|
10785
|
+
"use strict";
|
|
10786
|
+
init_tool_backend();
|
|
10787
|
+
init_claude2();
|
|
10788
|
+
init_opencode();
|
|
10789
|
+
init_codex2();
|
|
10790
|
+
init_agy();
|
|
10791
|
+
init_cursor();
|
|
10792
|
+
init_hermes();
|
|
10793
|
+
init_pi();
|
|
10794
|
+
init_tool_backend();
|
|
10795
|
+
_setRegistryBootstrapper(bootstrapBackends);
|
|
10796
|
+
}
|
|
10797
|
+
});
|
|
10798
|
+
|
|
10799
|
+
// ../engine/dist/core/session-multi-tool.js
|
|
10800
|
+
function extractTurnsAny(sessionId, opts = {}) {
|
|
10801
|
+
const backend = getBackendForId(sessionId);
|
|
10802
|
+
if (!backend)
|
|
10803
|
+
return { sessionId, found: false, turns: [], startMs: 0, endMs: 0 };
|
|
10804
|
+
return memoInSessionScope(`turns:${sessionId}:${opts.maxTurns ?? ""}:${opts.assistantMax ?? ""}:${opts.userMax ?? ""}`, () => backend.extractTurns(sessionId, opts));
|
|
10805
|
+
}
|
|
10806
|
+
function replaySessionAny(sessionId) {
|
|
10807
|
+
const backend = getBackendForId(sessionId);
|
|
10808
|
+
if (!backend)
|
|
10809
|
+
return { sessionId, found: false, projectPath: "", files: [], totalLinesAdded: 0, totalLinesRemoved: 0 };
|
|
10810
|
+
return memoInSessionScope(`replay:${sessionId}`, () => backend.replay(sessionId));
|
|
10811
|
+
}
|
|
10812
|
+
var init_session_multi_tool = __esm({
|
|
10813
|
+
"../engine/dist/core/session-multi-tool.js"() {
|
|
10814
|
+
"use strict";
|
|
10815
|
+
init_tool_backend();
|
|
10816
|
+
init_live_session_scan();
|
|
10817
|
+
init_backends();
|
|
10818
|
+
}
|
|
10819
|
+
});
|
|
10820
|
+
|
|
10821
|
+
// ../engine/dist/core/session-outcome.js
|
|
10822
|
+
function extractDecisionsFromTurn(turn) {
|
|
10823
|
+
if (turn.kind !== "assistant_text" || !turn.text)
|
|
10824
|
+
return [];
|
|
10825
|
+
const out = [];
|
|
10826
|
+
for (const pat of DECISION_PATTERNS) {
|
|
10827
|
+
const m = turn.text.match(pat);
|
|
10828
|
+
if (m) {
|
|
10829
|
+
const text = m[0].replace(/\s+/g, " ").trim();
|
|
10830
|
+
if (text.length > 20 && text.length < 280) {
|
|
10831
|
+
out.push({ text, ts: turn.ts, tsIso: turn.tsIso, line: turn.line });
|
|
10832
|
+
break;
|
|
10833
|
+
}
|
|
10834
|
+
}
|
|
10835
|
+
}
|
|
10836
|
+
return out;
|
|
10837
|
+
}
|
|
10838
|
+
function findLastClaimAndReaction(turns) {
|
|
10839
|
+
let lastAssistantIdx = -1;
|
|
10840
|
+
for (let i = turns.length - 1; i >= 0; i--) {
|
|
10841
|
+
if (turns[i].kind === "assistant_text" && turns[i].text && turns[i].text.trim().length > 30) {
|
|
10842
|
+
lastAssistantIdx = i;
|
|
10843
|
+
break;
|
|
10844
|
+
}
|
|
10845
|
+
}
|
|
10846
|
+
if (lastAssistantIdx === -1)
|
|
10847
|
+
return {};
|
|
10848
|
+
const claim = turns[lastAssistantIdx];
|
|
10849
|
+
let reaction;
|
|
10850
|
+
for (let i = lastAssistantIdx + 1; i < turns.length; i++) {
|
|
10851
|
+
if (turns[i].kind === "user" && turns[i].text && turns[i].text.trim().length > 0) {
|
|
10852
|
+
reaction = turns[i];
|
|
10853
|
+
break;
|
|
10854
|
+
}
|
|
10855
|
+
}
|
|
10856
|
+
if (!reaction) {
|
|
10857
|
+
return {
|
|
10858
|
+
claim: { text: claim.text, ts: claim.ts, tsIso: claim.tsIso, line: claim.line }
|
|
10859
|
+
};
|
|
10860
|
+
}
|
|
10861
|
+
const marked = markPrompt(reaction.text);
|
|
10862
|
+
return {
|
|
10863
|
+
claim: { text: claim.text, ts: claim.ts, tsIso: claim.tsIso, line: claim.line },
|
|
10864
|
+
reaction: {
|
|
10865
|
+
text: reaction.text,
|
|
10866
|
+
ts: reaction.ts,
|
|
10867
|
+
tsIso: reaction.tsIso,
|
|
10868
|
+
line: reaction.line,
|
|
10869
|
+
markers: marked.markers,
|
|
10870
|
+
intensity: marked.intensity
|
|
10871
|
+
}
|
|
10872
|
+
};
|
|
10873
|
+
}
|
|
10874
|
+
function classifyStatus(opts) {
|
|
10875
|
+
const lastPrompt = opts.prompts[opts.prompts.length - 1];
|
|
10876
|
+
if (lastPrompt && lastPrompt.markers.includes("interrupt")) {
|
|
10877
|
+
return { status: "interrupted", reason: "last user message was an interrupt" };
|
|
10878
|
+
}
|
|
10879
|
+
const ageHours = (Date.now() - opts.endMs) / (3600 * 1e3);
|
|
10880
|
+
if (ageHours < 2) {
|
|
10881
|
+
return { status: "in_progress", reason: "most recent activity is within the last 2 hours" };
|
|
10882
|
+
}
|
|
10883
|
+
if (opts.hasCommits) {
|
|
10884
|
+
return { status: "shipped", reason: "commits landed in repos that the session edited during its window" };
|
|
10885
|
+
}
|
|
10886
|
+
if (opts.filesChanged === 0) {
|
|
10887
|
+
return { status: "unknown", reason: "no file edits and no commits in window" };
|
|
10888
|
+
}
|
|
10889
|
+
const lastAssistant = [...opts.turns].reverse().find((t) => t.kind === "assistant_text" && t.text);
|
|
10890
|
+
if (lastAssistant && lastAssistant.text && /\?\s*$/.test(lastAssistant.text.trim())) {
|
|
10891
|
+
return { status: "abandoned", reason: "last assistant turn ended on a question and nothing landed" };
|
|
10892
|
+
}
|
|
10893
|
+
return { status: "abandoned", reason: "edits made but no commits \u2014 work stayed local" };
|
|
10894
|
+
}
|
|
10895
|
+
function computeOutcome(sessionId, opts = {}) {
|
|
10896
|
+
const backend = getBackendForId(sessionId);
|
|
10897
|
+
const notFound = {
|
|
10898
|
+
sessionId,
|
|
10899
|
+
found: false,
|
|
10900
|
+
status: "unknown",
|
|
10901
|
+
reason: "session not found",
|
|
10902
|
+
startMs: 0,
|
|
10903
|
+
endMs: 0,
|
|
10904
|
+
decisions: [],
|
|
10905
|
+
blockers: [],
|
|
10906
|
+
claimReaction: {},
|
|
10907
|
+
prompts: [],
|
|
10908
|
+
promptMarkers: { total: 0, interrupt: 0, frustrated: 0, correction: 0, approval: 0, question: 0, directive: 0, clarification_request: 0, peakIntensity: 0 },
|
|
10909
|
+
commits: { sessionId, startMs: 0, endMs: 0, repos: [], totalCommits: 0 },
|
|
10910
|
+
fileCount: 0,
|
|
10911
|
+
filesChanged: [],
|
|
10912
|
+
totalLinesAdded: 0,
|
|
10913
|
+
totalLinesRemoved: 0
|
|
10914
|
+
};
|
|
10915
|
+
if (!backend)
|
|
10916
|
+
return notFound;
|
|
10917
|
+
const turnsRes = extractTurnsAny(sessionId, { assistantMax: 2e3 });
|
|
10918
|
+
if (!turnsRes.found)
|
|
10919
|
+
return notFound;
|
|
10920
|
+
const decisions = [];
|
|
10921
|
+
const blockers = [];
|
|
10922
|
+
for (const t of turnsRes.turns) {
|
|
10923
|
+
if (t.kind === "assistant_text")
|
|
10924
|
+
decisions.push(...extractDecisionsFromTurn(t));
|
|
10925
|
+
if (t.kind === "tool_result" && t.resultIsError) {
|
|
10926
|
+
blockers.push({
|
|
10927
|
+
kind: "tool_error",
|
|
10928
|
+
text: t.resultSummary?.slice(0, 240) || "tool error",
|
|
10929
|
+
ts: t.ts,
|
|
9637
10930
|
tsIso: t.tsIso,
|
|
9638
10931
|
line: t.line
|
|
9639
10932
|
});
|
|
@@ -9710,7 +11003,7 @@ var init_session_outcome = __esm({
|
|
|
9710
11003
|
});
|
|
9711
11004
|
|
|
9712
11005
|
// ../engine/dist/core/first-prompt.js
|
|
9713
|
-
import { createReadStream, existsSync as
|
|
11006
|
+
import { createReadStream, existsSync as existsSync11, openSync as openSync4, readSync as readSync4, closeSync as closeSync4 } from "fs";
|
|
9714
11007
|
import { StringDecoder } from "string_decoder";
|
|
9715
11008
|
function stripBanners(s) {
|
|
9716
11009
|
let out = s;
|
|
@@ -9764,7 +11057,7 @@ function extractFirstUserPromptSync(filePath, options = {}) {
|
|
|
9764
11057
|
const maxBytes = options.maxBytes ?? 4 * 1024 * 1024;
|
|
9765
11058
|
let fd;
|
|
9766
11059
|
try {
|
|
9767
|
-
fd =
|
|
11060
|
+
fd = openSync4(filePath, "r");
|
|
9768
11061
|
} catch {
|
|
9769
11062
|
return "";
|
|
9770
11063
|
}
|
|
@@ -9776,7 +11069,7 @@ function extractFirstUserPromptSync(filePath, options = {}) {
|
|
|
9776
11069
|
let pos = 0;
|
|
9777
11070
|
let lineNo = 0;
|
|
9778
11071
|
for (; ; ) {
|
|
9779
|
-
const n =
|
|
11072
|
+
const n = readSync4(fd, buf, 0, CHUNK, pos);
|
|
9780
11073
|
if (n <= 0)
|
|
9781
11074
|
break;
|
|
9782
11075
|
pos += n;
|
|
@@ -9802,7 +11095,7 @@ function extractFirstUserPromptSync(filePath, options = {}) {
|
|
|
9802
11095
|
return "";
|
|
9803
11096
|
} finally {
|
|
9804
11097
|
try {
|
|
9805
|
-
|
|
11098
|
+
closeSync4(fd);
|
|
9806
11099
|
} catch {
|
|
9807
11100
|
}
|
|
9808
11101
|
}
|
|
@@ -9821,7 +11114,7 @@ var init_first_prompt = __esm({
|
|
|
9821
11114
|
});
|
|
9822
11115
|
|
|
9823
11116
|
// ../engine/dist/core/project-dir-name.js
|
|
9824
|
-
import { readdirSync as
|
|
11117
|
+
import { readdirSync as readdirSync8 } from "node:fs";
|
|
9825
11118
|
function looksWindowsEncoded(dirName) {
|
|
9826
11119
|
return /^[A-Za-z]--/.test(dirName);
|
|
9827
11120
|
}
|
|
@@ -9841,7 +11134,7 @@ function resolveProjectDirName(dirName, deps = {}) {
|
|
|
9841
11134
|
return structural;
|
|
9842
11135
|
if (looksWindowsEncoded(dirName))
|
|
9843
11136
|
return structural;
|
|
9844
|
-
const readdir = deps.readdir ?? ((p) =>
|
|
11137
|
+
const readdir = deps.readdir ?? ((p) => readdirSync8(p, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name));
|
|
9845
11138
|
const encodeName = (name) => name.replace(/[-_.]/g, "-");
|
|
9846
11139
|
let remaining = dirName.replace(/^-/, "");
|
|
9847
11140
|
let current = "/";
|
|
@@ -9879,8 +11172,8 @@ var init_project_dir_name = __esm({
|
|
|
9879
11172
|
});
|
|
9880
11173
|
|
|
9881
11174
|
// ../engine/dist/core/backends/claude.js
|
|
9882
|
-
import { existsSync as
|
|
9883
|
-
import { join as
|
|
11175
|
+
import { existsSync as existsSync12, readdirSync as readdirSync9, readFileSync as readFileSync9, statSync as statSync11, openSync as openSync5, readSync as readSync5, closeSync as closeSync5 } from "fs";
|
|
11176
|
+
import { join as join12, basename as basename7 } from "path";
|
|
9884
11177
|
function stripReminders(text) {
|
|
9885
11178
|
return text.replace(/<system-reminder>[\s\S]*?<\/system-reminder>/g, "").trim();
|
|
9886
11179
|
}
|
|
@@ -9912,44 +11205,44 @@ var init_claude2 = __esm({
|
|
|
9912
11205
|
}
|
|
9913
11206
|
// ── Subpath helpers used by the parser/source layer ─────────────
|
|
9914
11207
|
projectsDir() {
|
|
9915
|
-
return
|
|
11208
|
+
return join12(this.homeDir(), "projects");
|
|
9916
11209
|
}
|
|
9917
11210
|
plansDir() {
|
|
9918
|
-
return
|
|
11211
|
+
return join12(this.homeDir(), "plans");
|
|
9919
11212
|
}
|
|
9920
11213
|
todosDir() {
|
|
9921
|
-
return
|
|
11214
|
+
return join12(this.homeDir(), "todos");
|
|
9922
11215
|
}
|
|
9923
11216
|
/** Legacy task-list directory still used by older Claude Code releases. */
|
|
9924
11217
|
tasksDir() {
|
|
9925
|
-
return
|
|
11218
|
+
return join12(this.homeDir(), "tasks");
|
|
9926
11219
|
}
|
|
9927
11220
|
pasteCacheDir() {
|
|
9928
|
-
return
|
|
11221
|
+
return join12(this.homeDir(), "paste-cache");
|
|
9929
11222
|
}
|
|
9930
11223
|
skillsDir() {
|
|
9931
|
-
return
|
|
11224
|
+
return join12(this.homeDir(), "skills");
|
|
9932
11225
|
}
|
|
9933
11226
|
agentsDir() {
|
|
9934
|
-
return
|
|
11227
|
+
return join12(this.homeDir(), "agents");
|
|
9935
11228
|
}
|
|
9936
11229
|
commandsDir() {
|
|
9937
|
-
return
|
|
11230
|
+
return join12(this.homeDir(), "commands");
|
|
9938
11231
|
}
|
|
9939
11232
|
pluginsManifestPath() {
|
|
9940
|
-
return
|
|
11233
|
+
return join12(this.homeDir(), "plugins", "installed_plugins.json");
|
|
9941
11234
|
}
|
|
9942
11235
|
hooksFile() {
|
|
9943
|
-
return
|
|
11236
|
+
return join12(this.homeDir(), "hooks.json");
|
|
9944
11237
|
}
|
|
9945
11238
|
settingsFile() {
|
|
9946
|
-
return
|
|
11239
|
+
return join12(this.homeDir(), "settings.json");
|
|
9947
11240
|
}
|
|
9948
11241
|
historyFile() {
|
|
9949
|
-
return
|
|
11242
|
+
return join12(this.homeDir(), "history.jsonl");
|
|
9950
11243
|
}
|
|
9951
11244
|
isAvailable() {
|
|
9952
|
-
return
|
|
11245
|
+
return existsSync12(this.projectsDir());
|
|
9953
11246
|
}
|
|
9954
11247
|
// ── ID handling ────────────────────────────────────────────────
|
|
9955
11248
|
/**
|
|
@@ -9977,13 +11270,13 @@ var init_claude2 = __esm({
|
|
|
9977
11270
|
return null;
|
|
9978
11271
|
let head;
|
|
9979
11272
|
try {
|
|
9980
|
-
const fd =
|
|
11273
|
+
const fd = openSync5(located.path, "r");
|
|
9981
11274
|
try {
|
|
9982
11275
|
const buf = Buffer.alloc(65536);
|
|
9983
|
-
const n =
|
|
11276
|
+
const n = readSync5(fd, buf, 0, buf.length, 0);
|
|
9984
11277
|
head = buf.toString("utf-8", 0, n);
|
|
9985
11278
|
} finally {
|
|
9986
|
-
|
|
11279
|
+
closeSync5(fd);
|
|
9987
11280
|
}
|
|
9988
11281
|
} catch {
|
|
9989
11282
|
return null;
|
|
@@ -10010,7 +11303,7 @@ var init_claude2 = __esm({
|
|
|
10010
11303
|
let mtime = 0;
|
|
10011
11304
|
for (const c of copies) {
|
|
10012
11305
|
try {
|
|
10013
|
-
mtime = Math.max(mtime,
|
|
11306
|
+
mtime = Math.max(mtime, statSync11(c.path).mtimeMs);
|
|
10014
11307
|
} catch {
|
|
10015
11308
|
}
|
|
10016
11309
|
}
|
|
@@ -10055,23 +11348,23 @@ var init_claude2 = __esm({
|
|
|
10055
11348
|
prior.modified = ref.modified;
|
|
10056
11349
|
}
|
|
10057
11350
|
};
|
|
10058
|
-
if (!
|
|
11351
|
+
if (!existsSync12(root))
|
|
10059
11352
|
return;
|
|
10060
|
-
for (const proj of
|
|
11353
|
+
for (const proj of readdirSync9(root, { withFileTypes: true })) {
|
|
10061
11354
|
if (!proj.isDirectory())
|
|
10062
11355
|
continue;
|
|
10063
11356
|
if (filter && !proj.name.toLowerCase().includes(filter))
|
|
10064
11357
|
continue;
|
|
10065
|
-
const projPath =
|
|
11358
|
+
const projPath = join12(root, proj.name);
|
|
10066
11359
|
const projectPath = resolveProjectDirName(proj.name);
|
|
10067
|
-
const indexPath =
|
|
11360
|
+
const indexPath = join12(projPath, "sessions-index.json");
|
|
10068
11361
|
const indexed = /* @__PURE__ */ new Set();
|
|
10069
|
-
if (
|
|
11362
|
+
if (existsSync12(indexPath)) {
|
|
10070
11363
|
try {
|
|
10071
|
-
const indexData = JSON.parse(
|
|
11364
|
+
const indexData = JSON.parse(readFileSync9(indexPath, "utf-8"));
|
|
10072
11365
|
const entries = indexData.entries || [];
|
|
10073
11366
|
for (const entry of entries) {
|
|
10074
|
-
if (!entry.fullPath || !
|
|
11367
|
+
if (!entry.fullPath || !existsSync12(entry.fullPath))
|
|
10075
11368
|
continue;
|
|
10076
11369
|
indexed.add(entry.sessionId);
|
|
10077
11370
|
add({
|
|
@@ -10093,20 +11386,20 @@ var init_claude2 = __esm({
|
|
|
10093
11386
|
}
|
|
10094
11387
|
let files;
|
|
10095
11388
|
try {
|
|
10096
|
-
files =
|
|
11389
|
+
files = readdirSync9(projPath);
|
|
10097
11390
|
} catch {
|
|
10098
11391
|
continue;
|
|
10099
11392
|
}
|
|
10100
11393
|
for (const f of files) {
|
|
10101
11394
|
if (!f.endsWith(".jsonl") || f === "sessions-index.json")
|
|
10102
11395
|
continue;
|
|
10103
|
-
const sessionId =
|
|
11396
|
+
const sessionId = basename7(f, ".jsonl");
|
|
10104
11397
|
if (indexed.has(sessionId))
|
|
10105
11398
|
continue;
|
|
10106
|
-
const fullPath =
|
|
11399
|
+
const fullPath = join12(projPath, f);
|
|
10107
11400
|
let stat;
|
|
10108
11401
|
try {
|
|
10109
|
-
stat =
|
|
11402
|
+
stat = statSync11(fullPath);
|
|
10110
11403
|
} catch {
|
|
10111
11404
|
continue;
|
|
10112
11405
|
}
|
|
@@ -10360,7 +11653,7 @@ var init_claude2 = __esm({
|
|
|
10360
11653
|
const events = this.readEvents(rawId);
|
|
10361
11654
|
let fileMtime = 0;
|
|
10362
11655
|
try {
|
|
10363
|
-
fileMtime =
|
|
11656
|
+
fileMtime = statSync11(located.path).mtimeMs;
|
|
10364
11657
|
} catch {
|
|
10365
11658
|
}
|
|
10366
11659
|
return liveScanEditsFromEvents(events, this.fileToolMap, {
|
|
@@ -10396,32 +11689,32 @@ var init_claude2 = __esm({
|
|
|
10396
11689
|
collectRecentEdits(opts) {
|
|
10397
11690
|
const candidates = [];
|
|
10398
11691
|
for (const root of claudeProjectDirs()) {
|
|
10399
|
-
if (!
|
|
11692
|
+
if (!existsSync12(root))
|
|
10400
11693
|
continue;
|
|
10401
|
-
for (const projEntry of
|
|
11694
|
+
for (const projEntry of readdirSync9(root, { withFileTypes: true })) {
|
|
10402
11695
|
if (!projEntry.isDirectory())
|
|
10403
11696
|
continue;
|
|
10404
11697
|
if (opts.projectFilter && !projEntry.name.toLowerCase().includes(opts.projectFilter.toLowerCase()))
|
|
10405
11698
|
continue;
|
|
10406
|
-
const projPath =
|
|
11699
|
+
const projPath = join12(root, projEntry.name);
|
|
10407
11700
|
let files;
|
|
10408
11701
|
try {
|
|
10409
|
-
files =
|
|
11702
|
+
files = readdirSync9(projPath);
|
|
10410
11703
|
} catch {
|
|
10411
11704
|
continue;
|
|
10412
11705
|
}
|
|
10413
11706
|
for (const f of files) {
|
|
10414
11707
|
if (!f.endsWith(".jsonl") || f === "sessions-index.json")
|
|
10415
11708
|
continue;
|
|
10416
|
-
const fp =
|
|
11709
|
+
const fp = join12(projPath, f);
|
|
10417
11710
|
try {
|
|
10418
|
-
const st =
|
|
11711
|
+
const st = statSync11(fp);
|
|
10419
11712
|
let mtime = st.mtimeMs;
|
|
10420
|
-
const subDir =
|
|
10421
|
-
if (
|
|
10422
|
-
for (const sub of
|
|
11713
|
+
const subDir = join12(projPath, f.slice(0, -6), "subagents");
|
|
11714
|
+
if (existsSync12(subDir)) {
|
|
11715
|
+
for (const sub of readdirSync9(subDir)) {
|
|
10423
11716
|
try {
|
|
10424
|
-
const subSt =
|
|
11717
|
+
const subSt = statSync11(join12(subDir, sub));
|
|
10425
11718
|
if (subSt.mtimeMs > mtime)
|
|
10426
11719
|
mtime = subSt.mtimeMs;
|
|
10427
11720
|
} catch {
|
|
@@ -10430,7 +11723,7 @@ var init_claude2 = __esm({
|
|
|
10430
11723
|
}
|
|
10431
11724
|
if (mtime < opts.sinceMs)
|
|
10432
11725
|
continue;
|
|
10433
|
-
candidates.push({ rawId:
|
|
11726
|
+
candidates.push({ rawId: basename7(f, ".jsonl"), mtime });
|
|
10434
11727
|
} catch {
|
|
10435
11728
|
}
|
|
10436
11729
|
}
|
|
@@ -10486,7 +11779,7 @@ var init_claude2 = __esm({
|
|
|
10486
11779
|
let total = 0;
|
|
10487
11780
|
for (const located of findSessionFiles(this.toRawId(prefixedId))) {
|
|
10488
11781
|
try {
|
|
10489
|
-
total +=
|
|
11782
|
+
total += statSync11(located.path).size;
|
|
10490
11783
|
} catch {
|
|
10491
11784
|
}
|
|
10492
11785
|
}
|
|
@@ -10495,27 +11788,45 @@ var init_claude2 = __esm({
|
|
|
10495
11788
|
/**
|
|
10496
11789
|
* True when this session exists in MORE THAN ONE home.
|
|
10497
11790
|
*
|
|
10498
|
-
* Such a session cannot be tail-appended: `fileSize()` is the SUM across
|
|
10499
|
-
* copies while a byte offset addresses ONE file, so the two disagree by
|
|
10500
|
-
* construction and the "tail" is either empty or the wrong bytes. The caller
|
|
10501
|
-
* uses this to force a FULL sync, which ships the unioned container and is
|
|
10502
|
-
* correct regardless of how the halves are arranged.
|
|
10503
|
-
*
|
|
10504
11791
|
* Measured on ec05f266: ~/.claude 1715 records, ~/.claude-work 205 records,
|
|
10505
|
-
* zero overlap.
|
|
10506
|
-
*
|
|
11792
|
+
* zero overlap. `fileSize()` is the sum across the copies, and
|
|
11793
|
+
* `readFromOffset()` reads them joined end to end, primary first.
|
|
10507
11794
|
*/
|
|
10508
11795
|
spansMultipleSources(prefixedId) {
|
|
10509
11796
|
return findSessionFiles(this.toRawId(prefixedId)).length > 1;
|
|
10510
11797
|
}
|
|
11798
|
+
/**
|
|
11799
|
+
* The copies before the last one, each with its size. Claude Code writes to
|
|
11800
|
+
* the home of the profile that runs the session, so the live copy can grow,
|
|
11801
|
+
* appear, vanish or merge back into the primary. Each of those moves bytes
|
|
11802
|
+
* under an offset into the joined copies; this key changes with all of them.
|
|
11803
|
+
*/
|
|
11804
|
+
sourceLayout(prefixedId) {
|
|
11805
|
+
const copies = findSessionFiles(this.toRawId(prefixedId));
|
|
11806
|
+
if (copies.length < 2)
|
|
11807
|
+
return "";
|
|
11808
|
+
return copies.slice(0, -1).map((c) => {
|
|
11809
|
+
let size = -1;
|
|
11810
|
+
try {
|
|
11811
|
+
size = statSync11(c.path).size;
|
|
11812
|
+
} catch {
|
|
11813
|
+
}
|
|
11814
|
+
return `${c.path}:${size}`;
|
|
11815
|
+
}).join("|");
|
|
11816
|
+
}
|
|
11817
|
+
/**
|
|
11818
|
+
* Session 8de13d0a had 31 MB in ~/.claude and 2 MB in ~/.claude-work. Over
|
|
11819
|
+
* the 24 MB build ceiling a sync ships the transcript in chunks read here,
|
|
11820
|
+
* and this returned no text for a session in two homes. Each sync shipped
|
|
11821
|
+
* nothing, and the server stayed three hours behind.
|
|
11822
|
+
*/
|
|
10511
11823
|
async readFromOffset(prefixedId, offset, maxBytes) {
|
|
10512
|
-
const
|
|
10513
|
-
const copies = findSessionFiles(rawId);
|
|
11824
|
+
const copies = findSessionFiles(this.toRawId(prefixedId));
|
|
10514
11825
|
if (copies.length === 0)
|
|
10515
11826
|
return { text: "", newOffset: offset };
|
|
10516
|
-
if (copies.length
|
|
10517
|
-
return
|
|
10518
|
-
return
|
|
11827
|
+
if (copies.length === 1)
|
|
11828
|
+
return readTailFromOffset(copies[0].path, offset, maxBytes);
|
|
11829
|
+
return readSourcesFromOffset(copies.map((c) => c.path), offset, maxBytes);
|
|
10519
11830
|
}
|
|
10520
11831
|
};
|
|
10521
11832
|
claudeBackend = new ClaudeBackend();
|
|
@@ -10530,11 +11841,11 @@ var init_model_pricing = __esm({
|
|
|
10530
11841
|
});
|
|
10531
11842
|
|
|
10532
11843
|
// ../engine/dist/parsers/session.js
|
|
10533
|
-
import { createReadStream as createReadStream2, existsSync as
|
|
10534
|
-
import { join as
|
|
11844
|
+
import { createReadStream as createReadStream2, existsSync as existsSync13, readdirSync as readdirSync10, readFileSync as readFileSync10, statSync as statSync12, openSync as openSync6, readSync as readSync6, closeSync as closeSync6 } from "fs";
|
|
11845
|
+
import { join as join13, basename as basename8 } from "path";
|
|
10535
11846
|
function hasSubagentsDir(sessionPath) {
|
|
10536
11847
|
const dir = sessionPath.slice(0, -6);
|
|
10537
|
-
return
|
|
11848
|
+
return existsSync13(join13(dir, "subagents"));
|
|
10538
11849
|
}
|
|
10539
11850
|
var init_session = __esm({
|
|
10540
11851
|
"../engine/dist/parsers/session.js"() {
|
|
@@ -10549,9 +11860,9 @@ var init_session = __esm({
|
|
|
10549
11860
|
});
|
|
10550
11861
|
|
|
10551
11862
|
// ../engine/dist/transcript/shadow.js
|
|
10552
|
-
import { createHash } from "crypto";
|
|
11863
|
+
import { createHash as createHash2 } from "crypto";
|
|
10553
11864
|
function sha1(s) {
|
|
10554
|
-
return
|
|
11865
|
+
return createHash2("sha1").update(s).digest("hex");
|
|
10555
11866
|
}
|
|
10556
11867
|
function lineKey(line) {
|
|
10557
11868
|
try {
|
|
@@ -10633,11 +11944,11 @@ __export(live_session_scan_exports, {
|
|
|
10633
11944
|
withSessionReadCache: () => withSessionReadCache,
|
|
10634
11945
|
withSessionScanScope: () => withSessionScanScope
|
|
10635
11946
|
});
|
|
10636
|
-
import { existsSync as
|
|
10637
|
-
import { join as
|
|
10638
|
-
import { createHash as
|
|
11947
|
+
import { existsSync as existsSync14, readdirSync as readdirSync11, readFileSync as readFileSync11, statSync as statSync13, openSync as openSync7, readSync as readSync7, closeSync as closeSync7 } from "fs";
|
|
11948
|
+
import { join as join14, basename as basename9 } from "path";
|
|
11949
|
+
import { createHash as createHash3 } from "crypto";
|
|
10639
11950
|
function lazyGeminiProjectsJson() {
|
|
10640
|
-
return
|
|
11951
|
+
return join14(geminiHomeDir(), "projects.json");
|
|
10641
11952
|
}
|
|
10642
11953
|
function detectTool(sessionId) {
|
|
10643
11954
|
return getBackendForId(sessionId)?.id ?? "claude";
|
|
@@ -10694,24 +12005,24 @@ function sessionFileIndex() {
|
|
|
10694
12005
|
return p;
|
|
10695
12006
|
};
|
|
10696
12007
|
for (const root of roots) {
|
|
10697
|
-
if (seenRoots.has(root) || !
|
|
12008
|
+
if (seenRoots.has(root) || !existsSync14(root))
|
|
10698
12009
|
continue;
|
|
10699
12010
|
seenRoots.add(root);
|
|
10700
12011
|
dirs.set(root, dirMtime(root));
|
|
10701
12012
|
let entries;
|
|
10702
12013
|
try {
|
|
10703
|
-
entries =
|
|
12014
|
+
entries = readdirSync11(root, { withFileTypes: true });
|
|
10704
12015
|
} catch {
|
|
10705
12016
|
continue;
|
|
10706
12017
|
}
|
|
10707
12018
|
for (const entry of entries) {
|
|
10708
12019
|
if (!entry.isDirectory())
|
|
10709
12020
|
continue;
|
|
10710
|
-
const dir =
|
|
12021
|
+
const dir = join14(root, entry.name);
|
|
10711
12022
|
dirs.set(dir, dirMtime(dir));
|
|
10712
12023
|
let names;
|
|
10713
12024
|
try {
|
|
10714
|
-
names =
|
|
12025
|
+
names = readdirSync11(dir);
|
|
10715
12026
|
} catch {
|
|
10716
12027
|
continue;
|
|
10717
12028
|
}
|
|
@@ -10720,7 +12031,7 @@ function sessionFileIndex() {
|
|
|
10720
12031
|
continue;
|
|
10721
12032
|
const id = name.slice(0, -".jsonl".length);
|
|
10722
12033
|
const located = {
|
|
10723
|
-
path:
|
|
12034
|
+
path: join14(dir, name),
|
|
10724
12035
|
projectDir: entry.name,
|
|
10725
12036
|
projectPath: projectPathOf(entry.name)
|
|
10726
12037
|
};
|
|
@@ -10752,7 +12063,7 @@ function resolveSessionContentGroups(sessionId) {
|
|
|
10752
12063
|
};
|
|
10753
12064
|
for (const located of findSessionFiles(sessionId)) {
|
|
10754
12065
|
for (const p of resolveSessionContentPaths(located.path)) {
|
|
10755
|
-
add(p === located.path ? "main" : `subagents/${
|
|
12066
|
+
add(p === located.path ? "main" : `subagents/${basename9(p)}`, p);
|
|
10756
12067
|
}
|
|
10757
12068
|
}
|
|
10758
12069
|
const names = [...groups.keys()].sort((a, b) => a === "main" ? -1 : b === "main" ? 1 : a.localeCompare(b));
|
|
@@ -10786,7 +12097,7 @@ function readSessionGroupText(group) {
|
|
|
10786
12097
|
const parts = [];
|
|
10787
12098
|
for (const p of group.paths) {
|
|
10788
12099
|
try {
|
|
10789
|
-
const st =
|
|
12100
|
+
const st = statSync13(p);
|
|
10790
12101
|
parts.push(`${p}:${st.mtimeMs}:${st.size}`);
|
|
10791
12102
|
} catch {
|
|
10792
12103
|
parts.push(`${p}:missing`);
|
|
@@ -10801,12 +12112,12 @@ function readSessionGroupText(group) {
|
|
|
10801
12112
|
for (const p of group.paths) {
|
|
10802
12113
|
let raw;
|
|
10803
12114
|
try {
|
|
10804
|
-
raw =
|
|
12115
|
+
raw = readFileSync11(p, "utf-8");
|
|
10805
12116
|
} catch {
|
|
10806
12117
|
continue;
|
|
10807
12118
|
}
|
|
10808
12119
|
try {
|
|
10809
|
-
mtime = Math.max(mtime,
|
|
12120
|
+
mtime = Math.max(mtime, statSync13(p).mtimeMs);
|
|
10810
12121
|
} catch {
|
|
10811
12122
|
}
|
|
10812
12123
|
text = text ? mergeLineText(text, raw).text : raw;
|
|
@@ -10819,10 +12130,10 @@ function readSessionGroupText(group) {
|
|
|
10819
12130
|
function resolveSessionContentPaths(sessionFile) {
|
|
10820
12131
|
if (!hasSubagentsDir(sessionFile))
|
|
10821
12132
|
return [sessionFile];
|
|
10822
|
-
const subDir =
|
|
10823
|
-
if (!
|
|
12133
|
+
const subDir = join14(sessionFile.slice(0, -6), "subagents");
|
|
12134
|
+
if (!existsSync14(subDir))
|
|
10824
12135
|
return [sessionFile];
|
|
10825
|
-
const subPaths =
|
|
12136
|
+
const subPaths = readdirSync11(subDir).filter((f) => f.endsWith(".jsonl")).sort().map((f) => join14(subDir, f));
|
|
10826
12137
|
return [sessionFile, ...subPaths];
|
|
10827
12138
|
}
|
|
10828
12139
|
function liveScanSessionEdits(sessionId) {
|
|
@@ -10837,11 +12148,11 @@ function loadGeminiProjectMap() {
|
|
|
10837
12148
|
return geminiProjectMapCache;
|
|
10838
12149
|
const map = /* @__PURE__ */ new Map();
|
|
10839
12150
|
const projectsPath = lazyGeminiProjectsJson();
|
|
10840
|
-
if (
|
|
12151
|
+
if (existsSync14(projectsPath)) {
|
|
10841
12152
|
try {
|
|
10842
|
-
const data = JSON.parse(
|
|
12153
|
+
const data = JSON.parse(readFileSync11(projectsPath, "utf-8"));
|
|
10843
12154
|
for (const path of Object.keys(data.projects || {})) {
|
|
10844
|
-
map.set(
|
|
12155
|
+
map.set(createHash3("sha256").update(path).digest("hex"), path);
|
|
10845
12156
|
}
|
|
10846
12157
|
} catch {
|
|
10847
12158
|
}
|
|
@@ -10873,23 +12184,23 @@ function geminiIndex() {
|
|
|
10873
12184
|
const projMap = loadGeminiProjectMap();
|
|
10874
12185
|
const isGeminiChat = (f) => f.startsWith("session-") && (f.endsWith(".json") || f.endsWith(".jsonl"));
|
|
10875
12186
|
for (const tmpRoot of geminiTmpDirs()) {
|
|
10876
|
-
if (!
|
|
12187
|
+
if (!existsSync14(tmpRoot))
|
|
10877
12188
|
continue;
|
|
10878
12189
|
let projs;
|
|
10879
12190
|
try {
|
|
10880
|
-
projs =
|
|
12191
|
+
projs = readdirSync11(tmpRoot, { withFileTypes: true });
|
|
10881
12192
|
} catch {
|
|
10882
12193
|
continue;
|
|
10883
12194
|
}
|
|
10884
12195
|
for (const proj of projs) {
|
|
10885
12196
|
if (!proj.isDirectory())
|
|
10886
12197
|
continue;
|
|
10887
|
-
const chats =
|
|
10888
|
-
if (!
|
|
12198
|
+
const chats = join14(tmpRoot, proj.name, "chats");
|
|
12199
|
+
if (!existsSync14(chats))
|
|
10889
12200
|
continue;
|
|
10890
12201
|
let files;
|
|
10891
12202
|
try {
|
|
10892
|
-
files =
|
|
12203
|
+
files = readdirSync11(chats);
|
|
10893
12204
|
} catch {
|
|
10894
12205
|
continue;
|
|
10895
12206
|
}
|
|
@@ -10898,7 +12209,7 @@ function geminiIndex() {
|
|
|
10898
12209
|
continue;
|
|
10899
12210
|
const base = f.replace(/\.jsonl?$/, "");
|
|
10900
12211
|
const located = {
|
|
10901
|
-
path:
|
|
12212
|
+
path: join14(chats, f),
|
|
10902
12213
|
projectDir: proj.name,
|
|
10903
12214
|
projectPath: projMap.get(proj.name) || "",
|
|
10904
12215
|
format: f.endsWith(".jsonl") ? "jsonl" : "json"
|
|
@@ -10933,19 +12244,19 @@ function geminiInnerIdIndex(idx) {
|
|
|
10933
12244
|
return byInnerId;
|
|
10934
12245
|
}
|
|
10935
12246
|
function readHeadSync(path, bytes = 65536) {
|
|
10936
|
-
const fd =
|
|
12247
|
+
const fd = openSync7(path, "r");
|
|
10937
12248
|
try {
|
|
10938
12249
|
const buf = Buffer.alloc(bytes);
|
|
10939
|
-
const n =
|
|
12250
|
+
const n = readSync7(fd, buf, 0, buf.length, 0);
|
|
10940
12251
|
return buf.toString("utf-8", 0, n);
|
|
10941
12252
|
} finally {
|
|
10942
|
-
|
|
12253
|
+
closeSync7(fd);
|
|
10943
12254
|
}
|
|
10944
12255
|
}
|
|
10945
12256
|
function readGeminiSessionIdFromFile(path, format) {
|
|
10946
12257
|
if (format === "json") {
|
|
10947
12258
|
try {
|
|
10948
|
-
return JSON.parse(
|
|
12259
|
+
return JSON.parse(readFileSync11(path, "utf-8"))?.sessionId ?? null;
|
|
10949
12260
|
} catch {
|
|
10950
12261
|
return null;
|
|
10951
12262
|
}
|
|
@@ -10960,7 +12271,7 @@ function readGeminiSessionIdFromFile(path, format) {
|
|
|
10960
12271
|
function readGeminiMessages(path, format) {
|
|
10961
12272
|
if (format === "json") {
|
|
10962
12273
|
try {
|
|
10963
|
-
const json = JSON.parse(
|
|
12274
|
+
const json = JSON.parse(readFileSync11(path, "utf-8"));
|
|
10964
12275
|
return Array.isArray(json.messages) ? json.messages : [];
|
|
10965
12276
|
} catch {
|
|
10966
12277
|
return [];
|
|
@@ -10969,7 +12280,7 @@ function readGeminiMessages(path, format) {
|
|
|
10969
12280
|
const out = [];
|
|
10970
12281
|
let raw;
|
|
10971
12282
|
try {
|
|
10972
|
-
raw =
|
|
12283
|
+
raw = readFileSync11(path, "utf-8");
|
|
10973
12284
|
} catch {
|
|
10974
12285
|
return out;
|
|
10975
12286
|
}
|
|
@@ -11006,23 +12317,23 @@ function findCodexSessionFile(sessionId) {
|
|
|
11006
12317
|
return null;
|
|
11007
12318
|
}
|
|
11008
12319
|
function findCodexSessionFileIn(root, sessionId) {
|
|
11009
|
-
if (!
|
|
12320
|
+
if (!existsSync14(root))
|
|
11010
12321
|
return null;
|
|
11011
|
-
for (const year of
|
|
12322
|
+
for (const year of readdirSync11(root, { withFileTypes: true })) {
|
|
11012
12323
|
if (!year.isDirectory())
|
|
11013
12324
|
continue;
|
|
11014
|
-
const yearPath =
|
|
11015
|
-
for (const month of
|
|
12325
|
+
const yearPath = join14(root, year.name);
|
|
12326
|
+
for (const month of readdirSync11(yearPath, { withFileTypes: true })) {
|
|
11016
12327
|
if (!month.isDirectory())
|
|
11017
12328
|
continue;
|
|
11018
|
-
const monthPath =
|
|
11019
|
-
for (const day of
|
|
12329
|
+
const monthPath = join14(yearPath, month.name);
|
|
12330
|
+
for (const day of readdirSync11(monthPath, { withFileTypes: true })) {
|
|
11020
12331
|
if (!day.isDirectory())
|
|
11021
12332
|
continue;
|
|
11022
|
-
const dayPath =
|
|
12333
|
+
const dayPath = join14(monthPath, day.name);
|
|
11023
12334
|
let files;
|
|
11024
12335
|
try {
|
|
11025
|
-
files =
|
|
12336
|
+
files = readdirSync11(dayPath);
|
|
11026
12337
|
} catch {
|
|
11027
12338
|
continue;
|
|
11028
12339
|
}
|
|
@@ -11030,22 +12341,22 @@ function findCodexSessionFileIn(root, sessionId) {
|
|
|
11030
12341
|
if (!f.endsWith(".jsonl"))
|
|
11031
12342
|
continue;
|
|
11032
12343
|
if (f.includes(sessionId)) {
|
|
11033
|
-
return { path:
|
|
12344
|
+
return { path: join14(dayPath, f), projectPath: "" };
|
|
11034
12345
|
}
|
|
11035
12346
|
}
|
|
11036
12347
|
}
|
|
11037
12348
|
}
|
|
11038
12349
|
}
|
|
11039
12350
|
function scanDir(dir) {
|
|
11040
|
-
for (const entry of
|
|
12351
|
+
for (const entry of readdirSync11(dir, { withFileTypes: true })) {
|
|
11041
12352
|
if (entry.isDirectory()) {
|
|
11042
|
-
const found = scanDir(
|
|
12353
|
+
const found = scanDir(join14(dir, entry.name));
|
|
11043
12354
|
if (found)
|
|
11044
12355
|
return found;
|
|
11045
12356
|
} else if (entry.name.endsWith(".jsonl")) {
|
|
11046
|
-
const p =
|
|
12357
|
+
const p = join14(dir, entry.name);
|
|
11047
12358
|
try {
|
|
11048
|
-
const first =
|
|
12359
|
+
const first = readFileSync11(p, "utf-8").split("\n")[0];
|
|
11049
12360
|
if (!first)
|
|
11050
12361
|
continue;
|
|
11051
12362
|
const meta = JSON.parse(first);
|
|
@@ -11121,7 +12432,7 @@ var init_live_session_scan = __esm({
|
|
|
11121
12432
|
INDEX_RECHECK_MS = 1e3;
|
|
11122
12433
|
dirMtime = (p) => {
|
|
11123
12434
|
try {
|
|
11124
|
-
return
|
|
12435
|
+
return statSync13(p).mtimeMs;
|
|
11125
12436
|
} catch {
|
|
11126
12437
|
return -1;
|
|
11127
12438
|
}
|