@remnic/core 9.50.3 → 9.51.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/access-admin-ops-surface.d.ts +13 -11
- package/dist/access-admin-ops-surface.js +41 -39
- package/dist/access-authorization-probe.d.ts +13 -11
- package/dist/access-authorization-probe.js +42 -40
- package/dist/access-boundary.d.ts +13 -11
- package/dist/access-boundary.js +41 -39
- package/dist/access-cli.js +109 -104
- package/dist/access-cli.js.map +1 -1
- package/dist/access-coding-context-resolution.d.ts +3 -1
- package/dist/access-extraction-force-flush.d.ts +13 -11
- package/dist/access-extraction-force-flush.js +41 -39
- package/dist/access-health-types.d.ts +3 -1
- package/dist/access-http-lcm-compaction.d.ts +13 -11
- package/dist/access-http-lifecycle-flush.d.ts +13 -11
- package/dist/access-http-offline-stream.d.ts +13 -11
- package/dist/access-http-query.js +41 -39
- package/dist/access-http.d.ts +13 -11
- package/dist/access-http.js +52 -50
- package/dist/access-identity-continuity-surface.d.ts +11 -9
- package/dist/access-identity-continuity-surface.js +41 -39
- package/dist/access-lcm-surface.d.ts +13 -11
- package/dist/access-lcm-surface.js +41 -39
- package/dist/access-mcp.d.ts +13 -11
- package/dist/access-mcp.js +47 -45
- package/dist/access-memory-search-fanout.d.ts +4 -2
- package/dist/{access-namespace-preflight-Dj2QolFE.d.ts → access-namespace-preflight-Ds6CGlCP.d.ts} +1 -1
- package/dist/access-namespace-preflight.d.ts +5 -3
- package/dist/access-namespace-preflight.js +41 -39
- package/dist/access-observe-write-surface.d.ts +13 -11
- package/dist/access-observe-write-surface.js +41 -39
- package/dist/access-offline-manifest.d.ts +11 -9
- package/dist/access-operations-batch.js +43 -41
- package/dist/access-operations.d.ts +16 -14
- package/dist/access-operations.js +46 -44
- package/dist/access-recall-concurrency.d.ts +13 -11
- package/dist/access-recall-concurrency.js +41 -39
- package/dist/access-recall-response.d.ts +13 -11
- package/dist/access-recall-response.js +41 -39
- package/dist/access-recall-surface.d.ts +13 -11
- package/dist/access-recall-surface.js +41 -39
- package/dist/access-schema.d.ts +88 -88
- package/dist/access-schema.js +3 -3
- package/dist/{access-service-DesbnTSa.d.ts → access-service-BY0KJ44c.d.ts} +7 -7
- package/dist/access-service-helpers.d.ts +13 -11
- package/dist/access-service.d.ts +13 -11
- package/dist/access-service.js +41 -39
- package/dist/access-surface-catalog.d.ts +13 -11
- package/dist/access-wearables-meetings-surface.d.ts +5 -3
- package/dist/action-confidence.d.ts +3 -1
- package/dist/active-memory-bridge.d.ts +3 -1
- package/dist/active-recall.d.ts +3 -1
- package/dist/active-recall.js +5 -3
- package/dist/active-recall.js.map +1 -1
- package/dist/adapters/index.js +4 -4
- package/dist/adapters/registry.js +2 -2
- package/dist/ambient-provenance.d.ts +3 -1
- package/dist/archive-mutation-version.d.ts +9 -0
- package/dist/archive-mutation-version.js +20 -0
- package/dist/artifact-search.d.ts +3 -1
- package/dist/behavior-learner.d.ts +3 -1
- package/dist/behavior-signals.d.ts +3 -1
- package/dist/bootstrap.d.ts +11 -9
- package/dist/briefing.d.ts +4 -2
- package/dist/briefing.js +10 -9
- package/dist/buffer-surprise-report.d.ts +3 -1
- package/dist/buffer-turn-helpers.d.ts +3 -1
- package/dist/buffer.d.ts +4 -2
- package/dist/buffer.js +5 -5
- package/dist/bulk-import/index.d.ts +5 -3
- package/dist/calibration.d.ts +3 -1
- package/dist/capabilities.d.ts +3 -1
- package/dist/{catalog-D-G7RgXR.d.ts → catalog-D3B4RDy1.d.ts} +1 -1
- package/dist/causal-behavior.d.ts +3 -1
- package/dist/causal-behavior.js +2 -2
- package/dist/causal-chain.js +2 -2
- package/dist/causal-consolidation.d.ts +3 -1
- package/dist/causal-consolidation.js +13 -12
- package/dist/causal-consolidation.js.map +1 -1
- package/dist/causal-retrieval.js +2 -2
- package/dist/causal-trajectory-graph.d.ts +4 -2
- package/dist/causal-trajectory-graph.js +2 -1
- package/dist/causal-trajectory-graph.js.map +1 -1
- package/dist/causal-trajectory.js +1 -1
- package/dist/{chunk-KDG2KTWW.js → chunk-33SV3E43.js} +4 -4
- package/dist/{chunk-2HGUDBRJ.js → chunk-37ZO6LLJ.js} +2 -2
- package/dist/{chunk-QW3WFHKU.js → chunk-45BRYON6.js} +2 -2
- package/dist/{chunk-GZICBRWK.js → chunk-4F6X3RXW.js} +2 -2
- package/dist/{chunk-7BAYBYY5.js → chunk-4PC4PRJE.js} +2 -2
- package/dist/{chunk-VJXY5ZCT.js → chunk-52UKFTUK.js} +2 -2
- package/dist/{chunk-ESG7Z255.js → chunk-5UDMFE3Q.js} +2 -2
- package/dist/{chunk-JPHEOE6Z.js → chunk-6MTSFX7A.js} +15 -15
- package/dist/chunk-6ZN34ISG.js +44 -0
- package/dist/chunk-6ZN34ISG.js.map +1 -0
- package/dist/{chunk-M4MP34AQ.js → chunk-AHZOMHWN.js} +1 -1
- package/dist/{chunk-Z3RHPGSY.js → chunk-AIUBBHSF.js} +3 -3
- package/dist/{chunk-S3AYIQCU.js → chunk-AXIDQG5A.js} +12 -12
- package/dist/{chunk-QN7NTKRC.js → chunk-AZ2QBGYB.js} +1 -1
- package/dist/{chunk-47J4EDGR.js → chunk-AZM4CCAC.js} +8 -8
- package/dist/{chunk-IXXABW5A.js → chunk-BHXYAVUM.js} +11 -5
- package/dist/chunk-BHXYAVUM.js.map +1 -0
- package/dist/{chunk-L2DPF4DF.js → chunk-BIJAWZYW.js} +54 -12
- package/dist/chunk-BIJAWZYW.js.map +1 -0
- package/dist/{chunk-LEJPSMKM.js → chunk-C5QLI33R.js} +3 -3
- package/dist/{chunk-SV3PSA7Y.js → chunk-CV6BBVYV.js} +5 -5
- package/dist/{chunk-V7X767RT.js → chunk-DCZBSE4F.js} +52 -52
- package/dist/chunk-FC67T4E6.js +70 -0
- package/dist/chunk-FC67T4E6.js.map +1 -0
- package/dist/{chunk-7PKFPLLZ.js → chunk-FXTYIA46.js} +2 -2
- package/dist/{chunk-64BHFFKZ.js → chunk-HLYR2TQL.js} +5 -5
- package/dist/{chunk-5C2HNJMO.js → chunk-J5LLX6IL.js} +32 -32
- package/dist/{chunk-RKNJBZ55.js → chunk-JBPKEARU.js} +4 -4
- package/dist/{chunk-PLKFO2HJ.js → chunk-JL43BVDE.js} +2 -2
- package/dist/{chunk-2S72LWKX.js → chunk-JYL3BVVZ.js} +2 -2
- package/dist/{chunk-IPLYGWQF.js → chunk-KQAFEZQX.js} +5 -5
- package/dist/chunk-LXY5VVGM.js +61 -0
- package/dist/chunk-LXY5VVGM.js.map +1 -0
- package/dist/{chunk-PYKKWQZ2.js → chunk-M7BXDKPR.js} +6 -6
- package/dist/{chunk-Z54EM5YZ.js → chunk-MAXVH44E.js} +7 -7
- package/dist/{chunk-LYMNZZIM.js → chunk-NIS256NW.js} +2 -2
- package/dist/{chunk-67QG75MJ.js → chunk-NLK2VZYZ.js} +2 -2
- package/dist/{chunk-A4QMKFFO.js → chunk-O5FFBO2M.js} +14 -28
- package/dist/chunk-O5FFBO2M.js.map +1 -0
- package/dist/{chunk-JYIFGGEM.js → chunk-OUKBMQ2O.js} +2 -2
- package/dist/{chunk-75XOWNRS.js → chunk-OYESWJ43.js} +4 -4
- package/dist/{chunk-TS4AJ2GI.js → chunk-PA45XCUD.js} +3 -3
- package/dist/{chunk-BJYET5TX.js → chunk-PRDJMOST.js} +7 -1
- package/dist/chunk-PRDJMOST.js.map +1 -0
- package/dist/{chunk-4MBPIL4V.js → chunk-PUSLMZBQ.js} +2 -2
- package/dist/{chunk-RVO6TCTU.js → chunk-QCECHYZL.js} +6 -6
- package/dist/{chunk-GFIKG4FR.js → chunk-QGR6OVGT.js} +2 -2
- package/dist/{chunk-NSGU76YO.js → chunk-QHFV6VNU.js} +6 -2
- package/dist/chunk-QHFV6VNU.js.map +1 -0
- package/dist/{chunk-PDEKQWDV.js → chunk-QT2FRYI4.js} +18 -2
- package/dist/chunk-QT2FRYI4.js.map +1 -0
- package/dist/{chunk-HNZKVLAN.js → chunk-S354QO4R.js} +2 -2
- package/dist/{chunk-NO37MR45.js → chunk-SOEN6KCF.js} +2 -2
- package/dist/{chunk-FXX6RQYC.js → chunk-TPTECFWR.js} +2 -2
- package/dist/{chunk-GDDGZVFI.js → chunk-UEVLYVNW.js} +2 -2
- package/dist/{chunk-YQ4IQ2GA.js → chunk-UT5BV5OG.js} +2 -2
- package/dist/{chunk-LAQ33B5D.js → chunk-UU3G6GUP.js} +1 -1
- package/dist/chunk-UU3G6GUP.js.map +1 -0
- package/dist/{chunk-LNTUXKBT.js → chunk-UYYW2KPU.js} +2 -2
- package/dist/{chunk-BOFVT6P3.js → chunk-VBHPOWBJ.js} +175 -98
- package/dist/chunk-VBHPOWBJ.js.map +1 -0
- package/dist/{chunk-QSYL5CXK.js → chunk-VKXWK44Q.js} +3 -3
- package/dist/{chunk-KMWYZTIE.js → chunk-VMMQDEN5.js} +2 -2
- package/dist/{chunk-JB6D7EFB.js → chunk-VVG2BOKA.js} +2 -2
- package/dist/chunk-W5BIQOAJ.js +69 -0
- package/dist/chunk-W5BIQOAJ.js.map +1 -0
- package/dist/{chunk-V5H6OJCR.js → chunk-W5XVQFLM.js} +2 -2
- package/dist/{chunk-OIOZ6MOP.js → chunk-W6FNX2LP.js} +2 -2
- package/dist/{chunk-6QPHF6RR.js → chunk-WQM326GS.js} +2 -2
- package/dist/{chunk-TQFHQPWD.js → chunk-XADV5IZ3.js} +8 -8
- package/dist/{chunk-DATDLJAO.js → chunk-XAPMEU3K.js} +2 -2
- package/dist/chunk-XXSOVESA.js +42 -0
- package/dist/chunk-XXSOVESA.js.map +1 -0
- package/dist/{chunk-IK6X5QPO.js → chunk-ZHY5TMJG.js} +1193 -443
- package/dist/chunk-ZHY5TMJG.js.map +1 -0
- package/dist/{chunk-N3LL5NEU.js → chunk-ZMYDYNI4.js} +2 -2
- package/dist/{cli-D4vvtxjY.d.ts → cli-CnOkH9hA.d.ts} +6 -6
- package/dist/cli.d.ts +15 -13
- package/dist/cli.js +74 -70
- package/dist/coding/pre-action-gate.d.ts +3 -1
- package/dist/coding/pre-action-gate.js +2 -2
- package/dist/compounding/engine.d.ts +4 -2
- package/dist/compounding/engine.js +10 -9
- package/dist/compounding/preference-consolidator.d.ts +3 -1
- package/dist/compression-optimizer.d.ts +3 -1
- package/dist/config.d.ts +3 -1
- package/dist/config.js +5 -3
- package/dist/connectors/codex-materialize-runner.d.ts +3 -1
- package/dist/connectors/codex-materialize-runner.js +10 -9
- package/dist/connectors/codex-materialize.d.ts +3 -1
- package/dist/connectors/index.d.ts +3 -1
- package/dist/connectors/index.js +10 -9
- package/dist/consolidation-provenance-check.d.ts +4 -2
- package/dist/consolidation-undo.d.ts +4 -2
- package/dist/contradiction/index.d.ts +4 -2
- package/dist/contradiction-config.d.ts +29 -0
- package/dist/contradiction-config.js +12 -0
- package/dist/contradiction-config.js.map +1 -0
- package/dist/converge-config.d.ts +3 -1
- package/dist/convergence-refresh.d.ts +4 -2
- package/dist/convergence-refresh.js +12 -11
- package/dist/conversation-index/backend.d.ts +3 -1
- package/dist/conversation-index/chunker.d.ts +3 -1
- package/dist/conversation-index/faiss-adapter.d.ts +3 -1
- package/dist/conversation-index/indexer.d.ts +3 -1
- package/dist/conversation-index/search.d.ts +3 -1
- package/dist/corpus-watermark.d.ts +3 -1
- package/dist/corpus-watermark.js +21 -20
- package/dist/day-summary.d.ts +3 -1
- package/dist/delinearize.d.ts +3 -1
- package/dist/dependency-propagation-config.d.ts +3 -1
- package/dist/direct-answer-wiring.d.ts +3 -1
- package/dist/direct-answer.d.ts +3 -1
- package/dist/embedding-fallback.d.ts +3 -1
- package/dist/enrichment/index.d.ts +3 -1
- package/dist/entity-retrieval.d.ts +4 -2
- package/dist/entity-retrieval.js +10 -9
- package/dist/entity-schema.d.ts +3 -1
- package/dist/explicit-capture.d.ts +11 -9
- package/dist/explicit-capture.js +3 -2
- package/dist/external-wiki-access.d.ts +13 -11
- package/dist/external-wiki-access.js +42 -40
- package/dist/external-wiki-collection-registration.d.ts +3 -1
- package/dist/external-wiki-collection.d.ts +3 -1
- package/dist/external-wiki-mcp-tools.d.ts +13 -11
- package/dist/extraction-error-classification.d.ts +3 -1
- package/dist/extraction-faithfulness.d.ts +3 -1
- package/dist/extraction-judge-telemetry.d.ts +3 -1
- package/dist/extraction-judge-training.d.ts +3 -1
- package/dist/extraction-judge.d.ts +3 -1
- package/dist/extraction-liveness.d.ts +3 -1
- package/dist/extraction-normalization.d.ts +3 -1
- package/dist/extraction-prompt.d.ts +3 -1
- package/dist/extraction-source-grounding-rules.d.ts +3 -1
- package/dist/extraction-source-grounding.d.ts +3 -1
- package/dist/extraction.d.ts +3 -1
- package/dist/extraction.js +22 -21
- package/dist/fallback-llm.d.ts +3 -1
- package/dist/graph-444E-DrF.d.ts +349 -0
- package/dist/graph-dashboard-diff.d.ts +4 -2
- package/dist/graph-dashboard-key.d.ts +4 -2
- package/dist/graph-dashboard-parser.d.ts +4 -2
- package/dist/{graph-edge-decay-Z6ENALU2.js → graph-edge-decay-65KMOJWY.js} +4 -3
- package/dist/graph-edge-decay-65KMOJWY.js.map +1 -0
- package/dist/graph-edge-reinforcement.d.ts +4 -2
- package/dist/graph-path-reconstruction.d.ts +10 -0
- package/dist/graph-path-reconstruction.js +12 -0
- package/dist/graph-path-reconstruction.js.map +1 -0
- package/dist/graph-path-scoring-config.d.ts +8 -0
- package/dist/graph-path-scoring-config.js +10 -0
- package/dist/graph-path-scoring-config.js.map +1 -0
- package/dist/graph-path-scoring.d.ts +36 -0
- package/dist/graph-path-scoring.js +11 -0
- package/dist/graph-path-scoring.js.map +1 -0
- package/dist/graph-snapshot.d.ts +4 -2
- package/dist/graph-snapshot.js +3 -2
- package/dist/graph.d.ts +5 -318
- package/dist/graph.js +5 -1
- package/dist/harmonic-retrieval.js +1 -1
- package/dist/importance.d.ts +3 -1
- package/dist/importers/index.d.ts +3 -1
- package/dist/in-flight-reads.d.ts +3 -1
- package/dist/index.d.ts +696 -694
- package/dist/index.js +149 -144
- package/dist/intent.d.ts +3 -1
- package/dist/lcm/engine.d.ts +3 -1
- package/dist/lcm/engine.js +3 -3
- package/dist/lcm/index.d.ts +3 -1
- package/dist/lcm/index.js +6 -6
- package/dist/lcm/tools.d.ts +3 -1
- package/dist/lifecycle.d.ts +3 -1
- package/dist/live-connectors-runner.d.ts +3 -1
- package/dist/local-llm.d.ts +3 -1
- package/dist/local-model-endpoint.d.ts +3 -1
- package/dist/maintenance/memory-governance.d.ts +3 -1
- package/dist/maintenance/memory-governance.js +10 -9
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.d.ts +4 -2
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +10 -9
- package/dist/maintenance/rebuild-memory-projection.d.ts +4 -2
- package/dist/maintenance/rebuild-memory-projection.js +11 -10
- package/dist/{maintenance-ClmUVpGI.d.ts → maintenance-CKwxpdRx.d.ts} +3 -3
- package/dist/mcp-memory-inspector-app.d.ts +13 -11
- package/dist/memory-action-policy.d.ts +3 -1
- package/dist/memory-cache.d.ts +3 -1
- package/dist/memory-lifecycle-ledger-utils.d.ts +3 -1
- package/dist/memory-projection-store.d.ts +3 -1
- package/dist/memory-provenance.d.ts +3 -1
- package/dist/memory-snapshot.d.ts +3 -1
- package/dist/memory-worth-outcomes.d.ts +4 -2
- package/dist/models-json.d.ts +3 -1
- package/dist/namespaces/migrate.d.ts +5 -3
- package/dist/namespaces/migrate.js +20 -19
- package/dist/namespaces/principal.d.ts +3 -1
- package/dist/namespaces/search.d.ts +3 -1
- package/dist/namespaces/search.js +7 -7
- package/dist/namespaces/storage.d.ts +5 -3
- package/dist/namespaces/storage.js +10 -9
- package/dist/native-knowledge.d.ts +3 -1
- package/dist/offline-sync-impression-drain.d.ts +3 -1
- package/dist/operator-doctor-corpus.d.ts +3 -1
- package/dist/operator-doctor-corpus.js +22 -21
- package/dist/operator-doctor-replica.d.ts +3 -1
- package/dist/operator-toolkit.d.ts +6 -4
- package/dist/operator-toolkit.js +33 -29
- package/dist/orchestration/compression-guideline-coordinator.d.ts +4 -2
- package/dist/orchestration/maintenance.d.ts +6 -4
- package/dist/orchestration/maintenance.js +15 -14
- package/dist/{orchestrator-qJ07zI8H.d.ts → orchestrator-BshGam-o.d.ts} +45 -19
- package/dist/orchestrator.d.ts +11 -9
- package/dist/orchestrator.js +109 -104
- package/dist/patterns-cli.d.ts +3 -1
- package/dist/{pipeline-WFCxhHOn.d.ts → pipeline-D21Rs6gW.d.ts} +1 -1
- package/dist/policy-runtime.d.ts +3 -1
- package/dist/proactive-contention.d.ts +3 -1
- package/dist/provenance.d.ts +3 -1
- package/dist/{qmd-5tzkQ44y.d.ts → qmd-Dhle1P4X.d.ts} +1 -1
- package/dist/qmd-preflight.d.ts +4 -2
- package/dist/qmd-recall-cache.d.ts +3 -1
- package/dist/qmd.d.ts +4 -2
- package/dist/qmd.js +4 -4
- package/dist/recall-concurrency-config.d.ts +3 -1
- package/dist/recall-disclosure-escalation.d.ts +3 -1
- package/dist/recall-explain-renderer.d.ts +3 -1
- package/dist/recall-explain-renderer.js +3 -3
- package/dist/recall-memory-map.d.ts +3 -1
- package/dist/recall-planner-llm.d.ts +3 -1
- package/dist/recall-planner-llm.js +3 -3
- package/dist/recall-state.d.ts +7 -1
- package/dist/recall-state.js +1 -1
- package/dist/recall-tag-filter.d.ts +3 -1
- package/dist/recall-timings.d.ts +3 -1
- package/dist/recall-xray-cli.d.ts +3 -1
- package/dist/recall-xray-cli.js +4 -4
- package/dist/recall-xray-renderer.d.ts +3 -1
- package/dist/recall-xray-renderer.js +3 -3
- package/dist/recall-xray.d.ts +7 -1
- package/dist/recall-xray.js +2 -2
- package/dist/reconcile/cursor.d.ts +3 -1
- package/dist/reconcile/manifest.d.ts +3 -1
- package/dist/reconcile/plan.d.ts +3 -1
- package/dist/replay/normalizers/chatgpt.d.ts +3 -1
- package/dist/replay/normalizers/claude.d.ts +3 -1
- package/dist/replay/normalizers/openclaw.d.ts +3 -1
- package/dist/replay/normalizers/shared.d.ts +3 -1
- package/dist/replay/runner.d.ts +3 -1
- package/dist/replay/types.d.ts +3 -1
- package/dist/replica-divergence.d.ts +3 -1
- package/dist/replica-peers-config.d.ts +3 -1
- package/dist/resolve-auth-token.d.ts +3 -1
- package/dist/resume-bundles.js +6 -4
- package/dist/retrieval-agents.d.ts +4 -2
- package/dist/retrieval-tiers.d.ts +3 -1
- package/dist/routing/engine.d.ts +3 -1
- package/dist/routing/store.d.ts +3 -1
- package/dist/salvage-envelope.d.ts +3 -1
- package/dist/schemas.d.ts +116 -116
- package/dist/{scope-profiles--vfXPl3X.d.ts → scope-profiles-NzGPDXLn.d.ts} +1 -1
- package/dist/search/embed-helper.d.ts +3 -1
- package/dist/search/factory.d.ts +3 -1
- package/dist/search/factory.js +6 -6
- package/dist/search/index.d.ts +3 -1
- package/dist/search/index.js +12 -12
- package/dist/search/lancedb-backend.d.ts +3 -1
- package/dist/search/meilisearch-backend.d.ts +3 -1
- package/dist/search/noop-backend.d.ts +3 -1
- package/dist/search/orama-backend.d.ts +3 -1
- package/dist/search/port.d.ts +3 -1
- package/dist/search/remote-backend.d.ts +3 -1
- package/dist/{semantic-consolidation-BBDuP9nJ.d.ts → semantic-consolidation-DLDKvyy5.d.ts} +1 -1
- package/dist/semantic-consolidation.d.ts +4 -2
- package/dist/semantic-consolidation.js +11 -10
- package/dist/semantic-rule-promotion.js +10 -9
- package/dist/semantic-rule-verifier.d.ts +3 -1
- package/dist/semantic-rule-verifier.js +10 -9
- package/dist/{service-B8DtbpCM.d.ts → service-BIgVM7fL.d.ts} +2 -2
- package/dist/session-observer-bands.d.ts +3 -1
- package/dist/session-observer-state.d.ts +3 -1
- package/dist/shared-context/manager.d.ts +11 -9
- package/dist/signal.d.ts +3 -1
- package/dist/source-agent-qualifier.d.ts +3 -1
- package/dist/source-agent-qualifier.js +18 -17
- package/dist/{storage-BkYN1RIy.d.ts → storage-CVfGWqCI.d.ts} +7 -6
- package/dist/storage.d.ts +4 -2
- package/dist/storage.js +9 -8
- package/dist/summarizer.d.ts +3 -1
- package/dist/summary-snapshot.d.ts +3 -1
- package/dist/temporal-supersession.d.ts +4 -2
- package/dist/temporal-timeline-recall.d.ts +3 -1
- package/dist/temporal-validity.d.ts +3 -1
- package/dist/threading.d.ts +3 -1
- package/dist/tier-migration.d.ts +4 -2
- package/dist/tier-routing.d.ts +3 -1
- package/dist/topics.d.ts +3 -1
- package/dist/transcript.d.ts +3 -1
- package/dist/transfer/autodetect.js +1 -1
- package/dist/transfer/backup.js +1 -1
- package/dist/transfer/capsule-export.js +2 -2
- package/dist/transfer/types.d.ts +66 -66
- package/dist/trust-score-stage.d.ts +3 -1
- package/dist/trust-score.d.ts +3 -1
- package/dist/{types-u2s9j1uD.d.ts → types-CkiDUorT.d.ts} +10 -16
- package/dist/types.d.ts +3 -1
- package/dist/types.js +1 -1
- package/dist/utility-runtime.d.ts +3 -1
- package/dist/verified-recall.js +10 -9
- package/dist/write-envelope.d.ts +3 -1
- package/package.json +2 -2
- package/src/archive-mutation-version.ts +59 -0
- package/src/config.test.ts +45 -0
- package/src/config.ts +10 -30
- package/src/contradiction/contradiction.test.ts +86 -1
- package/src/contradiction-config.ts +92 -0
- package/src/graph-path-reconstruction.ts +91 -0
- package/src/graph-path-scoring-config.ts +55 -0
- package/src/graph-path-scoring.test.ts +238 -0
- package/src/graph-path-scoring.ts +77 -0
- package/src/graph.test.ts +225 -5
- package/src/graph.ts +72 -5
- package/src/orchestration/contradiction-linking-coordinator.ts +246 -76
- package/src/orchestration/contradiction-localization.integration.test.ts +1953 -0
- package/src/orchestration/extraction-anchor-snapshot.ts +83 -0
- package/src/orchestration/extraction-persist-deps.ts +8 -2
- package/src/orchestration/extraction-persist.ts +43 -34
- package/src/orchestration/graph-path-state-loader.test.ts +801 -0
- package/src/orchestration/graph-path-state-loader.ts +318 -0
- package/src/orchestration/graph-recall-coordinator.integration.test.ts +1163 -0
- package/src/orchestration/graph-recall-coordinator.ts +206 -29
- package/src/orchestration/graph-recall-seam.ts +19 -0
- package/src/orchestration/recall-internal-deps.ts +7 -13
- package/src/orchestration/recall-internal.ts +5 -0
- package/src/orchestration/recall-introspection.ts +9 -1
- package/src/orchestration/recall-search-pipeline.ts +8 -13
- package/src/orchestration/update-localization.test.ts +784 -0
- package/src/orchestration/update-localization.ts +173 -0
- package/src/orchestrator.ts +19 -18
- package/src/recall-state.test.ts +29 -1
- package/src/recall-state.ts +13 -0
- package/src/recall-xray-renderer.test.ts +45 -0
- package/src/recall-xray-renderer.ts +16 -0
- package/src/recall-xray.ts +13 -0
- package/src/storage/supersession-side-effects.ts +97 -0
- package/src/storage/tombstone-blocked-capture-sync.ts +13 -5
- package/src/storage-contract/cache-coherence.test.ts +299 -0
- package/src/storage.ts +86 -88
- package/src/types.ts +12 -15
- package/dist/chunk-A4QMKFFO.js.map +0 -1
- package/dist/chunk-BJYET5TX.js.map +0 -1
- package/dist/chunk-BOFVT6P3.js.map +0 -1
- package/dist/chunk-IK6X5QPO.js.map +0 -1
- package/dist/chunk-IXXABW5A.js.map +0 -1
- package/dist/chunk-L2DPF4DF.js.map +0 -1
- package/dist/chunk-LAQ33B5D.js.map +0 -1
- package/dist/chunk-NSGU76YO.js.map +0 -1
- package/dist/chunk-PDEKQWDV.js.map +0 -1
- /package/dist/{graph-edge-decay-Z6ENALU2.js.map → archive-mutation-version.js.map} +0 -0
- /package/dist/{chunk-KDG2KTWW.js.map → chunk-33SV3E43.js.map} +0 -0
- /package/dist/{chunk-2HGUDBRJ.js.map → chunk-37ZO6LLJ.js.map} +0 -0
- /package/dist/{chunk-QW3WFHKU.js.map → chunk-45BRYON6.js.map} +0 -0
- /package/dist/{chunk-GZICBRWK.js.map → chunk-4F6X3RXW.js.map} +0 -0
- /package/dist/{chunk-7BAYBYY5.js.map → chunk-4PC4PRJE.js.map} +0 -0
- /package/dist/{chunk-VJXY5ZCT.js.map → chunk-52UKFTUK.js.map} +0 -0
- /package/dist/{chunk-ESG7Z255.js.map → chunk-5UDMFE3Q.js.map} +0 -0
- /package/dist/{chunk-JPHEOE6Z.js.map → chunk-6MTSFX7A.js.map} +0 -0
- /package/dist/{chunk-M4MP34AQ.js.map → chunk-AHZOMHWN.js.map} +0 -0
- /package/dist/{chunk-Z3RHPGSY.js.map → chunk-AIUBBHSF.js.map} +0 -0
- /package/dist/{chunk-S3AYIQCU.js.map → chunk-AXIDQG5A.js.map} +0 -0
- /package/dist/{chunk-QN7NTKRC.js.map → chunk-AZ2QBGYB.js.map} +0 -0
- /package/dist/{chunk-47J4EDGR.js.map → chunk-AZM4CCAC.js.map} +0 -0
- /package/dist/{chunk-LEJPSMKM.js.map → chunk-C5QLI33R.js.map} +0 -0
- /package/dist/{chunk-SV3PSA7Y.js.map → chunk-CV6BBVYV.js.map} +0 -0
- /package/dist/{chunk-V7X767RT.js.map → chunk-DCZBSE4F.js.map} +0 -0
- /package/dist/{chunk-7PKFPLLZ.js.map → chunk-FXTYIA46.js.map} +0 -0
- /package/dist/{chunk-64BHFFKZ.js.map → chunk-HLYR2TQL.js.map} +0 -0
- /package/dist/{chunk-5C2HNJMO.js.map → chunk-J5LLX6IL.js.map} +0 -0
- /package/dist/{chunk-RKNJBZ55.js.map → chunk-JBPKEARU.js.map} +0 -0
- /package/dist/{chunk-PLKFO2HJ.js.map → chunk-JL43BVDE.js.map} +0 -0
- /package/dist/{chunk-2S72LWKX.js.map → chunk-JYL3BVVZ.js.map} +0 -0
- /package/dist/{chunk-IPLYGWQF.js.map → chunk-KQAFEZQX.js.map} +0 -0
- /package/dist/{chunk-PYKKWQZ2.js.map → chunk-M7BXDKPR.js.map} +0 -0
- /package/dist/{chunk-Z54EM5YZ.js.map → chunk-MAXVH44E.js.map} +0 -0
- /package/dist/{chunk-LYMNZZIM.js.map → chunk-NIS256NW.js.map} +0 -0
- /package/dist/{chunk-67QG75MJ.js.map → chunk-NLK2VZYZ.js.map} +0 -0
- /package/dist/{chunk-JYIFGGEM.js.map → chunk-OUKBMQ2O.js.map} +0 -0
- /package/dist/{chunk-75XOWNRS.js.map → chunk-OYESWJ43.js.map} +0 -0
- /package/dist/{chunk-TS4AJ2GI.js.map → chunk-PA45XCUD.js.map} +0 -0
- /package/dist/{chunk-4MBPIL4V.js.map → chunk-PUSLMZBQ.js.map} +0 -0
- /package/dist/{chunk-RVO6TCTU.js.map → chunk-QCECHYZL.js.map} +0 -0
- /package/dist/{chunk-GFIKG4FR.js.map → chunk-QGR6OVGT.js.map} +0 -0
- /package/dist/{chunk-HNZKVLAN.js.map → chunk-S354QO4R.js.map} +0 -0
- /package/dist/{chunk-NO37MR45.js.map → chunk-SOEN6KCF.js.map} +0 -0
- /package/dist/{chunk-FXX6RQYC.js.map → chunk-TPTECFWR.js.map} +0 -0
- /package/dist/{chunk-GDDGZVFI.js.map → chunk-UEVLYVNW.js.map} +0 -0
- /package/dist/{chunk-YQ4IQ2GA.js.map → chunk-UT5BV5OG.js.map} +0 -0
- /package/dist/{chunk-LNTUXKBT.js.map → chunk-UYYW2KPU.js.map} +0 -0
- /package/dist/{chunk-QSYL5CXK.js.map → chunk-VKXWK44Q.js.map} +0 -0
- /package/dist/{chunk-KMWYZTIE.js.map → chunk-VMMQDEN5.js.map} +0 -0
- /package/dist/{chunk-JB6D7EFB.js.map → chunk-VVG2BOKA.js.map} +0 -0
- /package/dist/{chunk-V5H6OJCR.js.map → chunk-W5XVQFLM.js.map} +0 -0
- /package/dist/{chunk-OIOZ6MOP.js.map → chunk-W6FNX2LP.js.map} +0 -0
- /package/dist/{chunk-6QPHF6RR.js.map → chunk-WQM326GS.js.map} +0 -0
- /package/dist/{chunk-TQFHQPWD.js.map → chunk-XADV5IZ3.js.map} +0 -0
- /package/dist/{chunk-DATDLJAO.js.map → chunk-XAPMEU3K.js.map} +0 -0
- /package/dist/{chunk-N3LL5NEU.js.map → chunk-ZMYDYNI4.js.map} +0 -0
|
@@ -0,0 +1,1163 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { appendFile, mkdir, mkdtemp, rm, symlink, writeFile } from "node:fs/promises";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import test from "node:test";
|
|
6
|
+
|
|
7
|
+
import { parseConfig } from "../config.js";
|
|
8
|
+
import { graphFilePath, GraphIndex, type GraphConfig } from "../graph.js";
|
|
9
|
+
import {
|
|
10
|
+
blendGraphExpandedRecallScore,
|
|
11
|
+
GraphRecallCoordinator,
|
|
12
|
+
} from "./graph-recall-coordinator.js";
|
|
13
|
+
import type { PluginConfig, QmdSearchResult } from "../types.js";
|
|
14
|
+
import { StorageManager } from "../storage.js";
|
|
15
|
+
|
|
16
|
+
const AS_OF = "2026-01-01T00:00:00.000Z";
|
|
17
|
+
const HISTORICAL_VALID_AT = "2025-01-01T00:00:00.000Z";
|
|
18
|
+
|
|
19
|
+
type NamespaceFixture = {
|
|
20
|
+
name: string;
|
|
21
|
+
dir: string;
|
|
22
|
+
storage: StorageManager;
|
|
23
|
+
seedPath: string;
|
|
24
|
+
intermediatePath: string;
|
|
25
|
+
cleanPath: string;
|
|
26
|
+
secondCleanPath?: string;
|
|
27
|
+
stalePath: string;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
function makeConfig(overrides: Record<string, unknown> = {}): PluginConfig {
|
|
31
|
+
return parseConfig({
|
|
32
|
+
openaiApiKey: "test-key",
|
|
33
|
+
graphRecallEnabled: true,
|
|
34
|
+
multiGraphMemoryEnabled: true,
|
|
35
|
+
maxGraphTraversalSteps: 2,
|
|
36
|
+
graphTraversalPageRankIterations: 0,
|
|
37
|
+
graphPathScoring: {
|
|
38
|
+
enabled: true,
|
|
39
|
+
invalidNodePenalty: 0.2,
|
|
40
|
+
includePathInProvenance: true,
|
|
41
|
+
},
|
|
42
|
+
...overrides,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function appendEdge(
|
|
47
|
+
dir: string,
|
|
48
|
+
from: string,
|
|
49
|
+
to: string,
|
|
50
|
+
weight = 1,
|
|
51
|
+
): Promise<void> {
|
|
52
|
+
const file = graphFilePath(dir, "entity");
|
|
53
|
+
await mkdir(path.dirname(file), { recursive: true });
|
|
54
|
+
await appendFile(
|
|
55
|
+
file,
|
|
56
|
+
`${JSON.stringify({
|
|
57
|
+
from,
|
|
58
|
+
to,
|
|
59
|
+
type: "entity",
|
|
60
|
+
weight,
|
|
61
|
+
label: "test",
|
|
62
|
+
ts: AS_OF,
|
|
63
|
+
})}\n`,
|
|
64
|
+
"utf8",
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async function createNamespace(
|
|
69
|
+
root: string,
|
|
70
|
+
name: string,
|
|
71
|
+
intermediateStatus: "active" | "superseded" = "active",
|
|
72
|
+
includeSecondClean = false,
|
|
73
|
+
): Promise<NamespaceFixture> {
|
|
74
|
+
const dir = path.join(root, name);
|
|
75
|
+
const storage = new StorageManager(dir);
|
|
76
|
+
await storage.ensureDirectories();
|
|
77
|
+
await storage.writeMemory("fact", `${name} seed`, {
|
|
78
|
+
source: "test",
|
|
79
|
+
validAt: HISTORICAL_VALID_AT,
|
|
80
|
+
});
|
|
81
|
+
await storage.writeMemory("fact", `${name} intermediate`, {
|
|
82
|
+
source: "test",
|
|
83
|
+
status: intermediateStatus,
|
|
84
|
+
validAt:
|
|
85
|
+
intermediateStatus === "superseded"
|
|
86
|
+
? "2026-02-01T00:00:00.000Z"
|
|
87
|
+
: HISTORICAL_VALID_AT,
|
|
88
|
+
});
|
|
89
|
+
await storage.writeMemory("fact", `${name} clean`, {
|
|
90
|
+
source: "test",
|
|
91
|
+
validAt: HISTORICAL_VALID_AT,
|
|
92
|
+
});
|
|
93
|
+
if (includeSecondClean) {
|
|
94
|
+
await storage.writeMemory("fact", `${name} second clean`, {
|
|
95
|
+
source: "test",
|
|
96
|
+
validAt: HISTORICAL_VALID_AT,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
await storage.writeMemory("fact", `${name} stale`, {
|
|
100
|
+
source: "test",
|
|
101
|
+
validAt: HISTORICAL_VALID_AT,
|
|
102
|
+
});
|
|
103
|
+
const paths = await storage.collectActiveMemoryPaths();
|
|
104
|
+
const memories = await storage.readAllMemories();
|
|
105
|
+
const find = (text: string): string => {
|
|
106
|
+
const memory = memories.find((item) => item.content.includes(text));
|
|
107
|
+
assert.ok(memory, `missing ${text}`);
|
|
108
|
+
return path.relative(dir, memory.path).split(path.sep).join("/");
|
|
109
|
+
};
|
|
110
|
+
const seedPath = find(`${name} seed`);
|
|
111
|
+
const intermediatePath = find(`${name} intermediate`);
|
|
112
|
+
const cleanPath = find(`${name} clean`);
|
|
113
|
+
const secondCleanPath = includeSecondClean ? find(`${name} second clean`) : undefined;
|
|
114
|
+
const stalePath = find(`${name} stale`);
|
|
115
|
+
await appendEdge(dir, seedPath, cleanPath, 1);
|
|
116
|
+
if (secondCleanPath) await appendEdge(dir, seedPath, secondCleanPath, 0.95);
|
|
117
|
+
await appendEdge(dir, seedPath, intermediatePath, 0.9);
|
|
118
|
+
await appendEdge(dir, intermediatePath, stalePath, 1);
|
|
119
|
+
assert.equal(paths.length, includeSecondClean ? 5 : 4);
|
|
120
|
+
return { name, dir, storage, seedPath, intermediatePath, cleanPath, secondCleanPath, stalePath };
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function makeCoordinator(
|
|
124
|
+
config: PluginConfig,
|
|
125
|
+
fixtures: Map<string, NamespaceFixture>,
|
|
126
|
+
readAllMemoriesCalls?: { count: number },
|
|
127
|
+
graphIndexes?: Map<string, GraphIndex>,
|
|
128
|
+
): GraphRecallCoordinator {
|
|
129
|
+
const indexes = new Map<string, GraphIndex>();
|
|
130
|
+
for (const fixture of fixtures.values()) {
|
|
131
|
+
indexes.set(
|
|
132
|
+
fixture.dir,
|
|
133
|
+
graphIndexes?.get(fixture.dir) ??
|
|
134
|
+
new GraphIndex(fixture.dir, config as unknown as GraphConfig),
|
|
135
|
+
);
|
|
136
|
+
if (readAllMemoriesCalls) {
|
|
137
|
+
const originalHot = fixture.storage.readAllMemories.bind(fixture.storage);
|
|
138
|
+
fixture.storage.readAllMemories = async (...args: Parameters<StorageManager["readAllMemories"]>) => {
|
|
139
|
+
readAllMemoriesCalls.count += 1;
|
|
140
|
+
return originalHot(...args);
|
|
141
|
+
};
|
|
142
|
+
const originalCold = fixture.storage.readAllColdMemories.bind(fixture.storage);
|
|
143
|
+
fixture.storage.readAllColdMemories = async () => {
|
|
144
|
+
readAllMemoriesCalls.count += 1;
|
|
145
|
+
return originalCold();
|
|
146
|
+
};
|
|
147
|
+
const originalArchived = fixture.storage.readArchivedMemories.bind(fixture.storage);
|
|
148
|
+
fixture.storage.readArchivedMemories = async () => {
|
|
149
|
+
readAllMemoriesCalls.count += 1;
|
|
150
|
+
return originalArchived();
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
const defaultFixture = fixtures.values().next().value as NamespaceFixture;
|
|
155
|
+
return new GraphRecallCoordinator({
|
|
156
|
+
getConfig: () => config,
|
|
157
|
+
getStorage: () => defaultFixture.storage,
|
|
158
|
+
storageFor: async (namespace) => {
|
|
159
|
+
const fixture = fixtures.get(namespace);
|
|
160
|
+
assert(fixture, `missing namespace fixture: ${namespace}`);
|
|
161
|
+
return fixture.storage;
|
|
162
|
+
},
|
|
163
|
+
graphIndexFor: (storage) => {
|
|
164
|
+
const index = indexes.get(storage.dir);
|
|
165
|
+
assert(index, `missing graph index: ${storage.dir}`);
|
|
166
|
+
return index;
|
|
167
|
+
},
|
|
168
|
+
namespaceFromPath: () => defaultFixture.name,
|
|
169
|
+
resolveColdQmdResultForRecall: async () => null,
|
|
170
|
+
storageForAbsoluteQmdResultPath: async (resultPath) => {
|
|
171
|
+
for (const fixture of fixtures.values()) {
|
|
172
|
+
if (resultPath.startsWith(`${fixture.dir}${path.sep}`)) {
|
|
173
|
+
return { storage: fixture.storage, dir: fixture.dir, namespace: fixture.name };
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return null;
|
|
177
|
+
},
|
|
178
|
+
readQmdResultMemory: async (resultPath, storage) =>
|
|
179
|
+
storage.readMemoryByPath(resultPath),
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function seedResult(fixture: NamespaceFixture): QmdSearchResult {
|
|
184
|
+
return {
|
|
185
|
+
docid: fixture.seedPath,
|
|
186
|
+
path: path.join(fixture.dir, fixture.seedPath),
|
|
187
|
+
snippet: "seed",
|
|
188
|
+
score: 0.5,
|
|
189
|
+
namespace: fixture.name,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
async function expand(
|
|
193
|
+
coordinator: GraphRecallCoordinator,
|
|
194
|
+
results: QmdSearchResult[],
|
|
195
|
+
namespaces: string[],
|
|
196
|
+
recallResultLimit = 1,
|
|
197
|
+
) {
|
|
198
|
+
return coordinator.expandResultsViaGraph({
|
|
199
|
+
memoryResults: results,
|
|
200
|
+
recallNamespaces: namespaces,
|
|
201
|
+
recallResultLimit,
|
|
202
|
+
asOf: AS_OF,
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
async function createHistoricalCapCandidates(
|
|
207
|
+
fixture: NamespaceFixture,
|
|
208
|
+
futureCount = 45,
|
|
209
|
+
): Promise<{ candidatePaths: string[]; validPath: string }> {
|
|
210
|
+
await Promise.all(
|
|
211
|
+
Array.from({ length: futureCount + 1 }, (_, index) =>
|
|
212
|
+
fixture.storage.writeMemory("fact", `historical scan candidate ${index}`, {
|
|
213
|
+
source: "test",
|
|
214
|
+
}),
|
|
215
|
+
),
|
|
216
|
+
);
|
|
217
|
+
const memories = (await fixture.storage.readAllMemories()).filter((memory) =>
|
|
218
|
+
memory.content.startsWith("historical scan candidate "),
|
|
219
|
+
);
|
|
220
|
+
assert.equal(memories.length, futureCount + 1);
|
|
221
|
+
const candidatePaths: string[] = [];
|
|
222
|
+
for (let index = 0; index <= futureCount; index += 1) {
|
|
223
|
+
const memory = memories.find(
|
|
224
|
+
(candidate) => candidate.content === `historical scan candidate ${index}`,
|
|
225
|
+
);
|
|
226
|
+
assert.ok(memory);
|
|
227
|
+
candidatePaths.push(path.relative(fixture.dir, memory.path).split(path.sep).join("/"));
|
|
228
|
+
assert.equal(
|
|
229
|
+
await fixture.storage.writeMemoryFrontmatter(memory, {
|
|
230
|
+
status: "active",
|
|
231
|
+
valid_at: index === futureCount ? HISTORICAL_VALID_AT : "2026-02-01T00:00:00.000Z",
|
|
232
|
+
}),
|
|
233
|
+
true,
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
const validPath = candidatePaths[futureCount];
|
|
237
|
+
assert.ok(validPath);
|
|
238
|
+
return { candidatePaths, validPath };
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function makeHistoricalCapGraphIndex(
|
|
242
|
+
fixture: NamespaceFixture,
|
|
243
|
+
candidatePaths: string[],
|
|
244
|
+
): GraphIndex {
|
|
245
|
+
return {
|
|
246
|
+
spreadingActivation: async () =>
|
|
247
|
+
candidatePaths.map((candidatePath, index) => ({
|
|
248
|
+
path: candidatePath,
|
|
249
|
+
score: 1 - index / 1000,
|
|
250
|
+
seed: fixture.seedPath,
|
|
251
|
+
hopDepth: 1,
|
|
252
|
+
decayedWeight: 1,
|
|
253
|
+
graphType: "entity" as const,
|
|
254
|
+
edgeConfidence: 1,
|
|
255
|
+
activationPath: {
|
|
256
|
+
nodeIds: [fixture.seedPath, candidatePath],
|
|
257
|
+
edgeConfidences: [1],
|
|
258
|
+
graphTypes: ["entity"],
|
|
259
|
+
},
|
|
260
|
+
})),
|
|
261
|
+
} as unknown as GraphIndex;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
test("real coordinator demotes stale paths before the namespace cap and repeats deterministically", async () => {
|
|
265
|
+
const root = await mkdtemp(path.join(os.tmpdir(), "remnic-graph-path-coordinator-"));
|
|
266
|
+
try {
|
|
267
|
+
const fixture = await createNamespace(root, "default", "superseded", true);
|
|
268
|
+
const cleanFixture = await createNamespace(root, "clean", "active");
|
|
269
|
+
const fixtures = new Map([[fixture.name, fixture], [cleanFixture.name, cleanFixture]]);
|
|
270
|
+
const coordinator = makeCoordinator(makeConfig(), fixtures);
|
|
271
|
+
const first = await expand(coordinator, [seedResult(fixture)], [fixture.name]);
|
|
272
|
+
const second = await expand(coordinator, [seedResult(fixture)], [fixture.name]);
|
|
273
|
+
assert.equal(first.merged.length, 4);
|
|
274
|
+
assert.equal(first.expandedPaths.length, 3);
|
|
275
|
+
assert.equal(first.expandedPaths[0]?.path.endsWith(fixture.cleanPath), true);
|
|
276
|
+
assert.equal(first.expandedPaths[1]?.path.endsWith(fixture.secondCleanPath ?? ""), true);
|
|
277
|
+
assert.equal(first.expandedPaths[2]?.path.endsWith(fixture.stalePath), true);
|
|
278
|
+
assert.equal(first.expandedPaths.slice(0, 2).some((entry) => entry.path.endsWith(fixture.stalePath)), false);
|
|
279
|
+
assert.deepEqual(first.merged, second.merged);
|
|
280
|
+
assert.deepEqual(first.expandedPaths, second.expandedPaths);
|
|
281
|
+
const capped = await expand(
|
|
282
|
+
coordinator,
|
|
283
|
+
[seedResult(fixture), seedResult(cleanFixture)],
|
|
284
|
+
[fixture.name, cleanFixture.name],
|
|
285
|
+
);
|
|
286
|
+
assert.ok(capped.merged.some((item) => item.namespace === cleanFixture.name));
|
|
287
|
+
} finally {
|
|
288
|
+
await rm(root, { recursive: true, force: true });
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
test("retains a superseded graph candidate at its historical asOf but not now", async () => {
|
|
292
|
+
const root = await mkdtemp(path.join(os.tmpdir(), "remnic-graph-path-historical-candidate-"));
|
|
293
|
+
try {
|
|
294
|
+
const fixture = await createNamespace(root, "default");
|
|
295
|
+
const candidate = (await fixture.storage.readAllMemories()).find((memory) =>
|
|
296
|
+
memory.path.endsWith(fixture.cleanPath),
|
|
297
|
+
);
|
|
298
|
+
assert.ok(candidate);
|
|
299
|
+
assert.equal(
|
|
300
|
+
await fixture.storage.writeMemoryFrontmatter(candidate, {
|
|
301
|
+
status: "superseded",
|
|
302
|
+
valid_at: "2025-01-01T00:00:00.000Z",
|
|
303
|
+
invalid_at: "2026-06-01T00:00:00.000Z",
|
|
304
|
+
}),
|
|
305
|
+
true,
|
|
306
|
+
);
|
|
307
|
+
|
|
308
|
+
const fixtures = new Map([[fixture.name, fixture]]);
|
|
309
|
+
const coordinator = makeCoordinator(makeConfig(), fixtures);
|
|
310
|
+
const historical = await expand(coordinator, [seedResult(fixture)], [fixture.name]);
|
|
311
|
+
const current = await coordinator.expandResultsViaGraph({
|
|
312
|
+
memoryResults: [seedResult(fixture)],
|
|
313
|
+
recallNamespaces: [fixture.name],
|
|
314
|
+
recallResultLimit: 1,
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
assert.equal(
|
|
318
|
+
historical.expandedPaths.some((entry) => entry.path.endsWith(fixture.cleanPath)),
|
|
319
|
+
true,
|
|
320
|
+
);
|
|
321
|
+
assert.equal(
|
|
322
|
+
current.expandedPaths.some((entry) => entry.path.endsWith(fixture.cleanPath)),
|
|
323
|
+
false,
|
|
324
|
+
);
|
|
325
|
+
} finally {
|
|
326
|
+
await rm(root, { recursive: true, force: true });
|
|
327
|
+
}
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
test("fills the current-time graph cap after rejecting expired candidates", async () => {
|
|
331
|
+
const root = await mkdtemp(path.join(os.tmpdir(), "remnic-graph-path-current-cap-"));
|
|
332
|
+
try {
|
|
333
|
+
const fixture = await createNamespace(root, "default");
|
|
334
|
+
const expired = [];
|
|
335
|
+
for (let index = 0; index < 8; index += 1) {
|
|
336
|
+
await fixture.storage.writeMemory("fact", `expired graph candidate ${index}`, {
|
|
337
|
+
source: "test",
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
const memories = (await fixture.storage.readAllMemories()).filter((memory) =>
|
|
341
|
+
memory.content.startsWith("expired graph candidate "),
|
|
342
|
+
);
|
|
343
|
+
assert.equal(memories.length, 8);
|
|
344
|
+
for (const memory of memories) {
|
|
345
|
+
const relativePath = path.relative(fixture.dir, memory.path).split(path.sep).join("/");
|
|
346
|
+
expired.push(relativePath);
|
|
347
|
+
assert.equal(
|
|
348
|
+
await fixture.storage.writeMemoryFrontmatter(memory, {
|
|
349
|
+
status: "active",
|
|
350
|
+
invalid_at: "2000-01-01T00:00:00.000Z",
|
|
351
|
+
}),
|
|
352
|
+
true,
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
const candidates = [
|
|
356
|
+
...expired.map((candidatePath, index) => ({
|
|
357
|
+
path: candidatePath,
|
|
358
|
+
score: 1 - index / 1000,
|
|
359
|
+
})),
|
|
360
|
+
{
|
|
361
|
+
path: fixture.cleanPath,
|
|
362
|
+
score: 0.5,
|
|
363
|
+
},
|
|
364
|
+
];
|
|
365
|
+
const fakeIndex = {
|
|
366
|
+
spreadingActivation: async () =>
|
|
367
|
+
candidates.map((candidate) => ({
|
|
368
|
+
...candidate,
|
|
369
|
+
seed: fixture.seedPath,
|
|
370
|
+
hopDepth: 1,
|
|
371
|
+
decayedWeight: candidate.score,
|
|
372
|
+
graphType: "entity" as const,
|
|
373
|
+
edgeConfidence: 1,
|
|
374
|
+
activationPath: {
|
|
375
|
+
nodeIds: [fixture.seedPath, candidate.path],
|
|
376
|
+
edgeConfidences: [1],
|
|
377
|
+
graphTypes: ["entity"],
|
|
378
|
+
},
|
|
379
|
+
})),
|
|
380
|
+
} as unknown as GraphIndex;
|
|
381
|
+
const coordinator = makeCoordinator(
|
|
382
|
+
makeConfig({
|
|
383
|
+
temporalBiTemporal: true,
|
|
384
|
+
temporalExpiredInInjection: false,
|
|
385
|
+
}),
|
|
386
|
+
new Map([[fixture.name, fixture]]),
|
|
387
|
+
undefined,
|
|
388
|
+
new Map([[fixture.dir, fakeIndex]]),
|
|
389
|
+
);
|
|
390
|
+
const result = await coordinator.expandResultsViaGraph({
|
|
391
|
+
memoryResults: [seedResult(fixture)],
|
|
392
|
+
recallNamespaces: [fixture.name],
|
|
393
|
+
recallResultLimit: 1,
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
assert.deepEqual(
|
|
397
|
+
result.expandedPaths.map((entry) => entry.path),
|
|
398
|
+
[path.join(fixture.dir, fixture.cleanPath)],
|
|
399
|
+
);
|
|
400
|
+
} finally {
|
|
401
|
+
await rm(root, { recursive: true, force: true });
|
|
402
|
+
}
|
|
403
|
+
});
|
|
404
|
+
test("excludes an archived graph candidate from historical expansion", async () => {
|
|
405
|
+
const root = await mkdtemp(path.join(os.tmpdir(), "remnic-graph-path-historical-archived-"));
|
|
406
|
+
try {
|
|
407
|
+
const fixture = await createNamespace(root, "default");
|
|
408
|
+
const candidate = (await fixture.storage.readAllMemories()).find((memory) =>
|
|
409
|
+
memory.path.endsWith(fixture.cleanPath),
|
|
410
|
+
);
|
|
411
|
+
assert.ok(candidate);
|
|
412
|
+
assert.equal(
|
|
413
|
+
await fixture.storage.writeMemoryFrontmatter(candidate, {
|
|
414
|
+
status: "archived",
|
|
415
|
+
valid_at: "2025-01-01T00:00:00.000Z",
|
|
416
|
+
invalid_at: "2026-06-01T00:00:00.000Z",
|
|
417
|
+
}),
|
|
418
|
+
true,
|
|
419
|
+
);
|
|
420
|
+
|
|
421
|
+
const result = await expand(
|
|
422
|
+
makeCoordinator(makeConfig(), new Map([[fixture.name, fixture]])),
|
|
423
|
+
[seedResult(fixture)],
|
|
424
|
+
[fixture.name],
|
|
425
|
+
);
|
|
426
|
+
|
|
427
|
+
assert.equal(
|
|
428
|
+
result.expandedPaths.some((entry) => entry.path.endsWith(fixture.cleanPath)),
|
|
429
|
+
false,
|
|
430
|
+
);
|
|
431
|
+
} finally {
|
|
432
|
+
await rm(root, { recursive: true, force: true });
|
|
433
|
+
}
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
test("loads valid_at onto active intermediate path state before historical scoring", async () => {
|
|
437
|
+
const root = await mkdtemp(path.join(os.tmpdir(), "remnic-graph-path-valid-at-"));
|
|
438
|
+
try {
|
|
439
|
+
const fixture = await createNamespace(root, "default");
|
|
440
|
+
const intermediate = (await fixture.storage.readAllMemories()).find((memory) =>
|
|
441
|
+
memory.path.endsWith(fixture.intermediatePath),
|
|
442
|
+
);
|
|
443
|
+
assert.ok(intermediate);
|
|
444
|
+
assert.equal(
|
|
445
|
+
await fixture.storage.writeMemoryFrontmatter(intermediate, {
|
|
446
|
+
valid_at: "2025-01-01T00:00:00.000Z",
|
|
447
|
+
}),
|
|
448
|
+
true,
|
|
449
|
+
);
|
|
450
|
+
|
|
451
|
+
const result = await expand(
|
|
452
|
+
makeCoordinator(makeConfig(), new Map([[fixture.name, fixture]])),
|
|
453
|
+
[seedResult(fixture)],
|
|
454
|
+
[fixture.name],
|
|
455
|
+
);
|
|
456
|
+
const stale = result.expandedPaths.find((entry) => entry.path.endsWith(fixture.stalePath));
|
|
457
|
+
assert.ok(stale);
|
|
458
|
+
assert.equal(stale.pathPenaltyApplied, false);
|
|
459
|
+
} finally {
|
|
460
|
+
await rm(root, { recursive: true, force: true });
|
|
461
|
+
}
|
|
462
|
+
});
|
|
463
|
+
|
|
464
|
+
test("retains an active cold candidate in disabled and enabled graph scoring modes", async () => {
|
|
465
|
+
const root = await mkdtemp(path.join(os.tmpdir(), "remnic-graph-path-cold-candidate-"));
|
|
466
|
+
try {
|
|
467
|
+
const fixture = await createNamespace(root, "default");
|
|
468
|
+
const cleanMemory = (await fixture.storage.readAllMemories()).find((memory) =>
|
|
469
|
+
memory.path.endsWith(fixture.cleanPath),
|
|
470
|
+
);
|
|
471
|
+
assert.ok(cleanMemory);
|
|
472
|
+
const moved = await fixture.storage.migrateMemoryToTier(cleanMemory, "cold");
|
|
473
|
+
assert.equal(moved.changed, true);
|
|
474
|
+
const coldPath = path.relative(fixture.dir, moved.targetPath).split(path.sep).join("/");
|
|
475
|
+
await appendEdge(fixture.dir, fixture.seedPath, coldPath, 1);
|
|
476
|
+
|
|
477
|
+
const fixtures = new Map([[fixture.name, fixture]]);
|
|
478
|
+
const disabledReads = { count: 0 };
|
|
479
|
+
const disabled = await expand(
|
|
480
|
+
makeCoordinator(makeConfig({ graphPathScoring: { enabled: false } }), fixtures, disabledReads),
|
|
481
|
+
[seedResult(fixture)],
|
|
482
|
+
[fixture.name],
|
|
483
|
+
);
|
|
484
|
+
assert.equal(disabledReads.count, 0);
|
|
485
|
+
|
|
486
|
+
const enabledReads = { count: 0 };
|
|
487
|
+
const enabled = await expand(
|
|
488
|
+
makeCoordinator(makeConfig(), fixtures, enabledReads),
|
|
489
|
+
[seedResult(fixture)],
|
|
490
|
+
[fixture.name],
|
|
491
|
+
);
|
|
492
|
+
assert.equal(enabledReads.count, 0);
|
|
493
|
+
|
|
494
|
+
const disabledCandidate = disabled.merged.find((item) => item.path === moved.targetPath);
|
|
495
|
+
const enabledCandidate = enabled.merged.find((item) => item.path === moved.targetPath);
|
|
496
|
+
assert.ok(disabledCandidate);
|
|
497
|
+
assert.ok(enabledCandidate);
|
|
498
|
+
assert.equal(disabledCandidate.namespace, fixture.name);
|
|
499
|
+
assert.equal(enabledCandidate.namespace, fixture.name);
|
|
500
|
+
assert.deepEqual(
|
|
501
|
+
{
|
|
502
|
+
docid: enabledCandidate.docid,
|
|
503
|
+
path: enabledCandidate.path,
|
|
504
|
+
namespace: enabledCandidate.namespace,
|
|
505
|
+
snippet: enabledCandidate.snippet,
|
|
506
|
+
},
|
|
507
|
+
{
|
|
508
|
+
docid: disabledCandidate.docid,
|
|
509
|
+
path: disabledCandidate.path,
|
|
510
|
+
namespace: disabledCandidate.namespace,
|
|
511
|
+
snippet: disabledCandidate.snippet,
|
|
512
|
+
},
|
|
513
|
+
);
|
|
514
|
+
assert.equal(enabledCandidate.score, disabledCandidate.score);
|
|
515
|
+
assert.equal("pathPenaltyApplied" in disabledCandidate, false);
|
|
516
|
+
assert.equal(enabledCandidate.pathPenaltyApplied, false);
|
|
517
|
+
assert.deepEqual(enabledCandidate.pathNodeIds, [fixture.seedPath, coldPath]);
|
|
518
|
+
} finally {
|
|
519
|
+
await rm(root, { recursive: true, force: true });
|
|
520
|
+
}
|
|
521
|
+
});
|
|
522
|
+
|
|
523
|
+
test("real coordinator demotes a path through an archived intermediate", async () => {
|
|
524
|
+
const root = await mkdtemp(path.join(os.tmpdir(), "remnic-graph-path-archive-"));
|
|
525
|
+
try {
|
|
526
|
+
const fixture = await createNamespace(root, "default");
|
|
527
|
+
const intermediate = (await fixture.storage.readAllMemories()).find((memory) =>
|
|
528
|
+
memory.path.endsWith(fixture.intermediatePath),
|
|
529
|
+
);
|
|
530
|
+
assert.ok(intermediate);
|
|
531
|
+
const intermediateId = intermediate.frontmatter.id;
|
|
532
|
+
assert.ok(await fixture.storage.archiveMemory(intermediate));
|
|
533
|
+
const archived = await fixture.storage.readArchivedMemories();
|
|
534
|
+
const archivedIntermediate = archived.find((memory) => memory.frontmatter.id === intermediateId);
|
|
535
|
+
assert.ok(archivedIntermediate);
|
|
536
|
+
assert.equal(path.basename(archivedIntermediate.path, ".md"), intermediateId);
|
|
537
|
+
const config = makeConfig();
|
|
538
|
+
const coordinator = makeCoordinator(config, new Map([[fixture.name, fixture]]));
|
|
539
|
+
const result = await expand(coordinator, [seedResult(fixture)], [fixture.name]);
|
|
540
|
+
const stale = result.expandedPaths.find((entry) => entry.path.endsWith(fixture.stalePath));
|
|
541
|
+
const clean = result.expandedPaths.find((entry) => entry.path.endsWith(fixture.cleanPath));
|
|
542
|
+
assert.ok(clean);
|
|
543
|
+
assert.ok(stale);
|
|
544
|
+
assert.deepEqual(stale.pathNodeIds, [fixture.seedPath, fixture.intermediatePath, fixture.stalePath]);
|
|
545
|
+
const activation = await new GraphIndex(
|
|
546
|
+
fixture.dir,
|
|
547
|
+
config as unknown as GraphConfig,
|
|
548
|
+
).spreadingActivation([fixture.seedPath], config.maxGraphTraversalSteps);
|
|
549
|
+
const expectedScore = (candidatePath: string, penalty: number): number => {
|
|
550
|
+
const candidate = activation.find((item) => item.path === candidatePath);
|
|
551
|
+
assert.ok(candidate);
|
|
552
|
+
return (
|
|
553
|
+
blendGraphExpandedRecallScore({
|
|
554
|
+
graphActivationScore: candidate.score,
|
|
555
|
+
seedRecallScore: 0.5,
|
|
556
|
+
activationWeight: config.graphExpansionActivationWeight,
|
|
557
|
+
blendMin: config.graphExpansionBlendMin,
|
|
558
|
+
blendMax: config.graphExpansionBlendMax,
|
|
559
|
+
}) * penalty
|
|
560
|
+
);
|
|
561
|
+
};
|
|
562
|
+
assert.equal(clean.score, expectedScore(fixture.cleanPath, 1));
|
|
563
|
+
assert.equal(stale.score, expectedScore(fixture.stalePath, config.graphPathScoring.invalidNodePenalty));
|
|
564
|
+
assert.equal(stale.pathPenaltyApplied, true);
|
|
565
|
+
} finally {
|
|
566
|
+
await rm(root, { recursive: true, force: true });
|
|
567
|
+
}
|
|
568
|
+
});
|
|
569
|
+
|
|
570
|
+
test("disabled boolean and parsed string false preserve output and skip corpus reads", async () => {
|
|
571
|
+
const root = await mkdtemp(path.join(os.tmpdir(), "remnic-graph-path-disabled-"));
|
|
572
|
+
try {
|
|
573
|
+
const fixture = await createNamespace(root, "default", "superseded");
|
|
574
|
+
const fixtures = new Map([[fixture.name, fixture]]);
|
|
575
|
+
const reads = { count: 0 };
|
|
576
|
+
const falseConfig = makeConfig({ graphPathScoring: { enabled: false } });
|
|
577
|
+
const stringConfig = makeConfig({ graphPathScoring: { enabled: "false" } });
|
|
578
|
+
const oldActivation = await new GraphIndex(
|
|
579
|
+
fixture.dir,
|
|
580
|
+
falseConfig as unknown as GraphConfig,
|
|
581
|
+
).spreadingActivation([fixture.seedPath], falseConfig.maxGraphTraversalSteps);
|
|
582
|
+
const expectedCandidate = oldActivation.find(
|
|
583
|
+
(candidate) => candidate.path === fixture.cleanPath,
|
|
584
|
+
);
|
|
585
|
+
assert.ok(expectedCandidate);
|
|
586
|
+
const expectedPath = path.join(fixture.dir, expectedCandidate.path);
|
|
587
|
+
const expectedScore = blendGraphExpandedRecallScore({
|
|
588
|
+
graphActivationScore: expectedCandidate.score,
|
|
589
|
+
seedRecallScore: 0.5,
|
|
590
|
+
activationWeight: falseConfig.graphExpansionActivationWeight,
|
|
591
|
+
blendMin: falseConfig.graphExpansionBlendMin,
|
|
592
|
+
blendMax: falseConfig.graphExpansionBlendMax,
|
|
593
|
+
});
|
|
594
|
+
const falseRun = await expand(makeCoordinator(falseConfig, fixtures, reads), [seedResult(fixture)], [fixture.name]);
|
|
595
|
+
const stringRun = await expand(makeCoordinator(stringConfig, fixtures, reads), [seedResult(fixture)], [fixture.name]);
|
|
596
|
+
const retained = falseRun.merged.find((item) => item.path === expectedPath);
|
|
597
|
+
assert.ok(retained);
|
|
598
|
+
assert.equal(retained.path, expectedPath);
|
|
599
|
+
assert.equal(retained.score, expectedScore);
|
|
600
|
+
assert.deepEqual(falseRun, stringRun);
|
|
601
|
+
assert.equal(reads.count, 0);
|
|
602
|
+
for (const item of [...falseRun.merged, ...falseRun.expandedPaths]) {
|
|
603
|
+
assert.equal("pathNodeIds" in item, false);
|
|
604
|
+
assert.equal("pathPenaltyApplied" in item, false);
|
|
605
|
+
}
|
|
606
|
+
} finally {
|
|
607
|
+
await rm(root, { recursive: true, force: true });
|
|
608
|
+
}
|
|
609
|
+
});
|
|
610
|
+
test("disabled graph path scoring excludes activation results that match seeds", async () => {
|
|
611
|
+
const root = await mkdtemp(path.join(os.tmpdir(), "remnic-graph-path-seed-exclusion-"));
|
|
612
|
+
try {
|
|
613
|
+
const fixture = await createNamespace(root, "default");
|
|
614
|
+
const fakeIndex = {
|
|
615
|
+
spreadingActivation: async () => [
|
|
616
|
+
{
|
|
617
|
+
path: fixture.seedPath,
|
|
618
|
+
score: 1,
|
|
619
|
+
seed: fixture.seedPath,
|
|
620
|
+
hopDepth: 0,
|
|
621
|
+
decayedWeight: 1,
|
|
622
|
+
graphType: "entity" as const,
|
|
623
|
+
edgeConfidence: 1,
|
|
624
|
+
},
|
|
625
|
+
{
|
|
626
|
+
path: fixture.cleanPath,
|
|
627
|
+
score: 0.8,
|
|
628
|
+
seed: fixture.seedPath,
|
|
629
|
+
hopDepth: 1,
|
|
630
|
+
decayedWeight: 1,
|
|
631
|
+
graphType: "entity" as const,
|
|
632
|
+
edgeConfidence: 1,
|
|
633
|
+
},
|
|
634
|
+
],
|
|
635
|
+
} as unknown as GraphIndex;
|
|
636
|
+
const coordinator = makeCoordinator(
|
|
637
|
+
makeConfig({ graphPathScoring: { enabled: false } }),
|
|
638
|
+
new Map([[fixture.name, fixture]]),
|
|
639
|
+
undefined,
|
|
640
|
+
new Map([[fixture.dir, fakeIndex]]),
|
|
641
|
+
);
|
|
642
|
+
|
|
643
|
+
const result = await expand(coordinator, [seedResult(fixture)], [fixture.name]);
|
|
644
|
+
|
|
645
|
+
assert.deepEqual(
|
|
646
|
+
result.expandedPaths.map((entry) => entry.path),
|
|
647
|
+
[path.join(fixture.dir, fixture.cleanPath)],
|
|
648
|
+
);
|
|
649
|
+
assert.equal(
|
|
650
|
+
result.merged.filter((item) => item.path.endsWith(fixture.seedPath)).length,
|
|
651
|
+
1,
|
|
652
|
+
);
|
|
653
|
+
} finally {
|
|
654
|
+
await rm(root, { recursive: true, force: true });
|
|
655
|
+
}
|
|
656
|
+
});
|
|
657
|
+
|
|
658
|
+
test("missing memory status defaults to active for path scoring", async () => {
|
|
659
|
+
const root = await mkdtemp(path.join(os.tmpdir(), "remnic-graph-path-status-"));
|
|
660
|
+
try {
|
|
661
|
+
const fixture = await createNamespace(root, "default");
|
|
662
|
+
const originalReadMemoryByPath = fixture.storage.readMemoryByPath.bind(fixture.storage);
|
|
663
|
+
fixture.storage.readMemoryByPath = async (filePath: string) => {
|
|
664
|
+
const memory = await originalReadMemoryByPath(filePath);
|
|
665
|
+
if (!memory || !memory.path.endsWith(fixture.intermediatePath)) return memory;
|
|
666
|
+
return {
|
|
667
|
+
...memory,
|
|
668
|
+
frontmatter: {
|
|
669
|
+
...memory.frontmatter,
|
|
670
|
+
status: undefined,
|
|
671
|
+
invalid_at: AS_OF,
|
|
672
|
+
},
|
|
673
|
+
};
|
|
674
|
+
};
|
|
675
|
+
const result = await expand(
|
|
676
|
+
makeCoordinator(makeConfig(), new Map([[fixture.name, fixture]])),
|
|
677
|
+
[seedResult(fixture)],
|
|
678
|
+
[fixture.name],
|
|
679
|
+
);
|
|
680
|
+
const stale = result.expandedPaths.find((entry) => entry.path.endsWith(fixture.stalePath));
|
|
681
|
+
assert.equal(stale?.pathPenaltyApplied, true);
|
|
682
|
+
} finally {
|
|
683
|
+
await rm(root, { recursive: true, force: true });
|
|
684
|
+
}
|
|
685
|
+
});
|
|
686
|
+
test("malformed intermediate status remains neutral for path scoring", async () => {
|
|
687
|
+
const root = await mkdtemp(path.join(os.tmpdir(), "remnic-graph-path-malformed-status-"));
|
|
688
|
+
try {
|
|
689
|
+
const fixture = await createNamespace(root, "default", "superseded");
|
|
690
|
+
const originalReadMemoryByPath = fixture.storage.readMemoryByPath.bind(fixture.storage);
|
|
691
|
+
fixture.storage.readMemoryByPath = async (filePath: string) => {
|
|
692
|
+
const memory = await originalReadMemoryByPath(filePath);
|
|
693
|
+
if (!memory || !memory.path.endsWith(fixture.intermediatePath)) return memory;
|
|
694
|
+
return {
|
|
695
|
+
...memory,
|
|
696
|
+
frontmatter: {
|
|
697
|
+
...memory.frontmatter,
|
|
698
|
+
status: "malformed" as never,
|
|
699
|
+
invalid_at: AS_OF,
|
|
700
|
+
},
|
|
701
|
+
};
|
|
702
|
+
};
|
|
703
|
+
const result = await expand(
|
|
704
|
+
makeCoordinator(makeConfig(), new Map([[fixture.name, fixture]])),
|
|
705
|
+
[seedResult(fixture)],
|
|
706
|
+
[fixture.name],
|
|
707
|
+
);
|
|
708
|
+
const stale = result.expandedPaths.find((entry) => entry.path.endsWith(fixture.stalePath));
|
|
709
|
+
assert.equal(stale?.pathPenaltyApplied, false);
|
|
710
|
+
} finally {
|
|
711
|
+
await rm(root, { recursive: true, force: true });
|
|
712
|
+
}
|
|
713
|
+
});
|
|
714
|
+
|
|
715
|
+
test("memoizes shared missing graph path state within one namespace expansion", async () => {
|
|
716
|
+
const root = await mkdtemp(path.join(os.tmpdir(), "remnic-graph-path-state-cache-"));
|
|
717
|
+
try {
|
|
718
|
+
const fixture = await createNamespace(root, "default");
|
|
719
|
+
await fixture.storage.writeMemory("fact", "default cache first", {
|
|
720
|
+
source: "test",
|
|
721
|
+
validAt: HISTORICAL_VALID_AT,
|
|
722
|
+
});
|
|
723
|
+
await fixture.storage.writeMemory("fact", "default cache second", {
|
|
724
|
+
source: "test",
|
|
725
|
+
validAt: HISTORICAL_VALID_AT,
|
|
726
|
+
});
|
|
727
|
+
const memories = await fixture.storage.readAllMemories();
|
|
728
|
+
const firstMemory = memories.find((memory) => memory.content === "default cache first");
|
|
729
|
+
const secondMemory = memories.find((memory) => memory.content === "default cache second");
|
|
730
|
+
assert.ok(firstMemory);
|
|
731
|
+
assert.ok(secondMemory);
|
|
732
|
+
const firstPath = path.relative(fixture.dir, firstMemory.path).split(path.sep).join("/");
|
|
733
|
+
const secondPath = path.relative(fixture.dir, secondMemory.path).split(path.sep).join("/");
|
|
734
|
+
const sharedMissingNode = "shared-missing-node";
|
|
735
|
+
await appendEdge(fixture.dir, fixture.seedPath, sharedMissingNode);
|
|
736
|
+
await appendEdge(fixture.dir, sharedMissingNode, firstPath);
|
|
737
|
+
await appendEdge(fixture.dir, sharedMissingNode, secondPath);
|
|
738
|
+
|
|
739
|
+
const missingPath = path.join(fixture.dir, sharedMissingNode);
|
|
740
|
+
let missingReads = 0;
|
|
741
|
+
const originalReadMemoryByPath = fixture.storage.readMemoryByPath.bind(fixture.storage);
|
|
742
|
+
fixture.storage.readMemoryByPath = async (filePath: string) => {
|
|
743
|
+
if (filePath === missingPath) missingReads += 1;
|
|
744
|
+
return originalReadMemoryByPath(filePath);
|
|
745
|
+
};
|
|
746
|
+
const result = await expand(
|
|
747
|
+
makeCoordinator(makeConfig(), new Map([[fixture.name, fixture]])),
|
|
748
|
+
[seedResult(fixture)],
|
|
749
|
+
[fixture.name],
|
|
750
|
+
2,
|
|
751
|
+
);
|
|
752
|
+
assert.ok(result.expandedPaths.some((entry) => entry.path.endsWith(firstPath)));
|
|
753
|
+
assert.ok(result.expandedPaths.some((entry) => entry.path.endsWith(secondPath)));
|
|
754
|
+
assert.equal(missingReads, 1);
|
|
755
|
+
} finally {
|
|
756
|
+
await rm(root, { recursive: true, force: true });
|
|
757
|
+
}
|
|
758
|
+
});
|
|
759
|
+
test("provenance ids are optional while penalty boolean remains enabled", async () => {
|
|
760
|
+
const root = await mkdtemp(path.join(os.tmpdir(), "remnic-graph-path-provenance-"));
|
|
761
|
+
try {
|
|
762
|
+
const fixture = await createNamespace(root, "default", "superseded");
|
|
763
|
+
const fixtures = new Map([[fixture.name, fixture]]);
|
|
764
|
+
const withIds = await expand(makeCoordinator(makeConfig(), fixtures), [seedResult(fixture)], [fixture.name]);
|
|
765
|
+
const withoutIds = await expand(
|
|
766
|
+
makeCoordinator(makeConfig({ graphPathScoring: { enabled: true, includePathInProvenance: false } }), fixtures),
|
|
767
|
+
[seedResult(fixture)],
|
|
768
|
+
[fixture.name],
|
|
769
|
+
);
|
|
770
|
+
const withStale = withIds.merged.find((item) => item.path.endsWith(fixture.stalePath));
|
|
771
|
+
const withoutStale = withoutIds.merged.find((item) => item.path.endsWith(fixture.stalePath));
|
|
772
|
+
assert.equal(withStale?.pathPenaltyApplied, true);
|
|
773
|
+
assert.equal(withoutStale?.pathPenaltyApplied, true);
|
|
774
|
+
assert.ok(withStale?.pathNodeIds);
|
|
775
|
+
assert.equal("pathNodeIds" in (withoutStale ?? {}), false);
|
|
776
|
+
} finally {
|
|
777
|
+
await rm(root, { recursive: true, force: true });
|
|
778
|
+
}
|
|
779
|
+
});
|
|
780
|
+
|
|
781
|
+
test("namespace corpus state stays isolated for equal intermediate ids", async () => {
|
|
782
|
+
const root = await mkdtemp(path.join(os.tmpdir(), "remnic-graph-path-namespaces-"));
|
|
783
|
+
try {
|
|
784
|
+
const stale = await createNamespace(root, "stale", "superseded");
|
|
785
|
+
const clean = await createNamespace(root, "clean", "active");
|
|
786
|
+
const cleanIntermediate = (await clean.storage.readAllMemories()).find((memory) =>
|
|
787
|
+
memory.path.endsWith(clean.intermediatePath),
|
|
788
|
+
);
|
|
789
|
+
assert.ok(cleanIntermediate);
|
|
790
|
+
assert.equal(
|
|
791
|
+
await clean.storage.writeMemoryFrontmatter(cleanIntermediate, {
|
|
792
|
+
valid_at: "2025-01-01T00:00:00.000Z",
|
|
793
|
+
}),
|
|
794
|
+
true,
|
|
795
|
+
);
|
|
796
|
+
const fixtures = new Map([stale, clean].map((item) => [item.name, item]));
|
|
797
|
+
const result = await expand(
|
|
798
|
+
makeCoordinator(makeConfig(), fixtures),
|
|
799
|
+
[seedResult(stale), seedResult(clean)],
|
|
800
|
+
[stale.name, clean.name],
|
|
801
|
+
);
|
|
802
|
+
assert.equal(result.merged.find((item) => item.namespace === stale.name && item.path.endsWith(stale.stalePath))?.pathPenaltyApplied, true);
|
|
803
|
+
assert.equal(result.merged.find((item) => item.namespace === clean.name && item.path.endsWith(clean.stalePath))?.pathPenaltyApplied, false);
|
|
804
|
+
} finally {
|
|
805
|
+
await rm(root, { recursive: true, force: true });
|
|
806
|
+
}
|
|
807
|
+
});
|
|
808
|
+
|
|
809
|
+
test("malformed and absent coordinator asOf use a finite current instant", async () => {
|
|
810
|
+
const root = await mkdtemp(path.join(os.tmpdir(), "remnic-graph-path-asof-fallback-"));
|
|
811
|
+
try {
|
|
812
|
+
const fixture = await createNamespace(root, "default", "superseded");
|
|
813
|
+
const coordinator = makeCoordinator(
|
|
814
|
+
makeConfig(),
|
|
815
|
+
new Map([[fixture.name, fixture]]),
|
|
816
|
+
);
|
|
817
|
+
const common = {
|
|
818
|
+
memoryResults: [seedResult(fixture)],
|
|
819
|
+
recallNamespaces: [fixture.name],
|
|
820
|
+
recallResultLimit: 1,
|
|
821
|
+
};
|
|
822
|
+
const malformed = await coordinator.expandResultsViaGraph({
|
|
823
|
+
...common,
|
|
824
|
+
asOf: "not-a-date",
|
|
825
|
+
});
|
|
826
|
+
const absent = await coordinator.expandResultsViaGraph(common);
|
|
827
|
+
const malformedStale = malformed.expandedPaths.find((entry) =>
|
|
828
|
+
entry.path.endsWith(fixture.stalePath),
|
|
829
|
+
);
|
|
830
|
+
const absentStale = absent.expandedPaths.find((entry) =>
|
|
831
|
+
entry.path.endsWith(fixture.stalePath),
|
|
832
|
+
);
|
|
833
|
+
assert.equal(malformedStale?.pathPenaltyApplied, true);
|
|
834
|
+
assert.equal(absentStale?.pathPenaltyApplied, true);
|
|
835
|
+
} finally {
|
|
836
|
+
await rm(root, { recursive: true, force: true });
|
|
837
|
+
}
|
|
838
|
+
});
|
|
839
|
+
|
|
840
|
+
test("deadline stops remaining graph state reads", async () => {
|
|
841
|
+
const root = await mkdtemp(path.join(os.tmpdir(), "remnic-graph-path-deadline-"));
|
|
842
|
+
try {
|
|
843
|
+
const fixture = await createNamespace(root, "default", "superseded");
|
|
844
|
+
const reads: string[] = [];
|
|
845
|
+
const originalNow = Date.now;
|
|
846
|
+
let now = 1_000;
|
|
847
|
+
try {
|
|
848
|
+
Date.now = () => now;
|
|
849
|
+
const originalReadMemoryByPath = fixture.storage.readMemoryByPath.bind(fixture.storage);
|
|
850
|
+
fixture.storage.readMemoryByPath = async (filePath: string) => {
|
|
851
|
+
reads.push(filePath);
|
|
852
|
+
if (reads.length === 1) now += 200;
|
|
853
|
+
return originalReadMemoryByPath(filePath);
|
|
854
|
+
};
|
|
855
|
+
const coordinator = makeCoordinator(
|
|
856
|
+
makeConfig(),
|
|
857
|
+
new Map([[fixture.name, fixture]]),
|
|
858
|
+
);
|
|
859
|
+
await coordinator.expandResultsViaGraph({
|
|
860
|
+
memoryResults: [seedResult(fixture)],
|
|
861
|
+
recallNamespaces: [fixture.name],
|
|
862
|
+
recallResultLimit: 1,
|
|
863
|
+
asOf: AS_OF,
|
|
864
|
+
deadlineAtMs: now + 100,
|
|
865
|
+
});
|
|
866
|
+
} finally {
|
|
867
|
+
Date.now = originalNow;
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
assert.equal(reads.length, 1);
|
|
871
|
+
} finally {
|
|
872
|
+
await rm(root, { recursive: true, force: true });
|
|
873
|
+
}
|
|
874
|
+
});
|
|
875
|
+
test("real coordinator demotes a path through a cold intermediate", async () => {
|
|
876
|
+
const root = await mkdtemp(path.join(os.tmpdir(), "remnic-graph-path-cold-intermediate-"));
|
|
877
|
+
try {
|
|
878
|
+
const fixture = await createNamespace(root, "default", "superseded");
|
|
879
|
+
const intermediate = (await fixture.storage.readAllMemories()).find((memory) =>
|
|
880
|
+
memory.path.endsWith(fixture.intermediatePath),
|
|
881
|
+
);
|
|
882
|
+
assert.ok(intermediate);
|
|
883
|
+
const moved = await fixture.storage.migrateMemoryToTier(intermediate, "cold");
|
|
884
|
+
assert.equal(moved.changed, true);
|
|
885
|
+
const coordinator = makeCoordinator(
|
|
886
|
+
makeConfig(),
|
|
887
|
+
new Map([[fixture.name, fixture]]),
|
|
888
|
+
);
|
|
889
|
+
const result = await expand(
|
|
890
|
+
coordinator,
|
|
891
|
+
[seedResult(fixture)],
|
|
892
|
+
[fixture.name],
|
|
893
|
+
);
|
|
894
|
+
const stale = result.expandedPaths.find((entry) =>
|
|
895
|
+
entry.path.endsWith(fixture.stalePath),
|
|
896
|
+
);
|
|
897
|
+
assert.equal(stale?.pathPenaltyApplied, true);
|
|
898
|
+
} finally {
|
|
899
|
+
await rm(root, { recursive: true, force: true });
|
|
900
|
+
}
|
|
901
|
+
});
|
|
902
|
+
|
|
903
|
+
test("rejects traversal graph nodes before reading outside storage", async () => {
|
|
904
|
+
const root = await mkdtemp(path.join(os.tmpdir(), "remnic-graph-path-traversal-"));
|
|
905
|
+
try {
|
|
906
|
+
const fixture = await createNamespace(root, "default");
|
|
907
|
+
const outsidePath = path.join(root, "outside.md");
|
|
908
|
+
await writeFile(
|
|
909
|
+
outsidePath,
|
|
910
|
+
"---\nid: outside\ncategory: fact\ncreated: 2026-01-01T00:00:00.000Z\nupdated: 2026-01-01T00:00:00.000Z\nsource: test\nconfidence: 0.9\nstatus: active\n---\n\noutside\n",
|
|
911
|
+
"utf8",
|
|
912
|
+
);
|
|
913
|
+
await appendEdge(fixture.dir, fixture.seedPath, "../outside.md");
|
|
914
|
+
const reads: string[] = [];
|
|
915
|
+
const originalReadMemoryByPath = fixture.storage.readMemoryByPath.bind(fixture.storage);
|
|
916
|
+
fixture.storage.readMemoryByPath = async (filePath: string) => {
|
|
917
|
+
reads.push(filePath);
|
|
918
|
+
return originalReadMemoryByPath(filePath);
|
|
919
|
+
};
|
|
920
|
+
const result = await expand(
|
|
921
|
+
makeCoordinator(makeConfig(), new Map([[fixture.name, fixture]])),
|
|
922
|
+
[seedResult(fixture)],
|
|
923
|
+
[fixture.name],
|
|
924
|
+
);
|
|
925
|
+
assert.equal(result.expandedPaths.some((entry) => entry.path === outsidePath), false);
|
|
926
|
+
assert.equal(reads.includes(outsidePath), false);
|
|
927
|
+
} finally {
|
|
928
|
+
await rm(root, { recursive: true, force: true });
|
|
929
|
+
}
|
|
930
|
+
});
|
|
931
|
+
|
|
932
|
+
test("rejects symlink graph nodes before reading outside storage", async () => {
|
|
933
|
+
if (process.platform === "win32") return;
|
|
934
|
+
const root = await mkdtemp(path.join(os.tmpdir(), "remnic-graph-path-symlink-"));
|
|
935
|
+
try {
|
|
936
|
+
const fixture = await createNamespace(root, "default");
|
|
937
|
+
const outsidePath = path.join(root, "outside.md");
|
|
938
|
+
await writeFile(
|
|
939
|
+
outsidePath,
|
|
940
|
+
"---\nid: outside\ncategory: fact\ncreated: 2026-01-01T00:00:00.000Z\nupdated: 2026-01-01T00:00:00.000Z\nsource: test\nconfidence: 0.9\nstatus: active\n---\n\noutside\n",
|
|
941
|
+
"utf8",
|
|
942
|
+
);
|
|
943
|
+
const linkPath = path.join(fixture.dir, "linked.md");
|
|
944
|
+
await symlink(outsidePath, linkPath);
|
|
945
|
+
await appendEdge(fixture.dir, fixture.seedPath, "linked.md");
|
|
946
|
+
const reads: string[] = [];
|
|
947
|
+
const originalReadMemoryByPath = fixture.storage.readMemoryByPath.bind(fixture.storage);
|
|
948
|
+
fixture.storage.readMemoryByPath = async (filePath: string) => {
|
|
949
|
+
reads.push(filePath);
|
|
950
|
+
return originalReadMemoryByPath(filePath);
|
|
951
|
+
};
|
|
952
|
+
const result = await expand(
|
|
953
|
+
makeCoordinator(makeConfig(), new Map([[fixture.name, fixture]])),
|
|
954
|
+
[seedResult(fixture)],
|
|
955
|
+
[fixture.name],
|
|
956
|
+
);
|
|
957
|
+
assert.equal(reads.includes(linkPath), false);
|
|
958
|
+
assert.equal(reads.includes(outsidePath), false);
|
|
959
|
+
} finally {
|
|
960
|
+
await rm(root, { recursive: true, force: true });
|
|
961
|
+
}
|
|
962
|
+
});
|
|
963
|
+
|
|
964
|
+
test("filters active candidates that start after historical asOf before the expansion cap", async () => {
|
|
965
|
+
const root = await mkdtemp(path.join(os.tmpdir(), "remnic-graph-path-historical-cap-"));
|
|
966
|
+
try {
|
|
967
|
+
const fixture = await createNamespace(root, "default");
|
|
968
|
+
await Promise.all(
|
|
969
|
+
Array.from({ length: 9 }, (_, index) =>
|
|
970
|
+
fixture.storage.writeMemory("fact", `historical cap candidate ${index}`, {
|
|
971
|
+
source: "test",
|
|
972
|
+
}),
|
|
973
|
+
),
|
|
974
|
+
);
|
|
975
|
+
const memories = (await fixture.storage.readAllMemories()).filter((memory) =>
|
|
976
|
+
memory.content.startsWith("historical cap candidate "),
|
|
977
|
+
);
|
|
978
|
+
assert.equal(memories.length, 9);
|
|
979
|
+
const candidatePaths: string[] = [];
|
|
980
|
+
for (const memory of memories) {
|
|
981
|
+
const relativePath = path.relative(fixture.dir, memory.path).split(path.sep).join("/");
|
|
982
|
+
candidatePaths.push(relativePath);
|
|
983
|
+
assert.equal(
|
|
984
|
+
await fixture.storage.writeMemoryFrontmatter(memory, {
|
|
985
|
+
status: "active",
|
|
986
|
+
valid_at: "2025-01-01T00:00:00.000Z",
|
|
987
|
+
}),
|
|
988
|
+
true,
|
|
989
|
+
);
|
|
990
|
+
}
|
|
991
|
+
const invalidPath = candidatePaths[0];
|
|
992
|
+
assert.ok(invalidPath);
|
|
993
|
+
const invalidMemory = memories[0];
|
|
994
|
+
assert.ok(invalidMemory);
|
|
995
|
+
assert.equal(
|
|
996
|
+
await fixture.storage.writeMemoryFrontmatter(invalidMemory, {
|
|
997
|
+
valid_at: "2026-02-01T00:00:00.000Z",
|
|
998
|
+
}),
|
|
999
|
+
true,
|
|
1000
|
+
);
|
|
1001
|
+
|
|
1002
|
+
const fakeIndex = {
|
|
1003
|
+
spreadingActivation: async () =>
|
|
1004
|
+
candidatePaths.map((candidatePath, index) => ({
|
|
1005
|
+
path: candidatePath,
|
|
1006
|
+
score: 1 - index / 1000,
|
|
1007
|
+
seed: fixture.seedPath,
|
|
1008
|
+
hopDepth: 1,
|
|
1009
|
+
decayedWeight: 1,
|
|
1010
|
+
graphType: "entity" as const,
|
|
1011
|
+
edgeConfidence: 1,
|
|
1012
|
+
activationPath: {
|
|
1013
|
+
nodeIds: [fixture.seedPath, candidatePath],
|
|
1014
|
+
edgeConfidences: [1],
|
|
1015
|
+
graphTypes: ["entity"],
|
|
1016
|
+
},
|
|
1017
|
+
})),
|
|
1018
|
+
} as unknown as GraphIndex;
|
|
1019
|
+
const coordinator = makeCoordinator(
|
|
1020
|
+
makeConfig(),
|
|
1021
|
+
new Map([[fixture.name, fixture]]),
|
|
1022
|
+
undefined,
|
|
1023
|
+
new Map([[fixture.dir, fakeIndex]]),
|
|
1024
|
+
);
|
|
1025
|
+
|
|
1026
|
+
const result = await expand(coordinator, [seedResult(fixture)], [fixture.name], 1);
|
|
1027
|
+
|
|
1028
|
+
assert.equal(result.expandedPaths.length, 8);
|
|
1029
|
+
assert.equal(
|
|
1030
|
+
result.expandedPaths.some((entry) => entry.path.endsWith(invalidPath)),
|
|
1031
|
+
false,
|
|
1032
|
+
);
|
|
1033
|
+
for (const candidatePath of candidatePaths.slice(1)) {
|
|
1034
|
+
assert.equal(
|
|
1035
|
+
result.expandedPaths.some((entry) => entry.path.endsWith(candidatePath)),
|
|
1036
|
+
true,
|
|
1037
|
+
);
|
|
1038
|
+
}
|
|
1039
|
+
} finally {
|
|
1040
|
+
await rm(root, { recursive: true, force: true });
|
|
1041
|
+
}
|
|
1042
|
+
});
|
|
1043
|
+
|
|
1044
|
+
test("scans the historical candidate window before the enabled expansion cap", async () => {
|
|
1045
|
+
const root = await mkdtemp(path.join(os.tmpdir(), "remnic-graph-path-historical-enabled-cap-"));
|
|
1046
|
+
try {
|
|
1047
|
+
const fixture = await createNamespace(root, "default");
|
|
1048
|
+
const { candidatePaths, validPath } = await createHistoricalCapCandidates(fixture);
|
|
1049
|
+
const fakeIndex = makeHistoricalCapGraphIndex(fixture, candidatePaths);
|
|
1050
|
+
const readCount = { count: 0 };
|
|
1051
|
+
const originalRead = fixture.storage.readMemoryByPath.bind(fixture.storage);
|
|
1052
|
+
fixture.storage.readMemoryByPath = async (filePath: string) => {
|
|
1053
|
+
readCount.count += 1;
|
|
1054
|
+
return originalRead(filePath);
|
|
1055
|
+
};
|
|
1056
|
+
const coordinator = makeCoordinator(
|
|
1057
|
+
makeConfig(),
|
|
1058
|
+
new Map([[fixture.name, fixture]]),
|
|
1059
|
+
undefined,
|
|
1060
|
+
new Map([[fixture.dir, fakeIndex]]),
|
|
1061
|
+
);
|
|
1062
|
+
|
|
1063
|
+
const result = await expand(coordinator, [seedResult(fixture)], [fixture.name], 1);
|
|
1064
|
+
|
|
1065
|
+
assert.equal(result.expandedPaths.length, 1);
|
|
1066
|
+
assert.equal(result.expandedPaths[0]?.path.endsWith(validPath), true);
|
|
1067
|
+
assert.ok(readCount.count <= 200);
|
|
1068
|
+
} finally {
|
|
1069
|
+
await rm(root, { recursive: true, force: true });
|
|
1070
|
+
}
|
|
1071
|
+
});
|
|
1072
|
+
|
|
1073
|
+
test("scans the historical candidate window before the disabled expansion cap", async () => {
|
|
1074
|
+
const root = await mkdtemp(path.join(os.tmpdir(), "remnic-graph-path-historical-disabled-cap-"));
|
|
1075
|
+
try {
|
|
1076
|
+
const fixture = await createNamespace(root, "default");
|
|
1077
|
+
const { candidatePaths, validPath } = await createHistoricalCapCandidates(fixture);
|
|
1078
|
+
const fakeIndex = makeHistoricalCapGraphIndex(fixture, candidatePaths);
|
|
1079
|
+
const readCount = { count: 0 };
|
|
1080
|
+
const originalRead = fixture.storage.readMemoryByPath.bind(fixture.storage);
|
|
1081
|
+
fixture.storage.readMemoryByPath = async (filePath: string) => {
|
|
1082
|
+
readCount.count += 1;
|
|
1083
|
+
return originalRead(filePath);
|
|
1084
|
+
};
|
|
1085
|
+
const coordinator = makeCoordinator(
|
|
1086
|
+
makeConfig({ graphPathScoring: { enabled: false } }),
|
|
1087
|
+
new Map([[fixture.name, fixture]]),
|
|
1088
|
+
undefined,
|
|
1089
|
+
new Map([[fixture.dir, fakeIndex]]),
|
|
1090
|
+
);
|
|
1091
|
+
|
|
1092
|
+
const result = await expand(coordinator, [seedResult(fixture)], [fixture.name], 1);
|
|
1093
|
+
|
|
1094
|
+
assert.equal(result.expandedPaths.length, 1);
|
|
1095
|
+
assert.equal(result.expandedPaths[0]?.path.endsWith(validPath), true);
|
|
1096
|
+
assert.ok(readCount.count <= 200);
|
|
1097
|
+
} finally {
|
|
1098
|
+
await rm(root, { recursive: true, force: true });
|
|
1099
|
+
}
|
|
1100
|
+
});
|
|
1101
|
+
|
|
1102
|
+
test("caps graph path state reads at 200 candidates with deterministic output", async () => {
|
|
1103
|
+
const root = await mkdtemp(path.join(os.tmpdir(), "remnic-graph-path-read-cap-"));
|
|
1104
|
+
try {
|
|
1105
|
+
const fixture = await createNamespace(root, "default", "active");
|
|
1106
|
+
for (let index = 0; index < 250; index += 1) {
|
|
1107
|
+
await fixture.storage.writeMemory("fact", `bounded-${index}`, {
|
|
1108
|
+
validAt: HISTORICAL_VALID_AT,
|
|
1109
|
+
});
|
|
1110
|
+
}
|
|
1111
|
+
const memories = (await fixture.storage.readAllMemories()).filter((memory) =>
|
|
1112
|
+
memory.content.startsWith("bounded-"),
|
|
1113
|
+
);
|
|
1114
|
+
assert.equal(memories.length, 250);
|
|
1115
|
+
const candidates = memories.map((memory, index) => {
|
|
1116
|
+
const relativePath = path.relative(fixture.dir, memory.path).split(path.sep).join("/");
|
|
1117
|
+
return {
|
|
1118
|
+
path: relativePath,
|
|
1119
|
+
score: 1 - index / 1000,
|
|
1120
|
+
seed: fixture.seedPath,
|
|
1121
|
+
hopDepth: 1,
|
|
1122
|
+
decayedWeight: 1,
|
|
1123
|
+
graphType: "entity" as const,
|
|
1124
|
+
edgeConfidence: 1,
|
|
1125
|
+
activationPath: {
|
|
1126
|
+
nodeIds: [fixture.seedPath, relativePath],
|
|
1127
|
+
edgeConfidences: [1],
|
|
1128
|
+
graphTypes: ["entity"],
|
|
1129
|
+
},
|
|
1130
|
+
};
|
|
1131
|
+
});
|
|
1132
|
+
const fakeIndex = {
|
|
1133
|
+
spreadingActivation: async () => candidates,
|
|
1134
|
+
} as unknown as GraphIndex;
|
|
1135
|
+
const readCount = { count: 0 };
|
|
1136
|
+
const originalRead = fixture.storage.readMemoryByPath.bind(fixture.storage);
|
|
1137
|
+
fixture.storage.readMemoryByPath = async (filePath: string) => {
|
|
1138
|
+
readCount.count += 1;
|
|
1139
|
+
return originalRead(filePath);
|
|
1140
|
+
};
|
|
1141
|
+
const coordinator = makeCoordinator(
|
|
1142
|
+
makeConfig(),
|
|
1143
|
+
new Map([[fixture.name, fixture]]),
|
|
1144
|
+
undefined,
|
|
1145
|
+
new Map([[fixture.dir, fakeIndex]]),
|
|
1146
|
+
);
|
|
1147
|
+
const first = await expand(coordinator, [seedResult(fixture)], [fixture.name], 100);
|
|
1148
|
+
const second = await expand(coordinator, [seedResult(fixture)], [fixture.name], 100);
|
|
1149
|
+
|
|
1150
|
+
assert.equal(
|
|
1151
|
+
readCount.count,
|
|
1152
|
+
400,
|
|
1153
|
+
JSON.stringify({ first: first.expandedPaths.length, second: second.expandedPaths.length }),
|
|
1154
|
+
);
|
|
1155
|
+
assert.equal(first.expandedPaths.length, 200);
|
|
1156
|
+
assert.deepEqual(
|
|
1157
|
+
first.expandedPaths.map(({ path: resultPath, score }) => [resultPath, score]),
|
|
1158
|
+
second.expandedPaths.map(({ path: resultPath, score }) => [resultPath, score]),
|
|
1159
|
+
);
|
|
1160
|
+
} finally {
|
|
1161
|
+
await rm(root, { recursive: true, force: true });
|
|
1162
|
+
}
|
|
1163
|
+
});
|