@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
package/src/access-service.ts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
-
import { constants as fsConstants } from "node:fs";
|
|
3
2
|
import { readdirSync, unlinkSync } from "node:fs";
|
|
4
3
|
import { stat } from "node:fs/promises";
|
|
5
4
|
import * as nodeFs from "node:fs/promises";
|
|
6
|
-
import { ZodError } from "zod";
|
|
7
5
|
import { summarizeHourlyStatus } from "./summarizer.js";
|
|
8
6
|
import { decodeCitationNamespace } from "./access-citation-namespace.js";
|
|
9
7
|
import {
|
|
@@ -288,6 +286,7 @@ import {
|
|
|
288
286
|
} from "./capabilities.js";
|
|
289
287
|
import { resolveRecallEnhancementCapabilities } from "./capabilities.js";
|
|
290
288
|
import { type CapsuleListEntry, defaultCapsulesDir } from "./capsule-cli.js";
|
|
289
|
+
import { isCapsuleImportArchiveInputError, validateCapsuleImportArchive } from "./capsule-import-gate.js";
|
|
291
290
|
import type { SemanticDedupLookup } from "./dedup/semantic.js";
|
|
292
291
|
import type { FallbackLlmClient } from "./fallback-llm.js";
|
|
293
292
|
import type { LocalLlmClient } from "./local-llm.js";
|
|
@@ -5366,6 +5365,9 @@ export class EngramAccessService extends SupportPassportAccessServiceBase {
|
|
|
5366
5365
|
* `versioning` defaults to the orchestrator's page-versioning config so
|
|
5367
5366
|
* `mode: "overwrite"` automatically snapshots prior content without the
|
|
5368
5367
|
* caller having to construct the config object.
|
|
5368
|
+
*
|
|
5369
|
+
* Remote callers (HTTP and MCP) may only import from the destination
|
|
5370
|
+
* namespace's capsule store; the CLI keeps unrestricted local paths.
|
|
5369
5371
|
*/
|
|
5370
5372
|
async capsuleImport(
|
|
5371
5373
|
opts: Omit<ImportCapsuleOptions, "root" | "memoryDir"> & {
|
|
@@ -5385,59 +5387,18 @@ export class EngramAccessService extends SupportPassportAccessServiceBase {
|
|
|
5385
5387
|
maxVersionsPerPage: this.orchestrator.config.versioningMaxPerPage,
|
|
5386
5388
|
sidecarDir: this.orchestrator.config.versioningSidecarDir,
|
|
5387
5389
|
};
|
|
5388
|
-
await
|
|
5390
|
+
await validateCapsuleImportArchive(importOptions.archivePath, storage.dir);
|
|
5389
5391
|
try {
|
|
5390
5392
|
return await importCapsuleFn({ ...importOptions, root, memoryDir, versioning });
|
|
5391
5393
|
} catch (err) {
|
|
5392
5394
|
const message = err instanceof Error ? err.message : String(err);
|
|
5393
|
-
if (
|
|
5395
|
+
if (isCapsuleImportArchiveInputError(err, message)) {
|
|
5394
5396
|
throw new EngramAccessInputError(`capsule import failed: ${message}`);
|
|
5395
5397
|
}
|
|
5396
5398
|
throw err;
|
|
5397
5399
|
}
|
|
5398
5400
|
}
|
|
5399
5401
|
|
|
5400
|
-
private async validateCapsuleImportArchivePath(archivePath: string): Promise<void> {
|
|
5401
|
-
let archiveStat;
|
|
5402
|
-
try {
|
|
5403
|
-
archiveStat = await stat(archivePath);
|
|
5404
|
-
} catch (err) {
|
|
5405
|
-
if (!this.isCapsuleImportPathInputFsError(err)) throw err;
|
|
5406
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
5407
|
-
throw new EngramAccessInputError(`capsule import failed: archive is not readable: ${message}`);
|
|
5408
|
-
}
|
|
5409
|
-
if (!archiveStat.isFile()) {
|
|
5410
|
-
throw new EngramAccessInputError("capsule import failed: archivePath must point to a file");
|
|
5411
|
-
}
|
|
5412
|
-
try {
|
|
5413
|
-
await nodeFs.access(archivePath, fsConstants.R_OK);
|
|
5414
|
-
} catch (err) {
|
|
5415
|
-
if (!this.isCapsuleImportPathInputFsError(err)) throw err;
|
|
5416
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
5417
|
-
throw new EngramAccessInputError(`capsule import failed: archive is not readable: ${message}`);
|
|
5418
|
-
}
|
|
5419
|
-
}
|
|
5420
|
-
|
|
5421
|
-
private isCapsuleImportPathInputFsError(err: unknown): boolean {
|
|
5422
|
-
const code =
|
|
5423
|
-
typeof err === "object" && err !== null && "code" in err ? (err as { code?: unknown }).code : undefined;
|
|
5424
|
-
return code === "ENOENT" || code === "ENOTDIR" || code === "EACCES" || code === "EPERM" || code === "ELOOP";
|
|
5425
|
-
}
|
|
5426
|
-
|
|
5427
|
-
private isCapsuleImportArchiveInputError(err: unknown, message: string): boolean {
|
|
5428
|
-
if (err instanceof ZodError) return true;
|
|
5429
|
-
const code =
|
|
5430
|
-
typeof err === "object" && err !== null && "code" in err ? (err as { code?: unknown }).code : undefined;
|
|
5431
|
-
if (typeof code === "string" && code.startsWith("Z_")) return true;
|
|
5432
|
-
return (
|
|
5433
|
-
message.startsWith("importCapsule: archive") ||
|
|
5434
|
-
message.startsWith("importCapsule: bundle") ||
|
|
5435
|
-
message.startsWith("importCapsule: manifest") ||
|
|
5436
|
-
message.startsWith("importCapsule: record") ||
|
|
5437
|
-
/incorrect header check|invalid stored block lengths|not in gzip format|unexpected end of file/i.test(message)
|
|
5438
|
-
);
|
|
5439
|
-
}
|
|
5440
|
-
|
|
5441
5402
|
/**
|
|
5442
5403
|
* Export a capsule archive from the orchestrator's memory directory.
|
|
5443
5404
|
*
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
import assert from "node:assert/strict";
|
|
12
12
|
import test from "node:test";
|
|
13
13
|
|
|
14
|
+
import { hostname } from "node:os";
|
|
14
15
|
import {
|
|
15
16
|
BacklogScheduler,
|
|
16
17
|
type BacklogSchedulerDeps,
|
|
@@ -23,10 +24,12 @@ import { Orchestrator } from "./orchestrator.js";
|
|
|
23
24
|
import {
|
|
24
25
|
cleanupDir,
|
|
25
26
|
makeHarmonicLifecycleConfig,
|
|
27
|
+
memoryFilesContaining,
|
|
26
28
|
mkTempMemoryDir,
|
|
27
29
|
singleFactResult,
|
|
28
30
|
stubExtraction,
|
|
29
31
|
} from "./testing/orchestrator-lite.js";
|
|
32
|
+
import type { DrainLease } from "./storage/buffer-drain-claims.js";
|
|
30
33
|
import type { BufferTurn } from "./types.js";
|
|
31
34
|
|
|
32
35
|
interface StubState {
|
|
@@ -79,10 +82,10 @@ function makeDeps(state: StubState, queue: ExtractionQueueCoordinator): BacklogS
|
|
|
79
82
|
},
|
|
80
83
|
},
|
|
81
84
|
liveBufferCounts: () => state.counts,
|
|
82
|
-
lease: () => ({
|
|
83
|
-
ownerId
|
|
85
|
+
lease: (ownerId) => ({
|
|
86
|
+
ownerId,
|
|
84
87
|
pid: process.pid,
|
|
85
|
-
host:
|
|
88
|
+
host: hostname(),
|
|
86
89
|
acquiredAt: new Date().toISOString(),
|
|
87
90
|
heartbeatAt: new Date().toISOString(),
|
|
88
91
|
leaseMs: 60_000,
|
|
@@ -113,6 +116,50 @@ function turn(content: string, sessionKey: string, timestamp = "2026-11-01T00:00
|
|
|
113
116
|
return { role: "user", content, timestamp, sessionKey };
|
|
114
117
|
}
|
|
115
118
|
|
|
119
|
+
test("scheduler instances use distinct lease tokens through claim and drain", async () => {
|
|
120
|
+
const issuedOwners: string[] = [];
|
|
121
|
+
const drainedOwners: string[] = [];
|
|
122
|
+
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
123
|
+
const key = `token-session-${attempt}`;
|
|
124
|
+
const queue = new ExtractionQueueCoordinator(1);
|
|
125
|
+
const state: StubState = {
|
|
126
|
+
counts: new Map([[key, { turns: 1, retainedTurns: 0 }]]),
|
|
127
|
+
claimed: new Set(),
|
|
128
|
+
claimTurns: new Map(),
|
|
129
|
+
claimedThisRun: new Set(),
|
|
130
|
+
outcomes: [emptyOutcome(key)],
|
|
131
|
+
drained: [],
|
|
132
|
+
};
|
|
133
|
+
const deps = makeDeps(state, queue);
|
|
134
|
+
deps.lease = (ownerId) => {
|
|
135
|
+
issuedOwners.push(ownerId);
|
|
136
|
+
return {
|
|
137
|
+
ownerId,
|
|
138
|
+
pid: process.pid,
|
|
139
|
+
host: hostname(),
|
|
140
|
+
acquiredAt: new Date().toISOString(),
|
|
141
|
+
heartbeatAt: new Date().toISOString(),
|
|
142
|
+
leaseMs: 60_000,
|
|
143
|
+
};
|
|
144
|
+
};
|
|
145
|
+
deps.drainKey = async (_drainKey, drainOptions) => {
|
|
146
|
+
drainedOwners.push(drainOptions.ownerId);
|
|
147
|
+
return emptyOutcome(key);
|
|
148
|
+
};
|
|
149
|
+
const scheduler = new BacklogScheduler(deps, { ownerId: "shared-label", pollMs: 3_600_000 });
|
|
150
|
+
scheduler.start();
|
|
151
|
+
try {
|
|
152
|
+
await scheduler.tickForTest();
|
|
153
|
+
await scheduler.settleForTest();
|
|
154
|
+
} finally {
|
|
155
|
+
await scheduler.stop();
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
assert.notEqual(issuedOwners[0], issuedOwners[1]);
|
|
160
|
+
assert.deepEqual([...new Set(drainedOwners)], [...new Set(issuedOwners)]);
|
|
161
|
+
});
|
|
162
|
+
|
|
116
163
|
test("startup drains existing claims and newly buffered sessions within the queue's concurrency", async () => {
|
|
117
164
|
const queue = new ExtractionQueueCoordinator(2);
|
|
118
165
|
const state: StubState = {
|
|
@@ -464,6 +511,399 @@ test("a wired scheduler drains real backlog and resumes after tombstone-consumin
|
|
|
464
511
|
}
|
|
465
512
|
});
|
|
466
513
|
|
|
514
|
+
// ── Real wiring: full cap cycle (fill → refuse → drain → admit) ─────────────
|
|
515
|
+
|
|
516
|
+
test("a full session cap admits a new durable session after the backlog drains", async () => {
|
|
517
|
+
// Real production SmartBuffer admission, real backlog scheduler/drain and
|
|
518
|
+
// the real persistence path (session files → drain claims → memory files).
|
|
519
|
+
// The extraction MODEL is a test double: it records the exact turn slice it
|
|
520
|
+
// is asked to extract and derives one fact from the first turn's content —
|
|
521
|
+
// every durability, admission and clearing decision below is production code.
|
|
522
|
+
const memoryDir = await mkTempMemoryDir("backlog-cap-cycle");
|
|
523
|
+
let orchestrator: Orchestrator | undefined;
|
|
524
|
+
try {
|
|
525
|
+
orchestrator = new Orchestrator(
|
|
526
|
+
makeHarmonicLifecycleConfig(memoryDir, {
|
|
527
|
+
extractionMinChars: 0,
|
|
528
|
+
extractionMinUserTurns: 1,
|
|
529
|
+
bufferMaxSessions: 4, // + the legacy `default` sentinel entry: 3 durable slots
|
|
530
|
+
})
|
|
531
|
+
);
|
|
532
|
+
const calls = stubExtraction(orchestrator, (turns) => singleFactResult(`cap-cycle fact for ${turns[0]?.content ?? "empty"}`));
|
|
533
|
+
const buffer = orchestrator.buffer;
|
|
534
|
+
const defaultNamespace = orchestrator.config.defaultNamespace;
|
|
535
|
+
|
|
536
|
+
// Mirrors production ingestion (turn-ingestion.ts): accepted captures are
|
|
537
|
+
// pinned BEFORE bounded admission, so the cap applies to real durable work.
|
|
538
|
+
const pinned = (key: string, content: string): BufferTurn => ({
|
|
539
|
+
...turn(content, key),
|
|
540
|
+
acceptedCapture: true,
|
|
541
|
+
writeNamespaceOverride: defaultNamespace,
|
|
542
|
+
});
|
|
543
|
+
const capture = async (key: string, content: string): Promise<void> => {
|
|
544
|
+
const outcome = await buffer.addTurnBounded(key, pinned(key, content));
|
|
545
|
+
assert.notEqual(outcome, null, `${key} admitted below the cap`);
|
|
546
|
+
await buffer.flushPendingSave({ throwOnFailure: true });
|
|
547
|
+
};
|
|
548
|
+
await capture("cap-1", "alpha one");
|
|
549
|
+
await capture("cap-2", "beta two");
|
|
550
|
+
await capture("cap-3", "gamma three");
|
|
551
|
+
|
|
552
|
+
// The cap genuinely bites: a fourth durable session is refused, never
|
|
553
|
+
// evicted (#3251).
|
|
554
|
+
assert.equal(
|
|
555
|
+
await orchestrator.buffer.addTurnBounded("cap-4", turn("delta four", "cap-4")),
|
|
556
|
+
null,
|
|
557
|
+
"a fourth session is refused at the cap"
|
|
558
|
+
);
|
|
559
|
+
|
|
560
|
+
const scheduler = wireBacklogScheduler(orchestrator, { pollMs: 3_600_000 });
|
|
561
|
+
try {
|
|
562
|
+
scheduler.start();
|
|
563
|
+
for (let attempt = 0; attempt < 6 && scheduler.snapshot().counters.processed < 3; attempt += 1) {
|
|
564
|
+
await scheduler.tickForTest();
|
|
565
|
+
await scheduler.settleForTest();
|
|
566
|
+
}
|
|
567
|
+
assert.equal(scheduler.snapshot().counters.processed, 3, "all three sessions drained");
|
|
568
|
+
assert.equal(scheduler.snapshot().counters.durableOutputs, 3, "one durable output per session");
|
|
569
|
+
assert.equal(calls.length, 3, "the model double saw one turn slice per session");
|
|
570
|
+
|
|
571
|
+
// Durable extraction kept the content: each session's fact is on disk.
|
|
572
|
+
for (const source of ["alpha one", "beta two", "gamma three"]) {
|
|
573
|
+
const hits = await memoryFilesContaining(memoryDir, `cap-cycle fact for ${source}`);
|
|
574
|
+
assert.ok(hits.length > 0, `content of the drained session was lost: ${source}`);
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
|
|
578
|
+
// The source turns are cleared from both the durable claim and the live
|
|
579
|
+
// SmartBuffer; the claim tombstones remain until a later owner save.
|
|
580
|
+
const claims = orchestrator.storage.bufferDrainClaims();
|
|
581
|
+
assert.deepEqual(
|
|
582
|
+
(await claims.list()).map((l) => l.turnCount).sort(),
|
|
583
|
+
[0, 0, 0],
|
|
584
|
+
"all drained claims are empty tombstones"
|
|
585
|
+
);
|
|
586
|
+
for (const key of ["cap-1", "cap-2", "cap-3"]) {
|
|
587
|
+
assert.deepEqual(orchestrator.buffer.getTurns(key), [], `${key} stale source turns cleared in memory`);
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
const admitted = await orchestrator.buffer.addTurnBounded("cap-4", pinned("cap-4", "delta four"));
|
|
591
|
+
assert.notEqual(admitted, null, "a fully drained backlog frees cap slots for a new session");
|
|
592
|
+
} finally {
|
|
593
|
+
await scheduler.stop();
|
|
594
|
+
}
|
|
595
|
+
} finally {
|
|
596
|
+
await orchestrator?.destroy().catch(() => undefined);
|
|
597
|
+
await cleanupDir(memoryDir);
|
|
598
|
+
}
|
|
599
|
+
});
|
|
600
|
+
|
|
601
|
+
|
|
602
|
+
test("same-key accepted ingress during extraction survives owner-fenced reconciliation", async () => {
|
|
603
|
+
const memoryDir = await mkTempMemoryDir("backlog-drain-ingress-race");
|
|
604
|
+
let orchestrator: Orchestrator | undefined;
|
|
605
|
+
try {
|
|
606
|
+
orchestrator = new Orchestrator(
|
|
607
|
+
makeHarmonicLifecycleConfig(memoryDir, { extractionMinChars: 0, extractionMinUserTurns: 1 })
|
|
608
|
+
);
|
|
609
|
+
const buffer = orchestrator.buffer;
|
|
610
|
+
const namespace = orchestrator.config.defaultNamespace;
|
|
611
|
+
const key = "race-session";
|
|
612
|
+
const oldTurn: BufferTurn = {
|
|
613
|
+
...turn("original captured turn", key),
|
|
614
|
+
acceptedCapture: true,
|
|
615
|
+
writeNamespaceOverride: namespace,
|
|
616
|
+
};
|
|
617
|
+
const newerTurn: BufferTurn = {
|
|
618
|
+
...turn("new accepted turn during drain", key, "2026-11-02T00:00:00.000Z"),
|
|
619
|
+
acceptedCapture: true,
|
|
620
|
+
writeNamespaceOverride: namespace,
|
|
621
|
+
principalOverride: "principal-race",
|
|
622
|
+
};
|
|
623
|
+
const entered = Promise.withResolvers<void>();
|
|
624
|
+
const release = Promise.withResolvers<void>();
|
|
625
|
+
let firstExtraction = true;
|
|
626
|
+
const calls = stubExtraction(orchestrator, async (turns) => {
|
|
627
|
+
if (firstExtraction) {
|
|
628
|
+
firstExtraction = false;
|
|
629
|
+
entered.resolve();
|
|
630
|
+
await release.promise;
|
|
631
|
+
}
|
|
632
|
+
return singleFactResult(`race fact for ${turns[0]?.content ?? "empty"}`);
|
|
633
|
+
});
|
|
634
|
+
|
|
635
|
+
assert.notEqual(await buffer.addTurnBounded(key, oldTurn), null);
|
|
636
|
+
await buffer.flushPendingSave({ throwOnFailure: true });
|
|
637
|
+
let now = 0;
|
|
638
|
+
const scheduler = wireBacklogScheduler(orchestrator, {
|
|
639
|
+
ownerId: "race-owner",
|
|
640
|
+
pollMs: 3_600_000,
|
|
641
|
+
retryBaseMs: 1,
|
|
642
|
+
retryMaxMs: 1,
|
|
643
|
+
now: () => now,
|
|
644
|
+
});
|
|
645
|
+
try {
|
|
646
|
+
scheduler.start();
|
|
647
|
+
await entered.promise;
|
|
648
|
+
assert.equal(
|
|
649
|
+
await orchestrator.storage.bufferDrainClaims().drainedTurnsForOwner(key, "not-race-owner"),
|
|
650
|
+
null,
|
|
651
|
+
"another owner cannot authorize live-buffer reconciliation"
|
|
652
|
+
);
|
|
653
|
+
|
|
654
|
+
// Live ingress saves into the owned claim while the model is still
|
|
655
|
+
// extracting its original snapshot.
|
|
656
|
+
assert.notEqual(await buffer.addTurnBounded(key, newerTurn), null);
|
|
657
|
+
await buffer.flushPendingSave({ throwOnFailure: true });
|
|
658
|
+
release.resolve();
|
|
659
|
+
await scheduler.settleForTest();
|
|
660
|
+
|
|
661
|
+
const pending = await orchestrator.storage.bufferDrainClaims().load(key);
|
|
662
|
+
assert.deepEqual(
|
|
663
|
+
pending?.entry.turns.map((item) => item.content),
|
|
664
|
+
["new accepted turn during drain"],
|
|
665
|
+
"the extracted source is removed while newer accepted ingress remains durable"
|
|
666
|
+
);
|
|
667
|
+
assert.equal(pending?.entry.turns[0]?.principalOverride, "principal-race");
|
|
668
|
+
assert.equal(pending?.entry.turns[0]?.writeNamespaceOverride, namespace);
|
|
669
|
+
assert.equal((await memoryFilesContaining(memoryDir, "race fact for original captured turn")).length, 1);
|
|
670
|
+
|
|
671
|
+
now = 2;
|
|
672
|
+
await scheduler.tickForTest();
|
|
673
|
+
await scheduler.settleForTest();
|
|
674
|
+
assert.equal(scheduler.snapshot().counters.processed, 1, "the newer turn also drains");
|
|
675
|
+
assert.deepEqual(calls.map((batch) => batch.map((item) => item.content)), [
|
|
676
|
+
["original captured turn"],
|
|
677
|
+
["new accepted turn during drain"],
|
|
678
|
+
]);
|
|
679
|
+
assert.equal(calls[1]?.[0]?.principalOverride, "principal-race");
|
|
680
|
+
assert.equal(calls[1]?.[0]?.writeNamespaceOverride, namespace);
|
|
681
|
+
assert.equal((await memoryFilesContaining(memoryDir, "race fact for original captured turn")).length, 1);
|
|
682
|
+
assert.equal((await memoryFilesContaining(memoryDir, "race fact for new accepted turn during drain")).length, 1);
|
|
683
|
+
assert.equal(scheduler.snapshot().counters.durableOutputs, 1);
|
|
684
|
+
} finally {
|
|
685
|
+
release.resolve();
|
|
686
|
+
await scheduler.stop();
|
|
687
|
+
}
|
|
688
|
+
} finally {
|
|
689
|
+
await orchestrator?.destroy().catch(() => undefined);
|
|
690
|
+
await cleanupDir(memoryDir);
|
|
691
|
+
}
|
|
692
|
+
});
|
|
693
|
+
|
|
694
|
+
|
|
695
|
+
|
|
696
|
+
test("a drain tombstones every identical accepted occurrence before live-buffer reconciliation", async () => {
|
|
697
|
+
const memoryDir = await mkTempMemoryDir("buffer-reconcile-identical-turns");
|
|
698
|
+
let orchestrator: Orchestrator | undefined;
|
|
699
|
+
try {
|
|
700
|
+
orchestrator = new Orchestrator(makeHarmonicLifecycleConfig(memoryDir));
|
|
701
|
+
const buffer = orchestrator.buffer;
|
|
702
|
+
const key = "duplicate-session";
|
|
703
|
+
const duplicate = turn("identical accepted occurrence", key);
|
|
704
|
+
const newer = {
|
|
705
|
+
...turn("new live turn after duplicate drain", key, "2026-11-05T00:00:00.000Z"),
|
|
706
|
+
acceptedCapture: true,
|
|
707
|
+
writeNamespaceOverride: orchestrator.config.defaultNamespace,
|
|
708
|
+
};
|
|
709
|
+
const claims = orchestrator.storage.bufferDrainClaims();
|
|
710
|
+
const now = new Date().toISOString();
|
|
711
|
+
const lease: DrainLease = {
|
|
712
|
+
ownerId: "duplicate-owner",
|
|
713
|
+
pid: process.pid,
|
|
714
|
+
host: hostname(),
|
|
715
|
+
acquiredAt: now,
|
|
716
|
+
heartbeatAt: now,
|
|
717
|
+
leaseMs: 60_000,
|
|
718
|
+
};
|
|
719
|
+
|
|
720
|
+
await buffer.addTurn(key, duplicate);
|
|
721
|
+
await buffer.addTurn(key, duplicate);
|
|
722
|
+
await buffer.flushPendingSave({ throwOnFailure: true });
|
|
723
|
+
assert.deepEqual(buffer.getTurns(key), [duplicate, duplicate]);
|
|
724
|
+
assert.equal(await claims.claimFromSessionFile(key, lease), "claimed");
|
|
725
|
+
const claimed = await claims.load(key);
|
|
726
|
+
assert.ok(claimed);
|
|
727
|
+
assert.deepEqual(claimed.entry.turns, [duplicate, duplicate]);
|
|
728
|
+
|
|
729
|
+
await claims.writeEntry(key, lease.ownerId, { turns: [], lastExtractionAt: null, extractionCount: 0 }, claimed.entry);
|
|
730
|
+
assert.deepEqual(await claims.drainedTurns(key), { turns: [duplicate, duplicate], retainedTurns: [] });
|
|
731
|
+
assert.equal(await claims.ack(key, lease.ownerId), "acked");
|
|
732
|
+
|
|
733
|
+
assert.notEqual(await buffer.addTurnBounded(key, newer), null);
|
|
734
|
+
await buffer.flushPendingSave({ throwOnFailure: true });
|
|
735
|
+
assert.deepEqual(buffer.getTurns(key), [newer]);
|
|
736
|
+
assert.deepEqual((await orchestrator.storage.loadBuffer()).entries?.[key]?.turns, [newer]);
|
|
737
|
+
} finally {
|
|
738
|
+
await orchestrator?.destroy().catch(() => undefined);
|
|
739
|
+
await cleanupDir(memoryDir);
|
|
740
|
+
}
|
|
741
|
+
});
|
|
742
|
+
|
|
743
|
+
test("ambiguous identical active and retained turns remain pending after cross-list progress", async () => {
|
|
744
|
+
const memoryDir = await mkTempMemoryDir("buffer-reconcile-cross-list-duplicates");
|
|
745
|
+
let orchestrator: Orchestrator | undefined;
|
|
746
|
+
try {
|
|
747
|
+
orchestrator = new Orchestrator(makeHarmonicLifecycleConfig(memoryDir));
|
|
748
|
+
const buffer = orchestrator.buffer;
|
|
749
|
+
const claims = orchestrator.storage.bufferDrainClaims();
|
|
750
|
+
const key = "cross-list-duplicates";
|
|
751
|
+
const same = turn("same occurrence identity in both lists", key);
|
|
752
|
+
const now = new Date().toISOString();
|
|
753
|
+
const lease: DrainLease = {
|
|
754
|
+
ownerId: "cross-list-owner",
|
|
755
|
+
pid: process.pid,
|
|
756
|
+
host: hostname(),
|
|
757
|
+
acquiredAt: now,
|
|
758
|
+
heartbeatAt: now,
|
|
759
|
+
leaseMs: 60_000,
|
|
760
|
+
};
|
|
761
|
+
|
|
762
|
+
await buffer.addTurn(key, same);
|
|
763
|
+
await buffer.retainDeferredTurns(key, [same], 10, [same]);
|
|
764
|
+
await buffer.flushPendingSave({ throwOnFailure: true });
|
|
765
|
+
assert.deepEqual(buffer.getTurns(key), [same, same]);
|
|
766
|
+
assert.equal(await claims.claimFromSessionFile(key, lease), "claimed");
|
|
767
|
+
const claim = await claims.load(key);
|
|
768
|
+
assert.ok(claim);
|
|
769
|
+
assert.deepEqual(claim.entry.turns, [same]);
|
|
770
|
+
assert.deepEqual(claim.entry.retainedTurns, [same]);
|
|
771
|
+
|
|
772
|
+
// One indistinguishable copy survives in the opposite list. Progress
|
|
773
|
+
// alone cannot prove whether that is the active or retained occurrence.
|
|
774
|
+
await claims.writeEntry(
|
|
775
|
+
key,
|
|
776
|
+
lease.ownerId,
|
|
777
|
+
{ turns: [], retainedTurns: [same], lastExtractionAt: null, extractionCount: 0 },
|
|
778
|
+
claim.entry,
|
|
779
|
+
);
|
|
780
|
+
const drained = await claims.drainedTurnsForOwner(key, lease.ownerId);
|
|
781
|
+
assert.deepEqual(drained, { turns: [], retainedTurns: [] });
|
|
782
|
+
assert.deepEqual(
|
|
783
|
+
(await claims.load(key))?.entry,
|
|
784
|
+
{ turns: [same], retainedTurns: [same], lastExtractionAt: null, extractionCount: 0 },
|
|
785
|
+
"both original source-list occurrences remain pending when provenance is ambiguous",
|
|
786
|
+
);
|
|
787
|
+
assert.ok(drained);
|
|
788
|
+
await buffer.save({ key, drained });
|
|
789
|
+
assert.deepEqual(buffer.getTurns(key), [same, same]);
|
|
790
|
+
} finally {
|
|
791
|
+
await orchestrator?.destroy().catch(() => undefined);
|
|
792
|
+
await cleanupDir(memoryDir);
|
|
793
|
+
}
|
|
794
|
+
});
|
|
795
|
+
|
|
796
|
+
test("claim tombstones clear identical active and retained copies on live save", async () => {
|
|
797
|
+
const memoryDir = await mkTempMemoryDir("buffer-reconcile-retained-provenance");
|
|
798
|
+
let orchestrator: Orchestrator | undefined;
|
|
799
|
+
try {
|
|
800
|
+
orchestrator = new Orchestrator(makeHarmonicLifecycleConfig(memoryDir));
|
|
801
|
+
const buffer = orchestrator.buffer;
|
|
802
|
+
const key = "retained-session";
|
|
803
|
+
const source = turn("same source identity", key);
|
|
804
|
+
const newer = {
|
|
805
|
+
...turn("new live turn", key, "2026-11-04T00:00:00.000Z"),
|
|
806
|
+
acceptedCapture: true,
|
|
807
|
+
writeNamespaceOverride: orchestrator.config.defaultNamespace,
|
|
808
|
+
};
|
|
809
|
+
const claims = orchestrator.storage.bufferDrainClaims();
|
|
810
|
+
const now = new Date().toISOString();
|
|
811
|
+
const lease: DrainLease = {
|
|
812
|
+
ownerId: "retained-owner",
|
|
813
|
+
pid: process.pid,
|
|
814
|
+
host: hostname(),
|
|
815
|
+
acquiredAt: now,
|
|
816
|
+
heartbeatAt: now,
|
|
817
|
+
leaseMs: 60_000,
|
|
818
|
+
};
|
|
819
|
+
|
|
820
|
+
await buffer.addTurn(key, source);
|
|
821
|
+
await buffer.retainDeferredTurns(key, [source]);
|
|
822
|
+
assert.deepEqual(buffer.getTurns(key), [source, source]);
|
|
823
|
+
assert.equal(await claims.claimFromSessionFile(key, lease), "claimed");
|
|
824
|
+
const claimed = await claims.load(key);
|
|
825
|
+
assert.ok(claimed);
|
|
826
|
+
assert.deepEqual(claimed.entry, {
|
|
827
|
+
turns: [source],
|
|
828
|
+
retainedTurns: [source],
|
|
829
|
+
lastExtractionAt: null,
|
|
830
|
+
extractionCount: 0,
|
|
831
|
+
});
|
|
832
|
+
await claims.writeEntry(key, lease.ownerId, { turns: [], lastExtractionAt: null, extractionCount: 0 }, claimed.entry);
|
|
833
|
+
assert.deepEqual(await claims.drainedTurns(key), { turns: [source], retainedTurns: [source] });
|
|
834
|
+
assert.equal(await claims.ack(key, lease.ownerId), "acked");
|
|
835
|
+
|
|
836
|
+
// A new same-key save consumes the tombstone through BufferSessionFiles.
|
|
837
|
+
assert.notEqual(await buffer.addTurnBounded(key, newer), null);
|
|
838
|
+
await buffer.flushPendingSave({ throwOnFailure: true });
|
|
839
|
+
assert.deepEqual(buffer.getTurns(key), [newer]);
|
|
840
|
+
assert.deepEqual(buffer.getRetainedDeferredTurns(key), []);
|
|
841
|
+
const persisted = await orchestrator.storage.loadBuffer();
|
|
842
|
+
assert.deepEqual(persisted.entries?.[key]?.turns, [newer]);
|
|
843
|
+
assert.equal(persisted.entries?.[key]?.retainedTurns, undefined);
|
|
844
|
+
} finally {
|
|
845
|
+
await orchestrator?.destroy().catch(() => undefined);
|
|
846
|
+
await cleanupDir(memoryDir);
|
|
847
|
+
}
|
|
848
|
+
});
|
|
849
|
+
test("a lease takeover after the drained snapshot cannot erase newer scoped ingress", async () => {
|
|
850
|
+
const memoryDir = await mkTempMemoryDir("backlog-takeover-reconcile");
|
|
851
|
+
let orchestrator: Orchestrator | undefined;
|
|
852
|
+
try {
|
|
853
|
+
orchestrator = new Orchestrator(makeHarmonicLifecycleConfig(memoryDir));
|
|
854
|
+
const buffer = orchestrator.buffer;
|
|
855
|
+
const claims = orchestrator.storage.bufferDrainClaims();
|
|
856
|
+
const key = "takeover-session";
|
|
857
|
+
const oldTurn: BufferTurn = {
|
|
858
|
+
...turn("old drained turn", key),
|
|
859
|
+
acceptedCapture: true,
|
|
860
|
+
writeNamespaceOverride: orchestrator.config.defaultNamespace,
|
|
861
|
+
};
|
|
862
|
+
const newerTurn: BufferTurn = {
|
|
863
|
+
...turn("new accepted turn", key, "2026-11-03T00:00:00.000Z"),
|
|
864
|
+
acceptedCapture: true,
|
|
865
|
+
writeNamespaceOverride: orchestrator.config.defaultNamespace,
|
|
866
|
+
principalOverride: "new-owner-principal",
|
|
867
|
+
};
|
|
868
|
+
const lease = (ownerId: string): DrainLease => {
|
|
869
|
+
const now = new Date().toISOString();
|
|
870
|
+
return { ownerId, pid: process.pid, host: hostname(), acquiredAt: now, heartbeatAt: now, leaseMs: 60_000 };
|
|
871
|
+
};
|
|
872
|
+
|
|
873
|
+
assert.notEqual(await buffer.addTurnBounded(key, oldTurn), null);
|
|
874
|
+
await buffer.flushPendingSave({ throwOnFailure: true });
|
|
875
|
+
assert.equal(await claims.claimFromSessionFile(key, lease("owner-one")), "claimed");
|
|
876
|
+
const claimed = await claims.load(key);
|
|
877
|
+
assert.ok(claimed);
|
|
878
|
+
await claims.writeEntry(
|
|
879
|
+
key,
|
|
880
|
+
"owner-one",
|
|
881
|
+
{ turns: [], lastExtractionAt: null, extractionCount: 0 },
|
|
882
|
+
claimed.entry
|
|
883
|
+
);
|
|
884
|
+
const ownerOneSnapshot = await claims.drainedTurnsForOwner(key, "owner-one");
|
|
885
|
+
assert.deepEqual(ownerOneSnapshot, { turns: [oldTurn], retainedTurns: [] });
|
|
886
|
+
assert.ok(ownerOneSnapshot);
|
|
887
|
+
|
|
888
|
+
// Simulate takeover after the old owner's fenced read but before its
|
|
889
|
+
// serialized live-buffer mutation. Progress is monotonic and identities
|
|
890
|
+
// are list-specific, so applying this snapshot cannot erase newer work.
|
|
891
|
+
await claims.releaseLease(key, "owner-one");
|
|
892
|
+
assert.equal(await claims.adopt(key, lease("owner-two")), "adopted");
|
|
893
|
+
assert.notEqual(await buffer.addTurnBounded(key, newerTurn), null);
|
|
894
|
+
await buffer.flushPendingSave({ throwOnFailure: true });
|
|
895
|
+
await buffer.save({ key, drained: ownerOneSnapshot });
|
|
896
|
+
assert.deepEqual(buffer.getTurns(key), [], "the old in-memory entry remains handed to the claim");
|
|
897
|
+
|
|
898
|
+
const afterIngress = await claims.load(key);
|
|
899
|
+
assert.deepEqual(afterIngress?.entry.turns, [newerTurn]);
|
|
900
|
+
assert.equal(afterIngress?.entry.turns[0]?.principalOverride, "new-owner-principal");
|
|
901
|
+
assert.equal(afterIngress?.entry.turns[0]?.writeNamespaceOverride, orchestrator.config.defaultNamespace);
|
|
902
|
+
} finally {
|
|
903
|
+
await orchestrator?.destroy().catch(() => undefined);
|
|
904
|
+
await cleanupDir(memoryDir);
|
|
905
|
+
}
|
|
906
|
+
});
|
|
467
907
|
test("a claim decision unsettled mid-rename keeps the key reserved until it settles", async () => {
|
|
468
908
|
const queue = new ExtractionQueueCoordinator(1);
|
|
469
909
|
const state: StubState = {
|
package/src/briefing-deadline.ts
CHANGED
|
@@ -3,20 +3,21 @@ import { isAbortError, raceAbort } from "./abort-error.js";
|
|
|
3
3
|
export const BRIEFING_DEADLINE_MS = 30_000;
|
|
4
4
|
|
|
5
5
|
export class BriefingTimeoutError extends Error {
|
|
6
|
-
constructor(readonly deadlineMs: number) {
|
|
7
|
-
super(
|
|
6
|
+
constructor(readonly deadlineMs: number, label = "briefing") {
|
|
7
|
+
super(`${label} timed out after ${deadlineMs}ms`);
|
|
8
8
|
this.name = "TimeoutError";
|
|
9
9
|
}
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
export async function withBriefingDeadline<T>(
|
|
13
13
|
run: (signal: AbortSignal) => Promise<T>,
|
|
14
|
-
abortSignal?: AbortSignal
|
|
14
|
+
abortSignal?: AbortSignal,
|
|
15
|
+
label = "briefing",
|
|
15
16
|
): Promise<T> {
|
|
16
17
|
const controller = new AbortController();
|
|
17
18
|
const signal = abortSignal ? AbortSignal.any([abortSignal, controller.signal]) : controller.signal;
|
|
18
19
|
const timer = setTimeout(
|
|
19
|
-
() => controller.abort(new BriefingTimeoutError(BRIEFING_DEADLINE_MS)),
|
|
20
|
+
() => controller.abort(new BriefingTimeoutError(BRIEFING_DEADLINE_MS, label)),
|
|
20
21
|
BRIEFING_DEADLINE_MS
|
|
21
22
|
);
|
|
22
23
|
try {
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/** Buffer session-entry admission: refusal errors and the live-admission probe. */
|
|
2
|
+
|
|
3
|
+
import { selectEvictedBufferKeys, type BufferEvictionHooks } from "./buffer-eviction.js";
|
|
4
|
+
import type { BufferEntryState } from "./types.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Raised when a NEW live-capture session cannot be admitted because the
|
|
8
|
+
* session-entry cap is full of non-evictable backlog. The turn was never
|
|
9
|
+
* accepted; the request is retryable once extraction drains an entry.
|
|
10
|
+
*/
|
|
11
|
+
export class BufferAdmissionRefusedError extends Error {
|
|
12
|
+
constructor(bufferKey: string, maxSessionEntries: number) {
|
|
13
|
+
super(
|
|
14
|
+
`buffer[${bufferKey}]: live capture refused at the session-entry cap (${maxSessionEntries}); ` +
|
|
15
|
+
"buffered backlog is no longer evicted to admit new sessions. Retry after extraction frees an entry",
|
|
16
|
+
);
|
|
17
|
+
this.name = "BufferAdmissionRefusedError";
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Durable observe capture refused at the entry cap; the request is retryable. */
|
|
22
|
+
export class ReplayAdmissionRefusedError extends Error {
|
|
23
|
+
constructor(message: string) {
|
|
24
|
+
super(message);
|
|
25
|
+
this.name = "ReplayAdmissionRefusedError";
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Live-admission bound (#1908 policy, backlog-safe): a NEW session enters
|
|
31
|
+
* only while under the cap or when an EMPTY entry can be reclaimed. Buffered
|
|
32
|
+
* backlog is never dropped for a live capture; the caller refuses instead.
|
|
33
|
+
*/
|
|
34
|
+
export function canAdmitNewEntryUnlocked(
|
|
35
|
+
entries: Readonly<Record<string, BufferEntryState>>,
|
|
36
|
+
bufferKey: string,
|
|
37
|
+
maxSessionEntries: number,
|
|
38
|
+
hooks: BufferEvictionHooks,
|
|
39
|
+
): boolean {
|
|
40
|
+
const count = Object.keys(entries).length;
|
|
41
|
+
if (count < maxSessionEntries) return true;
|
|
42
|
+
// Probe post-admission (as if this key had landed): admission is safe
|
|
43
|
+
// exactly when an EMPTY entry could then be reclaimed.
|
|
44
|
+
return selectEvictedBufferKeys(entries, [bufferKey], Math.max(0, count - 1), hooks).length > 0;
|
|
45
|
+
}
|