@remnic/core 1.0.3 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/abort-error.d.ts +32 -0
- package/dist/abort-error.js +11 -0
- package/dist/access-audit.d.ts +56 -0
- package/dist/access-audit.js +9 -0
- package/dist/access-audit.js.map +1 -0
- package/dist/access-cli.js +72 -54
- package/dist/access-cli.js.map +1 -1
- package/dist/access-http.d.ts +16 -8
- package/dist/access-http.js +25 -17
- package/dist/access-mcp.d.ts +16 -8
- package/dist/access-mcp.js +28 -6
- package/dist/access-schema.d.ts +130 -39
- package/dist/access-schema.js +5 -1
- package/dist/access-service-Br8ZydTK.d.ts +827 -0
- package/dist/access-service.d.ts +20 -660
- package/dist/access-service.js +22 -14
- package/dist/bootstrap.d.ts +6 -3
- package/dist/briefing.d.ts +1 -0
- package/dist/briefing.js +6 -5
- 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 +5 -5
- package/dist/causal-behavior.js +4 -4
- package/dist/causal-chain.js +2 -2
- package/dist/causal-consolidation.js +17 -16
- 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-QKAH5B6E.js → chunk-3GXCSUXR.js} +94 -6
- package/dist/chunk-3GXCSUXR.js.map +1 -0
- package/dist/{chunk-POBPGDWI.js → chunk-3OGMS3PE.js} +2 -2
- 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-6AUUAZEX.js +150 -0
- package/dist/chunk-6AUUAZEX.js.map +1 -0
- package/dist/{chunk-HITJFT7E.js → chunk-7I7FKFZH.js} +28 -21
- package/dist/chunk-7I7FKFZH.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-X4WESCKA.js → chunk-B5WXLVDY.js} +187 -6
- package/dist/chunk-B5WXLVDY.js.map +1 -0
- package/dist/{chunk-RCICHSHL.js → chunk-BGJGXLZ7.js} +111 -2
- package/dist/{chunk-RCICHSHL.js.map → chunk-BGJGXLZ7.js.map} +1 -1
- package/dist/{chunk-OJFGVJS6.js → chunk-BK2EFTE2.js} +319 -18
- package/dist/chunk-BK2EFTE2.js.map +1 -0
- package/dist/chunk-C4SQJZAF.js +486 -0
- package/dist/chunk-C4SQJZAF.js.map +1 -0
- package/dist/{chunk-GJQPH5G3.js → chunk-CUPFXL3J.js} +2 -2
- package/dist/chunk-DF3RVK3X.js +119 -0
- package/dist/chunk-DF3RVK3X.js.map +1 -0
- package/dist/{chunk-PMB3WGDL.js → chunk-DFTTJYSO.js} +167 -7
- package/dist/chunk-DFTTJYSO.js.map +1 -0
- package/dist/chunk-DGVM5SFL.js +69 -0
- package/dist/chunk-DGVM5SFL.js.map +1 -0
- package/dist/chunk-EIR5VLIH.js +90 -0
- package/dist/chunk-EIR5VLIH.js.map +1 -0
- package/dist/{chunk-PAORGQRI.js → chunk-EPQJM2GC.js} +37 -23
- package/dist/chunk-EPQJM2GC.js.map +1 -0
- package/dist/{chunk-POMSFKTB.js → chunk-F5VP6YCB.js} +368 -10
- 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-74JR4N5J.js → chunk-FVA6TGI3.js} +2 -2
- package/dist/chunk-GDFS42HT.js +206 -0
- package/dist/chunk-GDFS42HT.js.map +1 -0
- package/dist/{chunk-BKQJBXXX.js → chunk-GGD5W7TB.js} +2 -2
- package/dist/chunk-GGD5W7TB.js.map +1 -0
- package/dist/{chunk-V7XCAHIB.js → chunk-GKFXUTJ2.js} +508 -26
- package/dist/chunk-GKFXUTJ2.js.map +1 -0
- package/dist/{chunk-NSB3WSYS.js → chunk-HK3FGIEW.js} +278 -3
- package/dist/chunk-HK3FGIEW.js.map +1 -0
- package/dist/{chunk-AAI7JARD.js → chunk-HMDCOMYU.js} +8 -11
- package/dist/chunk-HMDCOMYU.js.map +1 -0
- package/dist/chunk-IISBCCWR.js +52 -0
- package/dist/chunk-IISBCCWR.js.map +1 -0
- package/dist/{chunk-YFYL2SIJ.js → chunk-INXV5JBT.js} +290 -46
- package/dist/chunk-INXV5JBT.js.map +1 -0
- package/dist/chunk-JBMSGZEQ.js +441 -0
- package/dist/chunk-JBMSGZEQ.js.map +1 -0
- package/dist/{chunk-UPMD5XND.js → chunk-JL2PU6AI.js} +16 -5
- package/dist/chunk-JL2PU6AI.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-AYPYCLR7.js → chunk-KUB6JU6H.js} +4 -4
- package/dist/chunk-KVBLZUKV.js +173 -0
- package/dist/chunk-KVBLZUKV.js.map +1 -0
- package/dist/chunk-LBLXEFWK.js +51 -0
- package/dist/chunk-LBLXEFWK.js.map +1 -0
- package/dist/{chunk-U2IQTSBY.js → chunk-LTCGGW2D.js} +1 -1
- package/dist/chunk-LTCGGW2D.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-PVGDJXVK.js +21 -0
- package/dist/chunk-PVGDJXVK.js.map +1 -0
- package/dist/chunk-PVPWZSSI.js +37 -0
- package/dist/chunk-PVPWZSSI.js.map +1 -0
- 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-JROGC36Y.js → chunk-RGLL5SPU.js} +2 -2
- package/dist/{chunk-2VFW5K5U.js → chunk-S3EEFKNY.js} +103 -65
- package/dist/chunk-S3EEFKNY.js.map +1 -0
- package/dist/chunk-SOBJ6NEY.js +18 -0
- package/dist/chunk-SOBJ6NEY.js.map +1 -0
- package/dist/{chunk-MYQWXITD.js → chunk-SPI27QT6.js} +2 -2
- 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-S4LX5EBI.js → chunk-VBVG2M5G.js} +64 -10
- package/dist/chunk-VBVG2M5G.js.map +1 -0
- package/dist/{chunk-KWP7T3DP.js → chunk-VDX363PS.js} +2 -2
- package/dist/{chunk-XMGSSBFX.js → chunk-VYM3VWOF.js} +1560 -244
- package/dist/chunk-VYM3VWOF.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-ECKDIK5F.js → chunk-WVVA7F5A.js} +2 -2
- 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-KEG4GNGI.js → chunk-XZ2TIKGC.js} +38 -8
- package/dist/chunk-XZ2TIKGC.js.map +1 -0
- package/dist/chunk-Y4FHOFJ2.js +140 -0
- package/dist/chunk-Y4FHOFJ2.js.map +1 -0
- package/dist/chunk-YNB73F22.js +137 -0
- package/dist/chunk-YNB73F22.js.map +1 -0
- package/dist/{chunk-7PA4OZEU.js → chunk-YNQKWQT4.js} +55 -30
- package/dist/chunk-YNQKWQT4.js.map +1 -0
- package/dist/chunk-ZAIM4TUE.js +488 -0
- package/dist/chunk-ZAIM4TUE.js.map +1 -0
- package/dist/{chunk-BTY5RRRF.js → chunk-ZEM3OK2K.js} +5 -5
- package/dist/chunk-ZZTOURJI.js +91 -0
- package/dist/chunk-ZZTOURJI.js.map +1 -0
- package/dist/{cli-DwIBnp2g.d.ts → cli-BkeRaYfk.d.ts} +2 -2
- package/dist/cli.d.ts +13 -5
- package/dist/cli.js +45 -33
- package/dist/config.js +1 -1
- 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-review-WIUBAR52.js +21 -0
- package/dist/contradiction-review-WIUBAR52.js.map +1 -0
- package/dist/contradiction-scan-E3GJTI4F.js +412 -0
- 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.d.ts +77 -0
- package/dist/direct-answer-wiring.js +10 -0
- package/dist/direct-answer-wiring.js.map +1 -0
- package/dist/direct-answer.d.ts +106 -0
- package/dist/direct-answer.js +10 -0
- package/dist/direct-answer.js.map +1 -0
- package/dist/{engine-X7X3AAG3.js → engine-F3GOXGE5.js} +7 -6
- package/dist/engine-F3GOXGE5.js.map +1 -0
- package/dist/entity-retrieval.d.ts +1 -0
- package/dist/entity-retrieval.js +6 -5
- 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 +7 -6
- package/dist/fallback-llm.d.ts +11 -2
- package/dist/fallback-llm.js +2 -2
- 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/harmonic-retrieval.js +2 -1
- package/dist/importance.js +1 -1
- package/dist/index.d.ts +589 -138
- package/dist/index.js +531 -403
- package/dist/index.js.map +1 -1
- package/dist/intent.js +1 -1
- package/dist/local-llm.d.ts +10 -3
- package/dist/local-llm.js +1 -1
- 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 +24 -14
- package/dist/{orchestrator-B9kwlCep.d.ts → orchestrator-CmJ-NTdJ.d.ts} +254 -10
- package/dist/orchestrator.d.ts +6 -3
- package/dist/orchestrator.js +59 -48
- 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 +2 -1
- 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 +39 -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/resolution-QBTDHTG7.js +100 -0
- package/dist/resolution-QBTDHTG7.js.map +1 -0
- package/dist/resolve-provider-secret.d.ts +24 -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 +301 -45
- 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 +20 -6
- package/dist/semantic-rule-promotion.js +6 -5
- package/dist/semantic-rule-verifier.js +6 -5
- package/dist/storage.d.ts +82 -1
- package/dist/storage.js +5 -4
- package/dist/summarizer.js +4 -4
- package/dist/temporal-supersession.d.ts +1 -0
- package/dist/tier-migration.d.ts +2 -1
- package/dist/types-DJhqDJUV.d.ts +50 -0
- package/dist/types.d.ts +309 -3
- package/dist/types.js +1 -1
- package/dist/verified-recall.js +6 -5
- package/package.json +1 -1
- package/dist/chunk-2VFW5K5U.js.map +0 -1
- package/dist/chunk-6ZH4TU6I.js.map +0 -1
- package/dist/chunk-7PA4OZEU.js.map +0 -1
- package/dist/chunk-AAI7JARD.js.map +0 -1
- package/dist/chunk-BKQJBXXX.js.map +0 -1
- package/dist/chunk-HITJFT7E.js.map +0 -1
- package/dist/chunk-J4IYOZZ5.js.map +0 -1
- package/dist/chunk-KEG4GNGI.js.map +0 -1
- package/dist/chunk-LAYN4LDC.js +0 -267
- package/dist/chunk-LAYN4LDC.js.map +0 -1
- package/dist/chunk-MTLYEMJB.js.map +0 -1
- package/dist/chunk-NSB3WSYS.js.map +0 -1
- package/dist/chunk-OJFGVJS6.js.map +0 -1
- package/dist/chunk-PAORGQRI.js.map +0 -1
- package/dist/chunk-PMB3WGDL.js.map +0 -1
- package/dist/chunk-POMSFKTB.js.map +0 -1
- package/dist/chunk-QDYXG4CS.js.map +0 -1
- package/dist/chunk-QKAH5B6E.js.map +0 -1
- package/dist/chunk-QNJMBKFK.js.map +0 -1
- package/dist/chunk-S4LX5EBI.js.map +0 -1
- package/dist/chunk-U2IQTSBY.js.map +0 -1
- package/dist/chunk-UEYA6UC7.js.map +0 -1
- package/dist/chunk-UPMD5XND.js.map +0 -1
- package/dist/chunk-UVJFDP7P.js +0 -202
- package/dist/chunk-UVJFDP7P.js.map +0 -1
- package/dist/chunk-V7XCAHIB.js.map +0 -1
- package/dist/chunk-X4WESCKA.js.map +0 -1
- package/dist/chunk-XMGSSBFX.js.map +0 -1
- package/dist/chunk-YFYL2SIJ.js.map +0 -1
- /package/dist/{engine-X7X3AAG3.js.map → abort-error.js.map} +0 -0
- /package/dist/{chunk-POBPGDWI.js.map → chunk-3OGMS3PE.js.map} +0 -0
- /package/dist/{chunk-GJQPH5G3.js.map → chunk-CUPFXL3J.js.map} +0 -0
- /package/dist/{chunk-74JR4N5J.js.map → chunk-FVA6TGI3.js.map} +0 -0
- /package/dist/{chunk-AYPYCLR7.js.map → chunk-KUB6JU6H.js.map} +0 -0
- /package/dist/{chunk-4NRAJUDS.js.map → chunk-RBBWYEFJ.js.map} +0 -0
- /package/dist/{chunk-JROGC36Y.js.map → chunk-RGLL5SPU.js.map} +0 -0
- /package/dist/{chunk-MYQWXITD.js.map → chunk-SPI27QT6.js.map} +0 -0
- /package/dist/{chunk-KWP7T3DP.js.map → chunk-VDX363PS.js.map} +0 -0
- /package/dist/{chunk-ECKDIK5F.js.map → chunk-WVVA7F5A.js.map} +0 -0
- /package/dist/{chunk-3QFQGRHO.js.map → chunk-XMHBH5H6.js.map} +0 -0
- /package/dist/{chunk-BTY5RRRF.js.map → chunk-ZEM3OK2K.js.map} +0 -0
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
// src/reasoning-trace-types.ts
|
|
2
|
+
function normalizeReasoningTraceSteps(raw) {
|
|
3
|
+
if (!Array.isArray(raw)) return [];
|
|
4
|
+
const out = [];
|
|
5
|
+
for (let i = 0; i < raw.length; i++) {
|
|
6
|
+
const s = raw[i];
|
|
7
|
+
let description = "";
|
|
8
|
+
let orderRaw;
|
|
9
|
+
if (typeof s === "string") {
|
|
10
|
+
description = s.trim();
|
|
11
|
+
} else if (s && typeof s === "object") {
|
|
12
|
+
const o = s;
|
|
13
|
+
if (typeof o.description === "string") description = o.description.trim();
|
|
14
|
+
else if (typeof o.intent === "string") description = o.intent.trim();
|
|
15
|
+
else if (typeof o.step === "string") description = o.step.trim();
|
|
16
|
+
else if (typeof o.text === "string") description = o.text.trim();
|
|
17
|
+
orderRaw = o.order;
|
|
18
|
+
}
|
|
19
|
+
if (!description) continue;
|
|
20
|
+
const order = typeof orderRaw === "number" && Number.isFinite(orderRaw) ? Math.max(1, Math.floor(orderRaw)) : i + 1;
|
|
21
|
+
out.push({ order, description });
|
|
22
|
+
}
|
|
23
|
+
return out;
|
|
24
|
+
}
|
|
25
|
+
function normalizeReasoningTrace(raw) {
|
|
26
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
|
|
27
|
+
const o = raw;
|
|
28
|
+
const steps = normalizeReasoningTraceSteps(o.steps);
|
|
29
|
+
const finalAnswer = typeof o.finalAnswer === "string" ? o.finalAnswer.trim() : typeof o.final_answer === "string" ? o.final_answer.trim() : "";
|
|
30
|
+
if (steps.length < 2 || finalAnswer.length === 0) return null;
|
|
31
|
+
const observedRaw = typeof o.observedOutcome === "string" ? o.observedOutcome : typeof o.observed_outcome === "string" ? o.observed_outcome : void 0;
|
|
32
|
+
const observedOutcome = observedRaw?.trim();
|
|
33
|
+
return {
|
|
34
|
+
steps,
|
|
35
|
+
finalAnswer,
|
|
36
|
+
observedOutcome: observedOutcome && observedOutcome.length > 0 ? observedOutcome : void 0
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function buildReasoningTraceMarkdownBody(trace) {
|
|
40
|
+
const sorted = [...trace.steps].sort((a, b) => a.order - b.order);
|
|
41
|
+
const lines = [];
|
|
42
|
+
for (const step of sorted) {
|
|
43
|
+
const n = Number.isFinite(step.order) ? Math.max(1, Math.floor(step.order)) : 1;
|
|
44
|
+
lines.push(`## Step ${n}`);
|
|
45
|
+
lines.push("");
|
|
46
|
+
lines.push(step.description.trim());
|
|
47
|
+
lines.push("");
|
|
48
|
+
}
|
|
49
|
+
lines.push("## Final Answer");
|
|
50
|
+
lines.push("");
|
|
51
|
+
lines.push(trace.finalAnswer.trim());
|
|
52
|
+
lines.push("");
|
|
53
|
+
if (trace.observedOutcome && trace.observedOutcome.trim().length > 0) {
|
|
54
|
+
lines.push("## Observed Outcome");
|
|
55
|
+
lines.push("");
|
|
56
|
+
lines.push(trace.observedOutcome.trim());
|
|
57
|
+
lines.push("");
|
|
58
|
+
}
|
|
59
|
+
return lines.join("\n").trimEnd() + "\n";
|
|
60
|
+
}
|
|
61
|
+
function buildReasoningTracePersistBody(title, trace) {
|
|
62
|
+
const head = typeof title === "string" ? title.trim() : "";
|
|
63
|
+
const body = buildReasoningTraceMarkdownBody(trace);
|
|
64
|
+
if (!head) return body;
|
|
65
|
+
return `${head}
|
|
66
|
+
|
|
67
|
+
${body}`.trimEnd() + "\n";
|
|
68
|
+
}
|
|
69
|
+
var STEP_HEADER_RE = /^##\s+Step\s+(\d+)\s*$/im;
|
|
70
|
+
var FINAL_HEADER_RE = /^##\s+Final Answer\s*$/im;
|
|
71
|
+
var OBSERVED_HEADER_RE = /^##\s+Observed Outcome\s*$/im;
|
|
72
|
+
function parseReasoningTraceFromBody(content) {
|
|
73
|
+
const text = content.replace(/\r\n/g, "\n").trim();
|
|
74
|
+
if (!text) return null;
|
|
75
|
+
const stepMatches = [...text.matchAll(new RegExp(STEP_HEADER_RE.source, "gim"))];
|
|
76
|
+
const finalMatch = FINAL_HEADER_RE.exec(text);
|
|
77
|
+
if (stepMatches.length === 0 || !finalMatch) return null;
|
|
78
|
+
const observedMatch = OBSERVED_HEADER_RE.exec(text);
|
|
79
|
+
const steps = [];
|
|
80
|
+
for (let i = 0; i < stepMatches.length; i++) {
|
|
81
|
+
const m = stepMatches[i];
|
|
82
|
+
const order = Number.parseInt(m[1] ?? String(i + 1), 10);
|
|
83
|
+
const start = (m.index ?? 0) + m[0].length;
|
|
84
|
+
const nextStepStart = i + 1 < stepMatches.length ? stepMatches[i + 1].index ?? text.length : text.length;
|
|
85
|
+
const end = Math.min(nextStepStart, finalMatch.index ?? text.length);
|
|
86
|
+
const block = text.slice(start, end).trim();
|
|
87
|
+
const description = block.split("\n").map((l) => l.trim()).filter((l) => l.length > 0).join(" ");
|
|
88
|
+
if (!description) continue;
|
|
89
|
+
steps.push({
|
|
90
|
+
order: Number.isFinite(order) ? order : i + 1,
|
|
91
|
+
description
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
if (steps.length < 2) return null;
|
|
95
|
+
const finalStart = (finalMatch.index ?? 0) + finalMatch[0].length;
|
|
96
|
+
const finalEnd = observedMatch?.index ?? text.length;
|
|
97
|
+
const finalAnswer = text.slice(finalStart, finalEnd).split("\n").map((l) => l.trim()).filter((l) => l.length > 0).join(" ").trim();
|
|
98
|
+
if (!finalAnswer) return null;
|
|
99
|
+
let observedOutcome;
|
|
100
|
+
if (observedMatch) {
|
|
101
|
+
const obsStart = (observedMatch.index ?? 0) + observedMatch[0].length;
|
|
102
|
+
const raw = text.slice(obsStart).split("\n").map((l) => l.trim()).filter((l) => l.length > 0).join(" ").trim();
|
|
103
|
+
if (raw) observedOutcome = raw;
|
|
104
|
+
}
|
|
105
|
+
return { steps, finalAnswer, observedOutcome };
|
|
106
|
+
}
|
|
107
|
+
function looksLikeReasoningTrace(message) {
|
|
108
|
+
if (typeof message !== "string") return false;
|
|
109
|
+
const text = message.trim();
|
|
110
|
+
if (text.length < 80) return false;
|
|
111
|
+
const stepMarkerRes = [
|
|
112
|
+
/\bstep\s+\d+\s*[:.\-]/i,
|
|
113
|
+
/^\s*\d+[.)]\s+\S/,
|
|
114
|
+
/^\s*(first|second|third|fourth|finally|then|next)\b[,:]/i
|
|
115
|
+
];
|
|
116
|
+
const lines = text.split(/\r?\n/);
|
|
117
|
+
let stepCount = 0;
|
|
118
|
+
for (const line of lines) {
|
|
119
|
+
if (stepMarkerRes.some((re) => re.test(line))) {
|
|
120
|
+
stepCount += 1;
|
|
121
|
+
if (stepCount >= 2) break;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
if (stepCount < 2) return false;
|
|
125
|
+
const wordFinalMarker = /\b(final answer|so the answer|therefore|conclusion|in the end|ended up|picked|chose)\b/i;
|
|
126
|
+
const colonMarker = /\b(answer:|result:)/i;
|
|
127
|
+
if (!wordFinalMarker.test(text) && !colonMarker.test(text)) return false;
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export {
|
|
132
|
+
normalizeReasoningTraceSteps,
|
|
133
|
+
normalizeReasoningTrace,
|
|
134
|
+
buildReasoningTraceMarkdownBody,
|
|
135
|
+
buildReasoningTracePersistBody,
|
|
136
|
+
parseReasoningTraceFromBody,
|
|
137
|
+
looksLikeReasoningTrace
|
|
138
|
+
};
|
|
139
|
+
//# sourceMappingURL=chunk-54V4BZWP.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/reasoning-trace-types.ts"],"sourcesContent":["/**\n * Reasoning-trace memory structural types (issue #564).\n *\n * Captures stored solution chains / chain-of-thought the user walked through\n * to solve a problem. Traces have an ordered list of steps, a final answer,\n * and an optional observed outcome.\n */\n\nexport interface ReasoningTraceStep {\n /** 1-based ordinal within the trace. */\n order: number;\n /** Human-readable description of what happened at this step. */\n description: string;\n}\n\nexport interface ReasoningTraceStructuredData {\n steps: ReasoningTraceStep[];\n finalAnswer: string;\n /** Optional confirmation of how the answer played out in practice. */\n observedOutcome?: string;\n}\n\n/** Normalize loose extraction JSON into ReasoningTraceStep records. */\nexport function normalizeReasoningTraceSteps(raw: unknown): ReasoningTraceStep[] {\n if (!Array.isArray(raw)) return [];\n const out: ReasoningTraceStep[] = [];\n for (let i = 0; i < raw.length; i++) {\n const s = raw[i];\n let description = \"\";\n let orderRaw: unknown;\n if (typeof s === \"string\") {\n description = s.trim();\n } else if (s && typeof s === \"object\") {\n const o = s as Record<string, unknown>;\n if (typeof o.description === \"string\") description = o.description.trim();\n else if (typeof o.intent === \"string\") description = o.intent.trim();\n else if (typeof o.step === \"string\") description = o.step.trim();\n else if (typeof o.text === \"string\") description = o.text.trim();\n orderRaw = o.order;\n }\n if (!description) continue;\n const order =\n typeof orderRaw === \"number\" && Number.isFinite(orderRaw)\n ? Math.max(1, Math.floor(orderRaw))\n : i + 1;\n out.push({ order, description });\n }\n return out;\n}\n\n/**\n * Normalize a loose reasoningTrace object (e.g. coming back from the LLM) to\n * a strict ReasoningTraceStructuredData shape. Returns null when the data is\n * clearly incomplete (no steps or no final answer).\n */\nexport function normalizeReasoningTrace(raw: unknown): ReasoningTraceStructuredData | null {\n if (!raw || typeof raw !== \"object\" || Array.isArray(raw)) return null;\n const o = raw as Record<string, unknown>;\n const steps = normalizeReasoningTraceSteps(o.steps);\n const finalAnswer =\n typeof o.finalAnswer === \"string\"\n ? o.finalAnswer.trim()\n : typeof o.final_answer === \"string\"\n ? (o.final_answer as string).trim()\n : \"\";\n // Prompts describe reasoning_trace as requiring ≥2 ordered steps + a final\n // answer. Rejecting one-step payloads here keeps the category semantically\n // distinct from ordinary `decision` facts and prevents malformed traces\n // from sneaking through loose local/direct extraction JSON.\n if (steps.length < 2 || finalAnswer.length === 0) return null;\n const observedRaw =\n typeof o.observedOutcome === \"string\"\n ? o.observedOutcome\n : typeof o.observed_outcome === \"string\"\n ? (o.observed_outcome as string)\n : undefined;\n const observedOutcome = observedRaw?.trim();\n return {\n steps,\n finalAnswer,\n observedOutcome: observedOutcome && observedOutcome.length > 0 ? observedOutcome : undefined,\n };\n}\n\n/**\n * Serialize a normalized reasoning trace into a human-readable markdown body.\n * Output shape mirrors the procedure body format: ## Step N sections followed\n * by final answer and optional observed outcome.\n */\nexport function buildReasoningTraceMarkdownBody(trace: ReasoningTraceStructuredData): string {\n const sorted = [...trace.steps].sort((a, b) => a.order - b.order);\n const lines: string[] = [];\n for (const step of sorted) {\n const n = Number.isFinite(step.order) ? Math.max(1, Math.floor(step.order)) : 1;\n lines.push(`## Step ${n}`);\n lines.push(\"\");\n lines.push(step.description.trim());\n lines.push(\"\");\n }\n lines.push(\"## Final Answer\");\n lines.push(\"\");\n lines.push(trace.finalAnswer.trim());\n lines.push(\"\");\n if (trace.observedOutcome && trace.observedOutcome.trim().length > 0) {\n lines.push(\"## Observed Outcome\");\n lines.push(\"\");\n lines.push(trace.observedOutcome.trim());\n lines.push(\"\");\n }\n return lines.join(\"\\n\").trimEnd() + \"\\n\";\n}\n\n/**\n * Combine a short title with the structured trace body. The title becomes the\n * one-line content of the stored memory, and the full body is appended below.\n */\nexport function buildReasoningTracePersistBody(\n title: string,\n trace: ReasoningTraceStructuredData,\n): string {\n const head = typeof title === \"string\" ? title.trim() : \"\";\n const body = buildReasoningTraceMarkdownBody(trace);\n if (!head) return body;\n return `${head}\\n\\n${body}`.trimEnd() + \"\\n\";\n}\n\nconst STEP_HEADER_RE = /^##\\s+Step\\s+(\\d+)\\s*$/im;\nconst FINAL_HEADER_RE = /^##\\s+Final Answer\\s*$/im;\nconst OBSERVED_HEADER_RE = /^##\\s+Observed Outcome\\s*$/im;\n\n/**\n * Best-effort parse of a reasoning-trace markdown body back into structured\n * data. Returns null when the document does not look like a reasoning trace.\n */\nexport function parseReasoningTraceFromBody(content: string): ReasoningTraceStructuredData | null {\n const text = content.replace(/\\r\\n/g, \"\\n\").trim();\n if (!text) return null;\n const stepMatches = [...text.matchAll(new RegExp(STEP_HEADER_RE.source, \"gim\"))];\n const finalMatch = FINAL_HEADER_RE.exec(text);\n if (stepMatches.length === 0 || !finalMatch) return null;\n\n const observedMatch = OBSERVED_HEADER_RE.exec(text);\n\n const steps: ReasoningTraceStep[] = [];\n for (let i = 0; i < stepMatches.length; i++) {\n const m = stepMatches[i];\n const order = Number.parseInt(m[1] ?? String(i + 1), 10);\n const start = (m.index ?? 0) + m[0].length;\n const nextStepStart =\n i + 1 < stepMatches.length ? stepMatches[i + 1].index ?? text.length : text.length;\n const end = Math.min(nextStepStart, finalMatch.index ?? text.length);\n const block = text.slice(start, end).trim();\n const description = block.split(\"\\n\").map((l) => l.trim()).filter((l) => l.length > 0).join(\" \");\n if (!description) continue;\n steps.push({\n order: Number.isFinite(order) ? order : i + 1,\n description,\n });\n }\n\n // Honor the >=2 ordered-steps invariant enforced everywhere else in the\n // pipeline (prompts, normalizer, schema). A single-step manually-edited\n // or corrupt file should not round-trip back in as a reasoning_trace.\n if (steps.length < 2) return null;\n\n const finalStart = (finalMatch.index ?? 0) + finalMatch[0].length;\n const finalEnd = observedMatch?.index ?? text.length;\n const finalAnswer = text\n .slice(finalStart, finalEnd)\n .split(\"\\n\")\n .map((l) => l.trim())\n .filter((l) => l.length > 0)\n .join(\" \")\n .trim();\n if (!finalAnswer) return null;\n\n let observedOutcome: string | undefined;\n if (observedMatch) {\n const obsStart = (observedMatch.index ?? 0) + observedMatch[0].length;\n const raw = text\n .slice(obsStart)\n .split(\"\\n\")\n .map((l) => l.trim())\n .filter((l) => l.length > 0)\n .join(\" \")\n .trim();\n if (raw) observedOutcome = raw;\n }\n\n return { steps, finalAnswer, observedOutcome };\n}\n\n/**\n * Heuristic detector for whether a user message contains a chain-of-thought /\n * solution trace that should be extracted. This is intentionally conservative:\n * we require at least two explicitly-ordered steps and some form of final\n * answer / resolution. Used by the extraction pipeline to decide whether to\n * even consider emitting a reasoning_trace fact, so false positives are more\n * costly than false negatives.\n */\nexport function looksLikeReasoningTrace(message: string): boolean {\n if (typeof message !== \"string\") return false;\n const text = message.trim();\n if (text.length < 80) return false;\n\n // Count lines that carry at least one step marker, not the total number of\n // marker matches. Summing across multiple regexes can let a single line\n // contribute two \"steps\" (e.g. \"Step 1: First,…\" matches both patterns),\n // which weakens the \"false positives > false negatives\" bias. Per-line\n // counting keeps the gate symmetric with real step structure.\n const stepMarkerRes = [\n /\\bstep\\s+\\d+\\s*[:.\\-]/i,\n /^\\s*\\d+[.)]\\s+\\S/,\n /^\\s*(first|second|third|fourth|finally|then|next)\\b[,:]/i,\n ];\n const lines = text.split(/\\r?\\n/);\n let stepCount = 0;\n for (const line of lines) {\n if (stepMarkerRes.some((re) => re.test(line))) {\n stepCount += 1;\n if (stepCount >= 2) break;\n }\n }\n if (stepCount < 2) return false;\n\n // Final-answer / resolution marker. Split into two checks so the trailing\n // `\\b` on the word-final alternatives doesn't accidentally require a word\n // character after `answer:` / `result:` (both end in a non-word `:`).\n const wordFinalMarker =\n /\\b(final answer|so the answer|therefore|conclusion|in the end|ended up|picked|chose)\\b/i;\n const colonMarker = /\\b(answer:|result:)/i;\n if (!wordFinalMarker.test(text) && !colonMarker.test(text)) return false;\n\n return true;\n}\n"],"mappings":";AAuBO,SAAS,6BAA6B,KAAoC;AAC/E,MAAI,CAAC,MAAM,QAAQ,GAAG,EAAG,QAAO,CAAC;AACjC,QAAM,MAA4B,CAAC;AACnC,WAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;AACnC,UAAM,IAAI,IAAI,CAAC;AACf,QAAI,cAAc;AAClB,QAAI;AACJ,QAAI,OAAO,MAAM,UAAU;AACzB,oBAAc,EAAE,KAAK;AAAA,IACvB,WAAW,KAAK,OAAO,MAAM,UAAU;AACrC,YAAM,IAAI;AACV,UAAI,OAAO,EAAE,gBAAgB,SAAU,eAAc,EAAE,YAAY,KAAK;AAAA,eAC/D,OAAO,EAAE,WAAW,SAAU,eAAc,EAAE,OAAO,KAAK;AAAA,eAC1D,OAAO,EAAE,SAAS,SAAU,eAAc,EAAE,KAAK,KAAK;AAAA,eACtD,OAAO,EAAE,SAAS,SAAU,eAAc,EAAE,KAAK,KAAK;AAC/D,iBAAW,EAAE;AAAA,IACf;AACA,QAAI,CAAC,YAAa;AAClB,UAAM,QACJ,OAAO,aAAa,YAAY,OAAO,SAAS,QAAQ,IACpD,KAAK,IAAI,GAAG,KAAK,MAAM,QAAQ,CAAC,IAChC,IAAI;AACV,QAAI,KAAK,EAAE,OAAO,YAAY,CAAC;AAAA,EACjC;AACA,SAAO;AACT;AAOO,SAAS,wBAAwB,KAAmD;AACzF,MAAI,CAAC,OAAO,OAAO,QAAQ,YAAY,MAAM,QAAQ,GAAG,EAAG,QAAO;AAClE,QAAM,IAAI;AACV,QAAM,QAAQ,6BAA6B,EAAE,KAAK;AAClD,QAAM,cACJ,OAAO,EAAE,gBAAgB,WACrB,EAAE,YAAY,KAAK,IACnB,OAAO,EAAE,iBAAiB,WACzB,EAAE,aAAwB,KAAK,IAChC;AAKN,MAAI,MAAM,SAAS,KAAK,YAAY,WAAW,EAAG,QAAO;AACzD,QAAM,cACJ,OAAO,EAAE,oBAAoB,WACzB,EAAE,kBACF,OAAO,EAAE,qBAAqB,WAC7B,EAAE,mBACH;AACN,QAAM,kBAAkB,aAAa,KAAK;AAC1C,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,iBAAiB,mBAAmB,gBAAgB,SAAS,IAAI,kBAAkB;AAAA,EACrF;AACF;AAOO,SAAS,gCAAgC,OAA6C;AAC3F,QAAM,SAAS,CAAC,GAAG,MAAM,KAAK,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK;AAChE,QAAM,QAAkB,CAAC;AACzB,aAAW,QAAQ,QAAQ;AACzB,UAAM,IAAI,OAAO,SAAS,KAAK,KAAK,IAAI,KAAK,IAAI,GAAG,KAAK,MAAM,KAAK,KAAK,CAAC,IAAI;AAC9E,UAAM,KAAK,WAAW,CAAC,EAAE;AACzB,UAAM,KAAK,EAAE;AACb,UAAM,KAAK,KAAK,YAAY,KAAK,CAAC;AAClC,UAAM,KAAK,EAAE;AAAA,EACf;AACA,QAAM,KAAK,iBAAiB;AAC5B,QAAM,KAAK,EAAE;AACb,QAAM,KAAK,MAAM,YAAY,KAAK,CAAC;AACnC,QAAM,KAAK,EAAE;AACb,MAAI,MAAM,mBAAmB,MAAM,gBAAgB,KAAK,EAAE,SAAS,GAAG;AACpE,UAAM,KAAK,qBAAqB;AAChC,UAAM,KAAK,EAAE;AACb,UAAM,KAAK,MAAM,gBAAgB,KAAK,CAAC;AACvC,UAAM,KAAK,EAAE;AAAA,EACf;AACA,SAAO,MAAM,KAAK,IAAI,EAAE,QAAQ,IAAI;AACtC;AAMO,SAAS,+BACd,OACA,OACQ;AACR,QAAM,OAAO,OAAO,UAAU,WAAW,MAAM,KAAK,IAAI;AACxD,QAAM,OAAO,gCAAgC,KAAK;AAClD,MAAI,CAAC,KAAM,QAAO;AAClB,SAAO,GAAG,IAAI;AAAA;AAAA,EAAO,IAAI,GAAG,QAAQ,IAAI;AAC1C;AAEA,IAAM,iBAAiB;AACvB,IAAM,kBAAkB;AACxB,IAAM,qBAAqB;AAMpB,SAAS,4BAA4B,SAAsD;AAChG,QAAM,OAAO,QAAQ,QAAQ,SAAS,IAAI,EAAE,KAAK;AACjD,MAAI,CAAC,KAAM,QAAO;AAClB,QAAM,cAAc,CAAC,GAAG,KAAK,SAAS,IAAI,OAAO,eAAe,QAAQ,KAAK,CAAC,CAAC;AAC/E,QAAM,aAAa,gBAAgB,KAAK,IAAI;AAC5C,MAAI,YAAY,WAAW,KAAK,CAAC,WAAY,QAAO;AAEpD,QAAM,gBAAgB,mBAAmB,KAAK,IAAI;AAElD,QAAM,QAA8B,CAAC;AACrC,WAAS,IAAI,GAAG,IAAI,YAAY,QAAQ,KAAK;AAC3C,UAAM,IAAI,YAAY,CAAC;AACvB,UAAM,QAAQ,OAAO,SAAS,EAAE,CAAC,KAAK,OAAO,IAAI,CAAC,GAAG,EAAE;AACvD,UAAM,SAAS,EAAE,SAAS,KAAK,EAAE,CAAC,EAAE;AACpC,UAAM,gBACJ,IAAI,IAAI,YAAY,SAAS,YAAY,IAAI,CAAC,EAAE,SAAS,KAAK,SAAS,KAAK;AAC9E,UAAM,MAAM,KAAK,IAAI,eAAe,WAAW,SAAS,KAAK,MAAM;AACnE,UAAM,QAAQ,KAAK,MAAM,OAAO,GAAG,EAAE,KAAK;AAC1C,UAAM,cAAc,MAAM,MAAM,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,CAAC,MAAM,EAAE,SAAS,CAAC,EAAE,KAAK,GAAG;AAC/F,QAAI,CAAC,YAAa;AAClB,UAAM,KAAK;AAAA,MACT,OAAO,OAAO,SAAS,KAAK,IAAI,QAAQ,IAAI;AAAA,MAC5C;AAAA,IACF,CAAC;AAAA,EACH;AAKA,MAAI,MAAM,SAAS,EAAG,QAAO;AAE7B,QAAM,cAAc,WAAW,SAAS,KAAK,WAAW,CAAC,EAAE;AAC3D,QAAM,WAAW,eAAe,SAAS,KAAK;AAC9C,QAAM,cAAc,KACjB,MAAM,YAAY,QAAQ,EAC1B,MAAM,IAAI,EACV,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EACnB,OAAO,CAAC,MAAM,EAAE,SAAS,CAAC,EAC1B,KAAK,GAAG,EACR,KAAK;AACR,MAAI,CAAC,YAAa,QAAO;AAEzB,MAAI;AACJ,MAAI,eAAe;AACjB,UAAM,YAAY,cAAc,SAAS,KAAK,cAAc,CAAC,EAAE;AAC/D,UAAM,MAAM,KACT,MAAM,QAAQ,EACd,MAAM,IAAI,EACV,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EACnB,OAAO,CAAC,MAAM,EAAE,SAAS,CAAC,EAC1B,KAAK,GAAG,EACR,KAAK;AACR,QAAI,IAAK,mBAAkB;AAAA,EAC7B;AAEA,SAAO,EAAE,OAAO,aAAa,gBAAgB;AAC/C;AAUO,SAAS,wBAAwB,SAA0B;AAChE,MAAI,OAAO,YAAY,SAAU,QAAO;AACxC,QAAM,OAAO,QAAQ,KAAK;AAC1B,MAAI,KAAK,SAAS,GAAI,QAAO;AAO7B,QAAM,gBAAgB;AAAA,IACpB;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACA,QAAM,QAAQ,KAAK,MAAM,OAAO;AAChC,MAAI,YAAY;AAChB,aAAW,QAAQ,OAAO;AACxB,QAAI,cAAc,KAAK,CAAC,OAAO,GAAG,KAAK,IAAI,CAAC,GAAG;AAC7C,mBAAa;AACb,UAAI,aAAa,EAAG;AAAA,IACtB;AAAA,EACF;AACA,MAAI,YAAY,EAAG,QAAO;AAK1B,QAAM,kBACJ;AACF,QAAM,cAAc;AACpB,MAAI,CAAC,gBAAgB,KAAK,IAAI,KAAK,CAAC,YAAY,KAAK,IAAI,EAAG,QAAO;AAEnE,SAAO;AACT;","names":[]}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import {
|
|
2
|
+
cosineSimilarity
|
|
3
|
+
} from "./chunk-KVE7R4CG.js";
|
|
4
|
+
|
|
5
|
+
// src/buffer-surprise.ts
|
|
6
|
+
var DEFAULT_SURPRISE_K = 5;
|
|
7
|
+
async function computeSurprise(turn, recentMemories, options) {
|
|
8
|
+
if (typeof turn !== "string") {
|
|
9
|
+
throw new TypeError("computeSurprise: `turn` must be a string");
|
|
10
|
+
}
|
|
11
|
+
if (!options || typeof options.embedFn !== "function") {
|
|
12
|
+
throw new TypeError(
|
|
13
|
+
"computeSurprise: `options.embedFn` is required and must be a function"
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
const candidates = Array.isArray(recentMemories) ? recentMemories : [];
|
|
17
|
+
if (candidates.length === 0) return 1;
|
|
18
|
+
const kRaw = typeof options.k === "number" ? options.k : DEFAULT_SURPRISE_K;
|
|
19
|
+
const kClamped = clampK(kRaw, candidates.length);
|
|
20
|
+
const turnEmbedding = await options.embedFn(turn);
|
|
21
|
+
const candidateEmbeddings = await Promise.all(
|
|
22
|
+
candidates.map((c) => options.embedFn(c.content))
|
|
23
|
+
);
|
|
24
|
+
const sims = [];
|
|
25
|
+
for (let i = 0; i < candidates.length; i += 1) {
|
|
26
|
+
const candEmbedding = candidateEmbeddings[i] ?? [];
|
|
27
|
+
sims.push(safeCosine(turnEmbedding, candEmbedding));
|
|
28
|
+
}
|
|
29
|
+
sims.sort((a, b) => b - a);
|
|
30
|
+
const top = sims.slice(0, kClamped);
|
|
31
|
+
if (top.length === 0) {
|
|
32
|
+
return 1;
|
|
33
|
+
}
|
|
34
|
+
let sum = 0;
|
|
35
|
+
for (const s of top) sum += s;
|
|
36
|
+
const nearestSim = sum / top.length;
|
|
37
|
+
const surprise = 1 - nearestSim;
|
|
38
|
+
if (!Number.isFinite(surprise)) return 1;
|
|
39
|
+
if (surprise < 0) return 0;
|
|
40
|
+
if (surprise > 1) return 1;
|
|
41
|
+
return surprise;
|
|
42
|
+
}
|
|
43
|
+
function clampK(k, ceiling) {
|
|
44
|
+
if (!Number.isFinite(k)) {
|
|
45
|
+
return Math.min(DEFAULT_SURPRISE_K, Math.max(1, ceiling));
|
|
46
|
+
}
|
|
47
|
+
const floored = Math.floor(k);
|
|
48
|
+
if (floored < 1) return 1;
|
|
49
|
+
if (floored > ceiling) return ceiling;
|
|
50
|
+
return floored;
|
|
51
|
+
}
|
|
52
|
+
function safeCosine(a, b) {
|
|
53
|
+
if (!Array.isArray(a) || !Array.isArray(b)) return 0;
|
|
54
|
+
if (a.length === 0 || b.length === 0) return 0;
|
|
55
|
+
if (a.length !== b.length) return 0;
|
|
56
|
+
const raw = cosineSimilarity(a, b);
|
|
57
|
+
if (!Number.isFinite(raw)) return 0;
|
|
58
|
+
if (raw < 0) return 0;
|
|
59
|
+
if (raw > 1) return 1;
|
|
60
|
+
return raw;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export {
|
|
64
|
+
DEFAULT_SURPRISE_K,
|
|
65
|
+
computeSurprise
|
|
66
|
+
};
|
|
67
|
+
//# sourceMappingURL=chunk-5JRF2PZA.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/buffer-surprise.ts"],"sourcesContent":["/**\n * Surprise helper — D-MEM-style novelty score for a turn against recent memories.\n *\n * Used (eventually, in a follow-up PR) by the smart buffer as an additive flush\n * trigger: a turn that is semantically far from everything already in memory is\n * \"surprising\" and worth extracting immediately, even if turn-count and signal\n * heuristics would otherwise keep buffering.\n *\n * This module intentionally contains ONLY the pure scoring function. Wiring\n * into the buffer, configuration flags, telemetry, and benchmark work live in\n * later PRs (see issue #563). Keeping the helper in a sibling file instead of\n * inside `buffer.ts` keeps the buffer module focused and makes the eventual\n * integration a small, reviewable change.\n *\n * # Formula\n *\n * Given an input `turn` and a list of `recentMemories`:\n *\n * 1. Embed the turn and every candidate memory via the caller-provided\n * `embedFn`.\n * 2. For each candidate, compute cosine similarity `cos(turn, candidate)` in\n * `[0, 1]` (negative cosines are clamped to 0 — \"opposite directions\"\n * is treated as maximally diverse, consistent with `recall-mmr.ts`).\n * 3. Keep the top-`k` *highest* similarities (the turn's nearest neighbors).\n * 4. Average those top-`k` similarities → `nearestSim ∈ [0, 1]`.\n * 5. Return `1 − nearestSim` as the surprise score.\n *\n * Intuition: a turn that is close to at least one recent memory has a high\n * `nearestSim` and therefore a *low* surprise (near 0 = redundant). A turn\n * that is far from all of its k nearest neighbors has a low `nearestSim` and\n * therefore a *high* surprise (near 1 = novel).\n *\n * Using the top-k average instead of a single nearest neighbor makes the\n * score less sensitive to one outlier duplicate (e.g. an exact restatement of\n * a stale fact). `k=1` reduces to pure nearest-neighbor distance.\n *\n * # Edge cases\n *\n * - `recentMemories` empty → returns `1.0` (maximally surprising). There is\n * nothing to compare against, so the turn cannot be redundant.\n * - `k` default is `5`. It is clamped to `[1, recentMemories.length]` so that\n * small corpora still produce a meaningful score (e.g. 3 memories with\n * `k=5` behaves the same as `k=3`).\n * - Zero-norm embeddings (all zeros) contribute similarity `0` (they cannot\n * be \"close\" to anything), which is treated as maximally surprising for\n * that pair. The rest of the corpus is scored normally.\n * - Embedding-length mismatches between the turn and a candidate are treated\n * as similarity `0` for that pair. We do not silently truncate to the\n * shorter vector because that would hide a real configuration bug.\n * - `embedFn` rejection is allowed to propagate. The caller decides whether\n * to catch, fall back, or fail the flush decision — this helper has no\n * opinion beyond the pure score.\n *\n * # Purity\n *\n * No I/O, no hidden globals. Given the same inputs and the same `embedFn`,\n * the output is deterministic.\n */\n\nimport { cosineSimilarity } from \"./semantic-chunking.js\";\n\n/** Minimal shape of a recent memory passed to `computeSurprise`. */\nexport interface RecentMemoryLike {\n /** Stable identifier. Only used for debug/logging; not part of the score. */\n readonly id: string;\n /** Text to embed and compare against the incoming turn. */\n readonly content: string;\n}\n\n/** Options accepted by `computeSurprise`. */\nexport interface ComputeSurpriseOptions {\n /**\n * Embedding function. Called once for the turn and once per candidate\n * memory. Callers may wrap a provider client, a local model, or a\n * deterministic hash for tests.\n */\n readonly embedFn: (text: string) => Promise<readonly number[]>;\n /**\n * Number of nearest neighbors to average over. Defaults to 5. Clamped to\n * `[1, recentMemories.length]`.\n */\n readonly k?: number;\n}\n\n/** Default k (top nearest neighbors to average). */\nexport const DEFAULT_SURPRISE_K = 5;\n\n/**\n * Compute a surprise score in `[0, 1]` for `turn` against `recentMemories`.\n *\n * See the module-level docstring for the exact formula, edge cases, and\n * purity guarantees.\n *\n * @param turn The incoming turn text (caller is responsible for\n * stringifying structured turn content — this helper\n * only needs the text to embed).\n * @param recentMemories Candidate memories to compare against.\n * @param options `embedFn` (required) and optional `k` (defaults to 5).\n * @returns A scalar in `[0, 1]`. `1.0` = maximally surprising / novel;\n * `0.0` = redundant with at least one recent memory.\n */\nexport async function computeSurprise(\n turn: string,\n recentMemories: readonly RecentMemoryLike[],\n options: ComputeSurpriseOptions,\n): Promise<number> {\n if (typeof turn !== \"string\") {\n throw new TypeError(\"computeSurprise: `turn` must be a string\");\n }\n if (!options || typeof options.embedFn !== \"function\") {\n throw new TypeError(\n \"computeSurprise: `options.embedFn` is required and must be a function\",\n );\n }\n const candidates = Array.isArray(recentMemories) ? recentMemories : [];\n\n // Empty corpus → maximally surprising. Document decision: we prefer\n // \"novelty by default\" over \"silence\" because the surprise score is meant\n // to *promote* flushing, and an empty buffer of recent memories is the\n // clearest case of \"we have no basis to call this redundant\".\n if (candidates.length === 0) return 1;\n\n const kRaw = typeof options.k === \"number\" ? options.k : DEFAULT_SURPRISE_K;\n // Clamp k to [1, candidates.length]. Non-integer / non-finite values fall\n // back to the default, then get re-clamped below.\n const kClamped = clampK(kRaw, candidates.length);\n\n // Embed the turn and every candidate. Rejections propagate — that's the\n // caller's decision to handle. Run candidate embeddings in parallel so a\n // slow embedder does not serialize the whole pass.\n const turnEmbedding = await options.embedFn(turn);\n const candidateEmbeddings = await Promise.all(\n candidates.map((c) => options.embedFn(c.content)),\n );\n\n const sims: number[] = [];\n for (let i = 0; i < candidates.length; i += 1) {\n const candEmbedding = candidateEmbeddings[i] ?? [];\n sims.push(safeCosine(turnEmbedding, candEmbedding));\n }\n\n // Sort similarities descending and take the top-k (the nearest neighbors).\n sims.sort((a, b) => b - a);\n const top = sims.slice(0, kClamped);\n\n if (top.length === 0) {\n // Defensive — with `candidates.length >= 1` and `kClamped >= 1`, we\n // should always have at least one similarity. If we somehow do not,\n // treat as maximally surprising rather than returning NaN.\n return 1;\n }\n\n let sum = 0;\n for (const s of top) sum += s;\n const nearestSim = sum / top.length;\n\n const surprise = 1 - nearestSim;\n // Numerical safety: clamp the final value. Cosine is clamped to [0, 1]\n // per-pair already, so the mean cannot escape that range under normal\n // arithmetic, but floating-point drift is cheap to guard against.\n if (!Number.isFinite(surprise)) return 1;\n if (surprise < 0) return 0;\n if (surprise > 1) return 1;\n return surprise;\n}\n\n// -----------------------------------------------------------------------------\n// Internals\n// -----------------------------------------------------------------------------\n\nfunction clampK(k: number, ceiling: number): number {\n if (!Number.isFinite(k)) {\n return Math.min(DEFAULT_SURPRISE_K, Math.max(1, ceiling));\n }\n const floored = Math.floor(k);\n if (floored < 1) return 1;\n if (floored > ceiling) return ceiling;\n return floored;\n}\n\n/**\n * Safe wrapper around `cosineSimilarity` that returns a similarity value in\n * `[0, 1]` (negative cosines are clamped to 0, matching the convention used\n * by `recall-mmr.ts`) and tolerates mismatched or empty vectors without\n * throwing. See `semantic-chunking.ts` for the shared cosine implementation.\n *\n * Returns `0` for:\n * - empty vectors,\n * - length mismatches (callers likely have a config bug, but surprise\n * scoring is a soft signal — do not crash the buffer flush decision),\n * - non-finite cosine results (zero-norm inputs, arithmetic drift).\n */\nfunction safeCosine(a: readonly number[], b: readonly number[]): number {\n if (!Array.isArray(a) || !Array.isArray(b)) return 0;\n if (a.length === 0 || b.length === 0) return 0;\n if (a.length !== b.length) return 0;\n const raw = cosineSimilarity(a as number[], b as number[]);\n if (!Number.isFinite(raw)) return 0;\n if (raw < 0) return 0;\n if (raw > 1) return 1;\n return raw;\n}\n\n"],"mappings":";;;;;AAqFO,IAAM,qBAAqB;AAgBlC,eAAsB,gBACpB,MACA,gBACA,SACiB;AACjB,MAAI,OAAO,SAAS,UAAU;AAC5B,UAAM,IAAI,UAAU,0CAA0C;AAAA,EAChE;AACA,MAAI,CAAC,WAAW,OAAO,QAAQ,YAAY,YAAY;AACrD,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,QAAM,aAAa,MAAM,QAAQ,cAAc,IAAI,iBAAiB,CAAC;AAMrE,MAAI,WAAW,WAAW,EAAG,QAAO;AAEpC,QAAM,OAAO,OAAO,QAAQ,MAAM,WAAW,QAAQ,IAAI;AAGzD,QAAM,WAAW,OAAO,MAAM,WAAW,MAAM;AAK/C,QAAM,gBAAgB,MAAM,QAAQ,QAAQ,IAAI;AAChD,QAAM,sBAAsB,MAAM,QAAQ;AAAA,IACxC,WAAW,IAAI,CAAC,MAAM,QAAQ,QAAQ,EAAE,OAAO,CAAC;AAAA,EAClD;AAEA,QAAM,OAAiB,CAAC;AACxB,WAAS,IAAI,GAAG,IAAI,WAAW,QAAQ,KAAK,GAAG;AAC7C,UAAM,gBAAgB,oBAAoB,CAAC,KAAK,CAAC;AACjD,SAAK,KAAK,WAAW,eAAe,aAAa,CAAC;AAAA,EACpD;AAGA,OAAK,KAAK,CAAC,GAAG,MAAM,IAAI,CAAC;AACzB,QAAM,MAAM,KAAK,MAAM,GAAG,QAAQ;AAElC,MAAI,IAAI,WAAW,GAAG;AAIpB,WAAO;AAAA,EACT;AAEA,MAAI,MAAM;AACV,aAAW,KAAK,IAAK,QAAO;AAC5B,QAAM,aAAa,MAAM,IAAI;AAE7B,QAAM,WAAW,IAAI;AAIrB,MAAI,CAAC,OAAO,SAAS,QAAQ,EAAG,QAAO;AACvC,MAAI,WAAW,EAAG,QAAO;AACzB,MAAI,WAAW,EAAG,QAAO;AACzB,SAAO;AACT;AAMA,SAAS,OAAO,GAAW,SAAyB;AAClD,MAAI,CAAC,OAAO,SAAS,CAAC,GAAG;AACvB,WAAO,KAAK,IAAI,oBAAoB,KAAK,IAAI,GAAG,OAAO,CAAC;AAAA,EAC1D;AACA,QAAM,UAAU,KAAK,MAAM,CAAC;AAC5B,MAAI,UAAU,EAAG,QAAO;AACxB,MAAI,UAAU,QAAS,QAAO;AAC9B,SAAO;AACT;AAcA,SAAS,WAAW,GAAsB,GAA8B;AACtE,MAAI,CAAC,MAAM,QAAQ,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,EAAG,QAAO;AACnD,MAAI,EAAE,WAAW,KAAK,EAAE,WAAW,EAAG,QAAO;AAC7C,MAAI,EAAE,WAAW,EAAE,OAAQ,QAAO;AAClC,QAAM,MAAM,iBAAiB,GAAe,CAAa;AACzD,MAAI,CAAC,OAAO,SAAS,GAAG,EAAG,QAAO;AAClC,MAAI,MAAM,EAAG,QAAO;AACpB,MAAI,MAAM,EAAG,QAAO;AACpB,SAAO;AACT;","names":[]}
|
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
// src/graph-retrieval.ts
|
|
2
|
+
var DEFAULT_PPR_DAMPING = 0.85;
|
|
3
|
+
var DEFAULT_PPR_ITERATIONS = 20;
|
|
4
|
+
var DEFAULT_PPR_TOLERANCE = 1e-6;
|
|
5
|
+
function normalizeSeedWeights(input) {
|
|
6
|
+
const out = /* @__PURE__ */ new Map();
|
|
7
|
+
if (!input) return out;
|
|
8
|
+
const entries = input instanceof Map ? input.entries() : Object.entries(input);
|
|
9
|
+
for (const [k, v] of entries) {
|
|
10
|
+
if (typeof k !== "string" || !k) continue;
|
|
11
|
+
if (typeof v !== "number" || !Number.isFinite(v) || v < 0) continue;
|
|
12
|
+
out.set(k, v);
|
|
13
|
+
}
|
|
14
|
+
return out;
|
|
15
|
+
}
|
|
16
|
+
function buildSeedVector(graph, seedIds, options) {
|
|
17
|
+
const seed = /* @__PURE__ */ new Map();
|
|
18
|
+
const explicitWeights = normalizeSeedWeights(options.seedWeights);
|
|
19
|
+
if (explicitWeights.size > 0) {
|
|
20
|
+
const allowedKeys = seedIds.length > 0 ? new Set(seedIds.filter((id) => typeof id === "string")) : null;
|
|
21
|
+
let total = 0;
|
|
22
|
+
for (const [id, w] of explicitWeights) {
|
|
23
|
+
if (!graph.nodes.has(id)) continue;
|
|
24
|
+
if (allowedKeys !== null && !allowedKeys.has(id)) continue;
|
|
25
|
+
seed.set(id, (seed.get(id) ?? 0) + w);
|
|
26
|
+
total += w;
|
|
27
|
+
}
|
|
28
|
+
if (total > 0) {
|
|
29
|
+
for (const [id, v] of seed) seed.set(id, v / total);
|
|
30
|
+
return seed;
|
|
31
|
+
}
|
|
32
|
+
seed.clear();
|
|
33
|
+
}
|
|
34
|
+
const validSeeds = /* @__PURE__ */ new Set();
|
|
35
|
+
for (const id of seedIds) {
|
|
36
|
+
if (typeof id === "string" && graph.nodes.has(id)) validSeeds.add(id);
|
|
37
|
+
}
|
|
38
|
+
if (validSeeds.size > 0) {
|
|
39
|
+
const share = 1 / validSeeds.size;
|
|
40
|
+
for (const id of validSeeds) seed.set(id, share);
|
|
41
|
+
return seed;
|
|
42
|
+
}
|
|
43
|
+
if (graph.nodes.size > 0) {
|
|
44
|
+
const share = 1 / graph.nodes.size;
|
|
45
|
+
for (const id of graph.nodes.keys()) seed.set(id, share);
|
|
46
|
+
}
|
|
47
|
+
return seed;
|
|
48
|
+
}
|
|
49
|
+
function buildAdjacency(graph) {
|
|
50
|
+
const outgoing = /* @__PURE__ */ new Map();
|
|
51
|
+
const outSum = /* @__PURE__ */ new Map();
|
|
52
|
+
for (const edge of graph.edges) {
|
|
53
|
+
if (!edge || typeof edge.from !== "string" || typeof edge.to !== "string") continue;
|
|
54
|
+
if (!graph.nodes.has(edge.from) || !graph.nodes.has(edge.to)) continue;
|
|
55
|
+
const weight = typeof edge.weight === "number" && Number.isFinite(edge.weight) && edge.weight > 0 ? edge.weight : 1;
|
|
56
|
+
let bucket = outgoing.get(edge.from);
|
|
57
|
+
if (!bucket) {
|
|
58
|
+
bucket = [];
|
|
59
|
+
outgoing.set(edge.from, bucket);
|
|
60
|
+
}
|
|
61
|
+
bucket.push({ to: edge.to, weight });
|
|
62
|
+
outSum.set(edge.from, (outSum.get(edge.from) ?? 0) + weight);
|
|
63
|
+
}
|
|
64
|
+
return { outgoing, outSum };
|
|
65
|
+
}
|
|
66
|
+
function queryGraph(graph, seedIds, options = {}) {
|
|
67
|
+
const n = graph.nodes.size;
|
|
68
|
+
if (n === 0) {
|
|
69
|
+
return { rankedNodes: [], iterations: 0, converged: true };
|
|
70
|
+
}
|
|
71
|
+
let damping = typeof options.damping === "number" ? options.damping : DEFAULT_PPR_DAMPING;
|
|
72
|
+
if (!Number.isFinite(damping) || damping < 0) damping = 0;
|
|
73
|
+
if (damping >= 1) damping = 1 - 1e-9;
|
|
74
|
+
let maxIter = typeof options.iterations === "number" ? Math.floor(options.iterations) : DEFAULT_PPR_ITERATIONS;
|
|
75
|
+
if (!Number.isFinite(maxIter) || maxIter < 0) maxIter = 0;
|
|
76
|
+
let tolerance = typeof options.tolerance === "number" ? options.tolerance : DEFAULT_PPR_TOLERANCE;
|
|
77
|
+
if (!Number.isFinite(tolerance) || tolerance < 0) tolerance = 0;
|
|
78
|
+
const seed = buildSeedVector(graph, seedIds, options);
|
|
79
|
+
const hasExplicitWeights = options.seedWeights !== void 0 && options.seedWeights !== null;
|
|
80
|
+
if (!hasExplicitWeights) {
|
|
81
|
+
let priorTotal = 0;
|
|
82
|
+
for (const [id, s] of seed) {
|
|
83
|
+
const node = graph.nodes.get(id);
|
|
84
|
+
const w = node !== void 0 && typeof node.weight === "number" && Number.isFinite(node.weight) && node.weight > 0 ? node.weight : 1;
|
|
85
|
+
const biased = s * w;
|
|
86
|
+
seed.set(id, biased);
|
|
87
|
+
priorTotal += biased;
|
|
88
|
+
}
|
|
89
|
+
if (priorTotal > 0) {
|
|
90
|
+
for (const [id, s] of seed) seed.set(id, s / priorTotal);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
const { outgoing, outSum } = buildAdjacency(graph);
|
|
94
|
+
let rank = /* @__PURE__ */ new Map();
|
|
95
|
+
for (const id of graph.nodes.keys()) rank.set(id, seed.get(id) ?? 0);
|
|
96
|
+
let seedTotal = 0;
|
|
97
|
+
for (const v of seed.values()) seedTotal += v;
|
|
98
|
+
if (seedTotal === 0) {
|
|
99
|
+
return { rankedNodes: [], iterations: 0, converged: true };
|
|
100
|
+
}
|
|
101
|
+
let converged = false;
|
|
102
|
+
let iter = 0;
|
|
103
|
+
for (; iter < maxIter; iter++) {
|
|
104
|
+
const next = /* @__PURE__ */ new Map();
|
|
105
|
+
let danglingMass = 0;
|
|
106
|
+
for (const [id, r] of rank) {
|
|
107
|
+
if ((outSum.get(id) ?? 0) === 0) danglingMass += r;
|
|
108
|
+
}
|
|
109
|
+
const teleportScale = 1 - damping + damping * danglingMass;
|
|
110
|
+
for (const [id, s] of seed) {
|
|
111
|
+
next.set(id, (next.get(id) ?? 0) + teleportScale * s);
|
|
112
|
+
}
|
|
113
|
+
for (const [from, edges] of outgoing) {
|
|
114
|
+
const r = rank.get(from) ?? 0;
|
|
115
|
+
if (r === 0) continue;
|
|
116
|
+
const total = outSum.get(from) ?? 0;
|
|
117
|
+
if (total === 0) continue;
|
|
118
|
+
const share = damping * r / total;
|
|
119
|
+
for (const { to, weight } of edges) {
|
|
120
|
+
next.set(to, (next.get(to) ?? 0) + share * weight);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
let delta = 0;
|
|
124
|
+
for (const id of graph.nodes.keys()) {
|
|
125
|
+
delta += Math.abs((next.get(id) ?? 0) - (rank.get(id) ?? 0));
|
|
126
|
+
}
|
|
127
|
+
rank = next;
|
|
128
|
+
if (delta < tolerance) {
|
|
129
|
+
converged = true;
|
|
130
|
+
iter += 1;
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
const ranked = [];
|
|
135
|
+
for (const [id, score] of rank) {
|
|
136
|
+
if (score > 0) ranked.push({ id, score });
|
|
137
|
+
}
|
|
138
|
+
ranked.sort((a, b) => {
|
|
139
|
+
if (b.score !== a.score) return b.score - a.score;
|
|
140
|
+
return a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
|
|
141
|
+
});
|
|
142
|
+
const topK = options.topK;
|
|
143
|
+
let trimmed;
|
|
144
|
+
if (typeof topK === "number") {
|
|
145
|
+
if (topK <= 0) {
|
|
146
|
+
trimmed = [];
|
|
147
|
+
} else if (topK < ranked.length) {
|
|
148
|
+
trimmed = ranked.slice(0, topK);
|
|
149
|
+
} else {
|
|
150
|
+
trimmed = ranked;
|
|
151
|
+
}
|
|
152
|
+
} else {
|
|
153
|
+
trimmed = ranked;
|
|
154
|
+
}
|
|
155
|
+
return { rankedNodes: trimmed, iterations: iter, converged };
|
|
156
|
+
}
|
|
157
|
+
var NODE_TYPES = /* @__PURE__ */ new Set([
|
|
158
|
+
"memory",
|
|
159
|
+
"entity",
|
|
160
|
+
"episode",
|
|
161
|
+
"concept",
|
|
162
|
+
"reflection"
|
|
163
|
+
]);
|
|
164
|
+
var EDGE_TYPES = /* @__PURE__ */ new Set([
|
|
165
|
+
"references",
|
|
166
|
+
"supersedes",
|
|
167
|
+
"authored-by",
|
|
168
|
+
"mentions",
|
|
169
|
+
"derived-from",
|
|
170
|
+
"temporal-next",
|
|
171
|
+
"related-to"
|
|
172
|
+
]);
|
|
173
|
+
function isNodeType(value) {
|
|
174
|
+
return typeof value === "string" && NODE_TYPES.has(value);
|
|
175
|
+
}
|
|
176
|
+
function isEdgeType(value) {
|
|
177
|
+
return typeof value === "string" && EDGE_TYPES.has(value);
|
|
178
|
+
}
|
|
179
|
+
var CITATION_REGEX = /\[Source:([^\]\n[]+)\]/gi;
|
|
180
|
+
function parseCitationFields(body) {
|
|
181
|
+
const out = {};
|
|
182
|
+
for (const rawPart of body.split(",")) {
|
|
183
|
+
const part = rawPart.trim();
|
|
184
|
+
if (!part) continue;
|
|
185
|
+
const eq = part.indexOf("=");
|
|
186
|
+
if (eq <= 0) continue;
|
|
187
|
+
const key = part.slice(0, eq).trim().toLowerCase();
|
|
188
|
+
const value = part.slice(eq + 1).trim();
|
|
189
|
+
if (key && value) out[key] = value;
|
|
190
|
+
}
|
|
191
|
+
return out;
|
|
192
|
+
}
|
|
193
|
+
function stripDerivedFromVersion(ref) {
|
|
194
|
+
const colon = ref.lastIndexOf(":");
|
|
195
|
+
if (colon < 0) return ref;
|
|
196
|
+
const tail = ref.slice(colon + 1);
|
|
197
|
+
if (tail.length === 0) return ref.slice(0, colon);
|
|
198
|
+
if (/^\d+$/.test(tail)) return ref.slice(0, colon);
|
|
199
|
+
return ref;
|
|
200
|
+
}
|
|
201
|
+
function extractGraphEdges(memories, options = {}) {
|
|
202
|
+
const includeDangling = options.includeDanglingEdges === true;
|
|
203
|
+
const nodes = /* @__PURE__ */ new Map();
|
|
204
|
+
const edges = [];
|
|
205
|
+
const seenEdgeKeys = /* @__PURE__ */ new Set();
|
|
206
|
+
const addNode = (id, type) => {
|
|
207
|
+
if (!nodes.has(id)) nodes.set(id, { id, type });
|
|
208
|
+
};
|
|
209
|
+
const addEdge = (from, to, type) => {
|
|
210
|
+
if (!from || !to || from === to) return;
|
|
211
|
+
const key = `${from}\0${to}\0${type}`;
|
|
212
|
+
if (seenEdgeKeys.has(key)) return;
|
|
213
|
+
seenEdgeKeys.add(key);
|
|
214
|
+
edges.push({ from, to, type });
|
|
215
|
+
};
|
|
216
|
+
for (const memory of memories) {
|
|
217
|
+
if (!memory?.id) continue;
|
|
218
|
+
addNode(memory.id, "memory");
|
|
219
|
+
}
|
|
220
|
+
const entityClaimed = /* @__PURE__ */ new Set();
|
|
221
|
+
for (const memory of memories) {
|
|
222
|
+
if (!memory?.id) continue;
|
|
223
|
+
if (typeof memory.entityRef === "string" && memory.entityRef) {
|
|
224
|
+
if (memory.entityRef !== memory.id) entityClaimed.add(memory.entityRef);
|
|
225
|
+
}
|
|
226
|
+
if (Array.isArray(memory.entityRefs)) {
|
|
227
|
+
for (const ref of memory.entityRefs) {
|
|
228
|
+
if (typeof ref === "string" && ref && ref !== memory.id) {
|
|
229
|
+
entityClaimed.add(ref);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
if (typeof memory.content === "string" && memory.content.length > 0) {
|
|
234
|
+
CITATION_REGEX.lastIndex = 0;
|
|
235
|
+
let match;
|
|
236
|
+
while ((match = CITATION_REGEX.exec(memory.content)) !== null) {
|
|
237
|
+
const body = match[1];
|
|
238
|
+
if (!body) continue;
|
|
239
|
+
const agent = parseCitationFields(body).agent;
|
|
240
|
+
if (!agent) continue;
|
|
241
|
+
const agentId = `agent:${agent}`;
|
|
242
|
+
if (agentId !== memory.id) entityClaimed.add(agentId);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
for (const memory of memories) {
|
|
247
|
+
if (memory?.id) entityClaimed.delete(memory.id);
|
|
248
|
+
}
|
|
249
|
+
for (const memory of memories) {
|
|
250
|
+
if (!memory?.id) continue;
|
|
251
|
+
const from = memory.id;
|
|
252
|
+
const canTargetMemory = (id) => {
|
|
253
|
+
const existing = nodes.get(id);
|
|
254
|
+
if (existing !== void 0) return existing.type === "memory";
|
|
255
|
+
if (entityClaimed.has(id)) return false;
|
|
256
|
+
return includeDangling;
|
|
257
|
+
};
|
|
258
|
+
if (typeof memory.supersedes === "string" && memory.supersedes) {
|
|
259
|
+
const to = memory.supersedes;
|
|
260
|
+
if (canTargetMemory(to)) {
|
|
261
|
+
if (!nodes.has(to)) addNode(to, "memory");
|
|
262
|
+
addEdge(from, to, "supersedes");
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
if (Array.isArray(memory.lineage)) {
|
|
266
|
+
for (const parent of memory.lineage) {
|
|
267
|
+
if (typeof parent !== "string" || !parent) continue;
|
|
268
|
+
if (!canTargetMemory(parent)) continue;
|
|
269
|
+
if (!nodes.has(parent)) addNode(parent, "memory");
|
|
270
|
+
addEdge(from, parent, "derived-from");
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
if (Array.isArray(memory.derived_from)) {
|
|
274
|
+
for (const raw of memory.derived_from) {
|
|
275
|
+
if (typeof raw !== "string" || !raw) continue;
|
|
276
|
+
const to = stripDerivedFromVersion(raw);
|
|
277
|
+
if (!to) continue;
|
|
278
|
+
if (!canTargetMemory(to)) continue;
|
|
279
|
+
if (!nodes.has(to)) addNode(to, "memory");
|
|
280
|
+
addEdge(from, to, "derived-from");
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
const entitySet = /* @__PURE__ */ new Set();
|
|
284
|
+
if (typeof memory.entityRef === "string" && memory.entityRef) {
|
|
285
|
+
entitySet.add(memory.entityRef);
|
|
286
|
+
}
|
|
287
|
+
if (Array.isArray(memory.entityRefs)) {
|
|
288
|
+
for (const ref of memory.entityRefs) {
|
|
289
|
+
if (typeof ref === "string" && ref) entitySet.add(ref);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
for (const ref of entitySet) {
|
|
293
|
+
const existing = nodes.get(ref);
|
|
294
|
+
if (existing !== void 0 && existing.type !== "entity") continue;
|
|
295
|
+
if (!existing) addNode(ref, "entity");
|
|
296
|
+
addEdge(from, ref, "mentions");
|
|
297
|
+
}
|
|
298
|
+
if (typeof memory.content === "string" && memory.content.length > 0) {
|
|
299
|
+
CITATION_REGEX.lastIndex = 0;
|
|
300
|
+
let match;
|
|
301
|
+
while ((match = CITATION_REGEX.exec(memory.content)) !== null) {
|
|
302
|
+
const body = match[1];
|
|
303
|
+
if (!body) continue;
|
|
304
|
+
const fields = parseCitationFields(body);
|
|
305
|
+
const agent = fields.agent;
|
|
306
|
+
if (!agent) continue;
|
|
307
|
+
const agentId = `agent:${agent}`;
|
|
308
|
+
const existing = nodes.get(agentId);
|
|
309
|
+
if (existing !== void 0 && existing.type !== "entity") continue;
|
|
310
|
+
if (!existing) addNode(agentId, "entity");
|
|
311
|
+
addEdge(from, agentId, "authored-by");
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
return { nodes, edges };
|
|
316
|
+
}
|
|
317
|
+
function buildGraphFromMemories(memories, options = {}) {
|
|
318
|
+
const { nodes, edges } = extractGraphEdges(memories, options);
|
|
319
|
+
return { nodes, edges };
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
export {
|
|
323
|
+
DEFAULT_PPR_DAMPING,
|
|
324
|
+
DEFAULT_PPR_ITERATIONS,
|
|
325
|
+
DEFAULT_PPR_TOLERANCE,
|
|
326
|
+
queryGraph,
|
|
327
|
+
isNodeType,
|
|
328
|
+
isEdgeType,
|
|
329
|
+
extractGraphEdges,
|
|
330
|
+
buildGraphFromMemories
|
|
331
|
+
};
|
|
332
|
+
//# sourceMappingURL=chunk-64NJRYU2.js.map
|