@remnic/core 1.1.0 → 1.1.2
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/access-audit.d.ts +56 -0
- package/dist/access-audit.js +9 -0
- package/dist/access-cli.js +70 -53
- package/dist/access-cli.js.map +1 -1
- package/dist/access-http.d.ts +16 -9
- package/dist/access-http.js +26 -18
- package/dist/access-mcp.d.ts +16 -9
- package/dist/access-mcp.js +30 -8
- package/dist/access-schema.d.ts +124 -33
- package/dist/access-schema.js +5 -1
- package/dist/{access-service-HmO1Trrx.d.ts → access-service-Br8ZydTK.d.ts} +158 -63
- package/dist/access-service.d.ts +13 -6
- package/dist/access-service.js +23 -14
- package/dist/bootstrap.d.ts +6 -3
- package/dist/briefing.d.ts +1 -0
- package/dist/briefing.js +8 -6
- package/dist/buffer-surprise-report.d.ts +70 -0
- package/dist/buffer-surprise-report.js +7 -0
- package/dist/buffer-surprise-report.js.map +1 -0
- package/dist/buffer-surprise.d.ts +98 -0
- package/dist/buffer-surprise.js +11 -0
- package/dist/buffer-surprise.js.map +1 -0
- package/dist/buffer.d.ts +100 -2
- package/dist/buffer.js +1 -1
- package/dist/calibration.js +6 -6
- package/dist/causal-behavior.js +4 -4
- package/dist/causal-chain.js +2 -2
- package/dist/causal-consolidation.js +19 -18
- package/dist/causal-consolidation.js.map +1 -1
- package/dist/causal-retrieval.js +4 -4
- package/dist/causal-trajectory.js +1 -1
- package/dist/{chunk-QNJMBKFK.js → chunk-2LGMW3DJ.js} +3 -2
- package/dist/chunk-2LGMW3DJ.js.map +1 -0
- package/dist/{chunk-QDYXG4CS.js → chunk-3FPTCC3Z.js} +4 -3
- package/dist/chunk-3FPTCC3Z.js.map +1 -0
- package/dist/chunk-3GPTTA4J.js +57 -0
- package/dist/chunk-3GPTTA4J.js.map +1 -0
- package/dist/{chunk-ITRLGI2T.js → chunk-3OGMS3PE.js} +2 -2
- package/dist/{chunk-DEPL3635.js → chunk-3YGHKTBF.js} +1446 -196
- package/dist/chunk-3YGHKTBF.js.map +1 -0
- package/dist/{chunk-BLKTA7MM.js → chunk-4HQS2HPX.js} +54 -21
- package/dist/chunk-4HQS2HPX.js.map +1 -0
- package/dist/chunk-54V4BZWP.js +139 -0
- package/dist/chunk-54V4BZWP.js.map +1 -0
- package/dist/chunk-5JRF2PZA.js +67 -0
- package/dist/chunk-5JRF2PZA.js.map +1 -0
- package/dist/chunk-64NJRYU2.js +332 -0
- package/dist/chunk-64NJRYU2.js.map +1 -0
- package/dist/{chunk-OIT5QGG4.js → chunk-6AUUAZEX.js} +72 -2
- package/dist/chunk-6AUUAZEX.js.map +1 -0
- package/dist/{chunk-3QHL5ABG.js → chunk-6YJHX2DL.js} +191 -10
- package/dist/chunk-6YJHX2DL.js.map +1 -0
- package/dist/chunk-AJU4PJGY.js +126 -0
- package/dist/chunk-AJU4PJGY.js.map +1 -0
- package/dist/chunk-ASAITVLA.js +64 -0
- package/dist/chunk-ASAITVLA.js.map +1 -0
- package/dist/{chunk-44ICJRF3.js → chunk-AYXIPSZO.js} +5 -5
- package/dist/{chunk-MBJHSA7F.js → chunk-BECYBZLX.js} +265 -20
- package/dist/chunk-BECYBZLX.js.map +1 -0
- package/dist/chunk-C4SQJZAF.js +486 -0
- package/dist/chunk-C4SQJZAF.js.map +1 -0
- package/dist/{chunk-6UJ47TVX.js → chunk-CUPFXL3J.js} +2 -2
- package/dist/chunk-DF3RVK3X.js +119 -0
- package/dist/chunk-DF3RVK3X.js.map +1 -0
- package/dist/{chunk-N42IWANG.js → chunk-DG6YMRDC.js} +3 -3
- package/dist/chunk-DGVM5SFL.js +69 -0
- package/dist/chunk-DGVM5SFL.js.map +1 -0
- package/dist/{chunk-3SV6CQHO.js → chunk-DIXB44VE.js} +102 -66
- package/dist/chunk-DIXB44VE.js.map +1 -0
- package/dist/chunk-EIR5VLIH.js +90 -0
- package/dist/chunk-EIR5VLIH.js.map +1 -0
- package/dist/{chunk-GV6NLQ4X.js → chunk-F5VP6YCB.js} +374 -16
- package/dist/chunk-F5VP6YCB.js.map +1 -0
- package/dist/{chunk-6ZH4TU6I.js → chunk-FAAFWE4G.js} +2 -1
- package/dist/chunk-FAAFWE4G.js.map +1 -0
- package/dist/{chunk-7WQ6SLIE.js → chunk-FVA6TGI3.js} +2 -2
- package/dist/{chunk-PAORGQRI.js → chunk-GA5P7RST.js} +37 -23
- package/dist/chunk-GA5P7RST.js.map +1 -0
- package/dist/chunk-GDFS42HT.js +206 -0
- package/dist/chunk-GDFS42HT.js.map +1 -0
- package/dist/chunk-IISBCCWR.js +52 -0
- package/dist/chunk-IISBCCWR.js.map +1 -0
- package/dist/chunk-JBMSGZEQ.js +441 -0
- package/dist/chunk-JBMSGZEQ.js.map +1 -0
- package/dist/{chunk-J4IYOZZ5.js → chunk-JXS5PDQ7.js} +3 -1
- package/dist/chunk-JXS5PDQ7.js.map +1 -0
- package/dist/chunk-KVBLZUKV.js +173 -0
- package/dist/chunk-KVBLZUKV.js.map +1 -0
- package/dist/{chunk-4LACOVZX.js → chunk-L7IXWRYE.js} +10 -5
- package/dist/chunk-L7IXWRYE.js.map +1 -0
- package/dist/chunk-LBLXEFWK.js +51 -0
- package/dist/chunk-LBLXEFWK.js.map +1 -0
- package/dist/{chunk-WBSAYXVI.js → chunk-LOIMBRDE.js} +201 -45
- package/dist/chunk-LOIMBRDE.js.map +1 -0
- package/dist/{chunk-3WHVNEN7.js → chunk-LTCGGW2D.js} +1 -1
- package/dist/chunk-LTCGGW2D.js.map +1 -0
- package/dist/{chunk-ZVBB3T7V.js → chunk-NBVAS5MT.js} +25 -23
- package/dist/chunk-NBVAS5MT.js.map +1 -0
- package/dist/{chunk-UEYA6UC7.js → chunk-NZLQTHS5.js} +25 -2
- package/dist/chunk-NZLQTHS5.js.map +1 -0
- package/dist/{chunk-NQEVYWX6.js → chunk-OC5OXUQ4.js} +211 -7
- package/dist/chunk-OC5OXUQ4.js.map +1 -0
- package/dist/{chunk-LK6SGL53.js → chunk-OR64ZGRZ.js} +3 -2
- package/dist/chunk-OR64ZGRZ.js.map +1 -0
- package/dist/{chunk-SYUK3VLY.js → chunk-PVICZTKG.js} +117 -5
- package/dist/chunk-PVICZTKG.js.map +1 -0
- package/dist/chunk-PVPWZSSI.js +37 -0
- package/dist/chunk-PVPWZSSI.js.map +1 -0
- package/dist/{chunk-JL2PU6AI.js → chunk-R2XRID2N.js} +2 -2
- package/dist/{chunk-4NRAJUDS.js → chunk-RBBWYEFJ.js} +1 -1
- package/dist/chunk-RFYAYKTD.js +146 -0
- package/dist/chunk-RFYAYKTD.js.map +1 -0
- package/dist/chunk-SOBJ6NEY.js +18 -0
- package/dist/chunk-SOBJ6NEY.js.map +1 -0
- package/dist/{chunk-JIU55F3X.js → chunk-SPI27QT6.js} +2 -2
- package/dist/{chunk-MVTHXUBX.js → chunk-STGWEHYR.js} +479 -20
- package/dist/chunk-STGWEHYR.js.map +1 -0
- package/dist/{chunk-6LX5ORAS.js → chunk-TMYO7B5P.js} +4 -4
- package/dist/chunk-TVVEYCNW.js +65 -0
- package/dist/chunk-TVVEYCNW.js.map +1 -0
- package/dist/chunk-ULYOGL6R.js +322 -0
- package/dist/chunk-ULYOGL6R.js.map +1 -0
- package/dist/{chunk-37UIFYWO.js → chunk-UWB5LMWY.js} +108 -9
- package/dist/chunk-UWB5LMWY.js.map +1 -0
- package/dist/{chunk-47UU5PU2.js → chunk-VBVG2M5G.js} +18 -3
- package/dist/chunk-VBVG2M5G.js.map +1 -0
- package/dist/{chunk-7ECD5ATE.js → chunk-VDX363PS.js} +2 -2
- package/dist/{chunk-O5ETUNBT.js → chunk-VTU2B4VF.js} +7 -3
- package/dist/chunk-VTU2B4VF.js.map +1 -0
- package/dist/{chunk-MTLYEMJB.js → chunk-WCLICCGB.js} +18 -3
- package/dist/chunk-WCLICCGB.js.map +1 -0
- package/dist/chunk-X6GF3FX2.js +26 -0
- package/dist/chunk-X6GF3FX2.js.map +1 -0
- package/dist/{chunk-3QFQGRHO.js → chunk-XMHBH5H6.js} +4 -4
- package/dist/{chunk-DHHP2Z4X.js → chunk-XXVWLXSG.js} +2 -2
- package/dist/{chunk-XZ2TIKGC.js → chunk-Y7R2XJ5Q.js} +25 -9
- package/dist/chunk-Y7R2XJ5Q.js.map +1 -0
- package/dist/{chunk-ALXMCZEU.js → chunk-Z2E7VW55.js} +6 -3
- package/dist/chunk-Z2E7VW55.js.map +1 -0
- package/dist/chunk-ZAIM4TUE.js +488 -0
- package/dist/chunk-ZAIM4TUE.js.map +1 -0
- package/dist/chunk-ZZTOURJI.js +91 -0
- package/dist/chunk-ZZTOURJI.js.map +1 -0
- package/dist/{cli-BneVIEvh.d.ts → cli-BkeRaYfk.d.ts} +2 -2
- package/dist/cli.d.ts +13 -6
- package/dist/cli.js +42 -31
- package/dist/config.js +2 -2
- package/dist/consolidation-operator.d.ts +41 -0
- package/dist/consolidation-operator.js +11 -0
- package/dist/consolidation-operator.js.map +1 -0
- package/dist/consolidation-provenance-check.d.ts +68 -0
- package/dist/consolidation-provenance-check.js +9 -0
- package/dist/consolidation-provenance-check.js.map +1 -0
- package/dist/consolidation-undo.d.ts +123 -0
- package/dist/consolidation-undo.js +426 -0
- package/dist/consolidation-undo.js.map +1 -0
- package/dist/{contradiction-scan-GR33PONM.js → contradiction-scan-E3GJTI4F.js} +43 -7
- package/dist/contradiction-scan-E3GJTI4F.js.map +1 -0
- package/dist/cross-namespace-budget.d.ts +133 -0
- package/dist/cross-namespace-budget.js +9 -0
- package/dist/cross-namespace-budget.js.map +1 -0
- package/dist/direct-answer-wiring.js +5 -70
- package/dist/direct-answer-wiring.js.map +1 -1
- package/dist/embedding-fallback.js +2 -1
- package/dist/{engine-5TIQBYZR.js → engine-72LSIWQP.js} +8 -7
- package/dist/engine-72LSIWQP.js.map +1 -0
- package/dist/entity-retrieval.d.ts +1 -0
- package/dist/entity-retrieval.js +7 -6
- package/dist/explicit-capture.d.ts +6 -3
- package/dist/explicit-capture.js +2 -2
- package/dist/extraction-judge-telemetry.d.ts +113 -0
- package/dist/extraction-judge-telemetry.js +14 -0
- package/dist/extraction-judge-telemetry.js.map +1 -0
- package/dist/extraction-judge-training.d.ts +85 -0
- package/dist/extraction-judge-training.js +16 -0
- package/dist/extraction-judge-training.js.map +1 -0
- package/dist/extraction-judge.d.ts +124 -2
- package/dist/extraction-judge.js +11 -1
- package/dist/extraction.js +10 -9
- package/dist/fallback-llm.js +3 -3
- package/dist/graph-recall.d.ts +100 -0
- package/dist/graph-recall.js +8 -0
- package/dist/graph-recall.js.map +1 -0
- package/dist/graph-retrieval.d.ts +271 -0
- package/dist/graph-retrieval.js +21 -0
- package/dist/graph-retrieval.js.map +1 -0
- package/dist/importance.js +1 -1
- package/dist/index.d.ts +585 -20
- package/dist/index.js +542 -344
- package/dist/index.js.map +1 -1
- package/dist/local-llm.js +2 -2
- package/dist/memory-worth-bench.d.ts +51 -0
- package/dist/memory-worth-bench.js +131 -0
- package/dist/memory-worth-bench.js.map +1 -0
- package/dist/memory-worth-filter.d.ts +128 -0
- package/dist/memory-worth-filter.js +10 -0
- package/dist/memory-worth-filter.js.map +1 -0
- package/dist/memory-worth-outcomes.d.ts +118 -0
- package/dist/memory-worth-outcomes.js +9 -0
- package/dist/memory-worth-outcomes.js.map +1 -0
- package/dist/memory-worth.d.ts +102 -0
- package/dist/memory-worth.js +7 -0
- package/dist/memory-worth.js.map +1 -0
- package/dist/operator-toolkit.d.ts +40 -1
- package/dist/operator-toolkit.js +25 -16
- package/dist/{orchestrator-DRYA6_lW.d.ts → orchestrator-CmJ-NTdJ.d.ts} +233 -8
- package/dist/orchestrator.d.ts +6 -3
- package/dist/orchestrator.js +54 -44
- package/dist/page-versioning.d.ts +12 -1
- package/dist/page-versioning.js +5 -3
- package/dist/{port-C1GZFv8h.d.ts → port-BADbLZU5.d.ts} +2 -2
- package/dist/qmd-recall-cache.d.ts +1 -1
- package/dist/qmd.d.ts +5 -3
- package/dist/qmd.js +3 -3
- package/dist/reasoning-trace-recall.d.ts +90 -0
- package/dist/reasoning-trace-recall.js +13 -0
- package/dist/reasoning-trace-recall.js.map +1 -0
- package/dist/reasoning-trace-types.d.ts +54 -0
- package/dist/reasoning-trace-types.js +17 -0
- package/dist/reasoning-trace-types.js.map +1 -0
- package/dist/recall-audit-anomaly.d.ts +112 -0
- package/dist/recall-audit-anomaly.js +11 -0
- package/dist/recall-audit-anomaly.js.map +1 -0
- package/dist/recall-audit.js +5 -44
- package/dist/recall-audit.js.map +1 -1
- package/dist/recall-explain-renderer.d.ts +49 -0
- package/dist/recall-explain-renderer.js +18 -0
- package/dist/recall-explain-renderer.js.map +1 -0
- package/dist/recall-state.d.ts +12 -1
- package/dist/recall-state.js +1 -1
- package/dist/recall-xray-cli.d.ts +40 -0
- package/dist/recall-xray-cli.js +11 -0
- package/dist/recall-xray-cli.js.map +1 -0
- package/dist/recall-xray-renderer.d.ts +44 -0
- package/dist/recall-xray-renderer.js +18 -0
- package/dist/recall-xray-renderer.js.map +1 -0
- package/dist/recall-xray.d.ts +179 -0
- package/dist/recall-xray.js +13 -0
- package/dist/recall-xray.js.map +1 -0
- package/dist/resolve-provider-secret.d.ts +5 -1
- package/dist/resolve-provider-secret.js +3 -1
- package/dist/resume-bundles.js +6 -6
- package/dist/retrieval-agents.d.ts +1 -1
- package/dist/retrieval-tiers.d.ts +17 -0
- package/dist/retrieval-tiers.js +9 -0
- package/dist/retrieval-tiers.js.map +1 -0
- package/dist/schemas.d.ts +309 -53
- package/dist/schemas.js +1 -1
- package/dist/{semantic-consolidation-DrvSYRdB.d.ts → semantic-consolidation-CxJU6MJk.d.ts} +62 -1
- package/dist/semantic-consolidation.d.ts +2 -1
- package/dist/semantic-consolidation.js +22 -7
- package/dist/semantic-rule-promotion.js +7 -6
- package/dist/semantic-rule-verifier.js +7 -6
- package/dist/storage.d.ts +82 -1
- package/dist/storage.js +6 -5
- package/dist/summarizer.js +6 -6
- package/dist/temporal-supersession.d.ts +1 -0
- package/dist/tier-migration.d.ts +2 -1
- package/dist/tokens.js +2 -1
- package/dist/types.d.ts +276 -2
- package/dist/types.js +1 -1
- package/dist/verified-recall.js +7 -6
- package/package.json +1 -1
- package/dist/chunk-37UIFYWO.js.map +0 -1
- package/dist/chunk-3QHL5ABG.js.map +0 -1
- package/dist/chunk-3SV6CQHO.js.map +0 -1
- package/dist/chunk-3WHVNEN7.js.map +0 -1
- package/dist/chunk-47UU5PU2.js.map +0 -1
- package/dist/chunk-4LACOVZX.js.map +0 -1
- package/dist/chunk-6ZH4TU6I.js.map +0 -1
- package/dist/chunk-ALXMCZEU.js.map +0 -1
- package/dist/chunk-BLKTA7MM.js.map +0 -1
- package/dist/chunk-DEPL3635.js.map +0 -1
- package/dist/chunk-GV6NLQ4X.js.map +0 -1
- package/dist/chunk-J4IYOZZ5.js.map +0 -1
- package/dist/chunk-LAYN4LDC.js +0 -267
- package/dist/chunk-LAYN4LDC.js.map +0 -1
- package/dist/chunk-LK6SGL53.js.map +0 -1
- package/dist/chunk-MBJHSA7F.js.map +0 -1
- package/dist/chunk-MTLYEMJB.js.map +0 -1
- package/dist/chunk-MVTHXUBX.js.map +0 -1
- package/dist/chunk-NQEVYWX6.js.map +0 -1
- package/dist/chunk-O5ETUNBT.js.map +0 -1
- package/dist/chunk-OIT5QGG4.js.map +0 -1
- package/dist/chunk-PAORGQRI.js.map +0 -1
- package/dist/chunk-QDYXG4CS.js.map +0 -1
- package/dist/chunk-QNJMBKFK.js.map +0 -1
- package/dist/chunk-SYUK3VLY.js.map +0 -1
- package/dist/chunk-UEYA6UC7.js.map +0 -1
- package/dist/chunk-UVJFDP7P.js +0 -202
- package/dist/chunk-UVJFDP7P.js.map +0 -1
- package/dist/chunk-WBSAYXVI.js.map +0 -1
- package/dist/chunk-XZ2TIKGC.js.map +0 -1
- package/dist/chunk-ZVBB3T7V.js.map +0 -1
- package/dist/contradiction-scan-GR33PONM.js.map +0 -1
- /package/dist/{engine-5TIQBYZR.js.map → access-audit.js.map} +0 -0
- /package/dist/{chunk-ITRLGI2T.js.map → chunk-3OGMS3PE.js.map} +0 -0
- /package/dist/{chunk-44ICJRF3.js.map → chunk-AYXIPSZO.js.map} +0 -0
- /package/dist/{chunk-6UJ47TVX.js.map → chunk-CUPFXL3J.js.map} +0 -0
- /package/dist/{chunk-N42IWANG.js.map → chunk-DG6YMRDC.js.map} +0 -0
- /package/dist/{chunk-7WQ6SLIE.js.map → chunk-FVA6TGI3.js.map} +0 -0
- /package/dist/{chunk-JL2PU6AI.js.map → chunk-R2XRID2N.js.map} +0 -0
- /package/dist/{chunk-4NRAJUDS.js.map → chunk-RBBWYEFJ.js.map} +0 -0
- /package/dist/{chunk-JIU55F3X.js.map → chunk-SPI27QT6.js.map} +0 -0
- /package/dist/{chunk-6LX5ORAS.js.map → chunk-TMYO7B5P.js.map} +0 -0
- /package/dist/{chunk-7ECD5ATE.js.map → chunk-VDX363PS.js.map} +0 -0
- /package/dist/{chunk-3QFQGRHO.js.map → chunk-XMHBH5H6.js.map} +0 -0
- /package/dist/{chunk-DHHP2Z4X.js.map → chunk-XXVWLXSG.js.map} +0 -0
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import {
|
|
2
|
+
log
|
|
3
|
+
} from "./chunk-2ODBA7MQ.js";
|
|
4
|
+
|
|
5
|
+
// src/extraction-judge-training.ts
|
|
6
|
+
import path from "path";
|
|
7
|
+
import { homedir } from "os";
|
|
8
|
+
import { appendFile, mkdir, readFile, readdir } from "fs/promises";
|
|
9
|
+
function expandTilde(p) {
|
|
10
|
+
const home = homedir();
|
|
11
|
+
if (p === "~" || p.startsWith("~/") || p.startsWith("~\\")) {
|
|
12
|
+
return home + p.slice(1);
|
|
13
|
+
}
|
|
14
|
+
if (p === "$HOME" || p.startsWith("$HOME/") || p.startsWith("$HOME\\")) {
|
|
15
|
+
return home + p.slice(5);
|
|
16
|
+
}
|
|
17
|
+
if (p === "${HOME}" || p.startsWith("${HOME}/") || p.startsWith("${HOME}\\")) {
|
|
18
|
+
return home + p.slice(7);
|
|
19
|
+
}
|
|
20
|
+
return p;
|
|
21
|
+
}
|
|
22
|
+
function resolveTrainingDir(options) {
|
|
23
|
+
if (options.directory && options.directory.length > 0) {
|
|
24
|
+
return expandTilde(options.directory);
|
|
25
|
+
}
|
|
26
|
+
return path.join(homedir(), ".remnic", "judge-training");
|
|
27
|
+
}
|
|
28
|
+
function dateStamp(iso) {
|
|
29
|
+
const ms = Date.parse(iso);
|
|
30
|
+
const d = Number.isFinite(ms) ? new Date(ms) : /* @__PURE__ */ new Date();
|
|
31
|
+
const yyyy = d.getUTCFullYear().toString().padStart(4, "0");
|
|
32
|
+
const mm = (d.getUTCMonth() + 1).toString().padStart(2, "0");
|
|
33
|
+
const dd = d.getUTCDate().toString().padStart(2, "0");
|
|
34
|
+
return `${yyyy}-${mm}-${dd}`;
|
|
35
|
+
}
|
|
36
|
+
function trainingFilePathFor(directory, iso) {
|
|
37
|
+
return path.join(directory, `${dateStamp(iso)}.jsonl`);
|
|
38
|
+
}
|
|
39
|
+
async function recordJudgeTrainingPair(row, options) {
|
|
40
|
+
if (!options.enabled) return;
|
|
41
|
+
const dir = resolveTrainingDir(options);
|
|
42
|
+
const filePath = trainingFilePathFor(dir, row.ts);
|
|
43
|
+
try {
|
|
44
|
+
await mkdir(dir, { recursive: true });
|
|
45
|
+
await appendFile(filePath, `${JSON.stringify(row)}
|
|
46
|
+
`, "utf-8");
|
|
47
|
+
} catch (err) {
|
|
48
|
+
log.debug(
|
|
49
|
+
`extraction-judge-training: append failed (non-fatal): ${err instanceof Error ? err.message : String(err)}`
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
async function readJudgeTrainingPairs(options) {
|
|
54
|
+
const dir = resolveTrainingDir({ enabled: true, ...options });
|
|
55
|
+
let entries;
|
|
56
|
+
try {
|
|
57
|
+
entries = await readdir(dir);
|
|
58
|
+
} catch (err) {
|
|
59
|
+
const code = err.code;
|
|
60
|
+
if (code === "ENOENT") return { rows: [], malformed: 0 };
|
|
61
|
+
throw err;
|
|
62
|
+
}
|
|
63
|
+
const rows = [];
|
|
64
|
+
let malformed = 0;
|
|
65
|
+
entries.sort();
|
|
66
|
+
for (const name of entries) {
|
|
67
|
+
if (!name.endsWith(".jsonl")) continue;
|
|
68
|
+
const raw = await readFile(path.join(dir, name), "utf-8");
|
|
69
|
+
for (const line of raw.split("\n")) {
|
|
70
|
+
if (!line.trim()) continue;
|
|
71
|
+
let parsed;
|
|
72
|
+
try {
|
|
73
|
+
parsed = JSON.parse(line);
|
|
74
|
+
} catch {
|
|
75
|
+
malformed += 1;
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
if (!isValidTrainingPair(parsed)) {
|
|
79
|
+
malformed += 1;
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
rows.push(parsed);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return { rows, malformed };
|
|
86
|
+
}
|
|
87
|
+
function isValidTrainingPair(value) {
|
|
88
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
const p = value;
|
|
92
|
+
if (p.version !== 1) return false;
|
|
93
|
+
if (typeof p.ts !== "string") return false;
|
|
94
|
+
if (typeof p.candidateText !== "string") return false;
|
|
95
|
+
if (typeof p.candidateCategory !== "string") return false;
|
|
96
|
+
if (p.verdictKind !== "accept" && p.verdictKind !== "reject" && p.verdictKind !== "defer") {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
if (typeof p.reason !== "string") return false;
|
|
100
|
+
if (p.candidateConfidence !== void 0 && typeof p.candidateConfidence !== "number") {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
if (p.priorDeferrals !== void 0 && typeof p.priorDeferrals !== "number") {
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
if (p.groundTruthLabel !== void 0 && p.groundTruthLabel !== "accept" && p.groundTruthLabel !== "reject" && p.groundTruthLabel !== "defer") {
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export {
|
|
113
|
+
resolveTrainingDir,
|
|
114
|
+
trainingFilePathFor,
|
|
115
|
+
recordJudgeTrainingPair,
|
|
116
|
+
readJudgeTrainingPairs,
|
|
117
|
+
isValidTrainingPair
|
|
118
|
+
};
|
|
119
|
+
//# sourceMappingURL=chunk-DF3RVK3X.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/extraction-judge-training.ts"],"sourcesContent":["/**\n * Extraction Judge Training Data Shim (issue #562, PR 4).\n *\n * Opt-in collector for `(candidate_text, verdict_kind, reason,\n * ground_truth_label?)` tuples. Rows are appended to JSONL files under\n * `~/.remnic/judge-training/<YYYY-MM-DD>.jsonl` so operators can ship the\n * data into a future GRPO training pipeline without exfiltrating live\n * memory content through the regular observation ledger.\n *\n * Gating:\n * - Off by default. Must be explicitly enabled via\n * `collectJudgeTrainingPairs: true` in plugin config.\n * - The ground-truth label is always optional — labels are added out-of-\n * band once reviewers disambiguate the candidate's fate.\n *\n * Privacy: the row carries only what the judge already sees — the\n * candidate text and its metadata. It does NOT carry session keys,\n * principal IDs, or any user identifiers. The file lives in the user's\n * home directory rather than the shared memory directory so it is never\n * committed, sync'd, or bundled into exports.\n */\n\nimport path from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { appendFile, mkdir, readFile, readdir } from \"node:fs/promises\";\nimport { log } from \"./logger.js\";\nimport type { JudgeVerdictKind } from \"./extraction-judge.js\";\n\n/**\n * Persisted training row. Intentionally minimal: just the signal needed\n * to train a judge replacement policy. Schema version is tagged so future\n * readers can migrate older rows.\n */\nexport interface JudgeTrainingPair {\n version: 1;\n ts: string; // ISO-8601\n candidateText: string;\n candidateCategory: string;\n candidateConfidence?: number;\n verdictKind: JudgeVerdictKind;\n reason: string;\n /**\n * Number of prior deferrals when the verdict was resolved. `0` for the\n * first resolution; only set when known (defer pathway).\n */\n priorDeferrals?: number;\n /**\n * Optional human-applied ground-truth label. Added after the fact by a\n * reviewer / labelling script; not present on fresh rows.\n */\n groundTruthLabel?: JudgeVerdictKind;\n}\n\nexport interface JudgeTrainingOptions {\n enabled: boolean;\n /**\n * Override for the output directory. Defaults to\n * `~/.remnic/judge-training`. Tests pass a temp path here.\n */\n directory?: string;\n}\n\n/**\n * Expand a leading `~` / `~/` / `$HOME/` / `${HOME}/` to the process home\n * directory. Node's `fs` APIs do not expand `~` themselves (CLAUDE.md\n * gotcha 17), so every user-facing path input must be funnelled through\n * this helper before it reaches the filesystem.\n */\nfunction expandTilde(p: string): string {\n const home = homedir();\n if (p === \"~\" || p.startsWith(\"~/\") || p.startsWith(\"~\\\\\")) {\n return home + p.slice(1);\n }\n if (p === \"$HOME\" || p.startsWith(\"$HOME/\") || p.startsWith(\"$HOME\\\\\")) {\n return home + p.slice(5);\n }\n if (p === \"${HOME}\" || p.startsWith(\"${HOME}/\") || p.startsWith(\"${HOME}\\\\\")) {\n return home + p.slice(7);\n }\n return p;\n}\n\nexport function resolveTrainingDir(options: JudgeTrainingOptions): string {\n if (options.directory && options.directory.length > 0) {\n // Expand `~` / `$HOME` in the override so operators can write the\n // config as the user sees it (CLAUDE.md gotcha 17).\n return expandTilde(options.directory);\n }\n return path.join(homedir(), \".remnic\", \"judge-training\");\n}\n\nfunction dateStamp(iso: string): string {\n // `YYYY-MM-DD` from an ISO-8601 string. Falls back to today on a parse\n // failure rather than throwing — the caller already wrote a row and the\n // timestamp is best-effort.\n const ms = Date.parse(iso);\n const d = Number.isFinite(ms) ? new Date(ms) : new Date();\n const yyyy = d.getUTCFullYear().toString().padStart(4, \"0\");\n const mm = (d.getUTCMonth() + 1).toString().padStart(2, \"0\");\n const dd = d.getUTCDate().toString().padStart(2, \"0\");\n return `${yyyy}-${mm}-${dd}`;\n}\n\nexport function trainingFilePathFor(\n directory: string,\n iso: string,\n): string {\n return path.join(directory, `${dateStamp(iso)}.jsonl`);\n}\n\n/**\n * Append a single training row. Fails open — write errors are logged at\n * debug level and swallowed, same policy as the telemetry emitter.\n * No-op when `options.enabled` is false.\n */\nexport async function recordJudgeTrainingPair(\n row: JudgeTrainingPair,\n options: JudgeTrainingOptions,\n): Promise<void> {\n if (!options.enabled) return;\n const dir = resolveTrainingDir(options);\n const filePath = trainingFilePathFor(dir, row.ts);\n try {\n await mkdir(dir, { recursive: true });\n await appendFile(filePath, `${JSON.stringify(row)}\\n`, \"utf-8\");\n } catch (err) {\n log.debug(\n `extraction-judge-training: append failed (non-fatal): ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n}\n\n/**\n * Read all training rows from the configured directory. Returns an empty\n * array when the directory is missing. Malformed lines are skipped and\n * counted in the returned `malformed` tally.\n */\nexport async function readJudgeTrainingPairs(\n options: Pick<JudgeTrainingOptions, \"directory\">,\n): Promise<{ rows: JudgeTrainingPair[]; malformed: number }> {\n const dir = resolveTrainingDir({ enabled: true, ...options });\n let entries: string[];\n try {\n entries = await readdir(dir);\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === \"ENOENT\") return { rows: [], malformed: 0 };\n throw err;\n }\n\n const rows: JudgeTrainingPair[] = [];\n let malformed = 0;\n // Sort so reads are deterministic across platforms.\n entries.sort();\n for (const name of entries) {\n if (!name.endsWith(\".jsonl\")) continue;\n const raw = await readFile(path.join(dir, name), \"utf-8\");\n for (const line of raw.split(\"\\n\")) {\n if (!line.trim()) continue;\n let parsed: unknown;\n try {\n parsed = JSON.parse(line);\n } catch {\n malformed += 1;\n continue;\n }\n if (!isValidTrainingPair(parsed)) {\n malformed += 1;\n continue;\n }\n rows.push(parsed);\n }\n }\n return { rows, malformed };\n}\n\n/**\n * Structural validator matching the persisted schema. Forward-compat: an\n * unknown `verdictKind` string is treated as malformed (strict training\n * signal — we do not want to admit unlabelled gibberish into a trainer).\n */\nexport function isValidTrainingPair(value: unknown): value is JudgeTrainingPair {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n return false;\n }\n const p = value as Record<string, unknown>;\n if (p.version !== 1) return false;\n if (typeof p.ts !== \"string\") return false;\n if (typeof p.candidateText !== \"string\") return false;\n if (typeof p.candidateCategory !== \"string\") return false;\n if (\n p.verdictKind !== \"accept\" &&\n p.verdictKind !== \"reject\" &&\n p.verdictKind !== \"defer\"\n ) {\n return false;\n }\n if (typeof p.reason !== \"string\") return false;\n if (\n p.candidateConfidence !== undefined &&\n typeof p.candidateConfidence !== \"number\"\n ) {\n return false;\n }\n if (p.priorDeferrals !== undefined && typeof p.priorDeferrals !== \"number\") {\n return false;\n }\n if (\n p.groundTruthLabel !== undefined &&\n p.groundTruthLabel !== \"accept\" &&\n p.groundTruthLabel !== \"reject\" &&\n p.groundTruthLabel !== \"defer\"\n ) {\n return false;\n }\n return true;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -8,10 +8,10 @@ import {
|
|
|
8
8
|
} from "./chunk-FEMOX5AD.js";
|
|
9
9
|
import {
|
|
10
10
|
LocalLlmClient
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-R2XRID2N.js";
|
|
12
12
|
import {
|
|
13
13
|
FallbackLlmClient
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-AYXIPSZO.js";
|
|
15
15
|
import {
|
|
16
16
|
extractJsonCandidates
|
|
17
17
|
} from "./chunk-UZB5KHKX.js";
|
|
@@ -618,4 +618,4 @@ ${truncatedConversation}`;
|
|
|
618
618
|
export {
|
|
619
619
|
HourlySummarizer
|
|
620
620
|
};
|
|
621
|
-
//# sourceMappingURL=chunk-
|
|
621
|
+
//# sourceMappingURL=chunk-DG6YMRDC.js.map
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DEFAULT_PPR_DAMPING,
|
|
3
|
+
DEFAULT_PPR_ITERATIONS,
|
|
4
|
+
buildGraphFromMemories,
|
|
5
|
+
queryGraph
|
|
6
|
+
} from "./chunk-64NJRYU2.js";
|
|
7
|
+
|
|
8
|
+
// src/graph-recall.ts
|
|
9
|
+
function runGraphRecall(config, options) {
|
|
10
|
+
if (!config.recallGraphEnabled) {
|
|
11
|
+
return {
|
|
12
|
+
ran: false,
|
|
13
|
+
results: [],
|
|
14
|
+
graph: null,
|
|
15
|
+
reason: "disabled",
|
|
16
|
+
iterations: 0,
|
|
17
|
+
converged: true
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
const topK = typeof config.recallGraphTopK === "number" && Number.isFinite(config.recallGraphTopK) ? config.recallGraphTopK : 50;
|
|
21
|
+
if (topK <= 0) {
|
|
22
|
+
return {
|
|
23
|
+
ran: false,
|
|
24
|
+
results: [],
|
|
25
|
+
graph: null,
|
|
26
|
+
reason: "topk-zero",
|
|
27
|
+
iterations: 0,
|
|
28
|
+
converged: true
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
if (options.memories.length === 0) {
|
|
32
|
+
return {
|
|
33
|
+
ran: false,
|
|
34
|
+
results: [],
|
|
35
|
+
graph: null,
|
|
36
|
+
reason: "empty-input",
|
|
37
|
+
iterations: 0,
|
|
38
|
+
converged: true
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
const graph = buildGraphFromMemories(options.memories);
|
|
42
|
+
const damping = typeof config.recallGraphDamping === "number" && Number.isFinite(config.recallGraphDamping) ? config.recallGraphDamping : DEFAULT_PPR_DAMPING;
|
|
43
|
+
const iterations = typeof config.recallGraphIterations === "number" && Number.isFinite(config.recallGraphIterations) ? config.recallGraphIterations : DEFAULT_PPR_ITERATIONS;
|
|
44
|
+
const ppr = queryGraph(graph, options.seedIds, {
|
|
45
|
+
damping,
|
|
46
|
+
iterations,
|
|
47
|
+
seedWeights: options.seedWeights
|
|
48
|
+
});
|
|
49
|
+
const memoryResults = [];
|
|
50
|
+
for (const node of ppr.rankedNodes) {
|
|
51
|
+
const graphNode = graph.nodes.get(node.id);
|
|
52
|
+
if (!graphNode || graphNode.type !== "memory") continue;
|
|
53
|
+
memoryResults.push({ id: node.id, score: node.score });
|
|
54
|
+
if (memoryResults.length >= topK) break;
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
ran: true,
|
|
58
|
+
results: memoryResults,
|
|
59
|
+
graph,
|
|
60
|
+
reason: "ran",
|
|
61
|
+
iterations: ppr.iterations,
|
|
62
|
+
converged: ppr.converged
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export {
|
|
67
|
+
runGraphRecall
|
|
68
|
+
};
|
|
69
|
+
//# sourceMappingURL=chunk-DGVM5SFL.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/graph-recall.ts"],"sourcesContent":["/**\n * Graph-based retrieval integration (issue #559 PR 4 of 5).\n *\n * Pure helper that composes `extractGraphEdges` (PR 2) and `queryGraph`\n * (PR 3) into a single retrieval surface. Operators opt in via the\n * `recallGraphEnabled` config flag; until the `retrieval-graph` bench in\n * PR 5 justifies flipping the default, this tier ships disabled.\n *\n * Kept as a pure function so the orchestrator can call it with whatever\n * candidate pool it has (hot cache, recent window, QMD first-pass, etc.)\n * without forcing a specific storage contract on this module.\n */\n\nimport {\n DEFAULT_PPR_DAMPING,\n DEFAULT_PPR_ITERATIONS,\n type MemoryEdgeSource,\n type RemnicGraph,\n buildGraphFromMemories,\n queryGraph,\n} from \"./graph-retrieval.js\";\n\n/**\n * Subset of `PluginConfig` that governs the graph retrieval tier. Kept\n * as a local interface so this module does not pull in the full\n * `PluginConfig` import — `orchestrator.ts` can pass the fields directly.\n */\nexport interface GraphRecallConfig {\n /** Master enable flag. When false, `runGraphRecall` is a no-op. */\n recallGraphEnabled: boolean;\n /** PPR damping factor (default 0.85). */\n recallGraphDamping: number;\n /** PPR power-iteration cap (default 20). */\n recallGraphIterations: number;\n /**\n * Max memories the graph tier returns. `0` disables the tier's\n * contribution without touching `recallGraphEnabled`.\n */\n recallGraphTopK: number;\n}\n\n/** Per-invocation options for `runGraphRecall`. */\nexport interface GraphRecallOptions {\n /**\n * Candidate memories to build the graph from. Typically the caller's\n * recall candidate pool (hot cache + QMD first-pass). The extractor\n * reads only the fields declared on `MemoryEdgeSource` — callers can\n * safely pass richer memory objects.\n */\n memories: readonly MemoryEdgeSource[];\n /**\n * Seed memory / entity ids produced by the query-to-graph matcher.\n * Typically the ids of the top QMD hits plus any entity-exact matches.\n * If empty, PPR falls back to a uniform distribution over graph nodes.\n */\n seedIds: readonly string[];\n /**\n * Optional per-seed weights. When provided, PPR starts from the\n * weighted distribution instead of uniform-over-seeds.\n */\n seedWeights?: ReadonlyMap<string, number> | Readonly<Record<string, number>>;\n}\n\n/** A single result from the graph tier. */\nexport interface GraphRecallResult {\n /** Memory id (the `to` of the highest-scoring `memory`-typed node). */\n id: string;\n /** PPR score in [0, 1]. Higher is better. */\n score: number;\n}\n\n/** The full shape returned by `runGraphRecall`. */\nexport interface GraphRecallRun {\n /**\n * Whether the graph tier actually ran. `false` when `recallGraphEnabled`\n * is `false` or `recallGraphTopK <= 0` — in both cases `results` is `[]`\n * and `reason` indicates which gate short-circuited.\n */\n ran: boolean;\n /**\n * Memory-typed ranked results. Entity / agent nodes are filtered out\n * because the orchestrator merges this list with memory-typed QMD\n * results via MMR.\n */\n results: GraphRecallResult[];\n /** The graph that was built (or `null` if the tier did not run). */\n graph: RemnicGraph | null;\n /** Debugging tag for tier-explain surfaces. */\n reason: \"ran\" | \"disabled\" | \"topk-zero\" | \"empty-input\";\n /** Number of power-iteration rounds that executed. */\n iterations: number;\n /** Whether PPR's L1 delta fell below tolerance before the iter cap. */\n converged: boolean;\n}\n\n/**\n * Pure graph retrieval run.\n *\n * 1. Short-circuits to `{ ran: false }` when the feature flag is off,\n * `topK <= 0`, or the memory pool is empty. No graph is built, no\n * PPR runs — this preserves the zero-cost guarantee for\n * `recallGraphEnabled: false` (the default).\n * 2. Otherwise builds the retrieval graph from the candidate pool via\n * `buildGraphFromMemories` (PR 2 extractor).\n * 3. Runs Personalized PageRank via `queryGraph` (PR 3).\n * 4. Projects ranked nodes to memory-typed ids only — entity and agent\n * nodes never appear in the recall result set.\n */\nexport function runGraphRecall(\n config: GraphRecallConfig,\n options: GraphRecallOptions,\n): GraphRecallRun {\n if (!config.recallGraphEnabled) {\n return {\n ran: false,\n results: [],\n graph: null,\n reason: \"disabled\",\n iterations: 0,\n converged: true,\n };\n }\n\n // `isFinite` guard on `topK` mirrors the checks on `damping` and\n // `iterations` below. Without it a `NaN` topK would pass the typeof\n // check, then `NaN <= 0` is false (bypassing the short-circuit) and\n // the downstream `memoryResults.length >= topK` never triggers.\n const topK =\n typeof config.recallGraphTopK === \"number\" && Number.isFinite(config.recallGraphTopK)\n ? config.recallGraphTopK\n : 50;\n if (topK <= 0) {\n return {\n ran: false,\n results: [],\n graph: null,\n reason: \"topk-zero\",\n iterations: 0,\n converged: true,\n };\n }\n\n if (options.memories.length === 0) {\n return {\n ran: false,\n results: [],\n graph: null,\n reason: \"empty-input\",\n iterations: 0,\n converged: true,\n };\n }\n\n const graph = buildGraphFromMemories(options.memories);\n\n const damping =\n typeof config.recallGraphDamping === \"number\" && Number.isFinite(config.recallGraphDamping)\n ? config.recallGraphDamping\n : DEFAULT_PPR_DAMPING;\n const iterations =\n typeof config.recallGraphIterations === \"number\" && Number.isFinite(config.recallGraphIterations)\n ? config.recallGraphIterations\n : DEFAULT_PPR_ITERATIONS;\n\n // PPR runs without a topK so we can post-filter non-memory nodes without\n // the trim dropping memory-typed results. We apply topK after projection.\n const ppr = queryGraph(graph, options.seedIds, {\n damping,\n iterations,\n seedWeights: options.seedWeights,\n });\n\n const memoryResults: GraphRecallResult[] = [];\n for (const node of ppr.rankedNodes) {\n const graphNode = graph.nodes.get(node.id);\n if (!graphNode || graphNode.type !== \"memory\") continue;\n memoryResults.push({ id: node.id, score: node.score });\n if (memoryResults.length >= topK) break;\n }\n\n return {\n ran: true,\n results: memoryResults,\n graph,\n reason: \"ran\",\n iterations: ppr.iterations,\n converged: ppr.converged,\n };\n}\n"],"mappings":";;;;;;;;AA4GO,SAAS,eACd,QACA,SACgB;AAChB,MAAI,CAAC,OAAO,oBAAoB;AAC9B,WAAO;AAAA,MACL,KAAK;AAAA,MACL,SAAS,CAAC;AAAA,MACV,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,YAAY;AAAA,MACZ,WAAW;AAAA,IACb;AAAA,EACF;AAMA,QAAM,OACJ,OAAO,OAAO,oBAAoB,YAAY,OAAO,SAAS,OAAO,eAAe,IAChF,OAAO,kBACP;AACN,MAAI,QAAQ,GAAG;AACb,WAAO;AAAA,MACL,KAAK;AAAA,MACL,SAAS,CAAC;AAAA,MACV,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,YAAY;AAAA,MACZ,WAAW;AAAA,IACb;AAAA,EACF;AAEA,MAAI,QAAQ,SAAS,WAAW,GAAG;AACjC,WAAO;AAAA,MACL,KAAK;AAAA,MACL,SAAS,CAAC;AAAA,MACV,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,YAAY;AAAA,MACZ,WAAW;AAAA,IACb;AAAA,EACF;AAEA,QAAM,QAAQ,uBAAuB,QAAQ,QAAQ;AAErD,QAAM,UACJ,OAAO,OAAO,uBAAuB,YAAY,OAAO,SAAS,OAAO,kBAAkB,IACtF,OAAO,qBACP;AACN,QAAM,aACJ,OAAO,OAAO,0BAA0B,YAAY,OAAO,SAAS,OAAO,qBAAqB,IAC5F,OAAO,wBACP;AAIN,QAAM,MAAM,WAAW,OAAO,QAAQ,SAAS;AAAA,IAC7C;AAAA,IACA;AAAA,IACA,aAAa,QAAQ;AAAA,EACvB,CAAC;AAED,QAAM,gBAAqC,CAAC;AAC5C,aAAW,QAAQ,IAAI,aAAa;AAClC,UAAM,YAAY,MAAM,MAAM,IAAI,KAAK,EAAE;AACzC,QAAI,CAAC,aAAa,UAAU,SAAS,SAAU;AAC/C,kBAAc,KAAK,EAAE,IAAI,KAAK,IAAI,OAAO,KAAK,MAAM,CAAC;AACrD,QAAI,cAAc,UAAU,KAAM;AAAA,EACpC;AAEA,SAAO;AAAA,IACL,KAAK;AAAA,IACL,SAAS;AAAA,IACT;AAAA,IACA,QAAQ;AAAA,IACR,YAAY,IAAI;AAAA,IAChB,WAAW,IAAI;AAAA,EACjB;AACF;","names":[]}
|
|
@@ -6,7 +6,10 @@ import {
|
|
|
6
6
|
ProactiveExtractionResultSchema,
|
|
7
7
|
ProactiveQuestionsResultSchema,
|
|
8
8
|
buildProfileConsolidationResultSchema
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-NZLQTHS5.js";
|
|
10
|
+
import {
|
|
11
|
+
normalizeReasoningTrace
|
|
12
|
+
} from "./chunk-54V4BZWP.js";
|
|
10
13
|
import {
|
|
11
14
|
ProfilingCollector
|
|
12
15
|
} from "./chunk-NBNN5GOB.js";
|
|
@@ -15,18 +18,18 @@ import {
|
|
|
15
18
|
} from "./chunk-FEMOX5AD.js";
|
|
16
19
|
import {
|
|
17
20
|
LocalLlmClient
|
|
18
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-R2XRID2N.js";
|
|
22
|
+
import {
|
|
23
|
+
delinearize
|
|
24
|
+
} from "./chunk-VEWZZM3H.js";
|
|
19
25
|
import {
|
|
20
26
|
buildExtensionsFooterForSummary,
|
|
21
27
|
formatDaySummaryMemories,
|
|
22
28
|
loadDaySummaryPrompt
|
|
23
29
|
} from "./chunk-GZCUW5IC.js";
|
|
24
|
-
import {
|
|
25
|
-
delinearize
|
|
26
|
-
} from "./chunk-VEWZZM3H.js";
|
|
27
30
|
import {
|
|
28
31
|
FallbackLlmClient
|
|
29
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-AYXIPSZO.js";
|
|
30
33
|
import {
|
|
31
34
|
buildChatCompletionTokenLimit,
|
|
32
35
|
shouldAssumeOpenAiChatCompletions
|
|
@@ -50,6 +53,22 @@ import {
|
|
|
50
53
|
// src/extraction.ts
|
|
51
54
|
import OpenAI from "openai";
|
|
52
55
|
var PROACTIVE_MIN_CONFIDENCE = 0.8;
|
|
56
|
+
var CONSOLIDATION_RESPONSE_SCHEMA = `{
|
|
57
|
+
"items": [
|
|
58
|
+
{
|
|
59
|
+
"existingId": "id",
|
|
60
|
+
"action": "ADD",
|
|
61
|
+
"mergeWith": "optional-existing-id",
|
|
62
|
+
"updatedContent": "optional replacement content",
|
|
63
|
+
"reason": "brief reason for this action"
|
|
64
|
+
}
|
|
65
|
+
],
|
|
66
|
+
"profileUpdates": ["optional profile update"],
|
|
67
|
+
"entityUpdates": [{"name": "person-jane-doe", "type": "person", "facts": ["Now leads the backend team", "Recently migrated the user service to TypeScript"]}]
|
|
68
|
+
}`;
|
|
69
|
+
function isPlainRecord(value) {
|
|
70
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
71
|
+
}
|
|
53
72
|
function normalizeQuestion(question) {
|
|
54
73
|
const priority = Number.isFinite(question.priority) ? Math.max(0, Math.min(1, question.priority)) : 0.5;
|
|
55
74
|
return {
|
|
@@ -168,7 +187,11 @@ var ExtractionEngine = class {
|
|
|
168
187
|
structuredAttributes: f?.structuredAttributes && typeof f.structuredAttributes === "object" && !Array.isArray(f.structuredAttributes) ? Object.fromEntries(
|
|
169
188
|
Object.entries(f.structuredAttributes).filter(([k, v]) => typeof k === "string" && typeof v === "string")
|
|
170
189
|
) : void 0,
|
|
171
|
-
procedureSteps: Array.isArray(f?.procedureSteps) ? normalizeProcedureSteps(f.procedureSteps) : void 0
|
|
190
|
+
procedureSteps: Array.isArray(f?.procedureSteps) ? normalizeProcedureSteps(f.procedureSteps) : void 0,
|
|
191
|
+
reasoningTrace: (() => {
|
|
192
|
+
const candidate = f?.reasoningTrace && typeof f.reasoningTrace === "object" && !Array.isArray(f.reasoningTrace) ? f.reasoningTrace : f?.reasoning_trace && typeof f.reasoning_trace === "object" && !Array.isArray(f.reasoning_trace) ? f.reasoning_trace : null;
|
|
193
|
+
return candidate ? normalizeReasoningTrace(candidate) ?? void 0 : void 0;
|
|
194
|
+
})()
|
|
172
195
|
})).filter((f) => f.content.length > 0) : [];
|
|
173
196
|
const questions = Array.isArray(parsed?.questions) ? parsed.questions.map((q) => {
|
|
174
197
|
if (typeof q === "string") return { question: q, context: "", priority: 0.5 };
|
|
@@ -195,16 +218,32 @@ var ExtractionEngine = class {
|
|
|
195
218
|
};
|
|
196
219
|
}
|
|
197
220
|
normalizeEntityUpdate(entity) {
|
|
221
|
+
const rawUpdates = isPlainRecord(entity?.updates) ? entity.updates : null;
|
|
222
|
+
const directFacts = Array.isArray(entity?.facts) ? entity.facts.filter((fact) => typeof fact === "string").map((fact) => fact.trim()).filter((fact) => fact.length > 0) : [];
|
|
223
|
+
const updateFacts = rawUpdates && Array.isArray(rawUpdates.facts) ? rawUpdates.facts.filter((fact) => typeof fact === "string").map((fact) => fact.trim()).filter((fact) => fact.length > 0) : [];
|
|
224
|
+
const scalarUpdateFacts = rawUpdates ? Object.keys(rawUpdates).sort((a, b) => a.localeCompare(b)).filter((key) => !["facts", "name", "promptedByQuestion", "structuredSections", "type"].includes(key)).flatMap((key) => {
|
|
225
|
+
const value = rawUpdates[key];
|
|
226
|
+
if (typeof value === "string" && value.trim().length > 0) {
|
|
227
|
+
return [`${key}: ${value.trim()}`];
|
|
228
|
+
}
|
|
229
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
230
|
+
return [`${key}: ${String(value)}`];
|
|
231
|
+
}
|
|
232
|
+
return [];
|
|
233
|
+
}) : [];
|
|
234
|
+
const structuredSectionsSource = Array.isArray(entity?.structuredSections) ? entity.structuredSections : Array.isArray(rawUpdates?.structuredSections) ? rawUpdates.structuredSections : [];
|
|
235
|
+
const name = typeof entity?.name === "string" ? entity.name.trim() : typeof entity?.entityId === "string" ? entity.entityId.trim() : typeof rawUpdates?.name === "string" ? rawUpdates.name.trim() : "";
|
|
236
|
+
const type = typeof entity?.type === "string" && entity.type.trim().length > 0 ? entity.type.trim() : typeof rawUpdates?.type === "string" && rawUpdates.type.trim().length > 0 ? rawUpdates.type.trim() : "other";
|
|
198
237
|
return {
|
|
199
|
-
name
|
|
200
|
-
type
|
|
201
|
-
facts:
|
|
202
|
-
structuredSections:
|
|
238
|
+
name,
|
|
239
|
+
type,
|
|
240
|
+
facts: [...directFacts, ...updateFacts, ...scalarUpdateFacts],
|
|
241
|
+
structuredSections: structuredSectionsSource.length > 0 ? structuredSectionsSource.map((section) => ({
|
|
203
242
|
key: typeof section?.key === "string" ? section.key.trim() : "",
|
|
204
243
|
title: typeof section?.title === "string" ? section.title.trim() : "",
|
|
205
244
|
facts: Array.isArray(section?.facts) ? section.facts.filter((fact) => typeof fact === "string").map((fact) => fact.trim()).filter((fact) => fact.length > 0) : []
|
|
206
245
|
})).filter((section) => section.key.length > 0 && section.title.length > 0 && section.facts.length > 0) : void 0,
|
|
207
|
-
promptedByQuestion: typeof entity?.promptedByQuestion === "string" ? entity.promptedByQuestion : void 0
|
|
246
|
+
promptedByQuestion: typeof entity?.promptedByQuestion === "string" ? entity.promptedByQuestion : typeof rawUpdates?.promptedByQuestion === "string" ? rawUpdates.promptedByQuestion : void 0
|
|
208
247
|
};
|
|
209
248
|
}
|
|
210
249
|
parseJsonObject(content) {
|
|
@@ -264,15 +303,36 @@ var ExtractionEngine = class {
|
|
|
264
303
|
return normalized.summary.length > 0 ? normalized : null;
|
|
265
304
|
}
|
|
266
305
|
sanitizeConsolidationResult(result) {
|
|
267
|
-
const items =
|
|
268
|
-
|
|
269
|
-
const
|
|
306
|
+
const items = [];
|
|
307
|
+
for (const item of Array.isArray(result.items) ? result.items : []) {
|
|
308
|
+
const rawAction = typeof item?.action === "string" ? item.action.toUpperCase() : "SKIP";
|
|
309
|
+
const action = rawAction === "ADD" || rawAction === "MERGE" || rawAction === "UPDATE" || rawAction === "INVALIDATE" || rawAction === "SKIP" ? rawAction : "SKIP";
|
|
310
|
+
const existingId = typeof item?.existingId === "string" ? item.existingId.trim() : typeof item?.newMemoryId === "string" ? item.newMemoryId.trim() : typeof item?.memoryId === "string" ? item.memoryId.trim() : "";
|
|
311
|
+
if (!existingId) continue;
|
|
312
|
+
const mergeWith = typeof item?.mergeWith === "string" ? item.mergeWith : void 0;
|
|
313
|
+
const reason = typeof item?.reason === "string" ? item.reason : "";
|
|
314
|
+
const rawUpdatedContent = typeof item?.updatedContent === "string" ? item.updatedContent : void 0;
|
|
315
|
+
if (!rawUpdatedContent) {
|
|
316
|
+
items.push({ existingId, action, mergeWith, updatedContent: void 0, reason });
|
|
317
|
+
continue;
|
|
318
|
+
}
|
|
319
|
+
const sanitized = sanitizeMemoryContent(rawUpdatedContent);
|
|
270
320
|
if (!sanitized.clean) {
|
|
271
|
-
log.warn(`consolidation item sanitized (${
|
|
321
|
+
log.warn(`consolidation item sanitized (${existingId}); violations=${sanitized.violations.join(", ")}`);
|
|
272
322
|
}
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
323
|
+
items.push({
|
|
324
|
+
existingId,
|
|
325
|
+
action,
|
|
326
|
+
mergeWith,
|
|
327
|
+
updatedContent: sanitized.text,
|
|
328
|
+
reason
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
const profileUpdates = (Array.isArray(result.profileUpdates) ? result.profileUpdates : []).map(
|
|
332
|
+
(update) => typeof update === "string" ? update.trim() : typeof update?.content === "string" ? update.content.trim() : ""
|
|
333
|
+
).filter((update) => update.length > 0);
|
|
334
|
+
const entityUpdates = (Array.isArray(result.entityUpdates) ? result.entityUpdates : []).map((entity) => this.normalizeEntityUpdate(entity)).filter((entity) => entity.name.length > 0);
|
|
335
|
+
return { items, profileUpdates, entityUpdates };
|
|
276
336
|
}
|
|
277
337
|
async applyProactiveQuestionPass(conversation, base) {
|
|
278
338
|
if (!this.config.proactiveExtractionEnabled) return base;
|
|
@@ -762,8 +822,16 @@ var ExtractionEngine = class {
|
|
|
762
822
|
log.debug(
|
|
763
823
|
`extracted ${result.facts.length} facts, ${result.entities.length} entities, ${(result.questions ?? []).length} questions via fallback (${detailed.modelUsed})`
|
|
764
824
|
);
|
|
825
|
+
const normalizedFacts = result.facts.map((f) => {
|
|
826
|
+
if (!f?.reasoningTrace) return f;
|
|
827
|
+
return {
|
|
828
|
+
...f,
|
|
829
|
+
reasoningTrace: normalizeReasoningTrace(f.reasoningTrace) ?? void 0
|
|
830
|
+
};
|
|
831
|
+
});
|
|
765
832
|
const sanitized = this.sanitizeExtractionResult({
|
|
766
833
|
...result,
|
|
834
|
+
facts: normalizedFacts,
|
|
767
835
|
questions: result.questions ?? [],
|
|
768
836
|
identityReflection: result.identityReflection ?? void 0
|
|
769
837
|
}, messageTimestamp);
|
|
@@ -831,6 +899,7 @@ Memory categories \u2014 use the MOST SPECIFIC category that fits:
|
|
|
831
899
|
- skill: Demonstrated capabilities
|
|
832
900
|
- rule: Explicit operational rules or constraints
|
|
833
901
|
- procedure: Repeatable workflows \u2014 use when the user describes a multi-step play (\u22652 ordered steps). Put the human-readable trigger/context in "content" (e.g. "When you deploy\u2026") and list steps in "procedureSteps" as [{"order":1,"intent":"\u2026"}, \u2026] mirroring the gateway extraction schema.
|
|
902
|
+
- reasoning_trace: Stored solution chains \u2014 use when the user narrates HOW they solved a specific problem step-by-step ("here's how I figured out\u2026", "the debugging went like this\u2026"). Put a short title in "content" (e.g. "How I debugged the staging latency spike") and the chain in "reasoningTrace": {"steps":[{"order":1,"description":"\u2026"}, \u2026], "finalAnswer":"\u2026", "observedOutcome":"\u2026" (optional)}. Require \u22652 ordered steps and a finalAnswer. Do NOT use for ordinary decisions (prefer "decision") or reusable workflows (prefer "procedure").
|
|
834
903
|
|
|
835
904
|
IMPORTANT: Do NOT label everything as "fact". Use "decision" for architectural choices, "commitment" for deadlines/promises, "principle" for reusable rules, "correction" for when the user rejects a suggestion, etc.
|
|
836
905
|
|
|
@@ -892,7 +961,7 @@ Also generate:
|
|
|
892
961
|
|
|
893
962
|
Output JSON:
|
|
894
963
|
{
|
|
895
|
-
"facts": [{"category": "decision", "content": "Chose PostgreSQL over MongoDB for the user service", "importance": 8, "confidence": 0.9, "structuredAttributes": {"chosen": "PostgreSQL", "rejected": "MongoDB"}}, {"category": "procedure", "content": "When you cut a hotfix release, follow the checklist", "importance": 8, "confidence": 0.9, "procedureSteps": [{"order": 1, "intent": "Branch from main and cherry-pick the fix"}, {"order": 2, "intent": "Run CI and tag the release"}]}, {"category": "commitment", "content": "Must ship v2.0 API by end of March", "importance": 10, "confidence": 1.0, "structuredAttributes": {"deadline": "end of March", "deliverable": "v2.0 API"}}, {"category": "fact", "content": "The store backend uses Redis for session caching", "importance": 6, "confidence": 0.95, "entityRef": "project-acme-store"}, {"category": "principle", "content": "Always run migrations in a transaction to avoid partial schema updates", "importance": 8, "confidence": 0.9}],
|
|
964
|
+
"facts": [{"category": "decision", "content": "Chose PostgreSQL over MongoDB for the user service", "importance": 8, "confidence": 0.9, "structuredAttributes": {"chosen": "PostgreSQL", "rejected": "MongoDB"}}, {"category": "procedure", "content": "When you cut a hotfix release, follow the checklist", "importance": 8, "confidence": 0.9, "procedureSteps": [{"order": 1, "intent": "Branch from main and cherry-pick the fix"}, {"order": 2, "intent": "Run CI and tag the release"}]}, {"category": "reasoning_trace", "content": "How I debugged the staging latency spike", "importance": 7, "confidence": 0.9, "reasoningTrace": {"steps": [{"order": 1, "description": "Checked CPU/memory dashboards \u2014 both were flat"}, {"order": 2, "description": "Ran a traceroute and saw retries against the cache tier"}, {"order": 3, "description": "Tailed cache-tier logs and spotted eviction storms"}], "finalAnswer": "Root cause was an undersized eviction policy on the session cache", "observedOutcome": "Increased cache size, p95 returned to baseline within 10 minutes"}}, {"category": "commitment", "content": "Must ship v2.0 API by end of March", "importance": 10, "confidence": 1.0, "structuredAttributes": {"deadline": "end of March", "deliverable": "v2.0 API"}}, {"category": "fact", "content": "The store backend uses Redis for session caching", "importance": 6, "confidence": 0.95, "entityRef": "project-acme-store"}, {"category": "principle", "content": "Always run migrations in a transaction to avoid partial schema updates", "importance": 8, "confidence": 0.9}],
|
|
896
965
|
"entities": [{"name": "person-jane-doe", "type": "person", "facts": ["Works at Acme Corp", "Prefers Python over JavaScript"], "structuredSections": [{"key": "beliefs", "title": "Beliefs", "facts": ["Python is a better fit than JavaScript for backend work."]}]}, {"name": "project-acme-store", "type": "project", "facts": ["Built with Next.js", "Deployed on Vercel"]}],
|
|
897
966
|
"profileUpdates": ["User prefers dark mode in all editors"],
|
|
898
967
|
"questions": [{"question": "Which cloud provider hosts the staging environment?", "context": "Came up during deployment discussion", "priority": 0.5}],
|
|
@@ -969,7 +1038,7 @@ ${truncatedConversation}`;
|
|
|
969
1038
|
|
|
970
1039
|
Respond with valid JSON matching this schema:
|
|
971
1040
|
{
|
|
972
|
-
"facts": [{"category": "decision", "content": "Chose React over Vue for the dashboard rewrite", "importance": 8, "confidence": 0.9, "tags": ["frontend"], "structuredAttributes": {"chosen": "React", "rejected": "Vue"}}, {"category": "fact", "content": "The API gateway uses rate limiting at 1000 req/min", "importance": 6, "confidence": 0.95, "tags": ["infra"], "entityRef": "project-dashboard", "structuredAttributes": {"rate_limit": "1000 req/min"}}],
|
|
1041
|
+
"facts": [{"category": "decision", "content": "Chose React over Vue for the dashboard rewrite", "importance": 8, "confidence": 0.9, "tags": ["frontend"], "structuredAttributes": {"chosen": "React", "rejected": "Vue"}}, {"category": "fact", "content": "The API gateway uses rate limiting at 1000 req/min", "importance": 6, "confidence": 0.95, "tags": ["infra"], "entityRef": "project-dashboard", "structuredAttributes": {"rate_limit": "1000 req/min"}}, {"category": "reasoning_trace", "content": "How I chose the dashboard rewrite framework", "confidence": 0.9, "tags": ["frontend"], "reasoningTrace": {"steps": [{"order": 1, "description": "Listed constraints: SSR needed, team mostly JS"}, {"order": 2, "description": "Ran a spike in Vue 3 \u2014 worked, but ecosystem felt thin for our needs"}, {"order": 3, "description": "Ran the same spike in React \u2014 integrated faster with Next.js"}], "finalAnswer": "Picked React with Next.js for SSR + ecosystem fit"}}],
|
|
973
1042
|
"entities": [{"name": "person-sarah-chen", "type": "person", "facts": ["Leads the backend team", "Joined from Google in 2024"], "structuredSections": [{"key": "beliefs", "title": "Beliefs", "facts": ["Small teams should own whole systems."]}]}, {"name": "project-dashboard", "type": "project", "facts": ["React-based admin panel", "Deployed on AWS ECS"]}],
|
|
974
1043
|
"profileUpdates": ["User prefers TypeScript over plain JavaScript"],
|
|
975
1044
|
"questions": [{"question": "What database does the analytics service use?", "context": "Came up during discussion of migration plan", "priority": 0.5}],
|
|
@@ -1015,7 +1084,8 @@ Respond with valid JSON matching this schema:
|
|
|
1015
1084
|
"moment",
|
|
1016
1085
|
"skill",
|
|
1017
1086
|
"rule",
|
|
1018
|
-
"procedure"
|
|
1087
|
+
"procedure",
|
|
1088
|
+
"reasoning_trace"
|
|
1019
1089
|
]);
|
|
1020
1090
|
const allowedEntityTypes = /* @__PURE__ */ new Set([
|
|
1021
1091
|
"person",
|
|
@@ -1073,6 +1143,7 @@ Memory categories:
|
|
|
1073
1143
|
- skill: Capabilities the user or agent has demonstrated (e.g., "user is proficient with Kubernetes")${this.config.causalRuleExtractionEnabled ? `
|
|
1074
1144
|
- rule: Causal rules discovered through experience (format: "IF <condition> THEN <action/outcome>", e.g., "IF Shopify API returns 401 THEN the admin token is missing read_products scope")` : ""}
|
|
1075
1145
|
- procedure: A reusable workflow the user wants remembered the same way across sessions. Set category to "procedure". Use "content" for a short title that includes explicit trigger phrasing (e.g. "When you deploy to production\u2026", "Whenever you ship a release\u2026"). Add "procedureSteps": an array of at least two objects {"order": number, "intent": "concrete step description"} in execution order. Optional per-step "toolCall": {"kind": "\u2026", "signature": "\u2026"}, "expectedOutcome", "optional": true.
|
|
1146
|
+
- reasoning_trace: A stored solution chain / chain-of-thought the user walked through to solve a problem (e.g. "Here's how I debugged the latency spike: first I checked\u2026, then I\u2026, finally I\u2026"). Set category to "reasoning_trace". Use "content" for a short title summarising the problem (e.g. "How I debugged the staging latency spike"). Add "reasoningTrace": {"steps": [{"order": number, "description": "what happened at this step"}, \u2026], "finalAnswer": "the conclusion or answer", "observedOutcome": "optional confirmation of how it played out"}. Require at least two ordered steps AND a finalAnswer. Use this category only when the user explicitly narrates their reasoning \u2014 not for ordinary decisions (use "decision") or reusable workflows (use "procedure").
|
|
1076
1147
|
|
|
1077
1148
|
Rules:
|
|
1078
1149
|
- Only extract genuinely NEW information worth remembering across sessions
|
|
@@ -1193,15 +1264,10 @@ Consolidate the new memories against existing ones.`
|
|
|
1193
1264
|
);
|
|
1194
1265
|
if (fallbackResult) {
|
|
1195
1266
|
log.debug(`consolidation: ${fallbackResult.items.length} decisions via fallback`);
|
|
1196
|
-
const normalizedEntityUpdates = fallbackResult.entityUpdates.map((entity) => this.normalizeEntityUpdate(entity));
|
|
1197
1267
|
return this.sanitizeConsolidationResult({
|
|
1198
|
-
items: fallbackResult.items
|
|
1199
|
-
...item,
|
|
1200
|
-
mergeWith: item.mergeWith ?? void 0,
|
|
1201
|
-
updatedContent: item.updatedContent ?? void 0
|
|
1202
|
-
})),
|
|
1268
|
+
items: fallbackResult.items,
|
|
1203
1269
|
profileUpdates: fallbackResult.profileUpdates,
|
|
1204
|
-
entityUpdates:
|
|
1270
|
+
entityUpdates: fallbackResult.entityUpdates
|
|
1205
1271
|
});
|
|
1206
1272
|
}
|
|
1207
1273
|
if (!this.client) {
|
|
@@ -1233,19 +1299,7 @@ New memories to consolidate:
|
|
|
1233
1299
|
${newList}
|
|
1234
1300
|
|
|
1235
1301
|
Respond with valid JSON only, matching this schema:
|
|
1236
|
-
{
|
|
1237
|
-
"items": [
|
|
1238
|
-
{
|
|
1239
|
-
"existingId": "id",
|
|
1240
|
-
"action": "ADD",
|
|
1241
|
-
"mergeWith": "optional-existing-id",
|
|
1242
|
-
"updatedContent": "optional replacement content",
|
|
1243
|
-
"reason": "brief reason for this action"
|
|
1244
|
-
}
|
|
1245
|
-
],
|
|
1246
|
-
"profileUpdates": ["optional profile update"],
|
|
1247
|
-
"entityUpdates": [{"name": "person-jane-doe", "type": "person", "facts": ["Now leads the backend team", "Recently migrated the user service to TypeScript"]}]
|
|
1248
|
-
}`;
|
|
1302
|
+
${CONSOLIDATION_RESPONSE_SCHEMA}`;
|
|
1249
1303
|
const response = await this.client.chat.completions.create({
|
|
1250
1304
|
model: this.config.model,
|
|
1251
1305
|
messages: [
|
|
@@ -1280,25 +1334,13 @@ Respond with valid JSON only, matching this schema:
|
|
|
1280
1334
|
tokenUsage: cUsage ? { input: cUsage.prompt_tokens, output: cUsage.completion_tokens, total: cUsage.total_tokens } : void 0
|
|
1281
1335
|
});
|
|
1282
1336
|
if (parsed && Array.isArray(parsed.items)) {
|
|
1283
|
-
const normalizedItems = parsed.items.map((item) => {
|
|
1284
|
-
const rawAction = typeof item?.action === "string" ? item.action.toUpperCase() : "SKIP";
|
|
1285
|
-
const action = rawAction === "ADD" || rawAction === "MERGE" || rawAction === "UPDATE" || rawAction === "INVALIDATE" || rawAction === "SKIP" ? rawAction : "SKIP";
|
|
1286
|
-
return {
|
|
1287
|
-
existingId: typeof item?.existingId === "string" ? item.existingId : typeof item?.newMemoryId === "string" ? item.newMemoryId : "",
|
|
1288
|
-
action,
|
|
1289
|
-
mergeWith: typeof item?.mergeWith === "string" ? item.mergeWith : void 0,
|
|
1290
|
-
updatedContent: typeof item?.updatedContent === "string" ? item.updatedContent : void 0,
|
|
1291
|
-
reason: typeof item?.reason === "string" ? item.reason : ""
|
|
1292
|
-
};
|
|
1293
|
-
}).filter((item) => item.existingId.length > 0);
|
|
1294
|
-
const normalizedEntityUpdates = Array.isArray(parsed.entityUpdates) ? parsed.entityUpdates.map((entity) => this.normalizeEntityUpdate(entity)).filter((entity) => entity.name.length > 0) : [];
|
|
1295
1337
|
log.debug(
|
|
1296
|
-
`consolidation: ${
|
|
1338
|
+
`consolidation: ${parsed.items.length} decisions`
|
|
1297
1339
|
);
|
|
1298
1340
|
return this.sanitizeConsolidationResult({
|
|
1299
|
-
items:
|
|
1300
|
-
profileUpdates: Array.isArray(parsed.profileUpdates) ? parsed.profileUpdates
|
|
1301
|
-
entityUpdates:
|
|
1341
|
+
items: parsed.items,
|
|
1342
|
+
profileUpdates: Array.isArray(parsed.profileUpdates) ? parsed.profileUpdates : [],
|
|
1343
|
+
entityUpdates: Array.isArray(parsed.entityUpdates) ? parsed.entityUpdates : []
|
|
1302
1344
|
});
|
|
1303
1345
|
}
|
|
1304
1346
|
log.warn("consolidation returned no parsed output");
|
|
@@ -1349,13 +1391,7 @@ New memories to consolidate:
|
|
|
1349
1391
|
${newList}
|
|
1350
1392
|
|
|
1351
1393
|
Respond with valid JSON matching this schema:
|
|
1352
|
-
{
|
|
1353
|
-
"items": [
|
|
1354
|
-
{"memoryId": "id", "action": "ADD|MERGE|UPDATE|INVALIDATE|SKIP", "reason": "why", "updatedContent": "optional new content"}
|
|
1355
|
-
],
|
|
1356
|
-
"profileUpdates": [{"section": "section name", "content": "new bullet"}],
|
|
1357
|
-
"entityUpdates": [{"entityId": "id", "updates": {"field": "value"}}]
|
|
1358
|
-
}`;
|
|
1394
|
+
${CONSOLIDATION_RESPONSE_SCHEMA}`;
|
|
1359
1395
|
const response = await this.localLlm.chatCompletion(
|
|
1360
1396
|
[
|
|
1361
1397
|
{ role: "system", content: "You are a memory consolidation system. Output valid JSON only." },
|
|
@@ -2224,4 +2260,4 @@ ${memoryList}` }
|
|
|
2224
2260
|
export {
|
|
2225
2261
|
ExtractionEngine
|
|
2226
2262
|
};
|
|
2227
|
-
//# sourceMappingURL=chunk-
|
|
2263
|
+
//# sourceMappingURL=chunk-DIXB44VE.js.map
|