@remnic/core 9.48.1 → 9.49.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/abstraction-nodes.d.ts +12 -1
- package/dist/abstraction-nodes.js +9 -2
- package/dist/access-admin-ops-surface.d.ts +11 -10
- package/dist/access-admin-ops-surface.js +46 -45
- package/dist/access-audit.js +2 -2
- package/dist/access-authorization-probe.d.ts +11 -10
- package/dist/access-authorization-probe.js +47 -46
- package/dist/access-boundary.d.ts +11 -10
- package/dist/access-boundary.js +46 -45
- package/dist/access-cli.js +127 -121
- 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 +11 -10
- package/dist/access-extraction-force-flush.js +46 -45
- package/dist/access-health-types.d.ts +1 -1
- package/dist/access-http-lcm-compaction.d.ts +11 -10
- package/dist/access-http-lifecycle-flush.d.ts +11 -10
- package/dist/access-http-offline-stream.d.ts +11 -10
- package/dist/access-http-query.js +46 -45
- package/dist/access-http.d.ts +11 -10
- package/dist/access-http.js +57 -56
- package/dist/access-identity-continuity-surface.d.ts +9 -8
- package/dist/access-identity-continuity-surface.js +46 -45
- package/dist/access-lcm-surface.d.ts +11 -10
- package/dist/access-lcm-surface.js +46 -45
- package/dist/access-mcp.d.ts +11 -10
- package/dist/access-mcp.js +52 -51
- package/dist/access-memory-search-fanout.d.ts +2 -2
- package/dist/{access-namespace-preflight--H6HwN85.d.ts → access-namespace-preflight-DuDU301-.d.ts} +1 -1
- package/dist/access-namespace-preflight.d.ts +3 -3
- package/dist/access-namespace-preflight.js +46 -45
- package/dist/access-observe-write-surface.d.ts +11 -10
- package/dist/access-observe-write-surface.js +46 -45
- package/dist/access-offline-manifest.d.ts +9 -8
- package/dist/access-offline-manifest.js +3 -3
- package/dist/access-operations-batch.js +48 -47
- package/dist/access-operations.d.ts +14 -13
- package/dist/access-operations.js +51 -50
- package/dist/access-recall-concurrency.d.ts +11 -10
- package/dist/access-recall-concurrency.js +46 -45
- package/dist/access-recall-response.d.ts +11 -10
- package/dist/access-recall-response.js +46 -45
- package/dist/access-recall-surface.d.ts +11 -10
- package/dist/access-recall-surface.js +46 -45
- package/dist/access-schema.d.ts +105 -105
- package/dist/access-schema.js +3 -3
- package/dist/{access-service-andNjwfD.d.ts → access-service--_3T_v23.d.ts} +7 -7
- package/dist/access-service-helpers.d.ts +11 -10
- package/dist/access-service.d.ts +11 -10
- package/dist/access-service.js +46 -45
- package/dist/access-surface-catalog.d.ts +11 -10
- package/dist/access-wearables-meetings-surface.d.ts +4 -3
- 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/active-recall.js +3 -2
- package/dist/active-recall.js.map +1 -1
- package/dist/adapters/index.js +4 -4
- package/dist/adapters/registry.js +2 -2
- package/dist/ambient-provenance.d.ts +1 -1
- package/dist/artifact-search.d.ts +1 -1
- package/dist/{auto-sync-7O6RP2RT.js → auto-sync-QFQGOEO2.js} +4 -4
- package/dist/behavior-learner.d.ts +1 -1
- package/dist/behavior-signals.d.ts +1 -1
- package/dist/bootstrap.d.ts +9 -8
- package/dist/briefing.d.ts +3 -2
- package/dist/briefing.js +15 -14
- package/dist/buffer-surprise-report.d.ts +1 -1
- package/dist/buffer-turn-helpers.d.ts +1 -1
- package/dist/buffer.d.ts +3 -2
- package/dist/buffer.js +4 -4
- package/dist/bulk-import/index.d.ts +3 -3
- package/dist/calibration.d.ts +1 -1
- package/dist/calibration.js +1 -1
- package/dist/capabilities.d.ts +1 -1
- package/dist/{capsule-merge-Q6AR3XL7.js → capsule-merge-KXTRZ23Z.js} +6 -6
- package/dist/{catalog-Iwbk9h5E.d.ts → catalog-DjUmUIal.d.ts} +1 -1
- package/dist/causal-behavior.d.ts +1 -1
- package/dist/causal-behavior.js +3 -3
- package/dist/causal-chain.js +3 -3
- package/dist/causal-consolidation.d.ts +1 -1
- package/dist/causal-consolidation.js +19 -18
- package/dist/causal-consolidation.js.map +1 -1
- package/dist/causal-retrieval.js +3 -3
- package/dist/causal-trajectory-graph.d.ts +1 -1
- package/dist/causal-trajectory.d.ts +51 -2
- package/dist/causal-trajectory.js +8 -2
- package/dist/{chunk-PLCZD3LG.js → chunk-24BKGKGO.js} +91 -45
- package/dist/chunk-24BKGKGO.js.map +1 -0
- package/dist/{chunk-ICP53SKG.js → chunk-2HIJHXB3.js} +2 -2
- package/dist/{chunk-LZGCJSQR.js → chunk-3LD4N2TF.js} +151 -37
- package/dist/chunk-3LD4N2TF.js.map +1 -0
- package/dist/{chunk-FJ2ND6CZ.js → chunk-47J4EDGR.js} +2 -2
- package/dist/{chunk-S55OAS5P.js → chunk-4EABKPZS.js} +4 -4
- package/dist/{chunk-GD7UPIBG.js → chunk-4JWJMRPJ.js} +2 -2
- package/dist/{chunk-3NDN5P55.js → chunk-4S3ECH3U.js} +2 -2
- package/dist/chunk-5GINB5LC.js +13 -0
- package/dist/chunk-5GINB5LC.js.map +1 -0
- package/dist/{chunk-CGASUJVP.js → chunk-5V6IXHPI.js} +4 -4
- package/dist/{chunk-A4JMGKCD.js → chunk-5XCN4XBU.js} +4 -4
- package/dist/{chunk-2RPJ4NEE.js → chunk-64BHFFKZ.js} +2 -2
- package/dist/{chunk-FQC3DHH2.js → chunk-66SE3BNH.js} +2 -2
- package/dist/{chunk-TERNBNJB.js → chunk-6FC3IZ26.js} +2 -2
- package/dist/{chunk-53AJILXF.js → chunk-6XGEBQ3R.js} +4 -4
- package/dist/{chunk-IP3DHDU2.js → chunk-7BLWBSRL.js} +3 -3
- package/dist/{chunk-A2TFK5PG.js → chunk-7NDM2PNK.js} +3 -3
- package/dist/{chunk-HRMEYQRO.js → chunk-7VWZ4LRW.js} +2 -2
- package/dist/{chunk-RN53QBM4.js → chunk-A4QMKFFO.js} +5 -1
- package/dist/chunk-A4QMKFFO.js.map +1 -0
- package/dist/chunk-ACPCA4NO.js +321 -0
- package/dist/chunk-ACPCA4NO.js.map +1 -0
- package/dist/chunk-AMTEYNYK.js +72 -0
- package/dist/chunk-AMTEYNYK.js.map +1 -0
- package/dist/{chunk-R6YJI3HE.js → chunk-AW4QUFQF.js} +1 -1
- package/dist/{chunk-P65YJEEL.js → chunk-AXFWMRKI.js} +2 -2
- package/dist/{chunk-WG5HSQ4P.js → chunk-BAKXNQ3C.js} +2 -2
- package/dist/{chunk-NM2WP3B7.js → chunk-BJYET5TX.js} +2 -2
- package/dist/{chunk-HLKPRWF4.js → chunk-DATDLJAO.js} +6 -6
- package/dist/chunk-DJUKBGAD.js +260 -0
- package/dist/chunk-DJUKBGAD.js.map +1 -0
- package/dist/{chunk-DX3XRGLH.js → chunk-DQTCGVFK.js} +2 -2
- package/dist/{chunk-HIDEBEAN.js → chunk-E633OXMH.js} +11 -11
- package/dist/{chunk-J7T4MX7P.js → chunk-EEAMSNSG.js} +12 -1
- package/dist/chunk-EEAMSNSG.js.map +1 -0
- package/dist/{chunk-EZNXW27Q.js → chunk-ELQCS47S.js} +4 -4
- package/dist/{chunk-HAYSYUAB.js → chunk-EURCX6PS.js} +2 -2
- package/dist/{chunk-RSPSHRZA.js → chunk-FV7YQTN3.js} +2 -2
- package/dist/chunk-G73MCJKL.js +20 -0
- package/dist/chunk-G73MCJKL.js.map +1 -0
- package/dist/{chunk-GWHPQOUC.js → chunk-GGBSF2X7.js} +2 -2
- package/dist/{chunk-SYUBB3WM.js → chunk-GKEYXXKX.js} +2 -2
- package/dist/{chunk-PELT6ADC.js → chunk-GSEPRIZC.js} +3 -3
- package/dist/{chunk-UTVZKHJK.js → chunk-GUKBW4HL.js} +2 -2
- package/dist/chunk-GXSOUIA4.js +293 -0
- package/dist/chunk-GXSOUIA4.js.map +1 -0
- package/dist/{chunk-2F6NP3NT.js → chunk-HDQUQBPW.js} +2 -2
- package/dist/{chunk-6HMYUWXR.js → chunk-HTP6XRUI.js} +2 -2
- package/dist/{chunk-UYUYZ4WY.js → chunk-IARXXZU4.js} +2 -2
- package/dist/{chunk-MNB5RMAY.js → chunk-IIFFC3RL.js} +2 -2
- package/dist/{chunk-KQAFEZQX.js → chunk-IPLYGWQF.js} +5 -5
- package/dist/{chunk-NZBZ7IS2.js → chunk-JDCQK3C5.js} +2 -2
- package/dist/{chunk-ANCC3JJH.js → chunk-JI62YGPI.js} +2 -2
- package/dist/{chunk-LULR4IUW.js → chunk-KATEWTQJ.js} +2 -2
- package/dist/{chunk-IJ7HK452.js → chunk-KMAP7SNQ.js} +2 -2
- package/dist/{chunk-PRVJH7CY.js → chunk-KMWYZTIE.js} +2 -2
- package/dist/{chunk-JGSKJHF7.js → chunk-KQS72GCR.js} +2 -2
- package/dist/{chunk-3S63QIFQ.js → chunk-LAQ33B5D.js} +1 -1
- package/dist/chunk-LAQ33B5D.js.map +1 -0
- package/dist/chunk-LNTUXKBT.js +224 -0
- package/dist/chunk-LNTUXKBT.js.map +1 -0
- package/dist/{chunk-FMCHFSZF.js → chunk-LQKXVQZR.js} +12 -12
- package/dist/{chunk-M4DMO67I.js → chunk-LTOAJXZB.js} +4 -4
- package/dist/{chunk-NIS256NW.js → chunk-LYMNZZIM.js} +2 -2
- package/dist/{chunk-W4FBGYGK.js → chunk-M4MP34AQ.js} +189 -5
- package/dist/chunk-M4MP34AQ.js.map +1 -0
- package/dist/{chunk-3PY7VHV7.js → chunk-MCA2X4WH.js} +2 -2
- package/dist/{chunk-S5NZBKKQ.js → chunk-N3CXAYPJ.js} +3 -3
- package/dist/{chunk-WQIHRA5F.js → chunk-N3LL5NEU.js} +2 -2
- package/dist/{chunk-G2RBWVFQ.js → chunk-NKL25FCF.js} +2 -2
- package/dist/{chunk-LMDRGRJ2.js → chunk-NOB2T2FC.js} +2 -2
- package/dist/{chunk-P2OTGMTR.js → chunk-NSGU76YO.js} +2 -2
- package/dist/{chunk-D5NGH2EW.js → chunk-O76MWTVS.js} +2 -2
- package/dist/{chunk-WZFAPHIK.js → chunk-OIOZ6MOP.js} +3 -3
- package/dist/{chunk-DNHVPZOD.js → chunk-P26YHAQH.js} +201 -1
- package/dist/chunk-P26YHAQH.js.map +1 -0
- package/dist/{chunk-UMOO7IY7.js → chunk-P2KBDJJ6.js} +2 -2
- package/dist/{chunk-KVJRXN2E.js → chunk-P4QOYG52.js} +125 -13
- package/dist/chunk-P4QOYG52.js.map +1 -0
- package/dist/chunk-P77QX3ID.js +332 -0
- package/dist/chunk-P77QX3ID.js.map +1 -0
- package/dist/{chunk-SXWHN7GC.js → chunk-PDEKQWDV.js} +2 -2
- package/dist/{chunk-2EJNXE53.js → chunk-PLCHU56K.js} +3 -3
- package/dist/{chunk-45OTV7FZ.js → chunk-PWIMF7LP.js} +38 -38
- package/dist/{chunk-7YKVM5H3.js → chunk-PWLB4TRK.js} +3 -3
- package/dist/{chunk-RVYVMQK4.js → chunk-QSYL5CXK.js} +3 -3
- package/dist/{chunk-NXUJRXSF.js → chunk-QWLW3VQH.js} +2 -2
- package/dist/chunk-RBOJCG4T.js +77 -0
- package/dist/chunk-RBOJCG4T.js.map +1 -0
- package/dist/{chunk-2P3GQVM2.js → chunk-RBPH3GZV.js} +418 -105
- package/dist/chunk-RBPH3GZV.js.map +1 -0
- package/dist/{chunk-JBPKEARU.js → chunk-RKNJBZ55.js} +4 -4
- package/dist/{chunk-QCECHYZL.js → chunk-RVO6TCTU.js} +6 -6
- package/dist/{chunk-5L7PTX2Q.js → chunk-S2SJEOTM.js} +2 -2
- package/dist/{chunk-52I3YZZ6.js → chunk-S75UQ3T3.js} +3 -3
- package/dist/{chunk-LXOM6IQU.js → chunk-SEF4IOFE.js} +2 -2
- package/dist/{chunk-DUEEQCTT.js → chunk-SHYIMIX5.js} +2 -2
- package/dist/{chunk-KSGEGHYA.js → chunk-T35UJI5R.js} +78 -74
- package/dist/chunk-T35UJI5R.js.map +1 -0
- package/dist/{chunk-7K5Q6COX.js → chunk-TVVEYCNW.js} +4 -4
- package/dist/{chunk-3JUYWPFJ.js → chunk-VFS3MK5F.js} +2 -2
- package/dist/chunk-VUDMT5CP.js +86 -0
- package/dist/chunk-VUDMT5CP.js.map +1 -0
- package/dist/{chunk-CMQZNEIF.js → chunk-WFLBP5GC.js} +8 -1
- package/dist/chunk-WFLBP5GC.js.map +1 -0
- package/dist/{chunk-WOEVJKKQ.js → chunk-WPZX7MEA.js} +4 -4
- package/dist/{chunk-ZRWB5D4H.js → chunk-WT6UL3UA.js} +2 -2
- package/dist/{chunk-MC6XO7T4.js → chunk-YLBE6PLR.js} +2 -2
- package/dist/{chunk-GF22HWS2.js → chunk-Z54EM5YZ.js} +4 -4
- package/dist/{chunk-GVX3NAUU.js → chunk-ZDAYWJV6.js} +2 -2
- package/dist/{chunk-4WBGZYZZ.js → chunk-ZJIFYUJI.js} +7 -7
- package/dist/chunk-ZJIFYUJI.js.map +1 -0
- package/dist/{chunk-JYPO7L3G.js → chunk-ZN4EYMKJ.js} +2 -2
- package/dist/{cli-p_WmVo2h.d.ts → cli-BLNmNEMT.d.ts} +16 -14
- package/dist/cli.d.ts +14 -13
- package/dist/cli.js +90 -87
- package/dist/coding/pre-action-gate.d.ts +66 -0
- package/dist/coding/pre-action-gate.js +22 -0
- package/dist/commitment-ledger.js +2 -2
- package/dist/compounding/engine.d.ts +3 -2
- package/dist/compounding/engine.js +16 -15
- 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/config.js +3 -2
- package/dist/connectors/codex-materialize-runner.d.ts +1 -1
- package/dist/connectors/codex-materialize-runner.js +15 -14
- package/dist/connectors/codex-materialize.d.ts +1 -1
- package/dist/connectors/index.d.ts +1 -1
- package/dist/connectors/index.js +15 -14
- package/dist/consolidation-provenance-check.d.ts +3 -2
- package/dist/consolidation-provenance-check.js +6 -6
- package/dist/consolidation-undo.d.ts +3 -2
- package/dist/consolidation-undo.js +5 -5
- package/dist/contradiction/index.d.ts +3 -2
- package/dist/contradiction/index.js +3 -3
- package/dist/converge-config.d.ts +1 -1
- package/dist/convergence-refresh.d.ts +3 -2
- package/dist/convergence-refresh.js +18 -17
- 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-read-cancellation.d.ts +38 -0
- package/dist/corpus-read-cancellation.js +9 -0
- package/dist/corpus-read-cancellation.js.map +1 -0
- package/dist/corpus-watermark.d.ts +1 -1
- package/dist/corpus-watermark.js +21 -20
- package/dist/cue-anchors.d.ts +19 -1
- package/dist/cue-anchors.js +10 -2
- package/dist/day-summary.d.ts +1 -1
- package/dist/delinearize.d.ts +1 -1
- package/dist/dependency-propagation-config.d.ts +10 -0
- package/dist/dependency-propagation-config.js +10 -0
- package/dist/dependency-propagation-config.js.map +1 -0
- package/dist/deterministic-order.d.ts +4 -0
- package/dist/deterministic-order.js +10 -0
- package/dist/deterministic-order.js.map +1 -0
- 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-retrieval.d.ts +3 -2
- package/dist/entity-retrieval.js +15 -14
- package/dist/entity-schema.d.ts +1 -1
- package/dist/evals.js +2 -2
- package/dist/explicit-capture.d.ts +9 -8
- package/dist/explicit-capture.js +6 -6
- package/dist/external-wiki-access.d.ts +11 -10
- package/dist/external-wiki-access.js +47 -46
- 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 +11 -10
- package/dist/extraction-error-classification.d.ts +1 -1
- package/dist/extraction-faithfulness.d.ts +1 -1
- package/dist/extraction-judge-telemetry.d.ts +1 -1
- package/dist/extraction-judge-training.d.ts +1 -1
- package/dist/extraction-judge.d.ts +1 -1
- package/dist/extraction-liveness.d.ts +1 -1
- package/dist/extraction-normalization.d.ts +14 -0
- package/dist/extraction-normalization.js +22 -0
- package/dist/extraction-normalization.js.map +1 -0
- package/dist/extraction-prompt.d.ts +4 -3
- package/dist/extraction-prompt.js +3 -1
- package/dist/extraction-source-grounding-rules.d.ts +1 -1
- package/dist/extraction-source-grounding.d.ts +1 -1
- package/dist/extraction.d.ts +18 -1
- package/dist/extraction.js +24 -21
- package/dist/fallback-llm.d.ts +1 -1
- package/dist/{first-start-migration-ZSHEFEI3.js → first-start-migration-626C5554.js} +5 -5
- package/dist/{forget-DNAJ66ZD.js → forget-GJ42LCBW.js} +2 -2
- 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-snapshot.d.ts +1 -1
- package/dist/graph.d.ts +1 -1
- package/dist/harmonic-construction.d.ts +43 -0
- package/dist/harmonic-construction.js +26 -0
- package/dist/harmonic-construction.js.map +1 -0
- package/dist/harmonic-retrieval.d.ts +1 -0
- package/dist/harmonic-retrieval.js +10 -4
- package/dist/importance.d.ts +1 -1
- package/dist/importers/index.d.ts +1 -1
- package/dist/in-flight-reads.d.ts +47 -3
- package/dist/in-flight-reads.js +5 -3
- package/dist/index.d.ts +699 -696
- package/dist/index.js +197 -150
- package/dist/intent.d.ts +1 -1
- package/dist/json-store.d.ts +15 -1
- package/dist/json-store.js +11 -3
- package/dist/lcm/engine.d.ts +1 -1
- package/dist/lcm/engine.js +2 -2
- package/dist/lcm/index.d.ts +1 -1
- package/dist/lcm/index.js +7 -7
- 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.d.ts +1 -1
- package/dist/local-model-endpoint.d.ts +1 -1
- package/dist/maintenance/archive-observations.js +2 -2
- package/dist/maintenance/atomic-file.d.ts +5 -0
- package/dist/maintenance/atomic-file.js +12 -0
- package/dist/maintenance/atomic-file.js.map +1 -0
- package/dist/maintenance/memory-governance.d.ts +1 -1
- package/dist/maintenance/memory-governance.js +15 -14
- package/dist/maintenance/migrate-observations.js +3 -3
- package/dist/maintenance/observation-ledger-utils.d.ts +1 -1
- package/dist/maintenance/observation-ledger-utils.js +2 -2
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.d.ts +3 -2
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +16 -15
- package/dist/maintenance/rebuild-memory-projection.d.ts +3 -2
- package/dist/maintenance/rebuild-memory-projection.js +17 -16
- package/dist/maintenance/rebuild-observations.js +3 -3
- package/dist/{maintenance-BfcuwfJS.d.ts → maintenance-DKbKZi3u.d.ts} +3 -3
- package/dist/mcp-memory-inspector-app.d.ts +11 -10
- package/dist/memory-action-policy.d.ts +1 -1
- package/dist/memory-cache.d.ts +1 -1
- package/dist/memory-corpus-version.js +2 -2
- 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-worth-outcomes.d.ts +3 -2
- package/dist/models-json.d.ts +1 -1
- package/dist/namespaces/migrate.d.ts +4 -3
- package/dist/namespaces/migrate.js +20 -19
- package/dist/namespaces/principal.d.ts +1 -1
- package/dist/namespaces/search.d.ts +1 -1
- package/dist/namespaces/search.js +4 -4
- package/dist/namespaces/storage.d.ts +4 -3
- package/dist/namespaces/storage.js +15 -14
- package/dist/native-knowledge.d.ts +1 -1
- package/dist/objective-state-writers.js +3 -3
- package/dist/objective-state.js +2 -2
- package/dist/offline-sync-impression-drain.d.ts +1 -1
- package/dist/offline-sync-impression-drain.js +2 -2
- package/dist/operator-doctor-corpus.d.ts +1 -1
- package/dist/operator-doctor-corpus.js +22 -21
- package/dist/operator-doctor-replica.d.ts +1 -1
- package/dist/operator-toolkit.d.ts +4 -3
- package/dist/operator-toolkit.js +31 -29
- package/dist/orchestration/compression-guideline-coordinator.d.ts +3 -2
- package/dist/orchestration/maintenance.d.ts +5 -4
- package/dist/orchestration/maintenance.js +21 -20
- package/dist/{orchestrator-B7nLo4z1.d.ts → orchestrator-BRwD7ceU.d.ts} +7 -7
- package/dist/orchestrator.d.ts +9 -8
- package/dist/orchestrator.js +127 -121
- package/dist/page-versioning.js +5 -5
- package/dist/patterns-cli.d.ts +1 -1
- package/dist/{pipeline-1h2GpiHP.d.ts → pipeline-DC-1C9T8.d.ts} +1 -1
- package/dist/policy-runtime.d.ts +1 -1
- package/dist/proactive-contention.d.ts +1 -1
- package/dist/provenance.d.ts +1 -1
- package/dist/{qmd-UdwoF71D.d.ts → qmd-DdeCEZdb.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/qmd.js +4 -4
- 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-explain-renderer.js +3 -3
- package/dist/recall-memory-map.d.ts +1 -1
- package/dist/recall-planner-llm.d.ts +1 -1
- package/dist/recall-state.d.ts +1 -1
- package/dist/recall-state.js +2 -2
- 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-cli.js +4 -4
- package/dist/recall-xray-renderer.d.ts +1 -1
- package/dist/recall-xray-renderer.js +3 -3
- package/dist/recall-xray.d.ts +1 -1
- package/dist/recall-xray.js +2 -2
- package/dist/reconcile/cursor.d.ts +1 -1
- package/dist/reconcile/manifest.d.ts +1 -1
- package/dist/reconcile/plan.d.ts +1 -1
- package/dist/relevance.js +2 -2
- 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/{resolution-3K27T4VO.js → resolution-MDPNOEG6.js} +4 -4
- package/dist/resolution-MDPNOEG6.js.map +1 -0
- package/dist/resolve-auth-token.d.ts +1 -1
- package/dist/resume-bundles.js +8 -7
- 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/salvage-envelope.js +3 -3
- package/dist/schemas.d.ts +213 -116
- package/dist/schemas.js +1 -1
- package/dist/{scope-profiles-APjhSe-w.d.ts → scope-profiles-BXVq4Jv0.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 +3 -3
- package/dist/search/index.d.ts +1 -1
- package/dist/search/index.js +3 -3
- 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-CrySDbk3.d.ts → semantic-consolidation-CGYxfa8F.d.ts} +1 -1
- package/dist/semantic-consolidation.d.ts +2 -2
- package/dist/semantic-consolidation.js +16 -15
- package/dist/semantic-rule-promotion.js +15 -14
- package/dist/semantic-rule-verifier.d.ts +1 -1
- package/dist/semantic-rule-verifier.js +15 -14
- package/dist/{service-B5W5IYCx.d.ts → service-CBDBQFCN.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 +11 -11
- package/dist/signal.d.ts +1 -1
- package/dist/source-agent-qualifier.d.ts +1 -1
- package/dist/source-agent-qualifier.js +19 -18
- package/dist/{storage-COL907QF.d.ts → storage-V--MQi7w.d.ts} +5 -4
- package/dist/storage.d.ts +3 -2
- package/dist/storage.js +14 -13
- package/dist/summarizer.d.ts +1 -1
- package/dist/summary-snapshot.d.ts +1 -1
- package/dist/temporal-supersession.d.ts +14 -2
- package/dist/temporal-supersession.js +1 -1
- 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 +3 -2
- package/dist/tier-routing.d.ts +1 -1
- package/dist/{tier-stats-OPTSBNRH.js → tier-stats-2JCZYEF7.js} +5 -5
- 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/capsule-import.js +6 -6
- package/dist/transfer/types.d.ts +66 -66
- package/dist/trust-score-stage.d.ts +1 -1
- package/dist/trust-score.d.ts +1 -1
- package/dist/trust-zones.js +2 -2
- package/dist/{types-D_Au9qJO.d.ts → types-D_a6Sqjz.d.ts} +25 -8
- package/dist/types.d.ts +1 -1
- package/dist/types.js +1 -1
- package/dist/utility-learner.js +3 -3
- package/dist/utility-runtime.d.ts +1 -1
- package/dist/utility-runtime.js +4 -4
- package/dist/utility-telemetry.js +2 -2
- package/dist/verified-recall.js +15 -14
- package/dist/work-product-ledger.js +2 -2
- package/dist/write-envelope.d.ts +1 -1
- package/dist/write-envelope.js +2 -2
- package/package.json +32 -2
- package/src/abstraction-nodes.ts +182 -4
- package/src/causal-trajectory.ts +258 -3
- package/src/cli/harmonic-search.ts +23 -0
- package/src/cli/research-status-commands.ts +2 -1
- package/src/cli.ts +1 -24
- package/src/coding/pre-action-gate.test.ts +516 -0
- package/src/coding/pre-action-gate.ts +320 -0
- package/src/config.test.ts +85 -0
- package/src/config.ts +2 -0
- package/src/corpus-read-cancellation.ts +42 -0
- package/src/cue-anchors.test.ts +90 -0
- package/src/cue-anchors.ts +264 -11
- package/src/dependency-frontmatter.test.ts +83 -0
- package/src/dependency-propagation-config.ts +94 -0
- package/src/deterministic-order.ts +14 -0
- package/src/entity-retrieval.ts +4 -4
- package/src/extraction-normalization.ts +71 -0
- package/src/extraction-prompt.ts +45 -11
- package/src/extraction.ts +48 -32
- package/src/faithfulness-graph-guard.test.ts +27 -0
- package/src/harmonic-construction.test.ts +1799 -0
- package/src/harmonic-construction.ts +367 -0
- package/src/harmonic-retrieval.ts +166 -25
- package/src/in-flight-reads.ts +119 -6
- package/src/index.ts +4 -5
- package/src/json-store.ts +354 -12
- package/src/orchestration/consolidation-run.ts +192 -0
- package/src/orchestration/contradiction-linking-coordinator.ts +35 -1
- package/src/orchestration/dependency-propagation-triggers.test.ts +286 -0
- package/src/orchestration/dependency-propagation.test.ts +487 -0
- package/src/orchestration/dependency-propagation.ts +263 -0
- package/src/orchestration/dependency-revalidation.ts +173 -0
- package/src/orchestration/extraction-persist-deps.ts +93 -0
- package/src/orchestration/extraction-persist.ts +101 -94
- package/src/orchestration/harmonic-construction-persist.ts +86 -0
- package/src/orchestration/namespace-read-fanout.ts +13 -2
- package/src/orchestration/recall-internal-deps.ts +2 -0
- package/src/orchestration/recall-internal.ts +5 -1
- package/src/orchestration/recall-search-pipeline.ts +2 -0
- package/src/orchestration/recall-search-prefilter.ts +5 -1
- package/src/orchestrator.ts +7 -6
- package/src/schemas.ts +15 -0
- package/src/storage/entity-store.ts +9 -2
- package/src/storage/memory-read-store.ts +33 -4
- package/src/storage-contract/corpus-read-cancellation.test.ts +408 -0
- package/src/storage.ts +75 -19
- package/src/temporal-supersession.ts +29 -1
- package/src/testing/orchestrator-lite.ts +27 -8
- package/src/testing/subjects/extraction-lifecycle.test.ts +91 -50
- package/src/types.ts +22 -7
- package/dist/chunk-2P3GQVM2.js.map +0 -1
- package/dist/chunk-3S63QIFQ.js.map +0 -1
- package/dist/chunk-4WBGZYZZ.js.map +0 -1
- package/dist/chunk-CMQZNEIF.js.map +0 -1
- package/dist/chunk-DNHVPZOD.js.map +0 -1
- package/dist/chunk-FZZ2QTJI.js +0 -111
- package/dist/chunk-FZZ2QTJI.js.map +0 -1
- package/dist/chunk-J7T4MX7P.js.map +0 -1
- package/dist/chunk-KSGEGHYA.js.map +0 -1
- package/dist/chunk-KVJRXN2E.js.map +0 -1
- package/dist/chunk-LPSF4OQH.js +0 -47
- package/dist/chunk-LPSF4OQH.js.map +0 -1
- package/dist/chunk-LZGCJSQR.js.map +0 -1
- package/dist/chunk-OADWQ5CR.js +0 -115
- package/dist/chunk-OADWQ5CR.js.map +0 -1
- package/dist/chunk-PDJTHRAW.js +0 -36
- package/dist/chunk-PDJTHRAW.js.map +0 -1
- package/dist/chunk-PLCZD3LG.js.map +0 -1
- package/dist/chunk-RN53QBM4.js.map +0 -1
- package/dist/chunk-W4FBGYGK.js.map +0 -1
- /package/dist/{auto-sync-7O6RP2RT.js.map → auto-sync-QFQGOEO2.js.map} +0 -0
- /package/dist/{capsule-merge-Q6AR3XL7.js.map → capsule-merge-KXTRZ23Z.js.map} +0 -0
- /package/dist/{chunk-ICP53SKG.js.map → chunk-2HIJHXB3.js.map} +0 -0
- /package/dist/{chunk-FJ2ND6CZ.js.map → chunk-47J4EDGR.js.map} +0 -0
- /package/dist/{chunk-S55OAS5P.js.map → chunk-4EABKPZS.js.map} +0 -0
- /package/dist/{chunk-GD7UPIBG.js.map → chunk-4JWJMRPJ.js.map} +0 -0
- /package/dist/{chunk-3NDN5P55.js.map → chunk-4S3ECH3U.js.map} +0 -0
- /package/dist/{chunk-CGASUJVP.js.map → chunk-5V6IXHPI.js.map} +0 -0
- /package/dist/{chunk-A4JMGKCD.js.map → chunk-5XCN4XBU.js.map} +0 -0
- /package/dist/{chunk-2RPJ4NEE.js.map → chunk-64BHFFKZ.js.map} +0 -0
- /package/dist/{chunk-FQC3DHH2.js.map → chunk-66SE3BNH.js.map} +0 -0
- /package/dist/{chunk-TERNBNJB.js.map → chunk-6FC3IZ26.js.map} +0 -0
- /package/dist/{chunk-53AJILXF.js.map → chunk-6XGEBQ3R.js.map} +0 -0
- /package/dist/{chunk-IP3DHDU2.js.map → chunk-7BLWBSRL.js.map} +0 -0
- /package/dist/{chunk-A2TFK5PG.js.map → chunk-7NDM2PNK.js.map} +0 -0
- /package/dist/{chunk-HRMEYQRO.js.map → chunk-7VWZ4LRW.js.map} +0 -0
- /package/dist/{chunk-R6YJI3HE.js.map → chunk-AW4QUFQF.js.map} +0 -0
- /package/dist/{chunk-P65YJEEL.js.map → chunk-AXFWMRKI.js.map} +0 -0
- /package/dist/{chunk-WG5HSQ4P.js.map → chunk-BAKXNQ3C.js.map} +0 -0
- /package/dist/{chunk-NM2WP3B7.js.map → chunk-BJYET5TX.js.map} +0 -0
- /package/dist/{chunk-HLKPRWF4.js.map → chunk-DATDLJAO.js.map} +0 -0
- /package/dist/{chunk-DX3XRGLH.js.map → chunk-DQTCGVFK.js.map} +0 -0
- /package/dist/{chunk-HIDEBEAN.js.map → chunk-E633OXMH.js.map} +0 -0
- /package/dist/{chunk-EZNXW27Q.js.map → chunk-ELQCS47S.js.map} +0 -0
- /package/dist/{chunk-HAYSYUAB.js.map → chunk-EURCX6PS.js.map} +0 -0
- /package/dist/{chunk-RSPSHRZA.js.map → chunk-FV7YQTN3.js.map} +0 -0
- /package/dist/{chunk-GWHPQOUC.js.map → chunk-GGBSF2X7.js.map} +0 -0
- /package/dist/{chunk-SYUBB3WM.js.map → chunk-GKEYXXKX.js.map} +0 -0
- /package/dist/{chunk-PELT6ADC.js.map → chunk-GSEPRIZC.js.map} +0 -0
- /package/dist/{chunk-UTVZKHJK.js.map → chunk-GUKBW4HL.js.map} +0 -0
- /package/dist/{chunk-2F6NP3NT.js.map → chunk-HDQUQBPW.js.map} +0 -0
- /package/dist/{chunk-6HMYUWXR.js.map → chunk-HTP6XRUI.js.map} +0 -0
- /package/dist/{chunk-UYUYZ4WY.js.map → chunk-IARXXZU4.js.map} +0 -0
- /package/dist/{chunk-MNB5RMAY.js.map → chunk-IIFFC3RL.js.map} +0 -0
- /package/dist/{chunk-KQAFEZQX.js.map → chunk-IPLYGWQF.js.map} +0 -0
- /package/dist/{chunk-NZBZ7IS2.js.map → chunk-JDCQK3C5.js.map} +0 -0
- /package/dist/{chunk-ANCC3JJH.js.map → chunk-JI62YGPI.js.map} +0 -0
- /package/dist/{chunk-LULR4IUW.js.map → chunk-KATEWTQJ.js.map} +0 -0
- /package/dist/{chunk-IJ7HK452.js.map → chunk-KMAP7SNQ.js.map} +0 -0
- /package/dist/{chunk-PRVJH7CY.js.map → chunk-KMWYZTIE.js.map} +0 -0
- /package/dist/{chunk-JGSKJHF7.js.map → chunk-KQS72GCR.js.map} +0 -0
- /package/dist/{chunk-FMCHFSZF.js.map → chunk-LQKXVQZR.js.map} +0 -0
- /package/dist/{chunk-M4DMO67I.js.map → chunk-LTOAJXZB.js.map} +0 -0
- /package/dist/{chunk-NIS256NW.js.map → chunk-LYMNZZIM.js.map} +0 -0
- /package/dist/{chunk-3PY7VHV7.js.map → chunk-MCA2X4WH.js.map} +0 -0
- /package/dist/{chunk-S5NZBKKQ.js.map → chunk-N3CXAYPJ.js.map} +0 -0
- /package/dist/{chunk-WQIHRA5F.js.map → chunk-N3LL5NEU.js.map} +0 -0
- /package/dist/{chunk-G2RBWVFQ.js.map → chunk-NKL25FCF.js.map} +0 -0
- /package/dist/{chunk-LMDRGRJ2.js.map → chunk-NOB2T2FC.js.map} +0 -0
- /package/dist/{chunk-P2OTGMTR.js.map → chunk-NSGU76YO.js.map} +0 -0
- /package/dist/{chunk-D5NGH2EW.js.map → chunk-O76MWTVS.js.map} +0 -0
- /package/dist/{chunk-WZFAPHIK.js.map → chunk-OIOZ6MOP.js.map} +0 -0
- /package/dist/{chunk-UMOO7IY7.js.map → chunk-P2KBDJJ6.js.map} +0 -0
- /package/dist/{chunk-SXWHN7GC.js.map → chunk-PDEKQWDV.js.map} +0 -0
- /package/dist/{chunk-2EJNXE53.js.map → chunk-PLCHU56K.js.map} +0 -0
- /package/dist/{chunk-45OTV7FZ.js.map → chunk-PWIMF7LP.js.map} +0 -0
- /package/dist/{chunk-7YKVM5H3.js.map → chunk-PWLB4TRK.js.map} +0 -0
- /package/dist/{chunk-RVYVMQK4.js.map → chunk-QSYL5CXK.js.map} +0 -0
- /package/dist/{chunk-NXUJRXSF.js.map → chunk-QWLW3VQH.js.map} +0 -0
- /package/dist/{chunk-JBPKEARU.js.map → chunk-RKNJBZ55.js.map} +0 -0
- /package/dist/{chunk-QCECHYZL.js.map → chunk-RVO6TCTU.js.map} +0 -0
- /package/dist/{chunk-5L7PTX2Q.js.map → chunk-S2SJEOTM.js.map} +0 -0
- /package/dist/{chunk-52I3YZZ6.js.map → chunk-S75UQ3T3.js.map} +0 -0
- /package/dist/{chunk-LXOM6IQU.js.map → chunk-SEF4IOFE.js.map} +0 -0
- /package/dist/{chunk-DUEEQCTT.js.map → chunk-SHYIMIX5.js.map} +0 -0
- /package/dist/{chunk-7K5Q6COX.js.map → chunk-TVVEYCNW.js.map} +0 -0
- /package/dist/{chunk-3JUYWPFJ.js.map → chunk-VFS3MK5F.js.map} +0 -0
- /package/dist/{chunk-WOEVJKKQ.js.map → chunk-WPZX7MEA.js.map} +0 -0
- /package/dist/{chunk-ZRWB5D4H.js.map → chunk-WT6UL3UA.js.map} +0 -0
- /package/dist/{chunk-MC6XO7T4.js.map → chunk-YLBE6PLR.js.map} +0 -0
- /package/dist/{chunk-GF22HWS2.js.map → chunk-Z54EM5YZ.js.map} +0 -0
- /package/dist/{chunk-GVX3NAUU.js.map → chunk-ZDAYWJV6.js.map} +0 -0
- /package/dist/{chunk-JYPO7L3G.js.map → chunk-ZN4EYMKJ.js.map} +0 -0
- /package/dist/{resolution-3K27T4VO.js.map → coding/pre-action-gate.js.map} +0 -0
- /package/dist/{first-start-migration-ZSHEFEI3.js.map → first-start-migration-626C5554.js.map} +0 -0
- /package/dist/{forget-DNAJ66ZD.js.map → forget-GJ42LCBW.js.map} +0 -0
- /package/dist/{tier-stats-OPTSBNRH.js.map → tier-stats-2JCZYEF7.js.map} +0 -0
package/src/in-flight-reads.ts
CHANGED
|
@@ -14,8 +14,20 @@ import type { MemoryFile } from "./types.js";
|
|
|
14
14
|
* an unlocked manager's decrypted scan must NOT be handed to a locked or
|
|
15
15
|
* differently-keyed manager for the same dir — that would bypass the keyId
|
|
16
16
|
* isolation getCachedMemories enforces. Locked/plaintext stores use keyId "".
|
|
17
|
+
*
|
|
18
|
+
* Since issue #2307 the registry also owns the scan's CANCELLATION, because
|
|
19
|
+
* whether a shared scan may be cancelled is purely a question of who is still
|
|
20
|
+
* attached to it — see `beginCoalescedScan`.
|
|
17
21
|
*/
|
|
18
|
-
|
|
22
|
+
type InFlightEntry = {
|
|
23
|
+
read: Promise<MemoryFile[]>;
|
|
24
|
+
/** Readers still awaiting this scan. At zero the scan has no reason to finish. */
|
|
25
|
+
waiters: number;
|
|
26
|
+
/** Withdraw the slot and abort the scan. Absent for an uncancellable scan. */
|
|
27
|
+
cancel?: () => void;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const inFlightReadsByKey = new Map<string, InFlightEntry>();
|
|
19
31
|
|
|
20
32
|
const DIR_SEP = "\u0000";
|
|
21
33
|
|
|
@@ -23,12 +35,113 @@ function composeKey(baseDir: string, keyId: string): string {
|
|
|
23
35
|
return `${baseDir}${DIR_SEP}${keyId}`;
|
|
24
36
|
}
|
|
25
37
|
|
|
26
|
-
|
|
27
|
-
|
|
38
|
+
/**
|
|
39
|
+
* A reader's attachment to a shared scan. `leave()` is idempotent per reader and
|
|
40
|
+
* must be called when that reader stops waiting — because its own signal fired, or
|
|
41
|
+
* because the scan settled.
|
|
42
|
+
*/
|
|
43
|
+
export interface CoalescedScanWaiter {
|
|
44
|
+
leave(): void;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Cancellation lifecycle for a coalesced corpus scan (issue #2307).
|
|
49
|
+
*
|
|
50
|
+
* The rule is reference counting, not ownership: **the scan is cancelled when its
|
|
51
|
+
* LAST waiter leaves, and never before.** One reader can therefore not cancel work
|
|
52
|
+
* another still needs, and two readers that both give up do not leave a full disk
|
|
53
|
+
* scan running for nobody — which was the abandoned I/O this whole change exists to
|
|
54
|
+
* remove.
|
|
55
|
+
*
|
|
56
|
+
* On the last leave the slot is withdrawn from the registry BEFORE the controller
|
|
57
|
+
* fires, so a reader arriving later cannot attach to a promise already doomed to
|
|
58
|
+
* reject with an `AbortError` it never asked for; it starts a fresh scan instead.
|
|
59
|
+
*/
|
|
60
|
+
export interface CoalescedScan extends CoalescedScanWaiter {
|
|
61
|
+
/** The signal the shared scan itself observes. */
|
|
62
|
+
readonly scanSignal: AbortSignal;
|
|
63
|
+
/** Publish the scan to the registry and arm cancellation. */
|
|
64
|
+
arm(read: Promise<MemoryFile[]>): void;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Build the per-reader `leave` closure: decrement once, cancel at zero. */
|
|
68
|
+
function trackWaiter(entry: InFlightEntry, callerSignal?: AbortSignal): CoalescedScanWaiter {
|
|
69
|
+
let left = false;
|
|
70
|
+
const leave = () => {
|
|
71
|
+
if (left) return;
|
|
72
|
+
left = true;
|
|
73
|
+
callerSignal?.removeEventListener("abort", onAbort);
|
|
74
|
+
entry.waiters -= 1;
|
|
75
|
+
if (entry.waiters <= 0) entry.cancel?.();
|
|
76
|
+
};
|
|
77
|
+
// Named so the listener can be removed again; `once` alone would leak the
|
|
78
|
+
// reference for a reader that leaves normally.
|
|
79
|
+
function onAbort() {
|
|
80
|
+
leave();
|
|
81
|
+
}
|
|
82
|
+
callerSignal?.addEventListener("abort", onAbort, { once: true });
|
|
83
|
+
return { leave };
|
|
28
84
|
}
|
|
29
85
|
|
|
30
|
-
|
|
31
|
-
|
|
86
|
+
/**
|
|
87
|
+
* Attach to an existing scan for (baseDir, keyId), or `undefined` when none is in
|
|
88
|
+
* flight. The caller MUST `leave()` once it stops awaiting the returned promise.
|
|
89
|
+
*/
|
|
90
|
+
export function attachInFlightReader(
|
|
91
|
+
baseDir: string,
|
|
92
|
+
keyId = "",
|
|
93
|
+
callerSignal?: AbortSignal,
|
|
94
|
+
): { read: Promise<MemoryFile[]>; waiter: CoalescedScanWaiter } | undefined {
|
|
95
|
+
const entry = inFlightReadsByKey.get(composeKey(baseDir, keyId));
|
|
96
|
+
if (entry === undefined) return undefined;
|
|
97
|
+
entry.waiters += 1;
|
|
98
|
+
return { read: entry.read, waiter: trackWaiter(entry, callerSignal) };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** Start a cancellable coalesced scan as its first waiter. */
|
|
102
|
+
export function beginCoalescedScan(
|
|
103
|
+
baseDir: string,
|
|
104
|
+
keyId: string,
|
|
105
|
+
callerSignal?: AbortSignal,
|
|
106
|
+
): CoalescedScan {
|
|
107
|
+
const controller = new AbortController();
|
|
108
|
+
let waiter: CoalescedScanWaiter | undefined;
|
|
109
|
+
return {
|
|
110
|
+
scanSignal: controller.signal,
|
|
111
|
+
arm(read: Promise<MemoryFile[]>): void {
|
|
112
|
+
// `waiters: 1` IS the starter; trackWaiter only builds its leave closure
|
|
113
|
+
// (attaching readers do their own increment).
|
|
114
|
+
const entry: InFlightEntry = {
|
|
115
|
+
read,
|
|
116
|
+
waiters: 1,
|
|
117
|
+
cancel: () => {
|
|
118
|
+
deleteInFlightRead(baseDir, keyId, read);
|
|
119
|
+
controller.abort();
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
inFlightReadsByKey.set(composeKey(baseDir, keyId), entry);
|
|
123
|
+
waiter = trackWaiter(entry, callerSignal);
|
|
124
|
+
},
|
|
125
|
+
leave(): void {
|
|
126
|
+
waiter?.leave();
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Register a scan promise directly, with no cancellation attached.
|
|
133
|
+
*
|
|
134
|
+
* For callers that own a promise rather than a scan they can abort — tests that
|
|
135
|
+
* seed a stale in-flight read, and any future producer outside `beginCoalescedScan`.
|
|
136
|
+
* The entry has no `cancel`, so waiters leaving is a no-op: an externally-owned
|
|
137
|
+
* promise is not this module's to abort.
|
|
138
|
+
*/
|
|
139
|
+
export function setInFlightRead(
|
|
140
|
+
baseDir: string,
|
|
141
|
+
keyId: string,
|
|
142
|
+
read: Promise<MemoryFile[]>,
|
|
143
|
+
): void {
|
|
144
|
+
inFlightReadsByKey.set(composeKey(baseDir, keyId), { read, waiters: 1 });
|
|
32
145
|
}
|
|
33
146
|
|
|
34
147
|
/**
|
|
@@ -41,7 +154,7 @@ export function deleteInFlightRead(
|
|
|
41
154
|
expected?: Promise<MemoryFile[]>,
|
|
42
155
|
): void {
|
|
43
156
|
const key = composeKey(baseDir, keyId);
|
|
44
|
-
if (expected === undefined || inFlightReadsByKey.get(key) === expected) {
|
|
157
|
+
if (expected === undefined || inFlightReadsByKey.get(key)?.read === expected) {
|
|
45
158
|
inFlightReadsByKey.delete(key);
|
|
46
159
|
}
|
|
47
160
|
}
|
package/src/index.ts
CHANGED
|
@@ -508,7 +508,8 @@ export {
|
|
|
508
508
|
type RemnicChatGptMemoryCard,
|
|
509
509
|
type RemnicChatGptMemoryInspectorResult,
|
|
510
510
|
} from "./mcp-memory-inspector-app.js";
|
|
511
|
-
|
|
511
|
+
export * from "./coding/pre-action-gate.js";
|
|
512
|
+
export * from "./causal-trajectory.js";
|
|
512
513
|
// Coding-agent subsystem (issue #569)
|
|
513
514
|
export {
|
|
514
515
|
resolveGitContext,
|
|
@@ -1442,10 +1443,7 @@ export {
|
|
|
1442
1443
|
type ForkLineage,
|
|
1443
1444
|
} from "./transfer/capsule-fork.js";
|
|
1444
1445
|
|
|
1445
|
-
//
|
|
1446
|
-
// Coding-graph engine contract (#1551 PR1) — types owned by core,
|
|
1447
|
-
// implemented by @remnic/coding-graph (à-la-carte optional).
|
|
1448
|
-
// ---------------------------------------------------------------------------
|
|
1446
|
+
// Coding-graph engine contract (#1551 PR1), implemented by optional @remnic/coding-graph.
|
|
1449
1447
|
|
|
1450
1448
|
export {
|
|
1451
1449
|
CODING_GRAPH_ENGINE_VERSION,
|
|
@@ -1463,3 +1461,4 @@ export type {
|
|
|
1463
1461
|
SymbolIR,
|
|
1464
1462
|
} from "./coding/coding-graph-types.js";
|
|
1465
1463
|
export type { ResolvedScopeProfilePlan } from "./namespaces/scope-profiles.js";
|
|
1464
|
+
export * from "./maintenance/atomic-file.js";
|
package/src/json-store.ts
CHANGED
|
@@ -1,19 +1,53 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import type { Dirent } from "node:fs";
|
|
3
|
+
import {
|
|
4
|
+
lstat,
|
|
5
|
+
mkdir,
|
|
6
|
+
open,
|
|
7
|
+
readFile,
|
|
8
|
+
readdir,
|
|
9
|
+
rename,
|
|
10
|
+
unlink,
|
|
11
|
+
utimes,
|
|
12
|
+
writeFile,
|
|
13
|
+
type FileHandle,
|
|
14
|
+
} from "node:fs/promises";
|
|
1
15
|
import path from "node:path";
|
|
2
|
-
import { readFile, readdir } from "node:fs/promises";
|
|
3
16
|
|
|
4
|
-
|
|
17
|
+
function hasErrorCode(error: unknown, code: string): boolean {
|
|
18
|
+
return typeof error === "object" && error !== null && "code" in error && (error as { code?: unknown }).code === code;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async function listJsonFilesStrictInner(dir: string, allowMissingDirectory: boolean): Promise<string[]> {
|
|
22
|
+
let entries: Dirent[];
|
|
5
23
|
try {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
24
|
+
entries = await readdir(dir, { withFileTypes: true });
|
|
25
|
+
} catch (error) {
|
|
26
|
+
if (allowMissingDirectory && hasErrorCode(error, "ENOENT")) return [];
|
|
27
|
+
throw error;
|
|
28
|
+
}
|
|
29
|
+
const out: string[] = [];
|
|
30
|
+
for (const entry of entries) {
|
|
31
|
+
const fullPath = path.join(dir, entry.name);
|
|
32
|
+
if (entry.isDirectory()) {
|
|
33
|
+
out.push(...(await listJsonFilesStrictInner(fullPath, false)));
|
|
34
|
+
} else if (entry.isFile() && entry.name.endsWith(".json")) {
|
|
35
|
+
out.push(fullPath);
|
|
15
36
|
}
|
|
16
|
-
|
|
37
|
+
}
|
|
38
|
+
return out.sort();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export async function listJsonFilesStrict(
|
|
42
|
+
dir: string,
|
|
43
|
+
options: { allowMissingDirectory?: boolean } = {}
|
|
44
|
+
): Promise<string[]> {
|
|
45
|
+
return listJsonFilesStrictInner(dir, options.allowMissingDirectory === true);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export async function listJsonFiles(dir: string): Promise<string[]> {
|
|
49
|
+
try {
|
|
50
|
+
return await listJsonFilesStrict(dir);
|
|
17
51
|
} catch {
|
|
18
52
|
return [];
|
|
19
53
|
}
|
|
@@ -37,6 +71,314 @@ export async function listNamedFiles(dir: string, fileName: string): Promise<str
|
|
|
37
71
|
}
|
|
38
72
|
}
|
|
39
73
|
|
|
74
|
+
const jsonStoreMutationTails = new Map<string, Promise<void>>();
|
|
75
|
+
|
|
76
|
+
const JSON_STORE_LOCK_RETRY_MS = 25;
|
|
77
|
+
const JSON_STORE_LOCK_STALE_MS = 60_000;
|
|
78
|
+
const JSON_STORE_LOCK_TIMEOUT_MS = 10_000;
|
|
79
|
+
|
|
80
|
+
type JsonStoreMutationLockTestHooks = {
|
|
81
|
+
beforeLockOwnerWrite?: (lockPath: string, ownerToken: string) => Promise<void> | void;
|
|
82
|
+
beforeLockOwnershipConfirm?: (lockPath: string, ownerToken: string) => Promise<void> | void;
|
|
83
|
+
afterReclaimGuardWrite?: (reclaimPath: string, ownerToken: string) => Promise<void> | void;
|
|
84
|
+
retryMs?: number;
|
|
85
|
+
staleLockMs?: number;
|
|
86
|
+
timeoutMs?: number;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
let jsonStoreMutationLockTestHooks: JsonStoreMutationLockTestHooks | null = null;
|
|
90
|
+
|
|
91
|
+
export function setJsonStoreMutationLockTestHooks(hooks: JsonStoreMutationLockTestHooks | null): void {
|
|
92
|
+
jsonStoreMutationLockTestHooks = hooks;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function jsonStoreLockRetryMs(): number {
|
|
96
|
+
return jsonStoreMutationLockTestHooks?.retryMs ?? JSON_STORE_LOCK_RETRY_MS;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function jsonStoreLockStaleMs(): number {
|
|
100
|
+
return jsonStoreMutationLockTestHooks?.staleLockMs ?? JSON_STORE_LOCK_STALE_MS;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function jsonStoreLockTimeoutMs(): number {
|
|
104
|
+
return jsonStoreMutationLockTestHooks?.timeoutMs ?? JSON_STORE_LOCK_TIMEOUT_MS;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async function closeAndRemoveJsonStoreReclaimGuard(reclaimPath: string, reclaimHandle: FileHandle): Promise<void> {
|
|
108
|
+
await reclaimHandle.close().catch(() => undefined);
|
|
109
|
+
await unlink(reclaimPath).catch((error) => {
|
|
110
|
+
if (!hasErrorCode(error, "ENOENT")) throw error;
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async function removeStaleJsonStoreReclaimGuard(reclaimPath: string): Promise<void> {
|
|
115
|
+
let initial: JsonStoreLockState;
|
|
116
|
+
try {
|
|
117
|
+
initial = await readJsonStoreLockState(reclaimPath);
|
|
118
|
+
} catch (error) {
|
|
119
|
+
if (hasErrorCode(error, "ENOENT")) return;
|
|
120
|
+
throw error;
|
|
121
|
+
}
|
|
122
|
+
if (Date.now() - initial.mtimeMs <= jsonStoreLockStaleMs()) return;
|
|
123
|
+
|
|
124
|
+
let ownerPid: number | undefined;
|
|
125
|
+
try {
|
|
126
|
+
const metadata = JSON.parse(initial.content) as { pid?: unknown };
|
|
127
|
+
ownerPid = typeof metadata.pid === "number" && Number.isInteger(metadata.pid) ? metadata.pid : undefined;
|
|
128
|
+
} catch {
|
|
129
|
+
ownerPid = undefined;
|
|
130
|
+
}
|
|
131
|
+
if (ownerPid !== undefined && processIsRunning(ownerPid)) return;
|
|
132
|
+
|
|
133
|
+
let confirmed: JsonStoreLockState;
|
|
134
|
+
try {
|
|
135
|
+
confirmed = await readJsonStoreLockState(reclaimPath);
|
|
136
|
+
} catch (error) {
|
|
137
|
+
if (hasErrorCode(error, "ENOENT")) return;
|
|
138
|
+
throw error;
|
|
139
|
+
}
|
|
140
|
+
if (!sameJsonStoreLockState(initial, confirmed)) return;
|
|
141
|
+
await unlink(reclaimPath).catch((error) => {
|
|
142
|
+
if (!hasErrorCode(error, "ENOENT")) throw error;
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
async function acquireJsonStoreReclaimGuard(reclaimPath: string): Promise<FileHandle | undefined> {
|
|
147
|
+
try {
|
|
148
|
+
return await open(reclaimPath, "wx", 0o600);
|
|
149
|
+
} catch (error) {
|
|
150
|
+
if (!hasErrorCode(error, "EEXIST")) throw error;
|
|
151
|
+
await removeStaleJsonStoreReclaimGuard(reclaimPath);
|
|
152
|
+
try {
|
|
153
|
+
return await open(reclaimPath, "wx", 0o600);
|
|
154
|
+
} catch (retryError) {
|
|
155
|
+
if (hasErrorCode(retryError, "EEXIST")) return undefined;
|
|
156
|
+
throw retryError;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function waitForJsonStoreLock(): Promise<void> {
|
|
162
|
+
const { promise, resolve } = Promise.withResolvers<void>();
|
|
163
|
+
setTimeout(resolve, jsonStoreLockRetryMs());
|
|
164
|
+
return promise;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function processIsRunning(pid: number): boolean {
|
|
168
|
+
try {
|
|
169
|
+
process.kill(pid, 0);
|
|
170
|
+
return true;
|
|
171
|
+
} catch (error) {
|
|
172
|
+
return !hasErrorCode(error, "ESRCH");
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
type JsonStoreLockState = {
|
|
177
|
+
mtimeMs: number;
|
|
178
|
+
size: number;
|
|
179
|
+
ino: number | undefined;
|
|
180
|
+
content: string;
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
async function readJsonStoreLockState(lockPath: string): Promise<JsonStoreLockState> {
|
|
184
|
+
const lockStat = await lstat(lockPath);
|
|
185
|
+
if (!lockStat.isFile()) {
|
|
186
|
+
throw new Error(`JSON store mutation lock is not a regular file: ${lockPath}`);
|
|
187
|
+
}
|
|
188
|
+
return {
|
|
189
|
+
mtimeMs: lockStat.mtimeMs,
|
|
190
|
+
size: lockStat.size,
|
|
191
|
+
ino: typeof lockStat.ino === "number" ? lockStat.ino : undefined,
|
|
192
|
+
content: await readFile(lockPath, "utf8"),
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function sameJsonStoreLockState(left: JsonStoreLockState, right: JsonStoreLockState): boolean {
|
|
197
|
+
return (
|
|
198
|
+
left.mtimeMs === right.mtimeMs &&
|
|
199
|
+
left.size === right.size &&
|
|
200
|
+
left.ino === right.ino &&
|
|
201
|
+
left.content === right.content
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
async function removeJsonStoreMutationLockIfOwned(lockPath: string, token: string): Promise<void> {
|
|
206
|
+
try {
|
|
207
|
+
const state = await readJsonStoreLockState(lockPath);
|
|
208
|
+
const metadata = JSON.parse(state.content) as { token?: unknown };
|
|
209
|
+
if (metadata.token === token) await unlink(lockPath);
|
|
210
|
+
} catch (error) {
|
|
211
|
+
if (!hasErrorCode(error, "ENOENT") && !(error instanceof SyntaxError)) throw error;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
async function removeStaleJsonStoreLock(lockPath: string): Promise<void> {
|
|
216
|
+
const reclaimPath = `${lockPath}.reclaim`;
|
|
217
|
+
const reclaimHandle = await acquireJsonStoreReclaimGuard(reclaimPath);
|
|
218
|
+
if (!reclaimHandle) return;
|
|
219
|
+
try {
|
|
220
|
+
await reclaimHandle.writeFile(JSON.stringify({ pid: process.pid }), "utf8");
|
|
221
|
+
await jsonStoreMutationLockTestHooks?.afterReclaimGuardWrite?.(reclaimPath, String(process.pid));
|
|
222
|
+
|
|
223
|
+
let initial: JsonStoreLockState;
|
|
224
|
+
try {
|
|
225
|
+
initial = await readJsonStoreLockState(lockPath);
|
|
226
|
+
} catch (error) {
|
|
227
|
+
if (hasErrorCode(error, "ENOENT")) return;
|
|
228
|
+
throw error;
|
|
229
|
+
}
|
|
230
|
+
if (Date.now() - initial.mtimeMs <= jsonStoreLockStaleMs()) return;
|
|
231
|
+
let ownerPid: number | undefined;
|
|
232
|
+
try {
|
|
233
|
+
const metadata = JSON.parse(initial.content) as { pid?: unknown };
|
|
234
|
+
ownerPid = typeof metadata.pid === "number" && Number.isInteger(metadata.pid) ? metadata.pid : undefined;
|
|
235
|
+
} catch {
|
|
236
|
+
ownerPid = undefined;
|
|
237
|
+
}
|
|
238
|
+
if (ownerPid !== undefined && processIsRunning(ownerPid)) return;
|
|
239
|
+
|
|
240
|
+
let confirmed: JsonStoreLockState;
|
|
241
|
+
try {
|
|
242
|
+
confirmed = await readJsonStoreLockState(lockPath);
|
|
243
|
+
} catch (error) {
|
|
244
|
+
if (hasErrorCode(error, "ENOENT")) return;
|
|
245
|
+
throw error;
|
|
246
|
+
}
|
|
247
|
+
if (!sameJsonStoreLockState(initial, confirmed)) return;
|
|
248
|
+
await unlink(lockPath).catch((error) => {
|
|
249
|
+
if (!hasErrorCode(error, "ENOENT")) throw error;
|
|
250
|
+
});
|
|
251
|
+
} finally {
|
|
252
|
+
await closeAndRemoveJsonStoreReclaimGuard(reclaimPath, reclaimHandle);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
async function confirmJsonStoreMutationLockOwnership(
|
|
257
|
+
lockPath: string,
|
|
258
|
+
token: string,
|
|
259
|
+
deadline: number
|
|
260
|
+
): Promise<boolean> {
|
|
261
|
+
const reclaimPath = `${lockPath}.reclaim`;
|
|
262
|
+
while (true) {
|
|
263
|
+
const reclaimHandle = await acquireJsonStoreReclaimGuard(reclaimPath);
|
|
264
|
+
if (!reclaimHandle) {
|
|
265
|
+
if (Date.now() >= deadline) return false;
|
|
266
|
+
await waitForJsonStoreLock();
|
|
267
|
+
continue;
|
|
268
|
+
}
|
|
269
|
+
try {
|
|
270
|
+
try {
|
|
271
|
+
const state = await readJsonStoreLockState(lockPath);
|
|
272
|
+
const metadata = JSON.parse(state.content) as { token?: unknown };
|
|
273
|
+
return metadata.token === token;
|
|
274
|
+
} catch (error) {
|
|
275
|
+
if (hasErrorCode(error, "ENOENT") || error instanceof SyntaxError) return false;
|
|
276
|
+
throw error;
|
|
277
|
+
}
|
|
278
|
+
} finally {
|
|
279
|
+
await closeAndRemoveJsonStoreReclaimGuard(reclaimPath, reclaimHandle);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
async function acquireJsonStoreMutationFileLock(key: string): Promise<() => Promise<void>> {
|
|
285
|
+
const lockPath = `${path.resolve(key)}.mutation.lock`;
|
|
286
|
+
await mkdir(path.dirname(lockPath), { recursive: true });
|
|
287
|
+
const token = randomUUID();
|
|
288
|
+
const deadline = Date.now() + jsonStoreLockTimeoutMs();
|
|
289
|
+
while (true) {
|
|
290
|
+
let handle: FileHandle;
|
|
291
|
+
try {
|
|
292
|
+
handle = await open(lockPath, "wx", 0o600);
|
|
293
|
+
} catch (error) {
|
|
294
|
+
if (!hasErrorCode(error, "EEXIST")) throw error;
|
|
295
|
+
await removeStaleJsonStoreLock(lockPath).catch((staleError) => {
|
|
296
|
+
if (!hasErrorCode(staleError, "ENOENT")) throw staleError;
|
|
297
|
+
});
|
|
298
|
+
if (Date.now() >= deadline) {
|
|
299
|
+
throw new Error(`Timed out acquiring JSON store mutation lock: ${lockPath}`);
|
|
300
|
+
}
|
|
301
|
+
await waitForJsonStoreLock();
|
|
302
|
+
continue;
|
|
303
|
+
}
|
|
304
|
+
try {
|
|
305
|
+
await jsonStoreMutationLockTestHooks?.beforeLockOwnerWrite?.(lockPath, token);
|
|
306
|
+
await handle.writeFile(JSON.stringify({ pid: process.pid, token }), "utf8");
|
|
307
|
+
} finally {
|
|
308
|
+
await handle.close().catch(() => undefined);
|
|
309
|
+
}
|
|
310
|
+
await jsonStoreMutationLockTestHooks?.beforeLockOwnershipConfirm?.(lockPath, token);
|
|
311
|
+
|
|
312
|
+
if (!(await confirmJsonStoreMutationLockOwnership(lockPath, token, deadline))) {
|
|
313
|
+
await removeJsonStoreMutationLockIfOwned(lockPath, token);
|
|
314
|
+
if (Date.now() >= deadline) {
|
|
315
|
+
throw new Error(`Timed out acquiring JSON store mutation lock: ${lockPath}`);
|
|
316
|
+
}
|
|
317
|
+
await waitForJsonStoreLock();
|
|
318
|
+
continue;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
const heartbeat = setInterval(
|
|
322
|
+
() => {
|
|
323
|
+
void readFile(lockPath, "utf8")
|
|
324
|
+
.then((content) => {
|
|
325
|
+
const metadata = JSON.parse(content) as { token?: unknown };
|
|
326
|
+
if (metadata.token !== token) return;
|
|
327
|
+
const now = new Date();
|
|
328
|
+
return utimes(lockPath, now, now);
|
|
329
|
+
})
|
|
330
|
+
.catch(() => undefined);
|
|
331
|
+
},
|
|
332
|
+
Math.max(1, Math.floor(jsonStoreLockStaleMs() / 3))
|
|
333
|
+
);
|
|
334
|
+
heartbeat.unref();
|
|
335
|
+
return async () => {
|
|
336
|
+
clearInterval(heartbeat);
|
|
337
|
+
try {
|
|
338
|
+
const metadata = JSON.parse(await readFile(lockPath, "utf8")) as { token?: unknown };
|
|
339
|
+
if (metadata.token === token) await unlink(lockPath);
|
|
340
|
+
} catch (error) {
|
|
341
|
+
if (!hasErrorCode(error, "ENOENT")) throw error;
|
|
342
|
+
}
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
export async function withJsonStoreMutationLock<T>(key: string, operation: () => Promise<T>): Promise<T> {
|
|
348
|
+
const normalizedKey = path.resolve(key);
|
|
349
|
+
const previous = jsonStoreMutationTails.get(normalizedKey) ?? Promise.resolve();
|
|
350
|
+
const { promise: current, resolve: release } = Promise.withResolvers<void>();
|
|
351
|
+
const tail = previous.catch(() => undefined).then(() => current);
|
|
352
|
+
jsonStoreMutationTails.set(normalizedKey, tail);
|
|
353
|
+
await previous.catch(() => undefined);
|
|
354
|
+
let releaseFileLock: (() => Promise<void>) | undefined;
|
|
355
|
+
try {
|
|
356
|
+
releaseFileLock = await acquireJsonStoreMutationFileLock(normalizedKey);
|
|
357
|
+
return await operation();
|
|
358
|
+
} finally {
|
|
359
|
+
try {
|
|
360
|
+
await releaseFileLock?.();
|
|
361
|
+
} finally {
|
|
362
|
+
release();
|
|
363
|
+
if (jsonStoreMutationTails.get(normalizedKey) === tail) {
|
|
364
|
+
jsonStoreMutationTails.delete(normalizedKey);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
40
370
|
export async function readJsonFile(filePath: string): Promise<unknown> {
|
|
41
371
|
return JSON.parse(await readFile(filePath, "utf8")) as unknown;
|
|
42
372
|
}
|
|
373
|
+
|
|
374
|
+
export async function writeJsonFileAtomic(filePath: string, value: unknown): Promise<void> {
|
|
375
|
+
await mkdir(path.dirname(filePath), { recursive: true });
|
|
376
|
+
const tempPath = `${filePath}.${process.pid}.${Date.now()}.${Math.random().toString(16).slice(2)}.tmp`;
|
|
377
|
+
try {
|
|
378
|
+
await writeFile(tempPath, JSON.stringify(value, null, 2), "utf8");
|
|
379
|
+
await rename(tempPath, filePath);
|
|
380
|
+
} catch (error) {
|
|
381
|
+
await unlink(tempPath).catch(() => undefined);
|
|
382
|
+
throw error;
|
|
383
|
+
}
|
|
384
|
+
}
|