@remnic/core 9.57.5 → 9.58.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/access-admin-ops-surface.d.ts +12 -11
- package/dist/access-admin-ops-surface.js +25 -24
- package/dist/access-authorization-probe.d.ts +12 -11
- package/dist/access-authorization-probe.js +26 -25
- package/dist/access-boundary.d.ts +12 -11
- package/dist/access-boundary.js +25 -24
- package/dist/access-cli.js +77 -76
- 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 +12 -11
- package/dist/access-extraction-force-flush.js +25 -24
- package/dist/access-health-types.d.ts +1 -1
- package/dist/access-http-lcm-compaction.d.ts +12 -11
- package/dist/access-http-lifecycle-flush.d.ts +12 -11
- package/dist/access-http-offline-stream.d.ts +12 -11
- package/dist/access-http-query.js +25 -24
- package/dist/access-http.d.ts +13 -12
- package/dist/access-http.js +36 -35
- package/dist/access-identity-continuity-surface.d.ts +10 -9
- package/dist/access-identity-continuity-surface.js +25 -24
- package/dist/access-lcm-surface.d.ts +12 -11
- package/dist/access-lcm-surface.js +25 -24
- package/dist/access-mcp.d.ts +12 -11
- package/dist/access-mcp.js +32 -31
- package/dist/access-memory-search-fanout.d.ts +2 -2
- package/dist/{access-namespace-preflight-Bpt-SYP0.d.ts → access-namespace-preflight-TBXz-8s_.d.ts} +1 -1
- package/dist/access-namespace-preflight.d.ts +3 -3
- package/dist/access-namespace-preflight.js +25 -24
- package/dist/access-observe-write-surface.d.ts +12 -11
- package/dist/access-observe-write-surface.js +25 -24
- package/dist/access-offline-manifest.d.ts +10 -9
- package/dist/access-operations-batch.js +27 -26
- package/dist/access-operations.d.ts +15 -14
- package/dist/access-operations.js +31 -30
- package/dist/access-recall-concurrency.d.ts +12 -11
- package/dist/access-recall-concurrency.js +25 -24
- package/dist/access-recall-response.d.ts +12 -11
- package/dist/access-recall-response.js +25 -24
- package/dist/access-recall-surface.d.ts +12 -11
- package/dist/access-recall-surface.js +25 -24
- package/dist/access-schema.d.ts +88 -88
- package/dist/access-schema.js +3 -3
- package/dist/{access-service-D9xXmKvd.d.ts → access-service-BQkRul4d.d.ts} +71 -71
- package/dist/access-service-helpers.d.ts +12 -11
- package/dist/access-service.d.ts +12 -11
- package/dist/access-service.js +25 -24
- package/dist/access-surface-catalog.d.ts +12 -11
- package/dist/access-surface-catalog.js +27 -26
- package/dist/access-surface-catalog.js.map +1 -1
- 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/behavior-learner.d.ts +1 -1
- package/dist/behavior-signals.d.ts +1 -1
- package/dist/bootstrap.d.ts +10 -9
- package/dist/briefing-window.d.ts +2 -2
- package/dist/briefing.d.ts +2 -2
- package/dist/briefing.js +10 -10
- package/dist/buffer-surprise-report.d.ts +1 -1
- package/dist/buffer-surprise.js +4 -3
- 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/{capsule-crypto-YO5QJ6L3.js → capsule-crypto-GWVG7LGC.js} +2 -2
- package/dist/{catalog-BasnO3Bw.d.ts → catalog-z1UerMbh.d.ts} +1 -1
- package/dist/causal-behavior.d.ts +2 -1
- package/dist/causal-consolidation.d.ts +1 -1
- package/dist/causal-consolidation.js +14 -13
- package/dist/causal-consolidation.js.map +1 -1
- package/dist/causal-trajectory-graph.d.ts +1 -1
- package/dist/{chunk-P4BC54KI.js → chunk-2GBFKHBZ.js} +14 -38
- package/dist/chunk-2GBFKHBZ.js.map +1 -0
- package/dist/{chunk-YTZA7U6Q.js → chunk-2VHCLHZ5.js} +3 -3
- package/dist/{chunk-NEJQ65NV.js → chunk-4CLE2LXB.js} +2 -2
- package/dist/{chunk-3VFVQDZO.js → chunk-4IIKJOYM.js} +1 -1
- package/dist/{chunk-3VFVQDZO.js.map → chunk-4IIKJOYM.js.map} +1 -1
- package/dist/{chunk-JLNBQWZ2.js → chunk-5JMAGU26.js} +7 -6
- package/dist/{chunk-JLNBQWZ2.js.map → chunk-5JMAGU26.js.map} +1 -1
- package/dist/{chunk-VDX2J7OX.js → chunk-5W7CTZ2E.js} +4 -4
- package/dist/{chunk-CZUHNJHX.js → chunk-6C7Q6NG3.js} +13 -13
- package/dist/chunk-6TBIWIAG.js +201 -0
- package/dist/chunk-6TBIWIAG.js.map +1 -0
- package/dist/{chunk-4O6R3GPK.js → chunk-6W2ISYUV.js} +4 -4
- package/dist/{chunk-3AJOJOFP.js → chunk-77DAFTUU.js} +3 -3
- package/dist/{chunk-IKT6ZVO7.js → chunk-7DHFB5SG.js} +3 -3
- package/dist/{chunk-SFR7J2G6.js → chunk-7G34CBKS.js} +2 -2
- package/dist/{chunk-IT57AOMC.js → chunk-AH2NX6S7.js} +3 -3
- package/dist/{chunk-HYQJQGD3.js → chunk-AM4T6CHX.js} +49 -35
- package/dist/chunk-AM4T6CHX.js.map +1 -0
- package/dist/{chunk-Z54EM5YZ.js → chunk-B2QHVLJO.js} +60 -30
- package/dist/chunk-B2QHVLJO.js.map +1 -0
- package/dist/{chunk-FI3PIOOI.js → chunk-B4CSPFYN.js} +8 -8
- package/dist/{chunk-7PJARJS4.js → chunk-BPM6UHUL.js} +2 -2
- package/dist/{chunk-PPUTUOC3.js → chunk-BTHDQOF4.js} +5 -5
- package/dist/{chunk-AW4A4XOP.js → chunk-CZGOLAEW.js} +41 -41
- package/dist/{chunk-5PGYCWA7.js → chunk-DE6YCZEE.js} +2 -2
- package/dist/{chunk-JOA4Q765.js → chunk-EUYWQ7TS.js} +2 -2
- package/dist/{chunk-O5AFC5KM.js → chunk-EWL5DICB.js} +2 -2
- package/dist/{chunk-4YZCIK5E.js → chunk-EZXGJJXM.js} +2 -2
- package/dist/{chunk-GXIY65UH.js → chunk-FI5EAP4N.js} +2 -2
- package/dist/{chunk-YOKSRIFI.js → chunk-FUWS2MNJ.js} +4 -4
- package/dist/{chunk-6RK2X7BR.js → chunk-G3JFFUKR.js} +2 -2
- package/dist/{chunk-VANZKHTN.js → chunk-G5OB4XZX.js} +4 -4
- package/dist/{chunk-66SLUXKM.js → chunk-HK6KYIF6.js} +2 -2
- package/dist/{chunk-XBGLEOID.js → chunk-I4BLRM7X.js} +9 -9
- package/dist/{chunk-5ZIWRMZM.js → chunk-IVKIODHO.js} +7 -6
- package/dist/chunk-IVKIODHO.js.map +1 -0
- package/dist/{chunk-6SX4MY5Q.js → chunk-J65AKOD6.js} +2 -2
- package/dist/{chunk-BPWZDHFM.js → chunk-J6QIAYNR.js} +2 -2
- package/dist/{chunk-XL3TATMJ.js → chunk-JHMAYABJ.js} +4 -4
- package/dist/{chunk-F7M36OG2.js → chunk-JNWISMYG.js} +10 -10
- package/dist/{chunk-VYIXCMUP.js → chunk-KBIIE57I.js} +3 -3
- package/dist/{chunk-QKPDN325.js → chunk-KF75ANST.js} +6 -3
- package/dist/chunk-KF75ANST.js.map +1 -0
- package/dist/{chunk-24PNX2GF.js → chunk-KXYMU4IN.js} +3 -3
- package/dist/{chunk-FKTD7IVS.js → chunk-L3JVAM5Q.js} +2 -2
- package/dist/{chunk-5QHDXMXP.js → chunk-LLZKOSGM.js} +1 -1
- package/dist/{chunk-5QHDXMXP.js.map → chunk-LLZKOSGM.js.map} +1 -1
- package/dist/{chunk-LIF6DO6U.js → chunk-MG7GI4TT.js} +2 -2
- package/dist/{chunk-RUZM6Z52.js → chunk-MMZK2DWN.js} +1 -1
- package/dist/{chunk-NTJ6TY44.js → chunk-ODAE36PG.js} +3 -3
- package/dist/{chunk-QFN4U7AG.js → chunk-ODZKV2F7.js} +3 -3
- package/dist/{chunk-QFN4U7AG.js.map → chunk-ODZKV2F7.js.map} +1 -1
- package/dist/{chunk-YLHM5BQQ.js → chunk-OLX2HJFW.js} +4 -4
- package/dist/{chunk-DNC4A7RS.js → chunk-OU7RWWW6.js} +2 -2
- package/dist/{chunk-L3LRWDJW.js → chunk-OV36CROW.js} +3 -3
- package/dist/{chunk-BXLOS5AJ.js → chunk-OWHERGF2.js} +2 -2
- package/dist/{chunk-CSV34HIW.js → chunk-P5OYNV7B.js} +2 -2
- package/dist/{chunk-SNW3YRO6.js → chunk-P6GOYLHN.js} +2 -2
- package/dist/{chunk-C5B3CIMJ.js → chunk-PFPNUFKZ.js} +109 -59
- package/dist/chunk-PFPNUFKZ.js.map +1 -0
- package/dist/{chunk-USGKHF36.js → chunk-QUJC22CT.js} +2 -2
- package/dist/{chunk-BVRREV3N.js → chunk-R7IV3EUJ.js} +2 -2
- package/dist/{chunk-KSXJOC4F.js → chunk-RDFF2NAU.js} +18 -8
- package/dist/chunk-RDFF2NAU.js.map +1 -0
- package/dist/{chunk-D4LVHPOA.js → chunk-RXN7Z2V5.js} +3 -3
- package/dist/{chunk-7VSHDLJQ.js → chunk-T2VJ2EO5.js} +6 -6
- package/dist/chunk-TOTT6S2V.js +20 -0
- package/dist/chunk-TOTT6S2V.js.map +1 -0
- package/dist/{chunk-U7BMN33M.js → chunk-UBSM6TZP.js} +2 -2
- package/dist/{chunk-DQEMWVMT.js → chunk-UVYI6VIX.js} +1 -1
- package/dist/{chunk-U7XLOSG2.js → chunk-VIM6QH4D.js} +2 -2
- package/dist/{chunk-LJJWFV2Q.js → chunk-VJXSAJFW.js} +2 -2
- package/dist/{chunk-HZC3OECF.js → chunk-VOKK4WCG.js} +2 -2
- package/dist/{chunk-LTAVSOZW.js → chunk-WVP2I6C5.js} +5 -4
- package/dist/chunk-WVP2I6C5.js.map +1 -0
- package/dist/{chunk-LRDP2AC2.js → chunk-YBCZTBN2.js} +2 -2
- package/dist/{chunk-USAUBGBC.js → chunk-YKZFX2HQ.js} +2 -2
- package/dist/{chunk-I2GIYTRU.js → chunk-ZOGOOSB3.js} +2 -2
- package/dist/{chunk-PBGWYM3H.js → chunk-ZPUIRTIZ.js} +27 -15
- package/dist/chunk-ZPUIRTIZ.js.map +1 -0
- package/dist/chunking.d.ts +3 -1
- package/dist/chunking.js +8 -3
- package/dist/{cli-DTOkYhKI.d.ts → cli-DsyVvOFP.d.ts} +5 -5
- package/dist/cli.d.ts +14 -13
- package/dist/cli.js +57 -56
- package/dist/coding/pre-action-gate.d.ts +1 -1
- package/dist/compounding/engine.d.ts +2 -2
- package/dist/compounding/engine.js +10 -10
- package/dist/compounding/preference-consolidator.d.ts +2 -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 +12 -11
- package/dist/connectors/codex-materialize.d.ts +7 -12
- package/dist/connectors/codex-materialize.js +2 -1
- package/dist/connectors/index.d.ts +1 -1
- package/dist/connectors/index.js +12 -11
- 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/contradiction/index.js +4 -4
- package/dist/converge-config.d.ts +1 -1
- package/dist/convergence-refresh.d.ts +2 -2
- package/dist/convergence-refresh.js +12 -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 +12 -12
- package/dist/day-summary.d.ts +1 -1
- package/dist/day-summary.js +3 -2
- package/dist/delinearize.d.ts +1 -1
- package/dist/dependency-propagation-config.d.ts +1 -1
- package/dist/{dependency-propagation-delivery-C_K_AHcv.d.ts → dependency-propagation-delivery-DdwSc1qh.d.ts} +1 -1
- package/dist/direct-answer-wiring.d.ts +1 -1
- package/dist/direct-answer.d.ts +1 -1
- package/dist/embedding-fallback.d.ts +1 -1
- package/dist/enrichment/index.d.ts +1 -1
- package/dist/entity-retrieval.d.ts +2 -2
- package/dist/entity-retrieval.js +10 -10
- package/dist/entity-schema.d.ts +1 -1
- package/dist/explicit-capture.d.ts +10 -9
- package/dist/external-wiki-access.d.ts +12 -11
- package/dist/external-wiki-access.js +26 -25
- 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 +12 -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-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 +16 -15
- package/dist/fallback-llm.d.ts +1 -1
- 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-retrieval.js +1 -1
- package/dist/importance.d.ts +1 -1
- package/dist/importers/index.d.ts +1 -1
- package/dist/in-flight-reads.d.ts +1 -1
- package/dist/index.d.ts +698 -697
- package/dist/index.js +81 -80
- package/dist/intent.d.ts +1 -1
- package/dist/lcm/archive.d.ts +3 -2
- package/dist/lcm/archive.js +2 -1
- package/dist/lcm/engine.d.ts +2 -1
- package/dist/lcm/engine.js +5 -4
- package/dist/lcm/index.d.ts +2 -1
- package/dist/lcm/index.js +7 -6
- package/dist/lcm/recall.d.ts +1 -0
- package/dist/lcm/summarizer.d.ts +1 -0
- package/dist/lcm/summarizer.js +3 -2
- package/dist/lcm/tools.d.ts +2 -1
- package/dist/lifecycle.d.ts +1 -1
- package/dist/live-connectors-runner.d.ts +1 -1
- package/dist/local-llm.d.ts +2 -3
- package/dist/local-llm.js +2 -1
- package/dist/local-model-endpoint.d.ts +1 -1
- package/dist/maintenance/memory-governance.d.ts +1 -1
- package/dist/maintenance/memory-governance.js +10 -10
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.d.ts +2 -2
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +10 -10
- package/dist/maintenance/rebuild-memory-projection.d.ts +2 -2
- package/dist/maintenance/rebuild-memory-projection.js +11 -11
- package/dist/{maintenance-DdP931i9.d.ts → maintenance-DvkiF7fm.d.ts} +3 -3
- package/dist/mcp-memory-inspector-app.d.ts +12 -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 +11 -11
- package/dist/namespaces/principal.d.ts +1 -1
- package/dist/namespaces/search.d.ts +1 -1
- package/dist/namespaces/search.js +7 -7
- package/dist/namespaces/storage.d.ts +3 -3
- package/dist/namespaces/storage.js +10 -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 +13 -13
- package/dist/operator-doctor-replica.d.ts +1 -1
- package/dist/operator-toolkit.d.ts +3 -3
- package/dist/operator-toolkit.js +18 -18
- package/dist/orchestration/compression-guideline-coordinator.d.ts +2 -2
- package/dist/orchestration/maintenance.d.ts +4 -4
- package/dist/orchestration/maintenance.js +15 -15
- package/dist/{orchestrator-DxUWX7gs.d.ts → orchestrator-CXPIzs3D.d.ts} +9 -9
- package/dist/orchestrator.d.ts +10 -9
- package/dist/orchestrator.js +77 -76
- package/dist/patterns-cli.d.ts +1 -1
- package/dist/{pipeline-BKUMU1vU.d.ts → pipeline-BuYWU9rt.d.ts} +1 -1
- package/dist/policy-runtime.d.ts +1 -1
- package/dist/proactive-contention.d.ts +1 -1
- package/dist/provenance.d.ts +1 -1
- package/dist/{public-http-C01DLhAp.d.ts → public-http-m9x1Qyzn.d.ts} +1 -1
- package/dist/{qmd-DJP-GLFe.d.ts → qmd-DYnrPw-m.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 +4 -3
- package/dist/recall-memory-map.d.ts +1 -1
- package/dist/recall-planner-llm.d.ts +1 -1
- package/dist/recall-state.d.ts +1 -1
- package/dist/recall-tag-filter.d.ts +1 -1
- package/dist/recall-timings.d.ts +1 -1
- package/dist/recall-xray-cli.d.ts +1 -1
- package/dist/recall-xray-cli.js +5 -4
- package/dist/recall-xray-renderer.d.ts +1 -1
- package/dist/recall-xray-renderer.js +4 -3
- package/dist/recall-xray.d.ts +2 -7
- package/dist/recall-xray.js +3 -2
- package/dist/reconcile/cursor.d.ts +1 -1
- package/dist/reconcile/manifest.d.ts +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 -3
- package/dist/retrieval-agents.d.ts +2 -2
- package/dist/retrieval-tiers.d.ts +1 -1
- package/dist/routing/engine.d.ts +1 -1
- package/dist/routing/store.d.ts +1 -1
- package/dist/salvage-envelope.d.ts +1 -1
- package/dist/schemas.d.ts +138 -138
- package/dist/{scope-profiles-CaKcnB-6.d.ts → scope-profiles-C1QG4Cmv.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 +6 -6
- package/dist/search/index.d.ts +1 -1
- package/dist/search/index.js +11 -11
- package/dist/search/lancedb-backend.d.ts +1 -1
- package/dist/search/lancedb-backend.js +2 -2
- package/dist/search/meilisearch-backend.d.ts +1 -1
- package/dist/search/meilisearch-backend.js +2 -2
- package/dist/search/noop-backend.d.ts +1 -1
- package/dist/search/orama-backend.d.ts +1 -1
- package/dist/search/orama-backend.js +2 -2
- package/dist/search/port.d.ts +1 -1
- package/dist/search/remote-backend.d.ts +1 -1
- package/dist/semantic-chunking.js +3 -2
- package/dist/{semantic-consolidation-P0eMWqh-.d.ts → semantic-consolidation-DMeX8XS0.d.ts} +1 -1
- package/dist/semantic-consolidation.d.ts +2 -2
- package/dist/semantic-consolidation.js +14 -13
- package/dist/semantic-rule-promotion.js +10 -10
- package/dist/semantic-rule-verifier.d.ts +1 -1
- package/dist/semantic-rule-verifier.js +10 -10
- package/dist/{service-D5GQLxq_.d.ts → service-CT3ENiTO.d.ts} +2 -2
- package/dist/session-observer-bands.d.ts +1 -1
- package/dist/session-observer-state.d.ts +1 -1
- package/dist/session-observer-state.js +1 -1
- package/dist/shared-context/manager.d.ts +9 -9
- package/dist/signal.d.ts +1 -1
- package/dist/source-agent-qualifier.d.ts +1 -1
- package/dist/source-agent-qualifier.js +12 -11
- package/dist/{storage-DIyil2ll.d.ts → storage-B0L76y7N.d.ts} +1 -1
- package/dist/storage.d.ts +2 -2
- package/dist/storage.js +9 -9
- package/dist/summarizer.d.ts +1 -1
- package/dist/summarizer.js +3 -2
- package/dist/summary-snapshot.d.ts +1 -1
- package/dist/support-passport/index.d.ts +15 -14
- package/dist/support-passport/index.js +28 -27
- 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/token-estimate.d.ts +10 -0
- package/dist/token-estimate.js +8 -0
- package/dist/token-estimate.js.map +1 -0
- package/dist/topics.d.ts +1 -1
- package/dist/transcript.d.ts +1 -6
- package/dist/transcript.js +2 -1
- package/dist/transfer/backup.js +2 -2
- package/dist/transfer/capsule-export.js +2 -2
- package/dist/transfer/capsule-import.js +2 -2
- package/dist/transfer/types.d.ts +66 -66
- package/dist/trust-score-stage.d.ts +1 -1
- package/dist/trust-score.d.ts +1 -1
- package/dist/{types-CbXieHKr.d.ts → types-D7pB0QwO.d.ts} +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.js +1 -1
- package/dist/utility-runtime.d.ts +1 -1
- package/dist/verified-recall.js +10 -10
- package/dist/write-envelope.d.ts +1 -1
- package/package.json +12 -2
- package/src/chunking.ts +124 -45
- package/src/config.ts +2 -0
- package/src/connectors/codex-materialize.ts +21 -26
- package/src/lcm/archive.ts +4 -5
- package/src/lcm/engine.ts +55 -34
- package/src/lcm/summarizer.ts +27 -16
- package/src/lcm-engine.test.ts +82 -2
- package/src/local-llm.ts +4 -6
- package/src/memory-extension-host/render-extensions-block.ts +3 -9
- package/src/orchestration/recall-section-coordinator.ts +93 -27
- package/src/recall-token-budget.test.ts +51 -0
- package/src/recall-xray.ts +4 -11
- package/src/semantic-chunking.ts +14 -59
- package/src/session-observer-state.ts +3 -3
- package/src/testing/subjects/recall-budget.test.ts +21 -5
- package/src/token-estimate.test.ts +38 -0
- package/src/token-estimate.ts +22 -0
- package/src/transcript.test.ts +38 -3
- package/src/transcript.ts +58 -59
- package/src/types.ts +1 -1
- package/dist/chunk-5ZIWRMZM.js.map +0 -1
- package/dist/chunk-C5B3CIMJ.js.map +0 -1
- package/dist/chunk-HYQJQGD3.js.map +0 -1
- package/dist/chunk-KSXJOC4F.js.map +0 -1
- package/dist/chunk-LTAVSOZW.js.map +0 -1
- package/dist/chunk-P4BC54KI.js.map +0 -1
- package/dist/chunk-PBGWYM3H.js.map +0 -1
- package/dist/chunk-QKPDN325.js.map +0 -1
- package/dist/chunk-UQ7RN5HK.js +0 -126
- package/dist/chunk-UQ7RN5HK.js.map +0 -1
- package/dist/chunk-Z54EM5YZ.js.map +0 -1
- /package/dist/{capsule-crypto-YO5QJ6L3.js.map → capsule-crypto-GWVG7LGC.js.map} +0 -0
- /package/dist/{chunk-YTZA7U6Q.js.map → chunk-2VHCLHZ5.js.map} +0 -0
- /package/dist/{chunk-NEJQ65NV.js.map → chunk-4CLE2LXB.js.map} +0 -0
- /package/dist/{chunk-VDX2J7OX.js.map → chunk-5W7CTZ2E.js.map} +0 -0
- /package/dist/{chunk-CZUHNJHX.js.map → chunk-6C7Q6NG3.js.map} +0 -0
- /package/dist/{chunk-4O6R3GPK.js.map → chunk-6W2ISYUV.js.map} +0 -0
- /package/dist/{chunk-3AJOJOFP.js.map → chunk-77DAFTUU.js.map} +0 -0
- /package/dist/{chunk-IKT6ZVO7.js.map → chunk-7DHFB5SG.js.map} +0 -0
- /package/dist/{chunk-SFR7J2G6.js.map → chunk-7G34CBKS.js.map} +0 -0
- /package/dist/{chunk-IT57AOMC.js.map → chunk-AH2NX6S7.js.map} +0 -0
- /package/dist/{chunk-FI3PIOOI.js.map → chunk-B4CSPFYN.js.map} +0 -0
- /package/dist/{chunk-7PJARJS4.js.map → chunk-BPM6UHUL.js.map} +0 -0
- /package/dist/{chunk-PPUTUOC3.js.map → chunk-BTHDQOF4.js.map} +0 -0
- /package/dist/{chunk-AW4A4XOP.js.map → chunk-CZGOLAEW.js.map} +0 -0
- /package/dist/{chunk-5PGYCWA7.js.map → chunk-DE6YCZEE.js.map} +0 -0
- /package/dist/{chunk-JOA4Q765.js.map → chunk-EUYWQ7TS.js.map} +0 -0
- /package/dist/{chunk-O5AFC5KM.js.map → chunk-EWL5DICB.js.map} +0 -0
- /package/dist/{chunk-4YZCIK5E.js.map → chunk-EZXGJJXM.js.map} +0 -0
- /package/dist/{chunk-GXIY65UH.js.map → chunk-FI5EAP4N.js.map} +0 -0
- /package/dist/{chunk-YOKSRIFI.js.map → chunk-FUWS2MNJ.js.map} +0 -0
- /package/dist/{chunk-6RK2X7BR.js.map → chunk-G3JFFUKR.js.map} +0 -0
- /package/dist/{chunk-VANZKHTN.js.map → chunk-G5OB4XZX.js.map} +0 -0
- /package/dist/{chunk-66SLUXKM.js.map → chunk-HK6KYIF6.js.map} +0 -0
- /package/dist/{chunk-XBGLEOID.js.map → chunk-I4BLRM7X.js.map} +0 -0
- /package/dist/{chunk-6SX4MY5Q.js.map → chunk-J65AKOD6.js.map} +0 -0
- /package/dist/{chunk-BPWZDHFM.js.map → chunk-J6QIAYNR.js.map} +0 -0
- /package/dist/{chunk-XL3TATMJ.js.map → chunk-JHMAYABJ.js.map} +0 -0
- /package/dist/{chunk-F7M36OG2.js.map → chunk-JNWISMYG.js.map} +0 -0
- /package/dist/{chunk-VYIXCMUP.js.map → chunk-KBIIE57I.js.map} +0 -0
- /package/dist/{chunk-24PNX2GF.js.map → chunk-KXYMU4IN.js.map} +0 -0
- /package/dist/{chunk-FKTD7IVS.js.map → chunk-L3JVAM5Q.js.map} +0 -0
- /package/dist/{chunk-LIF6DO6U.js.map → chunk-MG7GI4TT.js.map} +0 -0
- /package/dist/{chunk-RUZM6Z52.js.map → chunk-MMZK2DWN.js.map} +0 -0
- /package/dist/{chunk-NTJ6TY44.js.map → chunk-ODAE36PG.js.map} +0 -0
- /package/dist/{chunk-YLHM5BQQ.js.map → chunk-OLX2HJFW.js.map} +0 -0
- /package/dist/{chunk-DNC4A7RS.js.map → chunk-OU7RWWW6.js.map} +0 -0
- /package/dist/{chunk-L3LRWDJW.js.map → chunk-OV36CROW.js.map} +0 -0
- /package/dist/{chunk-BXLOS5AJ.js.map → chunk-OWHERGF2.js.map} +0 -0
- /package/dist/{chunk-CSV34HIW.js.map → chunk-P5OYNV7B.js.map} +0 -0
- /package/dist/{chunk-SNW3YRO6.js.map → chunk-P6GOYLHN.js.map} +0 -0
- /package/dist/{chunk-USGKHF36.js.map → chunk-QUJC22CT.js.map} +0 -0
- /package/dist/{chunk-BVRREV3N.js.map → chunk-R7IV3EUJ.js.map} +0 -0
- /package/dist/{chunk-D4LVHPOA.js.map → chunk-RXN7Z2V5.js.map} +0 -0
- /package/dist/{chunk-7VSHDLJQ.js.map → chunk-T2VJ2EO5.js.map} +0 -0
- /package/dist/{chunk-U7BMN33M.js.map → chunk-UBSM6TZP.js.map} +0 -0
- /package/dist/{chunk-DQEMWVMT.js.map → chunk-UVYI6VIX.js.map} +0 -0
- /package/dist/{chunk-U7XLOSG2.js.map → chunk-VIM6QH4D.js.map} +0 -0
- /package/dist/{chunk-LJJWFV2Q.js.map → chunk-VJXSAJFW.js.map} +0 -0
- /package/dist/{chunk-HZC3OECF.js.map → chunk-VOKK4WCG.js.map} +0 -0
- /package/dist/{chunk-LRDP2AC2.js.map → chunk-YBCZTBN2.js.map} +0 -0
- /package/dist/{chunk-USAUBGBC.js.map → chunk-YKZFX2HQ.js.map} +0 -0
- /package/dist/{chunk-I2GIYTRU.js.map → chunk-ZOGOOSB3.js.map} +0 -0
package/src/lcm-engine.test.ts
CHANGED
|
@@ -7,6 +7,7 @@ import test from "node:test";
|
|
|
7
7
|
import { LcmArchive } from "./lcm/archive.js";
|
|
8
8
|
import { LcmEngine, extractLcmConfig } from "./lcm/engine.js";
|
|
9
9
|
import { openLcmDatabase } from "./lcm/schema.js";
|
|
10
|
+
import { estimateTokenCount } from "./token-estimate.js";
|
|
10
11
|
import type { PluginConfig } from "./types.js";
|
|
11
12
|
|
|
12
13
|
function createPluginConfig(memoryDir: string): PluginConfig {
|
|
@@ -661,20 +662,99 @@ test("expandContext preserves first, middle, and last row identity when truncate
|
|
|
661
662
|
session_id: "session-truncated",
|
|
662
663
|
turn_index: 2,
|
|
663
664
|
role: "assistant",
|
|
664
|
-
content: "
|
|
665
|
+
content: "BBBBBBBB",
|
|
665
666
|
},
|
|
666
667
|
{
|
|
667
668
|
id: last.id,
|
|
668
669
|
session_id: "session-truncated",
|
|
669
670
|
turn_index: 4,
|
|
670
671
|
role: "assistant",
|
|
671
|
-
content: "
|
|
672
|
+
content: "DDDD",
|
|
672
673
|
},
|
|
673
674
|
]);
|
|
674
675
|
} finally {
|
|
675
676
|
await rm(memoryDir, { recursive: true, force: true });
|
|
676
677
|
}
|
|
677
678
|
});
|
|
679
|
+
test("expandContext applies token budgets to wide-script messages", async () => {
|
|
680
|
+
const memoryDir = await mkdtemp(
|
|
681
|
+
path.join(os.tmpdir(), "engram-lcm-expand-wide-script-"),
|
|
682
|
+
);
|
|
683
|
+
|
|
684
|
+
try {
|
|
685
|
+
const engine = new LcmEngine(
|
|
686
|
+
createPluginConfig(memoryDir),
|
|
687
|
+
async () => "summary",
|
|
688
|
+
);
|
|
689
|
+
await engine.ensureInitialized();
|
|
690
|
+
|
|
691
|
+
const db = openLcmDatabase(memoryDir);
|
|
692
|
+
try {
|
|
693
|
+
const archive = new LcmArchive(db);
|
|
694
|
+
archive.appendMessages("session-wide-script", [
|
|
695
|
+
{ turnIndex: 1, role: "user", content: "日本語".repeat(40) },
|
|
696
|
+
{ turnIndex: 2, role: "assistant", content: "日本語".repeat(40) },
|
|
697
|
+
{ turnIndex: 3, role: "user", content: "日本語".repeat(40) },
|
|
698
|
+
]);
|
|
699
|
+
} finally {
|
|
700
|
+
db.close();
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
const expanded = await engine.expandContext(
|
|
704
|
+
"session-wide-script",
|
|
705
|
+
1,
|
|
706
|
+
3,
|
|
707
|
+
10,
|
|
708
|
+
);
|
|
709
|
+
|
|
710
|
+
assert.ok(
|
|
711
|
+
expanded.reduce((sum, message) => sum + estimateTokenCount(message.content), 0) <= 10,
|
|
712
|
+
);
|
|
713
|
+
assert.ok(expanded.every((message) => message.content.length < 40));
|
|
714
|
+
} finally {
|
|
715
|
+
await rm(memoryDir, { recursive: true, force: true });
|
|
716
|
+
}
|
|
717
|
+
});
|
|
718
|
+
test("expandContext preserves the final message at the smallest positive budget", async () => {
|
|
719
|
+
const memoryDir = await mkdtemp(
|
|
720
|
+
path.join(os.tmpdir(), "engram-lcm-expand-minimum-budget-"),
|
|
721
|
+
);
|
|
722
|
+
|
|
723
|
+
let engine: LcmEngine | undefined;
|
|
724
|
+
try {
|
|
725
|
+
engine = new LcmEngine(
|
|
726
|
+
createPluginConfig(memoryDir),
|
|
727
|
+
async () => "summary",
|
|
728
|
+
);
|
|
729
|
+
await engine.ensureInitialized();
|
|
730
|
+
|
|
731
|
+
const db = openLcmDatabase(memoryDir);
|
|
732
|
+
try {
|
|
733
|
+
const archive = new LcmArchive(db);
|
|
734
|
+
archive.appendMessages("session-minimum-budget", [
|
|
735
|
+
{ turnIndex: 1, role: "user", content: "first message" },
|
|
736
|
+
{ turnIndex: 2, role: "assistant", content: "最後のメッセージ" },
|
|
737
|
+
]);
|
|
738
|
+
} finally {
|
|
739
|
+
db.close();
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
const expanded = await engine.expandContext(
|
|
743
|
+
"session-minimum-budget",
|
|
744
|
+
1,
|
|
745
|
+
2,
|
|
746
|
+
1,
|
|
747
|
+
);
|
|
748
|
+
|
|
749
|
+
assert.equal(expanded.at(-1)?.turn_index, 2);
|
|
750
|
+
assert.ok(
|
|
751
|
+
expanded.reduce((sum, message) => sum + estimateTokenCount(message.content), 0) <= 1,
|
|
752
|
+
);
|
|
753
|
+
} finally {
|
|
754
|
+
engine?.close();
|
|
755
|
+
await rm(memoryDir, { recursive: true, force: true });
|
|
756
|
+
}
|
|
757
|
+
});
|
|
678
758
|
|
|
679
759
|
test("assembleRecallWithTrace preserves text and reports selected summary metadata", async () => {
|
|
680
760
|
const memoryDir = await mkdtemp(
|
package/src/local-llm.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { log } from "./logger.js";
|
|
2
|
+
import { estimateTokenCount } from "./token-estimate.js";
|
|
2
3
|
import type { PluginConfig } from "./types.js";
|
|
3
4
|
import fs from "node:fs";
|
|
4
5
|
import os from "node:os";
|
|
@@ -1431,18 +1432,15 @@ export class LocalLlmClient {
|
|
|
1431
1432
|
|
|
1432
1433
|
return await this.runChatCompletionRequest(messages, options);
|
|
1433
1434
|
}
|
|
1434
|
-
|
|
1435
1435
|
/**
|
|
1436
|
-
* Estimate tokens when local LLM
|
|
1437
|
-
* Rough estimate: 1 token ≈ 4 characters
|
|
1436
|
+
* Estimate tokens when local LLM does not return usage stats.
|
|
1438
1437
|
*/
|
|
1439
1438
|
private estimateTokens(
|
|
1440
1439
|
messages: Array<{ role: string; content: string }>,
|
|
1441
1440
|
response: string
|
|
1442
1441
|
): { promptTokens: number; completionTokens: number; totalTokens: number } {
|
|
1443
|
-
const
|
|
1444
|
-
const
|
|
1445
|
-
const completionTokens = Math.ceil(response.length / 4);
|
|
1442
|
+
const promptTokens = messages.reduce((sum, message) => sum + estimateTokenCount(message.content), 0);
|
|
1443
|
+
const completionTokens = estimateTokenCount(response);
|
|
1446
1444
|
|
|
1447
1445
|
return {
|
|
1448
1446
|
promptTokens,
|
|
@@ -6,16 +6,10 @@
|
|
|
6
6
|
* truncates with a footer listing omitted extensions when over budget.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
import { estimateTokenCount } from "../token-estimate.js";
|
|
9
10
|
import { REMNIC_EXTENSIONS_TOTAL_TOKEN_LIMIT } from "./host-discovery.js";
|
|
10
11
|
import type { DiscoveredExtension } from "./types.js";
|
|
11
12
|
|
|
12
|
-
/**
|
|
13
|
-
* Approximate token count using the 4 chars per token heuristic.
|
|
14
|
-
*/
|
|
15
|
-
function estimateTokens(text: string): number {
|
|
16
|
-
return Math.ceil(text.length / 4);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
13
|
/**
|
|
20
14
|
* Render a markdown block containing extension instructions for injection
|
|
21
15
|
* into consolidation prompts.
|
|
@@ -36,14 +30,14 @@ extension produces or curates.
|
|
|
36
30
|
`;
|
|
37
31
|
|
|
38
32
|
let budget = REMNIC_EXTENSIONS_TOTAL_TOKEN_LIMIT;
|
|
39
|
-
budget -=
|
|
33
|
+
budget -= estimateTokenCount(header);
|
|
40
34
|
|
|
41
35
|
const inlined: string[] = [];
|
|
42
36
|
const omitted: string[] = [];
|
|
43
37
|
|
|
44
38
|
for (const ext of extensions) {
|
|
45
39
|
const block = `### remnic-extension/${ext.name}\n\`\`\`\n${ext.instructions}\n\`\`\`\n\n`;
|
|
46
|
-
const cost =
|
|
40
|
+
const cost = estimateTokenCount(block);
|
|
47
41
|
if (cost <= budget) {
|
|
48
42
|
inlined.push(block);
|
|
49
43
|
budget -= cost;
|
|
@@ -17,8 +17,9 @@
|
|
|
17
17
|
* TierMigrationCoordinator accessor pattern.
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
|
-
import type { PluginConfig, RecallSectionConfig } from "../types.js";
|
|
21
20
|
import type { LastRecallBudgetSummary } from "../recall-state.js";
|
|
21
|
+
import { estimateTokenCount } from "../token-estimate.js";
|
|
22
|
+
import type { PluginConfig, RecallSectionConfig } from "../types.js";
|
|
22
23
|
|
|
23
24
|
export interface RecallSectionAppendOptions {
|
|
24
25
|
atomic?: boolean;
|
|
@@ -179,18 +180,54 @@ export class RecallSectionCoordinator {
|
|
|
179
180
|
const end = boundary > 0 ? boundary : contentLimit;
|
|
180
181
|
return `${content.slice(0, end).trimEnd()}${suffix}`;
|
|
181
182
|
}
|
|
182
|
-
|
|
183
183
|
truncateRecallSectionToBudget(
|
|
184
184
|
content: string,
|
|
185
185
|
maxChars: number,
|
|
186
|
+
maxTokens?: number,
|
|
186
187
|
): string {
|
|
187
|
-
if (maxChars <= 0) return "";
|
|
188
|
-
if (content.length <= maxChars) return content;
|
|
188
|
+
if (maxChars <= 0 || maxTokens === 0) return "";
|
|
189
189
|
const suffix = "\n\n...(memory context trimmed)";
|
|
190
|
-
|
|
191
|
-
|
|
190
|
+
const charBounded =
|
|
191
|
+
content.length <= maxChars
|
|
192
|
+
? content
|
|
193
|
+
: maxChars <= suffix.length
|
|
194
|
+
? content.slice(0, maxChars)
|
|
195
|
+
: `${content.slice(0, maxChars - suffix.length)}${suffix}`;
|
|
196
|
+
if (maxTokens === undefined || estimateTokenCount(charBounded) <= maxTokens) {
|
|
197
|
+
return charBounded;
|
|
192
198
|
}
|
|
193
|
-
|
|
199
|
+
const markerFits =
|
|
200
|
+
maxChars > suffix.length && estimateTokenCount(suffix) <= maxTokens;
|
|
201
|
+
const tokenSource = content.length > maxChars ? content.slice(0, maxChars) : content;
|
|
202
|
+
const codePoints = [...tokenSource];
|
|
203
|
+
let low = 0;
|
|
204
|
+
let high = codePoints.length;
|
|
205
|
+
while (low < high) {
|
|
206
|
+
const mid = Math.ceil((low + high) / 2);
|
|
207
|
+
const prefix = codePoints.slice(0, mid).join("");
|
|
208
|
+
const candidate = markerFits ? `${prefix}${suffix}` : prefix;
|
|
209
|
+
if (candidate.length <= maxChars && estimateTokenCount(candidate) <= maxTokens) {
|
|
210
|
+
low = mid;
|
|
211
|
+
} else {
|
|
212
|
+
high = mid - 1;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
if (markerFits && low === 0) {
|
|
216
|
+
low = 0;
|
|
217
|
+
high = codePoints.length;
|
|
218
|
+
while (low < high) {
|
|
219
|
+
const mid = Math.ceil((low + high) / 2);
|
|
220
|
+
const prefix = codePoints.slice(0, mid).join("");
|
|
221
|
+
if (prefix.length <= maxChars && estimateTokenCount(prefix) <= maxTokens) {
|
|
222
|
+
low = mid;
|
|
223
|
+
} else {
|
|
224
|
+
high = mid - 1;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
return codePoints.slice(0, low).join("");
|
|
228
|
+
}
|
|
229
|
+
const prefix = codePoints.slice(0, low).join("");
|
|
230
|
+
return markerFits ? `${prefix}${suffix}` : prefix;
|
|
194
231
|
}
|
|
195
232
|
|
|
196
233
|
|
|
@@ -280,6 +317,7 @@ export class RecallSectionCoordinator {
|
|
|
280
317
|
}
|
|
281
318
|
|
|
282
319
|
const budget = this.getRecallBudgetChars(budgetOverride);
|
|
320
|
+
const tokenBudget = Math.max(0, Math.floor(this.getConfig().maxMemoryTokens));
|
|
283
321
|
const candidateMemoryChunks =
|
|
284
322
|
orderedSections
|
|
285
323
|
.find((section) => section.id === "memories")
|
|
@@ -312,10 +350,19 @@ export class RecallSectionCoordinator {
|
|
|
312
350
|
const memoryIndex = allocationOrder.indexOf("memories");
|
|
313
351
|
const memoryBudget = this.getRecallSectionMaxChars("memories") ?? budget;
|
|
314
352
|
const memoryAllocationBudget = Math.min(budget, memoryBudget);
|
|
315
|
-
let
|
|
353
|
+
let firstAtomicMemoryReserve:
|
|
354
|
+
| { chars: number; tokens: number }
|
|
355
|
+
| undefined;
|
|
316
356
|
for (const chunk of memorySection?.chunks ?? []) {
|
|
317
|
-
if (chunk.atomic
|
|
318
|
-
|
|
357
|
+
if (!chunk.atomic || chunk.content.length > memoryAllocationBudget) {
|
|
358
|
+
continue;
|
|
359
|
+
}
|
|
360
|
+
const tokens = estimateTokenCount(chunk.content);
|
|
361
|
+
if (tokens <= tokenBudget) {
|
|
362
|
+
firstAtomicMemoryReserve = {
|
|
363
|
+
chars: chunk.content.length,
|
|
364
|
+
tokens,
|
|
365
|
+
};
|
|
319
366
|
break;
|
|
320
367
|
}
|
|
321
368
|
}
|
|
@@ -323,6 +370,7 @@ export class RecallSectionCoordinator {
|
|
|
323
370
|
const includedMemoryPaths: string[] = [];
|
|
324
371
|
const includedMemoryNamespaces: Array<string | undefined> = [];
|
|
325
372
|
let usedChars = 0;
|
|
373
|
+
let usedTokens = 0;
|
|
326
374
|
let truncated = false;
|
|
327
375
|
const selected = new Map<string, string>();
|
|
328
376
|
|
|
@@ -331,19 +379,30 @@ export class RecallSectionCoordinator {
|
|
|
331
379
|
if (!section || selected.has(id)) continue;
|
|
332
380
|
const separatorChars = selected.size > 0 ? separator.length : 0;
|
|
333
381
|
const available = budget - usedChars - separatorChars;
|
|
382
|
+
const separatorTokens = selected.size > 0 ? estimateTokenCount(separator) : 0;
|
|
383
|
+
const availableTokens = tokenBudget - usedTokens - separatorTokens;
|
|
334
384
|
const sectionMaxChars = this.getRecallSectionMaxChars(id);
|
|
335
|
-
const
|
|
336
|
-
memoryIndex > allocationOrder.indexOf(id)
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
385
|
+
const reservedMemory =
|
|
386
|
+
memoryIndex > allocationOrder.indexOf(id)
|
|
387
|
+
? firstAtomicMemoryReserve
|
|
388
|
+
: undefined;
|
|
389
|
+
const memoryReserveSeparatorChars = separator.length;
|
|
390
|
+
const memoryReserveSeparatorTokens = estimateTokenCount(separator);
|
|
391
|
+
const memoryReserve = reservedMemory
|
|
392
|
+
? reservedMemory.chars + memoryReserveSeparatorChars
|
|
340
393
|
: 0;
|
|
341
394
|
const availableAfterMemoryReserve = available - memoryReserve;
|
|
342
395
|
const allocatedSectionAvailable =
|
|
343
396
|
typeof sectionMaxChars === "number"
|
|
344
397
|
? Math.min(availableAfterMemoryReserve, sectionMaxChars)
|
|
345
398
|
: availableAfterMemoryReserve;
|
|
346
|
-
|
|
399
|
+
const availableAfterMemoryReserveTokens = reservedMemory
|
|
400
|
+
? availableTokens -
|
|
401
|
+
reservedMemory.tokens -
|
|
402
|
+
memoryReserveSeparatorTokens
|
|
403
|
+
: availableTokens;
|
|
404
|
+
const allocatedTokenAvailable = availableAfterMemoryReserveTokens;
|
|
405
|
+
if (allocatedSectionAvailable <= 0 || allocatedTokenAvailable <= 0) {
|
|
347
406
|
truncated = true;
|
|
348
407
|
continue;
|
|
349
408
|
}
|
|
@@ -372,13 +431,11 @@ export class RecallSectionCoordinator {
|
|
|
372
431
|
Math.min(profileLimit, allocatedSectionAvailable),
|
|
373
432
|
)
|
|
374
433
|
: content;
|
|
375
|
-
finalContent =
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
allocatedSectionAvailable,
|
|
381
|
-
);
|
|
434
|
+
finalContent = this.truncateRecallSectionToBudget(
|
|
435
|
+
boundedContent,
|
|
436
|
+
allocatedSectionAvailable,
|
|
437
|
+
allocatedTokenAvailable,
|
|
438
|
+
);
|
|
382
439
|
if (!finalContent) {
|
|
383
440
|
truncated = true;
|
|
384
441
|
continue;
|
|
@@ -400,7 +457,10 @@ export class RecallSectionCoordinator {
|
|
|
400
457
|
const candidate = rendered
|
|
401
458
|
? `${rendered}\n\n${chunk.content}`
|
|
402
459
|
: chunk.content;
|
|
403
|
-
if (
|
|
460
|
+
if (
|
|
461
|
+
candidate.length <= allocatedSectionAvailable &&
|
|
462
|
+
estimateTokenCount(candidate) <= allocatedTokenAvailable
|
|
463
|
+
) {
|
|
404
464
|
rendered = candidate;
|
|
405
465
|
includedLeadingContent = true;
|
|
406
466
|
} else {
|
|
@@ -412,12 +472,16 @@ export class RecallSectionCoordinator {
|
|
|
412
472
|
const candidate = rendered
|
|
413
473
|
? `${rendered}\n\n${chunk.content}`
|
|
414
474
|
: chunk.content;
|
|
415
|
-
if (
|
|
475
|
+
if (
|
|
476
|
+
candidate.length > allocatedSectionAvailable ||
|
|
477
|
+
estimateTokenCount(candidate) > allocatedTokenAvailable
|
|
478
|
+
) {
|
|
416
479
|
if (
|
|
417
480
|
chunk.atomic &&
|
|
418
481
|
includedAtomicCount === 0 &&
|
|
419
482
|
includedLeadingContent &&
|
|
420
|
-
chunk.content.length <= allocatedSectionAvailable
|
|
483
|
+
chunk.content.length <= allocatedSectionAvailable &&
|
|
484
|
+
estimateTokenCount(chunk.content) <= allocatedTokenAvailable
|
|
421
485
|
) {
|
|
422
486
|
rendered = chunk.content;
|
|
423
487
|
includedLeadingContent = false;
|
|
@@ -435,7 +499,8 @@ export class RecallSectionCoordinator {
|
|
|
435
499
|
!chunk.atomic &&
|
|
436
500
|
includedAtomicCount === 0 &&
|
|
437
501
|
includedLeadingContent &&
|
|
438
|
-
chunk.content.length <= allocatedSectionAvailable
|
|
502
|
+
chunk.content.length <= allocatedSectionAvailable &&
|
|
503
|
+
estimateTokenCount(chunk.content) <= allocatedTokenAvailable
|
|
439
504
|
) {
|
|
440
505
|
rendered = chunk.content;
|
|
441
506
|
includedLeadingContent = false;
|
|
@@ -479,6 +544,7 @@ export class RecallSectionCoordinator {
|
|
|
479
544
|
}
|
|
480
545
|
selected.set(id, finalContent);
|
|
481
546
|
usedChars += separatorChars + finalContent.length;
|
|
547
|
+
usedTokens += separatorTokens + estimateTokenCount(finalContent);
|
|
482
548
|
}
|
|
483
549
|
|
|
484
550
|
const sections: string[] = [];
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import test from "node:test";
|
|
3
|
+
|
|
4
|
+
import { parseConfig } from "./config.js";
|
|
5
|
+
import { RecallSectionCoordinator } from "./orchestration/recall-section-coordinator.js";
|
|
6
|
+
import { estimateTokenCount } from "./token-estimate.js";
|
|
7
|
+
|
|
8
|
+
test("recall derived budget keeps Japanese injection within maxMemoryTokens", () => {
|
|
9
|
+
const config = parseConfig({
|
|
10
|
+
maxMemoryTokens: 12,
|
|
11
|
+
recallPipeline: [{ id: "memories", enabled: true }],
|
|
12
|
+
});
|
|
13
|
+
const coordinator = new RecallSectionCoordinator({ getConfig: () => config });
|
|
14
|
+
const buckets = new Map<string, string[]>();
|
|
15
|
+
|
|
16
|
+
coordinator.appendRecallSection(buckets, "memories", "日本語の記憶。".repeat(20));
|
|
17
|
+
const assembled = coordinator.assembleRecallSections(buckets);
|
|
18
|
+
const injected = assembled.sections.join("\n\n---\n\n");
|
|
19
|
+
|
|
20
|
+
assert.equal(config.recallBudgetChars, config.maxMemoryTokens * 4);
|
|
21
|
+
assert.ok(estimateTokenCount(injected) <= config.maxMemoryTokens);
|
|
22
|
+
});
|
|
23
|
+
test("recall reserves one memory chunk that fits both character and token budgets", () => {
|
|
24
|
+
const config = parseConfig({
|
|
25
|
+
maxMemoryTokens: 50,
|
|
26
|
+
recallBudgetChars: 150,
|
|
27
|
+
recallPipeline: [
|
|
28
|
+
{ id: "profile", enabled: true },
|
|
29
|
+
{ id: "memories", enabled: true },
|
|
30
|
+
],
|
|
31
|
+
});
|
|
32
|
+
const coordinator = new RecallSectionCoordinator({ getConfig: () => config });
|
|
33
|
+
const buckets = new Map<string, string[]>();
|
|
34
|
+
|
|
35
|
+
coordinator.appendRecallSection(buckets, "profile", "x".repeat(90));
|
|
36
|
+
coordinator.appendRecallSection(buckets, "memories", "日".repeat(51), {
|
|
37
|
+
atomic: true,
|
|
38
|
+
memoryId: "wide",
|
|
39
|
+
memoryPath: "memories/wide.md",
|
|
40
|
+
});
|
|
41
|
+
coordinator.appendRecallSection(buckets, "memories", "a".repeat(100), {
|
|
42
|
+
atomic: true,
|
|
43
|
+
memoryId: "latin",
|
|
44
|
+
memoryPath: "memories/latin.md",
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const assembled = coordinator.assembleRecallSections(buckets);
|
|
48
|
+
|
|
49
|
+
assert.deepEqual(assembled.includedMemoryIds, ["latin"]);
|
|
50
|
+
assert.ok(estimateTokenCount(assembled.sections.join("\n\n---\n\n")) <= 50);
|
|
51
|
+
});
|
package/src/recall-xray.ts
CHANGED
|
@@ -19,24 +19,17 @@
|
|
|
19
19
|
|
|
20
20
|
import { randomUUID } from "node:crypto";
|
|
21
21
|
|
|
22
|
-
import {
|
|
23
|
-
|
|
24
|
-
type RetrievedMemoryProvenance,
|
|
25
|
-
} from "./memory-provenance.js";
|
|
22
|
+
import { normalizeRetrievedMemoryProvenance, type RetrievedMemoryProvenance } from "./memory-provenance.js";
|
|
23
|
+
import { estimateTokenCount } from "./token-estimate.js";
|
|
26
24
|
import type { RecallDisclosure, RecallTierExplain } from "./types.js";
|
|
27
25
|
import { isRecallDisclosure } from "./types.js";
|
|
28
26
|
|
|
29
27
|
/**
|
|
30
|
-
* Estimate token cost of a payload
|
|
31
|
-
* heuristic. Non-negative integer; returns 0 for empty / null input.
|
|
32
|
-
* Used by recall surfaces to attach `estimatedTokens` to X-ray results
|
|
33
|
-
* (issue #677 PR 3/4). Identical to the private heuristic in
|
|
34
|
-
* `chunking.ts`; kept self-contained here so X-ray callers don't pull
|
|
35
|
-
* in chunking internals.
|
|
28
|
+
* Estimate token cost of a payload for X-ray disclosure accounting.
|
|
36
29
|
*/
|
|
37
30
|
export function estimateRecallTokens(text: string | null | undefined): number {
|
|
38
31
|
if (typeof text !== "string" || text.length === 0) return 0;
|
|
39
|
-
return
|
|
32
|
+
return estimateTokenCount(text);
|
|
40
33
|
}
|
|
41
34
|
|
|
42
35
|
/**
|
package/src/semantic-chunking.ts
CHANGED
|
@@ -6,7 +6,14 @@
|
|
|
6
6
|
* natural topic boundaries, producing more coherent chunks.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
chunkContent,
|
|
11
|
+
joinSentences,
|
|
12
|
+
splitSentences,
|
|
13
|
+
type Chunk,
|
|
14
|
+
type ChunkResult,
|
|
15
|
+
} from "./chunking.js";
|
|
16
|
+
import { estimateTokenCount } from "./token-estimate.js";
|
|
10
17
|
|
|
11
18
|
// ---------------------------------------------------------------------------
|
|
12
19
|
// Configuration
|
|
@@ -177,59 +184,7 @@ export function findLocalMinima(
|
|
|
177
184
|
}
|
|
178
185
|
|
|
179
186
|
// ---------------------------------------------------------------------------
|
|
180
|
-
// Sentence tokenizer
|
|
181
|
-
// ---------------------------------------------------------------------------
|
|
182
|
-
|
|
183
|
-
/**
|
|
184
|
-
* Split text into sentences at punctuation boundaries.
|
|
185
|
-
* Preserves punctuation with the preceding sentence.
|
|
186
|
-
*/
|
|
187
|
-
function splitSentences(text: string): string[] {
|
|
188
|
-
// Linear character scan instead of a regex. Every regex form of this split is
|
|
189
|
-
// either polynomial (CodeQL js/polynomial-redos) or — once bounded/anchored to
|
|
190
|
-
// satisfy CodeQL — mishandles long runs or interior punctuation (a global
|
|
191
|
-
// match drops a skipped prefix; a sticky match stops at the first non-boundary
|
|
192
|
-
// `.`, e.g. "v1.2.3" / "example.com", returning the whole document as one
|
|
193
|
-
// sentence and bypassing chunking). The scan is O(n), drops nothing, and
|
|
194
|
-
// handles interior punctuation correctly; normal prose splits identically to
|
|
195
|
-
// the previous /[^.!?]*[.!?]+(?:\s+|$)/g form.
|
|
196
|
-
const sentences: string[] = [];
|
|
197
|
-
let start = 0;
|
|
198
|
-
for (let i = 0; i < text.length; i++) {
|
|
199
|
-
const ch = text[i];
|
|
200
|
-
if (ch !== "." && ch !== "!" && ch !== "?") continue;
|
|
201
|
-
let end = i;
|
|
202
|
-
while (end + 1 < text.length) {
|
|
203
|
-
const n = text[end + 1];
|
|
204
|
-
if (n !== "." && n !== "!" && n !== "?") break;
|
|
205
|
-
end++;
|
|
206
|
-
}
|
|
207
|
-
const after = text[end + 1];
|
|
208
|
-
// A real boundary only if the terminator run ends the string or is followed
|
|
209
|
-
// by whitespace. Interior punctuation (no following whitespace) is left in
|
|
210
|
-
// place and the scan continues.
|
|
211
|
-
if (after === undefined || /\s/.test(after)) {
|
|
212
|
-
const sentence = text.slice(start, end + 1).trim();
|
|
213
|
-
if (sentence.length > 0) sentences.push(sentence);
|
|
214
|
-
start = end + 1;
|
|
215
|
-
}
|
|
216
|
-
i = end;
|
|
217
|
-
}
|
|
218
|
-
if (start < text.length) {
|
|
219
|
-
const remaining = text.slice(start).trim();
|
|
220
|
-
if (remaining.length > 0) sentences.push(remaining);
|
|
221
|
-
}
|
|
222
|
-
return sentences;
|
|
223
|
-
}
|
|
224
187
|
|
|
225
|
-
// ---------------------------------------------------------------------------
|
|
226
|
-
// Token estimation
|
|
227
|
-
// ---------------------------------------------------------------------------
|
|
228
|
-
|
|
229
|
-
/** Rough token estimate: ~4 chars per token for English. */
|
|
230
|
-
function estimateTokens(text: string): number {
|
|
231
|
-
return Math.ceil(text.length / 4);
|
|
232
|
-
}
|
|
233
188
|
|
|
234
189
|
// ---------------------------------------------------------------------------
|
|
235
190
|
// Core semantic chunking
|
|
@@ -316,7 +271,7 @@ function mergeShortSegments(
|
|
|
316
271
|
|
|
317
272
|
for (let i = 0; i < segments.length; i++) {
|
|
318
273
|
buffer = [...buffer, ...segments[i]];
|
|
319
|
-
const tokenCount =
|
|
274
|
+
const tokenCount = estimateTokenCount(joinSentences(buffer));
|
|
320
275
|
|
|
321
276
|
if (tokenCount >= minTokens || i === segments.length - 1) {
|
|
322
277
|
merged.push(buffer);
|
|
@@ -344,7 +299,7 @@ function splitLongSegment(
|
|
|
344
299
|
maxTokens: number,
|
|
345
300
|
targetTokens: number,
|
|
346
301
|
): SemanticChunk[] {
|
|
347
|
-
const text = segment
|
|
302
|
+
const text = joinSentences(segment);
|
|
348
303
|
// Cap targetTokens to maxTokens so recursive splitting never produces
|
|
349
304
|
// segments larger than the configured maximum (Finding 2, PR #420).
|
|
350
305
|
const cappedTarget = Math.min(targetTokens, maxTokens);
|
|
@@ -396,7 +351,7 @@ export async function semanticChunkContent(
|
|
|
396
351
|
const sentences = splitSentences(content);
|
|
397
352
|
|
|
398
353
|
if (sentences.length <= 1) {
|
|
399
|
-
const tokenCount =
|
|
354
|
+
const tokenCount = estimateTokenCount(content);
|
|
400
355
|
return {
|
|
401
356
|
chunked: false,
|
|
402
357
|
chunks: [
|
|
@@ -413,7 +368,7 @@ export async function semanticChunkContent(
|
|
|
413
368
|
}
|
|
414
369
|
|
|
415
370
|
// If total tokens is short enough, return as single chunk
|
|
416
|
-
const totalTokens =
|
|
371
|
+
const totalTokens = estimateTokenCount(content);
|
|
417
372
|
if (totalTokens <= cfg.minTokens) {
|
|
418
373
|
return {
|
|
419
374
|
chunked: false,
|
|
@@ -511,8 +466,8 @@ export async function semanticChunkContent(
|
|
|
511
466
|
|
|
512
467
|
for (let segIdx = 0; segIdx < segments.length; segIdx++) {
|
|
513
468
|
const segment = segments[segIdx];
|
|
514
|
-
const segText = segment
|
|
515
|
-
const segTokens =
|
|
469
|
+
const segText = joinSentences(segment);
|
|
470
|
+
const segTokens = estimateTokenCount(segText);
|
|
516
471
|
|
|
517
472
|
if (segTokens > cfg.maxTokens) {
|
|
518
473
|
// Recursive split for oversized segment
|
|
@@ -14,7 +14,7 @@ interface SessionObserverCursor {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
interface SessionObserverPersistedState {
|
|
17
|
-
version:
|
|
17
|
+
version: 2;
|
|
18
18
|
sessions: Record<string, SessionObserverCursor>;
|
|
19
19
|
}
|
|
20
20
|
|
|
@@ -134,7 +134,7 @@ export class SessionObserverState {
|
|
|
134
134
|
try {
|
|
135
135
|
const raw = await readFile(this.statePath, "utf-8");
|
|
136
136
|
const parsed = JSON.parse(raw) as SessionObserverPersistedState;
|
|
137
|
-
if (parsed?.version !==
|
|
137
|
+
if (parsed?.version !== 2 || !parsed.sessions || typeof parsed.sessions !== "object") {
|
|
138
138
|
return null;
|
|
139
139
|
}
|
|
140
140
|
return parsed;
|
|
@@ -246,7 +246,7 @@ export class SessionObserverState {
|
|
|
246
246
|
for (const [key, value] of sessionsMap.entries()) {
|
|
247
247
|
sessions[key] = value;
|
|
248
248
|
}
|
|
249
|
-
const payload: SessionObserverPersistedState = { version:
|
|
249
|
+
const payload: SessionObserverPersistedState = { version: 2, sessions };
|
|
250
250
|
await mkdir(path.dirname(this.statePath), { recursive: true });
|
|
251
251
|
await writeFile(this.statePath, JSON.stringify(payload, null, 2), "utf-8");
|
|
252
252
|
}
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*
|
|
10
10
|
* The nine rows are realized honestly against the coordinator's REAL behavior —
|
|
11
11
|
* no mocks. Config is built through the production `parseConfig` path so the
|
|
12
|
-
* budget derivation (recallBudgetChars = maxMemoryTokens
|
|
12
|
+
* budget derivation (recallBudgetChars = maxMemoryTokens), section
|
|
13
13
|
* enablement filtering, per-section maxChars caps, and cap-after-filter
|
|
14
14
|
* ordering are all exercised as they ship. Each row asserts one recall-budget
|
|
15
15
|
* contract invariant:
|
|
@@ -28,6 +28,7 @@ import assert from "node:assert/strict";
|
|
|
28
28
|
|
|
29
29
|
import { parseConfig } from "../../config.js";
|
|
30
30
|
import { RecallSectionCoordinator } from "../../orchestration/recall-section-coordinator.js";
|
|
31
|
+
import { estimateTokenCount } from "../../token-estimate.js";
|
|
31
32
|
import { createRecallSectionMetricRecorder, formatRecallSectionMetric } from "../../recall-qos.js";
|
|
32
33
|
import { reorderRecallResultsWithMmr } from "../../recall-mmr.js";
|
|
33
34
|
import { applyRuntimeRetrievalPolicy, expandQuery } from "../../retrieval.js";
|
|
@@ -297,10 +298,10 @@ const subject: LifecycleSubject<RecallBudgetState> = {
|
|
|
297
298
|
return;
|
|
298
299
|
}
|
|
299
300
|
case "sparse-metadata-with-binding": {
|
|
300
|
-
// Budget
|
|
301
|
+
// Budget keeps Latin-script headroom and token-aware assembly caps wide scripts.
|
|
301
302
|
assert.equal(config.recallBudgetChars, config.maxMemoryTokens * 4, "budget derives from maxMemoryTokens");
|
|
302
|
-
assert.
|
|
303
|
-
assert.ok(assembled.finalChars <= budget, "section sum stays within the
|
|
303
|
+
assert.ok(budget <= config.maxMemoryTokens * 4, "coordinator honors the character budget");
|
|
304
|
+
assert.ok(assembled.finalChars <= budget, "section sum stays within the character budget");
|
|
304
305
|
assert.ok(assembled.truncated, "oversized content forces truncation");
|
|
305
306
|
return;
|
|
306
307
|
}
|
|
@@ -372,7 +373,22 @@ const subject: LifecycleSubject<RecallBudgetState> = {
|
|
|
372
373
|
// truncateRecallSectionToBudget respects a hard per-section budget too.
|
|
373
374
|
const truncated = coordinator.truncateRecallSectionToBudget(body("m", 200), 50);
|
|
374
375
|
assert.equal(truncated.length, 50, "budget-aware truncation lands exactly on the per-section limit");
|
|
375
|
-
|
|
376
|
+
const tokenTrimmed = coordinator.truncateRecallSectionToBudget("x".repeat(200), 200, 20);
|
|
377
|
+
assert.match(tokenTrimmed, /\.\.\.\(memory context trimmed\)$/);
|
|
378
|
+
assert.ok(estimateTokenCount(tokenTrimmed) <= 20, "token-aware truncation keeps its marker inside the cap");
|
|
379
|
+
const markerBudget = estimateTokenCount("\n\n...(memory context trimmed)");
|
|
380
|
+
const markerOnlyBudget = coordinator.truncateRecallSectionToBudget("日本語".repeat(100), 200, markerBudget);
|
|
381
|
+
assert.ok(markerOnlyBudget.length > 0, "a marker-only budget keeps useful content");
|
|
382
|
+
assert.doesNotMatch(markerOnlyBudget, /\.\.\.\(memory context trimmed\)$/);
|
|
383
|
+
assert.ok(estimateTokenCount(markerOnlyBudget) <= markerBudget);
|
|
384
|
+
const wideScriptTrimmed = coordinator.truncateRecallSectionToBudget("日".repeat(100), 30, markerBudget);
|
|
385
|
+
assert.equal([...wideScriptTrimmed].length, 8, "wide-script fallback uses the available character room");
|
|
386
|
+
assert.ok(estimateTokenCount(wideScriptTrimmed) <= markerBudget);
|
|
387
|
+
assert.doesNotMatch(wideScriptTrimmed, /\.\.\.\(memory context trimmed\)$/);
|
|
388
|
+
const emojiTrimmed = coordinator.truncateRecallSectionToBudget("😀".repeat(30), 60, 29);
|
|
389
|
+
assert.ok(emojiTrimmed.length <= 60, "astral text stays within the UTF-16 character cap");
|
|
390
|
+
assert.ok(estimateTokenCount(emojiTrimmed) <= 29, "astral text stays within the token cap");
|
|
391
|
+
assert.match(emojiTrimmed, /\.\.\.\(memory context trimmed\)$/);
|
|
376
392
|
// QoS accounting that flags a section dropped for the budget deadline.
|
|
377
393
|
const skipMetric = formatRecallSectionMetric({
|
|
378
394
|
section: "memories",
|