@snoai/memory 0.0.0-stage → 1.0.1
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/LICENSE +176 -0
- package/README.md +33 -2
- package/config/b-profile-section-registry.json +118 -0
- package/config/llm/compiled/2026-05-19T230932Z/manifest.json +25 -0
- package/config/llm/compiled/2026-05-19T230932Z/registry-root.json +67 -0
- package/config/llm/compiled/2026-05-19T230932Z/resolved/mem_claw/openai_gpt_5_nano.json +5 -0
- package/config/llm/compiled/2026-05-19T230932Z/resolved/mem_claw/openrouter_auto.json +5 -0
- package/config/llm/compiled/2026-05-19T230932Z/resolved/mem_claw/sno_ai_extract.json +5 -0
- package/config/llm/compiled/2026-05-19T230932Z/source/mem_claw/openai_gpt_5_nano.mda +24 -0
- package/config/llm/compiled/2026-05-19T230932Z/source/mem_claw/openrouter_auto.mda +24 -0
- package/config/llm/compiled/2026-05-19T230932Z/source/mem_claw/sno_ai_extract.mda +24 -0
- package/config/llm/compiled/2026-07-21T001311Z/manifest.json +43 -0
- package/config/llm/compiled/2026-07-21T001311Z/registry-root.json +97 -0
- package/config/llm/compiled/2026-07-21T001311Z/resolved/mem_claw/openai_gpt_5_nano.json +5 -0
- package/config/llm/compiled/2026-07-21T001311Z/resolved/mem_claw/openrouter_auto.json +5 -0
- package/config/llm/compiled/2026-07-21T001311Z/resolved/mem_claw/sno_ai_extract.json +5 -0
- package/config/llm/compiled/2026-07-21T001311Z/resolved/mem_claw/sno_conflict_verdict.json +10 -0
- package/config/llm/compiled/2026-07-21T001311Z/resolved/mem_claw/sno_extract_chat.json +10 -0
- package/config/llm/compiled/2026-07-21T001311Z/resolved/mem_claw/sno_extract_profile.json +10 -0
- package/config/llm/compiled/2026-07-21T001311Z/source/mem_claw/openai_gpt_5_nano.mda +24 -0
- package/config/llm/compiled/2026-07-21T001311Z/source/mem_claw/openrouter_auto.mda +24 -0
- package/config/llm/compiled/2026-07-21T001311Z/source/mem_claw/sno_ai_extract.mda +24 -0
- package/config/llm/compiled/2026-07-21T001311Z/source/mem_claw/sno_conflict_verdict.mda +27 -0
- package/config/llm/compiled/2026-07-21T001311Z/source/mem_claw/sno_extract_chat.mda +27 -0
- package/config/llm/compiled/2026-07-21T001311Z/source/mem_claw/sno_extract_profile.mda +27 -0
- package/config/llm/compiled/2026-08-29T060335Z/manifest.json +43 -0
- package/config/llm/compiled/2026-08-29T060335Z/registry-root.json +97 -0
- package/config/llm/compiled/2026-08-29T060335Z/resolved/mem_claw/openai_gpt_5_nano.json +5 -0
- package/config/llm/compiled/2026-08-29T060335Z/resolved/mem_claw/openrouter_auto.json +5 -0
- package/config/llm/compiled/2026-08-29T060335Z/resolved/mem_claw/sno_ai_extract.json +5 -0
- package/config/llm/compiled/2026-08-29T060335Z/resolved/mem_claw/sno_conflict_verdict.json +10 -0
- package/config/llm/compiled/2026-08-29T060335Z/resolved/mem_claw/sno_extract_chat.json +10 -0
- package/config/llm/compiled/2026-08-29T060335Z/resolved/mem_claw/sno_extract_profile.json +10 -0
- package/config/llm/compiled/2026-08-29T060335Z/source/mem_claw/openai_gpt_5_nano.mda +24 -0
- package/config/llm/compiled/2026-08-29T060335Z/source/mem_claw/openrouter_auto.mda +24 -0
- package/config/llm/compiled/2026-08-29T060335Z/source/mem_claw/sno_ai_extract.mda +24 -0
- package/config/llm/compiled/2026-08-29T060335Z/source/mem_claw/sno_conflict_verdict.mda +27 -0
- package/config/llm/compiled/2026-08-29T060335Z/source/mem_claw/sno_extract_chat.mda +27 -0
- package/config/llm/compiled/2026-08-29T060335Z/source/mem_claw/sno_extract_profile.mda +27 -0
- package/config/llm/compiled/2026-09-04T004828Z/manifest.json +43 -0
- package/config/llm/compiled/2026-09-04T004828Z/registry-root.json +97 -0
- package/config/llm/compiled/2026-09-04T004828Z/resolved/mem_claw/openai_gpt_5_nano.json +5 -0
- package/config/llm/compiled/2026-09-04T004828Z/resolved/mem_claw/openrouter_auto.json +5 -0
- package/config/llm/compiled/2026-09-04T004828Z/resolved/mem_claw/sno_ai_extract.json +5 -0
- package/config/llm/compiled/2026-09-04T004828Z/resolved/mem_claw/sno_conflict_verdict.json +10 -0
- package/config/llm/compiled/2026-09-04T004828Z/resolved/mem_claw/sno_extract_chat.json +10 -0
- package/config/llm/compiled/2026-09-04T004828Z/resolved/mem_claw/sno_extract_profile.json +10 -0
- package/config/llm/compiled/2026-09-04T004828Z/source/mem_claw/openai_gpt_5_nano.mda +24 -0
- package/config/llm/compiled/2026-09-04T004828Z/source/mem_claw/openrouter_auto.mda +24 -0
- package/config/llm/compiled/2026-09-04T004828Z/source/mem_claw/sno_ai_extract.mda +24 -0
- package/config/llm/compiled/2026-09-04T004828Z/source/mem_claw/sno_conflict_verdict.mda +27 -0
- package/config/llm/compiled/2026-09-04T004828Z/source/mem_claw/sno_extract_chat.mda +27 -0
- package/config/llm/compiled/2026-09-04T004828Z/source/mem_claw/sno_extract_profile.mda +27 -0
- package/config/llm/current.json +4 -0
- package/config/llm/source/mem_claw/openai_gpt_5_nano.mda +24 -0
- package/config/llm/source/mem_claw/openrouter_auto.mda +24 -0
- package/config/llm/source/mem_claw/sno_ai_extract.mda +24 -0
- package/config/llm/source/mem_claw/sno_conflict_verdict.mda +27 -0
- package/config/llm/source/mem_claw/sno_extract_chat.mda +27 -0
- package/config/llm/source/mem_claw/sno_extract_profile.mda +27 -0
- package/config/llm.trust.json +37 -0
- package/dist/_types-CUKHSRkS.d.ts +103 -0
- package/dist/access-tracker-BbEvEocb.d.ts +110 -0
- package/dist/adapter-B7Jox9-6.d.ts +69 -0
- package/dist/adapter-XaRHZWZ2.js +333 -0
- package/dist/agent-llm-port-5oXgBpzJ.d.ts +53 -0
- package/dist/all-resources-BQK2ugjB.js +6379 -0
- package/dist/all-resources-Dij6l5h0.d.ts +18 -0
- package/dist/ambient-learning-text-normalizer-CxjATmMp.js +170 -0
- package/dist/ambient-learning-text-normalizer-_fH3LeuK.d.ts +10 -0
- package/dist/atomic-extraction-reply-BJ9XV_9s.js +863 -0
- package/dist/atomic-extraction-skill-CcXmuCqL.js +56 -0
- package/dist/atomic-memory-extraction-C5rmogoY.js +3605 -0
- package/dist/atomic-memory-extraction-d9EG-4ti.d.ts +327 -0
- package/dist/b-profile-section-dictionary-provider-BHkoNqgk.js +1812 -0
- package/dist/b-profile-section-dictionary-provider-BxbksLj3.d.ts +62 -0
- package/dist/best-effort-BPZSzKk0.d.ts +9 -0
- package/dist/calendar-instruction-CzqZbAPG.js +194 -0
- package/dist/capture-policy-detector-CxbHPkMX.d.ts +53 -0
- package/dist/capture-policy-detector-DaMfy9CZ.js +357 -0
- package/dist/classifier-DnbbtwTS.d.ts +272 -0
- package/dist/client.d.ts +34 -0
- package/dist/client.js +306 -0
- package/dist/coding-skin.d.ts +51 -0
- package/dist/coding-skin.js +55 -0
- package/dist/config-CL90qYva.js +2576 -0
- package/dist/connection-3IVXpuPE.d.ts +391 -0
- package/dist/connection-C9YD5sgm.js +297 -0
- package/dist/daily-log-generator-CH_KuWHs.js +469 -0
- package/dist/daily-log-generator-CsQPHl4A.d.ts +73 -0
- package/dist/daily-spend-estimator-DiwzBlo1.d.ts +8 -0
- package/dist/data-bootstrap-Bl3lthzE.d.ts +30 -0
- package/dist/data-bootstrap-P8sie9WA.js +172 -0
- package/dist/data-paths-C7sNdqvP.d.ts +22 -0
- package/dist/derived-suppression-cache-CI0durYx.d.ts +36 -0
- package/dist/diagnostic-encoder.d.ts +6 -0
- package/dist/diagnostic-encoder.js +29 -0
- package/dist/embedding-provider-client-Q_hJonK9.js +294 -0
- package/dist/embedding-provider-client-vynZMuit.d.ts +94 -0
- package/dist/entry-metadata-parser-BIVSMCIk.js +20 -0
- package/dist/error-D-wb-rZw.js +23 -0
- package/dist/error-signals-D8_3vYu4.d.ts +28 -0
- package/dist/error-signals-lMH2uAdV.js +134 -0
- package/dist/errors-CeVRxf9V.d.ts +49 -0
- package/dist/extraction-text-sanitizer-B_AhIecT.d.ts +32 -0
- package/dist/i18n-text-DbD6Qify.d.ts +13 -0
- package/dist/index-0aqS8pVF.d.ts +351 -0
- package/dist/index-BO4RU_mc.d.ts +650 -0
- package/dist/index-DL8AVj0Z.d.ts +571 -0
- package/dist/index.d.ts +65 -0
- package/dist/index.js +1201 -0
- package/dist/intent-analyzer-B5XVrWwe.d.ts +28 -0
- package/dist/internal/config/index.d.ts +2 -0
- package/dist/internal/config/index.js +581 -0
- package/dist/internal/config/plugin-config-schema.d.ts +2 -0
- package/dist/internal/config/plugin-config-schema.js +2 -0
- package/dist/internal/config/product-mode.d.ts +2 -0
- package/dist/internal/config/product-mode.js +36 -0
- package/dist/internal/config/settings.d.ts +2 -0
- package/dist/internal/config/settings.js +265 -0
- package/dist/internal/config/skill-categories.d.ts +4 -0
- package/dist/internal/config/skill-categories.js +68 -0
- package/dist/internal/config/skin-defaults.d.ts +6 -0
- package/dist/internal/config/skin-defaults.js +6 -0
- package/dist/internal/contract/discovery.d.ts +10 -0
- package/dist/internal/contract/discovery.js +53 -0
- package/dist/internal/contract/profile.d.ts +2 -0
- package/dist/internal/contract/profile.js +44 -0
- package/dist/internal/contract/routes.d.ts +2 -0
- package/dist/internal/contract/routes.js +62 -0
- package/dist/internal/engine/bindings/memory-cli-shared.d.ts +47 -0
- package/dist/internal/engine/bindings/memory-cli-shared.js +84 -0
- package/dist/internal/engine/bindings/memory-forget-tool.d.ts +5 -0
- package/dist/internal/engine/bindings/memory-forget-tool.js +66 -0
- package/dist/internal/engine/bindings/memory-list-tool.d.ts +5 -0
- package/dist/internal/engine/bindings/memory-list-tool.js +57 -0
- package/dist/internal/engine/bindings/memory-recall-tool.d.ts +10 -0
- package/dist/internal/engine/bindings/memory-recall-tool.js +283 -0
- package/dist/internal/engine/bindings/memory-reflection-resolve-tool.d.ts +5 -0
- package/dist/internal/engine/bindings/memory-reflection-resolve-tool.js +150 -0
- package/dist/internal/engine/bindings/memory-stats-tool.d.ts +5 -0
- package/dist/internal/engine/bindings/memory-stats-tool.js +43 -0
- package/dist/internal/engine/bindings/memory-store-tool.d.ts +5 -0
- package/dist/internal/engine/bindings/memory-store-tool.js +165 -0
- package/dist/internal/engine/bindings/memory-tool-access.d.ts +16 -0
- package/dist/internal/engine/bindings/memory-tool-access.js +46 -0
- package/dist/internal/engine/bindings/memory-tool-formatting.d.ts +62 -0
- package/dist/internal/engine/bindings/memory-tool-formatting.js +106 -0
- package/dist/internal/engine/bindings/memory-tool-results.d.ts +8 -0
- package/dist/internal/engine/bindings/memory-tool-results.js +41 -0
- package/dist/internal/engine/bindings/memory-tool-schemas.d.ts +109 -0
- package/dist/internal/engine/bindings/memory-tool-schemas.js +75 -0
- package/dist/internal/engine/bindings/memory-update-tool.d.ts +5 -0
- package/dist/internal/engine/bindings/memory-update-tool.js +127 -0
- package/dist/internal/engine/bindings/sno-station-mem-ambient-learning-hook.d.ts +14 -0
- package/dist/internal/engine/bindings/sno-station-mem-ambient-learning-hook.js +248 -0
- package/dist/internal/engine/bindings/sno-station-mem-auto-recall-hook.d.ts +12 -0
- package/dist/internal/engine/bindings/sno-station-mem-auto-recall-hook.js +324 -0
- package/dist/internal/engine/bindings/sno-station-mem-hook-types.d.ts +56 -0
- package/dist/internal/engine/bindings/sno-station-mem-hook-types.js +1 -0
- package/dist/internal/engine/bindings/sno-station-mem-insight-distill-factory.d.ts +14 -0
- package/dist/internal/engine/bindings/sno-station-mem-insight-distill-factory.js +119 -0
- package/dist/internal/engine/bindings/sno-station-mem-message-transcript.d.ts +52 -0
- package/dist/internal/engine/bindings/sno-station-mem-message-transcript.js +145 -0
- package/dist/internal/engine/bindings/sno-station-mem-reset-hook.d.ts +10 -0
- package/dist/internal/engine/bindings/sno-station-mem-reset-hook.js +9 -0
- package/dist/internal/engine/bindings/sno-station-mem-runtime-mode.d.ts +36 -0
- package/dist/internal/engine/bindings/sno-station-mem-runtime-mode.js +104 -0
- package/dist/internal/engine/bindings/sno-station-mem-session-state.d.ts +10 -0
- package/dist/internal/engine/bindings/sno-station-mem-session-state.js +27 -0
- package/dist/internal/engine/eval/trace.d.ts +2 -0
- package/dist/internal/engine/eval/trace.js +151 -0
- package/dist/internal/engine/extraction/ambient-learning-text-normalizer.d.ts +2 -0
- package/dist/internal/engine/extraction/ambient-learning-text-normalizer.js +2 -0
- package/dist/internal/engine/extraction/atomic-memory-extraction.d.ts +2 -0
- package/dist/internal/engine/extraction/atomic-memory-extraction.js +2 -0
- package/dist/internal/engine/extraction/b-profile-section-dictionary-provider.d.ts +2 -0
- package/dist/internal/engine/extraction/b-profile-section-dictionary-provider.js +2 -0
- package/dist/internal/engine/extraction/b-profile-section-rekey.d.ts +49 -0
- package/dist/internal/engine/extraction/b-profile-section-rekey.js +730 -0
- package/dist/internal/engine/extraction/capture-policy-detector.d.ts +2 -0
- package/dist/internal/engine/extraction/capture-policy-detector.js +2 -0
- package/dist/internal/engine/extraction/embedding-provider-client.d.ts +2 -0
- package/dist/internal/engine/extraction/embedding-provider-client.js +2 -0
- package/dist/internal/engine/extraction/extraction-text-sanitizer.d.ts +2 -0
- package/dist/internal/engine/extraction/extraction-text-sanitizer.js +114 -0
- package/dist/internal/engine/extraction/memory-metadata-codec.d.ts +3 -0
- package/dist/internal/engine/extraction/memory-metadata-codec.js +2 -0
- package/dist/internal/engine/extraction/memory-temporality-classifier.d.ts +2 -0
- package/dist/internal/engine/extraction/memory-temporality-classifier.js +2 -0
- package/dist/internal/engine/i18n/all-resources.d.ts +2 -0
- package/dist/internal/engine/i18n/all-resources.js +2 -0
- package/dist/internal/engine/i18n/locales.d.ts +2 -0
- package/dist/internal/engine/i18n/locales.js +19 -0
- package/dist/internal/engine/i18n/res/_types.d.ts +2 -0
- package/dist/internal/engine/i18n/res/_types.js +11 -0
- package/dist/internal/engine/observability/adapter.d.ts +2 -0
- package/dist/internal/engine/observability/adapter.js +2 -0
- package/dist/internal/engine/observability/best-effort.d.ts +2 -0
- package/dist/internal/engine/observability/best-effort.js +41 -0
- package/dist/internal/engine/observability/early-diagnostics.js +2 -0
- package/dist/internal/engine/observability/memory-snapshot.d.ts +2 -0
- package/dist/internal/engine/observability/memory-snapshot.js +44 -0
- package/dist/internal/engine/observability/observable-embedder.d.ts +2 -0
- package/dist/internal/engine/observability/observable-embedder.js +43 -0
- package/dist/internal/engine/observability/observable-llm-client.d.ts +2 -0
- package/dist/internal/engine/observability/observable-llm-client.js +98 -0
- package/dist/internal/engine/observability/observable-memory-store.d.ts +2 -0
- package/dist/internal/engine/observability/observable-memory-store.js +701 -0
- package/dist/internal/engine/observability/observable-retriever.d.ts +2 -0
- package/dist/internal/engine/observability/observable-retriever.js +95 -0
- package/dist/internal/engine/observability/runtime-diagnostics.d.ts +13 -0
- package/dist/internal/engine/observability/runtime-diagnostics.js +96 -0
- package/dist/internal/engine/observability/session-registry.d.ts +2 -0
- package/dist/internal/engine/observability/session-registry.js +58 -0
- package/dist/internal/engine/observability/version-metadata.d.ts +7 -0
- package/dist/internal/engine/observability/version-metadata.js +59 -0
- package/dist/internal/engine/operations/daily-spend-estimator.d.ts +2 -0
- package/dist/internal/engine/operations/daily-spend-estimator.js +97 -0
- package/dist/internal/engine/operations/learning-file-maintenance.d.ts +2 -0
- package/dist/internal/engine/operations/learning-file-maintenance.js +255 -0
- package/dist/internal/engine/operations/memory-tier-promoter.d.ts +2 -0
- package/dist/internal/engine/operations/memory-tier-promoter.js +95 -0
- package/dist/internal/engine/operations/runtime-audit-log.d.ts +3 -0
- package/dist/internal/engine/operations/runtime-audit-log.js +3 -0
- package/dist/internal/engine/provider/canonical-memory-corpus.d.ts +38 -0
- package/dist/internal/engine/provider/canonical-memory-corpus.js +253 -0
- package/dist/internal/engine/provider/provider-registration.d.ts +39 -0
- package/dist/internal/engine/provider/provider-registration.js +2 -0
- package/dist/internal/engine/provider/provider-row-renderer.d.ts +7 -0
- package/dist/internal/engine/provider/provider-row-renderer.js +53 -0
- package/dist/internal/engine/provider/provider-types.d.ts +2 -0
- package/dist/internal/engine/provider/provider-types.js +1 -0
- package/dist/internal/engine/reflection/daily-log-generator.d.ts +2 -0
- package/dist/internal/engine/reflection/daily-log-generator.js +2 -0
- package/dist/internal/engine/reflection/reflection-command-hooks.d.ts +2 -0
- package/dist/internal/engine/reflection/reflection-command-hooks.js +2 -0
- package/dist/internal/engine/reflection/reflection-deps.d.ts +2 -0
- package/dist/internal/engine/reflection/reflection-deps.js +1 -0
- package/dist/internal/engine/reflection/reflection-embedded-generator.d.ts +2 -0
- package/dist/internal/engine/reflection/reflection-embedded-generator.js +2 -0
- package/dist/internal/engine/reflection/reflection-injection-hooks.d.ts +2 -0
- package/dist/internal/engine/reflection/reflection-injection-hooks.js +219 -0
- package/dist/internal/engine/reflection/reflection-lifecycle-hooks.d.ts +2 -0
- package/dist/internal/engine/reflection/reflection-lifecycle-hooks.js +63 -0
- package/dist/internal/engine/reflection/strategy-hook-runner.d.ts +4 -0
- package/dist/internal/engine/reflection/strategy-hook-runner.js +3 -0
- package/dist/internal/engine/rem/classifier.d.ts +2 -0
- package/dist/internal/engine/rem/classifier.js +180 -0
- package/dist/internal/engine/rem/index.d.ts +4 -0
- package/dist/internal/engine/rem/index.js +2551 -0
- package/dist/internal/engine/rem/types.d.ts +2 -0
- package/dist/internal/engine/rem/types.js +40 -0
- package/dist/internal/engine/retrieval/access-tracker.d.ts +2 -0
- package/dist/internal/engine/retrieval/access-tracker.js +314 -0
- package/dist/internal/engine/retrieval/intent-analyzer.d.ts +2 -0
- package/dist/internal/engine/retrieval/intent-analyzer.js +27 -0
- package/dist/internal/engine/retrieval/retrieval-gate.d.ts +2 -0
- package/dist/internal/engine/retrieval/retrieval-gate.js +47 -0
- package/dist/internal/engine/retrieval/retriever.d.ts +2 -0
- package/dist/internal/engine/retrieval/retriever.js +2 -0
- package/dist/internal/engine/security/memory-scope-policy.d.ts +2 -0
- package/dist/internal/engine/security/memory-scope-policy.js +2 -0
- package/dist/internal/engine/security/redact.d.ts +2 -0
- package/dist/internal/engine/security/redact.js +34 -0
- package/dist/internal/engine/security/scopes.d.ts +3 -0
- package/dist/internal/engine/security/scopes.js +3 -0
- package/dist/internal/engine/shared/errors.d.ts +2 -0
- package/dist/internal/engine/shared/errors.js +66 -0
- package/dist/internal/engine/shared/i18n-text.d.ts +2 -0
- package/dist/internal/engine/shared/i18n-text.js +58 -0
- package/dist/internal/engine/shared/iso-date-time.d.ts +16 -0
- package/dist/internal/engine/shared/iso-date-time.js +40 -0
- package/dist/internal/engine/shared/lru.d.ts +2 -0
- package/dist/internal/engine/shared/lru.js +30 -0
- package/dist/internal/engine/shared/memory-kind-policy.d.ts +2 -0
- package/dist/internal/engine/shared/memory-kind-policy.js +64 -0
- package/dist/internal/engine/shared/paths.d.ts +3 -0
- package/dist/internal/engine/shared/paths.js +2 -0
- package/dist/internal/engine/shared/types.d.ts +3 -0
- package/dist/internal/engine/shared/types.js +25 -0
- package/dist/internal/engine/shared/utils.d.ts +2 -0
- package/dist/internal/engine/shared/utils.js +175 -0
- package/dist/internal/engine/telemetry/memory-telemetry-api.d.ts +134 -0
- package/dist/internal/engine/telemetry/memory-telemetry-api.js +730 -0
- package/dist/internal/engine/telemetry/memory-telemetry-observability.d.ts +24 -0
- package/dist/internal/engine/telemetry/memory-telemetry-observability.js +133 -0
- package/dist/internal/engine/telemetry/memory-telemetry-outbox.d.ts +2 -0
- package/dist/internal/engine/telemetry/memory-telemetry-outbox.js +443 -0
- package/dist/internal/engine/telemetry/memory-telemetry-types.d.ts +2 -0
- package/dist/internal/engine/telemetry/memory-telemetry-types.js +21 -0
- package/dist/internal/engine/telemetry/observe-ledger.d.ts +30 -0
- package/dist/internal/engine/telemetry/observe-ledger.js +111 -0
- package/dist/internal/model/agent-llm-port.d.ts +2 -0
- package/dist/internal/model/agent-llm-port.js +1 -0
- package/dist/internal/model/llm-client.d.ts +3 -0
- package/dist/internal/model/llm-client.js +3 -0
- package/dist/internal/model/llm-failure.d.ts +26 -0
- package/dist/internal/model/llm-failure.js +63 -0
- package/dist/internal/model/llm-mode-routing.d.ts +22 -0
- package/dist/internal/model/llm-mode-routing.js +37 -0
- package/dist/internal/store/atomic-memory-cutover-sql.d.ts +13 -0
- package/dist/internal/store/atomic-memory-cutover-sql.js +237 -0
- package/dist/internal/store/connection.d.ts +2 -0
- package/dist/internal/store/connection.js +2 -0
- package/dist/internal/store/data-bootstrap.d.ts +2 -0
- package/dist/internal/store/data-bootstrap.js +2 -0
- package/dist/internal/store/data-paths.d.ts +2 -0
- package/dist/internal/store/data-paths.js +65 -0
- package/dist/internal/store/maintenance.d.ts +75 -0
- package/dist/internal/store/maintenance.js +1401 -0
- package/dist/internal/store/memory-kinds-cutover-migrator.d.ts +28 -0
- package/dist/internal/store/memory-kinds-cutover-migrator.js +415 -0
- package/dist/internal/store/migrations.d.ts +27 -0
- package/dist/internal/store/migrations.js +201 -0
- package/dist/internal/store/sqlite-runtime.d.ts +2 -0
- package/dist/internal/store/sqlite-runtime.js +109 -0
- package/dist/internal/store/store.d.ts +3 -0
- package/dist/internal/store/store.js +2 -0
- package/dist/learning-file-maintenance-9b-Ut6Kv.d.ts +30 -0
- package/dist/llm-client-BSYUAcUb.js +1374 -0
- package/dist/llm-client-DOTSGzhe.d.ts +51 -0
- package/dist/locales-JxGmkG6t.d.ts +7 -0
- package/dist/log-site-catalog.generated-BMKWj-Nx.js +2321 -0
- package/dist/log-site-catalog.json +1 -0
- package/dist/lru-DyoaTyIs.d.ts +14 -0
- package/dist/memdump.d.ts +52 -0
- package/dist/memdump.js +163 -0
- package/dist/memory-entry-projector-DHo8jH10.js +1015 -0
- package/dist/memory-kind-policy-BiuRjSJc.d.ts +23 -0
- package/dist/memory-metadata-codec-DL1sbJiN.js +577 -0
- package/dist/memory-metadata-codec-DUvsYe_X.d.ts +68 -0
- package/dist/memory-runtime-DqUS4cWi.js +19459 -0
- package/dist/memory-scope-policy-1gg5QlWZ.d.ts +67 -0
- package/dist/memory-scope-policy-DICSCURA.js +283 -0
- package/dist/memory-snapshot-CqY1BUUA.d.ts +16 -0
- package/dist/memory-source-order-YMRoeDLd.js +134 -0
- package/dist/memory-store-base-CQ_bHlYA.d.ts +1094 -0
- package/dist/memory-store-shared-DqpWJXZZ.js +158 -0
- package/dist/memory-store-write-validation-B_hDG2zf.js +243 -0
- package/dist/memory-telemetry-incidents-BWA9kcZx.js +8 -0
- package/dist/memory-telemetry-metadata-CA7W8tK6.js +77 -0
- package/dist/memory-telemetry-outbox-ddTdImt7.d.ts +74 -0
- package/dist/memory-telemetry-receipts-MAJecvXQ.d.ts +34 -0
- package/dist/memory-telemetry-receipts-YsxbwOAf.js +91 -0
- package/dist/memory-telemetry-types-CufVmrCs.d.ts +14 -0
- package/dist/memory-temporality-classifier-BUjXG5o7.js +184 -0
- package/dist/memory-temporality-classifier-Brht5jyK.d.ts +43 -0
- package/dist/memory-tier-promoter-CqGxCkue.d.ts +43 -0
- package/dist/memory-tool-dependencies-CatGzI9R.d.ts +14 -0
- package/dist/memory-tool-dependencies-DjUoelnm.js +19 -0
- package/dist/model-call-table-CspopkBg.js +563 -0
- package/dist/model-reply-text-CKopStP2.js +334 -0
- package/dist/observable-embedder-CNNe9gkg.d.ts +17 -0
- package/dist/observable-llm-client-CRuKlefQ.d.ts +21 -0
- package/dist/observable-memory-store-CnGDpOY5.d.ts +73 -0
- package/dist/observable-retriever-Cooc62Qw.d.ts +23 -0
- package/dist/operation-cancellation-CTKWwNHk.js +81 -0
- package/dist/paths-qVF-5uBq.d.ts +1 -0
- package/dist/plugin-config-schema-C0UUbOmG.d.ts +413 -0
- package/dist/plugin-config-schema-YJLyIIcJ.js +361 -0
- package/dist/profile-TWPJ_myr.d.ts +13 -0
- package/dist/profile-section-writer-BhGKPBjw.js +2149 -0
- package/dist/provider-registration-CF1tg8Xy.js +721 -0
- package/dist/provider-runtime-types-DFhtdA6d.d.ts +134 -0
- package/dist/provider-types-CZGm6v88.d.ts +23 -0
- package/dist/redact-BTIZcHQL.d.ts +5 -0
- package/dist/reflection-command-hooks-ClUl-iv6.js +644 -0
- package/dist/reflection-command-hooks-G1-dI2Av.d.ts +32 -0
- package/dist/reflection-deps-Ccm2Jf5W.d.ts +19 -0
- package/dist/reflection-embedded-generator-BRvqV5N-.js +130 -0
- package/dist/reflection-embedded-generator-kfQfKEYV.d.ts +14 -0
- package/dist/reflection-injection-hooks-CS_uhjoD.d.ts +43 -0
- package/dist/reflection-lifecycle-hooks-CRSpUdLi.d.ts +27 -0
- package/dist/relation-dictionary-Cev1kyLe.js +4248 -0
- package/dist/release-download.d.ts +8 -0
- package/dist/release-download.js +32 -0
- package/dist/relevant-memories-context-m8nuFZcM.js +23 -0
- package/dist/rem-batch-executor-Dg2HgROM.js +2 -0
- package/dist/rem-batch-executor-DuC4nYyf.js +31640 -0
- package/dist/rem-consumer-retrieval-CR0dtcHR.js +246 -0
- package/dist/rem-trigger-Hn6od8IV.js +730 -0
- package/dist/resolver-Dy-hnjCR.js +103 -0
- package/dist/results-C3ZLroMb.js +140 -0
- package/dist/retrieval-gate-Bx2Zg7jD.d.ts +31 -0
- package/dist/retriever-Dxss5fRv.d.ts +469 -0
- package/dist/retriever-ZoLvPRHq.js +1757 -0
- package/dist/rolldown-runtime-D7D4PA-g.js +13 -0
- package/dist/routes-DLHT__Ay.js +60 -0
- package/dist/runtime-audit-log-BCeujlEp.js +430 -0
- package/dist/runtime-audit-log-zpQiR9HT.d.ts +149 -0
- package/dist/scopes-CSOn6zdd.js +71 -0
- package/dist/scopes-CtrY-YQ3.d.ts +23 -0
- package/dist/server-CSvxchqg.js +1412 -0
- package/dist/session-registry-DBoE7AvZ.d.ts +14 -0
- package/dist/sidecar/main.d.ts +1 -0
- package/dist/sidecar/main.js +56 -0
- package/dist/signed-registry-constants-Bv0fgZ8Q.d.ts +12 -0
- package/dist/signed-registry-constants-yq3DlG3B.js +82 -0
- package/dist/sno-station-mem-runtime-dependencies-CoWqUMbK.js +30 -0
- package/dist/sno-station-mem-runtime-dependencies-DHESIuPn.d.ts +31 -0
- package/dist/sqlite-runtime-L_ywirTm.d.ts +58 -0
- package/dist/store-C8sUTssE.d.ts +27 -0
- package/dist/store-nPql26YM.js +5830 -0
- package/dist/strategy-hook-runner-Bxy851Xx.d.ts +16 -0
- package/dist/strategy-hook-runner-Djify5Rw.js +110 -0
- package/dist/token-counter-BD1f48Km.js +73 -0
- package/dist/trace-C1JxMfs-.d.ts +86 -0
- package/dist/types-CWdhXLxr.d.ts +477 -0
- package/dist/types-pVomjzqR.d.ts +132 -0
- package/dist/unplaced-candidate-migration-CbQHzcEM.js +940 -0
- package/dist/utils-Ctb9aevJ.d.ts +34 -0
- package/drizzle/0000_initial.sql +10 -0
- package/drizzle/0001_virtual_tables.sql +34 -0
- package/drizzle/0002_add_indexes.sql +11 -0
- package/drizzle/0003_content_hash_lookup_index.sql +2 -0
- package/drizzle/0004_chunk_swap.sql +60 -0
- package/drizzle/0005_drop_legacy_vec_fts.sql +10 -0
- package/drizzle/0006_fts_simple.sql +45 -0
- package/drizzle/0007_restore_simple_fts.sql +40 -0
- package/drizzle/0008_widen_memory_hash_unique_index.sql +4 -0
- package/drizzle/0009_memory_telemetry.sql +109 -0
- package/drizzle/0010_provider_mappings.sql +27 -0
- package/drizzle/0011_db_optimization.sql +82 -0
- package/drizzle/0012_b_profile_lanes.sql +9 -0
- package/drizzle/0013_extraction_timestamps.sql +6 -0
- package/drizzle/0014_extraction_timestamp_source.sql +3 -0
- package/drizzle/0015_task_lifecycle_state.sql +101 -0
- package/drizzle/0016_task_lifecycle_legacy_migration.sql +144 -0
- package/drizzle/0017_unplaced_candidates.sql +15 -0
- package/drizzle/0018_rem_batch_chassis.sql +71 -0
- package/drizzle/0019_profile_recovery_entries.sql +17 -0
- package/drizzle/0020_rem_scan_retry_progress.down.sql +14 -0
- package/drizzle/0020_rem_scan_retry_progress.sql +24 -0
- package/drizzle/0021_rem_two_facet_storage.down.sql +14 -0
- package/drizzle/0021_rem_two_facet_storage.sql +42 -0
- package/drizzle/0022_rem_facet_chunk_versions.down.sql +4 -0
- package/drizzle/0022_rem_facet_chunk_versions.sql +4 -0
- package/drizzle/0023_rem_defect_ledger.sql +40 -0
- package/drizzle/0024_rem_unbuilt_foundations.down.sql +13 -0
- package/drizzle/0024_rem_unbuilt_foundations.sql +65 -0
- package/drizzle/0025_rem_journal_operation_constraint.sql +28 -0
- package/drizzle/0026_rem_journal_detail.sql +1 -0
- package/drizzle/0027_legacy_nodix_namespace.sql +3 -0
- package/drizzle/0028_memory_timezone.sql +23 -0
- package/drizzle/0029_rem_write_hash_symmetry.sql +17 -0
- package/drizzle/0030_atomic_memory_additive.sql +105 -0
- package/drizzle/0031_atomic_extraction_ledger.sql +23 -0
- package/drizzle/0032_atomic_memory_entities.sql +12 -0
- package/drizzle/0033_todo_own_store.sql +99 -0
- package/drizzle/meta/0000_snapshot.json +87 -0
- package/drizzle/meta/_journal.json +244 -0
- package/generated/rem-operations.json +1 -0
- package/package.json +126 -4
- package/scripts/download-simple-tokenizer.mjs +254 -0
- package/settings.default.json +243 -0
- package/skills/extract-atomic-memory/SKILL.md +298 -0
- package/skills/extract-atomic-memory/references/account-for-turns.md +9 -0
- package/skills/extract-atomic-memory/references/calendar-meaning.md +64 -0
- package/skills/extract-atomic-memory/references/capture.md +120 -0
- package/skills/extract-atomic-memory/references/enrichment.md +92 -0
- package/skills/extract-atomic-memory/references/missing-durable-half.md +6 -0
- package/skills/extract-atomic-memory/references/progress-classification.md +21 -0
- package/skills/extract-atomic-memory/references/resolve-subject.md +19 -0
- package/skills/extract-atomic-memory/references/surrounding-context.md +10 -0
- package/skills/extract-atomic-memory/references/user-subject-guard.md +29 -0
- package/skills/judge-group-closure/SKILL.md +25 -0
- package/skills/judge-rem-memory-updates/SKILL.md +56 -0
- package/skills/judge-rem-memory-updates/agents/openai.yaml +4 -0
- package/skills/judge-retired-position/SKILL.md +25 -0
- package/skills/judge-task-lifecycle/SKILL.md +45 -0
- package/skills/key-state-attribute/SKILL.md +16 -0
- package/skills/resolve-entity-identity/SKILL.md +24 -0
- package/skills/resolve-relative-time/SKILL.md +22 -0
- package/sqlite-extensions/LICENSE-simple-tokenizer +28 -0
- package/sqlite-extensions/LICENSE-sqlite-vec +21 -0
- package/sqlite-extensions/linux-arm64/vec0.so +0 -0
- package/sqlite-extensions/linux-x64/vec0.so +0 -0
|
@@ -0,0 +1,2576 @@
|
|
|
1
|
+
import { createLogger } from "@snoai/utils/logger";
|
|
2
|
+
import path, { dirname, join, resolve } from "node:path";
|
|
3
|
+
import { closeSync, existsSync, mkdirSync, openSync, readFileSync, readdirSync, rmSync, statSync, unlinkSync, utimesSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { homedir, totalmem, userInfo } from "node:os";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { LRUCache } from "lru-cache";
|
|
7
|
+
import { isLowercaseCanonicalUUIDv7 } from "@snoai/common-core";
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
import { CHUNKING_VERSION, DEFAULT_AGGREGATION_CONFIG, DEFAULT_CHUNK_CONFIG, HEAD_EXTRACT_MIN_CONTENT_TOKENS, HEAD_EXTRACT_OVERLAP_DROP_RATIO, HEAD_EXTRACT_TOKEN_BUDGET } from "@snoai/chunking";
|
|
10
|
+
//#region ../embedder/dist/index.mjs
|
|
11
|
+
/**
|
|
12
|
+
* Shared embedding constants — single source of truth for all apps.
|
|
13
|
+
* 1024-d is the ONLY supported dimension.
|
|
14
|
+
*/
|
|
15
|
+
const CONSTANTS_DIR = dirname(fileURLToPath(import.meta.url));
|
|
16
|
+
/** Local ONNX model identifier (Hugging Face hub) */
|
|
17
|
+
const LOCAL_EMBEDDING_MODEL = "tss-deposium/pplx-embed-v1-0.6b-onnx-int8-standard";
|
|
18
|
+
/**
|
|
19
|
+
* Pinned HuggingFace revision (commit SHA) for supply-chain integrity.
|
|
20
|
+
* Update this when intentionally upgrading to a newer model version.
|
|
21
|
+
*/
|
|
22
|
+
const LOCAL_EMBEDDING_MODEL_REVISION = "a18fdffe7480e6ea5643acb7757142b33838817a";
|
|
23
|
+
/** Default ONNX Runtime profile for constrained local agents. */
|
|
24
|
+
const LOCAL_EMBEDDING_SESSION_OPTIONS_DEFAULT = {
|
|
25
|
+
graphOptimizationLevel: "extended",
|
|
26
|
+
enableMemPattern: false,
|
|
27
|
+
enableCpuMemArena: false
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Default local embedding model cache directory.
|
|
31
|
+
* Absolute path anchored to the embedder package's own `.cache/models/` so it
|
|
32
|
+
* resolves identically regardless of process CWD. Apps can override via
|
|
33
|
+
* constructor config.
|
|
34
|
+
*/
|
|
35
|
+
const LOCAL_EMBEDDING_CACHE_DIR_DEFAULT = join(CONSTANTS_DIR, "..", ".cache", "models");
|
|
36
|
+
/**
|
|
37
|
+
* Local ONNX embedding provider — PPLX embed v1 0.6B INT8, 1024-d vectors.
|
|
38
|
+
*
|
|
39
|
+
* Self-contained: all params via constructor; no app-level config dependency.
|
|
40
|
+
*/
|
|
41
|
+
const log$1 = createLogger("embedder:local");
|
|
42
|
+
let sharedExtractor;
|
|
43
|
+
let sharedLoading;
|
|
44
|
+
let sharedRefCount = 0;
|
|
45
|
+
let sharedPipelineKey;
|
|
46
|
+
function buildSessionOptions(options) {
|
|
47
|
+
const sessionOptions = {
|
|
48
|
+
graphOptimizationLevel: options.graphOptimizationLevel ?? LOCAL_EMBEDDING_SESSION_OPTIONS_DEFAULT.graphOptimizationLevel,
|
|
49
|
+
enableMemPattern: options.enableMemPattern ?? LOCAL_EMBEDDING_SESSION_OPTIONS_DEFAULT.enableMemPattern,
|
|
50
|
+
enableCpuMemArena: options.enableCpuMemArena ?? LOCAL_EMBEDDING_SESSION_OPTIONS_DEFAULT.enableCpuMemArena,
|
|
51
|
+
freeDimensionOverrides: { batch_size: 1 }
|
|
52
|
+
};
|
|
53
|
+
if (options.executionMode !== void 0) sessionOptions.executionMode = options.executionMode;
|
|
54
|
+
if (options.interOpNumThreads !== void 0) sessionOptions.interOpNumThreads = options.interOpNumThreads;
|
|
55
|
+
if (options.intraOpNumThreads !== void 0) sessionOptions.intraOpNumThreads = options.intraOpNumThreads;
|
|
56
|
+
return sessionOptions;
|
|
57
|
+
}
|
|
58
|
+
function buildPipelineKey(identity) {
|
|
59
|
+
return JSON.stringify({
|
|
60
|
+
cacheDir: identity.cacheDir,
|
|
61
|
+
modelId: identity.modelId,
|
|
62
|
+
revision: identity.revision,
|
|
63
|
+
dtype: identity.dtype,
|
|
64
|
+
sessionOptions: buildSessionOptions(identity.sessionOptions)
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
function isNumericTensorData(value) {
|
|
68
|
+
if (typeof value !== "object" || value === null) return false;
|
|
69
|
+
return "length" in value && typeof value.length === "number";
|
|
70
|
+
}
|
|
71
|
+
function tensorDataToArray(data, length) {
|
|
72
|
+
if (!isNumericTensorData(data)) throw new Error("Local embedding returned non-array tensor data");
|
|
73
|
+
return typeof data.subarray === "function" ? Array.from(data.subarray(0, length)) : Array.from(data).slice(0, length);
|
|
74
|
+
}
|
|
75
|
+
var ModelNotFoundError = class extends Error {
|
|
76
|
+
constructor(modelId) {
|
|
77
|
+
super(`Local embedding model "${modelId}" not found in cacheDir. Download it via Hugging Face CLI or 'npm run model:pull'.`);
|
|
78
|
+
this.name = "ModelNotFoundError";
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
/** Thrown by `countTokens()` before the model (and its tokenizer) has loaded. */
|
|
82
|
+
var ModelNotLoadedError = class extends Error {
|
|
83
|
+
constructor(modelId) {
|
|
84
|
+
super(`Local embedding model "${modelId}" is not loaded yet; await embed() or the embedder warmup before counting tokens.`);
|
|
85
|
+
this.name = "ModelNotLoadedError";
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
var LocalEmbedProvider = class LocalEmbedProvider {
|
|
89
|
+
_disposed = false;
|
|
90
|
+
cacheDir;
|
|
91
|
+
dtype;
|
|
92
|
+
modelId;
|
|
93
|
+
revision;
|
|
94
|
+
nativeDim;
|
|
95
|
+
outputDim;
|
|
96
|
+
pooling;
|
|
97
|
+
sessionOptions;
|
|
98
|
+
pipelineKey;
|
|
99
|
+
offline;
|
|
100
|
+
mirror;
|
|
101
|
+
/**
|
|
102
|
+
* Guard: transformers `env` is process-global — only one cacheDir is allowed per process.
|
|
103
|
+
* Intentionally never reset during normal operation; use `resetStaticState()` only in tests.
|
|
104
|
+
*/
|
|
105
|
+
static configuredCacheDir;
|
|
106
|
+
/** Reset static state — FOR TESTS ONLY. Not safe in production. */
|
|
107
|
+
static resetStaticState() {
|
|
108
|
+
LocalEmbedProvider.configuredCacheDir = void 0;
|
|
109
|
+
sharedExtractor = void 0;
|
|
110
|
+
sharedLoading = void 0;
|
|
111
|
+
sharedRefCount = 0;
|
|
112
|
+
sharedPipelineKey = void 0;
|
|
113
|
+
}
|
|
114
|
+
/** Output dimension exposed to consumers (after Matryoshka truncation, if any). */
|
|
115
|
+
get dimension() {
|
|
116
|
+
return this.outputDim;
|
|
117
|
+
}
|
|
118
|
+
constructor(config) {
|
|
119
|
+
this.cacheDir = resolve(config?.cacheDir ?? LOCAL_EMBEDDING_CACHE_DIR_DEFAULT);
|
|
120
|
+
this.dtype = config?.dtype ?? "q8";
|
|
121
|
+
this.modelId = config?.model ?? "tss-deposium/pplx-embed-v1-0.6b-onnx-int8-standard";
|
|
122
|
+
this.revision = config?.revision ?? (config?.model === void 0 ? "a18fdffe7480e6ea5643acb7757142b33838817a" : void 0);
|
|
123
|
+
this.nativeDim = config?.nativeDim ?? 1024;
|
|
124
|
+
this.outputDim = config?.outputDim ?? this.nativeDim;
|
|
125
|
+
if (this.outputDim > this.nativeDim) throw new Error(`outputDim (${this.outputDim}) cannot exceed nativeDim (${this.nativeDim}) — Matryoshka truncation only shrinks dimensions.`);
|
|
126
|
+
this.pooling = config?.pooling ?? "mean";
|
|
127
|
+
this.sessionOptions = config?.sessionOptions ?? {};
|
|
128
|
+
this.offline = config?.offline ?? false;
|
|
129
|
+
this.mirror = config?.mirror;
|
|
130
|
+
this.pipelineKey = buildPipelineKey({
|
|
131
|
+
cacheDir: this.cacheDir,
|
|
132
|
+
modelId: this.modelId,
|
|
133
|
+
revision: this.revision,
|
|
134
|
+
dtype: this.dtype,
|
|
135
|
+
sessionOptions: this.sessionOptions
|
|
136
|
+
});
|
|
137
|
+
sharedRefCount++;
|
|
138
|
+
}
|
|
139
|
+
async getExtractor() {
|
|
140
|
+
if (this._disposed) throw new Error("LocalEmbedProvider has been disposed");
|
|
141
|
+
this.assertCompatibleSharedPipeline();
|
|
142
|
+
if (sharedExtractor) return sharedExtractor;
|
|
143
|
+
if (sharedLoading) {
|
|
144
|
+
const extractor = await sharedLoading;
|
|
145
|
+
if (this._disposed) throw new Error("LocalEmbedProvider has been disposed");
|
|
146
|
+
if (!sharedExtractor) sharedExtractor = extractor;
|
|
147
|
+
return extractor;
|
|
148
|
+
}
|
|
149
|
+
sharedPipelineKey = this.pipelineKey;
|
|
150
|
+
sharedLoading = this.initPipeline();
|
|
151
|
+
try {
|
|
152
|
+
const extractor = await sharedLoading;
|
|
153
|
+
sharedExtractor = extractor;
|
|
154
|
+
if (this._disposed) throw new Error("LocalEmbedProvider has been disposed");
|
|
155
|
+
return extractor;
|
|
156
|
+
} finally {
|
|
157
|
+
sharedLoading = void 0;
|
|
158
|
+
if (!sharedExtractor && sharedRefCount === 0) sharedPipelineKey = void 0;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
assertCompatibleSharedPipeline() {
|
|
162
|
+
if (sharedPipelineKey === void 0 || sharedPipelineKey === this.pipelineKey) return;
|
|
163
|
+
throw new Error("LocalEmbedProvider shared ONNX pipeline is already loaded with a different model, revision, dtype, cacheDir, or sessionOptions. Dispose all live providers before changing pipeline configuration.");
|
|
164
|
+
}
|
|
165
|
+
async initPipeline() {
|
|
166
|
+
if (LocalEmbedProvider.configuredCacheDir !== void 0 && LocalEmbedProvider.configuredCacheDir !== this.cacheDir) throw new Error(`transformers env is global; LocalEmbedProvider cacheDir must be consistent (existing: ${LocalEmbedProvider.configuredCacheDir}, requested: ${this.cacheDir})`);
|
|
167
|
+
LocalEmbedProvider.configuredCacheDir = this.cacheDir;
|
|
168
|
+
const { env, pipeline } = await import("@huggingface/transformers");
|
|
169
|
+
env.cacheDir = this.cacheDir;
|
|
170
|
+
env.allowRemoteModels = !this.offline;
|
|
171
|
+
env.localModelPath = this.cacheDir;
|
|
172
|
+
env.remotePathTemplate = `{model}/resolve/${this.revision ?? "main"}/`;
|
|
173
|
+
const hfEndpoint = this.mirror?.trim();
|
|
174
|
+
if (hfEndpoint) env.remoteHost = hfEndpoint.endsWith("/") ? hfEndpoint : `${hfEndpoint}/`;
|
|
175
|
+
log$1.info("loading ONNX model", {
|
|
176
|
+
model: this.modelId,
|
|
177
|
+
cacheDir: this.cacheDir,
|
|
178
|
+
dtype: this.dtype,
|
|
179
|
+
nativeDim: this.nativeDim,
|
|
180
|
+
outputDim: this.outputDim,
|
|
181
|
+
sessionOptions: buildSessionOptions(this.sessionOptions)
|
|
182
|
+
}, {
|
|
183
|
+
event_name: "embedder.local.provider.initpipeline",
|
|
184
|
+
file: "packages/embedder/src/local-provider.ts",
|
|
185
|
+
function: "initPipeline",
|
|
186
|
+
site_id: "embedder.local.provider.initpipeline.1"
|
|
187
|
+
});
|
|
188
|
+
const t0 = performance.now();
|
|
189
|
+
try {
|
|
190
|
+
const sessionOptions = buildSessionOptions(this.sessionOptions);
|
|
191
|
+
const extractor = await pipeline("feature-extraction", this.modelId, {
|
|
192
|
+
dtype: this.dtype,
|
|
193
|
+
device: "cpu",
|
|
194
|
+
session_options: sessionOptions
|
|
195
|
+
});
|
|
196
|
+
const durationMs = Math.round(performance.now() - t0);
|
|
197
|
+
log$1.info("ONNX model loaded", {
|
|
198
|
+
durationMs,
|
|
199
|
+
model: this.modelId
|
|
200
|
+
}, {
|
|
201
|
+
event_name: "embedder.local.provider.initpipeline",
|
|
202
|
+
file: "packages/embedder/src/local-provider.ts",
|
|
203
|
+
function: "initPipeline",
|
|
204
|
+
site_id: "embedder.local.provider.initpipeline.2"
|
|
205
|
+
});
|
|
206
|
+
return extractor;
|
|
207
|
+
} catch (error) {
|
|
208
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
209
|
+
if (msg.includes("no such file") || msg.includes("ENOENT") || msg.includes("not found") || msg.includes("Could not locate file")) {
|
|
210
|
+
log$1.error("ONNX model not found", {
|
|
211
|
+
cacheDir: this.cacheDir,
|
|
212
|
+
model: this.modelId
|
|
213
|
+
}, {
|
|
214
|
+
event_name: "embedder.local.provider.initpipeline",
|
|
215
|
+
file: "packages/embedder/src/local-provider.ts",
|
|
216
|
+
function: "initPipeline",
|
|
217
|
+
site_id: "embedder.local.provider.initpipeline.3"
|
|
218
|
+
});
|
|
219
|
+
throw new ModelNotFoundError(this.modelId);
|
|
220
|
+
}
|
|
221
|
+
log$1.error("ONNX model init failed", { error }, {
|
|
222
|
+
event_name: "embedder.local.provider.initpipeline",
|
|
223
|
+
file: "packages/embedder/src/local-provider.ts",
|
|
224
|
+
function: "initPipeline",
|
|
225
|
+
site_id: "embedder.local.provider.initpipeline.4"
|
|
226
|
+
});
|
|
227
|
+
throw error;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
countTokens(text) {
|
|
231
|
+
if (this._disposed) throw new Error("LocalEmbedProvider has been disposed");
|
|
232
|
+
this.assertCompatibleSharedPipeline();
|
|
233
|
+
if (!sharedExtractor) throw new ModelNotLoadedError(this.modelId);
|
|
234
|
+
return sharedExtractor.tokenizer.encode(text, { add_special_tokens: false }).length;
|
|
235
|
+
}
|
|
236
|
+
async embed(text) {
|
|
237
|
+
const output = await (await this.getExtractor())(text, {
|
|
238
|
+
pooling: this.pooling,
|
|
239
|
+
normalize: true
|
|
240
|
+
});
|
|
241
|
+
const lastDimIndex = output.dims.length - 1;
|
|
242
|
+
if (lastDimIndex < 0) throw new Error("Local embedding returned empty tensor dims");
|
|
243
|
+
const dim = output.dims[lastDimIndex];
|
|
244
|
+
if (dim === void 0) throw new Error("Local embedding returned undefined dimension");
|
|
245
|
+
if (dim !== this.nativeDim) throw new Error(`Model "${this.modelId}" produced ${dim}-d native vectors, but provider was configured with nativeDim=${this.nativeDim}.`);
|
|
246
|
+
const native = tensorDataToArray(output.data, dim);
|
|
247
|
+
if (native.length !== this.nativeDim) throw new Error(`Expected ${this.nativeDim}-d native embedding, got ${native.length}-d`);
|
|
248
|
+
if (this.outputDim === this.nativeDim) return native;
|
|
249
|
+
return truncateAndRenormalize(native, this.outputDim);
|
|
250
|
+
}
|
|
251
|
+
async embedDocuments(texts) {
|
|
252
|
+
if (texts.length === 0) return [];
|
|
253
|
+
log$1.debug("batch local embedding", { count: texts.length }, {
|
|
254
|
+
event_name: "embedder.local.provider.embeddocuments",
|
|
255
|
+
file: "packages/embedder/src/local-provider.ts",
|
|
256
|
+
function: "embedDocuments",
|
|
257
|
+
site_id: "embedder.local.provider.embeddocuments.5"
|
|
258
|
+
});
|
|
259
|
+
const results = new Array(texts.length);
|
|
260
|
+
for (let i = 0; i < texts.length; i++) {
|
|
261
|
+
const text = texts[i];
|
|
262
|
+
if (text === void 0) throw new Error(`Missing text at batch index ${i}`);
|
|
263
|
+
results[i] = await this.embed(text);
|
|
264
|
+
}
|
|
265
|
+
return results;
|
|
266
|
+
}
|
|
267
|
+
async dispose() {
|
|
268
|
+
if (this._disposed) return;
|
|
269
|
+
log$1.debug("disposing local embedding provider", {}, {
|
|
270
|
+
event_name: "embedder.local.provider.dispose",
|
|
271
|
+
file: "packages/embedder/src/local-provider.ts",
|
|
272
|
+
function: "dispose",
|
|
273
|
+
site_id: "embedder.local.provider.dispose.6"
|
|
274
|
+
});
|
|
275
|
+
this._disposed = true;
|
|
276
|
+
sharedRefCount = Math.max(0, sharedRefCount - 1);
|
|
277
|
+
if (sharedRefCount > 0) return;
|
|
278
|
+
const pending = sharedLoading;
|
|
279
|
+
if (pending) {
|
|
280
|
+
const extractor = await pending.catch(() => void 0);
|
|
281
|
+
if (sharedRefCount > 0) {
|
|
282
|
+
if (extractor && !sharedExtractor) sharedExtractor = extractor;
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
if (extractor && extractor !== sharedExtractor) await extractor.dispose();
|
|
286
|
+
}
|
|
287
|
+
if (sharedRefCount > 0) return;
|
|
288
|
+
if (sharedExtractor) {
|
|
289
|
+
await sharedExtractor.dispose();
|
|
290
|
+
sharedExtractor = void 0;
|
|
291
|
+
}
|
|
292
|
+
if (sharedLoading === void 0) sharedPipelineKey = void 0;
|
|
293
|
+
}
|
|
294
|
+
async warmup() {
|
|
295
|
+
await this.embed("warmup");
|
|
296
|
+
}
|
|
297
|
+
};
|
|
298
|
+
/**
|
|
299
|
+
* Slice the leading `outputDim` floats and re-L2-normalize.
|
|
300
|
+
*
|
|
301
|
+
* Qwen3 embedding models are trained with Matryoshka representation learning,
|
|
302
|
+
* so the head of a unit-length native vector remains a meaningful (but no
|
|
303
|
+
* longer unit-length) embedding. Re-normalizing keeps cosine similarity
|
|
304
|
+
* comparable across stored vectors of the same truncated dim.
|
|
305
|
+
*/
|
|
306
|
+
function truncateAndRenormalize(native, outputDim) {
|
|
307
|
+
if (outputDim <= 0 || outputDim > native.length) throw new Error(`truncateAndRenormalize: outputDim=${outputDim} out of range for native length ${native.length}`);
|
|
308
|
+
const head = native.slice(0, outputDim);
|
|
309
|
+
let normSq = 0;
|
|
310
|
+
for (let i = 0; i < outputDim; i++) {
|
|
311
|
+
const v = head[i] ?? 0;
|
|
312
|
+
normSq += v * v;
|
|
313
|
+
}
|
|
314
|
+
const norm = Math.sqrt(normSq);
|
|
315
|
+
if (norm === 0) return head;
|
|
316
|
+
for (let i = 0; i < outputDim; i++) head[i] = (head[i] ?? 0) / norm;
|
|
317
|
+
return head;
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* LRU-cached embedding provider wrapper.
|
|
321
|
+
*
|
|
322
|
+
* Wraps any EmbeddingProvider with an LRU cache (TTL + max-size eviction).
|
|
323
|
+
* Extracted from mem-claw's Embedder cache pattern,
|
|
324
|
+
* upgraded to use the `lru-cache` package instead of a hand-rolled Map.
|
|
325
|
+
*/
|
|
326
|
+
/** Shared key prefix for single-text and document-batch cache entries. */
|
|
327
|
+
const PASSAGE_PREFIX = "p:";
|
|
328
|
+
var CachedEmbeddingProvider = class {
|
|
329
|
+
inner;
|
|
330
|
+
cache;
|
|
331
|
+
inflight = /* @__PURE__ */ new Map();
|
|
332
|
+
get dimension() {
|
|
333
|
+
return this.inner.dimension;
|
|
334
|
+
}
|
|
335
|
+
countTokens(text) {
|
|
336
|
+
return this.inner.countTokens(text);
|
|
337
|
+
}
|
|
338
|
+
constructor(provider, config) {
|
|
339
|
+
this.inner = provider;
|
|
340
|
+
this.cache = new LRUCache({
|
|
341
|
+
max: config?.maxSize ?? 256,
|
|
342
|
+
ttl: config?.ttlMs ?? 18e5
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
async getOrLoad(key, load) {
|
|
346
|
+
const cached = this.cache.get(key);
|
|
347
|
+
if (cached) return [...cached];
|
|
348
|
+
const pending = this.inflight.get(key);
|
|
349
|
+
if (pending) return [...await pending];
|
|
350
|
+
const created = (async () => {
|
|
351
|
+
const cloned = [...await load()];
|
|
352
|
+
this.cache.set(key, cloned);
|
|
353
|
+
return cloned;
|
|
354
|
+
})();
|
|
355
|
+
this.inflight.set(key, created);
|
|
356
|
+
try {
|
|
357
|
+
return [...await created];
|
|
358
|
+
} catch (error) {
|
|
359
|
+
if (this.inflight.get(key) === created) this.inflight.delete(key);
|
|
360
|
+
throw error;
|
|
361
|
+
} finally {
|
|
362
|
+
if (this.inflight.get(key) === created) this.inflight.delete(key);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
async embed(text) {
|
|
366
|
+
const key = `${PASSAGE_PREFIX}${text}`;
|
|
367
|
+
return this.getOrLoad(key, async () => this.inner.embed(text));
|
|
368
|
+
}
|
|
369
|
+
async embedDocuments(texts) {
|
|
370
|
+
if (texts.length === 0) return [];
|
|
371
|
+
const results = new Array(texts.length);
|
|
372
|
+
const batchKeys = [];
|
|
373
|
+
const batchTexts = [];
|
|
374
|
+
const batchIndicesByKey = /* @__PURE__ */ new Map();
|
|
375
|
+
for (let i = 0; i < texts.length; i++) {
|
|
376
|
+
const text = texts[i];
|
|
377
|
+
if (text === void 0) continue;
|
|
378
|
+
const key = `${PASSAGE_PREFIX}${text}`;
|
|
379
|
+
const cached = this.cache.get(key);
|
|
380
|
+
if (cached) {
|
|
381
|
+
results[i] = Promise.resolve([...cached]);
|
|
382
|
+
continue;
|
|
383
|
+
}
|
|
384
|
+
const pending = this.inflight.get(key);
|
|
385
|
+
if (pending) {
|
|
386
|
+
results[i] = pending.then((embedding) => [...embedding]);
|
|
387
|
+
continue;
|
|
388
|
+
}
|
|
389
|
+
const existingBatch = batchIndicesByKey.get(key);
|
|
390
|
+
if (existingBatch) {
|
|
391
|
+
existingBatch.push(i);
|
|
392
|
+
continue;
|
|
393
|
+
}
|
|
394
|
+
batchKeys.push(key);
|
|
395
|
+
batchTexts.push(text);
|
|
396
|
+
batchIndicesByKey.set(key, [i]);
|
|
397
|
+
}
|
|
398
|
+
if (batchTexts.length > 0) {
|
|
399
|
+
const batchPromises = /* @__PURE__ */ new Map();
|
|
400
|
+
const freshEmbeddingsPromise = this.inner.embedDocuments(batchTexts).then((freshEmbeddings) => {
|
|
401
|
+
if (freshEmbeddings.length !== batchTexts.length) throw new Error(`inner.embedDocuments returned ${freshEmbeddings.length} results for ${batchTexts.length} inputs`);
|
|
402
|
+
return freshEmbeddings.map((embedding) => [...embedding]);
|
|
403
|
+
});
|
|
404
|
+
for (let j = 0; j < batchKeys.length; j++) {
|
|
405
|
+
const key = batchKeys[j];
|
|
406
|
+
if (key === void 0) continue;
|
|
407
|
+
const indices = batchIndicesByKey.get(key);
|
|
408
|
+
if (indices === void 0) continue;
|
|
409
|
+
const keyPromise = freshEmbeddingsPromise.then((freshEmbeddings) => {
|
|
410
|
+
const embedding = freshEmbeddings[j];
|
|
411
|
+
if (embedding === void 0) throw new Error(`Missing fresh embedding at batch index ${j}`);
|
|
412
|
+
this.cache.set(key, embedding);
|
|
413
|
+
return embedding;
|
|
414
|
+
});
|
|
415
|
+
batchPromises.set(key, keyPromise);
|
|
416
|
+
this.inflight.set(key, keyPromise);
|
|
417
|
+
for (const idx of indices) results[idx] = keyPromise.then((embedding) => [...embedding]);
|
|
418
|
+
}
|
|
419
|
+
const clearInflight = () => {
|
|
420
|
+
for (const [key, keyPromise] of batchPromises) if (this.inflight.get(key) === keyPromise) this.inflight.delete(key);
|
|
421
|
+
};
|
|
422
|
+
freshEmbeddingsPromise.then(clearInflight, clearInflight);
|
|
423
|
+
}
|
|
424
|
+
return Promise.all(results.map((result, index) => {
|
|
425
|
+
if (result === void 0) throw new Error(`Missing embedding at index ${index}`);
|
|
426
|
+
return result;
|
|
427
|
+
}));
|
|
428
|
+
}
|
|
429
|
+
async dispose() {
|
|
430
|
+
this.cache.clear();
|
|
431
|
+
this.inflight.clear();
|
|
432
|
+
if ("dispose" in this.inner && typeof this.inner.dispose === "function") await this.inner.dispose();
|
|
433
|
+
}
|
|
434
|
+
/** Clear the cache without disposing the inner provider */
|
|
435
|
+
clear() {
|
|
436
|
+
this.cache.clear();
|
|
437
|
+
this.inflight.clear();
|
|
438
|
+
}
|
|
439
|
+
/** Current cache size */
|
|
440
|
+
get size() {
|
|
441
|
+
return this.cache.size;
|
|
442
|
+
}
|
|
443
|
+
};
|
|
444
|
+
/**
|
|
445
|
+
* model-download.ts — Shared ONNX model download utility.
|
|
446
|
+
*
|
|
447
|
+
* Idempotent: skips download when a .download-complete marker and .onnx files
|
|
448
|
+
* exist in cacheDir. Interrupted downloads (no marker) trigger re-download.
|
|
449
|
+
* User-facing progress goes to stderr (not stdout) so it doesn't interfere
|
|
450
|
+
* with structured log output or piped commands.
|
|
451
|
+
*/
|
|
452
|
+
const log$2 = createLogger("embedder:model-download");
|
|
453
|
+
/** Marker file written after a successful download to distinguish complete vs partial cache. */
|
|
454
|
+
const DOWNLOAD_COMPLETE_MARKER = ".download-complete";
|
|
455
|
+
/** Lock file for cross-process download serialization (O_EXCL based). */
|
|
456
|
+
const DOWNLOAD_LOCK_FILE = ".download-lock";
|
|
457
|
+
/** Max time (ms) to wait for another process's download before giving up. */
|
|
458
|
+
const LOCK_WAIT_TIMEOUT_MS = 6e5;
|
|
459
|
+
/** Poll interval (ms) when waiting for a competing download to finish. */
|
|
460
|
+
const LOCK_POLL_INTERVAL_MS = 2e3;
|
|
461
|
+
/** Stale lock threshold (ms) — locks older than this are assumed abandoned. */
|
|
462
|
+
const LOCK_STALE_MS = 9e5;
|
|
463
|
+
/** Lock heartbeat interval (ms) while owning the download lock. */
|
|
464
|
+
const LOCK_HEARTBEAT_INTERVAL_MS = 5e3;
|
|
465
|
+
function parseDownloadMarker(raw) {
|
|
466
|
+
try {
|
|
467
|
+
const parsed = JSON.parse(raw);
|
|
468
|
+
const model = parsed.model;
|
|
469
|
+
const revision = parsed.revision;
|
|
470
|
+
const dtype = parsed.dtype;
|
|
471
|
+
const completedAt = parsed.completedAt;
|
|
472
|
+
if (typeof model !== "string" || typeof revision !== "string" || typeof dtype !== "string" || typeof completedAt !== "string") return null;
|
|
473
|
+
return {
|
|
474
|
+
model,
|
|
475
|
+
revision,
|
|
476
|
+
dtype,
|
|
477
|
+
completedAt
|
|
478
|
+
};
|
|
479
|
+
} catch {
|
|
480
|
+
return null;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
const DTYPE_SIZES = {
|
|
484
|
+
q4: "~400MB",
|
|
485
|
+
q8: "~639MB",
|
|
486
|
+
fp16: "~1.2GB",
|
|
487
|
+
fp32: "~2.4GB"
|
|
488
|
+
};
|
|
489
|
+
function getSystemRamGB() {
|
|
490
|
+
return Math.round(totalmem() / 1073741824);
|
|
491
|
+
}
|
|
492
|
+
/**
|
|
493
|
+
* Check if a valid model download exists in cacheDir.
|
|
494
|
+
*
|
|
495
|
+
* When `expectedRevision` and/or `expectedDtype` are provided, the marker
|
|
496
|
+
* metadata is validated against them — a mismatch (e.g. after model upgrade)
|
|
497
|
+
* returns false so the caller re-downloads. Without these params the check
|
|
498
|
+
* is structural only (marker + .onnx files), which is appropriate for
|
|
499
|
+
* lock-wait polling where dtype/revision aren't available.
|
|
500
|
+
*/
|
|
501
|
+
function isModelCached(cacheDir, expectedRevision, expectedDtype) {
|
|
502
|
+
try {
|
|
503
|
+
if (!existsSync(cacheDir)) return false;
|
|
504
|
+
const markerPath = join(cacheDir, DOWNLOAD_COMPLETE_MARKER);
|
|
505
|
+
if (!existsSync(markerPath)) return false;
|
|
506
|
+
const onnxDir = join(cacheDir, LOCAL_EMBEDDING_MODEL, "onnx");
|
|
507
|
+
if (!existsSync(onnxDir) || !readdirSync(onnxDir).some((e) => e.endsWith(".onnx"))) return false;
|
|
508
|
+
if (expectedRevision !== void 0 || expectedDtype !== void 0) {
|
|
509
|
+
const marker = parseDownloadMarker(readFileSync(markerPath, "utf8"));
|
|
510
|
+
if (!marker) {
|
|
511
|
+
log$2.warn("download marker is corrupt, re-download needed", { cacheDir }, {
|
|
512
|
+
event_name: "embedder.model.download.ismodelcached",
|
|
513
|
+
file: "packages/embedder/src/model-download.ts",
|
|
514
|
+
function: "isModelCached",
|
|
515
|
+
site_id: "embedder.model.download.ismodelcached.1"
|
|
516
|
+
});
|
|
517
|
+
return false;
|
|
518
|
+
}
|
|
519
|
+
if (expectedRevision !== void 0 && marker.revision !== expectedRevision) {
|
|
520
|
+
log$2.info("cached model revision mismatch, re-download needed", {
|
|
521
|
+
cached: String(marker.revision),
|
|
522
|
+
expected: expectedRevision
|
|
523
|
+
}, {
|
|
524
|
+
event_name: "embedder.model.download.ismodelcached",
|
|
525
|
+
file: "packages/embedder/src/model-download.ts",
|
|
526
|
+
function: "isModelCached",
|
|
527
|
+
site_id: "embedder.model.download.ismodelcached.2"
|
|
528
|
+
});
|
|
529
|
+
return false;
|
|
530
|
+
}
|
|
531
|
+
if (expectedDtype !== void 0 && marker.dtype !== expectedDtype) {
|
|
532
|
+
log$2.info("cached model dtype mismatch, re-download needed", {
|
|
533
|
+
cached: String(marker.dtype),
|
|
534
|
+
expected: expectedDtype
|
|
535
|
+
}, {
|
|
536
|
+
event_name: "embedder.model.download.ismodelcached",
|
|
537
|
+
file: "packages/embedder/src/model-download.ts",
|
|
538
|
+
function: "isModelCached",
|
|
539
|
+
site_id: "embedder.model.download.ismodelcached.3"
|
|
540
|
+
});
|
|
541
|
+
return false;
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
return true;
|
|
545
|
+
} catch (error) {
|
|
546
|
+
log$2.warn("failed to inspect model cache directory", {
|
|
547
|
+
cacheDir,
|
|
548
|
+
error
|
|
549
|
+
}, {
|
|
550
|
+
event_name: "embedder.model.download.ismodelcached",
|
|
551
|
+
file: "packages/embedder/src/model-download.ts",
|
|
552
|
+
function: "isModelCached",
|
|
553
|
+
site_id: "embedder.model.download.ismodelcached.4"
|
|
554
|
+
});
|
|
555
|
+
return false;
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
function getDirSizeMB(dirPath) {
|
|
559
|
+
let total = 0;
|
|
560
|
+
try {
|
|
561
|
+
const entries = readdirSync(dirPath, { recursive: true });
|
|
562
|
+
for (const entry of entries) {
|
|
563
|
+
if (typeof entry !== "string") continue;
|
|
564
|
+
const fullPath = join(dirPath, entry);
|
|
565
|
+
try {
|
|
566
|
+
const stat = statSync(fullPath);
|
|
567
|
+
if (stat.isFile()) total += stat.size;
|
|
568
|
+
} catch {}
|
|
569
|
+
}
|
|
570
|
+
} catch (error) {
|
|
571
|
+
log$2.warn("failed to read directory for size calculation", {
|
|
572
|
+
dirPath,
|
|
573
|
+
error
|
|
574
|
+
}, {
|
|
575
|
+
event_name: "embedder.model.download.getdirsizemb",
|
|
576
|
+
file: "packages/embedder/src/model-download.ts",
|
|
577
|
+
function: "getDirSizeMB",
|
|
578
|
+
site_id: "embedder.model.download.getdirsizemb.5"
|
|
579
|
+
});
|
|
580
|
+
}
|
|
581
|
+
return Math.round(total / 1048576);
|
|
582
|
+
}
|
|
583
|
+
function getDtypeDisplaySize(dtype) {
|
|
584
|
+
return DTYPE_SIZES[dtype] ?? "unknown size";
|
|
585
|
+
}
|
|
586
|
+
/**
|
|
587
|
+
* Acquire an exclusive download lock using O_EXCL (atomic create-if-not-exists).
|
|
588
|
+
* Returns true if lock acquired, false if another process holds it.
|
|
589
|
+
*/
|
|
590
|
+
function tryAcquireLock(cacheDir) {
|
|
591
|
+
const lockPath = join(cacheDir, DOWNLOAD_LOCK_FILE);
|
|
592
|
+
try {
|
|
593
|
+
mkdirSync(cacheDir, { recursive: true });
|
|
594
|
+
const fd = openSync(lockPath, "wx");
|
|
595
|
+
writeFileSync(fd, `${process.pid}\n${Date.now()}\n`);
|
|
596
|
+
closeSync(fd);
|
|
597
|
+
return true;
|
|
598
|
+
} catch (error) {
|
|
599
|
+
if (error instanceof Error && "code" in error && error.code === "EEXIST") return false;
|
|
600
|
+
throw error;
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
function releaseLock(cacheDir) {
|
|
604
|
+
try {
|
|
605
|
+
unlinkSync(join(cacheDir, DOWNLOAD_LOCK_FILE));
|
|
606
|
+
} catch {}
|
|
607
|
+
}
|
|
608
|
+
function heartbeatLock(cacheDir) {
|
|
609
|
+
const lockPath = join(cacheDir, DOWNLOAD_LOCK_FILE);
|
|
610
|
+
try {
|
|
611
|
+
const now = /* @__PURE__ */ new Date();
|
|
612
|
+
utimesSync(lockPath, now, now);
|
|
613
|
+
} catch {}
|
|
614
|
+
}
|
|
615
|
+
function isLockStale(cacheDir) {
|
|
616
|
+
const lockPath = join(cacheDir, DOWNLOAD_LOCK_FILE);
|
|
617
|
+
try {
|
|
618
|
+
const stat = statSync(lockPath);
|
|
619
|
+
if (Date.now() - stat.mtimeMs > LOCK_STALE_MS) return true;
|
|
620
|
+
const pid = Number.parseInt(readFileSync(lockPath, "utf8").split("\n")[0] ?? "", 10);
|
|
621
|
+
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
622
|
+
try {
|
|
623
|
+
process.kill(pid, 0);
|
|
624
|
+
return false;
|
|
625
|
+
} catch (error) {
|
|
626
|
+
return error instanceof Error && "code" in error && error.code === "ESRCH";
|
|
627
|
+
}
|
|
628
|
+
} catch {
|
|
629
|
+
return true;
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
function sleep(ms) {
|
|
633
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
634
|
+
}
|
|
635
|
+
/**
|
|
636
|
+
* Wait for an existing download lock to be released (another process finishing).
|
|
637
|
+
* If the lock is stale (abandoned), break it and return so the caller can retry.
|
|
638
|
+
*
|
|
639
|
+
* `expectedRevision` / `expectedDtype` are forwarded to `isModelCached` so a
|
|
640
|
+
* concurrent revision/dtype upgrade is not satisfied by a stale matching-shape
|
|
641
|
+
* cache from the previous version. Without them, two processes upgrading the
|
|
642
|
+
* same cacheDir can both return "finished" on the old artifacts.
|
|
643
|
+
*/
|
|
644
|
+
async function waitForLock(cacheDir, expectedRevision, expectedDtype) {
|
|
645
|
+
const lockPath = join(cacheDir, DOWNLOAD_LOCK_FILE);
|
|
646
|
+
const deadline = Date.now() + LOCK_WAIT_TIMEOUT_MS;
|
|
647
|
+
log$2.info("another process is downloading the model, waiting...", { cacheDir }, {
|
|
648
|
+
event_name: "embedder.model.download.waitforlock",
|
|
649
|
+
file: "packages/embedder/src/model-download.ts",
|
|
650
|
+
function: "waitForLock",
|
|
651
|
+
site_id: "embedder.model.download.waitforlock.6"
|
|
652
|
+
});
|
|
653
|
+
process.stderr.write(" Waiting for another download to finish...\n");
|
|
654
|
+
while (Date.now() < deadline) {
|
|
655
|
+
if (!existsSync(lockPath)) return;
|
|
656
|
+
if (isModelCached(cacheDir, expectedRevision, expectedDtype)) return;
|
|
657
|
+
if (isLockStale(cacheDir)) {
|
|
658
|
+
log$2.warn("breaking stale download lock", { cacheDir }, {
|
|
659
|
+
event_name: "embedder.model.download.waitforlock",
|
|
660
|
+
file: "packages/embedder/src/model-download.ts",
|
|
661
|
+
function: "waitForLock",
|
|
662
|
+
site_id: "embedder.model.download.waitforlock.7"
|
|
663
|
+
});
|
|
664
|
+
releaseLock(cacheDir);
|
|
665
|
+
return;
|
|
666
|
+
}
|
|
667
|
+
await sleep(LOCK_POLL_INTERVAL_MS);
|
|
668
|
+
}
|
|
669
|
+
throw new Error(`Timed out (${LOCK_WAIT_TIMEOUT_MS / 1e3}s) waiting for model download lock. Another process may still be downloading. Check ${join(cacheDir, DOWNLOAD_LOCK_FILE)}`);
|
|
670
|
+
}
|
|
671
|
+
/**
|
|
672
|
+
* Ensure the local ONNX embedding model is downloaded. Idempotent.
|
|
673
|
+
*
|
|
674
|
+
* - If .download-complete marker + .onnx files exist in cacheDir, logs "already cached" and returns.
|
|
675
|
+
* - Otherwise, downloads with progress output to stderr.
|
|
676
|
+
* - Resets `env.allowRemoteModels = false` after download to prevent
|
|
677
|
+
* surprise network calls during inference.
|
|
678
|
+
*/
|
|
679
|
+
async function ensureModelDownloaded(opts) {
|
|
680
|
+
const cacheDir = opts?.cacheDir ?? LOCAL_EMBEDDING_CACHE_DIR_DEFAULT;
|
|
681
|
+
const ramGB = getSystemRamGB();
|
|
682
|
+
const dtype = opts?.dtype ?? "q8";
|
|
683
|
+
if (isModelCached(cacheDir, "a18fdffe7480e6ea5643acb7757142b33838817a", dtype)) {
|
|
684
|
+
const sizeMB = getDirSizeMB(cacheDir);
|
|
685
|
+
log$2.info("local embedding model already cached", {
|
|
686
|
+
sizeMB,
|
|
687
|
+
cacheDir
|
|
688
|
+
}, {
|
|
689
|
+
event_name: "embedder.model.download.ensuremodeldownloaded",
|
|
690
|
+
file: "packages/embedder/src/model-download.ts",
|
|
691
|
+
function: "ensureModelDownloaded",
|
|
692
|
+
site_id: "embedder.model.download.ensuremodeldownloaded.8"
|
|
693
|
+
});
|
|
694
|
+
return;
|
|
695
|
+
}
|
|
696
|
+
if (opts?.offline) return;
|
|
697
|
+
const lockWasPresent = existsSync(join(cacheDir, DOWNLOAD_LOCK_FILE));
|
|
698
|
+
if (!tryAcquireLock(cacheDir)) {
|
|
699
|
+
await waitForLock(cacheDir, LOCAL_EMBEDDING_MODEL_REVISION, dtype);
|
|
700
|
+
if (isModelCached(cacheDir, "a18fdffe7480e6ea5643acb7757142b33838817a", dtype)) {
|
|
701
|
+
const sizeMB = getDirSizeMB(cacheDir);
|
|
702
|
+
log$2.info("local embedding model already cached (downloaded by another process)", {
|
|
703
|
+
sizeMB,
|
|
704
|
+
cacheDir
|
|
705
|
+
}, {
|
|
706
|
+
event_name: "embedder.model.download.ensuremodeldownloaded",
|
|
707
|
+
file: "packages/embedder/src/model-download.ts",
|
|
708
|
+
function: "ensureModelDownloaded",
|
|
709
|
+
site_id: "embedder.model.download.ensuremodeldownloaded.9"
|
|
710
|
+
});
|
|
711
|
+
return;
|
|
712
|
+
}
|
|
713
|
+
if (!tryAcquireLock(cacheDir)) throw new Error(`Failed to acquire model download lock after waiting. Another process may still be downloading. Check ${join(cacheDir, DOWNLOAD_LOCK_FILE)}`);
|
|
714
|
+
}
|
|
715
|
+
const modelDir = join(cacheDir, LOCAL_EMBEDDING_MODEL);
|
|
716
|
+
let completed = false;
|
|
717
|
+
let startedEmpty = false;
|
|
718
|
+
const lockHeartbeat = setInterval(() => heartbeatLock(cacheDir), LOCK_HEARTBEAT_INTERVAL_MS);
|
|
719
|
+
try {
|
|
720
|
+
heartbeatLock(cacheDir);
|
|
721
|
+
if (lockWasPresent) {
|
|
722
|
+
rmSync(join(cacheDir, DOWNLOAD_COMPLETE_MARKER), { force: true });
|
|
723
|
+
rmSync(modelDir, {
|
|
724
|
+
recursive: true,
|
|
725
|
+
force: true
|
|
726
|
+
});
|
|
727
|
+
} else if (!existsSync(join(cacheDir, DOWNLOAD_COMPLETE_MARKER)) && existsSync(modelDir) && existsSync(join(modelDir, "onnx")) && readdirSync(join(modelDir, "onnx")).some((e) => e.endsWith(".onnx"))) {
|
|
728
|
+
writeFileSync(join(cacheDir, DOWNLOAD_COMPLETE_MARKER), JSON.stringify({
|
|
729
|
+
model: LOCAL_EMBEDDING_MODEL,
|
|
730
|
+
revision: LOCAL_EMBEDDING_MODEL_REVISION,
|
|
731
|
+
dtype,
|
|
732
|
+
completedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
733
|
+
}));
|
|
734
|
+
completed = true;
|
|
735
|
+
return;
|
|
736
|
+
}
|
|
737
|
+
startedEmpty = !existsSync(modelDir);
|
|
738
|
+
const banner = [
|
|
739
|
+
"",
|
|
740
|
+
"Downloading local embedding model (first run only)",
|
|
741
|
+
`${"─".repeat(52)}`,
|
|
742
|
+
` Model: ${LOCAL_EMBEDDING_MODEL}`,
|
|
743
|
+
` Revision: ${LOCAL_EMBEDDING_MODEL_REVISION.slice(0, 12)}`,
|
|
744
|
+
` Dtype: ${dtype} (${getDtypeDisplaySize(dtype)})`,
|
|
745
|
+
` RAM: ${ramGB}GB`,
|
|
746
|
+
` Cache dir: ${cacheDir}`,
|
|
747
|
+
"",
|
|
748
|
+
" This may take a few minutes on the first run...",
|
|
749
|
+
""
|
|
750
|
+
];
|
|
751
|
+
process.stderr.write(`${banner.join("\n")}\n`);
|
|
752
|
+
log$2.info("starting model download", {
|
|
753
|
+
model: LOCAL_EMBEDDING_MODEL,
|
|
754
|
+
revision: LOCAL_EMBEDDING_MODEL_REVISION,
|
|
755
|
+
dtype,
|
|
756
|
+
cacheDir
|
|
757
|
+
}, {
|
|
758
|
+
event_name: "embedder.model.download.ensuremodeldownloaded",
|
|
759
|
+
file: "packages/embedder/src/model-download.ts",
|
|
760
|
+
function: "ensureModelDownloaded",
|
|
761
|
+
site_id: "embedder.model.download.ensuremodeldownloaded.10"
|
|
762
|
+
});
|
|
763
|
+
const { env, pipeline } = await import("@huggingface/transformers");
|
|
764
|
+
const prevCacheDir = env.cacheDir;
|
|
765
|
+
const prevAllowRemote = env.allowRemoteModels;
|
|
766
|
+
const prevLocalModelPath = env.localModelPath;
|
|
767
|
+
const prevRemoteHost = env.remoteHost;
|
|
768
|
+
const prevRemotePathTemplate = env.remotePathTemplate;
|
|
769
|
+
env.cacheDir = cacheDir;
|
|
770
|
+
env.allowRemoteModels = true;
|
|
771
|
+
env.localModelPath = cacheDir;
|
|
772
|
+
env.remotePathTemplate = `{model}/resolve/${LOCAL_EMBEDDING_MODEL_REVISION}/`;
|
|
773
|
+
if (opts?.mirror) env.remoteHost = opts.mirror.endsWith("/") ? opts.mirror : `${opts.mirror}/`;
|
|
774
|
+
let lastProgress = 0;
|
|
775
|
+
const startTime = performance.now();
|
|
776
|
+
try {
|
|
777
|
+
await pipeline("feature-extraction", LOCAL_EMBEDDING_MODEL, {
|
|
778
|
+
dtype,
|
|
779
|
+
device: "cpu",
|
|
780
|
+
progress_callback: (progress) => {
|
|
781
|
+
if (progress.status === "progress" && typeof progress.progress === "number") {
|
|
782
|
+
const pct = Math.round(progress.progress);
|
|
783
|
+
if (pct >= lastProgress + 10) {
|
|
784
|
+
lastProgress = pct;
|
|
785
|
+
process.stderr.write(` ${pct}% — ${progress.file ?? ""}\n`);
|
|
786
|
+
}
|
|
787
|
+
} else if (progress.status === "done" && progress.file) process.stderr.write(` done ${progress.file}\n`);
|
|
788
|
+
}
|
|
789
|
+
});
|
|
790
|
+
} finally {
|
|
791
|
+
env.cacheDir = prevCacheDir;
|
|
792
|
+
env.allowRemoteModels = prevAllowRemote;
|
|
793
|
+
env.localModelPath = prevLocalModelPath;
|
|
794
|
+
env.remoteHost = prevRemoteHost;
|
|
795
|
+
env.remotePathTemplate = prevRemotePathTemplate;
|
|
796
|
+
}
|
|
797
|
+
writeFileSync(join(cacheDir, DOWNLOAD_COMPLETE_MARKER), JSON.stringify({
|
|
798
|
+
model: LOCAL_EMBEDDING_MODEL,
|
|
799
|
+
revision: LOCAL_EMBEDDING_MODEL_REVISION,
|
|
800
|
+
dtype,
|
|
801
|
+
completedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
802
|
+
}));
|
|
803
|
+
completed = true;
|
|
804
|
+
const elapsedSec = Math.round((performance.now() - startTime) / 1e3);
|
|
805
|
+
const finalSizeMB = getDirSizeMB(cacheDir);
|
|
806
|
+
const summary = [
|
|
807
|
+
"",
|
|
808
|
+
" Download complete!",
|
|
809
|
+
` Size: ${finalSizeMB}MB | Time: ${elapsedSec}s`,
|
|
810
|
+
` Location: ${cacheDir}`,
|
|
811
|
+
""
|
|
812
|
+
];
|
|
813
|
+
process.stderr.write(`${summary.join("\n")}\n`);
|
|
814
|
+
log$2.info("model download complete", {
|
|
815
|
+
model: LOCAL_EMBEDDING_MODEL,
|
|
816
|
+
revision: LOCAL_EMBEDDING_MODEL_REVISION,
|
|
817
|
+
dtype,
|
|
818
|
+
sizeMB: finalSizeMB,
|
|
819
|
+
elapsedSec,
|
|
820
|
+
cacheDir
|
|
821
|
+
}, {
|
|
822
|
+
event_name: "embedder.model.download.ensuremodeldownloaded",
|
|
823
|
+
file: "packages/embedder/src/model-download.ts",
|
|
824
|
+
function: "ensureModelDownloaded",
|
|
825
|
+
site_id: "embedder.model.download.ensuremodeldownloaded.11"
|
|
826
|
+
});
|
|
827
|
+
} finally {
|
|
828
|
+
clearInterval(lockHeartbeat);
|
|
829
|
+
if (!completed && startedEmpty) rmSync(modelDir, {
|
|
830
|
+
recursive: true,
|
|
831
|
+
force: true
|
|
832
|
+
});
|
|
833
|
+
releaseLock(cacheDir);
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
//#endregion
|
|
837
|
+
//#region config/index.ts
|
|
838
|
+
/**
|
|
839
|
+
* Sno Station Memory centralized configuration.
|
|
840
|
+
* Shared tunable constants for every memory skin.
|
|
841
|
+
* Organized by domain: Scoring → Retrieval → Storage → Backup → Cache → Session → Capture → Reflection
|
|
842
|
+
*/
|
|
843
|
+
/** Maximum wait for sidecar requests and resource cleanup during shutdown. */
|
|
844
|
+
const MEMORY_SHUTDOWN_TIMEOUT_MS = 5e3;
|
|
845
|
+
/** Default importance score for new memories (0.0–1.0) */
|
|
846
|
+
const DEFAULT_IMPORTANCE = .7;
|
|
847
|
+
/** Default scope for memories without explicit scope */
|
|
848
|
+
const DEFAULT_SCOPE = "global";
|
|
849
|
+
/** Default embedding vector dimension (Voyage AI 1024-d) */
|
|
850
|
+
const VECTOR_DIMENSION_DEFAULT = 1024;
|
|
851
|
+
/** Production sno.ai host origin used by @snoai/observability. */
|
|
852
|
+
const SNO_OBSERVE_DEFAULT_BASE_URL = "https://www.sno.ai";
|
|
853
|
+
/** Default observability agent id for OpenClaw plugin emits. */
|
|
854
|
+
const SNO_OBSERVE_DEFAULT_AGENT_ID = "openclaw";
|
|
855
|
+
/** Bounded lifecycle observe flush — enough headroom for the SDK to complete one HTTP POST batch. */
|
|
856
|
+
const SNO_OBSERVE_FLUSH_TIMEOUT_MS = 8e3;
|
|
857
|
+
/** Default and minimum character-estimated token budgets for one recall result. */
|
|
858
|
+
const DEFAULT_RECALL_TOKEN_BUDGET = 7e3;
|
|
859
|
+
const MIN_RECALL_TOKEN_BUDGET = 5e3;
|
|
860
|
+
/** Weight for vector (semantic) branch in hybrid score fusion */
|
|
861
|
+
const DEFAULT_VECTOR_WEIGHT = .7;
|
|
862
|
+
/** Weight for BM25 (keyword) branch in hybrid score fusion */
|
|
863
|
+
const DEFAULT_BM25_WEIGHT = .3;
|
|
864
|
+
/**
|
|
865
|
+
* Hard ceiling for candidate pool expansion. Also the retrieval limit `memory_recall` asks for,
|
|
866
|
+
* so it bounds how much of a live store one answer can see. Raised 100 → 512 on 2026-09-06 (owner
|
|
867
|
+
* order): atomic rows are small and many, and at 100 a 159-row live store served 76 rows per
|
|
868
|
+
* question, cutting live to-dos the question asked for; at 512 the whole store came back for
|
|
869
|
+
* 3,476 tokens, half the recall token budget. Raised again 512 -> 2048 on 2026-09-15: the pool is
|
|
870
|
+
* `max(candidatePoolSize, limit * 2)` clamped here, so at 512 a caller asking for more than 256
|
|
871
|
+
* memories silently got fewer, and the ceiling — not the ranking — decided what the model saw.
|
|
872
|
+
*/
|
|
873
|
+
const MAX_CANDIDATE_POOL_SIZE = 2048;
|
|
874
|
+
/**
|
|
875
|
+
* Measurement-only overrides for the block 210 retrieval-breadth experiment.
|
|
876
|
+
*
|
|
877
|
+
* Both are OFF unless the named environment variable is set, and neither is
|
|
878
|
+
* reachable from the plugin configuration schema, so a deployed plugin cannot
|
|
879
|
+
* turn them on: the experiment harness sets them on the process it starts and
|
|
880
|
+
* nothing else does. They exist because the runtime and the schema both stop
|
|
881
|
+
* the candidate pool at `MAX_CANDIDATE_POOL_SIZE`, which makes the pool-200
|
|
882
|
+
* cell impossible to run at all without one enabling edit (PRD 210 DEC-1).
|
|
883
|
+
*
|
|
884
|
+
* Read at call time, not at import time, so a harness that sets the variable
|
|
885
|
+
* after this module loads still gets the value it asked for.
|
|
886
|
+
*/
|
|
887
|
+
function experimentCandidatePoolOverride() {
|
|
888
|
+
const raw = process.env.MEM_CLAW_EXPERIMENT_CANDIDATE_POOL_SIZE?.trim();
|
|
889
|
+
if (!raw) return void 0;
|
|
890
|
+
const parsed = Number(raw);
|
|
891
|
+
if (!Number.isSafeInteger(parsed) || parsed < 1) return void 0;
|
|
892
|
+
return parsed;
|
|
893
|
+
}
|
|
894
|
+
/** True only while the block 210 experiment harness has asked for MMR off. */
|
|
895
|
+
function experimentMmrDisabled() {
|
|
896
|
+
return process.env.MEM_CLAW_EXPERIMENT_DISABLE_MMR?.trim() === "1";
|
|
897
|
+
}
|
|
898
|
+
/**
|
|
899
|
+
* Token budget for the aggregation payload ONE consumer receives.
|
|
900
|
+
*
|
|
901
|
+
* Derived, and every number is a measurement. The answering engine's context is 64K tokens (owner,
|
|
902
|
+
* 2026-08-14, raised from 32K with the Qwen 3.8 rollout); half is reserved for the question, the
|
|
903
|
+
* surrounding prompt and the answer, leaving 32K for recalled evidence. Measured with
|
|
904
|
+
* `dev-scripts/census-aggregation-payload.mts` against the largest real store
|
|
905
|
+
* (`academic_researcher_weekly`, 138 active profile/episodic rows): the ENTIRE population renders in
|
|
906
|
+
* 43,699 characters ~= 10,925 tokens, a sixth of the context. A complete aggregation is therefore
|
|
907
|
+
* the ordinary case on real data, not an aspiration.
|
|
908
|
+
*
|
|
909
|
+
* The HALF is the load-bearing part, not the literal number. When the context moved to 64K this
|
|
910
|
+
* constant did not move with it for one commit, and a budget that stays put while its derivation
|
|
911
|
+
* doubles silently becomes a quarter of the context — a tightening nobody decided and nobody would
|
|
912
|
+
* find. That is the same shape as defect 2 below. If the context changes again, this changes.
|
|
913
|
+
*
|
|
914
|
+
* Known trade, stated rather than discovered later: widening this window is what raised aggregation
|
|
915
|
+
* from 0.045 to 0.744 on 2026-08-13, and it is also what dropped FAA 0.647 -> 0.600 in the same
|
|
916
|
+
* run, because retired rows the old ceiling had hidden came back into view. The answer to that is
|
|
917
|
+
* REM removing them (PRD 30/40), never a smaller recall window — shrinking this to protect FAA
|
|
918
|
+
* re-severs the counting path, which is the defect this constant exists to record.
|
|
919
|
+
*
|
|
920
|
+
* This replaces three ceilings that stacked on one path and severed it. Measured on the full
|
|
921
|
+
* benchmark run of 2026-08-13: 30 counting questions scored 0.045 mean with 28 of them exactly
|
|
922
|
+
* 0.000, while the same personas' other 60 questions scored 0.748. The model was not wrong — asked
|
|
923
|
+
* for a week of steps it answered "14,392 steps (recorded on June 7). No other step data is logged
|
|
924
|
+
* for the rest of the week", because 8 rows of 138 reached it. A model cannot total rows it was
|
|
925
|
+
* never shown. The three were:
|
|
926
|
+
*
|
|
927
|
+
* 1. `MAX_AGGREGATION_OUTPUT_ROWS = 8` — no derivation anywhere; 5.8% of a population handed to a
|
|
928
|
+
* question whose entire job is to total that population.
|
|
929
|
+
* 2. `MAX_AGGREGATION_TOOL_RESULT_CHARS = 16_384` — a unit error. 16,384 was the OLD 16K context
|
|
930
|
+
* in TOKENS, applied as a CHARACTER count, binding 3-4x tighter than it read.
|
|
931
|
+
* 3. Charging each consumer for the other's copy: the budget measured `JSON.stringify(result)`,
|
|
932
|
+
* which carries every row twice — once rendered, once in `memories` — while the OpenClaw host
|
|
933
|
+
* reads only the rendered text and the eval reads only `memories`.
|
|
934
|
+
*
|
|
935
|
+
* So this budget is counted in tokens, against the larger of the two consumer views, never a sum.
|
|
936
|
+
*/
|
|
937
|
+
const MAX_AGGREGATION_RESULT_TOKENS = 32768;
|
|
938
|
+
/** Additive recency boost magnitude */
|
|
939
|
+
const RECENCY_WEIGHT = .1;
|
|
940
|
+
/** Maximum allowed recency weight */
|
|
941
|
+
const RECENCY_WEIGHT_MAX = .5;
|
|
942
|
+
/** Base multiplier for importance weighting (score * (base + importance * base)) */
|
|
943
|
+
const IMPORTANCE_WEIGHT_BASE = .7;
|
|
944
|
+
/** Minimum time decay multiplier (floor for very old entries) */
|
|
945
|
+
const TIME_DECAY_FLOOR = .6;
|
|
946
|
+
/**
|
|
947
|
+
* Relevance vs diversity tradeoff for MMR (0.7 = 70% relevance, 30% diversity).
|
|
948
|
+
* `applyMmrDiversity` normalizes the relevance term to [0,1] (batch-max) before
|
|
949
|
+
* applying this weight — without that normalization, the shrinker chain above
|
|
950
|
+
* compresses relevance so far below the [0,1] cosine-similarity diversity term
|
|
951
|
+
* that this ratio was moot (diversity always won). Fixed 2026-07-05.
|
|
952
|
+
*/
|
|
953
|
+
const MMR_LAMBDA = .7;
|
|
954
|
+
/** Weight for fusion score in rerank blend */
|
|
955
|
+
const RERANK_BLEND_VECTOR = .3;
|
|
956
|
+
/** Weight for cross-encoder score in rerank blend */
|
|
957
|
+
const RERANK_BLEND_CROSS = .7;
|
|
958
|
+
/** Fusion-score weight in local cosine-blend rerank */
|
|
959
|
+
const LIGHTWEIGHT_FUSION_WEIGHT = .7;
|
|
960
|
+
/** Cosine-similarity weight in local cosine-blend rerank */
|
|
961
|
+
const LIGHTWEIGHT_COSINE_WEIGHT = .3;
|
|
962
|
+
/** Default rerank model identifier */
|
|
963
|
+
const DEFAULT_RERANK_MODEL = "rerank-2";
|
|
964
|
+
/** Default timeout for rerank API calls in milliseconds */
|
|
965
|
+
const DEFAULT_RERANK_TIMEOUT_MS = 15e3;
|
|
966
|
+
/**
|
|
967
|
+
* Maximum allowed list query page size; also caps each search branch
|
|
968
|
+
* (`memory-store-search-api.ts` clamps both the semantic and the keyword branch to it), so it
|
|
969
|
+
* stays >= the pool ceiling. Derived, not written: left at a literal 512 when the pool ceiling
|
|
970
|
+
* went 512 -> 2048 on 2026-09-15, every branch silently returned at most 512 candidates while
|
|
971
|
+
* retrieval asked for up to 2048, and the ceiling decided what ranking never saw.
|
|
972
|
+
*/
|
|
973
|
+
const MAX_LIST_LIMIT = MAX_CANDIDATE_POOL_SIZE;
|
|
974
|
+
const MAX_RECALLED_TODO_TOKENS = 1e3;
|
|
975
|
+
/**
|
|
976
|
+
* Ceiling for chunk-level fetch budgets (semantic/keyword chunk search).
|
|
977
|
+
* Deliberately independent of MAX_LIST_LIMIT: memory-level wrappers overfetch
|
|
978
|
+
* chunks (limit * 8) so one high-recall parent cannot collapse parent diversity.
|
|
979
|
+
*/
|
|
980
|
+
const MAX_CHUNK_FETCH_LIMIT = 4096;
|
|
981
|
+
/** Minimum score threshold for forget-query matches */
|
|
982
|
+
const FORGET_QUERY_MIN_SCORE = .8;
|
|
983
|
+
/**
|
|
984
|
+
* Minimum age of the newest backup before another is taken (1 day). A backup is a full copy of
|
|
985
|
+
* the store, so an hourly cadence cost a 1.7 GB store 40 GB of disk a day.
|
|
986
|
+
*/
|
|
987
|
+
const BACKUP_INTERVAL_MS = 864e5;
|
|
988
|
+
/**
|
|
989
|
+
* Share of the backup volume that must stay free. Before a backup is written the oldest backups are
|
|
990
|
+
* deleted until the copy fits above this floor, and again afterwards if the volume is still under
|
|
991
|
+
* it; the newest backup is always kept, and when even that is not enough the backup is skipped.
|
|
992
|
+
*/
|
|
993
|
+
const BACKUP_MIN_FREE_DISK_RATIO = .1;
|
|
994
|
+
/** How often the maintenance timer wakes to see which jobs are due (1 hour). */
|
|
995
|
+
const MAINTENANCE_TICK_MS = 36e5;
|
|
996
|
+
/** Full-page integrity sweep (1 hour). */
|
|
997
|
+
const INTEGRITY_CHECK_INTERVAL_MS = 36e5;
|
|
998
|
+
/** Usage-telemetry outbox drain and quarantine cleanup (1 hour). */
|
|
999
|
+
const USAGE_OUTBOX_INTERVAL_MS = 36e5;
|
|
1000
|
+
/** Pruning of expired recall/inject usage events (1 hour). */
|
|
1001
|
+
const USAGE_EVENT_RETENTION_INTERVAL_MS = 36e5;
|
|
1002
|
+
/** Full-text index segment merge (1 hour). */
|
|
1003
|
+
const FTS_MERGE_INTERVAL_MS = 36e5;
|
|
1004
|
+
/** Query-planner statistics refresh (1 hour). */
|
|
1005
|
+
const PLANNER_STATISTICS_INTERVAL_MS = 36e5;
|
|
1006
|
+
/**
|
|
1007
|
+
* How often the automatic REM trigger is evaluated (1 hour). Evaluating is only a check: REM
|
|
1008
|
+
* itself runs on its own daily schedule and volume rule in src/sidecar/rem-trigger.ts.
|
|
1009
|
+
*/
|
|
1010
|
+
const REM_TRIGGER_CHECK_INTERVAL_MS = 36e5;
|
|
1011
|
+
/** Embedder LRU cache TTL in milliseconds (30 minutes) */
|
|
1012
|
+
const CACHE_TTL_MS = 18e5;
|
|
1013
|
+
/** Transcript budget shared by an atomic window and its preceding context. */
|
|
1014
|
+
const ATOMIC_EXTRACTION_MAX_INPUT_TOKENS = 4096;
|
|
1015
|
+
const ATOMIC_ENRICHMENT_OUTPUT_TOKEN_BUDGET = 1400;
|
|
1016
|
+
/**
|
|
1017
|
+
* Output cap of one capture call. The capture reply lists every claim and then every fact with
|
|
1018
|
+
* its quote, roughly 70 tokens per fact; at 4,096 a 35-turn session replayed as one turn hit the
|
|
1019
|
+
* cap and lost the whole window (43 of 272 LoCoMo sessions, measured 2026-09-13). A window is
|
|
1020
|
+
* split by turns when a reply truncates, so a single long turn (a pasted document, a replayed
|
|
1021
|
+
* session) has only this cap between it and total loss. Sized from the budget, not the failure:
|
|
1022
|
+
* the prompt (skill, calendar reference, response schema, dictionaries) is about 10K tokens, the
|
|
1023
|
+
* transcript at most ATOMIC_EXTRACTION_MAX_INPUT_TOKENS, and the model serves a 64K context, so
|
|
1024
|
+
* 16,384 keeps the whole call near 30K; the longest LoCoMo session (40 messages, ~100 facts)
|
|
1025
|
+
* needs about 10K, which 8,192 only just covered.
|
|
1026
|
+
*/
|
|
1027
|
+
const ATOMIC_CAPTURE_OUTPUT_TOKEN_BUDGET = 16384;
|
|
1028
|
+
/**
|
|
1029
|
+
* Wall time one capture call is given. The generic client timeout is what an installation sets
|
|
1030
|
+
* for short judgments (30s by default, 60s on the LoCoMo VM) and does not know this call's output
|
|
1031
|
+
* budget, so raising the budget silently made the call unfinishable: measured 2026-09-20, four
|
|
1032
|
+
* sessions produced `LlmClientTerminalError category=timeout` at 60s after 105-130s of work and
|
|
1033
|
+
* stored nothing. Sized from the budget at the extraction model's measured ~60 output tokens per
|
|
1034
|
+
* second, with the same headroom the budget carries.
|
|
1035
|
+
*/
|
|
1036
|
+
const ATOMIC_CAPTURE_TIMEOUT_MS = 3e5;
|
|
1037
|
+
/** Maximum character length of auto-recall query before truncation */
|
|
1038
|
+
const DEFAULT_AUTO_RECALL_MAX_QUERY_LENGTH = 2e3;
|
|
1039
|
+
/** Maximum input characters for reflection prompt */
|
|
1040
|
+
const DEFAULT_REFLECTION_MAX_INPUT_CHARS = 24e3;
|
|
1041
|
+
/** Timeout for individual reflection LLM calls in milliseconds */
|
|
1042
|
+
const DEFAULT_REFLECTION_TIMEOUT_MS = 2e4;
|
|
1043
|
+
/** Session TTL in reflection tracking map (30 minutes) */
|
|
1044
|
+
const DEFAULT_REFLECTION_SESSION_TTL_MS = 18e5;
|
|
1045
|
+
/** Maximum chars scanned for error signals in conversation */
|
|
1046
|
+
const DEFAULT_REFLECTION_ERROR_SCAN_MAX_CHARS = 8e3;
|
|
1047
|
+
/** Cache TTL for loaded reflection slices in milliseconds */
|
|
1048
|
+
const REFLECTION_SLICE_CACHE_TTL_MS = 15e3;
|
|
1049
|
+
const RETRIEVAL_STORAGE_CHUNK_PROFILE = {
|
|
1050
|
+
minTokens: 256,
|
|
1051
|
+
targetTokens: 384,
|
|
1052
|
+
maxTokens: 448,
|
|
1053
|
+
overlapTokens: 32
|
|
1054
|
+
};
|
|
1055
|
+
/** Minimum tokens per chunk before merge (mirrors RETRIEVAL_STORAGE_CHUNK_PROFILE). */
|
|
1056
|
+
const CHUNK_MIN_TOKENS = RETRIEVAL_STORAGE_CHUNK_PROFILE.minTokens;
|
|
1057
|
+
/** Target tokens per chunk (mirrors RETRIEVAL_STORAGE_CHUNK_PROFILE). */
|
|
1058
|
+
const CHUNK_TARGET_TOKENS = RETRIEVAL_STORAGE_CHUNK_PROFILE.targetTokens;
|
|
1059
|
+
/** Max tokens per chunk before split (mirrors RETRIEVAL_STORAGE_CHUNK_PROFILE). */
|
|
1060
|
+
const CHUNK_MAX_TOKENS = RETRIEVAL_STORAGE_CHUNK_PROFILE.maxTokens;
|
|
1061
|
+
/** Overlap tokens between adjacent chunks (mirrors RETRIEVAL_STORAGE_CHUNK_PROFILE). */
|
|
1062
|
+
const CHUNK_OVERLAP_TOKENS = RETRIEVAL_STORAGE_CHUNK_PROFILE.overlapTokens;
|
|
1063
|
+
/** Tokenizer mode used by the chunker (mirrors DEFAULT_CHUNK_CONFIG.tokenizerMode). */
|
|
1064
|
+
const CHUNK_TOKENIZER_MODE = DEFAULT_CHUNK_CONFIG.tokenizerMode;
|
|
1065
|
+
/**
|
|
1066
|
+
* Diagnostic threshold for intra-memory chunk cosine similarity (PRD §8.1).
|
|
1067
|
+
* Pairs above this score are flagged as redundant during eval / introspection.
|
|
1068
|
+
*/
|
|
1069
|
+
const INTRA_MEMORY_COSINE_THRESHOLD = .92;
|
|
1070
|
+
/** Alias for VECTOR_DIMENSION_DEFAULT — the embedder output dimension. */
|
|
1071
|
+
const EMBEDDER_DIM = VECTOR_DIMENSION_DEFAULT;
|
|
1072
|
+
/** Default embedder provider id stamped into chunk provenance. */
|
|
1073
|
+
const EMBEDDER_PROVIDER_DEFAULT = "local-onnx";
|
|
1074
|
+
/**
|
|
1075
|
+
* Default embedder model id stamped into chunk provenance.
|
|
1076
|
+
*
|
|
1077
|
+
* Codex 2026-04-30 (batch-C C1): the previous hard-coded
|
|
1078
|
+
* `qwen3-embedding-4b@1024` did not match the actual local default
|
|
1079
|
+
* (`LOCAL_EMBEDDING_MODEL`, the bundled local embedding model), which would
|
|
1080
|
+
* mislabel every default-provider chunk in eval traces. Tracking the
|
|
1081
|
+
* embedder package's constant keeps provenance honest by construction.
|
|
1082
|
+
*/
|
|
1083
|
+
const EMBEDDER_MODEL_DEFAULT = LOCAL_EMBEDDING_MODEL;
|
|
1084
|
+
/** Per-hit bonus for repeated chunk hits inside one parent. */
|
|
1085
|
+
const MULTI_HIT_BONUS_PER_HIT = DEFAULT_AGGREGATION_CONFIG.multiHitBonusPerHit;
|
|
1086
|
+
/** Cap on the cumulative multi-hit bonus per parent. */
|
|
1087
|
+
const MULTI_HIT_BONUS_CAP = DEFAULT_AGGREGATION_CONFIG.multiHitBonusCap;
|
|
1088
|
+
/** Per-pair bonus for adjacent chunk hits within one parent. */
|
|
1089
|
+
const ADJACENCY_BONUS_PER_PAIR = DEFAULT_AGGREGATION_CONFIG.adjacencyBonusPerPair;
|
|
1090
|
+
/** Cap on the cumulative adjacency bonus per parent. */
|
|
1091
|
+
const ADJACENCY_BONUS_CAP = DEFAULT_AGGREGATION_CONFIG.adjacencyBonusCap;
|
|
1092
|
+
/** Invariant: bonusTotalInvariant >= MULTI_HIT_BONUS_CAP + ADJACENCY_BONUS_CAP. */
|
|
1093
|
+
const BONUS_TOTAL_INVARIANT = DEFAULT_AGGREGATION_CONFIG.bonusTotalInvariant;
|
|
1094
|
+
/** Filename for per-QA retrieval traces emitted during eval runs. */
|
|
1095
|
+
const EVAL_QA_TRACES_FILENAME = "qa_traces.jsonl";
|
|
1096
|
+
/** Multiplicative penalty applied to unreturned candidates after rerank. */
|
|
1097
|
+
const LIGHTWEIGHT_RERANK_PENALTY = .8;
|
|
1098
|
+
/**
|
|
1099
|
+
* Zod schema for the `recallLifecycle` config block. The whole block defaults
|
|
1100
|
+
* to `{}` so existing configs without it continue to parse and behave
|
|
1101
|
+
* bit-for-bit identically to the pre-Phase-0 baseline.
|
|
1102
|
+
*
|
|
1103
|
+
* Spec: openspec/changes/mem-lifecycle/specs/config/spec.md.
|
|
1104
|
+
*/
|
|
1105
|
+
const recallLifecycleSchema = z.object({
|
|
1106
|
+
retentionScorer: z.boolean().default(true),
|
|
1107
|
+
tierPromoter: z.boolean().default(true),
|
|
1108
|
+
autoRecallAccessTracking: z.boolean().default(true),
|
|
1109
|
+
traceEnabled: z.boolean().default(true),
|
|
1110
|
+
tierFloorMode: z.enum(["bare", "withFloor"]).default("bare"),
|
|
1111
|
+
tierPromotionTopK: z.number().int().positive().default(3),
|
|
1112
|
+
accessRateLimitMs: z.number().int().nonnegative().default(36e5),
|
|
1113
|
+
accessCountCeiling: z.number().int().positive().default(20)
|
|
1114
|
+
}).prefault({});
|
|
1115
|
+
/**
|
|
1116
|
+
* Resolved default `recallLifecycle` config. Use in code paths that need a
|
|
1117
|
+
* literal default object instead of re-parsing the schema each time.
|
|
1118
|
+
*/
|
|
1119
|
+
const DEFAULT_RECALL_LIFECYCLE = recallLifecycleSchema.parse(void 0);
|
|
1120
|
+
//#endregion
|
|
1121
|
+
//#region src/engine/i18n/locales.ts
|
|
1122
|
+
const SUPPORTED_LOCALES = [
|
|
1123
|
+
"en",
|
|
1124
|
+
"de",
|
|
1125
|
+
"es",
|
|
1126
|
+
"fr",
|
|
1127
|
+
"zh",
|
|
1128
|
+
"zh-Hant",
|
|
1129
|
+
"ja",
|
|
1130
|
+
"ko",
|
|
1131
|
+
"ru"
|
|
1132
|
+
];
|
|
1133
|
+
const SUPPORTED_SET = new Set(SUPPORTED_LOCALES);
|
|
1134
|
+
function isSupportedLocale(value) {
|
|
1135
|
+
return SUPPORTED_SET.has(value);
|
|
1136
|
+
}
|
|
1137
|
+
//#endregion
|
|
1138
|
+
//#region config/plugin-config-mode-schema.ts
|
|
1139
|
+
/**
|
|
1140
|
+
* Product LLM modes, tier-ordered: Local First (selected calls on host) → Agent
|
|
1141
|
+
* Native (every call borrows the host agent's model) → REM Enhanced (top
|
|
1142
|
+
* tier: SNO-REM-MEM serves the LoRA-covered calls, the host model covers
|
|
1143
|
+
* the rest).
|
|
1144
|
+
*/
|
|
1145
|
+
const PRODUCT_MODES = [
|
|
1146
|
+
"local-first",
|
|
1147
|
+
"agent-native",
|
|
1148
|
+
"rem-enhanced"
|
|
1149
|
+
];
|
|
1150
|
+
const DEFAULT_MODEL_MODE = "agent-native";
|
|
1151
|
+
const remEnhancedConfigSchema = z.object({ trigger: z.object({ tick: z.boolean().default(true) }).strict().prefault({}) }).strict().prefault({});
|
|
1152
|
+
const llmRoutingConfigSchema = z.object({
|
|
1153
|
+
mode: z.enum(PRODUCT_MODES),
|
|
1154
|
+
language: z.enum(SUPPORTED_LOCALES).default("en"),
|
|
1155
|
+
modelCalls: z.record(z.string(), z.record(z.enum(PRODUCT_MODES), z.enum([
|
|
1156
|
+
"off",
|
|
1157
|
+
"host",
|
|
1158
|
+
"sno-gpu"
|
|
1159
|
+
]))).optional()
|
|
1160
|
+
}).strict();
|
|
1161
|
+
//#endregion
|
|
1162
|
+
//#region config/plugin-config-embedding-schema.ts
|
|
1163
|
+
const localEmbedSessionOptionsSchema = z.object({
|
|
1164
|
+
graphOptimizationLevel: z.enum([
|
|
1165
|
+
"disabled",
|
|
1166
|
+
"basic",
|
|
1167
|
+
"extended",
|
|
1168
|
+
"all"
|
|
1169
|
+
]).default("extended"),
|
|
1170
|
+
enableMemPattern: z.boolean().default(false),
|
|
1171
|
+
enableCpuMemArena: z.boolean().default(false),
|
|
1172
|
+
executionMode: z.enum(["sequential", "parallel"]).optional(),
|
|
1173
|
+
interOpNumThreads: z.number().int().positive().optional(),
|
|
1174
|
+
intraOpNumThreads: z.number().int().positive().optional()
|
|
1175
|
+
}).strict().prefault({});
|
|
1176
|
+
const embeddingConfigSchema = z.object({
|
|
1177
|
+
provider: z.enum(["local-onnx"]).default("local-onnx"),
|
|
1178
|
+
/** Hugging Face model id. Defaults to the bundled PPLX INT8 model. */
|
|
1179
|
+
model: z.string().optional(),
|
|
1180
|
+
/** Output dimension after optional truncation and re-normalization. */
|
|
1181
|
+
dimensions: z.number().int().positive().default(VECTOR_DIMENSION_DEFAULT),
|
|
1182
|
+
/** Native dimension produced by the local model. */
|
|
1183
|
+
nativeDim: z.number().int().positive().optional(),
|
|
1184
|
+
/** Pinned HuggingFace revision (commit SHA) for local-onnx supply-chain integrity. */
|
|
1185
|
+
revision: z.string().optional(),
|
|
1186
|
+
/**
|
|
1187
|
+
* Pooling strategy for local-onnx. Read from the model's
|
|
1188
|
+
* `1_Pooling/config.json`. Omit to use the provider default; the bundled
|
|
1189
|
+
* PPLX model uses `mean`.
|
|
1190
|
+
*/
|
|
1191
|
+
pooling: z.enum([
|
|
1192
|
+
"last_token",
|
|
1193
|
+
"mean",
|
|
1194
|
+
"cls"
|
|
1195
|
+
]).optional(),
|
|
1196
|
+
normalized: z.boolean().optional(),
|
|
1197
|
+
/** Local ONNX model cache directory */
|
|
1198
|
+
cacheDir: z.string().optional(),
|
|
1199
|
+
offline: z.boolean().optional(),
|
|
1200
|
+
mirror: z.string().optional(),
|
|
1201
|
+
/** Quantization dtype: q4 | q8 | fp16 | fp32 */
|
|
1202
|
+
dtype: z.enum([
|
|
1203
|
+
"q4",
|
|
1204
|
+
"q8",
|
|
1205
|
+
"fp16",
|
|
1206
|
+
"fp32"
|
|
1207
|
+
]).default("q8"),
|
|
1208
|
+
/** ONNX Runtime session options for local low-memory operation. */
|
|
1209
|
+
sessionOptions: localEmbedSessionOptionsSchema,
|
|
1210
|
+
/** Enable text chunking for long passages (default: true) */
|
|
1211
|
+
chunking: z.boolean().default(true)
|
|
1212
|
+
}).prefault({});
|
|
1213
|
+
//#endregion
|
|
1214
|
+
//#region src/model/signed-registry-constants.ts
|
|
1215
|
+
/** Fixed signed, persisted, and host protocol values. Change only with their external authority. */
|
|
1216
|
+
const FIXED_EXTERNAL_VALUE_49 = "database contains both mem_claw_memories and nodix_memories";
|
|
1217
|
+
const FIXED_EXTERNAL_VALUE_51 = "PRAGMA table_info(mem_claw_memories)";
|
|
1218
|
+
const FIXED_EXTERNAL_VALUE_52 = "cannot read vec_mem_claw_chunks dimension";
|
|
1219
|
+
const FIXED_EXTERNAL_VALUE_53 = "vec_mem_claw_chunks does not match the current schema (configured dim ";
|
|
1220
|
+
const FIXED_EXTERNAL_VALUE_54 = "DROP TABLE mem_claw_chunks_fts";
|
|
1221
|
+
const FIXED_EXTERNAL_VALUE_55 = "database contains both vec_mem_claw_chunks and nodix_memory_chunk_vectors";
|
|
1222
|
+
const FIXED_EXTERNAL_VALUE_56 = "INSERT INTO nodix_memory_chunk_vectors(id, project_id, embedding) SELECT id, project_id, embedding FROM vec_mem_claw_chunks";
|
|
1223
|
+
const FIXED_EXTERNAL_VALUE_57 = "INSERT INTO nodix_memory_chunk_vectors(id, project_id, embedding)\n SELECT vectors.id, memories.project_id, vectors.embedding\n FROM vec_mem_claw_chunks AS vectors\n JOIN nodix_memory_chunks AS chunks ON chunks.chunk_id = vectors.id\n JOIN nodix_memories AS memories ON memories.id = chunks.memory_id";
|
|
1224
|
+
const FIXED_EXTERNAL_VALUE_58 = ";\n DROP TABLE vec_mem_claw_chunks;\n ";
|
|
1225
|
+
const FIXED_EXTERNAL_VALUE_62 = "mem_claw.unplaced-candidate-migration.relocated.non.active.memory.rows.into.the.unplaced.candidate.table";
|
|
1226
|
+
const FIXED_EXTERNAL_VALUE_8 = "https://www.sno.ai/.well-known/sno-mem-openclaw-release.json";
|
|
1227
|
+
const FIXED_EXTERNAL_VALUE_9 = "did:web:www.sno.ai#sno-mem-openclaw-release";
|
|
1228
|
+
const FIXED_IDX_MEMORY = "idx_mem_claw";
|
|
1229
|
+
const FIXED_IDX_MEMORY_ACTIVE_TASK_CURRENT_REVISION = "idx_mem_claw_active_task_current_revision";
|
|
1230
|
+
const FIXED_IDX_MEMORY_ACTIVE_TASK_EVIDENCE_INSTANCE = "idx_mem_claw_active_task_evidence_instance";
|
|
1231
|
+
const FIXED_IDX_MEMORY_ACTIVE_TASK_INSTANCES_PROJECTION = "idx_mem_claw_active_task_instances_projection";
|
|
1232
|
+
const FIXED_IDX_MEMORY_MEMORIES_CATEGORY = "idx_mem_claw_memories_category";
|
|
1233
|
+
const FIXED_IDX_MEMORY_MEMORIES_CONTENT_HASH = "idx_mem_claw_memories_content_hash";
|
|
1234
|
+
const FIXED_IDX_MEMORY_MEMORIES_FACT_ID = "idx_mem_claw_memories_fact_id";
|
|
1235
|
+
const FIXED_IDX_MEMORY_MEMORIES_LANE_PROJECT = "idx_mem_claw_memories_lane_project";
|
|
1236
|
+
const FIXED_IDX_MEMORY_MEMORIES_PROJECT = "idx_mem_claw_memories_project";
|
|
1237
|
+
const FIXED_IDX_MEMORY_MEMORIES_PROJECT_CONTENT_HASH = "idx_mem_claw_memories_project_content_hash";
|
|
1238
|
+
const FIXED_IDX_MEMORY_MEMORIES_PROJECT_FACT_KEY_ACTIVE = "idx_mem_claw_memories_project_fact_key_active";
|
|
1239
|
+
const FIXED_IDX_MEMORY_MEMORIES_PROJECT_TIMESTAMP = "idx_mem_claw_memories_project_timestamp";
|
|
1240
|
+
const FIXED_MEMORY = "mem_claw";
|
|
1241
|
+
const FIXED_MEMORY_ACTIVE_TASK_EVIDENCE = "mem_claw_active_task_evidence";
|
|
1242
|
+
const FIXED_MEMORY_ACTIVE_TASK_INSTANCES = "mem_claw_active_task_instances";
|
|
1243
|
+
const FIXED_MEMORY_ACTIVE_TASK_MIGRATION_EVIDENCE = "mem_claw_active_task_migration_evidence";
|
|
1244
|
+
const FIXED_MEMORY_ACTIVE_TASK_MIGRATION_MANIFESTS = "mem_claw_active_task_migration_manifests";
|
|
1245
|
+
const FIXED_MEMORY_ACTIVE_TASK_REVISIONS = "mem_claw_active_task_revisions";
|
|
1246
|
+
const FIXED_MEMORY_ACTIVE_TASK_TRANSITIONS = "mem_claw_active_task_transitions";
|
|
1247
|
+
const FIXED_MEMORY_CHUNKS = "mem_claw_chunks";
|
|
1248
|
+
const FIXED_MEMORY_CHUNKS_AD = "mem_claw_chunks_ad";
|
|
1249
|
+
const FIXED_MEMORY_CHUNKS_AI = "mem_claw_chunks_ai";
|
|
1250
|
+
const FIXED_MEMORY_CHUNKS_AU = "mem_claw_chunks_au";
|
|
1251
|
+
const FIXED_MEMORY_CHUNKS_FTS = "mem_claw_chunks_fts";
|
|
1252
|
+
const FIXED_MEMORY_CHUNKS_MEM_FACET_IDX = "mem_claw_chunks_mem_facet_idx";
|
|
1253
|
+
const FIXED_MEMORY_CHUNKS_MEM_IDX = "mem_claw_chunks_mem_idx";
|
|
1254
|
+
const FIXED_MEMORY_CHUNKS_MEMORY_ID_IDX = "mem_claw_chunks_memory_id_idx";
|
|
1255
|
+
const FIXED_MEMORY_EXTRACTION_TIMESTAMPS = "mem_claw_extraction_timestamps";
|
|
1256
|
+
const FIXED_MEMORY_MEMORIES = "mem_claw_memories";
|
|
1257
|
+
const FIXED_MEMORY_MEMORIES_FACT_ID_INSERT_GUARD = "mem_claw_memories_fact_id_insert_guard";
|
|
1258
|
+
const FIXED_MEMORY_MEMORIES_FACT_ID_UPDATE_GUARD = "mem_claw_memories_fact_id_update_guard";
|
|
1259
|
+
const FIXED_MEMORY_MIGRATION_MARKERS = "mem_claw_migration_markers";
|
|
1260
|
+
const FIXED_MEMORY_PROFILE_RECOVERY_ENTRIES = "mem_claw_profile_recovery_entries";
|
|
1261
|
+
const FIXED_MEMORY_PROVIDER_AGENT_MAPPINGS = "mem_claw_provider_agent_mappings";
|
|
1262
|
+
const FIXED_MEMORY_PROVIDER_PROJECT_AGENTS = "mem_claw_provider_project_agents";
|
|
1263
|
+
const FIXED_MEMORY_PROVIDER_PROJECT_MAPPINGS = "mem_claw_provider_project_mappings";
|
|
1264
|
+
const FIXED_MEMORY_ROW_PATH_PATTERN = /^mem-claw\/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\.md$/;
|
|
1265
|
+
const FIXED_MEMORY_SNO_AI_EXTRACT = "mem_claw/sno_ai_extract";
|
|
1266
|
+
const FIXED_MEMORY_SNO_CONFLICT_VERDICT = "mem_claw/sno_conflict_verdict";
|
|
1267
|
+
const FIXED_MEMORY_SNO_EXTRACT_CHAT = "mem_claw/sno_extract_chat";
|
|
1268
|
+
const FIXED_MEMORY_SNO_EXTRACT_PROFILE = "mem_claw/sno_extract_profile";
|
|
1269
|
+
const FIXED_MEMORY_TASK_LIFECYCLE_COMMANDS = "mem_claw_task_lifecycle_commands";
|
|
1270
|
+
const FIXED_MEMORY_UNPLACED_CANDIDATES = "mem_claw_unplaced_candidates";
|
|
1271
|
+
const FIXED_PROTOCOL_VALUE_63 = "mem-claw-internal-llm";
|
|
1272
|
+
const FIXED_PROTOCOL_VALUE_64 = ".openclaw";
|
|
1273
|
+
const FIXED_PROTOCOL_VALUE_65 = "SELECT project_id FROM nodix_provider_project_mappings WHERE user_id = ? AND external_system = 'openclaw' AND external_project_key = ? LIMIT 1";
|
|
1274
|
+
const FIXED_PROTOCOL_VALUE_66 = "SELECT agent_id FROM nodix_provider_agent_mappings WHERE user_id = ? AND external_system = 'openclaw' AND external_agent_key = ? LIMIT 1";
|
|
1275
|
+
const FIXED_PROTOCOL_VALUE_67 = "INSERT OR IGNORE INTO nodix_provider_project_mappings(user_id, external_system, external_project_key, project_id, created_at_ms) VALUES (?, 'openclaw', ?, ?, ?)";
|
|
1276
|
+
const FIXED_PROTOCOL_VALUE_68 = "INSERT OR IGNORE INTO nodix_provider_agent_mappings(user_id, external_system, external_agent_key, agent_id, created_at_ms) VALUES (?, 'openclaw', ?, ?, ?)";
|
|
1277
|
+
const FIXED_PROTOCOL_VALUE_69 = "http://localhost:8070/codex/v1";
|
|
1278
|
+
const FIXED_PROTOCOL_VALUE_70 = "hermes";
|
|
1279
|
+
const FIXED_PROTOCOL_VALUE_71 = "claude-code";
|
|
1280
|
+
const FIXED_PROTOCOL_VALUE_72 = "codex";
|
|
1281
|
+
const FIXED_PROTOCOL_VALUE_73 = "mem-claw:atomic-card:v1";
|
|
1282
|
+
const FIXED_PROTOCOL_VALUE_74 = "sno-mem-claw";
|
|
1283
|
+
const FIXED_PROTOCOL_VALUE_75 = "mem-claw/self_improvement_log";
|
|
1284
|
+
const FIXED_PROTOCOL_VALUE_76 = "Provider authority externalSystem must be openclaw";
|
|
1285
|
+
const FIXED_PROTOCOL_VALUE_77 = "mem-claw/";
|
|
1286
|
+
const FIXED_PROTOCOL_VALUE_78 = "mem-claw/reflection/";
|
|
1287
|
+
const FIXED_PROTOCOL_VALUE_79 = "Stable rules inherited from mem-claw reflections. Treat as long-term behavioral constraints unless user overrides.";
|
|
1288
|
+
const FIXED_VEC_MEMORY = "vec_mem_claw";
|
|
1289
|
+
const FIXED_VEC_MEMORY_CHUNKS = "vec_mem_claw_chunks";
|
|
1290
|
+
const PERSISTED_CANDIDATE_HASH_V1 = "mem-claw:unplaced-candidate:legacy:v1";
|
|
1291
|
+
const PERSISTED_CONTENT_HASH_V1 = "mem-claw:hash:v1";
|
|
1292
|
+
const PERSISTED_CONTENT_HASH_V3 = "mem-claw:hash:v3";
|
|
1293
|
+
const PERSISTED_PROVIDER_SYSTEM = "openclaw";
|
|
1294
|
+
//#endregion
|
|
1295
|
+
//#region src/model/llm-client-types.ts
|
|
1296
|
+
const LLM_PRESETS = [
|
|
1297
|
+
FIXED_MEMORY_SNO_AI_EXTRACT,
|
|
1298
|
+
FIXED_MEMORY_SNO_EXTRACT_CHAT,
|
|
1299
|
+
FIXED_MEMORY_SNO_EXTRACT_PROFILE,
|
|
1300
|
+
FIXED_MEMORY_SNO_CONFLICT_VERDICT
|
|
1301
|
+
];
|
|
1302
|
+
//#endregion
|
|
1303
|
+
//#region config/plugin-config-feature-schema.ts
|
|
1304
|
+
const sessionMemoryConfigSchema = z.object({
|
|
1305
|
+
enabled: z.boolean().default(true),
|
|
1306
|
+
messageCount: z.number().int().min(1).max(100).default(15)
|
|
1307
|
+
}).prefault({});
|
|
1308
|
+
const selfImprovementConfigSchema = z.object({
|
|
1309
|
+
enabled: z.boolean().default(true),
|
|
1310
|
+
beforeResetNote: z.boolean().default(true),
|
|
1311
|
+
skipSubagentBootstrap: z.boolean().default(true),
|
|
1312
|
+
ensureLearningFiles: z.boolean().default(true)
|
|
1313
|
+
}).prefault({});
|
|
1314
|
+
const extractionConfigSchema = z.object({ llm: z.object({
|
|
1315
|
+
preset: z.enum(LLM_PRESETS).default(FIXED_MEMORY_SNO_AI_EXTRACT),
|
|
1316
|
+
/** Overrides provider default / env baseURL if set. */
|
|
1317
|
+
baseURL: z.string().optional(),
|
|
1318
|
+
/** Overrides provider-native env if set. */
|
|
1319
|
+
apiKey: z.string().optional(),
|
|
1320
|
+
timeoutMs: z.number().int().min(1e3).max(3e5).default(3e4)
|
|
1321
|
+
}).strict().prefault({}) }).strict().prefault({});
|
|
1322
|
+
const memoryReflectionConfigSchema = z.object({
|
|
1323
|
+
messageCount: z.number().int().min(1).max(500).default(120),
|
|
1324
|
+
maxInputChars: z.number().int().min(1e3).max(2e5).default(DEFAULT_REFLECTION_MAX_INPUT_CHARS),
|
|
1325
|
+
timeoutMs: z.number().int().min(5e3).max(3e5).default(DEFAULT_REFLECTION_TIMEOUT_MS),
|
|
1326
|
+
errorReminderMaxEntries: z.number().int().min(0).max(50).default(3),
|
|
1327
|
+
dedupeErrorSignals: z.boolean().default(true),
|
|
1328
|
+
injectMode: z.enum([
|
|
1329
|
+
"inheritance+derived",
|
|
1330
|
+
"inheritance-only",
|
|
1331
|
+
"none"
|
|
1332
|
+
]).default("inheritance+derived"),
|
|
1333
|
+
storeToDb: z.boolean().default(true),
|
|
1334
|
+
/**
|
|
1335
|
+
* Inject reflection invariants/derived slices into agent prompts at recall
|
|
1336
|
+
* time. Default `false` until the LoCoMo paired-arm gate validates eval-
|
|
1337
|
+
* neutrality (PRD §4.4.1, §5 eval gate). Permanent kill switch even after
|
|
1338
|
+
* the default flips.
|
|
1339
|
+
*/
|
|
1340
|
+
injectIntoPrompt: z.boolean().default(false),
|
|
1341
|
+
agentId: z.string().optional()
|
|
1342
|
+
}).prefault({});
|
|
1343
|
+
//#endregion
|
|
1344
|
+
//#region config/plugin-config-observe-schema.ts
|
|
1345
|
+
const AGENT_IDS = [
|
|
1346
|
+
PERSISTED_PROVIDER_SYSTEM,
|
|
1347
|
+
FIXED_PROTOCOL_VALUE_70,
|
|
1348
|
+
FIXED_PROTOCOL_VALUE_71,
|
|
1349
|
+
FIXED_PROTOCOL_VALUE_72
|
|
1350
|
+
];
|
|
1351
|
+
const observeAgentIdSchema = z.enum(AGENT_IDS, { error: () => `observe.agentId must be one of ${AGENT_IDS.join(", ")}` });
|
|
1352
|
+
/** The observe agent id a skin reports under; a skin id outside the SDK's set is a hard error. */
|
|
1353
|
+
function observeAgentId(skinId) {
|
|
1354
|
+
return observeAgentIdSchema.parse(skinId);
|
|
1355
|
+
}
|
|
1356
|
+
function isObserveAgentId(skinId) {
|
|
1357
|
+
return observeAgentIdSchema.safeParse(skinId).success;
|
|
1358
|
+
}
|
|
1359
|
+
function isLoopbackUrl(url) {
|
|
1360
|
+
return url.hostname === "localhost" || url.hostname === "127.0.0.1" || url.hostname === "::1" || url.hostname === "[::1]";
|
|
1361
|
+
}
|
|
1362
|
+
function validateObserveBaseUrl(value, ctx) {
|
|
1363
|
+
let parsed;
|
|
1364
|
+
try {
|
|
1365
|
+
parsed = new URL(value);
|
|
1366
|
+
} catch {
|
|
1367
|
+
ctx.addIssue({
|
|
1368
|
+
code: z.ZodIssueCode.custom,
|
|
1369
|
+
path: ["baseUrl"],
|
|
1370
|
+
message: `Invalid observe.baseUrl: "${value}"`
|
|
1371
|
+
});
|
|
1372
|
+
return;
|
|
1373
|
+
}
|
|
1374
|
+
const normalized = parsed.origin;
|
|
1375
|
+
if (process.env.SNO_STATION_MEM_NODE_ENV === "test" && isLoopbackUrl(parsed)) {
|
|
1376
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") ctx.addIssue({
|
|
1377
|
+
code: z.ZodIssueCode.custom,
|
|
1378
|
+
path: ["baseUrl"],
|
|
1379
|
+
message: "observe.baseUrl test fixture must use http or https"
|
|
1380
|
+
});
|
|
1381
|
+
return;
|
|
1382
|
+
}
|
|
1383
|
+
if (normalized !== "https://www.sno.ai") ctx.addIssue({
|
|
1384
|
+
code: z.ZodIssueCode.custom,
|
|
1385
|
+
path: ["baseUrl"],
|
|
1386
|
+
message: `observe.baseUrl must be ${SNO_OBSERVE_DEFAULT_BASE_URL} outside loopback tests`
|
|
1387
|
+
});
|
|
1388
|
+
}
|
|
1389
|
+
const observeConfigSchema = z.object({
|
|
1390
|
+
enabled: z.boolean().default(true),
|
|
1391
|
+
baseUrl: z.string().default(SNO_OBSERVE_DEFAULT_BASE_URL),
|
|
1392
|
+
agentId: observeAgentIdSchema.default(SNO_OBSERVE_DEFAULT_AGENT_ID)
|
|
1393
|
+
}).prefault({}).superRefine((value, ctx) => {
|
|
1394
|
+
if (value.enabled) validateObserveBaseUrl(value.baseUrl, ctx);
|
|
1395
|
+
});
|
|
1396
|
+
//#endregion
|
|
1397
|
+
//#region config/plugin-config-retrieval-schema.ts
|
|
1398
|
+
const log = createLogger("sno-station-mem:config:retrieval");
|
|
1399
|
+
const retrievalConfigSchema = z.object({
|
|
1400
|
+
mode: z.enum(["precision-recall", "vector"]).default("precision-recall"),
|
|
1401
|
+
/**
|
|
1402
|
+
* Top-K memories injected per turn during auto-recall. Source of truth
|
|
1403
|
+
* for the runtime retriever `limit`; AC5 drift test pins this default
|
|
1404
|
+
* to AUTO_RECALL_INJECTION_TOP_K (currently 20). Lower (15) trades
|
|
1405
|
+
* ~5pp accuracy for ~25% fewer prompt tokens.
|
|
1406
|
+
*/
|
|
1407
|
+
recallTopK: z.number().int().min(0).max(2e3).default(20),
|
|
1408
|
+
vectorWeight: z.number().min(0).max(1).default(DEFAULT_VECTOR_WEIGHT),
|
|
1409
|
+
bm25Weight: z.number().min(0).max(1).default(DEFAULT_BM25_WEIGHT),
|
|
1410
|
+
minScore: z.number().min(0).max(1).default(0),
|
|
1411
|
+
rerank: z.enum([
|
|
1412
|
+
"cross-encoder",
|
|
1413
|
+
"lightweight",
|
|
1414
|
+
"none"
|
|
1415
|
+
]).default("lightweight"),
|
|
1416
|
+
candidatePoolSize: z.number().int().min(10).max(2e3).default(64),
|
|
1417
|
+
rerankApiKey: z.string().optional(),
|
|
1418
|
+
rerankModel: z.string().default(DEFAULT_RERANK_MODEL),
|
|
1419
|
+
rerankTimeoutMs: z.number().int().min(1e3).max(12e4).default(DEFAULT_RERANK_TIMEOUT_MS),
|
|
1420
|
+
recencyHalfLifeDays: z.number().min(0).max(365).default(14),
|
|
1421
|
+
recencyWeight: z.number().min(0).max(RECENCY_WEIGHT_MAX).default(RECENCY_WEIGHT),
|
|
1422
|
+
/** Drop memories past their valid_until timestamp (default OFF — needs A/B eval) */
|
|
1423
|
+
temporalWeighting: z.boolean().default(false),
|
|
1424
|
+
mmrWindowOnly: z.boolean().default(false),
|
|
1425
|
+
temporalExpiry: z.boolean().default(false),
|
|
1426
|
+
/** Dynamic memories decay 3× faster in time-decay scoring (default ON — low risk) */
|
|
1427
|
+
temporalDecay: z.boolean().default(true),
|
|
1428
|
+
lengthNormAnchor: z.number().int().min(0).max(5e3).default(0),
|
|
1429
|
+
hardMinScore: z.number().min(0).max(1).default(0),
|
|
1430
|
+
timeDecayHalfLifeDays: z.number().min(0).max(365).default(60),
|
|
1431
|
+
/** Weight for fusion score in rerank blend */
|
|
1432
|
+
rerankBlendVector: z.number().min(0).max(1).optional(),
|
|
1433
|
+
/** Weight for cross-encoder score in rerank blend */
|
|
1434
|
+
rerankBlendCross: z.number().min(0).max(1).optional(),
|
|
1435
|
+
/** Fusion-score weight when rerank="lightweight" (local cosine blend) */
|
|
1436
|
+
lightweightFusionWeight: z.number().min(0).max(1).optional(),
|
|
1437
|
+
/** Cosine-score weight when rerank="lightweight" (local cosine blend) */
|
|
1438
|
+
lightweightCosineWeight: z.number().min(0).max(1).optional(),
|
|
1439
|
+
/** Base multiplier for importance weighting */
|
|
1440
|
+
importanceWeightBase: z.number().min(0).max(1).optional(),
|
|
1441
|
+
/** Minimum multiplier for old entries */
|
|
1442
|
+
timeDecayFloor: z.number().min(0).max(1).optional(),
|
|
1443
|
+
/** Relevance vs diversity tradeoff (default: 0.7) */
|
|
1444
|
+
mmrLambda: z.number().min(0).max(1).optional(),
|
|
1445
|
+
/** Custom rerank API endpoint URL. */
|
|
1446
|
+
rerankEndpoint: z.string().optional(),
|
|
1447
|
+
/** Rerank provider name */
|
|
1448
|
+
rerankProvider: z.enum([
|
|
1449
|
+
"jina",
|
|
1450
|
+
"siliconflow",
|
|
1451
|
+
"pinecone",
|
|
1452
|
+
"voyage",
|
|
1453
|
+
"dashscope",
|
|
1454
|
+
"tei",
|
|
1455
|
+
"custom"
|
|
1456
|
+
]).optional(),
|
|
1457
|
+
/** Hard cap on candidates sent per rerank API request (some deployments reject over-limit batches) */
|
|
1458
|
+
rerankMaxCandidates: z.number().int().min(1).max(2e3).optional(),
|
|
1459
|
+
/** Scaling factor for access-based reinforcement (0 = disabled, default: 0.5) */
|
|
1460
|
+
reinforcementFactor: z.number().min(0).max(5).default(.5),
|
|
1461
|
+
/** Hard cap: effective half-life <= baseHalfLife * maxHalfLifeMultiplier (default: 3) */
|
|
1462
|
+
maxHalfLifeMultiplier: z.number().min(1).max(10).default(3)
|
|
1463
|
+
}).prefault({}).transform((ret, ctx) => {
|
|
1464
|
+
if (ret.rerank === "none") return ret;
|
|
1465
|
+
if ((ret.rerank === "cross-encoder" || ret.rerankEndpoint !== void 0) && ret.rerankProvider === void 0) ctx.addIssue({
|
|
1466
|
+
code: z.ZodIssueCode.custom,
|
|
1467
|
+
path: ["rerankProvider"],
|
|
1468
|
+
message: "retrieval.rerankProvider is required when retrieval.rerank is \"cross-encoder\" or retrieval.rerankEndpoint is set: the provider decides the request shape and the per-request batch limit"
|
|
1469
|
+
});
|
|
1470
|
+
if (ret.rerankEndpoint !== void 0) try {
|
|
1471
|
+
new URL(ret.rerankEndpoint);
|
|
1472
|
+
} catch {
|
|
1473
|
+
ctx.addIssue({
|
|
1474
|
+
code: z.ZodIssueCode.custom,
|
|
1475
|
+
path: ["rerankEndpoint"],
|
|
1476
|
+
message: `Invalid rerankEndpoint URL: "${ret.rerankEndpoint}"`
|
|
1477
|
+
});
|
|
1478
|
+
}
|
|
1479
|
+
const rerankApiKey = ret.rerankApiKey;
|
|
1480
|
+
if (ret.rerank === "cross-encoder" && !rerankApiKey?.trim()) {
|
|
1481
|
+
log.error("memory.rerank.key.missing", { cause: "missing-rerank-key" }, {
|
|
1482
|
+
event_name: "memory.rerank.key.missing",
|
|
1483
|
+
file: "packages/memory/config/plugin-config-retrieval-schema.ts",
|
|
1484
|
+
function: "retrievalConfigSchema",
|
|
1485
|
+
site_id: "memory.rerank.key.missing"
|
|
1486
|
+
});
|
|
1487
|
+
ctx.addIssue({
|
|
1488
|
+
code: z.ZodIssueCode.custom,
|
|
1489
|
+
path: ["rerankApiKey"],
|
|
1490
|
+
message: "retrieval.rerankApiKey is required for cross-encoder reranking"
|
|
1491
|
+
});
|
|
1492
|
+
}
|
|
1493
|
+
return ret;
|
|
1494
|
+
});
|
|
1495
|
+
//#endregion
|
|
1496
|
+
//#region config/session-strategy.ts
|
|
1497
|
+
/** @file session-strategy.ts
|
|
1498
|
+
* @purpose Defines session-memory strategy constants without importing plugin config schemas.
|
|
1499
|
+
* @boundary Shared literal values only; no schema or runtime registration dependencies.
|
|
1500
|
+
*/
|
|
1501
|
+
const SESSION_STRATEGIES = [
|
|
1502
|
+
"memoryReflection",
|
|
1503
|
+
"systemSessionMemory",
|
|
1504
|
+
"none"
|
|
1505
|
+
];
|
|
1506
|
+
//#endregion
|
|
1507
|
+
//#region config/plugin-config-schema.ts
|
|
1508
|
+
const scopesConfigSchema = z.object({
|
|
1509
|
+
default: z.string().default(DEFAULT_SCOPE),
|
|
1510
|
+
definitions: z.record(z.string(), z.object({
|
|
1511
|
+
description: z.string().optional(),
|
|
1512
|
+
metadata: z.record(z.string(), z.unknown()).optional()
|
|
1513
|
+
})).prefault({ global: { description: "Shared knowledge across all agents" } }),
|
|
1514
|
+
agentAccess: z.record(z.string(), z.array(z.string())).default({})
|
|
1515
|
+
}).prefault({});
|
|
1516
|
+
const providerConfigSchema = z.object({ userId: z.string().trim().optional() }).strict().prefault({});
|
|
1517
|
+
const ONBOARDING_PROFILES = [
|
|
1518
|
+
"local-active",
|
|
1519
|
+
"capture-only",
|
|
1520
|
+
"manual-only",
|
|
1521
|
+
"custom"
|
|
1522
|
+
];
|
|
1523
|
+
const REM_OPERATIONS = ["rem-replace", "rem-update"];
|
|
1524
|
+
const pluginConfigSchema = z.object({
|
|
1525
|
+
embedding: embeddingConfigSchema,
|
|
1526
|
+
observe: observeConfigSchema,
|
|
1527
|
+
dbPath: z.string().optional(),
|
|
1528
|
+
provider: providerConfigSchema,
|
|
1529
|
+
ambientLearning: z.boolean().default(true),
|
|
1530
|
+
autoRecall: z.boolean().default(false),
|
|
1531
|
+
/** Minimum query length for auto-recall to fire (default: 15) */
|
|
1532
|
+
autoRecallMinLength: z.number().int().min(0).max(200).default(2),
|
|
1533
|
+
/** Skip re-injecting a memory if it was shown within the last N turns (0 = no dedup) */
|
|
1534
|
+
autoRecallMinRepeated: z.number().int().min(0).max(100).default(0),
|
|
1535
|
+
/** Maximum character length of auto-recall query before truncation */
|
|
1536
|
+
autoRecallMaxQueryLength: z.number().int().min(100).max(1e4).default(DEFAULT_AUTO_RECALL_MAX_QUERY_LENGTH),
|
|
1537
|
+
/** Timeout in ms for the entire auto-recall pipeline (embed + search + rerank) */
|
|
1538
|
+
autoRecallTimeoutMs: z.number().int().min(500).max(6e4).default(5e3),
|
|
1539
|
+
/**
|
|
1540
|
+
* Whitelist: if non-empty, ONLY these agentIds receive auto-recall injection.
|
|
1541
|
+
* Takes precedence over autoRecallExcludeAgents when both are set. Entries
|
|
1542
|
+
* are trimmed; empty strings are dropped.
|
|
1543
|
+
*/
|
|
1544
|
+
autoRecallIncludeAgents: z.array(z.string().transform((s) => s.trim())).transform((arr) => arr.filter((s) => s.length > 0)).default([]),
|
|
1545
|
+
/**
|
|
1546
|
+
* Blocklist: agentIds excluded from auto-recall injection. The implicit
|
|
1547
|
+
* "main" fallback used when hook context lacks a parseable agent identity
|
|
1548
|
+
* is evaluated against this blocklist too.
|
|
1549
|
+
*/
|
|
1550
|
+
autoRecallExcludeAgents: z.array(z.string().transform((s) => s.trim())).transform((arr) => arr.filter((s) => s.length > 0)).default([]),
|
|
1551
|
+
captureAssistant: z.boolean().default(false),
|
|
1552
|
+
retrieval: retrievalConfigSchema,
|
|
1553
|
+
scopes: scopesConfigSchema,
|
|
1554
|
+
enableManagementTools: z.boolean().default(false),
|
|
1555
|
+
/**
|
|
1556
|
+
* Optional user-facing language for tool descriptions and other static
|
|
1557
|
+
* locale-bound surface that cannot be derived from query text.
|
|
1558
|
+
*/
|
|
1559
|
+
language: z.enum(SUPPORTED_LOCALES).optional(),
|
|
1560
|
+
sessionStrategy: z.enum(SESSION_STRATEGIES).default("systemSessionMemory"),
|
|
1561
|
+
sessionMemory: sessionMemoryConfigSchema,
|
|
1562
|
+
compression: z.object({ enabled: z.boolean().default(false) }).optional(),
|
|
1563
|
+
selfImprovement: selfImprovementConfigSchema,
|
|
1564
|
+
extraction: extractionConfigSchema,
|
|
1565
|
+
memoryReflection: memoryReflectionConfigSchema,
|
|
1566
|
+
/**
|
|
1567
|
+
* Phase 0 lifecycle stub channel. All flags default false; enabling any
|
|
1568
|
+
* lights up its dedicated wire site without altering the others.
|
|
1569
|
+
*/
|
|
1570
|
+
recallLifecycle: recallLifecycleSchema,
|
|
1571
|
+
memoryTelemetry: z.object({
|
|
1572
|
+
enabled: z.boolean().default(true),
|
|
1573
|
+
currentKeyVersion: z.number().int().positive().default(1),
|
|
1574
|
+
key: z.string().default(""),
|
|
1575
|
+
historicKeys: z.array(z.string()).default([])
|
|
1576
|
+
}).prefault({}),
|
|
1577
|
+
debugContent: z.boolean().default(false),
|
|
1578
|
+
/**
|
|
1579
|
+
* Product LLM mode, tier-ordered: local-first → agent-native →
|
|
1580
|
+
* rem-enhanced. A true bare config defaults to agent-native; model route
|
|
1581
|
+
* config requires an explicit product mode.
|
|
1582
|
+
*/
|
|
1583
|
+
mode: z.enum(PRODUCT_MODES).default(DEFAULT_MODEL_MODE),
|
|
1584
|
+
modelCalls: z.record(z.string(), z.record(z.enum(PRODUCT_MODES), z.enum([
|
|
1585
|
+
"off",
|
|
1586
|
+
"host",
|
|
1587
|
+
"sno-gpu"
|
|
1588
|
+
]))).optional(),
|
|
1589
|
+
remOperations: z.array(z.enum(REM_OPERATIONS)).min(1).max(2).default([...REM_OPERATIONS]),
|
|
1590
|
+
remEnhanced: remEnhancedConfigSchema,
|
|
1591
|
+
/**
|
|
1592
|
+
* Installer completion marker (bin/_onboarding-core.js writes it into
|
|
1593
|
+
* this same config block). The runtime never reads it, but the strict
|
|
1594
|
+
* schema must accept it or every installer-onboarded config fails to
|
|
1595
|
+
* load.
|
|
1596
|
+
*/
|
|
1597
|
+
onboarding: z.object({
|
|
1598
|
+
version: z.number().int().positive(),
|
|
1599
|
+
completedAt: z.string(),
|
|
1600
|
+
profile: z.enum(ONBOARDING_PROFILES)
|
|
1601
|
+
}).strict().optional()
|
|
1602
|
+
}).strict();
|
|
1603
|
+
//#endregion
|
|
1604
|
+
//#region src/model/model-call-table.ts
|
|
1605
|
+
const extract = {
|
|
1606
|
+
"local-first": "off",
|
|
1607
|
+
"agent-native": "host",
|
|
1608
|
+
"rem-enhanced": "sno-gpu"
|
|
1609
|
+
};
|
|
1610
|
+
const host = {
|
|
1611
|
+
"local-first": "off",
|
|
1612
|
+
"agent-native": "host",
|
|
1613
|
+
"rem-enhanced": "host"
|
|
1614
|
+
};
|
|
1615
|
+
const profile = {
|
|
1616
|
+
"local-first": "host",
|
|
1617
|
+
"agent-native": "host",
|
|
1618
|
+
"rem-enhanced": "host"
|
|
1619
|
+
};
|
|
1620
|
+
const rem = {
|
|
1621
|
+
"local-first": "host",
|
|
1622
|
+
"agent-native": "host",
|
|
1623
|
+
"rem-enhanced": "sno-gpu"
|
|
1624
|
+
};
|
|
1625
|
+
const remHost = {
|
|
1626
|
+
"local-first": "host",
|
|
1627
|
+
"agent-native": "host",
|
|
1628
|
+
"rem-enhanced": "host"
|
|
1629
|
+
};
|
|
1630
|
+
const chat = FIXED_MEMORY_SNO_EXTRACT_CHAT;
|
|
1631
|
+
const verdict = FIXED_MEMORY_SNO_CONFLICT_VERDICT;
|
|
1632
|
+
const MODEL_CALLS = {
|
|
1633
|
+
E1: {
|
|
1634
|
+
description: "conversation extraction",
|
|
1635
|
+
occasion: "memoryExtract",
|
|
1636
|
+
destinations: extract,
|
|
1637
|
+
snoPreset: chat,
|
|
1638
|
+
transport: {
|
|
1639
|
+
host: "agent-host-seam",
|
|
1640
|
+
snoGpu: "chat-completions"
|
|
1641
|
+
},
|
|
1642
|
+
promptVariant: {
|
|
1643
|
+
host: "standard",
|
|
1644
|
+
snoGpu: "standard"
|
|
1645
|
+
},
|
|
1646
|
+
replyParser: {
|
|
1647
|
+
host: "json",
|
|
1648
|
+
snoGpu: "json"
|
|
1649
|
+
}
|
|
1650
|
+
},
|
|
1651
|
+
E2: {
|
|
1652
|
+
description: "fact enrichment",
|
|
1653
|
+
occasion: "memoryExtract",
|
|
1654
|
+
destinations: extract,
|
|
1655
|
+
snoPreset: chat,
|
|
1656
|
+
transport: {
|
|
1657
|
+
host: "agent-host-seam",
|
|
1658
|
+
snoGpu: "chat-completions"
|
|
1659
|
+
},
|
|
1660
|
+
promptVariant: {
|
|
1661
|
+
host: "standard",
|
|
1662
|
+
snoGpu: "standard"
|
|
1663
|
+
},
|
|
1664
|
+
replyParser: {
|
|
1665
|
+
host: "json",
|
|
1666
|
+
snoGpu: "json"
|
|
1667
|
+
}
|
|
1668
|
+
},
|
|
1669
|
+
E3: {
|
|
1670
|
+
description: "missed figure recovery",
|
|
1671
|
+
occasion: "memoryExtract",
|
|
1672
|
+
destinations: extract,
|
|
1673
|
+
snoPreset: chat,
|
|
1674
|
+
transport: {
|
|
1675
|
+
host: "agent-host-seam",
|
|
1676
|
+
snoGpu: "chat-completions"
|
|
1677
|
+
},
|
|
1678
|
+
promptVariant: {
|
|
1679
|
+
host: "standard",
|
|
1680
|
+
snoGpu: "standard"
|
|
1681
|
+
},
|
|
1682
|
+
replyParser: {
|
|
1683
|
+
host: "json",
|
|
1684
|
+
snoGpu: "json"
|
|
1685
|
+
}
|
|
1686
|
+
},
|
|
1687
|
+
E4: {
|
|
1688
|
+
description: "compound record split",
|
|
1689
|
+
occasion: "memoryExtract",
|
|
1690
|
+
destinations: extract,
|
|
1691
|
+
snoPreset: chat,
|
|
1692
|
+
transport: {
|
|
1693
|
+
host: "agent-host-seam",
|
|
1694
|
+
snoGpu: "chat-completions"
|
|
1695
|
+
},
|
|
1696
|
+
promptVariant: {
|
|
1697
|
+
host: "standard",
|
|
1698
|
+
snoGpu: "standard"
|
|
1699
|
+
},
|
|
1700
|
+
replyParser: {
|
|
1701
|
+
host: "json",
|
|
1702
|
+
snoGpu: "json"
|
|
1703
|
+
}
|
|
1704
|
+
},
|
|
1705
|
+
E5: {
|
|
1706
|
+
description: "missing half recovery",
|
|
1707
|
+
occasion: "memoryExtract",
|
|
1708
|
+
destinations: extract,
|
|
1709
|
+
snoPreset: chat,
|
|
1710
|
+
transport: {
|
|
1711
|
+
host: "agent-host-seam",
|
|
1712
|
+
snoGpu: "chat-completions"
|
|
1713
|
+
},
|
|
1714
|
+
promptVariant: {
|
|
1715
|
+
host: "standard",
|
|
1716
|
+
snoGpu: "standard"
|
|
1717
|
+
},
|
|
1718
|
+
replyParser: {
|
|
1719
|
+
host: "json",
|
|
1720
|
+
snoGpu: "json"
|
|
1721
|
+
}
|
|
1722
|
+
},
|
|
1723
|
+
E6: {
|
|
1724
|
+
description: "subject check",
|
|
1725
|
+
occasion: "memoryExtract",
|
|
1726
|
+
destinations: extract,
|
|
1727
|
+
snoPreset: chat,
|
|
1728
|
+
transport: {
|
|
1729
|
+
host: "agent-host-seam",
|
|
1730
|
+
snoGpu: "chat-completions"
|
|
1731
|
+
},
|
|
1732
|
+
promptVariant: {
|
|
1733
|
+
host: "standard",
|
|
1734
|
+
snoGpu: "standard"
|
|
1735
|
+
},
|
|
1736
|
+
replyParser: {
|
|
1737
|
+
host: "json",
|
|
1738
|
+
snoGpu: "json"
|
|
1739
|
+
}
|
|
1740
|
+
},
|
|
1741
|
+
E7: {
|
|
1742
|
+
description: "unresolved subject recovery",
|
|
1743
|
+
occasion: "memoryExtract",
|
|
1744
|
+
destinations: extract,
|
|
1745
|
+
snoPreset: chat,
|
|
1746
|
+
transport: {
|
|
1747
|
+
host: "agent-host-seam",
|
|
1748
|
+
snoGpu: "chat-completions"
|
|
1749
|
+
},
|
|
1750
|
+
promptVariant: {
|
|
1751
|
+
host: "standard",
|
|
1752
|
+
snoGpu: "standard"
|
|
1753
|
+
},
|
|
1754
|
+
replyParser: {
|
|
1755
|
+
host: "json",
|
|
1756
|
+
snoGpu: "json"
|
|
1757
|
+
}
|
|
1758
|
+
},
|
|
1759
|
+
E8: {
|
|
1760
|
+
description: "same entity judgment",
|
|
1761
|
+
occasion: "memoryExtract",
|
|
1762
|
+
destinations: extract,
|
|
1763
|
+
snoPreset: chat,
|
|
1764
|
+
transport: {
|
|
1765
|
+
host: "agent-host-seam",
|
|
1766
|
+
snoGpu: "chat-completions"
|
|
1767
|
+
},
|
|
1768
|
+
promptVariant: {
|
|
1769
|
+
host: "standard",
|
|
1770
|
+
snoGpu: "standard"
|
|
1771
|
+
},
|
|
1772
|
+
replyParser: {
|
|
1773
|
+
host: "json",
|
|
1774
|
+
snoGpu: "json"
|
|
1775
|
+
}
|
|
1776
|
+
},
|
|
1777
|
+
E9: {
|
|
1778
|
+
description: "profile keying",
|
|
1779
|
+
occasion: "memoryExtract",
|
|
1780
|
+
destinations: extract,
|
|
1781
|
+
snoPreset: FIXED_MEMORY_SNO_EXTRACT_PROFILE,
|
|
1782
|
+
transport: {
|
|
1783
|
+
host: "agent-host-seam",
|
|
1784
|
+
snoGpu: "raw-completions"
|
|
1785
|
+
},
|
|
1786
|
+
promptVariant: {
|
|
1787
|
+
host: "standard",
|
|
1788
|
+
snoGpu: "standard"
|
|
1789
|
+
},
|
|
1790
|
+
replyParser: {
|
|
1791
|
+
host: "json",
|
|
1792
|
+
snoGpu: "json"
|
|
1793
|
+
}
|
|
1794
|
+
},
|
|
1795
|
+
E10: {
|
|
1796
|
+
description: "arrival retirement judgment",
|
|
1797
|
+
occasion: "conflictAdjudication",
|
|
1798
|
+
destinations: extract,
|
|
1799
|
+
snoPreset: verdict,
|
|
1800
|
+
transport: {
|
|
1801
|
+
host: "agent-host-seam",
|
|
1802
|
+
snoGpu: "raw-completions"
|
|
1803
|
+
},
|
|
1804
|
+
promptVariant: {
|
|
1805
|
+
host: "adapter-a-chat",
|
|
1806
|
+
snoGpu: "adapter-a-raw"
|
|
1807
|
+
},
|
|
1808
|
+
replyParser: {
|
|
1809
|
+
host: "json-or-single-token-verdict",
|
|
1810
|
+
snoGpu: "single-token-verdict"
|
|
1811
|
+
}
|
|
1812
|
+
},
|
|
1813
|
+
E11: {
|
|
1814
|
+
description: "date resolution",
|
|
1815
|
+
occasion: "dateResolution",
|
|
1816
|
+
destinations: host,
|
|
1817
|
+
snoPreset: chat,
|
|
1818
|
+
transport: {
|
|
1819
|
+
host: "agent-host-seam",
|
|
1820
|
+
snoGpu: "chat-completions"
|
|
1821
|
+
},
|
|
1822
|
+
promptVariant: {
|
|
1823
|
+
host: "standard",
|
|
1824
|
+
snoGpu: "standard"
|
|
1825
|
+
},
|
|
1826
|
+
replyParser: {
|
|
1827
|
+
host: "text",
|
|
1828
|
+
snoGpu: "text"
|
|
1829
|
+
}
|
|
1830
|
+
},
|
|
1831
|
+
E12: {
|
|
1832
|
+
description: "background state keying",
|
|
1833
|
+
occasion: "memoryExtract",
|
|
1834
|
+
destinations: extract,
|
|
1835
|
+
snoPreset: chat,
|
|
1836
|
+
transport: {
|
|
1837
|
+
host: "agent-host-seam",
|
|
1838
|
+
snoGpu: "chat-completions"
|
|
1839
|
+
},
|
|
1840
|
+
promptVariant: {
|
|
1841
|
+
host: "standard",
|
|
1842
|
+
snoGpu: "standard"
|
|
1843
|
+
},
|
|
1844
|
+
replyParser: {
|
|
1845
|
+
host: "json",
|
|
1846
|
+
snoGpu: "json"
|
|
1847
|
+
}
|
|
1848
|
+
},
|
|
1849
|
+
P1: {
|
|
1850
|
+
description: "profile conflict judgment",
|
|
1851
|
+
occasion: "conflictAdjudication",
|
|
1852
|
+
destinations: extract,
|
|
1853
|
+
snoPreset: verdict,
|
|
1854
|
+
transport: {
|
|
1855
|
+
host: "agent-host-seam",
|
|
1856
|
+
snoGpu: "raw-completions"
|
|
1857
|
+
},
|
|
1858
|
+
promptVariant: {
|
|
1859
|
+
host: "adapter-a-chat",
|
|
1860
|
+
snoGpu: "adapter-a-raw"
|
|
1861
|
+
},
|
|
1862
|
+
replyParser: {
|
|
1863
|
+
host: "json-or-single-token-verdict",
|
|
1864
|
+
snoGpu: "single-token-verdict"
|
|
1865
|
+
}
|
|
1866
|
+
},
|
|
1867
|
+
P2: {
|
|
1868
|
+
description: "profile lifecycle retirement",
|
|
1869
|
+
occasion: "profileSectionMerge",
|
|
1870
|
+
destinations: profile,
|
|
1871
|
+
snoPreset: chat,
|
|
1872
|
+
transport: {
|
|
1873
|
+
host: "agent-host-seam",
|
|
1874
|
+
snoGpu: "chat-completions"
|
|
1875
|
+
},
|
|
1876
|
+
promptVariant: {
|
|
1877
|
+
host: "standard",
|
|
1878
|
+
snoGpu: "standard"
|
|
1879
|
+
},
|
|
1880
|
+
replyParser: {
|
|
1881
|
+
host: "json",
|
|
1882
|
+
snoGpu: "json"
|
|
1883
|
+
}
|
|
1884
|
+
},
|
|
1885
|
+
P3: {
|
|
1886
|
+
description: "profile retirement recheck",
|
|
1887
|
+
occasion: "profileSectionMerge",
|
|
1888
|
+
destinations: profile,
|
|
1889
|
+
snoPreset: chat,
|
|
1890
|
+
transport: {
|
|
1891
|
+
host: "agent-host-seam",
|
|
1892
|
+
snoGpu: "chat-completions"
|
|
1893
|
+
},
|
|
1894
|
+
promptVariant: {
|
|
1895
|
+
host: "standard",
|
|
1896
|
+
snoGpu: "standard"
|
|
1897
|
+
},
|
|
1898
|
+
replyParser: {
|
|
1899
|
+
host: "json",
|
|
1900
|
+
snoGpu: "json"
|
|
1901
|
+
}
|
|
1902
|
+
},
|
|
1903
|
+
P4: {
|
|
1904
|
+
description: "profile section judgment",
|
|
1905
|
+
occasion: "profileSectionMerge",
|
|
1906
|
+
destinations: profile,
|
|
1907
|
+
snoPreset: chat,
|
|
1908
|
+
transport: {
|
|
1909
|
+
host: "agent-host-seam",
|
|
1910
|
+
snoGpu: "chat-completions"
|
|
1911
|
+
},
|
|
1912
|
+
promptVariant: {
|
|
1913
|
+
host: "standard",
|
|
1914
|
+
snoGpu: "standard"
|
|
1915
|
+
},
|
|
1916
|
+
replyParser: {
|
|
1917
|
+
host: "json",
|
|
1918
|
+
snoGpu: "json"
|
|
1919
|
+
}
|
|
1920
|
+
},
|
|
1921
|
+
P5: {
|
|
1922
|
+
description: "profile section text",
|
|
1923
|
+
occasion: "profileSectionMerge",
|
|
1924
|
+
destinations: profile,
|
|
1925
|
+
snoPreset: chat,
|
|
1926
|
+
transport: {
|
|
1927
|
+
host: "agent-host-seam",
|
|
1928
|
+
snoGpu: "chat-completions"
|
|
1929
|
+
},
|
|
1930
|
+
promptVariant: {
|
|
1931
|
+
host: "standard",
|
|
1932
|
+
snoGpu: "standard"
|
|
1933
|
+
},
|
|
1934
|
+
replyParser: {
|
|
1935
|
+
host: "json",
|
|
1936
|
+
snoGpu: "json"
|
|
1937
|
+
}
|
|
1938
|
+
},
|
|
1939
|
+
P6: {
|
|
1940
|
+
description: "retire by name",
|
|
1941
|
+
occasion: "profileSectionMerge",
|
|
1942
|
+
destinations: profile,
|
|
1943
|
+
snoPreset: chat,
|
|
1944
|
+
transport: {
|
|
1945
|
+
host: "agent-host-seam",
|
|
1946
|
+
snoGpu: "chat-completions"
|
|
1947
|
+
},
|
|
1948
|
+
promptVariant: {
|
|
1949
|
+
host: "standard",
|
|
1950
|
+
snoGpu: "standard"
|
|
1951
|
+
},
|
|
1952
|
+
replyParser: {
|
|
1953
|
+
host: "json",
|
|
1954
|
+
snoGpu: "json"
|
|
1955
|
+
}
|
|
1956
|
+
},
|
|
1957
|
+
T1: {
|
|
1958
|
+
description: "active task classification and match",
|
|
1959
|
+
occasion: "profileActiveTaskClassify",
|
|
1960
|
+
destinations: host,
|
|
1961
|
+
snoPreset: chat,
|
|
1962
|
+
transport: {
|
|
1963
|
+
host: "agent-host-seam",
|
|
1964
|
+
snoGpu: "chat-completions"
|
|
1965
|
+
},
|
|
1966
|
+
promptVariant: {
|
|
1967
|
+
host: "standard",
|
|
1968
|
+
snoGpu: "standard"
|
|
1969
|
+
},
|
|
1970
|
+
replyParser: {
|
|
1971
|
+
host: "json",
|
|
1972
|
+
snoGpu: "json"
|
|
1973
|
+
}
|
|
1974
|
+
},
|
|
1975
|
+
R1: {
|
|
1976
|
+
description: "session reflection",
|
|
1977
|
+
occasion: "summaryBuild",
|
|
1978
|
+
destinations: host,
|
|
1979
|
+
snoPreset: chat,
|
|
1980
|
+
transport: {
|
|
1981
|
+
host: "agent-host-seam",
|
|
1982
|
+
snoGpu: "chat-completions"
|
|
1983
|
+
},
|
|
1984
|
+
promptVariant: {
|
|
1985
|
+
host: "standard",
|
|
1986
|
+
snoGpu: "standard"
|
|
1987
|
+
},
|
|
1988
|
+
replyParser: {
|
|
1989
|
+
host: "text",
|
|
1990
|
+
snoGpu: "text"
|
|
1991
|
+
}
|
|
1992
|
+
},
|
|
1993
|
+
R2: {
|
|
1994
|
+
description: "label local sessions before the nightly cloud upload",
|
|
1995
|
+
calledBy: "RSI skill",
|
|
1996
|
+
destinations: host
|
|
1997
|
+
},
|
|
1998
|
+
R3: {
|
|
1999
|
+
description: "upload sessions nightly for cloud experience and skill review",
|
|
2000
|
+
calledBy: "RSI skill",
|
|
2001
|
+
destinations: {
|
|
2002
|
+
"local-first": "off",
|
|
2003
|
+
"agent-native": "sno-gpu",
|
|
2004
|
+
"rem-enhanced": "sno-gpu"
|
|
2005
|
+
}
|
|
2006
|
+
},
|
|
2007
|
+
R4: {
|
|
2008
|
+
description: "recall one relevant cloud experience on a session's first prompt",
|
|
2009
|
+
calledBy: "RSI skill",
|
|
2010
|
+
destinations: {
|
|
2011
|
+
"local-first": "off",
|
|
2012
|
+
"agent-native": "sno-gpu",
|
|
2013
|
+
"rem-enhanced": "sno-gpu"
|
|
2014
|
+
}
|
|
2015
|
+
},
|
|
2016
|
+
REM1: {
|
|
2017
|
+
description: "replace conflict pair",
|
|
2018
|
+
occasion: "conflictAdjudication",
|
|
2019
|
+
destinations: rem,
|
|
2020
|
+
snoPreset: verdict,
|
|
2021
|
+
transport: {
|
|
2022
|
+
host: "agent-host-seam",
|
|
2023
|
+
snoGpu: "raw-completions"
|
|
2024
|
+
},
|
|
2025
|
+
promptVariant: {
|
|
2026
|
+
host: "adapter-a-chat",
|
|
2027
|
+
snoGpu: "adapter-a-raw"
|
|
2028
|
+
},
|
|
2029
|
+
replyParser: {
|
|
2030
|
+
host: "json-or-single-token-verdict",
|
|
2031
|
+
snoGpu: "single-token-verdict"
|
|
2032
|
+
}
|
|
2033
|
+
},
|
|
2034
|
+
REM2: {
|
|
2035
|
+
description: "replace clauses",
|
|
2036
|
+
occasion: "memoryExtract",
|
|
2037
|
+
destinations: remHost,
|
|
2038
|
+
snoPreset: chat,
|
|
2039
|
+
transport: {
|
|
2040
|
+
host: "agent-host-seam",
|
|
2041
|
+
snoGpu: "chat-completions"
|
|
2042
|
+
},
|
|
2043
|
+
promptVariant: {
|
|
2044
|
+
host: "standard",
|
|
2045
|
+
snoGpu: "standard"
|
|
2046
|
+
},
|
|
2047
|
+
replyParser: {
|
|
2048
|
+
host: "json",
|
|
2049
|
+
snoGpu: "json"
|
|
2050
|
+
}
|
|
2051
|
+
},
|
|
2052
|
+
REM3: {
|
|
2053
|
+
description: "replace coverage",
|
|
2054
|
+
occasion: "memoryExtract",
|
|
2055
|
+
destinations: rem,
|
|
2056
|
+
snoPreset: chat,
|
|
2057
|
+
transport: {
|
|
2058
|
+
host: "agent-host-seam",
|
|
2059
|
+
snoGpu: "chat-completions"
|
|
2060
|
+
},
|
|
2061
|
+
promptVariant: {
|
|
2062
|
+
host: "standard",
|
|
2063
|
+
snoGpu: "standard"
|
|
2064
|
+
},
|
|
2065
|
+
replyParser: {
|
|
2066
|
+
host: "json",
|
|
2067
|
+
snoGpu: "json"
|
|
2068
|
+
}
|
|
2069
|
+
},
|
|
2070
|
+
REM4: {
|
|
2071
|
+
description: "replace clause carry",
|
|
2072
|
+
occasion: "memoryExtract",
|
|
2073
|
+
destinations: rem,
|
|
2074
|
+
snoPreset: chat,
|
|
2075
|
+
transport: {
|
|
2076
|
+
host: "agent-host-seam",
|
|
2077
|
+
snoGpu: "chat-completions"
|
|
2078
|
+
},
|
|
2079
|
+
promptVariant: {
|
|
2080
|
+
host: "standard",
|
|
2081
|
+
snoGpu: "standard"
|
|
2082
|
+
},
|
|
2083
|
+
replyParser: {
|
|
2084
|
+
host: "json",
|
|
2085
|
+
snoGpu: "json"
|
|
2086
|
+
}
|
|
2087
|
+
},
|
|
2088
|
+
REM5: {
|
|
2089
|
+
description: "update judgment",
|
|
2090
|
+
occasion: "memoryExtract",
|
|
2091
|
+
destinations: rem,
|
|
2092
|
+
snoPreset: chat,
|
|
2093
|
+
transport: {
|
|
2094
|
+
host: "agent-host-seam",
|
|
2095
|
+
snoGpu: "chat-completions"
|
|
2096
|
+
},
|
|
2097
|
+
promptVariant: {
|
|
2098
|
+
host: "standard",
|
|
2099
|
+
snoGpu: "standard"
|
|
2100
|
+
},
|
|
2101
|
+
replyParser: {
|
|
2102
|
+
host: "json",
|
|
2103
|
+
snoGpu: "json"
|
|
2104
|
+
}
|
|
2105
|
+
},
|
|
2106
|
+
REM6: {
|
|
2107
|
+
description: "update verification",
|
|
2108
|
+
occasion: "memoryExtract",
|
|
2109
|
+
destinations: remHost,
|
|
2110
|
+
snoPreset: chat,
|
|
2111
|
+
transport: {
|
|
2112
|
+
host: "agent-host-seam",
|
|
2113
|
+
snoGpu: "chat-completions"
|
|
2114
|
+
},
|
|
2115
|
+
promptVariant: {
|
|
2116
|
+
host: "standard",
|
|
2117
|
+
snoGpu: "standard"
|
|
2118
|
+
},
|
|
2119
|
+
replyParser: {
|
|
2120
|
+
host: "json",
|
|
2121
|
+
snoGpu: "json"
|
|
2122
|
+
}
|
|
2123
|
+
},
|
|
2124
|
+
REM7: {
|
|
2125
|
+
description: "update relation",
|
|
2126
|
+
occasion: "memoryExtract",
|
|
2127
|
+
destinations: remHost,
|
|
2128
|
+
snoPreset: chat,
|
|
2129
|
+
transport: {
|
|
2130
|
+
host: "agent-host-seam",
|
|
2131
|
+
snoGpu: "chat-completions"
|
|
2132
|
+
},
|
|
2133
|
+
promptVariant: {
|
|
2134
|
+
host: "standard",
|
|
2135
|
+
snoGpu: "standard"
|
|
2136
|
+
},
|
|
2137
|
+
replyParser: {
|
|
2138
|
+
host: "json",
|
|
2139
|
+
snoGpu: "json"
|
|
2140
|
+
}
|
|
2141
|
+
},
|
|
2142
|
+
REM8: {
|
|
2143
|
+
description: "update retirement target",
|
|
2144
|
+
occasion: "memoryExtract",
|
|
2145
|
+
destinations: remHost,
|
|
2146
|
+
snoPreset: chat,
|
|
2147
|
+
transport: {
|
|
2148
|
+
host: "agent-host-seam",
|
|
2149
|
+
snoGpu: "chat-completions"
|
|
2150
|
+
},
|
|
2151
|
+
promptVariant: {
|
|
2152
|
+
host: "standard",
|
|
2153
|
+
snoGpu: "standard"
|
|
2154
|
+
},
|
|
2155
|
+
replyParser: {
|
|
2156
|
+
host: "json",
|
|
2157
|
+
snoGpu: "json"
|
|
2158
|
+
}
|
|
2159
|
+
}
|
|
2160
|
+
};
|
|
2161
|
+
function modelCallDestination(callId, mode, modelCalls) {
|
|
2162
|
+
return modelCalls[callId][mode];
|
|
2163
|
+
}
|
|
2164
|
+
//#endregion
|
|
2165
|
+
//#region config/settings.ts
|
|
2166
|
+
const destination = z.enum([
|
|
2167
|
+
"off",
|
|
2168
|
+
"host",
|
|
2169
|
+
"sno-gpu"
|
|
2170
|
+
]);
|
|
2171
|
+
const destinations = z.strictObject({
|
|
2172
|
+
"local-first": destination,
|
|
2173
|
+
"agent-native": destination,
|
|
2174
|
+
"rem-enhanced": destination
|
|
2175
|
+
});
|
|
2176
|
+
const modelCalls = z.strictObject(Object.fromEntries(Object.keys(MODEL_CALLS).map((id) => [id, destinations])));
|
|
2177
|
+
const settingsSchema = z.strictObject({
|
|
2178
|
+
mode: z.enum(PRODUCT_MODES),
|
|
2179
|
+
modelCalls,
|
|
2180
|
+
user: z.strictObject({
|
|
2181
|
+
id: z.string().refine((id) => id === "" || isLowercaseCanonicalUUIDv7(id)),
|
|
2182
|
+
language: z.enum(SUPPORTED_LOCALES)
|
|
2183
|
+
}),
|
|
2184
|
+
snoGpu: z.strictObject({
|
|
2185
|
+
baseUrl: z.string(),
|
|
2186
|
+
apiKey: z.string()
|
|
2187
|
+
}),
|
|
2188
|
+
rerank: z.strictObject({
|
|
2189
|
+
mode: z.enum([
|
|
2190
|
+
"lightweight",
|
|
2191
|
+
"cross-encoder",
|
|
2192
|
+
"none"
|
|
2193
|
+
]),
|
|
2194
|
+
provider: z.enum([
|
|
2195
|
+
"jina",
|
|
2196
|
+
"siliconflow",
|
|
2197
|
+
"pinecone",
|
|
2198
|
+
"voyage",
|
|
2199
|
+
"dashscope",
|
|
2200
|
+
"tei",
|
|
2201
|
+
"custom"
|
|
2202
|
+
]),
|
|
2203
|
+
endpoint: z.string(),
|
|
2204
|
+
model: z.string(),
|
|
2205
|
+
apiKey: z.string(),
|
|
2206
|
+
timeoutMs: z.number().int().min(1e3).max(12e4),
|
|
2207
|
+
maxCandidates: z.number().int().min(1).max(2e3)
|
|
2208
|
+
}),
|
|
2209
|
+
store: z.strictObject({
|
|
2210
|
+
path: z.string(),
|
|
2211
|
+
encryptionKey: z.string().min(1).regex(/^[0-9a-f]{64}$/u)
|
|
2212
|
+
}),
|
|
2213
|
+
embedding: z.strictObject({
|
|
2214
|
+
model: z.string(),
|
|
2215
|
+
revision: z.string(),
|
|
2216
|
+
dtype: z.enum([
|
|
2217
|
+
"q4",
|
|
2218
|
+
"q8",
|
|
2219
|
+
"fp16",
|
|
2220
|
+
"fp32"
|
|
2221
|
+
]),
|
|
2222
|
+
threads: z.number().int().nonnegative(),
|
|
2223
|
+
cacheDir: z.string(),
|
|
2224
|
+
offline: z.boolean(),
|
|
2225
|
+
mirror: z.string()
|
|
2226
|
+
}),
|
|
2227
|
+
recall: z.strictObject({
|
|
2228
|
+
auto: z.boolean(),
|
|
2229
|
+
sessionStart: z.strictObject({
|
|
2230
|
+
limit: z.number().int().nonnegative(),
|
|
2231
|
+
maxChars: z.number().int().nonnegative(),
|
|
2232
|
+
timeoutMs: z.number().int().nonnegative()
|
|
2233
|
+
}),
|
|
2234
|
+
prompt: z.strictObject({
|
|
2235
|
+
limit: z.number().int().min(0).max(2e3),
|
|
2236
|
+
maxChars: z.number().int().nonnegative(),
|
|
2237
|
+
timeoutMs: z.number().int().min(500).max(6e4),
|
|
2238
|
+
minChars: z.number().int().min(0).max(200),
|
|
2239
|
+
minScore: z.number().min(0).max(1)
|
|
2240
|
+
}),
|
|
2241
|
+
explicitLimit: z.number().int().nonnegative()
|
|
2242
|
+
}),
|
|
2243
|
+
capture: z.strictObject({
|
|
2244
|
+
assistant: z.boolean(),
|
|
2245
|
+
ambient: z.boolean(),
|
|
2246
|
+
sessionMemory: z.strictObject({
|
|
2247
|
+
enabled: z.boolean(),
|
|
2248
|
+
messageCount: z.number().int().min(1).max(100)
|
|
2249
|
+
}),
|
|
2250
|
+
sessionStrategy: z.enum(SESSION_STRATEGIES)
|
|
2251
|
+
}),
|
|
2252
|
+
rem: z.strictObject({
|
|
2253
|
+
tick: z.boolean(),
|
|
2254
|
+
operations: z.array(z.enum(["rem-replace", "rem-update"])).min(1).max(2)
|
|
2255
|
+
}),
|
|
2256
|
+
telemetry: z.strictObject({
|
|
2257
|
+
memoryUsage: z.strictObject({
|
|
2258
|
+
enabled: z.boolean(),
|
|
2259
|
+
key: z.string(),
|
|
2260
|
+
historicKeys: z.array(z.string())
|
|
2261
|
+
}),
|
|
2262
|
+
observe: z.strictObject({
|
|
2263
|
+
enabled: z.boolean(),
|
|
2264
|
+
baseUrl: z.string()
|
|
2265
|
+
}),
|
|
2266
|
+
redactionRules: z.array(z.string())
|
|
2267
|
+
}),
|
|
2268
|
+
logging: z.strictObject({
|
|
2269
|
+
level: z.enum([
|
|
2270
|
+
"debug",
|
|
2271
|
+
"info",
|
|
2272
|
+
"warn",
|
|
2273
|
+
"error"
|
|
2274
|
+
]),
|
|
2275
|
+
file: z.string(),
|
|
2276
|
+
debugContent: z.boolean()
|
|
2277
|
+
}),
|
|
2278
|
+
memoryPackage: z.strictObject({
|
|
2279
|
+
path: z.string(),
|
|
2280
|
+
node: z.string()
|
|
2281
|
+
})
|
|
2282
|
+
});
|
|
2283
|
+
function defaultSettings() {
|
|
2284
|
+
const engine = pluginConfigSchema.parse({});
|
|
2285
|
+
return {
|
|
2286
|
+
mode: engine.mode,
|
|
2287
|
+
modelCalls: Object.fromEntries(Object.entries(MODEL_CALLS).map(([id, call]) => [id, call.destinations])),
|
|
2288
|
+
user: {
|
|
2289
|
+
id: "",
|
|
2290
|
+
language: "en"
|
|
2291
|
+
},
|
|
2292
|
+
snoGpu: {
|
|
2293
|
+
baseUrl: "https://rt3-llm.sno.ai",
|
|
2294
|
+
apiKey: ""
|
|
2295
|
+
},
|
|
2296
|
+
rerank: {
|
|
2297
|
+
mode: "lightweight",
|
|
2298
|
+
provider: "voyage",
|
|
2299
|
+
endpoint: "",
|
|
2300
|
+
model: engine.retrieval.rerankModel,
|
|
2301
|
+
apiKey: "",
|
|
2302
|
+
timeoutMs: engine.retrieval.rerankTimeoutMs,
|
|
2303
|
+
maxCandidates: 64
|
|
2304
|
+
},
|
|
2305
|
+
store: {
|
|
2306
|
+
path: "",
|
|
2307
|
+
encryptionKey: ""
|
|
2308
|
+
},
|
|
2309
|
+
embedding: {
|
|
2310
|
+
model: EMBEDDER_MODEL_DEFAULT,
|
|
2311
|
+
revision: LOCAL_EMBEDDING_MODEL_REVISION,
|
|
2312
|
+
dtype: engine.embedding.dtype,
|
|
2313
|
+
threads: 0,
|
|
2314
|
+
cacheDir: "",
|
|
2315
|
+
offline: false,
|
|
2316
|
+
mirror: ""
|
|
2317
|
+
},
|
|
2318
|
+
recall: {
|
|
2319
|
+
auto: true,
|
|
2320
|
+
sessionStart: {
|
|
2321
|
+
limit: 5,
|
|
2322
|
+
maxChars: 3500,
|
|
2323
|
+
timeoutMs: 8e3
|
|
2324
|
+
},
|
|
2325
|
+
prompt: {
|
|
2326
|
+
limit: 3,
|
|
2327
|
+
maxChars: 1500,
|
|
2328
|
+
timeoutMs: 4e3,
|
|
2329
|
+
minChars: 12,
|
|
2330
|
+
minScore: 0
|
|
2331
|
+
},
|
|
2332
|
+
explicitLimit: 5
|
|
2333
|
+
},
|
|
2334
|
+
capture: {
|
|
2335
|
+
assistant: true,
|
|
2336
|
+
ambient: true,
|
|
2337
|
+
sessionMemory: {
|
|
2338
|
+
enabled: true,
|
|
2339
|
+
messageCount: 15
|
|
2340
|
+
},
|
|
2341
|
+
sessionStrategy: "systemSessionMemory"
|
|
2342
|
+
},
|
|
2343
|
+
rem: {
|
|
2344
|
+
tick: engine.remEnhanced?.trigger.tick ?? true,
|
|
2345
|
+
operations: engine.remOperations
|
|
2346
|
+
},
|
|
2347
|
+
telemetry: {
|
|
2348
|
+
memoryUsage: {
|
|
2349
|
+
enabled: false,
|
|
2350
|
+
key: "",
|
|
2351
|
+
historicKeys: []
|
|
2352
|
+
},
|
|
2353
|
+
observe: {
|
|
2354
|
+
enabled: true,
|
|
2355
|
+
baseUrl: "https://www.sno.ai"
|
|
2356
|
+
},
|
|
2357
|
+
redactionRules: []
|
|
2358
|
+
},
|
|
2359
|
+
logging: {
|
|
2360
|
+
level: "info",
|
|
2361
|
+
file: "",
|
|
2362
|
+
debugContent: false
|
|
2363
|
+
},
|
|
2364
|
+
memoryPackage: {
|
|
2365
|
+
path: "",
|
|
2366
|
+
node: ""
|
|
2367
|
+
}
|
|
2368
|
+
};
|
|
2369
|
+
}
|
|
2370
|
+
function settingsToPluginConfig(settings) {
|
|
2371
|
+
return pluginConfigSchema.parse({
|
|
2372
|
+
mode: settings.mode,
|
|
2373
|
+
modelCalls: settings.modelCalls,
|
|
2374
|
+
provider: { userId: settings.user.id || void 0 },
|
|
2375
|
+
language: settings.user.language,
|
|
2376
|
+
dbPath: settings.store.path,
|
|
2377
|
+
embedding: {
|
|
2378
|
+
model: settings.embedding.model,
|
|
2379
|
+
revision: settings.embedding.revision || void 0,
|
|
2380
|
+
dtype: settings.embedding.dtype,
|
|
2381
|
+
cacheDir: settings.embedding.cacheDir || void 0,
|
|
2382
|
+
offline: settings.embedding.offline,
|
|
2383
|
+
mirror: settings.embedding.mirror,
|
|
2384
|
+
sessionOptions: { intraOpNumThreads: settings.embedding.threads || void 0 }
|
|
2385
|
+
},
|
|
2386
|
+
retrieval: {
|
|
2387
|
+
rerank: settings.rerank.mode,
|
|
2388
|
+
rerankProvider: settings.rerank.provider,
|
|
2389
|
+
recallTopK: settings.recall.prompt.limit,
|
|
2390
|
+
minScore: settings.recall.prompt.minScore,
|
|
2391
|
+
rerankEndpoint: settings.rerank.endpoint || void 0,
|
|
2392
|
+
rerankModel: settings.rerank.model,
|
|
2393
|
+
rerankApiKey: settings.rerank.apiKey,
|
|
2394
|
+
rerankTimeoutMs: settings.rerank.timeoutMs,
|
|
2395
|
+
rerankMaxCandidates: settings.rerank.maxCandidates
|
|
2396
|
+
},
|
|
2397
|
+
autoRecall: settings.recall.auto,
|
|
2398
|
+
autoRecallTimeoutMs: settings.recall.prompt.timeoutMs,
|
|
2399
|
+
autoRecallMinLength: settings.recall.prompt.minChars,
|
|
2400
|
+
captureAssistant: settings.capture.assistant,
|
|
2401
|
+
ambientLearning: settings.capture.ambient,
|
|
2402
|
+
debugContent: settings.logging.debugContent,
|
|
2403
|
+
sessionMemory: settings.capture.sessionMemory,
|
|
2404
|
+
sessionStrategy: settings.capture.sessionStrategy,
|
|
2405
|
+
remEnhanced: { trigger: { tick: settings.rem.tick } },
|
|
2406
|
+
remOperations: settings.rem.operations,
|
|
2407
|
+
memoryTelemetry: {
|
|
2408
|
+
enabled: settings.telemetry.memoryUsage.enabled,
|
|
2409
|
+
currentKeyVersion: settings.telemetry.memoryUsage.historicKeys.length + 1,
|
|
2410
|
+
key: settings.telemetry.memoryUsage.key,
|
|
2411
|
+
historicKeys: settings.telemetry.memoryUsage.historicKeys
|
|
2412
|
+
},
|
|
2413
|
+
observe: settings.telemetry.observe,
|
|
2414
|
+
extraction: { llm: {
|
|
2415
|
+
baseURL: settings.snoGpu.baseUrl,
|
|
2416
|
+
apiKey: settings.snoGpu.apiKey
|
|
2417
|
+
} }
|
|
2418
|
+
});
|
|
2419
|
+
}
|
|
2420
|
+
//#endregion
|
|
2421
|
+
//#region src/contract/profile.ts
|
|
2422
|
+
function getStateDir(defaultStateDir) {
|
|
2423
|
+
return path.resolve(process.env.SNO_PROFILE_DIR ?? defaultStateDir ?? path.join(homedir(), ".sno"));
|
|
2424
|
+
}
|
|
2425
|
+
function getSettingsPath() {
|
|
2426
|
+
return path.join(getStateDir(), "settings.json");
|
|
2427
|
+
}
|
|
2428
|
+
var SettingsUnavailableError = class extends Error {};
|
|
2429
|
+
function readSettings() {
|
|
2430
|
+
const settingsPath = getSettingsPath();
|
|
2431
|
+
let raw;
|
|
2432
|
+
try {
|
|
2433
|
+
raw = JSON.parse(readFileSync(settingsPath, "utf8"));
|
|
2434
|
+
} catch (error) {
|
|
2435
|
+
throw new SettingsUnavailableError(`settings unavailable: ${settingsPath}: ${error instanceof Error && "code" in error && error.code === "ENOENT" ? "file" : error instanceof SyntaxError ? `JSON: ${error.message}` : "file"}; see https://github.com/sno-ai/sno-station/blob/main/docs/memory-setup.md`);
|
|
2436
|
+
}
|
|
2437
|
+
const parsed = settingsSchema.safeParse(raw);
|
|
2438
|
+
if (!parsed.success) {
|
|
2439
|
+
const issue = parsed.error.issues[0];
|
|
2440
|
+
throw new SettingsUnavailableError(`settings unavailable: ${settingsPath}: ${issue?.code === "unrecognized_keys" ? [...issue.path, issue.keys[0]].join(".") : issue?.path.join(".") || "settings"}; see https://github.com/sno-ai/sno-station/blob/main/docs/memory-setup.md`);
|
|
2441
|
+
}
|
|
2442
|
+
return parsed.data;
|
|
2443
|
+
}
|
|
2444
|
+
function getSnoStationMemStateDir() {
|
|
2445
|
+
return path.join(getStateDir(), "sno-station-mem");
|
|
2446
|
+
}
|
|
2447
|
+
function getPrincipal() {
|
|
2448
|
+
return userInfo().username;
|
|
2449
|
+
}
|
|
2450
|
+
function getDiscoveryPath() {
|
|
2451
|
+
return path.join(getStateDir(), "station", "sidecar.json");
|
|
2452
|
+
}
|
|
2453
|
+
function getSidecarSocketPath() {
|
|
2454
|
+
return path.join(getStateDir(), "station", "sidecar.sock");
|
|
2455
|
+
}
|
|
2456
|
+
//#endregion
|
|
2457
|
+
//#region src/sidecar/config.ts
|
|
2458
|
+
/** @file config.ts
|
|
2459
|
+
* @purpose Centralizes the standalone REM sidecar's paths, protocol constants, and boot settings.
|
|
2460
|
+
* @boundary Environment and filesystem configuration for CLI discovery and local REM state.
|
|
2461
|
+
*/
|
|
2462
|
+
const REM_SIDECAR_HOST = "127.0.0.1";
|
|
2463
|
+
const REM_SIDECAR_ORIGIN = "http://127.0.0.1";
|
|
2464
|
+
const REM_CORRELATION_ID_HEADER = "x-rem-correlation-id";
|
|
2465
|
+
const REM_RUN_PATH = "/rem/run";
|
|
2466
|
+
const REM_JOBS_PATH_PREFIX = "/rem/jobs/";
|
|
2467
|
+
const HEALTH_PATH = "/healthz";
|
|
2468
|
+
const REM_SOURCE = "sidecar";
|
|
2469
|
+
const REM_MODEL_OUTPUT_TOKEN_CAP = 4096;
|
|
2470
|
+
const MEMORY_USAGE_FLUSH_INTERVAL_MS = 3e5;
|
|
2471
|
+
const SNO_REM_TEST_HOLD_MS_ENV = "SNO_STATION_MEM_REM_TEST_HOLD_MS";
|
|
2472
|
+
const SNO_REM_TRACE_ENV = "SNO_STATION_MEM_REM_TRACE";
|
|
2473
|
+
const REM_JOB_JOURNAL_NAME = "rem-wave-jobs.jsonl";
|
|
2474
|
+
const REM_TRACE_LOG_NAME = "rem-trace.jsonl";
|
|
2475
|
+
const REM_CHASSIS_JOURNAL_NAME = "rem-chassis-journal.jsonl";
|
|
2476
|
+
function getRemDiscoveryPath() {
|
|
2477
|
+
return getDiscoveryPath();
|
|
2478
|
+
}
|
|
2479
|
+
function getRemJobJournalPath() {
|
|
2480
|
+
return path.join(getSnoStationMemStateDir(), REM_JOB_JOURNAL_NAME);
|
|
2481
|
+
}
|
|
2482
|
+
function getRemChassisJournalPath() {
|
|
2483
|
+
return path.join(getSnoStationMemStateDir(), REM_CHASSIS_JOURNAL_NAME);
|
|
2484
|
+
}
|
|
2485
|
+
function getRemTraceLogPath(stateRoot = getStateDir()) {
|
|
2486
|
+
return path.join(stateRoot, "sno-station-mem", REM_TRACE_LOG_NAME);
|
|
2487
|
+
}
|
|
2488
|
+
function isRemTraceEnabled() {
|
|
2489
|
+
return ![
|
|
2490
|
+
"0",
|
|
2491
|
+
"false",
|
|
2492
|
+
"off"
|
|
2493
|
+
].includes((process.env[SNO_REM_TRACE_ENV] ?? "1").toLowerCase());
|
|
2494
|
+
}
|
|
2495
|
+
function readRemTestHoldMs() {
|
|
2496
|
+
const raw = process.env[SNO_REM_TEST_HOLD_MS_ENV];
|
|
2497
|
+
if (raw === void 0) return 0;
|
|
2498
|
+
const value = Number(raw);
|
|
2499
|
+
if (!Number.isSafeInteger(value) || value < 0) {
|
|
2500
|
+
reportConfigFailure(/* @__PURE__ */ new Error(`${SNO_REM_TEST_HOLD_MS_ENV} must be a non-negative integer`));
|
|
2501
|
+
return 0;
|
|
2502
|
+
}
|
|
2503
|
+
return value;
|
|
2504
|
+
}
|
|
2505
|
+
const DEFAULT_REM_CONFIGURATION = {
|
|
2506
|
+
profileId: "default",
|
|
2507
|
+
operations: {
|
|
2508
|
+
"rem-replace": true,
|
|
2509
|
+
"rem-update": true,
|
|
2510
|
+
"rem-distill": true,
|
|
2511
|
+
"rem-retire": true
|
|
2512
|
+
},
|
|
2513
|
+
budgets: { maxPairs: 200 },
|
|
2514
|
+
retrieval: {
|
|
2515
|
+
neighborLimit: 10,
|
|
2516
|
+
similarityThreshold: .8
|
|
2517
|
+
},
|
|
2518
|
+
coverage: { accuracyFloor: null },
|
|
2519
|
+
retries: { liveContentionRetries: 1 },
|
|
2520
|
+
facetPolicy: {
|
|
2521
|
+
aggregationGrammar: "current-first-v1",
|
|
2522
|
+
historyGrammar: "history-evidence-v1"
|
|
2523
|
+
},
|
|
2524
|
+
calibration: {
|
|
2525
|
+
minimumPublishableScoreEffect: null,
|
|
2526
|
+
minimumTargetCount: null,
|
|
2527
|
+
minimumTargetPercent: null
|
|
2528
|
+
},
|
|
2529
|
+
enableGateDigests: {
|
|
2530
|
+
"p5-production-config": "0".repeat(64),
|
|
2531
|
+
"p6-monthly-non-regression": "0".repeat(64),
|
|
2532
|
+
"p7-detector-gate-verdict": "0".repeat(64),
|
|
2533
|
+
"population-routing": "0".repeat(64),
|
|
2534
|
+
"rem-update": "0".repeat(64),
|
|
2535
|
+
"rem-replace": "0".repeat(64)
|
|
2536
|
+
}
|
|
2537
|
+
};
|
|
2538
|
+
function reportConfigFailure(error) {
|
|
2539
|
+
createLogger("sno-station-mem:config").error("sidecar.config.failed", { error }, {
|
|
2540
|
+
event_name: "sidecar.config.failed",
|
|
2541
|
+
file: "packages/memory/src/sidecar/config.ts",
|
|
2542
|
+
function: "reportConfigFailure",
|
|
2543
|
+
site_id: "sidecar.config.failed"
|
|
2544
|
+
});
|
|
2545
|
+
}
|
|
2546
|
+
function readRemOperationalConfig() {
|
|
2547
|
+
return DEFAULT_REM_CONFIGURATION;
|
|
2548
|
+
}
|
|
2549
|
+
const MAINTENANCE_INTERVAL_ENV = "SNO_STATION_MEM_MAINTENANCE_INTERVAL_MS";
|
|
2550
|
+
const REM_CLOCK_OVERRIDE_ENV = "SNO_STATION_MEM_REM_CLOCK_OVERRIDE";
|
|
2551
|
+
const REM_VOLUME_THRESHOLD_ENV = "SNO_STATION_MEM_REM_VOLUME_THRESHOLD";
|
|
2552
|
+
function readMaintenanceOverrides() {
|
|
2553
|
+
const positive = (name) => {
|
|
2554
|
+
const raw = process.env[name];
|
|
2555
|
+
if (raw === void 0) return void 0;
|
|
2556
|
+
const value = Number(raw);
|
|
2557
|
+
if (!Number.isSafeInteger(value) || value < 1) {
|
|
2558
|
+
reportConfigFailure(/* @__PURE__ */ new Error(`${name} must be a positive integer`));
|
|
2559
|
+
return;
|
|
2560
|
+
}
|
|
2561
|
+
return value;
|
|
2562
|
+
};
|
|
2563
|
+
const rawClock = process.env[REM_CLOCK_OVERRIDE_ENV];
|
|
2564
|
+
const now = rawClock === void 0 ? void 0 : new Date(rawClock);
|
|
2565
|
+
if (now && Number.isNaN(now.getTime())) {
|
|
2566
|
+
reportConfigFailure(/* @__PURE__ */ new Error(`${REM_CLOCK_OVERRIDE_ENV} must be an ISO timestamp`));
|
|
2567
|
+
return {};
|
|
2568
|
+
}
|
|
2569
|
+
return {
|
|
2570
|
+
intervalMs: positive(MAINTENANCE_INTERVAL_ENV),
|
|
2571
|
+
now,
|
|
2572
|
+
volumeThreshold: positive(REM_VOLUME_THRESHOLD_ENV)
|
|
2573
|
+
};
|
|
2574
|
+
}
|
|
2575
|
+
//#endregion
|
|
2576
|
+
export { FIXED_IDX_MEMORY_ACTIVE_TASK_EVIDENCE_INSTANCE as $, MAX_CHUNK_FETCH_LIMIT as $n, FIXED_VEC_MEMORY as $t, SESSION_STRATEGIES as A, DEFAULT_REFLECTION_SESSION_TTL_MS as An, FIXED_MEMORY_PROVIDER_AGENT_MAPPINGS as At, FIXED_EXTERNAL_VALUE_51 as B, FTS_MERGE_INTERVAL_MS as Bn, FIXED_PROTOCOL_VALUE_64 as Bt, getSnoStationMemStateDir as C, CHUNK_TARGET_TOKENS as Cn, LOCAL_EMBEDDING_CACHE_DIR_DEFAULT as Cr, FIXED_MEMORY_CHUNKS_MEM_IDX as Ct, settingsToPluginConfig as D, DEFAULT_RECALL_LIFECYCLE as Dn, ensureModelDownloaded as Dr, FIXED_MEMORY_MEMORIES_FACT_ID_UPDATE_GUARD as Dt, defaultSettings as E, DEFAULT_IMPORTANCE as En, LocalEmbedProvider as Er, FIXED_MEMORY_MEMORIES_FACT_ID_INSERT_GUARD as Et, extractionConfigSchema as F, EMBEDDER_DIM as Fn, FIXED_MEMORY_SNO_EXTRACT_CHAT as Ft, FIXED_EXTERNAL_VALUE_56 as G, INTEGRITY_CHECK_INTERVAL_MS as Gn, FIXED_PROTOCOL_VALUE_69 as Gt, FIXED_EXTERNAL_VALUE_53 as H, HEAD_EXTRACT_OVERLAP_DROP_RATIO as Hn, FIXED_PROTOCOL_VALUE_66 as Ht, memoryReflectionConfigSchema as I, EMBEDDER_MODEL_DEFAULT as In, FIXED_MEMORY_SNO_EXTRACT_PROFILE as It, FIXED_EXTERNAL_VALUE_62 as J, LIGHTWEIGHT_FUSION_WEIGHT as Jn, FIXED_PROTOCOL_VALUE_75 as Jt, FIXED_EXTERNAL_VALUE_57 as K, INTRA_MEMORY_COSINE_THRESHOLD as Kn, FIXED_PROTOCOL_VALUE_73 as Kt, selfImprovementConfigSchema as L, EMBEDDER_PROVIDER_DEFAULT as Ln, FIXED_MEMORY_TASK_LIFECYCLE_COMMANDS as Lt, isObserveAgentId as M, DEFAULT_RERANK_TIMEOUT_MS as Mn, FIXED_MEMORY_PROVIDER_PROJECT_MAPPINGS as Mt, observeAgentId as N, DEFAULT_SCOPE as Nn, FIXED_MEMORY_ROW_PATH_PATTERN as Nt, MODEL_CALLS as O, DEFAULT_RECALL_TOKEN_BUDGET as On, FIXED_MEMORY_MIGRATION_MARKERS as Ot, observeConfigSchema as P, DEFAULT_VECTOR_WEIGHT as Pn, FIXED_MEMORY_SNO_AI_EXTRACT as Pt, FIXED_IDX_MEMORY_ACTIVE_TASK_CURRENT_REVISION as Q, MAX_CANDIDATE_POOL_SIZE as Qn, FIXED_PROTOCOL_VALUE_79 as Qt, sessionMemoryConfigSchema as R, EVAL_QA_TRACES_FILENAME as Rn, FIXED_MEMORY_UNPLACED_CANDIDATES as Rt, getSidecarSocketPath as S, CHUNK_OVERLAP_TOKENS as Sn, CachedEmbeddingProvider as Sr, FIXED_MEMORY_CHUNKS_MEM_FACET_IDX as St, readSettings as T, DEFAULT_BM25_WEIGHT as Tn, LOCAL_EMBEDDING_MODEL_REVISION as Tr, FIXED_MEMORY_MEMORIES as Tt, FIXED_EXTERNAL_VALUE_54 as U, HEAD_EXTRACT_TOKEN_BUDGET as Un, FIXED_PROTOCOL_VALUE_67 as Ut, FIXED_EXTERNAL_VALUE_52 as V, HEAD_EXTRACT_MIN_CONTENT_TOKENS as Vn, FIXED_PROTOCOL_VALUE_65 as Vt, FIXED_EXTERNAL_VALUE_55 as W, IMPORTANCE_WEIGHT_BASE as Wn, FIXED_PROTOCOL_VALUE_68 as Wt, FIXED_EXTERNAL_VALUE_9 as X, MAINTENANCE_TICK_MS as Xn, FIXED_PROTOCOL_VALUE_77 as Xt, FIXED_EXTERNAL_VALUE_8 as Y, LIGHTWEIGHT_RERANK_PENALTY as Yn, FIXED_PROTOCOL_VALUE_76 as Yt, FIXED_IDX_MEMORY as Z, MAX_AGGREGATION_RESULT_TOKENS as Zn, FIXED_PROTOCOL_VALUE_78 as Zt, readRemTestHoldMs as _, BONUS_TOTAL_INVARIANT as _n, USAGE_OUTBOX_INTERVAL_MS as _r, FIXED_MEMORY_CHUNKS_AD as _t, REM_MODEL_OUTPUT_TOKEN_CAP as a, embeddingConfigSchema as an, MULTI_HIT_BONUS_CAP as ar, FIXED_IDX_MEMORY_MEMORIES_PROJECT as at, getPrincipal as b, CHUNK_MAX_TOKENS as bn, experimentMmrDisabled as br, FIXED_MEMORY_CHUNKS_FTS as bt, REM_SIDECAR_ORIGIN as c, isSupportedLocale as cn, RECENCY_WEIGHT as cr, FIXED_IDX_MEMORY_MEMORIES_PROJECT_TIMESTAMP as ct, getRemDiscoveryPath as d, ATOMIC_CAPTURE_OUTPUT_TOKEN_BUDGET as dn, RERANK_BLEND_CROSS as dr, FIXED_MEMORY_ACTIVE_TASK_INSTANCES as dt, FIXED_VEC_MEMORY_CHUNKS as en, MAX_LIST_LIMIT as er, FIXED_IDX_MEMORY_ACTIVE_TASK_INSTANCES_PROJECTION as et, getRemJobJournalPath as f, ATOMIC_CAPTURE_TIMEOUT_MS as fn, RERANK_BLEND_VECTOR as fr, FIXED_MEMORY_ACTIVE_TASK_MIGRATION_EVIDENCE as ft, readRemOperationalConfig as g, BACKUP_MIN_FREE_DISK_RATIO as gn, USAGE_EVENT_RETENTION_INTERVAL_MS as gr, FIXED_MEMORY_CHUNKS as gt, readMaintenanceOverrides as h, BACKUP_INTERVAL_MS as hn, TIME_DECAY_FLOOR as hr, FIXED_MEMORY_ACTIVE_TASK_TRANSITIONS as ht, REM_JOBS_PATH_PREFIX as i, PERSISTED_PROVIDER_SYSTEM as in, MMR_LAMBDA as ir, FIXED_IDX_MEMORY_MEMORIES_LANE_PROJECT as it, retrievalConfigSchema as j, DEFAULT_RERANK_MODEL as jn, FIXED_MEMORY_PROVIDER_PROJECT_AGENTS as jt, modelCallDestination as k, DEFAULT_REFLECTION_ERROR_SCAN_MAX_CHARS as kn, FIXED_MEMORY_PROFILE_RECOVERY_ENTRIES as kt, REM_SOURCE as l, ADJACENCY_BONUS_CAP as ln, REFLECTION_SLICE_CACHE_TTL_MS as lr, FIXED_MEMORY as lt, isRemTraceEnabled as m, ATOMIC_EXTRACTION_MAX_INPUT_TOKENS as mn, SNO_OBSERVE_FLUSH_TIMEOUT_MS as mr, FIXED_MEMORY_ACTIVE_TASK_REVISIONS as mt, MEMORY_USAGE_FLUSH_INTERVAL_MS as n, PERSISTED_CONTENT_HASH_V1 as nn, MEMORY_SHUTDOWN_TIMEOUT_MS as nr, FIXED_IDX_MEMORY_MEMORIES_CONTENT_HASH as nt, REM_RUN_PATH as o, llmRoutingConfigSchema as on, MULTI_HIT_BONUS_PER_HIT as or, FIXED_IDX_MEMORY_MEMORIES_PROJECT_CONTENT_HASH as ot, getRemTraceLogPath as p, ATOMIC_ENRICHMENT_OUTPUT_TOKEN_BUDGET as pn, RETRIEVAL_STORAGE_CHUNK_PROFILE as pr, FIXED_MEMORY_ACTIVE_TASK_MIGRATION_MANIFESTS as pt, FIXED_EXTERNAL_VALUE_58 as q, LIGHTWEIGHT_COSINE_WEIGHT as qn, FIXED_PROTOCOL_VALUE_74 as qt, REM_CORRELATION_ID_HEADER as r, PERSISTED_CONTENT_HASH_V3 as rn, MIN_RECALL_TOKEN_BUDGET as rr, FIXED_IDX_MEMORY_MEMORIES_FACT_ID as rt, REM_SIDECAR_HOST as s, SUPPORTED_LOCALES as sn, PLANNER_STATISTICS_INTERVAL_MS as sr, FIXED_IDX_MEMORY_MEMORIES_PROJECT_FACT_KEY_ACTIVE as st, HEALTH_PATH as t, PERSISTED_CANDIDATE_HASH_V1 as tn, MAX_RECALLED_TODO_TOKENS as tr, FIXED_IDX_MEMORY_MEMORIES_CATEGORY as tt, getRemChassisJournalPath as u, ADJACENCY_BONUS_PER_PAIR as un, REM_TRIGGER_CHECK_INTERVAL_MS as ur, FIXED_MEMORY_ACTIVE_TASK_EVIDENCE as ut, SettingsUnavailableError as v, CACHE_TTL_MS as vn, VECTOR_DIMENSION_DEFAULT as vr, FIXED_MEMORY_CHUNKS_AI as vt, getStateDir as w, CHUNK_TOKENIZER_MODE as wn, LOCAL_EMBEDDING_MODEL as wr, FIXED_MEMORY_EXTRACTION_TIMESTAMPS as wt, getSettingsPath as x, CHUNK_MIN_TOKENS as xn, recallLifecycleSchema as xr, FIXED_MEMORY_CHUNKS_MEMORY_ID_IDX as xt, getDiscoveryPath as y, CHUNKING_VERSION as yn, experimentCandidatePoolOverride as yr, FIXED_MEMORY_CHUNKS_AU as yt, FIXED_EXTERNAL_VALUE_49 as z, FORGET_QUERY_MIN_SCORE as zn, FIXED_PROTOCOL_VALUE_63 as zt };
|