@remnic/core 10.0.38 → 10.0.39
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 +161 -160
- 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.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-5K23NQRI.js → chunk-22YJQTPN.js} +2 -2
- 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-EEOYF3IB.js → chunk-3RJDYZJE.js} +2 -2
- package/dist/{chunk-SZPPCEYT.js → chunk-4HJ2QK32.js} +2 -2
- package/dist/{chunk-OLHN4ZNT.js → chunk-4LLV2N3R.js} +3 -3
- package/dist/{chunk-P5CUEB2Q.js → chunk-4QLKVZH6.js} +8 -8
- package/dist/{chunk-4IZ6AC2U.js → chunk-4UT2NUAC.js} +2 -2
- package/dist/{chunk-WCBLPKG3.js → chunk-5KZNWERU.js} +155 -142
- package/dist/chunk-5KZNWERU.js.map +1 -0
- package/dist/{chunk-VC3X7XP3.js → chunk-5Q6CR65T.js} +2 -2
- package/dist/{chunk-XTBEXZFN.js → chunk-5SF7JOJB.js} +4 -4
- 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-3AODCBKX.js → chunk-6Z2ZWBOV.js} +6 -6
- package/dist/{chunk-TAEJ2MRR.js → chunk-7IANFJ24.js} +56 -56
- package/dist/{chunk-PSQ2P2T3.js → chunk-7XZ7TOLQ.js} +3 -3
- package/dist/{chunk-6JEEY42N.js → chunk-AER3HD3B.js} +2 -2
- package/dist/{chunk-4B37KNMP.js → chunk-ANV373S5.js} +2 -2
- package/dist/{chunk-AYBAC2N4.js → chunk-B3I2QQU7.js} +3 -3
- package/dist/{chunk-TYLNWE4D.js → chunk-B7VKRCAG.js} +2 -2
- package/dist/{chunk-HXFIILGF.js → chunk-BFPF5N3H.js} +4 -4
- package/dist/{chunk-KQAFEZQX.js → chunk-BUKK5SWA.js} +2 -2
- package/dist/{chunk-53BLBDV6.js → chunk-BUKZ7MTN.js} +2 -2
- package/dist/{chunk-MCPSMWUC.js → chunk-CR72WZ3M.js} +9 -9
- 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-C2KOW2UQ.js → chunk-EKR6OO4P.js} +2 -2
- 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-SWZOAQUM.js → chunk-F2IU6A2L.js} +57 -57
- 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-JH4FCVWJ.js → chunk-G6OFEGUR.js} +2 -2
- package/dist/{chunk-L7PGPH4E.js → chunk-GCW7UT5I.js} +2 -2
- package/dist/{chunk-J7G656MY.js → chunk-GE5ZNDLE.js} +4 -4
- package/dist/{chunk-63YPKHAU.js → chunk-GLB6VV3J.js} +2 -2
- package/dist/{chunk-6F5BGIAF.js → chunk-GPVGAHXL.js} +3 -3
- package/dist/{chunk-XHX67LX6.js → chunk-GSBSGT43.js} +2 -2
- package/dist/{chunk-645WOU5I.js → chunk-H7BZBSP6.js} +4 -4
- package/dist/{chunk-GFYFKXWZ.js → chunk-HUDPXTXW.js} +2 -2
- package/dist/{chunk-L2JQSG5M.js → chunk-I7TR2OCU.js} +2 -2
- package/dist/{chunk-6SYS5YX3.js → chunk-IAOW2L6K.js} +2 -2
- package/dist/{chunk-STM4T335.js → chunk-IJH6METJ.js} +2 -2
- package/dist/{chunk-3XVBGGF3.js → chunk-IZRNMXZC.js} +2 -2
- package/dist/{chunk-XHVO3CGX.js → chunk-JGS55BAS.js} +53 -2
- package/dist/chunk-JGS55BAS.js.map +1 -0
- package/dist/{chunk-Y5ZHZKZD.js → chunk-JM6U3SAE.js} +2 -2
- package/dist/{chunk-74AY3S7U.js → chunk-JNLYHJG7.js} +4 -4
- package/dist/{chunk-QOUKZ2N3.js → chunk-JOOGPZJE.js} +2 -2
- package/dist/{chunk-EGNONTVT.js → chunk-JORSMFL6.js} +6 -6
- package/dist/{chunk-JF3MEIHO.js → chunk-JQCP5QW3.js} +4 -4
- package/dist/{chunk-POGPI5GR.js → chunk-JRTKYFFY.js} +2 -2
- package/dist/{chunk-5UPOKIFP.js → chunk-JT3IXJIL.js} +12 -12
- package/dist/{chunk-6DTIYAAN.js → chunk-JZFJZLJY.js} +2 -2
- package/dist/{chunk-6QOL4G76.js → chunk-KQXRVELF.js} +2 -2
- package/dist/{chunk-KOAVX2IT.js → chunk-L74NWAD4.js} +2 -2
- package/dist/{chunk-5DCS4C4S.js → chunk-LJXBFVI2.js} +4 -4
- package/dist/{chunk-OKKBOTRA.js → chunk-LPD6I2U6.js} +2 -2
- package/dist/{chunk-N7DVBHU3.js → chunk-LR7RW4T6.js} +3 -3
- package/dist/{chunk-FZVZYU5C.js → chunk-LXN737FX.js} +2 -2
- package/dist/{chunk-LOMSBYNS.js → chunk-MD3W2H3G.js} +298 -95
- package/dist/chunk-MD3W2H3G.js.map +1 -0
- package/dist/{chunk-7ZRRJ6RG.js → chunk-MGYZENUY.js} +2 -2
- package/dist/{chunk-YQ6SLFNZ.js → chunk-MPJDQC4G.js} +3 -3
- package/dist/{chunk-5ANUG4JZ.js → chunk-NJ2DZYEK.js} +2 -2
- package/dist/{chunk-OLH4XCLU.js → chunk-O2O4FDAH.js} +2 -2
- package/dist/{chunk-CVMAOPAH.js → chunk-ODJ2HRFW.js} +10 -10
- package/dist/{chunk-2NLLXCJG.js → chunk-OWHERGF2.js} +2 -2
- package/dist/{chunk-I73ZSORJ.js → chunk-OXSTKCYK.js} +4 -4
- package/dist/{chunk-RWRHGE75.js → chunk-P3YUEPSQ.js} +2 -2
- package/dist/{chunk-UFM3PJDT.js → chunk-PARM6OYL.js} +1 -1
- package/dist/chunk-PARM6OYL.js.map +1 -0
- package/dist/{chunk-TPKB7T6Z.js → chunk-Q6UZREJK.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-X247CYL6.js → chunk-RIAKNXZB.js} +4 -4
- package/dist/{chunk-WCIDCJOQ.js → chunk-RO2NNWR5.js} +3 -3
- package/dist/{chunk-AYXRK264.js → chunk-RROFIAYI.js} +9 -9
- package/dist/{chunk-YYGZ4EIY.js → chunk-RUAIO5UA.js} +2 -2
- package/dist/{chunk-FQFMLOAC.js → chunk-RWW5KPIY.js} +18 -18
- package/dist/{chunk-E5PVMB3I.js → chunk-SDXBR5QO.js} +2 -2
- package/dist/{chunk-NH6GZEQN.js → chunk-SJHFU52W.js} +8 -8
- package/dist/{chunk-FGEY2AGV.js → chunk-SRPGA6WN.js} +2 -2
- package/dist/{chunk-CUWOSRCS.js → chunk-SUKPM6DV.js} +2 -2
- package/dist/{chunk-TXP7Q7T5.js → chunk-TTQYX7VU.js} +2 -2
- package/dist/{chunk-TDCR6PQL.js → chunk-TWRPWJJT.js} +2 -2
- package/dist/{chunk-I75BWXRZ.js → chunk-U66CTIF5.js} +2 -2
- package/dist/{chunk-DG4H56SK.js → chunk-UIK3U36O.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-LHBOFROT.js → chunk-V2XMG7GM.js} +2 -2
- package/dist/chunk-VA3GNAWQ.js +58 -0
- package/dist/chunk-VA3GNAWQ.js.map +1 -0
- package/dist/{chunk-ZAP7BKTZ.js → chunk-VCOCOBUS.js} +6 -6
- package/dist/{chunk-4P4VLPGB.js → chunk-VECRSYQG.js} +1 -1
- package/dist/{chunk-GGI6FLO6.js → chunk-VHBGJAYC.js} +2 -2
- package/dist/{chunk-QO5KDQGG.js → chunk-VIS23B6O.js} +7 -7
- package/dist/{chunk-NMKETOAX.js → chunk-VSHWPOUB.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-7HXZOWTR.js → chunk-WQYS47G4.js} +2 -2
- package/dist/{chunk-JH2ALMPR.js → chunk-WR5POHQ3.js} +2 -2
- package/dist/{chunk-VSSQIBVU.js → chunk-WT2VVYBM.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-U7HJBODU.js → chunk-XKTHJHSQ.js} +5 -5
- package/dist/{chunk-JFI3ATK4.js → chunk-XMYWYJM2.js} +2 -2
- package/dist/{chunk-64S6X4IU.js → chunk-XTN54NML.js} +4 -4
- package/dist/{chunk-VGFNTY3Y.js → chunk-YFXRLRKP.js} +2 -2
- package/dist/{chunk-DAOMWGO2.js → chunk-ZX5N5D6M.js} +4 -4
- package/dist/{chunk-VGRM43X6.js → chunk-ZXFKG7VZ.js} +2 -2
- 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-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 +27 -27
- 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 +208 -207
- 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.js +7 -7
- 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/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 +161 -160
- 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/buffer-drop-extraction-chunk.test.ts +196 -0
- package/src/buffer-types.ts +65 -0
- package/src/buffer.ts +60 -62
- package/src/config.ts +8 -0
- package/src/extraction-window-drain.test.ts +123 -0
- package/src/extraction-window-drain.ts +110 -0
- package/src/orchestration/extraction-drain.ts +261 -0
- package/src/orchestration/extraction-run-helpers.ts +8 -0
- package/src/orchestration/extraction-run.ts +5 -4
- 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-LOMSBYNS.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-5K23NQRI.js.map → chunk-22YJQTPN.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-EEOYF3IB.js.map → chunk-3RJDYZJE.js.map} +0 -0
- /package/dist/{chunk-SZPPCEYT.js.map → chunk-4HJ2QK32.js.map} +0 -0
- /package/dist/{chunk-OLHN4ZNT.js.map → chunk-4LLV2N3R.js.map} +0 -0
- /package/dist/{chunk-P5CUEB2Q.js.map → chunk-4QLKVZH6.js.map} +0 -0
- /package/dist/{chunk-4IZ6AC2U.js.map → chunk-4UT2NUAC.js.map} +0 -0
- /package/dist/{chunk-VC3X7XP3.js.map → chunk-5Q6CR65T.js.map} +0 -0
- /package/dist/{chunk-XTBEXZFN.js.map → chunk-5SF7JOJB.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-3AODCBKX.js.map → chunk-6Z2ZWBOV.js.map} +0 -0
- /package/dist/{chunk-TAEJ2MRR.js.map → chunk-7IANFJ24.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-4B37KNMP.js.map → chunk-ANV373S5.js.map} +0 -0
- /package/dist/{chunk-AYBAC2N4.js.map → chunk-B3I2QQU7.js.map} +0 -0
- /package/dist/{chunk-TYLNWE4D.js.map → chunk-B7VKRCAG.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-53BLBDV6.js.map → chunk-BUKZ7MTN.js.map} +0 -0
- /package/dist/{chunk-MCPSMWUC.js.map → chunk-CR72WZ3M.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-C2KOW2UQ.js.map → chunk-EKR6OO4P.js.map} +0 -0
- /package/dist/{chunk-SWZOAQUM.js.map → chunk-F2IU6A2L.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-JH4FCVWJ.js.map → chunk-G6OFEGUR.js.map} +0 -0
- /package/dist/{chunk-L7PGPH4E.js.map → chunk-GCW7UT5I.js.map} +0 -0
- /package/dist/{chunk-J7G656MY.js.map → chunk-GE5ZNDLE.js.map} +0 -0
- /package/dist/{chunk-63YPKHAU.js.map → chunk-GLB6VV3J.js.map} +0 -0
- /package/dist/{chunk-6F5BGIAF.js.map → chunk-GPVGAHXL.js.map} +0 -0
- /package/dist/{chunk-XHX67LX6.js.map → chunk-GSBSGT43.js.map} +0 -0
- /package/dist/{chunk-645WOU5I.js.map → chunk-H7BZBSP6.js.map} +0 -0
- /package/dist/{chunk-GFYFKXWZ.js.map → chunk-HUDPXTXW.js.map} +0 -0
- /package/dist/{chunk-L2JQSG5M.js.map → chunk-I7TR2OCU.js.map} +0 -0
- /package/dist/{chunk-6SYS5YX3.js.map → chunk-IAOW2L6K.js.map} +0 -0
- /package/dist/{chunk-STM4T335.js.map → chunk-IJH6METJ.js.map} +0 -0
- /package/dist/{chunk-3XVBGGF3.js.map → chunk-IZRNMXZC.js.map} +0 -0
- /package/dist/{chunk-Y5ZHZKZD.js.map → chunk-JM6U3SAE.js.map} +0 -0
- /package/dist/{chunk-74AY3S7U.js.map → chunk-JNLYHJG7.js.map} +0 -0
- /package/dist/{chunk-QOUKZ2N3.js.map → chunk-JOOGPZJE.js.map} +0 -0
- /package/dist/{chunk-EGNONTVT.js.map → chunk-JORSMFL6.js.map} +0 -0
- /package/dist/{chunk-JF3MEIHO.js.map → chunk-JQCP5QW3.js.map} +0 -0
- /package/dist/{chunk-POGPI5GR.js.map → chunk-JRTKYFFY.js.map} +0 -0
- /package/dist/{chunk-5UPOKIFP.js.map → chunk-JT3IXJIL.js.map} +0 -0
- /package/dist/{chunk-6DTIYAAN.js.map → chunk-JZFJZLJY.js.map} +0 -0
- /package/dist/{chunk-6QOL4G76.js.map → chunk-KQXRVELF.js.map} +0 -0
- /package/dist/{chunk-KOAVX2IT.js.map → chunk-L74NWAD4.js.map} +0 -0
- /package/dist/{chunk-5DCS4C4S.js.map → chunk-LJXBFVI2.js.map} +0 -0
- /package/dist/{chunk-OKKBOTRA.js.map → chunk-LPD6I2U6.js.map} +0 -0
- /package/dist/{chunk-N7DVBHU3.js.map → chunk-LR7RW4T6.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-5ANUG4JZ.js.map → chunk-NJ2DZYEK.js.map} +0 -0
- /package/dist/{chunk-OLH4XCLU.js.map → chunk-O2O4FDAH.js.map} +0 -0
- /package/dist/{chunk-CVMAOPAH.js.map → chunk-ODJ2HRFW.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-RWRHGE75.js.map → chunk-P3YUEPSQ.js.map} +0 -0
- /package/dist/{chunk-TPKB7T6Z.js.map → chunk-Q6UZREJK.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-X247CYL6.js.map → chunk-RIAKNXZB.js.map} +0 -0
- /package/dist/{chunk-WCIDCJOQ.js.map → chunk-RO2NNWR5.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-FQFMLOAC.js.map → chunk-RWW5KPIY.js.map} +0 -0
- /package/dist/{chunk-E5PVMB3I.js.map → chunk-SDXBR5QO.js.map} +0 -0
- /package/dist/{chunk-NH6GZEQN.js.map → chunk-SJHFU52W.js.map} +0 -0
- /package/dist/{chunk-FGEY2AGV.js.map → chunk-SRPGA6WN.js.map} +0 -0
- /package/dist/{chunk-CUWOSRCS.js.map → chunk-SUKPM6DV.js.map} +0 -0
- /package/dist/{chunk-TXP7Q7T5.js.map → chunk-TTQYX7VU.js.map} +0 -0
- /package/dist/{chunk-TDCR6PQL.js.map → chunk-TWRPWJJT.js.map} +0 -0
- /package/dist/{chunk-I75BWXRZ.js.map → chunk-U66CTIF5.js.map} +0 -0
- /package/dist/{chunk-DG4H56SK.js.map → chunk-UIK3U36O.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-LHBOFROT.js.map → chunk-V2XMG7GM.js.map} +0 -0
- /package/dist/{chunk-ZAP7BKTZ.js.map → chunk-VCOCOBUS.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-QO5KDQGG.js.map → chunk-VIS23B6O.js.map} +0 -0
- /package/dist/{chunk-NMKETOAX.js.map → chunk-VSHWPOUB.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-7HXZOWTR.js.map → chunk-WQYS47G4.js.map} +0 -0
- /package/dist/{chunk-JH2ALMPR.js.map → chunk-WR5POHQ3.js.map} +0 -0
- /package/dist/{chunk-VSSQIBVU.js.map → chunk-WT2VVYBM.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-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-VGFNTY3Y.js.map → chunk-YFXRLRKP.js.map} +0 -0
- /package/dist/{chunk-DAOMWGO2.js.map → chunk-ZX5N5D6M.js.map} +0 -0
- /package/dist/{chunk-VGRM43X6.js.map → chunk-ZXFKG7VZ.js.map} +0 -0
- /package/dist/{resolution-4ZEQPXUZ.js.map → extraction-window-drain.js.map} +0 -0
package/dist/write-envelope.js
CHANGED
|
@@ -12,10 +12,10 @@ import {
|
|
|
12
12
|
isMemoryCategory,
|
|
13
13
|
isSealedMemoryEnvelope,
|
|
14
14
|
sealedWriteToLegacyArgs
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-TWRPWJJT.js";
|
|
16
16
|
import "./chunk-BT7NVCML.js";
|
|
17
|
-
import "./chunk-
|
|
18
|
-
import "./chunk-
|
|
17
|
+
import "./chunk-SDXBR5QO.js";
|
|
18
|
+
import "./chunk-W5EO4USJ.js";
|
|
19
19
|
import "./chunk-5FVVY2EB.js";
|
|
20
20
|
import "./chunk-JDSVLVCG.js";
|
|
21
21
|
import "./chunk-5GKPFL3S.js";
|
|
@@ -24,7 +24,7 @@ import "./chunk-EBRG7ZIK.js";
|
|
|
24
24
|
import "./chunk-P7FMDTKL.js";
|
|
25
25
|
import "./chunk-X4OZMANG.js";
|
|
26
26
|
import "./chunk-R2MBW2S4.js";
|
|
27
|
-
import "./chunk-
|
|
27
|
+
import "./chunk-EYGOM6AI.js";
|
|
28
28
|
import "./chunk-A6XUJE5D.js";
|
|
29
29
|
import "./chunk-C7HJ3NEI.js";
|
|
30
30
|
import "./chunk-VS2IYZRU.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@remnic/core",
|
|
3
|
-
"version": "10.0.
|
|
3
|
+
"version": "10.0.39",
|
|
4
4
|
"description": "Framework-agnostic Remnic memory engine — orchestrator, storage, extraction, search, trust zones",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -3347,7 +3347,7 @@
|
|
|
3347
3347
|
"core"
|
|
3348
3348
|
],
|
|
3349
3349
|
"peerDependencies": {
|
|
3350
|
-
"@remnic/coding-graph": "^10.0.
|
|
3350
|
+
"@remnic/coding-graph": "^10.0.39"
|
|
3351
3351
|
},
|
|
3352
3352
|
"peerDependenciesMeta": {
|
|
3353
3353
|
"@remnic/coding-graph": {
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { mkdtemp, rm } from "node:fs/promises";
|
|
4
|
+
import os from "node:os";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { SmartBuffer } from "./buffer.js";
|
|
7
|
+
import { parseConfig } from "./config.js";
|
|
8
|
+
import { StorageManager } from "./storage.js";
|
|
9
|
+
import type { BufferTurn } from "./types.js";
|
|
10
|
+
import { ExtractionDrain } from "./orchestration/extraction-drain.js";
|
|
11
|
+
import type { ExtractionRunResult } from "./orchestration/extraction-run.js";
|
|
12
|
+
|
|
13
|
+
function makeTurn(sessionKey: string, content: string): BufferTurn {
|
|
14
|
+
return {
|
|
15
|
+
role: "user",
|
|
16
|
+
content,
|
|
17
|
+
timestamp: "2026-10-29T12:00:00.000Z",
|
|
18
|
+
sessionKey,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** A real buffer on real storage; `restart()` is what a process restart would load from disk. */
|
|
23
|
+
async function withBuffer(run: (buffer: SmartBuffer, restart: () => Promise<SmartBuffer>) => Promise<void>): Promise<void> {
|
|
24
|
+
const root = await mkdtemp(path.join(os.tmpdir(), "remnic-drop-chunk-"));
|
|
25
|
+
try {
|
|
26
|
+
const buffer = new SmartBuffer(parseConfig({}), new StorageManager(root));
|
|
27
|
+
await run(buffer, async () => {
|
|
28
|
+
const reloaded = new SmartBuffer(parseConfig({}), new StorageManager(root));
|
|
29
|
+
await reloaded.load();
|
|
30
|
+
return reloaded;
|
|
31
|
+
});
|
|
32
|
+
} finally {
|
|
33
|
+
await rm(root, { recursive: true, force: true });
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
test("dropExtractionChunk removes the matched prefix and the drop survives a restart", async () => {
|
|
38
|
+
await withBuffer(async (buffer, restart) => {
|
|
39
|
+
const turns = [makeTurn("s1", "turn one"), makeTurn("s1", "turn two"), makeTurn("s1", "turn three")];
|
|
40
|
+
for (const turn of turns) await buffer.addTurn("s1", turn);
|
|
41
|
+
await buffer.flushPendingSave();
|
|
42
|
+
|
|
43
|
+
const dropped = await buffer.dropExtractionChunk("s1", turns.slice(0, 2));
|
|
44
|
+
|
|
45
|
+
assert.equal(dropped, 2);
|
|
46
|
+
assert.deepEqual(buffer.getTurns("s1").map((turn) => turn.content), ["turn three"]);
|
|
47
|
+
assert.deepEqual((await restart()).getTurns("s1").map((turn) => turn.content), ["turn three"], "what a restart loads");
|
|
48
|
+
// A drop is not an extraction: the extraction bookkeeping stays untouched.
|
|
49
|
+
assert.equal(buffer.getExtractionCount("s1"), 0);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test("dropExtractionChunk returns zero without dropping when turns no longer match", async () => {
|
|
54
|
+
await withBuffer(async (buffer, restart) => {
|
|
55
|
+
await buffer.addTurn("s1", makeTurn("s1", "kept turn"));
|
|
56
|
+
await buffer.flushPendingSave();
|
|
57
|
+
|
|
58
|
+
const dropped = await buffer.dropExtractionChunk("s1", [makeTurn("s1", "already extracted elsewhere")]);
|
|
59
|
+
|
|
60
|
+
assert.equal(dropped, 0);
|
|
61
|
+
assert.equal(buffer.getTurns("s1").length, 1);
|
|
62
|
+
assert.equal((await restart()).getTurns("s1").length, 1, "the kept turn is still there after a restart");
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test("dropExtractionChunk drops the whole matched window and a restart finds the buffer empty", async () => {
|
|
67
|
+
await withBuffer(async (buffer, restart) => {
|
|
68
|
+
const turns = [makeTurn("s1", "only turn")];
|
|
69
|
+
await buffer.addTurn("s1", turns[0]!);
|
|
70
|
+
await buffer.flushPendingSave();
|
|
71
|
+
|
|
72
|
+
const dropped = await buffer.dropExtractionChunk("s1", turns);
|
|
73
|
+
|
|
74
|
+
assert.equal(dropped, 1);
|
|
75
|
+
assert.equal(buffer.getTurns("s1").length, 0);
|
|
76
|
+
assert.equal((await restart()).getTurns("s1").length, 0);
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
const FAILED: ExtractionRunResult = { status: "skipped", reason: "empty_extraction_result", retainedBuffer: true, failureClass: "parse_empty", persistedCount: 0, durableOutputCount: 0 };
|
|
81
|
+
const DONE: ExtractionRunResult = { status: "completed", persistedCount: 1, durableOutputCount: 1 };
|
|
82
|
+
|
|
83
|
+
/** w7K's probe on #3266: a pinned capture sits before a native group drained with clearMatchingTurns. */
|
|
84
|
+
async function drainBehindPinnedCapture(buffer: SmartBuffer, results: ExtractionRunResult[], dropAfter: number): Promise<ExtractionRunResult> {
|
|
85
|
+
const pinned: BufferTurn = { ...makeTurn("s1", "pinned capture"), acceptedCapture: true };
|
|
86
|
+
const native = [makeTurn("s1", "a".repeat(60)), makeTurn("s1", "b".repeat(60))];
|
|
87
|
+
for (const turn of [pinned, ...native]) await buffer.addTurn("s1", turn);
|
|
88
|
+
await buffer.flushPendingSave();
|
|
89
|
+
const drain = new ExtractionDrain({
|
|
90
|
+
config: { extractionMinUserTurns: 1, extractionMinChars: 0, extractionMaxTurnChars: 100_000, extractionChunkDropAfterFailures: dropAfter },
|
|
91
|
+
extractionWindowBudgetChars: () => 100,
|
|
92
|
+
runExtraction: async (turns, options) => {
|
|
93
|
+
const result = results.shift() ?? DONE;
|
|
94
|
+
// A successful run clears its chunk the way the coordinator does for a native group: by identity.
|
|
95
|
+
if (result.status === "completed") await buffer.clearAfterExtraction("s1", turns, options?.clearMatchingTurns === true ? { allowNonPrefix: true } : undefined);
|
|
96
|
+
return result;
|
|
97
|
+
},
|
|
98
|
+
getStorage: async () => ({ saveDroppedExtractionChunk: async () => "/recovery/copy.json" }),
|
|
99
|
+
buffer,
|
|
100
|
+
extractionQueueCoordinator: { beginPreemptable: () => {} },
|
|
101
|
+
});
|
|
102
|
+
return await drain.run(native, {
|
|
103
|
+
bufferKey: "s1",
|
|
104
|
+
clearBufferAfterExtraction: true,
|
|
105
|
+
clearMatchingTurns: true,
|
|
106
|
+
skipUserTurnThreshold: false,
|
|
107
|
+
failOnExtractionFailure: false,
|
|
108
|
+
forceExtractionAttempt: false,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
test("a dropped chunk behind a pinned capture is removed by identity, like a successful clear (w7K)", async () => {
|
|
113
|
+
await withBuffer(async (buffer, restart) => {
|
|
114
|
+
const result = await drainBehindPinnedCapture(buffer, [FAILED, DONE], 1);
|
|
115
|
+
|
|
116
|
+
assert.equal(result.status, "completed");
|
|
117
|
+
assert.deepEqual(buffer.getTurns("s1").map((turn) => turn.content), ["pinned capture"], "the failed turn is gone, the pinned capture stays");
|
|
118
|
+
assert.deepEqual((await restart()).getTurns("s1").map((turn) => turn.content), ["pinned capture"], "and a restart agrees");
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
test("a failed chunk made only of deferred-retained turns can be dropped, and a restart agrees (Codex P2)", async () => {
|
|
123
|
+
await withBuffer(async (buffer, restart) => {
|
|
124
|
+
const retained = [makeTurn("s1", "retained one"), makeTurn("s1", "retained two")];
|
|
125
|
+
const live = makeTurn("s1", "live turn");
|
|
126
|
+
await buffer.retainDeferredTurns("s1", retained);
|
|
127
|
+
await buffer.addTurn("s1", live);
|
|
128
|
+
await buffer.flushPendingSave();
|
|
129
|
+
|
|
130
|
+
// The window a trigger snapshot sees is the retained turns followed by the live ones; the first chunk is retained-only.
|
|
131
|
+
const dropped = await buffer.dropExtractionChunk("s1", retained);
|
|
132
|
+
|
|
133
|
+
assert.equal(dropped, 2);
|
|
134
|
+
assert.deepEqual(buffer.getRetainedDeferredTurns("s1"), []);
|
|
135
|
+
assert.deepEqual(buffer.getTurns("s1").map((turn) => turn.content), ["live turn"]);
|
|
136
|
+
const reloaded = await restart();
|
|
137
|
+
assert.deepEqual(reloaded.getRetainedDeferredTurns("s1"), [], "retained turns stay dropped after a restart");
|
|
138
|
+
assert.deepEqual(reloaded.getTurns("s1").map((turn) => turn.content), ["live turn"]);
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
test("a chunk that spans retained and live turns drops both", async () => {
|
|
143
|
+
await withBuffer(async (buffer, restart) => {
|
|
144
|
+
const retained = [makeTurn("s1", "retained one")];
|
|
145
|
+
const live = [makeTurn("s1", "live one"), makeTurn("s1", "live two")];
|
|
146
|
+
await buffer.retainDeferredTurns("s1", retained);
|
|
147
|
+
for (const turn of live) await buffer.addTurn("s1", turn);
|
|
148
|
+
await buffer.flushPendingSave();
|
|
149
|
+
|
|
150
|
+
const dropped = await buffer.dropExtractionChunk("s1", [...retained, live[0]!]);
|
|
151
|
+
|
|
152
|
+
assert.equal(dropped, 2);
|
|
153
|
+
assert.deepEqual((await restart()).getTurns("s1").map((turn) => turn.content), ["live two"]);
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
test("deferred retention across chunks of one window stays within one retention window (Codex P2)", async () => {
|
|
158
|
+
await withBuffer(async (buffer, restart) => {
|
|
159
|
+
const window = Array.from({ length: 30 }, (_, i) => makeTurn("s1", `turn ${i}`));
|
|
160
|
+
for (const turn of window) await buffer.addTurn("s1", turn);
|
|
161
|
+
// Three chunks of ten, each deferring all of its turns; every run names the whole window as the processed snapshot.
|
|
162
|
+
for (let chunk = 0; chunk < 3; chunk++) {
|
|
163
|
+
await buffer.retainDeferredTurns("s1", window.slice(chunk * 10, chunk * 10 + 10), 10, window);
|
|
164
|
+
}
|
|
165
|
+
assert.equal(buffer.getRetainedDeferredTurns("s1").length, 10, "the latest chunk's retention replaces the earlier ones");
|
|
166
|
+
assert.equal((await restart()).getRetainedDeferredTurns("s1").length, 10);
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
test("a chunk holding only some retained turns drops those and keeps the rest of the retention slot (Phosphene)", async () => {
|
|
171
|
+
await withBuffer(async (buffer, restart) => {
|
|
172
|
+
const retained = [makeTurn("s1", "retained one"), makeTurn("s1", "retained two"), makeTurn("s1", "retained three")];
|
|
173
|
+
await buffer.retainDeferredTurns("s1", retained);
|
|
174
|
+
await buffer.addTurn("s1", makeTurn("s1", "live turn"));
|
|
175
|
+
await buffer.flushPendingSave();
|
|
176
|
+
|
|
177
|
+
const dropped = await buffer.dropExtractionChunk("s1", retained.slice(0, 2));
|
|
178
|
+
|
|
179
|
+
assert.equal(dropped, 2);
|
|
180
|
+
assert.deepEqual(buffer.getRetainedDeferredTurns("s1").map((turn) => turn.content), ["retained three"]);
|
|
181
|
+
const reloaded = await restart();
|
|
182
|
+
assert.deepEqual(reloaded.getRetainedDeferredTurns("s1").map((turn) => turn.content), ["retained three"]);
|
|
183
|
+
assert.deepEqual(reloaded.getTurns("s1").map((turn) => turn.content), ["retained three", "live turn"]);
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
test("retention for an early chunk leaves the retained turns of a later, unprocessed chunk alone (Codex P2)", async () => {
|
|
188
|
+
await withBuffer(async (buffer, restart) => {
|
|
189
|
+
const retained = [makeTurn("s1", "early retained"), makeTurn("s1", "late retained")];
|
|
190
|
+
await buffer.retainDeferredTurns("s1", retained);
|
|
191
|
+
// Chunk 0 holds the first retained turn; nothing was deferred in it, so it clears the slot for the turns it processed.
|
|
192
|
+
await buffer.retainDeferredTurns("s1", [], 0, retained.slice(0, 1));
|
|
193
|
+
assert.deepEqual(buffer.getRetainedDeferredTurns("s1").map((turn) => turn.content), ["late retained"]);
|
|
194
|
+
assert.deepEqual((await restart()).getRetainedDeferredTurns("s1").map((turn) => turn.content), ["late retained"]);
|
|
195
|
+
});
|
|
196
|
+
});
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/** Types split out of buffer.ts (structural ratchet, issue #1995). */
|
|
2
|
+
|
|
3
|
+
import type { BufferTurn } from "./types.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Optional surprise probe injected into `SmartBuffer`.
|
|
7
|
+
*
|
|
8
|
+
* Computes a D-MEM-style novelty score in `[0, 1]` for an incoming turn.
|
|
9
|
+
* The buffer treats the probe as purely additive: if it is not provided, if
|
|
10
|
+
* the feature flag is off, or if the probe throws/times out, the buffer
|
|
11
|
+
* falls back to the existing signal/turn-count/time triggers unchanged.
|
|
12
|
+
*
|
|
13
|
+
* Callers are responsible for sampling recent memories and passing them
|
|
14
|
+
* through the embedding pipeline — the buffer does not want to know about
|
|
15
|
+
* storage, embeddings, or QMD.
|
|
16
|
+
*
|
|
17
|
+
* @param bufferKey Identifier for the active buffer (session/thread).
|
|
18
|
+
* @param turn The incoming turn whose novelty is being scored.
|
|
19
|
+
* @param recentTurns Turns already buffered for this key (most recent first
|
|
20
|
+
* is NOT guaranteed — treat as unordered corpus).
|
|
21
|
+
* @returns A surprise score in `[0, 1]`, or `null` if no score could be
|
|
22
|
+
* produced (e.g. empty corpus, probe declined to embed).
|
|
23
|
+
*/
|
|
24
|
+
export interface BufferSurpriseProbe {
|
|
25
|
+
scoreTurn(
|
|
26
|
+
bufferKey: string,
|
|
27
|
+
turn: BufferTurn,
|
|
28
|
+
recentTurns: readonly BufferTurn[],
|
|
29
|
+
): Promise<number | null>;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Minimal data carried on the serialized telemetry write chain
|
|
34
|
+
* (issue #563 PR 3).
|
|
35
|
+
*
|
|
36
|
+
* We intentionally do NOT capture the full `BufferTurn` here: under
|
|
37
|
+
* slow filesystem latency the chain can back up, and retaining
|
|
38
|
+
* `turn.content` for every pending append causes memory pressure on
|
|
39
|
+
* large conversations. Only the fields the ledger row actually needs
|
|
40
|
+
* cross the chain boundary.
|
|
41
|
+
*/
|
|
42
|
+
export interface SurpriseTelemetryQueueEntry {
|
|
43
|
+
bufferKey: string;
|
|
44
|
+
turnRole: "user" | "assistant";
|
|
45
|
+
sessionKey: string | null;
|
|
46
|
+
surpriseScore: number;
|
|
47
|
+
triggered: boolean;
|
|
48
|
+
turnCountInWindow: number;
|
|
49
|
+
/**
|
|
50
|
+
* ISO timestamp captured at the moment the turn was scored, NOT when
|
|
51
|
+
* the ledger append eventually runs. Backpressure on the serialized
|
|
52
|
+
* write chain could otherwise shift event timestamps away from the
|
|
53
|
+
* real decision moment and distort the distribution report (p90
|
|
54
|
+
* inflated, current-threshold row misidentified).
|
|
55
|
+
*/
|
|
56
|
+
timestamp: string;
|
|
57
|
+
/**
|
|
58
|
+
* Threshold value in force when `triggered` was computed. Must be
|
|
59
|
+
* snapshot here rather than read from `config` at emit time — a
|
|
60
|
+
* concurrent config change between queue and write would otherwise
|
|
61
|
+
* record `triggered=true` against a newer threshold the operator
|
|
62
|
+
* never set, distorting precision/recall interpretation.
|
|
63
|
+
*/
|
|
64
|
+
threshold: number;
|
|
65
|
+
}
|
package/src/buffer.ts
CHANGED
|
@@ -31,6 +31,9 @@ import {
|
|
|
31
31
|
|
|
32
32
|
export type TriggerDecision = "extract_now" | "extract_batch" | "keep_buffering";
|
|
33
33
|
|
|
34
|
+
export type { BufferSurpriseProbe } from "./buffer-types.js";
|
|
35
|
+
import type { BufferSurpriseProbe, SurpriseTelemetryQueueEntry } from "./buffer-types.js";
|
|
36
|
+
|
|
34
37
|
export interface AddTurnOutcome {
|
|
35
38
|
decision: TriggerDecision;
|
|
36
39
|
extractionTurns?: BufferTurn[];
|
|
@@ -46,33 +49,6 @@ export interface RetainedTurnCleanupOptions {
|
|
|
46
49
|
codingContext?: import("./types.js").CodingContext;
|
|
47
50
|
}
|
|
48
51
|
|
|
49
|
-
/**
|
|
50
|
-
* Optional surprise probe injected into `SmartBuffer`.
|
|
51
|
-
*
|
|
52
|
-
* Computes a D-MEM-style novelty score in `[0, 1]` for an incoming turn.
|
|
53
|
-
* The buffer treats the probe as purely additive: if it is not provided, if
|
|
54
|
-
* the feature flag is off, or if the probe throws/times out, the buffer
|
|
55
|
-
* falls back to the existing signal/turn-count/time triggers unchanged.
|
|
56
|
-
*
|
|
57
|
-
* Callers are responsible for sampling recent memories and passing them
|
|
58
|
-
* through the embedding pipeline — the buffer does not want to know about
|
|
59
|
-
* storage, embeddings, or QMD.
|
|
60
|
-
*
|
|
61
|
-
* @param bufferKey Identifier for the active buffer (session/thread).
|
|
62
|
-
* @param turn The incoming turn whose novelty is being scored.
|
|
63
|
-
* @param recentTurns Turns already buffered for this key (most recent first
|
|
64
|
-
* is NOT guaranteed — treat as unordered corpus).
|
|
65
|
-
* @returns A surprise score in `[0, 1]`, or `null` if no score could be
|
|
66
|
-
* produced (e.g. empty corpus, probe declined to embed).
|
|
67
|
-
*/
|
|
68
|
-
export interface BufferSurpriseProbe {
|
|
69
|
-
scoreTurn(
|
|
70
|
-
bufferKey: string,
|
|
71
|
-
turn: BufferTurn,
|
|
72
|
-
recentTurns: readonly BufferTurn[],
|
|
73
|
-
): Promise<number | null>;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
52
|
const MAX_BUFFER_ENTRY_COUNT = 200;
|
|
77
53
|
const isAcceptedTurn = (t: { acceptedCapture?: boolean; writeNamespaceOverride?: string }): boolean =>
|
|
78
54
|
t.acceptedCapture === true || t.writeNamespaceOverride !== undefined;
|
|
@@ -96,41 +72,6 @@ const BUFFER_SAVE_MAX_DEFER_MULTIPLIER = 5;
|
|
|
96
72
|
*/
|
|
97
73
|
const MAX_SET_TIMEOUT_MS = 2_147_483_647;
|
|
98
74
|
|
|
99
|
-
/**
|
|
100
|
-
* Minimal data carried on the serialized telemetry write chain
|
|
101
|
-
* (issue #563 PR 3).
|
|
102
|
-
*
|
|
103
|
-
* We intentionally do NOT capture the full `BufferTurn` here: under
|
|
104
|
-
* slow filesystem latency the chain can back up, and retaining
|
|
105
|
-
* `turn.content` for every pending append causes memory pressure on
|
|
106
|
-
* large conversations. Only the fields the ledger row actually needs
|
|
107
|
-
* cross the chain boundary.
|
|
108
|
-
*/
|
|
109
|
-
interface SurpriseTelemetryQueueEntry {
|
|
110
|
-
bufferKey: string;
|
|
111
|
-
turnRole: "user" | "assistant";
|
|
112
|
-
sessionKey: string | null;
|
|
113
|
-
surpriseScore: number;
|
|
114
|
-
triggered: boolean;
|
|
115
|
-
turnCountInWindow: number;
|
|
116
|
-
/**
|
|
117
|
-
* ISO timestamp captured at the moment the turn was scored, NOT when
|
|
118
|
-
* the ledger append eventually runs. Backpressure on the serialized
|
|
119
|
-
* write chain could otherwise shift event timestamps away from the
|
|
120
|
-
* real decision moment and distort the distribution report (p90
|
|
121
|
-
* inflated, current-threshold row misidentified).
|
|
122
|
-
*/
|
|
123
|
-
timestamp: string;
|
|
124
|
-
/**
|
|
125
|
-
* Threshold value in force when `triggered` was computed. Must be
|
|
126
|
-
* snapshot here rather than read from `config` at emit time — a
|
|
127
|
-
* concurrent config change between queue and write would otherwise
|
|
128
|
-
* record `triggered=true` against a newer threshold the operator
|
|
129
|
-
* never set, distorting precision/recall interpretation.
|
|
130
|
-
*/
|
|
131
|
-
threshold: number;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
75
|
interface AddTurnMutationResult {
|
|
135
76
|
decision: TriggerDecision;
|
|
136
77
|
signalLevel: SignalLevel;
|
|
@@ -1148,6 +1089,63 @@ export class SmartBuffer {
|
|
|
1148
1089
|
});
|
|
1149
1090
|
}
|
|
1150
1091
|
|
|
1092
|
+
/**
|
|
1093
|
+
* Remove an extraction-window chunk from the buffer WITHOUT recording an
|
|
1094
|
+
* extraction (trigger-drain drop path). Used when a chunk fails extraction
|
|
1095
|
+
* repeatedly and must be dropped so the session can drain; the caller is
|
|
1096
|
+
* responsible for the side-file copy and logging. Returns the number of
|
|
1097
|
+
* live turns dropped; 0 when the turns no longer match. `allowNonPrefix`
|
|
1098
|
+
* matches by identity, like `clearAfterExtraction`, for callers (native
|
|
1099
|
+
* groups) whose chunk can sit behind a pinned capture.
|
|
1100
|
+
*/
|
|
1101
|
+
async dropExtractionChunk(
|
|
1102
|
+
bufferKey = "default",
|
|
1103
|
+
turns: readonly BufferTurn[],
|
|
1104
|
+
options?: { allowNonPrefix?: boolean },
|
|
1105
|
+
): Promise<number> {
|
|
1106
|
+
return await this.enqueueMutation(async () => {
|
|
1107
|
+
await this.loadUnlocked();
|
|
1108
|
+
const entry = this.entryFor(bufferKey);
|
|
1109
|
+
// A window's head can be deferred-retained turns (retainDeferredTurns): take those out of the chunk first.
|
|
1110
|
+
const retained = [...(entry.retainedTurns ?? [])];
|
|
1111
|
+
const liveTurns: BufferTurn[] = [];
|
|
1112
|
+
for (const turn of turns) {
|
|
1113
|
+
const at = retained.findIndex((kept) => bufferTurnsEqual(kept, turn));
|
|
1114
|
+
if (at >= 0) retained.splice(at, 1);
|
|
1115
|
+
else liveTurns.push(turn);
|
|
1116
|
+
}
|
|
1117
|
+
const retainedDropped = (entry.retainedTurns?.length ?? 0) - retained.length;
|
|
1118
|
+
let remaining = entry.turns;
|
|
1119
|
+
if (options?.allowNonPrefix === true) {
|
|
1120
|
+
remaining = [...entry.turns];
|
|
1121
|
+
for (const turn of liveTurns) {
|
|
1122
|
+
const at = remaining.findIndex((live) => bufferTurnsEqual(live, turn));
|
|
1123
|
+
if (at >= 0) remaining.splice(at, 1);
|
|
1124
|
+
}
|
|
1125
|
+
} else {
|
|
1126
|
+
remaining = entry.turns.slice(matchingQueuedExtractionPrefixLength(entry.turns, liveTurns));
|
|
1127
|
+
}
|
|
1128
|
+
const matchedCount = entry.turns.length - remaining.length + retainedDropped;
|
|
1129
|
+
if (matchedCount === 0) return 0;
|
|
1130
|
+
if (this.saveTimer) {
|
|
1131
|
+
clearTimeout(this.saveTimer);
|
|
1132
|
+
this.saveTimer = null;
|
|
1133
|
+
}
|
|
1134
|
+
entry.turns = remaining;
|
|
1135
|
+
if (retainedDropped > 0) {
|
|
1136
|
+
if (retained.length > 0) entry.retainedTurns = retained;
|
|
1137
|
+
else delete entry.retainedTurns;
|
|
1138
|
+
}
|
|
1139
|
+
if (bufferKey === "default") {
|
|
1140
|
+
this.state.turns = entry.turns;
|
|
1141
|
+
}
|
|
1142
|
+
this.persistence.markDirty(bufferKey);
|
|
1143
|
+
this.pruneEntries([bufferKey]);
|
|
1144
|
+
await this.saveNowRetainingPendingOnFailure("dropExtractionChunk");
|
|
1145
|
+
return matchedCount;
|
|
1146
|
+
});
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1151
1149
|
getExtractionCount(bufferKey = "default"): number {
|
|
1152
1150
|
return this.peekEntry(bufferKey)?.extractionCount ?? 0;
|
|
1153
1151
|
}
|
package/src/config.ts
CHANGED
|
@@ -2868,6 +2868,14 @@ export function parseConfig(
|
|
|
2868
2868
|
extractionBreakerAuthCooldownMs:
|
|
2869
2869
|
typeof cfg.extractionBreakerAuthCooldownMs === "number" && cfg.extractionBreakerAuthCooldownMs >= 0
|
|
2870
2870
|
? cfg.extractionBreakerAuthCooldownMs : 1_800_000,
|
|
2871
|
+
// Trigger-drain drop threshold: consecutive failed extraction attempts
|
|
2872
|
+
// before the failing buffer chunk is dropped (side-file copied first).
|
|
2873
|
+
extractionChunkDropAfterFailures: parseIntegerAtLeast(
|
|
2874
|
+
cfg.extractionChunkDropAfterFailures,
|
|
2875
|
+
5,
|
|
2876
|
+
1,
|
|
2877
|
+
"extractionChunkDropAfterFailures",
|
|
2878
|
+
),
|
|
2871
2879
|
// Local LLM fast tier (v9.1)
|
|
2872
2880
|
localLlmFastEnabled: cfg.localLlmFastEnabled === true,
|
|
2873
2881
|
localLlmFastModel:
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import {
|
|
4
|
+
chunkExtractionWindow,
|
|
5
|
+
classifyDrainResult,
|
|
6
|
+
extractionWindowBudgetChars,
|
|
7
|
+
mergeDrainResults,
|
|
8
|
+
} from "./extraction-window-drain.js";
|
|
9
|
+
import type { ExtractionRunResult } from "./orchestration/extraction-run.js";
|
|
10
|
+
import type { BufferTurn } from "./types.js";
|
|
11
|
+
|
|
12
|
+
function makeTurn(content: string, sessionKey = "s1"): BufferTurn {
|
|
13
|
+
return { role: "user", content, timestamp: "2026-10-29T00:00:00.000Z", sessionKey };
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function skippedResult(reason: string): ExtractionRunResult {
|
|
17
|
+
return { status: "skipped", reason, persistedCount: 0, durableOutputCount: 0 };
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
test("chunkExtractionWindow returns a single chunk when the window fits the budget", () => {
|
|
21
|
+
const turns = [makeTurn("a".repeat(10)), makeTurn("b".repeat(10))];
|
|
22
|
+
const chunks = chunkExtractionWindow(turns, 100);
|
|
23
|
+
assert.equal(chunks.length, 1);
|
|
24
|
+
assert.deepEqual(chunks[0], turns);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test("chunkExtractionWindow splits an oversized window into budget-fitting chunks in order", () => {
|
|
28
|
+
const turns = [
|
|
29
|
+
makeTurn("a".repeat(60)),
|
|
30
|
+
makeTurn("b".repeat(60)),
|
|
31
|
+
makeTurn("c".repeat(60)),
|
|
32
|
+
makeTurn("d".repeat(60)),
|
|
33
|
+
];
|
|
34
|
+
const chunks = chunkExtractionWindow(turns, 100);
|
|
35
|
+
assert.equal(chunks.length, 4);
|
|
36
|
+
for (const chunk of chunks) {
|
|
37
|
+
assert.ok(chunk.every((turn) => turn.content.length <= 100));
|
|
38
|
+
}
|
|
39
|
+
assert.deepEqual(
|
|
40
|
+
chunks.flat().map((turn) => turn.content[0]),
|
|
41
|
+
["a", "b", "c", "d"],
|
|
42
|
+
);
|
|
43
|
+
assert.deepEqual(chunks.flat(), turns);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test("chunkExtractionWindow gives a single oversized turn its own chunk", () => {
|
|
47
|
+
const turns = [makeTurn("x".repeat(250)), makeTurn("y".repeat(10))];
|
|
48
|
+
const chunks = chunkExtractionWindow(turns, 100);
|
|
49
|
+
assert.equal(chunks.length, 2);
|
|
50
|
+
assert.equal(chunks[0]?.length, 1);
|
|
51
|
+
assert.equal(chunks[1]?.length, 1);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test("chunkExtractionWindow treats a non-positive budget as no chunking", () => {
|
|
55
|
+
const turns = [makeTurn("a".repeat(500)), makeTurn("b".repeat(500))];
|
|
56
|
+
assert.equal(chunkExtractionWindow(turns, 0).length, 1);
|
|
57
|
+
assert.equal(chunkExtractionWindow(turns, Number.POSITIVE_INFINITY).length, 1);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test("chunkExtractionWindow returns no chunks for an empty window", () => {
|
|
61
|
+
assert.deepEqual(chunkExtractionWindow([], 100), []);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test("classifyDrainResult treats completed runs and benign skips as drained", () => {
|
|
65
|
+
assert.equal(classifyDrainResult({ status: "completed", persistedCount: 1, durableOutputCount: 2 }), "drained");
|
|
66
|
+
assert.equal(classifyDrainResult(skippedResult("below_threshold")), "drained");
|
|
67
|
+
assert.equal(classifyDrainResult(skippedResult("cron_session")), "drained");
|
|
68
|
+
assert.equal(classifyDrainResult(skippedResult("empty_normalized_turns")), "drained");
|
|
69
|
+
assert.equal(classifyDrainResult(skippedResult("processed_fingerprint")), "drained");
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test("classifyDrainResult reports breaker/backoff skips as deferred", () => {
|
|
73
|
+
assert.equal(classifyDrainResult(skippedResult("provider_circuit_open")), "deferred");
|
|
74
|
+
assert.equal(classifyDrainResult(skippedResult("extraction_backoff")), "deferred");
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test("classifyDrainResult reports empty extraction results as failed", () => {
|
|
78
|
+
assert.equal(
|
|
79
|
+
classifyDrainResult({ ...skippedResult("empty_extraction_result"), retainedBuffer: true }),
|
|
80
|
+
"failed",
|
|
81
|
+
"an empty result that kept the window for retry is a chunk failure",
|
|
82
|
+
);
|
|
83
|
+
assert.equal(
|
|
84
|
+
classifyDrainResult(skippedResult("empty_extraction_result")),
|
|
85
|
+
"drained",
|
|
86
|
+
"an empty result that cleared its turns extracted fine and found nothing durable",
|
|
87
|
+
);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test("mergeDrainResults sums counts, completes when any chunk completes, keeps the last skip reason", () => {
|
|
91
|
+
const first = skippedResult("empty_extraction_result");
|
|
92
|
+
const second: ExtractionRunResult = { status: "completed", persistedCount: 3, durableOutputCount: 4 };
|
|
93
|
+
// A completed chunk means the drain made durable progress.
|
|
94
|
+
const merged = mergeDrainResults(mergeDrainResults(null, first), second);
|
|
95
|
+
assert.equal(merged.status, "completed");
|
|
96
|
+
assert.equal(merged.persistedCount, 3);
|
|
97
|
+
assert.equal(merged.durableOutputCount, 4);
|
|
98
|
+
// All chunks skipped: the aggregate stays skipped with the last reason.
|
|
99
|
+
const mergedFailed = mergeDrainResults(
|
|
100
|
+
mergeDrainResults(null, skippedResult("provider_circuit_open")),
|
|
101
|
+
first,
|
|
102
|
+
);
|
|
103
|
+
assert.equal(mergedFailed.status, "skipped");
|
|
104
|
+
assert.equal(mergedFailed.reason, "empty_extraction_result");
|
|
105
|
+
assert.equal(mergedFailed.persistedCount, 0);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test("extractionWindowBudgetChars applies the learned local window only when the local model extracts", () => {
|
|
109
|
+
const registry = { calculateContextSizes: () => ({ maxInputChars: 12_000 }) };
|
|
110
|
+
const base = { registry, model: "local-model", maxContext: undefined };
|
|
111
|
+
|
|
112
|
+
assert.equal(extractionWindowBudgetChars({ ...base, localExtractionActive: true }), 12_000);
|
|
113
|
+
assert.equal(
|
|
114
|
+
extractionWindowBudgetChars({ ...base, localExtractionActive: false }),
|
|
115
|
+
Number.POSITIVE_INFINITY,
|
|
116
|
+
"gateway or disabled local LLM: the local window does not describe the extractor",
|
|
117
|
+
);
|
|
118
|
+
assert.equal(
|
|
119
|
+
extractionWindowBudgetChars({ ...base, registry: undefined, localExtractionActive: true }),
|
|
120
|
+
Number.POSITIVE_INFINITY,
|
|
121
|
+
"no registry (legacy host or test double): no budget",
|
|
122
|
+
);
|
|
123
|
+
});
|