@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.
- package/dist/main.mjs +88223 -77662
- package/dist/search-worker.mjs +436 -96
- package/dist-web/assets/{CodeBlockNode-DXrFlfJq.js → CodeBlockNode-BDSAqNOc.js} +2 -2
- package/dist-web/assets/{Tooltip-D-EUJqdY.js → Tooltip-BE4IWX2u.js} +1 -1
- package/dist-web/assets/{abnfDiagram-VRR7QNED-tXWm0yJO.js → abnfDiagram-VRR7QNED-CWbGy1cv.js} +1 -1
- package/dist-web/assets/{arc-Dcuc8NBZ.js → arc-DQe8dUYI.js} +1 -1
- package/dist-web/assets/{architectureDiagram-ZJ3FMSHR-BjFIGYfr.js → architectureDiagram-ZJ3FMSHR-BDOquUyr.js} +1 -1
- package/dist-web/assets/{blockDiagram-677ZJIJ3-BTYvQjTR.js → blockDiagram-677ZJIJ3-CJtbCzkv.js} +1 -1
- package/dist-web/assets/{c4Diagram-LMCZKHZV-C0xqgo03.js → c4Diagram-LMCZKHZV-t37hrhPN.js} +1 -1
- package/dist-web/assets/channel-AQC2UigP.js +1 -0
- package/dist-web/assets/{chunk-2Q5K7J3B-BBRTtiB5.js → chunk-2Q5K7J3B-DSH4W9W7.js} +1 -1
- package/dist-web/assets/{chunk-32BRIVSS-3l6XZxpp.js → chunk-32BRIVSS-DQSjSlNX.js} +1 -1
- package/dist-web/assets/{chunk-5VM5RSS4-A5rXJcEF.js → chunk-5VM5RSS4-g2RPEnPU.js} +1 -1
- package/dist-web/assets/{chunk-EX3LRPZG-CCB7x_Qg.js → chunk-EX3LRPZG-DdTW1EAS.js} +1 -1
- package/dist-web/assets/{chunk-JWPE2WC7-0N6Q_J0q.js → chunk-JWPE2WC7-BT01S0dy.js} +1 -1
- package/dist-web/assets/{chunk-MOJQB5TN-DOlN8qtA.js → chunk-MOJQB5TN-IWlQJyow.js} +1 -1
- package/dist-web/assets/{chunk-RYQCIY6F-KpXFcslK.js → chunk-RYQCIY6F-pV91xl4J.js} +1 -1
- package/dist-web/assets/{chunk-V7JOEXUC-CFknWZZI.js → chunk-V7JOEXUC-xr_3E2Wz.js} +1 -1
- package/dist-web/assets/{chunk-VR4S4FIN-gihGNGO1.js → chunk-VR4S4FIN-DAoPzKmc.js} +1 -1
- package/dist-web/assets/{chunk-XXDRQBXY-C3GGkCwU.js → chunk-XXDRQBXY-4Gs_c1bc.js} +1 -1
- package/dist-web/assets/classDiagram-OUVF2IWQ-9J31pWLC.js +1 -0
- package/dist-web/assets/classDiagram-v2-EOCWNBFH-9J31pWLC.js +1 -0
- package/dist-web/assets/{cose-bilkent-JH36ORCC-f33uYY00.js → cose-bilkent-JH36ORCC-DHYtc2Lg.js} +1 -1
- package/dist-web/assets/{cynefin-VYW2F7L2-DQGCJnlE.js → cynefin-VYW2F7L2-DZANU5FS.js} +1 -1
- package/dist-web/assets/{cynefinDiagram-TSTJHNR4-CHL_foL_.js → cynefinDiagram-TSTJHNR4-Cmq3GSLA.js} +1 -1
- package/dist-web/assets/{dagre-VKFMJZFB-EsZwpbfr.js → dagre-VKFMJZFB-BT-klNzv.js} +1 -1
- package/dist-web/assets/{diagram-FQU43EPY-Bk8FGDJs.js → diagram-FQU43EPY-DWnhmVxO.js} +1 -1
- package/dist-web/assets/{diagram-G47NLZAW-B_XI4_LC.js → diagram-G47NLZAW-sN3uAWXP.js} +1 -1
- package/dist-web/assets/{diagram-NH7WQ7WH-BQY-SQ3_.js → diagram-NH7WQ7WH-BgNStFDL.js} +1 -1
- package/dist-web/assets/{diagram-OA4YK3LP-CTorHnPK.js → diagram-OA4YK3LP-9eEe-Z3v.js} +1 -1
- package/dist-web/assets/{diagram-WEI45ONY-B3JjOxuA.js → diagram-WEI45ONY-aTjaDb2N.js} +1 -1
- package/dist-web/assets/{ebnfDiagram-CCIWWBDH-DPm9WU6R.js → ebnfDiagram-CCIWWBDH-R8xU167H.js} +1 -1
- package/dist-web/assets/{erDiagram-Q63AITRT-BF_M9qKN.js → erDiagram-Q63AITRT-CXEtYr4J.js} +1 -1
- package/dist-web/assets/{flowDiagram-23GEKE2U-jc1IFMCM.js → flowDiagram-23GEKE2U-BJmfiM29.js} +1 -1
- package/dist-web/assets/{ganttDiagram-NO4QXBWP-CCGkTOSq.js → ganttDiagram-NO4QXBWP-B7c5nduP.js} +1 -1
- package/dist-web/assets/{gitGraphDiagram-IHSO6WYX-EGCxWX3f.js → gitGraphDiagram-IHSO6WYX-DJsxIHAg.js} +1 -1
- package/dist-web/assets/index-Baap3_X9.js +962 -0
- package/dist-web/assets/{index-BAuXCnoo.js → index-CSaMMAzR.js} +1 -1
- package/dist-web/assets/{index-CWYttcZZ.js → index-DGpUsOcn.js} +1 -1
- package/dist-web/assets/{index-DUuyqWli.js → index-Dc9ox5VC.js} +2 -2
- package/dist-web/assets/index-dAouBbjn.css +32 -0
- package/dist-web/assets/{index10-BDy7C0f0.js → index10-rPjEEum5.js} +1 -1
- package/dist-web/assets/{index11-CyIrSiYK.js → index11-B7H8oxtu.js} +1 -1
- package/dist-web/assets/{index5-KI0z5ksp.js → index5-tPh937rm.js} +1 -1
- package/dist-web/assets/{index6-C-4gEeMt.js → index6-D8sB43ws.js} +1 -1
- package/dist-web/assets/{index7-Dx7US8OE.js → index7-DRyeTVHt.js} +1 -1
- package/dist-web/assets/{index8-cNFl9TtI.js → index8-CpVaCNRn.js} +1 -1
- package/dist-web/assets/{infoDiagram-FWYZ7A6U-2vAZupw5.js → infoDiagram-FWYZ7A6U-Ds-biZ51.js} +1 -1
- package/dist-web/assets/{ishikawaDiagram-FXEZZL3T-D62pnRj0.js → ishikawaDiagram-FXEZZL3T-D_WjGjIa.js} +1 -1
- package/dist-web/assets/{journeyDiagram-5HDEW3XC-lRqymxxK.js → journeyDiagram-5HDEW3XC-Df5gewaW.js} +1 -1
- package/dist-web/assets/{kanban-definition-HUTT4EX6-3pkuIM1M.js → kanban-definition-HUTT4EX6-Cds-KoSu.js} +1 -1
- package/dist-web/assets/{linear-1g72tnQ_.js → linear-CxP8zfSg.js} +1 -1
- package/dist-web/assets/{mermaid.core-Dxfcv_J5.js → mermaid.core-CCxZmhet.js} +4 -4
- package/dist-web/assets/{mindmap-definition-LN4V7U3C-DqraBb0o.js → mindmap-definition-LN4V7U3C-BYqiMfi6.js} +1 -1
- package/dist-web/assets/{pegDiagram-2B236MQR-BMHCAO0T.js → pegDiagram-2B236MQR-Dy4Ywp29.js} +1 -1
- package/dist-web/assets/{pieDiagram-ENE6RG2P-jm92KpVe.js → pieDiagram-ENE6RG2P-CCwKCWcY.js} +1 -1
- package/dist-web/assets/{quadrantDiagram-ABIIQ3AL-BvuCpWW3.js → quadrantDiagram-ABIIQ3AL-D-6UvOrh.js} +1 -1
- package/dist-web/assets/{railroadDiagram-RFXS5EU6-DmPKWKL9.js → railroadDiagram-RFXS5EU6-dVR2ADod.js} +1 -1
- package/dist-web/assets/{requirementDiagram-TGXJPOKE-Ddeu6LBZ.js → requirementDiagram-TGXJPOKE--ZrFbbKb.js} +1 -1
- package/dist-web/assets/rive-BTwUD7iz.js +1 -0
- package/dist-web/assets/rive_fallback-DXpHVJvQ.js +1 -0
- package/dist-web/assets/{sankeyDiagram-HTMAVEWB-CPg2z6Bh.js → sankeyDiagram-HTMAVEWB-gGCfZA-t.js} +1 -1
- package/dist-web/assets/{sequenceDiagram-DBY2YBRQ-VogAZWGu.js → sequenceDiagram-DBY2YBRQ-CgG59Dk9.js} +1 -1
- package/dist-web/assets/{sizeCapture-X5ZJPWSS-BDqVbn7D.js → sizeCapture-X5ZJPWSS-B4Fr8K_f.js} +1 -1
- package/dist-web/assets/{stateDiagram-2N3HPSRC-w14d3Aj4.js → stateDiagram-2N3HPSRC-BkQ1_Aee.js} +1 -1
- package/dist-web/assets/stateDiagram-v2-6OUMAXLB-DzeZEC_o.js +1 -0
- package/dist-web/assets/{swimlanes-5IMT3BWC-CJtgPYav.js → swimlanes-5IMT3BWC-BcJFT27N.js} +2 -2
- package/dist-web/assets/swimlanesDiagram-G3AALYLV-B8Mt2UUm.js +8 -0
- package/dist-web/assets/{timeline-definition-FHXFAJF6-Bl9qf2WR.js → timeline-definition-FHXFAJF6-r0QD09OO.js} +1 -1
- package/dist-web/assets/{vennDiagram-L72KCM5P-As6yPU6_.js → vennDiagram-L72KCM5P-Copqf7LE.js} +1 -1
- package/dist-web/assets/{vue.runtime.esm-bundler-sFotuYAs.js → vue.runtime.esm-bundler-C9hhbb_t.js} +1 -1
- package/dist-web/assets/{wardleyDiagram-EHGQE667-BLdfM1g3.js → wardleyDiagram-EHGQE667-nNOP_Z8t.js} +1 -1
- package/dist-web/assets/{xychartDiagram-FW5EYKEG-P0NbIN2z.js → xychartDiagram-FW5EYKEG-alLFyDZG.js} +1 -1
- package/dist-web/index.html +2 -2
- package/native/darwin/prebuilds/darwin-arm64/{darwin-modifiers.node → darwin-platform.node} +0 -0
- package/native/darwin/prebuilds/darwin-x64/{darwin-modifiers.node → darwin-platform.node} +0 -0
- package/native/linux/prebuilds/linux-arm64/linux-platform-x11.node +0 -0
- package/native/linux/prebuilds/linux-x64/linux-platform-x11.node +0 -0
- package/native/win32/prebuilds/win32-arm64/win32-platform.node +0 -0
- package/native/win32/prebuilds/win32-x64/win32-platform.node +0 -0
- package/package.json +5 -5
- package/dist-web/assets/channel-BWBJkLRL.js +0 -1
- package/dist-web/assets/classDiagram-OUVF2IWQ-CaG1i1dL.js +0 -1
- package/dist-web/assets/classDiagram-v2-EOCWNBFH-CaG1i1dL.js +0 -1
- package/dist-web/assets/index-C8RkgE6U.css +0 -32
- package/dist-web/assets/index-HU0LCM-X.js +0 -828
- package/dist-web/assets/k3_doodle1-27EZ2HSw.riv +0 -0
- package/dist-web/assets/kimi_avatar_default-srYjF2HV.riv +0 -0
- package/dist-web/assets/rive-BxcgqsjB.js +0 -1
- package/dist-web/assets/rive-CxG7kGQi.wasm +0 -0
- package/dist-web/assets/rive_fallback-ByshBW-N.js +0 -1
- package/dist-web/assets/rive_fallback-l90fUeAW.wasm +0 -0
- package/dist-web/assets/stateDiagram-v2-6OUMAXLB-Z4v6nvtB.js +0 -1
- package/dist-web/assets/swimlanesDiagram-G3AALYLV-Cbf2NUtt.js +0 -8
- package/native/win32/prebuilds/win32-arm64/win32-console-mode.node +0 -0
- package/native/win32/prebuilds/win32-x64/win32-console-mode.node +0 -0
package/dist/search-worker.mjs
CHANGED
|
@@ -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,
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
11577
|
-
|
|
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 (
|
|
12531
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
12673
|
-
|
|
12674
|
-
|
|
12675
|
-
|
|
12676
|
-
|
|
12677
|
-
|
|
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
|
-
|
|
12755
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
12881
|
-
|
|
12882
|
-
|
|
12883
|
-
|
|
12884
|
-
|
|
12885
|
-
|
|
12886
|
-
|
|
12887
|
-
|
|
12888
|
-
|
|
12889
|
-
|
|
12890
|
-
|
|
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
|
|
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:
|
|
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
|
|
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
|