@remnic/core 9.45.3 → 9.45.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/abort-error.d.ts +2 -1
- package/dist/abort-error.js +3 -1
- package/dist/access-admin-ops-surface.d.ts +14 -10
- package/dist/access-admin-ops-surface.js +84 -76
- package/dist/access-audit.js +2 -2
- package/dist/access-authorization-probe.d.ts +14 -10
- package/dist/access-authorization-probe.js +85 -77
- package/dist/access-boundary.d.ts +15 -11
- package/dist/access-boundary.js +84 -76
- package/dist/access-citation-namespace.js +2 -2
- package/dist/access-cli.js +193 -181
- package/dist/access-cli.js.map +1 -1
- package/dist/access-coding-context-resolution.d.ts +16 -0
- package/dist/access-coding-context-resolution.js +13 -0
- package/dist/access-coding-context-resolution.js.map +1 -0
- package/dist/access-extraction-force-flush.d.ts +14 -10
- package/dist/access-extraction-force-flush.js +84 -76
- package/dist/access-health-types.d.ts +2 -1
- package/dist/access-http-lcm-compaction.d.ts +16 -12
- package/dist/access-http-lcm-compaction.js +3 -3
- package/dist/access-http-lifecycle-flush.d.ts +14 -10
- package/dist/access-http-lifecycle-flush.js +2 -2
- package/dist/access-http-offline-stream.d.ts +123 -0
- package/dist/access-http-offline-stream.js +10 -0
- package/dist/access-http-offline-stream.js.map +1 -0
- package/dist/access-http-query.js +84 -76
- package/dist/access-http.d.ts +15 -10
- package/dist/access-http.js +105 -92
- package/dist/access-identity-continuity-surface.d.ts +9 -8
- package/dist/access-identity-continuity-surface.js +84 -76
- package/dist/access-lcm-surface.d.ts +14 -10
- package/dist/access-lcm-surface.js +84 -76
- package/dist/access-mcp-output-schemas.js +1 -1
- package/dist/access-mcp.d.ts +14 -10
- package/dist/access-mcp.js +95 -83
- package/dist/access-memory-search-fanout.d.ts +3 -2
- package/dist/{access-namespace-preflight-5yZ4g6F8.d.ts → access-namespace-preflight-CWUDwB4M.d.ts} +1 -1
- package/dist/access-namespace-preflight.d.ts +4 -3
- package/dist/access-namespace-preflight.js +84 -76
- package/dist/access-observe-helpers.d.ts +1 -0
- package/dist/access-observe-helpers.js +1 -1
- package/dist/access-observe-write-surface.d.ts +16 -10
- package/dist/access-observe-write-surface.js +84 -76
- package/dist/access-offline-manifest.d.ts +104 -0
- package/dist/access-offline-manifest.js +45 -0
- package/dist/access-offline-manifest.js.map +1 -0
- package/dist/access-operations-batch.js +86 -78
- package/dist/access-operations.d.ts +15 -11
- package/dist/access-operations.js +91 -80
- package/dist/access-recall-concurrency.d.ts +16 -26
- package/dist/access-recall-concurrency.js +86 -77
- package/dist/access-recall-response.d.ts +18 -14
- package/dist/access-recall-response.js +84 -76
- package/dist/access-recall-surface.d.ts +29 -24
- package/dist/access-recall-surface.js +84 -76
- package/dist/access-schema.js +3 -3
- package/dist/{access-service-CiJRsgAB.d.ts → access-service-CsU-ljFD.d.ts} +25 -16
- package/dist/access-service-helpers.d.ts +14 -10
- package/dist/access-service.d.ts +14 -10
- package/dist/access-service.js +84 -76
- package/dist/access-surface-catalog.d.ts +14 -10
- package/dist/access-surface-catalog.js +4 -0
- package/dist/access-surface-catalog.js.map +1 -1
- package/dist/access-wearables-meetings-surface.d.ts +4 -3
- 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 +10 -8
- package/dist/active-recall.js.map +1 -1
- package/dist/adapters/index.js +7 -7
- package/dist/adapters/registry.js +3 -3
- package/dist/{auto-sync-5OE5QOZB.js → auto-sync-UPTUVE4X.js} +6 -6
- package/dist/behavior-learner.d.ts +2 -1
- package/dist/behavior-learner.js +1 -1
- package/dist/behavior-signals.d.ts +2 -1
- package/dist/bootstrap.d.ts +9 -8
- package/dist/briefing.d.ts +3 -2
- package/dist/briefing.js +23 -22
- 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 +12 -11
- 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/{capsule-merge-IDWJPCWK.js → capsule-merge-Q6AR3XL7.js} +67 -54
- package/dist/capsule-merge-Q6AR3XL7.js.map +1 -0
- package/dist/{catalog-scBmy0sO.d.ts → catalog-bctMURhN.d.ts} +1 -1
- package/dist/causal-behavior.d.ts +2 -1
- package/dist/causal-behavior.js +2 -2
- package/dist/causal-chain.js +2 -2
- package/dist/causal-consolidation.d.ts +2 -1
- package/dist/causal-consolidation.js +27 -26
- package/dist/causal-consolidation.js.map +1 -1
- package/dist/causal-retrieval.js +2 -2
- package/dist/causal-trajectory-graph.d.ts +2 -1
- package/dist/causal-trajectory.js +1 -1
- package/dist/{chunk-VUPTKTDV.js → chunk-2MRHJNWL.js} +3 -3
- package/dist/{chunk-JBPKEARU.js → chunk-2QSZNTDO.js} +7 -7
- package/dist/{chunk-5IVLTFOV.js → chunk-44N6ESJ4.js} +2 -2
- package/dist/{chunk-WOCNN776.js → chunk-4POAMKV6.js} +100 -41
- package/dist/chunk-4POAMKV6.js.map +1 -0
- package/dist/{chunk-HO3QBJEY.js → chunk-5546W5SI.js} +2 -2
- package/dist/{chunk-U7GW7NE7.js → chunk-5CEECY6P.js} +10 -10
- package/dist/{chunk-4UH4BTGK.js → chunk-5SNZSWWR.js} +82 -82
- package/dist/{chunk-6CZPDBJS.js → chunk-5U7ID3EZ.js} +3 -3
- package/dist/{chunk-GF22HWS2.js → chunk-6NNNURLK.js} +7 -7
- package/dist/chunk-773TRRGT.js +36 -0
- package/dist/chunk-773TRRGT.js.map +1 -0
- package/dist/chunk-7CITSYSH.js +242 -0
- package/dist/chunk-7CITSYSH.js.map +1 -0
- package/dist/{chunk-227UFSEQ.js → chunk-7RZJWZ46.js} +5 -5
- package/dist/{chunk-E5UK4VPE.js → chunk-7SHJHPTY.js} +5 -5
- package/dist/{chunk-UVIKWC7M.js → chunk-7YERSTN7.js} +17 -17
- package/dist/{chunk-DLVPUX7U.js → chunk-7YKVM5H3.js} +8 -6
- package/dist/chunk-7YKVM5H3.js.map +1 -0
- package/dist/{chunk-3CE77VAP.js → chunk-A4JMGKCD.js} +87 -74
- package/dist/chunk-A4JMGKCD.js.map +1 -0
- package/dist/chunk-A5KNZSNA.js +26 -0
- package/dist/chunk-A5KNZSNA.js.map +1 -0
- package/dist/{chunk-S2NCTZGJ.js → chunk-AJQ57VV3.js} +15 -15
- package/dist/chunk-AJQ57VV3.js.map +1 -0
- package/dist/{chunk-CUM2BBPN.js → chunk-AUMWZSE7.js} +19 -10
- package/dist/chunk-AUMWZSE7.js.map +1 -0
- package/dist/{chunk-XYTOI63Z.js → chunk-BGHFOD5A.js} +19 -11
- package/dist/chunk-BGHFOD5A.js.map +1 -0
- package/dist/{chunk-BUE5E2HL.js → chunk-BGLY7EEM.js} +5 -314
- package/dist/chunk-BGLY7EEM.js.map +1 -0
- package/dist/chunk-BIPBQQFI.js +44 -0
- package/dist/chunk-BIPBQQFI.js.map +1 -0
- package/dist/{chunk-S5SLVSVY.js → chunk-BQIFZ2MP.js} +5 -5
- package/dist/chunk-BTTPKDIL.js +34 -0
- package/dist/{chunk-PVGDJXVK.js.map → chunk-BTTPKDIL.js.map} +1 -1
- package/dist/{chunk-E53DSZEN.js → chunk-C26TE27X.js} +3 -3
- package/dist/{chunk-LZ4LDKHZ.js → chunk-CLB32OHM.js} +6 -6
- package/dist/{chunk-Q6IP2ARR.js → chunk-CSOYVTRX.js} +2 -2
- package/dist/{chunk-XOQSEF7X.js → chunk-CVILPI77.js} +6 -6
- package/dist/chunk-D3I2MCN7.js +29 -0
- package/dist/chunk-D3I2MCN7.js.map +1 -0
- package/dist/{chunk-A5OJCXZL.js → chunk-DSUAAZZX.js} +15 -4
- package/dist/chunk-DSUAAZZX.js.map +1 -0
- package/dist/{chunk-HREE4JHI.js → chunk-DYVMXPCF.js} +486 -287
- package/dist/chunk-DYVMXPCF.js.map +1 -0
- package/dist/{chunk-J7VGGECD.js → chunk-DZPFMQZ7.js} +1 -1
- package/dist/chunk-DZPFMQZ7.js.map +1 -0
- package/dist/{chunk-PDLBLTOK.js → chunk-EITZHSX6.js} +9 -9
- package/dist/{chunk-D3J5SLQK.js → chunk-EJHEWCGQ.js} +81 -20
- package/dist/chunk-EJHEWCGQ.js.map +1 -0
- package/dist/{chunk-WUNIWKY4.js → chunk-ELIL2A37.js} +6 -6
- package/dist/{chunk-E6PNLPGJ.js → chunk-FZP32336.js} +806 -470
- package/dist/chunk-FZP32336.js.map +1 -0
- package/dist/{chunk-HZFHQ4AQ.js → chunk-G7A5HLUS.js} +23 -17
- package/dist/chunk-G7A5HLUS.js.map +1 -0
- package/dist/{chunk-CINZGPSJ.js → chunk-GUMZC2BO.js} +2 -2
- package/dist/{chunk-H24WNPA2.js → chunk-GWFSVJ4P.js} +3 -3
- package/dist/{chunk-7XC2HX75.js → chunk-GYPQL6XN.js} +157 -16
- package/dist/chunk-GYPQL6XN.js.map +1 -0
- package/dist/{chunk-TSH56VPU.js → chunk-HAYSYUAB.js} +2 -2
- package/dist/{chunk-TDHTVMPG.js → chunk-HFPASSFV.js} +7 -2
- package/dist/chunk-HFPASSFV.js.map +1 -0
- package/dist/chunk-J2WC5LF4.js +395 -0
- package/dist/chunk-J2WC5LF4.js.map +1 -0
- package/dist/{chunk-PKNLM44P.js → chunk-JH6BPLOP.js} +9 -9
- package/dist/{chunk-KXB4VXHY.js → chunk-JIYDRBVZ.js} +2 -2
- package/dist/chunk-JV3C2MNQ.js +74 -0
- package/dist/chunk-JV3C2MNQ.js.map +1 -0
- package/dist/{chunk-AV5ZEH4M.js → chunk-KVJRXN2E.js} +2 -2
- package/dist/{chunk-JVLCVFQ4.js → chunk-KXEFRMCF.js} +3 -3
- package/dist/{chunk-WFTIGIIS.js → chunk-L6CQ7WIW.js} +5 -5
- package/dist/{chunk-EJUEXZHX.js → chunk-LLJMAULE.js} +2 -2
- package/dist/{chunk-N5Z4EVOR.js → chunk-LNEWDF2A.js} +36 -11
- package/dist/chunk-LNEWDF2A.js.map +1 -0
- package/dist/{chunk-AS7YMSL6.js → chunk-M23RF3LJ.js} +4 -4
- package/dist/{chunk-UYLMNVY6.js → chunk-MDISBSYA.js} +16 -9
- package/dist/chunk-MDISBSYA.js.map +1 -0
- package/dist/{chunk-3F2LKUMU.js → chunk-NIS256NW.js} +2 -2
- package/dist/{chunk-YIAA2X3D.js → chunk-NK5RGJ65.js} +3 -3
- package/dist/chunk-NLDVVWCL.js +84 -0
- package/dist/chunk-NLDVVWCL.js.map +1 -0
- package/dist/{chunk-OQ5PRDDS.js → chunk-NTOIP2E6.js} +6 -6
- package/dist/{chunk-HLPD7B7P.js → chunk-NUEQKLNB.js} +2 -2
- package/dist/{chunk-4J7G5SY4.js → chunk-NZBZ7IS2.js} +72 -24
- package/dist/chunk-NZBZ7IS2.js.map +1 -0
- package/dist/{chunk-3TBLDMJA.js → chunk-O4KMGRXE.js} +5 -2
- package/dist/chunk-O4KMGRXE.js.map +1 -0
- package/dist/{chunk-33PLLLL7.js → chunk-P7NGM24P.js} +6 -6
- package/dist/{chunk-7TWUCYER.js → chunk-PNRA7FAD.js} +7 -7
- package/dist/{chunk-5I4M26Q7.js → chunk-QBSFOKY6.js} +4 -4
- package/dist/{chunk-QJAQNXWI.js → chunk-QCECHYZL.js} +76 -79
- package/dist/chunk-QCECHYZL.js.map +1 -0
- package/dist/{chunk-P4DRWEYC.js → chunk-QVSBTCVP.js} +2 -2
- package/dist/{chunk-AAIUERNB.js → chunk-R5RFK3CB.js} +6 -6
- package/dist/{chunk-WDISJRMG.js → chunk-RMMVQQA6.js} +2 -2
- package/dist/{chunk-KWMXR7XS.js → chunk-S2V7RUCL.js} +8 -1
- package/dist/{chunk-KWMXR7XS.js.map → chunk-S2V7RUCL.js.map} +1 -1
- package/dist/{chunk-5SM35XZR.js → chunk-S5NZBKKQ.js} +2 -2
- package/dist/{chunk-VNA7W7QF.js → chunk-SVH57F5Z.js} +12 -4
- package/dist/chunk-SVH57F5Z.js.map +1 -0
- package/dist/{chunk-WIPEXG6R.js → chunk-SXIMQLGV.js} +2 -2
- package/dist/{chunk-RVLJEII5.js → chunk-SYUBB3WM.js} +256 -187
- package/dist/chunk-SYUBB3WM.js.map +1 -0
- package/dist/{chunk-OAEQLRCY.js → chunk-TN76AKO3.js} +5 -5
- package/dist/{chunk-QN25OCUF.js → chunk-TQ4GD737.js} +5 -5
- package/dist/{chunk-VCD4PSK2.js → chunk-TQZ4DVBC.js} +7 -4
- package/dist/chunk-TQZ4DVBC.js.map +1 -0
- package/dist/{chunk-7K5Q6COX.js → chunk-TVVEYCNW.js} +4 -4
- package/dist/{chunk-DNWXFTLD.js → chunk-TYISDI6B.js} +2 -2
- package/dist/{chunk-4LMA77JH.js → chunk-U2VLVBBH.js} +3 -3
- package/dist/{chunk-5ZE7QHTK.js → chunk-ULVR5KG7.js} +5 -5
- package/dist/{chunk-NVRYW264.js → chunk-UQWIKLQK.js} +18 -15
- package/dist/chunk-UQWIKLQK.js.map +1 -0
- package/dist/{chunk-YV32N67O.js → chunk-URS5GSM7.js} +1 -1
- package/dist/chunk-URS5GSM7.js.map +1 -0
- package/dist/{chunk-4B77XSG5.js → chunk-UV72DEZD.js} +5 -5
- package/dist/{chunk-S2BBISHU.js → chunk-VANZKHTN.js} +3 -3
- package/dist/chunk-VHFXP27N.js +250 -0
- package/dist/chunk-VHFXP27N.js.map +1 -0
- package/dist/{chunk-3Y224NYC.js → chunk-VYISFWVX.js} +2 -2
- package/dist/{chunk-TIHMZFIV.js → chunk-W3IC7QOY.js} +3 -3
- package/dist/{chunk-NRV4Y3PM.js → chunk-W4FBGYGK.js} +1 -1
- package/dist/{chunk-P2BM76KT.js → chunk-WAS4HV46.js} +174 -43
- package/dist/chunk-WAS4HV46.js.map +1 -0
- package/dist/{chunk-57QKLRBV.js → chunk-WB7JSQCP.js} +2 -1
- package/dist/chunk-WB7JSQCP.js.map +1 -0
- package/dist/chunk-WGP57SR3.js +68 -0
- package/dist/chunk-WGP57SR3.js.map +1 -0
- package/dist/{chunk-ECK75GDJ.js → chunk-WQIHRA5F.js} +43 -24
- package/dist/chunk-WQIHRA5F.js.map +1 -0
- package/dist/{chunk-MU7ZHSZE.js → chunk-WZFAPHIK.js} +2 -2
- package/dist/{chunk-T2TTP3KU.js → chunk-X4IDNPOS.js} +2 -2
- package/dist/{chunk-BLAKS3CP.js → chunk-XL3TATMJ.js} +3 -3
- package/dist/{chunk-W4XJFLAL.js → chunk-XPRJSHNI.js} +2 -2
- package/dist/{chunk-5TFP4CUL.js → chunk-XQ3HXAQH.js} +14 -11
- package/dist/chunk-XQ3HXAQH.js.map +1 -0
- package/dist/{chunk-VWAXN2BN.js → chunk-XSFHWBQO.js} +93 -31
- package/dist/chunk-XSFHWBQO.js.map +1 -0
- package/dist/{chunk-DL7ZD3JH.js → chunk-XTAUXTDO.js} +14 -9
- package/dist/chunk-XTAUXTDO.js.map +1 -0
- package/dist/{chunk-JE7EK33X.js → chunk-Y7HRYCNW.js} +2 -2
- package/dist/{chunk-E6EE5B67.js → chunk-YK6BDDHP.js} +4 -4
- package/dist/{chunk-ES36HTSC.js → chunk-YLHM5BQQ.js} +3 -3
- package/dist/{chunk-RN7DY3FC.js → chunk-YMAQDHQM.js} +31 -12
- package/dist/chunk-YMAQDHQM.js.map +1 -0
- package/dist/{chunk-ULARSG5Z.js → chunk-YN6ASZUG.js} +10 -8
- package/dist/{chunk-ULARSG5Z.js.map → chunk-YN6ASZUG.js.map} +1 -1
- package/dist/{chunk-IKKWIPYN.js → chunk-YYQAKIDJ.js} +37 -1
- package/dist/chunk-YYQAKIDJ.js.map +1 -0
- package/dist/{chunk-BJKPPZ6Z.js → chunk-ZFGZ6PZ2.js} +2 -2
- package/dist/{chunk-CJ2ZR7UA.js → chunk-ZI32MKDW.js} +267 -214
- package/dist/chunk-ZI32MKDW.js.map +1 -0
- package/dist/{chunk-64UDOIMI.js → chunk-ZLQ37WJX.js} +2 -2
- package/dist/chunk-ZMAYKGPN.js +351 -0
- package/dist/chunk-ZMAYKGPN.js.map +1 -0
- package/dist/{cli-BUGN3d6_.d.ts → cli-BBHDtG36.d.ts} +5 -5
- package/dist/cli.d.ts +16 -12
- package/dist/cli.js +138 -124
- package/dist/compounding/engine.d.ts +3 -2
- package/dist/compounding/engine.js +24 -23
- 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 +10 -8
- package/dist/connectors/codex-materialize-runner.d.ts +2 -1
- package/dist/connectors/codex-materialize-runner.js +23 -22
- package/dist/connectors/codex-materialize.d.ts +2 -1
- package/dist/connectors/index.d.ts +2 -1
- package/dist/connectors/index.js +26 -25
- package/dist/consolidation-provenance-check.d.ts +3 -2
- package/dist/consolidation-provenance-check.js +4 -4
- package/dist/consolidation-undo.d.ts +3 -2
- package/dist/consolidation-undo.js +10 -7
- package/dist/consolidation-undo.js.map +1 -1
- package/dist/contradiction/index.d.ts +3 -2
- package/dist/converge-config.d.ts +2 -1
- package/dist/convergence-refresh.d.ts +4 -3
- package/dist/convergence-refresh.js +27 -26
- package/dist/conversation-index/backend.d.ts +2 -1
- package/dist/conversation-index/backend.js +2 -2
- 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 +42 -41
- package/dist/day-summary.d.ts +2 -1
- package/dist/delinearize.d.ts +2 -1
- package/dist/direct-answer-wiring.d.ts +2 -1
- package/dist/direct-answer-wiring.js +2 -2
- package/dist/direct-answer.d.ts +2 -1
- package/dist/embedding-fallback.d.ts +2 -1
- package/dist/embedding-fallback.js +2 -2
- package/dist/enrichment/index.d.ts +2 -1
- package/dist/entity-retrieval-boundaries.js +1 -1
- package/dist/entity-retrieval.d.ts +3 -2
- package/dist/entity-retrieval.js +24 -23
- package/dist/entity-schema.d.ts +2 -1
- package/dist/explicit-capture.d.ts +9 -8
- package/dist/explicit-capture.js +11 -9
- package/dist/external-wiki-access.d.ts +127 -0
- package/dist/external-wiki-access.js +186 -0
- package/dist/external-wiki-access.js.map +1 -0
- package/dist/external-wiki-cli.d.ts +13 -0
- package/dist/external-wiki-cli.js +96 -0
- package/dist/external-wiki-cli.js.map +1 -0
- package/dist/external-wiki-collection-registration.d.ts +39 -0
- package/dist/external-wiki-collection-registration.js +79 -0
- package/dist/external-wiki-collection-registration.js.map +1 -0
- package/dist/external-wiki-collection.d.ts +50 -0
- package/dist/external-wiki-collection.js +387 -0
- package/dist/external-wiki-collection.js.map +1 -0
- package/dist/external-wiki-config.d.ts +13 -0
- package/dist/external-wiki-config.js +13 -0
- package/dist/external-wiki-config.js.map +1 -0
- package/dist/external-wiki-guard.d.ts +11 -0
- package/dist/external-wiki-guard.js +18 -0
- package/dist/external-wiki-guard.js.map +1 -0
- package/dist/external-wiki-mcp-tools.d.ts +123 -0
- package/dist/external-wiki-mcp-tools.js +8 -0
- package/dist/external-wiki-mcp-tools.js.map +1 -0
- package/dist/external-wiki-search.d.ts +42 -0
- package/dist/external-wiki-search.js +11 -0
- package/dist/external-wiki-search.js.map +1 -0
- package/dist/external-wiki.d.ts +43 -0
- package/dist/external-wiki.js +16 -0
- package/dist/external-wiki.js.map +1 -0
- package/dist/extraction-error-classification.d.ts +2 -1
- package/dist/extraction-faithfulness.d.ts +2 -1
- package/dist/extraction-judge-telemetry.d.ts +2 -1
- package/dist/extraction-judge-training.d.ts +2 -1
- package/dist/extraction-judge.d.ts +2 -1
- package/dist/extraction-liveness.d.ts +2 -1
- package/dist/extraction-source-grounding-helpers.d.ts +5 -3
- package/dist/extraction-source-grounding-helpers.js +1 -1
- package/dist/extraction-source-grounding-rules.d.ts +2 -1
- package/dist/extraction-source-grounding-rules.js +2 -2
- package/dist/extraction-source-grounding.d.ts +2 -1
- package/dist/extraction-source-grounding.js +5 -5
- package/dist/extraction.d.ts +2 -1
- package/dist/extraction.js +45 -43
- package/dist/fallback-llm.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-snapshot.d.ts +2 -1
- package/dist/graph.d.ts +2 -1
- package/dist/harmonic-retrieval.js +2 -2
- package/dist/importance.d.ts +2 -1
- package/dist/importers/index.d.ts +2 -1
- package/dist/in-flight-reads.d.ts +2 -1
- package/dist/index.d.ts +362 -350
- package/dist/index.js +291 -259
- package/dist/intent.d.ts +2 -1
- package/dist/lcm/engine.d.ts +2 -1
- package/dist/lcm/engine.js +3 -3
- package/dist/lcm/index.d.ts +2 -1
- package/dist/lcm/index.js +8 -8
- 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.d.ts +2 -1
- package/dist/local-llm.js +2 -2
- package/dist/local-model-endpoint.d.ts +2 -1
- package/dist/maintenance/memory-governance.d.ts +2 -1
- package/dist/maintenance/memory-governance.js +24 -23
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.d.ts +3 -2
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +24 -23
- package/dist/maintenance/rebuild-memory-projection.d.ts +3 -2
- package/dist/maintenance/rebuild-memory-projection.js +26 -25
- package/dist/{maintenance-LbuS2rUA.d.ts → maintenance-D2mveyCd.d.ts} +3 -3
- package/dist/mcp-memory-inspector-app.d.ts +14 -10
- package/dist/mcp-read-only-tools.js +1 -1
- package/dist/memory-action-policy.d.ts +2 -1
- package/dist/memory-cache.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-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 +40 -39
- package/dist/namespaces/principal.d.ts +2 -1
- package/dist/namespaces/search.d.ts +2 -1
- package/dist/namespaces/search.js +18 -18
- package/dist/namespaces/storage.d.ts +4 -3
- package/dist/namespaces/storage.js +23 -22
- package/dist/native-knowledge.d.ts +2 -1
- package/dist/offline-sync-impression-drain.d.ts +2 -1
- package/dist/offline-sync-impression-drain.js +11 -9
- package/dist/offline-sync.d.ts +29 -3
- package/dist/offline-sync.js +6 -2
- package/dist/operator-doctor-corpus.d.ts +2 -1
- package/dist/operator-doctor-corpus.js +45 -44
- package/dist/operator-doctor-replica.d.ts +2 -1
- package/dist/operator-doctor-replica.js +3 -3
- package/dist/operator-toolkit.d.ts +4 -3
- package/dist/operator-toolkit.js +59 -55
- package/dist/orchestration/compression-guideline-coordinator.d.ts +3 -2
- package/dist/orchestration/maintenance.d.ts +5 -4
- package/dist/orchestration/maintenance.js +36 -33
- package/dist/{orchestrator-rHiWt32X.d.ts → orchestrator-DJYlwiUv.d.ts} +14 -7
- package/dist/orchestrator.d.ts +9 -8
- package/dist/orchestrator.js +201 -189
- package/dist/page-versioning.js +3 -3
- package/dist/patterns-cli.d.ts +2 -1
- package/dist/{pipeline-W_97O8BV.d.ts → pipeline-CNjY4wk3.d.ts} +1 -1
- package/dist/policy-runtime.d.ts +2 -1
- package/dist/policy-runtime.js +1 -1
- package/dist/proactive-contention.d.ts +2 -1
- package/dist/provenance.d.ts +2 -1
- package/dist/provenance.js +2 -2
- package/dist/{qmd-LWtMofAf.d.ts → qmd-C0UDZidt.d.ts} +9 -7
- package/dist/qmd-preflight.d.ts +5 -4
- package/dist/qmd-preflight.js +2 -2
- package/dist/qmd-recall-cache.d.ts +2 -1
- package/dist/qmd.d.ts +4 -3
- package/dist/qmd.js +5 -5
- 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-planner-llm.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 +8 -1
- package/dist/recall-xray.js +2 -2
- package/dist/reconcile/cursor.d.ts +12 -2
- package/dist/reconcile/cursor.js +46 -7
- package/dist/reconcile/cursor.js.map +1 -1
- package/dist/reconcile/manifest.d.ts +9 -2
- package/dist/reconcile/manifest.js +15 -268
- package/dist/reconcile/manifest.js.map +1 -1
- package/dist/reconcile/plan.d.ts +4 -1
- package/dist/reconcile/plan.js +4 -4
- 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-divergence.js +2 -2
- package/dist/replica-peers-config.d.ts +2 -1
- package/dist/resolve-auth-token.d.ts +2 -1
- package/dist/resume-bundles.js +12 -10
- package/dist/retrieval-agents.d.ts +4 -3
- package/dist/retrieval-agents.js +2 -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/schemas.d.ts +32 -32
- package/dist/{scope-profiles-BiQKOE-p.d.ts → scope-profiles-BJzp2F5A.d.ts} +1 -1
- package/dist/search/embed-helper.d.ts +2 -1
- package/dist/search/embed-helper.js +3 -3
- package/dist/search/factory.d.ts +4 -2
- package/dist/search/factory.js +20 -18
- package/dist/search/index.d.ts +2 -1
- package/dist/search/index.js +22 -22
- package/dist/search/lancedb-backend.d.ts +2 -1
- package/dist/search/lancedb-backend.js +4 -4
- package/dist/search/meilisearch-backend.d.ts +2 -1
- package/dist/search/meilisearch-backend.js +4 -4
- package/dist/search/noop-backend.d.ts +2 -1
- package/dist/search/orama-backend.d.ts +2 -1
- package/dist/search/orama-backend.js +4 -4
- package/dist/search/port.d.ts +18 -1
- package/dist/search/port.js +1 -1
- package/dist/search/remote-backend.d.ts +2 -1
- package/dist/search/remote-backend.js +2 -2
- package/dist/secure-store/index.js +2 -2
- package/dist/{semantic-consolidation-dHYCyqgh.d.ts → semantic-consolidation-D4KAZO2G.d.ts} +1 -1
- package/dist/semantic-consolidation.d.ts +3 -2
- package/dist/semantic-consolidation.js +25 -24
- package/dist/semantic-rule-promotion.js +23 -22
- package/dist/semantic-rule-verifier.d.ts +2 -1
- package/dist/semantic-rule-verifier.js +24 -23
- package/dist/{service-DJG3KCQ4.d.ts → service-BeDxSqE-.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/shared-context/manager.js +2 -2
- package/dist/signal.d.ts +2 -1
- package/dist/source-agent-qualifier.d.ts +2 -1
- package/dist/source-agent-qualifier.js +42 -40
- package/dist/{storage-F9FVh-w9.d.ts → storage-DfjxusTE.d.ts} +24 -12
- package/dist/storage.d.ts +3 -2
- package/dist/storage.js +25 -24
- package/dist/summarizer.d.ts +2 -1
- package/dist/summarizer.js +5 -5
- package/dist/summary-snapshot.d.ts +2 -1
- 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/tool-scoped-memory.d.ts +5 -1
- package/dist/tool-scoped-memory.js +3 -1
- package/dist/topics.d.ts +2 -1
- package/dist/transcript.d.ts +2 -1
- package/dist/transfer/capsule-import.js +4 -4
- package/dist/transfer/types.d.ts +12 -12
- package/dist/trust-score-stage.d.ts +2 -1
- package/dist/trust-score.d.ts +2 -1
- package/dist/{types-rEwubvim.d.ts → types-yX_FwERf.d.ts} +8 -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 +24 -23
- package/dist/write-envelope.d.ts +2 -1
- package/package.json +2 -2
- package/src/abort-error.ts +17 -0
- package/src/access-admin-ops-surface.ts +1 -0
- package/src/access-boundary.ts +1 -0
- package/src/access-coding-context-resolution.ts +42 -0
- package/src/access-extraction-force-flush.ts +38 -2
- package/src/access-http-lcm-compaction.ts +5 -2
- package/src/access-http-offline-stream.ts +79 -0
- package/src/access-http.test.ts +310 -3
- package/src/access-http.ts +85 -21
- package/src/access-mcp-output-schemas.ts +36 -0
- package/src/access-mcp.test.ts +95 -1
- package/src/access-mcp.ts +7 -9
- package/src/access-observe-helpers.ts +64 -22
- package/src/access-observe-write-surface.ts +8 -0
- package/src/access-offline-manifest.ts +54 -0
- package/src/access-operations-batch.ts +3 -3
- package/src/access-operations.ts +13 -7
- package/src/access-recall-concurrency.ts +3 -27
- package/src/access-recall-surface.ts +3 -0
- package/src/access-service-health.test.ts +32 -7
- package/src/access-service-namespace.test.ts +55 -0
- package/src/access-service-observe-idempotency.test.ts +34 -2
- package/src/access-service-observe-lcm-parity.test.ts +138 -3
- package/src/access-service-observe-scope.test.ts +160 -1
- package/src/access-service-offline-file-content.test.ts +345 -1
- package/src/access-service.ts +86 -59
- package/src/access-surface-catalog.ts +4 -0
- package/src/admin/admin-surfaces.ts +2 -0
- package/src/binary-lifecycle/pipeline.test.ts +106 -1
- package/src/binary-lifecycle/pipeline.ts +3 -0
- package/src/coding/git-context.test.ts +47 -0
- package/src/coding/git-context.ts +129 -27
- package/src/config.test.ts +24 -0
- package/src/config.ts +9 -12
- package/src/connector-provenance.test.ts +101 -0
- package/src/consolidation-undo.ts +3 -0
- package/src/convergence-refresh.ts +14 -2
- package/src/correction/correction-access-wiring.ts +9 -7
- package/src/correction/correction-executor.test.ts +206 -2
- package/src/correction/correction-executor.ts +29 -20
- package/src/correction/correction-planner.test.ts +36 -0
- package/src/correction/correction-planner.ts +9 -15
- package/src/correction/correction-service.ts +3 -1
- package/src/correction/correction-storage-integration.test.ts +77 -0
- package/src/correction/correction-surfaces.test.ts +41 -28
- package/src/correction/passive-capture.test.ts +59 -0
- package/src/correction/passive-capture.ts +4 -1
- package/src/entity-retrieval-boundaries.ts +17 -4
- package/src/entity-retrieval.ts +27 -6
- package/src/external-wiki-access.test.ts +65 -0
- package/src/external-wiki-access.ts +31 -0
- package/src/external-wiki-cli.ts +92 -0
- package/src/external-wiki-collection-registration.test.ts +163 -0
- package/src/external-wiki-collection-registration.ts +101 -0
- package/src/external-wiki-collection.test.ts +502 -0
- package/src/external-wiki-collection.ts +503 -0
- package/src/external-wiki-config.test.ts +135 -0
- package/src/external-wiki-config.ts +100 -0
- package/src/external-wiki-guard.ts +83 -0
- package/src/external-wiki-mcp-tools.ts +23 -0
- package/src/external-wiki-search.test.ts +301 -0
- package/src/external-wiki-search.ts +362 -0
- package/src/external-wiki.test.ts +240 -0
- package/src/external-wiki.ts +453 -0
- package/src/extraction-scope-source-agent.test.ts +21 -0
- package/src/extraction-source-grounding-helpers.ts +243 -53
- package/src/extraction-source-grounding-rules.ts +133 -39
- package/src/extraction-source-grounding.test.ts +363 -33
- package/src/extraction-timeout.test.ts +1 -0
- package/src/extraction-watermark.test.ts +150 -7
- package/src/extraction.ts +8 -1
- package/src/index.ts +8 -0
- package/src/mcp-read-only-tools.ts +1 -0
- package/src/offline-sync.test.ts +194 -0
- package/src/offline-sync.ts +217 -13
- package/src/orchestration/external-wiki-recall-guard.test.ts +94 -0
- package/src/orchestration/extraction-persist.ts +19 -2
- package/src/orchestration/extraction-run-helpers.ts +37 -19
- package/src/orchestration/extraction-run.test.ts +171 -2
- package/src/orchestration/extraction-run.ts +11 -13
- package/src/orchestration/extraction-watermark.ts +106 -17
- package/src/orchestration/generic-recall-paths.ts +14 -0
- package/src/orchestration/orchestrator-helpers.ts +2 -0
- package/src/orchestration/orchestrator-init.ts +9 -0
- package/src/orchestration/recall-internal-deps.ts +6 -0
- package/src/orchestration/recall-internal.ts +154 -33
- package/src/orchestration/recall-rerank-coordinator.ts +60 -25
- package/src/orchestration/recall-search-pipeline.test.ts +75 -1
- package/src/orchestration/recall-search-pipeline.ts +50 -10
- package/src/orchestrator-convergence-refresh.test.ts +36 -2
- package/src/orchestrator.ts +13 -3
- package/src/page-versioning.ts +5 -19
- package/src/procedural/procedure-recall.ts +5 -1
- package/src/provenance-surfaces.test.ts +3 -0
- package/src/qmd-client.test.ts +78 -17
- package/src/qmd.test.ts +24 -0
- package/src/qmd.ts +224 -315
- package/src/recall-explain-renderer.ts +3 -0
- package/src/recall-rerank-coordinator.test.ts +61 -0
- package/src/recall-xray-renderer.test.ts +6 -0
- package/src/recall-xray.test.ts +88 -1
- package/src/recall-xray.ts +15 -0
- package/src/reconcile/cursor.test.ts +48 -0
- package/src/reconcile/cursor.ts +58 -11
- package/src/reconcile/manifest.test.ts +4 -0
- package/src/reconcile/manifest.ts +41 -51
- package/src/reconcile/plan.test.ts +4 -0
- package/src/reconcile/plan.ts +28 -16
- package/src/search/factory.ts +6 -6
- package/src/search/port.ts +25 -9
- package/src/secure-store/secure-fs.ts +20 -12
- package/src/secure-store/secure-store.test.ts +41 -2
- package/src/spaces/index.test.ts +61 -2
- package/src/spaces/index.ts +44 -18
- package/src/storage/entity-alias-config.ts +97 -0
- package/src/storage/entity-canonical-id-lock.ts +53 -23
- package/src/storage/entity-canonical-id-migration.ts +53 -22
- package/src/storage/entity-canonical-id-references.ts +3 -0
- package/src/storage/entity-id-safety.ts +7 -0
- package/src/storage/entity-store.ts +6 -2
- package/src/storage/memory-frontmatter-metadata.ts +4 -9
- package/src/storage/memory-migration-serialization.ts +10 -15
- package/src/storage/tombstone-blocked-capture-mutation.ts +52 -64
- package/src/storage/tombstone-blocked-capture-sync.ts +244 -199
- package/src/storage-deletion-revisions.test.ts +91 -0
- package/src/storage-fact-hash-index-defer.test.ts +446 -163
- package/src/storage.ts +534 -245
- package/src/tool-scoped-memory.ts +10 -0
- package/src/transfer/capsule-import.ts +13 -0
- package/src/transfer/capsule-merge.ts +13 -0
- package/src/trust-score-recall-paths.test.ts +75 -1
- package/src/types.ts +7 -3
- package/dist/capsule-merge-IDWJPCWK.js.map +0 -1
- package/dist/chunk-3CE77VAP.js.map +0 -1
- package/dist/chunk-3TBLDMJA.js.map +0 -1
- package/dist/chunk-4J7G5SY4.js.map +0 -1
- package/dist/chunk-57QKLRBV.js.map +0 -1
- package/dist/chunk-5TFP4CUL.js.map +0 -1
- package/dist/chunk-7XC2HX75.js.map +0 -1
- package/dist/chunk-A5OJCXZL.js.map +0 -1
- package/dist/chunk-BUE5E2HL.js.map +0 -1
- package/dist/chunk-CJ2ZR7UA.js.map +0 -1
- package/dist/chunk-CUM2BBPN.js.map +0 -1
- package/dist/chunk-D3J5SLQK.js.map +0 -1
- package/dist/chunk-DL7ZD3JH.js.map +0 -1
- package/dist/chunk-DLVPUX7U.js.map +0 -1
- package/dist/chunk-E6PNLPGJ.js.map +0 -1
- package/dist/chunk-ECK75GDJ.js.map +0 -1
- package/dist/chunk-HREE4JHI.js.map +0 -1
- package/dist/chunk-HZFHQ4AQ.js.map +0 -1
- package/dist/chunk-IKKWIPYN.js.map +0 -1
- package/dist/chunk-J7VGGECD.js.map +0 -1
- package/dist/chunk-N5Z4EVOR.js.map +0 -1
- package/dist/chunk-NVRYW264.js.map +0 -1
- package/dist/chunk-P2BM76KT.js.map +0 -1
- package/dist/chunk-PVGDJXVK.js +0 -21
- package/dist/chunk-QJAQNXWI.js.map +0 -1
- package/dist/chunk-RN7DY3FC.js.map +0 -1
- package/dist/chunk-RVLJEII5.js.map +0 -1
- package/dist/chunk-S2NCTZGJ.js.map +0 -1
- package/dist/chunk-TDHTVMPG.js.map +0 -1
- package/dist/chunk-UYLMNVY6.js.map +0 -1
- package/dist/chunk-VCD4PSK2.js.map +0 -1
- package/dist/chunk-VNA7W7QF.js.map +0 -1
- package/dist/chunk-VWAXN2BN.js.map +0 -1
- package/dist/chunk-WOCNN776.js.map +0 -1
- package/dist/chunk-XYTOI63Z.js.map +0 -1
- package/dist/chunk-YV32N67O.js.map +0 -1
- /package/dist/{auto-sync-5OE5QOZB.js.map → auto-sync-UPTUVE4X.js.map} +0 -0
- /package/dist/{chunk-VUPTKTDV.js.map → chunk-2MRHJNWL.js.map} +0 -0
- /package/dist/{chunk-JBPKEARU.js.map → chunk-2QSZNTDO.js.map} +0 -0
- /package/dist/{chunk-5IVLTFOV.js.map → chunk-44N6ESJ4.js.map} +0 -0
- /package/dist/{chunk-HO3QBJEY.js.map → chunk-5546W5SI.js.map} +0 -0
- /package/dist/{chunk-U7GW7NE7.js.map → chunk-5CEECY6P.js.map} +0 -0
- /package/dist/{chunk-4UH4BTGK.js.map → chunk-5SNZSWWR.js.map} +0 -0
- /package/dist/{chunk-6CZPDBJS.js.map → chunk-5U7ID3EZ.js.map} +0 -0
- /package/dist/{chunk-GF22HWS2.js.map → chunk-6NNNURLK.js.map} +0 -0
- /package/dist/{chunk-227UFSEQ.js.map → chunk-7RZJWZ46.js.map} +0 -0
- /package/dist/{chunk-E5UK4VPE.js.map → chunk-7SHJHPTY.js.map} +0 -0
- /package/dist/{chunk-UVIKWC7M.js.map → chunk-7YERSTN7.js.map} +0 -0
- /package/dist/{chunk-S5SLVSVY.js.map → chunk-BQIFZ2MP.js.map} +0 -0
- /package/dist/{chunk-E53DSZEN.js.map → chunk-C26TE27X.js.map} +0 -0
- /package/dist/{chunk-LZ4LDKHZ.js.map → chunk-CLB32OHM.js.map} +0 -0
- /package/dist/{chunk-Q6IP2ARR.js.map → chunk-CSOYVTRX.js.map} +0 -0
- /package/dist/{chunk-XOQSEF7X.js.map → chunk-CVILPI77.js.map} +0 -0
- /package/dist/{chunk-PDLBLTOK.js.map → chunk-EITZHSX6.js.map} +0 -0
- /package/dist/{chunk-WUNIWKY4.js.map → chunk-ELIL2A37.js.map} +0 -0
- /package/dist/{chunk-CINZGPSJ.js.map → chunk-GUMZC2BO.js.map} +0 -0
- /package/dist/{chunk-H24WNPA2.js.map → chunk-GWFSVJ4P.js.map} +0 -0
- /package/dist/{chunk-TSH56VPU.js.map → chunk-HAYSYUAB.js.map} +0 -0
- /package/dist/{chunk-PKNLM44P.js.map → chunk-JH6BPLOP.js.map} +0 -0
- /package/dist/{chunk-KXB4VXHY.js.map → chunk-JIYDRBVZ.js.map} +0 -0
- /package/dist/{chunk-AV5ZEH4M.js.map → chunk-KVJRXN2E.js.map} +0 -0
- /package/dist/{chunk-JVLCVFQ4.js.map → chunk-KXEFRMCF.js.map} +0 -0
- /package/dist/{chunk-WFTIGIIS.js.map → chunk-L6CQ7WIW.js.map} +0 -0
- /package/dist/{chunk-EJUEXZHX.js.map → chunk-LLJMAULE.js.map} +0 -0
- /package/dist/{chunk-AS7YMSL6.js.map → chunk-M23RF3LJ.js.map} +0 -0
- /package/dist/{chunk-3F2LKUMU.js.map → chunk-NIS256NW.js.map} +0 -0
- /package/dist/{chunk-YIAA2X3D.js.map → chunk-NK5RGJ65.js.map} +0 -0
- /package/dist/{chunk-OQ5PRDDS.js.map → chunk-NTOIP2E6.js.map} +0 -0
- /package/dist/{chunk-HLPD7B7P.js.map → chunk-NUEQKLNB.js.map} +0 -0
- /package/dist/{chunk-33PLLLL7.js.map → chunk-P7NGM24P.js.map} +0 -0
- /package/dist/{chunk-7TWUCYER.js.map → chunk-PNRA7FAD.js.map} +0 -0
- /package/dist/{chunk-5I4M26Q7.js.map → chunk-QBSFOKY6.js.map} +0 -0
- /package/dist/{chunk-P4DRWEYC.js.map → chunk-QVSBTCVP.js.map} +0 -0
- /package/dist/{chunk-AAIUERNB.js.map → chunk-R5RFK3CB.js.map} +0 -0
- /package/dist/{chunk-WDISJRMG.js.map → chunk-RMMVQQA6.js.map} +0 -0
- /package/dist/{chunk-5SM35XZR.js.map → chunk-S5NZBKKQ.js.map} +0 -0
- /package/dist/{chunk-WIPEXG6R.js.map → chunk-SXIMQLGV.js.map} +0 -0
- /package/dist/{chunk-OAEQLRCY.js.map → chunk-TN76AKO3.js.map} +0 -0
- /package/dist/{chunk-QN25OCUF.js.map → chunk-TQ4GD737.js.map} +0 -0
- /package/dist/{chunk-7K5Q6COX.js.map → chunk-TVVEYCNW.js.map} +0 -0
- /package/dist/{chunk-DNWXFTLD.js.map → chunk-TYISDI6B.js.map} +0 -0
- /package/dist/{chunk-4LMA77JH.js.map → chunk-U2VLVBBH.js.map} +0 -0
- /package/dist/{chunk-5ZE7QHTK.js.map → chunk-ULVR5KG7.js.map} +0 -0
- /package/dist/{chunk-4B77XSG5.js.map → chunk-UV72DEZD.js.map} +0 -0
- /package/dist/{chunk-S2BBISHU.js.map → chunk-VANZKHTN.js.map} +0 -0
- /package/dist/{chunk-3Y224NYC.js.map → chunk-VYISFWVX.js.map} +0 -0
- /package/dist/{chunk-TIHMZFIV.js.map → chunk-W3IC7QOY.js.map} +0 -0
- /package/dist/{chunk-NRV4Y3PM.js.map → chunk-W4FBGYGK.js.map} +0 -0
- /package/dist/{chunk-MU7ZHSZE.js.map → chunk-WZFAPHIK.js.map} +0 -0
- /package/dist/{chunk-T2TTP3KU.js.map → chunk-X4IDNPOS.js.map} +0 -0
- /package/dist/{chunk-BLAKS3CP.js.map → chunk-XL3TATMJ.js.map} +0 -0
- /package/dist/{chunk-W4XJFLAL.js.map → chunk-XPRJSHNI.js.map} +0 -0
- /package/dist/{chunk-JE7EK33X.js.map → chunk-Y7HRYCNW.js.map} +0 -0
- /package/dist/{chunk-E6EE5B67.js.map → chunk-YK6BDDHP.js.map} +0 -0
- /package/dist/{chunk-ES36HTSC.js.map → chunk-YLHM5BQQ.js.map} +0 -0
- /package/dist/{chunk-BJKPPZ6Z.js.map → chunk-ZFGZ6PZ2.js.map} +0 -0
- /package/dist/{chunk-64UDOIMI.js.map → chunk-ZLQ37WJX.js.map} +0 -0
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
1
2
|
import assert from "node:assert/strict";
|
|
2
3
|
import { mkdtemp, mkdir, readFile, readdir, rename, rm, stat, utimes, writeFile } from "node:fs/promises";
|
|
3
4
|
import { existsSync } from "node:fs";
|
|
@@ -10,8 +11,12 @@ import {
|
|
|
10
11
|
buildExplicitCaptureDedupKey,
|
|
11
12
|
TombstoneBlockedCaptureIndex,
|
|
12
13
|
} from "./storage/tombstone-blocked-capture-index.js";
|
|
13
|
-
import {
|
|
14
|
-
|
|
14
|
+
import {
|
|
15
|
+
buildCapturePathLockIdentity,
|
|
16
|
+
runTombstoneBlockedMutation,
|
|
17
|
+
TombstoneBlockedCaptureWriteLock,
|
|
18
|
+
} from "./storage/tombstone-blocked-capture-mutation.js";
|
|
19
|
+
import { isEncryptedFile, SECURE_STORE_ENVELOPE_OVERHEAD_BYTES } from "./secure-store/secure-fs.js";
|
|
15
20
|
import type { MemoryFile } from "./types.js";
|
|
16
21
|
|
|
17
22
|
// Issue #1909 (Part B): writeMemory("fact") used to rewrite the whole
|
|
@@ -54,6 +59,171 @@ test("capture write locks combine streamed hashes in canonical order", async ()
|
|
|
54
59
|
);
|
|
55
60
|
});
|
|
56
61
|
});
|
|
62
|
+
test("active mutations recheck queued-review replacements inside coordinated identity locks", async () => {
|
|
63
|
+
const pathname = "/memory/facts/active.md";
|
|
64
|
+
const active = {
|
|
65
|
+
path: pathname,
|
|
66
|
+
content: "active content",
|
|
67
|
+
frontmatter: { id: "active", category: "fact", status: "active" },
|
|
68
|
+
} as MemoryFile;
|
|
69
|
+
const queued = {
|
|
70
|
+
path: pathname,
|
|
71
|
+
content: "queued content",
|
|
72
|
+
frontmatter: {
|
|
73
|
+
id: "queued",
|
|
74
|
+
category: "fact",
|
|
75
|
+
status: "pending_review",
|
|
76
|
+
tags: ["queued-review"],
|
|
77
|
+
sourceConnector: "provider-a",
|
|
78
|
+
},
|
|
79
|
+
} as MemoryFile;
|
|
80
|
+
const activeIdentity = buildExplicitCaptureDedupKey(
|
|
81
|
+
active.content,
|
|
82
|
+
active.frontmatter.category,
|
|
83
|
+
active.frontmatter.sourceConnector
|
|
84
|
+
);
|
|
85
|
+
const queuedIdentity = buildExplicitCaptureDedupKey(
|
|
86
|
+
queued.content,
|
|
87
|
+
queued.frontmatter.category,
|
|
88
|
+
queued.frontmatter.sourceConnector
|
|
89
|
+
);
|
|
90
|
+
let current = active;
|
|
91
|
+
const lockIdentities: string[][] = [];
|
|
92
|
+
let writes = 0;
|
|
93
|
+
|
|
94
|
+
await runTombstoneBlockedMutation(
|
|
95
|
+
{
|
|
96
|
+
readCurrent: async () => current,
|
|
97
|
+
isBlocked: () => false,
|
|
98
|
+
isQueuedReview: (memory) => memory?.frontmatter.id === queued.frontmatter.id,
|
|
99
|
+
memoryIdentity: (memory) => (memory.frontmatter.id === active.frontmatter.id ? activeIdentity : queuedIdentity),
|
|
100
|
+
prepareWrite: async () => "marker",
|
|
101
|
+
commitWrite: async () => {},
|
|
102
|
+
discardWrite: async () => {},
|
|
103
|
+
markUntrusted: () => {},
|
|
104
|
+
writeStorageSecureFile: async () => {
|
|
105
|
+
assert.equal(lockIdentities.length, 2);
|
|
106
|
+
assert.ok(lockIdentities[1]?.includes(queuedIdentity));
|
|
107
|
+
writes += 1;
|
|
108
|
+
},
|
|
109
|
+
withCaptureWriteLock: async (task, identity) => {
|
|
110
|
+
const identities = Array.isArray(identity) ? [...identity] : [identity];
|
|
111
|
+
lockIdentities.push(identities);
|
|
112
|
+
if (lockIdentities.length === 1) current = queued;
|
|
113
|
+
await task();
|
|
114
|
+
},
|
|
115
|
+
logWarning: () => {},
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
blocked: false,
|
|
119
|
+
pathname,
|
|
120
|
+
fileContent: "replacement",
|
|
121
|
+
identity: [
|
|
122
|
+
buildCapturePathLockIdentity(pathname),
|
|
123
|
+
activeIdentity,
|
|
124
|
+
buildExplicitCaptureDedupKey("replacement", "fact", undefined),
|
|
125
|
+
],
|
|
126
|
+
updateIndex: async () => {},
|
|
127
|
+
}
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
assert.equal(writes, 1);
|
|
131
|
+
assert.equal(lockIdentities.length, 2);
|
|
132
|
+
assert.ok(lockIdentities[0]?.includes(activeIdentity));
|
|
133
|
+
assert.ok(lockIdentities[0]?.includes(buildCapturePathLockIdentity(pathname)));
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
test("shared memory writes include their pathname in the capture lock union", async () => {
|
|
137
|
+
await withMemoryDir(async (dir) => {
|
|
138
|
+
const storage = new StorageManager(dir);
|
|
139
|
+
await storage.ensureDirectories();
|
|
140
|
+
const storageWithOverride = storage as unknown as {
|
|
141
|
+
withTombstoneBlockedCaptureWriteLock: (
|
|
142
|
+
task: () => Promise<unknown>,
|
|
143
|
+
identity?: string | readonly string[]
|
|
144
|
+
) => Promise<unknown>;
|
|
145
|
+
};
|
|
146
|
+
const originalLock = storageWithOverride.withTombstoneBlockedCaptureWriteLock.bind(storage);
|
|
147
|
+
const captured: Array<string | readonly string[] | undefined> = [];
|
|
148
|
+
storageWithOverride.withTombstoneBlockedCaptureWriteLock = async (task, identity) => {
|
|
149
|
+
captured.push(identity);
|
|
150
|
+
return await originalLock(task, identity);
|
|
151
|
+
};
|
|
152
|
+
try {
|
|
153
|
+
const written = await storage.writeMemory("fact", "Every writer shares the deterministic pathname lock.", {
|
|
154
|
+
source: "test",
|
|
155
|
+
});
|
|
156
|
+
const memory = await storage.getMemoryById(written.id);
|
|
157
|
+
assert.ok(memory);
|
|
158
|
+
const pathIdentity = buildCapturePathLockIdentity(memory.path);
|
|
159
|
+
assert.ok(captured.some((identity) => (Array.isArray(identity) ? identity : [identity]).includes(pathIdentity)));
|
|
160
|
+
} finally {
|
|
161
|
+
storageWithOverride.withTombstoneBlockedCaptureWriteLock = originalLock;
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
test("offline sync reserves a marker when its locked snapshot became blocked with the same identity", async () => {
|
|
167
|
+
await withMemoryDir(async (dir) => {
|
|
168
|
+
const storage = new StorageManager(dir);
|
|
169
|
+
await storage.ensureDirectories();
|
|
170
|
+
const target = path.join(dir, "memory", "facts", "same-identity.md");
|
|
171
|
+
const active = {
|
|
172
|
+
path: target,
|
|
173
|
+
content: "Same identity, different review state.",
|
|
174
|
+
frontmatter: {
|
|
175
|
+
id: "same-identity",
|
|
176
|
+
category: "fact",
|
|
177
|
+
status: "active",
|
|
178
|
+
sourceConnector: "provider-a",
|
|
179
|
+
},
|
|
180
|
+
} as MemoryFile;
|
|
181
|
+
const blocked = {
|
|
182
|
+
...active,
|
|
183
|
+
frontmatter: {
|
|
184
|
+
...active.frontmatter,
|
|
185
|
+
status: "pending_review",
|
|
186
|
+
blockedBy: "same-identity-tombstone",
|
|
187
|
+
},
|
|
188
|
+
} as MemoryFile;
|
|
189
|
+
let readCount = 0;
|
|
190
|
+
let prepared = 0;
|
|
191
|
+
const fakeIndex = {
|
|
192
|
+
withCaptureWriteLock: async <T>(task: () => Promise<T>) => await task(),
|
|
193
|
+
prepareWrite: async () => {
|
|
194
|
+
prepared += 1;
|
|
195
|
+
return "same-identity-marker";
|
|
196
|
+
},
|
|
197
|
+
commitWrite: async () => {},
|
|
198
|
+
discardWrite: async () => {},
|
|
199
|
+
markUntrusted: () => {},
|
|
200
|
+
};
|
|
201
|
+
const storageWithOverrides = storage as unknown as {
|
|
202
|
+
readMemoryByPath: (pathname: string) => Promise<MemoryFile | null>;
|
|
203
|
+
getTombstoneBlockedCaptureIndex: () => typeof fakeIndex;
|
|
204
|
+
invalidateAfterOfflineSyncMutation: (pathname: string, marker?: string) => Promise<void>;
|
|
205
|
+
runTombstoneBlockedOfflineSyncMutation: (
|
|
206
|
+
pathname: string,
|
|
207
|
+
after: MemoryFile | null,
|
|
208
|
+
write: () => Promise<void>
|
|
209
|
+
) => Promise<void>;
|
|
210
|
+
};
|
|
211
|
+
const originalRead = storageWithOverrides.readMemoryByPath.bind(storage);
|
|
212
|
+
const originalIndex = storageWithOverrides.getTombstoneBlockedCaptureIndex.bind(storage);
|
|
213
|
+
const originalInvalidate = storageWithOverrides.invalidateAfterOfflineSyncMutation.bind(storage);
|
|
214
|
+
storageWithOverrides.readMemoryByPath = async () => (readCount++ === 0 ? active : blocked);
|
|
215
|
+
storageWithOverrides.getTombstoneBlockedCaptureIndex = () => fakeIndex;
|
|
216
|
+
storageWithOverrides.invalidateAfterOfflineSyncMutation = async () => {};
|
|
217
|
+
try {
|
|
218
|
+
await storageWithOverrides.runTombstoneBlockedOfflineSyncMutation(target, active, async () => {});
|
|
219
|
+
assert.equal(prepared, 1);
|
|
220
|
+
} finally {
|
|
221
|
+
storageWithOverrides.readMemoryByPath = originalRead;
|
|
222
|
+
storageWithOverrides.getTombstoneBlockedCaptureIndex = originalIndex;
|
|
223
|
+
storageWithOverrides.invalidateAfterOfflineSyncMutation = originalInvalidate;
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
});
|
|
57
227
|
|
|
58
228
|
test("deferred fact writes do NO per-fact index saves; a single flush persists all", async () => {
|
|
59
229
|
await withMemoryDir(async (dir) => {
|
|
@@ -1662,6 +1832,45 @@ test("blocked chunk writes recheck identity after waiting on the capture lock",
|
|
|
1662
1832
|
});
|
|
1663
1833
|
});
|
|
1664
1834
|
|
|
1835
|
+
test("chunk rewrites reacquire discovered identities in canonical lock order", async () => {
|
|
1836
|
+
await withMemoryDir(async (dir) => {
|
|
1837
|
+
const storage = new StorageManager(dir);
|
|
1838
|
+
await storage.ensureDirectories();
|
|
1839
|
+
let queuedContent = "";
|
|
1840
|
+
let blockedContent = "";
|
|
1841
|
+
for (let index = 0; index < 100 && queuedContent.length === 0; index += 1) {
|
|
1842
|
+
const queuedCandidate = `queued identity ${index}`;
|
|
1843
|
+
const blockedCandidate = `blocked identity ${index}`;
|
|
1844
|
+
const queuedHash = createHash("sha256")
|
|
1845
|
+
.update(buildExplicitCaptureDedupKey(queuedCandidate, "fact", "provider-a"))
|
|
1846
|
+
.digest("hex");
|
|
1847
|
+
const blockedHash = createHash("sha256")
|
|
1848
|
+
.update(buildExplicitCaptureDedupKey(blockedCandidate, "fact", "provider-a"))
|
|
1849
|
+
.digest("hex");
|
|
1850
|
+
if (queuedHash < blockedHash) {
|
|
1851
|
+
queuedContent = queuedCandidate;
|
|
1852
|
+
blockedContent = blockedCandidate;
|
|
1853
|
+
}
|
|
1854
|
+
}
|
|
1855
|
+
assert.ok(queuedContent.length > 0);
|
|
1856
|
+
|
|
1857
|
+
await storage.writeChunk("nested-lock-order", 0, 1, "fact", queuedContent, {
|
|
1858
|
+
source: "explicit-inline-review",
|
|
1859
|
+
sourceConnector: "provider-a",
|
|
1860
|
+
status: "pending_review",
|
|
1861
|
+
tags: ["queued-review"],
|
|
1862
|
+
});
|
|
1863
|
+
const id = await storage.writeChunk("nested-lock-order", 0, 1, "fact", blockedContent, {
|
|
1864
|
+
source: "offline-sync",
|
|
1865
|
+
sourceConnector: "provider-a",
|
|
1866
|
+
status: "pending_review",
|
|
1867
|
+
blockedBy: "tombstone-lock-order",
|
|
1868
|
+
});
|
|
1869
|
+
|
|
1870
|
+
assert.equal((await storage.getMemoryById(id))?.content, blockedContent);
|
|
1871
|
+
});
|
|
1872
|
+
});
|
|
1873
|
+
|
|
1665
1874
|
test("offline sync rechecks blocked identity after waiting on the capture lock", async () => {
|
|
1666
1875
|
await withMemoryDir(async (dir) => {
|
|
1667
1876
|
const storage = new StorageManager(dir);
|
|
@@ -1725,6 +1934,84 @@ test("offline sync rechecks blocked identity after waiting on the capture lock",
|
|
|
1725
1934
|
assert.equal(matches[0]?.frontmatter.id, captureResult.id);
|
|
1726
1935
|
});
|
|
1727
1936
|
});
|
|
1937
|
+
test("offline sync locks queued-review target identities before replacement", async () => {
|
|
1938
|
+
await withMemoryDir(async (dir) => {
|
|
1939
|
+
const storage = new StorageManager(dir);
|
|
1940
|
+
await storage.ensureDirectories();
|
|
1941
|
+
const queuedContent = "A queued review must not disappear during capture dedupe.";
|
|
1942
|
+
const queuedResult = await storage.writeMemory("fact", queuedContent, {
|
|
1943
|
+
source: "explicit-inline-review",
|
|
1944
|
+
sourceConnector: "provider-a",
|
|
1945
|
+
status: "pending_review",
|
|
1946
|
+
tags: ["explicit-capture", "queued-review"],
|
|
1947
|
+
});
|
|
1948
|
+
const queuedMemory = await storage.getMemoryById(queuedResult.id);
|
|
1949
|
+
assert.ok(queuedMemory);
|
|
1950
|
+
const queuedIdentity = buildExplicitCaptureDedupKey(queuedContent, "fact", "provider-a");
|
|
1951
|
+
const entered = Promise.withResolvers<void>();
|
|
1952
|
+
const release = Promise.withResolvers<void>();
|
|
1953
|
+
const held = storage.withTombstoneBlockedCaptureWriteLock(async () => {
|
|
1954
|
+
entered.resolve();
|
|
1955
|
+
await release.promise;
|
|
1956
|
+
}, queuedIdentity);
|
|
1957
|
+
await entered.promise;
|
|
1958
|
+
const storageWithOverride = storage as unknown as {
|
|
1959
|
+
withTombstoneBlockedCaptureWriteLock: (
|
|
1960
|
+
task: () => Promise<unknown>,
|
|
1961
|
+
identity?: string | readonly string[]
|
|
1962
|
+
) => Promise<unknown>;
|
|
1963
|
+
};
|
|
1964
|
+
const originalLock = storageWithOverride.withTombstoneBlockedCaptureWriteLock.bind(storage);
|
|
1965
|
+
const coordinationAttempted = Promise.withResolvers<void>();
|
|
1966
|
+
storageWithOverride.withTombstoneBlockedCaptureWriteLock = async (task, identity) => {
|
|
1967
|
+
if ((Array.isArray(identity) ? identity : [identity]).includes(queuedIdentity)) {
|
|
1968
|
+
coordinationAttempted.resolve();
|
|
1969
|
+
}
|
|
1970
|
+
return await originalLock(task, identity);
|
|
1971
|
+
};
|
|
1972
|
+
|
|
1973
|
+
const replacementContent = "The reviewed replacement is now active.";
|
|
1974
|
+
const replacementFile = [
|
|
1975
|
+
"---",
|
|
1976
|
+
"id: queued-review-replacement",
|
|
1977
|
+
"category: fact",
|
|
1978
|
+
"created: 2026-01-01T00:00:00.000Z",
|
|
1979
|
+
"updated: 2026-01-01T00:00:00.000Z",
|
|
1980
|
+
"source: offline-sync",
|
|
1981
|
+
"confidence: 0.8",
|
|
1982
|
+
"confidenceTier: implied",
|
|
1983
|
+
"tags: []",
|
|
1984
|
+
"status: active",
|
|
1985
|
+
"---",
|
|
1986
|
+
"",
|
|
1987
|
+
replacementContent,
|
|
1988
|
+
"",
|
|
1989
|
+
].join("\n");
|
|
1990
|
+
let completed = false;
|
|
1991
|
+
let replacement: Promise<void> | undefined;
|
|
1992
|
+
try {
|
|
1993
|
+
replacement = storage.writeOfflineSyncFile(queuedMemory.path, Buffer.from(replacementFile, "utf8")).then(() => {
|
|
1994
|
+
completed = true;
|
|
1995
|
+
});
|
|
1996
|
+
const first = await Promise.race([
|
|
1997
|
+
coordinationAttempted.promise.then(() => "coordinated" as const),
|
|
1998
|
+
replacement.then(() => "completed" as const),
|
|
1999
|
+
]);
|
|
2000
|
+
assert.equal(first, "coordinated");
|
|
2001
|
+
assert.equal(completed, false, "offline sync must wait for the queued-review identity lock");
|
|
2002
|
+
release.resolve();
|
|
2003
|
+
await held;
|
|
2004
|
+
await replacement;
|
|
2005
|
+
assert.equal((await storage.getMemoryById("queued-review-replacement"))?.content, replacementContent);
|
|
2006
|
+
} finally {
|
|
2007
|
+
release.resolve();
|
|
2008
|
+
storageWithOverride.withTombstoneBlockedCaptureWriteLock = originalLock;
|
|
2009
|
+
await held.catch(() => undefined);
|
|
2010
|
+
await replacement?.catch(() => undefined);
|
|
2011
|
+
}
|
|
2012
|
+
});
|
|
2013
|
+
});
|
|
2014
|
+
|
|
1728
2015
|
test("offline sync reacquires a rewritten blocked target identity", async () => {
|
|
1729
2016
|
await withMemoryDir(async (dir) => {
|
|
1730
2017
|
const storage = new StorageManager(dir);
|
|
@@ -1776,9 +2063,7 @@ test("offline sync reacquires a rewritten blocked target identity", async () =>
|
|
|
1776
2063
|
contentA,
|
|
1777
2064
|
"",
|
|
1778
2065
|
].join("\n");
|
|
1779
|
-
const targetMemory = (await storage.readAllMemories()).find(
|
|
1780
|
-
(memory) => memory.frontmatter.id === target.id
|
|
1781
|
-
);
|
|
2066
|
+
const targetMemory = (await storage.readAllMemories()).find((memory) => memory.frontmatter.id === target.id);
|
|
1782
2067
|
assert.ok(targetMemory);
|
|
1783
2068
|
const identityA = buildExplicitCaptureDedupKey(contentA, "fact", "provider-a");
|
|
1784
2069
|
const identityB = buildExplicitCaptureDedupKey(contentB, "fact", "provider-a");
|
|
@@ -1786,7 +2071,7 @@ test("offline sync reacquires a rewritten blocked target identity", async () =>
|
|
|
1786
2071
|
const storageWithOverride = storage as unknown as {
|
|
1787
2072
|
withTombstoneBlockedCaptureWriteLock: (
|
|
1788
2073
|
task: () => Promise<unknown>,
|
|
1789
|
-
identity?: string | readonly string[]
|
|
2074
|
+
identity?: string | readonly string[]
|
|
1790
2075
|
) => Promise<unknown>;
|
|
1791
2076
|
};
|
|
1792
2077
|
const originalLock = storageWithOverride.withTombstoneBlockedCaptureWriteLock.bind(storage);
|
|
@@ -1816,7 +2101,7 @@ test("offline sync reacquires a rewritten blocked target identity", async () =>
|
|
|
1816
2101
|
await releaseMarker.promise;
|
|
1817
2102
|
}
|
|
1818
2103
|
return marker;
|
|
1819
|
-
}
|
|
2104
|
+
}
|
|
1820
2105
|
);
|
|
1821
2106
|
|
|
1822
2107
|
const sync = storage.writeOfflineSyncFile(targetMemory.path, Buffer.from(incomingFile, "utf8"));
|
|
@@ -1826,11 +2111,11 @@ test("offline sync reacquires a rewritten blocked target identity", async () =>
|
|
|
1826
2111
|
const secondKeys = capturedIdentities[1];
|
|
1827
2112
|
assert.ok(
|
|
1828
2113
|
(Array.isArray(firstKeys) ? firstKeys : [firstKeys]).includes(identityB),
|
|
1829
|
-
"sync must first observe the original target identity"
|
|
2114
|
+
"sync must first observe the original target identity"
|
|
1830
2115
|
);
|
|
1831
2116
|
assert.ok(
|
|
1832
2117
|
(Array.isArray(secondKeys) ? secondKeys : [secondKeys]).includes(identityC),
|
|
1833
|
-
"sync must reacquire the rewritten target identity"
|
|
2118
|
+
"sync must reacquire the rewritten target identity"
|
|
1834
2119
|
);
|
|
1835
2120
|
let recaptureCompleted = false;
|
|
1836
2121
|
const recapture = peer
|
|
@@ -1852,7 +2137,7 @@ test("offline sync reacquires a rewritten blocked target identity", async () =>
|
|
|
1852
2137
|
assert.equal(
|
|
1853
2138
|
after.find((memory) => memory.path === targetMemory.path)?.content,
|
|
1854
2139
|
contentA,
|
|
1855
|
-
"offline sync must replace the target after holding its current identity lock"
|
|
2140
|
+
"offline sync must replace the target after holding its current identity lock"
|
|
1856
2141
|
);
|
|
1857
2142
|
assert.equal(after.filter((memory) => memory.content === contentC).length, 1);
|
|
1858
2143
|
} finally {
|
|
@@ -2309,6 +2594,52 @@ test("chunked offline sync scans oversized frontmatter before locking its identi
|
|
|
2309
2594
|
});
|
|
2310
2595
|
});
|
|
2311
2596
|
|
|
2597
|
+
test("chunked offline sync lowercases contextual Unicode across staged read boundaries", async () => {
|
|
2598
|
+
await withMemoryDir(async (dir) => {
|
|
2599
|
+
const storage = new StorageManager(dir);
|
|
2600
|
+
await storage.ensureDirectories();
|
|
2601
|
+
const header = (id: string) =>
|
|
2602
|
+
[
|
|
2603
|
+
"---",
|
|
2604
|
+
`id: ${id}`,
|
|
2605
|
+
"category: fact",
|
|
2606
|
+
"created: 2026-01-01T00:00:00.000Z",
|
|
2607
|
+
"updated: 2026-01-01T00:00:00.000Z",
|
|
2608
|
+
"source: offline-sync",
|
|
2609
|
+
"confidence: 0.8",
|
|
2610
|
+
"confidenceTier: implied",
|
|
2611
|
+
"tags: []",
|
|
2612
|
+
"sourceConnector: provider-a",
|
|
2613
|
+
"status: pending_review",
|
|
2614
|
+
"blockedBy: tombstone-unicode-boundary",
|
|
2615
|
+
"---",
|
|
2616
|
+
].join("\n");
|
|
2617
|
+
const incomingHeader = header("unicode-boundary-incoming");
|
|
2618
|
+
const firstReadBoundary = SECURE_STORE_ENVELOPE_OVERHEAD_BYTES + 64 * 1024;
|
|
2619
|
+
const bodyOffset = Buffer.byteLength(`${incomingHeader}\n`);
|
|
2620
|
+
const prefixLength = firstReadBoundary - bodyOffset - Buffer.byteLength("\nΟΣ", "utf8");
|
|
2621
|
+
assert.ok(prefixLength > 0);
|
|
2622
|
+
const content = `${"x".repeat(prefixLength)}ΟΣΑ${"z".repeat(1_050_000)}`;
|
|
2623
|
+
assert.equal(Buffer.byteLength(`${incomingHeader}\n\n${"x".repeat(prefixLength)}ΟΣ`, "utf8"), firstReadBoundary);
|
|
2624
|
+
const memoryFile = (id: string) => `${header(id)}\n\n${content}\n`;
|
|
2625
|
+
const existingPath = path.join(dir, "facts", "2026-01-01", "unicode-boundary-existing.md");
|
|
2626
|
+
await storage.writeOfflineSyncFile(existingPath, Buffer.from(memoryFile("unicode-boundary-existing"), "utf8"));
|
|
2627
|
+
|
|
2628
|
+
const incomingPath = path.join(dir, "facts", "2026-01-01", "unicode-boundary-incoming.md");
|
|
2629
|
+
const incoming = Buffer.from(memoryFile("unicode-boundary-incoming"), "utf8");
|
|
2630
|
+
const chunks = (async function* (): AsyncIterable<Buffer> {
|
|
2631
|
+
for (let offset = 0; offset < incoming.length; offset += 32 * 1024) {
|
|
2632
|
+
yield incoming.subarray(offset, offset + 32 * 1024);
|
|
2633
|
+
}
|
|
2634
|
+
})();
|
|
2635
|
+
await storage.writeOfflineSyncFileChunks(incomingPath, chunks);
|
|
2636
|
+
|
|
2637
|
+
assert.equal(existsSync(incomingPath), false, "the normalized duplicate must not be published");
|
|
2638
|
+
const matches = (await storage.readAllMemories()).filter((memory) => memory.content === content);
|
|
2639
|
+
assert.equal(matches.length, 1);
|
|
2640
|
+
});
|
|
2641
|
+
});
|
|
2642
|
+
|
|
2312
2643
|
test("chunked offline sync encrypts oversized staging files when secure storage is enabled", async () => {
|
|
2313
2644
|
await withMemoryDir(async (dir) => {
|
|
2314
2645
|
const storage = new StorageManager(dir);
|
|
@@ -2481,9 +2812,7 @@ test("chunked offline sync acquires a target identity discovered in a nested rec
|
|
|
2481
2812
|
});
|
|
2482
2813
|
assert.ok(targetTombstone);
|
|
2483
2814
|
assert.equal(target.tombstoneBlocked, false);
|
|
2484
|
-
const targetMemory = (await storage.readAllMemories()).find(
|
|
2485
|
-
(memory) => memory.frontmatter.id === target.id
|
|
2486
|
-
);
|
|
2815
|
+
const targetMemory = (await storage.readAllMemories()).find((memory) => memory.frontmatter.id === target.id);
|
|
2487
2816
|
assert.ok(targetMemory);
|
|
2488
2817
|
const incomingFile = [
|
|
2489
2818
|
"---",
|
|
@@ -2512,13 +2841,13 @@ test("chunked offline sync acquires a target identity discovered in a nested rec
|
|
|
2512
2841
|
const storageWithOverrides = storage as unknown as {
|
|
2513
2842
|
withTombstoneBlockedCaptureWriteLock: (
|
|
2514
2843
|
task: () => Promise<unknown>,
|
|
2515
|
-
identity?: string | readonly string[]
|
|
2844
|
+
identity?: string | readonly string[]
|
|
2516
2845
|
) => Promise<unknown>;
|
|
2517
2846
|
runTombstoneBlockedOfflineSyncMutation: (
|
|
2518
2847
|
target: string,
|
|
2519
2848
|
after: MemoryFile | null,
|
|
2520
2849
|
write: () => Promise<void>,
|
|
2521
|
-
coordinate?: boolean
|
|
2850
|
+
coordinate?: boolean
|
|
2522
2851
|
) => Promise<void>;
|
|
2523
2852
|
};
|
|
2524
2853
|
const originalLock = storageWithOverrides.withTombstoneBlockedCaptureWriteLock.bind(storage);
|
|
@@ -2532,17 +2861,10 @@ test("chunked offline sync acquires a target identity discovered in a nested rec
|
|
|
2532
2861
|
let pauseNextPrepare = false;
|
|
2533
2862
|
const markerEntered = Promise.withResolvers<void>();
|
|
2534
2863
|
const releaseMarker = Promise.withResolvers<void>();
|
|
2535
|
-
storageWithOverrides.runTombstoneBlockedOfflineSyncMutation = async (
|
|
2536
|
-
syncTarget,
|
|
2537
|
-
after,
|
|
2538
|
-
write,
|
|
2539
|
-
coordinate,
|
|
2540
|
-
) => {
|
|
2864
|
+
storageWithOverrides.runTombstoneBlockedOfflineSyncMutation = async (syncTarget, after, write, coordinate) => {
|
|
2541
2865
|
if (!rewroteTarget) {
|
|
2542
2866
|
rewroteTarget = true;
|
|
2543
|
-
const current = (await peer.readAllMemories()).find(
|
|
2544
|
-
(memory) => memory.frontmatter.id === target.id
|
|
2545
|
-
);
|
|
2867
|
+
const current = (await peer.readAllMemories()).find((memory) => memory.frontmatter.id === target.id);
|
|
2546
2868
|
assert.ok(current);
|
|
2547
2869
|
await peer.writeMemoryFrontmatter(current, {
|
|
2548
2870
|
status: "pending_review",
|
|
@@ -2564,17 +2886,15 @@ test("chunked offline sync acquires a target identity discovered in a nested rec
|
|
|
2564
2886
|
await releaseMarker.promise;
|
|
2565
2887
|
}
|
|
2566
2888
|
return marker;
|
|
2567
|
-
}
|
|
2889
|
+
}
|
|
2568
2890
|
);
|
|
2569
2891
|
|
|
2570
2892
|
const sync = storage.writeOfflineSyncFileChunks(targetMemory.path, chunks);
|
|
2571
2893
|
try {
|
|
2572
2894
|
await markerEntered.promise;
|
|
2573
2895
|
assert.ok(
|
|
2574
|
-
capturedIdentities.some((identity) =>
|
|
2575
|
-
|
|
2576
|
-
),
|
|
2577
|
-
"nested chunk sync must acquire the identity discovered after the outer recheck",
|
|
2896
|
+
capturedIdentities.some((identity) => (Array.isArray(identity) ? identity : [identity]).includes(identityC)),
|
|
2897
|
+
"nested chunk sync must acquire the identity discovered after the outer recheck"
|
|
2578
2898
|
);
|
|
2579
2899
|
let recaptureCompleted = false;
|
|
2580
2900
|
const recapture = peer
|
|
@@ -2596,7 +2916,7 @@ test("chunked offline sync acquires a target identity discovered in a nested rec
|
|
|
2596
2916
|
assert.equal(
|
|
2597
2917
|
after.find((memory) => memory.path === targetMemory.path)?.content,
|
|
2598
2918
|
incomingContent,
|
|
2599
|
-
"chunk sync must replace the target only while holding its current identity lock"
|
|
2919
|
+
"chunk sync must replace the target only while holding its current identity lock"
|
|
2600
2920
|
);
|
|
2601
2921
|
assert.equal(after.filter((memory) => memory.content === targetContent).length, 1);
|
|
2602
2922
|
} finally {
|
|
@@ -2672,7 +2992,7 @@ test("large blocked offline sync streams through bounded staging and deduplicate
|
|
|
2672
2992
|
largeContent,
|
|
2673
2993
|
"",
|
|
2674
2994
|
].join("\n"),
|
|
2675
|
-
"utf8"
|
|
2995
|
+
"utf8"
|
|
2676
2996
|
);
|
|
2677
2997
|
assert.ok(incomingFile.byteLength > 1_048_576);
|
|
2678
2998
|
const target = path.join(dir, "cold", "large-stream-incoming.md");
|
|
@@ -2689,10 +3009,9 @@ test("large blocked offline sync streams through bounded staging and deduplicate
|
|
|
2689
3009
|
|
|
2690
3010
|
assert.equal(consumedBytes, incomingFile.byteLength);
|
|
2691
3011
|
assert.equal(existsSync(target), false, "a streamed blocked duplicate must not publish a new row");
|
|
2692
|
-
const matches = [
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
].filter((memory) => memory.frontmatter.id === existing.id);
|
|
3012
|
+
const matches = [...(await storage.readAllMemories()), ...(await storage.readAllColdMemories())].filter(
|
|
3013
|
+
(memory) => memory.frontmatter.id === existing.id
|
|
3014
|
+
);
|
|
2696
3015
|
assert.equal(matches.length, 1, "the existing blocked row remains the sole durable duplicate");
|
|
2697
3016
|
});
|
|
2698
3017
|
});
|
|
@@ -2827,10 +3146,7 @@ test("frontmatter rewrites invalidate cold cache before blocked index sync", asy
|
|
|
2827
3146
|
);
|
|
2828
3147
|
assert.ok(coldMemory, "migrated blocked memory must be readable from cold tier");
|
|
2829
3148
|
|
|
2830
|
-
assert.equal(
|
|
2831
|
-
await storage.writeMemoryFrontmatter(coldMemory, { sourceConnector: "provider-b" }),
|
|
2832
|
-
true
|
|
2833
|
-
);
|
|
3149
|
+
assert.equal(await storage.writeMemoryFrontmatter(coldMemory, { sourceConnector: "provider-b" }), true);
|
|
2834
3150
|
assert.equal(
|
|
2835
3151
|
await storage.hasTombstoneBlockedExplicitCapture(content, "fact", "provider-a"),
|
|
2836
3152
|
false,
|
|
@@ -3207,7 +3523,7 @@ test("blocked invalidation reserves the index marker before deleting under the i
|
|
|
3207
3523
|
const storageWithOverride = storage as unknown as {
|
|
3208
3524
|
withTombstoneBlockedCaptureWriteLock: (
|
|
3209
3525
|
task: () => Promise<boolean>,
|
|
3210
|
-
identity?: string | readonly string[]
|
|
3526
|
+
identity?: string | readonly string[]
|
|
3211
3527
|
) => Promise<boolean>;
|
|
3212
3528
|
};
|
|
3213
3529
|
let capturedIdentity: string | readonly string[] | undefined;
|
|
@@ -3229,7 +3545,7 @@ test("blocked invalidation reserves the index marker before deleting under the i
|
|
|
3229
3545
|
await release.promise;
|
|
3230
3546
|
}
|
|
3231
3547
|
return marker;
|
|
3232
|
-
}
|
|
3548
|
+
}
|
|
3233
3549
|
);
|
|
3234
3550
|
|
|
3235
3551
|
const invalidation = storage.invalidateMemory(first.id);
|
|
@@ -3238,15 +3554,12 @@ test("blocked invalidation reserves the index marker before deleting under the i
|
|
|
3238
3554
|
entered.promise.then(() => true),
|
|
3239
3555
|
new Promise<boolean>((resolve) => setTimeout(() => resolve(false), 100)),
|
|
3240
3556
|
]);
|
|
3241
|
-
assert.equal(
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
"blocked invalidation must reserve its rebuild marker before unlinking",
|
|
3245
|
-
);
|
|
3246
|
-
assert.equal(capturedIdentity, identity);
|
|
3557
|
+
assert.equal(enteredBeforeTimeout, true, "blocked invalidation must reserve its rebuild marker before unlinking");
|
|
3558
|
+
assert.ok(Array.isArray(capturedIdentity) && capturedIdentity.includes(identity));
|
|
3559
|
+
assert.ok(Array.isArray(capturedIdentity) && capturedIdentity.some((key) => key.startsWith("path ")));
|
|
3247
3560
|
assert.ok(
|
|
3248
3561
|
(await readdir(path.join(dir, "state", "tombstone-blocked-capture", "rebuild-required"))).length > 0,
|
|
3249
|
-
"blocked invalidation must publish its rebuild marker before unlinking"
|
|
3562
|
+
"blocked invalidation must publish its rebuild marker before unlinking"
|
|
3250
3563
|
);
|
|
3251
3564
|
|
|
3252
3565
|
let peerCompleted = false;
|
|
@@ -3266,10 +3579,7 @@ test("blocked invalidation reserves the index marker before deleting under the i
|
|
|
3266
3579
|
const second = await recapture;
|
|
3267
3580
|
assert.equal(second.tombstoneBlocked, true);
|
|
3268
3581
|
assert.notEqual(second.id, first.id);
|
|
3269
|
-
assert.equal(
|
|
3270
|
-
(await peer.readAllMemories()).filter((memory) => memory.content === content).length,
|
|
3271
|
-
1,
|
|
3272
|
-
);
|
|
3582
|
+
assert.equal((await peer.readAllMemories()).filter((memory) => memory.content === content).length, 1);
|
|
3273
3583
|
} finally {
|
|
3274
3584
|
release.resolve();
|
|
3275
3585
|
prepareSpy.mock.restore();
|
|
@@ -3313,7 +3623,7 @@ test("blocked archive serializes capture scans with the archive transition", asy
|
|
|
3313
3623
|
const storageWithOverride = storage as unknown as {
|
|
3314
3624
|
withTombstoneBlockedCaptureWriteLock: (
|
|
3315
3625
|
task: () => Promise<unknown>,
|
|
3316
|
-
identity?: string | readonly string[]
|
|
3626
|
+
identity?: string | readonly string[]
|
|
3317
3627
|
) => Promise<unknown>;
|
|
3318
3628
|
};
|
|
3319
3629
|
const originalLock = storageWithOverride.withTombstoneBlockedCaptureWriteLock.bind(storage);
|
|
@@ -3338,7 +3648,7 @@ test("blocked archive serializes capture scans with the archive transition", asy
|
|
|
3338
3648
|
await releaseMarker.promise;
|
|
3339
3649
|
}
|
|
3340
3650
|
return marker;
|
|
3341
|
-
}
|
|
3651
|
+
}
|
|
3342
3652
|
);
|
|
3343
3653
|
|
|
3344
3654
|
const archive = storage.archiveMemory(firstMemory);
|
|
@@ -3348,7 +3658,8 @@ test("blocked archive serializes capture scans with the archive transition", asy
|
|
|
3348
3658
|
new Promise<boolean>((resolve) => setTimeout(() => resolve(false), 100)),
|
|
3349
3659
|
]);
|
|
3350
3660
|
assert.equal(enteredBeforeTimeout, true, "archive must reserve its marker under the capture lock");
|
|
3351
|
-
assert.
|
|
3661
|
+
assert.ok(Array.isArray(capturedIdentity) && capturedIdentity.includes(identity));
|
|
3662
|
+
assert.ok(Array.isArray(capturedIdentity) && capturedIdentity.some((key) => key.startsWith("path ")));
|
|
3352
3663
|
|
|
3353
3664
|
let recaptureCompleted = false;
|
|
3354
3665
|
const recapture = peer
|
|
@@ -3372,12 +3683,12 @@ test("blocked archive serializes capture scans with the archive transition", asy
|
|
|
3372
3683
|
assert.equal(
|
|
3373
3684
|
(await peer.readAllMemories()).filter((memory) => memory.content === content).length,
|
|
3374
3685
|
1,
|
|
3375
|
-
"recapture must recreate the review row after archive removes the old row"
|
|
3686
|
+
"recapture must recreate the review row after archive removes the old row"
|
|
3376
3687
|
);
|
|
3377
3688
|
assert.equal(
|
|
3378
3689
|
await peer.hasTombstoneBlockedExplicitCapture(content, "fact", "provider-a"),
|
|
3379
3690
|
true,
|
|
3380
|
-
"the recreated review row must remain indexed"
|
|
3691
|
+
"the recreated review row must remain indexed"
|
|
3381
3692
|
);
|
|
3382
3693
|
} finally {
|
|
3383
3694
|
releaseMarker.resolve();
|
|
@@ -3396,7 +3707,7 @@ test("blocked archive failures discard their uncommitted rebuild marker", async
|
|
|
3396
3707
|
protected override writeStorageSecureFile(
|
|
3397
3708
|
filePath: string,
|
|
3398
3709
|
content: string | Buffer,
|
|
3399
|
-
forceEncrypt = false
|
|
3710
|
+
forceEncrypt = false
|
|
3400
3711
|
): Promise<void> {
|
|
3401
3712
|
if (this.failArchiveWrites && filePath.includes(`${path.sep}archive${path.sep}`)) {
|
|
3402
3713
|
return Promise.reject(new Error("simulated archive write failure"));
|
|
@@ -3476,7 +3787,7 @@ test("blocked invalidation reacquires the reloaded identity lock", async () => {
|
|
|
3476
3787
|
const storageWithOverride = storage as unknown as {
|
|
3477
3788
|
withTombstoneBlockedCaptureWriteLock: (
|
|
3478
3789
|
task: () => Promise<unknown>,
|
|
3479
|
-
identity?: string | readonly string[]
|
|
3790
|
+
identity?: string | readonly string[]
|
|
3480
3791
|
) => Promise<unknown>;
|
|
3481
3792
|
};
|
|
3482
3793
|
const originalLock = storageWithOverride.withTombstoneBlockedCaptureWriteLock.bind(storage);
|
|
@@ -3506,13 +3817,23 @@ test("blocked invalidation reacquires the reloaded identity lock", async () => {
|
|
|
3506
3817
|
await releaseMarker.promise;
|
|
3507
3818
|
}
|
|
3508
3819
|
return marker;
|
|
3509
|
-
}
|
|
3820
|
+
}
|
|
3510
3821
|
);
|
|
3511
3822
|
|
|
3512
3823
|
const invalidation = storage.invalidateMemory(first.id);
|
|
3513
3824
|
try {
|
|
3514
3825
|
await markerEntered.promise;
|
|
3515
|
-
assert.deepEqual(
|
|
3826
|
+
assert.deepEqual(
|
|
3827
|
+
capturedIdentities.map((identity) =>
|
|
3828
|
+
(Array.isArray(identity) ? identity : [identity]).filter((key) => key?.startsWith("category "))
|
|
3829
|
+
),
|
|
3830
|
+
[[identityA], [identityA, identityB]]
|
|
3831
|
+
);
|
|
3832
|
+
assert.ok(
|
|
3833
|
+
capturedIdentities.every((identity) =>
|
|
3834
|
+
(Array.isArray(identity) ? identity : [identity]).some((key) => key?.startsWith("path "))
|
|
3835
|
+
)
|
|
3836
|
+
);
|
|
3516
3837
|
let recaptureCompleted = false;
|
|
3517
3838
|
const recapture = peer
|
|
3518
3839
|
.writeMemory("fact", contentB, {
|
|
@@ -3530,10 +3851,7 @@ test("blocked invalidation reacquires the reloaded identity lock", async () => {
|
|
|
3530
3851
|
const second = await recapture;
|
|
3531
3852
|
assert.equal(second.tombstoneBlocked, true);
|
|
3532
3853
|
assert.notEqual(second.id, first.id);
|
|
3533
|
-
assert.equal(
|
|
3534
|
-
(await peer.readAllMemories()).filter((memory) => memory.content === contentB).length,
|
|
3535
|
-
1,
|
|
3536
|
-
);
|
|
3854
|
+
assert.equal((await peer.readAllMemories()).filter((memory) => memory.content === contentB).length, 1);
|
|
3537
3855
|
} finally {
|
|
3538
3856
|
releaseMarker.resolve();
|
|
3539
3857
|
prepareSpy.mock.restore();
|
|
@@ -3570,7 +3888,7 @@ test("blocked rewrites reacquire a target identity changed before update", async
|
|
|
3570
3888
|
createdBy: "user_correction",
|
|
3571
3889
|
sourceMemoryId,
|
|
3572
3890
|
rawContent,
|
|
3573
|
-
})
|
|
3891
|
+
})
|
|
3574
3892
|
);
|
|
3575
3893
|
}
|
|
3576
3894
|
const target = await storage.writeMemory("fact", contentA, {
|
|
@@ -3578,9 +3896,7 @@ test("blocked rewrites reacquire a target identity changed before update", async
|
|
|
3578
3896
|
sourceConnector: "provider-a",
|
|
3579
3897
|
});
|
|
3580
3898
|
assert.equal(target.tombstoneBlocked, true);
|
|
3581
|
-
const targetMemory = (await storage.readAllMemories()).find(
|
|
3582
|
-
(memory) => memory.frontmatter.id === target.id
|
|
3583
|
-
);
|
|
3899
|
+
const targetMemory = (await storage.readAllMemories()).find((memory) => memory.frontmatter.id === target.id);
|
|
3584
3900
|
assert.ok(targetMemory);
|
|
3585
3901
|
const identityA = buildExplicitCaptureDedupKey(contentA, "fact", "provider-a");
|
|
3586
3902
|
const identityB = buildExplicitCaptureDedupKey(contentB, "fact", "provider-a");
|
|
@@ -3588,7 +3904,7 @@ test("blocked rewrites reacquire a target identity changed before update", async
|
|
|
3588
3904
|
const storageWithOverride = storage as unknown as {
|
|
3589
3905
|
withTombstoneBlockedCaptureWriteLock: (
|
|
3590
3906
|
task: () => Promise<unknown>,
|
|
3591
|
-
identity?: string | readonly string[]
|
|
3907
|
+
identity?: string | readonly string[]
|
|
3592
3908
|
) => Promise<unknown>;
|
|
3593
3909
|
};
|
|
3594
3910
|
const originalLock = storageWithOverride.withTombstoneBlockedCaptureWriteLock.bind(storage);
|
|
@@ -3618,7 +3934,7 @@ test("blocked rewrites reacquire a target identity changed before update", async
|
|
|
3618
3934
|
await releaseMarker.promise;
|
|
3619
3935
|
}
|
|
3620
3936
|
return marker;
|
|
3621
|
-
}
|
|
3937
|
+
}
|
|
3622
3938
|
);
|
|
3623
3939
|
|
|
3624
3940
|
const update = storage.updateMemory(target.id, contentC, { sourceConnector: "provider-a" });
|
|
@@ -3629,11 +3945,11 @@ test("blocked rewrites reacquire a target identity changed before update", async
|
|
|
3629
3945
|
const secondKeys = capturedIdentities[1];
|
|
3630
3946
|
assert.ok(
|
|
3631
3947
|
(Array.isArray(firstKeys) ? firstKeys : [firstKeys]).includes(identityA),
|
|
3632
|
-
"update must first lock the identity read before the peer rewrite"
|
|
3948
|
+
"update must first lock the identity read before the peer rewrite"
|
|
3633
3949
|
);
|
|
3634
3950
|
assert.ok(
|
|
3635
3951
|
(Array.isArray(secondKeys) ? secondKeys : [secondKeys]).includes(identityB),
|
|
3636
|
-
"update must reacquire the rewritten target identity"
|
|
3952
|
+
"update must reacquire the rewritten target identity"
|
|
3637
3953
|
);
|
|
3638
3954
|
let recaptureCompleted = false;
|
|
3639
3955
|
const recapture = peer
|
|
@@ -3655,7 +3971,7 @@ test("blocked rewrites reacquire a target identity changed before update", async
|
|
|
3655
3971
|
assert.equal(
|
|
3656
3972
|
after.find((memory) => memory.path === targetMemory.path)?.content,
|
|
3657
3973
|
contentC,
|
|
3658
|
-
"update must write only after holding the current target identity lock"
|
|
3974
|
+
"update must write only after holding the current target identity lock"
|
|
3659
3975
|
);
|
|
3660
3976
|
assert.equal(after.filter((memory) => memory.content === contentB).length, 1);
|
|
3661
3977
|
} finally {
|
|
@@ -3914,130 +4230,97 @@ test("queued-review dispositions coordinate with explicit capture dedupe", async
|
|
|
3914
4230
|
});
|
|
3915
4231
|
});
|
|
3916
4232
|
|
|
3917
|
-
test("active rewrites
|
|
4233
|
+
test("active rewrites serialize the target path across their read and write", async () => {
|
|
3918
4234
|
await withMemoryDir(async (dir) => {
|
|
3919
4235
|
const storage = new StorageManager(dir);
|
|
3920
4236
|
const peer = new StorageManager(dir);
|
|
3921
4237
|
await storage.ensureDirectories();
|
|
3922
4238
|
await peer.ensureDirectories();
|
|
3923
|
-
const
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
semanticThreshold: 0.9,
|
|
3927
|
-
namespace: "default",
|
|
3928
|
-
};
|
|
3929
|
-
storage.setTombstonesConfig(tombstoneConfig);
|
|
3930
|
-
peer.setTombstonesConfig(tombstoneConfig);
|
|
3931
|
-
const contentA = "An active target becomes blocked before an update writes.";
|
|
3932
|
-
const contentB = "The rewritten blocked target identity must be locked.";
|
|
3933
|
-
const contentC = "The stale update must publish only after locking identity B.";
|
|
3934
|
-
const tombstoneId = await peer.appendTombstone({
|
|
3935
|
-
reason: "correction",
|
|
3936
|
-
createdBy: "user_correction",
|
|
3937
|
-
sourceMemoryId: "active-rewrite-recheck",
|
|
3938
|
-
rawContent: contentB,
|
|
3939
|
-
});
|
|
3940
|
-
assert.ok(tombstoneId);
|
|
4239
|
+
const contentA = "An active target is read before a concurrent update.";
|
|
4240
|
+
const contentB = "The concurrent update must run after the in-flight write.";
|
|
4241
|
+
const contentC = "The in-flight update holds the path through persistence.";
|
|
3941
4242
|
const target = await storage.writeMemory("fact", contentA, {
|
|
3942
4243
|
source: "test",
|
|
3943
4244
|
sourceConnector: "provider-a",
|
|
3944
4245
|
});
|
|
3945
4246
|
const targetMemory = await storage.getMemoryById(target.id);
|
|
3946
4247
|
assert.ok(targetMemory);
|
|
3947
|
-
assert.equal(targetMemory.frontmatter.status, "active");
|
|
3948
|
-
const identityB = buildExplicitCaptureDedupKey(contentB, "fact", "provider-a");
|
|
3949
|
-
await storage.hasTombstoneBlockedExplicitCapture(contentB, "fact", "provider-a");
|
|
3950
4248
|
|
|
4249
|
+
const readPaused = Promise.withResolvers<void>();
|
|
4250
|
+
const releaseRead = Promise.withResolvers<void>();
|
|
3951
4251
|
const storageWithOverrides = storage as unknown as {
|
|
3952
4252
|
readMemoryByPath: (filePath: string) => Promise<MemoryFile | null>;
|
|
3953
4253
|
withTombstoneBlockedCaptureWriteLock: (
|
|
3954
4254
|
task: () => Promise<unknown>,
|
|
3955
|
-
identity?: string | readonly string[]
|
|
4255
|
+
identity?: string | readonly string[]
|
|
3956
4256
|
) => Promise<unknown>;
|
|
3957
4257
|
};
|
|
3958
4258
|
const originalRead = storageWithOverrides.readMemoryByPath.bind(storage);
|
|
3959
|
-
let
|
|
4259
|
+
let pauseRead = true;
|
|
3960
4260
|
storageWithOverrides.readMemoryByPath = async (filePath) => {
|
|
3961
4261
|
const current = await originalRead(filePath);
|
|
3962
|
-
if (
|
|
3963
|
-
|
|
3964
|
-
|
|
3965
|
-
|
|
3966
|
-
const rewritten = await peer.getMemoryById(target.id);
|
|
3967
|
-
assert.ok(rewritten);
|
|
3968
|
-
assert.equal(
|
|
3969
|
-
await peer.writeMemoryFrontmatter(rewritten, {
|
|
3970
|
-
status: "pending_review",
|
|
3971
|
-
blockedBy: tombstoneId,
|
|
3972
|
-
}),
|
|
3973
|
-
true,
|
|
3974
|
-
);
|
|
3975
|
-
pauseNextPrepare = true;
|
|
3976
|
-
return await originalRead(filePath);
|
|
4262
|
+
if (pauseRead && filePath === targetMemory.path) {
|
|
4263
|
+
pauseRead = false;
|
|
4264
|
+
readPaused.resolve();
|
|
4265
|
+
await releaseRead.promise;
|
|
3977
4266
|
}
|
|
3978
4267
|
return current;
|
|
3979
4268
|
};
|
|
3980
|
-
const
|
|
3981
|
-
const
|
|
4269
|
+
const originalStorageLock = storageWithOverrides.withTombstoneBlockedCaptureWriteLock.bind(storage);
|
|
4270
|
+
const storageIdentities: Array<string | readonly string[] | undefined> = [];
|
|
3982
4271
|
storageWithOverrides.withTombstoneBlockedCaptureWriteLock = async (task, identity) => {
|
|
3983
|
-
|
|
3984
|
-
return await
|
|
4272
|
+
storageIdentities.push(identity);
|
|
4273
|
+
return await originalStorageLock(task, identity);
|
|
4274
|
+
};
|
|
4275
|
+
|
|
4276
|
+
const peerWithOverride = peer as unknown as {
|
|
4277
|
+
withTombstoneBlockedCaptureWriteLock: (
|
|
4278
|
+
task: () => Promise<unknown>,
|
|
4279
|
+
identity?: string | readonly string[]
|
|
4280
|
+
) => Promise<unknown>;
|
|
4281
|
+
};
|
|
4282
|
+
const originalPeerLock = peerWithOverride.withTombstoneBlockedCaptureWriteLock.bind(peer);
|
|
4283
|
+
const peerLockRequested = Promise.withResolvers<void>();
|
|
4284
|
+
const peerIdentities: Array<string | readonly string[] | undefined> = [];
|
|
4285
|
+
peerWithOverride.withTombstoneBlockedCaptureWriteLock = async (task, identity) => {
|
|
4286
|
+
peerIdentities.push(identity);
|
|
4287
|
+
peerLockRequested.resolve();
|
|
4288
|
+
return await originalPeerLock(task, identity);
|
|
3985
4289
|
};
|
|
3986
|
-
const releaseMarker = Promise.withResolvers<void>();
|
|
3987
|
-
const markerEntered = Promise.withResolvers<void>();
|
|
3988
|
-
let pauseNextPrepare = false;
|
|
3989
|
-
const originalPrepare = TombstoneBlockedCaptureIndex.prototype.prepareWrite;
|
|
3990
|
-
const prepareSpy = mock.method(
|
|
3991
|
-
TombstoneBlockedCaptureIndex.prototype,
|
|
3992
|
-
"prepareWrite",
|
|
3993
|
-
async function (this: TombstoneBlockedCaptureIndex) {
|
|
3994
|
-
const marker = await originalPrepare.call(this);
|
|
3995
|
-
if (pauseNextPrepare) {
|
|
3996
|
-
pauseNextPrepare = false;
|
|
3997
|
-
markerEntered.resolve();
|
|
3998
|
-
await releaseMarker.promise;
|
|
3999
|
-
}
|
|
4000
|
-
return marker;
|
|
4001
|
-
}
|
|
4002
|
-
);
|
|
4003
4290
|
|
|
4004
4291
|
const update = storage.updateMemory(target.id, contentC, { sourceConnector: "provider-a" });
|
|
4292
|
+
let competingUpdate: Promise<boolean> | undefined;
|
|
4293
|
+
let competingFinished = false;
|
|
4005
4294
|
try {
|
|
4006
|
-
await
|
|
4295
|
+
await readPaused.promise;
|
|
4296
|
+
competingUpdate = peer.updateMemory(target.id, contentB, { sourceConnector: "provider-a" }).then((updated) => {
|
|
4297
|
+
competingFinished = true;
|
|
4298
|
+
return updated;
|
|
4299
|
+
});
|
|
4300
|
+
await peerLockRequested.promise;
|
|
4301
|
+
const contentionWindow = Promise.withResolvers<void>();
|
|
4302
|
+
setTimeout(contentionWindow.resolve, 50);
|
|
4303
|
+
await contentionWindow.promise;
|
|
4304
|
+
const pathIdentity = buildCapturePathLockIdentity(targetMemory.path);
|
|
4007
4305
|
assert.ok(
|
|
4008
|
-
|
|
4009
|
-
(Array.isArray(identity) ? identity : [identity]).includes(identityB)
|
|
4010
|
-
),
|
|
4011
|
-
"active rewrite must reacquire the current blocked target identity",
|
|
4306
|
+
storageIdentities.some((identity) => (Array.isArray(identity) ? identity : [identity]).includes(pathIdentity))
|
|
4012
4307
|
);
|
|
4013
|
-
|
|
4014
|
-
|
|
4015
|
-
.writeMemory("fact", contentB, {
|
|
4016
|
-
source: "test",
|
|
4017
|
-
sourceConnector: "provider-a",
|
|
4018
|
-
})
|
|
4019
|
-
.then((result) => {
|
|
4020
|
-
recaptureCompleted = true;
|
|
4021
|
-
return result;
|
|
4022
|
-
});
|
|
4023
|
-
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
4024
|
-
assert.equal(recaptureCompleted, false, "recapture must wait for the current target identity lock");
|
|
4025
|
-
releaseMarker.resolve();
|
|
4026
|
-
assert.equal(await update, true);
|
|
4027
|
-
assert.equal((await recapture).tombstoneBlocked, true);
|
|
4028
|
-
const after = await peer.readAllMemories();
|
|
4029
|
-
assert.equal(
|
|
4030
|
-
after.find((memory) => memory.path === targetMemory.path)?.content,
|
|
4031
|
-
contentC,
|
|
4032
|
-
"the stale update must write the requested content",
|
|
4308
|
+
assert.ok(
|
|
4309
|
+
peerIdentities.some((identity) => (Array.isArray(identity) ? identity : [identity]).includes(pathIdentity))
|
|
4033
4310
|
);
|
|
4034
|
-
assert.equal(
|
|
4311
|
+
assert.equal(competingFinished, false, "the peer must wait while the first writer holds the target path");
|
|
4312
|
+
|
|
4313
|
+
releaseRead.resolve();
|
|
4314
|
+
assert.equal(await update, true);
|
|
4315
|
+
assert.equal(await competingUpdate, true);
|
|
4316
|
+
assert.equal((await peer.getMemoryById(target.id))?.content, contentB);
|
|
4035
4317
|
} finally {
|
|
4036
|
-
|
|
4037
|
-
prepareSpy.mock.restore();
|
|
4318
|
+
releaseRead.resolve();
|
|
4038
4319
|
storageWithOverrides.readMemoryByPath = originalRead;
|
|
4039
|
-
storageWithOverrides.withTombstoneBlockedCaptureWriteLock =
|
|
4320
|
+
storageWithOverrides.withTombstoneBlockedCaptureWriteLock = originalStorageLock;
|
|
4321
|
+
peerWithOverride.withTombstoneBlockedCaptureWriteLock = originalPeerLock;
|
|
4040
4322
|
await update.catch(() => false);
|
|
4323
|
+
await competingUpdate?.catch(() => false);
|
|
4041
4324
|
}
|
|
4042
4325
|
});
|
|
4043
4326
|
});
|