@moonshot-ai/kimi-code 0.42.0 → 0.43.0

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 (87) hide show
  1. package/dist/main.mjs +54542 -45374
  2. package/dist/search-worker.mjs +287 -90
  3. package/dist-web/assets/{CodeBlockNode-DXrFlfJq.js → CodeBlockNode-ClI5Zc7C.js} +2 -2
  4. package/dist-web/assets/{Tooltip-D-EUJqdY.js → Tooltip-Dvr1al-t.js} +1 -1
  5. package/dist-web/assets/{abnfDiagram-VRR7QNED-tXWm0yJO.js → abnfDiagram-VRR7QNED-CwBLQmjz.js} +1 -1
  6. package/dist-web/assets/{arc-Dcuc8NBZ.js → arc-CxZ33vix.js} +1 -1
  7. package/dist-web/assets/{architectureDiagram-ZJ3FMSHR-BjFIGYfr.js → architectureDiagram-ZJ3FMSHR-CM71RpvG.js} +1 -1
  8. package/dist-web/assets/{blockDiagram-677ZJIJ3-BTYvQjTR.js → blockDiagram-677ZJIJ3-CGAoZfe5.js} +1 -1
  9. package/dist-web/assets/{c4Diagram-LMCZKHZV-C0xqgo03.js → c4Diagram-LMCZKHZV-DLgWFktO.js} +1 -1
  10. package/dist-web/assets/channel-IY5GdLm-.js +1 -0
  11. package/dist-web/assets/{chunk-2Q5K7J3B-BBRTtiB5.js → chunk-2Q5K7J3B-D8ifWrnI.js} +1 -1
  12. package/dist-web/assets/{chunk-32BRIVSS-3l6XZxpp.js → chunk-32BRIVSS-DpG03lEd.js} +1 -1
  13. package/dist-web/assets/{chunk-5VM5RSS4-A5rXJcEF.js → chunk-5VM5RSS4-D5gZPx0N.js} +1 -1
  14. package/dist-web/assets/{chunk-EX3LRPZG-CCB7x_Qg.js → chunk-EX3LRPZG-BHr3M6yM.js} +1 -1
  15. package/dist-web/assets/{chunk-JWPE2WC7-0N6Q_J0q.js → chunk-JWPE2WC7-BxqMRrNb.js} +1 -1
  16. package/dist-web/assets/{chunk-MOJQB5TN-DOlN8qtA.js → chunk-MOJQB5TN-DGD3Ca9J.js} +1 -1
  17. package/dist-web/assets/{chunk-RYQCIY6F-KpXFcslK.js → chunk-RYQCIY6F-Csv6QbcC.js} +1 -1
  18. package/dist-web/assets/{chunk-V7JOEXUC-CFknWZZI.js → chunk-V7JOEXUC-DpflSafv.js} +1 -1
  19. package/dist-web/assets/{chunk-VR4S4FIN-gihGNGO1.js → chunk-VR4S4FIN-CHAEh_jD.js} +1 -1
  20. package/dist-web/assets/{chunk-XXDRQBXY-C3GGkCwU.js → chunk-XXDRQBXY-BW4XjZut.js} +1 -1
  21. package/dist-web/assets/classDiagram-OUVF2IWQ-CqrT8Lg9.js +1 -0
  22. package/dist-web/assets/classDiagram-v2-EOCWNBFH-CqrT8Lg9.js +1 -0
  23. package/dist-web/assets/{cose-bilkent-JH36ORCC-f33uYY00.js → cose-bilkent-JH36ORCC-BdAGtrTa.js} +1 -1
  24. package/dist-web/assets/{cynefin-VYW2F7L2-DQGCJnlE.js → cynefin-VYW2F7L2-DYKf_Z-v.js} +1 -1
  25. package/dist-web/assets/{cynefinDiagram-TSTJHNR4-CHL_foL_.js → cynefinDiagram-TSTJHNR4-BtxdUqIq.js} +1 -1
  26. package/dist-web/assets/{dagre-VKFMJZFB-EsZwpbfr.js → dagre-VKFMJZFB-4bDkTcg2.js} +1 -1
  27. package/dist-web/assets/{diagram-FQU43EPY-Bk8FGDJs.js → diagram-FQU43EPY-C1ljmLlO.js} +1 -1
  28. package/dist-web/assets/{diagram-G47NLZAW-B_XI4_LC.js → diagram-G47NLZAW-Dr4-bBys.js} +1 -1
  29. package/dist-web/assets/{diagram-NH7WQ7WH-BQY-SQ3_.js → diagram-NH7WQ7WH-Cj9RyKgp.js} +1 -1
  30. package/dist-web/assets/{diagram-OA4YK3LP-CTorHnPK.js → diagram-OA4YK3LP-JUEB_AtR.js} +1 -1
  31. package/dist-web/assets/{diagram-WEI45ONY-B3JjOxuA.js → diagram-WEI45ONY-t5xRZNwv.js} +1 -1
  32. package/dist-web/assets/{ebnfDiagram-CCIWWBDH-DPm9WU6R.js → ebnfDiagram-CCIWWBDH-DCeAYlnS.js} +1 -1
  33. package/dist-web/assets/{erDiagram-Q63AITRT-BF_M9qKN.js → erDiagram-Q63AITRT-DOSLOW_g.js} +1 -1
  34. package/dist-web/assets/{flowDiagram-23GEKE2U-jc1IFMCM.js → flowDiagram-23GEKE2U-DUMMY9pi.js} +1 -1
  35. package/dist-web/assets/{ganttDiagram-NO4QXBWP-CCGkTOSq.js → ganttDiagram-NO4QXBWP-NYl4Z3R4.js} +1 -1
  36. package/dist-web/assets/{gitGraphDiagram-IHSO6WYX-EGCxWX3f.js → gitGraphDiagram-IHSO6WYX-EeKfrgej.js} +1 -1
  37. package/dist-web/assets/index-7SHxad1A.js +962 -0
  38. package/dist-web/assets/{index-DUuyqWli.js → index-Brp-8WTi.js} +2 -2
  39. package/dist-web/assets/{index-CWYttcZZ.js → index-CBLflN6S.js} +1 -1
  40. package/dist-web/assets/{index-BAuXCnoo.js → index-CI2T7J0T.js} +1 -1
  41. package/dist-web/assets/{index-C8RkgE6U.css → index-CRa2d3RK.css} +2 -2
  42. package/dist-web/assets/{index10-BDy7C0f0.js → index10-DApEPd0n.js} +1 -1
  43. package/dist-web/assets/{index11-CyIrSiYK.js → index11-DW3IQa8f.js} +1 -1
  44. package/dist-web/assets/{index5-KI0z5ksp.js → index5-COqqDMp9.js} +1 -1
  45. package/dist-web/assets/{index6-C-4gEeMt.js → index6-Dsp2NXNw.js} +1 -1
  46. package/dist-web/assets/{index7-Dx7US8OE.js → index7-BRjOZjxG.js} +1 -1
  47. package/dist-web/assets/{index8-cNFl9TtI.js → index8-CrWWf7aj.js} +1 -1
  48. package/dist-web/assets/{infoDiagram-FWYZ7A6U-2vAZupw5.js → infoDiagram-FWYZ7A6U-DaH4t3R3.js} +1 -1
  49. package/dist-web/assets/{ishikawaDiagram-FXEZZL3T-D62pnRj0.js → ishikawaDiagram-FXEZZL3T-B6KrpWbv.js} +1 -1
  50. package/dist-web/assets/{journeyDiagram-5HDEW3XC-lRqymxxK.js → journeyDiagram-5HDEW3XC-JJbVJr8m.js} +1 -1
  51. package/dist-web/assets/{kanban-definition-HUTT4EX6-3pkuIM1M.js → kanban-definition-HUTT4EX6-CeRZEVYd.js} +1 -1
  52. package/dist-web/assets/{linear-1g72tnQ_.js → linear-DPKx2NhO.js} +1 -1
  53. package/dist-web/assets/{mermaid.core-Dxfcv_J5.js → mermaid.core-CgZTgv7Y.js} +4 -4
  54. package/dist-web/assets/{mindmap-definition-LN4V7U3C-DqraBb0o.js → mindmap-definition-LN4V7U3C-Bd7aLAJp.js} +1 -1
  55. package/dist-web/assets/{pegDiagram-2B236MQR-BMHCAO0T.js → pegDiagram-2B236MQR-CX4fQesk.js} +1 -1
  56. package/dist-web/assets/{pieDiagram-ENE6RG2P-jm92KpVe.js → pieDiagram-ENE6RG2P-DiFeh11k.js} +1 -1
  57. package/dist-web/assets/{quadrantDiagram-ABIIQ3AL-BvuCpWW3.js → quadrantDiagram-ABIIQ3AL-Bss2swqr.js} +1 -1
  58. package/dist-web/assets/{railroadDiagram-RFXS5EU6-DmPKWKL9.js → railroadDiagram-RFXS5EU6-Dpqal8U_.js} +1 -1
  59. package/dist-web/assets/{requirementDiagram-TGXJPOKE-Ddeu6LBZ.js → requirementDiagram-TGXJPOKE-BXFV4Te6.js} +1 -1
  60. package/dist-web/assets/rive-BTwUD7iz.js +1 -0
  61. package/dist-web/assets/rive_fallback-DXpHVJvQ.js +1 -0
  62. package/dist-web/assets/{sankeyDiagram-HTMAVEWB-CPg2z6Bh.js → sankeyDiagram-HTMAVEWB-BHcMCK_w.js} +1 -1
  63. package/dist-web/assets/{sequenceDiagram-DBY2YBRQ-VogAZWGu.js → sequenceDiagram-DBY2YBRQ-DyUTbMwM.js} +1 -1
  64. package/dist-web/assets/{sizeCapture-X5ZJPWSS-BDqVbn7D.js → sizeCapture-X5ZJPWSS-KpJKp6ok.js} +1 -1
  65. package/dist-web/assets/{stateDiagram-2N3HPSRC-w14d3Aj4.js → stateDiagram-2N3HPSRC-B9WRkRTB.js} +1 -1
  66. package/dist-web/assets/stateDiagram-v2-6OUMAXLB-DaA_2kTJ.js +1 -0
  67. package/dist-web/assets/{swimlanes-5IMT3BWC-CJtgPYav.js → swimlanes-5IMT3BWC-C0-5MIxj.js} +2 -2
  68. package/dist-web/assets/swimlanesDiagram-G3AALYLV-DMbj4GHb.js +8 -0
  69. package/dist-web/assets/{timeline-definition-FHXFAJF6-Bl9qf2WR.js → timeline-definition-FHXFAJF6-Blm9HJcK.js} +1 -1
  70. package/dist-web/assets/{vennDiagram-L72KCM5P-As6yPU6_.js → vennDiagram-L72KCM5P-BhpzFpgZ.js} +1 -1
  71. package/dist-web/assets/{vue.runtime.esm-bundler-sFotuYAs.js → vue.runtime.esm-bundler-BeN4Xi6a.js} +1 -1
  72. package/dist-web/assets/{wardleyDiagram-EHGQE667-BLdfM1g3.js → wardleyDiagram-EHGQE667-DGcHOZD9.js} +1 -1
  73. package/dist-web/assets/{xychartDiagram-FW5EYKEG-P0NbIN2z.js → xychartDiagram-FW5EYKEG---3Lbwvl.js} +1 -1
  74. package/dist-web/index.html +2 -2
  75. package/package.json +7 -7
  76. package/dist-web/assets/channel-BWBJkLRL.js +0 -1
  77. package/dist-web/assets/classDiagram-OUVF2IWQ-CaG1i1dL.js +0 -1
  78. package/dist-web/assets/classDiagram-v2-EOCWNBFH-CaG1i1dL.js +0 -1
  79. package/dist-web/assets/index-HU0LCM-X.js +0 -828
  80. package/dist-web/assets/k3_doodle1-27EZ2HSw.riv +0 -0
  81. package/dist-web/assets/kimi_avatar_default-srYjF2HV.riv +0 -0
  82. package/dist-web/assets/rive-BxcgqsjB.js +0 -1
  83. package/dist-web/assets/rive-CxG7kGQi.wasm +0 -0
  84. package/dist-web/assets/rive_fallback-ByshBW-N.js +0 -1
  85. package/dist-web/assets/rive_fallback-l90fUeAW.wasm +0 -0
  86. package/dist-web/assets/stateDiagram-v2-6OUMAXLB-Z4v6nvtB.js +0 -1
  87. package/dist-web/assets/swimlanesDiagram-G3AALYLV-Cbf2NUtt.js +0 -8
