@remnic/core 9.69.49 → 9.69.50
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 +15 -14
- package/dist/access-admin-ops-surface.js +23 -23
- package/dist/access-authorization-probe.d.ts +15 -14
- package/dist/access-authorization-probe.js +24 -24
- package/dist/access-boundary.d.ts +15 -14
- package/dist/access-boundary.js +23 -23
- package/dist/access-cli.js +74 -73
- package/dist/access-cli.js.map +1 -1
- package/dist/access-coding-context-resolution.d.ts +2 -1
- package/dist/access-extraction-force-flush.d.ts +15 -14
- package/dist/access-extraction-force-flush.js +23 -23
- package/dist/access-health-types.d.ts +2 -1
- package/dist/access-http-briefing.d.ts +15 -14
- package/dist/access-http-briefing.js +24 -24
- package/dist/access-http-lcm-compaction.d.ts +15 -14
- package/dist/access-http-lifecycle-flush.d.ts +15 -14
- package/dist/access-http-offline-stream.d.ts +15 -14
- package/dist/access-http-query.d.ts +3 -2
- package/dist/access-http-query.js +25 -25
- package/dist/access-http.d.ts +16 -15
- package/dist/access-http.js +40 -40
- package/dist/access-identity-continuity-surface.d.ts +12 -11
- package/dist/access-identity-continuity-surface.js +23 -23
- package/dist/access-lcm-surface.d.ts +15 -14
- package/dist/access-lcm-surface.js +23 -23
- package/dist/access-mcp-shared-context-tools.d.ts +15 -14
- package/dist/access-mcp.d.ts +15 -14
- package/dist/access-mcp.js +32 -32
- package/dist/access-memory-search-fanout.d.ts +3 -2
- package/dist/{access-namespace-preflight-DqRw_g-8.d.ts → access-namespace-preflight-C1q3XuAc.d.ts} +1 -1
- package/dist/access-namespace-preflight.d.ts +4 -3
- package/dist/access-namespace-preflight.js +23 -23
- package/dist/access-observe-transcript.d.ts +12 -11
- package/dist/access-observe-write-category.d.ts +4 -3
- package/dist/access-observe-write-category.js +4 -4
- package/dist/access-observe-write-surface.d.ts +15 -14
- package/dist/access-observe-write-surface.js +23 -23
- package/dist/access-offline-manifest.d.ts +12 -11
- package/dist/access-operations-batch.js +24 -24
- package/dist/access-operations.d.ts +15 -14
- package/dist/access-operations.js +31 -31
- package/dist/access-recall-concurrency.d.ts +15 -14
- package/dist/access-recall-concurrency.js +23 -23
- package/dist/access-recall-response.d.ts +15 -14
- package/dist/access-recall-response.js +23 -23
- package/dist/access-recall-surface.d.ts +15 -14
- package/dist/access-recall-surface.js +23 -23
- package/dist/access-schema.d.ts +2 -1
- package/dist/access-schema.js +3 -3
- package/dist/{access-service-70VHaltr.d.ts → access-service-BZaC-RZw.d.ts} +9 -9
- package/dist/access-service-helpers.d.ts +15 -14
- package/dist/access-service.d.ts +15 -14
- package/dist/access-service.js +23 -23
- package/dist/access-surface-catalog.d.ts +15 -14
- package/dist/access-surface-catalog.js +25 -25
- package/dist/access-wearables-meetings-surface.d.ts +5 -4
- package/dist/action-confidence.d.ts +2 -1
- package/dist/active-memory-bridge.d.ts +2 -1
- package/dist/active-recall.d.ts +2 -1
- package/dist/active-recall.js +2 -1
- package/dist/active-recall.js.map +1 -1
- package/dist/activity/digest.d.ts +2 -1
- package/dist/ambient-provenance.d.ts +2 -1
- package/dist/artifact-search.d.ts +2 -1
- package/dist/background-generation.d.ts +2 -1
- package/dist/behavior-learner.d.ts +2 -1
- package/dist/behavior-signals.d.ts +2 -1
- package/dist/bootstrap.d.ts +12 -11
- package/dist/briefing-window.d.ts +3 -2
- package/dist/briefing.d.ts +3 -2
- package/dist/briefing.js +7 -7
- package/dist/buffer-surprise-report.d.ts +2 -1
- package/dist/buffer-turn-helpers.d.ts +2 -1
- package/dist/buffer.d.ts +3 -2
- package/dist/bulk-import/index.d.ts +4 -3
- package/dist/calibration.d.ts +2 -1
- package/dist/capabilities.d.ts +2 -1
- package/dist/{catalog-Byi9prz9.d.ts → catalog-DK2eHhax.d.ts} +1 -1
- package/dist/causal-behavior.d.ts +2 -1
- package/dist/causal-consolidation.d.ts +2 -1
- package/dist/causal-consolidation.js +8 -8
- package/dist/causal-trajectory-graph.d.ts +2 -1
- package/dist/{chunk-QQA6VTOY.js → chunk-3MIZ2EKQ.js} +2 -2
- package/dist/{chunk-PAQ7YGJY.js → chunk-3RZB7TYL.js} +2 -2
- package/dist/{chunk-FVRZDZ7J.js → chunk-3SI3FFTW.js} +2 -2
- package/dist/{chunk-WNMVUWI7.js → chunk-3UQIS2GC.js} +2 -2
- package/dist/{chunk-FLZJMXFV.js → chunk-42BV46XS.js} +2 -2
- package/dist/{chunk-ZYZW46HZ.js → chunk-4FWNMN5R.js} +4 -4
- package/dist/chunk-4KQIWY4A.js +197 -0
- package/dist/chunk-4KQIWY4A.js.map +1 -0
- package/dist/{chunk-EBLKS57G.js → chunk-4PXFLDMJ.js} +2 -2
- package/dist/{chunk-XQFHKPNB.js → chunk-4SDO74N2.js} +2 -2
- package/dist/{chunk-YOC3EEZH.js → chunk-56C7OO3N.js} +13 -13
- package/dist/{chunk-76RTJ533.js → chunk-6525YIYL.js} +3 -3
- package/dist/{chunk-PSQRBCGR.js → chunk-AL7OLBL7.js} +2 -2
- package/dist/{chunk-WGCASEEQ.js → chunk-AYGWJ2UK.js} +2 -2
- package/dist/{chunk-JUOJGZPV.js → chunk-AYP2FZFA.js} +2 -2
- package/dist/{chunk-6DYCSSHN.js → chunk-BHI5ILU5.js} +2 -2
- package/dist/{chunk-LE2UESGS.js → chunk-BOTSR5S3.js} +2 -2
- package/dist/{chunk-5WIGM42T.js → chunk-C3PKRFZ4.js} +2 -2
- package/dist/{chunk-AJLQH4RO.js → chunk-D2XUSZ24.js} +9 -9
- package/dist/{chunk-P36WML7L.js → chunk-D6WX5FEF.js} +2 -2
- package/dist/{chunk-LLFMZYFS.js → chunk-D7QX26FF.js} +2 -2
- package/dist/{chunk-6VLOKESS.js → chunk-DH536WT2.js} +2 -2
- package/dist/{chunk-KHLNIDWB.js → chunk-DNO6QWCB.js} +2 -2
- package/dist/{chunk-UB4UVALS.js → chunk-E73SVGJA.js} +2 -2
- package/dist/{chunk-BHXY5FXL.js → chunk-ECLY4NZA.js} +2 -2
- package/dist/{chunk-OTGIAJQ2.js → chunk-EZV43XBD.js} +2 -2
- package/dist/{chunk-2WMXZYMS.js → chunk-F5V7KRQS.js} +2 -2
- package/dist/{chunk-SKYYPVBW.js → chunk-FO6IK4IF.js} +8 -8
- package/dist/{chunk-Z2OKRO3E.js → chunk-HRCWDO6S.js} +2 -2
- package/dist/{chunk-PZHN7R35.js → chunk-IC3PM3YO.js} +2 -2
- package/dist/{chunk-KQAFEZQX.js → chunk-IPLYGWQF.js} +5 -5
- package/dist/{chunk-RBHQA2UU.js → chunk-IST6MBYC.js} +36 -36
- package/dist/{chunk-N57PUWUE.js → chunk-IYHHCSF6.js} +30 -30
- package/dist/{chunk-U3ZRHNNG.js → chunk-JHUODJRW.js} +2 -2
- package/dist/{chunk-C3YEB7CR.js → chunk-JOP7W24J.js} +4 -4
- package/dist/{chunk-DHTOYRDE.js → chunk-JTPAXDSX.js} +2 -2
- package/dist/{chunk-LX32KV2C.js → chunk-KOEDSRF5.js} +2 -2
- package/dist/{chunk-JI5Q6EOY.js → chunk-LR7KUQ2W.js} +5 -5
- package/dist/{chunk-YKWWG3CM.js → chunk-MECKA6G2.js} +2 -2
- package/dist/{chunk-YA5M7OC4.js → chunk-ML5FW2RO.js} +2 -2
- package/dist/{chunk-T3OLYUU6.js → chunk-MQMZO23A.js} +2 -2
- package/dist/{chunk-RS53SDCA.js → chunk-MVMNEU65.js} +2 -2
- package/dist/{chunk-SNOAFXKX.js → chunk-OLJYDGGP.js} +9 -1
- package/dist/chunk-OLJYDGGP.js.map +1 -0
- package/dist/{chunk-7VTQETLT.js → chunk-OP5IBPHP.js} +2 -2
- package/dist/{chunk-TUSY4KRE.js → chunk-OQZYHCWM.js} +16 -16
- package/dist/{chunk-JVM3CEGL.js → chunk-PA5EOTRR.js} +3 -3
- package/dist/{chunk-K26LFJE4.js → chunk-PMLFPVYJ.js} +2 -2
- package/dist/{chunk-EST3J4LX.js → chunk-PPNFKQ6D.js} +2 -2
- package/dist/{chunk-DNAD57VY.js → chunk-PQWICTRO.js} +1 -1
- package/dist/chunk-PQWICTRO.js.map +1 -0
- package/dist/{chunk-EHUUXLLA.js → chunk-QSP4Y73Q.js} +2 -2
- package/dist/{chunk-AIKUSXIS.js → chunk-QXQCPNKA.js} +4 -4
- package/dist/{chunk-HIBNECBQ.js → chunk-QZDUNK6V.js} +6 -6
- package/dist/{chunk-UE7L4EDU.js → chunk-SRMBMMTK.js} +3 -3
- package/dist/{chunk-T3F2HWIG.js → chunk-TZ44E732.js} +2 -2
- package/dist/{chunk-ZDGSHXJU.js → chunk-UI6R2YUB.js} +1 -1
- package/dist/{chunk-NDWBT2VV.js → chunk-UVEBBXB5.js} +2 -2
- package/dist/{chunk-AOMVOC2E.js → chunk-W6V7JN2E.js} +3 -3
- package/dist/{chunk-4WSR7MYV.js → chunk-X6B2QWXW.js} +2 -2
- package/dist/{chunk-WHSNLCRO.js → chunk-XTMABIFW.js} +2 -2
- package/dist/{chunk-IIDXU2CY.js → chunk-XUKUMBP3.js} +2 -2
- package/dist/{chunk-SYJZUVTN.js → chunk-ZBAQMSEY.js} +3 -3
- package/dist/{chunk-3MX4GWJO.js → chunk-ZUNYFDI4.js} +2 -2
- package/dist/{cli-CdJiBilM.d.ts → cli-BHDMJqJS.d.ts} +5 -5
- package/dist/cli.d.ts +17 -16
- package/dist/cli.js +59 -58
- package/dist/coding/export-okf-codegraph.d.ts +2 -1
- package/dist/coding/export-okf-codegraph.js +6 -6
- package/dist/coding/pre-action-gate.d.ts +2 -1
- package/dist/compounding/engine.d.ts +3 -2
- package/dist/compounding/engine.js +7 -7
- package/dist/compounding/preference-consolidator.d.ts +2 -1
- package/dist/compression-optimizer.d.ts +2 -1
- package/dist/config.d.ts +2 -1
- package/dist/config.js +2 -1
- package/dist/connectors/codex-materialize-runner.d.ts +2 -1
- package/dist/connectors/codex-materialize-runner.js +7 -7
- package/dist/connectors/codex-materialize.d.ts +2 -1
- package/dist/connectors/index.d.ts +2 -1
- package/dist/connectors/index.js +7 -7
- package/dist/consolidation-provenance-check.d.ts +3 -2
- package/dist/consolidation-undo.d.ts +3 -2
- package/dist/contradiction/index.d.ts +5 -4
- package/dist/converge-config.d.ts +2 -1
- package/dist/convergence-refresh.d.ts +3 -2
- package/dist/convergence-refresh.js +9 -9
- package/dist/conversation-index/backend.d.ts +2 -1
- package/dist/conversation-index/chunker.d.ts +2 -1
- package/dist/conversation-index/faiss-adapter.d.ts +2 -1
- package/dist/conversation-index/indexer.d.ts +2 -1
- package/dist/conversation-index/search.d.ts +2 -1
- package/dist/corpus-watermark.d.ts +2 -1
- package/dist/corpus-watermark.js +9 -9
- package/dist/cross-lingual-recall.d.ts +2 -1
- package/dist/day-summary.d.ts +2 -1
- package/dist/deep-recall-http-glue.d.ts +15 -14
- package/dist/deep-recall-http-glue.js +4 -4
- package/dist/deep-recall-mcp-tools.d.ts +15 -14
- package/dist/deep-recall-policy-llm.d.ts +3 -2
- package/dist/deep-recall-seeds.d.ts +2 -1
- package/dist/delinearize.d.ts +2 -1
- package/dist/dependency-propagation-config.d.ts +2 -1
- package/dist/{dependency-propagation-delivery-BgHy44WX.d.ts → dependency-propagation-delivery-BMkR4gmY.d.ts} +1 -1
- package/dist/direct-answer-wiring.d.ts +2 -1
- package/dist/direct-answer.d.ts +2 -1
- package/dist/embedding-fallback.d.ts +2 -1
- package/dist/enrichment/index.d.ts +2 -1
- package/dist/entity-origin-fields.d.ts +2 -1
- package/dist/entity-retrieval.d.ts +3 -2
- package/dist/entity-retrieval.js +7 -7
- package/dist/entity-schema.d.ts +2 -1
- package/dist/episodic-context.d.ts +2 -1
- package/dist/eval-trajectory-contracts.d.ts +2 -1
- package/dist/eval-trajectory-store.d.ts +2 -1
- package/dist/eval-trajectory.d.ts +2 -1
- package/dist/explicit-capture.d.ts +12 -11
- package/dist/external-wiki-access.d.ts +15 -14
- package/dist/external-wiki-access.js +24 -24
- package/dist/external-wiki-collection-registration.d.ts +2 -1
- package/dist/external-wiki-collection.d.ts +2 -1
- package/dist/external-wiki-mcp-tools.d.ts +15 -14
- package/dist/extraction-error-classification.d.ts +2 -1
- package/dist/extraction-evidence-class.d.ts +2 -1
- package/dist/extraction-faithfulness.d.ts +3 -2
- package/dist/extraction-judge-telemetry.d.ts +3 -2
- package/dist/extraction-judge-training.d.ts +3 -2
- package/dist/extraction-judge.d.ts +3 -2
- package/dist/extraction-liveness.d.ts +2 -1
- package/dist/extraction-normalization.d.ts +2 -1
- package/dist/extraction-prompt.d.ts +2 -1
- package/dist/extraction-source-grounding-rules.d.ts +2 -1
- package/dist/extraction-source-grounding.d.ts +2 -1
- package/dist/extraction-span-config.d.ts +2 -1
- package/dist/extraction-span-materialize.d.ts +2 -1
- package/dist/extraction-span-wire.d.ts +2 -1
- package/dist/extraction.d.ts +3 -2
- package/dist/extraction.js +7 -7
- package/dist/fallback-llm-codex-subscription.d.ts +2 -1
- package/dist/fallback-llm.d.ts +2 -1
- package/dist/{generic-recall-paths-BoueWXYr.d.ts → generic-recall-paths-BCRtXU32.d.ts} +1 -1
- package/dist/graph-append-error.d.ts +2 -1
- package/dist/graph-dashboard-diff.d.ts +2 -1
- package/dist/graph-dashboard-key.d.ts +2 -1
- package/dist/graph-dashboard-parser.d.ts +2 -1
- package/dist/graph-edge-reinforcement.d.ts +2 -1
- package/dist/graph-jsonl.d.ts +2 -1
- package/dist/graph-path-reconstruction.d.ts +2 -1
- package/dist/graph-path-scoring.d.ts +2 -1
- package/dist/graph-snapshot.d.ts +2 -1
- package/dist/graph.d.ts +2 -1
- package/dist/harmonic-retrieval.js +1 -1
- package/dist/importance.d.ts +2 -1
- package/dist/importers/index.d.ts +2 -1
- package/dist/importers/test-utils.d.ts +2 -1
- package/dist/in-flight-reads.d.ts +2 -1
- package/dist/{index-cwbrXoxe.d.ts → index-DMNTW4W6.d.ts} +3 -3
- package/dist/index.d.ts +27 -26
- package/dist/index.js +112 -76
- package/dist/intent.d.ts +2 -1
- package/dist/lcm/engine.d.ts +2 -1
- package/dist/lcm/index.d.ts +2 -1
- package/dist/lcm/index.js +3 -3
- package/dist/lcm/tools.d.ts +2 -1
- package/dist/lifecycle.d.ts +2 -1
- package/dist/live-connectors-runner.d.ts +2 -1
- package/dist/{local-llm-B0vd_rp9.d.ts → local-llm-BXYC-mbU.d.ts} +1 -1
- package/dist/local-llm-helpers.d.ts +3 -2
- package/dist/local-llm.d.ts +3 -2
- package/dist/local-model-endpoint.d.ts +2 -1
- package/dist/location/index.d.ts +6 -5
- package/dist/location/index.js +9 -9
- package/dist/maintenance/memory-governance.d.ts +2 -1
- package/dist/maintenance/memory-governance.js +7 -7
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.d.ts +3 -2
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +7 -7
- package/dist/maintenance/rebuild-memory-projection.d.ts +3 -2
- package/dist/maintenance/rebuild-memory-projection.js +8 -8
- package/dist/{maintenance-DJgQkoLh.d.ts → maintenance-zJiLsKBY.d.ts} +3 -3
- package/dist/mcp-memory-inspector-app.d.ts +15 -14
- package/dist/memory-action-policy.d.ts +2 -1
- package/dist/memory-browse-http-glue.d.ts +15 -14
- package/dist/memory-browse-mcp-tools.d.ts +15 -14
- package/dist/memory-browse-operations.js +24 -24
- package/dist/memory-browse.d.ts +2 -1
- package/dist/memory-cache.d.ts +2 -1
- package/dist/memory-controller.d.ts +2 -1
- package/dist/memory-lifecycle-ledger-utils.d.ts +2 -1
- package/dist/memory-projection-store.d.ts +2 -1
- package/dist/memory-provenance.d.ts +2 -1
- package/dist/memory-snapshot.d.ts +2 -1
- package/dist/memory-subject-cli.d.ts +15 -14
- package/dist/memory-subject.d.ts +15 -14
- package/dist/memory-worth-outcomes.d.ts +3 -2
- package/dist/models-json.d.ts +2 -1
- package/dist/namespaces/migrate.d.ts +4 -3
- package/dist/namespaces/migrate.js +8 -8
- package/dist/namespaces/principal.d.ts +2 -1
- package/dist/namespaces/search.d.ts +2 -1
- package/dist/namespaces/search.js +3 -3
- package/dist/namespaces/storage.d.ts +4 -3
- package/dist/namespaces/storage.js +7 -7
- package/dist/native-knowledge.d.ts +2 -1
- package/dist/offline-sync-impression-drain.d.ts +2 -1
- package/dist/operator-doctor-corpus.d.ts +2 -1
- package/dist/operator-doctor-corpus.js +10 -10
- package/dist/operator-doctor-replica.d.ts +2 -1
- package/dist/operator-toolkit.d.ts +4 -3
- package/dist/operator-toolkit.js +17 -16
- package/dist/orchestration/compression-guideline-coordinator.d.ts +3 -2
- package/dist/orchestration/maintenance.d.ts +5 -4
- package/dist/orchestration/maintenance.js +15 -15
- package/dist/{orchestrator-Kx5zwNUm.d.ts → orchestrator-BTqT3F6X.d.ts} +10 -10
- package/dist/orchestrator.d.ts +12 -11
- package/dist/orchestrator.js +74 -73
- package/dist/patterns-cli.d.ts +2 -1
- package/dist/{pipeline-9SYxxign.d.ts → pipeline-CgO2Sd4O.d.ts} +1 -1
- package/dist/policy-runtime.d.ts +2 -1
- package/dist/proactive-contention.d.ts +3 -2
- package/dist/provenance.d.ts +2 -1
- package/dist/{public-http-Bvol6Jr4.d.ts → public-http-Dk1EA9ix.d.ts} +1 -1
- package/dist/{qmd-DjwuG9Xa.d.ts → qmd-CwxtVz64.d.ts} +1 -1
- package/dist/qmd-preflight.d.ts +3 -2
- package/dist/qmd-recall-cache.d.ts +2 -1
- package/dist/qmd.d.ts +3 -2
- package/dist/recall-concurrency-config.d.ts +2 -1
- package/dist/recall-disclosure-escalation.d.ts +2 -1
- package/dist/recall-explain-renderer.d.ts +2 -1
- package/dist/recall-explain-renderer.js +3 -3
- package/dist/recall-memory-map.d.ts +2 -1
- package/dist/recall-navigate-access.d.ts +2 -1
- package/dist/recall-navigate-access.js +2 -2
- package/dist/recall-navigate-disclosure.d.ts +2 -1
- package/dist/recall-navigate-disclosure.js +2 -2
- package/dist/recall-navigate.d.ts +2 -1
- package/dist/recall-navigate.js +2 -2
- package/dist/recall-navigation-http-glue.d.ts +15 -14
- package/dist/recall-navigation-mcp-tools.d.ts +15 -14
- package/dist/recall-navigation-operations.js +24 -24
- package/dist/recall-navigation-renderer.d.ts +2 -1
- package/dist/recall-navigation.d.ts +2 -1
- package/dist/recall-navigation.js +23 -23
- package/dist/recall-planner-llm.d.ts +2 -1
- package/dist/recall-recognition-tier.d.ts +121 -0
- package/dist/recall-recognition-tier.js +42 -0
- package/dist/recall-recognition-tier.js.map +1 -0
- package/dist/recall-state-view-admission.d.ts +3 -2
- package/dist/recall-state-view-anchors.d.ts +2 -1
- package/dist/recall-state.d.ts +2 -1
- package/dist/recall-tag-filter.d.ts +2 -1
- package/dist/recall-timings.d.ts +2 -1
- package/dist/recall-xray-cli.d.ts +2 -1
- package/dist/recall-xray-cli.js +4 -4
- package/dist/recall-xray-renderer.d.ts +2 -1
- package/dist/recall-xray-renderer.js +3 -3
- package/dist/recall-xray.d.ts +2 -1
- package/dist/recall-xray.js +2 -2
- package/dist/reconcile/cursor.d.ts +2 -1
- package/dist/reconcile/manifest.d.ts +2 -1
- package/dist/reconcile/plan.d.ts +2 -1
- package/dist/replay/normalizers/chatgpt.d.ts +2 -1
- package/dist/replay/normalizers/claude.d.ts +2 -1
- package/dist/replay/normalizers/openclaw.d.ts +2 -1
- package/dist/replay/normalizers/shared.d.ts +2 -1
- package/dist/replay/runner.d.ts +2 -1
- package/dist/replay/types.d.ts +2 -1
- package/dist/replica-divergence.d.ts +2 -1
- package/dist/replica-peers-config.d.ts +2 -1
- package/dist/resolve-auth-token.d.ts +2 -1
- package/dist/resume-bundles.js +3 -2
- package/dist/retrieval-agents.d.ts +3 -2
- package/dist/retrieval-tiers.d.ts +2 -1
- package/dist/routing/engine.d.ts +2 -1
- package/dist/routing/store.d.ts +2 -1
- package/dist/salvage-envelope.d.ts +2 -1
- package/dist/{scope-profiles-DPegZtRf.d.ts → scope-profiles-Bg1rB-Dp.d.ts} +1 -1
- package/dist/search/embed-helper.d.ts +2 -1
- package/dist/search/factory.d.ts +2 -1
- package/dist/search/factory.js +2 -2
- package/dist/search/index.d.ts +2 -1
- package/dist/search/index.js +4 -4
- package/dist/search/lancedb-backend.d.ts +2 -1
- package/dist/search/meilisearch-backend.d.ts +2 -1
- package/dist/search/noop-backend.d.ts +2 -1
- package/dist/search/orama-backend.d.ts +2 -1
- package/dist/search/port.d.ts +2 -1
- package/dist/search/remote-backend.d.ts +2 -1
- package/dist/{semantic-consolidation-DMzhKRYl.d.ts → semantic-consolidation-DEzRyeyn.d.ts} +1 -1
- package/dist/semantic-consolidation.d.ts +3 -2
- package/dist/semantic-consolidation.js +8 -8
- package/dist/semantic-rule-promotion.js +7 -7
- package/dist/semantic-rule-verifier.d.ts +2 -1
- package/dist/semantic-rule-verifier.js +7 -7
- package/dist/{service-E99KuPvi.d.ts → service-DGEIDOeD.d.ts} +2 -2
- package/dist/session-observer-bands.d.ts +2 -1
- package/dist/session-observer-state.d.ts +2 -1
- package/dist/shared-context/manager.d.ts +2 -1
- package/dist/signal.d.ts +2 -1
- package/dist/source-agent-qualifier.d.ts +2 -1
- package/dist/source-agent-qualifier.js +7 -7
- package/dist/standup/build.js +7 -7
- package/dist/{storage-BS7ux8F7.d.ts → storage-DNQWtOSM.d.ts} +1 -1
- package/dist/storage.d.ts +3 -2
- package/dist/storage.js +6 -6
- package/dist/subject-config.d.ts +2 -1
- package/dist/summarizer.d.ts +2 -1
- package/dist/summary-snapshot.d.ts +2 -1
- package/dist/support-passport/index.d.ts +18 -17
- package/dist/support-passport/index.js +26 -26
- package/dist/{tagging-BhOEcSXP.d.ts → tagging-CPVwgadt.d.ts} +2 -2
- package/dist/temporal-supersession.d.ts +3 -2
- package/dist/temporal-timeline-recall.d.ts +2 -1
- package/dist/temporal-validity.d.ts +2 -1
- package/dist/threading.d.ts +2 -1
- package/dist/tier-migration.d.ts +3 -2
- package/dist/tier-routing.d.ts +2 -1
- package/dist/topics.d.ts +2 -1
- package/dist/transcript.d.ts +2 -1
- package/dist/transfer/autodetect.js +1 -1
- package/dist/transfer/backup.js +1 -1
- package/dist/transfer/capsule-export.js +2 -2
- package/dist/transfer/export-okf.d.ts +2 -1
- package/dist/transfer/export-okf.js +7 -7
- package/dist/trust-score-stage.d.ts +2 -1
- package/dist/trust-score.d.ts +2 -1
- package/dist/{types-DQ2H8gAC.d.ts → types-DCB90q3g.d.ts} +2 -1
- package/dist/types.d.ts +2 -1
- package/dist/types.js +1 -1
- package/dist/utility-runtime.d.ts +2 -1
- package/dist/verified-recall.js +7 -7
- package/dist/who-knows-cli.d.ts +15 -14
- package/dist/who-knows-cli.js +8 -8
- package/dist/who-knows.d.ts +15 -14
- package/dist/who-knows.js +7 -7
- package/dist/write-envelope.d.ts +2 -1
- package/package.json +2 -2
- package/src/config.ts +5 -0
- package/src/index.ts +1 -0
- package/src/lifecycle/seed-graduation.test.ts +424 -0
- package/src/lifecycle/seed-graduation.ts +376 -0
- package/src/recall-recognition-tier.test.ts +306 -0
- package/src/recall-recognition-tier.ts +375 -0
- package/src/testing/subjects/seed-graduation.test.ts +128 -0
- package/src/types.ts +2 -0
- package/dist/chunk-DNAD57VY.js.map +0 -1
- package/dist/chunk-SNOAFXKX.js.map +0 -1
- /package/dist/{chunk-QQA6VTOY.js.map → chunk-3MIZ2EKQ.js.map} +0 -0
- /package/dist/{chunk-PAQ7YGJY.js.map → chunk-3RZB7TYL.js.map} +0 -0
- /package/dist/{chunk-FVRZDZ7J.js.map → chunk-3SI3FFTW.js.map} +0 -0
- /package/dist/{chunk-WNMVUWI7.js.map → chunk-3UQIS2GC.js.map} +0 -0
- /package/dist/{chunk-FLZJMXFV.js.map → chunk-42BV46XS.js.map} +0 -0
- /package/dist/{chunk-ZYZW46HZ.js.map → chunk-4FWNMN5R.js.map} +0 -0
- /package/dist/{chunk-EBLKS57G.js.map → chunk-4PXFLDMJ.js.map} +0 -0
- /package/dist/{chunk-XQFHKPNB.js.map → chunk-4SDO74N2.js.map} +0 -0
- /package/dist/{chunk-YOC3EEZH.js.map → chunk-56C7OO3N.js.map} +0 -0
- /package/dist/{chunk-76RTJ533.js.map → chunk-6525YIYL.js.map} +0 -0
- /package/dist/{chunk-PSQRBCGR.js.map → chunk-AL7OLBL7.js.map} +0 -0
- /package/dist/{chunk-WGCASEEQ.js.map → chunk-AYGWJ2UK.js.map} +0 -0
- /package/dist/{chunk-JUOJGZPV.js.map → chunk-AYP2FZFA.js.map} +0 -0
- /package/dist/{chunk-6DYCSSHN.js.map → chunk-BHI5ILU5.js.map} +0 -0
- /package/dist/{chunk-LE2UESGS.js.map → chunk-BOTSR5S3.js.map} +0 -0
- /package/dist/{chunk-5WIGM42T.js.map → chunk-C3PKRFZ4.js.map} +0 -0
- /package/dist/{chunk-AJLQH4RO.js.map → chunk-D2XUSZ24.js.map} +0 -0
- /package/dist/{chunk-P36WML7L.js.map → chunk-D6WX5FEF.js.map} +0 -0
- /package/dist/{chunk-LLFMZYFS.js.map → chunk-D7QX26FF.js.map} +0 -0
- /package/dist/{chunk-6VLOKESS.js.map → chunk-DH536WT2.js.map} +0 -0
- /package/dist/{chunk-KHLNIDWB.js.map → chunk-DNO6QWCB.js.map} +0 -0
- /package/dist/{chunk-UB4UVALS.js.map → chunk-E73SVGJA.js.map} +0 -0
- /package/dist/{chunk-BHXY5FXL.js.map → chunk-ECLY4NZA.js.map} +0 -0
- /package/dist/{chunk-OTGIAJQ2.js.map → chunk-EZV43XBD.js.map} +0 -0
- /package/dist/{chunk-2WMXZYMS.js.map → chunk-F5V7KRQS.js.map} +0 -0
- /package/dist/{chunk-SKYYPVBW.js.map → chunk-FO6IK4IF.js.map} +0 -0
- /package/dist/{chunk-Z2OKRO3E.js.map → chunk-HRCWDO6S.js.map} +0 -0
- /package/dist/{chunk-PZHN7R35.js.map → chunk-IC3PM3YO.js.map} +0 -0
- /package/dist/{chunk-KQAFEZQX.js.map → chunk-IPLYGWQF.js.map} +0 -0
- /package/dist/{chunk-RBHQA2UU.js.map → chunk-IST6MBYC.js.map} +0 -0
- /package/dist/{chunk-N57PUWUE.js.map → chunk-IYHHCSF6.js.map} +0 -0
- /package/dist/{chunk-U3ZRHNNG.js.map → chunk-JHUODJRW.js.map} +0 -0
- /package/dist/{chunk-C3YEB7CR.js.map → chunk-JOP7W24J.js.map} +0 -0
- /package/dist/{chunk-DHTOYRDE.js.map → chunk-JTPAXDSX.js.map} +0 -0
- /package/dist/{chunk-LX32KV2C.js.map → chunk-KOEDSRF5.js.map} +0 -0
- /package/dist/{chunk-JI5Q6EOY.js.map → chunk-LR7KUQ2W.js.map} +0 -0
- /package/dist/{chunk-YKWWG3CM.js.map → chunk-MECKA6G2.js.map} +0 -0
- /package/dist/{chunk-YA5M7OC4.js.map → chunk-ML5FW2RO.js.map} +0 -0
- /package/dist/{chunk-T3OLYUU6.js.map → chunk-MQMZO23A.js.map} +0 -0
- /package/dist/{chunk-RS53SDCA.js.map → chunk-MVMNEU65.js.map} +0 -0
- /package/dist/{chunk-7VTQETLT.js.map → chunk-OP5IBPHP.js.map} +0 -0
- /package/dist/{chunk-TUSY4KRE.js.map → chunk-OQZYHCWM.js.map} +0 -0
- /package/dist/{chunk-JVM3CEGL.js.map → chunk-PA5EOTRR.js.map} +0 -0
- /package/dist/{chunk-K26LFJE4.js.map → chunk-PMLFPVYJ.js.map} +0 -0
- /package/dist/{chunk-EST3J4LX.js.map → chunk-PPNFKQ6D.js.map} +0 -0
- /package/dist/{chunk-EHUUXLLA.js.map → chunk-QSP4Y73Q.js.map} +0 -0
- /package/dist/{chunk-AIKUSXIS.js.map → chunk-QXQCPNKA.js.map} +0 -0
- /package/dist/{chunk-HIBNECBQ.js.map → chunk-QZDUNK6V.js.map} +0 -0
- /package/dist/{chunk-UE7L4EDU.js.map → chunk-SRMBMMTK.js.map} +0 -0
- /package/dist/{chunk-T3F2HWIG.js.map → chunk-TZ44E732.js.map} +0 -0
- /package/dist/{chunk-ZDGSHXJU.js.map → chunk-UI6R2YUB.js.map} +0 -0
- /package/dist/{chunk-NDWBT2VV.js.map → chunk-UVEBBXB5.js.map} +0 -0
- /package/dist/{chunk-AOMVOC2E.js.map → chunk-W6V7JN2E.js.map} +0 -0
- /package/dist/{chunk-4WSR7MYV.js.map → chunk-X6B2QWXW.js.map} +0 -0
- /package/dist/{chunk-WHSNLCRO.js.map → chunk-XTMABIFW.js.map} +0 -0
- /package/dist/{chunk-IIDXU2CY.js.map → chunk-XUKUMBP3.js.map} +0 -0
- /package/dist/{chunk-SYJZUVTN.js.map → chunk-ZBAQMSEY.js.map} +0 -0
- /package/dist/{chunk-3MX4GWJO.js.map → chunk-ZUNYFDI4.js.map} +0 -0
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Corroboration-graduated seed memories (issue #2974) — foundation layer.
|
|
3
|
+
*
|
|
4
|
+
* A `pending_review` memory is a SEED: it must not enter active recall on its
|
|
5
|
+
* own say-so. This module decides, deterministically, when later evidence has
|
|
6
|
+
* corroborated a seed enough to graduate it to `active` in place:
|
|
7
|
+
*
|
|
8
|
+
* - corroboration is token-coverage similarity (same recall tokenizer the
|
|
9
|
+
* wearable trust pipeline uses — no LLM on this path),
|
|
10
|
+
* - the corroborating memory must come from an INDEPENDENT provenance: a
|
|
11
|
+
* different session than the seed's, a different source when session
|
|
12
|
+
* anchors are absent, and never a lineage descendant of the seed,
|
|
13
|
+
* - echo is suppressed: when the caller can show the evidence's session
|
|
14
|
+
* RECALLED the seed (the store's own content quoted back), that evidence
|
|
15
|
+
* never counts — otherwise recall would manufacture its own confirmation.
|
|
16
|
+
*
|
|
17
|
+
* Promotion itself reuses the existing in-place `pending_review -> active`
|
|
18
|
+
* machinery (`StorageManager.promoteWearableMemory`, the same status flip the
|
|
19
|
+
* wearable cross-device corroboration path uses) — no parallel write path.
|
|
20
|
+
* Every graduation stamps the corroborating evidence ids on the promoted row
|
|
21
|
+
* (audit surface; the lifecycle ledger already records the status transition).
|
|
22
|
+
*
|
|
23
|
+
* Foundation scope: the gate, the pass, and the config parser. Wiring the key
|
|
24
|
+
* into `parseConfig` (config.ts sits at its size ratchet), the conservative
|
|
25
|
+
* preset pin, scheduling inside the lifecycle sweep, contradiction holds, and
|
|
26
|
+
* the docs/manifest surface are follow-up layers on this issue.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
import { normalizeRecallTokens } from "../recall-tokenization.js";
|
|
30
|
+
import { stripAttributesSuffix } from "../storage.js";
|
|
31
|
+
import { STRUCTURED_ATTRIBUTE_LIMITS } from "../write-envelope.js";
|
|
32
|
+
import { excludeSupportPassportPrivateMemories } from "../support-passport/card-projection.js";
|
|
33
|
+
import type { MemoryFile, MemoryFrontmatter, MemoryStatus } from "../types.js";
|
|
34
|
+
|
|
35
|
+
// ---------------------------------------------------------------------------
|
|
36
|
+
// Config
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
|
|
39
|
+
export interface SeedGraduationConfig {
|
|
40
|
+
/** Master gate. Default false: review-mode stays the only promotion path. */
|
|
41
|
+
enabled: boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Independent corroborating memories required before a seed graduates.
|
|
44
|
+
* `Infinity` is not a config value — `enabled: false` is the disabled state.
|
|
45
|
+
*/
|
|
46
|
+
minCorroborations: number;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export const SEED_GRADUATION_DEFAULTS: SeedGraduationConfig = {
|
|
50
|
+
enabled: false,
|
|
51
|
+
minCorroborations: 2,
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const SEED_GRADUATION_MAX_CORROBORATIONS = 50;
|
|
55
|
+
|
|
56
|
+
const BOOLEAN_LIKE: Record<string, boolean> = {
|
|
57
|
+
true: true, "1": true, yes: true, on: true,
|
|
58
|
+
false: false, "0": false, no: false, off: false,
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
function parseFlag(src: Record<string, unknown>, key: string, fallback: boolean): boolean {
|
|
62
|
+
const raw = src[key];
|
|
63
|
+
if (raw === undefined || raw === null) return fallback;
|
|
64
|
+
if (typeof raw === "boolean") return raw;
|
|
65
|
+
const coerced = BOOLEAN_LIKE[String(raw).toLowerCase()];
|
|
66
|
+
if (coerced === undefined) {
|
|
67
|
+
throw new Error(
|
|
68
|
+
`seedGraduation.${key} must be a boolean or one of "true"/"false"/"1"/"0"/"yes"/"no"/"on"/"off" (got ${JSON.stringify(raw)}).`,
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
return coerced;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Strict parse (reject, never silently default — same contract as
|
|
76
|
+
* `driftDetection`). Invalid values throw at config load.
|
|
77
|
+
*/
|
|
78
|
+
export function parseSeedGraduationConfig(raw: unknown): SeedGraduationConfig {
|
|
79
|
+
if (raw === undefined || raw === null) return { ...SEED_GRADUATION_DEFAULTS };
|
|
80
|
+
if (typeof raw !== "object" || Array.isArray(raw)) {
|
|
81
|
+
throw new Error(
|
|
82
|
+
`seedGraduation must be an object (got ${JSON.stringify(raw)}). Omit the key to keep seed graduation disabled (issue #2974).`,
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
const src = raw as Record<string, unknown>;
|
|
86
|
+
const rawMin = src.minCorroborations;
|
|
87
|
+
let minCorroborations = SEED_GRADUATION_DEFAULTS.minCorroborations;
|
|
88
|
+
if (rawMin !== undefined && rawMin !== null) {
|
|
89
|
+
const n = typeof rawMin === "number" ? rawMin : Number(rawMin);
|
|
90
|
+
if (!Number.isInteger(n) || n < 1 || n > SEED_GRADUATION_MAX_CORROBORATIONS) {
|
|
91
|
+
throw new Error(
|
|
92
|
+
`seedGraduation.minCorroborations must be an integer in [1, ${SEED_GRADUATION_MAX_CORROBORATIONS}] (got ${JSON.stringify(rawMin)}). Use enabled: false to disable graduation.`,
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
minCorroborations = n;
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
enabled: parseFlag(src, "enabled", SEED_GRADUATION_DEFAULTS.enabled),
|
|
99
|
+
minCorroborations,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// ---------------------------------------------------------------------------
|
|
104
|
+
// Gate
|
|
105
|
+
// ---------------------------------------------------------------------------
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Fraction of the seed's tokens an evidence memory must carry to count as
|
|
109
|
+
* restating it. Mirrors the wearable trust pipeline's existing-memory
|
|
110
|
+
* support coverage (`MEMORY_SUPPORT_COVERAGE` = 0.7).
|
|
111
|
+
*/
|
|
112
|
+
const SEED_CORROBORATION_COVERAGE = 0.7;
|
|
113
|
+
|
|
114
|
+
/** Below this many distinct tokens a seed cannot be corroborated deterministically. */
|
|
115
|
+
const MIN_SEED_TOKENS = 4;
|
|
116
|
+
|
|
117
|
+
/** Structured-attribute key carrying the writing session's anchor, when known. */
|
|
118
|
+
const SESSION_ANCHOR_KEY = "sessionKey";
|
|
119
|
+
|
|
120
|
+
/** Reads a session anchor from frontmatter attributes; absent when unknown. */
|
|
121
|
+
function sessionAnchor(frontmatter: MemoryFrontmatter): string | undefined {
|
|
122
|
+
const value = frontmatter.structuredAttributes?.[SESSION_ANCHOR_KEY];
|
|
123
|
+
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function createdMs(memory: MemoryFile): number {
|
|
127
|
+
const ms = Date.parse(memory.frontmatter.created);
|
|
128
|
+
return Number.isFinite(ms) ? ms : Number.NaN;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/** Evidence rows eligible to corroborate: active or also awaiting review. */
|
|
132
|
+
function isEvidenceStatus(status: MemoryStatus | undefined): boolean {
|
|
133
|
+
return status === undefined || status === "active" || status === "pending_review";
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function hasLineageLink(seed: MemoryFile, evidence: MemoryFile): boolean {
|
|
137
|
+
const seedLineage = seed.frontmatter.lineage;
|
|
138
|
+
if (seedLineage !== undefined && seedLineage.includes(evidence.frontmatter.id)) return true;
|
|
139
|
+
const evidenceLineage = evidence.frontmatter.lineage;
|
|
140
|
+
if (evidenceLineage !== undefined && evidenceLineage.includes(seed.frontmatter.id)) return true;
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Deterministic independence test. A seed can never corroborate itself, so
|
|
146
|
+
* every anchor the two rows share disqualifies the evidence (fail closed):
|
|
147
|
+
*
|
|
148
|
+
* - lineage descent either way is derivation, not corroboration;
|
|
149
|
+
* - when both rows carry session anchors, the SAME session that wrote the
|
|
150
|
+
* seed cannot corroborate it;
|
|
151
|
+
* - without session anchors, provenance identity falls back to the source
|
|
152
|
+
* string — identical provenance is never independent.
|
|
153
|
+
*/
|
|
154
|
+
function isIndependentProvenance(seed: MemoryFile, evidence: MemoryFile): boolean {
|
|
155
|
+
if (hasLineageLink(seed, evidence)) return false;
|
|
156
|
+
const seedSession = sessionAnchor(seed.frontmatter);
|
|
157
|
+
const evidenceSession = sessionAnchor(evidence.frontmatter);
|
|
158
|
+
if (seedSession !== undefined && evidenceSession !== undefined) {
|
|
159
|
+
return seedSession !== evidenceSession;
|
|
160
|
+
}
|
|
161
|
+
return evidence.frontmatter.source !== seed.frontmatter.source;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/** Reads the token coverage of `seed`'s tokens inside `evidence`'s body. */
|
|
165
|
+
function coverageOf(seedTokens: ReadonlySet<string>, evidence: MemoryFile): number {
|
|
166
|
+
if (seedTokens.size === 0) return 0;
|
|
167
|
+
const evidenceTokens = new Set(normalizeRecallTokens(stripAttributesSuffix(evidence.content)));
|
|
168
|
+
let matches = 0;
|
|
169
|
+
for (const token of seedTokens) {
|
|
170
|
+
if (evidenceTokens.has(token)) matches += 1;
|
|
171
|
+
}
|
|
172
|
+
return matches / seedTokens.size;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Per-session recall history seam, satisfied by
|
|
177
|
+
* `RecallHandleHistoryStore.recent(sessionKey)`: the memory-id sets a session
|
|
178
|
+
* has recalled, newest first. Used ONLY to suppress echo — a session that
|
|
179
|
+
* recalled the seed and then produced a lookalike memory confirmed nothing.
|
|
180
|
+
*/
|
|
181
|
+
export type SeedRecallEchoLookup = (sessionKey: string) => readonly (readonly string[])[];
|
|
182
|
+
|
|
183
|
+
export interface SeedGraduationEvidenceRef {
|
|
184
|
+
memoryId: string;
|
|
185
|
+
source: string;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export interface SeedGraduationEvaluation {
|
|
189
|
+
decision: "promote" | "hold";
|
|
190
|
+
corroborationCount: number;
|
|
191
|
+
corroborating: SeedGraduationEvidenceRef[];
|
|
192
|
+
/** Evidence that restated the seed but was excluded as echo/derivation. */
|
|
193
|
+
echoSuppressedCount: number;
|
|
194
|
+
reasons: string[];
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export interface SeedGraduationGateOptions {
|
|
198
|
+
config?: SeedGraduationConfig;
|
|
199
|
+
recalledBySession?: SeedRecallEchoLookup;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export function evaluateSeedGraduation(
|
|
203
|
+
seed: MemoryFile,
|
|
204
|
+
evidence: readonly MemoryFile[],
|
|
205
|
+
options: SeedGraduationGateOptions = {},
|
|
206
|
+
): SeedGraduationEvaluation {
|
|
207
|
+
const config = options.config ?? SEED_GRADUATION_DEFAULTS;
|
|
208
|
+
if (!config.enabled) {
|
|
209
|
+
return {
|
|
210
|
+
decision: "hold",
|
|
211
|
+
corroborationCount: 0,
|
|
212
|
+
corroborating: [],
|
|
213
|
+
echoSuppressedCount: 0,
|
|
214
|
+
reasons: ["seed-graduation-disabled"],
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
const seedTokens = new Set(normalizeRecallTokens(stripAttributesSuffix(seed.content)));
|
|
218
|
+
if (seedTokens.size < MIN_SEED_TOKENS) {
|
|
219
|
+
return {
|
|
220
|
+
decision: "hold",
|
|
221
|
+
corroborationCount: 0,
|
|
222
|
+
corroborating: [],
|
|
223
|
+
echoSuppressedCount: 0,
|
|
224
|
+
reasons: ["seed-too-short-for-deterministic-corroboration"],
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const seedCreated = createdMs(seed);
|
|
229
|
+
const corroborating: SeedGraduationEvidenceRef[] = [];
|
|
230
|
+
const reasons: string[] = [];
|
|
231
|
+
let echoSuppressedCount = 0;
|
|
232
|
+
|
|
233
|
+
for (const candidate of evidence) {
|
|
234
|
+
if (candidate.frontmatter.id === seed.frontmatter.id) continue;
|
|
235
|
+
// Only LATER evidence graduates a seed — anything written at or before
|
|
236
|
+
// the seed's creation is contemporaneous, not corroborating.
|
|
237
|
+
const candidateCreated = createdMs(candidate);
|
|
238
|
+
if (!Number.isFinite(seedCreated) || !Number.isFinite(candidateCreated)) continue;
|
|
239
|
+
if (candidateCreated <= seedCreated) continue;
|
|
240
|
+
if (!isEvidenceStatus(candidate.frontmatter.status)) continue;
|
|
241
|
+
if (coverageOf(seedTokens, candidate) < SEED_CORROBORATION_COVERAGE) continue;
|
|
242
|
+
|
|
243
|
+
if (!isIndependentProvenance(seed, candidate)) {
|
|
244
|
+
echoSuppressedCount += 1;
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
const evidenceSession = sessionAnchor(candidate.frontmatter);
|
|
248
|
+
if (
|
|
249
|
+
options.recalledBySession !== undefined &&
|
|
250
|
+
evidenceSession !== undefined &&
|
|
251
|
+
options
|
|
252
|
+
.recalledBySession(evidenceSession)
|
|
253
|
+
.some((recalledIds) => recalledIds.includes(seed.frontmatter.id))
|
|
254
|
+
) {
|
|
255
|
+
// The store's own recalled content quoted back never counts.
|
|
256
|
+
echoSuppressedCount += 1;
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
corroborating.push({ memoryId: candidate.frontmatter.id, source: candidate.frontmatter.source });
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
if (echoSuppressedCount > 0) reasons.push(`echo-suppressed:${echoSuppressedCount}`);
|
|
263
|
+
if (corroborating.length >= config.minCorroborations) {
|
|
264
|
+
return {
|
|
265
|
+
decision: "promote",
|
|
266
|
+
corroborationCount: corroborating.length,
|
|
267
|
+
corroborating,
|
|
268
|
+
echoSuppressedCount,
|
|
269
|
+
reasons,
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
reasons.push(`corroboration-below-min:${corroborating.length}/${config.minCorroborations}`);
|
|
273
|
+
return {
|
|
274
|
+
decision: "hold",
|
|
275
|
+
corroborationCount: corroborating.length,
|
|
276
|
+
corroborating,
|
|
277
|
+
echoSuppressedCount,
|
|
278
|
+
reasons,
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// ---------------------------------------------------------------------------
|
|
283
|
+
// Pass
|
|
284
|
+
// ---------------------------------------------------------------------------
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Narrow storage seam satisfied by `StorageManager`. Promotion reuses the
|
|
288
|
+
* existing in-place `pending_review -> active` machinery rather than adding
|
|
289
|
+
* a parallel write path.
|
|
290
|
+
*/
|
|
291
|
+
export interface SeedGraduationStorage {
|
|
292
|
+
promoteWearableMemory(
|
|
293
|
+
id: string,
|
|
294
|
+
attributeUpdates: Record<string, string>,
|
|
295
|
+
confidence?: number,
|
|
296
|
+
): Promise<boolean>;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
export interface SeedGraduationPassInput {
|
|
300
|
+
/** Full memory corpus for the namespace being swept (same input the lifecycle policy pass takes). */
|
|
301
|
+
memories: readonly MemoryFile[];
|
|
302
|
+
storage: SeedGraduationStorage;
|
|
303
|
+
config?: SeedGraduationConfig;
|
|
304
|
+
recalledBySession?: SeedRecallEchoLookup;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
export interface SeedGraduationPassSummary {
|
|
308
|
+
evaluated: number;
|
|
309
|
+
promoted: number;
|
|
310
|
+
held: number;
|
|
311
|
+
echoSuppressed: number;
|
|
312
|
+
disabled: boolean;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/** Audit attribute stamped on every graduated row, naming the evidence. */
|
|
316
|
+
function graduationAttributes(evaluation: SeedGraduationEvaluation): Record<string, string> {
|
|
317
|
+
const ids = evaluation.corroborating
|
|
318
|
+
.map((entry) => entry.memoryId)
|
|
319
|
+
.join(",")
|
|
320
|
+
.slice(0, STRUCTURED_ATTRIBUTE_LIMITS.maxValueLength);
|
|
321
|
+
return {
|
|
322
|
+
graduatedBy: "independent-corroboration",
|
|
323
|
+
corroborationCount: String(evaluation.corroborationCount),
|
|
324
|
+
corroboratingMemoryIds: ids,
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* Sweep every `pending_review` seed and graduate the corroborated ones.
|
|
330
|
+
* Zero behavior while disabled (default) — review-mode promotion is
|
|
331
|
+
* untouched until an operator opts in.
|
|
332
|
+
*/
|
|
333
|
+
export async function runSeedGraduationPass(
|
|
334
|
+
input: SeedGraduationPassInput,
|
|
335
|
+
): Promise<SeedGraduationPassSummary> {
|
|
336
|
+
const config = input.config ?? SEED_GRADUATION_DEFAULTS;
|
|
337
|
+
if (!config.enabled) {
|
|
338
|
+
return { evaluated: 0, promoted: 0, held: 0, echoSuppressed: 0, disabled: true };
|
|
339
|
+
}
|
|
340
|
+
const corpus = excludeSupportPassportPrivateMemories(input.memories);
|
|
341
|
+
const evidencePool = corpus.filter((memory) => isEvidenceStatus(memory.frontmatter.status));
|
|
342
|
+
|
|
343
|
+
let promoted = 0;
|
|
344
|
+
let held = 0;
|
|
345
|
+
let echoSuppressed = 0;
|
|
346
|
+
let evaluated = 0;
|
|
347
|
+
|
|
348
|
+
for (const seed of corpus) {
|
|
349
|
+
if (seed.frontmatter.status !== "pending_review") continue;
|
|
350
|
+
// Tombstone-blocked rows need revokeTombstone first; the promotion
|
|
351
|
+
// method refuses them anyway, so skip before evaluating.
|
|
352
|
+
if (seed.frontmatter.blockedBy !== undefined) continue;
|
|
353
|
+
evaluated += 1;
|
|
354
|
+
|
|
355
|
+
const evaluation = evaluateSeedGraduation(seed, evidencePool, {
|
|
356
|
+
config,
|
|
357
|
+
recalledBySession: input.recalledBySession,
|
|
358
|
+
});
|
|
359
|
+
echoSuppressed += evaluation.echoSuppressedCount;
|
|
360
|
+
|
|
361
|
+
if (evaluation.decision === "hold") {
|
|
362
|
+
held += 1;
|
|
363
|
+
continue;
|
|
364
|
+
}
|
|
365
|
+
// A concurrent review resolution wins: promoteWearableMemory returns
|
|
366
|
+
// false when the row is no longer pending_review.
|
|
367
|
+
const wrote = await input.storage.promoteWearableMemory(
|
|
368
|
+
seed.frontmatter.id,
|
|
369
|
+
graduationAttributes(evaluation),
|
|
370
|
+
);
|
|
371
|
+
if (wrote) promoted += 1;
|
|
372
|
+
else held += 1;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
return { evaluated, promoted, held, echoSuppressed, disabled: false };
|
|
376
|
+
}
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* recall-recognition-tier.test.ts — issue #2975 foundation slice.
|
|
3
|
+
*
|
|
4
|
+
* Pins the full-index recognition tier contract:
|
|
5
|
+
* - under-threshold namespaces recall by recognizing against the FULL
|
|
6
|
+
* compact index (every id+description line reaches the recognizer) and
|
|
7
|
+
* never touch vector search;
|
|
8
|
+
* - above-threshold namespaces route to vector search and never call the
|
|
9
|
+
* recognizer;
|
|
10
|
+
* - a missing (or unreadable) index falls back to vector search cleanly,
|
|
11
|
+
* without throwing;
|
|
12
|
+
* - the tier decision is deterministic and pure on (index, maxEntries);
|
|
13
|
+
* - recognizer picks are validated against the index and returned in
|
|
14
|
+
* index order, never recognizer order;
|
|
15
|
+
* - a failing recognizer degrades LOUDLY to vector search, labeled.
|
|
16
|
+
*/
|
|
17
|
+
import assert from "node:assert/strict";
|
|
18
|
+
import os from "node:os";
|
|
19
|
+
import path from "node:path";
|
|
20
|
+
import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
21
|
+
import test from "node:test";
|
|
22
|
+
|
|
23
|
+
import { parseConfig } from "./config.js";
|
|
24
|
+
import {
|
|
25
|
+
buildRecognitionIndex,
|
|
26
|
+
buildRecognitionPrompt,
|
|
27
|
+
decideRecognitionTier,
|
|
28
|
+
loadRecognitionIndex,
|
|
29
|
+
parseRecognizerIds,
|
|
30
|
+
parseRecallRecognitionTier,
|
|
31
|
+
parseRecognitionIndexMaxEntries,
|
|
32
|
+
recallViaRecognitionTier,
|
|
33
|
+
recognitionIndexPath,
|
|
34
|
+
renderRecognitionIndex,
|
|
35
|
+
runRecognitionTier,
|
|
36
|
+
saveRecognitionIndex,
|
|
37
|
+
type RecognitionIndex,
|
|
38
|
+
type RecognitionIndexEntry,
|
|
39
|
+
} from "./recall-recognition-tier.js";
|
|
40
|
+
|
|
41
|
+
function entries(n: number): RecognitionIndexEntry[] {
|
|
42
|
+
const out: RecognitionIndexEntry[] = [];
|
|
43
|
+
for (let i = 1; i <= n; i++) {
|
|
44
|
+
out.push({
|
|
45
|
+
id: `m-${String(i).padStart(3, "0")}`,
|
|
46
|
+
description: `Recognition trigger line ${i} for namespace fixture ${i}.`,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
return out;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async function tmpNamespace(): Promise<string> {
|
|
53
|
+
return mkdtemp(path.join(os.tmpdir(), "remnic-recognition-"));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
test("decideRecognitionTier: deterministic decision table", () => {
|
|
57
|
+
const absent = decideRecognitionTier(null, { maxEntries: 10 });
|
|
58
|
+
assert.equal(absent.tier, "vector");
|
|
59
|
+
assert.equal(absent.reason, "index_absent");
|
|
60
|
+
assert.equal(absent.entriesConsidered, 0);
|
|
61
|
+
|
|
62
|
+
const within = decideRecognitionTier(
|
|
63
|
+
{ version: 1, entries: entries(3) },
|
|
64
|
+
{ maxEntries: 10 },
|
|
65
|
+
);
|
|
66
|
+
assert.equal(within.tier, "recognition");
|
|
67
|
+
assert.equal(within.reason, "index_within_threshold");
|
|
68
|
+
assert.equal(within.entriesConsidered, 3);
|
|
69
|
+
|
|
70
|
+
const atThreshold = decideRecognitionTier(
|
|
71
|
+
{ version: 1, entries: entries(4) },
|
|
72
|
+
{ maxEntries: 4 },
|
|
73
|
+
);
|
|
74
|
+
assert.equal(atThreshold.tier, "recognition");
|
|
75
|
+
|
|
76
|
+
const above = decideRecognitionTier(
|
|
77
|
+
{ version: 1, entries: entries(5) },
|
|
78
|
+
{ maxEntries: 4 },
|
|
79
|
+
);
|
|
80
|
+
assert.equal(above.tier, "vector");
|
|
81
|
+
assert.equal(above.reason, "index_above_threshold");
|
|
82
|
+
assert.equal(above.entriesConsidered, 5);
|
|
83
|
+
|
|
84
|
+
const empty = decideRecognitionTier({ version: 1, entries: [] }, { maxEntries: 10 });
|
|
85
|
+
assert.equal(empty.tier, "recognition");
|
|
86
|
+
assert.equal(empty.reason, "index_within_threshold");
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test("loadRecognitionIndex: missing, corrupt, wrong-shape, and future-version indexes return null without throwing", async () => {
|
|
90
|
+
const dir = await tmpNamespace();
|
|
91
|
+
try {
|
|
92
|
+
// Missing file.
|
|
93
|
+
assert.equal(await loadRecognitionIndex(dir), null);
|
|
94
|
+
|
|
95
|
+
// Corrupt JSON.
|
|
96
|
+
const file = recognitionIndexPath(dir);
|
|
97
|
+
await mkdir(path.dirname(file), { recursive: true });
|
|
98
|
+
await writeFile(file, "{ not json", "utf8");
|
|
99
|
+
assert.equal(await loadRecognitionIndex(dir), null);
|
|
100
|
+
|
|
101
|
+
// Valid JSON, wrong shape.
|
|
102
|
+
await writeFile(file, JSON.stringify({ version: 1, entries: "nope" }), "utf8");
|
|
103
|
+
assert.equal(await loadRecognitionIndex(dir), null);
|
|
104
|
+
|
|
105
|
+
// Future schema version.
|
|
106
|
+
await writeFile(
|
|
107
|
+
file,
|
|
108
|
+
JSON.stringify({ version: 99, entries: entries(2) }),
|
|
109
|
+
"utf8",
|
|
110
|
+
);
|
|
111
|
+
assert.equal(await loadRecognitionIndex(dir), null);
|
|
112
|
+
|
|
113
|
+
// Roundtrip: save then load returns the same index.
|
|
114
|
+
const index: RecognitionIndex = { version: 1, entries: entries(2) };
|
|
115
|
+
await saveRecognitionIndex(dir, index);
|
|
116
|
+
assert.deepEqual(await loadRecognitionIndex(dir), index);
|
|
117
|
+
} finally {
|
|
118
|
+
await rm(dir, { recursive: true, force: true });
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
test("buildRecognitionIndex: drops blank ids, trims descriptions, first id wins", () => {
|
|
123
|
+
const built = buildRecognitionIndex([
|
|
124
|
+
{ id: "m-001", description: " first line " },
|
|
125
|
+
{ id: "", description: "blank id is dropped" },
|
|
126
|
+
{ id: " ", description: "whitespace id is dropped" },
|
|
127
|
+
{ id: "m-001", description: "duplicate id keeps the first description" },
|
|
128
|
+
{ id: "m-002", description: "second line" },
|
|
129
|
+
]);
|
|
130
|
+
assert.deepEqual(built.entries, [
|
|
131
|
+
{ id: "m-001", description: "first line" },
|
|
132
|
+
{ id: "m-002", description: "second line" },
|
|
133
|
+
]);
|
|
134
|
+
assert.equal(built.version, 1);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
test("acceptance: under-threshold namespace uses the full-index path, not vector search", async () => {
|
|
138
|
+
const dir = await tmpNamespace();
|
|
139
|
+
try {
|
|
140
|
+
const index: RecognitionIndex = { version: 1, entries: entries(3) };
|
|
141
|
+
await saveRecognitionIndex(dir, index);
|
|
142
|
+
|
|
143
|
+
const prompts: string[] = [];
|
|
144
|
+
const outcome = await recallViaRecognitionTier({
|
|
145
|
+
memoryDir: dir,
|
|
146
|
+
query: "which memories bear on the question?",
|
|
147
|
+
maxEntries: 10,
|
|
148
|
+
recognize: async (prompt) => {
|
|
149
|
+
prompts.push(prompt);
|
|
150
|
+
return "m-002, m-003";
|
|
151
|
+
},
|
|
152
|
+
vectorSearch: async () => {
|
|
153
|
+
throw new Error("vector search must not run on the recognition tier");
|
|
154
|
+
},
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
assert.equal(outcome.decision.tier, "recognition");
|
|
158
|
+
assert.equal(outcome.decision.reason, "index_within_threshold");
|
|
159
|
+
assert.deepEqual(outcome.ids, ["m-002", "m-003"]);
|
|
160
|
+
assert.deepEqual(outcome.vectorResults, []);
|
|
161
|
+
assert.equal(outcome.degraded, undefined);
|
|
162
|
+
|
|
163
|
+
// Full-index proof: the recognizer saw EVERY index line.
|
|
164
|
+
assert.equal(prompts.length, 1);
|
|
165
|
+
for (const entry of index.entries) {
|
|
166
|
+
assert.ok(prompts[0].includes(`${entry.id}: ${entry.description}`), `prompt must carry ${entry.id}`);
|
|
167
|
+
}
|
|
168
|
+
} finally {
|
|
169
|
+
await rm(dir, { recursive: true, force: true });
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
test("acceptance: above-threshold namespace uses vector search, not the recognizer", async () => {
|
|
174
|
+
const dir = await tmpNamespace();
|
|
175
|
+
try {
|
|
176
|
+
await saveRecognitionIndex(dir, { version: 1, entries: entries(5) });
|
|
177
|
+
|
|
178
|
+
const outcome = await recallViaRecognitionTier({
|
|
179
|
+
memoryDir: dir,
|
|
180
|
+
query: "anything",
|
|
181
|
+
maxEntries: 4,
|
|
182
|
+
recognize: async () => {
|
|
183
|
+
throw new Error("recognizer must not run on the vector tier");
|
|
184
|
+
},
|
|
185
|
+
vectorSearch: async () => ["vec-hit-1", "vec-hit-2"],
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
assert.equal(outcome.decision.tier, "vector");
|
|
189
|
+
assert.equal(outcome.decision.reason, "index_above_threshold");
|
|
190
|
+
assert.deepEqual(outcome.ids, []);
|
|
191
|
+
assert.deepEqual(outcome.vectorResults, ["vec-hit-1", "vec-hit-2"]);
|
|
192
|
+
assert.equal(outcome.degraded, undefined);
|
|
193
|
+
} finally {
|
|
194
|
+
await rm(dir, { recursive: true, force: true });
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
test("acceptance: missing index falls back to vector search cleanly, without error", async () => {
|
|
199
|
+
const dir = await tmpNamespace();
|
|
200
|
+
try {
|
|
201
|
+
const outcome = await recallViaRecognitionTier({
|
|
202
|
+
memoryDir: dir,
|
|
203
|
+
query: "anything",
|
|
204
|
+
maxEntries: 10,
|
|
205
|
+
recognize: async () => {
|
|
206
|
+
throw new Error("recognizer must not run without an index");
|
|
207
|
+
},
|
|
208
|
+
vectorSearch: async () => ["vec-fallback"],
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
assert.equal(outcome.decision.tier, "vector");
|
|
212
|
+
assert.equal(outcome.decision.reason, "index_absent");
|
|
213
|
+
assert.deepEqual(outcome.ids, []);
|
|
214
|
+
assert.deepEqual(outcome.vectorResults, ["vec-fallback"]);
|
|
215
|
+
assert.equal(outcome.degraded, undefined);
|
|
216
|
+
} finally {
|
|
217
|
+
await rm(dir, { recursive: true, force: true });
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
test("loud degradation: a failing recognizer falls back to vector search, labeled", async () => {
|
|
222
|
+
const dir = await tmpNamespace();
|
|
223
|
+
try {
|
|
224
|
+
await saveRecognitionIndex(dir, { version: 1, entries: entries(2) });
|
|
225
|
+
|
|
226
|
+
const outcome = await recallViaRecognitionTier({
|
|
227
|
+
memoryDir: dir,
|
|
228
|
+
query: "anything",
|
|
229
|
+
maxEntries: 10,
|
|
230
|
+
recognize: async () => {
|
|
231
|
+
throw new Error("model down");
|
|
232
|
+
},
|
|
233
|
+
vectorSearch: async () => ["vec-degraded"],
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
assert.equal(outcome.decision.tier, "recognition");
|
|
237
|
+
assert.equal(outcome.degraded, "recognizer_unavailable");
|
|
238
|
+
assert.deepEqual(outcome.ids, []);
|
|
239
|
+
assert.deepEqual(outcome.vectorResults, ["vec-degraded"]);
|
|
240
|
+
} finally {
|
|
241
|
+
await rm(dir, { recursive: true, force: true });
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
test("runRecognitionTier: empty index short-circuits with no model call", async () => {
|
|
246
|
+
let calls = 0;
|
|
247
|
+
const run = await runRecognitionTier("q", [], async () => {
|
|
248
|
+
calls++;
|
|
249
|
+
return "m-001";
|
|
250
|
+
});
|
|
251
|
+
assert.equal(calls, 0);
|
|
252
|
+
assert.equal(run.skipped, true);
|
|
253
|
+
assert.deepEqual(run.ids, []);
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
test("runRecognitionTier: unknown ids are dropped; output is index order, not recognizer order", async () => {
|
|
257
|
+
const idx = entries(4);
|
|
258
|
+
const run = await runRecognitionTier("q", idx, async () =>
|
|
259
|
+
"m-004, hallucinated-id, m-001, m-004",
|
|
260
|
+
);
|
|
261
|
+
assert.deepEqual(run.ids, ["m-001", "m-004"]);
|
|
262
|
+
assert.deepEqual(run.dropped, ["hallucinated-id"]);
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
test("runRecognitionTier and prompt are byte-identical across runs with unchanged inputs", async () => {
|
|
266
|
+
const idx = entries(5);
|
|
267
|
+
const recognizer = async () => "m-003, m-001";
|
|
268
|
+
const first = await runRecognitionTier("same query", idx, recognizer);
|
|
269
|
+
const second = await runRecognitionTier("same query", idx, recognizer);
|
|
270
|
+
assert.deepEqual(first, second);
|
|
271
|
+
assert.equal(
|
|
272
|
+
buildRecognitionPrompt("same query", idx),
|
|
273
|
+
buildRecognitionPrompt("same query", idx),
|
|
274
|
+
);
|
|
275
|
+
assert.equal(renderRecognitionIndex(idx).split("\n").length, 5);
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
test("parseRecognizerIds: tolerant parsing of JSON arrays, lists, and noise", () => {
|
|
279
|
+
assert.deepEqual(parseRecognizerIds('["m-001","m-002"]'), ["m-001", "m-002"]);
|
|
280
|
+
assert.deepEqual(parseRecognizerIds("m-001, m-002"), ["m-001", "m-002"]);
|
|
281
|
+
assert.deepEqual(parseRecognizerIds("m-001\nm-002."), ["m-001", "m-002"]);
|
|
282
|
+
assert.deepEqual(parseRecognizerIds("m-001, m-001, m-002"), ["m-001", "m-002"]);
|
|
283
|
+
assert.deepEqual(parseRecognizerIds(null), []);
|
|
284
|
+
assert.deepEqual(parseRecognizerIds(" "), []);
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
test("config parse helpers: defaults, coercion, and rejection", () => {
|
|
288
|
+
assert.equal(parseRecallRecognitionTier(undefined), false);
|
|
289
|
+
assert.equal(parseRecallRecognitionTier(true), true);
|
|
290
|
+
assert.equal(parseRecallRecognitionTier("false"), false);
|
|
291
|
+
|
|
292
|
+
assert.equal(parseRecognitionIndexMaxEntries(undefined), 500);
|
|
293
|
+
assert.equal(parseRecognitionIndexMaxEntries(128.9), 128);
|
|
294
|
+
assert.throws(() => parseRecognitionIndexMaxEntries(0), /recognitionIndexMaxEntries/);
|
|
295
|
+
assert.throws(() => parseRecognitionIndexMaxEntries("abc"), /recognitionIndexMaxEntries/);
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
test("parseConfig: recognition tier keys parse with byte-identical defaults", () => {
|
|
299
|
+
const config = parseConfig({});
|
|
300
|
+
assert.equal(config.recallRecognitionTier, false);
|
|
301
|
+
assert.equal(config.recognitionIndexMaxEntries, 500);
|
|
302
|
+
|
|
303
|
+
const optedIn = parseConfig({ recallRecognitionTier: true, recognitionIndexMaxEntries: 42 });
|
|
304
|
+
assert.equal(optedIn.recallRecognitionTier, true);
|
|
305
|
+
assert.equal(optedIn.recognitionIndexMaxEntries, 42);
|
|
306
|
+
});
|