scream-code 0.18.8 → 0.19.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.
- package/README.md +26 -5
- package/dist/{app-Db7f8Kja.mjs → app-CMbpupvJ.mjs} +5300 -4861
- package/dist/bot-ui/assets/index-Bhcj384G.css +1 -0
- package/dist/bot-ui/assets/index-C0tnq2Bw.js +455 -0
- package/dist/bot-ui/index.html +13 -0
- package/dist/{dispatch-zsFzFFoY.mjs → dispatch-Brm4KZ8f.mjs} +1 -1
- package/dist/{dispatch-BPfUTccZ.mjs → dispatch-NoArU5dN.mjs} +3892 -80
- package/dist/main.mjs +1 -1
- package/dist/{markit-CVQeFGFG.mjs → markit-CV3BXOV_.mjs} +20 -13
- package/dist/{src-DA34cXSv.mjs → src-CsT5xa84.mjs} +1 -1
- package/dist/{src-ClB_Hzp9.mjs → src-Cu3sUIJK.mjs} +245 -30
- package/dist/{text-input-dialog-CFRUSCi6.mjs → text-input-dialog-BmdLwvYc.mjs} +1 -1
- package/dist/{text-input-dialog-DHDsHCqR.mjs → text-input-dialog-CCDiUbcL.mjs} +40 -10
- package/package.json +27 -18
- package/dist/public/assets/astro-DAp21e1Q.js +0 -2
- package/dist/public/assets/astro-DAp21e1Q.js.map +0 -1
- package/dist/public/assets/bash-Yzrsuije.js +0 -2
- package/dist/public/assets/bash-Yzrsuije.js.map +0 -1
- package/dist/public/assets/c-BIGW1oBm.js +0 -2
- package/dist/public/assets/c-BIGW1oBm.js.map +0 -1
- package/dist/public/assets/cpp-BRuaLJcg.js +0 -2
- package/dist/public/assets/cpp-BRuaLJcg.js.map +0 -1
- package/dist/public/assets/csharp-COcwbKMJ.js +0 -2
- package/dist/public/assets/csharp-COcwbKMJ.js.map +0 -1
- package/dist/public/assets/css-DEUVOdPa.js +0 -2
- package/dist/public/assets/css-DEUVOdPa.js.map +0 -1
- package/dist/public/assets/dart-CF10PKvl.js +0 -2
- package/dist/public/assets/dart-CF10PKvl.js.map +0 -1
- package/dist/public/assets/docker-BcOcwvcX.js +0 -2
- package/dist/public/assets/docker-BcOcwvcX.js.map +0 -1
- package/dist/public/assets/github-dark-DHJKELXO.js +0 -2
- package/dist/public/assets/github-dark-DHJKELXO.js.map +0 -1
- package/dist/public/assets/github-light-DAi9KRSo.js +0 -2
- package/dist/public/assets/github-light-DAi9KRSo.js.map +0 -1
- package/dist/public/assets/go-CxLEBnE3.js +0 -2
- package/dist/public/assets/go-CxLEBnE3.js.map +0 -1
- package/dist/public/assets/graphql-ChdNCCLP.js +0 -2
- package/dist/public/assets/graphql-ChdNCCLP.js.map +0 -1
- package/dist/public/assets/html-CNxCFRDw.js +0 -2
- package/dist/public/assets/html-CNxCFRDw.js.map +0 -1
- package/dist/public/assets/index-BIs_1Cpx.js +0 -240
- package/dist/public/assets/index-BIs_1Cpx.js.map +0 -1
- package/dist/public/assets/index-bnhnZew2.css +0 -1
- package/dist/public/assets/ini-BEwlwnbL.js +0 -2
- package/dist/public/assets/ini-BEwlwnbL.js.map +0 -1
- package/dist/public/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
- package/dist/public/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
- package/dist/public/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
- package/dist/public/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
- package/dist/public/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
- package/dist/public/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
- package/dist/public/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
- package/dist/public/assets/java-CylS5w8V.js +0 -2
- package/dist/public/assets/java-CylS5w8V.js.map +0 -1
- package/dist/public/assets/javascript-wDzz0qaB.js +0 -2
- package/dist/public/assets/javascript-wDzz0qaB.js.map +0 -1
- package/dist/public/assets/jetbrains-mono-cyrillic-wght-normal-D73BlboJ.woff2 +0 -0
- package/dist/public/assets/jetbrains-mono-greek-wght-normal-Bw9x6K1M.woff2 +0 -0
- package/dist/public/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2 +0 -0
- package/dist/public/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2 +0 -0
- package/dist/public/assets/jetbrains-mono-vietnamese-wght-normal-Bt-aOZkq.woff2 +0 -0
- package/dist/public/assets/json-Cp-IABpG.js +0 -2
- package/dist/public/assets/json-Cp-IABpG.js.map +0 -1
- package/dist/public/assets/jsx-g9-lgVsj.js +0 -2
- package/dist/public/assets/jsx-g9-lgVsj.js.map +0 -1
- package/dist/public/assets/kotlin-BdnUsdx6.js +0 -2
- package/dist/public/assets/kotlin-BdnUsdx6.js.map +0 -1
- package/dist/public/assets/logo-v2-ChmPAIbI.svg +0 -1
- package/dist/public/assets/lua-BaeVxFsk.js +0 -2
- package/dist/public/assets/lua-BaeVxFsk.js.map +0 -1
- package/dist/public/assets/make-CHLpvVh8.js +0 -2
- package/dist/public/assets/make-CHLpvVh8.js.map +0 -1
- package/dist/public/assets/markdown-Cvjx9yec.js +0 -2
- package/dist/public/assets/markdown-Cvjx9yec.js.map +0 -1
- package/dist/public/assets/perl-CptaX86O.js +0 -2
- package/dist/public/assets/perl-CptaX86O.js.map +0 -1
- package/dist/public/assets/postcss-DnzrXTVv.js +0 -2
- package/dist/public/assets/postcss-DnzrXTVv.js.map +0 -1
- package/dist/public/assets/protobuf-C7zT0LnQ.js +0 -2
- package/dist/public/assets/protobuf-C7zT0LnQ.js.map +0 -1
- package/dist/public/assets/python-B6aJPvgy.js +0 -2
- package/dist/public/assets/python-B6aJPvgy.js.map +0 -1
- package/dist/public/assets/r-Dspwwk_N.js +0 -2
- package/dist/public/assets/r-Dspwwk_N.js.map +0 -1
- package/dist/public/assets/ruby-CcrC3V9I.js +0 -2
- package/dist/public/assets/ruby-CcrC3V9I.js.map +0 -1
- package/dist/public/assets/rust-B1yitclQ.js +0 -2
- package/dist/public/assets/rust-B1yitclQ.js.map +0 -1
- package/dist/public/assets/scala-C151Ov-r.js +0 -2
- package/dist/public/assets/scala-C151Ov-r.js.map +0 -1
- package/dist/public/assets/sql-BLtJtn59.js +0 -2
- package/dist/public/assets/sql-BLtJtn59.js.map +0 -1
- package/dist/public/assets/svelte-U4fHntn6.js +0 -2
- package/dist/public/assets/svelte-U4fHntn6.js.map +0 -1
- package/dist/public/assets/swift-D82vCrfD.js +0 -2
- package/dist/public/assets/swift-D82vCrfD.js.map +0 -1
- package/dist/public/assets/toml-vGWfd6FD.js +0 -2
- package/dist/public/assets/toml-vGWfd6FD.js.map +0 -1
- package/dist/public/assets/tsx-COt5Ahok.js +0 -2
- package/dist/public/assets/tsx-COt5Ahok.js.map +0 -1
- package/dist/public/assets/typescript-BPQ3VLAy.js +0 -2
- package/dist/public/assets/typescript-BPQ3VLAy.js.map +0 -1
- package/dist/public/assets/xml-sdJ4AIDG.js +0 -2
- package/dist/public/assets/xml-sdJ4AIDG.js.map +0 -1
- package/dist/public/assets/yaml-Buea-lGh.js +0 -2
- package/dist/public/assets/yaml-Buea-lGh.js.map +0 -1
- package/dist/public/gateway.html +0 -156
- package/dist/public/icon.ico +0 -0
- package/dist/public/index.html +0 -29
package/dist/main.mjs
CHANGED
|
@@ -6,7 +6,7 @@ const __dirname = __cjsShimDirname(__filename);
|
|
|
6
6
|
import "./suppress-sqlite-warning-C2VB0doZ.mjs";
|
|
7
7
|
//#region src/main.ts
|
|
8
8
|
try {
|
|
9
|
-
(await import("./app-
|
|
9
|
+
(await import("./app-CMbpupvJ.mjs")).main();
|
|
10
10
|
} catch (error) {
|
|
11
11
|
process.stderr.write(`${error instanceof Error ? error.stack ?? error.message : String(error)}\n`);
|
|
12
12
|
process.exit(1);
|
|
@@ -8,6 +8,13 @@ import { n as require_base64_js, t as XMLParser } from "./XMLParser-DFAnGqkc.mjs
|
|
|
8
8
|
import * as fs$1 from "node:fs/promises";
|
|
9
9
|
import * as path$1 from "node:path";
|
|
10
10
|
import { createRequire } from "module";
|
|
11
|
+
//#region ../../packages/knowledge/src/markit/mupdf-quiet.ts
|
|
12
|
+
const globalScope = globalThis;
|
|
13
|
+
const moduleConfig = globalScope.$libmupdf_wasm_Module ?? {};
|
|
14
|
+
moduleConfig.print ??= () => {};
|
|
15
|
+
moduleConfig.printErr ??= () => {};
|
|
16
|
+
globalScope.$libmupdf_wasm_Module = moduleConfig;
|
|
17
|
+
//#endregion
|
|
11
18
|
//#region ../../node_modules/.pnpm/underscore@1.13.8/node_modules/underscore/modules/_setup.js
|
|
12
19
|
var VERSION, root, ArrayProto, ObjProto, SymbolProto, push, slice, toString, hasOwnProperty, supportsArrayBuffer, supportsDataView, nativeIsArray, nativeKeys, nativeCreate, nativeIsView, _isNaN, _isFinite, hasEnumBug, nonEnumerableProps, MAX_ARRAY_INDEX;
|
|
13
20
|
var init__setup = __esmMin((() => {
|
|
@@ -49718,7 +49725,7 @@ var require_turndown_plugin_gfm_cjs = /* @__PURE__ */ __commonJSMin(((exports) =
|
|
|
49718
49725
|
exports.gfm = gfm;
|
|
49719
49726
|
}));
|
|
49720
49727
|
//#endregion
|
|
49721
|
-
//#region ../../packages/
|
|
49728
|
+
//#region ../../packages/knowledge/src/markit/utils/turndown.ts
|
|
49722
49729
|
var import_lib = /* @__PURE__ */ __toESM(require_lib$1(), 1);
|
|
49723
49730
|
var import_turndown_cjs = /* @__PURE__ */ __toESM(require_turndown_cjs(), 1);
|
|
49724
49731
|
var import_turndown_plugin_gfm_cjs = require_turndown_plugin_gfm_cjs();
|
|
@@ -49792,7 +49799,7 @@ function normalizeTablesHtml(html) {
|
|
|
49792
49799
|
return result;
|
|
49793
49800
|
}
|
|
49794
49801
|
//#endregion
|
|
49795
|
-
//#region ../../packages/
|
|
49802
|
+
//#region ../../packages/knowledge/src/markit/converters/docx.ts
|
|
49796
49803
|
const EXTENSIONS$4 = [".docx"];
|
|
49797
49804
|
const MIMETYPES$4 = ["application/vnd.openxmlformats-officedocument.wordprocessingml.document"];
|
|
49798
49805
|
var DocxConverter = class {
|
|
@@ -50265,7 +50272,7 @@ function unzipSync(data, opts) {
|
|
|
50265
50272
|
return files;
|
|
50266
50273
|
}
|
|
50267
50274
|
//#endregion
|
|
50268
|
-
//#region ../../packages/
|
|
50275
|
+
//#region ../../packages/knowledge/src/markit/utils/zip.ts
|
|
50269
50276
|
/**
|
|
50270
50277
|
* Minimal ZIP reading for the markit document converters (docx/pptx/xlsx/
|
|
50271
50278
|
* epub are all ZIP containers). Read-only: stored + deflate entries via
|
|
@@ -50285,7 +50292,7 @@ function unzip(bytes) {
|
|
|
50285
50292
|
return unzipSync(bytes);
|
|
50286
50293
|
}
|
|
50287
50294
|
//#endregion
|
|
50288
|
-
//#region ../../packages/
|
|
50295
|
+
//#region ../../packages/knowledge/src/markit/converters/epub.ts
|
|
50289
50296
|
const EXTENSIONS$3 = [".epub"];
|
|
50290
50297
|
const MIMETYPES$3 = [
|
|
50291
50298
|
"application/epub",
|
|
@@ -50365,7 +50372,7 @@ var EpubConverter = class {
|
|
|
50365
50372
|
}
|
|
50366
50373
|
};
|
|
50367
50374
|
//#endregion
|
|
50368
|
-
//#region ../../packages/
|
|
50375
|
+
//#region ../../packages/knowledge/src/markit/converters/pdf/columns.ts
|
|
50369
50376
|
/**
|
|
50370
50377
|
* Minimum gap as a fraction of the total text width to consider a column
|
|
50371
50378
|
* boundary. A two-column layout typically has ~50% gap; we use a lower
|
|
@@ -50434,7 +50441,7 @@ function detectColumns(textBoxes) {
|
|
|
50434
50441
|
};
|
|
50435
50442
|
}
|
|
50436
50443
|
//#endregion
|
|
50437
|
-
//#region ../../packages/
|
|
50444
|
+
//#region ../../packages/knowledge/src/markit/converters/pdf/extract.ts
|
|
50438
50445
|
let mupdfModule;
|
|
50439
50446
|
async function loadMupdf() {
|
|
50440
50447
|
if (!mupdfModule) mupdfModule = await import("mupdf");
|
|
@@ -50936,7 +50943,7 @@ async function extractPages(input) {
|
|
|
50936
50943
|
return pages;
|
|
50937
50944
|
}
|
|
50938
50945
|
//#endregion
|
|
50939
|
-
//#region ../../packages/
|
|
50946
|
+
//#region ../../packages/knowledge/src/markit/converters/pdf/grid.ts
|
|
50940
50947
|
function castRaysForTextBox(textBox, segments) {
|
|
50941
50948
|
const cx = (textBox.bounds.left + textBox.bounds.right) / 2;
|
|
50942
50949
|
const cy = (textBox.bounds.top + textBox.bounds.bottom) / 2;
|
|
@@ -51584,7 +51591,7 @@ function resolveTableGrids(pageNumber, textBoxes, segments) {
|
|
|
51584
51591
|
};
|
|
51585
51592
|
}
|
|
51586
51593
|
//#endregion
|
|
51587
|
-
//#region ../../packages/
|
|
51594
|
+
//#region ../../packages/knowledge/src/markit/converters/pdf/headers.ts
|
|
51588
51595
|
/** Minimum number of pages to enable header/footer detection. */
|
|
51589
51596
|
const MIN_PAGES = 5;
|
|
51590
51597
|
/** Minimum Y position for top zone (from bottom of page in PDF coords). */
|
|
@@ -51656,7 +51663,7 @@ function stripHeadersFooters(pages) {
|
|
|
51656
51663
|
});
|
|
51657
51664
|
}
|
|
51658
51665
|
//#endregion
|
|
51659
|
-
//#region ../../packages/
|
|
51666
|
+
//#region ../../packages/knowledge/src/markit/converters/pdf/render.ts
|
|
51660
51667
|
/** Convert full-width ASCII characters (A→A, !→! etc.) to normal ASCII. */
|
|
51661
51668
|
function normalizeFullWidthAscii(text) {
|
|
51662
51669
|
return text.replace(/[!-~]/g, (ch) => String.fromCharCode(ch.charCodeAt(0) - 65248));
|
|
@@ -52051,7 +52058,7 @@ function renderPageContent(freeTextBoxes, tables, imageBlocks = [], allTextBoxes
|
|
|
52051
52058
|
return normalizeDetachedFirstColumnTables(mergeParagraphWraps(mergeConsecutiveHeadings(removePageNumbers(blocks), bodyFS), bodyFS)).map((b) => b.content).join("\n\n").trim();
|
|
52052
52059
|
}
|
|
52053
52060
|
//#endregion
|
|
52054
|
-
//#region ../../packages/
|
|
52061
|
+
//#region ../../packages/knowledge/src/markit/converters/pdf/index.ts
|
|
52055
52062
|
/**
|
|
52056
52063
|
* PDF to Markdown converter.
|
|
52057
52064
|
*
|
|
@@ -52145,7 +52152,7 @@ var PdfConverter = class {
|
|
|
52145
52152
|
}
|
|
52146
52153
|
};
|
|
52147
52154
|
//#endregion
|
|
52148
|
-
//#region ../../packages/
|
|
52155
|
+
//#region ../../packages/knowledge/src/markit/converters/pptx.ts
|
|
52149
52156
|
const EXTENSIONS$1 = [".pptx"];
|
|
52150
52157
|
const MIMETYPES$1 = ["application/vnd.openxmlformats-officedocument.presentationml.presentation"];
|
|
52151
52158
|
var PptxConverter = class {
|
|
@@ -52331,7 +52338,7 @@ function toList(val) {
|
|
|
52331
52338
|
return Array.isArray(val) ? val : [val];
|
|
52332
52339
|
}
|
|
52333
52340
|
//#endregion
|
|
52334
|
-
//#region ../../packages/
|
|
52341
|
+
//#region ../../packages/knowledge/src/markit/converters/xlsx.ts
|
|
52335
52342
|
const EXTENSIONS = [".xlsx"];
|
|
52336
52343
|
const MIMETYPES = ["application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"];
|
|
52337
52344
|
var XlsxConverter = class {
|
|
@@ -52420,7 +52427,7 @@ function toArray(val) {
|
|
|
52420
52427
|
return Array.isArray(val) ? val : [val];
|
|
52421
52428
|
}
|
|
52422
52429
|
//#endregion
|
|
52423
|
-
//#region ../../packages/
|
|
52430
|
+
//#region ../../packages/knowledge/src/markit/registry.ts
|
|
52424
52431
|
/**
|
|
52425
52432
|
* In-house document → markdown engine.
|
|
52426
52433
|
*
|
|
@@ -3,5 +3,5 @@ import { fileURLToPath as __cjsShimFileURLToPath } from 'node:url';
|
|
|
3
3
|
import { dirname as __cjsShimDirname } from 'node:path';
|
|
4
4
|
const __filename = __cjsShimFileURLToPath(import.meta.url);
|
|
5
5
|
const __dirname = __cjsShimDirname(__filename);
|
|
6
|
-
import { n as multiSearchWithTrace } from "./src-
|
|
6
|
+
import { n as multiSearchWithTrace } from "./src-Cu3sUIJK.mjs";
|
|
7
7
|
export { multiSearchWithTrace };
|
|
@@ -10,14 +10,14 @@ import Qr, { EventEmitter } from "events";
|
|
|
10
10
|
import js, { dirname, parse } from "path";
|
|
11
11
|
import { Buffer as Buffer$1 } from "buffer";
|
|
12
12
|
import Cs from "node:stream";
|
|
13
|
+
import * as Ps from "zlib";
|
|
14
|
+
import en from "zlib";
|
|
13
15
|
import Kt, { createReadStream, existsSync, mkdirSync, readFileSync, rmSync, statSync } from "node:fs";
|
|
14
16
|
import path, { basename, join, posix, resolve, win32 } from "node:path";
|
|
15
17
|
import { DatabaseSync } from "node:sqlite";
|
|
16
18
|
import { EventEmitter as EventEmitter$1 } from "node:events";
|
|
17
19
|
import { StringDecoder } from "node:string_decoder";
|
|
18
20
|
import zi from "assert";
|
|
19
|
-
import * as Ps from "zlib";
|
|
20
|
-
import en from "zlib";
|
|
21
21
|
import co from "node:assert";
|
|
22
22
|
//#region ../../node_modules/.pnpm/pathe@2.0.3/node_modules/pathe/dist/shared/pathe.M-eThtNZ.mjs
|
|
23
23
|
const _DRIVE_LETTER_START_RE = /^[A-Za-z]:\//;
|
|
@@ -218,7 +218,7 @@ function computeRelevanceScore(memo, query, usageCount = 0, currentProjectDir, p
|
|
|
218
218
|
const ti = intent?.temporalBias ?? .3;
|
|
219
219
|
const fi = intent?.factualBias ?? .6;
|
|
220
220
|
const factors = {
|
|
221
|
-
keywordOverlap:
|
|
221
|
+
keywordOverlap: computeKeywordOverlap(memo, query),
|
|
222
222
|
recency: computeRecency(memo.recordedAt),
|
|
223
223
|
usageBoost: Math.min(.3, usageCount * .1),
|
|
224
224
|
projectBoost: computeProjectBoost(memo.projectDir, currentProjectDir),
|
|
@@ -416,7 +416,21 @@ function extractKeywords(text) {
|
|
|
416
416
|
return [...new Set(tokens)];
|
|
417
417
|
}
|
|
418
418
|
function computeKeywordSimilarity(memo, query) {
|
|
419
|
-
|
|
419
|
+
return jaccardSimilarity(keywordText(memo), query);
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* Ranking variant: the distilled conclusion and the note are separate
|
|
423
|
+
* retrieval surfaces. Score each field on its own and keep the best match —
|
|
424
|
+
* a short conclusion would otherwise be diluted by the long base text.
|
|
425
|
+
*/
|
|
426
|
+
function computeKeywordOverlap(memo, query) {
|
|
427
|
+
return Math.max(jaccardSimilarity(keywordText(memo), query), jaccardSimilarity(memo.distilled ?? "", query), jaccardSimilarity(memo.note ?? "", query));
|
|
428
|
+
}
|
|
429
|
+
function keywordText(memo) {
|
|
430
|
+
return `${memo.userNeed} ${memo.approach} ${memo.whatFailed} ${memo.whatWorked}`;
|
|
431
|
+
}
|
|
432
|
+
function jaccardSimilarity(memoText, query) {
|
|
433
|
+
const memoWords = extractKeywords(memoText);
|
|
420
434
|
const queryWords = extractKeywords(query);
|
|
421
435
|
if (memoWords.length === 0 || queryWords.length === 0) return 0;
|
|
422
436
|
const intersection = memoWords.filter((w) => queryWords.includes(w)).length;
|
|
@@ -469,6 +483,10 @@ function createMemoryMemo(partial) {
|
|
|
469
483
|
outcome: partial.outcome,
|
|
470
484
|
whatFailed: partial.whatFailed,
|
|
471
485
|
whatWorked: partial.whatWorked,
|
|
486
|
+
distilled: partial.distilled,
|
|
487
|
+
origin: partial.origin ?? "session",
|
|
488
|
+
distilledFrom: partial.distilledFrom,
|
|
489
|
+
distilledAt: partial.distilledAt,
|
|
472
490
|
extractionSource: partial.extractionSource,
|
|
473
491
|
recordedAt: partial.recordedAt ?? Date.now(),
|
|
474
492
|
projectDir: partial.projectDir ?? "",
|
|
@@ -490,6 +508,10 @@ function toSummary(memo) {
|
|
|
490
508
|
outcome: memo.outcome,
|
|
491
509
|
whatFailed: memo.whatFailed,
|
|
492
510
|
whatWorked: memo.whatWorked,
|
|
511
|
+
distilled: memo.distilled,
|
|
512
|
+
origin: memo.origin,
|
|
513
|
+
distilledFrom: memo.distilledFrom,
|
|
514
|
+
distilledAt: memo.distilledAt,
|
|
493
515
|
extractionSource: memo.extractionSource,
|
|
494
516
|
recordedAt: memo.recordedAt,
|
|
495
517
|
projectDir: memo.projectDir,
|
|
@@ -3853,7 +3875,7 @@ async function ensureEmbeddingModelFiles(cacheDir, options) {
|
|
|
3853
3875
|
const message = error instanceof Error ? error.message : String(error);
|
|
3854
3876
|
throw new Error(`${message} (received ${received} of ${announcedTotal ?? "?"} bytes): ${url}`, { cause: error });
|
|
3855
3877
|
}
|
|
3856
|
-
if (chunk.done) break;
|
|
3878
|
+
if (chunk.done || chunk.value === void 0) break;
|
|
3857
3879
|
const bytes = chunk.value;
|
|
3858
3880
|
armIdleTimer();
|
|
3859
3881
|
await handle.write(bytes);
|
|
@@ -4113,7 +4135,19 @@ var MemoryMemoStore = class MemoryMemoStore {
|
|
|
4113
4135
|
JOIN memos_fts f ON m.rowid = f.rowid
|
|
4114
4136
|
WHERE f.memos_fts MATCH ? AND (m.project_dir = ? OR m.project_dir = '')
|
|
4115
4137
|
ORDER BY m.recorded_at DESC LIMIT ?`);
|
|
4116
|
-
|
|
4138
|
+
const rows = projectDir === void 0 ? stmt.all(ftsQuery, limit) : stmt.all(ftsQuery, projectDir, limit);
|
|
4139
|
+
const like = likePattern(query);
|
|
4140
|
+
const remaining = Math.max(0, limit - rows.length);
|
|
4141
|
+
const extraRows = remaining === 0 ? [] : projectDir === void 0 ? this.db.prepare("SELECT * FROM memos WHERE distilled LIKE ? ESCAPE '\\' OR note LIKE ? ESCAPE '\\' ORDER BY recorded_at DESC LIMIT ?").all(like, like, remaining) : this.db.prepare("SELECT * FROM memos WHERE (distilled LIKE ? ESCAPE '\\' OR note LIKE ? ESCAPE '\\') AND (project_dir = ? OR project_dir = '') ORDER BY recorded_at DESC LIMIT ?").all(like, like, projectDir, remaining);
|
|
4142
|
+
const seen = new Set(rows.map((row) => String(row["id"])));
|
|
4143
|
+
const merged = rows.map(rowToMemo);
|
|
4144
|
+
for (const row of extraRows) {
|
|
4145
|
+
const id = String(row["id"]);
|
|
4146
|
+
if (seen.has(id)) continue;
|
|
4147
|
+
seen.add(id);
|
|
4148
|
+
merged.push(rowToMemo(row));
|
|
4149
|
+
}
|
|
4150
|
+
return merged;
|
|
4117
4151
|
}
|
|
4118
4152
|
/** List memos with optional full-text search and pagination. */
|
|
4119
4153
|
async list(options) {
|
|
@@ -4255,7 +4289,12 @@ var MemoryMemoStore = class MemoryMemoStore {
|
|
|
4255
4289
|
extraction_source TEXT NOT NULL CHECK(extraction_source IN ('compaction', 'exit', 'manual')),
|
|
4256
4290
|
recorded_at INTEGER NOT NULL,
|
|
4257
4291
|
project_dir TEXT NOT NULL DEFAULT '',
|
|
4258
|
-
tags TEXT NOT NULL DEFAULT '[]'
|
|
4292
|
+
tags TEXT NOT NULL DEFAULT '[]',
|
|
4293
|
+
note TEXT,
|
|
4294
|
+
distilled TEXT NOT NULL DEFAULT '',
|
|
4295
|
+
origin TEXT NOT NULL DEFAULT 'session',
|
|
4296
|
+
distilled_from TEXT,
|
|
4297
|
+
distilled_at INTEGER
|
|
4259
4298
|
);
|
|
4260
4299
|
|
|
4261
4300
|
CREATE INDEX IF NOT EXISTS idx_memos_project_dir ON memos(project_dir);
|
|
@@ -4285,6 +4324,14 @@ var MemoryMemoStore = class MemoryMemoStore {
|
|
|
4285
4324
|
const info = this.db.prepare("PRAGMA table_info(memos)").all();
|
|
4286
4325
|
if (!info.some((col) => col.name === "project_dir")) this.db.exec("ALTER TABLE memos ADD COLUMN project_dir TEXT NOT NULL DEFAULT ''");
|
|
4287
4326
|
if (!info.some((col) => col.name === "tags")) this.db.exec("ALTER TABLE memos ADD COLUMN tags TEXT NOT NULL DEFAULT '[]'");
|
|
4327
|
+
if (!info.some((col) => col.name === "note")) this.db.exec("ALTER TABLE memos ADD COLUMN note TEXT");
|
|
4328
|
+
if (!info.some((col) => col.name === "distilled")) this.db.exec("ALTER TABLE memos ADD COLUMN distilled TEXT NOT NULL DEFAULT ''");
|
|
4329
|
+
if (!info.some((col) => col.name === "origin")) {
|
|
4330
|
+
this.db.exec("ALTER TABLE memos ADD COLUMN origin TEXT NOT NULL DEFAULT 'session'");
|
|
4331
|
+
this.db.exec("UPDATE memos SET origin = 'dream' WHERE distilled <> ''");
|
|
4332
|
+
}
|
|
4333
|
+
if (!info.some((col) => col.name === "distilled_from")) this.db.exec("ALTER TABLE memos ADD COLUMN distilled_from TEXT");
|
|
4334
|
+
if (!info.some((col) => col.name === "distilled_at")) this.db.exec("ALTER TABLE memos ADD COLUMN distilled_at INTEGER");
|
|
4288
4335
|
this.db.exec(`
|
|
4289
4336
|
CREATE INDEX IF NOT EXISTS idx_memos_project_dir ON memos(project_dir);
|
|
4290
4337
|
CREATE INDEX IF NOT EXISTS idx_memory_embeddings_created_at ON memory_embeddings(created_at DESC);
|
|
@@ -4328,15 +4375,16 @@ var MemoryMemoStore = class MemoryMemoStore {
|
|
|
4328
4375
|
if (this.db === void 0 || memos.length === 0) return;
|
|
4329
4376
|
const insert = this.db.prepare(`INSERT OR IGNORE INTO memos (
|
|
4330
4377
|
id, source_session_id, source_session_title, user_need, approach,
|
|
4331
|
-
outcome, what_failed, what_worked, extraction_source, recorded_at, project_dir, tags
|
|
4332
|
-
|
|
4378
|
+
outcome, what_failed, what_worked, extraction_source, recorded_at, project_dir, tags,
|
|
4379
|
+
note, distilled, origin, distilled_from, distilled_at
|
|
4380
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
4333
4381
|
RETURNING rowid`);
|
|
4334
4382
|
const insertFts = this.db.prepare(`INSERT INTO memos_fts(rowid, user_need, approach, what_failed, what_worked, source_session_title)
|
|
4335
4383
|
VALUES (?, ?, ?, ?, ?, ?)`);
|
|
4336
4384
|
this.db.exec("BEGIN TRANSACTION");
|
|
4337
4385
|
try {
|
|
4338
4386
|
for (const memo of memos) {
|
|
4339
|
-
const row = insert.get(memo.id, memo.sourceSessionId, memo.sourceSessionTitle ?? null, memo.userNeed, memo.approach, memo.outcome, memo.whatFailed, memo.whatWorked, memo.extractionSource, memo.recordedAt, memo.projectDir ?? "", JSON.stringify(memo.tags ?? []));
|
|
4387
|
+
const row = insert.get(memo.id, memo.sourceSessionId, memo.sourceSessionTitle ?? null, memo.userNeed, memo.approach, memo.outcome, memo.whatFailed, memo.whatWorked, memo.extractionSource, memo.recordedAt, memo.projectDir ?? "", JSON.stringify(memo.tags ?? []), memo.note ?? null, memo.distilled ?? "", memo.origin ?? "session", stringifyDistilledFrom(memo.distilledFrom), memo.distilledAt ?? null);
|
|
4340
4388
|
if (row === void 0) continue;
|
|
4341
4389
|
insertFts.run(row.rowid, toFtsText$1(memo.userNeed), toFtsText$1(memo.approach), toFtsText$1(memo.whatFailed), toFtsText$1(memo.whatWorked), toFtsText$1(memo.sourceSessionTitle ?? ""));
|
|
4342
4390
|
}
|
|
@@ -4351,14 +4399,15 @@ var MemoryMemoStore = class MemoryMemoStore {
|
|
|
4351
4399
|
if (this.db === void 0) return;
|
|
4352
4400
|
const insert = this.db.prepare(`INSERT INTO memos (
|
|
4353
4401
|
id, source_session_id, source_session_title, user_need, approach,
|
|
4354
|
-
outcome, what_failed, what_worked, extraction_source, recorded_at, project_dir, tags
|
|
4355
|
-
|
|
4402
|
+
outcome, what_failed, what_worked, extraction_source, recorded_at, project_dir, tags,
|
|
4403
|
+
note, distilled, origin, distilled_from, distilled_at
|
|
4404
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
4356
4405
|
RETURNING rowid`);
|
|
4357
4406
|
const insertFts = this.db.prepare(`INSERT INTO memos_fts(rowid, user_need, approach, what_failed, what_worked, source_session_title)
|
|
4358
4407
|
VALUES (?, ?, ?, ?, ?, ?)`);
|
|
4359
4408
|
this.db.exec("BEGIN TRANSACTION");
|
|
4360
4409
|
try {
|
|
4361
|
-
const row = insert.get(entry.id, entry.sourceSessionId, entry.sourceSessionTitle ?? null, entry.userNeed, entry.approach, entry.outcome, entry.whatFailed, entry.whatWorked, entry.extractionSource, entry.recordedAt, entry.projectDir ?? "", JSON.stringify(entry.tags ?? []));
|
|
4410
|
+
const row = insert.get(entry.id, entry.sourceSessionId, entry.sourceSessionTitle ?? null, entry.userNeed, entry.approach, entry.outcome, entry.whatFailed, entry.whatWorked, entry.extractionSource, entry.recordedAt, entry.projectDir ?? "", JSON.stringify(entry.tags ?? []), entry.note ?? null, entry.distilled ?? "", entry.origin ?? "session", stringifyDistilledFrom(entry.distilledFrom), entry.distilledAt ?? null);
|
|
4362
4411
|
insertFts.run(row.rowid, toFtsText$1(entry.userNeed), toFtsText$1(entry.approach), toFtsText$1(entry.whatFailed), toFtsText$1(entry.whatWorked), toFtsText$1(entry.sourceSessionTitle ?? ""));
|
|
4363
4412
|
this.db.exec("COMMIT");
|
|
4364
4413
|
this.scheduleEmbedding(entry);
|
|
@@ -4394,7 +4443,12 @@ var MemoryMemoStore = class MemoryMemoStore {
|
|
|
4394
4443
|
extraction_source = ?,
|
|
4395
4444
|
recorded_at = ?,
|
|
4396
4445
|
project_dir = ?,
|
|
4397
|
-
tags =
|
|
4446
|
+
tags = ?,
|
|
4447
|
+
note = ?,
|
|
4448
|
+
distilled = ?,
|
|
4449
|
+
origin = ?,
|
|
4450
|
+
distilled_from = ?,
|
|
4451
|
+
distilled_at = ?
|
|
4398
4452
|
WHERE id = ?
|
|
4399
4453
|
RETURNING rowid`);
|
|
4400
4454
|
const updateFts = this.db.prepare(`INSERT INTO memos_fts(rowid, user_need, approach, what_failed, what_worked, source_session_title)
|
|
@@ -4408,7 +4462,7 @@ var MemoryMemoStore = class MemoryMemoStore {
|
|
|
4408
4462
|
this.db.exec("ROLLBACK");
|
|
4409
4463
|
return false;
|
|
4410
4464
|
}
|
|
4411
|
-
const row = update.get(updated.sourceSessionId, updated.sourceSessionTitle ?? null, updated.userNeed, updated.approach, updated.outcome, updated.whatFailed, updated.whatWorked, updated.extractionSource, updated.recordedAt, updated.projectDir ?? "", JSON.stringify(updated.tags ?? []), id);
|
|
4465
|
+
const row = update.get(updated.sourceSessionId, updated.sourceSessionTitle ?? null, updated.userNeed, updated.approach, updated.outcome, updated.whatFailed, updated.whatWorked, updated.extractionSource, updated.recordedAt, updated.projectDir ?? "", JSON.stringify(updated.tags ?? []), updated.note ?? null, updated.distilled ?? "", updated.origin ?? "session", stringifyDistilledFrom(updated.distilledFrom), updated.distilledAt ?? null, id);
|
|
4412
4466
|
if (row === void 0) {
|
|
4413
4467
|
this.db.exec("ROLLBACK");
|
|
4414
4468
|
return false;
|
|
@@ -4656,6 +4710,9 @@ var MemoryMemoStore = class MemoryMemoStore {
|
|
|
4656
4710
|
function rowToMemo(row) {
|
|
4657
4711
|
const sourceSessionTitle = row["source_session_title"];
|
|
4658
4712
|
const projectDir = row["project_dir"];
|
|
4713
|
+
const distilled = row["distilled"];
|
|
4714
|
+
const note = row["note"];
|
|
4715
|
+
const distilledAt = row["distilled_at"];
|
|
4659
4716
|
return {
|
|
4660
4717
|
id: String(row["id"]),
|
|
4661
4718
|
sourceSessionId: String(row["source_session_id"]),
|
|
@@ -4665,12 +4722,36 @@ function rowToMemo(row) {
|
|
|
4665
4722
|
outcome: String(row["outcome"]),
|
|
4666
4723
|
whatFailed: String(row["what_failed"]),
|
|
4667
4724
|
whatWorked: String(row["what_worked"]),
|
|
4725
|
+
distilled: typeof distilled === "string" && distilled.trim().length > 0 ? distilled : void 0,
|
|
4726
|
+
origin: parseOrigin(row["origin"]),
|
|
4727
|
+
distilledFrom: parseDistilledFrom(row["distilled_from"]),
|
|
4728
|
+
distilledAt: typeof distilledAt === "number" && Number.isFinite(distilledAt) ? distilledAt : void 0,
|
|
4668
4729
|
extractionSource: row["extraction_source"],
|
|
4669
4730
|
recordedAt: Number(row["recorded_at"]),
|
|
4670
4731
|
projectDir: typeof projectDir === "string" ? projectDir : "",
|
|
4671
|
-
tags: parseTags(row["tags"])
|
|
4732
|
+
tags: parseTags(row["tags"]),
|
|
4733
|
+
note: typeof note === "string" && note.length > 0 ? note : void 0
|
|
4672
4734
|
};
|
|
4673
4735
|
}
|
|
4736
|
+
function parseOrigin(value) {
|
|
4737
|
+
return value === "dream" || value === "user" || value === "session" ? value : "session";
|
|
4738
|
+
}
|
|
4739
|
+
/** Column value for `distilled_from`: undefined when there is nothing to store. */
|
|
4740
|
+
function stringifyDistilledFrom(sources) {
|
|
4741
|
+
return sources === void 0 || sources.length === 0 ? null : JSON.stringify(sources);
|
|
4742
|
+
}
|
|
4743
|
+
/** Parse the `distilled_from` JSON column, dropping entries that fail validation. */
|
|
4744
|
+
function parseDistilledFrom(value) {
|
|
4745
|
+
if (typeof value !== "string" || value.length === 0) return void 0;
|
|
4746
|
+
try {
|
|
4747
|
+
const parsed = JSON.parse(value);
|
|
4748
|
+
if (!Array.isArray(parsed)) return void 0;
|
|
4749
|
+
const sources = parsed.filter((item) => typeof item === "object" && item !== null && typeof item.id === "string" && typeof item.title === "string" && typeof item.at === "number" && Number.isFinite(item.at));
|
|
4750
|
+
return sources.length > 0 ? sources.slice(0, 100) : void 0;
|
|
4751
|
+
} catch {
|
|
4752
|
+
return;
|
|
4753
|
+
}
|
|
4754
|
+
}
|
|
4674
4755
|
function parseTags(value) {
|
|
4675
4756
|
if (typeof value !== "string" || value.length === 0) return void 0;
|
|
4676
4757
|
try {
|
|
@@ -4682,12 +4763,17 @@ function parseTags(value) {
|
|
|
4682
4763
|
return;
|
|
4683
4764
|
}
|
|
4684
4765
|
}
|
|
4766
|
+
function likePattern(value) {
|
|
4767
|
+
return `%${value.replaceAll("\\", "\\\\").replaceAll("%", "\\%").replaceAll("_", "\\_")}%`;
|
|
4768
|
+
}
|
|
4685
4769
|
function memoMatchesSearch(memo, search) {
|
|
4686
4770
|
return [
|
|
4687
4771
|
memo.userNeed,
|
|
4688
4772
|
memo.approach,
|
|
4689
4773
|
memo.whatFailed,
|
|
4690
4774
|
memo.whatWorked,
|
|
4775
|
+
memo.distilled ?? "",
|
|
4776
|
+
memo.note ?? "",
|
|
4691
4777
|
memo.sourceSessionTitle ?? "",
|
|
4692
4778
|
...memo.tags ?? []
|
|
4693
4779
|
].join(" ").toLowerCase().replaceAll(/\s+/g, "").includes(search.replaceAll(/\s+/g, ""));
|
|
@@ -4861,6 +4947,7 @@ async function buildConsolidationPlan(store, options) {
|
|
|
4861
4947
|
async function applyConsolidation(store, plan) {
|
|
4862
4948
|
let deleted = 0;
|
|
4863
4949
|
let created = 0;
|
|
4950
|
+
let skipped = 0;
|
|
4864
4951
|
if (plan.resolved.length > 0) {
|
|
4865
4952
|
const worked = plan.resolved.filter((m) => m.whatWorked).map((m) => m.whatWorked).join("; ");
|
|
4866
4953
|
const failed = plan.resolved.filter((m) => m.whatFailed).map((m) => m.whatFailed).join("; ");
|
|
@@ -4899,9 +4986,22 @@ async function applyConsolidation(store, plan) {
|
|
|
4899
4986
|
created++;
|
|
4900
4987
|
}
|
|
4901
4988
|
}
|
|
4989
|
+
const mergeableGroups = [];
|
|
4902
4990
|
for (const group of plan.duplicateGroups) {
|
|
4991
|
+
const distilledText = (group.merged.distilled ?? "").trim();
|
|
4992
|
+
if (distilledText.length === 0 && group.memos.some(isDistilledRule)) {
|
|
4993
|
+
skipped++;
|
|
4994
|
+
continue;
|
|
4995
|
+
}
|
|
4996
|
+
mergeableGroups.push({
|
|
4997
|
+
group,
|
|
4998
|
+
distilledText
|
|
4999
|
+
});
|
|
5000
|
+
}
|
|
5001
|
+
for (const { group, distilledText } of mergeableGroups) {
|
|
4903
5002
|
const newest = group.memos.reduce((a, b) => a.recordedAt > b.recordedAt ? a : b);
|
|
4904
5003
|
const mergedTags = normalizeTags(group.memos.flatMap((m) => m.tags ?? []));
|
|
5004
|
+
const mergedNotes = [...new Set(group.memos.map((memo) => memo.note?.trim() ?? "").filter((note) => note.length > 0))];
|
|
4905
5005
|
const merged = createMemoryMemo({
|
|
4906
5006
|
sourceSessionId: newest.sourceSessionId,
|
|
4907
5007
|
sourceSessionTitle: newest.sourceSessionTitle,
|
|
@@ -4910,14 +5010,19 @@ async function applyConsolidation(store, plan) {
|
|
|
4910
5010
|
outcome: group.merged.outcome,
|
|
4911
5011
|
whatFailed: group.merged.whatFailed,
|
|
4912
5012
|
whatWorked: group.merged.whatWorked,
|
|
5013
|
+
note: mergedNotes.length > 0 ? mergedNotes.join("; ") : void 0,
|
|
4913
5014
|
tags: group.merged.tags ?? mergedTags,
|
|
5015
|
+
distilled: distilledText.length > 0 ? distilledText : void 0,
|
|
4914
5016
|
projectDir: newest.projectDir,
|
|
4915
|
-
extractionSource: "compaction"
|
|
5017
|
+
extractionSource: "compaction",
|
|
5018
|
+
origin: "dream",
|
|
5019
|
+
distilledAt: distilledText.length > 0 ? Date.now() : void 0,
|
|
5020
|
+
distilledFrom: collectLineage(group.memos)
|
|
4916
5021
|
});
|
|
4917
5022
|
await store.append(merged);
|
|
4918
5023
|
created++;
|
|
4919
5024
|
}
|
|
4920
|
-
for (const group of
|
|
5025
|
+
for (const { group } of mergeableGroups) for (const memo of group.memos) {
|
|
4921
5026
|
await store.delete(memo.id);
|
|
4922
5027
|
deleted++;
|
|
4923
5028
|
}
|
|
@@ -4931,9 +5036,31 @@ async function applyConsolidation(store, plan) {
|
|
|
4931
5036
|
}
|
|
4932
5037
|
return {
|
|
4933
5038
|
deleted,
|
|
4934
|
-
created
|
|
5039
|
+
created,
|
|
5040
|
+
skipped
|
|
4935
5041
|
};
|
|
4936
5042
|
}
|
|
5043
|
+
/**
|
|
5044
|
+
* Build the lineage snapshot for a merged memo: every source memo in the group
|
|
5045
|
+
* plus each source's own lineage, deduped by id so a rule distilled from other
|
|
5046
|
+
* rules keeps its whole ancestry. Sorted oldest-first and capped at 100.
|
|
5047
|
+
*/
|
|
5048
|
+
function collectLineage(memos) {
|
|
5049
|
+
const seen = /* @__PURE__ */ new Map();
|
|
5050
|
+
for (const memo of memos) {
|
|
5051
|
+
if (!seen.has(memo.id)) seen.set(memo.id, {
|
|
5052
|
+
id: memo.id,
|
|
5053
|
+
title: clip(memo.userNeed, 80),
|
|
5054
|
+
at: memo.recordedAt
|
|
5055
|
+
});
|
|
5056
|
+
for (const source of memo.distilledFrom ?? []) if (!seen.has(source.id)) seen.set(source.id, source);
|
|
5057
|
+
}
|
|
5058
|
+
return [...seen.values()].toSorted((a, b) => a.at - b.at).slice(-100);
|
|
5059
|
+
}
|
|
5060
|
+
function clip(text, maxLength) {
|
|
5061
|
+
const chars = Array.from(text);
|
|
5062
|
+
return chars.length <= maxLength ? text : chars.slice(0, maxLength).join("");
|
|
5063
|
+
}
|
|
4937
5064
|
function findDuplicateGroups(memos) {
|
|
4938
5065
|
const groups = [];
|
|
4939
5066
|
const used = /* @__PURE__ */ new Set();
|
|
@@ -5012,7 +5139,8 @@ function buildDuplicateGroup(cluster) {
|
|
|
5012
5139
|
approach: `合并 ${cluster.length} 条相关记录。最新方案: ${newest.approach}`,
|
|
5013
5140
|
outcome: bestOutcome,
|
|
5014
5141
|
whatFailed: failures.size > 0 ? [...failures].join("; ") : "none",
|
|
5015
|
-
whatWorked: successes.size > 0 ? [...successes].join("; ") : "none"
|
|
5142
|
+
whatWorked: successes.size > 0 ? [...successes].join("; ") : "none",
|
|
5143
|
+
distilled: ""
|
|
5016
5144
|
},
|
|
5017
5145
|
reason: `发现 ${cluster.length} 条相似记录(关键词重叠 > ${Math.round(SIMILARITY_THRESHOLD * 100)}%)`
|
|
5018
5146
|
};
|
|
@@ -5075,12 +5203,19 @@ function isOutcomeCompleted(outcome) {
|
|
|
5075
5203
|
const lower = outcome.toLowerCase();
|
|
5076
5204
|
return lower.includes("完成") || lower.includes("done") || lower.includes("completed") || lower.includes("成功") || lower.includes("success");
|
|
5077
5205
|
}
|
|
5206
|
+
/**
|
|
5207
|
+
* A distilled rule is not a stale experience: pruning it by age would delete
|
|
5208
|
+
* the conclusion /dream exists to accumulate.
|
|
5209
|
+
*/
|
|
5210
|
+
function isDistilledRule(memo) {
|
|
5211
|
+
return memo.origin === "dream" || memo.origin === "user" || (memo.distilled ?? "").trim().length > 0;
|
|
5212
|
+
}
|
|
5078
5213
|
function findResolved(memos) {
|
|
5079
|
-
return memos.filter((m) => isOutcomeCompleted(m.outcome) && Date.now() - m.recordedAt > 10080 * 60 * 1e3);
|
|
5214
|
+
return memos.filter((m) => !isDistilledRule(m) && isOutcomeCompleted(m.outcome) && Date.now() - m.recordedAt > 10080 * 60 * 1e3);
|
|
5080
5215
|
}
|
|
5081
5216
|
function findStale(memos, staleDays) {
|
|
5082
5217
|
const threshold = Date.now() - staleDays * 24 * 60 * 60 * 1e3;
|
|
5083
|
-
return memos.filter((m) => m.recordedAt < threshold && !isOutcomeCompleted(m.outcome) && !m.outcome.includes("blocked"));
|
|
5218
|
+
return memos.filter((m) => !isDistilledRule(m) && m.recordedAt < threshold && !isOutcomeCompleted(m.outcome) && !m.outcome.includes("blocked"));
|
|
5084
5219
|
}
|
|
5085
5220
|
//#endregion
|
|
5086
5221
|
//#region ../../packages/memory/src/dream.ts
|
|
@@ -6717,14 +6852,78 @@ async function embedOrNulls(engine, texts, skipEmbedding) {
|
|
|
6717
6852
|
}
|
|
6718
6853
|
/** LLM concurrency for extraction — callers and comments below refer to this constant. */
|
|
6719
6854
|
const LLM_CONCURRENCY = 20;
|
|
6720
|
-
|
|
6855
|
+
/** Formats read directly as UTF-8 text. */
|
|
6856
|
+
const TEXT_EXTENSIONS = new Set([
|
|
6721
6857
|
".md",
|
|
6722
6858
|
".markdown",
|
|
6723
6859
|
".txt"
|
|
6724
6860
|
]);
|
|
6725
|
-
/**
|
|
6726
|
-
|
|
6727
|
-
|
|
6861
|
+
/**
|
|
6862
|
+
* Binary documents run through the markit converter (they become Markdown,
|
|
6863
|
+
* then take the exact same chunking/embedding path as a `.md` file).
|
|
6864
|
+
*/
|
|
6865
|
+
const DOCUMENT_EXTENSIONS = new Set([
|
|
6866
|
+
".pdf",
|
|
6867
|
+
".docx",
|
|
6868
|
+
".xlsx"
|
|
6869
|
+
]);
|
|
6870
|
+
const SUPPORTED_EXTENSIONS = new Set([...TEXT_EXTENSIONS, ...DOCUMENT_EXTENSIONS]);
|
|
6871
|
+
/**
|
|
6872
|
+
* Hard ceiling for a converted document. Every chunk costs one LLM extraction
|
|
6873
|
+
* call, so an unbounded PDF would silently turn into hundreds of requests.
|
|
6874
|
+
*/
|
|
6875
|
+
const MAX_DOCUMENT_BYTES = 50 * 1024 * 1024;
|
|
6876
|
+
/** Lazily-loaded converter instance: only binary ingests pay the import cost. */
|
|
6877
|
+
let markitPromise;
|
|
6878
|
+
function getMarkit() {
|
|
6879
|
+
markitPromise ??= import("./markit-CV3BXOV_.mjs").then(({ Markit }) => new Markit()).catch((error) => {
|
|
6880
|
+
markitPromise = void 0;
|
|
6881
|
+
throw error;
|
|
6882
|
+
});
|
|
6883
|
+
return markitPromise;
|
|
6884
|
+
}
|
|
6885
|
+
/**
|
|
6886
|
+
* The converters record images as `<!-- image: id (page N, WxHpt) -->` when
|
|
6887
|
+
* they are not asked to write them out. Those comments carry no searchable
|
|
6888
|
+
* meaning and would otherwise burn tokens in every chunk they land in.
|
|
6889
|
+
*/
|
|
6890
|
+
function stripImagePlaceholders(markdown) {
|
|
6891
|
+
return markdown.replaceAll(/<!--\s*image:[^>]*-->/g, "").replaceAll(/\n{3,}/g, "\n\n").trim();
|
|
6892
|
+
}
|
|
6893
|
+
/**
|
|
6894
|
+
* Read a source file and fingerprint it.
|
|
6895
|
+
*
|
|
6896
|
+
* Text files hash the decoded string — exactly what the previous
|
|
6897
|
+
* `sha256(readFile(path, 'utf-8'))` produced, so existing `contentHash` values
|
|
6898
|
+
* keep matching even for non-UTF-8 text. Binary files hash the raw bytes, which
|
|
6899
|
+
* stays stable across converter upgrades.
|
|
6900
|
+
*/
|
|
6901
|
+
async function readSource(filePath) {
|
|
6902
|
+
const ext = getFileExtension(filePath);
|
|
6903
|
+
if (!TEXT_EXTENSIONS.has(ext)) {
|
|
6904
|
+
const { size } = await stat(filePath);
|
|
6905
|
+
if (size > MAX_DOCUMENT_BYTES) throw new Error(`文档超过 ${MAX_DOCUMENT_BYTES / 1024 / 1024}MB 上限,已跳过: ${basename$1(filePath)}`);
|
|
6906
|
+
}
|
|
6907
|
+
const bytes = await readFile(filePath);
|
|
6908
|
+
return {
|
|
6909
|
+
bytes,
|
|
6910
|
+
ext,
|
|
6911
|
+
contentHash: TEXT_EXTENSIONS.has(ext) ? createHash("sha256").update(bytes.toString("utf8")).digest("hex") : createHash("sha256").update(bytes).digest("hex")
|
|
6912
|
+
};
|
|
6913
|
+
}
|
|
6914
|
+
/**
|
|
6915
|
+
* Convert a binary document to Markdown. Only called once the bytes are known
|
|
6916
|
+
* to be new or changed — an unchanged PDF is never re-parsed.
|
|
6917
|
+
*/
|
|
6918
|
+
async function convertSource(filePath, bytes, ext, onConverting) {
|
|
6919
|
+
onConverting?.(basename$1(filePath));
|
|
6920
|
+
const { markdown } = await (await getMarkit()).convert(bytes, {
|
|
6921
|
+
extension: ext,
|
|
6922
|
+
filename: basename$1(filePath)
|
|
6923
|
+
});
|
|
6924
|
+
const content = stripImagePlaceholders(markdown ?? "");
|
|
6925
|
+
if (content.length === 0) throw new Error(ext === ".pdf" ? "文档未提取到文字(可能是扫描版 PDF),本功能不含 OCR" : "文档未提取到可索引的文字,文件可能已损坏或为空");
|
|
6926
|
+
return content;
|
|
6728
6927
|
}
|
|
6729
6928
|
function getFileExtension(filePath) {
|
|
6730
6929
|
const lastDot = filePath.lastIndexOf(".");
|
|
@@ -6792,6 +6991,10 @@ async function ensureEmbeddingModelMatches(store, engine) {
|
|
|
6792
6991
|
* Reports progress via callback.
|
|
6793
6992
|
*/
|
|
6794
6993
|
async function ingestFile(store, llm, filePath, onProgress) {
|
|
6994
|
+
if (!isSupportedFile(filePath)) {
|
|
6995
|
+
const ext = getFileExtension(filePath);
|
|
6996
|
+
throw new Error(`不支持的文件类型: ${ext === "" ? "(无扩展名)" : ext}`);
|
|
6997
|
+
}
|
|
6795
6998
|
const engine = store.getEmbeddingEngine();
|
|
6796
6999
|
const skipEmbed = engine === void 0 || !engine.available;
|
|
6797
7000
|
if (skipEmbed) onProgress?.({
|
|
@@ -6803,8 +7006,7 @@ async function ingestFile(store, llm, filePath, onProgress) {
|
|
|
6803
7006
|
stage: "chunking",
|
|
6804
7007
|
message: `读取并切分文件: ${basename$1(filePath)}`
|
|
6805
7008
|
});
|
|
6806
|
-
const
|
|
6807
|
-
const contentHash = createHash("sha256").update(content).digest("hex");
|
|
7009
|
+
const { bytes, ext, contentHash } = await readSource(filePath);
|
|
6808
7010
|
const existing = await store.findSourceByFilePath(filePath);
|
|
6809
7011
|
let replacedOldVersion = false;
|
|
6810
7012
|
if (existing !== void 0) {
|
|
@@ -6826,10 +7028,18 @@ async function ingestFile(store, llm, filePath, onProgress) {
|
|
|
6826
7028
|
stage: "chunking",
|
|
6827
7029
|
message: `检测到内容变更,替换旧版本: ${existing.name}`
|
|
6828
7030
|
});
|
|
7031
|
+
}
|
|
7032
|
+
const content = TEXT_EXTENSIONS.has(ext) ? bytes.toString("utf8") : await convertSource(filePath, bytes, ext, (file) => {
|
|
7033
|
+
onProgress?.({
|
|
7034
|
+
stage: "converting",
|
|
7035
|
+
message: file
|
|
7036
|
+
});
|
|
7037
|
+
});
|
|
7038
|
+
if (existing !== void 0) {
|
|
6829
7039
|
await store.deleteSource(existing.id);
|
|
6830
7040
|
replacedOldVersion = true;
|
|
6831
7041
|
}
|
|
6832
|
-
const sections = chunkContent(content,
|
|
7042
|
+
const sections = chunkContent(content, ext);
|
|
6833
7043
|
store.beginTransaction();
|
|
6834
7044
|
try {
|
|
6835
7045
|
if (sections.length === 0) {
|
|
@@ -7033,6 +7243,7 @@ async function collectSupportedFiles(dirPath) {
|
|
|
7033
7243
|
const files = [];
|
|
7034
7244
|
const entries = await readdir(dirPath, { withFileTypes: true });
|
|
7035
7245
|
for (const entry of entries) {
|
|
7246
|
+
if (entry.name.startsWith(".") || entry.name.startsWith("~$")) continue;
|
|
7036
7247
|
const fullPath = join$1(dirPath, entry.name);
|
|
7037
7248
|
if (entry.isDirectory()) files.push(...await collectSupportedFiles(fullPath));
|
|
7038
7249
|
else if (entry.isFile() && isSupportedFile(fullPath)) files.push(fullPath);
|
|
@@ -7041,7 +7252,7 @@ async function collectSupportedFiles(dirPath) {
|
|
|
7041
7252
|
}
|
|
7042
7253
|
async function ingestDirectory(store, llm, dirPath, onProgress) {
|
|
7043
7254
|
const files = await collectSupportedFiles(dirPath);
|
|
7044
|
-
if (files.length === 0) throw new Error(`目录下没有支持的文件 (.
|
|
7255
|
+
if (files.length === 0) throw new Error(`目录下没有支持的文件 (${[...SUPPORTED_EXTENSIONS].join(", ")}): ${dirPath}`);
|
|
7045
7256
|
const errors = [];
|
|
7046
7257
|
let succeeded = 0;
|
|
7047
7258
|
let created = 0;
|
|
@@ -7053,6 +7264,10 @@ async function ingestDirectory(store, llm, dirPath, onProgress) {
|
|
|
7053
7264
|
for (let i = 0; i < files.length; i++) {
|
|
7054
7265
|
const filePath = files[i];
|
|
7055
7266
|
const fileProgress = (progress) => {
|
|
7267
|
+
if (progress.stage === "converting") {
|
|
7268
|
+
onProgress?.(progress);
|
|
7269
|
+
return;
|
|
7270
|
+
}
|
|
7056
7271
|
const prefix = `批量摄入 ${i + 1}/${files.length}: ${basename$1(filePath)}`;
|
|
7057
7272
|
onProgress?.({
|
|
7058
7273
|
...progress,
|