@@ -3,7 +3,7 @@ import fs from "node:fs";
3
3
  import path, { join, relative } from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
5
  import { createHash, randomUUID } from "node:crypto";
6
- import fs$1, { open, readFile, readdir, rm, stat } from "node:fs/promises";
6
+ import fs$1, { open, readFile, readdir, stat } from "node:fs/promises";
7
7
  import os from "node:os";
8
8
  import { AsyncLocalStorage } from "node:async_hooks";
9
9
 
@@ -1469,6 +1469,14 @@ const HEADER_SIZE = 22;
1469
1469
  const CRC_SIZE = 4;
1470
1470
  const MAX_KEY_LEN = 65535;
1471
1471
  const MAX_VAL_LEN = 4294967295;
1472
+ var CorruptFrameError = class extends Error {
1473
+ offset;
1474
+ constructor(message, offset) {
1475
+ super(message);
1476
+ this.name = "CorruptFrameError";
1477
+ this.offset = offset;
1478
+ }
1479
+ };
1472
1480
  const EMPTY = Buffer.alloc(0);
1473
1481
  /**
1474
1482
  * Encode one record into a single Buffer.
@@ -2493,17 +2501,23 @@ async function catchUpWalAsync(walPath, offset, anchor, apply) {
2493
2501
  //#endregion
2494
2502
  //#region ../../packages/minidb/src/rename-replace.ts
2495
2503
  const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
2496
- async function renameReplace(src, dst, opts = {}) {
2497
- if (process.platform !== "win32") return fs$1.rename(src, dst);
2504
+ async function retryEperm(op, opts = {}) {
2498
2505
  const retries = opts.retries ?? 100;
2499
2506
  const base = opts.baseDelayMs ?? 20;
2500
2507
  for (let attempt = 0;; attempt++) try {
2501
- return await fs$1.rename(src, dst);
2508
+ return await op();
2502
2509
  } catch (e) {
2503
2510
  if (e.code !== "EPERM" || attempt >= retries) throw e;
2504
2511
  await sleep(base + Math.floor(Math.random() * (base + 10)));
2505
2512
  }
2506
2513
  }
2514
+ async function withWindowsEpermRetry(op, opts = {}) {
2515
+ if (process.platform !== "win32") return op();
2516
+ return retryEperm(op, opts);
2517
+ }
2518
+ async function renameReplace(src, dst, opts = {}) {
2519
+ return withWindowsEpermRetry(() => fs$1.rename(src, dst), opts);
2520
+ }
2507
2521
 
2508
2522
  //#endregion
2509
2523
  //#region ../../packages/minidb/src/snapshot.ts
@@ -9212,6 +9226,7 @@ var GenerationBuilder = class {
9212
9226
  };
9213
9227
  this.deps.stats.generationBuilds++;
9214
9228
  this.deps.stats.generationBuildDurationMs += performance.now() - t0;
9229
+ this.deps.noteBuildSuccess?.();
9215
9230
  const keep = new Set(prevCurrent ? [id, prevCurrent] : [id]);
9216
9231
  cleanupGenerations(this.deps.dir(), keep).catch(() => {});
9217
9232
  for (const name of textStates.keys()) await fs$1.rm(this.deps.textRegistry.textPostingsPath(name), { force: true }).catch(() => {});
@@ -9227,7 +9242,7 @@ var GenerationBuilder = class {
9227
9242
  return;
9228
9243
  }
9229
9244
  this.deps.stats.generationBuildErrors++;
9230
- this.deps.noteBuildFailure?.();
9245
+ this.deps.noteBuildFailure?.(e);
9231
9246
  throw e;
9232
9247
  } finally {
9233
9248
  if (this.genBuild === gb) this.genBuild = null;
@@ -10000,6 +10015,51 @@ var WritePath = class {
10000
10015
  }
10001
10016
  };
10002
10017
 
10018
+ //#endregion
10019
+ //#region ../../packages/minidb/src/wipe.ts
10020
+ async function acquireWithWait(lock, timeoutMs) {
10021
+ const deadline = Date.now() + timeoutMs;
10022
+ let delay = 10;
10023
+ for (;;) {
10024
+ if (await lock.acquire()) return true;
10025
+ if (Date.now() + delay > deadline) return false;
10026
+ const wait = delay + Math.floor(Math.random() * delay);
10027
+ await new Promise((resolve) => {
10028
+ setTimeout(resolve, wait);
10029
+ });
10030
+ delay = Math.min(delay * 2, 250);
10031
+ }
10032
+ }
10033
+ async function wipeStoreDir(opts) {
10034
+ const lockFiles = opts.lockFiles ?? ["db.lock"];
10035
+ const timeoutMs = opts.lockAcquireTimeoutMs ?? 0;
10036
+ const locks = lockFiles.map((file) => new LockFile(path.join(opts.dir, file)));
10037
+ const releaseAll = () => Promise.all(locks.map((lock) => lock.release().catch(() => {})));
10038
+ const results = await Promise.allSettled(locks.map((lock) => acquireWithWait(lock, timeoutMs)));
10039
+ const failed = results.find((r) => r.status === "rejected");
10040
+ if (failed !== void 0 || results.some((r) => r.status === "fulfilled" && !r.value)) {
10041
+ await releaseAll();
10042
+ if (failed !== void 0) throw failed.reason;
10043
+ return "locked";
10044
+ }
10045
+ try {
10046
+ const isolated = `${opts.dir}.wiping-${process.pid}-${randomUUID()}`;
10047
+ try {
10048
+ await withWindowsEpermRetry(() => fs$1.rename(opts.dir, isolated));
10049
+ } catch (error) {
10050
+ if (error.code === "ENOENT") return "wiped";
10051
+ throw error;
10052
+ }
10053
+ await fs$1.rm(isolated, {
10054
+ recursive: true,
10055
+ force: true
10056
+ });
10057
+ } finally {
10058
+ await releaseAll();
10059
+ }
10060
+ return "wiped";
10061
+ }
10062
+
10003
10063
  //#endregion
10004
10064
  //#region ../../packages/minidb/src/lifecycle.ts
10005
10065
  /** The open() flow: configure, acquire, recover, and kick background
@@ -10266,10 +10326,7 @@ async function openOrRebuildMiniDb(opts, hooks, open) {
10266
10326
  }
10267
10327
  return await open(opts);
10268
10328
  } catch {}
10269
- await fs$1.rm(opts.dir, {
10270
- recursive: true,
10271
- force: true
10272
- });
10329
+ if (await wipeStoreDir({ dir: opts.dir }) === "locked") throw err;
10273
10330
  return open(opts);
10274
10331
  }
10275
10332
  }
@@ -10880,6 +10937,7 @@ var MiniDb = class MiniDb {
10880
10937
  genBuildKickFailureBackoffMs = 3e5;
10881
10938
  lastGenBuildKickAt = 0;
10882
10939
  lastGenBuildFailureAt = 0;
10940
+ lastGenBuildError = null;
10883
10941
  /** Abort handle / mutation queue / single-flight guard / status of the
10884
10942
  * generation build all live in the GenerationBuilder facet (declared
10885
10943
  * below); these views keep the open / write / close paths' call sites
@@ -11014,8 +11072,12 @@ var MiniDb = class MiniDb {
11014
11072
  ensureOpen: () => this.ensureOpen(),
11015
11073
  ensureWritable: () => this.ensureWritable(),
11016
11074
  boundedTextBuild: (name, ti, def, checkpoint) => this.boundedTextBuild(name, ti, def, checkpoint),
11017
- noteBuildFailure: () => {
11075
+ noteBuildFailure: (err) => {
11018
11076
  this.lastGenBuildFailureAt = Date.now();
11077
+ if (err !== void 0) this.lastGenBuildError = err;
11078
+ },
11079
+ noteBuildSuccess: () => {
11080
+ this.lastGenBuildError = null;
11019
11081
  }
11020
11082
  });
11021
11083
  /** The write path facet (write-path.ts): set/del/batch/expire and their
@@ -11573,8 +11635,10 @@ var MiniDb = class MiniDb {
11573
11635
  poison = wal.poison;
11574
11636
  if (!poison) return;
11575
11637
  try {
11576
- const st = await fs$1.stat(this.walPath);
11577
- if (poison.failedAtOffset <= st.size) await fs$1.truncate(this.walPath, poison.failedAtOffset);
11638
+ await withWindowsEpermRetry(async () => {
11639
+ const st = await fs$1.stat(this.walPath);
11640
+ if (poison.failedAtOffset <= st.size) await fs$1.truncate(this.walPath, poison.failedAtOffset);
11641
+ });
11578
11642
  } catch (err) {
11579
11643
  this.writeDisabled = err;
11580
11644
  return;
@@ -11876,6 +11940,16 @@ var MiniDb = class MiniDb {
11876
11940
  }
11877
11941
  };
11878
11942
 
11943
+ //#endregion
11944
+ //#region ../../packages/minidb/src/error-classification.ts
11945
+ function classifyStorageError(error) {
11946
+ if (error instanceof AggregateError) return error.errors.some((inner) => classifyStorageError(inner) === "rebuild") ? "rebuild" : "transient";
11947
+ if (error instanceof SyntaxError || error instanceof CorruptFrameError) return "rebuild";
11948
+ const code = error?.code;
11949
+ if (code === "WAL_WRITE_DISABLED" || code === "WAL_POISONED") return "rebuild";
11950
+ return "transient";
11951
+ }
11952
+
11879
11953
  //#endregion
11880
11954
  //#region ../../packages/kap-server/src/search/docs.ts
11881
11955
  const MAX_DOC_TEXT_CHARS = 2e4;
@@ -12344,7 +12418,15 @@ function legacyFileMetaKey(filePath) {
12344
12418
  }
12345
12419
  const WIRE_READ_CHUNK_BYTES = 1 << 20;
12346
12420
  const WIRE_BATCH_OPS = 1e3;
12421
+ const SYNC_ROUND_BYTE_BUDGET = 64 << 20;
12422
+ const SYNC_ROUND_TIME_BUDGET_MS = 3e4;
12423
+ const SYNC_FAILURE_ESCALATION_LIMIT = 5;
12424
+ const SESSION_SYNC_FAILURE_SKIP_LIMIT = 5;
12425
+ const SESSION_SYNC_SKIP_COOLDOWN_MS = 3e5;
12347
12426
  const EMPTY_BUFFER = Buffer.alloc(0);
12427
+ function syncBudgetExhausted(budget) {
12428
+ return budget.bytesLeft <= 0 || Date.now() >= budget.deadline;
12429
+ }
12348
12430
  function errorMessage(error) {
12349
12431
  return error instanceof Error ? error.message : String(error);
12350
12432
  }
@@ -12434,10 +12516,17 @@ var SearchIndexCore = class {
12434
12516
  openError = null;
12435
12517
  fileMetaMigrated = false;
12436
12518
  lockToken;
12519
+ lastMaintenanceDetail;
12520
+ consecutiveSyncFailures = 0;
12521
+ sessionSyncFailures = /* @__PURE__ */ new Map();
12522
+ sessionSyncSkips = /* @__PURE__ */ new Map();
12437
12523
  db = null;
