jinzd-ai-cli 0.4.278 → 0.4.279

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-7Y4NAA6N.js → batch-6QZSWCBL.js} +2 -2
  2. package/dist/{chunk-X3722RFA.js → chunk-2BKRWTEQ.js} +1 -1
  3. package/dist/{chunk-SJCOE4C7.js → chunk-5XHEXRMN.js} +1 -1
  4. package/dist/{chunk-RFSKPCYC.js → chunk-6WMT7OQG.js} +1 -1
  5. package/dist/{chunk-JJCNNQTU.js → chunk-7L4TEK7Q.js} +239 -3
  6. package/dist/{chunk-LXXQTWT2.js → chunk-CMNXAJLH.js} +1 -1
  7. package/dist/{chunk-WOXIKQRI.js → chunk-FQJFGGKN.js} +4 -4
  8. package/dist/{chunk-L3CPM472.js → chunk-IIXZ7IPN.js} +1 -1
  9. package/dist/{chunk-LTZFUYBT.js → chunk-JLOQBYU4.js} +3 -3
  10. package/dist/{chunk-4Z6GAYB5.js → chunk-JS6VAPWM.js} +3 -3
  11. package/dist/{chunk-SB4XONS4.js → chunk-NZPHEUJM.js} +1 -1
  12. package/dist/{chunk-H7QC5VMQ.js → chunk-RJY3HJS4.js} +1 -1
  13. package/dist/{chunk-LOXEYWC3.js → chunk-SIVM2YQQ.js} +1 -1
  14. package/dist/{chunk-2BYQP6JX.js → chunk-UD3BVNC5.js} +1 -1
  15. package/dist/{ci-WDF2CYR4.js → ci-F4ESOYI5.js} +4 -4
  16. package/dist/{ci-format-7B5VAZGA.js → ci-format-AIRW3TVQ.js} +2 -2
  17. package/dist/{constants-DC3TNLTL.js → constants-3VMMZJDS.js} +1 -1
  18. package/dist/{doctor-cli-CDDG5XOY.js → doctor-cli-WVMEWMYE.js} +4 -4
  19. package/dist/electron-server.js +398 -58
  20. package/dist/{hub-TO7ODOIA.js → hub-JJCCCU3O.js} +1 -1
  21. package/dist/index.js +112 -323
  22. package/dist/{persistent-memory-BU63CSGK.js → persistent-memory-KSZKM4JG.js} +2 -2
  23. package/dist/{persistent-memory-JCENBMCJ.js → persistent-memory-QZN4Q3W4.js} +2 -2
  24. package/dist/{pr-GVSZYX6I.js → pr-GFB5JC26.js} +4 -4
  25. package/dist/{run-tests-R4IQS6IF.js → run-tests-EL54INOI.js} +2 -2
  26. package/dist/{run-tests-2Q5MJHXB.js → run-tests-HMHHIG7Y.js} +2 -2
  27. package/dist/{server-WXLE53HG.js → server-6C7GITHY.js} +5 -5
  28. package/dist/{server-KAURBKFI.js → server-WUSJWC3A.js} +187 -63
  29. package/dist/{task-orchestrator-RJ6FXYY5.js → task-orchestrator-C7RUUA2Q.js} +5 -5
  30. package/dist/{usage-V3R7DA7M.js → usage-CW6BT2UV.js} +2 -2
  31. package/dist/web/client/app.js +14 -2
  32. package/dist/web/client/app.js.br +0 -0
  33. package/dist/web/client/app.js.gz +0 -0
  34. package/package.json +1 -1
