@remnic/core 9.69.44 → 9.69.46
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 +14 -14
- package/dist/access-admin-ops-surface.js +36 -36
- package/dist/access-audit.js +2 -2
- package/dist/access-authorization-probe.d.ts +14 -14
- package/dist/access-authorization-probe.js +37 -37
- package/dist/access-boundary.d.ts +14 -14
- package/dist/access-boundary.js +36 -36
- package/dist/access-cli.js +90 -87
- package/dist/access-cli.js.map +1 -1
- package/dist/access-coding-context-resolution.d.ts +1 -1
- package/dist/access-extraction-force-flush.d.ts +14 -14
- package/dist/access-extraction-force-flush.js +36 -36
- package/dist/access-health-types.d.ts +1 -1
- package/dist/access-http-briefing.d.ts +14 -14
- package/dist/access-http-briefing.js +37 -37
- package/dist/access-http-lcm-compaction.d.ts +14 -14
- package/dist/access-http-lifecycle-flush.d.ts +14 -14
- package/dist/access-http-offline-stream.d.ts +14 -14
- package/dist/access-http-query.d.ts +2 -2
- package/dist/access-http-query.js +38 -38
- package/dist/access-http.d.ts +15 -15
- package/dist/access-http.js +52 -52
- package/dist/access-identity-continuity-surface.d.ts +11 -11
- package/dist/access-identity-continuity-surface.js +36 -36
- package/dist/access-lcm-surface.d.ts +14 -14
- package/dist/access-lcm-surface.js +36 -36
- package/dist/access-mcp-shared-context-tools.d.ts +14 -14
- package/dist/access-mcp.d.ts +14 -14
- package/dist/access-mcp.js +44 -44
- package/dist/access-memory-search-fanout.d.ts +2 -2
- package/dist/{access-namespace-preflight-CE-RrXKu.d.ts → access-namespace-preflight-DDnsN22V.d.ts} +1 -1
- package/dist/access-namespace-preflight.d.ts +3 -3
- package/dist/access-namespace-preflight.js +36 -36
- package/dist/access-observe-transcript.d.ts +11 -11
- package/dist/access-observe-write-category.d.ts +3 -3
- package/dist/access-observe-write-category.js +4 -4
- package/dist/access-observe-write-surface.d.ts +14 -14
- package/dist/access-observe-write-surface.js +36 -36
- package/dist/access-offline-manifest.d.ts +11 -11
- package/dist/access-offline-manifest.js +3 -3
- package/dist/access-operations-batch.js +37 -37
- package/dist/access-operations.d.ts +14 -14
- package/dist/access-operations.js +43 -43
- package/dist/access-recall-concurrency.d.ts +14 -14
- package/dist/access-recall-concurrency.js +36 -36
- package/dist/access-recall-response.d.ts +14 -14
- package/dist/access-recall-response.js +36 -36
- package/dist/access-recall-surface.d.ts +14 -14
- package/dist/access-recall-surface.js +36 -36
- package/dist/access-schema.d.ts +114 -114
- package/dist/access-schema.js +3 -3
- package/dist/{access-service-Cww3xAAy.d.ts → access-service-DGynYlbE.d.ts} +73 -73
- package/dist/access-service-helpers.d.ts +14 -14
- package/dist/access-service.d.ts +14 -14
- package/dist/access-service.js +36 -36
- package/dist/access-surface-catalog.d.ts +14 -14
- package/dist/access-surface-catalog.js +38 -38
- package/dist/access-wearables-meetings-surface.d.ts +4 -4
- package/dist/action-confidence.d.ts +1 -1
- package/dist/active-memory-bridge.d.ts +1 -1
- package/dist/active-recall.d.ts +1 -1
- package/dist/activity/digest.d.ts +1 -1
- package/dist/ambient-provenance.d.ts +1 -1
- package/dist/artifact-search.d.ts +1 -1
- package/dist/{auto-sync-QZD4RD6H.js → auto-sync-DG5KEOF5.js} +5 -5
- package/dist/background-generation.d.ts +1 -1
- package/dist/behavior-learner.d.ts +1 -1
- package/dist/behavior-signals.d.ts +1 -1
- package/dist/bootstrap.d.ts +11 -11
- package/dist/briefing-window.d.ts +2 -2
- package/dist/briefing.d.ts +2 -2
- package/dist/briefing.js +12 -12
- package/dist/buffer-surprise-report.d.ts +1 -1
- package/dist/buffer-turn-helpers.d.ts +1 -1
- package/dist/buffer.d.ts +2 -2
- package/dist/buffer.js +3 -3
- package/dist/bulk-import/index.d.ts +3 -3
- package/dist/calibration.d.ts +1 -1
- package/dist/calibration.js +2 -2
- package/dist/capabilities.d.ts +1 -1
- package/dist/{catalog-Ya3DDn_T.d.ts → catalog-Dl2hYqpS.d.ts} +1 -1
- package/dist/causal-behavior.d.ts +1 -1
- package/dist/causal-consolidation.d.ts +1 -1
- package/dist/causal-consolidation.js +13 -13
- package/dist/causal-trajectory-graph.d.ts +1 -1
- package/dist/{chunk-MOL7JK4F.js → chunk-3NYM6JA4.js} +2 -2
- package/dist/{chunk-WNMVUWI7.js → chunk-3UQIS2GC.js} +2 -2
- package/dist/{chunk-JDL5BYYF.js → chunk-47C6DF43.js} +3 -3
- package/dist/{chunk-ZBSP4BSN.js → chunk-4ECS33D4.js} +2 -2
- package/dist/{chunk-6EGIQEKC.js → chunk-4HTQ76O4.js} +2 -2
- package/dist/{chunk-7L54ARML.js → chunk-4XTSKGLX.js} +2 -2
- package/dist/{chunk-SUS64JER.js → chunk-5EHPB6RL.js} +2 -2
- package/dist/{chunk-J2MGTGKC.js → chunk-5ZXGH2ZL.js} +20 -3
- package/dist/chunk-5ZXGH2ZL.js.map +1 -0
- package/dist/{chunk-VDLNQVYW.js → chunk-62NYUFNH.js} +7 -7
- package/dist/{chunk-Y77UTH7F.js → chunk-66QOUYNS.js} +83 -64
- package/dist/chunk-66QOUYNS.js.map +1 -0
- package/dist/{chunk-SZV2L2IG.js → chunk-7BNB7KHB.js} +4 -4
- package/dist/chunk-AAQA6ZUP.js +41 -0
- package/dist/chunk-AAQA6ZUP.js.map +1 -0
- package/dist/{chunk-RJ7Z7YXK.js → chunk-AG6PANBG.js} +14 -20
- package/dist/chunk-AG6PANBG.js.map +1 -0
- package/dist/{chunk-IAGEEVQL.js → chunk-AN2O4KZY.js} +2 -2
- package/dist/{chunk-JUOJGZPV.js → chunk-AYP2FZFA.js} +2 -2
- package/dist/{chunk-XK76RJBN.js → chunk-AYZHHMTK.js} +2 -2
- package/dist/{chunk-LQVXSRAJ.js → chunk-B4EGAHHS.js} +2 -2
- package/dist/{chunk-HKQAI6XI.js → chunk-CFIHVWYZ.js} +2 -2
- package/dist/{chunk-DABSYX7V.js → chunk-CWMMKFO7.js} +3 -3
- package/dist/{chunk-WLHPCAVD.js → chunk-D5DTZBTV.js} +6 -6
- package/dist/{chunk-2HDLDBJW.js → chunk-DDBLZJRV.js} +2 -2
- package/dist/{chunk-F2FJTOV6.js → chunk-DO6R3LIE.js} +2 -2
- package/dist/{chunk-SHEHQBCG.js → chunk-DUJYRVUD.js} +638 -45
- package/dist/chunk-DUJYRVUD.js.map +1 -0
- package/dist/{chunk-LD22AAJI.js → chunk-EAZ3YZKN.js} +2 -2
- package/dist/{chunk-46FVFU7S.js → chunk-EC5SVM6H.js} +2 -2
- package/dist/{chunk-GKKLI23D.js → chunk-EEHQY7ER.js} +2 -2
- package/dist/{chunk-UHSOPTNB.js → chunk-EP6BA7VV.js} +50 -15
- package/dist/chunk-EP6BA7VV.js.map +1 -0
- package/dist/{chunk-BPXJINHW.js → chunk-EUP4AXIW.js} +2 -2
- package/dist/{chunk-2WMXZYMS.js → chunk-F5V7KRQS.js} +2 -2
- package/dist/{chunk-4AX5OYKC.js → chunk-FDDCZDBI.js} +2 -2
- package/dist/{chunk-XLPZN3A5.js → chunk-FMEP2RB5.js} +2 -2
- package/dist/{chunk-D7UUB33A.js → chunk-FSH6KRKO.js} +2 -2
- package/dist/{chunk-3254BYQV.js → chunk-GGY7WKMB.js} +1 -1
- package/dist/{chunk-ROXDOKLM.js → chunk-GYYLZOSO.js} +4 -4
- package/dist/{chunk-3K64PLY3.js → chunk-HXDXR2FH.js} +23 -23
- package/dist/{chunk-XOCGC3FJ.js → chunk-HYWPUYLO.js} +2 -2
- package/dist/{chunk-KQAFEZQX.js → chunk-IPLYGWQF.js} +5 -5
- package/dist/{chunk-M6FKXOYV.js → chunk-IXMWTNNY.js} +2 -2
- package/dist/{chunk-ZYSOT6HD.js → chunk-J3IWHCMF.js} +2 -2
- package/dist/{chunk-3YQ7VLP3.js → chunk-J5OXWG66.js} +2 -2
- package/dist/{chunk-H2N6SLTR.js → chunk-JIWZVOQM.js} +2 -2
- package/dist/{chunk-C3YEB7CR.js → chunk-JOP7W24J.js} +4 -4
- package/dist/{chunk-F6PTJLEM.js → chunk-K4KE5MYE.js} +1 -1
- package/dist/{chunk-IOTYIECA.js → chunk-KF57IQFX.js} +13 -13
- package/dist/{chunk-JL4TQYM5.js → chunk-L272A5QW.js} +33 -2
- package/dist/{chunk-JL4TQYM5.js.map → chunk-L272A5QW.js.map} +1 -1
- package/dist/chunk-M4DB6ZNN.js +87 -0
- package/dist/chunk-M4DB6ZNN.js.map +1 -0
- package/dist/{chunk-ZVQ5HF77.js → chunk-N46SNYMC.js} +2 -2
- package/dist/{chunk-YR5QT4SR.js → chunk-NAFPHFDM.js} +2 -2
- package/dist/{chunk-VHANUT45.js → chunk-ORQ7AZMU.js} +7 -7
- package/dist/{chunk-EOXCOZ6L.js → chunk-OV7GJ4KW.js} +2 -2
- package/dist/{chunk-VJXMFBVR.js → chunk-P7S7BVBJ.js} +2 -2
- package/dist/{chunk-GMU7MAHS.js → chunk-QUFJ3NJE.js} +260 -796
- package/dist/chunk-QUFJ3NJE.js.map +1 -0
- package/dist/{chunk-7F47R4H4.js → chunk-S3SND7SO.js} +2 -2
- package/dist/{chunk-5AJZ7EUW.js → chunk-S7KDHFF2.js} +2 -2
- package/dist/{chunk-4WMGRLTV.js → chunk-SRRZZHA5.js} +2 -2
- package/dist/{chunk-KDLOKVX7.js → chunk-SW42MMAH.js} +2 -2
- package/dist/{chunk-2YIUUXNK.js → chunk-T5NI5K46.js} +2 -2
- package/dist/{chunk-QCTXQ2HW.js → chunk-TE6B2UU6.js} +2 -2
- package/dist/{chunk-JDOBQPJY.js → chunk-TNQR7N52.js} +2 -2
- package/dist/{chunk-7K5Q6COX.js → chunk-TVVEYCNW.js} +4 -4
- package/dist/{chunk-T3F2HWIG.js → chunk-TZ44E732.js} +2 -2
- package/dist/{chunk-PF7LQ22G.js → chunk-U4ESGVZ2.js} +3 -3
- package/dist/{chunk-WJPR2XUN.js → chunk-UEJY6ZTQ.js} +2 -2
- package/dist/{chunk-K3IVJ5ML.js → chunk-UGGOQKO6.js} +26 -26
- package/dist/{chunk-OCX7OWOC.js → chunk-UOREPQ3C.js} +2 -2
- package/dist/{chunk-R3H2AXO2.js → chunk-VGRVJ6Z6.js} +2 -2
- package/dist/{chunk-IWHNT35B.js → chunk-WOBMSMUK.js} +3 -3
- package/dist/{chunk-DDGQDNQW.js → chunk-XLZZLUWL.js} +2 -2
- package/dist/{chunk-KORQWGUA.js → chunk-Y3XHIWVP.js} +2 -2
- package/dist/{chunk-BZDK5EOD.js → chunk-YFVIA5A6.js} +2 -2
- package/dist/{chunk-GR76OKET.js → chunk-YSBYNCCN.js} +2 -2
- package/dist/{chunk-OGAKUA7I.js → chunk-YUQPUK6H.js} +2 -2
- package/dist/{chunk-TRTLWUDD.js → chunk-ZTHGFTMW.js} +2 -2
- package/dist/{cli-Cl7bKIWJ.d.ts → cli-Cik3H7q1.d.ts} +5 -5
- package/dist/cli.d.ts +16 -16
- package/dist/cli.js +67 -67
- package/dist/coding/export-okf-codegraph.d.ts +1 -1
- package/dist/coding/export-okf-codegraph.js +11 -11
- package/dist/coding/pre-action-gate.d.ts +1 -1
- package/dist/compounding/engine.d.ts +2 -2
- package/dist/compounding/engine.js +12 -12
- package/dist/compounding/preference-consolidator.d.ts +1 -1
- package/dist/compression-optimizer.d.ts +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/connectors/codex-materialize-runner.d.ts +1 -1
- package/dist/connectors/codex-materialize-runner.js +12 -12
- package/dist/connectors/codex-materialize.d.ts +1 -1
- package/dist/connectors/index.d.ts +1 -1
- package/dist/connectors/index.js +12 -12
- package/dist/consolidation-provenance-check.d.ts +2 -2
- package/dist/consolidation-undo.d.ts +2 -2
- package/dist/contradiction/index.d.ts +4 -4
- package/dist/converge-config.d.ts +1 -1
- package/dist/convergence-refresh.d.ts +2 -2
- package/dist/convergence-refresh.js +14 -14
- package/dist/conversation-index/backend.d.ts +1 -1
- package/dist/conversation-index/chunker.d.ts +1 -1
- package/dist/conversation-index/faiss-adapter.d.ts +1 -1
- package/dist/conversation-index/indexer.d.ts +1 -1
- package/dist/conversation-index/search.d.ts +1 -1
- package/dist/corpus-watermark.d.ts +1 -1
- package/dist/corpus-watermark.js +14 -14
- package/dist/cross-lingual-recall.d.ts +1 -1
- package/dist/day-summary.d.ts +1 -1
- package/dist/deep-recall-http-glue.d.ts +14 -14
- package/dist/deep-recall-http-glue.js +4 -4
- package/dist/deep-recall-mcp-tools.d.ts +14 -14
- package/dist/deep-recall-policy-llm.d.ts +2 -2
- package/dist/deep-recall-policy-llm.js +3 -3
- package/dist/deep-recall-seeds.d.ts +1 -1
- package/dist/delinearize.d.ts +1 -1
- package/dist/dependency-propagation-config.d.ts +1 -1
- package/dist/{dependency-propagation-delivery-CggS9BFT.d.ts → dependency-propagation-delivery-BXaUoVUf.d.ts} +1 -1
- package/dist/direct-answer-wiring.d.ts +1 -1
- package/dist/direct-answer.d.ts +1 -1
- package/dist/embedding-fallback.d.ts +1 -1
- package/dist/enrichment/index.d.ts +1 -1
- package/dist/entity-origin-fields.d.ts +1 -1
- package/dist/entity-retrieval.d.ts +2 -2
- package/dist/entity-retrieval.js +12 -12
- package/dist/entity-schema.d.ts +1 -1
- package/dist/episodic-context.d.ts +1 -1
- package/dist/eval-trajectory-contracts.d.ts +1 -1
- package/dist/eval-trajectory-store.d.ts +1 -1
- package/dist/eval-trajectory.d.ts +1 -1
- package/dist/explicit-capture.d.ts +11 -11
- package/dist/explicit-capture.js +2 -2
- package/dist/external-wiki-access.d.ts +14 -14
- package/dist/external-wiki-access.js +37 -37
- package/dist/external-wiki-collection-registration.d.ts +1 -1
- package/dist/external-wiki-collection.d.ts +1 -1
- package/dist/external-wiki-mcp-tools.d.ts +14 -14
- package/dist/extraction-error-classification.d.ts +1 -1
- package/dist/extraction-faithfulness.d.ts +2 -2
- package/dist/extraction-faithfulness.js +3 -3
- package/dist/extraction-judge-telemetry.d.ts +2 -2
- package/dist/extraction-judge-training.d.ts +2 -2
- package/dist/extraction-judge.d.ts +2 -2
- package/dist/extraction-judge.js +3 -3
- package/dist/extraction-liveness.d.ts +1 -1
- package/dist/extraction-normalization.d.ts +1 -1
- package/dist/extraction-prompt.d.ts +1 -1
- package/dist/extraction-source-grounding-rules.d.ts +1 -1
- package/dist/extraction-source-grounding.d.ts +1 -1
- package/dist/extraction-span-config.d.ts +1 -1
- package/dist/extraction-span-materialize.d.ts +1 -1
- package/dist/extraction-span-wire.d.ts +23 -4
- package/dist/extraction-span-wire.js +7 -8
- package/dist/extraction-span-wire.js.map +1 -1
- package/dist/extraction.d.ts +2 -2
- package/dist/extraction.js +19 -17
- package/dist/fallback-llm-codex-subscription.d.ts +1 -1
- package/dist/fallback-llm-codex-subscription.js +1 -1
- package/dist/fallback-llm.d.ts +1 -1
- package/dist/fallback-llm.js +2 -2
- package/dist/{generic-recall-paths-CoXPSnCw.d.ts → generic-recall-paths-BkVVEoeO.d.ts} +1 -1
- package/dist/graph-append-error.d.ts +1 -1
- package/dist/graph-dashboard-diff.d.ts +1 -1
- package/dist/graph-dashboard-key.d.ts +1 -1
- package/dist/graph-dashboard-parser.d.ts +1 -1
- package/dist/graph-edge-reinforcement.d.ts +1 -1
- package/dist/graph-jsonl.d.ts +1 -1
- package/dist/graph-path-reconstruction.d.ts +1 -1
- package/dist/graph-path-scoring.d.ts +1 -1
- package/dist/graph-snapshot.d.ts +1 -1
- package/dist/graph.d.ts +1 -1
- package/dist/harmonic-retrieval.js +1 -1
- package/dist/importance.d.ts +1 -1
- package/dist/importers/index.d.ts +1 -1
- package/dist/importers/test-utils.d.ts +1 -1
- package/dist/in-flight-reads.d.ts +1 -1
- package/dist/{index-fdw_oK5a.d.ts → index-Zb6SUV4R.d.ts} +3 -3
- package/dist/index.d.ts +703 -703
- package/dist/index.js +90 -87
- package/dist/intent.d.ts +1 -1
- package/dist/lcm/engine.d.ts +1 -1
- package/dist/lcm/index.d.ts +1 -1
- package/dist/lcm/index.js +3 -3
- package/dist/lcm/tools.d.ts +1 -1
- package/dist/lifecycle.d.ts +1 -1
- package/dist/live-connectors-runner.d.ts +1 -1
- package/dist/{local-llm-3-jtw52a.d.ts → local-llm-DdrhmecM.d.ts} +1 -1
- package/dist/local-llm-helpers.d.ts +2 -2
- package/dist/local-llm.d.ts +2 -2
- package/dist/local-model-endpoint.d.ts +1 -1
- package/dist/location/index.d.ts +5 -5
- package/dist/location/index.js +14 -14
- package/dist/maintenance/memory-governance.d.ts +1 -1
- package/dist/maintenance/memory-governance.js +12 -12
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.d.ts +2 -2
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +12 -12
- package/dist/maintenance/rebuild-memory-projection.d.ts +2 -2
- package/dist/maintenance/rebuild-memory-projection.js +13 -13
- package/dist/{maintenance-DgeeH0a1.d.ts → maintenance-Du_VRvVg.d.ts} +3 -3
- package/dist/mcp-memory-inspector-app.d.ts +14 -14
- package/dist/memory-action-policy.d.ts +1 -1
- package/dist/memory-age.d.ts +100 -0
- package/dist/memory-age.js +22 -0
- package/dist/memory-age.js.map +1 -0
- package/dist/memory-cache.d.ts +1 -1
- package/dist/memory-controller.d.ts +1 -1
- package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
- package/dist/memory-projection-store.d.ts +1 -1
- package/dist/memory-provenance.d.ts +1 -1
- package/dist/memory-snapshot.d.ts +1 -1
- package/dist/memory-subject-cli.d.ts +14 -14
- package/dist/memory-subject.d.ts +14 -14
- package/dist/memory-worth-outcomes.d.ts +2 -2
- package/dist/models-json.d.ts +1 -1
- package/dist/namespaces/migrate.d.ts +3 -3
- package/dist/namespaces/migrate.js +13 -13
- package/dist/namespaces/principal.d.ts +1 -1
- package/dist/namespaces/search.d.ts +1 -1
- package/dist/namespaces/search.js +3 -3
- package/dist/namespaces/storage.d.ts +3 -3
- package/dist/namespaces/storage.js +12 -12
- package/dist/native-knowledge.d.ts +1 -1
- package/dist/offline-sync-impression-drain.d.ts +1 -1
- package/dist/operator-doctor-corpus.d.ts +1 -1
- package/dist/operator-doctor-corpus.js +15 -15
- package/dist/operator-doctor-replica.d.ts +1 -1
- package/dist/operator-toolkit.d.ts +3 -3
- package/dist/operator-toolkit.js +20 -20
- package/dist/orchestration/compression-guideline-coordinator.d.ts +2 -2
- package/dist/orchestration/maintenance.d.ts +4 -4
- package/dist/orchestration/maintenance.js +20 -20
- package/dist/{orchestrator-VXWKdatm.d.ts → orchestrator-D9U3Zgjp.d.ts} +10 -10
- package/dist/orchestrator.d.ts +11 -11
- package/dist/orchestrator.js +90 -87
- package/dist/patterns-cli.d.ts +1 -1
- package/dist/{pipeline-CwXZBKR5.d.ts → pipeline-4lqKJZ_0.d.ts} +1 -1
- package/dist/policy-runtime.d.ts +1 -1
- package/dist/proactive-contention.d.ts +2 -2
- package/dist/provenance.d.ts +1 -1
- package/dist/{public-http-DdmM4scJ.d.ts → public-http-AabioFLh.d.ts} +1 -1
- package/dist/{qmd-DOLTPIQg.d.ts → qmd-DnoWaXKb.d.ts} +1 -1
- package/dist/qmd-preflight.d.ts +2 -2
- package/dist/qmd-recall-cache.d.ts +1 -1
- package/dist/qmd.d.ts +2 -2
- package/dist/recall-concurrency-config.d.ts +1 -1
- package/dist/recall-disclosure-escalation.d.ts +1 -1
- package/dist/recall-explain-renderer.d.ts +1 -1
- package/dist/recall-memory-map.d.ts +1 -1
- package/dist/recall-navigate-access.d.ts +1 -1
- package/dist/recall-navigate-disclosure.d.ts +1 -1
- package/dist/recall-navigate.d.ts +1 -1
- package/dist/recall-navigation-http-glue.d.ts +14 -14
- package/dist/recall-navigation-mcp-tools.d.ts +14 -14
- package/dist/recall-navigation-operations.js +37 -37
- package/dist/recall-navigation-renderer.d.ts +1 -1
- package/dist/recall-navigation.d.ts +1 -1
- package/dist/recall-navigation.js +36 -36
- package/dist/recall-planner-llm.d.ts +1 -1
- package/dist/recall-planner-llm.js +2 -2
- package/dist/recall-state-view-admission.d.ts +2 -2
- package/dist/recall-state-view-admission.js +3 -3
- package/dist/recall-state-view-anchors.d.ts +1 -1
- package/dist/recall-state.d.ts +1 -1
- package/dist/recall-tag-filter.d.ts +1 -1
- package/dist/recall-timings.d.ts +1 -1
- package/dist/recall-xray-cli.d.ts +1 -1
- package/dist/recall-xray-renderer.d.ts +1 -1
- package/dist/recall-xray.d.ts +1 -1
- package/dist/reconcile/cursor.d.ts +1 -1
- package/dist/reconcile/manifest.d.ts +1 -1
- package/dist/reconcile/manifest.js +2 -2
- package/dist/reconcile/plan.d.ts +1 -1
- package/dist/replay/normalizers/chatgpt.d.ts +1 -1
- package/dist/replay/normalizers/claude.d.ts +1 -1
- package/dist/replay/normalizers/openclaw.d.ts +1 -1
- package/dist/replay/normalizers/shared.d.ts +1 -1
- package/dist/replay/runner.d.ts +1 -1
- package/dist/replay/types.d.ts +1 -1
- package/dist/replica-divergence.d.ts +1 -1
- package/dist/replica-peers-config.d.ts +1 -1
- package/dist/resolve-auth-token.d.ts +1 -1
- package/dist/retrieval-agents.d.ts +2 -2
- package/dist/retrieval-tiers.d.ts +1 -1
- package/dist/routing/engine.d.ts +1 -1
- package/dist/routing/store.d.ts +1 -1
- package/dist/salvage-envelope.d.ts +1 -1
- package/dist/schemas.d.ts +160 -160
- package/dist/{scope-profiles-C564-hrv.d.ts → scope-profiles-crazXegZ.d.ts} +1 -1
- package/dist/search/embed-helper.d.ts +1 -1
- package/dist/search/factory.d.ts +1 -1
- package/dist/search/factory.js +2 -2
- package/dist/search/index.d.ts +1 -1
- package/dist/search/index.js +4 -4
- package/dist/search/lancedb-backend.d.ts +1 -1
- package/dist/search/meilisearch-backend.d.ts +1 -1
- package/dist/search/noop-backend.d.ts +1 -1
- package/dist/search/orama-backend.d.ts +1 -1
- package/dist/search/port.d.ts +1 -1
- package/dist/search/remote-backend.d.ts +1 -1
- package/dist/{semantic-consolidation-g6x-ShY8.d.ts → semantic-consolidation-B09JI9WM.d.ts} +1 -1
- package/dist/semantic-consolidation.d.ts +2 -2
- package/dist/semantic-consolidation.js +13 -13
- package/dist/semantic-rule-promotion.js +12 -12
- package/dist/semantic-rule-verifier.d.ts +1 -1
- package/dist/semantic-rule-verifier.js +12 -12
- package/dist/{service-CaCOk0rd.d.ts → service-ByjbCuj7.d.ts} +2 -2
- package/dist/session-observer-bands.d.ts +1 -1
- package/dist/session-observer-state.d.ts +1 -1
- package/dist/shared-context/manager.d.ts +7 -7
- package/dist/signal.d.ts +1 -1
- package/dist/source-agent-qualifier.d.ts +1 -1
- package/dist/source-agent-qualifier.js +15 -15
- package/dist/source-attribution.d.ts +7 -4
- package/dist/source-attribution.js +1 -1
- package/dist/standup/build.js +12 -12
- package/dist/{storage-C9nsZX5Q.d.ts → storage-CbdF077z.d.ts} +17 -3
- package/dist/storage.d.ts +2 -2
- package/dist/storage.js +11 -11
- package/dist/subject-config.d.ts +1 -1
- package/dist/summarizer.d.ts +1 -1
- package/dist/summarizer.js +3 -3
- package/dist/summary-snapshot.d.ts +1 -1
- package/dist/support-passport/index.d.ts +17 -17
- package/dist/support-passport/index.js +39 -39
- package/dist/{tagging-BA4RFdH3.d.ts → tagging-CF7DNV7_.d.ts} +2 -2
- package/dist/temporal-supersession.d.ts +2 -2
- package/dist/temporal-timeline-recall.d.ts +1 -1
- package/dist/temporal-validity.d.ts +1 -1
- package/dist/threading.d.ts +1 -1
- package/dist/tier-migration.d.ts +2 -2
- package/dist/tier-routing.d.ts +1 -1
- package/dist/topics.d.ts +1 -1
- package/dist/transcript.d.ts +1 -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/export-okf.d.ts +1 -1
- package/dist/transfer/export-okf.js +12 -12
- package/dist/transfer/types.d.ts +78 -78
- package/dist/trust-score-stage.d.ts +1 -1
- package/dist/trust-score.d.ts +1 -1
- package/dist/{types-Sps-lg5I.d.ts → types-nmhnJpxx.d.ts} +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/utility-runtime.d.ts +1 -1
- package/dist/verified-recall.js +12 -12
- package/dist/who-knows-cli.d.ts +14 -14
- package/dist/who-knows-cli.js +13 -13
- package/dist/who-knows.d.ts +14 -14
- package/dist/who-knows.js +12 -12
- package/dist/write-envelope.d.ts +1 -1
- package/package.json +2 -2
- package/src/background-generation.test.ts +77 -0
- package/src/dedup/merge.test.ts +185 -8
- package/src/extraction-span-wire.test.ts +17 -1
- package/src/extraction-span-wire.ts +46 -3
- package/src/extraction.ts +2 -11
- package/src/memory-age.test.ts +106 -0
- package/src/memory-age.ts +196 -0
- package/src/orchestration/recall-search-pipeline.test.ts +207 -1
- package/src/orchestration/recall-search-pipeline.ts +13 -9
- package/src/orchestration/semantic-merge-commit-effects.ts +31 -9
- package/src/orchestration/semantic-merge-persist.ts +16 -5
- package/src/providers/codex-subscription.test.ts +109 -3
- package/src/providers/codex-subscription.ts +57 -17
- package/src/source-attribution.test.ts +27 -0
- package/src/source-attribution.ts +41 -5
- package/src/storage/cas-revision-host.ts +22 -0
- package/src/storage/cas-revision-store.test.ts +44 -1
- package/src/storage/cas-revision-store.ts +23 -0
- package/src/storage/tombstone-blocked-capture-sync.ts +26 -1
- package/src/storage-frontmatter-cas.test.ts +49 -1
- package/src/storage.ts +1 -1
- package/src/summarizer.ts +18 -0
- package/dist/chunk-GMU7MAHS.js.map +0 -1
- package/dist/chunk-J2MGTGKC.js.map +0 -1
- package/dist/chunk-RJ7Z7YXK.js.map +0 -1
- package/dist/chunk-SHEHQBCG.js.map +0 -1
- package/dist/chunk-UHSOPTNB.js.map +0 -1
- package/dist/chunk-Y77UTH7F.js.map +0 -1
- /package/dist/{auto-sync-QZD4RD6H.js.map → auto-sync-DG5KEOF5.js.map} +0 -0
- /package/dist/{chunk-MOL7JK4F.js.map → chunk-3NYM6JA4.js.map} +0 -0
- /package/dist/{chunk-WNMVUWI7.js.map → chunk-3UQIS2GC.js.map} +0 -0
- /package/dist/{chunk-JDL5BYYF.js.map → chunk-47C6DF43.js.map} +0 -0
- /package/dist/{chunk-ZBSP4BSN.js.map → chunk-4ECS33D4.js.map} +0 -0
- /package/dist/{chunk-6EGIQEKC.js.map → chunk-4HTQ76O4.js.map} +0 -0
- /package/dist/{chunk-7L54ARML.js.map → chunk-4XTSKGLX.js.map} +0 -0
- /package/dist/{chunk-SUS64JER.js.map → chunk-5EHPB6RL.js.map} +0 -0
- /package/dist/{chunk-VDLNQVYW.js.map → chunk-62NYUFNH.js.map} +0 -0
- /package/dist/{chunk-SZV2L2IG.js.map → chunk-7BNB7KHB.js.map} +0 -0
- /package/dist/{chunk-IAGEEVQL.js.map → chunk-AN2O4KZY.js.map} +0 -0
- /package/dist/{chunk-JUOJGZPV.js.map → chunk-AYP2FZFA.js.map} +0 -0
- /package/dist/{chunk-XK76RJBN.js.map → chunk-AYZHHMTK.js.map} +0 -0
- /package/dist/{chunk-LQVXSRAJ.js.map → chunk-B4EGAHHS.js.map} +0 -0
- /package/dist/{chunk-HKQAI6XI.js.map → chunk-CFIHVWYZ.js.map} +0 -0
- /package/dist/{chunk-DABSYX7V.js.map → chunk-CWMMKFO7.js.map} +0 -0
- /package/dist/{chunk-WLHPCAVD.js.map → chunk-D5DTZBTV.js.map} +0 -0
- /package/dist/{chunk-2HDLDBJW.js.map → chunk-DDBLZJRV.js.map} +0 -0
- /package/dist/{chunk-F2FJTOV6.js.map → chunk-DO6R3LIE.js.map} +0 -0
- /package/dist/{chunk-LD22AAJI.js.map → chunk-EAZ3YZKN.js.map} +0 -0
- /package/dist/{chunk-46FVFU7S.js.map → chunk-EC5SVM6H.js.map} +0 -0
- /package/dist/{chunk-GKKLI23D.js.map → chunk-EEHQY7ER.js.map} +0 -0
- /package/dist/{chunk-BPXJINHW.js.map → chunk-EUP4AXIW.js.map} +0 -0
- /package/dist/{chunk-2WMXZYMS.js.map → chunk-F5V7KRQS.js.map} +0 -0
- /package/dist/{chunk-4AX5OYKC.js.map → chunk-FDDCZDBI.js.map} +0 -0
- /package/dist/{chunk-XLPZN3A5.js.map → chunk-FMEP2RB5.js.map} +0 -0
- /package/dist/{chunk-D7UUB33A.js.map → chunk-FSH6KRKO.js.map} +0 -0
- /package/dist/{chunk-3254BYQV.js.map → chunk-GGY7WKMB.js.map} +0 -0
- /package/dist/{chunk-ROXDOKLM.js.map → chunk-GYYLZOSO.js.map} +0 -0
- /package/dist/{chunk-3K64PLY3.js.map → chunk-HXDXR2FH.js.map} +0 -0
- /package/dist/{chunk-XOCGC3FJ.js.map → chunk-HYWPUYLO.js.map} +0 -0
- /package/dist/{chunk-KQAFEZQX.js.map → chunk-IPLYGWQF.js.map} +0 -0
- /package/dist/{chunk-M6FKXOYV.js.map → chunk-IXMWTNNY.js.map} +0 -0
- /package/dist/{chunk-ZYSOT6HD.js.map → chunk-J3IWHCMF.js.map} +0 -0
- /package/dist/{chunk-3YQ7VLP3.js.map → chunk-J5OXWG66.js.map} +0 -0
- /package/dist/{chunk-H2N6SLTR.js.map → chunk-JIWZVOQM.js.map} +0 -0
- /package/dist/{chunk-C3YEB7CR.js.map → chunk-JOP7W24J.js.map} +0 -0
- /package/dist/{chunk-F6PTJLEM.js.map → chunk-K4KE5MYE.js.map} +0 -0
- /package/dist/{chunk-IOTYIECA.js.map → chunk-KF57IQFX.js.map} +0 -0
- /package/dist/{chunk-ZVQ5HF77.js.map → chunk-N46SNYMC.js.map} +0 -0
- /package/dist/{chunk-YR5QT4SR.js.map → chunk-NAFPHFDM.js.map} +0 -0
- /package/dist/{chunk-VHANUT45.js.map → chunk-ORQ7AZMU.js.map} +0 -0
- /package/dist/{chunk-EOXCOZ6L.js.map → chunk-OV7GJ4KW.js.map} +0 -0
- /package/dist/{chunk-VJXMFBVR.js.map → chunk-P7S7BVBJ.js.map} +0 -0
- /package/dist/{chunk-7F47R4H4.js.map → chunk-S3SND7SO.js.map} +0 -0
- /package/dist/{chunk-5AJZ7EUW.js.map → chunk-S7KDHFF2.js.map} +0 -0
- /package/dist/{chunk-4WMGRLTV.js.map → chunk-SRRZZHA5.js.map} +0 -0
- /package/dist/{chunk-KDLOKVX7.js.map → chunk-SW42MMAH.js.map} +0 -0
- /package/dist/{chunk-2YIUUXNK.js.map → chunk-T5NI5K46.js.map} +0 -0
- /package/dist/{chunk-QCTXQ2HW.js.map → chunk-TE6B2UU6.js.map} +0 -0
- /package/dist/{chunk-JDOBQPJY.js.map → chunk-TNQR7N52.js.map} +0 -0
- /package/dist/{chunk-7K5Q6COX.js.map → chunk-TVVEYCNW.js.map} +0 -0
- /package/dist/{chunk-T3F2HWIG.js.map → chunk-TZ44E732.js.map} +0 -0
- /package/dist/{chunk-PF7LQ22G.js.map → chunk-U4ESGVZ2.js.map} +0 -0
- /package/dist/{chunk-WJPR2XUN.js.map → chunk-UEJY6ZTQ.js.map} +0 -0
- /package/dist/{chunk-K3IVJ5ML.js.map → chunk-UGGOQKO6.js.map} +0 -0
- /package/dist/{chunk-OCX7OWOC.js.map → chunk-UOREPQ3C.js.map} +0 -0
- /package/dist/{chunk-R3H2AXO2.js.map → chunk-VGRVJ6Z6.js.map} +0 -0
- /package/dist/{chunk-IWHNT35B.js.map → chunk-WOBMSMUK.js.map} +0 -0
- /package/dist/{chunk-DDGQDNQW.js.map → chunk-XLZZLUWL.js.map} +0 -0
- /package/dist/{chunk-KORQWGUA.js.map → chunk-Y3XHIWVP.js.map} +0 -0
- /package/dist/{chunk-BZDK5EOD.js.map → chunk-YFVIA5A6.js.map} +0 -0
- /package/dist/{chunk-GR76OKET.js.map → chunk-YSBYNCCN.js.map} +0 -0
- /package/dist/{chunk-OGAKUA7I.js.map → chunk-YUQPUK6H.js.map} +0 -0
- /package/dist/{chunk-TRTLWUDD.js.map → chunk-ZTHGFTMW.js.map} +0 -0
package/src/dedup/merge.test.ts
CHANGED
|
@@ -769,7 +769,6 @@ async function harness(
|
|
|
769
769
|
registerFactContentHash: async (id: string, hash: string, _expectedContent: string) => {
|
|
770
770
|
calls.hashRegistrations.push({ id, hash });
|
|
771
771
|
},
|
|
772
|
-
readCasRevision: async (p: string) => (p === targetPath ? casRevision : undefined),
|
|
773
772
|
readCasRevisionStatus: async (p: string): Promise<CasRevisionReadStatus> => {
|
|
774
773
|
if (p !== targetPath) return { status: "absent" };
|
|
775
774
|
if (overrides.receiptStatus === "unavailable") {
|
|
@@ -3460,7 +3459,10 @@ async function postEffectsHarness(options: {
|
|
|
3460
3459
|
: null,
|
|
3461
3460
|
// #2807: static token — these fixtures drive the rollback paths, not
|
|
3462
3461
|
// ownership attribution.
|
|
3463
|
-
|
|
3462
|
+
readCasRevisionStatus: async (): Promise<CasRevisionReadStatus> => ({
|
|
3463
|
+
status: "present",
|
|
3464
|
+
revision: "2026-08-20T00:00:00.000Z",
|
|
3465
|
+
}),
|
|
3464
3466
|
} as unknown as StorageManager;
|
|
3465
3467
|
const deps = {
|
|
3466
3468
|
config: parseConfig({ memoryDir: dir }),
|
|
@@ -3729,6 +3731,12 @@ test("runMergedTargetPostEffects: a writer committing mid-rebuild keeps its edge
|
|
|
3729
3731
|
content: buildGraphEdgeCalls > 0 ? NEWER_BODY : MERGED,
|
|
3730
3732
|
}
|
|
3731
3733
|
: null,
|
|
3734
|
+
// #2807: B's mid-rebuild commit mints a NEW token — the identity the
|
|
3735
|
+
// revision guard compares (public `updated` no longer carries it).
|
|
3736
|
+
readCasRevisionStatus: async (): Promise<CasRevisionReadStatus> =>
|
|
3737
|
+
buildGraphEdgeCalls > 0
|
|
3738
|
+
? { status: "present", revision: "2026-08-22T01:00:00.001Z" }
|
|
3739
|
+
: { status: "present", revision: "2026-08-20T00:00:00.000Z" },
|
|
3732
3740
|
} as unknown as StorageManager;
|
|
3733
3741
|
const deps = {
|
|
3734
3742
|
...h.deps,
|
|
@@ -3800,8 +3808,10 @@ test("runMergedTargetPostEffects: a stale writer's rollback preserves the newer
|
|
|
3800
3808
|
: null,
|
|
3801
3809
|
// #2807: B's mid-rebuild commit mints a NEW token — the identity the
|
|
3802
3810
|
// revision guard compares (public `updated` no longer carries it).
|
|
3803
|
-
|
|
3804
|
-
buildStarted
|
|
3811
|
+
readCasRevisionStatus: async (): Promise<CasRevisionReadStatus> =>
|
|
3812
|
+
buildStarted
|
|
3813
|
+
? { status: "present", revision: "2026-08-22T01:00:00.001Z" }
|
|
3814
|
+
: { status: "present", revision: "2026-08-20T00:00:00.000Z" },
|
|
3805
3815
|
} as unknown as StorageManager;
|
|
3806
3816
|
const graphConfig = parseConfig({ memoryDir: h.dir });
|
|
3807
3817
|
const graphIndex = new GraphIndex(h.dir, graphConfig);
|
|
@@ -3849,6 +3859,170 @@ test("runMergedTargetPostEffects: a stale writer's rollback preserves the newer
|
|
|
3849
3859
|
);
|
|
3850
3860
|
});
|
|
3851
3861
|
|
|
3862
|
+
// ── #2837: the revision guard must FAIL CLOSED. The old guard read the
|
|
3863
|
+
// receipt fail-open (readCasRevision), so a rewrite whose BOTH sidecar reads
|
|
3864
|
+
// were unavailable compared undefined against undefined, passed, and
|
|
3865
|
+
// installed replacement edges no receipt ever verified. ────────────────────
|
|
3866
|
+
|
|
3867
|
+
test("runMergedTargetPostEffects: both receipt reads unavailable skips the graph rewrite instead of installing unchecked (#2837)", async () => {
|
|
3868
|
+
const h = await postEffectsHarness();
|
|
3869
|
+
const OTHER = "facts/2026-08-19/other.md";
|
|
3870
|
+
const priorEntity: GraphEdge = { from: h.targetRelPath, to: OTHER, type: "entity", weight: 1, label: "entity-billing-service", ts: "2026-08-19T00:00:00.000Z" };
|
|
3871
|
+
await seedGraphFile(h.dir, "entity", [priorEntity]);
|
|
3872
|
+
let built = false;
|
|
3873
|
+
const storage = {
|
|
3874
|
+
dir: h.dir,
|
|
3875
|
+
getMemoryByIdIncludingArchived: async (id: string) =>
|
|
3876
|
+
id === "fact-target"
|
|
3877
|
+
? {
|
|
3878
|
+
path: path.join(h.dir, h.targetRelPath),
|
|
3879
|
+
frontmatter: { id, category: "fact", entityRef: "entity-billing-service" },
|
|
3880
|
+
content: MERGED,
|
|
3881
|
+
}
|
|
3882
|
+
: null,
|
|
3883
|
+
// The fail-open collapse the OLD guard trusted: an unreadable sidecar
|
|
3884
|
+
// reads as undefined on both sides of the remove-and-rebuild.
|
|
3885
|
+
readCasRevision: async () => undefined,
|
|
3886
|
+
readCasRevisionStatus: async (): Promise<CasRevisionReadStatus> => ({
|
|
3887
|
+
status: "unavailable",
|
|
3888
|
+
reason: "simulated sidecar outage",
|
|
3889
|
+
}),
|
|
3890
|
+
} as unknown as StorageManager;
|
|
3891
|
+
const deps = {
|
|
3892
|
+
...h.deps,
|
|
3893
|
+
buildGraphEdge: async () => {
|
|
3894
|
+
built = true;
|
|
3895
|
+
},
|
|
3896
|
+
} as unknown as ExtractionPersistDeps;
|
|
3897
|
+
const graphContext = { allMemsForGraph: [], memoryPathById: new Map(), previousPersistedRelPath: "facts/2026-08-18/prev.md" };
|
|
3898
|
+
await runMergedTargetPostEffects(
|
|
3899
|
+
deps,
|
|
3900
|
+
storage,
|
|
3901
|
+
{ targetId: "fact-target", mergedContent: MERGED },
|
|
3902
|
+
{
|
|
3903
|
+
category: "fact",
|
|
3904
|
+
incomingContent: INCOMING,
|
|
3905
|
+
incomingConfidence: 0.9,
|
|
3906
|
+
namespace: "default",
|
|
3907
|
+
graphCaps: ALL_GRAPH_CAPS,
|
|
3908
|
+
graphContext,
|
|
3909
|
+
threadIdForEdge: undefined,
|
|
3910
|
+
threadEpisodeIdsForGraph: undefined,
|
|
3911
|
+
},
|
|
3912
|
+
);
|
|
3913
|
+
assert.equal(built, false, "the graph effect must be SKIPPED while receipt identity is unavailable");
|
|
3914
|
+
assert.deepEqual(
|
|
3915
|
+
await readGraphFile(h.dir, "entity"),
|
|
3916
|
+
[priorEntity],
|
|
3917
|
+
"the prior edges survive untouched — nothing was removed for an install that could never be verified",
|
|
3918
|
+
);
|
|
3919
|
+
assert.equal(graphContext.previousPersistedRelPath, "facts/2026-08-18/prev.md", "a skipped rewrite must not advance the adjacency chain");
|
|
3920
|
+
});
|
|
3921
|
+
|
|
3922
|
+
test("runMergedTargetPostEffects: a receipt that becomes unavailable mid-rebuild rolls the install back (#2837)", async () => {
|
|
3923
|
+
const h = await postEffectsHarness();
|
|
3924
|
+
const OTHER = "facts/2026-08-19/other.md";
|
|
3925
|
+
const priorEntity: GraphEdge = { from: h.targetRelPath, to: OTHER, type: "entity", weight: 1, label: "entity-billing-service", ts: "2026-08-19T00:00:00.000Z" };
|
|
3926
|
+
await seedGraphFile(h.dir, "entity", [priorEntity]);
|
|
3927
|
+
let buildStarted = false;
|
|
3928
|
+
const storage = {
|
|
3929
|
+
dir: h.dir,
|
|
3930
|
+
getMemoryByIdIncludingArchived: async (id: string) =>
|
|
3931
|
+
id === "fact-target"
|
|
3932
|
+
? {
|
|
3933
|
+
path: path.join(h.dir, h.targetRelPath),
|
|
3934
|
+
frontmatter: { id, category: "fact", entityRef: "entity-billing-service" },
|
|
3935
|
+
content: MERGED,
|
|
3936
|
+
}
|
|
3937
|
+
: null,
|
|
3938
|
+
readCasRevision: async () => undefined,
|
|
3939
|
+
readCasRevisionStatus: async (): Promise<CasRevisionReadStatus> =>
|
|
3940
|
+
buildStarted
|
|
3941
|
+
? { status: "unavailable", reason: "simulated transient sidecar read failure" }
|
|
3942
|
+
: { status: "present", revision: "2026-08-20T00:00:00.000Z" },
|
|
3943
|
+
} as unknown as StorageManager;
|
|
3944
|
+
const rebuilt: GraphEdge = { from: h.targetRelPath, to: OTHER, type: "entity", weight: 1, label: "entity-billing-service-v3", ts: "2026-08-23T00:00:00.000Z" };
|
|
3945
|
+
const deps = {
|
|
3946
|
+
...h.deps,
|
|
3947
|
+
buildGraphEdge: async () => {
|
|
3948
|
+
buildStarted = true;
|
|
3949
|
+
await appendEdge(h.dir, { ...rebuilt });
|
|
3950
|
+
return [{ ...rebuilt }];
|
|
3951
|
+
},
|
|
3952
|
+
} as unknown as ExtractionPersistDeps;
|
|
3953
|
+
const graphContext = { allMemsForGraph: [], memoryPathById: new Map(), previousPersistedRelPath: "facts/2026-08-18/prev.md" };
|
|
3954
|
+
await runMergedTargetPostEffects(
|
|
3955
|
+
deps,
|
|
3956
|
+
storage,
|
|
3957
|
+
{ targetId: "fact-target", mergedContent: MERGED },
|
|
3958
|
+
{
|
|
3959
|
+
category: "fact",
|
|
3960
|
+
incomingContent: INCOMING,
|
|
3961
|
+
incomingConfidence: 0.9,
|
|
3962
|
+
namespace: "default",
|
|
3963
|
+
graphCaps: ALL_GRAPH_CAPS,
|
|
3964
|
+
graphContext,
|
|
3965
|
+
threadIdForEdge: undefined,
|
|
3966
|
+
threadEpisodeIdsForGraph: undefined,
|
|
3967
|
+
},
|
|
3968
|
+
);
|
|
3969
|
+
assert.deepEqual(
|
|
3970
|
+
await readGraphFile(h.dir, "entity"),
|
|
3971
|
+
[priorEntity],
|
|
3972
|
+
"an unverifiable post-build receipt must roll the remove-and-rebuild back to the prior edges",
|
|
3973
|
+
);
|
|
3974
|
+
assert.equal(graphContext.previousPersistedRelPath, "facts/2026-08-18/prev.md", "a rolled-back rewrite must not advance the adjacency chain");
|
|
3975
|
+
});
|
|
3976
|
+
test("runMergedTargetPostEffects: a genuinely absent receipt on both reads still installs (#2837)", async () => {
|
|
3977
|
+
const h = await postEffectsHarness();
|
|
3978
|
+
const OTHER = "facts/2026-08-19/other.md";
|
|
3979
|
+
const priorEntity: GraphEdge = { from: h.targetRelPath, to: OTHER, type: "entity", weight: 1, label: "entity-billing-service", ts: "2026-08-19T00:00:00.000Z" };
|
|
3980
|
+
await seedGraphFile(h.dir, "entity", [priorEntity]);
|
|
3981
|
+
const storage = {
|
|
3982
|
+
dir: h.dir,
|
|
3983
|
+
getMemoryByIdIncludingArchived: async (id: string) =>
|
|
3984
|
+
id === "fact-target"
|
|
3985
|
+
? {
|
|
3986
|
+
path: path.join(h.dir, h.targetRelPath),
|
|
3987
|
+
frontmatter: { id, category: "fact", entityRef: "entity-billing-service" },
|
|
3988
|
+
content: MERGED,
|
|
3989
|
+
}
|
|
3990
|
+
: null,
|
|
3991
|
+
readCasRevision: async () => undefined,
|
|
3992
|
+
readCasRevisionStatus: async (): Promise<CasRevisionReadStatus> => ({ status: "absent" }),
|
|
3993
|
+
} as unknown as StorageManager;
|
|
3994
|
+
const rebuilt: GraphEdge = { from: h.targetRelPath, to: OTHER, type: "entity", weight: 1, label: "entity-billing-service-v3", ts: "2026-08-23T00:00:00.000Z" };
|
|
3995
|
+
const deps = {
|
|
3996
|
+
...h.deps,
|
|
3997
|
+
buildGraphEdge: async () => {
|
|
3998
|
+
await appendEdge(h.dir, { ...rebuilt });
|
|
3999
|
+
return [{ ...rebuilt }];
|
|
4000
|
+
},
|
|
4001
|
+
} as unknown as ExtractionPersistDeps;
|
|
4002
|
+
const graphContext = { allMemsForGraph: [], memoryPathById: new Map(), previousPersistedRelPath: "facts/2026-08-18/prev.md" };
|
|
4003
|
+
await runMergedTargetPostEffects(
|
|
4004
|
+
deps,
|
|
4005
|
+
storage,
|
|
4006
|
+
{ targetId: "fact-target", mergedContent: MERGED },
|
|
4007
|
+
{
|
|
4008
|
+
category: "fact",
|
|
4009
|
+
incomingContent: INCOMING,
|
|
4010
|
+
incomingConfidence: 0.9,
|
|
4011
|
+
namespace: "default",
|
|
4012
|
+
graphCaps: ALL_GRAPH_CAPS,
|
|
4013
|
+
graphContext,
|
|
4014
|
+
threadIdForEdge: undefined,
|
|
4015
|
+
threadEpisodeIdsForGraph: undefined,
|
|
4016
|
+
},
|
|
4017
|
+
);
|
|
4018
|
+
assert.deepEqual(
|
|
4019
|
+
await readGraphFile(h.dir, "entity"),
|
|
4020
|
+
[rebuilt],
|
|
4021
|
+
"a target that never minted a receipt (pre-sidecar legacy) still installs its rebuilt edges",
|
|
4022
|
+
);
|
|
4023
|
+
assert.equal(graphContext.previousPersistedRelPath, h.targetRelPath, "the verified install advances the batch's adjacency chain");
|
|
4024
|
+
});
|
|
4025
|
+
|
|
3852
4026
|
// ── Round N+16 (A): the N+14 surgical rollback leaned on build()'s RETURN
|
|
3853
4027
|
// value. A build that THROWS mid-append (the entity rows landed, a later
|
|
3854
4028
|
// append failed) loses that return, and the rollback fell back to the
|
|
@@ -4194,11 +4368,12 @@ test("applySemanticMergeAtPersist: a timestamp-less retirement between the conte
|
|
|
4194
4368
|
// identity the rollback compares.
|
|
4195
4369
|
const receipt = h.calls.commitRevisions[0];
|
|
4196
4370
|
assert.ok(receipt, "the content CAS stamped a receipt");
|
|
4197
|
-
const
|
|
4198
|
-
assert.ok(
|
|
4371
|
+
const standingStatus = await h.storage.readCasRevisionStatus(h.target.path);
|
|
4372
|
+
assert.ok(standingStatus.status === "present", "the retirement minted a standing receipt");
|
|
4373
|
+
const standingToken = standingStatus.status === "present" ? standingStatus.revision : undefined;
|
|
4199
4374
|
assert.notEqual(standingToken, receipt, "the timestamp-less retirement retired the CAS's receipt token");
|
|
4200
4375
|
assert.ok(
|
|
4201
|
-
new Date(standingToken).getTime() > new Date(receipt).getTime(),
|
|
4376
|
+
standingToken !== undefined && new Date(standingToken).getTime() > new Date(receipt).getTime(),
|
|
4202
4377
|
"the timestamp-less retirement advanced the token strictly past the CAS receipt",
|
|
4203
4378
|
);
|
|
4204
4379
|
assert.deepEqual(
|
|
@@ -4324,7 +4499,9 @@ test("applySemanticMergeAtPersist: the monotonic receipt survives the metadata p
|
|
|
4324
4499
|
T.toISOString(),
|
|
4325
4500
|
"#2807: the metadata patch stamps business time (mergePatch.updated) verbatim — never a revision",
|
|
4326
4501
|
);
|
|
4327
|
-
const
|
|
4502
|
+
const standingRead = await h.storage.readCasRevisionStatus(h.target.path);
|
|
4503
|
+
assert.equal(standingRead.status, "present", "the standing receipt is readable after the metadata patches");
|
|
4504
|
+
const standingToken = standingRead.status === "present" ? standingRead.revision : undefined;
|
|
4328
4505
|
const [aReceipt, bReceipt, cReceipt2] = h.calls.commitRevisions;
|
|
4329
4506
|
assert.ok(aReceipt && bReceipt && cReceipt2);
|
|
4330
4507
|
assert.ok(aReceipt < bReceipt && bReceipt < cReceipt2, "A, B, C receipts are unique and strictly increasing");
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
2
|
import { test } from "node:test";
|
|
3
|
-
import { applySpanMode } from "./extraction-span-wire.js";
|
|
3
|
+
import { applySpanMode, extractionProviderJsonSchema } from "./extraction-span-wire.js";
|
|
4
4
|
|
|
5
5
|
const TEXT = "Alice moved to Seattle";
|
|
6
6
|
|
|
@@ -18,3 +18,19 @@ test("applySpanMode: enabled slices half-open [start, end)", () => {
|
|
|
18
18
|
end: 11,
|
|
19
19
|
});
|
|
20
20
|
});
|
|
21
|
+
|
|
22
|
+
function factProperties(spanMode: "off" | "shadow" | "on") {
|
|
23
|
+
const schema = extractionProviderJsonSchema(spanMode);
|
|
24
|
+
const properties = schema.properties as
|
|
25
|
+
| Record<string, { items?: { properties?: Record<string, unknown> } }>
|
|
26
|
+
| undefined;
|
|
27
|
+
return properties?.facts?.items?.properties ?? {};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
test("provider schema omits span in off mode and keeps it in on/shadow (issue #2952)", () => {
|
|
31
|
+
assert.equal("span" in factProperties("off"), false);
|
|
32
|
+
assert.equal("span" in factProperties("on"), true);
|
|
33
|
+
assert.equal("span" in factProperties("shadow"), true);
|
|
34
|
+
assert.deepEqual(extractionProviderJsonSchema("on"), extractionProviderJsonSchema("shadow"));
|
|
35
|
+
assert.notEqual(extractionProviderJsonSchema("off"), extractionProviderJsonSchema("on"));
|
|
36
|
+
});
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Extraction seam for span mode (issue #2333 leftover slice).
|
|
2
|
+
* Extraction seam for span mode (issue #2333 leftover slice + #2952).
|
|
3
3
|
*
|
|
4
|
-
* Default off. Callers pass `enabled
|
|
5
|
-
*
|
|
4
|
+
* Default off. Callers pass `enabled` to `applySpanMode`. The provider JSON
|
|
5
|
+
* schema is mode-aware so off-mode structured output cannot advertise `span`.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
import { zodToJsonSchema } from "zod-to-json-schema";
|
|
9
|
+
import type { ExtractionSpanConfig } from "./extraction-span-config.js";
|
|
8
10
|
import { parseSpanOffsets, type ParsedSpan } from "./extraction-span.js";
|
|
11
|
+
import { ExtractionProviderOutputSchema } from "./schemas.js";
|
|
9
12
|
|
|
10
13
|
export function applySpanMode(opts: {
|
|
11
14
|
enabled?: boolean | 0 | 1;
|
|
@@ -15,3 +18,43 @@ export function applySpanMode(opts: {
|
|
|
15
18
|
}): ParsedSpan {
|
|
16
19
|
return parseSpanOffsets(opts.text, { start: opts.start, end: opts.end }, opts.enabled);
|
|
17
20
|
}
|
|
21
|
+
|
|
22
|
+
const JSON_SCHEMA_OPTS = { $refStrategy: "none" as const, effectStrategy: "input" as const };
|
|
23
|
+
|
|
24
|
+
const EXTRACTION_PROVIDER_JSON_SCHEMA_WITH_SPAN = zodToJsonSchema(
|
|
25
|
+
ExtractionProviderOutputSchema,
|
|
26
|
+
JSON_SCHEMA_OPTS,
|
|
27
|
+
) as Record<string, unknown>;
|
|
28
|
+
|
|
29
|
+
type FactItems = {
|
|
30
|
+
properties?: Record<string, unknown>;
|
|
31
|
+
required?: string[];
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
function omitFactSpan(schema: Record<string, unknown>): Record<string, unknown> {
|
|
35
|
+
const clone = structuredClone(schema) as {
|
|
36
|
+
properties?: { facts?: { items?: FactItems } };
|
|
37
|
+
};
|
|
38
|
+
const items = clone.properties?.facts?.items;
|
|
39
|
+
if (!items?.properties || !("span" in items.properties)) {
|
|
40
|
+
throw new Error("extraction provider schema is missing facts.items.properties.span");
|
|
41
|
+
}
|
|
42
|
+
delete items.properties.span;
|
|
43
|
+
if (Array.isArray(items.required)) {
|
|
44
|
+
items.required = items.required.filter((key) => key !== "span");
|
|
45
|
+
}
|
|
46
|
+
return clone as Record<string, unknown>;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const EXTRACTION_PROVIDER_JSON_SCHEMA_WITHOUT_SPAN = omitFactSpan(
|
|
50
|
+
EXTRACTION_PROVIDER_JSON_SCHEMA_WITH_SPAN,
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
/** Provider JSON schema: `span` is present in on/shadow, absent in off. */
|
|
54
|
+
export function extractionProviderJsonSchema(
|
|
55
|
+
spanMode: ExtractionSpanConfig["spanMode"] = "off",
|
|
56
|
+
): Record<string, unknown> {
|
|
57
|
+
return spanMode === "off"
|
|
58
|
+
? EXTRACTION_PROVIDER_JSON_SCHEMA_WITHOUT_SPAN
|
|
59
|
+
: EXTRACTION_PROVIDER_JSON_SCHEMA_WITH_SPAN;
|
|
60
|
+
}
|
package/src/extraction.ts
CHANGED
|
@@ -7,7 +7,6 @@ import { shouldRunProactivePass } from "./proactive-contention.js";
|
|
|
7
7
|
import { FallbackLlmClient, fallbackLlmRuntimeContextFromConfig, gatewayTaskChainOptions } from "./fallback-llm.js";
|
|
8
8
|
import { revalidateDependentsViaLlm, type RevalidationFastChatCompletion } from "./orchestration/dependency-revalidation.js";
|
|
9
9
|
import {
|
|
10
|
-
ExtractionProviderOutputSchema,
|
|
11
10
|
ExtractionResultSchema,
|
|
12
11
|
ConsolidationResultSchema,
|
|
13
12
|
IdentityConsolidationResultSchema,
|
|
@@ -53,9 +52,9 @@ import {
|
|
|
53
52
|
eventTimePromptInstruction,
|
|
54
53
|
spanModePromptSection,
|
|
55
54
|
} from "./extraction-prompt.js";
|
|
56
|
-
import { zodToJsonSchema } from "zod-to-json-schema";
|
|
57
55
|
import { containsExtractionPlaceholder, extractionAttributes, extractionCueAnchors, extractionText, isPlainRecord } from "./extraction-normalization.js";
|
|
58
56
|
import { applyExtractionSpanMaterialization, bindLocalExtractionPrompt, buildSpanMaterializeTurns, stripUntrustedFactSpans, truncateLocalExtractionConversation, type SpanMaterializeTurn } from "./extraction-span-materialize.js";
|
|
57
|
+
import { extractionProviderJsonSchema } from "./extraction-span-wire.js";
|
|
59
58
|
import { resolveLocalLlmCapabilities, resolveMemoryLifecycleCapabilities, resolvePipelineProcessingCapabilities, resolveRecallAuxiliaryCapabilities } from "./capabilities.js";
|
|
60
59
|
type ExtractionQuestion = ExtractionResult["questions"][number];
|
|
61
60
|
type ExtractedFactResult = ExtractionResult["facts"][number];
|
|
@@ -64,14 +63,6 @@ type ExtractedRelationshipResult = NonNullable<ExtractionResult["relationships"]
|
|
|
64
63
|
|
|
65
64
|
const PROACTIVE_MIN_CONFIDENCE = 0.8;
|
|
66
65
|
|
|
67
|
-
const EXTRACTION_PROVIDER_JSON_SCHEMA = zodToJsonSchema(
|
|
68
|
-
ExtractionProviderOutputSchema,
|
|
69
|
-
{
|
|
70
|
-
$refStrategy: "none",
|
|
71
|
-
effectStrategy: "input",
|
|
72
|
-
},
|
|
73
|
-
) as Record<string, unknown>;
|
|
74
|
-
|
|
75
66
|
function extractionEntityType(value: unknown): ExtractedEntityResult["type"] | undefined {
|
|
76
67
|
const type = extractionText(value);
|
|
77
68
|
if (
|
|
@@ -1353,7 +1344,7 @@ export class ExtractionEngine {
|
|
|
1353
1344
|
timeoutMs: this.config.taskLlmTimeoutMs ?? this.config.localLlmTimeoutMs,
|
|
1354
1345
|
jsonSchema: {
|
|
1355
1346
|
name: "extraction_result",
|
|
1356
|
-
schema:
|
|
1347
|
+
schema: extractionProviderJsonSchema(this.config.extraction?.spanMode ?? "off"),
|
|
1357
1348
|
strict: false,
|
|
1358
1349
|
},
|
|
1359
1350
|
signal,
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import test from "node:test";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
applyRecencyBoost,
|
|
6
|
+
assessMtimeTrust,
|
|
7
|
+
collectMtimeFallbackContext,
|
|
8
|
+
resolveMemoryAge,
|
|
9
|
+
utcDayKey,
|
|
10
|
+
} from "./memory-age.js";
|
|
11
|
+
|
|
12
|
+
const DAY_MS = 86_400_000;
|
|
13
|
+
const NOW = Date.parse("2026-08-25T12:00:00.000Z");
|
|
14
|
+
|
|
15
|
+
function spreadDays(count: number, startMs = NOW): number[] {
|
|
16
|
+
return Array.from({ length: count }, (_unused, i) => startMs + i * DAY_MS);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
test("resolveMemoryAge: content dates win over a fresh mtime (#2976)", () => {
|
|
20
|
+
const created = new Date(NOW - 40 * DAY_MS).toISOString();
|
|
21
|
+
const resolved = resolveMemoryAge({ created });
|
|
22
|
+
assert.equal(resolved.source, "content");
|
|
23
|
+
assert.equal(resolved.referenceMs, Date.parse(created), "ages by the content date, not the fresh mtime");
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test("resolveMemoryAge: created wins over updated; updated backs a missing created", () => {
|
|
27
|
+
const created = new Date(NOW - 90 * DAY_MS).toISOString();
|
|
28
|
+
const updated = new Date(NOW - 1 * DAY_MS).toISOString();
|
|
29
|
+
assert.equal(resolveMemoryAge({ created, updated }).referenceMs, Date.parse(created));
|
|
30
|
+
assert.equal(resolveMemoryAge({ created: undefined, updated }).referenceMs, Date.parse(updated));
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test("resolveMemoryAge: unparsable content dates never produce a reference", () => {
|
|
34
|
+
for (const bad of ["not-a-date", "", "2026-13-45"] as const) {
|
|
35
|
+
const resolved = resolveMemoryAge({ created: bad, updated: bad });
|
|
36
|
+
assert.equal(resolved.referenceMs, null, `unparsable date ${JSON.stringify(bad)} must not resolve`);
|
|
37
|
+
assert.equal(resolved.source, "unknown");
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test("resolveMemoryAge: mtime backs age only when trusted and no content date exists", () => {
|
|
42
|
+
const mtimeMs = NOW - 2 * DAY_MS;
|
|
43
|
+
const trusted = assessMtimeTrust([mtimeMs, NOW - 9 * DAY_MS]);
|
|
44
|
+
assert.equal(resolveMemoryAge({}, mtimeMs, trusted).source, "mtime");
|
|
45
|
+
assert.equal(resolveMemoryAge({ created: "garbage" }, mtimeMs, trusted).source, "mtime");
|
|
46
|
+
assert.equal(resolveMemoryAge({ created: new Date(NOW).toISOString() }, mtimeMs, trusted).source, "content");
|
|
47
|
+
const distrusted = assessMtimeTrust([mtimeMs, mtimeMs, NOW - 9 * DAY_MS, NOW - 11 * DAY_MS]);
|
|
48
|
+
assert.equal(distrusted.bulkTouchDays.has(utcDayKey(mtimeMs)), true, "two of four same-day files cluster");
|
|
49
|
+
const refused = resolveMemoryAge({}, mtimeMs, distrusted);
|
|
50
|
+
assert.equal(refused.source, "unknown", "an untrusted mtime is unknown age, never fresh");
|
|
51
|
+
assert.equal(refused.referenceMs, null);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test("assessMtimeTrust: the measured distill-kura shape (50 of 214 files on one day) is a bulk touch", () => {
|
|
55
|
+
const bulkDay = NOW - 400 * DAY_MS;
|
|
56
|
+
const sample = [...Array<number>(50).fill(bulkDay), ...spreadDays(164, NOW - 200 * DAY_MS)];
|
|
57
|
+
const trust = assessMtimeTrust(sample);
|
|
58
|
+
assert.equal(trust.bulkTouchDays.size, 1);
|
|
59
|
+
assert.equal(trust.bulkTouchDays.has(utcDayKey(bulkDay)), true);
|
|
60
|
+
assert.equal(trust.isTrusted(bulkDay), false);
|
|
61
|
+
assert.equal(trust.isTrusted(sample[sample.length - 1]!), true);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test("assessMtimeTrust: the 20% boundary is inclusive", () => {
|
|
65
|
+
// 43 of 214 = 20.09% -> flagged; 42 of 214 = 19.6% -> not flagged.
|
|
66
|
+
const flagged = assessMtimeTrust([...Array<number>(43).fill(NOW), ...spreadDays(171, NOW - 400 * DAY_MS)]);
|
|
67
|
+
assert.equal(flagged.bulkTouchDays.has(utcDayKey(NOW)), true, "43/214 on one day is >= 20%");
|
|
68
|
+
const clear = assessMtimeTrust([...Array<number>(42).fill(NOW), ...spreadDays(172, NOW - 400 * DAY_MS)]);
|
|
69
|
+
assert.equal(clear.bulkTouchDays.has(utcDayKey(NOW)), false, "42/214 on one day is < 20%");
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test("assessMtimeTrust: a single file never forms a cluster (min 2)", () => {
|
|
73
|
+
const trust = assessMtimeTrust([NOW]);
|
|
74
|
+
assert.equal(trust.bulkTouchDays.size, 0);
|
|
75
|
+
assert.equal(trust.isTrusted(NOW), true);
|
|
76
|
+
const pair = assessMtimeTrust([NOW, NOW]);
|
|
77
|
+
assert.equal(pair.bulkTouchDays.size, 1, "two files on one day meet the min-2 rule");
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test("applyRecencyBoost: exact blend, unknown age untouched, weight 0 untouched, future clamped", () => {
|
|
81
|
+
const memory = {
|
|
82
|
+
path: "facts/a.md",
|
|
83
|
+
frontmatter: { created: new Date(NOW - 14 * DAY_MS).toISOString() },
|
|
84
|
+
};
|
|
85
|
+
const expected = 1 * 0.5 + Math.pow(0.5, 14 / 7) * 0.5;
|
|
86
|
+
assert.ok(Math.abs(applyRecencyBoost(1, memory, 0.5, NOW) - expected) < 1e-12);
|
|
87
|
+
// Unknown age (no dates, no trusted mtime) leaves the score alone.
|
|
88
|
+
assert.equal(applyRecencyBoost(0.7, { path: "facts/b.md", frontmatter: {} }, 0.5, NOW), 0.7);
|
|
89
|
+
assert.equal(applyRecencyBoost(0.7, memory, 0, NOW), 0.7);
|
|
90
|
+
// A corrupt future date clamps to age zero instead of boosting past 1.
|
|
91
|
+
const future = {
|
|
92
|
+
path: "facts/c.md",
|
|
93
|
+
frontmatter: { created: new Date(NOW + 30 * DAY_MS).toISOString() },
|
|
94
|
+
};
|
|
95
|
+
assert.equal(applyRecencyBoost(1, future, 0.5, NOW), 1);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test("collectMtimeFallbackContext: fully dated memories skip it entirely (no stats)", async () => {
|
|
99
|
+
// The paths do not exist: any stat would surface below as a null mtime
|
|
100
|
+
// instead of the documented null short-circuit.
|
|
101
|
+
const context = await collectMtimeFallbackContext([
|
|
102
|
+
{ path: "/nonexistent/facts/a.md", created: new Date(NOW).toISOString() },
|
|
103
|
+
{ path: "/nonexistent/facts/b.md", updated: new Date(NOW).toISOString() },
|
|
104
|
+
]);
|
|
105
|
+
assert.equal(context, null);
|
|
106
|
+
});
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared memory-age resolution (issue #2976).
|
|
3
|
+
*
|
|
4
|
+
* "Age is not mtime" (distill-kura `weave.py` `age_days`): `cp -r`, a
|
|
5
|
+
* restore, a checkout, or a bulk import resets file mtimes, so any freshness
|
|
6
|
+
* mechanism keyed on mtime either switches itself off or boosts everything
|
|
7
|
+
* equally. Remnic therefore resolves a memory's age from dates written
|
|
8
|
+
* INSIDE the memory first:
|
|
9
|
+
*
|
|
10
|
+
* 1. frontmatter `created` — the recall ranking reference before #2976,
|
|
11
|
+
* kept first so every dated memory scores exactly as it did;
|
|
12
|
+
* 2. frontmatter `updated` — the content-revision date;
|
|
13
|
+
* 3. file mtime — only when no content date exists AND the caller's trust
|
|
14
|
+
* assessment accepts it (see `assessMtimeTrust`);
|
|
15
|
+
* 4. otherwise UNKNOWN: no reference instant, and downstream scoring must
|
|
16
|
+
* treat unknown age as never-fresh (no recency boost), never as
|
|
17
|
+
* "just written".
|
|
18
|
+
*
|
|
19
|
+
* Bi-temporal `valid_at`/`observedAt` are deliberately NOT in the ladder:
|
|
20
|
+
* they date when the fact was true, not when the memory was written, so a
|
|
21
|
+
* fresh correction about an old event would be misaged by them.
|
|
22
|
+
*
|
|
23
|
+
* Bulk-touch heuristic (distill-kura measured 50/214 files sharing one
|
|
24
|
+
* bulk-touch day, mtime understating true age by a median of 11 days, worst
|
|
25
|
+
* case 425): a UTC calendar day shared by >= max(BULK_TOUCH_MIN_FILES,
|
|
26
|
+
* BULK_TOUCH_STORE_FRACTION of the sampled files) is a bulk-touch day, and
|
|
27
|
+
* mtimes falling on a bulk-touch day are untrusted — those ages are unknown,
|
|
28
|
+
* not fresh. The sample is the scored result set of the calling pipeline
|
|
29
|
+
* (bounded I/O), which observes a bulk-touched store as a same-day cluster.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
import { stat } from "node:fs/promises";
|
|
33
|
+
|
|
34
|
+
/** Minimum files sharing one calendar day before that day is bulk-touched. */
|
|
35
|
+
export const BULK_TOUCH_MIN_FILES = 2;
|
|
36
|
+
/** Fraction of the sampled files on one day that marks a bulk touch. */
|
|
37
|
+
export const BULK_TOUCH_STORE_FRACTION = 0.2;
|
|
38
|
+
|
|
39
|
+
/** Recency half-life (days) used by `applyRecencyBoost`. */
|
|
40
|
+
export const RECENCY_HALF_LIFE_DAYS = 7;
|
|
41
|
+
|
|
42
|
+
/** How the resolved age reference was obtained. */
|
|
43
|
+
export type MemoryAgeSource = "content" | "mtime" | "unknown";
|
|
44
|
+
|
|
45
|
+
export interface ResolvedMemoryAge {
|
|
46
|
+
/** Reference instant for age computations, or null when age is unknown. */
|
|
47
|
+
referenceMs: number | null;
|
|
48
|
+
source: MemoryAgeSource;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Content dates written inside the memory (frontmatter). */
|
|
52
|
+
export interface MemoryAgeDates {
|
|
53
|
+
created?: string | null;
|
|
54
|
+
updated?: string | null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Minimal memory shape the resolver needs; `path` keys the mtime fallback. */
|
|
58
|
+
export interface MemoryAgeSubject extends MemoryAgeDates {
|
|
59
|
+
path: string;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** MemoryFile slice `applyRecencyBoost` reads; dates live in frontmatter. */
|
|
63
|
+
export interface RecencyBoostSubject {
|
|
64
|
+
path: string;
|
|
65
|
+
frontmatter: MemoryAgeDates;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Result of assessing a sample of mtimes for bulk-touch clustering. */
|
|
69
|
+
export interface MtimeTrust {
|
|
70
|
+
/** UTC day keys ("YYYY-MM-DD") whose mtime counts mark a bulk touch. */
|
|
71
|
+
bulkTouchDays: ReadonlySet<string>;
|
|
72
|
+
isTrusted(mtimeMs: number): boolean;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Per-recall mtime fallback context (null when no scored memory needs it). */
|
|
76
|
+
export interface MtimeFallbackContext {
|
|
77
|
+
mtimeMsByPath: Map<string, number | null>;
|
|
78
|
+
trust: MtimeTrust;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function finiteMs(value: string | null | undefined): number | null {
|
|
82
|
+
if (typeof value !== "string") return null;
|
|
83
|
+
const ms = Date.parse(value);
|
|
84
|
+
return Number.isFinite(ms) ? ms : null;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Resolve the reference instant for a memory's age. Content dates win over
|
|
89
|
+
* mtime; an mtime is consulted only when no content date parses and the
|
|
90
|
+
* trust assessment (when provided) accepts it; otherwise age is unknown.
|
|
91
|
+
*/
|
|
92
|
+
export function resolveMemoryAge(
|
|
93
|
+
subject: MemoryAgeDates,
|
|
94
|
+
mtimeMs?: number | null,
|
|
95
|
+
trust?: MtimeTrust | null,
|
|
96
|
+
): ResolvedMemoryAge {
|
|
97
|
+
const contentMs = finiteMs(subject.created) ?? finiteMs(subject.updated);
|
|
98
|
+
if (contentMs !== null) return { referenceMs: contentMs, source: "content" };
|
|
99
|
+
if (
|
|
100
|
+
typeof mtimeMs === "number" &&
|
|
101
|
+
Number.isFinite(mtimeMs) &&
|
|
102
|
+
trust?.isTrusted(mtimeMs) !== false
|
|
103
|
+
) {
|
|
104
|
+
return { referenceMs: mtimeMs, source: "mtime" };
|
|
105
|
+
}
|
|
106
|
+
return { referenceMs: null, source: "unknown" };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** UTC calendar-day key ("YYYY-MM-DD") for a millisecond instant. */
|
|
110
|
+
export function utcDayKey(ms: number): string {
|
|
111
|
+
return new Date(ms).toISOString().slice(0, 10);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Assess a sample of mtimes: days shared by >= max(BULK_TOUCH_MIN_FILES,
|
|
116
|
+
* BULK_TOUCH_STORE_FRACTION of the sample) are bulk-touch days, and their
|
|
117
|
+
* mtimes are untrusted for age. An empty or scattered sample trusts nothing
|
|
118
|
+
* less than it did before — only clustering changes anything.
|
|
119
|
+
*/
|
|
120
|
+
export function assessMtimeTrust(sampleMtimesMs: readonly number[]): MtimeTrust {
|
|
121
|
+
const counts = new Map<string, number>();
|
|
122
|
+
for (const ms of sampleMtimesMs) {
|
|
123
|
+
if (!Number.isFinite(ms)) continue;
|
|
124
|
+
const key = utcDayKey(ms);
|
|
125
|
+
counts.set(key, (counts.get(key) ?? 0) + 1);
|
|
126
|
+
}
|
|
127
|
+
const threshold = Math.max(
|
|
128
|
+
BULK_TOUCH_MIN_FILES,
|
|
129
|
+
sampleMtimesMs.length * BULK_TOUCH_STORE_FRACTION,
|
|
130
|
+
);
|
|
131
|
+
const bulkTouchDays = new Set<string>();
|
|
132
|
+
for (const [day, count] of counts) {
|
|
133
|
+
if (count >= threshold) bulkTouchDays.add(day);
|
|
134
|
+
}
|
|
135
|
+
return {
|
|
136
|
+
bulkTouchDays,
|
|
137
|
+
isTrusted: (mtimeMs: number) => Number.isFinite(mtimeMs) && !bulkTouchDays.has(utcDayKey(mtimeMs)),
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Build the mtime fallback context for a recall boost pass. Returns null —
|
|
143
|
+
* with zero stat calls — when every scored memory already has a content
|
|
144
|
+
* date, so the common path pays nothing. Otherwise stats the scored files
|
|
145
|
+
* (bounded by the result count) and assesses their mtimes for bulk-touch
|
|
146
|
+
* clustering. Unreadable files map to null mtimes (unknown age, never fresh).
|
|
147
|
+
*/
|
|
148
|
+
export async function collectMtimeFallbackContext(
|
|
149
|
+
memories: ReadonlyArray<MemoryAgeSubject>,
|
|
150
|
+
): Promise<MtimeFallbackContext | null> {
|
|
151
|
+
const needsFallback = memories.some(
|
|
152
|
+
(m) => finiteMs(m.created) === null && finiteMs(m.updated) === null,
|
|
153
|
+
);
|
|
154
|
+
if (!needsFallback) return null;
|
|
155
|
+
const stats = await Promise.all(
|
|
156
|
+
memories.map(async (m) => {
|
|
157
|
+
try {
|
|
158
|
+
return (await stat(m.path)).mtimeMs;
|
|
159
|
+
} catch {
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
}),
|
|
163
|
+
);
|
|
164
|
+
const mtimeMsByPath = new Map<string, number | null>(
|
|
165
|
+
memories.map((m, i) => [m.path, stats[i] ?? null]),
|
|
166
|
+
);
|
|
167
|
+
const trust = assessMtimeTrust(
|
|
168
|
+
stats.filter((ms): ms is number => ms !== null && Number.isFinite(ms)),
|
|
169
|
+
);
|
|
170
|
+
return { mtimeMsByPath, trust };
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Apply the exponential recency blend to one score. Age resolves per
|
|
175
|
+
* `resolveMemoryAge`; unknown age leaves the score untouched (never fresh).
|
|
176
|
+
* A future reference instant clamps to age zero rather than boosting above
|
|
177
|
+
* the recency ceiling (corrupt dates are not super-fresh).
|
|
178
|
+
*/
|
|
179
|
+
export function applyRecencyBoost(
|
|
180
|
+
score: number,
|
|
181
|
+
memory: RecencyBoostSubject,
|
|
182
|
+
recencyWeight: number,
|
|
183
|
+
nowMs: number,
|
|
184
|
+
fallback?: MtimeFallbackContext | null,
|
|
185
|
+
): number {
|
|
186
|
+
if (recencyWeight <= 0) return score;
|
|
187
|
+
const resolved = resolveMemoryAge(
|
|
188
|
+
memory.frontmatter,
|
|
189
|
+
fallback?.mtimeMsByPath.get(memory.path) ?? null,
|
|
190
|
+
fallback?.trust,
|
|
191
|
+
);
|
|
192
|
+
if (resolved.referenceMs === null) return score;
|
|
193
|
+
const ageDays = Math.max(0, (nowMs - resolved.referenceMs) / 86_400_000);
|
|
194
|
+
const recencyScore = Math.pow(0.5, ageDays / RECENCY_HALF_LIFE_DAYS);
|
|
195
|
+
return score * (1 - recencyWeight) + recencyScore * recencyWeight;
|
|
196
|
+
}
|