@remnic/core 9.30.1 → 9.32.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 +10 -10
- package/dist/access-admin-ops-surface.js +55 -44
- package/dist/access-authorization-probe.d.ts +10 -10
- package/dist/access-authorization-probe.js +56 -45
- package/dist/access-boundary.d.ts +13 -11
- package/dist/access-boundary.js +55 -44
- package/dist/access-cli.js +170 -126
- package/dist/access-cli.js.map +1 -1
- package/dist/access-extraction-force-flush.d.ts +119 -0
- package/dist/access-extraction-force-flush.js +171 -0
- package/dist/access-health-types.d.ts +1 -1
- package/dist/access-http-lcm-compaction.d.ts +47 -13
- package/dist/access-http-lcm-compaction.js +7 -3
- package/dist/access-http-lifecycle-flush.d.ts +132 -0
- package/dist/access-http-lifecycle-flush.js +9 -0
- package/dist/access-http-lifecycle-flush.js.map +1 -0
- package/dist/access-http.d.ts +11 -10
- package/dist/access-http.js +71 -59
- package/dist/access-identity-continuity-surface.d.ts +8 -8
- package/dist/access-identity-continuity-surface.js +55 -44
- package/dist/access-lcm-surface.d.ts +10 -10
- package/dist/access-lcm-surface.js +55 -44
- package/dist/access-mcp-output-schemas.js +1 -1
- package/dist/access-mcp.d.ts +16 -10
- package/dist/access-mcp.js +63 -52
- package/dist/access-memory-search-fanout.d.ts +2 -2
- package/dist/{access-namespace-preflight-VfXssvoI.d.ts → access-namespace-preflight-COhbXBqS.d.ts} +1 -1
- package/dist/access-namespace-preflight.d.ts +3 -3
- package/dist/access-namespace-preflight.js +55 -44
- package/dist/access-observe-helpers.d.ts +22 -0
- package/dist/access-observe-helpers.js +10 -0
- package/dist/access-observe-helpers.js.map +1 -0
- package/dist/access-observe-write-surface.d.ts +51 -47
- package/dist/access-observe-write-surface.js +55 -44
- package/dist/access-operations-batch.js +58 -47
- package/dist/access-operations.d.ts +11 -11
- package/dist/access-operations.js +60 -49
- package/dist/access-recall-concurrency.d.ts +10 -10
- package/dist/access-recall-concurrency.js +55 -44
- package/dist/access-recall-response.d.ts +10 -10
- package/dist/access-recall-response.js +55 -44
- package/dist/access-recall-surface.d.ts +10 -10
- package/dist/access-recall-surface.js +55 -44
- package/dist/access-schema.d.ts +37 -7
- package/dist/access-schema.js +5 -3
- package/dist/{access-service-B5RwVsTV.d.ts → access-service-BmBTtEKO.d.ts} +44 -20
- package/dist/access-service-helpers.d.ts +113 -0
- package/dist/access-service-helpers.js +8 -0
- package/dist/access-service-helpers.js.map +1 -0
- package/dist/access-service.d.ts +10 -10
- package/dist/access-service.js +55 -44
- package/dist/access-surface-catalog.d.ts +10 -10
- package/dist/access-surface-catalog.js +2 -0
- package/dist/access-surface-catalog.js.map +1 -1
- package/dist/access-wearables-meetings-surface.d.ts +3 -3
- package/dist/action-confidence.d.ts +1 -1
- package/dist/active-memory-bridge.d.ts +1 -1
- package/dist/active-recall.d.ts +1 -1
- package/dist/active-recall.js +2 -2
- package/dist/adapters/index.js +7 -7
- package/dist/adapters/registry.js +3 -3
- package/dist/behavior-learner.d.ts +1 -1
- package/dist/behavior-signals.d.ts +1 -1
- package/dist/bootstrap.d.ts +8 -8
- package/dist/briefing.d.ts +2 -2
- package/dist/briefing.js +14 -14
- package/dist/buffer-surprise-report.d.ts +1 -1
- package/dist/buffer-turn-helpers.d.ts +5 -0
- package/dist/buffer-turn-helpers.js +24 -0
- package/dist/buffer-turn-helpers.js.map +1 -0
- package/dist/buffer.d.ts +17 -4
- package/dist/buffer.js +21 -1
- package/dist/bulk-import/index.d.ts +3 -3
- package/dist/calibration.d.ts +1 -1
- package/dist/capabilities.d.ts +1 -1
- package/dist/{capsule-crypto-GWVG7LGC.js → capsule-crypto-7FJQINUR.js} +2 -2
- package/dist/capsule-crypto-7FJQINUR.js.map +1 -0
- package/dist/{capsule-merge-5KKJQBWQ.js → capsule-merge-2H2IUTBT.js} +4 -4
- package/dist/{catalog-V7inUQdp.d.ts → catalog-Dm-DvNBY.d.ts} +1 -1
- package/dist/causal-behavior.d.ts +1 -1
- package/dist/causal-behavior.js +2 -2
- package/dist/causal-chain.js +2 -2
- package/dist/causal-consolidation.d.ts +1 -1
- package/dist/causal-consolidation.js +21 -22
- package/dist/causal-consolidation.js.map +1 -1
- package/dist/causal-retrieval.js +2 -2
- package/dist/causal-trajectory-graph.d.ts +1 -1
- package/dist/causal-trajectory.js +1 -1
- package/dist/{chunk-LEPOP46W.js → chunk-227UFSEQ.js} +4 -4
- package/dist/{chunk-I5NLEENJ.js → chunk-27HHNYSQ.js} +3 -3
- package/dist/{chunk-2SA56WMI.js → chunk-2LTDWSR6.js} +4 -4
- package/dist/{chunk-OWHERGF2.js → chunk-2NLLXCJG.js} +2 -2
- package/dist/{chunk-SPJXKAVF.js → chunk-2NTGBZLZ.js} +14 -13
- package/dist/chunk-2NTGBZLZ.js.map +1 -0
- package/dist/{chunk-P5PXWTW7.js → chunk-2Q4JJ3FZ.js} +3 -3
- package/dist/{chunk-G5R3D5UP.js → chunk-2QIMTVEJ.js} +6 -5
- package/dist/chunk-2QIMTVEJ.js.map +1 -0
- package/dist/{chunk-H5CPS5JP.js → chunk-35IVNNRK.js} +3 -3
- package/dist/{chunk-LBOD4U5B.js → chunk-3EQ2XB3W.js} +6 -6
- package/dist/{chunk-YTSNAUXN.js → chunk-3F2LKUMU.js} +22 -8
- package/dist/chunk-3F2LKUMU.js.map +1 -0
- package/dist/chunk-3QL24R5Y.js +9 -0
- package/dist/chunk-3QL24R5Y.js.map +1 -0
- package/dist/chunk-4RNZMRAV.js +60 -0
- package/dist/chunk-4RNZMRAV.js.map +1 -0
- package/dist/chunk-5IVLTFOV.js +65 -0
- package/dist/chunk-5IVLTFOV.js.map +1 -0
- package/dist/{chunk-2EUVEDF5.js → chunk-5WZVQRHV.js} +2 -2
- package/dist/{chunk-ALSGMEJR.js → chunk-65ZPVYTA.js} +7 -7
- package/dist/{chunk-GIBMDSJ2.js → chunk-6P7MTSU5.js} +2 -2
- package/dist/{chunk-5VQJBH2G.js → chunk-6TAUHL33.js} +2 -2
- package/dist/{chunk-WVO4G37W.js → chunk-7TEZUUGM.js} +3 -3
- package/dist/{chunk-MJCGLM25.js → chunk-7VUAG236.js} +5 -5
- package/dist/{chunk-CZRYLSSW.js → chunk-AJL6NNX6.js} +2 -2
- package/dist/{chunk-LGHKJABJ.js → chunk-AQEJE3WO.js} +40 -18
- package/dist/chunk-AQEJE3WO.js.map +1 -0
- package/dist/{chunk-66772WBP.js → chunk-AQWVJURR.js} +5 -5
- package/dist/{chunk-RKNJBZ55.js → chunk-AU7Q3LSC.js} +7 -7
- package/dist/{chunk-IYZ5K7MU.js → chunk-BUE5E2HL.js} +4 -187
- package/dist/chunk-BUE5E2HL.js.map +1 -0
- package/dist/{chunk-NHWB6HG2.js → chunk-CE5355TT.js} +21 -5
- package/dist/chunk-CE5355TT.js.map +1 -0
- package/dist/{chunk-M3C2V3PV.js → chunk-CJDNT45S.js} +12 -12
- package/dist/{chunk-D2ANTDGO.js → chunk-E6BMSH7U.js} +2 -2
- package/dist/{chunk-CKMO4RDY.js → chunk-EMDLM3LW.js} +4 -4
- package/dist/chunk-ER7W6H4T.js +112 -0
- package/dist/chunk-ER7W6H4T.js.map +1 -0
- package/dist/{chunk-RGIWINDN.js → chunk-FHBITMRY.js} +30 -30
- package/dist/{chunk-6FUPCJUL.js → chunk-FMFLGS5Z.js} +54 -54
- package/dist/chunk-FMFLGS5Z.js.map +1 -0
- package/dist/{chunk-FDAMS4PK.js → chunk-G2YQ57YO.js} +4 -4
- package/dist/{chunk-6U7F52DE.js → chunk-HFQQWAMS.js} +62 -62
- package/dist/{chunk-5W4ZLBEZ.js → chunk-HKPJPOW7.js} +2 -2
- package/dist/{chunk-JCBWCYEJ.js → chunk-IGWFKW4B.js} +2 -2
- package/dist/{chunk-AITCS2RC.js → chunk-IJ7HK452.js} +4 -4
- package/dist/{chunk-4WKA6I65.js → chunk-J3ZGPYXB.js} +1135 -311
- package/dist/chunk-J3ZGPYXB.js.map +1 -0
- package/dist/{chunk-D3LMYTBV.js → chunk-J7WZUL2K.js} +9 -9
- package/dist/{chunk-5W7CTZ2E.js → chunk-KQAFEZQX.js} +4 -4
- package/dist/{chunk-YNQBRLC5.js → chunk-LUQ443JP.js} +520 -2011
- package/dist/chunk-LUQ443JP.js.map +1 -0
- package/dist/{chunk-Z54EM5YZ.js → chunk-MAXVH44E.js} +7 -7
- package/dist/{chunk-WZFAPHIK.js → chunk-MU7ZHSZE.js} +2 -2
- package/dist/chunk-N5Z4EVOR.js +123 -0
- package/dist/chunk-N5Z4EVOR.js.map +1 -0
- package/dist/{chunk-AZ5BTEKX.js → chunk-NQUF7XKR.js} +2 -2
- package/dist/{chunk-W4FBGYGK.js → chunk-NRV4Y3PM.js} +1 -1
- package/dist/{chunk-62U4J2GN.js → chunk-ODGOP7P2.js} +2 -2
- package/dist/{chunk-QJAQNXWI.js → chunk-ONHRRJZM.js} +6 -6
- package/dist/{chunk-ACXPX4AG.js → chunk-OPBTPRGY.js} +2 -2
- package/dist/{chunk-XOZ4ALGB.js → chunk-QDTZ2HF7.js} +3 -3
- package/dist/{chunk-GBJ4RDFR.js → chunk-QIHIHZYC.js} +66 -91
- package/dist/chunk-QIHIHZYC.js.map +1 -0
- package/dist/{chunk-ZPSVBSOF.js → chunk-QMWFQBIL.js} +3 -3
- package/dist/{chunk-W5EG6LE3.js → chunk-THCPRMRF.js} +3 -3
- package/dist/{chunk-4A6YGWQN.js → chunk-TL4TO3EA.js} +115 -120
- package/dist/chunk-TL4TO3EA.js.map +1 -0
- package/dist/{chunk-VI2JQPNQ.js → chunk-U7QYBSQJ.js} +2 -2
- package/dist/chunk-ULARSG5Z.js +203 -0
- package/dist/chunk-ULARSG5Z.js.map +1 -0
- package/dist/{chunk-TH2LSRMG.js → chunk-UORKJ34C.js} +2 -2
- package/dist/{chunk-4Z4XNEWZ.js → chunk-VCD4PSK2.js} +56 -12
- package/dist/chunk-VCD4PSK2.js.map +1 -0
- package/dist/{chunk-K5GB36ME.js → chunk-VMZ56EE3.js} +2 -2
- package/dist/{chunk-HUHX2OCX.js → chunk-VVDPPDDF.js} +1 -1
- package/dist/chunk-VVDPPDDF.js.map +1 -0
- package/dist/{chunk-RMMVQQA6.js → chunk-WDISJRMG.js} +2 -2
- package/dist/{chunk-B6JDYXL7.js → chunk-WWUVONGW.js} +7 -1
- package/dist/chunk-WWUVONGW.js.map +1 -0
- package/dist/{chunk-UVYI6VIX.js → chunk-X7Y7WX73.js} +1 -1
- package/dist/{chunk-CVILPI77.js → chunk-XOQSEF7X.js} +6 -6
- package/dist/{chunk-RPW2NOU6.js → chunk-YIAA2X3D.js} +41 -1
- package/dist/chunk-YIAA2X3D.js.map +1 -0
- package/dist/chunk-YPC7W6GA.js +1604 -0
- package/dist/chunk-YPC7W6GA.js.map +1 -0
- package/dist/{chunk-YTQNQ4KB.js → chunk-YX5LN6JR.js} +3 -3
- package/dist/{chunk-IC6CBHCA.js → chunk-Z7XKUWYS.js} +2 -2
- package/dist/{chunk-PHWUFPCO.js → chunk-ZCPP5OW6.js} +61 -53
- package/dist/chunk-ZCPP5OW6.js.map +1 -0
- package/dist/{cli-C4GbETPC.d.ts → cli-DRJLv1Xt.d.ts} +5 -5
- package/dist/cli.d.ts +12 -12
- package/dist/cli.js +96 -86
- package/dist/compounding/engine.d.ts +2 -2
- package/dist/compounding/engine.js +14 -14
- 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 +2 -2
- package/dist/connectors/codex-materialize-runner.d.ts +1 -1
- package/dist/connectors/codex-materialize-runner.js +14 -15
- package/dist/connectors/codex-materialize.d.ts +1 -1
- package/dist/connectors/index.d.ts +3 -2
- package/dist/connectors/index.js +24 -23
- package/dist/consolidation-provenance-check.d.ts +2 -2
- package/dist/consolidation-provenance-check.js +5 -5
- package/dist/consolidation-undo.d.ts +2 -2
- package/dist/consolidation-undo.js +3 -3
- package/dist/contradiction/index.d.ts +2 -2
- package/dist/contradiction/index.js +4 -4
- package/dist/conversation-index/backend.d.ts +1 -1
- package/dist/conversation-index/backend.js +2 -2
- 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/corpus-watermark.d.ts +1 -1
- package/dist/corpus-watermark.js +26 -26
- package/dist/day-summary.d.ts +1 -1
- 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/enrichment/index.d.ts +1 -1
- package/dist/entity-retrieval.d.ts +2 -2
- package/dist/entity-retrieval.js +14 -14
- package/dist/entity-schema.d.ts +1 -1
- package/dist/explicit-capture.d.ts +8 -8
- package/dist/extraction-error-classification.d.ts +1 -1
- package/dist/extraction-faithfulness.d.ts +1 -1
- 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-liveness.d.ts +1 -1
- package/dist/extraction-source-grounding-rules.d.ts +1 -1
- package/dist/extraction-source-grounding.d.ts +15 -2
- package/dist/extraction-source-grounding.js +10 -1
- package/dist/extraction.d.ts +2 -15
- package/dist/extraction.js +5 -4
- package/dist/fallback-llm.d.ts +1 -1
- 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/importance.d.ts +1 -1
- package/dist/importers/index.d.ts +1 -1
- package/dist/in-flight-reads.d.ts +1 -1
- package/dist/index.d.ts +19 -19
- package/dist/index.js +253 -246
- package/dist/intent.d.ts +1 -1
- package/dist/lcm/engine.d.ts +1 -1
- package/dist/lcm/engine.js +3 -3
- package/dist/lcm/index.d.ts +1 -1
- package/dist/lcm/index.js +6 -6
- 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/local-llm-helpers.d.ts +7 -0
- package/dist/local-llm-helpers.js +16 -0
- package/dist/local-llm-helpers.js.map +1 -0
- package/dist/local-llm.d.ts +3 -6
- package/dist/local-llm.js +2 -1
- package/dist/local-model-endpoint.d.ts +1 -1
- package/dist/maintenance/memory-governance.d.ts +1 -1
- package/dist/maintenance/memory-governance.js +14 -14
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.d.ts +2 -2
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +14 -14
- package/dist/maintenance/rebuild-memory-projection.d.ts +2 -2
- package/dist/maintenance/rebuild-memory-projection.js +15 -15
- package/dist/{maintenance-CL30NWtc.d.ts → maintenance-C6Pffz30.d.ts} +3 -3
- package/dist/mcp-memory-inspector-app.d.ts +10 -10
- package/dist/memory-action-policy.d.ts +1 -1
- package/dist/memory-cache.d.ts +1 -1
- package/dist/memory-corpus-version.js +2 -2
- 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 +24 -24
- package/dist/namespaces/principal.d.ts +1 -1
- package/dist/namespaces/search.d.ts +1 -1
- package/dist/namespaces/search.js +9 -9
- package/dist/namespaces/storage.d.ts +3 -3
- package/dist/namespaces/storage.js +14 -14
- package/dist/native-knowledge.d.ts +1 -1
- package/dist/offline-sync-impression-drain.d.ts +1 -1
- package/dist/offline-sync-impression-drain.js +6 -5
- package/dist/operator-doctor-corpus.d.ts +1 -1
- package/dist/operator-doctor-corpus.js +29 -29
- package/dist/operator-doctor-replica.d.ts +1 -1
- package/dist/operator-doctor-replica.js +3 -3
- package/dist/operator-toolkit.d.ts +3 -3
- package/dist/operator-toolkit.js +39 -38
- package/dist/orchestration/compression-guideline-coordinator.d.ts +2 -2
- package/dist/orchestration/maintenance.d.ts +4 -4
- package/dist/orchestration/maintenance.js +22 -21
- package/dist/{orchestrator-CKTCRwWV.d.ts → orchestrator-eQlmasno.d.ts} +72 -34
- package/dist/orchestrator.d.ts +8 -8
- package/dist/orchestrator.js +169 -162
- package/dist/page-versioning.js +3 -3
- package/dist/patterns-cli.d.ts +1 -1
- package/dist/{pipeline-B7wj4rRZ.d.ts → pipeline-CZGmAMQ2.d.ts} +1 -1
- package/dist/policy-runtime.d.ts +1 -1
- package/dist/proactive-contention.d.ts +1 -1
- package/dist/provenance.d.ts +1 -1
- package/dist/{qmd-q9tg4dHp.d.ts → qmd-DBj4MF8T.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/qmd.js +4 -4
- package/dist/recall-concurrency-config.d.ts +1 -1
- 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-memory-map.d.ts +1 -1
- package/dist/recall-planner-llm.d.ts +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/replica-divergence.d.ts +1 -1
- package/dist/replica-divergence.js +2 -2
- package/dist/replica-peers-config.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/salvage-envelope.d.ts +1 -1
- package/dist/{scope-profiles-CWDyglPM.d.ts → scope-profiles-S85lrsUn.d.ts} +1 -1
- package/dist/search/embed-helper.d.ts +1 -1
- package/dist/search/factory.d.ts +1 -1
- package/dist/search/factory.js +8 -8
- package/dist/search/index.d.ts +1 -1
- package/dist/search/index.js +12 -12
- 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-BUE3deLn.d.ts → semantic-consolidation-DVzDcV6x.d.ts} +1 -1
- package/dist/semantic-consolidation.d.ts +2 -2
- package/dist/semantic-consolidation.js +17 -18
- package/dist/semantic-rule-promotion.js +14 -14
- package/dist/semantic-rule-verifier.d.ts +1 -1
- package/dist/semantic-rule-verifier.js +14 -14
- package/dist/{service-CXgM0bQG.d.ts → service-C2M-wD4W.d.ts} +2 -2
- 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 +1 -1
- package/dist/signal.d.ts +1 -1
- package/dist/{storage-DysJq-XH.d.ts → storage-dyBC3Q84.d.ts} +1 -1
- package/dist/storage.d.ts +2 -2
- package/dist/storage.js +13 -13
- package/dist/summarizer.d.ts +1 -1
- package/dist/summarizer.js +3 -2
- 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/tokens.js +2 -2
- package/dist/topics.d.ts +1 -1
- package/dist/transcript.d.ts +1 -1
- package/dist/transfer/backup.js +2 -2
- package/dist/transfer/capsule-export.js +2 -2
- package/dist/transfer/capsule-import.js +5 -5
- package/dist/trust-score-stage.d.ts +1 -1
- package/dist/trust-score.d.ts +1 -1
- package/dist/{types-aPPzhnjT.d.ts → types-D63ZI--P.d.ts} +23 -2
- 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 +14 -14
- package/dist/write-envelope.d.ts +1 -1
- package/package.json +2 -2
- package/src/access-boundary.ts +3 -0
- package/src/access-cli.test.ts +93 -0
- package/src/access-cli.ts +56 -2
- package/src/access-extraction-force-flush.ts +177 -0
- package/src/access-http-extraction-force-flush.test.ts +132 -0
- package/src/access-http-lcm-compaction.ts +99 -22
- package/src/access-http-lifecycle-flush.ts +81 -0
- package/src/access-http.test.ts +227 -0
- package/src/access-http.ts +33 -40
- package/src/access-mcp-output-schemas.ts +6 -0
- package/src/access-mcp.test.ts +40 -0
- package/src/access-mcp.ts +32 -1
- package/src/access-observe-helpers.ts +180 -0
- package/src/access-observe-write-surface.ts +78 -19
- package/src/access-operations-batch.ts +2 -1
- package/src/access-schema-pi.test.ts +17 -0
- package/src/access-schema.ts +28 -11
- package/src/access-service-helpers.ts +46 -0
- package/src/access-service-observe-lcm-parity.test.ts +781 -31
- package/src/access-service-observe-scope.test.ts +88 -0
- package/src/access-service.ts +46 -128
- package/src/access-surface-catalog.test.ts +32 -0
- package/src/access-surface-catalog.ts +2 -0
- package/src/buffer-session.test.ts +148 -0
- package/src/buffer-turn-helpers.ts +152 -0
- package/src/buffer.ts +126 -201
- package/src/connectors/index.ts +1 -1
- package/src/connectors/label.ts +18 -0
- package/src/correction/correction-access-wiring.ts +79 -27
- package/src/correction/correction-executor.test.ts +542 -1
- package/src/correction/correction-executor.ts +309 -89
- package/src/correction/correction-planner.test.ts +35 -1
- package/src/correction/correction-planner.ts +50 -3
- package/src/correction/correction-service.ts +12 -7
- package/src/correction/correction-storage-integration.test.ts +172 -1
- package/src/correction/passive-capture.test.ts +35 -0
- package/src/correction/passive-capture.ts +17 -3
- package/src/extraction-source-grounding.ts +55 -1
- package/src/extraction-timeout.test.ts +51 -0
- package/src/extraction.ts +64 -90
- package/src/local-llm-headers-timeout.test.ts +48 -0
- package/src/local-llm-helpers.ts +58 -0
- package/src/local-llm.ts +62 -58
- package/src/orchestration/extraction-run-helpers.ts +309 -0
- package/src/orchestration/extraction-run.test.ts +350 -7
- package/src/orchestration/extraction-run.ts +327 -250
- package/src/orchestration/recall-result-formatter.ts +21 -2
- package/src/orchestration/recall-search-pipeline.test.ts +34 -0
- package/src/orchestration/recall-search-pipeline.ts +9 -1
- package/src/orchestration/session-context.ts +193 -18
- package/src/orchestration/turn-ingestion.ts +61 -11
- package/src/orchestrator-flush.test.ts +511 -5
- package/src/orchestrator.ts +8 -6
- package/src/recall-source-connector-label.test.ts +300 -0
- package/src/types.ts +3 -3
- package/dist/chunk-3UXOZBHV.js +0 -20
- package/dist/chunk-3UXOZBHV.js.map +0 -1
- package/dist/chunk-4A6YGWQN.js.map +0 -1
- package/dist/chunk-4WKA6I65.js.map +0 -1
- package/dist/chunk-4Z4XNEWZ.js.map +0 -1
- package/dist/chunk-6FUPCJUL.js.map +0 -1
- package/dist/chunk-B6JDYXL7.js.map +0 -1
- package/dist/chunk-G5R3D5UP.js.map +0 -1
- package/dist/chunk-GBJ4RDFR.js.map +0 -1
- package/dist/chunk-HUHX2OCX.js.map +0 -1
- package/dist/chunk-IYZ5K7MU.js.map +0 -1
- package/dist/chunk-LGHKJABJ.js.map +0 -1
- package/dist/chunk-NHWB6HG2.js.map +0 -1
- package/dist/chunk-PHWUFPCO.js.map +0 -1
- package/dist/chunk-RPW2NOU6.js.map +0 -1
- package/dist/chunk-SPJXKAVF.js.map +0 -1
- package/dist/chunk-YNQBRLC5.js.map +0 -1
- package/dist/chunk-YTSNAUXN.js.map +0 -1
- /package/dist/{capsule-crypto-GWVG7LGC.js.map → access-extraction-force-flush.js.map} +0 -0
- /package/dist/{capsule-merge-5KKJQBWQ.js.map → capsule-merge-2H2IUTBT.js.map} +0 -0
- /package/dist/{chunk-LEPOP46W.js.map → chunk-227UFSEQ.js.map} +0 -0
- /package/dist/{chunk-I5NLEENJ.js.map → chunk-27HHNYSQ.js.map} +0 -0
- /package/dist/{chunk-2SA56WMI.js.map → chunk-2LTDWSR6.js.map} +0 -0
- /package/dist/{chunk-OWHERGF2.js.map → chunk-2NLLXCJG.js.map} +0 -0
- /package/dist/{chunk-P5PXWTW7.js.map → chunk-2Q4JJ3FZ.js.map} +0 -0
- /package/dist/{chunk-H5CPS5JP.js.map → chunk-35IVNNRK.js.map} +0 -0
- /package/dist/{chunk-LBOD4U5B.js.map → chunk-3EQ2XB3W.js.map} +0 -0
- /package/dist/{chunk-2EUVEDF5.js.map → chunk-5WZVQRHV.js.map} +0 -0
- /package/dist/{chunk-ALSGMEJR.js.map → chunk-65ZPVYTA.js.map} +0 -0
- /package/dist/{chunk-GIBMDSJ2.js.map → chunk-6P7MTSU5.js.map} +0 -0
- /package/dist/{chunk-5VQJBH2G.js.map → chunk-6TAUHL33.js.map} +0 -0
- /package/dist/{chunk-WVO4G37W.js.map → chunk-7TEZUUGM.js.map} +0 -0
- /package/dist/{chunk-MJCGLM25.js.map → chunk-7VUAG236.js.map} +0 -0
- /package/dist/{chunk-CZRYLSSW.js.map → chunk-AJL6NNX6.js.map} +0 -0
- /package/dist/{chunk-66772WBP.js.map → chunk-AQWVJURR.js.map} +0 -0
- /package/dist/{chunk-RKNJBZ55.js.map → chunk-AU7Q3LSC.js.map} +0 -0
- /package/dist/{chunk-M3C2V3PV.js.map → chunk-CJDNT45S.js.map} +0 -0
- /package/dist/{chunk-D2ANTDGO.js.map → chunk-E6BMSH7U.js.map} +0 -0
- /package/dist/{chunk-CKMO4RDY.js.map → chunk-EMDLM3LW.js.map} +0 -0
- /package/dist/{chunk-RGIWINDN.js.map → chunk-FHBITMRY.js.map} +0 -0
- /package/dist/{chunk-FDAMS4PK.js.map → chunk-G2YQ57YO.js.map} +0 -0
- /package/dist/{chunk-6U7F52DE.js.map → chunk-HFQQWAMS.js.map} +0 -0
- /package/dist/{chunk-5W4ZLBEZ.js.map → chunk-HKPJPOW7.js.map} +0 -0
- /package/dist/{chunk-JCBWCYEJ.js.map → chunk-IGWFKW4B.js.map} +0 -0
- /package/dist/{chunk-AITCS2RC.js.map → chunk-IJ7HK452.js.map} +0 -0
- /package/dist/{chunk-D3LMYTBV.js.map → chunk-J7WZUL2K.js.map} +0 -0
- /package/dist/{chunk-5W7CTZ2E.js.map → chunk-KQAFEZQX.js.map} +0 -0
- /package/dist/{chunk-Z54EM5YZ.js.map → chunk-MAXVH44E.js.map} +0 -0
- /package/dist/{chunk-WZFAPHIK.js.map → chunk-MU7ZHSZE.js.map} +0 -0
- /package/dist/{chunk-AZ5BTEKX.js.map → chunk-NQUF7XKR.js.map} +0 -0
- /package/dist/{chunk-W4FBGYGK.js.map → chunk-NRV4Y3PM.js.map} +0 -0
- /package/dist/{chunk-62U4J2GN.js.map → chunk-ODGOP7P2.js.map} +0 -0
- /package/dist/{chunk-QJAQNXWI.js.map → chunk-ONHRRJZM.js.map} +0 -0
- /package/dist/{chunk-ACXPX4AG.js.map → chunk-OPBTPRGY.js.map} +0 -0
- /package/dist/{chunk-XOZ4ALGB.js.map → chunk-QDTZ2HF7.js.map} +0 -0
- /package/dist/{chunk-ZPSVBSOF.js.map → chunk-QMWFQBIL.js.map} +0 -0
- /package/dist/{chunk-W5EG6LE3.js.map → chunk-THCPRMRF.js.map} +0 -0
- /package/dist/{chunk-VI2JQPNQ.js.map → chunk-U7QYBSQJ.js.map} +0 -0
- /package/dist/{chunk-TH2LSRMG.js.map → chunk-UORKJ34C.js.map} +0 -0
- /package/dist/{chunk-K5GB36ME.js.map → chunk-VMZ56EE3.js.map} +0 -0
- /package/dist/{chunk-RMMVQQA6.js.map → chunk-WDISJRMG.js.map} +0 -0
- /package/dist/{chunk-UVYI6VIX.js.map → chunk-X7Y7WX73.js.map} +0 -0
- /package/dist/{chunk-CVILPI77.js.map → chunk-XOQSEF7X.js.map} +0 -0
- /package/dist/{chunk-YTQNQ4KB.js.map → chunk-YX5LN6JR.js.map} +0 -0
- /package/dist/{chunk-IC6CBHCA.js.map → chunk-Z7XKUWYS.js.map} +0 -0
|
@@ -0,0 +1,1604 @@
|
|
|
1
|
+
import {
|
|
2
|
+
resolveCodingNamespaceOverlay,
|
|
3
|
+
resolveScopeProfilePlan
|
|
4
|
+
} from "./chunk-BUE5E2HL.js";
|
|
5
|
+
import {
|
|
6
|
+
hasBroadGraphIntent,
|
|
7
|
+
planRecallMode
|
|
8
|
+
} from "./chunk-PD6O7AXF.js";
|
|
9
|
+
import {
|
|
10
|
+
parseQmdExplain
|
|
11
|
+
} from "./chunk-ONHRRJZM.js";
|
|
12
|
+
import {
|
|
13
|
+
buildCompressionGuidelinesMarkdown
|
|
14
|
+
} from "./chunk-OD4FM2U7.js";
|
|
15
|
+
import {
|
|
16
|
+
resolveLifecycleState
|
|
17
|
+
} from "./chunk-TBBDFYXW.js";
|
|
18
|
+
import {
|
|
19
|
+
parseFlexibleIsoTimestamp
|
|
20
|
+
} from "./chunk-P7FMDTKL.js";
|
|
21
|
+
import {
|
|
22
|
+
defaultNamespaceForPrincipal,
|
|
23
|
+
resolvePrincipal
|
|
24
|
+
} from "./chunk-3GD2HFR6.js";
|
|
25
|
+
import {
|
|
26
|
+
resolveMemoryLifecycleCapabilities,
|
|
27
|
+
resolvePresentationCapabilities
|
|
28
|
+
} from "./chunk-2XRE6FLM.js";
|
|
29
|
+
import {
|
|
30
|
+
abortError,
|
|
31
|
+
throwIfAborted
|
|
32
|
+
} from "./chunk-PVGDJXVK.js";
|
|
33
|
+
import {
|
|
34
|
+
categoryDirName
|
|
35
|
+
} from "./chunk-VS2IYZRU.js";
|
|
36
|
+
import {
|
|
37
|
+
log
|
|
38
|
+
} from "./chunk-3WCASNQZ.js";
|
|
39
|
+
|
|
40
|
+
// src/orchestration/orchestrator-helpers.ts
|
|
41
|
+
import { createHash } from "crypto";
|
|
42
|
+
import os from "os";
|
|
43
|
+
import path from "path";
|
|
44
|
+
var BulkImportBatchPartialFailureError = class extends Error {
|
|
45
|
+
partialResult;
|
|
46
|
+
originalError;
|
|
47
|
+
constructor(message, partialResult, originalError) {
|
|
48
|
+
super(message);
|
|
49
|
+
this.name = "BulkImportBatchPartialFailureError";
|
|
50
|
+
this.partialResult = partialResult;
|
|
51
|
+
this.originalError = originalError;
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
function mapRecallSourceToXrayServedBy(source) {
|
|
55
|
+
switch (source) {
|
|
56
|
+
case "recent_scan":
|
|
57
|
+
return "recent-scan";
|
|
58
|
+
case "hot_qmd":
|
|
59
|
+
case "hot_embedding":
|
|
60
|
+
case "cold_fallback":
|
|
61
|
+
case "none":
|
|
62
|
+
return "hybrid";
|
|
63
|
+
}
|
|
64
|
+
const _exhaustive = source;
|
|
65
|
+
void _exhaustive;
|
|
66
|
+
return "hybrid";
|
|
67
|
+
}
|
|
68
|
+
var abortRecallError = abortError;
|
|
69
|
+
function throwIfRecallAborted(signal, message = "recall aborted") {
|
|
70
|
+
throwIfAborted(signal, message);
|
|
71
|
+
}
|
|
72
|
+
async function raceRecallAbort(promise, signal, message = "recall aborted") {
|
|
73
|
+
throwIfRecallAborted(signal, message);
|
|
74
|
+
if (!signal) return promise;
|
|
75
|
+
let onAbort = null;
|
|
76
|
+
const abortPromise = new Promise((_resolve, reject) => {
|
|
77
|
+
onAbort = () => reject(abortRecallError(message));
|
|
78
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
79
|
+
});
|
|
80
|
+
try {
|
|
81
|
+
return await Promise.race([promise, abortPromise]);
|
|
82
|
+
} finally {
|
|
83
|
+
if (onAbort) {
|
|
84
|
+
signal.removeEventListener("abort", onAbort);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
var COMPACTION_SIGNAL_MAX_AGE_MS = 60 * 60 * 1e3;
|
|
89
|
+
var DEFAULT_QMD_STARTUP_COLLECTION_CHECK_TIMEOUT_MS = 1e4;
|
|
90
|
+
function normalizeIanaTimeZone(value) {
|
|
91
|
+
if (typeof value !== "string") return void 0;
|
|
92
|
+
const trimmed = value.trim();
|
|
93
|
+
if (!trimmed) return void 0;
|
|
94
|
+
try {
|
|
95
|
+
Intl.DateTimeFormat(void 0, { timeZone: trimmed });
|
|
96
|
+
return trimmed;
|
|
97
|
+
} catch {
|
|
98
|
+
return void 0;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
function formatDateInTimeZone(date, timeZone) {
|
|
102
|
+
const parts = new Intl.DateTimeFormat("en-US", {
|
|
103
|
+
timeZone,
|
|
104
|
+
year: "numeric",
|
|
105
|
+
month: "2-digit",
|
|
106
|
+
day: "2-digit"
|
|
107
|
+
}).formatToParts(date);
|
|
108
|
+
const get = (type) => parts.find((part) => part.type === type)?.value ?? "";
|
|
109
|
+
return `${get("year")}-${get("month")}-${get("day")}`;
|
|
110
|
+
}
|
|
111
|
+
function utcDateKey(date) {
|
|
112
|
+
return date.toISOString().slice(0, 10);
|
|
113
|
+
}
|
|
114
|
+
function utcDateKeysAround(date) {
|
|
115
|
+
const dayMs = 864e5;
|
|
116
|
+
const keys = [
|
|
117
|
+
utcDateKey(new Date(date.getTime() - dayMs)),
|
|
118
|
+
utcDateKey(date),
|
|
119
|
+
utcDateKey(new Date(date.getTime() + dayMs))
|
|
120
|
+
];
|
|
121
|
+
return keys.filter((value, index, array) => array.indexOf(value) === index);
|
|
122
|
+
}
|
|
123
|
+
function utcDateKeysForLocalDay(date, timeZone) {
|
|
124
|
+
const targetLocalDate = formatDateInTimeZone(date, timeZone);
|
|
125
|
+
const keys = /* @__PURE__ */ new Set();
|
|
126
|
+
const hourMs = 36e5;
|
|
127
|
+
const scanStart = date.getTime() - 48 * hourMs;
|
|
128
|
+
const scanEnd = date.getTime() + 48 * hourMs;
|
|
129
|
+
for (let ms = scanStart; ms <= scanEnd; ms += hourMs) {
|
|
130
|
+
const candidate = new Date(ms);
|
|
131
|
+
if (formatDateInTimeZone(candidate, timeZone) === targetLocalDate) {
|
|
132
|
+
keys.add(utcDateKey(candidate));
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return keys.size > 0 ? [...keys].sort() : utcDateKeysAround(date);
|
|
136
|
+
}
|
|
137
|
+
function parseFiniteDate(value) {
|
|
138
|
+
if (typeof value !== "string" || value.trim().length === 0) return null;
|
|
139
|
+
const parsed = new Date(value);
|
|
140
|
+
return Number.isFinite(parsed.getTime()) ? parsed : null;
|
|
141
|
+
}
|
|
142
|
+
function filterHourlySummaryMarkdownForLocalDay(raw, utcDate, timeZone, targetLocalDate) {
|
|
143
|
+
const hourHeaderPattern = /^## ([01]\d|2[0-3]):00[ \t]*$/gm;
|
|
144
|
+
const matches = Array.from(raw.matchAll(hourHeaderPattern));
|
|
145
|
+
if (matches.length === 0) return null;
|
|
146
|
+
const firstSectionStart = matches[0]?.index ?? 0;
|
|
147
|
+
const preamble = raw.slice(0, firstSectionStart).trim();
|
|
148
|
+
const sections = [];
|
|
149
|
+
for (let index = 0; index < matches.length; index += 1) {
|
|
150
|
+
const match = matches[index];
|
|
151
|
+
const hour = match[1];
|
|
152
|
+
if (!hour) continue;
|
|
153
|
+
const sectionTimestamp = parseFiniteDate(`${utcDate}T${hour}:00:00.000Z`);
|
|
154
|
+
if (!sectionTimestamp || formatDateInTimeZone(sectionTimestamp, timeZone) !== targetLocalDate) {
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
const sectionStart = match.index ?? 0;
|
|
158
|
+
const sectionEnd = matches[index + 1]?.index ?? raw.length;
|
|
159
|
+
const section = raw.slice(sectionStart, sectionEnd).trim();
|
|
160
|
+
if (section.length > 0) sections.push(section);
|
|
161
|
+
}
|
|
162
|
+
if (sections.length === 0) return null;
|
|
163
|
+
return [preamble, ...sections].filter((section) => section.length > 0).join("\n\n");
|
|
164
|
+
}
|
|
165
|
+
function qmdStartupCollectionCheckTimeoutMs() {
|
|
166
|
+
const raw = process.env.REMNIC_QMD_STARTUP_COLLECTION_CHECK_TIMEOUT_MS ?? process.env.ENGRAM_QMD_STARTUP_COLLECTION_CHECK_TIMEOUT_MS;
|
|
167
|
+
if (raw === void 0) return DEFAULT_QMD_STARTUP_COLLECTION_CHECK_TIMEOUT_MS;
|
|
168
|
+
const parsed = Number(raw);
|
|
169
|
+
return Number.isFinite(parsed) && parsed >= 1e3 ? Math.floor(parsed) : DEFAULT_QMD_STARTUP_COLLECTION_CHECK_TIMEOUT_MS;
|
|
170
|
+
}
|
|
171
|
+
async function qmdStartupCollectionCheckWithTimeout(promise, controller, label) {
|
|
172
|
+
const timeoutMs = qmdStartupCollectionCheckTimeoutMs();
|
|
173
|
+
let timer;
|
|
174
|
+
let settled = false;
|
|
175
|
+
const timeoutPromise = new Promise((resolve) => {
|
|
176
|
+
timer = setTimeout(() => {
|
|
177
|
+
if (settled) return;
|
|
178
|
+
controller.abort();
|
|
179
|
+
log.warn(
|
|
180
|
+
`QMD startup collection check for ${label} timed out after ${timeoutMs}ms; keeping search enabled fail-open`
|
|
181
|
+
);
|
|
182
|
+
resolve("unknown");
|
|
183
|
+
}, timeoutMs);
|
|
184
|
+
timer.unref?.();
|
|
185
|
+
});
|
|
186
|
+
const checkedPromise = promise.catch((err) => {
|
|
187
|
+
log.warn(
|
|
188
|
+
`QMD startup collection check for ${label} failed; keeping search enabled fail-open: ${err}`
|
|
189
|
+
);
|
|
190
|
+
return "unknown";
|
|
191
|
+
}).finally(() => {
|
|
192
|
+
settled = true;
|
|
193
|
+
if (timer) clearTimeout(timer);
|
|
194
|
+
});
|
|
195
|
+
return await Promise.race([checkedPromise, timeoutPromise]);
|
|
196
|
+
}
|
|
197
|
+
function defaultWorkspaceDir() {
|
|
198
|
+
return path.join(os.homedir(), ".openclaw", "workspace");
|
|
199
|
+
}
|
|
200
|
+
function sanitizeSessionKeyForFilename(sessionKey) {
|
|
201
|
+
const readable = sessionKey.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
202
|
+
const hash = createHash("sha256").update(sessionKey).digest("hex").slice(0, 12);
|
|
203
|
+
return `${readable}-${hash}`;
|
|
204
|
+
}
|
|
205
|
+
function sourceValidAtMs(turn) {
|
|
206
|
+
if (typeof turn.sourceValidAt !== "string") return null;
|
|
207
|
+
return parseFlexibleIsoTimestamp(turn.sourceValidAt.trim());
|
|
208
|
+
}
|
|
209
|
+
var SOURCE_VALID_AT_CONTEXT_TURNS = 2;
|
|
210
|
+
function sourceValidAtSliceKey(turn, index) {
|
|
211
|
+
const validAtMs = sourceValidAtMs(turn);
|
|
212
|
+
return validAtMs === null ? `unknown:${index}` : String(validAtMs);
|
|
213
|
+
}
|
|
214
|
+
function asExtractionContextTurn(turn) {
|
|
215
|
+
return { ...turn, extractionContextOnly: true };
|
|
216
|
+
}
|
|
217
|
+
function asExtractionTargetTurn(turn) {
|
|
218
|
+
const { extractionContextOnly: _contextOnly, ...targetTurn } = turn;
|
|
219
|
+
return targetTurn;
|
|
220
|
+
}
|
|
221
|
+
function sourceValidAtContextTurns(turns, targetStart, targetEnd, targetValidAtMs) {
|
|
222
|
+
if (targetValidAtMs === null) return [];
|
|
223
|
+
return turns.flatMap((turn, index) => {
|
|
224
|
+
if (index >= targetStart && index < targetEnd) return [];
|
|
225
|
+
const contextValidAtMs = sourceValidAtMs(turn);
|
|
226
|
+
if (contextValidAtMs === null || contextValidAtMs > targetValidAtMs) {
|
|
227
|
+
return [];
|
|
228
|
+
}
|
|
229
|
+
return [{ turn, index, validAtMs: contextValidAtMs }];
|
|
230
|
+
}).sort((a, b) => {
|
|
231
|
+
if (a.validAtMs < b.validAtMs) return -1;
|
|
232
|
+
if (a.validAtMs > b.validAtMs) return 1;
|
|
233
|
+
if (a.index === b.index) return 0;
|
|
234
|
+
return a.index < b.index ? -1 : 1;
|
|
235
|
+
}).slice(-SOURCE_VALID_AT_CONTEXT_TURNS).map(({ turn }) => asExtractionContextTurn(turn));
|
|
236
|
+
}
|
|
237
|
+
function targetSourceValidAtSortMs(turns) {
|
|
238
|
+
let latestMs = null;
|
|
239
|
+
for (const turn of turns) {
|
|
240
|
+
if (turn.extractionContextOnly === true) continue;
|
|
241
|
+
const validAtMs = sourceValidAtMs(turn);
|
|
242
|
+
if (validAtMs === null) continue;
|
|
243
|
+
if (latestMs === null || validAtMs > latestMs) {
|
|
244
|
+
latestMs = validAtMs;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
return latestMs ?? Number.POSITIVE_INFINITY;
|
|
248
|
+
}
|
|
249
|
+
function sortSourceValidAtSlicesChronologically(slices) {
|
|
250
|
+
return slices.map((turns, order) => ({
|
|
251
|
+
turns,
|
|
252
|
+
order,
|
|
253
|
+
targetValidAtMs: targetSourceValidAtSortMs(turns)
|
|
254
|
+
})).sort((a, b) => {
|
|
255
|
+
if (a.targetValidAtMs < b.targetValidAtMs) return -1;
|
|
256
|
+
if (a.targetValidAtMs > b.targetValidAtMs) return 1;
|
|
257
|
+
if (a.order === b.order) return 0;
|
|
258
|
+
return a.order < b.order ? -1 : 1;
|
|
259
|
+
}).map((slice) => slice.turns);
|
|
260
|
+
}
|
|
261
|
+
function splitTurnsBySourceValidAt(turns, options = {}) {
|
|
262
|
+
if (turns.length === 0) return [];
|
|
263
|
+
if (!turns.some((turn) => sourceValidAtMs(turn) !== null)) {
|
|
264
|
+
return [[...turns]];
|
|
265
|
+
}
|
|
266
|
+
const slices = [];
|
|
267
|
+
let start = 0;
|
|
268
|
+
while (start < turns.length) {
|
|
269
|
+
const targetValidAtMs = sourceValidAtMs(turns[start]);
|
|
270
|
+
const activeKey = sourceValidAtSliceKey(turns[start], start);
|
|
271
|
+
let end = start + 1;
|
|
272
|
+
while (end < turns.length && sourceValidAtSliceKey(turns[end], end) === activeKey) {
|
|
273
|
+
end += 1;
|
|
274
|
+
}
|
|
275
|
+
const contextTurns = options.includeContext === false ? [] : sourceValidAtContextTurns(turns, start, end, targetValidAtMs);
|
|
276
|
+
slices.push([
|
|
277
|
+
...contextTurns,
|
|
278
|
+
...turns.slice(start, end).map(asExtractionTargetTurn)
|
|
279
|
+
]);
|
|
280
|
+
start = end;
|
|
281
|
+
}
|
|
282
|
+
return sortSourceValidAtSlicesChronologically(slices);
|
|
283
|
+
}
|
|
284
|
+
function isArtifactMemoryPath(filePath) {
|
|
285
|
+
return /(?:^|[\\/])artifacts(?:[\\/]|$)/i.test(filePath);
|
|
286
|
+
}
|
|
287
|
+
function isMeetingRecordPath(filePath) {
|
|
288
|
+
return /(?:^|[\\/])meetings[\\/]\d{4}-\d{2}-\d{2}[\\/]mtg-\d{4}-\d{2}-\d{2}-[0-9a-f]{8}\.md$/i.test(filePath);
|
|
289
|
+
}
|
|
290
|
+
var ACTIVITY_DIGEST_TOPLEVEL = /^activity[\\/]\d{4}-\d{2}-\d{2}\.md$/i;
|
|
291
|
+
var ACTIVITY_DIGEST_ANYWHERE = /(?:^|[\\/])activity[\\/]\d{4}-\d{2}-\d{2}\.md$/i;
|
|
292
|
+
function isActivityDigestPath(filePath, memoryRoot) {
|
|
293
|
+
if (memoryRoot !== void 0 && memoryRoot.length > 0) {
|
|
294
|
+
const relative = path.relative(memoryRoot, path.resolve(memoryRoot, filePath));
|
|
295
|
+
return ACTIVITY_DIGEST_TOPLEVEL.test(relative);
|
|
296
|
+
}
|
|
297
|
+
return ACTIVITY_DIGEST_ANYWHERE.test(filePath);
|
|
298
|
+
}
|
|
299
|
+
function buildCompressionGuidelinesMarkdown2(events, generatedAtIso = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
300
|
+
return buildCompressionGuidelinesMarkdown(events, generatedAtIso);
|
|
301
|
+
}
|
|
302
|
+
function applyQueryAwareCandidateFilter(candidates, candidatePaths) {
|
|
303
|
+
if (!candidatePaths) return candidates;
|
|
304
|
+
if (candidatePaths.size === 0) return [];
|
|
305
|
+
const filtered = candidates.filter(
|
|
306
|
+
(candidate) => candidatePaths.has(candidate.path)
|
|
307
|
+
);
|
|
308
|
+
return filtered.length > 0 ? filtered : candidates;
|
|
309
|
+
}
|
|
310
|
+
function tokenizeRecallQuery(prompt) {
|
|
311
|
+
return prompt.toLowerCase().split(/[^a-z0-9]+/i).map((t) => t.trim()).filter((t) => t.length >= 3);
|
|
312
|
+
}
|
|
313
|
+
function hasLifecycleMetadata(frontmatter) {
|
|
314
|
+
return frontmatter.lifecycleState !== void 0 || frontmatter.verificationState !== void 0 || frontmatter.policyClass !== void 0 || frontmatter.lastValidatedAt !== void 0 || frontmatter.decayScore !== void 0 || frontmatter.heatScore !== void 0;
|
|
315
|
+
}
|
|
316
|
+
function shouldFilterLifecycleRecallCandidate(frontmatter, options) {
|
|
317
|
+
if (!options.lifecyclePolicyEnabled || !options.lifecycleFilterStaleEnabled)
|
|
318
|
+
return false;
|
|
319
|
+
if (!hasLifecycleMetadata(frontmatter)) return false;
|
|
320
|
+
const lifecycleState = resolveLifecycleState(frontmatter);
|
|
321
|
+
return lifecycleState === "stale" || lifecycleState === "archived";
|
|
322
|
+
}
|
|
323
|
+
function lifecycleRecallScoreAdjustment(frontmatter, options) {
|
|
324
|
+
if (!options.lifecyclePolicyEnabled) return 0;
|
|
325
|
+
if (!hasLifecycleMetadata(frontmatter)) return 0;
|
|
326
|
+
let delta = 0;
|
|
327
|
+
const lifecycleState = resolveLifecycleState(frontmatter);
|
|
328
|
+
switch (lifecycleState) {
|
|
329
|
+
case "active":
|
|
330
|
+
delta += 0.05;
|
|
331
|
+
break;
|
|
332
|
+
case "validated":
|
|
333
|
+
delta += 0.03;
|
|
334
|
+
break;
|
|
335
|
+
case "candidate":
|
|
336
|
+
delta -= 0.01;
|
|
337
|
+
break;
|
|
338
|
+
case "stale":
|
|
339
|
+
delta -= 0.06;
|
|
340
|
+
break;
|
|
341
|
+
case "archived":
|
|
342
|
+
delta -= 0.08;
|
|
343
|
+
break;
|
|
344
|
+
}
|
|
345
|
+
if (frontmatter.verificationState === "disputed") {
|
|
346
|
+
delta -= 0.12;
|
|
347
|
+
}
|
|
348
|
+
return delta;
|
|
349
|
+
}
|
|
350
|
+
function computeArtifactRecallLimit(recallMode, recallResultLimit, verbatimArtifactsMaxRecall) {
|
|
351
|
+
if (recallMode === "no_recall") return 0;
|
|
352
|
+
if (Math.max(0, recallResultLimit) === 0) return 0;
|
|
353
|
+
const base = Math.max(0, verbatimArtifactsMaxRecall);
|
|
354
|
+
if (recallMode === "minimal") {
|
|
355
|
+
return Math.min(base, Math.max(0, recallResultLimit));
|
|
356
|
+
}
|
|
357
|
+
return base;
|
|
358
|
+
}
|
|
359
|
+
function resolveEffectiveRecallMode(options) {
|
|
360
|
+
return resolveRecallModeDecision(options).effectiveMode;
|
|
361
|
+
}
|
|
362
|
+
function finalizeRecallModeDecision(plannedModeRaw, options) {
|
|
363
|
+
let plannedMode = plannedModeRaw;
|
|
364
|
+
const graphExpandedIntentDetected = options.plannerEnabled && options.graphExpandedIntentEnabled === true && hasBroadGraphIntent(options.prompt);
|
|
365
|
+
if (plannedMode !== "graph_mode" && graphExpandedIntentDetected) {
|
|
366
|
+
plannedMode = "graph_mode";
|
|
367
|
+
}
|
|
368
|
+
if (plannedMode === "graph_mode" && (!options.graphRecallEnabled || !options.multiGraphMemoryEnabled)) {
|
|
369
|
+
return {
|
|
370
|
+
plannedMode,
|
|
371
|
+
effectiveMode: "full",
|
|
372
|
+
graphExpandedIntentDetected,
|
|
373
|
+
graphReason: !options.graphRecallEnabled ? "graph recall disabled by config" : "multi-graph memory disabled by config"
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
return {
|
|
377
|
+
plannedMode,
|
|
378
|
+
effectiveMode: plannedMode,
|
|
379
|
+
graphExpandedIntentDetected
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
function resolveRecallModeDecision(options) {
|
|
383
|
+
const plannedMode = options.plannerEnabled ? planRecallMode(options.prompt) : "full";
|
|
384
|
+
return finalizeRecallModeDecision(plannedMode, options);
|
|
385
|
+
}
|
|
386
|
+
async function resolveRecallModeDecisionAsync(options) {
|
|
387
|
+
const heuristicDecision = resolveRecallModeDecision(options);
|
|
388
|
+
const plannerLlmEnabled = options.caps.recallPlannerLlm;
|
|
389
|
+
if (!options.plannerEnabled || !plannerLlmEnabled) {
|
|
390
|
+
return heuristicDecision;
|
|
391
|
+
}
|
|
392
|
+
const { planRecallModeLLM } = await import("./recall-planner-llm.js");
|
|
393
|
+
const planned = await planRecallModeLLM(
|
|
394
|
+
options.prompt,
|
|
395
|
+
options.hints,
|
|
396
|
+
options.config,
|
|
397
|
+
options.caps,
|
|
398
|
+
options.llm,
|
|
399
|
+
options.signal
|
|
400
|
+
);
|
|
401
|
+
if (options.config.recallPlannerShadowMode) {
|
|
402
|
+
return {
|
|
403
|
+
...heuristicDecision,
|
|
404
|
+
plannerSource: planned.source,
|
|
405
|
+
plannerReason: `shadow:${planned.reason}`,
|
|
406
|
+
plannerLatencyMs: planned.latencyMs,
|
|
407
|
+
plannerFallbackUsed: planned.fallbackUsed,
|
|
408
|
+
plannerModelUsed: planned.modelUsed,
|
|
409
|
+
plannerHeuristicMode: planned.heuristicMode,
|
|
410
|
+
shadowLlmMode: planned.mode
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
const llmDecision = finalizeRecallModeDecision(planned.mode, options);
|
|
414
|
+
return {
|
|
415
|
+
...llmDecision,
|
|
416
|
+
plannerSource: planned.source,
|
|
417
|
+
plannerReason: planned.reason,
|
|
418
|
+
plannerLatencyMs: planned.latencyMs,
|
|
419
|
+
plannerFallbackUsed: planned.fallbackUsed,
|
|
420
|
+
plannerModelUsed: planned.modelUsed,
|
|
421
|
+
plannerHeuristicMode: planned.heuristicMode
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
function computeArtifactCandidateFetchLimit(targetCount) {
|
|
425
|
+
const cappedTarget = Math.max(0, targetCount);
|
|
426
|
+
if (cappedTarget === 0) return 0;
|
|
427
|
+
const headroom = Math.max(8, cappedTarget * 4);
|
|
428
|
+
return Math.min(200, cappedTarget + headroom);
|
|
429
|
+
}
|
|
430
|
+
function computeQmdHybridFetchLimit(recallFetchLimit, artifactsEnabled, maxArtifactRecall) {
|
|
431
|
+
const cappedRecallLimit = Math.max(0, recallFetchLimit);
|
|
432
|
+
if (cappedRecallLimit === 0) return 0;
|
|
433
|
+
if (!artifactsEnabled) return cappedRecallLimit;
|
|
434
|
+
const artifactHeadroom = Math.max(20, Math.max(0, maxArtifactRecall) * 8);
|
|
435
|
+
return Math.min(400, cappedRecallLimit + artifactHeadroom);
|
|
436
|
+
}
|
|
437
|
+
function summarizeGraphShadowComparison(baseline, merged, topN) {
|
|
438
|
+
const limit = Math.max(0, Math.floor(topN));
|
|
439
|
+
const baselineTop = limit > 0 ? baseline.slice(0, limit) : [];
|
|
440
|
+
const graphTop = limit > 0 ? merged.slice(0, limit) : [];
|
|
441
|
+
const baselineByPath = new Map(
|
|
442
|
+
baselineTop.map((item) => [item.path, item.score])
|
|
443
|
+
);
|
|
444
|
+
const graphByPath = new Map(graphTop.map((item) => [item.path, item.score]));
|
|
445
|
+
let overlapCount = 0;
|
|
446
|
+
let overlapDeltaSum = 0;
|
|
447
|
+
for (const [p, baselineScore] of baselineByPath.entries()) {
|
|
448
|
+
const graphScore = graphByPath.get(p);
|
|
449
|
+
if (typeof graphScore !== "number") continue;
|
|
450
|
+
overlapCount += 1;
|
|
451
|
+
overlapDeltaSum += graphScore - baselineScore;
|
|
452
|
+
}
|
|
453
|
+
const baselineCount = baselineTop.length;
|
|
454
|
+
return {
|
|
455
|
+
baselineCount,
|
|
456
|
+
graphCount: graphTop.length,
|
|
457
|
+
overlapCount,
|
|
458
|
+
overlapRatio: baselineCount > 0 ? overlapCount / baselineCount : 0,
|
|
459
|
+
averageOverlapDelta: overlapCount > 0 ? overlapDeltaSum / overlapCount : 0
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
function parseGraphRecallRankedResults(value) {
|
|
463
|
+
if (!Array.isArray(value)) return [];
|
|
464
|
+
const parsed = [];
|
|
465
|
+
for (const entry of value) {
|
|
466
|
+
if (!entry || typeof entry !== "object") continue;
|
|
467
|
+
const candidate = entry;
|
|
468
|
+
if (typeof candidate.path !== "string" || typeof candidate.score !== "number")
|
|
469
|
+
continue;
|
|
470
|
+
parsed.push({
|
|
471
|
+
path: candidate.path,
|
|
472
|
+
score: candidate.score,
|
|
473
|
+
docid: typeof candidate.docid === "string" ? candidate.docid : void 0,
|
|
474
|
+
sourceLabels: Array.isArray(candidate.sourceLabels) ? candidate.sourceLabels.filter(
|
|
475
|
+
(item) => typeof item === "string"
|
|
476
|
+
) : []
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
return parsed.slice(0, 64);
|
|
480
|
+
}
|
|
481
|
+
function parseMemoryIntentSnapshot(value) {
|
|
482
|
+
const candidate = value && typeof value === "object" ? value : {};
|
|
483
|
+
return {
|
|
484
|
+
goal: typeof candidate.goal === "string" ? candidate.goal : "unknown",
|
|
485
|
+
actionType: typeof candidate.actionType === "string" ? candidate.actionType : "unknown",
|
|
486
|
+
entityTypes: Array.isArray(candidate.entityTypes) ? candidate.entityTypes.filter(
|
|
487
|
+
(item) => typeof item === "string"
|
|
488
|
+
) : [],
|
|
489
|
+
taskInitiation: candidate.taskInitiation === true
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
function buildQmdIntentHint(intent) {
|
|
493
|
+
const parts = [];
|
|
494
|
+
if (intent.goal !== "unknown") {
|
|
495
|
+
parts.push(`goal:${intent.goal.replace(/_/g, " ")}`);
|
|
496
|
+
}
|
|
497
|
+
if (intent.actionType !== "unknown") {
|
|
498
|
+
parts.push(`action:${intent.actionType.replace(/_/g, " ")}`);
|
|
499
|
+
}
|
|
500
|
+
if (intent.entityTypes.length > 0) {
|
|
501
|
+
parts.push(`entities:${intent.entityTypes.join(",")}`);
|
|
502
|
+
}
|
|
503
|
+
if (intent.taskInitiation === true) {
|
|
504
|
+
parts.push("task_initiation");
|
|
505
|
+
}
|
|
506
|
+
return parts.length > 0 ? parts.join(" ") : void 0;
|
|
507
|
+
}
|
|
508
|
+
function parseQmdRecallResults(value) {
|
|
509
|
+
if (!Array.isArray(value)) return [];
|
|
510
|
+
const parsed = [];
|
|
511
|
+
for (const entry of value) {
|
|
512
|
+
if (!entry || typeof entry !== "object") continue;
|
|
513
|
+
const candidate = entry;
|
|
514
|
+
if (typeof candidate.path !== "string" || typeof candidate.score !== "number")
|
|
515
|
+
continue;
|
|
516
|
+
parsed.push({
|
|
517
|
+
docid: typeof candidate.docid === "string" ? candidate.docid : "",
|
|
518
|
+
path: candidate.path,
|
|
519
|
+
snippet: typeof candidate.snippet === "string" ? candidate.snippet : "",
|
|
520
|
+
score: candidate.score,
|
|
521
|
+
explain: parseQmdExplain(candidate.explain),
|
|
522
|
+
transport: candidate.transport === "daemon" || candidate.transport === "subprocess" || candidate.transport === "hybrid" || candidate.transport === "scoped_prefilter" ? candidate.transport : void 0
|
|
523
|
+
});
|
|
524
|
+
}
|
|
525
|
+
return parsed.slice(0, 32);
|
|
526
|
+
}
|
|
527
|
+
function mergeArtifactRecallCandidates(candidatesByNamespace, limit) {
|
|
528
|
+
const cappedLimit = Math.max(0, limit);
|
|
529
|
+
if (cappedLimit === 0) return [];
|
|
530
|
+
const out = [];
|
|
531
|
+
const seen = /* @__PURE__ */ new Set();
|
|
532
|
+
let offset = 0;
|
|
533
|
+
while (out.length < cappedLimit) {
|
|
534
|
+
let hasAnyCandidateAtOffset = false;
|
|
535
|
+
for (const list of candidatesByNamespace) {
|
|
536
|
+
if (offset >= list.length) continue;
|
|
537
|
+
hasAnyCandidateAtOffset = true;
|
|
538
|
+
const item = list[offset];
|
|
539
|
+
const dedupeKey = `${item.frontmatter.id}:${item.frontmatter.sourceMemoryId ?? ""}:${item.content}`;
|
|
540
|
+
if (seen.has(dedupeKey)) continue;
|
|
541
|
+
seen.add(dedupeKey);
|
|
542
|
+
out.push(item);
|
|
543
|
+
if (out.length >= cappedLimit) break;
|
|
544
|
+
}
|
|
545
|
+
if (!hasAnyCandidateAtOffset) break;
|
|
546
|
+
offset += 1;
|
|
547
|
+
}
|
|
548
|
+
return out;
|
|
549
|
+
}
|
|
550
|
+
function resolveRecentThreadMemoryPaths(options) {
|
|
551
|
+
const maxRecent = Math.max(0, options.maxRecent);
|
|
552
|
+
if (options.threadEpisodeIds.length === 0 || maxRecent === 0) return [];
|
|
553
|
+
const pathById = options.pathById ?? buildMemoryPathById(options.allMemsForGraph, options.storageDir);
|
|
554
|
+
if (pathById.size === 0) return [];
|
|
555
|
+
const pendingReviewIds = new Set(
|
|
556
|
+
(options.allMemsForGraph ?? []).filter((m) => m.frontmatter.status === "pending_review" && m.frontmatter.id).map((m) => m.frontmatter.id)
|
|
557
|
+
);
|
|
558
|
+
return options.threadEpisodeIds.filter((id) => id !== options.currentMemoryId).filter((id) => !pendingReviewIds.has(id)).slice(-maxRecent).map((id) => pathById.get(id)).filter((p) => typeof p === "string" && p.length > 0);
|
|
559
|
+
}
|
|
560
|
+
function buildMemoryPathById(allMemsForGraph, storageDir) {
|
|
561
|
+
const pathById = /* @__PURE__ */ new Map();
|
|
562
|
+
for (const mem of allMemsForGraph ?? []) {
|
|
563
|
+
const id = mem.frontmatter.id;
|
|
564
|
+
if (!id) continue;
|
|
565
|
+
pathById.set(id, path.relative(storageDir, mem.path));
|
|
566
|
+
}
|
|
567
|
+
return pathById;
|
|
568
|
+
}
|
|
569
|
+
function appendMemoryToGraphContext(options) {
|
|
570
|
+
if (!Array.isArray(options.allMemsForGraph)) return;
|
|
571
|
+
const nowIso = (/* @__PURE__ */ new Date()).toISOString();
|
|
572
|
+
options.allMemsForGraph.push({
|
|
573
|
+
path: path.join(options.storageDir, options.memoryRelPath),
|
|
574
|
+
content: options.content,
|
|
575
|
+
frontmatter: {
|
|
576
|
+
id: options.memoryId,
|
|
577
|
+
category: options.category,
|
|
578
|
+
created: nowIso,
|
|
579
|
+
updated: nowIso,
|
|
580
|
+
source: "extraction",
|
|
581
|
+
confidence: 0.8,
|
|
582
|
+
confidenceTier: "implied",
|
|
583
|
+
tags: [],
|
|
584
|
+
entityRef: options.entityRef,
|
|
585
|
+
status: "active"
|
|
586
|
+
}
|
|
587
|
+
});
|
|
588
|
+
}
|
|
589
|
+
function resolvePersistedMemoryRelativePath(options) {
|
|
590
|
+
const persisted = options.pathById.get(options.memoryId);
|
|
591
|
+
if (persisted) return persisted;
|
|
592
|
+
if (options.category === "correction") {
|
|
593
|
+
return path.join("corrections", `${options.memoryId}.md`);
|
|
594
|
+
}
|
|
595
|
+
const subtree = categoryDirName(options.category);
|
|
596
|
+
const idParts = options.memoryId.split("-");
|
|
597
|
+
const maybeTimestamp = Number(idParts[1]);
|
|
598
|
+
if (Number.isFinite(maybeTimestamp) && maybeTimestamp > 0) {
|
|
599
|
+
const day = new Date(maybeTimestamp).toISOString().slice(0, 10);
|
|
600
|
+
return path.join(subtree, day, `${options.memoryId}.md`);
|
|
601
|
+
}
|
|
602
|
+
return path.join(subtree, `${options.memoryId}.md`);
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
// src/orchestration/extraction-run-helpers.ts
|
|
606
|
+
function combineExtractionAbortSignals(primary, deadlineController) {
|
|
607
|
+
if (!deadlineController) return primary;
|
|
608
|
+
return primary ? AbortSignal.any([primary, deadlineController.signal]) : deadlineController.signal;
|
|
609
|
+
}
|
|
610
|
+
function isRecord(value) {
|
|
611
|
+
return typeof value === "object" && value !== null;
|
|
612
|
+
}
|
|
613
|
+
function deriveTopicsFromExtraction(result) {
|
|
614
|
+
if (!isRecord(result)) return [];
|
|
615
|
+
const topics = /* @__PURE__ */ new Set();
|
|
616
|
+
const facts = Array.isArray(result.facts) ? result.facts : [];
|
|
617
|
+
for (const rawFact of facts) {
|
|
618
|
+
if (!isRecord(rawFact)) continue;
|
|
619
|
+
const tags = Array.isArray(rawFact.tags) ? rawFact.tags : [];
|
|
620
|
+
for (const tag of tags) {
|
|
621
|
+
if (typeof tag === "string" && tag.length >= 2) topics.add(tag.toLowerCase());
|
|
622
|
+
}
|
|
623
|
+
if (typeof rawFact.entityRef === "string" && rawFact.entityRef.length >= 2) {
|
|
624
|
+
topics.add(rawFact.entityRef.toLowerCase());
|
|
625
|
+
}
|
|
626
|
+
if (typeof rawFact.category === "string" && rawFact.category.length > 0) {
|
|
627
|
+
topics.add(rawFact.category);
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
const entities = Array.isArray(result.entities) ? result.entities : [];
|
|
631
|
+
for (const rawEntity of entities) {
|
|
632
|
+
if (!isRecord(rawEntity)) continue;
|
|
633
|
+
if (typeof rawEntity.name === "string" && rawEntity.name.length >= 2) {
|
|
634
|
+
topics.add(rawEntity.name.toLowerCase());
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
return [...topics].slice(0, 16);
|
|
638
|
+
}
|
|
639
|
+
var EXTRACTION_RETRY_STATE_MAX_ENTRIES = 500;
|
|
640
|
+
function computeExtractionRetryNextEligibleMs(attempts, scheduleMs, maxBackoffMs, jitterRatio, now, rng = Math.random) {
|
|
641
|
+
if (!Number.isFinite(attempts) || !Number.isInteger(attempts) || attempts < 1) {
|
|
642
|
+
throw new RangeError("attempts must be a finite integer >= 1");
|
|
643
|
+
}
|
|
644
|
+
if (!Number.isFinite(maxBackoffMs) || maxBackoffMs < 0) {
|
|
645
|
+
throw new RangeError("maxBackoffMs must be a finite number >= 0");
|
|
646
|
+
}
|
|
647
|
+
if (!Number.isFinite(jitterRatio) || jitterRatio < 0 || jitterRatio > 1) {
|
|
648
|
+
throw new RangeError("jitterRatio must be a finite number in [0, 1]");
|
|
649
|
+
}
|
|
650
|
+
const safeNow = Number.isFinite(now) ? now : 0;
|
|
651
|
+
const cap = maxBackoffMs;
|
|
652
|
+
if (!Array.isArray(scheduleMs) || scheduleMs.length === 0) {
|
|
653
|
+
return safeNow + cap;
|
|
654
|
+
}
|
|
655
|
+
const idx = Math.min(attempts - 1, scheduleMs.length - 1);
|
|
656
|
+
const step = scheduleMs[idx];
|
|
657
|
+
const base = Math.min(typeof step === "number" && Number.isFinite(step) && step > 0 ? step : cap, cap);
|
|
658
|
+
const randomValue = rng();
|
|
659
|
+
if (!Number.isFinite(randomValue) || randomValue < 0 || randomValue > 1) {
|
|
660
|
+
throw new RangeError("rng() must return a finite number in [0, 1]");
|
|
661
|
+
}
|
|
662
|
+
const jitter = 1 + (randomValue * 2 - 1) * jitterRatio;
|
|
663
|
+
return safeNow + Math.max(0, Math.round(base * jitter));
|
|
664
|
+
}
|
|
665
|
+
function capExtractionRetryStateEntries(entries, maxEntries) {
|
|
666
|
+
if (!Number.isFinite(maxEntries) || !Number.isInteger(maxEntries) || maxEntries <= 0) return [];
|
|
667
|
+
const sorted = [...entries].sort((a, b) => a.firstFailedAt.localeCompare(b.firstFailedAt));
|
|
668
|
+
return sorted.slice(-maxEntries);
|
|
669
|
+
}
|
|
670
|
+
function deriveSourceConnector(turns) {
|
|
671
|
+
if (turns.length === 0) return void 0;
|
|
672
|
+
const connectors = /* @__PURE__ */ new Set();
|
|
673
|
+
let untaggedCount = 0;
|
|
674
|
+
for (const turn of turns) {
|
|
675
|
+
if (typeof turn.sourceConnector === "string" && turn.sourceConnector.length > 0) {
|
|
676
|
+
connectors.add(turn.sourceConnector);
|
|
677
|
+
} else {
|
|
678
|
+
untaggedCount += 1;
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
if (untaggedCount > 0) return void 0;
|
|
682
|
+
return connectors.size === 1 ? [...connectors][0] : void 0;
|
|
683
|
+
}
|
|
684
|
+
async function runExtractionPostPersistBestEffort(runDeadlineAware, stage, operation, options) {
|
|
685
|
+
try {
|
|
686
|
+
await runDeadlineAware(operation, stage, false, {
|
|
687
|
+
ignoreAbort: options?.ignoreAbort,
|
|
688
|
+
ignoreDeadline: options?.ignoreDeadline
|
|
689
|
+
});
|
|
690
|
+
} catch (error) {
|
|
691
|
+
if (options?.propagateErrors) throw error;
|
|
692
|
+
log.warn(`runExtraction: ${stage} failed after persistence (non-fatal)`, error);
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
async function runExtractionDeferRetention(runPostPersistBestEffort, options) {
|
|
696
|
+
await runPostPersistBestEffort("during_defer_retention", async () => {
|
|
697
|
+
if (options.clearBufferAfterExtraction && !options.extractionJudgeShadow) {
|
|
698
|
+
const deferredCount = options.getDeferredCount();
|
|
699
|
+
if (deferredCount > 0 && options.normalizedTurns.length > 0) {
|
|
700
|
+
await options.retainDeferredTurns(options.bufferKey, options.normalizedTurns, 10);
|
|
701
|
+
} else {
|
|
702
|
+
await options.retainDeferredTurns(options.bufferKey, [], 0);
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
});
|
|
706
|
+
}
|
|
707
|
+
async function runExtractionPostPersist(options) {
|
|
708
|
+
let metadataFailed = false;
|
|
709
|
+
let deadlineError;
|
|
710
|
+
const recordFailure = (stage, error) => {
|
|
711
|
+
metadataFailed = true;
|
|
712
|
+
if (options.isDeadlineError(error) && deadlineError === void 0) {
|
|
713
|
+
deadlineError = error;
|
|
714
|
+
}
|
|
715
|
+
log.warn(`runExtraction: ${stage} failed after durable persistence; continuing with buffer clear`, error);
|
|
716
|
+
};
|
|
717
|
+
let meta = options.meta;
|
|
718
|
+
try {
|
|
719
|
+
meta ??= await options.runDeadlineAware(
|
|
720
|
+
() => options.storage.loadMeta(),
|
|
721
|
+
"during_post_persist_load_meta",
|
|
722
|
+
false
|
|
723
|
+
);
|
|
724
|
+
} catch (error) {
|
|
725
|
+
recordFailure("metadata load", error);
|
|
726
|
+
}
|
|
727
|
+
if (meta && options.extractionFingerprint && options.shouldPersistProcessedFingerprint) {
|
|
728
|
+
try {
|
|
729
|
+
await options.runDeadlineAware(
|
|
730
|
+
() => options.recordProcessedExtractionFingerprint(options.storage, options.extractionFingerprint, meta),
|
|
731
|
+
"during_post_persist_fingerprint",
|
|
732
|
+
false
|
|
733
|
+
);
|
|
734
|
+
} catch (error) {
|
|
735
|
+
recordFailure("processed fingerprint", error);
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
if (meta) {
|
|
739
|
+
meta.extractionCount += 1;
|
|
740
|
+
meta.lastExtractionAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
741
|
+
meta.totalMemories += Array.isArray(options.result.facts) ? options.result.facts.length : 0;
|
|
742
|
+
meta.totalEntities += Array.isArray(options.result.entities) ? options.result.entities.length : 0;
|
|
743
|
+
try {
|
|
744
|
+
await options.runDeadlineAware(
|
|
745
|
+
() => options.storage.saveMeta(meta),
|
|
746
|
+
"during_post_persist_meta_save",
|
|
747
|
+
false
|
|
748
|
+
);
|
|
749
|
+
} catch (error) {
|
|
750
|
+
recordFailure("metadata save", error);
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
const runBestEffort = options.runPostPersistBestEffort;
|
|
754
|
+
await runExtractionDeferRetention(runBestEffort, {
|
|
755
|
+
clearBufferAfterExtraction: options.clearBufferAfterExtraction,
|
|
756
|
+
extractionJudgeShadow: options.extractionJudgeShadow,
|
|
757
|
+
getDeferredCount: options.getDeferredCount,
|
|
758
|
+
normalizedTurns: options.normalizedTurns,
|
|
759
|
+
bufferKey: options.bufferKey,
|
|
760
|
+
retainDeferredTurns: options.retainDeferredTurns
|
|
761
|
+
});
|
|
762
|
+
await runBestEffort(
|
|
763
|
+
"during_buffer_clear",
|
|
764
|
+
() => options.clearBuffer({ ignoreAbort: true }),
|
|
765
|
+
{
|
|
766
|
+
ignoreAbort: true,
|
|
767
|
+
ignoreDeadline: options.failOnExtractionFailure,
|
|
768
|
+
propagateErrors: options.failOnExtractionFailure
|
|
769
|
+
}
|
|
770
|
+
);
|
|
771
|
+
if (options.failOnExtractionFailure && typeof options.deadlineMs === "number" && Date.now() >= options.deadlineMs) {
|
|
772
|
+
throw options.createDeadlineError("during_buffer_clear");
|
|
773
|
+
}
|
|
774
|
+
if (deadlineError) throw deadlineError;
|
|
775
|
+
return { meta, metadataFailed };
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
// src/orchestration/extraction-run.ts
|
|
779
|
+
import { createHash as createHash2 } from "crypto";
|
|
780
|
+
var ExtractionDeadlineError = class extends Error {
|
|
781
|
+
stage;
|
|
782
|
+
constructor(stage) {
|
|
783
|
+
super(`replay extraction deadline exceeded (${stage})`);
|
|
784
|
+
this.name = "ExtractionDeadlineError";
|
|
785
|
+
this.stage = stage;
|
|
786
|
+
}
|
|
787
|
+
};
|
|
788
|
+
var ExtractionRunCoordinator = class {
|
|
789
|
+
constructor(deps) {
|
|
790
|
+
this.deps = deps;
|
|
791
|
+
}
|
|
792
|
+
deps;
|
|
793
|
+
recentExtractionFingerprints = /* @__PURE__ */ new Map();
|
|
794
|
+
// Per-fingerprint extraction retry/backoff state (extraction hot-loop
|
|
795
|
+
// hardening). Outer key = namespace, inner key = fingerprint. Hydrated lazily
|
|
796
|
+
// from namespace-scoped meta so the hot path is a Map lookup; persisted back
|
|
797
|
+
// to meta on change (restart-safe, cross-process coherent via the shared
|
|
798
|
+
// meta.json). The breaker below is intentionally per-process in-memory
|
|
799
|
+
// (mirrors local-llm.cooldownUntilMs); a restart clears it but the persisted
|
|
800
|
+
// backoff still throttles, so a restart can't resurrect the hot loop.
|
|
801
|
+
extractionRetryState = /* @__PURE__ */ new Map();
|
|
802
|
+
hydratedRetryNamespaces = /* @__PURE__ */ new Set();
|
|
803
|
+
providerBreaker = { consecutiveFailures: 0, openUntilMs: 0, state: "closed", lastReason: "" };
|
|
804
|
+
get config() {
|
|
805
|
+
return this.deps.config;
|
|
806
|
+
}
|
|
807
|
+
// -------------------------------------------------------------------------
|
|
808
|
+
// Source-valid-at helpers (moved from orchestrator.ts module scope)
|
|
809
|
+
// -------------------------------------------------------------------------
|
|
810
|
+
latestSourceValidAtFromTurns(turns) {
|
|
811
|
+
let latestMs = null;
|
|
812
|
+
for (const turn of turns) {
|
|
813
|
+
if (turn.extractionContextOnly === true) continue;
|
|
814
|
+
if (typeof turn.sourceValidAt !== "string") continue;
|
|
815
|
+
const parsed = parseFlexibleIsoTimestamp(turn.sourceValidAt.trim());
|
|
816
|
+
if (parsed === null) continue;
|
|
817
|
+
if (latestMs === null || parsed > latestMs) {
|
|
818
|
+
latestMs = parsed;
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
return latestMs === null ? void 0 : new Date(latestMs).toISOString();
|
|
822
|
+
}
|
|
823
|
+
// -------------------------------------------------------------------------
|
|
824
|
+
// Extraction dedupe fingerprint helpers
|
|
825
|
+
// -------------------------------------------------------------------------
|
|
826
|
+
normalizeExtractionFingerprintTurns(turns) {
|
|
827
|
+
if (!Array.isArray(turns) || turns.length === 0) return [];
|
|
828
|
+
return turns.filter((turn) => turn.role === "user" || turn.role === "assistant").map((turn) => {
|
|
829
|
+
if (typeof turn.turnFingerprint === "string" && turn.turnFingerprint.length > 0) {
|
|
830
|
+
return `fp:${turn.turnFingerprint}`;
|
|
831
|
+
}
|
|
832
|
+
return `${turn.role}:${(turn.content ?? "").replace(/\s+/g, " ").trim().slice(0, this.config.extractionMaxTurnChars)}`;
|
|
833
|
+
}).filter((value) => value.length > 0);
|
|
834
|
+
}
|
|
835
|
+
buildExtractionFingerprint(turns, bufferKey) {
|
|
836
|
+
const normalized = this.normalizeExtractionFingerprintTurns(turns).join("\n");
|
|
837
|
+
if (!normalized) return null;
|
|
838
|
+
return createHash2("sha256").update(`${bufferKey}
|
|
839
|
+
${normalized}`).digest("hex");
|
|
840
|
+
}
|
|
841
|
+
shouldQueueExtraction(turns, options = {}) {
|
|
842
|
+
const lifecycleCaps = resolveMemoryLifecycleCapabilities(this.config);
|
|
843
|
+
if (!lifecycleCaps.extractionDedupe) return true;
|
|
844
|
+
if (!Array.isArray(turns) || turns.length === 0) return false;
|
|
845
|
+
const bufferKey = options.bufferKey ?? turns[0]?.sessionKey ?? "default";
|
|
846
|
+
const fingerprint = this.buildExtractionFingerprint(turns, bufferKey);
|
|
847
|
+
if (!fingerprint) return false;
|
|
848
|
+
const now = Date.now();
|
|
849
|
+
const seenAt = this.recentExtractionFingerprints.get(fingerprint);
|
|
850
|
+
if (seenAt && now - seenAt < this.config.extractionDedupeWindowMs) {
|
|
851
|
+
log.debug("extraction dedupe: skipped duplicate buffered turn set");
|
|
852
|
+
return false;
|
|
853
|
+
}
|
|
854
|
+
if (options.commit !== false) {
|
|
855
|
+
this.recentExtractionFingerprints.set(fingerprint, now);
|
|
856
|
+
}
|
|
857
|
+
if (options.commit !== false && this.recentExtractionFingerprints.size > 200) {
|
|
858
|
+
const entries = Array.from(this.recentExtractionFingerprints.entries()).sort((a, b) => a[1] - b[1]);
|
|
859
|
+
for (const [key] of entries.slice(0, entries.length - 200)) {
|
|
860
|
+
this.recentExtractionFingerprints.delete(key);
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
return true;
|
|
864
|
+
}
|
|
865
|
+
// -------------------------------------------------------------------------
|
|
866
|
+
// Processed-fingerprint recording
|
|
867
|
+
// -------------------------------------------------------------------------
|
|
868
|
+
async recordProcessedExtractionFingerprint(storage, fingerprint, preloadedMeta) {
|
|
869
|
+
const meta = preloadedMeta ?? await storage.loadMeta();
|
|
870
|
+
const observedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
871
|
+
const seen = new Map(
|
|
872
|
+
(meta.processedExtractionFingerprints ?? []).map((entry) => [entry.fingerprint, entry.observedAt])
|
|
873
|
+
);
|
|
874
|
+
seen.set(fingerprint, observedAt);
|
|
875
|
+
meta.processedExtractionFingerprints = Array.from(seen.entries()).map(([value, at]) => ({ fingerprint: value, observedAt: at })).sort((left, right) => left.observedAt.localeCompare(right.observedAt)).slice(-500);
|
|
876
|
+
if (!preloadedMeta) {
|
|
877
|
+
await storage.saveMeta(meta);
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
// -------------------------------------------------------------------------
|
|
881
|
+
// Extraction retry/backoff + circuit breaker (extraction hot-loop hardening)
|
|
882
|
+
// -------------------------------------------------------------------------
|
|
883
|
+
/** Lazily hydrate the in-memory retry-state mirror for a namespace from meta.
|
|
884
|
+
* One-time per namespace per coordinator; the hot path is then a Map lookup.
|
|
885
|
+
* Cross-process coherence: a fresh coordinator hydrates from the shared
|
|
886
|
+
* meta.json, so a failure persisted by another process is honored here. */
|
|
887
|
+
hydrateRetryStateFromMeta(namespace, meta) {
|
|
888
|
+
if (this.hydratedRetryNamespaces.has(namespace)) return;
|
|
889
|
+
this.hydratedRetryNamespaces.add(namespace);
|
|
890
|
+
const nsMap = this.extractionRetryState.get(namespace) ?? /* @__PURE__ */ new Map();
|
|
891
|
+
for (const entry of meta.extractionRetryState ?? []) {
|
|
892
|
+
const nextEligibleAtMs = Date.parse(entry.nextEligibleAt);
|
|
893
|
+
const firstFailedAtMs = Date.parse(entry.firstFailedAt);
|
|
894
|
+
nsMap.set(entry.fingerprint, {
|
|
895
|
+
attempts: entry.attempts,
|
|
896
|
+
nextEligibleAtMs: Number.isFinite(nextEligibleAtMs) ? nextEligibleAtMs : 0,
|
|
897
|
+
firstFailedAtMs: Number.isFinite(firstFailedAtMs) ? firstFailedAtMs : Date.now(),
|
|
898
|
+
lastFailureClass: entry.lastFailureClass
|
|
899
|
+
});
|
|
900
|
+
}
|
|
901
|
+
this.extractionRetryState.set(namespace, nsMap);
|
|
902
|
+
}
|
|
903
|
+
logBreakerTransition(state) {
|
|
904
|
+
log.warn("extraction: provider circuit breaker transition", {
|
|
905
|
+
state,
|
|
906
|
+
consecutiveFailures: this.providerBreaker.consecutiveFailures,
|
|
907
|
+
lastReason: this.providerBreaker.lastReason,
|
|
908
|
+
openUntilMs: this.providerBreaker.openUntilMs
|
|
909
|
+
});
|
|
910
|
+
}
|
|
911
|
+
/** True while the breaker suppresses non-forced attempts. Flips an expired
|
|
912
|
+
* `open` breaker to `half_open`, which allows exactly one probe (the next
|
|
913
|
+
* non-forced attempt) because the extraction queue is a serial drain. */
|
|
914
|
+
isProviderBreakerOpen(now) {
|
|
915
|
+
if (now < this.providerBreaker.openUntilMs) return true;
|
|
916
|
+
if (this.providerBreaker.state === "open") {
|
|
917
|
+
this.providerBreaker.state = "half_open";
|
|
918
|
+
this.logBreakerTransition("half_open");
|
|
919
|
+
}
|
|
920
|
+
return false;
|
|
921
|
+
}
|
|
922
|
+
onProviderFailure(cls, now) {
|
|
923
|
+
this.providerBreaker.consecutiveFailures += 1;
|
|
924
|
+
const trip = cls === "auth_config" || // A failed half-open probe re-opens immediately (standard breaker
|
|
925
|
+
// semantics). Without this, an auth_config-opened breaker (which trips
|
|
926
|
+
// below the threshold) would get stuck half_open after a transient
|
|
927
|
+
// probe failure and stop suppressing (cursor review).
|
|
928
|
+
this.providerBreaker.state === "half_open" || this.providerBreaker.consecutiveFailures >= this.config.extractionBreakerFailureThreshold;
|
|
929
|
+
if (!trip) return;
|
|
930
|
+
const cooldown = cls === "auth_config" ? this.config.extractionBreakerAuthCooldownMs : this.config.extractionBreakerCooldownMs;
|
|
931
|
+
this.providerBreaker.openUntilMs = now + Math.max(0, cooldown);
|
|
932
|
+
this.providerBreaker.lastReason = cls;
|
|
933
|
+
if (this.providerBreaker.state !== "open") {
|
|
934
|
+
this.providerBreaker.state = "open";
|
|
935
|
+
this.logBreakerTransition("open");
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
resetProviderBreakerOnSuccess() {
|
|
939
|
+
const wasDisturbed = this.providerBreaker.state !== "closed" || this.providerBreaker.consecutiveFailures > 0 || this.providerBreaker.openUntilMs > 0;
|
|
940
|
+
if (!wasDisturbed) return;
|
|
941
|
+
const wasOpen = this.providerBreaker.state !== "closed";
|
|
942
|
+
this.providerBreaker.consecutiveFailures = 0;
|
|
943
|
+
this.providerBreaker.openUntilMs = 0;
|
|
944
|
+
this.providerBreaker.lastReason = "";
|
|
945
|
+
this.providerBreaker.state = "closed";
|
|
946
|
+
if (wasOpen) this.logBreakerTransition("closed");
|
|
947
|
+
}
|
|
948
|
+
/** Rebuild `meta.extractionRetryState` from the in-memory namespace mirror,
|
|
949
|
+
* bounded to the newest entries, and prune the mirror to match. */
|
|
950
|
+
persistRetryStateToMeta(namespace, meta) {
|
|
951
|
+
const nsMap = this.extractionRetryState.get(namespace);
|
|
952
|
+
const entries = nsMap ? Array.from(nsMap.entries()).map(([fingerprint, st]) => ({
|
|
953
|
+
fingerprint,
|
|
954
|
+
attempts: st.attempts,
|
|
955
|
+
nextEligibleAt: new Date(st.nextEligibleAtMs).toISOString(),
|
|
956
|
+
firstFailedAt: new Date(st.firstFailedAtMs).toISOString(),
|
|
957
|
+
lastFailureClass: st.lastFailureClass
|
|
958
|
+
})) : [];
|
|
959
|
+
const capped = capExtractionRetryStateEntries(entries, EXTRACTION_RETRY_STATE_MAX_ENTRIES);
|
|
960
|
+
meta.extractionRetryState = capped;
|
|
961
|
+
if (nsMap && capped.length < nsMap.size) {
|
|
962
|
+
const kept = new Set(capped.map((e) => e.fingerprint));
|
|
963
|
+
for (const key of Array.from(nsMap.keys())) {
|
|
964
|
+
if (!kept.has(key)) nsMap.delete(key);
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
/** Record an extraction failure into per-fingerprint backoff state + breaker,
|
|
969
|
+
* persisting to namespace meta. Fail-open: never throws upward. */
|
|
970
|
+
async recordExtractionFailure(storage, namespace, fingerprint, cls, meta, now) {
|
|
971
|
+
try {
|
|
972
|
+
this.hydrateRetryStateFromMeta(namespace, meta);
|
|
973
|
+
const nsMap = this.extractionRetryState.get(namespace) ?? /* @__PURE__ */ new Map();
|
|
974
|
+
const prev = nsMap.get(fingerprint);
|
|
975
|
+
const attempts = (prev?.attempts ?? 0) + 1;
|
|
976
|
+
const firstFailedAtMs = prev?.firstFailedAtMs ?? now;
|
|
977
|
+
let nextEligibleAtMs;
|
|
978
|
+
if (cls === "parse_empty" && attempts > this.config.extractionParseEmptyMaxAttempts) {
|
|
979
|
+
nextEligibleAtMs = now + Math.max(0, this.config.extractionRetryMaxBackoffMs);
|
|
980
|
+
} else {
|
|
981
|
+
nextEligibleAtMs = computeExtractionRetryNextEligibleMs(
|
|
982
|
+
attempts,
|
|
983
|
+
this.config.extractionRetryScheduleMs,
|
|
984
|
+
this.config.extractionRetryMaxBackoffMs,
|
|
985
|
+
this.config.extractionRetryJitterRatio,
|
|
986
|
+
now
|
|
987
|
+
);
|
|
988
|
+
}
|
|
989
|
+
nsMap.set(fingerprint, { attempts, nextEligibleAtMs, firstFailedAtMs, lastFailureClass: cls });
|
|
990
|
+
this.extractionRetryState.set(namespace, nsMap);
|
|
991
|
+
this.onProviderFailure(cls, now);
|
|
992
|
+
this.persistRetryStateToMeta(namespace, meta);
|
|
993
|
+
await storage.saveMeta(meta);
|
|
994
|
+
} catch (err) {
|
|
995
|
+
log.warn("runExtraction: failed to record extraction retry state (non-fatal)", err);
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
/** Delete a fingerprint's retry entry from the mirror + meta. Returns whether
|
|
999
|
+
* meta changed (so the caller can decide to persist). Does not touch the
|
|
1000
|
+
* breaker — callers pair this with `resetProviderBreakerOnSuccess`. */
|
|
1001
|
+
clearExtractionRetryEntry(namespace, fingerprint, meta) {
|
|
1002
|
+
const nsMap = this.extractionRetryState.get(namespace);
|
|
1003
|
+
if (!nsMap?.has(fingerprint)) return false;
|
|
1004
|
+
nsMap.delete(fingerprint);
|
|
1005
|
+
this.persistRetryStateToMeta(namespace, meta);
|
|
1006
|
+
return true;
|
|
1007
|
+
}
|
|
1008
|
+
/** Live in-process resilience snapshot (breaker + backoff population). */
|
|
1009
|
+
getExtractionResilienceStatus() {
|
|
1010
|
+
let backoffFingerprintCount = 0;
|
|
1011
|
+
for (const nsMap of this.extractionRetryState.values()) {
|
|
1012
|
+
backoffFingerprintCount += nsMap.size;
|
|
1013
|
+
}
|
|
1014
|
+
return {
|
|
1015
|
+
breaker: {
|
|
1016
|
+
state: this.providerBreaker.state,
|
|
1017
|
+
openUntilMs: this.providerBreaker.openUntilMs,
|
|
1018
|
+
consecutiveFailures: this.providerBreaker.consecutiveFailures,
|
|
1019
|
+
lastReason: this.providerBreaker.lastReason
|
|
1020
|
+
},
|
|
1021
|
+
backoffFingerprintCount
|
|
1022
|
+
};
|
|
1023
|
+
}
|
|
1024
|
+
// -------------------------------------------------------------------------
|
|
1025
|
+
// Main extraction pipeline
|
|
1026
|
+
// -------------------------------------------------------------------------
|
|
1027
|
+
async runExtraction(turns, options = {}) {
|
|
1028
|
+
log.debug(`running extraction on ${turns.length} turns`);
|
|
1029
|
+
const clearBufferAfterExtraction = options.clearBufferAfterExtraction ?? true;
|
|
1030
|
+
const clearMatchingTurns = options.clearMatchingTurns === true;
|
|
1031
|
+
const skipCharThreshold = options.skipCharThreshold ?? false;
|
|
1032
|
+
const skipUserTurnThreshold = options.skipUserTurnThreshold ?? false;
|
|
1033
|
+
const deadlineMs = typeof options.deadlineMs === "number" && Number.isFinite(options.deadlineMs) ? options.deadlineMs : void 0;
|
|
1034
|
+
const bufferKey = options.bufferKey ?? turns[0]?.sessionKey ?? "default";
|
|
1035
|
+
const throwIfDeadlineExceeded = (stage) => {
|
|
1036
|
+
if (typeof deadlineMs === "number" && Date.now() >= deadlineMs) {
|
|
1037
|
+
throw new ExtractionDeadlineError(stage);
|
|
1038
|
+
}
|
|
1039
|
+
};
|
|
1040
|
+
const throwIfAborted2 = (stage) => {
|
|
1041
|
+
throwIfRecallAborted(options.abortSignal, `extraction aborted (${stage})`);
|
|
1042
|
+
};
|
|
1043
|
+
const runPassiveCapture = async (captureTurns, captureOptions) => {
|
|
1044
|
+
const deadlineController = typeof deadlineMs === "number" && Number.isFinite(deadlineMs) ? new AbortController() : void 0;
|
|
1045
|
+
const captureSignal = combineExtractionAbortSignals(
|
|
1046
|
+
captureOptions.abortSignal ?? options.abortSignal,
|
|
1047
|
+
deadlineController
|
|
1048
|
+
);
|
|
1049
|
+
let deadlineTimer;
|
|
1050
|
+
if (deadlineController && typeof deadlineMs === "number") {
|
|
1051
|
+
const scheduleDeadlineAbort = () => {
|
|
1052
|
+
const remainingMs = deadlineMs - Date.now();
|
|
1053
|
+
if (remainingMs <= 0) {
|
|
1054
|
+
deadlineController.abort();
|
|
1055
|
+
return;
|
|
1056
|
+
}
|
|
1057
|
+
deadlineTimer = setTimeout(
|
|
1058
|
+
() => {
|
|
1059
|
+
if (Date.now() >= deadlineMs) {
|
|
1060
|
+
deadlineController.abort();
|
|
1061
|
+
} else {
|
|
1062
|
+
scheduleDeadlineAbort();
|
|
1063
|
+
}
|
|
1064
|
+
},
|
|
1065
|
+
Math.min(remainingMs, 2147483647)
|
|
1066
|
+
);
|
|
1067
|
+
};
|
|
1068
|
+
scheduleDeadlineAbort();
|
|
1069
|
+
}
|
|
1070
|
+
try {
|
|
1071
|
+
await raceRecallAbort(
|
|
1072
|
+
this.deps.maybeCapturePassiveCorrections(captureTurns, {
|
|
1073
|
+
...captureOptions,
|
|
1074
|
+
...captureSignal ? { abortSignal: captureSignal } : {}
|
|
1075
|
+
}),
|
|
1076
|
+
captureSignal,
|
|
1077
|
+
"extraction aborted (during_passive_capture)"
|
|
1078
|
+
);
|
|
1079
|
+
} catch (error) {
|
|
1080
|
+
if (typeof deadlineMs === "number" && Date.now() >= deadlineMs) {
|
|
1081
|
+
throw new ExtractionDeadlineError("during_passive_capture");
|
|
1082
|
+
}
|
|
1083
|
+
throw error;
|
|
1084
|
+
} finally {
|
|
1085
|
+
if (deadlineTimer) clearTimeout(deadlineTimer);
|
|
1086
|
+
}
|
|
1087
|
+
};
|
|
1088
|
+
const clearBuffer = async (options2) => {
|
|
1089
|
+
if (options2?.ignoreAbort !== true) {
|
|
1090
|
+
throwIfDeadlineExceeded("before_clear_buffer");
|
|
1091
|
+
throwIfAborted2("before_clear_buffer");
|
|
1092
|
+
}
|
|
1093
|
+
if (clearBufferAfterExtraction) {
|
|
1094
|
+
await this.deps.getBuffer().clearAfterExtraction(bufferKey, turns, clearMatchingTurns ? { allowNonPrefix: true } : void 0);
|
|
1095
|
+
}
|
|
1096
|
+
};
|
|
1097
|
+
const sessionKey = turns[0]?.sessionKey ?? "";
|
|
1098
|
+
if (sessionKey.includes(":cron:")) {
|
|
1099
|
+
log.debug(`skipping extraction for cron session: ${sessionKey}`);
|
|
1100
|
+
await clearBuffer();
|
|
1101
|
+
return {
|
|
1102
|
+
status: "skipped",
|
|
1103
|
+
reason: "cron_session",
|
|
1104
|
+
persistedCount: 0,
|
|
1105
|
+
durableOutputCount: 0
|
|
1106
|
+
};
|
|
1107
|
+
}
|
|
1108
|
+
const normalizedTurns = turns.filter((t) => (t.role === "user" || t.role === "assistant") && typeof t.content === "string").map((t) => ({
|
|
1109
|
+
...t,
|
|
1110
|
+
content: t.content.trim().slice(0, this.config.extractionMaxTurnChars)
|
|
1111
|
+
})).filter((t) => t.content.length > 0);
|
|
1112
|
+
const targetTurns = normalizedTurns.filter((turn) => turn.extractionContextOnly !== true);
|
|
1113
|
+
if (targetTurns.length === 0) {
|
|
1114
|
+
log.debug("skipping extraction: no non-context turns after normalization");
|
|
1115
|
+
if (normalizedTurns.length > 0) {
|
|
1116
|
+
throwIfDeadlineExceeded("before_context_only_capture");
|
|
1117
|
+
const capturePrincipal = typeof options.principalOverride === "string" && options.principalOverride.length > 0 ? options.principalOverride : resolvePrincipal(sessionKey, this.config);
|
|
1118
|
+
const captureNamespace = typeof options.writeNamespaceOverride === "string" && options.writeNamespaceOverride.length > 0 ? options.writeNamespaceOverride : this.deps.resolveSelfNamespace(sessionKey);
|
|
1119
|
+
await runPassiveCapture(normalizedTurns, {
|
|
1120
|
+
sessionKey,
|
|
1121
|
+
principal: capturePrincipal,
|
|
1122
|
+
namespace: captureNamespace,
|
|
1123
|
+
bufferKey,
|
|
1124
|
+
isLiveSession: clearBufferAfterExtraction
|
|
1125
|
+
});
|
|
1126
|
+
throwIfDeadlineExceeded("before_context_only_clear");
|
|
1127
|
+
}
|
|
1128
|
+
await clearBuffer();
|
|
1129
|
+
return { status: "skipped", reason: "empty_normalized_turns", persistedCount: 0, durableOutputCount: 0 };
|
|
1130
|
+
}
|
|
1131
|
+
const sourceValidAt = this.latestSourceValidAtFromTurns(targetTurns);
|
|
1132
|
+
throwIfDeadlineExceeded("before_extract");
|
|
1133
|
+
throwIfAborted2("before_extract");
|
|
1134
|
+
const userTurns = targetTurns.filter((t) => t.role === "user");
|
|
1135
|
+
const totalChars = targetTurns.reduce((sum, t) => sum + t.content.length, 0);
|
|
1136
|
+
const belowCharThreshold = totalChars < this.config.extractionMinChars;
|
|
1137
|
+
const belowUserTurnThreshold = !skipUserTurnThreshold && userTurns.length < this.config.extractionMinUserTurns;
|
|
1138
|
+
if (!skipCharThreshold && belowCharThreshold || belowUserTurnThreshold) {
|
|
1139
|
+
log.debug(`skipping extraction: below threshold (totalChars=${totalChars}, userTurns=${userTurns.length})`);
|
|
1140
|
+
{
|
|
1141
|
+
const capturePrincipal = typeof options.principalOverride === "string" && options.principalOverride.length > 0 ? options.principalOverride : resolvePrincipal(sessionKey, this.config);
|
|
1142
|
+
const captureWO = typeof options.writeNamespaceOverride === "string" && options.writeNamespaceOverride.length > 0 ? options.writeNamespaceOverride : void 0;
|
|
1143
|
+
const captureCodingCtx = sessionKey ? this.deps.getCodingContextForSession(sessionKey) : null;
|
|
1144
|
+
const captureCodingOv = resolveCodingNamespaceOverlay(
|
|
1145
|
+
captureCodingCtx,
|
|
1146
|
+
this.config.codingMode,
|
|
1147
|
+
this.config.defaultNamespace
|
|
1148
|
+
);
|
|
1149
|
+
const captureScopePlan = resolveScopeProfilePlan({
|
|
1150
|
+
config: this.config,
|
|
1151
|
+
principal: capturePrincipal,
|
|
1152
|
+
codingContext: captureCodingCtx,
|
|
1153
|
+
codingOverlay: captureCodingOv
|
|
1154
|
+
});
|
|
1155
|
+
const captureNamespace = captureWO ?? captureScopePlan?.writeNamespace ?? this.deps.resolveSelfNamespace(sessionKey);
|
|
1156
|
+
await runPassiveCapture(normalizedTurns, {
|
|
1157
|
+
sessionKey,
|
|
1158
|
+
principal: capturePrincipal,
|
|
1159
|
+
namespace: captureNamespace,
|
|
1160
|
+
bufferKey,
|
|
1161
|
+
isLiveSession: clearBufferAfterExtraction
|
|
1162
|
+
});
|
|
1163
|
+
}
|
|
1164
|
+
await clearBuffer();
|
|
1165
|
+
return {
|
|
1166
|
+
status: "skipped",
|
|
1167
|
+
reason: "below_threshold",
|
|
1168
|
+
persistedCount: 0,
|
|
1169
|
+
durableOutputCount: 0
|
|
1170
|
+
};
|
|
1171
|
+
}
|
|
1172
|
+
const principal = typeof options.principalOverride === "string" && options.principalOverride.length > 0 ? options.principalOverride : resolvePrincipal(sessionKey, this.config);
|
|
1173
|
+
const explicitWriteNamespace = typeof options.writeNamespaceOverride === "string" && options.writeNamespaceOverride.length > 0 ? options.writeNamespaceOverride : void 0;
|
|
1174
|
+
const codingContextForWrite = sessionKey ? this.deps.getCodingContextForSession(sessionKey) : null;
|
|
1175
|
+
const codingOverlayForWrite = resolveCodingNamespaceOverlay(
|
|
1176
|
+
codingContextForWrite,
|
|
1177
|
+
this.config.codingMode,
|
|
1178
|
+
this.config.defaultNamespace
|
|
1179
|
+
);
|
|
1180
|
+
const scopeProfileGatePlan = options.scopeProfileWritePlan !== void 0 ? options.scopeProfileWritePlan : resolveScopeProfilePlan({
|
|
1181
|
+
config: this.config,
|
|
1182
|
+
principal,
|
|
1183
|
+
codingContext: codingContextForWrite,
|
|
1184
|
+
codingOverlay: codingOverlayForWrite
|
|
1185
|
+
});
|
|
1186
|
+
const scopeProfileWritePlan = explicitWriteNamespace && options.scopeProfileWritePlan === void 0 ? null : scopeProfileGatePlan;
|
|
1187
|
+
if (scopeProfileWritePlan) {
|
|
1188
|
+
const selectedLayer = scopeProfileWritePlan.layers.find((layer) => layer.id === scopeProfileWritePlan.writeLayer);
|
|
1189
|
+
const writeNamespaceReadable = scopeProfileWritePlan.readNamespaces.includes(
|
|
1190
|
+
scopeProfileWritePlan.writeNamespace
|
|
1191
|
+
);
|
|
1192
|
+
if (!selectedLayer?.writable || !writeNamespaceReadable) {
|
|
1193
|
+
log.warn(
|
|
1194
|
+
`runExtraction: skipping scope profile ${scopeProfileWritePlan.profileId} because write layer ${scopeProfileWritePlan.writeLayer} is not writable inside the profile read stack`
|
|
1195
|
+
);
|
|
1196
|
+
await clearBuffer();
|
|
1197
|
+
return {
|
|
1198
|
+
status: "skipped",
|
|
1199
|
+
reason: "scope_profile_no_writable_layer",
|
|
1200
|
+
persistedCount: 0,
|
|
1201
|
+
durableOutputCount: 0
|
|
1202
|
+
};
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
|
+
const selfNamespace = explicitWriteNamespace ?? scopeProfileWritePlan?.writeNamespace ?? this.deps.applyCodingNamespaceOverlay(sessionKey, defaultNamespaceForPrincipal(principal, this.config));
|
|
1206
|
+
const extractionDeadlineController = typeof deadlineMs === "number" && Number.isFinite(deadlineMs) ? new AbortController() : void 0;
|
|
1207
|
+
const extractionAbortSignal = extractionDeadlineController ? options.abortSignal ? AbortSignal.any([options.abortSignal, extractionDeadlineController.signal]) : extractionDeadlineController.signal : options.abortSignal;
|
|
1208
|
+
let extractionDeadlineTimer;
|
|
1209
|
+
const clearExtractionDeadlineTimer = () => {
|
|
1210
|
+
if (extractionDeadlineTimer) {
|
|
1211
|
+
clearTimeout(extractionDeadlineTimer);
|
|
1212
|
+
extractionDeadlineTimer = void 0;
|
|
1213
|
+
}
|
|
1214
|
+
};
|
|
1215
|
+
const runDeadlineAware = async (operation, phase, clearTimerOnError = true, options2 = {}) => {
|
|
1216
|
+
const operationSignal = options2.ignoreAbort ? void 0 : extractionAbortSignal;
|
|
1217
|
+
try {
|
|
1218
|
+
if (!options2.ignoreDeadline && typeof deadlineMs === "number" && Date.now() >= deadlineMs) {
|
|
1219
|
+
throwIfDeadlineExceeded(phase);
|
|
1220
|
+
}
|
|
1221
|
+
if (operationSignal?.aborted) {
|
|
1222
|
+
throwIfAborted2(phase);
|
|
1223
|
+
}
|
|
1224
|
+
return await raceRecallAbort(
|
|
1225
|
+
operation(),
|
|
1226
|
+
operationSignal,
|
|
1227
|
+
`extraction aborted (${phase})`
|
|
1228
|
+
);
|
|
1229
|
+
} catch (error) {
|
|
1230
|
+
if (clearTimerOnError) clearExtractionDeadlineTimer();
|
|
1231
|
+
if (!options2.ignoreDeadline && typeof deadlineMs === "number" && Date.now() >= deadlineMs) {
|
|
1232
|
+
throw new ExtractionDeadlineError(phase);
|
|
1233
|
+
}
|
|
1234
|
+
throw error;
|
|
1235
|
+
}
|
|
1236
|
+
};
|
|
1237
|
+
if (extractionDeadlineController && typeof deadlineMs === "number") {
|
|
1238
|
+
const deadline = deadlineMs;
|
|
1239
|
+
const maxTimerDelayMs = 2147483647;
|
|
1240
|
+
const scheduleDeadlineAbort = () => {
|
|
1241
|
+
const remainingMs = deadline - Date.now();
|
|
1242
|
+
if (remainingMs <= 0) {
|
|
1243
|
+
extractionDeadlineController.abort();
|
|
1244
|
+
return;
|
|
1245
|
+
}
|
|
1246
|
+
extractionDeadlineTimer = setTimeout(() => {
|
|
1247
|
+
if (Date.now() >= deadline) {
|
|
1248
|
+
extractionDeadlineController.abort();
|
|
1249
|
+
} else {
|
|
1250
|
+
scheduleDeadlineAbort();
|
|
1251
|
+
}
|
|
1252
|
+
}, Math.min(remainingMs, maxTimerDelayMs));
|
|
1253
|
+
};
|
|
1254
|
+
scheduleDeadlineAbort();
|
|
1255
|
+
}
|
|
1256
|
+
const storage = await runDeadlineAware(
|
|
1257
|
+
() => this.deps.getStorageRouter().storageFor(selfNamespace),
|
|
1258
|
+
"during_storage"
|
|
1259
|
+
);
|
|
1260
|
+
const shouldPersistProcessedFingerprint = targetTurns.some((turn) => turn.persistProcessedFingerprint === true);
|
|
1261
|
+
const extractionFingerprint = this.buildExtractionFingerprint(targetTurns, bufferKey);
|
|
1262
|
+
let meta = extractionFingerprint && shouldPersistProcessedFingerprint ? await runDeadlineAware(() => storage.loadMeta(), "during_load_meta") : null;
|
|
1263
|
+
if (extractionFingerprint && shouldPersistProcessedFingerprint && (meta?.processedExtractionFingerprints ?? []).some((entry) => entry.fingerprint === extractionFingerprint)) {
|
|
1264
|
+
await runDeadlineAware(() => clearBuffer(), "during_clear_buffer");
|
|
1265
|
+
clearExtractionDeadlineTimer();
|
|
1266
|
+
return {
|
|
1267
|
+
status: "skipped",
|
|
1268
|
+
reason: "processed_fingerprint",
|
|
1269
|
+
persistedCount: 0,
|
|
1270
|
+
durableOutputCount: 0
|
|
1271
|
+
};
|
|
1272
|
+
}
|
|
1273
|
+
const forcedExtractionAttempt = options.failOnExtractionFailure === true || options.forceExtractionAttempt === true;
|
|
1274
|
+
const extractionRetryEnabled = resolveMemoryLifecycleCapabilities(this.config).extractionRetry;
|
|
1275
|
+
if (extractionFingerprint && !forcedExtractionAttempt && extractionRetryEnabled) {
|
|
1276
|
+
try {
|
|
1277
|
+
meta ??= await runDeadlineAware(() => storage.loadMeta(), "during_retry_gate");
|
|
1278
|
+
this.hydrateRetryStateFromMeta(selfNamespace, meta);
|
|
1279
|
+
const nowMs = Date.now();
|
|
1280
|
+
let suppressReason = null;
|
|
1281
|
+
if (this.isProviderBreakerOpen(nowMs)) {
|
|
1282
|
+
suppressReason = "provider_circuit_open";
|
|
1283
|
+
} else {
|
|
1284
|
+
const st = this.extractionRetryState.get(selfNamespace)?.get(extractionFingerprint);
|
|
1285
|
+
if (st && nowMs < st.nextEligibleAtMs) suppressReason = "extraction_backoff";
|
|
1286
|
+
}
|
|
1287
|
+
if (suppressReason) {
|
|
1288
|
+
try {
|
|
1289
|
+
await runDeadlineAware(
|
|
1290
|
+
() => runPassiveCapture(normalizedTurns, {
|
|
1291
|
+
sessionKey,
|
|
1292
|
+
principal,
|
|
1293
|
+
namespace: selfNamespace,
|
|
1294
|
+
bufferKey,
|
|
1295
|
+
isLiveSession: clearBufferAfterExtraction
|
|
1296
|
+
}),
|
|
1297
|
+
"during_passive_capture"
|
|
1298
|
+
);
|
|
1299
|
+
} catch (captureErr) {
|
|
1300
|
+
if (captureErr instanceof ExtractionDeadlineError) throw captureErr;
|
|
1301
|
+
log.warn("runExtraction: passive correction capture failed on suppressed attempt (non-fatal)", captureErr);
|
|
1302
|
+
}
|
|
1303
|
+
clearExtractionDeadlineTimer();
|
|
1304
|
+
return { status: "skipped", reason: suppressReason, persistedCount: 0, durableOutputCount: 0 };
|
|
1305
|
+
}
|
|
1306
|
+
} catch (err) {
|
|
1307
|
+
if (err instanceof ExtractionDeadlineError) throw err;
|
|
1308
|
+
log.warn("runExtraction: extraction retry gate check failed; proceeding (fail-open)", err);
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
const existingEntities = await runDeadlineAware(
|
|
1312
|
+
() => storage.listEntityNames(),
|
|
1313
|
+
"during_list_entity_names"
|
|
1314
|
+
);
|
|
1315
|
+
let result;
|
|
1316
|
+
try {
|
|
1317
|
+
result = await raceRecallAbort(
|
|
1318
|
+
this.deps.getExtraction().extract(normalizedTurns, existingEntities, extractionAbortSignal),
|
|
1319
|
+
extractionAbortSignal,
|
|
1320
|
+
"extraction aborted (during_extract)"
|
|
1321
|
+
);
|
|
1322
|
+
} catch (error) {
|
|
1323
|
+
clearExtractionDeadlineTimer();
|
|
1324
|
+
if (typeof deadlineMs === "number" && Date.now() >= deadlineMs) {
|
|
1325
|
+
throw new ExtractionDeadlineError("during_extract");
|
|
1326
|
+
}
|
|
1327
|
+
throw error;
|
|
1328
|
+
}
|
|
1329
|
+
try {
|
|
1330
|
+
throwIfDeadlineExceeded("before_persist");
|
|
1331
|
+
throwIfAborted2("before_persist");
|
|
1332
|
+
if (!result) {
|
|
1333
|
+
log.warn("runExtraction: extraction returned null/undefined");
|
|
1334
|
+
if (options.failOnExtractionFailure) {
|
|
1335
|
+
throw new Error("extraction failed: invalid_extraction_result");
|
|
1336
|
+
}
|
|
1337
|
+
await clearBuffer();
|
|
1338
|
+
return {
|
|
1339
|
+
status: "skipped",
|
|
1340
|
+
reason: "invalid_extraction_result",
|
|
1341
|
+
persistedCount: 0,
|
|
1342
|
+
durableOutputCount: 0
|
|
1343
|
+
};
|
|
1344
|
+
}
|
|
1345
|
+
const invalidExtractionResultFields = [
|
|
1346
|
+
["facts", result.facts],
|
|
1347
|
+
["entities", result.entities],
|
|
1348
|
+
["questions", result.questions],
|
|
1349
|
+
["profileUpdates", result.profileUpdates]
|
|
1350
|
+
].filter(([, value]) => !Array.isArray(value)).map(([field]) => field);
|
|
1351
|
+
if (invalidExtractionResultFields.length > 0) {
|
|
1352
|
+
log.warn("runExtraction: extraction returned invalid collection fields", {
|
|
1353
|
+
invalidFields: invalidExtractionResultFields,
|
|
1354
|
+
resultKeys: typeof result === "object" && result !== null ? Object.keys(result) : []
|
|
1355
|
+
});
|
|
1356
|
+
if (options.failOnExtractionFailure) {
|
|
1357
|
+
throw new Error("extraction failed: invalid_extraction_result");
|
|
1358
|
+
}
|
|
1359
|
+
await clearBuffer();
|
|
1360
|
+
return {
|
|
1361
|
+
status: "skipped",
|
|
1362
|
+
reason: "invalid_extraction_result",
|
|
1363
|
+
persistedCount: 0,
|
|
1364
|
+
durableOutputCount: 0
|
|
1365
|
+
};
|
|
1366
|
+
}
|
|
1367
|
+
const extractionFailure = typeof result.extractionFailure === "string" && result.extractionFailure.trim().length > 0 ? result.extractionFailure : void 0;
|
|
1368
|
+
let recordedRetryFailure = false;
|
|
1369
|
+
if (extractionFingerprint && extractionRetryEnabled) {
|
|
1370
|
+
if (extractionFailure) {
|
|
1371
|
+
try {
|
|
1372
|
+
meta ??= await runDeadlineAware(() => storage.loadMeta(), "during_retry_bookkeeping_load", false);
|
|
1373
|
+
await runDeadlineAware(
|
|
1374
|
+
() => this.recordExtractionFailure(
|
|
1375
|
+
storage,
|
|
1376
|
+
selfNamespace,
|
|
1377
|
+
extractionFingerprint,
|
|
1378
|
+
result.extractionFailureClass ?? "provider_retryable",
|
|
1379
|
+
meta,
|
|
1380
|
+
Date.now()
|
|
1381
|
+
),
|
|
1382
|
+
"during_retry_bookkeeping_save",
|
|
1383
|
+
false
|
|
1384
|
+
);
|
|
1385
|
+
recordedRetryFailure = true;
|
|
1386
|
+
} catch (err) {
|
|
1387
|
+
if (err instanceof ExtractionDeadlineError) throw err;
|
|
1388
|
+
log.warn("runExtraction: failed to load meta for retry bookkeeping (non-fatal)", err);
|
|
1389
|
+
}
|
|
1390
|
+
} else {
|
|
1391
|
+
this.resetProviderBreakerOnSuccess();
|
|
1392
|
+
try {
|
|
1393
|
+
meta ??= await runDeadlineAware(() => storage.loadMeta(), "during_retry_bookkeeping_load", false);
|
|
1394
|
+
this.hydrateRetryStateFromMeta(selfNamespace, meta);
|
|
1395
|
+
if (this.clearExtractionRetryEntry(selfNamespace, extractionFingerprint, meta)) {
|
|
1396
|
+
await runDeadlineAware(() => storage.saveMeta(meta), "during_retry_bookkeeping_save", false);
|
|
1397
|
+
}
|
|
1398
|
+
} catch (err) {
|
|
1399
|
+
if (err instanceof ExtractionDeadlineError) throw err;
|
|
1400
|
+
log.warn("runExtraction: failed to persist cleared retry state (non-fatal)", err);
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1404
|
+
if (options.failOnExtractionFailure && extractionFailure) {
|
|
1405
|
+
throw new Error(`extraction failed: ${extractionFailure}`);
|
|
1406
|
+
}
|
|
1407
|
+
if (result.facts.length === 0 && result.entities.length === 0 && result.questions.length === 0 && result.profileUpdates.length === 0) {
|
|
1408
|
+
log.debug("runExtraction: extraction produced no durable outputs; skipping persistence");
|
|
1409
|
+
if (extractionFailure) {
|
|
1410
|
+
log.warn(
|
|
1411
|
+
"runExtraction: extraction reported failure with no durable outputs; not marking fingerprint processed",
|
|
1412
|
+
{ extractionFailure }
|
|
1413
|
+
);
|
|
1414
|
+
}
|
|
1415
|
+
if (extractionFingerprint && shouldPersistProcessedFingerprint && !extractionFailure) {
|
|
1416
|
+
meta ??= await storage.loadMeta();
|
|
1417
|
+
await this.deps.recordProcessedExtractionFingerprint(storage, extractionFingerprint, meta);
|
|
1418
|
+
meta.extractionCount += 1;
|
|
1419
|
+
meta.lastExtractionAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1420
|
+
await storage.saveMeta(meta);
|
|
1421
|
+
}
|
|
1422
|
+
await runPassiveCapture(normalizedTurns, {
|
|
1423
|
+
sessionKey,
|
|
1424
|
+
principal,
|
|
1425
|
+
namespace: selfNamespace,
|
|
1426
|
+
bufferKey,
|
|
1427
|
+
isLiveSession: clearBufferAfterExtraction
|
|
1428
|
+
});
|
|
1429
|
+
if (recordedRetryFailure) {
|
|
1430
|
+
log.debug("runExtraction: retaining buffer for backoff retry after failure");
|
|
1431
|
+
} else {
|
|
1432
|
+
await clearBuffer();
|
|
1433
|
+
}
|
|
1434
|
+
return { status: "skipped", reason: "empty_extraction_result", persistedCount: 0, durableOutputCount: 0 };
|
|
1435
|
+
}
|
|
1436
|
+
let threadIdForExtraction = null;
|
|
1437
|
+
if (resolvePresentationCapabilities(this.config).threading && turns.length > 0) {
|
|
1438
|
+
const lastTurn = turns[turns.length - 1];
|
|
1439
|
+
try {
|
|
1440
|
+
threadIdForExtraction = await runDeadlineAware(
|
|
1441
|
+
() => this.deps.getThreading().processTurn(lastTurn, []),
|
|
1442
|
+
"during_threading"
|
|
1443
|
+
);
|
|
1444
|
+
} catch (err) {
|
|
1445
|
+
if (err instanceof ExtractionDeadlineError) throw err;
|
|
1446
|
+
log.warn("[threading] processTurn failed before persistence (non-fatal)", err);
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
throwIfDeadlineExceeded("before_persist");
|
|
1450
|
+
throwIfAborted2("before_persist");
|
|
1451
|
+
const { persistedIds } = await this.deps.persistExtraction(
|
|
1452
|
+
result,
|
|
1453
|
+
storage,
|
|
1454
|
+
threadIdForExtraction,
|
|
1455
|
+
{ sessionKey, principal, validAt: sourceValidAt, sourceConnector: deriveSourceConnector(targetTurns) },
|
|
1456
|
+
// Pass the KNOWN base namespace (NHIdx) so the catalog write touch records the
|
|
1457
|
+
// real namespace rather than a guess decoded from the storage dir.
|
|
1458
|
+
selfNamespace,
|
|
1459
|
+
scopeProfileWritePlan,
|
|
1460
|
+
// Verbatim source turns for the faithfulness gate (#1576) so it can
|
|
1461
|
+
// locate a quote per fact when #1575 spans are absent.
|
|
1462
|
+
normalizedTurns.map((t) => t.content).join("\n\n")
|
|
1463
|
+
);
|
|
1464
|
+
const durableOutputCount = result.facts.length + result.entities.length + result.questions.length + result.profileUpdates.length;
|
|
1465
|
+
try {
|
|
1466
|
+
options.onDurableCommit?.();
|
|
1467
|
+
} catch (error) {
|
|
1468
|
+
log.warn("runExtraction: durable-commit callback failed", error);
|
|
1469
|
+
}
|
|
1470
|
+
const runPostPersistBestEffort = runExtractionPostPersistBestEffort.bind(null, runDeadlineAware);
|
|
1471
|
+
const postPersist = await runExtractionPostPersist({
|
|
1472
|
+
result,
|
|
1473
|
+
storage,
|
|
1474
|
+
meta: meta ?? void 0,
|
|
1475
|
+
extractionFingerprint: extractionFingerprint ?? void 0,
|
|
1476
|
+
shouldPersistProcessedFingerprint,
|
|
1477
|
+
recordProcessedExtractionFingerprint: this.deps.recordProcessedExtractionFingerprint,
|
|
1478
|
+
runDeadlineAware,
|
|
1479
|
+
isDeadlineError: (error) => error instanceof ExtractionDeadlineError,
|
|
1480
|
+
createDeadlineError: (stage) => new ExtractionDeadlineError(stage),
|
|
1481
|
+
deadlineMs,
|
|
1482
|
+
clearBufferAfterExtraction,
|
|
1483
|
+
runPostPersistBestEffort,
|
|
1484
|
+
extractionJudgeShadow: this.config.extractionJudgeShadow,
|
|
1485
|
+
getDeferredCount: () => this.deps.getLastPersistExtractionDeferredCount(),
|
|
1486
|
+
normalizedTurns,
|
|
1487
|
+
bufferKey,
|
|
1488
|
+
retainDeferredTurns: (key, retainedTurns, max) => this.deps.getBuffer().retainDeferredTurns(key, retainedTurns, max),
|
|
1489
|
+
clearBuffer,
|
|
1490
|
+
failOnExtractionFailure: options.failOnExtractionFailure === true
|
|
1491
|
+
});
|
|
1492
|
+
meta = postPersist.meta ?? null;
|
|
1493
|
+
const postPersistMetadataFailed = postPersist.metadataFailed;
|
|
1494
|
+
await runPostPersistBestEffort(
|
|
1495
|
+
"during_post_persist_passive_capture",
|
|
1496
|
+
() => runPassiveCapture(normalizedTurns, {
|
|
1497
|
+
sessionKey,
|
|
1498
|
+
principal,
|
|
1499
|
+
namespace: selfNamespace,
|
|
1500
|
+
bufferKey,
|
|
1501
|
+
isLiveSession: clearBufferAfterExtraction,
|
|
1502
|
+
abortSignal: extractionAbortSignal
|
|
1503
|
+
})
|
|
1504
|
+
);
|
|
1505
|
+
if (resolvePresentationCapabilities(this.config).memoryBoxes && persistedIds.length > 0) {
|
|
1506
|
+
const extractionTopics = deriveTopicsFromExtraction(result);
|
|
1507
|
+
const firstUserTurn = turns.find((t) => t.role === "user");
|
|
1508
|
+
const boxGoal = firstUserTurn?.content?.slice(0, 100)?.trim() || void 0;
|
|
1509
|
+
await runPostPersistBestEffort(
|
|
1510
|
+
"during_memory_box",
|
|
1511
|
+
() => this.deps.boxBuilderFor(storage).onExtraction({
|
|
1512
|
+
topics: extractionTopics,
|
|
1513
|
+
memoryIds: persistedIds,
|
|
1514
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1515
|
+
goal: boxGoal
|
|
1516
|
+
})
|
|
1517
|
+
);
|
|
1518
|
+
}
|
|
1519
|
+
if (resolvePresentationCapabilities(this.config).threading && threadIdForExtraction && persistedIds.length > 0) {
|
|
1520
|
+
await runPostPersistBestEffort(
|
|
1521
|
+
"during_thread_episode_append",
|
|
1522
|
+
() => this.deps.appendPersistedThreadEpisodes(threadIdForExtraction, persistedIds)
|
|
1523
|
+
);
|
|
1524
|
+
}
|
|
1525
|
+
if (resolvePresentationCapabilities(this.config).threading && threadIdForExtraction) {
|
|
1526
|
+
const conversationContent = turns.map((t) => t.content).join(" ");
|
|
1527
|
+
await runPostPersistBestEffort(
|
|
1528
|
+
"during_thread_title_update",
|
|
1529
|
+
() => this.deps.getThreading().updateThreadTitle(threadIdForExtraction, conversationContent)
|
|
1530
|
+
);
|
|
1531
|
+
}
|
|
1532
|
+
const nonZeroExtraction = durableOutputCount > 0;
|
|
1533
|
+
try {
|
|
1534
|
+
this.deps.maybeScheduleConsolidation(nonZeroExtraction);
|
|
1535
|
+
} catch (err) {
|
|
1536
|
+
log.warn("runExtraction: consolidation scheduling failed after persistence (non-fatal)", err);
|
|
1537
|
+
}
|
|
1538
|
+
try {
|
|
1539
|
+
this.deps.requestQmdMaintenance();
|
|
1540
|
+
} catch (err) {
|
|
1541
|
+
log.warn("runExtraction: QMD maintenance scheduling failed after persistence (non-fatal)", err);
|
|
1542
|
+
}
|
|
1543
|
+
await runPostPersistBestEffort(
|
|
1544
|
+
"during_tier_migration",
|
|
1545
|
+
() => this.deps.runTierMigrationCycle(storage, "extraction")
|
|
1546
|
+
);
|
|
1547
|
+
return {
|
|
1548
|
+
status: "completed",
|
|
1549
|
+
persistedCount: persistedIds.length,
|
|
1550
|
+
durableOutputCount,
|
|
1551
|
+
postPersistMetadataFailed
|
|
1552
|
+
};
|
|
1553
|
+
} finally {
|
|
1554
|
+
clearExtractionDeadlineTimer();
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1557
|
+
};
|
|
1558
|
+
|
|
1559
|
+
export {
|
|
1560
|
+
BulkImportBatchPartialFailureError,
|
|
1561
|
+
mapRecallSourceToXrayServedBy,
|
|
1562
|
+
abortRecallError,
|
|
1563
|
+
throwIfRecallAborted,
|
|
1564
|
+
raceRecallAbort,
|
|
1565
|
+
COMPACTION_SIGNAL_MAX_AGE_MS,
|
|
1566
|
+
normalizeIanaTimeZone,
|
|
1567
|
+
formatDateInTimeZone,
|
|
1568
|
+
utcDateKeysForLocalDay,
|
|
1569
|
+
parseFiniteDate,
|
|
1570
|
+
filterHourlySummaryMarkdownForLocalDay,
|
|
1571
|
+
qmdStartupCollectionCheckWithTimeout,
|
|
1572
|
+
defaultWorkspaceDir,
|
|
1573
|
+
sanitizeSessionKeyForFilename,
|
|
1574
|
+
targetSourceValidAtSortMs,
|
|
1575
|
+
splitTurnsBySourceValidAt,
|
|
1576
|
+
isArtifactMemoryPath,
|
|
1577
|
+
isMeetingRecordPath,
|
|
1578
|
+
isActivityDigestPath,
|
|
1579
|
+
buildCompressionGuidelinesMarkdown2 as buildCompressionGuidelinesMarkdown,
|
|
1580
|
+
applyQueryAwareCandidateFilter,
|
|
1581
|
+
tokenizeRecallQuery,
|
|
1582
|
+
shouldFilterLifecycleRecallCandidate,
|
|
1583
|
+
lifecycleRecallScoreAdjustment,
|
|
1584
|
+
computeArtifactRecallLimit,
|
|
1585
|
+
resolveEffectiveRecallMode,
|
|
1586
|
+
resolveRecallModeDecision,
|
|
1587
|
+
resolveRecallModeDecisionAsync,
|
|
1588
|
+
computeArtifactCandidateFetchLimit,
|
|
1589
|
+
computeQmdHybridFetchLimit,
|
|
1590
|
+
summarizeGraphShadowComparison,
|
|
1591
|
+
parseGraphRecallRankedResults,
|
|
1592
|
+
parseMemoryIntentSnapshot,
|
|
1593
|
+
buildQmdIntentHint,
|
|
1594
|
+
parseQmdRecallResults,
|
|
1595
|
+
mergeArtifactRecallCandidates,
|
|
1596
|
+
resolveRecentThreadMemoryPaths,
|
|
1597
|
+
buildMemoryPathById,
|
|
1598
|
+
appendMemoryToGraphContext,
|
|
1599
|
+
resolvePersistedMemoryRelativePath,
|
|
1600
|
+
deriveTopicsFromExtraction,
|
|
1601
|
+
ExtractionDeadlineError,
|
|
1602
|
+
ExtractionRunCoordinator
|
|
1603
|
+
};
|
|
1604
|
+
//# sourceMappingURL=chunk-YPC7W6GA.js.map
|