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":"plugin-root.js","names":[],"sources":["../../utils/plugin-root.ts"],"sourcesContent":["/**\n * Plugin-root path resolution (issue #1174).\n *\n * Walks up from a given module URL until it finds the directory that contains\n * `openclaw.plugin.json` — the unambiguous marker for the plugin's package\n * root. This works regardless of whether the runtime entry is the TypeScript\n * source (`extensions/memory-hybrid/index.ts`), the compiled multi-file dist\n * (`extensions/memory-hybrid/dist/index.js`), or any nested submodule\n * (`dist/cli/cmd-install.js`).\n *\n * Replaces the brittle pattern of `join(dirname(fileURLToPath(import.meta.url)), \"..\")`,\n * which silently resolved to `dist/` instead of the plugin root once the\n * package was bundled.\n */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\n/** Marker file that uniquely identifies the plugin's package root. */\nconst PLUGIN_MANIFEST_FILENAME = \"openclaw.plugin.json\";\n\n/** Cache: metaUrl -> plugin root absolute path. */\nconst rootCache = new Map<string, string>();\n\n/**\n * Resolve the plugin's package root directory by walking up parents from the\n * given `import.meta.url` until a directory containing `openclaw.plugin.json`\n * is found.\n *\n * @throws Error if no manifest is found between the start directory and the filesystem root.\n */\nexport function findPluginRoot(metaUrl: string): string {\n const cached = rootCache.get(metaUrl);\n if (cached !== undefined) return cached;\n\n const startFile = fileURLToPath(metaUrl);\n let current = dirname(startFile);\n // Hard upper bound to avoid pathological loops on malformed URLs.\n for (let i = 0; i < 64; i++) {\n if (existsSync(join(current, PLUGIN_MANIFEST_FILENAME))) {\n rootCache.set(metaUrl, current);\n return current;\n }\n const parent = dirname(current);\n if (parent === current) break;\n current = parent;\n }\n\n throw new Error(\n `findPluginRoot: could not locate ${PLUGIN_MANIFEST_FILENAME} above ${startFile}. The plugin must be installed with its manifest at the package root.`,\n );\n}\n\n/** Shape of `package.json` fields we read at runtime. */\nexport interface PluginPackageJson {\n name: string;\n version: string;\n [key: string]: unknown;\n}\n\n/**\n * Read and parse the plugin's `package.json` from the package root resolved\n * via {@link findPluginRoot}.\n *\n * @throws Error if the manifest cannot be located or `package.json` is malformed.\n */\nexport function readPluginPackageJson(metaUrl: string): PluginPackageJson {\n const root = findPluginRoot(metaUrl);\n const pkgPath = join(root, \"package.json\");\n const raw = readFileSync(pkgPath, \"utf-8\");\n const parsed = JSON.parse(raw) as PluginPackageJson;\n if (typeof parsed.version !== \"string\" || parsed.version.length === 0) {\n throw new Error(`readPluginPackageJson: missing version in ${pkgPath}`);\n }\n return parsed;\n}\n\n/** Test-only: clear the resolver cache (used by unit tests). */\nexport function _clearPluginRootCache(): void {\n rootCache.clear();\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAoBA,MAAM,2BAA2B;;AAGjC,MAAM,4BAAY,IAAI,KAAqB;;;;;;;;AAS3C,SAAgB,eAAe,SAAyB;CACtD,MAAM,SAAS,UAAU,IAAI,QAAQ;CACrC,IAAI,WAAW,KAAA,GAAW,OAAO;CAEjC,MAAM,YAAY,cAAc,QAAQ;CACxC,IAAI,UAAU,QAAQ,UAAU;CAEhC,KAAK,IAAI,IAAI,GAAG,IAAI,IAAI,KAAK;EAC3B,IAAI,WAAW,KAAK,SAAS,yBAAyB,CAAC,EAAE;GACvD,UAAU,IAAI,SAAS,QAAQ;GAC/B,OAAO;;EAET,MAAM,SAAS,QAAQ,QAAQ;EAC/B,IAAI,WAAW,SAAS;EACxB,UAAU;;CAGZ,MAAM,IAAI,MACR,oCAAoC,yBAAyB,SAAS,UAAU,uEACjF;;;;;;;;AAgBH,SAAgB,sBAAsB,SAAoC;CAExE,MAAM,UAAU,KADH,eAAe,QACH,EAAE,eAAe;CAC1C,MAAM,MAAM,aAAa,SAAS,QAAQ;CAC1C,MAAM,SAAS,KAAK,MAAM,IAAI;CAC9B,IAAI,OAAO,OAAO,YAAY,YAAY,OAAO,QAAQ,WAAW,GAClE,MAAM,IAAI,MAAM,6CAA6C,UAAU;CAEzE,OAAO"}
1
+ {"version":3,"file":"plugin-root.js","names":[],"sources":["../../utils/plugin-root.ts"],"sourcesContent":["/**\n * Plugin-root path resolution (issue #1174).\n *\n * Walks up from a given module URL until it finds the directory that contains\n * `openclaw.plugin.json` — the unambiguous marker for the plugin's package\n * root. This works regardless of whether the runtime entry is the TypeScript\n * source (`extensions/memory-hybrid/index.ts`), the compiled multi-file dist\n * (`extensions/memory-hybrid/dist/index.js`), or any nested submodule\n * (`dist/cli/cmd-install.js`).\n *\n * Replaces the brittle pattern of `join(dirname(fileURLToPath(import.meta.url)), \"..\")`,\n * which silently resolved to `dist/` instead of the plugin root once the\n * package was bundled.\n */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\n/** Marker file that uniquely identifies the plugin's package root. */\nconst PLUGIN_MANIFEST_FILENAME = \"openclaw.plugin.json\";\n\n/** Cache: metaUrl -> plugin root absolute path. */\nconst rootCache = new Map<string, string>();\n\n/**\n * Resolve the plugin's package root directory by walking up parents from the\n * given `import.meta.url` until a directory containing `openclaw.plugin.json`\n * is found.\n *\n * @throws Error if no manifest is found between the start directory and the filesystem root.\n */\nexport function findPluginRoot(metaUrl: string): string {\n const cached = rootCache.get(metaUrl);\n if (cached !== undefined) return cached;\n\n const startFile = fileURLToPath(metaUrl);\n let current = dirname(startFile);\n // Hard upper bound to avoid pathological loops on malformed URLs.\n for (let i = 0; i < 64; i++) {\n if (existsSync(join(current, PLUGIN_MANIFEST_FILENAME))) {\n rootCache.set(metaUrl, current);\n return current;\n }\n const parent = dirname(current);\n if (parent === current) break;\n current = parent;\n }\n\n throw new Error(\n `findPluginRoot: could not locate ${PLUGIN_MANIFEST_FILENAME} above ${startFile}. The plugin must be installed with its manifest at the package root.`,\n );\n}\n\n/** Shape of `package.json` fields we read at runtime. */\nexport interface PluginPackageJson {\n name: string;\n version: string;\n [key: string]: unknown;\n}\n\n/**\n * Read and parse the plugin's `package.json` from the package root resolved\n * via {@link findPluginRoot}.\n *\n * @throws Error if the manifest cannot be located or `package.json` is malformed.\n */\nexport function readPluginPackageJson(metaUrl: string): PluginPackageJson {\n const root = findPluginRoot(metaUrl);\n const pkgPath = join(root, \"package.json\");\n const raw = readFileSync(pkgPath, \"utf-8\");\n const parsed = JSON.parse(raw) as PluginPackageJson;\n if (typeof parsed.version !== \"string\" || parsed.version.length === 0) {\n throw new Error(`readPluginPackageJson: missing version in ${pkgPath}`);\n }\n return parsed;\n}\n\n/** Test-only: clear the resolver cache (used by unit tests). */\nexport function _clearPluginRootCache(): void {\n rootCache.clear();\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAoBA,MAAM,2BAA2B;;AAGjC,MAAM,4BAAY,IAAI,IAAoB;;;;;;;;AAS1C,SAAgB,eAAe,SAAyB;CACtD,MAAM,SAAS,UAAU,IAAI,OAAO;CACpC,IAAI,WAAW,KAAA,GAAW,OAAO;CAEjC,MAAM,YAAY,cAAc,OAAO;CACvC,IAAI,UAAU,QAAQ,SAAS;CAE/B,KAAK,IAAI,IAAI,GAAG,IAAI,IAAI,KAAK;EAC3B,IAAI,WAAW,KAAK,SAAS,wBAAwB,CAAC,GAAG;GACvD,UAAU,IAAI,SAAS,OAAO;GAC9B,OAAO;EACT;EACA,MAAM,SAAS,QAAQ,OAAO;EAC9B,IAAI,WAAW,SAAS;EACxB,UAAU;CACZ;CAEA,MAAM,IAAI,MACR,oCAAoC,yBAAyB,SAAS,UAAU,sEAClF;AACF;;;;;;;AAeA,SAAgB,sBAAsB,SAAoC;CAExE,MAAM,UAAU,KADH,eAAe,OACJ,GAAG,cAAc;CACzC,MAAM,MAAM,aAAa,SAAS,OAAO;CACzC,MAAM,SAAS,KAAK,MAAM,GAAG;CAC7B,IAAI,OAAO,OAAO,YAAY,YAAY,OAAO,QAAQ,WAAW,GAClE,MAAM,IAAI,MAAM,6CAA6C,SAAS;CAExE,OAAO;AACT"}
@@ -1 +1 @@
1
- {"version":3,"file":"plugin-update-check.js","names":[],"sources":["../../utils/plugin-update-check.ts"],"sourcesContent":["import { mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport type { UpdateNudgeConfig } from \"../config.js\";\nimport { compareVersions } from \"./version-check.js\";\n\nconst DEFAULT_TIMEOUT_MS = 3000;\nconst NPM_LATEST_URL = \"https://registry.npmjs.org/openclaw-hybrid-memory/latest\";\nconst GITHUB_LATEST_URL = \"https://api.github.com/repos/markus-lassfolk/openclaw-hybrid-memory/releases/latest\";\n\nexport interface LatestPublishedVersion {\n latestVersion: string | null;\n source: \"npm\" | \"github\" | null;\n}\n\nexport interface VersionCheckCacheEntry {\n latestVersion: string;\n source: \"npm\" | \"github\";\n checkedAt: string;\n lastNudgedAt?: string;\n}\n\nexport interface UpdateNudgeLogger {\n info?: (msg: string) => void;\n warn?: (msg: string) => void;\n debug?: (msg: string) => void;\n}\n\nasync function fetchWithTimeout(\n url: string,\n expectedHostname: string,\n timeoutMs: number,\n fetchImpl: typeof fetch,\n): Promise<Response> {\n const parsed = new URL(url);\n if (parsed.hostname !== expectedHostname) {\n throw new Error(`Unexpected hostname: ${parsed.hostname}`);\n }\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), timeoutMs);\n try {\n return await fetchImpl(url, { signal: controller.signal });\n } finally {\n clearTimeout(timeout);\n }\n}\n\nfunction asVersionString(value: unknown): string | null {\n return typeof value === \"string\" && /^v?\\d+\\.\\d+\\.\\d+/.test(value.trim()) ? value.trim().replace(/^v/, \"\") : null;\n}\n\nexport async function fetchLatestPublishedVersion(\n fetchImpl: typeof fetch = fetch,\n timeoutMs = DEFAULT_TIMEOUT_MS,\n): Promise<LatestPublishedVersion> {\n const candidates: Array<{ version: string; source: \"npm\" | \"github\" }> = [];\n\n try {\n const response = await fetchWithTimeout(NPM_LATEST_URL, \"registry.npmjs.org\", timeoutMs, fetchImpl);\n if (response.ok) {\n const payload = (await response.json()) as { version?: unknown };\n const version = asVersionString(payload.version);\n if (version) candidates.push({ version, source: \"npm\" });\n }\n } catch {\n /* non-fatal */\n }\n\n try {\n const response = await fetchWithTimeout(GITHUB_LATEST_URL, \"api.github.com\", timeoutMs, fetchImpl);\n if (response.ok) {\n const payload = (await response.json()) as { tag_name?: unknown };\n const version = asVersionString(payload.tag_name);\n if (version) candidates.push({ version, source: \"github\" });\n }\n } catch {\n /* non-fatal */\n }\n\n const newest = candidates.reduce<{ version: string; source: \"npm\" | \"github\" } | null>((latest, candidate) => {\n if (!latest) return candidate;\n return compareVersions(candidate.version, latest.version) > 0 ? candidate : latest;\n }, null);\n\n return {\n latestVersion: newest?.version ?? null,\n source: newest?.source ?? null,\n };\n}\n\nexport function isPluginOutdated(currentVersion: string, latestVersion: string | null): boolean {\n return latestVersion != null && compareVersions(currentVersion, latestVersion) < 0;\n}\n\nexport function readVersionCheckCache(cacheFilePath: string): VersionCheckCacheEntry | null {\n try {\n const raw = JSON.parse(readFileSync(cacheFilePath, \"utf-8\")) as Partial<VersionCheckCacheEntry>;\n if (\n typeof raw.latestVersion !== \"string\" ||\n (raw.source !== \"npm\" && raw.source !== \"github\") ||\n typeof raw.checkedAt !== \"string\"\n ) {\n return null;\n }\n return {\n latestVersion: raw.latestVersion,\n source: raw.source,\n checkedAt: raw.checkedAt,\n lastNudgedAt: typeof raw.lastNudgedAt === \"string\" ? raw.lastNudgedAt : undefined,\n };\n } catch {\n return null;\n }\n}\n\nexport function writeVersionCheckCache(cacheFilePath: string, entry: VersionCheckCacheEntry): void {\n mkdirSync(dirname(cacheFilePath), { recursive: true });\n writeFileSync(cacheFilePath, JSON.stringify(entry, null, 2), \"utf-8\");\n}\n\nexport function isVersionCheckCacheFresh(\n entry: VersionCheckCacheEntry,\n cacheTtlHours: number,\n nowMs = Date.now(),\n): boolean {\n const checkedAtMs = Date.parse(entry.checkedAt);\n if (!Number.isFinite(checkedAtMs)) return false;\n return nowMs - checkedAtMs <= cacheTtlHours * 3600_000;\n}\n\nexport function shouldEmitUpdateNudge(\n entry: VersionCheckCacheEntry,\n updateNudge: UpdateNudgeConfig,\n nowMs = Date.now(),\n): boolean {\n if (!updateNudge.enabled) return false;\n if (updateNudge.intervalHours === 0) return false;\n if (!entry.lastNudgedAt) return true;\n const lastNudgedMs = Date.parse(entry.lastNudgedAt);\n if (!Number.isFinite(lastNudgedMs)) return true;\n return nowMs - lastNudgedMs >= updateNudge.intervalHours * 3600_000;\n}\n\nexport function markUpdateNudged(\n entry: VersionCheckCacheEntry,\n nowIso = new Date().toISOString(),\n): VersionCheckCacheEntry {\n return { ...entry, lastNudgedAt: nowIso };\n}\n\nexport function maybeLogOutdatedVersionNudge(\n currentVersion: string,\n entry: VersionCheckCacheEntry,\n updateNudge: UpdateNudgeConfig,\n logger: UpdateNudgeLogger,\n): VersionCheckCacheEntry {\n if (!isPluginOutdated(currentVersion, entry.latestVersion)) return entry;\n if (!updateNudge.enabled) {\n logger.info?.(\n `memory-hybrid: telemetry muted for outdated plugin v${currentVersion} (latest published: v${entry.latestVersion}).`,\n );\n return entry;\n }\n if (!shouldEmitUpdateNudge(entry, updateNudge)) {\n logger.info?.(\n `memory-hybrid: telemetry muted for outdated plugin v${currentVersion} (latest published: v${entry.latestVersion}).`,\n );\n return entry;\n }\n\n logger.warn?.(\n `memory-hybrid: update available — installed v${currentVersion}, latest published is v${entry.latestVersion}. Telemetry is muted on outdated clients to keep GlitchTip clean. Upgrade with: openclaw hybrid-mem upgrade`,\n );\n return markUpdateNudged(entry);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"plugin-update-check.js","names":[],"sources":["../../utils/plugin-update-check.ts"],"sourcesContent":["import { mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport type { UpdateNudgeConfig } from \"../config.js\";\nimport { compareVersions } from \"./version-check.js\";\n\nconst DEFAULT_TIMEOUT_MS = 3000;\nconst NPM_LATEST_URL = \"https://registry.npmjs.org/openclaw-hybrid-memory/latest\";\nconst GITHUB_LATEST_URL = \"https://api.github.com/repos/markus-lassfolk/openclaw-hybrid-memory/releases/latest\";\n\nexport interface LatestPublishedVersion {\n latestVersion: string | null;\n source: \"npm\" | \"github\" | null;\n}\n\nexport interface VersionCheckCacheEntry {\n latestVersion: string;\n source: \"npm\" | \"github\";\n checkedAt: string;\n lastNudgedAt?: string;\n}\n\nexport interface UpdateNudgeLogger {\n info?: (msg: string) => void;\n warn?: (msg: string) => void;\n debug?: (msg: string) => void;\n}\n\nasync function fetchWithTimeout(\n url: string,\n expectedHostname: string,\n timeoutMs: number,\n fetchImpl: typeof fetch,\n): Promise<Response> {\n const parsed = new URL(url);\n if (parsed.hostname !== expectedHostname) {\n throw new Error(`Unexpected hostname: ${parsed.hostname}`);\n }\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), timeoutMs);\n try {\n return await fetchImpl(url, { signal: controller.signal });\n } finally {\n clearTimeout(timeout);\n }\n}\n\nfunction asVersionString(value: unknown): string | null {\n return typeof value === \"string\" && /^v?\\d+\\.\\d+\\.\\d+/.test(value.trim()) ? value.trim().replace(/^v/, \"\") : null;\n}\n\nexport async function fetchLatestPublishedVersion(\n fetchImpl: typeof fetch = fetch,\n timeoutMs = DEFAULT_TIMEOUT_MS,\n): Promise<LatestPublishedVersion> {\n const candidates: Array<{ version: string; source: \"npm\" | \"github\" }> = [];\n\n try {\n const response = await fetchWithTimeout(NPM_LATEST_URL, \"registry.npmjs.org\", timeoutMs, fetchImpl);\n if (response.ok) {\n const payload = (await response.json()) as { version?: unknown };\n const version = asVersionString(payload.version);\n if (version) candidates.push({ version, source: \"npm\" });\n }\n } catch {\n /* non-fatal */\n }\n\n try {\n const response = await fetchWithTimeout(GITHUB_LATEST_URL, \"api.github.com\", timeoutMs, fetchImpl);\n if (response.ok) {\n const payload = (await response.json()) as { tag_name?: unknown };\n const version = asVersionString(payload.tag_name);\n if (version) candidates.push({ version, source: \"github\" });\n }\n } catch {\n /* non-fatal */\n }\n\n const newest = candidates.reduce<{ version: string; source: \"npm\" | \"github\" } | null>((latest, candidate) => {\n if (!latest) return candidate;\n return compareVersions(candidate.version, latest.version) > 0 ? candidate : latest;\n }, null);\n\n return {\n latestVersion: newest?.version ?? null,\n source: newest?.source ?? null,\n };\n}\n\nexport function isPluginOutdated(currentVersion: string, latestVersion: string | null): boolean {\n return latestVersion != null && compareVersions(currentVersion, latestVersion) < 0;\n}\n\nexport function readVersionCheckCache(cacheFilePath: string): VersionCheckCacheEntry | null {\n try {\n const raw = JSON.parse(readFileSync(cacheFilePath, \"utf-8\")) as Partial<VersionCheckCacheEntry>;\n if (\n typeof raw.latestVersion !== \"string\" ||\n (raw.source !== \"npm\" && raw.source !== \"github\") ||\n typeof raw.checkedAt !== \"string\"\n ) {\n return null;\n }\n return {\n latestVersion: raw.latestVersion,\n source: raw.source,\n checkedAt: raw.checkedAt,\n lastNudgedAt: typeof raw.lastNudgedAt === \"string\" ? raw.lastNudgedAt : undefined,\n };\n } catch {\n return null;\n }\n}\n\nexport function writeVersionCheckCache(cacheFilePath: string, entry: VersionCheckCacheEntry): void {\n mkdirSync(dirname(cacheFilePath), { recursive: true });\n writeFileSync(cacheFilePath, JSON.stringify(entry, null, 2), \"utf-8\");\n}\n\nexport function isVersionCheckCacheFresh(\n entry: VersionCheckCacheEntry,\n cacheTtlHours: number,\n nowMs = Date.now(),\n): boolean {\n const checkedAtMs = Date.parse(entry.checkedAt);\n if (!Number.isFinite(checkedAtMs)) return false;\n return nowMs - checkedAtMs <= cacheTtlHours * 3600_000;\n}\n\nexport function shouldEmitUpdateNudge(\n entry: VersionCheckCacheEntry,\n updateNudge: UpdateNudgeConfig,\n nowMs = Date.now(),\n): boolean {\n if (!updateNudge.enabled) return false;\n if (updateNudge.intervalHours === 0) return false;\n if (!entry.lastNudgedAt) return true;\n const lastNudgedMs = Date.parse(entry.lastNudgedAt);\n if (!Number.isFinite(lastNudgedMs)) return true;\n return nowMs - lastNudgedMs >= updateNudge.intervalHours * 3600_000;\n}\n\nexport function markUpdateNudged(\n entry: VersionCheckCacheEntry,\n nowIso = new Date().toISOString(),\n): VersionCheckCacheEntry {\n return { ...entry, lastNudgedAt: nowIso };\n}\n\nexport function maybeLogOutdatedVersionNudge(\n currentVersion: string,\n entry: VersionCheckCacheEntry,\n updateNudge: UpdateNudgeConfig,\n logger: UpdateNudgeLogger,\n): VersionCheckCacheEntry {\n if (!isPluginOutdated(currentVersion, entry.latestVersion)) return entry;\n if (!updateNudge.enabled) {\n logger.info?.(\n `memory-hybrid: telemetry muted for outdated plugin v${currentVersion} (latest published: v${entry.latestVersion}).`,\n );\n return entry;\n }\n if (!shouldEmitUpdateNudge(entry, updateNudge)) {\n logger.info?.(\n `memory-hybrid: telemetry muted for outdated plugin v${currentVersion} (latest published: v${entry.latestVersion}).`,\n );\n return entry;\n }\n\n logger.warn?.(\n `memory-hybrid: update available — installed v${currentVersion}, latest published is v${entry.latestVersion}. Telemetry is muted on outdated clients to keep GlitchTip clean. Upgrade with: openclaw hybrid-mem upgrade`,\n );\n return markUpdateNudged(entry);\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"procedure-risk.js","names":[],"sources":["../../utils/procedure-risk.ts"],"sourcesContent":["import type { ProcedureEntry } from \"../types/memory.js\";\n\nexport type ProcedureRiskLevel = \"low\" | \"medium\" | \"high\";\n\nconst HIGH_RISK_PATTERN =\n /(?:\\brm\\s+-[rf]+\\b|\\bdd\\s+if=|\\bmkfs\\b|\\bshred\\b|\\bdrop\\s+table\\b|\\btruncate\\s+table\\b)|(?:\\bprivate[_-]?key\\b|\\b(?:token|password)\\b)\\s*[:=]/i;\n\nconst MEDIUM_RISK_PATTERN =\n /\\b(systemctl|service)\\s+(?:start|stop|restart|reload|enable|disable)\\b|\\b(npm|pnpm|yarn|pip|pip3|apt|apt-get|brew|cargo|gem)\\s+(install|add|remove|uninstall|upgrade)\\b|\\bssh\\s+[^\\n]+@|\\bscp\\s+|\\brsync\\s+[^\\n]+:|\\b(curl|wget|fetch)\\b[^\\n]*(?:-X\\s*(?:POST|PUT|PATCH|DELETE)|--request\\s*(?:POST|PUT|PATCH|DELETE))/i;\n\nexport function parseRecipeOrRaw(recipeJson: string): unknown {\n try {\n return JSON.parse(recipeJson) as unknown;\n } catch {\n return { malformed: true, raw: recipeJson };\n }\n}\n\n/** Heuristic risk tier for procedure text + recipe (used for promotion gates, scoring, and lifecycle demotion). */\nexport function determineRiskLevel(proc: ProcedureEntry, recipe: unknown): ProcedureRiskLevel {\n const combined = `${proc.taskPattern}\\n${JSON.stringify(recipe)}`;\n if (HIGH_RISK_PATTERN.test(combined)) return \"high\";\n if (MEDIUM_RISK_PATTERN.test(combined)) return \"medium\";\n return \"low\";\n}\n"],"mappings":";AAIA,MAAM,oBACJ;AAEF,MAAM,sBACJ;AAEF,SAAgB,iBAAiB,YAA6B;CAC5D,IAAI;EACF,OAAO,KAAK,MAAM,WAAW;SACvB;EACN,OAAO;GAAE,WAAW;GAAM,KAAK;GAAY;;;;AAK/C,SAAgB,mBAAmB,MAAsB,QAAqC;CAC5F,MAAM,WAAW,GAAG,KAAK,YAAY,IAAI,KAAK,UAAU,OAAO;CAC/D,IAAI,kBAAkB,KAAK,SAAS,EAAE,OAAO;CAC7C,IAAI,oBAAoB,KAAK,SAAS,EAAE,OAAO;CAC/C,OAAO"}
1
+ {"version":3,"file":"procedure-risk.js","names":[],"sources":["../../utils/procedure-risk.ts"],"sourcesContent":["import type { ProcedureEntry } from \"../types/memory.js\";\n\nexport type ProcedureRiskLevel = \"low\" | \"medium\" | \"high\";\n\nconst HIGH_RISK_PATTERN =\n /(?:\\brm\\s+-[rf]+\\b|\\bdd\\s+if=|\\bmkfs\\b|\\bshred\\b|\\bdrop\\s+table\\b|\\btruncate\\s+table\\b)|(?:\\bprivate[_-]?key\\b|\\b(?:token|password)\\b)\\s*[:=]/i;\n\nconst MEDIUM_RISK_PATTERN =\n /\\b(systemctl|service)\\s+(?:start|stop|restart|reload|enable|disable)\\b|\\b(npm|pnpm|yarn|pip|pip3|apt|apt-get|brew|cargo|gem)\\s+(install|add|remove|uninstall|upgrade)\\b|\\bssh\\s+[^\\n]+@|\\bscp\\s+|\\brsync\\s+[^\\n]+:|\\b(curl|wget|fetch)\\b[^\\n]*(?:-X\\s*(?:POST|PUT|PATCH|DELETE)|--request\\s*(?:POST|PUT|PATCH|DELETE))/i;\n\nexport function parseRecipeOrRaw(recipeJson: string): unknown {\n try {\n return JSON.parse(recipeJson) as unknown;\n } catch {\n return { malformed: true, raw: recipeJson };\n }\n}\n\n/** Heuristic risk tier for procedure text + recipe (used for promotion gates, scoring, and lifecycle demotion). */\nexport function determineRiskLevel(proc: ProcedureEntry, recipe: unknown): ProcedureRiskLevel {\n const combined = `${proc.taskPattern}\\n${JSON.stringify(recipe)}`;\n if (HIGH_RISK_PATTERN.test(combined)) return \"high\";\n if (MEDIUM_RISK_PATTERN.test(combined)) return \"medium\";\n return \"low\";\n}\n"],"mappings":";AAIA,MAAM,oBACJ;AAEF,MAAM,sBACJ;AAEF,SAAgB,iBAAiB,YAA6B;CAC5D,IAAI;EACF,OAAO,KAAK,MAAM,UAAU;CAC9B,QAAQ;EACN,OAAO;GAAE,WAAW;GAAM,KAAK;EAAW;CAC5C;AACF;;AAGA,SAAgB,mBAAmB,MAAsB,QAAqC;CAC5F,MAAM,WAAW,GAAG,KAAK,YAAY,IAAI,KAAK,UAAU,MAAM;CAC9D,IAAI,kBAAkB,KAAK,QAAQ,GAAG,OAAO;CAC7C,IAAI,oBAAoB,KAAK,QAAQ,GAAG,OAAO;CAC/C,OAAO;AACT"}
@@ -1 +1 @@
1
- {"version":3,"file":"progress-indicators.js","names":[],"sources":["../../utils/progress-indicators.ts"],"sourcesContent":["/**\n * Progress indicators, spinners, and status feedback for CLI operations\n */\n\nconst SPINNER_FRAMES = [\"⠋\", \"⠙\", \"⠹\", \"⠸\", \"⠼\", \"⠴\", \"⠦\", \"⠧\", \"⠇\", \"⠏\"];\n\nexport class ProgressSpinner {\n private interval: NodeJS.Timeout | null = null;\n private frameIndex = 0;\n private message: string;\n private startTime: number;\n\n constructor(message: string) {\n this.message = message;\n this.startTime = Date.now();\n }\n\n start(): void {\n if (this.interval) return;\n\n // Only show spinner in TTY mode\n if (!process.stdout.isTTY) {\n return;\n }\n\n this.interval = setInterval(() => {\n const frame = SPINNER_FRAMES[this.frameIndex];\n const elapsed = Math.floor((Date.now() - this.startTime) / 1000);\n const elapsedStr = elapsed > 0 ? ` (${elapsed}s)` : \"\";\n process.stdout.write(`\\r${frame} ${this.message}${elapsedStr}`);\n this.frameIndex = (this.frameIndex + 1) % SPINNER_FRAMES.length;\n }, 80);\n }\n\n update(message: string): void {\n this.message = message;\n }\n\n success(message?: string): void {\n this.stop();\n const elapsed = Math.floor((Date.now() - this.startTime) / 1000);\n const elapsedStr = elapsed > 0 ? ` in ${elapsed}s` : \"\";\n process.stdout.write(`✓ ${message ?? this.message}${elapsedStr}\\n`);\n }\n\n fail(message?: string): void {\n this.stop();\n process.stdout.write(`✗ ${message ?? this.message}\\n`);\n }\n\n stop(): void {\n if (this.interval) {\n clearInterval(this.interval);\n this.interval = null;\n }\n if (process.stdout.isTTY) {\n process.stdout.write(\"\\r\\x1b[K\"); // Clear line\n }\n }\n}\n\nexport class ProgressBar {\n private current = 0;\n private message: string;\n private total: number;\n private startTime: number;\n private width = 40;\n private lastLoggedPercent = -1;\n\n constructor(message: string, total: number) {\n this.message = message;\n this.total = total;\n this.startTime = Date.now();\n }\n\n update(current: number, status?: string): void {\n const safeTotal = Math.max(1, this.total);\n const percent = Math.max(0, Math.min(100, Math.floor((current / safeTotal) * 100)));\n\n if (!process.stdout.isTTY) {\n const milestone = Math.floor(percent / 10) * 10;\n if (milestone > this.lastLoggedPercent) {\n this.lastLoggedPercent = milestone;\n const statusStr = status ? ` - ${status}` : \"\";\n process.stdout.write(`${this.message}: ${milestone}% (${current}/${this.total})${statusStr}\\n`);\n }\n return;\n }\n\n const filled = Math.floor((current / safeTotal) * this.width);\n const empty = this.width - filled;\n const bar = \"█\".repeat(filled) + \"░\".repeat(empty);\n\n // Calculate ETA\n const elapsed = Date.now() - this.startTime;\n const rate = current / (elapsed / 1000);\n const remaining = Math.max(0, this.total - current);\n const eta = remaining / rate;\n const etaStr = eta > 0 && Number.isFinite(eta) ? ` ETA ${formatDuration(eta)}` : \"\";\n\n const statusStr = status ? ` - ${status}` : \"\";\n process.stdout.write(`\\r${this.message}: [${bar}] ${percent}% (${current}/${this.total})${etaStr}${statusStr}`);\n }\n\n complete(message?: string): void {\n if (process.stdout.isTTY) {\n process.stdout.write(\"\\r\\x1b[K\"); // Clear line\n }\n const elapsed = Math.floor((Date.now() - this.startTime) / 1000);\n process.stdout.write(`✓ ${message || this.message} - ${this.total} items in ${elapsed}s\\n`);\n }\n}\n\nfunction formatDuration(seconds: number): string {\n if (seconds < 60) {\n return `${Math.floor(seconds)}s`;\n }\n const mins = Math.floor(seconds / 60);\n const secs = Math.floor(seconds % 60);\n return `${mins}m ${secs}s`;\n}\n\n/**\n * Simple status message with icon\n */\nexport function statusMessage(type: \"info\" | \"success\" | \"warning\" | \"error\", message: string): void {\n const icons = {\n info: \"ℹ️\",\n success: \"✓\",\n warning: \"⚠️\",\n error: \"✗\",\n };\n process.stdout.write(`${icons[type]} ${message}\\n`);\n}\n\n/**\n * Show a completion summary with stats\n */\nexport function showCompletionSummary(\n operation: string,\n stats: Record<string, number | string>,\n durationMs: number,\n): void {\n const durationStr = formatDuration(durationMs / 1000);\n process.stdout.write(`✓ ${operation} complete in ${durationStr}\\n`);\n\n for (const [key, value] of Object.entries(stats)) {\n process.stdout.write(` ${key}: ${value}\\n`);\n }\n}\n"],"mappings":";;;;AAIA,MAAM,iBAAiB;CAAC;CAAK;CAAK;CAAK;CAAK;CAAK;CAAK;CAAK;CAAK;CAAK;CAAI;AAEzE,IAAa,kBAAb,MAA6B;CAC3B,WAA0C;CAC1C,aAAqB;CACrB;CACA;CAEA,YAAY,SAAiB;EAC3B,KAAK,UAAU;EACf,KAAK,YAAY,KAAK,KAAK;;CAG7B,QAAc;EACZ,IAAI,KAAK,UAAU;EAGnB,IAAI,CAAC,QAAQ,OAAO,OAClB;EAGF,KAAK,WAAW,kBAAkB;GAChC,MAAM,QAAQ,eAAe,KAAK;GAClC,MAAM,UAAU,KAAK,OAAO,KAAK,KAAK,GAAG,KAAK,aAAa,IAAK;GAChE,MAAM,aAAa,UAAU,IAAI,KAAK,QAAQ,MAAM;GACpD,QAAQ,OAAO,MAAM,KAAK,MAAM,GAAG,KAAK,UAAU,aAAa;GAC/D,KAAK,cAAc,KAAK,aAAa,KAAK,eAAe;KACxD,GAAG;;CAGR,OAAO,SAAuB;EAC5B,KAAK,UAAU;;CAGjB,QAAQ,SAAwB;EAC9B,KAAK,MAAM;EACX,MAAM,UAAU,KAAK,OAAO,KAAK,KAAK,GAAG,KAAK,aAAa,IAAK;EAChE,MAAM,aAAa,UAAU,IAAI,OAAO,QAAQ,KAAK;EACrD,QAAQ,OAAO,MAAM,KAAK,WAAW,KAAK,UAAU,WAAW,IAAI;;CAGrE,KAAK,SAAwB;EAC3B,KAAK,MAAM;EACX,QAAQ,OAAO,MAAM,KAAK,WAAW,KAAK,QAAQ,IAAI;;CAGxD,OAAa;EACX,IAAI,KAAK,UAAU;GACjB,cAAc,KAAK,SAAS;GAC5B,KAAK,WAAW;;EAElB,IAAI,QAAQ,OAAO,OACjB,QAAQ,OAAO,MAAM,WAAW;;;;;;AAqEtC,SAAgB,cAAc,MAAgD,SAAuB;CAOnG,QAAQ,OAAO,MAAM,GAAG;EALtB,MAAM;EACN,SAAS;EACT,SAAS;EACT,OAAO;EAEoB,CAAC,MAAM,GAAG,QAAQ,IAAI"}
1
+ {"version":3,"file":"progress-indicators.js","names":[],"sources":["../../utils/progress-indicators.ts"],"sourcesContent":["/**\n * Progress indicators, spinners, and status feedback for CLI operations\n */\n\nconst SPINNER_FRAMES = [\"⠋\", \"⠙\", \"⠹\", \"⠸\", \"⠼\", \"⠴\", \"⠦\", \"⠧\", \"⠇\", \"⠏\"];\n\nexport class ProgressSpinner {\n private interval: NodeJS.Timeout | null = null;\n private frameIndex = 0;\n private message: string;\n private startTime: number;\n\n constructor(message: string) {\n this.message = message;\n this.startTime = Date.now();\n }\n\n start(): void {\n if (this.interval) return;\n\n // Only show spinner in TTY mode\n if (!process.stdout.isTTY) {\n return;\n }\n\n this.interval = setInterval(() => {\n const frame = SPINNER_FRAMES[this.frameIndex];\n const elapsed = Math.floor((Date.now() - this.startTime) / 1000);\n const elapsedStr = elapsed > 0 ? ` (${elapsed}s)` : \"\";\n process.stdout.write(`\\r${frame} ${this.message}${elapsedStr}`);\n this.frameIndex = (this.frameIndex + 1) % SPINNER_FRAMES.length;\n }, 80);\n }\n\n update(message: string): void {\n this.message = message;\n }\n\n success(message?: string): void {\n this.stop();\n const elapsed = Math.floor((Date.now() - this.startTime) / 1000);\n const elapsedStr = elapsed > 0 ? ` in ${elapsed}s` : \"\";\n process.stdout.write(`✓ ${message ?? this.message}${elapsedStr}\\n`);\n }\n\n fail(message?: string): void {\n this.stop();\n process.stdout.write(`✗ ${message ?? this.message}\\n`);\n }\n\n stop(): void {\n if (this.interval) {\n clearInterval(this.interval);\n this.interval = null;\n }\n if (process.stdout.isTTY) {\n process.stdout.write(\"\\r\\x1b[K\"); // Clear line\n }\n }\n}\n\nexport class ProgressBar {\n private current = 0;\n private message: string;\n private total: number;\n private startTime: number;\n private width = 40;\n private lastLoggedPercent = -1;\n\n constructor(message: string, total: number) {\n this.message = message;\n this.total = total;\n this.startTime = Date.now();\n }\n\n update(current: number, status?: string): void {\n const safeTotal = Math.max(1, this.total);\n const percent = Math.max(0, Math.min(100, Math.floor((current / safeTotal) * 100)));\n\n if (!process.stdout.isTTY) {\n const milestone = Math.floor(percent / 10) * 10;\n if (milestone > this.lastLoggedPercent) {\n this.lastLoggedPercent = milestone;\n const statusStr = status ? ` - ${status}` : \"\";\n process.stdout.write(`${this.message}: ${milestone}% (${current}/${this.total})${statusStr}\\n`);\n }\n return;\n }\n\n const filled = Math.floor((current / safeTotal) * this.width);\n const empty = this.width - filled;\n const bar = \"█\".repeat(filled) + \"░\".repeat(empty);\n\n // Calculate ETA\n const elapsed = Date.now() - this.startTime;\n const rate = current / (elapsed / 1000);\n const remaining = Math.max(0, this.total - current);\n const eta = remaining / rate;\n const etaStr = eta > 0 && Number.isFinite(eta) ? ` ETA ${formatDuration(eta)}` : \"\";\n\n const statusStr = status ? ` - ${status}` : \"\";\n process.stdout.write(`\\r${this.message}: [${bar}] ${percent}% (${current}/${this.total})${etaStr}${statusStr}`);\n }\n\n complete(message?: string): void {\n if (process.stdout.isTTY) {\n process.stdout.write(\"\\r\\x1b[K\"); // Clear line\n }\n const elapsed = Math.floor((Date.now() - this.startTime) / 1000);\n process.stdout.write(`✓ ${message || this.message} - ${this.total} items in ${elapsed}s\\n`);\n }\n}\n\nfunction formatDuration(seconds: number): string {\n if (seconds < 60) {\n return `${Math.floor(seconds)}s`;\n }\n const mins = Math.floor(seconds / 60);\n const secs = Math.floor(seconds % 60);\n return `${mins}m ${secs}s`;\n}\n\n/**\n * Simple status message with icon\n */\nexport function statusMessage(type: \"info\" | \"success\" | \"warning\" | \"error\", message: string): void {\n const icons = {\n info: \"ℹ️\",\n success: \"✓\",\n warning: \"⚠️\",\n error: \"✗\",\n };\n process.stdout.write(`${icons[type]} ${message}\\n`);\n}\n\n/**\n * Show a completion summary with stats\n */\nexport function showCompletionSummary(\n operation: string,\n stats: Record<string, number | string>,\n durationMs: number,\n): void {\n const durationStr = formatDuration(durationMs / 1000);\n process.stdout.write(`✓ ${operation} complete in ${durationStr}\\n`);\n\n for (const [key, value] of Object.entries(stats)) {\n process.stdout.write(` ${key}: ${value}\\n`);\n }\n}\n"],"mappings":";;;;AAIA,MAAM,iBAAiB;CAAC;CAAK;CAAK;CAAK;CAAK;CAAK;CAAK;CAAK;CAAK;CAAK;AAAG;AAExE,IAAa,kBAAb,MAA6B;CAC3B,WAA0C;CAC1C,aAAqB;CACrB;CACA;CAEA,YAAY,SAAiB;EAC3B,KAAK,UAAU;EACf,KAAK,YAAY,KAAK,IAAI;CAC5B;CAEA,QAAc;EACZ,IAAI,KAAK,UAAU;EAGnB,IAAI,CAAC,QAAQ,OAAO,OAClB;EAGF,KAAK,WAAW,kBAAkB;GAChC,MAAM,QAAQ,eAAe,KAAK;GAClC,MAAM,UAAU,KAAK,OAAO,KAAK,IAAI,IAAI,KAAK,aAAa,GAAI;GAC/D,MAAM,aAAa,UAAU,IAAI,KAAK,QAAQ,MAAM;GACpD,QAAQ,OAAO,MAAM,KAAK,MAAM,GAAG,KAAK,UAAU,YAAY;GAC9D,KAAK,cAAc,KAAK,aAAa,KAAK,eAAe;EAC3D,GAAG,EAAE;CACP;CAEA,OAAO,SAAuB;EAC5B,KAAK,UAAU;CACjB;CAEA,QAAQ,SAAwB;EAC9B,KAAK,KAAK;EACV,MAAM,UAAU,KAAK,OAAO,KAAK,IAAI,IAAI,KAAK,aAAa,GAAI;EAC/D,MAAM,aAAa,UAAU,IAAI,OAAO,QAAQ,KAAK;EACrD,QAAQ,OAAO,MAAM,KAAK,WAAW,KAAK,UAAU,WAAW,GAAG;CACpE;CAEA,KAAK,SAAwB;EAC3B,KAAK,KAAK;EACV,QAAQ,OAAO,MAAM,KAAK,WAAW,KAAK,QAAQ,GAAG;CACvD;CAEA,OAAa;EACX,IAAI,KAAK,UAAU;GACjB,cAAc,KAAK,QAAQ;GAC3B,KAAK,WAAW;EAClB;EACA,IAAI,QAAQ,OAAO,OACjB,QAAQ,OAAO,MAAM,UAAU;CAEnC;AACF;;;;AAkEA,SAAgB,cAAc,MAAgD,SAAuB;CAOnG,QAAQ,OAAO,MAAM,GAAG;EALtB,MAAM;EACN,SAAS;EACT,SAAS;EACT,OAAO;CAEmB,EAAE,MAAM,GAAG,QAAQ,GAAG;AACpD"}
@@ -1 +1 @@
1
- {"version":3,"file":"prompt-loader.js","names":[],"sources":["../../utils/prompt-loader.ts"],"sourcesContent":["/**\n * Load prompt templates from prompts/ directory.\n * Resolves prompts/ relative to the plugin root (the directory containing\n * `openclaw.plugin.json`), so paths are correct whether the runtime entry is\n * the TS source or the compiled `dist/index.js` (issue #1174).\n */\n\nimport { readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { findPluginRoot } from \"./plugin-root.js\";\n\nconst PROMPTS_DIR = join(findPluginRoot(import.meta.url), \"prompts\");\n\nconst cache = new Map<string, string>();\n\nexport function loadPrompt(name: string): string {\n let template = cache.get(name);\n if (template !== undefined) return template;\n const path = join(PROMPTS_DIR, `${name}.txt`);\n template = readFileSync(path, \"utf-8\");\n cache.set(name, template);\n return template;\n}\n\nfunction escapeRegExp(str: string): string {\n return str.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\nexport function fillPrompt(template: string, vars: Record<string, string>): string {\n let out = template;\n for (const [key, value] of Object.entries(vars)) {\n const pattern = new RegExp(`\\\\{\\\\{${escapeRegExp(key)}\\\\}\\\\}`, \"g\");\n out = out.replace(pattern, () => value);\n }\n return out;\n}\n"],"mappings":";;;;;;;;;;AAWA,MAAM,cAAc,KAAK,eAAe,OAAO,KAAK,IAAI,EAAE,UAAU;AAEpE,MAAM,wBAAQ,IAAI,KAAqB;AAEvC,SAAgB,WAAW,MAAsB;CAC/C,IAAI,WAAW,MAAM,IAAI,KAAK;CAC9B,IAAI,aAAa,KAAA,GAAW,OAAO;CAEnC,WAAW,aADE,KAAK,aAAa,GAAG,KAAK,MACX,EAAE,QAAQ;CACtC,MAAM,IAAI,MAAM,SAAS;CACzB,OAAO;;AAGT,SAAS,aAAa,KAAqB;CACzC,OAAO,IAAI,QAAQ,uBAAuB,OAAO;;AAGnD,SAAgB,WAAW,UAAkB,MAAsC;CACjF,IAAI,MAAM;CACV,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,KAAK,EAAE;EAC/C,MAAM,UAAU,IAAI,OAAO,SAAS,aAAa,IAAI,CAAC,SAAS,IAAI;EACnE,MAAM,IAAI,QAAQ,eAAe,MAAM;;CAEzC,OAAO"}
1
+ {"version":3,"file":"prompt-loader.js","names":[],"sources":["../../utils/prompt-loader.ts"],"sourcesContent":["/**\n * Load prompt templates from prompts/ directory.\n * Resolves prompts/ relative to the plugin root (the directory containing\n * `openclaw.plugin.json`), so paths are correct whether the runtime entry is\n * the TS source or the compiled `dist/index.js` (issue #1174).\n */\n\nimport { readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { findPluginRoot } from \"./plugin-root.js\";\n\nconst PROMPTS_DIR = join(findPluginRoot(import.meta.url), \"prompts\");\n\nconst cache = new Map<string, string>();\n\nexport function loadPrompt(name: string): string {\n let template = cache.get(name);\n if (template !== undefined) return template;\n const path = join(PROMPTS_DIR, `${name}.txt`);\n template = readFileSync(path, \"utf-8\");\n cache.set(name, template);\n return template;\n}\n\nfunction escapeRegExp(str: string): string {\n return str.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\nexport function fillPrompt(template: string, vars: Record<string, string>): string {\n let out = template;\n for (const [key, value] of Object.entries(vars)) {\n const pattern = new RegExp(`\\\\{\\\\{${escapeRegExp(key)}\\\\}\\\\}`, \"g\");\n out = out.replace(pattern, () => value);\n }\n return out;\n}\n"],"mappings":";;;;;;;;;;AAWA,MAAM,cAAc,KAAK,eAAe,OAAO,KAAK,GAAG,GAAG,SAAS;AAEnE,MAAM,wBAAQ,IAAI,IAAoB;AAEtC,SAAgB,WAAW,MAAsB;CAC/C,IAAI,WAAW,MAAM,IAAI,IAAI;CAC7B,IAAI,aAAa,KAAA,GAAW,OAAO;CAEnC,WAAW,aADE,KAAK,aAAa,GAAG,KAAK,KACZ,GAAG,OAAO;CACrC,MAAM,IAAI,MAAM,QAAQ;CACxB,OAAO;AACT;AAEA,SAAS,aAAa,KAAqB;CACzC,OAAO,IAAI,QAAQ,uBAAuB,MAAM;AAClD;AAEA,SAAgB,WAAW,UAAkB,MAAsC;CACjF,IAAI,MAAM;CACV,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,IAAI,GAAG;EAC/C,MAAM,UAAU,IAAI,OAAO,SAAS,aAAa,GAAG,EAAE,SAAS,GAAG;EAClE,MAAM,IAAI,QAAQ,eAAe,KAAK;CACxC;CACA,OAAO;AACT"}
@@ -1 +1 @@
1
- {"version":3,"file":"provenance.js","names":[],"sources":["../../utils/provenance.ts"],"sourcesContent":["/**\n * ACP provenance utilities for council reviews (Issue #280).\n *\n * Council reviews are orchestrated externally by the main agent (not by this plugin).\n * This module provides helpers so orchestrators can include provenance metadata in:\n * - ACP session spawn calls (X-Trace-Id, X-Council-Member, X-Session-Key headers)\n * - GitHub PR review comments (trace ID in comment body)\n *\n * Usage in an orchestration agent:\n * import { getProvenanceHeaders, formatProvenanceReceipt } from \".../utils/provenance.js\";\n * const headers = getProvenanceHeaders(sessionKey, { councilMember: \"🔮 Gemini\" });\n * // Pass headers to sessions_spawn, and append formatProvenanceReceipt(...) to the review body.\n */\n\nimport { randomBytes } from \"node:crypto\";\nimport type { CouncilProvenanceMode } from \"../config/types/maintenance.js\";\n\n/**\n * ACP provenance headers for a council review session spawn.\n *\n * Standard fields:\n * - X-Trace-Id: unique identifier for this council review run (shared across all members)\n * - X-Council-Member: name/label of this reviewer (e.g. \"Gemini Architect\")\n * - X-Session-Key: the derived session key for this council member session\n * - X-Parent-Session: the orchestrator session that spawned this review\n */\ntype ProvenanceHeaders = Record<string, string>;\n\n/**\n * Generate ACP provenance headers for a council member session.\n *\n * @param sessionKey Unique key for this council member session (e.g. \"council-review-abc123\")\n * @param opts Optional metadata to include in headers\n * @returns Map of header name → value; pass to sessions_spawn or embed in comments\n *\n * @example\n * const headers = getProvenanceHeaders(\"council-review-pr-283\", {\n * councilMember: \"🔮 Gemini Architect\",\n * traceId: sharedTraceId,\n * parentSession: \"main\",\n * });\n */\nexport function getProvenanceHeaders(\n sessionKey: string,\n opts: {\n councilMember?: string;\n traceId?: string;\n parentSession?: string;\n } = {},\n): ProvenanceHeaders {\n const traceId = opts.traceId ?? generateTraceId();\n const headers: ProvenanceHeaders = {\n \"X-Trace-Id\": traceId,\n \"X-Session-Key\": sessionKey,\n };\n if (opts.councilMember) {\n headers[\"X-Council-Member\"] = opts.councilMember;\n }\n if (opts.parentSession) {\n headers[\"X-Parent-Session\"] = opts.parentSession;\n }\n return headers;\n}\n\n/**\n * Format a provenance receipt to append to a GitHub PR review comment.\n * This creates a traceable audit trail linking the GitHub comment to the ACP session.\n *\n * @param traceId The trace ID from getProvenanceHeaders (or a shared run ID)\n * @param sessionKey The council member session key\n * @returns Markdown string to append to the review body\n *\n * @example\n * const receipt = formatProvenanceReceipt(\"trace-abc123\", \"council-review-pr-283\");\n * // Append to review body: body + \"\\n\\n\" + receipt\n */\nexport function formatProvenanceReceipt(traceId: string, sessionKey: string): string {\n const ts = new Date().toISOString();\n return `---\\n*Provenance: trace-id=${traceId} session=${sessionKey} at=${ts}*`;\n}\n\n/**\n * Build provenance metadata based on the configured mode.\n *\n * @param mode Council provenance mode from config (default: \"meta+receipt\")\n * @param sessionKey Council member session key\n * @param opts Optional: councilMember name, parentSession, shared traceId\n * @returns { headers, receipt } — headers for ACP spawn, receipt for GitHub comments\n * Either may be null depending on mode.\n */\nexport function buildProvenanceMetadata(\n mode: CouncilProvenanceMode,\n sessionKey: string,\n opts: {\n councilMember?: string;\n traceId?: string;\n parentSession?: string;\n } = {},\n): { headers: ProvenanceHeaders | null; receipt: string | null } {\n if (mode === \"none\") {\n return { headers: null, receipt: null };\n }\n\n const traceId = opts.traceId ?? generateTraceId();\n\n const headers: ProvenanceHeaders | null =\n mode === \"meta\" || mode === \"meta+receipt\" ? getProvenanceHeaders(sessionKey, { ...opts, traceId }) : null;\n\n const receipt: string | null =\n mode === \"receipt\" || mode === \"meta+receipt\" ? formatProvenanceReceipt(traceId, sessionKey) : null;\n\n return { headers, receipt };\n}\n\n/**\n * Generate a unique trace ID for a council review run.\n * Format: \"trace-<8 hex chars>\" — short enough to include in comments, unique enough for audit.\n */\nexport function generateTraceId(): string {\n return `trace-${randomBytes(4).toString(\"hex\")}`;\n}\n\n/**\n * Build a council session key from a prefix and optional PR/run identifier.\n *\n * @param prefix Session key prefix from config (e.g. \"council-review\")\n * @param suffix Optional suffix (e.g. PR number \"283\" or short hash)\n * @returns Combined session key (e.g. \"council-review-283\" or \"council-review-a1b2c3d4\")\n */\nexport function buildCouncilSessionKey(prefix: string, suffix?: string): string {\n const s = suffix?.trim() || randomBytes(4).toString(\"hex\");\n return `${prefix}-${s}`;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0CA,SAAgB,qBACd,YACA,OAII,EAAE,EACa;CAEnB,MAAM,UAA6B;EACjC,cAFc,KAAK,WAAW,iBAAiB;EAG/C,iBAAiB;EAClB;CACD,IAAI,KAAK,eACP,QAAQ,sBAAsB,KAAK;CAErC,IAAI,KAAK,eACP,QAAQ,sBAAsB,KAAK;CAErC,OAAO;;;;;;;;;;;;;;AAeT,SAAgB,wBAAwB,SAAiB,YAA4B;CAEnF,OAAO,8BAA8B,QAAQ,WAAW,WAAW,uBADxD,IAAI,MAAM,EAAC,aACqD,CAAC;;;;;;;;;;;AAY9E,SAAgB,wBACd,MACA,YACA,OAII,EAAE,EACyD;CAC/D,IAAI,SAAS,QACX,OAAO;EAAE,SAAS;EAAM,SAAS;EAAM;CAGzC,MAAM,UAAU,KAAK,WAAW,iBAAiB;CAQjD,OAAO;EAAE,SALP,SAAS,UAAU,SAAS,iBAAiB,qBAAqB,YAAY;GAAE,GAAG;GAAM;GAAS,CAAC,GAAG;EAKtF,SAFhB,SAAS,aAAa,SAAS,iBAAiB,wBAAwB,SAAS,WAAW,GAAG;EAEtE;;;;;;AAO7B,SAAgB,kBAA0B;CACxC,OAAO,SAAS,YAAY,EAAE,CAAC,SAAS,MAAM;;;;;;;;;AAUhD,SAAgB,uBAAuB,QAAgB,QAAyB;CAE9E,OAAO,GAAG,OAAO,GADP,QAAQ,MAAM,IAAI,YAAY,EAAE,CAAC,SAAS,MAAM"}
1
+ {"version":3,"file":"provenance.js","names":[],"sources":["../../utils/provenance.ts"],"sourcesContent":["/**\n * ACP provenance utilities for council reviews (Issue #280).\n *\n * Council reviews are orchestrated externally by the main agent (not by this plugin).\n * This module provides helpers so orchestrators can include provenance metadata in:\n * - ACP session spawn calls (X-Trace-Id, X-Council-Member, X-Session-Key headers)\n * - GitHub PR review comments (trace ID in comment body)\n *\n * Usage in an orchestration agent:\n * import { getProvenanceHeaders, formatProvenanceReceipt } from \".../utils/provenance.js\";\n * const headers = getProvenanceHeaders(sessionKey, { councilMember: \"🔮 Gemini\" });\n * // Pass headers to sessions_spawn, and append formatProvenanceReceipt(...) to the review body.\n */\n\nimport { randomBytes } from \"node:crypto\";\nimport type { CouncilProvenanceMode } from \"../config/types/maintenance.js\";\n\n/**\n * ACP provenance headers for a council review session spawn.\n *\n * Standard fields:\n * - X-Trace-Id: unique identifier for this council review run (shared across all members)\n * - X-Council-Member: name/label of this reviewer (e.g. \"Gemini Architect\")\n * - X-Session-Key: the derived session key for this council member session\n * - X-Parent-Session: the orchestrator session that spawned this review\n */\ntype ProvenanceHeaders = Record<string, string>;\n\n/**\n * Generate ACP provenance headers for a council member session.\n *\n * @param sessionKey Unique key for this council member session (e.g. \"council-review-abc123\")\n * @param opts Optional metadata to include in headers\n * @returns Map of header name → value; pass to sessions_spawn or embed in comments\n *\n * @example\n * const headers = getProvenanceHeaders(\"council-review-pr-283\", {\n * councilMember: \"🔮 Gemini Architect\",\n * traceId: sharedTraceId,\n * parentSession: \"main\",\n * });\n */\nexport function getProvenanceHeaders(\n sessionKey: string,\n opts: {\n councilMember?: string;\n traceId?: string;\n parentSession?: string;\n } = {},\n): ProvenanceHeaders {\n const traceId = opts.traceId ?? generateTraceId();\n const headers: ProvenanceHeaders = {\n \"X-Trace-Id\": traceId,\n \"X-Session-Key\": sessionKey,\n };\n if (opts.councilMember) {\n headers[\"X-Council-Member\"] = opts.councilMember;\n }\n if (opts.parentSession) {\n headers[\"X-Parent-Session\"] = opts.parentSession;\n }\n return headers;\n}\n\n/**\n * Format a provenance receipt to append to a GitHub PR review comment.\n * This creates a traceable audit trail linking the GitHub comment to the ACP session.\n *\n * @param traceId The trace ID from getProvenanceHeaders (or a shared run ID)\n * @param sessionKey The council member session key\n * @returns Markdown string to append to the review body\n *\n * @example\n * const receipt = formatProvenanceReceipt(\"trace-abc123\", \"council-review-pr-283\");\n * // Append to review body: body + \"\\n\\n\" + receipt\n */\nexport function formatProvenanceReceipt(traceId: string, sessionKey: string): string {\n const ts = new Date().toISOString();\n return `---\\n*Provenance: trace-id=${traceId} session=${sessionKey} at=${ts}*`;\n}\n\n/**\n * Build provenance metadata based on the configured mode.\n *\n * @param mode Council provenance mode from config (default: \"meta+receipt\")\n * @param sessionKey Council member session key\n * @param opts Optional: councilMember name, parentSession, shared traceId\n * @returns { headers, receipt } — headers for ACP spawn, receipt for GitHub comments\n * Either may be null depending on mode.\n */\nexport function buildProvenanceMetadata(\n mode: CouncilProvenanceMode,\n sessionKey: string,\n opts: {\n councilMember?: string;\n traceId?: string;\n parentSession?: string;\n } = {},\n): { headers: ProvenanceHeaders | null; receipt: string | null } {\n if (mode === \"none\") {\n return { headers: null, receipt: null };\n }\n\n const traceId = opts.traceId ?? generateTraceId();\n\n const headers: ProvenanceHeaders | null =\n mode === \"meta\" || mode === \"meta+receipt\" ? getProvenanceHeaders(sessionKey, { ...opts, traceId }) : null;\n\n const receipt: string | null =\n mode === \"receipt\" || mode === \"meta+receipt\" ? formatProvenanceReceipt(traceId, sessionKey) : null;\n\n return { headers, receipt };\n}\n\n/**\n * Generate a unique trace ID for a council review run.\n * Format: \"trace-<8 hex chars>\" — short enough to include in comments, unique enough for audit.\n */\nexport function generateTraceId(): string {\n return `trace-${randomBytes(4).toString(\"hex\")}`;\n}\n\n/**\n * Build a council session key from a prefix and optional PR/run identifier.\n *\n * @param prefix Session key prefix from config (e.g. \"council-review\")\n * @param suffix Optional suffix (e.g. PR number \"283\" or short hash)\n * @returns Combined session key (e.g. \"council-review-283\" or \"council-review-a1b2c3d4\")\n */\nexport function buildCouncilSessionKey(prefix: string, suffix?: string): string {\n const s = suffix?.trim() || randomBytes(4).toString(\"hex\");\n return `${prefix}-${s}`;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0CA,SAAgB,qBACd,YACA,OAII,CAAC,GACc;CAEnB,MAAM,UAA6B;EACjC,cAFc,KAAK,WAAW,gBAAgB;EAG9C,iBAAiB;CACnB;CACA,IAAI,KAAK,eACP,QAAQ,sBAAsB,KAAK;CAErC,IAAI,KAAK,eACP,QAAQ,sBAAsB,KAAK;CAErC,OAAO;AACT;;;;;;;;;;;;;AAcA,SAAgB,wBAAwB,SAAiB,YAA4B;CAEnF,OAAO,8BAA8B,QAAQ,WAAW,WAAW,uBADxD,IAAI,KAAK,GAAE,YACoD,EAAE;AAC9E;;;;;;;;;;AAWA,SAAgB,wBACd,MACA,YACA,OAII,CAAC,GAC0D;CAC/D,IAAI,SAAS,QACX,OAAO;EAAE,SAAS;EAAM,SAAS;CAAK;CAGxC,MAAM,UAAU,KAAK,WAAW,gBAAgB;CAQhD,OAAO;EAAE,SALP,SAAS,UAAU,SAAS,iBAAiB,qBAAqB,YAAY;GAAE,GAAG;GAAM;EAAQ,CAAC,IAAI;EAKtF,SAFhB,SAAS,aAAa,SAAS,iBAAiB,wBAAwB,SAAS,UAAU,IAAI;CAEvE;AAC5B;;;;;AAMA,SAAgB,kBAA0B;CACxC,OAAO,SAAS,YAAY,CAAC,EAAE,SAAS,KAAK;AAC/C;;;;;;;;AASA,SAAgB,uBAAuB,QAAgB,QAAyB;CAE9E,OAAO,GAAG,OAAO,GADP,QAAQ,KAAK,KAAK,YAAY,CAAC,EAAE,SAAS,KAAK;AAE3D"}
@@ -1 +1 @@
1
- {"version":3,"file":"provider-detection.js","names":[],"sources":["../../utils/provider-detection.ts"],"sourcesContent":["/**\n * Auto-detect available embedding providers and their status\n */\n\nimport { createRequire } from \"node:module\";\n\nconst require = createRequire(import.meta.url);\n\nexport interface ProviderStatus {\n provider: \"openai\" | \"ollama\" | \"onnx\" | \"google\";\n available: boolean;\n reason?: string;\n recommendation?: string;\n requiresApiKey: boolean;\n localOnly: boolean;\n}\n\n/**\n * Check if Ollama is running on localhost\n */\nasync function checkOllamaAvailable(): Promise<boolean> {\n try {\n const response = await fetch(\"http://localhost:11434/api/tags\", {\n signal: AbortSignal.timeout(2000),\n });\n return response.ok;\n } catch {\n return false;\n }\n}\n\n/**\n * Check if ONNX runtime is available\n */\nfunction checkOnnxAvailable(): boolean {\n try {\n // Check if onnxruntime-node is installed\n require.resolve(\"onnxruntime-node\");\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Check OpenAI API availability (requires API key)\n */\nfunction hasConfiguredApiKey(apiKey?: string): boolean {\n return Boolean(apiKey && apiKey.trim().length > 0);\n}\n\n/**\n * Detect all available embedding providers\n */\nexport async function detectAvailableProviders(\n currentApiKey?: string,\n googleApiKey?: string,\n): Promise<ProviderStatus[]> {\n const ollamaAvailable = await checkOllamaAvailable();\n const onnxAvailable = checkOnnxAvailable();\n const openaiConfigured = hasConfiguredApiKey(currentApiKey);\n const googleConfigured = hasConfiguredApiKey(googleApiKey);\n\n return [\n {\n provider: \"ollama\",\n available: ollamaAvailable,\n reason: ollamaAvailable\n ? \"Ollama is running on localhost:11434\"\n : \"Ollama is not running. Start with: ollama serve\",\n recommendation: ollamaAvailable\n ? \"Best for local, private embedding without API costs\"\n : \"Install from https://ollama.ai and run: ollama pull nomic-embed-text\",\n requiresApiKey: false,\n localOnly: true,\n },\n {\n provider: \"onnx\",\n available: onnxAvailable,\n reason: onnxAvailable ? \"ONNX runtime is installed\" : \"onnxruntime-node is not installed\",\n recommendation: onnxAvailable\n ? \"Lightweight local embeddings, no dependencies\"\n : \"Install with: npm install onnxruntime-node\",\n requiresApiKey: false,\n localOnly: true,\n },\n {\n provider: \"openai\",\n available: openaiConfigured,\n reason: openaiConfigured ? \"API key is configured\" : \"No API key found. Requires OpenAI API key\",\n recommendation: \"High-quality embeddings, requires API key and incurs costs\",\n requiresApiKey: true,\n localOnly: false,\n },\n {\n provider: \"google\",\n available: googleConfigured,\n reason: googleConfigured ? \"Google API key is configured\" : \"No API key found. Requires Google/Gemini API key\",\n recommendation: \"Google embeddings via configured Gemini/Google API key\",\n requiresApiKey: true,\n localOnly: false,\n },\n ];\n}\n\n/**\n * Recommend the best available provider based on what's currently available\n */\nexport async function recommendProvider(\n currentApiKey?: string,\n googleApiKey?: string,\n): Promise<\"ollama\" | \"onnx\" | \"openai\" | \"google\"> {\n const providers = await detectAvailableProviders(currentApiKey, googleApiKey);\n\n // Prefer local providers to avoid API costs for new users\n const ollama = providers.find((p) => p.provider === \"ollama\");\n if (ollama?.available) {\n return \"ollama\";\n }\n\n const onnx = providers.find((p) => p.provider === \"onnx\");\n if (onnx?.available) {\n return \"onnx\";\n }\n\n // Fall back to configured cloud providers.\n const openai = providers.find((p) => p.provider === \"openai\");\n if (openai?.available) return \"openai\";\n const google = providers.find((p) => p.provider === \"google\");\n if (google?.available) return \"google\";\n\n // Default to ollama with instructions to set it up\n return \"ollama\";\n}\n\n/**\n * Format provider status for CLI display\n */\nexport function formatProviderStatus(status: ProviderStatus): string {\n const icon = status.available ? \"✓\" : \"✗\";\n const badge = status.localOnly ? \"[Local]\" : \"[Cloud]\";\n const apiKeyBadge = status.requiresApiKey ? \"[API Key Required]\" : \"\";\n\n const lines = [`${icon} ${status.provider.toUpperCase()} ${badge} ${apiKeyBadge}`, ` ${status.reason}`];\n\n if (status.recommendation) {\n lines.push(` ${status.recommendation}`);\n }\n\n return lines.join(\"\\n\");\n}\n"],"mappings":";;;;;AAMA,MAAM,UAAU,cAAc,OAAO,KAAK,IAAI;;;;AAc9C,eAAe,uBAAyC;CACtD,IAAI;EAIF,QAAO,MAHgB,MAAM,mCAAmC,EAC9D,QAAQ,YAAY,QAAQ,IAAK,EAClC,CAAC,EACc;SACV;EACN,OAAO;;;;;;AAOX,SAAS,qBAA8B;CACrC,IAAI;EAEF,QAAQ,QAAQ,mBAAmB;EACnC,OAAO;SACD;EACN,OAAO;;;;;;AAOX,SAAS,oBAAoB,QAA0B;CACrD,OAAO,QAAQ,UAAU,OAAO,MAAM,CAAC,SAAS,EAAE;;;;;AAMpD,eAAsB,yBACpB,eACA,cAC2B;CAC3B,MAAM,kBAAkB,MAAM,sBAAsB;CACpD,MAAM,gBAAgB,oBAAoB;CAC1C,MAAM,mBAAmB,oBAAoB,cAAc;CAC3D,MAAM,mBAAmB,oBAAoB,aAAa;CAE1D,OAAO;EACL;GACE,UAAU;GACV,WAAW;GACX,QAAQ,kBACJ,yCACA;GACJ,gBAAgB,kBACZ,wDACA;GACJ,gBAAgB;GAChB,WAAW;GACZ;EACD;GACE,UAAU;GACV,WAAW;GACX,QAAQ,gBAAgB,8BAA8B;GACtD,gBAAgB,gBACZ,kDACA;GACJ,gBAAgB;GAChB,WAAW;GACZ;EACD;GACE,UAAU;GACV,WAAW;GACX,QAAQ,mBAAmB,0BAA0B;GACrD,gBAAgB;GAChB,gBAAgB;GAChB,WAAW;GACZ;EACD;GACE,UAAU;GACV,WAAW;GACX,QAAQ,mBAAmB,iCAAiC;GAC5D,gBAAgB;GAChB,gBAAgB;GAChB,WAAW;GACZ;EACF;;;;;AAMH,eAAsB,kBACpB,eACA,cACkD;CAClD,MAAM,YAAY,MAAM,yBAAyB,eAAe,aAAa;CAI7E,IADe,UAAU,MAAM,MAAM,EAAE,aAAa,SAC1C,EAAE,WACV,OAAO;CAIT,IADa,UAAU,MAAM,MAAM,EAAE,aAAa,OAC1C,EAAE,WACR,OAAO;CAKT,IADe,UAAU,MAAM,MAAM,EAAE,aAAa,SAC1C,EAAE,WAAW,OAAO;CAE9B,IADe,UAAU,MAAM,MAAM,EAAE,aAAa,SAC1C,EAAE,WAAW,OAAO;CAG9B,OAAO;;;;;AAMT,SAAgB,qBAAqB,QAAgC;CACnE,MAAM,OAAO,OAAO,YAAY,MAAM;CACtC,MAAM,QAAQ,OAAO,YAAY,YAAY;CAC7C,MAAM,cAAc,OAAO,iBAAiB,uBAAuB;CAEnE,MAAM,QAAQ,CAAC,GAAG,KAAK,GAAG,OAAO,SAAS,aAAa,CAAC,GAAG,MAAM,GAAG,eAAe,MAAM,OAAO,SAAS;CAEzG,IAAI,OAAO,gBACT,MAAM,KAAK,MAAM,OAAO,iBAAiB;CAG3C,OAAO,MAAM,KAAK,KAAK"}
1
+ {"version":3,"file":"provider-detection.js","names":[],"sources":["../../utils/provider-detection.ts"],"sourcesContent":["/**\n * Auto-detect available embedding providers and their status\n */\n\nimport { createRequire } from \"node:module\";\n\nconst require = createRequire(import.meta.url);\n\nexport interface ProviderStatus {\n provider: \"openai\" | \"ollama\" | \"onnx\" | \"google\";\n available: boolean;\n reason?: string;\n recommendation?: string;\n requiresApiKey: boolean;\n localOnly: boolean;\n}\n\n/**\n * Check if Ollama is running on localhost\n */\nasync function checkOllamaAvailable(): Promise<boolean> {\n try {\n const response = await fetch(\"http://localhost:11434/api/tags\", {\n signal: AbortSignal.timeout(2000),\n });\n return response.ok;\n } catch {\n return false;\n }\n}\n\n/**\n * Check if ONNX runtime is available\n */\nfunction checkOnnxAvailable(): boolean {\n try {\n // Check if onnxruntime-node is installed\n require.resolve(\"onnxruntime-node\");\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Check OpenAI API availability (requires API key)\n */\nfunction hasConfiguredApiKey(apiKey?: string): boolean {\n return Boolean(apiKey && apiKey.trim().length > 0);\n}\n\n/**\n * Detect all available embedding providers\n */\nexport async function detectAvailableProviders(\n currentApiKey?: string,\n googleApiKey?: string,\n): Promise<ProviderStatus[]> {\n const ollamaAvailable = await checkOllamaAvailable();\n const onnxAvailable = checkOnnxAvailable();\n const openaiConfigured = hasConfiguredApiKey(currentApiKey);\n const googleConfigured = hasConfiguredApiKey(googleApiKey);\n\n return [\n {\n provider: \"ollama\",\n available: ollamaAvailable,\n reason: ollamaAvailable\n ? \"Ollama is running on localhost:11434\"\n : \"Ollama is not running. Start with: ollama serve\",\n recommendation: ollamaAvailable\n ? \"Best for local, private embedding without API costs\"\n : \"Install from https://ollama.ai and run: ollama pull nomic-embed-text\",\n requiresApiKey: false,\n localOnly: true,\n },\n {\n provider: \"onnx\",\n available: onnxAvailable,\n reason: onnxAvailable ? \"ONNX runtime is installed\" : \"onnxruntime-node is not installed\",\n recommendation: onnxAvailable\n ? \"Lightweight local embeddings, no dependencies\"\n : \"Install with: npm install onnxruntime-node\",\n requiresApiKey: false,\n localOnly: true,\n },\n {\n provider: \"openai\",\n available: openaiConfigured,\n reason: openaiConfigured ? \"API key is configured\" : \"No API key found. Requires OpenAI API key\",\n recommendation: \"High-quality embeddings, requires API key and incurs costs\",\n requiresApiKey: true,\n localOnly: false,\n },\n {\n provider: \"google\",\n available: googleConfigured,\n reason: googleConfigured ? \"Google API key is configured\" : \"No API key found. Requires Google/Gemini API key\",\n recommendation: \"Google embeddings via configured Gemini/Google API key\",\n requiresApiKey: true,\n localOnly: false,\n },\n ];\n}\n\n/**\n * Recommend the best available provider based on what's currently available\n */\nexport async function recommendProvider(\n currentApiKey?: string,\n googleApiKey?: string,\n): Promise<\"ollama\" | \"onnx\" | \"openai\" | \"google\"> {\n const providers = await detectAvailableProviders(currentApiKey, googleApiKey);\n\n // Prefer local providers to avoid API costs for new users\n const ollama = providers.find((p) => p.provider === \"ollama\");\n if (ollama?.available) {\n return \"ollama\";\n }\n\n const onnx = providers.find((p) => p.provider === \"onnx\");\n if (onnx?.available) {\n return \"onnx\";\n }\n\n // Fall back to configured cloud providers.\n const openai = providers.find((p) => p.provider === \"openai\");\n if (openai?.available) return \"openai\";\n const google = providers.find((p) => p.provider === \"google\");\n if (google?.available) return \"google\";\n\n // Default to ollama with instructions to set it up\n return \"ollama\";\n}\n\n/**\n * Format provider status for CLI display\n */\nexport function formatProviderStatus(status: ProviderStatus): string {\n const icon = status.available ? \"✓\" : \"✗\";\n const badge = status.localOnly ? \"[Local]\" : \"[Cloud]\";\n const apiKeyBadge = status.requiresApiKey ? \"[API Key Required]\" : \"\";\n\n const lines = [`${icon} ${status.provider.toUpperCase()} ${badge} ${apiKeyBadge}`, ` ${status.reason}`];\n\n if (status.recommendation) {\n lines.push(` ${status.recommendation}`);\n }\n\n return lines.join(\"\\n\");\n}\n"],"mappings":";;;;;AAMA,MAAM,UAAU,cAAc,OAAO,KAAK,GAAG;;;;AAc7C,eAAe,uBAAyC;CACtD,IAAI;EAIF,QAAO,MAHgB,MAAM,mCAAmC,EAC9D,QAAQ,YAAY,QAAQ,GAAI,EAClC,CAAC,GACe;CAClB,QAAQ;EACN,OAAO;CACT;AACF;;;;AAKA,SAAS,qBAA8B;CACrC,IAAI;EAEF,QAAQ,QAAQ,kBAAkB;EAClC,OAAO;CACT,QAAQ;EACN,OAAO;CACT;AACF;;;;AAKA,SAAS,oBAAoB,QAA0B;CACrD,OAAO,QAAQ,UAAU,OAAO,KAAK,EAAE,SAAS,CAAC;AACnD;;;;AAKA,eAAsB,yBACpB,eACA,cAC2B;CAC3B,MAAM,kBAAkB,MAAM,qBAAqB;CACnD,MAAM,gBAAgB,mBAAmB;CACzC,MAAM,mBAAmB,oBAAoB,aAAa;CAC1D,MAAM,mBAAmB,oBAAoB,YAAY;CAEzD,OAAO;EACL;GACE,UAAU;GACV,WAAW;GACX,QAAQ,kBACJ,yCACA;GACJ,gBAAgB,kBACZ,wDACA;GACJ,gBAAgB;GAChB,WAAW;EACb;EACA;GACE,UAAU;GACV,WAAW;GACX,QAAQ,gBAAgB,8BAA8B;GACtD,gBAAgB,gBACZ,kDACA;GACJ,gBAAgB;GAChB,WAAW;EACb;EACA;GACE,UAAU;GACV,WAAW;GACX,QAAQ,mBAAmB,0BAA0B;GACrD,gBAAgB;GAChB,gBAAgB;GAChB,WAAW;EACb;EACA;GACE,UAAU;GACV,WAAW;GACX,QAAQ,mBAAmB,iCAAiC;GAC5D,gBAAgB;GAChB,gBAAgB;GAChB,WAAW;EACb;CACF;AACF;;;;AAKA,eAAsB,kBACpB,eACA,cACkD;CAClD,MAAM,YAAY,MAAM,yBAAyB,eAAe,YAAY;CAI5E,IADe,UAAU,MAAM,MAAM,EAAE,aAAa,QAC3C,GAAG,WACV,OAAO;CAIT,IADa,UAAU,MAAM,MAAM,EAAE,aAAa,MAC3C,GAAG,WACR,OAAO;CAKT,IADe,UAAU,MAAM,MAAM,EAAE,aAAa,QAC3C,GAAG,WAAW,OAAO;CAE9B,IADe,UAAU,MAAM,MAAM,EAAE,aAAa,QAC3C,GAAG,WAAW,OAAO;CAG9B,OAAO;AACT;;;;AAKA,SAAgB,qBAAqB,QAAgC;CACnE,MAAM,OAAO,OAAO,YAAY,MAAM;CACtC,MAAM,QAAQ,OAAO,YAAY,YAAY;CAC7C,MAAM,cAAc,OAAO,iBAAiB,uBAAuB;CAEnE,MAAM,QAAQ,CAAC,GAAG,KAAK,GAAG,OAAO,SAAS,YAAY,EAAE,GAAG,MAAM,GAAG,eAAe,MAAM,OAAO,QAAQ;CAExG,IAAI,OAAO,gBACT,MAAM,KAAK,MAAM,OAAO,gBAAgB;CAG1C,OAAO,MAAM,KAAK,IAAI;AACxB"}
@@ -1 +1 @@
1
- {"version":3,"file":"registration-superseded.js","names":[],"sources":["../../utils/registration-superseded.ts"],"sourcesContent":["import { getHybridMemoryRegistrationState } from \"../setup/hybrid-memory-generation-state.js\";\n\nexport function isDbClosedError(err: unknown): boolean {\n const msg = err instanceof Error ? err.message : String(err);\n return /not open|connection is not open|database is not open/i.test(msg);\n}\n\n/** True when a newer plugin registration replaced this bootstrap / hook generation. */\nexport function isRegistrationSuperseded(bootRegistrationGeneration: number): boolean {\n if (bootRegistrationGeneration < 0) return false;\n return getHybridMemoryRegistrationState().registrationGenerationRef.value !== bootRegistrationGeneration;\n}\n\nexport type LifecycleGenerationContext = {\n registrationGeneration?: number;\n currentRegistrationGenerationRef?: { value: number };\n};\n\n/** Lifecycle / recall work tied to a specific plugin registration generation. */\nexport function isRecallContextSuperseded(ctx: LifecycleGenerationContext): boolean {\n const ownerGeneration = ctx.registrationGeneration ?? -1;\n if (ownerGeneration < 0) return false;\n const liveGeneration = ctx.currentRegistrationGenerationRef?.value;\n if (liveGeneration !== undefined && liveGeneration !== ownerGeneration) return true;\n return isRegistrationSuperseded(ownerGeneration);\n}\n\n/** Suppress noisy errors when reload closed DBs under in-flight work (not a user-facing outage). */\nexport function shouldSuppressStaleLifecycleError(ctx: LifecycleGenerationContext, err: unknown): boolean {\n return isRecallContextSuperseded(ctx) && isDbClosedError(err);\n}\n\n/** @deprecated Use shouldSuppressStaleLifecycleError */\nexport const shouldSuppressStaleRecallError = shouldSuppressStaleLifecycleError;\n\n/**\n * Log at debug and return true when err is a superseded-generation DB close.\n * Callers should return early without capturePluginError / logger.warn.\n */\nexport function suppressStaleLifecycleDbError(\n ctx: LifecycleGenerationContext,\n err: unknown,\n logger: { debug?: (msg: string) => void },\n debugMessage: string,\n): boolean {\n if (!shouldSuppressStaleLifecycleError(ctx, err)) return false;\n logger.debug?.(debugMessage);\n return true;\n}\n"],"mappings":";;AAEA,SAAgB,gBAAgB,KAAuB;CACrD,MAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;CAC5D,OAAO,wDAAwD,KAAK,IAAI;;;AAI1E,SAAgB,yBAAyB,4BAA6C;CACpF,IAAI,6BAA6B,GAAG,OAAO;CAC3C,OAAO,kCAAkC,CAAC,0BAA0B,UAAU;;;AAShF,SAAgB,0BAA0B,KAA0C;CAClF,MAAM,kBAAkB,IAAI,0BAA0B;CACtD,IAAI,kBAAkB,GAAG,OAAO;CAChC,MAAM,iBAAiB,IAAI,kCAAkC;CAC7D,IAAI,mBAAmB,KAAA,KAAa,mBAAmB,iBAAiB,OAAO;CAC/E,OAAO,yBAAyB,gBAAgB;;;AAIlD,SAAgB,kCAAkC,KAAiC,KAAuB;CACxG,OAAO,0BAA0B,IAAI,IAAI,gBAAgB,IAAI;;;;;;AAU/D,SAAgB,8BACd,KACA,KACA,QACA,cACS;CACT,IAAI,CAAC,kCAAkC,KAAK,IAAI,EAAE,OAAO;CACzD,OAAO,QAAQ,aAAa;CAC5B,OAAO"}
1
+ {"version":3,"file":"registration-superseded.js","names":[],"sources":["../../utils/registration-superseded.ts"],"sourcesContent":["import { getHybridMemoryRegistrationState } from \"../setup/hybrid-memory-generation-state.js\";\n\nexport function isDbClosedError(err: unknown): boolean {\n const msg = err instanceof Error ? err.message : String(err);\n return /not open|connection is not open|database is not open/i.test(msg);\n}\n\n/** True when a newer plugin registration replaced this bootstrap / hook generation. */\nexport function isRegistrationSuperseded(bootRegistrationGeneration: number): boolean {\n if (bootRegistrationGeneration < 0) return false;\n return getHybridMemoryRegistrationState().registrationGenerationRef.value !== bootRegistrationGeneration;\n}\n\nexport type LifecycleGenerationContext = {\n registrationGeneration?: number;\n currentRegistrationGenerationRef?: { value: number };\n};\n\n/** Lifecycle / recall work tied to a specific plugin registration generation. */\nexport function isRecallContextSuperseded(ctx: LifecycleGenerationContext): boolean {\n const ownerGeneration = ctx.registrationGeneration ?? -1;\n if (ownerGeneration < 0) return false;\n const liveGeneration = ctx.currentRegistrationGenerationRef?.value;\n if (liveGeneration !== undefined && liveGeneration !== ownerGeneration) return true;\n return isRegistrationSuperseded(ownerGeneration);\n}\n\n/** Suppress noisy errors when reload closed DBs under in-flight work (not a user-facing outage). */\nexport function shouldSuppressStaleLifecycleError(ctx: LifecycleGenerationContext, err: unknown): boolean {\n return isRecallContextSuperseded(ctx) && isDbClosedError(err);\n}\n\n/** @deprecated Use shouldSuppressStaleLifecycleError */\nexport const shouldSuppressStaleRecallError = shouldSuppressStaleLifecycleError;\n\n/**\n * Log at debug and return true when err is a superseded-generation DB close.\n * Callers should return early without capturePluginError / logger.warn.\n */\nexport function suppressStaleLifecycleDbError(\n ctx: LifecycleGenerationContext,\n err: unknown,\n logger: { debug?: (msg: string) => void },\n debugMessage: string,\n): boolean {\n if (!shouldSuppressStaleLifecycleError(ctx, err)) return false;\n logger.debug?.(debugMessage);\n return true;\n}\n"],"mappings":";;AAEA,SAAgB,gBAAgB,KAAuB;CACrD,MAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;CAC3D,OAAO,wDAAwD,KAAK,GAAG;AACzE;;AAGA,SAAgB,yBAAyB,4BAA6C;CACpF,IAAI,6BAA6B,GAAG,OAAO;CAC3C,OAAO,iCAAiC,EAAE,0BAA0B,UAAU;AAChF;;AAQA,SAAgB,0BAA0B,KAA0C;CAClF,MAAM,kBAAkB,IAAI,0BAA0B;CACtD,IAAI,kBAAkB,GAAG,OAAO;CAChC,MAAM,iBAAiB,IAAI,kCAAkC;CAC7D,IAAI,mBAAmB,KAAA,KAAa,mBAAmB,iBAAiB,OAAO;CAC/E,OAAO,yBAAyB,eAAe;AACjD;;AAGA,SAAgB,kCAAkC,KAAiC,KAAuB;CACxG,OAAO,0BAA0B,GAAG,KAAK,gBAAgB,GAAG;AAC9D;;;;;AASA,SAAgB,8BACd,KACA,KACA,QACA,cACS;CACT,IAAI,CAAC,kCAAkC,KAAK,GAAG,GAAG,OAAO;CACzD,OAAO,QAAQ,YAAY;CAC3B,OAAO;AACT"}
@@ -1 +1 @@
1
- {"version":3,"file":"salience.js","names":[],"sources":["../../utils/salience.ts"],"sourcesContent":["/**\n * Dynamic Salience Scoring (Access-Based Importance)\n *\n * Combines:\n * 1. Access Boost — frequently recalled facts score higher\n * 2. Time Decay — older, unused memories fade in relevance\n */\n\nimport type { MemoryEntry } from \"../types/memory.js\";\nimport { SECONDS_PER_DAY } from \"./constants.js\";\n\n/** Default half-life in days: after this many days without access, salience halves. */\nconst SALIENCE_DECAY_HALF_LIFE_DAYS = 30;\n\n/**\n * Compute dynamic salience multiplier for a base score.\n * Combines access boost (recall_count) and time decay (last_accessed).\n *\n * @param baseScore — Original relevance score (0–1)\n * @param entry — Memory with recallCount, lastAccessed, importance\n * @param options — Optional overrides\n * @returns Adjusted score\n */\nexport function computeDynamicSalience(\n baseScore: number,\n entry: MemoryEntry,\n options?: {\n /** Apply access boost (default true) */\n accessBoost?: boolean;\n /** Apply time decay (default true) */\n timeDecay?: boolean;\n /** Half-life in days for decay (default 30) */\n halfLifeDays?: number;\n /** Access boost strength (default 0.1) */\n accessBoostStrength?: number;\n },\n): number {\n const {\n accessBoost = true,\n timeDecay = true,\n halfLifeDays = SALIENCE_DECAY_HALF_LIFE_DAYS,\n accessBoostStrength = 0.1,\n } = options ?? {};\n\n let s = baseScore;\n\n // 1. Access Boost: frequently recalled facts score higher\n if (accessBoost) {\n const recallCount = entry.recallCount ?? 0;\n if (recallCount > 0) {\n s *= 1 + accessBoostStrength * Math.log(recallCount + 1);\n }\n }\n\n // 2. Time Decay: older, unused memories fade\n if (timeDecay) {\n const nowSec = Math.floor(Date.now() / 1000);\n const lastAccess = entry.lastAccessed ?? entry.lastConfirmedAt ?? entry.createdAt;\n const daysSinceAccess = (nowSec - lastAccess) / SECONDS_PER_DAY;\n // decay = 1 / (1 + days / halfLife) — reciprocal decay (not exponential)\n // At halfLife days: factor = 0.5\n const decayFactor = 1 / (1 + daysSinceAccess / halfLifeDays);\n s *= decayFactor;\n }\n\n return Math.min(1, Math.max(0, s));\n}\n"],"mappings":";;;AAYA,MAAM,gCAAgC;;;;;;;;;;AAWtC,SAAgB,uBACd,WACA,OACA,SAUQ;CACR,MAAM,EACJ,cAAc,MACd,YAAY,MACZ,eAAe,+BACf,sBAAsB,OACpB,WAAW,EAAE;CAEjB,IAAI,IAAI;CAGR,IAAI,aAAa;EACf,MAAM,cAAc,MAAM,eAAe;EACzC,IAAI,cAAc,GAChB,KAAK,IAAI,sBAAsB,KAAK,IAAI,cAAc,EAAE;;CAK5D,IAAI,WAAW;EAMb,MAAM,cAAc,KAAK,KALV,KAAK,MAAM,KAAK,KAAK,GAAG,IAER,IADZ,MAAM,gBAAgB,MAAM,mBAAmB,MAAM,cACxB,kBAGD;EAC/C,KAAK;;CAGP,OAAO,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,EAAE,CAAC"}
1
+ {"version":3,"file":"salience.js","names":[],"sources":["../../utils/salience.ts"],"sourcesContent":["/**\n * Dynamic Salience Scoring (Access-Based Importance)\n *\n * Combines:\n * 1. Access Boost — frequently recalled facts score higher\n * 2. Time Decay — older, unused memories fade in relevance\n */\n\nimport type { MemoryEntry } from \"../types/memory.js\";\nimport { SECONDS_PER_DAY } from \"./constants.js\";\n\n/** Default half-life in days: after this many days without access, salience halves. */\nconst SALIENCE_DECAY_HALF_LIFE_DAYS = 30;\n\n/**\n * Compute dynamic salience multiplier for a base score.\n * Combines access boost (recall_count) and time decay (last_accessed).\n *\n * @param baseScore — Original relevance score (0–1)\n * @param entry — Memory with recallCount, lastAccessed, importance\n * @param options — Optional overrides\n * @returns Adjusted score\n */\nexport function computeDynamicSalience(\n baseScore: number,\n entry: MemoryEntry,\n options?: {\n /** Apply access boost (default true) */\n accessBoost?: boolean;\n /** Apply time decay (default true) */\n timeDecay?: boolean;\n /** Half-life in days for decay (default 30) */\n halfLifeDays?: number;\n /** Access boost strength (default 0.1) */\n accessBoostStrength?: number;\n },\n): number {\n const {\n accessBoost = true,\n timeDecay = true,\n halfLifeDays = SALIENCE_DECAY_HALF_LIFE_DAYS,\n accessBoostStrength = 0.1,\n } = options ?? {};\n\n let s = baseScore;\n\n // 1. Access Boost: frequently recalled facts score higher\n if (accessBoost) {\n const recallCount = entry.recallCount ?? 0;\n if (recallCount > 0) {\n s *= 1 + accessBoostStrength * Math.log(recallCount + 1);\n }\n }\n\n // 2. Time Decay: older, unused memories fade\n if (timeDecay) {\n const nowSec = Math.floor(Date.now() / 1000);\n const lastAccess = entry.lastAccessed ?? entry.lastConfirmedAt ?? entry.createdAt;\n const daysSinceAccess = (nowSec - lastAccess) / SECONDS_PER_DAY;\n // decay = 1 / (1 + days / halfLife) — reciprocal decay (not exponential)\n // At halfLife days: factor = 0.5\n const decayFactor = 1 / (1 + daysSinceAccess / halfLifeDays);\n s *= decayFactor;\n }\n\n return Math.min(1, Math.max(0, s));\n}\n"],"mappings":";;;AAYA,MAAM,gCAAgC;;;;;;;;;;AAWtC,SAAgB,uBACd,WACA,OACA,SAUQ;CACR,MAAM,EACJ,cAAc,MACd,YAAY,MACZ,eAAe,+BACf,sBAAsB,OACpB,WAAW,CAAC;CAEhB,IAAI,IAAI;CAGR,IAAI,aAAa;EACf,MAAM,cAAc,MAAM,eAAe;EACzC,IAAI,cAAc,GAChB,KAAK,IAAI,sBAAsB,KAAK,IAAI,cAAc,CAAC;CAE3D;CAGA,IAAI,WAAW;EAMb,MAAM,cAAc,KAAK,KALV,KAAK,MAAM,KAAK,IAAI,IAAI,GAET,KADX,MAAM,gBAAgB,MAAM,mBAAmB,MAAM,cACxB,kBAGD;EAC/C,KAAK;CACP;CAEA,OAAO,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,CAAC,CAAC;AACnC"}
@@ -1 +1 @@
1
- {"version":3,"file":"sanitize-messages.js","names":[],"sources":["../../utils/sanitize-messages.ts"],"sourcesContent":["/**\n * Sanitize message arrays for Claude/Anthropic API.\n *\n * The API requires: each assistant message that contains `tool_use` blocks must be\n * immediately followed by a tool-role message with a `tool_result` block for every\n * tool_use id. When the conversation is trimmed (e.g. context window) or replayed\n * from logs, the follow-up tool_result messages can be dropped, causing:\n *\n * \"LLM request rejected: messages.N: `tool_use` ids were found without\n * `tool_result` blocks immediately after: id1, id2. Each `tool_use` block\n * must have a corresponding `tool_result` block in the next...\"\n *\n * This utility fixes that by inserting synthetic tool_result messages for any\n * orphan tool_use ids so the request can be sent successfully.\n */\n\nexport type MessageLike = {\n role: string;\n content?:\n | string\n | Array<{\n type?: string;\n id?: string;\n tool_use_id?: string;\n content?: unknown;\n text?: string;\n name?: string;\n input?: unknown;\n }>;\n};\n\nconst PLACEHOLDER_CONTENT = \"[Output omitted or truncated.]\";\n\n/**\n * Collect tool_use ids from an assistant message's content blocks.\n */\nfunction getToolUseIds(content: MessageLike[\"content\"]): string[] {\n if (!Array.isArray(content)) return [];\n const ids: string[] = [];\n for (const block of content) {\n if (block && typeof block === \"object\" && (block as { type?: string }).type === \"tool_use\") {\n const id = (block as { id?: string }).id;\n if (typeof id === \"string\" && id.trim()) ids.push(id.trim());\n }\n }\n return ids;\n}\n\n/**\n * Collect tool_use_ids from a tool message's content blocks.\n */\nfunction getToolResultIds(content: MessageLike[\"content\"]): Set<string> {\n if (!Array.isArray(content)) return new Set();\n const set = new Set<string>();\n for (const block of content) {\n if (\n block &&\n typeof block === \"object\" &&\n ((block as { type?: string }).type === \"tool_result\" || (block as { type?: string }).type === \"result\")\n ) {\n const tid = (block as { tool_use_id?: string }).tool_use_id;\n if (typeof tid === \"string\" && tid.trim()) set.add(tid.trim());\n }\n }\n return set;\n}\n\n/**\n * Ensure every assistant message that has tool_use blocks is immediately followed\n * by a tool message with a tool_result for each tool_use id. Inserts synthetic\n * tool messages with placeholder content for any missing tool_results.\n *\n * @param messages - Array of messages (role + content). Modified in place only by\n * inserting new elements; existing objects are not mutated.\n * @returns New array with inserted tool messages where needed (or same array if\n * no changes).\n */\nexport function sanitizeMessagesForClaude(messages: MessageLike[]): MessageLike[] {\n if (!Array.isArray(messages) || messages.length === 0) return messages;\n\n const out: MessageLike[] = [];\n let changed = false;\n\n for (let i = 0; i < messages.length; i++) {\n const msg = messages[i];\n if (!msg || typeof msg !== \"object\") {\n out.push(msg);\n continue;\n }\n\n const role = (msg as MessageLike).role;\n const content = (msg as MessageLike).content;\n\n out.push(msg);\n\n if (role !== \"assistant\") continue;\n\n const toolUseIds = getToolUseIds(content);\n if (toolUseIds.length === 0) continue;\n\n const next = messages[i + 1];\n const nextRole = next && typeof next === \"object\" ? (next as MessageLike).role : undefined;\n const nextContent = next && typeof next === \"object\" ? (next as MessageLike).content : undefined;\n const nextResultIds = nextRole === \"tool\" ? getToolResultIds(nextContent) : new Set<string>();\n\n const missing = toolUseIds.filter((id) => !nextResultIds.has(id));\n if (missing.length === 0) continue;\n\n const placeholderToolMessage: MessageLike = {\n role: \"tool\",\n content: missing.map((tool_use_id) => ({\n type: \"tool_result\",\n tool_use_id,\n content: PLACEHOLDER_CONTENT,\n })),\n };\n\n if (nextRole === \"tool\" && next && Array.isArray(nextContent)) {\n const existingBlocks = nextContent.slice() as Array<{ type?: string; tool_use_id?: string; content?: unknown }>;\n for (const id of missing) {\n existingBlocks.push({\n type: \"tool_result\",\n tool_use_id: id,\n content: PLACEHOLDER_CONTENT,\n });\n }\n out.push({\n ...next,\n content: existingBlocks,\n } as MessageLike);\n changed = true;\n i += 1;\n continue;\n }\n\n out.push(placeholderToolMessage);\n changed = true;\n }\n\n return changed ? out : messages;\n}\n\n/**\n * Strip OpenAI Responses API `reasoning` items from message content arrays before replay.\n *\n * After context trimming, a `reasoning` block (id prefix `rs_*`) can remain without its\n * required following `message` / `function_call` item, causing:\n * 400 Item 'rs_…' of type 'reasoning' was provided without its required following item.\n *\n * We strip from any role that carries array content (defensive). If an assistant turn\n * would become empty, insert a minimal text block so the request remains valid.\n */\nfunction isOpenAIResponsesReasoningBlock(block: unknown): boolean {\n if (!block || typeof block !== \"object\") return false;\n const b = block as Record<string, unknown>;\n if (b.type === \"reasoning\") return true;\n if (typeof b.id === \"string\" && b.id.startsWith(\"rs_\")) return true;\n return false;\n}\n\n/**\n * @returns New array if any reasoning blocks were removed; otherwise the original array.\n */\nexport function sanitizeMessagesForOpenAIResponses(messages: MessageLike[]): MessageLike[] {\n if (!Array.isArray(messages) || messages.length === 0) return messages;\n\n let changed = false;\n const out: MessageLike[] = [];\n\n for (const msg of messages) {\n if (!msg || typeof msg !== \"object\") {\n out.push(msg);\n continue;\n }\n const content = (msg as MessageLike).content;\n if (!Array.isArray(content)) {\n out.push(msg);\n continue;\n }\n const filtered = content.filter((block) => !isOpenAIResponsesReasoningBlock(block));\n if (filtered.length !== content.length) {\n changed = true;\n const role = (msg as MessageLike).role;\n if (role === \"assistant\" && filtered.length === 0) {\n out.push({\n ...(msg as object),\n content: [{ type: \"text\", text: \"(prior reasoning trace omitted)\" }],\n } as MessageLike);\n } else {\n out.push({ ...(msg as object), content: filtered } as MessageLike);\n }\n } else {\n out.push(msg);\n }\n }\n\n return changed ? out : messages;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"sanitize-messages.js","names":[],"sources":["../../utils/sanitize-messages.ts"],"sourcesContent":["/**\n * Sanitize message arrays for Claude/Anthropic API.\n *\n * The API requires: each assistant message that contains `tool_use` blocks must be\n * immediately followed by a tool-role message with a `tool_result` block for every\n * tool_use id. When the conversation is trimmed (e.g. context window) or replayed\n * from logs, the follow-up tool_result messages can be dropped, causing:\n *\n * \"LLM request rejected: messages.N: `tool_use` ids were found without\n * `tool_result` blocks immediately after: id1, id2. Each `tool_use` block\n * must have a corresponding `tool_result` block in the next...\"\n *\n * This utility fixes that by inserting synthetic tool_result messages for any\n * orphan tool_use ids so the request can be sent successfully.\n */\n\nexport type MessageLike = {\n role: string;\n content?:\n | string\n | Array<{\n type?: string;\n id?: string;\n tool_use_id?: string;\n content?: unknown;\n text?: string;\n name?: string;\n input?: unknown;\n }>;\n};\n\nconst PLACEHOLDER_CONTENT = \"[Output omitted or truncated.]\";\n\n/**\n * Collect tool_use ids from an assistant message's content blocks.\n */\nfunction getToolUseIds(content: MessageLike[\"content\"]): string[] {\n if (!Array.isArray(content)) return [];\n const ids: string[] = [];\n for (const block of content) {\n if (block && typeof block === \"object\" && (block as { type?: string }).type === \"tool_use\") {\n const id = (block as { id?: string }).id;\n if (typeof id === \"string\" && id.trim()) ids.push(id.trim());\n }\n }\n return ids;\n}\n\n/**\n * Collect tool_use_ids from a tool message's content blocks.\n */\nfunction getToolResultIds(content: MessageLike[\"content\"]): Set<string> {\n if (!Array.isArray(content)) return new Set();\n const set = new Set<string>();\n for (const block of content) {\n if (\n block &&\n typeof block === \"object\" &&\n ((block as { type?: string }).type === \"tool_result\" || (block as { type?: string }).type === \"result\")\n ) {\n const tid = (block as { tool_use_id?: string }).tool_use_id;\n if (typeof tid === \"string\" && tid.trim()) set.add(tid.trim());\n }\n }\n return set;\n}\n\n/**\n * Ensure every assistant message that has tool_use blocks is immediately followed\n * by a tool message with a tool_result for each tool_use id. Inserts synthetic\n * tool messages with placeholder content for any missing tool_results.\n *\n * @param messages - Array of messages (role + content). Modified in place only by\n * inserting new elements; existing objects are not mutated.\n * @returns New array with inserted tool messages where needed (or same array if\n * no changes).\n */\nexport function sanitizeMessagesForClaude(messages: MessageLike[]): MessageLike[] {\n if (!Array.isArray(messages) || messages.length === 0) return messages;\n\n const out: MessageLike[] = [];\n let changed = false;\n\n for (let i = 0; i < messages.length; i++) {\n const msg = messages[i];\n if (!msg || typeof msg !== \"object\") {\n out.push(msg);\n continue;\n }\n\n const role = (msg as MessageLike).role;\n const content = (msg as MessageLike).content;\n\n out.push(msg);\n\n if (role !== \"assistant\") continue;\n\n const toolUseIds = getToolUseIds(content);\n if (toolUseIds.length === 0) continue;\n\n const next = messages[i + 1];\n const nextRole = next && typeof next === \"object\" ? (next as MessageLike).role : undefined;\n const nextContent = next && typeof next === \"object\" ? (next as MessageLike).content : undefined;\n const nextResultIds = nextRole === \"tool\" ? getToolResultIds(nextContent) : new Set<string>();\n\n const missing = toolUseIds.filter((id) => !nextResultIds.has(id));\n if (missing.length === 0) continue;\n\n const placeholderToolMessage: MessageLike = {\n role: \"tool\",\n content: missing.map((tool_use_id) => ({\n type: \"tool_result\",\n tool_use_id,\n content: PLACEHOLDER_CONTENT,\n })),\n };\n\n if (nextRole === \"tool\" && next && Array.isArray(nextContent)) {\n const existingBlocks = nextContent.slice() as Array<{ type?: string; tool_use_id?: string; content?: unknown }>;\n for (const id of missing) {\n existingBlocks.push({\n type: \"tool_result\",\n tool_use_id: id,\n content: PLACEHOLDER_CONTENT,\n });\n }\n out.push({\n ...next,\n content: existingBlocks,\n } as MessageLike);\n changed = true;\n i += 1;\n continue;\n }\n\n out.push(placeholderToolMessage);\n changed = true;\n }\n\n return changed ? out : messages;\n}\n\n/**\n * Strip OpenAI Responses API `reasoning` items from message content arrays before replay.\n *\n * After context trimming, a `reasoning` block (id prefix `rs_*`) can remain without its\n * required following `message` / `function_call` item, causing:\n * 400 Item 'rs_…' of type 'reasoning' was provided without its required following item.\n *\n * We strip from any role that carries array content (defensive). If an assistant turn\n * would become empty, insert a minimal text block so the request remains valid.\n */\nfunction isOpenAIResponsesReasoningBlock(block: unknown): boolean {\n if (!block || typeof block !== \"object\") return false;\n const b = block as Record<string, unknown>;\n if (b.type === \"reasoning\") return true;\n if (typeof b.id === \"string\" && b.id.startsWith(\"rs_\")) return true;\n return false;\n}\n\n/**\n * @returns New array if any reasoning blocks were removed; otherwise the original array.\n */\nexport function sanitizeMessagesForOpenAIResponses(messages: MessageLike[]): MessageLike[] {\n if (!Array.isArray(messages) || messages.length === 0) return messages;\n\n let changed = false;\n const out: MessageLike[] = [];\n\n for (const msg of messages) {\n if (!msg || typeof msg !== \"object\") {\n out.push(msg);\n continue;\n }\n const content = (msg as MessageLike).content;\n if (!Array.isArray(content)) {\n out.push(msg);\n continue;\n }\n const filtered = content.filter((block) => !isOpenAIResponsesReasoningBlock(block));\n if (filtered.length !== content.length) {\n changed = true;\n const role = (msg as MessageLike).role;\n if (role === \"assistant\" && filtered.length === 0) {\n out.push({\n ...(msg as object),\n content: [{ type: \"text\", text: \"(prior reasoning trace omitted)\" }],\n } as MessageLike);\n } else {\n out.push({ ...(msg as object), content: filtered } as MessageLike);\n }\n } else {\n out.push(msg);\n }\n }\n\n return changed ? out : messages;\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"scope-filter.js","names":[],"sources":["../../utils/scope-filter.ts"],"sourcesContent":["/**\n * Helper to build scope filter for tool handlers (memory_recall, memory_recall_procedures).\n * Handles explicit parameters, agent-scoped filtering, and orchestrator fallback.\n *\n * ⚠️ SECURITY: By default, tool scope params (userId, agentId, sessionId) are IGNORED to prevent\n * cross-user memory access in multi-tenant setups. Set multiAgent.trustToolScopeParams=true to enable.\n */\n\nimport { addOperationBreadcrumb } from \"../services/error-reporter.js\";\nimport type { ScopeFilter } from \"../types/memory.js\";\n\nexport function buildToolScopeFilter(\n params: {\n userId?: string | null;\n agentId?: string | null;\n sessionId?: string | null;\n /** When multiAgent.trustToolScopeParams is true, must be true to apply caller scope (#874). */\n confirmCrossTenantScope?: boolean;\n },\n currentAgent: string | null,\n config: {\n multiAgent: { orchestratorId: string; trustToolScopeParams?: boolean };\n autoRecall: { scopeFilter?: ScopeFilter };\n },\n): ScopeFilter | undefined {\n const { userId, agentId, sessionId, confirmCrossTenantScope } = params;\n\n // Security: Only trust tool params if explicitly enabled in config\n const trustParams = config.multiAgent.trustToolScopeParams === true;\n const hasScopeParams = Boolean(userId || agentId || sessionId);\n if (hasScopeParams && trustParams && confirmCrossTenantScope) {\n return { userId: userId ?? null, agentId: agentId ?? null, sessionId: sessionId ?? null };\n }\n if (hasScopeParams && (!trustParams || !confirmCrossTenantScope)) {\n // Debug: Log when explicit scope params are ignored for security\n addOperationBreadcrumb(\"scope-filter\", \"params-ignored-security\");\n }\n\n if (currentAgent && currentAgent !== config.multiAgent.orchestratorId) {\n return {\n userId: config.autoRecall.scopeFilter?.userId ?? null,\n agentId: currentAgent,\n sessionId: config.autoRecall.scopeFilter?.sessionId ?? null,\n };\n }\n\n if (config.autoRecall.scopeFilter) {\n return config.autoRecall.scopeFilter;\n }\n return undefined;\n}\n"],"mappings":";;;;;;;;;AAWA,SAAgB,qBACd,QAOA,cACA,QAIyB;CACzB,MAAM,EAAE,QAAQ,SAAS,WAAW,4BAA4B;CAGhE,MAAM,cAAc,OAAO,WAAW,yBAAyB;CAC/D,MAAM,iBAAiB,QAAQ,UAAU,WAAW,UAAU;CAC9D,IAAI,kBAAkB,eAAe,yBACnC,OAAO;EAAE,QAAQ,UAAU;EAAM,SAAS,WAAW;EAAM,WAAW,aAAa;EAAM;CAE3F,IAAI,mBAAmB,CAAC,eAAe,CAAC,0BAEtC,uBAAuB,gBAAgB,0BAA0B;CAGnE,IAAI,gBAAgB,iBAAiB,OAAO,WAAW,gBACrD,OAAO;EACL,QAAQ,OAAO,WAAW,aAAa,UAAU;EACjD,SAAS;EACT,WAAW,OAAO,WAAW,aAAa,aAAa;EACxD;CAGH,IAAI,OAAO,WAAW,aACpB,OAAO,OAAO,WAAW"}
1
+ {"version":3,"file":"scope-filter.js","names":[],"sources":["../../utils/scope-filter.ts"],"sourcesContent":["/**\n * Helper to build scope filter for tool handlers (memory_recall, memory_recall_procedures).\n * Handles explicit parameters, agent-scoped filtering, and orchestrator fallback.\n *\n * ⚠️ SECURITY: By default, tool scope params (userId, agentId, sessionId) are IGNORED to prevent\n * cross-user memory access in multi-tenant setups. Set multiAgent.trustToolScopeParams=true to enable.\n */\n\nimport { addOperationBreadcrumb } from \"../services/error-reporter.js\";\nimport type { ScopeFilter } from \"../types/memory.js\";\n\nexport function buildToolScopeFilter(\n params: {\n userId?: string | null;\n agentId?: string | null;\n sessionId?: string | null;\n /** When multiAgent.trustToolScopeParams is true, must be true to apply caller scope (#874). */\n confirmCrossTenantScope?: boolean;\n },\n currentAgent: string | null,\n config: {\n multiAgent: { orchestratorId: string; trustToolScopeParams?: boolean };\n autoRecall: { scopeFilter?: ScopeFilter };\n },\n): ScopeFilter | undefined {\n const { userId, agentId, sessionId, confirmCrossTenantScope } = params;\n\n // Security: Only trust tool params if explicitly enabled in config\n const trustParams = config.multiAgent.trustToolScopeParams === true;\n const hasScopeParams = Boolean(userId || agentId || sessionId);\n if (hasScopeParams && trustParams && confirmCrossTenantScope) {\n return { userId: userId ?? null, agentId: agentId ?? null, sessionId: sessionId ?? null };\n }\n if (hasScopeParams && (!trustParams || !confirmCrossTenantScope)) {\n // Debug: Log when explicit scope params are ignored for security\n addOperationBreadcrumb(\"scope-filter\", \"params-ignored-security\");\n }\n\n if (currentAgent && currentAgent !== config.multiAgent.orchestratorId) {\n return {\n userId: config.autoRecall.scopeFilter?.userId ?? null,\n agentId: currentAgent,\n sessionId: config.autoRecall.scopeFilter?.sessionId ?? null,\n };\n }\n\n if (config.autoRecall.scopeFilter) {\n return config.autoRecall.scopeFilter;\n }\n return undefined;\n}\n"],"mappings":";;;;;;;;;AAWA,SAAgB,qBACd,QAOA,cACA,QAIyB;CACzB,MAAM,EAAE,QAAQ,SAAS,WAAW,4BAA4B;CAGhE,MAAM,cAAc,OAAO,WAAW,yBAAyB;CAC/D,MAAM,iBAAiB,QAAQ,UAAU,WAAW,SAAS;CAC7D,IAAI,kBAAkB,eAAe,yBACnC,OAAO;EAAE,QAAQ,UAAU;EAAM,SAAS,WAAW;EAAM,WAAW,aAAa;CAAK;CAE1F,IAAI,mBAAmB,CAAC,eAAe,CAAC,0BAEtC,uBAAuB,gBAAgB,yBAAyB;CAGlE,IAAI,gBAAgB,iBAAiB,OAAO,WAAW,gBACrD,OAAO;EACL,QAAQ,OAAO,WAAW,aAAa,UAAU;EACjD,SAAS;EACT,WAAW,OAAO,WAAW,aAAa,aAAa;CACzD;CAGF,IAAI,OAAO,WAAW,aACpB,OAAO,OAAO,WAAW;AAG7B"}
@@ -1 +1 @@
1
- {"version":3,"file":"skill-discovery.js","names":[],"sources":["../../utils/skill-discovery.ts"],"sourcesContent":["import { existsSync, readdirSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { SKILL_ATOMIC_TEMP_PREFIX, isSkillDirComplete } from \"./atomic-write.js\";\n\nexport interface DiscoveredSkillDir {\n /** Directory name under the scanned skills root. */\n name: string;\n /** Absolute path to the completed skill directory. */\n dirPath: string;\n /** Absolute path to the directory's SKILL.md file. */\n skillPath: string;\n}\n\n/**\n * Returns true for hidden scratch directories left behind by interrupted\n * atomic skill writes. These are never valid loader candidates.\n */\nexport function isAtomicSkillWriteScratchDir(name: string): boolean {\n return name.startsWith(SKILL_ATOMIC_TEMP_PREFIX) || /^\\..+\\.bak-/.test(name);\n}\n\n/**\n * Discover skill directories that are safe for loader-style consumption.\n *\n * Auto-generated skill directories must contain `.openclaw-skill-complete`;\n * markerless directories are treated as in-progress writes and skipped even if\n * they already contain `SKILL.md`. Legacy compatibility belongs in migration or\n * duplicate-detection paths, not in active loader discovery.\n */\nexport function discoverCompletedSkillDirs(skillsDir: string): DiscoveredSkillDir[] {\n const out: DiscoveredSkillDir[] = [];\n if (!existsSync(skillsDir)) return out;\n\n for (const entry of safeReadSkillDir(skillsDir)) {\n if (isAtomicSkillWriteScratchDir(entry.name)) continue;\n if (!entry.isDirectory) continue;\n\n const dirPath = join(skillsDir, entry.name);\n if (!isSkillDirComplete(dirPath)) continue;\n\n const skillPath = join(dirPath, \"SKILL.md\");\n if (!existsSync(skillPath)) continue;\n\n out.push({ name: entry.name, dirPath, skillPath });\n }\n\n return out;\n}\n\nfunction safeReadSkillDir(dir: string): Array<{ name: string; isDirectory: boolean }> {\n try {\n return readdirSync(dir, { withFileTypes: true }).map((entry) => ({\n name: entry.name,\n isDirectory: entry.isDirectory(),\n }));\n } catch {\n return [];\n }\n}\n"],"mappings":";;;;;;;;AAiBA,SAAgB,6BAA6B,MAAuB;CAClE,OAAO,KAAK,WAAA,uBAAoC,IAAI,cAAc,KAAK,KAAK;;;;;;;;;;AAW9E,SAAgB,2BAA2B,WAAyC;CAClF,MAAM,MAA4B,EAAE;CACpC,IAAI,CAAC,WAAW,UAAU,EAAE,OAAO;CAEnC,KAAK,MAAM,SAAS,iBAAiB,UAAU,EAAE;EAC/C,IAAI,6BAA6B,MAAM,KAAK,EAAE;EAC9C,IAAI,CAAC,MAAM,aAAa;EAExB,MAAM,UAAU,KAAK,WAAW,MAAM,KAAK;EAC3C,IAAI,CAAC,mBAAmB,QAAQ,EAAE;EAElC,MAAM,YAAY,KAAK,SAAS,WAAW;EAC3C,IAAI,CAAC,WAAW,UAAU,EAAE;EAE5B,IAAI,KAAK;GAAE,MAAM,MAAM;GAAM;GAAS;GAAW,CAAC;;CAGpD,OAAO;;AAGT,SAAS,iBAAiB,KAA4D;CACpF,IAAI;EACF,OAAO,YAAY,KAAK,EAAE,eAAe,MAAM,CAAC,CAAC,KAAK,WAAW;GAC/D,MAAM,MAAM;GACZ,aAAa,MAAM,aAAa;GACjC,EAAE;SACG;EACN,OAAO,EAAE"}
1
+ {"version":3,"file":"skill-discovery.js","names":[],"sources":["../../utils/skill-discovery.ts"],"sourcesContent":["import { existsSync, readdirSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { SKILL_ATOMIC_TEMP_PREFIX, isSkillDirComplete } from \"./atomic-write.js\";\n\nexport interface DiscoveredSkillDir {\n /** Directory name under the scanned skills root. */\n name: string;\n /** Absolute path to the completed skill directory. */\n dirPath: string;\n /** Absolute path to the directory's SKILL.md file. */\n skillPath: string;\n}\n\n/**\n * Returns true for hidden scratch directories left behind by interrupted\n * atomic skill writes. These are never valid loader candidates.\n */\nexport function isAtomicSkillWriteScratchDir(name: string): boolean {\n return name.startsWith(SKILL_ATOMIC_TEMP_PREFIX) || /^\\..+\\.bak-/.test(name);\n}\n\n/**\n * Discover skill directories that are safe for loader-style consumption.\n *\n * Auto-generated skill directories must contain `.openclaw-skill-complete`;\n * markerless directories are treated as in-progress writes and skipped even if\n * they already contain `SKILL.md`. Legacy compatibility belongs in migration or\n * duplicate-detection paths, not in active loader discovery.\n */\nexport function discoverCompletedSkillDirs(skillsDir: string): DiscoveredSkillDir[] {\n const out: DiscoveredSkillDir[] = [];\n if (!existsSync(skillsDir)) return out;\n\n for (const entry of safeReadSkillDir(skillsDir)) {\n if (isAtomicSkillWriteScratchDir(entry.name)) continue;\n if (!entry.isDirectory) continue;\n\n const dirPath = join(skillsDir, entry.name);\n if (!isSkillDirComplete(dirPath)) continue;\n\n const skillPath = join(dirPath, \"SKILL.md\");\n if (!existsSync(skillPath)) continue;\n\n out.push({ name: entry.name, dirPath, skillPath });\n }\n\n return out;\n}\n\nfunction safeReadSkillDir(dir: string): Array<{ name: string; isDirectory: boolean }> {\n try {\n return readdirSync(dir, { withFileTypes: true }).map((entry) => ({\n name: entry.name,\n isDirectory: entry.isDirectory(),\n }));\n } catch {\n return [];\n }\n}\n"],"mappings":";;;;;;;;AAiBA,SAAgB,6BAA6B,MAAuB;CAClE,OAAO,KAAK,WAAA,sBAAmC,KAAK,cAAc,KAAK,IAAI;AAC7E;;;;;;;;;AAUA,SAAgB,2BAA2B,WAAyC;CAClF,MAAM,MAA4B,CAAC;CACnC,IAAI,CAAC,WAAW,SAAS,GAAG,OAAO;CAEnC,KAAK,MAAM,SAAS,iBAAiB,SAAS,GAAG;EAC/C,IAAI,6BAA6B,MAAM,IAAI,GAAG;EAC9C,IAAI,CAAC,MAAM,aAAa;EAExB,MAAM,UAAU,KAAK,WAAW,MAAM,IAAI;EAC1C,IAAI,CAAC,mBAAmB,OAAO,GAAG;EAElC,MAAM,YAAY,KAAK,SAAS,UAAU;EAC1C,IAAI,CAAC,WAAW,SAAS,GAAG;EAE5B,IAAI,KAAK;GAAE,MAAM,MAAM;GAAM;GAAS;EAAU,CAAC;CACnD;CAEA,OAAO;AACT;AAEA,SAAS,iBAAiB,KAA4D;CACpF,IAAI;EACF,OAAO,YAAY,KAAK,EAAE,eAAe,KAAK,CAAC,EAAE,KAAK,WAAW;GAC/D,MAAM,MAAM;GACZ,aAAa,MAAM,YAAY;EACjC,EAAE;CACJ,QAAQ;EACN,OAAO,CAAC;CACV;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"sqlite-file-perms.js","names":[],"sources":["../../utils/sqlite-file-perms.ts"],"sourcesContent":["/**\n * Restrict SQLite database file permissions (owner read/write only).\n * Issue #865: document OS-level protection; chmod best-effort after open.\n */\n\nimport { chmodSync, existsSync } from \"node:fs\";\n\nconst MODE_0600 = 0o600;\n\nexport function tryRestrictSqliteDbFileMode(dbPath: string): void {\n if (dbPath === \":memory:\") return;\n const candidates = [dbPath, `${dbPath}-wal`, `${dbPath}-shm`, `${dbPath}-journal`];\n for (const p of candidates) {\n try {\n if (existsSync(p)) {\n chmodSync(p, MODE_0600);\n }\n } catch {\n // Non-fatal: some environments restrict chmod (e.g. exotic mounts).\n }\n }\n}\n"],"mappings":";;;;;;AAOA,MAAM,YAAY;AAElB,SAAgB,4BAA4B,QAAsB;CAChE,IAAI,WAAW,YAAY;CAC3B,MAAM,aAAa;EAAC;EAAQ,GAAG,OAAO;EAAO,GAAG,OAAO;EAAO,GAAG,OAAO;EAAU;CAClF,KAAK,MAAM,KAAK,YACd,IAAI;EACF,IAAI,WAAW,EAAE,EACf,UAAU,GAAG,UAAU;SAEnB"}
1
+ {"version":3,"file":"sqlite-file-perms.js","names":[],"sources":["../../utils/sqlite-file-perms.ts"],"sourcesContent":["/**\n * Restrict SQLite database file permissions (owner read/write only).\n * Issue #865: document OS-level protection; chmod best-effort after open.\n */\n\nimport { chmodSync, existsSync } from \"node:fs\";\n\nconst MODE_0600 = 0o600;\n\nexport function tryRestrictSqliteDbFileMode(dbPath: string): void {\n if (dbPath === \":memory:\") return;\n const candidates = [dbPath, `${dbPath}-wal`, `${dbPath}-shm`, `${dbPath}-journal`];\n for (const p of candidates) {\n try {\n if (existsSync(p)) {\n chmodSync(p, MODE_0600);\n }\n } catch {\n // Non-fatal: some environments restrict chmod (e.g. exotic mounts).\n }\n }\n}\n"],"mappings":";;;;;;AAOA,MAAM,YAAY;AAElB,SAAgB,4BAA4B,QAAsB;CAChE,IAAI,WAAW,YAAY;CAC3B,MAAM,aAAa;EAAC;EAAQ,GAAG,OAAO;EAAO,GAAG,OAAO;EAAO,GAAG,OAAO;CAAS;CACjF,KAAK,MAAM,KAAK,YACd,IAAI;EACF,IAAI,WAAW,CAAC,GACd,UAAU,GAAG,SAAS;CAE1B,QAAQ,CAER;AAEJ"}
@@ -1 +1 @@
1
- {"version":3,"file":"sqlite-outcome-compat.js","names":[],"sources":["../../utils/sqlite-outcome-compat.ts"],"sourcesContent":["/**\n * SQLite CHECK drift vs application enums (legacy `CREATE TABLE IF NOT EXISTS`).\n *\n * Older `episodes` tables may CHECK only `('success','partial','failed')` while\n * current code inserts `failure` / `unknown`. Older `audit_log` may omit\n * `skipped` while new paths append `skipped`. `CREATE TABLE IF NOT EXISTS`\n * never upgrades CHECK constraints — inserts fail at runtime.\n *\n * Strategy: read `sqlite_master.sql` once per DB (WeakMap cache), normalize\n * values at insert time. Reads map legacy stored values back to public types\n * where needed (`failed` → `failure` for episodes).\n */\n\nimport type { DatabaseSync } from \"node:sqlite\";\nimport type { EpisodeOutcome } from \"../types/memory.js\";\n\nconst episodesLegacyFailedOnlyCache = new WeakMap<DatabaseSync, boolean>();\n\nfunction tableCreateSql(db: DatabaseSync, name: string): string | undefined {\n const row = db.prepare(\"SELECT sql FROM sqlite_master WHERE type = 'table' AND name = ?\").get(name) as\n | { sql?: string }\n | undefined;\n return typeof row?.sql === \"string\" ? row.sql : undefined;\n}\n\n/**\n * True when `episodes` exists with legacy CHECK using `failed` but not `failure`\n * (current shipped DDL uses `failure` and `unknown`).\n */\nexport function episodesTableIsLegacyFailedOutcomeCheck(db: DatabaseSync): boolean {\n let cached = episodesLegacyFailedOnlyCache.get(db);\n if (cached !== undefined) return cached;\n const sql = tableCreateSql(db, \"episodes\") ?? \"\";\n if (sql.length === 0) {\n episodesLegacyFailedOnlyCache.set(db, false);\n return false;\n }\n const hasFailed = sql.includes(\"'failed'\");\n const hasFailure = sql.includes(\"'failure'\");\n cached = hasFailed && !hasFailure;\n episodesLegacyFailedOnlyCache.set(db, cached);\n return cached;\n}\n\n/**\n * Outcome string bound on INSERT — maps app enums to values accepted by the\n * live table CHECK.\n */\nexport function episodeOutcomeForSqliteInsert(db: DatabaseSync, outcome: EpisodeOutcome): string {\n if (!episodesTableIsLegacyFailedOutcomeCheck(db)) {\n return outcome;\n }\n if (outcome === \"failure\" || outcome === \"unknown\") {\n return \"failed\";\n }\n return outcome;\n}\n\n/**\n * Map a stored `episodes.outcome` row value to the public {@link EpisodeOutcome}.\n * Legacy rows may still contain `failed` from older writers.\n */\nexport function storedEpisodeOutcomeToPublic(stored: string): EpisodeOutcome {\n if (stored === \"failed\") {\n return \"failure\";\n }\n if (stored === \"success\" || stored === \"partial\" || stored === \"failure\" || stored === \"unknown\") {\n return stored;\n }\n return \"unknown\";\n}\n\n/** Test-only: clear cached legacy detection for a db handle. */\nexport function _resetEpisodesOutcomeCompatCacheForTests(db: DatabaseSync): void {\n episodesLegacyFailedOnlyCache.delete(db);\n}\n"],"mappings":";AAgBA,MAAM,gDAAgC,IAAI,SAAgC;AAE1E,SAAS,eAAe,IAAkB,MAAkC;CAC1E,MAAM,MAAM,GAAG,QAAQ,kEAAkE,CAAC,IAAI,KAAK;CAGnG,OAAO,OAAO,KAAK,QAAQ,WAAW,IAAI,MAAM,KAAA;;;;;;AAOlD,SAAgB,wCAAwC,IAA2B;CACjF,IAAI,SAAS,8BAA8B,IAAI,GAAG;CAClD,IAAI,WAAW,KAAA,GAAW,OAAO;CACjC,MAAM,MAAM,eAAe,IAAI,WAAW,IAAI;CAC9C,IAAI,IAAI,WAAW,GAAG;EACpB,8BAA8B,IAAI,IAAI,MAAM;EAC5C,OAAO;;CAET,MAAM,YAAY,IAAI,SAAS,WAAW;CAC1C,MAAM,aAAa,IAAI,SAAS,YAAY;CAC5C,SAAS,aAAa,CAAC;CACvB,8BAA8B,IAAI,IAAI,OAAO;CAC7C,OAAO;;;;;;AAOT,SAAgB,8BAA8B,IAAkB,SAAiC;CAC/F,IAAI,CAAC,wCAAwC,GAAG,EAC9C,OAAO;CAET,IAAI,YAAY,aAAa,YAAY,WACvC,OAAO;CAET,OAAO;;;;;;AAOT,SAAgB,6BAA6B,QAAgC;CAC3E,IAAI,WAAW,UACb,OAAO;CAET,IAAI,WAAW,aAAa,WAAW,aAAa,WAAW,aAAa,WAAW,WACrF,OAAO;CAET,OAAO"}
1
+ {"version":3,"file":"sqlite-outcome-compat.js","names":[],"sources":["../../utils/sqlite-outcome-compat.ts"],"sourcesContent":["/**\n * SQLite CHECK drift vs application enums (legacy `CREATE TABLE IF NOT EXISTS`).\n *\n * Older `episodes` tables may CHECK only `('success','partial','failed')` while\n * current code inserts `failure` / `unknown`. Older `audit_log` may omit\n * `skipped` while new paths append `skipped`. `CREATE TABLE IF NOT EXISTS`\n * never upgrades CHECK constraints — inserts fail at runtime.\n *\n * Strategy: read `sqlite_master.sql` once per DB (WeakMap cache), normalize\n * values at insert time. Reads map legacy stored values back to public types\n * where needed (`failed` → `failure` for episodes).\n */\n\nimport type { DatabaseSync } from \"node:sqlite\";\nimport type { EpisodeOutcome } from \"../types/memory.js\";\n\nconst episodesLegacyFailedOnlyCache = new WeakMap<DatabaseSync, boolean>();\n\nfunction tableCreateSql(db: DatabaseSync, name: string): string | undefined {\n const row = db.prepare(\"SELECT sql FROM sqlite_master WHERE type = 'table' AND name = ?\").get(name) as\n | { sql?: string }\n | undefined;\n return typeof row?.sql === \"string\" ? row.sql : undefined;\n}\n\n/**\n * True when `episodes` exists with legacy CHECK using `failed` but not `failure`\n * (current shipped DDL uses `failure` and `unknown`).\n */\nexport function episodesTableIsLegacyFailedOutcomeCheck(db: DatabaseSync): boolean {\n let cached = episodesLegacyFailedOnlyCache.get(db);\n if (cached !== undefined) return cached;\n const sql = tableCreateSql(db, \"episodes\") ?? \"\";\n if (sql.length === 0) {\n episodesLegacyFailedOnlyCache.set(db, false);\n return false;\n }\n const hasFailed = sql.includes(\"'failed'\");\n const hasFailure = sql.includes(\"'failure'\");\n cached = hasFailed && !hasFailure;\n episodesLegacyFailedOnlyCache.set(db, cached);\n return cached;\n}\n\n/**\n * Outcome string bound on INSERT — maps app enums to values accepted by the\n * live table CHECK.\n */\nexport function episodeOutcomeForSqliteInsert(db: DatabaseSync, outcome: EpisodeOutcome): string {\n if (!episodesTableIsLegacyFailedOutcomeCheck(db)) {\n return outcome;\n }\n if (outcome === \"failure\" || outcome === \"unknown\") {\n return \"failed\";\n }\n return outcome;\n}\n\n/**\n * Map a stored `episodes.outcome` row value to the public {@link EpisodeOutcome}.\n * Legacy rows may still contain `failed` from older writers.\n */\nexport function storedEpisodeOutcomeToPublic(stored: string): EpisodeOutcome {\n if (stored === \"failed\") {\n return \"failure\";\n }\n if (stored === \"success\" || stored === \"partial\" || stored === \"failure\" || stored === \"unknown\") {\n return stored;\n }\n return \"unknown\";\n}\n\n/** Test-only: clear cached legacy detection for a db handle. */\nexport function _resetEpisodesOutcomeCompatCacheForTests(db: DatabaseSync): void {\n episodesLegacyFailedOnlyCache.delete(db);\n}\n"],"mappings":";AAgBA,MAAM,gDAAgC,IAAI,QAA+B;AAEzE,SAAS,eAAe,IAAkB,MAAkC;CAC1E,MAAM,MAAM,GAAG,QAAQ,iEAAiE,EAAE,IAAI,IAAI;CAGlG,OAAO,OAAO,KAAK,QAAQ,WAAW,IAAI,MAAM,KAAA;AAClD;;;;;AAMA,SAAgB,wCAAwC,IAA2B;CACjF,IAAI,SAAS,8BAA8B,IAAI,EAAE;CACjD,IAAI,WAAW,KAAA,GAAW,OAAO;CACjC,MAAM,MAAM,eAAe,IAAI,UAAU,KAAK;CAC9C,IAAI,IAAI,WAAW,GAAG;EACpB,8BAA8B,IAAI,IAAI,KAAK;EAC3C,OAAO;CACT;CACA,MAAM,YAAY,IAAI,SAAS,UAAU;CACzC,MAAM,aAAa,IAAI,SAAS,WAAW;CAC3C,SAAS,aAAa,CAAC;CACvB,8BAA8B,IAAI,IAAI,MAAM;CAC5C,OAAO;AACT;;;;;AAMA,SAAgB,8BAA8B,IAAkB,SAAiC;CAC/F,IAAI,CAAC,wCAAwC,EAAE,GAC7C,OAAO;CAET,IAAI,YAAY,aAAa,YAAY,WACvC,OAAO;CAET,OAAO;AACT;;;;;AAMA,SAAgB,6BAA6B,QAAgC;CAC3E,IAAI,WAAW,UACb,OAAO;CAET,IAAI,WAAW,aAAa,WAAW,aAAa,WAAW,aAAa,WAAW,WACrF,OAAO;CAET,OAAO;AACT"}
@@ -1 +1 @@
1
- {"version":3,"file":"sqlite-transaction.js","names":[],"sources":["../../utils/sqlite-transaction.ts"],"sourcesContent":["/**\n * Transaction helper for node:sqlite (DatabaseSync).\n *\n * node:sqlite does not ship a `.transaction()` helper like better-sqlite3 does.\n * This module provides `createTransaction`, a drop-in functional replacement.\n *\n * Usage:\n * const tx = createTransaction(db, (arg: T) => { ... });\n * tx(arg); // executes inside BEGIN / COMMIT, ROLLBACK on error\n */\n\nimport type { DatabaseSync } from \"node:sqlite\";\n\n/** SQLite lock acquisition: IMMEDIATE reserves a write lock at BEGIN (serializes quota + insert). */\nexport type SqliteTransactionBeginMode = \"DEFERRED\" | \"IMMEDIATE\";\n\n/**\n * Wraps a function in a SQLite BEGIN / COMMIT / ROLLBACK block.\n * Mirrors the semantics of better-sqlite3's `db.transaction(fn)` helper.\n *\n * Supports nested transactions via SAVEPOINTs: if a transaction is already\n * active when this function is called, it uses SAVEPOINT/RELEASE/ROLLBACK TO\n * instead of BEGIN/COMMIT/ROLLBACK.\n *\n * @param db An open DatabaseSync instance.\n * @param fn The function to execute inside the transaction.\n * @returns A new function with the same signature that, when called, runs\n * `fn` inside a transaction and returns its return value.\n */\n\nlet savepointCounter = 0;\n\nexport function createTransaction<T extends unknown[], R>(\n db: DatabaseSync,\n fn: (...args: T) => R,\n beginMode: SqliteTransactionBeginMode = \"DEFERRED\",\n): (...args: T) => R {\n return (...args: T): R => {\n const isNested = db.isTransaction;\n const savepointName = isNested ? `sp_${++savepointCounter}` : null;\n\n if (isNested) {\n db.exec(`SAVEPOINT ${savepointName}`);\n } else {\n db.exec(beginMode === \"IMMEDIATE\" ? \"BEGIN IMMEDIATE\" : \"BEGIN\");\n }\n\n try {\n const result = fn(...args);\n if (isNested) {\n db.exec(`RELEASE ${savepointName}`);\n } else {\n db.exec(\"COMMIT\");\n }\n return result;\n } catch (err) {\n try {\n if (isNested) {\n db.exec(`ROLLBACK TO ${savepointName}`);\n db.exec(`RELEASE ${savepointName}`);\n } else {\n db.exec(\"ROLLBACK\");\n }\n } catch {\n // ignore rollback errors — connection may already be broken\n }\n throw err;\n }\n };\n}\n"],"mappings":";;;;;;;;;;;;;;AA8BA,IAAI,mBAAmB;AAEvB,SAAgB,kBACd,IACA,IACA,YAAwC,YACrB;CACnB,QAAQ,GAAG,SAAe;EACxB,MAAM,WAAW,GAAG;EACpB,MAAM,gBAAgB,WAAW,MAAM,EAAE,qBAAqB;EAE9D,IAAI,UACF,GAAG,KAAK,aAAa,gBAAgB;OAErC,GAAG,KAAK,cAAc,cAAc,oBAAoB,QAAQ;EAGlE,IAAI;GACF,MAAM,SAAS,GAAG,GAAG,KAAK;GAC1B,IAAI,UACF,GAAG,KAAK,WAAW,gBAAgB;QAEnC,GAAG,KAAK,SAAS;GAEnB,OAAO;WACA,KAAK;GACZ,IAAI;IACF,IAAI,UAAU;KACZ,GAAG,KAAK,eAAe,gBAAgB;KACvC,GAAG,KAAK,WAAW,gBAAgB;WAEnC,GAAG,KAAK,WAAW;WAEf;GAGR,MAAM"}
1
+ {"version":3,"file":"sqlite-transaction.js","names":[],"sources":["../../utils/sqlite-transaction.ts"],"sourcesContent":["/**\n * Transaction helper for node:sqlite (DatabaseSync).\n *\n * node:sqlite does not ship a `.transaction()` helper like better-sqlite3 does.\n * This module provides `createTransaction`, a drop-in functional replacement.\n *\n * Usage:\n * const tx = createTransaction(db, (arg: T) => { ... });\n * tx(arg); // executes inside BEGIN / COMMIT, ROLLBACK on error\n */\n\nimport type { DatabaseSync } from \"node:sqlite\";\n\n/** SQLite lock acquisition: IMMEDIATE reserves a write lock at BEGIN (serializes quota + insert). */\nexport type SqliteTransactionBeginMode = \"DEFERRED\" | \"IMMEDIATE\";\n\n/**\n * Wraps a function in a SQLite BEGIN / COMMIT / ROLLBACK block.\n * Mirrors the semantics of better-sqlite3's `db.transaction(fn)` helper.\n *\n * Supports nested transactions via SAVEPOINTs: if a transaction is already\n * active when this function is called, it uses SAVEPOINT/RELEASE/ROLLBACK TO\n * instead of BEGIN/COMMIT/ROLLBACK.\n *\n * @param db An open DatabaseSync instance.\n * @param fn The function to execute inside the transaction.\n * @returns A new function with the same signature that, when called, runs\n * `fn` inside a transaction and returns its return value.\n */\n\nlet savepointCounter = 0;\n\nexport function createTransaction<T extends unknown[], R>(\n db: DatabaseSync,\n fn: (...args: T) => R,\n beginMode: SqliteTransactionBeginMode = \"DEFERRED\",\n): (...args: T) => R {\n return (...args: T): R => {\n const isNested = db.isTransaction;\n const savepointName = isNested ? `sp_${++savepointCounter}` : null;\n\n if (isNested) {\n db.exec(`SAVEPOINT ${savepointName}`);\n } else {\n db.exec(beginMode === \"IMMEDIATE\" ? \"BEGIN IMMEDIATE\" : \"BEGIN\");\n }\n\n try {\n const result = fn(...args);\n if (isNested) {\n db.exec(`RELEASE ${savepointName}`);\n } else {\n db.exec(\"COMMIT\");\n }\n return result;\n } catch (err) {\n try {\n if (isNested) {\n db.exec(`ROLLBACK TO ${savepointName}`);\n db.exec(`RELEASE ${savepointName}`);\n } else {\n db.exec(\"ROLLBACK\");\n }\n } catch {\n // ignore rollback errors — connection may already be broken\n }\n throw err;\n }\n };\n}\n"],"mappings":";;;;;;;;;;;;;;AA8BA,IAAI,mBAAmB;AAEvB,SAAgB,kBACd,IACA,IACA,YAAwC,YACrB;CACnB,QAAQ,GAAG,SAAe;EACxB,MAAM,WAAW,GAAG;EACpB,MAAM,gBAAgB,WAAW,MAAM,EAAE,qBAAqB;EAE9D,IAAI,UACF,GAAG,KAAK,aAAa,eAAe;OAEpC,GAAG,KAAK,cAAc,cAAc,oBAAoB,OAAO;EAGjE,IAAI;GACF,MAAM,SAAS,GAAG,GAAG,IAAI;GACzB,IAAI,UACF,GAAG,KAAK,WAAW,eAAe;QAElC,GAAG,KAAK,QAAQ;GAElB,OAAO;EACT,SAAS,KAAK;GACZ,IAAI;IACF,IAAI,UAAU;KACZ,GAAG,KAAK,eAAe,eAAe;KACtC,GAAG,KAAK,WAAW,eAAe;IACpC,OACE,GAAG,KAAK,UAAU;GAEtB,QAAQ,CAER;GACA,MAAM;EACR;CACF;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"stable-stringify.js","names":[],"sources":["../../utils/stable-stringify.ts"],"sourcesContent":["/**\n * Deterministic JSON serialization with sorted object keys.\n *\n * Produces stable, order-independent fingerprints for deduplication and caching.\n * Arrays preserve order, objects are sorted alphabetically by key.\n */\n\n/**\n * Stringify a value with sorted object keys for stable, order-independent serialization.\n *\n * @param value - Any JSON-serializable value (object, array, primitive, null, undefined).\n * @returns Deterministic JSON string with sorted object keys.\n */\nexport function stableStringify(value: unknown): string {\n if (Array.isArray(value)) {\n return `[${value.map((item) => stableStringify(item)).join(\",\")}]`;\n }\n if (value && typeof value === \"object\") {\n const entries = Object.entries(value as Record<string, unknown>).sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));\n return `{${entries.map(([key, item]) => `${JSON.stringify(key)}:${stableStringify(item)}`).join(\",\")}}`;\n }\n const serialized = JSON.stringify(value);\n return serialized === undefined ? \"null\" : serialized;\n}\n"],"mappings":";;;;;;;;;;;;;AAaA,SAAgB,gBAAgB,OAAwB;CACtD,IAAI,MAAM,QAAQ,MAAM,EACtB,OAAO,IAAI,MAAM,KAAK,SAAS,gBAAgB,KAAK,CAAC,CAAC,KAAK,IAAI,CAAC;CAElE,IAAI,SAAS,OAAO,UAAU,UAE5B,OAAO,IADS,OAAO,QAAQ,MAAiC,CAAC,MAAM,CAAC,IAAI,CAAC,OAAQ,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,EAC5F,CAAC,KAAK,CAAC,KAAK,UAAU,GAAG,KAAK,UAAU,IAAI,CAAC,GAAG,gBAAgB,KAAK,GAAG,CAAC,KAAK,IAAI,CAAC;CAEvG,MAAM,aAAa,KAAK,UAAU,MAAM;CACxC,OAAO,eAAe,KAAA,IAAY,SAAS"}
1
+ {"version":3,"file":"stable-stringify.js","names":[],"sources":["../../utils/stable-stringify.ts"],"sourcesContent":["/**\n * Deterministic JSON serialization with sorted object keys.\n *\n * Produces stable, order-independent fingerprints for deduplication and caching.\n * Arrays preserve order, objects are sorted alphabetically by key.\n */\n\n/**\n * Stringify a value with sorted object keys for stable, order-independent serialization.\n *\n * @param value - Any JSON-serializable value (object, array, primitive, null, undefined).\n * @returns Deterministic JSON string with sorted object keys.\n */\nexport function stableStringify(value: unknown): string {\n if (Array.isArray(value)) {\n return `[${value.map((item) => stableStringify(item)).join(\",\")}]`;\n }\n if (value && typeof value === \"object\") {\n const entries = Object.entries(value as Record<string, unknown>).sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));\n return `{${entries.map(([key, item]) => `${JSON.stringify(key)}:${stableStringify(item)}`).join(\",\")}}`;\n }\n const serialized = JSON.stringify(value);\n return serialized === undefined ? \"null\" : serialized;\n}\n"],"mappings":";;;;;;;;;;;;;AAaA,SAAgB,gBAAgB,OAAwB;CACtD,IAAI,MAAM,QAAQ,KAAK,GACrB,OAAO,IAAI,MAAM,KAAK,SAAS,gBAAgB,IAAI,CAAC,EAAE,KAAK,GAAG,EAAE;CAElE,IAAI,SAAS,OAAO,UAAU,UAE5B,OAAO,IADS,OAAO,QAAQ,KAAgC,EAAE,MAAM,CAAC,IAAI,CAAC,OAAQ,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,CAC7F,EAAE,KAAK,CAAC,KAAK,UAAU,GAAG,KAAK,UAAU,GAAG,EAAE,GAAG,gBAAgB,IAAI,GAAG,EAAE,KAAK,GAAG,EAAE;CAEvG,MAAM,aAAa,KAAK,UAAU,KAAK;CACvC,OAAO,eAAe,KAAA,IAAY,SAAS;AAC7C"}
@@ -1 +1 @@
1
- {"version":3,"file":"subagent-ended-utils.js","names":[],"sources":["../../utils/subagent-ended-utils.ts"],"sourcesContent":["/**\n * Helpers for OpenClaw **subagent_ended** payloads (issue #966).\n * Core passes targetSessionKey, outcome, error, etc.; shapes vary slightly by version.\n */\n\n/** OpenClaw core dispatch shapes — see runSubagentEnded */\nexport type SubagentEndedEvent = {\n targetSessionKey?: string;\n sessionKey?: string;\n label?: string;\n success?: boolean;\n outcome?: string;\n error?: string;\n reason?: string;\n runId?: string;\n};\n\nexport function subagentEndedIsSuccess(ev: SubagentEndedEvent): boolean {\n if (typeof ev.success === \"boolean\") return ev.success;\n const o = (ev.outcome ?? \"\").toLowerCase();\n if (!o) return true;\n if ([\"error\", \"timeout\", \"killed\", \"failed\", \"failure\"].includes(o)) return false;\n if ([\"success\", \"completed\", \"ok\", \"done\"].includes(o)) return true;\n return true;\n}\n\n/**\n * Resolve which active task row this event refers to: prefer label match, then\n * `subagent === targetSessionKey` when core sends session keys without a stable label.\n */\nexport function findActiveTaskForSubagentEnd<T extends { label: string; subagent?: string }>(\n active: T[],\n ev: SubagentEndedEvent,\n): T | undefined {\n const targetKey = ev.targetSessionKey ?? ev.sessionKey;\n const candidateLabel = ev.label ?? targetKey;\n if (candidateLabel) {\n const byLabel = active.find((t) => t.label === candidateLabel);\n if (byLabel) return byLabel;\n }\n if (targetKey) {\n return active.find((t) => t.subagent === targetKey);\n }\n return undefined;\n}\n"],"mappings":";AAiBA,SAAgB,uBAAuB,IAAiC;CACtE,IAAI,OAAO,GAAG,YAAY,WAAW,OAAO,GAAG;CAC/C,MAAM,KAAK,GAAG,WAAW,IAAI,aAAa;CAC1C,IAAI,CAAC,GAAG,OAAO;CACf,IAAI;EAAC;EAAS;EAAW;EAAU;EAAU;EAAU,CAAC,SAAS,EAAE,EAAE,OAAO;CAC5E,IAAI;EAAC;EAAW;EAAa;EAAM;EAAO,CAAC,SAAS,EAAE,EAAE,OAAO;CAC/D,OAAO;;;;;;AAOT,SAAgB,6BACd,QACA,IACe;CACf,MAAM,YAAY,GAAG,oBAAoB,GAAG;CAC5C,MAAM,iBAAiB,GAAG,SAAS;CACnC,IAAI,gBAAgB;EAClB,MAAM,UAAU,OAAO,MAAM,MAAM,EAAE,UAAU,eAAe;EAC9D,IAAI,SAAS,OAAO;;CAEtB,IAAI,WACF,OAAO,OAAO,MAAM,MAAM,EAAE,aAAa,UAAU"}
1
+ {"version":3,"file":"subagent-ended-utils.js","names":[],"sources":["../../utils/subagent-ended-utils.ts"],"sourcesContent":["/**\n * Helpers for OpenClaw **subagent_ended** payloads (issue #966).\n * Core passes targetSessionKey, outcome, error, etc.; shapes vary slightly by version.\n */\n\n/** OpenClaw core dispatch shapes — see runSubagentEnded */\nexport type SubagentEndedEvent = {\n targetSessionKey?: string;\n sessionKey?: string;\n label?: string;\n success?: boolean;\n outcome?: string;\n error?: string;\n reason?: string;\n runId?: string;\n};\n\nexport function subagentEndedIsSuccess(ev: SubagentEndedEvent): boolean {\n if (typeof ev.success === \"boolean\") return ev.success;\n const o = (ev.outcome ?? \"\").toLowerCase();\n if (!o) return true;\n if ([\"error\", \"timeout\", \"killed\", \"failed\", \"failure\"].includes(o)) return false;\n if ([\"success\", \"completed\", \"ok\", \"done\"].includes(o)) return true;\n return true;\n}\n\n/**\n * Resolve which active task row this event refers to: prefer label match, then\n * `subagent === targetSessionKey` when core sends session keys without a stable label.\n */\nexport function findActiveTaskForSubagentEnd<T extends { label: string; subagent?: string }>(\n active: T[],\n ev: SubagentEndedEvent,\n): T | undefined {\n const targetKey = ev.targetSessionKey ?? ev.sessionKey;\n const candidateLabel = ev.label ?? targetKey;\n if (candidateLabel) {\n const byLabel = active.find((t) => t.label === candidateLabel);\n if (byLabel) return byLabel;\n }\n if (targetKey) {\n return active.find((t) => t.subagent === targetKey);\n }\n return undefined;\n}\n"],"mappings":";AAiBA,SAAgB,uBAAuB,IAAiC;CACtE,IAAI,OAAO,GAAG,YAAY,WAAW,OAAO,GAAG;CAC/C,MAAM,KAAK,GAAG,WAAW,IAAI,YAAY;CACzC,IAAI,CAAC,GAAG,OAAO;CACf,IAAI;EAAC;EAAS;EAAW;EAAU;EAAU;CAAS,EAAE,SAAS,CAAC,GAAG,OAAO;CAC5E,IAAI;EAAC;EAAW;EAAa;EAAM;CAAM,EAAE,SAAS,CAAC,GAAG,OAAO;CAC/D,OAAO;AACT;;;;;AAMA,SAAgB,6BACd,QACA,IACe;CACf,MAAM,YAAY,GAAG,oBAAoB,GAAG;CAC5C,MAAM,iBAAiB,GAAG,SAAS;CACnC,IAAI,gBAAgB;EAClB,MAAM,UAAU,OAAO,MAAM,MAAM,EAAE,UAAU,cAAc;EAC7D,IAAI,SAAS,OAAO;CACtB;CACA,IAAI,WACF,OAAO,OAAO,MAAM,MAAM,EAAE,aAAa,SAAS;AAGtD"}
@@ -1 +1 @@
1
- {"version":3,"file":"tags.js","names":[],"sources":["../../utils/tags.ts"],"sourcesContent":["/**\n * Tag extraction and fuzzy dedupe helpers for facts.\n */\n\nimport { createHash } from \"node:crypto\";\n\n/** Normalize text for fuzzy dedupe (2.3): trim, collapse whitespace, lowercase. */\nexport function normalizeTextForDedupe(text: string): string {\n return text.trim().replace(/\\s+/g, \" \").toLowerCase();\n}\n\nexport function normalizedHash(text: string): string {\n return createHash(\"sha256\").update(normalizeTextForDedupe(text)).digest(\"hex\");\n}\n\n/** Tag patterns: [tag, regex]. Order matters; first match wins. */\nexport const TAG_PATTERNS: Array<[string, RegExp]> = [\n [\"nibe\", /\\bnibe\\b/i],\n [\"zigbee\", /\\bzigbee\\b/i],\n [\"z-wave\", /\\bz-?wave\\b/i],\n [\"auth\", /\\bauth(entication|orization)?\\b/i],\n [\"homeassistant\", /\\bhome[- ]?assistant\\b/i],\n [\"openclaw\", /\\bopenclaw\\b/i],\n [\"postgres\", /\\bpostgres(ql)?\\b/i],\n [\"sqlite\", /\\bsqlite\\b/i],\n [\"lancedb\", /\\blancedb\\b/i],\n [\"api\", /\\bapi\\s+(key|endpoint|url)\\b/i],\n [\"docker\", /\\bdocker\\b/i],\n [\"kubernetes\", /\\bkubernetes|k8s\\b/i],\n [\"ha\", /\\bha\\b/i],\n];\n\n/** Extract topic tags from text. Returns lowercase, deduplicated tags. */\nexport function extractTags(text: string, entity?: string | null): string[] {\n const combined = [text, entity].filter(Boolean).join(\" \").toLowerCase();\n const seen = new Set<string>();\n for (const [tag, re] of TAG_PATTERNS) {\n if (re.test(combined) && !seen.has(tag)) {\n seen.add(tag);\n }\n }\n return [...seen];\n}\n\n/** Serialize tags for SQLite storage (comma-separated). */\nexport function serializeTags(tags: string[]): string | null {\n if (tags.length === 0) return null;\n return tags.join(\",\");\n}\n\n/** Parse tags from SQLite (comma-separated). */\nexport function parseTags(s: string | null | undefined): string[] {\n if (!s || typeof s !== \"string\") return [];\n return s\n .split(\",\")\n .map((t) => t.trim().toLowerCase())\n .filter((t) => t.length > 0);\n}\n\n/** Check if tags string contains tag (comma-separated, exact match). */\nexport function tagsContains(tagsStr: string | null | undefined, tag: string): boolean {\n if (!tagsStr) return false;\n const tagLower = tag.toLowerCase().trim();\n return parseTags(tagsStr).includes(tagLower);\n}\n"],"mappings":";;;;;;AAOA,SAAgB,uBAAuB,MAAsB;CAC3D,OAAO,KAAK,MAAM,CAAC,QAAQ,QAAQ,IAAI,CAAC,aAAa;;AAGvD,SAAgB,eAAe,MAAsB;CACnD,OAAO,WAAW,SAAS,CAAC,OAAO,uBAAuB,KAAK,CAAC,CAAC,OAAO,MAAM;;;AAIhF,MAAa,eAAwC;CACnD,CAAC,QAAQ,YAAY;CACrB,CAAC,UAAU,cAAc;CACzB,CAAC,UAAU,eAAe;CAC1B,CAAC,QAAQ,mCAAmC;CAC5C,CAAC,iBAAiB,0BAA0B;CAC5C,CAAC,YAAY,gBAAgB;CAC7B,CAAC,YAAY,qBAAqB;CAClC,CAAC,UAAU,cAAc;CACzB,CAAC,WAAW,eAAe;CAC3B,CAAC,OAAO,gCAAgC;CACxC,CAAC,UAAU,cAAc;CACzB,CAAC,cAAc,sBAAsB;CACrC,CAAC,MAAM,UAAU;CAClB;;AAGD,SAAgB,YAAY,MAAc,QAAkC;CAC1E,MAAM,WAAW,CAAC,MAAM,OAAO,CAAC,OAAO,QAAQ,CAAC,KAAK,IAAI,CAAC,aAAa;CACvE,MAAM,uBAAO,IAAI,KAAa;CAC9B,KAAK,MAAM,CAAC,KAAK,OAAO,cACtB,IAAI,GAAG,KAAK,SAAS,IAAI,CAAC,KAAK,IAAI,IAAI,EACrC,KAAK,IAAI,IAAI;CAGjB,OAAO,CAAC,GAAG,KAAK;;;AAIlB,SAAgB,cAAc,MAA+B;CAC3D,IAAI,KAAK,WAAW,GAAG,OAAO;CAC9B,OAAO,KAAK,KAAK,IAAI;;;AAIvB,SAAgB,UAAU,GAAwC;CAChE,IAAI,CAAC,KAAK,OAAO,MAAM,UAAU,OAAO,EAAE;CAC1C,OAAO,EACJ,MAAM,IAAI,CACV,KAAK,MAAM,EAAE,MAAM,CAAC,aAAa,CAAC,CAClC,QAAQ,MAAM,EAAE,SAAS,EAAE;;;AAIhC,SAAgB,aAAa,SAAoC,KAAsB;CACrF,IAAI,CAAC,SAAS,OAAO;CACrB,MAAM,WAAW,IAAI,aAAa,CAAC,MAAM;CACzC,OAAO,UAAU,QAAQ,CAAC,SAAS,SAAS"}
1
+ {"version":3,"file":"tags.js","names":[],"sources":["../../utils/tags.ts"],"sourcesContent":["/**\n * Tag extraction and fuzzy dedupe helpers for facts.\n */\n\nimport { createHash } from \"node:crypto\";\n\n/** Normalize text for fuzzy dedupe (2.3): trim, collapse whitespace, lowercase. */\nexport function normalizeTextForDedupe(text: string): string {\n return text.trim().replace(/\\s+/g, \" \").toLowerCase();\n}\n\nexport function normalizedHash(text: string): string {\n return createHash(\"sha256\").update(normalizeTextForDedupe(text)).digest(\"hex\");\n}\n\n/** Tag patterns: [tag, regex]. Order matters; first match wins. */\nexport const TAG_PATTERNS: Array<[string, RegExp]> = [\n [\"nibe\", /\\bnibe\\b/i],\n [\"zigbee\", /\\bzigbee\\b/i],\n [\"z-wave\", /\\bz-?wave\\b/i],\n [\"auth\", /\\bauth(entication|orization)?\\b/i],\n [\"homeassistant\", /\\bhome[- ]?assistant\\b/i],\n [\"openclaw\", /\\bopenclaw\\b/i],\n [\"postgres\", /\\bpostgres(ql)?\\b/i],\n [\"sqlite\", /\\bsqlite\\b/i],\n [\"lancedb\", /\\blancedb\\b/i],\n [\"api\", /\\bapi\\s+(key|endpoint|url)\\b/i],\n [\"docker\", /\\bdocker\\b/i],\n [\"kubernetes\", /\\bkubernetes|k8s\\b/i],\n [\"ha\", /\\bha\\b/i],\n];\n\n/** Extract topic tags from text. Returns lowercase, deduplicated tags. */\nexport function extractTags(text: string, entity?: string | null): string[] {\n const combined = [text, entity].filter(Boolean).join(\" \").toLowerCase();\n const seen = new Set<string>();\n for (const [tag, re] of TAG_PATTERNS) {\n if (re.test(combined) && !seen.has(tag)) {\n seen.add(tag);\n }\n }\n return [...seen];\n}\n\n/** Serialize tags for SQLite storage (comma-separated). */\nexport function serializeTags(tags: string[]): string | null {\n if (tags.length === 0) return null;\n return tags.join(\",\");\n}\n\n/** Parse tags from SQLite (comma-separated). */\nexport function parseTags(s: string | null | undefined): string[] {\n if (!s || typeof s !== \"string\") return [];\n return s\n .split(\",\")\n .map((t) => t.trim().toLowerCase())\n .filter((t) => t.length > 0);\n}\n\n/** Check if tags string contains tag (comma-separated, exact match). */\nexport function tagsContains(tagsStr: string | null | undefined, tag: string): boolean {\n if (!tagsStr) return false;\n const tagLower = tag.toLowerCase().trim();\n return parseTags(tagsStr).includes(tagLower);\n}\n"],"mappings":";;;;;;AAOA,SAAgB,uBAAuB,MAAsB;CAC3D,OAAO,KAAK,KAAK,EAAE,QAAQ,QAAQ,GAAG,EAAE,YAAY;AACtD;AAEA,SAAgB,eAAe,MAAsB;CACnD,OAAO,WAAW,QAAQ,EAAE,OAAO,uBAAuB,IAAI,CAAC,EAAE,OAAO,KAAK;AAC/E;;AAGA,MAAa,eAAwC;CACnD,CAAC,QAAQ,WAAW;CACpB,CAAC,UAAU,aAAa;CACxB,CAAC,UAAU,cAAc;CACzB,CAAC,QAAQ,kCAAkC;CAC3C,CAAC,iBAAiB,yBAAyB;CAC3C,CAAC,YAAY,eAAe;CAC5B,CAAC,YAAY,oBAAoB;CACjC,CAAC,UAAU,aAAa;CACxB,CAAC,WAAW,cAAc;CAC1B,CAAC,OAAO,+BAA+B;CACvC,CAAC,UAAU,aAAa;CACxB,CAAC,cAAc,qBAAqB;CACpC,CAAC,MAAM,SAAS;AAClB;;AAGA,SAAgB,YAAY,MAAc,QAAkC;CAC1E,MAAM,WAAW,CAAC,MAAM,MAAM,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG,EAAE,YAAY;CACtE,MAAM,uBAAO,IAAI,IAAY;CAC7B,KAAK,MAAM,CAAC,KAAK,OAAO,cACtB,IAAI,GAAG,KAAK,QAAQ,KAAK,CAAC,KAAK,IAAI,GAAG,GACpC,KAAK,IAAI,GAAG;CAGhB,OAAO,CAAC,GAAG,IAAI;AACjB;;AAGA,SAAgB,cAAc,MAA+B;CAC3D,IAAI,KAAK,WAAW,GAAG,OAAO;CAC9B,OAAO,KAAK,KAAK,GAAG;AACtB;;AAGA,SAAgB,UAAU,GAAwC;CAChE,IAAI,CAAC,KAAK,OAAO,MAAM,UAAU,OAAO,CAAC;CACzC,OAAO,EACJ,MAAM,GAAG,EACT,KAAK,MAAM,EAAE,KAAK,EAAE,YAAY,CAAC,EACjC,QAAQ,MAAM,EAAE,SAAS,CAAC;AAC/B;;AAGA,SAAgB,aAAa,SAAoC,KAAsB;CACrF,IAAI,CAAC,SAAS,OAAO;CACrB,MAAM,WAAW,IAAI,YAAY,EAAE,KAAK;CACxC,OAAO,UAAU,OAAO,EAAE,SAAS,QAAQ;AAC7C"}
@@ -1 +1 @@
1
- {"version":3,"file":"text.js","names":[],"sources":["../../utils/text.ts"],"sourcesContent":["/**\n * Shared text utilities (truncation, etc.).\n */\n\n/** Single place for text truncation. Returns text up to maxLen, with suffix if truncated. */\nexport function truncateText(text: string, maxLen: number, suffix = \"…\"): string {\n if (maxLen <= 0) return \"\";\n if (maxLen <= suffix.length) return suffix.slice(0, maxLen);\n if (text.length <= maxLen) return text;\n return text.slice(0, maxLen - suffix.length).trim() + suffix;\n}\n\n/** Truncate for storage (config-driven); appends \" [truncated]\" when truncated. Handles null/undefined gracefully. */\nexport function truncateForStorage(text: string | null | undefined, maxChars: number): string {\n if (text == null) return \"\";\n if (maxChars <= 0) return \"\";\n if (text.length <= maxChars) return text.trim();\n return `${text.slice(0, maxChars).trim()} [truncated]`;\n}\n\n/** Rough token count (OpenAI-style: ~4 chars per token for English). Used for auto-recall cap. */\nexport function estimateTokens(text: string): number {\n return Math.ceil(text.length / 4);\n}\n\n/**\n * More accurate token estimate for display (e.g. progressive index cost).\n * Uses word-boundary heuristic: each word contributes max(1, ceil(len/4)) so short words\n * aren't overcounted. Improves accuracy vs length/4 for typical English without a tokenizer dependency.\n */\nexport function estimateTokensForDisplay(text: string): number {\n if (!text.trim()) return 0;\n const words = text.split(/\\s+/).filter(Boolean);\n return words.reduce((sum, w) => sum + Math.max(1, Math.ceil(w.length / 4)), 0);\n}\n\n/**\n * Chunk session text into overlapping windows. No content is dropped.\n * Used by distill CLI for oversized sessions.\n *\n * @param text - Full session text\n * @param maxTokens - Max tokens per chunk (~4 chars per token)\n * @param overlapRatio - Fraction of chunk to overlap (default 0.1)\n */\nexport function chunkSessionText(text: string, maxTokens: number, overlapRatio = 0.1): string[] {\n const maxChars = maxTokens * 4;\n if (text.length <= maxChars) return [text];\n const overlapChars = Math.floor(maxChars * overlapRatio);\n const chunks: string[] = [];\n let offset = 0;\n while (offset < text.length) {\n const end = Math.min(offset + maxChars, text.length);\n chunks.push(text.slice(offset, end));\n if (end >= text.length) break;\n offset = end - overlapChars;\n }\n return chunks;\n}\n\n/**\n * Chunk text by character size with overlap. Used by ingest-files.\n */\nexport function chunkTextByChars(text: string, chunkSize: number, overlap: number): string[] {\n if (chunkSize <= 0 || text.length <= chunkSize) return text ? [text] : [\"\"];\n const step = Math.max(1, chunkSize - overlap);\n const chunks: string[] = [];\n let offset = 0;\n while (offset < text.length) {\n chunks.push(text.slice(offset, offset + chunkSize));\n offset += step;\n if (offset >= text.length) break;\n }\n return chunks;\n}\n\n/**\n * Format a single progressive index line as \"[category] title (N tok)\".\n * Used by auto-recall when injectionFormat is progressive or progressive_hybrid.\n */\nexport function formatProgressiveIndexLine(\n category: string,\n title: string,\n tokenCost: number,\n position: number,\n): string {\n return ` ${position}. [${category}] ${title} (${tokenCost} tok)`;\n}\n\n/**\n * Extract text content from message content blocks (used by extraction services).\n * Handles array of content blocks with type \"text\".\n */\nexport function extractMessageText(content: unknown): string {\n if (!Array.isArray(content)) return \"\";\n const parts: string[] = [];\n for (const block of content) {\n if (block && typeof block === \"object\" && (block as { type?: string }).type === \"text\") {\n const text = (block as { text?: string }).text;\n if (typeof text === \"string\" && text.trim()) parts.push(text.trim());\n }\n }\n return parts.join(\"\\n\");\n}\n\n/**\n * Simple truncate with ellipsis (used by extraction services).\n * Different from truncateText which uses a configurable suffix.\n */\nexport function truncate(s: string, max: number): string {\n const t = s.trim();\n if (t.length <= max) return t;\n return `${t.slice(0, max)}...`;\n}\n\n/**\n * Extract timestamp from session filename if it looks like YYYY-MM-DD-*.jsonl.\n */\nexport function timestampFromFilename(name: string): string | undefined {\n const match = name.match(/^(\\d{4}-\\d{2}-\\d{2})/);\n return match ? match[1] : undefined;\n}\n\n/**\n * Shared helper for generating directory-safe slugs (lowercase, hyphens, alphanumeric; max 60 chars).\n * @param text - The text to slugify.\n * @param fallback - Fallback value when the slug is empty after processing (default: `\"skill\"`).\n * Pass `\"procedure\"` when generating slugs for the generate-auto-skills pipeline to preserve\n * its original fallback behavior.\n */\nexport function slugifyForSkill(text: string, fallback = \"skill\"): string {\n const slug = text\n .toLowerCase()\n .replace(/\\s+/g, \"-\")\n .replace(/[^a-z0-9-]/g, \"\")\n .replace(/-+/g, \"-\")\n .replace(/^-|-$/g, \"\")\n .slice(0, 60);\n return slug || fallback;\n}\n\n/**\n * Deduplicate and clean an array of strings (trim, filter empty).\n */\nexport function uniqueStrings(values: string[]): string[] {\n return Array.from(new Set(values.map((value) => value.trim()).filter((value) => value.length > 0)));\n}\n\n/**\n * Convert a hyphenated slug to title case (e.g., \"my-skill-name\" → \"My Skill Name\").\n * Uses the basename of the slug to handle paths correctly.\n */\nexport function titleCase(slug: string): string {\n const base = slug.includes(\"/\") ? (slug.split(\"/\").pop() ?? slug) : slug;\n return base\n .split(\"-\")\n .map((p) => (p ? p[0]?.toUpperCase() + p.slice(1) : p))\n .join(\" \");\n}\n\n/**\n * Sanitize recalled fact text by removing thinking/reasoning wrappers and normalizing whitespace.\n * Used by recall injection and context audit to ensure consistent token estimates.\n */\nexport function sanitizeRecallFactText(text: string): string {\n return text\n .replace(/<redacted_thinking>[\\s\\S]*?<\\/redacted_thinking>/gi, \" \")\n .replace(/<thinking>[\\s\\S]*?<\\/thinking>/gi, \" \")\n .replace(/<reasoning>[\\s\\S]*?<\\/reasoning>/gi, \" \")\n .replace(/<think>[\\s\\S]*?<\\/think>/gi, \" \")\n .replace(/\\s+/g, \" \")\n .trim();\n}\n\n/**\n * @deprecated Use sanitizeRecallFactText. Kept as an alias for existing callers/tests.\n */\nexport const sanitizeHotFactText = sanitizeRecallFactText;\n\n/**\n * Strip leading whitespace plus repeated HTML comment blocks from skill content.\n *\n * Skill installers can prepend metadata comments before YAML frontmatter. This\n * helper keeps parser behavior consistent across generated-skill validation,\n * legacy crystallization detection, and static skill validation. If a closing\n * comment marker is followed by content on the same line (for example\n * `<!-- meta --> ---`), scanning resumes at that content instead of discarding\n * the whole line.\n */\nexport function stripLeadingHtmlComments(content: string): string {\n let body = content.replace(/^\\uFEFF/, \"\");\n\n while (true) {\n const trimmed = body.replace(/^\\s*/, \"\");\n if (!trimmed.startsWith(\"<!--\")) return trimmed;\n\n const closeIndex = trimmed.indexOf(\"-->\");\n if (closeIndex === -1) return trimmed;\n\n body = trimmed.slice(closeIndex + 3);\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"text.js","names":[],"sources":["../../utils/text.ts"],"sourcesContent":["/**\n * Shared text utilities (truncation, etc.).\n */\n\n/** Single place for text truncation. Returns text up to maxLen, with suffix if truncated. */\nexport function truncateText(text: string, maxLen: number, suffix = \"…\"): string {\n if (maxLen <= 0) return \"\";\n if (maxLen <= suffix.length) return suffix.slice(0, maxLen);\n if (text.length <= maxLen) return text;\n return text.slice(0, maxLen - suffix.length).trim() + suffix;\n}\n\n/** Truncate for storage (config-driven); appends \" [truncated]\" when truncated. Handles null/undefined gracefully. */\nexport function truncateForStorage(text: string | null | undefined, maxChars: number): string {\n if (text == null) return \"\";\n if (maxChars <= 0) return \"\";\n if (text.length <= maxChars) return text.trim();\n return `${text.slice(0, maxChars).trim()} [truncated]`;\n}\n\n/** Rough token count (OpenAI-style: ~4 chars per token for English). Used for auto-recall cap. */\nexport function estimateTokens(text: string): number {\n return Math.ceil(text.length / 4);\n}\n\n/**\n * More accurate token estimate for display (e.g. progressive index cost).\n * Uses word-boundary heuristic: each word contributes max(1, ceil(len/4)) so short words\n * aren't overcounted. Improves accuracy vs length/4 for typical English without a tokenizer dependency.\n */\nexport function estimateTokensForDisplay(text: string): number {\n if (!text.trim()) return 0;\n const words = text.split(/\\s+/).filter(Boolean);\n return words.reduce((sum, w) => sum + Math.max(1, Math.ceil(w.length / 4)), 0);\n}\n\n/**\n * Chunk session text into overlapping windows. No content is dropped.\n * Used by distill CLI for oversized sessions.\n *\n * @param text - Full session text\n * @param maxTokens - Max tokens per chunk (~4 chars per token)\n * @param overlapRatio - Fraction of chunk to overlap (default 0.1)\n */\nexport function chunkSessionText(text: string, maxTokens: number, overlapRatio = 0.1): string[] {\n const maxChars = maxTokens * 4;\n if (text.length <= maxChars) return [text];\n const overlapChars = Math.floor(maxChars * overlapRatio);\n const chunks: string[] = [];\n let offset = 0;\n while (offset < text.length) {\n const end = Math.min(offset + maxChars, text.length);\n chunks.push(text.slice(offset, end));\n if (end >= text.length) break;\n offset = end - overlapChars;\n }\n return chunks;\n}\n\n/**\n * Chunk text by character size with overlap. Used by ingest-files.\n */\nexport function chunkTextByChars(text: string, chunkSize: number, overlap: number): string[] {\n if (chunkSize <= 0 || text.length <= chunkSize) return text ? [text] : [\"\"];\n const step = Math.max(1, chunkSize - overlap);\n const chunks: string[] = [];\n let offset = 0;\n while (offset < text.length) {\n chunks.push(text.slice(offset, offset + chunkSize));\n offset += step;\n if (offset >= text.length) break;\n }\n return chunks;\n}\n\n/**\n * Format a single progressive index line as \"[category] title (N tok)\".\n * Used by auto-recall when injectionFormat is progressive or progressive_hybrid.\n */\nexport function formatProgressiveIndexLine(\n category: string,\n title: string,\n tokenCost: number,\n position: number,\n): string {\n return ` ${position}. [${category}] ${title} (${tokenCost} tok)`;\n}\n\n/**\n * Extract text content from message content blocks (used by extraction services).\n * Handles array of content blocks with type \"text\".\n */\nexport function extractMessageText(content: unknown): string {\n if (!Array.isArray(content)) return \"\";\n const parts: string[] = [];\n for (const block of content) {\n if (block && typeof block === \"object\" && (block as { type?: string }).type === \"text\") {\n const text = (block as { text?: string }).text;\n if (typeof text === \"string\" && text.trim()) parts.push(text.trim());\n }\n }\n return parts.join(\"\\n\");\n}\n\n/**\n * Simple truncate with ellipsis (used by extraction services).\n * Different from truncateText which uses a configurable suffix.\n */\nexport function truncate(s: string, max: number): string {\n const t = s.trim();\n if (t.length <= max) return t;\n return `${t.slice(0, max)}...`;\n}\n\n/**\n * Extract timestamp from session filename if it looks like YYYY-MM-DD-*.jsonl.\n */\nexport function timestampFromFilename(name: string): string | undefined {\n const match = name.match(/^(\\d{4}-\\d{2}-\\d{2})/);\n return match ? match[1] : undefined;\n}\n\n/**\n * Shared helper for generating directory-safe slugs (lowercase, hyphens, alphanumeric; max 60 chars).\n * @param text - The text to slugify.\n * @param fallback - Fallback value when the slug is empty after processing (default: `\"skill\"`).\n * Pass `\"procedure\"` when generating slugs for the generate-auto-skills pipeline to preserve\n * its original fallback behavior.\n */\nexport function slugifyForSkill(text: string, fallback = \"skill\"): string {\n const slug = text\n .toLowerCase()\n .replace(/\\s+/g, \"-\")\n .replace(/[^a-z0-9-]/g, \"\")\n .replace(/-+/g, \"-\")\n .replace(/^-|-$/g, \"\")\n .slice(0, 60);\n return slug || fallback;\n}\n\n/**\n * Deduplicate and clean an array of strings (trim, filter empty).\n */\nexport function uniqueStrings(values: string[]): string[] {\n return Array.from(new Set(values.map((value) => value.trim()).filter((value) => value.length > 0)));\n}\n\n/**\n * Convert a hyphenated slug to title case (e.g., \"my-skill-name\" → \"My Skill Name\").\n * Uses the basename of the slug to handle paths correctly.\n */\nexport function titleCase(slug: string): string {\n const base = slug.includes(\"/\") ? (slug.split(\"/\").pop() ?? slug) : slug;\n return base\n .split(\"-\")\n .map((p) => (p ? p[0]?.toUpperCase() + p.slice(1) : p))\n .join(\" \");\n}\n\n/**\n * Sanitize recalled fact text by removing thinking/reasoning wrappers and normalizing whitespace.\n * Used by recall injection and context audit to ensure consistent token estimates.\n */\nexport function sanitizeRecallFactText(text: string): string {\n return text\n .replace(/<redacted_thinking>[\\s\\S]*?<\\/redacted_thinking>/gi, \" \")\n .replace(/<thinking>[\\s\\S]*?<\\/thinking>/gi, \" \")\n .replace(/<reasoning>[\\s\\S]*?<\\/reasoning>/gi, \" \")\n .replace(/<think>[\\s\\S]*?<\\/think>/gi, \" \")\n .replace(/\\s+/g, \" \")\n .trim();\n}\n\n/**\n * @deprecated Use sanitizeRecallFactText. Kept as an alias for existing callers/tests.\n */\nexport const sanitizeHotFactText = sanitizeRecallFactText;\n\n/**\n * Strip leading whitespace plus repeated HTML comment blocks from skill content.\n *\n * Skill installers can prepend metadata comments before YAML frontmatter. This\n * helper keeps parser behavior consistent across generated-skill validation,\n * legacy crystallization detection, and static skill validation. If a closing\n * comment marker is followed by content on the same line (for example\n * `<!-- meta --> ---`), scanning resumes at that content instead of discarding\n * the whole line.\n */\nexport function stripLeadingHtmlComments(content: string): string {\n let body = content.replace(/^\\uFEFF/, \"\");\n\n while (true) {\n const trimmed = body.replace(/^\\s*/, \"\");\n if (!trimmed.startsWith(\"<!--\")) return trimmed;\n\n const closeIndex = trimmed.indexOf(\"-->\");\n if (closeIndex === -1) return trimmed;\n\n body = trimmed.slice(closeIndex + 3);\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"timeout.js","names":[],"sources":["../../utils/timeout.ts"],"sourcesContent":["/**\n * Shared timeout utility for lifecycle stages.\n * Races a promise against a timeout, returning null if the timeout wins.\n */\n\nexport function withTimeout<T>(ms: number, fn: () => Promise<T>): Promise<T | null>;\nexport function withTimeout<T, F>(ms: number, fn: () => Promise<T>, fallback: F): Promise<T | F>;\nexport function withTimeout<T, F = null>(ms: number, fn: () => Promise<T>, fallback?: F): Promise<T | F | null> {\n const fallbackValue = (fallback === undefined ? null : fallback) as F | null;\n let timer: ReturnType<typeof setTimeout> | undefined;\n const timeoutPromise = new Promise<F | null>((resolve) => {\n timer = setTimeout(() => resolve(fallbackValue), ms);\n });\n return Promise.race([fn(), timeoutPromise]).finally(() => {\n clearTimeout(timer);\n });\n}\n"],"mappings":";AAOA,SAAgB,YAAyB,IAAY,IAAsB,UAAqC;CAC9G,MAAM,gBAAiB,aAAa,KAAA,IAAY,OAAO;CACvD,IAAI;CACJ,MAAM,iBAAiB,IAAI,SAAmB,YAAY;EACxD,QAAQ,iBAAiB,QAAQ,cAAc,EAAE,GAAG;GACpD;CACF,OAAO,QAAQ,KAAK,CAAC,IAAI,EAAE,eAAe,CAAC,CAAC,cAAc;EACxD,aAAa,MAAM;GACnB"}
1
+ {"version":3,"file":"timeout.js","names":[],"sources":["../../utils/timeout.ts"],"sourcesContent":["/**\n * Shared timeout utility for lifecycle stages.\n * Races a promise against a timeout, returning null if the timeout wins.\n */\n\nexport function withTimeout<T>(ms: number, fn: () => Promise<T>): Promise<T | null>;\nexport function withTimeout<T, F>(ms: number, fn: () => Promise<T>, fallback: F): Promise<T | F>;\nexport function withTimeout<T, F = null>(ms: number, fn: () => Promise<T>, fallback?: F): Promise<T | F | null> {\n const fallbackValue = (fallback === undefined ? null : fallback) as F | null;\n let timer: ReturnType<typeof setTimeout> | undefined;\n const timeoutPromise = new Promise<F | null>((resolve) => {\n timer = setTimeout(() => resolve(fallbackValue), ms);\n });\n return Promise.race([fn(), timeoutPromise]).finally(() => {\n clearTimeout(timer);\n });\n}\n"],"mappings":";AAOA,SAAgB,YAAyB,IAAY,IAAsB,UAAqC;CAC9G,MAAM,gBAAiB,aAAa,KAAA,IAAY,OAAO;CACvD,IAAI;CACJ,MAAM,iBAAiB,IAAI,SAAmB,YAAY;EACxD,QAAQ,iBAAiB,QAAQ,aAAa,GAAG,EAAE;CACrD,CAAC;CACD,OAAO,QAAQ,KAAK,CAAC,GAAG,GAAG,cAAc,CAAC,EAAE,cAAc;EACxD,aAAa,KAAK;CACpB,CAAC;AACH"}
@@ -1 +1 @@
1
- {"version":3,"file":"typebox.js","names":[],"sources":["../../utils/typebox.ts"],"sourcesContent":["import { Type } from \"@sinclair/typebox\";\n\nexport function stringEnum<T extends readonly string[]>(values: T, options: { description?: string } = {}) {\n return Type.Unsafe<T[number]>({\n type: \"string\",\n enum: [...values],\n ...options,\n });\n}\n"],"mappings":";;AAEA,SAAgB,WAAwC,QAAW,UAAoC,EAAE,EAAE;CACzG,OAAO,KAAK,OAAkB;EAC5B,MAAM;EACN,MAAM,CAAC,GAAG,OAAO;EACjB,GAAG;EACJ,CAAC"}
1
+ {"version":3,"file":"typebox.js","names":[],"sources":["../../utils/typebox.ts"],"sourcesContent":["import { Type } from \"@sinclair/typebox\";\n\nexport function stringEnum<T extends readonly string[]>(values: T, options: { description?: string } = {}) {\n return Type.Unsafe<T[number]>({\n type: \"string\",\n enum: [...values],\n ...options,\n });\n}\n"],"mappings":";;AAEA,SAAgB,WAAwC,QAAW,UAAoC,CAAC,GAAG;CACzG,OAAO,KAAK,OAAkB;EAC5B,MAAM;EACN,MAAM,CAAC,GAAG,MAAM;EAChB,GAAG;CACL,CAAC;AACH"}
@@ -1 +1 @@
1
- {"version":3,"file":"version-check.js","names":[],"sources":["../../utils/version-check.ts"],"sourcesContent":["/**\n * OpenClaw version compatibility checks.\n *\n * Parses and compares version strings in YYYY.M.N format\n * to enforce minimum gateway version requirements at startup.\n */\n\n/**\n * Minimum OpenClaw **gateway** version we warn below (CLI subcommands, `api.version`, SIGUSR1 reload).\n * Kept in sync with `package.json` `peerDependencies.openclaw`. This is a soft check (`checkOpenClawVersion` logs a warning; it does not block load).\n *\n * Pinned to the 2026.5.x line to avoid the open-ended peer range that\n * previously let npm resolve to an intermediate `openclaw` build whose\n * transitive `@duckflux/core@^0.1.0` constraint had no published versions\n * (issue #1172).\n */\nexport const MIN_OPENCLAW_VERSION = \"2026.5.0\";\n\n/**\n * Parses a version string into a numeric tuple.\n * Handles optional leading `v`, pre-release suffixes (e.g. `2026.3.8-beta`),\n * and build metadata. Rejects empty segments like `2026..8`.\n * Returns null if three non-negative integers cannot be found.\n */\nexport function parseVersion(version: string): [number, number, number] | null {\n const match = version.trim().match(/^v?(\\d+)\\.(\\d+)\\.(\\d+)/);\n if (!match) return null;\n return [Number(match[1]), Number(match[2]), Number(match[3])];\n}\n\n/**\n * Returns true if `current` is >= `minimum`.\n * If either version is unparseable, returns true to avoid false positives.\n */\nexport function isVersionAtLeast(current: string, minimum: string): boolean {\n const cur = parseVersion(current);\n const min = parseVersion(minimum);\n if (!cur || !min) return true;\n for (let i = 0; i < 3; i++) {\n if (cur[i] > min[i]) return true;\n if (cur[i] < min[i]) return false;\n }\n return true; // equal\n}\n\n/**\n * Compare two version strings numerically.\n * Returns -1 when `a < b`, 1 when `a > b`, and 0 when equal or unparseable.\n */\nexport function compareVersions(a: string, b: string): number {\n const versionA = parseVersion(a);\n const versionB = parseVersion(b);\n if (!versionA || !versionB) return 0;\n\n for (let i = 0; i < 3; i++) {\n if (versionA[i] < versionB[i]) return -1;\n if (versionA[i] > versionB[i]) return 1;\n }\n\n return 0;\n}\n\n/**\n * Checks the running OpenClaw gateway version against the minimum requirement.\n * Logs a clear warning if the version is below the minimum — does not hard-fail.\n *\n * @param currentVersion - The `api.version` string passed by the gateway (may be undefined)\n * @param logger - Plugin logger for emitting the warning\n */\nexport function checkOpenClawVersion(\n currentVersion: string | undefined,\n logger: { warn: (msg: string) => void },\n): void {\n if (!currentVersion) {\n logger.warn(\n `memory-hybrid: WARNING — OpenClaw version is undefined (gateway likely < v${MIN_OPENCLAW_VERSION}). Minimum recommended is v${MIN_OPENCLAW_VERSION}. Some features (CLI subcommands, SIGUSR1 reload) may not work.`,\n );\n return;\n }\n if (!isVersionAtLeast(currentVersion, MIN_OPENCLAW_VERSION)) {\n logger.warn(\n `memory-hybrid: WARNING — OpenClaw v${currentVersion} detected, minimum recommended is v${MIN_OPENCLAW_VERSION}. Some features (CLI subcommands, SIGUSR1 reload) may not work.`,\n );\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAgBA,MAAa,uBAAuB;;;;;;;AAQpC,SAAgB,aAAa,SAAkD;CAC7E,MAAM,QAAQ,QAAQ,MAAM,CAAC,MAAM,yBAAyB;CAC5D,IAAI,CAAC,OAAO,OAAO;CACnB,OAAO;EAAC,OAAO,MAAM,GAAG;EAAE,OAAO,MAAM,GAAG;EAAE,OAAO,MAAM,GAAG;EAAC;;;;;;AAO/D,SAAgB,iBAAiB,SAAiB,SAA0B;CAC1E,MAAM,MAAM,aAAa,QAAQ;CACjC,MAAM,MAAM,aAAa,QAAQ;CACjC,IAAI,CAAC,OAAO,CAAC,KAAK,OAAO;CACzB,KAAK,IAAI,IAAI,GAAG,IAAI,GAAG,KAAK;EAC1B,IAAI,IAAI,KAAK,IAAI,IAAI,OAAO;EAC5B,IAAI,IAAI,KAAK,IAAI,IAAI,OAAO;;CAE9B,OAAO;;;;;;AAOT,SAAgB,gBAAgB,GAAW,GAAmB;CAC5D,MAAM,WAAW,aAAa,EAAE;CAChC,MAAM,WAAW,aAAa,EAAE;CAChC,IAAI,CAAC,YAAY,CAAC,UAAU,OAAO;CAEnC,KAAK,IAAI,IAAI,GAAG,IAAI,GAAG,KAAK;EAC1B,IAAI,SAAS,KAAK,SAAS,IAAI,OAAO;EACtC,IAAI,SAAS,KAAK,SAAS,IAAI,OAAO;;CAGxC,OAAO;;;;;;;;;AAUT,SAAgB,qBACd,gBACA,QACM;CACN,IAAI,CAAC,gBAAgB;EACnB,OAAO,KACL,6EAA6E,qBAAqB,6BAA6B,qBAAqB,iEACrJ;EACD;;CAEF,IAAI,CAAC,iBAAiB,gBAAA,WAAqC,EACzD,OAAO,KACL,sCAAsC,eAAe,qCAAqC,qBAAqB,iEAChH"}
1
+ {"version":3,"file":"version-check.js","names":[],"sources":["../../utils/version-check.ts"],"sourcesContent":["/**\n * OpenClaw version compatibility checks.\n *\n * Parses and compares version strings in YYYY.M.N format\n * to enforce minimum gateway version requirements at startup.\n */\n\n/**\n * Minimum OpenClaw **gateway** version we warn below (CLI subcommands, `api.version`, SIGUSR1 reload).\n * Kept in sync with `package.json` `peerDependencies.openclaw`. This is a soft check (`checkOpenClawVersion` logs a warning; it does not block load).\n *\n * Pinned to the 2026.5.x line to avoid the open-ended peer range that\n * previously let npm resolve to an intermediate `openclaw` build whose\n * transitive `@duckflux/core@^0.1.0` constraint had no published versions\n * (issue #1172).\n */\nexport const MIN_OPENCLAW_VERSION = \"2026.5.0\";\n\n/**\n * Parses a version string into a numeric tuple.\n * Handles optional leading `v`, pre-release suffixes (e.g. `2026.3.8-beta`),\n * and build metadata. Rejects empty segments like `2026..8`.\n * Returns null if three non-negative integers cannot be found.\n */\nexport function parseVersion(version: string): [number, number, number] | null {\n const match = version.trim().match(/^v?(\\d+)\\.(\\d+)\\.(\\d+)/);\n if (!match) return null;\n return [Number(match[1]), Number(match[2]), Number(match[3])];\n}\n\n/**\n * Returns true if `current` is >= `minimum`.\n * If either version is unparseable, returns true to avoid false positives.\n */\nexport function isVersionAtLeast(current: string, minimum: string): boolean {\n const cur = parseVersion(current);\n const min = parseVersion(minimum);\n if (!cur || !min) return true;\n for (let i = 0; i < 3; i++) {\n if (cur[i] > min[i]) return true;\n if (cur[i] < min[i]) return false;\n }\n return true; // equal\n}\n\n/**\n * Compare two version strings numerically.\n * Returns -1 when `a < b`, 1 when `a > b`, and 0 when equal or unparseable.\n */\nexport function compareVersions(a: string, b: string): number {\n const versionA = parseVersion(a);\n const versionB = parseVersion(b);\n if (!versionA || !versionB) return 0;\n\n for (let i = 0; i < 3; i++) {\n if (versionA[i] < versionB[i]) return -1;\n if (versionA[i] > versionB[i]) return 1;\n }\n\n return 0;\n}\n\n/**\n * Checks the running OpenClaw gateway version against the minimum requirement.\n * Logs a clear warning if the version is below the minimum — does not hard-fail.\n *\n * @param currentVersion - The `api.version` string passed by the gateway (may be undefined)\n * @param logger - Plugin logger for emitting the warning\n */\nexport function checkOpenClawVersion(\n currentVersion: string | undefined,\n logger: { warn: (msg: string) => void },\n): void {\n if (!currentVersion) {\n logger.warn(\n `memory-hybrid: WARNING — OpenClaw version is undefined (gateway likely < v${MIN_OPENCLAW_VERSION}). Minimum recommended is v${MIN_OPENCLAW_VERSION}. Some features (CLI subcommands, SIGUSR1 reload) may not work.`,\n );\n return;\n }\n if (!isVersionAtLeast(currentVersion, MIN_OPENCLAW_VERSION)) {\n logger.warn(\n `memory-hybrid: WARNING — OpenClaw v${currentVersion} detected, minimum recommended is v${MIN_OPENCLAW_VERSION}. Some features (CLI subcommands, SIGUSR1 reload) may not work.`,\n );\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAgBA,MAAa,uBAAuB;;;;;;;AAQpC,SAAgB,aAAa,SAAkD;CAC7E,MAAM,QAAQ,QAAQ,KAAK,EAAE,MAAM,wBAAwB;CAC3D,IAAI,CAAC,OAAO,OAAO;CACnB,OAAO;EAAC,OAAO,MAAM,EAAE;EAAG,OAAO,MAAM,EAAE;EAAG,OAAO,MAAM,EAAE;CAAC;AAC9D;;;;;AAMA,SAAgB,iBAAiB,SAAiB,SAA0B;CAC1E,MAAM,MAAM,aAAa,OAAO;CAChC,MAAM,MAAM,aAAa,OAAO;CAChC,IAAI,CAAC,OAAO,CAAC,KAAK,OAAO;CACzB,KAAK,IAAI,IAAI,GAAG,IAAI,GAAG,KAAK;EAC1B,IAAI,IAAI,KAAK,IAAI,IAAI,OAAO;EAC5B,IAAI,IAAI,KAAK,IAAI,IAAI,OAAO;CAC9B;CACA,OAAO;AACT;;;;;AAMA,SAAgB,gBAAgB,GAAW,GAAmB;CAC5D,MAAM,WAAW,aAAa,CAAC;CAC/B,MAAM,WAAW,aAAa,CAAC;CAC/B,IAAI,CAAC,YAAY,CAAC,UAAU,OAAO;CAEnC,KAAK,IAAI,IAAI,GAAG,IAAI,GAAG,KAAK;EAC1B,IAAI,SAAS,KAAK,SAAS,IAAI,OAAO;EACtC,IAAI,SAAS,KAAK,SAAS,IAAI,OAAO;CACxC;CAEA,OAAO;AACT;;;;;;;;AASA,SAAgB,qBACd,gBACA,QACM;CACN,IAAI,CAAC,gBAAgB;EACnB,OAAO,KACL,6EAA6E,qBAAqB,6BAA6B,qBAAqB,gEACtJ;EACA;CACF;CACA,IAAI,CAAC,iBAAiB,gBAAA,UAAoC,GACxD,OAAO,KACL,sCAAsC,eAAe,qCAAqC,qBAAqB,gEACjH;AAEJ"}