@remnic/core 1.1.0 → 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/access-audit.d.ts +56 -0
- package/dist/access-audit.js +9 -0
- package/dist/access-cli.js +62 -45
- package/dist/access-cli.js.map +1 -1
- package/dist/access-http.d.ts +16 -9
- package/dist/access-http.js +25 -17
- package/dist/access-mcp.d.ts +16 -9
- package/dist/access-mcp.js +29 -7
- 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 +22 -14
- package/dist/bootstrap.d.ts +6 -3
- package/dist/briefing.d.ts +1 -0
- package/dist/briefing.js +7 -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 +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-44ICJRF3.js → chunk-3GXCSUXR.js} +4 -4
- package/dist/{chunk-ITRLGI2T.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-OIT5QGG4.js → chunk-6AUUAZEX.js} +72 -2
- package/dist/chunk-6AUUAZEX.js.map +1 -0
- package/dist/{chunk-ZVBB3T7V.js → chunk-7I7FKFZH.js} +24 -22
- 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-3QHL5ABG.js → chunk-B5WXLVDY.js} +187 -6
- package/dist/chunk-B5WXLVDY.js.map +1 -0
- package/dist/{chunk-SYUK3VLY.js → chunk-BGJGXLZ7.js} +111 -2
- package/dist/{chunk-SYUK3VLY.js.map → chunk-BGJGXLZ7.js.map} +1 -1
- package/dist/{chunk-MBJHSA7F.js → chunk-BK2EFTE2.js} +258 -13
- 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-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-37UIFYWO.js → chunk-DFTTJYSO.js} +108 -9
- 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-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-GDFS42HT.js +206 -0
- package/dist/chunk-GDFS42HT.js.map +1 -0
- package/dist/{chunk-MVTHXUBX.js → chunk-GKFXUTJ2.js} +479 -20
- package/dist/chunk-GKFXUTJ2.js.map +1 -0
- package/dist/{chunk-NQEVYWX6.js → chunk-HK3FGIEW.js} +209 -5
- package/dist/chunk-HK3FGIEW.js.map +1 -0
- package/dist/chunk-IISBCCWR.js +52 -0
- package/dist/chunk-IISBCCWR.js.map +1 -0
- package/dist/{chunk-WBSAYXVI.js → chunk-INXV5JBT.js} +198 -42
- 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-J4IYOZZ5.js → chunk-JXS5PDQ7.js} +3 -1
- package/dist/chunk-JXS5PDQ7.js.map +1 -0
- package/dist/{chunk-6LX5ORAS.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-3WHVNEN7.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-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-DHHP2Z4X.js → chunk-RGLL5SPU.js} +2 -2
- package/dist/{chunk-3SV6CQHO.js → chunk-S3EEFKNY.js} +101 -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-JIU55F3X.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-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-DEPL3635.js → chunk-VYM3VWOF.js} +1432 -188
- 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-4LACOVZX.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-BLKTA7MM.js → chunk-YNQKWQT4.js} +50 -17
- 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-N42IWANG.js → chunk-ZEM3OK2K.js} +2 -2
- 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 +40 -29
- 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-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/{engine-5TIQBYZR.js → engine-F3GOXGE5.js} +8 -7
- package/dist/engine-F3GOXGE5.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 +6 -5
- 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/importance.js +1 -1
- package/dist/index.d.ts +585 -20
- package/dist/index.js +503 -312
- package/dist/index.js.map +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 +23 -14
- 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 +49 -39
- 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 +1 -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 +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/resume-bundles.js +5 -5
- 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 +287 -31
- 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 +21 -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 +3 -3
- package/dist/temporal-supersession.d.ts +1 -0
- package/dist/tier-migration.d.ts +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-6ZH4TU6I.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-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-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-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-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-44ICJRF3.js.map → chunk-3GXCSUXR.js.map} +0 -0
- /package/dist/{chunk-ITRLGI2T.js.map → chunk-3OGMS3PE.js.map} +0 -0
- /package/dist/{chunk-6UJ47TVX.js.map → chunk-CUPFXL3J.js.map} +0 -0
- /package/dist/{chunk-7WQ6SLIE.js.map → chunk-FVA6TGI3.js.map} +0 -0
- /package/dist/{chunk-6LX5ORAS.js.map → chunk-KUB6JU6H.js.map} +0 -0
- /package/dist/{chunk-4NRAJUDS.js.map → chunk-RBBWYEFJ.js.map} +0 -0
- /package/dist/{chunk-DHHP2Z4X.js.map → chunk-RGLL5SPU.js.map} +0 -0
- /package/dist/{chunk-JIU55F3X.js.map → chunk-SPI27QT6.js.map} +0 -0
- /package/dist/{chunk-7ECD5ATE.js.map → chunk-VDX363PS.js.map} +0 -0
- /package/dist/{chunk-4LACOVZX.js.map → chunk-WVVA7F5A.js.map} +0 -0
- /package/dist/{chunk-3QFQGRHO.js.map → chunk-XMHBH5H6.js.map} +0 -0
- /package/dist/{chunk-N42IWANG.js.map → chunk-ZEM3OK2K.js.map} +0 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import {
|
|
2
|
+
parseXrayFormat
|
|
3
|
+
} from "./chunk-ZAIM4TUE.js";
|
|
4
|
+
|
|
5
|
+
// src/recall-xray-cli.ts
|
|
6
|
+
function parseXrayBudgetFlag(value) {
|
|
7
|
+
if (value === void 0 || value === null) return void 0;
|
|
8
|
+
const parsed = typeof value === "number" ? value : Number(value);
|
|
9
|
+
if (!Number.isFinite(parsed) || parsed <= 0 || !Number.isInteger(parsed)) {
|
|
10
|
+
throw new Error(
|
|
11
|
+
`--budget expects a positive integer; got ${JSON.stringify(value)}`
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
return parsed;
|
|
15
|
+
}
|
|
16
|
+
function parseXrayCliOptions(rawQuery, options) {
|
|
17
|
+
if (typeof rawQuery !== "string" || rawQuery.trim().length === 0) {
|
|
18
|
+
throw new Error("xray: <query> is required and must be non-empty");
|
|
19
|
+
}
|
|
20
|
+
const format = parseXrayFormat(options.format);
|
|
21
|
+
const budget = parseXrayBudgetFlag(options.budget);
|
|
22
|
+
const namespace = typeof options.namespace === "string" && options.namespace.trim().length > 0 ? options.namespace.trim() : void 0;
|
|
23
|
+
const outPath = typeof options.out === "string" && options.out.trim().length > 0 ? options.out.trim() : void 0;
|
|
24
|
+
return {
|
|
25
|
+
query: rawQuery,
|
|
26
|
+
format,
|
|
27
|
+
...budget !== void 0 ? { budget } : {},
|
|
28
|
+
...namespace !== void 0 ? { namespace } : {},
|
|
29
|
+
...outPath !== void 0 ? { outPath } : {}
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export {
|
|
34
|
+
parseXrayBudgetFlag,
|
|
35
|
+
parseXrayCliOptions
|
|
36
|
+
};
|
|
37
|
+
//# sourceMappingURL=chunk-PVPWZSSI.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/recall-xray-cli.ts"],"sourcesContent":["/**\n * Input-validation helpers for the `remnic xray` CLI command (issue\n * #570, PR 3).\n *\n * Pulled out of `cli.ts` so the validation paths can be unit-tested in\n * isolation — the full CLI handler is hard to exercise without booting\n * an orchestrator. CLAUDE.md rules 14 + 51 require that `--format`,\n * `--budget`, `--namespace`, and `--out` reject missing-value /\n * unknown / non-positive arguments with a listed-options error, rather\n * than silently defaulting.\n */\n\nimport {\n parseXrayFormat,\n type RecallXrayFormat,\n} from \"./recall-xray-renderer.js\";\n\nexport interface ParsedXrayCliOptions {\n format: RecallXrayFormat;\n /** Positive integer override, or undefined when not specified. */\n budget?: number;\n /** Trimmed namespace, or undefined when not specified. */\n namespace?: string;\n /** Trimmed, tilde-unexpanded output path, or undefined when stdout. */\n outPath?: string;\n}\n\n/**\n * Validate and coerce `--budget <chars>`. Must be a positive integer;\n * throws a listed-options error otherwise.\n */\nexport function parseXrayBudgetFlag(value: unknown): number | undefined {\n if (value === undefined || value === null) return undefined;\n const parsed = typeof value === \"number\" ? value : Number(value);\n if (\n !Number.isFinite(parsed) ||\n parsed <= 0 ||\n !Number.isInteger(parsed)\n ) {\n throw new Error(\n `--budget expects a positive integer; got ${JSON.stringify(value)}`,\n );\n }\n return parsed;\n}\n\n/**\n * Parse and validate the full option bag for `remnic xray`. Extracted\n * so the CLI handler in `cli.ts` can stay thin and the validation can\n * be unit-tested without booting an orchestrator.\n */\nexport function parseXrayCliOptions(\n rawQuery: unknown,\n options: Record<string, unknown>,\n): { query: string } & ParsedXrayCliOptions {\n if (typeof rawQuery !== \"string\" || rawQuery.trim().length === 0) {\n throw new Error(\"xray: <query> is required and must be non-empty\");\n }\n const format = parseXrayFormat(options.format);\n const budget = parseXrayBudgetFlag(options.budget);\n const namespace =\n typeof options.namespace === \"string\" &&\n options.namespace.trim().length > 0\n ? options.namespace.trim()\n : undefined;\n const outPath =\n typeof options.out === \"string\" && options.out.trim().length > 0\n ? options.out.trim()\n : undefined;\n return {\n query: rawQuery,\n format,\n ...(budget !== undefined ? { budget } : {}),\n ...(namespace !== undefined ? { namespace } : {}),\n ...(outPath !== undefined ? { outPath } : {}),\n };\n}\n"],"mappings":";;;;;AA+BO,SAAS,oBAAoB,OAAoC;AACtE,MAAI,UAAU,UAAa,UAAU,KAAM,QAAO;AAClD,QAAM,SAAS,OAAO,UAAU,WAAW,QAAQ,OAAO,KAAK;AAC/D,MACE,CAAC,OAAO,SAAS,MAAM,KACvB,UAAU,KACV,CAAC,OAAO,UAAU,MAAM,GACxB;AACA,UAAM,IAAI;AAAA,MACR,4CAA4C,KAAK,UAAU,KAAK,CAAC;AAAA,IACnE;AAAA,EACF;AACA,SAAO;AACT;AAOO,SAAS,oBACd,UACA,SAC0C;AAC1C,MAAI,OAAO,aAAa,YAAY,SAAS,KAAK,EAAE,WAAW,GAAG;AAChE,UAAM,IAAI,MAAM,iDAAiD;AAAA,EACnE;AACA,QAAM,SAAS,gBAAgB,QAAQ,MAAM;AAC7C,QAAM,SAAS,oBAAoB,QAAQ,MAAM;AACjD,QAAM,YACJ,OAAO,QAAQ,cAAc,YAC7B,QAAQ,UAAU,KAAK,EAAE,SAAS,IAC9B,QAAQ,UAAU,KAAK,IACvB;AACN,QAAM,UACJ,OAAO,QAAQ,QAAQ,YAAY,QAAQ,IAAI,KAAK,EAAE,SAAS,IAC3D,QAAQ,IAAI,KAAK,IACjB;AACN,SAAO;AAAA,IACL,OAAO;AAAA,IACP;AAAA,IACA,GAAI,WAAW,SAAY,EAAE,OAAO,IAAI,CAAC;AAAA,IACzC,GAAI,cAAc,SAAY,EAAE,UAAU,IAAI,CAAC;AAAA,IAC/C,GAAI,YAAY,SAAY,EAAE,QAAQ,IAAI,CAAC;AAAA,EAC7C;AACF;","names":[]}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
// src/recall-audit-anomaly.ts
|
|
2
|
+
var DEFAULT_ANOMALY_DETECTOR_CONFIG = Object.freeze({
|
|
3
|
+
enabled: false,
|
|
4
|
+
windowMs: 5 * 6e4,
|
|
5
|
+
repeatQueryLimit: 5,
|
|
6
|
+
namespaceWalkLimit: 3,
|
|
7
|
+
highCardinalityReturnLimit: 50,
|
|
8
|
+
rapidFireLimit: 30
|
|
9
|
+
});
|
|
10
|
+
function effectiveConfig(raw) {
|
|
11
|
+
const base = { ...DEFAULT_ANOMALY_DETECTOR_CONFIG };
|
|
12
|
+
if (!raw) return base;
|
|
13
|
+
const out = { ...base };
|
|
14
|
+
if (typeof raw.enabled === "boolean") out.enabled = raw.enabled;
|
|
15
|
+
if (typeof raw.windowMs === "number" && Number.isFinite(raw.windowMs) && raw.windowMs > 0) {
|
|
16
|
+
out.windowMs = raw.windowMs;
|
|
17
|
+
}
|
|
18
|
+
for (const key of [
|
|
19
|
+
"repeatQueryLimit",
|
|
20
|
+
"namespaceWalkLimit",
|
|
21
|
+
"highCardinalityReturnLimit",
|
|
22
|
+
"rapidFireLimit"
|
|
23
|
+
]) {
|
|
24
|
+
const v = raw[key];
|
|
25
|
+
if (typeof v === "number" && Number.isFinite(v) && v > 0) {
|
|
26
|
+
const floored = Math.floor(v);
|
|
27
|
+
out[key] = floored >= 1 ? floored : base[key];
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return out;
|
|
31
|
+
}
|
|
32
|
+
function normalizeQueryText(raw) {
|
|
33
|
+
if (typeof raw !== "string") return "";
|
|
34
|
+
return raw.toLowerCase().replace(/\s+/g, " ").trim();
|
|
35
|
+
}
|
|
36
|
+
var SEVERITY_ORDER = {
|
|
37
|
+
alert: 0,
|
|
38
|
+
warn: 1,
|
|
39
|
+
info: 2
|
|
40
|
+
};
|
|
41
|
+
function parseTsMs(ts) {
|
|
42
|
+
const n = new Date(ts).getTime();
|
|
43
|
+
return Number.isFinite(n) ? n : null;
|
|
44
|
+
}
|
|
45
|
+
function detectRecallAnomalies(input) {
|
|
46
|
+
const cfg = effectiveConfig(input.config);
|
|
47
|
+
const cutoff = input.now - cfg.windowMs;
|
|
48
|
+
const windowed = [];
|
|
49
|
+
for (let i = 0; i < input.entries.length; i++) {
|
|
50
|
+
const entry = input.entries[i];
|
|
51
|
+
const tsMs = parseTsMs(entry.ts);
|
|
52
|
+
if (tsMs === null) continue;
|
|
53
|
+
if (tsMs >= cutoff && tsMs <= input.now) {
|
|
54
|
+
windowed.push({ entry, index: i });
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (!cfg.enabled) {
|
|
58
|
+
return { flags: [], windowEntryCount: windowed.length, windowMs: cfg.windowMs };
|
|
59
|
+
}
|
|
60
|
+
const flags = [];
|
|
61
|
+
{
|
|
62
|
+
const byQuery = /* @__PURE__ */ new Map();
|
|
63
|
+
for (const { entry, index } of windowed) {
|
|
64
|
+
const key = normalizeQueryText(entry.queryText);
|
|
65
|
+
if (key.length === 0) continue;
|
|
66
|
+
const arr = byQuery.get(key);
|
|
67
|
+
if (arr) arr.push(index);
|
|
68
|
+
else byQuery.set(key, [index]);
|
|
69
|
+
}
|
|
70
|
+
for (const [query, indices] of byQuery) {
|
|
71
|
+
if (indices.length > cfg.repeatQueryLimit) {
|
|
72
|
+
flags.push({
|
|
73
|
+
kind: "repeat-query",
|
|
74
|
+
severity: "warn",
|
|
75
|
+
message: `query "${query}" repeated ${indices.length} times in ${cfg.windowMs}ms`,
|
|
76
|
+
entryIndices: indices.slice(),
|
|
77
|
+
signal: query
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
{
|
|
83
|
+
const namespaces = /* @__PURE__ */ new Set();
|
|
84
|
+
const indices = [];
|
|
85
|
+
for (const { entry, index } of windowed) {
|
|
86
|
+
if (!Array.isArray(entry.candidateMemoryIds)) continue;
|
|
87
|
+
let touchedAny = false;
|
|
88
|
+
for (const id of entry.candidateMemoryIds) {
|
|
89
|
+
if (typeof id !== "string" || id.length === 0) continue;
|
|
90
|
+
const slash = id.indexOf("/");
|
|
91
|
+
if (slash > 0) {
|
|
92
|
+
namespaces.add(id.slice(0, slash));
|
|
93
|
+
} else {
|
|
94
|
+
namespaces.add("__unprefixed__");
|
|
95
|
+
}
|
|
96
|
+
touchedAny = true;
|
|
97
|
+
}
|
|
98
|
+
if (touchedAny) indices.push(index);
|
|
99
|
+
}
|
|
100
|
+
if (namespaces.size > cfg.namespaceWalkLimit) {
|
|
101
|
+
flags.push({
|
|
102
|
+
kind: "namespace-walk",
|
|
103
|
+
severity: "alert",
|
|
104
|
+
message: `queries touched ${namespaces.size} distinct namespaces in ${cfg.windowMs}ms`,
|
|
105
|
+
entryIndices: indices,
|
|
106
|
+
signal: namespaces.size
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
{
|
|
111
|
+
for (const { entry, index } of windowed) {
|
|
112
|
+
const ids = Array.isArray(entry.candidateMemoryIds) ? entry.candidateMemoryIds : [];
|
|
113
|
+
if (ids.length > cfg.highCardinalityReturnLimit) {
|
|
114
|
+
flags.push({
|
|
115
|
+
kind: "high-cardinality-return",
|
|
116
|
+
severity: "alert",
|
|
117
|
+
message: `single recall returned ${ids.length} candidate memory IDs`,
|
|
118
|
+
entryIndices: [index],
|
|
119
|
+
signal: ids.length
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
if (windowed.length > cfg.rapidFireLimit) {
|
|
125
|
+
flags.push({
|
|
126
|
+
kind: "rapid-fire",
|
|
127
|
+
severity: "warn",
|
|
128
|
+
message: `${windowed.length} recall entries in ${cfg.windowMs}ms`,
|
|
129
|
+
entryIndices: windowed.map((w) => w.index),
|
|
130
|
+
signal: windowed.length
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
flags.sort((a, b) => {
|
|
134
|
+
const s = SEVERITY_ORDER[a.severity] - SEVERITY_ORDER[b.severity];
|
|
135
|
+
if (s !== 0) return s;
|
|
136
|
+
return a.kind.localeCompare(b.kind);
|
|
137
|
+
});
|
|
138
|
+
return { flags, windowEntryCount: windowed.length, windowMs: cfg.windowMs };
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export {
|
|
142
|
+
DEFAULT_ANOMALY_DETECTOR_CONFIG,
|
|
143
|
+
normalizeQueryText,
|
|
144
|
+
detectRecallAnomalies
|
|
145
|
+
};
|
|
146
|
+
//# sourceMappingURL=chunk-RFYAYKTD.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/recall-audit-anomaly.ts"],"sourcesContent":["/**\n * Recall-audit anomaly detector (issue #565 PR 5/5).\n *\n * Given a sequence of `RecallAuditEntry` records for a single principal /\n * session, classify the series and emit `AnomalyFlag`s for suspicious\n * patterns. Intended to run on both:\n *\n * 1. The MCP / HTTP access layers — every `recall` / `memory_search` /\n * `memory_timeline` call appends an audit entry and the detector is\n * invoked against the tail of the series (streaming mode).\n * 2. The existing Openclaw hook (already writes `transcripts/*.jsonl`) —\n * the detector can be invoked out-of-band over the same entries.\n *\n * See the threat model §5 (gap: recall-audit was only on the Openclaw\n * hook) and §6.3 (anomaly signals) for the patterns below.\n *\n * The module is pure: no I/O, no clock. Callers pass the entries +\n * configured thresholds; the detector returns a deterministic classification.\n *\n * Patterns detected:\n *\n * - `repeat-query` — the same normalized query text issued more than N\n * times in the window. Covers the ADAM \"exploitation\" phase where the\n * attacker re-queries a high-signal token until it plateaus.\n * - `namespace-walk` — the same principal's queries visit more than N\n * distinct candidate memory namespaces in the window. Suggests the\n * attacker is enumerating the namespace tree for leaks.\n * - `high-cardinality-return` — a single recall surfaced more than N\n * candidate memory IDs in one response. Covers the \"one query, dump\n * everything\" exfiltration path.\n * - `rapid-fire` — more than N recalls in the window irrespective of\n * content. Blunt instrument; useful when nothing else fires.\n *\n * Each flag carries the entry indices that support it so an audit UI can\n * highlight the underlying evidence.\n */\n\nimport type { RecallAuditEntry } from \"./recall-audit.js\";\n\nexport type AnomalyKind =\n | \"repeat-query\"\n | \"namespace-walk\"\n | \"high-cardinality-return\"\n | \"rapid-fire\";\n\nexport type AnomalySeverity = \"info\" | \"warn\" | \"alert\";\n\nexport interface AnomalyFlag {\n kind: AnomalyKind;\n severity: AnomalySeverity;\n /** Human-readable rationale, e.g. `\"query 'alex' repeated 12 times in 60s\"`. */\n message: string;\n /** Indices into the supplied `entries` array that triggered the flag. */\n entryIndices: number[];\n /**\n * Optional extra signal the caller can use to key metrics. For\n * `repeat-query` this is the normalized query text; for\n * `namespace-walk` it is the namespace count; etc.\n */\n signal?: string | number;\n}\n\nexport interface AnomalyDetectorConfig {\n /** Detector feature-flag. Defaults to false — ships disabled. */\n enabled?: boolean;\n /**\n * Rolling window in ms. The detector only considers entries whose `ts`\n * falls within `[now - windowMs, now]`. Default 5 minutes.\n */\n windowMs?: number;\n /**\n * Threshold for `repeat-query`: a normalized query repeated more than\n * this many times in the window trips the flag. Default 5.\n */\n repeatQueryLimit?: number;\n /**\n * Threshold for `namespace-walk`: more than this many distinct\n * candidate namespaces in the window trips the flag. Default 3.\n */\n namespaceWalkLimit?: number;\n /**\n * Threshold for `high-cardinality-return`: more than this many\n * `candidateMemoryIds` in a single entry trips the flag. Default 50.\n */\n highCardinalityReturnLimit?: number;\n /**\n * Threshold for `rapid-fire`: more than this many entries in the\n * window trips the flag. Default 30. (Matches the default\n * `recallCrossNamespaceBudgetHardLimit` from PR 4.)\n */\n rapidFireLimit?: number;\n}\n\nexport const DEFAULT_ANOMALY_DETECTOR_CONFIG: Required<AnomalyDetectorConfig> =\n Object.freeze({\n enabled: false,\n windowMs: 5 * 60_000,\n repeatQueryLimit: 5,\n namespaceWalkLimit: 3,\n highCardinalityReturnLimit: 50,\n rapidFireLimit: 30,\n });\n\nexport interface AnomalyDetectorInput {\n entries: readonly RecallAuditEntry[];\n /** Current time in epoch ms. Entries older than `now - windowMs` are ignored. */\n now: number;\n config?: AnomalyDetectorConfig;\n}\n\nexport interface AnomalyDetectorResult {\n /** All flags ordered by descending severity then kind. */\n flags: AnomalyFlag[];\n /** Number of entries inside the active window (after filtering). */\n windowEntryCount: number;\n /** Window size actually used, in ms. */\n windowMs: number;\n}\n\nfunction effectiveConfig(\n raw: AnomalyDetectorConfig | undefined,\n): Required<AnomalyDetectorConfig> {\n const base = { ...DEFAULT_ANOMALY_DETECTOR_CONFIG };\n if (!raw) return base;\n const out = { ...base };\n if (typeof raw.enabled === \"boolean\") out.enabled = raw.enabled;\n if (\n typeof raw.windowMs === \"number\" &&\n Number.isFinite(raw.windowMs) &&\n raw.windowMs > 0\n ) {\n out.windowMs = raw.windowMs;\n }\n for (const key of [\n \"repeatQueryLimit\",\n \"namespaceWalkLimit\",\n \"highCardinalityReturnLimit\",\n \"rapidFireLimit\",\n ] as const) {\n const v = raw[key];\n if (typeof v === \"number\" && Number.isFinite(v) && v > 0) {\n const floored = Math.floor(v);\n // Guard against fractional inputs (e.g. 0.5) that floor to 0 and\n // turn every detector into a flood-on-anything trigger.\n out[key] = floored >= 1 ? floored : base[key];\n }\n }\n return out;\n}\n\n/**\n * Normalize query text so cosmetic variation doesn't defeat the\n * repeat-query detector. Lowercase, collapse whitespace, trim.\n */\nexport function normalizeQueryText(raw: string): string {\n if (typeof raw !== \"string\") return \"\";\n return raw.toLowerCase().replace(/\\s+/g, \" \").trim();\n}\n\nconst SEVERITY_ORDER: Record<AnomalySeverity, number> = {\n alert: 0,\n warn: 1,\n info: 2,\n};\n\nfunction parseTsMs(ts: string): number | null {\n const n = new Date(ts).getTime();\n return Number.isFinite(n) ? n : null;\n}\n\n/**\n * Run the detector against a series of audit entries. Pure; no side\n * effects.\n */\nexport function detectRecallAnomalies(\n input: AnomalyDetectorInput,\n): AnomalyDetectorResult {\n const cfg = effectiveConfig(input.config);\n const cutoff = input.now - cfg.windowMs;\n\n // Filter to the active window, preserving original indices so flags\n // can reference the caller's entries array directly.\n const windowed: { entry: RecallAuditEntry; index: number }[] = [];\n for (let i = 0; i < input.entries.length; i++) {\n const entry = input.entries[i]!;\n const tsMs = parseTsMs(entry.ts);\n if (tsMs === null) continue;\n if (tsMs >= cutoff && tsMs <= input.now) {\n windowed.push({ entry, index: i });\n }\n }\n\n if (!cfg.enabled) {\n return { flags: [], windowEntryCount: windowed.length, windowMs: cfg.windowMs };\n }\n\n const flags: AnomalyFlag[] = [];\n\n // 1) repeat-query: bucket by normalized queryText.\n {\n const byQuery = new Map<string, number[]>();\n for (const { entry, index } of windowed) {\n const key = normalizeQueryText(entry.queryText);\n if (key.length === 0) continue;\n const arr = byQuery.get(key);\n if (arr) arr.push(index);\n else byQuery.set(key, [index]);\n }\n for (const [query, indices] of byQuery) {\n if (indices.length > cfg.repeatQueryLimit) {\n flags.push({\n kind: \"repeat-query\",\n severity: \"warn\",\n message: `query \"${query}\" repeated ${indices.length} times in ${cfg.windowMs}ms`,\n entryIndices: indices.slice(),\n signal: query,\n });\n }\n }\n }\n\n // 2) namespace-walk: distinct namespaces extracted from candidateMemoryIds.\n // Candidate IDs that begin with `<namespace>/` disclose the namespace\n // directly. IDs without a slash prefix (e.g. bare filenames) do not\n // disclose a namespace, but a recall whose result list mixes\n // prefixed AND unprefixed IDs is itself suspicious — it suggests\n // the backend leaked memories from both the caller's own namespace\n // and named tenants. We track unprefixed IDs under a reserved\n // sentinel namespace so they still contribute to the count when a\n // trail is all-unprefixed (common for older audit entries sourced\n // from filenames).\n {\n const namespaces = new Set<string>();\n const indices: number[] = [];\n for (const { entry, index } of windowed) {\n if (!Array.isArray(entry.candidateMemoryIds)) continue;\n let touchedAny = false;\n for (const id of entry.candidateMemoryIds) {\n if (typeof id !== \"string\" || id.length === 0) continue;\n const slash = id.indexOf(\"/\");\n if (slash > 0) {\n namespaces.add(id.slice(0, slash));\n } else {\n namespaces.add(\"__unprefixed__\");\n }\n touchedAny = true;\n }\n if (touchedAny) indices.push(index);\n }\n if (namespaces.size > cfg.namespaceWalkLimit) {\n flags.push({\n kind: \"namespace-walk\",\n severity: \"alert\",\n message: `queries touched ${namespaces.size} distinct namespaces in ${cfg.windowMs}ms`,\n entryIndices: indices,\n signal: namespaces.size,\n });\n }\n }\n\n // 3) high-cardinality-return: one entry's candidateMemoryIds > limit.\n {\n for (const { entry, index } of windowed) {\n const ids = Array.isArray(entry.candidateMemoryIds)\n ? entry.candidateMemoryIds\n : [];\n if (ids.length > cfg.highCardinalityReturnLimit) {\n flags.push({\n kind: \"high-cardinality-return\",\n severity: \"alert\",\n message: `single recall returned ${ids.length} candidate memory IDs`,\n entryIndices: [index],\n signal: ids.length,\n });\n }\n }\n }\n\n // 4) rapid-fire: windowed count alone.\n if (windowed.length > cfg.rapidFireLimit) {\n flags.push({\n kind: \"rapid-fire\",\n severity: \"warn\",\n message: `${windowed.length} recall entries in ${cfg.windowMs}ms`,\n entryIndices: windowed.map((w) => w.index),\n signal: windowed.length,\n });\n }\n\n flags.sort((a, b) => {\n const s = SEVERITY_ORDER[a.severity] - SEVERITY_ORDER[b.severity];\n if (s !== 0) return s;\n return a.kind.localeCompare(b.kind);\n });\n\n return { flags, windowEntryCount: windowed.length, windowMs: cfg.windowMs };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
StorageManager
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-F5VP6YCB.js";
|
|
4
4
|
import {
|
|
5
5
|
parseContinuityImprovementLoops,
|
|
6
6
|
parseContinuityIncident
|
|
@@ -1860,4 +1860,4 @@ export {
|
|
|
1860
1860
|
defaultTierMigrationCycleBudget,
|
|
1861
1861
|
CompoundingEngine
|
|
1862
1862
|
};
|
|
1863
|
-
//# sourceMappingURL=chunk-
|
|
1863
|
+
//# sourceMappingURL=chunk-RGLL5SPU.js.map
|
|
@@ -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";
|
|
@@ -16,17 +19,17 @@ import {
|
|
|
16
19
|
import {
|
|
17
20
|
LocalLlmClient
|
|
18
21
|
} from "./chunk-JL2PU6AI.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-3GXCSUXR.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-S3EEFKNY.js.map
|