jinzd-ai-cli 0.4.281 → 0.4.283
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/{batch-237ZXBQD.js → batch-6WWR4P5U.js} +2 -2
- package/dist/{chunk-5GYOQ7EF.js → chunk-2YVZTDP4.js} +1 -1
- package/dist/{chunk-J3RWNQOF.js → chunk-4J7FIGQD.js} +1 -1
- package/dist/{chunk-UTJQXG4P.js → chunk-CUS4FJVP.js} +3 -3
- package/dist/{chunk-FHRIDE2T.js → chunk-D7BIF6JO.js} +4 -4
- package/dist/{chunk-WUIOUJA2.js → chunk-F3YN754E.js} +1 -1
- package/dist/{chunk-6NO5FZW6.js → chunk-IUVDZVZ4.js} +18 -0
- package/dist/{chunk-IXEKDAIX.js → chunk-JR2J3V6J.js} +1 -1
- package/dist/{chunk-2FCKZ3OS.js → chunk-JSQGDPDK.js} +1 -1
- package/dist/{chunk-J5TXHFDO.js → chunk-LXJF46SO.js} +191 -4
- package/dist/{chunk-H2VE4TTN.js → chunk-M3UJ2JCW.js} +1 -1
- package/dist/{chunk-VB54ILH7.js → chunk-NUJTNH76.js} +1 -1
- package/dist/{chunk-4XJVSMZ6.js → chunk-QDOFRKY2.js} +1 -1
- package/dist/{chunk-BVGSR3BA.js → chunk-RALUII7R.js} +3 -3
- package/dist/{chunk-L7C34TFI.js → chunk-V4IY5CRJ.js} +1 -1
- package/dist/{chunk-JT2A2M4F.js → chunk-Z3QNLNSU.js} +1 -1
- package/dist/{ci-LXRTYJ6F.js → ci-FTS7XNWA.js} +4 -4
- package/dist/{ci-format-II3OT2VU.js → ci-format-N5PXGS7Y.js} +2 -2
- package/dist/{constants-X53QCUTP.js → constants-3B5KIPLK.js} +1 -1
- package/dist/{doctor-cli-6JCJEQR4.js → doctor-cli-VRMIEREY.js} +4 -4
- package/dist/electron-server.js +355 -77
- package/dist/{hub-RXAFF7QI.js → hub-TZET5544.js} +2 -2
- package/dist/index.js +153 -152
- package/dist/{persist-TBQILDSE.js → persist-S77WRVLD.js} +2 -2
- package/dist/{persistent-memory-7CKJLS25.js → persistent-memory-3OYRXF2L.js} +2 -2
- package/dist/{persistent-memory-WAKFKH2I.js → persistent-memory-IPCUJVWF.js} +2 -2
- package/dist/{pr-DZTRHAMG.js → pr-XUXWP52Z.js} +4 -4
- package/dist/{run-tests-4ESYEROQ.js → run-tests-3T5WRG62.js} +2 -2
- package/dist/{run-tests-XGCNUT5S.js → run-tests-PUOXATHQ.js} +2 -2
- package/dist/{server-6RYXFG3H.js → server-4XSDRPZM.js} +176 -82
- package/dist/{server-EFA7UP4I.js → server-WGEGVVY5.js} +5 -5
- package/dist/{task-orchestrator-H3AEIHR3.js → task-orchestrator-LEW2VSZD.js} +5 -5
- package/dist/{usage-W7TZWHY7.js → usage-7VEAXMMP.js} +2 -2
- package/package.json +1 -1
package/dist/electron-server.js
CHANGED
|
@@ -40,14 +40,14 @@ import {
|
|
|
40
40
|
touchMemoryReferences,
|
|
41
41
|
updateMemoryApproval,
|
|
42
42
|
updateMemoryEntry
|
|
43
|
-
} from "./chunk-
|
|
43
|
+
} from "./chunk-JR2J3V6J.js";
|
|
44
44
|
import {
|
|
45
45
|
redactJson,
|
|
46
46
|
scanString
|
|
47
47
|
} from "./chunk-FSC6KEWU.js";
|
|
48
48
|
import {
|
|
49
49
|
runTestsTool
|
|
50
|
-
} from "./chunk-
|
|
50
|
+
} from "./chunk-F3YN754E.js";
|
|
51
51
|
import {
|
|
52
52
|
AGENTIC_BEHAVIOR_GUIDELINE,
|
|
53
53
|
APP_NAME,
|
|
@@ -80,7 +80,7 @@ import {
|
|
|
80
80
|
SUBAGENT_MAX_ROUNDS_LIMIT,
|
|
81
81
|
VERSION,
|
|
82
82
|
buildUserIdentityPrompt
|
|
83
|
-
} from "./chunk-
|
|
83
|
+
} from "./chunk-2YVZTDP4.js";
|
|
84
84
|
import {
|
|
85
85
|
hasSemanticIndex,
|
|
86
86
|
semanticSearch
|
|
@@ -4726,6 +4726,24 @@ var Session = class _Session {
|
|
|
4726
4726
|
this.updated = /* @__PURE__ */ new Date();
|
|
4727
4727
|
this.dirty = true;
|
|
4728
4728
|
}
|
|
4729
|
+
/**
|
|
4730
|
+
* 把对话截断到前 `count` 条(`/rewind` 用)。
|
|
4731
|
+
*
|
|
4732
|
+
* 存在的理由是 `dirty`:`messages` 是个普通公开字段,两端此前都直接
|
|
4733
|
+
* `session.messages = session.messages.slice(0, n)`,**不会置脏**——于是 REPL 压根不存,
|
|
4734
|
+
* Web 存了也被 `save()` 的 `if (!force && !dirty) return` 跳过。结果 `/rewind` 只改内存,
|
|
4735
|
+
* 而它同时真的回退了磁盘上的文件:重启后文件是回退后的、对话是回退前的,
|
|
4736
|
+
* 恰恰制造出 rewind 本身要消除的那种不一致(P2-02 第 6 批)。
|
|
4737
|
+
*
|
|
4738
|
+
* 落在截断点之后的检查点一并丢弃(它们指向已经不存在的消息)。
|
|
4739
|
+
*/
|
|
4740
|
+
truncateTo(count) {
|
|
4741
|
+
const target = Math.max(0, Math.min(count, this.messages.length));
|
|
4742
|
+
this.messages = this.messages.slice(0, target);
|
|
4743
|
+
this.checkpoints = this.checkpoints.filter((c) => c.messageIndex <= target);
|
|
4744
|
+
this.updated = /* @__PURE__ */ new Date();
|
|
4745
|
+
this.dirty = true;
|
|
4746
|
+
}
|
|
4729
4747
|
addMessage(message) {
|
|
4730
4748
|
this.messages.push(message);
|
|
4731
4749
|
this.updated = /* @__PURE__ */ new Date();
|
|
@@ -6603,8 +6621,8 @@ ${listing}` : "");
|
|
|
6603
6621
|
}
|
|
6604
6622
|
const { size } = st;
|
|
6605
6623
|
if (size > MAX_FILE_BYTES) {
|
|
6606
|
-
const
|
|
6607
|
-
return `[File too large: ${filePath} (${
|
|
6624
|
+
const mb2 = (size / 1024 / 1024).toFixed(1);
|
|
6625
|
+
return `[File too large: ${filePath} (${mb2} MB)]
|
|
6608
6626
|
Exceeds single read limit of ${MAX_FILE_BYTES / 1024 / 1024} MB.
|
|
6609
6627
|
Use the bash tool to read in segments, e.g.:
|
|
6610
6628
|
head -n 100 "${normalizedPath}" # first 100 lines
|
|
@@ -16732,6 +16750,67 @@ function clearedSessionMessage(priorSessionId, priorMessageCount = 0) {
|
|
|
16732
16750
|
return priorSessionId && priorMessageCount > 0 ? `Started a new session. Previous conversation saved as ${priorSessionId.slice(0, 8)} (${priorMessageCount} messages) \u2014 reload it with /session load ${priorSessionId.slice(0, 8)}.` : "Started a new session.";
|
|
16733
16751
|
}
|
|
16734
16752
|
|
|
16753
|
+
// src/core/session-history.ts
|
|
16754
|
+
function parseRewindArgs(args, messageCount) {
|
|
16755
|
+
const sub = args[0];
|
|
16756
|
+
if (!sub || sub.toLowerCase() === "list") return { kind: "list" };
|
|
16757
|
+
const n = Number.parseInt(sub, 10);
|
|
16758
|
+
if (Number.isNaN(n) || n < 1 || n > messageCount) {
|
|
16759
|
+
return { kind: "invalid", message: `Invalid message number: ${sub}. Range: 1-${messageCount}` };
|
|
16760
|
+
}
|
|
16761
|
+
return { kind: "rewind", target: n };
|
|
16762
|
+
}
|
|
16763
|
+
function buildRewindList(messages, checkpointIndices, previewWidth = 70) {
|
|
16764
|
+
return messages.map((m, i) => ({
|
|
16765
|
+
index: i + 1,
|
|
16766
|
+
role: m.role,
|
|
16767
|
+
preview: getContentText(m.content).replace(/\s+/g, " ").trim().slice(0, previewWidth),
|
|
16768
|
+
hasCheckpoint: checkpointIndices.includes(i)
|
|
16769
|
+
}));
|
|
16770
|
+
}
|
|
16771
|
+
function formatRewindReport(report) {
|
|
16772
|
+
const lines = [
|
|
16773
|
+
`\u2713 Rewound to message ${report.target}`,
|
|
16774
|
+
` Messages removed: ${report.messagesRemoved}`
|
|
16775
|
+
];
|
|
16776
|
+
if (report.filesRestored > 0 || report.filesDeleted > 0) {
|
|
16777
|
+
lines.push(` Files restored: ${report.filesRestored}, files deleted: ${report.filesDeleted}`);
|
|
16778
|
+
for (const f of report.files) lines.push(` ${f}`);
|
|
16779
|
+
} else {
|
|
16780
|
+
lines.push(" No file changes to revert.");
|
|
16781
|
+
}
|
|
16782
|
+
return lines;
|
|
16783
|
+
}
|
|
16784
|
+
function resolveForkPoint(session, checkpointName) {
|
|
16785
|
+
const name = checkpointName?.trim();
|
|
16786
|
+
if (!name) {
|
|
16787
|
+
return { ok: true, messageCount: session.messages.length, fromLabel: "current position" };
|
|
16788
|
+
}
|
|
16789
|
+
const cp = session.checkpoints.find((c) => c.name === name);
|
|
16790
|
+
if (!cp) {
|
|
16791
|
+
const available = session.checkpoints.map((c) => c.name);
|
|
16792
|
+
return {
|
|
16793
|
+
ok: false,
|
|
16794
|
+
message: available.length > 0 ? `Checkpoint "${name}" not found. Available: ${available.join(", ")}` : `Checkpoint "${name}" not found. No checkpoints saved. Use /checkpoint save <name> first.`
|
|
16795
|
+
};
|
|
16796
|
+
}
|
|
16797
|
+
return {
|
|
16798
|
+
ok: true,
|
|
16799
|
+
messageCount: cp.messageIndex,
|
|
16800
|
+
fromLabel: `checkpoint "${cp.name}" (${cp.messageIndex} messages)`
|
|
16801
|
+
};
|
|
16802
|
+
}
|
|
16803
|
+
function formatForkReport(report) {
|
|
16804
|
+
return [
|
|
16805
|
+
"Session Forked",
|
|
16806
|
+
` Original: ${report.originalId.slice(0, 8)} "${report.originalTitle}"`,
|
|
16807
|
+
` Forked: ${report.forkedId.slice(0, 8)} "${report.forkedTitle}"`,
|
|
16808
|
+
` From: ${report.fromLabel}`,
|
|
16809
|
+
` Messages: ${report.messageCount} copied, ${report.checkpointCount} checkpoint(s) preserved`,
|
|
16810
|
+
` Use /session load ${report.originalId.slice(0, 8)} to switch back to the original.`
|
|
16811
|
+
];
|
|
16812
|
+
}
|
|
16813
|
+
|
|
16735
16814
|
// src/web/commands/session-commands.ts
|
|
16736
16815
|
async function handleClear(_args, ctx) {
|
|
16737
16816
|
const prior = ctx.sessions.current;
|
|
@@ -16759,7 +16838,7 @@ Cache: write=${cacheCreate} read=${cacheRead}` : "";
|
|
|
16759
16838
|
Cost: ${formatCost(cost)}` : "";
|
|
16760
16839
|
let memoryLine = "";
|
|
16761
16840
|
try {
|
|
16762
|
-
const { countPendingMemories: countPendingMemories2 } = await import("./persistent-memory-
|
|
16841
|
+
const { countPendingMemories: countPendingMemories2 } = await import("./persistent-memory-IPCUJVWF.js");
|
|
16763
16842
|
const pending = countPendingMemories2(ctx.config.getConfigDir());
|
|
16764
16843
|
if (pending > 0) memoryLine = `
|
|
16765
16844
|
Memory: \u26A0 ${pending} pending approval \u2014 see the Memory panel or /memory`;
|
|
@@ -17338,27 +17417,27 @@ async function handleFork(args, ctx) {
|
|
|
17338
17417
|
ctx.send({ type: "info", message: "No active session to fork." });
|
|
17339
17418
|
return;
|
|
17340
17419
|
}
|
|
17341
|
-
const
|
|
17342
|
-
|
|
17343
|
-
|
|
17344
|
-
|
|
17345
|
-
const cp = session.checkpoints.find((c) => c.name === sub);
|
|
17346
|
-
if (!cp) {
|
|
17347
|
-
const available = session.checkpoints.map((c) => c.name);
|
|
17348
|
-
ctx.send({ type: "error", message: available.length > 0 ? `Checkpoint "${sub}" not found. Available: ${available.join(", ")}` : `Checkpoint "${sub}" not found. No checkpoints saved.` });
|
|
17349
|
-
return;
|
|
17350
|
-
}
|
|
17351
|
-
messageCount = cp.messageIndex;
|
|
17352
|
-
fromLabel = `checkpoint "${cp.name}" (${cp.messageIndex} messages)`;
|
|
17420
|
+
const point = resolveForkPoint(session, args.join(" ").trim());
|
|
17421
|
+
if (!point.ok) {
|
|
17422
|
+
ctx.send({ type: "error", message: point.message });
|
|
17423
|
+
return;
|
|
17353
17424
|
}
|
|
17354
17425
|
try {
|
|
17355
|
-
const originalId = session.id
|
|
17356
|
-
const
|
|
17357
|
-
|
|
17358
|
-
|
|
17359
|
-
|
|
17360
|
-
|
|
17361
|
-
|
|
17426
|
+
const originalId = session.id;
|
|
17427
|
+
const originalTitle = session.title ?? "(untitled)";
|
|
17428
|
+
const forked = await ctx.sessions.forkSession(point.messageCount);
|
|
17429
|
+
ctx.send({
|
|
17430
|
+
type: "info",
|
|
17431
|
+
message: formatForkReport({
|
|
17432
|
+
originalId,
|
|
17433
|
+
originalTitle,
|
|
17434
|
+
forkedId: forked.id,
|
|
17435
|
+
forkedTitle: forked.title ?? "(untitled)",
|
|
17436
|
+
fromLabel: point.fromLabel,
|
|
17437
|
+
messageCount: forked.messages.length,
|
|
17438
|
+
checkpointCount: forked.checkpoints.length
|
|
17439
|
+
}).join("\n")
|
|
17440
|
+
});
|
|
17362
17441
|
ctx.sendSessionMessages();
|
|
17363
17442
|
ctx.sendStatus();
|
|
17364
17443
|
ctx.sendSessionList();
|
|
@@ -17372,41 +17451,42 @@ async function handleRewind(args, ctx) {
|
|
|
17372
17451
|
ctx.send({ type: "info", message: "No messages to rewind." });
|
|
17373
17452
|
return;
|
|
17374
17453
|
}
|
|
17375
|
-
const
|
|
17376
|
-
if (
|
|
17377
|
-
|
|
17378
|
-
`];
|
|
17379
|
-
const cpIndices = (await import("./file-checkpoint-CGH6OJVI.js")).fileCheckpoints.getMessageIndices();
|
|
17380
|
-
for (let i = 0; i < session.messages.length; i++) {
|
|
17381
|
-
const m = session.messages[i];
|
|
17382
|
-
const text = getContentText(m.content).replace(/\n/g, " ").slice(0, 60);
|
|
17383
|
-
const pin = cpIndices.includes(i) ? " \u{1F4CC}" : "";
|
|
17384
|
-
lines.push(` [${i + 1}] ${m.role.padEnd(10)} ${text}${pin}`);
|
|
17385
|
-
}
|
|
17386
|
-
lines.push("", "Usage: /rewind <n> \u2014 rewind to message N", "\u{1F4CC} = file checkpoint");
|
|
17387
|
-
ctx.send({ type: "info", message: lines.join("\n") });
|
|
17454
|
+
const intent = parseRewindArgs(args, session.messages.length);
|
|
17455
|
+
if (intent.kind === "invalid") {
|
|
17456
|
+
ctx.send({ type: "error", message: intent.message });
|
|
17388
17457
|
return;
|
|
17389
17458
|
}
|
|
17390
|
-
const
|
|
17391
|
-
if (
|
|
17392
|
-
|
|
17459
|
+
const { fileCheckpoints: fileCheckpoints2 } = await import("./file-checkpoint-CGH6OJVI.js");
|
|
17460
|
+
if (intent.kind === "list") {
|
|
17461
|
+
const rows = buildRewindList(session.messages, fileCheckpoints2.getMessageIndices());
|
|
17462
|
+
ctx.send({
|
|
17463
|
+
type: "info",
|
|
17464
|
+
message: [
|
|
17465
|
+
`Conversation messages (${rows.length} total):`,
|
|
17466
|
+
"",
|
|
17467
|
+
...rows.map((r) => ` [${r.index}] ${r.role.padEnd(10)} ${r.preview}${r.hasCheckpoint ? " \u{1F4CC}" : ""}`),
|
|
17468
|
+
"",
|
|
17469
|
+
"Usage: /rewind <n> \u2014 rewind to message N (1-based)",
|
|
17470
|
+
"\u{1F4CC} = file checkpoint exists at this point"
|
|
17471
|
+
].join("\n")
|
|
17472
|
+
});
|
|
17393
17473
|
return;
|
|
17394
17474
|
}
|
|
17395
|
-
const
|
|
17396
|
-
const
|
|
17397
|
-
|
|
17398
|
-
|
|
17399
|
-
|
|
17400
|
-
|
|
17401
|
-
|
|
17402
|
-
|
|
17403
|
-
|
|
17404
|
-
|
|
17405
|
-
|
|
17406
|
-
|
|
17407
|
-
|
|
17408
|
-
|
|
17409
|
-
|
|
17475
|
+
const messagesRemoved = session.messages.length - intent.target;
|
|
17476
|
+
const result = fileCheckpoints2.restoreToMessageIndex(intent.target);
|
|
17477
|
+
session.truncateTo(intent.target);
|
|
17478
|
+
await ctx.sessions.save();
|
|
17479
|
+
ctx.resetWebSessionUsage();
|
|
17480
|
+
ctx.send({
|
|
17481
|
+
type: "info",
|
|
17482
|
+
message: formatRewindReport({
|
|
17483
|
+
target: intent.target,
|
|
17484
|
+
messagesRemoved,
|
|
17485
|
+
filesRestored: result.restored,
|
|
17486
|
+
filesDeleted: result.deleted,
|
|
17487
|
+
files: result.files
|
|
17488
|
+
}).join("\n")
|
|
17489
|
+
});
|
|
17410
17490
|
ctx.sendSessionMessages();
|
|
17411
17491
|
ctx.sendStatus();
|
|
17412
17492
|
}
|
|
@@ -17666,7 +17746,7 @@ async function handleTest(args, ctx) {
|
|
|
17666
17746
|
const isCommand = argStr.includes(" ") || /^(mvn|gradle|npm|pytest|cargo|go)\b/.test(argStr);
|
|
17667
17747
|
testArgs = isCommand ? { command: argStr } : { filter: argStr };
|
|
17668
17748
|
}
|
|
17669
|
-
const runTests = ctx.runTests ?? (await import("./run-tests-
|
|
17749
|
+
const runTests = ctx.runTests ?? (await import("./run-tests-3T5WRG62.js")).executeTests;
|
|
17670
17750
|
const report = await runTests(testArgs);
|
|
17671
17751
|
ctx.send({ type: "info", message: report });
|
|
17672
17752
|
} catch (err) {
|
|
@@ -17786,6 +17866,121 @@ function parseOutFlag(args) {
|
|
|
17786
17866
|
return v && !v.startsWith("--") ? v : void 0;
|
|
17787
17867
|
}
|
|
17788
17868
|
|
|
17869
|
+
// src/core/memory-command.ts
|
|
17870
|
+
var MEMORY_USAGE = "Governed persistent memory: show [--pending|--orphaned|--stale <days>|--scope <s>|--search <text>] | add | edit <id> <text> | approve/reject/delete/expire <id> | rebind <id> <path> | clear | backup [--out <file>] | restore <file> | export [--format json|md] [--out <file>] | path\nChat memory index (cross-session recall): rebuild | refresh | status | recall <query> | index-clear";
|
|
17871
|
+
function parseMemoryCommand(args) {
|
|
17872
|
+
const sub = (args[0] ?? "show").toLowerCase();
|
|
17873
|
+
const rest = args.slice(1);
|
|
17874
|
+
switch (sub) {
|
|
17875
|
+
case "rebuild":
|
|
17876
|
+
return { kind: "rebuild", full: true };
|
|
17877
|
+
case "refresh":
|
|
17878
|
+
return { kind: "rebuild", full: false };
|
|
17879
|
+
case "status":
|
|
17880
|
+
return { kind: "status" };
|
|
17881
|
+
case "recall": {
|
|
17882
|
+
const query = rest.join(" ").trim();
|
|
17883
|
+
return query ? { kind: "recall", query } : { kind: "usage", message: "Usage: /memory recall <query>" };
|
|
17884
|
+
}
|
|
17885
|
+
case "index-clear":
|
|
17886
|
+
return { kind: "index-clear" };
|
|
17887
|
+
case "show":
|
|
17888
|
+
case "view":
|
|
17889
|
+
case "list":
|
|
17890
|
+
return { kind: "show", rest };
|
|
17891
|
+
case "add":
|
|
17892
|
+
return { kind: "add", rest };
|
|
17893
|
+
case "edit":
|
|
17894
|
+
return { kind: "edit", rest };
|
|
17895
|
+
case "approve":
|
|
17896
|
+
case "reject":
|
|
17897
|
+
case "delete":
|
|
17898
|
+
case "expire":
|
|
17899
|
+
return { kind: "manage", action: sub, rest };
|
|
17900
|
+
case "rebind":
|
|
17901
|
+
return { kind: "rebind", rest };
|
|
17902
|
+
case "export":
|
|
17903
|
+
return { kind: "export", rest };
|
|
17904
|
+
case "clear":
|
|
17905
|
+
return { kind: "clear", rest };
|
|
17906
|
+
case "backup":
|
|
17907
|
+
return { kind: "backup", rest };
|
|
17908
|
+
case "restore":
|
|
17909
|
+
return { kind: "restore", rest };
|
|
17910
|
+
case "path":
|
|
17911
|
+
return { kind: "path" };
|
|
17912
|
+
default:
|
|
17913
|
+
return { kind: "usage", message: `Unknown /memory subcommand "${args[0]}".
|
|
17914
|
+
${MEMORY_USAGE}` };
|
|
17915
|
+
}
|
|
17916
|
+
}
|
|
17917
|
+
var mb = (n) => `${(n / 1024 / 1024).toFixed(2)} MB`;
|
|
17918
|
+
function buildMemoryStatusView(input) {
|
|
17919
|
+
const governed = [
|
|
17920
|
+
{ label: "active", value: String(input.active) }
|
|
17921
|
+
];
|
|
17922
|
+
governed.push(
|
|
17923
|
+
input.pending > 0 ? {
|
|
17924
|
+
label: "pending",
|
|
17925
|
+
// 照着敲就能用(v0.4.250 定的口径):跳到待批准列表 + 点明 id 是 8 位
|
|
17926
|
+
value: `${input.pending} \u2014 run /memory show --pending, then /memory approve <8-char-id>`,
|
|
17927
|
+
warn: true
|
|
17928
|
+
} : { label: "pending", value: "0" }
|
|
17929
|
+
);
|
|
17930
|
+
if (input.injected) {
|
|
17931
|
+
const note = input.injected.truncated ? ` \u26A0 truncated \u2014 only newest ${input.injected.entryCount} of ${input.injected.totalCount} injected` : "";
|
|
17932
|
+
governed.push({
|
|
17933
|
+
label: "injected",
|
|
17934
|
+
value: `${input.injected.entryCount} of ${input.injected.totalCount} (${input.injected.chars} chars)${note}`,
|
|
17935
|
+
warn: input.injected.truncated
|
|
17936
|
+
});
|
|
17937
|
+
} else {
|
|
17938
|
+
governed.push({ label: "injected", value: "0" });
|
|
17939
|
+
}
|
|
17940
|
+
governed.push({ label: "store", value: input.storePath });
|
|
17941
|
+
if (!input.chatIndex.exists) {
|
|
17942
|
+
return { governed, chatIndex: [], chatIndexHint: "not built yet \u2014 run /memory rebuild" };
|
|
17943
|
+
}
|
|
17944
|
+
return {
|
|
17945
|
+
governed,
|
|
17946
|
+
chatIndex: [
|
|
17947
|
+
{ label: "chunks", value: String(input.chatIndex.chunks) },
|
|
17948
|
+
{ label: "sessions", value: String(input.chatIndex.sessions) },
|
|
17949
|
+
{ label: "built", value: input.chatIndex.built?.replace("T", " ").slice(0, 19) ?? "-" },
|
|
17950
|
+
{ label: "model", value: input.chatIndex.model ?? "-" },
|
|
17951
|
+
{ label: "vec file", value: mb(input.chatIndex.vecFileSizeBytes) },
|
|
17952
|
+
{ label: "chunks file", value: mb(input.chatIndex.chunksFileSizeBytes) }
|
|
17953
|
+
],
|
|
17954
|
+
chatIndexHint: null
|
|
17955
|
+
};
|
|
17956
|
+
}
|
|
17957
|
+
function rebuildStartMessage(full) {
|
|
17958
|
+
return full ? "\u{1F9E0} Rebuilding chat memory index from scratch (this may take a while on first run \u2014 embedding model ~117 MB)\u2026" : "\u{1F9E0} Refreshing chat memory index (incremental)\u2026";
|
|
17959
|
+
}
|
|
17960
|
+
function rebuildDoneMessage(stats) {
|
|
17961
|
+
return `\u2713 Chat index: ${stats.chunksTotal} chunks (${stats.chunksAdded} new, ${stats.chunksRemoved} removed) across ${stats.sessionsIndexed + stats.sessionsSkipped} sessions (${stats.sessionsIndexed} re-indexed, ${stats.sessionsSkipped} cached) in ${stats.durationMs}ms`;
|
|
17962
|
+
}
|
|
17963
|
+
var clip = (text, max) => text.length > max ? `${text.slice(0, max)}\u2026` : text;
|
|
17964
|
+
function buildRecallView(query, persistent, chat) {
|
|
17965
|
+
const view = {
|
|
17966
|
+
persistentHeading: persistent.length > 0 ? `\u{1F4DD} Persistent memory \u2014 ${persistent.length} hit(s):` : null,
|
|
17967
|
+
persistent: persistent.map((h, i) => ({
|
|
17968
|
+
label: `[${i + 1}] id ${h.id.slice(0, 8)} \xB7 ${h.scope} \xB7 coverage ${h.score.toFixed(2)}`,
|
|
17969
|
+
body: h.content
|
|
17970
|
+
})),
|
|
17971
|
+
chatHeading: chat.length > 0 ? `\u{1F9E0} Chat history recall for "${query}" \u2014 ${chat.length} hit(s):` : null,
|
|
17972
|
+
chat: chat.map((h, i) => ({
|
|
17973
|
+
label: `[${i + 1}] ${h.timestamp.slice(0, 16).replace("T", " ")} \xB7 ${h.title} \xB7 score ${h.score.toFixed(3)}`,
|
|
17974
|
+
body: clip(h.text, 400)
|
|
17975
|
+
})),
|
|
17976
|
+
note: null
|
|
17977
|
+
};
|
|
17978
|
+
if (chat.length === 0) {
|
|
17979
|
+
view.note = persistent.length === 0 ? `No memories matched "${query}" (persistent + chat index).` : "(no chat-history hits above score 0.20)";
|
|
17980
|
+
}
|
|
17981
|
+
return view;
|
|
17982
|
+
}
|
|
17983
|
+
|
|
17789
17984
|
// src/web/commands/memory-commands.ts
|
|
17790
17985
|
async function handleSystem(args, ctx) {
|
|
17791
17986
|
const text = args.join(" ").trim();
|
|
@@ -17916,8 +18111,91 @@ async function handleIndex(args, ctx) {
|
|
|
17916
18111
|
}
|
|
17917
18112
|
}
|
|
17918
18113
|
async function handleMemory(args, ctx) {
|
|
17919
|
-
const
|
|
17920
|
-
|
|
18114
|
+
const intent = parseMemoryCommand(args);
|
|
18115
|
+
const configDir = ctx.config.getConfigDir();
|
|
18116
|
+
if (intent.kind === "usage") {
|
|
18117
|
+
ctx.send({ type: "error", message: intent.message });
|
|
18118
|
+
return;
|
|
18119
|
+
}
|
|
18120
|
+
if (intent.kind === "rebuild") {
|
|
18121
|
+
ctx.send({ type: "info", message: rebuildStartMessage(intent.full) });
|
|
18122
|
+
try {
|
|
18123
|
+
const { buildChatIndex } = await import("./chat-index-TDU3S2MG.js");
|
|
18124
|
+
const stats = await buildChatIndex({ full: intent.full });
|
|
18125
|
+
ctx.send({ type: "info", message: rebuildDoneMessage(stats) });
|
|
18126
|
+
} catch (err) {
|
|
18127
|
+
ctx.send({ type: "error", message: `Rebuild failed: ${err instanceof Error ? err.message : String(err)}` });
|
|
18128
|
+
}
|
|
18129
|
+
return;
|
|
18130
|
+
}
|
|
18131
|
+
if (intent.kind === "index-clear") {
|
|
18132
|
+
const { clearChatIndex } = await import("./chat-index-TDU3S2MG.js");
|
|
18133
|
+
clearChatIndex();
|
|
18134
|
+
ctx.send({ type: "info", message: "Chat memory index cleared (persistent memory untouched)." });
|
|
18135
|
+
return;
|
|
18136
|
+
}
|
|
18137
|
+
if (intent.kind === "status") {
|
|
18138
|
+
const { getChatIndexStatus } = await import("./chat-index-TDU3S2MG.js");
|
|
18139
|
+
const { listMemoryEntries: listMemoryEntries2, isMemoryExpired: isMemoryExpired2, formatMemoryForPrompt: formatMemoryForPrompt2 } = await import("./persistent-memory-IPCUJVWF.js");
|
|
18140
|
+
const memories = listMemoryEntries2(configDir, { includeExpired: true, includeRejected: true });
|
|
18141
|
+
const injected = formatMemoryForPrompt2(configDir, process.cwd(), ctx.config.get("memory")?.maxChars);
|
|
18142
|
+
const view = buildMemoryStatusView({
|
|
18143
|
+
active: memories.filter((m) => m.approved && !isMemoryExpired2(m)).length,
|
|
18144
|
+
pending: memories.filter((m) => !m.approved && !isMemoryExpired2(m)).length,
|
|
18145
|
+
injected: injected ? { entryCount: injected.entryCount, totalCount: injected.totalCount, chars: injected.content.length, truncated: injected.truncated } : null,
|
|
18146
|
+
storePath: memoryStorePath(configDir),
|
|
18147
|
+
chatIndex: getChatIndexStatus()
|
|
18148
|
+
});
|
|
18149
|
+
ctx.send({
|
|
18150
|
+
type: "info",
|
|
18151
|
+
message: [
|
|
18152
|
+
"\u{1F9E0} Governed Memory",
|
|
18153
|
+
...view.governed.map((r) => ` ${r.label.padEnd(13)}: ${r.value}`),
|
|
18154
|
+
"",
|
|
18155
|
+
"\u{1F9E0} Chat Memory Index",
|
|
18156
|
+
...view.chatIndexHint ? [` ${view.chatIndexHint}`] : view.chatIndex.map((r) => ` ${r.label.padEnd(13)}: ${r.value}`)
|
|
18157
|
+
].join("\n")
|
|
18158
|
+
});
|
|
18159
|
+
return;
|
|
18160
|
+
}
|
|
18161
|
+
if (intent.kind === "recall") {
|
|
18162
|
+
const { searchPersistentMemories: searchPersistentMemories2, touchMemoryReferences: touchMemoryReferences2 } = await import("./persistent-memory-IPCUJVWF.js");
|
|
18163
|
+
const { searchChatMemory: searchChatMemory2 } = await import("./chat-index-TDU3S2MG.js");
|
|
18164
|
+
const persistentHits = searchPersistentMemories2(configDir, intent.query, process.cwd(), 3);
|
|
18165
|
+
if (persistentHits.length > 0) {
|
|
18166
|
+
touchMemoryReferences2(configDir, persistentHits.map((h) => h.entry.id));
|
|
18167
|
+
}
|
|
18168
|
+
let chatHits = [];
|
|
18169
|
+
try {
|
|
18170
|
+
chatHits = await searchChatMemory2(intent.query, { topK: 5, minScore: 0.2 });
|
|
18171
|
+
} catch (err) {
|
|
18172
|
+
ctx.send({ type: "error", message: `Recall failed: ${err instanceof Error ? err.message : String(err)}` });
|
|
18173
|
+
return;
|
|
18174
|
+
}
|
|
18175
|
+
const view = buildRecallView(
|
|
18176
|
+
intent.query,
|
|
18177
|
+
persistentHits.map((h) => ({ id: h.entry.id, scope: h.entry.scope, score: h.score, content: h.entry.content })),
|
|
18178
|
+
chatHits.map((h) => ({
|
|
18179
|
+
timestamp: h.chunk.timestamp,
|
|
18180
|
+
title: h.chunk.sessionTitle ?? h.chunk.sessionId.slice(0, 8),
|
|
18181
|
+
score: h.score,
|
|
18182
|
+
text: h.chunk.text
|
|
18183
|
+
}))
|
|
18184
|
+
);
|
|
18185
|
+
const lines = [];
|
|
18186
|
+
if (view.persistentHeading) {
|
|
18187
|
+
lines.push(view.persistentHeading, "");
|
|
18188
|
+
for (const hit of view.persistent) lines.push(` ${hit.label}`, ` ${hit.body}`, "");
|
|
18189
|
+
}
|
|
18190
|
+
if (view.chatHeading) {
|
|
18191
|
+
lines.push(view.chatHeading, "");
|
|
18192
|
+
for (const hit of view.chat) lines.push(` ${hit.label}`, ` ${hit.body}`, "");
|
|
18193
|
+
}
|
|
18194
|
+
if (view.note) lines.push(view.note);
|
|
18195
|
+
ctx.send({ type: "info", message: lines.join("\n") });
|
|
18196
|
+
return;
|
|
18197
|
+
}
|
|
18198
|
+
if (intent.kind === "backup") {
|
|
17921
18199
|
try {
|
|
17922
18200
|
const result = backupMemoryStore(ctx.config.getConfigDir(), parseOutFlag(args.slice(1)));
|
|
17923
18201
|
ctx.send({ type: "info", message: `\u2713 Backed up ${result.count} entr${result.count === 1 ? "y" : "ies"} to ${result.backupPath}` });
|
|
@@ -17926,20 +18204,20 @@ async function handleMemory(args, ctx) {
|
|
|
17926
18204
|
}
|
|
17927
18205
|
return;
|
|
17928
18206
|
}
|
|
17929
|
-
if (
|
|
17930
|
-
const
|
|
17931
|
-
const verdict = checkWebRestorePath(
|
|
18207
|
+
if (intent.kind === "restore") {
|
|
18208
|
+
const configDir2 = ctx.config.getConfigDir();
|
|
18209
|
+
const verdict = checkWebRestorePath(configDir2, args[1]);
|
|
17932
18210
|
if (!verdict.ok) {
|
|
17933
18211
|
ctx.send({ type: "error", message: verdict.reason });
|
|
17934
18212
|
return;
|
|
17935
18213
|
}
|
|
17936
18214
|
try {
|
|
17937
|
-
const result = restoreMemoryEntries(
|
|
18215
|
+
const result = restoreMemoryEntries(configDir2, verdict.resolved);
|
|
17938
18216
|
const lines = [
|
|
17939
18217
|
`\u2713 Restored ${result.imported} entr${result.imported === 1 ? "y" : "ies"} (${result.skipped} duplicate${result.skipped === 1 ? "" : "s"} skipped)`
|
|
17940
18218
|
];
|
|
17941
18219
|
if (result.backupPath) lines.push(`Pre-restore backup: ${result.backupPath}`);
|
|
17942
|
-
const pending = countPendingMemories(
|
|
18220
|
+
const pending = countPendingMemories(configDir2);
|
|
17943
18221
|
if (pending > 0) lines.push(`${pending} entr${pending === 1 ? "y" : "ies"} pending approval \u2014 /memory show --pending`);
|
|
17944
18222
|
ctx.send({ type: "info", message: lines.join("\n") });
|
|
17945
18223
|
ctx.sendMemoryEntries();
|
|
@@ -17948,7 +18226,7 @@ async function handleMemory(args, ctx) {
|
|
|
17948
18226
|
}
|
|
17949
18227
|
return;
|
|
17950
18228
|
}
|
|
17951
|
-
if (
|
|
18229
|
+
if (intent.kind === "rebind") {
|
|
17952
18230
|
const id = args[1];
|
|
17953
18231
|
const newPath = args.slice(2).join(" ").trim();
|
|
17954
18232
|
if (!id || !newPath) {
|
|
@@ -17964,11 +18242,11 @@ async function handleMemory(args, ctx) {
|
|
|
17964
18242
|
}
|
|
17965
18243
|
return;
|
|
17966
18244
|
}
|
|
17967
|
-
if (
|
|
18245
|
+
if (intent.kind === "path") {
|
|
17968
18246
|
ctx.send({ type: "info", message: memoryStorePath(ctx.config.getConfigDir()) });
|
|
17969
18247
|
return;
|
|
17970
18248
|
}
|
|
17971
|
-
if (
|
|
18249
|
+
if (intent.kind === "add") {
|
|
17972
18250
|
const rest = args.slice(1);
|
|
17973
18251
|
const opts = {};
|
|
17974
18252
|
const text = [];
|
|
@@ -17986,7 +18264,7 @@ async function handleMemory(args, ctx) {
|
|
|
17986
18264
|
return;
|
|
17987
18265
|
}
|
|
17988
18266
|
ctx.memoryAdd(text.join(" "), opts);
|
|
17989
|
-
} else if (
|
|
18267
|
+
} else if (intent.kind === "edit") {
|
|
17990
18268
|
const id = args[1];
|
|
17991
18269
|
const text = args.slice(2).join(" ").trim();
|
|
17992
18270
|
if (!id || !text) {
|
|
@@ -17994,15 +18272,15 @@ async function handleMemory(args, ctx) {
|
|
|
17994
18272
|
return;
|
|
17995
18273
|
}
|
|
17996
18274
|
ctx.memoryEdit(id, text);
|
|
17997
|
-
} else if (
|
|
18275
|
+
} else if (intent.kind === "manage") {
|
|
17998
18276
|
if (!args[1]) {
|
|
17999
|
-
ctx.send({ type: "info", message: `Usage: /memory ${
|
|
18277
|
+
ctx.send({ type: "info", message: `Usage: /memory ${intent.action} <id>` });
|
|
18000
18278
|
return;
|
|
18001
18279
|
}
|
|
18002
|
-
ctx.handleMemoryManage(
|
|
18003
|
-
} else if (
|
|
18280
|
+
ctx.handleMemoryManage(intent.action, args[1], intent.action === "expire" ? args[2] : void 0);
|
|
18281
|
+
} else if (intent.kind === "export") {
|
|
18004
18282
|
if (args[1] === "md") {
|
|
18005
|
-
const { exportMemoryEntries: exportMemoryEntries2 } = await import("./persistent-memory-
|
|
18283
|
+
const { exportMemoryEntries: exportMemoryEntries2 } = await import("./persistent-memory-IPCUJVWF.js");
|
|
18006
18284
|
ctx.send({
|
|
18007
18285
|
type: "export_data",
|
|
18008
18286
|
format: "md",
|
|
@@ -18012,7 +18290,7 @@ async function handleMemory(args, ctx) {
|
|
|
18012
18290
|
} else {
|
|
18013
18291
|
ctx.handleMemoryManage("export");
|
|
18014
18292
|
}
|
|
18015
|
-
} else if (
|
|
18293
|
+
} else if (intent.kind === "clear") {
|
|
18016
18294
|
if (args[1] === "confirm") {
|
|
18017
18295
|
ctx.memoryClear();
|
|
18018
18296
|
} else {
|
|
@@ -18022,8 +18300,7 @@ async function handleMemory(args, ctx) {
|
|
|
18022
18300
|
});
|
|
18023
18301
|
}
|
|
18024
18302
|
} else {
|
|
18025
|
-
|
|
18026
|
-
ctx.memoryShow(isShow ? args.slice(1) : args);
|
|
18303
|
+
ctx.memoryShow(intent.rest);
|
|
18027
18304
|
}
|
|
18028
18305
|
}
|
|
18029
18306
|
async function handleAddDir(args, ctx) {
|
|
@@ -18455,7 +18732,8 @@ async function handleHelp(_args, ctx) {
|
|
|
18455
18732
|
" /tools \u2014 Show tools, MCP servers & skills",
|
|
18456
18733
|
" /export [md|json] \u2014 Export conversation",
|
|
18457
18734
|
" /skill [name|off|list|reload] \u2014 Manage agent skills",
|
|
18458
|
-
" /memory
|
|
18735
|
+
" /memory show|add|edit|approve|reject|delete|expire|rebind|clear|backup|restore|export|path \u2014 Governed memory",
|
|
18736
|
+
" /memory rebuild|refresh|status|recall <query>|index-clear \u2014 Chat memory index (cross-session recall)",
|
|
18459
18737
|
" /agent list|switch|stop|summary \u2014 Manage named sub-agents",
|
|
18460
18738
|
" /yolo [on|off] \u2014 Toggle auto-approve (skip confirmations)",
|
|
18461
18739
|
" /search <keyword> \u2014 Search across all session histories",
|
|
@@ -143,7 +143,7 @@ ${content}`);
|
|
|
143
143
|
const state = await runDiscussion(config, providers, options.topic);
|
|
144
144
|
if (options.save !== false && state.messages.length > 0) {
|
|
145
145
|
try {
|
|
146
|
-
const { persistDiscussion } = await import("./persist-
|
|
146
|
+
const { persistDiscussion } = await import("./persist-S77WRVLD.js");
|
|
147
147
|
const { path } = await persistDiscussion(state, configManager, defaultProvider, defaultModel);
|
|
148
148
|
console.log(chalk.dim(`
|
|
149
149
|
\u{1F4BE} Saved to history \u2014 open it in the Web UI and hit \u{1F3AC} to replay.
|
|
@@ -158,7 +158,7 @@ ${content}`);
|
|
|
158
158
|
}
|
|
159
159
|
}
|
|
160
160
|
async function runTaskMode(config, providers, configManager, topic) {
|
|
161
|
-
const { TaskOrchestrator } = await import("./task-orchestrator-
|
|
161
|
+
const { TaskOrchestrator } = await import("./task-orchestrator-LEW2VSZD.js");
|
|
162
162
|
const orchestrator = new TaskOrchestrator(config, providers, configManager);
|
|
163
163
|
let interrupted = false;
|
|
164
164
|
const onSigint = () => {
|