@moonshot-ai/kimi-code 0.42.0 → 0.43.1

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 (96) hide show
  1. package/dist/main.mjs +88223 -77662
  2. package/dist/search-worker.mjs +436 -96
  3. package/dist-web/assets/{CodeBlockNode-DXrFlfJq.js → CodeBlockNode-BDSAqNOc.js} +2 -2
  4. package/dist-web/assets/{Tooltip-D-EUJqdY.js → Tooltip-BE4IWX2u.js} +1 -1
  5. package/dist-web/assets/{abnfDiagram-VRR7QNED-tXWm0yJO.js → abnfDiagram-VRR7QNED-CWbGy1cv.js} +1 -1
  6. package/dist-web/assets/{arc-Dcuc8NBZ.js → arc-DQe8dUYI.js} +1 -1
  7. package/dist-web/assets/{architectureDiagram-ZJ3FMSHR-BjFIGYfr.js → architectureDiagram-ZJ3FMSHR-BDOquUyr.js} +1 -1
  8. package/dist-web/assets/{blockDiagram-677ZJIJ3-BTYvQjTR.js → blockDiagram-677ZJIJ3-CJtbCzkv.js} +1 -1
  9. package/dist-web/assets/{c4Diagram-LMCZKHZV-C0xqgo03.js → c4Diagram-LMCZKHZV-t37hrhPN.js} +1 -1
  10. package/dist-web/assets/channel-AQC2UigP.js +1 -0
  11. package/dist-web/assets/{chunk-2Q5K7J3B-BBRTtiB5.js → chunk-2Q5K7J3B-DSH4W9W7.js} +1 -1
  12. package/dist-web/assets/{chunk-32BRIVSS-3l6XZxpp.js → chunk-32BRIVSS-DQSjSlNX.js} +1 -1
  13. package/dist-web/assets/{chunk-5VM5RSS4-A5rXJcEF.js → chunk-5VM5RSS4-g2RPEnPU.js} +1 -1
  14. package/dist-web/assets/{chunk-EX3LRPZG-CCB7x_Qg.js → chunk-EX3LRPZG-DdTW1EAS.js} +1 -1
  15. package/dist-web/assets/{chunk-JWPE2WC7-0N6Q_J0q.js → chunk-JWPE2WC7-BT01S0dy.js} +1 -1
  16. package/dist-web/assets/{chunk-MOJQB5TN-DOlN8qtA.js → chunk-MOJQB5TN-IWlQJyow.js} +1 -1
  17. package/dist-web/assets/{chunk-RYQCIY6F-KpXFcslK.js → chunk-RYQCIY6F-pV91xl4J.js} +1 -1
  18. package/dist-web/assets/{chunk-V7JOEXUC-CFknWZZI.js → chunk-V7JOEXUC-xr_3E2Wz.js} +1 -1
  19. package/dist-web/assets/{chunk-VR4S4FIN-gihGNGO1.js → chunk-VR4S4FIN-DAoPzKmc.js} +1 -1
  20. package/dist-web/assets/{chunk-XXDRQBXY-C3GGkCwU.js → chunk-XXDRQBXY-4Gs_c1bc.js} +1 -1
  21. package/dist-web/assets/classDiagram-OUVF2IWQ-9J31pWLC.js +1 -0
  22. package/dist-web/assets/classDiagram-v2-EOCWNBFH-9J31pWLC.js +1 -0
  23. package/dist-web/assets/{cose-bilkent-JH36ORCC-f33uYY00.js → cose-bilkent-JH36ORCC-DHYtc2Lg.js} +1 -1
  24. package/dist-web/assets/{cynefin-VYW2F7L2-DQGCJnlE.js → cynefin-VYW2F7L2-DZANU5FS.js} +1 -1
  25. package/dist-web/assets/{cynefinDiagram-TSTJHNR4-CHL_foL_.js → cynefinDiagram-TSTJHNR4-Cmq3GSLA.js} +1 -1
  26. package/dist-web/assets/{dagre-VKFMJZFB-EsZwpbfr.js → dagre-VKFMJZFB-BT-klNzv.js} +1 -1
  27. package/dist-web/assets/{diagram-FQU43EPY-Bk8FGDJs.js → diagram-FQU43EPY-DWnhmVxO.js} +1 -1
  28. package/dist-web/assets/{diagram-G47NLZAW-B_XI4_LC.js → diagram-G47NLZAW-sN3uAWXP.js} +1 -1
  29. package/dist-web/assets/{diagram-NH7WQ7WH-BQY-SQ3_.js → diagram-NH7WQ7WH-BgNStFDL.js} +1 -1
  30. package/dist-web/assets/{diagram-OA4YK3LP-CTorHnPK.js → diagram-OA4YK3LP-9eEe-Z3v.js} +1 -1
  31. package/dist-web/assets/{diagram-WEI45ONY-B3JjOxuA.js → diagram-WEI45ONY-aTjaDb2N.js} +1 -1
  32. package/dist-web/assets/{ebnfDiagram-CCIWWBDH-DPm9WU6R.js → ebnfDiagram-CCIWWBDH-R8xU167H.js} +1 -1
  33. package/dist-web/assets/{erDiagram-Q63AITRT-BF_M9qKN.js → erDiagram-Q63AITRT-CXEtYr4J.js} +1 -1
  34. package/dist-web/assets/{flowDiagram-23GEKE2U-jc1IFMCM.js → flowDiagram-23GEKE2U-BJmfiM29.js} +1 -1
  35. package/dist-web/assets/{ganttDiagram-NO4QXBWP-CCGkTOSq.js → ganttDiagram-NO4QXBWP-B7c5nduP.js} +1 -1
  36. package/dist-web/assets/{gitGraphDiagram-IHSO6WYX-EGCxWX3f.js → gitGraphDiagram-IHSO6WYX-DJsxIHAg.js} +1 -1
  37. package/dist-web/assets/index-Baap3_X9.js +962 -0
  38. package/dist-web/assets/{index-BAuXCnoo.js → index-CSaMMAzR.js} +1 -1
  39. package/dist-web/assets/{index-CWYttcZZ.js → index-DGpUsOcn.js} +1 -1
  40. package/dist-web/assets/{index-DUuyqWli.js → index-Dc9ox5VC.js} +2 -2
  41. package/dist-web/assets/index-dAouBbjn.css +32 -0
  42. package/dist-web/assets/{index10-BDy7C0f0.js → index10-rPjEEum5.js} +1 -1
  43. package/dist-web/assets/{index11-CyIrSiYK.js → index11-B7H8oxtu.js} +1 -1
  44. package/dist-web/assets/{index5-KI0z5ksp.js → index5-tPh937rm.js} +1 -1
  45. package/dist-web/assets/{index6-C-4gEeMt.js → index6-D8sB43ws.js} +1 -1
  46. package/dist-web/assets/{index7-Dx7US8OE.js → index7-DRyeTVHt.js} +1 -1
  47. package/dist-web/assets/{index8-cNFl9TtI.js → index8-CpVaCNRn.js} +1 -1
  48. package/dist-web/assets/{infoDiagram-FWYZ7A6U-2vAZupw5.js → infoDiagram-FWYZ7A6U-Ds-biZ51.js} +1 -1
  49. package/dist-web/assets/{ishikawaDiagram-FXEZZL3T-D62pnRj0.js → ishikawaDiagram-FXEZZL3T-D_WjGjIa.js} +1 -1
  50. package/dist-web/assets/{journeyDiagram-5HDEW3XC-lRqymxxK.js → journeyDiagram-5HDEW3XC-Df5gewaW.js} +1 -1
  51. package/dist-web/assets/{kanban-definition-HUTT4EX6-3pkuIM1M.js → kanban-definition-HUTT4EX6-Cds-KoSu.js} +1 -1
  52. package/dist-web/assets/{linear-1g72tnQ_.js → linear-CxP8zfSg.js} +1 -1
  53. package/dist-web/assets/{mermaid.core-Dxfcv_J5.js → mermaid.core-CCxZmhet.js} +4 -4
  54. package/dist-web/assets/{mindmap-definition-LN4V7U3C-DqraBb0o.js → mindmap-definition-LN4V7U3C-BYqiMfi6.js} +1 -1
  55. package/dist-web/assets/{pegDiagram-2B236MQR-BMHCAO0T.js → pegDiagram-2B236MQR-Dy4Ywp29.js} +1 -1
  56. package/dist-web/assets/{pieDiagram-ENE6RG2P-jm92KpVe.js → pieDiagram-ENE6RG2P-CCwKCWcY.js} +1 -1
  57. package/dist-web/assets/{quadrantDiagram-ABIIQ3AL-BvuCpWW3.js → quadrantDiagram-ABIIQ3AL-D-6UvOrh.js} +1 -1
  58. package/dist-web/assets/{railroadDiagram-RFXS5EU6-DmPKWKL9.js → railroadDiagram-RFXS5EU6-dVR2ADod.js} +1 -1
  59. package/dist-web/assets/{requirementDiagram-TGXJPOKE-Ddeu6LBZ.js → requirementDiagram-TGXJPOKE--ZrFbbKb.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-gGCfZA-t.js} +1 -1
  63. package/dist-web/assets/{sequenceDiagram-DBY2YBRQ-VogAZWGu.js → sequenceDiagram-DBY2YBRQ-CgG59Dk9.js} +1 -1
  64. package/dist-web/assets/{sizeCapture-X5ZJPWSS-BDqVbn7D.js → sizeCapture-X5ZJPWSS-B4Fr8K_f.js} +1 -1
  65. package/dist-web/assets/{stateDiagram-2N3HPSRC-w14d3Aj4.js → stateDiagram-2N3HPSRC-BkQ1_Aee.js} +1 -1
  66. package/dist-web/assets/stateDiagram-v2-6OUMAXLB-DzeZEC_o.js +1 -0
  67. package/dist-web/assets/{swimlanes-5IMT3BWC-CJtgPYav.js → swimlanes-5IMT3BWC-BcJFT27N.js} +2 -2
  68. package/dist-web/assets/swimlanesDiagram-G3AALYLV-B8Mt2UUm.js +8 -0
  69. package/dist-web/assets/{timeline-definition-FHXFAJF6-Bl9qf2WR.js → timeline-definition-FHXFAJF6-r0QD09OO.js} +1 -1
  70. package/dist-web/assets/{vennDiagram-L72KCM5P-As6yPU6_.js → vennDiagram-L72KCM5P-Copqf7LE.js} +1 -1
  71. package/dist-web/assets/{vue.runtime.esm-bundler-sFotuYAs.js → vue.runtime.esm-bundler-C9hhbb_t.js} +1 -1
  72. package/dist-web/assets/{wardleyDiagram-EHGQE667-BLdfM1g3.js → wardleyDiagram-EHGQE667-nNOP_Z8t.js} +1 -1
  73. package/dist-web/assets/{xychartDiagram-FW5EYKEG-P0NbIN2z.js → xychartDiagram-FW5EYKEG-alLFyDZG.js} +1 -1
  74. package/dist-web/index.html +2 -2
  75. package/native/darwin/prebuilds/darwin-arm64/{darwin-modifiers.node → darwin-platform.node} +0 -0
  76. package/native/darwin/prebuilds/darwin-x64/{darwin-modifiers.node → darwin-platform.node} +0 -0
  77. package/native/linux/prebuilds/linux-arm64/linux-platform-x11.node +0 -0
  78. package/native/linux/prebuilds/linux-x64/linux-platform-x11.node +0 -0
  79. package/native/win32/prebuilds/win32-arm64/win32-platform.node +0 -0
  80. package/native/win32/prebuilds/win32-x64/win32-platform.node +0 -0
  81. package/package.json +5 -5
  82. package/dist-web/assets/channel-BWBJkLRL.js +0 -1
  83. package/dist-web/assets/classDiagram-OUVF2IWQ-CaG1i1dL.js +0 -1
  84. package/dist-web/assets/classDiagram-v2-EOCWNBFH-CaG1i1dL.js +0 -1
  85. package/dist-web/assets/index-C8RkgE6U.css +0 -32
  86. package/dist-web/assets/index-HU0LCM-X.js +0 -828
  87. package/dist-web/assets/k3_doodle1-27EZ2HSw.riv +0 -0
  88. package/dist-web/assets/kimi_avatar_default-srYjF2HV.riv +0 -0
  89. package/dist-web/assets/rive-BxcgqsjB.js +0 -1
  90. package/dist-web/assets/rive-CxG7kGQi.wasm +0 -0
  91. package/dist-web/assets/rive_fallback-ByshBW-N.js +0 -1
  92. package/dist-web/assets/rive_fallback-l90fUeAW.wasm +0 -0
  93. package/dist-web/assets/stateDiagram-v2-6OUMAXLB-Z4v6nvtB.js +0 -1
  94. package/dist-web/assets/swimlanesDiagram-G3AALYLV-Cbf2NUtt.js +0 -8
  95. package/native/win32/prebuilds/win32-arm64/win32-console-mode.node +0 -0
  96. package/native/win32/prebuilds/win32-x64/win32-console-mode.node +0 -0
