@remnic/core 9.62.1 → 9.62.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/access-admin-ops-surface.d.ts +11 -11
- package/dist/access-admin-ops-surface.js +26 -26
- package/dist/access-audit.js +2 -2
- package/dist/access-authorization-probe.d.ts +11 -11
- package/dist/access-authorization-probe.js +27 -27
- package/dist/access-boundary.d.ts +11 -11
- package/dist/access-boundary.js +26 -26
- package/dist/access-cli.js +88 -86
- package/dist/access-cli.js.map +1 -1
- package/dist/access-coding-context-resolution.d.ts +1 -1
- package/dist/access-extraction-force-flush.d.ts +11 -11
- package/dist/access-extraction-force-flush.js +26 -26
- package/dist/access-health-types.d.ts +1 -1
- package/dist/access-http-lcm-compaction.d.ts +11 -11
- package/dist/access-http-lifecycle-flush.d.ts +11 -11
- package/dist/access-http-offline-stream.d.ts +11 -11
- package/dist/access-http-query.js +26 -26
- package/dist/access-http.d.ts +12 -12
- package/dist/access-http.js +36 -36
- package/dist/access-identity-continuity-surface.d.ts +9 -9
- package/dist/access-identity-continuity-surface.js +26 -26
- package/dist/access-lcm-surface.d.ts +11 -11
- package/dist/access-lcm-surface.js +26 -26
- package/dist/access-mcp.d.ts +11 -11
- package/dist/access-mcp.js +31 -31
- package/dist/access-memory-search-fanout.d.ts +2 -2
- package/dist/{access-namespace-preflight-EMvicnrb.d.ts → access-namespace-preflight-yO4LMGFH.d.ts} +1 -1
- package/dist/access-namespace-preflight.d.ts +3 -3
- package/dist/access-namespace-preflight.js +26 -26
- package/dist/access-observe-write-surface.d.ts +11 -11
- package/dist/access-observe-write-surface.js +26 -26
- package/dist/access-offline-manifest.d.ts +9 -9
- package/dist/access-offline-manifest.js +2 -2
- package/dist/access-operations-batch.js +27 -27
- package/dist/access-operations.d.ts +11 -11
- package/dist/access-operations.js +30 -30
- package/dist/access-recall-concurrency.d.ts +11 -11
- package/dist/access-recall-concurrency.js +26 -26
- package/dist/access-recall-response.d.ts +11 -11
- package/dist/access-recall-response.js +26 -26
- package/dist/access-recall-surface.d.ts +11 -11
- package/dist/access-recall-surface.js +26 -26
- package/dist/{access-service-BeDYQQ62.d.ts → access-service-M4LgO141.d.ts} +7 -7
- package/dist/access-service-helpers.d.ts +11 -11
- package/dist/access-service.d.ts +11 -11
- package/dist/access-service.js +26 -26
- package/dist/access-surface-catalog.d.ts +11 -11
- package/dist/access-surface-catalog.js +28 -28
- package/dist/access-wearables-meetings-surface.d.ts +3 -3
- package/dist/action-confidence.d.ts +1 -1
- package/dist/active-memory-bridge.d.ts +1 -1
- package/dist/active-recall.d.ts +1 -1
- package/dist/active-recall.js +1 -1
- package/dist/ambient-provenance.d.ts +1 -1
- package/dist/artifact-search.d.ts +1 -1
- package/dist/{auto-sync-FMWQQB3J.js → auto-sync-Z52VWMNC.js} +3 -3
- package/dist/behavior-learner.d.ts +1 -1
- package/dist/behavior-learner.js +2 -2
- package/dist/behavior-signals.d.ts +1 -1
- package/dist/bootstrap.d.ts +9 -9
- package/dist/briefing-window.d.ts +2 -2
- package/dist/briefing.d.ts +2 -2
- package/dist/briefing.js +11 -10
- package/dist/buffer-surprise-report.d.ts +1 -1
- package/dist/buffer-turn-helpers.d.ts +1 -1
- package/dist/buffer.d.ts +2 -2
- package/dist/bulk-import/index.d.ts +3 -3
- package/dist/calibration.d.ts +1 -1
- package/dist/capabilities.d.ts +1 -1
- package/dist/{catalog-B2SxZ6LU.d.ts → catalog-CyYTf_ok.d.ts} +1 -1
- package/dist/causal-behavior.d.ts +1 -1
- package/dist/causal-behavior.js +3 -3
- package/dist/causal-chain.js +3 -3
- package/dist/causal-consolidation.d.ts +1 -1
- package/dist/causal-consolidation.js +14 -14
- package/dist/causal-retrieval.js +3 -3
- package/dist/causal-trajectory-graph.d.ts +1 -1
- package/dist/causal-trajectory.js +2 -2
- package/dist/{chunk-B2QHVLJO.js → chunk-256FBCVX.js} +22 -10
- package/dist/chunk-256FBCVX.js.map +1 -0
- package/dist/{chunk-QP2IZTUR.js → chunk-2TYIAKU5.js} +2 -2
- package/dist/{chunk-QNMNNFKD.js → chunk-347ZR6D4.js} +2 -2
- package/dist/{chunk-76YNCJ5H.js → chunk-3F3CVVDS.js} +2 -2
- package/dist/{chunk-KCFZOSR6.js → chunk-3MA7JVXN.js} +4 -4
- package/dist/{chunk-KHQ57QLJ.js → chunk-46WM6JX2.js} +7 -3
- package/dist/chunk-46WM6JX2.js.map +1 -0
- package/dist/chunk-47DU4SGJ.js +92 -0
- package/dist/chunk-47DU4SGJ.js.map +1 -0
- package/dist/{chunk-HKYNBEL4.js → chunk-4FFROSFG.js} +37 -14
- package/dist/{chunk-HKYNBEL4.js.map → chunk-4FFROSFG.js.map} +1 -1
- package/dist/{chunk-TLUSSS2F.js → chunk-4HO3542O.js} +2 -2
- package/dist/{chunk-4E67XCT2.js → chunk-4IZ2IT7D.js} +3 -3
- package/dist/{chunk-33JBK2XP.js → chunk-5F6HMNMZ.js} +2 -2
- package/dist/{chunk-5WQMORU5.js → chunk-5PFGMO33.js} +2 -2
- package/dist/{chunk-6HEM6HTQ.js → chunk-6GGTATZF.js} +2 -2
- package/dist/{chunk-UIRU3MIJ.js → chunk-7BJYTX6Q.js} +2 -2
- package/dist/{chunk-2GRLJMKJ.js → chunk-7DRSWPUV.js} +3 -3
- package/dist/{chunk-TVVEYCNW.js → chunk-7K5Q6COX.js} +4 -4
- package/dist/{chunk-HXXGZ4KV.js → chunk-ALNZEWGN.js} +2 -2
- package/dist/{chunk-5WVUO6QT.js → chunk-AR253JBM.js} +1 -1
- package/dist/chunk-AR253JBM.js.map +1 -0
- package/dist/{chunk-67MKNKJQ.js → chunk-AYIHDC5S.js} +2 -2
- package/dist/{chunk-IXJ5U6ZL.js → chunk-BBHFAVMP.js} +8 -8
- package/dist/{chunk-IIRK5V5Z.js → chunk-BHO6RHE5.js} +3 -3
- package/dist/{chunk-WANOCU3K.js → chunk-BP6WMYLP.js} +3 -3
- package/dist/{chunk-ZJKLGS76.js → chunk-CALURYWR.js} +283 -248
- package/dist/chunk-CALURYWR.js.map +1 -0
- package/dist/{chunk-JLMH5YMU.js → chunk-CH627RRV.js} +2 -2
- package/dist/{chunk-XIABRKF6.js → chunk-CLJ5QS6J.js} +239 -47
- package/dist/chunk-CLJ5QS6J.js.map +1 -0
- package/dist/{chunk-YLHM5BQQ.js → chunk-DCUMFQYM.js} +171 -8
- package/dist/chunk-DCUMFQYM.js.map +1 -0
- package/dist/{chunk-ZBJMUXZH.js → chunk-DJ7HMNSL.js} +15 -8
- package/dist/chunk-DJ7HMNSL.js.map +1 -0
- package/dist/{chunk-WTMG5GAK.js → chunk-DROAPP6N.js} +3 -3
- package/dist/{chunk-PBH2Z2NY.js → chunk-DTLF2SLZ.js} +2 -2
- package/dist/{chunk-7JAXC526.js → chunk-EFWHCTYJ.js} +2 -2
- package/dist/{chunk-3XG4QEA2.js → chunk-FF6E6HOT.js} +39 -39
- package/dist/{chunk-NP7FLVZW.js → chunk-G2FCSILA.js} +2 -2
- package/dist/{chunk-7PVTOZR4.js → chunk-H23GIXJC.js} +2 -2
- package/dist/{chunk-WT6UL3UA.js → chunk-H7PRHUBD.js} +2 -2
- package/dist/{chunk-ETCVSZLG.js → chunk-HSBQJRV2.js} +2 -2
- package/dist/{chunk-GZRDOVQN.js → chunk-HVKMJAEF.js} +2 -2
- package/dist/{chunk-CSOYVTRX.js → chunk-IIPJKCZB.js} +3 -3
- package/dist/{chunk-E57E5SCS.js → chunk-IRLH4NM2.js} +3 -3
- package/dist/{chunk-4G45LJJP.js → chunk-IZRNVNPV.js} +2 -2
- package/dist/{chunk-YSUSUZR4.js → chunk-JCJGYNIS.js} +3 -3
- package/dist/{chunk-EU74U4GT.js → chunk-JDI7G6EA.js} +2 -2
- package/dist/{chunk-QWPAQLYV.js → chunk-JFLKSD5V.js} +2 -2
- package/dist/{chunk-KQS72GCR.js → chunk-LJOIZPJH.js} +2 -2
- package/dist/{chunk-U27G4E5G.js → chunk-LLGDVG52.js} +3 -3
- package/dist/{chunk-7N7DB4L5.js → chunk-LTJTP5YQ.js} +10 -9
- package/dist/chunk-LTJTP5YQ.js.map +1 -0
- package/dist/{chunk-XL3TATMJ.js → chunk-OQIRBGUU.js} +21 -12
- package/dist/chunk-OQIRBGUU.js.map +1 -0
- package/dist/{chunk-JGW2EUCQ.js → chunk-PMRUMGK6.js} +2 -2
- package/dist/{chunk-3G6YU5YD.js → chunk-PSNN25DW.js} +2 -2
- package/dist/{chunk-LRGSLV7M.js → chunk-PYHOAXTI.js} +4 -4
- package/dist/{chunk-75U2FR7U.js → chunk-QCI73Z7C.js} +2 -2
- package/dist/{chunk-EHMMRZOZ.js → chunk-QICOU4RY.js} +2 -2
- package/dist/{chunk-K4WIUTNH.js → chunk-RFH7U5LT.js} +2 -2
- package/dist/{chunk-4SGDYYWV.js → chunk-RPKJHYVE.js} +2 -2
- package/dist/{chunk-BHO3IVAN.js → chunk-RYST72N5.js} +12 -1
- package/dist/chunk-RYST72N5.js.map +1 -0
- package/dist/chunk-T3GIETKV.js +152 -0
- package/dist/chunk-T3GIETKV.js.map +1 -0
- package/dist/{chunk-DYDACAK2.js → chunk-TGSMU3F2.js} +3 -3
- package/dist/{chunk-MJFDJTDC.js → chunk-TIRJV2LW.js} +2 -2
- package/dist/{chunk-QRA3XVJX.js → chunk-UDTME32W.js} +2 -2
- package/dist/{chunk-LAM6UDFV.js → chunk-WMOJIOIG.js} +2 -2
- package/dist/{chunk-LLLY5IBK.js → chunk-WR2YKUIW.js} +2 -2
- package/dist/{chunk-KYI3CM42.js → chunk-X6E7TT3A.js} +2 -2
- package/dist/{chunk-QN7XQEIT.js → chunk-YIIC5IDU.js} +2 -2
- package/dist/{chunk-JDJOAL4B.js → chunk-YSR3DXGC.js} +2 -2
- package/dist/{chunk-LBJBNWS2.js → chunk-YTHQHH5U.js} +2 -2
- package/dist/{chunk-JF6YSHJM.js → chunk-ZPGHA7EO.js} +12 -12
- package/dist/{chunk-VUDALPI5.js → chunk-ZQKXRKMR.js} +19 -5
- package/dist/chunk-ZQKXRKMR.js.map +1 -0
- package/dist/{chunk-YG56A6XY.js → chunk-ZS4JADYY.js} +3 -3
- package/dist/{cli-DB_uakTc.d.ts → cli-Bw9z-KqX.d.ts} +5 -5
- package/dist/cli.d.ts +13 -13
- package/dist/cli.js +60 -60
- package/dist/coding/pre-action-gate.d.ts +1 -1
- package/dist/coding/pre-action-gate.js +3 -3
- package/dist/compounding/engine.d.ts +2 -2
- package/dist/compounding/engine.js +11 -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 +1 -1
- package/dist/connectors/codex-materialize-runner.d.ts +1 -1
- package/dist/connectors/codex-materialize-runner.js +11 -10
- package/dist/connectors/codex-materialize.d.ts +1 -1
- package/dist/connectors/index.d.ts +1 -1
- package/dist/connectors/index.js +11 -10
- package/dist/consolidation-provenance-check.d.ts +2 -2
- package/dist/consolidation-undo.d.ts +2 -2
- package/dist/contradiction/index.d.ts +2 -2
- package/dist/converge-config.d.ts +1 -1
- package/dist/convergence-refresh.d.ts +2 -2
- package/dist/convergence-refresh.js +13 -12
- package/dist/conversation-index/backend.d.ts +1 -1
- package/dist/conversation-index/chunker.d.ts +1 -1
- package/dist/conversation-index/faiss-adapter.d.ts +1 -1
- package/dist/conversation-index/indexer.d.ts +1 -1
- package/dist/conversation-index/search.d.ts +1 -1
- package/dist/corpus-watermark.d.ts +1 -1
- package/dist/corpus-watermark.js +13 -12
- package/dist/day-summary.d.ts +1 -1
- package/dist/delinearize.d.ts +1 -1
- package/dist/dependency-propagation-config.d.ts +1 -1
- package/dist/{dependency-propagation-delivery-DcOVpK3y.d.ts → dependency-propagation-delivery-BxhvAppj.d.ts} +1 -1
- package/dist/direct-answer-wiring.d.ts +1 -1
- package/dist/direct-answer-wiring.js +3 -3
- package/dist/direct-answer.d.ts +1 -1
- package/dist/direct-answer.js +2 -2
- package/dist/embedding-fallback.d.ts +1 -1
- package/dist/enrichment/index.d.ts +1 -1
- package/dist/entity-origin-fields.d.ts +1 -1
- package/dist/entity-retrieval.d.ts +2 -2
- package/dist/entity-retrieval.js +11 -10
- package/dist/entity-schema.d.ts +1 -1
- package/dist/episodic-context.d.ts +92 -0
- package/dist/episodic-context.js +16 -0
- package/dist/episodic-context.js.map +1 -0
- package/dist/explicit-capture.d.ts +9 -9
- package/dist/external-wiki-access.d.ts +11 -11
- package/dist/external-wiki-access.js +27 -27
- package/dist/external-wiki-collection-registration.d.ts +1 -1
- package/dist/external-wiki-collection.d.ts +1 -1
- package/dist/external-wiki-mcp-tools.d.ts +11 -11
- 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-normalization.d.ts +1 -1
- package/dist/extraction-normalization.js +2 -2
- package/dist/extraction-prompt.d.ts +1 -1
- package/dist/extraction-source-grounding-rules.d.ts +1 -1
- package/dist/extraction-source-grounding.d.ts +1 -1
- package/dist/extraction.d.ts +1 -1
- package/dist/extraction.js +14 -14
- package/dist/fallback-llm.d.ts +1 -1
- package/dist/{forget-5TWK7CZT.js → forget-SZ2BXDGF.js} +2 -2
- package/dist/graph-dashboard-diff.d.ts +1 -1
- package/dist/graph-dashboard-key.d.ts +1 -1
- package/dist/graph-dashboard-parser.d.ts +1 -1
- package/dist/graph-edge-reinforcement.d.ts +1 -1
- package/dist/graph-path-reconstruction.d.ts +1 -1
- package/dist/graph-path-scoring.d.ts +1 -1
- package/dist/graph-snapshot.d.ts +1 -1
- package/dist/graph.d.ts +1 -1
- package/dist/harmonic-construction.js +2 -2
- package/dist/harmonic-retrieval.js +2 -2
- 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 +21 -21
- package/dist/index.js +100 -98
- package/dist/intent.d.ts +1 -1
- package/dist/lcm/engine.d.ts +8 -2
- package/dist/lcm/engine.js +4 -4
- 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 +11 -10
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.d.ts +2 -2
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +11 -10
- package/dist/maintenance/rebuild-memory-projection.d.ts +2 -2
- package/dist/maintenance/rebuild-memory-projection.js +12 -11
- package/dist/{maintenance-5c1olVtB.d.ts → maintenance-uJb74j64.d.ts} +3 -3
- package/dist/mcp-memory-inspector-app.d.ts +11 -11
- package/dist/memory-action-policy.d.ts +1 -1
- package/dist/memory-cache.d.ts +1 -1
- package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
- package/dist/memory-projection-store.d.ts +1 -1
- package/dist/memory-provenance.d.ts +1 -1
- package/dist/memory-snapshot.d.ts +1 -1
- package/dist/memory-worth-outcomes.d.ts +2 -2
- package/dist/models-json.d.ts +1 -1
- package/dist/namespaces/migrate.d.ts +3 -3
- package/dist/namespaces/migrate.js +12 -11
- package/dist/namespaces/principal.d.ts +1 -1
- package/dist/namespaces/search.d.ts +1 -1
- package/dist/namespaces/search.js +6 -5
- package/dist/namespaces/storage.d.ts +3 -3
- package/dist/namespaces/storage.js +11 -10
- package/dist/native-knowledge.d.ts +1 -1
- package/dist/offline-sync-impression-drain.d.ts +1 -1
- package/dist/operator-doctor-corpus.d.ts +1 -1
- package/dist/operator-doctor-corpus.js +14 -13
- package/dist/operator-doctor-replica.d.ts +1 -1
- package/dist/operator-toolkit.d.ts +3 -3
- package/dist/operator-toolkit.js +19 -18
- package/dist/orchestration/compression-guideline-coordinator.d.ts +2 -2
- package/dist/orchestration/maintenance.d.ts +4 -4
- package/dist/orchestration/maintenance.js +16 -15
- package/dist/{orchestrator-HPuY6I4i.d.ts → orchestrator-CFi-5JSR.d.ts} +23 -8
- package/dist/orchestrator.d.ts +9 -9
- package/dist/orchestrator.js +88 -86
- package/dist/patterns-cli.d.ts +1 -1
- package/dist/{pipeline-CvVe1Q2_.d.ts → pipeline-BdF3ferY.d.ts} +1 -1
- package/dist/policy-runtime.d.ts +1 -1
- package/dist/policy-runtime.js +3 -3
- package/dist/proactive-contention.d.ts +1 -1
- package/dist/provenance.d.ts +1 -1
- package/dist/{public-http-D0JKFkyf.d.ts → public-http-DK-QwaBu.d.ts} +1 -1
- package/dist/{qmd-Bd0Tp76_.d.ts → qmd-D_62bYNl.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-query-policy.js +2 -2
- package/dist/recall-state.d.ts +1 -1
- package/dist/recall-tag-filter.d.ts +1 -1
- package/dist/recall-timings.d.ts +1 -1
- package/dist/recall-tokenization.d.ts +3 -1
- package/dist/recall-tokenization.js +5 -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/reconcile/cursor.d.ts +1 -1
- package/dist/reconcile/manifest.d.ts +10 -1
- package/dist/reconcile/manifest.js +1 -1
- package/dist/reconcile/plan.d.ts +1 -1
- package/dist/replay/normalizers/chatgpt.d.ts +1 -1
- package/dist/replay/normalizers/claude.d.ts +1 -1
- package/dist/replay/normalizers/openclaw.d.ts +1 -1
- package/dist/replay/normalizers/shared.d.ts +1 -1
- package/dist/replay/runner.d.ts +1 -1
- package/dist/replay/types.d.ts +1 -1
- package/dist/replica-divergence.d.ts +1 -1
- package/dist/replica-peers-config.d.ts +1 -1
- package/dist/resolve-auth-token.d.ts +1 -1
- package/dist/resume-bundles.js +4 -4
- package/dist/retrieval-agents.d.ts +2 -2
- package/dist/retrieval-tiers.d.ts +1 -1
- package/dist/routing/engine.d.ts +1 -1
- package/dist/routing/store.d.ts +1 -1
- package/dist/salvage-envelope.d.ts +1 -1
- package/dist/{scope-profiles-ClLtm9gK.d.ts → scope-profiles-CeMv7pP0.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 +5 -4
- package/dist/search/index.d.ts +1 -1
- package/dist/search/index.js +9 -8
- package/dist/search/lancedb-backend.d.ts +14 -1
- package/dist/search/lancedb-backend.js +1 -1
- package/dist/search/meilisearch-backend.d.ts +1 -1
- package/dist/search/noop-backend.d.ts +1 -1
- package/dist/search/orama-backend.d.ts +22 -1
- package/dist/search/orama-backend.js +2 -1
- package/dist/search/port.d.ts +1 -1
- package/dist/search/remote-backend.d.ts +1 -1
- package/dist/{semantic-consolidation-B1EkeEDj.d.ts → semantic-consolidation-B-85brYj.d.ts} +1 -1
- package/dist/semantic-consolidation.d.ts +2 -2
- package/dist/semantic-consolidation.js +12 -12
- package/dist/semantic-rule-promotion.js +11 -10
- package/dist/semantic-rule-verifier.d.ts +1 -1
- package/dist/semantic-rule-verifier.js +11 -11
- package/dist/{service-Cf3ANQj9.d.ts → service-CoO0HA2E.d.ts} +2 -2
- package/dist/session-observer-bands.d.ts +1 -1
- package/dist/session-observer-state.d.ts +1 -1
- package/dist/shared-context/manager.d.ts +1 -1
- package/dist/signal.d.ts +1 -1
- package/dist/source-agent-qualifier.d.ts +1 -1
- package/dist/source-agent-qualifier.js +11 -10
- package/dist/{storage-DS2O_6Qt.d.ts → storage-BVAVitkO.d.ts} +1 -1
- package/dist/storage.d.ts +2 -2
- package/dist/storage.js +10 -9
- package/dist/summarizer.d.ts +1 -1
- package/dist/summary-snapshot.d.ts +1 -1
- package/dist/support-passport/index.d.ts +14 -14
- package/dist/support-passport/index.js +29 -29
- package/dist/temporal-supersession.d.ts +2 -2
- package/dist/temporal-timeline-recall.d.ts +1 -1
- package/dist/temporal-validity.d.ts +1 -1
- package/dist/threading.d.ts +1 -1
- package/dist/tier-migration.d.ts +2 -2
- package/dist/tier-routing.d.ts +1 -1
- package/dist/topics.d.ts +1 -1
- package/dist/transcript.d.ts +1 -1
- package/dist/transfer/import-sqlite.js +2 -2
- package/dist/trust-score-stage.d.ts +1 -1
- package/dist/trust-score.d.ts +1 -1
- package/dist/trust-zones.js +2 -2
- package/dist/{types-BOANGhCJ.d.ts → types-DWHOxlZP.d.ts} +3 -0
- package/dist/types.d.ts +1 -1
- package/dist/types.js +1 -1
- package/dist/user-message-cleaning.d.ts +23 -0
- package/dist/user-message-cleaning.js +10 -0
- package/dist/user-message-cleaning.js.map +1 -0
- package/dist/utility-runtime.d.ts +1 -1
- package/dist/verified-recall.js +11 -11
- package/dist/work-product-ledger.js +2 -2
- package/dist/write-envelope.d.ts +1 -1
- package/package.json +12 -2
- package/src/access-service-offline-file-content.test.ts +1 -0
- package/src/config.ts +11 -0
- package/src/episodic-context-recall.integration.test.ts +169 -0
- package/src/episodic-context.test.ts +266 -0
- package/src/episodic-context.ts +274 -0
- package/src/lcm/engine.ts +18 -1
- package/src/lifecycle/tombstones.test.ts +134 -0
- package/src/lifecycle/tombstones.ts +29 -2
- package/src/orchestration/episodic-context-section.ts +255 -0
- package/src/orchestration/recall-internal-deps.ts +2 -0
- package/src/orchestration/recall-internal.ts +40 -25
- package/src/orchestration/recall-result-formatter.ts +37 -0
- package/src/recall-tokenization.ts +14 -8
- package/src/reconcile/manifest.test.ts +133 -4
- package/src/reconcile/manifest.ts +43 -7
- package/src/search/factory.ts +1 -0
- package/src/search/lancedb-backend.ts +24 -16
- package/src/search/orama-backend.test.ts +208 -1
- package/src/search/orama-backend.ts +133 -18
- package/src/search/orama-cjk-tokenizer.test.ts +108 -0
- package/src/search/orama-cjk-tokenizer.ts +143 -0
- package/src/storage/tombstone-migration-sources.test.ts +116 -0
- package/src/storage/tombstone-migration-sources.ts +90 -0
- package/src/storage.ts +13 -34
- package/src/types.ts +3 -3
- package/src/user-message-cleaning.ts +139 -0
- package/dist/chunk-5WVUO6QT.js.map +0 -1
- package/dist/chunk-7N7DB4L5.js.map +0 -1
- package/dist/chunk-B2QHVLJO.js.map +0 -1
- package/dist/chunk-BHO3IVAN.js.map +0 -1
- package/dist/chunk-KHQ57QLJ.js.map +0 -1
- package/dist/chunk-VUDALPI5.js.map +0 -1
- package/dist/chunk-XIABRKF6.js.map +0 -1
- package/dist/chunk-XL3TATMJ.js.map +0 -1
- package/dist/chunk-YLHM5BQQ.js.map +0 -1
- package/dist/chunk-ZBJMUXZH.js.map +0 -1
- package/dist/chunk-ZJKLGS76.js.map +0 -1
- /package/dist/{auto-sync-FMWQQB3J.js.map → auto-sync-Z52VWMNC.js.map} +0 -0
- /package/dist/{chunk-QP2IZTUR.js.map → chunk-2TYIAKU5.js.map} +0 -0
- /package/dist/{chunk-QNMNNFKD.js.map → chunk-347ZR6D4.js.map} +0 -0
- /package/dist/{chunk-76YNCJ5H.js.map → chunk-3F3CVVDS.js.map} +0 -0
- /package/dist/{chunk-KCFZOSR6.js.map → chunk-3MA7JVXN.js.map} +0 -0
- /package/dist/{chunk-TLUSSS2F.js.map → chunk-4HO3542O.js.map} +0 -0
- /package/dist/{chunk-4E67XCT2.js.map → chunk-4IZ2IT7D.js.map} +0 -0
- /package/dist/{chunk-33JBK2XP.js.map → chunk-5F6HMNMZ.js.map} +0 -0
- /package/dist/{chunk-5WQMORU5.js.map → chunk-5PFGMO33.js.map} +0 -0
- /package/dist/{chunk-6HEM6HTQ.js.map → chunk-6GGTATZF.js.map} +0 -0
- /package/dist/{chunk-UIRU3MIJ.js.map → chunk-7BJYTX6Q.js.map} +0 -0
- /package/dist/{chunk-2GRLJMKJ.js.map → chunk-7DRSWPUV.js.map} +0 -0
- /package/dist/{chunk-TVVEYCNW.js.map → chunk-7K5Q6COX.js.map} +0 -0
- /package/dist/{chunk-HXXGZ4KV.js.map → chunk-ALNZEWGN.js.map} +0 -0
- /package/dist/{chunk-67MKNKJQ.js.map → chunk-AYIHDC5S.js.map} +0 -0
- /package/dist/{chunk-IXJ5U6ZL.js.map → chunk-BBHFAVMP.js.map} +0 -0
- /package/dist/{chunk-IIRK5V5Z.js.map → chunk-BHO6RHE5.js.map} +0 -0
- /package/dist/{chunk-WANOCU3K.js.map → chunk-BP6WMYLP.js.map} +0 -0
- /package/dist/{chunk-JLMH5YMU.js.map → chunk-CH627RRV.js.map} +0 -0
- /package/dist/{chunk-WTMG5GAK.js.map → chunk-DROAPP6N.js.map} +0 -0
- /package/dist/{chunk-PBH2Z2NY.js.map → chunk-DTLF2SLZ.js.map} +0 -0
- /package/dist/{chunk-7JAXC526.js.map → chunk-EFWHCTYJ.js.map} +0 -0
- /package/dist/{chunk-3XG4QEA2.js.map → chunk-FF6E6HOT.js.map} +0 -0
- /package/dist/{chunk-NP7FLVZW.js.map → chunk-G2FCSILA.js.map} +0 -0
- /package/dist/{chunk-7PVTOZR4.js.map → chunk-H23GIXJC.js.map} +0 -0
- /package/dist/{chunk-WT6UL3UA.js.map → chunk-H7PRHUBD.js.map} +0 -0
- /package/dist/{chunk-ETCVSZLG.js.map → chunk-HSBQJRV2.js.map} +0 -0
- /package/dist/{chunk-GZRDOVQN.js.map → chunk-HVKMJAEF.js.map} +0 -0
- /package/dist/{chunk-CSOYVTRX.js.map → chunk-IIPJKCZB.js.map} +0 -0
- /package/dist/{chunk-E57E5SCS.js.map → chunk-IRLH4NM2.js.map} +0 -0
- /package/dist/{chunk-4G45LJJP.js.map → chunk-IZRNVNPV.js.map} +0 -0
- /package/dist/{chunk-YSUSUZR4.js.map → chunk-JCJGYNIS.js.map} +0 -0
- /package/dist/{chunk-EU74U4GT.js.map → chunk-JDI7G6EA.js.map} +0 -0
- /package/dist/{chunk-QWPAQLYV.js.map → chunk-JFLKSD5V.js.map} +0 -0
- /package/dist/{chunk-KQS72GCR.js.map → chunk-LJOIZPJH.js.map} +0 -0
- /package/dist/{chunk-U27G4E5G.js.map → chunk-LLGDVG52.js.map} +0 -0
- /package/dist/{chunk-JGW2EUCQ.js.map → chunk-PMRUMGK6.js.map} +0 -0
- /package/dist/{chunk-3G6YU5YD.js.map → chunk-PSNN25DW.js.map} +0 -0
- /package/dist/{chunk-LRGSLV7M.js.map → chunk-PYHOAXTI.js.map} +0 -0
- /package/dist/{chunk-75U2FR7U.js.map → chunk-QCI73Z7C.js.map} +0 -0
- /package/dist/{chunk-EHMMRZOZ.js.map → chunk-QICOU4RY.js.map} +0 -0
- /package/dist/{chunk-K4WIUTNH.js.map → chunk-RFH7U5LT.js.map} +0 -0
- /package/dist/{chunk-4SGDYYWV.js.map → chunk-RPKJHYVE.js.map} +0 -0
- /package/dist/{chunk-DYDACAK2.js.map → chunk-TGSMU3F2.js.map} +0 -0
- /package/dist/{chunk-MJFDJTDC.js.map → chunk-TIRJV2LW.js.map} +0 -0
- /package/dist/{chunk-QRA3XVJX.js.map → chunk-UDTME32W.js.map} +0 -0
- /package/dist/{chunk-LAM6UDFV.js.map → chunk-WMOJIOIG.js.map} +0 -0
- /package/dist/{chunk-LLLY5IBK.js.map → chunk-WR2YKUIW.js.map} +0 -0
- /package/dist/{chunk-KYI3CM42.js.map → chunk-X6E7TT3A.js.map} +0 -0
- /package/dist/{chunk-QN7XQEIT.js.map → chunk-YIIC5IDU.js.map} +0 -0
- /package/dist/{chunk-JDJOAL4B.js.map → chunk-YSR3DXGC.js.map} +0 -0
- /package/dist/{chunk-LBJBNWS2.js.map → chunk-YTHQHH5U.js.map} +0 -0
- /package/dist/{chunk-JF6YSHJM.js.map → chunk-ZPGHA7EO.js.map} +0 -0
- /package/dist/{chunk-YG56A6XY.js.map → chunk-ZS4JADYY.js.map} +0 -0
- /package/dist/{forget-5TWK7CZT.js.map → forget-SZ2BXDGF.js.map} +0 -0
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Episodic context planning (issue #2331, harmonic-memory P3).
|
|
3
|
+
*
|
|
4
|
+
* Turns the structured `sources` provenance on recalled facts into episode
|
|
5
|
+
* windows over the LCM archive: resolve each source's turn, group overlapping
|
|
6
|
+
* turn ranges within a session into merged episodes, and cap the episode
|
|
7
|
+
* count/span. Pure planning — no archive access — plus an async wrapper that
|
|
8
|
+
* applies the single documented fallback (quote lookup) through an injected
|
|
9
|
+
* locator so the planner stays testable without SQLite.
|
|
10
|
+
*
|
|
11
|
+
* Window bounds are `[start, end)` throughout (`toTurn` exclusive); the
|
|
12
|
+
* archive's `getMessages` bounds are inclusive, so the fetch site passes
|
|
13
|
+
* `toTurn - 1`.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import type { ProvenanceSource } from "./types.js";
|
|
17
|
+
|
|
18
|
+
/** One fact contributing to episodic planning, with its final recall rank. */
|
|
19
|
+
export interface EpisodicFactInput {
|
|
20
|
+
memoryId: string;
|
|
21
|
+
/** Final recall rank of the fact (0 = best). Lower wins. */
|
|
22
|
+
rank: number;
|
|
23
|
+
sources?: ProvenanceSource[];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** A merged, fetch-ready episode window. */
|
|
27
|
+
export interface EpisodeWindow {
|
|
28
|
+
sessionKey: string;
|
|
29
|
+
/** Inclusive first turn. */
|
|
30
|
+
fromTurn: number;
|
|
31
|
+
/** EXCLUSIVE end turn (`[start, end)` semantics). */
|
|
32
|
+
toTurn: number;
|
|
33
|
+
/** Best (lowest) rank among contributing facts — drives ordering. */
|
|
34
|
+
factRank: number;
|
|
35
|
+
/** Sorted, deduplicated contributing memory ids (citation line). */
|
|
36
|
+
memoryIds: string[];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Locates a quote's turn index within a session, or null on a miss. */
|
|
40
|
+
export type QuoteLocator = (
|
|
41
|
+
quote: string,
|
|
42
|
+
sessionKey: string,
|
|
43
|
+
) => Promise<number | null>;
|
|
44
|
+
|
|
45
|
+
const TURN_FIELD_SEPARATOR = String.fromCharCode(1);
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Resolve a provenance `turnId` to a numeric LCM turn index, or null.
|
|
49
|
+
*
|
|
50
|
+
* Extraction writes `turnId` as the host turn fingerprint
|
|
51
|
+
* (`buildTurnFingerprint`: `role␁content␁thread␁turnIndex`), so the numeric
|
|
52
|
+
* LCM turn index is the LAST `␁`-separated field. A bare integer string is
|
|
53
|
+
* also accepted. Anything else is unresolvable — skip, never guess.
|
|
54
|
+
*/
|
|
55
|
+
export function resolveProvenanceTurnIndex(turnId: unknown): number | null {
|
|
56
|
+
if (typeof turnId !== "string" || turnId.length === 0) return null;
|
|
57
|
+
const lastField = turnId.includes(TURN_FIELD_SEPARATOR)
|
|
58
|
+
? turnId.split(TURN_FIELD_SEPARATOR).pop()!
|
|
59
|
+
: turnId;
|
|
60
|
+
if (!/^\d+$/.test(lastField)) return null;
|
|
61
|
+
const parsed = Number.parseInt(lastField, 10);
|
|
62
|
+
return Number.isSafeInteger(parsed) && parsed >= 0 ? parsed : null;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
interface DraftWindow {
|
|
66
|
+
sessionKey: string;
|
|
67
|
+
fromTurn: number;
|
|
68
|
+
toTurn: number;
|
|
69
|
+
factRank: number;
|
|
70
|
+
bestTurn: number;
|
|
71
|
+
memoryIds: Set<string>;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function windowForTurn(
|
|
75
|
+
sessionKey: string,
|
|
76
|
+
turn: number,
|
|
77
|
+
rank: number,
|
|
78
|
+
memoryId: string,
|
|
79
|
+
): DraftWindow {
|
|
80
|
+
const fromTurn = Math.max(0, turn - 1);
|
|
81
|
+
return {
|
|
82
|
+
sessionKey,
|
|
83
|
+
fromTurn,
|
|
84
|
+
toTurn: turn + 2,
|
|
85
|
+
factRank: rank,
|
|
86
|
+
bestTurn: turn,
|
|
87
|
+
memoryIds: new Set([memoryId]),
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function mergeDrafts(drafts: DraftWindow[], maxTurnsPerEpisode: number): EpisodeWindow[] {
|
|
92
|
+
const bySession = new Map<string, DraftWindow[]>();
|
|
93
|
+
for (const draft of drafts) {
|
|
94
|
+
const list = bySession.get(draft.sessionKey);
|
|
95
|
+
if (list) list.push(draft);
|
|
96
|
+
else bySession.set(draft.sessionKey, [draft]);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const windows: EpisodeWindow[] = [];
|
|
100
|
+
for (const [, sessionDrafts] of bySession) {
|
|
101
|
+
// Sort by fromTurn, then toTurn, so a shuffle cannot change merge results.
|
|
102
|
+
sessionDrafts.sort(
|
|
103
|
+
(a, b) => a.fromTurn - b.fromTurn || a.toTurn - b.toTurn,
|
|
104
|
+
);
|
|
105
|
+
let current: DraftWindow | null = null;
|
|
106
|
+
const flush = () => {
|
|
107
|
+
if (!current) return;
|
|
108
|
+
windows.push(finalizeWindow(current, maxTurnsPerEpisode));
|
|
109
|
+
current = null;
|
|
110
|
+
};
|
|
111
|
+
for (const draft of sessionDrafts) {
|
|
112
|
+
if (current && draft.fromTurn <= current.toTurn) {
|
|
113
|
+
// Overlapping or adjacent ([a,b) + [b,c) share no turn but read as one
|
|
114
|
+
// continuous episode): merge. Merged bounds stay [start, end).
|
|
115
|
+
current.toTurn = Math.max(current.toTurn, draft.toTurn);
|
|
116
|
+
if (draft.factRank < current.factRank) {
|
|
117
|
+
current.factRank = draft.factRank;
|
|
118
|
+
current.bestTurn = draft.bestTurn;
|
|
119
|
+
}
|
|
120
|
+
for (const id of draft.memoryIds) current.memoryIds.add(id);
|
|
121
|
+
} else {
|
|
122
|
+
flush();
|
|
123
|
+
current = { ...draft, memoryIds: new Set(draft.memoryIds) };
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
flush();
|
|
127
|
+
}
|
|
128
|
+
return windows;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function finalizeWindow(draft: DraftWindow, maxTurnsPerEpisode: number): EpisodeWindow {
|
|
132
|
+
let { fromTurn, toTurn } = draft;
|
|
133
|
+
const span = toTurn - fromTurn;
|
|
134
|
+
if (maxTurnsPerEpisode > 0 && span > maxTurnsPerEpisode) {
|
|
135
|
+
// Trim the tail but keep the highest-ranked fact's turn inside: anchor one
|
|
136
|
+
// turn of leading context on the best turn, clamped to the merged range.
|
|
137
|
+
fromTurn = Math.min(
|
|
138
|
+
Math.max(draft.bestTurn - 1, draft.fromTurn),
|
|
139
|
+
draft.toTurn - maxTurnsPerEpisode,
|
|
140
|
+
);
|
|
141
|
+
// maxTurnsPerEpisode === 1 (or a clamp collision) can push the anchor
|
|
142
|
+
// above the best turn; slide back down so the best turn stays included.
|
|
143
|
+
if (fromTurn + maxTurnsPerEpisode <= draft.bestTurn) {
|
|
144
|
+
fromTurn = Math.min(draft.bestTurn, draft.toTurn - maxTurnsPerEpisode);
|
|
145
|
+
}
|
|
146
|
+
toTurn = fromTurn + maxTurnsPerEpisode;
|
|
147
|
+
}
|
|
148
|
+
return {
|
|
149
|
+
sessionKey: draft.sessionKey,
|
|
150
|
+
fromTurn,
|
|
151
|
+
toTurn,
|
|
152
|
+
factRank: draft.factRank,
|
|
153
|
+
memoryIds: [...draft.memoryIds].sort(),
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/** Total, stable ordering: rank asc, sessionKey asc, fromTurn asc, then residual identity. */
|
|
158
|
+
export function compareEpisodeWindows(
|
|
159
|
+
left: EpisodeWindow,
|
|
160
|
+
right: EpisodeWindow,
|
|
161
|
+
): number {
|
|
162
|
+
const leftIds = left.memoryIds.join("\u{0}");
|
|
163
|
+
const rightIds = right.memoryIds.join("\u{0}");
|
|
164
|
+
return (
|
|
165
|
+
left.factRank - right.factRank ||
|
|
166
|
+
(left.sessionKey < right.sessionKey ? -1 : left.sessionKey > right.sessionKey ? 1 : 0) ||
|
|
167
|
+
left.fromTurn - right.fromTurn ||
|
|
168
|
+
left.toTurn - right.toTurn ||
|
|
169
|
+
(leftIds < rightIds ? -1 : leftIds > rightIds ? 1 : 0)
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Plan episode windows from recalled facts' structured provenance.
|
|
175
|
+
*
|
|
176
|
+
* Pure: no archive access. Facts without a structured `sources` array are
|
|
177
|
+
* skipped (no legacy scalar `source` parsing). Sources whose `turnId` does
|
|
178
|
+
* not resolve to a number are skipped — the caller decides whether to attempt
|
|
179
|
+
* the quote fallback for facts that lost every source.
|
|
180
|
+
*
|
|
181
|
+
* `maxEpisodes` or `maxTurnsPerEpisode` of 0 disables the section: returns
|
|
182
|
+
* `[]` before any work (zero semantics, checklist item 33).
|
|
183
|
+
*/
|
|
184
|
+
export function planEpisodeWindows(options: {
|
|
185
|
+
recalledFacts: ReadonlyArray<EpisodicFactInput>;
|
|
186
|
+
maxEpisodes: number;
|
|
187
|
+
maxTurnsPerEpisode: number;
|
|
188
|
+
}): EpisodeWindow[] {
|
|
189
|
+
if (options.maxEpisodes <= 0 || options.maxTurnsPerEpisode <= 0) return [];
|
|
190
|
+
const drafts: DraftWindow[] = [];
|
|
191
|
+
for (const fact of options.recalledFacts) {
|
|
192
|
+
if (!fact || !Array.isArray(fact.sources)) continue;
|
|
193
|
+
for (const source of fact.sources) {
|
|
194
|
+
if (!source || typeof source.sessionKey !== "string" || source.sessionKey.length === 0) {
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
const turn = resolveProvenanceTurnIndex(source.turnId);
|
|
198
|
+
if (turn === null) continue;
|
|
199
|
+
drafts.push(
|
|
200
|
+
windowForTurn(source.sessionKey, turn, fact.rank, fact.memoryId),
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
if (drafts.length === 0) return [];
|
|
205
|
+
const windows = mergeDrafts(drafts, options.maxTurnsPerEpisode);
|
|
206
|
+
return windows.sort(compareEpisodeWindows).slice(0, options.maxEpisodes);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Facts whose sources all failed turn resolution but that carry a quote.
|
|
211
|
+
* These are the ONLY candidates for the quote-lookup fallback.
|
|
212
|
+
*/
|
|
213
|
+
export function factsNeedingQuoteFallback(
|
|
214
|
+
recalledFacts: ReadonlyArray<EpisodicFactInput>,
|
|
215
|
+
): EpisodicFactInput[] {
|
|
216
|
+
return recalledFacts.filter(
|
|
217
|
+
(fact) =>
|
|
218
|
+
fact &&
|
|
219
|
+
Array.isArray(fact.sources) &&
|
|
220
|
+
fact.sources.length > 0 &&
|
|
221
|
+
fact.sources.some((source) => typeof source?.quote === "string" && source.quote.length > 0) &&
|
|
222
|
+
!fact.sources.some((source) => resolveProvenanceTurnIndex(source?.turnId) !== null),
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Plan episode windows, applying the single documented fallback: for a fact
|
|
228
|
+
* whose turnIds ALL failed resolution but that has a quote, locate the quote
|
|
229
|
+
* in the archive and window around the located turn. A locator miss means the
|
|
230
|
+
* fact contributes no episode. Falls back to the pure plan when no locator is
|
|
231
|
+
* supplied.
|
|
232
|
+
*/
|
|
233
|
+
export async function planEpisodeWindowsWithFallback(options: {
|
|
234
|
+
recalledFacts: ReadonlyArray<EpisodicFactInput>;
|
|
235
|
+
maxEpisodes: number;
|
|
236
|
+
maxTurnsPerEpisode: number;
|
|
237
|
+
locateQuote?: QuoteLocator;
|
|
238
|
+
}): Promise<EpisodeWindow[]> {
|
|
239
|
+
const facts = [...options.recalledFacts];
|
|
240
|
+
const locator = options.locateQuote;
|
|
241
|
+
if (locator && facts.length > 0) {
|
|
242
|
+
for (const fact of factsNeedingQuoteFallback(options.recalledFacts)) {
|
|
243
|
+
for (const source of fact.sources ?? []) {
|
|
244
|
+
if (typeof source?.quote !== "string" || source.quote.length === 0) continue;
|
|
245
|
+
if (typeof source.sessionKey !== "string" || source.sessionKey.length === 0) continue;
|
|
246
|
+
let turn: number | null = null;
|
|
247
|
+
try {
|
|
248
|
+
turn = await locator(source.quote, source.sessionKey);
|
|
249
|
+
} catch {
|
|
250
|
+
turn = null;
|
|
251
|
+
}
|
|
252
|
+
if (turn === null || !Number.isSafeInteger(turn) || turn < 0) continue;
|
|
253
|
+
facts.push({
|
|
254
|
+
memoryId: fact.memoryId,
|
|
255
|
+
rank: fact.rank,
|
|
256
|
+
sources: [
|
|
257
|
+
{
|
|
258
|
+
sessionKey: source.sessionKey,
|
|
259
|
+
turnId: String(turn),
|
|
260
|
+
observedAt: source.observedAt,
|
|
261
|
+
quote: source.quote,
|
|
262
|
+
},
|
|
263
|
+
],
|
|
264
|
+
});
|
|
265
|
+
break; // one located quote per fact is enough to window it
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
return planEpisodeWindows({
|
|
270
|
+
recalledFacts: facts,
|
|
271
|
+
maxEpisodes: options.maxEpisodes,
|
|
272
|
+
maxTurnsPerEpisode: options.maxTurnsPerEpisode,
|
|
273
|
+
});
|
|
274
|
+
}
|
package/src/lcm/engine.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type Database from "better-sqlite3";
|
|
2
2
|
import { openLcmDatabase, ensureLcmStateDir } from "./schema.js";
|
|
3
|
-
import { LcmArchive, type LcmStructuredRecallMatch } from "./archive.js";
|
|
3
|
+
import { LcmArchive, type LcmMessage, type LcmStructuredRecallMatch } from "./archive.js";
|
|
4
4
|
import { LcmDag } from "./dag.js";
|
|
5
5
|
import { LcmSummarizer, type SummarizeFn } from "./summarizer.js";
|
|
6
6
|
import {
|
|
@@ -541,6 +541,23 @@ export class LcmEngine {
|
|
|
541
541
|
};
|
|
542
542
|
}
|
|
543
543
|
|
|
544
|
+
/**
|
|
545
|
+
* Retrieve raw archived messages for a turn range (inclusive bounds),
|
|
546
|
+
* read-only and unshaped — no token-budget trimming (issue #2331
|
|
547
|
+
* episodic-context). Callers own truncation and cleaning.
|
|
548
|
+
*/
|
|
549
|
+
async getTurnRange(
|
|
550
|
+
sessionId: string,
|
|
551
|
+
fromTurn: number,
|
|
552
|
+
toTurn: number,
|
|
553
|
+
): Promise<LcmMessage[]> {
|
|
554
|
+
if (!this.config.enabled) return [];
|
|
555
|
+
const normalizedSessionId = normalizeLcmSessionId(sessionId);
|
|
556
|
+
if (!normalizedSessionId) return [];
|
|
557
|
+
await this.ensureInitialized();
|
|
558
|
+
return this.archive!.getMessages(normalizedSessionId, fromTurn, toTurn);
|
|
559
|
+
}
|
|
560
|
+
|
|
544
561
|
/** Retrieve raw messages for a turn range (lossless expansion). */
|
|
545
562
|
async expandContext(
|
|
546
563
|
sessionId: string,
|
|
@@ -829,6 +829,140 @@ describe("TombstoneStore — revocation supersedes tombstone", () => {
|
|
|
829
829
|
});
|
|
830
830
|
});
|
|
831
831
|
|
|
832
|
+
describe("TombstoneStore — rebuild identity preservation (issue #2367)", () => {
|
|
833
|
+
it("repairs a CJK collision instead of indexing the empty legacy skeleton (issue #2367)", async () => {
|
|
834
|
+
const env = await makeStore("default");
|
|
835
|
+
// Two distinct CJK-only strings collide under the lossy legacy normalizer
|
|
836
|
+
// (both skeletons are empty), so a persisted legacy hash of the source
|
|
837
|
+
// numerically equals the legacy hash of the body. The empty skeleton
|
|
838
|
+
// identifies nothing, so rebuild must repair the row to the current body
|
|
839
|
+
// hash rather than publish the collision key on the exact tier.
|
|
840
|
+
const source = "利用者は紅茶を好む。";
|
|
841
|
+
const body = "利用者は珈琲を好む。";
|
|
842
|
+
const unrelated = "利用者は抹茶を好む。";
|
|
843
|
+
const persistedHash = computeLegacyContentHash(source);
|
|
844
|
+
assert.equal(persistedHash, computeLegacyContentHash(body));
|
|
845
|
+
assert.notEqual(computeHash(source), computeHash(body));
|
|
846
|
+
|
|
847
|
+
await env.store.rebuild([
|
|
848
|
+
{
|
|
849
|
+
memoryId: "fact-cjk-override",
|
|
850
|
+
rawContent: body,
|
|
851
|
+
contentHash: persistedHash,
|
|
852
|
+
reason: "correction",
|
|
853
|
+
createdBy: "user_correction",
|
|
854
|
+
createdAt: "2026-01-01T00:00:00.000Z",
|
|
855
|
+
},
|
|
856
|
+
]);
|
|
857
|
+
|
|
858
|
+
const entry = env.store.snapshot().find((e) => e.kind === "tombstone");
|
|
859
|
+
assert.ok(entry);
|
|
860
|
+
assert.equal(entry.contentHash, computeHash(body));
|
|
861
|
+
assert.equal(entry.currentContentHashAlias, undefined);
|
|
862
|
+
// The body stays blocked; the shared empty-skeleton key is NOT indexed,
|
|
863
|
+
// so unrelated CJK bodies (and empty-content writes) are not blocked
|
|
864
|
+
// through it.
|
|
865
|
+
assert.equal(
|
|
866
|
+
env.store.lookup({ namespace: "default", contentHash: computeHash(body) })?.matchedTier,
|
|
867
|
+
"exact",
|
|
868
|
+
);
|
|
869
|
+
assert.equal(
|
|
870
|
+
env.store.lookup({ namespace: "default", contentHash: computeHash(unrelated) }),
|
|
871
|
+
null,
|
|
872
|
+
);
|
|
873
|
+
assert.equal(
|
|
874
|
+
env.store.lookup({ namespace: "default", contentHash: computeLegacyContentHash(unrelated) }),
|
|
875
|
+
null,
|
|
876
|
+
);
|
|
877
|
+
});
|
|
878
|
+
|
|
879
|
+
it("preserves an explicit identity hash on an empty-skeleton body instead of repairing it", async () => {
|
|
880
|
+
const env = await makeStore("default");
|
|
881
|
+
// A CJK-only body normalizes to an empty legacy skeleton, but the
|
|
882
|
+
// persisted hash here is a DISTINCT explicit contentHashSource — it does
|
|
883
|
+
// not equal the body's legacy hash, so it identifies an external source,
|
|
884
|
+
// not the body. Rebuild must keep it (plus the body alias), not replace
|
|
885
|
+
// it with the current body hash.
|
|
886
|
+
const body = "利用者は紅茶を好む。";
|
|
887
|
+
const source = "canonical://source/fact-cjk-explicit";
|
|
888
|
+
const explicitHash = computeHash(source);
|
|
889
|
+
assert.notEqual(explicitHash, computeLegacyContentHash(body));
|
|
890
|
+
assert.notEqual(explicitHash, computeHash(body));
|
|
891
|
+
|
|
892
|
+
await env.store.rebuild([
|
|
893
|
+
{
|
|
894
|
+
memoryId: "fact-cjk-explicit",
|
|
895
|
+
rawContent: body,
|
|
896
|
+
contentHash: explicitHash,
|
|
897
|
+
reason: "correction",
|
|
898
|
+
createdBy: "user_correction",
|
|
899
|
+
createdAt: "2026-01-01T00:00:00.000Z",
|
|
900
|
+
},
|
|
901
|
+
]);
|
|
902
|
+
|
|
903
|
+
const entry = env.store.snapshot().find((e) => e.kind === "tombstone");
|
|
904
|
+
assert.ok(entry);
|
|
905
|
+
assert.equal(entry.contentHash, explicitHash);
|
|
906
|
+
assert.equal(entry.currentContentHashAlias, computeHash(body));
|
|
907
|
+
// The explicit identity and the body are both blocked.
|
|
908
|
+
assert.equal(
|
|
909
|
+
env.store.lookup({ namespace: "default", contentHash: explicitHash })?.matchedTier,
|
|
910
|
+
"exact",
|
|
911
|
+
);
|
|
912
|
+
assert.equal(
|
|
913
|
+
env.store.lookup({ namespace: "default", contentHash: computeHash(body) })?.matchedTier,
|
|
914
|
+
"exact",
|
|
915
|
+
);
|
|
916
|
+
});
|
|
917
|
+
|
|
918
|
+
it("preserves a persisted hash whose ASCII skeleton collides with an accented body", async () => {
|
|
919
|
+
const env = await makeStore("default");
|
|
920
|
+
const body = "The user prefers café.";
|
|
921
|
+
await env.store.rebuild([
|
|
922
|
+
{
|
|
923
|
+
memoryId: "fact-accented-override",
|
|
924
|
+
rawContent: body,
|
|
925
|
+
contentHash: computeLegacyContentHash(body),
|
|
926
|
+
reason: "correction",
|
|
927
|
+
createdBy: "user_correction",
|
|
928
|
+
createdAt: "2026-01-01T00:00:00.000Z",
|
|
929
|
+
},
|
|
930
|
+
]);
|
|
931
|
+
const entry = env.store.snapshot().find((e) => e.kind === "tombstone");
|
|
932
|
+
assert.ok(entry);
|
|
933
|
+
// The legacy equality is ambiguous (the skeleton "the user prefers caf"
|
|
934
|
+
// is shared with distinct strings), so the persisted identity is kept and
|
|
935
|
+
// the current body hash rides along as the alias.
|
|
936
|
+
assert.equal(entry.contentHash, computeLegacyContentHash(body));
|
|
937
|
+
assert.equal(entry.currentContentHashAlias, computeHash(body));
|
|
938
|
+
assert.equal(
|
|
939
|
+
env.store.lookup({ namespace: "default", contentHash: computeHash(body) })?.matchedTier,
|
|
940
|
+
"exact",
|
|
941
|
+
);
|
|
942
|
+
});
|
|
943
|
+
|
|
944
|
+
it("keeps a single identity when the persisted hash already equals the current body hash", async () => {
|
|
945
|
+
const env = await makeStore("default");
|
|
946
|
+
const body = "The cache uses Redis.";
|
|
947
|
+
await env.store.rebuild([
|
|
948
|
+
{
|
|
949
|
+
memoryId: "fact-unambiguous",
|
|
950
|
+
rawContent: body,
|
|
951
|
+
contentHash: computeLegacyContentHash(body),
|
|
952
|
+
reason: "correction",
|
|
953
|
+
createdBy: "user_correction",
|
|
954
|
+
createdAt: "2026-01-01T00:00:00.000Z",
|
|
955
|
+
},
|
|
956
|
+
]);
|
|
957
|
+
const entry = env.store.snapshot().find((e) => e.kind === "tombstone");
|
|
958
|
+
assert.ok(entry);
|
|
959
|
+
// Pure-ASCII body: legacy and current normalizers agree, so the legacy
|
|
960
|
+
// hash equals the current hash and there is nothing to preserve.
|
|
961
|
+
assert.equal(entry.contentHash, computeHash(body));
|
|
962
|
+
assert.equal(entry.currentContentHashAlias, undefined);
|
|
963
|
+
});
|
|
964
|
+
});
|
|
965
|
+
|
|
832
966
|
describe("TombstoneStore — rebuild equivalence", () => {
|
|
833
967
|
it("rebuild from a fixture corpus reproduces identical lookup decisions", async () => {
|
|
834
968
|
const env = await makeStore("default");
|
|
@@ -41,7 +41,11 @@
|
|
|
41
41
|
// ---------------------------------------------------------------------------
|
|
42
42
|
|
|
43
43
|
import { serializeMutations, withHeldFileLock } from "../utils/serialize-mutations.js";
|
|
44
|
-
import {
|
|
44
|
+
import {
|
|
45
|
+
computeLegacyContentHash,
|
|
46
|
+
isUnambiguousLegacyContentHash,
|
|
47
|
+
normalizeLegacyContent,
|
|
48
|
+
} from "../content-hash.js";
|
|
45
49
|
|
|
46
50
|
/** Why a tombstone was emitted. */
|
|
47
51
|
export type TombstoneReason =
|
|
@@ -896,10 +900,33 @@ export class TombstoneStore {
|
|
|
896
900
|
const currentHash = this.options.hashContent(m.rawContent);
|
|
897
901
|
const currentNormalizedText = this.options.normalizeText(m.rawContent);
|
|
898
902
|
const persistedHash = m.contentHash;
|
|
903
|
+
// A persisted hash equal to the body's LEGACY hash only proves the
|
|
904
|
+
// record predates the Unicode normalizer when that legacy hash is
|
|
905
|
+
// unambiguous. When the legacy normalizer is lossy for this body
|
|
906
|
+
// (any body whose ASCII skeleton collides with a distinct
|
|
907
|
+
// contentHashSource), the equality cannot distinguish "old body
|
|
908
|
+
// hash" from "explicit source identity" — replacing the persisted
|
|
909
|
+
// hash would displace a live identity (issue #2367). Preserving
|
|
910
|
+
// keeps BOTH keys: contentHash stays the persisted identity and
|
|
911
|
+
// currentContentHashAlias still blocks the body.
|
|
912
|
+
|
|
913
|
+
// Repair only when the persisted hash provably identifies THIS
|
|
914
|
+
// body's stale legacy hash: either the unambiguous case (legacy and
|
|
915
|
+
// current normalizations agree), or the empty-skeleton case — every
|
|
916
|
+
// non-ASCII-only body normalizes to the empty legacy skeleton, so a
|
|
917
|
+
// persisted hash equal to hash("") is the shared non-identifying
|
|
918
|
+
// legacy body hash and publishing it on the exact tier would
|
|
919
|
+
// collide unrelated tombstones. Any OTHER persisted hash on an
|
|
920
|
+
// empty-skeleton body is a distinct explicit identity (e.g.
|
|
921
|
+
// contentHashSource) and must be preserved.
|
|
922
|
+
const legacyBodyHash = computeLegacyContentHash(m.rawContent);
|
|
923
|
+
const repairEmptySkeleton =
|
|
924
|
+
normalizeLegacyContent(m.rawContent).length === 0 && persistedHash === legacyBodyHash;
|
|
899
925
|
const preserveOverride =
|
|
900
926
|
persistedHash !== undefined &&
|
|
901
927
|
persistedHash !== currentHash &&
|
|
902
|
-
|
|
928
|
+
!repairEmptySkeleton &&
|
|
929
|
+
!isUnambiguousLegacyContentHash(m.rawContent, persistedHash, currentNormalizedText);
|
|
903
930
|
return {
|
|
904
931
|
id:
|
|
905
932
|
existingBySource.get(`${m.memoryId}\u{0000}${m.supersessionKey ?? ""}`) ??
|