@remnic/core 10.0.38 → 10.0.40
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 +73 -73
- package/dist/access-audit.js +2 -2
- package/dist/access-authorization-probe.js +74 -74
- package/dist/access-boundary.js +73 -73
- package/dist/access-citation-namespace.js +2 -2
- package/dist/access-cli.js +162 -161
- package/dist/access-cli.js.map +1 -1
- package/dist/access-extraction-force-flush.js +73 -73
- package/dist/access-http-briefing.js +74 -74
- package/dist/access-http-query.js +75 -75
- package/dist/access-http.js +98 -98
- package/dist/access-identity-continuity-surface.js +73 -73
- package/dist/access-lcm-surface.js +73 -73
- package/dist/access-mcp.js +87 -87
- package/dist/access-namespace-preflight.js +73 -73
- package/dist/access-observe-write-category.js +15 -15
- package/dist/access-observe-write-surface.js +73 -73
- package/dist/access-offline-manifest.js +12 -12
- package/dist/access-operations-batch.js +77 -77
- package/dist/access-operations.js +84 -84
- package/dist/access-recall-concurrency.js +73 -73
- package/dist/access-recall-diagnostics.js +2 -2
- package/dist/access-recall-response.js +73 -73
- package/dist/access-recall-surface.js +73 -73
- package/dist/access-schema.js +10 -10
- package/dist/access-service.d.ts +2 -2
- package/dist/access-service.d.ts.map +1 -1
- package/dist/access-service.js +73 -73
- package/dist/access-surface-catalog.js +75 -75
- package/dist/active-recall.js +29 -29
- package/dist/adapters/index.js +4 -4
- package/dist/adapters/registry.js +2 -2
- package/dist/{auto-sync-432USQDH.js → auto-sync-BNCBMU6H.js} +7 -7
- package/dist/briefing.js +19 -19
- package/dist/buffer-types.d.ts +59 -0
- package/dist/buffer-types.d.ts.map +1 -0
- package/dist/buffer-types.js +1 -0
- package/dist/buffer.d.ts +14 -22
- package/dist/buffer.d.ts.map +1 -1
- package/dist/buffer.js +11 -11
- package/dist/calibration.js +2 -2
- package/dist/{capsule-crypto-7FJQINUR.js → capsule-crypto-GWVG7LGC.js} +2 -2
- 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 +2 -2
- package/dist/causal-trajectory.js +1 -1
- package/dist/{chunk-MKE5DP3S.js → chunk-23ZMQONL.js} +2 -2
- package/dist/{chunk-3K5IEJTK.js → chunk-2AV2U2SZ.js} +3 -3
- package/dist/{chunk-HJ3UTGIW.js → chunk-2JNBPOZ2.js} +3 -3
- package/dist/{chunk-5ANUG4JZ.js → chunk-2T6KEOUL.js} +2 -2
- package/dist/{chunk-EEOYF3IB.js → chunk-3RJDYZJE.js} +2 -2
- package/dist/{chunk-P5CUEB2Q.js → chunk-4QLKVZH6.js} +8 -8
- package/dist/{chunk-XTBEXZFN.js → chunk-5SF7JOJB.js} +4 -4
- package/dist/{chunk-53BLBDV6.js → chunk-5TRU6QQC.js} +2 -2
- package/dist/{chunk-DKHX2FHI.js → chunk-64WHWIPL.js} +2 -2
- package/dist/{chunk-TVIGN3XZ.js → chunk-6FRFVIKC.js} +10 -10
- package/dist/{chunk-JIA435P3.js → chunk-6ITNEPJO.js} +2 -2
- package/dist/{chunk-WEK3OUGO.js → chunk-6X3J4NLZ.js} +10 -5
- package/dist/chunk-6X3J4NLZ.js.map +1 -0
- package/dist/{chunk-FGEY2AGV.js → chunk-7BCYAJKP.js} +2 -2
- package/dist/{chunk-PSQ2P2T3.js → chunk-7XZ7TOLQ.js} +3 -3
- package/dist/{chunk-6JEEY42N.js → chunk-AER3HD3B.js} +2 -2
- package/dist/{chunk-POGPI5GR.js → chunk-AY6LO7GD.js} +2 -2
- package/dist/{chunk-HXFIILGF.js → chunk-BFPF5N3H.js} +4 -4
- package/dist/{chunk-WCBLPKG3.js → chunk-BIYVUPC6.js} +172 -143
- package/dist/chunk-BIYVUPC6.js.map +1 -0
- package/dist/{chunk-KQAFEZQX.js → chunk-BUKK5SWA.js} +2 -2
- package/dist/{chunk-LHBOFROT.js → chunk-CHNZPOWX.js} +4 -3
- package/dist/{chunk-LHBOFROT.js.map → chunk-CHNZPOWX.js.map} +1 -1
- package/dist/{chunk-6F5BGIAF.js → chunk-CNAL35D5.js} +3 -3
- package/dist/{chunk-YC2C67F7.js → chunk-CRXGQRNA.js} +3 -3
- package/dist/{chunk-WRDFYSUC.js → chunk-CZVNOTLR.js} +2 -2
- package/dist/{chunk-VPO53QMG.js → chunk-D6DOLQYK.js} +2 -2
- package/dist/{chunk-RGKXF4W3.js → chunk-E5IEI2MR.js} +2 -2
- package/dist/{chunk-Z2T5BPOZ.js → chunk-EIYJK7OD.js} +2 -2
- package/dist/{chunk-FRAL3EU4.js → chunk-EKPRLTAE.js} +2 -2
- package/dist/{chunk-MCPSMWUC.js → chunk-EPEDMEHO.js} +10 -10
- package/dist/{chunk-MCPSMWUC.js.map → chunk-EPEDMEHO.js.map} +1 -1
- package/dist/{chunk-TYGLV6ZG.js → chunk-EYGOM6AI.js} +2 -2
- package/dist/{chunk-TYGLV6ZG.js.map → chunk-EYGOM6AI.js.map} +1 -1
- package/dist/{chunk-I75BWXRZ.js → chunk-F2RDRE7M.js} +2 -2
- package/dist/{chunk-2AKIVSHP.js → chunk-FAKRTS3Y.js} +2 -2
- package/dist/{chunk-MZWHIAOL.js → chunk-FC3ZFEXF.js} +2 -2
- package/dist/{chunk-3PLAL2KP.js → chunk-FJ264QBP.js} +17 -9
- package/dist/chunk-FJ264QBP.js.map +1 -0
- package/dist/{chunk-7VFNGQ4V.js → chunk-FLDXEJFX.js} +2 -2
- package/dist/{chunk-TYLNWE4D.js → chunk-FNEYSTB6.js} +2 -2
- package/dist/{chunk-XHX67LX6.js → chunk-FPHU6KVI.js} +2 -2
- package/dist/{chunk-JH4FCVWJ.js → chunk-G6OFEGUR.js} +2 -2
- package/dist/{chunk-WCIDCJOQ.js → chunk-GAUS4XGN.js} +3 -3
- package/dist/{chunk-SZPPCEYT.js → chunk-GDOI4U4D.js} +2 -2
- package/dist/{chunk-63YPKHAU.js → chunk-GLB6VV3J.js} +2 -2
- package/dist/{chunk-3XVBGGF3.js → chunk-GRN4BVPM.js} +2 -2
- package/dist/{chunk-ZAP7BKTZ.js → chunk-GZMHBRIR.js} +6 -6
- package/dist/{chunk-645WOU5I.js → chunk-H7BZBSP6.js} +4 -4
- package/dist/{chunk-TXP7Q7T5.js → chunk-HN35OH2C.js} +2 -2
- package/dist/{chunk-SWZOAQUM.js → chunk-HSNV4CSW.js} +57 -57
- package/dist/{chunk-GFYFKXWZ.js → chunk-HUDPXTXW.js} +2 -2
- package/dist/{chunk-C2KOW2UQ.js → chunk-I7CWAJHX.js} +2 -2
- package/dist/{chunk-L2JQSG5M.js → chunk-I7TR2OCU.js} +2 -2
- package/dist/{chunk-5DCS4C4S.js → chunk-ICTJOOWZ.js} +4 -4
- package/dist/{chunk-NH6GZEQN.js → chunk-IF6EG22P.js} +10 -10
- package/dist/chunk-IF6EG22P.js.map +1 -0
- package/dist/{chunk-STM4T335.js → chunk-IJH6METJ.js} +2 -2
- package/dist/{chunk-6SYS5YX3.js → chunk-IJJWM2ZA.js} +2 -2
- package/dist/{chunk-3AODCBKX.js → chunk-IWDFHVPJ.js} +6 -6
- package/dist/{chunk-XHVO3CGX.js → chunk-JGS55BAS.js} +53 -2
- package/dist/chunk-JGS55BAS.js.map +1 -0
- package/dist/{chunk-L7PGPH4E.js → chunk-JJSMSS45.js} +2 -2
- package/dist/{chunk-74AY3S7U.js → chunk-JNLYHJG7.js} +4 -4
- package/dist/{chunk-OLH4XCLU.js → chunk-JXQTECMU.js} +2 -2
- package/dist/{chunk-6QOL4G76.js → chunk-KQXRVELF.js} +2 -2
- package/dist/{chunk-PJRKLB4Z.js → chunk-L5PM7B3I.js} +16 -2
- package/dist/{chunk-PJRKLB4Z.js.map → chunk-L5PM7B3I.js.map} +1 -1
- package/dist/{chunk-VGRM43X6.js → chunk-LB6HIXQC.js} +2 -2
- package/dist/{chunk-N7DVBHU3.js → chunk-LR7RW4T6.js} +3 -3
- package/dist/{chunk-AYBAC2N4.js → chunk-LWIU5BMA.js} +3 -3
- package/dist/{chunk-FZVZYU5C.js → chunk-LXN737FX.js} +2 -2
- package/dist/{chunk-7ZRRJ6RG.js → chunk-MGYZENUY.js} +2 -2
- package/dist/{chunk-YQ6SLFNZ.js → chunk-MPJDQC4G.js} +3 -3
- package/dist/{chunk-7HXZOWTR.js → chunk-N3PUGD7E.js} +2 -2
- package/dist/{chunk-EGNONTVT.js → chunk-NBEM5QEH.js} +6 -6
- package/dist/{chunk-QO5KDQGG.js → chunk-NFJWB27H.js} +20 -18
- package/dist/chunk-NFJWB27H.js.map +1 -0
- package/dist/{chunk-JF3MEIHO.js → chunk-O67H7OXJ.js} +4 -4
- package/dist/{chunk-OLHN4ZNT.js → chunk-ONIVKGNC.js} +3 -3
- package/dist/{chunk-2NLLXCJG.js → chunk-OWHERGF2.js} +2 -2
- package/dist/{chunk-I73ZSORJ.js → chunk-OXSTKCYK.js} +4 -4
- package/dist/{chunk-UFM3PJDT.js → chunk-PARM6OYL.js} +1 -1
- package/dist/chunk-PARM6OYL.js.map +1 -0
- package/dist/{chunk-VSSQIBVU.js → chunk-PJXSBXEC.js} +2 -2
- package/dist/{chunk-4B37KNMP.js → chunk-PW63SXFM.js} +2 -2
- package/dist/{chunk-TPKB7T6Z.js → chunk-Q6UZREJK.js} +2 -2
- package/dist/{chunk-X247CYL6.js → chunk-Q77SLBWT.js} +4 -4
- package/dist/{chunk-TAEJ2MRR.js → chunk-QFD7OANX.js} +62 -62
- package/dist/chunk-QFD7OANX.js.map +1 -0
- package/dist/{chunk-5K23NQRI.js → chunk-QGNZ5QPV.js} +2 -2
- package/dist/{chunk-UJDTXWMT.js → chunk-QIMGULGN.js} +2 -2
- package/dist/{chunk-E24RUQEN.js → chunk-QNIYL5SK.js} +2 -2
- package/dist/{chunk-KOY25HUB.js → chunk-QYAXMXOB.js} +2 -2
- package/dist/{chunk-J7G656MY.js → chunk-R42VUN5V.js} +4 -4
- package/dist/{chunk-LOMSBYNS.js → chunk-RPHCSMTQ.js} +309 -106
- package/dist/chunk-RPHCSMTQ.js.map +1 -0
- package/dist/{chunk-AYXRK264.js → chunk-RROFIAYI.js} +9 -9
- package/dist/{chunk-YYGZ4EIY.js → chunk-RUAIO5UA.js} +2 -2
- package/dist/{chunk-E5PVMB3I.js → chunk-SDXBR5QO.js} +2 -2
- package/dist/{chunk-OKKBOTRA.js → chunk-SLVR3U4E.js} +2 -2
- package/dist/{chunk-CUWOSRCS.js → chunk-SUKPM6DV.js} +2 -2
- package/dist/{chunk-TDCR6PQL.js → chunk-TWRPWJJT.js} +2 -2
- package/dist/{chunk-6DTIYAAN.js → chunk-UGV256H3.js} +2 -2
- package/dist/{chunk-DG4H56SK.js → chunk-UIK3U36O.js} +2 -2
- package/dist/{chunk-KOAVX2IT.js → chunk-UKIIAEJF.js} +2 -2
- package/dist/{chunk-XLEP7SS3.js → chunk-UMSH4I3E.js} +2 -2
- package/dist/{chunk-X7Y7WX73.js → chunk-UVYI6VIX.js} +1 -1
- package/dist/chunk-VA3GNAWQ.js +58 -0
- package/dist/chunk-VA3GNAWQ.js.map +1 -0
- package/dist/{chunk-4P4VLPGB.js → chunk-VECRSYQG.js} +1 -1
- package/dist/{chunk-GGI6FLO6.js → chunk-VHBGJAYC.js} +2 -2
- package/dist/{chunk-NMKETOAX.js → chunk-VSHWPOUB.js} +2 -2
- package/dist/{chunk-VC3X7XP3.js → chunk-VTZSIG6O.js} +2 -2
- package/dist/{chunk-UBN5AOLT.js → chunk-W5EO4USJ.js} +2 -2
- package/dist/{chunk-Y7427SL7.js → chunk-WCGJK4NQ.js} +4 -4
- package/dist/{chunk-4IZ6AC2U.js → chunk-WJMAWDAU.js} +2 -2
- package/dist/{chunk-JH2ALMPR.js → chunk-WR5POHQ3.js} +2 -2
- package/dist/{chunk-EHIKY2IA.js → chunk-WU5WTCH5.js} +2 -2
- package/dist/{chunk-QKXPOLQD.js → chunk-WUBGMV3G.js} +6 -6
- package/dist/{chunk-FKTMWDN2.js → chunk-WUL6LDJ4.js} +2 -2
- package/dist/{chunk-5UPOKIFP.js → chunk-XBHUMPIQ.js} +12 -12
- package/dist/{chunk-Y5ZHZKZD.js → chunk-XJ77IOAD.js} +2 -2
- package/dist/{chunk-U7HJBODU.js → chunk-XKTHJHSQ.js} +5 -5
- package/dist/{chunk-FQFMLOAC.js → chunk-XLUMO5H2.js} +20 -20
- package/dist/chunk-XLUMO5H2.js.map +1 -0
- package/dist/{chunk-JFI3ATK4.js → chunk-XMYWYJM2.js} +2 -2
- package/dist/{chunk-64S6X4IU.js → chunk-XTN54NML.js} +4 -4
- package/dist/{chunk-CVMAOPAH.js → chunk-XXBCUIYQ.js} +10 -10
- package/dist/{chunk-QOUKZ2N3.js → chunk-XXW3P3RX.js} +2 -2
- package/dist/{chunk-RWRHGE75.js → chunk-Y473XFTD.js} +2 -2
- package/dist/{chunk-VGFNTY3Y.js → chunk-YFXRLRKP.js} +2 -2
- package/dist/{chunk-DAOMWGO2.js → chunk-ZX5N5D6M.js} +4 -4
- package/dist/cli.js +121 -121
- package/dist/coding/export-okf-codegraph.js +20 -20
- package/dist/coding/pre-action-gate.js +2 -2
- package/dist/compounding/engine.js +21 -21
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +29 -29
- package/dist/connectors/codex-materialize-runner.js +19 -19
- package/dist/connectors/index.js +19 -19
- package/dist/contradiction/index.js +5 -5
- package/dist/convergence-refresh.js +21 -21
- package/dist/corpus-watermark.js +21 -21
- package/dist/deep-recall-http-glue.js +11 -11
- package/dist/deep-recall.js +2 -2
- package/dist/entity-retrieval.js +20 -20
- package/dist/event-order-recall.js +2 -2
- package/dist/explicit-capture.js +6 -6
- package/dist/external-wiki-access.js +74 -74
- package/dist/extraction-error-classification.d.ts +15 -0
- package/dist/extraction-error-classification.d.ts.map +1 -1
- package/dist/extraction-error-classification.js +7 -3
- package/dist/extraction-window-drain.d.ts +48 -0
- package/dist/extraction-window-drain.d.ts.map +1 -0
- package/dist/extraction-window-drain.js +14 -0
- package/dist/extraction.js +28 -28
- package/dist/{graph-edge-decay-2BZTY2IM.js → graph-edge-decay-3HVLMT6U.js} +5 -5
- package/dist/graph-edge-decay-3HVLMT6U.js.map +1 -0
- package/dist/graph-jsonl.js +3 -3
- package/dist/graph-snapshot.js +3 -3
- package/dist/graph.js +4 -4
- package/dist/harmonic-retrieval.js +20 -20
- package/dist/harmonic-source-lookup.js +2 -2
- package/dist/index.js +209 -208
- package/dist/lcm/engine.js +2 -2
- package/dist/lcm/index.js +7 -7
- package/dist/local-llm.js +2 -2
- package/dist/location/index.js +22 -22
- package/dist/maintenance/memory-governance.js +19 -19
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +19 -19
- package/dist/maintenance/rebuild-memory-projection-runner.js +21 -21
- package/dist/maintenance/rebuild-memory-projection.js +20 -20
- package/dist/memory-browse-operations.js +74 -74
- package/dist/memory-browse.js +6 -6
- package/dist/memory-subject.js +3 -3
- package/dist/namespaces/migrate.js +20 -20
- package/dist/namespaces/search.d.ts +1 -0
- package/dist/namespaces/search.d.ts.map +1 -1
- package/dist/namespaces/search.js +11 -9
- package/dist/namespaces/storage.js +19 -19
- package/dist/offline-sync-changeset-generations.js +3 -3
- package/dist/offline-sync-embedding-generation.js +2 -2
- package/dist/offline-sync-file-io.js +2 -2
- package/dist/offline-sync-impression-drain.js +3 -3
- package/dist/offline-sync-snapshot-stream.js +8 -8
- package/dist/offline-sync-upload-staging.js +3 -3
- package/dist/offline-sync.js +9 -9
- package/dist/operator-doctor-corpus.js +22 -22
- package/dist/operator-toolkit.js +40 -40
- package/dist/orchestration/extraction-drain.d.ts +90 -0
- package/dist/orchestration/extraction-drain.d.ts.map +1 -0
- package/dist/orchestration/extraction-run-helpers.d.ts +3 -0
- package/dist/orchestration/extraction-run-helpers.d.ts.map +1 -1
- package/dist/orchestration/extraction-run.d.ts +4 -1
- package/dist/orchestration/extraction-run.d.ts.map +1 -1
- package/dist/orchestration/maintenance.js +33 -33
- package/dist/orchestration/qmd-result-resolver.d.ts +5 -0
- package/dist/orchestration/qmd-result-resolver.d.ts.map +1 -1
- package/dist/orchestration/recall-search-pipeline.d.ts.map +1 -1
- package/dist/orchestration/turn-ingestion.d.ts +3 -0
- package/dist/orchestration/turn-ingestion.d.ts.map +1 -1
- package/dist/orchestrator.d.ts +8 -0
- package/dist/orchestrator.d.ts.map +1 -1
- package/dist/orchestrator.js +162 -161
- package/dist/qmd.js +5 -5
- package/dist/recall-explain-renderer.js +3 -3
- package/dist/recall-navigate-access.js +2 -2
- package/dist/recall-navigate-disclosure.js +2 -2
- package/dist/recall-navigate.js +2 -2
- package/dist/recall-navigation-operations.js +74 -74
- package/dist/recall-navigation.js +73 -73
- package/dist/recall-state-view-admission.js +6 -6
- package/dist/recall-why-service.js +6 -6
- package/dist/recall-xray-cli.js +4 -4
- package/dist/recall-xray-renderer.js +3 -3
- package/dist/recall-xray.js +2 -2
- package/dist/reconcile/manifest.js +3 -3
- package/dist/{resolution-4ZEQPXUZ.js → resolution-NNYG37YB.js} +6 -6
- package/dist/resolution-NNYG37YB.js.map +1 -0
- package/dist/resume-bundles.js +30 -30
- package/dist/retrieval-agents.js +2 -2
- package/dist/salvage-envelope.js +5 -5
- package/dist/search/factory.js +6 -6
- package/dist/search/index.js +10 -10
- package/dist/secure-store/index.js +2 -2
- package/dist/semantic-consolidation.js +20 -20
- package/dist/semantic-rule-promotion.js +19 -19
- package/dist/semantic-rule-verifier.js +19 -19
- package/dist/source-agent-qualifier.js +23 -23
- package/dist/standup/build.js +19 -19
- package/dist/storage/dropped-extraction-chunk.d.ts +8 -0
- package/dist/storage/dropped-extraction-chunk.d.ts.map +1 -0
- package/dist/storage.d.ts +1 -0
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +18 -18
- package/dist/subject-config.js +4 -4
- package/dist/summarizer.js +3 -3
- package/dist/support-passport/index.js +76 -76
- 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 +20 -20
- package/dist/types.d.ts +7 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -1
- package/dist/verified-recall.js +19 -19
- package/dist/who-knows-cli.js +20 -20
- package/dist/who-knows.js +19 -19
- package/dist/write-envelope.js +4 -4
- package/package.json +2 -2
- package/src/access-entity-principal.test.ts +190 -0
- package/src/access-http.ts +2 -2
- package/src/access-operations-batch.ts +2 -2
- package/src/access-service.ts +7 -6
- package/src/buffer-drop-extraction-chunk.test.ts +196 -0
- package/src/buffer-types.ts +65 -0
- package/src/buffer.ts +60 -62
- package/src/chat/chat-executor.ts +1 -1
- package/src/config.ts +8 -0
- package/src/extraction-error-classification.ts +23 -0
- package/src/extraction-local-failure-class.test.ts +55 -0
- package/src/extraction-window-drain.test.ts +123 -0
- package/src/extraction-window-drain.ts +110 -0
- package/src/extraction.ts +9 -9
- package/src/namespaces/search.ts +1 -1
- package/src/orchestration/extraction-drain.ts +261 -0
- package/src/orchestration/extraction-run-helpers.ts +8 -0
- package/src/orchestration/extraction-run.test.ts +83 -2
- package/src/orchestration/extraction-run.ts +5 -4
- package/src/orchestration/qmd-result-resolver.ts +26 -1
- package/src/orchestration/recall-search-pipeline.ts +11 -12
- package/src/orchestration/turn-ingestion.ts +9 -5
- package/src/orchestrator.ts +21 -0
- package/src/secure-store/secure-fs.ts +3 -1
- package/src/storage/dropped-extraction-chunk.ts +27 -0
- package/src/storage-dropped-chunk-filenames.test.ts +30 -0
- package/src/storage.ts +6 -7
- package/src/types.ts +7 -0
- package/dist/chunk-3PLAL2KP.js.map +0 -1
- package/dist/chunk-FQFMLOAC.js.map +0 -1
- package/dist/chunk-LOMSBYNS.js.map +0 -1
- package/dist/chunk-NH6GZEQN.js.map +0 -1
- package/dist/chunk-QO5KDQGG.js.map +0 -1
- package/dist/chunk-TAEJ2MRR.js.map +0 -1
- package/dist/chunk-UFM3PJDT.js.map +0 -1
- package/dist/chunk-WCBLPKG3.js.map +0 -1
- package/dist/chunk-WEK3OUGO.js.map +0 -1
- package/dist/chunk-XHVO3CGX.js.map +0 -1
- /package/dist/{auto-sync-432USQDH.js.map → auto-sync-BNCBMU6H.js.map} +0 -0
- /package/dist/{capsule-crypto-7FJQINUR.js.map → buffer-types.js.map} +0 -0
- /package/dist/{graph-edge-decay-2BZTY2IM.js.map → capsule-crypto-GWVG7LGC.js.map} +0 -0
- /package/dist/{chunk-MKE5DP3S.js.map → chunk-23ZMQONL.js.map} +0 -0
- /package/dist/{chunk-3K5IEJTK.js.map → chunk-2AV2U2SZ.js.map} +0 -0
- /package/dist/{chunk-HJ3UTGIW.js.map → chunk-2JNBPOZ2.js.map} +0 -0
- /package/dist/{chunk-5ANUG4JZ.js.map → chunk-2T6KEOUL.js.map} +0 -0
- /package/dist/{chunk-EEOYF3IB.js.map → chunk-3RJDYZJE.js.map} +0 -0
- /package/dist/{chunk-P5CUEB2Q.js.map → chunk-4QLKVZH6.js.map} +0 -0
- /package/dist/{chunk-XTBEXZFN.js.map → chunk-5SF7JOJB.js.map} +0 -0
- /package/dist/{chunk-53BLBDV6.js.map → chunk-5TRU6QQC.js.map} +0 -0
- /package/dist/{chunk-DKHX2FHI.js.map → chunk-64WHWIPL.js.map} +0 -0
- /package/dist/{chunk-TVIGN3XZ.js.map → chunk-6FRFVIKC.js.map} +0 -0
- /package/dist/{chunk-JIA435P3.js.map → chunk-6ITNEPJO.js.map} +0 -0
- /package/dist/{chunk-FGEY2AGV.js.map → chunk-7BCYAJKP.js.map} +0 -0
- /package/dist/{chunk-PSQ2P2T3.js.map → chunk-7XZ7TOLQ.js.map} +0 -0
- /package/dist/{chunk-6JEEY42N.js.map → chunk-AER3HD3B.js.map} +0 -0
- /package/dist/{chunk-POGPI5GR.js.map → chunk-AY6LO7GD.js.map} +0 -0
- /package/dist/{chunk-HXFIILGF.js.map → chunk-BFPF5N3H.js.map} +0 -0
- /package/dist/{chunk-KQAFEZQX.js.map → chunk-BUKK5SWA.js.map} +0 -0
- /package/dist/{chunk-6F5BGIAF.js.map → chunk-CNAL35D5.js.map} +0 -0
- /package/dist/{chunk-YC2C67F7.js.map → chunk-CRXGQRNA.js.map} +0 -0
- /package/dist/{chunk-WRDFYSUC.js.map → chunk-CZVNOTLR.js.map} +0 -0
- /package/dist/{chunk-VPO53QMG.js.map → chunk-D6DOLQYK.js.map} +0 -0
- /package/dist/{chunk-RGKXF4W3.js.map → chunk-E5IEI2MR.js.map} +0 -0
- /package/dist/{chunk-Z2T5BPOZ.js.map → chunk-EIYJK7OD.js.map} +0 -0
- /package/dist/{chunk-FRAL3EU4.js.map → chunk-EKPRLTAE.js.map} +0 -0
- /package/dist/{chunk-I75BWXRZ.js.map → chunk-F2RDRE7M.js.map} +0 -0
- /package/dist/{chunk-2AKIVSHP.js.map → chunk-FAKRTS3Y.js.map} +0 -0
- /package/dist/{chunk-MZWHIAOL.js.map → chunk-FC3ZFEXF.js.map} +0 -0
- /package/dist/{chunk-7VFNGQ4V.js.map → chunk-FLDXEJFX.js.map} +0 -0
- /package/dist/{chunk-TYLNWE4D.js.map → chunk-FNEYSTB6.js.map} +0 -0
- /package/dist/{chunk-XHX67LX6.js.map → chunk-FPHU6KVI.js.map} +0 -0
- /package/dist/{chunk-JH4FCVWJ.js.map → chunk-G6OFEGUR.js.map} +0 -0
- /package/dist/{chunk-WCIDCJOQ.js.map → chunk-GAUS4XGN.js.map} +0 -0
- /package/dist/{chunk-SZPPCEYT.js.map → chunk-GDOI4U4D.js.map} +0 -0
- /package/dist/{chunk-63YPKHAU.js.map → chunk-GLB6VV3J.js.map} +0 -0
- /package/dist/{chunk-3XVBGGF3.js.map → chunk-GRN4BVPM.js.map} +0 -0
- /package/dist/{chunk-ZAP7BKTZ.js.map → chunk-GZMHBRIR.js.map} +0 -0
- /package/dist/{chunk-645WOU5I.js.map → chunk-H7BZBSP6.js.map} +0 -0
- /package/dist/{chunk-TXP7Q7T5.js.map → chunk-HN35OH2C.js.map} +0 -0
- /package/dist/{chunk-SWZOAQUM.js.map → chunk-HSNV4CSW.js.map} +0 -0
- /package/dist/{chunk-GFYFKXWZ.js.map → chunk-HUDPXTXW.js.map} +0 -0
- /package/dist/{chunk-C2KOW2UQ.js.map → chunk-I7CWAJHX.js.map} +0 -0
- /package/dist/{chunk-L2JQSG5M.js.map → chunk-I7TR2OCU.js.map} +0 -0
- /package/dist/{chunk-5DCS4C4S.js.map → chunk-ICTJOOWZ.js.map} +0 -0
- /package/dist/{chunk-STM4T335.js.map → chunk-IJH6METJ.js.map} +0 -0
- /package/dist/{chunk-6SYS5YX3.js.map → chunk-IJJWM2ZA.js.map} +0 -0
- /package/dist/{chunk-3AODCBKX.js.map → chunk-IWDFHVPJ.js.map} +0 -0
- /package/dist/{chunk-L7PGPH4E.js.map → chunk-JJSMSS45.js.map} +0 -0
- /package/dist/{chunk-74AY3S7U.js.map → chunk-JNLYHJG7.js.map} +0 -0
- /package/dist/{chunk-OLH4XCLU.js.map → chunk-JXQTECMU.js.map} +0 -0
- /package/dist/{chunk-6QOL4G76.js.map → chunk-KQXRVELF.js.map} +0 -0
- /package/dist/{chunk-VGRM43X6.js.map → chunk-LB6HIXQC.js.map} +0 -0
- /package/dist/{chunk-N7DVBHU3.js.map → chunk-LR7RW4T6.js.map} +0 -0
- /package/dist/{chunk-AYBAC2N4.js.map → chunk-LWIU5BMA.js.map} +0 -0
- /package/dist/{chunk-FZVZYU5C.js.map → chunk-LXN737FX.js.map} +0 -0
- /package/dist/{chunk-7ZRRJ6RG.js.map → chunk-MGYZENUY.js.map} +0 -0
- /package/dist/{chunk-YQ6SLFNZ.js.map → chunk-MPJDQC4G.js.map} +0 -0
- /package/dist/{chunk-7HXZOWTR.js.map → chunk-N3PUGD7E.js.map} +0 -0
- /package/dist/{chunk-EGNONTVT.js.map → chunk-NBEM5QEH.js.map} +0 -0
- /package/dist/{chunk-JF3MEIHO.js.map → chunk-O67H7OXJ.js.map} +0 -0
- /package/dist/{chunk-OLHN4ZNT.js.map → chunk-ONIVKGNC.js.map} +0 -0
- /package/dist/{chunk-2NLLXCJG.js.map → chunk-OWHERGF2.js.map} +0 -0
- /package/dist/{chunk-I73ZSORJ.js.map → chunk-OXSTKCYK.js.map} +0 -0
- /package/dist/{chunk-VSSQIBVU.js.map → chunk-PJXSBXEC.js.map} +0 -0
- /package/dist/{chunk-4B37KNMP.js.map → chunk-PW63SXFM.js.map} +0 -0
- /package/dist/{chunk-TPKB7T6Z.js.map → chunk-Q6UZREJK.js.map} +0 -0
- /package/dist/{chunk-X247CYL6.js.map → chunk-Q77SLBWT.js.map} +0 -0
- /package/dist/{chunk-5K23NQRI.js.map → chunk-QGNZ5QPV.js.map} +0 -0
- /package/dist/{chunk-UJDTXWMT.js.map → chunk-QIMGULGN.js.map} +0 -0
- /package/dist/{chunk-E24RUQEN.js.map → chunk-QNIYL5SK.js.map} +0 -0
- /package/dist/{chunk-KOY25HUB.js.map → chunk-QYAXMXOB.js.map} +0 -0
- /package/dist/{chunk-J7G656MY.js.map → chunk-R42VUN5V.js.map} +0 -0
- /package/dist/{chunk-AYXRK264.js.map → chunk-RROFIAYI.js.map} +0 -0
- /package/dist/{chunk-YYGZ4EIY.js.map → chunk-RUAIO5UA.js.map} +0 -0
- /package/dist/{chunk-E5PVMB3I.js.map → chunk-SDXBR5QO.js.map} +0 -0
- /package/dist/{chunk-OKKBOTRA.js.map → chunk-SLVR3U4E.js.map} +0 -0
- /package/dist/{chunk-CUWOSRCS.js.map → chunk-SUKPM6DV.js.map} +0 -0
- /package/dist/{chunk-TDCR6PQL.js.map → chunk-TWRPWJJT.js.map} +0 -0
- /package/dist/{chunk-6DTIYAAN.js.map → chunk-UGV256H3.js.map} +0 -0
- /package/dist/{chunk-DG4H56SK.js.map → chunk-UIK3U36O.js.map} +0 -0
- /package/dist/{chunk-KOAVX2IT.js.map → chunk-UKIIAEJF.js.map} +0 -0
- /package/dist/{chunk-XLEP7SS3.js.map → chunk-UMSH4I3E.js.map} +0 -0
- /package/dist/{chunk-X7Y7WX73.js.map → chunk-UVYI6VIX.js.map} +0 -0
- /package/dist/{chunk-4P4VLPGB.js.map → chunk-VECRSYQG.js.map} +0 -0
- /package/dist/{chunk-GGI6FLO6.js.map → chunk-VHBGJAYC.js.map} +0 -0
- /package/dist/{chunk-NMKETOAX.js.map → chunk-VSHWPOUB.js.map} +0 -0
- /package/dist/{chunk-VC3X7XP3.js.map → chunk-VTZSIG6O.js.map} +0 -0
- /package/dist/{chunk-UBN5AOLT.js.map → chunk-W5EO4USJ.js.map} +0 -0
- /package/dist/{chunk-Y7427SL7.js.map → chunk-WCGJK4NQ.js.map} +0 -0
- /package/dist/{chunk-4IZ6AC2U.js.map → chunk-WJMAWDAU.js.map} +0 -0
- /package/dist/{chunk-JH2ALMPR.js.map → chunk-WR5POHQ3.js.map} +0 -0
- /package/dist/{chunk-EHIKY2IA.js.map → chunk-WU5WTCH5.js.map} +0 -0
- /package/dist/{chunk-QKXPOLQD.js.map → chunk-WUBGMV3G.js.map} +0 -0
- /package/dist/{chunk-FKTMWDN2.js.map → chunk-WUL6LDJ4.js.map} +0 -0
- /package/dist/{chunk-5UPOKIFP.js.map → chunk-XBHUMPIQ.js.map} +0 -0
- /package/dist/{chunk-Y5ZHZKZD.js.map → chunk-XJ77IOAD.js.map} +0 -0
- /package/dist/{chunk-U7HJBODU.js.map → chunk-XKTHJHSQ.js.map} +0 -0
- /package/dist/{chunk-JFI3ATK4.js.map → chunk-XMYWYJM2.js.map} +0 -0
- /package/dist/{chunk-64S6X4IU.js.map → chunk-XTN54NML.js.map} +0 -0
- /package/dist/{chunk-CVMAOPAH.js.map → chunk-XXBCUIYQ.js.map} +0 -0
- /package/dist/{chunk-QOUKZ2N3.js.map → chunk-XXW3P3RX.js.map} +0 -0
- /package/dist/{chunk-RWRHGE75.js.map → chunk-Y473XFTD.js.map} +0 -0
- /package/dist/{chunk-VGFNTY3Y.js.map → chunk-YFXRLRKP.js.map} +0 -0
- /package/dist/{chunk-DAOMWGO2.js.map → chunk-ZX5N5D6M.js.map} +0 -0
- /package/dist/{resolution-4ZEQPXUZ.js.map → extraction-window-drain.js.map} +0 -0
|
@@ -0,0 +1,261 @@
|
|
|
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
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { createHash } from "node:crypto";
|
|
11
|
+
import { isAbortError, preemptedExtractionError } from "../abort-error.js";
|
|
12
|
+
import { describeError } from "../buffer-turn-helpers.js";
|
|
13
|
+
import { chunkExtractionWindow, classifyDrainResult, mergeDrainResults } from "../extraction-window-drain.js";
|
|
14
|
+
import { log } from "../logger.js";
|
|
15
|
+
import type { BufferTurn } from "../types.js";
|
|
16
|
+
import type { ExtractionRunCoordinator, ExtractionRunResult } from "./extraction-run.js";
|
|
17
|
+
|
|
18
|
+
type RunOptions = Parameters<ExtractionRunCoordinator["runExtraction"]>[1];
|
|
19
|
+
|
|
20
|
+
/** The `runExtraction` options the drain forwards, with the ones it reads made required. */
|
|
21
|
+
export type DrainRunOptions = RunOptions & {
|
|
22
|
+
bufferKey: string;
|
|
23
|
+
clearBufferAfterExtraction: boolean;
|
|
24
|
+
skipUserTurnThreshold: boolean;
|
|
25
|
+
failOnExtractionFailure: boolean;
|
|
26
|
+
forceExtractionAttempt: boolean;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/** What the drain needs from its host (structurally satisfied by `TurnIngestionDeps`). */
|
|
30
|
+
export interface ExtractionDrainDeps {
|
|
31
|
+
config: {
|
|
32
|
+
extractionMinUserTurns: number;
|
|
33
|
+
extractionMinChars: number;
|
|
34
|
+
extractionMaxTurnChars: number;
|
|
35
|
+
extractionChunkDropAfterFailures: number;
|
|
36
|
+
};
|
|
37
|
+
extractionWindowBudgetChars?(): number;
|
|
38
|
+
runExtraction(turns: BufferTurn[], options: RunOptions): Promise<ExtractionRunResult>;
|
|
39
|
+
getStorage(): Promise<{
|
|
40
|
+
saveDroppedExtractionChunk(bufferKey: string, reason: string, turns: readonly BufferTurn[]): Promise<string>;
|
|
41
|
+
}>;
|
|
42
|
+
buffer: {
|
|
43
|
+
dropExtractionChunk(bufferKey: string, turns: readonly BufferTurn[], options?: { allowNonPrefix?: boolean }): Promise<number>;
|
|
44
|
+
};
|
|
45
|
+
extractionQueueCoordinator: { beginPreemptable(abort: () => void): void };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Identity of a chunk: its failure count must not follow a buffer key to different content. */
|
|
49
|
+
function chunkFingerprint(chunk: readonly BufferTurn[]): string {
|
|
50
|
+
const hash = createHash("sha256");
|
|
51
|
+
for (const turn of chunk) hash.update(`${turn.role}\u0000${turn.timestamp}\u0000${turn.content}\u0001`);
|
|
52
|
+
return hash.digest("hex");
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
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
|
+
*/
|
|
63
|
+
private readonly failures = new Map<string, { fingerprint: string; count: number }>();
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Buffer key to the first turn of the chunk a qualified window stopped on.
|
|
67
|
+
* The rest of such a window stays eligible on the next attempt even when it
|
|
68
|
+
* is assistant-only or short: the window passed the thresholds as a whole,
|
|
69
|
+
* and re-judging its tail would clear it unextracted. Cleared when the
|
|
70
|
+
* window drains; one entry per key.
|
|
71
|
+
*/
|
|
72
|
+
private readonly qualified = new Map<string, string>();
|
|
73
|
+
|
|
74
|
+
constructor(private readonly deps: ExtractionDrainDeps) {}
|
|
75
|
+
|
|
76
|
+
/** Characters the coordinator would count for this window: user/assistant text, trimmed and capped per turn, context-only turns left out. */
|
|
77
|
+
private windowChars(turns: readonly BufferTurn[]): number {
|
|
78
|
+
let total = 0;
|
|
79
|
+
for (const turn of turns) {
|
|
80
|
+
if ((turn.role !== "user" && turn.role !== "assistant") || turn.extractionContextOnly === true) continue;
|
|
81
|
+
total += turn.content.trim().slice(0, this.deps.config.extractionMaxTurnChars).length;
|
|
82
|
+
}
|
|
83
|
+
return total;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
private failureCount(bufferKey: string, chunk: readonly BufferTurn[]): number {
|
|
87
|
+
const entry = this.failures.get(bufferKey);
|
|
88
|
+
return entry && entry.fingerprint === chunkFingerprint(chunk) ? entry.count : 0;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
private setFailureCount(bufferKey: string, chunk: readonly BufferTurn[], count: number): void {
|
|
92
|
+
if (count <= 0) this.failures.delete(bufferKey);
|
|
93
|
+
else this.failures.set(bufferKey, { fingerprint: chunkFingerprint(chunk), count });
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Run `runExtraction` over the trigger-drain window. When the window outgrows
|
|
98
|
+
* the extraction model's input budget (the stall that keeps a session's buffer
|
|
99
|
+
* growing forever: every observe re-fires the trigger on a window that can
|
|
100
|
+
* never extract), split it into budget-fitting chunks and drain them in
|
|
101
|
+
* order; each successful run clears its chunk, so the buffer shrinks across
|
|
102
|
+
* observes. A chunk that fails `extractionChunkDropAfterFailures` times in a
|
|
103
|
+
* row is dropped (side-file copy first). Breaker/backoff retention results
|
|
104
|
+
* stop the drain without counting as chunk failures.
|
|
105
|
+
*
|
|
106
|
+
* Explicit drains (replay/import `clearBufferAfterExtraction: false`, force
|
|
107
|
+
* flush, fail-closed callers, accepted captures) and windows that fit the
|
|
108
|
+
* budget (or hosts without the budget accessor) take the single-run path.
|
|
109
|
+
*
|
|
110
|
+
* `preemptable`: the run was queued as preemptable automatic work. Each chunk's
|
|
111
|
+
* persist step ends the host's preemption registration, so the drain registers
|
|
112
|
+
* it again before every later chunk; a fail-closed flush can then abort the
|
|
113
|
+
* next chunk's model request instead of waiting behind the whole window.
|
|
114
|
+
*/
|
|
115
|
+
async run(turns: BufferTurn[], runOptions: DrainRunOptions, preemptable = false): Promise<ExtractionRunResult> {
|
|
116
|
+
const budgetChars = this.deps.extractionWindowBudgetChars?.() ?? Number.POSITIVE_INFINITY;
|
|
117
|
+
const chunkable =
|
|
118
|
+
runOptions.clearBufferAfterExtraction &&
|
|
119
|
+
!runOptions.forceExtractionAttempt &&
|
|
120
|
+
!runOptions.failOnExtractionFailure &&
|
|
121
|
+
!turns.some((turn) => turn.acceptedCapture === true) &&
|
|
122
|
+
Number.isFinite(budgetChars) &&
|
|
123
|
+
budgetChars > 0;
|
|
124
|
+
const chunks = chunkable ? chunkExtractionWindow(turns, budgetChars) : [];
|
|
125
|
+
const resumed =
|
|
126
|
+
chunks.length > 0 && this.qualified.get(runOptions.bufferKey) === chunkFingerprint(chunks[0].slice(0, 1));
|
|
127
|
+
// The tail of a window that already qualified goes through the chunk loop even when it now fits in one
|
|
128
|
+
// chunk, so its failures still count toward the drop threshold.
|
|
129
|
+
if (chunks.length <= 1 && !resumed) return await this.deps.runExtraction(turns, runOptions);
|
|
130
|
+
// Eligibility (minimum user turns and characters) belongs to the whole
|
|
131
|
+
// window: a window that never qualified keeps the single-run path, which
|
|
132
|
+
// judges it exactly as before.
|
|
133
|
+
if (!resumed) {
|
|
134
|
+
const windowUserTurns = turns.filter((turn) => turn.role === "user").length;
|
|
135
|
+
const belowChars = !runOptions.skipCharThreshold && this.windowChars(turns) < this.deps.config.extractionMinChars;
|
|
136
|
+
if ((!runOptions.skipUserTurnThreshold && windowUserTurns < this.deps.config.extractionMinUserTurns) || belowChars) {
|
|
137
|
+
return await this.deps.runExtraction(turns, runOptions);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
// The window qualified as a whole; its chunks must not be re-judged one by
|
|
141
|
+
// one (an assistant-only or short chunk would be cleared unextracted).
|
|
142
|
+
const baseOptions = { ...runOptions, skipCharThreshold: true, skipUserTurnThreshold: true };
|
|
143
|
+
|
|
144
|
+
const bufferKey = runOptions.bufferKey;
|
|
145
|
+
// A native group clears its turns by identity (a pinned capture can sit in front); a drop must find them the same way.
|
|
146
|
+
const byIdentity = runOptions.clearMatchingTurns === true;
|
|
147
|
+
const dropAfter = this.deps.config.extractionChunkDropAfterFailures;
|
|
148
|
+
let aggregate: ExtractionRunResult | null = null;
|
|
149
|
+
for (const [position, chunk] of chunks.entries()) {
|
|
150
|
+
this.qualified.set(bufferKey, chunkFingerprint(chunk.slice(0, 1)));
|
|
151
|
+
// retentionWindow: the chunks processed so far. A chunk's deferred retention replaces theirs (retained turns do not pile
|
|
152
|
+
// up across chunks) and leaves the retained turns of chunks not yet processed in the buffer.
|
|
153
|
+
let chunkOptions: DrainRunOptions = { ...baseOptions, retentionWindow: chunks.slice(0, position + 1).flat() };
|
|
154
|
+
let preempt: AbortController | undefined;
|
|
155
|
+
if (preemptable && position > 0) {
|
|
156
|
+
const local = new AbortController();
|
|
157
|
+
preempt = local;
|
|
158
|
+
this.deps.extractionQueueCoordinator.beginPreemptable(() => local.abort(preemptedExtractionError()));
|
|
159
|
+
chunkOptions = {
|
|
160
|
+
...chunkOptions,
|
|
161
|
+
abortSignal: runOptions.abortSignal ? AbortSignal.any([runOptions.abortSignal, local.signal]) : local.signal,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
let failures = this.failureCount(bufferKey, chunk);
|
|
165
|
+
let result: ExtractionRunResult;
|
|
166
|
+
try {
|
|
167
|
+
result = await this.deps.runExtraction(chunk, chunkOptions);
|
|
168
|
+
} catch (err) {
|
|
169
|
+
// The chunk-local abort leaves the outer signal untouched, so hand back the
|
|
170
|
+
// preemption reason the host looks for (as it does for the first chunk).
|
|
171
|
+
if (isAbortError(err)) throw preempt && preempt.signal.aborted && !runOptions.abortSignal?.aborted ? preempt.signal.reason : err;
|
|
172
|
+
failures += 1;
|
|
173
|
+
if (failures < dropAfter) {
|
|
174
|
+
this.setFailureCount(bufferKey, chunk, failures);
|
|
175
|
+
throw err;
|
|
176
|
+
}
|
|
177
|
+
// A chunk that keeps throwing is dropped like one that keeps failing,
|
|
178
|
+
// so later chunks are not stuck behind it.
|
|
179
|
+
const thrown: ExtractionRunResult = {
|
|
180
|
+
status: "skipped",
|
|
181
|
+
reason: `thrown: ${describeError(err)}`,
|
|
182
|
+
persistedCount: 0,
|
|
183
|
+
durableOutputCount: 0,
|
|
184
|
+
};
|
|
185
|
+
if (!(await this.dropFailedChunk(bufferKey, chunk, dropAfter, thrown, byIdentity))) {
|
|
186
|
+
this.setFailureCount(bufferKey, chunk, failures);
|
|
187
|
+
throw err;
|
|
188
|
+
}
|
|
189
|
+
this.setFailureCount(bufferKey, chunk, 0);
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
const verdict = classifyDrainResult(result);
|
|
193
|
+
if (verdict === "drained") {
|
|
194
|
+
this.setFailureCount(bufferKey, chunk, 0);
|
|
195
|
+
aggregate = mergeDrainResults(aggregate, result);
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
// Provider breaker/backoff retention: keep everything for retry.
|
|
199
|
+
if (verdict === "deferred") return mergeDrainResults(aggregate, result);
|
|
200
|
+
// An outage or a bad key is not the chunk's fault: keep the chunk and do not count toward dropping it.
|
|
201
|
+
if (result.failureClass === "provider_retryable" || result.failureClass === "auth_config") {
|
|
202
|
+
return mergeDrainResults(aggregate, result);
|
|
203
|
+
}
|
|
204
|
+
failures += 1;
|
|
205
|
+
if (failures >= dropAfter) {
|
|
206
|
+
if (!(await this.dropFailedChunk(bufferKey, chunk, dropAfter, result, byIdentity))) {
|
|
207
|
+
// No recovery copy: keep the only buffered copy and retry later.
|
|
208
|
+
this.setFailureCount(bufferKey, chunk, failures);
|
|
209
|
+
return mergeDrainResults(aggregate, result);
|
|
210
|
+
}
|
|
211
|
+
this.setFailureCount(bufferKey, chunk, 0);
|
|
212
|
+
aggregate = mergeDrainResults(aggregate, result);
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
this.setFailureCount(bufferKey, chunk, failures);
|
|
216
|
+
// Retain the remaining window for the next trigger (existing behavior).
|
|
217
|
+
return mergeDrainResults(aggregate, result);
|
|
218
|
+
}
|
|
219
|
+
this.qualified.delete(bufferKey);
|
|
220
|
+
return aggregate ?? { status: "skipped", persistedCount: 0, durableOutputCount: 0 };
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/** Side-file copy first (content survives a failed drop), then remove the failing chunk from the buffer. Never throws:
|
|
224
|
+
* the drop must not fail the extraction task that already settled its result. */
|
|
225
|
+
private async dropFailedChunk(
|
|
226
|
+
bufferKey: string,
|
|
227
|
+
chunk: readonly BufferTurn[],
|
|
228
|
+
dropAfter: number,
|
|
229
|
+
result: ExtractionRunResult,
|
|
230
|
+
byIdentity: boolean,
|
|
231
|
+
): Promise<boolean> {
|
|
232
|
+
const reason =
|
|
233
|
+
`dropped after ${dropAfter} repeated extraction failures ` +
|
|
234
|
+
`(last result: status=${result.status} reason=${result.reason ?? "n/a"})`;
|
|
235
|
+
try {
|
|
236
|
+
const storage = await this.deps.getStorage();
|
|
237
|
+
const path = await storage.saveDroppedExtractionChunk(bufferKey, reason, chunk);
|
|
238
|
+
log.warn(`buffer[${bufferKey}]: dropping ${chunk.length} turns — ${reason}; copy saved to ${path}`);
|
|
239
|
+
} catch (err) {
|
|
240
|
+
// Never destroy the only buffered copy without a recovery file.
|
|
241
|
+
log.warn(
|
|
242
|
+
`buffer[${bufferKey}]: could not save the dropped-chunk recovery copy; keeping the chunk in the buffer: ${describeError(err)}`,
|
|
243
|
+
);
|
|
244
|
+
return false;
|
|
245
|
+
}
|
|
246
|
+
try {
|
|
247
|
+
const dropped = await this.deps.buffer.dropExtractionChunk(bufferKey, chunk, { allowNonPrefix: byIdentity });
|
|
248
|
+
if (dropped > 0) {
|
|
249
|
+
log.warn(`buffer[${bufferKey}]: removed ${dropped} buffered turns — ${reason}`);
|
|
250
|
+
return true;
|
|
251
|
+
}
|
|
252
|
+
// Nothing removed (the live turns changed): the chunk is still buffered, so this is not a drop.
|
|
253
|
+
log.warn(`buffer[${bufferKey}]: dropped chunk no longer matches the buffer; keeping it and its failure count`);
|
|
254
|
+
} catch (err) {
|
|
255
|
+
log.warn(
|
|
256
|
+
`buffer[${bufferKey}]: failed to remove dropped chunk from buffer (will retry next trigger): ${describeError(err)}`,
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
return false;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { BufferTurn, ExtractionFailureClass, ExtractionResult, MetaState } from "../types.js";
|
|
2
2
|
import type { StorageManager } from "../index.js";
|
|
3
3
|
import { log } from "../logger.js";
|
|
4
|
+
import type { ExtractionRunResult } from "./extraction-run.js";
|
|
4
5
|
|
|
5
6
|
export function combineExtractionAbortSignals(
|
|
6
7
|
primary: AbortSignal | undefined,
|
|
@@ -11,6 +12,13 @@ export function combineExtractionAbortSignals(
|
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
|
|
15
|
+
/** Result of a run whose extraction found nothing durable; `retained` marks a recorded failure that left the window in the buffer. */
|
|
16
|
+
export function emptyExtractionResult(retained: boolean, failureClass?: ExtractionFailureClass): ExtractionRunResult {
|
|
17
|
+
const result: ExtractionRunResult = { status: "skipped", reason: "empty_extraction_result", persistedCount: 0, durableOutputCount: 0 };
|
|
18
|
+
if (retained) Object.assign(result, { retainedBuffer: true, failureClass: failureClass ?? "provider_retryable" }); // same default as the retry bookkeeping
|
|
19
|
+
return result;
|
|
20
|
+
}
|
|
21
|
+
|
|
14
22
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
15
23
|
return typeof value === "object" && value !== null;
|
|
16
24
|
}
|
|
@@ -88,6 +88,7 @@ const migrationSummary: TierMigrationCycleSummary = {
|
|
|
88
88
|
|
|
89
89
|
interface Harness {
|
|
90
90
|
config: PluginConfig;
|
|
91
|
+
buffer: SmartBuffer;
|
|
91
92
|
storageForNs: (ns: string) => Promise<StorageManager>;
|
|
92
93
|
setStorageForDelay: (delayMs: number) => void;
|
|
93
94
|
newCoordinator: () => ExtractionRunCoordinator;
|
|
@@ -105,7 +106,7 @@ interface Harness {
|
|
|
105
106
|
coord: ExtractionRunCoordinator,
|
|
106
107
|
content: string,
|
|
107
108
|
opts?: { force?: boolean; failClosed?: boolean; writeNamespaceOverride?: string },
|
|
108
|
-
) => Promise<{ status: string; reason?: string }>;
|
|
109
|
+
) => Promise<{ status: string; reason?: string; failureClass?: string; retainedBuffer?: boolean }>;
|
|
109
110
|
cleanup: () => Promise<void>;
|
|
110
111
|
passiveCapture: () => { principal?: string; namespace?: string } | null;
|
|
111
112
|
setThreadingBlock: (blocked: boolean) => void;
|
|
@@ -201,6 +202,7 @@ async function makeHarness(overrides: Record<string, unknown> = {}): Promise<Har
|
|
|
201
202
|
|
|
202
203
|
return {
|
|
203
204
|
config,
|
|
205
|
+
buffer,
|
|
204
206
|
storageForNs,
|
|
205
207
|
setStorageForDelay: (delayMs) => {
|
|
206
208
|
storageForDelayMs = delayMs;
|
|
@@ -243,7 +245,7 @@ async function makeHarness(overrides: Record<string, unknown> = {}): Promise<Har
|
|
|
243
245
|
forceExtractionAttempt: opts.force === true,
|
|
244
246
|
writeNamespaceOverride: opts.writeNamespaceOverride,
|
|
245
247
|
});
|
|
246
|
-
return { status: result.status, reason: result.reason };
|
|
248
|
+
return { status: result.status, reason: result.reason, failureClass: result.failureClass, retainedBuffer: result.retainedBuffer };
|
|
247
249
|
},
|
|
248
250
|
cleanup: async () => {
|
|
249
251
|
StorageManager.clearAllStaticCaches();
|
|
@@ -1336,3 +1338,82 @@ test("#3140: a successful flush heals the recorded LLM health", async () => {
|
|
|
1336
1338
|
await h.cleanup();
|
|
1337
1339
|
}
|
|
1338
1340
|
});
|
|
1341
|
+
|
|
1342
|
+
// ---------------------------------------------------------------------------
|
|
1343
|
+
// Wiring the trigger drain depends on (#3266): what a real run hands back, and what it asks of the buffer
|
|
1344
|
+
// ---------------------------------------------------------------------------
|
|
1345
|
+
|
|
1346
|
+
test("a retained failure carries the extraction's failure class, so the drain can tell an outage from a bad chunk", async () => {
|
|
1347
|
+
for (const cls of ["provider_retryable", "auth_config", "parse_empty"] as const) {
|
|
1348
|
+
const h = await makeHarness({ extractionRetryScheduleMs: [3_600_000], extractionBreakerFailureThreshold: 100 });
|
|
1349
|
+
try {
|
|
1350
|
+
h.setRespond(() => failureResult(cls));
|
|
1351
|
+
const result = await h.run(h.newCoordinator(), `class-${cls}`);
|
|
1352
|
+
assert.equal(result.retainedBuffer, true, `${cls}: the window is kept for a retry`);
|
|
1353
|
+
assert.equal(result.failureClass, cls, `${cls}: the class reaches the run result`);
|
|
1354
|
+
} finally {
|
|
1355
|
+
await h.cleanup();
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
});
|
|
1359
|
+
|
|
1360
|
+
test("a failure with no class defaults to provider_retryable on the run result, like the retry bookkeeping", async () => {
|
|
1361
|
+
const h = await makeHarness({ extractionRetryScheduleMs: [3_600_000], extractionBreakerFailureThreshold: 100 });
|
|
1362
|
+
try {
|
|
1363
|
+
h.setRespond(() => ({ ...failureResult("parse_empty"), extractionFailureClass: undefined }));
|
|
1364
|
+
const result = await h.run(h.newCoordinator(), "class-default");
|
|
1365
|
+
assert.equal(result.failureClass, "provider_retryable");
|
|
1366
|
+
} finally {
|
|
1367
|
+
await h.cleanup();
|
|
1368
|
+
}
|
|
1369
|
+
});
|
|
1370
|
+
|
|
1371
|
+
test("a run without a failure reports no failure class", async () => {
|
|
1372
|
+
const h = await makeHarness();
|
|
1373
|
+
try {
|
|
1374
|
+
h.setRespond(() => ({ ...successResult(), facts: [] }));
|
|
1375
|
+
const result = await h.run(h.newCoordinator(), "class-none");
|
|
1376
|
+
assert.equal(result.failureClass, undefined);
|
|
1377
|
+
} finally {
|
|
1378
|
+
await h.cleanup();
|
|
1379
|
+
}
|
|
1380
|
+
});
|
|
1381
|
+
|
|
1382
|
+
test("the retention window a chunk run is given reaches the buffer as the processed snapshot (#3266)", async () => {
|
|
1383
|
+
const h = await makeHarness();
|
|
1384
|
+
try {
|
|
1385
|
+
const seen: Array<readonly BufferTurn[] | undefined> = [];
|
|
1386
|
+
const original = h.buffer.retainDeferredTurns.bind(h.buffer);
|
|
1387
|
+
h.buffer.retainDeferredTurns = async (key, turns, max, processed) => {
|
|
1388
|
+
seen.push(processed);
|
|
1389
|
+
await original(key, turns, max, processed);
|
|
1390
|
+
};
|
|
1391
|
+
const chunk = makeTurns("chunk-turns");
|
|
1392
|
+
const window = [...makeTurns("earlier-turns"), ...chunk];
|
|
1393
|
+
h.setRespond(() => successResult());
|
|
1394
|
+
await h.newCoordinator().runExtraction(chunk, {
|
|
1395
|
+
skipCharThreshold: true,
|
|
1396
|
+
skipUserTurnThreshold: true,
|
|
1397
|
+
clearBufferAfterExtraction: true,
|
|
1398
|
+
bufferKey: "window",
|
|
1399
|
+
failOnExtractionFailure: false,
|
|
1400
|
+
forceExtractionAttempt: false,
|
|
1401
|
+
retentionWindow: window,
|
|
1402
|
+
});
|
|
1403
|
+
assert.equal(seen.length, 1, "the run asked the buffer to update its retention slot once");
|
|
1404
|
+
assert.deepEqual(seen[0], window, "with the window it was given, not just its own chunk");
|
|
1405
|
+
|
|
1406
|
+
seen.length = 0;
|
|
1407
|
+
await h.newCoordinator().runExtraction(chunk, {
|
|
1408
|
+
skipCharThreshold: true,
|
|
1409
|
+
skipUserTurnThreshold: true,
|
|
1410
|
+
clearBufferAfterExtraction: true,
|
|
1411
|
+
bufferKey: "window",
|
|
1412
|
+
failOnExtractionFailure: false,
|
|
1413
|
+
forceExtractionAttempt: false,
|
|
1414
|
+
});
|
|
1415
|
+
assert.deepEqual(seen[0], chunk, "without a window the run keeps naming its own turns");
|
|
1416
|
+
} finally {
|
|
1417
|
+
await h.cleanup();
|
|
1418
|
+
}
|
|
1419
|
+
});
|
|
@@ -38,6 +38,7 @@ export interface ExtractionRunResult {
|
|
|
38
38
|
persistedCount: number;
|
|
39
39
|
durableOutputCount: number;
|
|
40
40
|
postPersistMetadataFailed?: boolean;
|
|
41
|
+
retainedBuffer?: boolean; failureClass?: ExtractionFailureClass; // retained: the run left the window in the buffer for a retry (a recorded failure) of this class
|
|
41
42
|
}
|
|
42
43
|
|
|
43
44
|
export class ExtractionDeadlineError extends Error {
|
|
@@ -118,7 +119,7 @@ import {
|
|
|
118
119
|
commitEmptySuccessExtraction,
|
|
119
120
|
computeExtractionRetryNextEligibleMs,
|
|
120
121
|
deriveSourceConnector,
|
|
121
|
-
deriveTopicsFromExtraction,
|
|
122
|
+
deriveTopicsFromExtraction, emptyExtractionResult,
|
|
122
123
|
runExtractionPostPersist,
|
|
123
124
|
runExtractionPostPersistBestEffort,
|
|
124
125
|
EXTRACTION_RETRY_STATE_MAX_ENTRIES,
|
|
@@ -439,7 +440,7 @@ export class ExtractionRunCoordinator {
|
|
|
439
440
|
turns: BufferTurn[],
|
|
440
441
|
options: {
|
|
441
442
|
clearBufferAfterExtraction?: boolean;
|
|
442
|
-
clearMatchingTurns?: boolean;
|
|
443
|
+
clearMatchingTurns?: boolean; retentionWindow?: readonly BufferTurn[];
|
|
443
444
|
skipCharThreshold?: boolean;
|
|
444
445
|
skipUserTurnThreshold?: boolean;
|
|
445
446
|
deadlineMs?: number;
|
|
@@ -1044,7 +1045,7 @@ export class ExtractionRunCoordinator {
|
|
|
1044
1045
|
if (!shouldPersistProcessedFingerprint) {
|
|
1045
1046
|
await commitEmptySuccessExtraction(emptySuccessCommit);
|
|
1046
1047
|
}
|
|
1047
|
-
return
|
|
1048
|
+
return emptyExtractionResult(recordedRetryFailure, result.extractionFailureClass);
|
|
1048
1049
|
}
|
|
1049
1050
|
|
|
1050
1051
|
let threadIdForExtraction: string | null = null;
|
|
@@ -1108,7 +1109,7 @@ export class ExtractionRunCoordinator {
|
|
|
1108
1109
|
normalizedTurns: normalizedTurns as BufferTurn[],
|
|
1109
1110
|
bufferKey,
|
|
1110
1111
|
retainDeferredTurns: (key, retainedTurns, max) =>
|
|
1111
|
-
this.deps.getBuffer().retainDeferredTurns(key, retainedTurns, max, turns),
|
|
1112
|
+
this.deps.getBuffer().retainDeferredTurns(key, retainedTurns, max, options.retentionWindow ?? turns),
|
|
1112
1113
|
clearBuffer,
|
|
1113
1114
|
failOnExtractionFailure: options.failOnExtractionFailure === true,
|
|
1114
1115
|
});
|
|
@@ -26,12 +26,13 @@
|
|
|
26
26
|
* RecallRerankCoordinator / ConversationIndexCoordinator accessor pattern.
|
|
27
27
|
*/
|
|
28
28
|
|
|
29
|
+
import { existsSync } from "node:fs";
|
|
29
30
|
import path from "node:path";
|
|
30
31
|
import { resolveNamespaceCapabilities } from "../capabilities.js";
|
|
31
32
|
import type { StorageManager } from "../index.js";
|
|
32
33
|
import { log } from "../logger.js";
|
|
33
34
|
import { namespaceIdentityFromToken } from "../namespaces/identity.js";
|
|
34
|
-
import { normalizeQmdResultPath } from "../namespaces/search.js";
|
|
35
|
+
import { normalizeQmdResultPath, resolveNamespaceResultPath } from "../namespaces/search.js";
|
|
35
36
|
import { SecureStoreLockedError } from "../secure-store/index.js";
|
|
36
37
|
import { isPathInsideStorageRoot } from "../storage-paths.js";
|
|
37
38
|
import { isSupportPassportPrivateMemory } from "../support-passport/card-projection.js";
|
|
@@ -136,6 +137,30 @@ export function qmdResultPathCandidates(storageDir: string, resultPath: string):
|
|
|
136
137
|
return [...candidates];
|
|
137
138
|
}
|
|
138
139
|
|
|
140
|
+
/** Canonicalize flat hot-memory identities before merging with native hits (#3194).
|
|
141
|
+
* This is a path-only map: no candidates, collections or namespace scopes are removed.
|
|
142
|
+
* Cold/explicit foreign collections keep their existing owner-resolution path.
|
|
143
|
+
*/
|
|
144
|
+
export function normalizeFlatQmdResults(
|
|
145
|
+
results: QmdSearchResult[],
|
|
146
|
+
config: PluginConfig,
|
|
147
|
+
collection = config.qmdCollection,
|
|
148
|
+
): QmdSearchResult[] {
|
|
149
|
+
if (!collection || resolveNamespaceCapabilities(config).namespaces || collection !== config.qmdCollection) return results;
|
|
150
|
+
return results.map((result) => ({
|
|
151
|
+
...result,
|
|
152
|
+
path: withHotFactsPath(config.memoryDir, resolveNamespaceResultPath(config.memoryDir, collection, result.path)),
|
|
153
|
+
}));
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/** A hot-facts collection registered at the facts/ subtree returns date-relative paths (#2111); meet the loaded memory's path. */
|
|
157
|
+
function withHotFactsPath(memoryDir: string, resolved: string): string {
|
|
158
|
+
const relative = path.relative(path.resolve(memoryDir), resolved).split(path.sep).join("/");
|
|
159
|
+
if (!/^\d{4}-\d{2}-\d{2}\//.test(relative) || existsSync(resolved)) return resolved;
|
|
160
|
+
const hot = path.join(path.resolve(memoryDir), "facts", relative);
|
|
161
|
+
return existsSync(hot) ? hot : resolved;
|
|
162
|
+
}
|
|
163
|
+
|
|
139
164
|
/**
|
|
140
165
|
* Extract a non-empty `dir` from a storage, returning `null` when the
|
|
141
166
|
* property is absent or not a non-empty string at runtime. Proxy-backed
|
|
@@ -27,7 +27,7 @@ import { inferIntentFromText, intentCompatibilityScore } from "../intent.js";
|
|
|
27
27
|
import { log } from "../logger.js";
|
|
28
28
|
import { NamespaceStorageRouter } from "../namespaces/storage.js";
|
|
29
29
|
import { NegativeExampleStore } from "../negative.js";
|
|
30
|
-
import { qmdCollectionPathParts } from "./qmd-result-resolver.js";
|
|
30
|
+
import { normalizeFlatQmdResults, qmdCollectionPathParts } from "./qmd-result-resolver.js";
|
|
31
31
|
import type { RecallRerankCoordinator, RecallResultPartitionSink } from "./recall-rerank-coordinator.js";
|
|
32
32
|
import type { GraphRecallExpansionOptions, GraphRecallExpansionResult } from "./graph-recall-coordinator.js";
|
|
33
33
|
import { RelevanceStore } from "../relevance.js";
|
|
@@ -394,7 +394,7 @@ export class RecallSearchPipelineCoordinator {
|
|
|
394
394
|
break;
|
|
395
395
|
}
|
|
396
396
|
|
|
397
|
-
|
|
397
|
+
let primaryResults = options.collection
|
|
398
398
|
? options.abortSignal
|
|
399
399
|
? await this.deps.qmd.search(
|
|
400
400
|
prompt,
|
|
@@ -426,6 +426,7 @@ export class RecallSearchPipelineCoordinator {
|
|
|
426
426
|
onDegradation: options.onDegradation,
|
|
427
427
|
},
|
|
428
428
|
});
|
|
429
|
+
primaryResults = normalizeFlatQmdResults(primaryResults, this.deps.config, options.collection);
|
|
429
430
|
lastPrimaryResultCount = primaryResults.length;
|
|
430
431
|
lastHybridResultCount = 0;
|
|
431
432
|
lastHybridTopUpUsed = false;
|
|
@@ -452,7 +453,7 @@ export class RecallSearchPipelineCoordinator {
|
|
|
452
453
|
// uniform across primary + top-up (gotcha #39). Issue #1335 (codex review #1422).
|
|
453
454
|
lastHybridTopUpSkippedReason = "lex_strategy";
|
|
454
455
|
} else {
|
|
455
|
-
|
|
456
|
+
let hybridResults = options.collection
|
|
456
457
|
? await this.deps.qmd.hybridSearch(
|
|
457
458
|
prompt,
|
|
458
459
|
options.collection,
|
|
@@ -474,6 +475,7 @@ export class RecallSearchPipelineCoordinator {
|
|
|
474
475
|
onDegradation: options.onDegradation,
|
|
475
476
|
},
|
|
476
477
|
});
|
|
478
|
+
hybridResults = normalizeFlatQmdResults(hybridResults, this.deps.config, options.collection);
|
|
477
479
|
lastHybridResultCount = hybridResults.length;
|
|
478
480
|
lastHybridTopUpUsed = hybridResults.length > 0;
|
|
479
481
|
if (hybridResults.length > 0) {
|
|
@@ -740,9 +742,7 @@ export class RecallSearchPipelineCoordinator {
|
|
|
740
742
|
// limps home later cannot mutate a recorded recall's collector.
|
|
741
743
|
coldQmdObserverActive = false;
|
|
742
744
|
if (longTerm.length > 0) {
|
|
743
|
-
log.debug(
|
|
744
|
-
`cold-tier recall source=cold-qmd collection=${coldCollection} hits=${longTerm.length}`,
|
|
745
|
-
);
|
|
745
|
+
log.debug(`cold-tier recall source=cold-qmd collection=${coldCollection} hits=${longTerm.length}`);
|
|
746
746
|
}
|
|
747
747
|
}
|
|
748
748
|
}
|
|
@@ -940,9 +940,7 @@ export class RecallSearchPipelineCoordinator {
|
|
|
940
940
|
}
|
|
941
941
|
}
|
|
942
942
|
if (caps.rerank && this.deps.config.rerankProvider === "cloud") {
|
|
943
|
-
log.debug(
|
|
944
|
-
"rerankProvider=cloud is reserved/experimental in v2.2.0; skipping rerank",
|
|
945
|
-
);
|
|
943
|
+
log.debug("rerankProvider=cloud is reserved/experimental in v2.2.0; skipping rerank");
|
|
946
944
|
}
|
|
947
945
|
|
|
948
946
|
if (caps.recallTrustScore && results.length > 0) {
|
|
@@ -1149,6 +1147,9 @@ export class RecallSearchPipelineCoordinator {
|
|
|
1149
1147
|
: null;
|
|
1150
1148
|
const buildRecallRow = (r: QmdSearchResult, memory: MemoryFile | undefined): QmdSearchResult => ({
|
|
1151
1149
|
...r,
|
|
1150
|
+
// Render loaded flat memories, not QMD's front-matter/chunk preview (#3194).
|
|
1151
|
+
snippet: memory && !resolveNamespaceCapabilities(this.deps.config).namespaces ? memory.content : r.snippet,
|
|
1152
|
+
line: memory && !resolveNamespaceCapabilities(this.deps.config).namespaces ? undefined : r.line,
|
|
1152
1153
|
sourceConnector: memory ? memory.frontmatter.sourceConnector : undefined,
|
|
1153
1154
|
origin: memory ? memory.frontmatter.origin : undefined,
|
|
1154
1155
|
// #1952: an active state view carries the chain fields the inject
|
|
@@ -1358,9 +1359,7 @@ export class RecallSearchPipelineCoordinator {
|
|
|
1358
1359
|
}
|
|
1359
1360
|
}
|
|
1360
1361
|
if (connectorPartitionFilteredCount > 0) {
|
|
1361
|
-
log.debug(
|
|
1362
|
-
`connector partition filter removed ${connectorPartitionFilteredCount} tool-scoped candidates from recall`,
|
|
1363
|
-
);
|
|
1362
|
+
log.debug(`connector partition filter removed ${connectorPartitionFilteredCount} tool-scoped candidates from recall`);
|
|
1364
1363
|
}
|
|
1365
1364
|
if (supportPassportFilteredCount > 0) {
|
|
1366
1365
|
log.debug(
|
|
@@ -42,6 +42,7 @@ import { TranscriptManager } from "../transcript.js";
|
|
|
42
42
|
import type { BufferTurn, PluginConfig, SourceConnectorProvenance } from "../types.js";
|
|
43
43
|
|
|
44
44
|
import { groupTurnsByExtractionScope, bufferTurnsEqual } from "../buffer-turn-helpers.js";
|
|
45
|
+
import { ExtractionDrain } from "./extraction-drain.js";
|
|
45
46
|
import { resolveScopeProfilePlan, type ResolvedScopeProfilePlan, hasExplicitNamespacePolicy } from "../namespaces/scope-profiles.js";
|
|
46
47
|
import { resolveCodingNamespaceOverlay, combineNamespaces } from "../coding/coding-namespace.js";
|
|
47
48
|
import { canWriteNamespace, defaultNamespaceForPrincipal } from "../namespaces/principal.js";
|
|
@@ -114,6 +115,8 @@ export interface TurnIngestionDeps {
|
|
|
114
115
|
},
|
|
115
116
|
): Promise<void>;
|
|
116
117
|
resolveMemoryIdOrHandle(ref: string, sessionKey?: string): string;
|
|
118
|
+
/** Max conversation chars the extraction model accepts; absent on legacy hosts (single-run behavior). */
|
|
119
|
+
extractionWindowBudgetChars?(): number;
|
|
117
120
|
runExtraction(
|
|
118
121
|
...args: Parameters<ExtractionRunCoordinator["runExtraction"]>
|
|
119
122
|
): Promise<ExtractionRunResult>;
|
|
@@ -156,9 +159,10 @@ function isFlushPlanRecoveryTurn(
|
|
|
156
159
|
}
|
|
157
160
|
|
|
158
161
|
export class TurnIngestionCoordinator {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
+
private readonly drain: ExtractionDrain; // chunked trigger-drain (extraction-drain.ts)
|
|
163
|
+
constructor(private readonly deps: TurnIngestionDeps) {
|
|
164
|
+
this.drain = new ExtractionDrain(deps);
|
|
165
|
+
}
|
|
162
166
|
|
|
163
167
|
async processTurn(
|
|
164
168
|
role: "user" | "assistant",
|
|
@@ -1067,7 +1071,7 @@ export class TurnIngestionCoordinator {
|
|
|
1067
1071
|
effectiveScopePlan = fresh;
|
|
1068
1072
|
}
|
|
1069
1073
|
}
|
|
1070
|
-
const result = await this.
|
|
1074
|
+
const result = await this.drain.run(effectiveTurns, {
|
|
1071
1075
|
clearBufferAfterExtraction:
|
|
1072
1076
|
options.clearBufferAfterExtraction ?? true,
|
|
1073
1077
|
clearMatchingTurns: options.clearMatchingTurns === true,
|
|
@@ -1083,7 +1087,7 @@ export class TurnIngestionCoordinator {
|
|
|
1083
1087
|
principalOverride: options.principalOverride,
|
|
1084
1088
|
scopeProfileWritePlan: effectiveScopePlan,
|
|
1085
1089
|
forceExtractionAttempt: options.forceExtractionAttempt === true,
|
|
1086
|
-
});
|
|
1090
|
+
}, preemptable);
|
|
1087
1091
|
settleTask(undefined, result);
|
|
1088
1092
|
} catch (err) {
|
|
1089
1093
|
const reason = abortSignal.reason;
|
package/src/orchestrator.ts
CHANGED
|
@@ -238,6 +238,7 @@ import {
|
|
|
238
238
|
} from "./maintenance/pattern-reinforcement.js";
|
|
239
239
|
import { refreshConvergedNamespaces } from "./convergence-refresh.js";
|
|
240
240
|
import { ModelRegistry } from "./model-registry.js";
|
|
241
|
+
import { extractionWindowBudgetChars } from "./extraction-window-drain.js";
|
|
241
242
|
import { applyRuntimeRetrievalPolicy, expandQuery } from "./retrieval.js";
|
|
242
243
|
import {
|
|
243
244
|
mergeWithAgentResults,
|
|
@@ -3062,6 +3063,26 @@ export class Orchestrator {
|
|
|
3062
3063
|
);
|
|
3063
3064
|
}
|
|
3064
3065
|
|
|
3066
|
+
/**
|
|
3067
|
+
* Max conversation characters the extraction model can take as input
|
|
3068
|
+
* (context window the ModelRegistry learned, capped by
|
|
3069
|
+
* `localLlmMaxContext` when set). Exposed for the trigger-drain chunking
|
|
3070
|
+
* in the turn-ingestion coordinator so an oversized buffer window is
|
|
3071
|
+
* drained in fitting chunks instead of failing whole.
|
|
3072
|
+
*/
|
|
3073
|
+
private extractionWindowBudgetChars(): number {
|
|
3074
|
+
// Object.create(Orchestrator.prototype) test doubles skip the constructor,
|
|
3075
|
+
// so neither modelRegistry nor config exists there: report "no budget".
|
|
3076
|
+
if (!this.modelRegistry) return Number.POSITIVE_INFINITY;
|
|
3077
|
+
return extractionWindowBudgetChars({
|
|
3078
|
+
registry: this.modelRegistry,
|
|
3079
|
+
localExtractionActive:
|
|
3080
|
+
this.config.modelSource !== "gateway" && resolveLocalLlmCapabilities(this.config).localLlm,
|
|
3081
|
+
model: this.config.localLlmModel,
|
|
3082
|
+
maxContext: this.config.localLlmMaxContext,
|
|
3083
|
+
});
|
|
3084
|
+
}
|
|
3085
|
+
|
|
3065
3086
|
private normalizeExtractionFingerprintTurns(turns: BufferTurn[]): string[] {
|
|
3066
3087
|
return this.extractionRunCoordinator.normalizeExtractionFingerprintTurns(turns);
|
|
3067
3088
|
}
|
|
@@ -902,7 +902,9 @@ const ENCRYPTABLE_STATE_SIDECARS = new Set([
|
|
|
902
902
|
]);
|
|
903
903
|
|
|
904
904
|
function isEncryptableStateSidecar(normalized: string): boolean {
|
|
905
|
-
|
|
905
|
+
// state/dropped/ holds copies of extraction-buffer turns dropped after
|
|
906
|
+
// repeated extraction failures — same content sensitivity as buffer.json.
|
|
907
|
+
return ENCRYPTABLE_STATE_SIDECARS.has(normalized) || normalized.startsWith("state/dropped/");
|
|
906
908
|
}
|
|
907
909
|
|
|
908
910
|
/**
|