jinzd-ai-cli 0.4.280 → 0.4.282

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.
Files changed (34) hide show
  1. package/dist/{batch-F27NOTWC.js → batch-SBP6H7IK.js} +2 -2
  2. package/dist/{chunk-NHJ4GHL4.js → chunk-6IRG727F.js} +1 -1
  3. package/dist/{chunk-CP5LAA52.js → chunk-7XKIDSJT.js} +4 -4
  4. package/dist/{chunk-EOP775JU.js → chunk-EVD6CCO2.js} +1 -1
  5. package/dist/{chunk-B3FHA6IN.js → chunk-GSGJABL6.js} +3 -3
  6. package/dist/{chunk-VQCFEA4Z.js → chunk-HJDJAI7T.js} +1 -1
  7. package/dist/{chunk-CGDYO52A.js → chunk-IUVDZVZ4.js} +19 -0
  8. package/dist/{chunk-UBPRUSAF.js → chunk-JQU4GYET.js} +1 -1
  9. package/dist/{chunk-IIO2SMEJ.js → chunk-KDXA7RXA.js} +74 -4
  10. package/dist/{chunk-UMWDNNPS.js → chunk-LNUWIY6M.js} +1 -1
  11. package/dist/{chunk-MX75TNE5.js → chunk-NUJTNH76.js} +1 -1
  12. package/dist/{chunk-PUC277TH.js → chunk-RHSCROLG.js} +1 -1
  13. package/dist/{chunk-V7EZ4SDC.js → chunk-SGUPIQYT.js} +1 -1
  14. package/dist/{chunk-PN42JO74.js → chunk-TNIMHTKG.js} +1 -1
  15. package/dist/{chunk-TAIZZY3N.js → chunk-WMHLWCZQ.js} +3 -3
  16. package/dist/{chunk-LPZNOYRJ.js → chunk-YRPL7A3S.js} +1 -1
  17. package/dist/{ci-HJ4OVHCM.js → ci-VAAMX44S.js} +4 -4
  18. package/dist/{ci-format-NKJRZ3QI.js → ci-format-RMULFSB5.js} +2 -2
  19. package/dist/{constants-6LCZOBJC.js → constants-PXLMEBJY.js} +1 -1
  20. package/dist/{doctor-cli-7JGPC5G4.js → doctor-cli-C2DFYU2M.js} +4 -4
  21. package/dist/electron-server.js +147 -59
  22. package/dist/{hub-NX7HYN7V.js → hub-XIU3YTOE.js} +2 -2
  23. package/dist/index.js +81 -86
  24. package/dist/{persist-PQM4BRI4.js → persist-S77WRVLD.js} +2 -2
  25. package/dist/{persistent-memory-MHHX7F25.js → persistent-memory-ACXKU6RV.js} +2 -2
  26. package/dist/{persistent-memory-E6WECUTW.js → persistent-memory-TA224ZFQ.js} +2 -2
  27. package/dist/{pr-GLUFBQLI.js → pr-J74HW44E.js} +4 -4
  28. package/dist/{run-tests-YPJDOEJA.js → run-tests-4KTX5YMO.js} +2 -2
  29. package/dist/{run-tests-LWAFPVS3.js → run-tests-JLZ2KIGV.js} +2 -2
  30. package/dist/{server-CXXYIPMU.js → server-226R6675.js} +77 -66
  31. package/dist/{server-P6DI27CY.js → server-4CZXYGJN.js} +5 -5
  32. package/dist/{task-orchestrator-FW4LBPTS.js → task-orchestrator-6C3R7KSK.js} +5 -5
  33. package/dist/{usage-CJUHQDT2.js → usage-FY3TEJJS.js} +2 -2
  34. package/package.json +1 -1
