@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,211 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { SmartBuffer, BufferAdmissionRefusedError } from "./buffer.js";
|
|
4
|
+
import { parseConfig } from "./config.js";
|
|
5
|
+
import { log } from "./logger.js";
|
|
6
|
+
import { TurnIngestionCoordinator } from "./orchestration/turn-ingestion.js";
|
|
7
|
+
import type { BufferEntryState, BufferState, BufferTurn } from "./types.js";
|
|
8
|
+
|
|
9
|
+
class FakeStorage {
|
|
10
|
+
public saved: BufferState | null = null;
|
|
11
|
+
public saveCount = 0;
|
|
12
|
+
|
|
13
|
+
constructor(private readonly initial: BufferState) {}
|
|
14
|
+
|
|
15
|
+
async loadBuffer(): Promise<BufferState> {
|
|
16
|
+
return structuredClone(this.initial);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async saveBuffer(state: BufferState): Promise<void> {
|
|
20
|
+
this.saveCount += 1;
|
|
21
|
+
this.saved = structuredClone(state);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function makeTurn(key: string, content: string, accepted = false): BufferTurn {
|
|
26
|
+
const turn: BufferTurn = {
|
|
27
|
+
role: "user",
|
|
28
|
+
content,
|
|
29
|
+
timestamp: new Date().toISOString(),
|
|
30
|
+
sessionKey: key,
|
|
31
|
+
};
|
|
32
|
+
if (accepted) turn.acceptedCapture = true;
|
|
33
|
+
return turn;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function entry(turns: BufferTurn[]): BufferEntryState {
|
|
37
|
+
return { turns, lastExtractionAt: null, extractionCount: 0 };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function emptyEntry(): BufferEntryState {
|
|
41
|
+
return { turns: [], lastExtractionAt: null, extractionCount: 0 };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** default + the given entries; default stays empty unless given. */
|
|
45
|
+
function seededState(entries: Record<string, BufferEntryState>): BufferState {
|
|
46
|
+
const defaultEntry = entries.default ?? emptyEntry();
|
|
47
|
+
return {
|
|
48
|
+
turns: defaultEntry.turns,
|
|
49
|
+
lastExtractionAt: defaultEntry.lastExtractionAt,
|
|
50
|
+
extractionCount: defaultEntry.extractionCount,
|
|
51
|
+
entries: { default: defaultEntry, ...entries },
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function makeBuffer(state: BufferState, maxSessions: number) {
|
|
56
|
+
const config = parseConfig({ bufferSaveDebounceMs: 0, bufferMaxSessions: maxSessions });
|
|
57
|
+
const storage = new FakeStorage(state);
|
|
58
|
+
const buffer = new SmartBuffer(config, storage);
|
|
59
|
+
return { buffer, storage };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const CAP = 4;
|
|
63
|
+
|
|
64
|
+
function savedKeys(storage: FakeStorage): string[] {
|
|
65
|
+
return Object.keys(storage.saved?.entries ?? {});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
test("live capture at a cap full of non-empty backlog is refused, never evicts buffered turns", async () => {
|
|
69
|
+
const { buffer } = makeBuffer(
|
|
70
|
+
seededState({
|
|
71
|
+
a: entry([makeTurn("a", "backlog a")]),
|
|
72
|
+
b: entry([makeTurn("b", "backlog b")]),
|
|
73
|
+
c: entry([makeTurn("c", "backlog c")]),
|
|
74
|
+
}),
|
|
75
|
+
CAP,
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
await assert.rejects(
|
|
79
|
+
buffer.addTurn("new-session", makeTurn("new-session", "live turn")),
|
|
80
|
+
BufferAdmissionRefusedError,
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
assert.deepEqual(buffer.getTurns("a").map((t) => t.content), ["backlog a"], "oldest backlog lost to live admission");
|
|
84
|
+
assert.deepEqual(buffer.getTurns("b").map((t) => t.content), ["backlog b"]);
|
|
85
|
+
assert.deepEqual(buffer.getTurns("c").map((t) => t.content), ["backlog c"]);
|
|
86
|
+
assert.equal(buffer.getTurns("new-session").length, 0, "refused turn was not buffered");
|
|
87
|
+
|
|
88
|
+
// Accepted captures are subject to the same no-backlog-loss limit: refusal
|
|
89
|
+
// happens before acceptance, preserving accepted-capture non-eviction.
|
|
90
|
+
assert.equal(
|
|
91
|
+
await buffer.addTurnBounded("accepted-1", makeTurn("accepted-1", "accepted", true)),
|
|
92
|
+
null,
|
|
93
|
+
"accepted capture is refused rather than evicting native backlog",
|
|
94
|
+
);
|
|
95
|
+
assert.deepEqual(buffer.getTurns("a").map((t) => t.content), ["backlog a"]);
|
|
96
|
+
assert.deepEqual(buffer.getTurns("b").map((t) => t.content), ["backlog b"]);
|
|
97
|
+
assert.deepEqual(buffer.getTurns("c").map((t) => t.content), ["backlog c"]);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
test("an already-admitted session keeps accepting turns at a full cap", async () => {
|
|
101
|
+
const { buffer, storage } = makeBuffer(
|
|
102
|
+
seededState({
|
|
103
|
+
a: entry([makeTurn("a", "backlog a")]),
|
|
104
|
+
b: entry([makeTurn("b", "backlog b")]),
|
|
105
|
+
c: entry([makeTurn("c", "backlog c")]),
|
|
106
|
+
}),
|
|
107
|
+
CAP,
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
const decision = await buffer.addTurn("b", makeTurn("b", "another b turn"));
|
|
111
|
+
assert.equal(decision, "keep_buffering");
|
|
112
|
+
assert.deepEqual(buffer.getTurns("b").map((t) => t.content), ["backlog b", "another b turn"]);
|
|
113
|
+
assert.deepEqual(buffer.getTurns("a").map((t) => t.content), ["backlog a"], "existing-session admission must not evict peers");
|
|
114
|
+
assert.deepEqual(buffer.getTurns("c").map((t) => t.content), ["backlog c"]);
|
|
115
|
+
assert.equal(savedKeys(storage).length, CAP, "entry count stays at the cap");
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
test("empty entries are still evicted first to admit a new live session", async () => {
|
|
119
|
+
const { buffer, storage } = makeBuffer(
|
|
120
|
+
seededState({
|
|
121
|
+
"empty-oldest": emptyEntry(),
|
|
122
|
+
n1: entry([makeTurn("n1", "native 1")]),
|
|
123
|
+
n2: entry([makeTurn("n2", "native 2")]),
|
|
124
|
+
}),
|
|
125
|
+
CAP,
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
const decision = await buffer.addTurn("new-session", makeTurn("new-session", "live turn"));
|
|
129
|
+
assert.equal(decision, "keep_buffering");
|
|
130
|
+
const keys = savedKeys(storage);
|
|
131
|
+
assert.ok(!keys.includes("empty-oldest"), "oldest empty entry reclaimed");
|
|
132
|
+
assert.ok(keys.includes("new-session"), "new session admitted");
|
|
133
|
+
assert.deepEqual(buffer.getTurns("n1").map((t) => t.content), ["native 1"], "empty eviction must not touch backlog");
|
|
134
|
+
assert.deepEqual(buffer.getTurns("n2").map((t) => t.content), ["native 2"]);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
test("a retained-only entry is non-evictable backlog: live admission is refused instead", async () => {
|
|
138
|
+
const retainedOnly: BufferEntryState = {
|
|
139
|
+
turns: [],
|
|
140
|
+
retainedTurns: [makeTurn("r", "deferred retry turn")],
|
|
141
|
+
lastExtractionAt: null,
|
|
142
|
+
extractionCount: 0,
|
|
143
|
+
};
|
|
144
|
+
const { buffer } = makeBuffer(
|
|
145
|
+
seededState({
|
|
146
|
+
r: retainedOnly,
|
|
147
|
+
n1: entry([makeTurn("n1", "native 1")]),
|
|
148
|
+
n2: entry([makeTurn("n2", "native 2")]),
|
|
149
|
+
}),
|
|
150
|
+
CAP,
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
await assert.rejects(
|
|
154
|
+
buffer.addTurn("new-session", makeTurn("new-session", "live turn")),
|
|
155
|
+
BufferAdmissionRefusedError,
|
|
156
|
+
);
|
|
157
|
+
assert.equal(
|
|
158
|
+
buffer.getRetainedDeferredTurns("r").length,
|
|
159
|
+
1,
|
|
160
|
+
"retained retry turns must not be evicted as 'empty'",
|
|
161
|
+
);
|
|
162
|
+
});
|
|
163
|
+
test("concurrent multi-session admissions reserve capacity without partial batches", async () => {
|
|
164
|
+
const { buffer } = makeBuffer(seededState({}), 2);
|
|
165
|
+
const results = await Promise.all([
|
|
166
|
+
buffer.addTurnsBounded([{ bufferKey: "batch-a", turns: [makeTurn("batch-a", "a")] }]),
|
|
167
|
+
buffer.addTurnsBounded([{ bufferKey: "batch-b", turns: [makeTurn("batch-b", "b")] }]),
|
|
168
|
+
]);
|
|
169
|
+
|
|
170
|
+
assert.equal(results.filter((result) => result !== null).length, 1);
|
|
171
|
+
assert.equal(results.filter((result) => result === null).length, 1);
|
|
172
|
+
assert.equal(buffer.getTurns("batch-a").length + buffer.getTurns("batch-b").length, 1);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
test("processTurn propagates retryable admission refusal without queueing extraction", async () => {
|
|
176
|
+
const { buffer } = makeBuffer(
|
|
177
|
+
seededState({
|
|
178
|
+
a: entry([makeTurn("a", "backlog a")]),
|
|
179
|
+
b: entry([makeTurn("b", "backlog b")]),
|
|
180
|
+
c: entry([makeTurn("c", "backlog c")]),
|
|
181
|
+
}),
|
|
182
|
+
CAP,
|
|
183
|
+
);
|
|
184
|
+
const config = parseConfig({
|
|
185
|
+
memoryDir: "/tmp/unused-buffer-eviction-backpressure",
|
|
186
|
+
bufferSaveDebounceMs: 0,
|
|
187
|
+
bufferMaxSessions: CAP,
|
|
188
|
+
});
|
|
189
|
+
const queued: BufferTurn[][] = [];
|
|
190
|
+
type CoordinatorDeps = ConstructorParameters<typeof TurnIngestionCoordinator>[0];
|
|
191
|
+
// SAFETY: this fixture exercises only admission + refusal; the remaining
|
|
192
|
+
// coordinator deps are never reached on this path.
|
|
193
|
+
const deps = Object.assign({} as CoordinatorDeps, {
|
|
194
|
+
buffer,
|
|
195
|
+
config,
|
|
196
|
+
queueBufferedExtraction: async (turns: BufferTurn[]) => {
|
|
197
|
+
queued.push(turns);
|
|
198
|
+
},
|
|
199
|
+
});
|
|
200
|
+
const coordinator = new TurnIngestionCoordinator(deps);
|
|
201
|
+
|
|
202
|
+
await assert.rejects(
|
|
203
|
+
coordinator.processTurn("user", "live turn", "new-session"),
|
|
204
|
+
BufferAdmissionRefusedError,
|
|
205
|
+
"caller must receive retryable refusal rather than successful acknowledgment",
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
assert.equal(queued.length, 0, "a refused turn must not queue extraction");
|
|
209
|
+
assert.equal(buffer.getTurns("new-session").length, 0, "a refused turn must not be buffered");
|
|
210
|
+
assert.deepEqual(buffer.getTurns("a").map((x) => x.content), ["backlog a"], "backlog dropped during refused admission");
|
|
211
|
+
});
|
package/src/buffer-eviction.ts
CHANGED
|
@@ -10,11 +10,10 @@ export interface BufferEvictionHooks {
|
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
|
-
* Pick
|
|
14
|
-
*
|
|
15
|
-
* empty entries
|
|
16
|
-
*
|
|
17
|
-
* entries holding an accepted capture are never chosen.
|
|
13
|
+
* Pick empty entries to evict so `entries` returns to `maxCount`. Backlog
|
|
14
|
+
* (active or retained turns) is never evicted; admission must refuse when
|
|
15
|
+
* empty entries cannot free a slot. "default", retained keys and entries
|
|
16
|
+
* holding an accepted capture are never chosen.
|
|
18
17
|
*/
|
|
19
18
|
export function selectEvictedBufferKeys(
|
|
20
19
|
entries: Readonly<Record<string, BufferEntryState>>,
|
|
@@ -35,17 +34,12 @@ export function selectEvictedBufferKeys(
|
|
|
35
34
|
const candidates = keys.filter(
|
|
36
35
|
(key) => key !== "default" && !retainKeys.includes(key) && !hooks.holdsAcceptedCapture(entries[key]),
|
|
37
36
|
);
|
|
37
|
+
// Backlog = active turns OR retained retry turns (#1908); only an entry
|
|
38
|
+
// holding neither counts as empty.
|
|
38
39
|
const emptyRemovable = candidates
|
|
39
|
-
.filter((key) => (entries[key]?.turns.length ?? 0) === 0)
|
|
40
|
+
.filter((key) => (entries[key]?.turns.length ?? 0) === 0 && (entries[key]?.retainedTurns?.length ?? 0) === 0)
|
|
40
41
|
.sort(byOldestActivity);
|
|
41
42
|
|
|
42
43
|
const removableCount = Math.max(0, keys.length - maxCount);
|
|
43
|
-
|
|
44
|
-
if (evicted.length < removableCount) {
|
|
45
|
-
const nonEmptyRemovable = candidates
|
|
46
|
-
.filter((key) => (entries[key]?.turns.length ?? 0) > 0)
|
|
47
|
-
.sort(byOldestActivity);
|
|
48
|
-
return evicted.concat(nonEmptyRemovable.slice(0, removableCount - evicted.length));
|
|
49
|
-
}
|
|
50
|
-
return evicted;
|
|
44
|
+
return emptyRemovable.slice(0, removableCount);
|
|
51
45
|
}
|
|
@@ -22,7 +22,7 @@ import { mkdtemp, rm } from "node:fs/promises";
|
|
|
22
22
|
import os from "node:os";
|
|
23
23
|
import path from "node:path";
|
|
24
24
|
|
|
25
|
-
import { SmartBuffer } from "./buffer.js";
|
|
25
|
+
import { SmartBuffer, BufferAdmissionRefusedError } from "./buffer.js";
|
|
26
26
|
import { parseConfig } from "./config.js";
|
|
27
27
|
import { StorageManager } from "./storage.js";
|
|
28
28
|
import type { BufferEntryState, BufferState, BufferTurn } from "./types.js";
|
|
@@ -161,34 +161,28 @@ test("clearAfterExtraction persists the cleared session state", async () => {
|
|
|
161
161
|
assert.equal(record?.entry.extractionCount, 1);
|
|
162
162
|
});
|
|
163
163
|
|
|
164
|
-
test("evicted sessions get their files deleted", async () => {
|
|
164
|
+
test("empty evicted sessions get their files deleted", async () => {
|
|
165
165
|
const storage = new IncrementalStorage(emptyState());
|
|
166
|
-
const buffer = new SmartBuffer(parseConfig({ triggerMode: "every_n", bufferMaxTurns: 100, bufferMaxSessions:
|
|
167
|
-
|
|
168
|
-
//
|
|
169
|
-
//
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
}
|
|
166
|
+
const buffer = new SmartBuffer(parseConfig({ triggerMode: "every_n", bufferMaxTurns: 100, bufferMaxSessions: 4 }), storage);
|
|
167
|
+
|
|
168
|
+
// Clear one persisted entry to make it genuinely empty. Accepted-capture
|
|
169
|
+
// admission may reclaim this slot, but never a session with buffered turns.
|
|
170
|
+
await buffer.addTurn("empty-old", makeTurn("empty-old", "will be extracted"));
|
|
171
|
+
await buffer.flushPendingSave();
|
|
172
|
+
await buffer.clearAfterExtraction("empty-old");
|
|
173
|
+
await buffer.addTurnBounded("native-1", makeTurn("native-1", "native 1"));
|
|
174
|
+
await buffer.addTurnBounded("native-2", makeTurn("native-2", "native 2"));
|
|
176
175
|
await buffer.flushPendingSave();
|
|
177
176
|
|
|
178
|
-
const
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
177
|
+
const accepted = await buffer.addTurnBounded("accepted", {
|
|
178
|
+
...makeTurn("accepted", "durable accepted"),
|
|
179
|
+
acceptedCapture: true,
|
|
180
|
+
});
|
|
181
|
+
assert.notEqual(accepted, null, "empty entry should be reclaimed to admit accepted capture");
|
|
183
182
|
await buffer.flushPendingSave();
|
|
184
|
-
assert.ok(storage.deleted.includes("
|
|
185
|
-
assert.
|
|
186
|
-
|
|
187
|
-
"capacity pressure evicted entries without deleting their session files",
|
|
188
|
-
);
|
|
189
|
-
for (const key of storage.deleted) {
|
|
190
|
-
assert.equal(storage.sessions.has(key), false, `evicted session ${key} still has a file record`);
|
|
191
|
-
}
|
|
183
|
+
assert.ok(storage.deleted.includes("empty-old"), "reclaimed empty entry's file was not deleted");
|
|
184
|
+
assert.equal(buffer.getTurns("native-1").length, 1);
|
|
185
|
+
assert.equal(buffer.getTurns("native-2").length, 1);
|
|
192
186
|
});
|
|
193
187
|
|
|
194
188
|
test("the default entry cap leaves headroom for live sessions during backlog drains (#3303)", async () => {
|
|
@@ -273,9 +267,7 @@ test("legacy-only storage keeps the whole-state saveBuffer behavior", async () =
|
|
|
273
267
|
assert.equal(storage.lastSaved?.entries?.["thread-b"]?.turns[0]?.content, "beta");
|
|
274
268
|
});
|
|
275
269
|
|
|
276
|
-
test("durable capture at the entry cap
|
|
277
|
-
// Stalled extraction left the cap full of native (non-accepted)
|
|
278
|
-
// sessions; addTurnBounded refused every new accepted session.
|
|
270
|
+
test("durable capture at the entry cap is refused without evicting native backlog (COS-244c)", async () => {
|
|
279
271
|
const storage = new IncrementalStorage(emptyState());
|
|
280
272
|
const buffer = new SmartBuffer(
|
|
281
273
|
parseConfig({ triggerMode: "every_n", bufferMaxTurns: 100, bufferMaxSessions: 4 }),
|
|
@@ -286,17 +278,13 @@ test("durable capture at the entry cap evicts native entries instead of refusing
|
|
|
286
278
|
await buffer.flushPendingSave();
|
|
287
279
|
|
|
288
280
|
const accepted = (key: string): BufferTurn => ({ ...makeTurn(key, "accepted"), acceptedCapture: true });
|
|
281
|
+
assert.equal(await buffer.addTurnBounded("accepted-0", accepted("accepted-0")), null);
|
|
282
|
+
await buffer.flushPendingSave();
|
|
283
|
+
assert.equal(storage.deleted.length, 0, "accepted refusal must not evict native session files");
|
|
289
284
|
for (let i = 0; i < 3; i++) {
|
|
290
|
-
|
|
291
|
-
assert.notEqual(outcome, null, `accepted-${i} refused while native entries were evictable`);
|
|
285
|
+
assert.ok(buffer.getTurns(`native-${i}`).some((turn) => turn.content === `native ${i}`));
|
|
292
286
|
}
|
|
293
|
-
|
|
294
|
-
assert.ok(storage.deleted.includes("native-0"), "oldest native entry was not evicted");
|
|
295
|
-
|
|
296
|
-
// Accepted work is still never evicted: with only accepted entries left the
|
|
297
|
-
// cap refuses rather than dropping accepted captures.
|
|
298
|
-
assert.equal(await buffer.addTurnBounded("accepted-3", accepted("accepted-3")), null);
|
|
299
|
-
for (let i = 0; i < 3; i++) assert.ok(!storage.deleted.includes(`accepted-${i}`));
|
|
287
|
+
assert.equal(buffer.getTurns("accepted-0").length, 0, "refused capture was never accepted");
|
|
300
288
|
});
|
|
301
289
|
|
|
302
290
|
test("at-cap admission evicts nothing when eviction cannot free a slot (COS-244c)", async () => {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { DrainedTurnSets } from "./buffer-turn-helpers.js";
|
|
2
|
+
import type { BufferEntryState, BufferState, BufferSurpriseEvent } from "./types.js";
|
|
2
3
|
import type { BufferSessionSaveOutcome } from "./storage/buffer-drain-claims.js";
|
|
3
4
|
|
|
4
5
|
/**
|
|
@@ -26,8 +27,8 @@ export interface BufferStorage {
|
|
|
26
27
|
export interface BufferPersistOutcome {
|
|
27
28
|
/** Sessions whose in-memory entry moved into a drain claim: the caller must evict them (a claimed session no longer lives in this process). */
|
|
28
29
|
handedOver: string[];
|
|
29
|
-
/** Per session, the turns a drain
|
|
30
|
-
drainedTurns: Map<string,
|
|
30
|
+
/** Per session, the turns a drain extracted, kept in their original lists. */
|
|
31
|
+
drainedTurns: Map<string, DrainedTurnSets>;
|
|
31
32
|
}
|
|
32
33
|
|
|
33
34
|
/**
|
|
@@ -73,17 +74,20 @@ export class BufferSessionPersistence {
|
|
|
73
74
|
storage: BufferStorage,
|
|
74
75
|
state: BufferState,
|
|
75
76
|
peekEntry: (key: string) => BufferEntryState | undefined,
|
|
77
|
+
reconcile?: { key: string; drained: DrainedTurnSets },
|
|
76
78
|
): Promise<BufferPersistOutcome> {
|
|
79
|
+
const drainedTurns = new Map<string, DrainedTurnSets>(
|
|
80
|
+
reconcile ? [[reconcile.key, reconcile.drained]] : [],
|
|
81
|
+
);
|
|
77
82
|
const saveSession = storage.saveBufferSession?.bind(storage);
|
|
78
83
|
const deleteSession = storage.deleteBufferSession?.bind(storage);
|
|
79
84
|
if (!saveSession || !deleteSession) {
|
|
80
85
|
await storage.saveBuffer(state);
|
|
81
86
|
this.dirty.clear();
|
|
82
87
|
this.deleted.clear();
|
|
83
|
-
return { handedOver: [], drainedTurns
|
|
88
|
+
return { handedOver: [], drainedTurns };
|
|
84
89
|
}
|
|
85
90
|
const handedOver: string[] = [];
|
|
86
|
-
const drainedTurns = new Map<string, BufferTurn[]>();
|
|
87
91
|
for (const key of [...this.deleted]) {
|
|
88
92
|
await deleteSession(key);
|
|
89
93
|
this.deleted.delete(key);
|
|
@@ -104,7 +108,12 @@ export class BufferSessionPersistence {
|
|
|
104
108
|
if (outcome?.status === "handed_over") handedOver.push(key);
|
|
105
109
|
// A drain tombstone: the turns it extracted must leave this process's
|
|
106
110
|
// copy too, or the owner would extract them a second time.
|
|
107
|
-
if (
|
|
111
|
+
if (
|
|
112
|
+
outcome?.drainedTurns &&
|
|
113
|
+
(outcome.drainedTurns.turns.length > 0 || outcome.drainedTurns.retainedTurns.length > 0)
|
|
114
|
+
) {
|
|
115
|
+
drainedTurns.set(key, outcome.drainedTurns);
|
|
116
|
+
}
|
|
108
117
|
}
|
|
109
118
|
return { handedOver, drainedTurns };
|
|
110
119
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import test from "node:test";
|
|
2
2
|
import assert from "node:assert/strict";
|
|
3
|
-
import { SmartBuffer } from "./buffer.js";
|
|
3
|
+
import { SmartBuffer, BufferAdmissionRefusedError } from "./buffer.js";
|
|
4
4
|
import { parseConfig } from "./config.js";
|
|
5
5
|
import type { BufferSurpriseProbe } from "./buffer.js";
|
|
6
6
|
import type { BufferState, BufferTurn } from "./types.js";
|
|
@@ -642,13 +642,14 @@ test("getTurns prepends retained deferred turns before live turns", async () =>
|
|
|
642
642
|
assert.equal(all[1]?.content, "new live turn");
|
|
643
643
|
});
|
|
644
644
|
|
|
645
|
-
test("SmartBuffer
|
|
645
|
+
test("SmartBuffer refuses new sessions instead of dropping backlog at the cap (#1908 policy)", async () => {
|
|
646
646
|
// Pre-#1908 this contract was "never prune non-empty entries". With retry
|
|
647
647
|
// retention (failed extractions keep their turns), unbounded non-empty
|
|
648
648
|
// growth during a provider outage would eventually OOM the daemon — the
|
|
649
|
-
// documented policy (#1908 step 7)
|
|
650
|
-
//
|
|
651
|
-
// Empty entries are still evicted
|
|
649
|
+
// documented policy (#1908 step 7) bounds the ENTRY COUNT, not by dropping
|
|
650
|
+
// buffered turns: a NEW session is refused (retryable backpressure) when
|
|
651
|
+
// only non-empty backlog fills the cap. Empty entries are still evicted
|
|
652
|
+
// first, and already-admitted sessions keep buffering.
|
|
652
653
|
const entries = Object.fromEntries(
|
|
653
654
|
Array.from({ length: 205 }, (_, index) => [
|
|
654
655
|
`thread-${index}`,
|
|
@@ -674,21 +675,32 @@ test("SmartBuffer prefers pruning empty entries and bounds pending-turn buffers
|
|
|
674
675
|
});
|
|
675
676
|
const buffer = new SmartBuffer(parseConfig({ bufferSaveDebounceMs: 0, bufferMaxSessions: 200 }), storage as unknown as ConstructorParameters<typeof SmartBuffer>[1]);
|
|
676
677
|
|
|
677
|
-
await
|
|
678
|
+
await assert.rejects(
|
|
679
|
+
buffer.addTurn("active-thread", makeTurn("active-thread", "pending memory")),
|
|
680
|
+
BufferAdmissionRefusedError,
|
|
681
|
+
"a new session must be refused while only non-empty backlog fills the cap",
|
|
682
|
+
);
|
|
683
|
+
|
|
684
|
+
// Already-admitted sessions keep buffering; the save reflects the
|
|
685
|
+
// post-refusal state: nothing was evicted, nothing refused was buffered.
|
|
686
|
+
await buffer.addTurn("thread-204", makeTurn("thread-204", "another turn"));
|
|
687
|
+
assert.equal(buffer.getTurns("thread-204").length, 2);
|
|
678
688
|
|
|
679
689
|
const persistedKeys = Object.keys(storage.saved?.entries ?? {});
|
|
680
|
-
assert.ok(persistedKeys.length <=
|
|
690
|
+
assert.ok(persistedKeys.length <= 206, `entry count stays at the seeded cap state (got ${persistedKeys.length})`);
|
|
681
691
|
assert.ok(persistedKeys.includes("default"), "default entry never pruned");
|
|
682
|
-
assert.ok(persistedKeys.includes("
|
|
683
|
-
assert.ok(!persistedKeys.includes("thread-0"), "oldest pending entry pruned once over the cap");
|
|
692
|
+
assert.ok(persistedKeys.includes("thread-0"), "oldest backlog keeps its turns for extraction");
|
|
684
693
|
assert.ok(persistedKeys.includes("thread-204"), "newest pending entry survives");
|
|
694
|
+
assert.ok(!persistedKeys.includes("active-thread"), "the refused turn was never buffered");
|
|
685
695
|
});
|
|
686
696
|
|
|
687
|
-
test("pruneEntries evicts
|
|
697
|
+
test("pruneEntries never evicts non-empty backlog; live admission is refused past the cap (#1908 retry-retention bound)", async () => {
|
|
688
698
|
// Retained failed extractions keep their turns in the buffer, so during a
|
|
689
699
|
// provider outage non-empty entries can exceed MAX_BUFFER_ENTRY_COUNT (200).
|
|
690
|
-
// The cap is the documented bound:
|
|
691
|
-
//
|
|
700
|
+
// The cap is still the documented bound: buffered turn data is never the
|
|
701
|
+
// release valve — a NEW session is refused (retryable backpressure) until
|
|
702
|
+
// extraction drains an entry, so the daemon cannot OOM on unbounded growth
|
|
703
|
+
// and no buffered turn is dropped (codex review).
|
|
692
704
|
const entries: NonNullable<BufferState["entries"]> = {};
|
|
693
705
|
for (let i = 0; i < 210; i += 1) {
|
|
694
706
|
const at = new Date(1_700_000_000_000 + i * 60_000).toISOString();
|
|
@@ -701,13 +713,17 @@ test("pruneEntries evicts oldest NON-empty session entries past the cap (#1908 r
|
|
|
701
713
|
const storage = new FakeStorage({ turns: [], lastExtractionAt: null, extractionCount: 0, entries });
|
|
702
714
|
const buffer = new SmartBuffer(parseConfig({ bufferSaveDebounceMs: 0, bufferMaxSessions: 200 }), storage as unknown as ConstructorParameters<typeof SmartBuffer>[1]);
|
|
703
715
|
await buffer.load();
|
|
704
|
-
await
|
|
716
|
+
await assert.rejects(
|
|
717
|
+
buffer.addTurn("session-newest", makeTurn("session-newest", "new turn")),
|
|
718
|
+
BufferAdmissionRefusedError,
|
|
719
|
+
"live admission refuses rather than evicting oldest non-empty backlog",
|
|
720
|
+
);
|
|
705
721
|
|
|
706
722
|
const savedKeys = Object.keys(storage.saved?.entries ?? {});
|
|
707
|
-
assert.ok(savedKeys.length <=
|
|
708
|
-
assert.
|
|
709
|
-
assert.equal(buffer.getTurns("session-000").length, 0, "oldest non-empty entry was evicted");
|
|
723
|
+
assert.ok(savedKeys.length <= 211, `entries bounded at MAX_BUFFER_ENTRY_COUNT plus default (got ${savedKeys.length})`);
|
|
724
|
+
assert.equal(buffer.getTurns("session-000").length, 1, "oldest non-empty entry keeps its turn for extraction");
|
|
710
725
|
assert.ok(buffer.getTurns("session-209").length > 0, "newest pre-existing entry survives");
|
|
726
|
+
assert.ok(!savedKeys.includes("session-newest"), "the refused turn was never buffered");
|
|
711
727
|
});
|
|
712
728
|
|
|
713
729
|
// ---------------------------------------------------------------------------
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import type { BufferEntryState, BufferState, BufferTurn } from "./types.js";
|
|
2
2
|
|
|
3
|
+
/** Extracted identities retain whether they came from active or retained turns. */
|
|
4
|
+
export interface DrainedTurnSets {
|
|
5
|
+
turns: BufferTurn[];
|
|
6
|
+
retainedTurns: BufferTurn[];
|
|
7
|
+
}
|
|
8
|
+
|
|
3
9
|
export interface BufferTurnOwner {
|
|
4
10
|
/**
|
|
5
11
|
* Server-resolved principal that owns this session at the authenticated
|
|
@@ -392,3 +398,44 @@ export function probeWithTimeout<T>(inflight: Promise<T>, timeoutMs: number): Pr
|
|
|
392
398
|
if (timer) clearTimeout(timer);
|
|
393
399
|
});
|
|
394
400
|
}
|
|
401
|
+
|
|
402
|
+
export const isAcceptedTurn = (t: { acceptedCapture?: boolean; writeNamespaceOverride?: string }): boolean =>
|
|
403
|
+
t.acceptedCapture === true || t.writeNamespaceOverride !== undefined;
|
|
404
|
+
|
|
405
|
+
export function entryActivityAt(entry: BufferEntryState): number {
|
|
406
|
+
const lastTurnAt = entry.turns.reduce((latest, turn) => {
|
|
407
|
+
const parsed = Date.parse(turn.timestamp);
|
|
408
|
+
return Number.isFinite(parsed) ? Math.max(latest, parsed) : latest;
|
|
409
|
+
}, -1);
|
|
410
|
+
const lastExtractionAt =
|
|
411
|
+
entry.lastExtractionAt === null ? Number.NaN : Date.parse(entry.lastExtractionAt);
|
|
412
|
+
return Math.max(
|
|
413
|
+
lastTurnAt,
|
|
414
|
+
Number.isFinite(lastExtractionAt) ? lastExtractionAt : -1,
|
|
415
|
+
);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
export function entryHoldsAcceptedCapture(entry: BufferEntryState | undefined): boolean {
|
|
419
|
+
return (entry?.turns ?? []).some(isAcceptedTurn) || (entry?.retainedTurns ?? []).some(isAcceptedTurn);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
export function normalizeBufferState(state: BufferState): BufferState {
|
|
423
|
+
// SAFETY: persisted-state contract — entries, when present, is a record of BufferEntryState.
|
|
424
|
+
const entries = Object.assign(
|
|
425
|
+
Object.create(null),
|
|
426
|
+
state.entries ?? {},
|
|
427
|
+
) as NonNullable<BufferState["entries"]>;
|
|
428
|
+
if (!entries.default) {
|
|
429
|
+
entries.default = {
|
|
430
|
+
turns: Array.isArray(state.turns) ? [...state.turns] : [],
|
|
431
|
+
lastExtractionAt: state.lastExtractionAt ?? null,
|
|
432
|
+
extractionCount: state.extractionCount ?? 0,
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
return {
|
|
436
|
+
turns: entries.default.turns,
|
|
437
|
+
lastExtractionAt: entries.default.lastExtractionAt,
|
|
438
|
+
extractionCount: entries.default.extractionCount,
|
|
439
|
+
entries,
|
|
440
|
+
};
|
|
441
|
+
}
|
package/src/buffer-types.ts
CHANGED
|
@@ -1,6 +1,33 @@
|
|
|
1
1
|
/** Types split out of buffer.ts (structural ratchet, issue #1995). */
|
|
2
2
|
|
|
3
|
-
import type { BufferTurn } from "./types.js";
|
|
3
|
+
import type { BufferTurn, CodingContext, SignalLevel } from "./types.js";
|
|
4
|
+
import type { ResolvedScopeProfilePlan } from "./namespaces/scope-profiles.js";
|
|
5
|
+
|
|
6
|
+
export type TriggerDecision = "extract_now" | "extract_batch" | "keep_buffering";
|
|
7
|
+
|
|
8
|
+
export interface AddTurnOutcome {
|
|
9
|
+
decision: TriggerDecision;
|
|
10
|
+
extractionTurns?: BufferTurn[];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface AddTurnMutationResult {
|
|
14
|
+
decision: TriggerDecision;
|
|
15
|
+
signalLevel: SignalLevel;
|
|
16
|
+
priorTurns: BufferTurn[];
|
|
17
|
+
activeTurnsSnapshot: BufferTurn[];
|
|
18
|
+
retainedTurnsSnapshot: BufferTurn[];
|
|
19
|
+
turnCountInWindow: number;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface RetainedTurnCleanupOptions {
|
|
23
|
+
abortSignal?: AbortSignal;
|
|
24
|
+
deadlineMs?: number;
|
|
25
|
+
/** Keep cleanup within the accepted namespace. */
|
|
26
|
+
writeNamespaceOverride?: string;
|
|
27
|
+
/** Match the original profile and project binding, not just its write namespace. */
|
|
28
|
+
scopeProfileWritePlan?: ResolvedScopeProfilePlan | null;
|
|
29
|
+
codingContext?: CodingContext;
|
|
30
|
+
}
|
|
4
31
|
|
|
5
32
|
/**
|
|
6
33
|
* Optional surprise probe injected into `SmartBuffer`.
|