@remnic/core 9.65.1 → 9.65.3
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 +44 -44
- package/dist/access-admin-ops-surface.js +36 -35
- package/dist/access-authorization-probe.d.ts +39 -40
- package/dist/access-authorization-probe.js +37 -36
- package/dist/access-boundary.d.ts +41 -185
- package/dist/access-boundary.js +36 -35
- package/dist/access-cli.js +110 -107
- package/dist/access-cli.js.map +1 -1
- package/dist/access-coding-context-resolution.d.ts +3 -1
- package/dist/access-extraction-force-flush.d.ts +25 -25
- package/dist/access-extraction-force-flush.js +36 -35
- package/dist/access-health-types.d.ts +3 -1
- package/dist/access-http-lcm-compaction.d.ts +39 -39
- package/dist/access-http-lifecycle-flush.d.ts +40 -41
- package/dist/access-http-offline-stream.d.ts +25 -25
- package/dist/access-http-query.d.ts +4 -2
- package/dist/access-http-query.js +38 -37
- package/dist/access-http.d.ts +51 -44
- package/dist/access-http.js +57 -56
- package/dist/access-identity-continuity-surface.d.ts +12 -12
- package/dist/access-identity-continuity-surface.js +36 -35
- package/dist/access-lcm-surface.d.ts +27 -27
- package/dist/access-lcm-surface.js +36 -35
- package/dist/access-mcp-admin-tools.d.ts +9 -1
- package/dist/access-mcp-admin-tools.js +3 -1
- package/dist/access-mcp-output-schemas.js +1 -1
- package/dist/access-mcp.d.ts +39 -39
- package/dist/access-mcp.js +50 -47
- package/dist/access-memory-search-fanout.d.ts +4 -2
- package/dist/{access-namespace-preflight-BKUnKsBv.d.ts → access-namespace-preflight-B7Usyz-K.d.ts} +1 -1
- package/dist/access-namespace-preflight.d.ts +5 -3
- package/dist/access-namespace-preflight.js +36 -35
- package/dist/access-observe-write-surface.d.ts +39 -39
- package/dist/access-observe-write-surface.js +36 -35
- package/dist/access-offline-manifest.d.ts +12 -12
- package/dist/access-offline-manifest.js +5 -5
- package/dist/access-operations-batch.js +41 -40
- package/dist/access-operations.d.ts +46 -47
- package/dist/access-operations.js +44 -43
- package/dist/access-recall-concurrency.d.ts +40 -40
- package/dist/access-recall-concurrency.js +36 -35
- package/dist/access-recall-response.d.ts +24 -24
- package/dist/access-recall-response.js +36 -35
- package/dist/access-recall-surface.d.ts +24 -24
- package/dist/access-recall-surface.js +36 -35
- package/dist/access-schema.d.ts +139 -139
- package/dist/access-schema.js +3 -3
- package/dist/{access-service-B1Lbb-nQ.d.ts → access-service-DkaIG3n8.d.ts} +688 -140
- package/dist/access-service-helpers.d.ts +40 -40
- package/dist/access-service.d.ts +47 -47
- package/dist/access-service.js +36 -35
- package/dist/access-surface-catalog.d.ts +41 -42
- package/dist/access-surface-catalog.js +44 -37
- package/dist/access-surface-catalog.js.map +1 -1
- package/dist/access-wearables-meetings-surface.d.ts +5 -3
- package/dist/action-confidence.d.ts +3 -1
- package/dist/active-memory-bridge.d.ts +3 -1
- package/dist/active-recall.d.ts +3 -1
- package/dist/active-recall.js +11 -1
- package/dist/active-recall.js.map +1 -1
- package/dist/adapters/index.js +4 -4
- package/dist/adapters/registry.js +2 -2
- package/dist/ambient-provenance.d.ts +3 -1
- package/dist/artifact-search.d.ts +3 -1
- package/dist/{auto-sync-5L7EUZ27.js → auto-sync-H64ZCNIR.js} +13 -5
- package/dist/{auto-sync-5L7EUZ27.js.map → auto-sync-H64ZCNIR.js.map} +1 -1
- package/dist/behavior-learner.d.ts +3 -1
- package/dist/behavior-signals.d.ts +3 -1
- package/dist/bootstrap.d.ts +12 -12
- package/dist/briefing-window.d.ts +4 -2
- package/dist/briefing.d.ts +4 -2
- package/dist/briefing.js +16 -15
- package/dist/buffer-surprise-report.d.ts +3 -1
- package/dist/buffer-turn-helpers.d.ts +3 -1
- package/dist/buffer.d.ts +4 -2
- package/dist/buffer.js +4 -4
- package/dist/bulk-import/index.d.ts +5 -3
- package/dist/calibration.d.ts +3 -1
- package/dist/calibration.js +1 -1
- package/dist/capabilities.d.ts +3 -1
- package/dist/{capsule-crypto-7FJQINUR.js → capsule-crypto-YO5QJ6L3.js} +2 -2
- package/dist/{catalog-DjVhJoRM.d.ts → catalog-CX2BZEJh.d.ts} +1 -1
- package/dist/causal-behavior.d.ts +1 -1
- package/dist/causal-consolidation.d.ts +3 -1
- package/dist/causal-consolidation.js +20 -19
- package/dist/causal-consolidation.js.map +1 -1
- package/dist/causal-trajectory-graph.d.ts +1 -1
- package/dist/{chunk-MIBKEW7A.js → chunk-2VXWFYJE.js} +3 -3
- package/dist/{chunk-3YTTAGPB.js → chunk-3DVBKTSO.js} +2 -2
- package/dist/{chunk-EIR5VLIH.js → chunk-4K7IW3F7.js} +2 -2
- package/dist/chunk-4K7IW3F7.js.map +1 -0
- package/dist/{chunk-JTSVMJHV.js → chunk-4XDGYCZI.js} +3 -3
- package/dist/chunk-573TEZCD.js +88 -0
- package/dist/chunk-573TEZCD.js.map +1 -0
- package/dist/{chunk-HQSBYI5X.js → chunk-57HVIG5U.js} +2 -2
- package/dist/{chunk-PMVPCZJZ.js → chunk-6433HV72.js} +2 -2
- package/dist/{chunk-N6TRHP72.js → chunk-6TAX66IA.js} +2 -2
- package/dist/{chunk-AGKIG3YA.js → chunk-6VALIZKG.js} +2 -2
- package/dist/{chunk-YQ3CBLL4.js → chunk-76PL4XLG.js} +512 -8
- package/dist/chunk-76PL4XLG.js.map +1 -0
- package/dist/{chunk-4NZHHGVL.js → chunk-7BDZTALN.js} +4 -1
- package/dist/chunk-7BDZTALN.js.map +1 -0
- package/dist/{chunk-NS5ETDYD.js → chunk-7BEPVIH3.js} +3 -3
- package/dist/{chunk-JG22O53B.js → chunk-7CBKAQIG.js} +2 -1
- package/dist/chunk-7CBKAQIG.js.map +1 -0
- package/dist/{chunk-LYIZXQYY.js → chunk-7KFIMGMP.js} +2 -2
- package/dist/{chunk-LWVO2QZV.js → chunk-7QOO4LGC.js} +56 -56
- package/dist/chunk-AAXJNINV.js +55 -0
- package/dist/chunk-AAXJNINV.js.map +1 -0
- package/dist/{chunk-56BSXSHC.js → chunk-AB3A7EFH.js} +31 -2
- package/dist/{chunk-56BSXSHC.js.map → chunk-AB3A7EFH.js.map} +1 -1
- package/dist/{chunk-CCTVAVTT.js → chunk-AUO2LJ3K.js} +2 -2
- package/dist/{chunk-ALCJRQ72.js → chunk-AYDDB3XI.js} +2 -2
- package/dist/{chunk-3TMYV3LP.js → chunk-AYYGRVRP.js} +5 -5
- package/dist/{chunk-W6TSGWYS.js → chunk-AZAA5DNP.js} +2 -2
- package/dist/{chunk-WTOE7CW4.js → chunk-BKYPJUQL.js} +122 -16
- package/dist/chunk-BKYPJUQL.js.map +1 -0
- package/dist/{chunk-6JJT5AKI.js → chunk-BQUEAVNW.js} +2 -2
- package/dist/{chunk-2NLLXCJG.js → chunk-BXLOS5AJ.js} +2 -2
- package/dist/{chunk-CEH2AFXG.js → chunk-C24UZSFW.js} +6 -6
- package/dist/{chunk-ZPN7W3OP.js → chunk-CDUUDBWJ.js} +3 -3
- package/dist/{chunk-35NN3ZFA.js → chunk-CTVWELNK.js} +21 -3
- package/dist/chunk-CTVWELNK.js.map +1 -0
- package/dist/{chunk-Y23B2QXP.js → chunk-D27J626P.js} +2 -2
- package/dist/{chunk-2572MRP2.js → chunk-DHTQP334.js} +2 -2
- package/dist/{chunk-HJQZQUPC.js → chunk-DJZWNKFO.js} +5 -5
- package/dist/{chunk-X7Y7WX73.js → chunk-DQEMWVMT.js} +1 -1
- package/dist/{chunk-VJ6ECBIP.js → chunk-E4MVQQVX.js} +2 -2
- package/dist/{chunk-SYJ6EKKE.js → chunk-EIVIB73I.js} +2 -2
- package/dist/{chunk-HUNSEO5U.js → chunk-EV3YFLCB.js} +2 -2
- package/dist/{chunk-GEULB2VI.js → chunk-FC7TIRFS.js} +21 -1
- package/dist/chunk-FC7TIRFS.js.map +1 -0
- package/dist/{chunk-VPGRXEAB.js → chunk-G57LQRP6.js} +2 -2
- package/dist/{chunk-BC4QD3FM.js → chunk-GN55KU2P.js} +2 -2
- package/dist/{chunk-4W6INNXO.js → chunk-GUXS322H.js} +2 -2
- package/dist/{chunk-DBXZZIDU.js → chunk-H5FW4S5Q.js} +434 -693
- package/dist/chunk-H5FW4S5Q.js.map +1 -0
- package/dist/chunk-I32NKXE2.js +879 -0
- package/dist/chunk-I32NKXE2.js.map +1 -0
- package/dist/{chunk-3MADV4YC.js → chunk-IQSC3V2E.js} +2 -2
- package/dist/{chunk-UYCIS6V5.js → chunk-JCWY73FK.js} +2 -2
- package/dist/{chunk-T42L3S6A.js → chunk-LBWQ65SX.js} +4 -4
- package/dist/{chunk-MSTNSVXI.js → chunk-LDBFXD4F.js} +2 -1
- package/dist/chunk-LDBFXD4F.js.map +1 -0
- package/dist/{chunk-CEQF6L5K.js → chunk-LUNJ3WFO.js} +9 -7
- package/dist/chunk-LUNJ3WFO.js.map +1 -0
- package/dist/{chunk-256FBCVX.js → chunk-M4AHBM72.js} +7 -7
- package/dist/{chunk-YEQEGSJA.js → chunk-MBDJJTAT.js} +3 -3
- package/dist/{chunk-IAY7YBXS.js → chunk-MRTUN665.js} +2 -2
- package/dist/{chunk-JFJ3MHGV.js → chunk-NXKJHSDN.js} +1 -1
- package/dist/chunk-NXKJHSDN.js.map +1 -0
- package/dist/{chunk-R3AAOKR2.js → chunk-NZWEEALH.js} +2 -2
- package/dist/{chunk-I2HTKOOP.js → chunk-ODZG724P.js} +2 -2
- package/dist/{chunk-7QFBLYFP.js → chunk-OJD4WS2E.js} +3 -3
- package/dist/{chunk-CUS6S7PN.js → chunk-OLWTCWQ2.js} +9 -9
- package/dist/chunk-OLWTCWQ2.js.map +1 -0
- package/dist/{chunk-53ENM6F6.js → chunk-P4DHENRE.js} +2 -2
- package/dist/{chunk-DEFI2QBT.js → chunk-PAYGC3BD.js} +52 -22
- package/dist/chunk-PAYGC3BD.js.map +1 -0
- package/dist/{chunk-KPBLJCCV.js → chunk-PDVQ5JU3.js} +2 -2
- package/dist/{chunk-7BY7V4YX.js → chunk-Q4WONDMJ.js} +4 -3
- package/dist/{chunk-7BY7V4YX.js.map → chunk-Q4WONDMJ.js.map} +1 -1
- package/dist/{chunk-ESCQ6LY7.js → chunk-QFMO32TO.js} +3 -3
- package/dist/{chunk-HQSPXTQO.js → chunk-QXIQPCCN.js} +1 -1
- package/dist/{chunk-CFRBTATE.js → chunk-R47WINCY.js} +3 -3
- package/dist/{chunk-G72IPMR7.js → chunk-R4OCGPSH.js} +2 -2
- package/dist/{chunk-LCU5R6TQ.js → chunk-RAIUTAZZ.js} +22 -3
- package/dist/chunk-RAIUTAZZ.js.map +1 -0
- package/dist/chunk-RIAM7DND.js +167 -0
- package/dist/chunk-RIAM7DND.js.map +1 -0
- package/dist/{chunk-VZVP2W4O.js → chunk-RK4WXUI2.js} +2 -2
- package/dist/{chunk-JBPKEARU.js → chunk-RKNJBZ55.js} +4 -4
- package/dist/{chunk-6L27N6YI.js → chunk-S7Q5CM7E.js} +2 -2
- package/dist/{chunk-CTQRPANJ.js → chunk-SVSRPG6M.js} +2 -2
- package/dist/{chunk-GXWDWOWP.js → chunk-TDKSCV62.js} +2 -2
- package/dist/{chunk-S2VBSTTA.js → chunk-THG4GYIE.js} +2 -2
- package/dist/{chunk-IXRC7EQG.js → chunk-TR7DGXGI.js} +2 -2
- package/dist/{chunk-63FHSH7E.js → chunk-TYUS5TLG.js} +4 -4
- package/dist/{chunk-7PWWGYGO.js → chunk-URHCUXKI.js} +2 -2
- package/dist/{chunk-Z54TXZ2F.js → chunk-VA5KMSAE.js} +16 -7
- package/dist/chunk-VA5KMSAE.js.map +1 -0
- package/dist/{chunk-KQAFEZQX.js → chunk-VDX2J7OX.js} +2 -2
- package/dist/{chunk-MUJ4QFUQ.js → chunk-W4BMEGQA.js} +4 -4
- package/dist/{chunk-5HK3CJAR.js → chunk-W675UJCZ.js} +2 -2
- package/dist/{chunk-3PXVYJMC.js → chunk-WN5FSGLE.js} +2077 -215
- package/dist/chunk-WN5FSGLE.js.map +1 -0
- package/dist/{chunk-3RHFGGI5.js → chunk-YF6UHWOV.js} +30 -10
- package/dist/chunk-YF6UHWOV.js.map +1 -0
- package/dist/{chunk-X5SDZMO7.js → chunk-YHCBJULX.js} +2 -2
- package/dist/{chunk-5UYSCYR7.js → chunk-Z4RPXHJH.js} +4 -4
- package/dist/{cli-Pulnz-qm.d.ts → cli-ChKeYMZv.d.ts} +5 -5
- package/dist/cli.d.ts +25 -25
- package/dist/cli.js +81 -78
- package/dist/coding/pre-action-gate.d.ts +2 -60
- package/dist/compounding/engine.d.ts +4 -2
- package/dist/compounding/engine.js +17 -16
- package/dist/compounding/preference-consolidator.d.ts +3 -1
- package/dist/compression-optimizer.d.ts +3 -1
- package/dist/config.d.ts +3 -1
- package/dist/config.js +11 -1
- package/dist/connectors/codex-materialize-runner.d.ts +3 -1
- package/dist/connectors/codex-materialize-runner.js +16 -15
- package/dist/connectors/codex-materialize.d.ts +3 -1
- package/dist/connectors/index.d.ts +3 -1
- package/dist/connectors/index.js +16 -15
- package/dist/consolidation-provenance-check.d.ts +4 -2
- package/dist/consolidation-undo.d.ts +4 -2
- package/dist/contradiction/index.d.ts +10 -270
- package/dist/contradiction/index.js +20 -7
- package/dist/{contradiction-review-QKJH5DKD.js → contradiction-review-WRVLRWHS.js} +6 -2
- package/dist/{contradiction-scan-VOTQRRSX.js → contradiction-scan-AKUWPX5N.js} +5 -3
- package/dist/converge-config.d.ts +3 -1
- package/dist/convergence-refresh.d.ts +4 -2
- package/dist/convergence-refresh.js +18 -17
- package/dist/conversation-index/backend.d.ts +3 -1
- package/dist/conversation-index/chunker.d.ts +3 -1
- package/dist/conversation-index/faiss-adapter.d.ts +3 -1
- package/dist/conversation-index/indexer.d.ts +3 -1
- package/dist/conversation-index/search.d.ts +3 -1
- package/dist/corpus-watermark.d.ts +3 -1
- package/dist/corpus-watermark.js +18 -17
- package/dist/cross-lingual-recall.d.ts +3 -1
- package/dist/day-summary.d.ts +3 -1
- package/dist/delinearize.d.ts +3 -1
- package/dist/dependency-propagation-config.d.ts +3 -1
- package/dist/{dependency-propagation-delivery-Co17gxYb.d.ts → dependency-propagation-delivery-B8ZMBBCY.d.ts} +1 -1
- package/dist/direct-answer-wiring.d.ts +3 -1
- package/dist/direct-answer.d.ts +3 -1
- package/dist/embedding-fallback.d.ts +3 -1
- package/dist/enrichment/index.d.ts +3 -1
- package/dist/entity-origin-fields.d.ts +3 -1
- package/dist/entity-retrieval.d.ts +4 -2
- package/dist/entity-retrieval.js +16 -15
- package/dist/entity-schema.d.ts +3 -1
- package/dist/episodic-context.d.ts +3 -1
- package/dist/explicit-capture.d.ts +12 -12
- package/dist/explicit-capture.js +7 -6
- package/dist/external-wiki-access.d.ts +41 -42
- package/dist/external-wiki-access.js +37 -36
- package/dist/external-wiki-collection-registration.d.ts +3 -1
- package/dist/external-wiki-collection.d.ts +3 -1
- package/dist/external-wiki-mcp-tools.d.ts +40 -40
- package/dist/extraction-error-classification.d.ts +3 -1
- package/dist/extraction-faithfulness.d.ts +3 -1
- package/dist/extraction-judge-telemetry.d.ts +3 -1
- package/dist/extraction-judge-training.d.ts +3 -1
- package/dist/extraction-judge.d.ts +3 -1
- package/dist/extraction-judge.js +2 -2
- package/dist/extraction-liveness.d.ts +3 -1
- package/dist/extraction-normalization.d.ts +3 -1
- package/dist/extraction-normalization.js +2 -2
- package/dist/extraction-prompt.d.ts +4 -2
- package/dist/extraction-prompt.js +1 -1
- package/dist/extraction-source-grounding-rules.d.ts +3 -1
- package/dist/extraction-source-grounding.d.ts +3 -1
- package/dist/extraction.d.ts +3 -1
- package/dist/extraction.js +22 -21
- package/dist/fallback-llm.d.ts +3 -1
- package/dist/graph-dashboard-diff.d.ts +3 -1
- package/dist/graph-dashboard-key.d.ts +3 -1
- package/dist/graph-dashboard-parser.d.ts +3 -1
- package/dist/graph-edge-reinforcement.d.ts +3 -1
- package/dist/graph-path-reconstruction.d.ts +3 -1
- package/dist/graph-path-scoring.d.ts +3 -1
- package/dist/graph-snapshot.d.ts +3 -1
- package/dist/graph.d.ts +3 -1
- package/dist/harmonic-retrieval.js +1 -1
- package/dist/importance.d.ts +3 -1
- package/dist/importers/index.d.ts +3 -1
- package/dist/in-flight-reads.d.ts +3 -1
- package/dist/index-CexDrJzC.d.ts +284 -0
- package/dist/index.d.ts +946 -707
- package/dist/index.js +253 -151
- package/dist/intent.d.ts +3 -1
- package/dist/lcm/engine.d.ts +3 -1
- package/dist/lcm/engine.js +3 -3
- package/dist/lcm/index.d.ts +3 -1
- package/dist/lcm/index.js +6 -6
- package/dist/lcm/tools.d.ts +3 -1
- package/dist/lifecycle.d.ts +3 -1
- package/dist/live-connectors-runner.d.ts +3 -1
- package/dist/local-llm.d.ts +3 -1
- package/dist/local-model-endpoint.d.ts +3 -1
- package/dist/maintenance/memory-governance.d.ts +3 -1
- package/dist/maintenance/memory-governance.js +16 -15
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.d.ts +4 -2
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +16 -15
- package/dist/maintenance/rebuild-memory-projection.d.ts +4 -2
- package/dist/maintenance/rebuild-memory-projection.js +17 -16
- package/dist/{maintenance-DiSLtVN7.d.ts → maintenance-nkdtSrVr.d.ts} +3 -3
- package/dist/mcp-memory-inspector-app.d.ts +37 -37
- package/dist/mcp-read-only-tools.js +1 -1
- package/dist/memory-action-policy.d.ts +3 -1
- package/dist/memory-cache.d.ts +3 -1
- package/dist/memory-lifecycle-ledger-utils.d.ts +3 -1
- package/dist/memory-projection-store.d.ts +3 -1
- package/dist/memory-provenance.d.ts +3 -1
- package/dist/memory-snapshot.d.ts +3 -1
- package/dist/memory-subject-cli.d.ts +177 -0
- package/dist/memory-subject-cli.js +16 -0
- package/dist/memory-subject.d.ts +128 -0
- package/dist/memory-subject.js +44 -0
- package/dist/memory-subject.js.map +1 -0
- package/dist/memory-worth-outcomes.d.ts +7 -4
- package/dist/memory-worth-outcomes.js +1 -1
- package/dist/models-json.d.ts +3 -1
- package/dist/namespaces/migrate.d.ts +5 -3
- package/dist/namespaces/migrate.js +17 -16
- package/dist/namespaces/principal.d.ts +3 -1
- package/dist/namespaces/search.d.ts +3 -1
- package/dist/namespaces/search.js +6 -6
- package/dist/namespaces/storage.d.ts +5 -3
- package/dist/namespaces/storage.js +16 -15
- package/dist/native-knowledge.d.ts +3 -1
- package/dist/offline-sync-impression-drain.d.ts +3 -1
- package/dist/operator-doctor-corpus.d.ts +3 -1
- package/dist/operator-doctor-corpus.js +19 -18
- package/dist/operator-doctor-replica.d.ts +3 -1
- package/dist/operator-toolkit.d.ts +5 -3
- package/dist/operator-toolkit.js +29 -24
- package/dist/orchestration/compression-guideline-coordinator.d.ts +4 -2
- package/dist/orchestration/maintenance.d.ts +6 -4
- package/dist/orchestration/maintenance.js +22 -21
- package/dist/{orchestrator-DzCaxNS4.d.ts → orchestrator-Bvq1-RQs.d.ts} +23 -9
- package/dist/orchestrator.d.ts +12 -12
- package/dist/orchestrator.js +111 -108
- package/dist/patterns-cli.d.ts +3 -1
- package/dist/{pipeline-HJ8DKbFl.d.ts → pipeline-C8vfIiTX.d.ts} +1 -1
- package/dist/policy-runtime.d.ts +3 -1
- package/dist/proactive-contention.d.ts +3 -1
- package/dist/provenance.d.ts +3 -1
- package/dist/{public-http-DwXauTX3.d.ts → public-http-Cy0aYbys.d.ts} +1 -1
- package/dist/{qmd-BzxluoLt.d.ts → qmd-B9p1SZTZ.d.ts} +1 -1
- package/dist/qmd-preflight.d.ts +4 -2
- package/dist/qmd-recall-cache.d.ts +3 -1
- package/dist/qmd.d.ts +4 -2
- package/dist/qmd.js +4 -4
- package/dist/recall-concurrency-config.d.ts +3 -1
- package/dist/recall-disclosure-escalation.d.ts +3 -1
- package/dist/recall-explain-renderer.d.ts +3 -1
- package/dist/recall-explain-renderer.js +3 -3
- package/dist/recall-memory-map.d.ts +3 -1
- package/dist/recall-planner-llm.d.ts +3 -1
- package/dist/recall-state.d.ts +3 -1
- package/dist/recall-tag-filter.d.ts +3 -1
- package/dist/recall-timings.d.ts +3 -1
- package/dist/recall-xray-cli.d.ts +3 -1
- package/dist/recall-xray-cli.js +4 -4
- package/dist/recall-xray-renderer.d.ts +3 -1
- package/dist/recall-xray-renderer.js +3 -3
- package/dist/recall-xray.d.ts +3 -1
- package/dist/recall-xray.js +2 -2
- package/dist/reconcile/cursor.d.ts +3 -1
- package/dist/reconcile/manifest.d.ts +3 -1
- package/dist/reconcile/manifest.js +3 -3
- package/dist/reconcile/plan.d.ts +3 -1
- package/dist/replay/normalizers/chatgpt.d.ts +3 -1
- package/dist/replay/normalizers/claude.d.ts +3 -1
- package/dist/replay/normalizers/openclaw.d.ts +3 -1
- package/dist/replay/normalizers/shared.d.ts +3 -1
- package/dist/replay/runner.d.ts +3 -1
- package/dist/replay/types.d.ts +3 -1
- package/dist/replica-divergence.d.ts +3 -1
- package/dist/replica-peers-config.d.ts +3 -1
- package/dist/resolution-4552BNRV.js +36 -0
- package/dist/resolution-4552BNRV.js.map +1 -0
- package/dist/resolve-auth-token.d.ts +3 -1
- package/dist/resume-bundles.js +9 -2
- package/dist/retrieval-agents.d.ts +4 -2
- package/dist/retrieval-tiers.d.ts +3 -1
- package/dist/routing/engine.d.ts +3 -1
- package/dist/routing/store.d.ts +3 -1
- package/dist/salvage-envelope.d.ts +3 -1
- package/dist/salvage-envelope.js +10 -2
- package/dist/schemas.d.ts +130 -116
- package/dist/schemas.js +1 -1
- package/dist/{scope-profiles-CEMZz0t5.d.ts → scope-profiles-DLrRxFWR.d.ts} +1 -1
- package/dist/search/embed-helper.d.ts +3 -1
- package/dist/search/factory.d.ts +3 -1
- package/dist/search/factory.js +5 -5
- package/dist/search/index.d.ts +3 -1
- package/dist/search/index.js +9 -9
- package/dist/search/lancedb-backend.d.ts +3 -1
- package/dist/search/meilisearch-backend.d.ts +3 -1
- package/dist/search/noop-backend.d.ts +3 -1
- package/dist/search/orama-backend.d.ts +3 -1
- package/dist/search/port.d.ts +3 -1
- package/dist/search/remote-backend.d.ts +3 -1
- package/dist/{semantic-TddhF0y7.d.ts → semantic-BalR6EXU.d.ts} +1 -1
- package/dist/{semantic-consolidation-D9dYj6vu.d.ts → semantic-consolidation-DlN6xe20.d.ts} +1 -1
- package/dist/semantic-consolidation.d.ts +4 -2
- package/dist/semantic-consolidation.js +17 -16
- package/dist/semantic-rule-promotion.js +16 -15
- package/dist/semantic-rule-verifier.d.ts +3 -1
- package/dist/semantic-rule-verifier.js +16 -15
- package/dist/{service-_qgST_0y.d.ts → service-GkYyVfm9.d.ts} +2 -2
- package/dist/session-observer-bands.d.ts +3 -1
- package/dist/session-observer-state.d.ts +3 -1
- package/dist/shared-context/manager.d.ts +13 -11
- package/dist/signal.d.ts +3 -1
- package/dist/source-agent-qualifier.d.ts +3 -1
- package/dist/source-agent-qualifier.js +18 -17
- package/dist/{storage-B9lbNTYE.d.ts → storage-BIDzuKIn.d.ts} +3 -2
- package/dist/storage.d.ts +4 -2
- package/dist/storage.js +18 -17
- package/dist/subject-config.d.ts +40 -0
- package/dist/subject-config.js +20 -0
- package/dist/subject-config.js.map +1 -0
- package/dist/summarizer.d.ts +3 -1
- package/dist/summary-snapshot.d.ts +3 -1
- package/dist/support-passport/index.d.ts +43 -43
- package/dist/support-passport/index.js +39 -38
- package/dist/temporal-supersession.d.ts +4 -2
- package/dist/temporal-timeline-recall.d.ts +3 -1
- package/dist/temporal-validity.d.ts +3 -1
- package/dist/threading.d.ts +3 -1
- package/dist/tier-migration.d.ts +4 -2
- package/dist/tier-routing.d.ts +3 -1
- package/dist/topics.d.ts +3 -1
- package/dist/transcript.d.ts +3 -1
- package/dist/transfer/backup.js +2 -2
- package/dist/transfer/capsule-export.js +2 -2
- package/dist/transfer/capsule-import.js +2 -2
- package/dist/transfer/types.d.ts +86 -86
- package/dist/trust-score-stage.d.ts +3 -1
- package/dist/trust-score.d.ts +3 -1
- package/dist/{types-BrEzuxPA.d.ts → types-BxAcOl5Q.d.ts} +360 -32
- package/dist/types.d.ts +3 -1
- package/dist/types.js +1 -1
- package/dist/utility-runtime.d.ts +3 -1
- package/dist/verified-recall.js +16 -15
- package/dist/who-knows-cli.d.ts +40 -40
- package/dist/who-knows-cli.js +17 -16
- package/dist/who-knows.d.ts +42 -42
- package/dist/who-knows.js +16 -15
- package/dist/write-envelope.d.ts +7 -2
- package/dist/write-envelope.js +10 -1
- package/package.json +2 -2
- package/scripts/build-with-heap.mjs +1 -1
- package/src/access-boundary.ts +6 -0
- package/src/access-http.ts +6 -5
- package/src/access-mcp-admin-tools.ts +38 -0
- package/src/access-mcp-output-schemas.ts +20 -0
- package/src/access-mcp.ts +8 -8
- package/src/access-operations-batch.ts +13 -0
- package/src/access-service.ts +112 -0
- package/src/access-surface-catalog.test.ts +24 -0
- package/src/access-surface-catalog.ts +6 -0
- package/src/coding/action-gate.test.ts +341 -0
- package/src/coding/action-gate.ts +556 -0
- package/src/config.ts +15 -17
- package/src/contradiction/contradiction-review.ts +50 -4
- package/src/contradiction/contradiction-scan.ts +14 -3
- package/src/contradiction/resolution.ts +50 -2
- package/src/extraction-prompt.ts +1 -0
- package/src/extraction.ts +4 -1
- package/src/index.ts +9 -9
- package/src/mcp-read-only-tools.ts +1 -0
- package/src/memory-subject-cli.ts +135 -0
- package/src/memory-subject.test.ts +48 -0
- package/src/memory-subject.ts +314 -0
- package/src/memory-worth-outcomes.test.ts +6 -2
- package/src/memory-worth-outcomes.ts +11 -7
- package/src/operator-toolkit.ts +1 -1
- package/src/orchestration/extraction-persist.ts +48 -48
- package/src/orchestration/recall-internal.ts +6 -5
- package/src/orchestration/recall-rerank-coordinator.ts +123 -4
- package/src/orchestration/recall-result-formatter.ts +6 -2
- package/src/orchestration/recall-search-pipeline.ts +14 -1
- package/src/preferences/drift-config.ts +118 -0
- package/src/preferences/drift-recall.ts +133 -0
- package/src/preferences/drift-types.ts +87 -0
- package/src/preferences/index.ts +12 -0
- package/src/preferences/preference-drift.test.ts +690 -0
- package/src/preferences/preference-drift.ts +724 -0
- package/src/procedural/library-maintenance.test.ts +582 -0
- package/src/procedural/library-maintenance.ts +546 -0
- package/src/procedural/maintenance-config.ts +78 -0
- package/src/procedural/procedure-recall.ts +29 -1
- package/src/procedural/procedure-stats.ts +20 -0
- package/src/review/review-deck-access.test.ts +325 -0
- package/src/review/review-deck-http-base.ts +133 -0
- package/src/review/review-deck-mutation.test.ts +417 -0
- package/src/review/review-deck-mutation.ts +677 -0
- package/src/review/review-deck-snapshot.ts +196 -0
- package/src/review/review-deck-surface.ts +117 -0
- package/src/review/review-deck.test.ts +347 -0
- package/src/review/review-deck.ts +311 -0
- package/src/schemas.ts +7 -0
- package/src/spaces/index.ts +38 -2
- package/src/storage.ts +10 -8
- package/src/subject-config.ts +80 -0
- package/src/types.ts +33 -32
- package/src/write-envelope.test.ts +14 -0
- package/src/write-envelope.ts +24 -3
- package/dist/chunk-35NN3ZFA.js.map +0 -1
- package/dist/chunk-3PXVYJMC.js.map +0 -1
- package/dist/chunk-3RHFGGI5.js.map +0 -1
- package/dist/chunk-4NZHHGVL.js.map +0 -1
- package/dist/chunk-CEQF6L5K.js.map +0 -1
- package/dist/chunk-CUS6S7PN.js.map +0 -1
- package/dist/chunk-DBXZZIDU.js.map +0 -1
- package/dist/chunk-DEFI2QBT.js.map +0 -1
- package/dist/chunk-EIR5VLIH.js.map +0 -1
- package/dist/chunk-GEULB2VI.js.map +0 -1
- package/dist/chunk-JFJ3MHGV.js.map +0 -1
- package/dist/chunk-JG22O53B.js.map +0 -1
- package/dist/chunk-LCU5R6TQ.js.map +0 -1
- package/dist/chunk-MDVBCPOS.js +0 -402
- package/dist/chunk-MDVBCPOS.js.map +0 -1
- package/dist/chunk-MSTNSVXI.js.map +0 -1
- package/dist/chunk-WTOE7CW4.js.map +0 -1
- package/dist/chunk-YQ3CBLL4.js.map +0 -1
- package/dist/chunk-Z54TXZ2F.js.map +0 -1
- package/dist/resolution-DKMBPXHI.js +0 -18
- /package/dist/{capsule-crypto-7FJQINUR.js.map → capsule-crypto-YO5QJ6L3.js.map} +0 -0
- /package/dist/{chunk-MIBKEW7A.js.map → chunk-2VXWFYJE.js.map} +0 -0
- /package/dist/{chunk-3YTTAGPB.js.map → chunk-3DVBKTSO.js.map} +0 -0
- /package/dist/{chunk-JTSVMJHV.js.map → chunk-4XDGYCZI.js.map} +0 -0
- /package/dist/{chunk-HQSBYI5X.js.map → chunk-57HVIG5U.js.map} +0 -0
- /package/dist/{chunk-PMVPCZJZ.js.map → chunk-6433HV72.js.map} +0 -0
- /package/dist/{chunk-N6TRHP72.js.map → chunk-6TAX66IA.js.map} +0 -0
- /package/dist/{chunk-AGKIG3YA.js.map → chunk-6VALIZKG.js.map} +0 -0
- /package/dist/{chunk-NS5ETDYD.js.map → chunk-7BEPVIH3.js.map} +0 -0
- /package/dist/{chunk-LYIZXQYY.js.map → chunk-7KFIMGMP.js.map} +0 -0
- /package/dist/{chunk-LWVO2QZV.js.map → chunk-7QOO4LGC.js.map} +0 -0
- /package/dist/{chunk-CCTVAVTT.js.map → chunk-AUO2LJ3K.js.map} +0 -0
- /package/dist/{chunk-ALCJRQ72.js.map → chunk-AYDDB3XI.js.map} +0 -0
- /package/dist/{chunk-3TMYV3LP.js.map → chunk-AYYGRVRP.js.map} +0 -0
- /package/dist/{chunk-W6TSGWYS.js.map → chunk-AZAA5DNP.js.map} +0 -0
- /package/dist/{chunk-6JJT5AKI.js.map → chunk-BQUEAVNW.js.map} +0 -0
- /package/dist/{chunk-2NLLXCJG.js.map → chunk-BXLOS5AJ.js.map} +0 -0
- /package/dist/{chunk-CEH2AFXG.js.map → chunk-C24UZSFW.js.map} +0 -0
- /package/dist/{chunk-ZPN7W3OP.js.map → chunk-CDUUDBWJ.js.map} +0 -0
- /package/dist/{chunk-Y23B2QXP.js.map → chunk-D27J626P.js.map} +0 -0
- /package/dist/{chunk-2572MRP2.js.map → chunk-DHTQP334.js.map} +0 -0
- /package/dist/{chunk-HJQZQUPC.js.map → chunk-DJZWNKFO.js.map} +0 -0
- /package/dist/{chunk-X7Y7WX73.js.map → chunk-DQEMWVMT.js.map} +0 -0
- /package/dist/{chunk-VJ6ECBIP.js.map → chunk-E4MVQQVX.js.map} +0 -0
- /package/dist/{chunk-SYJ6EKKE.js.map → chunk-EIVIB73I.js.map} +0 -0
- /package/dist/{chunk-HUNSEO5U.js.map → chunk-EV3YFLCB.js.map} +0 -0
- /package/dist/{chunk-VPGRXEAB.js.map → chunk-G57LQRP6.js.map} +0 -0
- /package/dist/{chunk-BC4QD3FM.js.map → chunk-GN55KU2P.js.map} +0 -0
- /package/dist/{chunk-4W6INNXO.js.map → chunk-GUXS322H.js.map} +0 -0
- /package/dist/{chunk-3MADV4YC.js.map → chunk-IQSC3V2E.js.map} +0 -0
- /package/dist/{chunk-UYCIS6V5.js.map → chunk-JCWY73FK.js.map} +0 -0
- /package/dist/{chunk-T42L3S6A.js.map → chunk-LBWQ65SX.js.map} +0 -0
- /package/dist/{chunk-256FBCVX.js.map → chunk-M4AHBM72.js.map} +0 -0
- /package/dist/{chunk-YEQEGSJA.js.map → chunk-MBDJJTAT.js.map} +0 -0
- /package/dist/{chunk-IAY7YBXS.js.map → chunk-MRTUN665.js.map} +0 -0
- /package/dist/{chunk-R3AAOKR2.js.map → chunk-NZWEEALH.js.map} +0 -0
- /package/dist/{chunk-I2HTKOOP.js.map → chunk-ODZG724P.js.map} +0 -0
- /package/dist/{chunk-7QFBLYFP.js.map → chunk-OJD4WS2E.js.map} +0 -0
- /package/dist/{chunk-53ENM6F6.js.map → chunk-P4DHENRE.js.map} +0 -0
- /package/dist/{chunk-KPBLJCCV.js.map → chunk-PDVQ5JU3.js.map} +0 -0
- /package/dist/{chunk-ESCQ6LY7.js.map → chunk-QFMO32TO.js.map} +0 -0
- /package/dist/{chunk-HQSPXTQO.js.map → chunk-QXIQPCCN.js.map} +0 -0
- /package/dist/{chunk-CFRBTATE.js.map → chunk-R47WINCY.js.map} +0 -0
- /package/dist/{chunk-G72IPMR7.js.map → chunk-R4OCGPSH.js.map} +0 -0
- /package/dist/{chunk-VZVP2W4O.js.map → chunk-RK4WXUI2.js.map} +0 -0
- /package/dist/{chunk-JBPKEARU.js.map → chunk-RKNJBZ55.js.map} +0 -0
- /package/dist/{chunk-6L27N6YI.js.map → chunk-S7Q5CM7E.js.map} +0 -0
- /package/dist/{chunk-CTQRPANJ.js.map → chunk-SVSRPG6M.js.map} +0 -0
- /package/dist/{chunk-GXWDWOWP.js.map → chunk-TDKSCV62.js.map} +0 -0
- /package/dist/{chunk-S2VBSTTA.js.map → chunk-THG4GYIE.js.map} +0 -0
- /package/dist/{chunk-IXRC7EQG.js.map → chunk-TR7DGXGI.js.map} +0 -0
- /package/dist/{chunk-63FHSH7E.js.map → chunk-TYUS5TLG.js.map} +0 -0
- /package/dist/{chunk-7PWWGYGO.js.map → chunk-URHCUXKI.js.map} +0 -0
- /package/dist/{chunk-KQAFEZQX.js.map → chunk-VDX2J7OX.js.map} +0 -0
- /package/dist/{chunk-MUJ4QFUQ.js.map → chunk-W4BMEGQA.js.map} +0 -0
- /package/dist/{chunk-5HK3CJAR.js.map → chunk-W675UJCZ.js.map} +0 -0
- /package/dist/{chunk-X5SDZMO7.js.map → chunk-YHCBJULX.js.map} +0 -0
- /package/dist/{chunk-5UYSCYR7.js.map → chunk-Z4RPXHJH.js.map} +0 -0
- /package/dist/{contradiction-review-QKJH5DKD.js.map → contradiction-review-WRVLRWHS.js.map} +0 -0
- /package/dist/{contradiction-scan-VOTQRRSX.js.map → contradiction-scan-AKUWPX5N.js.map} +0 -0
- /package/dist/{resolution-DKMBPXHI.js.map → memory-subject-cli.js.map} +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/write-envelope.ts"],"sourcesContent":["/**\n * Sealed memory-write envelope (issue #1989, umbrella #1988).\n *\n * THE single stamping point for cross-cutting memory-creation fields.\n *\n * Why this exists: PR #1852 needed 27 rework commits because adding one\n * cross-cutting field (`sourceConnector`) required hand-editing every\n * write call site and every idempotency payload, and reviewers discovered\n * missed sites one round at a time. This module removes the fan-out:\n *\n * 1. `composeMemoryEnvelope()` is the only way to mint a\n * `SealedMemoryEnvelope` — the brand is a non-exported `unique symbol`,\n * so a hand-built object literal fails `tsc` (`npm run check-types`\n * is already a required repo-wide gate; the type system IS the fence).\n * 2. `buildWriteIdempotencyPayload()` derives every access-surface\n * idempotency payload from ONE ordered field registry\n * (`WRITE_FINGERPRINT_FIELDS`). Adding a fingerprint-relevant field is\n * a one-line registry change picked up by every surface; forgetting to\n * classify a new `MemoryWriteInput` field is a compile error (see the\n * exhaustiveness guards at the bottom of this file).\n *\n * PR1 scope (no call-site changes): the composer, the registry, and the\n * payload builder, fully tested. PR2–PR4 migrate storage entry points and\n * the write call sites onto this module (see #1989 for the slicing).\n */\n\nimport type { MemoryCategory } from \"./types.js\";\nimport { normalizeTags } from \"./recall-tag-filter.js\";\nimport { parseFlexibleIsoTimestamp } from \"./utils/iso-timestamp.js\";\nimport { assemblePersistedBody } from \"./structured-attributes.js\";\n\n// ---------------------------------------------------------------------------\n// Input surface\n// ---------------------------------------------------------------------------\n\n/**\n * Every cross-cutting memory-creation field lives HERE and only here.\n *\n * When you add a field you MUST also add it to either\n * `WRITE_FINGERPRINT_FIELDS` or `FINGERPRINT_EXEMPT_FIELDS` — the\n * exhaustiveness guard below makes forgetting a compile error.\n */\nexport interface MemoryWriteInput {\n /** Raw memory content. Must be non-empty after trimming. */\n content: string;\n /** Memory category — validated against the canonical category set. */\n category: MemoryCategory;\n /** Free-form labels; normalized (trim/dedupe) and capped, see TAG_LIMITS. */\n tags?: string[];\n /** String→string structured attributes; non-string values are rejected. */\n structuredAttributes?: Record<string, string>;\n /** Entity file reference (e.g. `person-jane-doe`). */\n entityRef?: string;\n /** Extraction confidence in [0, 1]. */\n confidence?: number;\n /** Opaque TTL expression (e.g. `90d` or an ISO date) — validated non-empty. */\n ttl?: string;\n /** Event-time validity start (ISO 8601). */\n validAt?: string;\n /** Connector identity that produced this write (issue #1852). */\n sourceConnector?: string;\n /** Origin class stamped at creation time (issue #1955). */\n origin?: string;\n /** Human-readable provenance note. */\n sourceReason?: string;\n}\n\n/** Context supplied by the write path, not by the payload author. */\nexport interface WriteContext {\n /** Frontmatter `source` string (e.g. `extraction`, `wearable:bee`). */\n source: string;\n /** Clock override for tests. */\n now?: () => Date;\n}\n\n/** Scope facts that participate in idempotency but never in frontmatter. */\nexport interface FingerprintScope {\n /** Access surface performing the write (e.g. `memory_store`, `observe`). */\n surface: string;\n namespace?: string;\n principal?: string;\n codingContext?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Normalization limits (single source of truth — docs/tags.md contract)\n// ---------------------------------------------------------------------------\n\nexport const TAG_LIMITS = Object.freeze({\n /** Maximum number of tags persisted per memory. */\n maxTags: 50,\n /** Maximum length of a single tag. */\n maxTagLength: 256,\n});\n\nexport const STRUCTURED_ATTRIBUTE_LIMITS = Object.freeze({\n /** Maximum number of structured attributes per memory. */\n maxEntries: 64,\n /** Maximum length of one attribute key. */\n maxKeyLength: 128,\n /** Maximum length of one attribute value. */\n maxValueLength: 1024,\n});\n\nconst MEMORY_CATEGORY_TABLE: Record<MemoryCategory, true> = {\n fact: true,\n preference: true,\n correction: true,\n entity: true,\n decision: true,\n relationship: true,\n principle: true,\n commitment: true,\n moment: true,\n skill: true,\n rule: true,\n procedure: true,\n reasoning_trace: true,\n};\n\nconst MEMORY_CATEGORY_NAMES = Object.keys(MEMORY_CATEGORY_TABLE).sort();\n\nexport function isMemoryCategory(value: string): value is MemoryCategory {\n return Object.prototype.hasOwnProperty.call(MEMORY_CATEGORY_TABLE, value);\n}\n\n// ---------------------------------------------------------------------------\n// Sealed envelope\n// ---------------------------------------------------------------------------\n\ndeclare const sealed: unique symbol;\n\n/**\n * A normalized, validated memory-write envelope.\n *\n * Opaque: only `composeMemoryEnvelope()` can mint one. Storage creation\n * entry points accept this type (PR2), so every write path is forced\n * through the composer at compile time.\n */\nexport interface SealedMemoryEnvelope {\n readonly [sealed]: true;\n /**\n * Caller-supplied content VERBATIM (validated non-empty-after-trim).\n * `StorageManager.writeMemory` persists content byte-for-byte (callers\n * that trim, e.g. explicit capture, do so before composing), so the\n * envelope must not transform it — the earlier compose-time trim/sanitize\n * broke byte-parity with extraction writes and was removed in PR2.\n */\n readonly content: string;\n /**\n * The exact body persistence will write: attribute-suffix enrichment then\n * combined sanitization, produced by the SAME `assemblePersistedBody`\n * helper `writeMemory` uses (issue #1989 PR2; AGENTS.md §13). This is the\n * form write-idempotency fingerprints hash.\n */\n readonly persistedBody: string;\n /** Injection patterns matched during body assembly (empty = clean). */\n readonly sanitizeViolations: readonly string[];\n /**\n * Salvage-mode hygiene notes (empty in strict mode): each dropped/clamped\n * invalid optional field is recorded here — visible, never silent\n * (repo rule 34). Callers persisting machine-generated input log these.\n */\n readonly salvageNotes: readonly string[];\n readonly category: MemoryCategory;\n readonly tags: readonly string[];\n /**\n * CANONICAL attributes (keys trim+lowercase, values trim) — the form\n * fingerprints and downstream consumers use; stable across caller casing.\n */\n readonly structuredAttributes: Readonly<Record<string, string>> | undefined;\n /**\n * The validated ORIGINAL attribute map, byte-preserved. `writeMemory`\n * persists frontmatter attributes raw (canonicalizing only the body\n * suffix via normalizeAttributePairs) — a pre-existing raw/canonical\n * inconsistency (supersession keys read the raw form). The sealed path\n * preserves those bytes; canonicalizing the frontmatter at the storage\n * boundary is a follow-up once the legacy path retires (#1989 PR4).\n */\n readonly rawStructuredAttributes: Readonly<Record<string, string>> | undefined;\n readonly entityRef: string | undefined;\n readonly confidence: number | undefined;\n readonly ttl: string | undefined;\n readonly validAt: string | undefined;\n readonly sourceConnector: string | undefined;\n readonly origin: string | undefined;\n readonly sourceReason: string | undefined;\n readonly source: string;\n /** ISO timestamp the envelope was composed (from ctx.now). */\n readonly composedAt: string;\n}\n\nfunction fail(field: string, message: string): never {\n throw new Error(`composeMemoryEnvelope: ${field} ${message}`);\n}\n\n/**\n * Compose-time input hygiene mode (issue #1989 PR2, review round on #2014).\n *\n * - `strict` (default): every invalid field throws — correct for OPERATOR\n * and API input (explicit capture, memory_store), where a bad value is a\n * caller bug that must surface.\n * - `salvage`: for MACHINE-GENERATED input (LLM extraction, wearable\n * ingestion). One malformed fact from an extractor must not abort a whole\n * persistence batch that legacy `writeMemory` would have accepted.\n * Invalid tags/attributes/optional fields are DROPPED with a note pushed\n * to `envelope.salvageNotes` — visible, never silent (repo rule 34);\n * callers log the notes. Content, category, ctx.source, and validAt stay\n * FATAL in both modes (legacy writeMemory also throws on bad validAt, and\n * a write without valid content/category cannot proceed).\n */\nexport interface ComposeEnvelopeOptions {\n salvage?: boolean;\n}\n\n/** Internal reporter: throws in strict mode, records a note in salvage. */\ninterface Hygiene {\n salvage: boolean;\n notes: string[];\n flag(field: string, message: string): void;\n}\n\nfunction makeHygiene(salvage: boolean): Hygiene {\n const notes: string[] = [];\n return {\n salvage,\n notes,\n flag(field: string, message: string): void {\n if (!salvage) fail(field, message);\n notes.push(`${field} ${message}`);\n },\n };\n}\n\n/**\n * Validate and CANONICALIZE `validAt` via the repo's shared flexible ISO\n * parser (`utils/iso-timestamp.ts`) — the same family durable writes use, so\n * the envelope can never accept a value storage would reject or reinterpret\n * (review findings on #1998: no second validation convention; reduced\n * precision like `2026-07-17T09:30Z` is valid; offsets are bounded ±14:00;\n * `Date.parse` alone is not an ISO validator).\n *\n * Canonical form is the epoch round-trip (`Date.toISOString()`), so\n * fingerprints are identical for `...T09:30Z`, `...T09:30:00.000Z`, and the\n * same instant expressed with an offset (§13 hash-consistency).\n */\nfunction validateIsoTimestamp(field: string, value: string): string {\n const trimmed = value.trim();\n if (trimmed.length === 0) fail(field, \"must be a non-empty ISO 8601 timestamp\");\n const epochMs = parseFlexibleIsoTimestamp(trimmed);\n if (epochMs === null) {\n fail(\n field,\n `is not a valid ISO 8601 date/timestamp: ${JSON.stringify(value)} (date-only, reduced precision, Z, and ±HH:MM offsets up to ±14:00 are accepted; calendar overflow is not)`,\n );\n }\n return new Date(epochMs).toISOString();\n}\n\nfunction normalizeEnvelopeTags(raw: string[] | undefined, hygiene: Hygiene): readonly string[] {\n if (raw === undefined) return Object.freeze([]);\n if (!Array.isArray(raw)) {\n hygiene.flag(\"tags\", \"must be an array of strings — dropped\");\n return Object.freeze([]);\n }\n const usable: string[] = [];\n for (const tag of raw) {\n if (typeof tag !== \"string\") {\n hygiene.flag(\"tags\", `entries must be strings (got ${typeof tag})`);\n continue;\n }\n if (tag.trim().length > TAG_LIMITS.maxTagLength) {\n hygiene.flag(\"tags\", `entry exceeds ${TAG_LIMITS.maxTagLength} characters: ${JSON.stringify(tag.slice(0, 40))}…`);\n continue;\n }\n usable.push(tag);\n }\n let normalized = normalizeTags(usable) ?? [];\n if (normalized.length > TAG_LIMITS.maxTags) {\n hygiene.flag(\"tags\", `exceed the ${TAG_LIMITS.maxTags}-tag limit (got ${normalized.length}) — keeping the first ${TAG_LIMITS.maxTags}`);\n normalized = normalized.slice(0, TAG_LIMITS.maxTags);\n }\n return Object.freeze(normalized);\n}\n\nfunction normalizeStructuredAttributes(\n raw: Record<string, string> | undefined,\n hygiene: Hygiene,\n): { canonical: Readonly<Record<string, string>>; raw: Readonly<Record<string, string>> } | undefined {\n if (raw === undefined) return undefined;\n if (\n raw === null ||\n typeof raw !== \"object\" ||\n Array.isArray(raw) ||\n (Object.getPrototypeOf(raw) !== Object.prototype && Object.getPrototypeOf(raw) !== null)\n ) {\n // Map/Date/class instances have empty Object.entries() and would\n // silently normalize to \"no attributes\" (review finding on #1998\n // round 5) — never a plain pass-through.\n hygiene.flag(\"structuredAttributes\", \"must be a plain object of string values (Map, Date, and class instances are rejected) — dropped\");\n return undefined;\n }\n let entries = Object.entries(raw);\n if (entries.length === 0) return undefined;\n if (entries.length > STRUCTURED_ATTRIBUTE_LIMITS.maxEntries) {\n hygiene.flag(\n \"structuredAttributes\",\n `exceed the ${STRUCTURED_ATTRIBUTE_LIMITS.maxEntries}-entry limit (got ${entries.length}) — keeping the first ${STRUCTURED_ATTRIBUTE_LIMITS.maxEntries} in insertion order`,\n );\n entries = entries.slice(0, STRUCTURED_ATTRIBUTE_LIMITS.maxEntries);\n }\n // Keys that collide with Object.prototype machinery are REJECTED outright:\n // `hashAccessIdempotencyPayload`'s stableStringify rebuilds objects with\n // plain assignment, where a \"__proto__\" own property silently vanishes —\n // so such a key can never round-trip through the fingerprint faithfully\n // (review finding on #1998 round 3). Rejection beats smuggling (§1/§39).\n const FORBIDDEN_ATTRIBUTE_KEYS = [\"__proto__\", \"constructor\", \"prototype\"];\n const out: Record<string, string> = Object.create(null);\n const survivingOriginal: Array<[string, string]> = [];\n for (const [key, value] of entries) {\n // Canonicalize exactly like storage's normalizeAttributePairs\n // (storage.ts:1277): keys trim+lowercase, values trim — so the\n // fingerprint and the persisted searchable form can never diverge\n // (review finding on #1998; AGENTS.md §13 hash-consistency).\n const cleanKey = key.trim().toLowerCase();\n if (cleanKey.length === 0) {\n hygiene.flag(\"structuredAttributes\", \"contain an empty key\");\n continue;\n }\n if (cleanKey.length > STRUCTURED_ATTRIBUTE_LIMITS.maxKeyLength) {\n hygiene.flag(\"structuredAttributes\", `key exceeds ${STRUCTURED_ATTRIBUTE_LIMITS.maxKeyLength} characters: ${JSON.stringify(cleanKey.slice(0, 40))}…`);\n continue;\n }\n if (typeof value !== \"string\") {\n hygiene.flag(\"structuredAttributes\", `value for ${JSON.stringify(cleanKey)} must be a string (got ${typeof value}) — stringify numbers/booleans at the call site`);\n continue;\n }\n const cleanValue = value.trim();\n if (FORBIDDEN_ATTRIBUTE_KEYS.includes(cleanKey)) {\n hygiene.flag(\n \"structuredAttributes\",\n `contain the reserved key ${JSON.stringify(cleanKey)} — prototype-machinery names cannot round-trip through fingerprint serialization`,\n );\n continue;\n }\n if (cleanValue.length > STRUCTURED_ATTRIBUTE_LIMITS.maxValueLength) {\n hygiene.flag(\"structuredAttributes\", `value for ${JSON.stringify(cleanKey)} exceeds ${STRUCTURED_ATTRIBUTE_LIMITS.maxValueLength} characters`);\n continue;\n }\n if (Object.hasOwn(out, cleanKey)) {\n hygiene.flag(\"structuredAttributes\", `contain duplicate key after canonicalization (trim + lowercase): ${JSON.stringify(cleanKey)}`);\n continue;\n }\n out[cleanKey] = cleanValue;\n survivingOriginal.push([key, value]);\n }\n if (Object.keys(out).length === 0) return undefined;\n // Copy onto a normal object so downstream JSON/stableStringify behavior is\n // unchanged; keys are already validated. The RAW map carries only the\n // SURVIVING original pairs — dropped entries must not reach frontmatter.\n return {\n canonical: Object.freeze({ ...out }),\n raw: Object.freeze(Object.fromEntries(survivingOriginal)),\n };\n}\n\nfunction normalizeOptionalString(\n field: string,\n value: string | undefined,\n hygiene: Hygiene,\n): string | undefined {\n if (value === undefined) return undefined;\n if (typeof value !== \"string\") {\n hygiene.flag(field, `must be a string (got ${typeof value}) — dropped`);\n return undefined;\n }\n const trimmed = value.trim();\n if (trimmed.length === 0) {\n hygiene.flag(field, \"must be non-empty when provided — dropped\");\n return undefined;\n }\n return trimmed;\n}\n\n/**\n * Compose (normalize + validate + seal) a memory-write envelope.\n *\n * Strict mode (default) throws on any invalid input — never silently\n * defaults (AGENTS.md §1/§39). Salvage mode (`{ salvage: true }`, for\n * machine-generated input like LLM extraction) drops invalid OPTIONAL\n * fields with notes on `envelope.salvageNotes` instead of aborting the\n * batch; content/category/source/validAt stay fatal in both modes.\n */\nexport function composeMemoryEnvelope(\n input: MemoryWriteInput,\n ctx: WriteContext,\n opts: ComposeEnvelopeOptions = {},\n): SealedMemoryEnvelope {\n if (input === null || typeof input !== \"object\") fail(\"input\", \"must be an object\");\n if (ctx === null || typeof ctx !== \"object\") fail(\"ctx\", \"must be an object\");\n const hygiene = makeHygiene(opts.salvage === true);\n\n if (typeof input.content !== \"string\" || input.content.trim().length === 0) {\n fail(\"content\", \"must be a non-empty string\");\n }\n if (typeof input.category !== \"string\" || !isMemoryCategory(input.category)) {\n fail(\n \"category\",\n `must be one of: ${MEMORY_CATEGORY_NAMES.join(\", \")} (got ${JSON.stringify(input.category)})`,\n );\n }\n if (typeof ctx.source !== \"string\" || ctx.source.trim().length === 0) {\n fail(\"ctx.source\", \"must be a non-empty string\");\n }\n\n let confidence: number | undefined;\n if (input.confidence !== undefined) {\n if (\n typeof input.confidence !== \"number\" ||\n !Number.isFinite(input.confidence) ||\n input.confidence < 0 ||\n input.confidence > 1\n ) {\n hygiene.flag(\n \"confidence\",\n `must be a finite number within [0, 1] (got ${String(input.confidence)}) — dropped (storage defaults apply)`,\n );\n } else {\n confidence = input.confidence;\n }\n }\n\n // validAt stays FATAL in salvage too: legacy writeMemory throws on an\n // invalid validAt (normalizeMemoryWriteTimestamp), so salvage-dropping it\n // would CHANGE behavior rather than preserve it.\n const validAt =\n input.validAt === undefined ? undefined : validateIsoTimestamp(\"validAt\", input.validAt);\n\n const now = ctx.now ? ctx.now() : new Date();\n if (!(now instanceof Date) || Number.isNaN(now.getTime())) {\n fail(\"ctx.now\", \"must return a valid Date\");\n }\n\n const attributes = normalizeStructuredAttributes(input.structuredAttributes, hygiene);\n // Assemble the EXACT persisted form (attribute suffix + combined sanitize)\n // with the same helper writeMemory uses — fingerprints hash this form, so\n // envelope and storage cannot diverge (§13; #1998 round-8 thread resolved\n // here in PR2 as promised). writeMemory assembles from the RAW map;\n // normalizeAttributePairs canonicalizes internally, so raw and canonical\n // maps yield the same suffix — asserted by the parity suite.\n const assembled = assemblePersistedBody(\n input.content,\n attributes ? { ...attributes.raw } : undefined,\n );\n if (assembled.text.trim().length === 0) {\n fail(\"content\", \"is empty after sanitization\");\n }\n\n const body = {\n content: input.content,\n persistedBody: assembled.text,\n sanitizeViolations: Object.freeze([...assembled.violations]),\n category: input.category,\n tags: normalizeEnvelopeTags(input.tags, hygiene),\n structuredAttributes: attributes?.canonical,\n rawStructuredAttributes: attributes?.raw,\n entityRef: normalizeOptionalString(\"entityRef\", input.entityRef, hygiene),\n confidence,\n ttl: normalizeOptionalString(\"ttl\", input.ttl, hygiene),\n validAt,\n sourceConnector: normalizeOptionalString(\"sourceConnector\", input.sourceConnector, hygiene),\n origin: normalizeOptionalString(\"origin\", input.origin, hygiene),\n sourceReason: normalizeOptionalString(\"sourceReason\", input.sourceReason, hygiene),\n source: ctx.source.trim(),\n composedAt: now.toISOString(),\n salvageNotes: Object.freeze([...hygiene.notes]),\n };\n const envelope = Object.freeze(body) as unknown as SealedMemoryEnvelope;\n SEALED_ENVELOPES.add(envelope);\n return envelope;\n}\n\n// The runtime seal registry. The declared `sealed` unique symbol exists only\n// at the type level (compile-time fence); this WeakSet is the runtime fence.\nconst SEALED_ENVELOPES = new WeakSet<object>();\n\n/**\n * TOCTOU guard for the structural arm (#2014 round 4): a frozen object may\n * still carry ACCESSOR properties whose getters return different values on\n * each read — valid data during re-composition, forged data when the write\n * path reads the field again. Frozen DATA properties are immutable, so\n * requiring data-only descriptors (on the envelope and its array/map\n * fields) makes every later read provably identical to the validated one.\n */\nfunction hasOnlyFrozenDataProperties(value: object): boolean {\n if (!Object.isFrozen(value)) return false;\n // Inherited accessors are as dangerous as own ones (round 5): a frozen\n // object can inherit a `tags` getter from a custom prototype. Composer\n // envelopes are plain object literals and plain arrays — require exactly\n // those prototypes so prototype-supplied getters cannot serve fields.\n const proto = Object.getPrototypeOf(value);\n if (Array.isArray(value)) {\n if (proto !== Array.prototype) return false;\n } else if (proto !== Object.prototype && proto !== null) {\n return false;\n }\n for (const key of Reflect.ownKeys(value)) {\n const descriptor = Object.getOwnPropertyDescriptor(value, key);\n if (!descriptor || descriptor.get !== undefined || descriptor.set !== undefined) {\n return false;\n }\n }\n return true;\n}\n\n/**\n * Runtime check (belt) — the compile-time brand is the real fence.\n *\n * Fast path: WeakSet membership (same module graph). Fallback: RE-COMPOSITION\n * EQUIVALENCE — Node resolves `@remnic/core` through `dist/` for built\n * consumers and `src/` for tsx-loaded tests, and each graph gets its OWN\n * WeakSet, so identity alone would reject genuinely-sealed envelopes that\n * crossed a build boundary. The structural arm requires frozen DATA-only\n * properties (no accessors — TOCTOU guard, round 4), then re-runs the\n * STRICT composer on the candidate's own inputs and demands every envelope\n * field reproduce exactly (#2014 round 2: shape-only validation admitted\n * lookalikes with 51 tags or out-of-range confidence). A value that\n * survives has, by construction, passed every composer invariant, and its\n * fields cannot change between validation and the write that follows.\n *\n * `salvageNotes` is deliberately NOT compared: a legitimately salvage-minted\n * envelope carries notes while the strict re-composition of its (already\n * clean) surviving fields yields none. Notes are advisory provenance, not\n * identity — every load-bearing field is still verified.\n */\n\nexport function isSealedMemoryEnvelope(value: unknown): value is SealedMemoryEnvelope {\n if (typeof value !== \"object\" || value === null) return false;\n if (SEALED_ENVELOPES.has(value)) return true;\n if (!hasOnlyFrozenDataProperties(value)) return false;\n const v = value as Partial<SealedMemoryEnvelope>;\n if (\n typeof v.content !== \"string\" ||\n typeof v.persistedBody !== \"string\" ||\n typeof v.source !== \"string\" ||\n typeof v.composedAt !== \"string\" ||\n typeof v.category !== \"string\" ||\n !isMemoryCategory(v.category) ||\n !Array.isArray(v.tags) ||\n !hasOnlyFrozenDataProperties(v.tags) ||\n !Array.isArray(v.sanitizeViolations) ||\n !hasOnlyFrozenDataProperties(v.sanitizeViolations) ||\n !Array.isArray(v.salvageNotes) ||\n !hasOnlyFrozenDataProperties(v.salvageNotes)\n ) {\n return false;\n }\n const attrs = v.rawStructuredAttributes;\n if (\n attrs !== undefined &&\n (attrs === null || typeof attrs !== \"object\" || Array.isArray(attrs) || !hasOnlyFrozenDataProperties(attrs))\n ) {\n return false;\n }\n const canonicalAttrs = v.structuredAttributes;\n if (\n canonicalAttrs !== undefined &&\n (canonicalAttrs === null ||\n typeof canonicalAttrs !== \"object\" ||\n Array.isArray(canonicalAttrs) ||\n !hasOnlyFrozenDataProperties(canonicalAttrs))\n ) {\n return false;\n }\n let rebuilt: SealedMemoryEnvelope;\n try {\n rebuilt = composeMemoryEnvelope(\n {\n content: v.content,\n category: v.category,\n ...(v.tags.length > 0 ? { tags: [...(v.tags as string[])] } : {}),\n ...(attrs !== undefined ? { structuredAttributes: { ...attrs } } : {}),\n ...(v.entityRef !== undefined ? { entityRef: v.entityRef } : {}),\n ...(v.confidence !== undefined ? { confidence: v.confidence } : {}),\n ...(v.ttl !== undefined ? { ttl: v.ttl } : {}),\n ...(v.validAt !== undefined ? { validAt: v.validAt } : {}),\n ...(v.sourceConnector !== undefined ? { sourceConnector: v.sourceConnector } : {}),\n ...(v.origin !== undefined ? { origin: v.origin } : {}),\n ...(v.sourceReason !== undefined ? { sourceReason: v.sourceReason } : {}),\n },\n { source: v.source, now: () => new Date(v.composedAt as string) },\n );\n } catch {\n // Strict re-composition rejected an input — the candidate carries a\n // value the composer would never have minted.\n return false;\n }\n const sameArray = (a: readonly string[], b: readonly string[]): boolean =>\n a.length === b.length && a.every((entry, i) => entry === b[i]);\n const sameMap = (\n a: Readonly<Record<string, string>> | undefined,\n b: Readonly<Record<string, string>> | undefined,\n ): boolean => {\n if (a === undefined || b === undefined) return a === b;\n const ak = Object.keys(a);\n const bk = Object.keys(b);\n return ak.length === bk.length && ak.every((k) => Object.hasOwn(b, k) && a[k] === b[k]);\n };\n return (\n rebuilt.content === v.content &&\n rebuilt.persistedBody === v.persistedBody &&\n rebuilt.category === v.category &&\n rebuilt.source === v.source &&\n rebuilt.composedAt === v.composedAt &&\n rebuilt.confidence === v.confidence &&\n rebuilt.entityRef === v.entityRef &&\n rebuilt.ttl === v.ttl &&\n rebuilt.validAt === v.validAt &&\n rebuilt.sourceConnector === v.sourceConnector &&\n rebuilt.origin === v.origin &&\n rebuilt.sourceReason === v.sourceReason &&\n sameArray(rebuilt.tags, v.tags as string[]) &&\n sameArray(rebuilt.sanitizeViolations, v.sanitizeViolations as string[]) &&\n sameMap(rebuilt.structuredAttributes, v.structuredAttributes) &&\n sameMap(rebuilt.rawStructuredAttributes, attrs)\n );\n}\n\n/**\n * The ONE mapping from a sealed envelope (+ extras) to the legacy\n * `writeMemory(category, content, options)` argument shape. Used by\n * `StorageManager.writeSealedMemory` AND by test doubles that stub storage —\n * so mock fidelity (AGENTS.md §21) cannot drift from production.\n */\nexport function sealedWriteToLegacyArgs(\n envelope: SealedMemoryEnvelope,\n extras: Record<string, unknown> = {},\n): { category: MemoryCategory; content: string; options: Record<string, unknown> } {\n return {\n category: envelope.category,\n content: envelope.content,\n options: {\n ...extras,\n confidence: envelope.confidence,\n tags: envelope.tags.length > 0 ? [...envelope.tags] : undefined,\n entityRef: envelope.entityRef,\n source: envelope.source,\n expiresAt: envelope.ttl,\n validAt: envelope.validAt,\n // RAW map for frontmatter byte-parity with the legacy path; the body\n // suffix canonicalizes identically from either form.\n structuredAttributes: envelope.rawStructuredAttributes\n ? { ...envelope.rawStructuredAttributes }\n : undefined,\n // Unconditional (round 3): envelope-owned fields must ALWAYS overwrite\n // extras — a conditional spread let extras.sourceConnector smuggle\n // unvalidated connector provenance past the sealed envelope when the\n // envelope had none.\n sourceConnector: envelope.sourceConnector,\n origin: envelope.origin,\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Idempotency field registry\n// ---------------------------------------------------------------------------\n\n/**\n * Ordered registry of envelope fields that participate in write-idempotency\n * fingerprints. Adding a fingerprint-relevant field = one entry here; every\n * surface that builds its payload via `buildWriteIdempotencyPayload()` picks\n * it up automatically.\n */\nexport const WRITE_FINGERPRINT_FIELDS = [\n \"content\",\n \"category\",\n \"tags\",\n \"structuredAttributes\",\n \"entityRef\",\n \"ttl\",\n \"validAt\",\n \"sourceConnector\",\n] as const satisfies readonly (keyof MemoryWriteInput)[];\n\n/**\n * Envelope fields deliberately EXCLUDED from fingerprints. Exempting a field\n * is a visible, reviewable decision — this list exists so the exhaustiveness\n * guard can force every new `MemoryWriteInput` field to be classified.\n */\nexport const FINGERPRINT_EXEMPT_FIELDS = [\n // Confidence is a scoring detail: two extractions of the same fact with\n // slightly different confidence are the same write for dedup purposes.\n \"confidence\",\n // Free-text provenance note; carries no identity.\n // Origin is provenance metadata, not write identity (#1955).\n \"origin\",\n \"sourceReason\",\n] as const satisfies readonly (keyof MemoryWriteInput)[];\n\n/** Scope keys always folded into the fingerprint payload. */\nexport const FINGERPRINT_SCOPE_FIELDS = [\n \"surface\",\n \"namespace\",\n \"principal\",\n \"codingContext\",\n] as const satisfies readonly (keyof FingerprintScope)[];\n\n// --- compile-time exhaustiveness guards ------------------------------------\n// A new MemoryWriteInput field that is in NEITHER list is a compile error;\n// a registry entry that names a non-existent field is a compile error; a\n// field in BOTH lists is a compile error.\n\ntype RegistryField = (typeof WRITE_FINGERPRINT_FIELDS)[number];\ntype ExemptField = (typeof FINGERPRINT_EXEMPT_FIELDS)[number];\ntype UnclassifiedField = Exclude<keyof MemoryWriteInput, RegistryField | ExemptField>;\ntype DoublyClassifiedField = RegistryField & ExemptField;\n\nconst assertEveryFieldClassified: UnclassifiedField extends never ? true : never = true;\nconst assertNoDoubleClassification: DoublyClassifiedField extends never ? true : never = true;\nvoid assertEveryFieldClassified;\nvoid assertNoDoubleClassification;\n\n// ---------------------------------------------------------------------------\n// Payload builder\n// ---------------------------------------------------------------------------\n\n/**\n * Build the canonical write-idempotency payload for a sealed envelope.\n *\n * Determinism guarantees (AGENTS.md §26):\n * - tags are SORTED in the payload (tag order never changes identity);\n * - structured attributes are serialized key-sorted by the existing\n * `stableStringify` inside `hashAccessIdempotencyPayload`;\n * - the payload shape is versioned (`v: 1`) so a future field-semantics\n * change can migrate stored idempotency state explicitly instead of\n * silently colliding.\n *\n * Feed the result to `hashAccessIdempotencyPayload()` — this module does\n * not hash so surfaces keep a single hashing implementation.\n */\nexport function buildWriteIdempotencyPayload(\n envelope: SealedMemoryEnvelope,\n scope: FingerprintScope,\n): Record<string, unknown> {\n if (!isSealedMemoryEnvelope(envelope)) {\n throw new Error(\n \"buildWriteIdempotencyPayload: envelope must be minted by composeMemoryEnvelope\",\n );\n }\n if (scope === null || typeof scope !== \"object\") {\n throw new Error(\"buildWriteIdempotencyPayload: scope must be an object\");\n }\n if (typeof scope.surface !== \"string\" || scope.surface.trim().length === 0) {\n throw new Error(\"buildWriteIdempotencyPayload: scope.surface must be a non-empty string\");\n }\n\n const fields: Record<string, unknown> = {};\n for (const field of WRITE_FINGERPRINT_FIELDS) {\n const value = envelope[field];\n if (value === undefined) continue;\n if (field === \"tags\") {\n const tags = value as readonly string[];\n if (tags.length === 0) continue;\n fields.tags = [...tags].sort();\n continue;\n }\n if (field === \"content\") {\n // Fingerprints hash the PERSISTED form (attribute suffix + combined\n // sanitize), not the raw input — one instant of stored state, one\n // fingerprint (§13; issue #1989 PR2).\n fields.content = envelope.persistedBody;\n continue;\n }\n fields[field] = value;\n }\n // The write source (extraction vs wearable:bee vs observe replay, ...) is\n // identity: the same content arriving from two different sources must not\n // dedupe into one write (review finding on #1998). Not a MemoryWriteInput\n // key, so it rides beside the registry fields explicitly.\n fields.source = envelope.source;\n\n const scopeOut: Record<string, string> = {};\n for (const key of FINGERPRINT_SCOPE_FIELDS) {\n const value = scope[key];\n if (value === undefined) continue;\n if (typeof value !== \"string\") {\n throw new Error(`buildWriteIdempotencyPayload: scope.${key} must be a string`);\n }\n // Trimmed: \"ns\" and \"ns \" must not mint distinct fingerprints (review\n // finding on #1998). Empty-after-trim optional scope values are dropped.\n const cleanValue = value.trim();\n if (key === \"surface\" || cleanValue.length > 0) {\n scopeOut[key] = cleanValue;\n }\n }\n\n return { v: 1, kind: \"memory-write\", fields, scope: scopeOut };\n}\n\n// ---------------------------------------------------------------------------\n// Access-surface request fingerprints (issue #1989 PR3)\n// ---------------------------------------------------------------------------\n//\n// The access surfaces (memory_store / suggestion_submit writes, their peek\n// twins, and observe) key their idempotency dedup on a hash of the RAW\n// request — computed BEFORE validation, and persisted indefinitely in\n// `AccessIdempotencyStore` (last-512 LRU, no time TTL). Those stored hashes\n// are load-bearing state: changing the payload shape would silently break\n// replay/conflict detection for every in-flight key. So these builders\n// REPRODUCE each surface's historical shape byte-for-byte (asserted by the\n// parity suite) instead of adopting the versioned envelope payload above —\n// a versioned migration of stored state is the explicit prerequisite for\n// unifying them (#1989 PR3 scope note).\n//\n// What centralizing buys even under byte-parity: one module now owns every\n// fingerprint shape, and the compile-time assertion below ties the write\n// shape to WRITE_FINGERPRINT_FIELDS — adding a fingerprint-relevant field\n// to MemoryWriteInput fails compilation here until the author decides how\n// the access surfaces represent it.\n\n/** The raw request fields the write surfaces fingerprint (legacy shape). */\nexport interface AccessWriteFingerprintParts {\n schemaVersion: number;\n namespace: string;\n content: string;\n category?: string;\n confidence?: number;\n tags?: string[];\n entityRef?: string;\n ttl?: string;\n sourceReason?: string;\n sourceConnector?: string;\n}\n\n// Registry coupling: every fingerprint-relevant MemoryWriteInput field must\n// either appear in AccessWriteFingerprintParts or be named in this explicit\n// exemption list. `structuredAttributes` and `validAt` are exempt because\n// the access write requests cannot express them today (ExplicitCaptureInput\n// has no such fields) — the moment they become representable, this type\n// stops compiling and forces a fingerprint decision (+ versioned state\n// migration) instead of a silent dedup gap.\ntype AccessSurfaceUnrepresentable = \"structuredAttributes\" | \"validAt\";\ntype UncoveredAccessFingerprintField = Exclude<\n Extract<RegistryField, keyof MemoryWriteInput>,\n keyof AccessWriteFingerprintParts | AccessSurfaceUnrepresentable\n>;\nconst assertAccessFingerprintCoversRegistry: UncoveredAccessFingerprintField extends never\n ? true\n : never = true;\nvoid assertAccessFingerprintCoversRegistry;\n\n/**\n * Fingerprint payload for `memory_store` / `suggestion_submit` (write AND\n * peek paths — both MUST call this so peek can never diverge from the\n * write it predicts). Key set and values reproduce the historical inline\n * literals exactly; `hashAccessIdempotencyPayload`'s stableStringify sorts\n * keys and drops `undefined`, so optional absent fields hash identically\n * to the old shape.\n */\nexport function buildAccessWriteRequestFingerprint(\n parts: AccessWriteFingerprintParts,\n): Record<string, unknown> {\n return {\n schemaVersion: parts.schemaVersion,\n content: parts.content,\n category: parts.category,\n confidence: parts.confidence,\n namespace: parts.namespace,\n tags: parts.tags,\n entityRef: parts.entityRef,\n ttl: parts.ttl,\n sourceReason: parts.sourceReason,\n sourceConnector: parts.sourceConnector,\n };\n}\n\n/** The raw request fields the observe surface fingerprints (legacy shape). */\nexport interface ObserveFingerprintParts {\n sessionKey: string;\n messages: unknown;\n namespace?: string;\n skipExtraction?: boolean;\n authenticatedPrincipal?: string;\n cwd?: string;\n projectTag?: string;\n /** Resolved coding context — `null` (not undefined) when absent, matching the historical literal. */\n effectiveCodingContext: unknown;\n sourceConnector?: string;\n}\n\n/**\n * Fingerprint payload for `observe` — turn ingestion, not a memory write,\n * so it fingerprints session/message identity rather than MemoryWriteInput\n * fields. Shape reproduces the historical inline literal exactly.\n */\nexport function buildObserveRequestFingerprint(\n parts: ObserveFingerprintParts,\n): Record<string, unknown> {\n return {\n sessionKey: parts.sessionKey,\n messages: parts.messages,\n namespace: parts.namespace,\n skipExtraction: parts.skipExtraction,\n authenticatedPrincipal: parts.authenticatedPrincipal,\n cwd: parts.cwd,\n projectTag: parts.projectTag,\n effectiveCodingContext: parts.effectiveCodingContext,\n sourceConnector: parts.sourceConnector,\n };\n}\n"],"mappings":"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","names":[]}
|
package/dist/chunk-MDVBCPOS.js
DELETED
|
@@ -1,402 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
readPair,
|
|
3
|
-
resolvePair
|
|
4
|
-
} from "./chunk-35NN3ZFA.js";
|
|
5
|
-
import {
|
|
6
|
-
composeMemoryEnvelope
|
|
7
|
-
} from "./chunk-LCU5R6TQ.js";
|
|
8
|
-
import {
|
|
9
|
-
log
|
|
10
|
-
} from "./chunk-3WCASNQZ.js";
|
|
11
|
-
|
|
12
|
-
// src/contradiction/resolution.ts
|
|
13
|
-
var VALID_VERBS = ["keep-a", "keep-b", "merge", "both-valid", "needs-more-context"];
|
|
14
|
-
function isValidResolutionVerb(value) {
|
|
15
|
-
return VALID_VERBS.includes(value);
|
|
16
|
-
}
|
|
17
|
-
async function executeResolution(memoryDir, storage, pairId, verb, options = {}) {
|
|
18
|
-
if (typeof verb !== "string" || !isValidResolutionVerb(verb)) {
|
|
19
|
-
throw new Error(`Invalid contradiction resolution verb: ${String(verb)}`);
|
|
20
|
-
}
|
|
21
|
-
const pair = readPair(memoryDir, pairId);
|
|
22
|
-
if (!pair) {
|
|
23
|
-
return { pairId, verb, affectedIds: [], message: `Pair ${pairId} not found` };
|
|
24
|
-
}
|
|
25
|
-
if (pair.namespace && !options.storageForNamespace) {
|
|
26
|
-
throw new Error(
|
|
27
|
-
"contradiction resolution requires storageForNamespace for namespaced pairs so callers resolve the correct namespace storage"
|
|
28
|
-
);
|
|
29
|
-
}
|
|
30
|
-
const resolutionStorage = options.storageForNamespace ? await options.storageForNamespace(pair.namespace) : storage;
|
|
31
|
-
if (pair.resolution && pair.resolution !== "needs-more-context") {
|
|
32
|
-
return { pairId, verb, affectedIds: [], message: `Pair already resolved with verb "${pair.resolution}"` };
|
|
33
|
-
}
|
|
34
|
-
const [idA, idB] = pair.memoryIds;
|
|
35
|
-
const affectedIds = [];
|
|
36
|
-
let message = "";
|
|
37
|
-
let supersedeFailed = false;
|
|
38
|
-
let rollbackAfterResolveFailure = null;
|
|
39
|
-
let recordCatalogWriteTouch = null;
|
|
40
|
-
const buildCatalogTouch = () => {
|
|
41
|
-
if (!options.onMergedMemoryWritten) return null;
|
|
42
|
-
const onMergedMemoryWritten = options.onMergedMemoryWritten;
|
|
43
|
-
const namespace = pair.namespace;
|
|
44
|
-
const storageDir = resolutionStorage.dir;
|
|
45
|
-
return () => onMergedMemoryWritten(namespace, storageDir);
|
|
46
|
-
};
|
|
47
|
-
const catalogWriteTouch = buildCatalogTouch();
|
|
48
|
-
const recordCatalogWriteTouchSafely = (context, touch = catalogWriteTouch) => {
|
|
49
|
-
if (!touch) return;
|
|
50
|
-
try {
|
|
51
|
-
touch();
|
|
52
|
-
} catch (err) {
|
|
53
|
-
log.warn(
|
|
54
|
-
"[contradiction-resolution] catalog write touch failed for pair=%s context=%s: %s",
|
|
55
|
-
pairId,
|
|
56
|
-
context,
|
|
57
|
-
err instanceof Error ? err.message : err
|
|
58
|
-
);
|
|
59
|
-
}
|
|
60
|
-
};
|
|
61
|
-
const touchCatalogIfRollbackLeftChange = async (context, snapshots, replacement) => {
|
|
62
|
-
if (!catalogWriteTouch) return;
|
|
63
|
-
if (await rollbackLeftDurableMutation(resolutionStorage, snapshots, replacement)) {
|
|
64
|
-
recordCatalogWriteTouchSafely(context);
|
|
65
|
-
}
|
|
66
|
-
};
|
|
67
|
-
switch (verb) {
|
|
68
|
-
case "keep-a": {
|
|
69
|
-
const keepTarget = await validateKeepTarget(resolutionStorage, pairId, idA);
|
|
70
|
-
if (!keepTarget.ok) {
|
|
71
|
-
supersedeFailed = true;
|
|
72
|
-
message = keepTarget.message;
|
|
73
|
-
break;
|
|
74
|
-
}
|
|
75
|
-
const sourceB = await loadSourceSnapshot(resolutionStorage, idB);
|
|
76
|
-
const ok = sourceB ? await supersedeSafe(resolutionStorage, idB, idA, "contradiction-resolution:keep-a") : false;
|
|
77
|
-
if (ok) {
|
|
78
|
-
affectedIds.push(idB);
|
|
79
|
-
rollbackAfterResolveFailure = async () => restoreMemorySnapshot(resolutionStorage, sourceB, "contradiction-resolution:keep-a-rollback");
|
|
80
|
-
recordCatalogWriteTouch = catalogWriteTouch;
|
|
81
|
-
message = `Kept ${idA}, superseded ${idB}`;
|
|
82
|
-
} else {
|
|
83
|
-
supersedeFailed = true;
|
|
84
|
-
const rolledBack = sourceB ? await restoreMemorySnapshot(resolutionStorage, sourceB, "contradiction-resolution:keep-a-rollback") : false;
|
|
85
|
-
if (sourceB && !rolledBack) {
|
|
86
|
-
await touchCatalogIfRollbackLeftChange("keep-a-rollback-incomplete", [sourceB]);
|
|
87
|
-
}
|
|
88
|
-
message = rolledBack ? `Supersede failed for ${idB}; restored ${idB} and did not resolve` : `Supersede failed for ${idB}; rollback incomplete for ${idB} and pair is not resolved`;
|
|
89
|
-
}
|
|
90
|
-
break;
|
|
91
|
-
}
|
|
92
|
-
case "keep-b": {
|
|
93
|
-
const keepTarget = await validateKeepTarget(resolutionStorage, pairId, idB);
|
|
94
|
-
if (!keepTarget.ok) {
|
|
95
|
-
supersedeFailed = true;
|
|
96
|
-
message = keepTarget.message;
|
|
97
|
-
break;
|
|
98
|
-
}
|
|
99
|
-
const sourceA = await loadSourceSnapshot(resolutionStorage, idA);
|
|
100
|
-
const ok = sourceA ? await supersedeSafe(resolutionStorage, idA, idB, "contradiction-resolution:keep-b") : false;
|
|
101
|
-
if (ok) {
|
|
102
|
-
affectedIds.push(idA);
|
|
103
|
-
rollbackAfterResolveFailure = async () => restoreMemorySnapshot(resolutionStorage, sourceA, "contradiction-resolution:keep-b-rollback");
|
|
104
|
-
recordCatalogWriteTouch = catalogWriteTouch;
|
|
105
|
-
message = `Kept ${idB}, superseded ${idA}`;
|
|
106
|
-
} else {
|
|
107
|
-
supersedeFailed = true;
|
|
108
|
-
const rolledBack = sourceA ? await restoreMemorySnapshot(resolutionStorage, sourceA, "contradiction-resolution:keep-b-rollback") : false;
|
|
109
|
-
if (sourceA && !rolledBack) {
|
|
110
|
-
await touchCatalogIfRollbackLeftChange("keep-b-rollback-incomplete", [sourceA]);
|
|
111
|
-
}
|
|
112
|
-
message = rolledBack ? `Supersede failed for ${idA}; restored ${idA} and did not resolve` : `Supersede failed for ${idA}; rollback incomplete for ${idA} and pair is not resolved`;
|
|
113
|
-
}
|
|
114
|
-
break;
|
|
115
|
-
}
|
|
116
|
-
case "merge": {
|
|
117
|
-
const replacement = await prepareMergeReplacement(resolutionStorage, pairId, idA, idB, options);
|
|
118
|
-
if (!replacement.ok) {
|
|
119
|
-
supersedeFailed = true;
|
|
120
|
-
message = replacement.message;
|
|
121
|
-
break;
|
|
122
|
-
}
|
|
123
|
-
const okA = await supersedeSafe(resolutionStorage, idA, replacement.mergedId, "contradiction-resolution:merge");
|
|
124
|
-
if (!okA) {
|
|
125
|
-
supersedeFailed = true;
|
|
126
|
-
const rolledBackA = await restoreMemorySnapshot(resolutionStorage, replacement.sourceA);
|
|
127
|
-
message = rolledBackA ? `Merge failed for ${idA}; restored ${idA} and did not resolve` : `Merge failed for ${idA}; rollback incomplete for ${idA} and pair is not resolved`;
|
|
128
|
-
if (rolledBackA) {
|
|
129
|
-
await cleanupCreatedReplacement(resolutionStorage, replacement);
|
|
130
|
-
} else {
|
|
131
|
-
await touchCatalogIfRollbackLeftChange("merge-first-rollback-incomplete", [replacement.sourceA], replacement);
|
|
132
|
-
}
|
|
133
|
-
break;
|
|
134
|
-
}
|
|
135
|
-
const okB = await supersedeSafe(resolutionStorage, idB, replacement.mergedId, "contradiction-resolution:merge");
|
|
136
|
-
if (!okB) {
|
|
137
|
-
supersedeFailed = true;
|
|
138
|
-
const rolledBackA = await restoreMemorySnapshot(resolutionStorage, replacement.sourceA);
|
|
139
|
-
const rolledBackB = await restoreMemorySnapshot(resolutionStorage, replacement.sourceB);
|
|
140
|
-
message = rolledBackA && rolledBackB ? `Merge failed for ${idB}; restored ${idA} and ${idB} and did not resolve` : `Merge failed for ${idB}; rollback incomplete for ${[
|
|
141
|
-
rolledBackA ? void 0 : idA,
|
|
142
|
-
rolledBackB ? void 0 : idB
|
|
143
|
-
].filter(Boolean).join(", ")} and pair is not resolved`;
|
|
144
|
-
if (rolledBackA && rolledBackB) {
|
|
145
|
-
await cleanupCreatedReplacement(resolutionStorage, replacement);
|
|
146
|
-
} else {
|
|
147
|
-
await touchCatalogIfRollbackLeftChange(
|
|
148
|
-
"merge-second-rollback-incomplete",
|
|
149
|
-
[replacement.sourceA, replacement.sourceB],
|
|
150
|
-
replacement
|
|
151
|
-
);
|
|
152
|
-
}
|
|
153
|
-
break;
|
|
154
|
-
}
|
|
155
|
-
affectedIds.push(idA, idB);
|
|
156
|
-
rollbackAfterResolveFailure = async () => {
|
|
157
|
-
const rolledBackA = await restoreMemorySnapshot(resolutionStorage, replacement.sourceA);
|
|
158
|
-
const rolledBackB = await restoreMemorySnapshot(resolutionStorage, replacement.sourceB);
|
|
159
|
-
if (rolledBackA && rolledBackB) {
|
|
160
|
-
await cleanupCreatedReplacement(resolutionStorage, replacement);
|
|
161
|
-
}
|
|
162
|
-
return rolledBackA && rolledBackB;
|
|
163
|
-
};
|
|
164
|
-
recordCatalogWriteTouch = catalogWriteTouch;
|
|
165
|
-
message = `Both memories superseded by merged ${replacement.mergedId}`;
|
|
166
|
-
break;
|
|
167
|
-
}
|
|
168
|
-
case "both-valid": {
|
|
169
|
-
message = "Pair marked as both-valid; cooldown applied";
|
|
170
|
-
break;
|
|
171
|
-
}
|
|
172
|
-
case "needs-more-context": {
|
|
173
|
-
message = "Deferred; no action taken, short cooldown applied";
|
|
174
|
-
break;
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
if (!supersedeFailed) {
|
|
178
|
-
let resolved = false;
|
|
179
|
-
try {
|
|
180
|
-
resolved = resolvePair(memoryDir, pairId, verb) !== null;
|
|
181
|
-
} catch (err) {
|
|
182
|
-
log.warn(
|
|
183
|
-
"[contradiction-resolution] failed to persist pair=%s verb=%s: %s",
|
|
184
|
-
pairId,
|
|
185
|
-
verb,
|
|
186
|
-
err instanceof Error ? err.message : err
|
|
187
|
-
);
|
|
188
|
-
}
|
|
189
|
-
if (!resolved) {
|
|
190
|
-
if (rollbackAfterResolveFailure) {
|
|
191
|
-
const rolledBack = await rollbackAfterResolveFailure();
|
|
192
|
-
affectedIds.length = 0;
|
|
193
|
-
message = rolledBack ? `Resolution persistence failed; rolled back memory changes and did not resolve ${pairId}` : "Resolution persistence failed; rollback incomplete and pair is not resolved";
|
|
194
|
-
if (!rolledBack && recordCatalogWriteTouch) {
|
|
195
|
-
recordCatalogWriteTouchSafely("resolve-persistence-rollback-incomplete", recordCatalogWriteTouch);
|
|
196
|
-
}
|
|
197
|
-
} else {
|
|
198
|
-
message = "Resolution persistence failed; pair is not resolved";
|
|
199
|
-
}
|
|
200
|
-
} else if (recordCatalogWriteTouch) {
|
|
201
|
-
recordCatalogWriteTouchSafely("resolved", recordCatalogWriteTouch);
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
log.info("[contradiction-resolution] pair=%s verb=%s affected=%d", pairId, verb, affectedIds.length);
|
|
205
|
-
return { pairId, verb, affectedIds, message };
|
|
206
|
-
}
|
|
207
|
-
async function prepareMergeReplacement(storage, pairId, idA, idB, options) {
|
|
208
|
-
const sourceA = await storage.getMemoryById(idA);
|
|
209
|
-
const sourceB = await storage.getMemoryById(idB);
|
|
210
|
-
if (!sourceA || !sourceB) {
|
|
211
|
-
return { ok: false, message: `Merge requires both source memories to exist; not resolving ${pairId}` };
|
|
212
|
-
}
|
|
213
|
-
const requestedMergedId = options.mergedMemoryId?.trim();
|
|
214
|
-
if (requestedMergedId) {
|
|
215
|
-
if (requestedMergedId === idA || requestedMergedId === idB) {
|
|
216
|
-
return { ok: false, message: "Merge replacement must be distinct from both source memories; not resolving" };
|
|
217
|
-
}
|
|
218
|
-
const replacement2 = await storage.getMemoryById(requestedMergedId);
|
|
219
|
-
if (!replacement2) {
|
|
220
|
-
return { ok: false, message: `Merged memory ${requestedMergedId} not found; not resolving` };
|
|
221
|
-
}
|
|
222
|
-
const replacementStatus = replacement2.frontmatter.status ?? "active";
|
|
223
|
-
if (replacementStatus !== "active") {
|
|
224
|
-
return {
|
|
225
|
-
ok: false,
|
|
226
|
-
message: `Merged memory ${requestedMergedId} is ${replacementStatus}; not resolving`
|
|
227
|
-
};
|
|
228
|
-
}
|
|
229
|
-
return { ok: true, mergedId: requestedMergedId, sourceA, sourceB, created: false };
|
|
230
|
-
}
|
|
231
|
-
const mergedContent = options.mergedContent;
|
|
232
|
-
if (typeof mergedContent !== "string" || mergedContent.trim().length === 0) {
|
|
233
|
-
return {
|
|
234
|
-
ok: false,
|
|
235
|
-
message: "Merge requires mergedMemoryId or mergedContent; no memories changed"
|
|
236
|
-
};
|
|
237
|
-
}
|
|
238
|
-
const category = options.mergedCategory ?? mergedMemoryCategory(sourceA, sourceB);
|
|
239
|
-
let mergedId;
|
|
240
|
-
try {
|
|
241
|
-
const mergeEnvelope = composeMemoryEnvelope(
|
|
242
|
-
{
|
|
243
|
-
content: mergedContent,
|
|
244
|
-
category,
|
|
245
|
-
confidence: Math.min(sourceA.frontmatter.confidence ?? 0.8, sourceB.frontmatter.confidence ?? 0.8),
|
|
246
|
-
tags: ["contradiction-resolution", "merge"]
|
|
247
|
-
},
|
|
248
|
-
{ source: "contradiction-resolution" },
|
|
249
|
-
{ salvage: true }
|
|
250
|
-
);
|
|
251
|
-
if (mergeEnvelope.salvageNotes.length > 0) {
|
|
252
|
-
log.warn(`contradiction-resolution write salvaged invalid fields: ${mergeEnvelope.salvageNotes.join("; ")}`);
|
|
253
|
-
}
|
|
254
|
-
const mergeResult = await storage.writeSealedMemory(mergeEnvelope, {
|
|
255
|
-
actor: "contradiction-resolution",
|
|
256
|
-
lineage: [idA, idB],
|
|
257
|
-
derivedVia: "merge"
|
|
258
|
-
});
|
|
259
|
-
mergedId = mergeResult.id;
|
|
260
|
-
if (mergeResult.tombstoneBlocked) {
|
|
261
|
-
await cleanupMemoryId(storage, mergedId);
|
|
262
|
-
return { ok: false, message: `Merged memory for ${pairId} was tombstone-blocked (pending_review); not resolving \u2014 sources kept active` };
|
|
263
|
-
}
|
|
264
|
-
} catch (err) {
|
|
265
|
-
log.warn(
|
|
266
|
-
"[contradiction-resolution] merged memory creation failed for %s: %s",
|
|
267
|
-
pairId,
|
|
268
|
-
err instanceof Error ? err.message : err
|
|
269
|
-
);
|
|
270
|
-
return { ok: false, message: `Merged memory could not be created; not resolving ${pairId}` };
|
|
271
|
-
}
|
|
272
|
-
const replacement = await storage.getMemoryById(mergedId);
|
|
273
|
-
if (!replacement) {
|
|
274
|
-
await cleanupMemoryId(storage, mergedId);
|
|
275
|
-
return { ok: false, message: `Merged memory ${mergedId} could not be verified; not resolving` };
|
|
276
|
-
}
|
|
277
|
-
return { ok: true, mergedId, sourceA, sourceB, created: true };
|
|
278
|
-
}
|
|
279
|
-
function mergedMemoryCategory(sourceA, sourceB) {
|
|
280
|
-
return sourceA.frontmatter.category === sourceB.frontmatter.category ? sourceA.frontmatter.category : "fact";
|
|
281
|
-
}
|
|
282
|
-
async function validateKeepTarget(storage, pairId, keepId) {
|
|
283
|
-
const target = await loadSourceSnapshot(storage, keepId);
|
|
284
|
-
if (!target) {
|
|
285
|
-
return { ok: false, message: `Kept memory ${keepId} not found; not resolving ${pairId}` };
|
|
286
|
-
}
|
|
287
|
-
const status = target.frontmatter.status ?? "active";
|
|
288
|
-
if (status !== "active") {
|
|
289
|
-
return { ok: false, message: `Kept memory ${keepId} is ${status}; not resolving ${pairId}` };
|
|
290
|
-
}
|
|
291
|
-
return { ok: true };
|
|
292
|
-
}
|
|
293
|
-
async function loadSourceSnapshot(storage, memoryId) {
|
|
294
|
-
try {
|
|
295
|
-
return await storage.getMemoryById(memoryId);
|
|
296
|
-
} catch (err) {
|
|
297
|
-
log.warn(
|
|
298
|
-
"[contradiction-resolution] source snapshot failed for %s: %s",
|
|
299
|
-
memoryId,
|
|
300
|
-
err instanceof Error ? err.message : err
|
|
301
|
-
);
|
|
302
|
-
return null;
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
async function restoreMemorySnapshot(storage, memory, reasonCode = "contradiction-resolution:merge-rollback") {
|
|
306
|
-
try {
|
|
307
|
-
const current = await storage.getMemoryById(memory.frontmatter.id);
|
|
308
|
-
if (!current) return false;
|
|
309
|
-
const restoredFrontmatter = {
|
|
310
|
-
...memory.frontmatter,
|
|
311
|
-
status: memory.frontmatter.status,
|
|
312
|
-
supersededBy: memory.frontmatter.supersededBy,
|
|
313
|
-
supersededAt: memory.frontmatter.supersededAt
|
|
314
|
-
};
|
|
315
|
-
return await storage.writeMemoryFrontmatter(current, restoredFrontmatter, {
|
|
316
|
-
actor: "contradiction-resolution",
|
|
317
|
-
reasonCode
|
|
318
|
-
});
|
|
319
|
-
} catch (err) {
|
|
320
|
-
log.warn(
|
|
321
|
-
"[contradiction-resolution] rollback failed for %s: %s",
|
|
322
|
-
memory.frontmatter.id,
|
|
323
|
-
err instanceof Error ? err.message : err
|
|
324
|
-
);
|
|
325
|
-
return false;
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
async function rollbackLeftDurableMutation(storage, snapshots, replacement) {
|
|
329
|
-
for (const snapshot of snapshots) {
|
|
330
|
-
try {
|
|
331
|
-
const current = await storage.getMemoryById(snapshot.frontmatter.id);
|
|
332
|
-
if (!current) return true;
|
|
333
|
-
if (supersessionStateChanged(current, snapshot)) return true;
|
|
334
|
-
} catch (err) {
|
|
335
|
-
log.warn(
|
|
336
|
-
"[contradiction-resolution] rollback inspection failed for %s: %s",
|
|
337
|
-
snapshot.frontmatter.id,
|
|
338
|
-
err instanceof Error ? err.message : err
|
|
339
|
-
);
|
|
340
|
-
return true;
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
if (replacement?.created) {
|
|
344
|
-
try {
|
|
345
|
-
return await storage.getMemoryById(replacement.mergedId) !== null;
|
|
346
|
-
} catch (err) {
|
|
347
|
-
log.warn(
|
|
348
|
-
"[contradiction-resolution] rollback replacement inspection failed for %s: %s",
|
|
349
|
-
replacement.mergedId,
|
|
350
|
-
err instanceof Error ? err.message : err
|
|
351
|
-
);
|
|
352
|
-
return true;
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
return false;
|
|
356
|
-
}
|
|
357
|
-
function supersessionStateChanged(current, snapshot) {
|
|
358
|
-
return current.frontmatter.status !== snapshot.frontmatter.status || current.frontmatter.supersededBy !== snapshot.frontmatter.supersededBy || current.frontmatter.supersededAt !== snapshot.frontmatter.supersededAt;
|
|
359
|
-
}
|
|
360
|
-
async function cleanupCreatedReplacement(storage, replacement) {
|
|
361
|
-
if (!replacement.created) return;
|
|
362
|
-
await cleanupMemoryId(storage, replacement.mergedId);
|
|
363
|
-
}
|
|
364
|
-
async function cleanupMemoryId(storage, memoryId) {
|
|
365
|
-
try {
|
|
366
|
-
const memory = await storage.getMemoryById(memoryId);
|
|
367
|
-
const invalidated = await storage.invalidateMemory(memoryId);
|
|
368
|
-
if (invalidated && memory?.frontmatter.category === "fact") {
|
|
369
|
-
await storage.removeFactContentHashesForMemories([memory]);
|
|
370
|
-
}
|
|
371
|
-
} catch (err) {
|
|
372
|
-
log.warn(
|
|
373
|
-
"[contradiction-resolution] cleanup failed for merged memory %s: %s",
|
|
374
|
-
memoryId,
|
|
375
|
-
err instanceof Error ? err.message : err
|
|
376
|
-
);
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
async function supersedeSafe(storage, oldId, newId, reason) {
|
|
380
|
-
try {
|
|
381
|
-
const result = await storage.supersedeMemory(oldId, newId, reason);
|
|
382
|
-
if (result === false) {
|
|
383
|
-
log.warn("[contradiction-resolution] supersede returned false for %s \u2192 %s", oldId, newId);
|
|
384
|
-
return false;
|
|
385
|
-
}
|
|
386
|
-
return true;
|
|
387
|
-
} catch (err) {
|
|
388
|
-
log.warn(
|
|
389
|
-
"[contradiction-resolution] supersede failed %s \u2192 %s: %s",
|
|
390
|
-
oldId,
|
|
391
|
-
newId,
|
|
392
|
-
err instanceof Error ? err.message : err
|
|
393
|
-
);
|
|
394
|
-
return false;
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
export {
|
|
399
|
-
isValidResolutionVerb,
|
|
400
|
-
executeResolution
|
|
401
|
-
};
|
|
402
|
-
//# sourceMappingURL=chunk-MDVBCPOS.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/contradiction/resolution.ts"],"sourcesContent":["/**\n * Resolution Verbs — executes user-chosen resolution actions on contradiction pairs (issue #520).\n *\n * All resolution paths delegate to StorageManager.supersedeMemory. Do not\n * reimplement supersession logic here (rule 22: deduplicate resolution).\n */\n\nimport { log } from \"../logger.js\";\nimport { composeMemoryEnvelope } from \"../write-envelope.js\";\nimport type { StorageManager } from \"../storage.js\";\nimport type { MemoryCategory, MemoryFile } from \"../types.js\";\nimport type { ResolutionVerb } from \"./contradiction-review.js\";\nimport { readPair, resolvePair } from \"./contradiction-review.js\";\n\nexport interface ResolutionResult {\n pairId: string;\n verb: ResolutionVerb;\n /** Memory IDs affected by the resolution. */\n affectedIds: string[];\n /** Human-readable status. */\n message: string;\n}\n\nexport interface ExecuteResolutionOptions {\n /** Existing merged memory to supersede both source memories to. */\n mergedMemoryId?: string;\n /** Content for a new merged memory. Required for merge when mergedMemoryId is omitted. */\n mergedContent?: string;\n /** Category for a newly created merged memory. Defaults to the shared source category, or fact. */\n mergedCategory?: MemoryCategory;\n /** Resolve storage for the pair namespace, or the default namespace for legacy unscoped pairs. */\n storageForNamespace?: (namespace: string | undefined) => StorageManager | Promise<StorageManager>;\n /**\n * Best-effort hook invoked after a contradiction resolution leaves a durable\n * mutation in the namespace's memory files (issue #1499 sweep, NH1dX / NH3X3).\n * Every mutating verb — `merge` (creates a new memory and supersedes both\n * sources), `keep-a`, and `keep-b` (supersede the losing source + rewrite\n * frontmatter) — writes directly to the pair's (possibly DYNAMIC) namespace\n * storage, bypassing the extraction write path that records catalog writes. So\n * without this the namespace's `lastWriteAt` stays stale and QMD maintenance /\n * `writtenSince` can skip a namespace whose only post-write mutation is\n * resolving a contradiction. It fires after the resolution commits, or after a\n * failed resolution/rollback path when durable memory changes are still left on\n * disk. If a failure rolls back cleanly, this is never called, so the catalog\n * never records a write that did not survive (rule #25). Non-mutating verbs\n * (`both-valid`, `needs-more-context`) never trigger it. Callers wire this to\n * `Orchestrator.recordCatalogWrite(namespace, storageDir)`. Must be\n * failure-tolerant: it is fire-and-forget and must never affect resolution.\n */\n onMergedMemoryWritten?: (namespace: string | undefined, storageDir: string) => void;\n}\n\nconst VALID_VERBS: ResolutionVerb[] = [\"keep-a\", \"keep-b\", \"merge\", \"both-valid\", \"needs-more-context\"];\n\nexport function isValidResolutionVerb(value: string): value is ResolutionVerb {\n return VALID_VERBS.includes(value as ResolutionVerb);\n}\n\n/**\n * Execute a resolution verb on a contradiction pair.\n *\n * - `keep-a`: Supersede B, keep A active.\n * - `keep-b`: Supersede A, keep B active.\n * - `merge`: Create or verify a real merged memory, then supersede both inputs.\n * - `both-valid`: Mark pair as reviewed; no memories are superseded.\n * - `needs-more-context`: Defer; no action, short cooldown.\n */\nexport async function executeResolution(\n memoryDir: string,\n storage: StorageManager,\n pairId: string,\n verb: ResolutionVerb,\n options: ExecuteResolutionOptions = {},\n): Promise<ResolutionResult> {\n if (typeof verb !== \"string\" || !isValidResolutionVerb(verb)) {\n throw new Error(`Invalid contradiction resolution verb: ${String(verb)}`);\n }\n\n const pair = readPair(memoryDir, pairId);\n if (!pair) {\n return { pairId, verb, affectedIds: [], message: `Pair ${pairId} not found` };\n }\n\n if (pair.namespace && !options.storageForNamespace) {\n throw new Error(\n \"contradiction resolution requires storageForNamespace for namespaced pairs so callers resolve the correct namespace storage\",\n );\n }\n\n const resolutionStorage = options.storageForNamespace\n ? await options.storageForNamespace(pair.namespace)\n : storage;\n\n if (pair.resolution && pair.resolution !== \"needs-more-context\") {\n return { pairId, verb, affectedIds: [], message: `Pair already resolved with verb \"${pair.resolution}\"` };\n }\n\n const [idA, idB] = pair.memoryIds;\n const affectedIds: string[] = [];\n let message = \"\";\n let supersedeFailed = false;\n let rollbackAfterResolveFailure: (() => Promise<boolean>) | null = null;\n // Deferred catalog-write touch for any resolution that leaves durable namespace\n // memory mutations (issue #1499 sweep, NH1dX / NH3X3). Rule #25: never record a\n // catalog touch for a write that is fully rolled back. Successful mutating\n // resolutions invoke it after `resolvePair` persists. Failed paths invoke it\n // only when rollback inspection shows the namespace still differs from the\n // pre-mutation snapshot.\n let recordCatalogWriteTouch: (() => void) | null = null;\n // Returns the deferred touch fn for a mutating verb (or null when the caller\n // wired no catalog hook), so each branch assigns `recordCatalogWriteTouch`\n // directly in the function body — keeping TS control-flow narrowing intact at\n // the post-commit invocation below.\n const buildCatalogTouch = (): (() => void) | null => {\n if (!options.onMergedMemoryWritten) return null;\n const onMergedMemoryWritten = options.onMergedMemoryWritten;\n const namespace = pair.namespace;\n const storageDir = resolutionStorage.dir;\n return () => onMergedMemoryWritten(namespace, storageDir);\n };\n const catalogWriteTouch = buildCatalogTouch();\n const recordCatalogWriteTouchSafely = (context: string, touch = catalogWriteTouch): void => {\n if (!touch) return;\n try {\n touch();\n } catch (err) {\n log.warn(\n \"[contradiction-resolution] catalog write touch failed for pair=%s context=%s: %s\",\n pairId,\n context,\n err instanceof Error ? err.message : err,\n );\n }\n };\n const touchCatalogIfRollbackLeftChange = async (\n context: string,\n snapshots: MemoryFile[],\n replacement?: Extract<MergeReplacement, { ok: true }>,\n ): Promise<void> => {\n if (!catalogWriteTouch) return;\n if (await rollbackLeftDurableMutation(resolutionStorage, snapshots, replacement)) {\n recordCatalogWriteTouchSafely(context);\n }\n };\n\n switch (verb) {\n case \"keep-a\": {\n const keepTarget = await validateKeepTarget(resolutionStorage, pairId, idA);\n if (!keepTarget.ok) {\n supersedeFailed = true;\n message = keepTarget.message;\n break;\n }\n const sourceB = await loadSourceSnapshot(resolutionStorage, idB);\n const ok = sourceB\n ? await supersedeSafe(resolutionStorage, idB, idA, \"contradiction-resolution:keep-a\")\n : false;\n if (ok) {\n affectedIds.push(idB);\n rollbackAfterResolveFailure = async () =>\n restoreMemorySnapshot(resolutionStorage, sourceB!, \"contradiction-resolution:keep-a-rollback\");\n // keep-a superseded idB in this namespace — record a catalog touch once\n // the resolution durably commits (NH3X3).\n recordCatalogWriteTouch = catalogWriteTouch;\n message = `Kept ${idA}, superseded ${idB}`;\n }\n else {\n supersedeFailed = true;\n const rolledBack = sourceB\n ? await restoreMemorySnapshot(resolutionStorage, sourceB, \"contradiction-resolution:keep-a-rollback\")\n : false;\n if (sourceB && !rolledBack) {\n await touchCatalogIfRollbackLeftChange(\"keep-a-rollback-incomplete\", [sourceB]);\n }\n message = rolledBack\n ? `Supersede failed for ${idB}; restored ${idB} and did not resolve`\n : `Supersede failed for ${idB}; rollback incomplete for ${idB} and pair is not resolved`;\n }\n break;\n }\n case \"keep-b\": {\n const keepTarget = await validateKeepTarget(resolutionStorage, pairId, idB);\n if (!keepTarget.ok) {\n supersedeFailed = true;\n message = keepTarget.message;\n break;\n }\n const sourceA = await loadSourceSnapshot(resolutionStorage, idA);\n const ok = sourceA\n ? await supersedeSafe(resolutionStorage, idA, idB, \"contradiction-resolution:keep-b\")\n : false;\n if (ok) {\n affectedIds.push(idA);\n rollbackAfterResolveFailure = async () =>\n restoreMemorySnapshot(resolutionStorage, sourceA!, \"contradiction-resolution:keep-b-rollback\");\n // keep-b superseded idA in this namespace — record a catalog touch once\n // the resolution durably commits (NH3X3).\n recordCatalogWriteTouch = catalogWriteTouch;\n message = `Kept ${idB}, superseded ${idA}`;\n }\n else {\n supersedeFailed = true;\n const rolledBack = sourceA\n ? await restoreMemorySnapshot(resolutionStorage, sourceA, \"contradiction-resolution:keep-b-rollback\")\n : false;\n if (sourceA && !rolledBack) {\n await touchCatalogIfRollbackLeftChange(\"keep-b-rollback-incomplete\", [sourceA]);\n }\n message = rolledBack\n ? `Supersede failed for ${idA}; restored ${idA} and did not resolve`\n : `Supersede failed for ${idA}; rollback incomplete for ${idA} and pair is not resolved`;\n }\n break;\n }\n case \"merge\": {\n const replacement = await prepareMergeReplacement(resolutionStorage, pairId, idA, idB, options);\n if (!replacement.ok) {\n supersedeFailed = true;\n message = replacement.message;\n break;\n }\n\n const okA = await supersedeSafe(resolutionStorage, idA, replacement.mergedId, \"contradiction-resolution:merge\");\n if (!okA) {\n supersedeFailed = true;\n const rolledBackA = await restoreMemorySnapshot(resolutionStorage, replacement.sourceA);\n message = rolledBackA\n ? `Merge failed for ${idA}; restored ${idA} and did not resolve`\n : `Merge failed for ${idA}; rollback incomplete for ${idA} and pair is not resolved`;\n if (rolledBackA) {\n await cleanupCreatedReplacement(resolutionStorage, replacement);\n }\n else {\n await touchCatalogIfRollbackLeftChange(\"merge-first-rollback-incomplete\", [replacement.sourceA], replacement);\n }\n break;\n }\n\n const okB = await supersedeSafe(resolutionStorage, idB, replacement.mergedId, \"contradiction-resolution:merge\");\n if (!okB) {\n supersedeFailed = true;\n const rolledBackA = await restoreMemorySnapshot(resolutionStorage, replacement.sourceA);\n const rolledBackB = await restoreMemorySnapshot(resolutionStorage, replacement.sourceB);\n message = rolledBackA && rolledBackB\n ? `Merge failed for ${idB}; restored ${idA} and ${idB} and did not resolve`\n : `Merge failed for ${idB}; rollback incomplete for ${[\n rolledBackA ? undefined : idA,\n rolledBackB ? undefined : idB,\n ].filter(Boolean).join(\", \")} and pair is not resolved`;\n if (rolledBackA && rolledBackB) {\n await cleanupCreatedReplacement(resolutionStorage, replacement);\n }\n else {\n await touchCatalogIfRollbackLeftChange(\n \"merge-second-rollback-incomplete\",\n [replacement.sourceA, replacement.sourceB],\n replacement,\n );\n }\n break;\n }\n\n affectedIds.push(idA, idB);\n rollbackAfterResolveFailure = async () => {\n const rolledBackA = await restoreMemorySnapshot(resolutionStorage, replacement.sourceA);\n const rolledBackB = await restoreMemorySnapshot(resolutionStorage, replacement.sourceB);\n if (rolledBackA && rolledBackB) {\n await cleanupCreatedReplacement(resolutionStorage, replacement);\n }\n return rolledBackA && rolledBackB;\n };\n // Catalog write touch (issue #1499 sweep): the merge supersedes BOTH sources\n // (and, when created, writes a fresh merged memory) in the pair's (possibly\n // dynamic) namespace storage — but the resolution is not durable yet\n // (resolvePair persists below, and a failure rolls the merge back). Defer\n // the touch so it fires ONLY after the resolution commits past the rollback\n // point (NH1dX, rule #25). Arm it for EVERY successful merge — even reusing\n // an existing merged-id still supersedes both sources, a namespace mutation\n // that must refresh `lastWriteAt` (NH3X3). Otherwise a dynamic namespace\n // whose only durable mutation is a contradiction merge stays invisible to\n // QMD maintenance / `writtenSince`. Best-effort on the caller side.\n recordCatalogWriteTouch = catalogWriteTouch;\n message = `Both memories superseded by merged ${replacement.mergedId}`;\n break;\n }\n case \"both-valid\": {\n message = \"Pair marked as both-valid; cooldown applied\";\n break;\n }\n case \"needs-more-context\": {\n message = \"Deferred; no action taken, short cooldown applied\";\n break;\n }\n }\n\n if (!supersedeFailed) {\n let resolved = false;\n try {\n resolved = resolvePair(memoryDir, pairId, verb) !== null;\n } catch (err) {\n log.warn(\n \"[contradiction-resolution] failed to persist pair=%s verb=%s: %s\",\n pairId,\n verb,\n err instanceof Error ? err.message : err,\n );\n }\n if (!resolved) {\n if (rollbackAfterResolveFailure) {\n const rolledBack = await rollbackAfterResolveFailure();\n affectedIds.length = 0;\n message = rolledBack\n ? `Resolution persistence failed; rolled back memory changes and did not resolve ${pairId}`\n : \"Resolution persistence failed; rollback incomplete and pair is not resolved\";\n if (!rolledBack && recordCatalogWriteTouch) {\n recordCatalogWriteTouchSafely(\"resolve-persistence-rollback-incomplete\", recordCatalogWriteTouch);\n }\n } else {\n message = \"Resolution persistence failed; pair is not resolved\";\n }\n } else if (recordCatalogWriteTouch) {\n // The resolution durably committed (memory mutated AND the resolution\n // persisted past the rollback point). Only now is it safe to record the\n // catalog write for the namespace mutation (NH1dX / NH3X3, rule #25).\n // Best-effort: the caller's callback swallows errors; guard here so a\n // throwing callback never derails a successful resolution.\n recordCatalogWriteTouchSafely(\"resolved\", recordCatalogWriteTouch);\n }\n }\n log.info(\"[contradiction-resolution] pair=%s verb=%s affected=%d\", pairId, verb, affectedIds.length);\n return { pairId, verb, affectedIds, message };\n}\n\ntype MergeReplacement =\n | {\n ok: true;\n mergedId: string;\n sourceA: MemoryFile;\n sourceB: MemoryFile;\n created: boolean;\n }\n | {\n ok: false;\n message: string;\n };\n\nasync function prepareMergeReplacement(\n storage: StorageManager,\n pairId: string,\n idA: string,\n idB: string,\n options: ExecuteResolutionOptions,\n): Promise<MergeReplacement> {\n const sourceA = await storage.getMemoryById(idA);\n const sourceB = await storage.getMemoryById(idB);\n if (!sourceA || !sourceB) {\n return { ok: false, message: `Merge requires both source memories to exist; not resolving ${pairId}` };\n }\n\n const requestedMergedId = options.mergedMemoryId?.trim();\n if (requestedMergedId) {\n if (requestedMergedId === idA || requestedMergedId === idB) {\n return { ok: false, message: \"Merge replacement must be distinct from both source memories; not resolving\" };\n }\n const replacement = await storage.getMemoryById(requestedMergedId);\n if (!replacement) {\n return { ok: false, message: `Merged memory ${requestedMergedId} not found; not resolving` };\n }\n const replacementStatus = replacement.frontmatter.status ?? \"active\";\n if (replacementStatus !== \"active\") {\n return {\n ok: false,\n message: `Merged memory ${requestedMergedId} is ${replacementStatus}; not resolving`,\n };\n }\n return { ok: true, mergedId: requestedMergedId, sourceA, sourceB, created: false };\n }\n\n const mergedContent = options.mergedContent;\n if (typeof mergedContent !== \"string\" || mergedContent.trim().length === 0) {\n return {\n ok: false,\n message: \"Merge requires mergedMemoryId or mergedContent; no memories changed\",\n };\n }\n\n const category = options.mergedCategory ?? mergedMemoryCategory(sourceA, sourceB);\n let mergedId: string;\n try {\n // Sealed-envelope write (issue #1989 PR4): merged content is\n // LLM-produced — salvage; drops are warn-logged.\n const mergeEnvelope = composeMemoryEnvelope(\n {\n content: mergedContent,\n category,\n confidence: Math.min(sourceA.frontmatter.confidence ?? 0.8, sourceB.frontmatter.confidence ?? 0.8),\n tags: [\"contradiction-resolution\", \"merge\"],\n },\n { source: \"contradiction-resolution\" },\n { salvage: true },\n );\n if (mergeEnvelope.salvageNotes.length > 0) {\n log.warn(`contradiction-resolution write salvaged invalid fields: ${mergeEnvelope.salvageNotes.join(\"; \")}`);\n }\n const mergeResult = await storage.writeSealedMemory(mergeEnvelope, {\n actor: \"contradiction-resolution\",\n lineage: [idA, idB],\n derivedVia: \"merge\",\n });\n mergedId = mergeResult.id;\n if (mergeResult.tombstoneBlocked) {\n // #1645: merged content matched a tombstone (pending_review). Don't\n // supersede both sources to a non-active replacement — that retires\n // the only active copies. Clean up the pending merge and abort the pair.\n await cleanupMemoryId(storage, mergedId);\n return { ok: false, message: `Merged memory for ${pairId} was tombstone-blocked (pending_review); not resolving — sources kept active` };\n }\n } catch (err) {\n log.warn(\n \"[contradiction-resolution] merged memory creation failed for %s: %s\",\n pairId,\n err instanceof Error ? err.message : err,\n );\n return { ok: false, message: `Merged memory could not be created; not resolving ${pairId}` };\n }\n const replacement = await storage.getMemoryById(mergedId);\n if (!replacement) {\n await cleanupMemoryId(storage, mergedId);\n return { ok: false, message: `Merged memory ${mergedId} could not be verified; not resolving` };\n }\n return { ok: true, mergedId, sourceA, sourceB, created: true };\n}\n\nfunction mergedMemoryCategory(sourceA: MemoryFile, sourceB: MemoryFile): MemoryCategory {\n return sourceA.frontmatter.category === sourceB.frontmatter.category\n ? sourceA.frontmatter.category\n : \"fact\";\n}\n\ntype KeepTargetValidation =\n | { ok: true }\n | { ok: false; message: string };\n\nasync function validateKeepTarget(\n storage: StorageManager,\n pairId: string,\n keepId: string,\n): Promise<KeepTargetValidation> {\n const target = await loadSourceSnapshot(storage, keepId);\n if (!target) {\n return { ok: false, message: `Kept memory ${keepId} not found; not resolving ${pairId}` };\n }\n\n const status = target.frontmatter.status ?? \"active\";\n if (status !== \"active\") {\n return { ok: false, message: `Kept memory ${keepId} is ${status}; not resolving ${pairId}` };\n }\n\n return { ok: true };\n}\n\nasync function loadSourceSnapshot(storage: StorageManager, memoryId: string): Promise<MemoryFile | null> {\n try {\n return await storage.getMemoryById(memoryId);\n } catch (err) {\n log.warn(\n \"[contradiction-resolution] source snapshot failed for %s: %s\",\n memoryId,\n err instanceof Error ? err.message : err,\n );\n return null;\n }\n}\n\nasync function restoreMemorySnapshot(\n storage: StorageManager,\n memory: MemoryFile,\n reasonCode = \"contradiction-resolution:merge-rollback\",\n): Promise<boolean> {\n try {\n const current = await storage.getMemoryById(memory.frontmatter.id);\n if (!current) return false;\n const restoredFrontmatter: Partial<MemoryFile[\"frontmatter\"]> = {\n ...memory.frontmatter,\n status: memory.frontmatter.status,\n supersededBy: memory.frontmatter.supersededBy,\n supersededAt: memory.frontmatter.supersededAt,\n };\n return await storage.writeMemoryFrontmatter(current, restoredFrontmatter, {\n actor: \"contradiction-resolution\",\n reasonCode,\n });\n } catch (err) {\n log.warn(\n \"[contradiction-resolution] rollback failed for %s: %s\",\n memory.frontmatter.id,\n err instanceof Error ? err.message : err,\n );\n return false;\n }\n}\n\nasync function rollbackLeftDurableMutation(\n storage: StorageManager,\n snapshots: MemoryFile[],\n replacement?: Extract<MergeReplacement, { ok: true }>,\n): Promise<boolean> {\n for (const snapshot of snapshots) {\n try {\n const current = await storage.getMemoryById(snapshot.frontmatter.id);\n if (!current) return true;\n if (supersessionStateChanged(current, snapshot)) return true;\n } catch (err) {\n log.warn(\n \"[contradiction-resolution] rollback inspection failed for %s: %s\",\n snapshot.frontmatter.id,\n err instanceof Error ? err.message : err,\n );\n return true;\n }\n }\n\n if (replacement?.created) {\n try {\n return (await storage.getMemoryById(replacement.mergedId)) !== null;\n } catch (err) {\n log.warn(\n \"[contradiction-resolution] rollback replacement inspection failed for %s: %s\",\n replacement.mergedId,\n err instanceof Error ? err.message : err,\n );\n return true;\n }\n }\n\n return false;\n}\n\nfunction supersessionStateChanged(current: MemoryFile, snapshot: MemoryFile): boolean {\n return (\n current.frontmatter.status !== snapshot.frontmatter.status ||\n current.frontmatter.supersededBy !== snapshot.frontmatter.supersededBy ||\n current.frontmatter.supersededAt !== snapshot.frontmatter.supersededAt\n );\n}\n\nasync function cleanupCreatedReplacement(storage: StorageManager, replacement: Extract<MergeReplacement, { ok: true }>): Promise<void> {\n if (!replacement.created) return;\n await cleanupMemoryId(storage, replacement.mergedId);\n}\n\nasync function cleanupMemoryId(storage: StorageManager, memoryId: string): Promise<void> {\n try {\n const memory = await storage.getMemoryById(memoryId);\n const invalidated = await storage.invalidateMemory(memoryId);\n if (invalidated && memory?.frontmatter.category === \"fact\") {\n await storage.removeFactContentHashesForMemories([memory]);\n }\n } catch (err) {\n log.warn(\n \"[contradiction-resolution] cleanup failed for merged memory %s: %s\",\n memoryId,\n err instanceof Error ? err.message : err,\n );\n }\n}\n\nasync function supersedeSafe(\n storage: StorageManager,\n oldId: string,\n newId: string,\n reason: string,\n): Promise<boolean> {\n try {\n const result = await storage.supersedeMemory(oldId, newId, reason);\n if (result === false) {\n log.warn(\"[contradiction-resolution] supersede returned false for %s → %s\", oldId, newId);\n return false;\n }\n return true;\n } catch (err) {\n log.warn(\n \"[contradiction-resolution] supersede failed %s → %s: %s\",\n oldId,\n newId,\n err instanceof Error ? err.message : err,\n );\n return false;\n }\n}\n"],"mappings":"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","names":["replacement"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/mcp-read-only-tools.ts"],"sourcesContent":["/**\n * Conservative allowlist of canonical MCP tool suffixes that are\n * unambiguously read-only. Tools in this set are tagged with\n * `annotations: { readOnlyHint: true }` so ChatGPT (and other MCP\n * clients that honor the hint) can skip per-call confirmation.\n *\n * The list is suffix-based so it covers both the `remnic.*` and\n * `engram.*` naming forms. Anything not on it stays unannotated:\n * uncertainty is resolved as \"might mutate\".\n *\n * Excluded by construction: anything that writes, runs a pipeline,\n * flushes, applies, records, imports, or destructively deletes.\n */\nexport const MCP_READ_ONLY_TOOL_SUFFIXES: Readonly<Record<string, true>> = {\n recall: true,\n recall_explain: true,\n recall_tier_explain: true,\n recall_xray: true,\n who_knows: true,\n briefing: true,\n wearables_status: true,\n transcript_day: true,\n transcript_search: true,\n transcript_memories: true,\n meetings_list: true,\n meetings_get: true,\n action_confidence: true,\n capsule_list: true,\n procedural_stats: true,\n memory_get: true,\n memory_timeline: true,\n entity_get: true,\n review_queue_list: true,\n lcm_search: true,\n continuity_incident_list: true,\n identity_anchor_get: true,\n memory_identity: true,\n memory_search: true,\n external_wiki_search: true,\n memory_profile: true,\n memory_entities_list: true,\n memory_questions: true,\n memory_last_recall: true,\n memory_intent_debug: true,\n memory_qmd_debug: true,\n memory_graph_explain: true,\n graph_snapshot: true,\n review_list: true,\n profiling_report: true,\n peer_list: true,\n peer_get: true,\n peer_profile_get: true,\n console_state: true,\n dreams_status: true,\n codegraph_list_projects: true,\n codegraph_index_status: true,\n codegraph_search_graph: true,\n codegraph_trace_path: true,\n codegraph_detect_changes: true,\n codegraph_query_graph: true,\n codegraph_get_schema: true,\n codegraph_get_snippet: true,\n codegraph_get_architecture: true,\n codegraph_search_code: true,\n support_passport_memory_preview: true,\n support_passport_cards_list: true,\n support_passport_grants_list: true,\n};\n"],"mappings":";AAaO,IAAM,8BAA8D;AAAA,EACzE,QAAQ;AAAA,EACR,gBAAgB;AAAA,EAChB,qBAAqB;AAAA,EACrB,aAAa;AAAA,EACb,WAAW;AAAA,EACX,UAAU;AAAA,EACV,kBAAkB;AAAA,EAClB,gBAAgB;AAAA,EAChB,mBAAmB;AAAA,EACnB,qBAAqB;AAAA,EACrB,eAAe;AAAA,EACf,cAAc;AAAA,EACd,mBAAmB;AAAA,EACnB,cAAc;AAAA,EACd,kBAAkB;AAAA,EAClB,YAAY;AAAA,EACZ,iBAAiB;AAAA,EACjB,YAAY;AAAA,EACZ,mBAAmB;AAAA,EACnB,YAAY;AAAA,EACZ,0BAA0B;AAAA,EAC1B,qBAAqB;AAAA,EACrB,iBAAiB;AAAA,EACjB,eAAe;AAAA,EACf,sBAAsB;AAAA,EACtB,gBAAgB;AAAA,EAChB,sBAAsB;AAAA,EACtB,kBAAkB;AAAA,EAClB,oBAAoB;AAAA,EACpB,qBAAqB;AAAA,EACrB,kBAAkB;AAAA,EAClB,sBAAsB;AAAA,EACtB,gBAAgB;AAAA,EAChB,aAAa;AAAA,EACb,kBAAkB;AAAA,EAClB,WAAW;AAAA,EACX,UAAU;AAAA,EACV,kBAAkB;AAAA,EAClB,eAAe;AAAA,EACf,eAAe;AAAA,EACf,yBAAyB;AAAA,EACzB,wBAAwB;AAAA,EACxB,wBAAwB;AAAA,EACxB,sBAAsB;AAAA,EACtB,0BAA0B;AAAA,EAC1B,uBAAuB;AAAA,EACvB,sBAAsB;AAAA,EACtB,uBAAuB;AAAA,EACvB,4BAA4B;AAAA,EAC5B,uBAAuB;AAAA,EACvB,iCAAiC;AAAA,EACjC,6BAA6B;AAAA,EAC7B,8BAA8B;AAChC;","names":[]}
|