@snoai/memory 1.2.8 → 1.2.10

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.
Files changed (177) hide show
  1. package/README.md +4 -4
  2. package/dist/{access-tracker-D1e-2-Bb.d.ts → access-tracker-BOPHSJjf.d.ts} +4 -4
  3. package/dist/{adapter-TCrv5P-C.d.ts → adapter-lyb6RaQE.d.ts} +2 -2
  4. package/dist/{atomic-memory-extraction-C0n0wHzt.d.ts → atomic-memory-extraction-B_XpkhMd.d.ts} +7 -5
  5. package/dist/{atomic-memory-extraction-BG42OXou.js → atomic-memory-extraction-o2v8mp7G.js} +109 -50
  6. package/dist/{b-profile-section-dictionary-provider-y3DMQ518.d.ts → b-profile-section-dictionary-provider-BK5NBs-y.d.ts} +1 -1
  7. package/dist/{capture-policy-detector-BY7v5Cq1.d.ts → capture-policy-detector-BvVGmxxX.d.ts} +1 -1
  8. package/dist/{classifier-DnbbtwTS.d.ts → classifier-CXQxJJPP.d.ts} +1 -1
  9. package/dist/client.d.ts +1 -1
  10. package/dist/{coding-skin-_Qu3INZD.js → coding-skin-azRXA51Z.js} +2 -1
  11. package/dist/coding-skin.d.ts +1 -0
  12. package/dist/coding-skin.js +2 -2
  13. package/dist/{config-BCZRhUvW.js → config-C7LzSWhG.js} +9 -4
  14. package/dist/{connection-C9YD5sgm.js → connection-PQcq3TPO.js} +1 -1
  15. package/dist/{derived-suppression-cache-BLAH0nB9.d.ts → derived-suppression-cache-Ckwjey7I.d.ts} +1 -1
  16. package/dist/diagnostic-encoder.js +1 -1
  17. package/dist/{index-dQMkc_6y.d.ts → index-CsPUNkcm.d.ts} +2 -2
  18. package/dist/{index-BO4RU_mc.d.ts → index-DPfJrOFD.d.ts} +1 -1
  19. package/dist/{index-DL8AVj0Z.d.ts → index-Pb9LrHgL.d.ts} +4 -5
  20. package/dist/index.d.ts +12 -9
  21. package/dist/index.js +97 -47
  22. package/dist/internal/config/index.d.ts +1 -1
  23. package/dist/internal/config/index.js +4 -5
  24. package/dist/internal/config/plugin-config-schema.d.ts +1 -1
  25. package/dist/internal/config/plugin-config-schema.js +1 -1
  26. package/dist/internal/config/product-mode.d.ts +1 -1
  27. package/dist/internal/config/settings.d.ts +1 -1
  28. package/dist/internal/config/settings.js +4 -4
  29. package/dist/internal/config/skin-defaults.d.ts +1 -1
  30. package/dist/internal/contract/profile.d.ts +1 -1
  31. package/dist/internal/contract/routes.d.ts +1 -1
  32. package/dist/internal/engine/bindings/memory-cli-shared.d.ts +4 -4
  33. package/dist/internal/engine/bindings/memory-forget-tool.js +1 -1
  34. package/dist/internal/engine/bindings/memory-list-tool.js +1 -1
  35. package/dist/internal/engine/bindings/memory-recall-tool.js +1 -1
  36. package/dist/internal/engine/bindings/memory-reflection-resolve-tool.js +1 -1
  37. package/dist/internal/engine/bindings/memory-stats-tool.js +2 -2
  38. package/dist/internal/engine/bindings/memory-store-tool.js +4 -4
  39. package/dist/internal/engine/bindings/memory-tool-access.d.ts +1 -1
  40. package/dist/internal/engine/bindings/memory-tool-access.js +1 -1
  41. package/dist/internal/engine/bindings/memory-tool-results.d.ts +1 -1
  42. package/dist/internal/engine/bindings/memory-tool-results.js +1 -1
  43. package/dist/internal/engine/bindings/memory-tool-schemas.d.ts +5 -5
  44. package/dist/internal/engine/bindings/memory-tool-schemas.js +1 -1
  45. package/dist/internal/engine/bindings/memory-update-tool.js +3 -3
  46. package/dist/internal/engine/bindings/sno-station-mem-ambient-learning-hook.d.ts +4 -4
  47. package/dist/internal/engine/bindings/sno-station-mem-ambient-learning-hook.js +4 -4
  48. package/dist/internal/engine/bindings/sno-station-mem-auto-recall-hook.d.ts +4 -4
  49. package/dist/internal/engine/bindings/sno-station-mem-auto-recall-hook.js +2 -2
  50. package/dist/internal/engine/bindings/sno-station-mem-insight-distill-factory.d.ts +6 -6
  51. package/dist/internal/engine/bindings/sno-station-mem-insight-distill-factory.js +7 -5
  52. package/dist/internal/engine/bindings/sno-station-mem-message-transcript.js +2 -2
  53. package/dist/internal/engine/bindings/sno-station-mem-reset-hook.d.ts +3 -3
  54. package/dist/internal/engine/bindings/sno-station-mem-runtime-mode.d.ts +1 -1
  55. package/dist/internal/engine/bindings/sno-station-mem-runtime-mode.js +3 -3
  56. package/dist/internal/engine/extraction/atomic-memory-extraction.d.ts +1 -1
  57. package/dist/internal/engine/extraction/atomic-memory-extraction.js +1 -1
  58. package/dist/internal/engine/extraction/b-profile-section-dictionary-provider.d.ts +1 -1
  59. package/dist/internal/engine/extraction/b-profile-section-rekey.d.ts +5 -5
  60. package/dist/internal/engine/extraction/capture-policy-detector.d.ts +1 -1
  61. package/dist/internal/engine/extraction/memory-metadata-codec.d.ts +2 -2
  62. package/dist/internal/engine/extraction/memory-temporality-classifier.d.ts +1 -1
  63. package/dist/internal/engine/extraction/memory-temporality-classifier.js +1 -1
  64. package/dist/internal/engine/observability/adapter.d.ts +1 -1
  65. package/dist/internal/engine/observability/memory-snapshot.d.ts +1 -1
  66. package/dist/internal/engine/observability/observable-embedder.d.ts +1 -1
  67. package/dist/internal/engine/observability/observable-llm-client.d.ts +1 -1
  68. package/dist/internal/engine/observability/observable-llm-client.js +0 -2
  69. package/dist/internal/engine/observability/observable-memory-store.d.ts +1 -1
  70. package/dist/internal/engine/observability/observable-memory-store.js +2 -2
  71. package/dist/internal/engine/observability/observable-retriever.d.ts +1 -1
  72. package/dist/internal/engine/observability/observable-retriever.js +2 -2
  73. package/dist/internal/engine/observability/runtime-diagnostics.d.ts +1 -1
  74. package/dist/internal/engine/observability/runtime-diagnostics.js +3 -3
  75. package/dist/internal/engine/operations/daily-spend-estimator.js +1 -1
  76. package/dist/internal/engine/operations/memory-tier-promoter.d.ts +1 -1
  77. package/dist/internal/engine/operations/runtime-audit-log.d.ts +3 -3
  78. package/dist/internal/engine/operations/runtime-audit-log.js +2 -2
  79. package/dist/internal/engine/provider/provider-registration.d.ts +4 -4
  80. package/dist/internal/engine/provider/provider-row-renderer.d.ts +1 -1
  81. package/dist/internal/engine/reflection/reflection-command-hooks.d.ts +1 -1
  82. package/dist/internal/engine/reflection/reflection-command-hooks.js +1 -1
  83. package/dist/internal/engine/reflection/reflection-deps.d.ts +1 -1
  84. package/dist/internal/engine/reflection/reflection-embedded-generator.d.ts +1 -1
  85. package/dist/internal/engine/reflection/reflection-embedded-generator.js +1 -1
  86. package/dist/internal/engine/reflection/reflection-injection-hooks.d.ts +1 -1
  87. package/dist/internal/engine/reflection/reflection-injection-hooks.js +1 -1
  88. package/dist/internal/engine/reflection/reflection-lifecycle-hooks.d.ts +1 -1
  89. package/dist/internal/engine/reflection/reflection-lifecycle-hooks.js +1 -1
  90. package/dist/internal/engine/reflection/strategy-hook-runner.d.ts +3 -3
  91. package/dist/internal/engine/reflection/strategy-hook-runner.js +2 -2
  92. package/dist/internal/engine/rem/classifier.d.ts +1 -1
  93. package/dist/internal/engine/rem/index.d.ts +2 -2
  94. package/dist/internal/engine/rem/index.js +9 -7
  95. package/dist/internal/engine/retrieval/access-tracker.d.ts +1 -1
  96. package/dist/internal/engine/retrieval/retriever.d.ts +1 -1
  97. package/dist/internal/engine/retrieval/retriever.js +1 -1
  98. package/dist/internal/engine/security/memory-scope-policy.js +1 -1
  99. package/dist/internal/engine/security/scopes.js +2 -2
  100. package/dist/internal/engine/shared/memory-kind-policy.d.ts +1 -1
  101. package/dist/internal/engine/shared/paths.d.ts +2 -2
  102. package/dist/internal/engine/shared/types.d.ts +2 -2
  103. package/dist/internal/engine/shared/types.js +1 -1
  104. package/dist/internal/engine/shared/utils.d.ts +1 -1
  105. package/dist/internal/engine/shared/utils.js +2 -3
  106. package/dist/internal/engine/telemetry/memory-telemetry-api.js +2 -2
  107. package/dist/internal/engine/telemetry/observe-ledger.d.ts +1 -1
  108. package/dist/internal/engine/telemetry/observe-ledger.js +46 -19
  109. package/dist/internal/model/llm-client.d.ts +2 -2
  110. package/dist/internal/model/llm-client.js +1 -1
  111. package/dist/internal/model/llm-mode-routing.d.ts +1 -1
  112. package/dist/internal/model/llm-mode-routing.js +1 -1
  113. package/dist/internal/store/atomic-memory-cutover-sql.js +1 -1
  114. package/dist/internal/store/connection.js +1 -1
  115. package/dist/internal/store/maintenance.d.ts +2 -2
  116. package/dist/internal/store/maintenance.js +444 -40
  117. package/dist/internal/store/memory-kinds-cutover-migrator.js +1 -1
  118. package/dist/internal/store/migrations.js +1 -1
  119. package/dist/internal/store/store.d.ts +2 -2
  120. package/dist/internal/store/store.js +1 -1
  121. package/dist/{llm-client-RqmOEQkg.d.ts → llm-client-BTaUVmDe.d.ts} +1 -1
  122. package/dist/{llm-client-fVrzPcyz.js → llm-client-ConEyQEs.js} +51 -12
  123. package/dist/{log-site-catalog.generated-Bb9w8j4f.js → log-site-catalog.generated-C9RmyAA0.js} +227 -192
  124. package/dist/log-site-catalog.json +1 -1
  125. package/dist/{memory-kind-policy-BOGb8thm.d.ts → memory-kind-policy-ClQEXeOL.d.ts} +1 -1
  126. package/dist/{memory-metadata-codec-DcSqjbWR.d.ts → memory-metadata-codec-wVAuoKrG.d.ts} +1 -1
  127. package/dist/{memory-runtime-DSP8cXik.js → memory-runtime-HibOMCFa.js} +482 -297
  128. package/dist/{memory-scope-policy-DICSCURA.js → memory-scope-policy--TiLZnrE.js} +2 -2
  129. package/dist/{memory-snapshot-C5BnJ2rO.d.ts → memory-snapshot-uTdxJnIh.d.ts} +2 -2
  130. package/dist/{memory-store-base-z593oJ4h.d.ts → memory-store-base-Bk2dOKED.d.ts} +5 -6
  131. package/dist/{memory-store-shared-DqpWJXZZ.js → memory-store-shared-CURizQIy.js} +1 -1
  132. package/dist/{memory-telemetry-receipts-YsxbwOAf.js → memory-telemetry-receipts-cjIQGwdp.js} +5 -1
  133. package/dist/{memory-temporality-classifier-SzlUCgTj.d.ts → memory-temporality-classifier-CcxfHYoT.d.ts} +1 -1
  134. package/dist/{memory-temporality-classifier-CeSOL2vq.js → memory-temporality-classifier-MBJyJdvB.js} +2 -2
  135. package/dist/{memory-tier-promoter-Z7jprFB7.d.ts → memory-tier-promoter-MkZPQ8u1.d.ts} +1 -1
  136. package/dist/memory-tool-dependencies--SrMdT3P.d.ts +14 -0
  137. package/dist/{memory-tool-dependencies-CnaGABQG.js → memory-tool-dependencies-DUMY8SwB.js} +3 -3
  138. package/dist/{model-call-table-BZJQkbKX.js → model-call-table-BE97f37s.js} +6 -1
  139. package/dist/{observable-embedder-DjhGxHx2.d.ts → observable-embedder-CGr3Zejf.d.ts} +1 -1
  140. package/dist/{observable-llm-client-CLJLMmwA.d.ts → observable-llm-client-CXUA7Gi3.d.ts} +3 -3
  141. package/dist/{observable-memory-store-B48HV2NK.d.ts → observable-memory-store-BwwKcUzf.d.ts} +4 -4
  142. package/dist/{observable-retriever-CAdHncKh.d.ts → observable-retriever-BZpdv93Y.d.ts} +5 -5
  143. package/dist/paths-DmmPxD_v.d.ts +1 -0
  144. package/dist/{plugin-config-schema-VBSPGEG3.d.ts → plugin-config-schema-BrhMvnXJ.d.ts} +3 -1
  145. package/dist/{plugin-config-schema-hIfZTELX.js → plugin-config-schema-Di-uKTP0.js} +1 -1
  146. package/dist/{profile-CQNCtVIu.d.ts → profile-CF5oDjR7.d.ts} +1 -1
  147. package/dist/{profile-section-writer-joJEIYhF.js → profile-section-writer-CmNa95uN.js} +3 -3
  148. package/dist/{reflection-command-hooks-Cl0ShtbF.d.ts → reflection-command-hooks-CM2mWb69.d.ts} +4 -4
  149. package/dist/{reflection-command-hooks-CG8n1GNK.js → reflection-command-hooks-CnTj8cKL.js} +3 -3
  150. package/dist/{reflection-deps-B0_cPBZt.d.ts → reflection-deps-B5I0nYZS.d.ts} +2 -2
  151. package/dist/{reflection-embedded-generator-C5kA0T0v.d.ts → reflection-embedded-generator-CogQIBcG.d.ts} +3 -3
  152. package/dist/{reflection-embedded-generator-D342_5o_.js → reflection-embedded-generator-DR525Moy.js} +1 -1
  153. package/dist/{reflection-injection-hooks-CtgcjLGx.d.ts → reflection-injection-hooks-BnbfZqpj.d.ts} +4 -4
  154. package/dist/{reflection-lifecycle-hooks-Pd7P9wya.d.ts → reflection-lifecycle-hooks-DMDhtidj.d.ts} +3 -3
  155. package/dist/{rem-batch-executor-DsXWpJYU.js → rem-batch-executor-8x4FBLtW.js} +1 -1
  156. package/dist/{rem-trigger-DZaplx_E.js → rem-trigger-DmRERTNH.js} +1672 -1003
  157. package/dist/{retriever-ZoLvPRHq.js → retriever-BXQmJMh0.js} +16 -3
  158. package/dist/{retriever-DQdyGlkr.d.ts → retriever-BpxV-0fB.d.ts} +8 -8
  159. package/dist/{runtime-audit-log-BCeujlEp.js → runtime-audit-log-DK6xAYxh.js} +103 -2
  160. package/dist/{runtime-audit-log-Bn3JLBPj.d.ts → runtime-audit-log-DdhLlTxi.d.ts} +4 -2
  161. package/dist/{scopes-CSOn6zdd.js → scopes-a4Kly9n7.js} +1 -1
  162. package/dist/{server-Dw34jY1d.js → server-CCduS_Ea.js} +17 -320
  163. package/dist/sidecar/main.js +11 -4
  164. package/dist/sno-station-mem-runtime-dependencies-AdFFr3bU.d.ts +31 -0
  165. package/dist/{sno-station-mem-runtime-dependencies-DAMOKR9y.js → sno-station-mem-runtime-dependencies-CexFtaY6.js} +9 -9
  166. package/dist/{store-DKndKig1.d.ts → store-BDIAl2cc.d.ts} +1 -1
  167. package/dist/{store-CUk-jEGA.js → store-BQxkgJTX.js} +31 -23
  168. package/dist/{strategy-hook-runner-BSk_o6Yt.d.ts → strategy-hook-runner-DT9emgCZ.d.ts} +7 -7
  169. package/dist/{strategy-hook-runner-CWApiUZW.js → strategy-hook-runner-SvyyGBXw.js} +1 -1
  170. package/dist/{types-Del9J7UB.d.ts → types-Ae3a8562.d.ts} +1 -1
  171. package/dist/{utils-Ctb9aevJ.d.ts → utils-CDw2ttQf.d.ts} +2 -3
  172. package/package.json +2 -2
  173. package/settings.default.json +4 -4
  174. package/dist/memory-tool-dependencies-CJNppsKh.d.ts +0 -14
  175. package/dist/paths-D3dEsoE4.d.ts +0 -1
  176. package/dist/sno-station-mem-runtime-dependencies-8CRIkARK.d.ts +0 -31
  177. package/dist/{unplaced-candidate-migration-CbQHzcEM.js → unplaced-candidate-migration-BCcdQkcx.js} +1 -1
@@ -1,7 +1,7 @@
1
- import { $ as FIXED_IDX_MEMORY_ACTIVE_TASK_EVIDENCE_INSTANCE, $n as MAX_CHUNK_FETCH_LIMIT, $t as FIXED_VEC_MEMORY, At as FIXED_MEMORY_PROVIDER_AGENT_MAPPINGS, B as FIXED_EXTERNAL_VALUE_51, C as getSnoStationMemStateDir, Cn as CHUNK_TARGET_TOKENS, Cr as LOCAL_EMBEDDING_CACHE_DIR_DEFAULT, Ct as FIXED_MEMORY_CHUNKS_MEM_IDX, D as settingsToPluginConfig, Dn as DEFAULT_RECALL_LIFECYCLE, Dr as ensureModelDownloaded, Dt as FIXED_MEMORY_MEMORIES_FACT_ID_UPDATE_GUARD, En as DEFAULT_IMPORTANCE, Er as LocalEmbedProvider, Et as FIXED_MEMORY_MEMORIES_FACT_ID_INSERT_GUARD, Fn as EMBEDDER_DIM, Ft as FIXED_MEMORY_SNO_EXTRACT_CHAT, G as FIXED_EXTERNAL_VALUE_56, Gt as FIXED_PROTOCOL_VALUE_69, H as FIXED_EXTERNAL_VALUE_53, Hn as HEAD_EXTRACT_OVERLAP_DROP_RATIO, In as EMBEDDER_MODEL_DEFAULT, J as FIXED_EXTERNAL_VALUE_62, Jn as LIGHTWEIGHT_FUSION_WEIGHT, K as FIXED_EXTERNAL_VALUE_57, Kn as INTRA_MEMORY_COSINE_THRESHOLD, Kt as FIXED_PROTOCOL_VALUE_73, Ln as EMBEDDER_PROVIDER_DEFAULT, Lt as FIXED_MEMORY_TASK_LIFECYCLE_COMMANDS, Mn as DEFAULT_RERANK_TIMEOUT_MS, Mt as FIXED_MEMORY_PROVIDER_PROJECT_MAPPINGS, N as observeAgentId, O as MODEL_CALLS, Ot as FIXED_MEMORY_MIGRATION_MARKERS, Pn as DEFAULT_VECTOR_WEIGHT, Pr as REM_SIDECAR_TOKEN_HEADER, Q as FIXED_IDX_MEMORY_ACTIVE_TASK_CURRENT_REVISION, Rn as EVAL_QA_TRACES_FILENAME, Rt as FIXED_MEMORY_UNPLACED_CANDIDATES, Sn as CHUNK_OVERLAP_TOKENS, Sr as CachedEmbeddingProvider, St as FIXED_MEMORY_CHUNKS_MEM_FACET_IDX, T as readSettings, Tn as DEFAULT_BM25_WEIGHT, Tt as FIXED_MEMORY_MEMORIES, U as FIXED_EXTERNAL_VALUE_54, Un as HEAD_EXTRACT_TOKEN_BUDGET, V as FIXED_EXTERNAL_VALUE_52, Vn as HEAD_EXTRACT_MIN_CONTENT_TOKENS, W as FIXED_EXTERNAL_VALUE_55, Wn as IMPORTANCE_WEIGHT_BASE, X as FIXED_EXTERNAL_VALUE_9, Y as FIXED_EXTERNAL_VALUE_8, Yn as LIGHTWEIGHT_RERANK_PENALTY, Z as FIXED_IDX_MEMORY, _n as BONUS_TOTAL_INVARIANT, _t as FIXED_MEMORY_CHUNKS_AD, a as REM_MODEL_OUTPUT_TOKEN_CAP, ar as MULTI_HIT_BONUS_CAP, at as FIXED_IDX_MEMORY_MEMORIES_PROJECT, bn as CHUNK_MAX_TOKENS, br as experimentMmrDisabled, bt as FIXED_MEMORY_CHUNKS_FTS, cr as RECENCY_WEIGHT, ct as FIXED_IDX_MEMORY_MEMORIES_PROJECT_TIMESTAMP, d as getRemDiscoveryPath, dr as RERANK_BLEND_CROSS, dt as FIXED_MEMORY_ACTIVE_TASK_INSTANCES, en as FIXED_VEC_MEMORY_CHUNKS, er as MAX_LIST_LIMIT, et as FIXED_IDX_MEMORY_ACTIVE_TASK_INSTANCES_PROJECTION, fr as RERANK_BLEND_VECTOR, ft as FIXED_MEMORY_ACTIVE_TASK_MIGRATION_EVIDENCE, g as readRemOperationalConfig, gt as FIXED_MEMORY_CHUNKS, hr as TIME_DECAY_FLOOR, ht as FIXED_MEMORY_ACTIVE_TASK_TRANSITIONS, in as PERSISTED_PROVIDER_SYSTEM, ir as MMR_LAMBDA, it as FIXED_IDX_MEMORY_MEMORIES_LANE_PROJECT, jn as DEFAULT_RERANK_MODEL, jt as FIXED_MEMORY_PROVIDER_PROJECT_AGENTS, k as modelCallDestination, kt as FIXED_MEMORY_PROFILE_RECOVERY_ENTRIES, ln as ADJACENCY_BONUS_CAP, lt as FIXED_MEMORY, mt as FIXED_MEMORY_ACTIVE_TASK_REVISIONS, nn as PERSISTED_CONTENT_HASH_V1, nt as FIXED_IDX_MEMORY_MEMORIES_CONTENT_HASH, o as REM_RUN_PATH, on as llmRoutingConfigSchema, or as MULTI_HIT_BONUS_PER_HIT, ot as FIXED_IDX_MEMORY_MEMORIES_PROJECT_CONTENT_HASH, pr as RETRIEVAL_STORAGE_CHUNK_PROFILE, pt as FIXED_MEMORY_ACTIVE_TASK_MIGRATION_MANIFESTS, q as FIXED_EXTERNAL_VALUE_58, qn as LIGHTWEIGHT_COSINE_WEIGHT, r as REM_CORRELATION_ID_HEADER, rn as PERSISTED_CONTENT_HASH_V3, rt as FIXED_IDX_MEMORY_MEMORIES_FACT_ID, s as REM_SIDECAR_HOST, sn as SUPPORTED_LOCALES, st as FIXED_IDX_MEMORY_MEMORIES_PROJECT_FACT_KEY_ACTIVE, tn as PERSISTED_CANDIDATE_HASH_V1, tt as FIXED_IDX_MEMORY_MEMORIES_CATEGORY, un as ADJACENCY_BONUS_PER_PAIR, ut as FIXED_MEMORY_ACTIVE_TASK_EVIDENCE, vn as CACHE_TTL_MS, vt as FIXED_MEMORY_CHUNKS_AI, wn as CHUNK_TOKENIZER_MODE, wr as LOCAL_EMBEDDING_MODEL, wt as FIXED_MEMORY_EXTRACTION_TIMESTAMPS, xn as CHUNK_MIN_TOKENS, xt as FIXED_MEMORY_CHUNKS_MEMORY_ID_IDX, yn as CHUNKING_VERSION$1, yr as experimentCandidatePoolOverride, yt as FIXED_MEMORY_CHUNKS_AU, z as FIXED_EXTERNAL_VALUE_49 } from "./config-BCZRhUvW.js";
1
+ import { $ as FIXED_IDX_MEMORY_ACTIVE_TASK_EVIDENCE_INSTANCE, $n as MAX_CHUNK_FETCH_LIMIT, $t as FIXED_VEC_MEMORY, At as FIXED_MEMORY_PROVIDER_AGENT_MAPPINGS, B as FIXED_EXTERNAL_VALUE_51, C as getSnoStationMemStateDir, Cn as CHUNK_TARGET_TOKENS, Cr as LOCAL_EMBEDDING_CACHE_DIR_DEFAULT, Ct as FIXED_MEMORY_CHUNKS_MEM_IDX, D as settingsToPluginConfig, Dn as DEFAULT_RECALL_LIFECYCLE, Dr as ensureModelDownloaded, Dt as FIXED_MEMORY_MEMORIES_FACT_ID_UPDATE_GUARD, En as DEFAULT_IMPORTANCE, Er as LocalEmbedProvider, Et as FIXED_MEMORY_MEMORIES_FACT_ID_INSERT_GUARD, Fn as EMBEDDER_DIM, Ft as FIXED_MEMORY_SNO_EXTRACT_CHAT, G as FIXED_EXTERNAL_VALUE_56, Gt as FIXED_PROTOCOL_VALUE_69, H as FIXED_EXTERNAL_VALUE_53, Hn as HEAD_EXTRACT_OVERLAP_DROP_RATIO, In as EMBEDDER_MODEL_DEFAULT, J as FIXED_EXTERNAL_VALUE_62, Jn as LIGHTWEIGHT_FUSION_WEIGHT, K as FIXED_EXTERNAL_VALUE_57, Kn as INTRA_MEMORY_COSINE_THRESHOLD, Kt as FIXED_PROTOCOL_VALUE_73, Ln as EMBEDDER_PROVIDER_DEFAULT, Lt as FIXED_MEMORY_TASK_LIFECYCLE_COMMANDS, Mn as DEFAULT_RERANK_TIMEOUT_MS, Mt as FIXED_MEMORY_PROVIDER_PROJECT_MAPPINGS, N as observeAgentId, O as MODEL_CALLS, Ot as FIXED_MEMORY_MIGRATION_MARKERS, Pn as DEFAULT_VECTOR_WEIGHT, Pr as REM_SIDECAR_TOKEN_HEADER, Q as FIXED_IDX_MEMORY_ACTIVE_TASK_CURRENT_REVISION, Rn as EVAL_QA_TRACES_FILENAME, Rt as FIXED_MEMORY_UNPLACED_CANDIDATES, Sn as CHUNK_OVERLAP_TOKENS, Sr as CachedEmbeddingProvider, St as FIXED_MEMORY_CHUNKS_MEM_FACET_IDX, T as readSettings, Tn as DEFAULT_BM25_WEIGHT, Tt as FIXED_MEMORY_MEMORIES, U as FIXED_EXTERNAL_VALUE_54, Un as HEAD_EXTRACT_TOKEN_BUDGET, V as FIXED_EXTERNAL_VALUE_52, Vn as HEAD_EXTRACT_MIN_CONTENT_TOKENS, W as FIXED_EXTERNAL_VALUE_55, Wn as IMPORTANCE_WEIGHT_BASE, X as FIXED_EXTERNAL_VALUE_9, Y as FIXED_EXTERNAL_VALUE_8, Yn as LIGHTWEIGHT_RERANK_PENALTY, Z as FIXED_IDX_MEMORY, _n as BONUS_TOTAL_INVARIANT, _t as FIXED_MEMORY_CHUNKS_AD, a as REM_MODEL_OUTPUT_TOKEN_CAP, ar as MULTI_HIT_BONUS_CAP, at as FIXED_IDX_MEMORY_MEMORIES_PROJECT, bn as CHUNK_MAX_TOKENS, br as experimentMmrDisabled, bt as FIXED_MEMORY_CHUNKS_FTS, cr as RECENCY_WEIGHT, ct as FIXED_IDX_MEMORY_MEMORIES_PROJECT_TIMESTAMP, d as getRemDiscoveryPath, dr as RERANK_BLEND_CROSS, dt as FIXED_MEMORY_ACTIVE_TASK_INSTANCES, en as FIXED_VEC_MEMORY_CHUNKS, er as MAX_LIST_LIMIT, et as FIXED_IDX_MEMORY_ACTIVE_TASK_INSTANCES_PROJECTION, fr as RERANK_BLEND_VECTOR, ft as FIXED_MEMORY_ACTIVE_TASK_MIGRATION_EVIDENCE, g as readRemOperationalConfig, gt as FIXED_MEMORY_CHUNKS, hr as TIME_DECAY_FLOOR, ht as FIXED_MEMORY_ACTIVE_TASK_TRANSITIONS, in as PERSISTED_PROVIDER_SYSTEM, ir as MMR_LAMBDA, it as FIXED_IDX_MEMORY_MEMORIES_LANE_PROJECT, jn as DEFAULT_RERANK_MODEL, jt as FIXED_MEMORY_PROVIDER_PROJECT_AGENTS, k as modelCallDestination, kt as FIXED_MEMORY_PROFILE_RECOVERY_ENTRIES, ln as ADJACENCY_BONUS_CAP, lt as FIXED_MEMORY, mt as FIXED_MEMORY_ACTIVE_TASK_REVISIONS, nn as PERSISTED_CONTENT_HASH_V1, nt as FIXED_IDX_MEMORY_MEMORIES_CONTENT_HASH, o as REM_RUN_PATH, on as llmRoutingConfigSchema, or as MULTI_HIT_BONUS_PER_HIT, ot as FIXED_IDX_MEMORY_MEMORIES_PROJECT_CONTENT_HASH, pr as RETRIEVAL_STORAGE_CHUNK_PROFILE, pt as FIXED_MEMORY_ACTIVE_TASK_MIGRATION_MANIFESTS, q as FIXED_EXTERNAL_VALUE_58, qn as LIGHTWEIGHT_COSINE_WEIGHT, r as REM_CORRELATION_ID_HEADER, rn as PERSISTED_CONTENT_HASH_V3, rt as FIXED_IDX_MEMORY_MEMORIES_FACT_ID, s as REM_SIDECAR_HOST, sn as SUPPORTED_LOCALES, st as FIXED_IDX_MEMORY_MEMORIES_PROJECT_FACT_KEY_ACTIVE, tn as PERSISTED_CANDIDATE_HASH_V1, tt as FIXED_IDX_MEMORY_MEMORIES_CATEGORY, un as ADJACENCY_BONUS_PER_PAIR, ut as FIXED_MEMORY_ACTIVE_TASK_EVIDENCE, vn as CACHE_TTL_MS, vt as FIXED_MEMORY_CHUNKS_AI, wn as CHUNK_TOKENIZER_MODE, wr as LOCAL_EMBEDDING_MODEL, wt as FIXED_MEMORY_EXTRACTION_TIMESTAMPS, xn as CHUNK_MIN_TOKENS, xt as FIXED_MEMORY_CHUNKS_MEMORY_ID_IDX, yn as CHUNKING_VERSION$1, yr as experimentCandidatePoolOverride, yt as FIXED_MEMORY_CHUNKS_AU, z as FIXED_EXTERNAL_VALUE_49 } from "./config-C7LzSWhG.js";
2
2
  import { createLogger, createLogger as createLogger$1, currentLogContext, privateLogReference, withLogContext } from "@snoai/utils/logger";
