@remnic/core 9.69.48 → 9.69.50
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 +15 -14
- package/dist/access-admin-ops-surface.js +36 -34
- package/dist/access-authorization-probe.d.ts +15 -14
- package/dist/access-authorization-probe.js +37 -35
- package/dist/access-boundary.d.ts +15 -14
- package/dist/access-boundary.js +36 -34
- package/dist/access-cli.js +114 -112
- package/dist/access-cli.js.map +1 -1
- package/dist/access-coding-context-resolution.d.ts +2 -1
- package/dist/access-extraction-force-flush.d.ts +15 -14
- package/dist/access-extraction-force-flush.js +36 -34
- package/dist/access-health-types.d.ts +2 -1
- package/dist/access-http-briefing.d.ts +15 -14
- package/dist/access-http-briefing.js +37 -35
- package/dist/access-http-lcm-compaction.d.ts +15 -14
- package/dist/access-http-lifecycle-flush.d.ts +15 -14
- package/dist/access-http-offline-stream.d.ts +15 -14
- package/dist/access-http-query.d.ts +3 -2
- package/dist/access-http-query.js +38 -36
- package/dist/access-http.d.ts +16 -15
- package/dist/access-http.js +57 -55
- package/dist/access-identity-continuity-surface.d.ts +12 -11
- package/dist/access-identity-continuity-surface.js +36 -34
- package/dist/access-lcm-surface.d.ts +15 -14
- package/dist/access-lcm-surface.js +36 -34
- package/dist/access-mcp-shared-context-tools.d.ts +15 -14
- package/dist/access-mcp.d.ts +15 -14
- package/dist/access-mcp.js +46 -44
- package/dist/access-memory-search-fanout.d.ts +3 -2
- package/dist/{access-namespace-preflight-CetF9ziA.d.ts → access-namespace-preflight-C1q3XuAc.d.ts} +1 -1
- package/dist/access-namespace-preflight.d.ts +4 -3
- package/dist/access-namespace-preflight.js +36 -34
- package/dist/access-observe-transcript.d.ts +12 -11
- package/dist/access-observe-write-category.d.ts +4 -3
- package/dist/access-observe-write-category.js +4 -4
- package/dist/access-observe-write-surface.d.ts +15 -14
- package/dist/access-observe-write-surface.js +36 -34
- package/dist/access-offline-manifest.d.ts +12 -11
- package/dist/access-operations-batch.js +38 -36
- package/dist/access-operations.d.ts +15 -14
- package/dist/access-operations.js +45 -43
- package/dist/access-recall-concurrency.d.ts +15 -14
- package/dist/access-recall-concurrency.js +36 -34
- package/dist/access-recall-response.d.ts +15 -14
- package/dist/access-recall-response.js +36 -34
- package/dist/access-recall-surface.d.ts +15 -14
- package/dist/access-recall-surface.js +36 -34
- package/dist/access-schema.d.ts +2 -1
- package/dist/access-schema.js +3 -3
- package/dist/{access-service-DUkTWyZS.d.ts → access-service-BZaC-RZw.d.ts} +9 -9
- package/dist/access-service-helpers.d.ts +15 -14
- package/dist/access-service.d.ts +15 -14
- package/dist/access-service.js +36 -34
- package/dist/access-surface-catalog.d.ts +15 -14
- package/dist/access-surface-catalog.js +38 -36
- package/dist/access-surface-catalog.js.map +1 -1
- package/dist/access-wearables-meetings-surface.d.ts +5 -4
- package/dist/action-confidence.d.ts +2 -1
- package/dist/active-memory-bridge.d.ts +2 -1
- package/dist/active-recall.d.ts +2 -1
- package/dist/active-recall.js +3 -2
- package/dist/active-recall.js.map +1 -1
- package/dist/activity/digest.d.ts +2 -1
- package/dist/adapters/index.js +4 -4
- package/dist/adapters/registry.js +2 -2
- package/dist/ambient-provenance.d.ts +2 -1
- package/dist/artifact-search.d.ts +2 -1
- package/dist/background-generation.d.ts +2 -1
- package/dist/behavior-learner.d.ts +2 -1
- package/dist/behavior-signals.d.ts +2 -1
- package/dist/bootstrap.d.ts +12 -11
- package/dist/briefing-window.d.ts +3 -2
- package/dist/briefing.d.ts +3 -2
- package/dist/briefing.js +12 -12
- package/dist/buffer-surprise-report.d.ts +2 -1
- package/dist/buffer-turn-helpers.d.ts +2 -1
- package/dist/buffer.d.ts +3 -2
- package/dist/buffer.js +5 -5
- package/dist/bulk-import/index.d.ts +4 -3
- package/dist/calibration.d.ts +2 -1
- package/dist/capabilities.d.ts +2 -1
- package/dist/{catalog-Bns9zpl3.d.ts → catalog-DK2eHhax.d.ts} +1 -1
- package/dist/causal-behavior.d.ts +2 -1
- package/dist/causal-consolidation.d.ts +2 -1
- package/dist/causal-consolidation.js +13 -13
- package/dist/causal-trajectory-graph.d.ts +2 -1
- package/dist/{chunk-DE4ZGH2N.js → chunk-2IVHOQRY.js} +8 -3
- package/dist/chunk-2IVHOQRY.js.map +1 -0
- package/dist/{chunk-CD7VTZIG.js → chunk-3MIZ2EKQ.js} +2 -2
- package/dist/{chunk-PXJ6HE2W.js → chunk-3RABJOGD.js} +2 -2
- package/dist/{chunk-B4WI7OF2.js → chunk-3RZB7TYL.js} +2 -2
- package/dist/{chunk-XYGINDIJ.js → chunk-3SI3FFTW.js} +2 -2
- package/dist/{chunk-WNMVUWI7.js → chunk-3UQIS2GC.js} +2 -2
- package/dist/{chunk-XZUHXKM3.js → chunk-42BV46XS.js} +2 -2
- package/dist/{chunk-BMBAA2M5.js → chunk-4FWNMN5R.js} +4 -4
- package/dist/chunk-4KQIWY4A.js +197 -0
- package/dist/chunk-4KQIWY4A.js.map +1 -0
- package/dist/{chunk-2B2RHLHI.js → chunk-4PXFLDMJ.js} +2 -2
- package/dist/{chunk-7DU2F7LM.js → chunk-4SDO74N2.js} +6 -6
- package/dist/{chunk-32ZBIGID.js → chunk-56C7OO3N.js} +14 -14
- package/dist/{chunk-OS4JVTHR.js → chunk-6525YIYL.js} +3 -3
- package/dist/{chunk-X2NFFBTO.js → chunk-A7GQBJP5.js} +4 -4
- package/dist/{chunk-BIXELUIR.js → chunk-AL7OLBL7.js} +2 -2
- package/dist/{chunk-EGOWY342.js → chunk-AYGWJ2UK.js} +2 -2
- package/dist/{chunk-JUOJGZPV.js → chunk-AYP2FZFA.js} +2 -2
- package/dist/{chunk-GGROFIYX.js → chunk-BHI5ILU5.js} +2 -2
- package/dist/{chunk-LIAM7U2J.js → chunk-BOTSR5S3.js} +2 -2
- package/dist/{chunk-6A6HSXWV.js → chunk-C3PKRFZ4.js} +2 -2
- package/dist/{chunk-MNG536IS.js → chunk-D2XUSZ24.js} +19 -19
- package/dist/{chunk-T3T5HTZZ.js → chunk-D6WX5FEF.js} +2 -2
- package/dist/{chunk-YXWMZAAN.js → chunk-D7QX26FF.js} +2 -2
- package/dist/{chunk-55XWRL6F.js → chunk-DH536WT2.js} +2 -2
- package/dist/{chunk-VV5GCNKG.js → chunk-DNO6QWCB.js} +2 -2
- package/dist/{chunk-P3YNEMYY.js → chunk-E64HBW7M.js} +2 -2
- package/dist/{chunk-LIDJWAQV.js → chunk-E73SVGJA.js} +2 -2
- package/dist/{chunk-XXLH6KFO.js → chunk-ECLY4NZA.js} +2 -2
- package/dist/{chunk-B6VDWDFN.js → chunk-EUP4AXIW.js} +2 -2
- package/dist/{chunk-DRLZB3CN.js → chunk-EZV43XBD.js} +2 -2
- package/dist/{chunk-2WMXZYMS.js → chunk-F5V7KRQS.js} +2 -2
- package/dist/{chunk-SS33RR4F.js → chunk-FO6IK4IF.js} +8 -8
- package/dist/{chunk-645WOU5I.js → chunk-H7BZBSP6.js} +4 -4
- package/dist/{chunk-ZEIJQ4I3.js → chunk-HRCWDO6S.js} +2 -2
- package/dist/{chunk-FGRJJBFO.js → chunk-IC3PM3YO.js} +2 -2
- package/dist/{chunk-KQAFEZQX.js → chunk-IPLYGWQF.js} +5 -5
- package/dist/{chunk-R32AEEU3.js → chunk-IST6MBYC.js} +56 -56
- package/dist/{chunk-MKJFPIVG.js → chunk-IYHHCSF6.js} +47 -47
- package/dist/{chunk-JYXLW22X.js → chunk-JG4AP36X.js} +2 -2
- package/dist/{chunk-QGAKUOKU.js → chunk-JHUODJRW.js} +2 -2
- package/dist/{chunk-XKHRNAK7.js → chunk-JIWZVOQM.js} +2 -2
- package/dist/{chunk-PA67GXYJ.js → chunk-JOP7W24J.js} +8 -8
- package/dist/{chunk-SK6HEQTW.js → chunk-JTPAXDSX.js} +2 -2
- package/dist/{chunk-Y5VT7BIT.js → chunk-K4KE5MYE.js} +2 -2
- package/dist/{chunk-TQSCR5DU.js → chunk-KG2OOAFF.js} +6 -6
- package/dist/{chunk-PPJLJD2D.js → chunk-KOEDSRF5.js} +2 -2
- package/dist/{chunk-3RKVSECD.js → chunk-LR7KUQ2W.js} +7 -7
- package/dist/{chunk-WL3PGPGU.js → chunk-MECKA6G2.js} +2 -2
- package/dist/{chunk-MKLDZNBD.js → chunk-ML5FW2RO.js} +2 -2
- package/dist/{chunk-KVIMQAER.js → chunk-MQMZO23A.js} +2 -2
- package/dist/{chunk-HG74GEVY.js → chunk-MVMNEU65.js} +2 -2
- package/dist/{chunk-OSTXIFZW.js → chunk-OLJYDGGP.js} +46 -4
- package/dist/chunk-OLJYDGGP.js.map +1 -0
- package/dist/{chunk-4LPIVP77.js → chunk-OP5IBPHP.js} +2 -2
- package/dist/{chunk-KPYMORPQ.js → chunk-OQZYHCWM.js} +209 -46
- package/dist/chunk-OQZYHCWM.js.map +1 -0
- package/dist/{chunk-7YUX52XZ.js → chunk-PA5EOTRR.js} +3 -3
- package/dist/{chunk-QB4VXCFN.js → chunk-PMLFPVYJ.js} +2 -2
- package/dist/{chunk-LYMJ36UI.js → chunk-PPNFKQ6D.js} +2 -2
- package/dist/{chunk-WQW6KPVC.js → chunk-PQWICTRO.js} +1 -1
- package/dist/chunk-PQWICTRO.js.map +1 -0
- package/dist/{chunk-QMWKENNK.js → chunk-Q4AUIFUA.js} +4 -4
- package/dist/{chunk-MUNR43V3.js → chunk-QSP4Y73Q.js} +2 -2
- package/dist/{chunk-JIURESYF.js → chunk-QXQCPNKA.js} +7 -7
- package/dist/{chunk-TW4MXM7H.js → chunk-QZDUNK6V.js} +6 -6
- package/dist/{chunk-WYU66IVZ.js → chunk-SRMBMMTK.js} +3 -3
- package/dist/{chunk-IO7K7GES.js → chunk-TZ44E732.js} +2 -2
- package/dist/chunk-UD6W67JM.js +260 -0
- package/dist/chunk-UD6W67JM.js.map +1 -0
- package/dist/{chunk-5KBI2MOC.js → chunk-UI6R2YUB.js} +1 -1
- package/dist/{chunk-V7IONNTJ.js → chunk-UVEBBXB5.js} +2 -2
- package/dist/{chunk-I6IJDTRP.js → chunk-VBRS7Z3O.js} +2 -2
- package/dist/{chunk-XEHLYA5Z.js → chunk-W6V7JN2E.js} +3 -3
- package/dist/{chunk-NNFB4CU4.js → chunk-X6B2QWXW.js} +2 -2
- package/dist/{chunk-BNX2TUSF.js → chunk-XTMABIFW.js} +2 -2
- package/dist/{chunk-QLCMOHRE.js → chunk-XUKUMBP3.js} +2 -2
- package/dist/{chunk-EZ25VE3G.js → chunk-YNDLCWXS.js} +4 -4
- package/dist/{chunk-EWBDJQ5Y.js → chunk-ZBAQMSEY.js} +3 -3
- package/dist/{chunk-MQT3WP3F.js → chunk-ZUNYFDI4.js} +2 -2
- package/dist/{cli-Bbndoc6k.d.ts → cli-BHDMJqJS.d.ts} +5 -5
- package/dist/cli.d.ts +17 -16
- package/dist/cli.js +82 -79
- package/dist/coding/export-okf-codegraph.d.ts +2 -1
- package/dist/coding/export-okf-codegraph.js +14 -14
- package/dist/coding/pre-action-gate.d.ts +2 -1
- package/dist/compounding/engine.d.ts +3 -2
- package/dist/compounding/engine.js +13 -13
- package/dist/compounding/preference-consolidator.d.ts +2 -1
- package/dist/compression-optimizer.d.ts +2 -1
- package/dist/config.d.ts +2 -1
- package/dist/config.js +3 -2
- package/dist/connectors/codex-materialize-runner.d.ts +2 -1
- package/dist/connectors/codex-materialize-runner.js +12 -12
- package/dist/connectors/codex-materialize.d.ts +2 -1
- package/dist/connectors/index.d.ts +2 -1
- package/dist/connectors/index.js +12 -12
- package/dist/consolidation-provenance-check.d.ts +3 -2
- package/dist/consolidation-undo.d.ts +3 -2
- package/dist/contradiction/index.d.ts +5 -4
- package/dist/converge-config.d.ts +2 -1
- package/dist/convergence-refresh.d.ts +3 -2
- package/dist/convergence-refresh.js +14 -14
- package/dist/conversation-index/backend.d.ts +2 -1
- package/dist/conversation-index/chunker.d.ts +2 -1
- package/dist/conversation-index/faiss-adapter.d.ts +2 -1
- package/dist/conversation-index/indexer.d.ts +2 -1
- package/dist/conversation-index/search.d.ts +2 -1
- package/dist/corpus-watermark.d.ts +2 -1
- package/dist/corpus-watermark.js +14 -14
- package/dist/cross-lingual-recall.d.ts +2 -1
- package/dist/dashboard-runtime.js +2 -2
- package/dist/day-summary.d.ts +2 -1
- package/dist/deep-recall-http-glue.d.ts +15 -14
- package/dist/deep-recall-http-glue.js +4 -4
- package/dist/deep-recall-mcp-tools.d.ts +15 -14
- package/dist/deep-recall-policy-llm.d.ts +3 -2
- package/dist/deep-recall-seeds.d.ts +2 -1
- package/dist/delinearize.d.ts +2 -1
- package/dist/dependency-propagation-config.d.ts +2 -1
- package/dist/{dependency-propagation-delivery-B-LvcDaq.d.ts → dependency-propagation-delivery-BMkR4gmY.d.ts} +1 -1
- package/dist/direct-answer-wiring.d.ts +2 -1
- package/dist/direct-answer.d.ts +2 -1
- package/dist/directory-sidecars.d.ts +87 -0
- package/dist/directory-sidecars.js +22 -0
- package/dist/directory-sidecars.js.map +1 -0
- package/dist/embedding-fallback.d.ts +2 -1
- package/dist/enrichment/index.d.ts +2 -1
- package/dist/entity-origin-fields.d.ts +2 -1
- package/dist/entity-retrieval.d.ts +3 -2
- package/dist/entity-retrieval.js +13 -13
- package/dist/entity-schema.d.ts +2 -1
- package/dist/episodic-context.d.ts +2 -1
- package/dist/eval-trajectory-contracts.d.ts +2 -1
- package/dist/eval-trajectory-store.d.ts +2 -1
- package/dist/eval-trajectory.d.ts +2 -1
- package/dist/event-order-recall.js +2 -2
- package/dist/explicit-capture.d.ts +12 -11
- package/dist/external-wiki-access.d.ts +15 -14
- package/dist/external-wiki-access.js +37 -35
- package/dist/external-wiki-collection-registration.d.ts +2 -1
- package/dist/external-wiki-collection.d.ts +2 -1
- package/dist/external-wiki-mcp-tools.d.ts +15 -14
- package/dist/extraction-error-classification.d.ts +2 -1
- package/dist/extraction-evidence-class.d.ts +2 -1
- package/dist/extraction-faithfulness.d.ts +3 -2
- package/dist/extraction-judge-telemetry.d.ts +3 -2
- package/dist/extraction-judge-training.d.ts +3 -2
- package/dist/extraction-judge.d.ts +3 -2
- package/dist/extraction-liveness.d.ts +2 -1
- package/dist/extraction-normalization.d.ts +2 -1
- package/dist/extraction-prompt.d.ts +2 -1
- package/dist/extraction-source-grounding-rules.d.ts +2 -1
- package/dist/extraction-source-grounding.d.ts +2 -1
- package/dist/extraction-span-config.d.ts +2 -1
- package/dist/extraction-span-materialize.d.ts +2 -1
- package/dist/extraction-span-wire.d.ts +2 -1
- package/dist/extraction.d.ts +3 -2
- package/dist/extraction.js +16 -16
- package/dist/fallback-llm-codex-subscription.d.ts +2 -1
- package/dist/fallback-llm.d.ts +2 -1
- package/dist/{generic-recall-paths-Bfc7zigS.d.ts → generic-recall-paths-BCRtXU32.d.ts} +1 -1
- package/dist/graph-append-error.d.ts +2 -1
- package/dist/graph-dashboard-diff.d.ts +2 -1
- package/dist/graph-dashboard-key.d.ts +2 -1
- package/dist/graph-dashboard-parser.d.ts +2 -1
- package/dist/graph-edge-reinforcement.d.ts +2 -1
- package/dist/graph-jsonl.d.ts +2 -1
- package/dist/graph-path-reconstruction.d.ts +2 -1
- package/dist/graph-path-scoring.d.ts +2 -1
- package/dist/graph-snapshot.d.ts +2 -1
- package/dist/graph.d.ts +2 -1
- package/dist/harmonic-retrieval.js +1 -1
- package/dist/importance.d.ts +2 -1
- package/dist/importers/index.d.ts +2 -1
- package/dist/importers/test-utils.d.ts +2 -1
- package/dist/in-flight-reads.d.ts +2 -1
- package/dist/{index-C7IA1DxB.d.ts → index-DMNTW4W6.d.ts} +3 -3
- package/dist/index.d.ts +43 -42
- package/dist/index.js +182 -145
- package/dist/intent.d.ts +2 -1
- package/dist/lcm/engine.d.ts +2 -1
- package/dist/lcm/engine.js +2 -2
- package/dist/lcm/index.d.ts +2 -1
- package/dist/lcm/index.js +7 -7
- package/dist/lcm/tools.d.ts +2 -1
- package/dist/lifecycle.d.ts +2 -1
- package/dist/live-connectors-runner.d.ts +2 -1
- package/dist/{local-llm-DH4tWtck.d.ts → local-llm-BXYC-mbU.d.ts} +1 -1
- package/dist/local-llm-helpers.d.ts +3 -2
- package/dist/local-llm.d.ts +3 -2
- package/dist/local-model-endpoint.d.ts +2 -1
- package/dist/location/index.d.ts +6 -5
- package/dist/location/index.js +14 -14
- package/dist/maintenance/memory-governance.d.ts +2 -1
- package/dist/maintenance/memory-governance.js +12 -12
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.d.ts +3 -2
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +12 -12
- package/dist/maintenance/rebuild-memory-projection.d.ts +3 -2
- package/dist/maintenance/rebuild-memory-projection.js +13 -13
- package/dist/{maintenance-n6yeTQhR.d.ts → maintenance-zJiLsKBY.d.ts} +3 -3
- package/dist/mcp-memory-inspector-app.d.ts +15 -14
- package/dist/memory-action-policy.d.ts +2 -1
- package/dist/memory-browse-http-glue.d.ts +15 -14
- package/dist/memory-browse-mcp-tools.d.ts +15 -14
- package/dist/memory-browse-operations.js +37 -35
- package/dist/memory-browse.d.ts +2 -1
- package/dist/memory-browse.js +7 -5
- package/dist/memory-cache.d.ts +2 -1
- package/dist/memory-controller.d.ts +2 -1
- package/dist/memory-lifecycle-ledger-utils.d.ts +2 -1
- package/dist/memory-projection-store.d.ts +2 -1
- package/dist/memory-provenance.d.ts +2 -1
- package/dist/memory-snapshot.d.ts +2 -1
- package/dist/memory-subject-cli.d.ts +15 -14
- package/dist/memory-subject.d.ts +15 -14
- package/dist/memory-worth-outcomes.d.ts +3 -2
- package/dist/models-json.d.ts +2 -1
- package/dist/namespaces/migrate.d.ts +4 -3
- package/dist/namespaces/migrate.js +13 -13
- package/dist/namespaces/principal.d.ts +2 -1
- package/dist/namespaces/search.d.ts +2 -1
- package/dist/namespaces/search.js +6 -6
- package/dist/namespaces/storage.d.ts +4 -3
- package/dist/namespaces/storage.js +12 -12
- package/dist/native-knowledge.d.ts +2 -1
- package/dist/offline-sync-impression-drain.d.ts +2 -1
- package/dist/operator-doctor-corpus.d.ts +2 -1
- package/dist/operator-doctor-corpus.js +15 -15
- package/dist/operator-doctor-replica.d.ts +2 -1
- package/dist/operator-toolkit.d.ts +4 -3
- package/dist/operator-toolkit.js +23 -22
- package/dist/orchestration/compression-guideline-coordinator.d.ts +3 -2
- package/dist/orchestration/maintenance.d.ts +5 -4
- package/dist/orchestration/maintenance.js +20 -20
- package/dist/{orchestrator-Cirjo3ZB.d.ts → orchestrator-BTqT3F6X.d.ts} +10 -10
- package/dist/orchestrator.d.ts +12 -11
- package/dist/orchestrator.js +114 -112
- package/dist/patterns-cli.d.ts +2 -1
- package/dist/{pipeline-DK79FrgH.d.ts → pipeline-CgO2Sd4O.d.ts} +1 -1
- package/dist/policy-runtime.d.ts +2 -1
- package/dist/proactive-contention.d.ts +3 -2
- package/dist/provenance.d.ts +2 -1
- package/dist/{public-http-DaHHhWRR.d.ts → public-http-Dk1EA9ix.d.ts} +1 -1
- package/dist/{qmd-BT0ErlRS.d.ts → qmd-CwxtVz64.d.ts} +1 -1
- package/dist/qmd-preflight.d.ts +3 -2
- package/dist/qmd-recall-cache.d.ts +2 -1
- package/dist/qmd.d.ts +3 -2
- package/dist/qmd.js +4 -4
- package/dist/recall-concurrency-config.d.ts +2 -1
- package/dist/recall-disclosure-escalation.d.ts +2 -1
- package/dist/recall-explain-renderer.d.ts +2 -1
- package/dist/recall-explain-renderer.js +3 -3
- package/dist/recall-memory-map.d.ts +2 -1
- package/dist/recall-navigate-access.d.ts +2 -1
- package/dist/recall-navigate-access.js +2 -2
- package/dist/recall-navigate-disclosure.d.ts +2 -1
- package/dist/recall-navigate-disclosure.js +2 -2
- package/dist/recall-navigate.d.ts +2 -1
- package/dist/recall-navigate.js +2 -2
- package/dist/recall-navigation-http-glue.d.ts +15 -14
- package/dist/recall-navigation-mcp-tools.d.ts +15 -14
- package/dist/recall-navigation-operations.js +37 -35
- package/dist/recall-navigation-renderer.d.ts +2 -1
- package/dist/recall-navigation.d.ts +2 -1
- package/dist/recall-navigation.js +36 -34
- package/dist/recall-planner-llm.d.ts +2 -1
- package/dist/recall-recognition-tier.d.ts +121 -0
- package/dist/recall-recognition-tier.js +42 -0
- package/dist/recall-recognition-tier.js.map +1 -0
- package/dist/recall-state-view-admission.d.ts +3 -2
- package/dist/recall-state-view-admission.js +8 -5
- package/dist/recall-state-view-anchors.d.ts +2 -1
- package/dist/recall-state.d.ts +2 -1
- package/dist/recall-tag-filter.d.ts +2 -1
- package/dist/recall-timings.d.ts +2 -1
- package/dist/recall-xray-cli.d.ts +2 -1
- package/dist/recall-xray-cli.js +4 -4
- package/dist/recall-xray-renderer.d.ts +2 -1
- package/dist/recall-xray-renderer.js +3 -3
- package/dist/recall-xray.d.ts +2 -1
- package/dist/recall-xray.js +2 -2
- package/dist/reconcile/cursor.d.ts +2 -1
- package/dist/reconcile/manifest.d.ts +2 -1
- package/dist/reconcile/plan.d.ts +2 -1
- package/dist/replay/normalizers/chatgpt.d.ts +2 -1
- package/dist/replay/normalizers/claude.d.ts +2 -1
- package/dist/replay/normalizers/openclaw.d.ts +2 -1
- package/dist/replay/normalizers/shared.d.ts +2 -1
- package/dist/replay/runner.d.ts +2 -1
- package/dist/replay/types.d.ts +2 -1
- package/dist/replica-divergence.d.ts +2 -1
- package/dist/replica-peers-config.d.ts +2 -1
- package/dist/resolve-auth-token.d.ts +2 -1
- package/dist/resume-bundles.js +4 -3
- package/dist/retrieval-agents.d.ts +3 -2
- package/dist/retrieval-tiers.d.ts +2 -1
- package/dist/routing/engine.d.ts +2 -1
- package/dist/routing/store.d.ts +2 -1
- package/dist/salvage-envelope.d.ts +2 -1
- package/dist/{scope-profiles-BgPQx9iE.d.ts → scope-profiles-Bg1rB-Dp.d.ts} +1 -1
- package/dist/search/embed-helper.d.ts +2 -1
- package/dist/search/factory.d.ts +2 -1
- package/dist/search/factory.js +5 -5
- package/dist/search/index.d.ts +2 -1
- package/dist/search/index.js +9 -9
- package/dist/search/lancedb-backend.d.ts +2 -1
- package/dist/search/meilisearch-backend.d.ts +2 -1
- package/dist/search/noop-backend.d.ts +2 -1
- package/dist/search/orama-backend.d.ts +2 -1
- package/dist/search/port.d.ts +2 -1
- package/dist/search/remote-backend.d.ts +2 -1
- package/dist/{semantic-consolidation-BrAWKVcm.d.ts → semantic-consolidation-DEzRyeyn.d.ts} +1 -1
- package/dist/semantic-consolidation.d.ts +3 -2
- package/dist/semantic-consolidation.js +13 -13
- package/dist/semantic-rule-promotion.js +12 -12
- package/dist/semantic-rule-verifier.d.ts +2 -1
- package/dist/semantic-rule-verifier.js +12 -12
- package/dist/{service-CqHOTg87.d.ts → service-DGEIDOeD.d.ts} +2 -2
- package/dist/session-observer-bands.d.ts +2 -1
- package/dist/session-observer-state.d.ts +2 -1
- package/dist/shared-context/manager.d.ts +2 -1
- package/dist/signal.d.ts +2 -1
- package/dist/source-agent-qualifier.d.ts +2 -1
- package/dist/source-agent-qualifier.js +15 -15
- package/dist/standup/build.js +12 -12
- package/dist/{storage-mD2YQ8_2.d.ts → storage-DNQWtOSM.d.ts} +1 -1
- package/dist/storage.d.ts +3 -2
- package/dist/storage.js +11 -11
- package/dist/subject-config.d.ts +2 -1
- package/dist/summarizer.d.ts +2 -1
- package/dist/summary-snapshot.d.ts +2 -1
- package/dist/support-passport/index.d.ts +18 -17
- package/dist/support-passport/index.js +39 -37
- package/dist/{tagging-CiAVA-ID.d.ts → tagging-CPVwgadt.d.ts} +2 -2
- package/dist/temporal-supersession.d.ts +3 -2
- package/dist/temporal-timeline-recall.d.ts +2 -1
- package/dist/temporal-validity.d.ts +2 -1
- package/dist/threading.d.ts +2 -1
- package/dist/tier-migration.d.ts +3 -2
- package/dist/tier-routing.d.ts +2 -1
- package/dist/topics.d.ts +2 -1
- package/dist/transcript.d.ts +2 -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 +2 -1
- package/dist/transfer/export-okf.js +12 -12
- package/dist/trust-score-stage.d.ts +2 -1
- package/dist/trust-score.d.ts +2 -1
- package/dist/{types-DgDaoY61.d.ts → types-DCB90q3g.d.ts} +21 -1
- package/dist/types.d.ts +2 -1
- package/dist/types.js +1 -1
- package/dist/utility-runtime.d.ts +2 -1
- package/dist/verified-recall.js +12 -12
- package/dist/who-knows-cli.d.ts +15 -14
- package/dist/who-knows-cli.js +13 -13
- package/dist/who-knows.d.ts +15 -14
- package/dist/who-knows.js +12 -12
- package/dist/write-envelope.d.ts +2 -1
- package/package.json +2 -2
- package/src/config.ts +9 -0
- package/src/directory-sidecars.test.ts +268 -0
- package/src/directory-sidecars.ts +383 -0
- package/src/experience/session-experience-config.ts +51 -0
- package/src/experience/session-experience-lifecycle.test.ts +139 -0
- package/src/experience/session-experience.test.ts +233 -0
- package/src/experience/session-experience.ts +240 -0
- package/src/index.ts +1 -0
- package/src/lifecycle/seed-graduation.test.ts +424 -0
- package/src/lifecycle/seed-graduation.ts +376 -0
- package/src/orchestration/generic-recall-paths.ts +5 -1
- package/src/orchestration/session-context.ts +50 -0
- package/src/recall-recognition-tier.test.ts +306 -0
- package/src/recall-recognition-tier.ts +375 -0
- package/src/testing/subjects/seed-graduation.test.ts +128 -0
- package/src/types.ts +5 -0
- package/dist/chunk-DE4ZGH2N.js.map +0 -1
- package/dist/chunk-KPYMORPQ.js.map +0 -1
- package/dist/chunk-OSTXIFZW.js.map +0 -1
- package/dist/chunk-WQW6KPVC.js.map +0 -1
- /package/dist/{chunk-CD7VTZIG.js.map → chunk-3MIZ2EKQ.js.map} +0 -0
- /package/dist/{chunk-PXJ6HE2W.js.map → chunk-3RABJOGD.js.map} +0 -0
- /package/dist/{chunk-B4WI7OF2.js.map → chunk-3RZB7TYL.js.map} +0 -0
- /package/dist/{chunk-XYGINDIJ.js.map → chunk-3SI3FFTW.js.map} +0 -0
- /package/dist/{chunk-WNMVUWI7.js.map → chunk-3UQIS2GC.js.map} +0 -0
- /package/dist/{chunk-XZUHXKM3.js.map → chunk-42BV46XS.js.map} +0 -0
- /package/dist/{chunk-BMBAA2M5.js.map → chunk-4FWNMN5R.js.map} +0 -0
- /package/dist/{chunk-2B2RHLHI.js.map → chunk-4PXFLDMJ.js.map} +0 -0
- /package/dist/{chunk-7DU2F7LM.js.map → chunk-4SDO74N2.js.map} +0 -0
- /package/dist/{chunk-32ZBIGID.js.map → chunk-56C7OO3N.js.map} +0 -0
- /package/dist/{chunk-OS4JVTHR.js.map → chunk-6525YIYL.js.map} +0 -0
- /package/dist/{chunk-X2NFFBTO.js.map → chunk-A7GQBJP5.js.map} +0 -0
- /package/dist/{chunk-BIXELUIR.js.map → chunk-AL7OLBL7.js.map} +0 -0
- /package/dist/{chunk-EGOWY342.js.map → chunk-AYGWJ2UK.js.map} +0 -0
- /package/dist/{chunk-JUOJGZPV.js.map → chunk-AYP2FZFA.js.map} +0 -0
- /package/dist/{chunk-GGROFIYX.js.map → chunk-BHI5ILU5.js.map} +0 -0
- /package/dist/{chunk-LIAM7U2J.js.map → chunk-BOTSR5S3.js.map} +0 -0
- /package/dist/{chunk-6A6HSXWV.js.map → chunk-C3PKRFZ4.js.map} +0 -0
- /package/dist/{chunk-MNG536IS.js.map → chunk-D2XUSZ24.js.map} +0 -0
- /package/dist/{chunk-T3T5HTZZ.js.map → chunk-D6WX5FEF.js.map} +0 -0
- /package/dist/{chunk-YXWMZAAN.js.map → chunk-D7QX26FF.js.map} +0 -0
- /package/dist/{chunk-55XWRL6F.js.map → chunk-DH536WT2.js.map} +0 -0
- /package/dist/{chunk-VV5GCNKG.js.map → chunk-DNO6QWCB.js.map} +0 -0
- /package/dist/{chunk-P3YNEMYY.js.map → chunk-E64HBW7M.js.map} +0 -0
- /package/dist/{chunk-LIDJWAQV.js.map → chunk-E73SVGJA.js.map} +0 -0
- /package/dist/{chunk-XXLH6KFO.js.map → chunk-ECLY4NZA.js.map} +0 -0
- /package/dist/{chunk-B6VDWDFN.js.map → chunk-EUP4AXIW.js.map} +0 -0
- /package/dist/{chunk-DRLZB3CN.js.map → chunk-EZV43XBD.js.map} +0 -0
- /package/dist/{chunk-2WMXZYMS.js.map → chunk-F5V7KRQS.js.map} +0 -0
- /package/dist/{chunk-SS33RR4F.js.map → chunk-FO6IK4IF.js.map} +0 -0
- /package/dist/{chunk-645WOU5I.js.map → chunk-H7BZBSP6.js.map} +0 -0
- /package/dist/{chunk-ZEIJQ4I3.js.map → chunk-HRCWDO6S.js.map} +0 -0
- /package/dist/{chunk-FGRJJBFO.js.map → chunk-IC3PM3YO.js.map} +0 -0
- /package/dist/{chunk-KQAFEZQX.js.map → chunk-IPLYGWQF.js.map} +0 -0
- /package/dist/{chunk-R32AEEU3.js.map → chunk-IST6MBYC.js.map} +0 -0
- /package/dist/{chunk-MKJFPIVG.js.map → chunk-IYHHCSF6.js.map} +0 -0
- /package/dist/{chunk-JYXLW22X.js.map → chunk-JG4AP36X.js.map} +0 -0
- /package/dist/{chunk-QGAKUOKU.js.map → chunk-JHUODJRW.js.map} +0 -0
- /package/dist/{chunk-XKHRNAK7.js.map → chunk-JIWZVOQM.js.map} +0 -0
- /package/dist/{chunk-PA67GXYJ.js.map → chunk-JOP7W24J.js.map} +0 -0
- /package/dist/{chunk-SK6HEQTW.js.map → chunk-JTPAXDSX.js.map} +0 -0
- /package/dist/{chunk-Y5VT7BIT.js.map → chunk-K4KE5MYE.js.map} +0 -0
- /package/dist/{chunk-TQSCR5DU.js.map → chunk-KG2OOAFF.js.map} +0 -0
- /package/dist/{chunk-PPJLJD2D.js.map → chunk-KOEDSRF5.js.map} +0 -0
- /package/dist/{chunk-3RKVSECD.js.map → chunk-LR7KUQ2W.js.map} +0 -0
- /package/dist/{chunk-WL3PGPGU.js.map → chunk-MECKA6G2.js.map} +0 -0
- /package/dist/{chunk-MKLDZNBD.js.map → chunk-ML5FW2RO.js.map} +0 -0
- /package/dist/{chunk-KVIMQAER.js.map → chunk-MQMZO23A.js.map} +0 -0
- /package/dist/{chunk-HG74GEVY.js.map → chunk-MVMNEU65.js.map} +0 -0
- /package/dist/{chunk-4LPIVP77.js.map → chunk-OP5IBPHP.js.map} +0 -0
- /package/dist/{chunk-7YUX52XZ.js.map → chunk-PA5EOTRR.js.map} +0 -0
- /package/dist/{chunk-QB4VXCFN.js.map → chunk-PMLFPVYJ.js.map} +0 -0
- /package/dist/{chunk-LYMJ36UI.js.map → chunk-PPNFKQ6D.js.map} +0 -0
- /package/dist/{chunk-QMWKENNK.js.map → chunk-Q4AUIFUA.js.map} +0 -0
- /package/dist/{chunk-MUNR43V3.js.map → chunk-QSP4Y73Q.js.map} +0 -0
- /package/dist/{chunk-JIURESYF.js.map → chunk-QXQCPNKA.js.map} +0 -0
- /package/dist/{chunk-TW4MXM7H.js.map → chunk-QZDUNK6V.js.map} +0 -0
- /package/dist/{chunk-WYU66IVZ.js.map → chunk-SRMBMMTK.js.map} +0 -0
- /package/dist/{chunk-IO7K7GES.js.map → chunk-TZ44E732.js.map} +0 -0
- /package/dist/{chunk-5KBI2MOC.js.map → chunk-UI6R2YUB.js.map} +0 -0
- /package/dist/{chunk-V7IONNTJ.js.map → chunk-UVEBBXB5.js.map} +0 -0
- /package/dist/{chunk-I6IJDTRP.js.map → chunk-VBRS7Z3O.js.map} +0 -0
- /package/dist/{chunk-XEHLYA5Z.js.map → chunk-W6V7JN2E.js.map} +0 -0
- /package/dist/{chunk-NNFB4CU4.js.map → chunk-X6B2QWXW.js.map} +0 -0
- /package/dist/{chunk-BNX2TUSF.js.map → chunk-XTMABIFW.js.map} +0 -0
- /package/dist/{chunk-QLCMOHRE.js.map → chunk-XUKUMBP3.js.map} +0 -0
- /package/dist/{chunk-EZ25VE3G.js.map → chunk-YNDLCWXS.js.map} +0 -0
- /package/dist/{chunk-EWBDJQ5Y.js.map → chunk-ZBAQMSEY.js.map} +0 -0
- /package/dist/{chunk-MQT3WP3F.js.map → chunk-ZUNYFDI4.js.map} +0 -0
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { chmodSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { mkdir, readdir, readFile, stat } from "node:fs/promises";
|
|
4
|
+
import os from "node:os";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { test } from "node:test";
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
DIRECTORY_SIDECAR_BASENAME,
|
|
10
|
+
DIRECTORY_SIDECAR_MARKER,
|
|
11
|
+
findDirectorySidecarsForQuery,
|
|
12
|
+
loadDirectorySidecar,
|
|
13
|
+
runDirectorySidecarMaintenance,
|
|
14
|
+
} from "./directory-sidecars.js";
|
|
15
|
+
import {
|
|
16
|
+
isGenericRecallExcludedPath,
|
|
17
|
+
isSearchExcludedPath,
|
|
18
|
+
} from "./orchestration/generic-recall-paths.js";
|
|
19
|
+
|
|
20
|
+
function store(): string {
|
|
21
|
+
return mkdtempSync(path.join(os.tmpdir(), "dir-sidecars-"));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async function writeMemory(root: string, rel: string, body: string): Promise<string> {
|
|
25
|
+
const target = path.join(root, rel);
|
|
26
|
+
await mkdir(path.dirname(target), { recursive: true });
|
|
27
|
+
writeFileSync(target, `---\ncategory: fact\n---\n${body}\n`);
|
|
28
|
+
return target;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async function exists(target: string): Promise<boolean> {
|
|
32
|
+
try {
|
|
33
|
+
await stat(target);
|
|
34
|
+
return true;
|
|
35
|
+
} catch {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
test("maintenance writes sidecars bottom-up and skips unchanged on rerun", async () => {
|
|
41
|
+
const root = store();
|
|
42
|
+
try {
|
|
43
|
+
await writeMemory(root, "facts/2026-01-02/fact-1.md", "Telescope calibration log for the north dome.");
|
|
44
|
+
await writeMemory(root, "facts/2026-01-03/fact-2.md", "Guide star acquisition failed twice.");
|
|
45
|
+
await writeMemory(root, "entities/obs-1.md", "The north dome observer rotation schedule.");
|
|
46
|
+
|
|
47
|
+
const first = await runDirectorySidecarMaintenance(root, true);
|
|
48
|
+
const dayDir = path.join(root, "facts", "2026-01-02");
|
|
49
|
+
const monthParent = path.join(root, "facts");
|
|
50
|
+
assert.ok(
|
|
51
|
+
first.written.some((p) => p === path.join(dayDir, DIRECTORY_SIDECAR_BASENAME)),
|
|
52
|
+
"day sidecar written",
|
|
53
|
+
);
|
|
54
|
+
assert.ok(
|
|
55
|
+
first.written.some((p) => p === path.join(monthParent, DIRECTORY_SIDECAR_BASENAME)),
|
|
56
|
+
"parent sidecar written after children (bottom-up)",
|
|
57
|
+
);
|
|
58
|
+
// Bottom-up proof: the day sidecar is written BEFORE the parent.
|
|
59
|
+
const dayIdx = first.written.findIndex((p) => p === path.join(dayDir, DIRECTORY_SIDECAR_BASENAME));
|
|
60
|
+
const parentIdx = first.written.findIndex(
|
|
61
|
+
(p) => p === path.join(monthParent, DIRECTORY_SIDECAR_BASENAME),
|
|
62
|
+
);
|
|
63
|
+
assert.ok(dayIdx < parentIdx, "child sidecar written before parent");
|
|
64
|
+
|
|
65
|
+
const daySidecar = await readFile(path.join(dayDir, DIRECTORY_SIDECAR_BASENAME), "utf8");
|
|
66
|
+
assert.ok(daySidecar.startsWith(DIRECTORY_SIDECAR_MARKER));
|
|
67
|
+
assert.match(daySidecar, /Telescope calibration/);
|
|
68
|
+
// The parent embeds the child directory's abstract.
|
|
69
|
+
const parentSidecar = await readFile(path.join(monthParent, DIRECTORY_SIDECAR_BASENAME), "utf8");
|
|
70
|
+
assert.match(parentSidecar, /2026-01-02/);
|
|
71
|
+
|
|
72
|
+
const second = await runDirectorySidecarMaintenance(root, true);
|
|
73
|
+
assert.deepEqual(second.written, [], "unchanged directories are skipped");
|
|
74
|
+
assert.deepEqual(second.removed, []);
|
|
75
|
+
} finally {
|
|
76
|
+
rmSync(root, { recursive: true, force: true });
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test("overview query answers from the sidecar without reading child memory files", async (t) => {
|
|
81
|
+
const root = store();
|
|
82
|
+
try {
|
|
83
|
+
const dayDir = path.join(root, "facts", "2026-01-02");
|
|
84
|
+
await writeMemory(root, "facts/2026-01-02/fact-1.md", "Telescope calibration log for the north dome.");
|
|
85
|
+
await writeMemory(root, "facts/2026-01-02/fact-2.md", "Mirror alignment drift measured at 3 arcmin.");
|
|
86
|
+
await writeMemory(root, "entities/crew-1.md", "Catering roster for the base kitchen.");
|
|
87
|
+
await runDirectorySidecarMaintenance(root, true);
|
|
88
|
+
|
|
89
|
+
const matches = await findDirectorySidecarsForQuery(root, "telescope calibration");
|
|
90
|
+
assert.ok(matches.length >= 1, "query finds a directory via its sidecar");
|
|
91
|
+
assert.equal(path.dirname(matches[0].dir), path.join(root, "facts"));
|
|
92
|
+
assert.match(matches[0].abstract, /Telescope/i);
|
|
93
|
+
// Catering directory must not outrank the telescope directory.
|
|
94
|
+
assert.ok(!matches.some((m) => m.dir === path.join(root, "entities")));
|
|
95
|
+
|
|
96
|
+
// Strong proof no child file content was read: make children unreadable
|
|
97
|
+
// (stat still works, so freshness holds) and query again.
|
|
98
|
+
if (process.getuid && process.getuid() === 0) {
|
|
99
|
+
t.skip("chmod proof skipped under root");
|
|
100
|
+
} else {
|
|
101
|
+
for (const name of await readdir(dayDir)) {
|
|
102
|
+
if (name.endsWith(".md") && name !== DIRECTORY_SIDECAR_BASENAME) {
|
|
103
|
+
chmodSync(path.join(dayDir, name), 0o000);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
try {
|
|
107
|
+
const stillMatches = await findDirectorySidecarsForQuery(root, "mirror alignment");
|
|
108
|
+
assert.ok(stillMatches.length >= 1);
|
|
109
|
+
assert.match(stillMatches[0].overview, /Mirror alignment/);
|
|
110
|
+
} finally {
|
|
111
|
+
for (const name of await readdir(dayDir)) {
|
|
112
|
+
if (name.endsWith(".md") && name !== DIRECTORY_SIDECAR_BASENAME) {
|
|
113
|
+
chmodSync(path.join(dayDir, name), 0o644);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
} finally {
|
|
119
|
+
rmSync(root, { recursive: true, force: true });
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
test("adding a file invalidates the sidecar and refresh refreshes it deterministically", async () => {
|
|
124
|
+
const root = store();
|
|
125
|
+
try {
|
|
126
|
+
const dayDir = path.join(root, "facts", "2026-01-02");
|
|
127
|
+
await writeMemory(root, "facts/2026-01-02/fact-1.md", "Boiler pressure nominal.");
|
|
128
|
+
await runDirectorySidecarMaintenance(root, true);
|
|
129
|
+
|
|
130
|
+
const before = await loadDirectorySidecar(dayDir, { refresh: false });
|
|
131
|
+
assert.equal(before?.fresh, true, "sidecar is fresh right after maintenance");
|
|
132
|
+
|
|
133
|
+
await writeMemory(root, "facts/2026-01-02/fact-2.md", "Coolant pump replaced with spare unit.");
|
|
134
|
+
|
|
135
|
+
const stale = await loadDirectorySidecar(dayDir, { refresh: false });
|
|
136
|
+
assert.equal(stale?.fresh, false, "adding a file marks the sidecar stale");
|
|
137
|
+
|
|
138
|
+
// Eager maintenance rewrites the stale sidecar.
|
|
139
|
+
const report = await runDirectorySidecarMaintenance(root, true);
|
|
140
|
+
assert.ok(
|
|
141
|
+
report.written.some((p) => p === path.join(dayDir, DIRECTORY_SIDECAR_BASENAME)),
|
|
142
|
+
"stale sidecar rewritten by maintenance",
|
|
143
|
+
);
|
|
144
|
+
const afterEager = await loadDirectorySidecar(dayDir, { refresh: false });
|
|
145
|
+
assert.equal(afterEager?.fresh, true);
|
|
146
|
+
assert.match(afterEager?.overview ?? "", /Coolant pump/);
|
|
147
|
+
|
|
148
|
+
// Lazy refresh also converges after a later direct write, with no maintenance run.
|
|
149
|
+
await writeMemory(root, "facts/2026-01-02/fact-3.md", "Thruster gimbal recalibrated.");
|
|
150
|
+
const lazilyRefreshed = await loadDirectorySidecar(dayDir);
|
|
151
|
+
assert.equal(lazilyRefreshed?.fresh, true);
|
|
152
|
+
assert.match(lazilyRefreshed?.overview ?? "", /Thruster gimbal/);
|
|
153
|
+
} finally {
|
|
154
|
+
rmSync(root, { recursive: true, force: true });
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
test("deleting a child updates the parent and prunes emptied directories", async () => {
|
|
159
|
+
const root = store();
|
|
160
|
+
try {
|
|
161
|
+
const dayDir = path.join(root, "facts", "2026-01-02");
|
|
162
|
+
const fact1 = await writeMemory(root, "facts/2026-01-02/fact-1.md", "First observation run.");
|
|
163
|
+
await writeMemory(root, "facts/2026-01-02/fact-2.md", "Second observation run.");
|
|
164
|
+
await runDirectorySidecarMaintenance(root, true);
|
|
165
|
+
|
|
166
|
+
rmSync(fact1);
|
|
167
|
+
const report = await runDirectorySidecarMaintenance(root, true);
|
|
168
|
+
const daySidecar = await readFile(path.join(dayDir, DIRECTORY_SIDECAR_BASENAME), "utf8");
|
|
169
|
+
assert.doesNotMatch(daySidecar, /First observation/, "deleted child leaves the sidecar");
|
|
170
|
+
assert.match(daySidecar, /Second observation/);
|
|
171
|
+
assert.ok(
|
|
172
|
+
report.written.some((p) => p === path.join(dayDir, DIRECTORY_SIDECAR_BASENAME)),
|
|
173
|
+
"child deletion rewrites the day sidecar",
|
|
174
|
+
);
|
|
175
|
+
assert.ok(
|
|
176
|
+
report.written.some((p) => p === path.join(root, "facts", DIRECTORY_SIDECAR_BASENAME)),
|
|
177
|
+
"child deletion propagates to the parent sidecar",
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
for (const name of await readdir(dayDir)) {
|
|
181
|
+
if (name.endsWith(".md") && name !== DIRECTORY_SIDECAR_BASENAME) {
|
|
182
|
+
rmSync(path.join(dayDir, name));
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
const pruned = await runDirectorySidecarMaintenance(root, true);
|
|
186
|
+
assert.ok(
|
|
187
|
+
pruned.removed.some((p) => p === path.join(dayDir, DIRECTORY_SIDECAR_BASENAME)),
|
|
188
|
+
"emptied directory loses its sidecar",
|
|
189
|
+
);
|
|
190
|
+
assert.equal(await exists(path.join(dayDir, DIRECTORY_SIDECAR_BASENAME)), false);
|
|
191
|
+
} finally {
|
|
192
|
+
rmSync(root, { recursive: true, force: true });
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
test("namespace scoping: lookup stays inside the requested namespace", async () => {
|
|
197
|
+
const root = store();
|
|
198
|
+
try {
|
|
199
|
+
await writeMemory(root, "namespaces/alpha/facts/2026-05-01/a-1.md", "Alpha wing telescope mirror audit.");
|
|
200
|
+
await writeMemory(root, "namespaces/beta/facts/2026-05-01/b-1.md", "Beta wing telescope mirror audit.");
|
|
201
|
+
const report = await runDirectorySidecarMaintenance(root, true);
|
|
202
|
+
assert.ok(report.written.some((p) => p.includes(path.join("alpha", "facts", "2026-05-01"))));
|
|
203
|
+
assert.ok(report.written.some((p) => p.includes(path.join("beta", "facts", "2026-05-01"))));
|
|
204
|
+
|
|
205
|
+
const scoped = await findDirectorySidecarsForQuery(root, "telescope mirror", { namespace: "beta" });
|
|
206
|
+
assert.ok(scoped.length >= 1);
|
|
207
|
+
const betaRoot = path.join(root, "namespaces", "beta");
|
|
208
|
+
assert.ok(
|
|
209
|
+
scoped.every((m) => {
|
|
210
|
+
const r = path.relative(betaRoot, m.dir);
|
|
211
|
+
return r !== "" && !r.startsWith("..") && !path.isAbsolute(r);
|
|
212
|
+
}),
|
|
213
|
+
"no hit may escape the requested namespace",
|
|
214
|
+
);
|
|
215
|
+
assert.match(scoped[0].abstract, /Beta wing/i);
|
|
216
|
+
|
|
217
|
+
const unscoped = await findDirectorySidecarsForQuery(root, "telescope mirror");
|
|
218
|
+
assert.equal(unscoped.length, 2, "unscoped query spans both namespaces");
|
|
219
|
+
} finally {
|
|
220
|
+
rmSync(root, { recursive: true, force: true });
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
test("sidecar files are excluded from generic recall but stay searchable", async () => {
|
|
225
|
+
assert.equal(
|
|
226
|
+
isGenericRecallExcludedPath(path.join("facts", "2026-01-02", DIRECTORY_SIDECAR_BASENAME)),
|
|
227
|
+
true,
|
|
228
|
+
"sidecar must never be injected as a recall memory",
|
|
229
|
+
);
|
|
230
|
+
assert.equal(
|
|
231
|
+
isGenericRecallExcludedPath(
|
|
232
|
+
path.join("namespaces", "beta", "facts", "2026-01-02", DIRECTORY_SIDECAR_BASENAME),
|
|
233
|
+
),
|
|
234
|
+
true,
|
|
235
|
+
);
|
|
236
|
+
assert.equal(
|
|
237
|
+
isSearchExcludedPath(path.join("facts", "2026-01-02", DIRECTORY_SIDECAR_BASENAME)),
|
|
238
|
+
false,
|
|
239
|
+
"sidecars stay full-text searchable like OKF index files",
|
|
240
|
+
);
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
test("user-authored sidecar-named files are never clobbered or removed", async () => {
|
|
244
|
+
const root = store();
|
|
245
|
+
try {
|
|
246
|
+
const factsDir = path.join(root, "facts");
|
|
247
|
+
await mkdir(factsDir, { recursive: true });
|
|
248
|
+
const userFile = path.join(factsDir, DIRECTORY_SIDECAR_BASENAME);
|
|
249
|
+
writeFileSync(userFile, "# my own notes\n");
|
|
250
|
+
await writeMemory(root, "facts/fact-1.md", "Ordinary memory.");
|
|
251
|
+
|
|
252
|
+
const enabled = await runDirectorySidecarMaintenance(root, true);
|
|
253
|
+
assert.ok(
|
|
254
|
+
!enabled.written.some((p) => p === userFile),
|
|
255
|
+
"user file without the marker is not overwritten",
|
|
256
|
+
);
|
|
257
|
+
assert.equal(await readFile(userFile, "utf8"), "# my own notes\n");
|
|
258
|
+
|
|
259
|
+
const disabled = await runDirectorySidecarMaintenance(root, false);
|
|
260
|
+
assert.ok(
|
|
261
|
+
!disabled.removed.some((p) => p === userFile),
|
|
262
|
+
"disabling maintenance never deletes a user file",
|
|
263
|
+
);
|
|
264
|
+
assert.equal(await readFile(userFile, "utf8"), "# my own notes\n");
|
|
265
|
+
} finally {
|
|
266
|
+
rmSync(root, { recursive: true, force: true });
|
|
267
|
+
}
|
|
268
|
+
});
|
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @remnic/core — per-directory abstract/overview sidecars (issue #2977).
|
|
3
|
+
*
|
|
4
|
+
* One derived `overview.md` per memory directory (category roots and every
|
|
5
|
+
* directory beneath them, including `namespaces/<ns>/` subtrees), rendered
|
|
6
|
+
* bottom-up from existing memory content:
|
|
7
|
+
*
|
|
8
|
+
* - the ABSTRACT is a single line (<=256 chars) composed from child titles;
|
|
9
|
+
* - the OVERVIEW body lists one preview line per child memory and embeds
|
|
10
|
+
* each child DIRECTORY's abstract, so a parent summarizes its subtree
|
|
11
|
+
* without embedding full child overviews.
|
|
12
|
+
*
|
|
13
|
+
* Determinism: no LLM, no embeddings. Content is assembled with the same
|
|
14
|
+
* preview machinery the OKF index files use (`normalizeProjectionPreview`).
|
|
15
|
+
*
|
|
16
|
+
* Invalidation: each sidecar stores a content-free fingerprint of its subtree
|
|
17
|
+
* (child name|size|mtime for files, recursive fingerprints for child dirs).
|
|
18
|
+
* Any add/edit/delete anywhere below a directory changes its fingerprint, so
|
|
19
|
+
* `loadDirectorySidecar` can detect staleness with `readdir`+`stat` alone and
|
|
20
|
+
* refresh without reading child memory files first. Eager regeneration runs
|
|
21
|
+
* through {@link runDirectorySidecarMaintenance}; lazy refresh on read covers
|
|
22
|
+
* direct-write paths that bypass scheduled maintenance (Gotcha #43).
|
|
23
|
+
*
|
|
24
|
+
* Isolation: sidecars are derived artifacts, excluded from generic recall via
|
|
25
|
+
* `isGenericRecallExcludedPath` (basename match, the same tradeoff OKF makes
|
|
26
|
+
* for `index.md`/`log.md`) while staying full-text searchable. Files at the
|
|
27
|
+
* sidecar path WITHOUT the marker are user content: never overwritten, never
|
|
28
|
+
* removed.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
import { createHash } from "node:crypto";
|
|
32
|
+
import { lstatSync, readFileSync, readdirSync, statSync, unlinkSync } from "node:fs";
|
|
33
|
+
import path from "node:path";
|
|
34
|
+
|
|
35
|
+
import { writeFileAtomically } from "./maintenance/atomic-file.js";
|
|
36
|
+
import { normalizeProjectionPreview } from "./memory-projection-format.js";
|
|
37
|
+
import { OKF_RESERVED_BASENAMES } from "./okf/type-mapping.js";
|
|
38
|
+
import { RECALL_FALLBACK_DIRS } from "./utils/category-dir.js";
|
|
39
|
+
|
|
40
|
+
export const DIRECTORY_SIDECAR_BASENAME = "overview.md";
|
|
41
|
+
export const DIRECTORY_SIDECAR_MARKER = "<!-- remnic-directory-sidecar -->";
|
|
42
|
+
|
|
43
|
+
const ABSTRACT_MAX_CHARS = 256;
|
|
44
|
+
const OVERVIEW_MAX_CHARS = 4000;
|
|
45
|
+
const DEFAULT_QUERY_LIMIT = 5;
|
|
46
|
+
|
|
47
|
+
/** Basenames never treated as child memories (ours plus OKF's reserved ones). */
|
|
48
|
+
const RESERVED_CHILD_BASENAMES: Readonly<Record<string, true>> = Object.freeze({
|
|
49
|
+
...OKF_RESERVED_BASENAMES,
|
|
50
|
+
[DIRECTORY_SIDECAR_BASENAME]: true,
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
export interface DirectorySidecar {
|
|
54
|
+
/** Absolute directory the sidecar describes. */
|
|
55
|
+
readonly dir: string;
|
|
56
|
+
/** Single-line L0 abstract (<=256 chars). */
|
|
57
|
+
readonly abstract: string;
|
|
58
|
+
/** Abstract plus the per-child preview body (<=4000 chars). */
|
|
59
|
+
readonly overview: string;
|
|
60
|
+
/** Number of memory `.md` children in this directory. */
|
|
61
|
+
readonly childCount: number;
|
|
62
|
+
/** False when the stored fingerprint no longer matches the subtree. */
|
|
63
|
+
readonly fresh: boolean;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface DirectorySidecarMatch extends DirectorySidecar {
|
|
67
|
+
readonly score: number;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export interface DirectorySidecarReport {
|
|
71
|
+
written: string[];
|
|
72
|
+
removed: string[];
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
interface ParsedSidecar {
|
|
76
|
+
abstract: string;
|
|
77
|
+
overview: string;
|
|
78
|
+
childCount: number;
|
|
79
|
+
fingerprint: string;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function listEntries(dir: string) {
|
|
83
|
+
try {
|
|
84
|
+
return readdirSync(dir, { withFileTypes: true });
|
|
85
|
+
} catch {
|
|
86
|
+
return [];
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Root check that refuses symlinks (fs-boundary rule, PRs #1938/#1999). */
|
|
91
|
+
function isRealDirectory(entryPath: string): boolean {
|
|
92
|
+
try {
|
|
93
|
+
const stats = lstatSync(entryPath);
|
|
94
|
+
return !stats.isSymbolicLink() && stats.isDirectory();
|
|
95
|
+
} catch {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function memoryChildren(dir: string): string[] {
|
|
101
|
+
// Dirent.isFile() is false for symlinks, so a linked memory never enters the corpus.
|
|
102
|
+
return listEntries(dir)
|
|
103
|
+
.filter((entry) => entry.isFile() && !RESERVED_CHILD_BASENAMES[entry.name] && entry.name.endsWith(".md"))
|
|
104
|
+
.map((entry) => entry.name)
|
|
105
|
+
.sort((a, b) => a.localeCompare(b));
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function directoryChildren(dir: string): string[] {
|
|
109
|
+
// Dirent.isDirectory() is false for symlinks (fs-boundary rule, PRs #1938/#1999).
|
|
110
|
+
return listEntries(dir)
|
|
111
|
+
.filter((entry) => entry.isDirectory())
|
|
112
|
+
.map((entry) => entry.name)
|
|
113
|
+
.sort((a, b) => a.localeCompare(b));
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Content-free subtree fingerprint: sha256 over sorted child descriptors.
|
|
118
|
+
* Files contribute `name|size|mtimeMs`; directories contribute their own
|
|
119
|
+
* recursive fingerprint, so any change below a directory invalidates every
|
|
120
|
+
* ancestor. Reads no file contents.
|
|
121
|
+
*/
|
|
122
|
+
export function directoryFingerprint(dir: string): string {
|
|
123
|
+
const hash = createHash("sha256");
|
|
124
|
+
for (const name of memoryChildren(dir)) {
|
|
125
|
+
try {
|
|
126
|
+
const stats = statSync(path.join(dir, name));
|
|
127
|
+
hash.update(`f|${name}|${stats.size}|${Math.trunc(stats.mtimeMs)}\n`);
|
|
128
|
+
} catch {
|
|
129
|
+
hash.update(`f|${name}|missing\n`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
for (const name of directoryChildren(dir)) {
|
|
133
|
+
hash.update(`d|${name}|${directoryFingerprint(path.join(dir, name))}\n`);
|
|
134
|
+
}
|
|
135
|
+
return hash.digest("hex");
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function readFileOrNull(target: string): string | null {
|
|
139
|
+
try {
|
|
140
|
+
return readFileSync(target, "utf8");
|
|
141
|
+
} catch {
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function parseSidecar(content: string): ParsedSidecar | null {
|
|
147
|
+
if (!content.startsWith(DIRECTORY_SIDECAR_MARKER)) return null;
|
|
148
|
+
const fingerprint = /^fingerprint: ([0-9a-f]{64})$/m.exec(content)?.[1];
|
|
149
|
+
if (!fingerprint) return null;
|
|
150
|
+
const abstract = /^## Abstract\n([^\n]+)$/m.exec(content)?.[1] ?? "";
|
|
151
|
+
const overviewStart = content.indexOf("## Abstract");
|
|
152
|
+
const overview =
|
|
153
|
+
overviewStart === -1 ? "" : content.slice(overviewStart, overviewStart + OVERVIEW_MAX_CHARS).trim();
|
|
154
|
+
// The `* name — preview` child bullets are the rendered child count.
|
|
155
|
+
const childCount = (content.match(/^\* .+$/gm) ?? []).length;
|
|
156
|
+
return { abstract, overview, childCount, fingerprint };
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function sidecarPath(dir: string): string {
|
|
160
|
+
return path.join(dir, DIRECTORY_SIDECAR_BASENAME);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function renderSidecar(dir: string, fingerprint: string, childAbstracts: Map<string, string>): string | null {
|
|
164
|
+
const children = memoryChildren(dir);
|
|
165
|
+
const childDirs = directoryChildren(dir).filter((name) => childAbstracts.has(path.join(dir, name)));
|
|
166
|
+
if (children.length === 0 && childDirs.length === 0) return null;
|
|
167
|
+
|
|
168
|
+
const previews = children.map((name) => {
|
|
169
|
+
try {
|
|
170
|
+
return normalizeProjectionPreview(readFileSync(path.join(dir, name), "utf8"), 120);
|
|
171
|
+
} catch {
|
|
172
|
+
return "";
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
const titles = children.map(
|
|
176
|
+
(name, index) => previews[index].split(" ").slice(0, 8).join(" ") || name.replace(/\.md$/, ""),
|
|
177
|
+
);
|
|
178
|
+
const abstract = normalizeProjectionPreview(
|
|
179
|
+
`${path.basename(dir)}/ — ${children.length} memories: ${titles.slice(0, 5).join(" · ")}`,
|
|
180
|
+
ABSTRACT_MAX_CHARS,
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
const lines = [
|
|
184
|
+
DIRECTORY_SIDECAR_MARKER,
|
|
185
|
+
`fingerprint: ${fingerprint}`,
|
|
186
|
+
`# ${path.basename(dir)}/ overview`,
|
|
187
|
+
"",
|
|
188
|
+
"## Abstract",
|
|
189
|
+
abstract,
|
|
190
|
+
"",
|
|
191
|
+
"## Children",
|
|
192
|
+
];
|
|
193
|
+
for (const [index, name] of children.entries()) {
|
|
194
|
+
lines.push(`* ${name} — ${previews[index]}`);
|
|
195
|
+
}
|
|
196
|
+
for (const name of childDirs) {
|
|
197
|
+
lines.push(`### ${name}/`);
|
|
198
|
+
lines.push(childAbstracts.get(path.join(dir, name)) ?? "");
|
|
199
|
+
}
|
|
200
|
+
lines.push("");
|
|
201
|
+
return lines.join("\n");
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/** Scope roots whose category subtrees carry sidecars: the memory root plus every namespace root. */
|
|
205
|
+
function scopeRoots(memoryDir: string): string[] {
|
|
206
|
+
const roots = [memoryDir];
|
|
207
|
+
const namespacesRoot = path.join(memoryDir, "namespaces");
|
|
208
|
+
for (const entry of listEntries(namespacesRoot)) {
|
|
209
|
+
if (entry.isDirectory() && isRealDirectory(path.join(namespacesRoot, entry.name))) {
|
|
210
|
+
roots.push(path.join(namespacesRoot, entry.name));
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return roots;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Every directory that should carry a sidecar, deepest first (post-order) so
|
|
218
|
+
* parents render after their children and can embed child abstracts.
|
|
219
|
+
*/
|
|
220
|
+
function collectSidecarDirs(scopeRoot: string): string[] {
|
|
221
|
+
const dirs: string[] = [];
|
|
222
|
+
const walk = (dir: string, isCategoryRoot: boolean): boolean => {
|
|
223
|
+
let carries = memoryChildren(dir).length > 0;
|
|
224
|
+
for (const name of directoryChildren(dir)) {
|
|
225
|
+
carries = walk(path.join(dir, name), false) || carries;
|
|
226
|
+
}
|
|
227
|
+
// A directory that stopped carrying memories must still be visited so its
|
|
228
|
+
// orphaned sidecar can be pruned or rewritten (cache-invalidation completeness).
|
|
229
|
+
const hasSidecar = readFileOrNull(sidecarPath(dir))?.startsWith(DIRECTORY_SIDECAR_MARKER) === true;
|
|
230
|
+
if (isCategoryRoot || carries || hasSidecar) dirs.push(dir);
|
|
231
|
+
return carries;
|
|
232
|
+
};
|
|
233
|
+
for (const category of RECALL_FALLBACK_DIRS) {
|
|
234
|
+
const categoryDir = path.join(scopeRoot, category);
|
|
235
|
+
if (!isRealDirectory(categoryDir)) continue;
|
|
236
|
+
walk(categoryDir, true);
|
|
237
|
+
}
|
|
238
|
+
return dirs;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Regenerate directory sidecars incrementally. Unchanged sidecars are left
|
|
243
|
+
* untouched; emptied directories lose theirs; user files at the sidecar path
|
|
244
|
+
* (no marker) are never written or removed. With `enabled: false` every
|
|
245
|
+
* generated sidecar is removed — the OKF index-maintenance contract.
|
|
246
|
+
*/
|
|
247
|
+
export async function runDirectorySidecarMaintenance(
|
|
248
|
+
memoryDir: string,
|
|
249
|
+
enabled: boolean,
|
|
250
|
+
): Promise<DirectorySidecarReport> {
|
|
251
|
+
const written: string[] = [];
|
|
252
|
+
const removed: string[] = [];
|
|
253
|
+
for (const scope of scopeRoots(memoryDir)) {
|
|
254
|
+
const childAbstracts = new Map<string, string>();
|
|
255
|
+
for (const dir of collectSidecarDirs(scope)) {
|
|
256
|
+
const target = sidecarPath(dir);
|
|
257
|
+
const raw = readFileOrNull(target);
|
|
258
|
+
const existing = raw !== null && raw.startsWith(DIRECTORY_SIDECAR_MARKER) ? raw : null;
|
|
259
|
+
if (!enabled) {
|
|
260
|
+
if (existing !== null) {
|
|
261
|
+
unlinkSync(target);
|
|
262
|
+
removed.push(target);
|
|
263
|
+
}
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
266
|
+
const next = renderSidecar(dir, directoryFingerprint(dir), childAbstracts);
|
|
267
|
+
if (next === null) {
|
|
268
|
+
if (existing !== null) {
|
|
269
|
+
unlinkSync(target);
|
|
270
|
+
removed.push(target);
|
|
271
|
+
}
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
const parsedNext = parseSidecar(next);
|
|
275
|
+
if (parsedNext) childAbstracts.set(dir, parsedNext.abstract);
|
|
276
|
+
if (raw === next) continue;
|
|
277
|
+
if (existing === null && raw !== null) continue; // user-authored file: hands off
|
|
278
|
+
await writeFileAtomically(target, next);
|
|
279
|
+
written.push(target);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return { written, removed };
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Read a directory's sidecar for retrieval. By default a stale sidecar is
|
|
287
|
+
* refreshed in place (child directories first), which is what makes direct
|
|
288
|
+
* writes that bypass maintenance still converge. With `{ refresh: false }`
|
|
289
|
+
* the stored sidecar is returned as-is with `fresh: false` so callers can
|
|
290
|
+
* observe staleness without writes.
|
|
291
|
+
*/
|
|
292
|
+
export async function loadDirectorySidecar(
|
|
293
|
+
dir: string,
|
|
294
|
+
options: { refresh?: boolean } = {},
|
|
295
|
+
): Promise<DirectorySidecar | null> {
|
|
296
|
+
const refresh = options.refresh !== false;
|
|
297
|
+
const target = sidecarPath(dir);
|
|
298
|
+
const stored = readFileOrNull(target);
|
|
299
|
+
const generated = stored !== null && stored.startsWith(DIRECTORY_SIDECAR_MARKER);
|
|
300
|
+
const parsed = generated ? parseSidecar(stored) : null;
|
|
301
|
+
const fingerprint = directoryFingerprint(dir);
|
|
302
|
+
if (parsed !== null && (parsed.fingerprint === fingerprint || !refresh)) {
|
|
303
|
+
return {
|
|
304
|
+
dir,
|
|
305
|
+
abstract: parsed.abstract,
|
|
306
|
+
overview: parsed.overview,
|
|
307
|
+
childCount: parsed.childCount,
|
|
308
|
+
fresh: parsed.fingerprint === fingerprint,
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
if (!refresh) return null;
|
|
312
|
+
|
|
313
|
+
// Refresh bottom-up so this directory's body embeds current child abstracts.
|
|
314
|
+
const childAbstracts = new Map<string, string>();
|
|
315
|
+
for (const name of directoryChildren(dir)) {
|
|
316
|
+
const child = await loadDirectorySidecar(path.join(dir, name));
|
|
317
|
+
if (child) childAbstracts.set(path.join(dir, name), child.abstract);
|
|
318
|
+
}
|
|
319
|
+
const next = renderSidecar(dir, directoryFingerprint(dir), childAbstracts);
|
|
320
|
+
if (next === null) {
|
|
321
|
+
if (generated) unlinkSync(target);
|
|
322
|
+
return null;
|
|
323
|
+
}
|
|
324
|
+
if (readFileOrNull(target) !== next) {
|
|
325
|
+
if (stored !== null && !generated) {
|
|
326
|
+
// User-authored file at the sidecar path: serve nothing rather than clobber it.
|
|
327
|
+
return null;
|
|
328
|
+
}
|
|
329
|
+
await writeFileAtomically(target, next);
|
|
330
|
+
}
|
|
331
|
+
const refreshed = parseSidecar(next);
|
|
332
|
+
return refreshed
|
|
333
|
+
? {
|
|
334
|
+
dir,
|
|
335
|
+
abstract: refreshed.abstract,
|
|
336
|
+
overview: refreshed.overview,
|
|
337
|
+
childCount: refreshed.childCount,
|
|
338
|
+
fresh: true,
|
|
339
|
+
}
|
|
340
|
+
: null;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Overview-query primitive for hierarchical retrieval (layer 1 of #2977):
|
|
345
|
+
* score every sidecar's abstract (+3 per query term) and overview (+1 per
|
|
346
|
+
* term) lexically, then return the most specific matches — when both a
|
|
347
|
+
* directory and its descendant match, only the descendant is kept, matching
|
|
348
|
+
* the drill-down semantics the full retrieval mode will build on.
|
|
349
|
+
*/
|
|
350
|
+
export async function findDirectorySidecarsForQuery(
|
|
351
|
+
memoryDir: string,
|
|
352
|
+
query: string,
|
|
353
|
+
options: { namespace?: string; limit?: number } = {},
|
|
354
|
+
): Promise<DirectorySidecarMatch[]> {
|
|
355
|
+
const scope = options.namespace ? path.join(memoryDir, "namespaces", options.namespace) : memoryDir;
|
|
356
|
+
if (!isRealDirectory(scope)) return [];
|
|
357
|
+
const terms = query
|
|
358
|
+
.toLowerCase()
|
|
359
|
+
.split(/[^a-z0-9]+/)
|
|
360
|
+
.filter((term) => term.length >= 2);
|
|
361
|
+
|
|
362
|
+
const matches: DirectorySidecarMatch[] = [];
|
|
363
|
+
for (const root of scopeRoots(scope)) {
|
|
364
|
+
for (const dir of collectSidecarDirs(root)) {
|
|
365
|
+
const sidecar = await loadDirectorySidecar(dir);
|
|
366
|
+
if (!sidecar) continue;
|
|
367
|
+
const abstractLower = sidecar.abstract.toLowerCase();
|
|
368
|
+
const overviewLower = sidecar.overview.toLowerCase();
|
|
369
|
+
let score = 0;
|
|
370
|
+
for (const term of terms) {
|
|
371
|
+
if (abstractLower.includes(term)) score += 3;
|
|
372
|
+
else if (overviewLower.includes(term)) score += 1;
|
|
373
|
+
}
|
|
374
|
+
if (score > 0) matches.push({ ...sidecar, score });
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
matches.sort((a, b) => b.score - a.score || a.dir.localeCompare(b.dir));
|
|
378
|
+
|
|
379
|
+
const limit = options.limit ?? DEFAULT_QUERY_LIMIT;
|
|
380
|
+
return matches
|
|
381
|
+
.filter((candidate) => !matches.some((other) => other.dir.startsWith(`${candidate.dir}${path.sep}`)))
|
|
382
|
+
.slice(0, limit);
|
|
383
|
+
}
|