@@ -40,14 +40,14 @@ import {
40
40
  touchMemoryReferences,
41
41
  updateMemoryApproval,
42
42
  updateMemoryEntry
43
- } from "./chunk-LXXQTWT2.js";
43
+ } from "./chunk-CMNXAJLH.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-LOXEYWC3.js";
50
+ } from "./chunk-SIVM2YQQ.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-SJCOE4C7.js";
83
+ } from "./chunk-5XHEXRMN.js";
84
84
  import {
85
85
  hasSemanticIndex,
86
86
  semanticSearch
@@ -16407,6 +16407,232 @@ function lastAssistantText(messages) {
16407
16407
  return void 0;
16408
16408
  }
16409
16409
 
16410
+ // src/core/session-command.ts
16411
+ var DEFAULT_SESSION_LIST_LIMIT = 50;
16412
+ function parseSessionListArgs(args) {
16413
+ let limit = DEFAULT_SESSION_LIST_LIMIT;
16414
+ let all = false;
16415
+ const filter = {};
16416
+ const warnings = [];
16417
+ let search = null;
16418
+ let days = null;
16419
+ let since = null;
16420
+ let sort = { field: "updated", order: "desc" };
16421
+ let json = false;
16422
+ let stale = null;
16423
+ let verbose = false;
16424
+ for (let i = 1; i < args.length; i++) {
16425
+ const a = args[i];
16426
+ if (a === "--all" || a === "-a") {
16427
+ all = true;
16428
+ } else if ((a === "--limit" || a === "-n") && i + 1 < args.length) {
16429
+ const n = Number(args[i + 1]);
16430
+ if (Number.isFinite(n) && n > 0) {
16431
+ limit = n;
16432
+ i++;
16433
+ }
16434
+ } else if (a === "--filter" && i + 1 < args.length) {
16435
+ const val = args[++i];
16436
+ const eq = val.indexOf("=");
16437
+ if (eq > 0) {
16438
+ const key = val.slice(0, eq);
16439
+ const vals = val.slice(eq + 1).split(",").map((s) => s.trim().toLowerCase());
16440
+ if (key === "provider") filter.provider = vals;
16441
+ else if (key === "model") filter.model = vals;
16442
+ else if (key === "messages") {
16443
+ const n = Number(vals[0]);
16444
+ if (Number.isFinite(n) && n >= 0) filter.messages = n;
16445
+ else warnings.push(`Invalid filter value for "messages": expected a non-negative integer, got "${vals[0]}"`);
16446
+ } else {
16447
+ warnings.push(`Unknown filter key: "${key}". Supported keys: provider, model, messages`);
16448
+ }
16449
+ } else {
16450
+ warnings.push(`Invalid --filter syntax: "${val}". Expected key=value`);
16451
+ }
16452
+ } else if ((a === "--search" || a === "-s") && i + 1 < args.length) {
16453
+ search = args[++i];
16454
+ } else if (a === "--days" && i + 1 < args.length) {
16455
+ const n = Number(args[++i]);
16456
+ if (Number.isFinite(n) && n > 0) days = n;
16457
+ } else if (a === "--since" && i + 1 < args.length) {
16458
+ since = args[++i];
16459
+ } else if (a === "--sort" && i + 1 < args.length) {
16460
+ const sv = args[++i];
16461
+ const colon = sv.indexOf(":");
16462
+ const field = colon > 0 ? sv.slice(0, colon) : sv;
16463
+ const order = colon > 0 ? sv.slice(colon + 1) : "desc";
16464
+ if (field === "updated" || field === "created" || field === "messages" || field === "size") {
16465
+ sort = { field, order: order === "asc" ? "asc" : "desc" };
16466
+ }
16467
+ } else if (a === "--json") {
16468
+ json = true;
16469
+ } else if (a === "--verbose" || a === "-v") {
16470
+ verbose = true;
16471
+ } else if (a === "--stale" && i + 1 < args.length) {
16472
+ const n = Number(args[++i]);
16473
+ if (Number.isFinite(n) && n > 0) stale = n;
16474
+ }
16475
+ }
16476
+ if (all) limit = -1;
16477
+ return { limit, all, filter, search, days, since, sort, json, stale, verbose, warnings };
16478
+ }
16479
+ function sessionListNeedsFileSize(opts) {
16480
+ return opts.verbose || opts.sort.field === "size";
16481
+ }
16482
+ function selectSessions(sessions, opts, now = Date.now()) {
16483
+ let matched = sessions;
16484
+ const hasFilters = !!opts.filter.provider || !!opts.filter.model || opts.filter.messages !== void 0;
16485
+ const hasSearch = !!opts.search;
16486
+ const hasTimeFilter = opts.days !== null || opts.since !== null;
16487
+ const hasStale = opts.stale !== null;
16488
+ if (hasFilters) {
16489
+ const p = opts.filter.provider;
16490
+ const m = opts.filter.model;
16491
+ const msg = opts.filter.messages;
16492
+ matched = matched.filter((s) => {
16493
+ if (p && !p.includes(s.provider.toLowerCase())) return false;
16494
+ if (m && !m.some((kw) => s.model.toLowerCase().includes(kw))) return false;
16495
+ if (msg !== void 0 && s.messageCount !== msg) return false;
16496
+ return true;
16497
+ });
16498
+ }
16499
+ if (hasSearch) {
16500
+ const q = opts.search.toLowerCase();
16501
+ matched = matched.filter(
16502
+ (s) => (s.title ?? "").toLowerCase().includes(q) || s.id.toLowerCase().includes(q)
16503
+ );
16504
+ }
16505
+ if (hasTimeFilter) {
16506
+ let cutoff = 0;
16507
+ if (opts.since) cutoff = new Date(opts.since).getTime();
16508
+ else if (opts.days !== null) cutoff = now - opts.days * 24 * 60 * 60 * 1e3;
16509
+ if (!Number.isNaN(cutoff)) {
16510
+ matched = matched.filter((s) => s.updated.getTime() >= cutoff);
16511
+ }
16512
+ }
16513
+ const staleCutoff = hasStale ? now - opts.stale * 24 * 60 * 60 * 1e3 : null;
16514
+ if (staleCutoff !== null) {
16515
+ matched = matched.filter((s) => s.updated.getTime() < staleCutoff);
16516
+ }
16517
+ const defaultSort = opts.sort.field === "updated" && opts.sort.order === "desc";
16518
+ if (!defaultSort) {
16519
+ const mul = opts.sort.order === "asc" ? 1 : -1;
16520
+ matched = [...matched].sort((a, b) => {
16521
+ switch (opts.sort.field) {
16522
+ case "created":
16523
+ return (a.created.getTime() - b.created.getTime()) * mul;
16524
+ case "messages":
16525
+ return (a.messageCount - b.messageCount) * mul;
16526
+ case "size":
16527
+ return ((a.fileSize ?? 0) - (b.fileSize ?? 0)) * mul;
16528
+ default:
16529
+ return (a.updated.getTime() - b.updated.getTime()) * mul;
16530
+ }
16531
+ });
16532
+ }
16533
+ const filterTags = [];
16534
+ if (hasFilters) filterTags.push("filtered");
16535
+ if (hasSearch) filterTags.push("searched");
16536
+ if (hasTimeFilter) filterTags.push("time-limited");
16537
+ if (hasStale) filterTags.push(`stale >${opts.stale}d`);
16538
+ return {
16539
+ matched,
16540
+ shown: opts.limit > 0 ? matched.slice(0, opts.limit) : matched,
16541
+ totalCount: matched.length,
16542
+ totalMessages: matched.reduce((sum, s) => sum + s.messageCount, 0),
16543
+ staleCutoff,
16544
+ filterTags,
16545
+ needsFileSize: sessionListNeedsFileSize(opts)
16546
+ };
16547
+ }
16548
+ function sessionListSummary(view) {
16549
+ const tag = view.filterTags.length > 0 ? ` (${view.filterTags.join(", ")})` : "";
16550
+ return `${view.shown.length} of ${view.totalCount} sessions${tag} \xB7 ${view.totalMessages} messages total`;
16551
+ }
16552
+ function sessionListJsonRow(s, includeFileSize) {
16553
+ return {
16554
+ id: s.id,
16555
+ title: s.title ?? null,
16556
+ provider: s.provider,
16557
+ model: s.model,
16558
+ messageCount: s.messageCount,
16559
+ created: s.created.toISOString(),
16560
+ updated: s.updated.toISOString(),
16561
+ ...includeFileSize ? { fileSize: s.fileSize } : {},
16562
+ ...s.branchCount !== void 0 ? { branchCount: s.branchCount, activeBranch: s.activeBranch } : {}
16563
+ };
16564
+ }
16565
+ function resolveSessionRef(sessions, prefix, policy) {
16566
+ const matches2 = sessions.filter((s) => s.id.startsWith(prefix));
16567
+ if (matches2.length === 0) {
16568
+ return { status: "not-found", message: `Session '${prefix}' not found.` };
16569
+ }
16570
+ if (matches2.length > 1 && policy === "strict") {
16571
+ return {
16572
+ status: "ambiguous",
16573
+ matches: matches2,
16574
+ message: `Ambiguous prefix '${prefix}' matches ${matches2.length} sessions \u2014 be more specific:`
16575
+ };
16576
+ }
16577
+ return {
16578
+ status: "ok",
16579
+ match: matches2[0],
16580
+ ambiguousWith: matches2.length > 1 ? matches2 : [],
16581
+ message: matches2.length > 1 ? `\u26A0 Ambiguous prefix '${prefix}' matches ${matches2.length} sessions \u2014 loading most recent:` : void 0
16582
+ };
16583
+ }
16584
+ function formatSessionSummaryLine(s, titleWidth = 60) {
16585
+ const title = s.title ?? "(untitled)";
16586
+ const shown = title.length > titleWidth ? `${title.slice(0, titleWidth - 1)}\u2026` : title;
16587
+ return `${s.id.slice(0, 12)} ${s.provider}/${s.model} ${String(s.messageCount).padStart(4)} msgs ${s.updated.toISOString().slice(0, 10)} ${shown}`;
16588
+ }
16589
+ function parseBatchDeleteArgs(args) {
16590
+ let messages;
16591
+ let dryRun = false;
16592
+ for (let i = 1; i < args.length; i++) {
16593
+ if (args[i] === "--filter" && i + 1 < args.length) {
16594
+ const val = args[++i];
16595
+ const eq = val.indexOf("=");
16596
+ if (eq <= 0) {
16597
+ return { dryRun, error: `Invalid --filter syntax: "${val}". Expected key=value (e.g., messages=0)` };
16598
+ }
16599
+ const key = val.slice(0, eq);
16600
+ const vals = val.slice(eq + 1).split(",").map((s) => s.trim().toLowerCase());
16601
+ if (key !== "messages") {
16602
+ return { dryRun, error: `Unsupported filter key for delete: "${key}". Only "messages" is supported.` };
16603
+ }
16604
+ const n = Number(vals[0]);
16605
+ if (!Number.isFinite(n) || n < 0) {
16606
+ return { dryRun, error: `Invalid filter value for "messages": expected a non-negative integer, got "${vals[0]}"` };
16607
+ }
16608
+ messages = n;
16609
+ } else if (args[i] === "--dry-run") {
16610
+ dryRun = true;
16611
+ }
16612
+ }
16613
+ if (messages === void 0) {
16614
+ return { dryRun, error: "Usage: /session delete --filter messages=N [--dry-run]" };
16615
+ }
16616
+ return { messages, dryRun };
16617
+ }
16618
+ function parseCleanArgs(args) {
16619
+ let staleDays = 90;
16620
+ let dryRun = false;
16621
+ for (let i = 1; i < args.length; i++) {
16622
+ if (args[i] === "--stale" && i + 1 < args.length) {
16623
+ const n = Number(args[++i]);
16624
+ if (Number.isFinite(n) && n > 0) staleDays = n;
16625
+ } else if (args[i] === "--dry-run") {
16626
+ dryRun = true;
16627
+ }
16628
+ }
16629
+ return { staleDays, dryRun };
16630
+ }
16631
+ function selectStaleSessions(sessions, staleDays, now = Date.now()) {
16632
+ const cutoff = now - staleDays * 24 * 60 * 60 * 1e3;
16633
+ return sessions.filter((s) => s.updated.getTime() < cutoff);
16634
+ }
16635
+
16410
16636
  // src/web/commands/session-commands.ts
16411
16637
  async function handleClear(_args, ctx) {
16412
16638
  ctx.saveIfNeeded();
@@ -16430,7 +16656,7 @@ Cache: write=${cacheCreate} read=${cacheRead}` : "";
16430
16656
  Cost: ${formatCost(cost)}` : "";
16431
16657
  let memoryLine = "";
16432
16658
  try {
16433
- const { countPendingMemories: countPendingMemories2 } = await import("./persistent-memory-JCENBMCJ.js");
16659
+ const { countPendingMemories: countPendingMemories2 } = await import("./persistent-memory-QZN4Q3W4.js");
16434
16660
  const pending = countPendingMemories2(ctx.config.getConfigDir());
16435
16661
  if (pending > 0) memoryLine = `
16436
16662
  Memory: \u26A0 ${pending} pending approval \u2014 see the Memory panel or /memory`;
@@ -16473,11 +16699,17 @@ async function handleSession(args, ctx) {
16473
16699
  ctx.sendSessionList();
16474
16700
  return;
16475
16701
  }
16476
- const list = ctx.sessions.listSessions();
16477
- const found = list.find((s) => s.id.startsWith(targetId));
16478
- if (found) {
16702
+ const ref = resolveSessionRef(ctx.sessions.listSessions(), targetId, "most-recent");
16703
+ if (ref.status === "ok") {
16704
+ const found = ref.match;
16479
16705
  ctx.sessions.loadSession(found.id);
16480
16706
  ctx.resetWebSessionUsage();
16707
+ if (ref.message) {
16708
+ ctx.send({
16709
+ type: "info",
16710
+ message: [ref.message, ...ref.ambiguousWith.slice(0, 5).map((m) => ` ${formatSessionSummaryLine(m)}`)].join("\n")
16711
+ });
16712
+ }
16481
16713
  ctx.send({ type: "info", message: `Loaded session: ${found.id.slice(0, 8)} "${found.title ?? ""}" (${found.messageCount} messages)` });
16482
16714
  const loadedSession = ctx.sessions.current;
16483
16715
  if (loadedSession && loadedSession.messages.length > 0) {
@@ -16507,52 +16739,81 @@ async function handleSession(args, ctx) {
16507
16739
  return;
16508
16740
  }
16509
16741
  if (sub === "list") {
16510
- ctx.sendSessionList();
16742
+ if (args.length <= 1) {
16743
+ ctx.sendSessionList();
16744
+ return;
16745
+ }
16746
+ const opts = parseSessionListArgs(args);
16747
+ const all = ctx.sessions.listSessions({ includeFileSize: sessionListNeedsFileSize(opts) });
16748
+ if (all.length === 0) {
16749
+ ctx.send({ type: "info", message: "No saved sessions." });
16750
+ return;
16751
+ }
16752
+ const view = selectSessions(all, opts);
16753
+ if (view.totalCount === 0) {
16754
+ ctx.send({
16755
+ type: "info",
16756
+ message: [...opts.warnings.map((w) => `\u26A0 ${w}`), "No sessions match the filter criteria."].join("\n")
16757
+ });
16758
+ return;
16759
+ }
16760
+ if (opts.json) {
16761
+ ctx.send({
16762
+ type: "info",
16763
+ message: JSON.stringify(view.shown.map((s) => sessionListJsonRow(s, view.needsFileSize)), null, 2)
16764
+ });
16765
+ return;
16766
+ }
16767
+ const lines = [
16768
+ ...opts.warnings.map((w) => `\u26A0 ${w}`),
16769
+ ...view.shown.map((s) => formatSessionSummaryLine(s)),
16770
+ `\u2500\u2500 ${sessionListSummary(view)}`
16771
+ ];
16772
+ if (view.shown.length < view.totalCount) lines.push("Use --all to show all, or --limit N to change count.");
16773
+ ctx.send({ type: "info", message: lines.join("\n") });
16511
16774
  return;
16512
16775
  }
16513
16776
  if (sub === "delete" && args[1]) {
16514
16777
  const targetId = args[1];
16515
16778
  if (targetId === "--filter") {
16516
- let filterMessages;
16517
- for (let i = 1; i < args.length; i++) {
16518
- if (args[i] === "--filter" && i + 1 < args.length) {
16519
- const val = args[i + 1];
16520
- i++;
16521
- const eq = val.indexOf("=");
16522
- if (eq > 0) {
16523
- const key = val.slice(0, eq);
16524
- const vals = val.slice(eq + 1).split(",").map((s) => s.trim().toLowerCase());
16525
- if (key === "messages") {
16526
- const n = Number(vals[0]);
16527
- if (Number.isFinite(n) && n >= 0) filterMessages = n;
16528
- }
16529
- }
16530
- }
16531
- }
16532
- if (filterMessages === void 0) {
16533
- ctx.send({ type: "error", message: "Usage: /session delete --filter messages=N" });
16779
+ const opts = parseBatchDeleteArgs(args);
16780
+ if (opts.error) {
16781
+ ctx.send({ type: "error", message: opts.error });
16534
16782
  return;
16535
16783
  }
16536
- const list2 = ctx.sessions.listSessions();
16537
- const matches2 = list2.filter((s) => s.messageCount === filterMessages);
16784
+ const matches2 = ctx.sessions.listSessions().filter((s) => s.messageCount === opts.messages);
16538
16785
  if (matches2.length === 0) {
16539
- ctx.send({ type: "info", message: `No sessions with ${filterMessages} message(s) found.` });
16786
+ ctx.send({ type: "info", message: `No sessions with ${opts.messages} message(s) found.` });
16787
+ return;
16788
+ }
16789
+ const preview = [
16790
+ `Found ${matches2.length} session(s) with ${opts.messages} message(s):`,
16791
+ ...matches2.slice(0, 20).map((s) => ` ${formatSessionSummaryLine(s)}`),
16792
+ ...matches2.length > 20 ? [` ... and ${matches2.length - 20} more`] : []
16793
+ ];
16794
+ if (opts.dryRun) {
16795
+ ctx.send({
16796
+ type: "info",
16797
+ message: [...preview, "--dry-run: no sessions were deleted. Remove --dry-run to actually delete."].join("\n")
16798
+ });
16540
16799
  return;
16541
16800
  }
16542
16801
  let deleted = 0;
16543
16802
  let failed = 0;
16544
16803
  for (const s of matches2) {
16545
- if (ctx.sessions.deleteSession(s.id)) {
16546
- deleted++;
16547
- } else {
16548
- failed++;
16549
- }
16804
+ if (ctx.sessions.deleteSession(s.id)) deleted++;
16805
+ else failed++;
16550
16806
  }
16551
16807
  if (!ctx.sessions.current) {
16552
- ctx.sessions.createSession(ctx.currentProvider, ctx.currentModel);
16808
+ const created = ctx.sessions.createSession(ctx.currentProvider, ctx.currentModel);
16809
+ ctx.unsavedSessions.set(created.id, created);
16553
16810
  ctx.resetWebSessionUsage();
16554
16811
  }
16555
- ctx.send({ type: "info", message: `Deleted ${deleted} session(s) with ${filterMessages} message(s)${failed > 0 ? `, ${failed} failed` : ""}.` });
16812
+ ctx.send({
16813
+ type: "info",
16814
+ message: [...preview, `Deleted ${deleted} session(s)${failed > 0 ? `, ${failed} failed` : ""}.`].join("\n")
16815
+ });
16816
+ ctx.sendStatus();
16556
16817
  ctx.sendSessionList();
16557
16818
  return;
16558
16819
  }
@@ -16561,20 +16822,79 @@ async function handleSession(args, ctx) {
16561
16822
  ctx.unsavedSessions.delete(cachedKey);
16562
16823
  if (ctx.sessions.current?.id === cachedKey) {
16563
16824
  ctx.sessions.clearCurrent();
16825
+ const created = ctx.sessions.createSession(ctx.currentProvider, ctx.currentModel);
16826
+ ctx.unsavedSessions.set(created.id, created);
16827
+ ctx.resetWebSessionUsage();
16828
+ ctx.sendStatus();
16564
16829
  }
16565
16830
  ctx.send({ type: "info", message: `Deleted session: ${cachedKey.slice(0, 8)}` });
16566
16831
  ctx.sendSessionList();
16567
16832
  return;
16568
16833
  }
16569
- const list = ctx.sessions.listSessions();
16570
- const found = list.find((s) => s.id.startsWith(targetId));
16571
- if (found) {
16572
- ctx.sessions.deleteSession(found.id);
16573
- ctx.send({ type: "info", message: `Deleted session: ${found.id.slice(0, 8)}` });
16574
- ctx.sendSessionList();
16575
- } else {
16834
+ const ref = resolveSessionRef(ctx.sessions.listSessions(), targetId, "strict");
16835
+ if (ref.status === "not-found") {
16576
16836
  ctx.send({ type: "error", message: `Session not found: ${targetId}` });
16837
+ return;
16838
+ }
16839
+ if (ref.status === "ambiguous") {
16840
+ ctx.send({
16841
+ type: "error",
16842
+ message: [ref.message, ...ref.matches.slice(0, 10).map((m) => ` ${formatSessionSummaryLine(m)}`)].join("\n")
16843
+ });
16844
+ return;
16845
+ }
16846
+ const wasCurrent = ctx.sessions.current?.id === ref.match.id;
16847
+ if (!ctx.sessions.deleteSession(ref.match.id)) {
16848
+ ctx.send({ type: "error", message: "Delete failed \u2014 session file not found on disk." });
16849
+ return;
16850
+ }
16851
+ ctx.send({ type: "info", message: `Deleted session: ${ref.match.id.slice(0, 8)}` });
16852
+ if (wasCurrent) {
16853
+ const created = ctx.sessions.createSession(ctx.currentProvider, ctx.currentModel);
16854
+ ctx.unsavedSessions.set(created.id, created);
16855
+ ctx.resetWebSessionUsage();
16856
+ ctx.send({ type: "info", message: "Current session was deleted \u2014 started a fresh session." });
16857
+ ctx.sendStatus();
16858
+ }
16859
+ ctx.sendSessionList();
16860
+ return;
16861
+ }
16862
+ if (sub === "clean") {
16863
+ const opts = parseCleanArgs(args);
16864
+ const stale = selectStaleSessions(ctx.sessions.listSessions(), opts.staleDays);
16865
+ if (stale.length === 0) {
16866
+ ctx.send({ type: "info", message: `No sessions older than ${opts.staleDays} days found.` });
16867
+ return;
16577
16868
  }
16869
+ const preview = [
16870
+ `Found ${stale.length} session(s) not updated in ${opts.staleDays}+ days:`,
16871
+ ...stale.slice(0, 20).map((s) => ` ${formatSessionSummaryLine(s)}`),
16872
+ ...stale.length > 20 ? [` ... and ${stale.length - 20} more`] : []
16873
+ ];
16874
+ if (opts.dryRun) {
16875
+ ctx.send({
16876
+ type: "info",
16877
+ message: [...preview, "--dry-run: no sessions were deleted. Remove --dry-run to actually delete."].join("\n")
16878
+ });
16879
+ return;
16880
+ }
16881
+ let deleted = 0;
16882
+ let failed = 0;
16883
+ for (const s of stale) {
16884
+ if (ctx.sessions.deleteSession(s.id)) deleted++;
16885
+ else failed++;
16886
+ }
16887
+ if (!ctx.sessions.current) {
16888
+ const created = ctx.sessions.createSession(ctx.currentProvider, ctx.currentModel);
16889
+ ctx.unsavedSessions.set(created.id, created);
16890
+ ctx.resetWebSessionUsage();
16891
+ ctx.sendStatus();
16892
+ }
16893
+ ctx.send({
16894
+ type: "info",
16895
+ message: [...preview, `Cleaned up: ${deleted} deleted${failed > 0 ? `, ${failed} failed` : ""}.`].join("\n")
16896
+ });
16897
+ ctx.sendSessionList();
16578
16898
  return;
16579
16899
  }
16580
16900
  if (sub === "batch-delete") {
@@ -16598,6 +16918,12 @@ async function handleSession(args, ctx) {
16598
16918
  deleted++;
16599
16919
  }
16600
16920
  }
16921
+ if (!ctx.sessions.current) {
16922
+ const created = ctx.sessions.createSession(ctx.currentProvider, ctx.currentModel);
16923
+ ctx.unsavedSessions.set(created.id, created);
16924
+ ctx.resetWebSessionUsage();
16925
+ ctx.sendStatus();
16926
+ }
16601
16927
  ctx.send({ type: "info", message: `Deleted ${deleted} session(s).` });
16602
16928
  ctx.sendSessionList();
16603
16929
  return;
@@ -16611,26 +16937,40 @@ async function handleSession(args, ctx) {
16611
16937
  }
16612
16938
  const cachedKey = ctx.unsavedSessions.has(targetId) ? targetId : [...ctx.unsavedSessions.keys()].find((k) => k.startsWith(targetId));
16613
16939
  if (cachedKey) {
16614
- const session = ctx.unsavedSessions.get(cachedKey);
16615
- session.title = newTitle;
16940
+ const session2 = ctx.unsavedSessions.get(cachedKey);
16941
+ session2.title = newTitle;
16616
16942
  ctx.send({ type: "info", message: `Renamed session: "${newTitle}"` });
16617
16943
  ctx.sendSessionList();
16618
16944
  return;
16619
16945
  }
16620
- const list = ctx.sessions.listSessions();
16621
- const found = list.find((s) => s.id.startsWith(targetId));
16622
- if (found) {
16623
- const session = ctx.sessions.loadSession(found.id);
16624
- session.title = newTitle;
16625
- ctx.sessions.save();
16626
- ctx.send({ type: "info", message: `Renamed session: "${newTitle}"` });
16627
- ctx.sendSessionList();
16628
- } else {
16946
+ const ref = resolveSessionRef(ctx.sessions.listSessions(), targetId, "strict");
16947
+ if (ref.status === "not-found") {
16629
16948
  ctx.send({ type: "error", message: `Session not found: ${targetId}` });
16949
+ return;
16950
+ }
16951
+ if (ref.status === "ambiguous") {
16952
+ ctx.send({
16953
+ type: "error",
16954
+ message: [ref.message, ...ref.matches.slice(0, 10).map((m) => ` ${formatSessionSummaryLine(m)}`)].join("\n")
16955
+ });
16956
+ return;
16957
+ }
16958
+ const priorId = ctx.sessions.current?.id ?? null;
16959
+ const isCurrent = priorId === ref.match.id;
16960
+ const session = isCurrent ? ctx.sessions.current : ctx.sessions.loadSession(ref.match.id);
16961
+ session.title = newTitle;
16962
+ ctx.sessions.save();
16963
+ if (!isCurrent && priorId) {
16964
+ ctx.sessions.loadSession(priorId);
16630
16965
  }
16966
+ ctx.send({ type: "info", message: `Renamed session: "${newTitle}"` });
16967
+ ctx.sendSessionList();
16631
16968
  return;
16632
16969
  }
16633
- ctx.send({ type: "info", message: "Usage: /session new | list | load <id> | delete <id> | delete --filter messages=N | rename <id> <title>" });
16970
+ ctx.send({
16971
+ type: "info",
16972
+ message: "Usage: /session new | list [--filter k=v] [--search kw] [--limit N|--all] [--sort f[:o]] [--json] | load <id> | delete <id> | delete --filter messages=N [--dry-run] | clean --stale N [--dry-run] | rename <id> <title>"
16973
+ });
16634
16974
  }
16635
16975
  async function handleExport(args, ctx) {
16636
16976
  const format = args[0] === "json" ? "json" : "md";
@@ -17243,7 +17583,7 @@ async function handleTest(args, ctx) {
17243
17583
  const isCommand = argStr.includes(" ") || /^(mvn|gradle|npm|pytest|cargo|go)\b/.test(argStr);
17244
17584
  testArgs = isCommand ? { command: argStr } : { filter: argStr };
17245
17585
  }
17246
- const runTests = ctx.runTests ?? (await import("./run-tests-2Q5MJHXB.js")).executeTests;
17586
+ const runTests = ctx.runTests ?? (await import("./run-tests-HMHHIG7Y.js")).executeTests;
17247
17587
  const report = await runTests(testArgs);
17248
17588
  ctx.send({ type: "info", message: report });
17249
17589
  } catch (err) {
@@ -17579,7 +17919,7 @@ async function handleMemory(args, ctx) {
17579
17919
  ctx.handleMemoryManage(sub, args[1], sub === "expire" ? args[2] : void 0);
17580
17920
  } else if (sub === "export") {
17581
17921
  if (args[1] === "md") {
17582
- const { exportMemoryEntries: exportMemoryEntries2 } = await import("./persistent-memory-JCENBMCJ.js");
17922
+ const { exportMemoryEntries: exportMemoryEntries2 } = await import("./persistent-memory-QZN4Q3W4.js");
17583
17923
  ctx.send({
17584
17924
  type: "export_data",
17585
17925
  format: "md",
@@ -18023,7 +18363,7 @@ async function handleHelp(_args, ctx) {
18023
18363
  " /think [on|off] \u2014 Toggle extended thinking mode",
18024
18364
  " /plan [execute|exit|status] \u2014 Read-only planning mode (bare /plan enters it)",
18025
18365
  " /route [on|off|show|test <msg>] \u2014 Smart model routing",
18026
- " /session new|list|load|delete <id> \u2014 Session management",
18366
+ " /session new|list [filters]|load <id>|delete <id>|delete --filter messages=N [--dry-run]|clean --stale N [--dry-run]|rename <id> <title> \u2014 Session management",
18027
18367
  " /system [prompt|clear] \u2014 Set/view/reset system prompt",
18028
18368
  " /context [status|reload] \u2014 Show/reload context layers",
18029
18369
  " /status \u2014 Show session info & token usage",
@@ -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-RJ6FXYY5.js");
161
+ const { TaskOrchestrator } = await import("./task-orchestrator-C7RUUA2Q.js");
162
162
  const orchestrator = new TaskOrchestrator(config, providers, configManager);
163
163
  let interrupted = false;
164
164
  const onSigint = () => {