chat-recall 0.7.7 → 0.7.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +362 -210
- package/dist/cli.js.map +1 -1
- package/dist/mcp-relay.js +1 -1
- package/dist/mcp.js +305 -167
- package/dist/scan-worker.js +70 -16
- package/dist/watch.js +80 -26
- package/package.json +1 -1
- package/skills/chat-recall-find/SKILL.md +3 -1
- package/skills/chat-recall-resume/SKILL.md +3 -1
package/dist/mcp.js
CHANGED
|
@@ -5402,8 +5402,8 @@ function writeSecrets(secrets) {
|
|
|
5402
5402
|
_secretsCache = null;
|
|
5403
5403
|
const p = secretsPath();
|
|
5404
5404
|
mkdirSync3(dirname4(p), { recursive: true });
|
|
5405
|
-
const
|
|
5406
|
-
writeFileSync(p,
|
|
5405
|
+
const body2 = "# chat-recall secrets \u2014 NOT committed/synced. Mode 0600.\n" + Object.entries(secrets).filter(([, v]) => v).map(([k, v]) => `${k}=${v}`).join("\n") + "\n";
|
|
5406
|
+
writeFileSync(p, body2);
|
|
5407
5407
|
try {
|
|
5408
5408
|
chmodSync2(p, 384);
|
|
5409
5409
|
} catch {
|
|
@@ -6462,11 +6462,11 @@ async function parseSessionFile(sessionPath, maxMessages = 50) {
|
|
|
6462
6462
|
}
|
|
6463
6463
|
const toolResult = obj.toolUseResult;
|
|
6464
6464
|
if (toolResult && typeof toolResult === "object" && toolCount < maxMessages) {
|
|
6465
|
-
const
|
|
6465
|
+
const resultText2 = toolResult.result;
|
|
6466
6466
|
const query = toolResult.query;
|
|
6467
|
-
if (
|
|
6467
|
+
if (resultText2 && typeof resultText2 === "string" && isValuableToolResult(resultText2, "WebSearch")) {
|
|
6468
6468
|
const searchText = query ? `Search: ${query}
|
|
6469
|
-
${
|
|
6469
|
+
${resultText2}` : resultText2;
|
|
6470
6470
|
content.toolResults.push({
|
|
6471
6471
|
text: searchText.slice(0, 2e3),
|
|
6472
6472
|
lineNumber: lineNum,
|
|
@@ -7391,9 +7391,29 @@ function findSessionFile(sessionId) {
|
|
|
7391
7391
|
function invalidateSessionFileIndex() {
|
|
7392
7392
|
indexCache = null;
|
|
7393
7393
|
}
|
|
7394
|
+
function indexStillValid(cache4) {
|
|
7395
|
+
const roots = claudeProjectDirs();
|
|
7396
|
+
if (roots.length !== cache4.roots.length || roots.some((r, i) => r !== cache4.roots[i]))
|
|
7397
|
+
return false;
|
|
7398
|
+
for (const [dir, mtime] of cache4.dirs) {
|
|
7399
|
+
if (dirMtime(dir) !== mtime)
|
|
7400
|
+
return false;
|
|
7401
|
+
}
|
|
7402
|
+
return true;
|
|
7403
|
+
}
|
|
7394
7404
|
function sessionFileIndex() {
|
|
7395
|
-
if (indexCache)
|
|
7396
|
-
|
|
7405
|
+
if (indexCache) {
|
|
7406
|
+
const now = Date.now();
|
|
7407
|
+
if (now - indexCache.checkedAt < INDEX_RECHECK_MS)
|
|
7408
|
+
return indexCache.byId;
|
|
7409
|
+
if (indexStillValid(indexCache)) {
|
|
7410
|
+
indexCache.checkedAt = now;
|
|
7411
|
+
return indexCache.byId;
|
|
7412
|
+
}
|
|
7413
|
+
indexCache = null;
|
|
7414
|
+
}
|
|
7415
|
+
const dirs = /* @__PURE__ */ new Map();
|
|
7416
|
+
const roots = claudeProjectDirs();
|
|
7397
7417
|
const byId = /* @__PURE__ */ new Map();
|
|
7398
7418
|
const seenRoots = /* @__PURE__ */ new Set();
|
|
7399
7419
|
const projectPaths = /* @__PURE__ */ new Map();
|
|
@@ -7405,10 +7425,11 @@ function sessionFileIndex() {
|
|
|
7405
7425
|
}
|
|
7406
7426
|
return p;
|
|
7407
7427
|
};
|
|
7408
|
-
for (const root of
|
|
7428
|
+
for (const root of roots) {
|
|
7409
7429
|
if (seenRoots.has(root) || !existsSync8(root))
|
|
7410
7430
|
continue;
|
|
7411
7431
|
seenRoots.add(root);
|
|
7432
|
+
dirs.set(root, dirMtime(root));
|
|
7412
7433
|
let entries;
|
|
7413
7434
|
try {
|
|
7414
7435
|
entries = readdirSync8(root, { withFileTypes: true });
|
|
@@ -7419,6 +7440,7 @@ function sessionFileIndex() {
|
|
|
7419
7440
|
if (!entry.isDirectory())
|
|
7420
7441
|
continue;
|
|
7421
7442
|
const dir = join12(root, entry.name);
|
|
7443
|
+
dirs.set(dir, dirMtime(dir));
|
|
7422
7444
|
let names;
|
|
7423
7445
|
try {
|
|
7424
7446
|
names = readdirSync8(dir);
|
|
@@ -7443,7 +7465,7 @@ function sessionFileIndex() {
|
|
|
7443
7465
|
}
|
|
7444
7466
|
}
|
|
7445
7467
|
if (scanScopeDepth > 0)
|
|
7446
|
-
indexCache = byId;
|
|
7468
|
+
indexCache = { byId, dirs, roots, checkedAt: Date.now() };
|
|
7447
7469
|
return byId;
|
|
7448
7470
|
}
|
|
7449
7471
|
function findSessionFiles(sessionId) {
|
|
@@ -7601,7 +7623,7 @@ function findCodexSessionFileIn(root, sessionId) {
|
|
|
7601
7623
|
}
|
|
7602
7624
|
return scanDir(root);
|
|
7603
7625
|
}
|
|
7604
|
-
var scanScopeDepth, indexCache, readScope, readScopeDepth, derivedScope, geminiCache, MAX_EVENT_LINE_BYTES;
|
|
7626
|
+
var scanScopeDepth, indexCache, INDEX_RECHECK_MS, dirMtime, readScope, readScopeDepth, derivedScope, geminiCache, MAX_EVENT_LINE_BYTES;
|
|
7605
7627
|
var init_live_session_scan = __esm({
|
|
7606
7628
|
"../engine/dist/core/live-session-scan.js"() {
|
|
7607
7629
|
"use strict";
|
|
@@ -7613,6 +7635,14 @@ var init_live_session_scan = __esm({
|
|
|
7613
7635
|
init_project_dir_name();
|
|
7614
7636
|
scanScopeDepth = 0;
|
|
7615
7637
|
indexCache = null;
|
|
7638
|
+
INDEX_RECHECK_MS = 1e3;
|
|
7639
|
+
dirMtime = (p) => {
|
|
7640
|
+
try {
|
|
7641
|
+
return statSync8(p).mtimeMs;
|
|
7642
|
+
} catch {
|
|
7643
|
+
return -1;
|
|
7644
|
+
}
|
|
7645
|
+
};
|
|
7616
7646
|
readScope = null;
|
|
7617
7647
|
readScopeDepth = 0;
|
|
7618
7648
|
derivedScope = null;
|
|
@@ -7828,7 +7858,7 @@ function gitLogRepo(repo, startIso, endIso) {
|
|
|
7828
7858
|
const authorIso = seg.slice(fieldPositions[1] + 1, fieldPositions[2]);
|
|
7829
7859
|
const authorName = seg.slice(fieldPositions[2] + 1, fieldPositions[3]);
|
|
7830
7860
|
const subject = seg.slice(fieldPositions[3] + 1, fieldPositions[4]);
|
|
7831
|
-
let
|
|
7861
|
+
let body2 = seg.slice(fieldPositions[4] + 1);
|
|
7832
7862
|
current = {
|
|
7833
7863
|
sha,
|
|
7834
7864
|
shortSha,
|
|
@@ -7842,11 +7872,11 @@ function gitLogRepo(repo, startIso, endIso) {
|
|
|
7842
7872
|
};
|
|
7843
7873
|
const recovered = [];
|
|
7844
7874
|
let m;
|
|
7845
|
-
while (m = TRAILING_NUMSTAT.exec(
|
|
7875
|
+
while (m = TRAILING_NUMSTAT.exec(body2)) {
|
|
7846
7876
|
recovered.unshift(m[1]);
|
|
7847
|
-
|
|
7877
|
+
body2 = body2.slice(0, body2.length - m[0].length);
|
|
7848
7878
|
}
|
|
7849
|
-
current.body =
|
|
7879
|
+
current.body = body2.trim();
|
|
7850
7880
|
for (const line of recovered)
|
|
7851
7881
|
recordNumstat(line);
|
|
7852
7882
|
} else if (NUMSTAT.test(seg)) {
|
|
@@ -8713,15 +8743,36 @@ var init_claude2 = __esm({
|
|
|
8713
8743
|
listSessions(opts = {}) {
|
|
8714
8744
|
const cutoff = opts.sinceMs ?? 0;
|
|
8715
8745
|
const filter = opts.projectFilter?.toLowerCase();
|
|
8716
|
-
const
|
|
8717
|
-
const out = [];
|
|
8746
|
+
const byId = /* @__PURE__ */ new Map();
|
|
8718
8747
|
for (const root of claudeProjectDirs()) {
|
|
8719
|
-
this.collectSessionsFromRoot(root,
|
|
8748
|
+
this.collectSessionsFromRoot(root, filter, byId, opts.previews !== false);
|
|
8720
8749
|
}
|
|
8750
|
+
const out = [...byId.values()].filter((r) => r.mtime >= cutoff);
|
|
8721
8751
|
out.sort((a, b) => b.mtime - a.mtime);
|
|
8722
8752
|
return opts.limit ? out.slice(0, opts.limit) : out;
|
|
8723
8753
|
}
|
|
8724
|
-
|
|
8754
|
+
/**
|
|
8755
|
+
* Add this root's sessions to `byId`. A session resumed under a second
|
|
8756
|
+
* profile has one file in each home under the same id. It is ONE session: the
|
|
8757
|
+
* first home keeps its path, and its mtime is the newest of all copies.
|
|
8758
|
+
*
|
|
8759
|
+
* Each copy was listed as its own ref before. Session 8da8d72b had a primary
|
|
8760
|
+
* copy frozen at 05:55 and a second-home copy growing until 12:00; the walk
|
|
8761
|
+
* handled both refs under one ledger row with two different mtimes, and the
|
|
8762
|
+
* row that shipped nothing stamped the other copy's mtime.
|
|
8763
|
+
*/
|
|
8764
|
+
collectSessionsFromRoot(root, filter, byId, previews) {
|
|
8765
|
+
const add = (ref) => {
|
|
8766
|
+
const prior = byId.get(ref.rawId);
|
|
8767
|
+
if (!prior) {
|
|
8768
|
+
byId.set(ref.rawId, ref);
|
|
8769
|
+
return;
|
|
8770
|
+
}
|
|
8771
|
+
if (ref.mtime > prior.mtime) {
|
|
8772
|
+
prior.mtime = ref.mtime;
|
|
8773
|
+
prior.modified = ref.modified;
|
|
8774
|
+
}
|
|
8775
|
+
};
|
|
8725
8776
|
if (!existsSync11(root))
|
|
8726
8777
|
return;
|
|
8727
8778
|
for (const proj of readdirSync9(root, { withFileTypes: true })) {
|
|
@@ -8732,6 +8783,7 @@ var init_claude2 = __esm({
|
|
|
8732
8783
|
const projPath = join13(root, proj.name);
|
|
8733
8784
|
const projectPath = resolveProjectDirName(proj.name);
|
|
8734
8785
|
const indexPath = join13(projPath, "sessions-index.json");
|
|
8786
|
+
const indexed = /* @__PURE__ */ new Set();
|
|
8735
8787
|
if (existsSync11(indexPath)) {
|
|
8736
8788
|
try {
|
|
8737
8789
|
const indexData = JSON.parse(readFileSync7(indexPath, "utf-8"));
|
|
@@ -8739,10 +8791,8 @@ var init_claude2 = __esm({
|
|
|
8739
8791
|
for (const entry of entries) {
|
|
8740
8792
|
if (!entry.fullPath || !existsSync11(entry.fullPath))
|
|
8741
8793
|
continue;
|
|
8742
|
-
|
|
8743
|
-
|
|
8744
|
-
seen.add(entry.sessionId);
|
|
8745
|
-
out.push({
|
|
8794
|
+
indexed.add(entry.sessionId);
|
|
8795
|
+
add({
|
|
8746
8796
|
toolId: "claude",
|
|
8747
8797
|
rawId: entry.sessionId,
|
|
8748
8798
|
prefixedId: entry.sessionId,
|
|
@@ -8769,7 +8819,7 @@ var init_claude2 = __esm({
|
|
|
8769
8819
|
if (!f.endsWith(".jsonl") || f === "sessions-index.json")
|
|
8770
8820
|
continue;
|
|
8771
8821
|
const sessionId = basename10(f, ".jsonl");
|
|
8772
|
-
if (
|
|
8822
|
+
if (indexed.has(sessionId))
|
|
8773
8823
|
continue;
|
|
8774
8824
|
const fullPath = join13(projPath, f);
|
|
8775
8825
|
let stat3;
|
|
@@ -8778,10 +8828,13 @@ var init_claude2 = __esm({
|
|
|
8778
8828
|
} catch {
|
|
8779
8829
|
continue;
|
|
8780
8830
|
}
|
|
8781
|
-
|
|
8831
|
+
const prior = byId.get(sessionId);
|
|
8832
|
+
if (prior) {
|
|
8833
|
+
add({ ...prior, mtime: stat3.mtimeMs, modified: stat3.mtime.toISOString() });
|
|
8782
8834
|
continue;
|
|
8835
|
+
}
|
|
8783
8836
|
const firstPrompt = previews ? extractFirstUserPromptSync(fullPath, { maxLength: 200 }) : "";
|
|
8784
|
-
|
|
8837
|
+
add({
|
|
8785
8838
|
toolId: "claude",
|
|
8786
8839
|
rawId: sessionId,
|
|
8787
8840
|
prefixedId: sessionId,
|
|
@@ -8911,20 +8964,20 @@ var init_claude2 = __esm({
|
|
|
8911
8964
|
text += (text ? "\n" : "") + it.text;
|
|
8912
8965
|
} else if (it.type === "tool_result") {
|
|
8913
8966
|
const id = typeof it.tool_use_id === "string" ? it.tool_use_id : "";
|
|
8914
|
-
let
|
|
8967
|
+
let body2 = "";
|
|
8915
8968
|
const c = it.content;
|
|
8916
8969
|
if (typeof c === "string")
|
|
8917
|
-
|
|
8970
|
+
body2 = c;
|
|
8918
8971
|
else if (Array.isArray(c)) {
|
|
8919
8972
|
for (const sub of c) {
|
|
8920
8973
|
if (sub && typeof sub === "object" && sub.type === "text") {
|
|
8921
8974
|
const t = sub.text;
|
|
8922
8975
|
if (typeof t === "string")
|
|
8923
|
-
|
|
8976
|
+
body2 += t;
|
|
8924
8977
|
}
|
|
8925
8978
|
}
|
|
8926
8979
|
}
|
|
8927
|
-
toolResults.push({ id, body, isError: it.is_error === true });
|
|
8980
|
+
toolResults.push({ id, body: body2, isError: it.is_error === true });
|
|
8928
8981
|
}
|
|
8929
8982
|
}
|
|
8930
8983
|
}
|
|
@@ -9556,7 +9609,7 @@ var init_opencode = __esm({
|
|
|
9556
9609
|
});
|
|
9557
9610
|
const out = d?.state?.output;
|
|
9558
9611
|
if (out !== void 0 && out !== null) {
|
|
9559
|
-
const
|
|
9612
|
+
const body2 = typeof out === "string" ? out : JSON.stringify(out);
|
|
9560
9613
|
events.push({
|
|
9561
9614
|
kind: "tool_result",
|
|
9562
9615
|
ts,
|
|
@@ -9564,9 +9617,9 @@ var init_opencode = __esm({
|
|
|
9564
9617
|
line: lineNum,
|
|
9565
9618
|
toolUseId: callId,
|
|
9566
9619
|
toolName,
|
|
9567
|
-
resultBody:
|
|
9620
|
+
resultBody: body2,
|
|
9568
9621
|
resultIsError: d?.state?.status === "error",
|
|
9569
|
-
resultBytes:
|
|
9622
|
+
resultBytes: body2.length
|
|
9570
9623
|
});
|
|
9571
9624
|
}
|
|
9572
9625
|
}
|
|
@@ -10270,13 +10323,13 @@ var init_codex2 = __esm({
|
|
|
10270
10323
|
} else if (obj.type === "response_item" && payload.type === "function_call_output") {
|
|
10271
10324
|
const callId = String(payload.call_id || "");
|
|
10272
10325
|
const out = payload.output;
|
|
10273
|
-
let
|
|
10326
|
+
let body2 = "";
|
|
10274
10327
|
let isError = false;
|
|
10275
10328
|
let exitCode;
|
|
10276
10329
|
if (typeof out === "string")
|
|
10277
|
-
|
|
10330
|
+
body2 = out;
|
|
10278
10331
|
else if (out && typeof out === "object") {
|
|
10279
|
-
|
|
10332
|
+
body2 = String(out.content ?? out.output ?? out.stdout ?? out.text ?? "");
|
|
10280
10333
|
if (typeof out.exit_code === "number") {
|
|
10281
10334
|
exitCode = out.exit_code;
|
|
10282
10335
|
isError = exitCode !== 0;
|
|
@@ -10288,10 +10341,10 @@ var init_codex2 = __esm({
|
|
|
10288
10341
|
tsIso,
|
|
10289
10342
|
line: lineNum,
|
|
10290
10343
|
toolUseId: callId,
|
|
10291
|
-
resultBody:
|
|
10344
|
+
resultBody: body2,
|
|
10292
10345
|
resultIsError: isError,
|
|
10293
10346
|
resultExitCode: exitCode,
|
|
10294
|
-
resultBytes:
|
|
10347
|
+
resultBytes: body2.length
|
|
10295
10348
|
});
|
|
10296
10349
|
}
|
|
10297
10350
|
}
|
|
@@ -11313,15 +11366,15 @@ function messagesToEvents(messages, createdAtMs, updatedAtMs) {
|
|
|
11313
11366
|
const p = part;
|
|
11314
11367
|
if (p.type !== "tool-result")
|
|
11315
11368
|
continue;
|
|
11316
|
-
const
|
|
11369
|
+
const body2 = resultBodyOf(p);
|
|
11317
11370
|
events.push({
|
|
11318
11371
|
kind: "tool_result",
|
|
11319
11372
|
ts: clock,
|
|
11320
11373
|
line: 0,
|
|
11321
11374
|
toolUseId: typeof p.toolCallId === "string" ? p.toolCallId : void 0,
|
|
11322
|
-
resultBody:
|
|
11375
|
+
resultBody: body2,
|
|
11323
11376
|
resultIsError: isError,
|
|
11324
|
-
resultBytes: Buffer.byteLength(
|
|
11377
|
+
resultBytes: Buffer.byteLength(body2, "utf8")
|
|
11325
11378
|
});
|
|
11326
11379
|
}
|
|
11327
11380
|
}
|
|
@@ -12184,11 +12237,11 @@ var init_cursor = __esm({
|
|
|
12184
12237
|
const events = readComposerEvents(r.composer);
|
|
12185
12238
|
if (events.length === 0)
|
|
12186
12239
|
return null;
|
|
12187
|
-
const
|
|
12240
|
+
const body2 = events.map((e) => JSON.stringify(e)).join("\n") + "\n";
|
|
12188
12241
|
return {
|
|
12189
12242
|
tool: "cursor",
|
|
12190
12243
|
mtime: statSync15(r.composer.globalDbPath).mtimeMs,
|
|
12191
|
-
files: [{ name: `${r.composer.composerId}.jsonl`, bytes: Buffer.from(
|
|
12244
|
+
files: [{ name: `${r.composer.composerId}.jsonl`, bytes: Buffer.from(body2, "utf8") }]
|
|
12192
12245
|
};
|
|
12193
12246
|
} catch {
|
|
12194
12247
|
return null;
|
|
@@ -13599,11 +13652,11 @@ async function collectCode(opts) {
|
|
|
13599
13652
|
for (const block of log22.split("\0END\0")) {
|
|
13600
13653
|
if (!block.includes("\0"))
|
|
13601
13654
|
continue;
|
|
13602
|
-
const [h,
|
|
13655
|
+
const [h, body2] = [block.slice(0, block.indexOf("\0")).trim(), block.slice(block.indexOf("\0") + 1)];
|
|
13603
13656
|
if (!h)
|
|
13604
13657
|
continue;
|
|
13605
13658
|
totalCommits++;
|
|
13606
|
-
if (AI_RE.test(
|
|
13659
|
+
if (AI_RE.test(body2)) {
|
|
13607
13660
|
aiCommits++;
|
|
13608
13661
|
try {
|
|
13609
13662
|
const files2 = execSync3(`git -C "${ws}" show --format= --name-only ${h}`, { encoding: "utf8", timeout: 2e4, maxBuffer: 64 * 1024 * 1024, stdio: ["ignore", "pipe", "ignore"] });
|
|
@@ -15773,8 +15826,8 @@ function parseFrontmatter(content) {
|
|
|
15773
15826
|
return { body: content };
|
|
15774
15827
|
const header = content.slice(3, close);
|
|
15775
15828
|
const afterFence = content.indexOf("\n", close + 1);
|
|
15776
|
-
const
|
|
15777
|
-
const out = { body };
|
|
15829
|
+
const body2 = afterFence === -1 ? "" : content.slice(afterFence + 1);
|
|
15830
|
+
const out = { body: body2 };
|
|
15778
15831
|
for (const line of header.split("\n")) {
|
|
15779
15832
|
const m = line.match(/^\s*([A-Za-z_]+)\s*:\s*(.+?)\s*$/);
|
|
15780
15833
|
if (!m)
|
|
@@ -15866,9 +15919,9 @@ var init_plan_source = __esm({
|
|
|
15866
15919
|
const isAgentPlan = planName.includes("-agent-");
|
|
15867
15920
|
const content = readFileSync20(filePath, "utf-8");
|
|
15868
15921
|
const fm = parseFrontmatter(content);
|
|
15869
|
-
const
|
|
15870
|
-
const firstLine =
|
|
15871
|
-
const projectPath = fm.cwd || this.extractProjectPath(
|
|
15922
|
+
const body2 = fm.body;
|
|
15923
|
+
const firstLine = body2.split("\n").find((l) => l.trim())?.replace(/^#+\s*/, "").trim() || planName;
|
|
15924
|
+
const projectPath = fm.cwd || this.extractProjectPath(body2);
|
|
15872
15925
|
const baseName = planName.replace(/-agent-[a-z0-9]+$/i, "");
|
|
15873
15926
|
const claudeSessionId = fm.sessionId || (UUID_RE3.test(baseName) ? baseName : void 0);
|
|
15874
15927
|
yield {
|
|
@@ -15878,7 +15931,7 @@ var init_plan_source = __esm({
|
|
|
15878
15931
|
projectPath,
|
|
15879
15932
|
filePath,
|
|
15880
15933
|
mtime: stat3.mtimeMs,
|
|
15881
|
-
contentPreview:
|
|
15934
|
+
contentPreview: body2.slice(0, 300),
|
|
15882
15935
|
extra: {
|
|
15883
15936
|
tool: "claude",
|
|
15884
15937
|
isAgentPlan,
|
|
@@ -15985,8 +16038,8 @@ var init_plan_source = __esm({
|
|
|
15985
16038
|
return [];
|
|
15986
16039
|
const content = readFileSync20(item.filePath, "utf-8");
|
|
15987
16040
|
const chunks = [];
|
|
15988
|
-
const { body } = parseFrontmatter(content);
|
|
15989
|
-
const sections = splitByHeaders(
|
|
16041
|
+
const { body: body2 } = parseFrontmatter(content);
|
|
16042
|
+
const sections = splitByHeaders(body2);
|
|
15990
16043
|
for (let i = 0; i < sections.length; i++) {
|
|
15991
16044
|
const section = sections[i];
|
|
15992
16045
|
if (!section.text.trim() || section.text.trim().length < 20)
|
|
@@ -16619,10 +16672,10 @@ import { homedir as homedir11 } from "os";
|
|
|
16619
16672
|
import { join as join34, basename as basename16 } from "path";
|
|
16620
16673
|
function parseFrontmatter2(content) {
|
|
16621
16674
|
const fm = {};
|
|
16622
|
-
let
|
|
16675
|
+
let body2 = content;
|
|
16623
16676
|
const m = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)$/);
|
|
16624
16677
|
if (m) {
|
|
16625
|
-
|
|
16678
|
+
body2 = m[2];
|
|
16626
16679
|
for (const line of m[1].split(/\r?\n/)) {
|
|
16627
16680
|
const t = line.trim();
|
|
16628
16681
|
if (!t || t.startsWith("#"))
|
|
@@ -16639,7 +16692,7 @@ function parseFrontmatter2(content) {
|
|
|
16639
16692
|
fm[k] = v;
|
|
16640
16693
|
}
|
|
16641
16694
|
}
|
|
16642
|
-
return { fm, body };
|
|
16695
|
+
return { fm, body: body2 };
|
|
16643
16696
|
}
|
|
16644
16697
|
function projectHashToPath(hashDir) {
|
|
16645
16698
|
return resolveProjectDirName(hashDir);
|
|
@@ -16716,9 +16769,9 @@ var init_agent_memory_source = __esm({
|
|
|
16716
16769
|
if (!existsSync29(item.filePath))
|
|
16717
16770
|
return [];
|
|
16718
16771
|
const content = readFileSync23(item.filePath, "utf-8");
|
|
16719
|
-
const { body } = parseFrontmatter2(content);
|
|
16772
|
+
const { body: body2 } = parseFrontmatter2(content);
|
|
16720
16773
|
const chunks = [];
|
|
16721
|
-
const sections = splitByHeaders(
|
|
16774
|
+
const sections = splitByHeaders(body2);
|
|
16722
16775
|
for (let i = 0; i < sections.length; i++) {
|
|
16723
16776
|
const section = sections[i];
|
|
16724
16777
|
if (!section.text.trim() || section.text.trim().length < 20)
|
|
@@ -18438,11 +18491,11 @@ function parseCommand(content, fallbackName, format) {
|
|
|
18438
18491
|
body: t.prompt || ""
|
|
18439
18492
|
};
|
|
18440
18493
|
}
|
|
18441
|
-
const { fm, body } = parseFrontmatter4(content);
|
|
18494
|
+
const { fm, body: body2 } = parseFrontmatter4(content);
|
|
18442
18495
|
return {
|
|
18443
18496
|
name: fm.name || fallbackName,
|
|
18444
18497
|
description: fm.description || "",
|
|
18445
|
-
body:
|
|
18498
|
+
body: body2.trim()
|
|
18446
18499
|
};
|
|
18447
18500
|
}
|
|
18448
18501
|
var MAX_CHUNK_CHARS12, MAX_BODY_CHARS2, SlashCommandsSource;
|
|
@@ -18586,12 +18639,12 @@ function parseAgent(content, fallbackName, format) {
|
|
|
18586
18639
|
body: t.developer_instructions || t.instructions || ""
|
|
18587
18640
|
};
|
|
18588
18641
|
}
|
|
18589
|
-
const { fm, body } = parseFrontmatter4(content);
|
|
18642
|
+
const { fm, body: body2 } = parseFrontmatter4(content);
|
|
18590
18643
|
return {
|
|
18591
18644
|
name: fm.name || fallbackName,
|
|
18592
18645
|
description: fm.description || "",
|
|
18593
18646
|
tools: fm.tools || "",
|
|
18594
|
-
body:
|
|
18647
|
+
body: body2.trim()
|
|
18595
18648
|
};
|
|
18596
18649
|
}
|
|
18597
18650
|
var MAX_CHUNK_CHARS13, MAX_BODY_CHARS3, SubagentsSource;
|
|
@@ -19495,7 +19548,7 @@ var init_http = __esm({
|
|
|
19495
19548
|
CLI_VERSION = JSON.parse(readFileSync36(new URL("../package.json", import.meta.url), "utf-8")).version || CLI_VERSION;
|
|
19496
19549
|
} catch {
|
|
19497
19550
|
try {
|
|
19498
|
-
if (true) CLI_VERSION = "0.7.
|
|
19551
|
+
if (true) CLI_VERSION = "0.7.8";
|
|
19499
19552
|
} catch {
|
|
19500
19553
|
}
|
|
19501
19554
|
}
|
|
@@ -19994,7 +20047,7 @@ var init_telemetry = __esm({
|
|
|
19994
20047
|
init_sync_client();
|
|
19995
20048
|
cliVersion2 = () => {
|
|
19996
20049
|
try {
|
|
19997
|
-
return true ? "0.7.
|
|
20050
|
+
return true ? "0.7.8" : "dev";
|
|
19998
20051
|
} catch {
|
|
19999
20052
|
return "dev";
|
|
20000
20053
|
}
|
|
@@ -20035,20 +20088,20 @@ async function probeServer(base, opts = {}) {
|
|
|
20035
20088
|
if (res.status === 404 || res.status === 410) return { kind: "moved", status: res.status };
|
|
20036
20089
|
if (res.status >= 500) return { kind: "server_error", status: res.status };
|
|
20037
20090
|
if (!res.ok) return { kind: "moved", status: res.status };
|
|
20038
|
-
let
|
|
20091
|
+
let body2;
|
|
20039
20092
|
try {
|
|
20040
|
-
|
|
20093
|
+
body2 = await res.text();
|
|
20041
20094
|
} catch (err) {
|
|
20042
20095
|
return { kind: "unreachable", error: describeCause(err), reason: classifyUnreachable(err) };
|
|
20043
20096
|
}
|
|
20044
20097
|
let parsed;
|
|
20045
20098
|
try {
|
|
20046
|
-
parsed = JSON.parse(
|
|
20099
|
+
parsed = JSON.parse(body2);
|
|
20047
20100
|
} catch {
|
|
20048
|
-
return { kind: "not_api", status: res.status, snippet:
|
|
20101
|
+
return { kind: "not_api", status: res.status, snippet: body2.trim().slice(0, 60) };
|
|
20049
20102
|
}
|
|
20050
20103
|
if (typeof parsed?.apiVersion !== "number") {
|
|
20051
|
-
return { kind: "not_api", status: res.status, snippet:
|
|
20104
|
+
return { kind: "not_api", status: res.status, snippet: body2.trim().slice(0, 60) };
|
|
20052
20105
|
}
|
|
20053
20106
|
return {
|
|
20054
20107
|
kind: "ok",
|
|
@@ -20533,10 +20586,10 @@ async function fetchTenantSecurityConfig(cred) {
|
|
|
20533
20586
|
let tenantRules = [];
|
|
20534
20587
|
let rulePackVersion = null;
|
|
20535
20588
|
if (rulesRes.ok) {
|
|
20536
|
-
const
|
|
20537
|
-
const enabled = (
|
|
20589
|
+
const body2 = await rulesRes.json().catch(() => ({}));
|
|
20590
|
+
const enabled = (body2.rules || []).filter((r) => r.enabled !== false);
|
|
20538
20591
|
tenantRules = enabled;
|
|
20539
|
-
const packSpec =
|
|
20592
|
+
const packSpec = body2.pack?.rules ? { version: body2.pack.version, rules: body2.pack.rules.map((r) => ({ name: r.name, regex: r.regex, flags: r.flags, redact: true, source: r.source })) } : { version: body2.version, rules: enabled.filter((r) => r.redact === true).map((r) => ({ name: r.name, regex: r.regex, redact: true, source: "tenant" })) };
|
|
20540
20593
|
lastServerPackSpec = { version: packSpec.version || "", rules: packSpec.rules };
|
|
20541
20594
|
const pack = installServerRulePack(packSpec);
|
|
20542
20595
|
rulePackVersion = serverRulePackVersion();
|
|
@@ -20544,20 +20597,20 @@ async function fetchTenantSecurityConfig(cred) {
|
|
|
20544
20597
|
console.error(`[sync] server redaction rule "${r.name}" ignored: ${r.reason}`);
|
|
20545
20598
|
}
|
|
20546
20599
|
if (pack.accepted > 0) {
|
|
20547
|
-
const rev =
|
|
20600
|
+
const rev = body2.pack?.revision ? `, rev ${body2.pack.revision}` : "";
|
|
20548
20601
|
console.error(`[sync] ${pack.accepted} server redaction rule(s) active (pack ${rulePackVersion}${rev})`);
|
|
20549
20602
|
}
|
|
20550
20603
|
}
|
|
20551
20604
|
let verifySecrets = true;
|
|
20552
20605
|
if (settingsRes?.ok) {
|
|
20553
|
-
const
|
|
20554
|
-
if (typeof
|
|
20555
|
-
if (typeof
|
|
20556
|
-
setTelemetryEligible(base,
|
|
20606
|
+
const body2 = await settingsRes.json().catch(() => ({}));
|
|
20607
|
+
if (typeof body2.verifySecrets === "boolean") verifySecrets = body2.verifySecrets;
|
|
20608
|
+
if (typeof body2.telemetry === "boolean") {
|
|
20609
|
+
setTelemetryEligible(base, body2.telemetry);
|
|
20557
20610
|
try {
|
|
20558
20611
|
const prior = readCollectorHealth()?.telemetryEligible ?? {};
|
|
20559
20612
|
updateCollectorHealth({
|
|
20560
|
-
telemetryEligible: { ...prior, [base]: { allowed:
|
|
20613
|
+
telemetryEligible: { ...prior, [base]: { allowed: body2.telemetry, at: Date.now() } }
|
|
20561
20614
|
});
|
|
20562
20615
|
} catch {
|
|
20563
20616
|
}
|
|
@@ -20616,14 +20669,14 @@ async function fetchTenantSyncConfig(cred) {
|
|
|
20616
20669
|
return EMPTY_SYNC_CONFIG;
|
|
20617
20670
|
}
|
|
20618
20671
|
if (!res.ok) return lastKnownSyncConfig(base, `HTTP ${res.status}`);
|
|
20619
|
-
const
|
|
20672
|
+
const body2 = await res.json().catch(() => ({}));
|
|
20620
20673
|
const config2 = {
|
|
20621
|
-
excludeTools: Array.isArray(
|
|
20622
|
-
excludeProjects: Array.isArray(
|
|
20674
|
+
excludeTools: Array.isArray(body2.excludeTools) ? body2.excludeTools.filter((t) => typeof t === "string") : [],
|
|
20675
|
+
excludeProjects: Array.isArray(body2.excludeProjects) ? body2.excludeProjects.filter((p) => typeof p === "string") : [],
|
|
20623
20676
|
// Ids only — installSourceExclusions drops anything else, so a server
|
|
20624
20677
|
// sending a path cannot make this collector read it.
|
|
20625
|
-
excludeSources: Array.isArray(
|
|
20626
|
-
approveSources: Array.isArray(
|
|
20678
|
+
excludeSources: Array.isArray(body2.excludeSources) ? body2.excludeSources.filter((x) => typeof x === "string") : [],
|
|
20679
|
+
approveSources: Array.isArray(body2.approveSources) ? body2.approveSources.filter((x) => typeof x === "string") : []
|
|
20627
20680
|
};
|
|
20628
20681
|
const applied = installSourceExclusions(config2.excludeSources);
|
|
20629
20682
|
if (applied.excluded > 0) {
|
|
@@ -21018,9 +21071,9 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
21018
21071
|
const maxInflight = explicitThrottle ? 1 : isLocalHost(cred.serverUrl) ? Math.max(LOCAL_INFLIGHT, advertised ?? 0) : advertised ?? REMOTE_INFLIGHT_FALLBACK;
|
|
21019
21072
|
const UPLOAD_TIMEOUT_MS = Number(process.env.CHAT_RECALL_UPLOAD_TIMEOUT_MS) || 9e4;
|
|
21020
21073
|
const gzipBody = serverAcceptsGzipBody(cred.serverUrl);
|
|
21021
|
-
const post = async (
|
|
21074
|
+
const post = async (body2) => {
|
|
21022
21075
|
const payload = (() => {
|
|
21023
|
-
const withProgress = walkProgressForUpload ? { ...
|
|
21076
|
+
const withProgress = walkProgressForUpload ? { ...body2, progress: walkProgressForUpload } : body2;
|
|
21024
21077
|
const json = JSON.stringify(withProgress);
|
|
21025
21078
|
const raw = Buffer.from(json, "utf8");
|
|
21026
21079
|
return gzipBody ? gzipSync3(raw, { level: 6 }) : raw;
|
|
@@ -21044,25 +21097,25 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
21044
21097
|
});
|
|
21045
21098
|
if (res.ok) {
|
|
21046
21099
|
acceptedUploads.set(cred.serverUrl, (acceptedUploads.get(cred.serverUrl) ?? 0) + 1);
|
|
21047
|
-
const
|
|
21048
|
-
if (typeof
|
|
21049
|
-
setTelemetryEligible(base,
|
|
21100
|
+
const body3 = await res.json().catch(() => ({}));
|
|
21101
|
+
if (typeof body3.telemetry === "boolean") {
|
|
21102
|
+
setTelemetryEligible(base, body3.telemetry);
|
|
21050
21103
|
try {
|
|
21051
21104
|
const prior = readCollectorHealth()?.telemetryEligible ?? {};
|
|
21052
21105
|
updateCollectorHealth({
|
|
21053
|
-
telemetryEligible: { ...prior, [base]: { allowed:
|
|
21106
|
+
telemetryEligible: { ...prior, [base]: { allowed: body3.telemetry, at: Date.now() } }
|
|
21054
21107
|
});
|
|
21055
21108
|
} catch {
|
|
21056
21109
|
}
|
|
21057
21110
|
}
|
|
21058
|
-
const adv =
|
|
21111
|
+
const adv = body3.limits?.ingestConcurrencyPerTenant;
|
|
21059
21112
|
if (typeof adv === "number" && Number.isFinite(adv) && adv >= 1) {
|
|
21060
21113
|
setServerIngestConcurrency(base, Math.floor(adv));
|
|
21061
21114
|
}
|
|
21062
|
-
if (
|
|
21115
|
+
if (body3.cli && body3.cli.version) {
|
|
21063
21116
|
const { planAutoUpdate: planAutoUpdate2, runAutoUpdate: runAutoUpdate2, sweepStaleStaging: sweepStaleStaging2 } = await Promise.resolve().then(() => (init_auto_update(), auto_update_exports));
|
|
21064
21117
|
const authHeaders = cred.token ? { authorization: `Bearer ${cred.token}` } : {};
|
|
21065
|
-
const plan = planAutoUpdate2(base, { cli:
|
|
21118
|
+
const plan = planAutoUpdate2(base, { cli: body3.cli }, ownVersion, process.env.CHAT_RECALL_AUTO_UPDATE);
|
|
21066
21119
|
sweepStaleStaging2();
|
|
21067
21120
|
if (plan.update) {
|
|
21068
21121
|
void runAutoUpdate2(base, authHeaders, ownVersion).catch(() => {
|
|
@@ -21079,13 +21132,13 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
21079
21132
|
if (res.status === 402) {
|
|
21080
21133
|
let detail = text;
|
|
21081
21134
|
try {
|
|
21082
|
-
const
|
|
21083
|
-
detail =
|
|
21084
|
-
if (
|
|
21085
|
-
${
|
|
21086
|
-
if (
|
|
21087
|
-
if (
|
|
21088
|
-
${
|
|
21135
|
+
const body3 = JSON.parse(text);
|
|
21136
|
+
detail = body3.error ?? text;
|
|
21137
|
+
if (body3.detail) detail += `
|
|
21138
|
+
${body3.detail}`;
|
|
21139
|
+
if (body3.resetsAt) detail += ` (resets ${new Date(body3.resetsAt).toISOString().slice(0, 10)})`;
|
|
21140
|
+
if (body3.upgradeUrl) detail += `
|
|
21141
|
+
${body3.upgradeUrl}`;
|
|
21089
21142
|
} catch {
|
|
21090
21143
|
}
|
|
21091
21144
|
throw classedError("payment_required", `sync paused by the server: ${detail}`);
|
|
@@ -21114,7 +21167,7 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
21114
21167
|
let poolError = null;
|
|
21115
21168
|
const MAX_INFLIGHT_BYTES = (Number(process.env.CHAT_RECALL_MAX_INFLIGHT_MB) || 32) * 1024 * 1024;
|
|
21116
21169
|
let inflightBytes = 0;
|
|
21117
|
-
const submit = async (
|
|
21170
|
+
const submit = async (body2, after, bytesHint = 0) => {
|
|
21118
21171
|
while (inflight.size > 0 && (inflight.size >= maxInflight || inflightBytes + bytesHint > MAX_INFLIGHT_BYTES)) {
|
|
21119
21172
|
await Promise.race(inflight);
|
|
21120
21173
|
}
|
|
@@ -21122,7 +21175,7 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
21122
21175
|
inflightBytes += bytesHint;
|
|
21123
21176
|
let task;
|
|
21124
21177
|
task = (async () => {
|
|
21125
|
-
await post(
|
|
21178
|
+
await post(body2);
|
|
21126
21179
|
after();
|
|
21127
21180
|
})().catch((e) => {
|
|
21128
21181
|
poolError = poolError ?? e;
|
|
@@ -21189,18 +21242,18 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
21189
21242
|
signal: ac.signal
|
|
21190
21243
|
});
|
|
21191
21244
|
if (!res.ok) throw new Error(`append sync failed: HTTP ${res.status} ${await res.text().catch(() => "")}`);
|
|
21192
|
-
const
|
|
21193
|
-
if (
|
|
21245
|
+
const body2 = await res.json().catch(() => ({}));
|
|
21246
|
+
if (body2.cli && body2.cli.version) {
|
|
21194
21247
|
const { planAutoUpdate: planAutoUpdate2, runAutoUpdate: runAutoUpdate2, sweepStaleStaging: sweepStaleStaging2 } = await Promise.resolve().then(() => (init_auto_update(), auto_update_exports));
|
|
21195
21248
|
const authHeaders = cred.token ? { authorization: `Bearer ${cred.token}` } : {};
|
|
21196
|
-
const plan = planAutoUpdate2(base, { cli:
|
|
21249
|
+
const plan = planAutoUpdate2(base, { cli: body2.cli }, ownVersion, process.env.CHAT_RECALL_AUTO_UPDATE);
|
|
21197
21250
|
sweepStaleStaging2();
|
|
21198
21251
|
if (plan.update) {
|
|
21199
21252
|
void runAutoUpdate2(base, authHeaders, ownVersion).catch(() => {
|
|
21200
21253
|
});
|
|
21201
21254
|
}
|
|
21202
21255
|
}
|
|
21203
|
-
const fullNeeded = new Set(
|
|
21256
|
+
const fullNeeded = new Set(body2.full_resync_needed ?? []);
|
|
21204
21257
|
const acked = convs.filter((c) => !fullNeeded.has(c.session_id));
|
|
21205
21258
|
if (acked.length > 0) {
|
|
21206
21259
|
try {
|
|
@@ -21235,9 +21288,9 @@ async function syncToTargetInScope(cred, opts = {}) {
|
|
|
21235
21288
|
signal: ac.signal
|
|
21236
21289
|
});
|
|
21237
21290
|
if (!res.ok) throw new Error(`chunked head sync failed: HTTP ${res.status} ${await res.text().catch(() => "")}`);
|
|
21238
|
-
const
|
|
21291
|
+
const body2 = await res.json().catch(() => ({}));
|
|
21239
21292
|
const id = conv.session_id;
|
|
21240
|
-
const guarded = (
|
|
21293
|
+
const guarded = (body2.shrink_guarded ?? []).find((g) => g.session_id === id);
|
|
21241
21294
|
const mtime = conv.mtime;
|
|
21242
21295
|
try {
|
|
21243
21296
|
if (!guarded) {
|
|
@@ -22114,7 +22167,7 @@ var init_sync_client = __esm({
|
|
|
22114
22167
|
ownVersion = JSON.parse(readFileSync38(join49(dir, "../package.json"), "utf-8")).version || ownVersion;
|
|
22115
22168
|
} catch {
|
|
22116
22169
|
try {
|
|
22117
|
-
if (true) ownVersion = "0.7.
|
|
22170
|
+
if (true) ownVersion = "0.7.8";
|
|
22118
22171
|
} catch {
|
|
22119
22172
|
}
|
|
22120
22173
|
}
|
|
@@ -22175,7 +22228,7 @@ function reportClientEvent2(kind, opts = {}) {
|
|
|
22175
22228
|
try {
|
|
22176
22229
|
const targets = loadAllCredentials();
|
|
22177
22230
|
if (!targets.length) return;
|
|
22178
|
-
const
|
|
22231
|
+
const body2 = JSON.stringify({
|
|
22179
22232
|
events: [{
|
|
22180
22233
|
ts: Date.now(),
|
|
22181
22234
|
kind: String(kind).slice(0, 64),
|
|
@@ -22192,7 +22245,7 @@ function reportClientEvent2(kind, opts = {}) {
|
|
|
22192
22245
|
void fetch(`${t.serverUrl.replace(/\/+$/, "")}/api/client-events`, {
|
|
22193
22246
|
method: "POST",
|
|
22194
22247
|
headers,
|
|
22195
|
-
body,
|
|
22248
|
+
body: body2,
|
|
22196
22249
|
signal: AbortSignal.timeout(3e3)
|
|
22197
22250
|
}).catch(() => {
|
|
22198
22251
|
});
|
|
@@ -22258,8 +22311,8 @@ async function fetchNpmLatest() {
|
|
|
22258
22311
|
headers: { accept: "application/vnd.npm.install-v1+json" }
|
|
22259
22312
|
}, 5e3);
|
|
22260
22313
|
if (!res.ok) return null;
|
|
22261
|
-
const
|
|
22262
|
-
const v =
|
|
22314
|
+
const body2 = await res.json();
|
|
22315
|
+
const v = body2["dist-tags"]?.latest;
|
|
22263
22316
|
return typeof v === "string" && v ? v : null;
|
|
22264
22317
|
} catch {
|
|
22265
22318
|
return null;
|
|
@@ -23538,9 +23591,9 @@ function splitSummary(summary) {
|
|
|
23538
23591
|
marks.sort((a, b) => a.start - b.start);
|
|
23539
23592
|
for (let i = 0; i < marks.length; i++) {
|
|
23540
23593
|
const stop = i + 1 < marks.length ? marks[i + 1].end : summary.length;
|
|
23541
|
-
const
|
|
23542
|
-
if (
|
|
23543
|
-
out[marks[i].key] =
|
|
23594
|
+
const body2 = summary.slice(marks[i].start, stop).trim();
|
|
23595
|
+
if (body2)
|
|
23596
|
+
out[marks[i].key] = body2;
|
|
23544
23597
|
}
|
|
23545
23598
|
return out;
|
|
23546
23599
|
}
|
|
@@ -23640,6 +23693,104 @@ function crossProjectNote(headProject, priorRows, hasProjectFilter) {
|
|
|
23640
23693
|
return `_These sessions span ${names.size} projects (${[...names].join(", ")}). Pass \`project_filter\` to scope the resume to one._`;
|
|
23641
23694
|
}
|
|
23642
23695
|
|
|
23696
|
+
// ../engine/dist/mcp/show-render.js
|
|
23697
|
+
var BODY_LIMIT = 2e3;
|
|
23698
|
+
var EDGE = 100;
|
|
23699
|
+
var INDENT = " ";
|
|
23700
|
+
var oneLine = (v) => String(v).replace(/\s*\n\s*/g, " \u23CE ");
|
|
23701
|
+
var indent = (text) => text.split("\n").map((l) => INDENT + l).join("\n");
|
|
23702
|
+
function safeSlice(s, start, end) {
|
|
23703
|
+
const lo = start > 0 && /[\uDC00-\uDFFF]/.test(s[start] ?? "") ? start - 1 : start;
|
|
23704
|
+
const hi = end < s.length && /[\uD800-\uDBFF]/.test(s[end - 1] ?? "") ? end + 1 : end;
|
|
23705
|
+
return s.slice(lo, hi);
|
|
23706
|
+
}
|
|
23707
|
+
function body(text, line, opts) {
|
|
23708
|
+
if (opts.full || text.length <= BODY_LIMIT)
|
|
23709
|
+
return indent(text);
|
|
23710
|
+
const lines = text.split("\n").length;
|
|
23711
|
+
const cut = text.length - 2 * EDGE;
|
|
23712
|
+
const how = opts.expandHint ? ` ${opts.expandHint(line)}` : "";
|
|
23713
|
+
return [
|
|
23714
|
+
indent(safeSlice(text, 0, EDGE)),
|
|
23715
|
+
`${INDENT}\u2026 ${cut} characters cut (${text.length} in all, ${lines} lines).${how} \u2026`,
|
|
23716
|
+
indent(safeSlice(text, text.length - EDGE, text.length))
|
|
23717
|
+
].join("\n");
|
|
23718
|
+
}
|
|
23719
|
+
function resultText(result) {
|
|
23720
|
+
if (typeof result === "string")
|
|
23721
|
+
return result;
|
|
23722
|
+
if (Array.isArray(result)) {
|
|
23723
|
+
return result.map((b) => {
|
|
23724
|
+
if (b?.type === "text")
|
|
23725
|
+
return String(b.text ?? "");
|
|
23726
|
+
if (b?.type === "image")
|
|
23727
|
+
return "[image]";
|
|
23728
|
+
if (b?.type === "tool_reference")
|
|
23729
|
+
return `[tool_reference: ${b.tool_name ?? ""}]`;
|
|
23730
|
+
return JSON.stringify(b);
|
|
23731
|
+
}).join("\n");
|
|
23732
|
+
}
|
|
23733
|
+
return JSON.stringify(result);
|
|
23734
|
+
}
|
|
23735
|
+
function renderToolCall(tc, line, opts) {
|
|
23736
|
+
const inp = tc.input && typeof tc.input === "object" ? tc.input : {};
|
|
23737
|
+
const out = [];
|
|
23738
|
+
if (tc.name === "Bash" && inp.command !== void 0) {
|
|
23739
|
+
const desc = inp.description ? ` # ${oneLine(inp.description)}` : "";
|
|
23740
|
+
out.push(`[Bash]${desc}
|
|
23741
|
+
$ ${oneLine(inp.command)}`);
|
|
23742
|
+
} else if (inp.file_path !== void 0) {
|
|
23743
|
+
const inline = {};
|
|
23744
|
+
const blocks = [];
|
|
23745
|
+
for (const [k, v] of Object.entries(inp)) {
|
|
23746
|
+
if (k === "file_path")
|
|
23747
|
+
continue;
|
|
23748
|
+
if (typeof v === "string" && (v.includes("\n") || v.length > 120))
|
|
23749
|
+
blocks.push([k, v]);
|
|
23750
|
+
else
|
|
23751
|
+
inline[k] = v;
|
|
23752
|
+
}
|
|
23753
|
+
const opts_ = Object.keys(inline).length ? ` ${JSON.stringify(inline)}` : "";
|
|
23754
|
+
out.push(`[${tc.name}] ${inp.file_path}${opts_}`);
|
|
23755
|
+
for (const [k, v] of blocks)
|
|
23756
|
+
out.push(` ${k}:`, body(v, line, opts));
|
|
23757
|
+
} else if (typeof tc.input === "string") {
|
|
23758
|
+
out.push(`[${tc.name}]`, body(tc.input, line, opts));
|
|
23759
|
+
} else {
|
|
23760
|
+
const json = JSON.stringify(tc.input ?? {});
|
|
23761
|
+
if (opts.full || json.length <= BODY_LIMIT)
|
|
23762
|
+
out.push(`[${tc.name}] ${oneLine(json)}`);
|
|
23763
|
+
else
|
|
23764
|
+
out.push(`[${tc.name}]`, body(json, line, opts));
|
|
23765
|
+
}
|
|
23766
|
+
if (tc.result !== void 0) {
|
|
23767
|
+
out.push(tc.isError ? " \u2192 error:" : " \u2192 result:", body(resultText(tc.result), line, opts));
|
|
23768
|
+
}
|
|
23769
|
+
return out.join("\n");
|
|
23770
|
+
}
|
|
23771
|
+
function renderShowMessages(messages, opts = {}) {
|
|
23772
|
+
const out = [];
|
|
23773
|
+
for (const msg of messages) {
|
|
23774
|
+
const text = msg.content ?? "";
|
|
23775
|
+
const thinking = msg.thinking ?? "";
|
|
23776
|
+
const calls = msg.toolCalls ?? [];
|
|
23777
|
+
if (!text.trim() && !thinking.trim() && !calls.length && msg.thinking !== void 0)
|
|
23778
|
+
continue;
|
|
23779
|
+
out.push(`**${msg.role}** (line ${msg.line})`);
|
|
23780
|
+
if (thinking.trim())
|
|
23781
|
+
out.push(`_(thinking)_
|
|
23782
|
+
${thinking}`);
|
|
23783
|
+
if (text.trim())
|
|
23784
|
+
out.push(text);
|
|
23785
|
+
for (const tc of calls)
|
|
23786
|
+
out.push(renderToolCall(tc, msg.line, opts));
|
|
23787
|
+
if (!text.trim() && !thinking.trim() && !calls.length)
|
|
23788
|
+
out.push("_(empty)_");
|
|
23789
|
+
out.push("");
|
|
23790
|
+
}
|
|
23791
|
+
return out;
|
|
23792
|
+
}
|
|
23793
|
+
|
|
23643
23794
|
// ../engine/dist/core/decision-areas.js
|
|
23644
23795
|
var DECISION_AREAS = [
|
|
23645
23796
|
"auth",
|
|
@@ -23758,12 +23909,12 @@ import { config } from "dotenv";
|
|
|
23758
23909
|
var TRIAL_WARN_DAYS = 3;
|
|
23759
23910
|
async function buildHttpError(path2, res) {
|
|
23760
23911
|
const status = res.status;
|
|
23761
|
-
let
|
|
23912
|
+
let body2 = null;
|
|
23762
23913
|
try {
|
|
23763
|
-
|
|
23914
|
+
body2 = await res.clone().json();
|
|
23764
23915
|
} catch {
|
|
23765
23916
|
}
|
|
23766
|
-
const str = (k) => typeof
|
|
23917
|
+
const str = (k) => typeof body2?.[k] === "string" ? body2[k] : "";
|
|
23767
23918
|
if (status === 402) {
|
|
23768
23919
|
const parts = [str("error") || "payment required"];
|
|
23769
23920
|
if (str("detail"))
|
|
@@ -24190,14 +24341,14 @@ function codeindexAvailable() {
|
|
|
24190
24341
|
}
|
|
24191
24342
|
return _codeindexAvailable;
|
|
24192
24343
|
}
|
|
24193
|
-
function withCodeindexHint(
|
|
24344
|
+
function withCodeindexHint(body2, kind) {
|
|
24194
24345
|
if (!codeindexAvailable())
|
|
24195
|
-
return
|
|
24346
|
+
return body2;
|
|
24196
24347
|
const hints = {
|
|
24197
24348
|
files: "\n\n_Tip: codeindex is also installed \u2014 call `find_symbol` or `get_outline` on these files for current symbol-level detail._",
|
|
24198
24349
|
session: "\n\n_Tip: codeindex is also installed \u2014 call `get_outline` on each file for its current symbol structure._"
|
|
24199
24350
|
};
|
|
24200
|
-
return
|
|
24351
|
+
return body2 + hints[kind];
|
|
24201
24352
|
}
|
|
24202
24353
|
function remoteCredentials() {
|
|
24203
24354
|
const ambient = currentCredentials();
|
|
@@ -24227,14 +24378,14 @@ function remoteCredentials() {
|
|
|
24227
24378
|
return null;
|
|
24228
24379
|
}
|
|
24229
24380
|
}
|
|
24230
|
-
async function remotePost(path2,
|
|
24381
|
+
async function remotePost(path2, body2) {
|
|
24231
24382
|
const cred = remoteCredentials();
|
|
24232
24383
|
if (!cred)
|
|
24233
24384
|
throw new Error('scope "server" needs a login \u2014 run `chat-recall login <server-url>` first.');
|
|
24234
24385
|
const res = await fetch(cred.base + path2, {
|
|
24235
24386
|
method: "POST",
|
|
24236
24387
|
headers: { "content-type": "application/json", authorization: `Bearer ${cred.token}` },
|
|
24237
|
-
body: JSON.stringify(
|
|
24388
|
+
body: JSON.stringify(body2)
|
|
24238
24389
|
});
|
|
24239
24390
|
if (!res.ok)
|
|
24240
24391
|
throw await buildHttpError(path2, res);
|
|
@@ -24251,27 +24402,27 @@ async function remoteGet(path2) {
|
|
|
24251
24402
|
throw await buildHttpError(path2, res);
|
|
24252
24403
|
return res.json();
|
|
24253
24404
|
}
|
|
24254
|
-
async function remotePatch(path2,
|
|
24405
|
+
async function remotePatch(path2, body2) {
|
|
24255
24406
|
const cred = remoteCredentials();
|
|
24256
24407
|
if (!cred)
|
|
24257
24408
|
throw new Error('scope "server" needs a login \u2014 run `chat-recall login <server-url>` first.');
|
|
24258
24409
|
const res = await fetch(cred.base + path2, {
|
|
24259
24410
|
method: "PATCH",
|
|
24260
24411
|
headers: { "content-type": "application/json", authorization: `Bearer ${cred.token}` },
|
|
24261
|
-
body: JSON.stringify(
|
|
24412
|
+
body: JSON.stringify(body2)
|
|
24262
24413
|
});
|
|
24263
24414
|
if (!res.ok)
|
|
24264
24415
|
throw await buildHttpError(path2, res);
|
|
24265
24416
|
return res.json();
|
|
24266
24417
|
}
|
|
24267
|
-
async function remotePut(path2,
|
|
24418
|
+
async function remotePut(path2, body2) {
|
|
24268
24419
|
const cred = remoteCredentials();
|
|
24269
24420
|
if (!cred)
|
|
24270
24421
|
throw new Error('scope "server" needs a login \u2014 run `chat-recall login <server-url>` first.');
|
|
24271
24422
|
const res = await fetch(cred.base + path2, {
|
|
24272
24423
|
method: "PUT",
|
|
24273
24424
|
headers: { "content-type": "application/json", authorization: `Bearer ${cred.token}` },
|
|
24274
|
-
body: JSON.stringify(
|
|
24425
|
+
body: JSON.stringify(body2)
|
|
24275
24426
|
});
|
|
24276
24427
|
if (!res.ok)
|
|
24277
24428
|
throw await buildHttpError(path2, res);
|
|
@@ -24324,12 +24475,12 @@ async function remoteGetSoft(path2, params = {}) {
|
|
|
24324
24475
|
const q = qs.toString();
|
|
24325
24476
|
const res = await fetch(cred.base + (q ? `${path2}?${q}` : path2), { headers: { authorization: `Bearer ${cred.token}` } });
|
|
24326
24477
|
if (res.status === 202 || res.status === 404 || res.status === 409) {
|
|
24327
|
-
const
|
|
24328
|
-
let message =
|
|
24329
|
-
if (Array.isArray(
|
|
24478
|
+
const body2 = await res.json().catch(() => ({}));
|
|
24479
|
+
let message = body2.message ?? body2.error;
|
|
24480
|
+
if (Array.isArray(body2.candidates) && body2.candidates.length) {
|
|
24330
24481
|
message = `${message ?? "Ambiguous session id prefix."}
|
|
24331
24482
|
Candidates:
|
|
24332
|
-
${
|
|
24483
|
+
${body2.candidates.map((c) => ` \u2022 ${c}`).join("\n")}
|
|
24333
24484
|
Re-run with a longer prefix or the full id.`;
|
|
24334
24485
|
}
|
|
24335
24486
|
return { status: res.status, data: null, message };
|
|
@@ -24352,35 +24503,9 @@ var RecallShowSchema = z.object({
|
|
|
24352
24503
|
session_id: z.string().describe("Session ID from search results"),
|
|
24353
24504
|
around_line: z.number().optional().describe("Optional line number to show context around"),
|
|
24354
24505
|
max_messages: z.number().optional().default(10).describe("Maximum messages to return"),
|
|
24355
|
-
from_end: z.number().optional().describe("Return the last N messages of the session. Mutually exclusive with around_line.")
|
|
24506
|
+
from_end: z.number().optional().describe("Return the last N messages of the session. Mutually exclusive with around_line."),
|
|
24507
|
+
expand_line: z.number().optional().describe("Return only the message at this line, with every tool input and result whole.")
|
|
24356
24508
|
});
|
|
24357
|
-
function renderShowMessages(messages) {
|
|
24358
|
-
const fmtToolCall = (tc) => {
|
|
24359
|
-
const inp = tc.input || {};
|
|
24360
|
-
const oneLine = (v) => String(v).replace(/\s*\n\s*/g, " \u23CE ");
|
|
24361
|
-
if (tc.name === "Bash" && inp.command !== void 0) {
|
|
24362
|
-
const desc = inp.description ? ` # ${oneLine(inp.description)}` : "";
|
|
24363
|
-
return `[Bash]${desc}
|
|
24364
|
-
$ ${oneLine(inp.command)}`;
|
|
24365
|
-
}
|
|
24366
|
-
if (inp.file_path !== void 0)
|
|
24367
|
-
return `[${tc.name}] ${inp.file_path}`;
|
|
24368
|
-
return `[${tc.name}] ${oneLine(JSON.stringify(inp))}`;
|
|
24369
|
-
};
|
|
24370
|
-
const out = [];
|
|
24371
|
-
for (const msg of messages) {
|
|
24372
|
-
out.push(`**${msg.role}** (line ${msg.line})`);
|
|
24373
|
-
const text = msg.content;
|
|
24374
|
-
if (text.trim())
|
|
24375
|
-
out.push(text);
|
|
24376
|
-
for (const tc of msg.toolCalls || [])
|
|
24377
|
-
out.push(fmtToolCall(tc));
|
|
24378
|
-
if (!text.trim() && !(msg.toolCalls || []).length)
|
|
24379
|
-
out.push("_(empty)_");
|
|
24380
|
-
out.push("");
|
|
24381
|
-
}
|
|
24382
|
-
return out;
|
|
24383
|
-
}
|
|
24384
24509
|
var RecallRecentSchema = z.object({
|
|
24385
24510
|
project_filter: z.string().optional().describe('Filter by project name (e.g., "acme", "poly")'),
|
|
24386
24511
|
limit: z.number().optional().default(10).describe("Number of recent sessions to show"),
|
|
@@ -24990,14 +25115,17 @@ plan id (the plan filename without .md, as returned by
|
|
|
24990
25115
|
recall_memory_search(source_types:['plan'])) and renders the complete plan text.
|
|
24991
25116
|
|
|
24992
25117
|
Set \`from_end: N\` to fetch the last N messages (no line-number guessing).
|
|
24993
|
-
|
|
25118
|
+
Message text and commands come back in full. Tool calls come with their inputs
|
|
25119
|
+
and results; a body over 2000 characters shows its first and last 100 and the
|
|
25120
|
+
line to pass as \`expand_line\` to get it whole.`,
|
|
24994
25121
|
inputSchema: {
|
|
24995
25122
|
type: "object",
|
|
24996
25123
|
properties: {
|
|
24997
25124
|
session_id: { type: "string", description: "Session ID from search results, or a plan id (filename without .md)" },
|
|
24998
25125
|
around_line: { type: "number", description: "Optional line number to show context around" },
|
|
24999
25126
|
max_messages: { type: "number", default: 10, description: "Maximum messages to return" },
|
|
25000
|
-
from_end: { type: "number", description: "Return the last N messages (alternative to around_line)." }
|
|
25127
|
+
from_end: { type: "number", description: "Return the last N messages (alternative to around_line)." },
|
|
25128
|
+
expand_line: { type: "number", description: "Return only the message at this line, with every tool input and result whole." }
|
|
25001
25129
|
},
|
|
25002
25130
|
required: ["session_id"]
|
|
25003
25131
|
}
|
|
@@ -26429,6 +26557,16 @@ ${plan.data.content}` }] };
|
|
|
26429
26557
|
return { content: [{ type: "text", text: soft.message || `Session (or plan) not found: ${params.session_id}` }] };
|
|
26430
26558
|
}
|
|
26431
26559
|
const messagesList = soft.data.messages;
|
|
26560
|
+
const expandHint = (line) => `Call recall_show with session_id "${params.session_id}" and expand_line ${line} for the whole text.`;
|
|
26561
|
+
if (params.expand_line !== void 0) {
|
|
26562
|
+
const hit = messagesList.find((m) => m.line === params.expand_line);
|
|
26563
|
+
if (!hit) {
|
|
26564
|
+
const lines = messagesList.map((m) => m.line);
|
|
26565
|
+
return { content: [{ type: "text", text: `No message at line ${params.expand_line} in ${params.session_id}. Its lines run from ${Math.min(...lines)} to ${Math.max(...lines)}.` }] };
|
|
26566
|
+
}
|
|
26567
|
+
const out = [`Session: ${params.session_id}`, `Line ${hit.line}, whole.`, "", ...renderShowMessages([hit], { full: true })];
|
|
26568
|
+
return { content: [{ type: "text", text: out.join("\n") }] };
|
|
26569
|
+
}
|
|
26432
26570
|
let displayMessages = messagesList;
|
|
26433
26571
|
if (params.from_end !== void 0) {
|
|
26434
26572
|
const n = Math.max(1, Math.min(params.from_end, messagesList.length));
|
|
@@ -26453,7 +26591,7 @@ ${plan.data.content}` }] };
|
|
|
26453
26591
|
output.push(`Showing ${displayMessages.length} around line ${params.around_line}.`);
|
|
26454
26592
|
}
|
|
26455
26593
|
output.push("");
|
|
26456
|
-
output.push(...renderShowMessages(displayMessages));
|
|
26594
|
+
output.push(...renderShowMessages(displayMessages, { expandHint }));
|
|
26457
26595
|
{
|
|
26458
26596
|
const rc = resumeCommandFor(params.session_id);
|
|
26459
26597
|
if (rc)
|
|
@@ -27198,10 +27336,10 @@ _Call again with \`detail: true\` for the full brief (fix, locations, agent prom
|
|
|
27198
27336
|
const head = `## ${i + 1}. ${t.title}
|
|
27199
27337
|
|
|
27200
27338
|
- id: \`${t.id}\` \xB7 status: ${t.status}${t.projectId ? ` \xB7 project: ${t.projectId}` : ""}${t.linkedSessionId ? ` \xB7 already claimed by session ${t.linkedSessionId.slice(0, 8)}` : ""}`;
|
|
27201
|
-
const
|
|
27202
|
-
return
|
|
27339
|
+
const body2 = (t.description || "").trim();
|
|
27340
|
+
return body2 ? `${head}
|
|
27203
27341
|
|
|
27204
|
-
${
|
|
27342
|
+
${body2}` : head;
|
|
27205
27343
|
});
|
|
27206
27344
|
const howTo = [
|
|
27207
27345
|
"---",
|
|
@@ -28418,8 +28556,8 @@ _Tell the user \u2014 this changes how future sessions treat their repo._` }] };
|
|
|
28418
28556
|
if (params.scope === "fan_out" && !params.device) {
|
|
28419
28557
|
return { content: [{ type: "text", text: "`fan_out` copies between ONE machine's own tools \u2014 name the machine with `device`." }] };
|
|
28420
28558
|
}
|
|
28421
|
-
const
|
|
28422
|
-
const r = await remotePost("/api/sync-intents",
|
|
28559
|
+
const body2 = params.scope === "fan_out" ? { kind: "sync_all", deviceId: params.device } : { kind: "pull", types: params.types, name: params.name, deviceId: params.device ?? null };
|
|
28560
|
+
const r = await remotePost("/api/sync-intents", body2);
|
|
28423
28561
|
if (!r.id)
|
|
28424
28562
|
return { content: [{ type: "text", text: `Queue refused it: ${r.error || "unknown error"}` }] };
|
|
28425
28563
|
const what = params.name ? `"${params.name}"` : params.types?.length ? params.types.join(", ") : "every artifact type";
|
|
@@ -28852,7 +28990,7 @@ function installCrashGuards() {
|
|
|
28852
28990
|
reportClientEvent2("mcp_crash", { message: err.stack || err.message });
|
|
28853
28991
|
});
|
|
28854
28992
|
}
|
|
28855
|
-
var RELAY_VERSION = true ? "0.7.
|
|
28993
|
+
var RELAY_VERSION = true ? "0.7.8" : "0.0.0";
|
|
28856
28994
|
function daemonIdleSecs() {
|
|
28857
28995
|
const raw = process.env.CHAT_RECALL_DAEMON_IDLE_SECS;
|
|
28858
28996
|
if (raw === void 0) return 900;
|