12438
12524
  openPromise = null;
12439
12525
  refreshPromise = null;
12440
12526
  fullSyncDone = false;
12527
+ syncRoundBytes = SYNC_ROUND_BYTE_BUDGET;
12528
+ syncRoundMs = SYNC_ROUND_TIME_BUDGET_MS;
12529
+ syncSkipCooldownMs = SESSION_SYNC_SKIP_COOLDOWN_MS;
12441
12530
  constructor(options) {
12442
12531
  this.options = options;
12443
12532
  }
@@ -12517,6 +12606,7 @@ var SearchIndexCore = class {
12517
12606
  const opts = {
12518
12607
  dir: this.indexDir,
12519
12608
  valueCodec: "json",
12609
+ valueMode: "disk",
12520
12610
  fsyncPolicy: "everysec",
12521
12611
  onLockFail: "readonly",
12522
12612
  onLockAcquired: (info) => {
@@ -12527,26 +12617,12 @@ var SearchIndexCore = class {
12527
12617
  try {
12528
12618
  return await MiniDb.open(opts);
12529
12619
  } catch (error) {
12530
- if (!isRebuildableCorruption(error)) throw error;
12531
- let probeError;
12532
- try {
12533
- await (await MiniDb.open({
12534
- dir: opts.dir,
12535
- valueCodec: opts.valueCodec
12536
- })).close().catch(() => {});
12537
- probeError = void 0;
12538
- } catch (error) {
12539
- probeError = error;
12540
- }
12541
- if (probeError instanceof LockError) throw error;
12620
+ if (classifyStorageError(error) !== "rebuild") throw error;
12621
+ if (await wipeStoreDir({ dir: this.indexDir }) === "locked") throw error;
12542
12622
  this.log.warn("global search: search-index corruption detected; rebuilding from scratch", {
12543
12623
  dir: this.indexDir,
12544
12624
  error: errorMessage(error)
12545
12625
  });
12546
- await rm(this.indexDir, {
12547
- recursive: true,
12548
- force: true
12549
- });
12550
12626
  return MiniDb.open(opts);
12551
12627
  }
12552
12628
  }
@@ -12627,10 +12703,29 @@ var SearchIndexCore = class {
12627
12703
  let outcome = {
12628
12704
  noop: true,
12629
12705
  sessions: 0,
12630
- documents: 0
12706
+ documents: 0,
12707
+ truncated: false,
12708
+ failures: 0
12631
12709
  };
12632
12710
  await this.tracked(async () => {
12633
- outcome = await this.runSync(sessions);
12711
+ try {
12712
+ outcome = await this.runSync(sessions);
12713
+ this.consecutiveSyncFailures = 0;
12714
+ } catch (error) {
12715
+ if (classifyStorageError(error) !== "rebuild") {
12716
+ this.consecutiveSyncFailures += 1;
12717
+ if (this.consecutiveSyncFailures < SYNC_FAILURE_ESCALATION_LIMIT) throw error;
12718
+ }
12719
+ this.consecutiveSyncFailures = 0;
12720
+ await this.recoverByRebuild(error);
12721
+ outcome = {
12722
+ noop: false,
12723
+ sessions: 0,
12724
+ documents: 0,
12725
+ truncated: true,
12726
+ failures: 0
12727
+ };
12728
+ }
12634
12729
  });
12635
12730
  return {
12636
12731
  ...outcome,
@@ -12638,56 +12733,78 @@ var SearchIndexCore = class {
12638
12733
  lifecycle: this.lifecycleState()
12639
12734
  };
12640
12735
  }
12736
+ async recoverByRebuild(cause) {
12737
+ this.log.warn("global search: search-index state is unrecoverable; wiping and rebuilding from the transcripts", {
12738
+ dir: this.indexDir,
12739
+ error: errorMessage(cause)
12740
+ });
12741
+ await this.reindex();
12742
+ }
12641
12743
  async runSync(sessions) {
12642
- if (this.disposed) return {
12744
+ const noop = {
12643
12745
  noop: true,
12644
12746
  sessions: 0,
12645
- documents: 0
12747
+ documents: 0,
12748
+ truncated: false,
12749
+ failures: 0
12646
12750
  };
12751
+ if (this.disposed) return noop;
12647
12752
  this.syncReplaced = false;
12648
12753
  await this.ensureOpen();
12649
12754
  const db = this.db;
12650
- if (!db || db.readOnly || this.disposed) return {
12651
- noop: true,
12652
- sessions: 0,
12653
- documents: 0
12654
- };
12755
+ if (!db || db.readOnly || this.disposed) return noop;
12655
12756
  await this.migrateFileMetaKeys(db);
12656
12757
  const currentIds = new Set(sessions.map((s) => s.id));
12657
12758
  for (const row of db.query({
12658
12759
  key: { prefix: SESSION_META_PREFIX },
12659
12760
  project: []
12660
12761
  })) {
12661
- if (this.disposed) return {
12662
- noop: true,
12663
- sessions: 0,
12664
- documents: 0
12665
- };
12762
+ if (this.disposed) return noop;
12666
12763
  const sessionId = row.key.slice(14);
12667
12764
  if (!currentIds.has(sessionId)) await this.deleteSessionDocs(db, sessionId);
12668
12765
  }
12766
+ const budget = {
12767
+ bytesLeft: this.syncRoundBytes,
12768
+ deadline: Date.now() + this.syncRoundMs
12769
+ };
12669
12770
  let indexed = 0;
12771
+ let truncated = false;
12772
+ let failures = 0;
12670
12773
  for (const summary of sessions) {
12671
- if (this.disposed) return {
12672
- noop: true,
12673
- sessions: 0,
12674
- documents: 0
12675
- };
12676
- try {
12677
- await this.syncSession(db, summary);
12678
- indexed++;
12679
- } catch (error) {
12680
- this.log.warn("global search: failed to index session", {
12681
- sessionId: summary.id,
12682
- error: error instanceof Error ? error.message : String(error)
12683
- });
12774
+ if (this.disposed) return noop;
12775
+ const skip = this.sessionSyncSkips.get(summary.id);
12776
+ if (skip !== void 0 && summary.updatedAt === skip.updatedAt && Date.now() - skip.at < this.syncSkipCooldownMs) continue;
12777
+ this.sessionSyncSkips.delete(summary.id);
12778
+ if (syncBudgetExhausted(budget)) {
12779
+ truncated = true;
12780
+ break;
12684
12781
  }
12782
+ const result = await this.syncSession(db, summary, budget);
12783
+ if (result.truncated) truncated = true;
12784
+ if (result.failed) {
12785
+ const count = (this.sessionSyncFailures.get(summary.id) ?? 0) + 1;
12786
+ this.sessionSyncFailures.set(summary.id, count);
12787
+ if (count >= SESSION_SYNC_FAILURE_SKIP_LIMIT) {
12788
+ this.sessionSyncFailures.delete(summary.id);
12789
+ this.sessionSyncSkips.set(summary.id, {
12790
+ at: Date.now(),
12791
+ updatedAt: summary.updatedAt
12792
+ });
12793
+ this.log.warn("global search: giving up on a session whose wire transcript stays unreadable", {
12794
+ sessionId: summary.id,
12795
+ error: result.error
12796
+ });
12797
+ } else {
12798
+ failures += 1;
12799
+ this.log.warn("global search: failed to index session", {
12800
+ sessionId: summary.id,
12801
+ error: result.error
12802
+ });
12803
+ }
12804
+ } else this.sessionSyncFailures.delete(summary.id);
12805
+ indexed++;
12685
12806
  }
12686
- if (this.disposed) return {
12687
- noop: true,
12688
- sessions: 0,
12689
- documents: 0
12690
- };
12807
+ if (this.disposed) return noop;
12691
12808
  const metaCount = db.query({
12692
12809
  key: { prefix: "\0meta\\" },
12693
12810
  project: []
@@ -12699,12 +12816,14 @@ var SearchIndexCore = class {
12699
12816
  lastIndexedAt: Date.now()
12700
12817
  };
12701
12818
  await db.set(STATS_KEY, stats);
12702
- this.fullSyncDone = true;
12819
+ if (!truncated && failures === 0) this.fullSyncDone = true;
12703
12820
  if (this.syncReplaced) this.generation++;
12704
12821
  return {
12705
12822
  noop: false,
12706
12823
  sessions: indexed,
12707
- documents: stats.documents
12824
+ documents: stats.documents,
12825
+ truncated,
12826
+ failures
12708
12827
  };
12709
12828
  }
12710
12829
  async migrateFileMetaKeys(db) {
@@ -12741,7 +12860,7 @@ var SearchIndexCore = class {
12741
12860
  })) await db.del(row.key);
12742
12861
  await db.del(SESSION_META_PREFIX + sessionId);
12743
12862
  }
12744
- async syncSession(db, summary) {
12863
+ async syncSession(db, summary, budget) {
12745
12864
  const wireFiles = await collectWireFiles(summary.dir);
12746
12865
  const seenPaths = new Set(wireFiles.map((file) => file.path));
12747
12866
  for (const row of db.query({ key: { prefix: fileMetaPrefixFor(summary.id) } })) {
@@ -12751,7 +12870,17 @@ var SearchIndexCore = class {
12751
12870
  await this.deleteFileDocs(db, meta);
12752
12871
  await db.del(row.key);
12753
12872
  }
12754
- for (const file of wireFiles) await this.syncWireFile(db, summary, file);
12873
+ let truncated = false;
12874
+ let failed = false;
12875
+ let error;
12876
+ for (const file of wireFiles) {
12877
+ const result = await this.syncWireFile(db, summary, file, budget);
12878
+ if (result.truncated) truncated = true;
12879
+ if (result.failed) {
12880
+ failed = true;
12881
+ error ??= result.error;
12882
+ }
12883
+ }
12755
12884
  const title = summary.title ?? "";
12756
12885
  const titleKey = `${summary.id}/$title`;
12757
12886
  const existing = db.get(titleKey);
@@ -12772,6 +12901,11 @@ var SearchIndexCore = class {
12772
12901
  }
12773
12902
  } else if (existing !== void 0) await db.del(titleKey);
12774
12903
  if (db.get(SESSION_META_PREFIX + summary.id) === void 0) await db.set(SESSION_META_PREFIX + summary.id, { kind: "sessionMeta" });
12904
+ return {
12905
+ truncated,
12906
+ failed,
12907
+ error
12908
+ };
12775
12909
  }
12776
12910
  async deleteFileDocs(db, meta) {
12777
12911
  const prefix = `${meta.sessionId}/${meta.agentId}/${meta.source}:`;
@@ -12780,12 +12914,15 @@ var SearchIndexCore = class {
12780
12914
  project: []
12781
12915
  })) await db.del(row.key);
12782
12916
  }
12783
- async syncWireFile(db, summary, file) {
12917
+ async syncWireFile(db, summary, file, budget) {
12784
12918
  let st;
12785
12919
  try {
12786
12920
  st = await stat(file.path);
12787
12921
  } catch {
12788
- return;
12922
+ return {
12923
+ truncated: false,
12924
+ failed: false
12925
+ };
12789
12926
  }
12790
12927
  const size = st.size;
12791
12928
  const metaKey = fileMetaKey(summary.id, file.path);
@@ -12839,22 +12976,51 @@ var SearchIndexCore = class {
12839
12976
  });
12840
12977
  await db.batch(ops);
12841
12978
  }
12842
- return;
12979
+ return {
12980
+ truncated: false,
12981
+ failed: false
12982
+ };
12983
+ }
12984
+ let handle;
12985
+ try {
12986
+ handle = await open(file.path, "r");
12987
+ } catch (error) {
12988
+ return {
12989
+ truncated: false,
12990
+ failed: true,
12991
+ error: errorMessage(error)
12992
+ };
12843
12993
  }
12844
- const handle = await open(file.path, "r");
12845
12994
  const ops = [];
12846
12995
  let byteCursor = offset;
12996
+ let position = offset;
12997
+ let wireError;
12847
12998
  try {
12848
- let position = offset;
12849
12999
  let pending = EMPTY_BUFFER;
13000
+ let finishing = false;
12850
13001
  const chunk = Buffer.allocUnsafe(WIRE_READ_CHUNK_BYTES);
12851
13002
  while (position < size) {
12852
- if (this.disposed) return;
12853
- const { bytesRead } = await handle.read(chunk, 0, Math.min(chunk.length, size - position), position);
13003
+ if (this.disposed) return {
13004
+ truncated: false,
13005
+ failed: false
13006
+ };
13007
+ if (syncBudgetExhausted(budget)) {
13008
+ if (pending.length === 0) break;
13009
+ finishing = true;
13010
+ }
13011
+ let bytesRead;
13012
+ try {
13013
+ ({bytesRead} = await handle.read(chunk, 0, Math.min(chunk.length, size - position), position));
13014
+ } catch (error) {
13015
+ wireError = error;
13016
+ break;
13017
+ }
12854
13018
  if (bytesRead === 0) break;
13019
+ budget.bytesLeft -= bytesRead;
12855
13020
  const slice = chunk.subarray(0, bytesRead);
12856
13021
  position += bytesRead;
12857
13022
  let start = 0;
13023
+ let completedRecord = false;
12858
13024
  for (;;) {
12859
13025
  const nl = slice.indexOf(10, start);
12860
13026
  if (nl === -1) break;
@@ -12866,10 +13032,24 @@ var SearchIndexCore = class {
12866
13032
  turnState,
12867
13033
  stepState
12868
13034
  }));
13035
+ completedRecord = true;
12869
13036
  start = nl + 1;
12870
13037
  }
12871
13038
  pending = pending.length > 0 ? Buffer.concat([pending, slice.subarray(start)]) : Buffer.from(slice.subarray(start));
13039
+ if (finishing && completedRecord) break;
12872
13040
  if (ops.length >= WIRE_BATCH_OPS) {
13041
+ ops.push({
13042
+ op: "set",
13043
+ key: metaKey,
13044
+ value: fileMeta(byteCursor, turnState, stepState)
13045
+ });
13046
+ if (legacyKey !== null) {
13047
+ ops.push({
13048
+ op: "del",
13049
+ key: legacyKey
13050
+ });
13051
+ legacyKey = null;
13052
+ }
12873
13053
  await db.batch(ops);
12874
13054
  ops.length = 0;
12875
13055
  }
@@ -12877,17 +13057,24 @@ var SearchIndexCore = class {
12877
13057
  } finally {
12878
13058
  await handle.close();
12879
13059
  }
12880
- if (byteCursor === offset && legacyKey === null) return;
12881
- ops.push({
12882
- op: "set",
12883
- key: metaKey,
12884
- value: fileMeta(byteCursor, turnState, stepState)
12885
- });
12886
- if (legacyKey !== null) ops.push({
12887
- op: "del",
12888
- key: legacyKey
12889
- });
12890
- await db.batch(ops);
13060
+ const truncated = position < size && syncBudgetExhausted(budget);
13061
+ if (byteCursor !== offset || legacyKey !== null) {
13062
+ ops.push({
13063
+ op: "set",
13064
+ key: metaKey,
13065
+ value: fileMeta(byteCursor, turnState, stepState)
13066
+ });
13067
+ if (legacyKey !== null) ops.push({
13068
+ op: "del",
13069
+ key: legacyKey
13070
+ });
13071
+ await db.batch(ops);
13072
+ }
13073
+ return {
13074
+ truncated,
13075
+ failed: wireError !== void 0,
13076
+ error: wireError !== void 0 ? errorMessage(wireError) : void 0
13077
+ };
12891
13078
  }
12892
13079
  collectWireLine(ops, summary, file, line, lineOffset, counters) {
12893
13080
  let { turnState, stepState } = counters;
@@ -13042,16 +13229,16 @@ var SearchIndexCore = class {
13042
13229
  }
13043
13230
  this.openPromise = null;
13044
13231
  this.fullSyncDone = false;
13232
+ this.sessionSyncFailures.clear();
13233
+ this.sessionSyncSkips.clear();
13045
13234
  this.lockToken = void 0;
13046
- await rm(this.indexDir, {
13047
- recursive: true,
13048
- force: true
13049
- });
13235
+ if (await wipeStoreDir({ dir: this.indexDir }) === "locked") throw new GlobalSearchError("readonly_index", "another process holds the search-index write lock; reindex from that process");
13050
13236
  await this.ensureOpen();
13051
13237
  }
13052
13238
  lifecycleState() {
13053
13239
  if (this.disposed) return { state: this.db === null ? "stopped" : "closing" };
13054
13240
  const db = this.db;
13241
+ if (db !== null) this.noteMaintenanceDetail(db);
13055
13242
  if (db === null) {
13056
13243
  if (this.openPromise !== null) return { state: "opening" };
13057
13244
  if (this.openError !== null) return {
@@ -13063,10 +13250,23 @@ var SearchIndexCore = class {
13063
13250
  if (db.textIndexBuilding(TEXT_INDEX_NAME) || db.textIndexBuilding(TRI_INDEX_NAME)) return { state: "building" };
13064
13251
  return { state: "ready" };
13065
13252
  }
13253
+ maintenanceDetail(db) {
13254
+ const failure = db.lastCompactError ?? db.lastGenBuildError;
13255
+ return failure === null ? void 0 : errorMessage(failure);
13256
+ }
13257
+ noteMaintenanceDetail(db) {
13258
+ const detail = this.maintenanceDetail(db);
13259
+ if (detail === this.lastMaintenanceDetail) return;
13260
+ const previous = this.lastMaintenanceDetail;
13261
+ this.lastMaintenanceDetail = detail;
13262
+ if (detail !== void 0) this.log.warn("global search: index maintenance is failing; the WAL keeps growing until it recovers", { error: detail });
13263
+ else if (previous !== void 0) this.log.info("global search: index maintenance recovered");
13264
+ }
13066
13265
  async status() {
13067
13266
  await this.ensureOpen();
13068
13267
  if (this.db?.readOnly === true) await this.refresh();
13069
13268
  const stats = this.db?.get(STATS_KEY);
13269
+ const maintenance = this.db !== null ? this.maintenanceDetail(this.db) : void 0;
13070
13270
  return {
13071
13271
  sessions: stats?.kind === "stats" ? stats.sessions : 0,
13072
13272
  documents: stats?.kind === "stats" ? stats.documents : 0,
@@ -13074,7 +13274,7 @@ var SearchIndexCore = class {
13074
13274
  generation: this.generation,
13075
13275
  readOnly: this.db?.readOnly === true,
13076
13276
  lockToken: this.lockToken,
13077
- degraded: this.lastRefreshError?.message,
13277
+ degraded: this.lastRefreshError?.message ?? maintenance,
13078
13278
  lifecycle: this.lifecycleState()
13079
13279
  };
13080
13280
  }
@@ -13137,9 +13337,6 @@ async function collectWireFiles(sessionDir) {
13137
13337
  function docKeyPrefix(sessionId, file) {
13138
13338
  return `${sessionId}/${file.agentId}/${file.source}:`;
13139
13339
  }
13140
- function isRebuildableCorruption(error) {
13141
- return error instanceof SyntaxError || error !== null && typeof error === "object" && error.name === "CorruptFrameError";
13142
- }
13143
13340
 
13144
13341
  //#endregion
13145
13342
  //#region ../../packages/kap-server/src/search/worker/entry.ts