@@ -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,10 +12418,72 @@ 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
  }
12433
+ async function sessionDirectoryIdentity(dir) {
12434
+ try {
12435
+ const info = await stat(dir, { bigint: true });
12436
+ if (!info.isDirectory() || info.ino <= 0n || info.birthtimeNs <= 0n) return void 0;
12437
+ return `${info.dev}:${info.ino}:${info.birthtimeNs}`;
12438
+ } catch (error) {
12439
+ const code = error.code;
12440
+ if (code === "ENOENT" || code === "ENOTDIR") return void 0;
12441
+ throw error;
12442
+ }
12443
+ }
12444
+ const querySourceChecks = {
12445
+ running: false,
12446
+ waiters: /* @__PURE__ */ new Set()
12447
+ };
12448
+ async function queryDirectoryIdentity(dir, deadlineAt, deadline) {
12449
+ while (querySourceChecks.running) {
12450
+ let wake;
12451
+ const available = new Promise((resolve) => {
12452
+ wake = () => {
12453
+ resolve(true);
12454
+ };
12455
+ });
12456
+ querySourceChecks.waiters.add(wake);
12457
+ try {
12458
+ if (await Promise.race([available, deadline]) === null) return null;
12459
+ } finally {
12460
+ querySourceChecks.waiters.delete(wake);
12461
+ }
12462
+ }
12463
+ if (Date.now() >= deadlineAt) return null;
12464
+ querySourceChecks.running = true;
12465
+ const identity = sessionDirectoryIdentity(dir);
12466
+ const release = () => {
12467
+ querySourceChecks.running = false;
12468
+ for (const wake of querySourceChecks.waiters) wake();
12469
+ querySourceChecks.waiters.clear();
12470
+ };
12471
+ identity.then(release, release);
12472
+ return Promise.race([identity, deadline]);
12473
+ }
12474
+ async function sessionDirectoryTitle(dir, log) {
12475
+ for (const scope of ["", "session-meta"]) try {
12476
+ const meta = JSON.parse(await readFile(join(dir, scope, "state.json"), "utf8"));
12477
+ if (typeof meta === "object" && meta !== null && "title" in meta && typeof meta.title === "string") return meta.title;
12478
+ return "";
12479
+ } catch (error) {
12480
+ if (error.code !== "ENOENT") log.warn("search index: cannot read session title", {
12481
+ dir,
12482
+ error: errorMessage(error)
12483
+ });
12484
+ }
12485
+ return "";
12486
+ }
12351
12487
  const INITIAL_TURN_STATE = {
12352
12488
  next: 0,
12353
12489
  hasTurn: false,
@@ -12434,10 +12570,17 @@ var SearchIndexCore = class {
12434
12570
  openError = null;
12435
12571
  fileMetaMigrated = false;
12436
12572
  lockToken;
12573
+ lastMaintenanceDetail;
12574
+ consecutiveSyncFailures = 0;
12575
+ sessionSyncFailures = /* @__PURE__ */ new Map();
12576
+ sessionSyncSkips = /* @__PURE__ */ new Map();
12437
12577
  db = null;
12438
12578
  openPromise = null;
12439
12579
  refreshPromise = null;
12440
12580
  fullSyncDone = false;
12581
+ syncRoundBytes = SYNC_ROUND_BYTE_BUDGET;
12582
+ syncRoundMs = SYNC_ROUND_TIME_BUDGET_MS;
12583
+ syncSkipCooldownMs = SESSION_SYNC_SKIP_COOLDOWN_MS;
12441
12584
  constructor(options) {
12442
12585
  this.options = options;
12443
12586
  }
@@ -12517,6 +12660,7 @@ var SearchIndexCore = class {
12517
12660
  const opts = {
12518
12661
  dir: this.indexDir,
12519
12662
  valueCodec: "json",
12663
+ valueMode: "disk",
12520
12664
  fsyncPolicy: "everysec",
12521
12665
  onLockFail: "readonly",
12522
12666
  onLockAcquired: (info) => {
@@ -12527,26 +12671,12 @@ var SearchIndexCore = class {
12527
12671
  try {
12528
12672
  return await MiniDb.open(opts);
12529
12673
  } 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;
12674
+ if (classifyStorageError(error) !== "rebuild") throw error;
12675
+ if (await wipeStoreDir({ dir: this.indexDir }) === "locked") throw error;
12542
12676
  this.log.warn("global search: search-index corruption detected; rebuilding from scratch", {
12543
12677
  dir: this.indexDir,
12544
12678
  error: errorMessage(error)
12545
12679
  });
12546
- await rm(this.indexDir, {
12547
- recursive: true,
12548
- force: true
12549
- });
12550
12680
  return MiniDb.open(opts);
12551
12681
  }
12552
12682
  }
@@ -12627,10 +12757,29 @@ var SearchIndexCore = class {
12627
12757
  let outcome = {
12628
12758
  noop: true,
12629
12759
  sessions: 0,
12630
- documents: 0
12760
+ documents: 0,
12761
+ truncated: false,
12762
+ failures: 0
12631
12763
  };
12632
12764
  await this.tracked(async () => {
12633
- outcome = await this.runSync(sessions);
12765
+ try {
12766
+ outcome = await this.runSync(sessions);
12767
+ this.consecutiveSyncFailures = 0;
12768
+ } catch (error) {
12769
+ if (classifyStorageError(error) !== "rebuild") {
12770
+ this.consecutiveSyncFailures += 1;
12771
+ if (this.consecutiveSyncFailures < SYNC_FAILURE_ESCALATION_LIMIT) throw error;
12772
+ }
12773
+ this.consecutiveSyncFailures = 0;
12774
+ await this.recoverByRebuild(error);
12775
+ outcome = {
12776
+ noop: false,
12777
+ sessions: 0,
12778
+ documents: 0,
12779
+ truncated: true,
12780
+ failures: 0
12781
+ };
12782
+ }
12634
12783
  });
12635
12784
  return {
12636
12785
  ...outcome,
@@ -12638,73 +12787,99 @@ var SearchIndexCore = class {
12638
12787
  lifecycle: this.lifecycleState()
12639
12788
  };
12640
12789
  }
12790
+ async recoverByRebuild(cause) {
12791
+ this.log.warn("global search: search-index state is unrecoverable; wiping and rebuilding from the transcripts", {
12792
+ dir: this.indexDir,
12793
+ error: errorMessage(cause)
12794
+ });
12795
+ await this.reindex();
12796
+ }
12641
12797
  async runSync(sessions) {
12642
- if (this.disposed) return {
12798
+ const noop = {
12643
12799
  noop: true,
12644
12800
  sessions: 0,
12645
- documents: 0
12801
+ documents: 0,
12802
+ truncated: false,
12803
+ failures: 0
12646
12804
  };
12805
+ if (this.disposed) return noop;
12647
12806
  this.syncReplaced = false;
12648
12807
  await this.ensureOpen();
12649
12808
  const db = this.db;
12650
- if (!db || db.readOnly || this.disposed) return {
12651
- noop: true,
12652
- sessions: 0,
12653
- documents: 0
12654
- };
12809
+ if (!db || db.readOnly || this.disposed) return noop;
12655
12810
  await this.migrateFileMetaKeys(db);
12656
12811
  const currentIds = new Set(sessions.map((s) => s.id));
12657
12812
  for (const row of db.query({
12658
12813
  key: { prefix: SESSION_META_PREFIX },
12659
12814
  project: []
12660
12815
  })) {
12661
- if (this.disposed) return {
12662
- noop: true,
12663
- sessions: 0,
12664
- documents: 0
12665
- };
12816
+ if (this.disposed) return noop;
12666
12817
  const sessionId = row.key.slice(14);
12667
12818
  if (!currentIds.has(sessionId)) await this.deleteSessionDocs(db, sessionId);
12668
12819
  }
12820
+ const budget = {
12821
+ bytesLeft: this.syncRoundBytes,
12822
+ deadline: Date.now() + this.syncRoundMs
12823
+ };
12669
12824
  let indexed = 0;
12825
+ let truncated = false;
12826
+ let failures = 0;
12670
12827
  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
- });
12828
+ if (this.disposed) return noop;
12829
+ const skip = this.sessionSyncSkips.get(summary.id);
12830
+ if (skip !== void 0 && summary.updatedAt === skip.updatedAt && Date.now() - skip.at < this.syncSkipCooldownMs) continue;
12831
+ this.sessionSyncSkips.delete(summary.id);
12832
+ if (syncBudgetExhausted(budget)) {
12833
+ truncated = true;
12834
+ break;
12684
12835
  }
12836
+ const result = await this.syncSession(db, summary, budget);
12837
+ if (result === void 0) continue;
12838
+ if (result.truncated) truncated = true;
12839
+ if (result.failed) {
12840
+ const count = (this.sessionSyncFailures.get(summary.id) ?? 0) + 1;
12841
+ this.sessionSyncFailures.set(summary.id, count);
12842
+ if (count >= SESSION_SYNC_FAILURE_SKIP_LIMIT) {
12843
+ this.sessionSyncFailures.delete(summary.id);
12844
+ this.sessionSyncSkips.set(summary.id, {
12845
+ at: Date.now(),
12846
+ updatedAt: summary.updatedAt
12847
+ });
12848
+ this.log.warn("global search: giving up on a session whose wire transcript stays unreadable", {
12849
+ sessionId: summary.id,
12850
+ error: result.error
12851
+ });
12852
+ } else {
12853
+ failures += 1;
12854
+ this.log.warn("global search: failed to index session", {
12855
+ sessionId: summary.id,
12856
+ error: result.error
12857
+ });
12858
+ }
12859
+ } else this.sessionSyncFailures.delete(summary.id);
12860
+ indexed++;
12685
12861
  }
12686
- if (this.disposed) return {
12687
- noop: true,
12688
- sessions: 0,
12689
- documents: 0
12690
- };
12862
+ if (this.disposed) return noop;
12691
12863
  const metaCount = db.query({
12692
12864
  key: { prefix: "\0meta\\" },
12693
12865
  project: []
12694
12866
  }).length;
12695
12867
  const stats = {
12696
12868
  kind: "stats",
12869
+ degraded: indexed < sessions.length ? `Skipped ${sessions.length - indexed} session(s) during indexing` : void 0,
12697
12870
  sessions: indexed,
12698
12871
  documents: db.size - metaCount,
12699
12872
  lastIndexedAt: Date.now()
12700
12873
  };
12701
12874
  await db.set(STATS_KEY, stats);
12702
- this.fullSyncDone = true;
12875
+ if (!truncated && failures === 0) this.fullSyncDone = true;
12703
12876
  if (this.syncReplaced) this.generation++;
12704
12877
  return {
12705
12878
  noop: false,
12706
12879
  sessions: indexed,
12707
- documents: stats.documents
12880
+ documents: stats.documents,
12881
+ truncated,
12882
+ failures
12708
12883
  };
12709
12884
  }
12710
12885
  async migrateFileMetaKeys(db) {
@@ -12741,7 +12916,35 @@ var SearchIndexCore = class {
12741
12916
  })) await db.del(row.key);
12742
12917
  await db.del(SESSION_META_PREFIX + sessionId);
12743
12918
  }
12744
- async syncSession(db, summary) {
12919
+ async syncSession(db, summary, budget) {
12920
+ let identity;
12921
+ try {
12922
+ identity = await sessionDirectoryIdentity(summary.dir);
12923
+ } catch (error) {
12924
+ this.log.warn("global search: failed to index session", {
12925
+ sessionId: summary.id,
12926
+ error: errorMessage(error)
12927
+ });
12928
+ return;
12929
+ }
12930
+ if (identity === void 0) {
12931
+ await this.deleteSessionDocs(db, summary.id);
12932
+ return;
12933
+ }
12934
+ const title = await sessionDirectoryTitle(summary.dir, this.log);
12935
+ const metaKey = SESSION_META_PREFIX + summary.id;
12936
+ const previous = db.get(metaKey);
12937
+ if (previous?.kind !== "sessionMeta" || previous.identity !== identity || previous.dir !== summary.dir) {
12938
+ await this.deleteSessionDocs(db, summary.id);
12939
+ if (previous !== void 0) this.syncReplaced = true;
12940
+ }
12941
+ const meta = {
12942
+ kind: "sessionMeta",
12943
+ dir: summary.dir,
12944
+ identity,
12945
+ title
12946
+ };
12947
+ if (previous?.kind !== "sessionMeta" || previous.identity !== identity || previous.dir !== summary.dir || previous.title !== title) await db.set(metaKey, meta);
12745
12948
  const wireFiles = await collectWireFiles(summary.dir);
12746
12949
  const seenPaths = new Set(wireFiles.map((file) => file.path));
12747
12950
  for (const row of db.query({ key: { prefix: fileMetaPrefixFor(summary.id) } })) {
@@ -12751,14 +12954,28 @@ var SearchIndexCore = class {
12751
12954
  await this.deleteFileDocs(db, meta);
12752
12955
  await db.del(row.key);
12753
12956
  }
12754
- for (const file of wireFiles) await this.syncWireFile(db, summary, file);
12755
- const title = summary.title ?? "";
12957
+ let truncated = false;
12958
+ let failed = false;
12959
+ let error;
12960
+ for (const file of wireFiles) {
12961
+ const result = await this.syncWireFile(db, {
12962
+ ...summary,
12963
+ title,
12964
+ sessionIdentity: identity
12965
+ }, file, budget);
12966
+ if (result.truncated) truncated = true;
12967
+ if (result.failed) {
12968
+ failed = true;
12969
+ error ??= result.error;
12970
+ }
12971
+ }
12756
12972
  const titleKey = `${summary.id}/$title`;
12757
12973
  const existing = db.get(titleKey);
12758
12974
  if (title.length > 0) {
12759
12975
  if (existing?.kind !== "title" || existing.text !== title) {
12760
12976
  const doc = {
12761
12977
  kind: "title",
12978
+ sessionIdentity: identity,
12762
12979
  sessionId: summary.id,
12763
12980
  workspaceId: summary.workspaceId,
12764
12981
  sessionTitle: title,
@@ -12771,7 +12988,11 @@ var SearchIndexCore = class {
12771
12988
  if (existing !== void 0) this.syncReplaced = true;
12772
12989
  }
12773
12990
  } else if (existing !== void 0) await db.del(titleKey);
12774
- if (db.get(SESSION_META_PREFIX + summary.id) === void 0) await db.set(SESSION_META_PREFIX + summary.id, { kind: "sessionMeta" });
12991
+ return {
12992
+ truncated,
12993
+ failed,
12994
+ error
12995
+ };
12775
12996
  }
12776
12997
  async deleteFileDocs(db, meta) {
12777
12998
  const prefix = `${meta.sessionId}/${meta.agentId}/${meta.source}:`;
@@ -12780,12 +13001,15 @@ var SearchIndexCore = class {
12780
13001
  project: []
12781
13002
  })) await db.del(row.key);
12782
13003
  }
12783
- async syncWireFile(db, summary, file) {
13004
+ async syncWireFile(db, summary, file, budget) {
12784
13005
  let st;
12785
13006
  try {
12786
13007
  st = await stat(file.path);
12787
13008
  } catch {
12788
- return;
13009
+ return {
13010
+ truncated: false,
13011
+ failed: false
13012
+ };
12789
13013
  }
12790
13014
  const size = st.size;
12791
13015
  const metaKey = fileMetaKey(summary.id, file.path);
@@ -12839,22 +13063,51 @@ var SearchIndexCore = class {
12839
13063
  });
12840
13064
  await db.batch(ops);
12841
13065
  }
12842
- return;
13066
+ return {
13067
+ truncated: false,
13068
+ failed: false
13069
+ };
13070
+ }
13071
+ let handle;
13072
+ try {
13073
+ handle = await open(file.path, "r");
13074
+ } catch (error) {
13075
+ return {
13076
+ truncated: false,
13077
+ failed: true,
13078
+ error: errorMessage(error)
13079
+ };
12843
13080
  }
12844
- const handle = await open(file.path, "r");
12845
13081
  const ops = [];
12846
13082
  let byteCursor = offset;
13083
+ let position = offset;
13084
+ let wireError;
12847
13085
  try {
12848
- let position = offset;
12849
13086
  let pending = EMPTY_BUFFER;
13087
+ let finishing = false;
12850
13088
  const chunk = Buffer.allocUnsafe(WIRE_READ_CHUNK_BYTES);
12851
13089
  while (position < size) {
12852
- if (this.disposed) return;
12853
- const { bytesRead } = await handle.read(chunk, 0, Math.min(chunk.length, size - position), position);
13090
+ if (this.disposed) return {
13091
+ truncated: false,
13092
+ failed: false
13093
+ };
13094
+ if (syncBudgetExhausted(budget)) {
13095
+ if (pending.length === 0) break;
13096
+ finishing = true;
13097
+ }
13098
+ let bytesRead;
13099
+ try {
13100
+ ({bytesRead} = await handle.read(chunk, 0, Math.min(chunk.length, size - position), position));
13101
+ } catch (error) {
13102
+ wireError = error;
13103
+ break;
13104
+ }
12854
13105
  if (bytesRead === 0) break;
13106
+ budget.bytesLeft -= bytesRead;
12855
13107
  const slice = chunk.subarray(0, bytesRead);
12856
13108
  position += bytesRead;
12857
13109
  let start = 0;
13110
+ let completedRecord = false;
12858
13111
  for (;;) {
12859
13112
  const nl = slice.indexOf(10, start);
12860
13113
  if (nl === -1) break;
@@ -12866,10 +13119,24 @@ var SearchIndexCore = class {
12866
13119
  turnState,
12867
13120
  stepState
12868
13121
  }));
13122
+ completedRecord = true;
12869
13123
  start = nl + 1;
12870
13124
  }
12871
13125
  pending = pending.length > 0 ? Buffer.concat([pending, slice.subarray(start)]) : Buffer.from(slice.subarray(start));
13126
+ if (finishing && completedRecord) break;
12872
13127
  if (ops.length >= WIRE_BATCH_OPS) {
13128
+ ops.push({
13129
+ op: "set",
13130
+ key: metaKey,
13131
+ value: fileMeta(byteCursor, turnState, stepState)
13132
+ });
13133
+ if (legacyKey !== null) {
13134
+ ops.push({
13135
+ op: "del",
13136
+ key: legacyKey
13137
+ });
13138
+ legacyKey = null;
13139
+ }
12873
13140
  await db.batch(ops);
12874
13141
  ops.length = 0;
12875
13142
  }
@@ -12877,17 +13144,24 @@ var SearchIndexCore = class {
12877
13144
  } finally {
12878
13145
  await handle.close();
12879
13146
  }
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);
13147
+ const truncated = position < size && syncBudgetExhausted(budget);
13148
+ if (byteCursor !== offset || legacyKey !== null) {
13149
+ ops.push({
13150
+ op: "set",
13151
+ key: metaKey,
13152
+ value: fileMeta(byteCursor, turnState, stepState)
13153
+ });
13154
+ if (legacyKey !== null) ops.push({
13155
+ op: "del",
13156
+ key: legacyKey
13157
+ });
13158
+ await db.batch(ops);
13159
+ }
13160
+ return {
13161
+ truncated,
13162
+ failed: wireError !== void 0,
13163
+ error: wireError !== void 0 ? errorMessage(wireError) : void 0
13164
+ };
12891
13165
  }
