@remnic/core 10.0.84 → 10.0.86
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/access-admin-ops-surface.js +90 -88
- package/dist/access-authorization-probe.js +91 -89
- package/dist/access-boundary.d.ts.map +1 -1
- package/dist/access-boundary.js +90 -88
- package/dist/access-cli.js +198 -194
- package/dist/access-cli.js.map +1 -1
- package/dist/access-errors.d.ts +14 -0
- package/dist/access-errors.d.ts.map +1 -1
- package/dist/access-errors.js +5 -1
- package/dist/access-extraction-force-flush.js +90 -88
- package/dist/access-http-briefing.js +91 -89
- package/dist/access-http-query.js +92 -90
- package/dist/access-http.d.ts +12 -0
- package/dist/access-http.d.ts.map +1 -1
- package/dist/access-http.js +119 -116
- package/dist/access-identity-continuity-surface.js +90 -88
- package/dist/access-lcm-surface.js +90 -88
- package/dist/access-mcp-frames.d.ts +29 -0
- package/dist/access-mcp-frames.d.ts.map +1 -0
- package/dist/access-mcp-frames.js +10 -0
- package/dist/access-mcp.d.ts +1 -1
- package/dist/access-mcp.d.ts.map +1 -1
- package/dist/access-mcp.js +105 -102
- package/dist/access-memory-search-fanout.js +2 -2
- package/dist/access-namespace-preflight.js +90 -88
- package/dist/access-observe-write-category.js +15 -14
- package/dist/access-observe-write-surface.d.ts.map +1 -1
- package/dist/access-observe-write-surface.js +90 -88
- package/dist/access-offline-manifest.js +10 -10
- package/dist/access-operations-batch.js +94 -92
- package/dist/access-operations.js +101 -99
- package/dist/access-recall-concurrency.js +90 -88
- package/dist/access-recall-diagnostics.js +2 -2
- package/dist/access-recall-response.js +90 -88
- package/dist/access-recall-surface.js +90 -88
- package/dist/access-schema.d.ts.map +1 -1
- package/dist/access-schema.js +10 -9
- package/dist/access-service.d.ts +3 -3
- package/dist/access-service.d.ts.map +1 -1
- package/dist/access-service.js +90 -88
- package/dist/access-surface-catalog.js +92 -90
- package/dist/access-surface-catalog.js.map +1 -1
- package/dist/access-token-capabilities.js +2 -2
- package/dist/active-recall.js +38 -38
- package/dist/adapters/index.js +7 -7
- package/dist/adapters/registry.js +3 -3
- package/dist/{auto-sync-C7E6G7Y2.js → auto-sync-3AZW7MXI.js} +6 -6
- package/dist/briefing-deadline.d.ts +2 -2
- package/dist/briefing-deadline.d.ts.map +1 -1
- package/dist/briefing-deadline.js +1 -1
- package/dist/briefing.js +25 -25
- package/dist/buffer-admission.d.ts +22 -0
- package/dist/buffer-admission.d.ts.map +1 -0
- package/dist/buffer-admission.js +13 -0
- package/dist/buffer-eviction.d.ts +4 -5
- package/dist/buffer-eviction.d.ts.map +1 -1
- package/dist/buffer-eviction.js +1 -1
- package/dist/buffer-persistence.d.ts +8 -4
- package/dist/buffer-persistence.d.ts.map +1 -1
- package/dist/buffer-persistence.js +1 -1
- package/dist/buffer-turn-helpers.d.ts +12 -0
- package/dist/buffer-turn-helpers.d.ts.map +1 -1
- package/dist/buffer-turn-helpers.js +9 -1
- package/dist/buffer-types.d.ts +24 -1
- package/dist/buffer-types.d.ts.map +1 -1
- package/dist/buffer.d.ts +21 -19
- package/dist/buffer.d.ts.map +1 -1
- package/dist/buffer.js +17 -13
- package/dist/calibration.js +3 -3
- package/dist/{capsule-crypto-YO5QJ6L3.js → capsule-crypto-7FJQINUR.js} +2 -2
- package/dist/capsule-gunzip-KGBDGF7P.js +11 -0
- package/dist/capsule-gunzip-KGBDGF7P.js.map +1 -0
- package/dist/capsule-import-gate.d.ts +11 -0
- package/dist/capsule-import-gate.d.ts.map +1 -0
- package/dist/capsule-import-gate.js +12 -0
- package/dist/capsule-import-gate.js.map +1 -0
- package/dist/{capsule-merge-XVTQJJ6R.js → capsule-merge-2IIXFXN5.js} +5 -11
- package/dist/capsule-merge-2IIXFXN5.js.map +1 -0
- package/dist/causal-behavior.js +2 -2
- package/dist/causal-chain.js +2 -2
- package/dist/causal-consolidation.js +27 -27
- package/dist/causal-retrieval.js +2 -2
- package/dist/causal-trajectory-graph.js +2 -2
- package/dist/causal-trajectory.js +1 -1
- package/dist/{chunk-MSIRVXQ3.js → chunk-2G4TV2ML.js} +12 -12
- package/dist/{chunk-B74NPUHT.js → chunk-2JG2NI7C.js} +54 -54
- package/dist/chunk-2JG2NI7C.js.map +1 -0
- package/dist/{chunk-IR4C3DP2.js → chunk-2K3AUDH7.js} +2 -2
- package/dist/{chunk-2IW5PT7U.js → chunk-2N4OUSIA.js} +2 -2
- package/dist/{chunk-BXLOS5AJ.js → chunk-2NLLXCJG.js} +2 -2
- package/dist/{chunk-QZSIDSB2.js → chunk-2NUDHVYM.js} +2 -2
- package/dist/{chunk-HPFVZPRH.js → chunk-2U4LMSEX.js} +3 -3
- package/dist/{chunk-WHQXWFH7.js → chunk-2XOFVTTT.js} +2 -2
- package/dist/{chunk-I6ORDT54.js → chunk-32CA7B6L.js} +2 -2
- package/dist/{chunk-T77AX6DG.js → chunk-32YX7EJ3.js} +4 -4
- package/dist/{chunk-HLKUZW42.js → chunk-3CTKEXXF.js} +144 -156
- package/dist/chunk-3CTKEXXF.js.map +1 -0
- package/dist/{chunk-HLWXCDUQ.js → chunk-3DAWHP5K.js} +5 -5
- package/dist/{chunk-ZJJFUEPO.js → chunk-3PJEDHLD.js} +2 -2
- package/dist/{chunk-PJYK7HH2.js → chunk-3UZ2VMKD.js} +2 -2
- package/dist/{chunk-IN4OFAHX.js → chunk-3Y5X6YFH.js} +2 -2
- package/dist/{chunk-YUTT4ZTI.js → chunk-45VUHDV2.js} +2 -2
- package/dist/{chunk-T6T2RMWW.js → chunk-4GMI5HFH.js} +12 -12
- package/dist/{chunk-YOEYL6CH.js → chunk-4LCVA2LW.js} +3 -3
- package/dist/{chunk-66JOLMWS.js → chunk-4X7HQKAG.js} +2 -2
- package/dist/{chunk-65SKM4BF.js → chunk-4XYAWK5H.js} +2 -2
- package/dist/{chunk-H7BZBSP6.js → chunk-52II7PB6.js} +4 -4
- package/dist/{chunk-TPX7XXY4.js → chunk-5SOSQTVE.js} +2 -2
- package/dist/{chunk-G3T6YMBO.js → chunk-64S6X4IU.js} +7 -7
- package/dist/{chunk-BK7AJUWB.js → chunk-6BCWFCDG.js} +7 -7
- package/dist/{chunk-V27FK6D6.js → chunk-6XTUZKCZ.js} +2 -2
- package/dist/{chunk-KASJJGRG.js → chunk-6ZELOK6O.js} +3 -8
- package/dist/chunk-6ZELOK6O.js.map +1 -0
- package/dist/{chunk-LECSS7EE.js → chunk-73UZZFJC.js} +3 -3
- package/dist/{chunk-4EACCIPO.js → chunk-77PDJ4OE.js} +3 -3
- package/dist/{chunk-GQZSGXR6.js → chunk-7GSL4LBG.js} +2 -2
- package/dist/{chunk-B6SPPYGJ.js → chunk-7IWRLYFJ.js} +2 -2
- package/dist/{chunk-R45L4TV6.js → chunk-7LCGFUMB.js} +6 -6
- package/dist/{chunk-A7BM2JTH.js → chunk-AJHNXIQU.js} +2 -2
- package/dist/{chunk-2D7BTSDW.js → chunk-ANZZLRUX.js} +2 -2
- package/dist/{chunk-ZXF3O7P4.js → chunk-ARRTLPRC.js} +2 -2
- package/dist/{chunk-S5CQI2KF.js → chunk-B3GKQ6DU.js} +2 -2
- package/dist/{chunk-XRBHIR7Q.js → chunk-B56LORLY.js} +2 -2
- package/dist/{chunk-KO3YBHOV.js → chunk-BIVKPQ6Q.js} +3 -3
- package/dist/{chunk-QZBTIYKD.js → chunk-BLSHFIR6.js} +7 -6
- package/dist/chunk-BLSHFIR6.js.map +1 -0
- package/dist/{chunk-54FDG7K7.js → chunk-C2HORX76.js} +3 -3
- package/dist/{chunk-WAD32CZG.js → chunk-CC4V7HNP.js} +43 -30
- package/dist/chunk-CC4V7HNP.js.map +1 -0
- package/dist/{chunk-LGEH473S.js → chunk-CJCTKL6Y.js} +9 -1
- package/dist/chunk-CJCTKL6Y.js.map +1 -0
- package/dist/{chunk-E33UJHY3.js → chunk-CN7Q3T2J.js} +4 -4
- package/dist/{chunk-YUTG35M4.js → chunk-CYCEROPR.js} +3 -3
- package/dist/{chunk-TFLPDNQV.js → chunk-D5EV6Y2L.js} +3 -3
- package/dist/{chunk-BFBS7OFQ.js → chunk-DC55RIEZ.js} +4 -4
- package/dist/{chunk-B4ZU34JQ.js → chunk-DT7SNJH6.js} +3 -3
- package/dist/{chunk-VLMBSGQF.js → chunk-DZ7DPDVW.js} +10 -8
- package/dist/chunk-DZ7DPDVW.js.map +1 -0
- package/dist/{chunk-FVRTCYZ3.js → chunk-EA2LA5RI.js} +2 -2
- package/dist/{chunk-WBN66HPI.js → chunk-ELHJK7LM.js} +6 -6
- package/dist/{chunk-77R7BQZ2.js → chunk-EQ4U73UH.js} +77 -24
- package/dist/chunk-EQ4U73UH.js.map +1 -0
- package/dist/{chunk-3JPTUCD7.js → chunk-EUBDYOLD.js} +32 -23
- package/dist/chunk-EUBDYOLD.js.map +1 -0
- package/dist/{chunk-YNDLCWXS.js → chunk-EZ25VE3G.js} +4 -4
- package/dist/{chunk-YAUHSNPB.js → chunk-F74G4KIH.js} +2 -2
- package/dist/{chunk-MPGLM2VU.js → chunk-FF3EFLUT.js} +7 -5
- package/dist/chunk-FF3EFLUT.js.map +1 -0
- package/dist/{chunk-BV2BRHD4.js → chunk-FHGWKODN.js} +2 -2
- package/dist/chunk-FKBGK3NC.js +62 -0
- package/dist/chunk-FKBGK3NC.js.map +1 -0
- package/dist/{chunk-X2KO4FK5.js → chunk-FNFURZBZ.js} +5 -5
- package/dist/chunk-FNFURZBZ.js.map +1 -0
- package/dist/{chunk-KESJ5XX3.js → chunk-G54BOKZ6.js} +2 -2
- package/dist/{chunk-YK7GOPIE.js → chunk-G67EUQAB.js} +9 -7
- package/dist/chunk-G67EUQAB.js.map +1 -0
- package/dist/{chunk-K36RMINQ.js → chunk-GOZXQNIM.js} +3 -3
- package/dist/{chunk-OQSSQOKQ.js → chunk-GULUWOA5.js} +2 -2
- package/dist/{chunk-K6RTQZA4.js → chunk-HFUKXOUW.js} +2 -2
- package/dist/{chunk-WV254W4V.js → chunk-HLN2HOXX.js} +2 -2
- package/dist/{chunk-FUOAIJKQ.js → chunk-IBDWYRDR.js} +3 -3
- package/dist/{chunk-Z3DDQXM4.js → chunk-IBGU6AN6.js} +45 -45
- package/dist/chunk-IBGU6AN6.js.map +1 -0
- package/dist/{chunk-AJG2TAMR.js → chunk-IEEV27BC.js} +2 -2
- package/dist/chunk-IGLJNHOX.js +62 -0
- package/dist/chunk-IGLJNHOX.js.map +1 -0
- package/dist/{chunk-LTDLJCMQ.js → chunk-IJ3OEECW.js} +4 -4
- package/dist/{chunk-BK4FBHZ3.js → chunk-IPD5GC7C.js} +3 -3
- package/dist/{chunk-KFLD7ZZG.js → chunk-IUKYFTGW.js} +3 -3
- package/dist/{chunk-GCAQ3VUJ.js → chunk-JM2YZKCQ.js} +2 -2
- package/dist/{chunk-BNQJKWWZ.js → chunk-K3FH3LAQ.js} +4 -4
- package/dist/{chunk-LU66I56Q.js → chunk-KVSOEVAF.js} +61 -12
- package/dist/chunk-KVSOEVAF.js.map +1 -0
- package/dist/chunk-M3BEJJNV.js +31 -0
- package/dist/chunk-M3BEJJNV.js.map +1 -0
- package/dist/{chunk-RZH5RVJC.js → chunk-MTTYIXSU.js} +2 -2
- package/dist/{chunk-XHGYZRVW.js → chunk-NIVKG3DS.js} +5 -5
- package/dist/{chunk-VZELUMKK.js → chunk-NQP4ZYGG.js} +2 -2
- package/dist/{chunk-7PMOCQOL.js → chunk-NT7F7WJQ.js} +192 -63
- package/dist/chunk-NT7F7WJQ.js.map +1 -0
- package/dist/{chunk-YO2JEX2Z.js → chunk-OGBQMY46.js} +2 -2
- package/dist/{chunk-TLHF75OR.js → chunk-OL553AWT.js} +12 -12
- package/dist/{chunk-CLVILSBR.js → chunk-OZNBZJ3S.js} +22 -22
- package/dist/{chunk-VUROWXFL.js → chunk-P452SEKO.js} +2 -2
- package/dist/{chunk-JX3E4XCW.js → chunk-PDOOUXCS.js} +2 -2
- package/dist/{chunk-6QYPL6NI.js → chunk-PKZ274PI.js} +3 -3
- package/dist/{chunk-KJCL2FZO.js → chunk-QD5R6U5J.js} +2 -2
- package/dist/{chunk-JRVMCZE3.js → chunk-QEYTRFR3.js} +2 -2
- package/dist/{chunk-VKDAJJQP.js → chunk-QEZ3DW5O.js} +11 -11
- package/dist/{chunk-WYPCJK6K.js → chunk-QKERFEJS.js} +2 -2
- package/dist/{chunk-BDN3TDGV.js → chunk-QKES7GU6.js} +2 -2
- package/dist/{chunk-PCQWKMOL.js → chunk-QNSZYTSG.js} +2 -2
- package/dist/chunk-QV2VZKD5.js +31 -0
- package/dist/chunk-QV2VZKD5.js.map +1 -0
- package/dist/{chunk-IZJQNDGI.js → chunk-R6HMAIKM.js} +2 -2
- package/dist/{chunk-CIIHIAJ2.js → chunk-ROGO7FRA.js} +6 -5
- package/dist/{chunk-CIIHIAJ2.js.map → chunk-ROGO7FRA.js.map} +1 -1
- package/dist/{chunk-NUSAAN75.js → chunk-ROKDHO74.js} +2 -2
- package/dist/{chunk-D5SSBCYY.js → chunk-RWGRSKN5.js} +7 -5
- package/dist/chunk-RWGRSKN5.js.map +1 -0
- package/dist/{chunk-FF6N7BXC.js → chunk-S45INSV3.js} +4 -4
- package/dist/{chunk-2RUTN7PR.js → chunk-S7GJNA3K.js} +2 -2
- package/dist/{chunk-XT45WCKX.js → chunk-SYKJBS7S.js} +10 -10
- package/dist/{chunk-5BE3FLKL.js → chunk-TAUHAP5J.js} +4 -4
- package/dist/{chunk-RJWL3RHB.js → chunk-TFEHSPGQ.js} +2 -2
- package/dist/{chunk-XYP5WBDO.js → chunk-TQ4SCPGQ.js} +76 -60
- package/dist/chunk-TQ4SCPGQ.js.map +1 -0
- package/dist/{chunk-K3B72DKJ.js → chunk-TRMZHGIX.js} +8 -8
- package/dist/{chunk-WBDIZWJQ.js → chunk-TSS3D7ZY.js} +2 -2
- package/dist/{chunk-6LCWKS62.js → chunk-UAUV3BWS.js} +104 -121
- package/dist/chunk-UAUV3BWS.js.map +1 -0
- package/dist/{chunk-KQUSP6LB.js → chunk-UDKWFDV2.js} +1 -1
- package/dist/chunk-UDKWFDV2.js.map +1 -0
- package/dist/{chunk-HQPFACXO.js → chunk-UH3FPX3L.js} +2 -2
- package/dist/{chunk-GGJFRVRD.js → chunk-US7KPQA5.js} +2 -2
- package/dist/{chunk-FJ2VJTGA.js → chunk-UWRHHRDK.js} +3 -3
- package/dist/{chunk-4KLQI3L2.js → chunk-UXXK523X.js} +40 -2
- package/dist/chunk-UXXK523X.js.map +1 -0
- package/dist/{chunk-SDESKFKT.js → chunk-UYHDMCY2.js} +2 -2
- package/dist/{chunk-3T3VHXUH.js → chunk-VKILERSL.js} +2 -2
- package/dist/{chunk-2TGIKMMZ.js → chunk-WXDTOE6F.js} +2 -2
- package/dist/{chunk-KQLBXUUQ.js → chunk-WYG5UNDY.js} +2 -2
- package/dist/{chunk-DQEMWVMT.js → chunk-X7Y7WX73.js} +1 -1
- package/dist/{chunk-MPDLLLOG.js → chunk-XO23OKMK.js} +9 -5
- package/dist/chunk-XO23OKMK.js.map +1 -0
- package/dist/{chunk-WC475JOG.js → chunk-XU37EJTW.js} +24 -24
- package/dist/chunk-XU37EJTW.js.map +1 -0
- package/dist/{chunk-FZVROZKC.js → chunk-XVZA6VXC.js} +4 -4
- package/dist/{chunk-QA3Y2O6H.js → chunk-YAVIBVCU.js} +2 -2
- package/dist/{chunk-2XJZLSXE.js → chunk-YL4UKMD7.js} +124 -58
- package/dist/chunk-YL4UKMD7.js.map +1 -0
- package/dist/{chunk-VPEIWMUT.js → chunk-YLTU2DC4.js} +2 -2
- package/dist/{chunk-HWHNQVRV.js → chunk-YMJRLT7S.js} +28 -8
- package/dist/chunk-YMJRLT7S.js.map +1 -0
- package/dist/{chunk-EBOHFZI4.js → chunk-YRZPBN46.js} +3 -3
- package/dist/{chunk-FMFCQRDM.js → chunk-ZANNIXXE.js} +3 -3
- package/dist/{chunk-HQGATKG2.js → chunk-ZBFLV657.js} +3 -3
- package/dist/{chunk-6L63IN3M.js → chunk-ZOJUWVAU.js} +1 -1
- package/dist/cli.js +144 -141
- package/dist/coding/export-okf-codegraph.js +24 -24
- package/dist/coding/pre-action-gate.js +2 -2
- package/dist/compounding/engine.js +26 -26
- package/dist/config.js +35 -35
- package/dist/connectors/codex-materialize-runner.js +24 -24
- package/dist/connectors/index.js +28 -28
- package/dist/contradiction/index.js +4 -4
- package/dist/convergence-refresh.js +26 -26
- package/dist/corpus-watermark.js +28 -28
- package/dist/dashboard-runtime.js +2 -2
- package/dist/deep-recall-config.js +2 -2
- package/dist/deep-recall-http-glue.js +12 -12
- package/dist/entity-retrieval.js +25 -25
- package/dist/event-order-recall.js +2 -2
- package/dist/explicit-capture.js +6 -6
- package/dist/external-wiki-access.js +91 -89
- package/dist/extraction-evidence-class.js +2 -2
- package/dist/extraction-quote-grounding.d.ts +5 -1
- package/dist/extraction-quote-grounding.d.ts.map +1 -1
- package/dist/extraction-quote-grounding.js +1 -1
- package/dist/extraction-source-grounding-rules.d.ts +3 -1
- package/dist/extraction-source-grounding-rules.d.ts.map +1 -1
- package/dist/extraction-source-grounding-rules.js +2 -2
- package/dist/extraction-source-grounding.d.ts +1 -1
- package/dist/extraction-source-grounding.d.ts.map +1 -1
- package/dist/extraction-source-grounding.js +5 -5
- package/dist/extraction-span-materialize.js +2 -2
- package/dist/extraction.d.ts.map +1 -1
- package/dist/extraction.js +43 -43
- package/dist/{first-start-migration-QVD22WWS.js → first-start-migration-A6PHIBUQ.js} +4 -4
- package/dist/{graph-edge-decay-CXTNJ7IQ.js → graph-edge-decay-EKWXRBWF.js} +5 -5
- package/dist/graph-edge-decay-EKWXRBWF.js.map +1 -0
- package/dist/graph-jsonl.js +3 -3
- package/dist/graph-snapshot.js +3 -3
- package/dist/graph.js +2 -2
- package/dist/harmonic-retrieval.js +24 -24
- package/dist/index.js +255 -251
- package/dist/lcm/engine.js +2 -2
- package/dist/lcm/index.js +5 -5
- package/dist/local-llm.js +3 -3
- package/dist/location/index.js +28 -28
- package/dist/maintenance/memory-governance.js +24 -24
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +24 -24
- package/dist/maintenance/rebuild-memory-projection-runner.js +26 -26
- package/dist/maintenance/rebuild-memory-projection.js +25 -25
- package/dist/memory-browse-http-glue.js +2 -2
- package/dist/memory-browse-operations.js +91 -89
- package/dist/memory-browse.js +5 -5
- package/dist/memory-subject.js +2 -2
- package/dist/namespaces/migrate.js +25 -25
- package/dist/namespaces/search.d.ts +9 -0
- package/dist/namespaces/search.d.ts.map +1 -1
- package/dist/namespaces/search.js +13 -7
- package/dist/namespaces/storage.js +24 -24
- package/dist/offline-sync-changeset-generations.js +3 -3
- package/dist/offline-sync-embedding-generation.js +2 -2
- package/dist/offline-sync-impression-drain.js +2 -2
- package/dist/offline-sync-snapshot-stream.js +6 -6
- package/dist/offline-sync.js +5 -5
- package/dist/operator-doctor-corpus.js +31 -31
- package/dist/operator-doctor-replica.js +4 -4
- package/dist/operator-toolkit.js +49 -49
- package/dist/orchestration/backlog-scheduler.d.ts +3 -2
- package/dist/orchestration/backlog-scheduler.d.ts.map +1 -1
- package/dist/orchestration/backlog-scheduler.js +209 -205
- package/dist/orchestration/backlog-scheduler.js.map +1 -1
- package/dist/orchestration/buffer-drain-runner.d.ts +6 -1
- package/dist/orchestration/buffer-drain-runner.d.ts.map +1 -1
- package/dist/orchestration/buffer-drain-runner.js +199 -195
- package/dist/orchestration/extraction-persist.d.ts.map +1 -1
- package/dist/orchestration/fact-dedup-window.d.ts +1 -4
- package/dist/orchestration/fact-dedup-window.d.ts.map +1 -1
- package/dist/orchestration/maintenance.js +41 -41
- package/dist/orchestration/turn-ingestion.d.ts +2 -9
- package/dist/orchestration/turn-ingestion.d.ts.map +1 -1
- package/dist/orchestrator.js +198 -194
- package/dist/provenance.js +2 -2
- package/dist/qmd.js +4 -4
- package/dist/recall-navigation-http-glue.js +2 -2
- package/dist/recall-navigation-operations.js +91 -89
- package/dist/recall-navigation.js +90 -88
- package/dist/recall-state-view-admission.js +5 -5
- package/dist/recall-why-service.js +5 -5
- package/dist/reconcile/manifest.js +2 -2
- package/dist/replica-divergence.js +3 -3
- package/dist/{resolution-BNWE5Y25.js → resolution-23ZBDM6A.js} +5 -5
- package/dist/resolution-23ZBDM6A.js.map +1 -0
- package/dist/resume-bundles.js +36 -36
- package/dist/salvage-envelope.js +4 -4
- package/dist/search/factory.js +6 -6
- package/dist/search/index.js +12 -12
- package/dist/semantic-consolidation.js +25 -25
- package/dist/semantic-rule-promotion.js +24 -24
- package/dist/semantic-rule-verifier.js +24 -24
- package/dist/source-agent-qualifier.d.ts +2 -0
- package/dist/source-agent-qualifier.d.ts.map +1 -1
- package/dist/source-agent-qualifier.js +36 -36
- package/dist/standup/build.js +25 -25
- package/dist/storage/buffer-drain-claims.d.ts +18 -6
- package/dist/storage/buffer-drain-claims.d.ts.map +1 -1
- package/dist/storage/buffer-drain-claims.js +2 -2
- package/dist/storage/buffer-session-files.js +2 -2
- package/dist/storage/content-hash-index.d.ts +8 -0
- package/dist/storage/content-hash-index.d.ts.map +1 -1
- package/dist/storage.d.ts +10 -1
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +23 -23
- package/dist/subject-config.js +3 -3
- package/dist/summarizer.js +4 -4
- package/dist/support-passport/index.js +93 -91
- package/dist/tokens.js +3 -3
- package/dist/transfer/autodetect.js +1 -1
- package/dist/transfer/backup.js +2 -2
- package/dist/transfer/capsule-export.js +3 -3
- package/dist/transfer/capsule-gunzip.d.ts +14 -0
- package/dist/transfer/capsule-gunzip.d.ts.map +1 -0
- package/dist/transfer/capsule-import.d.ts.map +1 -1
- package/dist/transfer/capsule-import.js +3 -2
- package/dist/transfer/capsule-merge.d.ts.map +1 -1
- package/dist/transfer/export-okf.js +24 -24
- package/dist/verified-recall.js +24 -24
- package/dist/who-knows-cli.js +26 -26
- package/dist/who-knows.js +25 -25
- package/dist/write-envelope.js +3 -3
- package/package.json +2 -2
- package/src/access-boundary.ts +15 -0
- package/src/access-errors.ts +22 -0
- package/src/access-fleet-wide-work-peer.test.ts +236 -0
- package/src/access-http-peer-fleet-wide.test.ts +192 -0
- package/src/access-http.test.ts +249 -4
- package/src/access-http.ts +114 -39
- package/src/access-mcp-frames.test.ts +47 -0
- package/src/access-mcp-frames.ts +74 -0
- package/src/access-mcp.test.ts +210 -0
- package/src/access-mcp.ts +47 -37
- package/src/access-observe-replay-durability.test.ts +40 -4
- package/src/access-observe-write-surface.ts +4 -4
- package/src/access-operations-batch.ts +23 -12
- package/src/access-proto-injection.test.ts +340 -0
- package/src/access-schema.ts +31 -5
- package/src/access-service-capsule-import.test.ts +235 -0
- package/src/access-service.ts +6 -45
- package/src/backlog-scheduler.test.ts +443 -3
- package/src/briefing-deadline.ts +5 -4
- package/src/buffer-admission.ts +45 -0
- package/src/buffer-eviction-backpressure.test.ts +211 -0
- package/src/buffer-eviction.ts +8 -14
- package/src/buffer-incremental-persistence.test.ts +25 -37
- package/src/buffer-persistence.ts +15 -6
- package/src/buffer-session.test.ts +32 -16
- package/src/buffer-turn-helpers.ts +47 -0
- package/src/buffer-types.ts +28 -1
- package/src/buffer.ts +83 -91
- package/src/capsule-import-gate.ts +109 -0
- package/src/cli.ts +4 -4
- package/src/extraction-quote-grounding.test.ts +125 -0
- package/src/extraction-quote-grounding.ts +99 -25
- package/src/extraction-source-grounding-rules.ts +6 -1
- package/src/extraction-source-grounding.ts +2 -0
- package/src/extraction.ts +3 -4
- package/src/fact-dedup-window.test.ts +103 -20
- package/src/namespaces/search.test.ts +480 -1
- package/src/namespaces/search.ts +93 -10
- package/src/orchestration/backlog-scheduler.ts +13 -12
- package/src/orchestration/buffer-drain-runner.ts +40 -16
- package/src/orchestration/extraction-persist.ts +2 -10
- package/src/orchestration/fact-dedup-window.ts +5 -12
- package/src/orchestration/turn-ingestion.ts +38 -39
- package/src/recall-navigation-operations.ts +9 -2
- package/src/source-agent-qualifier.ts +3 -0
- package/src/storage/buffer-drain-claims.test.ts +330 -6
- package/src/storage/buffer-drain-claims.ts +226 -80
- package/src/storage/buffer-session-files.ts +2 -2
- package/src/storage/content-hash-index.ts +9 -0
- package/src/storage.ts +15 -0
- package/src/transfer/capsule-gunzip.ts +39 -0
- package/src/transfer/capsule-import.test.ts +73 -0
- package/src/transfer/capsule-import.ts +2 -2
- package/src/transfer/capsule-merge.test.ts +34 -0
- package/src/transfer/capsule-merge.ts +2 -10
- package/dist/capsule-merge-XVTQJJ6R.js.map +0 -1
- package/dist/chunk-2XJZLSXE.js.map +0 -1
- package/dist/chunk-3JPTUCD7.js.map +0 -1
- package/dist/chunk-4KLQI3L2.js.map +0 -1
- package/dist/chunk-6LCWKS62.js.map +0 -1
- package/dist/chunk-77R7BQZ2.js.map +0 -1
- package/dist/chunk-7PMOCQOL.js.map +0 -1
- package/dist/chunk-B74NPUHT.js.map +0 -1
- package/dist/chunk-D5SSBCYY.js.map +0 -1
- package/dist/chunk-HLKUZW42.js.map +0 -1
- package/dist/chunk-HWHNQVRV.js.map +0 -1
- package/dist/chunk-KASJJGRG.js.map +0 -1
- package/dist/chunk-KQUSP6LB.js.map +0 -1
- package/dist/chunk-LGEH473S.js.map +0 -1
- package/dist/chunk-LU66I56Q.js.map +0 -1
- package/dist/chunk-MPDLLLOG.js.map +0 -1
- package/dist/chunk-MPGLM2VU.js.map +0 -1
- package/dist/chunk-QZBTIYKD.js.map +0 -1
- package/dist/chunk-VLMBSGQF.js.map +0 -1
- package/dist/chunk-WAD32CZG.js.map +0 -1
- package/dist/chunk-WC475JOG.js.map +0 -1
- package/dist/chunk-X2KO4FK5.js.map +0 -1
- package/dist/chunk-XYP5WBDO.js.map +0 -1
- package/dist/chunk-YK7GOPIE.js.map +0 -1
- package/dist/chunk-Z3DDQXM4.js.map +0 -1
- /package/dist/{capsule-crypto-YO5QJ6L3.js.map → access-mcp-frames.js.map} +0 -0
- /package/dist/{auto-sync-C7E6G7Y2.js.map → auto-sync-3AZW7MXI.js.map} +0 -0
- /package/dist/{graph-edge-decay-CXTNJ7IQ.js.map → buffer-admission.js.map} +0 -0
- /package/dist/{resolution-BNWE5Y25.js.map → capsule-crypto-7FJQINUR.js.map} +0 -0
- /package/dist/{chunk-MSIRVXQ3.js.map → chunk-2G4TV2ML.js.map} +0 -0
- /package/dist/{chunk-IR4C3DP2.js.map → chunk-2K3AUDH7.js.map} +0 -0
- /package/dist/{chunk-2IW5PT7U.js.map → chunk-2N4OUSIA.js.map} +0 -0
- /package/dist/{chunk-BXLOS5AJ.js.map → chunk-2NLLXCJG.js.map} +0 -0
- /package/dist/{chunk-QZSIDSB2.js.map → chunk-2NUDHVYM.js.map} +0 -0
- /package/dist/{chunk-HPFVZPRH.js.map → chunk-2U4LMSEX.js.map} +0 -0
- /package/dist/{chunk-WHQXWFH7.js.map → chunk-2XOFVTTT.js.map} +0 -0
- /package/dist/{chunk-I6ORDT54.js.map → chunk-32CA7B6L.js.map} +0 -0
- /package/dist/{chunk-T77AX6DG.js.map → chunk-32YX7EJ3.js.map} +0 -0
- /package/dist/{chunk-HLWXCDUQ.js.map → chunk-3DAWHP5K.js.map} +0 -0
- /package/dist/{chunk-ZJJFUEPO.js.map → chunk-3PJEDHLD.js.map} +0 -0
- /package/dist/{chunk-PJYK7HH2.js.map → chunk-3UZ2VMKD.js.map} +0 -0
- /package/dist/{chunk-IN4OFAHX.js.map → chunk-3Y5X6YFH.js.map} +0 -0
- /package/dist/{chunk-YUTT4ZTI.js.map → chunk-45VUHDV2.js.map} +0 -0
- /package/dist/{chunk-T6T2RMWW.js.map → chunk-4GMI5HFH.js.map} +0 -0
- /package/dist/{chunk-YOEYL6CH.js.map → chunk-4LCVA2LW.js.map} +0 -0
- /package/dist/{chunk-66JOLMWS.js.map → chunk-4X7HQKAG.js.map} +0 -0
- /package/dist/{chunk-65SKM4BF.js.map → chunk-4XYAWK5H.js.map} +0 -0
- /package/dist/{chunk-H7BZBSP6.js.map → chunk-52II7PB6.js.map} +0 -0
- /package/dist/{chunk-TPX7XXY4.js.map → chunk-5SOSQTVE.js.map} +0 -0
- /package/dist/{chunk-G3T6YMBO.js.map → chunk-64S6X4IU.js.map} +0 -0
- /package/dist/{chunk-BK7AJUWB.js.map → chunk-6BCWFCDG.js.map} +0 -0
- /package/dist/{chunk-V27FK6D6.js.map → chunk-6XTUZKCZ.js.map} +0 -0
- /package/dist/{chunk-LECSS7EE.js.map → chunk-73UZZFJC.js.map} +0 -0
- /package/dist/{chunk-4EACCIPO.js.map → chunk-77PDJ4OE.js.map} +0 -0
- /package/dist/{chunk-GQZSGXR6.js.map → chunk-7GSL4LBG.js.map} +0 -0
- /package/dist/{chunk-B6SPPYGJ.js.map → chunk-7IWRLYFJ.js.map} +0 -0
- /package/dist/{chunk-R45L4TV6.js.map → chunk-7LCGFUMB.js.map} +0 -0
- /package/dist/{chunk-A7BM2JTH.js.map → chunk-AJHNXIQU.js.map} +0 -0
- /package/dist/{chunk-2D7BTSDW.js.map → chunk-ANZZLRUX.js.map} +0 -0
- /package/dist/{chunk-ZXF3O7P4.js.map → chunk-ARRTLPRC.js.map} +0 -0
- /package/dist/{chunk-S5CQI2KF.js.map → chunk-B3GKQ6DU.js.map} +0 -0
- /package/dist/{chunk-XRBHIR7Q.js.map → chunk-B56LORLY.js.map} +0 -0
- /package/dist/{chunk-KO3YBHOV.js.map → chunk-BIVKPQ6Q.js.map} +0 -0
- /package/dist/{chunk-54FDG7K7.js.map → chunk-C2HORX76.js.map} +0 -0
- /package/dist/{chunk-E33UJHY3.js.map → chunk-CN7Q3T2J.js.map} +0 -0
- /package/dist/{chunk-YUTG35M4.js.map → chunk-CYCEROPR.js.map} +0 -0
- /package/dist/{chunk-TFLPDNQV.js.map → chunk-D5EV6Y2L.js.map} +0 -0
- /package/dist/{chunk-BFBS7OFQ.js.map → chunk-DC55RIEZ.js.map} +0 -0
- /package/dist/{chunk-B4ZU34JQ.js.map → chunk-DT7SNJH6.js.map} +0 -0
- /package/dist/{chunk-FVRTCYZ3.js.map → chunk-EA2LA5RI.js.map} +0 -0
- /package/dist/{chunk-WBN66HPI.js.map → chunk-ELHJK7LM.js.map} +0 -0
- /package/dist/{chunk-YNDLCWXS.js.map → chunk-EZ25VE3G.js.map} +0 -0
- /package/dist/{chunk-YAUHSNPB.js.map → chunk-F74G4KIH.js.map} +0 -0
- /package/dist/{chunk-BV2BRHD4.js.map → chunk-FHGWKODN.js.map} +0 -0
- /package/dist/{chunk-KESJ5XX3.js.map → chunk-G54BOKZ6.js.map} +0 -0
- /package/dist/{chunk-K36RMINQ.js.map → chunk-GOZXQNIM.js.map} +0 -0
- /package/dist/{chunk-OQSSQOKQ.js.map → chunk-GULUWOA5.js.map} +0 -0
- /package/dist/{chunk-K6RTQZA4.js.map → chunk-HFUKXOUW.js.map} +0 -0
- /package/dist/{chunk-WV254W4V.js.map → chunk-HLN2HOXX.js.map} +0 -0
- /package/dist/{chunk-FUOAIJKQ.js.map → chunk-IBDWYRDR.js.map} +0 -0
- /package/dist/{chunk-AJG2TAMR.js.map → chunk-IEEV27BC.js.map} +0 -0
- /package/dist/{chunk-LTDLJCMQ.js.map → chunk-IJ3OEECW.js.map} +0 -0
- /package/dist/{chunk-BK4FBHZ3.js.map → chunk-IPD5GC7C.js.map} +0 -0
- /package/dist/{chunk-KFLD7ZZG.js.map → chunk-IUKYFTGW.js.map} +0 -0
- /package/dist/{chunk-GCAQ3VUJ.js.map → chunk-JM2YZKCQ.js.map} +0 -0
- /package/dist/{chunk-BNQJKWWZ.js.map → chunk-K3FH3LAQ.js.map} +0 -0
- /package/dist/{chunk-RZH5RVJC.js.map → chunk-MTTYIXSU.js.map} +0 -0
- /package/dist/{chunk-XHGYZRVW.js.map → chunk-NIVKG3DS.js.map} +0 -0
- /package/dist/{chunk-VZELUMKK.js.map → chunk-NQP4ZYGG.js.map} +0 -0
- /package/dist/{chunk-YO2JEX2Z.js.map → chunk-OGBQMY46.js.map} +0 -0
- /package/dist/{chunk-TLHF75OR.js.map → chunk-OL553AWT.js.map} +0 -0
- /package/dist/{chunk-CLVILSBR.js.map → chunk-OZNBZJ3S.js.map} +0 -0
- /package/dist/{chunk-VUROWXFL.js.map → chunk-P452SEKO.js.map} +0 -0
- /package/dist/{chunk-JX3E4XCW.js.map → chunk-PDOOUXCS.js.map} +0 -0
- /package/dist/{chunk-6QYPL6NI.js.map → chunk-PKZ274PI.js.map} +0 -0
- /package/dist/{chunk-KJCL2FZO.js.map → chunk-QD5R6U5J.js.map} +0 -0
- /package/dist/{chunk-JRVMCZE3.js.map → chunk-QEYTRFR3.js.map} +0 -0
- /package/dist/{chunk-VKDAJJQP.js.map → chunk-QEZ3DW5O.js.map} +0 -0
- /package/dist/{chunk-WYPCJK6K.js.map → chunk-QKERFEJS.js.map} +0 -0
- /package/dist/{chunk-BDN3TDGV.js.map → chunk-QKES7GU6.js.map} +0 -0
- /package/dist/{chunk-PCQWKMOL.js.map → chunk-QNSZYTSG.js.map} +0 -0
- /package/dist/{chunk-IZJQNDGI.js.map → chunk-R6HMAIKM.js.map} +0 -0
- /package/dist/{chunk-NUSAAN75.js.map → chunk-ROKDHO74.js.map} +0 -0
- /package/dist/{chunk-FF6N7BXC.js.map → chunk-S45INSV3.js.map} +0 -0
- /package/dist/{chunk-2RUTN7PR.js.map → chunk-S7GJNA3K.js.map} +0 -0
- /package/dist/{chunk-XT45WCKX.js.map → chunk-SYKJBS7S.js.map} +0 -0
- /package/dist/{chunk-5BE3FLKL.js.map → chunk-TAUHAP5J.js.map} +0 -0
- /package/dist/{chunk-RJWL3RHB.js.map → chunk-TFEHSPGQ.js.map} +0 -0
- /package/dist/{chunk-K3B72DKJ.js.map → chunk-TRMZHGIX.js.map} +0 -0
- /package/dist/{chunk-WBDIZWJQ.js.map → chunk-TSS3D7ZY.js.map} +0 -0
- /package/dist/{chunk-HQPFACXO.js.map → chunk-UH3FPX3L.js.map} +0 -0
- /package/dist/{chunk-GGJFRVRD.js.map → chunk-US7KPQA5.js.map} +0 -0
- /package/dist/{chunk-FJ2VJTGA.js.map → chunk-UWRHHRDK.js.map} +0 -0
- /package/dist/{chunk-SDESKFKT.js.map → chunk-UYHDMCY2.js.map} +0 -0
- /package/dist/{chunk-3T3VHXUH.js.map → chunk-VKILERSL.js.map} +0 -0
- /package/dist/{chunk-2TGIKMMZ.js.map → chunk-WXDTOE6F.js.map} +0 -0
- /package/dist/{chunk-KQLBXUUQ.js.map → chunk-WYG5UNDY.js.map} +0 -0
- /package/dist/{chunk-DQEMWVMT.js.map → chunk-X7Y7WX73.js.map} +0 -0
- /package/dist/{chunk-FZVROZKC.js.map → chunk-XVZA6VXC.js.map} +0 -0
- /package/dist/{chunk-QA3Y2O6H.js.map → chunk-YAVIBVCU.js.map} +0 -0
- /package/dist/{chunk-VPEIWMUT.js.map → chunk-YLTU2DC4.js.map} +0 -0
- /package/dist/{chunk-EBOHFZI4.js.map → chunk-YRZPBN46.js.map} +0 -0
- /package/dist/{chunk-FMFCQRDM.js.map → chunk-ZANNIXXE.js.map} +0 -0
- /package/dist/{chunk-HQGATKG2.js.map → chunk-ZBFLV657.js.map} +0 -0
- /package/dist/{chunk-6L63IN3M.js.map → chunk-ZOJUWVAU.js.map} +0 -0
- /package/dist/{first-start-migration-QVD22WWS.js.map → first-start-migration-A6PHIBUQ.js.map} +0 -0
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Request-local prototype injection and post-parse namespace authorization
|
|
3
|
+
* (issue #3225 B1).
|
|
4
|
+
*
|
|
5
|
+
* A JSON body can carry an own `__proto__` member (JSON.parse defines it as a
|
|
6
|
+
* real own property). The null-strip preprocessors used to copy that member
|
|
7
|
+
* into a plain `{}` accumulator, whose setter switched the accumulator's
|
|
8
|
+
* prototype, so an inherited `namespace` became an ordinary parsed field
|
|
9
|
+
* AFTER the transports had authorized the raw arguments. These tests drive
|
|
10
|
+
* the real registered boundary operations with JSON-decoded input and assert
|
|
11
|
+
* that reserved keys are rejected and that a parsed explicit namespace still
|
|
12
|
+
* passes the token namespace allow-list before the handler runs.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import assert from "node:assert/strict";
|
|
16
|
+
import test from "node:test";
|
|
17
|
+
|
|
18
|
+
import { getOperation, type BoundOperation } from "./access-boundary.js";
|
|
19
|
+
import {
|
|
20
|
+
EngramAccessInputError,
|
|
21
|
+
type EngramAccessMemoryResponse,
|
|
22
|
+
type EngramAccessRecallRequest,
|
|
23
|
+
type EngramAccessRecallResponse,
|
|
24
|
+
type EngramAccessService,
|
|
25
|
+
} from "./access-service.js";
|
|
26
|
+
import { validateRequest, type RecallRequest } from "./access-schema.js";
|
|
27
|
+
import type { RecallNavigationResult } from "./recall-navigation.js";
|
|
28
|
+
import { EngramAccessForbiddenError } from "./access-errors.js";
|
|
29
|
+
import { tokenCapabilityStore } from "./access-token-capabilities.js";
|
|
30
|
+
import "./access-operations.js";
|
|
31
|
+
import "./access-operations-batch.js";
|
|
32
|
+
import "./recall-navigation-operations.js";
|
|
33
|
+
|
|
34
|
+
function makeService(handlers: Partial<EngramAccessService>): EngramAccessService {
|
|
35
|
+
// SAFETY: the operations under test dispatch only through the handlers supplied here.
|
|
36
|
+
return handlers as EngramAccessService;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function mustBeRegistered(name: Parameters<typeof getOperation>[0]): BoundOperation {
|
|
40
|
+
const op = getOperation(name);
|
|
41
|
+
assert.ok(op, `${name} must be registered`);
|
|
42
|
+
return op;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
test("own __proto__ member cannot smuggle a namespace into memory_profile", async () => {
|
|
46
|
+
let handlerCalls = 0;
|
|
47
|
+
const service = makeService({
|
|
48
|
+
memoryProfile: async () => {
|
|
49
|
+
handlerCalls += 1;
|
|
50
|
+
throw new Error("handler must not run for a prototype-injection attempt");
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
const exploit = JSON.parse('{"__proto__":{"namespace":"other"}}');
|
|
54
|
+
const op = mustBeRegistered("memory_profile");
|
|
55
|
+
await assert.rejects(
|
|
56
|
+
() =>
|
|
57
|
+
tokenCapabilityStore.run(
|
|
58
|
+
{ version: 1, namespaces: ["default"], ops: ["memory_profile"] },
|
|
59
|
+
() => op.run(exploit, { service }),
|
|
60
|
+
),
|
|
61
|
+
EngramAccessInputError,
|
|
62
|
+
);
|
|
63
|
+
assert.equal(handlerCalls, 0, "handler must not run for a prototype-injection attempt");
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test("own __proto__ member cannot smuggle a namespace into recall", async () => {
|
|
67
|
+
let handlerCalls = 0;
|
|
68
|
+
const service = makeService({
|
|
69
|
+
recall: async () => {
|
|
70
|
+
handlerCalls += 1;
|
|
71
|
+
throw new Error("handler must not run for a prototype-injection attempt");
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
const exploit = JSON.parse('{"__proto__":{"namespace":"other"},"query":"hello"}');
|
|
75
|
+
const op = mustBeRegistered("recall");
|
|
76
|
+
await assert.rejects(
|
|
77
|
+
() =>
|
|
78
|
+
tokenCapabilityStore.run(
|
|
79
|
+
{ version: 1, namespaces: ["default"], ops: ["recall"] },
|
|
80
|
+
() => op.run(exploit, { service }),
|
|
81
|
+
),
|
|
82
|
+
EngramAccessInputError,
|
|
83
|
+
);
|
|
84
|
+
assert.equal(handlerCalls, 0, "handler must not run for a prototype-injection attempt");
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test("reserved constructor and prototype keys are rejected on both null-strip paths", async () => {
|
|
88
|
+
const profileService = makeService({
|
|
89
|
+
memoryProfile: async () => {
|
|
90
|
+
throw new Error("handler must not run for a reserved-key argument");
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
const profileOp = mustBeRegistered("memory_profile");
|
|
94
|
+
await assert.rejects(
|
|
95
|
+
() =>
|
|
96
|
+
tokenCapabilityStore.run(
|
|
97
|
+
{ version: 1, namespaces: ["default"], ops: ["memory_profile"] },
|
|
98
|
+
() => profileOp.run(JSON.parse('{"constructor":{"namespace":"other"}}'), { service: profileService }),
|
|
99
|
+
),
|
|
100
|
+
EngramAccessInputError,
|
|
101
|
+
);
|
|
102
|
+
await assert.rejects(
|
|
103
|
+
() =>
|
|
104
|
+
tokenCapabilityStore.run(
|
|
105
|
+
{ version: 1, namespaces: ["default"], ops: ["memory_profile"] },
|
|
106
|
+
() => profileOp.run(JSON.parse('{"prototype":{"namespace":"other"}}'), { service: profileService }),
|
|
107
|
+
),
|
|
108
|
+
EngramAccessInputError,
|
|
109
|
+
);
|
|
110
|
+
const recallService = makeService({
|
|
111
|
+
recall: async () => {
|
|
112
|
+
throw new Error("handler must not run for a reserved-key argument");
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
const recallOp = mustBeRegistered("recall");
|
|
116
|
+
await assert.rejects(
|
|
117
|
+
() =>
|
|
118
|
+
tokenCapabilityStore.run(
|
|
119
|
+
{ version: 1, namespaces: ["default"], ops: ["recall"] },
|
|
120
|
+
() => recallOp.run(JSON.parse('{"query":"q","constructor":{"namespace":"other"}}'), { service: recallService }),
|
|
121
|
+
),
|
|
122
|
+
EngramAccessInputError,
|
|
123
|
+
);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
test("navigation null-strip rejects an own __proto__ member (memory_expand)", async () => {
|
|
127
|
+
let handlerCalls = 0;
|
|
128
|
+
const service = makeService({
|
|
129
|
+
recallNavigate: async () => {
|
|
130
|
+
handlerCalls += 1;
|
|
131
|
+
throw new Error("handler must not run for a prototype-injection attempt");
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
const exploit = JSON.parse(
|
|
135
|
+
'{"__proto__":{"namespace":"other"},"memoryId":"m1","sessionKey":"s1"}',
|
|
136
|
+
);
|
|
137
|
+
const op = mustBeRegistered("memory_expand");
|
|
138
|
+
await assert.rejects(
|
|
139
|
+
() =>
|
|
140
|
+
tokenCapabilityStore.run(
|
|
141
|
+
{ version: 1, namespaces: ["default"], ops: ["memory_expand"] },
|
|
142
|
+
() => op.run(exploit, { service }),
|
|
143
|
+
),
|
|
144
|
+
EngramAccessInputError,
|
|
145
|
+
);
|
|
146
|
+
assert.equal(handlerCalls, 0, "handler must not run for a prototype-injection attempt");
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
test("a parsed explicit namespace outside the token allow-list is rejected before the handler", async () => {
|
|
150
|
+
let handlerCalls = 0;
|
|
151
|
+
let capturedNamespace: string | undefined;
|
|
152
|
+
const service = makeService({
|
|
153
|
+
memoryProfile: async (namespace) => {
|
|
154
|
+
handlerCalls += 1;
|
|
155
|
+
capturedNamespace = namespace;
|
|
156
|
+
throw new Error("handler must not run for a denied namespace");
|
|
157
|
+
},
|
|
158
|
+
});
|
|
159
|
+
const op = mustBeRegistered("memory_profile");
|
|
160
|
+
await assert.rejects(
|
|
161
|
+
() =>
|
|
162
|
+
tokenCapabilityStore.run(
|
|
163
|
+
{ version: 1, namespaces: ["default"], ops: ["memory_profile"] },
|
|
164
|
+
() => op.run({ namespace: "other" }, { service }),
|
|
165
|
+
),
|
|
166
|
+
EngramAccessForbiddenError,
|
|
167
|
+
);
|
|
168
|
+
assert.equal(handlerCalls, 0, "handler must not run for a denied namespace");
|
|
169
|
+
assert.equal(capturedNamespace, undefined);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
test("an allowed explicit namespace still reaches the handler", async () => {
|
|
173
|
+
let profileCalls = 0;
|
|
174
|
+
let profileNamespace: string | undefined;
|
|
175
|
+
const profileService = makeService({
|
|
176
|
+
memoryProfile: async (namespace) => {
|
|
177
|
+
profileCalls += 1;
|
|
178
|
+
profileNamespace = namespace;
|
|
179
|
+
return {};
|
|
180
|
+
},
|
|
181
|
+
});
|
|
182
|
+
const profileOp = mustBeRegistered("memory_profile");
|
|
183
|
+
await tokenCapabilityStore.run(
|
|
184
|
+
{ version: 1, namespaces: ["default", "other"], ops: ["memory_profile"] },
|
|
185
|
+
() => profileOp.run({ namespace: "other" }, { service: profileService }),
|
|
186
|
+
);
|
|
187
|
+
assert.equal(profileCalls, 1);
|
|
188
|
+
assert.equal(profileNamespace, "other");
|
|
189
|
+
|
|
190
|
+
let recallCalls = 0;
|
|
191
|
+
let recallNamespace: string | undefined;
|
|
192
|
+
const recallResponse: EngramAccessRecallResponse = {
|
|
193
|
+
query: "hello",
|
|
194
|
+
namespace: "other",
|
|
195
|
+
context: "",
|
|
196
|
+
count: 0,
|
|
197
|
+
memoryIds: [],
|
|
198
|
+
results: [],
|
|
199
|
+
fallbackUsed: false,
|
|
200
|
+
sourcesUsed: [],
|
|
201
|
+
disclosure: "chunk",
|
|
202
|
+
};
|
|
203
|
+
const recallService = makeService({
|
|
204
|
+
recall: async (input) => {
|
|
205
|
+
recallCalls += 1;
|
|
206
|
+
recallNamespace = input.namespace;
|
|
207
|
+
return recallResponse;
|
|
208
|
+
},
|
|
209
|
+
});
|
|
210
|
+
const recallOp = mustBeRegistered("recall");
|
|
211
|
+
await tokenCapabilityStore.run(
|
|
212
|
+
{ version: 1, namespaces: ["default", "other"], ops: ["recall"] },
|
|
213
|
+
() => recallOp.run({ query: "hello", namespace: "other" }, { service: recallService }),
|
|
214
|
+
);
|
|
215
|
+
assert.equal(recallCalls, 1);
|
|
216
|
+
assert.equal(recallNamespace, "other");
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
test("an absent namespace is not defaulted at the boundary", async () => {
|
|
220
|
+
let profileNamespace: string | undefined;
|
|
221
|
+
const service = makeService({
|
|
222
|
+
memoryProfile: async (namespace) => {
|
|
223
|
+
profileNamespace = namespace;
|
|
224
|
+
return {};
|
|
225
|
+
},
|
|
226
|
+
});
|
|
227
|
+
const op = mustBeRegistered("memory_profile");
|
|
228
|
+
await tokenCapabilityStore.run(
|
|
229
|
+
{ version: 1, namespaces: ["default"], ops: ["memory_profile"] },
|
|
230
|
+
() => op.run({}, { service }),
|
|
231
|
+
);
|
|
232
|
+
assert.equal(profileNamespace, undefined, "the boundary must not invent a namespace");
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
test("codingContext null clearing still works through recall", async () => {
|
|
236
|
+
let capturedInput: EngramAccessRecallRequest | undefined;
|
|
237
|
+
const recallResponse: EngramAccessRecallResponse = {
|
|
238
|
+
query: "q",
|
|
239
|
+
namespace: "default",
|
|
240
|
+
context: "",
|
|
241
|
+
count: 0,
|
|
242
|
+
memoryIds: [],
|
|
243
|
+
results: [],
|
|
244
|
+
fallbackUsed: false,
|
|
245
|
+
sourcesUsed: [],
|
|
246
|
+
disclosure: "chunk",
|
|
247
|
+
};
|
|
248
|
+
const service = makeService({
|
|
249
|
+
recall: async (input) => {
|
|
250
|
+
capturedInput = input;
|
|
251
|
+
return recallResponse;
|
|
252
|
+
},
|
|
253
|
+
});
|
|
254
|
+
const op = mustBeRegistered("recall");
|
|
255
|
+
await tokenCapabilityStore.run(
|
|
256
|
+
{ version: 1, namespaces: ["default"], ops: ["recall"] },
|
|
257
|
+
() => op.run(JSON.parse('{"query":"q","codingContext":null}'), { service }),
|
|
258
|
+
);
|
|
259
|
+
assert.ok(capturedInput, "recall handler must run");
|
|
260
|
+
assert.ok(Object.hasOwn(capturedInput, "codingContext"), "codingContext must survive the null strip");
|
|
261
|
+
assert.equal(capturedInput.codingContext, null);
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
test("an allowed explicit namespace reaches the memory_expand handler", async () => {
|
|
265
|
+
let capturedNamespace: string | undefined;
|
|
266
|
+
const navigationResult: RecallNavigationResult = {
|
|
267
|
+
ok: false,
|
|
268
|
+
action: "expand",
|
|
269
|
+
error: "disabled",
|
|
270
|
+
message: "",
|
|
271
|
+
rendered: "",
|
|
272
|
+
};
|
|
273
|
+
const service = makeService({
|
|
274
|
+
recallNavigate: async (input) => {
|
|
275
|
+
capturedNamespace = input.namespace;
|
|
276
|
+
return navigationResult;
|
|
277
|
+
},
|
|
278
|
+
});
|
|
279
|
+
const op = mustBeRegistered("memory_expand");
|
|
280
|
+
await tokenCapabilityStore.run(
|
|
281
|
+
{ version: 1, namespaces: ["ns2"], ops: ["memory_expand"] },
|
|
282
|
+
() =>
|
|
283
|
+
op.run({ memoryId: "m1", sessionKey: "s1", namespace: "ns2" }, { service }),
|
|
284
|
+
);
|
|
285
|
+
assert.equal(capturedNamespace, "ns2");
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
test("own __proto__ member cannot smuggle a namespace through the wire request parse", () => {
|
|
289
|
+
// validateRequest is the shared entry the HTTP REST body reader
|
|
290
|
+
// (readValidatedBody) and the MCP wire-schema parse (parseMcpRequest) run.
|
|
291
|
+
// A reserved key returns the documented validation failure, not a throw.
|
|
292
|
+
const recallExploit = JSON.parse('{"__proto__":{"namespace":"other"},"query":"hello"}');
|
|
293
|
+
const recallResult = validateRequest<RecallRequest>("recall", recallExploit);
|
|
294
|
+
assert.equal(recallResult.success, false);
|
|
295
|
+
if (!recallResult.success) {
|
|
296
|
+
assert.equal(recallResult.error.error, "request validation failed");
|
|
297
|
+
assert.equal(recallResult.error.code, "validation_error");
|
|
298
|
+
assert.ok(
|
|
299
|
+
recallResult.error.details.some(
|
|
300
|
+
(detail) => detail.field === "(root)" && /__proto__/.test(detail.message),
|
|
301
|
+
),
|
|
302
|
+
"the details must name the reserved key",
|
|
303
|
+
);
|
|
304
|
+
}
|
|
305
|
+
const observeResult = validateRequest("observe", JSON.parse('{"__proto__":{"namespace":"other"}}'));
|
|
306
|
+
assert.equal(observeResult.success, false);
|
|
307
|
+
if (!observeResult.success) {
|
|
308
|
+
assert.equal(observeResult.error.code, "validation_error");
|
|
309
|
+
assert.ok(
|
|
310
|
+
observeResult.error.details.some((detail) => /__proto__/.test(detail.message)),
|
|
311
|
+
"the details must name the reserved key",
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
test("wire request parse still preserves codingContext null clearing", () => {
|
|
317
|
+
const result = validateRequest<RecallRequest>("recall", JSON.parse('{"query":"q","codingContext":null}'));
|
|
318
|
+
assert.ok(result.success, "a codingContext null clear must parse");
|
|
319
|
+
if (result.success) {
|
|
320
|
+
assert.ok(Object.hasOwn(result.data, "codingContext"), "codingContext must survive the wire null strip");
|
|
321
|
+
assert.equal(result.data.codingContext, null);
|
|
322
|
+
}
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
test("an explicit null namespace still reaches the handler (transport null-for-absent idiom)", async () => {
|
|
326
|
+
let capturedNamespace: string | undefined;
|
|
327
|
+
const memoryResponse: EngramAccessMemoryResponse = { found: true, namespace: "global" };
|
|
328
|
+
const service = makeService({
|
|
329
|
+
memoryGet: async (_memoryId, namespace) => {
|
|
330
|
+
capturedNamespace = namespace;
|
|
331
|
+
return memoryResponse;
|
|
332
|
+
},
|
|
333
|
+
});
|
|
334
|
+
const op = mustBeRegistered("memory_get");
|
|
335
|
+
await tokenCapabilityStore.run(
|
|
336
|
+
{ version: 1, namespaces: ["global"], ops: ["memory_get"] },
|
|
337
|
+
() => op.run({ memoryId: "fact-1", namespace: null, sessionKey: null }, { service }),
|
|
338
|
+
);
|
|
339
|
+
assert.equal(capturedNamespace, undefined, "an explicit null namespace means absent");
|
|
340
|
+
});
|
package/src/access-schema.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import { z } from "zod";
|
|
6
6
|
|
|
7
|
+
import { EngramAccessInputError, rejectReservedArgumentKey } from "./access-errors.js";
|
|
7
8
|
import type { MemoryCategory } from "./types.js";
|
|
8
9
|
import {
|
|
9
10
|
ACTION_CONFIDENCE_CONTEXT_READINESS,
|
|
@@ -855,17 +856,42 @@ export function validateRequest<T = unknown>(
|
|
|
855
856
|
},
|
|
856
857
|
};
|
|
857
858
|
}
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
859
|
+
let parseResult: z.ZodSafeParseResult<unknown>;
|
|
860
|
+
try {
|
|
861
|
+
parseResult = schema.safeParse(stripNullsExceptCodingContext(body));
|
|
862
|
+
} catch (error) {
|
|
863
|
+
// A reserved argument key (__proto__/constructor/prototype, issue #3225
|
|
864
|
+
// B1) is an ordinary client input fault: return the same documented
|
|
865
|
+
// failure shape a failed parse returns instead of throwing past the
|
|
866
|
+
// callers. Anything else keeps propagating.
|
|
867
|
+
if (error instanceof EngramAccessInputError) {
|
|
868
|
+
return {
|
|
869
|
+
success: false,
|
|
870
|
+
error: {
|
|
871
|
+
error: "request validation failed",
|
|
872
|
+
code: "validation_error",
|
|
873
|
+
details: [{ field: "(root)", message: error.message }],
|
|
874
|
+
},
|
|
875
|
+
};
|
|
876
|
+
}
|
|
877
|
+
throw error;
|
|
878
|
+
}
|
|
879
|
+
if (parseResult.success) {
|
|
880
|
+
// SAFETY: T is the type each caller associates with schemaName, and the
|
|
881
|
+
// schema registered under schemaName is that type's canonical parser.
|
|
882
|
+
return { success: true, data: parseResult.data as T };
|
|
861
883
|
}
|
|
862
|
-
return { success: false, error: formatZodError(
|
|
884
|
+
return { success: false, error: formatZodError(parseResult.error) };
|
|
863
885
|
}
|
|
864
886
|
|
|
865
887
|
function stripNullsExceptCodingContext(data: unknown): unknown {
|
|
866
888
|
if (!data || typeof data !== "object" || Array.isArray(data)) return data;
|
|
867
|
-
|
|
889
|
+
// Object.create(null): a plain {} lets a copied own `__proto__` member
|
|
890
|
+
// invoke the prototype setter, so an inherited field could reach the wire
|
|
891
|
+
// schema parse (issue #3225 B1).
|
|
892
|
+
const cleaned = Object.create(null);
|
|
868
893
|
for (const [key, value] of Object.entries(data as Record<string, unknown>)) {
|
|
894
|
+
rejectReservedArgumentKey(key);
|
|
869
895
|
if (value === null && key !== "codingContext") continue;
|
|
870
896
|
cleaned[key] = value;
|
|
871
897
|
}
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { mkdir, mkdtemp, readFile, rm, symlink, writeFile } from "node:fs/promises";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import test, { type TestContext } from "node:test";
|
|
6
|
+
import { EngramAccessInputError } from "./access-errors.js";
|
|
7
|
+
import { EngramAccessService } from "./access-service.js";
|
|
8
|
+
import { defaultCapsulesDir } from "./capsule-cli.js";
|
|
9
|
+
import { parseConfig } from "./config.js";
|
|
10
|
+
import { NamespaceStorageRouter } from "./namespaces/storage.js";
|
|
11
|
+
import type { Orchestrator } from "./orchestrator.js";
|
|
12
|
+
import { generateSalt } from "./secure-store/cipher.js";
|
|
13
|
+
import { buildHeaderFromPassphrase, writeHeader, secureStoreDir } from "./secure-store/header.js";
|
|
14
|
+
import * as keyring from "./secure-store/keyring.js";
|
|
15
|
+
import type { ScryptParams } from "./secure-store/kdf.js";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Capsule import source confinement (issue #3225 C1): a remote import may
|
|
19
|
+
* only read an archive that lives inside the destination namespace's capsule
|
|
20
|
+
* store, the same store `capsule export` writes and `capsule_list` lists.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
const FAST_SCRYPT: ScryptParams = {
|
|
24
|
+
N: 1 << 10,
|
|
25
|
+
r: 8,
|
|
26
|
+
p: 1,
|
|
27
|
+
keyLength: 32,
|
|
28
|
+
maxmem: 64 * 1024 * 1024,
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const TEST_PASSPHRASE = "capsule-confinement-passphrase";
|
|
32
|
+
|
|
33
|
+
/** Initialize a secure-store header in `memoryDir` and unlock its keyring. */
|
|
34
|
+
async function initAndUnlockStore(memoryDir: string): Promise<void> {
|
|
35
|
+
const salt = generateSalt();
|
|
36
|
+
const { header, derivedKey } = buildHeaderFromPassphrase({
|
|
37
|
+
passphrase: TEST_PASSPHRASE,
|
|
38
|
+
salt,
|
|
39
|
+
algorithm: "scrypt",
|
|
40
|
+
params: FAST_SCRYPT,
|
|
41
|
+
});
|
|
42
|
+
await writeHeader(memoryDir, header);
|
|
43
|
+
const keyCopy = Buffer.from(derivedKey);
|
|
44
|
+
keyring.unlock(secureStoreDir(memoryDir), keyCopy);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
interface Fixture {
|
|
48
|
+
memoryDir: string;
|
|
49
|
+
service: EngramAccessService;
|
|
50
|
+
storageRouter: NamespaceStorageRouter;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Real service over real namespace storage; the capsule paths touch only config and getStorage. */
|
|
54
|
+
async function createFixture(t: TestContext): Promise<Fixture> {
|
|
55
|
+
const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-capsule-import-"));
|
|
56
|
+
t.after(() => rm(memoryDir, { recursive: true, force: true }));
|
|
57
|
+
const config = parseConfig({
|
|
58
|
+
memoryDir,
|
|
59
|
+
namespacesEnabled: true,
|
|
60
|
+
defaultNamespace: "default",
|
|
61
|
+
sharedNamespace: "shared",
|
|
62
|
+
namespacePolicies: [
|
|
63
|
+
{ name: "default", readPrincipals: ["owner"], writePrincipals: ["owner"] },
|
|
64
|
+
{ name: "foreign", readPrincipals: ["owner"], writePrincipals: ["owner"] },
|
|
65
|
+
],
|
|
66
|
+
});
|
|
67
|
+
const storageRouter = new NamespaceStorageRouter(config);
|
|
68
|
+
const storage = await storageRouter.storageFor("default");
|
|
69
|
+
await mkdir(path.join(storage.dir, "facts"), { recursive: true });
|
|
70
|
+
await writeFile(path.join(storage.dir, "facts", "seeded.md"), "---\nid: fact-seeded\n---\nSeeded fact body.");
|
|
71
|
+
const dependencies: Pick<Orchestrator, "config" | "getStorage"> = {
|
|
72
|
+
config,
|
|
73
|
+
getStorage: (namespace) => storageRouter.storageFor(namespace ?? config.defaultNamespace),
|
|
74
|
+
};
|
|
75
|
+
// SAFETY: the capsule paths touch only `config` and `getStorage` (the Pick above); the rest of the Orchestrator is unused here.
|
|
76
|
+
return { memoryDir, service: new EngramAccessService(dependencies as Orchestrator), storageRouter };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const REFUSAL = /capsule import failed: archivePath must be an existing file inside the namespace capsule store/;
|
|
80
|
+
|
|
81
|
+
test("capsule import refuses an archive outside the namespace capsule store", async (t) => {
|
|
82
|
+
const { memoryDir, service } = await createFixture(t);
|
|
83
|
+
const exported = await service.capsuleExport({ name: "relocation", namespace: "default", principal: "owner" });
|
|
84
|
+
const outsideArchive = path.join(path.dirname(defaultCapsulesDir(memoryDir)), "outside.capsule.json.gz");
|
|
85
|
+
await mkdir(path.dirname(outsideArchive), { recursive: true });
|
|
86
|
+
await writeFile(outsideArchive, await readFile(exported.archivePath));
|
|
87
|
+
try {
|
|
88
|
+
await assert.rejects(
|
|
89
|
+
() => service.capsuleImport({ archivePath: outsideArchive, namespace: "default", principal: "owner" }),
|
|
90
|
+
REFUSAL,
|
|
91
|
+
);
|
|
92
|
+
// A `..` segment that walks out of the store resolves to the same outside file.
|
|
93
|
+
await assert.rejects(
|
|
94
|
+
() =>
|
|
95
|
+
service.capsuleImport({
|
|
96
|
+
archivePath: path.join(defaultCapsulesDir(memoryDir), "..", "outside.capsule.json.gz"),
|
|
97
|
+
namespace: "default",
|
|
98
|
+
principal: "owner",
|
|
99
|
+
}),
|
|
100
|
+
REFUSAL,
|
|
101
|
+
);
|
|
102
|
+
} finally {
|
|
103
|
+
await rm(outsideArchive, { force: true });
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test("capsule import refuses an archive in another namespace's capsule store", async (t) => {
|
|
108
|
+
const { service, storageRouter } = await createFixture(t);
|
|
109
|
+
const exported = await service.capsuleExport({ name: "cross-principal", namespace: "default", principal: "owner" });
|
|
110
|
+
const foreignStorage = await storageRouter.storageFor("foreign");
|
|
111
|
+
const foreignStore = defaultCapsulesDir(foreignStorage.dir);
|
|
112
|
+
await mkdir(foreignStore, { recursive: true });
|
|
113
|
+
const foreignArchive = path.join(foreignStore, "cross-principal.capsule.json.gz");
|
|
114
|
+
await writeFile(foreignArchive, await readFile(exported.archivePath));
|
|
115
|
+
try {
|
|
116
|
+
await assert.rejects(
|
|
117
|
+
() => service.capsuleImport({ archivePath: foreignArchive, namespace: "default", principal: "owner" }),
|
|
118
|
+
REFUSAL,
|
|
119
|
+
);
|
|
120
|
+
} finally {
|
|
121
|
+
await rm(foreignArchive, { force: true });
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
test("capsule import refuses a store symlink that points outside", async (t) => {
|
|
126
|
+
const { memoryDir, service } = await createFixture(t);
|
|
127
|
+
const exported = await service.capsuleExport({ name: "symlinked", namespace: "default", principal: "owner" });
|
|
128
|
+
const outsideArchive = path.join(path.dirname(defaultCapsulesDir(memoryDir)), "outside.capsule.json.gz");
|
|
129
|
+
await mkdir(path.dirname(outsideArchive), { recursive: true });
|
|
130
|
+
await writeFile(outsideArchive, await readFile(exported.archivePath));
|
|
131
|
+
const linkedPath = path.join(defaultCapsulesDir(memoryDir), "linked.capsule.json.gz");
|
|
132
|
+
await symlink(outsideArchive, linkedPath);
|
|
133
|
+
try {
|
|
134
|
+
await assert.rejects(
|
|
135
|
+
() => service.capsuleImport({ archivePath: linkedPath, namespace: "default", principal: "owner" }),
|
|
136
|
+
REFUSAL,
|
|
137
|
+
);
|
|
138
|
+
} finally {
|
|
139
|
+
await rm(linkedPath, { force: true });
|
|
140
|
+
await rm(outsideArchive, { force: true });
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test("capsule import refuses a missing namespace capsule store", async (t) => {
|
|
145
|
+
const { memoryDir, service } = await createFixture(t);
|
|
146
|
+
const exported = await service.capsuleExport({ name: "nosuchstore", namespace: "default", principal: "owner" });
|
|
147
|
+
const outsideArchive = path.join(path.dirname(defaultCapsulesDir(memoryDir)), "outside.capsule.json.gz");
|
|
148
|
+
await mkdir(path.dirname(outsideArchive), { recursive: true });
|
|
149
|
+
await writeFile(outsideArchive, await readFile(exported.archivePath));
|
|
150
|
+
await rm(defaultCapsulesDir(memoryDir), { recursive: true, force: true });
|
|
151
|
+
try {
|
|
152
|
+
await assert.rejects(
|
|
153
|
+
() => service.capsuleImport({ archivePath: outsideArchive, namespace: "default", principal: "owner" }),
|
|
154
|
+
REFUSAL,
|
|
155
|
+
);
|
|
156
|
+
} finally {
|
|
157
|
+
await rm(outsideArchive, { force: true });
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
test("capsule import gives the same refusal for every outside path, whatever the path holds", async (t) => {
|
|
162
|
+
const { memoryDir, service } = await createFixture(t);
|
|
163
|
+
const outsideDir = path.join(memoryDir, "outside-dir");
|
|
164
|
+
await mkdir(outsideDir, { recursive: true });
|
|
165
|
+
const presentFile = path.join(outsideDir, "present.capsule.json.gz");
|
|
166
|
+
await writeFile(presentFile, "not an archive");
|
|
167
|
+
const loop = path.join(outsideDir, "loop");
|
|
168
|
+
await symlink(loop, loop);
|
|
169
|
+
const candidates = [
|
|
170
|
+
presentFile,
|
|
171
|
+
path.join(outsideDir, "absent.capsule.json.gz"),
|
|
172
|
+
outsideDir,
|
|
173
|
+
loop,
|
|
174
|
+
path.join(presentFile, "below-a-file"),
|
|
175
|
+
`${presentFile}\0suffix`,
|
|
176
|
+
path.join(os.tmpdir(), "remnic-capsule-import-no-such-dir", "x.capsule.json.gz"),
|
|
177
|
+
];
|
|
178
|
+
const messages = new Set<string>();
|
|
179
|
+
for (const archivePath of candidates) {
|
|
180
|
+
await assert.rejects(
|
|
181
|
+
() => service.capsuleImport({ archivePath, namespace: "default", principal: "owner" }),
|
|
182
|
+
(err) => {
|
|
183
|
+
assert.ok(err instanceof EngramAccessInputError);
|
|
184
|
+
messages.add(err.message);
|
|
185
|
+
return true;
|
|
186
|
+
},
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
assert.equal(messages.size, 1, `refusal messages differ: ${[...messages].join(" | ")}`);
|
|
190
|
+
assert.match([...messages][0] ?? "", REFUSAL);
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
test("export, list, and import of the listed archivePath round-trips a plain archive", async (t) => {
|
|
194
|
+
const { service, storageRouter } = await createFixture(t);
|
|
195
|
+
await service.capsuleExport({ name: "roundtrip", namespace: "default", principal: "owner" });
|
|
196
|
+
const list = await service.capsuleList({ namespace: "default", principal: "owner" });
|
|
197
|
+
const entry = list.capsules[0];
|
|
198
|
+
assert.ok(entry, "capsule_list must return the exported archive");
|
|
199
|
+
const storage = await storageRouter.storageFor("default");
|
|
200
|
+
// Restore after loss: drop the seeded file so the import must write it back.
|
|
201
|
+
await rm(path.join(storage.dir, "facts", "seeded.md"), { force: true });
|
|
202
|
+
const imported = await service.capsuleImport({ archivePath: entry.archivePath, namespace: "default", principal: "owner" });
|
|
203
|
+
assert.equal(imported.imported.length, 1);
|
|
204
|
+
assert.equal(imported.skipped.length, 0);
|
|
205
|
+
const landing = imported.imported[0];
|
|
206
|
+
assert.ok(landing, "the seeded fact must be reported as imported");
|
|
207
|
+
const body = await readFile(path.join(storage.dir, landing.targetPath), "utf-8");
|
|
208
|
+
assert.match(body, /Seeded fact body\./);
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
test("export, list, and import of the listed archivePath round-trips an encrypted archive", async (t) => {
|
|
212
|
+
const { memoryDir, service, storageRouter } = await createFixture(t);
|
|
213
|
+
await initAndUnlockStore(memoryDir);
|
|
214
|
+
const exported = await service.capsuleExport({
|
|
215
|
+
name: "encrypted-roundtrip",
|
|
216
|
+
namespace: "default",
|
|
217
|
+
principal: "owner",
|
|
218
|
+
encrypt: true,
|
|
219
|
+
});
|
|
220
|
+
assert.match(exported.archivePath, /\.enc$/);
|
|
221
|
+
const list = await service.capsuleList({ namespace: "default", principal: "owner" });
|
|
222
|
+
const entry = list.capsules[0];
|
|
223
|
+
assert.ok(entry, "capsule_list must return the exported encrypted archive");
|
|
224
|
+
assert.equal(entry.archivePath, exported.archivePath);
|
|
225
|
+
const storage = await storageRouter.storageFor("default");
|
|
226
|
+
// Restore after loss: drop the seeded file so the import must write it back.
|
|
227
|
+
await rm(path.join(storage.dir, "facts", "seeded.md"), { force: true });
|
|
228
|
+
const imported = await service.capsuleImport({ archivePath: entry.archivePath, namespace: "default", principal: "owner" });
|
|
229
|
+
assert.equal(imported.imported.length, 1);
|
|
230
|
+
assert.equal(imported.skipped.length, 0);
|
|
231
|
+
const landing = imported.imported[0];
|
|
232
|
+
assert.ok(landing, "the seeded fact must be reported as imported");
|
|
233
|
+
const body = await readFile(path.join(storage.dir, landing.targetPath), "utf-8");
|
|
234
|
+
assert.match(body, /Seeded fact body\./);
|
|
235
|
+
});
|