@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
|
@@ -15,18 +15,29 @@ var sessionKeySchema = z.string().trim().min(1).max(512).optional();
|
|
|
15
15
|
var idempotencyKeySchema = z.string().trim().min(1).max(256).optional();
|
|
16
16
|
var dryRunSchema = z.boolean().optional();
|
|
17
17
|
var schemaVersionSchema = z.number().int().optional();
|
|
18
|
+
var codingContextSchema = z.object({
|
|
19
|
+
projectId: z.string().trim().min(1, "codingContext.projectId is required").max(128),
|
|
20
|
+
branch: z.string().trim().max(256).nullable(),
|
|
21
|
+
rootPath: z.string().trim().min(1, "codingContext.rootPath is required").max(1024),
|
|
22
|
+
defaultBranch: z.string().trim().max(256).nullable()
|
|
23
|
+
}).nullable();
|
|
18
24
|
var recallRequestSchema = z.object({
|
|
19
25
|
query: z.string().min(1, "query is required"),
|
|
20
26
|
sessionKey: sessionKeySchema,
|
|
21
27
|
namespace: namespaceSchema,
|
|
22
28
|
topK: z.number().int().min(0).max(200).optional(),
|
|
23
29
|
mode: z.enum(["auto", "no_recall", "minimal", "full", "graph_mode"]).optional(),
|
|
24
|
-
includeDebug: z.boolean().optional()
|
|
30
|
+
includeDebug: z.boolean().optional(),
|
|
31
|
+
codingContext: codingContextSchema.optional()
|
|
25
32
|
});
|
|
26
33
|
var recallExplainRequestSchema = z.object({
|
|
27
34
|
sessionKey: sessionKeySchema,
|
|
28
35
|
namespace: namespaceSchema
|
|
29
36
|
});
|
|
37
|
+
var setCodingContextRequestSchema = z.object({
|
|
38
|
+
sessionKey: z.string().trim().min(1, "sessionKey is required").max(512),
|
|
39
|
+
codingContext: codingContextSchema
|
|
40
|
+
});
|
|
30
41
|
var messageSchema = z.object({
|
|
31
42
|
role: z.enum(["user", "assistant"]),
|
|
32
43
|
content: z.string().min(1, "message content must be non-empty")
|
|
@@ -50,7 +61,8 @@ var categorySchema = z.enum([
|
|
|
50
61
|
"moment",
|
|
51
62
|
"skill",
|
|
52
63
|
"rule",
|
|
53
|
-
"procedure"
|
|
64
|
+
"procedure",
|
|
65
|
+
"reasoning_trace"
|
|
54
66
|
]).optional();
|
|
55
67
|
var confidenceSchema = z.number().min(0).max(1).optional();
|
|
56
68
|
var tagsSchema = z.array(z.string().max(256)).max(50).optional();
|
|
@@ -116,6 +128,7 @@ var daySummaryRequestSchema = z.object({
|
|
|
116
128
|
var schemas = {
|
|
117
129
|
recall: recallRequestSchema,
|
|
118
130
|
recallExplain: recallExplainRequestSchema,
|
|
131
|
+
setCodingContext: setCodingContextRequestSchema,
|
|
119
132
|
observe: observeRequestSchema,
|
|
120
133
|
memoryStore: memoryStoreRequestSchema,
|
|
121
134
|
suggestionSubmit: suggestionSubmitRequestSchema,
|
|
@@ -146,8 +159,10 @@ function validateRequest(schemaName, body) {
|
|
|
146
159
|
|
|
147
160
|
export {
|
|
148
161
|
formatZodError,
|
|
162
|
+
codingContextSchema,
|
|
149
163
|
recallRequestSchema,
|
|
150
164
|
recallExplainRequestSchema,
|
|
165
|
+
setCodingContextRequestSchema,
|
|
151
166
|
observeRequestSchema,
|
|
152
167
|
memoryStoreRequestSchema,
|
|
153
168
|
suggestionSubmitRequestSchema,
|
|
@@ -158,4 +173,4 @@ export {
|
|
|
158
173
|
daySummaryRequestSchema,
|
|
159
174
|
validateRequest
|
|
160
175
|
};
|
|
161
|
-
//# sourceMappingURL=chunk-
|
|
176
|
+
//# sourceMappingURL=chunk-WCLICCGB.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/access-schema.ts"],"sourcesContent":["// Request/response schema validation for the Remnic HTTP API.\n// Uses zod for runtime validation — returns structured 400 errors with\n// field-level detail so consumers get clear feedback on malformed requests.\n\nimport { z } from \"zod\";\n\n// ---------------------------------------------------------------------------\n// Error formatting\n// ---------------------------------------------------------------------------\n\nexport interface SchemaValidationError {\n error: string;\n code: \"validation_error\";\n details: Array<{ field: string; message: string }>;\n}\n\nexport function formatZodError(error: z.ZodError): SchemaValidationError {\n return {\n error: \"request validation failed\",\n code: \"validation_error\",\n details: error.issues.map((issue) => ({\n field: issue.path.join(\".\") || \"(root)\",\n message: issue.message,\n })),\n };\n}\n\n// ---------------------------------------------------------------------------\n// Shared fields\n// ---------------------------------------------------------------------------\n\nconst namespaceSchema = z.string().trim().max(256).optional();\nconst sessionKeySchema = z.string().trim().min(1).max(512).optional();\nconst idempotencyKeySchema = z.string().trim().min(1).max(256).optional();\nconst dryRunSchema = z.boolean().optional();\nconst schemaVersionSchema = z.number().int().optional();\n\n// ---------------------------------------------------------------------------\n// Recall\n// ---------------------------------------------------------------------------\n\n/**\n * Coding-agent context (issue #569). Optional payload that connectors may\n * ship with a recall request so the project/branch namespace overlay\n * applies to that recall. All fields are validated per CLAUDE.md #51 —\n * empty-string projectId / rootPath is rejected, not silently accepted.\n */\nexport const codingContextSchema = z\n .object({\n projectId: z.string().trim().min(1, \"codingContext.projectId is required\").max(128),\n branch: z.string().trim().max(256).nullable(),\n rootPath: z.string().trim().min(1, \"codingContext.rootPath is required\").max(1024),\n defaultBranch: z.string().trim().max(256).nullable(),\n })\n .nullable();\n\nexport const recallRequestSchema = z.object({\n query: z.string().min(1, \"query is required\"),\n sessionKey: sessionKeySchema,\n namespace: namespaceSchema,\n topK: z.number().int().min(0).max(200).optional(),\n mode: z.enum([\"auto\", \"no_recall\", \"minimal\", \"full\", \"graph_mode\"]).optional(),\n includeDebug: z.boolean().optional(),\n codingContext: codingContextSchema.optional(),\n});\n\nexport const recallExplainRequestSchema = z.object({\n sessionKey: sessionKeySchema,\n namespace: namespaceSchema,\n});\n\n/**\n * Standalone \"set coding context\" request. Used by the HTTP endpoint\n * `POST /engram/v1/coding-context` and the MCP `remnic.set_coding_context`\n * tool (PR 7). `codingContext: null` clears the attached context.\n */\nexport const setCodingContextRequestSchema = z.object({\n sessionKey: z.string().trim().min(1, \"sessionKey is required\").max(512),\n codingContext: codingContextSchema,\n});\n\n// ---------------------------------------------------------------------------\n// Observe\n// ---------------------------------------------------------------------------\n\nconst messageSchema = z.object({\n role: z.enum([\"user\", \"assistant\"]),\n content: z.string().min(1, \"message content must be non-empty\"),\n});\n\nexport const observeRequestSchema = z.object({\n sessionKey: z.string().trim().min(1, \"sessionKey is required\").max(512),\n messages: z.array(messageSchema).min(1, \"messages must be a non-empty array\"),\n namespace: namespaceSchema,\n skipExtraction: z.boolean().optional(),\n});\n\n// ---------------------------------------------------------------------------\n// Memory store / suggestion submit\n// ---------------------------------------------------------------------------\n\nconst writeContentSchema = z.string().min(1, \"content is required\").max(50000);\nconst categorySchema = z\n .enum([\n \"fact\", \"preference\", \"correction\", \"entity\", \"decision\",\n \"relationship\", \"principle\", \"commitment\", \"moment\", \"skill\", \"rule\", \"procedure\",\n \"reasoning_trace\",\n ])\n .optional();\nconst confidenceSchema = z.number().min(0).max(1).optional();\nconst tagsSchema = z.array(z.string().max(256)).max(50).optional();\nconst entityRefSchema = z.string().trim().max(512).optional();\nconst ttlSchema = z.string().trim().max(128).optional();\nconst sourceReasonSchema = z.string().trim().max(2000).optional();\n\nexport const memoryStoreRequestSchema = z.object({\n schemaVersion: schemaVersionSchema,\n idempotencyKey: idempotencyKeySchema,\n dryRun: dryRunSchema,\n sessionKey: sessionKeySchema,\n content: writeContentSchema,\n category: categorySchema,\n confidence: confidenceSchema,\n namespace: namespaceSchema,\n tags: tagsSchema,\n entityRef: entityRefSchema,\n ttl: ttlSchema,\n sourceReason: sourceReasonSchema,\n});\n\nexport const suggestionSubmitRequestSchema = memoryStoreRequestSchema;\n\n// ---------------------------------------------------------------------------\n// Review disposition\n// ---------------------------------------------------------------------------\n\nexport const reviewDispositionRequestSchema = z.object({\n memoryId: z.string().trim().min(1, \"memoryId is required\"),\n status: z.enum([\n \"active\", \"pending_review\", \"quarantined\", \"rejected\", \"superseded\", \"archived\",\n ]),\n reasonCode: z.string().trim().min(1, \"reasonCode is required\"),\n namespace: namespaceSchema,\n});\n\n// ---------------------------------------------------------------------------\n// Trust-zone promote\n// ---------------------------------------------------------------------------\n\nexport const trustZonePromoteRequestSchema = z.object({\n recordId: z.string().trim().min(1, \"recordId is required\"),\n targetZone: z.enum([\"working\", \"trusted\"], {\n errorMap: () => ({ message: \"targetZone must be 'working' or 'trusted'\" }),\n }),\n promotionReason: z.string().trim().min(1, \"promotionReason is required\"),\n recordedAt: z.string().trim().optional(),\n summary: z.string().trim().max(5000).optional(),\n dryRun: dryRunSchema,\n namespace: namespaceSchema,\n});\n\n// ---------------------------------------------------------------------------\n// Trust-zone demo-seed\n// ---------------------------------------------------------------------------\n\nexport const trustZoneDemoSeedRequestSchema = z.object({\n scenario: z.string().trim().max(256).optional(),\n recordedAt: z.string().trim().optional(),\n dryRun: dryRunSchema,\n namespace: namespaceSchema,\n});\n\n// ---------------------------------------------------------------------------\n// LCM search\n// ---------------------------------------------------------------------------\n\nexport const lcmSearchRequestSchema = z.object({\n query: z.string().min(1, \"query is required\"),\n sessionKey: sessionKeySchema,\n namespace: namespaceSchema,\n limit: z.number().int().min(1).max(100).optional(),\n});\n\n// ---------------------------------------------------------------------------\n// Day summary\n// ---------------------------------------------------------------------------\n\nexport const daySummaryRequestSchema = z.object({\n memories: z.string().max(100000).optional(),\n sessionKey: sessionKeySchema,\n namespace: namespaceSchema,\n});\n\n// ---------------------------------------------------------------------------\n// Inferred types\n// ---------------------------------------------------------------------------\n\nexport type RecallRequest = z.infer<typeof recallRequestSchema>;\nexport type RecallExplainRequest = z.infer<typeof recallExplainRequestSchema>;\nexport type SetCodingContextRequest = z.infer<typeof setCodingContextRequestSchema>;\nexport type ObserveRequest = z.infer<typeof observeRequestSchema>;\nexport type MemoryStoreRequest = z.infer<typeof memoryStoreRequestSchema>;\nexport type SuggestionSubmitRequest = z.infer<typeof suggestionSubmitRequestSchema>;\nexport type ReviewDispositionRequest = z.infer<typeof reviewDispositionRequestSchema>;\nexport type TrustZonePromoteRequest = z.infer<typeof trustZonePromoteRequestSchema>;\nexport type TrustZoneDemoSeedRequest = z.infer<typeof trustZoneDemoSeedRequestSchema>;\nexport type LcmSearchRequest = z.infer<typeof lcmSearchRequestSchema>;\nexport type DaySummaryRequest = z.infer<typeof daySummaryRequestSchema>;\n\n// ---------------------------------------------------------------------------\n// Validation helper\n// ---------------------------------------------------------------------------\n\nexport type SchemaName =\n | \"recall\"\n | \"recallExplain\"\n | \"setCodingContext\"\n | \"observe\"\n | \"memoryStore\"\n | \"suggestionSubmit\"\n | \"reviewDisposition\"\n | \"trustZonePromote\"\n | \"trustZoneDemoSeed\"\n | \"lcmSearch\"\n | \"daySummary\";\n\nexport type SchemaTypeFor<N extends SchemaName> =\n N extends \"recall\" ? RecallRequest\n : N extends \"recallExplain\" ? RecallExplainRequest\n : N extends \"setCodingContext\" ? SetCodingContextRequest\n : N extends \"observe\" ? ObserveRequest\n : N extends \"memoryStore\" ? MemoryStoreRequest\n : N extends \"suggestionSubmit\" ? SuggestionSubmitRequest\n : N extends \"reviewDisposition\" ? ReviewDispositionRequest\n : N extends \"trustZonePromote\" ? TrustZonePromoteRequest\n : N extends \"trustZoneDemoSeed\" ? TrustZoneDemoSeedRequest\n : N extends \"lcmSearch\" ? LcmSearchRequest\n : N extends \"daySummary\" ? DaySummaryRequest\n : never;\n\nconst schemas: Record<SchemaName, z.ZodTypeAny> = {\n recall: recallRequestSchema,\n recallExplain: recallExplainRequestSchema,\n setCodingContext: setCodingContextRequestSchema,\n observe: observeRequestSchema,\n memoryStore: memoryStoreRequestSchema,\n suggestionSubmit: suggestionSubmitRequestSchema,\n reviewDisposition: reviewDispositionRequestSchema,\n trustZonePromote: trustZonePromoteRequestSchema,\n trustZoneDemoSeed: trustZoneDemoSeedRequestSchema,\n lcmSearch: lcmSearchRequestSchema,\n daySummary: daySummaryRequestSchema,\n};\n\n/**\n * Validate a request body against the named schema.\n * Returns `{ success: true, data }` on pass or\n * `{ success: false, error }` on failure with field-level detail.\n */\nexport function validateRequest<T = unknown>(\n schemaName: SchemaName,\n body: unknown,\n): { success: true; data: T } | { success: false; error: SchemaValidationError } {\n const schema = schemas[schemaName];\n if (!schema) {\n return {\n success: false,\n error: {\n error: `unknown schema: ${schemaName}`,\n code: \"validation_error\",\n details: [],\n },\n };\n }\n const result = schema.safeParse(body);\n if (result.success) {\n return { success: true, data: result.data as T };\n }\n return { success: false, error: formatZodError(result.error) };\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
|
log
|
|
6
6
|
} from "./chunk-2ODBA7MQ.js";
|
|
@@ -810,4 +810,4 @@ export {
|
|
|
810
810
|
resolveBriefingSaveDir,
|
|
811
811
|
briefingFilename
|
|
812
812
|
};
|
|
813
|
-
//# sourceMappingURL=chunk-
|
|
813
|
+
//# sourceMappingURL=chunk-WVVA7F5A.js.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// src/consolidation-operator.ts
|
|
2
|
+
var CONSOLIDATION_OPERATORS = [
|
|
3
|
+
"split",
|
|
4
|
+
"merge",
|
|
5
|
+
"update"
|
|
6
|
+
];
|
|
7
|
+
var DERIVED_FROM_ENTRY_RE = /^(.+):(\d+)$/;
|
|
8
|
+
function isValidDerivedFromEntry(entry) {
|
|
9
|
+
if (typeof entry !== "string") return false;
|
|
10
|
+
const match = entry.match(DERIVED_FROM_ENTRY_RE);
|
|
11
|
+
if (!match) return false;
|
|
12
|
+
const pathPart = match[1];
|
|
13
|
+
if (pathPart.length === 0 || pathPart.trim().length === 0) return false;
|
|
14
|
+
const versionNum = Number(match[2]);
|
|
15
|
+
return Number.isInteger(versionNum) && versionNum >= 0;
|
|
16
|
+
}
|
|
17
|
+
function isConsolidationOperator(value) {
|
|
18
|
+
return typeof value === "string" && CONSOLIDATION_OPERATORS.includes(value);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export {
|
|
22
|
+
CONSOLIDATION_OPERATORS,
|
|
23
|
+
isValidDerivedFromEntry,
|
|
24
|
+
isConsolidationOperator
|
|
25
|
+
};
|
|
26
|
+
//# sourceMappingURL=chunk-X6GF3FX2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/consolidation-operator.ts"],"sourcesContent":["/**\n * consolidation-operator.ts — Standalone operator vocabulary + validators\n * for the consolidation subsystem (issue #561, All-Mem paper\n * arxiv:2603.19595).\n *\n * This module is intentionally dependency-free so storage, the `remnic\n * doctor` check (PR 4), and the undo CLI (PR 5) can import the validators\n * without dragging in the full consolidation engine — which in turn pulls\n * in the Codex materialize runner and creates a `storage → consolidation\n * → codex-materialize-runner → storage` import cycle.\n *\n * The `semantic-consolidation.ts` module re-exports these symbols so\n * existing import paths continue to work.\n */\n\n/**\n * Operator algebra for non-destructive consolidation.\n *\n * - `split` — one source memory is rewritten as multiple smaller memories.\n * - `merge` — multiple source memories are collapsed into one canonical\n * memory.\n * - `update` — a newer value supersedes an older value within the same\n * logical fact.\n */\nexport type ConsolidationOperator = \"split\" | \"merge\" | \"update\";\n\n/**\n * Allowed values for the `derived_via` frontmatter field. Used by storage\n * validation to reject unknown operator values on write.\n */\nexport const CONSOLIDATION_OPERATORS: readonly ConsolidationOperator[] = [\n \"split\",\n \"merge\",\n \"update\",\n] as const;\n\n/**\n * Regular expression for validating a single `derived_from` entry.\n *\n * Format: `<non-empty memory path>:<integer version >= 0>`. Matches the\n * `path:versionNumber` convention used by `page-versioning.ts` snapshots\n * (e.g. `\"facts/preferences.md:3\"`). The path portion is greedy-last so\n * paths that themselves contain a colon remain parseable — only the final\n * `:<digits>` is consumed as the version.\n */\nconst DERIVED_FROM_ENTRY_RE = /^(.+):(\\d+)$/;\n\n/**\n * Validate a `derived_from` entry string. Returns `true` if the entry\n * parses as `<non-empty path>:<integer >= 0>`. Kept pure so storage and\n * future CLI/doctor paths can share the same validator.\n */\nexport function isValidDerivedFromEntry(entry: unknown): entry is string {\n if (typeof entry !== \"string\") return false;\n const match = entry.match(DERIVED_FROM_ENTRY_RE);\n if (!match) return false;\n const pathPart = match[1];\n if (pathPart.length === 0 || pathPart.trim().length === 0) return false;\n const versionNum = Number(match[2]);\n return Number.isInteger(versionNum) && versionNum >= 0;\n}\n\n/**\n * Type guard for `ConsolidationOperator`.\n */\nexport function isConsolidationOperator(value: unknown): value is ConsolidationOperator {\n return (\n typeof value === \"string\" &&\n (CONSOLIDATION_OPERATORS as readonly string[]).includes(value)\n );\n}\n"],"mappings":";AA8BO,IAAM,0BAA4D;AAAA,EACvE;AAAA,EACA;AAAA,EACA;AACF;AAWA,IAAM,wBAAwB;AAOvB,SAAS,wBAAwB,OAAiC;AACvE,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,QAAM,QAAQ,MAAM,MAAM,qBAAqB;AAC/C,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,WAAW,MAAM,CAAC;AACxB,MAAI,SAAS,WAAW,KAAK,SAAS,KAAK,EAAE,WAAW,EAAG,QAAO;AAClE,QAAM,aAAa,OAAO,MAAM,CAAC,CAAC;AAClC,SAAO,OAAO,UAAU,UAAU,KAAK,cAAc;AACvD;AAKO,SAAS,wBAAwB,OAAgD;AACtF,SACE,OAAO,UAAU,YAChB,wBAA8C,SAAS,KAAK;AAEjE;","names":[]}
|
|
@@ -5,6 +5,9 @@ import {
|
|
|
5
5
|
countRecallTokenOverlap,
|
|
6
6
|
normalizeRecallTokens
|
|
7
7
|
} from "./chunk-DT5TVLJE.js";
|
|
8
|
+
import {
|
|
9
|
+
log
|
|
10
|
+
} from "./chunk-2ODBA7MQ.js";
|
|
8
11
|
import {
|
|
9
12
|
assertIsoRecordedAt,
|
|
10
13
|
assertString,
|
|
@@ -15,9 +18,6 @@ import {
|
|
|
15
18
|
listJsonFiles,
|
|
16
19
|
readJsonFile
|
|
17
20
|
} from "./chunk-LPSF4OQH.js";
|
|
18
|
-
import {
|
|
19
|
-
log
|
|
20
|
-
} from "./chunk-2ODBA7MQ.js";
|
|
21
21
|
|
|
22
22
|
// src/causal-chain.ts
|
|
23
23
|
import path from "path";
|
|
@@ -280,4 +280,4 @@ export {
|
|
|
280
280
|
scoreStitchCandidate,
|
|
281
281
|
stitchCausalChain
|
|
282
282
|
};
|
|
283
|
-
//# sourceMappingURL=chunk-
|
|
283
|
+
//# sourceMappingURL=chunk-XMHBH5H6.js.map
|
|
@@ -53,13 +53,25 @@ async function getGatewayResolver() {
|
|
|
53
53
|
async function findRuntimeModules() {
|
|
54
54
|
const { readdirSync } = await import("fs");
|
|
55
55
|
const { createRequire } = await import("module");
|
|
56
|
+
const { execFileSync } = await import("child_process");
|
|
56
57
|
const candidates = [];
|
|
57
58
|
const distDirs = [];
|
|
59
|
+
const pushDistDirs = (entryPath) => {
|
|
60
|
+
const resolvedEntryDir = path.dirname(entryPath);
|
|
61
|
+
const packageRoot = path.basename(resolvedEntryDir) === "dist" ? path.resolve(resolvedEntryDir, "..") : resolvedEntryDir;
|
|
62
|
+
const candidateDistDirs = [
|
|
63
|
+
path.join(packageRoot, "dist"),
|
|
64
|
+
path.join(packageRoot, "..", "dist")
|
|
65
|
+
];
|
|
66
|
+
for (const candidate of candidateDistDirs) {
|
|
67
|
+
const resolved = path.resolve(candidate);
|
|
68
|
+
if (!distDirs.includes(resolved)) distDirs.push(resolved);
|
|
69
|
+
}
|
|
70
|
+
};
|
|
58
71
|
try {
|
|
59
72
|
const req = createRequire(import.meta.url);
|
|
60
73
|
const openclawMain = req.resolve("openclaw");
|
|
61
|
-
|
|
62
|
-
if (openclawDist) distDirs.push(path.resolve(openclawDist));
|
|
74
|
+
pushDistDirs(openclawMain);
|
|
63
75
|
} catch {
|
|
64
76
|
}
|
|
65
77
|
try {
|
|
@@ -68,12 +80,22 @@ async function findRuntimeModules() {
|
|
|
68
80
|
if (mainScript) {
|
|
69
81
|
const realScript = realpathSync(mainScript);
|
|
70
82
|
if (realScript.includes("openclaw")) {
|
|
71
|
-
|
|
72
|
-
if (!distDirs.includes(distDir)) distDirs.push(distDir);
|
|
83
|
+
pushDistDirs(realScript);
|
|
73
84
|
}
|
|
74
85
|
}
|
|
75
86
|
} catch {
|
|
76
87
|
}
|
|
88
|
+
try {
|
|
89
|
+
const openclawBin = execFileSync("which", ["openclaw"], {
|
|
90
|
+
encoding: "utf8",
|
|
91
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
92
|
+
}).trim();
|
|
93
|
+
if (openclawBin) {
|
|
94
|
+
const { realpathSync } = await import("fs");
|
|
95
|
+
pushDistDirs(realpathSync(openclawBin));
|
|
96
|
+
}
|
|
97
|
+
} catch {
|
|
98
|
+
}
|
|
77
99
|
for (const dir of distDirs) {
|
|
78
100
|
try {
|
|
79
101
|
const files = readdirSync(dir);
|
|
@@ -88,7 +110,10 @@ async function findRuntimeModules() {
|
|
|
88
110
|
return candidates;
|
|
89
111
|
}
|
|
90
112
|
async function resolveProviderApiKey(providerId, apiKeyValue, gatewayConfig, agentDir) {
|
|
91
|
-
const
|
|
113
|
+
const resolvedAgentDir = path.resolve(
|
|
114
|
+
agentDir ?? path.join(os.homedir(), ".openclaw", "agents", "main", "agent")
|
|
115
|
+
);
|
|
116
|
+
const cacheKey = `provider:${providerId}:agentDir:${resolvedAgentDir}`;
|
|
92
117
|
if (resolvedCache.has(cacheKey)) {
|
|
93
118
|
return resolvedCache.get(cacheKey);
|
|
94
119
|
}
|
|
@@ -104,7 +129,6 @@ async function resolveProviderApiKey(providerId, apiKeyValue, gatewayConfig, age
|
|
|
104
129
|
const resolver = await getGatewayResolver();
|
|
105
130
|
if (resolver) {
|
|
106
131
|
try {
|
|
107
|
-
const resolvedAgentDir = agentDir ?? path.join(os.homedir(), ".openclaw", "agents", "main", "agent");
|
|
108
132
|
const auth = await resolver({ provider: providerId, cfg: gatewayConfig, agentDir: resolvedAgentDir });
|
|
109
133
|
if (auth?.apiKey) {
|
|
110
134
|
resolved = auth.apiKey;
|
|
@@ -154,10 +178,16 @@ function clearSecretCache() {
|
|
|
154
178
|
_resolverLoaded = false;
|
|
155
179
|
_resolverNextRetryAt = 0;
|
|
156
180
|
}
|
|
181
|
+
function __setGatewayResolverForTest(resolver) {
|
|
182
|
+
_resolveApiKeyForProvider = resolver;
|
|
183
|
+
_resolverLoaded = resolver !== null;
|
|
184
|
+
_resolverNextRetryAt = 0;
|
|
185
|
+
}
|
|
157
186
|
|
|
158
187
|
export {
|
|
159
188
|
resolveProviderApiKey,
|
|
160
189
|
getGatewayRuntimeAuthForModel,
|
|
161
|
-
clearSecretCache
|
|
190
|
+
clearSecretCache,
|
|
191
|
+
__setGatewayResolverForTest
|
|
162
192
|
};
|
|
163
|
-
//# sourceMappingURL=chunk-
|
|
193
|
+
//# sourceMappingURL=chunk-XZ2TIKGC.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/resolve-provider-secret.ts"],"sourcesContent":["import { log } from \"./logger.js\";\nimport { readEnvVar } from \"./runtime/env.js\";\nimport path from \"node:path\";\nimport os from \"node:os\";\n\n/**\n * Resolve a provider API key using OpenClaw's own auth resolution system.\n *\n * This module delegates to the gateway's `resolveApiKeyForProvider()` function,\n * which handles all secret reference formats (SecretRef objects, auth profiles,\n * \"secretref-managed\" markers, environment variables, etc.) using the same\n * codepath the gateway uses for its own agent sessions.\n *\n * For plain-text API keys, a fast path returns them directly without\n * involving the gateway auth system.\n *\n * Results are cached per provider for the gateway process lifetime.\n */\n\ntype ResolveApiKeyFn = (params: {\n provider: string;\n cfg?: unknown;\n agentDir?: string;\n}) => Promise<{ apiKey?: string; source?: string; mode?: string } | null>;\n\n/**\n * Resolve request-ready auth for a model, including provider-owned transforms\n * (e.g., OAuth token exchange, base URL override for openai-codex).\n */\nexport type GetRuntimeAuthForModelFn = (params: {\n model: { provider: string; id: string; api?: string; baseUrl?: string };\n cfg?: unknown;\n workspaceDir?: string;\n}) => Promise<{\n apiKey?: string;\n baseUrl?: string;\n source?: string;\n mode?: string;\n profileId?: string;\n} | null>;\n\nlet _resolveApiKeyForProvider: ResolveApiKeyFn | null = null;\nlet _getRuntimeAuthForModel: GetRuntimeAuthForModelFn | null = null;\nlet _resolverLoaded = false;\nlet _resolverNextRetryAt = 0;\nconst RESOLVER_RETRY_BACKOFF_MS = 60_000; // 1 minute between retries after first failure\nconst resolvedCache = new Map<string, string | undefined>();\n\n/**\n * Lazily load the gateway's resolveApiKeyForProvider function.\n * Returns null if not available (e.g., running outside the gateway process).\n */\nasync function getGatewayResolver(): Promise<ResolveApiKeyFn | null> {\n if (_resolverLoaded) {\n return _resolveApiKeyForProvider;\n }\n // Backoff: don't re-scan filesystem on every call when module wasn't found.\n // After a failure, wait RESOLVER_RETRY_BACKOFF_MS before trying again.\n if (_resolverNextRetryAt > 0 && Date.now() < _resolverNextRetryAt) {\n return null;\n }\n\n try {\n // The gateway bundles this in a runtime chunk — import it dynamically.\n // This import path is stable across gateway versions since it's a named runtime export.\n const candidates = [\n // Try glob-matching the runtime module name (hash varies per build)\n ...await findRuntimeModules(),\n ];\n\n const { pathToFileURL } = await import(\"node:url\");\n for (const candidate of candidates) {\n try {\n // Convert native path to file:// URL for cross-platform ESM import compatibility\n const importUrl = pathToFileURL(candidate).href;\n const mod = await import(importUrl);\n if (typeof mod.resolveApiKeyForProvider === \"function\") {\n _resolveApiKeyForProvider = mod.resolveApiKeyForProvider;\n if (typeof mod.getRuntimeAuthForModel === \"function\") {\n _getRuntimeAuthForModel = mod.getRuntimeAuthForModel;\n log.debug(\"loaded gateway getRuntimeAuthForModel from runtime module\");\n }\n _resolverLoaded = true;\n log.debug(\"loaded gateway resolveApiKeyForProvider from runtime module\");\n return _resolveApiKeyForProvider;\n }\n } catch {\n // Try next candidate\n }\n }\n } catch {\n // Silent\n }\n\n // Backoff before retrying — avoid repeated fs scanning.\n // Retries after RESOLVER_RETRY_BACKOFF_MS so the resolver can\n // recover if the gateway restarts or the module becomes available.\n _resolverNextRetryAt = Date.now() + RESOLVER_RETRY_BACKOFF_MS;\n log.debug(`gateway resolveApiKeyForProvider not available — will retry after ${RESOLVER_RETRY_BACKOFF_MS / 1000}s`);\n return null;\n}\n\n/**\n * Find the gateway's model-auth runtime module by scanning the dist directory.\n * Uses require.resolve to find the openclaw package regardless of install method.\n */\nasync function findRuntimeModules(): Promise<string[]> {\n const { readdirSync } = await import(\"node:fs\");\n const { createRequire } = await import(\"node:module\");\n const { execFileSync } = await import(\"node:child_process\");\n const candidates: string[] = [];\n\n // Discover the openclaw dist directory from the installed package,\n // regardless of how it was installed (Homebrew, npm global, local, etc.)\n const distDirs: string[] = [];\n const pushDistDirs = (entryPath: string): void => {\n const resolvedEntryDir = path.dirname(entryPath);\n const packageRoot = path.basename(resolvedEntryDir) === \"dist\"\n ? path.resolve(resolvedEntryDir, \"..\")\n : resolvedEntryDir;\n const candidateDistDirs = [\n path.join(packageRoot, \"dist\"),\n path.join(packageRoot, \"..\", \"dist\"),\n ];\n for (const candidate of candidateDistDirs) {\n const resolved = path.resolve(candidate);\n if (!distDirs.includes(resolved)) distDirs.push(resolved);\n }\n };\n\n try {\n // require.resolve finds the package from the current process context\n const req = createRequire(import.meta.url);\n const openclawMain = req.resolve(\"openclaw\");\n pushDistDirs(openclawMain);\n } catch {\n // openclaw not resolvable from plugin context — try alternate paths\n }\n\n // Fallback: infer from the running process (gateway runs from its own dist/)\n // Use fs.realpathSync to resolve symlinks (e.g., /usr/local/bin/openclaw → actual path)\n try {\n const { realpathSync } = await import(\"node:fs\");\n const mainScript = process.argv[1];\n if (mainScript) {\n const realScript = realpathSync(mainScript);\n if (realScript.includes(\"openclaw\")) {\n pushDistDirs(realScript);\n }\n }\n } catch {\n // Silent\n }\n\n // Fallback: inspect the installed openclaw binary on PATH (Homebrew/global npm).\n try {\n const openclawBin = execFileSync(\"which\", [\"openclaw\"], {\n encoding: \"utf8\",\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n }).trim();\n if (openclawBin) {\n const { realpathSync } = await import(\"node:fs\");\n pushDistDirs(realpathSync(openclawBin));\n }\n } catch {\n // Silent\n }\n\n for (const dir of distDirs) {\n try {\n const files = readdirSync(dir);\n for (const f of files) {\n if (f.startsWith(\"runtime-model-auth.runtime-\") && f.endsWith(\".js\")) {\n candidates.push(path.join(dir, f));\n }\n }\n } catch {\n // Directory doesn't exist — skip\n }\n }\n\n return candidates;\n}\n\n/**\n * Resolve a provider API key from various OpenClaw formats.\n *\n * Resolution order:\n * 1. Plain-text string → returned immediately\n * 2. Gateway's resolveApiKeyForProvider → handles all secret ref formats\n * 3. Environment variable fallback (PROVIDER_NAME_API_KEY)\n * 4. undefined → provider is skipped in the fallback chain\n */\nexport async function resolveProviderApiKey(\n providerId: string,\n apiKeyValue: unknown,\n gatewayConfig?: unknown,\n agentDir?: string,\n): Promise<string | undefined> {\n const resolvedAgentDir = path.resolve(\n agentDir ?? path.join(os.homedir(), \".openclaw\", \"agents\", \"main\", \"agent\"),\n );\n\n // Check cache first\n const cacheKey = `provider:${providerId}:agentDir:${resolvedAgentDir}`;\n if (resolvedCache.has(cacheKey)) {\n return resolvedCache.get(cacheKey);\n }\n\n let resolved: string | undefined;\n\n // Fast path: plain-text string that looks like an actual API key\n if (typeof apiKeyValue === \"string\" && apiKeyValue.trim().length > 0) {\n // Skip known non-API-key markers used by the gateway for auth modes\n // that don't use bearer tokens (OAuth, local endpoints, GCP credentials)\n if (\n apiKeyValue === \"secretref-managed\" ||\n apiKeyValue.endsWith(\"-oauth\") ||\n apiKeyValue.endsWith(\"-local\") ||\n apiKeyValue === \"lm-studio\" ||\n apiKeyValue.startsWith(\"gcp-\")\n ) {\n // Fall through to gateway resolver / env var fallback\n } else {\n resolved = apiKeyValue;\n resolvedCache.set(cacheKey, resolved);\n return resolved;\n }\n }\n\n // The API key is either a SecretRef object, \"secretref-managed\", or empty.\n // Try the gateway's own auth resolution system first.\n const resolver = await getGatewayResolver();\n if (resolver) {\n try {\n const auth = await resolver({ provider: providerId, cfg: gatewayConfig, agentDir: resolvedAgentDir });\n if (auth?.apiKey) {\n resolved = auth.apiKey;\n log.debug(`resolved API key for provider \"${providerId}\" via gateway auth (source: ${auth.source ?? \"unknown\"}, mode: ${auth.mode ?? \"unknown\"})`);\n resolvedCache.set(cacheKey, resolved);\n return resolved;\n }\n } catch (err) {\n log.debug(\n `gateway auth resolution failed for provider \"${providerId}\": ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n\n // Environment variable fallback\n resolved = resolveFromEnv(providerId);\n if (resolved) {\n log.debug(`resolved API key for provider \"${providerId}\" from environment variable`);\n } else {\n log.debug(`could not resolve API key for provider \"${providerId}\" — skipping`);\n }\n\n // Only cache successful resolutions — failures are retried on next call\n // so providers can recover after transient issues (e.g., 1Password agent restart)\n if (resolved) {\n resolvedCache.set(cacheKey, resolved);\n }\n return resolved;\n}\n\n/**\n * Try to resolve an API key from environment variables.\n */\nfunction resolveFromEnv(providerId: string): string | undefined {\n const normalized = providerId.toUpperCase().replace(/[^A-Z0-9]/g, \"_\");\n const candidates = [\n `${normalized}_API_KEY`,\n `${normalized}_TOKEN`,\n ];\n for (const envVar of candidates) {\n const value = readEnvVar(envVar);\n if (value && value.trim().length > 0) {\n return value.trim();\n }\n }\n return undefined;\n}\n\n/**\n * Get the gateway's getRuntimeAuthForModel function, if available.\n * This resolves request-ready auth including provider-owned transforms\n * (OAuth token exchange, base URL override for codex/copilot/etc.).\n * Must be called after at least one resolveProviderApiKey() call to\n * trigger the lazy module load.\n */\nexport async function getGatewayRuntimeAuthForModel(): Promise<GetRuntimeAuthForModelFn | null> {\n // Ensure the runtime module has been loaded\n await getGatewayResolver();\n return _getRuntimeAuthForModel;\n}\n\n/**\n * Clear the resolution cache (useful for testing or key rotation).\n */\nexport function clearSecretCache(): void {\n resolvedCache.clear();\n _resolveApiKeyForProvider = null;\n _getRuntimeAuthForModel = null;\n _resolverLoaded = false;\n _resolverNextRetryAt = 0;\n}\n\nexport function __setGatewayResolverForTest(resolver: ResolveApiKeyFn | null): void {\n _resolveApiKeyForProvider = resolver;\n _resolverLoaded = resolver !== null;\n _resolverNextRetryAt = 0;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import {
|
|
2
|
+
countRecallTokenOverlap,
|
|
3
|
+
normalizeRecallTokens
|
|
4
|
+
} from "./chunk-DT5TVLJE.js";
|
|
5
|
+
|
|
6
|
+
// src/direct-answer.ts
|
|
7
|
+
var FILTER_LABELS = {
|
|
8
|
+
nonActiveStatus: "non-active-status",
|
|
9
|
+
notTrustedZone: "not-trusted-zone",
|
|
10
|
+
ineligibleTaxonomyBucket: "ineligible-taxonomy-bucket",
|
|
11
|
+
belowImportanceFloor: "below-importance-floor",
|
|
12
|
+
entityRefMismatch: "entity-ref-mismatch",
|
|
13
|
+
belowTokenOverlapFloor: "below-token-overlap-floor"
|
|
14
|
+
};
|
|
15
|
+
function isDirectAnswerEligible(input) {
|
|
16
|
+
const { query, candidates, config, queryEntityRefs } = input;
|
|
17
|
+
if (!config.enabled) {
|
|
18
|
+
return {
|
|
19
|
+
eligible: false,
|
|
20
|
+
reason: "disabled",
|
|
21
|
+
narrative: "direct-answer tier is disabled",
|
|
22
|
+
filteredBy: []
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
const queryTokens = new Set(normalizeRecallTokens(query));
|
|
26
|
+
if (queryTokens.size === 0) {
|
|
27
|
+
return {
|
|
28
|
+
eligible: false,
|
|
29
|
+
reason: "empty-query",
|
|
30
|
+
narrative: "query has no searchable tokens after normalization",
|
|
31
|
+
filteredBy: []
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
if (candidates.length === 0) {
|
|
35
|
+
return {
|
|
36
|
+
eligible: false,
|
|
37
|
+
reason: "no-candidates",
|
|
38
|
+
narrative: "no candidates supplied",
|
|
39
|
+
filteredBy: []
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
const filteredBy = [];
|
|
43
|
+
let working = candidates;
|
|
44
|
+
working = applyFilter(working, filteredBy, FILTER_LABELS.nonActiveStatus, (c) => {
|
|
45
|
+
const status = c.memory.frontmatter.status ?? "active";
|
|
46
|
+
return status === "active";
|
|
47
|
+
});
|
|
48
|
+
working = applyFilter(
|
|
49
|
+
working,
|
|
50
|
+
filteredBy,
|
|
51
|
+
FILTER_LABELS.notTrustedZone,
|
|
52
|
+
(c) => c.trustZone === "trusted"
|
|
53
|
+
);
|
|
54
|
+
working = applyFilter(
|
|
55
|
+
working,
|
|
56
|
+
filteredBy,
|
|
57
|
+
FILTER_LABELS.ineligibleTaxonomyBucket,
|
|
58
|
+
(c) => c.taxonomyBucket !== null && config.eligibleTaxonomyBuckets.includes(c.taxonomyBucket)
|
|
59
|
+
);
|
|
60
|
+
working = applyFilter(working, filteredBy, FILTER_LABELS.belowImportanceFloor, (c) => {
|
|
61
|
+
if (c.memory.frontmatter.verificationState === "user_confirmed") return true;
|
|
62
|
+
return c.importanceScore >= config.importanceFloor;
|
|
63
|
+
});
|
|
64
|
+
if (queryEntityRefs && queryEntityRefs.length > 0) {
|
|
65
|
+
const normRefs = new Set(queryEntityRefs.map((r) => r.toLowerCase()));
|
|
66
|
+
working = applyFilter(working, filteredBy, FILTER_LABELS.entityRefMismatch, (c) => {
|
|
67
|
+
const ref = c.memory.frontmatter.entityRef;
|
|
68
|
+
if (!ref) return true;
|
|
69
|
+
return normRefs.has(ref.toLowerCase());
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
if (working.length === 0) {
|
|
73
|
+
return {
|
|
74
|
+
eligible: false,
|
|
75
|
+
reason: "no-eligible-candidates",
|
|
76
|
+
narrative: "no candidates survived eligibility filters",
|
|
77
|
+
filteredBy
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
const scored = working.map((candidate) => {
|
|
81
|
+
const searchable = `${candidate.memory.frontmatter.tags?.join(" ") ?? ""} ${candidate.memory.content}`.trim();
|
|
82
|
+
const matches = countRecallTokenOverlap(queryTokens, searchable);
|
|
83
|
+
return { candidate, tokenOverlap: matches / queryTokens.size };
|
|
84
|
+
});
|
|
85
|
+
const overlapSurvivors = scored.filter((s) => s.tokenOverlap >= config.tokenOverlapFloor);
|
|
86
|
+
if (overlapSurvivors.length < scored.length) {
|
|
87
|
+
filteredBy.push(FILTER_LABELS.belowTokenOverlapFloor);
|
|
88
|
+
}
|
|
89
|
+
if (overlapSurvivors.length === 0) {
|
|
90
|
+
return {
|
|
91
|
+
eligible: false,
|
|
92
|
+
reason: "below-token-overlap-floor",
|
|
93
|
+
narrative: `no candidate met token-overlap floor ${config.tokenOverlapFloor}`,
|
|
94
|
+
filteredBy
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
overlapSurvivors.sort(compareScored);
|
|
98
|
+
if (overlapSurvivors.length >= 2) {
|
|
99
|
+
const topScore = scoreFor(overlapSurvivors[0]);
|
|
100
|
+
const secondScore = scoreFor(overlapSurvivors[1]);
|
|
101
|
+
if (topScore - secondScore < config.ambiguityMargin) {
|
|
102
|
+
return {
|
|
103
|
+
eligible: false,
|
|
104
|
+
reason: "ambiguous",
|
|
105
|
+
narrative: `top two candidates within ambiguityMargin ${config.ambiguityMargin}`,
|
|
106
|
+
filteredBy
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
const winner = overlapSurvivors[0];
|
|
111
|
+
const bucket = winner.candidate.taxonomyBucket ?? "unknown";
|
|
112
|
+
return {
|
|
113
|
+
eligible: true,
|
|
114
|
+
reason: "eligible",
|
|
115
|
+
winner: winner.candidate,
|
|
116
|
+
tokenOverlap: winner.tokenOverlap,
|
|
117
|
+
narrative: `trusted ${bucket}, unambiguous, token-overlap ${winner.tokenOverlap.toFixed(2)}`,
|
|
118
|
+
filteredBy
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
function applyFilter(working, filteredBy, label, keep) {
|
|
122
|
+
const before = working.length;
|
|
123
|
+
const next = working.filter(keep);
|
|
124
|
+
if (next.length < before) filteredBy.push(label);
|
|
125
|
+
return next;
|
|
126
|
+
}
|
|
127
|
+
function scoreFor(s) {
|
|
128
|
+
return s.candidate.matchScore ?? s.tokenOverlap;
|
|
129
|
+
}
|
|
130
|
+
function compareScored(a, b) {
|
|
131
|
+
const diff = scoreFor(b) - scoreFor(a);
|
|
132
|
+
if (diff !== 0) return diff;
|
|
133
|
+
return a.candidate.memory.path.localeCompare(b.candidate.memory.path);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export {
|
|
137
|
+
FILTER_LABELS,
|
|
138
|
+
isDirectAnswerEligible
|
|
139
|
+
};
|
|
140
|
+
//# sourceMappingURL=chunk-Y4FHOFJ2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/direct-answer.ts"],"sourcesContent":["/**\n * Direct-answer retrieval tier eligibility (issue #518 slice 2).\n *\n * This module is a pure decision layer. It takes a query, a set of\n * caller-resolved candidates (each already decorated with trust-zone,\n * taxonomy-bucket, and importance information), and the direct-answer\n * config, then returns an eligibility verdict.\n *\n * Keeping the module pure means:\n *\n * - Tests do not need a trust-zones store, taxonomy resolver, or importance\n * scorer on disk.\n * - Slice 3 (retrieval.ts wiring) is responsible for resolving those signals\n * before calling in; the wiring layer decides where candidates come from\n * (entity index, token prefilter, etc.). This module only decides\n * whether the surfaced candidates add up to a confident direct answer.\n *\n * Not wired into retrieval yet — see slice 3.\n */\n\nimport type { MemoryFile, MemoryStatus } from \"./types.js\";\nimport type { TrustZoneName } from \"./trust-zones.js\";\nimport {\n countRecallTokenOverlap,\n normalizeRecallTokens,\n} from \"./recall-tokenization.js\";\n\n/**\n * Caller-supplied candidate.\n *\n * `trustZone`, `taxonomyBucket`, and `importanceScore` are resolved outside\n * this module because each comes from a different subsystem. Passing them\n * as inputs keeps the function deterministic and easy to unit-test.\n *\n * `matchScore` is optional; if omitted, candidates are ranked by\n * token-overlap ratio. Callers that already computed a better ranking\n * score (e.g. BM25, vector similarity) can supply it to drive the\n * ambiguity check.\n */\nexport interface DirectAnswerCandidate {\n memory: MemoryFile;\n trustZone: TrustZoneName | null;\n taxonomyBucket: string | null;\n importanceScore: number;\n matchScore?: number;\n}\n\nexport interface DirectAnswerConfig {\n enabled: boolean;\n tokenOverlapFloor: number;\n importanceFloor: number;\n ambiguityMargin: number;\n eligibleTaxonomyBuckets: string[];\n}\n\nexport interface DirectAnswerInput {\n query: string;\n candidates: DirectAnswerCandidate[];\n config: DirectAnswerConfig;\n /**\n * Optional entity-ref hints resolved from the query upstream. When\n * supplied, a candidate with a set `entityRef` must match one of these\n * (case-insensitive) to remain eligible. Candidates without an\n * `entityRef` are allowed through regardless.\n */\n queryEntityRefs?: string[];\n}\n\nexport type DirectAnswerReason =\n | \"disabled\"\n | \"empty-query\"\n | \"no-candidates\"\n | \"no-eligible-candidates\"\n | \"below-token-overlap-floor\"\n | \"ambiguous\"\n | \"eligible\";\n\nexport interface DirectAnswerResult {\n eligible: boolean;\n reason: DirectAnswerReason;\n /** Winning candidate when eligible. */\n winner?: DirectAnswerCandidate;\n /** Computed token-overlap ratio (0..1) of the winner. */\n tokenOverlap?: number;\n /**\n * Human-readable summary suitable for\n * `RecallTierExplain.tierReason`.\n */\n narrative: string;\n /**\n * Filter labels that eliminated at least one candidate along the way.\n * Populated regardless of eligibility so the caller can surface the\n * narrowing steps in `RecallTierExplain.filteredBy`.\n */\n filteredBy: string[];\n}\n\n/** Filter labels — exported so callers and tests can match them structurally. */\nexport const FILTER_LABELS = {\n nonActiveStatus: \"non-active-status\",\n notTrustedZone: \"not-trusted-zone\",\n ineligibleTaxonomyBucket: \"ineligible-taxonomy-bucket\",\n belowImportanceFloor: \"below-importance-floor\",\n entityRefMismatch: \"entity-ref-mismatch\",\n belowTokenOverlapFloor: \"below-token-overlap-floor\",\n} as const;\n\ninterface ScoredCandidate {\n candidate: DirectAnswerCandidate;\n tokenOverlap: number;\n}\n\n/**\n * Determine whether a query can be served by the direct-answer tier.\n *\n * Decision ladder, in order:\n *\n * 1. config.enabled === false → \"disabled\"\n * 2. empty query tokens → \"empty-query\"\n * 3. empty candidates → \"no-candidates\"\n * 4. hard filters drop all candidates → \"no-eligible-candidates\"\n * 5. token-overlap floor drops all → \"below-token-overlap-floor\"\n * 6. top two candidates within ambiguityMargin → \"ambiguous\"\n * 7. otherwise → \"eligible\"\n */\nexport function isDirectAnswerEligible(\n input: DirectAnswerInput,\n): DirectAnswerResult {\n const { query, candidates, config, queryEntityRefs } = input;\n\n if (!config.enabled) {\n return {\n eligible: false,\n reason: \"disabled\",\n narrative: \"direct-answer tier is disabled\",\n filteredBy: [],\n };\n }\n\n const queryTokens = new Set(normalizeRecallTokens(query));\n if (queryTokens.size === 0) {\n return {\n eligible: false,\n reason: \"empty-query\",\n narrative: \"query has no searchable tokens after normalization\",\n filteredBy: [],\n };\n }\n\n if (candidates.length === 0) {\n return {\n eligible: false,\n reason: \"no-candidates\",\n narrative: \"no candidates supplied\",\n filteredBy: [],\n };\n }\n\n const filteredBy: string[] = [];\n let working: DirectAnswerCandidate[] = candidates;\n\n working = applyFilter(working, filteredBy, FILTER_LABELS.nonActiveStatus, (c) => {\n const status: MemoryStatus = c.memory.frontmatter.status ?? \"active\";\n return status === \"active\";\n });\n\n working = applyFilter(working, filteredBy, FILTER_LABELS.notTrustedZone, (c) =>\n c.trustZone === \"trusted\",\n );\n\n working = applyFilter(\n working,\n filteredBy,\n FILTER_LABELS.ineligibleTaxonomyBucket,\n (c) =>\n c.taxonomyBucket !== null &&\n config.eligibleTaxonomyBuckets.includes(c.taxonomyBucket),\n );\n\n working = applyFilter(working, filteredBy, FILTER_LABELS.belowImportanceFloor, (c) => {\n if (c.memory.frontmatter.verificationState === \"user_confirmed\") return true;\n return c.importanceScore >= config.importanceFloor;\n });\n\n if (queryEntityRefs && queryEntityRefs.length > 0) {\n const normRefs = new Set(queryEntityRefs.map((r) => r.toLowerCase()));\n working = applyFilter(working, filteredBy, FILTER_LABELS.entityRefMismatch, (c) => {\n const ref = c.memory.frontmatter.entityRef;\n if (!ref) return true;\n return normRefs.has(ref.toLowerCase());\n });\n }\n\n if (working.length === 0) {\n return {\n eligible: false,\n reason: \"no-eligible-candidates\",\n narrative: \"no candidates survived eligibility filters\",\n filteredBy,\n };\n }\n\n const scored: ScoredCandidate[] = working.map((candidate) => {\n const searchable =\n `${candidate.memory.frontmatter.tags?.join(\" \") ?? \"\"} ${candidate.memory.content}`.trim();\n const matches = countRecallTokenOverlap(queryTokens, searchable);\n return { candidate, tokenOverlap: matches / queryTokens.size };\n });\n\n const overlapSurvivors = scored.filter((s) => s.tokenOverlap >= config.tokenOverlapFloor);\n if (overlapSurvivors.length < scored.length) {\n filteredBy.push(FILTER_LABELS.belowTokenOverlapFloor);\n }\n\n if (overlapSurvivors.length === 0) {\n return {\n eligible: false,\n reason: \"below-token-overlap-floor\",\n narrative: `no candidate met token-overlap floor ${config.tokenOverlapFloor}`,\n filteredBy,\n };\n }\n\n overlapSurvivors.sort(compareScored);\n\n if (overlapSurvivors.length >= 2) {\n const topScore = scoreFor(overlapSurvivors[0]);\n const secondScore = scoreFor(overlapSurvivors[1]);\n if (topScore - secondScore < config.ambiguityMargin) {\n return {\n eligible: false,\n reason: \"ambiguous\",\n narrative: `top two candidates within ambiguityMargin ${config.ambiguityMargin}`,\n filteredBy,\n };\n }\n }\n\n const winner = overlapSurvivors[0];\n const bucket = winner.candidate.taxonomyBucket ?? \"unknown\";\n return {\n eligible: true,\n reason: \"eligible\",\n winner: winner.candidate,\n tokenOverlap: winner.tokenOverlap,\n narrative: `trusted ${bucket}, unambiguous, token-overlap ${winner.tokenOverlap.toFixed(2)}`,\n filteredBy,\n };\n}\n\nfunction applyFilter(\n working: DirectAnswerCandidate[],\n filteredBy: string[],\n label: string,\n keep: (c: DirectAnswerCandidate) => boolean,\n): DirectAnswerCandidate[] {\n const before = working.length;\n const next = working.filter(keep);\n if (next.length < before) filteredBy.push(label);\n return next;\n}\n\nfunction scoreFor(s: ScoredCandidate): number {\n return s.candidate.matchScore ?? s.tokenOverlap;\n}\n\nfunction compareScored(a: ScoredCandidate, b: ScoredCandidate): number {\n const diff = scoreFor(b) - scoreFor(a);\n if (diff !== 0) return diff;\n // Stable secondary key on path so the comparator returns 0 only for equal\n // entries (CLAUDE.md rule 19).\n return a.candidate.memory.path.localeCompare(b.candidate.memory.path);\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
// src/contradiction/contradiction-review.ts
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import { createHash } from "crypto";
|
|
5
|
+
function computePairId(memoryIdA, memoryIdB) {
|
|
6
|
+
const sorted = [memoryIdA, memoryIdB].sort();
|
|
7
|
+
return createHash("sha256").update(sorted.join("::")).digest("hex").slice(0, 24);
|
|
8
|
+
}
|
|
9
|
+
function reviewDir(memoryDir) {
|
|
10
|
+
return path.join(memoryDir, ".review", "contradictions");
|
|
11
|
+
}
|
|
12
|
+
function pairPath(memoryDir, pairId) {
|
|
13
|
+
if (pairId.includes("/") || pairId.includes("\\") || pairId.includes("..")) {
|
|
14
|
+
throw new Error(`Invalid pairId: ${pairId}`);
|
|
15
|
+
}
|
|
16
|
+
return path.join(reviewDir(memoryDir), `${pairId}.json`);
|
|
17
|
+
}
|
|
18
|
+
function ensureDir(memoryDir) {
|
|
19
|
+
const dir = reviewDir(memoryDir);
|
|
20
|
+
if (!fs.existsSync(dir)) {
|
|
21
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function writePair(memoryDir, pair) {
|
|
25
|
+
ensureDir(memoryDir);
|
|
26
|
+
const pairId = computePairId(pair.memoryIds[0], pair.memoryIds[1]);
|
|
27
|
+
const existing = readPair(memoryDir, pairId);
|
|
28
|
+
if (existing?.resolution) {
|
|
29
|
+
return existing;
|
|
30
|
+
}
|
|
31
|
+
if (existing && existing.confidence >= pair.confidence) {
|
|
32
|
+
return existing;
|
|
33
|
+
}
|
|
34
|
+
const full = {
|
|
35
|
+
...pair,
|
|
36
|
+
pairId,
|
|
37
|
+
lastReviewedAt: existing?.lastReviewedAt,
|
|
38
|
+
resolution: existing?.resolution
|
|
39
|
+
};
|
|
40
|
+
const filePath = pairPath(memoryDir, pairId);
|
|
41
|
+
const tmpPath = `${filePath}.tmp`;
|
|
42
|
+
fs.writeFileSync(tmpPath, JSON.stringify(full, null, 2), "utf-8");
|
|
43
|
+
fs.renameSync(tmpPath, filePath);
|
|
44
|
+
return full;
|
|
45
|
+
}
|
|
46
|
+
function writePairs(memoryDir, pairs) {
|
|
47
|
+
const seen = /* @__PURE__ */ new Set();
|
|
48
|
+
const results = [];
|
|
49
|
+
for (const pair of pairs) {
|
|
50
|
+
const key = computePairId(pair.memoryIds[0], pair.memoryIds[1]);
|
|
51
|
+
if (seen.has(key)) continue;
|
|
52
|
+
seen.add(key);
|
|
53
|
+
results.push(writePair(memoryDir, pair));
|
|
54
|
+
}
|
|
55
|
+
return results;
|
|
56
|
+
}
|
|
57
|
+
function readPair(memoryDir, pairId) {
|
|
58
|
+
const filePath = pairPath(memoryDir, pairId);
|
|
59
|
+
try {
|
|
60
|
+
const raw = fs.readFileSync(filePath, "utf-8");
|
|
61
|
+
const parsed = JSON.parse(raw);
|
|
62
|
+
if (typeof parsed === "object" && parsed !== null && Array.isArray(parsed.memoryIds)) {
|
|
63
|
+
return parsed;
|
|
64
|
+
}
|
|
65
|
+
return null;
|
|
66
|
+
} catch {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
function listPairs(memoryDir, options) {
|
|
71
|
+
const startTime = Date.now();
|
|
72
|
+
const dir = reviewDir(memoryDir);
|
|
73
|
+
const { filter = "all", namespace, limit = 50 } = options ?? {};
|
|
74
|
+
const pairs = [];
|
|
75
|
+
let total = 0;
|
|
76
|
+
if (!fs.existsSync(dir)) {
|
|
77
|
+
return { pairs: [], total: 0, durationMs: Date.now() - startTime };
|
|
78
|
+
}
|
|
79
|
+
for (const entry of fs.readdirSync(dir)) {
|
|
80
|
+
if (!entry.endsWith(".json")) continue;
|
|
81
|
+
try {
|
|
82
|
+
const raw = fs.readFileSync(path.join(dir, entry), "utf-8");
|
|
83
|
+
const pair = JSON.parse(raw);
|
|
84
|
+
if (typeof pair !== "object" || pair === null) continue;
|
|
85
|
+
if (!Array.isArray(pair.memoryIds)) continue;
|
|
86
|
+
if (namespace && pair.namespace !== namespace) continue;
|
|
87
|
+
if (filter === "unresolved") {
|
|
88
|
+
if (pair.resolution) continue;
|
|
89
|
+
if (pair.verdict === "independent") continue;
|
|
90
|
+
} else if (filter !== "all" && pair.verdict !== filter) {
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
total++;
|
|
94
|
+
if (pairs.length < limit) pairs.push(pair);
|
|
95
|
+
} catch {
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return { pairs, total, durationMs: Date.now() - startTime };
|
|
100
|
+
}
|
|
101
|
+
function isCoolingDown(pair, cooldownDays) {
|
|
102
|
+
if (cooldownDays <= 0) return false;
|
|
103
|
+
if (!pair.lastReviewedAt) return false;
|
|
104
|
+
const lastReviewed = new Date(pair.lastReviewedAt).getTime();
|
|
105
|
+
if (!Number.isFinite(lastReviewed)) return false;
|
|
106
|
+
const cooldownMs = cooldownDays * 24 * 60 * 60 * 1e3;
|
|
107
|
+
return Date.now() < lastReviewed + cooldownMs;
|
|
108
|
+
}
|
|
109
|
+
function resolvePair(memoryDir, pairId, verb) {
|
|
110
|
+
const existing = readPair(memoryDir, pairId);
|
|
111
|
+
if (!existing) return null;
|
|
112
|
+
const updated = {
|
|
113
|
+
...existing,
|
|
114
|
+
lastReviewedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
115
|
+
resolution: verb
|
|
116
|
+
};
|
|
117
|
+
const filePath = pairPath(memoryDir, pairId);
|
|
118
|
+
const tmpPath = `${filePath}.tmp`;
|
|
119
|
+
fs.writeFileSync(tmpPath, JSON.stringify(updated, null, 2), "utf-8");
|
|
120
|
+
fs.renameSync(tmpPath, filePath);
|
|
121
|
+
return updated;
|
|
122
|
+
}
|
|
123
|
+
function memoryHashesChanged(_memoryDir, _pair, _getCurrentHash) {
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export {
|
|
128
|
+
computePairId,
|
|
129
|
+
writePair,
|
|
130
|
+
writePairs,
|
|
131
|
+
readPair,
|
|
132
|
+
listPairs,
|
|
133
|
+
isCoolingDown,
|
|
134
|
+
resolvePair,
|
|
135
|
+
memoryHashesChanged
|
|
136
|
+
};
|
|
137
|
+
//# sourceMappingURL=chunk-YNB73F22.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/contradiction/contradiction-review.ts"],"sourcesContent":["/**\n * Contradiction Review Queue — storage for detected contradiction pairs (issue #520).\n *\n * Stores candidate pairs as JSON files under `memoryDir/.review/contradictions/`.\n * Pair IDs are deterministic (sha256 of sorted memory IDs) so reruns are idempotent.\n *\n * Lifecycle:\n * - `contradicts` → awaiting user review\n * - `duplicates` → auto-flagged for dedup (still needs user approval)\n * - `independent` / `both-valid` → dormant with cooldown\n */\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { createHash } from \"node:crypto\";\nimport { log } from \"../logger.js\";\nimport type { ContradictionVerdict } from \"./contradiction-judge.js\";\n\n// ── Types ──────────────────────────────────────────────────────────────────────\n\nexport type ResolutionVerb = \"keep-a\" | \"keep-b\" | \"merge\" | \"both-valid\" | \"needs-more-context\";\n\nexport interface ContradictionPair {\n /** Deterministic pair ID: sha256(sorted(memoryIdA, memoryIdB)). */\n pairId: string;\n /** Memory IDs (sorted). */\n memoryIds: [string, string];\n /** Judge verdict. */\n verdict: ContradictionVerdict;\n /** Judge rationale. */\n rationale: string;\n /** Judge confidence in [0, 1]. */\n confidence: number;\n /** ISO timestamp when detected. */\n detectedAt: string;\n /** ISO timestamp when last reviewed by user. */\n lastReviewedAt?: string;\n /** Resolution verb applied by user. */\n resolution?: ResolutionVerb;\n /** Namespace scope. */\n namespace?: string;\n}\n\nexport interface ContradictionListResult {\n pairs: ContradictionPair[];\n total: number;\n durationMs: number;\n}\n\nexport type ContradictionFilter = ContradictionVerdict | \"all\" | \"unresolved\";\n\n// ── Helpers ────────────────────────────────────────────────────────────────────\n\nexport function computePairId(memoryIdA: string, memoryIdB: string): string {\n const sorted = [memoryIdA, memoryIdB].sort();\n return createHash(\"sha256\").update(sorted.join(\"::\")).digest(\"hex\").slice(0, 24);\n}\n\nfunction reviewDir(memoryDir: string): string {\n return path.join(memoryDir, \".review\", \"contradictions\");\n}\n\nfunction pairPath(memoryDir: string, pairId: string): string {\n if (pairId.includes(\"/\") || pairId.includes(\"\\\\\") || pairId.includes(\"..\")) {\n throw new Error(`Invalid pairId: ${pairId}`);\n }\n return path.join(reviewDir(memoryDir), `${pairId}.json`);\n}\n\nfunction ensureDir(memoryDir: string): void {\n const dir = reviewDir(memoryDir);\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n}\n\n// ── Write ──────────────────────────────────────────────────────────────────────\n\n/**\n * Write a contradiction pair to the review queue.\n * Idempotent: if the pair already exists with a higher or equal confidence,\n * the existing entry is preserved.\n */\nexport function writePair(memoryDir: string, pair: Omit<ContradictionPair, \"pairId\"> & { memoryIds: [string, string] }): ContradictionPair {\n ensureDir(memoryDir);\n const pairId = computePairId(pair.memoryIds[0], pair.memoryIds[1]);\n const existing = readPair(memoryDir, pairId);\n\n // Preserve user resolution if already reviewed\n if (existing?.resolution) {\n return existing;\n }\n\n // Preserve cooldown: don't overwrite a cooled-down entry with lower confidence\n if (existing && existing.confidence >= pair.confidence) {\n return existing;\n }\n\n const full: ContradictionPair = {\n ...pair,\n pairId,\n lastReviewedAt: existing?.lastReviewedAt,\n resolution: existing?.resolution,\n };\n\n const filePath = pairPath(memoryDir, pairId);\n const tmpPath = `${filePath}.tmp`;\n\n // Atomic write: temp then rename (rule 54)\n fs.writeFileSync(tmpPath, JSON.stringify(full, null, 2), \"utf-8\");\n fs.renameSync(tmpPath, filePath);\n\n return full;\n}\n\n/**\n * Write multiple pairs, deduplicating inputs first (rule 49).\n */\nexport function writePairs(memoryDir: string, pairs: Array<Omit<ContradictionPair, \"pairId\"> & { memoryIds: [string, string] }>): ContradictionPair[] {\n const seen = new Set<string>();\n const results: ContradictionPair[] = [];\n\n for (const pair of pairs) {\n const key = computePairId(pair.memoryIds[0], pair.memoryIds[1]);\n if (seen.has(key)) continue;\n seen.add(key);\n results.push(writePair(memoryDir, pair));\n }\n\n return results;\n}\n\n// ── Read ───────────────────────────────────────────────────────────────────────\n\n/**\n * Read a single pair by ID. Returns null if not found.\n */\nexport function readPair(memoryDir: string, pairId: string): ContradictionPair | null {\n const filePath = pairPath(memoryDir, pairId);\n try {\n const raw = fs.readFileSync(filePath, \"utf-8\");\n const parsed = JSON.parse(raw);\n if (typeof parsed === \"object\" && parsed !== null && Array.isArray(parsed.memoryIds)) {\n return parsed as ContradictionPair;\n }\n return null;\n } catch {\n return null;\n }\n}\n\n/**\n * List pairs in the review queue, optionally filtered by verdict.\n */\nexport function listPairs(\n memoryDir: string,\n options?: {\n filter?: ContradictionFilter;\n namespace?: string;\n limit?: number;\n },\n): ContradictionListResult {\n const startTime = Date.now();\n const dir = reviewDir(memoryDir);\n const { filter = \"all\", namespace, limit = 50 } = options ?? {};\n const pairs: ContradictionPair[] = [];\n let total = 0;\n\n if (!fs.existsSync(dir)) {\n return { pairs: [], total: 0, durationMs: Date.now() - startTime };\n }\n\n for (const entry of fs.readdirSync(dir)) {\n if (!entry.endsWith(\".json\")) continue;\n\n try {\n const raw = fs.readFileSync(path.join(dir, entry), \"utf-8\");\n const pair = JSON.parse(raw) as ContradictionPair;\n\n if (typeof pair !== \"object\" || pair === null) continue;\n if (!Array.isArray(pair.memoryIds)) continue;\n\n // Namespace filter\n if (namespace && pair.namespace !== namespace) continue;\n\n // Verdict filter\n if (filter === \"unresolved\") {\n if (pair.resolution) continue;\n if (pair.verdict === \"independent\") continue;\n } else if (filter !== \"all\" && pair.verdict !== filter) {\n continue;\n }\n\n total++;\n if (pairs.length < limit) pairs.push(pair);\n } catch {\n continue;\n }\n }\n\n return { pairs, total, durationMs: Date.now() - startTime };\n}\n\n// ── Cooldown ───────────────────────────────────────────────────────────────────\n\n/**\n * Check if a pair is within its cooldown window.\n * Returns true if the pair should be SKIPPED (still cooling down).\n */\nexport function isCoolingDown(pair: ContradictionPair, cooldownDays: number): boolean {\n if (cooldownDays <= 0) return false; // rule 27: guard against 0\n if (!pair.lastReviewedAt) return false;\n\n const lastReviewed = new Date(pair.lastReviewedAt).getTime();\n if (!Number.isFinite(lastReviewed)) return false;\n\n const cooldownMs = cooldownDays * 24 * 60 * 60 * 1000;\n return Date.now() < lastReviewed + cooldownMs;\n}\n\n/**\n * Mark a pair as reviewed (sets lastReviewedAt and resolution).\n */\nexport function resolvePair(\n memoryDir: string,\n pairId: string,\n verb: ResolutionVerb,\n): ContradictionPair | null {\n const existing = readPair(memoryDir, pairId);\n if (!existing) return null;\n\n const updated: ContradictionPair = {\n ...existing,\n lastReviewedAt: new Date().toISOString(),\n resolution: verb,\n };\n\n const filePath = pairPath(memoryDir, pairId);\n const tmpPath = `${filePath}.tmp`;\n\n fs.writeFileSync(tmpPath, JSON.stringify(updated, null, 2), \"utf-8\");\n fs.renameSync(tmpPath, filePath);\n\n return updated;\n}\n\n/**\n * Check whether a pair's referenced memories have changed since detection,\n * which should override cooldown.\n */\nexport function memoryHashesChanged(\n _memoryDir: string,\n _pair: ContradictionPair,\n _getCurrentHash: (memoryId: string) => string | null,\n): boolean {\n // Intentionally a stub for now — the full implementation would compare\n // content hashes stored at detection time with current hashes.\n return false;\n}\n"],"mappings":"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","names":[]}
|