@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,11 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Chunked trigger-drain for the extraction pipeline.
|
|
3
|
-
*
|
|
4
|
-
* Split out of `turn-ingestion.ts`: that file is at the structural ratchet's
|
|
5
|
-
* line cap (issue #1995), and the drain is a self-contained unit — it only
|
|
6
|
-
* needs the host's `runExtraction`, the buffer drop, the storage side-file
|
|
7
|
-
* copy and the preemption hook.
|
|
8
|
-
*/
|
|
1
|
+
/** Chunked trigger-drain for the extraction pipeline. */
|
|
9
2
|
|
|
10
3
|
import { createHash } from "node:crypto";
|
|
11
4
|
import { isAbortError, preemptedExtractionError } from "../abort-error.js";
|
|
@@ -36,12 +29,18 @@ export interface ExtractionDrainDeps {
|
|
|
36
29
|
extractionChunkDropAfterFailures: number;
|
|
37
30
|
};
|
|
38
31
|
extractionWindowBudgetChars?(): number;
|
|
32
|
+
/** Rendered-size oracle: the exact character count the extraction renderer */
|
|
33
|
+
measureRenderedTurns?(turns: readonly BufferTurn[]): number;
|
|
39
34
|
runExtraction(turns: BufferTurn[], options: RunOptions): Promise<ExtractionRunResult>;
|
|
40
35
|
getStorage(): Promise<{
|
|
41
36
|
saveDroppedExtractionChunk(bufferKey: string, reason: string, turns: readonly BufferTurn[]): Promise<string>;
|
|
42
37
|
}>;
|
|
43
38
|
buffer: {
|
|
44
|
-
dropExtractionChunk(
|
|
39
|
+
dropExtractionChunk(
|
|
40
|
+
bufferKey: string,
|
|
41
|
+
turns: readonly BufferTurn[],
|
|
42
|
+
options?: { allowNonPrefix?: boolean }
|
|
43
|
+
): Promise<number>;
|
|
45
44
|
};
|
|
46
45
|
}
|
|
47
46
|
|
|
@@ -52,14 +51,28 @@ function chunkFingerprint(chunk: readonly BufferTurn[]): string {
|
|
|
52
51
|
return hash.digest("hex");
|
|
53
52
|
}
|
|
54
53
|
|
|
54
|
+
/** The chunking decision for one trigger-drain window. */
|
|
55
|
+
interface ChunkPlan {
|
|
56
|
+
singleRun: boolean;
|
|
57
|
+
chunks: BufferTurn[][];
|
|
58
|
+
resumed: boolean;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** The drain-loop action for one chunk result. */
|
|
62
|
+
interface ChunkVerdict {
|
|
63
|
+
action: "continue" | "return" | "failed";
|
|
64
|
+
result: ExtractionRunResult;
|
|
65
|
+
aggregate: ExtractionRunResult | null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Per-chunk run options plus the chunk-local preemption controller. */
|
|
69
|
+
interface ChunkExecutionOptions {
|
|
70
|
+
options: DrainRunOptions;
|
|
71
|
+
preempt?: AbortController;
|
|
72
|
+
}
|
|
73
|
+
|
|
55
74
|
export class ExtractionDrain {
|
|
56
|
-
/**
|
|
57
|
-
* Consecutive failed attempts on the head chunk, per buffer key. In memory on
|
|
58
|
-
* purpose (a restart re-counts from zero). Bound to the chunk's content: a
|
|
59
|
-
* count recorded for one chunk never applies to a different one, so a force
|
|
60
|
-
* flush that clears a failing chunk cannot make unrelated content inherit its
|
|
61
|
-
* failures.
|
|
62
|
-
*/
|
|
75
|
+
/** Consecutive failed attempts on the head chunk, per buffer key. In memory on */
|
|
63
76
|
private readonly failures = new Map<string, { fingerprint: string; count: number }>();
|
|
64
77
|
|
|
65
78
|
/**
|
|
@@ -71,7 +84,15 @@ export class ExtractionDrain {
|
|
|
71
84
|
*/
|
|
72
85
|
private readonly qualified = new Map<string, string>();
|
|
73
86
|
|
|
74
|
-
|
|
87
|
+
/** The claim drain runs with `dropFailedChunks: false`: a chunk that keeps */
|
|
88
|
+
private readonly dropFailedChunks: boolean;
|
|
89
|
+
|
|
90
|
+
private readonly deps: ExtractionDrainDeps;
|
|
91
|
+
|
|
92
|
+
constructor(deps: ExtractionDrainDeps, options?: { dropFailedChunks?: boolean }) {
|
|
93
|
+
this.deps = deps;
|
|
94
|
+
this.dropFailedChunks = options?.dropFailedChunks !== false;
|
|
95
|
+
}
|
|
75
96
|
|
|
76
97
|
/** Characters the coordinator would count for this window: user/assistant text, trimmed and capped per turn, context-only turns left out. */
|
|
77
98
|
private windowChars(turns: readonly BufferTurn[]): number {
|
|
@@ -118,36 +139,77 @@ export class ExtractionDrain {
|
|
|
118
139
|
turns: BufferTurn[],
|
|
119
140
|
runOptions: DrainRunOptions,
|
|
120
141
|
preemptable = false,
|
|
121
|
-
preemptHandle?: ExtractionPreemptHandle
|
|
142
|
+
preemptHandle?: ExtractionPreemptHandle
|
|
122
143
|
): Promise<ExtractionRunResult> {
|
|
123
144
|
const budgetChars = this.deps.extractionWindowBudgetChars?.() ?? Number.POSITIVE_INFINITY;
|
|
124
|
-
const
|
|
125
|
-
runOptions.clearBufferAfterExtraction &&
|
|
126
|
-
!runOptions.forceExtractionAttempt &&
|
|
127
|
-
!runOptions.failOnExtractionFailure &&
|
|
128
|
-
!turns.some((turn) => turn.acceptedCapture === true) &&
|
|
129
|
-
Number.isFinite(budgetChars) &&
|
|
130
|
-
budgetChars > 0;
|
|
131
|
-
const chunks = chunkable ? chunkExtractionWindow(turns, budgetChars) : [];
|
|
132
|
-
const resumed =
|
|
133
|
-
chunks.length > 0 && this.qualified.get(runOptions.bufferKey) === chunkFingerprint(chunks[0].slice(0, 1));
|
|
145
|
+
const plan = this.chunkPlan(turns, runOptions, budgetChars);
|
|
134
146
|
// The tail of a window that already qualified goes through the chunk loop even when it now fits in one
|
|
135
147
|
// chunk, so its failures still count toward the drop threshold.
|
|
136
|
-
if (
|
|
148
|
+
if (plan.singleRun) return await this.deps.runExtraction(turns, runOptions);
|
|
137
149
|
// Eligibility (minimum user turns and characters) belongs to the whole
|
|
138
150
|
// window: a window that never qualified keeps the single-run path, which
|
|
139
151
|
// judges it exactly as before.
|
|
140
|
-
if (!resumed) {
|
|
141
|
-
|
|
142
|
-
const belowChars = !runOptions.skipCharThreshold && this.windowChars(turns) < this.deps.config.extractionMinChars;
|
|
143
|
-
if ((!runOptions.skipUserTurnThreshold && windowUserTurns < this.deps.config.extractionMinUserTurns) || belowChars) {
|
|
144
|
-
return await this.deps.runExtraction(turns, runOptions);
|
|
145
|
-
}
|
|
152
|
+
if (!plan.resumed && this.windowBelowThreshold(turns, runOptions)) {
|
|
153
|
+
return await this.deps.runExtraction(turns, runOptions);
|
|
146
154
|
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
155
|
+
return await this.runChunked(plan.chunks, runOptions, preemptable, preemptHandle);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** Chunking plan for one trigger-drain window. Accepted-capture groups */
|
|
159
|
+
private chunkPlan(
|
|
160
|
+
turns: readonly BufferTurn[],
|
|
161
|
+
runOptions: DrainRunOptions,
|
|
162
|
+
budgetChars: number
|
|
163
|
+
): ChunkPlan {
|
|
164
|
+
const chunks = this.chunkable(runOptions, turns, budgetChars)
|
|
165
|
+
? this.deps.measureRenderedTurns !== undefined
|
|
166
|
+
? this.chunkByRenderedSize(turns, budgetChars)
|
|
167
|
+
: chunkExtractionWindow(turns, budgetChars)
|
|
168
|
+
: [];
|
|
169
|
+
const resumed =
|
|
170
|
+
chunks.length > 0 && this.qualified.get(runOptions.bufferKey) === chunkFingerprint(chunks[0].slice(0, 1));
|
|
171
|
+
return { singleRun: chunks.length <= 1 && !resumed, chunks, resumed };
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/** Automatic chunking needs a clearable window, no fail-closed/forced
|
|
175
|
+
* caller, and a real budget to chunk against. */
|
|
176
|
+
private chunkable(runOptions: DrainRunOptions, turns: readonly BufferTurn[], budgetChars: number): boolean {
|
|
177
|
+
return (
|
|
178
|
+
runOptions.clearBufferAfterExtraction &&
|
|
179
|
+
!runOptions.forceExtractionAttempt &&
|
|
180
|
+
!runOptions.failOnExtractionFailure &&
|
|
181
|
+
this.identityCleared(runOptions, turns) &&
|
|
182
|
+
Number.isFinite(budgetChars) &&
|
|
183
|
+
budgetChars > 0
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/** A native group clears its turns by identity (a pinned capture can sit
|
|
188
|
+
* in front); a drop must find them the same way. */
|
|
189
|
+
private identityCleared(runOptions: DrainRunOptions, turns: readonly BufferTurn[]): boolean {
|
|
190
|
+
return runOptions.clearMatchingTurns === true || !turns.some((turn) => turn.acceptedCapture === true);
|
|
191
|
+
}
|
|
150
192
|
|
|
193
|
+
/** Eligibility (minimum user turns and characters) belongs to the whole
|
|
194
|
+
* window: a window that never qualified keeps the single-run path, which
|
|
195
|
+
* judges it exactly as before. */
|
|
196
|
+
private windowBelowThreshold(turns: readonly BufferTurn[], runOptions: DrainRunOptions): boolean {
|
|
197
|
+
const windowUserTurns = turns.filter((turn) => turn.role === "user").length;
|
|
198
|
+
const belowChars = !runOptions.skipCharThreshold && this.windowChars(turns) < this.deps.config.extractionMinChars;
|
|
199
|
+
return (
|
|
200
|
+
(!runOptions.skipUserTurnThreshold && windowUserTurns < this.deps.config.extractionMinUserTurns) || belowChars
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/** The window qualified as a whole; its chunks must not be re-judged one by
|
|
205
|
+
* one (an assistant-only or short chunk would be cleared unextracted). */
|
|
206
|
+
private async runChunked(
|
|
207
|
+
chunks: readonly BufferTurn[][],
|
|
208
|
+
runOptions: DrainRunOptions,
|
|
209
|
+
preemptable: boolean,
|
|
210
|
+
preemptHandle?: ExtractionPreemptHandle
|
|
211
|
+
): Promise<ExtractionRunResult> {
|
|
212
|
+
const baseOptions = { ...runOptions, skipCharThreshold: true, skipUserTurnThreshold: true };
|
|
151
213
|
const bufferKey = runOptions.bufferKey;
|
|
152
214
|
// A native group clears its turns by identity (a pinned capture can sit in front); a drop must find them the same way.
|
|
153
215
|
const byIdentity = runOptions.clearMatchingTurns === true;
|
|
@@ -155,78 +217,184 @@ export class ExtractionDrain {
|
|
|
155
217
|
let aggregate: ExtractionRunResult | null = null;
|
|
156
218
|
for (const [position, chunk] of chunks.entries()) {
|
|
157
219
|
this.qualified.set(bufferKey, chunkFingerprint(chunk.slice(0, 1)));
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
...chunkOptions,
|
|
168
|
-
abortSignal: runOptions.abortSignal ? AbortSignal.any([runOptions.abortSignal, local.signal]) : local.signal,
|
|
169
|
-
};
|
|
170
|
-
}
|
|
171
|
-
let failures = this.failureCount(bufferKey, chunk);
|
|
220
|
+
const { options: chunkOptions, preempt } = this.chunkOptionsFor(
|
|
221
|
+
chunks,
|
|
222
|
+
position,
|
|
223
|
+
baseOptions,
|
|
224
|
+
runOptions,
|
|
225
|
+
preemptable,
|
|
226
|
+
preemptHandle
|
|
227
|
+
);
|
|
228
|
+
const failures = this.failureCount(bufferKey, chunk);
|
|
172
229
|
let result: ExtractionRunResult;
|
|
173
230
|
try {
|
|
174
231
|
result = await this.deps.runExtraction(chunk, chunkOptions);
|
|
175
232
|
} catch (err) {
|
|
176
|
-
|
|
177
|
-
//
|
|
178
|
-
|
|
179
|
-
failures
|
|
180
|
-
if (failures < dropAfter) {
|
|
181
|
-
this.setFailureCount(bufferKey, chunk, failures);
|
|
182
|
-
throw err;
|
|
183
|
-
}
|
|
184
|
-
// A chunk that keeps throwing is dropped like one that keeps failing,
|
|
185
|
-
// so later chunks are not stuck behind it.
|
|
186
|
-
const thrown: ExtractionRunResult = {
|
|
187
|
-
status: "skipped",
|
|
188
|
-
reason: `thrown: ${describeError(err)}`,
|
|
189
|
-
persistedCount: 0,
|
|
190
|
-
durableOutputCount: 0,
|
|
191
|
-
};
|
|
192
|
-
if (!(await this.dropFailedChunk(bufferKey, chunk, dropAfter, thrown, byIdentity))) {
|
|
193
|
-
this.setFailureCount(bufferKey, chunk, failures);
|
|
194
|
-
throw err;
|
|
195
|
-
}
|
|
196
|
-
this.setFailureCount(bufferKey, chunk, 0);
|
|
233
|
+
this.rethrowChunkAbort(err, preempt, runOptions.abortSignal?.aborted === true);
|
|
234
|
+
// A successfully dropped thrown chunk does not end the drain: later
|
|
235
|
+
// chunks still run this pass (live drop policy).
|
|
236
|
+
aggregate = await this.settleThrownChunk(bufferKey, chunk, failures, dropAfter, err, byIdentity, aggregate);
|
|
197
237
|
continue;
|
|
198
238
|
}
|
|
199
|
-
const
|
|
200
|
-
if (
|
|
201
|
-
|
|
202
|
-
aggregate = mergeDrainResults(aggregate, result);
|
|
239
|
+
const settled = this.settleVerdict(bufferKey, chunk, result, aggregate);
|
|
240
|
+
if (settled.action === "continue") {
|
|
241
|
+
aggregate = settled.result;
|
|
203
242
|
continue;
|
|
204
243
|
}
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
}
|
|
218
|
-
this.setFailureCount(bufferKey, chunk, 0);
|
|
219
|
-
aggregate = mergeDrainResults(aggregate, result);
|
|
244
|
+
if (settled.action === "return") return settled.result;
|
|
245
|
+
const failed = await this.settleFailedChunk({
|
|
246
|
+
bufferKey,
|
|
247
|
+
chunk,
|
|
248
|
+
failures,
|
|
249
|
+
dropAfter,
|
|
250
|
+
result: settled.result,
|
|
251
|
+
byIdentity,
|
|
252
|
+
aggregate: settled.aggregate,
|
|
253
|
+
});
|
|
254
|
+
if (failed.action === "continue") {
|
|
255
|
+
aggregate = failed.result;
|
|
220
256
|
continue;
|
|
221
257
|
}
|
|
222
|
-
|
|
223
|
-
// Retain the remaining window for the next trigger (existing behavior).
|
|
224
|
-
return mergeDrainResults(aggregate, result);
|
|
258
|
+
return failed.result;
|
|
225
259
|
}
|
|
226
260
|
this.qualified.delete(bufferKey);
|
|
227
261
|
return aggregate ?? { status: "skipped", persistedCount: 0, durableOutputCount: 0 };
|
|
228
262
|
}
|
|
229
263
|
|
|
264
|
+
/** retention replaces theirs (retained turns do not pile up across chunks) */
|
|
265
|
+
private chunkOptionsFor(
|
|
266
|
+
chunks: readonly BufferTurn[][],
|
|
267
|
+
position: number,
|
|
268
|
+
baseOptions: DrainRunOptions,
|
|
269
|
+
runOptions: DrainRunOptions,
|
|
270
|
+
preemptable: boolean,
|
|
271
|
+
preemptHandle?: ExtractionPreemptHandle
|
|
272
|
+
): ChunkExecutionOptions {
|
|
273
|
+
let options: DrainRunOptions = { ...baseOptions, retentionWindow: chunks.slice(0, position + 1).flat() };
|
|
274
|
+
let preempt: AbortController | undefined;
|
|
275
|
+
if (preemptable && position > 0) {
|
|
276
|
+
preempt = this.armChunkPreempt(preemptHandle);
|
|
277
|
+
options = {
|
|
278
|
+
...options,
|
|
279
|
+
abortSignal: runOptions.abortSignal
|
|
280
|
+
? AbortSignal.any([runOptions.abortSignal, preempt.signal])
|
|
281
|
+
: preempt.signal,
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
return { options, preempt };
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/** A chunk-local abort controller so a fail-closed flush can abort the
|
|
288
|
+
* next chunk instead of waiting behind the whole window. */
|
|
289
|
+
private armChunkPreempt(preemptHandle?: ExtractionPreemptHandle): AbortController {
|
|
290
|
+
const local = new AbortController();
|
|
291
|
+
preemptHandle?.arm(() => local.abort(preemptedExtractionError()));
|
|
292
|
+
return local;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/** An abort error is rethrown (the chunk-local abort leaves the outer
|
|
296
|
+
* signal untouched, so the host gets the preemption reason it looks for,
|
|
297
|
+
* as it does for the first chunk). */
|
|
298
|
+
private rethrowChunkAbort(cause: unknown, preempt: AbortController | undefined, outerAborted: boolean): void {
|
|
299
|
+
if (!isAbortError(cause)) return;
|
|
300
|
+
throw preempt && preempt.signal.aborted && !outerAborted ? preempt.signal.reason : cause;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/** A chunk whose extraction THREW: count the failure; below the drop
|
|
304
|
+
* threshold (or on a no-drop drain) rethrow so the window is retained;
|
|
305
|
+
* at the threshold a chunk that keeps throwing is dropped like one that
|
|
306
|
+
* keeps failing, so later chunks are not stuck behind it. */
|
|
307
|
+
private async settleThrownChunk(
|
|
308
|
+
bufferKey: string,
|
|
309
|
+
chunk: readonly BufferTurn[],
|
|
310
|
+
failures: number,
|
|
311
|
+
dropAfter: number,
|
|
312
|
+
cause: unknown,
|
|
313
|
+
byIdentity: boolean,
|
|
314
|
+
aggregate: ExtractionRunResult | null
|
|
315
|
+
): Promise<ExtractionRunResult> {
|
|
316
|
+
failures += 1;
|
|
317
|
+
if (failures < dropAfter || !this.dropFailedChunks) {
|
|
318
|
+
this.setFailureCount(bufferKey, chunk, failures);
|
|
319
|
+
throw cause;
|
|
320
|
+
}
|
|
321
|
+
const thrown: ExtractionRunResult = {
|
|
322
|
+
status: "skipped",
|
|
323
|
+
reason: `thrown: ${describeError(cause)}`,
|
|
324
|
+
persistedCount: 0,
|
|
325
|
+
durableOutputCount: 0,
|
|
326
|
+
};
|
|
327
|
+
if (!(await this.dropFailedChunk(bufferKey, chunk, dropAfter, thrown, byIdentity))) {
|
|
328
|
+
this.setFailureCount(bufferKey, chunk, failures);
|
|
329
|
+
throw cause;
|
|
330
|
+
}
|
|
331
|
+
this.setFailureCount(bufferKey, chunk, 0);
|
|
332
|
+
return mergeDrainResults(aggregate, thrown);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/** Map one chunk result to the drain-loop action: drained chunks continue
|
|
336
|
+
* to the next chunk; breaker/backoff retention and provider faults keep
|
|
337
|
+
* everything for retry without counting a chunk failure. */
|
|
338
|
+
private settleVerdict(
|
|
339
|
+
bufferKey: string,
|
|
340
|
+
chunk: readonly BufferTurn[],
|
|
341
|
+
result: ExtractionRunResult,
|
|
342
|
+
aggregate: ExtractionRunResult | null
|
|
343
|
+
): ChunkVerdict {
|
|
344
|
+
const verdict = classifyDrainResult(result);
|
|
345
|
+
if (verdict === "drained") {
|
|
346
|
+
this.setFailureCount(bufferKey, chunk, 0);
|
|
347
|
+
return { action: "continue", result: mergeDrainResults(aggregate, result), aggregate };
|
|
348
|
+
}
|
|
349
|
+
// Provider breaker/backoff retention: keep everything for retry.
|
|
350
|
+
if (verdict === "deferred") return { action: "return", result: mergeDrainResults(aggregate, result), aggregate };
|
|
351
|
+
// An outage or a bad key is not the chunk's fault: keep the chunk and do not count toward dropping it.
|
|
352
|
+
if (result.failureClass === "provider_retryable" || result.failureClass === "auth_config") {
|
|
353
|
+
return { action: "return", result: mergeDrainResults(aggregate, result), aggregate };
|
|
354
|
+
}
|
|
355
|
+
return { action: "failed", result, aggregate };
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/** (live drains, recovery copy first) or keep it as durable retryable */
|
|
359
|
+
private async settleFailedChunk(args: {
|
|
360
|
+
bufferKey: string;
|
|
361
|
+
chunk: readonly BufferTurn[];
|
|
362
|
+
failures: number;
|
|
363
|
+
dropAfter: number;
|
|
364
|
+
result: ExtractionRunResult;
|
|
365
|
+
byIdentity: boolean;
|
|
366
|
+
aggregate: ExtractionRunResult | null;
|
|
367
|
+
}): Promise<{ action: "continue" | "return"; result: ExtractionRunResult }> {
|
|
368
|
+
const failures = args.failures + 1;
|
|
369
|
+
if (failures >= args.dropAfter && this.dropFailedChunks) {
|
|
370
|
+
if (!(await this.dropFailedChunk(args.bufferKey, args.chunk, args.dropAfter, args.result, args.byIdentity))) {
|
|
371
|
+
// No recovery copy: keep the only buffered copy and retry later.
|
|
372
|
+
this.setFailureCount(args.bufferKey, args.chunk, failures);
|
|
373
|
+
return { action: "return", result: mergeDrainResults(args.aggregate, args.result) };
|
|
374
|
+
}
|
|
375
|
+
this.setFailureCount(args.bufferKey, args.chunk, 0);
|
|
376
|
+
return { action: "continue", result: mergeDrainResults(args.aggregate, args.result) };
|
|
377
|
+
}
|
|
378
|
+
this.setFailureCount(args.bufferKey, args.chunk, failures);
|
|
379
|
+
return { action: "return", result: mergeDrainResults(args.aggregate, args.result) };
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/** Greedy chunking sized by the RENDERED conversation (the oracle the model */
|
|
383
|
+
private chunkByRenderedSize(turns: readonly BufferTurn[], budgetChars: number): BufferTurn[][] {
|
|
384
|
+
const measure = this.deps.measureRenderedTurns!;
|
|
385
|
+
const chunks: BufferTurn[][] = [];
|
|
386
|
+
let current: BufferTurn[] = [];
|
|
387
|
+
for (const turn of turns) {
|
|
388
|
+
if (current.length > 0 && measure([...current, turn]) > budgetChars) {
|
|
389
|
+
chunks.push(current);
|
|
390
|
+
current = [];
|
|
391
|
+
}
|
|
392
|
+
current.push(turn);
|
|
393
|
+
}
|
|
394
|
+
if (current.length > 0) chunks.push(current);
|
|
395
|
+
return chunks;
|
|
396
|
+
}
|
|
397
|
+
|
|
230
398
|
/** Side-file copy first (content survives a failed drop), then remove the failing chunk from the buffer. Never throws:
|
|
231
399
|
* the drop must not fail the extraction task that already settled its result. */
|
|
232
400
|
private async dropFailedChunk(
|
|
@@ -234,22 +402,14 @@ export class ExtractionDrain {
|
|
|
234
402
|
chunk: readonly BufferTurn[],
|
|
235
403
|
dropAfter: number,
|
|
236
404
|
result: ExtractionRunResult,
|
|
237
|
-
byIdentity: boolean
|
|
405
|
+
byIdentity: boolean
|
|
238
406
|
): Promise<boolean> {
|
|
239
407
|
const reason =
|
|
240
408
|
`dropped after ${dropAfter} repeated extraction failures ` +
|
|
241
409
|
`(last result: status=${result.status} reason=${result.reason ?? "n/a"})`;
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
log.warn(`buffer[${bufferKey}]: dropping ${chunk.length} turns — ${reason}; copy saved to ${path}`);
|
|
246
|
-
} catch (err) {
|
|
247
|
-
// Never destroy the only buffered copy without a recovery file.
|
|
248
|
-
log.warn(
|
|
249
|
-
`buffer[${bufferKey}]: could not save the dropped-chunk recovery copy; keeping the chunk in the buffer: ${describeError(err)}`,
|
|
250
|
-
);
|
|
251
|
-
return false;
|
|
252
|
-
}
|
|
410
|
+
const copyPath = await this.saveRecoveryCopy(bufferKey, reason, chunk);
|
|
411
|
+
if (copyPath === null) return false;
|
|
412
|
+
log.warn(`buffer[${bufferKey}]: dropping ${chunk.length} turns — ${reason}; copy saved to ${copyPath}`);
|
|
253
413
|
try {
|
|
254
414
|
const dropped = await this.deps.buffer.dropExtractionChunk(bufferKey, chunk, { allowNonPrefix: byIdentity });
|
|
255
415
|
if (dropped > 0) {
|
|
@@ -260,9 +420,28 @@ export class ExtractionDrain {
|
|
|
260
420
|
log.warn(`buffer[${bufferKey}]: dropped chunk no longer matches the buffer; keeping it and its failure count`);
|
|
261
421
|
} catch (err) {
|
|
262
422
|
log.warn(
|
|
263
|
-
`buffer[${bufferKey}]: failed to remove dropped chunk from buffer (will retry next trigger): ${describeError(err)}
|
|
423
|
+
`buffer[${bufferKey}]: failed to remove dropped chunk from buffer (will retry next trigger): ${describeError(err)}`
|
|
264
424
|
);
|
|
265
425
|
}
|
|
266
426
|
return false;
|
|
267
427
|
}
|
|
428
|
+
|
|
429
|
+
/** The recovery side-file copy for a dropped chunk. null = the copy could
|
|
430
|
+
* not be written — the caller must keep the only buffered copy. */
|
|
431
|
+
private async saveRecoveryCopy(
|
|
432
|
+
bufferKey: string,
|
|
433
|
+
reason: string,
|
|
434
|
+
chunk: readonly BufferTurn[]
|
|
435
|
+
): Promise<string | null> {
|
|
436
|
+
try {
|
|
437
|
+
const storage = await this.deps.getStorage();
|
|
438
|
+
return await storage.saveDroppedExtractionChunk(bufferKey, reason, chunk);
|
|
439
|
+
} catch (err) {
|
|
440
|
+
// Never destroy the only buffered copy without a recovery file.
|
|
441
|
+
log.warn(
|
|
442
|
+
`buffer[${bufferKey}]: could not save the dropped-chunk recovery copy; keeping the chunk in the buffer: ${describeError(err)}`
|
|
443
|
+
);
|
|
444
|
+
return null;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
268
447
|
}
|
|
@@ -79,6 +79,13 @@ export class ExtractionQueueCoordinator {
|
|
|
79
79
|
private accepting = true;
|
|
80
80
|
/** Buffer keys with a task currently running (per-key exclusion). */
|
|
81
81
|
private readonly activeKeys = new Set<string>();
|
|
82
|
+
/**
|
|
83
|
+
* Buffer keys with a claim DECISION in flight (daemon backlog scheduler).
|
|
84
|
+
* A reservation is taken synchronously BEFORE the async claim/decision so
|
|
85
|
+
* no queued task for the key can start past the gate while the decision is
|
|
86
|
+
* unsettled; selection skips reserved keys until release.
|
|
87
|
+
*/
|
|
88
|
+
private readonly reservedKeys = new Set<string>();
|
|
82
89
|
/** Preempt slots of the currently running tasks, keyed by slot identity. */
|
|
83
90
|
private readonly preemptSlots = new Map<object, PreemptSlot>();
|
|
84
91
|
private preemptSeq = 0;
|
|
@@ -133,6 +140,44 @@ export class ExtractionQueueCoordinator {
|
|
|
133
140
|
return true;
|
|
134
141
|
}
|
|
135
142
|
|
|
143
|
+
/**
|
|
144
|
+
* Synchronously reserve `key` so no task for it starts until the key's
|
|
145
|
+
* owner (the backlog scheduler's claim decision) settles and releases.
|
|
146
|
+
* Returns false when the key is active, queued, or already reserved —
|
|
147
|
+
* single-threaded JS makes this check+add atomic.
|
|
148
|
+
*/
|
|
149
|
+
tryReserveKey(key: string): boolean {
|
|
150
|
+
if (this.activeKeys.has(key) || this.reservedKeys.has(key)) return false;
|
|
151
|
+
if (this.queue.some((entry) => entry.key === key)) return false;
|
|
152
|
+
this.reservedKeys.add(key);
|
|
153
|
+
// The processor may be parked with ONLY reserved entries left: restart
|
|
154
|
+
// it so selection is re-evaluated (it will skip the reserved key).
|
|
155
|
+
if (!this.processing && this.queue.length > 0) {
|
|
156
|
+
this.processing = true;
|
|
157
|
+
this.processQueue().catch((err) => {
|
|
158
|
+
this.logExtractionQueueFailure(err, "processor");
|
|
159
|
+
this.processing = false;
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
return true;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/** Settle a reservation: the key's decision is made and enqueued work (if
|
|
166
|
+
* any) now carries the key authority itself. Idempotent; wakes a parked
|
|
167
|
+
* processor waiting only on this release. */
|
|
168
|
+
releaseKeyReservation(key: string): void {
|
|
169
|
+
if (!this.reservedKeys.delete(key)) return;
|
|
170
|
+
if (this.queue.length > 0 && !this.processing) {
|
|
171
|
+
this.processing = true;
|
|
172
|
+
this.processQueue().catch((err) => {
|
|
173
|
+
this.logExtractionQueueFailure(err, "processor");
|
|
174
|
+
this.processing = false;
|
|
175
|
+
});
|
|
176
|
+
} else {
|
|
177
|
+
this.wake?.();
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
136
181
|
preemptRunning(preferredKey?: string): void {
|
|
137
182
|
const armed = Array.from(this.preemptSlots.values()).filter((slot) => slot.armed !== undefined);
|
|
138
183
|
if (armed.length === 0) return;
|
|
@@ -200,7 +245,10 @@ export class ExtractionQueueCoordinator {
|
|
|
200
245
|
}
|
|
201
246
|
// Park until a task settles OR a new task is enqueued. A queued task
|
|
202
247
|
// whose key is still held always implies an in-flight task holding
|
|
203
|
-
// it, so inFlight is non-empty here whenever the loop must wait.
|
|
248
|
+
// it, so inFlight is non-empty here whenever the loop must wait. A
|
|
249
|
+
// queued task behind a RESERVATION (no in-flight holder) leaves the
|
|
250
|
+
// loop instead; releaseKeyReservation and enqueue restart the
|
|
251
|
+
// processor when it is not running.
|
|
204
252
|
if (inFlight.size === 0) break;
|
|
205
253
|
const { promise: wakePromise, resolve: wake } = Promise.withResolvers<void>();
|
|
206
254
|
this.wake = wake;
|
|
@@ -215,11 +263,11 @@ export class ExtractionQueueCoordinator {
|
|
|
215
263
|
}
|
|
216
264
|
}
|
|
217
265
|
|
|
218
|
-
/** Take the next queue entry whose key is not already running (FIFO within a key). */
|
|
266
|
+
/** Take the next queue entry whose key is not already running or reserved (FIFO within a key). */
|
|
219
267
|
private takeRunnable(): QueueEntry | undefined {
|
|
220
268
|
for (let index = 0; index < this.queue.length; index += 1) {
|
|
221
269
|
const entry = this.queue[index];
|
|
222
|
-
if (entry.key !== undefined && this.activeKeys.has(entry.key)) continue;
|
|
270
|
+
if (entry.key !== undefined && (this.activeKeys.has(entry.key) || this.reservedKeys.has(entry.key))) continue;
|
|
223
271
|
this.queue.splice(index, 1);
|
|
224
272
|
if (entry.key !== undefined) this.activeKeys.add(entry.key);
|
|
225
273
|
return entry;
|
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
} from "./extraction-run.js";
|
|
25
25
|
import { StorageManager } from "../storage.js";
|
|
26
26
|
import { parseConfig } from "../config.js";
|
|
27
|
+
import { ProviderCircuitBreaker } from "./provider-breaker.js";
|
|
27
28
|
import type { ExtractionEngine } from "../extraction.js";
|
|
28
29
|
import type { ThreadingManager } from "../threading.js";
|
|
29
30
|
import type { BufferTurn, ExtractionResult, ExtractionFailureClass, PluginConfig } from "../types.js";
|
|
@@ -102,6 +103,7 @@ interface Harness {
|
|
|
102
103
|
fn: ExtractionRunCoordinatorDeps["recordProcessedExtractionFingerprint"],
|
|
103
104
|
) => void;
|
|
104
105
|
recordedProcessedCount: () => number;
|
|
106
|
+
makeDeps: () => ExtractionRunCoordinatorDeps;
|
|
105
107
|
run: (
|
|
106
108
|
coord: ExtractionRunCoordinator,
|
|
107
109
|
content: string,
|
|
@@ -233,6 +235,7 @@ async function makeHarness(overrides: Record<string, unknown> = {}): Promise<Har
|
|
|
233
235
|
persistHandler = fn;
|
|
234
236
|
},
|
|
235
237
|
recordedProcessedCount: () => recordedProcessedCount,
|
|
238
|
+
makeDeps,
|
|
236
239
|
passiveCapture: () => passiveCapture,
|
|
237
240
|
run: async (coord, content, opts = {}) => {
|
|
238
241
|
const result = await coord.runExtraction(makeTurns(content), {
|
|
@@ -1416,3 +1419,41 @@ test("the retention window a chunk run is given reaches the buffer as the proces
|
|
|
1416
1419
|
await h.cleanup();
|
|
1417
1420
|
}
|
|
1418
1421
|
});
|
|
1422
|
+
|
|
1423
|
+
test("isolated claim drains share the orchestrator's provider breaker (review round)", async () => {
|
|
1424
|
+
const harness = await makeHarness({
|
|
1425
|
+
extractionBreakerFailureThreshold: 1,
|
|
1426
|
+
extractionBreakerCooldownMs: 60_000,
|
|
1427
|
+
});
|
|
1428
|
+
try {
|
|
1429
|
+
// extractionRunCoordinatorForBuffer builds a fresh coordinator per claim
|
|
1430
|
+
// drain; the ORCHESTRATOR-wide breaker must be the one they all consult.
|
|
1431
|
+
const shared = new ProviderCircuitBreaker();
|
|
1432
|
+
const makeIsolated = () =>
|
|
1433
|
+
new ExtractionRunCoordinator({ ...harness.makeDeps(), providerBreaker: shared });
|
|
1434
|
+
harness.setRespond(() => ({
|
|
1435
|
+
facts: [],
|
|
1436
|
+
profileUpdates: [],
|
|
1437
|
+
entities: [],
|
|
1438
|
+
questions: [],
|
|
1439
|
+
extractionFailure: "provider down",
|
|
1440
|
+
extractionFailureClass: "provider_retryable",
|
|
1441
|
+
}));
|
|
1442
|
+
|
|
1443
|
+
// The first failing attempt opens the SHARED breaker.
|
|
1444
|
+
const first = await harness.run(makeIsolated(), "breaker-share");
|
|
1445
|
+
assert.equal(first.reason, "empty_extraction_result");
|
|
1446
|
+
|
|
1447
|
+
// A DIFFERENT isolated coordinator (a fresh claim drain) must observe the
|
|
1448
|
+
// opened breaker instead of re-calling the failing provider. Distinct
|
|
1449
|
+
// fingerprint, so only the breaker can suppress this attempt.
|
|
1450
|
+
const second = await harness.run(makeIsolated(), "breaker-share-2");
|
|
1451
|
+
assert.equal(
|
|
1452
|
+
second.reason,
|
|
1453
|
+
"provider_circuit_open",
|
|
1454
|
+
"a fresh isolated drain must honor the orchestrator-wide breaker instead of hammering the failing provider",
|
|
1455
|
+
);
|
|
1456
|
+
} finally {
|
|
1457
|
+
await harness.cleanup();
|
|
1458
|
+
}
|
|
1459
|
+
});
|