openclaw-hybrid-memory 2026.5.310 → 2026.6.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 (535) hide show
  1. package/api/plugin-runtime.ts +2 -0
  2. package/backends/facts-db/contradictions.ts +1 -1
  3. package/cli/cmd-extract-directives.ts +225 -11
  4. package/cli/cmd-extract-proposals.ts +5 -6
  5. package/cli/cmd-extract-reinforcement.ts +71 -0
  6. package/cli/cmd-feedback.ts +15 -9
  7. package/cli/commands/manage/register-reflection-pipeline.ts +247 -13
  8. package/cli/commands/manage/register-storage-maintenance.ts +224 -15
  9. package/cli/commands/manage/storage-stats-helpers.ts +13 -2
  10. package/cli/context.ts +9 -19
  11. package/cli/distill.ts +31 -1
  12. package/cli/register.ts +28 -38
  13. package/dist/api/plugin-runtime.js.map +1 -1
  14. package/dist/backends/agent-health-store.js.map +1 -1
  15. package/dist/backends/apitap-store.js.map +1 -1
  16. package/dist/backends/audit-store.js.map +1 -1
  17. package/dist/backends/base-sqlite-store.js.map +1 -1
  18. package/dist/backends/cost-tracker.js.map +1 -1
  19. package/dist/backends/credentials-db.js +2 -3
  20. package/dist/backends/credentials-db.js.map +1 -1
  21. package/dist/backends/crystallization-store.js.map +1 -1
  22. package/dist/backends/edict-store.js.map +1 -1
  23. package/dist/backends/event-bus.js.map +1 -1
  24. package/dist/backends/event-log.js.map +1 -1
  25. package/dist/backends/facts-db/cache-manager.js.map +1 -1
  26. package/dist/backends/facts-db/clusters.js.map +1 -1
  27. package/dist/backends/facts-db/contradictions.js +1 -1
  28. package/dist/backends/facts-db/contradictions.js.map +1 -1
  29. package/dist/backends/facts-db/crud.js.map +1 -1
  30. package/dist/backends/facts-db/db-connection.js.map +1 -1
  31. package/dist/backends/facts-db/entity-autolink.js.map +1 -1
  32. package/dist/backends/facts-db/entity-layer.js.map +1 -1
  33. package/dist/backends/facts-db/episodes.js.map +1 -1
  34. package/dist/backends/facts-db/fact-queries.js.map +1 -1
  35. package/dist/backends/facts-db/fact-read-queries.js.map +1 -1
  36. package/dist/backends/facts-db/facts-db-layer1.js.map +1 -1
  37. package/dist/backends/facts-db/facts-db-layer2.js.map +1 -1
  38. package/dist/backends/facts-db/facts-db-layer3.js.map +1 -1
  39. package/dist/backends/facts-db/fts-text.js.map +1 -1
  40. package/dist/backends/facts-db/generated-skills/policy.js.map +1 -1
  41. package/dist/backends/facts-db/generated-skills.js.map +1 -1
  42. package/dist/backends/facts-db/housekeeping.js.map +1 -1
  43. package/dist/backends/facts-db/links.js.map +1 -1
  44. package/dist/backends/facts-db/maintenance.js.map +1 -1
  45. package/dist/backends/facts-db/procedures/crud.js.map +1 -1
  46. package/dist/backends/facts-db/procedures/internal.js.map +1 -1
  47. package/dist/backends/facts-db/procedures/promotion.js.map +1 -1
  48. package/dist/backends/facts-db/procedures/search.js.map +1 -1
  49. package/dist/backends/facts-db/procedures/stats.js.map +1 -1
  50. package/dist/backends/facts-db/reinforcement.js.map +1 -1
  51. package/dist/backends/facts-db/row-mapper.js.map +1 -1
  52. package/dist/backends/facts-db/scan-cursors.js.map +1 -1
  53. package/dist/backends/facts-db/schema-bootstrap.js.map +1 -1
  54. package/dist/backends/facts-db/scope-sql.js.map +1 -1
  55. package/dist/backends/facts-db/search.js.map +1 -1
  56. package/dist/backends/facts-db/stats.js.map +1 -1
  57. package/dist/backends/facts-db/types.js.map +1 -1
  58. package/dist/backends/facts-db/variants.js.map +1 -1
  59. package/dist/backends/identity-reflection-store.js.map +1 -1
  60. package/dist/backends/issue-store.js.map +1 -1
  61. package/dist/backends/learnings-db.js.map +1 -1
  62. package/dist/backends/migrations/facts-migrations.js.map +1 -1
  63. package/dist/backends/migrations/procedures.js.map +1 -1
  64. package/dist/backends/narratives-db.js.map +1 -1
  65. package/dist/backends/persona-state-store.js.map +1 -1
  66. package/dist/backends/proposals-db.js.map +1 -1
  67. package/dist/backends/scope-filter-sql.js.map +1 -1
  68. package/dist/backends/sqlite-schema-meta.js.map +1 -1
  69. package/dist/backends/tool-proposal-store.js.map +1 -1
  70. package/dist/backends/vector-db/constants.js.map +1 -1
  71. package/dist/backends/vector-db/path-utils.js.map +1 -1
  72. package/dist/backends/vector-db/runtime-locks.js.map +1 -1
  73. package/dist/backends/vector-db/vector-db-class.js.map +1 -1
  74. package/dist/backends/wal.js.map +1 -1
  75. package/dist/backends/workflow-store.js.map +1 -1
  76. package/dist/benchmark/shadow-eval.js.map +1 -1
  77. package/dist/cli/active-tasks.js.map +1 -1
  78. package/dist/cli/backup.js.map +1 -1
  79. package/dist/cli/benchmark.js.map +1 -1
  80. package/dist/cli/cmd-backfill.js.map +1 -1
  81. package/dist/cli/cmd-config.js.map +1 -1
  82. package/dist/cli/cmd-credentials.js.map +1 -1
  83. package/dist/cli/cmd-demo.js.map +1 -1
  84. package/dist/cli/cmd-distill.js.map +1 -1
  85. package/dist/cli/cmd-doctor.js.map +1 -1
  86. package/dist/cli/cmd-examples.js.map +1 -1
  87. package/dist/cli/cmd-extract-daily.js.map +1 -1
  88. package/dist/cli/cmd-extract-directives.js +141 -10
  89. package/dist/cli/cmd-extract-directives.js.map +1 -1
  90. package/dist/cli/cmd-extract-procedures.js.map +1 -1
  91. package/dist/cli/cmd-extract-proposals.js +3 -2
  92. package/dist/cli/cmd-extract-proposals.js.map +1 -1
  93. package/dist/cli/cmd-extract-reinforcement.js +39 -0
  94. package/dist/cli/cmd-extract-reinforcement.js.map +1 -1
  95. package/dist/cli/cmd-extract-sessions.js.map +1 -1
  96. package/dist/cli/cmd-feedback.js +9 -4
  97. package/dist/cli/cmd-feedback.js.map +1 -1
  98. package/dist/cli/cmd-health.js.map +1 -1
  99. package/dist/cli/cmd-providers.js.map +1 -1
  100. package/dist/cli/cmd-selfcorrection.js.map +1 -1
  101. package/dist/cli/cmd-setup.js.map +1 -1
  102. package/dist/cli/cmd-status.js.map +1 -1
  103. package/dist/cli/cmd-store.js.map +1 -1
  104. package/dist/cli/cmd-user-friendly.js.map +1 -1
  105. package/dist/cli/cmd-verify.js.map +1 -1
  106. package/dist/cli/commands/manage/bindings.js.map +1 -1
  107. package/dist/cli/commands/manage/dream-cycle-followup.js.map +1 -1
  108. package/dist/cli/commands/manage/maintenance-heartbeat.js.map +1 -1
  109. package/dist/cli/commands/manage/register-agents-audit-runall.js.map +1 -1
  110. package/dist/cli/commands/manage/register-analyze-maintenance-logs.js.map +1 -1
  111. package/dist/cli/commands/manage/register-budget-proposals.js.map +1 -1
  112. package/dist/cli/commands/manage/register-config-cli.js.map +1 -1
  113. package/dist/cli/commands/manage/register-corrections-and-pipeline.js.map +1 -1
  114. package/dist/cli/commands/manage/register-corrections.js.map +1 -1
  115. package/dist/cli/commands/manage/register-council.js.map +1 -1
  116. package/dist/cli/commands/manage/register-credentials-scope.js.map +1 -1
  117. package/dist/cli/commands/manage/register-digest.js.map +1 -1
  118. package/dist/cli/commands/manage/register-lifecycle.js.map +1 -1
  119. package/dist/cli/commands/manage/register-procedure-lifecycle.js.map +1 -1
  120. package/dist/cli/commands/manage/register-reconcile-cron-ledgers.js.map +1 -1
  121. package/dist/cli/commands/manage/register-reflection-pipeline.js +144 -7
  122. package/dist/cli/commands/manage/register-reflection-pipeline.js.map +1 -1
  123. package/dist/cli/commands/manage/register-self-correction-feedback.js.map +1 -1
  124. package/dist/cli/commands/manage/register-storage-and-stats.js.map +1 -1
  125. package/dist/cli/commands/manage/register-storage-entities-decay.js.map +1 -1
  126. package/dist/cli/commands/manage/register-storage-graph-audit.js.map +1 -1
  127. package/dist/cli/commands/manage/register-storage-maintenance.js +152 -9
  128. package/dist/cli/commands/manage/register-storage-maintenance.js.map +1 -1
  129. package/dist/cli/commands/manage/register-validate-cron-exit.js.map +1 -1
  130. package/dist/cli/commands/manage/storage-stats-helpers.js +10 -3
  131. package/dist/cli/commands/manage/storage-stats-helpers.js.map +1 -1
  132. package/dist/cli/commands/register-manage-commands.js.map +1 -1
  133. package/dist/cli/config-feature-summaries.js.map +1 -1
  134. package/dist/cli/config-output-sink.js.map +1 -1
  135. package/dist/cli/distill-session-jsonl.js.map +1 -1
  136. package/dist/cli/distill.js +10 -1
  137. package/dist/cli/distill.js.map +1 -1
  138. package/dist/cli/global-verbose.js.map +1 -1
  139. package/dist/cli/goals.js.map +1 -1
  140. package/dist/cli/hybrid-mem-commander-utils.js.map +1 -1
  141. package/dist/cli/install/config-merge.js.map +1 -1
  142. package/dist/cli/install/cron-jobs.js.map +1 -1
  143. package/dist/cli/install/embedding-detect.js.map +1 -1
  144. package/dist/cli/install/run-install.js.map +1 -1
  145. package/dist/cli/install/workspace.js.map +1 -1
  146. package/dist/cli/proposals.js.map +1 -1
  147. package/dist/cli/register.js.map +1 -1
  148. package/dist/cli/shared.js.map +1 -1
  149. package/dist/cli/skills.js.map +1 -1
  150. package/dist/cli/task-queue-status.js.map +1 -1
  151. package/dist/cli/verified.js.map +1 -1
  152. package/dist/cli/verify/fact-count.js.map +1 -1
  153. package/dist/cli/verify/openclaw-config.js.map +1 -1
  154. package/dist/cli/verify/plugin-config-credentials.js.map +1 -1
  155. package/dist/cli/verify/sections/config-cron.js.map +1 -1
  156. package/dist/cli/verify/sections/embeddings.js.map +1 -1
  157. package/dist/cli/verify/sections/infrastructure.js.map +1 -1
  158. package/dist/cli/verify/sections/llm-models.js.map +1 -1
  159. package/dist/cli/verify/sections/reconcile.js.map +1 -1
  160. package/dist/cli/verify/verify-run-state.js.map +1 -1
  161. package/dist/cli/verify-llm-azure-auth.js.map +1 -1
  162. package/dist/cli/verify.js.map +1 -1
  163. package/dist/config/hybrid-schema.js.map +1 -1
  164. package/dist/config/index.js.map +1 -1
  165. package/dist/config/maintenance-fallback-policy.js.map +1 -1
  166. package/dist/config/parsers/capture.js.map +1 -1
  167. package/dist/config/parsers/core.js.map +1 -1
  168. package/dist/config/parsers/features.js.map +1 -1
  169. package/dist/config/parsers/index.js.map +1 -1
  170. package/dist/config/parsers/maintenance.js.map +1 -1
  171. package/dist/config/parsers/retrieval.js.map +1 -1
  172. package/dist/config/parsers/sensors.js.map +1 -1
  173. package/dist/config/skill-sections.js.map +1 -1
  174. package/dist/config/skill-size-limits.js.map +1 -1
  175. package/dist/config/types/agents.js.map +1 -1
  176. package/dist/config/types/bootstrap.js.map +1 -1
  177. package/dist/config/types/core.js.map +1 -1
  178. package/dist/config/types/index.js.map +1 -1
  179. package/dist/config/utils.js.map +1 -1
  180. package/dist/index-help.js.map +1 -1
  181. package/dist/index-testing-exports.js.map +1 -1
  182. package/dist/index.d.ts +1 -1
  183. package/dist/index.js +2 -2
  184. package/dist/index.js.map +1 -1
  185. package/dist/lifecycle/hook-resolution-api.js.map +1 -1
  186. package/dist/lifecycle/hooks.js +0 -1
  187. package/dist/lifecycle/hooks.js.map +1 -1
  188. package/dist/lifecycle/resolve-agent-id.js.map +1 -1
  189. package/dist/lifecycle/session-state.js.map +1 -1
  190. package/dist/lifecycle/stage-active-task.js.map +1 -1
  191. package/dist/lifecycle/stage-auth-failure.js.map +1 -1
  192. package/dist/lifecycle/stage-capture/run-capture.js.map +1 -1
  193. package/dist/lifecycle/stage-capture.js.map +1 -1
  194. package/dist/lifecycle/stage-cleanup.js.map +1 -1
  195. package/dist/lifecycle/stage-credential-hint.js.map +1 -1
  196. package/dist/lifecycle/stage-frustration.js.map +1 -1
  197. package/dist/lifecycle/stage-goal-stewardship.js.map +1 -1
  198. package/dist/lifecycle/stage-goal-subagent.js.map +1 -1
  199. package/dist/lifecycle/stage-injection.js +1 -1
  200. package/dist/lifecycle/stage-injection.js.map +1 -1
  201. package/dist/lifecycle/stage-recall/run-recall.js.map +1 -1
  202. package/dist/lifecycle/stage-recall.js.map +1 -1
  203. package/dist/lifecycle/stage-setup.js.map +1 -1
  204. package/dist/routes/dashboard/collectors.js.map +1 -1
  205. package/dist/routes/dashboard/html.js.map +1 -1
  206. package/dist/routes/dashboard/server.js.map +1 -1
  207. package/dist/routes/dashboard-graph.js.map +1 -1
  208. package/dist/routes/graphql-resolvers.js.map +1 -1
  209. package/dist/routes/graphql-server.js.map +1 -1
  210. package/dist/services/active-task-checkpoint.js.map +1 -1
  211. package/dist/services/active-task-injection.js.map +1 -1
  212. package/dist/services/active-task.js.map +1 -1
  213. package/dist/services/adaptive-catch-up-pacing.js +25 -0
  214. package/dist/services/adaptive-catch-up-pacing.js.map +1 -0
  215. package/dist/services/adaptive-maintenance-llm.js.map +1 -1
  216. package/dist/services/adaptive-model-limits.js.map +1 -1
  217. package/dist/services/ambient-retrieval.js.map +1 -1
  218. package/dist/services/apitap-service.js.map +1 -1
  219. package/dist/services/audit-health-exit-info.js.map +1 -1
  220. package/dist/services/audit-health-json.js.map +1 -1
  221. package/dist/services/auth-failure-detect.js.map +1 -1
  222. package/dist/services/auto-capture.js.map +1 -1
  223. package/dist/services/auto-classifier.js.map +1 -1
  224. package/dist/services/auto-skills-audit.js.map +1 -1
  225. package/dist/services/bootstrap-optional.js.map +1 -1
  226. package/dist/services/bootstrap-priority.js.map +1 -1
  227. package/dist/services/bootstrap.js.map +1 -1
  228. package/dist/services/capture-provenance.js.map +1 -1
  229. package/dist/services/capture-utils.js.map +1 -1
  230. package/dist/services/chat.js +22 -3
  231. package/dist/services/chat.js.map +1 -1
  232. package/dist/services/classification-scope.js.map +1 -1
  233. package/dist/services/classification.js.map +1 -1
  234. package/dist/services/cli-sql-dump.js.map +1 -1
  235. package/dist/services/consolidation.js.map +1 -1
  236. package/dist/services/context-audit.js +1 -1
  237. package/dist/services/context-audit.js.map +1 -1
  238. package/dist/services/context-budget.js.map +1 -1
  239. package/dist/services/context-engine.js.map +1 -1
  240. package/dist/services/contextual-variants.js.map +1 -1
  241. package/dist/services/continuous-verifier.js.map +1 -1
  242. package/dist/services/contradiction-adjudicator.js.map +1 -1
  243. package/dist/services/cost-context.js.map +1 -1
  244. package/dist/services/cost-feature-labels.js.map +1 -1
  245. package/dist/services/credential-migration.js.map +1 -1
  246. package/dist/services/credential-scanner.js.map +1 -1
  247. package/dist/services/credential-validation.js.map +1 -1
  248. package/dist/services/cron-exit-validator.js.map +1 -1
  249. package/dist/services/cron-guard.js.map +1 -1
  250. package/dist/services/cron-job-bash-harness.js +52 -5
  251. package/dist/services/cron-job-bash-harness.js.map +1 -1
  252. package/dist/services/cron-maintenance-reconciler.js +1 -3
  253. package/dist/services/cron-maintenance-reconciler.js.map +1 -1
  254. package/dist/services/cross-agent-learning.js.map +1 -1
  255. package/dist/services/crystallization-proposer.js.map +1 -1
  256. package/dist/services/dedupe-policy.js.map +1 -1
  257. package/dist/services/deprecated-cron-commands.js.map +1 -1
  258. package/dist/services/directive-extract.js.map +1 -1
  259. package/dist/services/document-chunker.js.map +1 -1
  260. package/dist/services/document-grader.js.map +1 -1
  261. package/dist/services/dream-cycle.js.map +1 -1
  262. package/dist/services/embedding-migration.js.map +1 -1
  263. package/dist/services/embedding-registry.js.map +1 -1
  264. package/dist/services/embeddings/chain-provider.js.map +1 -1
  265. package/dist/services/embeddings/factory.js.map +1 -1
  266. package/dist/services/embeddings/fallback-provider.js.map +1 -1
  267. package/dist/services/embeddings/ollama-provider.js.map +1 -1
  268. package/dist/services/embeddings/onnx-provider.js.map +1 -1
  269. package/dist/services/embeddings/openai-provider.js.map +1 -1
  270. package/dist/services/embeddings/shared.js +3 -3
  271. package/dist/services/embeddings/shared.js.map +1 -1
  272. package/dist/services/embeddings/types.js.map +1 -1
  273. package/dist/services/entity-enrichment-adaptive.js +128 -0
  274. package/dist/services/entity-enrichment-adaptive.js.map +1 -0
  275. package/dist/services/entity-enrichment-cli.js +389 -42
  276. package/dist/services/entity-enrichment-cli.js.map +1 -1
  277. package/dist/services/entity-enrichment.js +31 -5
  278. package/dist/services/entity-enrichment.js.map +1 -1
  279. package/dist/services/error-reporter/noisy-errors.js.map +1 -1
  280. package/dist/services/error-reporter/sanitize.js.map +1 -1
  281. package/dist/services/error-reporter.js.map +1 -1
  282. package/dist/services/event-hub-repair.js.map +1 -1
  283. package/dist/services/export-memory.js.map +1 -1
  284. package/dist/services/fact-extraction.js.map +1 -1
  285. package/dist/services/feedback-effectiveness.js.map +1 -1
  286. package/dist/services/find-duplicates.js.map +1 -1
  287. package/dist/services/frustration-detector.js.map +1 -1
  288. package/dist/services/fts-search.js.map +1 -1
  289. package/dist/services/gap-detector.js.map +1 -1
  290. package/dist/services/generated-skill-lifecycle.js.map +1 -1
  291. package/dist/services/generated-skill-validation.js.map +1 -1
  292. package/dist/services/goal-active-task-mirror.js.map +1 -1
  293. package/dist/services/goal-circuit-breaker.js.map +1 -1
  294. package/dist/services/goal-health.js.map +1 -1
  295. package/dist/services/goal-registry.js.map +1 -1
  296. package/dist/services/goal-stewardship-heartbeat.js.map +1 -1
  297. package/dist/services/goal-stewardship-llm-triage.js.map +1 -1
  298. package/dist/services/goal-stewardship-verify-cron.js.map +1 -1
  299. package/dist/services/goal-stewardship.js.map +1 -1
  300. package/dist/services/goal-subagent.js.map +1 -1
  301. package/dist/services/graph-retrieval.js.map +1 -1
  302. package/dist/services/humanizer-score.js.map +1 -1
  303. package/dist/services/hybrid-mem-cron-default-job-steps.js.map +1 -1
  304. package/dist/services/hyde-helper.js.map +1 -1
  305. package/dist/services/identity-reflection.js.map +1 -1
  306. package/dist/services/implicit-feedback-extract.js.map +1 -1
  307. package/dist/services/index.js.map +1 -1
  308. package/dist/services/ingest-utils.js.map +1 -1
  309. package/dist/services/intent-template.js.map +1 -1
  310. package/dist/services/json-array-parser.js.map +1 -1
  311. package/dist/services/knowledge-gaps.js.map +1 -1
  312. package/dist/services/language-keywords-build.js.map +1 -1
  313. package/dist/services/lifecycle/github-adapter.js.map +1 -1
  314. package/dist/services/llm-rate-limit-headers.js +1 -2
  315. package/dist/services/llm-rate-limit-headers.js.map +1 -1
  316. package/dist/services/maintenance-auto-fix.js.map +1 -1
  317. package/dist/services/maintenance-log-analyzer.js +7 -1
  318. package/dist/services/maintenance-log-analyzer.js.map +1 -1
  319. package/dist/services/maintenance-timestamp.js.map +1 -1
  320. package/dist/services/memory-diagnostics.js.map +1 -1
  321. package/dist/services/memory-index.js.map +1 -1
  322. package/dist/services/merge-results.js.map +1 -1
  323. package/dist/services/model-capabilities.js.map +1 -1
  324. package/dist/services/model-pricing.js.map +1 -1
  325. package/dist/services/narrative-recall.js.map +1 -1
  326. package/dist/services/openclaw-session-artifact.js.map +1 -1
  327. package/dist/services/passive-observer.js.map +1 -1
  328. package/dist/services/pattern-detector-hash.js.map +1 -1
  329. package/dist/services/pattern-detector.js.map +1 -1
  330. package/dist/services/pending-autopilot/foundation.js.map +1 -1
  331. package/dist/services/pending-autopilot/redaction.js.map +1 -1
  332. package/dist/services/pending-autopilot/store.js.map +1 -1
  333. package/dist/services/pending-autopilot/types.js.map +1 -1
  334. package/dist/services/pending-digest-autopilot-cron.js.map +1 -1
  335. package/dist/services/pending-digest-autopilot.js.map +1 -1
  336. package/dist/services/pending-review-digest.js.map +1 -1
  337. package/dist/services/persona-proposal-triage.js.map +1 -1
  338. package/dist/services/persona-state-promotion.js.map +1 -1
  339. package/dist/services/post-compaction-recall.js.map +1 -1
  340. package/dist/services/pre-consolidation-flush.js.map +1 -1
  341. package/dist/services/pre-finalization-guard.js.map +1 -1
  342. package/dist/services/procedure-cluster.js.map +1 -1
  343. package/dist/services/procedure-extractor.js.map +1 -1
  344. package/dist/services/procedure-promotion/duplicate-skill-cache.js.map +1 -1
  345. package/dist/services/procedure-promotion-policy.js.map +1 -1
  346. package/dist/services/procedure-selection-metrics.js.map +1 -1
  347. package/dist/services/procedure-skill-eval.js.map +1 -1
  348. package/dist/services/procedure-skill-generator.js.map +1 -1
  349. package/dist/services/procedure-skill-recipe.js.map +1 -1
  350. package/dist/services/procedure-skill-shrink.js.map +1 -1
  351. package/dist/services/procedure-skill-workflow.js.map +1 -1
  352. package/dist/services/provenance.js.map +1 -1
  353. package/dist/services/public-export-bundle.js.map +1 -1
  354. package/dist/services/python-bridge.js.map +1 -1
  355. package/dist/services/query-expander.js.map +1 -1
  356. package/dist/services/query-validator.js.map +1 -1
  357. package/dist/services/recall-pipeline.js.map +1 -1
  358. package/dist/services/recall-timing.js.map +1 -1
  359. package/dist/services/recent-http-attempts.js.map +1 -1
  360. package/dist/services/reflection/shared.js.map +1 -1
  361. package/dist/services/reflection.js.map +1 -1
  362. package/dist/services/reinforcement-extract.js.map +1 -1
  363. package/dist/services/reranker.js.map +1 -1
  364. package/dist/services/responses-adapter.js.map +1 -1
  365. package/dist/services/retrieval-aliases.js.map +1 -1
  366. package/dist/services/retrieval-mode-policy.js.map +1 -1
  367. package/dist/services/retrieval-orchestrator/packing.js.map +1 -1
  368. package/dist/services/retrieval-orchestrator.d.ts +2 -3
  369. package/dist/services/retrieval-orchestrator.js.map +1 -1
  370. package/dist/services/rrf-fusion.js.map +1 -1
  371. package/dist/services/self-correction-extract.js.map +1 -1
  372. package/dist/services/session-observability.js.map +1 -1
  373. package/dist/services/session-pre-filter.js.map +1 -1
  374. package/dist/services/shortest-path.js.map +1 -1
  375. package/dist/services/skill-allowed-tools.js.map +1 -1
  376. package/dist/services/skill-creator-validator.js.map +1 -1
  377. package/dist/services/skill-crystallizer-helpers.js.map +1 -1
  378. package/dist/services/skill-crystallizer.js.map +1 -1
  379. package/dist/services/skill-description-builder.js.map +1 -1
  380. package/dist/services/skill-eval-synthesizer.js.map +1 -1
  381. package/dist/services/skill-examples-builder.js.map +1 -1
  382. package/dist/services/skill-frontmatter.js.map +1 -1
  383. package/dist/services/skill-name-validator.js.map +1 -1
  384. package/dist/services/skill-prompt-injection.js.map +1 -1
  385. package/dist/services/skill-reference-sidecar.js.map +1 -1
  386. package/dist/services/skill-script-bundler.js.map +1 -1
  387. package/dist/services/skill-validator.js.map +1 -1
  388. package/dist/services/startup-memory-attribution.js.map +1 -1
  389. package/dist/services/task-hygiene.js.map +1 -1
  390. package/dist/services/task-ledger/canonical.js.map +1 -1
  391. package/dist/services/task-ledger-facts.js.map +1 -1
  392. package/dist/services/task-queue-leases.js.map +1 -1
  393. package/dist/services/task-queue-watchdog.js.map +1 -1
  394. package/dist/services/tool-effectiveness.js.map +1 -1
  395. package/dist/services/tool-proposer.js.map +1 -1
  396. package/dist/services/tools-md-section.js.map +1 -1
  397. package/dist/services/topic-clusters.js.map +1 -1
  398. package/dist/services/trajectory-tracker.js.map +1 -1
  399. package/dist/services/vector-backend-observability.js.map +1 -1
  400. package/dist/services/vector-lifecycle-audit.js.map +1 -1
  401. package/dist/services/vector-maintenance.js.map +1 -1
  402. package/dist/services/vector-search.js.map +1 -1
  403. package/dist/services/verification-store.js.map +1 -1
  404. package/dist/services/verified-fact-triage.js.map +1 -1
  405. package/dist/services/wal-helpers.js.map +1 -1
  406. package/dist/services/workflow-tracker.js.map +1 -1
  407. package/dist/setup/bootstrap-databases.js.map +1 -1
  408. package/dist/setup/cli-context/cli-services.js.map +1 -1
  409. package/dist/setup/cli-context/help-text.js.map +1 -1
  410. package/dist/setup/cli-context/metadata.js.map +1 -1
  411. package/dist/setup/cli-context/register-cli-with-help.js.map +1 -1
  412. package/dist/setup/cli-context/register-full.js.map +1 -1
  413. package/dist/setup/cli-context/register-help.js.map +1 -1
  414. package/dist/setup/cost-instrumentation.js.map +1 -1
  415. package/dist/setup/hybrid-memory-generation-state.js.map +1 -1
  416. package/dist/setup/hybrid-memory-reload-coordinator.js +13 -13
  417. package/dist/setup/hybrid-memory-reload-coordinator.js.map +1 -1
  418. package/dist/setup/plugin-service.js.map +1 -1
  419. package/dist/setup/provider-router.js.map +1 -1
  420. package/dist/setup/register-context-engine.js.map +1 -1
  421. package/dist/setup/register-hooks.js.map +1 -1
  422. package/dist/setup/register-plugin.js +25 -21
  423. package/dist/setup/register-plugin.js.map +1 -1
  424. package/dist/setup/register-tools.js.map +1 -1
  425. package/dist/setup/reregister-policy.js +2 -2
  426. package/dist/setup/reregister-policy.js.map +1 -1
  427. package/dist/setup/tool-installers.js.map +1 -1
  428. package/dist/setup/workspace-bootstrap.js.map +1 -1
  429. package/dist/src/worker/narratives.js.map +1 -1
  430. package/dist/tools/apitap-tools.js.map +1 -1
  431. package/dist/tools/credential-tools.js.map +1 -1
  432. package/dist/tools/crystallization-tools.js.map +1 -1
  433. package/dist/tools/dashboard-routes.js.map +1 -1
  434. package/dist/tools/document-tools.js.map +1 -1
  435. package/dist/tools/goal-tools.js.map +1 -1
  436. package/dist/tools/graph-tools.js.map +1 -1
  437. package/dist/tools/issue-tools.js.map +1 -1
  438. package/dist/tools/memory/build-runtime.js.map +1 -1
  439. package/dist/tools/memory/helpers.js.map +1 -1
  440. package/dist/tools/memory/register-checkpoint-tools.js.map +1 -1
  441. package/dist/tools/memory/register-directory-tools.js.map +1 -1
  442. package/dist/tools/memory/register-edict-tools.js.map +1 -1
  443. package/dist/tools/memory/register-episode-tools.js.map +1 -1
  444. package/dist/tools/memory/register-recall-tools.js.map +1 -1
  445. package/dist/tools/memory/register-store-tools.js.map +1 -1
  446. package/dist/tools/memory-tools.js.map +1 -1
  447. package/dist/tools/persona-tools.js.map +1 -1
  448. package/dist/tools/provenance-tools.js.map +1 -1
  449. package/dist/tools/public-api-routes.js.map +1 -1
  450. package/dist/tools/safe-register-http-route.js.map +1 -1
  451. package/dist/tools/self-extension-tools.js.map +1 -1
  452. package/dist/tools/task-hygiene-tools.js.map +1 -1
  453. package/dist/tools/utility-tools.js.map +1 -1
  454. package/dist/tools/verification-tools.js.map +1 -1
  455. package/dist/tools/workflow-tools.js.map +1 -1
  456. package/dist/types/issue-types.js.map +1 -1
  457. package/dist/types/learnings-types.js.map +1 -1
  458. package/dist/types/memory.js.map +1 -1
  459. package/dist/utils/apim-gateway-fetch.js.map +1 -1
  460. package/dist/utils/atomic-write.js.map +1 -1
  461. package/dist/utils/auth-failover.js.map +1 -1
  462. package/dist/utils/auth.js.map +1 -1
  463. package/dist/utils/compaction-model-watchdog.js.map +1 -1
  464. package/dist/utils/consolidation-controls.js.map +1 -1
  465. package/dist/utils/constants.js.map +1 -1
  466. package/dist/utils/date-detector.js.map +1 -1
  467. package/dist/utils/dates.js.map +1 -1
  468. package/dist/utils/decay.js.map +1 -1
  469. package/dist/utils/duration.js.map +1 -1
  470. package/dist/utils/embed-call.js.map +1 -1
  471. package/dist/utils/entity-lookup-resolve.js.map +1 -1
  472. package/dist/utils/entity-mention-quality.js.map +1 -1
  473. package/dist/utils/entity-stopwords.js.map +1 -1
  474. package/dist/utils/env-manager.js.map +1 -1
  475. package/dist/utils/error-tracking.js.map +1 -1
  476. package/dist/utils/event-loop-yield.js.map +1 -1
  477. package/dist/utils/extract-last-user-message.js.map +1 -1
  478. package/dist/utils/extraction-from-template.js.map +1 -1
  479. package/dist/utils/fact-embeddings.js.map +1 -1
  480. package/dist/utils/file-snapshot.js.map +1 -1
  481. package/dist/utils/format.js.map +1 -1
  482. package/dist/utils/fs.js.map +1 -1
  483. package/dist/utils/gh-repo-arg.js.map +1 -1
  484. package/dist/utils/hybrid-mem-json-cli.js.map +1 -1
  485. package/dist/utils/language-keywords.js.map +1 -1
  486. package/dist/utils/lifecycle-generation.js.map +1 -1
  487. package/dist/utils/llm-json-array.js.map +1 -1
  488. package/dist/utils/llm-selection.js.map +1 -1
  489. package/dist/utils/logger.js.map +1 -1
  490. package/dist/utils/model-provider-family.js.map +1 -1
  491. package/dist/utils/model-tier.js.map +1 -1
  492. package/dist/utils/openclaw-agent-defaults.js.map +1 -1
  493. package/dist/utils/path.js.map +1 -1
  494. package/dist/utils/plugin-root.js.map +1 -1
  495. package/dist/utils/plugin-update-check.js.map +1 -1
  496. package/dist/utils/procedure-risk.js.map +1 -1
  497. package/dist/utils/progress-indicators.js.map +1 -1
  498. package/dist/utils/prompt-loader.js.map +1 -1
  499. package/dist/utils/provenance.js.map +1 -1
  500. package/dist/utils/provider-detection.js.map +1 -1
  501. package/dist/utils/registration-superseded.js.map +1 -1
  502. package/dist/utils/salience.js.map +1 -1
  503. package/dist/utils/sanitize-messages.js.map +1 -1
  504. package/dist/utils/scope-filter.js.map +1 -1
  505. package/dist/utils/skill-discovery.js.map +1 -1
  506. package/dist/utils/sqlite-file-perms.js.map +1 -1
  507. package/dist/utils/sqlite-outcome-compat.js.map +1 -1
  508. package/dist/utils/sqlite-transaction.js.map +1 -1
  509. package/dist/utils/stable-stringify.js.map +1 -1
  510. package/dist/utils/subagent-ended-utils.js.map +1 -1
  511. package/dist/utils/tags.js.map +1 -1
  512. package/dist/utils/text.js.map +1 -1
  513. package/dist/utils/timeout.js.map +1 -1
  514. package/dist/utils/typebox.js.map +1 -1
  515. package/dist/utils/version-check.js.map +1 -1
  516. package/dist/utils/wal-replay.js.map +1 -1
  517. package/dist/versionInfo.js.map +1 -1
  518. package/index.ts +2 -2
  519. package/lifecycle/hooks.ts +0 -1
  520. package/npm-shrinkwrap.json +487 -186
  521. package/openclaw.plugin.json +1 -1
  522. package/package.json +2 -2
  523. package/services/adaptive-catch-up-pacing.ts +28 -0
  524. package/services/chat.ts +34 -1
  525. package/services/cron-job-bash-harness.ts +52 -5
  526. package/services/embeddings/shared.ts +5 -2
  527. package/services/entity-enrichment-adaptive.ts +245 -0
  528. package/services/entity-enrichment-cli.ts +553 -47
  529. package/services/entity-enrichment.ts +43 -2
  530. package/services/llm-rate-limit-headers.ts +1 -4
  531. package/services/maintenance-log-analyzer.ts +13 -9
  532. package/services/reinforcement-extract.ts +19 -0
  533. package/setup/hybrid-memory-reload-coordinator.ts +26 -0
  534. package/setup/register-plugin.ts +62 -32
  535. package/setup/reregister-policy.ts +10 -5