@@ -40,14 +40,14 @@ import {
40
40
  touchMemoryReferences,
41
41
  updateMemoryApproval,
42
42
  updateMemoryEntry
43
- } from "./chunk-PUC277TH.js";
43
+ } from "./chunk-RHSCROLG.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-NHJ4GHL4.js";
50
+ } from "./chunk-6IRG727F.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-PN42JO74.js";
83
+ } from "./chunk-TNIMHTKG.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();
@@ -5228,6 +5246,7 @@ var SessionManager = class {
5228
5246
  /** P1-PERF-03: `force` 跳过脏检查(用于进程退出、fork 等必须落盘场景) */
5229
5247
  async save(force = false) {
5230
5248
  if (!this._current) return;
5249
+ if (this._current.messages.length === 0) return;
5231
5250
  if (!force && !this._current.dirty) return;
5232
5251
  mkdirSync3(this.historyDir, { recursive: true });
5233
5252
  const filePath = join4(this.historyDir, `${this._current.id}.json`);
@@ -16727,13 +16746,81 @@ function selectStaleSessions(sessions, staleDays, now = Date.now()) {
16727
16746
  const cutoff = now - staleDays * 24 * 60 * 60 * 1e3;
16728
16747
  return sessions.filter((s) => s.updated.getTime() < cutoff);
16729
16748
  }
16749
+ function clearedSessionMessage(priorSessionId, priorMessageCount = 0) {
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.";
16751
+ }
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
+ }
16730
16813
 
16731
16814
  // src/web/commands/session-commands.ts
16732
16815
  async function handleClear(_args, ctx) {
16816
+ const prior = ctx.sessions.current;
16817
+ const priorId = prior?.id ?? null;
16818
+ const priorCount = prior?.messages.length ?? 0;
16733
16819
  ctx.saveIfNeeded();
16734
- ctx.sessions.createSession(ctx.currentProvider, ctx.currentModel);
16820
+ const created = ctx.sessions.createSession(ctx.currentProvider, ctx.currentModel);
16821
+ ctx.unsavedSessions.set(created.id, created);
16735
16822
  ctx.resetWebSessionUsage();
16736
- ctx.send({ type: "info", message: "Conversation cleared." });
16823
+ ctx.send({ type: "info", message: clearedSessionMessage(priorId, priorCount) });
16737
16824
  ctx.sendStatus();
16738
16825
  ctx.sendSessionList();
16739
16826
  }
@@ -16751,7 +16838,7 @@ Cache: write=${cacheCreate} read=${cacheRead}` : "";
16751
16838
  Cost: ${formatCost(cost)}` : "";
16752
16839
  let memoryLine = "";
16753
16840
  try {
16754
- const { countPendingMemories: countPendingMemories2 } = await import("./persistent-memory-E6WECUTW.js");
16841
+ const { countPendingMemories: countPendingMemories2 } = await import("./persistent-memory-TA224ZFQ.js");
16755
16842
  const pending = countPendingMemories2(ctx.config.getConfigDir());
16756
16843
  if (pending > 0) memoryLine = `
16757
16844
  Memory: \u26A0 ${pending} pending approval \u2014 see the Memory panel or /memory`;
@@ -17330,27 +17417,27 @@ async function handleFork(args, ctx) {
17330
17417
  ctx.send({ type: "info", message: "No active session to fork." });
17331
17418
  return;
17332
17419
  }
17333
- const sub = args.join(" ").trim();
17334
- let messageCount = session.messages.length;
17335
- let fromLabel = "current position";
17336
- if (sub) {
17337
- const cp = session.checkpoints.find((c) => c.name === sub);
17338
- if (!cp) {
17339
- const available = session.checkpoints.map((c) => c.name);
17340
- ctx.send({ type: "error", message: available.length > 0 ? `Checkpoint "${sub}" not found. Available: ${available.join(", ")}` : `Checkpoint "${sub}" not found. No checkpoints saved.` });
17341
- return;
17342
- }
17343
- messageCount = cp.messageIndex;
17344
- 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;
17345
17424
  }
17346
17425
  try {
17347
- const originalId = session.id.slice(0, 8);
17348
- const forked = await ctx.sessions.forkSession(messageCount);
17349
- ctx.send({ type: "info", message: `\u{1F500} Session Forked
17350
- Original: ${originalId}
17351
- Forked: ${forked.id.slice(0, 8)} "${forked.title ?? "(untitled)"}"
17352
- From: ${fromLabel}
17353
- Messages: ${forked.messages.length} copied` });
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
+ });
17354
17441
  ctx.sendSessionMessages();
17355
17442
  ctx.sendStatus();
17356
17443
  ctx.sendSessionList();
@@ -17364,41 +17451,42 @@ async function handleRewind(args, ctx) {
17364
17451
  ctx.send({ type: "info", message: "No messages to rewind." });
17365
17452
  return;
17366
17453
  }
17367
- const rewindSub = args[0];
17368
- if (rewindSub === "list" || !rewindSub) {
17369
- const lines = [`Conversation messages (${session.messages.length} total):
17370
- `];
17371
- const cpIndices = (await import("./file-checkpoint-CGH6OJVI.js")).fileCheckpoints.getMessageIndices();
17372
- for (let i = 0; i < session.messages.length; i++) {
17373
- const m = session.messages[i];
17374
- const text = getContentText(m.content).replace(/\n/g, " ").slice(0, 60);
17375
- const pin = cpIndices.includes(i) ? " \u{1F4CC}" : "";
17376
- lines.push(` [${i + 1}] ${m.role.padEnd(10)} ${text}${pin}`);
17377
- }
17378
- lines.push("", "Usage: /rewind <n> \u2014 rewind to message N", "\u{1F4CC} = file checkpoint");
17379
- 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 });
17380
17457
  return;