12892
13166
  collectWireLine(ops, summary, file, line, lineOffset, counters) {
12893
13167
  let { turnState, stepState } = counters;
@@ -12902,6 +13176,7 @@ var SearchIndexCore = class {
12902
13176
  const stepOrdinal = e.stepUuid !== void 0 ? stepState.byUuid[e.stepUuid] : void 0;
12903
13177
  const doc = {
12904
13178
  kind: "message",
13179
+ sessionIdentity: summary.sessionIdentity,
12905
13180
  sessionId: summary.id,
12906
13181
  workspaceId: summary.workspaceId,
12907
13182
  sessionTitle: summary.title ?? "",
@@ -13021,14 +13296,69 @@ var SearchIndexCore = class {
13021
13296
  const boundary = page.kind === "keyset" ? page.boundary : void 0;
13022
13297
  const matched = matchDocs(q, candidates, boundary, budget);
13023
13298
  incomplete ??= matched.incomplete;
13024
- const { pageRows, hasMore } = paginateRows(q, page, matched.rows);
13299
+ const index = this.readIndexView(serveDb, freshnessStale);
13300
+ const sources = /* @__PURE__ */ new Map();
13301
+ for (const row of matched.rows) {
13302
+ const id = row.value.sessionId;
13303
+ if (sources.has(id)) continue;
13304
+ if (Date.now() > budget.deadlineAt) {
13305
+ incomplete ??= "deadline";
13306
+ break;
13307
+ }
13308
+ const meta = serveDb.get(SESSION_META_PREFIX + id);
13309
+ sources.set(id, meta?.kind === "sessionMeta" ? meta : void 0);
13310
+ }
13311
+ const identities = /* @__PURE__ */ new Map();
13312
+ const visible = [];
13313
+ let deadlineTimer;
13314
+ const deadline = new Promise((resolve) => {
13315
+ deadlineTimer = setTimeout(() => resolve(null), Math.max(0, budget.deadlineAt - Date.now()));
13316
+ deadlineTimer.unref?.();
13317
+ });
13318
+ try {
13319
+ for (const row of matched.rows) {
13320
+ if (Date.now() > budget.deadlineAt) {
13321
+ incomplete ??= "deadline";
13322
+ break;
13323
+ }
13324
+ const meta = sources.get(row.value.sessionId);
13325
+ if (meta?.dir === void 0 || row.value.sessionIdentity === void 0 || meta.identity !== row.value.sessionIdentity) {
13326
+ freshnessStale = true;
13327
+ continue;
13328
+ }
13329
+ if (!identities.has(meta.dir)) try {
13330
+ const identity = await queryDirectoryIdentity(meta.dir, budget.deadlineAt, deadline);
13331
+ if (identity === null || Date.now() > budget.deadlineAt) {
13332
+ incomplete ??= "deadline";
13333
+ break;
13334
+ }
13335
+ identities.set(meta.dir, identity);
13336
+ } catch (error) {
13337
+ throw new GlobalSearchError("index_unavailable", `cannot verify search source: ${errorMessage(error)}`);
13338
+ }
13339
+ if (identities.get(meta.dir) === row.value.sessionIdentity) visible.push({
13340
+ ...row,
13341
+ value: {
13342
+ ...row.value,
13343
+ sessionTitle: meta.title ?? ""
13344
+ }
13345
+ });
13346
+ else freshnessStale = true;
13347
+ }
13348
+ } finally {
13349
+ if (deadlineTimer !== void 0) clearTimeout(deadlineTimer);
13350
+ }
13351
+ const { pageRows, hasMore } = paginateRows(q, page, visible);
13025
13352
  return {
13026
13353
  kind: "page",
13027
13354
  rows: pageRows,
13028
13355
  hasMore,
13029
13356
  incomplete,
13030
13357
  generation,
13031
- index: this.readIndexView(serveDb, freshnessStale)
13358
+ index: {
13359
+ ...index,
13360
+ freshnessStale: freshnessStale || this.db !== serveDb
13361
+ }
13032
13362
  };
13033
13363
  }
13034
13364
  async reindex() {
@@ -13042,16 +13372,16 @@ var SearchIndexCore = class {
13042
13372
  }
13043
13373
  this.openPromise = null;
13044
13374
  this.fullSyncDone = false;
13375
+ this.sessionSyncFailures.clear();
13376
+ this.sessionSyncSkips.clear();
13045
13377
  this.lockToken = void 0;
13046
- await rm(this.indexDir, {
13047
- recursive: true,
13048
- force: true
13049
- });
13378
+ 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
13379
  await this.ensureOpen();
13051
13380
  }
13052
13381
  lifecycleState() {
13053
13382
  if (this.disposed) return { state: this.db === null ? "stopped" : "closing" };
13054
13383
  const db = this.db;
13384
+ if (db !== null) this.noteMaintenanceDetail(db);
13055
13385
  if (db === null) {
13056
13386
  if (this.openPromise !== null) return { state: "opening" };
13057
13387
  if (this.openError !== null) return {
@@ -13063,10 +13393,23 @@ var SearchIndexCore = class {
13063
13393
  if (db.textIndexBuilding(TEXT_INDEX_NAME) || db.textIndexBuilding(TRI_INDEX_NAME)) return { state: "building" };
13064
13394
  return { state: "ready" };
13065
13395
  }
13396
+ maintenanceDetail(db) {
13397
+ const failure = db.lastCompactError ?? db.lastGenBuildError;
13398
+ return failure === null ? void 0 : errorMessage(failure);
13399
+ }
13400
+ noteMaintenanceDetail(db) {
13401
+ const detail = this.maintenanceDetail(db);
13402
+ if (detail === this.lastMaintenanceDetail) return;
13403
+ const previous = this.lastMaintenanceDetail;
13404
+ this.lastMaintenanceDetail = detail;
13405
+ if (detail !== void 0) this.log.warn("global search: index maintenance is failing; the WAL keeps growing until it recovers", { error: detail });
13406
+ else if (previous !== void 0) this.log.info("global search: index maintenance recovered");
13407
+ }
13066
13408
  async status() {
13067
13409
  await this.ensureOpen();
13068
13410
  if (this.db?.readOnly === true) await this.refresh();
13069
13411
  const stats = this.db?.get(STATS_KEY);
13412
+ const maintenance = this.db !== null ? this.maintenanceDetail(this.db) : void 0;
13070
13413
  return {
13071
13414
  sessions: stats?.kind === "stats" ? stats.sessions : 0,
13072
13415
  documents: stats?.kind === "stats" ? stats.documents : 0,
@@ -13074,7 +13417,7 @@ var SearchIndexCore = class {
13074
13417
  generation: this.generation,
13075
13418
  readOnly: this.db?.readOnly === true,
13076
13419
  lockToken: this.lockToken,
13077
- degraded: this.lastRefreshError?.message,
13420
+ degraded: this.lastRefreshError?.message ?? maintenance ?? (stats?.kind === "stats" ? stats.degraded : void 0),
13078
13421
  lifecycle: this.lifecycleState()
13079
13422
  };
13080
13423
  }
@@ -13087,7 +13430,7 @@ var SearchIndexCore = class {
13087
13430
  documents: stats?.kind === "stats" ? stats.documents : 0,
13088
13431
  readOnly: handle?.readOnly === true,
13089
13432
  freshnessStale: true,
13090
- degraded: this.lastRefreshError?.message,
13433
+ degraded: this.lastRefreshError?.message ?? (stats?.kind === "stats" ? stats.degraded : void 0),
13091
13434
  lockToken: this.lockToken
13092
13435
  };
13093
13436
  }
@@ -13101,7 +13444,7 @@ var SearchIndexCore = class {
13101
13444
  documents,
13102
13445
  readOnly: db.readOnly,
13103
13446
  freshnessStale,
13104
- degraded: this.lastRefreshError?.message,
13447
+ degraded: this.lastRefreshError?.message ?? (stats?.kind === "stats" ? stats.degraded : void 0),
13105
13448
  lockToken: this.lockToken
13106
13449
  };
13107
13450
  }
@@ -13137,9 +13480,6 @@ async function collectWireFiles(sessionDir) {
13137
13480
  function docKeyPrefix(sessionId, file) {
13138
13481
  return `${sessionId}/${file.agentId}/${file.source}:`;
13139
13482
  }
13140
- function isRebuildableCorruption(error) {
13141
- return error instanceof SyntaxError || error !== null && typeof error === "object" && error.name === "CorruptFrameError";
13142
- }
13143
13483
 
13144
13484
  //#endregion
13145
13485
  //#region ../../packages/kap-server/src/search/worker/entry.ts