@remnic/core 9.62.0 → 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 +28 -28
- package/dist/access-audit.js +2 -2
- package/dist/access-authorization-probe.d.ts +11 -11
- package/dist/access-authorization-probe.js +29 -29
- package/dist/access-boundary.d.ts +11 -11
- package/dist/access-boundary.js +28 -28
- package/dist/access-cli.js +93 -91
- 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 +28 -28
- 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 +28 -28
- package/dist/access-http.d.ts +12 -12
- package/dist/access-http.js +38 -38
- package/dist/access-identity-continuity-surface.d.ts +9 -9
- package/dist/access-identity-continuity-surface.js +28 -28
- package/dist/access-lcm-surface.d.ts +11 -11
- package/dist/access-lcm-surface.js +28 -28
- package/dist/access-mcp.d.ts +11 -11
- package/dist/access-mcp.js +33 -33
- 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 +28 -28
- package/dist/access-observe-write-surface.d.ts +11 -11
- package/dist/access-observe-write-surface.js +28 -28
- package/dist/access-offline-manifest.d.ts +9 -9
- package/dist/access-offline-manifest.js +2 -2
- package/dist/access-operations-batch.js +29 -29
- package/dist/access-operations.d.ts +11 -11
- package/dist/access-operations.js +32 -32
- package/dist/access-recall-concurrency.d.ts +11 -11
- package/dist/access-recall-concurrency.js +28 -28
- package/dist/access-recall-response.d.ts +11 -11
- package/dist/access-recall-response.js +28 -28
- package/dist/access-recall-surface.d.ts +11 -11
- package/dist/access-recall-surface.js +28 -28
- 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 +28 -28
- package/dist/access-surface-catalog.d.ts +11 -11
- package/dist/access-surface-catalog.js +30 -30
- 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-memory-bridge.js +2 -2
- package/dist/active-recall.d.ts +1 -1
- package/dist/active-recall.js +3 -3
- 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 +13 -12
- 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 +16 -16
- 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-HQBVQICL.js → chunk-347ZR6D4.js} +2 -2
- package/dist/{chunk-76YNCJ5H.js → chunk-3F3CVVDS.js} +2 -2
- package/dist/{chunk-TE4MKFT2.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-AWRJBAIH.js → chunk-4FFROSFG.js} +37 -14
- package/dist/{chunk-AWRJBAIH.js.map → chunk-4FFROSFG.js.map} +1 -1
- package/dist/{chunk-N4OYPGZS.js → chunk-4HO3542O.js} +2 -2
- package/dist/{chunk-HEEEMJ26.js → chunk-4IZ2IT7D.js} +3 -3
- package/dist/{chunk-4XAVQLBR.js → chunk-4Y2RHVCV.js} +2 -2
- 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-W3Y6FYRK.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-S3B3UIQ3.js → chunk-AYIHDC5S.js} +2 -2
- package/dist/{chunk-2RIDW23Y.js → chunk-BBHFAVMP.js} +8 -8
- package/dist/{chunk-MJ4UGUR6.js → chunk-BHO6RHE5.js} +3 -3
- package/dist/{chunk-URN7XWGC.js → chunk-BP6WMYLP.js} +3 -3
- package/dist/{chunk-YUIBXMZY.js → chunk-CALURYWR.js} +284 -249
- package/dist/chunk-CALURYWR.js.map +1 -0
- package/dist/{chunk-D5HDJWNN.js → chunk-CH627RRV.js} +4 -4
- package/dist/{chunk-C3R732UY.js → chunk-CLJ5QS6J.js} +257 -60
- 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-XDK6ZFLL.js → chunk-DROAPP6N.js} +3 -3
- package/dist/{chunk-MU3TPZOV.js → chunk-DTLF2SLZ.js} +2 -2
- package/dist/{chunk-I67HKRBF.js → chunk-EFWHCTYJ.js} +2 -2
- package/dist/{chunk-XD4VGLDL.js → chunk-FF6E6HOT.js} +56 -52
- package/dist/chunk-FF6E6HOT.js.map +1 -0
- package/dist/{chunk-NP7FLVZW.js → chunk-G2FCSILA.js} +2 -2
- package/dist/{chunk-3GPBFDXH.js → chunk-H23GIXJC.js} +2 -2
- package/dist/{chunk-WT6UL3UA.js → chunk-H7PRHUBD.js} +2 -2
- package/dist/{chunk-4JGSG7QN.js → chunk-HSBQJRV2.js} +2 -2
- package/dist/{chunk-L33N3BCA.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-H32ANIFP.js → chunk-IZRNVNPV.js} +2 -2
- package/dist/{chunk-XBR5BAKW.js → chunk-JCJGYNIS.js} +3 -3
- package/dist/{chunk-EIQFIJDM.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-N4EPIKSY.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-VOELBUS4.js → chunk-NXH34IPL.js} +2 -2
- package/dist/{chunk-XL3TATMJ.js → chunk-OQIRBGUU.js} +21 -12
- package/dist/chunk-OQIRBGUU.js.map +1 -0
- package/dist/chunk-PJKTFXE2.js +119 -0
- package/dist/chunk-PJKTFXE2.js.map +1 -0
- package/dist/{chunk-JGW2EUCQ.js → chunk-PMRUMGK6.js} +2 -2
- package/dist/{chunk-KAB7TZN3.js → chunk-PSNN25DW.js} +2 -2
- package/dist/{chunk-LRGSLV7M.js → chunk-PYHOAXTI.js} +4 -4
- package/dist/{chunk-PA5OPZX2.js → chunk-QCI73Z7C.js} +2 -2
- package/dist/{chunk-EHMMRZOZ.js → chunk-QICOU4RY.js} +2 -2
- package/dist/{chunk-FW73HN33.js → chunk-RFH7U5LT.js} +2 -2
- package/dist/{chunk-4SGDYYWV.js → chunk-RPKJHYVE.js} +2 -2
- package/dist/{chunk-DSA47WBN.js → chunk-RYST72N5.js} +13 -2
- package/dist/chunk-RYST72N5.js.map +1 -0
- package/dist/{chunk-L4DLLKPN.js → chunk-SZ4HFZKB.js} +2 -2
- package/dist/chunk-T3GIETKV.js +152 -0
- package/dist/chunk-T3GIETKV.js.map +1 -0
- package/dist/{chunk-MYZ4NA43.js → chunk-TGSMU3F2.js} +3 -3
- package/dist/{chunk-CM4SZZBN.js → chunk-TIRJV2LW.js} +2 -2
- package/dist/{chunk-EGW4O32Z.js → chunk-UDTME32W.js} +7 -4
- package/dist/chunk-UDTME32W.js.map +1 -0
- package/dist/{chunk-CIA6ZUBA.js → chunk-WMOJIOIG.js} +2 -2
- package/dist/{chunk-W4Q54HM3.js → chunk-WR2YKUIW.js} +2 -2
- package/dist/{chunk-VWDTV6SH.js → chunk-X6E7TT3A.js} +2 -2
- package/dist/{chunk-D4OCJSLV.js → chunk-YDPAE2RD.js} +2 -2
- package/dist/{chunk-G3YBWFUC.js → chunk-YIIC5IDU.js} +35 -18
- package/dist/chunk-YIIC5IDU.js.map +1 -0
- package/dist/{chunk-QRKYABS6.js → chunk-YSR3DXGC.js} +2 -2
- package/dist/{chunk-LBJBNWS2.js → chunk-YTHQHH5U.js} +2 -2
- package/dist/{chunk-GN7HO2LA.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-FQSDU634.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 +62 -62
- 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 +13 -12
- package/dist/compounding/preference-consolidator.d.ts +1 -1
- package/dist/compression-optimizer.d.ts +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.js +3 -3
- package/dist/connectors/codex-materialize-runner.d.ts +1 -1
- package/dist/connectors/codex-materialize-runner.js +13 -12
- package/dist/connectors/codex-materialize.d.ts +1 -1
- package/dist/connectors/index.d.ts +1 -1
- package/dist/connectors/index.js +13 -12
- 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 +15 -14
- 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 +15 -14
- 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 +13 -12
- 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 +29 -29
- 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-source-grounding.js +3 -3
- package/dist/extraction.d.ts +1 -1
- package/dist/extraction.js +18 -18
- 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 +22 -21
- package/dist/index.js +118 -103
- 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 +13 -12
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.d.ts +2 -2
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +13 -12
- package/dist/maintenance/rebuild-memory-projection.d.ts +2 -2
- package/dist/maintenance/rebuild-memory-projection.js +14 -13
- 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 +14 -13
- 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 +13 -12
- 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 +16 -15
- package/dist/operator-doctor-replica.d.ts +1 -1
- package/dist/operator-toolkit.d.ts +3 -3
- package/dist/operator-toolkit.js +21 -20
- package/dist/orchestration/compression-guideline-coordinator.d.ts +2 -2
- package/dist/orchestration/maintenance.d.ts +4 -4
- package/dist/orchestration/maintenance.js +18 -17
- package/dist/{orchestrator-HPuY6I4i.d.ts → orchestrator-CFi-5JSR.d.ts} +23 -8
- package/dist/orchestrator.d.ts +9 -9
- package/dist/orchestrator.js +93 -91
- 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/provenance.js +2 -2
- 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 +6 -6
- 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 +14 -14
- package/dist/semantic-rule-promotion.js +13 -12
- package/dist/semantic-rule-verifier.d.ts +1 -1
- package/dist/semantic-rule-verifier.js +13 -13
- 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 +15 -14
- package/dist/{storage-DS2O_6Qt.d.ts → storage-BVAVitkO.d.ts} +1 -1
- package/dist/storage.d.ts +2 -2
- package/dist/storage.js +12 -11
- 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 +31 -31
- 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 +13 -13
- package/dist/whitespace.d.ts +26 -1
- package/dist/whitespace.js +11 -3
- 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/cli.ts +14 -13
- package/src/config.ts +11 -0
- package/src/entity-retrieval.ts +4 -7
- 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/harmonic-construction.test.ts +187 -2
- package/src/harmonic-retrieval.ts +54 -32
- package/src/index.ts +3 -4
- 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/orchestration/recall-section-coordinator.ts +20 -14
- 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/src/whitespace.test.ts +178 -0
- package/src/whitespace.ts +119 -0
- package/dist/chunk-5WVUO6QT.js.map +0 -1
- package/dist/chunk-6MKAMLQL.js +0 -16
- package/dist/chunk-6MKAMLQL.js.map +0 -1
- package/dist/chunk-7N7DB4L5.js.map +0 -1
- package/dist/chunk-B2QHVLJO.js.map +0 -1
- package/dist/chunk-C3R732UY.js.map +0 -1
- package/dist/chunk-DSA47WBN.js.map +0 -1
- package/dist/chunk-EGW4O32Z.js.map +0 -1
- package/dist/chunk-G3YBWFUC.js.map +0 -1
- package/dist/chunk-KHQ57QLJ.js.map +0 -1
- package/dist/chunk-VUDALPI5.js.map +0 -1
- package/dist/chunk-XD4VGLDL.js.map +0 -1
- package/dist/chunk-XL3TATMJ.js.map +0 -1
- package/dist/chunk-YLHM5BQQ.js.map +0 -1
- package/dist/chunk-YUIBXMZY.js.map +0 -1
- package/dist/chunk-ZBJMUXZH.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-HQBVQICL.js.map → chunk-347ZR6D4.js.map} +0 -0
- /package/dist/{chunk-76YNCJ5H.js.map → chunk-3F3CVVDS.js.map} +0 -0
- /package/dist/{chunk-TE4MKFT2.js.map → chunk-3MA7JVXN.js.map} +0 -0
- /package/dist/{chunk-N4OYPGZS.js.map → chunk-4HO3542O.js.map} +0 -0
- /package/dist/{chunk-HEEEMJ26.js.map → chunk-4IZ2IT7D.js.map} +0 -0
- /package/dist/{chunk-4XAVQLBR.js.map → chunk-4Y2RHVCV.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-W3Y6FYRK.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-S3B3UIQ3.js.map → chunk-AYIHDC5S.js.map} +0 -0
- /package/dist/{chunk-2RIDW23Y.js.map → chunk-BBHFAVMP.js.map} +0 -0
- /package/dist/{chunk-MJ4UGUR6.js.map → chunk-BHO6RHE5.js.map} +0 -0
- /package/dist/{chunk-URN7XWGC.js.map → chunk-BP6WMYLP.js.map} +0 -0
- /package/dist/{chunk-D5HDJWNN.js.map → chunk-CH627RRV.js.map} +0 -0
- /package/dist/{chunk-XDK6ZFLL.js.map → chunk-DROAPP6N.js.map} +0 -0
- /package/dist/{chunk-MU3TPZOV.js.map → chunk-DTLF2SLZ.js.map} +0 -0
- /package/dist/{chunk-I67HKRBF.js.map → chunk-EFWHCTYJ.js.map} +0 -0
- /package/dist/{chunk-NP7FLVZW.js.map → chunk-G2FCSILA.js.map} +0 -0
- /package/dist/{chunk-3GPBFDXH.js.map → chunk-H23GIXJC.js.map} +0 -0
- /package/dist/{chunk-WT6UL3UA.js.map → chunk-H7PRHUBD.js.map} +0 -0
- /package/dist/{chunk-4JGSG7QN.js.map → chunk-HSBQJRV2.js.map} +0 -0
- /package/dist/{chunk-L33N3BCA.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-H32ANIFP.js.map → chunk-IZRNVNPV.js.map} +0 -0
- /package/dist/{chunk-XBR5BAKW.js.map → chunk-JCJGYNIS.js.map} +0 -0
- /package/dist/{chunk-EIQFIJDM.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-N4EPIKSY.js.map → chunk-LLGDVG52.js.map} +0 -0
- /package/dist/{chunk-VOELBUS4.js.map → chunk-NXH34IPL.js.map} +0 -0
- /package/dist/{chunk-JGW2EUCQ.js.map → chunk-PMRUMGK6.js.map} +0 -0
- /package/dist/{chunk-KAB7TZN3.js.map → chunk-PSNN25DW.js.map} +0 -0
- /package/dist/{chunk-LRGSLV7M.js.map → chunk-PYHOAXTI.js.map} +0 -0
- /package/dist/{chunk-PA5OPZX2.js.map → chunk-QCI73Z7C.js.map} +0 -0
- /package/dist/{chunk-EHMMRZOZ.js.map → chunk-QICOU4RY.js.map} +0 -0
- /package/dist/{chunk-FW73HN33.js.map → chunk-RFH7U5LT.js.map} +0 -0
- /package/dist/{chunk-4SGDYYWV.js.map → chunk-RPKJHYVE.js.map} +0 -0
- /package/dist/{chunk-L4DLLKPN.js.map → chunk-SZ4HFZKB.js.map} +0 -0
- /package/dist/{chunk-MYZ4NA43.js.map → chunk-TGSMU3F2.js.map} +0 -0
- /package/dist/{chunk-CM4SZZBN.js.map → chunk-TIRJV2LW.js.map} +0 -0
- /package/dist/{chunk-CIA6ZUBA.js.map → chunk-WMOJIOIG.js.map} +0 -0
- /package/dist/{chunk-W4Q54HM3.js.map → chunk-WR2YKUIW.js.map} +0 -0
- /package/dist/{chunk-VWDTV6SH.js.map → chunk-X6E7TT3A.js.map} +0 -0
- /package/dist/{chunk-D4OCJSLV.js.map → chunk-YDPAE2RD.js.map} +0 -0
- /package/dist/{chunk-QRKYABS6.js.map → chunk-YSR3DXGC.js.map} +0 -0
- /package/dist/{chunk-LBJBNWS2.js.map → chunk-YTHQHH5U.js.map} +0 -0
- /package/dist/{chunk-GN7HO2LA.js.map → chunk-ZPGHA7EO.js.map} +0 -0
- /package/dist/{chunk-FQSDU634.js.map → chunk-ZS4JADYY.js.map} +0 -0
- /package/dist/{forget-5TWK7CZT.js.map → forget-SZ2BXDGF.js.map} +0 -0
|
@@ -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 ?? ""}`) ??
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Episodic-context recall section (issue #2331, harmonic-memory P3).
|
|
3
|
+
*
|
|
4
|
+
* Extracted sibling of `recallInternal`'s final assembly phase: resolves the
|
|
5
|
+
* structured `sources` provenance of the top recalled facts to LCM archive
|
|
6
|
+
* turn ranges, groups overlapping ranges into episodes, and appends the raw
|
|
7
|
+
* turns as a `## Source Episodes` section — pure lookup + formatting, no LLM
|
|
8
|
+
* calls, read-only archive access, inside the enrichment deadline.
|
|
9
|
+
*
|
|
10
|
+
* Opt-in: the registry entry is gated on `episodicContextEnabled === true`
|
|
11
|
+
* (default off), so flag-off returns before touching the archive and recall
|
|
12
|
+
* output stays byte-identical.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { lcmSessionKeyForNamespace } from "../coding/coding-namespace.js";
|
|
16
|
+
import { type EpisodicFactInput, planEpisodeWindowsWithFallback } from "../episodic-context.js";
|
|
17
|
+
import type { LcmMessage } from "../lcm/archive.js";
|
|
18
|
+
import { log } from "../logger.js";
|
|
19
|
+
import {
|
|
20
|
+
type RecallSectionMetric,
|
|
21
|
+
resolveRecallCoreSectionDeadlineMs,
|
|
22
|
+
runRecallSectionWithinDeadline,
|
|
23
|
+
} from "../recall-qos.js";
|
|
24
|
+
import { cleanArchivedUserMessage } from "../user-message-cleaning.js";
|
|
25
|
+
import type { RecallSectionBuckets } from "./recall-section-coordinator.js";
|
|
26
|
+
import type { RecallInternalDeps } from "./recall-internal-deps.js";
|
|
27
|
+
import type { ScopePlan } from "../scopes/scope-plan.js";
|
|
28
|
+
|
|
29
|
+
/** Cap on recalled facts whose provenance is read per recall. */
|
|
30
|
+
const EPISODIC_FACT_SCAN_LIMIT = 8;
|
|
31
|
+
/** Per-turn character cap inside a Source Episodes block. */
|
|
32
|
+
const EPISODIC_TURN_CHAR_LIMIT = 300;
|
|
33
|
+
|
|
34
|
+
export async function appendEpisodicContextSection(
|
|
35
|
+
deps: RecallInternalDeps,
|
|
36
|
+
options: {
|
|
37
|
+
sectionBuckets: RecallSectionBuckets;
|
|
38
|
+
recalledMemoryPaths: string[];
|
|
39
|
+
recalledMemoryNamespaces: Array<string | undefined>;
|
|
40
|
+
scopePlan: ScopePlan;
|
|
41
|
+
namespacesEnabled: boolean;
|
|
42
|
+
enrichmentSectionDeadlineMs: number;
|
|
43
|
+
recallOuterTimeoutMs: number;
|
|
44
|
+
recallStart: number;
|
|
45
|
+
abortSignal?: AbortSignal;
|
|
46
|
+
recordMetric: (metric: RecallSectionMetric) => void;
|
|
47
|
+
},
|
|
48
|
+
): Promise<void> {
|
|
49
|
+
const engine = deps.lcmEngine;
|
|
50
|
+
if (!engine?.enabled) return;
|
|
51
|
+
if (!deps.isRecallSectionEnabled("episodic-context", false)) return;
|
|
52
|
+
const maxEpisodes =
|
|
53
|
+
deps.getRecallSectionNumber("episodic-context", "maxResults") ?? 2;
|
|
54
|
+
const maxTurns = deps.getRecallSectionNumber("episodic-context", "maxTurns") ?? 8;
|
|
55
|
+
const skip = (timing: string) =>
|
|
56
|
+
options.recordMetric({
|
|
57
|
+
section: "episodicContext",
|
|
58
|
+
priority: "enrichment",
|
|
59
|
+
durationMs: 0,
|
|
60
|
+
deadlineMs: options.enrichmentSectionDeadlineMs,
|
|
61
|
+
source: "skip",
|
|
62
|
+
success: true,
|
|
63
|
+
timing,
|
|
64
|
+
});
|
|
65
|
+
if (maxEpisodes <= 0 || maxTurns <= 0) {
|
|
66
|
+
// Zero semantics (checklist item 33): disable before any archive query.
|
|
67
|
+
skip("skip(limit=0)");
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
if (options.recalledMemoryPaths.length === 0) {
|
|
71
|
+
skip("skip(no-memories)");
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const outerDeadlineAtMs =
|
|
76
|
+
options.recallOuterTimeoutMs > 0
|
|
77
|
+
? options.recallStart + options.recallOuterTimeoutMs
|
|
78
|
+
: null;
|
|
79
|
+
const remainingOuterMs =
|
|
80
|
+
outerDeadlineAtMs === null ? null : outerDeadlineAtMs - deps.recallAssemblyClockMs();
|
|
81
|
+
const deadlineMs = resolveRecallCoreSectionDeadlineMs({
|
|
82
|
+
configuredCoreDeadlineMs: options.enrichmentSectionDeadlineMs,
|
|
83
|
+
remainingOuterMs,
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const outcome = await runRecallSectionWithinDeadline<string | null>({
|
|
87
|
+
deadlineMs,
|
|
88
|
+
fallback: null,
|
|
89
|
+
parentSignal: options.abortSignal,
|
|
90
|
+
run: async (signal) => {
|
|
91
|
+
// 1. Structured provenance from the top recalled facts. Facts whose
|
|
92
|
+
// frontmatter carries no `sources` array are silently skipped (no
|
|
93
|
+
// legacy scalar `source` parsing — issue non-goal).
|
|
94
|
+
const facts: EpisodicFactInput[] = [];
|
|
95
|
+
const scanLimit = Math.min(
|
|
96
|
+
options.recalledMemoryPaths.length,
|
|
97
|
+
EPISODIC_FACT_SCAN_LIMIT,
|
|
98
|
+
);
|
|
99
|
+
for (let index = 0; index < scanLimit; index += 1) {
|
|
100
|
+
const memoryPath = options.recalledMemoryPaths[index]!;
|
|
101
|
+
const namespace = options.recalledMemoryNamespaces[index];
|
|
102
|
+
try {
|
|
103
|
+
const storage = await deps.storageRouter.storageFor(
|
|
104
|
+
namespace ?? deps.config.defaultNamespace,
|
|
105
|
+
);
|
|
106
|
+
const memory = await storage.readMemoryByPath(memoryPath);
|
|
107
|
+
const sources = memory?.frontmatter?.sources;
|
|
108
|
+
if (memory && Array.isArray(sources) && sources.length > 0) {
|
|
109
|
+
facts.push({
|
|
110
|
+
memoryId: memory.frontmatter.id,
|
|
111
|
+
rank: index,
|
|
112
|
+
sources,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
} catch (err) {
|
|
116
|
+
log.debug(
|
|
117
|
+
`episodic-context: provenance read failed for ${memoryPath}`,
|
|
118
|
+
err,
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (facts.length === 0) return null;
|
|
123
|
+
|
|
124
|
+
// 2. Authoritative LCM session ids for a provenance sessionKey
|
|
125
|
+
// (checklist item 30): only keys framed under the caller's authorized read
|
|
126
|
+
// namespaces. A foreign session's rows live under a foreign namespace
|
|
127
|
+
// frame and are unreachable through this mapping.
|
|
128
|
+
const readNamespaces = options.namespacesEnabled
|
|
129
|
+
? options.scopePlan.lcmReadNamespaces
|
|
130
|
+
: [deps.config.defaultNamespace];
|
|
131
|
+
const candidateSessionIds = (sessionKey: string): string[] => {
|
|
132
|
+
const seen = new Set<string>();
|
|
133
|
+
const out: string[] = [];
|
|
134
|
+
for (const namespace of readNamespaces) {
|
|
135
|
+
const key =
|
|
136
|
+
lcmSessionKeyForNamespace(
|
|
137
|
+
namespace,
|
|
138
|
+
sessionKey,
|
|
139
|
+
deps.config.defaultNamespace,
|
|
140
|
+
) ?? sessionKey;
|
|
141
|
+
if (!seen.has(key)) {
|
|
142
|
+
seen.add(key);
|
|
143
|
+
out.push(key);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return out;
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
// 3. Plan windows (pure) with the single documented quote fallback.
|
|
150
|
+
const windows = await planEpisodeWindowsWithFallback({
|
|
151
|
+
recalledFacts: facts,
|
|
152
|
+
maxEpisodes,
|
|
153
|
+
maxTurnsPerEpisode: maxTurns,
|
|
154
|
+
locateQuote: async (quote, sessionKey) => {
|
|
155
|
+
for (const sessionId of candidateSessionIds(sessionKey)) {
|
|
156
|
+
try {
|
|
157
|
+
const hits = await engine.searchContextFull(quote, 1, sessionId);
|
|
158
|
+
if (hits.length > 0) return hits[0]!.turn_index;
|
|
159
|
+
} catch (err) {
|
|
160
|
+
// One failing session read must not sink the fallback; try the
|
|
161
|
+
// next candidate (fail-open per candidate).
|
|
162
|
+
log.debug(
|
|
163
|
+
`episodic-context: quote search failed for session ${sessionId}`,
|
|
164
|
+
err,
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return null;
|
|
169
|
+
},
|
|
170
|
+
});
|
|
171
|
+
if (windows.length === 0) return null;
|
|
172
|
+
|
|
173
|
+
// 4. Fetch + clean raw turns. `getMessages` bounds are inclusive, so
|
|
174
|
+
// the exclusive `toTurn` passes as `toTurn - 1`.
|
|
175
|
+
const episodes: Array<{
|
|
176
|
+
sessionKey: string;
|
|
177
|
+
fromTurn: number;
|
|
178
|
+
toTurn: number;
|
|
179
|
+
memoryIds: readonly string[];
|
|
180
|
+
turns: ReadonlyArray<{ role: string; content: string }>;
|
|
181
|
+
}> = [];
|
|
182
|
+
for (const window of windows) {
|
|
183
|
+
if (signal.aborted) break;
|
|
184
|
+
let rows: LcmMessage[] = [];
|
|
185
|
+
for (const sessionId of candidateSessionIds(window.sessionKey)) {
|
|
186
|
+
try {
|
|
187
|
+
const fetched = await engine.getTurnRange(
|
|
188
|
+
sessionId,
|
|
189
|
+
window.fromTurn,
|
|
190
|
+
window.toTurn - 1,
|
|
191
|
+
);
|
|
192
|
+
if (fetched.length > 0) {
|
|
193
|
+
rows = fetched;
|
|
194
|
+
break;
|
|
195
|
+
}
|
|
196
|
+
} catch (err) {
|
|
197
|
+
// A failing session read skips that candidate only; the window
|
|
198
|
+
// is dropped when every candidate fails (fail-open).
|
|
199
|
+
log.debug(
|
|
200
|
+
`episodic-context: turn-range read failed for session ${sessionId}`,
|
|
201
|
+
err,
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
if (rows.length === 0) continue;
|
|
206
|
+
const turns = rows
|
|
207
|
+
.filter((m) => m.role === "user" || m.role === "assistant")
|
|
208
|
+
.map((m) => ({
|
|
209
|
+
role: m.role,
|
|
210
|
+
content:
|
|
211
|
+
m.role === "user"
|
|
212
|
+
? cleanArchivedUserMessage(m.content)
|
|
213
|
+
: m.content,
|
|
214
|
+
}))
|
|
215
|
+
.map((turn) => ({
|
|
216
|
+
...turn,
|
|
217
|
+
content:
|
|
218
|
+
turn.content.length > EPISODIC_TURN_CHAR_LIMIT
|
|
219
|
+
? `${turn.content.slice(0, EPISODIC_TURN_CHAR_LIMIT)}…`
|
|
220
|
+
: turn.content,
|
|
221
|
+
}));
|
|
222
|
+
if (turns.length === 0) continue;
|
|
223
|
+
episodes.push({
|
|
224
|
+
sessionKey: window.sessionKey,
|
|
225
|
+
fromTurn: window.fromTurn,
|
|
226
|
+
toTurn: window.toTurn,
|
|
227
|
+
memoryIds: window.memoryIds,
|
|
228
|
+
turns,
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
return episodes.length > 0
|
|
232
|
+
? deps.recallResultFormatter.formatEpisodicContext(episodes)
|
|
233
|
+
: null;
|
|
234
|
+
},
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
options.recordMetric({
|
|
238
|
+
section: "episodicContext",
|
|
239
|
+
priority: "enrichment",
|
|
240
|
+
durationMs: outcome.durationMs,
|
|
241
|
+
deadlineMs,
|
|
242
|
+
source: outcome.timedOut ? "timeout" : "fresh",
|
|
243
|
+
success: true,
|
|
244
|
+
...(outcome.timedOut
|
|
245
|
+
? { timing: `timeout(${Math.max(0, Math.round(outcome.durationMs))}ms)` }
|
|
246
|
+
: {}),
|
|
247
|
+
});
|
|
248
|
+
if (outcome.value === null) return;
|
|
249
|
+
const maxChars = deps.getRecallSectionMaxChars("episodic-context");
|
|
250
|
+
const body =
|
|
251
|
+
typeof maxChars === "number"
|
|
252
|
+
? deps.truncateRecallSectionToBudget(outcome.value, maxChars)
|
|
253
|
+
: outcome.value;
|
|
254
|
+
deps.appendRecallSection(options.sectionBuckets, "episodic-context", body);
|
|
255
|
+
}
|
|
@@ -13,6 +13,7 @@ import type { EvalShadowRecallRecord } from "../evals.js";
|
|
|
13
13
|
import type { HarmonicRetrievalResult } from "../harmonic-retrieval.js";
|
|
14
14
|
import type { StorageManager } from "../index.js";
|
|
15
15
|
import type { LcmEngine } from "../lcm/index.js";
|
|
16
|
+
import type { RecallResultFormatter } from "./recall-result-formatter.js";
|
|
16
17
|
import type { NamespaceCatalog } from "../namespaces/catalog.js";
|
|
17
18
|
import type { NamespaceStorageRouter } from "../namespaces/storage.js";
|
|
18
19
|
import type { ObjectiveStateSearchResult } from "../objective-state.js";
|
|
@@ -248,6 +249,7 @@ export interface RecallInternalDeps {
|
|
|
248
249
|
formatHarmonicRetrievalResults(
|
|
249
250
|
results: HarmonicRetrievalResult[],
|
|
250
251
|
): string;
|
|
252
|
+
readonly recallResultFormatter: RecallResultFormatter;
|
|
251
253
|
formatObjectiveStateResults(
|
|
252
254
|
results: ObjectiveStateSearchResult[],
|
|
253
255
|
): string;
|
|
@@ -70,9 +70,9 @@ import { isDisagreementPrompt } from "../signal.js";
|
|
|
70
70
|
import { buildTargetedFactRecallSection, shouldRecallTargetedFactEvidence } from "../targeted-fact-recall.js";
|
|
71
71
|
import { shouldFilterSupersededFromRecall } from "../temporal-supersession.js";
|
|
72
72
|
import { queryTemporalTimelineAsync } from "../temporal-index.js";
|
|
73
|
-
import { buildTemporalTimelineRecallSection, type TemporalTimelineRecallItem } from "../temporal-timeline-recall.js";
|
|
74
|
-
import { isValidAsOf } from "../temporal-validity.js";
|
|
75
73
|
import { type TrustStageResultItem, projectTrustForXray } from "../trust-score-stage.js";
|
|
74
|
+
import { isValidAsOf } from "../temporal-validity.js";
|
|
75
|
+
import { buildTemporalTimelineRecallSection, type TemporalTimelineRecallItem } from "../temporal-timeline-recall.js";
|
|
76
76
|
import { searchTrustZoneRecords } from "../trust-zones.js";
|
|
77
77
|
import type { IdentityInjectionMode, MemoryFile, QmdSearchResult, RecallPlanMode } from "../types.js";
|
|
78
78
|
import { type VerifiedEpisodeResult, compareVerifiedEpisodeResults, searchVerifiedEpisodes } from "../verified-recall.js";
|
|
@@ -105,6 +105,7 @@ import {
|
|
|
105
105
|
type RecallResultPartitionSink,
|
|
106
106
|
} from "./recall-rerank-coordinator.js";
|
|
107
107
|
import type { RecallInternalDeps } from "./recall-internal-deps.js";
|
|
108
|
+
import { appendEpisodicContextSection } from "./episodic-context-section.js";
|
|
108
109
|
import { resolveCompositeProfileStorage } from "./recall-profile-storage.js";
|
|
109
110
|
|
|
110
111
|
export class RecallInternalCoordinator {
|
|
@@ -469,29 +470,16 @@ export class RecallInternalCoordinator {
|
|
|
469
470
|
scopeProfilePlan,
|
|
470
471
|
lcmReadSessionIds,
|
|
471
472
|
} = scopePlan;
|
|
472
|
-
//
|
|
473
|
-
// FIRST non-empty result (#1505
|
|
474
|
-
//
|
|
475
|
-
//
|
|
476
|
-
//
|
|
477
|
-
//
|
|
478
|
-
//
|
|
479
|
-
//
|
|
480
|
-
//
|
|
481
|
-
//
|
|
482
|
-
// section builders take `sessionIds`, structured-parts merges inline below).
|
|
483
|
-
// This first-non-empty helper now serves ONLY the compressed-history section,
|
|
484
|
-
// which is a per-session HOLISTIC DAG narrative with no per-item id to merge or
|
|
485
|
-
// dedupe on — see its call site for the rationale.
|
|
486
|
-
//
|
|
487
|
-
// When the set is a single key (single-user / no-overlay / explicit-namespace),
|
|
488
|
-
// this is exactly one call — unchanged. `lcmSessionId` is `string | undefined`:
|
|
489
|
-
// a legacy SESSIONLESS recall yields the single `undefined` key so the read
|
|
490
|
-
// runs ONE archive-wide read with no `session_id` filter (pre-#1505 behavior).
|
|
491
|
-
// Hosted scope profiles are stricter: without a session key there is no
|
|
492
|
-
// namespace-scoped LCM key to query, so the key set stays empty and LCM cannot
|
|
493
|
-
// bypass the profile read stack via an archive-wide read. NEVER the literal
|
|
494
|
-
// "default" session id (codex P2).
|
|
473
|
+
// `firstNonEmptyLcmRead`: query the ordered LCM read key set, return the
|
|
474
|
+
// FIRST non-empty result (#1505). The query-SCORED sections no longer use
|
|
475
|
+
// it — they MERGE candidates across every authorized key (a weak primary
|
|
476
|
+
// hit must not mask stronger fallback evidence); this helper now serves
|
|
477
|
+
// only compressed-history, a holistic per-session DAG narrative with no
|
|
478
|
+
// per-item id to merge on. A single-key set is exactly one call. A legacy
|
|
479
|
+
// SESSIONLESS recall yields one `undefined` key: one archive-wide read,
|
|
480
|
+
// no `session_id` filter. Hosted scope profiles without a session key get
|
|
481
|
+
// an EMPTY key set, so LCM cannot bypass the profile read stack. NEVER
|
|
482
|
+
// the literal "default" session id (codex P2).
|
|
495
483
|
const firstNonEmptyLcmRead = async <T>(
|
|
496
484
|
read: (lcmSessionId: string | undefined) => Promise<T>,
|
|
497
485
|
isEmpty: (value: T) => boolean,
|
|
@@ -4926,6 +4914,32 @@ export class RecallInternalCoordinator {
|
|
|
4926
4914
|
recallPlan: timings.recallPlan,
|
|
4927
4915
|
queryPolicy: timings.queryPolicy,
|
|
4928
4916
|
});
|
|
4917
|
+
// Episodic context (#2331): opt-in raw-turn episodes for the top recalled
|
|
4918
|
+
// facts; flag off is a no-op so recall output stays byte-identical.
|
|
4919
|
+
// Namespaces must be index-aligned with recalledMemoryPaths here — every
|
|
4920
|
+
// recall branch populates xrayRecalledResults from the same array it
|
|
4921
|
+
// mapped paths from, so per-path namespaces derive from those results
|
|
4922
|
+
// (the assembly-time assignment below runs after this section).
|
|
4923
|
+
const episodicNamespaceByPath = new Map<string, string | undefined>();
|
|
4924
|
+
for (const result of xrayRecalledResults) {
|
|
4925
|
+
if (result.path) episodicNamespaceByPath.set(result.path, result.namespace);
|
|
4926
|
+
}
|
|
4927
|
+
const episodicMemoryNamespaces = recalledMemoryPaths.map((memoryPath) =>
|
|
4928
|
+
episodicNamespaceByPath.get(memoryPath),
|
|
4929
|
+
);
|
|
4930
|
+
// The section is advisory context: an archive error degrades the
|
|
4931
|
+
// section, never the recall (fail-open).
|
|
4932
|
+
try {
|
|
4933
|
+
await appendEpisodicContextSection(this.deps, {
|
|
4934
|
+
sectionBuckets, recalledMemoryPaths,
|
|
4935
|
+
recalledMemoryNamespaces: episodicMemoryNamespaces,
|
|
4936
|
+
scopePlan, namespacesEnabled,
|
|
4937
|
+
enrichmentSectionDeadlineMs, recallOuterTimeoutMs, recallStart,
|
|
4938
|
+
recordMetric: recordRecallSectionMetric, abortSignal: options.abortSignal,
|
|
4939
|
+
});
|
|
4940
|
+
} catch (err) {
|
|
4941
|
+
log.debug("episodic-context: section degraded after error", err);
|
|
4942
|
+
}
|
|
4929
4943
|
const recallBudgetChars = this.deps.getRecallBudgetChars(options.budgetCharsOverride);
|
|
4930
4944
|
const assembledRecall = this.deps.assembleRecallSections(
|
|
4931
4945
|
sectionBuckets, contextBudgetForFooter(recallBudgetChars, curiosityFooter),
|
|
@@ -5326,4 +5340,5 @@ export class RecallInternalCoordinator {
|
|
|
5326
5340
|
closeProfileTrace();
|
|
5327
5341
|
}
|
|
5328
5342
|
}
|
|
5343
|
+
|
|
5329
5344
|
}
|
|
@@ -384,6 +384,43 @@ export class RecallResultFormatter {
|
|
|
384
384
|
return `## Harmonic Retrieval\n\n${lines.join("\n\n")}`;
|
|
385
385
|
}
|
|
386
386
|
|
|
387
|
+
/**
|
|
388
|
+
* Render grouped raw-turn episodes for the top recalled facts (issue #2331).
|
|
389
|
+
* Returns `null` for zero episodes so the section is omitted entirely —
|
|
390
|
+
* never an empty header.
|
|
391
|
+
*/
|
|
392
|
+
formatEpisodicContext(
|
|
393
|
+
episodes: ReadonlyArray<{
|
|
394
|
+
sessionKey: string;
|
|
395
|
+
fromTurn: number;
|
|
396
|
+
toTurn: number;
|
|
397
|
+
memoryIds: readonly string[];
|
|
398
|
+
turns: ReadonlyArray<{ role: string; content: string }>;
|
|
399
|
+
}>,
|
|
400
|
+
): string | null {
|
|
401
|
+
if (episodes.length === 0) return null;
|
|
402
|
+
const blocks = episodes.map((episode) => {
|
|
403
|
+
const shortKey =
|
|
404
|
+
episode.sessionKey.length > 8
|
|
405
|
+
? `${episode.sessionKey.slice(0, 8)}…`
|
|
406
|
+
: episode.sessionKey;
|
|
407
|
+
const header =
|
|
408
|
+
`### Episode: ${shortKey} turns ${episode.fromTurn}-${episode.toTurn - 1} ` +
|
|
409
|
+
`(supports [${episode.memoryIds.join(", ")}])`;
|
|
410
|
+
const lines = episode.turns.map((turn) => {
|
|
411
|
+
// Archived conversation turns carry no origin: least-privilege
|
|
412
|
+
// fencing applies when origin authority is enabled (issue #2331).
|
|
413
|
+
const content = renderAuthorityBoundContent(turn.content, undefined, {
|
|
414
|
+
enabled: this.originAuthorityEnabled,
|
|
415
|
+
untrustedOrigins: this.config.untrustedOrigins,
|
|
416
|
+
});
|
|
417
|
+
return `${turn.role}: ${content}`;
|
|
418
|
+
});
|
|
419
|
+
return [header, ...lines].join("\n");
|
|
420
|
+
});
|
|
421
|
+
return `## Source Episodes\n\n${blocks.join("\n\n")}`;
|
|
422
|
+
}
|
|
423
|
+
|
|
387
424
|
formatWorkProductResults(
|
|
388
425
|
results: WorkProductLedgerSearchResult[],
|
|
389
426
|
): string {
|