17381
17458
  }
17382
- const rewindN = parseInt(rewindSub, 10);
17383
- if (isNaN(rewindN) || rewindN < 1 || rewindN > session.messages.length) {
17384
- ctx.send({ type: "error", message: `Invalid message number: ${rewindSub}. Range: 1-${session.messages.length}` });
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
+ });
17385
17473
  return;
17386
17474
  }
17387
- const { fileCheckpoints: fc } = await import("./file-checkpoint-CGH6OJVI.js");
17388
- const rewindRemoved = session.messages.length - rewindN;
17389
- const rewindResult = fc.restoreToMessageIndex(rewindN);
17390
- session.messages = session.messages.slice(0, rewindN);
17391
- session.checkpoints = session.checkpoints.filter((c) => c.messageIndex <= rewindN);
17392
- session.updated = /* @__PURE__ */ new Date();
17393
- ctx.sessions.save();
17394
- const rewindLines = [`\u2713 Rewound to message ${rewindN}`, ` Messages removed: ${rewindRemoved}`];
17395
- if (rewindResult.restored > 0 || rewindResult.deleted > 0) {
17396
- rewindLines.push(` Files restored: ${rewindResult.restored}, deleted: ${rewindResult.deleted}`);
17397
- for (const f of rewindResult.files) rewindLines.push(` ${f}`);
17398
- } else {
17399
- rewindLines.push(" No file changes to revert.");
17400
- }
17401
- ctx.send({ type: "info", message: rewindLines.join("\n") });
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
+ });
17402
17490
  ctx.sendSessionMessages();
17403
17491
  ctx.sendStatus();
17404
17492
  }
@@ -17658,7 +17746,7 @@ async function handleTest(args, ctx) {
17658
17746
  const isCommand = argStr.includes(" ") || /^(mvn|gradle|npm|pytest|cargo|go)\b/.test(argStr);
17659
17747
  testArgs = isCommand ? { command: argStr } : { filter: argStr };
17660
17748
  }
17661
- const runTests = ctx.runTests ?? (await import("./run-tests-LWAFPVS3.js")).executeTests;
17749
+ const runTests = ctx.runTests ?? (await import("./run-tests-JLZ2KIGV.js")).executeTests;
17662
17750
  const report = await runTests(testArgs);
17663
17751
  ctx.send({ type: "info", message: report });
17664
17752
  } catch (err) {
@@ -17994,7 +18082,7 @@ async function handleMemory(args, ctx) {
17994
18082
  ctx.handleMemoryManage(sub, args[1], sub === "expire" ? args[2] : void 0);
17995
18083
  } else if (sub === "export") {
17996
18084
  if (args[1] === "md") {
17997
- const { exportMemoryEntries: exportMemoryEntries2 } = await import("./persistent-memory-E6WECUTW.js");
18085
+ const { exportMemoryEntries: exportMemoryEntries2 } = await import("./persistent-memory-TA224ZFQ.js");
17998
18086
  ctx.send({
17999
18087
  type: "export_data",
18000
18088
  format: "md",
@@ -18433,7 +18521,7 @@ async function handleHelp(_args, ctx) {
18433
18521
  " /about \u2014 Version & author info",
18434
18522
  " /provider <id> \u2014 Switch AI provider",
18435
18523
  " /model <id> \u2014 Switch model",
18436
- " /clear \u2014 Clear conversation & start new session",
18524
+ " /clear \u2014 Start a new session (previous conversation is kept)",
18437
18525
  " /compact [hint] \u2014 Compress conversation history",
18438
18526
  " /think [on|off] \u2014 Toggle extended thinking mode",
18439
18527
  " /plan [execute|exit|status] \u2014 Read-only planning mode (bare /plan enters it)",
@@ -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-PQM4BRI4.js");
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-FW4LBPTS.js");
161
+ const { TaskOrchestrator } = await import("./task-orchestrator-6C3R7KSK.js");
162
162
  const orchestrator = new TaskOrchestrator(config, providers, configManager);
163
163
  let interrupted = false;
164
164
  const onSigint = () => {