@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
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import test from "node:test";
|
|
2
1
|
import assert from "node:assert/strict";
|
|
3
2
|
import { mkdtemp, rm } from "node:fs/promises";
|
|
4
3
|
import os from "node:os";
|
|
5
4
|
import path from "node:path";
|
|
5
|
+
import test from "node:test";
|
|
6
6
|
import { SmartBuffer } from "./buffer.js";
|
|
7
7
|
import { parseConfig } from "./config.js";
|
|
8
|
-
import { StorageManager } from "./storage.js";
|
|
9
|
-
import type { BufferTurn } from "./types.js";
|
|
10
8
|
import { ExtractionDrain } from "./orchestration/extraction-drain.js";
|
|
11
9
|
import type { ExtractionRunResult } from "./orchestration/extraction-run.js";
|
|
10
|
+
import { StorageManager } from "./storage.js";
|
|
11
|
+
import type { BufferTurn } from "./types.js";
|
|
12
12
|
|
|
13
13
|
function makeTurn(sessionKey: string, content: string): BufferTurn {
|
|
14
14
|
return {
|
|
@@ -20,7 +20,9 @@ function makeTurn(sessionKey: string, content: string): BufferTurn {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
/** A real buffer on real storage; `restart()` is what a process restart would load from disk. */
|
|
23
|
-
async function withBuffer(
|
|
23
|
+
async function withBuffer(
|
|
24
|
+
run: (buffer: SmartBuffer, restart: () => Promise<SmartBuffer>) => Promise<void>
|
|
25
|
+
): Promise<void> {
|
|
24
26
|
const root = await mkdtemp(path.join(os.tmpdir(), "remnic-drop-chunk-"));
|
|
25
27
|
try {
|
|
26
28
|
const buffer = new SmartBuffer(parseConfig({}), new StorageManager(root));
|
|
@@ -43,8 +45,15 @@ test("dropExtractionChunk removes the matched prefix and the drop survives a res
|
|
|
43
45
|
const dropped = await buffer.dropExtractionChunk("s1", turns.slice(0, 2));
|
|
44
46
|
|
|
45
47
|
assert.equal(dropped, 2);
|
|
46
|
-
assert.deepEqual(
|
|
47
|
-
|
|
48
|
+
assert.deepEqual(
|
|
49
|
+
buffer.getTurns("s1").map((turn) => turn.content),
|
|
50
|
+
["turn three"]
|
|
51
|
+
);
|
|
52
|
+
assert.deepEqual(
|
|
53
|
+
(await restart()).getTurns("s1").map((turn) => turn.content),
|
|
54
|
+
["turn three"],
|
|
55
|
+
"what a restart loads"
|
|
56
|
+
);
|
|
48
57
|
// A drop is not an extraction: the extraction bookkeeping stays untouched.
|
|
49
58
|
assert.equal(buffer.getExtractionCount("s1"), 0);
|
|
50
59
|
});
|
|
@@ -77,25 +86,54 @@ test("dropExtractionChunk drops the whole matched window and a restart finds the
|
|
|
77
86
|
});
|
|
78
87
|
});
|
|
79
88
|
|
|
80
|
-
const FAILED: ExtractionRunResult = {
|
|
89
|
+
const FAILED: ExtractionRunResult = {
|
|
90
|
+
status: "skipped",
|
|
91
|
+
reason: "empty_extraction_result",
|
|
92
|
+
retainedBuffer: true,
|
|
93
|
+
failureClass: "parse_empty",
|
|
94
|
+
persistedCount: 0,
|
|
95
|
+
durableOutputCount: 0,
|
|
96
|
+
};
|
|
81
97
|
const DONE: ExtractionRunResult = { status: "completed", persistedCount: 1, durableOutputCount: 1 };
|
|
82
98
|
|
|
83
99
|
/** w7K's probe on #3266: a pinned capture sits before a native group drained with clearMatchingTurns. */
|
|
84
|
-
async function drainBehindPinnedCapture(
|
|
100
|
+
async function drainBehindPinnedCapture(
|
|
101
|
+
buffer: SmartBuffer,
|
|
102
|
+
results: ExtractionRunResult[],
|
|
103
|
+
dropAfter: number,
|
|
104
|
+
overrides: {
|
|
105
|
+
getStorage?: () => Promise<{
|
|
106
|
+
saveDroppedExtractionChunk: (bufferKey: string, reason: string, turns: readonly BufferTurn[]) => Promise<string>;
|
|
107
|
+
}>;
|
|
108
|
+
runExtractionOverride?: () => Promise<ExtractionRunResult>;
|
|
109
|
+
} = {}
|
|
110
|
+
): Promise<ExtractionRunResult> {
|
|
85
111
|
const pinned: BufferTurn = { ...makeTurn("s1", "pinned capture"), acceptedCapture: true };
|
|
86
112
|
const native = [makeTurn("s1", "a".repeat(60)), makeTurn("s1", "b".repeat(60))];
|
|
87
113
|
for (const turn of [pinned, ...native]) await buffer.addTurn("s1", turn);
|
|
88
114
|
await buffer.flushPendingSave();
|
|
89
115
|
const drain = new ExtractionDrain({
|
|
90
|
-
config: {
|
|
116
|
+
config: {
|
|
117
|
+
extractionMinUserTurns: 1,
|
|
118
|
+
extractionMinChars: 0,
|
|
119
|
+
extractionMaxTurnChars: 100_000,
|
|
120
|
+
extractionChunkDropAfterFailures: dropAfter,
|
|
121
|
+
},
|
|
91
122
|
extractionWindowBudgetChars: () => 100,
|
|
92
123
|
runExtraction: async (turns, options) => {
|
|
124
|
+
if (overrides.runExtractionOverride) return await overrides.runExtractionOverride();
|
|
93
125
|
const result = results.shift() ?? DONE;
|
|
94
126
|
// A successful run clears its chunk the way the coordinator does for a native group: by identity.
|
|
95
|
-
if (result.status === "completed")
|
|
127
|
+
if (result.status === "completed")
|
|
128
|
+
await buffer.clearAfterExtraction(
|
|
129
|
+
"s1",
|
|
130
|
+
turns,
|
|
131
|
+
options?.clearMatchingTurns === true ? { allowNonPrefix: true } : undefined
|
|
132
|
+
);
|
|
96
133
|
return result;
|
|
97
134
|
},
|
|
98
|
-
getStorage:
|
|
135
|
+
getStorage:
|
|
136
|
+
overrides.getStorage ?? (async () => ({ saveDroppedExtractionChunk: async () => "/recovery/copy.json" })),
|
|
99
137
|
buffer,
|
|
100
138
|
});
|
|
101
139
|
return await drain.run(native, {
|
|
@@ -113,8 +151,16 @@ test("a dropped chunk behind a pinned capture is removed by identity, like a suc
|
|
|
113
151
|
const result = await drainBehindPinnedCapture(buffer, [FAILED, DONE], 1);
|
|
114
152
|
|
|
115
153
|
assert.equal(result.status, "completed");
|
|
116
|
-
assert.deepEqual(
|
|
117
|
-
|
|
154
|
+
assert.deepEqual(
|
|
155
|
+
buffer.getTurns("s1").map((turn) => turn.content),
|
|
156
|
+
["pinned capture"],
|
|
157
|
+
"the failed turn is gone, the pinned capture stays"
|
|
158
|
+
);
|
|
159
|
+
assert.deepEqual(
|
|
160
|
+
(await restart()).getTurns("s1").map((turn) => turn.content),
|
|
161
|
+
["pinned capture"],
|
|
162
|
+
"and a restart agrees"
|
|
163
|
+
);
|
|
118
164
|
});
|
|
119
165
|
});
|
|
120
166
|
|
|
@@ -131,10 +177,16 @@ test("a failed chunk made only of deferred-retained turns can be dropped, and a
|
|
|
131
177
|
|
|
132
178
|
assert.equal(dropped, 2);
|
|
133
179
|
assert.deepEqual(buffer.getRetainedDeferredTurns("s1"), []);
|
|
134
|
-
assert.deepEqual(
|
|
180
|
+
assert.deepEqual(
|
|
181
|
+
buffer.getTurns("s1").map((turn) => turn.content),
|
|
182
|
+
["live turn"]
|
|
183
|
+
);
|
|
135
184
|
const reloaded = await restart();
|
|
136
185
|
assert.deepEqual(reloaded.getRetainedDeferredTurns("s1"), [], "retained turns stay dropped after a restart");
|
|
137
|
-
assert.deepEqual(
|
|
186
|
+
assert.deepEqual(
|
|
187
|
+
reloaded.getTurns("s1").map((turn) => turn.content),
|
|
188
|
+
["live turn"]
|
|
189
|
+
);
|
|
138
190
|
});
|
|
139
191
|
});
|
|
140
192
|
|
|
@@ -149,7 +201,10 @@ test("a chunk that spans retained and live turns drops both", async () => {
|
|
|
149
201
|
const dropped = await buffer.dropExtractionChunk("s1", [...retained, live[0]!]);
|
|
150
202
|
|
|
151
203
|
assert.equal(dropped, 2);
|
|
152
|
-
assert.deepEqual(
|
|
204
|
+
assert.deepEqual(
|
|
205
|
+
(await restart()).getTurns("s1").map((turn) => turn.content),
|
|
206
|
+
["live two"]
|
|
207
|
+
);
|
|
153
208
|
});
|
|
154
209
|
});
|
|
155
210
|
|
|
@@ -161,7 +216,11 @@ test("deferred retention across chunks of one window stays within one retention
|
|
|
161
216
|
for (let chunk = 0; chunk < 3; chunk++) {
|
|
162
217
|
await buffer.retainDeferredTurns("s1", window.slice(chunk * 10, chunk * 10 + 10), 10, window);
|
|
163
218
|
}
|
|
164
|
-
assert.equal(
|
|
219
|
+
assert.equal(
|
|
220
|
+
buffer.getRetainedDeferredTurns("s1").length,
|
|
221
|
+
10,
|
|
222
|
+
"the latest chunk's retention replaces the earlier ones"
|
|
223
|
+
);
|
|
165
224
|
assert.equal((await restart()).getRetainedDeferredTurns("s1").length, 10);
|
|
166
225
|
});
|
|
167
226
|
});
|
|
@@ -176,10 +235,19 @@ test("a chunk holding only some retained turns drops those and keeps the rest of
|
|
|
176
235
|
const dropped = await buffer.dropExtractionChunk("s1", retained.slice(0, 2));
|
|
177
236
|
|
|
178
237
|
assert.equal(dropped, 2);
|
|
179
|
-
assert.deepEqual(
|
|
238
|
+
assert.deepEqual(
|
|
239
|
+
buffer.getRetainedDeferredTurns("s1").map((turn) => turn.content),
|
|
240
|
+
["retained three"]
|
|
241
|
+
);
|
|
180
242
|
const reloaded = await restart();
|
|
181
|
-
assert.deepEqual(
|
|
182
|
-
|
|
243
|
+
assert.deepEqual(
|
|
244
|
+
reloaded.getRetainedDeferredTurns("s1").map((turn) => turn.content),
|
|
245
|
+
["retained three"]
|
|
246
|
+
);
|
|
247
|
+
assert.deepEqual(
|
|
248
|
+
reloaded.getTurns("s1").map((turn) => turn.content),
|
|
249
|
+
["retained three", "live turn"]
|
|
250
|
+
);
|
|
183
251
|
});
|
|
184
252
|
});
|
|
185
253
|
|
|
@@ -189,7 +257,154 @@ test("retention for an early chunk leaves the retained turns of a later, unproce
|
|
|
189
257
|
await buffer.retainDeferredTurns("s1", retained);
|
|
190
258
|
// Chunk 0 holds the first retained turn; nothing was deferred in it, so it clears the slot for the turns it processed.
|
|
191
259
|
await buffer.retainDeferredTurns("s1", [], 0, retained.slice(0, 1));
|
|
192
|
-
assert.deepEqual(
|
|
193
|
-
|
|
260
|
+
assert.deepEqual(
|
|
261
|
+
buffer.getRetainedDeferredTurns("s1").map((turn) => turn.content),
|
|
262
|
+
["late retained"]
|
|
263
|
+
);
|
|
264
|
+
assert.deepEqual(
|
|
265
|
+
(await restart()).getRetainedDeferredTurns("s1").map((turn) => turn.content),
|
|
266
|
+
["late retained"]
|
|
267
|
+
);
|
|
268
|
+
});
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
test("a no-drop drain retains a repeatedly failing chunk as durable retryable backlog", async () => {
|
|
272
|
+
await withBuffer(async (buffer, restart) => {
|
|
273
|
+
const window = [makeTurn("s1", "a".repeat(60)), makeTurn("s1", "b".repeat(60))];
|
|
274
|
+
for (const turn of window) await buffer.addTurn("s1", turn);
|
|
275
|
+
await buffer.flushPendingSave();
|
|
276
|
+
|
|
277
|
+
let sideFiles = 0;
|
|
278
|
+
const drain = new ExtractionDrain(
|
|
279
|
+
{
|
|
280
|
+
config: {
|
|
281
|
+
extractionMinUserTurns: 1,
|
|
282
|
+
extractionMinChars: 0,
|
|
283
|
+
extractionMaxTurnChars: 100_000,
|
|
284
|
+
extractionChunkDropAfterFailures: 2,
|
|
285
|
+
},
|
|
286
|
+
extractionWindowBudgetChars: () => 100,
|
|
287
|
+
runExtraction: async () => {
|
|
288
|
+
throw new Error("provider down");
|
|
289
|
+
},
|
|
290
|
+
getStorage: async () => ({
|
|
291
|
+
saveDroppedExtractionChunk: async () => {
|
|
292
|
+
sideFiles += 1;
|
|
293
|
+
return "/recovery/copy.json";
|
|
294
|
+
},
|
|
295
|
+
}),
|
|
296
|
+
buffer,
|
|
297
|
+
},
|
|
298
|
+
{ dropFailedChunks: false }
|
|
299
|
+
);
|
|
300
|
+
|
|
301
|
+
const runOptions = {
|
|
302
|
+
bufferKey: "s1",
|
|
303
|
+
clearBufferAfterExtraction: true,
|
|
304
|
+
clearMatchingTurns: true,
|
|
305
|
+
skipUserTurnThreshold: false,
|
|
306
|
+
failOnExtractionFailure: false,
|
|
307
|
+
forceExtractionAttempt: false,
|
|
308
|
+
} as const;
|
|
309
|
+
|
|
310
|
+
// First window: the head chunk fails once (below the threshold) — it throws and stays.
|
|
311
|
+
await assert.rejects(() => drain.run(window, runOptions), /provider down/);
|
|
312
|
+
assert.equal(sideFiles, 0, "no recovery side-file below the threshold");
|
|
313
|
+
// Second window: the same head chunk reaches the drop threshold. The no-drop
|
|
314
|
+
// policy must RETAIN it — no side-file copy, no removal, no completion.
|
|
315
|
+
await assert.rejects(() => drain.run(window, runOptions), /provider down/);
|
|
316
|
+
assert.equal(sideFiles, 0, "a no-drop drain must never side-file a failed chunk");
|
|
317
|
+
assert.deepEqual(
|
|
318
|
+
(await restart()).getTurns("s1").map((turn) => turn.content),
|
|
319
|
+
["a".repeat(60), "b".repeat(60)],
|
|
320
|
+
"both chunks stay in the buffer as durable retryable backlog"
|
|
321
|
+
);
|
|
322
|
+
});
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
test("a failed chunk with NO recovery copy is kept as retryable backlog, not dropped", async () => {
|
|
326
|
+
await withBuffer(async (buffer, restart) => {
|
|
327
|
+
const result = await drainBehindPinnedCapture(buffer, [FAILED, FAILED, DONE], 1, {
|
|
328
|
+
getStorage: async () => ({
|
|
329
|
+
saveDroppedExtractionChunk: async () => {
|
|
330
|
+
throw new Error("disk full");
|
|
331
|
+
},
|
|
332
|
+
}),
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
// dropFailedChunk returns false: the only buffered copy stays and the
|
|
336
|
+
// drain stops on that chunk (later chunks are not acked past it).
|
|
337
|
+
assert.notEqual(result.status, "completed", "no silent drop without a recovery copy");
|
|
338
|
+
assert.deepEqual(
|
|
339
|
+
(await restart()).getTurns("s1").some((turn) => turn.content === "a".repeat(60)),
|
|
340
|
+
true,
|
|
341
|
+
"the failed chunk's content stays buffered for retry"
|
|
342
|
+
);
|
|
343
|
+
});
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
test("a failing buffer removal keeps the chunk: the drop is retried next trigger", async () => {
|
|
347
|
+
await withBuffer(async (buffer, restart) => {
|
|
348
|
+
buffer.dropExtractionChunk = async () => {
|
|
349
|
+
throw new Error("buffer busy");
|
|
350
|
+
};
|
|
351
|
+
const result = await drainBehindPinnedCapture(buffer, [FAILED, FAILED, DONE], 2);
|
|
352
|
+
|
|
353
|
+
assert.notEqual(result.status, "completed");
|
|
354
|
+
assert.deepEqual(
|
|
355
|
+
(await restart()).getTurns("s1").some((turn) => turn.content === "a".repeat(60)),
|
|
356
|
+
true,
|
|
357
|
+
"the chunk stays buffered when the buffer removal itself fails"
|
|
358
|
+
);
|
|
359
|
+
});
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
test("a chunk the buffer no longer matches is kept: nothing removed is not a drop", async () => {
|
|
363
|
+
await withBuffer(async (buffer, restart) => {
|
|
364
|
+
buffer.dropExtractionChunk = async () => 0;
|
|
365
|
+
const result = await drainBehindPinnedCapture(buffer, [FAILED, FAILED, DONE], 1);
|
|
366
|
+
|
|
367
|
+
assert.notEqual(result.status, "completed");
|
|
368
|
+
assert.deepEqual(
|
|
369
|
+
(await restart()).getTurns("s1").some((turn) => turn.content === "a".repeat(60)),
|
|
370
|
+
true,
|
|
371
|
+
"nothing removed means the chunk is still buffered"
|
|
372
|
+
);
|
|
373
|
+
});
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
test("one failure below the drop threshold keeps the chunk (no drop, no side file)", async () => {
|
|
377
|
+
await withBuffer(async (buffer, restart) => {
|
|
378
|
+
const result = await drainBehindPinnedCapture(buffer, [FAILED, DONE], 3);
|
|
379
|
+
|
|
380
|
+
assert.notEqual(result.status, "completed", "a single failure below the threshold retains the window");
|
|
381
|
+
assert.deepEqual(
|
|
382
|
+
(await restart()).getTurns("s1").some((turn) => turn.content === "a".repeat(60)),
|
|
383
|
+
true,
|
|
384
|
+
"the failing chunk stays buffered for the next trigger"
|
|
385
|
+
);
|
|
386
|
+
});
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
test("a thrown chunk dropped at the threshold must not end the drain: later chunks still run", async () => {
|
|
390
|
+
await withBuffer(async (buffer, restart) => {
|
|
391
|
+
// chunk1 THROWS (model crash); at dropAfter 1 it is side-file-copied and
|
|
392
|
+
// dropped — the live drop policy then CONTINUES to the next chunk.
|
|
393
|
+
let calls = 0;
|
|
394
|
+
const result = await drainBehindPinnedCapture(buffer, [DONE], 1, {
|
|
395
|
+
getStorage: async () => ({ saveDroppedExtractionChunk: async () => "/recovery/copy.json" }),
|
|
396
|
+
runExtractionOverride: async () => {
|
|
397
|
+
calls += 1;
|
|
398
|
+
if (calls === 1) throw new Error("model connection reset");
|
|
399
|
+
return DONE;
|
|
400
|
+
},
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
assert.equal(result.status, "completed", "the drain finishes with the later chunk");
|
|
404
|
+
assert.deepEqual(
|
|
405
|
+
(await restart()).getTurns("s1").filter((turn) => turn.content === "a".repeat(60)),
|
|
406
|
+
[],
|
|
407
|
+
"the thrown chunk was dropped by identity"
|
|
408
|
+
);
|
|
194
409
|
});
|
|
195
410
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { BufferEntryState, BufferTurn } from "./types.js";
|
|
1
|
+
import type { BufferEntryState, BufferState, BufferTurn } from "./types.js";
|
|
2
2
|
|
|
3
3
|
export interface BufferTurnOwner {
|
|
4
4
|
/**
|
|
@@ -120,6 +120,25 @@ export function subtractBufferTurns(turns: readonly BufferTurn[], drained: reado
|
|
|
120
120
|
return kept;
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
+
/** Per-key pending counts over the IN-MEMORY buffer state (counts only — no
|
|
124
|
+
* turn payloads). The claim store's answer stays authoritative for claimed
|
|
125
|
+
* keys; callers must filter through `BufferDrainClaims.isClaimed` before
|
|
126
|
+
* treating a count here as work. */
|
|
127
|
+
export interface BufferEntryCounts {
|
|
128
|
+
turns: number;
|
|
129
|
+
retainedTurns: number;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function bufferEntryCounts(state: BufferState): Map<string, BufferEntryCounts> {
|
|
133
|
+
const out = new Map<string, BufferEntryCounts>();
|
|
134
|
+
const entries = state.entries;
|
|
135
|
+
if (!entries) return out;
|
|
136
|
+
for (const [key, entry] of Object.entries(entries)) {
|
|
137
|
+
out.set(key, { turns: entry.turns.length, retainedTurns: entry.retainedTurns?.length ?? 0 });
|
|
138
|
+
}
|
|
139
|
+
return out;
|
|
140
|
+
}
|
|
141
|
+
|
|
123
142
|
export function bufferTurnsEqual(left: BufferTurn | undefined, right: BufferTurn): boolean {
|
|
124
143
|
if (!left) return false;
|
|
125
144
|
return (
|
|
@@ -200,6 +219,12 @@ export function groupTurnsByExtractionScope(turns: BufferTurn[]): ExtractionScop
|
|
|
200
219
|
turn.writeNamespaceOverride ?? "",
|
|
201
220
|
turn.principalOverride ?? "",
|
|
202
221
|
scopeProfilePlanIdentity(turn.scopeProfileWritePlan),
|
|
222
|
+
// Accepted captures are deliberately accepted content: they must never
|
|
223
|
+
// share a scope group with ordinary turns, or a group-level capture
|
|
224
|
+
// policy (the explicit-mode gate, the planless-accepted null pin) is
|
|
225
|
+
// applied to ordinary conversation turns because `.some(acceptedCapture)`
|
|
226
|
+
// only looked at the merged group.
|
|
227
|
+
turn.acceptedCapture === true ? "accepted" : "",
|
|
203
228
|
turn.codingContext?.projectId ?? "",
|
|
204
229
|
turn.codingContext?.rootPath ?? "",
|
|
205
230
|
turn.codingContext?.branch ?? "",
|
package/src/buffer.ts
CHANGED
|
@@ -17,6 +17,7 @@ import { scopeProfilePlanIdentity, subtractBufferTurns } from "./buffer-turn-hel
|
|
|
17
17
|
import { ExtractionDeadlineError } from "./orchestration/extraction-run.js";
|
|
18
18
|
import type { ExtractionBufferSnapshot } from "./extraction-liveness.js";
|
|
19
19
|
import {
|
|
20
|
+
bufferEntryCounts,
|
|
20
21
|
bufferTurnArrayIsSuffixOfSnapshot,
|
|
21
22
|
bufferTurnArraysEqual,
|
|
22
23
|
bufferTurnsEqual,
|
|
@@ -27,6 +28,7 @@ import {
|
|
|
27
28
|
matchingQueuedExtractionPrefixLength,
|
|
28
29
|
probeWithTimeout,
|
|
29
30
|
selectRetainedDeferredTurns,
|
|
31
|
+
type BufferEntryCounts,
|
|
30
32
|
} from "./buffer-turn-helpers.js";
|
|
31
33
|
|
|
32
34
|
export type TriggerDecision = "extract_now" | "extract_batch" | "keep_buffering";
|
|
@@ -332,6 +334,12 @@ export class SmartBuffer {
|
|
|
332
334
|
return this.storage.isBufferSessionClaimed?.(key) === true;
|
|
333
335
|
}
|
|
334
336
|
|
|
337
|
+
/** Per-key pending counts over the IN-MEMORY state (counts only — no turn
|
|
338
|
+
* payloads), for the daemon backlog scheduler's bounded candidate scan. */
|
|
339
|
+
entryCounts(): Map<string, BufferEntryCounts> {
|
|
340
|
+
return bufferEntryCounts(this.state);
|
|
341
|
+
}
|
|
342
|
+
|
|
335
343
|
async save(): Promise<void> {
|
|
336
344
|
await this.enqueueMutation(async () => this.saveUnlocked());
|
|
337
345
|
}
|
|
@@ -46,22 +46,22 @@ export type DrainVerdict = "drained" | "deferred" | "failed";
|
|
|
46
46
|
|
|
47
47
|
/**
|
|
48
48
|
* Run results that deliberately RETAIN the buffer (provider breaker open,
|
|
49
|
-
* per-fingerprint backoff) are deferrals, not chunk
|
|
50
|
-
* would drop good content
|
|
49
|
+
* per-fingerprint backoff, a scope policy refusal) are deferrals, not chunk
|
|
50
|
+
* failures — counting them would eventually drop good content that never got
|
|
51
|
+
* its attempt.
|
|
51
52
|
*/
|
|
52
53
|
const DRAIN_DEFERRAL_REASONS = {
|
|
53
54
|
provider_circuit_open: true,
|
|
54
55
|
extraction_backoff: true,
|
|
56
|
+
scope_profile_no_writable_layer: true,
|
|
55
57
|
};
|
|
56
58
|
|
|
57
59
|
export function classifyDrainResult(result: ExtractionRunResult): DrainVerdict {
|
|
58
60
|
if (result.status !== "skipped") return "drained";
|
|
59
|
-
// An empty result is a failure only when the run kept the window for retry;
|
|
60
|
-
// otherwise it extracted fine, found nothing durable, and cleared its turns.
|
|
61
|
-
if (result.reason === "empty_extraction_result") {
|
|
62
|
-
return result.retainedBuffer === true ? "failed" : "drained";
|
|
63
|
-
}
|
|
64
61
|
if (result.reason !== undefined && result.reason in DRAIN_DEFERRAL_REASONS) return "deferred";
|
|
62
|
+
// A run that kept its window for retry did NOT consume the content: it is a
|
|
63
|
+
// chunk failure, never a drain — even when the window was retained.
|
|
64
|
+
if (result.retainedBuffer === true) return "failed";
|
|
65
65
|
return "drained";
|
|
66
66
|
}
|
|
67
67
|
|
package/src/lcm/archive.ts
CHANGED
|
@@ -188,20 +188,72 @@ export class LcmArchive {
|
|
|
188
188
|
.all(sessionId, fromTurn, toTurn) as LcmMessage[];
|
|
189
189
|
}
|
|
190
190
|
|
|
191
|
-
/**
|
|
192
|
-
|
|
193
|
-
|
|
191
|
+
/** Highest turn index covered by a leaf summary (-1 when none exist). */
|
|
192
|
+
private lastLeafSummaryEnd(sessionId: string): number {
|
|
193
|
+
// SAFETY: the query returns exactly one aggregate column, `last_end`.
|
|
194
|
+
const row = this.db
|
|
194
195
|
.prepare(
|
|
195
196
|
"SELECT MAX(msg_end) as last_end FROM lcm_summary_nodes WHERE session_id = ? AND depth = 0",
|
|
196
197
|
)
|
|
197
198
|
.get(sessionId) as { last_end: number | null } | undefined;
|
|
199
|
+
return row?.last_end ?? -1;
|
|
200
|
+
}
|
|
198
201
|
|
|
199
|
-
|
|
200
|
-
|
|
202
|
+
/** Retrieve unsummarized messages (after last leaf summary), optionally bounded. */
|
|
203
|
+
getUnsummarizedMessages(sessionId: string, limit?: number): LcmMessage[] {
|
|
204
|
+
const lastSummarized = this.lastLeafSummaryEnd(sessionId);
|
|
205
|
+
if (limit !== undefined && limit <= 0) return [];
|
|
206
|
+
|
|
207
|
+
const stmt = this.db.prepare(
|
|
208
|
+
limit === undefined
|
|
209
|
+
? "SELECT * FROM lcm_messages WHERE session_id = ? AND turn_index > ? ORDER BY turn_index"
|
|
210
|
+
: "SELECT * FROM lcm_messages WHERE session_id = ? AND turn_index > ? ORDER BY turn_index LIMIT ?",
|
|
211
|
+
);
|
|
212
|
+
// SAFETY: the statement selects the lcm_messages row shape.
|
|
213
|
+
const rows = (
|
|
214
|
+
limit === undefined ? stmt.all(sessionId, lastSummarized) : stmt.all(sessionId, lastSummarized, limit)
|
|
215
|
+
) as LcmMessage[];
|
|
216
|
+
return rows;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/** Count archived turns that no leaf summary covers yet. */
|
|
220
|
+
countUnsummarizedMessages(sessionId: string): number {
|
|
221
|
+
const lastSummarized = this.lastLeafSummaryEnd(sessionId);
|
|
222
|
+
// SAFETY: the query returns exactly one aggregate column, `n`.
|
|
223
|
+
const row = this.db
|
|
224
|
+
.prepare(
|
|
225
|
+
"SELECT COUNT(*) as n FROM lcm_messages WHERE session_id = ? AND turn_index > ?",
|
|
226
|
+
)
|
|
227
|
+
.get(sessionId, lastSummarized) as { n: number };
|
|
228
|
+
return row.n;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Sessions whose durable archive still holds at least `minTurns`
|
|
233
|
+
* unsummarized turns. Metadata only — used to resume outstanding summary
|
|
234
|
+
* work after a restart.
|
|
235
|
+
*/
|
|
236
|
+
listSessionsWithUnsummarizedTurns(minTurns: number): string[] {
|
|
237
|
+
if (minTurns <= 0) return [];
|
|
238
|
+
// SAFETY: the query returns one `session_id` column per session.
|
|
239
|
+
const rows = this.db
|
|
201
240
|
.prepare(
|
|
202
|
-
|
|
241
|
+
`
|
|
242
|
+
SELECT m.session_id AS session_id
|
|
243
|
+
FROM lcm_messages m
|
|
244
|
+
LEFT JOIN (
|
|
245
|
+
SELECT session_id, MAX(msg_end) AS last_end
|
|
246
|
+
FROM lcm_summary_nodes
|
|
247
|
+
WHERE depth = 0
|
|
248
|
+
GROUP BY session_id
|
|
249
|
+
) leaves ON leaves.session_id = m.session_id
|
|
250
|
+
WHERE m.turn_index > COALESCE(leaves.last_end, -1)
|
|
251
|
+
GROUP BY m.session_id
|
|
252
|
+
HAVING COUNT(*) >= ?
|
|
253
|
+
`,
|
|
203
254
|
)
|
|
204
|
-
.all(
|
|
255
|
+
.all(minTurns) as Array<{ session_id: string }>;
|
|
256
|
+
return rows.map((row) => row.session_id);
|
|
205
257
|
}
|
|
206
258
|
|
|
207
259
|
/** Full-text search across all messages. */
|