@@ -1 +1 @@
1
- {"version":3,"file":"query-expander.js","names":[],"sources":["../../services/query-expander.ts"],"sourcesContent":["/**\n * Query Expander Service (Issue #160).\n *\n * At retrieval time, expand the user's query into 3-5 variant queries via LLM,\n * then search against all variants and merge results via RRF. This bridges\n * vocabulary gaps at query time (complementing #159's index-time variants).\n *\n * Key design:\n * - LRU cache to memoize expansions for identical queries.\n * - Timeout: if LLM takes >15s (default), fall back to original query only.\n * - Graceful degradation: any failure returns [original query].\n * - Optional context parameter improves expansion quality.\n */\n\nimport type OpenAI from \"openai\";\nimport type { QueryExpansionConfig } from \"../config.js\";\nimport { chatComplete } from \"./chat.js\";\nimport { CostFeature } from \"./cost-feature-labels.js\";\nimport { extractJsonArray } from \"./json-array-parser.js\";\n\n// ---------------------------------------------------------------------------\n// Rule-based alias expansion\n// ---------------------------------------------------------------------------\n\nconst RULE_BASED_REPLACEMENTS: Array<{ pattern: RegExp; replacement: string }> = [\n { pattern: /\\bHA\\b/gi, replacement: \"Home Assistant\" },\n { pattern: /\\bVM\\b/gi, replacement: \"virtual machine\" },\n { pattern: /\\bDB\\b/gi, replacement: \"database\" },\n { pattern: /\\bAPI\\b/gi, replacement: \"application programming interface\" },\n { pattern: /\\bconfig\\b/gi, replacement: \"configuration\" },\n { pattern: /\\bsettings\\b/gi, replacement: \"configuration\" },\n { pattern: /\\bauth\\b/gi, replacement: \"authentication\" },\n { pattern: /\\bcreds\\b/gi, replacement: \"credentials\" },\n { pattern: /\\brepo\\b/gi, replacement: \"repository\" },\n { pattern: /\\bdocs\\b/gi, replacement: \"documentation\" },\n { pattern: /\\benv\\b/gi, replacement: \"environment\" },\n { pattern: /\\bsvc\\b/gi, replacement: \"service\" },\n];\n\nexport function generateRuleBasedAlias(query: string): string | null {\n if (!query.trim()) return null;\n let alias = query;\n for (const { pattern, replacement } of RULE_BASED_REPLACEMENTS) {\n alias = alias.replace(pattern, replacement);\n }\n alias = alias.replace(/\\s+/g, \" \").trim();\n if (!alias) return null;\n if (alias.toLowerCase() === query.trim().toLowerCase()) return null;\n return alias;\n}\n\n// ---------------------------------------------------------------------------\n// LRU Cache\n// ---------------------------------------------------------------------------\n\n/** Minimal LRU cache backed by a Map (insertion-order eviction). */\nclass LRUCache<K, V> {\n private readonly cache: Map<K, V>;\n private readonly capacity: number;\n\n constructor(capacity: number) {\n this.cache = new Map();\n this.capacity = Math.max(1, capacity);\n }\n\n get(key: K): V | undefined {\n if (!this.cache.has(key)) return undefined;\n const value = this.cache.get(key)!;\n // Refresh: delete and re-insert to move to end (most-recently-used).\n this.cache.delete(key);\n this.cache.set(key, value);\n return value;\n }\n\n set(key: K, value: V): void {\n if (this.cache.has(key)) {\n this.cache.delete(key);\n } else if (this.cache.size >= this.capacity) {\n // Evict least-recently-used (first inserted key).\n const oldest = this.cache.keys().next().value as K;\n this.cache.delete(oldest);\n }\n this.cache.set(key, value);\n }\n\n get size(): number {\n return this.cache.size;\n }\n\n has(key: K): boolean {\n return this.cache.has(key);\n }\n\n clear(): void {\n this.cache.clear();\n }\n}\n\n// ---------------------------------------------------------------------------\n// Prompt template\n// ---------------------------------------------------------------------------\n\nconst EXPANSION_PROMPT_TEMPLATE = `Given this search query: '{query}'{contextLine}\n\nGenerate 3-5 alternative search queries that would find the same information using different vocabulary, synonyms, or phrasing. Focus on semantic equivalents — not just rephrasing, but different angles a person might use to search for the same content.\n\nReturn ONLY a JSON array of strings, no other text. Example: [\"alternative one\", \"alternative two\", \"alternative three\"]`;\n\n// ---------------------------------------------------------------------------\n// Response parser\n// ---------------------------------------------------------------------------\n\n/**\n * Parse a JSON array of strings from an LLM response.\n * Handles responses that wrap the JSON in prose or code fences.\n * Tries every [...] substring so that arrays containing literal \"]\" in string\n * values (e.g. [\"query about [topic]\"]) parse correctly, and prose after the\n * array does not get included (unlike a single greedy match).\n */\nexport function parseExpansionsFromResponse(response: string, maxVariants: number): string[] {\n return extractJsonArray(response)\n .filter((v): v is string => typeof v === \"string\" && v.trim().length > 0)\n .map((v) => v.trim())\n .slice(0, maxVariants);\n}\n\n// ---------------------------------------------------------------------------\n// QueryExpander\n// ---------------------------------------------------------------------------\n\n/**\n * Expands a search query into multiple variant queries via LLM.\n * Results are memoized in an LRU cache to avoid redundant LLM calls.\n */\nexport class QueryExpander {\n private readonly cache: LRUCache<string, string[]>;\n\n constructor(\n private readonly config: QueryExpansionConfig,\n private readonly openai: OpenAI,\n ) {\n this.cache = new LRUCache(config.cacheSize);\n }\n\n getMode(): \"always\" | \"conditional\" | \"off\" {\n if (!this.config.enabled) return \"off\";\n return this.config.mode ?? \"always\";\n }\n\n getThreshold(): number {\n return this.config.threshold ?? 0.03;\n }\n\n getRuleBasedAlias(query: string): string | null {\n return generateRuleBasedAlias(query);\n }\n\n /**\n * Expand a query into multiple alternative queries.\n *\n * @returns Array starting with the original query, followed by LLM-generated variants.\n * On any failure or when disabled, returns [query] (original only).\n */\n async expandQuery(query: string, context?: string): Promise<string[]> {\n if (this.getMode() === \"off\") return [query];\n if (!query.trim()) return [query];\n\n const cacheKey = context ? `${query}\\x00${context}` : query;\n const cached = this.cache.get(cacheKey);\n if (cached) return cached;\n\n try {\n const contextLine = context ? `\\nContext: ${context.slice(0, 300)}` : \"\";\n const prompt = EXPANSION_PROMPT_TEMPLATE.replace(\"{query}\", () => query).replace(\n \"{contextLine}\",\n () => contextLine,\n );\n\n const model = this.config.model ?? \"openai/gpt-4.1-nano\";\n\n const response = await chatComplete({\n model,\n content: prompt,\n temperature: 0.7,\n maxTokens: 300,\n openai: this.openai,\n timeoutMs: this.config.timeoutMs,\n feature: CostFeature.queryExpansion,\n });\n\n const variants = parseExpansionsFromResponse(response, this.config.maxVariants);\n // Deduplicate: remove variants identical to the original query.\n const filtered = variants.filter((v) => v.toLowerCase() !== query.toLowerCase());\n\n const result = [query, ...filtered];\n this.cache.set(cacheKey, result);\n return result;\n } catch (err) {\n // Graceful degradation — any LLM failure returns original query only.\n // Log would go here if logger was passed in; for now, silently degrade.\n void err;\n return [query];\n }\n }\n\n /** Number of entries currently in the cache (for testing). */\n get cacheSize(): number {\n return this.cache.size;\n }\n\n /** Clear the memoization cache (for testing). */\n clearCache(): void {\n this.cache.clear();\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"query-expander.js","names":[],"sources":["../../services/query-expander.ts"],"sourcesContent":["/**\n * Query Expander Service (Issue #160).\n *\n * At retrieval time, expand the user's query into 3-5 variant queries via LLM,\n * then search against all variants and merge results via RRF. This bridges\n * vocabulary gaps at query time (complementing #159's index-time variants).\n *\n * Key design:\n * - LRU cache to memoize expansions for identical queries.\n * - Timeout: if LLM takes >15s (default), fall back to original query only.\n * - Graceful degradation: any failure returns [original query].\n * - Optional context parameter improves expansion quality.\n */\n\nimport type OpenAI from \"openai\";\nimport type { QueryExpansionConfig } from \"../config.js\";\nimport { chatComplete } from \"./chat.js\";\nimport { CostFeature } from \"./cost-feature-labels.js\";\nimport { extractJsonArray } from \"./json-array-parser.js\";\n\n// ---------------------------------------------------------------------------\n// Rule-based alias expansion\n// ---------------------------------------------------------------------------\n\nconst RULE_BASED_REPLACEMENTS: Array<{ pattern: RegExp; replacement: string }> = [\n { pattern: /\\bHA\\b/gi, replacement: \"Home Assistant\" },\n { pattern: /\\bVM\\b/gi, replacement: \"virtual machine\" },\n { pattern: /\\bDB\\b/gi, replacement: \"database\" },\n { pattern: /\\bAPI\\b/gi, replacement: \"application programming interface\" },\n { pattern: /\\bconfig\\b/gi, replacement: \"configuration\" },\n { pattern: /\\bsettings\\b/gi, replacement: \"configuration\" },\n { pattern: /\\bauth\\b/gi, replacement: \"authentication\" },\n { pattern: /\\bcreds\\b/gi, replacement: \"credentials\" },\n { pattern: /\\brepo\\b/gi, replacement: \"repository\" },\n { pattern: /\\bdocs\\b/gi, replacement: \"documentation\" },\n { pattern: /\\benv\\b/gi, replacement: \"environment\" },\n { pattern: /\\bsvc\\b/gi, replacement: \"service\" },\n];\n\nexport function generateRuleBasedAlias(query: string): string | null {\n if (!query.trim()) return null;\n let alias = query;\n for (const { pattern, replacement } of RULE_BASED_REPLACEMENTS) {\n alias = alias.replace(pattern, replacement);\n }\n alias = alias.replace(/\\s+/g, \" \").trim();\n if (!alias) return null;\n if (alias.toLowerCase() === query.trim().toLowerCase()) return null;\n return alias;\n}\n\n// ---------------------------------------------------------------------------\n// LRU Cache\n// ---------------------------------------------------------------------------\n\n/** Minimal LRU cache backed by a Map (insertion-order eviction). */\nclass LRUCache<K, V> {\n private readonly cache: Map<K, V>;\n private readonly capacity: number;\n\n constructor(capacity: number) {\n this.cache = new Map();\n this.capacity = Math.max(1, capacity);\n }\n\n get(key: K): V | undefined {\n if (!this.cache.has(key)) return undefined;\n const value = this.cache.get(key)!;\n // Refresh: delete and re-insert to move to end (most-recently-used).\n this.cache.delete(key);\n this.cache.set(key, value);\n return value;\n }\n\n set(key: K, value: V): void {\n if (this.cache.has(key)) {\n this.cache.delete(key);\n } else if (this.cache.size >= this.capacity) {\n // Evict least-recently-used (first inserted key).\n const oldest = this.cache.keys().next().value as K;\n this.cache.delete(oldest);\n }\n this.cache.set(key, value);\n }\n\n get size(): number {\n return this.cache.size;\n }\n\n has(key: K): boolean {\n return this.cache.has(key);\n }\n\n clear(): void {\n this.cache.clear();\n }\n}\n\n// ---------------------------------------------------------------------------\n// Prompt template\n// ---------------------------------------------------------------------------\n\nconst EXPANSION_PROMPT_TEMPLATE = `Given this search query: '{query}'{contextLine}\n\nGenerate 3-5 alternative search queries that would find the same information using different vocabulary, synonyms, or phrasing. Focus on semantic equivalents — not just rephrasing, but different angles a person might use to search for the same content.\n\nReturn ONLY a JSON array of strings, no other text. Example: [\"alternative one\", \"alternative two\", \"alternative three\"]`;\n\n// ---------------------------------------------------------------------------\n// Response parser\n// ---------------------------------------------------------------------------\n\n/**\n * Parse a JSON array of strings from an LLM response.\n * Handles responses that wrap the JSON in prose or code fences.\n * Tries every [...] substring so that arrays containing literal \"]\" in string\n * values (e.g. [\"query about [topic]\"]) parse correctly, and prose after the\n * array does not get included (unlike a single greedy match).\n */\nexport function parseExpansionsFromResponse(response: string, maxVariants: number): string[] {\n return extractJsonArray(response)\n .filter((v): v is string => typeof v === \"string\" && v.trim().length > 0)\n .map((v) => v.trim())\n .slice(0, maxVariants);\n}\n\n// ---------------------------------------------------------------------------\n// QueryExpander\n// ---------------------------------------------------------------------------\n\n/**\n * Expands a search query into multiple variant queries via LLM.\n * Results are memoized in an LRU cache to avoid redundant LLM calls.\n */\nexport class QueryExpander {\n private readonly cache: LRUCache<string, string[]>;\n\n constructor(\n private readonly config: QueryExpansionConfig,\n private readonly openai: OpenAI,\n ) {\n this.cache = new LRUCache(config.cacheSize);\n }\n\n getMode(): \"always\" | \"conditional\" | \"off\" {\n if (!this.config.enabled) return \"off\";\n return this.config.mode ?? \"always\";\n }\n\n getThreshold(): number {\n return this.config.threshold ?? 0.03;\n }\n\n getRuleBasedAlias(query: string): string | null {\n return generateRuleBasedAlias(query);\n }\n\n /**\n * Expand a query into multiple alternative queries.\n *\n * @returns Array starting with the original query, followed by LLM-generated variants.\n * On any failure or when disabled, returns [query] (original only).\n */\n async expandQuery(query: string, context?: string): Promise<string[]> {\n if (this.getMode() === \"off\") return [query];\n if (!query.trim()) return [query];\n\n const cacheKey = context ? `${query}\\x00${context}` : query;\n const cached = this.cache.get(cacheKey);\n if (cached) return cached;\n\n try {\n const contextLine = context ? `\\nContext: ${context.slice(0, 300)}` : \"\";\n const prompt = EXPANSION_PROMPT_TEMPLATE.replace(\"{query}\", () => query).replace(\n \"{contextLine}\",\n () => contextLine,\n );\n\n const model = this.config.model ?? \"openai/gpt-4.1-nano\";\n\n const response = await chatComplete({\n model,\n content: prompt,\n temperature: 0.7,\n maxTokens: 300,\n openai: this.openai,\n timeoutMs: this.config.timeoutMs,\n feature: CostFeature.queryExpansion,\n });\n\n const variants = parseExpansionsFromResponse(response, this.config.maxVariants);\n // Deduplicate: remove variants identical to the original query.\n const filtered = variants.filter((v) => v.toLowerCase() !== query.toLowerCase());\n\n const result = [query, ...filtered];\n this.cache.set(cacheKey, result);\n return result;\n } catch (err) {\n // Graceful degradation — any LLM failure returns original query only.\n // Log would go here if logger was passed in; for now, silently degrade.\n void err;\n return [query];\n }\n }\n\n /** Number of entries currently in the cache (for testing). */\n get cacheSize(): number {\n return this.cache.size;\n }\n\n /** Clear the memoization cache (for testing). */\n clearCache(): void {\n this.cache.clear();\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"query-validator.js","names":[],"sources":["../../services/query-validator.ts"],"sourcesContent":["/**\n * Lightweight guard for deciding whether a user message should enter memory retrieval.\n *\n * The validator is intentionally conservative: it only blocks obvious conversational\n * filler / social chatter so we avoid wasting embedding + retrieval work on turns that\n * clearly do not need memory lookup.\n */\n\nexport interface QueryValidationResult {\n requiresLookup: boolean;\n reason: string;\n}\n\nconst FILLER_PATTERNS: RegExp[] = [\n /^(?:hi|hello|hey|yo|hiya)[!. ]*$/i,\n /^(?:thanks|thank you|thx|tysm)[!. ]*$/i,\n /^(?:ok|okay|kk|cool|nice|great|awesome|got it|sounds good|sure|yep|yup|nah|nope)[!. ]*$/i,\n /^(?:good morning|good afternoon|good evening|good night)[!. ]*$/i,\n /^(?:how are you|how's it going|what's up|sup|who are you|tell me a joke)[?!. ]*$/i,\n /^(?:please continue|continue|go on|carry on)[!. ]*$/i,\n];\n\n/**\n * Returns whether the query should go through memory retrieval.\n *\n * Only obviously non-retrieval chatter is rejected; everything else is allowed so we\n * don't accidentally suppress legitimate memory lookups.\n */\nexport function validateQueryForMemoryLookup(query: string): QueryValidationResult {\n const normalized = query.replace(/\\s+/g, \" \").trim();\n\n if (!normalized) {\n return { requiresLookup: false, reason: \"empty-query\" };\n }\n\n if (!/[\\p{L}\\p{N}]/u.test(normalized)) {\n return { requiresLookup: false, reason: \"non-semantic-input\" };\n }\n\n if (FILLER_PATTERNS.some((pattern) => pattern.test(normalized))) {\n return { requiresLookup: false, reason: \"conversational-filler\" };\n }\n\n return { requiresLookup: true, reason: \"memory-lookup-candidate\" };\n}\n"],"mappings":";AAaA,MAAM,kBAA4B;CAChC;CACA;CACA;CACA;CACA;CACA;CACD;;;;;;;AAQD,SAAgB,6BAA6B,OAAsC;CACjF,MAAM,aAAa,MAAM,QAAQ,QAAQ,IAAI,CAAC,MAAM;CAEpD,IAAI,CAAC,YACH,OAAO;EAAE,gBAAgB;EAAO,QAAQ;EAAe;CAGzD,IAAI,CAAC,gBAAgB,KAAK,WAAW,EACnC,OAAO;EAAE,gBAAgB;EAAO,QAAQ;EAAsB;CAGhE,IAAI,gBAAgB,MAAM,YAAY,QAAQ,KAAK,WAAW,CAAC,EAC7D,OAAO;EAAE,gBAAgB;EAAO,QAAQ;EAAyB;CAGnE,OAAO;EAAE,gBAAgB;EAAM,QAAQ;EAA2B"}
1
+ {"version":3,"file":"query-validator.js","names":[],"sources":["../../services/query-validator.ts"],"sourcesContent":["/**\n * Lightweight guard for deciding whether a user message should enter memory retrieval.\n *\n * The validator is intentionally conservative: it only blocks obvious conversational\n * filler / social chatter so we avoid wasting embedding + retrieval work on turns that\n * clearly do not need memory lookup.\n */\n\nexport interface QueryValidationResult {\n requiresLookup: boolean;\n reason: string;\n}\n\nconst FILLER_PATTERNS: RegExp[] = [\n /^(?:hi|hello|hey|yo|hiya)[!. ]*$/i,\n /^(?:thanks|thank you|thx|tysm)[!. ]*$/i,\n /^(?:ok|okay|kk|cool|nice|great|awesome|got it|sounds good|sure|yep|yup|nah|nope)[!. ]*$/i,\n /^(?:good morning|good afternoon|good evening|good night)[!. ]*$/i,\n /^(?:how are you|how's it going|what's up|sup|who are you|tell me a joke)[?!. ]*$/i,\n /^(?:please continue|continue|go on|carry on)[!. ]*$/i,\n];\n\n/**\n * Returns whether the query should go through memory retrieval.\n *\n * Only obviously non-retrieval chatter is rejected; everything else is allowed so we\n * don't accidentally suppress legitimate memory lookups.\n */\nexport function validateQueryForMemoryLookup(query: string): QueryValidationResult {\n const normalized = query.replace(/\\s+/g, \" \").trim();\n\n if (!normalized) {\n return { requiresLookup: false, reason: \"empty-query\" };\n }\n\n if (!/[\\p{L}\\p{N}]/u.test(normalized)) {\n return { requiresLookup: false, reason: \"non-semantic-input\" };\n }\n\n if (FILLER_PATTERNS.some((pattern) => pattern.test(normalized))) {\n return { requiresLookup: false, reason: \"conversational-filler\" };\n }\n\n return { requiresLookup: true, reason: \"memory-lookup-candidate\" };\n}\n"],"mappings":";AAaA,MAAM,kBAA4B;CAChC;CACA;CACA;CACA;CACA;CACA;AACF;;;;;;;AAQA,SAAgB,6BAA6B,OAAsC;CACjF,MAAM,aAAa,MAAM,QAAQ,QAAQ,GAAG,EAAE,KAAK;CAEnD,IAAI,CAAC,YACH,OAAO;EAAE,gBAAgB;EAAO,QAAQ;CAAc;CAGxD,IAAI,CAAC,gBAAgB,KAAK,UAAU,GAClC,OAAO;EAAE,gBAAgB;EAAO,QAAQ;CAAqB;CAG/D,IAAI,gBAAgB,MAAM,YAAY,QAAQ,KAAK,UAAU,CAAC,GAC5D,OAAO;EAAE,gBAAgB;EAAO,QAAQ;CAAwB;CAGlE,OAAO;EAAE,gBAAgB;EAAM,QAAQ;CAA0B;AACnE"}
@@ -1 +1 @@
1
- {"version":3,"file":"recall-pipeline.js","names":[],"sources":["../../services/recall-pipeline.ts"],"sourcesContent":["/**\n * Recall pipeline core — extracted from lifecycle/stage-recall.ts (Issue #498).\n *\n * `runRecallPipelineQuery` is the single-query FTS+vector search step.\n * All dependencies are passed explicitly so the function is unit-testable\n * without instantiating the full LifecycleContext.\n *\n * Engineering Goal 3: Strict Separation of Concerns\n * Engineering Goal 4: Testability (simple stubs, no live API keys)\n */\n\nimport type OpenAI from \"openai\";\nimport type { FactsDB } from \"../backends/facts-db.js\";\nimport type { VectorDB } from \"../backends/vector-db.js\";\nimport type { QueryExpansionConfig } from \"../config.js\";\nimport type { getCronModelConfig } from \"../config.js\";\nimport type { PendingLLMWarnings } from \"../services/chat.js\";\nimport { capturePluginError } from \"../services/error-reporter.js\";\nimport { filterByScope, mergeResults } from \"../services/merge-results.js\";\nimport type { ScopeFilter, SearchResult } from \"../types/memory.js\";\nimport { applyConsolidationRetrievalControls } from \"../utils/consolidation-controls.js\";\nimport { yieldEventLoop } from \"../utils/event-loop-yield.js\";\nimport { computeDynamicSalience } from \"../utils/salience.js\";\nimport type { EmbeddingProvider } from \"./embeddings.js\";\nimport { shouldSuppressEmbeddingError } from \"./embeddings.js\";\nimport { expandQueryWithHyde } from \"./hyde-helper.js\";\nimport { createRecallSpan, createRecallTimingLogger } from \"./recall-timing.js\";\nimport { DEFAULT_INTERACTIVE_RECALL_POLICY, type InteractiveRecallPolicy } from \"./retrieval-mode-policy.js\";\nimport { isDbClosedError, isRegistrationSuperseded } from \"../utils/registration-superseded.js\";\n\nfunction isStalePipelineDbError(deps: RecallPipelineDeps, err: unknown): boolean {\n const gen = deps.registrationGeneration;\n if (gen === undefined || gen < 0) return false;\n return isRegistrationSuperseded(gen) && isDbClosedError(err);\n}\n\nasync function embedWithAbortRace(\n embedPromise: Promise<number[]>,\n signal: AbortSignal,\n abortMessage: string,\n): Promise<number[]> {\n let onAbort: (() => void) | undefined;\n try {\n return await Promise.race([\n embedPromise,\n new Promise<number[]>((_, reject) => {\n if (signal.aborted) {\n reject(Object.assign(new Error(abortMessage), { name: \"AbortError\" }));\n return;\n }\n onAbort = () => reject(Object.assign(new Error(abortMessage), { name: \"AbortError\" }));\n signal.addEventListener(\"abort\", onAbort, { once: true });\n }),\n ]);\n } finally {\n if (onAbort) signal.removeEventListener(\"abort\", onAbort);\n }\n}\n\n/** Logger subset required by the recall pipeline (avoids importing ClawdbotPluginApi). */\nexport interface RecallLogger {\n debug?: (msg: string) => void;\n warn: (msg: string) => void;\n}\n\n/** Minimal subset of HybridMemoryConfig sections consumed by the pipeline. */\nexport interface RecallPipelineCfg {\n queryExpansion: QueryExpansionConfig;\n retrievalStrategies: Array<\"semantic\" | \"fts5\" | \"graph\">;\n memoryTieringEnabled: boolean;\n recallTiming?: \"off\" | \"basic\" | \"verbose\";\n /** Passed through to getCronModelConfig/getLLMModelPreference — the raw cfg object. */\n rawCfg: Parameters<typeof getCronModelConfig>[0];\n}\n\n/** Search options that `factsDb.search()` / `factsDb.lookup()` accept. */\nexport interface RecallSearchOpts {\n tierFilter: \"warm\" | \"all\";\n scopeFilter: ScopeFilter | undefined;\n reinforcementBoost: number;\n diversityWeight: number;\n /** Passed to `FactsDB.search` — bounded FTS + two-phase fetch on interactive recall. */\n interactiveFtsFastPath?: boolean;\n}\n\n/** All explicit dependencies consumed by `runRecallPipelineQuery`. */\nexport interface RecallPipelineDeps {\n factsDb: Pick<FactsDB, \"search\" | \"getById\" | \"lookup\" | \"getSupersededTexts\">;\n vectorDb: Pick<VectorDB, \"search\">;\n embeddings: Pick<EmbeddingProvider, \"embed\">;\n openai: OpenAI;\n cfg: RecallPipelineCfg;\n recallOpts: RecallSearchOpts;\n minScore: number;\n pendingLLMWarnings: PendingLLMWarnings;\n logger: RecallLogger;\n /** Hook generation for suppressing errors after hot reload closed DB handles. */\n registrationGeneration?: number;\n}\n\n/**\n * Run a single recall query: FTS + optional vector search, merge, tier-filter.\n *\n * @param query - The user query (trimmed internally)\n * @param limitNum - Max results to return\n * @param deps - Explicit dependencies (testable via simple stubs)\n * @param hydeUsedRef - Shared mutable ref: when `limitHydeOnce` is set, the\n * first call that runs HyDE sets this to `true` and\n * subsequent calls skip HyDE. Pass `{ value: false }`\n * per-top-level recall invocation.\n * @param opts - Per-call options (entity lookup, labels, etc.)\n */\nexport async function runRecallPipelineQuery(\n query: string,\n limitNum: number,\n deps: RecallPipelineDeps,\n hydeUsedRef: { value: boolean },\n opts?: {\n entity?: string;\n probeId?: string;\n hydeLabel?: string;\n errorPrefix?: string;\n limitHydeOnce?: boolean;\n precomputedVector?: number[];\n policy?: InteractiveRecallPolicy;\n timingSpan?: string;\n timingOp?: string;\n },\n): Promise<SearchResult[]> {\n const { factsDb, vectorDb, embeddings, openai, cfg, recallOpts, minScore, pendingLLMWarnings, logger } = deps;\n\n const policy = opts?.policy ?? DEFAULT_INTERACTIVE_RECALL_POLICY;\n\n const trimmed = query.trim();\n if (!trimmed) return [];\n\n const useSemantic = cfg.retrievalStrategies.includes(\"semantic\");\n const recallTiming = createRecallTimingLogger({\n logger,\n mode: cfg.recallTiming ?? \"off\",\n span: opts?.timingSpan ?? createRecallSpan(\"recall\"),\n op: opts?.timingOp ?? `${opts?.errorPrefix ?? policy.mode}-pipeline`,\n });\n const stageMs = { fts: 0, embed: 0, vector: 0, merge: 0 };\n const pipelineWallT0 = Date.now();\n const probeId = opts?.probeId;\n let probeStage = useSemantic ? \"semantic_setup\" : \"fts_only_setup\";\n let vectorStepStatus = useSemantic ? \"pending\" : \"fts-only\";\n let pipelineProbeWatchdog: ReturnType<typeof setTimeout> | undefined;\n const clearPipelineProbeWatchdog = (): void => {\n if (pipelineProbeWatchdog !== undefined) clearTimeout(pipelineProbeWatchdog);\n };\n const probeDebug = (message: string): void => {\n if (probeId) logger.debug?.(message);\n };\n const probeWarn = (message: string): void => {\n if (probeId) logger.warn?.(message);\n };\n if (probeId) {\n pipelineProbeWatchdog = setTimeout(() => {\n const elapsedMs = Date.now() - pipelineWallT0;\n logger.warn?.(\n `memory-hybrid: recall-probe id=${probeId} pipeline-still-running elapsedMs=${elapsedMs} stage=${probeStage} semantic=${useSemantic} ftsMs=${stageMs.fts} embedMs=${stageMs.embed} vectorMs=${stageMs.vector} mergeMs=${stageMs.merge}`,\n );\n }, 5_000);\n pipelineProbeWatchdog.unref?.();\n probeDebug(\n `memory-hybrid: recall-probe id=${probeId} pipeline-enter semantic=${useSemantic} mode=${policy.mode} queryChars=${trimmed.length} limit=${limitNum}`,\n );\n }\n const runStartedAt = recallTiming.phaseStarted(\"pipeline_run\", {\n query_chars: trimmed.length,\n limit: limitNum,\n semantic_enabled: useSemantic,\n });\n\n let sqliteResults: SearchResult[] = [];\n let lanceResults: SearchResult[] = [];\n /** FTS row count from `factsDb.search` (for diagnostics). */\n let ftsRowCount = 0;\n\n /**\n * When semantic search is enabled, schedule FTS on the next macrotask so embedding / HyDE\n * I/O can run concurrently with synchronous SQLite FTS (#1050). Single-threaded CPU time\n * is unchanged, but wall-clock overlaps FTS with network-bound embed.\n */\n const runFtsSearchSync = (): {\n sqliteResults: SearchResult[];\n ftsRowCount: number;\n entityLookupRows: number;\n } => {\n let rows: SearchResult[] = [];\n let entityLookupRows = 0;\n if (opts?.entity) {\n const entityResults = factsDb\n .lookup(opts.entity, undefined, undefined, { scopeFilter: recallOpts.scopeFilter })\n .slice(0, limitNum);\n entityLookupRows = entityResults.length;\n rows = entityResults;\n }\n const ftsPart = factsDb.search(trimmed, limitNum, recallOpts);\n return {\n sqliteResults: [...rows, ...ftsPart],\n ftsRowCount: ftsPart.length,\n entityLookupRows,\n };\n };\n\n try {\n if (!useSemantic) {\n probeStage = \"fts_search\";\n probeDebug(`memory-hybrid: recall-probe id=${probeId} stage=fts_search start semantic=false`);\n const ftsStartedAt = recallTiming.phaseStarted(\"fts_search\", { limit: limitNum });\n const t0 = Date.now();\n const ftsOut = runFtsSearchSync();\n stageMs.fts = Date.now() - t0;\n sqliteResults = ftsOut.sqliteResults;\n ftsRowCount = ftsOut.ftsRowCount;\n recallTiming.phaseCompleted(\"fts_search\", ftsStartedAt, {\n fts_rows: ftsOut.ftsRowCount,\n entity_lookup_rows: ftsOut.entityLookupRows,\n sqlite_rows: ftsOut.sqliteResults.length,\n });\n probeDebug(\n `memory-hybrid: recall-probe id=${probeId} stage=fts_search end elapsedMs=${stageMs.fts} rows=${ftsOut.sqliteResults.length}`,\n );\n if (stageMs.fts >= 2_000) {\n probeWarn(\n `memory-hybrid: recall-probe id=${probeId} stage=fts_search slow elapsedMs=${stageMs.fts} rows=${ftsOut.sqliteResults.length}`,\n );\n }\n await yieldEventLoop();\n } else {\n const vectorStepStartedAt = recallTiming.phaseStarted(\"vector_step\");\n vectorStepStatus = \"ok\";\n const directiveAbort = new AbortController();\n\n // --- Phase 1: Kick off async I/O (HyDE / embed) BEFORE scheduling FTS ---\n // FTS runs synchronously inside setImmediate and can block the event loop for\n // tens of seconds on large fact stores (13k+ rows). If the vector-step timeout\n // (setTimeout) is registered before FTS, the timer fires after FTS unblocks but\n // the vector step has not had any CPU time — producing a spurious timeout.\n //\n // Fix (#42): initiate the network-bound embed call first so the HTTP request is\n // in-flight while FTS occupies the CPU, then schedule FTS on the next macrotask.\n // The timeout is armed only after FTS yields, so it measures actual vector-step\n // execution time rather than event-loop starvation from FTS.\n\n let textToEmbed = trimmed;\n const allowHyde = policy.allowHyde && cfg.queryExpansion.enabled && (!opts?.limitHydeOnce || !hydeUsedRef.value);\n\n if (allowHyde) {\n if (opts?.limitHydeOnce) hydeUsedRef.value = true;\n if (!directiveAbort.signal.aborted) {\n probeStage = \"hyde_generation\";\n probeDebug(`memory-hybrid: recall-probe id=${probeId} stage=hyde_generation start`);\n const hydeStartedAt = recallTiming.phaseStarted(\"hyde_generation\");\n textToEmbed = await expandQueryWithHyde({\n query: trimmed,\n rawCfg: cfg.rawCfg,\n model: cfg.queryExpansion.model,\n timeoutMs: cfg.queryExpansion.timeoutMs,\n openai,\n label: opts?.hydeLabel ?? \"HyDE\",\n signal: directiveAbort.signal,\n pendingWarnings: pendingLLMWarnings,\n logger,\n subsystem: \"auto-recall\",\n operation: `${opts?.errorPrefix ?? \"\"}hyde-generation`,\n });\n recallTiming.phaseCompleted(\"hyde_generation\", hydeStartedAt, {\n input_chars: trimmed.length,\n output_chars: textToEmbed.length,\n });\n probeDebug(\n `memory-hybrid: recall-probe id=${probeId} stage=hyde_generation end outputChars=${textToEmbed.length}`,\n );\n }\n }\n\n const _precomputedVector = opts?.precomputedVector;\n const usePrecomputedVector = Boolean(_precomputedVector) && textToEmbed === trimmed;\n\n // Kick off the embed HTTP request NOW (before FTS blocks the loop).\n // The returned promise will resolve once the network response arrives — which\n // can happen while FTS is occupying the CPU on the next macrotask.\n // Wall-clock for `stageMs.embed` / telemetry: HyDE + FTS must not be counted as \"embed\" time.\n const embedT0 = Date.now();\n const embedPromise = usePrecomputedVector\n ? Promise.resolve(_precomputedVector as number[])\n : embeddings.embed(textToEmbed);\n\n // --- Phase 2: Run FTS synchronously on the next macrotask ---\n const ftsPromise = new Promise<SearchResult[]>((resolve, reject) => {\n setImmediate(() => {\n try {\n probeStage = \"semantic_fts_search\";\n probeDebug(`memory-hybrid: recall-probe id=${probeId} stage=semantic_fts_search start`);\n const ftsStartedAt = recallTiming.phaseStarted(\"fts_search\", { limit: limitNum });\n const t0 = Date.now();\n const ftsOut = runFtsSearchSync();\n stageMs.fts = Date.now() - t0;\n ftsRowCount = ftsOut.ftsRowCount;\n recallTiming.phaseCompleted(\"fts_search\", ftsStartedAt, {\n fts_rows: ftsOut.ftsRowCount,\n entity_lookup_rows: ftsOut.entityLookupRows,\n sqlite_rows: ftsOut.sqliteResults.length,\n });\n probeDebug(\n `memory-hybrid: recall-probe id=${probeId} stage=semantic_fts_search end elapsedMs=${stageMs.fts} rows=${ftsOut.sqliteResults.length}`,\n );\n if (stageMs.fts >= 2_000) {\n probeWarn(\n `memory-hybrid: recall-probe id=${probeId} stage=semantic_fts_search slow elapsedMs=${stageMs.fts} rows=${ftsOut.sqliteResults.length}`,\n );\n }\n resolve(ftsOut.sqliteResults);\n } catch (err) {\n reject(err instanceof Error ? err : new Error(String(err)));\n }\n });\n });\n\n // --- Phase 3: Await FTS first, then arm timeout and run vector step ---\n // By awaiting ftsPromise here we guarantee the timeout is only armed AFTER FTS\n // has finished and the event loop is free. This prevents FTS event-loop\n // starvation from eating into the vector-step budget.\n let ftsRows: SearchResult[];\n try {\n ftsRows = await ftsPromise;\n } catch (ftsErr) {\n // Embed was started before FTS; if FTS fails first, fire-and-forget the\n // embed promise so a fast network rejection cannot surface as an unhandled\n // rejection, without blocking the error path on embed settling.\n embedPromise.catch(() => {\n /* intentionally ignored */\n });\n throw ftsErr;\n }\n\n const vectorStepPromise = (async (): Promise<SearchResult[]> => {\n if (directiveAbort.signal.aborted) {\n const abortError = new Error(`recall pipeline timed out after ${policy.vectorStepTimeoutMs}ms`);\n abortError.name = \"AbortError\";\n throw abortError;\n }\n\n probeStage = \"embed_query\";\n probeDebug(\n `memory-hybrid: recall-probe id=${probeId} stage=embed_query start precomputed=${usePrecomputedVector}`,\n );\n const embedStartedAt = recallTiming.phaseStarted(\"embed_query\", {\n precomputed_vector: usePrecomputedVector,\n });\n const vector = usePrecomputedVector\n ? (_precomputedVector as number[])\n : await embedWithAbortRace(\n embedPromise.then((v) => {\n stageMs.embed = Date.now() - embedT0;\n return v;\n }),\n directiveAbort.signal,\n `recall pipeline timed out after ${policy.vectorStepTimeoutMs}ms`,\n );\n if (usePrecomputedVector) {\n stageMs.embed = Date.now() - embedT0;\n }\n recallTiming.phaseCompleted(\"embed_query\", embedStartedAt, {\n precomputed_vector: usePrecomputedVector,\n input_chars: textToEmbed.length,\n });\n probeDebug(\n `memory-hybrid: recall-probe id=${probeId} stage=embed_query end elapsedMs=${stageMs.embed} inputChars=${textToEmbed.length}`,\n );\n if (stageMs.embed >= 2_000) {\n probeWarn(\n `memory-hybrid: recall-probe id=${probeId} stage=embed_query slow elapsedMs=${stageMs.embed} inputChars=${textToEmbed.length}`,\n );\n }\n\n probeStage = \"lancedb_search\";\n probeDebug(\n `memory-hybrid: recall-probe id=${probeId} stage=lancedb_search start limit=${limitNum * 2} minScore=${minScore}`,\n );\n const vectorStartedAt = recallTiming.phaseStarted(\"lancedb_search\", {\n limit: limitNum * 2,\n min_score: minScore,\n });\n const vecT0 = Date.now();\n const rawResults = await vectorDb.search(vector, limitNum * 2, minScore);\n stageMs.vector = Date.now() - vecT0;\n const nowSec = Math.floor(Date.now() / 1000);\n let results = filterByScope(\n rawResults,\n (id, o) => {\n const entry = factsDb.getById(id, o);\n if (!entry) return null;\n if (entry.supersededAt != null) return null;\n if (entry.expiresAt != null && entry.expiresAt <= nowSec) return null;\n return entry;\n },\n recallOpts.scopeFilter,\n );\n results = results\n .map((r) => {\n const fullEntry = factsDb.getById(r.entry.id);\n if (\n fullEntry &&\n fullEntry.supersededAt == null &&\n (fullEntry.expiresAt == null || fullEntry.expiresAt > nowSec)\n ) {\n const salienceScore = computeDynamicSalience(r.score, fullEntry);\n const controlledScore = applyConsolidationRetrievalControls(salienceScore, fullEntry);\n return { ...r, entry: fullEntry, score: controlledScore };\n }\n return null;\n })\n .filter((r): r is SearchResult => r !== null);\n recallTiming.phaseCompleted(\"lancedb_search\", vectorStartedAt, {\n raw_hits: rawResults.length,\n hits: results.length,\n });\n probeDebug(\n `memory-hybrid: recall-probe id=${probeId} stage=lancedb_search end elapsedMs=${stageMs.vector} rawHits=${rawResults.length} hits=${results.length}`,\n );\n if (stageMs.vector >= 2_000) {\n probeWarn(\n `memory-hybrid: recall-probe id=${probeId} stage=lancedb_search slow elapsedMs=${stageMs.vector} rawHits=${rawResults.length} hits=${results.length}`,\n );\n }\n return results;\n })();\n\n let timeoutId: NodeJS.Timeout | undefined;\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutId = setTimeout(() => {\n probeWarn(\n `memory-hybrid: recall-probe id=${probeId} stage=vector_timeout fired elapsedMs=${Date.now() - pipelineWallT0} configuredMs=${policy.vectorStepTimeoutMs} probeStage=${probeStage}`,\n );\n directiveAbort.abort();\n reject(new Error(`${policy.mode} timed out after ${policy.vectorStepTimeoutMs}ms`));\n }, policy.vectorStepTimeoutMs);\n });\n\n const vectorRacePromise = Promise.race([vectorStepPromise, timeoutPromise])\n .catch((err: unknown) => {\n const isTimeout = err instanceof Error && err.message.includes(\"timed out\");\n vectorStepStatus = isTimeout ? \"timeout\" : \"error\";\n if (isTimeout) logger.warn(`memory-hybrid: ${String(err)}, using FTS-only recall`);\n else {\n if (isStalePipelineDbError(deps, err)) {\n probeDebug(\n `memory-hybrid: recall-probe id=${probeId} ${opts?.errorPrefix ?? \"\"}vector recall skipped (registration superseded)`,\n );\n return [] as SearchResult[];\n }\n if (!shouldSuppressEmbeddingError(err)) {\n capturePluginError(err instanceof Error ? err : new Error(String(err)), {\n operation: `${opts?.errorPrefix ?? \"\"}vector-recall`,\n subsystem: \"auto-recall\",\n backend: \"lancedb\",\n });\n }\n logger.warn(`memory-hybrid: ${opts?.errorPrefix ?? \"\"}vector recall failed: ${err}`);\n }\n return [] as SearchResult[];\n })\n .finally(() => {\n if (timeoutId !== undefined) clearTimeout(timeoutId);\n });\n\n sqliteResults = ftsRows;\n lanceResults = await vectorRacePromise;\n recallTiming.phaseCompleted(\"vector_step\", vectorStepStartedAt, {\n status: vectorStepStatus,\n hits: lanceResults.length,\n });\n\n vectorStepPromise.catch((err) => {\n if (\n !directiveAbort.signal.aborted &&\n !isStalePipelineDbError(deps, err) &&\n !shouldSuppressEmbeddingError(err)\n ) {\n capturePluginError(err instanceof Error ? err : new Error(String(err)), {\n operation: `${opts?.errorPrefix ?? \"\"}vector-recall-post-timeout`,\n subsystem: \"auto-recall\",\n });\n }\n });\n }\n\n await yieldEventLoop();\n\n probeStage = \"merge_results\";\n const mergeStartedAt = recallTiming.phaseStarted(\"merge_results\");\n const mergeT0 = Date.now();\n let results: SearchResult[];\n try {\n results = mergeResults(sqliteResults, lanceResults, limitNum, factsDb);\n } catch (err) {\n if (isStalePipelineDbError(deps, err)) {\n probeDebug(`memory-hybrid: recall-probe id=${probeId} merge_results skipped (registration superseded)`);\n return [];\n }\n throw err;\n }\n stageMs.merge = Date.now() - mergeT0;\n recallTiming.phaseCompleted(\"merge_results\", mergeStartedAt, {\n sqlite_rows: sqliteResults.length,\n vector_hits: lanceResults.length,\n merged_rows: results.length,\n });\n probeDebug(\n `memory-hybrid: recall-probe id=${probeId} stage=merge_results end elapsedMs=${stageMs.merge} mergedRows=${results.length}`,\n );\n if (stageMs.merge >= 2_000) {\n probeWarn(\n `memory-hybrid: recall-probe id=${probeId} stage=merge_results slow elapsedMs=${stageMs.merge} mergedRows=${results.length}`,\n );\n }\n\n if (cfg.memoryTieringEnabled && results.length > 0) {\n results = results\n .filter((r) => {\n const full = factsDb.getById(r.entry.id);\n return full && full.tier !== \"cold\";\n })\n .slice(0, limitNum);\n }\n\n const wallClockMs = Date.now() - pipelineWallT0;\n logger.debug?.(\n `memory-hybrid: ${policy.mode} timing (ms) — FTS: ${stageMs.fts}, embed: ${stageMs.embed}, vector: ${stageMs.vector}, merge: ${stageMs.merge}, wall: ${wallClockMs}`,\n );\n probeDebug(\n `memory-hybrid: recall-probe id=${probeId} pipeline-exit elapsedMs=${wallClockMs} semantic=${useSemantic} status=${vectorStepStatus} mergedRows=${results.length}`,\n );\n if (wallClockMs >= 5_000) {\n probeWarn(\n `memory-hybrid: recall-probe id=${probeId} pipeline-slow elapsedMs=${wallClockMs} semantic=${useSemantic} status=${vectorStepStatus} ftsMs=${stageMs.fts} embedMs=${stageMs.embed} vectorMs=${stageMs.vector} mergeMs=${stageMs.merge}`,\n );\n }\n recallTiming.phaseCompleted(\"pipeline_run\", runStartedAt, {\n fts_ms: stageMs.fts,\n embed_ms: stageMs.embed,\n vector_ms: stageMs.vector,\n merge_ms: stageMs.merge,\n wall_ms: wallClockMs,\n fts_rows: ftsRowCount,\n vector_hits: lanceResults.length,\n merged_rows: results.length,\n });\n\n return results;\n } catch (err) {\n if (isStalePipelineDbError(deps, err)) {\n probeDebug(\n `memory-hybrid: recall-probe id=${probeId} pipeline skipped (registration superseded) elapsedMs=${Date.now() - pipelineWallT0} stage=${probeStage}`,\n );\n return [];\n }\n probeWarn(\n `memory-hybrid: recall-probe id=${probeId} pipeline-error elapsedMs=${Date.now() - pipelineWallT0} stage=${probeStage} semantic=${useSemantic} error=${err instanceof Error ? err.message : String(err)}`,\n );\n throw err;\n } finally {\n clearPipelineProbeWatchdog();\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"recall-pipeline.js","names":[],"sources":["../../services/recall-pipeline.ts"],"sourcesContent":["/**\n * Recall pipeline core — extracted from lifecycle/stage-recall.ts (Issue #498).\n *\n * `runRecallPipelineQuery` is the single-query FTS+vector search step.\n * All dependencies are passed explicitly so the function is unit-testable\n * without instantiating the full LifecycleContext.\n *\n * Engineering Goal 3: Strict Separation of Concerns\n * Engineering Goal 4: Testability (simple stubs, no live API keys)\n */\n\nimport type OpenAI from \"openai\";\nimport type { FactsDB } from \"../backends/facts-db.js\";\nimport type { VectorDB } from \"../backends/vector-db.js\";\nimport type { QueryExpansionConfig } from \"../config.js\";\nimport type { getCronModelConfig } from \"../config.js\";\nimport type { PendingLLMWarnings } from \"../services/chat.js\";\nimport { capturePluginError } from \"../services/error-reporter.js\";\nimport { filterByScope, mergeResults } from \"../services/merge-results.js\";\nimport type { ScopeFilter, SearchResult } from \"../types/memory.js\";\nimport { applyConsolidationRetrievalControls } from \"../utils/consolidation-controls.js\";\nimport { yieldEventLoop } from \"../utils/event-loop-yield.js\";\nimport { computeDynamicSalience } from \"../utils/salience.js\";\nimport type { EmbeddingProvider } from \"./embeddings.js\";\nimport { shouldSuppressEmbeddingError } from \"./embeddings.js\";\nimport { expandQueryWithHyde } from \"./hyde-helper.js\";\nimport { createRecallSpan, createRecallTimingLogger } from \"./recall-timing.js\";\nimport { DEFAULT_INTERACTIVE_RECALL_POLICY, type InteractiveRecallPolicy } from \"./retrieval-mode-policy.js\";\nimport { isDbClosedError, isRegistrationSuperseded } from \"../utils/registration-superseded.js\";\n\nfunction isStalePipelineDbError(deps: RecallPipelineDeps, err: unknown): boolean {\n const gen = deps.registrationGeneration;\n if (gen === undefined || gen < 0) return false;\n return isRegistrationSuperseded(gen) && isDbClosedError(err);\n}\n\nasync function embedWithAbortRace(\n embedPromise: Promise<number[]>,\n signal: AbortSignal,\n abortMessage: string,\n): Promise<number[]> {\n let onAbort: (() => void) | undefined;\n try {\n return await Promise.race([\n embedPromise,\n new Promise<number[]>((_, reject) => {\n if (signal.aborted) {\n reject(Object.assign(new Error(abortMessage), { name: \"AbortError\" }));\n return;\n }\n onAbort = () => reject(Object.assign(new Error(abortMessage), { name: \"AbortError\" }));\n signal.addEventListener(\"abort\", onAbort, { once: true });\n }),\n ]);\n } finally {\n if (onAbort) signal.removeEventListener(\"abort\", onAbort);\n }\n}\n\n/** Logger subset required by the recall pipeline (avoids importing ClawdbotPluginApi). */\nexport interface RecallLogger {\n debug?: (msg: string) => void;\n warn: (msg: string) => void;\n}\n\n/** Minimal subset of HybridMemoryConfig sections consumed by the pipeline. */\nexport interface RecallPipelineCfg {\n queryExpansion: QueryExpansionConfig;\n retrievalStrategies: Array<\"semantic\" | \"fts5\" | \"graph\">;\n memoryTieringEnabled: boolean;\n recallTiming?: \"off\" | \"basic\" | \"verbose\";\n /** Passed through to getCronModelConfig/getLLMModelPreference — the raw cfg object. */\n rawCfg: Parameters<typeof getCronModelConfig>[0];\n}\n\n/** Search options that `factsDb.search()` / `factsDb.lookup()` accept. */\nexport interface RecallSearchOpts {\n tierFilter: \"warm\" | \"all\";\n scopeFilter: ScopeFilter | undefined;\n reinforcementBoost: number;\n diversityWeight: number;\n /** Passed to `FactsDB.search` — bounded FTS + two-phase fetch on interactive recall. */\n interactiveFtsFastPath?: boolean;\n}\n\n/** All explicit dependencies consumed by `runRecallPipelineQuery`. */\nexport interface RecallPipelineDeps {\n factsDb: Pick<FactsDB, \"search\" | \"getById\" | \"lookup\" | \"getSupersededTexts\">;\n vectorDb: Pick<VectorDB, \"search\">;\n embeddings: Pick<EmbeddingProvider, \"embed\">;\n openai: OpenAI;\n cfg: RecallPipelineCfg;\n recallOpts: RecallSearchOpts;\n minScore: number;\n pendingLLMWarnings: PendingLLMWarnings;\n logger: RecallLogger;\n /** Hook generation for suppressing errors after hot reload closed DB handles. */\n registrationGeneration?: number;\n}\n\n/**\n * Run a single recall query: FTS + optional vector search, merge, tier-filter.\n *\n * @param query - The user query (trimmed internally)\n * @param limitNum - Max results to return\n * @param deps - Explicit dependencies (testable via simple stubs)\n * @param hydeUsedRef - Shared mutable ref: when `limitHydeOnce` is set, the\n * first call that runs HyDE sets this to `true` and\n * subsequent calls skip HyDE. Pass `{ value: false }`\n * per-top-level recall invocation.\n * @param opts - Per-call options (entity lookup, labels, etc.)\n */\nexport async function runRecallPipelineQuery(\n query: string,\n limitNum: number,\n deps: RecallPipelineDeps,\n hydeUsedRef: { value: boolean },\n opts?: {\n entity?: string;\n probeId?: string;\n hydeLabel?: string;\n errorPrefix?: string;\n limitHydeOnce?: boolean;\n precomputedVector?: number[];\n policy?: InteractiveRecallPolicy;\n timingSpan?: string;\n timingOp?: string;\n },\n): Promise<SearchResult[]> {\n const { factsDb, vectorDb, embeddings, openai, cfg, recallOpts, minScore, pendingLLMWarnings, logger } = deps;\n\n const policy = opts?.policy ?? DEFAULT_INTERACTIVE_RECALL_POLICY;\n\n const trimmed = query.trim();\n if (!trimmed) return [];\n\n const useSemantic = cfg.retrievalStrategies.includes(\"semantic\");\n const recallTiming = createRecallTimingLogger({\n logger,\n mode: cfg.recallTiming ?? \"off\",\n span: opts?.timingSpan ?? createRecallSpan(\"recall\"),\n op: opts?.timingOp ?? `${opts?.errorPrefix ?? policy.mode}-pipeline`,\n });\n const stageMs = { fts: 0, embed: 0, vector: 0, merge: 0 };\n const pipelineWallT0 = Date.now();\n const probeId = opts?.probeId;\n let probeStage = useSemantic ? \"semantic_setup\" : \"fts_only_setup\";\n let vectorStepStatus = useSemantic ? \"pending\" : \"fts-only\";\n let pipelineProbeWatchdog: ReturnType<typeof setTimeout> | undefined;\n const clearPipelineProbeWatchdog = (): void => {\n if (pipelineProbeWatchdog !== undefined) clearTimeout(pipelineProbeWatchdog);\n };\n const probeDebug = (message: string): void => {\n if (probeId) logger.debug?.(message);\n };\n const probeWarn = (message: string): void => {\n if (probeId) logger.warn?.(message);\n };\n if (probeId) {\n pipelineProbeWatchdog = setTimeout(() => {\n const elapsedMs = Date.now() - pipelineWallT0;\n logger.warn?.(\n `memory-hybrid: recall-probe id=${probeId} pipeline-still-running elapsedMs=${elapsedMs} stage=${probeStage} semantic=${useSemantic} ftsMs=${stageMs.fts} embedMs=${stageMs.embed} vectorMs=${stageMs.vector} mergeMs=${stageMs.merge}`,\n );\n }, 5_000);\n pipelineProbeWatchdog.unref?.();\n probeDebug(\n `memory-hybrid: recall-probe id=${probeId} pipeline-enter semantic=${useSemantic} mode=${policy.mode} queryChars=${trimmed.length} limit=${limitNum}`,\n );\n }\n const runStartedAt = recallTiming.phaseStarted(\"pipeline_run\", {\n query_chars: trimmed.length,\n limit: limitNum,\n semantic_enabled: useSemantic,\n });\n\n let sqliteResults: SearchResult[] = [];\n let lanceResults: SearchResult[] = [];\n /** FTS row count from `factsDb.search` (for diagnostics). */\n let ftsRowCount = 0;\n\n /**\n * When semantic search is enabled, schedule FTS on the next macrotask so embedding / HyDE\n * I/O can run concurrently with synchronous SQLite FTS (#1050). Single-threaded CPU time\n * is unchanged, but wall-clock overlaps FTS with network-bound embed.\n */\n const runFtsSearchSync = (): {\n sqliteResults: SearchResult[];\n ftsRowCount: number;\n entityLookupRows: number;\n } => {\n let rows: SearchResult[] = [];\n let entityLookupRows = 0;\n if (opts?.entity) {\n const entityResults = factsDb\n .lookup(opts.entity, undefined, undefined, { scopeFilter: recallOpts.scopeFilter })\n .slice(0, limitNum);\n entityLookupRows = entityResults.length;\n rows = entityResults;\n }\n const ftsPart = factsDb.search(trimmed, limitNum, recallOpts);\n return {\n sqliteResults: [...rows, ...ftsPart],\n ftsRowCount: ftsPart.length,\n entityLookupRows,\n };\n };\n\n try {\n if (!useSemantic) {\n probeStage = \"fts_search\";\n probeDebug(`memory-hybrid: recall-probe id=${probeId} stage=fts_search start semantic=false`);\n const ftsStartedAt = recallTiming.phaseStarted(\"fts_search\", { limit: limitNum });\n const t0 = Date.now();\n const ftsOut = runFtsSearchSync();\n stageMs.fts = Date.now() - t0;\n sqliteResults = ftsOut.sqliteResults;\n ftsRowCount = ftsOut.ftsRowCount;\n recallTiming.phaseCompleted(\"fts_search\", ftsStartedAt, {\n fts_rows: ftsOut.ftsRowCount,\n entity_lookup_rows: ftsOut.entityLookupRows,\n sqlite_rows: ftsOut.sqliteResults.length,\n });\n probeDebug(\n `memory-hybrid: recall-probe id=${probeId} stage=fts_search end elapsedMs=${stageMs.fts} rows=${ftsOut.sqliteResults.length}`,\n );\n if (stageMs.fts >= 2_000) {\n probeWarn(\n `memory-hybrid: recall-probe id=${probeId} stage=fts_search slow elapsedMs=${stageMs.fts} rows=${ftsOut.sqliteResults.length}`,\n );\n }\n await yieldEventLoop();\n } else {\n const vectorStepStartedAt = recallTiming.phaseStarted(\"vector_step\");\n vectorStepStatus = \"ok\";\n const directiveAbort = new AbortController();\n\n // --- Phase 1: Kick off async I/O (HyDE / embed) BEFORE scheduling FTS ---\n // FTS runs synchronously inside setImmediate and can block the event loop for\n // tens of seconds on large fact stores (13k+ rows). If the vector-step timeout\n // (setTimeout) is registered before FTS, the timer fires after FTS unblocks but\n // the vector step has not had any CPU time — producing a spurious timeout.\n //\n // Fix (#42): initiate the network-bound embed call first so the HTTP request is\n // in-flight while FTS occupies the CPU, then schedule FTS on the next macrotask.\n // The timeout is armed only after FTS yields, so it measures actual vector-step\n // execution time rather than event-loop starvation from FTS.\n\n let textToEmbed = trimmed;\n const allowHyde = policy.allowHyde && cfg.queryExpansion.enabled && (!opts?.limitHydeOnce || !hydeUsedRef.value);\n\n if (allowHyde) {\n if (opts?.limitHydeOnce) hydeUsedRef.value = true;\n if (!directiveAbort.signal.aborted) {\n probeStage = \"hyde_generation\";\n probeDebug(`memory-hybrid: recall-probe id=${probeId} stage=hyde_generation start`);\n const hydeStartedAt = recallTiming.phaseStarted(\"hyde_generation\");\n textToEmbed = await expandQueryWithHyde({\n query: trimmed,\n rawCfg: cfg.rawCfg,\n model: cfg.queryExpansion.model,\n timeoutMs: cfg.queryExpansion.timeoutMs,\n openai,\n label: opts?.hydeLabel ?? \"HyDE\",\n signal: directiveAbort.signal,\n pendingWarnings: pendingLLMWarnings,\n logger,\n subsystem: \"auto-recall\",\n operation: `${opts?.errorPrefix ?? \"\"}hyde-generation`,\n });\n recallTiming.phaseCompleted(\"hyde_generation\", hydeStartedAt, {\n input_chars: trimmed.length,\n output_chars: textToEmbed.length,\n });\n probeDebug(\n `memory-hybrid: recall-probe id=${probeId} stage=hyde_generation end outputChars=${textToEmbed.length}`,\n );\n }\n }\n\n const _precomputedVector = opts?.precomputedVector;\n const usePrecomputedVector = Boolean(_precomputedVector) && textToEmbed === trimmed;\n\n // Kick off the embed HTTP request NOW (before FTS blocks the loop).\n // The returned promise will resolve once the network response arrives — which\n // can happen while FTS is occupying the CPU on the next macrotask.\n // Wall-clock for `stageMs.embed` / telemetry: HyDE + FTS must not be counted as \"embed\" time.\n const embedT0 = Date.now();\n const embedPromise = usePrecomputedVector\n ? Promise.resolve(_precomputedVector as number[])\n : embeddings.embed(textToEmbed);\n\n // --- Phase 2: Run FTS synchronously on the next macrotask ---\n const ftsPromise = new Promise<SearchResult[]>((resolve, reject) => {\n setImmediate(() => {\n try {\n probeStage = \"semantic_fts_search\";\n probeDebug(`memory-hybrid: recall-probe id=${probeId} stage=semantic_fts_search start`);\n const ftsStartedAt = recallTiming.phaseStarted(\"fts_search\", { limit: limitNum });\n const t0 = Date.now();\n const ftsOut = runFtsSearchSync();\n stageMs.fts = Date.now() - t0;\n ftsRowCount = ftsOut.ftsRowCount;\n recallTiming.phaseCompleted(\"fts_search\", ftsStartedAt, {\n fts_rows: ftsOut.ftsRowCount,\n entity_lookup_rows: ftsOut.entityLookupRows,\n sqlite_rows: ftsOut.sqliteResults.length,\n });\n probeDebug(\n `memory-hybrid: recall-probe id=${probeId} stage=semantic_fts_search end elapsedMs=${stageMs.fts} rows=${ftsOut.sqliteResults.length}`,\n );\n if (stageMs.fts >= 2_000) {\n probeWarn(\n `memory-hybrid: recall-probe id=${probeId} stage=semantic_fts_search slow elapsedMs=${stageMs.fts} rows=${ftsOut.sqliteResults.length}`,\n );\n }\n resolve(ftsOut.sqliteResults);\n } catch (err) {\n reject(err instanceof Error ? err : new Error(String(err)));\n }\n });\n });\n\n // --- Phase 3: Await FTS first, then arm timeout and run vector step ---\n // By awaiting ftsPromise here we guarantee the timeout is only armed AFTER FTS\n // has finished and the event loop is free. This prevents FTS event-loop\n // starvation from eating into the vector-step budget.\n let ftsRows: SearchResult[];\n try {\n ftsRows = await ftsPromise;\n } catch (ftsErr) {\n // Embed was started before FTS; if FTS fails first, fire-and-forget the\n // embed promise so a fast network rejection cannot surface as an unhandled\n // rejection, without blocking the error path on embed settling.\n embedPromise.catch(() => {\n /* intentionally ignored */\n });\n throw ftsErr;\n }\n\n const vectorStepPromise = (async (): Promise<SearchResult[]> => {\n if (directiveAbort.signal.aborted) {\n const abortError = new Error(`recall pipeline timed out after ${policy.vectorStepTimeoutMs}ms`);\n abortError.name = \"AbortError\";\n throw abortError;\n }\n\n probeStage = \"embed_query\";\n probeDebug(\n `memory-hybrid: recall-probe id=${probeId} stage=embed_query start precomputed=${usePrecomputedVector}`,\n );\n const embedStartedAt = recallTiming.phaseStarted(\"embed_query\", {\n precomputed_vector: usePrecomputedVector,\n });\n const vector = usePrecomputedVector\n ? (_precomputedVector as number[])\n : await embedWithAbortRace(\n embedPromise.then((v) => {\n stageMs.embed = Date.now() - embedT0;\n return v;\n }),\n directiveAbort.signal,\n `recall pipeline timed out after ${policy.vectorStepTimeoutMs}ms`,\n );\n if (usePrecomputedVector) {\n stageMs.embed = Date.now() - embedT0;\n }\n recallTiming.phaseCompleted(\"embed_query\", embedStartedAt, {\n precomputed_vector: usePrecomputedVector,\n input_chars: textToEmbed.length,\n });\n probeDebug(\n `memory-hybrid: recall-probe id=${probeId} stage=embed_query end elapsedMs=${stageMs.embed} inputChars=${textToEmbed.length}`,\n );\n if (stageMs.embed >= 2_000) {\n probeWarn(\n `memory-hybrid: recall-probe id=${probeId} stage=embed_query slow elapsedMs=${stageMs.embed} inputChars=${textToEmbed.length}`,\n );\n }\n\n probeStage = \"lancedb_search\";\n probeDebug(\n `memory-hybrid: recall-probe id=${probeId} stage=lancedb_search start limit=${limitNum * 2} minScore=${minScore}`,\n );\n const vectorStartedAt = recallTiming.phaseStarted(\"lancedb_search\", {\n limit: limitNum * 2,\n min_score: minScore,\n });\n const vecT0 = Date.now();\n const rawResults = await vectorDb.search(vector, limitNum * 2, minScore);\n stageMs.vector = Date.now() - vecT0;\n const nowSec = Math.floor(Date.now() / 1000);\n let results = filterByScope(\n rawResults,\n (id, o) => {\n const entry = factsDb.getById(id, o);\n if (!entry) return null;\n if (entry.supersededAt != null) return null;\n if (entry.expiresAt != null && entry.expiresAt <= nowSec) return null;\n return entry;\n },\n recallOpts.scopeFilter,\n );\n results = results\n .map((r) => {\n const fullEntry = factsDb.getById(r.entry.id);\n if (\n fullEntry &&\n fullEntry.supersededAt == null &&\n (fullEntry.expiresAt == null || fullEntry.expiresAt > nowSec)\n ) {\n const salienceScore = computeDynamicSalience(r.score, fullEntry);\n const controlledScore = applyConsolidationRetrievalControls(salienceScore, fullEntry);\n return { ...r, entry: fullEntry, score: controlledScore };\n }\n return null;\n })\n .filter((r): r is SearchResult => r !== null);\n recallTiming.phaseCompleted(\"lancedb_search\", vectorStartedAt, {\n raw_hits: rawResults.length,\n hits: results.length,\n });\n probeDebug(\n `memory-hybrid: recall-probe id=${probeId} stage=lancedb_search end elapsedMs=${stageMs.vector} rawHits=${rawResults.length} hits=${results.length}`,\n );\n if (stageMs.vector >= 2_000) {\n probeWarn(\n `memory-hybrid: recall-probe id=${probeId} stage=lancedb_search slow elapsedMs=${stageMs.vector} rawHits=${rawResults.length} hits=${results.length}`,\n );\n }\n return results;\n })();\n\n let timeoutId: NodeJS.Timeout | undefined;\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutId = setTimeout(() => {\n probeWarn(\n `memory-hybrid: recall-probe id=${probeId} stage=vector_timeout fired elapsedMs=${Date.now() - pipelineWallT0} configuredMs=${policy.vectorStepTimeoutMs} probeStage=${probeStage}`,\n );\n directiveAbort.abort();\n reject(new Error(`${policy.mode} timed out after ${policy.vectorStepTimeoutMs}ms`));\n }, policy.vectorStepTimeoutMs);\n });\n\n const vectorRacePromise = Promise.race([vectorStepPromise, timeoutPromise])\n .catch((err: unknown) => {\n const isTimeout = err instanceof Error && err.message.includes(\"timed out\");\n vectorStepStatus = isTimeout ? \"timeout\" : \"error\";\n if (isTimeout) logger.warn(`memory-hybrid: ${String(err)}, using FTS-only recall`);\n else {\n if (isStalePipelineDbError(deps, err)) {\n probeDebug(\n `memory-hybrid: recall-probe id=${probeId} ${opts?.errorPrefix ?? \"\"}vector recall skipped (registration superseded)`,\n );\n return [] as SearchResult[];\n }\n if (!shouldSuppressEmbeddingError(err)) {\n capturePluginError(err instanceof Error ? err : new Error(String(err)), {\n operation: `${opts?.errorPrefix ?? \"\"}vector-recall`,\n subsystem: \"auto-recall\",\n backend: \"lancedb\",\n });\n }\n logger.warn(`memory-hybrid: ${opts?.errorPrefix ?? \"\"}vector recall failed: ${err}`);\n }\n return [] as SearchResult[];\n })\n .finally(() => {\n if (timeoutId !== undefined) clearTimeout(timeoutId);\n });\n\n sqliteResults = ftsRows;\n lanceResults = await vectorRacePromise;\n recallTiming.phaseCompleted(\"vector_step\", vectorStepStartedAt, {\n status: vectorStepStatus,\n hits: lanceResults.length,\n });\n\n vectorStepPromise.catch((err) => {\n if (\n !directiveAbort.signal.aborted &&\n !isStalePipelineDbError(deps, err) &&\n !shouldSuppressEmbeddingError(err)\n ) {\n capturePluginError(err instanceof Error ? err : new Error(String(err)), {\n operation: `${opts?.errorPrefix ?? \"\"}vector-recall-post-timeout`,\n subsystem: \"auto-recall\",\n });\n }\n });\n }\n\n await yieldEventLoop();\n\n probeStage = \"merge_results\";\n const mergeStartedAt = recallTiming.phaseStarted(\"merge_results\");\n const mergeT0 = Date.now();\n let results: SearchResult[];\n try {\n results = mergeResults(sqliteResults, lanceResults, limitNum, factsDb);\n } catch (err) {\n if (isStalePipelineDbError(deps, err)) {\n probeDebug(`memory-hybrid: recall-probe id=${probeId} merge_results skipped (registration superseded)`);\n return [];\n }\n throw err;\n }\n stageMs.merge = Date.now() - mergeT0;\n recallTiming.phaseCompleted(\"merge_results\", mergeStartedAt, {\n sqlite_rows: sqliteResults.length,\n vector_hits: lanceResults.length,\n merged_rows: results.length,\n });\n probeDebug(\n `memory-hybrid: recall-probe id=${probeId} stage=merge_results end elapsedMs=${stageMs.merge} mergedRows=${results.length}`,\n );\n if (stageMs.merge >= 2_000) {\n probeWarn(\n `memory-hybrid: recall-probe id=${probeId} stage=merge_results slow elapsedMs=${stageMs.merge} mergedRows=${results.length}`,\n );\n }\n\n if (cfg.memoryTieringEnabled && results.length > 0) {\n results = results\n .filter((r) => {\n const full = factsDb.getById(r.entry.id);\n return full && full.tier !== \"cold\";\n })\n .slice(0, limitNum);\n }\n\n const wallClockMs = Date.now() - pipelineWallT0;\n logger.debug?.(\n `memory-hybrid: ${policy.mode} timing (ms) — FTS: ${stageMs.fts}, embed: ${stageMs.embed}, vector: ${stageMs.vector}, merge: ${stageMs.merge}, wall: ${wallClockMs}`,\n );\n probeDebug(\n `memory-hybrid: recall-probe id=${probeId} pipeline-exit elapsedMs=${wallClockMs} semantic=${useSemantic} status=${vectorStepStatus} mergedRows=${results.length}`,\n );\n if (wallClockMs >= 5_000) {\n probeWarn(\n `memory-hybrid: recall-probe id=${probeId} pipeline-slow elapsedMs=${wallClockMs} semantic=${useSemantic} status=${vectorStepStatus} ftsMs=${stageMs.fts} embedMs=${stageMs.embed} vectorMs=${stageMs.vector} mergeMs=${stageMs.merge}`,\n );\n }\n recallTiming.phaseCompleted(\"pipeline_run\", runStartedAt, {\n fts_ms: stageMs.fts,\n embed_ms: stageMs.embed,\n vector_ms: stageMs.vector,\n merge_ms: stageMs.merge,\n wall_ms: wallClockMs,\n fts_rows: ftsRowCount,\n vector_hits: lanceResults.length,\n merged_rows: results.length,\n });\n\n return results;\n } catch (err) {\n if (isStalePipelineDbError(deps, err)) {\n probeDebug(\n `memory-hybrid: recall-probe id=${probeId} pipeline skipped (registration superseded) elapsedMs=${Date.now() - pipelineWallT0} stage=${probeStage}`,\n );\n return [];\n }\n probeWarn(\n `memory-hybrid: recall-probe id=${probeId} pipeline-error elapsedMs=${Date.now() - pipelineWallT0} stage=${probeStage} semantic=${useSemantic} error=${err instanceof Error ? err.message : String(err)}`,\n );\n throw err;\n } finally {\n clearPipelineProbeWatchdog();\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"recall-timing.js","names":[],"sources":["../../services/recall-timing.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\n\nexport type RecallTimingMode = \"off\" | \"basic\" | \"verbose\";\n\ntype RecallTimingValue = string | number | boolean | undefined | null;\n\nexport interface RecallTimingLogger {\n span: string;\n mode: RecallTimingMode;\n phaseStarted: (phase: string, fields?: Record<string, RecallTimingValue>) => number;\n phaseCompleted: (phase: string, startedAtMs: number, fields?: Record<string, RecallTimingValue>) => number;\n event: (phase: string, event: \"started\" | \"completed\", fields?: Record<string, RecallTimingValue>) => void;\n}\n\nfunction sanitize(value: string): string {\n return value.replace(/\\s+/g, \"_\");\n}\n\nfunction toFieldParts(fields: Record<string, RecallTimingValue> | undefined): string[] {\n if (!fields) return [];\n const parts: string[] = [];\n for (const [key, value] of Object.entries(fields)) {\n if (value === undefined || value === null) continue;\n parts.push(`${sanitize(key)}=${sanitize(String(value))}`);\n }\n return parts;\n}\n\nexport function createRecallSpan(prefix = \"recall\"): string {\n return `${prefix}-${randomUUID().split(\"-\")[0]}`;\n}\n\nexport function createRecallTimingLogger(args: {\n logger: { debug?: (msg: string) => void };\n mode: RecallTimingMode;\n span: string;\n op: string;\n subsystem?: string;\n}): RecallTimingLogger {\n const { logger, mode, span, op, subsystem = \"recall\" } = args;\n\n function event(phase: string, eventType: \"started\" | \"completed\", fields?: Record<string, RecallTimingValue>): void {\n if (mode === \"off\") return;\n if (eventType === \"started\" && mode !== \"verbose\") return;\n const parts = [\n `span=${sanitize(span)}`,\n `phase=${sanitize(phase)}`,\n `op=${sanitize(op)}`,\n `event=${eventType}`,\n ...toFieldParts(fields),\n ];\n if (mode === \"verbose\") {\n parts.push(`ts=${new Date().toISOString()}`);\n }\n logger.debug?.(`memory-hybrid: ${subsystem} ${parts.join(\" \")}`);\n }\n\n return {\n span,\n mode,\n phaseStarted(phase, fields) {\n const startedAtMs = Date.now();\n event(phase, \"started\", fields);\n return startedAtMs;\n },\n phaseCompleted(phase, startedAtMs, fields) {\n const durationMs = Math.max(0, Date.now() - startedAtMs);\n event(phase, \"completed\", { ...fields, duration_ms: durationMs });\n return durationMs;\n },\n event,\n };\n}\n"],"mappings":";;AAcA,SAAS,SAAS,OAAuB;CACvC,OAAO,MAAM,QAAQ,QAAQ,IAAI;;AAGnC,SAAS,aAAa,QAAiE;CACrF,IAAI,CAAC,QAAQ,OAAO,EAAE;CACtB,MAAM,QAAkB,EAAE;CAC1B,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,OAAO,EAAE;EACjD,IAAI,UAAU,KAAA,KAAa,UAAU,MAAM;EAC3C,MAAM,KAAK,GAAG,SAAS,IAAI,CAAC,GAAG,SAAS,OAAO,MAAM,CAAC,GAAG;;CAE3D,OAAO;;AAGT,SAAgB,iBAAiB,SAAS,UAAkB;CAC1D,OAAO,GAAG,OAAO,GAAG,YAAY,CAAC,MAAM,IAAI,CAAC;;AAG9C,SAAgB,yBAAyB,MAMlB;CACrB,MAAM,EAAE,QAAQ,MAAM,MAAM,IAAI,YAAY,aAAa;CAEzD,SAAS,MAAM,OAAe,WAAoC,QAAkD;EAClH,IAAI,SAAS,OAAO;EACpB,IAAI,cAAc,aAAa,SAAS,WAAW;EACnD,MAAM,QAAQ;GACZ,QAAQ,SAAS,KAAK;GACtB,SAAS,SAAS,MAAM;GACxB,MAAM,SAAS,GAAG;GAClB,SAAS;GACT,GAAG,aAAa,OAAO;GACxB;EACD,IAAI,SAAS,WACX,MAAM,KAAK,uBAAM,IAAI,MAAM,EAAC,aAAa,GAAG;EAE9C,OAAO,QAAQ,kBAAkB,UAAU,GAAG,MAAM,KAAK,IAAI,GAAG;;CAGlE,OAAO;EACL;EACA;EACA,aAAa,OAAO,QAAQ;GAC1B,MAAM,cAAc,KAAK,KAAK;GAC9B,MAAM,OAAO,WAAW,OAAO;GAC/B,OAAO;;EAET,eAAe,OAAO,aAAa,QAAQ;GACzC,MAAM,aAAa,KAAK,IAAI,GAAG,KAAK,KAAK,GAAG,YAAY;GACxD,MAAM,OAAO,aAAa;IAAE,GAAG;IAAQ,aAAa;IAAY,CAAC;GACjE,OAAO;;EAET;EACD"}
1
+ {"version":3,"file":"recall-timing.js","names":[],"sources":["../../services/recall-timing.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\n\nexport type RecallTimingMode = \"off\" | \"basic\" | \"verbose\";\n\ntype RecallTimingValue = string | number | boolean | undefined | null;\n\nexport interface RecallTimingLogger {\n span: string;\n mode: RecallTimingMode;\n phaseStarted: (phase: string, fields?: Record<string, RecallTimingValue>) => number;\n phaseCompleted: (phase: string, startedAtMs: number, fields?: Record<string, RecallTimingValue>) => number;\n event: (phase: string, event: \"started\" | \"completed\", fields?: Record<string, RecallTimingValue>) => void;\n}\n\nfunction sanitize(value: string): string {\n return value.replace(/\\s+/g, \"_\");\n}\n\nfunction toFieldParts(fields: Record<string, RecallTimingValue> | undefined): string[] {\n if (!fields) return [];\n const parts: string[] = [];\n for (const [key, value] of Object.entries(fields)) {\n if (value === undefined || value === null) continue;\n parts.push(`${sanitize(key)}=${sanitize(String(value))}`);\n }\n return parts;\n}\n\nexport function createRecallSpan(prefix = \"recall\"): string {\n return `${prefix}-${randomUUID().split(\"-\")[0]}`;\n}\n\nexport function createRecallTimingLogger(args: {\n logger: { debug?: (msg: string) => void };\n mode: RecallTimingMode;\n span: string;\n op: string;\n subsystem?: string;\n}): RecallTimingLogger {\n const { logger, mode, span, op, subsystem = \"recall\" } = args;\n\n function event(phase: string, eventType: \"started\" | \"completed\", fields?: Record<string, RecallTimingValue>): void {\n if (mode === \"off\") return;\n if (eventType === \"started\" && mode !== \"verbose\") return;\n const parts = [\n `span=${sanitize(span)}`,\n `phase=${sanitize(phase)}`,\n `op=${sanitize(op)}`,\n `event=${eventType}`,\n ...toFieldParts(fields),\n ];\n if (mode === \"verbose\") {\n parts.push(`ts=${new Date().toISOString()}`);\n }\n logger.debug?.(`memory-hybrid: ${subsystem} ${parts.join(\" \")}`);\n }\n\n return {\n span,\n mode,\n phaseStarted(phase, fields) {\n const startedAtMs = Date.now();\n event(phase, \"started\", fields);\n return startedAtMs;\n },\n phaseCompleted(phase, startedAtMs, fields) {\n const durationMs = Math.max(0, Date.now() - startedAtMs);\n event(phase, \"completed\", { ...fields, duration_ms: durationMs });\n return durationMs;\n },\n event,\n };\n}\n"],"mappings":";;AAcA,SAAS,SAAS,OAAuB;CACvC,OAAO,MAAM,QAAQ,QAAQ,GAAG;AAClC;AAEA,SAAS,aAAa,QAAiE;CACrF,IAAI,CAAC,QAAQ,OAAO,CAAC;CACrB,MAAM,QAAkB,CAAC;CACzB,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,MAAM,GAAG;EACjD,IAAI,UAAU,KAAA,KAAa,UAAU,MAAM;EAC3C,MAAM,KAAK,GAAG,SAAS,GAAG,EAAE,GAAG,SAAS,OAAO,KAAK,CAAC,GAAG;CAC1D;CACA,OAAO;AACT;AAEA,SAAgB,iBAAiB,SAAS,UAAkB;CAC1D,OAAO,GAAG,OAAO,GAAG,WAAW,EAAE,MAAM,GAAG,EAAE;AAC9C;AAEA,SAAgB,yBAAyB,MAMlB;CACrB,MAAM,EAAE,QAAQ,MAAM,MAAM,IAAI,YAAY,aAAa;CAEzD,SAAS,MAAM,OAAe,WAAoC,QAAkD;EAClH,IAAI,SAAS,OAAO;EACpB,IAAI,cAAc,aAAa,SAAS,WAAW;EACnD,MAAM,QAAQ;GACZ,QAAQ,SAAS,IAAI;GACrB,SAAS,SAAS,KAAK;GACvB,MAAM,SAAS,EAAE;GACjB,SAAS;GACT,GAAG,aAAa,MAAM;EACxB;EACA,IAAI,SAAS,WACX,MAAM,KAAK,uBAAM,IAAI,KAAK,GAAE,YAAY,GAAG;EAE7C,OAAO,QAAQ,kBAAkB,UAAU,GAAG,MAAM,KAAK,GAAG,GAAG;CACjE;CAEA,OAAO;EACL;EACA;EACA,aAAa,OAAO,QAAQ;GAC1B,MAAM,cAAc,KAAK,IAAI;GAC7B,MAAM,OAAO,WAAW,MAAM;GAC9B,OAAO;EACT;EACA,eAAe,OAAO,aAAa,QAAQ;GACzC,MAAM,aAAa,KAAK,IAAI,GAAG,KAAK,IAAI,IAAI,WAAW;GACvD,MAAM,OAAO,aAAa;IAAE,GAAG;IAAQ,aAAa;GAAW,CAAC;GAChE,OAAO;EACT;EACA;CACF;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"recent-http-attempts.js","names":[],"sources":["../../services/recent-http-attempts.ts"],"sourcesContent":["/**\n * In-process rolling record of outbound provider HTTP attempts (chat + embeddings).\n * Used only for diagnostics when rate limits fire — not authoritative billing data.\n */\n\nexport type RecentHttpAttempt = {\n t: number;\n operation?: string;\n model?: string;\n};\n\nconst MAX_EVENTS = 500;\nconst events: RecentHttpAttempt[] = [];\n\nfunction pruneOlderThan(cutoffMs: number): void {\n while (events.length > 0 && events[0].t < cutoffMs) {\n events.shift();\n }\n}\n\n/**\n * Record one outbound attempt before the SDK call (includes retries as separate rows).\n */\nexport function recordProviderHttpAttempt(meta: { operation?: string; model?: string }): void {\n const now = Date.now();\n events.push({ t: now, operation: meta.operation, model: meta.model });\n while (events.length > MAX_EVENTS) {\n events.shift();\n }\n pruneOlderThan(now - 180_000);\n}\n\n/** Clears the in-process ring buffer (Vitest only). */\nexport function resetRecentHttpAttemptsForTests(): void {\n events.length = 0;\n}\n\n/** Read-only slice for diagnostics — must not mutate `events` (rate-limit logs may run back-to-back; pruning here broke the 180s window). */\nfunction eventsInWindow(windowMs: number): RecentHttpAttempt[] {\n const now = Date.now();\n const start = now - windowMs;\n return events.filter((e) => e.t >= start);\n}\n\nfunction aggregateByOperationModel(rows: RecentHttpAttempt[]): Map<string, number> {\n const m = new Map<string, number>();\n for (const e of rows) {\n const op = e.operation?.trim() || \"?\";\n const mod = e.model?.trim() || \"?\";\n const key = `${op} · ${mod}`;\n m.set(key, (m.get(key) ?? 0) + 1);\n }\n return m;\n}\n\nfunction formatTop(m: Map<string, number>, limit: number): string {\n return [...m.entries()]\n .sort((a, b) => b[1] - a[1])\n .slice(0, limit)\n .map(([k, c]) => `${c}× ${k}`)\n .join(\"; \");\n}\n\n/**\n * Summarize recent attempts for rate-limit log lines (this Node process only).\n */\nexport function formatRecentHttpAttemptsForRateLimitLog(): string {\n const w180 = eventsInWindow(180_000);\n const w60 = eventsInWindow(60_000);\n const top60 = formatTop(aggregateByOperationModel(w60), 6);\n const top180 = formatTop(aggregateByOperationModel(w180), 6);\n const s60 = w60.length === 0 ? \"0\" : String(w60.length);\n const s180 = w180.length === 0 ? \"0\" : String(w180.length);\n return `this-process traffic: last60s=${s60} attempt(s) [${top60 || \"—\"}]; last180s=${s180} attempt(s) [${top180 || \"—\"}] (same IP / subscription as other gateway jobs will share provider limits)`;\n}\n"],"mappings":";AAWA,MAAM,aAAa;AACnB,MAAM,SAA8B,EAAE;AAEtC,SAAS,eAAe,UAAwB;CAC9C,OAAO,OAAO,SAAS,KAAK,OAAO,GAAG,IAAI,UACxC,OAAO,OAAO;;;;;AAOlB,SAAgB,0BAA0B,MAAoD;CAC5F,MAAM,MAAM,KAAK,KAAK;CACtB,OAAO,KAAK;EAAE,GAAG;EAAK,WAAW,KAAK;EAAW,OAAO,KAAK;EAAO,CAAC;CACrE,OAAO,OAAO,SAAS,YACrB,OAAO,OAAO;CAEhB,eAAe,MAAM,KAAQ;;;AAS/B,SAAS,eAAe,UAAuC;CAE7D,MAAM,QADM,KAAK,KACA,GAAG;CACpB,OAAO,OAAO,QAAQ,MAAM,EAAE,KAAK,MAAM;;AAG3C,SAAS,0BAA0B,MAAgD;CACjF,MAAM,oBAAI,IAAI,KAAqB;CACnC,KAAK,MAAM,KAAK,MAAM;EAGpB,MAAM,MAAM,GAFD,EAAE,WAAW,MAAM,IAAI,IAEhB,KADN,EAAE,OAAO,MAAM,IAAI;EAE/B,EAAE,IAAI,MAAM,EAAE,IAAI,IAAI,IAAI,KAAK,EAAE;;CAEnC,OAAO;;AAGT,SAAS,UAAU,GAAwB,OAAuB;CAChE,OAAO,CAAC,GAAG,EAAE,SAAS,CAAC,CACpB,MAAM,GAAG,MAAM,EAAE,KAAK,EAAE,GAAG,CAC3B,MAAM,GAAG,MAAM,CACf,KAAK,CAAC,GAAG,OAAO,GAAG,EAAE,IAAI,IAAI,CAC7B,KAAK,KAAK;;;;;AAMf,SAAgB,0CAAkD;CAChE,MAAM,OAAO,eAAe,KAAQ;CACpC,MAAM,MAAM,eAAe,IAAO;CAClC,MAAM,QAAQ,UAAU,0BAA0B,IAAI,EAAE,EAAE;CAC1D,MAAM,SAAS,UAAU,0BAA0B,KAAK,EAAE,EAAE;CAC5D,MAAM,MAAM,IAAI,WAAW,IAAI,MAAM,OAAO,IAAI,OAAO;CACvD,MAAM,OAAO,KAAK,WAAW,IAAI,MAAM,OAAO,KAAK,OAAO;CAC1D,OAAO,iCAAiC,IAAI,eAAe,SAAS,IAAI,cAAc,KAAK,eAAe,UAAU,IAAI"}
1
+ {"version":3,"file":"recent-http-attempts.js","names":[],"sources":["../../services/recent-http-attempts.ts"],"sourcesContent":["/**\n * In-process rolling record of outbound provider HTTP attempts (chat + embeddings).\n * Used only for diagnostics when rate limits fire — not authoritative billing data.\n */\n\nexport type RecentHttpAttempt = {\n t: number;\n operation?: string;\n model?: string;\n};\n\nconst MAX_EVENTS = 500;\nconst events: RecentHttpAttempt[] = [];\n\nfunction pruneOlderThan(cutoffMs: number): void {\n while (events.length > 0 && events[0].t < cutoffMs) {\n events.shift();\n }\n}\n\n/**\n * Record one outbound attempt before the SDK call (includes retries as separate rows).\n */\nexport function recordProviderHttpAttempt(meta: { operation?: string; model?: string }): void {\n const now = Date.now();\n events.push({ t: now, operation: meta.operation, model: meta.model });\n while (events.length > MAX_EVENTS) {\n events.shift();\n }\n pruneOlderThan(now - 180_000);\n}\n\n/** Clears the in-process ring buffer (Vitest only). */\nexport function resetRecentHttpAttemptsForTests(): void {\n events.length = 0;\n}\n\n/** Read-only slice for diagnostics — must not mutate `events` (rate-limit logs may run back-to-back; pruning here broke the 180s window). */\nfunction eventsInWindow(windowMs: number): RecentHttpAttempt[] {\n const now = Date.now();\n const start = now - windowMs;\n return events.filter((e) => e.t >= start);\n}\n\nfunction aggregateByOperationModel(rows: RecentHttpAttempt[]): Map<string, number> {\n const m = new Map<string, number>();\n for (const e of rows) {\n const op = e.operation?.trim() || \"?\";\n const mod = e.model?.trim() || \"?\";\n const key = `${op} · ${mod}`;\n m.set(key, (m.get(key) ?? 0) + 1);\n }\n return m;\n}\n\nfunction formatTop(m: Map<string, number>, limit: number): string {\n return [...m.entries()]\n .sort((a, b) => b[1] - a[1])\n .slice(0, limit)\n .map(([k, c]) => `${c}× ${k}`)\n .join(\"; \");\n}\n\n/**\n * Summarize recent attempts for rate-limit log lines (this Node process only).\n */\nexport function formatRecentHttpAttemptsForRateLimitLog(): string {\n const w180 = eventsInWindow(180_000);\n const w60 = eventsInWindow(60_000);\n const top60 = formatTop(aggregateByOperationModel(w60), 6);\n const top180 = formatTop(aggregateByOperationModel(w180), 6);\n const s60 = w60.length === 0 ? \"0\" : String(w60.length);\n const s180 = w180.length === 0 ? \"0\" : String(w180.length);\n return `this-process traffic: last60s=${s60} attempt(s) [${top60 || \"—\"}]; last180s=${s180} attempt(s) [${top180 || \"—\"}] (same IP / subscription as other gateway jobs will share provider limits)`;\n}\n"],"mappings":";AAWA,MAAM,aAAa;AACnB,MAAM,SAA8B,CAAC;AAErC,SAAS,eAAe,UAAwB;CAC9C,OAAO,OAAO,SAAS,KAAK,OAAO,GAAG,IAAI,UACxC,OAAO,MAAM;AAEjB;;;;AAKA,SAAgB,0BAA0B,MAAoD;CAC5F,MAAM,MAAM,KAAK,IAAI;CACrB,OAAO,KAAK;EAAE,GAAG;EAAK,WAAW,KAAK;EAAW,OAAO,KAAK;CAAM,CAAC;CACpE,OAAO,OAAO,SAAS,YACrB,OAAO,MAAM;CAEf,eAAe,MAAM,IAAO;AAC9B;;AAQA,SAAS,eAAe,UAAuC;CAE7D,MAAM,QADM,KAAK,IACD,IAAI;CACpB,OAAO,OAAO,QAAQ,MAAM,EAAE,KAAK,KAAK;AAC1C;AAEA,SAAS,0BAA0B,MAAgD;CACjF,MAAM,oBAAI,IAAI,IAAoB;CAClC,KAAK,MAAM,KAAK,MAAM;EAGpB,MAAM,MAAM,GAFD,EAAE,WAAW,KAAK,KAAK,IAEhB,KADN,EAAE,OAAO,KAAK,KAAK;EAE/B,EAAE,IAAI,MAAM,EAAE,IAAI,GAAG,KAAK,KAAK,CAAC;CAClC;CACA,OAAO;AACT;AAEA,SAAS,UAAU,GAAwB,OAAuB;CAChE,OAAO,CAAC,GAAG,EAAE,QAAQ,CAAC,EACnB,MAAM,GAAG,MAAM,EAAE,KAAK,EAAE,EAAE,EAC1B,MAAM,GAAG,KAAK,EACd,KAAK,CAAC,GAAG,OAAO,GAAG,EAAE,IAAI,GAAG,EAC5B,KAAK,IAAI;AACd;;;;AAKA,SAAgB,0CAAkD;CAChE,MAAM,OAAO,eAAe,IAAO;CACnC,MAAM,MAAM,eAAe,GAAM;CACjC,MAAM,QAAQ,UAAU,0BAA0B,GAAG,GAAG,CAAC;CACzD,MAAM,SAAS,UAAU,0BAA0B,IAAI,GAAG,CAAC;CAC3D,MAAM,MAAM,IAAI,WAAW,IAAI,MAAM,OAAO,IAAI,MAAM;CACtD,MAAM,OAAO,KAAK,WAAW,IAAI,MAAM,OAAO,KAAK,MAAM;CACzD,OAAO,iCAAiC,IAAI,eAAe,SAAS,IAAI,cAAc,KAAK,eAAe,UAAU,IAAI;AAC1H"}
@@ -1 +1 @@
1
- {"version":3,"file":"shared.js","names":[],"sources":["../../../services/reflection/shared.ts"],"sourcesContent":["import { REFLECTION_PATTERN_MAX_CHARS } from \"../../utils/constants.js\";\n\nexport const REFLECTION_PATTERN_MIN_CHARS = 20;\nexport const REFLECTION_RULE_MIN_CHARS = 10;\nexport const REFLECTION_RULE_MAX_CHARS = 120;\nexport const REFLECTION_META_MIN_CHARS = 20;\nexport const REFLECTION_MAX_PATTERNS_FOR_RULES = 50;\nexport const REFLECTION_MAX_PATTERNS_FOR_META = 30;\n\n/**\n * Normalize vector to unit length.\n */\nexport function normalizeVector(v: number[]): number[] {\n const norm = Math.sqrt(v.reduce((s, x) => s + x * x, 0)) || 1;\n return v.map((x) => x / norm);\n}\n\n/**\n * Compute dot product between two PRE-NORMALIZED vectors.\n * This is an optimized version that assumes both vectors are already unit-length.\n * Returns the dot product, which equals cosine similarity for normalized vectors.\n *\n * IMPORTANT: Use this ONLY when vectors are normalized via normalizeVector() first.\n * For arbitrary (non-normalized) vectors, use cosineSimilarity from ambient-retrieval.ts instead.\n */\nexport function dotProductSimilarity(a: number[], b: number[]): number {\n if (a.length !== b.length) return 0;\n let dot = 0;\n for (let i = 0; i < a.length; i++) {\n dot += a[i] * b[i];\n }\n return dot;\n}\n\n/**\n * Parse PATTERN: lines from reflection LLM response. Exported for tests.\n */\nexport function parsePatternsFromReflectionResponse(rawResponse: string): string[] {\n const patterns: string[] = [];\n for (const line of rawResponse.split(/\\n/)) {\n const m = line.match(/^\\s*PATTERN:\\s*(.+)/);\n if (!m) continue;\n const text = m[1].trim();\n if (text.length >= REFLECTION_PATTERN_MIN_CHARS && text.length <= REFLECTION_PATTERN_MAX_CHARS) {\n patterns.push(text);\n }\n }\n const seenInBatch = new Set<string>();\n const unique: string[] = [];\n for (const p of patterns) {\n const key = p.toLowerCase().replace(/\\s+/g, \" \");\n if (seenInBatch.has(key)) continue;\n seenInBatch.add(key);\n unique.push(p);\n }\n return unique;\n}\n"],"mappings":";;;AAYA,SAAgB,gBAAgB,GAAuB;CACrD,MAAM,OAAO,KAAK,KAAK,EAAE,QAAQ,GAAG,MAAM,IAAI,IAAI,GAAG,EAAE,CAAC,IAAI;CAC5D,OAAO,EAAE,KAAK,MAAM,IAAI,KAAK;;;;;;;;;;AAW/B,SAAgB,qBAAqB,GAAa,GAAqB;CACrE,IAAI,EAAE,WAAW,EAAE,QAAQ,OAAO;CAClC,IAAI,MAAM;CACV,KAAK,IAAI,IAAI,GAAG,IAAI,EAAE,QAAQ,KAC5B,OAAO,EAAE,KAAK,EAAE;CAElB,OAAO;;;;;AAMT,SAAgB,oCAAoC,aAA+B;CACjF,MAAM,WAAqB,EAAE;CAC7B,KAAK,MAAM,QAAQ,YAAY,MAAM,KAAK,EAAE;EAC1C,MAAM,IAAI,KAAK,MAAM,sBAAsB;EAC3C,IAAI,CAAC,GAAG;EACR,MAAM,OAAO,EAAE,GAAG,MAAM;EACxB,IAAI,KAAK,UAAA,MAA0C,KAAK,UAAA,KACtD,SAAS,KAAK,KAAK;;CAGvB,MAAM,8BAAc,IAAI,KAAa;CACrC,MAAM,SAAmB,EAAE;CAC3B,KAAK,MAAM,KAAK,UAAU;EACxB,MAAM,MAAM,EAAE,aAAa,CAAC,QAAQ,QAAQ,IAAI;EAChD,IAAI,YAAY,IAAI,IAAI,EAAE;EAC1B,YAAY,IAAI,IAAI;EACpB,OAAO,KAAK,EAAE;;CAEhB,OAAO"}
1
+ {"version":3,"file":"shared.js","names":[],"sources":["../../../services/reflection/shared.ts"],"sourcesContent":["import { REFLECTION_PATTERN_MAX_CHARS } from \"../../utils/constants.js\";\n\nexport const REFLECTION_PATTERN_MIN_CHARS = 20;\nexport const REFLECTION_RULE_MIN_CHARS = 10;\nexport const REFLECTION_RULE_MAX_CHARS = 120;\nexport const REFLECTION_META_MIN_CHARS = 20;\nexport const REFLECTION_MAX_PATTERNS_FOR_RULES = 50;\nexport const REFLECTION_MAX_PATTERNS_FOR_META = 30;\n\n/**\n * Normalize vector to unit length.\n */\nexport function normalizeVector(v: number[]): number[] {\n const norm = Math.sqrt(v.reduce((s, x) => s + x * x, 0)) || 1;\n return v.map((x) => x / norm);\n}\n\n/**\n * Compute dot product between two PRE-NORMALIZED vectors.\n * This is an optimized version that assumes both vectors are already unit-length.\n * Returns the dot product, which equals cosine similarity for normalized vectors.\n *\n * IMPORTANT: Use this ONLY when vectors are normalized via normalizeVector() first.\n * For arbitrary (non-normalized) vectors, use cosineSimilarity from ambient-retrieval.ts instead.\n */\nexport function dotProductSimilarity(a: number[], b: number[]): number {\n if (a.length !== b.length) return 0;\n let dot = 0;\n for (let i = 0; i < a.length; i++) {\n dot += a[i] * b[i];\n }\n return dot;\n}\n\n/**\n * Parse PATTERN: lines from reflection LLM response. Exported for tests.\n */\nexport function parsePatternsFromReflectionResponse(rawResponse: string): string[] {\n const patterns: string[] = [];\n for (const line of rawResponse.split(/\\n/)) {\n const m = line.match(/^\\s*PATTERN:\\s*(.+)/);\n if (!m) continue;\n const text = m[1].trim();\n if (text.length >= REFLECTION_PATTERN_MIN_CHARS && text.length <= REFLECTION_PATTERN_MAX_CHARS) {\n patterns.push(text);\n }\n }\n const seenInBatch = new Set<string>();\n const unique: string[] = [];\n for (const p of patterns) {\n const key = p.toLowerCase().replace(/\\s+/g, \" \");\n if (seenInBatch.has(key)) continue;\n seenInBatch.add(key);\n unique.push(p);\n }\n return unique;\n}\n"],"mappings":";;;AAYA,SAAgB,gBAAgB,GAAuB;CACrD,MAAM,OAAO,KAAK,KAAK,EAAE,QAAQ,GAAG,MAAM,IAAI,IAAI,GAAG,CAAC,CAAC,KAAK;CAC5D,OAAO,EAAE,KAAK,MAAM,IAAI,IAAI;AAC9B;;;;;;;;;AAUA,SAAgB,qBAAqB,GAAa,GAAqB;CACrE,IAAI,EAAE,WAAW,EAAE,QAAQ,OAAO;CAClC,IAAI,MAAM;CACV,KAAK,IAAI,IAAI,GAAG,IAAI,EAAE,QAAQ,KAC5B,OAAO,EAAE,KAAK,EAAE;CAElB,OAAO;AACT;;;;AAKA,SAAgB,oCAAoC,aAA+B;CACjF,MAAM,WAAqB,CAAC;CAC5B,KAAK,MAAM,QAAQ,YAAY,MAAM,IAAI,GAAG;EAC1C,MAAM,IAAI,KAAK,MAAM,qBAAqB;EAC1C,IAAI,CAAC,GAAG;EACR,MAAM,OAAO,EAAE,GAAG,KAAK;EACvB,IAAI,KAAK,UAAA,MAA0C,KAAK,UAAA,KACtD,SAAS,KAAK,IAAI;CAEtB;CACA,MAAM,8BAAc,IAAI,IAAY;CACpC,MAAM,SAAmB,CAAC;CAC1B,KAAK,MAAM,KAAK,UAAU;EACxB,MAAM,MAAM,EAAE,YAAY,EAAE,QAAQ,QAAQ,GAAG;EAC/C,IAAI,YAAY,IAAI,GAAG,GAAG;EAC1B,YAAY,IAAI,GAAG;EACnB,OAAO,KAAK,CAAC;CACf;CACA,OAAO;AACT"}