@snoai/memory 0.0.0-stage → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +176 -0
- package/README.md +7 -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-CSc21GtV.d.ts +110 -0
- package/dist/adapter-DMEmCTKo.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-BpOUWPJM.d.ts +327 -0
- package/dist/atomic-memory-extraction-sdNf1-B1.js +3605 -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-CHz5GOPA.d.ts +53 -0
- package/dist/capture-policy-detector-DaMfy9CZ.js +357 -0
- package/dist/classifier-C96uH9OF.d.ts +272 -0
- package/dist/client.d.ts +34 -0
- package/dist/client.js +305 -0
- package/dist/coding-skin.d.ts +51 -0
- package/dist/coding-skin.js +55 -0
- package/dist/config-tSrvLXgV.js +2577 -0
- package/dist/connection-DAbzki8K.d.ts +391 -0
- package/dist/connection-DliCOBB2.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-CYniFp8b.js +172 -0
- package/dist/data-bootstrap-CcJUwkqf.d.ts +30 -0
- package/dist/data-paths-C7sNdqvP.d.ts +22 -0
- package/dist/derived-suppression-cache-C7bGARc0.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-BsMap4It.d.ts +650 -0
- package/dist/index-DL8AVj0Z.d.ts +571 -0
- package/dist/index-DQ7VL6d3.d.ts +350 -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 +61 -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 +37 -0
- package/dist/internal/engine/bindings/sno-station-mem-runtime-mode.js +105 -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-B7Xoxqou.js +1378 -0
- package/dist/llm-client-DOTSGzhe.d.ts +51 -0
- package/dist/locales-JxGmkG6t.d.ts +7 -0
- package/dist/log-site-catalog.generated-B9WYdnAM.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-LKt0u3gy.d.ts +23 -0
- package/dist/memory-metadata-codec-C3oQd47V.d.ts +68 -0
- package/dist/memory-metadata-codec-DL1sbJiN.js +577 -0
- package/dist/memory-runtime-BZtREuwW.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-3e44ND2i.d.ts +16 -0
- package/dist/memory-source-order-BtrIxaIt.js +134 -0
- package/dist/memory-store-base-XRA4S9Xl.d.ts +1095 -0
- package/dist/memory-store-shared-DeIDkhLj.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-CEdRTN5J.d.ts +43 -0
- package/dist/memory-temporality-classifier-CKiTAHXe.js +184 -0
- package/dist/memory-tier-promoter-DbTpahkP.d.ts +43 -0
- package/dist/memory-tool-dependencies-CLODycUw.js +19 -0
- package/dist/memory-tool-dependencies-N69chrQc.d.ts +14 -0
- package/dist/model-call-table-CspopkBg.js +563 -0
- package/dist/model-reply-text-CKopStP2.js +334 -0
- package/dist/observable-embedder-DQnNgf9O.d.ts +17 -0
- package/dist/observable-llm-client-uwqPADpN.d.ts +21 -0
- package/dist/observable-memory-store-BaZKbhgq.d.ts +73 -0
- package/dist/observable-retriever-Dt_NUxAA.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-hIphqpUY.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-BeQiJI9-.js +644 -0
- package/dist/reflection-command-hooks-C1Vxgb-O.d.ts +32 -0
- package/dist/reflection-deps-RpLPFrSc.d.ts +19 -0
- package/dist/reflection-embedded-generator-CDUhgV96.js +130 -0
- package/dist/reflection-embedded-generator-CxOex5xK.d.ts +14 -0
- package/dist/reflection-injection-hooks-DfUgqu8f.d.ts +43 -0
- package/dist/reflection-lifecycle-hooks-Ct5HBdSG.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-Cbm5gfSe.js +31644 -0
- package/dist/rem-batch-executor-bc0IFlp2.js +2 -0
- package/dist/rem-consumer-retrieval-CRpvG8KS.js +246 -0
- package/dist/rem-trigger-BTxkcjgT.js +729 -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-CygHBazG.js +1757 -0
- package/dist/retriever-Ruk3zp7_.d.ts +469 -0
- package/dist/rolldown-runtime-D7D4PA-g.js +13 -0
- package/dist/routes-Ce7SQKec.js +59 -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-BPAY4NTG.js +1410 -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-Bu-MiqPX.d.ts +31 -0
- package/dist/sno-station-mem-runtime-dependencies-DjLOhB45.js +30 -0
- package/dist/sqlite-runtime-L_ywirTm.d.ts +58 -0
- package/dist/store-2pM0-dZg.d.ts +27 -0
- package/dist/store-DzpZBMEm.js +5830 -0
- package/dist/strategy-hook-runner-gBtU47tX.d.ts +16 -0
- package/dist/strategy-hook-runner-zmdIYOmQ.js +110 -0
- package/dist/token-counter-BD1f48Km.js +73 -0
- package/dist/trace-1t1n_wUm.d.ts +86 -0
- package/dist/types-DxKqU60b.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,1410 @@
|
|
|
1
|
+
import { $ as appendAuditEntryStrict, Ct as ModelCallRefusedError, H as parseRemOperationType, V as REM_BUILT_OPERATION_TYPES, at as readJsonlLines, tt as getAuditPath } from "./rem-batch-executor-Cbm5gfSe.js";
|
|
2
|
+
import { a as MEMORY_ROUTES, n as MEMORY_DEFAULT_SKIN_ID, o as MEMORY_SKIN_HEADER, r as MEMORY_ERROR_STATUS, s as memoryMethod } from "./routes-Ce7SQKec.js";
|
|
3
|
+
import { C as getSidecarSocketPath, _ as readRemOperationalConfig, b as getDiscoveryPath, c as REM_SIDECAR_ORIGIN, d as getRemChassisJournalPath, f as getRemDiscoveryPath, i as REM_JOBS_PATH_PREFIX, p as getRemJobJournalPath, r as REM_CORRELATION_ID_HEADER, rr as MEMORY_SHUTDOWN_TIMEOUT_MS, s as REM_SIDECAR_HOST, u as REM_SOURCE, v as readRemTestHoldMs, w as getSnoStationMemStateDir, x as getPrincipal, y as SettingsUnavailableError } from "./config-tSrvLXgV.js";
|
|
4
|
+
import { n as remNeedsHost, r as ContractError } from "./rem-trigger-BTxkcjgT.js";
|
|
5
|
+
import { setImmediate } from "node:timers/promises";
|
|
6
|
+
import { createLogger, currentLogContext, effectiveLogLevel, emitDiagnostic } from "@snoai/utils/logger";
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
import { existsSync, readFileSync, rmSync } from "node:fs";
|
|
9
|
+
import { z } from "zod";
|
|
10
|
+
import { randomBytes, randomUUID } from "node:crypto";
|
|
11
|
+
import { mkdir, open, readFile, rename, rm } from "node:fs/promises";
|
|
12
|
+
import { withLogContext as withLogContext$1 } from "@snoai/utils/log-context";
|
|
13
|
+
import { createServer } from "node:http";
|
|
14
|
+
import { createConnection, createServer as createServer$1 } from "node:net";
|
|
15
|
+
import locking from "fs-ext";
|
|
16
|
+
//#region src/contract/discovery.ts
|
|
17
|
+
const discoverySchema = z.strictObject({
|
|
18
|
+
pid: z.number().int().positive(),
|
|
19
|
+
port: z.number().int().min(1).max(65535),
|
|
20
|
+
token: z.string().regex(/^[a-f0-9]{64}$/)
|
|
21
|
+
});
|
|
22
|
+
async function readDiscovery() {
|
|
23
|
+
let text;
|
|
24
|
+
try {
|
|
25
|
+
text = await readFile(getDiscoveryPath(), "utf8");
|
|
26
|
+
} catch (error) {
|
|
27
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") return void 0;
|
|
28
|
+
throw new ContractError("sidecar-unreachable");
|
|
29
|
+
}
|
|
30
|
+
try {
|
|
31
|
+
return discoverySchema.parse(JSON.parse(text));
|
|
32
|
+
} catch {
|
|
33
|
+
throw new ContractError("sidecar-unreachable");
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
//#endregion
|
|
37
|
+
//#region src/sidecar/request-body.ts
|
|
38
|
+
var PayloadTooLargeError = class extends Error {
|
|
39
|
+
constructor() {
|
|
40
|
+
super("payload_too_large");
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
function readRequestBody(request, signal) {
|
|
44
|
+
return new Promise((resolve, reject) => {
|
|
45
|
+
const chunks = [];
|
|
46
|
+
let bytes = 0;
|
|
47
|
+
const cleanup = () => {
|
|
48
|
+
request.off("data", onData);
|
|
49
|
+
request.off("end", onEnd);
|
|
50
|
+
request.off("error", onError);
|
|
51
|
+
request.off("aborted", onAborted);
|
|
52
|
+
signal?.removeEventListener("abort", onAbort);
|
|
53
|
+
};
|
|
54
|
+
const onError = (error) => {
|
|
55
|
+
cleanup();
|
|
56
|
+
request.resume();
|
|
57
|
+
reject(error);
|
|
58
|
+
};
|
|
59
|
+
const onAbort = () => onError(signal?.reason);
|
|
60
|
+
const onAborted = () => onError(/* @__PURE__ */ new Error("request_aborted"));
|
|
61
|
+
const onEnd = () => {
|
|
62
|
+
cleanup();
|
|
63
|
+
resolve(Buffer.concat(chunks).toString("utf8"));
|
|
64
|
+
};
|
|
65
|
+
const onData = (chunk) => {
|
|
66
|
+
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
67
|
+
bytes += buffer.length;
|
|
68
|
+
if (bytes > 8388608) {
|
|
69
|
+
onError(new PayloadTooLargeError());
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
chunks.push(buffer);
|
|
73
|
+
};
|
|
74
|
+
request.on("data", onData);
|
|
75
|
+
request.once("end", onEnd);
|
|
76
|
+
request.once("error", onError);
|
|
77
|
+
request.once("aborted", onAborted);
|
|
78
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
79
|
+
if (signal?.aborted) onAbort();
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
//#endregion
|
|
83
|
+
//#region src/sidecar/memory-routes.ts
|
|
84
|
+
const log$2 = createLogger("sno-station-mem:memory-routes");
|
|
85
|
+
async function serveMemoryRoute(request, response, pathname, openPool, activeTasks) {
|
|
86
|
+
const method = memoryMethod(pathname);
|
|
87
|
+
if (!method || request.method !== "POST") return false;
|
|
88
|
+
const skinId = request.headers[MEMORY_SKIN_HEADER];
|
|
89
|
+
let timer;
|
|
90
|
+
const controller = new AbortController();
|
|
91
|
+
const abort = () => controller.abort(new ContractError("timeout"));
|
|
92
|
+
let rejectDeadline;
|
|
93
|
+
if (method !== "capture") response.once("close", abort);
|
|
94
|
+
try {
|
|
95
|
+
const deadline = new Promise((_, reject) => {
|
|
96
|
+
rejectDeadline = () => reject(controller.signal.reason);
|
|
97
|
+
controller.signal.addEventListener("abort", rejectDeadline, { once: true });
|
|
98
|
+
timer = setTimeout(abort, MEMORY_ROUTES[method].timeoutMs);
|
|
99
|
+
});
|
|
100
|
+
const task = (async () => {
|
|
101
|
+
const body = await readBody(request, controller.signal);
|
|
102
|
+
controller.signal.throwIfAborted();
|
|
103
|
+
const pool = await openPool();
|
|
104
|
+
controller.signal.throwIfAborted();
|
|
105
|
+
return pool.invoke(method, body, typeof skinId === "string" && skinId.trim() ? skinId : MEMORY_DEFAULT_SKIN_ID, controller.signal);
|
|
106
|
+
})();
|
|
107
|
+
const boundedTask = Promise.race([task, deadline]);
|
|
108
|
+
const completion = Object.assign(task.then(() => void 0, () => void 0), {
|
|
109
|
+
label: currentLogContext().operation_id ?? method,
|
|
110
|
+
method
|
|
111
|
+
});
|
|
112
|
+
activeTasks.add(completion);
|
|
113
|
+
completion.finally(() => activeTasks.delete(completion));
|
|
114
|
+
const result = await boundedTask;
|
|
115
|
+
response.writeHead(200, { "content-type": "application/json" });
|
|
116
|
+
response.end(JSON.stringify(result));
|
|
117
|
+
} catch (error) {
|
|
118
|
+
if (error instanceof SettingsUnavailableError) {
|
|
119
|
+
response.writeHead(503, { "content-type": "application/json" });
|
|
120
|
+
response.end(JSON.stringify({
|
|
121
|
+
degraded: true,
|
|
122
|
+
error: error.message,
|
|
123
|
+
reason: error.message
|
|
124
|
+
}));
|
|
125
|
+
return true;
|
|
126
|
+
}
|
|
127
|
+
log$2.error("Memory engine request failed", {
|
|
128
|
+
method,
|
|
129
|
+
skinId,
|
|
130
|
+
error,
|
|
131
|
+
error_message: error instanceof Error ? error.message : String(error),
|
|
132
|
+
error_stack: error instanceof Error ? error.stack : void 0
|
|
133
|
+
}, {
|
|
134
|
+
event_name: "memory.sidecar.request.failed",
|
|
135
|
+
file: "packages/memory/src/sidecar/memory-routes.ts",
|
|
136
|
+
function: "serveMemoryRoute",
|
|
137
|
+
site_id: "memory.sidecar.request.failed"
|
|
138
|
+
});
|
|
139
|
+
if (error instanceof PayloadTooLargeError) {
|
|
140
|
+
response.writeHead(413, { "content-type": "application/json" });
|
|
141
|
+
response.end(JSON.stringify({ error: "payload_too_large" }));
|
|
142
|
+
return true;
|
|
143
|
+
}
|
|
144
|
+
const reason = error instanceof ContractError ? error.reason : "engine-failed";
|
|
145
|
+
const status = MEMORY_ERROR_STATUS[reason];
|
|
146
|
+
if (!response.headersSent) response.writeHead(status, { "content-type": "application/json" });
|
|
147
|
+
response.end(JSON.stringify({
|
|
148
|
+
degraded: true,
|
|
149
|
+
reason
|
|
150
|
+
}));
|
|
151
|
+
} finally {
|
|
152
|
+
clearTimeout(timer);
|
|
153
|
+
response.off("close", abort);
|
|
154
|
+
if (rejectDeadline) controller.signal.removeEventListener("abort", rejectDeadline);
|
|
155
|
+
}
|
|
156
|
+
return true;
|
|
157
|
+
}
|
|
158
|
+
async function readBody(request, signal) {
|
|
159
|
+
const body = await readRequestBody(request, signal);
|
|
160
|
+
try {
|
|
161
|
+
return JSON.parse(body);
|
|
162
|
+
} catch {
|
|
163
|
+
throw new ContractError("invalid-input");
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
//#endregion
|
|
167
|
+
//#region src/sidecar/rem-chassis-journal.ts
|
|
168
|
+
/** @file rem-chassis-journal.ts
|
|
169
|
+
* @purpose Persists reason-bearing REM chassis stage outcomes as append-only JSONL.
|
|
170
|
+
* @boundary Sidecar runner journal only; job-state transitions remain in RemJobStore.
|
|
171
|
+
*/
|
|
172
|
+
var RemChassisJournal = class {
|
|
173
|
+
journalPath;
|
|
174
|
+
operationQueue = Promise.resolve();
|
|
175
|
+
constructor(journalPath) {
|
|
176
|
+
this.journalPath = journalPath;
|
|
177
|
+
}
|
|
178
|
+
appendJournal(jobId, jobType, entry) {
|
|
179
|
+
return this.queueAppend(jobId, jobType, entry);
|
|
180
|
+
}
|
|
181
|
+
appendJournalWithCorrelation(jobId, jobType, correlationId, entry) {
|
|
182
|
+
return this.queueAppend(jobId, jobType, entry, correlationId);
|
|
183
|
+
}
|
|
184
|
+
queueAppend(jobId, jobType, entry, correlationId) {
|
|
185
|
+
const result = this.operationQueue.then(() => this.append(jobId, jobType, entry, correlationId));
|
|
186
|
+
this.operationQueue = result.then(() => void 0, () => void 0);
|
|
187
|
+
return result;
|
|
188
|
+
}
|
|
189
|
+
async append(jobId, jobType, entry, correlationId) {
|
|
190
|
+
const parent = path.dirname(this.journalPath);
|
|
191
|
+
await mkdir(parent, { recursive: true });
|
|
192
|
+
const journalAlreadyExisted = existsSync(this.journalPath);
|
|
193
|
+
const handle = await open(this.journalPath, "a");
|
|
194
|
+
try {
|
|
195
|
+
const record = {
|
|
196
|
+
job_id: jobId,
|
|
197
|
+
job_type: jobType,
|
|
198
|
+
...correlationId === void 0 ? {} : { correlation_id: correlationId },
|
|
199
|
+
stage: entry.stage,
|
|
200
|
+
outcome: entry.outcome,
|
|
201
|
+
pairs_scanned: entry.pairsScanned,
|
|
202
|
+
verdicts: entry.verdicts,
|
|
203
|
+
actions_applied: entry.actionsApplied,
|
|
204
|
+
...entry.reason === void 0 ? {} : { reason: entry.reason },
|
|
205
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
206
|
+
};
|
|
207
|
+
await handle.writeFile(`${JSON.stringify(record)}\n`, "utf8");
|
|
208
|
+
await handle.sync();
|
|
209
|
+
} finally {
|
|
210
|
+
await handle.close();
|
|
211
|
+
}
|
|
212
|
+
if (!journalAlreadyExisted) await syncDirectory$2(parent);
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
async function syncDirectory$2(directory) {
|
|
216
|
+
if (process.platform === "win32") return;
|
|
217
|
+
const handle = await open(directory, "r");
|
|
218
|
+
try {
|
|
219
|
+
await handle.sync();
|
|
220
|
+
} finally {
|
|
221
|
+
await handle.close();
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
//#endregion
|
|
225
|
+
//#region src/sidecar/rem-job-store.ts
|
|
226
|
+
/** @file rem-job-store.ts
|
|
227
|
+
* @purpose Persists append-only REM job state transitions.
|
|
228
|
+
* @boundary Durable local JSONL journal used by the standalone REM sidecar.
|
|
229
|
+
*/
|
|
230
|
+
const log$1 = createLogger("sno-station-mem:rem-jobs");
|
|
231
|
+
const remJobStateSchema = z.enum([
|
|
232
|
+
"queued",
|
|
233
|
+
"running",
|
|
234
|
+
"done",
|
|
235
|
+
"failed",
|
|
236
|
+
"skipped"
|
|
237
|
+
]);
|
|
238
|
+
const remJobMeasurementsSchema = z.object({
|
|
239
|
+
rows_considered: z.number().int().nonnegative(),
|
|
240
|
+
pairs_built: z.number().int().nonnegative().optional(),
|
|
241
|
+
pair_cap_binding: z.boolean().optional(),
|
|
242
|
+
model_calls: z.number().int().nonnegative(),
|
|
243
|
+
model_tokens: z.number().int().nonnegative(),
|
|
244
|
+
wall_ms: z.number().int().nonnegative()
|
|
245
|
+
}).strict();
|
|
246
|
+
const remJobStatsSchema = z.object({
|
|
247
|
+
operations: z.number().int().nonnegative(),
|
|
248
|
+
applied_count: z.number().int().nonnegative().optional(),
|
|
249
|
+
actionable_candidate_count: z.number().int().nonnegative().optional(),
|
|
250
|
+
applied_fraction: z.number().min(0).max(1).nullable().optional(),
|
|
251
|
+
scan: z.object({
|
|
252
|
+
scope: z.string().min(1),
|
|
253
|
+
candidate_count: z.number().int().nonnegative(),
|
|
254
|
+
stamped_skipped_count: z.number().int().nonnegative().optional(),
|
|
255
|
+
actionable_candidate_count: z.number().int().nonnegative().optional()
|
|
256
|
+
}).strict().optional(),
|
|
257
|
+
parse_failure_count: z.number().int().nonnegative().optional(),
|
|
258
|
+
top_refusal_reasons: z.array(z.string().min(1)).max(2).optional(),
|
|
259
|
+
measured: remJobMeasurementsSchema.optional(),
|
|
260
|
+
by_operation: z.array(z.object({
|
|
261
|
+
operation: z.string().min(1),
|
|
262
|
+
applied_count: z.number().int().nonnegative(),
|
|
263
|
+
actionable_candidate_count: z.number().int().nonnegative(),
|
|
264
|
+
candidate_count: z.number().int().nonnegative(),
|
|
265
|
+
parse_failure_count: z.number().int().nonnegative(),
|
|
266
|
+
top_refusal_reasons: z.array(z.string().min(1)).max(2),
|
|
267
|
+
measured: remJobMeasurementsSchema
|
|
268
|
+
}).strict()).min(1).optional()
|
|
269
|
+
}).strict();
|
|
270
|
+
function parseRemJobStats(value) {
|
|
271
|
+
const parsed = remJobStatsSchema.safeParse(value);
|
|
272
|
+
return parsed.success ? parsed.data : void 0;
|
|
273
|
+
}
|
|
274
|
+
const remWaveJobSchema = z.object({
|
|
275
|
+
payloadVersion: z.literal(1),
|
|
276
|
+
waveId: z.string().min(1),
|
|
277
|
+
correlationId: z.string().min(1),
|
|
278
|
+
scope: z.string().min(1),
|
|
279
|
+
requestedOperations: z.array(z.string().min(1)).min(1),
|
|
280
|
+
state: remJobStateSchema,
|
|
281
|
+
startedAt: z.string().datetime().nullable(),
|
|
282
|
+
finishedAt: z.string().datetime().nullable(),
|
|
283
|
+
stats: remJobStatsSchema,
|
|
284
|
+
error: z.string().min(1).optional()
|
|
285
|
+
}).strict();
|
|
286
|
+
const legacyJobSchema = z.object({
|
|
287
|
+
job_id: z.string().min(1),
|
|
288
|
+
state: remJobStateSchema,
|
|
289
|
+
type: z.string().min(1),
|
|
290
|
+
scope: z.string().min(1),
|
|
291
|
+
started_at: z.string().datetime().nullable(),
|
|
292
|
+
finished_at: z.string().datetime().nullable(),
|
|
293
|
+
stats: remJobStatsSchema,
|
|
294
|
+
error: z.string().min(1).optional(),
|
|
295
|
+
correlation_id: z.string().min(1)
|
|
296
|
+
}).strict();
|
|
297
|
+
const OPERATION_ORDER = [
|
|
298
|
+
"rem-replace",
|
|
299
|
+
"rem-update",
|
|
300
|
+
"rem-distill",
|
|
301
|
+
"rem-retire"
|
|
302
|
+
];
|
|
303
|
+
var RemJobStore = class RemJobStore {
|
|
304
|
+
journalPath;
|
|
305
|
+
onDurableTransition;
|
|
306
|
+
jobs = /* @__PURE__ */ new Map();
|
|
307
|
+
persistedJobs = /* @__PURE__ */ new Set();
|
|
308
|
+
mergeKeys = /* @__PURE__ */ new Map();
|
|
309
|
+
operationQueue = Promise.resolve();
|
|
310
|
+
constructor(journalPath, onDurableTransition) {
|
|
311
|
+
this.journalPath = journalPath;
|
|
312
|
+
this.onDurableTransition = onDurableTransition;
|
|
313
|
+
}
|
|
314
|
+
static async open(journalPath, onDurableTransition = () => void 0) {
|
|
315
|
+
const store = new RemJobStore(journalPath, onDurableTransition);
|
|
316
|
+
await store.migrateLegacyJournal().catch((error) => store.reportFailure(error));
|
|
317
|
+
await store.load();
|
|
318
|
+
return store;
|
|
319
|
+
}
|
|
320
|
+
isPersisted(jobId) {
|
|
321
|
+
return this.persistedJobs.has(jobId);
|
|
322
|
+
}
|
|
323
|
+
get(jobId) {
|
|
324
|
+
const job = this.jobs.get(jobId);
|
|
325
|
+
return job === void 0 ? void 0 : toRemJob(job);
|
|
326
|
+
}
|
|
327
|
+
nonTerminalJobs() {
|
|
328
|
+
return Array.from(this.jobs.values()).filter((job) => job.state === "queued" || job.state === "running").map(toRemJob);
|
|
329
|
+
}
|
|
330
|
+
async createQueued(type, scope, correlationId) {
|
|
331
|
+
return this.enqueue(async () => {
|
|
332
|
+
const requestedOperations = canonicalOperations(typeof type === "string" ? [type] : type);
|
|
333
|
+
if (requestedOperations.length === 0) throw new Error("REM wave requires at least one supported operation");
|
|
334
|
+
const mergeKey = `${correlationId}\0${scope}`;
|
|
335
|
+
const existingId = this.mergeKeys.get(mergeKey);
|
|
336
|
+
const existing = existingId === void 0 ? void 0 : this.jobs.get(existingId);
|
|
337
|
+
if (existing !== void 0 && existing.state === "queued") {
|
|
338
|
+
const mergedOperations = canonicalOperations([...existing.requestedOperations, ...requestedOperations]);
|
|
339
|
+
if (mergedOperations.length === existing.requestedOperations.length) return {
|
|
340
|
+
created: false,
|
|
341
|
+
job: toRemJob(existing)
|
|
342
|
+
};
|
|
343
|
+
const updated = remWaveJobSchema.parse({
|
|
344
|
+
...existing,
|
|
345
|
+
requestedOperations: mergedOperations
|
|
346
|
+
});
|
|
347
|
+
const durable = await this.persist(updated);
|
|
348
|
+
this.jobs.set(updated.waveId, updated);
|
|
349
|
+
if (durable) this.onDurableTransition(toRemJob(updated));
|
|
350
|
+
return {
|
|
351
|
+
created: false,
|
|
352
|
+
job: toRemJob(updated)
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
if (existing !== void 0 && existing.state !== "skipped" && requestedOperations.every((operation) => existing.requestedOperations.includes(operation))) return {
|
|
356
|
+
created: false,
|
|
357
|
+
job: toRemJob(existing)
|
|
358
|
+
};
|
|
359
|
+
const job = remWaveJobSchema.parse({
|
|
360
|
+
payloadVersion: 1,
|
|
361
|
+
waveId: `rem-wave-${randomUUID()}`,
|
|
362
|
+
correlationId,
|
|
363
|
+
scope,
|
|
364
|
+
requestedOperations,
|
|
365
|
+
state: "queued",
|
|
366
|
+
startedAt: null,
|
|
367
|
+
finishedAt: null,
|
|
368
|
+
stats: { operations: 0 }
|
|
369
|
+
});
|
|
370
|
+
const durable = await this.persist(job);
|
|
371
|
+
this.jobs.set(job.waveId, job);
|
|
372
|
+
this.mergeKeys.set(mergeKey, job.waveId);
|
|
373
|
+
const outward = toRemJob(job);
|
|
374
|
+
if (durable) this.onDurableTransition(outward);
|
|
375
|
+
return {
|
|
376
|
+
created: true,
|
|
377
|
+
job: outward
|
|
378
|
+
};
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
async transition(jobId, transition) {
|
|
382
|
+
return this.enqueue(async () => {
|
|
383
|
+
const current = this.jobs.get(jobId);
|
|
384
|
+
if (!current) throw new Error(`REM job not found: ${jobId}`);
|
|
385
|
+
const next = remWaveJobSchema.parse({
|
|
386
|
+
...current,
|
|
387
|
+
state: transition.state,
|
|
388
|
+
...transition.started_at === void 0 ? {} : { startedAt: transition.started_at },
|
|
389
|
+
...transition.finished_at === void 0 ? {} : { finishedAt: transition.finished_at },
|
|
390
|
+
...transition.stats === void 0 ? {} : { stats: transition.stats },
|
|
391
|
+
...transition.error === void 0 ? {} : { error: transition.error }
|
|
392
|
+
});
|
|
393
|
+
const durable = await this.persist(next);
|
|
394
|
+
this.jobs.set(jobId, next);
|
|
395
|
+
const outward = toRemJob(next);
|
|
396
|
+
if (durable) this.onDurableTransition(outward);
|
|
397
|
+
return outward;
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
async load() {
|
|
401
|
+
if (!existsSync(this.journalPath)) return;
|
|
402
|
+
const bytes = await readFile(this.journalPath);
|
|
403
|
+
const lines = bytes.toString("utf8").split("\n");
|
|
404
|
+
const endsWithNewline = bytes.length === 0 || bytes[bytes.length - 1] === 10;
|
|
405
|
+
for (const [index, line] of lines.entries()) {
|
|
406
|
+
if (!line) continue;
|
|
407
|
+
if (!endsWithNewline && index === lines.length - 1) {
|
|
408
|
+
const lastNewline = bytes.lastIndexOf(10);
|
|
409
|
+
await this.truncateJournal(lastNewline < 0 ? 0 : lastNewline + 1);
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
try {
|
|
413
|
+
const parsed = JSON.parse(line);
|
|
414
|
+
const job = remWaveJobSchema.parse(parsed);
|
|
415
|
+
this.jobs.set(job.waveId, job);
|
|
416
|
+
this.mergeKeys.set(`${job.correlationId}\0${job.scope}`, job.waveId);
|
|
417
|
+
} catch (error) {
|
|
418
|
+
log$1.error("rem.journal.failed", {
|
|
419
|
+
path: this.journalPath,
|
|
420
|
+
line: index + 1,
|
|
421
|
+
error
|
|
422
|
+
}, {
|
|
423
|
+
event_name: "rem.journal.failed",
|
|
424
|
+
file: "packages/memory/src/sidecar/rem-job-store.ts",
|
|
425
|
+
function: "load",
|
|
426
|
+
site_id: "rem-job-store.load.invalid-line"
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
async migrateLegacyJournal() {
|
|
432
|
+
const legacyPath = path.join(path.dirname(this.journalPath), "rem-jobs.jsonl");
|
|
433
|
+
if (!existsSync(legacyPath)) return;
|
|
434
|
+
const latest = /* @__PURE__ */ new Map();
|
|
435
|
+
for await (const line of readJsonlLines(legacyPath)) try {
|
|
436
|
+
const parsed = JSON.parse(line);
|
|
437
|
+
const job = legacyJobSchema.parse(parsed);
|
|
438
|
+
latest.set(job.job_id, job);
|
|
439
|
+
} catch (error) {
|
|
440
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
441
|
+
this.reportFailure(reason);
|
|
442
|
+
}
|
|
443
|
+
const interrupted = [...latest.values()].filter((job) => job.state === "queued" || job.state === "running");
|
|
444
|
+
if (interrupted.length > 0) {
|
|
445
|
+
const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
446
|
+
const handle = await open(legacyPath, "a");
|
|
447
|
+
try {
|
|
448
|
+
for (const job of interrupted) await handle.writeFile(`${JSON.stringify({
|
|
449
|
+
...job,
|
|
450
|
+
state: "failed",
|
|
451
|
+
finished_at: finishedAt,
|
|
452
|
+
error: "sidecar_upgrade_interrupted"
|
|
453
|
+
})}\n`, "utf8");
|
|
454
|
+
await handle.sync();
|
|
455
|
+
} finally {
|
|
456
|
+
await handle.close();
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
await rename(legacyPath, path.join(path.dirname(legacyPath), "rem-jobs.v0.jsonl"));
|
|
460
|
+
}
|
|
461
|
+
reportFailure(error) {
|
|
462
|
+
log$1.error("rem.journal.failed", {
|
|
463
|
+
error,
|
|
464
|
+
path: this.journalPath
|
|
465
|
+
}, {
|
|
466
|
+
event_name: "rem.journal.failed",
|
|
467
|
+
file: "packages/memory/src/sidecar/rem-job-store.ts",
|
|
468
|
+
function: "reportFailure",
|
|
469
|
+
site_id: "rem.journal.failed"
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
async persist(job) {
|
|
473
|
+
this.persistedJobs.delete(job.waveId);
|
|
474
|
+
await this.append(job);
|
|
475
|
+
this.persistedJobs.add(job.waveId);
|
|
476
|
+
return true;
|
|
477
|
+
}
|
|
478
|
+
async truncateJournal(length) {
|
|
479
|
+
const handle = await open(this.journalPath, "r+");
|
|
480
|
+
try {
|
|
481
|
+
await handle.truncate(length);
|
|
482
|
+
await handle.sync();
|
|
483
|
+
} finally {
|
|
484
|
+
await handle.close();
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
async append(job) {
|
|
488
|
+
const parent = path.dirname(this.journalPath);
|
|
489
|
+
await mkdir(parent, { recursive: true });
|
|
490
|
+
const journalAlreadyExisted = existsSync(this.journalPath);
|
|
491
|
+
const handle = await open(this.journalPath, "a");
|
|
492
|
+
try {
|
|
493
|
+
await handle.writeFile(`${JSON.stringify(job)}\n`, "utf8");
|
|
494
|
+
await handle.sync();
|
|
495
|
+
} finally {
|
|
496
|
+
await handle.close();
|
|
497
|
+
}
|
|
498
|
+
if (!journalAlreadyExisted) await syncDirectory$1(parent);
|
|
499
|
+
}
|
|
500
|
+
enqueue(operation) {
|
|
501
|
+
const result = this.operationQueue.then(operation);
|
|
502
|
+
this.operationQueue = result.then(() => void 0, () => void 0);
|
|
503
|
+
return result;
|
|
504
|
+
}
|
|
505
|
+
};
|
|
506
|
+
function canonicalOperations(operations) {
|
|
507
|
+
const unique = new Set(operations);
|
|
508
|
+
return OPERATION_ORDER.filter((operation) => unique.has(operation));
|
|
509
|
+
}
|
|
510
|
+
function toRemJob(job) {
|
|
511
|
+
return {
|
|
512
|
+
job_id: job.waveId,
|
|
513
|
+
state: job.state,
|
|
514
|
+
type: job.requestedOperations[0] ?? "rem-replace",
|
|
515
|
+
scope: job.scope,
|
|
516
|
+
started_at: job.startedAt,
|
|
517
|
+
finished_at: job.finishedAt,
|
|
518
|
+
stats: job.stats,
|
|
519
|
+
...job.error === void 0 ? {} : { error: job.error },
|
|
520
|
+
correlation_id: job.correlationId,
|
|
521
|
+
requested_operations: [...job.requestedOperations]
|
|
522
|
+
};
|
|
523
|
+
}
|
|
524
|
+
async function syncDirectory$1(directory) {
|
|
525
|
+
if (process.platform === "win32") return;
|
|
526
|
+
const handle = await open(directory, "r");
|
|
527
|
+
try {
|
|
528
|
+
await handle.sync();
|
|
529
|
+
} finally {
|
|
530
|
+
await handle.close();
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
//#endregion
|
|
534
|
+
//#region src/sidecar/server.ts
|
|
535
|
+
/** @file server.ts
|
|
536
|
+
* @purpose Runs the loopback HTTP surface and empty asynchronous REM executor.
|
|
537
|
+
* @boundary Sno CLI requests, durable REM job state, and the existing local audit writer.
|
|
538
|
+
*/
|
|
539
|
+
const log = createLogger("sno-station-mem:rem-sidecar");
|
|
540
|
+
const runRequestSchema = z.union([z.object({
|
|
541
|
+
type: z.string().min(1),
|
|
542
|
+
scope: z.string().trim().min(1)
|
|
543
|
+
}).strict(), z.object({
|
|
544
|
+
types: z.array(z.string().min(1)).min(1),
|
|
545
|
+
scope: z.string().trim().min(1)
|
|
546
|
+
}).strict()]);
|
|
547
|
+
var HttpError = class extends Error {
|
|
548
|
+
status;
|
|
549
|
+
code;
|
|
550
|
+
constructor(status, code) {
|
|
551
|
+
super(code);
|
|
552
|
+
this.status = status;
|
|
553
|
+
this.code = code;
|
|
554
|
+
}
|
|
555
|
+
};
|
|
556
|
+
var DuplicateSidecarError = class extends Error {};
|
|
557
|
+
async function socketIsLive(socketPath) {
|
|
558
|
+
return new Promise((resolve, reject) => {
|
|
559
|
+
const socket = createConnection(socketPath);
|
|
560
|
+
socket.once("connect", () => {
|
|
561
|
+
socket.destroy();
|
|
562
|
+
resolve(true);
|
|
563
|
+
});
|
|
564
|
+
socket.once("error", (error) => {
|
|
565
|
+
socket.destroy();
|
|
566
|
+
if ("code" in error && (error.code === "ECONNREFUSED" || error.code === "ENOENT")) resolve(false);
|
|
567
|
+
else reject(error);
|
|
568
|
+
});
|
|
569
|
+
});
|
|
570
|
+
}
|
|
571
|
+
async function bindSidecarSocket() {
|
|
572
|
+
const socketPath = getSidecarSocketPath();
|
|
573
|
+
await mkdir(path.dirname(socketPath), {
|
|
574
|
+
recursive: true,
|
|
575
|
+
mode: 448
|
|
576
|
+
});
|
|
577
|
+
const directory = await open(path.dirname(socketPath), "r");
|
|
578
|
+
try {
|
|
579
|
+
for (;;) try {
|
|
580
|
+
locking.flockSync(directory.fd, "exnb");
|
|
581
|
+
break;
|
|
582
|
+
} catch (error) {
|
|
583
|
+
if (!(error instanceof Error && "code" in error && error.code === "EAGAIN")) throw error;
|
|
584
|
+
await setImmediate();
|
|
585
|
+
}
|
|
586
|
+
const guard = createServer$1((socket) => socket.destroy());
|
|
587
|
+
for (;;) try {
|
|
588
|
+
await new Promise((resolve, reject) => {
|
|
589
|
+
guard.once("error", reject);
|
|
590
|
+
guard.listen(socketPath, () => {
|
|
591
|
+
guard.removeListener("error", reject);
|
|
592
|
+
resolve();
|
|
593
|
+
});
|
|
594
|
+
});
|
|
595
|
+
return guard;
|
|
596
|
+
} catch (error) {
|
|
597
|
+
if (!(error instanceof Error && "code" in error && error.code === "EADDRINUSE")) throw error;
|
|
598
|
+
if (await socketIsLive(socketPath)) {
|
|
599
|
+
const discovery = await readDiscovery().catch(() => void 0);
|
|
600
|
+
emitDiagnostic("info", "sidecar.duplicate.exit", { pid: discovery?.pid }, {
|
|
601
|
+
event_name: "sidecar.duplicate.exit",
|
|
602
|
+
file: "packages/memory/src/sidecar/server.ts",
|
|
603
|
+
function: "bindSidecarSocket",
|
|
604
|
+
site_id: "sidecar.duplicate.exit"
|
|
605
|
+
});
|
|
606
|
+
throw new DuplicateSidecarError();
|
|
607
|
+
}
|
|
608
|
+
await rm(socketPath, { force: true });
|
|
609
|
+
}
|
|
610
|
+
} finally {
|
|
611
|
+
await directory.close();
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
async function startRemSidecar() {
|
|
615
|
+
const guard = await bindSidecarSocket();
|
|
616
|
+
const exitCleanup = () => {
|
|
617
|
+
guard.close();
|
|
618
|
+
};
|
|
619
|
+
process.once("exit", exitCleanup);
|
|
620
|
+
const releaseGuard = async () => {
|
|
621
|
+
await new Promise((resolve) => guard.close(() => resolve()));
|
|
622
|
+
process.removeListener("exit", exitCleanup);
|
|
623
|
+
};
|
|
624
|
+
try {
|
|
625
|
+
const sidecar = await startOwnedRemSidecar();
|
|
626
|
+
return {
|
|
627
|
+
port: sidecar.port,
|
|
628
|
+
async stop() {
|
|
629
|
+
let drained;
|
|
630
|
+
try {
|
|
631
|
+
({drained} = await sidecar.stop());
|
|
632
|
+
} finally {
|
|
633
|
+
if (drained) drained.then(releaseGuard);
|
|
634
|
+
else await releaseGuard();
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
};
|
|
638
|
+
} catch (error) {
|
|
639
|
+
await releaseGuard();
|
|
640
|
+
throw error;
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
async function startOwnedRemSidecar() {
|
|
644
|
+
let stopping = false;
|
|
645
|
+
let currentMemory;
|
|
646
|
+
let openingMemory;
|
|
647
|
+
let settingsError;
|
|
648
|
+
const memory = {
|
|
649
|
+
current: () => currentMemory,
|
|
650
|
+
async open() {
|
|
651
|
+
if (currentMemory) return currentMemory;
|
|
652
|
+
if (settingsError) throw settingsError;
|
|
653
|
+
openingMemory ??= import("./memory-runtime-BZtREuwW.js").then((module) => module.MemoryRuntimePool.open()).then((pool) => {
|
|
654
|
+
currentMemory = pool;
|
|
655
|
+
if (stopping) pool.stopTimers();
|
|
656
|
+
return pool;
|
|
657
|
+
}).catch((error) => {
|
|
658
|
+
if (error instanceof SettingsUnavailableError) settingsError = error;
|
|
659
|
+
throw error;
|
|
660
|
+
}).finally(() => {
|
|
661
|
+
openingMemory = void 0;
|
|
662
|
+
});
|
|
663
|
+
return openingMemory;
|
|
664
|
+
}
|
|
665
|
+
};
|
|
666
|
+
const token = randomBytes(32).toString("hex");
|
|
667
|
+
const holdMs = readRemTestHoldMs();
|
|
668
|
+
const chassisJournal = new RemChassisJournal(getRemChassisJournalPath());
|
|
669
|
+
const onDurableTransition = (job) => {
|
|
670
|
+
log.info("job_transition_durable", {
|
|
671
|
+
event: "job_transition_durable",
|
|
672
|
+
job_id: job.job_id,
|
|
673
|
+
state: job.state,
|
|
674
|
+
correlation_id: job.correlation_id,
|
|
675
|
+
durable: true
|
|
676
|
+
}, {
|
|
677
|
+
event_name: "sno_station_mem.server.job.transition.durable",
|
|
678
|
+
file: "packages/memory/src/sidecar/server.ts",
|
|
679
|
+
function: "<anonymous callback>",
|
|
680
|
+
site_id: "server.<anonymous callback>.5eb5a60e6b"
|
|
681
|
+
});
|
|
682
|
+
};
|
|
683
|
+
const store = RemJobStore.open(getRemJobJournalPath(), onDurableTransition);
|
|
684
|
+
const pendingTimers = /* @__PURE__ */ new Map();
|
|
685
|
+
const startPendingStarts = () => {
|
|
686
|
+
for (const start of pendingTimers.values()) start();
|
|
687
|
+
};
|
|
688
|
+
const activeTasks = /* @__PURE__ */ new Set();
|
|
689
|
+
const pendingRequests = /* @__PURE__ */ new Set();
|
|
690
|
+
const server = createServer((request, response) => {
|
|
691
|
+
const started = performance.now();
|
|
692
|
+
const operationId = `rem-http-${randomUUID()}`;
|
|
693
|
+
const context = {};
|
|
694
|
+
const requestLabel = `${operationId} ${request.method} ${requestPath(request)}`;
|
|
695
|
+
pendingRequests.add(requestLabel);
|
|
696
|
+
let requestLogged = false;
|
|
697
|
+
const recordRequest = (cancelled = false) => {
|
|
698
|
+
if (requestLogged) return;
|
|
699
|
+
requestLogged = true;
|
|
700
|
+
pendingRequests.delete(requestLabel);
|
|
701
|
+
let outcome = "success";
|
|
702
|
+
if (cancelled) outcome = "cancelled";
|
|
703
|
+
else if (response.statusCode >= 500) outcome = "failed";
|
|
704
|
+
else if (response.statusCode >= 400) outcome = "refused";
|
|
705
|
+
withLogContext$1({
|
|
706
|
+
operation_id: operationId,
|
|
707
|
+
job_id: context.job_id,
|
|
708
|
+
external_reference: context.correlation_id
|
|
709
|
+
}, () => {
|
|
710
|
+
log[outcome === "failed" ? "error" : "info"]("http_request", {
|
|
711
|
+
outcome,
|
|
712
|
+
method: request.method ?? "UNKNOWN",
|
|
713
|
+
path: requestPath(request),
|
|
714
|
+
status: response.statusCode,
|
|
715
|
+
duration_ms: Math.max(0, Math.round(performance.now() - started)),
|
|
716
|
+
...context
|
|
717
|
+
}, {
|
|
718
|
+
event_name: "sno_station_mem.server.http.request",
|
|
719
|
+
file: "packages/memory/src/sidecar/server.ts",
|
|
720
|
+
function: "recordRequest",
|
|
721
|
+
site_id: "server.<anonymous callback>.47c4662916"
|
|
722
|
+
});
|
|
723
|
+
});
|
|
724
|
+
};
|
|
725
|
+
response.once("finish", () => recordRequest());
|
|
726
|
+
response.once("close", () => recordRequest(!response.writableFinished));
|
|
727
|
+
withLogContext$1({ operation_id: operationId }, () => routeRequest(request, response, store, chassisJournal, holdMs, pendingTimers, activeTasks, context, memory, () => stopping).catch((error) => {
|
|
728
|
+
if (error instanceof SettingsUnavailableError) {
|
|
729
|
+
context.error_code = error.message;
|
|
730
|
+
if (!response.headersSent) sendJson(response, 503, { error: error.message });
|
|
731
|
+
else response.end();
|
|
732
|
+
return;
|
|
733
|
+
}
|
|
734
|
+
let httpError = new HttpError(500, "internal_error");
|
|
735
|
+
if (error instanceof HttpError) httpError = error;
|
|
736
|
+
else if (error instanceof PayloadTooLargeError) httpError = new HttpError(413, "payload_too_large");
|
|
737
|
+
context.error_code = httpError.code;
|
|
738
|
+
log.error("request_failed", { error }, {
|
|
739
|
+
event_name: "sno_station_mem.server.request.failed",
|
|
740
|
+
file: "packages/memory/src/sidecar/server.ts",
|
|
741
|
+
function: "<anonymous callback>",
|
|
742
|
+
site_id: "server.<anonymous callback>.a7a2656a08"
|
|
743
|
+
});
|
|
744
|
+
if (!response.headersSent) sendJson(response, httpError.status, { error: httpError.code });
|
|
745
|
+
else response.end();
|
|
746
|
+
}));
|
|
747
|
+
});
|
|
748
|
+
const jobs = await store;
|
|
749
|
+
const recoveryJobs = jobs.nonTerminalJobs();
|
|
750
|
+
await listen(server);
|
|
751
|
+
const address = server.address();
|
|
752
|
+
if (!address || typeof address === "string") {
|
|
753
|
+
server.close();
|
|
754
|
+
throw new Error("REM sidecar did not bind a TCP address");
|
|
755
|
+
}
|
|
756
|
+
const discoveryPath = getRemDiscoveryPath();
|
|
757
|
+
const discovery = {
|
|
758
|
+
port: address.port,
|
|
759
|
+
token,
|
|
760
|
+
pid: process.pid
|
|
761
|
+
};
|
|
762
|
+
let discoveryTimer;
|
|
763
|
+
let discoveryAttempt = 0;
|
|
764
|
+
const publishDiscovery = async () => {
|
|
765
|
+
discoveryAttempt++;
|
|
766
|
+
try {
|
|
767
|
+
await writeDiscovery(discoveryPath, discovery);
|
|
768
|
+
if (discoveryAttempt > 1) log.info("sidecar.discovery.published", { attempt: discoveryAttempt }, {
|
|
769
|
+
event_name: "sidecar.discovery.published",
|
|
770
|
+
file: "packages/memory/src/sidecar/server.ts",
|
|
771
|
+
function: "publishDiscovery",
|
|
772
|
+
site_id: "sidecar.discovery.published"
|
|
773
|
+
});
|
|
774
|
+
} catch (error) {
|
|
775
|
+
log.error("sidecar.discovery.failed", {
|
|
776
|
+
attempt: discoveryAttempt,
|
|
777
|
+
error
|
|
778
|
+
}, {
|
|
779
|
+
event_name: "sidecar.discovery.failed",
|
|
780
|
+
file: "packages/memory/src/sidecar/server.ts",
|
|
781
|
+
function: "publishDiscovery",
|
|
782
|
+
site_id: "sidecar.discovery.failed"
|
|
783
|
+
});
|
|
784
|
+
if (!stopping) discoveryTimer = setTimeout(() => {
|
|
785
|
+
discoveryPublication = publishDiscovery();
|
|
786
|
+
}, 5e3);
|
|
787
|
+
}
|
|
788
|
+
};
|
|
789
|
+
let discoveryPublication = publishDiscovery();
|
|
790
|
+
await discoveryPublication;
|
|
791
|
+
memory.open().catch((error) => {
|
|
792
|
+
if (!(error instanceof SettingsUnavailableError)) reportSidecarFailure("memory startup", error);
|
|
793
|
+
});
|
|
794
|
+
const recovery = (async () => {
|
|
795
|
+
const jobIds = await recoverInterruptedJobs(jobs, await readCompletedJobStats(new Set(recoveryJobs.map((job) => job.job_id))), recoveryJobs);
|
|
796
|
+
for (const jobId of jobIds) await runChassisJob(jobs, chassisJournal, jobId, 0, await memory.open());
|
|
797
|
+
})().catch((error) => reportSidecarFailure("recovery", error));
|
|
798
|
+
activeTasks.add(Object.assign(recovery, { label: "rem-recovery" }));
|
|
799
|
+
recovery.finally(() => activeTasks.delete(recovery));
|
|
800
|
+
const exitCleanup = () => {
|
|
801
|
+
removeOwnedDiscoverySync(discoveryPath, token);
|
|
802
|
+
};
|
|
803
|
+
process.once("exit", exitCleanup);
|
|
804
|
+
log.info("started", {
|
|
805
|
+
host: REM_SIDECAR_HOST,
|
|
806
|
+
port: address.port,
|
|
807
|
+
pid: process.pid
|
|
808
|
+
}, {
|
|
809
|
+
event_name: "sno_station_mem.server.started",
|
|
810
|
+
file: "packages/memory/src/sidecar/server.ts",
|
|
811
|
+
function: "startLockedRemSidecar",
|
|
812
|
+
site_id: "server.startLockedRemSidecar.96a92b085b"
|
|
813
|
+
});
|
|
814
|
+
return {
|
|
815
|
+
port: address.port,
|
|
816
|
+
async stop() {
|
|
817
|
+
const started = performance.now();
|
|
818
|
+
let cleanedUp = false;
|
|
819
|
+
let phase = "http_requests";
|
|
820
|
+
let timer;
|
|
821
|
+
stopping = true;
|
|
822
|
+
clearTimeout(discoveryTimer);
|
|
823
|
+
process.removeListener("exit", exitCleanup);
|
|
824
|
+
try {
|
|
825
|
+
startPendingStarts();
|
|
826
|
+
currentMemory?.stopTimers();
|
|
827
|
+
const cleanup = (async () => {
|
|
828
|
+
await closeServer(server);
|
|
829
|
+
startPendingStarts();
|
|
830
|
+
phase = "active_tasks";
|
|
831
|
+
await Promise.allSettled(activeTasks);
|
|
832
|
+
phase = "runtime_open";
|
|
833
|
+
await openingMemory?.catch(() => void 0);
|
|
834
|
+
phase = "runtime_close";
|
|
835
|
+
await currentMemory?.close();
|
|
836
|
+
phase = "discovery";
|
|
837
|
+
await discoveryPublication;
|
|
838
|
+
return true;
|
|
839
|
+
})();
|
|
840
|
+
cleanedUp = await Promise.race([cleanup, new Promise((resolve) => {
|
|
841
|
+
timer = setTimeout(() => {
|
|
842
|
+
log.error("sidecar.shutdown.timeout", {
|
|
843
|
+
phase,
|
|
844
|
+
active_tasks: activeTasks.size,
|
|
845
|
+
pending_requests: [...pendingRequests],
|
|
846
|
+
running_tasks: [...activeTasks].map((task) => task.label ?? "rem")
|
|
847
|
+
}, {
|
|
848
|
+
event_name: "sidecar.shutdown.timeout",
|
|
849
|
+
file: "packages/memory/src/sidecar/server.ts",
|
|
850
|
+
function: "startRemSidecar.stop",
|
|
851
|
+
site_id: "sidecar.shutdown.timeout"
|
|
852
|
+
});
|
|
853
|
+
for (const task of activeTasks) withLogContext$1({ operation_id: task.label }, () => {
|
|
854
|
+
log.error("sidecar.shutdown.task.running", { method: task.method ?? "rem" }, {
|
|
855
|
+
event_name: "sidecar.shutdown.task.running",
|
|
856
|
+
file: "packages/memory/src/sidecar/server.ts",
|
|
857
|
+
function: "startRemSidecar.stop",
|
|
858
|
+
site_id: "sidecar.shutdown.task.running"
|
|
859
|
+
});
|
|
860
|
+
});
|
|
861
|
+
server.closeAllConnections();
|
|
862
|
+
currentMemory?.stopTimers();
|
|
863
|
+
resolve(false);
|
|
864
|
+
}, MEMORY_SHUTDOWN_TIMEOUT_MS);
|
|
865
|
+
})]);
|
|
866
|
+
} finally {
|
|
867
|
+
clearTimeout(timer);
|
|
868
|
+
await discoveryPublication;
|
|
869
|
+
await removeOwnedDiscovery(discoveryPath, token);
|
|
870
|
+
log[cleanedUp ? "info" : "error"]("REM sidecar cleanup completed", {
|
|
871
|
+
outcome: cleanedUp ? "success" : "failed",
|
|
872
|
+
duration_ms: performance.now() - started,
|
|
873
|
+
active_tasks: activeTasks.size
|
|
874
|
+
}, {
|
|
875
|
+
event_name: "sidecar.shutdown.completed",
|
|
876
|
+
file: "packages/memory/src/sidecar/server.ts",
|
|
877
|
+
function: "startRemSidecar.stop",
|
|
878
|
+
site_id: "sidecar.shutdown.completed"
|
|
879
|
+
});
|
|
880
|
+
}
|
|
881
|
+
return cleanedUp ? {} : { drained: Promise.allSettled(activeTasks).then(() => void 0) };
|
|
882
|
+
}
|
|
883
|
+
};
|
|
884
|
+
}
|
|
885
|
+
async function routeRequest(request, response, pendingStore, chassisJournal, holdMs, pendingTimers, activeTasks, context, memory, isStopping) {
|
|
886
|
+
const url = new URL(request.url ?? "/healthz", REM_SIDECAR_ORIGIN);
|
|
887
|
+
if (request.method === "GET" && url.pathname === "/healthz") {
|
|
888
|
+
try {
|
|
889
|
+
const runtime = await memory.open();
|
|
890
|
+
sendJson(response, 200, {
|
|
891
|
+
status: runtime.modelPreparationError ? "degraded" : "ok",
|
|
892
|
+
...runtime.modelPreparationError && { error: runtime.modelPreparationError },
|
|
893
|
+
log_level: effectiveLogLevel(),
|
|
894
|
+
principal: getPrincipal(),
|
|
895
|
+
storePath: runtime.storePath,
|
|
896
|
+
accessCounters: runtime.counters
|
|
897
|
+
});
|
|
898
|
+
} catch (error) {
|
|
899
|
+
if (!(error instanceof SettingsUnavailableError)) throw error;
|
|
900
|
+
sendJson(response, 503, {
|
|
901
|
+
status: "error",
|
|
902
|
+
error: error.message
|
|
903
|
+
});
|
|
904
|
+
}
|
|
905
|
+
return;
|
|
906
|
+
}
|
|
907
|
+
if (url.pathname.startsWith("/v1/")) {
|
|
908
|
+
if (await serveMemoryRoute(request, response, url.pathname, () => memory.open(), activeTasks)) return;
|
|
909
|
+
}
|
|
910
|
+
if (request.method === "POST" && url.pathname === "/rem/run") {
|
|
911
|
+
const correlationId = readCorrelationId(request) ?? `rem-corr-${randomUUID()}`;
|
|
912
|
+
context.correlation_id = correlationId;
|
|
913
|
+
const raw = await readJsonBody(request);
|
|
914
|
+
const parsedInput = runRequestSchema.safeParse(raw);
|
|
915
|
+
if (!parsedInput.success) throw new HttpError(400, "invalid_request");
|
|
916
|
+
const input = parsedInput.data;
|
|
917
|
+
const types = "types" in input ? input.types : [input.type];
|
|
918
|
+
const requestedTypes = types.filter((type) => REM_BUILT_OPERATION_TYPES.some((operation) => operation === type));
|
|
919
|
+
const unknownTypes = types.filter((type) => !requestedTypes.includes(type));
|
|
920
|
+
if (unknownTypes.length) {
|
|
921
|
+
reportSidecarFailure("unsupported_rem_type", new Error(unknownTypes.join(",")));
|
|
922
|
+
context.error_code = "unsupported_rem_type";
|
|
923
|
+
sendJson(response, 400, {
|
|
924
|
+
error: "unsupported_rem_type",
|
|
925
|
+
unknownTypes
|
|
926
|
+
});
|
|
927
|
+
return;
|
|
928
|
+
}
|
|
929
|
+
const store = await pendingStore;
|
|
930
|
+
const runtime = await memory.open();
|
|
931
|
+
if (remNeedsHost(runtime.config.mode, runtime.settings.modelCalls) && !runtime.connectedRemPort()) {
|
|
932
|
+
context.error_code = "no host model connected";
|
|
933
|
+
sendJson(response, 503, { error: "no host model connected" });
|
|
934
|
+
return;
|
|
935
|
+
}
|
|
936
|
+
let allocation;
|
|
937
|
+
try {
|
|
938
|
+
allocation = await store.createQueued(requestedTypes, input.scope, correlationId);
|
|
939
|
+
} catch (error) {
|
|
940
|
+
if (errorMessage(error) === "wave_closed") {
|
|
941
|
+
context.error_code = "wave_closed";
|
|
942
|
+
sendJson(response, 409, { error: "wave_closed" });
|
|
943
|
+
return;
|
|
944
|
+
}
|
|
945
|
+
throw error;
|
|
946
|
+
}
|
|
947
|
+
const { created, job } = allocation;
|
|
948
|
+
context.job_id = job.job_id;
|
|
949
|
+
log.info("job_allocated", {
|
|
950
|
+
created,
|
|
951
|
+
event: "job_allocated",
|
|
952
|
+
job_id: job.job_id,
|
|
953
|
+
type: job.type,
|
|
954
|
+
scope: job.scope,
|
|
955
|
+
correlation_id: job.correlation_id
|
|
956
|
+
}, {
|
|
957
|
+
event_name: "sno_station_mem.server.job.allocated",
|
|
958
|
+
file: "packages/memory/src/sidecar/server.ts",
|
|
959
|
+
function: "routeRequest",
|
|
960
|
+
site_id: "server.routeRequest.0ea180a77c"
|
|
961
|
+
});
|
|
962
|
+
if (created) {
|
|
963
|
+
const delayMs = "types" in input ? 0 : 100;
|
|
964
|
+
const { promise: task, resolve, reject } = Promise.withResolvers();
|
|
965
|
+
activeTasks.add(Object.assign(task, { label: job.job_id }));
|
|
966
|
+
task.catch((error) => reportSidecarFailure("rem_run", error)).finally(() => activeTasks.delete(task));
|
|
967
|
+
const start = () => {
|
|
968
|
+
clearTimeout(timer);
|
|
969
|
+
pendingTimers.delete(timer);
|
|
970
|
+
runChassisJob(store, chassisJournal, job.job_id, holdMs, runtime).then(resolve, reject);
|
|
971
|
+
};
|
|
972
|
+
const timer = setTimeout(start, delayMs);
|
|
973
|
+
pendingTimers.set(timer, start);
|
|
974
|
+
if (isStopping()) start();
|
|
975
|
+
}
|
|
976
|
+
sendJson(response, 202, {
|
|
977
|
+
job_id: job.job_id,
|
|
978
|
+
waveId: job.job_id
|
|
979
|
+
});
|
|
980
|
+
return;
|
|
981
|
+
}
|
|
982
|
+
if (request.method === "GET" && url.pathname.startsWith("/rem/jobs/")) {
|
|
983
|
+
const store = await pendingStore;
|
|
984
|
+
const jobId = url.pathname.slice(REM_JOBS_PATH_PREFIX.length);
|
|
985
|
+
const job = store.get(jobId);
|
|
986
|
+
context.job_id = jobId;
|
|
987
|
+
context.correlation_id = readCorrelationId(request) ?? job?.correlation_id;
|
|
988
|
+
if (!job) {
|
|
989
|
+
context.error_code = "job_not_found";
|
|
990
|
+
sendJson(response, 404, { error: "job_not_found" });
|
|
991
|
+
return;
|
|
992
|
+
}
|
|
993
|
+
sendJson(response, 200, job);
|
|
994
|
+
return;
|
|
995
|
+
}
|
|
996
|
+
context.error_code = "not_found";
|
|
997
|
+
sendJson(response, 404, { error: "not_found" });
|
|
998
|
+
}
|
|
999
|
+
async function runChassisJob(store, journal, waveId, holdMs, runtime) {
|
|
1000
|
+
const queued = store.get(waveId);
|
|
1001
|
+
if (queued === void 0) throw new Error(`REM wave not found: ${waveId}`);
|
|
1002
|
+
const resuming = queued.state === "running";
|
|
1003
|
+
return withLogContext$1({
|
|
1004
|
+
operation_id: queued.job_id,
|
|
1005
|
+
job_id: queued.job_id,
|
|
1006
|
+
session_reference: queued.scope,
|
|
1007
|
+
external_reference: queued.correlation_id
|
|
1008
|
+
}, async () => {
|
|
1009
|
+
const started = performance.now();
|
|
1010
|
+
let persistence = "unavailable";
|
|
1011
|
+
let completion;
|
|
1012
|
+
let failed = false;
|
|
1013
|
+
let failure;
|
|
1014
|
+
const requestedOperations = queued.requested_operations.flatMap((operation) => {
|
|
1015
|
+
const parsed = parseRemOperationType(operation);
|
|
1016
|
+
return parsed === void 0 ? [] : [parsed];
|
|
1017
|
+
});
|
|
1018
|
+
let writesApplied = false;
|
|
1019
|
+
try {
|
|
1020
|
+
if (remNeedsHost(runtime.config.mode, runtime.settings.modelCalls) && !runtime.connectedRemPort()) {
|
|
1021
|
+
await skipNonTerminalJob(store, queued.job_id, "no host model connected");
|
|
1022
|
+
return;
|
|
1023
|
+
}
|
|
1024
|
+
const running = resuming ? queued : await store.transition(queued.job_id, {
|
|
1025
|
+
state: "running",
|
|
1026
|
+
started_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
1027
|
+
});
|
|
1028
|
+
if (!resuming) await auditRem("rem_triggered", running);
|
|
1029
|
+
if (holdMs > 0) await new Promise((resolvePromise) => setTimeout(resolvePromise, holdMs));
|
|
1030
|
+
const configuration = readRemOperationalConfig();
|
|
1031
|
+
const enabledBySettings = runtime.settings.rem.operations;
|
|
1032
|
+
const enabledOperations = [];
|
|
1033
|
+
const cleanRefusalReasons = [];
|
|
1034
|
+
for (const operation of requestedOperations) {
|
|
1035
|
+
let reason;
|
|
1036
|
+
if (!isBuiltOperation(operation)) reason = `not-built:${operation}`;
|
|
1037
|
+
else if (configuration.operations[operation] && enabledBySettings.includes(operation)) {
|
|
1038
|
+
enabledOperations.push(operation);
|
|
1039
|
+
continue;
|
|
1040
|
+
} else reason = `switched-off:${operation}`;
|
|
1041
|
+
cleanRefusalReasons.push(reason);
|
|
1042
|
+
await appendChassisRefusal(journal, queued, operation, "refused", reason);
|
|
1043
|
+
}
|
|
1044
|
+
const { runRemProductionOrderedWave } = await import("./rem-batch-executor-bc0IFlp2.js");
|
|
1045
|
+
const result = enabledOperations.length === 0 ? {
|
|
1046
|
+
decision: "allow",
|
|
1047
|
+
reasonCode: null,
|
|
1048
|
+
waveId: queued.job_id,
|
|
1049
|
+
actionsApplied: 0,
|
|
1050
|
+
actionableCandidateCount: 0,
|
|
1051
|
+
appliedFraction: null,
|
|
1052
|
+
candidateCount: 0,
|
|
1053
|
+
stampedSkippedCount: 0,
|
|
1054
|
+
parseFailureCount: 0,
|
|
1055
|
+
topRefusalReasons: [],
|
|
1056
|
+
measurements: {
|
|
1057
|
+
rowsConsidered: 0,
|
|
1058
|
+
pairsBuilt: 0,
|
|
1059
|
+
pairCapBinding: false,
|
|
1060
|
+
modelCalls: 0,
|
|
1061
|
+
modelTokens: 0,
|
|
1062
|
+
wallMs: 0
|
|
1063
|
+
},
|
|
1064
|
+
perOperation: []
|
|
1065
|
+
} : await runRemProductionOrderedWave({
|
|
1066
|
+
mode: runtime.config.mode,
|
|
1067
|
+
settings: runtime.settings,
|
|
1068
|
+
agentPort: runtime.connectedRemPort(),
|
|
1069
|
+
scope: queued.scope,
|
|
1070
|
+
waveId: queued.job_id,
|
|
1071
|
+
requestedOperations: enabledOperations
|
|
1072
|
+
});
|
|
1073
|
+
if (result.decision === "refuse") {
|
|
1074
|
+
const operation = enabledOperations[0] ?? "rem-replace";
|
|
1075
|
+
const reason = result.reasonCode;
|
|
1076
|
+
await appendChassisRefusal(journal, queued, operation, "failed", reason);
|
|
1077
|
+
throw new Error(reason);
|
|
1078
|
+
}
|
|
1079
|
+
writesApplied = true;
|
|
1080
|
+
const completionStats = {
|
|
1081
|
+
operations: result.actionsApplied,
|
|
1082
|
+
applied_count: result.actionsApplied,
|
|
1083
|
+
actionable_candidate_count: result.actionableCandidateCount,
|
|
1084
|
+
applied_fraction: result.appliedFraction,
|
|
1085
|
+
scan: {
|
|
1086
|
+
scope: queued.scope,
|
|
1087
|
+
candidate_count: result.candidateCount,
|
|
1088
|
+
stamped_skipped_count: result.stampedSkippedCount,
|
|
1089
|
+
actionable_candidate_count: result.actionableCandidateCount
|
|
1090
|
+
},
|
|
1091
|
+
parse_failure_count: result.parseFailureCount,
|
|
1092
|
+
top_refusal_reasons: [...cleanRefusalReasons, ...result.topRefusalReasons].slice(0, 2),
|
|
1093
|
+
measured: {
|
|
1094
|
+
rows_considered: result.measurements.rowsConsidered,
|
|
1095
|
+
pairs_built: result.measurements.pairsBuilt,
|
|
1096
|
+
pair_cap_binding: result.measurements.pairCapBinding,
|
|
1097
|
+
model_calls: result.measurements.modelCalls,
|
|
1098
|
+
model_tokens: result.measurements.modelTokens,
|
|
1099
|
+
wall_ms: result.measurements.wallMs
|
|
1100
|
+
},
|
|
1101
|
+
...result.perOperation.length === 0 ? {} : { by_operation: result.perOperation.map(({ operation, ...operationResult }) => ({
|
|
1102
|
+
operation,
|
|
1103
|
+
applied_count: operationResult.actionsApplied,
|
|
1104
|
+
actionable_candidate_count: operationResult.actionableCandidateCount,
|
|
1105
|
+
candidate_count: operationResult.candidateCount,
|
|
1106
|
+
parse_failure_count: operationResult.parseFailureCount,
|
|
1107
|
+
top_refusal_reasons: operationResult.topRefusalReasons,
|
|
1108
|
+
measured: {
|
|
1109
|
+
rows_considered: operationResult.measurements.rowsConsidered,
|
|
1110
|
+
pairs_built: operationResult.measurements.pairsBuilt,
|
|
1111
|
+
pair_cap_binding: operationResult.measurements.pairCapBinding,
|
|
1112
|
+
model_calls: operationResult.measurements.modelCalls,
|
|
1113
|
+
model_tokens: operationResult.measurements.modelTokens,
|
|
1114
|
+
wall_ms: operationResult.measurements.wallMs
|
|
1115
|
+
}
|
|
1116
|
+
})) }
|
|
1117
|
+
};
|
|
1118
|
+
completion = completionStats;
|
|
1119
|
+
persistence = await persistCompletedJob(store, running, completionStats);
|
|
1120
|
+
for (const operationResult of result.perOperation) try {
|
|
1121
|
+
await journal.appendJournalWithCorrelation(running.job_id, operationResult.operation, running.correlation_id, {
|
|
1122
|
+
stage: operationResult.operation,
|
|
1123
|
+
outcome: operationResult.actionsApplied > 0 ? "done" : "no-action",
|
|
1124
|
+
pairsScanned: operationResult.measurements.pairsBuilt,
|
|
1125
|
+
verdicts: operationResult.actionableCandidateCount,
|
|
1126
|
+
actionsApplied: operationResult.actionsApplied
|
|
1127
|
+
});
|
|
1128
|
+
} catch (error) {
|
|
1129
|
+
log.error("job_completion_journal_failed", {
|
|
1130
|
+
error,
|
|
1131
|
+
job_id: running.job_id,
|
|
1132
|
+
job_type: operationResult.operation,
|
|
1133
|
+
correlation_id: running.correlation_id
|
|
1134
|
+
}, {
|
|
1135
|
+
event_name: "sno_station_mem.server.job.completion.journal.failed",
|
|
1136
|
+
file: "packages/memory/src/sidecar/server.ts",
|
|
1137
|
+
function: "runChassisJob",
|
|
1138
|
+
site_id: "server.runChassisJob.e7639bd8fc"
|
|
1139
|
+
});
|
|
1140
|
+
}
|
|
1141
|
+
} catch (error) {
|
|
1142
|
+
failed = true;
|
|
1143
|
+
failure = error;
|
|
1144
|
+
if (!writesApplied) {
|
|
1145
|
+
if (error instanceof ModelCallRefusedError) await skipNonTerminalJob(store, queued.job_id, error.reason, error.callId, error.destination);
|
|
1146
|
+
else await failNonTerminalJob(store, queued.job_id, errorMessage(error));
|
|
1147
|
+
}
|
|
1148
|
+
} finally {
|
|
1149
|
+
let outcome = "empty-success";
|
|
1150
|
+
if (failed) outcome = writesApplied ? "partial" : "failed";
|
|
1151
|
+
else if (persistence === "unavailable") outcome = "partial";
|
|
1152
|
+
else if ((completion?.applied_count ?? 0) > 0) outcome = "success";
|
|
1153
|
+
log[failed || persistence === "unavailable" ? "error" : "info"]("REM job completed", {
|
|
1154
|
+
outcome,
|
|
1155
|
+
job_id: queued.job_id,
|
|
1156
|
+
basis: persistence,
|
|
1157
|
+
applied_count: completion?.applied_count ?? (writesApplied ? null : 0),
|
|
1158
|
+
candidate_count: completion?.scan?.candidate_count ?? null,
|
|
1159
|
+
parse_failure_count: completion?.parse_failure_count ?? null,
|
|
1160
|
+
duration_ms: performance.now() - started,
|
|
1161
|
+
...failure === void 0 ? {} : { error: failure }
|
|
1162
|
+
}, {
|
|
1163
|
+
event_name: "sidecar.job.completed",
|
|
1164
|
+
file: "packages/memory/src/sidecar/server.ts",
|
|
1165
|
+
function: "runChassisJob",
|
|
1166
|
+
site_id: "sidecar.job.completed"
|
|
1167
|
+
});
|
|
1168
|
+
}
|
|
1169
|
+
});
|
|
1170
|
+
}
|
|
1171
|
+
async function persistCompletedJob(store, job, stats) {
|
|
1172
|
+
const auditPersisted = await auditRem("rem_completed", job, { stats });
|
|
1173
|
+
await store.transition(job.job_id, {
|
|
1174
|
+
state: "done",
|
|
1175
|
+
finished_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1176
|
+
stats
|
|
1177
|
+
});
|
|
1178
|
+
if (store.isPersisted(job.job_id)) return "job_journal";
|
|
1179
|
+
if (auditPersisted) return "completed_audit";
|
|
1180
|
+
reportSidecarFailure("completion-persistence", /* @__PURE__ */ new Error("completed work has no durable completion receipt"));
|
|
1181
|
+
return "unavailable";
|
|
1182
|
+
}
|
|
1183
|
+
async function appendChassisRefusal(journal, job, jobType, outcome, reason) {
|
|
1184
|
+
await journal.appendJournalWithCorrelation(job.job_id, jobType, job.correlation_id, {
|
|
1185
|
+
stage: jobType,
|
|
1186
|
+
outcome,
|
|
1187
|
+
pairsScanned: 0,
|
|
1188
|
+
verdicts: 0,
|
|
1189
|
+
actionsApplied: 0,
|
|
1190
|
+
reason
|
|
1191
|
+
});
|
|
1192
|
+
}
|
|
1193
|
+
function isBuiltOperation(jobType) {
|
|
1194
|
+
return REM_BUILT_OPERATION_TYPES.some((operation) => operation === jobType);
|
|
1195
|
+
}
|
|
1196
|
+
function errorMessage(error) {
|
|
1197
|
+
return error instanceof Error ? error.message : String(error);
|
|
1198
|
+
}
|
|
1199
|
+
async function failNonTerminalJob(store, jobId, reason) {
|
|
1200
|
+
const current = store.get(jobId);
|
|
1201
|
+
if (!current || current.state !== "queued" && current.state !== "running") return;
|
|
1202
|
+
try {
|
|
1203
|
+
await auditRem("rem_failed", current, { error: reason });
|
|
1204
|
+
await store.transition(jobId, {
|
|
1205
|
+
state: "failed",
|
|
1206
|
+
finished_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1207
|
+
error: reason
|
|
1208
|
+
});
|
|
1209
|
+
} catch (error) {
|
|
1210
|
+
log.error("job_failure_audit_failed", {
|
|
1211
|
+
error,
|
|
1212
|
+
job_id: jobId
|
|
1213
|
+
}, {
|
|
1214
|
+
event_name: "sno_station_mem.server.job.failure.audit.failed",
|
|
1215
|
+
file: "packages/memory/src/sidecar/server.ts",
|
|
1216
|
+
function: "failNonTerminalJob",
|
|
1217
|
+
site_id: "server.failNonTerminalJob.cf84e07c6d"
|
|
1218
|
+
});
|
|
1219
|
+
}
|
|
1220
|
+
}
|
|
1221
|
+
async function skipNonTerminalJob(store, jobId, reason, callId, destination) {
|
|
1222
|
+
const current = store.get(jobId);
|
|
1223
|
+
if (!current || current.state !== "queued" && current.state !== "running") return;
|
|
1224
|
+
await auditRem("rem_skipped", current, {
|
|
1225
|
+
reason,
|
|
1226
|
+
call_id: callId,
|
|
1227
|
+
destination
|
|
1228
|
+
});
|
|
1229
|
+
await store.transition(jobId, {
|
|
1230
|
+
state: "skipped",
|
|
1231
|
+
finished_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1232
|
+
error: reason
|
|
1233
|
+
});
|
|
1234
|
+
}
|
|
1235
|
+
async function recoverInterruptedJobs(store, completedJobStats, jobs) {
|
|
1236
|
+
const resumableJobIds = [];
|
|
1237
|
+
for (const job of jobs) try {
|
|
1238
|
+
const stats = completedJobStats.get(job.job_id);
|
|
1239
|
+
if (stats !== void 0) {
|
|
1240
|
+
await store.transition(job.job_id, {
|
|
1241
|
+
state: "done",
|
|
1242
|
+
finished_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1243
|
+
stats
|
|
1244
|
+
});
|
|
1245
|
+
continue;
|
|
1246
|
+
}
|
|
1247
|
+
if (job.state === "running") {
|
|
1248
|
+
log.warn("job_recovery_resuming", {
|
|
1249
|
+
job_id: job.job_id,
|
|
1250
|
+
correlation_id: job.correlation_id,
|
|
1251
|
+
reason: "completion_receipt_missing_replay_same_wave"
|
|
1252
|
+
}, {
|
|
1253
|
+
event_name: "sno_station_mem.server.job.recovery.resuming",
|
|
1254
|
+
file: "packages/memory/src/sidecar/server.ts",
|
|
1255
|
+
function: "recoverInterruptedJobs",
|
|
1256
|
+
site_id: "server.recoverInterruptedJobs.15309ea841"
|
|
1257
|
+
});
|
|
1258
|
+
resumableJobIds.push(job.job_id);
|
|
1259
|
+
continue;
|
|
1260
|
+
}
|
|
1261
|
+
await auditRem("rem_failed", job, { error: "sidecar_restart" });
|
|
1262
|
+
await store.transition(job.job_id, {
|
|
1263
|
+
state: "failed",
|
|
1264
|
+
finished_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1265
|
+
error: "sidecar_restart"
|
|
1266
|
+
});
|
|
1267
|
+
} catch (error) {
|
|
1268
|
+
reportSidecarFailure("job-recovery", error);
|
|
1269
|
+
}
|
|
1270
|
+
return resumableJobIds;
|
|
1271
|
+
}
|
|
1272
|
+
async function readCompletedJobStats(wantedJobs) {
|
|
1273
|
+
const completed = /* @__PURE__ */ new Map();
|
|
1274
|
+
for await (const line of readJsonlLines(getAuditPath(getSnoStationMemStateDir()))) {
|
|
1275
|
+
if (line.length === 0) continue;
|
|
1276
|
+
try {
|
|
1277
|
+
const entry = JSON.parse(line);
|
|
1278
|
+
if (!isRecord(entry) || entry["event"] !== "rem_completed") continue;
|
|
1279
|
+
const details = entry["details"];
|
|
1280
|
+
if (!isRecord(details) || typeof details["job_id"] !== "string" || !wantedJobs.has(details["job_id"])) continue;
|
|
1281
|
+
const type = details["type"];
|
|
1282
|
+
if (typeof type !== "string" || parseRemOperationType(type) === void 0) continue;
|
|
1283
|
+
const stats = parseRemJobStats(details["stats"]);
|
|
1284
|
+
if (stats === void 0) continue;
|
|
1285
|
+
completed.set(details["job_id"], stats);
|
|
1286
|
+
} catch {}
|
|
1287
|
+
}
|
|
1288
|
+
return completed;
|
|
1289
|
+
}
|
|
1290
|
+
async function auditRem(event, job, extra = {}) {
|
|
1291
|
+
return appendAuditEntryStrict(getSnoStationMemStateDir(), {
|
|
1292
|
+
event,
|
|
1293
|
+
scope: job.scope,
|
|
1294
|
+
resultStatus: event === "rem_skipped" ? "skipped" : event === "rem_failed" ? "error" : "ok",
|
|
1295
|
+
details: {
|
|
1296
|
+
type: job.type,
|
|
1297
|
+
scope: job.scope,
|
|
1298
|
+
job_id: job.job_id,
|
|
1299
|
+
correlation_id: job.correlation_id,
|
|
1300
|
+
source: REM_SOURCE,
|
|
1301
|
+
stats: { operations: 0 },
|
|
1302
|
+
...extra
|
|
1303
|
+
}
|
|
1304
|
+
});
|
|
1305
|
+
}
|
|
1306
|
+
function readCorrelationId(request) {
|
|
1307
|
+
const value = request.headers[REM_CORRELATION_ID_HEADER];
|
|
1308
|
+
if (typeof value !== "string") return void 0;
|
|
1309
|
+
const trimmed = value.trim();
|
|
1310
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
1311
|
+
}
|
|
1312
|
+
async function readJsonBody(request) {
|
|
1313
|
+
const body = await readRequestBody(request);
|
|
1314
|
+
try {
|
|
1315
|
+
return JSON.parse(body);
|
|
1316
|
+
} catch {
|
|
1317
|
+
throw new HttpError(400, "invalid_request");
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
function sendJson(response, status, body) {
|
|
1321
|
+
const payload = JSON.stringify(body);
|
|
1322
|
+
response.writeHead(status, {
|
|
1323
|
+
"Content-Type": "application/json",
|
|
1324
|
+
"Content-Length": Buffer.byteLength(payload)
|
|
1325
|
+
});
|
|
1326
|
+
response.end(payload);
|
|
1327
|
+
}
|
|
1328
|
+
function requestPath(request) {
|
|
1329
|
+
try {
|
|
1330
|
+
return new URL(request.url ?? "/healthz", REM_SIDECAR_ORIGIN).pathname;
|
|
1331
|
+
} catch {
|
|
1332
|
+
return request.url ?? "/healthz";
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
async function listen(server) {
|
|
1336
|
+
await new Promise((resolvePromise, reject) => {
|
|
1337
|
+
server.once("error", reject);
|
|
1338
|
+
server.listen(0, REM_SIDECAR_HOST, () => {
|
|
1339
|
+
server.removeListener("error", reject);
|
|
1340
|
+
resolvePromise();
|
|
1341
|
+
});
|
|
1342
|
+
});
|
|
1343
|
+
}
|
|
1344
|
+
async function closeServer(server) {
|
|
1345
|
+
if (!server.listening) return;
|
|
1346
|
+
await new Promise((resolvePromise, reject) => {
|
|
1347
|
+
server.close((error) => error ? reject(error) : resolvePromise());
|
|
1348
|
+
});
|
|
1349
|
+
}
|
|
1350
|
+
async function writeDiscovery(discoveryPath, discovery) {
|
|
1351
|
+
const parent = path.dirname(discoveryPath);
|
|
1352
|
+
await mkdir(parent, { recursive: true });
|
|
1353
|
+
const temporaryPath = `${discoveryPath}.${randomUUID()}.tmp`;
|
|
1354
|
+
const handle = await open(temporaryPath, "wx", 384);
|
|
1355
|
+
try {
|
|
1356
|
+
try {
|
|
1357
|
+
await handle.writeFile(JSON.stringify(discovery), "utf8");
|
|
1358
|
+
await handle.sync();
|
|
1359
|
+
} finally {
|
|
1360
|
+
await handle.close();
|
|
1361
|
+
}
|
|
1362
|
+
await rename(temporaryPath, discoveryPath);
|
|
1363
|
+
await syncDirectory(parent);
|
|
1364
|
+
} finally {
|
|
1365
|
+
await rm(temporaryPath, { force: true });
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
async function removeOwnedDiscovery(discoveryPath, token) {
|
|
1369
|
+
if (!await discoveryBelongsTo(discoveryPath, token)) return;
|
|
1370
|
+
await rm(discoveryPath, { force: true });
|
|
1371
|
+
await syncDirectory(path.dirname(discoveryPath));
|
|
1372
|
+
}
|
|
1373
|
+
function removeOwnedDiscoverySync(discoveryPath, token) {
|
|
1374
|
+
try {
|
|
1375
|
+
if (JSON.parse(readFileSync(discoveryPath, "utf8")).token === token) rmSync(discoveryPath, { force: true });
|
|
1376
|
+
} catch {}
|
|
1377
|
+
}
|
|
1378
|
+
async function discoveryBelongsTo(discoveryPath, token) {
|
|
1379
|
+
if (!existsSync(discoveryPath)) return false;
|
|
1380
|
+
try {
|
|
1381
|
+
return JSON.parse(await readFile(discoveryPath, "utf8")).token === token;
|
|
1382
|
+
} catch {
|
|
1383
|
+
return false;
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1386
|
+
async function syncDirectory(directory) {
|
|
1387
|
+
if (process.platform === "win32") return;
|
|
1388
|
+
const handle = await open(directory, "r");
|
|
1389
|
+
try {
|
|
1390
|
+
await handle.sync();
|
|
1391
|
+
} finally {
|
|
1392
|
+
await handle.close();
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
function isRecord(value) {
|
|
1396
|
+
return typeof value === "object" && value !== null;
|
|
1397
|
+
}
|
|
1398
|
+
function reportSidecarFailure(step, error) {
|
|
1399
|
+
log.error("sidecar.operation.failed", {
|
|
1400
|
+
step,
|
|
1401
|
+
error
|
|
1402
|
+
}, {
|
|
1403
|
+
event_name: "sidecar.operation.failed",
|
|
1404
|
+
file: "packages/memory/src/sidecar/server.ts",
|
|
1405
|
+
function: "reportSidecarFailure",
|
|
1406
|
+
site_id: "sidecar.operation.failed"
|
|
1407
|
+
});
|
|
1408
|
+
}
|
|
1409
|
+
//#endregion
|
|
1410
|
+
export { DuplicateSidecarError, startRemSidecar };
|