3
3
  import path, { dirname, join, resolve } from "node:path";
4
- import { appendFileSync, createReadStream, existsSync, mkdirSync, readFileSync, realpathSync, writeFileSync } from "node:fs";
4
+ import { appendFileSync, closeSync, createReadStream, existsSync, fstatSync, fsyncSync, ftruncateSync, mkdirSync, openSync, readFileSync, readSync, realpathSync, renameSync, rmSync, statSync, writeFileSync, writeSync } from "node:fs";
5
5
  import { arch, availableParallelism, platform } from "node:os";
6
6
  import { fileURLToPath } from "node:url";
7
7
  import { LRUCache } from "lru-cache";
@@ -13,10 +13,11 @@ import { getDek, openEncryptedDb, openEncryptedDbReadonly } from "@snoai/sqlite-
13
13
  import { appendFile, mkdir, open, readFile, rename, rm } from "node:fs/promises";
14
14
  import { Temporal } from "@js-temporal/polyfill";
15
15
  import { redactForStorage, sanitizeContentIngress, sanitizeStructuredJsonForStorage } from "@snoai/content-sanitizer";
16
+ import { createInterface } from "node:readline";
17
+ import { StringDecoder } from "node:string_decoder";
16
18
  import { CallPipeline, ConfigRegistryManager, KeyPool, loadLlmixTrustManifest, registryRootOptionsFromTrustManifest } from "@snoai/llmix";
17
19
  import { Buffer as Buffer$1 } from "node:buffer";
18
20
  import { Mutex } from "async-mutex";
19
- import { createInterface } from "node:readline";
20
21
  import { drizzle } from "drizzle-orm/better-sqlite3";
21
22
  import { migrate } from "drizzle-orm/better-sqlite3/migrator";
22
23
  import { index, integer, primaryKey, real, sqliteTable, text } from "drizzle-orm/sqlite-core";
@@ -37,7 +38,7 @@ var __exportAll = (all, no_symbols) => {
37
38
  };
38
39
  //#endregion
39
40
  //#region src/engine/telemetry/observe-ledger.ts
