@remnic/core 9.6.39 → 9.7.0
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.d.ts +6 -6
- package/dist/access-admin-ops-surface.js +16 -16
- package/dist/access-boundary.d.ts +6 -6
- package/dist/access-boundary.js +17 -17
- package/dist/access-cli.js +56 -55
- package/dist/access-cli.js.map +1 -1
- package/dist/access-http.d.ts +6 -6
- package/dist/access-http.js +22 -22
- package/dist/access-identity-continuity-surface.d.ts +5 -5
- package/dist/access-identity-continuity-surface.js +16 -16
- package/dist/access-lcm-surface.d.ts +6 -6
- package/dist/access-lcm-surface.js +16 -16
- package/dist/access-mcp.d.ts +6 -6
- package/dist/access-mcp.js +21 -21
- package/dist/access-observe-write-surface.d.ts +6 -6
- package/dist/access-observe-write-surface.js +16 -16
- package/dist/access-operations-batch.js +19 -19
- package/dist/access-operations.d.ts +9 -9
- package/dist/access-operations.js +20 -20
- package/dist/access-recall-surface.d.ts +6 -6
- package/dist/access-recall-surface.js +16 -16
- package/dist/access-schema.d.ts +64 -64
- package/dist/{access-service-Fw9AYEqG.d.ts → access-service-CJ5hrLqR.d.ts} +4 -4
- package/dist/access-service.d.ts +6 -6
- package/dist/access-service.js +16 -16
- package/dist/access-surface-catalog.d.ts +6 -6
- package/dist/action-confidence.d.ts +1 -1
- package/dist/active-memory-bridge.d.ts +1 -1
- package/dist/active-memory-bridge.js +3 -3
- package/dist/active-recall.d.ts +1 -1
- package/dist/active-recall.js +1 -1
- package/dist/{auto-sync-4A5YBWYO.js → auto-sync-676N6N6Y.js} +4 -4
- package/dist/behavior-learner.d.ts +1 -1
- package/dist/behavior-signals.d.ts +1 -1
- package/dist/bootstrap.d.ts +5 -5
- package/dist/briefing.d.ts +2 -2
- package/dist/briefing.js +5 -5
- package/dist/buffer-surprise-report.d.ts +1 -1
- package/dist/buffer.d.ts +2 -2
- package/dist/buffer.js +2 -2
- package/dist/bulk-import/index.d.ts +3 -3
- package/dist/calibration.d.ts +1 -1
- package/dist/calibration.js +1 -1
- package/dist/capabilities.d.ts +4 -2
- package/dist/capabilities.js +1 -1
- package/dist/{catalog-CnS_MP-8.d.ts → catalog-XCOyxXKw.d.ts} +1 -1
- package/dist/causal-behavior.d.ts +1 -1
- package/dist/causal-consolidation.d.ts +1 -1
- package/dist/causal-consolidation.js +7 -7
- package/dist/causal-trajectory-graph.d.ts +1 -1
- package/dist/{chunk-SFHI7JBE.js → chunk-2FSHROYK.js} +3 -3
- package/dist/{chunk-WHZ7MK72.js → chunk-4YUPLX7U.js} +3 -3
- package/dist/{chunk-ZIKJD6NL.js → chunk-53Y3TETS.js} +2 -2
- package/dist/{chunk-UI4FVU6T.js → chunk-5CJVLG3H.js} +2 -2
- package/dist/{chunk-BAVVI5GP.js → chunk-7PND4PUC.js} +3 -3
- package/dist/{chunk-NREKOGZ2.js → chunk-ARD5NE4M.js} +3 -3
- package/dist/{chunk-GCTJG5MP.js → chunk-AWHOZTF6.js} +4 -4
- package/dist/{chunk-VSF7AAGE.js → chunk-BFPETA3S.js} +3 -2
- package/dist/chunk-BFPETA3S.js.map +1 -0
- package/dist/{chunk-ZGPDSEKG.js → chunk-BFVEEB7W.js} +2 -2
- package/dist/{chunk-AZK3NEHL.js → chunk-BLXNPBJI.js} +2 -2
- package/dist/chunk-C7SRUWBW.js +54 -0
- package/dist/chunk-C7SRUWBW.js.map +1 -0
- package/dist/{chunk-VTRF5QBX.js → chunk-CBOHQNTJ.js} +2 -2
- package/dist/{chunk-ZSDRGPMV.js → chunk-D6KNJRGX.js} +2 -2
- package/dist/{chunk-2V6L3L56.js → chunk-DDHT5J5M.js} +33 -8
- package/dist/chunk-DDHT5J5M.js.map +1 -0
- package/dist/{chunk-3WULMX34.js → chunk-DSWYYEIK.js} +10 -1
- package/dist/chunk-DSWYYEIK.js.map +1 -0
- package/dist/{chunk-U5JP7FCM.js → chunk-E47TABDR.js} +2 -2
- package/dist/{chunk-7TEWOG3R.js → chunk-FMECZFUT.js} +2 -2
- package/dist/{chunk-QWGNDWKB.js → chunk-FNMFJWBY.js} +2 -2
- package/dist/{chunk-CZGIIADK.js → chunk-FXGC5UUH.js} +56 -11
- package/dist/chunk-FXGC5UUH.js.map +1 -0
- package/dist/{chunk-JIKJPDQJ.js → chunk-GKCCHHZK.js} +15 -5
- package/dist/chunk-GKCCHHZK.js.map +1 -0
- package/dist/{chunk-EDD2G35Z.js → chunk-GWMTFDHF.js} +4 -4
- package/dist/{chunk-VCW74PNH.js → chunk-H67QFYUK.js} +2 -2
- package/dist/{chunk-5ELELET5.js → chunk-HT2DQ27U.js} +3 -3
- package/dist/{chunk-VU3SVYMA.js → chunk-HTIIXNJQ.js} +2 -2
- package/dist/{chunk-I73G4UH7.js → chunk-IALLX7EE.js} +2 -2
- package/dist/{chunk-PE6XR4GG.js → chunk-IKQTFQMI.js} +2 -2
- package/dist/{chunk-XZ5I7OZR.js → chunk-KIPLG7S7.js} +4 -4
- package/dist/{chunk-QLVLW7UD.js → chunk-KSBEEMQF.js} +17 -5
- package/dist/chunk-KSBEEMQF.js.map +1 -0
- package/dist/{chunk-RU73ZROD.js → chunk-KWJP22WP.js} +12 -12
- package/dist/{chunk-MNUHL34L.js → chunk-LKT755LP.js} +2 -2
- package/dist/{chunk-PI23H7FV.js → chunk-M7VGG3ID.js} +3 -3
- package/dist/{chunk-PWSZNML3.js → chunk-NGJT7IW5.js} +284 -38
- package/dist/chunk-NGJT7IW5.js.map +1 -0
- package/dist/{chunk-HVE3UVAB.js → chunk-NS4GEMLR.js} +3 -3
- package/dist/{chunk-ZMXXHUEZ.js → chunk-O24E5CFS.js} +3 -3
- package/dist/{chunk-3BZDBG7U.js → chunk-PML3GU4Q.js} +3 -3
- package/dist/{chunk-DQ2CJYUM.js → chunk-PSQ32V5P.js} +3 -3
- package/dist/{chunk-7XY6TJLM.js → chunk-PUPQ2PMC.js} +3 -3
- package/dist/{chunk-EBVUFGJ4.js → chunk-QFOLNA2C.js} +2 -2
- package/dist/{chunk-METJNEIB.js → chunk-QMUERLZK.js} +1 -1
- package/dist/chunk-QMUERLZK.js.map +1 -0
- package/dist/{chunk-PQG2AOL4.js → chunk-QRYNEVHP.js} +28 -4
- package/dist/{chunk-PQG2AOL4.js.map → chunk-QRYNEVHP.js.map} +1 -1
- package/dist/{chunk-CVEEJKRD.js → chunk-RQTCDICG.js} +2 -2
- package/dist/{chunk-F7AE7L6G.js → chunk-RZEJDF3S.js} +2 -2
- package/dist/{chunk-YFBTVOMB.js → chunk-SYNYI4ZM.js} +2 -2
- package/dist/{chunk-BRDJ2HFP.js → chunk-TRWPCR6P.js} +2 -2
- package/dist/{chunk-YAN6NTJN.js → chunk-UKA7GNI2.js} +3 -3
- package/dist/{chunk-TQNRI55H.js → chunk-UR6PBX2J.js} +4 -51
- package/dist/chunk-UR6PBX2J.js.map +1 -0
- package/dist/{chunk-XH2Y6UIW.js → chunk-VD56CJRR.js} +4 -4
- package/dist/{chunk-J3XHPFEU.js → chunk-VXMWDXIJ.js} +2 -2
- package/dist/{chunk-QT2RZNRT.js → chunk-VY6LKKVN.js} +2 -2
- package/dist/{chunk-PRTA6444.js → chunk-W4PGTAS2.js} +2 -2
- package/dist/{chunk-WX4XTAOI.js → chunk-WGE3OYC3.js} +7 -7
- package/dist/{chunk-RVLE2SNP.js → chunk-X6FSCBJZ.js} +22 -22
- package/dist/{chunk-6MWGGPJJ.js → chunk-Y4EMHIQ5.js} +7 -7
- package/dist/{chunk-IGCQHJSQ.js → chunk-Z3HJ5L3Z.js} +2 -2
- package/dist/{chunk-O2EL7EGG.js → chunk-ZQ7UBUZ6.js} +2 -2
- package/dist/{chunk-5SORASJK.js → chunk-ZXUG3AFP.js} +2 -2
- package/dist/{cli-PGF41MN6.d.ts → cli-CtECZr1q.d.ts} +5 -5
- package/dist/cli.d.ts +8 -8
- package/dist/cli.js +37 -36
- package/dist/compounding/engine.d.ts +2 -2
- package/dist/compounding/engine.js +7 -7
- package/dist/compounding/preference-consolidator.d.ts +1 -1
- package/dist/compression-optimizer.d.ts +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.js +1 -1
- package/dist/connectors/codex-materialize-runner.d.ts +1 -1
- package/dist/connectors/codex-materialize-runner.js +5 -5
- package/dist/connectors/codex-materialize.d.ts +1 -1
- package/dist/connectors/index.d.ts +1 -1
- package/dist/connectors/index.js +7 -7
- package/dist/consolidation-provenance-check.d.ts +2 -2
- package/dist/consolidation-undo.d.ts +2 -2
- package/dist/contradiction/index.d.ts +2 -2
- package/dist/contradiction/index.js +2 -2
- package/dist/{contradiction-scan-SSKCAMLX.js → contradiction-scan-J57T5QV7.js} +3 -3
- package/dist/conversation-index/backend.d.ts +1 -1
- package/dist/conversation-index/chunker.d.ts +1 -1
- package/dist/conversation-index/faiss-adapter.d.ts +1 -1
- package/dist/conversation-index/indexer.d.ts +1 -1
- package/dist/conversation-index/search.d.ts +1 -1
- package/dist/day-summary.d.ts +1 -1
- package/dist/day-summary.js +2 -2
- package/dist/delinearize.d.ts +1 -1
- package/dist/direct-answer-wiring.d.ts +1 -1
- package/dist/direct-answer.d.ts +1 -1
- package/dist/embedding-fallback.d.ts +1 -1
- package/dist/embedding-fallback.js +2 -2
- package/dist/enrichment/index.d.ts +1 -1
- package/dist/entity-retrieval.d.ts +2 -2
- package/dist/entity-retrieval.js +6 -6
- package/dist/entity-schema.d.ts +1 -1
- package/dist/explicit-capture.d.ts +5 -5
- package/dist/explicit-capture.js +2 -2
- package/dist/extraction-faithfulness.d.ts +1 -1
- package/dist/extraction-faithfulness.js +2 -2
- package/dist/extraction-judge-telemetry.d.ts +1 -1
- package/dist/extraction-judge-training.d.ts +1 -1
- package/dist/extraction-judge.d.ts +1 -1
- package/dist/extraction-judge.js +2 -2
- package/dist/extraction.d.ts +17 -2
- package/dist/extraction.js +12 -7
- package/dist/fallback-llm.d.ts +5 -2
- package/dist/fallback-llm.js +1 -1
- package/dist/{first-start-migration-4I3VDSYS.js → first-start-migration-TI67YMLG.js} +2 -2
- package/dist/graph-dashboard-diff.d.ts +1 -1
- package/dist/graph-dashboard-key.d.ts +1 -1
- package/dist/graph-dashboard-parser.d.ts +1 -1
- package/dist/graph-edge-reinforcement.d.ts +1 -1
- package/dist/graph-snapshot.d.ts +1 -1
- package/dist/graph.d.ts +1 -1
- package/dist/identity-continuity.d.ts +1 -1
- package/dist/importance.d.ts +1 -1
- package/dist/importers/index.d.ts +1 -1
- package/dist/index.d.ts +13 -13
- package/dist/index.js +56 -55
- package/dist/intent.d.ts +1 -1
- package/dist/lcm/engine.d.ts +1 -1
- package/dist/lcm/index.d.ts +1 -1
- package/dist/lcm/tools.d.ts +1 -1
- package/dist/lifecycle.d.ts +1 -1
- package/dist/live-connectors-runner.d.ts +1 -1
- package/dist/live-connectors-runner.js +3 -2
- package/dist/local-llm.d.ts +1 -1
- package/dist/local-llm.js +2 -2
- package/dist/local-model-endpoint.d.ts +1 -1
- package/dist/maintenance/memory-governance.d.ts +1 -1
- package/dist/maintenance/memory-governance.js +5 -5
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +5 -5
- package/dist/maintenance/rebuild-memory-projection.js +6 -6
- package/dist/mcp-memory-inspector-app.d.ts +6 -6
- package/dist/memory-action-policy.d.ts +1 -1
- package/dist/memory-cache.d.ts +1 -1
- package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
- package/dist/memory-projection-store.d.ts +1 -1
- package/dist/memory-provenance.d.ts +1 -1
- package/dist/memory-worth-outcomes.d.ts +2 -2
- package/dist/models-json.d.ts +1 -1
- package/dist/namespaces/migrate.d.ts +3 -3
- package/dist/namespaces/migrate.js +10 -10
- package/dist/namespaces/principal.d.ts +1 -1
- package/dist/namespaces/principal.js +2 -2
- package/dist/namespaces/search.d.ts +1 -1
- package/dist/namespaces/search.js +4 -4
- package/dist/namespaces/storage.d.ts +3 -3
- package/dist/namespaces/storage.js +6 -6
- package/dist/native-knowledge.d.ts +1 -1
- package/dist/operator-toolkit.d.ts +2 -2
- package/dist/operator-toolkit.js +16 -16
- package/dist/orchestration/compression-guideline-coordinator.d.ts +2 -2
- package/dist/orchestration/compression-guideline-coordinator.js +2 -2
- package/dist/orchestration/maintenance.d.ts +2 -2
- package/dist/orchestration/maintenance.js +10 -10
- package/dist/{orchestrator-xePY-WPe.d.ts → orchestrator-CKctWsw9.d.ts} +51 -4
- package/dist/orchestrator.d.ts +5 -5
- package/dist/orchestrator.js +56 -55
- package/dist/patterns-cli.d.ts +1 -1
- package/dist/{pipeline-Zgwz-Kne.d.ts → pipeline-BWwGIys5.d.ts} +1 -1
- package/dist/policy-runtime.d.ts +1 -1
- package/dist/provenance.d.ts +1 -1
- package/dist/{qmd-B6Xwfn5_.d.ts → qmd-DFuUrBWW.d.ts} +1 -1
- package/dist/qmd-preflight.d.ts +2 -2
- package/dist/qmd-recall-cache.d.ts +1 -1
- package/dist/qmd.d.ts +2 -2
- package/dist/recall-disclosure-escalation.d.ts +1 -1
- package/dist/recall-explain-renderer.d.ts +1 -1
- package/dist/recall-explain-renderer.js +3 -3
- package/dist/recall-planner-llm.d.ts +1 -1
- package/dist/recall-planner-llm.js +1 -1
- package/dist/recall-state.d.ts +1 -1
- package/dist/recall-tag-filter.d.ts +1 -1
- package/dist/recall-timings.d.ts +1 -1
- package/dist/recall-xray-cli.d.ts +1 -1
- package/dist/recall-xray-cli.js +4 -4
- package/dist/recall-xray-renderer.d.ts +1 -1
- package/dist/recall-xray-renderer.js +3 -3
- package/dist/recall-xray.d.ts +1 -1
- package/dist/recall-xray.js +2 -2
- package/dist/replay/normalizers/chatgpt.d.ts +1 -1
- package/dist/replay/normalizers/claude.d.ts +1 -1
- package/dist/replay/normalizers/openclaw.d.ts +1 -1
- package/dist/replay/normalizers/shared.d.ts +1 -1
- package/dist/replay/runner.d.ts +1 -1
- package/dist/replay/types.d.ts +1 -1
- package/dist/resolve-auth-token.d.ts +1 -1
- package/dist/resume-bundles.js +2 -2
- package/dist/retrieval-agents.d.ts +2 -2
- package/dist/retrieval-tiers.d.ts +1 -1
- package/dist/routing/engine.d.ts +1 -1
- package/dist/routing/store.d.ts +1 -1
- package/dist/schemas.d.ts +52 -52
- package/dist/search/embed-helper.d.ts +1 -1
- package/dist/search/embed-helper.js +2 -2
- package/dist/search/factory.d.ts +1 -1
- package/dist/search/factory.js +3 -3
- package/dist/search/index.d.ts +1 -1
- package/dist/search/index.js +3 -3
- package/dist/search/lancedb-backend.d.ts +1 -1
- package/dist/search/meilisearch-backend.d.ts +1 -1
- package/dist/search/noop-backend.d.ts +1 -1
- package/dist/search/orama-backend.d.ts +1 -1
- package/dist/search/port.d.ts +1 -1
- package/dist/search/remote-backend.d.ts +1 -1
- package/dist/{semantic-consolidation-ClfD_CWg.d.ts → semantic-consolidation-1Wlr7NWL.d.ts} +1 -1
- package/dist/semantic-consolidation.d.ts +2 -2
- package/dist/semantic-consolidation.js +7 -7
- package/dist/semantic-rule-promotion.js +5 -5
- package/dist/semantic-rule-verifier.d.ts +1 -1
- package/dist/semantic-rule-verifier.js +5 -5
- package/dist/session-observer-bands.d.ts +1 -1
- package/dist/session-observer-state.d.ts +1 -1
- package/dist/shared-context/manager.d.ts +9 -9
- package/dist/shared-context/manager.js +2 -2
- package/dist/signal.d.ts +1 -1
- package/dist/{storage-ZrHZ0dSM.d.ts → storage-BQwP90sb.d.ts} +1 -1
- package/dist/storage.d.ts +2 -2
- package/dist/storage.js +4 -4
- package/dist/summarizer.d.ts +1 -1
- package/dist/summarizer.js +4 -4
- package/dist/summary-snapshot.d.ts +1 -1
- package/dist/temporal-supersession.d.ts +2 -2
- package/dist/temporal-timeline-recall.d.ts +1 -1
- package/dist/temporal-validity.d.ts +1 -1
- package/dist/threading.d.ts +1 -1
- package/dist/tier-migration.d.ts +2 -2
- package/dist/tier-routing.d.ts +1 -1
- package/dist/{tier-stats-5XF4UBRQ.js → tier-stats-KV7IWCJR.js} +2 -2
- package/dist/topics.d.ts +1 -1
- package/dist/transcript.d.ts +1 -1
- package/dist/transfer/types.d.ts +66 -66
- package/dist/trust-score-stage.d.ts +1 -1
- package/dist/trust-score.d.ts +1 -1
- package/dist/{types-BbtQZBq9.d.ts → types-DgIkyAsK.d.ts} +39 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.js +1 -1
- package/dist/utility-runtime.d.ts +1 -1
- package/dist/verified-recall.js +5 -5
- package/package.json +2 -2
- package/src/buffer-session.test.ts +40 -8
- package/src/buffer.ts +41 -17
- package/src/capabilities.test.ts +1 -0
- package/src/capabilities.ts +4 -0
- package/src/config.test.ts +19 -0
- package/src/config.ts +29 -0
- package/src/extraction.test.ts +39 -0
- package/src/extraction.ts +72 -2
- package/src/fallback-llm.ts +26 -5
- package/src/operator-toolkit.ts +53 -0
- package/src/orchestration/extraction-run.test.ts +639 -0
- package/src/orchestration/extraction-run.ts +366 -2
- package/src/orchestration/session-context.ts +2 -0
- package/src/orchestration/turn-ingestion.ts +5 -0
- package/src/orchestrator-flush.test.ts +36 -2
- package/src/orchestrator.ts +12 -1
- package/src/storage.ts +34 -0
- package/src/types.ts +40 -0
- package/dist/chunk-2V6L3L56.js.map +0 -1
- package/dist/chunk-3WULMX34.js.map +0 -1
- package/dist/chunk-CZGIIADK.js.map +0 -1
- package/dist/chunk-JIKJPDQJ.js.map +0 -1
- package/dist/chunk-METJNEIB.js.map +0 -1
- package/dist/chunk-PWSZNML3.js.map +0 -1
- package/dist/chunk-QLVLW7UD.js.map +0 -1
- package/dist/chunk-TQNRI55H.js.map +0 -1
- package/dist/chunk-VSF7AAGE.js.map +0 -1
- /package/dist/{auto-sync-4A5YBWYO.js.map → auto-sync-676N6N6Y.js.map} +0 -0
- /package/dist/{chunk-SFHI7JBE.js.map → chunk-2FSHROYK.js.map} +0 -0
- /package/dist/{chunk-WHZ7MK72.js.map → chunk-4YUPLX7U.js.map} +0 -0
- /package/dist/{chunk-ZIKJD6NL.js.map → chunk-53Y3TETS.js.map} +0 -0
- /package/dist/{chunk-UI4FVU6T.js.map → chunk-5CJVLG3H.js.map} +0 -0
- /package/dist/{chunk-BAVVI5GP.js.map → chunk-7PND4PUC.js.map} +0 -0
- /package/dist/{chunk-NREKOGZ2.js.map → chunk-ARD5NE4M.js.map} +0 -0
- /package/dist/{chunk-GCTJG5MP.js.map → chunk-AWHOZTF6.js.map} +0 -0
- /package/dist/{chunk-ZGPDSEKG.js.map → chunk-BFVEEB7W.js.map} +0 -0
- /package/dist/{chunk-AZK3NEHL.js.map → chunk-BLXNPBJI.js.map} +0 -0
- /package/dist/{chunk-VTRF5QBX.js.map → chunk-CBOHQNTJ.js.map} +0 -0
- /package/dist/{chunk-ZSDRGPMV.js.map → chunk-D6KNJRGX.js.map} +0 -0
- /package/dist/{chunk-U5JP7FCM.js.map → chunk-E47TABDR.js.map} +0 -0
- /package/dist/{chunk-7TEWOG3R.js.map → chunk-FMECZFUT.js.map} +0 -0
- /package/dist/{chunk-QWGNDWKB.js.map → chunk-FNMFJWBY.js.map} +0 -0
- /package/dist/{chunk-EDD2G35Z.js.map → chunk-GWMTFDHF.js.map} +0 -0
- /package/dist/{chunk-VCW74PNH.js.map → chunk-H67QFYUK.js.map} +0 -0
- /package/dist/{chunk-5ELELET5.js.map → chunk-HT2DQ27U.js.map} +0 -0
- /package/dist/{chunk-VU3SVYMA.js.map → chunk-HTIIXNJQ.js.map} +0 -0
- /package/dist/{chunk-I73G4UH7.js.map → chunk-IALLX7EE.js.map} +0 -0
- /package/dist/{chunk-PE6XR4GG.js.map → chunk-IKQTFQMI.js.map} +0 -0
- /package/dist/{chunk-XZ5I7OZR.js.map → chunk-KIPLG7S7.js.map} +0 -0
- /package/dist/{chunk-RU73ZROD.js.map → chunk-KWJP22WP.js.map} +0 -0
- /package/dist/{chunk-MNUHL34L.js.map → chunk-LKT755LP.js.map} +0 -0
- /package/dist/{chunk-PI23H7FV.js.map → chunk-M7VGG3ID.js.map} +0 -0
- /package/dist/{chunk-HVE3UVAB.js.map → chunk-NS4GEMLR.js.map} +0 -0
- /package/dist/{chunk-ZMXXHUEZ.js.map → chunk-O24E5CFS.js.map} +0 -0
- /package/dist/{chunk-3BZDBG7U.js.map → chunk-PML3GU4Q.js.map} +0 -0
- /package/dist/{chunk-DQ2CJYUM.js.map → chunk-PSQ32V5P.js.map} +0 -0
- /package/dist/{chunk-7XY6TJLM.js.map → chunk-PUPQ2PMC.js.map} +0 -0
- /package/dist/{chunk-EBVUFGJ4.js.map → chunk-QFOLNA2C.js.map} +0 -0
- /package/dist/{chunk-CVEEJKRD.js.map → chunk-RQTCDICG.js.map} +0 -0
- /package/dist/{chunk-F7AE7L6G.js.map → chunk-RZEJDF3S.js.map} +0 -0
- /package/dist/{chunk-YFBTVOMB.js.map → chunk-SYNYI4ZM.js.map} +0 -0
- /package/dist/{chunk-BRDJ2HFP.js.map → chunk-TRWPCR6P.js.map} +0 -0
- /package/dist/{chunk-YAN6NTJN.js.map → chunk-UKA7GNI2.js.map} +0 -0
- /package/dist/{chunk-XH2Y6UIW.js.map → chunk-VD56CJRR.js.map} +0 -0
- /package/dist/{chunk-J3XHPFEU.js.map → chunk-VXMWDXIJ.js.map} +0 -0
- /package/dist/{chunk-QT2RZNRT.js.map → chunk-VY6LKKVN.js.map} +0 -0
- /package/dist/{chunk-PRTA6444.js.map → chunk-W4PGTAS2.js.map} +0 -0
- /package/dist/{chunk-WX4XTAOI.js.map → chunk-WGE3OYC3.js.map} +0 -0
- /package/dist/{chunk-RVLE2SNP.js.map → chunk-X6FSCBJZ.js.map} +0 -0
- /package/dist/{chunk-6MWGGPJJ.js.map → chunk-Y4EMHIQ5.js.map} +0 -0
- /package/dist/{chunk-IGCQHJSQ.js.map → chunk-Z3HJ5L3Z.js.map} +0 -0
- /package/dist/{chunk-O2EL7EGG.js.map → chunk-ZQ7UBUZ6.js.map} +0 -0
- /package/dist/{chunk-5SORASJK.js.map → chunk-ZXUG3AFP.js.map} +0 -0
- /package/dist/{contradiction-scan-SSKCAMLX.js.map → contradiction-scan-J57T5QV7.js.map} +0 -0
- /package/dist/{first-start-migration-4I3VDSYS.js.map → first-start-migration-TI67YMLG.js.map} +0 -0
- /package/dist/{tier-stats-5XF4UBRQ.js.map → tier-stats-KV7IWCJR.js.map} +0 -0
|
@@ -492,13 +492,19 @@ test("getTurns prepends retained deferred turns before live turns", async () =>
|
|
|
492
492
|
assert.equal(all[1]?.content, "new live turn");
|
|
493
493
|
});
|
|
494
494
|
|
|
495
|
-
test("SmartBuffer
|
|
495
|
+
test("SmartBuffer prefers pruning empty entries and bounds pending-turn buffers at the cap (#1908 policy)", async () => {
|
|
496
|
+
// Pre-#1908 this contract was "never prune non-empty entries". With retry
|
|
497
|
+
// retention (failed extractions keep their turns), unbounded non-empty
|
|
498
|
+
// growth during a provider outage would eventually OOM the daemon — the
|
|
499
|
+
// documented policy (#1908 step 7) is: retain up to MAX_BUFFER_ENTRY_COUNT
|
|
500
|
+
// session entries, then prune the OLDEST sessions with a loud warning.
|
|
501
|
+
// Empty entries are still evicted first.
|
|
496
502
|
const entries = Object.fromEntries(
|
|
497
503
|
Array.from({ length: 205 }, (_, index) => [
|
|
498
504
|
`thread-${index}`,
|
|
499
505
|
{
|
|
500
506
|
turns: [makeTurn(`thread-${index}`, `memory ${index}`)],
|
|
501
|
-
lastExtractionAt:
|
|
507
|
+
lastExtractionAt: new Date(1_700_000_000_000 + index * 60_000).toISOString(),
|
|
502
508
|
extractionCount: 0,
|
|
503
509
|
},
|
|
504
510
|
]),
|
|
@@ -516,14 +522,40 @@ test("SmartBuffer never prunes logical session buffers that still have pending t
|
|
|
516
522
|
...entries,
|
|
517
523
|
},
|
|
518
524
|
});
|
|
519
|
-
const buffer = new SmartBuffer(parseConfig({}), storage as
|
|
525
|
+
const buffer = new SmartBuffer(parseConfig({}), storage as unknown as ConstructorParameters<typeof SmartBuffer>[1]);
|
|
520
526
|
|
|
521
527
|
await buffer.addTurn("active-thread", makeTurn("active-thread", "pending memory"));
|
|
522
528
|
|
|
523
529
|
const persistedKeys = Object.keys(storage.saved?.entries ?? {});
|
|
524
|
-
assert.
|
|
525
|
-
assert.ok(persistedKeys.includes("default"));
|
|
526
|
-
assert.ok(persistedKeys.includes("active-thread"));
|
|
527
|
-
assert.ok(persistedKeys.includes("thread-0"));
|
|
528
|
-
assert.ok(persistedKeys.includes("thread-204"));
|
|
530
|
+
assert.ok(persistedKeys.length <= 201, `bounded at the cap plus default (got ${persistedKeys.length})`);
|
|
531
|
+
assert.ok(persistedKeys.includes("default"), "default entry never pruned");
|
|
532
|
+
assert.ok(persistedKeys.includes("active-thread"), "the just-written key is retained");
|
|
533
|
+
assert.ok(!persistedKeys.includes("thread-0"), "oldest pending entry pruned once over the cap");
|
|
534
|
+
assert.ok(persistedKeys.includes("thread-204"), "newest pending entry survives");
|
|
535
|
+
});
|
|
536
|
+
|
|
537
|
+
test("pruneEntries evicts oldest NON-empty session entries past the cap (#1908 retry-retention bound)", async () => {
|
|
538
|
+
// Retained failed extractions keep their turns in the buffer, so during a
|
|
539
|
+
// provider outage non-empty entries can exceed MAX_BUFFER_ENTRY_COUNT (200).
|
|
540
|
+
// The cap is the documented bound: the oldest non-empty sessions must be
|
|
541
|
+
// evicted (loudly) rather than growing without bound (codex review).
|
|
542
|
+
const entries: NonNullable<BufferState["entries"]> = {};
|
|
543
|
+
for (let i = 0; i < 210; i += 1) {
|
|
544
|
+
const at = new Date(1_700_000_000_000 + i * 60_000).toISOString();
|
|
545
|
+
entries[`session-${String(i).padStart(3, "0")}`] = {
|
|
546
|
+
turns: [{ role: "user", content: `retained turn ${i}`, timestamp: at, sessionKey: `session-${i}` }],
|
|
547
|
+
lastExtractionAt: at,
|
|
548
|
+
extractionCount: 0,
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
const storage = new FakeStorage({ turns: [], lastExtractionAt: null, extractionCount: 0, entries });
|
|
552
|
+
const buffer = new SmartBuffer(parseConfig({}), storage as unknown as ConstructorParameters<typeof SmartBuffer>[1]);
|
|
553
|
+
await buffer.load();
|
|
554
|
+
await buffer.addTurn("session-newest", makeTurn("session-newest", "new turn"));
|
|
555
|
+
|
|
556
|
+
const savedKeys = Object.keys(storage.saved?.entries ?? {});
|
|
557
|
+
assert.ok(savedKeys.length <= 200, `entries bounded at MAX_BUFFER_ENTRY_COUNT (got ${savedKeys.length})`);
|
|
558
|
+
assert.ok(savedKeys.includes("session-newest"), "the just-written key is retained");
|
|
559
|
+
assert.equal(buffer.getTurns("session-000").length, 0, "oldest non-empty entry was evicted");
|
|
560
|
+
assert.ok(buffer.getTurns("session-209").length > 0, "newest pre-existing entry survives");
|
|
529
561
|
});
|
package/src/buffer.ts
CHANGED
|
@@ -206,28 +206,52 @@ export class SmartBuffer {
|
|
|
206
206
|
if (keys.length <= MAX_BUFFER_ENTRY_COUNT) return;
|
|
207
207
|
|
|
208
208
|
const insertionOrder = new Map(keys.map((key, index) => [key, index]));
|
|
209
|
-
const
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
turns: [],
|
|
215
|
-
lastExtractionAt: null,
|
|
216
|
-
extractionCount: 0,
|
|
217
|
-
});
|
|
218
|
-
const rightAt = this.entryActivityAt(entries[right] ?? {
|
|
219
|
-
turns: [],
|
|
220
|
-
lastExtractionAt: null,
|
|
221
|
-
extractionCount: 0,
|
|
222
|
-
});
|
|
223
|
-
if (leftAt !== rightAt) return leftAt - rightAt;
|
|
224
|
-
return (insertionOrder.get(left) ?? 0) - (insertionOrder.get(right) ?? 0);
|
|
209
|
+
const byOldestActivity = (left: string, right: string): number => {
|
|
210
|
+
const leftAt = this.entryActivityAt(entries[left] ?? {
|
|
211
|
+
turns: [],
|
|
212
|
+
lastExtractionAt: null,
|
|
213
|
+
extractionCount: 0,
|
|
225
214
|
});
|
|
215
|
+
const rightAt = this.entryActivityAt(entries[right] ?? {
|
|
216
|
+
turns: [],
|
|
217
|
+
lastExtractionAt: null,
|
|
218
|
+
extractionCount: 0,
|
|
219
|
+
});
|
|
220
|
+
if (leftAt !== rightAt) return leftAt - rightAt;
|
|
221
|
+
return (insertionOrder.get(left) ?? 0) - (insertionOrder.get(right) ?? 0);
|
|
222
|
+
};
|
|
223
|
+
const candidates = keys.filter((key) => key !== "default" && !retainKeys.includes(key));
|
|
224
|
+
const emptyRemovable = candidates
|
|
225
|
+
.filter((key) => (entries[key]?.turns.length ?? 0) === 0)
|
|
226
|
+
.sort(byOldestActivity);
|
|
226
227
|
|
|
227
228
|
const removableCount = Math.max(0, keys.length - MAX_BUFFER_ENTRY_COUNT);
|
|
228
|
-
|
|
229
|
+
let evicted = emptyRemovable.slice(0, removableCount);
|
|
230
|
+
if (evicted.length < removableCount) {
|
|
231
|
+
// Retained failed extractions (#1908 retry retention) keep their turns in
|
|
232
|
+
// the buffer, so empty entries alone may not bring the map back under the
|
|
233
|
+
// cap during a provider outage. The cap is the documented bound — evict
|
|
234
|
+
// the oldest NON-empty sessions too rather than growing unboundedly
|
|
235
|
+
// (codex review).
|
|
236
|
+
const nonEmptyRemovable = candidates
|
|
237
|
+
.filter((key) => (entries[key]?.turns.length ?? 0) > 0)
|
|
238
|
+
.sort(byOldestActivity);
|
|
239
|
+
evicted = evicted.concat(nonEmptyRemovable.slice(0, removableCount - evicted.length));
|
|
240
|
+
}
|
|
241
|
+
for (const key of evicted) {
|
|
229
242
|
delete entries[key];
|
|
230
243
|
}
|
|
244
|
+
if (evicted.length > 0) {
|
|
245
|
+
// Loud degradation (extraction hot-loop hardening, Step 7): during a
|
|
246
|
+
// prolonged provider outage the breaker holds extraction off and buffered
|
|
247
|
+
// sessions accumulate. Eviction of the oldest empty session entries past
|
|
248
|
+
// MAX_BUFFER_ENTRY_COUNT is bounded and expected, but must be observable —
|
|
249
|
+
// not silent — so operators can see buffer pressure.
|
|
250
|
+
log.warn(
|
|
251
|
+
`buffer: pruned ${evicted.length} oldest session entr${evicted.length === 1 ? "y" : "ies"} ` +
|
|
252
|
+
`past MAX_BUFFER_ENTRY_COUNT=${MAX_BUFFER_ENTRY_COUNT}; buffered turn data for those sessions was dropped`,
|
|
253
|
+
);
|
|
254
|
+
}
|
|
231
255
|
}
|
|
232
256
|
|
|
233
257
|
private async loadUnlocked(): Promise<void> {
|
package/src/capabilities.test.ts
CHANGED
|
@@ -366,6 +366,7 @@ const LIFECYCLE_FIELD_TO_FLAG: Record<keyof MemoryLifecycleCapabilitySet, string
|
|
|
366
366
|
extractionScopeClassification: "extractionScopeClassificationEnabled",
|
|
367
367
|
extractionJudge: "extractionJudgeEnabled",
|
|
368
368
|
extractionDedupe: "extractionDedupeEnabled",
|
|
369
|
+
extractionRetry: "extractionRetryEnabled",
|
|
369
370
|
extractionTelemetryPrefilter: "extractionTelemetryPrefilterEnabled",
|
|
370
371
|
extractionJudgeTelemetry: "extractionJudgeTelemetryEnabled",
|
|
371
372
|
embeddingFallback: "embeddingFallbackEnabled",
|
package/src/capabilities.ts
CHANGED
|
@@ -257,6 +257,8 @@ export interface MemoryLifecycleCapabilitySet {
|
|
|
257
257
|
readonly extractionJudgeTelemetry: boolean;
|
|
258
258
|
/** `embeddingFallbackEnabled` — semantic-dedup / archive-search embedding fallback. */
|
|
259
259
|
readonly embeddingFallback: boolean;
|
|
260
|
+
/** `extractionRetryEnabled` — per-fingerprint backoff + provider circuit breaker on the extraction retry path. */
|
|
261
|
+
readonly extractionRetry: boolean;
|
|
260
262
|
}
|
|
261
263
|
|
|
262
264
|
/**
|
|
@@ -275,6 +277,7 @@ export type MemoryLifecycleConfigProjection = Pick<
|
|
|
275
277
|
| "extractionTelemetryPrefilterEnabled"
|
|
276
278
|
| "extractionJudgeTelemetryEnabled"
|
|
277
279
|
| "embeddingFallbackEnabled"
|
|
280
|
+
| "extractionRetryEnabled"
|
|
278
281
|
>;
|
|
279
282
|
|
|
280
283
|
/**
|
|
@@ -299,6 +302,7 @@ export function resolveMemoryLifecycleCapabilities(
|
|
|
299
302
|
extractionTelemetryPrefilter: config.extractionTelemetryPrefilterEnabled,
|
|
300
303
|
extractionJudgeTelemetry: config.extractionJudgeTelemetryEnabled,
|
|
301
304
|
embeddingFallback: config.embeddingFallbackEnabled,
|
|
305
|
+
extractionRetry: config.extractionRetryEnabled,
|
|
302
306
|
});
|
|
303
307
|
}
|
|
304
308
|
|
package/src/config.test.ts
CHANGED
|
@@ -2142,3 +2142,22 @@ test("parseConfig correction.maxAffected rejects non-integers (Number.isInteger,
|
|
|
2142
2142
|
assert.equal(parseConfig({ correctionMaxAffected: 20 }).correctionMaxAffected, 20);
|
|
2143
2143
|
assert.equal(parseConfig({}).correctionMaxAffected, 10, "absent → documented default");
|
|
2144
2144
|
});
|
|
2145
|
+
|
|
2146
|
+
test("parseConfig rejects non-positive extraction retry/breaker numerics and falls back to defaults (codex review, rule 17)", () => {
|
|
2147
|
+
const c = parseConfig({
|
|
2148
|
+
extractionRetryMaxBackoffMs: 0,
|
|
2149
|
+
extractionRetryJitterRatio: -0.5,
|
|
2150
|
+
extractionParseEmptyMaxAttempts: 0,
|
|
2151
|
+
extractionBreakerFailureThreshold: -1,
|
|
2152
|
+
extractionBreakerCooldownMs: -5, // 0 is a valid "half-open immediately" escape hatch; only negative is rejected
|
|
2153
|
+
extractionBreakerAuthCooldownMs: -100,
|
|
2154
|
+
extractionRetryScheduleMs: [1000, 0, -5],
|
|
2155
|
+
});
|
|
2156
|
+
assert.equal(c.extractionRetryMaxBackoffMs, 21_600_000, "non-positive cap -> default");
|
|
2157
|
+
assert.equal(c.extractionRetryJitterRatio, 0.2, "out-of-range jitter -> default");
|
|
2158
|
+
assert.equal(c.extractionParseEmptyMaxAttempts, 3, "non-positive attempts -> default");
|
|
2159
|
+
assert.equal(c.extractionBreakerFailureThreshold, 5, "non-positive threshold -> default");
|
|
2160
|
+
assert.equal(c.extractionBreakerCooldownMs, 300_000, "non-positive cooldown -> default");
|
|
2161
|
+
assert.equal(c.extractionBreakerAuthCooldownMs, 1_800_000, "non-positive auth cooldown -> default");
|
|
2162
|
+
assert.deepEqual(c.extractionRetryScheduleMs, [60_000, 300_000, 1_800_000, 7_200_000], "schedule with non-positive entry -> default");
|
|
2163
|
+
});
|
package/src/config.ts
CHANGED
|
@@ -2826,6 +2826,35 @@ export function parseConfig(
|
|
|
2826
2826
|
typeof cfg.localLlm400TripThreshold === "number" ? cfg.localLlm400TripThreshold : 5,
|
|
2827
2827
|
localLlm400CooldownMs:
|
|
2828
2828
|
typeof cfg.localLlm400CooldownMs === "number" ? cfg.localLlm400CooldownMs : 120_000,
|
|
2829
|
+
// Extraction retry/backoff + circuit breaker (extraction hot-loop hardening)
|
|
2830
|
+
extractionRetryEnabled: coerceBooleanLike(cfg.extractionRetryEnabled) ?? true,
|
|
2831
|
+
extractionRetryScheduleMs:
|
|
2832
|
+
Array.isArray(cfg.extractionRetryScheduleMs) &&
|
|
2833
|
+
cfg.extractionRetryScheduleMs.length > 0 &&
|
|
2834
|
+
cfg.extractionRetryScheduleMs.every((n) => typeof n === "number" && Number.isFinite(n) && n > 0)
|
|
2835
|
+
? (cfg.extractionRetryScheduleMs as number[])
|
|
2836
|
+
: [60_000, 300_000, 1_800_000, 7_200_000],
|
|
2837
|
+
extractionRetryMaxBackoffMs:
|
|
2838
|
+
typeof cfg.extractionRetryMaxBackoffMs === "number" && cfg.extractionRetryMaxBackoffMs > 0
|
|
2839
|
+
? cfg.extractionRetryMaxBackoffMs : 21_600_000,
|
|
2840
|
+
extractionRetryJitterRatio:
|
|
2841
|
+
typeof cfg.extractionRetryJitterRatio === "number" &&
|
|
2842
|
+
cfg.extractionRetryJitterRatio >= 0 && cfg.extractionRetryJitterRatio <= 1
|
|
2843
|
+
? cfg.extractionRetryJitterRatio : 0.2,
|
|
2844
|
+
extractionParseEmptyMaxAttempts:
|
|
2845
|
+
typeof cfg.extractionParseEmptyMaxAttempts === "number" &&
|
|
2846
|
+
Number.isInteger(cfg.extractionParseEmptyMaxAttempts) && cfg.extractionParseEmptyMaxAttempts > 0
|
|
2847
|
+
? cfg.extractionParseEmptyMaxAttempts : 3,
|
|
2848
|
+
extractionBreakerFailureThreshold:
|
|
2849
|
+
typeof cfg.extractionBreakerFailureThreshold === "number" &&
|
|
2850
|
+
Number.isInteger(cfg.extractionBreakerFailureThreshold) && cfg.extractionBreakerFailureThreshold > 0
|
|
2851
|
+
? cfg.extractionBreakerFailureThreshold : 5,
|
|
2852
|
+
extractionBreakerCooldownMs:
|
|
2853
|
+
typeof cfg.extractionBreakerCooldownMs === "number" && cfg.extractionBreakerCooldownMs >= 0
|
|
2854
|
+
? cfg.extractionBreakerCooldownMs : 300_000,
|
|
2855
|
+
extractionBreakerAuthCooldownMs:
|
|
2856
|
+
typeof cfg.extractionBreakerAuthCooldownMs === "number" && cfg.extractionBreakerAuthCooldownMs >= 0
|
|
2857
|
+
? cfg.extractionBreakerAuthCooldownMs : 1_800_000,
|
|
2829
2858
|
// Local LLM fast tier (v9.1)
|
|
2830
2859
|
localLlmFastEnabled: cfg.localLlmFastEnabled === true,
|
|
2831
2860
|
localLlmFastModel:
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extraction failure-classification tests (extraction hot-loop hardening).
|
|
3
|
+
*
|
|
4
|
+
* The retry/backoff + circuit-breaker layer keys off `ExtractionFailureClass`.
|
|
5
|
+
* These tests lock the mapping from synthetic provider errors (429/500/401/
|
|
6
|
+
* no-models/parse-empty) to the coarse class, reusing the shared
|
|
7
|
+
* `isTransientHttpError` classifier rather than a bespoke copy.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import assert from "node:assert/strict";
|
|
11
|
+
import test from "node:test";
|
|
12
|
+
|
|
13
|
+
import { classifyExtractionThrownError, classifyFallbackParseFailure } from "./extraction.js";
|
|
14
|
+
|
|
15
|
+
test("classifyExtractionThrownError: 401/403 → auth_config", () => {
|
|
16
|
+
assert.equal(classifyExtractionThrownError({ status: 401 }), "auth_config");
|
|
17
|
+
assert.equal(classifyExtractionThrownError({ status: 403 }), "auth_config");
|
|
18
|
+
assert.equal(classifyExtractionThrownError({ statusCode: 401 }), "auth_config");
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test("classifyExtractionThrownError: 429/5xx → provider_retryable", () => {
|
|
22
|
+
assert.equal(classifyExtractionThrownError({ status: 429 }), "provider_retryable");
|
|
23
|
+
assert.equal(classifyExtractionThrownError({ status: 500 }), "provider_retryable");
|
|
24
|
+
assert.equal(classifyExtractionThrownError({ status: 503 }), "provider_retryable");
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test("classifyExtractionThrownError: bare network error / unknown → provider_retryable (fail-open toward retry)", () => {
|
|
28
|
+
assert.equal(classifyExtractionThrownError(new Error("socket hang up")), "provider_retryable");
|
|
29
|
+
// A terminal non-auth 4xx still defaults to retryable (capped by the attempt budget).
|
|
30
|
+
assert.equal(classifyExtractionThrownError({ status: 400 }), "provider_retryable");
|
|
31
|
+
assert.equal(classifyExtractionThrownError(null), "provider_retryable");
|
|
32
|
+
assert.equal(classifyExtractionThrownError("not an object"), "provider_retryable");
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("classifyFallbackParseFailure: maps the gateway parse-failure reasons", () => {
|
|
36
|
+
assert.equal(classifyFallbackParseFailure("no_models"), "auth_config");
|
|
37
|
+
assert.equal(classifyFallbackParseFailure("http_error"), "provider_retryable");
|
|
38
|
+
assert.equal(classifyFallbackParseFailure("empty"), "parse_empty");
|
|
39
|
+
});
|
package/src/extraction.ts
CHANGED
|
@@ -26,6 +26,7 @@ import type {
|
|
|
26
26
|
GatewayConfig,
|
|
27
27
|
MemoryCategory,
|
|
28
28
|
DaySummaryResult as DaySummaryResultShape,
|
|
29
|
+
ExtractionFailureClass,
|
|
29
30
|
} from "./types.js";
|
|
30
31
|
import { ModelRegistry } from "./model-registry.js";
|
|
31
32
|
import { extractJsonCandidates } from "./json-extract.js";
|
|
@@ -38,6 +39,7 @@ import { normalizeProcedureSteps } from "./procedural/procedure-types.js";
|
|
|
38
39
|
import { normalizeReasoningTrace } from "./reasoning-trace-types.js";
|
|
39
40
|
import { looksLikeMechanicalTelemetryTranscript } from "./telemetry-transcript.js";
|
|
40
41
|
import { buildFactProvenance, type ProvenanceTurnInput } from "./provenance.js";
|
|
42
|
+
import { isTransientHttpError } from "./connectors/live/transient-errors.js";
|
|
41
43
|
import { resolvePipelineProcessingCapabilities } from "./capabilities.js";
|
|
42
44
|
import { resolveMemoryLifecycleCapabilities,
|
|
43
45
|
resolveLocalLlmCapabilities,resolveRecallAuxiliaryCapabilities } from "./capabilities.js";
|
|
@@ -46,6 +48,56 @@ type ExtractionQuestion = ExtractionResult["questions"][number];
|
|
|
46
48
|
type ExtractedFactResult = ExtractionResult["facts"][number];
|
|
47
49
|
type ExtractedEntityResult = ExtractionResult["entities"][number];
|
|
48
50
|
type ExtractedRelationshipResult = NonNullable<ExtractionResult["relationships"]>[number];
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Read a numeric HTTP status off a thrown error without re-implementing the
|
|
54
|
+
* shared transient classifier. Uses `in`/typeof narrowing (no inline casts).
|
|
55
|
+
*/
|
|
56
|
+
function httpStatusFromError(err: unknown): number | undefined {
|
|
57
|
+
if (!err || typeof err !== "object") return undefined;
|
|
58
|
+
if ("status" in err && typeof err.status === "number" && Number.isFinite(err.status)) return err.status;
|
|
59
|
+
if ("statusCode" in err && typeof err.statusCode === "number" && Number.isFinite(err.statusCode)) {
|
|
60
|
+
return err.statusCode;
|
|
61
|
+
}
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Map a thrown extraction error to a coarse failure class for the retry/breaker
|
|
67
|
+
* layer. 401/403 → `auth_config` (misconfigured provider, open the breaker);
|
|
68
|
+
* transient (429/5xx/network, per the shared `isTransientHttpError`) → back off;
|
|
69
|
+
* anything else defaults to `provider_retryable` (fail-open toward retry, but
|
|
70
|
+
* capped by the per-fingerprint attempt budget). Never throws.
|
|
71
|
+
*/
|
|
72
|
+
export function classifyExtractionThrownError(err: unknown): ExtractionFailureClass {
|
|
73
|
+
try {
|
|
74
|
+
const status = httpStatusFromError(err);
|
|
75
|
+
if (status === 401 || status === 403) return "auth_config";
|
|
76
|
+
if (isTransientHttpError(err)) return "provider_retryable";
|
|
77
|
+
return "provider_retryable";
|
|
78
|
+
} catch {
|
|
79
|
+
return "provider_retryable";
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Map the gateway fallback's discriminated parse-failure reason to a coarse
|
|
85
|
+
* extraction failure class. "no models configured" is an auth/config problem;
|
|
86
|
+
* an HTTP/transport failure backs off; an unparseable-but-present response is a
|
|
87
|
+
* genuine empty-parse.
|
|
88
|
+
*/
|
|
89
|
+
export function classifyFallbackParseFailure(
|
|
90
|
+
reason: "no_models" | "empty" | "http_error",
|
|
91
|
+
): ExtractionFailureClass {
|
|
92
|
+
switch (reason) {
|
|
93
|
+
case "no_models":
|
|
94
|
+
return "auth_config";
|
|
95
|
+
case "http_error":
|
|
96
|
+
return "provider_retryable";
|
|
97
|
+
case "empty":
|
|
98
|
+
return "parse_empty";
|
|
99
|
+
}
|
|
100
|
+
}
|
|
49
101
|
const PROACTIVE_MIN_CONFIDENCE = 0.8;
|
|
50
102
|
const CONSOLIDATION_RESPONSE_SCHEMA = `{
|
|
51
103
|
"items": [
|
|
@@ -975,7 +1027,7 @@ export class ExtractionEngine {
|
|
|
975
1027
|
options: { temperature?: number; maxTokens?: number } = {},
|
|
976
1028
|
): Promise<T | null> {
|
|
977
1029
|
const detailed = await this.fallbackLlm.parseWithSchemaDetailed(messages, schema, this.withGatewayAgent(options));
|
|
978
|
-
if (detailed
|
|
1030
|
+
if ("modelUsed" in detailed) {
|
|
979
1031
|
const durationMs = Date.now() - startedAtMs;
|
|
980
1032
|
this.emit({
|
|
981
1033
|
kind: "llm_end",
|
|
@@ -1119,11 +1171,16 @@ export class ExtractionEngine {
|
|
|
1119
1171
|
localLlm: resolveLocalLlmCapabilities(this.config).localLlm,
|
|
1120
1172
|
});
|
|
1121
1173
|
this.profiler.startSpan("total", extractionTraceId);
|
|
1174
|
+
// True when a local or direct extractor was attempted before the gateway
|
|
1175
|
+
// fallback, so a gateway "no models" result doesn't re-classify a real
|
|
1176
|
+
// primary failure as auth_config (codex review: preserve direct/local failures).
|
|
1177
|
+
let primaryExtractorAttempted = false;
|
|
1122
1178
|
|
|
1123
1179
|
try {
|
|
1124
1180
|
// Try local LLM first if enabled
|
|
1125
1181
|
if (this.shouldUseLocalLlm) {
|
|
1126
1182
|
this.profiler.startSpan("local-llm", extractionTraceId);
|
|
1183
|
+
primaryExtractorAttempted = true;
|
|
1127
1184
|
try {
|
|
1128
1185
|
const localResult = await this.extractWithLocalLlm(conversation, existingEntities);
|
|
1129
1186
|
if (localResult) {
|
|
@@ -1144,6 +1201,7 @@ export class ExtractionEngine {
|
|
|
1144
1201
|
entities: [],
|
|
1145
1202
|
questions: [],
|
|
1146
1203
|
extractionFailure: "local_llm_unavailable",
|
|
1204
|
+
extractionFailureClass: "provider_retryable",
|
|
1147
1205
|
};
|
|
1148
1206
|
}
|
|
1149
1207
|
log.info("extraction: local LLM unavailable, falling back to gateway default AI");
|
|
@@ -1156,6 +1214,7 @@ export class ExtractionEngine {
|
|
|
1156
1214
|
entities: [],
|
|
1157
1215
|
questions: [],
|
|
1158
1216
|
extractionFailure: "local_llm_error",
|
|
1217
|
+
extractionFailureClass: "provider_retryable",
|
|
1159
1218
|
};
|
|
1160
1219
|
}
|
|
1161
1220
|
log.info("extraction: local LLM error, falling back to gateway default AI:", err);
|
|
@@ -1168,6 +1227,7 @@ export class ExtractionEngine {
|
|
|
1168
1227
|
// Try direct OpenAI-compatible client (Scryr, OpenRouter, etc.)
|
|
1169
1228
|
if (this.shouldUseDirectClient) {
|
|
1170
1229
|
this.profiler.startSpan("direct-client", extractionTraceId);
|
|
1230
|
+
primaryExtractorAttempted = true;
|
|
1171
1231
|
try {
|
|
1172
1232
|
const directResult = await this.extractWithDirectClient(conversation, existingEntities);
|
|
1173
1233
|
if (directResult) {
|
|
@@ -1250,7 +1310,7 @@ export class ExtractionEngine {
|
|
|
1250
1310
|
|
|
1251
1311
|
const fallbackDurationMs = Date.now() - fallbackStartTime;
|
|
1252
1312
|
|
|
1253
|
-
if (detailed
|
|
1313
|
+
if ("modelUsed" in detailed && Array.isArray(detailed.result.facts)) {
|
|
1254
1314
|
const result = detailed.result;
|
|
1255
1315
|
this.emit({
|
|
1256
1316
|
kind: "llm_end", traceId: fallbackTraceId, model: detailed.modelUsed, operation: "extraction",
|
|
@@ -1299,12 +1359,21 @@ export class ExtractionEngine {
|
|
|
1299
1359
|
durationMs: fallbackDurationMs, error: "fallback returned no parsed output",
|
|
1300
1360
|
});
|
|
1301
1361
|
log.warn("extraction fallback returned no parsed output");
|
|
1362
|
+
const fallbackParseFailureClass: ExtractionFailureClass =
|
|
1363
|
+
detailed.result === null
|
|
1364
|
+
? detailed.failureReason === "no_models" && primaryExtractorAttempted
|
|
1365
|
+
? // Gateway had no models, but a local/direct extractor already failed —
|
|
1366
|
+
// the root cause is the primary (transient), not gateway auth/config.
|
|
1367
|
+
"provider_retryable"
|
|
1368
|
+
: classifyFallbackParseFailure(detailed.failureReason)
|
|
1369
|
+
: "parse_empty";
|
|
1302
1370
|
return {
|
|
1303
1371
|
facts: [],
|
|
1304
1372
|
profileUpdates: [],
|
|
1305
1373
|
entities: [],
|
|
1306
1374
|
questions: [],
|
|
1307
1375
|
extractionFailure: "fallback_no_parsed_output",
|
|
1376
|
+
extractionFailureClass: fallbackParseFailureClass,
|
|
1308
1377
|
};
|
|
1309
1378
|
} catch (err) {
|
|
1310
1379
|
this.emit({
|
|
@@ -1318,6 +1387,7 @@ export class ExtractionEngine {
|
|
|
1318
1387
|
entities: [],
|
|
1319
1388
|
questions: [],
|
|
1320
1389
|
extractionFailure: "fallback_failed",
|
|
1390
|
+
extractionFailureClass: classifyExtractionThrownError(err),
|
|
1321
1391
|
};
|
|
1322
1392
|
} finally {
|
|
1323
1393
|
try { this.profiler.endSpan("gateway-fallback", extractionTraceId); } catch { /* span may already be closed */ }
|
package/src/fallback-llm.ts
CHANGED
|
@@ -280,9 +280,30 @@ export class FallbackLlmClient {
|
|
|
280
280
|
messages: Array<{ role: "system" | "user" | "assistant"; content: string }>,
|
|
281
281
|
schema: { parse: (data: unknown) => T },
|
|
282
282
|
options: FallbackLlmOptions = {},
|
|
283
|
-
): Promise<
|
|
284
|
-
|
|
285
|
-
|
|
283
|
+
): Promise<
|
|
284
|
+
| { result: T; modelUsed: string }
|
|
285
|
+
| { result: null; failureReason: "no_models" | "empty" | "http_error" }
|
|
286
|
+
> {
|
|
287
|
+
let response: FallbackLlmResponse | null;
|
|
288
|
+
try {
|
|
289
|
+
response = await this.chatCompletion(messages, options);
|
|
290
|
+
} catch (err) {
|
|
291
|
+
// Caller aborts must propagate (e.g. recall planner cancellation) — do
|
|
292
|
+
// not swallow them as a provider failure, or abort-driven callers lose
|
|
293
|
+
// cancellation and treat it as an extraction error (codex review).
|
|
294
|
+
if (options.signal?.aborted) throw err;
|
|
295
|
+
log.warn("fallback LLM: chatCompletion threw during structured parse:", err);
|
|
296
|
+
return { result: null, failureReason: "http_error" };
|
|
297
|
+
}
|
|
298
|
+
if (!response?.content) {
|
|
299
|
+
// chatCompletion returns null both when no models are configured
|
|
300
|
+
// (auth/config) and when every configured model errored (transient).
|
|
301
|
+
// Disambiguate via the resolved model chain so the retry layer can pick
|
|
302
|
+
// the right failure class.
|
|
303
|
+
const hasModels =
|
|
304
|
+
this.getModelChain(options.agentId, options.model, options.modelChain).length > 0;
|
|
305
|
+
return { result: null, failureReason: hasModels ? "http_error" : "no_models" };
|
|
306
|
+
}
|
|
286
307
|
|
|
287
308
|
try {
|
|
288
309
|
const candidates = extractJsonCandidates(response.content);
|
|
@@ -294,10 +315,10 @@ export class FallbackLlmClient {
|
|
|
294
315
|
// keep trying other candidates
|
|
295
316
|
}
|
|
296
317
|
}
|
|
297
|
-
return null;
|
|
318
|
+
return { result: null, failureReason: "empty" };
|
|
298
319
|
} catch (err) {
|
|
299
320
|
log.warn("fallback LLM: failed to parse structured output:", err);
|
|
300
|
-
return null;
|
|
321
|
+
return { result: null, failureReason: "empty" };
|
|
301
322
|
}
|
|
302
323
|
}
|
|
303
324
|
|
package/src/operator-toolkit.ts
CHANGED
|
@@ -1272,6 +1272,59 @@ export async function runOperatorDoctor(options: OperatorDoctorOptions): Promise
|
|
|
1272
1272
|
details: meta,
|
|
1273
1273
|
});
|
|
1274
1274
|
|
|
1275
|
+
// Extraction resilience (extraction hot-loop hardening). Surfaces the
|
|
1276
|
+
// persisted per-fingerprint retry/backoff state so an operator can see when
|
|
1277
|
+
// the extraction provider is failing without tailing logs. auth/config
|
|
1278
|
+
// failures are an error (misconfigured provider, will not self-heal at the
|
|
1279
|
+
// current cadence); transient backoff is a warn; a clean state is ok. Reads
|
|
1280
|
+
// the already-loaded meta — no extra I/O, cross-process safe (the in-memory
|
|
1281
|
+
// breaker lives in the daemon; this derives from durable state).
|
|
1282
|
+
const retryEntries = meta.extractionRetryState ?? [];
|
|
1283
|
+
const authConfigFailures = retryEntries.filter((entry) => entry.lastFailureClass === "auth_config");
|
|
1284
|
+
const nowMs = Date.now();
|
|
1285
|
+
const parkedFingerprints = retryEntries.filter((entry) => {
|
|
1286
|
+
const eligibleAt = Date.parse(entry.nextEligibleAt);
|
|
1287
|
+
return Number.isFinite(eligibleAt) && eligibleAt > nowMs;
|
|
1288
|
+
});
|
|
1289
|
+
// The in-memory breaker opens only after extractionBreakerFailureThreshold
|
|
1290
|
+
// CONSECUTIVE provider failures, each of which parks a fingerprint — so
|
|
1291
|
+
// doctor infers "likely open" only when the parked transient count reaches
|
|
1292
|
+
// that threshold. A single parked fingerprint is ordinary backoff, not a
|
|
1293
|
+
// suspended-extraction signal (cursor review: don't overclaim). The breaker
|
|
1294
|
+
// itself is per-process (daemon-only), hence the durable-signal inference.
|
|
1295
|
+
const providerRetryableParked = parkedFingerprints.filter(
|
|
1296
|
+
(entry) => entry.lastFailureClass === "provider_retryable",
|
|
1297
|
+
);
|
|
1298
|
+
const breakerLikelyOpen =
|
|
1299
|
+
providerRetryableParked.length >= Math.max(1, config.extractionBreakerFailureThreshold);
|
|
1300
|
+
const resilienceStatus: "ok" | "warn" | "error" =
|
|
1301
|
+
authConfigFailures.length > 0 ? "error" : parkedFingerprints.length > 0 ? "warn" : "ok";
|
|
1302
|
+
checks.push({
|
|
1303
|
+
key: "extraction-resilience",
|
|
1304
|
+
status: resilienceStatus,
|
|
1305
|
+
summary:
|
|
1306
|
+
resilienceStatus === "error"
|
|
1307
|
+
? `Extraction provider has ${authConfigFailures.length} auth/config failure(s); the circuit breaker opens immediately and extraction is suspended.`
|
|
1308
|
+
: resilienceStatus === "warn"
|
|
1309
|
+
? `${parkedFingerprints.length} extraction fingerprint(s) in backoff after transient provider failures${
|
|
1310
|
+
breakerLikelyOpen ? "; the provider circuit breaker is likely OPEN in the daemon (non-forced extraction is suspended until it half-opens)" : ""
|
|
1311
|
+
}.`
|
|
1312
|
+
: "No extraction provider failures are currently parked.",
|
|
1313
|
+
remediation:
|
|
1314
|
+
resilienceStatus === "error"
|
|
1315
|
+
? 'Check the extraction LLM gateway/model configuration (a 401/403 or "no models configured" was seen). Extraction resumes automatically once the provider is reachable.'
|
|
1316
|
+
: resilienceStatus === "warn"
|
|
1317
|
+
? "Transient provider failures (e.g. 429/5xx). Backoff clears automatically as the provider recovers; no action needed unless it persists."
|
|
1318
|
+
: undefined,
|
|
1319
|
+
details: {
|
|
1320
|
+
backoffFingerprintCount: retryEntries.length,
|
|
1321
|
+
authConfigFailureCount: authConfigFailures.length,
|
|
1322
|
+
parkedFingerprintCount: parkedFingerprints.length,
|
|
1323
|
+
providerRetryableParkedCount: providerRetryableParked.length,
|
|
1324
|
+
breakerLikelyOpen,
|
|
1325
|
+
},
|
|
1326
|
+
});
|
|
1327
|
+
|
|
1275
1328
|
// Memory projection health (issue #1829). Surfaces a present-but-unopenable
|
|
1276
1329
|
// projection (wrong ABI / corrupt) that previously degraded every memory
|
|
1277
1330
|
// list to a silent full-corpus scan. Absent and openable are both "ok".
|