@remnic/core 10.0.69 → 10.0.71
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 +82 -79
- package/dist/access-authorization-probe.js +83 -80
- package/dist/access-boundary.js +82 -79
- package/dist/access-cli.js +196 -193
- package/dist/access-cli.js.map +1 -1
- package/dist/access-extraction-force-flush.js +82 -79
- package/dist/access-http-briefing.js +83 -80
- package/dist/access-http-query.js +84 -81
- package/dist/access-http.js +106 -103
- package/dist/access-identity-continuity-surface.js +82 -79
- package/dist/access-lcm-surface.js +82 -79
- package/dist/access-mcp.js +95 -92
- package/dist/access-namespace-preflight.js +82 -79
- package/dist/access-observe-lcm-archive.d.ts +5 -0
- package/dist/access-observe-lcm-archive.d.ts.map +1 -0
- package/dist/access-observe-lcm-archive.js +9 -0
- package/dist/access-observe-objective-state.d.ts +8 -0
- package/dist/access-observe-objective-state.d.ts.map +1 -0
- package/dist/access-observe-objective-state.js +17 -0
- package/dist/access-observe-stage-timing.d.ts +11 -0
- package/dist/access-observe-stage-timing.d.ts.map +1 -0
- package/dist/access-observe-stage-timing.js +9 -0
- package/dist/access-observe-stage-timing.js.map +1 -0
- package/dist/access-observe-write-category.js +13 -13
- package/dist/access-observe-write-surface.d.ts.map +1 -1
- package/dist/access-observe-write-surface.js +82 -79
- package/dist/access-offline-manifest.js +8 -8
- package/dist/access-operations-batch.js +85 -82
- package/dist/access-operations.js +92 -89
- package/dist/access-recall-concurrency.js +82 -79
- package/dist/access-recall-response.js +82 -79
- package/dist/access-recall-surface.js +82 -79
- package/dist/access-schema.js +12 -12
- package/dist/access-service.d.ts +1 -0
- package/dist/access-service.d.ts.map +1 -1
- package/dist/access-service.js +82 -79
- package/dist/access-surface-catalog.js +84 -81
- package/dist/access-surface-catalog.js.map +1 -1
- package/dist/active-recall.js +26 -26
- package/dist/adapters/index.js +4 -4
- package/dist/adapters/registry.js +2 -2
- package/dist/briefing.js +17 -17
- package/dist/buffer-turn-helpers.d.ts +10 -1
- package/dist/buffer-turn-helpers.d.ts.map +1 -1
- package/dist/buffer-turn-helpers.js +3 -1
- package/dist/buffer.d.ts +4 -0
- package/dist/buffer.d.ts.map +1 -1
- package/dist/buffer.js +9 -9
- package/dist/calibration.js +4 -4
- package/dist/{capsule-crypto-GWVG7LGC.js → capsule-crypto-7FJQINUR.js} +2 -2
- package/dist/capsule-crypto-7FJQINUR.js.map +1 -0
- package/dist/causal-behavior.js +2 -2
- package/dist/causal-chain.js +2 -2
- package/dist/causal-consolidation.js +22 -22
- package/dist/causal-retrieval.js +2 -2
- package/dist/causal-trajectory-graph.js +3 -3
- package/dist/causal-trajectory.js +1 -1
- package/dist/{chunk-6WKWL53V.js → chunk-23BA3USG.js} +128 -122
- package/dist/chunk-23BA3USG.js.map +1 -0
- package/dist/{chunk-OWHERGF2.js → chunk-2NLLXCJG.js} +2 -2
- package/dist/{chunk-CI3MAAIY.js → chunk-2R44BBPK.js} +2 -2
- package/dist/{chunk-QDZWQBLU.js → chunk-2R7OCBRR.js} +2 -2
- package/dist/{chunk-B3MWELZ3.js → chunk-2WPWEVAH.js} +2 -2
- package/dist/{chunk-PLCYKAWH.js → chunk-3ICAWZUU.js} +4 -4
- package/dist/{chunk-R4ZHF4K2.js → chunk-43FLJLT2.js} +4 -4
- package/dist/{chunk-PMDOGXA2.js → chunk-4PMFSYH7.js} +2 -2
- package/dist/{chunk-HXFIILGF.js → chunk-4RASUAN3.js} +4 -4
- package/dist/{chunk-EI6M5NVK.js → chunk-5JBVPJCT.js} +2 -2
- package/dist/{chunk-4NV7O76Q.js → chunk-5ORJHH43.js} +2 -2
- package/dist/{chunk-74AY3S7U.js → chunk-5RW247F5.js} +10 -10
- package/dist/{chunk-4XINO77L.js → chunk-66JOLMWS.js} +2 -2
- package/dist/{chunk-AYJQ644F.js → chunk-6KBCUIZP.js} +2 -2
- package/dist/{chunk-GKL6QYZO.js → chunk-6L63IN3M.js} +1 -1
- package/dist/{chunk-OPFVXM2D.js → chunk-7P5VBRBS.js} +2 -2
- package/dist/{chunk-4SOWMA55.js → chunk-7WFY2L5J.js} +3 -3
- package/dist/{chunk-GJ4HU6LK.js → chunk-AGRMXXVA.js} +2 -2
- package/dist/{chunk-KOLJJN5G.js → chunk-AQNHEDXP.js} +11 -4
- package/dist/chunk-AQNHEDXP.js.map +1 -0
- package/dist/{chunk-IVUVQPTQ.js → chunk-AZNE2H6D.js} +2 -2
- package/dist/{chunk-HPB5TF55.js → chunk-BCBY5GYZ.js} +3 -3
- package/dist/{chunk-M4IEZGCK.js → chunk-BFBS7OFQ.js} +7 -7
- package/dist/{chunk-4PG5R6BJ.js → chunk-BNU5TQLR.js} +2 -2
- package/dist/{chunk-TVE7W67N.js → chunk-BTU77RB5.js} +2 -2
- package/dist/{chunk-H6VJSITJ.js → chunk-CIL4GHMD.js} +3 -3
- package/dist/{chunk-KZZ5EZ7O.js → chunk-DC3DWF2M.js} +2 -2
- package/dist/{chunk-R2BIVU2Z.js → chunk-DE4U5PTV.js} +50 -46
- package/dist/chunk-DE4U5PTV.js.map +1 -0
- package/dist/{chunk-DDBB55L2.js → chunk-DSSTOV5M.js} +534 -361
- package/dist/chunk-DSSTOV5M.js.map +1 -0
- package/dist/{chunk-4MPAKYZU.js → chunk-EGKDONTC.js} +12 -6
- package/dist/chunk-EGKDONTC.js.map +1 -0
- package/dist/{chunk-AUFPPBC3.js → chunk-ENPJX3BB.js} +3 -3
- package/dist/{chunk-CTH2CQCB.js → chunk-EQ6EMJRG.js} +2 -2
- package/dist/{chunk-63A2BU2B.js → chunk-F4ZHKDBW.js} +5 -5
- package/dist/{chunk-3LBDEGQA.js → chunk-FDUCIUQ5.js} +2 -2
- package/dist/{chunk-OCK4D5PC.js → chunk-FGGLVCHI.js} +2 -2
- package/dist/{chunk-XVZA6VXC.js → chunk-FZVROZKC.js} +4 -4
- package/dist/{chunk-3RHU5MD7.js → chunk-GXAZ4Q2S.js} +2 -2
- package/dist/{chunk-UVVFPCPX.js → chunk-H2X32F2K.js} +2 -2
- package/dist/{chunk-645WOU5I.js → chunk-H7BZBSP6.js} +4 -4
- package/dist/{chunk-W7K5BP3M.js → chunk-HQPFACXO.js} +2 -2
- package/dist/{chunk-TECK5IVY.js → chunk-HZNURRDS.js} +2 -2
- package/dist/{chunk-U2CAGMUD.js → chunk-ICBUTKLL.js} +2 -2
- package/dist/{chunk-B4DXREWC.js → chunk-IQXQ3KWR.js} +2 -2
- package/dist/{chunk-AL3TCNSJ.js → chunk-J6GZ2254.js} +4 -4
- package/dist/{chunk-5MGKGEL7.js → chunk-J72ZY7NU.js} +2 -2
- package/dist/{chunk-OH55F76C.js → chunk-JEX2JL3U.js} +2 -2
- package/dist/{chunk-Y3XPSGNF.js → chunk-JGLZ46JS.js} +2 -2
- package/dist/{chunk-EXQJMFRF.js → chunk-JY4LXMZR.js} +2 -2
- package/dist/{chunk-6P7U3HWG.js → chunk-K36RMINQ.js} +3 -3
- package/dist/{chunk-BUKK5SWA.js → chunk-KQAFEZQX.js} +2 -2
- package/dist/{chunk-LN4X2NDA.js → chunk-KY3KJUNN.js} +10 -10
- package/dist/chunk-L3E7MLMK.js +20 -0
- package/dist/chunk-L3E7MLMK.js.map +1 -0
- package/dist/{chunk-H5DIRCZP.js → chunk-LHMCZ6VW.js} +18 -18
- package/dist/{chunk-GGD2YDS6.js → chunk-LN7CWLAU.js} +2 -2
- package/dist/{chunk-GHHQLSSY.js → chunk-LYOCAUI6.js} +3 -3
- package/dist/{chunk-VA3GNAWQ.js → chunk-LZIOY7ZU.js} +4 -5
- package/dist/chunk-LZIOY7ZU.js.map +1 -0
- package/dist/{chunk-W26D2USW.js → chunk-MUH7RR6T.js} +6 -6
- package/dist/{chunk-IAGVM7EY.js → chunk-N2Y6COEM.js} +2 -2
- package/dist/{chunk-64JLBFCQ.js → chunk-N7NAGECH.js} +2 -2
- package/dist/{chunk-5Y6VA6X2.js → chunk-OGQGDOYK.js} +2 -2
- package/dist/{chunk-U7MIMS2Y.js → chunk-OUBEJWVI.js} +17 -1
- package/dist/chunk-OUBEJWVI.js.map +1 -0
- package/dist/{chunk-JLMOH5JA.js → chunk-OWZXDBV4.js} +2 -2
- package/dist/{chunk-ZORS5BG5.js → chunk-PJAMFIDE.js} +10 -10
- package/dist/chunk-QCFZDYYG.js +34 -0
- package/dist/chunk-QCFZDYYG.js.map +1 -0
- package/dist/{chunk-SXTVZ6JL.js → chunk-QIOQI7QZ.js} +2 -2
- package/dist/{chunk-MMCO4SRG.js → chunk-QNN4FYBD.js} +19 -11
- package/dist/chunk-QNN4FYBD.js.map +1 -0
- package/dist/{chunk-75DCOS6I.js → chunk-QUPTDKSX.js} +67 -67
- package/dist/{chunk-HH73M65H.js → chunk-QVZZE6VH.js} +3 -3
- package/dist/{chunk-XWE3EOWX.js → chunk-RZED7FAU.js} +2 -2
- package/dist/{chunk-IZBIDPMY.js → chunk-S37O4S6E.js} +2 -2
- package/dist/{chunk-NHWDTOBN.js → chunk-S73ZVU37.js} +21 -21
- package/dist/{chunk-YSATDEBY.js → chunk-SBRUTD5O.js} +5 -5
- package/dist/{chunk-WVP2I6C5.js → chunk-SDL22RRW.js} +50 -11
- package/dist/chunk-SDL22RRW.js.map +1 -0
- package/dist/{chunk-PRKNAIGZ.js → chunk-T6T2RMWW.js} +5 -5
- package/dist/{chunk-CIEEYVUB.js → chunk-TPX7XXY4.js} +2 -2
- package/dist/{chunk-L334PSLW.js → chunk-U2A56LLF.js} +8 -8
- package/dist/{chunk-P4TV3TQ2.js → chunk-U6IL4724.js} +5 -5
- package/dist/{chunk-5UM4H64E.js → chunk-UAOJVQXC.js} +2 -2
- package/dist/{chunk-MOIJWWNA.js → chunk-UE5JYPU6.js} +13 -13
- package/dist/{chunk-TGQW3AMK.js → chunk-UIFWZMO7.js} +23 -23
- package/dist/{chunk-UW6LBFYN.js → chunk-USX5YIKB.js} +6 -6
- package/dist/{chunk-QGFR73SN.js → chunk-VK5HKVQ7.js} +2 -2
- package/dist/{chunk-ZOBYTRBU.js → chunk-VNTQU6KB.js} +47 -2
- package/dist/chunk-VNTQU6KB.js.map +1 -0
- package/dist/{chunk-IHCXMYSE.js → chunk-VPEIWMUT.js} +2 -2
- package/dist/{chunk-O5ZINWW3.js → chunk-VSJ3ARFS.js} +2 -2
- package/dist/{chunk-RLVI2M5Q.js → chunk-W4V7P5HP.js} +2 -2
- package/dist/{chunk-ELHJK7LM.js → chunk-WBN66HPI.js} +6 -6
- package/dist/{chunk-BJLCV2P3.js → chunk-WGRAB3KB.js} +2 -2
- package/dist/{chunk-5KDDAQ6K.js → chunk-WN2DASTS.js} +10 -10
- package/dist/{chunk-IG3EZ3TF.js → chunk-WNQ3B3Q5.js} +2 -2
- package/dist/{chunk-V6SBA5O7.js → chunk-WYOHQJKC.js} +7 -7
- package/dist/chunk-WZU5AGO6.js +38 -0
- package/dist/chunk-WZU5AGO6.js.map +1 -0
- package/dist/{chunk-WHROVQ5Q.js → chunk-X4DHAXFO.js} +3 -3
- package/dist/{chunk-UVYI6VIX.js → chunk-X7Y7WX73.js} +1 -1
- package/dist/{chunk-JLGBJ4AX.js → chunk-XPZM7GP6.js} +10 -10
- package/dist/{chunk-64S6X4IU.js → chunk-XTN54NML.js} +4 -4
- package/dist/{chunk-UYUFDTIX.js → chunk-YIXWY4OZ.js} +2 -2
- package/dist/{chunk-NCZGGGWI.js → chunk-YPYSC3TX.js} +12 -12
- package/dist/{chunk-AVGDYYMA.js → chunk-ZBET3W26.js} +3 -3
- package/dist/{chunk-Y7427SL7.js → chunk-ZG4GNPRE.js} +216 -50
- package/dist/chunk-ZG4GNPRE.js.map +1 -0
- package/dist/chunk-ZODKV27J.js +443 -0
- package/dist/chunk-ZODKV27J.js.map +1 -0
- package/dist/cli.js +133 -130
- package/dist/coding/export-okf-codegraph.js +16 -16
- package/dist/coding/pre-action-gate.js +2 -2
- package/dist/compounding/engine.js +18 -18
- package/dist/config.js +26 -26
- package/dist/connectors/codex-materialize-runner.js +19 -19
- package/dist/connectors/codex-materialize.js +2 -2
- package/dist/connectors/index.js +20 -20
- package/dist/consolidation-provenance-check.js +2 -2
- package/dist/convergence-refresh.js +20 -20
- package/dist/corpus-watermark.js +20 -20
- package/dist/deep-recall-http-glue.js +13 -13
- package/dist/deep-recall.js +2 -2
- package/dist/embedding-fallback.js +2 -2
- package/dist/entity-retrieval.js +20 -20
- package/dist/event-order-recall.js +2 -2
- package/dist/external-wiki-access.js +83 -80
- package/dist/extraction-span-materialize.js +2 -2
- package/dist/extraction-window-drain.d.ts.map +1 -1
- package/dist/extraction-window-drain.js +1 -1
- package/dist/extraction.js +32 -32
- package/dist/{graph-edge-decay-OEX5OGMI.js → graph-edge-decay-CXTNJ7IQ.js} +6 -6
- package/dist/graph-edge-decay-CXTNJ7IQ.js.map +1 -0
- package/dist/graph-jsonl.js +4 -4
- package/dist/graph-snapshot.js +4 -4
- package/dist/graph.js +3 -3
- package/dist/harmonic-retrieval.js +17 -17
- package/dist/index.js +281 -278
- package/dist/lcm/archive.d.ts +12 -2
- package/dist/lcm/archive.d.ts.map +1 -1
- package/dist/lcm/archive.js +2 -2
- package/dist/lcm/engine.d.ts +59 -8
- package/dist/lcm/engine.d.ts.map +1 -1
- package/dist/lcm/engine.js +6 -6
- package/dist/lcm/index.js +16 -16
- package/dist/lcm/queue.d.ts +7 -3
- package/dist/lcm/queue.d.ts.map +1 -1
- package/dist/lcm/queue.js +1 -1
- package/dist/lcm/summarizer.d.ts +8 -1
- package/dist/lcm/summarizer.d.ts.map +1 -1
- package/dist/lcm/summarizer.js +3 -3
- package/dist/local-llm.js +4 -4
- package/dist/location/index.js +19 -19
- package/dist/maintenance/memory-governance.js +18 -18
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +17 -17
- package/dist/maintenance/rebuild-memory-projection-runner.js +20 -20
- package/dist/maintenance/rebuild-memory-projection.js +19 -19
- package/dist/memory-browse-operations.js +83 -80
- package/dist/memory-browse.js +6 -6
- package/dist/namespaces/migrate.js +18 -18
- package/dist/namespaces/search.js +7 -7
- package/dist/namespaces/storage.js +17 -17
- package/dist/offline-sync-changeset-generations.js +5 -5
- package/dist/offline-sync-embedding-generation.js +4 -4
- package/dist/offline-sync-snapshot-stream.js +7 -7
- package/dist/offline-sync.js +9 -9
- package/dist/operator-doctor-corpus.js +21 -21
- package/dist/operator-toolkit.js +41 -41
- package/dist/orchestration/backlog-scheduler.d.ts +151 -0
- package/dist/orchestration/backlog-scheduler.d.ts.map +1 -0
- package/dist/orchestration/backlog-scheduler.js +891 -0
- package/dist/orchestration/backlog-scheduler.js.map +1 -0
- package/dist/orchestration/buffer-drain-runner.d.ts +31 -27
- package/dist/orchestration/buffer-drain-runner.d.ts.map +1 -1
- package/dist/orchestration/buffer-drain-runner.js +202 -404
- package/dist/orchestration/buffer-drain-runner.js.map +1 -1
- package/dist/orchestration/extraction-drain.d.ts +50 -17
- package/dist/orchestration/extraction-drain.d.ts.map +1 -1
- package/dist/orchestration/extraction-queue-coordinator.d.ts +19 -1
- package/dist/orchestration/extraction-queue-coordinator.d.ts.map +1 -1
- package/dist/orchestration/extraction-run.d.ts +8 -0
- package/dist/orchestration/extraction-run.d.ts.map +1 -1
- package/dist/orchestration/extraction-task-queueing.d.ts +44 -0
- package/dist/orchestration/extraction-task-queueing.d.ts.map +1 -0
- package/dist/orchestration/maintenance.js +33 -33
- package/dist/orchestration/turn-ingestion.d.ts +11 -5
- package/dist/orchestration/turn-ingestion.d.ts.map +1 -1
- package/dist/orchestrator.d.ts +13 -0
- package/dist/orchestrator.d.ts.map +1 -1
- package/dist/orchestrator.js +196 -193
- package/dist/qmd.js +4 -4
- package/dist/recall-navigation-operations.js +83 -80
- package/dist/recall-navigation.js +82 -79
- package/dist/recall-state-view-admission.js +6 -6
- package/dist/recall-why-service.js +6 -6
- package/dist/resume-bundles.js +29 -29
- 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 +17 -17
- package/dist/semantic-rule-verifier.js +17 -17
- package/dist/source-agent-qualifier.js +24 -24
- package/dist/standup/build.js +17 -17
- package/dist/storage/buffer-drain-claims.d.ts +15 -0
- 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.js +16 -16
- package/dist/summarizer.js +6 -6
- package/dist/support-passport/index.js +85 -82
- package/dist/transfer/backup.js +2 -2
- package/dist/transfer/capsule-export.js +2 -2
- package/dist/transfer/capsule-import.js +2 -2
- package/dist/transfer/export-okf.js +17 -17
- package/dist/verified-recall.js +17 -17
- package/dist/who-knows-cli.js +18 -18
- package/dist/who-knows.js +17 -17
- package/package.json +12 -2
- package/src/access-observe-lcm-archive.ts +30 -0
- package/src/access-observe-lcm-fail-closed.test.ts +118 -0
- package/src/access-observe-objective-state.ts +40 -0
- package/src/access-observe-stage-timing.test.ts +246 -0
- package/src/access-observe-stage-timing.ts +58 -0
- package/src/access-observe-write-surface.ts +45 -45
- package/src/access-service.ts +1 -0
- package/src/backlog-scheduler.test.ts +806 -0
- package/src/buffer-drain-handover.test.ts +363 -18
- package/src/buffer-drain-inprocess.test.ts +355 -0
- package/src/buffer-drain-model-boundary.test.ts +1062 -0
- package/src/buffer-drop-extraction-chunk.test.ts +237 -22
- package/src/buffer-turn-helpers.ts +26 -1
- package/src/buffer.ts +8 -0
- package/src/extraction-window-drain.ts +7 -7
- package/src/lcm/archive.ts +59 -7
- package/src/lcm/engine.ts +256 -53
- package/src/lcm/queue.test.ts +32 -14
- package/src/lcm/queue.ts +24 -16
- package/src/lcm/summarizer.ts +15 -2
- package/src/lcm-durability.test.ts +856 -0
- package/src/lcm-engine.test.ts +12 -3
- package/src/orchestration/backlog-scheduler.ts +481 -0
- package/src/orchestration/buffer-drain-runner.ts +545 -180
- package/src/orchestration/extraction-drain.ts +287 -108
- package/src/orchestration/extraction-queue-coordinator.ts +51 -3
- package/src/orchestration/extraction-run.test.ts +41 -0
- package/src/orchestration/extraction-run.ts +24 -5
- package/src/orchestration/extraction-task-queueing.ts +87 -0
- package/src/orchestration/turn-ingestion.ts +46 -59
- package/src/orchestrator-extraction-queue.test.ts +46 -2
- package/src/orchestrator-flush.test.ts +4 -1
- package/src/orchestrator.ts +47 -21
- package/src/storage/buffer-drain-claims.ts +55 -0
- package/dist/chunk-4MPAKYZU.js.map +0 -1
- package/dist/chunk-6WKWL53V.js.map +0 -1
- package/dist/chunk-DDBB55L2.js.map +0 -1
- package/dist/chunk-KOLJJN5G.js.map +0 -1
- package/dist/chunk-MMCO4SRG.js.map +0 -1
- package/dist/chunk-R2BIVU2Z.js.map +0 -1
- package/dist/chunk-U7MIMS2Y.js.map +0 -1
- package/dist/chunk-VA3GNAWQ.js.map +0 -1
- package/dist/chunk-WVP2I6C5.js.map +0 -1
- package/dist/chunk-Y7427SL7.js.map +0 -1
- package/dist/chunk-ZOBYTRBU.js.map +0 -1
- /package/dist/{capsule-crypto-GWVG7LGC.js.map → access-observe-lcm-archive.js.map} +0 -0
- /package/dist/{graph-edge-decay-OEX5OGMI.js.map → access-observe-objective-state.js.map} +0 -0
- /package/dist/{chunk-OWHERGF2.js.map → chunk-2NLLXCJG.js.map} +0 -0
- /package/dist/{chunk-CI3MAAIY.js.map → chunk-2R44BBPK.js.map} +0 -0
- /package/dist/{chunk-QDZWQBLU.js.map → chunk-2R7OCBRR.js.map} +0 -0
- /package/dist/{chunk-B3MWELZ3.js.map → chunk-2WPWEVAH.js.map} +0 -0
- /package/dist/{chunk-PLCYKAWH.js.map → chunk-3ICAWZUU.js.map} +0 -0
- /package/dist/{chunk-R4ZHF4K2.js.map → chunk-43FLJLT2.js.map} +0 -0
- /package/dist/{chunk-PMDOGXA2.js.map → chunk-4PMFSYH7.js.map} +0 -0
- /package/dist/{chunk-HXFIILGF.js.map → chunk-4RASUAN3.js.map} +0 -0
- /package/dist/{chunk-EI6M5NVK.js.map → chunk-5JBVPJCT.js.map} +0 -0
- /package/dist/{chunk-4NV7O76Q.js.map → chunk-5ORJHH43.js.map} +0 -0
- /package/dist/{chunk-74AY3S7U.js.map → chunk-5RW247F5.js.map} +0 -0
- /package/dist/{chunk-4XINO77L.js.map → chunk-66JOLMWS.js.map} +0 -0
- /package/dist/{chunk-AYJQ644F.js.map → chunk-6KBCUIZP.js.map} +0 -0
- /package/dist/{chunk-GKL6QYZO.js.map → chunk-6L63IN3M.js.map} +0 -0
- /package/dist/{chunk-OPFVXM2D.js.map → chunk-7P5VBRBS.js.map} +0 -0
- /package/dist/{chunk-4SOWMA55.js.map → chunk-7WFY2L5J.js.map} +0 -0
- /package/dist/{chunk-GJ4HU6LK.js.map → chunk-AGRMXXVA.js.map} +0 -0
- /package/dist/{chunk-IVUVQPTQ.js.map → chunk-AZNE2H6D.js.map} +0 -0
- /package/dist/{chunk-HPB5TF55.js.map → chunk-BCBY5GYZ.js.map} +0 -0
- /package/dist/{chunk-M4IEZGCK.js.map → chunk-BFBS7OFQ.js.map} +0 -0
- /package/dist/{chunk-4PG5R6BJ.js.map → chunk-BNU5TQLR.js.map} +0 -0
- /package/dist/{chunk-TVE7W67N.js.map → chunk-BTU77RB5.js.map} +0 -0
- /package/dist/{chunk-H6VJSITJ.js.map → chunk-CIL4GHMD.js.map} +0 -0
- /package/dist/{chunk-KZZ5EZ7O.js.map → chunk-DC3DWF2M.js.map} +0 -0
- /package/dist/{chunk-AUFPPBC3.js.map → chunk-ENPJX3BB.js.map} +0 -0
- /package/dist/{chunk-CTH2CQCB.js.map → chunk-EQ6EMJRG.js.map} +0 -0
- /package/dist/{chunk-63A2BU2B.js.map → chunk-F4ZHKDBW.js.map} +0 -0
- /package/dist/{chunk-3LBDEGQA.js.map → chunk-FDUCIUQ5.js.map} +0 -0
- /package/dist/{chunk-OCK4D5PC.js.map → chunk-FGGLVCHI.js.map} +0 -0
- /package/dist/{chunk-XVZA6VXC.js.map → chunk-FZVROZKC.js.map} +0 -0
- /package/dist/{chunk-3RHU5MD7.js.map → chunk-GXAZ4Q2S.js.map} +0 -0
- /package/dist/{chunk-UVVFPCPX.js.map → chunk-H2X32F2K.js.map} +0 -0
- /package/dist/{chunk-645WOU5I.js.map → chunk-H7BZBSP6.js.map} +0 -0
- /package/dist/{chunk-W7K5BP3M.js.map → chunk-HQPFACXO.js.map} +0 -0
- /package/dist/{chunk-TECK5IVY.js.map → chunk-HZNURRDS.js.map} +0 -0
- /package/dist/{chunk-U2CAGMUD.js.map → chunk-ICBUTKLL.js.map} +0 -0
- /package/dist/{chunk-B4DXREWC.js.map → chunk-IQXQ3KWR.js.map} +0 -0
- /package/dist/{chunk-AL3TCNSJ.js.map → chunk-J6GZ2254.js.map} +0 -0
- /package/dist/{chunk-5MGKGEL7.js.map → chunk-J72ZY7NU.js.map} +0 -0
- /package/dist/{chunk-OH55F76C.js.map → chunk-JEX2JL3U.js.map} +0 -0
- /package/dist/{chunk-Y3XPSGNF.js.map → chunk-JGLZ46JS.js.map} +0 -0
- /package/dist/{chunk-EXQJMFRF.js.map → chunk-JY4LXMZR.js.map} +0 -0
- /package/dist/{chunk-6P7U3HWG.js.map → chunk-K36RMINQ.js.map} +0 -0
- /package/dist/{chunk-BUKK5SWA.js.map → chunk-KQAFEZQX.js.map} +0 -0
- /package/dist/{chunk-LN4X2NDA.js.map → chunk-KY3KJUNN.js.map} +0 -0
- /package/dist/{chunk-H5DIRCZP.js.map → chunk-LHMCZ6VW.js.map} +0 -0
- /package/dist/{chunk-GGD2YDS6.js.map → chunk-LN7CWLAU.js.map} +0 -0
- /package/dist/{chunk-GHHQLSSY.js.map → chunk-LYOCAUI6.js.map} +0 -0
- /package/dist/{chunk-W26D2USW.js.map → chunk-MUH7RR6T.js.map} +0 -0
- /package/dist/{chunk-IAGVM7EY.js.map → chunk-N2Y6COEM.js.map} +0 -0
- /package/dist/{chunk-64JLBFCQ.js.map → chunk-N7NAGECH.js.map} +0 -0
- /package/dist/{chunk-5Y6VA6X2.js.map → chunk-OGQGDOYK.js.map} +0 -0
- /package/dist/{chunk-JLMOH5JA.js.map → chunk-OWZXDBV4.js.map} +0 -0
- /package/dist/{chunk-ZORS5BG5.js.map → chunk-PJAMFIDE.js.map} +0 -0
- /package/dist/{chunk-SXTVZ6JL.js.map → chunk-QIOQI7QZ.js.map} +0 -0
- /package/dist/{chunk-75DCOS6I.js.map → chunk-QUPTDKSX.js.map} +0 -0
- /package/dist/{chunk-HH73M65H.js.map → chunk-QVZZE6VH.js.map} +0 -0
- /package/dist/{chunk-XWE3EOWX.js.map → chunk-RZED7FAU.js.map} +0 -0
- /package/dist/{chunk-IZBIDPMY.js.map → chunk-S37O4S6E.js.map} +0 -0
- /package/dist/{chunk-NHWDTOBN.js.map → chunk-S73ZVU37.js.map} +0 -0
- /package/dist/{chunk-YSATDEBY.js.map → chunk-SBRUTD5O.js.map} +0 -0
- /package/dist/{chunk-PRKNAIGZ.js.map → chunk-T6T2RMWW.js.map} +0 -0
- /package/dist/{chunk-CIEEYVUB.js.map → chunk-TPX7XXY4.js.map} +0 -0
- /package/dist/{chunk-L334PSLW.js.map → chunk-U2A56LLF.js.map} +0 -0
- /package/dist/{chunk-P4TV3TQ2.js.map → chunk-U6IL4724.js.map} +0 -0
- /package/dist/{chunk-5UM4H64E.js.map → chunk-UAOJVQXC.js.map} +0 -0
- /package/dist/{chunk-MOIJWWNA.js.map → chunk-UE5JYPU6.js.map} +0 -0
- /package/dist/{chunk-TGQW3AMK.js.map → chunk-UIFWZMO7.js.map} +0 -0
- /package/dist/{chunk-UW6LBFYN.js.map → chunk-USX5YIKB.js.map} +0 -0
- /package/dist/{chunk-QGFR73SN.js.map → chunk-VK5HKVQ7.js.map} +0 -0
- /package/dist/{chunk-IHCXMYSE.js.map → chunk-VPEIWMUT.js.map} +0 -0
- /package/dist/{chunk-O5ZINWW3.js.map → chunk-VSJ3ARFS.js.map} +0 -0
- /package/dist/{chunk-RLVI2M5Q.js.map → chunk-W4V7P5HP.js.map} +0 -0
- /package/dist/{chunk-ELHJK7LM.js.map → chunk-WBN66HPI.js.map} +0 -0
- /package/dist/{chunk-BJLCV2P3.js.map → chunk-WGRAB3KB.js.map} +0 -0
- /package/dist/{chunk-5KDDAQ6K.js.map → chunk-WN2DASTS.js.map} +0 -0
- /package/dist/{chunk-IG3EZ3TF.js.map → chunk-WNQ3B3Q5.js.map} +0 -0
- /package/dist/{chunk-V6SBA5O7.js.map → chunk-WYOHQJKC.js.map} +0 -0
- /package/dist/{chunk-WHROVQ5Q.js.map → chunk-X4DHAXFO.js.map} +0 -0
- /package/dist/{chunk-UVYI6VIX.js.map → chunk-X7Y7WX73.js.map} +0 -0
- /package/dist/{chunk-JLGBJ4AX.js.map → chunk-XPZM7GP6.js.map} +0 -0
- /package/dist/{chunk-64S6X4IU.js.map → chunk-XTN54NML.js.map} +0 -0
- /package/dist/{chunk-UYUFDTIX.js.map → chunk-YIXWY4OZ.js.map} +0 -0
- /package/dist/{chunk-NCZGGGWI.js.map → chunk-YPYSC3TX.js.map} +0 -0
- /package/dist/{chunk-AVGDYYMA.js.map → chunk-ZBET3W26.js.map} +0 -0
|
@@ -10,22 +10,28 @@
|
|
|
10
10
|
// claim for the drain worker.
|
|
11
11
|
// 4. default-key mirror: handing over "default" clears the legacy mirror.
|
|
12
12
|
import assert from "node:assert/strict";
|
|
13
|
-
import { existsSync,
|
|
13
|
+
import { existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
|
|
14
14
|
import os from "node:os";
|
|
15
15
|
import { hostname } from "node:os";
|
|
16
16
|
import path from "node:path";
|
|
17
17
|
import test from "node:test";
|
|
18
18
|
|
|
19
|
+
import type { BufferStorage } from "./buffer-persistence.js";
|
|
20
|
+
import type { SessionObserverState } from "./session-observer-state.js";
|
|
21
|
+
import type { TranscriptManager } from "./transcript.js";
|
|
19
22
|
import { SmartBuffer } from "./buffer.js";
|
|
23
|
+
import { parseConfig } from "./config.js";
|
|
24
|
+
import { ClaimBufferStorage } from "./orchestration/buffer-drain-runner.js";
|
|
25
|
+
import { ExtractionDrain } from "./orchestration/extraction-drain.js";
|
|
20
26
|
import type { ExtractionPreemptHandle } from "./orchestration/extraction-queue-coordinator.js";
|
|
21
|
-
import {
|
|
27
|
+
import { ExtractionQueueCoordinator } from "./orchestration/extraction-queue-coordinator.js";
|
|
28
|
+
import type { ExtractionRunResult } from "./orchestration/extraction-run.js";
|
|
29
|
+
import { TurnIngestionCoordinator, type TurnIngestionDeps } from "./orchestration/turn-ingestion.js";
|
|
22
30
|
import { BufferDrainClaims, type DrainLease } from "./storage/buffer-drain-claims.js";
|
|
23
|
-
import
|
|
31
|
+
import { bufferSessionFileName } from "./storage/buffer-session-files.js";
|
|
32
|
+
import { BufferSessionFiles } from "./storage/buffer-session-files.js";
|
|
24
33
|
import type { BufferState } from "./types.js";
|
|
25
|
-
import { TurnIngestionCoordinator, type TurnIngestionDeps } from "./orchestration/turn-ingestion.js";
|
|
26
34
|
import type { BufferEntryState, BufferTurn, PluginConfig } from "./types.js";
|
|
27
|
-
import { parseConfig } from "./config.js";
|
|
28
|
-
import type { ExtractionRunResult } from "./orchestration/extraction-run.js";
|
|
29
35
|
|
|
30
36
|
function plainIo() {
|
|
31
37
|
return {
|
|
@@ -85,18 +91,37 @@ function makeHarness(): Harness {
|
|
|
85
91
|
buffer,
|
|
86
92
|
claims,
|
|
87
93
|
storage,
|
|
88
|
-
lease: {
|
|
94
|
+
lease: {
|
|
95
|
+
ownerId: "worker-1",
|
|
96
|
+
pid: process.pid,
|
|
97
|
+
host: hostname(),
|
|
98
|
+
acquiredAt: now,
|
|
99
|
+
heartbeatAt: now,
|
|
100
|
+
leaseMs: 60_000,
|
|
101
|
+
},
|
|
89
102
|
cleanup: () => rmSync(stateDir, { recursive: true, force: true }),
|
|
90
103
|
};
|
|
91
104
|
}
|
|
92
105
|
|
|
106
|
+
/**
|
|
107
|
+
* SAFETY: the harness stubs two observers this scenario never reads; the cast
|
|
108
|
+
* re-types the stub to the contract the coordinator declares.
|
|
109
|
+
*/
|
|
110
|
+
function asStub<T>(cause: unknown): T {
|
|
111
|
+
// SAFETY: the caller supplies the stub shape the scenario exercises.
|
|
112
|
+
return cause as T;
|
|
113
|
+
}
|
|
114
|
+
|
|
93
115
|
test("a claimed session's save hands over and evicts instead of writing a session file", async () => {
|
|
94
116
|
const h = makeHarness();
|
|
95
117
|
try {
|
|
96
118
|
await h.buffer.load();
|
|
97
119
|
await h.buffer.addTurn("sess-1", turn("old-1"));
|
|
98
120
|
await h.buffer.addTurn("sess-1", turn("old-2"));
|
|
99
|
-
assert.ok(
|
|
121
|
+
assert.ok(
|
|
122
|
+
readdirSync(path.join(h.stateDir, "buffer")).some((name) => name.endsWith(".json")),
|
|
123
|
+
"seeded turns persisted as a session file"
|
|
124
|
+
);
|
|
100
125
|
|
|
101
126
|
const outcome = await h.claims.claimFromSessionFile("sess-1", h.lease);
|
|
102
127
|
assert.equal(outcome, "claimed");
|
|
@@ -112,7 +137,7 @@ test("a claimed session's save hands over and evicts instead of writing a sessio
|
|
|
112
137
|
assert.deepEqual(
|
|
113
138
|
claim.entry.turns.map((t) => t.content),
|
|
114
139
|
["old-1", "old-2", "new-3"],
|
|
115
|
-
"the owner's newest turn merges into the claim; nothing duplicates"
|
|
140
|
+
"the owner's newest turn merges into the claim; nothing duplicates"
|
|
116
141
|
);
|
|
117
142
|
const topLevel = readdirSync(path.join(h.stateDir, "buffer")).filter((name) => name.endsWith(".json"));
|
|
118
143
|
assert.deepEqual(topLevel, [], "a claimed session must never be written back as a session file");
|
|
@@ -126,11 +151,11 @@ test("a claimed session's save hands over and evicts instead of writing a sessio
|
|
|
126
151
|
assert.deepEqual(
|
|
127
152
|
claimAfter.entry.turns.map((t) => t.content),
|
|
128
153
|
["old-1", "old-2", "new-3", "post-eviction"],
|
|
129
|
-
"post-handover observes keep flowing into the claim"
|
|
154
|
+
"post-handover observes keep flowing into the claim"
|
|
130
155
|
);
|
|
131
156
|
assert.deepEqual(
|
|
132
157
|
readdirSync(path.join(h.stateDir, "buffer")).filter((name) => name.endsWith(".json")),
|
|
133
|
-
[]
|
|
158
|
+
[]
|
|
134
159
|
);
|
|
135
160
|
} finally {
|
|
136
161
|
h.cleanup();
|
|
@@ -148,11 +173,14 @@ test("handover of the default key clears the legacy top-level mirror", async ()
|
|
|
148
173
|
await h.buffer.save();
|
|
149
174
|
const claim = await h.claims.load("default");
|
|
150
175
|
assert.ok(claim);
|
|
151
|
-
assert.deepEqual(
|
|
176
|
+
assert.deepEqual(
|
|
177
|
+
claim.entry.turns.map((t) => t.content),
|
|
178
|
+
["legacy", "legacy-2"]
|
|
179
|
+
);
|
|
152
180
|
// No duplicate writes: the snapshot must settle empty after handover.
|
|
153
181
|
assert.deepEqual(
|
|
154
182
|
readdirSync(path.join(h.stateDir, "buffer")).filter((name) => name.endsWith(".json")),
|
|
155
|
-
[]
|
|
183
|
+
[]
|
|
156
184
|
);
|
|
157
185
|
} finally {
|
|
158
186
|
h.cleanup();
|
|
@@ -200,15 +228,20 @@ function gateFixture(isClaimed: boolean) {
|
|
|
200
228
|
void fn().catch(() => {});
|
|
201
229
|
return true;
|
|
202
230
|
},
|
|
203
|
-
runPreemptable: (
|
|
231
|
+
runPreemptable: (
|
|
232
|
+
_preempt: ExtractionPreemptHandle | undefined,
|
|
233
|
+
_preemptable: boolean,
|
|
234
|
+
parent: AbortSignal | undefined,
|
|
235
|
+
fn: (signal: AbortSignal) => Promise<void>
|
|
236
|
+
) => fn(parent ?? new AbortController().signal),
|
|
204
237
|
},
|
|
205
238
|
extractionWindowBudgetChars: () => 500,
|
|
206
239
|
getStorage: async () => ({ saveDroppedExtractionChunk: async () => "/dropped" }),
|
|
207
240
|
shouldQueueExtraction: () => true,
|
|
208
|
-
runExtraction:
|
|
241
|
+
runExtraction: async (turns: BufferTurn[]) => {
|
|
209
242
|
runs.push({ turns });
|
|
210
243
|
return { status: "completed", persistedCount: 0, durableOutputCount: 0 };
|
|
211
|
-
}
|
|
244
|
+
},
|
|
212
245
|
// SAFETY: the fixture provides exactly the deps the coordinator reads
|
|
213
246
|
// for the drain path (buffer, queue coordinator, budget, storage, dedupe
|
|
214
247
|
// gate and runExtraction); the cast only fills the unused members.
|
|
@@ -285,8 +318,12 @@ test("the owner keeps the claim while a drain is still running on it", async ()
|
|
|
285
318
|
extractionCount: 0,
|
|
286
319
|
});
|
|
287
320
|
assert.equal(saveOutcome?.status, "handed_over", "a running drain keeps its claim");
|
|
288
|
-
assert.equal(await h.claims.load("sess-1") !== null, true, "the claim survives the owner's mid-drain save");
|
|
289
|
-
assert.equal(
|
|
321
|
+
assert.equal((await h.claims.load("sess-1")) !== null, true, "the claim survives the owner's mid-drain save");
|
|
322
|
+
assert.equal(
|
|
323
|
+
existsSync(h.claims.leasePathForTest("sess-1")),
|
|
324
|
+
true,
|
|
325
|
+
"the lease survives the owner's mid-drain save"
|
|
326
|
+
);
|
|
290
327
|
|
|
291
328
|
// The drain finishes: everything extracted, the drainer acks (drops its
|
|
292
329
|
// lease, leaving the tombstone). Only now does the owner's save consume it.
|
|
@@ -305,3 +342,311 @@ test("the owner keeps the claim while a drain is still running on it", async ()
|
|
|
305
342
|
h.cleanup();
|
|
306
343
|
}
|
|
307
344
|
});
|
|
345
|
+
|
|
346
|
+
// ── In-process claim drain: isolated per-task execution (no global swap) ───
|
|
347
|
+
|
|
348
|
+
test("claim-backed queue tasks drain their own claims without a global buffer swap", async () => {
|
|
349
|
+
const h = makeHarness();
|
|
350
|
+
try {
|
|
351
|
+
await h.buffer.load();
|
|
352
|
+
const t0 = "2026-11-01T00:00:00.000Z";
|
|
353
|
+
await h.buffer.addTurn("sess-a", turn("a1", "sess-a", t0));
|
|
354
|
+
await h.buffer.addTurn("sess-b", turn("b1", "sess-b", t0));
|
|
355
|
+
await h.buffer.addTurn("sess-live", turn("live-1", "sess-live", t0));
|
|
356
|
+
await h.buffer.save();
|
|
357
|
+
const now = new Date().toISOString();
|
|
358
|
+
const mkLease = (ownerId: string): DrainLease => ({
|
|
359
|
+
ownerId,
|
|
360
|
+
pid: process.pid,
|
|
361
|
+
host: hostname(),
|
|
362
|
+
acquiredAt: now,
|
|
363
|
+
heartbeatAt: now,
|
|
364
|
+
leaseMs: 60_000,
|
|
365
|
+
});
|
|
366
|
+
assert.equal(await h.claims.claimFromSessionFile("sess-a", mkLease("drain-a")), "claimed");
|
|
367
|
+
assert.equal(await h.claims.claimFromSessionFile("sess-b", mkLease("drain-b")), "claimed");
|
|
368
|
+
|
|
369
|
+
// Identity tracker: a global swap (withBufferStorage) would replace this
|
|
370
|
+
// instance mid-task; the live deps read it live, exactly like selfDeps.
|
|
371
|
+
const liveHolder = { current: h.buffer };
|
|
372
|
+
|
|
373
|
+
// Per-key claim buffers over the SAME claim store (what the runner wires).
|
|
374
|
+
const claimBufferFor = async (key: string, ownerId: string): Promise<SmartBuffer> => {
|
|
375
|
+
const claim = await h.claims.load(key);
|
|
376
|
+
if (!claim) throw new Error(`test setup: claim for ${key} missing`);
|
|
377
|
+
const claimStorage = new ClaimBufferStorage(h.claims, key, ownerId, claim.entry);
|
|
378
|
+
return new SmartBuffer(makeConfig(), claimStorage);
|
|
379
|
+
};
|
|
380
|
+
const claimBuffers = new Map<string, SmartBuffer>([
|
|
381
|
+
["sess-a", await claimBufferFor("sess-a", "drain-a")],
|
|
382
|
+
["sess-b", await claimBufferFor("sess-b", "drain-b")],
|
|
383
|
+
]);
|
|
384
|
+
for (const claimBuffer of claimBuffers.values()) await claimBuffer.load();
|
|
385
|
+
|
|
386
|
+
const realQueue = new ExtractionQueueCoordinator(1);
|
|
387
|
+
const runs: { task: string; turns: string[]; liveAtCall: SmartBuffer }[] = [];
|
|
388
|
+
const liveRuns = new Set<string>();
|
|
389
|
+
const makeDrainStub =
|
|
390
|
+
(task: string, claimBuffer: SmartBuffer | undefined) =>
|
|
391
|
+
// Mimic extraction-run: read the turns handed over, clear them through
|
|
392
|
+
// the CURRENT live instance (a swap would clear into a claim buffer).
|
|
393
|
+
async (turns: BufferTurn[]): Promise<ExtractionRunResult> => {
|
|
394
|
+
runs.push({ task, turns: turns.map((t) => t.content), liveAtCall: liveHolder.current });
|
|
395
|
+
const target = claimBuffer ?? liveHolder.current;
|
|
396
|
+
await target.clearAfterExtraction(task, turns, { allowNonPrefix: true });
|
|
397
|
+
if (!claimBuffer) liveRuns.add(task);
|
|
398
|
+
return { status: "completed", persistedCount: turns.length, durableOutputCount: turns.length };
|
|
399
|
+
};
|
|
400
|
+
|
|
401
|
+
const liveStub = makeDrainStub("sess-live", undefined);
|
|
402
|
+
const executionFor = (key: string) => {
|
|
403
|
+
const claimBuffer = claimBuffers.get(key)!;
|
|
404
|
+
const drain = new ExtractionDrain(
|
|
405
|
+
{
|
|
406
|
+
config: {
|
|
407
|
+
extractionMinUserTurns: 1,
|
|
408
|
+
extractionMinChars: 0,
|
|
409
|
+
extractionMaxTurnChars: 100_000,
|
|
410
|
+
extractionChunkDropAfterFailures: 3,
|
|
411
|
+
},
|
|
412
|
+
extractionWindowBudgetChars: () => 500,
|
|
413
|
+
runExtraction: makeDrainStub(key, claimBuffer),
|
|
414
|
+
getStorage: async () => ({ saveDroppedExtractionChunk: async () => "/dropped" }),
|
|
415
|
+
buffer: claimBuffer,
|
|
416
|
+
},
|
|
417
|
+
{ dropFailedChunks: false }
|
|
418
|
+
);
|
|
419
|
+
return { buffer: claimBuffer, drain };
|
|
420
|
+
};
|
|
421
|
+
// SAFETY: the harness supplies exactly the members this scenario's
|
|
422
|
+
// TurnIngestionCoordinator reads; the remaining contract members are
|
|
423
|
+
// unreachable on this path.
|
|
424
|
+
const noopStorage = async () => ({ saveDroppedExtractionChunk: async () => "/dropped" });
|
|
425
|
+
const deps: TurnIngestionDeps = {
|
|
426
|
+
get buffer() {
|
|
427
|
+
return liveHolder.current;
|
|
428
|
+
},
|
|
429
|
+
bulkImportWriteNamespace: () => "bulk-import",
|
|
430
|
+
config: makeConfig(),
|
|
431
|
+
extractionQueueCoordinator: realQueue,
|
|
432
|
+
getStorage: async () => {
|
|
433
|
+
throw new Error("no storage on this path");
|
|
434
|
+
},
|
|
435
|
+
heartbeatObserverChains: new Map(),
|
|
436
|
+
lcmEngine: null,
|
|
437
|
+
passiveCorrectionDedup: new Set(),
|
|
438
|
+
passiveCorrectionTelemetry: {
|
|
439
|
+
detected: 0,
|
|
440
|
+
queued: 0,
|
|
441
|
+
autoApplied: 0,
|
|
442
|
+
suppressedReasonCounts: {},
|
|
443
|
+
},
|
|
444
|
+
passiveCorrectionService: () => {
|
|
445
|
+
throw new Error("passive corrections unreachable in this scenario");
|
|
446
|
+
},
|
|
447
|
+
shouldQueueExtraction: () => true,
|
|
448
|
+
runExtraction: liveStub,
|
|
449
|
+
queueBufferedExtraction: async () => undefined,
|
|
450
|
+
resolveMemoryIdOrHandle: () => "memory-1",
|
|
451
|
+
sessionObserver: asStub<SessionObserverState>({ turns: [], lastTurnAt: null }),
|
|
452
|
+
transcript: asStub<TranscriptManager>({ append: async () => undefined }),
|
|
453
|
+
};
|
|
454
|
+
const coordinator = new TurnIngestionCoordinator(deps);
|
|
455
|
+
|
|
456
|
+
const settled: Promise<void>[] = [];
|
|
457
|
+
const enqueueClaimTask = (key: string, ownerId: string): void => {
|
|
458
|
+
const claimBuffer = claimBuffers.get(key);
|
|
459
|
+
settled.push(
|
|
460
|
+
new Promise<void>((resolve, reject) => {
|
|
461
|
+
void coordinator
|
|
462
|
+
.queueBufferedExtraction(
|
|
463
|
+
// The claim's turns, grouped upstream (grouping is the runner's
|
|
464
|
+
// next commit; the seam carries whatever list it is given).
|
|
465
|
+
(claimBuffer?.getTurns(key) ?? []).map((t) => ({ ...t })),
|
|
466
|
+
"trigger_mode",
|
|
467
|
+
{
|
|
468
|
+
bufferKey: key,
|
|
469
|
+
clearBufferAfterExtraction: true,
|
|
470
|
+
clearMatchingTurns: true,
|
|
471
|
+
skipDedupeCheck: true,
|
|
472
|
+
skipCharThreshold: true,
|
|
473
|
+
skipUserTurnThreshold: true,
|
|
474
|
+
execution: executionFor(key),
|
|
475
|
+
onTaskSettled: (error) => (error ? reject(error) : resolve()),
|
|
476
|
+
}
|
|
477
|
+
)
|
|
478
|
+
.catch(reject);
|
|
479
|
+
})
|
|
480
|
+
);
|
|
481
|
+
};
|
|
482
|
+
enqueueClaimTask("sess-a", "drain-a");
|
|
483
|
+
enqueueClaimTask("sess-b", "drain-b");
|
|
484
|
+
// The live foreground capture arrives while the claim tasks hold the queue.
|
|
485
|
+
settled.push(
|
|
486
|
+
new Promise<void>((resolve, reject) => {
|
|
487
|
+
void coordinator
|
|
488
|
+
.queueBufferedExtraction([turn("live-2", "sess-live", "2026-11-01T00:01:00.000Z")], "trigger_mode", {
|
|
489
|
+
bufferKey: "sess-live",
|
|
490
|
+
clearBufferAfterExtraction: true,
|
|
491
|
+
onTaskSettled: (error) => (error ? reject(error) : resolve()),
|
|
492
|
+
})
|
|
493
|
+
.catch(reject);
|
|
494
|
+
})
|
|
495
|
+
);
|
|
496
|
+
await Promise.all(settled);
|
|
497
|
+
|
|
498
|
+
// No swap ever happened: every stub saw the live instance, and the live
|
|
499
|
+
// task's clear went through the live buffer (its turns are gone from it).
|
|
500
|
+
for (const run of runs) {
|
|
501
|
+
assert.equal(run.liveAtCall, h.buffer, "the live buffer instance must never be swapped mid-task");
|
|
502
|
+
}
|
|
503
|
+
assert.deepEqual(
|
|
504
|
+
runs.find((r) => r.task === "sess-a")?.turns,
|
|
505
|
+
["a1"],
|
|
506
|
+
"task a extracted only its own claim's turns"
|
|
507
|
+
);
|
|
508
|
+
assert.deepEqual(
|
|
509
|
+
runs.find((r) => r.task === "sess-b")?.turns,
|
|
510
|
+
["b1"],
|
|
511
|
+
"task b extracted only its own claim's turns"
|
|
512
|
+
);
|
|
513
|
+
assert.equal(liveRuns.has("sess-live"), true, "the live task ran through the live path");
|
|
514
|
+
assert.deepEqual(
|
|
515
|
+
h.buffer.getTurns("sess-live").map((t) => t.content),
|
|
516
|
+
["live-1"],
|
|
517
|
+
"the live task cleared only its queued snapshot through the live buffer"
|
|
518
|
+
);
|
|
519
|
+
// Claim progress landed in the RIGHT claim files (no cross-talk), and the
|
|
520
|
+
// claims' turns were extracted from the claims, not from any live copy.
|
|
521
|
+
assert.deepEqual((await h.claims.load("sess-a"))?.entry.turns ?? [], [], "claim a is drained in place");
|
|
522
|
+
assert.deepEqual((await h.claims.load("sess-b"))?.entry.turns ?? [], [], "claim b is drained in place");
|
|
523
|
+
assert.deepEqual(
|
|
524
|
+
h.buffer.getTurns("sess-a").map((t) => t.content),
|
|
525
|
+
["a1"],
|
|
526
|
+
"the live buffer's in-memory copy is untouched by the claim drain"
|
|
527
|
+
);
|
|
528
|
+
} finally {
|
|
529
|
+
h.cleanup();
|
|
530
|
+
}
|
|
531
|
+
});
|
|
532
|
+
|
|
533
|
+
/** Claim keys in the order `listPage` scans them: sorted claim file names
|
|
534
|
+
* (`list()` itself is directory-enumeration order, which is not sorted). */
|
|
535
|
+
async function claimKeysInListPageOrder(claims: BufferDrainClaims): Promise<string[]> {
|
|
536
|
+
const keys = (await claims.list()).map((listed) => listed.key);
|
|
537
|
+
const nameOf = new Map(keys.map((key) => [key, bufferSessionFileName(key)] as const));
|
|
538
|
+
const names = [...nameOf.values()].sort();
|
|
539
|
+
return keys.sort((left, right) => names.indexOf(nameOf.get(left)!) - names.indexOf(nameOf.get(right)!));
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
test("listPage never spends its page limit on empty tombstone claims", async () => {
|
|
543
|
+
const h = makeHarness();
|
|
544
|
+
try {
|
|
545
|
+
await h.buffer.load();
|
|
546
|
+
const keys = ["tomb-a", "tomb-b", "tomb-c", "tomb-d", "tomb-e", "tomb-pending"];
|
|
547
|
+
for (const key of keys) await h.buffer.addTurn(key, turn(`t-${key}`, key));
|
|
548
|
+
await h.buffer.save();
|
|
549
|
+
for (const key of keys) assert.equal(await h.claims.claimFromSessionFile(key, h.lease), "claimed");
|
|
550
|
+
// Claim file names are hashed: the LAST claim in listing order is the one
|
|
551
|
+
// left pending, so a limit counted against tombstones cannot reach it.
|
|
552
|
+
const order = await claimKeysInListPageOrder(h.claims);
|
|
553
|
+
const pending = order[order.length - 1]!;
|
|
554
|
+
for (const key of order) {
|
|
555
|
+
if (key === pending) continue;
|
|
556
|
+
const loaded = await h.claims.load(key);
|
|
557
|
+
await h.claims.writeEntry(key, h.lease.ownerId, entry([]), loaded!.entry);
|
|
558
|
+
assert.equal(await h.claims.ack(key, h.lease.ownerId), "acked");
|
|
559
|
+
}
|
|
560
|
+
const page = await h.claims.listPage(undefined, 1);
|
|
561
|
+
assert.deepEqual(
|
|
562
|
+
page.entries.map((listed) => listed.key),
|
|
563
|
+
[pending],
|
|
564
|
+
"the one pending claim is returned on the first page, past the tombstones"
|
|
565
|
+
);
|
|
566
|
+
assert.ok(page.entries.every((listed) => listed.turnCount > 0), "a page never lists an empty tombstone");
|
|
567
|
+
} finally {
|
|
568
|
+
h.cleanup();
|
|
569
|
+
}
|
|
570
|
+
});
|
|
571
|
+
|
|
572
|
+
test("listPage bounds the claim bodies it reads per call and resumes past a long tombstone run", async () => {
|
|
573
|
+
const h = makeHarness();
|
|
574
|
+
try {
|
|
575
|
+
await h.buffer.load();
|
|
576
|
+
// limit 1 reads at most 8 bodies per call: eight tombstones fill the
|
|
577
|
+
// budget exactly, so the pending claim behind them needs a second page.
|
|
578
|
+
const keys = Array.from({ length: 9 }, (_, index) => `run-${index}`);
|
|
579
|
+
for (const key of keys) await h.buffer.addTurn(key, turn(`t-${key}`, key));
|
|
580
|
+
await h.buffer.save();
|
|
581
|
+
for (const key of keys) assert.equal(await h.claims.claimFromSessionFile(key, h.lease), "claimed");
|
|
582
|
+
const order = await claimKeysInListPageOrder(h.claims);
|
|
583
|
+
const pending = order[order.length - 1]!;
|
|
584
|
+
for (const key of order) {
|
|
585
|
+
if (key === pending) continue;
|
|
586
|
+
const loaded = await h.claims.load(key);
|
|
587
|
+
await h.claims.writeEntry(key, h.lease.ownerId, entry([]), loaded!.entry);
|
|
588
|
+
assert.equal(await h.claims.ack(key, h.lease.ownerId), "acked");
|
|
589
|
+
}
|
|
590
|
+
const first = await h.claims.listPage(undefined, 1);
|
|
591
|
+
assert.deepEqual(first.entries, [], "the scan budget is spent on tombstones: nothing listed this call");
|
|
592
|
+
assert.notEqual(first.nextCursor, undefined, "the cursor advanced past every scanned name");
|
|
593
|
+
const second = await h.claims.listPage(first.nextCursor, 1);
|
|
594
|
+
assert.deepEqual(
|
|
595
|
+
second.entries.map((listed) => listed.key),
|
|
596
|
+
[pending],
|
|
597
|
+
"the next page resumes after the scanned run and reaches the pending claim"
|
|
598
|
+
);
|
|
599
|
+
} finally {
|
|
600
|
+
h.cleanup();
|
|
601
|
+
}
|
|
602
|
+
});
|
|
603
|
+
|
|
604
|
+
test("listPage returns exactly its limit when more pending claims exist", async () => {
|
|
605
|
+
const h = makeHarness();
|
|
606
|
+
try {
|
|
607
|
+
await h.buffer.load();
|
|
608
|
+
const keys = ["limit-a", "limit-b", "limit-c"];
|
|
609
|
+
for (const key of keys) await h.buffer.addTurn(key, turn(`t-${key}`, key));
|
|
610
|
+
await h.buffer.save();
|
|
611
|
+
for (const key of keys) assert.equal(await h.claims.claimFromSessionFile(key, h.lease), "claimed");
|
|
612
|
+
const page = await h.claims.listPage(undefined, 2);
|
|
613
|
+
assert.equal(page.entries.length, 2, "a page never exceeds its limit");
|
|
614
|
+
assert.notEqual(page.nextCursor, undefined, "a full page leaves a cursor for the remainder");
|
|
615
|
+
} finally {
|
|
616
|
+
h.cleanup();
|
|
617
|
+
}
|
|
618
|
+
});
|
|
619
|
+
|
|
620
|
+
test("listPage's cursor is the LAST CONSUMED claim, so a page boundary never skips one", async () => {
|
|
621
|
+
const h = makeHarness();
|
|
622
|
+
try {
|
|
623
|
+
await h.buffer.load();
|
|
624
|
+
for (const key of ["page-sess-a", "page-sess-b", "page-sess-c"]) {
|
|
625
|
+
await h.buffer.addTurn(key, turn(`t-${key}`, key));
|
|
626
|
+
}
|
|
627
|
+
await h.buffer.save();
|
|
628
|
+
for (const key of ["page-sess-a", "page-sess-b", "page-sess-c"]) {
|
|
629
|
+
assert.equal(await h.claims.claimFromSessionFile(key, h.lease), "claimed");
|
|
630
|
+
}
|
|
631
|
+
// Claim file names are hashed, so pagination is asserted order-free.
|
|
632
|
+
const seen = new Set<string>();
|
|
633
|
+
let cursor: string | undefined;
|
|
634
|
+
let pages = 0;
|
|
635
|
+
for (;;) {
|
|
636
|
+
const page = await h.claims.listPage(cursor, 2);
|
|
637
|
+
pages += 1;
|
|
638
|
+
for (const entry of page.entries) seen.add(entry.key);
|
|
639
|
+
if (page.nextCursor === undefined) break;
|
|
640
|
+
cursor = page.nextCursor;
|
|
641
|
+
if (pages > 10) throw new Error("pagination never exhausted");
|
|
642
|
+
}
|
|
643
|
+
assert.deepEqual(
|
|
644
|
+
[...seen].sort(),
|
|
645
|
+
["page-sess-a", "page-sess-b", "page-sess-c"],
|
|
646
|
+
"every page-boundary claim must be visited exactly once across rotating pages"
|
|
647
|
+
);
|
|
648
|
+
assert.equal(seen.size, pages === 2 ? 3 : 3, "no claim repeats across the rotation");
|
|
649
|
+
} finally {
|
|
650
|
+
h.cleanup();
|
|
651
|
+
}
|
|
652
|
+
});
|