40
- const log$23 = createLogger("sno-station-mem:observe-ledger");
41
+ const log$24 = createLogger("sno-station-mem:observe-ledger");
41
42
  const rowSchema = z.object({
42
43
  ts_ms: z.number().int().nonnegative(),
43
44
  agent_id: z.string().transform(observeAgentId).optional(),
@@ -54,7 +55,7 @@ function observeLedgerSyncedPath(profileDir) {
54
55
  }
55
56
  function warn(observe, message) {
56
57
  if (observe.logger) observe.logger.warn(message);
57
- else log$23.warn(message, void 0, {
58
+ else log$24.warn(message, void 0, {
58
59
  event_name: "observe.ledger.failed",
59
60
  file: "packages/memory/src/engine/telemetry/observe-ledger.ts",
60
61
  function: "forwardObserveLedger",
@@ -79,25 +80,52 @@ function consumeRows(profileDir, observe, batchSize) {
79
80
  rows,
80
81
  offset
81
82
  };
82
- const ledger = readFileSync(path);
83
- if (offset > ledger.length) {
84
- warn(observe, `offset beyond ledger: ${offset} > ${ledger.length}`);
85
- return {
86
- rows,
87
- offset
88
- };
89
- }
90
- while (rows.length < batchSize) {
91
- const newline = ledger.indexOf(10, offset);
92
- if (newline === -1) break;
93
- rows.push({
94
- start: offset,
95
- end: newline + 1,
96
- text: ledger.toString("utf8", offset, newline)
97
- });
98
- offset = newline + 1;
83
+ const fd = openSync(path, "r+");
84
+ try {
85
+ const size = fstatSync(fd).size;
86
+ if (offset > size) {
87
+ warn(observe, `offset beyond ledger: ${offset} > ${size}`);
88
+ offset = 0;
89
+ writeFileSync(syncedPath, "0", { mode: 384 });
90
+ }
91
+ const buffer = Buffer.alloc(65536);
92
+ let position = offset;
93
+ let pending = Buffer.alloc(0);
94
+ while (rows.length < batchSize && position < size) {
95
+ const count = readSync(fd, buffer, 0, Math.min(buffer.length, size - position), position);
96
+ if (count === 0) break;
97
+ position += count;
98
+ pending = Buffer.concat([pending, buffer.subarray(0, count)]);
99
+ let consumed = 0;
100
+ while (rows.length < batchSize) {
101
+ const newline = pending.indexOf(10, consumed);
102
+ if (newline === -1) break;
103
+ const end = offset + newline - consumed + 1;
104
+ rows.push({
105
+ start: offset,
106
+ end,
107
+ text: pending.toString("utf8", consumed, newline)
108
+ });
109
+ offset = end;
110
+ consumed = newline + 1;
111
+ }
112
+ pending = pending.subarray(consumed);
113
+ }
114
+ if (offset >= 1048576 && size - offset <= 65536) {
115
+ let source = offset;
116
+ for (let end = fstatSync(fd).size; source < end; end = fstatSync(fd).size) while (source < end) {
117
+ const count = readSync(fd, buffer, 0, Math.min(buffer.length, end - source), source);
118
+ if (count === 0) break;
119
+ writeSync(fd, buffer, 0, count, source - offset);
120
+ source += count;
121
+ }
122
+ ftruncateSync(fd, source - offset);
123
+ offset = 0;
124
+ }
125
+ if (rows.length) writeFileSync(syncedPath, String(offset), { mode: 384 });
126
+ } finally {
127
+ closeSync(fd);
99
128
  }
100
- if (rows.length) writeFileSync(syncedPath, String(offset), { mode: 384 });
101
129
  return {
102
130
  rows,
103
131
  offset
@@ -141,7 +169,7 @@ async function forwardObserveLedgerUntilIdle(options) {
141
169
  //#endregion
142
170
  //#region src/engine/operation-cancellation.ts
143
171
  const operations = new AsyncLocalStorage();
144
- const log$22 = createLogger("sno-station-mem:cancellation");
172
+ const log$23 = createLogger("sno-station-mem:cancellation");
145
173
  /** Carries cancellation through the existing nested extraction and reflection writers. */
146
174
  async function withMemoryOperation(method, signal, run) {
147
175
  const operation = {
@@ -157,7 +185,7 @@ async function withMemoryOperation(method, signal, run) {
157
185
  signal?.throwIfAborted();
158
186
  return result;
159
187
  } finally {
160
- if (signal?.aborted) log$22.error("memory.operation.aborted", {
188
+ if (signal?.aborted) log$23.error("memory.operation.aborted", {
161
189
  method,
162
190
  outcome: "aborted",
163
191
  writes: operation.writes
@@ -5828,6 +5856,18 @@ function bytesToF32(bytes) {
5828
5856
  function stableHash(text) {
5829
5857
  return createHash("sha256").update(text).digest("hex");
5830
5858
  }
5859
+ /**
5860
+ * Return a short, secret-redacted, whitespace-collapsed preview of `text` for
5861
+ * audit logs. Previews are enabled by default and pass through `redactSecrets`
5862
+ * before they are persisted in audit.jsonl on disk.
5863
+ */
5864
+ function debugContentPreview(text, enabled, maxChars = 80) {
5865
+ if (!enabled) return void 0;
5866
+ if (!text) return void 0;
5867
+ const collapsed = redactSecrets(text).replace(/\s+/g, " ").trim();
5868
+ if (collapsed.length === 0) return void 0;
5869
+ return collapsed.length > maxChars ? `${collapsed.slice(0, maxChars)}...` : collapsed;
5870
+ }
5831
5871
  z.object({ source: z.object({ commit: z.string().min(1) }) });
5832
5872
  //#endregion
5833
5873
  //#region config/skin-defaults.ts
@@ -5847,165 +5887,675 @@ const CODING_SKIN_CHILD_DEADLINE_MS = 11e4;
5847
5887
  const CODING_SKIN_SESSION_QUERY = "standing decisions, open tasks, conventions and known pitfalls for this repository";
5848
5888
  const CODING_SKIN_LEDGER_MAX_CHARS = 12e3;
5849
5889
  //#endregion
5850
- //#region src/model/llm-failure.ts
5851
- const CREDENTIAL_CATEGORIES = /* @__PURE__ */ new Set([
5852
- "auth",
5853
- "credential-expired",
5854
- "credential-revoked"
5855
- ]);
5856
- const TERMINAL_CATEGORIES = /* @__PURE__ */ new Set([...CREDENTIAL_CATEGORIES, "exhausted"]);
5857
- function isCredentialRevokedMessage(message) {
5858
- return /oauth token refresh failed for openai:.*\binvalid_grant\b.*\btoken revoked\b/i.test(message);
5890
+ //#region src/engine/operations/jsonl-lines.ts
5891
+ const log$22 = createLogger("sno-station-mem:jsonl");
5892
+ /** Preserve complete records without materializing an unbounded log as one string. */
5893
+ async function* readJsonlLines(file) {
5894
+ const stream = createReadStream(file, { encoding: "utf8" });
5895
+ const lines = createInterface({
5896
+ input: stream,
5897
+ crlfDelay: Infinity
5898
+ });
5899
+ try {
5900
+ for await (const line of lines) if (line.trim()) yield line;
5901
+ } catch (error) {
5902
+ if (error instanceof Error && "code" in error && error.code === "ENOENT") return;
5903
+ log$22.error("jsonl.read.failed", {
5904
+ file,
5905
+ error
5906
+ }, {
5907
+ event_name: "memory.jsonl.read.failed",
5908
+ file: "packages/memory/src/engine/operations/jsonl-lines.ts",
5909
+ function: "readJsonlLines",
5910
+ site_id: "memory.jsonl.read.failed"
5911
+ });
5912
+ } finally {
5913
+ lines.close();
5914
+ stream.destroy();
5915
+ }
5859
5916
  }
5860
- function isCredentialExpiredMessage(message) {
5861
- return /\bexpired access token\b/i.test(message);
5917
+ //#endregion
5918
+ //#region src/engine/operations/runtime-audit-log.ts
5919
+ /** @file runtime-audit-log.ts
5920
+ * @purpose Records audit events for operational visibility and safety-sensitive actions.
5921
+ * @boundary Plugin runtime events and filesystem-backed audit persistence.
5922
+ * @see sno-station-mem-plugin-runtime.ts, memory-tool-registration.ts, errors.ts.
5923
+ */
5924
+ const auditWriteQueues = /* @__PURE__ */ new Map();
5925
+ const memoryAuditScope = new AsyncLocalStorage();
5926
+ const mutationAttemptScope = new AsyncLocalStorage();
5927
+ const mutationRecoveryPromises = /* @__PURE__ */ new Map();
5928
+ const MEMORY_AUDIT_COMMON_KEYS = [
5929
+ "operation",
5930
+ "audit_phase",
5931
+ "audit_operation_id"
5932
+ ];
5933
+ const MUTATION_AUDIT_DETAIL_KEYS = [
5934
+ "mutation_writer",
5935
+ "mutation_subject",
5936
+ "mutation_outcome",
5937
+ "refusal_reason"
5938
+ ];
5939
+ const MEMORY_AUDIT_ALLOWED_DETAIL_KEYS = {
5940
+ memory_injected: /* @__PURE__ */ new Set([
5941
+ ...MEMORY_AUDIT_COMMON_KEYS,
5942
+ "scope",
5943
+ "category",
5944
+ "lane",
5945
+ "count",
5946
+ "memory_ids",
5947
+ "write_outcome",
5948
+ "created_count",
5949
+ "existing_count"
5950
+ ]),
5951
+ memory_searched: /* @__PURE__ */ new Set([
5952
+ ...MEMORY_AUDIT_COMMON_KEYS,
5953
+ "scope",
5954
+ "query_kind",
5955
+ "requested_count",
5956
+ "result_count",
5957
+ "memory_ids"
5958
+ ]),
5959
+ memory_read: /* @__PURE__ */ new Set([
5960
+ ...MEMORY_AUDIT_COMMON_KEYS,
5961
+ "scope",
5962
+ "requested_count",
5963
+ "result_count",
5964
+ "memory_ids",
5965
+ "requested_fact_id",
5966
+ "resolved_fact_id",
5967
+ "fact_ids",
5968
+ "found",
5969
+ "outcome"
5970
+ ]),
5971
+ memory_updated: /* @__PURE__ */ new Set([
5972
+ ...MEMORY_AUDIT_COMMON_KEYS,
5973
+ ...MUTATION_AUDIT_DETAIL_KEYS,
5974
+ "scope",
5975
+ "requested_count",
5976
+ "result_count",
5977
+ "memory_ids",
5978
+ "outcome",
5979
+ "migration_status",
5980
+ "rewritten_rows",
5981
+ "checked_rows"
5982
+ ]),
5983
+ memory_superseded: /* @__PURE__ */ new Set([
5984
+ ...MEMORY_AUDIT_COMMON_KEYS,
5985
+ ...MUTATION_AUDIT_DETAIL_KEYS,
5986
+ "scope",
5987
+ "replacement_memory_id",
5988
+ "closed_memory_ids",
5989
+ "write_outcome"
5990
+ ]),
5991
+ memory_deleted: /* @__PURE__ */ new Set([
5992
+ ...MEMORY_AUDIT_COMMON_KEYS,
5993
+ "scope",
5994
+ "deleted_memory_ids",
5995
+ "delete_reason",
5996
+ "count",
5997
+ "outcome",
5998
+ "removed_files"
5999
+ ]),
6000
+ memory_purged: /* @__PURE__ */ new Set([
6001
+ ...MEMORY_AUDIT_COMMON_KEYS,
6002
+ "target_fact_id",
6003
+ "purged_fact_ids",
6004
+ "failed_fact_ids",
6005
+ "status",
6006
+ "blocked_reason"
6007
+ ])
6008
+ };
6009
+ /** Implements enqueue audit write as the local audit log state operation. */
6010
+ function enqueueAuditWrite(auditPath, line) {
6011
+ const next = (auditWriteQueues.get(auditPath) ?? Promise.resolve(true)).catch(() => void 0).then(async () => {
6012
+ mkdirSync(path.dirname(auditPath), { recursive: true });
6013
+ await appendFile(auditPath, line);
6014
+ compactAuditLog(auditPath);
6015
+ return true;
6016
+ }).catch((error) => {
6017
+ createLogger("sno-station-mem:audit").error("Audit entry append failed", { error }, {
6018
+ event_name: "memory.audit.append.failed",
6019
+ file: "packages/memory/src/engine/operations/runtime-audit-log.ts",
6020
+ function: "enqueueAuditWrite",
6021
+ site_id: "memory.audit.append.failed"
6022
+ });
6023
+ return false;
6024
+ });
6025
+ auditWriteQueues.set(auditPath, next);
6026
+ next.finally(() => {
6027
+ if (auditWriteQueues.get(auditPath) === next) auditWriteQueues.delete(auditPath);
6028
+ });
6029
+ return next;
5862
6030
  }
5863
- function isAuthMessage(message) {
5864
- return /\b(?:401|403)\b/.test(message) || /unauthori[sz]ed/i.test(message) || /no api key found/i.test(message) || /(?:invalid|incorrect) api[- ]?key/i.test(message) || /(?:invalid|expired) (?:access |oauth )?token/i.test(message) || /(?:api[- ]?key|token).*(?:invalid|expired|rejected)/i.test(message) || /credential (?:is )?(?:invalid|expired|rejected)/i.test(message) || /no (?:valid )?credential (?:found|available|configured)/i.test(message) || /re-?authenticate/i.test(message) || /no authentication configured/i.test(message);
6031
+ /** Returns audit path from audit log state state without side effects. */
6032
+ function getAuditPath(stateDir) {
6033
+ return path.join(stateDir, "audit.jsonl");
5865
6034
  }
5866
- function isExhaustedMessage(message) {
5867
- return /you(?:'|’)ve reached your codex subscription usage limit/i.test(message);
6035
+ /** Retain the recent tail and two days of REM replay evidence when the audit exceeds 16 MiB. */
6036
+ function compactAuditLog(auditPath) {
6037
+ try {
6038
+ compactAuditFile(auditPath);
6039
+ } catch (error) {
6040
+ createLogger("sno-station-mem:audit").error("Audit compaction failed; existing records retained", { error }, {
6041
+ event_name: "memory.audit.compact.failed",
6042
+ file: "packages/memory/src/engine/operations/runtime-audit-log.ts",
6043
+ function: "compactAuditLog",
6044
+ site_id: "memory.audit.compact.failed"
6045
+ });
6046
+ }
5868
6047
  }
5869
- function isThrottleMessage(message) {
5870
- return /\b429\b/.test(message) || /rate.?limit/i.test(message) || /quota/i.test(message);
6048
+ function compactAuditFile(auditPath) {
6049
+ if (!existsSync(auditPath) || statSync(auditPath).size <= 16777216) return;
6050
+ const fd = openSync(auditPath, "r");
6051
+ const retained = [];
6052
+ const openAttempts = /* @__PURE__ */ new Map();
6053
+ let tail;
6054
+ let size = 0;
6055
+ try {
6056
+ size = statSync(auditPath).size;
6057
+ const tailStart = Math.max(0, size - 8388608);
6058
+ tail = Buffer.alloc(size - tailStart);
6059
+ const tailRead = readSync(fd, tail, 0, tail.length, tailStart);
6060
+ tail = tail.subarray(0, tailRead);
6061
+ const newline = tail.indexOf(10);
6062
+ const prefixEnd = newline < 0 ? size : tailStart + newline + 1;
6063
+ tail = newline < 0 ? Buffer.alloc(0) : tail.subarray(newline + 1);
6064
+ const cutoff = (/* @__PURE__ */ new Date(Date.now() - 1728e5)).toISOString();
6065
+ const buffer = Buffer.alloc(65536);
6066
+ let position = 0;
6067
+ let pending = "";
6068
+ let linePosition = 0;
6069
+ const decoder = new StringDecoder("utf8");
6070
+ let oversized = false;
6071
+ while (position < prefixEnd) {
6072
+ const count = readSync(fd, buffer, 0, Math.min(buffer.length, prefixEnd - position), position);
6073
+ if (count === 0) break;
6074
+ position += count;
6075
+ const parts = (pending + decoder.write(buffer.subarray(0, count))).split("\n");
6076
+ pending = parts.pop() ?? "";
6077
+ for (const line of parts) {
6078
+ if (oversized) {
6079
+ oversized = false;
6080
+ linePosition++;
6081
+ continue;
6082
+ }
6083
+ try {
6084
+ const entry = JSON.parse(line);
6085
+ if (isRecord$1(entry) && typeof entry["event"] === "string" && entry["event"].startsWith("rem_") && typeof entry["timestamp"] === "string" && entry["timestamp"] >= cutoff && (entry["event"] !== "rem_trigger_evaluated" || isRecord$1(entry["details"]) && entry["details"]["row"] === "dispatch")) retained.push({
6086
+ position: linePosition,
6087
+ line: line + "\n"
6088
+ });
6089
+ if (isRecord$1(entry) && isRecord$1(entry["details"])) {
6090
+ const id = entry["details"]["audit_operation_id"];
6091
+ if (typeof id === "string" && entry["decision"] === "mutation-attempt-open") openAttempts.set(id, {
6092
+ position: linePosition,
6093
+ line: line + "\n"
6094
+ });
6095
+ if (typeof id === "string" && entry["details"]["mutation_outcome"] !== void 0) openAttempts.delete(id);
6096
+ }
6097
+ } catch {}
6098
+ linePosition++;
6099
+ }
6100
+ if (pending.length > 1048576 || oversized) {
6101
+ pending = "";
6102
+ oversized = true;
6103
+ }
6104
+ }
6105
+ } finally {
6106
+ closeSync(fd);
6107
+ }
6108
+ const temporary = `${auditPath}.${randomUUID()}.tmp`;
6109
+ try {
6110
+ writeFileSync(temporary, [...retained, ...openAttempts.values()].sort((a, b) => a.position - b.position).map((record) => record.line).join(""), { mode: 384 });
6111
+ appendFileSync(temporary, tail);
6112
+ const output = openSync(temporary, "r+");
6113
+ try {
6114
+ fsyncSync(output);
6115
+ } finally {
6116
+ closeSync(output);
6117
+ }
6118
+ const grown = statSync(auditPath).size;
6119
+ if (grown > size) {
6120
+ const late = Buffer.alloc(grown - size);
6121
+ const source = openSync(auditPath, "r");
6122
+ try {
6123
+ appendFileSync(temporary, late.subarray(0, readSync(source, late, 0, late.length, size)));
6124
+ } finally {
6125
+ closeSync(source);
6126
+ }
6127
+ }
6128
+ renameSync(temporary, auditPath);
6129
+ } finally {
6130
+ rmSync(temporary, { force: true });
6131
+ }
5871
6132
  }
5872
- function categoryOf(status, message) {
5873
- if (isCredentialRevokedMessage(message)) return "credential-revoked";
5874
- if (isCredentialExpiredMessage(message)) return "credential-expired";
5875
- if (status === 401 || status === 403 || isAuthMessage(message)) return "auth";
5876
- if (isExhaustedMessage(message)) return "exhausted";
5877
- if (status === 429 || isThrottleMessage(message)) return "throttle";
5878
- if (status !== void 0 && (status === 404 || status === 408 || status >= 500)) return "transport";
5879
- return "unknown";
6133
+ function isRecord$1(value) {
6134
+ return typeof value === "object" && value !== null;
5880
6135
  }
5881
- /** Classifies one failed model call from whatever the transport surfaced: a status, a message, or both. */
5882
- function classifyLlmFailure(input) {
5883
- const category = categoryOf(input.status, input.message ?? "");
5884
- const terminal = TERMINAL_CATEGORIES.has(category);
6136
+ function isMemoryAuditEvent(event) {
6137
+ return event.startsWith("memory_");
6138
+ }
6139
+ function redactAuditValue(value) {
6140
+ if (typeof value === "string") return redactSecrets(value);
6141
+ if (Array.isArray(value)) return value.map(redactAuditValue);
6142
+ if (!isRecord$1(value)) return value;
6143
+ const redacted = {};
6144
+ for (const [key, child] of Object.entries(value)) redacted[key] = redactAuditValue(child);
6145
+ return redacted;
6146
+ }
6147
+ function prepareAuditEntry(entry) {
6148
+ if (!isMemoryAuditEvent(entry.event) || entry.details?.["audit_phase"] === void 0) return entry;
6149
+ const allowed = MEMORY_AUDIT_ALLOWED_DETAIL_KEYS[entry.event];
6150
+ const details = {};
6151
+ for (const [key, value] of Object.entries(entry.details)) if (allowed.has(key)) details[key] = value;
6152
+ else createLogger("sno-station-mem:audit").error("memory.audit.detail.unknown", {
6153
+ event: entry.event,
6154
+ key
6155
+ }, {
6156
+ event_name: "memory.audit.detail.unknown",
6157
+ file: "packages/memory/src/engine/operations/runtime-audit-log.ts",
6158
+ function: "prepareAuditEntry",
6159
+ site_id: "memory.audit.detail.unknown"
6160
+ });
5885
6161
  return {
5886
- category,
5887
- terminal,
5888
- endpointRefused: terminal || input.status === 404 || input.status === 503
6162
+ ...entry,
6163
+ ...entry.scope === void 0 ? {} : { scope: redactSecrets(entry.scope) },
6164
+ details: redactAuditValue(details)
5889
6165
  };
5890
6166
  }
5891
- function isCredentialFailure(category) {
5892
- return CREDENTIAL_CATEGORIES.has(category);
6167
+ /** Persists audit entry through the single audit log state write path. */
6168
+ function appendAuditEntry(stateDir, entry) {
6169
+ const auditPath = getAuditPath(stateDir);
6170
+ const prepared = prepareAuditEntry(entry);
6171
+ enqueueAuditWrite(auditPath, `${JSON.stringify({
6172
+ ...prepared,
6173
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
6174
+ })}\n`);
5893
6175
  }
5894
- function isTerminalLlmFailure$1(category) {
5895
- return TERMINAL_CATEGORIES.has(category);
6176
+ /** Waits for the audit append; failures are logged without blocking the operation. */
6177
+ async function appendAuditEntryStrict(stateDir, entry) {
6178
+ const auditPath = getAuditPath(stateDir);
6179
+ const prepared = prepareAuditEntry(entry);
6180
+ return enqueueAuditWrite(auditPath, `${JSON.stringify({
6181
+ ...prepared,
6182
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
6183
+ })}\n`);
5896
6184
  }
5897
- /** A status worth one more attempt against the same endpoint. */
5898
- function isTransientLlmStatus(status) {
5899
- return status === 408 || status === 429 || status >= 500;
6185
+ /** Attempts a synchronous audit append and logs a failure. */
6186
+ function appendAuditEntrySync(stateDir, entry) {
6187
+ const auditPath = getAuditPath(stateDir);
6188
+ const prepared = prepareAuditEntry(entry);
6189
+ try {
6190
+ mkdirSync(path.dirname(auditPath), { recursive: true });
6191
+ appendFileSync(auditPath, `${JSON.stringify({
6192
+ ...prepared,
6193
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
6194
+ })}\n`);
6195
+ compactAuditLog(auditPath);
6196
+ } catch (error) {
6197
+ createLogger("sno-station-mem:audit").error("memory.audit.append.failed", { error }, {
6198
+ event_name: "memory.audit.append.failed",
6199
+ file: "packages/memory/src/engine/operations/runtime-audit-log.ts",
6200
+ function: "appendAuditEntrySync",
6201
+ site_id: "memory.audit.sync.failed"
6202
+ });
6203
+ }
5900
6204
  }
5901
- //#endregion
5902
- //#region src/model/llmix-registry.ts
5903
- /** @file llmix-registry.ts
5904
- * @purpose Loads the bundled signed LLMIx registry for sno-station-mem.
5905
- * @boundary Trust-anchor verification and package-relative asset resolution only.
5906
- */
5907
- const SNO_STATION_MEM_RELEASE_ANCHOR_URL = FIXED_EXTERNAL_VALUE_8;
5908
- const SNO_STATION_MEM_RELEASE_KEY_ID = FIXED_EXTERNAL_VALUE_9;
5909
- const SNO_STATION_MEM_RELEASE_ANCHOR_ATTEMPTS = 2;
5910
- const SNO_STATION_MEM_RELEASE_ANCHOR_RETRY_DELAY_MS = 50;
5911
- const ALLOWED_HTTP_ENDPOINT_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1"]);
5912
- function parsePresetId(preset) {
5913
- const [module, presetName] = preset.split("/");
5914
- if (!module || !presetName) throw new Error(`sno-station-mem llm-client: invalid LLMIx preset id "${preset}"`);
6205
+ function memoryAuditDetails(operation, phase, operationId, details) {
5915
6206
  return {
5916
- module,
5917
- preset: presetName
6207
+ ...details,
6208
+ operation,
6209
+ audit_phase: phase,
6210
+ audit_operation_id: operationId
5918
6211
  };
5919
6212
  }
5920
- function assertSupportedProvider(provider) {
5921
- if (provider === "openai" || provider === "openrouter" || provider === "sno-gpu") return provider;
5922
- throw new Error(`sno-station-mem llm-client: unsupported LLMIx provider "${provider}"`);
6213
+ /** Records the start and outcome without using audit availability as admission. */
6214
+ async function runWithMemoryAudit(options) {
6215
+ if (memoryAuditScope.getStore()) return options.run();
6216
+ return memoryAuditScope.run(true, async () => {
6217
+ const operationId = randomUUID();
6218
+ await appendAuditEntryStrict(options.stateDir, {
6219
+ event: options.event,
6220
+ scope: options.scope,
6221
+ resultStatus: "partial",
6222
+ details: memoryAuditDetails(options.operation, "started", operationId, options.startedDetails)
6223
+ });
6224
+ let result;
6225
+ try {
6226
+ result = await options.run();
6227
+ } catch (error) {
6228
+ await appendAuditEntryStrict(options.stateDir, {
6229
+ event: options.event,
6230
+ scope: options.scope,
6231
+ resultStatus: "error",
6232
+ errorCode: "memory_operation_failed",
6233
+ details: memoryAuditDetails(options.operation, "failed", operationId, options.startedDetails)
6234
+ });
6235
+ throw error;
6236
+ }
6237
+ await appendAuditEntryStrict(options.stateDir, {
6238
+ event: options.event,
6239
+ scope: options.scope,
6240
+ resultStatus: "ok",
6241
+ details: memoryAuditDetails(options.operation, "completed", operationId, options.completedDetails(result))
6242
+ });
6243
+ return result;
6244
+ });
5923
6245
  }
5924
- function resolveEndpointBase(baseSource, preservePath) {
5925
- let parsed;
5926
- try {
5927
- parsed = new URL(baseSource);
5928
- } catch {
5929
- throw new Error(`sno-station-mem llm-client: invalid extraction.llm.baseURL "${baseSource}"`);
6246
+ async function readOpenMutationAttempts(stateDir) {
6247
+ const auditPath = getAuditPath(stateDir);
6248
+ if (!existsSync(auditPath)) return [];
6249
+ const openAttempts = /* @__PURE__ */ new Map();
6250
+ for await (const line of readJsonlLines(auditPath)) {
6251
+ if (!line.trim()) continue;
6252
+ let parsed;
6253
+ try {
6254
+ parsed = JSON.parse(line);
6255
+ } catch {
6256
+ continue;
6257
+ }
6258
+ if (!isRecord$1(parsed) || !isRecord$1(parsed["details"])) continue;
6259
+ const event = parsed["event"];
6260
+ const decision = parsed["decision"];
6261
+ const details = parsed["details"];
6262
+ const attemptId = details["audit_operation_id"];
6263
+ if (typeof attemptId !== "string") continue;
6264
+ if (details["mutation_outcome"] !== void 0) {
6265
+ openAttempts.delete(attemptId);
6266
+ continue;
6267
+ }
6268
+ if (decision !== "mutation-attempt-open" || details["audit_phase"] !== "started" || event !== "memory_updated" && event !== "memory_superseded" || typeof details["operation"] !== "string" || details["mutation_writer"] !== "profile-section" && details["mutation_writer"] !== "task-lifecycle" && details["mutation_writer"] !== "legacy-profile-refusal") continue;
6269
+ openAttempts.set(attemptId, {
6270
+ event,
6271
+ operation: details["operation"],
6272
+ attemptId,
6273
+ writer: details["mutation_writer"],
6274
+ ...typeof details["mutation_subject"] === "string" ? { subject: details["mutation_subject"] } : {}
6275
+ });
5930
6276
  }
5931
- if (parsed.protocol !== "https:" && !ALLOWED_HTTP_ENDPOINT_HOSTS.has(parsed.hostname)) throw new Error("sno-station-mem llm-client: extraction.llm.baseURL must use https");
5932
- if (parsed.protocol === "https:" && parsed.hostname.toLowerCase() === "api.openai.com") throw new Error("sno-station-mem llm-client: direct api.openai.com transport is forbidden; use local ccproxy or an explicit custom endpoint");
5933
- if (!preservePath) return parsed.origin;
5934
- const base = parsed.toString();
5935
- return base.endsWith("/") ? base : `${base}/`;
5936
- }
5937
- function packageRootFromImportMeta() {
5938
- const moduleDir = path.dirname(fileURLToPath(import.meta.url));
5939
- const bundledRoot = path.resolve(moduleDir, "..");
5940
- return [bundledRoot, path.resolve(moduleDir, "../..")].find(hasBundledRegistryAssets) ?? bundledRoot;
5941
- }
5942
- function hasBundledRegistryAssets(packageRoot) {
5943
- return existsSync(path.join(packageRoot, "config", "llm", "current.json")) && existsSync(path.join(packageRoot, "config", "llm.trust.json"));
6277
+ return [...openAttempts.values()];
5944
6278
  }
5945
- function resolveBundledLlmixRegistryPaths(packageRoot = packageRootFromImportMeta()) {
5946
- const registryDir = path.join(packageRoot, "config", "llm");
5947
- const trustManifestPath = path.join(packageRoot, "config", "llm.trust.json");
5948
- if (!hasBundledRegistryAssets(packageRoot)) throw new Error("sno-station-mem LLMIx registry assets are missing from the installed package");
6279
+ function mutationAuditDetails(input) {
5949
6280
  return {
5950
- registryDir,
5951
- trustManifestPath
6281
+ operation: input.operation,
6282
+ audit_phase: input.phase,
6283
+ audit_operation_id: input.attemptId,
6284
+ mutation_writer: input.writer,
6285
+ ...input.subject === void 0 ? {} : { mutation_subject: input.subject },
6286
+ ...input.outcome === void 0 ? {} : { mutation_outcome: input.outcome },
6287
+ ...input.refusalReason === void 0 ? {} : { refusal_reason: input.refusalReason }
5952
6288
  };
5953
6289
  }
5954
- function isJsonObject(value) {
5955
- return typeof value === "object" && value !== null && !Array.isArray(value);
5956
- }
5957
- function readPublicKeyJwk(document) {
5958
- if (!isJsonObject(document) || document.id !== "did:web:www.sno.ai") return null;
5959
- const methods = document.verificationMethod;
5960
- if (!Array.isArray(methods)) return null;
5961
- for (const method of methods) {
5962
- if (!isJsonObject(method) || method.id !== SNO_STATION_MEM_RELEASE_KEY_ID) continue;
5963
- const jwk = method.publicKeyJwk;
5964
- if (isJsonObject(jwk) && jwk.kty === "OKP" && jwk.crv === "Ed25519" && typeof jwk.x === "string") return jwk;
5965
- }
5966
- return null;
6290
+ function mutationAuditStatus(outcome) {
6291
+ if (outcome === "failed" || outcome === "interrupted-unknown") return "error";
6292
+ if (outcome.startsWith("refused-")) return "skipped";
6293
+ return "ok";
5967
6294
  }
5968
- function verifyEd25519(jwk, paeBytes, signature) {
5969
- const publicKey = createPublicKey({
5970
- key: jwk,
5971
- format: "jwk"
6295
+ async function closeMutationAttempt(stateDir, attempt, completion) {
6296
+ const phase = completion.outcome === "failed" || completion.outcome === "interrupted-unknown" ? "failed" : "completed";
6297
+ await appendAuditEntryStrict(stateDir, {
6298
+ event: attempt.event,
6299
+ resultStatus: mutationAuditStatus(completion.outcome),
6300
+ decision: completion.outcome,
6301
+ details: mutationAuditDetails({
6302
+ operation: attempt.operation,
6303
+ phase,
6304
+ attemptId: attempt.attemptId,
6305
+ writer: attempt.writer,
6306
+ ...attempt.subject === void 0 ? {} : { subject: attempt.subject },
6307
+ outcome: completion.outcome,
6308
+ ...completion.refusalReason === void 0 ? {} : { refusalReason: completion.refusalReason }
6309
+ })
5972
6310
  });
5973
- return verify(null, Buffer$1.from(paeBytes), publicKey, Buffer$1.from(signature, "base64"));
5974
- }
5975
- function releaseAnchorFailureMessage(error) {
5976
- if (!isJsonObject(error) || !isJsonObject(error.details)) return null;
5977
- const cause = error.details.cause;
5978
- if (typeof cause !== "string") return null;
5979
- return cause.includes("sno-station-mem LLMIx release anchor fetch") ? cause : null;
5980
6311
  }
5981
- async function fetchReleaseAnchorOnce(fetchImpl, timeoutMs) {
5982
- const controller = new AbortController();
5983
- let timeout;
5984
- const timeoutPromise = new Promise((_resolve, reject) => {
5985
- timeout = setTimeout(() => {
5986
- controller.abort();
5987
- reject(/* @__PURE__ */ new Error(`sno-station-mem LLMIx release anchor fetch timed out after ${timeoutMs}ms`));
5988
- }, timeoutMs);
5989
- });
5990
- try {
5991
- return await Promise.race([fetchImpl(SNO_STATION_MEM_RELEASE_ANCHOR_URL, { signal: controller.signal }), timeoutPromise]);
5992
- } finally {
5993
- if (timeout !== void 0) clearTimeout(timeout);
5994
- }
6312
+ /** Closes mutation attempts whose process stopped before a terminal audit record was written. */
6313
+ async function recoverInterruptedMutationAttempts(stateDir) {
6314
+ const attempts = await readOpenMutationAttempts(stateDir);
6315
+ for (const attempt of attempts) await closeMutationAttempt(stateDir, attempt, { outcome: "interrupted-unknown" });
6316
+ return attempts.map((attempt) => attempt.attemptId);
5995
6317
  }
5996
- function isTransientAnchorResponse(response) {
5997
- return isTransientLlmStatus(response.status);
6318
+ function ensureMutationRecovery(stateDir) {
6319
+ const existing = mutationRecoveryPromises.get(stateDir);
6320
+ if (existing) return existing;
6321
+ const recovery = recoverInterruptedMutationAttempts(stateDir).then(() => void 0);
6322
+ mutationRecoveryPromises.set(stateDir, recovery);
6323
+ return recovery;
5998
6324
  }
5999
- async function fetchReleaseAnchor(fetchImpl, timeoutMs) {
6000
- for (let attempt = 1; attempt <= SNO_STATION_MEM_RELEASE_ANCHOR_ATTEMPTS; attempt++) {
6325
+ /** Runs one mutation under a durable open record and exactly one terminal outcome. */
6326
+ async function runWithMutationAttempt(options) {
6327
+ if (mutationAttemptScope.getStore()) return options.run();
6328
+ await ensureMutationRecovery(options.stateDir);
6329
+ const attempt = {
6330
+ event: options.event,
6331
+ operation: options.operation,
6332
+ attemptId: randomUUID(),
6333
+ writer: options.writer,
6334
+ ...options.subject === void 0 ? {} : { subject: options.subject }
6335
+ };
6336
+ return mutationAttemptScope.run(attempt.attemptId, async () => {
6337
+ await appendAuditEntryStrict(options.stateDir, {
6338
+ event: options.event,
6339
+ resultStatus: "partial",
6340
+ decision: "mutation-attempt-open",
6341
+ details: mutationAuditDetails({
6342
+ operation: attempt.operation,
6343
+ phase: "started",
6344
+ attemptId: attempt.attemptId,
6345
+ writer: attempt.writer,
6346
+ ...attempt.subject === void 0 ? {} : { subject: attempt.subject }
6347
+ })
6348
+ });
6349
+ let result;
6001
6350
  try {
6002
- const response = await fetchReleaseAnchorOnce(fetchImpl, timeoutMs);
6003
- if (attempt === SNO_STATION_MEM_RELEASE_ANCHOR_ATTEMPTS || !isTransientAnchorResponse(response)) return response;
6351
+ result = await options.run();
6004
6352
  } catch (error) {
6005
- if (attempt === SNO_STATION_MEM_RELEASE_ANCHOR_ATTEMPTS) throw error;
6353
+ await closeMutationAttempt(options.stateDir, attempt, options.failedOutcome?.(error) ?? { outcome: "failed" });
6354
+ throw error;
6006
6355
  }
6007
- await new Promise((resolve) => setTimeout(resolve, SNO_STATION_MEM_RELEASE_ANCHOR_RETRY_DELAY_MS));
6008
- }
6356
+ await closeMutationAttempt(options.stateDir, attempt, options.completedOutcome(result));
6357
+ return result;
6358
+ });
6359
+ }
6360
+ /** Returns the durable identifier owned by the current mutation boundary. */
6361
+ function currentMutationAttemptId() {
6362
+ const attemptId = mutationAttemptScope.getStore();
6363
+ if (!attemptId) throw new Error("mutation attempt identifier requested outside a mutation boundary");
6364
+ return attemptId;
6365
+ }
6366
+ /** Runs a synchronous memory operation only after its durable start record is persisted. */
6367
+ function runWithMemoryAuditSync(options) {
6368
+ if (memoryAuditScope.getStore()) return options.run();
6369
+ return memoryAuditScope.run(true, () => {
6370
+ const operationId = randomUUID();
6371
+ appendAuditEntrySync(options.stateDir, {
6372
+ event: options.event,
6373
+ scope: options.scope,
6374
+ resultStatus: "partial",
6375
+ details: memoryAuditDetails(options.operation, "started", operationId, options.startedDetails)
6376
+ });
6377
+ let result;
6378
+ try {
6379
+ result = options.run();
6380
+ } catch (error) {
6381
+ appendAuditEntrySync(options.stateDir, {
6382
+ event: options.event,
6383
+ scope: options.scope,
6384
+ resultStatus: "error",
6385
+ errorCode: "memory_operation_failed",
6386
+ details: memoryAuditDetails(options.operation, "failed", operationId, options.startedDetails)
6387
+ });
6388
+ throw error;
6389
+ }
6390
+ appendAuditEntrySync(options.stateDir, {
6391
+ event: options.event,
6392
+ scope: options.scope,
6393
+ resultStatus: "ok",
6394
+ details: memoryAuditDetails(options.operation, "completed", operationId, options.completedDetails(result))
6395
+ });
6396
+ return result;
6397
+ });
6398
+ }
6399
+ //#endregion
6400
+ //#region src/model/llm-failure.ts
6401
+ const CREDENTIAL_CATEGORIES = /* @__PURE__ */ new Set([
6402
+ "auth",
6403
+ "credential-expired",
6404
+ "credential-revoked"
6405
+ ]);
6406
+ const TERMINAL_CATEGORIES = /* @__PURE__ */ new Set([...CREDENTIAL_CATEGORIES, "exhausted"]);
6407
+ function isCredentialRevokedMessage(message) {
6408
+ return /oauth token refresh failed for openai:.*\binvalid_grant\b.*\btoken revoked\b/i.test(message);
6409
+ }
6410
+ function isCredentialExpiredMessage(message) {
6411
+ return /\bexpired access token\b/i.test(message);
6412
+ }
6413
+ function isAuthMessage(message) {
6414
+ return /\b(?:401|403)\b/.test(message) || /unauthori[sz]ed/i.test(message) || /no api key found/i.test(message) || /(?:invalid|incorrect) api[- ]?key/i.test(message) || /(?:invalid|expired) (?:access |oauth )?token/i.test(message) || /(?:api[- ]?key|token).*(?:invalid|expired|rejected)/i.test(message) || /credential (?:is )?(?:invalid|expired|rejected)/i.test(message) || /no (?:valid )?credential (?:found|available|configured)/i.test(message) || /re-?authenticate/i.test(message) || /no authentication configured/i.test(message);
6415
+ }
6416
+ function isExhaustedMessage(message) {
6417
+ return /you(?:'|’)ve reached your codex subscription usage limit/i.test(message);
6418
+ }
6419
+ function isThrottleMessage(message) {
6420
+ return /\b429\b/.test(message) || /rate.?limit/i.test(message) || /quota/i.test(message);
6421
+ }
6422
+ function categoryOf(status, message) {
6423
+ if (isCredentialRevokedMessage(message)) return "credential-revoked";
6424
+ if (isCredentialExpiredMessage(message)) return "credential-expired";
6425
+ if (status === 401 || status === 403 || isAuthMessage(message)) return "auth";
6426
+ if (isExhaustedMessage(message)) return "exhausted";
6427
+ if (status === 429 || isThrottleMessage(message)) return "throttle";
6428
+ if (status !== void 0 && (status === 404 || status === 408 || status >= 500)) return "transport";
6429
+ return "unknown";
6430
+ }
6431
+ /** Classifies one failed model call from whatever the transport surfaced: a status, a message, or both. */
6432
+ function classifyLlmFailure(input) {
6433
+ const category = categoryOf(input.status, input.message ?? "");
6434
+ const terminal = TERMINAL_CATEGORIES.has(category);
6435
+ return {
6436
+ category,
6437
+ terminal,
6438
+ endpointRefused: terminal || input.status === 404 || input.status === 503
6439
+ };
6440
+ }
6441
+ function isCredentialFailure(category) {
6442
+ return CREDENTIAL_CATEGORIES.has(category);
6443
+ }
6444
+ function isTerminalLlmFailure$1(category) {
6445
+ return TERMINAL_CATEGORIES.has(category);
6446
+ }
6447
+ /** A status worth one more attempt against the same endpoint. */
6448
+ function isTransientLlmStatus(status) {
6449
+ return status === 408 || status === 429 || status >= 500;
6450
+ }
6451
+ //#endregion
6452
+ //#region src/model/llmix-registry.ts
6453
+ /** @file llmix-registry.ts
6454
+ * @purpose Loads the bundled signed LLMIx registry for sno-station-mem.
6455
+ * @boundary Trust-anchor verification and package-relative asset resolution only.
6456
+ */
6457
+ const SNO_STATION_MEM_RELEASE_ANCHOR_URL = FIXED_EXTERNAL_VALUE_8;
6458
+ const SNO_STATION_MEM_RELEASE_KEY_ID = FIXED_EXTERNAL_VALUE_9;
6459
+ const SNO_STATION_MEM_RELEASE_ANCHOR_ATTEMPTS = 2;
6460
+ const SNO_STATION_MEM_RELEASE_ANCHOR_RETRY_DELAY_MS = 50;
6461
+ const ALLOWED_HTTP_ENDPOINT_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1"]);
6462
+ function parsePresetId(preset) {
6463
+ const [module, presetName] = preset.split("/");
6464
+ if (!module || !presetName) throw new Error(`sno-station-mem llm-client: invalid LLMIx preset id "${preset}"`);
6465
+ return {
6466
+ module,
6467
+ preset: presetName
6468
+ };
6469
+ }
6470
+ function assertSupportedProvider(provider) {
6471
+ if (provider === "openai" || provider === "openrouter" || provider === "sno-gpu") return provider;
6472
+ throw new Error(`sno-station-mem llm-client: unsupported LLMIx provider "${provider}"`);
6473
+ }
6474
+ function resolveEndpointBase(baseSource, preservePath) {
6475
+ let parsed;
6476
+ try {
6477
+ parsed = new URL(baseSource);
6478
+ } catch {
6479
+ throw new Error(`sno-station-mem llm-client: invalid extraction.llm.baseURL "${baseSource}"`);
6480
+ }
6481
+ if (parsed.protocol !== "https:" && !ALLOWED_HTTP_ENDPOINT_HOSTS.has(parsed.hostname)) throw new Error("sno-station-mem llm-client: extraction.llm.baseURL must use https");
6482
+ if (parsed.protocol === "https:" && parsed.hostname.toLowerCase() === "api.openai.com") throw new Error("sno-station-mem llm-client: direct api.openai.com transport is forbidden; use local ccproxy or an explicit custom endpoint");
6483
+ if (!preservePath) return parsed.origin;
6484
+ const base = parsed.toString();
6485
+ return base.endsWith("/") ? base : `${base}/`;
6486
+ }
6487
+ function packageRootFromImportMeta() {
6488
+ const moduleDir = path.dirname(fileURLToPath(import.meta.url));
6489
+ const bundledRoot = path.resolve(moduleDir, "..");
6490
+ return [bundledRoot, path.resolve(moduleDir, "../..")].find(hasBundledRegistryAssets) ?? bundledRoot;
6491
+ }
6492
+ function hasBundledRegistryAssets(packageRoot) {
6493
+ return existsSync(path.join(packageRoot, "config", "llm", "current.json")) && existsSync(path.join(packageRoot, "config", "llm.trust.json"));
6494
+ }
6495
+ function resolveBundledLlmixRegistryPaths(packageRoot = packageRootFromImportMeta()) {
6496
+ const registryDir = path.join(packageRoot, "config", "llm");
6497
+ const trustManifestPath = path.join(packageRoot, "config", "llm.trust.json");
6498
+ if (!hasBundledRegistryAssets(packageRoot)) throw new Error("sno-station-mem LLMIx registry assets are missing from the installed package");
6499
+ return {
6500
+ registryDir,
6501
+ trustManifestPath
6502
+ };
6503
+ }
6504
+ function isJsonObject(value) {
6505
+ return typeof value === "object" && value !== null && !Array.isArray(value);
6506
+ }
6507
+ function readPublicKeyJwk(document) {
6508
+ if (!isJsonObject(document) || document.id !== "did:web:www.sno.ai") return null;
6509
+ const methods = document.verificationMethod;
6510
+ if (!Array.isArray(methods)) return null;
6511
+ for (const method of methods) {
6512
+ if (!isJsonObject(method) || method.id !== SNO_STATION_MEM_RELEASE_KEY_ID) continue;
6513
+ const jwk = method.publicKeyJwk;
6514
+ if (isJsonObject(jwk) && jwk.kty === "OKP" && jwk.crv === "Ed25519" && typeof jwk.x === "string") return jwk;
6515
+ }
6516
+ return null;
6517
+ }
6518
+ function verifyEd25519(jwk, paeBytes, signature) {
6519
+ const publicKey = createPublicKey({
6520
+ key: jwk,
6521
+ format: "jwk"
6522
+ });
6523
+ return verify(null, Buffer$1.from(paeBytes), publicKey, Buffer$1.from(signature, "base64"));
6524
+ }
6525
+ function releaseAnchorFailureMessage(error) {
6526
+ if (!isJsonObject(error) || !isJsonObject(error.details)) return null;
6527
+ const cause = error.details.cause;
6528
+ if (typeof cause !== "string") return null;
6529
+ return cause.includes("sno-station-mem LLMIx release anchor fetch") ? cause : null;
6530
+ }
6531
+ async function fetchReleaseAnchorOnce(fetchImpl, timeoutMs) {
6532
+ const controller = new AbortController();
6533
+ let timeout;
6534
+ const timeoutPromise = new Promise((_resolve, reject) => {
6535
+ timeout = setTimeout(() => {
6536
+ controller.abort();
6537
+ reject(/* @__PURE__ */ new Error(`sno-station-mem LLMIx release anchor fetch timed out after ${timeoutMs}ms`));
6538
+ }, timeoutMs);
6539
+ });
6540
+ try {
6541
+ return await Promise.race([fetchImpl(SNO_STATION_MEM_RELEASE_ANCHOR_URL, { signal: controller.signal }), timeoutPromise]);
6542
+ } finally {
6543
+ if (timeout !== void 0) clearTimeout(timeout);
6544
+ }
6545
+ }
6546
+ function isTransientAnchorResponse(response) {
6547
+ return isTransientLlmStatus(response.status);
6548
+ }
6549
+ async function fetchReleaseAnchor(fetchImpl, timeoutMs) {
6550
+ for (let attempt = 1; attempt <= SNO_STATION_MEM_RELEASE_ANCHOR_ATTEMPTS; attempt++) {
6551
+ try {
6552
+ const response = await fetchReleaseAnchorOnce(fetchImpl, timeoutMs);
6553
+ if (attempt === SNO_STATION_MEM_RELEASE_ANCHOR_ATTEMPTS || !isTransientAnchorResponse(response)) return response;
6554
+ } catch (error) {
6555
+ if (attempt === SNO_STATION_MEM_RELEASE_ANCHOR_ATTEMPTS) throw error;
6556
+ }
6557
+ await new Promise((resolve) => setTimeout(resolve, SNO_STATION_MEM_RELEASE_ANCHOR_RETRY_DELAY_MS));
6558
+ }
6009
6559
  throw new Error("sno-station-mem LLMIx release anchor fetch exhausted");
6010
6560
  }
6011
6561
  function createSnoMemSnoStationMemDidWebVerifier(fetchImpl = globalThis.fetch, options = {}) {
@@ -6051,7 +6601,10 @@ function materializeBundledLlmixEndpoint(input) {
6051
6601
  if (!path) throw new Error(`sno-station-mem llm-client: signed endpoint path missing from preset ${input.preset.preset}`);
6052
6602
  return {
6053
6603
  url: new URL(path, resolveEndpointBase(input.baseSource, input.preset.provider !== "sno-gpu")).toString(),
6054
- preset: input.preset
6604
+ preset: input.preset.provider === "sno-gpu" && path === "/extract/profile/v1/completions" ? {
6605
+ ...input.preset,
6606
+ model: "b-profile"
6607
+ } : input.preset
6055
6608
  };
6056
6609
  }
6057
6610
  async function resolveConfiguredBundledLlmixEndpoint(input) {
@@ -6672,6 +7225,7 @@ function normalizeMemoryLlmRequest(value) {
6672
7225
  const emptyReplyAttempts = raw.emptyReplyAttempts;
6673
7226
  if (emptyReplyAttempts !== void 0 && (!Number.isInteger(emptyReplyAttempts) || Number(emptyReplyAttempts) < 1 || Number(emptyReplyAttempts) > EMPTY_REPLY_ATTEMPTS)) throw new Error(`sno-station-mem llm-client: emptyReplyAttempts must be an integer from 1 to ${EMPTY_REPLY_ATTEMPTS}`);
6674
7227
  const enableThinking = raw.enableThinking;
7228
+ const takeProviderAttempt = raw.takeProviderAttempt;
6675
7229
  if (enableThinking !== void 0 && typeof enableThinking !== "boolean") throw new Error("sno-station-mem llm-client: enableThinking must be a boolean");
6676
7230
  const signal = raw.signal;
6677
7231
  if (signal !== void 0 && !hasAbortSignalShape(signal)) throw new Error("sno-station-mem llm-client: signal must be an AbortSignal");
@@ -6685,6 +7239,7 @@ function normalizeMemoryLlmRequest(value) {
6685
7239
  ...maxTokens !== void 0 ? { maxTokens: Number(maxTokens) } : {},
6686
7240
  ...emptyReplyAttempts !== void 0 ? { emptyReplyAttempts: Number(emptyReplyAttempts) } : {},
6687
7241
  ...enableThinking !== void 0 ? { enableThinking } : {},
7242
+ ...typeof takeProviderAttempt === "function" ? { takeProviderAttempt: () => takeProviderAttempt() === true } : {},
6688
7243
  ...requestId !== void 0 ? { requestId } : {},
6689
7244
  ...typeof raw.promptTemplateHash === "string" && /^[a-f0-9]{64}$/.test(raw.promptTemplateHash) ? { promptTemplateHash: raw.promptTemplateHash } : {},
6690
7245
  ...typeof raw.extractionSkillHash === "string" && /^[a-f0-9]{64}$/.test(raw.extractionSkillHash) ? { extractionSkillHash: raw.extractionSkillHash } : {},
@@ -6692,7 +7247,9 @@ function normalizeMemoryLlmRequest(value) {
6692
7247
  };
6693
7248
  }
6694
7249
  function textSystemContent(request) {
6695
- return MODEL_CALLS[request.callId].occasion === "conflictAdjudication" ? CONFLICT_ADJUDICATION_SYSTEM_CONTENT : REFLECTION_TEXT_SYSTEM_CONTENT;
7250
+ const occasion = MODEL_CALLS[request.callId].occasion;
7251
+ if (occasion === "conflictAdjudication") return CONFLICT_ADJUDICATION_SYSTEM_CONTENT;
7252
+ return occasion === "memoryExtract" ? JSON_SYSTEM_CONTENT : REFLECTION_TEXT_SYSTEM_CONTENT;
6696
7253
  }
6697
7254
  /** Creates the LLM client facade with provider routing, key rotation, and JSON parsing. */
6698
7255
  function createLlmClient(config) {
@@ -6722,8 +7279,29 @@ function createLlmClient(config) {
6722
7279
  }
6723
7280
  return endpointPromise;
6724
7281
  };
7282
+ const providerBudget = new AsyncLocalStorage();
6725
7283
  const pipeline = new CallPipeline({
6726
- dispatch: callProvider,
7284
+ dispatch: async (context) => {
7285
+ const budget = providerBudget.getStore();
7286
+ if (!budget) return callProvider(context);
7287
+ const timeoutMs = Math.floor(budget.deadlineMs - performance.now());
7288
+ if (timeoutMs <= 0) throw Object.assign(/* @__PURE__ */ new Error("Request deadline exhausted before provider dispatch"), { statusCode: 499 });
7289
+ if (budget.remaining <= 0 || budget.takeProviderAttempt && !budget.takeProviderAttempt()) throw Object.assign(/* @__PURE__ */ new Error("Provider request attempts exhausted"), { statusCode: 499 });
7290
+ budget.remaining -= 1;
7291
+ const providerConfig = context.config;
7292
+ const dispatchConfig = {
7293
+ ...providerConfig,
7294
+ timeoutMs,
7295
+ ...providerConfig.rawCompletion ? { rawCompletion: {
7296
+ ...providerConfig.rawCompletion,
7297
+ timeoutMs
7298
+ } } : {}
7299
+ };
7300
+ return callProvider({
7301
+ ...context,
7302
+ config: dispatchConfig
7303
+ });
7304
+ },
6727
7305
  maxRetries: 2,
6728
7306
  retryBaseMs: 100,
6729
7307
  retryMaxDelayMs: 500,
@@ -6795,6 +7373,7 @@ function createLlmClient(config) {
6795
7373
  if (transport === "agent-host-seam") {
6796
7374
  const agentPort = config.agentPort;
6797
7375
  if (!agentPort) throw new LlmClientTerminalError("transport", `sno-station-mem: llm-client [${request.callId}] AgentLlmPort is unavailable`);
7376
+ if (request.takeProviderAttempt && !request.takeProviderAttempt()) return null;
6798
7377
  config.onTransportAttempt?.({
6799
7378
  callId: request.callId,
6800
7379
  destination,
@@ -7037,13 +7616,18 @@ function createLlmClient(config) {
7037
7616
  * result is returned untouched — it means no call was made, not that the model said nothing.
7038
7617
  */
7039
7618
  const requestContentRetryingEmpty = async (state, request, systemContent, deadlineMs, prompt = request.prompt) => {
7040
- let content = await requestContent(state, request, systemContent, prompt);
7619
+ const budget = {
7620
+ remaining: EMPTY_REPLY_ATTEMPTS,
7621
+ deadlineMs,
7622
+ takeProviderAttempt: request.takeProviderAttempt
7623
+ };
7624
+ let content = await providerBudget.run(budget, () => requestContent(state, request, systemContent, prompt));
7041
7625
  let usage = state.usage;
7042
7626
  try {
7043
7627
  checkMemoryOperation();
7044
7628
  const attemptLimit = request.emptyReplyAttempts ?? EMPTY_REPLY_ATTEMPTS;
7045
7629
  for (let attempt = 2; attempt <= attemptLimit; attempt += 1) {
7046
- if (content === null || content.raw.trim().length > 0) break;
7630
+ if (content === null || content.raw.trim().length > 0 || budget.remaining <= 0) break;
7047
7631
  const remainingMs = deadlineMs - performance.now();
7048
7632
  if (remainingMs <= 0) break;
7049
7633
  log$20.debug("Retrying empty model reply", {
@@ -7057,10 +7641,10 @@ function createLlmClient(config) {
7057
7641
  function: "requestContentRetryingEmpty",
7058
7642
  site_id: "shared.llm-client.requestContentRetryingEmpty.cd874fefff"
7059
7643
  });
7060
- content = await requestContent(state, {
7644
+ content = await providerBudget.run(budget, () => requestContent(state, {
7061
7645
  ...request,
7062
7646
  timeoutMs: remainingMs
7063
- }, systemContent, prompt);
7647
+ }, systemContent, prompt));
7064
7648
  usage = addUsage(usage, state.usage);
7065
7649
  checkMemoryOperation();
7066
7650
  }
@@ -7085,6 +7669,7 @@ function createLlmClient(config) {
7085
7669
  if (snoError !== void 0) throw snoError;
7086
7670
  return content;
7087
7671
  }
7672
+ const remainingMs = Math.floor(deadlineMs - performance.now());
7088
7673
  const originalLastError = state.error;
7089
7674
  const originalLastUsage = state.usage;
7090
7675
  const source = snoError instanceof Error ? snoError.message : originalLastError ?? "";
@@ -7096,21 +7681,25 @@ function createLlmClient(config) {
7096
7681
  else if (!config.apiKey?.trim()) snoFailure = "missing_key";
7097
7682
  const operationId = config.operationId ?? memoryOperationId();
7098
7683
  let hostFailure = "empty_reply";
7099
- if (!config.agentPort) hostFailure = "port_unavailable";
7684
+ if (remainingMs <= 0) hostFailure = "deadline_exhausted";
7685
+ else if (!config.agentPort) hostFailure = "port_unavailable";
7100
7686
  else if ([
7101
7687
  "E10",
7102
7688
  "P1",
7103
7689
  "REM1"
7104
7690
  ].includes(request.callId) && !request.hostPrompt) hostFailure = "host_prompt_missing";
7105
7691
  else try {
7106
- const host = await requestContent(state, request, systemContent, request.hostPrompt ?? request.prompt, "host");
7692
+ const host = await requestContent(state, {
7693
+ ...request,
7694
+ timeoutMs: remainingMs
7695
+ }, systemContent, request.hostPrompt ?? request.prompt, "host");
7107
7696
  if (host?.raw.trim()) {
7108
7697
  log$20.info("Sno model call answered by host", {
7109
7698
  call_id: request.callId,
7110
7699
  operation_id: operationId,
7111
7700
  sno_failure: snoFailure,
7112
7701
  host_outcome: "answered",
7113
- timeout_ms: resolveRequestTimeoutMs(request)
7702
+ timeout_ms: remainingMs
7114
7703
  }, {
7115
7704
  event_name: "memory.llm_client.fallback",
7116
7705
  file: "packages/memory/src/model/llm-client.ts",
@@ -7122,7 +7711,7 @@ function createLlmClient(config) {
7122
7711
  }
7123
7712
  if (host === null) hostFailure = "host_error";
7124
7713
  } catch (error) {
7125
- if (request.signal?.aborted) throw error;
7714
+ if (request.signal?.aborted || error instanceof ModelCallRefusedError) throw error;
7126
7715
  hostFailure = error instanceof LlmClientTerminalError ? error.category : "transport";
7127
7716
  }
7128
7717
  state.error = originalLastError;
@@ -7267,7 +7856,7 @@ function createLlmClient(config) {
7267
7856
  //#region src/engine/security/scope-policy-types.ts
7268
7857
  const DEFAULT_SCOPE_CONFIG = {
7269
7858
  default: "global",
7270
- definitions: { global: { description: "Shared knowledge across all agents" } },
7859
+ definitions: { global: { description: "Knowledge used by all your agents" } },
7271
7860
  agentAccess: {},
7272
7861
  extraAccessibleScopes: []
7273
7862
  };
@@ -7361,7 +7950,7 @@ var MemoryScopePolicy = class {
7361
7950
  },
7362
7951
  extraAccessibleScopes: normalizeExtraAccessibleScopes(config.extraAccessibleScopes ?? DEFAULT_SCOPE_CONFIG.extraAccessibleScopes)
7363
7952
  };
7364
- if (!this.config.definitions.global) this.config.definitions.global = { description: "Shared knowledge across all agents" };
7953
+ if (!this.config.definitions.global) this.config.definitions.global = { description: "Knowledge used by all your agents" };
7365
7954
  this.validateConfiguration();
7366
7955
  }
7367
7956
  validateConfiguration() {
@@ -14321,487 +14910,77 @@ async function resolveMemoryDate(input) {
14321
14910
  }
14322
14911
  };
14323
14912
  }
14324
- const stage = {
14325
- modelCalled: true,
14326
- reason: reply.reason
14327
- };
14328
- const calculated = calculateCalendarTime(reply.time, anchor, unresolved.timezone);
14329
- log$17.info("Memory date resolution completed", {
14330
- model_called: true,
14331
- resolved: calculated !== null
14332
- }, {
14333
- event_name: "sno_station_mem.date-resolution.date.resolution.stage",
14334
- file: "packages/memory/src/engine/extraction/date-resolution.ts",
14335
- function: "resolveMemoryDate",
14336
- site_id: "date-resolution.resolveMemoryDate.1224fa35b2"
14337
- });
14338
- if (reply.time.kind === "none") return {
14339
- ...unresolved,
14340
- interval: {
14341
- type: "static",
14342
- resolutionStatus: "static"
14343
- },
14344
- stage
14345
- };
14346
- if (!calculated) return {
14347
- ...unresolved,
14348
- stage
14349
- };
14350
- return {
14351
- interval: {
14352
- type: "bounded",
14353
- resolutionStatus: "resolved",
14354
- from: calculated.from,
14355
- until: calculated.until,
14356
- phrase: input.expression ?? input.text,
14357
- date: calculated.label,
14358
- precision: calculated.precision,
14359
- timezone: calculated.timezone
14360
- },
14361
- timestamp: unresolved.timestamp,
14362
- timezone: unresolved.timezone,
14363
- stage
14364
- };
14365
- }
14366
- //#endregion
14367
- //#region src/engine/extraction/memory-temporality-classifier.ts
14368
- function parseSessionTimestamp(sessionDateTime) {
14369
- return parseIsoDateTimeMs(sessionDateTime);
14370
- }
14371
- function serializeIntervalMetadata(category, interval) {
14372
- if (interval.type === "static") return {
14373
- memory_temporal_type: "static",
14374
- temporal_resolution_status: "static"
14375
- };
14376
- const base = {
14377
- memory_temporal_type: "dynamic",
14378
- temporal_resolution_status: interval.resolutionStatus
14379
- };
14380
- if ("phrase" in interval && interval.phrase) base.temporal_phrase = interval.phrase;
14381
- if (interval.type === "instant") {
14382
- if (category === "episodic") base.event_at = interval.at;
14383
- base.valid_from = interval.at;
14384
- } else if (interval.type === "bounded") {
14385
- base.valid_from = interval.from;
14386
- if (category === "episodic") base.valid_until = interval.until;
14387
- if (interval.date !== void 0) base.temporal_date = interval.date;
14388
- if (interval.precision !== void 0) base.temporal_precision = interval.precision;
14389
- if (interval.timezone !== void 0) base.temporal_timezone = interval.timezone;
14390
- } else if (interval.type === "ongoing") {
14391
- base.valid_from = interval.from;
14392
- if (interval.until !== void 0) base.valid_until = interval.until;
14393
- }
14394
- return base;
14395
- }
14396
- //#endregion
14397
- //#region src/engine/operations/jsonl-lines.ts
14398
- const log$16 = createLogger("sno-station-mem:jsonl");
14399
- /** Preserve complete records without materializing an unbounded log as one string. */
14400
- async function* readJsonlLines(file) {
14401
- const stream = createReadStream(file, { encoding: "utf8" });
14402
- const lines = createInterface({
14403
- input: stream,
14404
- crlfDelay: Infinity
14405
- });
14406
- try {
14407
- for await (const line of lines) if (line.trim()) yield line;
14408
- } catch (error) {
14409
- if (error instanceof Error && "code" in error && error.code === "ENOENT") return;
14410
- log$16.error("jsonl.read.failed", {
14411
- file,
14412
- error
14413
- }, {
14414
- event_name: "memory.jsonl.read.failed",
14415
- file: "packages/memory/src/engine/operations/jsonl-lines.ts",
14416
- function: "readJsonlLines",
14417
- site_id: "memory.jsonl.read.failed"
14418
- });
14419
- } finally {
14420
- lines.close();
14421
- stream.destroy();
14422
- }
14423
- }
14424
- //#endregion
14425
- //#region src/engine/operations/runtime-audit-log.ts
14426
- /** @file runtime-audit-log.ts
14427
- * @purpose Records audit events for operational visibility and safety-sensitive actions.
14428
- * @boundary Plugin runtime events and filesystem-backed audit persistence.
14429
- * @see sno-station-mem-plugin-runtime.ts, memory-tool-registration.ts, errors.ts.
14430
- */
14431
- const auditWriteQueues = /* @__PURE__ */ new Map();
14432
- const memoryAuditScope = new AsyncLocalStorage();
14433
- const mutationAttemptScope = new AsyncLocalStorage();
14434
- const mutationRecoveryPromises = /* @__PURE__ */ new Map();
14435
- const MEMORY_AUDIT_COMMON_KEYS = [
14436
- "operation",
14437
- "audit_phase",
14438
- "audit_operation_id"
14439
- ];
14440
- const MUTATION_AUDIT_DETAIL_KEYS = [
14441
- "mutation_writer",
14442
- "mutation_subject",
14443
- "mutation_outcome",
14444
- "refusal_reason"
14445
- ];
14446
- const MEMORY_AUDIT_ALLOWED_DETAIL_KEYS = {
14447
- memory_injected: /* @__PURE__ */ new Set([
14448
- ...MEMORY_AUDIT_COMMON_KEYS,
14449
- "scope",
14450
- "category",
14451
- "lane",
14452
- "count",
14453
- "memory_ids",
14454
- "write_outcome",
14455
- "created_count",
14456
- "existing_count"
14457
- ]),
14458
- memory_searched: /* @__PURE__ */ new Set([
14459
- ...MEMORY_AUDIT_COMMON_KEYS,
14460
- "scope",
14461
- "query_kind",
14462
- "requested_count",
14463
- "result_count",
14464
- "memory_ids"
14465
- ]),
14466
- memory_read: /* @__PURE__ */ new Set([
14467
- ...MEMORY_AUDIT_COMMON_KEYS,
14468
- "scope",
14469
- "requested_count",
14470
- "result_count",
14471
- "memory_ids",
14472
- "requested_fact_id",
14473
- "resolved_fact_id",
14474
- "fact_ids",
14475
- "found",
14476
- "outcome"
14477
- ]),
14478
- memory_updated: /* @__PURE__ */ new Set([
14479
- ...MEMORY_AUDIT_COMMON_KEYS,
14480
- ...MUTATION_AUDIT_DETAIL_KEYS,
14481
- "scope",
14482
- "requested_count",
14483
- "result_count",
14484
- "memory_ids",
14485
- "outcome",
14486
- "migration_status",
14487
- "rewritten_rows",
14488
- "checked_rows"
14489
- ]),
14490
- memory_superseded: /* @__PURE__ */ new Set([
14491
- ...MEMORY_AUDIT_COMMON_KEYS,
14492
- ...MUTATION_AUDIT_DETAIL_KEYS,
14493
- "scope",
14494
- "replacement_memory_id",
14495
- "closed_memory_ids",
14496
- "write_outcome"
14497
- ]),
14498
- memory_deleted: /* @__PURE__ */ new Set([
14499
- ...MEMORY_AUDIT_COMMON_KEYS,
14500
- "scope",
14501
- "deleted_memory_ids",
14502
- "delete_reason",
14503
- "count",
14504
- "outcome",
14505
- "removed_files"
14506
- ]),
14507
- memory_purged: /* @__PURE__ */ new Set([
14508
- ...MEMORY_AUDIT_COMMON_KEYS,
14509
- "target_fact_id",
14510
- "purged_fact_ids",
14511
- "failed_fact_ids",
14512
- "status",
14513
- "blocked_reason"
14514
- ])
14515
- };
14516
- /** Implements enqueue audit write as the local audit log state operation. */
14517
- function enqueueAuditWrite(auditPath, line) {
14518
- const next = (auditWriteQueues.get(auditPath) ?? Promise.resolve(true)).catch(() => void 0).then(async () => {
14519
- mkdirSync(path.dirname(auditPath), { recursive: true });
14520
- await appendFile(auditPath, line);
14521
- return true;
14522
- }).catch((error) => {
14523
- createLogger("sno-station-mem:audit").error("Audit entry append failed", { error }, {
14524
- event_name: "memory.audit.append.failed",
14525
- file: "packages/memory/src/engine/operations/runtime-audit-log.ts",
14526
- function: "enqueueAuditWrite",
14527
- site_id: "memory.audit.append.failed"
14528
- });
14529
- return false;
14530
- });
14531
- auditWriteQueues.set(auditPath, next);
14532
- next.finally(() => {
14533
- if (auditWriteQueues.get(auditPath) === next) auditWriteQueues.delete(auditPath);
14534
- });
14535
- return next;
14536
- }
14537
- /** Returns audit path from audit log state state without side effects. */
14538
- function getAuditPath(stateDir) {
14539
- return path.join(stateDir, "audit.jsonl");
14540
- }
14541
- function isRecord$1(value) {
14542
- return typeof value === "object" && value !== null;
14543
- }
14544
- function isMemoryAuditEvent(event) {
14545
- return event.startsWith("memory_");
14546
- }
14547
- function redactAuditValue(value) {
14548
- if (typeof value === "string") return redactSecrets(value);
14549
- if (Array.isArray(value)) return value.map(redactAuditValue);
14550
- if (!isRecord$1(value)) return value;
14551
- const redacted = {};
14552
- for (const [key, child] of Object.entries(value)) redacted[key] = redactAuditValue(child);
14553
- return redacted;
14554
- }
14555
- function prepareAuditEntry(entry) {
14556
- if (!isMemoryAuditEvent(entry.event) || entry.details?.["audit_phase"] === void 0) return entry;
14557
- const allowed = MEMORY_AUDIT_ALLOWED_DETAIL_KEYS[entry.event];
14558
- const details = {};
14559
- for (const [key, value] of Object.entries(entry.details)) if (allowed.has(key)) details[key] = value;
14560
- else createLogger("sno-station-mem:audit").error("memory.audit.detail.unknown", {
14561
- event: entry.event,
14562
- key
14563
- }, {
14564
- event_name: "memory.audit.detail.unknown",
14565
- file: "packages/memory/src/engine/operations/runtime-audit-log.ts",
14566
- function: "prepareAuditEntry",
14567
- site_id: "memory.audit.detail.unknown"
14568
- });
14569
- return {
14570
- ...entry,
14571
- ...entry.scope === void 0 ? {} : { scope: redactSecrets(entry.scope) },
14572
- details: redactAuditValue(details)
14573
- };
14574
- }
14575
- /** Persists audit entry through the single audit log state write path. */
14576
- function appendAuditEntry(stateDir, entry) {
14577
- const auditPath = getAuditPath(stateDir);
14578
- const prepared = prepareAuditEntry(entry);
14579
- enqueueAuditWrite(auditPath, `${JSON.stringify({
14580
- ...prepared,
14581
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
14582
- })}\n`);
14583
- }
14584
- /** Waits for the audit append; failures are logged without blocking the operation. */
14585
- async function appendAuditEntryStrict(stateDir, entry) {
14586
- const auditPath = getAuditPath(stateDir);
14587
- const prepared = prepareAuditEntry(entry);
14588
- return enqueueAuditWrite(auditPath, `${JSON.stringify({
14589
- ...prepared,
14590
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
14591
- })}\n`);
14592
- }
14593
- /** Attempts a synchronous audit append and logs a failure. */
14594
- function appendAuditEntrySync(stateDir, entry) {
14595
- const auditPath = getAuditPath(stateDir);
14596
- const prepared = prepareAuditEntry(entry);
14597
- try {
14598
- mkdirSync(path.dirname(auditPath), { recursive: true });
14599
- appendFileSync(auditPath, `${JSON.stringify({
14600
- ...prepared,
14601
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
14602
- })}\n`);
14603
- } catch (error) {
14604
- createLogger("sno-station-mem:audit").error("memory.audit.append.failed", { error }, {
14605
- event_name: "memory.audit.append.failed",
14606
- file: "packages/memory/src/engine/operations/runtime-audit-log.ts",
14607
- function: "appendAuditEntrySync",
14608
- site_id: "memory.audit.sync.failed"
14609
- });
14610
- }
14611
- }
14612
- function memoryAuditDetails(operation, phase, operationId, details) {
14613
- return {
14614
- ...details,
14615
- operation,
14616
- audit_phase: phase,
14617
- audit_operation_id: operationId
14618
- };
14619
- }
14620
- /** Records the start and outcome without using audit availability as admission. */
14621
- async function runWithMemoryAudit(options) {
14622
- if (memoryAuditScope.getStore()) return options.run();
14623
- return memoryAuditScope.run(true, async () => {
14624
- const operationId = randomUUID();
14625
- await appendAuditEntryStrict(options.stateDir, {
14626
- event: options.event,
14627
- scope: options.scope,
14628
- resultStatus: "partial",
14629
- details: memoryAuditDetails(options.operation, "started", operationId, options.startedDetails)
14630
- });
14631
- let result;
14632
- try {
14633
- result = await options.run();
14634
- } catch (error) {
14635
- await appendAuditEntryStrict(options.stateDir, {
14636
- event: options.event,
14637
- scope: options.scope,
14638
- resultStatus: "error",
14639
- errorCode: "memory_operation_failed",
14640
- details: memoryAuditDetails(options.operation, "failed", operationId, options.startedDetails)
14641
- });
14642
- throw error;
14643
- }
14644
- await appendAuditEntryStrict(options.stateDir, {
14645
- event: options.event,
14646
- scope: options.scope,
14647
- resultStatus: "ok",
14648
- details: memoryAuditDetails(options.operation, "completed", operationId, options.completedDetails(result))
14649
- });
14650
- return result;
14651
- });
14652
- }
14653
- async function readOpenMutationAttempts(stateDir) {
14654
- const auditPath = getAuditPath(stateDir);
14655
- if (!existsSync(auditPath)) return [];
14656
- const openAttempts = /* @__PURE__ */ new Map();
14657
- for await (const line of readJsonlLines(auditPath)) {
14658
- if (!line.trim()) continue;
14659
- let parsed;
14660
- try {
14661
- parsed = JSON.parse(line);
14662
- } catch {
14663
- continue;
14664
- }
14665
- if (!isRecord$1(parsed) || !isRecord$1(parsed["details"])) continue;
14666
- const event = parsed["event"];
14667
- const decision = parsed["decision"];
14668
- const details = parsed["details"];
14669
- const attemptId = details["audit_operation_id"];
14670
- if (typeof attemptId !== "string") continue;
14671
- if (details["mutation_outcome"] !== void 0) {
14672
- openAttempts.delete(attemptId);
14673
- continue;
14674
- }
14675
- if (decision !== "mutation-attempt-open" || details["audit_phase"] !== "started" || event !== "memory_updated" && event !== "memory_superseded" || typeof details["operation"] !== "string" || details["mutation_writer"] !== "profile-section" && details["mutation_writer"] !== "task-lifecycle" && details["mutation_writer"] !== "legacy-profile-refusal") continue;
14676
- openAttempts.set(attemptId, {
14677
- event,
14678
- operation: details["operation"],
14679
- attemptId,
14680
- writer: details["mutation_writer"],
14681
- ...typeof details["mutation_subject"] === "string" ? { subject: details["mutation_subject"] } : {}
14682
- });
14683
- }
14684
- return [...openAttempts.values()];
14685
- }
14686
- function mutationAuditDetails(input) {
14687
- return {
14688
- operation: input.operation,
14689
- audit_phase: input.phase,
14690
- audit_operation_id: input.attemptId,
14691
- mutation_writer: input.writer,
14692
- ...input.subject === void 0 ? {} : { mutation_subject: input.subject },
14693
- ...input.outcome === void 0 ? {} : { mutation_outcome: input.outcome },
14694
- ...input.refusalReason === void 0 ? {} : { refusal_reason: input.refusalReason }
14913
+ const stage = {
14914
+ modelCalled: true,
14915
+ reason: reply.reason
14695
14916
  };
14696
- }
14697
- function mutationAuditStatus(outcome) {
14698
- if (outcome === "failed" || outcome === "interrupted-unknown") return "error";
14699
- if (outcome.startsWith("refused-")) return "skipped";
14700
- return "ok";
14701
- }
14702
- async function closeMutationAttempt(stateDir, attempt, completion) {
14703
- const phase = completion.outcome === "failed" || completion.outcome === "interrupted-unknown" ? "failed" : "completed";
14704
- await appendAuditEntryStrict(stateDir, {
14705
- event: attempt.event,
14706
- resultStatus: mutationAuditStatus(completion.outcome),
14707
- decision: completion.outcome,
14708
- details: mutationAuditDetails({
14709
- operation: attempt.operation,
14710
- phase,
14711
- attemptId: attempt.attemptId,
14712
- writer: attempt.writer,
14713
- ...attempt.subject === void 0 ? {} : { subject: attempt.subject },
14714
- outcome: completion.outcome,
14715
- ...completion.refusalReason === void 0 ? {} : { refusalReason: completion.refusalReason }
14716
- })
14917
+ const calculated = calculateCalendarTime(reply.time, anchor, unresolved.timezone);
14918
+ log$17.info("Memory date resolution completed", {
14919
+ model_called: true,
14920
+ resolved: calculated !== null
14921
+ }, {
14922
+ event_name: "sno_station_mem.date-resolution.date.resolution.stage",
14923
+ file: "packages/memory/src/engine/extraction/date-resolution.ts",
14924
+ function: "resolveMemoryDate",
14925
+ site_id: "date-resolution.resolveMemoryDate.1224fa35b2"
14717
14926
  });
14718
- }
14719
- /** Closes mutation attempts whose process stopped before a terminal audit record was written. */
14720
- async function recoverInterruptedMutationAttempts(stateDir) {
14721
- const attempts = await readOpenMutationAttempts(stateDir);
14722
- for (const attempt of attempts) await closeMutationAttempt(stateDir, attempt, { outcome: "interrupted-unknown" });
14723
- return attempts.map((attempt) => attempt.attemptId);
14724
- }
14725
- function ensureMutationRecovery(stateDir) {
14726
- const existing = mutationRecoveryPromises.get(stateDir);
14727
- if (existing) return existing;
14728
- const recovery = recoverInterruptedMutationAttempts(stateDir).then(() => void 0);
14729
- mutationRecoveryPromises.set(stateDir, recovery);
14730
- return recovery;
14731
- }
14732
- /** Runs one mutation under a durable open record and exactly one terminal outcome. */
14733
- async function runWithMutationAttempt(options) {
14734
- if (mutationAttemptScope.getStore()) return options.run();
14735
- await ensureMutationRecovery(options.stateDir);
14736
- const attempt = {
14737
- event: options.event,
14738
- operation: options.operation,
14739
- attemptId: randomUUID(),
14740
- writer: options.writer,
14741
- ...options.subject === void 0 ? {} : { subject: options.subject }
14927
+ if (reply.time.kind === "none") return {
14928
+ ...unresolved,
14929
+ interval: {
14930
+ type: "static",
14931
+ resolutionStatus: "static"
14932
+ },
14933
+ stage
14934
+ };
14935
+ if (!calculated) return {
14936
+ ...unresolved,
14937
+ stage
14938
+ };
14939
+ return {
14940
+ interval: {
14941
+ type: "bounded",
14942
+ resolutionStatus: "resolved",
14943
+ from: calculated.from,
14944
+ until: calculated.until,
14945
+ phrase: input.expression ?? input.text,
14946
+ date: calculated.label,
14947
+ precision: calculated.precision,
14948
+ timezone: calculated.timezone
14949
+ },
14950
+ timestamp: unresolved.timestamp,
14951
+ timezone: unresolved.timezone,
14952
+ stage
14742
14953
  };
14743
- return mutationAttemptScope.run(attempt.attemptId, async () => {
14744
- await appendAuditEntryStrict(options.stateDir, {
14745
- event: options.event,
14746
- resultStatus: "partial",
14747
- decision: "mutation-attempt-open",
14748
- details: mutationAuditDetails({
14749
- operation: attempt.operation,
14750
- phase: "started",
14751
- attemptId: attempt.attemptId,
14752
- writer: attempt.writer,
14753
- ...attempt.subject === void 0 ? {} : { subject: attempt.subject }
14754
- })
14755
- });
14756
- let result;
14757
- try {
14758
- result = await options.run();
14759
- } catch (error) {
14760
- await closeMutationAttempt(options.stateDir, attempt, options.failedOutcome?.(error) ?? { outcome: "failed" });
14761
- throw error;
14762
- }
14763
- await closeMutationAttempt(options.stateDir, attempt, options.completedOutcome(result));
14764
- return result;
14765
- });
14766
14954
  }
14767
- /** Returns the durable identifier owned by the current mutation boundary. */
14768
- function currentMutationAttemptId() {
14769
- const attemptId = mutationAttemptScope.getStore();
14770
- if (!attemptId) throw new Error("mutation attempt identifier requested outside a mutation boundary");
14771
- return attemptId;
14955
+ //#endregion
14956
+ //#region src/engine/extraction/memory-temporality-classifier.ts
14957
+ function parseSessionTimestamp(sessionDateTime) {
14958
+ return parseIsoDateTimeMs(sessionDateTime);
14772
14959
  }
14773
- /** Runs a synchronous memory operation only after its durable start record is persisted. */
14774
- function runWithMemoryAuditSync(options) {
14775
- if (memoryAuditScope.getStore()) return options.run();
14776
- return memoryAuditScope.run(true, () => {
14777
- const operationId = randomUUID();
14778
- appendAuditEntrySync(options.stateDir, {
14779
- event: options.event,
14780
- scope: options.scope,
14781
- resultStatus: "partial",
14782
- details: memoryAuditDetails(options.operation, "started", operationId, options.startedDetails)
14783
- });
14784
- let result;
14785
- try {
14786
- result = options.run();
14787
- } catch (error) {
14788
- appendAuditEntrySync(options.stateDir, {
14789
- event: options.event,
14790
- scope: options.scope,
14791
- resultStatus: "error",
14792
- errorCode: "memory_operation_failed",
14793
- details: memoryAuditDetails(options.operation, "failed", operationId, options.startedDetails)
14794
- });
14795
- throw error;
14796
- }
14797
- appendAuditEntrySync(options.stateDir, {
14798
- event: options.event,
14799
- scope: options.scope,
14800
- resultStatus: "ok",
14801
- details: memoryAuditDetails(options.operation, "completed", operationId, options.completedDetails(result))
14802
- });
14803
- return result;
14804
- });
14960
+ function serializeIntervalMetadata(category, interval) {
14961
+ if (interval.type === "static") return {
14962
+ memory_temporal_type: "static",
14963
+ temporal_resolution_status: "static"
14964
+ };
14965
+ const base = {
14966
+ memory_temporal_type: "dynamic",
14967
+ temporal_resolution_status: interval.resolutionStatus
14968
+ };
14969
+ if ("phrase" in interval && interval.phrase) base.temporal_phrase = interval.phrase;
14970
+ if (interval.type === "instant") {
14971
+ if (category === "episodic") base.event_at = interval.at;
14972
+ base.valid_from = interval.at;
14973
+ } else if (interval.type === "bounded") {
14974
+ base.valid_from = interval.from;
14975
+ if (category === "episodic") base.valid_until = interval.until;
14976
+ if (interval.date !== void 0) base.temporal_date = interval.date;
14977
+ if (interval.precision !== void 0) base.temporal_precision = interval.precision;
14978
+ if (interval.timezone !== void 0) base.temporal_timezone = interval.timezone;
14979
+ } else if (interval.type === "ongoing") {
14980
+ base.valid_from = interval.from;
14981
+ if (interval.until !== void 0) base.valid_until = interval.until;
14982
+ }
14983
+ return base;
14805
14984
  }
14806
14985
  createLogger("sno-station-mem:scopes");
14807
14986
  //#endregion
@@ -15763,12 +15942,14 @@ function createVerdictGeneration(database, input) {
15763
15942
  generation_id, pair_id, left_row_id, right_row_id, sort_key,
15764
15943
  claim_state, attempt_count, progress_state, refusal_reason, inherited_from_generation_id
15765
15944
  ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
15766
- const inheritedRefusal = input.inheritedRefusalsFromGenerationId ? database.prepare(`SELECT attempt_count, refusal_reason FROM nodix_rem_scan_pairs
15767
- WHERE generation_id = ? AND pair_id = ? AND progress_state = 'refused'
15768
- AND refusal_reason IN ('content_changed', 'target_changed')`) : void 0;
15945
+ const inheritedRefusal = database.prepare(`SELECT pair.attempt_count, pair.refusal_reason, pair.generation_id
15946
+ FROM nodix_rem_scan_pairs AS pair
15947
+ JOIN nodix_rem_scan_generations AS generation USING (generation_id)
15948
+ WHERE pair.pair_id = ? AND pair.progress_state = 'refused'
15949
+ ORDER BY generation.rowid DESC LIMIT 1`);
15769
15950
  for (const pair of input.pairs) {
15770
- const inherited = inheritedRefusal?.get(input.inheritedRefusalsFromGenerationId, pair.pairId);
15771
- insertPair.run(input.generationId, pair.pairId, pair.leftRowId, pair.rightRowId, pair.sortKey, "unvisited", inherited?.attempt_count ?? 0, inherited ? "refused" : "pending", inherited?.refusal_reason ?? null, inherited ? input.inheritedRefusalsFromGenerationId : null);
15951
+ const inherited = inheritedRefusal.get(pair.pairId);
15952
+ insertPair.run(input.generationId, pair.pairId, pair.leftRowId, pair.rightRowId, pair.sortKey, "unvisited", inherited?.attempt_count ?? 0, inherited ? "refused" : "pending", inherited?.refusal_reason ?? null, inherited?.generation_id ?? null);
15772
15953
  }
15773
15954
  }).immediate();
15774
15955
  }
@@ -15803,7 +15984,7 @@ function listVerdictPairs(database, generationId) {
15803
15984
  }
15804
15985
  function readClosedVerdictPairIds(database, pairIds) {
15805
15986
  const rows = database.prepare(`SELECT DISTINCT pair_id FROM nodix_rem_scan_pairs
15806
- WHERE progress_state = 'closed' AND pair_id IN (SELECT value FROM json_each(?))`).all(JSON.stringify(pairIds));
15987
+ WHERE progress_state IN ('closed', 'exhausted') AND pair_id IN (SELECT value FROM json_each(?))`).all(JSON.stringify(pairIds));
15807
15988
  return new Set(rows.map((row) => row.pair_id));
15808
15989
  }
15809
15990
  function claimNextPair(database, input) {
@@ -15816,7 +15997,7 @@ function claimNextPair(database, input) {
15816
15997
  reserved_tokens, attempt_count, progress_state, refusal_reason, inherited_from_generation_id
15817
15998
  FROM nodix_rem_scan_pairs
15818
15999
  WHERE generation_id = ? AND claim_state = 'unvisited'
15819
- ORDER BY CASE progress_state WHEN 'refused' THEN 0 ELSE 1 END, sort_key, pair_id
16000
+ ORDER BY attempt_count, sort_key, pair_id
15820
16001
  LIMIT 1`).get(input.generationId);
15821
16002
  if (!pair) return void 0;
15822
16003
  if (asRunResult(database.prepare(`UPDATE nodix_rem_scan_pairs SET
@@ -17838,7 +18019,7 @@ function loadSqliteVecExtension(database) {
17838
18019
  * idempotent, so an interrupted move simply completes on the next boot.
17839
18020
  * @boundary Reads and writes only those two tables through the caller's handle.
17840
18021
  */
17841
- const log$14 = createLogger("unplaced-candidate-migration");
18022
+ const log$15 = createLogger("unplaced-candidate-migration");
17842
18023
  /**
17843
18024
  * Legacy rows are keyed on their own row id, not on the live writer's basis.
17844
18025
  * Two legacy rows can carry byte-identical payloads and reasons and still be
@@ -17881,7 +18062,7 @@ function migrateUnplacedCandidates(sqlite) {
17881
18062
  }
17882
18063
  return moved;
17883
18064
  })();
17884
- log$14.warn("relocated non-active memory rows into the unplaced-candidate table", { moved }, {
18065
+ log$15.warn("relocated non-active memory rows into the unplaced-candidate table", { moved }, {
17885
18066
  event_name: FIXED_EXTERNAL_VALUE_62,
17886
18067
  file: "packages/memory/src/store/unplaced-candidate-migration.ts",
17887
18068
  function: "migrateUnplacedCandidates",
@@ -17896,7 +18077,7 @@ function migrateUnplacedCandidates(sqlite) {
17896
18077
  * @boundary Filesystem safety, database pragmas, migrations, and runtime loading.
17897
18078
  * @see sqlite-runtime.ts, schema.ts, store.ts.
17898
18079
  */
17899
- const log$13 = createLogger("sno-station-mem:db");
18080
+ const log$14 = createLogger("sno-station-mem:db");
17900
18081
  function parseVecTableDimension(sql) {
17901
18082
  if (!/create\s+virtual\s+table/i.test(sql) || !/using\s+vec0/i.test(sql)) throw new StorageError(`nodix_memory_chunk_vectors exists but is not a sqlite-vec virtual table: ${sql}`);
17902
18083
  const token = sql.match(/embedding\s+float\[(\d+)\]/i)?.[1];
@@ -17935,7 +18116,7 @@ function ensureChunkVecTable(db, vectorDim) {
17935
18116
  const existing = readChunkVecTableState(db);
17936
18117
  if (existing && (existing.dimension !== vectorDim || !existing.hasPartitionKey)) {
17937
18118
  if (existing.rowCount > 0) {
17938
- log$13.error("storage.vector.configuration.mismatch", {
18119
+ log$14.error("storage.vector.configuration.mismatch", {
17939
18120
  vectorDim,
17940
18121
  existing
17941
18122
  }, {
@@ -17975,7 +18156,7 @@ function loadSimpleTokenizer(db) {
17975
18156
  db.loadExtension(extensionPath);
17976
18157
  db.prepare("SELECT jieba_dict(?)").get(dictPath);
17977
18158
  db.exec("CREATE VIRTUAL TABLE temp.nodix_simple_tokenizer_smoke USING fts5(x, tokenize='simple 0'); DROP TABLE temp.nodix_simple_tokenizer_smoke;");
17978
- log$13.info("simple-tokenizer loaded", {
18159
+ log$14.info("simple-tokenizer loaded", {
17979
18160
  platform,
17980
18161
  extensionPath
17981
18162
  }, {
@@ -18005,7 +18186,7 @@ function resolveMigrationsDir() {
18005
18186
  const MIGRATIONS_DIR = resolveMigrationsDir();
18006
18187
  /** Implements init db as the local SQLite connection setup operation. */
18007
18188
  function initDb(dbPath, vectorDim) {
18008
- log$13.info("initializing database", {
18189
+ log$14.info("initializing database", {
18009
18190
  dbPath,
18010
18191
  vectorDim
18011
18192
  }, {
@@ -18022,7 +18203,7 @@ function initDb(dbPath, vectorDim) {
18022
18203
  run();
18023
18204
  pending.delete(step);
18024
18205
  } catch (error) {
18025
- log$13.error("storage.setup.failed", {
18206
+ log$14.error("storage.setup.failed", {
18026
18207
  step,
18027
18208
  error
18028
18209
  }, {
@@ -18081,7 +18262,7 @@ function ensureColumn(database, tableName, columnName, declaration) {
18081
18262
  * @boundary SQLite schema, embedding vectors, FTS, and metadata invariants.
18082
18263
  * @see connection.ts, schema.ts, retriever.ts.
18083
18264
  */
18084
- const log$12 = createLogger("sno-station-mem:store");
18265
+ const log$13 = createLogger("sno-station-mem:store");
18085
18266
  /**
18086
18267
  * Reflection v3: discriminator added to content-hash input when the
18087
18268
  * row carries a `metadata.mappedKind`. Two rows with same projectId + same store
@@ -18191,7 +18372,11 @@ function loadMemoryTelemetryKeySet(options) {
18191
18372
  historic: /* @__PURE__ */ new Map()
18192
18373
  };
18193
18374
  const key = options.key;
18194
- if (!key || key.trim().length === 0) throw new Error("telemetry.memoryUsage.key is required when memory telemetry is enabled");
18375
+ if (!key || key.trim().length === 0) return {
18376
+ enabled: true,
18377
+ current: null,
18378
+ historic: new Map(options.historicKeys.map((value, index) => [index + 1, value]))
18379
+ };
18195
18380
  const currentKeyVersion = options.currentKeyVersion ?? options.historicKeys.length + 1;
18196
18381
  if (!Number.isInteger(currentKeyVersion) || currentKeyVersion <= 0) throw new Error("memory telemetry current key version must be a positive integer");
18197
18382
  return {
@@ -18348,7 +18533,7 @@ function verification(status, receipt) {
18348
18533
  }
18349
18534
  //#endregion
18350
18535
  //#region src/engine/telemetry/memory-telemetry-events.ts
18351
- const log$11 = createLogger("sno-station-mem:memory-telemetry-events");
18536
+ const log$12 = createLogger("sno-station-mem:memory-telemetry-events");
18352
18537
  var MemoryTelemetryEventWriter = class {
18353
18538
  sqlite;
18354
18539
  enabled;
@@ -18381,28 +18566,40 @@ var MemoryTelemetryEventWriter = class {
18381
18566
  historicKeys: options.config?.historicKeys ?? [],
18382
18567
  currentKeyVersion: options.config?.currentKeyVersion
18383
18568
  });
18384
- this.receiptService = options.config?.receiptService ?? createMemoryTelemetryReceiptService(keySet);
18569
+ this.receiptService = options.config?.receiptService ?? (keySet.current ? createMemoryTelemetryReceiptService(keySet) : null);
18385
18570
  } catch (error) {
18386
- log$11.warn("memory telemetry disabled: receipt key unavailable", { error }, {
18387
- event_name: "sno_station_mem.memory-telemetry-events.memory.telemetry.disabled.receipt.key.unavailable",
18571
+ log$12.warn("memory telemetry signing unavailable; recording unsigned events", { error }, {
18572
+ event_name: "memory.telemetry.signing.unavailable",
18388
18573
  file: "packages/memory/src/engine/telemetry/memory-telemetry-events.ts",
18389
18574
  function: "<anonymous callback>",
18390
18575
  site_id: "memory-telemetry-events.<anonymous callback>.23cfef2d08"
18391
18576
  });
18392
- this.enabled = false;
18393
18577
  this.receiptService = null;
18394
18578
  }
18395
18579
  }
18396
18580
  writeReceiptEvent(input) {
18397
18581
  if (!this.enabled) return;
18398
18582
  const timestampMs = input.timestampMs ?? Date.now();
18399
- const receipt = this.requireReceiptService().sign({
18400
- factId: input.factId,
18401
- contentHash: input.contentHash,
18402
- timestampMs
18583
+ let receipt;
18584
+ try {
18585
+ receipt = this.receiptService?.sign({
18586
+ factId: input.factId,
18587
+ contentHash: input.contentHash,
18588
+ timestampMs
18589
+ });
18590
+ } catch (error) {
18591
+ log$12.warn("memory telemetry signing failed; recording unsigned event", { error }, {
18592
+ event_name: "memory.telemetry.signing.failed",
18593
+ file: "packages/memory/src/engine/telemetry/memory-telemetry-events.ts",
18594
+ function: "writeReceiptEvent",
18595
+ site_id: "memory.telemetry.signing.failed"
18596
+ });
18597
+ }
18598
+ const metadata = validateMemoryTelemetryMetadata(input.eventType, {
18599
+ ...input.metadata,
18600
+ ...input.eventType === "create" ? { receipt_status: receipt ? "signed" : "unsigned" } : {}
18403
18601
  });
18404
- const metadata = validateMemoryTelemetryMetadata(input.eventType, input.metadata);
18405
- this.insertReceiptEventStatement.run(input.eventType, input.factId, input.memoryKind, timestampMs, this.agentId, input.projectId, input.sourceEventId ?? null, input.derivedFrom ? JSON.stringify(input.derivedFrom) : null, receipt.contentHash, receipt.receiptHmac, receipt.keyVersion, JSON.stringify(metadata));
18602
+ this.insertReceiptEventStatement.run(input.eventType, input.factId, input.memoryKind, timestampMs, this.agentId, input.projectId, input.sourceEventId ?? null, input.derivedFrom ? JSON.stringify(input.derivedFrom) : null, input.contentHash, receipt?.receiptHmac ?? null, receipt?.keyVersion ?? null, JSON.stringify(metadata));
18406
18603
  }
18407
18604
  writeLifecycleEvent(input) {
18408
18605
  if (!this.enabled) return;
@@ -18414,10 +18611,6 @@ var MemoryTelemetryEventWriter = class {
18414
18611
  if (!this.enabled) return null;
18415
18612
  return this.readLatestReceiptEventIdStatement.get(factId)?.id ?? null;
18416
18613
  }
18417
- requireReceiptService() {
18418
- if (!this.receiptService) throw new Error("memory telemetry receipt service is unavailable");
18419
- return this.receiptService;
18420
- }
18421
18614
  };
18422
18615
  //#endregion
18423
18616
  //#region src/store/rem-update-stamp-migration.ts
@@ -18761,7 +18954,7 @@ var MemoryStore = class {
18761
18954
  sqlite.close();
18762
18955
  throw error;
18763
18956
  }
18764
- log$12.info("memory store initialized", {
18957
+ log$13.info("memory store initialized", {
18765
18958
  dbPath: config.dbPath,
18766
18959
  vectorDim: this.vectorDim,
18767
18960
  hasFtsSupport: this.hasFtsSupport
@@ -18920,7 +19113,7 @@ Object.assign(MemoryStore.prototype, {
18920
19113
  } catch (error) {
18921
19114
  failedRows++;
18922
19115
  if (firstFailure === void 0 && error instanceof Error) firstFailure = error;
18923
- log$12.warn("legacy memory chunk backfill skipped row", {
19116
+ log$13.warn("legacy memory chunk backfill skipped row", {
18924
19117
  memory_id: row.id,
18925
19118
  error
18926
19119
  }, {
@@ -18970,7 +19163,7 @@ Object.assign(MemoryStore.prototype, {
18970
19163
  this.backfillFailureCount = 0;
18971
19164
  this.backfillNextRetryAt = 0;
18972
19165
  this.backfillLastFailureAt = 0;
18973
- if (count > 0) log$12.info("backfilled legacy memory chunks", { count }, {
19166
+ if (count > 0) log$13.info("backfilled legacy memory chunks", { count }, {
18974
19167
  event_name: "sno_station_mem.memory-store-backfill.backfilled.legacy.memory.chunks",
18975
19168
  file: "packages/memory/src/store/memory-store-backfill.ts",
18976
19169
  function: "<anonymous callback>",
@@ -18983,7 +19176,7 @@ Object.assign(MemoryStore.prototype, {
18983
19176
  this.backfillLastFailureAt = failedAt;
18984
19177
  const retryInMs = this.backfillFailureCount < 3 ? CHUNK_BACKFILL_RETRY_BASE_MS * 2 ** (this.backfillFailureCount - 1) : null;
18985
19178
  this.backfillNextRetryAt = retryInMs === null ? Number.POSITIVE_INFINITY : failedAt + retryInMs;
18986
- log$12.warn("legacy memory chunk backfill failed", {
19179
+ log$13.warn("legacy memory chunk backfill failed", {
18987
19180
  attempt: this.backfillFailureCount,
18988
19181
  maxAttempts: 3,
18989
19182
  retryInMs,
@@ -19281,7 +19474,7 @@ Object.assign(MemoryStore.prototype, {
19281
19474
  const entry = readEntry(this, key);
19282
19475
  if (entry.state !== "pending_reprocess" || entry.reprocessReason === null) throw new StorageError(`Cannot reopen atomic extraction from state '${entry.state}'`);
19283
19476
  if (entry.reprocessAttemptCount >= attemptCap) {
19284
- log$12.error("atomic extraction chunk stuck at reprocess attempt cap", {
19477
+ log$13.error("atomic extraction chunk stuck at reprocess attempt cap", {
19285
19478
  product_session_reference: privateLogReference(key.conversationId),
19286
19479
  chunk_hash: key.chunkHash,
19287
19480
  version: key.pipelineVersion,
@@ -21111,7 +21304,7 @@ function canTrustedWrite(kind) {
21111
21304
  * @purpose Validates and normalizes memory-kind metadata before direct store writes.
21112
21305
  * @boundary Storage write chokepoint only; extraction owns metadata semantics.
21113
21306
  */
21114
- const log$9 = createLogger("sno-station-mem:active-task-shape-alarm");
21307
+ const log$10 = createLogger("sno-station-mem:active-task-shape-alarm");
21115
21308
  function validateAtomicCardWrite(input) {
21116
21309
  if (input.category !== "state") return;
21117
21310
  if (input.subject === null || !input.subject.startsWith("entity:") || input.subject.length === 7) throw new StorageError("state memory requires a non-empty entity subject");
@@ -21155,7 +21348,7 @@ function formatCause(error) {
21155
21348
  return error instanceof Error ? error.message : String(error);
21156
21349
  }
21157
21350
  function shapeAlarm(operation, reason) {
21158
- log$9.error("Active task shape validation failed", {
21351
+ log$10.error("Active task shape validation failed", {
21159
21352
  operation,
21160
21353
  reason_code: reason.replace(/[^a-z0-9]+/gi, "_").toLowerCase()
21161
21354
  }, {
@@ -21600,7 +21793,7 @@ async function readAtomicArrivalRetirementCandidateSet(store, input) {
21600
21793
  };
21601
21794
  const scoreById = scoreCandidatesBySimilarity(store, await store.embedder.embed(nominatedRow.text), candidates.map(({ id }) => id));
21602
21795
  const ranked = candidates.sort((left, right) => (scoreById.get(right.id) ?? Number.NEGATIVE_INFINITY) - (scoreById.get(left.id) ?? Number.NEGATIVE_INFINITY) || left.id.localeCompare(right.id));
21603
- if (ranked.length > 10) appendArrivalRetirementJournal(store, {
21796
+ if (ranked.length > 3) appendArrivalRetirementJournal(store, {
21604
21797
  jobId: input.jobId,
21605
21798
  nominatedRowId: nominatedRow.id,
21606
21799
  outcome: "no-action",
@@ -21608,12 +21801,12 @@ async function readAtomicArrivalRetirementCandidateSet(store, input) {
21608
21801
  actionsApplied: 0,
21609
21802
  detail: {
21610
21803
  nominatedRowId: nominatedRow.id,
21611
- omittedCount: ranked.length - 10
21804
+ omittedCount: ranked.length - 3
21612
21805
  }
21613
21806
  });
21614
21807
  return {
21615
21808
  nominatedRow: judgedRow(nominatedRow),
21616
- candidateRows: ranked.slice(0, 10).map(judgedRow)
21809
+ candidateRows: ranked.slice(0, 3).map(judgedRow)
21617
21810
  };
21618
21811
  }
21619
21812
  function journalAtomicArrivalRetirementRefusal(store, input) {
@@ -23542,7 +23735,7 @@ Object.assign(MemoryStore.prototype, {
23542
23735
  async store(entry) {
23543
23736
  const safeEntry = sanitizeStoreInput(entry);
23544
23737
  const lane = safeEntry.lane ?? "active";
23545
- log$12.debug("storing memory", {
23738
+ log$13.debug("storing memory", {
23546
23739
  category: safeEntry.category,
23547
23740
  projectId: safeEntry.projectId
23548
23741
  }, {
@@ -23585,7 +23778,7 @@ Object.assign(MemoryStore.prototype, {
23585
23778
  if (writeIdentity) {
23586
23779
  const alreadyWritten = this.findByExtractionIdempotencyKey(projectId, writeIdentity);
23587
23780
  if (alreadyWritten) {
23588
- log$12.debug("idempotency key match, skipping store", {
23781
+ log$13.debug("idempotency key match, skipping store", {
23589
23782
  projectId,
23590
23783
  memory_id: alreadyWritten.id
23591
23784
  }, {
@@ -23599,7 +23792,7 @@ Object.assign(MemoryStore.prototype, {
23599
23792
  }
23600
23793
  const earlyDup = this.readExistingByHash(projectId, hash, category);
23601
23794
  if (earlyDup && !canRevive(earlyDup)) {
23602
- log$12.debug("content hash match, skipping store", {
23795
+ log$13.debug("content hash match, skipping store", {
23603
23796
  projectId,
23604
23797
  memory_id: earlyDup.id
23605
23798
  }, {
@@ -23616,7 +23809,7 @@ Object.assign(MemoryStore.prototype, {
23616
23809
  if (writeIdentity) {
23617
23810
  const alreadyWritten = this.findByExtractionIdempotencyKey(projectId, writeIdentity);
23618
23811
  if (alreadyWritten) {
23619
- log$12.debug("idempotency key match, skipping store", {
23812
+ log$13.debug("idempotency key match, skipping store", {
23620
23813
  projectId,
23621
23814
  memory_id: alreadyWritten.id
23622
23815
  }, {
@@ -23630,7 +23823,7 @@ Object.assign(MemoryStore.prototype, {
23630
23823
  }
23631
23824
  const existing = this.readExistingByHash(projectId, hash, category);
23632
23825
  if (existing && !canRevive(existing)) {
23633
- log$12.debug("content hash match, skipping store", {
23826
+ log$13.debug("content hash match, skipping store", {
23634
23827
  projectId,
23635
23828
  memory_id: existing.id
23636
23829
  }, {
@@ -23940,7 +24133,7 @@ Object.assign(MemoryStore.prototype, {
23940
24133
  assertProfileRecoveryWrite(args.profileRecovery);
23941
24134
  const { activeFactGuard, activeTaskProjectionGuard, closes, preserveExisting, reviveInvalidatedExisting } = args;
23942
24135
  const create = sanitizeStoreInput(args.create);
23943
- log$12.debug("supersede write", {
24136
+ log$13.debug("supersede write", {
23944
24137
  category: create.category,
23945
24138
  projectId: create.projectId,
23946
24139
  closes: closes.length
@@ -24720,7 +24913,7 @@ function mergeMetadata(current, patch) {
24720
24913
  Object.assign(MemoryStore.prototype, {
24721
24914
  async update(id, changes) {
24722
24915
  const countRecordTokens = await recordTokenCounter(this.embedder);
24723
- log$12.debug("updating memory", { memory_id: id }, {
24916
+ log$13.debug("updating memory", { memory_id: id }, {
24724
24917
  event_name: "sno_station_mem.memory-store-update-api.updating.memory",
24725
24918
  file: "packages/memory/src/store/memory-store-update-api.ts",
24726
24919
  function: "update",
@@ -24845,7 +25038,7 @@ Object.assign(MemoryStore.prototype, {
24845
25038
  });
24846
25039
  },
24847
25040
  async updateTier(memoryId, newTier, options) {
24848
- log$12.debug("updating tier", {
25041
+ log$13.debug("updating tier", {
24849
25042
  memory_id: memoryId,
24850
25043
  tier: newTier
24851
25044
  }, {
@@ -24867,7 +25060,7 @@ Object.assign(MemoryStore.prototype, {
24867
25060
  });
24868
25061
  },
24869
25062
  async updateMetadata(memoryId, patch) {
24870
- log$12.debug("updating metadata", { memory_id: memoryId }, {
25063
+ log$13.debug("updating metadata", { memory_id: memoryId }, {
24871
25064
  event_name: "sno_station_mem.memory-store-update-api.updating.metadata",
24872
25065
  file: "packages/memory/src/store/memory-store-update-api.ts",
24873
25066
  function: "updateMetadata",
@@ -24885,7 +25078,7 @@ Object.assign(MemoryStore.prototype, {
24885
25078
  });
24886
25079
  },
24887
25080
  async resolveReflectionItem(memoryId, opts) {
24888
- log$12.debug("resolving reflection item", { memory_id: memoryId }, {
25081
+ log$13.debug("resolving reflection item", { memory_id: memoryId }, {
24889
25082
  event_name: "sno_station_mem.memory-store-update-api.resolving.reflection.item",
24890
25083
  file: "packages/memory/src/store/memory-store-update-api.ts",
24891
25084
  function: "resolveReflectionItem",
@@ -24920,7 +25113,7 @@ Object.assign(MemoryStore.prototype, {
24920
25113
  */
24921
25114
  async applyMetadataDeltas(entries) {
24922
25115
  if (entries.length === 0) return;
24923
- log$12.debug("applying metadata deltas", { count: entries.length }, {
25116
+ log$13.debug("applying metadata deltas", { count: entries.length }, {
24924
25117
  event_name: "sno_station_mem.memory-store-update-api.applying.metadata.deltas",
24925
25118
  file: "packages/memory/src/store/memory-store-update-api.ts",
24926
25119
  function: "applyMetadataDeltas",
@@ -24944,7 +25137,7 @@ Object.assign(MemoryStore.prototype, {
24944
25137
  });
24945
25138
  },
24946
25139
  async applyMetadataDelta(memoryId, deltaFn) {
24947
- log$12.debug("applying metadata delta", { memory_id: memoryId }, {
25140
+ log$13.debug("applying metadata delta", { memory_id: memoryId }, {
24948
25141
  event_name: "sno_station_mem.memory-store-update-api.applying.metadata.delta",
24949
25142
  file: "packages/memory/src/store/memory-store-update-api.ts",
24950
25143
  function: "applyMetadataDelta",
@@ -24965,7 +25158,7 @@ Object.assign(MemoryStore.prototype, {
24965
25158
  });
24966
25159
  //#endregion
24967
25160
  //#region src/store/memory-store-rem-api.ts
24968
- const log$7 = createLogger("sno-station-mem:rem-write");
25161
+ const log$8 = createLogger("sno-station-mem:rem-write");
24969
25162
  Object.assign(MemoryStore.prototype, { async applyRemTextVersion(input) {
24970
25163
  if (input.reason.trim().length === 0) throw new Error("mutation reason is required");
24971
25164
  if (input.idempotencyKey !== void 0 && input.idempotencyKey.trim().length === 0) throw new Error("REM idempotency key must be non-empty");
@@ -24996,7 +25189,7 @@ Object.assign(MemoryStore.prototype, { async applyRemTextVersion(input) {
24996
25189
  });
24997
25190
  } catch (error) {
24998
25191
  if (completedMutation === void 0) throw error;
24999
- log$7.error("rem_write_completion_audit_failed", {
25192
+ log$8.error("rem_write_completion_audit_failed", {
25000
25193
  error,
25001
25194
  row_id: input.rowId
25002
25195
  }, {
@@ -25105,7 +25298,7 @@ async function applyTransaction(store, input, chunks) {
25105
25298
  }
25106
25299
  });
25107
25300
  } catch (error) {
25108
- log$7.error("rem_write_receipt_failed", {
25301
+ log$8.error("rem_write_receipt_failed", {
25109
25302
  error,
25110
25303
  row_id: existing.id
25111
25304
  }, {
@@ -25312,7 +25505,7 @@ function buildChunkSearchResults(vecRows, metaRows, limit, minScore) {
25312
25505
  Object.assign(MemoryStore.prototype, {
25313
25506
  async searchChunksSemantic(vector, opts = {}) {
25314
25507
  if (!this.db.vectorSearchAvailable) {
25315
- log$12.error("storage.vector.search.unavailable", { dbPath: this.dbPath }, {
25508
+ log$13.error("storage.vector.search.unavailable", { dbPath: this.dbPath }, {
25316
25509
  event_name: "storage.vector.search.unavailable",
25317
25510
  file: "packages/memory/src/store/memory-store-chunk-search.ts",
25318
25511
  function: "searchChunksSemantic",
@@ -25335,7 +25528,7 @@ Object.assign(MemoryStore.prototype, {
25335
25528
  const metaRows = readSemanticMetaRows(this.sqlite, vecRows, opts);
25336
25529
  const results = buildChunkSearchResults(vecRows, metaRows, limit, minScore);
25337
25530
  if (results.length >= limit || vecRows.length < candidateBudget) {
25338
- log$12.debug("chunk semantic search", {
25531
+ log$13.debug("chunk semantic search", {
25339
25532
  limit,
25340
25533
  projectIdFilter: opts.projectIdFilter,
25341
25534
  vecCandidates: vecRows.length,
@@ -25355,7 +25548,7 @@ Object.assign(MemoryStore.prototype, {
25355
25548
  candidateBudget = Math.min(candidateBudget * SEMANTIC_CANDIDATE_GROWTH_FACTOR, vectorRowCount);
25356
25549
  }
25357
25550
  } catch (error) {
25358
- log$12.error("chunk semantic search failed", {
25551
+ log$13.error("chunk semantic search failed", {
25359
25552
  error,
25360
25553
  limit,
25361
25554
  projectIdFilter: opts.projectIdFilter,
@@ -25420,7 +25613,7 @@ Object.assign(MemoryStore.prototype, {
25420
25613
  rank: index + 1
25421
25614
  });
25422
25615
  });
25423
- log$12.debug("chunk keyword search", {
25616
+ log$13.debug("chunk keyword search", {
25424
25617
  query_length: safeQuery?.length ?? "unavailable",
25425
25618
  limit,
25426
25619
  minScore,
@@ -25433,7 +25626,7 @@ Object.assign(MemoryStore.prototype, {
25433
25626
  });
25434
25627
  return results;
25435
25628
  } catch (error) {
25436
- log$12.error("chunk keyword search failed", {
25629
+ log$13.error("chunk keyword search failed", {
25437
25630
  error,
25438
25631
  query_length: safeQuery?.length ?? "unavailable",
25439
25632
  limit,
@@ -25609,7 +25802,7 @@ Object.assign(MemoryStore.prototype, {
25609
25802
  if (results.length >= limit) break;
25610
25803
  }
25611
25804
  this.attachSnippets(results, opts.facetPolicy);
25612
- log$12.debug("semantic search (memory-level wrapper)", {
25805
+ log$13.debug("semantic search (memory-level wrapper)", {
25613
25806
  limit,
25614
25807
  chunkLimit,
25615
25808
  projectIdFilter: opts.projectIdFilter,
@@ -25655,7 +25848,7 @@ Object.assign(MemoryStore.prototype, {
25655
25848
  if (results.length >= limit) break;
25656
25849
  }
25657
25850
  this.attachSnippets(results, opts.facetPolicy);
25658
- log$12.debug("keyword search (memory-level wrapper)", {
25851
+ log$13.debug("keyword search (memory-level wrapper)", {
25659
25852
  limit,
25660
25853
  chunkLimit,
25661
25854
  projectIdFilter: opts.projectIdFilter,
@@ -25686,7 +25879,7 @@ Object.assign(MemoryStore.prototype, {
25686
25879
  neighborAfter: 2
25687
25880
  });
25688
25881
  } catch (err) {
25689
- log$12.warn("snippet expand window failed; falling back to entry text", {
25882
+ log$13.warn("snippet expand window failed; falling back to entry text", {
25690
25883
  memory_id: result.entry.id,
25691
25884
  chunkIndex: result.chunkIndex,
25692
25885
  totalChunks,
@@ -25826,7 +26019,7 @@ Object.assign(MemoryStore.prototype, {
25826
26019
  }
25827
26020
  return metadata;
25828
26021
  } catch (e) {
25829
- log$12.warn("malformed metadata JSON", {
26022
+ log$13.warn("malformed metadata JSON", {
25830
26023
  memory_id: memoryId,
25831
26024
  error: e
25832
26025
  }, {
@@ -25890,7 +26083,7 @@ function writeDeleteEvents(internals, rows, reason) {
25890
26083
  }
25891
26084
  Object.assign(MemoryStore.prototype, {
25892
26085
  async delete(idOrPrefix, options) {
25893
- log$12.info("deleting memories", { idOrPrefix }, {
26086
+ log$13.info("deleting memories", { idOrPrefix }, {
25894
26087
  event_name: "sno_station_mem.memory-store-admin-api.deleting.memories",
25895
26088
  file: "packages/memory/src/store/memory-store-admin-api.ts",
25896
26089
  function: "delete",
@@ -25915,7 +26108,7 @@ Object.assign(MemoryStore.prototype, {
25915
26108
  async deleteMany(ids, options) {
25916
26109
  const uniqueIds = Array.from(new Set(ids));
25917
26110
  if (uniqueIds.length === 0) return 0;
25918
- log$12.info("deleting many memories", { count: uniqueIds.length }, {
26111
+ log$13.info("deleting many memories", { count: uniqueIds.length }, {
25919
26112
  event_name: "sno_station_mem.memory-store-admin-api.deleting.many.memories",
25920
26113
  file: "packages/memory/src/store/memory-store-admin-api.ts",
25921
26114
  function: "deleteMany",
@@ -25949,7 +26142,7 @@ Object.assign(MemoryStore.prototype, {
25949
26142
  }).immediate();
25950
26143
  },
25951
26144
  async bulkDelete(filter, options) {
25952
- log$12.info("bulk delete", {
26145
+ log$13.info("bulk delete", {
25953
26146
  projectId: filter.projectId,
25954
26147
  category: filter.category
25955
26148
  }, {
@@ -26020,7 +26213,7 @@ Object.assign(MemoryStore.prototype, {
26020
26213
  unplacedParams.push(filter.category);
26021
26214
  }
26022
26215
  this.sqlite.prepare(`DELETE FROM nodix_unplaced_memory_candidates${unplacedWhere.length > 0 ? ` WHERE ${unplacedWhere.join(" AND ")}` : ""}`).run(...unplacedParams);
26023
- if (truncated) log$12.warn("bulk delete reached safety iteration cap", {
26216
+ if (truncated) log$13.warn("bulk delete reached safety iteration cap", {
26024
26217
  projectId: filter.projectId,
26025
26218
  category: filter.category,
26026
26219
  deleted,
@@ -26063,7 +26256,7 @@ Object.assign(MemoryStore.prototype, {
26063
26256
  this.sqlite.exec("PRAGMA optimize");
26064
26257
  this.sqlite.exec("PRAGMA wal_checkpoint(TRUNCATE)");
26065
26258
  } catch (error) {
26066
- log$12.warn("sqlite close-time maintenance failed", { error }, {
26259
+ log$13.warn("sqlite close-time maintenance failed", { error }, {
26067
26260
  event_name: "sno_station_mem.memory-store-admin-api.sqlite.close.time.maintenance.failed",
26068
26261
  file: "packages/memory/src/store/memory-store-admin-api.ts",
26069
26262
  function: "closeSqlite",
@@ -26159,7 +26352,7 @@ function truncateToTokens(text, maxTokens, countTokens) {
26159
26352
  * @see store.ts, retriever.ts.
26160
26353
  */
26161
26354
  /** Local ONNX embedding with an LRU cache and oversized-text chunking. */
26162
- const log$6 = createLogger("sno-station-mem:embed");
26355
+ const log$7 = createLogger("sno-station-mem:embed");
26163
26356
  var Embedder = class {
26164
26357
  provider;
26165
26358
  chunkingEnabled;
@@ -26189,7 +26382,7 @@ var Embedder = class {
26189
26382
  maxSize: 256,
26190
26383
  ttlMs: CACHE_TTL_MS
26191
26384
  });
26192
- log$6.info("embedder initialized", {
26385
+ log$7.info("embedder initialized", {
26193
26386
  provider: providerKind,
26194
26387
  model: this.model,
26195
26388
  dimensions: this.dimensions,
@@ -26249,7 +26442,7 @@ var Embedder = class {
26249
26442
  if (pieceTokens > 512) throw new EmbeddingError(`Embedder chunk of ${pieceTokens} tokens exceeds DEFAULT_MAX_CONTEXT_TOKENS (512)`);
26250
26443
  }
26251
26444
  if (chunks.length <= 1) return this.embedDirect(chunks[0] ?? text);
26252
- log$6.warn("legacy chunker invoked (embedding-API safety net)", {
26445
+ log$7.warn("legacy chunker invoked (embedding-API safety net)", {
26253
26446
  chunks: chunks.length,
26254
26447
  totalTokens: tokens,
26255
26448
  maxTokens: 512
@@ -26267,7 +26460,7 @@ var Embedder = class {
26267
26460
  const vector = await this.provider.embed(text);
26268
26461
  return this.toFloat32(vector);
26269
26462
  } catch (error) {
26270
- log$6.error("passage embedding failed", {
26463
+ log$7.error("passage embedding failed", {
26271
26464
  textLength: text.length,
26272
26465
  error
26273
26466
  }, {
@@ -26282,7 +26475,7 @@ var Embedder = class {
26282
26475
  /** Implements embed many as the local embedding generation operation. */
26283
26476
  async embedMany(values) {
26284
26477
  await this.awaitWarmup();
26285
- log$6.debug("batch embedding", { count: values.length }, {
26478
+ log$7.debug("batch embedding", { count: values.length }, {
26286
26479
  event_name: "sno_station_mem.embedding-provider-client.batch.embedding",
26287
26480
  file: "packages/memory/src/engine/extraction/embedding-provider-client.ts",
26288
26481
  function: "embedMany",
@@ -26293,7 +26486,7 @@ var Embedder = class {
26293
26486
  if (vectors.length !== values.length) throw new EmbeddingError(`Embedding provider returned ${vectors.length} vectors for ${values.length} inputs`);
26294
26487
  return vectors.map((v) => this.toFloat32(v));
26295
26488
  } catch (error) {
26296
- log$6.error("batch embedding failed", {
26489
+ log$7.error("batch embedding failed", {
26297
26490
  count: values.length,
26298
26491
  error
26299
26492
  }, {
@@ -26332,14 +26525,14 @@ var Embedder = class {
26332
26525
  if (this.modelDownload) await ensureModelDownloaded(this.modelDownload);
26333
26526
  const vector = await this.provider.embed("warmup");
26334
26527
  if (vector.length !== this.dimensions) throw new EmbeddingError(`Embedder warmup dimension mismatch: expected ${this.dimensions}, got ${vector.length}`);
26335
- log$6.info("embedder warmup ok", { dimensions: this.dimensions }, {
26528
+ log$7.info("embedder warmup ok", { dimensions: this.dimensions }, {
26336
26529
  event_name: "sno_station_mem.embedding-provider-client.embedder.warmup.ok",
26337
26530
  file: "packages/memory/src/engine/extraction/embedding-provider-client.ts",
26338
26531
  function: "<anonymous callback>",
26339
26532
  site_id: "embedding-provider-client.<anonymous callback>.32cc9ec43e"
26340
26533
  });
26341
26534
  } catch (error) {
26342
- log$6.error("embedder warmup failed", { error }, {
26535
+ log$7.error("embedder warmup failed", { error }, {
26343
26536
  event_name: "sno_station_mem.embedding-provider-client.embedder.warmup.failed",
26344
26537
  file: "packages/memory/src/engine/extraction/embedding-provider-client.ts",
26345
26538
  function: "<anonymous callback>",
@@ -26377,7 +26570,7 @@ function createEmbedder(config, stateDir) {
26377
26570
  * run-dir provided via `EVAL_TRACE_DIR` (env). Phase 0 instrumentation
26378
26571
  * (MRR/Recall, gold-rank, miss classification) remains explicitly waived.
26379
26572
  */
26380
- const log$5 = createLogger("sno-station-mem:eval-trace");
26573
+ const log$6 = createLogger("sno-station-mem:eval-trace");
26381
26574
  /**
26382
26575
  * Returns true when the eval-trace surface should write. Reads the env var at
26383
26576
  * call time so harness-flipped values take effect without a re-import.
@@ -26412,7 +26605,7 @@ function appendQaTrace(trace, traceDirOverride) {
26412
26605
  context: currentLogContext()
26413
26606
  })}\n`, "utf8");
26414
26607
  } catch (err) {
26415
- log$5.warn("failed to append qa trace", { error: err }, {
26608
+ log$6.warn("failed to append qa trace", { error: err }, {
26416
26609
  event_name: "sno_station_mem.trace.failed.to.append.qa.trace",
26417
26610
  file: "packages/memory/src/engine/eval/trace.ts",
26418
26611
  function: "appendQaTrace",
@@ -26482,7 +26675,7 @@ function sha256Hex(input) {
26482
26675
  * Tracks memory access patterns to support reinforcement-based decay.
26483
26676
  * Frequently accessed memories decay more slowly (longer effective half-life).
26484
26677
  */
26485
- const log$4 = createLogger("sno-station-mem:access-tracker");
26678
+ const log$5 = createLogger("sno-station-mem:access-tracker");
26486
26679
  const MIN_ACCESS_COUNT = 0;
26487
26680
  const LEGACY_MAX_ACCESS_COUNT = 1e4;
26488
26681
  /** Default debounce interval before flushing pending writes (5 seconds) */
@@ -26631,7 +26824,7 @@ var AccessTracker = class {
26631
26824
  this.clearTimer();
26632
26825
  if (this.flushPromise) await this.flushPromise;
26633
26826
  if (this.pending.size > 0) await this.doFlush();
26634
- if (this.pending.size > 0) log$4.warn("destroying with pending writes", { pendingCount: this.pending.size }, {
26827
+ if (this.pending.size > 0) log$5.warn("destroying with pending writes", { pendingCount: this.pending.size }, {
26635
26828
  event_name: "sno_station_mem.access-tracker.destroying.with.pending.writes",
26636
26829
  file: "packages/memory/src/engine/retrieval/access-tracker.ts",
26637
26830
  function: "destroy",
@@ -26666,7 +26859,7 @@ var AccessTracker = class {
26666
26859
  const failures = (this.writeFailures.get(id) ?? 0) + 1;
26667
26860
  if (failures >= MAX_WRITE_FAILURES_PER_ID) {
26668
26861
  this.writeFailures.delete(id);
26669
- log$4.warn("dropping access delta after repeated write failures", {
26862
+ log$5.warn("dropping access delta after repeated write failures", {
26670
26863
  memory_id: id,
26671
26864
  failures,
26672
26865
  error: err
@@ -26681,7 +26874,7 @@ var AccessTracker = class {
26681
26874
  this.writeFailures.set(id, failures);
26682
26875
  this.pending.set(id, (this.pending.get(id) ?? 0) + delta);
26683
26876
  }
26684
- log$4.warn("access flush batch failed", {
26877
+ log$5.warn("access flush batch failed", {
26685
26878
  batchSize: batch.size,
26686
26879
  error: err,
26687
26880
  committed_count: 0
@@ -26713,7 +26906,7 @@ var AccessTracker = class {
26713
26906
  if (prevLast > 0 && now - prevLast < this.recallLifecycle.accessRateLimitMs) return void 0;
26714
26907
  if (prevCount > ceiling && !this.clampedOnce.has(id)) {
26715
26908
  this.clampedOnce.add(id);
26716
- log$4.warn("legacy access count clamped to ceiling", {
26909
+ log$5.warn("legacy access count clamped to ceiling", {
26717
26910
  memory_id: id,
26718
26911
  previousCount: prevCount,
26719
26912
  ceiling
@@ -27071,7 +27264,7 @@ Object.assign(MemoryRetriever.prototype, {
27071
27264
  });
27072
27265
  //#endregion
27073
27266
  //#region src/engine/retrieval/retrieval-scoring-utils.ts
27074
- const log$3 = createLogger$1("sno-station-mem:retriever");
27267
+ const log$4 = createLogger$1("sno-station-mem:retriever");
27075
27268
  function dotProduct(a, b) {
27076
27269
  let sum = 0;
27077
27270
  const len = a.length;
@@ -27097,6 +27290,19 @@ function collectParallelStage(results, key, fallbackIds) {
27097
27290
  }
27098
27291
  //#endregion
27099
27292
  //#region src/engine/retrieval/retriever-search-modes.ts
27293
+ /**
27294
+ * Ordinary long prose is chunked and averaged by the embedder. Text it cannot cut under the model's limit (a pasted log or hash
27295
+ * dump) is rejected, and would fail the whole recall, so only then is the query cut to the limit; keyword search still sees all of it.
27296
+ */
27297
+ async function embedQuery(embedder, query) {
27298
+ await embedder.warmup();
27299
+ try {
27300
+ return await embedder.embed(query);
27301
+ } catch (error) {
27302
+ if (!(error instanceof EmbeddingError)) throw error;
27303
+ return embedder.embed(embedder.truncateToTokens(query, 512));
27304
+ }
27305
+ }
27100
27306
  Object.assign(MemoryRetriever.prototype, {
27101
27307
  async aggregationComplete(context, trace) {
27102
27308
  trace?.startStage("aggregation_evidence", []);
@@ -27126,7 +27332,7 @@ Object.assign(MemoryRetriever.prototype, {
27126
27332
  aggregationIncomplete: result.aggregationIncomplete
27127
27333
  }));
27128
27334
  trace?.endStage(mapped.map((result) => result.entry.id), mapped.map((result) => result.score));
27129
- log$3.info("aggregation evidence served", {
27335
+ log$4.info("aggregation evidence served", {
27130
27336
  rows: mapped.length,
27131
27337
  scopeRows: mapped[0]?.scopeRowCount ?? 0,
27132
27338
  readingBudget: context.limit,
@@ -27142,7 +27348,7 @@ Object.assign(MemoryRetriever.prototype, {
27142
27348
  return mapped;
27143
27349
  },
27144
27350
  async vectorOnly(context, trace) {
27145
- const vector = await this.embedder.embed(context.query);
27351
+ const vector = await embedQuery(this.embedder, context.query);
27146
27352
  this.throwIfAborted(context);
27147
27353
  const searchLimit = this.config.temporalExpiry ? Math.max(this.config.candidatePoolSize, context.limit * 2) : context.limit;
27148
27354
  trace?.startStage("vector_search", []);
@@ -27185,7 +27391,7 @@ Object.assign(MemoryRetriever.prototype, {
27185
27391
  },
27186
27392
  async precisionRecall(context, trace) {
27187
27393
  trace?.startStage("embed_query", []);
27188
- const queryVector = await this.embedder.embed(context.query);
27394
+ const queryVector = await embedQuery(this.embedder, context.query);
27189
27395
  trace?.endStage([]);
27190
27396
  this.throwIfAborted(context);
27191
27397
  const bm25Query = context.query;
@@ -27218,14 +27424,14 @@ Object.assign(MemoryRetriever.prototype, {
27218
27424
  throw new RetrievalError("Both vector and keyword search failed", vectorSettled.reason);
27219
27425
  }
27220
27426
  let vectorResults = vectorSettled.status === "fulfilled" ? vectorSettled.value : [];
27221
- if (vectorSettled.status === "rejected") log$3.warn("vector search branch failed, using keyword-only", { error: vectorError }, {
27427
+ if (vectorSettled.status === "rejected") log$4.warn("vector search branch failed, using keyword-only", { error: vectorError }, {
27222
27428
  event_name: "memory.retriever_search_modes.diagnostic",
27223
27429
  file: "packages/memory/src/engine/retrieval/retriever-search-modes.ts",
27224
27430
  function: "precisionRecall",
27225
27431
  site_id: "retrieval.retriever-search-modes.precisionRecall.97b8770f50"
27226
27432
  });
27227
27433
  let keywordResults = keywordSettled.status === "fulfilled" ? keywordSettled.value : [];
27228
- if (keywordSettled.status === "rejected") log$3.warn("keyword search branch failed, using vector-only", { error: keywordError }, {
27434
+ if (keywordSettled.status === "rejected") log$4.warn("keyword search branch failed, using vector-only", { error: keywordError }, {
27229
27435
  event_name: "memory.retriever_search_modes.diagnostic",
27230
27436
  file: "packages/memory/src/engine/retrieval/retriever-search-modes.ts",
27231
27437
  function: "precisionRecall",
@@ -27243,7 +27449,7 @@ Object.assign(MemoryRetriever.prototype, {
27243
27449
  trace?.startStage("rrf_fusion", [...vectorResults.map((r) => r.entry.id), ...keywordResults.map((r) => r.entry.id)]);
27244
27450
  let fused = this.rrfFuse(vectorResults, keywordResults);
27245
27451
  trace?.endStage(fused.map((r) => r.entry.id), fused.map((r) => r.score));
27246
- log$3.debug("precision recall search", {
27452
+ log$4.debug("precision recall search", {
27247
27453
  vectorCount: vectorResults.length,
27248
27454
  keywordCount: keywordResults.length,
27249
27455
  fusedCount: fused.length,
@@ -27545,7 +27751,7 @@ Object.assign(MemoryRetriever.prototype, {
27545
27751
  if (this.config.rerank === "lightweight" || !this.config.rerankApiKey) return { candidates: this.rerankLightweight(candidates, queryVector) };
27546
27752
  const rerankEndpoint = this.config.rerankEndpoint ?? RERANK_DEFAULT_ENDPOINTS[provider];
27547
27753
  if (!rerankEndpoint) {
27548
- log$3.warn("rerank skipped: no endpoint for provider", { provider }, {
27754
+ log$4.warn("rerank skipped: no endpoint for provider", { provider }, {
27549
27755
  event_name: "memory.retriever_rerank.diagnostic",
27550
27756
  file: "packages/memory/src/engine/retrieval/retriever-rerank.ts",
27551
27757
  function: "rerank",
@@ -27569,7 +27775,7 @@ Object.assign(MemoryRetriever.prototype, {
27569
27775
  const queryTokens = this.embedder.countTokens(query);
27570
27776
  const documentTokenBudget = 509 - queryTokens;
27571
27777
  if (documentTokenBudget <= 0) {
27572
- log$3.warn("rerank skipped: the query alone fills the reranker token window", {
27778
+ log$4.warn("rerank skipped: the query alone fills the reranker token window", {
27573
27779
  queryTokens,
27574
27780
  window: 512,
27575
27781
  templateTokens: 3
@@ -27593,7 +27799,7 @@ Object.assign(MemoryRetriever.prototype, {
27593
27799
  if (bounded !== c.entry.text) truncatedCandidates += 1;
27594
27800
  return bounded;
27595
27801
  });
27596
- if (truncatedCandidates > 0) log$3.warn("rerank candidates were cut to the document budget the query left", {
27802
+ if (truncatedCandidates > 0) log$4.warn("rerank candidates were cut to the document budget the query left", {
27597
27803
  truncatedCandidates,
27598
27804
  documentTokenBudget,
27599
27805
  queryTokens
@@ -27630,7 +27836,7 @@ Object.assign(MemoryRetriever.prototype, {
27630
27836
  const target = Math.max(1, Math.floor(allowed * localPairTokens / named.inputTokens) - queryTokens);
27631
27837
  return this.embedder.truncateToTokens(text, target);
27632
27838
  });
27633
- log$3.warn("rerank batch refused over its token window; resending the named texts cut to fit", {
27839
+ log$4.warn("rerank batch refused over its token window; resending the named texts cut to fit", {
27634
27840
  status: response.status,
27635
27841
  overLongCount: refusal.overLong.length,
27636
27842
  maxInputTokens: refusal.maxInputTokens,
@@ -27644,7 +27850,7 @@ Object.assign(MemoryRetriever.prototype, {
27644
27850
  });
27645
27851
  continue;
27646
27852
  }
27647
- log$3.warn("rerank API failed, using pre-rerank results", {
27853
+ log$4.warn("rerank API failed, using pre-rerank results", {
27648
27854
  status: response.status,
27649
27855
  endpoint_hash: createHash("sha256").update(rerankEndpoint).digest("hex"),
27650
27856
  retryAfter,
@@ -27661,7 +27867,7 @@ Object.assign(MemoryRetriever.prototype, {
27661
27867
  }
27662
27868
  const parsed = parseRerankResponse(provider, data);
27663
27869
  if (!parsed) {
27664
- log$3.warn("rerank API returned unparseable response", {
27870
+ log$4.warn("rerank API returned unparseable response", {
27665
27871
  provider,
27666
27872
  endpoint_hash: createHash("sha256").update(rerankEndpoint).digest("hex"),
27667
27873
  batchStart: start
@@ -27729,7 +27935,7 @@ Object.assign(MemoryRetriever.prototype, {
27729
27935
  score: clamp01(this.getRerankSourceScore(r) * LIGHTWEIGHT_RERANK_PENALTY, 0)
27730
27936
  }));
27731
27937
  const merged = [...reranked, ...unreturned].sort((a, b) => b.score - a.score);
27732
- log$3.debug("reranked", {
27938
+ log$4.debug("reranked", {
27733
27939
  totalCandidateCount: candidates.length,
27734
27940
  sentToRerankCount: toRerank.length,
27735
27941
  returnedCount: reranked.length,
@@ -27753,7 +27959,7 @@ Object.assign(MemoryRetriever.prototype, {
27753
27959
  let reason;
27754
27960
  if (isRerankTimeout(error)) {
27755
27961
  reason = "timeout";
27756
- log$3.warn("rerank API timed out, using pre-rerank results", {
27962
+ log$4.warn("rerank API timed out, using pre-rerank results", {
27757
27963
  timeoutMs: rerankTimeoutMs,
27758
27964
  attempts: RERANK_TRANSIENT_ATTEMPTS
27759
27965
  }, {
@@ -27764,7 +27970,7 @@ Object.assign(MemoryRetriever.prototype, {
27764
27970
  });
27765
27971
  } else {
27766
27972
  reason = "request_error";
27767
- log$3.warn("rerank error, using pre-rerank results", {
27973
+ log$4.warn("rerank error, using pre-rerank results", {
27768
27974
  error,
27769
27975
  attempts: RERANK_TRANSIENT_ATTEMPTS
27770
27976
  }, {
@@ -27823,7 +28029,7 @@ Object.assign(MemoryRetriever.prototype, {
27823
28029
  }
27824
28030
  };
27825
28031
  });
27826
- if (dimMismatchCount > 0) log$3.warn("lightweight rerank: chunk vector dim mismatch on some candidates", {
28032
+ if (dimMismatchCount > 0) log$4.warn("lightweight rerank: chunk vector dim mismatch on some candidates", {
27827
28033
  dimMismatchCount,
27828
28034
  queryDim: queryVector.length
27829
28035
  }, {
@@ -27832,7 +28038,7 @@ Object.assign(MemoryRetriever.prototype, {
27832
28038
  function: "rerankLightweight",
27833
28039
  site_id: "retrieval.retriever-rerank.rerankLightweight.70eb2031f1"
27834
28040
  });
27835
- log$3.debug("lightweight rerank applied", {
28041
+ log$4.debug("lightweight rerank applied", {
27836
28042
  candidateCount: candidates.length,
27837
28043
  blendedCount,
27838
28044
  fusionWeight: wF,
@@ -28230,7 +28436,7 @@ Object.assign(MemoryRetriever.prototype, {
28230
28436
  ...input,
28231
28437
  signal: memoryOperationSignal(input.signal)
28232
28438
  });
28233
- log$3.info("retrieval started", {
28439
+ log$4.info("retrieval started", {
28234
28440
  queryLength: context.query.length,
28235
28441
  limit: context.limit,
28236
28442
  mode: this.config.mode,
@@ -28304,7 +28510,7 @@ Object.assign(MemoryRetriever.prototype, {
28304
28510
  ...traceMetadata && { traceMetadata }
28305
28511
  });
28306
28512
  }
28307
- log$3.info("Memory retrieval completed", {
28513
+ log$4.info("Memory retrieval completed", {
28308
28514
  store_reference: privateLogReference(this.store.dbPath),
28309
28515
  outcome: results.length ? "success" : "empty_success",
28310
28516
  duration_ms: performance.now() - started,
@@ -28331,7 +28537,7 @@ Object.assign(MemoryRetriever.prototype, {
28331
28537
  });
28332
28538
  return results;
28333
28539
  } catch (error) {
28334
- log$3.error("Memory retrieval failed", {
28540
+ log$4.error("Memory retrieval failed", {
28335
28541
  outcome: "failed",
28336
28542
  error,
28337
28543
  duration_ms: performance.now() - started
@@ -28373,7 +28579,7 @@ Object.assign(MemoryRetriever.prototype, {
28373
28579
  if (!isExplicitAggregation && this._accessTracker && (this._recallLifecycle?.autoRecallAccessTracking === true || context.source === "manual") && results.length > 0) this._accessTracker.recordAccess(results.map((r) => r.entry.id));
28374
28580
  if (!isExplicitAggregation && this._recallLifecycle?.tierPromoter && this._tierPromoter && results.length > 0) await this.evaluateTopKTierTransitions(results);
28375
28581
  if (this._statsCollector) this._statsCollector.recordQuery(finalTrace, context.source ?? "debug");
28376
- log$3.info("Traced memory retrieval completed", {
28582
+ log$4.info("Traced memory retrieval completed", {
28377
28583
  outcome: results.length ? "success" : "empty_success",
28378
28584
  store_reference: privateLogReference(this.store.dbPath),
28379
28585
  duration_ms: performance.now() - started,
@@ -28398,7 +28604,7 @@ Object.assign(MemoryRetriever.prototype, {
28398
28604
  trace: finalTrace
28399
28605
  };
28400
28606
  } catch (error) {
28401
- log$3.error("Traced memory retrieval failed", {
28607
+ log$4.error("Traced memory retrieval failed", {
28402
28608
  outcome: "failed",
28403
28609
  error,
28404
28610
  duration_ms: performance.now() - started
@@ -28461,18 +28667,393 @@ Object.assign(MemoryRetriever.prototype, {
28461
28667
  if (!transition || transition.toTier === tier) continue;
28462
28668
  await this.store.updateTier(entry.id, transition.toTier, { writerAuthority: "offline-family" });
28463
28669
  } catch (error) {
28464
- log$3.warn("tier evaluation failed for memory", {
28465
- memoryId: r.entry.id,
28466
- error
28467
- }, {
28468
- event_name: "memory.retrieval.tier.failed",
28469
- file: "packages/memory/src/engine/retrieval/retriever-execution.ts",
28470
- function: "evaluateTopKTierTransitions",
28471
- site_id: "memory.retrieval.tier.failed"
28472
- });
28670
+ log$4.warn("tier evaluation failed for memory", {
28671
+ memoryId: r.entry.id,
28672
+ error
28673
+ }, {
28674
+ event_name: "memory.retrieval.tier.failed",
28675
+ file: "packages/memory/src/engine/retrieval/retriever-execution.ts",
28676
+ function: "evaluateTopKTierTransitions",
28677
+ site_id: "memory.retrieval.tier.failed"
28678
+ });
28679
+ }
28680
+ }
28681
+ });
28682
+ //#endregion
28683
+ //#region src/sidecar/rem-job-store.ts
28684
+ /** @file rem-job-store.ts
28685
+ * @purpose Persists append-only REM job state transitions.
28686
+ * @boundary Durable local JSONL journal used by the standalone REM sidecar.
28687
+ */
28688
+ const log$3 = createLogger("sno-station-mem:rem-jobs");
28689
+ const remJobStateSchema = z.enum([
28690
+ "queued",
28691
+ "running",
28692
+ "done",
28693
+ "failed",
28694
+ "skipped"
28695
+ ]);
28696
+ const remJobMeasurementsSchema = z.object({
28697
+ rows_considered: z.number().int().nonnegative(),
28698
+ pairs_built: z.number().int().nonnegative().optional(),
28699
+ pair_cap_binding: z.boolean().optional(),
28700
+ model_calls: z.number().int().nonnegative(),
28701
+ model_tokens: z.number().int().nonnegative(),
28702
+ wall_ms: z.number().int().nonnegative()
28703
+ }).strict();
28704
+ const remJobStatsSchema = z.object({
28705
+ operations: z.number().int().nonnegative(),
28706
+ applied_count: z.number().int().nonnegative().optional(),
28707
+ actionable_candidate_count: z.number().int().nonnegative().optional(),
28708
+ applied_fraction: z.number().min(0).max(1).nullable().optional(),
28709
+ scan: z.object({
28710
+ scope: z.string().min(1),
28711
+ candidate_count: z.number().int().nonnegative(),
28712
+ stamped_skipped_count: z.number().int().nonnegative().optional(),
28713
+ actionable_candidate_count: z.number().int().nonnegative().optional()
28714
+ }).strict().optional(),
28715
+ parse_failure_count: z.number().int().nonnegative().optional(),
28716
+ top_refusal_reasons: z.array(z.string().min(1)).max(2).optional(),
28717
+ measured: remJobMeasurementsSchema.optional(),
28718
+ by_operation: z.array(z.object({
28719
+ operation: z.string().min(1),
28720
+ applied_count: z.number().int().nonnegative(),
28721
+ actionable_candidate_count: z.number().int().nonnegative(),
28722
+ candidate_count: z.number().int().nonnegative(),
28723
+ parse_failure_count: z.number().int().nonnegative(),
28724
+ top_refusal_reasons: z.array(z.string().min(1)).max(2),
28725
+ measured: remJobMeasurementsSchema
28726
+ }).strict()).min(1).optional()
28727
+ }).strict();
28728
+ function parseRemJobStats(value) {
28729
+ const parsed = remJobStatsSchema.safeParse(value);
28730
+ return parsed.success ? parsed.data : void 0;
28731
+ }
28732
+ const remWaveJobSchema = z.object({
28733
+ payloadVersion: z.literal(1),
28734
+ waveId: z.string().min(1),
28735
+ correlationId: z.string().min(1),
28736
+ scope: z.string().min(1),
28737
+ requestedOperations: z.array(z.string().min(1)).min(1),
28738
+ state: remJobStateSchema,
28739
+ startedAt: z.string().datetime().nullable(),
28740
+ finishedAt: z.string().datetime().nullable(),
28741
+ stats: remJobStatsSchema,
28742
+ error: z.string().min(1).optional()
28743
+ }).strict();
28744
+ const legacyJobSchema = z.object({
28745
+ job_id: z.string().min(1),
28746
+ state: remJobStateSchema,
28747
+ type: z.string().min(1),
28748
+ scope: z.string().min(1),
28749
+ started_at: z.string().datetime().nullable(),
28750
+ finished_at: z.string().datetime().nullable(),
28751
+ stats: remJobStatsSchema,
28752
+ error: z.string().min(1).optional(),
28753
+ correlation_id: z.string().min(1)
28754
+ }).strict();
28755
+ const OPERATION_ORDER = [
28756
+ "rem-replace",
28757
+ "rem-update",
28758
+ "rem-distill",
28759
+ "rem-retire"
28760
+ ];
28761
+ var RemJobStore = class RemJobStore {
28762
+ journalPath;
28763
+ onDurableTransition;
28764
+ jobs = /* @__PURE__ */ new Map();
28765
+ persistedJobs = /* @__PURE__ */ new Set();
28766
+ mergeKeys = /* @__PURE__ */ new Map();
28767
+ operationQueue = Promise.resolve();
28768
+ journalRecords = 0;
28769
+ constructor(journalPath, onDurableTransition) {
28770
+ this.journalPath = journalPath;
28771
+ this.onDurableTransition = onDurableTransition;
28772
+ }
28773
+ static async open(journalPath, onDurableTransition = () => void 0) {
28774
+ const store = new RemJobStore(journalPath, onDurableTransition);
28775
+ await store.migrateLegacyJournal().catch((error) => store.reportFailure(error));
28776
+ await store.load();
28777
+ return store;
28778
+ }
28779
+ isPersisted(jobId) {
28780
+ return this.persistedJobs.has(jobId);
28781
+ }
28782
+ get(jobId) {
28783
+ const job = this.jobs.get(jobId);
28784
+ return job === void 0 ? void 0 : toRemJob(job);
28785
+ }
28786
+ static async readCompletedJobs(journalPath) {
28787
+ const completed = /* @__PURE__ */ new Map();
28788
+ for await (const line of readJsonlLines(journalPath)) try {
28789
+ const parsed = remWaveJobSchema.parse(JSON.parse(line));
28790
+ if (parsed.state === "done") completed.set(parsed.waveId, toRemJob(parsed));
28791
+ else completed.delete(parsed.waveId);
28792
+ } catch (error) {
28793
+ log$3.error("rem.journal.failed", {
28794
+ error,
28795
+ path: journalPath
28796
+ }, {
28797
+ event_name: "rem.journal.failed",
28798
+ file: "packages/memory/src/sidecar/rem-job-store.ts",
28799
+ function: "readCompletedJobs",
28800
+ site_id: "rem-job-store.readCompletedJobs.invalid-line"
28801
+ });
28802
+ }
28803
+ return [...completed.values()];
28804
+ }
28805
+ nonTerminalJobs() {
28806
+ return Array.from(this.jobs.values()).filter((job) => job.state === "queued" || job.state === "running").map(toRemJob);
28807
+ }
28808
+ async createQueued(type, scope, correlationId) {
28809
+ return this.enqueue(async () => {
28810
+ const requestedOperations = canonicalOperations(typeof type === "string" ? [type] : type);
28811
+ if (requestedOperations.length === 0) throw new Error("REM wave requires at least one supported operation");
28812
+ const mergeKey = `${correlationId}\0${scope}`;
28813
+ const existingId = this.mergeKeys.get(mergeKey);
28814
+ const existing = existingId === void 0 ? void 0 : this.jobs.get(existingId);
28815
+ if (existing !== void 0 && existing.state === "queued") {
28816
+ const mergedOperations = canonicalOperations([...existing.requestedOperations, ...requestedOperations]);
28817
+ if (mergedOperations.length === existing.requestedOperations.length) return {
28818
+ created: false,
28819
+ job: toRemJob(existing)
28820
+ };
28821
+ const updated = remWaveJobSchema.parse({
28822
+ ...existing,
28823
+ requestedOperations: mergedOperations
28824
+ });
28825
+ const durable = await this.persist(updated);
28826
+ this.jobs.set(updated.waveId, updated);
28827
+ if (durable) this.onDurableTransition(toRemJob(updated));
28828
+ return {
28829
+ created: false,
28830
+ job: toRemJob(updated)
28831
+ };
28832
+ }
28833
+ if (existing !== void 0 && existing.state !== "skipped" && existing.state !== "failed" && requestedOperations.every((operation) => existing.requestedOperations.includes(operation))) return {
28834
+ created: false,
28835
+ job: toRemJob(existing)
28836
+ };
28837
+ const job = remWaveJobSchema.parse({
28838
+ payloadVersion: 1,
28839
+ waveId: `rem-wave-${randomUUID()}`,
28840
+ correlationId,
28841
+ scope,
28842
+ requestedOperations,
28843
+ state: "queued",
28844
+ startedAt: null,
28845
+ finishedAt: null,
28846
+ stats: { operations: 0 }
28847
+ });
28848
+ const durable = await this.persist(job);
28849
+ this.jobs.set(job.waveId, job);
28850
+ this.mergeKeys.set(mergeKey, job.waveId);
28851
+ const outward = toRemJob(job);
28852
+ if (durable) this.onDurableTransition(outward);
28853
+ return {
28854
+ created: true,
28855
+ job: outward
28856
+ };
28857
+ });
28858
+ }
28859
+ async transition(jobId, transition) {
28860
+ return this.enqueue(async () => {
28861
+ const current = this.jobs.get(jobId);
28862
+ if (!current) throw new Error(`REM job not found: ${jobId}`);
28863
+ const next = remWaveJobSchema.parse({
28864
+ ...current,
28865
+ state: transition.state,
28866
+ ...transition.started_at === void 0 ? {} : { startedAt: transition.started_at },
28867
+ ...transition.finished_at === void 0 ? {} : { finishedAt: transition.finished_at },
28868
+ ...transition.stats === void 0 ? {} : { stats: transition.stats },
28869
+ ...transition.error === void 0 ? {} : { error: transition.error }
28870
+ });
28871
+ if (next.state === "done") this.jobs.set(jobId, next);
28872
+ const durable = await this.persist(next);
28873
+ this.jobs.set(jobId, next);
28874
+ const outward = toRemJob(next);
28875
+ if (durable) this.onDurableTransition(outward);
28876
+ return outward;
28877
+ });
28878
+ }
28879
+ async load() {
28880
+ if (!existsSync(this.journalPath)) return;
28881
+ await this.removeTornTail();
28882
+ let index = 0;
28883
+ for await (const line of readJsonlLines(this.journalPath)) {
28884
+ index++;
28885
+ this.journalRecords++;
28886
+ try {
28887
+ const job = remWaveJobSchema.parse(JSON.parse(line));
28888
+ this.jobs.set(job.waveId, job);
28889
+ this.mergeKeys.set(`${job.correlationId}\0${job.scope}`, job.waveId);
28890
+ } catch (error) {
28891
+ log$3.error("rem.journal.failed", {
28892
+ path: this.journalPath,
28893
+ line: index,
28894
+ error
28895
+ }, {
28896
+ event_name: "rem.journal.failed",
28897
+ file: "packages/memory/src/sidecar/rem-job-store.ts",
28898
+ function: "load",
28899
+ site_id: "rem-job-store.load.invalid-line"
28900
+ });
28901
+ }
28902
+ }
28903
+ if (this.journalRecords >= 256) await this.compact().catch((error) => this.reportFailure(error));
28904
+ }
28905
+ async removeTornTail() {
28906
+ const handle = await open(this.journalPath, "r");
28907
+ let committed = (await handle.stat()).size;
28908
+ const buffer = Buffer.alloc(65536);
28909
+ try {
28910
+ while (committed > 0) {
28911
+ const start = Math.max(0, committed - buffer.length);
28912
+ const { bytesRead } = await handle.read(buffer, 0, committed - start, start);
28913
+ const newline = buffer.subarray(0, bytesRead).lastIndexOf(10);
28914
+ if (newline >= 0) {
28915
+ committed = start + newline + 1;
28916
+ break;
28917
+ }
28918
+ committed = start;
28919
+ }
28920
+ if (committed < (await handle.stat()).size) await this.truncateJournal(committed);
28921
+ } finally {
28922
+ await handle.close();
28923
+ }
28924
+ }
28925
+ async compact() {
28926
+ const cutoff = (/* @__PURE__ */ new Date(Date.now() - 1728e5)).toISOString();
28927
+ const retained = [...this.jobs.values()].filter((job) => job.state === "queued" || job.state === "running" || (job.finishedAt ?? job.startedAt ?? "") >= cutoff);
28928
+ const temporary = `${this.journalPath}.${randomUUID()}.tmp`;
28929
+ const handle = await open(temporary, "wx", 384);
28930
+ try {
28931
+ try {
28932
+ for (const job of retained) await handle.writeFile(`${JSON.stringify(job)}\n`, "utf8");
28933
+ await handle.sync();
28934
+ } finally {
28935
+ await handle.close();
28936
+ }
28937
+ await rename(temporary, this.journalPath);
28938
+ await syncDirectory$1(path.dirname(this.journalPath));
28939
+ const retainedIds = new Set(retained.map((job) => job.waveId));
28940
+ for (const id of this.jobs.keys()) if (!retainedIds.has(id)) {
28941
+ this.jobs.delete(id);
28942
+ this.persistedJobs.delete(id);
28943
+ }
28944
+ for (const [key, id] of this.mergeKeys) if (!retainedIds.has(id)) this.mergeKeys.delete(key);
28945
+ this.journalRecords = 0;
28946
+ } finally {
28947
+ await rm(temporary, { force: true });
28948
+ }
28949
+ }
28950
+ async migrateLegacyJournal() {
28951
+ const legacyPath = path.join(path.dirname(this.journalPath), "rem-jobs.jsonl");
28952
+ if (!existsSync(legacyPath)) return;
28953
+ const latest = /* @__PURE__ */ new Map();
28954
+ for await (const line of readJsonlLines(legacyPath)) try {
28955
+ const parsed = JSON.parse(line);
28956
+ const job = legacyJobSchema.parse(parsed);
28957
+ latest.set(job.job_id, job);
28958
+ } catch (error) {
28959
+ const reason = error instanceof Error ? error.message : String(error);
28960
+ this.reportFailure(reason);
28961
+ }
28962
+ const interrupted = [...latest.values()].filter((job) => job.state === "queued" || job.state === "running");
28963
+ if (interrupted.length > 0) {
28964
+ const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
28965
+ const handle = await open(legacyPath, "a");
28966
+ try {
28967
+ for (const job of interrupted) await handle.writeFile(`${JSON.stringify({
28968
+ ...job,
28969
+ state: "failed",
28970
+ finished_at: finishedAt,
28971
+ error: "sidecar_upgrade_interrupted"
28972
+ })}\n`, "utf8");
28973
+ await handle.sync();
28974
+ } finally {
28975
+ await handle.close();
28976
+ }
28473
28977
  }
28978
+ await rename(legacyPath, path.join(path.dirname(legacyPath), "rem-jobs.v0.jsonl"));
28474
28979
  }
28475
- });
28980
+ reportFailure(error) {
28981
+ log$3.error("rem.journal.failed", {
28982
+ error,
28983
+ path: this.journalPath
28984
+ }, {
28985
+ event_name: "rem.journal.failed",
28986
+ file: "packages/memory/src/sidecar/rem-job-store.ts",
28987
+ function: "reportFailure",
28988
+ site_id: "rem.journal.failed"
28989
+ });
28990
+ }
28991
+ async persist(job) {
28992
+ this.persistedJobs.delete(job.waveId);
28993
+ await this.append(job);
28994
+ this.persistedJobs.add(job.waveId);
28995
+ this.journalRecords++;
28996
+ if (this.journalRecords >= 256) {
28997
+ this.jobs.set(job.waveId, job);
28998
+ await this.compact().catch((error) => this.reportFailure(error));
28999
+ }
29000
+ return true;
29001
+ }
29002
+ async truncateJournal(length) {
29003
+ const handle = await open(this.journalPath, "r+");
29004
+ try {
29005
+ await handle.truncate(length);
29006
+ await handle.sync();
29007
+ } finally {
29008
+ await handle.close();
29009
+ }
29010
+ }
29011
+ async append(job) {
29012
+ const parent = path.dirname(this.journalPath);
29013
+ await mkdir(parent, { recursive: true });
29014
+ const journalAlreadyExisted = existsSync(this.journalPath);
29015
+ const handle = await open(this.journalPath, "a");
29016
+ try {
29017
+ await handle.writeFile(`${JSON.stringify(job)}\n`, "utf8");
29018
+ await handle.sync();
29019
+ } finally {
29020
+ await handle.close();
29021
+ }
29022
+ if (!journalAlreadyExisted) await syncDirectory$1(parent);
29023
+ }
29024
+ enqueue(operation) {
29025
+ const result = this.operationQueue.then(operation);
29026
+ this.operationQueue = result.then(() => void 0, () => void 0);
29027
+ return result;
29028
+ }
29029
+ };
29030
+ function canonicalOperations(operations) {
29031
+ const unique = new Set(operations);
29032
+ return OPERATION_ORDER.filter((operation) => unique.has(operation));
29033
+ }
29034
+ function toRemJob(job) {
29035
+ return {
29036
+ job_id: job.waveId,
29037
+ state: job.state,
29038
+ type: job.requestedOperations[0] ?? "rem-replace",
29039
+ scope: job.scope,
29040
+ started_at: job.startedAt,
29041
+ finished_at: job.finishedAt,
29042
+ stats: job.stats,
29043
+ ...job.error === void 0 ? {} : { error: job.error },
29044
+ correlation_id: job.correlationId,
29045
+ requested_operations: [...job.requestedOperations]
29046
+ };
29047
+ }
29048
+ async function syncDirectory$1(directory) {
29049
+ if (process.platform === "win32") return;
29050
+ const handle = await open(directory, "r");
29051
+ try {
29052
+ await handle.sync();
29053
+ } finally {
29054
+ await handle.close();
29055
+ }
29056
+ }
28476
29057
  //#endregion
28477
29058
  //#region src/sidecar/rem-entry-foundations.ts
28478
29059
  const sha256Schema = z.string().regex(/^[0-9a-f]{64}$/u);
@@ -29355,6 +29936,10 @@ async function runRemProductionOrderedWave(input) {
29355
29936
  agentPort: input.agentPort,
29356
29937
  ...input.implementationVersion === void 0 ? {} : { implementationVersion: input.implementationVersion }
29357
29938
  });
29939
+ if (result.hostRefusal) {
29940
+ if (!(result.actionsApplied > 0 || repository.listJournal(waveId).some((entry) => entry.actionsApplied > 0 || entry.outcome === "no-action" && entry.verdicts > 0))) throw result.hostRefusal;
29941
+ refusal = result.hostRefusal;
29942
+ }
29358
29943
  results.push({
29359
29944
  operation: jobType,
29360
29945
  ...result
@@ -29363,7 +29948,10 @@ async function runRemProductionOrderedWave(input) {
29363
29948
  }
29364
29949
  }]
29365
29950
  }))[jobType] === "failed") stageFailed = true;
29366
- if (refusal) throw refusal;
29951
+ if (refusal) {
29952
+ if (refusal.destination !== "host" || stageFailed) throw refusal;
29953
+ break;
29954
+ }
29367
29955
  }
29368
29956
  } finally {
29369
29957
  journalDatabase.db.close();
@@ -29394,7 +29982,7 @@ async function runRemProductionOrderedWave(input) {
29394
29982
  candidateCount: Math.max(0, ...results.map((result) => result.candidateCount)),
29395
29983
  stampedSkippedCount: results.reduce((total, result) => total + result.stampedSkippedCount, 0),
29396
29984
  parseFailureCount: results.reduce((total, result) => total + result.parseFailureCount, 0),
29397
- topRefusalReasons: [...new Set(results.flatMap((result) => result.topRefusalReasons))].slice(0, 2),
29985
+ topRefusalReasons: [.../* @__PURE__ */ new Set([...refusal ? [`host-refused:${refusal.callId}`] : [], ...results.flatMap((result) => result.topRefusalReasons)])].slice(0, 2),
29398
29986
  measurements: {
29399
29987
  rowsConsidered: Math.max(0, ...results.map((result) => result.measurements.rowsConsidered)),
29400
29988
  pairsBuilt: results.reduce((total, result) => total + result.measurements.pairsBuilt, 0),
@@ -29499,7 +30087,7 @@ async function runRemBatchJobUnlocked(input) {
29499
30087
  terminalState: "done",
29500
30088
  appliedFraction,
29501
30089
  parseFailureCount,
29502
- topRefusalReasons
30090
+ topRefusalReasons: [...result.hostRefusal ? [`host-refused:${result.hostRefusal.callId}`] : [], ...topRefusalReasons].slice(0, 2)
29503
30091
  };
29504
30092
  return completed;
29505
30093
  } finally {
@@ -29739,230 +30327,264 @@ function createRemUpdateBudget(configuration) {
29739
30327
  };
29740
30328
  }
29741
30329
  function recordRemUpdateModelCall(budget, prompt) {
30330
+ if (budget.modelCalls >= (budget.configuration?.budgets.maxPairs ?? 0)) return false;
29742
30331
  budget.modelCalls += 1;
29743
30332
  budget.tokens += countTokens(prompt) + REM_MODEL_OUTPUT_TOKEN_CAP;
30333
+ return true;
29744
30334
  }
29745
30335
  async function runUpdate(input) {
29746
30336
  const budget = createRemUpdateBudget(input.configuration);
29747
- const candidates = input.candidates;
29748
- const ports = createSnoStationMemRemPorts({
29749
- database: input.runtime.database,
29750
- memoryStore: input.runtime.store
29751
- });
29752
- const configurationSha256 = input.configuration === void 0 ? "0".repeat(64) : deriveRemConfigurationSha256(input.configuration);
29753
- const recovery = createSnoStationMemRemRecovery(input.runtime.database);
29754
- const mutationExecutor = createSnoStationMemRemMutationExecutor({
29755
- database: input.runtime.database,
29756
- jobType: input.jobType,
29757
- configurationSha256,
29758
- liveContentionRetries: input.configuration?.retries.liveContentionRetries ?? 0,
29759
- recovery,
29760
- applyTextVersion: (write, verification) => ports.conflict.writeTextVersion({
29761
- ...write,
29762
- verification
29763
- })
29764
- });
29765
- await mutationExecutor.recoverPendingAttempts();
29766
- const relationEffects = await runProductionUpdateRelations({
29767
- ...input,
29768
- budget,
29769
- candidates,
29770
- configurationSha256,
29771
- mutationExecutor,
29772
- recovery
29773
- });
29774
- let actionsApplied = relationEffects.actionsApplied;
29775
- let verdicts = relationEffects.verdicts;
29776
- let llmCalls = relationEffects.llmCalls;
29777
- let successfulLlmCalls = relationEffects.successfulLlmCalls;
29778
- let lastRefusalReason = relationEffects.lastRefusalReason;
29779
- const stampedRowIds = new Set(input.stampedRows.map((row) => row.id));
29780
- const owned = candidates.filter((candidate) => candidate.owner === "restate" && !stampedRowIds.has(candidate.id) && !relationEffects.handledRowIds.has(candidate.id));
29781
- for (const stampedRow of input.stampedRows) {
29782
- if (relationEffects.handledRowIds.has(stampedRow.id)) continue;
29783
- appendRefusal(input.repository, input.jobId, input.jobType, stampedRow.id, "already_stamped");
29784
- }
29785
- for (const [index, candidate] of owned.entries()) {
29786
- const claimed = claimCandidate(input.repository, candidate, "restate");
29787
- if (!claimed) continue;
29788
- log$1.info("update_progress", {
29789
- job_id: input.jobId,
29790
- current: index + 1,
29791
- total: owned.length,
29792
- row_id: candidate.id
29793
- }, {
29794
- event_name: "sno_station_mem.rem-batch-executor.update.progress",
29795
- file: "packages/memory/src/sidecar/rem-batch-executor.ts",
29796
- function: "runUpdate",
29797
- site_id: "rem-batch-executor.runUpdate.dbf2a836db"
30337
+ try {
30338
+ const candidates = input.candidates;
30339
+ const ports = createSnoStationMemRemPorts({
30340
+ database: input.runtime.database,
30341
+ memoryStore: input.runtime.store
29798
30342
  });
29799
- try {
29800
- if (candidate.state === "ambiguous") {
29801
- lastRefusalReason = "ambiguous_unresolved";
29802
- appendRefusal(input.repository, input.jobId, input.jobType, candidate.id, "ambiguous_unresolved");
29803
- completeCandidate(input.repository, claimed, candidate.content_hash, input.jobId, input.jobType);
29804
- continue;
29805
- }
29806
- if (candidate.state !== "transition") {
29807
- lastRefusalReason = "row_not_owned";
29808
- releaseCandidate(input.repository, claimed);
29809
- appendRefusal(input.repository, input.jobId, input.jobType, candidate.id, "row_not_owned");
29810
- continue;
29811
- }
29812
- if (candidate.category !== "profile" && candidate.category !== "episodic" && candidate.category !== "state") {
29813
- lastRefusalReason = "missing_row_kind";
29814
- releaseCandidate(input.repository, claimed);
29815
- appendRefusal(input.repository, input.jobId, input.jobType, candidate.id, "missing_row_kind");
29816
- continue;
29817
- }
29818
- const enumeratedMembers = enumerateRemUpdateMembers(candidate.text);
29819
- const prompt = renderRemUpdateJudgmentPrompt({
29820
- judgmentSkill: REM_UPDATE_JUDGMENT_SKILL.rewrite,
29821
- source: candidate.text,
29822
- ...enumeratedMembers.length === 0 ? {} : { enumeratedMembers }
29823
- });
29824
- recordRemUpdateModelCall(budget, prompt);
29825
- llmCalls += 1;
29826
- const reply = await completeJsonStage(input.runtime, "rem-update-judgment", prompt);
29827
- const decision = decideRemUpdateFromReply(typeof reply === "string" ? reply : JSON.stringify(reply) ?? "");
29828
- log$1.info("update_decision_evaluated", {
30343
+ const configurationSha256 = input.configuration === void 0 ? "0".repeat(64) : deriveRemConfigurationSha256(input.configuration);
30344
+ const recovery = createSnoStationMemRemRecovery(input.runtime.database);
30345
+ const mutationExecutor = createSnoStationMemRemMutationExecutor({
30346
+ database: input.runtime.database,
30347
+ jobType: input.jobType,
30348
+ configurationSha256,
30349
+ liveContentionRetries: input.configuration?.retries.liveContentionRetries ?? 0,
30350
+ recovery,
30351
+ applyTextVersion: (write, verification) => ports.conflict.writeTextVersion({
30352
+ ...write,
30353
+ verification
30354
+ })
30355
+ });
30356
+ await mutationExecutor.recoverPendingAttempts();
30357
+ const relationEffects = await runProductionUpdateRelations({
30358
+ ...input,
30359
+ budget,
30360
+ candidates,
30361
+ configurationSha256,
30362
+ mutationExecutor,
30363
+ recovery
30364
+ });
30365
+ let actionsApplied = relationEffects.actionsApplied;
30366
+ let verdicts = relationEffects.verdicts;
30367
+ let llmCalls = relationEffects.llmCalls;
30368
+ let successfulLlmCalls = relationEffects.successfulLlmCalls;
30369
+ let lastRefusalReason = relationEffects.lastRefusalReason;
30370
+ const stampedRowIds = new Set(input.stampedRows.map((row) => row.id));
30371
+ const owned = candidates.filter((candidate) => candidate.owner === "restate" && !stampedRowIds.has(candidate.id) && !relationEffects.handledRowIds.has(candidate.id));
30372
+ for (const stampedRow of input.stampedRows) {
30373
+ if (relationEffects.handledRowIds.has(stampedRow.id)) continue;
30374
+ appendRefusal(input.repository, input.jobId, input.jobType, stampedRow.id, "already_stamped");
30375
+ }
30376
+ for (const [index, candidate] of owned.entries()) {
30377
+ const claimed = claimCandidate(input.repository, candidate, "restate");
30378
+ if (!claimed) continue;
30379
+ log$1.info("update_progress", {
29829
30380
  job_id: input.jobId,
29830
- row_id: candidate.id,
29831
- outcome: decision.outcome,
29832
- ...decision.outcome === "refuse" ? { reason: decision.reason } : { retired_value_count: decision.retiredValues.length }
30381
+ current: index + 1,
30382
+ total: owned.length,
30383
+ row_id: candidate.id
29833
30384
  }, {
29834
- event_name: "sno_station_mem.rem-batch-executor.update.decision.evaluated",
30385
+ event_name: "sno_station_mem.rem-batch-executor.update.progress",
29835
30386
  file: "packages/memory/src/sidecar/rem-batch-executor.ts",
29836
30387
  function: "runUpdate",
29837
- site_id: "rem-batch-executor.runUpdate.7dfc387204"
30388
+ site_id: "rem-batch-executor.runUpdate.dbf2a836db"
29838
30389
  });
29839
- if (decision.outcome === "refuse") {
29840
- if (decision.reason !== "model_response_invalid") {
29841
- successfulLlmCalls += 1;
29842
- if (decision.reason !== "no_retired_fact") verdicts += 1;
30390
+ try {
30391
+ if (candidate.state === "ambiguous") {
30392
+ lastRefusalReason = "ambiguous_unresolved";
30393
+ appendRefusal(input.repository, input.jobId, input.jobType, candidate.id, "ambiguous_unresolved");
30394
+ completeCandidate(input.repository, claimed, candidate.content_hash, input.jobId, input.jobType);
30395
+ continue;
29843
30396
  }
29844
- lastRefusalReason = decision.reason;
29845
- releaseCandidate(input.repository, claimed);
29846
- appendRefusal(input.repository, input.jobId, input.jobType, candidate.id, decision.reason);
29847
- continue;
29848
- }
29849
- successfulLlmCalls += 1;
29850
- const verificationPrompt = renderRemUpdateVerificationPrompt({
29851
- judgmentSkill: REM_UPDATE_JUDGMENT_SKILL.verification,
29852
- source: candidate.text,
29853
- proposedCurrent: decision.proposedCurrent,
29854
- retiredValues: decision.retiredValues
29855
- });
29856
- recordRemUpdateModelCall(budget, verificationPrompt);
29857
- llmCalls += 1;
29858
- const verificationReply = await completeJsonStage(input.runtime, "rem-update-verification", verificationPrompt);
29859
- const verification = decideRemUpdateVerification(typeof verificationReply === "string" ? verificationReply : JSON.stringify(verificationReply) ?? "");
29860
- log$1.info("update_verification_evaluated", {
29861
- job_id: input.jobId,
29862
- row_id: candidate.id,
29863
- outcome: verification.outcome,
29864
- ...verification.outcome === "refuse" ? { reason: verification.reason } : {}
29865
- }, {
29866
- event_name: "sno_station_mem.rem-batch-executor.update.verification.evaluated",
29867
- file: "packages/memory/src/sidecar/rem-batch-executor.ts",
29868
- function: "runUpdate",
29869
- site_id: "rem-batch-executor.runUpdate.7ffc737c24"
29870
- });
29871
- if (verification.outcome === "refuse") {
29872
- if (verification.reason !== "model_response_invalid") {
29873
- successfulLlmCalls += 1;
29874
- verdicts += 1;
30397
+ if (candidate.state !== "transition") {
30398
+ lastRefusalReason = "row_not_owned";
30399
+ releaseCandidate(input.repository, claimed);
30400
+ appendRefusal(input.repository, input.jobId, input.jobType, candidate.id, "row_not_owned");
30401
+ continue;
29875
30402
  }
29876
- lastRefusalReason = verification.reason;
29877
- releaseCandidate(input.repository, claimed);
29878
- appendRefusal(input.repository, input.jobId, input.jobType, candidate.id, verification.reason);
29879
- continue;
29880
- }
29881
- successfulLlmCalls += 1;
29882
- verdicts += 1;
29883
- const rewriteConfig = {
29884
- implementationVersion: input.implementationVersion ?? "rem-update-v1",
29885
- memoryKind: candidate.category,
29886
- locale: candidate.locale,
29887
- localeResource: getRemUpdateLocaleResource(candidate.locale)
29888
- };
29889
- const updateStamp = deriveRemUpdateStamp({
29890
- source: candidate.text,
29891
- ...rewriteConfig
29892
- });
29893
- const evidenceId = `rem-update:${input.jobId}:${candidate.id}:rewrite`;
29894
- input.runtime.database.prepare(`INSERT OR REPLACE INTO nodix_rem_write_verdicts(
30403
+ if (candidate.category !== "profile" && candidate.category !== "episodic" && candidate.category !== "state") {
30404
+ lastRefusalReason = "missing_row_kind";
30405
+ releaseCandidate(input.repository, claimed);
30406
+ appendRefusal(input.repository, input.jobId, input.jobType, candidate.id, "missing_row_kind");
30407
+ continue;
30408
+ }
30409
+ const enumeratedMembers = enumerateRemUpdateMembers(candidate.text);
30410
+ const prompt = renderRemUpdateJudgmentPrompt({
30411
+ judgmentSkill: REM_UPDATE_JUDGMENT_SKILL.rewrite,
30412
+ source: candidate.text,
30413
+ ...enumeratedMembers.length === 0 ? {} : { enumeratedMembers }
30414
+ });
30415
+ if (!recordRemUpdateModelCall(budget, prompt)) {
30416
+ releaseCandidate(input.repository, claimed);
30417
+ break;
30418
+ }
30419
+ llmCalls += 1;
30420
+ const reply = await completeJsonStage(input.runtime, "rem-update-judgment", prompt);
30421
+ const decision = decideRemUpdateFromReply(typeof reply === "string" ? reply : JSON.stringify(reply) ?? "");
30422
+ log$1.info("update_decision_evaluated", {
30423
+ job_id: input.jobId,
30424
+ row_id: candidate.id,
30425
+ outcome: decision.outcome,
30426
+ ...decision.outcome === "refuse" ? { reason: decision.reason } : { retired_value_count: decision.retiredValues.length }
30427
+ }, {
30428
+ event_name: "sno_station_mem.rem-batch-executor.update.decision.evaluated",
30429
+ file: "packages/memory/src/sidecar/rem-batch-executor.ts",
30430
+ function: "runUpdate",
30431
+ site_id: "rem-batch-executor.runUpdate.7dfc387204"
30432
+ });
30433
+ if (decision.outcome === "refuse") {
30434
+ if (decision.reason !== "model_response_invalid") {
30435
+ successfulLlmCalls += 1;
30436
+ if (decision.reason !== "no_retired_fact") verdicts += 1;
30437
+ }
30438
+ lastRefusalReason = decision.reason;
30439
+ releaseCandidate(input.repository, claimed);
30440
+ appendRefusal(input.repository, input.jobId, input.jobType, candidate.id, decision.reason);
30441
+ continue;
30442
+ }
30443
+ successfulLlmCalls += 1;
30444
+ const verificationPrompt = renderRemUpdateVerificationPrompt({
30445
+ judgmentSkill: REM_UPDATE_JUDGMENT_SKILL.verification,
30446
+ source: candidate.text,
30447
+ proposedCurrent: decision.proposedCurrent,
30448
+ retiredValues: decision.retiredValues
30449
+ });
30450
+ if (!recordRemUpdateModelCall(budget, verificationPrompt)) {
30451
+ releaseCandidate(input.repository, claimed);
30452
+ break;
30453
+ }
30454
+ llmCalls += 1;
30455
+ const verificationReply = await completeJsonStage(input.runtime, "rem-update-verification", verificationPrompt);
30456
+ const verification = decideRemUpdateVerification(typeof verificationReply === "string" ? verificationReply : JSON.stringify(verificationReply) ?? "");
30457
+ log$1.info("update_verification_evaluated", {
30458
+ job_id: input.jobId,
30459
+ row_id: candidate.id,
30460
+ outcome: verification.outcome,
30461
+ ...verification.outcome === "refuse" ? { reason: verification.reason } : {}
30462
+ }, {
30463
+ event_name: "sno_station_mem.rem-batch-executor.update.verification.evaluated",
30464
+ file: "packages/memory/src/sidecar/rem-batch-executor.ts",
30465
+ function: "runUpdate",
30466
+ site_id: "rem-batch-executor.runUpdate.7ffc737c24"
30467
+ });
30468
+ if (verification.outcome === "refuse") {
30469
+ if (verification.reason !== "model_response_invalid") {
30470
+ successfulLlmCalls += 1;
30471
+ verdicts += 1;
30472
+ }
30473
+ lastRefusalReason = verification.reason;
30474
+ releaseCandidate(input.repository, claimed);
30475
+ appendRefusal(input.repository, input.jobId, input.jobType, candidate.id, verification.reason);
30476
+ continue;
30477
+ }
30478
+ successfulLlmCalls += 1;
30479
+ verdicts += 1;
30480
+ const rewriteConfig = {
30481
+ implementationVersion: input.implementationVersion ?? "rem-update-v1",
30482
+ memoryKind: candidate.category,
30483
+ locale: candidate.locale,
30484
+ localeResource: getRemUpdateLocaleResource(candidate.locale)
30485
+ };
30486
+ const updateStamp = deriveRemUpdateStamp({
30487
+ source: candidate.text,
30488
+ ...rewriteConfig
30489
+ });
30490
+ const evidenceId = `rem-update:${input.jobId}:${candidate.id}:rewrite`;
30491
+ input.runtime.database.prepare(`INSERT OR REPLACE INTO nodix_rem_write_verdicts(
29895
30492
  evidence_id, winner_row_id, loser_row_id, target_row_id,
29896
30493
  retired_fact_atoms_json, recorded_at
29897
30494
  ) VALUES (?, ?, ?, ?, ?, ?)`).run(evidenceId, candidate.id, candidate.id, candidate.id, JSON.stringify([candidate.text]), ports.clock.now());
29898
- const handle = await mutationExecutor.openAttempt({
29899
- jobId: input.jobId,
29900
- stage: "rem-update",
29901
- rowId: candidate.id,
29902
- writer: "writeTextVersion",
29903
- authorization: {
30495
+ const handle = await mutationExecutor.openAttempt({
30496
+ jobId: input.jobId,
30497
+ stage: "rem-update",
29904
30498
  rowId: candidate.id,
29905
- preWriteContentSha256: candidate.content_hash,
29906
- proposedTextSha256: sha256(decision.proposedCurrent),
29907
- evidenceId,
29908
- configurationSha256
30499
+ writer: "writeTextVersion",
30500
+ authorization: {
30501
+ rowId: candidate.id,
30502
+ preWriteContentSha256: candidate.content_hash,
30503
+ proposedTextSha256: sha256(decision.proposedCurrent),
30504
+ evidenceId,
30505
+ configurationSha256
30506
+ }
30507
+ });
30508
+ const resolution = await mutationExecutor.mutateAttempt(handle, {
30509
+ kind: "writeTextVersion",
30510
+ replacementText: decision.proposedCurrent,
30511
+ historyText: candidate.text,
30512
+ supersededItems: decision.retiredValues,
30513
+ sourceVersion: candidate.text,
30514
+ rewriteConfig,
30515
+ idempotencyKey: updateStamp,
30516
+ reason: "REM update accepted the model judgment and mechanical source checks.",
30517
+ timestamp: ports.clock.now()
30518
+ });
30519
+ const outcome = await mutationExecutor.closeAttempt(handle, resolution);
30520
+ if (outcome.outcome !== "succeeded") {
30521
+ releaseCandidate(input.repository, claimed);
30522
+ appendRefusal(input.repository, input.jobId, input.jobType, candidate.id, outcome.reasonCode ?? "mutation_failed");
30523
+ continue;
29909
30524
  }
29910
- });
29911
- const resolution = await mutationExecutor.mutateAttempt(handle, {
29912
- kind: "writeTextVersion",
29913
- replacementText: decision.proposedCurrent,
29914
- historyText: candidate.text,
29915
- supersededItems: decision.retiredValues,
29916
- sourceVersion: candidate.text,
29917
- rewriteConfig,
29918
- idempotencyKey: updateStamp,
29919
- reason: "REM update accepted the model judgment and mechanical source checks.",
29920
- timestamp: ports.clock.now()
29921
- });
29922
- const outcome = await mutationExecutor.closeAttempt(handle, resolution);
29923
- if (outcome.outcome !== "succeeded") {
30525
+ if (outcome.postWriteContentSha256 === null) throw new Error("successful REM write attempt omitted its post-write content hash");
30526
+ actionsApplied += 1;
30527
+ completeCandidate(input.repository, claimed, outcome.postWriteContentSha256, input.jobId, input.jobType);
30528
+ } catch (error) {
29924
30529
  releaseCandidate(input.repository, claimed);
29925
- appendRefusal(input.repository, input.jobId, input.jobType, candidate.id, outcome.reasonCode ?? "mutation_failed");
29926
- continue;
30530
+ throw error;
29927
30531
  }
29928
- if (outcome.postWriteContentSha256 === null) throw new Error("successful REM write attempt omitted its post-write content hash");
29929
- actionsApplied += 1;
29930
- completeCandidate(input.repository, claimed, outcome.postWriteContentSha256, input.jobId, input.jobType);
29931
- } catch (error) {
29932
- releaseCandidate(input.repository, claimed);
29933
- throw error;
29934
30532
  }
30533
+ if (llmCalls > 0 && successfulLlmCalls === 0) throw new Error(`REM LLM calls all failed: ${lastRefusalReason ?? input.runtime.llm.getLastError() ?? "unknown"}`);
30534
+ const updateRows = appliedUnitStatistics(owned.length, actionsApplied - relationEffects.actionsApplied);
30535
+ const relationPairs = appliedUnitStatistics(relationEffects.pairsScanned, relationEffects.actionsApplied);
30536
+ input.repository.appendJournal(input.jobId, input.jobType, {
30537
+ stage: "update-rows",
30538
+ outcome: updateRows.applied > 0 ? "done" : "no-action",
30539
+ pairsScanned: updateRows.considered,
30540
+ verdicts: 0,
30541
+ actionsApplied: updateRows.applied
30542
+ });
30543
+ input.repository.appendJournal(input.jobId, input.jobType, {
30544
+ stage: "update-relation-pairs",
30545
+ outcome: relationPairs.applied > 0 ? "done" : "no-action",
30546
+ pairsScanned: relationPairs.considered,
30547
+ verdicts: 0,
30548
+ actionsApplied: relationPairs.applied
30549
+ });
30550
+ return {
30551
+ ...journal(input.jobType, relationEffects.pairsScanned, verdicts, actionsApplied),
30552
+ updateRows,
30553
+ relationPairs,
30554
+ measurements: {
30555
+ rowsConsidered: candidates.length,
30556
+ pairsBuilt: 0,
30557
+ pairCapBinding: budget.modelCalls >= (input.configuration?.budgets.maxPairs ?? 0),
30558
+ modelCalls: budget.modelCalls,
30559
+ modelTokens: budget.tokens,
30560
+ wallMs: Date.now() - budget.startedAtMs
30561
+ }
30562
+ };
30563
+ } catch (error) {
30564
+ if (!(error instanceof ModelCallRefusedError) || error.destination !== "host") throw error;
30565
+ const entries = input.repository.listJournal(input.jobId, input.jobType);
30566
+ const applied = input.runtime.database.prepare(`SELECT count(*) AS count FROM (
30567
+ SELECT row_id FROM nodix_rem_write_attempts
30568
+ WHERE job_id = ? AND stage LIKE 'rem-update%' AND outcome = 'succeeded'
30569
+ AND writer IN ('softClose', 'writeTextVersion')
30570
+ UNION
30571
+ SELECT json_extract(detail, '$.targetRowId') FROM nodix_rem_journal
30572
+ WHERE job_id = ? AND job_type = 'rem-update' AND outcome = 'done'
30573
+ AND stage LIKE 'update-retirement-target:%'
30574
+ )`).get(input.jobId, input.jobId);
30575
+ return {
30576
+ ...journal(input.jobType, entries.reduce((total, entry) => total + entry.pairsScanned, 0), entries.reduce((total, entry) => total + entry.verdicts, 0), applied.count),
30577
+ hostRefusal: error,
30578
+ measurements: {
30579
+ rowsConsidered: input.candidates.length,
30580
+ pairsBuilt: 0,
30581
+ pairCapBinding: true,
30582
+ modelCalls: budget.modelCalls,
30583
+ modelTokens: budget.tokens,
30584
+ wallMs: Date.now() - budget.startedAtMs
30585
+ }
30586
+ };
29935
30587
  }
29936
- if (llmCalls > 0 && successfulLlmCalls === 0) throw new Error(`REM LLM calls all failed: ${lastRefusalReason ?? input.runtime.llm.getLastError() ?? "unknown"}`);
29937
- const updateRows = appliedUnitStatistics(owned.length, actionsApplied - relationEffects.actionsApplied);
29938
- const relationPairs = appliedUnitStatistics(relationEffects.pairsScanned, relationEffects.actionsApplied);
29939
- input.repository.appendJournal(input.jobId, input.jobType, {
29940
- stage: "update-rows",
29941
- outcome: updateRows.applied > 0 ? "done" : "no-action",
29942
- pairsScanned: updateRows.considered,
29943
- verdicts: 0,
29944
- actionsApplied: updateRows.applied
29945
- });
29946
- input.repository.appendJournal(input.jobId, input.jobType, {
29947
- stage: "update-relation-pairs",
29948
- outcome: relationPairs.applied > 0 ? "done" : "no-action",
29949
- pairsScanned: relationPairs.considered,
29950
- verdicts: 0,
29951
- actionsApplied: relationPairs.applied
29952
- });
29953
- return {
29954
- ...journal(input.jobType, relationEffects.pairsScanned, verdicts, actionsApplied),
29955
- updateRows,
29956
- relationPairs,
29957
- measurements: {
29958
- rowsConsidered: candidates.length,
29959
- pairsBuilt: 0,
29960
- pairCapBinding: false,
29961
- modelCalls: budget.modelCalls,
29962
- modelTokens: budget.tokens,
29963
- wallMs: Date.now() - budget.startedAtMs
29964
- }
29965
- };
29966
30588
  }
29967
30589
  function appliedUnitStatistics(considered, applied) {
29968
30590
  return {
@@ -29991,6 +30613,7 @@ async function runProductionUpdateRelations(input) {
29991
30613
  let successfulLlmCalls = 0;
29992
30614
  let verdicts = 0;
29993
30615
  for (let workIndex = 0; workIndex < relationWork.length; workIndex += 1) {
30616
+ if (input.budget.modelCalls >= input.configuration.budgets.maxPairs) break;
29994
30617
  const work = relationWork[workIndex];
29995
30618
  if (work === void 0) continue;
29996
30619
  const { candidates } = work;
@@ -29998,6 +30621,7 @@ async function runProductionUpdateRelations(input) {
29998
30621
  let retirementApplied = retirementPair !== void 0;
29999
30622
  const nominatedRows = candidates.filter((candidate) => candidate.owner === "restate" || candidate.state === "pure-negation").sort((left, right) => compareMemorySourceOrder(rowOrderKey(right), rowOrderKey(left)) || right.id.localeCompare(left.id));
30000
30623
  for (const nominatedRow of retirementPair === void 0 ? nominatedRows : []) {
30624
+ if (input.budget.modelCalls >= input.configuration.budgets.maxPairs) break;
30001
30625
  if (handledRowIds.has(nominatedRow.id)) continue;
30002
30626
  const candidateSet = await buildRemRetirementCandidateSet({
30003
30627
  runtime: input.runtime,
@@ -30059,7 +30683,7 @@ async function runProductionUpdateRelations(input) {
30059
30683
  predecessorTexts: [prior.text],
30060
30684
  successorTexts: linked.map((candidate) => candidate.text)
30061
30685
  });
30062
- recordRemUpdateModelCall(input.budget, prompt);
30686
+ if (!recordRemUpdateModelCall(input.budget, prompt)) break;
30063
30687
  llmCalls += 1;
30064
30688
  const reply = await completeTextStage(input.runtime, "rem-update-relation-judgment", prompt);
30065
30689
  let relation = decideRemUpdateRelationFromReply(reply ?? "");
@@ -30099,7 +30723,7 @@ async function runProductionUpdateRelations(input) {
30099
30723
  source: prior.text,
30100
30724
  supersedingText: relationCandidate.text
30101
30725
  });
30102
- recordRemUpdateModelCall(input.budget, rewritePrompt);
30726
+ if (!recordRemUpdateModelCall(input.budget, rewritePrompt)) break;
30103
30727
  llmCalls += 1;
30104
30728
  const rewriteReply = await completeJsonStage(input.runtime, "rem-update-judgment", rewritePrompt);
30105
30729
  const rewrite = decideRemUpdateFromReply(typeof rewriteReply === "string" ? rewriteReply : JSON.stringify(rewriteReply) ?? "");
@@ -30120,7 +30744,7 @@ async function runProductionUpdateRelations(input) {
30120
30744
  proposedCurrent: rewrite.proposedCurrent,
30121
30745
  retiredValues: rewrite.retiredValues
30122
30746
  });
30123
- recordRemUpdateModelCall(input.budget, verificationPrompt);
30747
+ if (!recordRemUpdateModelCall(input.budget, verificationPrompt)) break;
30124
30748
  llmCalls += 1;
30125
30749
  const verificationReply = await completeJsonStage(input.runtime, "rem-update-verification", verificationPrompt);
30126
30750
  const verification = decideRemUpdateVerification(typeof verificationReply === "string" ? verificationReply : JSON.stringify(verificationReply) ?? "");
@@ -30152,7 +30776,7 @@ async function runProductionUpdateRelations(input) {
30152
30776
  retiredValues: [prior.text],
30153
30777
  retractionText: retraction.text
30154
30778
  });
30155
- recordRemUpdateModelCall(input.budget, verificationPrompt);
30779
+ if (!recordRemUpdateModelCall(input.budget, verificationPrompt)) break;
30156
30780
  llmCalls += 1;
30157
30781
  const verificationReply = await completeJsonStage(input.runtime, "rem-update-verification", verificationPrompt);
30158
30782
  const verification = decideRemUpdateVerification(typeof verificationReply === "string" ? verificationReply : JSON.stringify(verificationReply) ?? "");
@@ -30370,7 +30994,7 @@ async function runRemRetirementTargetGate(input) {
30370
30994
  text: candidate.text
30371
30995
  }))
30372
30996
  });
30373
- recordRemUpdateModelCall(input.budget, prompt);
30997
+ if (!recordRemUpdateModelCall(input.budget, prompt)) break;
30374
30998
  llmCalls += 1;
30375
30999
  let reply = null;
30376
31000
  try {
@@ -31145,7 +31769,19 @@ async function runReplace(input) {
31145
31769
  claimToken: candidate.claimToken
31146
31770
  }));
31147
31771
  }
31148
- throw error;
31772
+ if (!(error instanceof ModelCallRefusedError) || error.destination !== "host") throw error;
31773
+ return {
31774
+ ...journal(input.jobType, pairsScanned, verdicts, actionsApplied),
31775
+ hostRefusal: error,
31776
+ measurements: {
31777
+ rowsConsidered: input.candidates.length,
31778
+ pairsBuilt: indexedPairs.length,
31779
+ pairCapBinding: true,
31780
+ modelCalls: llmCalls,
31781
+ modelTokens,
31782
+ wallMs: Date.now() - startedAtMs
31783
+ }
31784
+ };
31149
31785
  }
31150
31786
  for (const candidate of participatingClaimed) {
31151
31787
  const modified = completedContentHashes.has(candidate.id) || closedRowIds.has(candidate.id);
@@ -31303,7 +31939,7 @@ async function buildRemReplaceCandidateQueue(input) {
31303
31939
  vectors = lookup.readVectors(projectCurrent.map((row) => row.id));
31304
31940
  currentVectors.set(candidate.project_id, vectors);
31305
31941
  }
31306
- found = pairWithNearest(pairs, candidate, projectCurrent, vector, vectors, 10) || found;
31942
+ found = pairWithNearest(pairs, candidate, projectCurrent, vector, vectors, 3) || found;
31307
31943
  }
31308
31944
  matches = await lookup.searchSemantic(vector, {
31309
31945
  category: candidate.category,
@@ -32015,6 +32651,28 @@ async function evaluateScope(input, auditStateDir, state, scope, candidateCount,
32015
32651
  }
32016
32652
  const correlationId = remAutomaticCorrelationId(trigger, scope, trigger === "daily" ? nextDue.toISOString() : localDate);
32017
32653
  const priorAttempts = scopeState.attempts.identity === correlationId ? scopeState.attempts.count : 0;
32654
+ if (priorAttempts >= 3) {
32655
+ await recordDecision(auditStateDir, scope, "skipped", {
32656
+ row: "attempt-limit",
32657
+ trigger,
32658
+ local_date: localDate,
32659
+ correlation_id: correlationId,
32660
+ attempts: priorAttempts
32661
+ });
32662
+ const nextState = replaceScopeState(state, scope, {
32663
+ ...scopeState,
32664
+ last_volume_pass_date: localDate,
32665
+ attempts: {
32666
+ identity: null,
32667
+ count: 0
32668
+ }
32669
+ });
32670
+ await writeRemTriggerStateAtomic(input.stateDir, nextState);
32671
+ return {
32672
+ state: nextState,
32673
+ dispatched: false
32674
+ };
32675
+ }
32018
32676
  await recordDecision(auditStateDir, scope, "ok", {
32019
32677
  row: "dispatch",
32020
32678
  trigger,
@@ -32109,6 +32767,40 @@ async function dispatchRemWave(input, scope, correlationId) {
32109
32767
  });
32110
32768
  }
32111
32769
  async function applyCompletedBaselines(auditStateDir, triggerStateDir, state) {
32770
+ const durableCorrelations = /* @__PURE__ */ new Set();
32771
+ try {
32772
+ const completedJobs = await RemJobStore.readCompletedJobs(path.join(triggerStateDir, "rem-wave-jobs.jsonl"));
32773
+ for (const job of completedJobs) {
32774
+ durableCorrelations.add(job.correlation_id);
32775
+ const current = state.scopes[job.scope];
32776
+ if (!current || !job.correlation_id.startsWith("rem-auto-") || !job.started_at) continue;
32777
+ const localDate = localDateAt(new Date(job.started_at), current.schedule_zone);
32778
+ const measured = job.stats.measured;
32779
+ const completedScope = {
32780
+ ...current,
32781
+ last_pass_at: job.started_at > current.last_pass_at ? job.started_at : current.last_pass_at,
32782
+ last_volume_pass_date: localDate > (current.last_volume_pass_date ?? "") ? localDate : current.last_volume_pass_date,
32783
+ ...current.attempts.identity === job.correlation_id ? { attempts: {
32784
+ identity: null,
32785
+ count: 0
32786
+ } } : {},
32787
+ ...measured?.pair_cap_binding === false ? { last_covered_count: measured.rows_considered } : {}
32788
+ };
32789
+ if (JSON.stringify(current) === JSON.stringify(completedScope)) continue;
32790
+ state = replaceScopeState(state, job.scope, completedScope);
32791
+ await writeRemTriggerStateAtomic(triggerStateDir, state);
32792
+ }
32793
+ } catch (error) {
32794
+ log.warn("REM completion jobs could not be read", {
32795
+ error,
32796
+ outcome: "partial"
32797
+ }, {
32798
+ event_name: "sidecar.trigger.completion_jobs.read.failed",
32799
+ file: "packages/memory/src/sidecar/rem-trigger.ts",
32800
+ function: "applyCompletedBaselines",
32801
+ site_id: "sidecar.trigger.completion_jobs.read.failed"
32802
+ });
32803
+ }
32112
32804
  let content;
32113
32805
  try {
32114
32806
  content = await readAuditTail(getAuditPath(auditStateDir));
@@ -32127,7 +32819,6 @@ async function applyCompletedBaselines(auditStateDir, triggerStateDir, state) {
32127
32819
  return state;
32128
32820
  }
32129
32821
  const dispatched = /* @__PURE__ */ new Map();
32130
- const triesByCorrelation = /* @__PURE__ */ new Map();
32131
32822
  let nextState = state;
32132
32823
  let malformedCount = 0;
32133
32824
  for (const line of content.split("\n")) {
@@ -32149,28 +32840,6 @@ async function applyCompletedBaselines(auditStateDir, triggerStateDir, state) {
32149
32840
  });
32150
32841
  continue;
32151
32842
  }
32152
- if (entry["event"] === "rem_skipped" && typeof correlationId === "string") {
32153
- const skipped = dispatched.get(correlationId);
32154
- const tries = (triesByCorrelation.get(correlationId) ?? 0) + (typeof details?.["destination"] === "string" ? 1 : 0);
32155
- triesByCorrelation.set(correlationId, tries);
32156
- const skippedScopeState = skipped ? nextState.scopes[skipped.scope] : void 0;
32157
- if (skipped && skippedScopeState && tries >= 3 && isLocalDate(skipped.localDate) && (skippedScopeState.last_volume_pass_date ?? "") < skipped.localDate) nextState = replaceScopeState(nextState, skipped.scope, {
32158
- ...skippedScopeState,
32159
- last_volume_pass_date: skipped.localDate,
32160
- attempts: {
32161
- identity: null,
32162
- count: 0
32163
- }
32164
- });
32165
- else if (skipped && skippedScopeState?.attempts.identity === correlationId) nextState = replaceScopeState(nextState, skipped.scope, {
32166
- ...skippedScopeState,
32167
- attempts: {
32168
- identity: null,
32169
- count: 0
32170
- }
32171
- });
32172
- continue;
32173
- }
32174
32843
  if (entry["event"] === "rem_failed" && details?.["source"] === "sidecar" && typeof correlationId === "string") {
32175
32844
  const failed = dispatched.get(correlationId);
32176
32845
  const failedScopeState = failed ? nextState.scopes[failed.scope] : void 0;
@@ -32184,15 +32853,15 @@ async function applyCompletedBaselines(auditStateDir, triggerStateDir, state) {
32184
32853
  });
32185
32854
  continue;
32186
32855
  }
32187
- if (entry["event"] !== "rem_completed" || typeof correlationId !== "string") continue;
32856
+ if (entry["event"] !== "rem_completed" || typeof correlationId !== "string" || durableCorrelations.has(correlationId)) continue;
32188
32857
  const dispatch = dispatched.get(correlationId);
32189
32858
  if (dispatch === void 0 || state.scopes[dispatch.scope] === void 0 || entry["scope"] !== dispatch.scope) continue;
32190
32859
  const currentScopeState = requiredScopeState(nextState, dispatch.scope);
32191
32860
  let completedScopeState = {
32192
32861
  ...currentScopeState,
32193
- ...dispatch.trigger === "daily" && isValidInstant(dispatch.passAt) ? { last_pass_at: dispatch.passAt } : {},
32194
- ...dispatch.trigger === "daily" && isLocalDate(dispatch.localDate) ? { last_volume_pass_date: dispatch.localDate } : {},
32195
- ...dispatch.trigger === "volume" && isLocalDate(dispatch.localDate) ? { last_volume_pass_date: dispatch.localDate } : {},
32862
+ ...dispatch.trigger === "daily" && isValidInstant(dispatch.passAt) && dispatch.passAt > currentScopeState.last_pass_at ? { last_pass_at: dispatch.passAt } : {},
32863
+ ...dispatch.trigger === "daily" && isLocalDate(dispatch.localDate) && dispatch.localDate > (currentScopeState.last_volume_pass_date ?? "") ? { last_volume_pass_date: dispatch.localDate } : {},
32864
+ ...dispatch.trigger === "volume" && isLocalDate(dispatch.localDate) && dispatch.localDate > (currentScopeState.last_volume_pass_date ?? "") ? { last_volume_pass_date: dispatch.localDate } : {},
32196
32865
  ...currentScopeState.attempts.identity === correlationId ? { attempts: {
32197
32866
  identity: null,
32198
32867
  count: 0
@@ -32200,7 +32869,7 @@ async function applyCompletedBaselines(auditStateDir, triggerStateDir, state) {
32200
32869
  };
32201
32870
  const measured = asRecord(asRecord(details?.["stats"])?.["measured"]);
32202
32871
  const rowsConsidered = measured?.["rows_considered"];
32203
- if (measured?.["pair_cap_binding"] === false && Number.isInteger(rowsConsidered) && Number(rowsConsidered) >= 0) completedScopeState = {
32872
+ if ((!isValidInstant(dispatch.passAt) || dispatch.passAt >= currentScopeState.last_pass_at) && measured?.["pair_cap_binding"] === false && Number.isInteger(rowsConsidered) && Number(rowsConsidered) >= 0) completedScopeState = {
32204
32873
  ...completedScopeState,
32205
32874
  last_covered_count: Number(rowsConsidered)
32206
32875
  };
@@ -32396,4 +33065,4 @@ function errorMessage(error) {
32396
33065
  return error instanceof Error ? error.message : String(error);
32397
33066
  }
32398
33067
  //#endregion
32399
- export { compareMemorySourceOrder as $, ATOMIC_EXTRACTION_SKILL as $t, recordTokenCounter as A, classifyLlmFailure as At, validateMemoryTelemetryMetadata as B, truncateGraphemes as Bt, resolveHookAgentId as C, createScopePolicy as Ct, closeAtomicArrivalRetirementTargets as D, createLlmClient as Dt, recordAtomicEntityIdentityJournal as E, ModelCallRefusedError as Et, AtomicExtractionCompletedElsewhere as F, clamp01 as Ft, parseRemOperationType as G, normalizeIsoDateTimeString as Gt, readChunkVecTableState as H, buildInsightMetadata as Ht, normalizeMemoryRelationPredicate as I, clampInt as It, orderAdapterAPair as J, AGGREGATION_OPERATIONS as Jt, adapterAViewFromRecord as K, parseIsoDateTimeMs as Kt, MemoryStore as L, stableHash as Lt, canonicalizeProfileSectionName as M, CODING_SKIN_LEDGER_MAX_CHARS as Mt, normalizeTopicToSectionName as N, CODING_SKIN_SESSION_QUERY as Nt, journalAtomicArrivalRetirementRefusal as O, isTerminalLlmFailure as Ot, getSnoStationMemDataDir as P, HOST_MODEL_DEADLINE_HEADER as Pt, renderAdapterAPrompt as Q, resolveLlmRoute as Qt, StaleSupersedeTargetError as R, stripHtmlTags as Rt, resolveTaskLifecycleEffectiveTime as S, MemoryScopePolicy as St, normalizeEntityName as T, LlmClientTerminalError as Tt, loadSqliteVecExtension as U, parseInsightMetadata as Ut, randomUUID$1 as V, redactSecrets as Vt, REM_BUILT_OPERATION_TYPES as W, stringifyInsightMetadata as Wt, prospectiveProfileView as X, normalizeCategory as Xt, parseAdapterAChatVerdict as Y, MEMORY_CATEGORIES as Yt, renderAdapterAChatPrompt as Z, pickLlmRoutingConfig as Zt, findUniqueActiveTaskByDescription as _, memoryFileWrite as _n, ALL_RESOURCES as _t, enumerateRemCandidateScopes as a, relations as an, runWithMemoryAudit as at, admitTaskLifecycleAssertion as b, forwardObserveLedgerUntilIdle as bn, SnoStationMemError as bt, runRemProductionOrderedWave as c, isCalendarLabel as cn, readJsonlLines as ct, MemoryRetriever as d, MEMORY_TELEMETRY_DELETE_REASONS as dn, resolveMemoryDate as dt, ATOMIC_EXTRACTION_SKILL_HASH as en, readMemorySourceOrderOrOldest as et, DEFAULT_RETRIEVAL_CONFIG as f, isMemoryTelemetryEventType as fn, unresolvedMemoryDate as ft, buildTaskLifecycleCandidateSet as g, checkMemoryOperation as gn, normalizeAmbientLearningText as gt, acceptsTaskLifecycleJudgment as h, openSqliteDatabaseReadonly as hn, stripEnvelopeMetadata as ht, createRemModelStageResponsePort as i, state_vocabulary_default as in, getAuditPath as it, canExtractorWrite as j, isTerminalLlmFailure$1 as jt, readAtomicArrivalRetirementCandidateSet as k, withProviderResponses as kt, runWithCanonicalStoreWriteMutex as l, sessionZoneCarriedBy as ln, parseSessionTimestamp as lt, Embedder as m, openSqliteDatabase as mn, buildIndexedText as mt, remNeedsHost as n, modelReplyJsonCandidates as nn, appendAuditEntryStrict as nt, readCandidates as o, calculateCalendarTime as on, runWithMemoryAuditSync as ot, AccessTracker as p, initSqliteRuntime as pn, boundSectionContent as pt, isAdapterAVerdictReply as q, deriveFactKey as qt, buildRemReplaceCandidateQueue as r, readModelReplyJson as rn, currentMutationAttemptId as rt, runRemBatchJob as s, calendarInstructionSchema as sn, runWithMutationAttempt as st, evaluateRemAutomaticTriggers as t, atomicExtractionSkillReference as tn, appendAuditEntry as tt, selectRemUpdateRelationCandidates as u, attribute_dictionary_default as un, serializeIntervalMetadata as ut, resolveTaskLifecycle as v, memoryOperationSignal as vn, RESOURCES_BY_LOCALE as vt, listAtomicMemoryEntityCandidates as w, parseAgentIdFromSessionKey$1 as wt, buildTaskLifecycleCommandClaim as x, StorageError as xt, taskLifecycleCandidateSetVersion as y, withMemoryOperation as yn, RetrievalError as yt, TaskLifecycleStaleResolutionError as z, stripRoleLabelPrefix as zt };
33068
+ export { renderAdapterAChatPrompt as $, MEMORY_CATEGORIES as $t, journalAtomicArrivalRetirementRefusal as A, getAuditPath as At, StaleSupersedeTargetError as B, debugContentPreview as Bt, buildTaskLifecycleCommandClaim as C, withMemoryOperation as Cn, withProviderResponses as Ct, normalizeEntityName as D, appendAuditEntryStrict as Dt, listAtomicMemoryEntityCandidates as E, appendAuditEntry as Et, normalizeTopicToSectionName as F, CODING_SKIN_LEDGER_MAX_CHARS as Ft, loadSqliteVecExtension as G, redactSecrets as Gt, validateMemoryTelemetryMetadata as H, stripHtmlTags as Ht, getSnoStationMemDataDir as I, CODING_SKIN_SESSION_QUERY as It, adapterAViewFromRecord as J, stringifyInsightMetadata as Jt, REM_BUILT_OPERATION_TYPES as K, buildInsightMetadata as Kt, AtomicExtractionCompletedElsewhere as L, HOST_MODEL_DEADLINE_HEADER as Lt, recordTokenCounter as M, runWithMemoryAuditSync as Mt, canExtractorWrite as N, runWithMutationAttempt as Nt, recordAtomicEntityIdentityJournal as O, compactAuditLog as Ot, canonicalizeProfileSectionName as P, readJsonlLines as Pt, prospectiveProfileView as Q, AGGREGATION_OPERATIONS as Qt, normalizeMemoryRelationPredicate as R, clamp01 as Rt, admitTaskLifecycleAssertion as S, memoryOperationSignal as Sn, isTerminalLlmFailure as St, resolveHookAgentId as T, isTerminalLlmFailure$1 as Tt, randomUUID$1 as U, stripRoleLabelPrefix as Ut, TaskLifecycleStaleResolutionError as V, stableHash as Vt, readChunkVecTableState as W, truncateGraphemes as Wt, orderAdapterAPair as X, parseIsoDateTimeMs as Xt, isAdapterAVerdictReply as Y, normalizeIsoDateTimeString as Yt, parseAdapterAChatVerdict as Z, deriveFactKey as Zt, acceptsTaskLifecycleJudgment as _, initSqliteRuntime as _n, createScopePolicy as _t, enumerateRemCandidateScopes as a, atomicExtractionSkillReference as an, resolveMemoryDate as at, resolveTaskLifecycle as b, checkMemoryOperation as bn, ModelCallRefusedError as bt, runRemProductionOrderedWave as c, state_vocabulary_default as cn, buildIndexedText as ct, RemJobStore as d, calendarInstructionSchema as dn, ALL_RESOURCES as dt, normalizeCategory as en, renderAdapterAPrompt as et, parseRemJobStats as f, isCalendarLabel as fn, RESOURCES_BY_LOCALE as ft, Embedder as g, isMemoryTelemetryEventType as gn, MemoryScopePolicy as gt, AccessTracker as h, MEMORY_TELEMETRY_DELETE_REASONS as hn, StorageError as ht, createRemModelStageResponsePort as i, ATOMIC_EXTRACTION_SKILL_HASH as in, serializeIntervalMetadata as it, readAtomicArrivalRetirementCandidateSet as j, runWithMemoryAudit as jt, closeAtomicArrivalRetirementTargets as k, currentMutationAttemptId as kt, runWithCanonicalStoreWriteMutex as l, relations as ln, stripEnvelopeMetadata as lt, DEFAULT_RETRIEVAL_CONFIG as m, attribute_dictionary_default as mn, SnoStationMemError as mt, remNeedsHost as n, resolveLlmRoute as nn, readMemorySourceOrderOrOldest as nt, readCandidates as o, modelReplyJsonCandidates as on, unresolvedMemoryDate as ot, MemoryRetriever as p, sessionZoneCarriedBy as pn, RetrievalError as pt, parseRemOperationType as q, parseInsightMetadata as qt, buildRemReplaceCandidateQueue as r, ATOMIC_EXTRACTION_SKILL as rn, parseSessionTimestamp as rt, runRemBatchJob as s, readModelReplyJson as sn, boundSectionContent as st, evaluateRemAutomaticTriggers as t, pickLlmRoutingConfig as tn, compareMemorySourceOrder as tt, selectRemUpdateRelationCandidates as u, calculateCalendarTime as un, normalizeAmbientLearningText as ut, buildTaskLifecycleCandidateSet as v, openSqliteDatabase as vn, parseAgentIdFromSessionKey$1 as vt, resolveTaskLifecycleEffectiveTime as w, forwardObserveLedgerUntilIdle as wn, classifyLlmFailure as wt, taskLifecycleCandidateSetVersion as x, memoryFileWrite as xn, createLlmClient as xt, findUniqueActiveTaskByDescription as y, openSqliteDatabaseReadonly as yn, LlmClientTerminalError as yt, MemoryStore as z, clampInt as zt };