@remnic/core 9.27.0 → 9.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/access-admin-ops-surface.d.ts +13 -10
- package/dist/access-admin-ops-surface.js +43 -38
- package/dist/access-audit.js +2 -2
- package/dist/access-authorization-probe.d.ts +13 -10
- package/dist/access-authorization-probe.js +44 -39
- package/dist/access-boundary.d.ts +13 -10
- package/dist/access-boundary.js +43 -38
- package/dist/access-cli.js +100 -94
- package/dist/access-cli.js.map +1 -1
- package/dist/access-health-types.d.ts +10 -1
- package/dist/access-http-lcm-compaction.d.ts +13 -10
- package/dist/access-http.d.ts +13 -10
- package/dist/access-http.js +53 -48
- package/dist/access-identity-continuity-surface.d.ts +9 -8
- package/dist/access-identity-continuity-surface.js +43 -38
- package/dist/access-lcm-surface.d.ts +13 -10
- package/dist/access-lcm-surface.js +43 -38
- package/dist/access-mcp.d.ts +13 -10
- package/dist/access-mcp.js +48 -43
- package/dist/access-memory-search-fanout.d.ts +2 -2
- package/dist/{access-namespace-preflight-wRSZEO5V.d.ts → access-namespace-preflight-BiQ_-iUJ.d.ts} +1 -1
- package/dist/access-namespace-preflight.d.ts +3 -3
- package/dist/access-namespace-preflight.js +43 -38
- package/dist/access-observe-write-surface.d.ts +13 -10
- package/dist/access-observe-write-surface.js +43 -38
- package/dist/access-operations-batch.js +45 -40
- package/dist/access-operations.d.ts +16 -13
- package/dist/access-operations.js +47 -42
- package/dist/access-recall-concurrency.d.ts +13 -10
- package/dist/access-recall-concurrency.js +43 -38
- package/dist/access-recall-response.d.ts +13 -10
- package/dist/access-recall-response.js +43 -38
- package/dist/access-recall-surface.d.ts +13 -10
- package/dist/access-recall-surface.js +43 -38
- package/dist/access-schema.d.ts +80 -80
- package/dist/access-schema.js +4 -4
- package/dist/{access-service-B4-Fu9x2.d.ts → access-service-CXkIgcGh.d.ts} +12 -8
- package/dist/access-service.d.ts +13 -10
- package/dist/access-service.js +43 -38
- package/dist/access-surface-catalog.d.ts +13 -10
- package/dist/access-wearables-meetings-surface.d.ts +4 -3
- package/dist/action-confidence.d.ts +1 -1
- package/dist/active-memory-bridge.d.ts +1 -1
- package/dist/active-recall.d.ts +1 -1
- package/dist/active-recall.js +4 -2
- package/dist/active-recall.js.map +1 -1
- package/dist/adapters/index.js +7 -7
- package/dist/adapters/registry.js +3 -3
- package/dist/behavior-learner.d.ts +1 -1
- package/dist/behavior-signals.d.ts +1 -1
- package/dist/bootstrap.d.ts +9 -8
- package/dist/briefing.d.ts +3 -2
- package/dist/briefing.js +12 -10
- package/dist/buffer-surprise-report.d.ts +1 -1
- package/dist/buffer.d.ts +3 -2
- package/dist/bulk-import/index.d.ts +3 -3
- package/dist/calibration.d.ts +1 -1
- package/dist/capabilities.d.ts +1 -1
- package/dist/{capsule-crypto-YO5QJ6L3.js → capsule-crypto-7FJQINUR.js} +2 -2
- package/dist/{catalog-BLLxZ5L5.d.ts → catalog-Bouj8Gfg.d.ts} +1 -1
- package/dist/causal-behavior.d.ts +1 -1
- package/dist/causal-consolidation.d.ts +1 -1
- package/dist/causal-consolidation.js +13 -11
- package/dist/causal-consolidation.js.map +1 -1
- package/dist/causal-trajectory-graph.d.ts +1 -1
- package/dist/{chunk-7RZJWZ46.js → chunk-227UFSEQ.js} +5 -5
- package/dist/{chunk-UT3OQPFA.js → chunk-2BEJ4XCF.js} +2 -2
- package/dist/{chunk-EOLC6EXV.js → chunk-2FYNLDKJ.js} +24 -3
- package/dist/chunk-2FYNLDKJ.js.map +1 -0
- package/dist/{chunk-BXLOS5AJ.js → chunk-2NLLXCJG.js} +2 -2
- package/dist/{chunk-6NNNURLK.js → chunk-2PHX3I5C.js} +8 -8
- package/dist/{chunk-HORJVJ6K.js → chunk-2TQ2UFKH.js} +65 -60
- package/dist/chunk-2TQ2UFKH.js.map +1 -0
- package/dist/{chunk-RPA2RX5P.js → chunk-3BSJBIZ3.js} +2 -2
- package/dist/{chunk-JCVQV6QX.js → chunk-3UOSVTFR.js} +11 -11
- package/dist/{chunk-AJ7QCSIS.js → chunk-7CPOZH6Y.js} +2 -2
- package/dist/{chunk-MEBQFQ26.js → chunk-AHFZXUZM.js} +7 -5
- package/dist/chunk-AHFZXUZM.js.map +1 -0
- package/dist/{chunk-U3TH5Q2Q.js → chunk-ALTP3MFS.js} +2 -2
- package/dist/{chunk-77CNV7DY.js → chunk-BLAKS3CP.js} +4 -4
- package/dist/{chunk-XQ4EH6DI.js → chunk-BR4UFMLO.js} +2 -2
- package/dist/chunk-CVILPI77.js +432 -0
- package/dist/chunk-CVILPI77.js.map +1 -0
- package/dist/{chunk-WEAO45M4.js → chunk-DKQM5Q6Q.js} +2 -2
- package/dist/{chunk-CSXHRAJJ.js → chunk-EG64LXKL.js} +3 -3
- package/dist/{chunk-PTN6UAUA.js → chunk-ES36HTSC.js} +4 -4
- package/dist/{chunk-5BRT4Z2I.js → chunk-EZWWCN7I.js} +2 -2
- package/dist/{chunk-XKGPIWGI.js → chunk-FBZTWUK5.js} +5 -5
- package/dist/{chunk-GJJBPI6I.js → chunk-FOCEI3TN.js} +6 -3
- package/dist/chunk-FOCEI3TN.js.map +1 -0
- package/dist/{chunk-FRVTCCGD.js → chunk-GJPCP5CA.js} +2 -2
- package/dist/{chunk-CI26JDZA.js → chunk-GQSMYXKL.js} +6 -4
- package/dist/{chunk-CI26JDZA.js.map → chunk-GQSMYXKL.js.map} +1 -1
- package/dist/{chunk-TGQ2NTWH.js → chunk-IJ6WHMFB.js} +16 -2
- package/dist/{chunk-TGQ2NTWH.js.map → chunk-IJ6WHMFB.js.map} +1 -1
- package/dist/{chunk-VDX2J7OX.js → chunk-IPLYGWQF.js} +6 -6
- package/dist/{chunk-YSGBD3QN.js → chunk-IYT632D5.js} +3 -3
- package/dist/{chunk-2QSZNTDO.js → chunk-JBPKEARU.js} +7 -7
- package/dist/{chunk-RSYJV25N.js → chunk-JIF3VRKQ.js} +8 -8
- package/dist/{chunk-V25OWXAC.js → chunk-JQ7T5VDC.js} +1 -1
- package/dist/chunk-JQ7T5VDC.js.map +1 -0
- package/dist/{chunk-EUSLKI4X.js → chunk-KKBTBUVT.js} +2 -2
- package/dist/{chunk-RNY3BO35.js → chunk-LIHC3T43.js} +2 -2
- package/dist/{chunk-RGJBM7Z3.js → chunk-MJPV4LBH.js} +79 -79
- package/dist/{chunk-RGJBM7Z3.js.map → chunk-MJPV4LBH.js.map} +1 -1
- package/dist/{chunk-UGH6VPPO.js → chunk-MWTMTW2T.js} +2 -2
- package/dist/{chunk-MG6MNSP4.js → chunk-MXJ3VP6S.js} +6 -6
- package/dist/{chunk-XKO7BM2C.js → chunk-NBNDVNJ2.js} +186 -25
- package/dist/chunk-NBNDVNJ2.js.map +1 -0
- package/dist/{chunk-M5EPMNS4.js → chunk-P2CLYN6H.js} +2 -2
- package/dist/{chunk-BNGPHGVZ.js → chunk-PXQM2HOG.js} +13 -6
- package/dist/{chunk-BNGPHGVZ.js.map → chunk-PXQM2HOG.js.map} +1 -1
- package/dist/{chunk-CIQXI47W.js → chunk-Q5VJUNXA.js} +1192 -268
- package/dist/chunk-Q5VJUNXA.js.map +1 -0
- package/dist/{chunk-KGTD2KZJ.js → chunk-Q6YJW6KE.js} +2 -2
- package/dist/{chunk-X3SQYAPP.js → chunk-R372P2EH.js} +2 -2
- package/dist/{chunk-4MJZTF42.js → chunk-RKXZ7P4V.js} +3 -3
- package/dist/chunk-RMMVQQA6.js +70 -0
- package/dist/chunk-RMMVQQA6.js.map +1 -0
- package/dist/{chunk-2RAC4JIG.js → chunk-S24E4MBQ.js} +14 -14
- package/dist/chunk-S24E4MBQ.js.map +1 -0
- package/dist/{chunk-RYHWZ52A.js → chunk-S2BBISHU.js} +4 -4
- package/dist/{chunk-LLISELSY.js → chunk-TBMRQZDY.js} +2 -2
- package/dist/{chunk-FMLKOL2Z.js → chunk-TC3VNKGX.js} +4 -4
- package/dist/{chunk-7K5Q6COX.js → chunk-TVVEYCNW.js} +4 -4
- package/dist/{chunk-NUWMZIYZ.js → chunk-UR5SIN7H.js} +53 -35
- package/dist/chunk-UR5SIN7H.js.map +1 -0
- package/dist/{chunk-JDOJZTFF.js → chunk-VFNI3RPT.js} +2 -2
- package/dist/chunk-VNA7W7QF.js +134 -0
- package/dist/chunk-VNA7W7QF.js.map +1 -0
- package/dist/{chunk-EYJSW2RV.js → chunk-VYYNBJXH.js} +5 -5
- package/dist/chunk-WNIUPHWV.js +152 -0
- package/dist/chunk-WNIUPHWV.js.map +1 -0
- package/dist/chunk-WRJVB3C2.js +118 -0
- package/dist/chunk-WRJVB3C2.js.map +1 -0
- package/dist/{chunk-BHLN7L7M.js → chunk-WVVK2RFM.js} +6 -6
- package/dist/{chunk-DQEMWVMT.js → chunk-X7Y7WX73.js} +1 -1
- package/dist/{chunk-HEXSNYJD.js → chunk-YOBNCD3Z.js} +6 -6
- package/dist/chunk-YXZG5TVN.js +40 -0
- package/dist/chunk-YXZG5TVN.js.map +1 -0
- package/dist/{chunk-4DJQYKMN.js → chunk-ZIKENA2C.js} +2 -2
- package/dist/chunk-ZIKENA2C.js.map +1 -0
- package/dist/{chunk-SK3EYPKC.js → chunk-ZN4UZWMT.js} +2 -2
- package/dist/{cli-Bz7X7dGx.d.ts → cli-DJbkaZBU.d.ts} +5 -5
- package/dist/cli.d.ts +14 -12
- package/dist/cli.js +79 -74
- package/dist/compounding/engine.d.ts +3 -2
- package/dist/compounding/engine.js +12 -10
- package/dist/compounding/preference-consolidator.d.ts +1 -1
- package/dist/compression-optimizer.d.ts +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.js +4 -2
- package/dist/connectors/codex-materialize-runner.d.ts +1 -1
- package/dist/connectors/codex-materialize-runner.js +12 -10
- package/dist/connectors/codex-materialize.d.ts +1 -1
- package/dist/connectors/index.d.ts +1 -1
- package/dist/connectors/index.js +12 -10
- package/dist/consolidation-provenance-check.d.ts +3 -2
- package/dist/consolidation-undo.d.ts +3 -2
- package/dist/contradiction/index.d.ts +3 -2
- package/dist/conversation-index/backend.d.ts +1 -1
- package/dist/conversation-index/backend.js +2 -2
- package/dist/conversation-index/chunker.d.ts +1 -1
- package/dist/conversation-index/faiss-adapter.d.ts +1 -1
- package/dist/conversation-index/indexer.d.ts +1 -1
- package/dist/conversation-index/search.d.ts +1 -1
- package/dist/corpus-watermark.d.ts +15 -2
- package/dist/corpus-watermark.js +25 -21
- package/dist/day-summary.d.ts +1 -1
- package/dist/delinearize.d.ts +1 -1
- package/dist/direct-answer-wiring.d.ts +1 -1
- package/dist/direct-answer.d.ts +1 -1
- package/dist/embedding-fallback.d.ts +1 -1
- package/dist/enrichment/index.d.ts +1 -1
- package/dist/entity-id-normalization.d.ts +4 -0
- package/dist/entity-id-normalization.js +10 -0
- package/dist/entity-id-normalization.js.map +1 -0
- package/dist/entity-retrieval-boundaries.d.ts +3 -0
- package/dist/entity-retrieval-boundaries.js +8 -0
- package/dist/entity-retrieval-boundaries.js.map +1 -0
- package/dist/entity-retrieval.d.ts +3 -2
- package/dist/entity-retrieval.js +13 -10
- package/dist/entity-schema.d.ts +1 -1
- package/dist/entity-schema.js +1 -1
- package/dist/explicit-capture.d.ts +9 -8
- package/dist/extraction-error-classification.d.ts +1 -1
- package/dist/extraction-faithfulness.d.ts +1 -1
- package/dist/extraction-judge-telemetry.d.ts +1 -1
- package/dist/extraction-judge-training.d.ts +1 -1
- package/dist/extraction-judge.d.ts +1 -1
- package/dist/extraction-liveness.d.ts +1 -1
- package/dist/extraction.d.ts +1 -1
- package/dist/fallback-llm.d.ts +1 -1
- package/dist/{first-start-migration-LEXL3XYL.js → first-start-migration-BKNGOFED.js} +4 -4
- package/dist/graph-dashboard-diff.d.ts +1 -1
- package/dist/graph-dashboard-key.d.ts +1 -1
- package/dist/graph-dashboard-parser.d.ts +1 -1
- package/dist/graph-edge-reinforcement.d.ts +1 -1
- package/dist/graph-snapshot.d.ts +1 -1
- package/dist/graph.d.ts +1 -1
- package/dist/importance.d.ts +1 -1
- package/dist/importers/index.d.ts +1 -1
- package/dist/in-flight-reads.d.ts +1 -1
- package/dist/index.d.ts +409 -407
- package/dist/index.js +147 -141
- package/dist/intent.d.ts +1 -1
- package/dist/lcm/engine.d.ts +1 -1
- package/dist/lcm/engine.js +3 -3
- package/dist/lcm/index.d.ts +1 -1
- package/dist/lcm/index.js +9 -9
- package/dist/lcm/tools.d.ts +1 -1
- package/dist/lifecycle.d.ts +1 -1
- package/dist/live-connectors-runner.d.ts +1 -1
- package/dist/local-llm.d.ts +1 -1
- package/dist/local-model-endpoint.d.ts +1 -1
- package/dist/maintenance/memory-governance.d.ts +1 -1
- package/dist/maintenance/memory-governance.js +12 -10
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.d.ts +3 -2
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +12 -10
- package/dist/maintenance/rebuild-memory-projection.d.ts +3 -2
- package/dist/maintenance/rebuild-memory-projection.js +13 -11
- package/dist/{maintenance-DgnD2z8S.d.ts → maintenance-DveW6azo.d.ts} +3 -3
- package/dist/mcp-memory-inspector-app.d.ts +13 -10
- package/dist/memory-action-policy.d.ts +1 -1
- package/dist/memory-cache.d.ts +1 -1
- package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
- package/dist/memory-projection-mutations.d.ts +4 -0
- package/dist/memory-projection-mutations.js +15 -0
- package/dist/memory-projection-mutations.js.map +1 -0
- package/dist/memory-projection-store.d.ts +1 -1
- package/dist/memory-provenance.d.ts +1 -1
- package/dist/memory-worth-outcomes.d.ts +3 -2
- package/dist/models-json.d.ts +1 -1
- package/dist/namespaces/migrate.d.ts +4 -3
- package/dist/namespaces/migrate.js +22 -20
- package/dist/namespaces/principal.d.ts +1 -1
- package/dist/namespaces/search.d.ts +1 -1
- package/dist/namespaces/search.js +9 -9
- package/dist/namespaces/storage.d.ts +4 -3
- package/dist/namespaces/storage.js +12 -10
- package/dist/native-knowledge.d.ts +1 -1
- package/dist/offline-sync-impression-drain.d.ts +1 -1
- package/dist/offline-sync-impression-drain.js +3 -3
- package/dist/operator-doctor-corpus.d.ts +22 -3
- package/dist/operator-doctor-corpus.js +32 -22
- package/dist/operator-doctor-replica.d.ts +52 -0
- package/dist/operator-doctor-replica.js +15 -0
- package/dist/operator-doctor-replica.js.map +1 -0
- package/dist/operator-toolkit.d.ts +7 -3
- package/dist/operator-toolkit.js +33 -27
- package/dist/orchestration/compression-guideline-coordinator.d.ts +3 -2
- package/dist/orchestration/maintenance.d.ts +5 -4
- package/dist/orchestration/maintenance.js +17 -15
- package/dist/{orchestrator-rIHmHpv1.d.ts → orchestrator-Cvlgpe6I.d.ts} +7 -7
- package/dist/orchestrator.d.ts +9 -8
- package/dist/orchestrator.js +99 -93
- package/dist/patterns-cli.d.ts +1 -1
- package/dist/{pipeline-Bq5zU6Bp.d.ts → pipeline-BveYVaz7.d.ts} +1 -1
- package/dist/policy-runtime.d.ts +1 -1
- package/dist/proactive-contention.d.ts +1 -1
- package/dist/provenance.d.ts +1 -1
- package/dist/{qmd-3ILJkC70.d.ts → qmd-CLWDsbYR.d.ts} +1 -1
- package/dist/qmd-preflight.d.ts +2 -2
- package/dist/qmd-recall-cache.d.ts +1 -1
- package/dist/qmd.d.ts +2 -2
- package/dist/recall-concurrency-config.d.ts +1 -1
- package/dist/recall-disclosure-escalation.d.ts +1 -1
- package/dist/recall-explain-renderer.d.ts +1 -1
- package/dist/recall-explain-renderer.js +3 -3
- package/dist/recall-memory-map.d.ts +1 -1
- package/dist/recall-planner-llm.d.ts +1 -1
- package/dist/recall-state.d.ts +1 -1
- package/dist/recall-state.js +2 -2
- package/dist/recall-tag-filter.d.ts +1 -1
- package/dist/recall-timings.d.ts +1 -1
- package/dist/recall-xray-cli.d.ts +1 -1
- package/dist/recall-xray-cli.js +4 -4
- package/dist/recall-xray-renderer.d.ts +1 -1
- package/dist/recall-xray-renderer.js +3 -3
- package/dist/recall-xray.d.ts +1 -1
- package/dist/recall-xray.js +2 -2
- package/dist/replay/normalizers/chatgpt.d.ts +1 -1
- package/dist/replay/normalizers/claude.d.ts +1 -1
- package/dist/replay/normalizers/openclaw.d.ts +1 -1
- package/dist/replay/normalizers/shared.d.ts +1 -1
- package/dist/replay/runner.d.ts +1 -1
- package/dist/replay/types.d.ts +1 -1
- package/dist/replica-divergence.d.ts +279 -0
- package/dist/replica-divergence.js +28 -0
- package/dist/replica-divergence.js.map +1 -0
- package/dist/replica-peers-config.d.ts +5 -0
- package/dist/replica-peers-config.js +13 -0
- package/dist/replica-peers-config.js.map +1 -0
- package/dist/resolve-auth-token.d.ts +21 -2
- package/dist/resolve-auth-token.js +5 -1
- package/dist/resume-bundles.js +5 -3
- package/dist/retrieval-agents.d.ts +2 -2
- package/dist/retrieval-tiers.d.ts +1 -1
- package/dist/routing/engine.d.ts +1 -1
- package/dist/routing/store.d.ts +1 -1
- package/dist/salvage-envelope.d.ts +1 -1
- package/dist/schemas.d.ts +76 -76
- package/dist/{scope-profiles-D0-ff4xT.d.ts → scope-profiles-BVf3DZ7z.d.ts} +1 -1
- package/dist/search/embed-helper.d.ts +1 -1
- package/dist/search/factory.d.ts +1 -1
- package/dist/search/factory.js +8 -8
- package/dist/search/index.d.ts +1 -1
- package/dist/search/index.js +13 -13
- package/dist/search/lancedb-backend.d.ts +1 -1
- package/dist/search/lancedb-backend.js +2 -2
- package/dist/search/meilisearch-backend.d.ts +1 -1
- package/dist/search/meilisearch-backend.js +2 -2
- package/dist/search/noop-backend.d.ts +1 -1
- package/dist/search/orama-backend.d.ts +1 -1
- package/dist/search/orama-backend.js +2 -2
- package/dist/search/port.d.ts +1 -1
- package/dist/search/remote-backend.d.ts +1 -1
- package/dist/{semantic-consolidation-4Tfh7c1g.d.ts → semantic-consolidation-DXLV47y2.d.ts} +1 -1
- package/dist/semantic-consolidation.d.ts +2 -2
- package/dist/semantic-consolidation.js +13 -11
- package/dist/semantic-rule-promotion.js +12 -10
- package/dist/semantic-rule-verifier.d.ts +1 -1
- package/dist/semantic-rule-verifier.js +12 -10
- package/dist/{service-cTvil7D_.d.ts → service-DbgPTeMJ.d.ts} +2 -2
- package/dist/session-observer-bands.d.ts +1 -1
- package/dist/session-observer-state.d.ts +1 -1
- package/dist/shared-context/manager.d.ts +9 -9
- package/dist/signal.d.ts +1 -1
- package/dist/{storage-C9ibpTme.d.ts → storage-DOzTm8wG.d.ts} +13 -19
- package/dist/storage.d.ts +3 -2
- package/dist/storage.js +13 -10
- package/dist/summarizer.d.ts +1 -1
- package/dist/summary-snapshot.d.ts +1 -1
- package/dist/temporal-supersession.d.ts +3 -2
- package/dist/temporal-timeline-recall.d.ts +1 -1
- package/dist/temporal-validity.d.ts +1 -1
- package/dist/threading.d.ts +1 -1
- package/dist/tier-migration.d.ts +3 -2
- package/dist/tier-routing.d.ts +1 -1
- package/dist/topics.d.ts +1 -1
- package/dist/transcript.d.ts +1 -1
- package/dist/transfer/autodetect.js +1 -1
- package/dist/transfer/backup.js +2 -2
- package/dist/transfer/capsule-export.js +3 -3
- package/dist/transfer/capsule-import.js +2 -2
- package/dist/transfer/types.d.ts +66 -66
- package/dist/trust-score-stage.d.ts +1 -1
- package/dist/trust-score.d.ts +1 -1
- package/dist/{types-DHBxIF0s.d.ts → types-BA7W-_xy.d.ts} +53 -4
- package/dist/types.d.ts +1 -1
- package/dist/types.js +1 -1
- package/dist/utility-runtime.d.ts +1 -1
- package/dist/verified-recall.js +12 -10
- package/dist/write-envelope.d.ts +1 -1
- package/package.json +2 -2
- package/src/access-health-types.ts +8 -0
- package/src/access-http.test.ts +4 -0
- package/src/access-service-health.test.ts +23 -0
- package/src/access-service.ts +27 -4
- package/src/cli.ts +9 -7
- package/src/config.ts +2 -1
- package/src/corpus-watermark.test.ts +21 -0
- package/src/corpus-watermark.ts +25 -3
- package/src/entity-id-normalization.ts +55 -0
- package/src/entity-retrieval-boundaries.ts +134 -0
- package/src/entity-retrieval.ts +253 -27
- package/src/entity-schema.ts +2 -1
- package/src/memory-projection-mutations.ts +132 -0
- package/src/namespaces/storage.ts +5 -3
- package/src/operator-doctor-corpus.ts +41 -0
- package/src/operator-doctor-replica.test.ts +261 -0
- package/src/operator-doctor-replica.ts +129 -0
- package/src/operator-toolkit.ts +5 -4
- package/src/orchestration/orchestrator-init.ts +2 -2
- package/src/replica-divergence.test.ts +1475 -0
- package/src/replica-divergence.ts +910 -0
- package/src/replica-peers-config.ts +250 -0
- package/src/resolve-auth-token.ts +38 -0
- package/src/storage/entity-canonical-id-migration-adapter.ts +72 -0
- package/src/storage/entity-canonical-id-migration-runner.ts +68 -0
- package/src/storage/entity-canonical-id-migration.ts +904 -0
- package/src/storage/entity-store.ts +44 -0
- package/src/storage/memory-frontmatter-metadata.ts +17 -0
- package/src/storage/memory-migration-serialization.ts +37 -0
- package/src/storage/memory-read-store.ts +3 -2
- package/src/storage/profile-header.ts +8 -7
- package/src/storage-profile-timestamp.test.ts +68 -0
- package/src/storage.ts +68 -69
- package/src/types.ts +3 -3
- package/dist/chunk-2RAC4JIG.js.map +0 -1
- package/dist/chunk-4DJQYKMN.js.map +0 -1
- package/dist/chunk-CIQXI47W.js.map +0 -1
- package/dist/chunk-EOLC6EXV.js.map +0 -1
- package/dist/chunk-GJJBPI6I.js.map +0 -1
- package/dist/chunk-HORJVJ6K.js.map +0 -1
- package/dist/chunk-MEBQFQ26.js.map +0 -1
- package/dist/chunk-NUWMZIYZ.js.map +0 -1
- package/dist/chunk-V25OWXAC.js.map +0 -1
- package/dist/chunk-XKO7BM2C.js.map +0 -1
- /package/dist/{capsule-crypto-YO5QJ6L3.js.map → capsule-crypto-7FJQINUR.js.map} +0 -0
- /package/dist/{chunk-7RZJWZ46.js.map → chunk-227UFSEQ.js.map} +0 -0
- /package/dist/{chunk-UT3OQPFA.js.map → chunk-2BEJ4XCF.js.map} +0 -0
- /package/dist/{chunk-BXLOS5AJ.js.map → chunk-2NLLXCJG.js.map} +0 -0
- /package/dist/{chunk-6NNNURLK.js.map → chunk-2PHX3I5C.js.map} +0 -0
- /package/dist/{chunk-RPA2RX5P.js.map → chunk-3BSJBIZ3.js.map} +0 -0
- /package/dist/{chunk-JCVQV6QX.js.map → chunk-3UOSVTFR.js.map} +0 -0
- /package/dist/{chunk-AJ7QCSIS.js.map → chunk-7CPOZH6Y.js.map} +0 -0
- /package/dist/{chunk-U3TH5Q2Q.js.map → chunk-ALTP3MFS.js.map} +0 -0
- /package/dist/{chunk-77CNV7DY.js.map → chunk-BLAKS3CP.js.map} +0 -0
- /package/dist/{chunk-XQ4EH6DI.js.map → chunk-BR4UFMLO.js.map} +0 -0
- /package/dist/{chunk-WEAO45M4.js.map → chunk-DKQM5Q6Q.js.map} +0 -0
- /package/dist/{chunk-CSXHRAJJ.js.map → chunk-EG64LXKL.js.map} +0 -0
- /package/dist/{chunk-PTN6UAUA.js.map → chunk-ES36HTSC.js.map} +0 -0
- /package/dist/{chunk-5BRT4Z2I.js.map → chunk-EZWWCN7I.js.map} +0 -0
- /package/dist/{chunk-XKGPIWGI.js.map → chunk-FBZTWUK5.js.map} +0 -0
- /package/dist/{chunk-FRVTCCGD.js.map → chunk-GJPCP5CA.js.map} +0 -0
- /package/dist/{chunk-VDX2J7OX.js.map → chunk-IPLYGWQF.js.map} +0 -0
- /package/dist/{chunk-YSGBD3QN.js.map → chunk-IYT632D5.js.map} +0 -0
- /package/dist/{chunk-2QSZNTDO.js.map → chunk-JBPKEARU.js.map} +0 -0
- /package/dist/{chunk-RSYJV25N.js.map → chunk-JIF3VRKQ.js.map} +0 -0
- /package/dist/{chunk-EUSLKI4X.js.map → chunk-KKBTBUVT.js.map} +0 -0
- /package/dist/{chunk-RNY3BO35.js.map → chunk-LIHC3T43.js.map} +0 -0
- /package/dist/{chunk-UGH6VPPO.js.map → chunk-MWTMTW2T.js.map} +0 -0
- /package/dist/{chunk-MG6MNSP4.js.map → chunk-MXJ3VP6S.js.map} +0 -0
- /package/dist/{chunk-M5EPMNS4.js.map → chunk-P2CLYN6H.js.map} +0 -0
- /package/dist/{chunk-KGTD2KZJ.js.map → chunk-Q6YJW6KE.js.map} +0 -0
- /package/dist/{chunk-X3SQYAPP.js.map → chunk-R372P2EH.js.map} +0 -0
- /package/dist/{chunk-4MJZTF42.js.map → chunk-RKXZ7P4V.js.map} +0 -0
- /package/dist/{chunk-RYHWZ52A.js.map → chunk-S2BBISHU.js.map} +0 -0
- /package/dist/{chunk-LLISELSY.js.map → chunk-TBMRQZDY.js.map} +0 -0
- /package/dist/{chunk-FMLKOL2Z.js.map → chunk-TC3VNKGX.js.map} +0 -0
- /package/dist/{chunk-7K5Q6COX.js.map → chunk-TVVEYCNW.js.map} +0 -0
- /package/dist/{chunk-JDOJZTFF.js.map → chunk-VFNI3RPT.js.map} +0 -0
- /package/dist/{chunk-EYJSW2RV.js.map → chunk-VYYNBJXH.js.map} +0 -0
- /package/dist/{chunk-BHLN7L7M.js.map → chunk-WVVK2RFM.js.map} +0 -0
- /package/dist/{chunk-DQEMWVMT.js.map → chunk-X7Y7WX73.js.map} +0 -0
- /package/dist/{chunk-HEXSNYJD.js.map → chunk-YOBNCD3Z.js.map} +0 -0
- /package/dist/{chunk-SK3EYPKC.js.map → chunk-ZN4UZWMT.js.map} +0 -0
- /package/dist/{first-start-migration-LEXL3XYL.js.map → first-start-migration-BKNGOFED.js.map} +0 -0
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `replicaPeers` config surface (issue #2149) — types + parser.
|
|
3
|
+
*
|
|
4
|
+
* Deliberately split out of replica-divergence.ts as a LIGHT module: it imports
|
|
5
|
+
* no heavy modules (no corpus-watermark, no access-token-capabilities). PluginConfig
|
|
6
|
+
* carries `replicaPeers: ReplicaPeersConfig`, so types.ts imports ReplicaPeersConfig
|
|
7
|
+
* from HERE. If that type instead lived in replica-divergence.ts (which pulls in the
|
|
8
|
+
* corpus census graph), then types.ts -> replica-divergence.ts -> corpus-watermark.ts
|
|
9
|
+
* -> types.ts would form a heavy import cycle that rollup-plugin-dts re-expands across
|
|
10
|
+
* every PluginConfig-inlining entry, OOMing the default-heap DTS worker (the issue
|
|
11
|
+
* #1562 heap cliff). Keeping the config surface corpus-free severs that cycle.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { coerceBool, coerceNumber } from "./connectors/coerce.js";
|
|
15
|
+
import { isAgentAccessSecretRef } from "./resolve-auth-token.js";
|
|
16
|
+
import type { AgentAccessAuthToken } from "./types.js";
|
|
17
|
+
|
|
18
|
+
const DEFAULT_POLL_INTERVAL_MS = 300_000; // 5 min
|
|
19
|
+
const DEFAULT_REQUEST_TIMEOUT_MS = 10_000;
|
|
20
|
+
const DEFAULT_MAX_FILE_COUNT_DELTA = 100;
|
|
21
|
+
const DEFAULT_MAX_WATERMARK_AGE_DELTA_MS = 900_000; // 15 min
|
|
22
|
+
/** Node clamps a `setTimeout`/`AbortSignal.timeout` delay above 2^31-1 ms to 1ms,
|
|
23
|
+
* which would make `withDeadline` fire immediately and mark every peer unreachable. */
|
|
24
|
+
const MAX_TIMER_DELAY_MS = 2_147_483_647;
|
|
25
|
+
|
|
26
|
+
export interface ReplicaPeerConfig {
|
|
27
|
+
/** Base URL of the peer's agent-access HTTP server (http/https). */
|
|
28
|
+
url: string;
|
|
29
|
+
/**
|
|
30
|
+
* Bearer token for the peer's authenticated `/health`. A literal string
|
|
31
|
+
* (env-expanded) or an unresolved SecretRef preserved verbatim and resolved
|
|
32
|
+
* at poll time — mirrors `agentAccessHttp.authToken` exactly.
|
|
33
|
+
*/
|
|
34
|
+
token?: AgentAccessAuthToken;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface ReplicaPeersConfig {
|
|
38
|
+
/** Master gate. Default false — a daemon with no peers behaves exactly as before. */
|
|
39
|
+
enabled: boolean;
|
|
40
|
+
/** Peers to poll. Default []. */
|
|
41
|
+
peers: ReplicaPeerConfig[];
|
|
42
|
+
/** How often a peer is re-polled (SWR TTL), ms. Default 5 min. */
|
|
43
|
+
pollIntervalMs: number;
|
|
44
|
+
/** Per-peer request timeout, ms. Default 10s. */
|
|
45
|
+
requestTimeoutMs: number;
|
|
46
|
+
/** File-count delta (per namespace) beyond which a peer is flagged diverged. Default 100. */
|
|
47
|
+
maxFileCountDelta: number;
|
|
48
|
+
/** Newest-write timestamp gap (per namespace) beyond which a peer is flagged diverged, ms. Default 15 min. */
|
|
49
|
+
maxWatermarkAgeDeltaMs: number;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Expand `${ENV_VAR}` placeholders in a config string, matching config.ts's
|
|
54
|
+
* `resolveEnvVars` semantics (throw on unset var / malformed placeholder).
|
|
55
|
+
* Inlined rather than imported because config.ts imports THIS module for
|
|
56
|
+
* `parseReplicaPeersConfig`, so importing back would be circular — the same
|
|
57
|
+
* reason extraction-liveness.ts inlines its numeric validation.
|
|
58
|
+
*/
|
|
59
|
+
function expandEnvValue(value: string): string {
|
|
60
|
+
const resolved = value.replace(/\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g, (_match, name: string) => {
|
|
61
|
+
const envValue = process.env[name];
|
|
62
|
+
if (envValue === undefined || envValue === "") {
|
|
63
|
+
throw new Error(`replicaPeers: environment variable ${name} is not set`);
|
|
64
|
+
}
|
|
65
|
+
return envValue;
|
|
66
|
+
});
|
|
67
|
+
// An UNTERMINATED `${NAME` matches neither the replacement regex nor the
|
|
68
|
+
// balanced leftover check below, so the literal typo would be sent as the
|
|
69
|
+
// bearer token and surface as a misleading `http_401` (round 8, codex P2).
|
|
70
|
+
const unterminated = resolved.match(/\$\{[^}]*$/);
|
|
71
|
+
if (unterminated) {
|
|
72
|
+
throw new Error(`replicaPeers: unterminated environment variable placeholder: ${unterminated[0]}`);
|
|
73
|
+
}
|
|
74
|
+
const remaining = resolved.match(/\$\{[^}]*\}/);
|
|
75
|
+
if (remaining) {
|
|
76
|
+
throw new Error(`replicaPeers: malformed environment variable placeholder: ${remaining[0]}`);
|
|
77
|
+
}
|
|
78
|
+
return resolved;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* An absent key falls back to the default; any PRESENT value (including an
|
|
83
|
+
* explicit `null`) must coerce to an integer `>= min`, else THROW. A fractional
|
|
84
|
+
* or out-of-range value is rejected, never floored/reinterpreted (§1/§17/§39).
|
|
85
|
+
*/
|
|
86
|
+
function parseIntegerAtLeast(value: unknown, min: number, dflt: number, label: string, max = Number.MAX_SAFE_INTEGER): number {
|
|
87
|
+
if (value === undefined) return dflt;
|
|
88
|
+
const coerced = coerceNumber(value);
|
|
89
|
+
if (coerced === undefined || !Number.isFinite(coerced) || !Number.isInteger(coerced) || coerced < min) {
|
|
90
|
+
throw new Error(`${label} must be an integer greater than or equal to ${min}; got ${JSON.stringify(value)}`);
|
|
91
|
+
}
|
|
92
|
+
if (coerced > max) {
|
|
93
|
+
throw new Error(`${label} must be an integer no greater than ${max}; got ${JSON.stringify(value)}`);
|
|
94
|
+
}
|
|
95
|
+
return coerced;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function asRecord(value: unknown, label: string): Record<string, unknown> {
|
|
99
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
100
|
+
throw new Error(`${label} must be an object`);
|
|
101
|
+
}
|
|
102
|
+
return value as Record<string, unknown>;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function parseReplicaPeerToken(raw: unknown, index: number): AgentAccessAuthToken | undefined {
|
|
106
|
+
// Only an OMITTED token selects unauthenticated polling. A present `null` (round
|
|
107
|
+
// 4, codex P2) or an explicit empty/whitespace string (round 6, codex P2) is a
|
|
108
|
+
// present-but-invalid credential: silently dropping it polls without the token
|
|
109
|
+
// and surfaces a healthy peer as `http_401`, hiding the real config error.
|
|
110
|
+
if (raw === undefined) return undefined;
|
|
111
|
+
if (typeof raw === "string") {
|
|
112
|
+
const trimmed = raw.trim();
|
|
113
|
+
if (trimmed.length === 0) {
|
|
114
|
+
throw new Error(`replicaPeers.peers[${index}].token must be a non-empty string or a SecretRef object`);
|
|
115
|
+
}
|
|
116
|
+
// Re-validate AFTER expansion: `${PEER_TOKEN}` pointing at a whitespace-only
|
|
117
|
+
// env value clears the check above, then resolveAgentAccessAuthToken
|
|
118
|
+
// normalizes it to undefined — silently downgrading an explicitly
|
|
119
|
+
// authenticated peer to an unauthenticated poll read as http_401 (round 9).
|
|
120
|
+
const expanded = expandEnvValue(trimmed);
|
|
121
|
+
if (expanded.trim().length === 0) {
|
|
122
|
+
throw new Error(`replicaPeers.peers[${index}].token expanded to an empty value`);
|
|
123
|
+
}
|
|
124
|
+
return expanded;
|
|
125
|
+
}
|
|
126
|
+
if (isAgentAccessSecretRef(raw)) return raw;
|
|
127
|
+
throw new Error(`replicaPeers.peers[${index}].token must be a string or a SecretRef object`);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function parseReplicaPeer(value: unknown, index: number): ReplicaPeerConfig {
|
|
131
|
+
const record = asRecord(value, `replicaPeers.peers[${index}]`);
|
|
132
|
+
if (typeof record.url !== "string" || record.url.trim().length === 0) {
|
|
133
|
+
throw new Error(`replicaPeers.peers[${index}].url must be a non-empty string`);
|
|
134
|
+
}
|
|
135
|
+
const url = expandEnvValue(record.url.trim());
|
|
136
|
+
let parsed: URL;
|
|
137
|
+
try {
|
|
138
|
+
parsed = new URL(url);
|
|
139
|
+
} catch {
|
|
140
|
+
throw new Error(`replicaPeers.peers[${index}].url is not a valid URL`);
|
|
141
|
+
}
|
|
142
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
143
|
+
throw new Error(`replicaPeers.peers[${index}].url must be an http(s) URL (got ${parsed.protocol})`);
|
|
144
|
+
}
|
|
145
|
+
// The health path is appended to this base by string concatenation, so a query
|
|
146
|
+
// or fragment would corrupt the request URL (`https://h?x` + `/engram/v1/health`
|
|
147
|
+
// targets `/` with a mangled query; a fragment drops the path). Reject them at
|
|
148
|
+
// parse time — a peer URL must be a clean base (round 6, codex). Embedded
|
|
149
|
+
// credentials (`user:pass@host`) are rejected too: Node's fetch refuses to
|
|
150
|
+
// construct a request from a URL with credentials, so a healthy peer would
|
|
151
|
+
// falsely read as unreachable while redaction hid the config mistake.
|
|
152
|
+
if (parsed.search.length > 0 || parsed.hash.length > 0 || parsed.username.length > 0 || parsed.password.length > 0) {
|
|
153
|
+
throw new Error(`replicaPeers.peers[${index}].url must be a clean base URL (no query, fragment, or embedded credentials)`);
|
|
154
|
+
}
|
|
155
|
+
const token = parseReplicaPeerToken(record.token, index);
|
|
156
|
+
// Store the CANONICAL form: `new URL()` tolerates surrounding whitespace that
|
|
157
|
+
// the raw string keeps, and the health path is appended by concatenation - so
|
|
158
|
+
// " https://peer /engram/v1/health" reads as an unreachable healthy peer.
|
|
159
|
+
const canonical = parsed.toString().replace(/\/+$/, "");
|
|
160
|
+
return token === undefined ? { url: canonical } : { url: canonical, token };
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Parse the `replicaPeers` config block. Mirrors the nested-block validation of
|
|
165
|
+
* the other config-block parsers; coerces string booleans (§24) and rejects
|
|
166
|
+
* invalid numbers/urls/shapes (§1/§39).
|
|
167
|
+
*/
|
|
168
|
+
/**
|
|
169
|
+
* Strict boolean for the parse path: absent -> the default, present -> must be
|
|
170
|
+
* a recognized boolean token. A value like `1` must NOT silently reinterpret as
|
|
171
|
+
* `false`, which would leave monitoring off after an operator tried to enable
|
|
172
|
+
* it (round 3, codex P2). The lenient read-boundary resolver still defaults.
|
|
173
|
+
*/
|
|
174
|
+
function parseStrictBool(value: unknown, fallback: boolean, label: string): boolean {
|
|
175
|
+
if (value === undefined) return fallback;
|
|
176
|
+
const coerced = coerceBool(value);
|
|
177
|
+
if (coerced === undefined) {
|
|
178
|
+
throw new Error(`${label} must be a boolean (got ${JSON.stringify(value)})`);
|
|
179
|
+
}
|
|
180
|
+
return coerced;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export function parseReplicaPeersConfig(cfg: Record<string, unknown>): ReplicaPeersConfig {
|
|
184
|
+
const raw = cfg.replicaPeers;
|
|
185
|
+
if (raw !== undefined && (raw === null || typeof raw !== "object" || Array.isArray(raw))) {
|
|
186
|
+
throw new Error(`replicaPeers must be a plain object (got ${JSON.stringify(raw)})`);
|
|
187
|
+
}
|
|
188
|
+
const block = (raw ?? {}) as Record<string, unknown>;
|
|
189
|
+
if (block.peers !== undefined && !Array.isArray(block.peers)) {
|
|
190
|
+
throw new Error(`replicaPeers.peers must be an array (got ${JSON.stringify(block.peers)})`);
|
|
191
|
+
}
|
|
192
|
+
const peers = (block.peers ?? []).map(parseReplicaPeer);
|
|
193
|
+
return {
|
|
194
|
+
enabled: parseStrictBool(block.enabled, false, "replicaPeers.enabled"),
|
|
195
|
+
peers,
|
|
196
|
+
pollIntervalMs: parseIntegerAtLeast(block.pollIntervalMs, 1, DEFAULT_POLL_INTERVAL_MS, "replicaPeers.pollIntervalMs"),
|
|
197
|
+
requestTimeoutMs: parseIntegerAtLeast(block.requestTimeoutMs, 1, DEFAULT_REQUEST_TIMEOUT_MS, "replicaPeers.requestTimeoutMs", MAX_TIMER_DELAY_MS),
|
|
198
|
+
maxFileCountDelta: parseIntegerAtLeast(block.maxFileCountDelta, 0, DEFAULT_MAX_FILE_COUNT_DELTA, "replicaPeers.maxFileCountDelta"),
|
|
199
|
+
maxWatermarkAgeDeltaMs: parseIntegerAtLeast(
|
|
200
|
+
block.maxWatermarkAgeDeltaMs,
|
|
201
|
+
0,
|
|
202
|
+
DEFAULT_MAX_WATERMARK_AGE_DELTA_MS,
|
|
203
|
+
"replicaPeers.maxWatermarkAgeDeltaMs",
|
|
204
|
+
),
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* READ-boundary resolver (mirrors `resolveExtractionLivenessConfig`, issue #2155).
|
|
210
|
+
* `parseConfig` always populates `replicaPeers`, but a host adapter, an older
|
|
211
|
+
* persisted config, or a hand-built `PluginConfig` can hand a READ surface
|
|
212
|
+
* (/health, doctor) an absent, partial, or loosely-typed block. Every replica
|
|
213
|
+
* surface must degrade to the documented default (disabled, no peers, no polling)
|
|
214
|
+
* rather than throw — `/health` must stay answerable. Unlike the strict parser, a
|
|
215
|
+
* present-but-invalid field falls back to its default here instead of throwing, and
|
|
216
|
+
* a malformed peer entry is dropped rather than aborting the whole block.
|
|
217
|
+
*/
|
|
218
|
+
export function resolveReplicaPeersConfig(block: unknown): ReplicaPeersConfig {
|
|
219
|
+
const record =
|
|
220
|
+
block !== null && typeof block === "object" && !Array.isArray(block) ? (block as Record<string, unknown>) : {};
|
|
221
|
+
return {
|
|
222
|
+
enabled: coerceBool(record.enabled) ?? false,
|
|
223
|
+
peers: resolveReplicaPeers(record.peers),
|
|
224
|
+
pollIntervalMs: resolveIntegerAtLeast(record.pollIntervalMs, 1, DEFAULT_POLL_INTERVAL_MS),
|
|
225
|
+
requestTimeoutMs: resolveIntegerAtLeast(record.requestTimeoutMs, 1, DEFAULT_REQUEST_TIMEOUT_MS, MAX_TIMER_DELAY_MS),
|
|
226
|
+
maxFileCountDelta: resolveIntegerAtLeast(record.maxFileCountDelta, 0, DEFAULT_MAX_FILE_COUNT_DELTA),
|
|
227
|
+
maxWatermarkAgeDeltaMs: resolveIntegerAtLeast(record.maxWatermarkAgeDeltaMs, 0, DEFAULT_MAX_WATERMARK_AGE_DELTA_MS),
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/** Lenient sibling of {@link parseIntegerAtLeast}: a present-but-invalid value falls back, never throws. */
|
|
232
|
+
function resolveIntegerAtLeast(value: unknown, min: number, dflt: number, max = Number.MAX_SAFE_INTEGER): number {
|
|
233
|
+
if (value === undefined) return dflt;
|
|
234
|
+
const coerced = coerceNumber(value);
|
|
235
|
+
return coerced !== undefined && Number.isFinite(coerced) && Number.isInteger(coerced) && coerced >= min && coerced <= max ? coerced : dflt;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/** Lenient peer list: a non-array is empty; a malformed entry is dropped, never thrown (read boundary). */
|
|
239
|
+
function resolveReplicaPeers(value: unknown): ReplicaPeerConfig[] {
|
|
240
|
+
if (!Array.isArray(value)) return [];
|
|
241
|
+
const peers: ReplicaPeerConfig[] = [];
|
|
242
|
+
value.forEach((entry, index) => {
|
|
243
|
+
try {
|
|
244
|
+
peers.push(parseReplicaPeer(entry, index));
|
|
245
|
+
} catch {
|
|
246
|
+
// Read surfaces never throw on a bad peer — parseConfig is the enforcing boundary.
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
return peers;
|
|
250
|
+
}
|
|
@@ -154,3 +154,41 @@ export function isAgentAccessSecretRef(value: unknown): value is SecretRef {
|
|
|
154
154
|
export function clearAuthTokenSecretCache(): void {
|
|
155
155
|
resolvedCache = new WeakMap<ResolveSecretRefFn, Map<string, string>>();
|
|
156
156
|
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Resolve a host SecretRef resolver, preferring an explicitly supplied one over
|
|
160
|
+
* the lazy loader.
|
|
161
|
+
*
|
|
162
|
+
* Shared by every CLI command that authenticates with a SecretRef — `access
|
|
163
|
+
* serve` and `doctor` (replica peer tokens, issue #2149) — so the two cannot
|
|
164
|
+
* drift. Structurally typed rather than importing the CLI's options type, which
|
|
165
|
+
* would invert the dependency.
|
|
166
|
+
*/
|
|
167
|
+
export async function loadHostSecretRefResolver(options: {
|
|
168
|
+
resolveSecretRef?: ResolveSecretRefFn | null;
|
|
169
|
+
loadResolveSecretRef?: () =>
|
|
170
|
+
| Promise<ResolveSecretRefFn | null | undefined>
|
|
171
|
+
| ResolveSecretRefFn
|
|
172
|
+
| null
|
|
173
|
+
| undefined;
|
|
174
|
+
}): Promise<ResolveSecretRefFn | null | undefined> {
|
|
175
|
+
if (options.resolveSecretRef) return options.resolveSecretRef;
|
|
176
|
+
if (!options.loadResolveSecretRef) return null;
|
|
177
|
+
try {
|
|
178
|
+
return await options.loadResolveSecretRef();
|
|
179
|
+
} catch {
|
|
180
|
+
// A host whose lazy loader throws means "no resolver available" — not
|
|
181
|
+
// "abort the caller". `doctor` must still run its unrelated checks and let
|
|
182
|
+
// the replica check report token_error per affected peer (round 9, codex).
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Adapt a host's lazily-loaded SecretRef resolver into the eager
|
|
189
|
+
* `ResolveSecretRefFn` that `EngramAccessService` expects, so replica peer
|
|
190
|
+
* tokens resolve at poll time (issue #2149).
|
|
191
|
+
*/
|
|
192
|
+
export function hostSecretRefResolver(options: Parameters<typeof loadHostSecretRefResolver>[0]): ResolveSecretRefFn {
|
|
193
|
+
return (ref, context) => loadHostSecretRefResolver(options).then((resolve) => resolve?.(ref, context));
|
|
194
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { EntityFile, MemoryFile } from "../types.js";
|
|
2
|
+
import {
|
|
3
|
+
getFingerprint,
|
|
4
|
+
migrateLegacyEntityCanonicalIds,
|
|
5
|
+
validateRoots,
|
|
6
|
+
type EntityCanonicalIdMigrationDependencies,
|
|
7
|
+
} from "./entity-canonical-id-migration.js";
|
|
8
|
+
import {
|
|
9
|
+
rewriteProjectedEntityReferences,
|
|
10
|
+
rewriteProjectedMemoryEntityReference,
|
|
11
|
+
} from "../memory-projection-mutations.js";
|
|
12
|
+
|
|
13
|
+
export interface EntityCanonicalIdMigrationHost {
|
|
14
|
+
readonly baseDir: string;
|
|
15
|
+
readonly entitiesDir: string;
|
|
16
|
+
readonly stateDir: string;
|
|
17
|
+
readonly archiveDir: string;
|
|
18
|
+
normalizeEntityName(raw: string, type: string): string;
|
|
19
|
+
resolveEntityFilePath(id: string): string | null;
|
|
20
|
+
readStorageSecureFile(filePath: string): Promise<string>;
|
|
21
|
+
writeStorageSecureFile(filePath: string, content: string, forceEncrypt?: boolean): Promise<void>;
|
|
22
|
+
isEncryptedFileHeader(filePath: string): Promise<boolean>;
|
|
23
|
+
snapshotBeforeWrite(filePath: string, trigger: "write"): Promise<void>;
|
|
24
|
+
readMemoryByPath(filePath: string): Promise<MemoryFile | null>;
|
|
25
|
+
readAllMemories(): Promise<MemoryFile[]>;
|
|
26
|
+
readAllColdMemories(): Promise<MemoryFile[]>;
|
|
27
|
+
readArchivedMemories(): Promise<MemoryFile[]>;
|
|
28
|
+
invalidateKnowledgeIndexCache(): void;
|
|
29
|
+
invalidateAllMemoriesCache(): void;
|
|
30
|
+
invalidateColdMemoriesCache(): void;
|
|
31
|
+
bumpMemoryStatusVersion(): void;
|
|
32
|
+
getCorpusScanVersion(): string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export async function runLegacyEntityCanonicalIdMigration(
|
|
36
|
+
host: EntityCanonicalIdMigrationHost,
|
|
37
|
+
parseEntityFile: (content: string) => EntityFile,
|
|
38
|
+
serializeEntityFile: (entity: EntityFile) => string,
|
|
39
|
+
serializeMemoryWithEntityRef: (memory: MemoryFile, entityRef: string) => string,
|
|
40
|
+
): Promise<string> {
|
|
41
|
+
const readMigrationFingerprint = () =>
|
|
42
|
+
getFingerprint(host.baseDir, host.entitiesDir, () => host.getCorpusScanVersion());
|
|
43
|
+
const completionFingerprint = await migrateLegacyEntityCanonicalIds({
|
|
44
|
+
stateDir: host.stateDir,
|
|
45
|
+
entitiesDir: host.entitiesDir,
|
|
46
|
+
normalizeEntityName: host.normalizeEntityName.bind(host),
|
|
47
|
+
resolveEntityFilePath: host.resolveEntityFilePath.bind(host),
|
|
48
|
+
readStorageSecureFile: host.readStorageSecureFile.bind(host),
|
|
49
|
+
writeStorageSecureFile: host.writeStorageSecureFile.bind(host),
|
|
50
|
+
isEncryptedStorageFile: host.isEncryptedFileHeader.bind(host),
|
|
51
|
+
snapshotBeforeWrite: host.snapshotBeforeWrite.bind(host),
|
|
52
|
+
parseEntityFile,
|
|
53
|
+
serializeEntityFile,
|
|
54
|
+
serializeMemoryWithEntityRef,
|
|
55
|
+
readMemoryByPath: host.readMemoryByPath.bind(host),
|
|
56
|
+
readAllMemories: host.readAllMemories.bind(host),
|
|
57
|
+
readAllColdMemories: host.readAllColdMemories.bind(host),
|
|
58
|
+
readArchivedMemories: host.readArchivedMemories.bind(host),
|
|
59
|
+
validateMemoryScanRoots: () =>
|
|
60
|
+
validateRoots(host.baseDir, host.entitiesDir, host.stateDir, host.archiveDir),
|
|
61
|
+
rewriteProjectedEntityReferences: (mappings) =>
|
|
62
|
+
rewriteProjectedEntityReferences(host.baseDir, mappings),
|
|
63
|
+
rewriteProjectedMemoryEntityReference: (memoryId, previousEntityRef, nextEntityRef) =>
|
|
64
|
+
rewriteProjectedMemoryEntityReference(host.baseDir, memoryId, previousEntityRef, nextEntityRef),
|
|
65
|
+
invalidateKnowledgeIndexCache: host.invalidateKnowledgeIndexCache.bind(host),
|
|
66
|
+
invalidateAllMemoriesCache: host.invalidateAllMemoriesCache.bind(host),
|
|
67
|
+
invalidateColdMemoriesCache: host.invalidateColdMemoriesCache.bind(host),
|
|
68
|
+
bumpMemoryStatusVersion: host.bumpMemoryStatusVersion.bind(host),
|
|
69
|
+
readMigrationFingerprint,
|
|
70
|
+
} satisfies EntityCanonicalIdMigrationDependencies);
|
|
71
|
+
return completionFingerprint ?? readMigrationFingerprint();
|
|
72
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
export class EntityCanonicalIdMigrationRunner {
|
|
2
|
+
private inFlight: Promise<void> | null = null;
|
|
3
|
+
private directoriesInitialized = false;
|
|
4
|
+
private migrationComplete = false;
|
|
5
|
+
private completedFingerprint: string | null = null;
|
|
6
|
+
|
|
7
|
+
public constructor(
|
|
8
|
+
private readonly canRun: () => boolean,
|
|
9
|
+
private readonly runMigration: () => Promise<string | void>,
|
|
10
|
+
private readonly readFingerprint: () => Promise<string> = async () => "static",
|
|
11
|
+
) {}
|
|
12
|
+
|
|
13
|
+
public async markDirectoriesInitialized(): Promise<void> {
|
|
14
|
+
this.directoriesInitialized = true;
|
|
15
|
+
await this.ensure();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
public ensure(): Promise<void> {
|
|
19
|
+
if (!this.canRun()) return Promise.resolve();
|
|
20
|
+
if (this.inFlight) return this.inFlight;
|
|
21
|
+
const migration = this.runOrReuse();
|
|
22
|
+
this.inFlight = migration;
|
|
23
|
+
void migration.then(
|
|
24
|
+
() => this.clearInFlight(migration),
|
|
25
|
+
() => this.clearInFlight(migration),
|
|
26
|
+
);
|
|
27
|
+
return migration;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
public triggerAfterUnlock(): Promise<void> {
|
|
31
|
+
if (!this.directoriesInitialized) return Promise.resolve();
|
|
32
|
+
return this.ensure();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
private async runOrReuse(): Promise<void> {
|
|
36
|
+
let fingerprintAtStart = await this.readFingerprint();
|
|
37
|
+
if (this.migrationComplete && fingerprintAtStart === this.completedFingerprint) return;
|
|
38
|
+
|
|
39
|
+
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
40
|
+
const completionFingerprint = await this.runMigration();
|
|
41
|
+
const fingerprintAfterMigration = await this.readFingerprint();
|
|
42
|
+
const fingerprintAfterStabilityRead = await this.readFingerprint();
|
|
43
|
+
const returnedFingerprint =
|
|
44
|
+
typeof completionFingerprint === "string" ? completionFingerprint : fingerprintAfterMigration;
|
|
45
|
+
if (
|
|
46
|
+
returnedFingerprint === fingerprintAfterMigration
|
|
47
|
+
&& fingerprintAfterMigration === fingerprintAfterStabilityRead
|
|
48
|
+
&& (
|
|
49
|
+
typeof completionFingerprint === "string"
|
|
50
|
+
|| fingerprintAfterMigration === fingerprintAtStart
|
|
51
|
+
)
|
|
52
|
+
) {
|
|
53
|
+
this.migrationComplete = true;
|
|
54
|
+
this.completedFingerprint = fingerprintAfterStabilityRead;
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
fingerprintAtStart = fingerprintAfterStabilityRead;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
this.migrationComplete = false;
|
|
61
|
+
this.completedFingerprint = null;
|
|
62
|
+
throw new Error("Entity canonical-id migration fingerprint changed while migration was completing; retry migration.");
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
private clearInFlight(migration: Promise<void>): void {
|
|
66
|
+
if (this.inFlight === migration) this.inFlight = null;
|
|
67
|
+
}
|
|
68
|
+
}
|