@remnic/core 9.29.1 → 9.30.1
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 +11 -11
- package/dist/access-admin-ops-surface.js +19 -18
- package/dist/access-authorization-probe.d.ts +11 -11
- package/dist/access-authorization-probe.js +20 -19
- package/dist/access-boundary.d.ts +11 -11
- package/dist/access-boundary.js +19 -18
- package/dist/access-citation-namespace.js +2 -2
- package/dist/access-cli.js +45 -44
- package/dist/access-cli.js.map +1 -1
- package/dist/access-health-types.d.ts +1 -1
- package/dist/access-http-lcm-compaction.d.ts +11 -11
- package/dist/access-http.d.ts +11 -11
- package/dist/access-http.js +25 -24
- package/dist/access-identity-continuity-surface.d.ts +8 -8
- package/dist/access-identity-continuity-surface.js +19 -18
- package/dist/access-lcm-surface.d.ts +10 -10
- package/dist/access-lcm-surface.js +19 -18
- package/dist/access-mcp.d.ts +11 -11
- package/dist/access-mcp.js +23 -22
- package/dist/access-memory-search-fanout.d.ts +2 -2
- package/dist/{access-namespace-preflight-DCXBkDp0.d.ts → access-namespace-preflight-VfXssvoI.d.ts} +1 -1
- package/dist/access-namespace-preflight.d.ts +3 -3
- package/dist/access-namespace-preflight.js +19 -18
- package/dist/access-observe-write-surface.d.ts +10 -10
- package/dist/access-observe-write-surface.js +19 -18
- package/dist/access-operations-batch.js +20 -19
- package/dist/access-operations.d.ts +11 -11
- package/dist/access-operations.js +22 -21
- package/dist/access-recall-concurrency.d.ts +11 -11
- package/dist/access-recall-concurrency.js +19 -18
- package/dist/access-recall-response.d.ts +10 -10
- package/dist/access-recall-response.js +19 -18
- package/dist/access-recall-surface.d.ts +10 -10
- package/dist/access-recall-surface.js +19 -18
- package/dist/access-schema.js +3 -3
- package/dist/{access-service-DvPW_M5j.d.ts → access-service-B5RwVsTV.d.ts} +7 -7
- package/dist/access-service.d.ts +11 -11
- package/dist/access-service.js +19 -18
- package/dist/access-surface-catalog.d.ts +11 -11
- package/dist/access-wearables-meetings-surface.d.ts +4 -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 +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 +3 -2
- package/dist/briefing.js +10 -9
- package/dist/buffer-surprise-report.d.ts +1 -1
- package/dist/buffer.d.ts +3 -2
- 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/{catalog-BIVgftWu.d.ts → catalog-V7inUQdp.d.ts} +1 -1
- package/dist/causal-behavior.d.ts +1 -1
- package/dist/causal-consolidation.d.ts +1 -1
- package/dist/causal-consolidation.js +11 -10
- package/dist/causal-consolidation.js.map +1 -1
- package/dist/causal-trajectory-graph.d.ts +1 -1
- package/dist/{chunk-FZRQZW4H.js → chunk-2EUVEDF5.js} +2 -2
- package/dist/{chunk-PBUZNGMX.js → chunk-4WKA6I65.js} +20 -20
- package/dist/{chunk-PBUZNGMX.js.map → chunk-4WKA6I65.js.map} +1 -1
- package/dist/{chunk-AIK5W3IW.js → chunk-5TFP4CUL.js} +68 -2
- package/dist/chunk-5TFP4CUL.js.map +1 -0
- package/dist/{chunk-3JNDOAQ2.js → chunk-5W4ZLBEZ.js} +9 -9
- package/dist/{chunk-5ZCUDWD4.js → chunk-62U4J2GN.js} +2 -2
- package/dist/{chunk-IGPEJVBY.js → chunk-66772WBP.js} +2 -2
- package/dist/{chunk-EP5BVCRI.js → chunk-6BNE62PM.js} +2 -2
- package/dist/{chunk-AAP5T6QG.js → chunk-6DNMUYEE.js} +2 -2
- package/dist/{chunk-35DL7IOI.js → chunk-6FUPCJUL.js} +7 -7
- package/dist/{chunk-JP3YPDAW.js → chunk-6U7F52DE.js} +19 -19
- package/dist/{chunk-E4OCKU67.js → chunk-7XC2HX75.js} +2 -2
- package/dist/{chunk-OVRQLAYR.js → chunk-AZ5BTEKX.js} +2 -2
- package/dist/{chunk-2AXMQWCN.js → chunk-CZRYLSSW.js} +3 -3
- package/dist/chunk-FBUE6PD3.js +1769 -0
- package/dist/chunk-FBUE6PD3.js.map +1 -0
- package/dist/{chunk-PTSNLEXK.js → chunk-FDAMS4PK.js} +2 -2
- package/dist/{chunk-WL5EBRDH.js → chunk-FFAZ2XFU.js} +9 -8
- package/dist/chunk-FFAZ2XFU.js.map +1 -0
- package/dist/{chunk-2GXM5N5A.js → chunk-GIBMDSJ2.js} +2 -2
- package/dist/{chunk-E5OBR6CJ.js → chunk-H5CPS5JP.js} +3 -3
- package/dist/{chunk-2D7FEUCK.js → chunk-I5NLEENJ.js} +2 -2
- package/dist/{chunk-DRBXHRXE.js → chunk-IB26HVAJ.js} +2 -2
- package/dist/{chunk-QDKK63Y7.js → chunk-IC6CBHCA.js} +3 -3
- package/dist/{chunk-IAEAFLF2.js → chunk-JCBWCYEJ.js} +2 -2
- package/dist/{chunk-3PDOVUHC.js → chunk-KXB4VXHY.js} +2 -2
- package/dist/{chunk-WU7TTVOD.js → chunk-LBOD4U5B.js} +7 -7
- package/dist/{chunk-UBKEQOTD.js → chunk-LGHKJABJ.js} +3 -3
- package/dist/{chunk-VSURVXKY.js → chunk-M3C2V3PV.js} +10 -10
- package/dist/{chunk-OFHFH655.js → chunk-MJCGLM25.js} +8 -8
- package/dist/{chunk-OYJKSZIQ.js → chunk-NHWB6HG2.js} +2 -2
- package/dist/{chunk-TC3VNKGX.js → chunk-NM2WP3B7.js} +4 -4
- package/dist/{chunk-GAWOEQEQ.js → chunk-P5PXWTW7.js} +2 -2
- package/dist/chunk-PH3ZWL3W.js +786 -0
- package/dist/chunk-PH3ZWL3W.js.map +1 -0
- package/dist/{chunk-2QMKMVHH.js → chunk-QNWXCJ76.js} +2 -2
- package/dist/{chunk-JMQUBRV6.js → chunk-RGIWINDN.js} +340 -771
- package/dist/chunk-RGIWINDN.js.map +1 -0
- package/dist/{chunk-WRFBW2TT.js → chunk-SPJXKAVF.js} +3 -3
- package/dist/{chunk-HKJCMOT6.js → chunk-TH2LSRMG.js} +2 -2
- package/dist/{chunk-CW4AGY3G.js → chunk-VI2JQPNQ.js} +2 -2
- package/dist/{chunk-OP6F6L3S.js → chunk-W5EG6LE3.js} +2 -2
- package/dist/{chunk-DEBHXHJR.js → chunk-WIPEXG6R.js} +2 -2
- package/dist/{chunk-SH6YO3LT.js → chunk-WVO4G37W.js} +2 -2
- package/dist/{chunk-FUYEVEX7.js → chunk-YNQBRLC5.js} +24 -22
- package/dist/chunk-YNQBRLC5.js.map +1 -0
- package/dist/{chunk-7L45JFPW.js → chunk-YTQNQ4KB.js} +14 -14
- package/dist/{chunk-PKRIQL3V.js → chunk-YTSNAUXN.js} +2 -2
- package/dist/{chunk-5QDRGWT2.js → chunk-ZPSVBSOF.js} +4 -4
- package/dist/{cli-CxoSOiG0.d.ts → cli-C4GbETPC.d.ts} +5 -5
- package/dist/cli.d.ts +13 -13
- package/dist/cli.js +36 -35
- package/dist/compounding/engine.d.ts +3 -2
- package/dist/compounding/engine.js +10 -9
- 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 +3 -3
- package/dist/connectors/codex-materialize-runner.d.ts +1 -1
- package/dist/connectors/codex-materialize-runner.js +10 -9
- package/dist/connectors/codex-materialize.d.ts +1 -1
- package/dist/connectors/index.d.ts +1 -1
- package/dist/connectors/index.js +10 -9
- package/dist/consolidation-provenance-check.d.ts +3 -2
- package/dist/consolidation-undo.d.ts +3 -2
- package/dist/contradiction/index.d.ts +3 -2
- package/dist/conversation-index/backend.d.ts +1 -1
- package/dist/conversation-index/chunker.d.ts +1 -1
- package/dist/conversation-index/faiss-adapter.d.ts +1 -1
- package/dist/conversation-index/indexer.d.ts +1 -1
- package/dist/conversation-index/search.d.ts +1 -1
- package/dist/corpus-watermark.d.ts +1 -1
- package/dist/corpus-watermark.js +12 -11
- 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 +3 -2
- package/dist/entity-retrieval.js +10 -9
- package/dist/entity-schema.d.ts +1 -1
- package/dist/explicit-capture.d.ts +52 -10
- package/dist/explicit-capture.js +13 -1
- 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 +1 -1
- package/dist/extraction.d.ts +1 -1
- package/dist/fallback-llm.d.ts +1 -1
- package/dist/{forget-HRPLVB3W.js → forget-4UY2EOKH.js} +3 -3
- 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 +47 -45
- package/dist/intent.d.ts +1 -1
- package/dist/lcm/engine.d.ts +1 -1
- package/dist/lcm/index.d.ts +1 -1
- package/dist/lcm/tools.d.ts +1 -1
- package/dist/lifecycle.d.ts +1 -1
- package/dist/live-connectors-runner.d.ts +1 -1
- package/dist/local-llm.d.ts +1 -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 +10 -9
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.d.ts +2 -2
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +10 -9
- package/dist/maintenance/rebuild-memory-projection.d.ts +3 -2
- package/dist/maintenance/rebuild-memory-projection.js +11 -10
- package/dist/{maintenance-Cb-PjxQd.d.ts → maintenance-CL30NWtc.d.ts} +3 -3
- package/dist/mcp-memory-inspector-app.d.ts +11 -11
- package/dist/memory-action-policy.d.ts +1 -1
- package/dist/memory-cache.d.ts +1 -1
- package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
- package/dist/memory-projection-store.d.ts +1 -1
- package/dist/memory-provenance.d.ts +1 -1
- package/dist/memory-worth-outcomes.d.ts +3 -2
- package/dist/models-json.d.ts +1 -1
- package/dist/namespaces/migrate.d.ts +4 -3
- package/dist/namespaces/migrate.js +11 -10
- package/dist/namespaces/principal.d.ts +1 -1
- package/dist/namespaces/search.d.ts +1 -1
- package/dist/namespaces/storage.d.ts +4 -3
- package/dist/namespaces/storage.js +10 -9
- 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 +5 -5
- package/dist/offline-sync.js +2 -2
- package/dist/operator-doctor-corpus.d.ts +1 -1
- package/dist/operator-doctor-corpus.js +13 -12
- package/dist/operator-doctor-replica.d.ts +1 -1
- package/dist/operator-toolkit.d.ts +4 -3
- package/dist/operator-toolkit.js +18 -17
- package/dist/orchestration/compression-guideline-coordinator.d.ts +3 -2
- package/dist/orchestration/maintenance.d.ts +5 -4
- package/dist/orchestration/maintenance.js +15 -14
- package/dist/{orchestrator-5GKqb_HL.d.ts → orchestrator-CKTCRwWV.d.ts} +7 -7
- package/dist/orchestrator.d.ts +8 -8
- package/dist/orchestrator.js +45 -44
- package/dist/patterns-cli.d.ts +1 -1
- package/dist/{pipeline-3Qi1TpRo.d.ts → pipeline-B7wj4rRZ.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/{purge-YSGX5BOG.js → purge-533FZLS4.js} +16 -9
- package/dist/purge-533FZLS4.js.map +1 -0
- package/dist/qmd-preflight.d.ts +2 -2
- package/dist/{qmd-D9JqzzRX.d.ts → qmd-q9tg4dHp.d.ts} +1 -1
- package/dist/qmd-recall-cache.d.ts +1 -1
- package/dist/qmd.d.ts +2 -2
- 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-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-state.js +2 -2
- 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-renderer.d.ts +1 -1
- package/dist/recall-xray.d.ts +1 -1
- 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-peers-config.d.ts +1 -1
- package/dist/resolve-auth-token.d.ts +1 -1
- package/dist/resume-bundles.js +4 -4
- 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-C4g6_UdP.d.ts → scope-profiles-CWDyglPM.d.ts} +1 -1
- package/dist/search/embed-helper.d.ts +1 -1
- package/dist/search/factory.d.ts +1 -1
- package/dist/search/index.d.ts +1 -1
- 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/secure-store/index.js +2 -2
- package/dist/{semantic-consolidation-BrxqnzTW.d.ts → semantic-consolidation-BUE3deLn.d.ts} +1 -1
- package/dist/semantic-consolidation.d.ts +2 -2
- package/dist/semantic-consolidation.js +11 -10
- package/dist/semantic-rule-promotion.js +10 -9
- package/dist/semantic-rule-verifier.d.ts +1 -1
- package/dist/semantic-rule-verifier.js +10 -9
- package/dist/{service-Bi1JgxdP.d.ts → service-CXgM0bQG.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-CcH6yPk2.d.ts → storage-DysJq-XH.d.ts} +163 -27
- package/dist/storage.d.ts +3 -2
- package/dist/storage.js +12 -10
- package/dist/summarizer.d.ts +1 -1
- package/dist/summarizer.js +3 -3
- package/dist/summary-snapshot.d.ts +1 -1
- package/dist/summary-snapshot.js +2 -2
- package/dist/temporal-supersession.d.ts +3 -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 +3 -2
- package/dist/tier-routing.d.ts +1 -1
- package/dist/topics.d.ts +1 -1
- package/dist/transcript.d.ts +1 -1
- package/dist/trust-score-stage.d.ts +1 -1
- package/dist/trust-score.d.ts +1 -1
- package/dist/{types-Dg0w9ADr.d.ts → types-aPPzhnjT.d.ts} +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/utility-runtime.d.ts +1 -1
- package/dist/utils/serialize-mutations.d.ts +11 -7
- package/dist/utils/serialize-mutations.js +1 -1
- package/dist/verified-recall.js +10 -9
- package/dist/write-envelope.d.ts +1 -1
- package/package.json +2 -2
- package/src/access-observe-write-surface.ts +1 -1
- package/src/access-service-coding-write.test.ts +40 -0
- package/src/explicit-capture.test.ts +1110 -1
- package/src/explicit-capture.ts +534 -135
- package/src/maintenance/purge.test.ts +157 -1
- package/src/maintenance/purge.ts +15 -9
- package/src/orchestration/recall-internal-deps.ts +385 -0
- package/src/orchestration/recall-internal.ts +16 -375
- package/src/secure-store/secure-fs.ts +78 -1
- package/src/storage/content-hash-index.ts +43 -17
- package/src/storage/tombstone-blocked-capture-index.ts +562 -0
- package/src/storage/tombstone-blocked-capture-mutation.ts +284 -0
- package/src/storage/tombstone-blocked-capture-sync.ts +872 -0
- package/src/storage-fact-hash-index-defer.test.ts +2900 -57
- package/src/storage.ts +266 -265
- package/src/testing/subjects/tombstone-blocked-capture.test.ts +181 -0
- package/src/utils/serialize-mutations.test.ts +4 -1
- package/src/utils/serialize-mutations.ts +26 -27
- package/dist/chunk-55MDBZLC.js +0 -490
- package/dist/chunk-55MDBZLC.js.map +0 -1
- package/dist/chunk-AIK5W3IW.js.map +0 -1
- package/dist/chunk-FUYEVEX7.js.map +0 -1
- package/dist/chunk-JMQUBRV6.js.map +0 -1
- package/dist/chunk-WL5EBRDH.js.map +0 -1
- package/dist/purge-YSGX5BOG.js.map +0 -1
- /package/dist/{chunk-FZRQZW4H.js.map → chunk-2EUVEDF5.js.map} +0 -0
- /package/dist/{chunk-3JNDOAQ2.js.map → chunk-5W4ZLBEZ.js.map} +0 -0
- /package/dist/{chunk-5ZCUDWD4.js.map → chunk-62U4J2GN.js.map} +0 -0
- /package/dist/{chunk-IGPEJVBY.js.map → chunk-66772WBP.js.map} +0 -0
- /package/dist/{chunk-EP5BVCRI.js.map → chunk-6BNE62PM.js.map} +0 -0
- /package/dist/{chunk-AAP5T6QG.js.map → chunk-6DNMUYEE.js.map} +0 -0
- /package/dist/{chunk-35DL7IOI.js.map → chunk-6FUPCJUL.js.map} +0 -0
- /package/dist/{chunk-JP3YPDAW.js.map → chunk-6U7F52DE.js.map} +0 -0
- /package/dist/{chunk-E4OCKU67.js.map → chunk-7XC2HX75.js.map} +0 -0
- /package/dist/{chunk-OVRQLAYR.js.map → chunk-AZ5BTEKX.js.map} +0 -0
- /package/dist/{chunk-2AXMQWCN.js.map → chunk-CZRYLSSW.js.map} +0 -0
- /package/dist/{chunk-PTSNLEXK.js.map → chunk-FDAMS4PK.js.map} +0 -0
- /package/dist/{chunk-2GXM5N5A.js.map → chunk-GIBMDSJ2.js.map} +0 -0
- /package/dist/{chunk-E5OBR6CJ.js.map → chunk-H5CPS5JP.js.map} +0 -0
- /package/dist/{chunk-2D7FEUCK.js.map → chunk-I5NLEENJ.js.map} +0 -0
- /package/dist/{chunk-DRBXHRXE.js.map → chunk-IB26HVAJ.js.map} +0 -0
- /package/dist/{chunk-QDKK63Y7.js.map → chunk-IC6CBHCA.js.map} +0 -0
- /package/dist/{chunk-IAEAFLF2.js.map → chunk-JCBWCYEJ.js.map} +0 -0
- /package/dist/{chunk-3PDOVUHC.js.map → chunk-KXB4VXHY.js.map} +0 -0
- /package/dist/{chunk-WU7TTVOD.js.map → chunk-LBOD4U5B.js.map} +0 -0
- /package/dist/{chunk-UBKEQOTD.js.map → chunk-LGHKJABJ.js.map} +0 -0
- /package/dist/{chunk-VSURVXKY.js.map → chunk-M3C2V3PV.js.map} +0 -0
- /package/dist/{chunk-OFHFH655.js.map → chunk-MJCGLM25.js.map} +0 -0
- /package/dist/{chunk-OYJKSZIQ.js.map → chunk-NHWB6HG2.js.map} +0 -0
- /package/dist/{chunk-TC3VNKGX.js.map → chunk-NM2WP3B7.js.map} +0 -0
- /package/dist/{chunk-GAWOEQEQ.js.map → chunk-P5PXWTW7.js.map} +0 -0
- /package/dist/{chunk-2QMKMVHH.js.map → chunk-QNWXCJ76.js.map} +0 -0
- /package/dist/{chunk-WRFBW2TT.js.map → chunk-SPJXKAVF.js.map} +0 -0
- /package/dist/{chunk-HKJCMOT6.js.map → chunk-TH2LSRMG.js.map} +0 -0
- /package/dist/{chunk-CW4AGY3G.js.map → chunk-VI2JQPNQ.js.map} +0 -0
- /package/dist/{chunk-OP6F6L3S.js.map → chunk-W5EG6LE3.js.map} +0 -0
- /package/dist/{chunk-DEBHXHJR.js.map → chunk-WIPEXG6R.js.map} +0 -0
- /package/dist/{chunk-SH6YO3LT.js.map → chunk-WVO4G37W.js.map} +0 -0
- /package/dist/{chunk-7L45JFPW.js.map → chunk-YTQNQ4KB.js.map} +0 -0
- /package/dist/{chunk-PKRIQL3V.js.map → chunk-YTSNAUXN.js.map} +0 -0
- /package/dist/{chunk-5QDRGWT2.js.map → chunk-ZPSVBSOF.js.map} +0 -0
- /package/dist/{forget-HRPLVB3W.js.map → forget-4UY2EOKH.js.map} +0 -0
package/src/explicit-capture.ts
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
import { composeMemoryEnvelope, TAG_LIMITS } from "./write-envelope.js";
|
|
2
2
|
import { resolveNamespaceCapabilities } from "./capabilities.js";
|
|
3
|
-
import { randomUUID } from "node:crypto";
|
|
3
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
4
4
|
import type { Orchestrator } from "./orchestrator.js";
|
|
5
|
+
import { log } from "./logger.js";
|
|
5
6
|
import { isSafeRouteNamespace } from "./routing/engine.js";
|
|
6
7
|
import { sanitizeMemoryContent } from "./sanitize.js";
|
|
7
|
-
import { ContentHashIndex } from "./storage.js";
|
|
8
8
|
import type { CaptureMode, MemoryCategory, MemoryLifecycleEvent, PluginConfig } from "./types.js";
|
|
9
|
+
import {
|
|
10
|
+
buildExplicitCaptureDedupKey,
|
|
11
|
+
isTombstoneBlockedCaptureWriteLockBusy,
|
|
12
|
+
normalizeExplicitCaptureContent,
|
|
13
|
+
} from "./storage/tombstone-blocked-capture-index.js";
|
|
14
|
+
import type { StorageManager } from "./storage.js";
|
|
9
15
|
|
|
10
16
|
export type ExplicitCaptureInput = {
|
|
11
17
|
content: string;
|
|
@@ -107,13 +113,6 @@ function asTrimmed(value: string | undefined): string | undefined {
|
|
|
107
113
|
return trimmed && trimmed.length > 0 ? trimmed : undefined;
|
|
108
114
|
}
|
|
109
115
|
|
|
110
|
-
function normalizeCaptureContent(value: string): string {
|
|
111
|
-
return value
|
|
112
|
-
.toLowerCase()
|
|
113
|
-
.replace(/\s+/g, " ")
|
|
114
|
-
.trim();
|
|
115
|
-
}
|
|
116
|
-
|
|
117
116
|
function redactSecrets(value: string): string {
|
|
118
117
|
let redacted = value;
|
|
119
118
|
for (const { pattern, replacement } of SECRET_REDACTION_PATTERNS) {
|
|
@@ -153,9 +152,13 @@ function sanitizeReviewMetadata(value: string | undefined): string | undefined {
|
|
|
153
152
|
}
|
|
154
153
|
|
|
155
154
|
function sanitizeReviewTags(tags: string[] | undefined): string[] {
|
|
156
|
-
return Array.from(
|
|
157
|
-
|
|
158
|
-
|
|
155
|
+
return Array.from(
|
|
156
|
+
new Set(
|
|
157
|
+
(tags ?? [])
|
|
158
|
+
.map((tag) => sanitizeReviewMetadata(tag))
|
|
159
|
+
.filter((tag): tag is string => typeof tag === "string" && tag.length > 0)
|
|
160
|
+
)
|
|
161
|
+
);
|
|
159
162
|
}
|
|
160
163
|
|
|
161
164
|
function normalizeExplicitCaptureError(error: unknown): string {
|
|
@@ -164,9 +167,13 @@ function normalizeExplicitCaptureError(error: unknown): string {
|
|
|
164
167
|
return rendered.length > 0 ? rendered : "explicit capture failed";
|
|
165
168
|
}
|
|
166
169
|
|
|
170
|
+
function isUnsupportedExplicitCaptureNamespaceError(error: unknown): boolean {
|
|
171
|
+
return normalizeExplicitCaptureError(error).startsWith("unsupported namespace:");
|
|
172
|
+
}
|
|
173
|
+
|
|
167
174
|
function resolveExplicitCaptureReviewNamespace(
|
|
168
175
|
orchestrator: Orchestrator,
|
|
169
|
-
namespace: string | undefined
|
|
176
|
+
namespace: string | undefined
|
|
170
177
|
): string | undefined {
|
|
171
178
|
const normalized = asTrimmed(namespace);
|
|
172
179
|
if (!normalized) return undefined;
|
|
@@ -175,7 +182,7 @@ function resolveExplicitCaptureReviewNamespace(
|
|
|
175
182
|
|
|
176
183
|
function resolveExplicitCaptureNamespace(
|
|
177
184
|
orchestrator: Orchestrator,
|
|
178
|
-
namespace: string | undefined
|
|
185
|
+
namespace: string | undefined
|
|
179
186
|
): string | undefined {
|
|
180
187
|
const normalized = asTrimmed(namespace);
|
|
181
188
|
if (!normalized) return undefined;
|
|
@@ -185,11 +192,15 @@ function resolveExplicitCaptureNamespace(
|
|
|
185
192
|
}
|
|
186
193
|
return normalized;
|
|
187
194
|
}
|
|
188
|
-
const allowed = new Set(
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
195
|
+
const allowed = new Set(
|
|
196
|
+
[
|
|
197
|
+
orchestrator.config.defaultNamespace,
|
|
198
|
+
orchestrator.config.sharedNamespace,
|
|
199
|
+
...orchestrator.config.namespacePolicies.map((policy) => policy.name),
|
|
200
|
+
]
|
|
201
|
+
.map((value) => value.trim())
|
|
202
|
+
.filter(Boolean)
|
|
203
|
+
);
|
|
193
204
|
if (!allowed.has(normalized)) {
|
|
194
205
|
throw new Error(`unsupported namespace: ${normalized}`);
|
|
195
206
|
}
|
|
@@ -217,10 +228,7 @@ function parseExplicitCaptureTtl(ttl: string | undefined): string | undefined {
|
|
|
217
228
|
}
|
|
218
229
|
|
|
219
230
|
const multiplier =
|
|
220
|
-
unit === "m" ? 60_000
|
|
221
|
-
: unit === "h" ? 60 * 60_000
|
|
222
|
-
: unit === "d" ? 24 * 60 * 60_000
|
|
223
|
-
: 7 * 24 * 60 * 60_000;
|
|
231
|
+
unit === "m" ? 60_000 : unit === "h" ? 60 * 60_000 : unit === "d" ? 24 * 60 * 60_000 : 7 * 24 * 60 * 60_000;
|
|
224
232
|
return new Date(Date.now() + amount * multiplier).toISOString();
|
|
225
233
|
}
|
|
226
234
|
|
|
@@ -233,7 +241,7 @@ function parseInlineConfidence(value: string): number {
|
|
|
233
241
|
return Number.isFinite(parsed) ? parsed : Number.NaN;
|
|
234
242
|
}
|
|
235
243
|
|
|
236
|
-
function parseInlineNote(block: string): ExplicitCaptureInput
|
|
244
|
+
function parseInlineNote(block: string): ExplicitCaptureInput {
|
|
237
245
|
const lines = block.replace(/\r/g, "").split("\n");
|
|
238
246
|
const note: Partial<ExplicitCaptureInput> = {};
|
|
239
247
|
let idx = 0;
|
|
@@ -301,18 +309,21 @@ function parseInlineNote(block: string): ExplicitCaptureInput | null {
|
|
|
301
309
|
}
|
|
302
310
|
}
|
|
303
311
|
|
|
304
|
-
return
|
|
312
|
+
return { content: "", ...note };
|
|
305
313
|
}
|
|
306
314
|
|
|
307
|
-
|
|
315
|
+
function parseInlineExplicitCaptureCandidates(text: string): ExplicitCaptureInput[] {
|
|
308
316
|
const notes: ExplicitCaptureInput[] = [];
|
|
309
317
|
for (const match of text.matchAll(INLINE_NOTE_RE)) {
|
|
310
|
-
|
|
311
|
-
if (parsed) notes.push(parsed);
|
|
318
|
+
notes.push(parseInlineNote(match[1] ?? ""));
|
|
312
319
|
}
|
|
313
320
|
return notes;
|
|
314
321
|
}
|
|
315
322
|
|
|
323
|
+
export function parseInlineExplicitCaptureNotes(text: string): ExplicitCaptureInput[] {
|
|
324
|
+
return parseInlineExplicitCaptureCandidates(text).filter((note) => asTrimmed(note.content));
|
|
325
|
+
}
|
|
326
|
+
|
|
316
327
|
export function hasInlineExplicitCaptureMarkup(text: string): boolean {
|
|
317
328
|
return INLINE_NOTE_MARKUP_RE.test(text);
|
|
318
329
|
}
|
|
@@ -323,7 +334,7 @@ export function stripInlineExplicitCaptureNotes(text: string): string {
|
|
|
323
334
|
|
|
324
335
|
export function validateExplicitCaptureInput(
|
|
325
336
|
input: ExplicitCaptureInput,
|
|
326
|
-
mode: ExplicitCaptureValidationMode = "strict_explicit"
|
|
337
|
+
mode: ExplicitCaptureValidationMode = "strict_explicit"
|
|
327
338
|
): ValidExplicitCapture {
|
|
328
339
|
const content = asTrimmed(input.content);
|
|
329
340
|
if (!content) throw new Error("content is required");
|
|
@@ -400,37 +411,81 @@ async function findDuplicateExplicitCapture(
|
|
|
400
411
|
orchestrator: Orchestrator,
|
|
401
412
|
resolvedNamespace: string | undefined,
|
|
402
413
|
candidate: ValidExplicitCapture,
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
414
|
+
storageOverride?: StorageManager
|
|
415
|
+
): Promise<{ id: string; tombstoneBlocked: boolean } | null> {
|
|
416
|
+
const storage = storageOverride ?? (await orchestrator.getStorage(resolvedNamespace));
|
|
417
|
+
// Tombstone-blocked rows are absent from the active fact hash index. An
|
|
418
|
+
// authoritative miss can therefore skip the corpus scan only after the
|
|
419
|
+
// targeted blocked-row index reports a miss and remains authoritative.
|
|
420
|
+
let authoritativeFactHashMiss = false;
|
|
421
|
+
if (candidate.category === "fact" && typeof storage.hasFactContentHash === "function") {
|
|
409
422
|
try {
|
|
410
|
-
const hasHash = await
|
|
411
|
-
candidate.content,
|
|
412
|
-
);
|
|
423
|
+
const hasHash = await storage.hasFactContentHash(candidate.content);
|
|
413
424
|
if (!hasHash) {
|
|
414
425
|
const authoritative =
|
|
415
|
-
typeof
|
|
416
|
-
|
|
417
|
-
? await (storage as { isFactContentHashAuthoritative: () => Promise<boolean> | boolean })
|
|
418
|
-
.isFactContentHashAuthoritative()
|
|
426
|
+
typeof storage.isFactContentHashAuthoritative === "function"
|
|
427
|
+
? await storage.isFactContentHashAuthoritative()
|
|
419
428
|
: false;
|
|
420
|
-
|
|
429
|
+
authoritativeFactHashMiss = authoritative;
|
|
421
430
|
}
|
|
422
431
|
} catch (err) {
|
|
423
|
-
// Fail open: hash
|
|
432
|
+
// Fail open: hash indexes are only an optimization, so fall back to the
|
|
433
|
+
// full corpus scan when either index cannot answer.
|
|
424
434
|
void err;
|
|
425
435
|
}
|
|
426
436
|
}
|
|
427
|
-
|
|
428
|
-
|
|
437
|
+
if (authoritativeFactHashMiss) {
|
|
438
|
+
const checkBlockedIndex = storage.checkTombstoneBlockedExplicitCapture;
|
|
439
|
+
if (typeof checkBlockedIndex === "function") {
|
|
440
|
+
try {
|
|
441
|
+
const result = await checkBlockedIndex.call(
|
|
442
|
+
storage,
|
|
443
|
+
candidate.content,
|
|
444
|
+
candidate.category,
|
|
445
|
+
candidate.sourceConnector
|
|
446
|
+
);
|
|
447
|
+
if (!result.has && result.authoritative) return null;
|
|
448
|
+
} catch (err) {
|
|
449
|
+
// Fail open: an unavailable targeted index must not hide a durable
|
|
450
|
+
// pending-review duplicate.
|
|
451
|
+
void err;
|
|
452
|
+
}
|
|
453
|
+
} else {
|
|
454
|
+
const hasBlockedIndex = storage.hasTombstoneBlockedExplicitCapture;
|
|
455
|
+
if (typeof hasBlockedIndex === "function") {
|
|
456
|
+
try {
|
|
457
|
+
const hasBlocked = await hasBlockedIndex.call(
|
|
458
|
+
storage,
|
|
459
|
+
candidate.content,
|
|
460
|
+
candidate.category,
|
|
461
|
+
candidate.sourceConnector
|
|
462
|
+
);
|
|
463
|
+
if (!hasBlocked) {
|
|
464
|
+
const isIndexAuthoritative =
|
|
465
|
+
typeof storage.isTombstoneBlockedExplicitCaptureIndexAuthoritative === "function"
|
|
466
|
+
? await storage.isTombstoneBlockedExplicitCaptureIndexAuthoritative()
|
|
467
|
+
: false;
|
|
468
|
+
if (isIndexAuthoritative) return null;
|
|
469
|
+
}
|
|
470
|
+
} catch (err) {
|
|
471
|
+
// Fail open: an unavailable targeted index must not hide a durable
|
|
472
|
+
// pending-review duplicate.
|
|
473
|
+
void err;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
const hotMemories = await storage.readAllMemories();
|
|
479
|
+
const coldMemories = typeof storage.readAllColdMemories === "function" ? await storage.readAllColdMemories() : [];
|
|
480
|
+
const existing = [...hotMemories, ...coldMemories];
|
|
481
|
+
const normalizedCandidate = normalizeExplicitCaptureContent(candidate.content);
|
|
429
482
|
const match = existing.find((memory) => {
|
|
430
483
|
const status = memory.frontmatter.status ?? "active";
|
|
431
|
-
if (status !== "active")
|
|
484
|
+
if (status !== "active" && (status !== "pending_review" || !memory.frontmatter.blockedBy)) {
|
|
485
|
+
return false;
|
|
486
|
+
}
|
|
432
487
|
if (memory.frontmatter.category !== candidate.category) return false;
|
|
433
|
-
if (
|
|
488
|
+
if (normalizeExplicitCaptureContent(memory.content) !== normalizedCandidate) return false;
|
|
434
489
|
// Connector-aware dedup: same content from different connectors is NOT
|
|
435
490
|
// a duplicate. Operator (undefined) and connector-tagged memories are
|
|
436
491
|
// always distinct. Normalize empty strings to undefined.
|
|
@@ -439,23 +494,27 @@ async function findDuplicateExplicitCapture(
|
|
|
439
494
|
if (existingConnector !== newConnector) return false;
|
|
440
495
|
return true;
|
|
441
496
|
});
|
|
442
|
-
return match
|
|
497
|
+
return match
|
|
498
|
+
? {
|
|
499
|
+
id: match.frontmatter.id,
|
|
500
|
+
tombstoneBlocked: match.frontmatter.status === "pending_review" && Boolean(match.frontmatter.blockedBy),
|
|
501
|
+
}
|
|
502
|
+
: null;
|
|
443
503
|
}
|
|
444
|
-
|
|
445
|
-
export async function persistExplicitCapture(
|
|
504
|
+
async function persistExplicitCaptureUnlocked(
|
|
446
505
|
orchestrator: Orchestrator,
|
|
447
506
|
candidate: ValidExplicitCapture,
|
|
448
507
|
source: ExplicitCaptureSource,
|
|
508
|
+
resolvedNamespace: string | undefined,
|
|
509
|
+
storage: StorageManager
|
|
449
510
|
): Promise<{ id: string; duplicateOf?: string; tombstoneBlocked?: boolean }> {
|
|
450
|
-
const
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
return { id: duplicateOf, duplicateOf };
|
|
511
|
+
const duplicate = await findDuplicateExplicitCapture(orchestrator, resolvedNamespace, candidate, storage);
|
|
512
|
+
if (duplicate) {
|
|
513
|
+
return duplicate.tombstoneBlocked
|
|
514
|
+
? { id: duplicate.id, duplicateOf: duplicate.id, tombstoneBlocked: true }
|
|
515
|
+
: { id: duplicate.id, duplicateOf: duplicate.id };
|
|
456
516
|
}
|
|
457
517
|
|
|
458
|
-
const storage = await orchestrator.getStorage(resolvedNamespace);
|
|
459
518
|
// #1645 (review thread yG-): surface the tombstone block so callers
|
|
460
519
|
// (memory_store tool, access-service HTTP/MCP) can report the capture as
|
|
461
520
|
// queued for review instead of a successfully stored active memory.
|
|
@@ -471,9 +530,10 @@ export async function persistExplicitCapture(
|
|
|
471
530
|
...(candidate.sourceConnector ? { sourceConnector: candidate.sourceConnector } : {}),
|
|
472
531
|
...(candidate.sourceReason ? { sourceReason: candidate.sourceReason } : {}),
|
|
473
532
|
},
|
|
474
|
-
{ source: source === "inline" ? "explicit-inline" : "explicit" }
|
|
533
|
+
{ source: source === "inline" ? "explicit-inline" : "explicit" }
|
|
475
534
|
);
|
|
476
|
-
const { id, tombstoneBlocked } = await storage.writeSealedMemory(captureEnvelope);
|
|
535
|
+
const { id, duplicateOf, tombstoneBlocked } = await storage.writeSealedMemory(captureEnvelope);
|
|
536
|
+
if (duplicateOf) return { id, duplicateOf, tombstoneBlocked };
|
|
477
537
|
// #1522: catalog touch handled at the storage chokepoint — the StorageManager's
|
|
478
538
|
// post-write hook records the namespace touch automatically.
|
|
479
539
|
|
|
@@ -481,7 +541,7 @@ export async function persistExplicitCapture(
|
|
|
481
541
|
const event: MemoryLifecycleEvent = {
|
|
482
542
|
eventId: `mle-${randomUUID()}`,
|
|
483
543
|
memoryId: id,
|
|
484
|
-
eventType: "explicit_capture_accepted",
|
|
544
|
+
eventType: tombstoneBlocked ? "explicit_capture_queued" : "explicit_capture_accepted",
|
|
485
545
|
timestamp: created,
|
|
486
546
|
actor: explicitCaptureActor(source),
|
|
487
547
|
reasonCode: candidate.sourceReason,
|
|
@@ -489,7 +549,22 @@ export async function persistExplicitCapture(
|
|
|
489
549
|
};
|
|
490
550
|
await storage.appendMemoryLifecycleEvents([event]);
|
|
491
551
|
|
|
492
|
-
return { id, tombstoneBlocked };
|
|
552
|
+
return { id, duplicateOf, tombstoneBlocked };
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
export async function persistExplicitCapture(
|
|
556
|
+
orchestrator: Orchestrator,
|
|
557
|
+
candidate: ValidExplicitCapture,
|
|
558
|
+
source: ExplicitCaptureSource
|
|
559
|
+
): Promise<{ id: string; duplicateOf?: string; tombstoneBlocked?: boolean }> {
|
|
560
|
+
const resolvedNamespace = candidate.namespacePreResolved
|
|
561
|
+
? asTrimmed(candidate.namespace)
|
|
562
|
+
: resolveExplicitCaptureNamespace(orchestrator, candidate.namespace);
|
|
563
|
+
const storage = await orchestrator.getStorage(resolvedNamespace);
|
|
564
|
+
const persist = () => persistExplicitCaptureUnlocked(orchestrator, candidate, source, resolvedNamespace, storage);
|
|
565
|
+
if (typeof storage.withTombstoneBlockedCaptureWriteLock !== "function") return await persist();
|
|
566
|
+
const lockIdentity = buildExplicitCaptureDedupKey(candidate.content, candidate.category, candidate.sourceConnector);
|
|
567
|
+
return await storage.withTombstoneBlockedCaptureWriteLock(persist, lockIdentity);
|
|
493
568
|
}
|
|
494
569
|
|
|
495
570
|
function buildExplicitCaptureReviewContent(input: ExplicitCaptureInput, reason: string): string {
|
|
@@ -501,14 +576,7 @@ function buildExplicitCaptureReviewContent(input: ExplicitCaptureInput, reason:
|
|
|
501
576
|
const safeTtl = sanitizeReviewMetadata(input.ttl);
|
|
502
577
|
const safeSourceReason = sanitizeReviewMetadata(input.sourceReason);
|
|
503
578
|
const safeTags = sanitizeReviewTags(input.tags);
|
|
504
|
-
const lines = [
|
|
505
|
-
"Explicit capture queued for review.",
|
|
506
|
-
"",
|
|
507
|
-
`Reason: ${reason}`,
|
|
508
|
-
"",
|
|
509
|
-
"Submitted content:",
|
|
510
|
-
safeContent,
|
|
511
|
-
];
|
|
579
|
+
const lines = ["Explicit capture queued for review.", "", `Reason: ${reason}`, "", "Submitted content:", safeContent];
|
|
512
580
|
const metadata = [
|
|
513
581
|
safeCategory ? `Requested category: ${safeCategory}` : undefined,
|
|
514
582
|
safeNamespace ? `Requested namespace: ${safeNamespace}` : undefined,
|
|
@@ -523,20 +591,43 @@ function buildExplicitCaptureReviewContent(input: ExplicitCaptureInput, reason:
|
|
|
523
591
|
return lines.join("\n");
|
|
524
592
|
}
|
|
525
593
|
|
|
594
|
+
function explicitCaptureReviewNamespaces(
|
|
595
|
+
orchestrator: Orchestrator,
|
|
596
|
+
queueNamespace: string | undefined
|
|
597
|
+
): Array<string | undefined> {
|
|
598
|
+
const config = orchestrator.config;
|
|
599
|
+
return [asTrimmed(queueNamespace) ?? asTrimmed(config?.defaultNamespace)];
|
|
600
|
+
}
|
|
601
|
+
|
|
526
602
|
async function findQueuedExplicitCaptureDuplicate(
|
|
527
603
|
orchestrator: Orchestrator,
|
|
528
604
|
namespace: string | undefined,
|
|
529
605
|
content: string,
|
|
530
|
-
sourceConnector?: string
|
|
606
|
+
sourceConnector?: string
|
|
531
607
|
): Promise<string | null> {
|
|
532
|
-
const
|
|
533
|
-
|
|
534
|
-
|
|
608
|
+
const storages = Array.from(
|
|
609
|
+
new Set(
|
|
610
|
+
await Promise.all(
|
|
611
|
+
explicitCaptureReviewNamespaces(orchestrator, namespace).map((reviewNamespace) =>
|
|
612
|
+
orchestrator.getStorage(reviewNamespace)
|
|
613
|
+
)
|
|
614
|
+
)
|
|
615
|
+
)
|
|
616
|
+
);
|
|
617
|
+
const memorySets = await Promise.all(
|
|
618
|
+
storages.map(async (storage) => {
|
|
619
|
+
const hotMemories = await storage.readAllMemories();
|
|
620
|
+
const coldMemories = typeof storage.readAllColdMemories === "function" ? await storage.readAllColdMemories() : [];
|
|
621
|
+
return [...hotMemories, ...coldMemories];
|
|
622
|
+
})
|
|
623
|
+
);
|
|
624
|
+
const existing = memorySets.flat();
|
|
625
|
+
const normalized = normalizeExplicitCaptureContent(content);
|
|
535
626
|
const match = existing.find((memory) => {
|
|
536
627
|
const status = memory.frontmatter.status ?? "active";
|
|
537
628
|
if (status !== "pending_review") return false;
|
|
538
629
|
if (!(memory.frontmatter.tags ?? []).includes("queued-review")) return false;
|
|
539
|
-
if (
|
|
630
|
+
if (normalizeExplicitCaptureContent(memory.content) !== normalized) return false;
|
|
540
631
|
const existingConnector = memory.frontmatter.sourceConnector?.trim() || undefined;
|
|
541
632
|
const newConnector = sourceConnector?.trim() || undefined;
|
|
542
633
|
if (existingConnector !== newConnector) return false;
|
|
@@ -545,84 +636,108 @@ async function findQueuedExplicitCaptureDuplicate(
|
|
|
545
636
|
return match?.frontmatter.id ?? null;
|
|
546
637
|
}
|
|
547
638
|
|
|
639
|
+
export type ExplicitCaptureReviewQueueOptions = {
|
|
640
|
+
/** A server-authorized namespace resolved from the authenticated session. */
|
|
641
|
+
resolvedNamespace?: string;
|
|
642
|
+
};
|
|
643
|
+
|
|
548
644
|
export async function queueExplicitCaptureForReview(
|
|
549
645
|
orchestrator: Orchestrator,
|
|
550
646
|
input: ExplicitCaptureInput,
|
|
551
647
|
source: ExplicitCaptureSource,
|
|
552
648
|
error: unknown,
|
|
649
|
+
options: ExplicitCaptureReviewQueueOptions = {}
|
|
553
650
|
): Promise<{ id: string; duplicateOf?: string }> {
|
|
554
651
|
const reason = sanitizeReviewText(normalizeExplicitCaptureError(error), "explicit capture failed");
|
|
555
652
|
const requestedNamespace = asTrimmed(input.namespace);
|
|
653
|
+
const resolvedNamespace = asTrimmed(options.resolvedNamespace);
|
|
556
654
|
// A caller-pre-authorized namespace (e.g. a session-owned project overlay
|
|
557
655
|
// from the access service) routes directly; otherwise apply the static
|
|
558
656
|
// policy allow-list guard (#1434).
|
|
559
|
-
const queueNamespace =
|
|
560
|
-
|
|
561
|
-
|
|
657
|
+
const queueNamespace =
|
|
658
|
+
resolvedNamespace ??
|
|
659
|
+
((input as { namespacePreResolved?: boolean }).namespacePreResolved
|
|
660
|
+
? requestedNamespace
|
|
661
|
+
: resolveExplicitCaptureReviewNamespace(orchestrator, requestedNamespace));
|
|
562
662
|
const content = buildExplicitCaptureReviewContent(input, reason);
|
|
563
|
-
const duplicateOf = await findQueuedExplicitCaptureDuplicate(orchestrator, queueNamespace, content, input.sourceConnector);
|
|
564
|
-
if (duplicateOf) {
|
|
565
|
-
return { id: duplicateOf, duplicateOf };
|
|
566
|
-
}
|
|
567
|
-
|
|
568
663
|
const requestedCategory = asTrimmed(input.category);
|
|
569
|
-
const reviewCategory =
|
|
570
|
-
|
|
571
|
-
|
|
664
|
+
const reviewCategory =
|
|
665
|
+
requestedCategory && INLINE_ALLOWED_CATEGORIES.has(requestedCategory as MemoryCategory)
|
|
666
|
+
? (requestedCategory as MemoryCategory)
|
|
667
|
+
: "fact";
|
|
572
668
|
const requestedTags = sanitizeReviewTags(input.tags);
|
|
573
669
|
const storage = await orchestrator.getStorage(queueNamespace);
|
|
574
|
-
const
|
|
575
|
-
|
|
670
|
+
const lockIdentity = buildExplicitCaptureDedupKey(content, reviewCategory, input.sourceConnector);
|
|
671
|
+
const queue = async (): Promise<{ id: string; duplicateOf?: string }> => {
|
|
672
|
+
const duplicateOf = await findQueuedExplicitCaptureDuplicate(
|
|
673
|
+
orchestrator,
|
|
674
|
+
queueNamespace,
|
|
576
675
|
content,
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
...(input.sourceConnector ? { sourceConnector: input.sourceConnector } : {}),
|
|
582
|
-
},
|
|
583
|
-
{ source: source === "inline" ? "explicit-inline-review" : "explicit-review" },
|
|
584
|
-
// The review queue is the FALLBACK for a capture that already failed
|
|
585
|
-
// primary validation — it must never be un-queueable. The tag list is
|
|
586
|
-
// machine-assembled (requested tags + the two fixed review tags), so 49+
|
|
587
|
-
// requested tags would push past the 50-tag limit and strict compose
|
|
588
|
-
// would throw, losing the capture (#2014 review round). Salvage clamps
|
|
589
|
-
// instead; the fixed review tags sort first in the assembly above, so
|
|
590
|
-
// they always survive the clamp.
|
|
591
|
-
{ salvage: true },
|
|
592
|
-
);
|
|
593
|
-
const { id: id } = await storage.writeSealedMemory(reviewEnvelope);
|
|
594
|
-
try {
|
|
595
|
-
const created = await storage.getMemoryById(id);
|
|
596
|
-
if (created) {
|
|
597
|
-
await storage.writeMemoryFrontmatter(created, {
|
|
598
|
-
status: "pending_review",
|
|
599
|
-
updated: new Date().toISOString(),
|
|
600
|
-
}, {
|
|
601
|
-
actor: explicitCaptureActor(source),
|
|
602
|
-
reasonCode: reason,
|
|
603
|
-
ruleVersion: "explicit-capture.v1",
|
|
604
|
-
});
|
|
676
|
+
input.sourceConnector
|
|
677
|
+
);
|
|
678
|
+
if (duplicateOf) {
|
|
679
|
+
return { id: duplicateOf, duplicateOf };
|
|
605
680
|
}
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
681
|
+
|
|
682
|
+
const reviewEnvelope = composeMemoryEnvelope(
|
|
683
|
+
{
|
|
684
|
+
content,
|
|
685
|
+
category: reviewCategory,
|
|
686
|
+
confidence: 0.2,
|
|
687
|
+
tags: Array.from(new Set([...EXPLICIT_CAPTURE_REVIEW_TAGS, ...requestedTags])),
|
|
688
|
+
...(sanitizeReviewMetadata(input.entityRef) ? { entityRef: sanitizeReviewMetadata(input.entityRef) } : {}),
|
|
689
|
+
...(input.sourceConnector ? { sourceConnector: input.sourceConnector } : {}),
|
|
690
|
+
},
|
|
691
|
+
{ source: source === "inline" ? "explicit-inline-review" : "explicit-review" },
|
|
692
|
+
// The review queue is the FALLBACK for a capture that already failed
|
|
693
|
+
// primary validation — it must never be un-queueable. The tag list is
|
|
694
|
+
// machine-assembled (requested tags + the two fixed review tags), so 49+
|
|
695
|
+
// requested tags would push past the 50-tag limit and strict compose
|
|
696
|
+
// would throw, losing the capture (#2014 review round). Salvage clamps
|
|
697
|
+
// instead; the fixed review tags sort first in the assembly above, so
|
|
698
|
+
// they always survive the clamp.
|
|
699
|
+
{ salvage: true }
|
|
700
|
+
);
|
|
701
|
+
const { id } = await storage.writeSealedMemory(reviewEnvelope);
|
|
702
|
+
try {
|
|
703
|
+
const created = await storage.getMemoryById(id);
|
|
704
|
+
if (created) {
|
|
705
|
+
await storage.writeMemoryFrontmatter(
|
|
706
|
+
created,
|
|
707
|
+
{
|
|
708
|
+
status: "pending_review",
|
|
709
|
+
updated: new Date().toISOString(),
|
|
710
|
+
},
|
|
711
|
+
{
|
|
712
|
+
actor: explicitCaptureActor(source),
|
|
713
|
+
reasonCode: reason,
|
|
714
|
+
ruleVersion: "explicit-capture.v1",
|
|
715
|
+
}
|
|
716
|
+
);
|
|
717
|
+
}
|
|
718
|
+
} finally {
|
|
719
|
+
// Record the catalog write touch (issue #1499, round 5/6 codex P2; NIhUg).
|
|
720
|
+
// A queued review capture writes memory to the namespace's root (the DEFAULT
|
|
721
|
+
// root when undefined), so its `lastWriteAt` must reflect the write once
|
|
722
|
+
// `writeMemory` returns an id. If the later pending-review frontmatter update
|
|
723
|
+
// fails, the memory file is still durable and must not disappear from
|
|
724
|
+
// writtenSince/maintenance scheduling. Guarded optional hook (rule #33).
|
|
725
|
+
// #1522: catalog touch handled at the storage chokepoint.
|
|
726
|
+
}
|
|
727
|
+
const event: MemoryLifecycleEvent = {
|
|
728
|
+
eventId: `mle-${randomUUID()}`,
|
|
729
|
+
memoryId: id,
|
|
730
|
+
eventType: "explicit_capture_queued",
|
|
731
|
+
timestamp: new Date().toISOString(),
|
|
732
|
+
actor: explicitCaptureActor(source),
|
|
733
|
+
reasonCode: reason,
|
|
734
|
+
ruleVersion: "explicit-capture.v1",
|
|
735
|
+
};
|
|
736
|
+
await storage.appendMemoryLifecycleEvents([event]);
|
|
737
|
+
return { id };
|
|
623
738
|
};
|
|
624
|
-
|
|
625
|
-
return
|
|
739
|
+
if (typeof storage.withTombstoneBlockedCaptureWriteLock !== "function") return await queue();
|
|
740
|
+
return await storage.withTombstoneBlockedCaptureWriteLock(queue, lockIdentity);
|
|
626
741
|
}
|
|
627
742
|
|
|
628
743
|
export function shouldSkipImplicitExtraction(cfg: Pick<PluginConfig, "captureMode">): boolean {
|
|
@@ -632,3 +747,287 @@ export function shouldSkipImplicitExtraction(cfg: Pick<PluginConfig, "captureMod
|
|
|
632
747
|
export function shouldProcessInlineExplicitCapture(cfg: Pick<PluginConfig, "captureMode">): boolean {
|
|
633
748
|
return cfg.captureMode !== "implicit";
|
|
634
749
|
}
|
|
750
|
+
|
|
751
|
+
export interface InlineExplicitCaptureProcessRequest {
|
|
752
|
+
content: string;
|
|
753
|
+
captureMode: CaptureMode;
|
|
754
|
+
dedupeKeys?: readonly string[];
|
|
755
|
+
namespace?: string;
|
|
756
|
+
namespacePreResolved?: boolean;
|
|
757
|
+
reviewNamespace?: string;
|
|
758
|
+
reviewNamespacePreResolved?: boolean;
|
|
759
|
+
sourceConnector?: string;
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
export interface InlineExplicitCaptureProcessResult {
|
|
763
|
+
content: string;
|
|
764
|
+
processed: number;
|
|
765
|
+
accepted: number;
|
|
766
|
+
queued: number;
|
|
767
|
+
duplicates: number;
|
|
768
|
+
failed: number;
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
export interface InlineExplicitCaptureProcessorOptions {
|
|
772
|
+
sourceConnector?: string;
|
|
773
|
+
maxDedupeKeys?: number;
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
const DEFAULT_INLINE_CAPTURE_DEDUPE_KEY_LIMIT = 1024;
|
|
777
|
+
|
|
778
|
+
export class InlineExplicitCaptureProcessor {
|
|
779
|
+
private readonly observedKeys = new Set<string>();
|
|
780
|
+
private readonly observedKeyOrder: string[] = [];
|
|
781
|
+
private readonly maxDedupeKeys: number;
|
|
782
|
+
private readonly processingTails = new Map<string, Promise<void>>();
|
|
783
|
+
|
|
784
|
+
constructor(
|
|
785
|
+
private readonly orchestrator: Orchestrator,
|
|
786
|
+
private readonly options: InlineExplicitCaptureProcessorOptions = {}
|
|
787
|
+
) {
|
|
788
|
+
const requestedLimit = options.maxDedupeKeys ?? DEFAULT_INLINE_CAPTURE_DEDUPE_KEY_LIMIT;
|
|
789
|
+
this.maxDedupeKeys =
|
|
790
|
+
Number.isFinite(requestedLimit) && requestedLimit > 0
|
|
791
|
+
? Math.max(1, Math.floor(requestedLimit))
|
|
792
|
+
: DEFAULT_INLINE_CAPTURE_DEDUPE_KEY_LIMIT;
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
private enqueue<T>(key: string, operation: () => Promise<T>): Promise<T> {
|
|
796
|
+
const previous = this.processingTails.get(key) ?? Promise.resolve();
|
|
797
|
+
let release!: () => void;
|
|
798
|
+
const current = new Promise<void>((resolve) => {
|
|
799
|
+
release = resolve;
|
|
800
|
+
});
|
|
801
|
+
this.processingTails.set(key, current);
|
|
802
|
+
return previous.then(operation).finally(() => {
|
|
803
|
+
if (this.processingTails.get(key) === current) this.processingTails.delete(key);
|
|
804
|
+
release();
|
|
805
|
+
});
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
private buildDedupeKeys(
|
|
809
|
+
dedupeKeys: readonly string[] | undefined,
|
|
810
|
+
input: ExplicitCaptureInput,
|
|
811
|
+
namespacePreResolved: boolean,
|
|
812
|
+
fallbackReviewNamespace?: string
|
|
813
|
+
): { replayKeys: string[]; validationFailureKey: string } {
|
|
814
|
+
let fallbackNamespace: string | undefined;
|
|
815
|
+
let effectiveNamespace = asTrimmed(input.namespace);
|
|
816
|
+
if (!namespacePreResolved) {
|
|
817
|
+
try {
|
|
818
|
+
effectiveNamespace = resolveExplicitCaptureNamespace(this.orchestrator, effectiveNamespace);
|
|
819
|
+
} catch {
|
|
820
|
+
fallbackNamespace = asTrimmed(fallbackReviewNamespace) ?? this.orchestrator.config.defaultNamespace;
|
|
821
|
+
effectiveNamespace = fallbackNamespace;
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
const requestedNamespace = asTrimmed(input.namespace);
|
|
825
|
+
const namespaceScope =
|
|
826
|
+
fallbackNamespace === undefined
|
|
827
|
+
? effectiveNamespace ?? this.orchestrator.config.defaultNamespace
|
|
828
|
+
: `${fallbackNamespace}\u0000requested:${requestedNamespace ?? ""}`;
|
|
829
|
+
const sourceConnectorScope = asTrimmed(input.sourceConnector) ?? "";
|
|
830
|
+
const deliveryKeys = Array.from(
|
|
831
|
+
new Set((dedupeKeys ?? []).map((key) => asTrimmed(key)).filter((key): key is string => key !== undefined))
|
|
832
|
+
).sort();
|
|
833
|
+
const noteHash = createHash("sha256")
|
|
834
|
+
.update(
|
|
835
|
+
JSON.stringify({
|
|
836
|
+
content: normalizeExplicitCaptureContent(input.content),
|
|
837
|
+
category: asTrimmed(input.category) ?? "fact",
|
|
838
|
+
})
|
|
839
|
+
)
|
|
840
|
+
.digest("hex");
|
|
841
|
+
const validationTags = Array.from(
|
|
842
|
+
new Set((input.tags ?? []).map((tag) => asTrimmed(tag)).filter((tag): tag is string => tag !== undefined))
|
|
843
|
+
).sort();
|
|
844
|
+
const validationHash = createHash("sha256")
|
|
845
|
+
.update(
|
|
846
|
+
JSON.stringify({
|
|
847
|
+
content: asTrimmed(input.content) ?? "",
|
|
848
|
+
category: asTrimmed(input.category) ?? "fact",
|
|
849
|
+
confidence:
|
|
850
|
+
input.confidence === undefined
|
|
851
|
+
? "default"
|
|
852
|
+
: Number.isFinite(input.confidence)
|
|
853
|
+
? input.confidence
|
|
854
|
+
: "invalid",
|
|
855
|
+
namespace: asTrimmed(input.namespace),
|
|
856
|
+
tags: validationTags,
|
|
857
|
+
entityRef: asTrimmed(input.entityRef),
|
|
858
|
+
sourceReason: asTrimmed(input.sourceReason),
|
|
859
|
+
ttl: asTrimmed(input.ttl),
|
|
860
|
+
deliveryKeys,
|
|
861
|
+
})
|
|
862
|
+
)
|
|
863
|
+
.digest("hex");
|
|
864
|
+
const identityPrefix = `${namespaceScope}\u0000${sourceConnectorScope}\u0000`;
|
|
865
|
+
const fallbackKey = `${identityPrefix}fallback:inline-memory-note:${noteHash}`;
|
|
866
|
+
return {
|
|
867
|
+
replayKeys:
|
|
868
|
+
deliveryKeys.length > 0
|
|
869
|
+
? deliveryKeys.map((key) => `${identityPrefix}${key}:inline-memory-note:${noteHash}`)
|
|
870
|
+
: [fallbackKey],
|
|
871
|
+
validationFailureKey: `${identityPrefix}validation:inline-memory-note:${validationHash}`,
|
|
872
|
+
};
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
private isNamespacePolicyAuthorized(namespace: string | undefined): boolean {
|
|
876
|
+
try {
|
|
877
|
+
resolveExplicitCaptureNamespace(this.orchestrator, namespace);
|
|
878
|
+
return true;
|
|
879
|
+
} catch {
|
|
880
|
+
return false;
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
private resolveReviewFallbackNamespace(
|
|
885
|
+
request: InlineExplicitCaptureProcessRequest,
|
|
886
|
+
input: ExplicitCaptureInput
|
|
887
|
+
): string | undefined {
|
|
888
|
+
if (request.namespacePreResolved === true || this.isNamespacePolicyAuthorized(input.namespace)) return undefined;
|
|
889
|
+
return request.reviewNamespacePreResolved === true
|
|
890
|
+
? asTrimmed(request.reviewNamespace)
|
|
891
|
+
: this.orchestrator.config.defaultNamespace;
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
|
|
895
|
+
private remember(keys: readonly string[]): void {
|
|
896
|
+
for (const key of keys) {
|
|
897
|
+
if (this.observedKeys.has(key)) continue;
|
|
898
|
+
this.observedKeys.add(key);
|
|
899
|
+
this.observedKeyOrder.push(key);
|
|
900
|
+
}
|
|
901
|
+
while (this.observedKeyOrder.length > this.maxDedupeKeys) {
|
|
902
|
+
const expired = this.observedKeyOrder.shift();
|
|
903
|
+
if (expired) this.observedKeys.delete(expired);
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
private async processNote(
|
|
908
|
+
request: InlineExplicitCaptureProcessRequest,
|
|
909
|
+
input: ExplicitCaptureInput,
|
|
910
|
+
replayKeys: readonly string[],
|
|
911
|
+
validationFailureKey: string
|
|
912
|
+
): Promise<Pick<InlineExplicitCaptureProcessResult, "processed" | "accepted" | "queued" | "duplicates" | "failed">> {
|
|
913
|
+
const serializationKey = replayKeys.at(-1) ?? validationFailureKey;
|
|
914
|
+
return this.enqueue(serializationKey, async () => {
|
|
915
|
+
if (replayKeys.some((key) => this.observedKeys.has(key)) || this.observedKeys.has(validationFailureKey)) {
|
|
916
|
+
return { processed: 0, accepted: 0, queued: 0, duplicates: 1, failed: 0 };
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
const classifyPersisted = (
|
|
920
|
+
persisted: Awaited<ReturnType<typeof persistExplicitCapture>>
|
|
921
|
+
): Pick<InlineExplicitCaptureProcessResult, "processed" | "accepted" | "queued" | "duplicates" | "failed"> => {
|
|
922
|
+
this.remember(replayKeys);
|
|
923
|
+
if (persisted.duplicateOf) {
|
|
924
|
+
return { processed: 1, accepted: 0, queued: 0, duplicates: 1, failed: 0 };
|
|
925
|
+
}
|
|
926
|
+
if (persisted.tombstoneBlocked) {
|
|
927
|
+
this.orchestrator.requestQmdMaintenanceForTool("inline.memory_note.review");
|
|
928
|
+
return { processed: 1, accepted: 0, queued: 1, duplicates: 0, failed: 0 };
|
|
929
|
+
}
|
|
930
|
+
this.orchestrator.requestQmdMaintenanceForTool("inline.memory_note");
|
|
931
|
+
return { processed: 1, accepted: 1, queued: 0, duplicates: 0, failed: 0 };
|
|
932
|
+
};
|
|
933
|
+
const persistCandidate = async (candidate: ValidExplicitCapture) =>
|
|
934
|
+
classifyPersisted(await persistExplicitCapture(this.orchestrator, candidate, "inline"));
|
|
935
|
+
|
|
936
|
+
let validationSucceeded = false;
|
|
937
|
+
let candidate: ValidExplicitCapture | undefined;
|
|
938
|
+
try {
|
|
939
|
+
candidate = validateExplicitCaptureInput(input);
|
|
940
|
+
if (request.namespacePreResolved === true) candidate.namespacePreResolved = true;
|
|
941
|
+
validationSucceeded = true;
|
|
942
|
+
return await persistCandidate(candidate);
|
|
943
|
+
} catch (error) {
|
|
944
|
+
let captureError: unknown = error;
|
|
945
|
+
if (validationSucceeded && candidate && isTombstoneBlockedCaptureWriteLockBusy(error)) {
|
|
946
|
+
try {
|
|
947
|
+
return await persistCandidate(candidate);
|
|
948
|
+
} catch (retryError) {
|
|
949
|
+
if (isTombstoneBlockedCaptureWriteLockBusy(retryError)) {
|
|
950
|
+
log.warn(
|
|
951
|
+
`explicit inline capture deferred after tombstone write lock contention: ${normalizeExplicitCaptureError(retryError)}`
|
|
952
|
+
);
|
|
953
|
+
return { processed: 0, accepted: 0, queued: 0, duplicates: 0, failed: 1 };
|
|
954
|
+
}
|
|
955
|
+
captureError = retryError;
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
if (validationSucceeded && !isUnsupportedExplicitCaptureNamespaceError(captureError)) {
|
|
959
|
+
log.warn(`explicit inline capture persistence failed: ${normalizeExplicitCaptureError(captureError)}`);
|
|
960
|
+
return { processed: 0, accepted: 0, queued: 0, duplicates: 0, failed: 1 };
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
const queueInput = request.namespacePreResolved === true ? { ...input, namespacePreResolved: true } : input;
|
|
964
|
+
const reviewNamespace = this.resolveReviewFallbackNamespace(request, input);
|
|
965
|
+
try {
|
|
966
|
+
const review = await queueExplicitCaptureForReview(
|
|
967
|
+
this.orchestrator,
|
|
968
|
+
queueInput,
|
|
969
|
+
"inline",
|
|
970
|
+
captureError,
|
|
971
|
+
reviewNamespace ? { resolvedNamespace: reviewNamespace } : undefined
|
|
972
|
+
);
|
|
973
|
+
this.remember(validationSucceeded ? replayKeys : [validationFailureKey]);
|
|
974
|
+
if (review.duplicateOf) {
|
|
975
|
+
return { processed: 1, accepted: 0, queued: 0, duplicates: 1, failed: 0 };
|
|
976
|
+
}
|
|
977
|
+
this.orchestrator.requestQmdMaintenanceForTool("inline.memory_note.review");
|
|
978
|
+
return { processed: 1, accepted: 0, queued: 1, duplicates: 0, failed: 0 };
|
|
979
|
+
} catch (queueError) {
|
|
980
|
+
log.warn(
|
|
981
|
+
`explicit inline capture rejected: ${normalizeExplicitCaptureError(captureError)}; review queue fallback failed: ${normalizeExplicitCaptureError(queueError)}`
|
|
982
|
+
);
|
|
983
|
+
return { processed: 0, accepted: 0, queued: 0, duplicates: 0, failed: 1 };
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
});
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
async process(request: InlineExplicitCaptureProcessRequest): Promise<InlineExplicitCaptureProcessResult> {
|
|
990
|
+
if (!shouldProcessInlineExplicitCapture({ captureMode: request.captureMode })) {
|
|
991
|
+
return { content: request.content, processed: 0, accepted: 0, queued: 0, duplicates: 0, failed: 0 };
|
|
992
|
+
}
|
|
993
|
+
if (request.namespacePreResolved === true && !asTrimmed(request.namespace)) {
|
|
994
|
+
throw new Error("namespacePreResolved requires a resolved namespace");
|
|
995
|
+
}
|
|
996
|
+
if (request.reviewNamespacePreResolved === true && !asTrimmed(request.reviewNamespace)) {
|
|
997
|
+
throw new Error("reviewNamespacePreResolved requires a resolved namespace");
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
const notes = parseInlineExplicitCaptureCandidates(request.content);
|
|
1001
|
+
const content = hasInlineExplicitCaptureMarkup(request.content)
|
|
1002
|
+
? stripInlineExplicitCaptureNotes(request.content)
|
|
1003
|
+
: request.content;
|
|
1004
|
+
const sourceConnector = asTrimmed(request.sourceConnector) ?? asTrimmed(this.options.sourceConnector);
|
|
1005
|
+
let processed = 0;
|
|
1006
|
+
let accepted = 0;
|
|
1007
|
+
let queued = 0;
|
|
1008
|
+
let duplicates = 0;
|
|
1009
|
+
let failed = 0;
|
|
1010
|
+
|
|
1011
|
+
for (const note of notes) {
|
|
1012
|
+
const input: ExplicitCaptureInput = {
|
|
1013
|
+
...note,
|
|
1014
|
+
...(request.namespace !== undefined ? { namespace: request.namespace } : {}),
|
|
1015
|
+
...(sourceConnector ? { sourceConnector } : {}),
|
|
1016
|
+
};
|
|
1017
|
+
const { replayKeys, validationFailureKey } = this.buildDedupeKeys(
|
|
1018
|
+
request.dedupeKeys,
|
|
1019
|
+
input,
|
|
1020
|
+
request.namespacePreResolved === true,
|
|
1021
|
+
this.resolveReviewFallbackNamespace(request, input)
|
|
1022
|
+
);
|
|
1023
|
+
const result = await this.processNote(request, input, replayKeys, validationFailureKey);
|
|
1024
|
+
processed += result.processed;
|
|
1025
|
+
accepted += result.accepted;
|
|
1026
|
+
queued += result.queued;
|
|
1027
|
+
duplicates += result.duplicates;
|
|
1028
|
+
failed += result.failed;
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
return { content, processed, accepted, queued, duplicates, failed };
|
|
1032
|
+
}
|
|
1033
|
+
}
|