claude-smart 0.2.44 → 0.2.46

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 (323) hide show
  1. package/.claude-plugin/marketplace.json +3 -3
  2. package/README.md +34 -5
  3. package/bin/claude-smart.js +295 -5
  4. package/package.json +22 -3
  5. package/plugin/.claude-plugin/plugin.json +4 -2
  6. package/plugin/.codex-plugin/plugin.json +1 -1
  7. package/plugin/README.md +22 -2
  8. package/plugin/commands/clear-all.md +1 -0
  9. package/plugin/commands/dashboard.md +1 -0
  10. package/plugin/commands/learn.md +1 -0
  11. package/plugin/commands/restart.md +1 -0
  12. package/plugin/commands/show.md +1 -0
  13. package/plugin/dashboard/app/configure/env/page.tsx +40 -14
  14. package/plugin/dashboard/app/configure/server/page.tsx +51 -1
  15. package/plugin/dashboard/app/preferences/[id]/page.tsx +8 -8
  16. package/plugin/dashboard/app/skills/project/[id]/page.tsx +7 -3
  17. package/plugin/dashboard/app/skills/shared/[id]/page.tsx +10 -3
  18. package/plugin/dashboard/lib/claude-settings-file.ts +20 -10
  19. package/plugin/dashboard/lib/reflexio-client.ts +16 -0
  20. package/plugin/dashboard/lib/status.ts +10 -3
  21. package/plugin/dashboard/lib/types.ts +18 -6
  22. package/plugin/hooks/codex-hooks.json +0 -1
  23. package/plugin/opencode/assistant-buffer.ts +108 -0
  24. package/plugin/opencode/dist/assistant-buffer.js +96 -0
  25. package/plugin/opencode/dist/internal.js +12 -0
  26. package/plugin/opencode/dist/payload.js +85 -0
  27. package/plugin/opencode/dist/server.mjs +158 -0
  28. package/plugin/opencode/internal.ts +18 -0
  29. package/plugin/opencode/package.json +3 -0
  30. package/plugin/opencode/payload.ts +90 -0
  31. package/plugin/opencode/server.mts +174 -0
  32. package/plugin/opencode/tsconfig.json +13 -0
  33. package/plugin/pyproject.toml +20 -3
  34. package/plugin/scripts/_lib.sh +77 -29
  35. package/plugin/scripts/backend-service.sh +27 -5
  36. package/plugin/scripts/dashboard-service.sh +7 -5
  37. package/plugin/scripts/ensure-plugin-root.sh +4 -4
  38. package/plugin/scripts/hook_entry.sh +3 -3
  39. package/plugin/scripts/opencode-claude-compat +4 -0
  40. package/plugin/scripts/opencode-claude-compat.cmd +3 -0
  41. package/plugin/scripts/opencode-claude-compat.js +225 -0
  42. package/plugin/scripts/smart-install.sh +10 -8
  43. package/plugin/src/README.md +1 -1
  44. package/plugin/src/claude_smart/cli.py +304 -6
  45. package/plugin/src/claude_smart/env_config.py +12 -4
  46. package/plugin/src/claude_smart/events/session_start.py +26 -7
  47. package/plugin/src/claude_smart/events/stop.py +1 -1
  48. package/plugin/src/claude_smart/ids.py +1 -1
  49. package/plugin/src/claude_smart/reflexio_adapter.py +5 -5
  50. package/plugin/src/claude_smart/runtime.py +7 -1
  51. package/plugin/uv.lock +1 -1
  52. package/plugin/vendor/reflexio/.env.example +54 -30
  53. package/plugin/vendor/reflexio/README.md +14 -8
  54. package/plugin/vendor/reflexio/pyproject.toml +13 -1
  55. package/plugin/vendor/reflexio/reflexio/README.md +1 -0
  56. package/plugin/vendor/reflexio/reflexio/cli/README.md +9 -7
  57. package/plugin/vendor/reflexio/reflexio/cli/__main__.py +10 -1
  58. package/plugin/vendor/reflexio/reflexio/cli/bootstrap_config.py +22 -5
  59. package/plugin/vendor/reflexio/reflexio/cli/commands/interactions.py +34 -4
  60. package/plugin/vendor/reflexio/reflexio/cli/commands/profiles.py +12 -7
  61. package/plugin/vendor/reflexio/reflexio/cli/commands/services.py +13 -6
  62. package/plugin/vendor/reflexio/reflexio/cli/commands/setup_cmd.py +210 -50
  63. package/plugin/vendor/reflexio/reflexio/cli/commands/shortcuts.py +15 -2
  64. package/plugin/vendor/reflexio/reflexio/cli/env_loader.py +292 -22
  65. package/plugin/vendor/reflexio/reflexio/cli/log_format.py +29 -4
  66. package/plugin/vendor/reflexio/reflexio/cli/run_services.py +17 -8
  67. package/plugin/vendor/reflexio/reflexio/cli/stop_services.py +13 -10
  68. package/plugin/vendor/reflexio/reflexio/client/client.py +57 -13
  69. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/README.md +135 -257
  70. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/npm/openclaw-smart/bin/openclaw-smart.js +13 -0
  71. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/npm/openclaw-smart/package.json +15 -0
  72. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/README.md +38 -0
  73. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/index.ts +151 -110
  74. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/openclaw.plugin.json +20 -10
  75. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/package.json +28 -6
  76. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/pyproject.toml +42 -0
  77. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/_lib.sh +371 -0
  78. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/backend-log-runner.sh +33 -0
  79. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/backend-service.sh +271 -0
  80. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/cli.sh +68 -0
  81. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/dashboard-open.sh +15 -0
  82. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/ensure-plugin-root.sh +84 -0
  83. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/hook_entry.sh +106 -0
  84. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/npm-cli.js +219 -0
  85. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/smart-install.sh +269 -0
  86. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/clear-all/SKILL.md +8 -0
  87. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/dashboard/SKILL.md +8 -0
  88. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/learn/SKILL.md +10 -0
  89. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/reflexio/SKILL.md +15 -44
  90. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/restart/SKILL.md +6 -0
  91. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/show/SKILL.md +8 -0
  92. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/cli.py +634 -0
  93. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/context_format.py +224 -0
  94. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/context_inject.py +79 -0
  95. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/__init__.py +0 -0
  96. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/after_tool_call.py +160 -0
  97. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/agent_end.py +187 -0
  98. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/before_prompt_build.py +69 -0
  99. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/before_tool_call.py +30 -0
  100. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/session_end.py +36 -0
  101. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/session_start.py +130 -0
  102. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/hook.py +131 -0
  103. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/ids.py +94 -0
  104. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/internal_call.py +75 -0
  105. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/oc_cite.py +196 -0
  106. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/optimizer_assistant.py +272 -0
  107. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/publish.py +96 -0
  108. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/query_compose.py +66 -0
  109. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/reflexio_adapter.py +336 -0
  110. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/runtime.py +47 -0
  111. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/stall_banner.py +61 -0
  112. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/state.py +323 -0
  113. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/__init__.py +0 -0
  114. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/integration/__init__.py +0 -0
  115. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/integration/test_e2e_session_loop.py +190 -0
  116. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/integration/test_publish_to_local_reflexio_integration.py +112 -0
  117. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/integration/test_recursion_guard_integration.py +86 -0
  118. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/integration/test_search_inject_integration.py +144 -0
  119. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_cli.py +184 -0
  120. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_events_after_tool_call.py +142 -0
  121. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_events_agent_end.py +233 -0
  122. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_events_before_prompt_build.py +116 -0
  123. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_events_before_tool_call.py +35 -0
  124. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_events_session_end.py +47 -0
  125. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_events_session_start.py +109 -0
  126. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_hook_dispatch.py +117 -0
  127. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_ids.py +41 -0
  128. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_internal_call.py +50 -0
  129. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_oc_cite.py +88 -0
  130. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_publish.py +48 -0
  131. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_query_compose.py +48 -0
  132. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_reflexio_adapter.py +188 -0
  133. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_runtime.py +41 -0
  134. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_state.py +235 -0
  135. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests-ts/__mocks__/plugin-entry-stub.ts +6 -0
  136. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests-ts/test_npm_cli.test.ts +56 -0
  137. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests-ts/test_shim_dispatch.test.ts +171 -0
  138. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tsconfig.build.json +18 -0
  139. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/{tsconfig.json → plugin/tsconfig.json} +2 -2
  140. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/uv.lock +3835 -0
  141. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/vitest.config.ts +18 -0
  142. package/plugin/vendor/reflexio/reflexio/lib/_agent_playbook.py +238 -10
  143. package/plugin/vendor/reflexio/reflexio/lib/_config.py +11 -4
  144. package/plugin/vendor/reflexio/reflexio/lib/_generation.py +16 -5
  145. package/plugin/vendor/reflexio/reflexio/lib/_lineage_parity_readers.py +187 -0
  146. package/plugin/vendor/reflexio/reflexio/lib/_profiles.py +198 -6
  147. package/plugin/vendor/reflexio/reflexio/lib/_reflection.py +1 -1
  148. package/plugin/vendor/reflexio/reflexio/lib/_search.py +29 -20
  149. package/plugin/vendor/reflexio/reflexio/lib/_user_playbook.py +2 -1
  150. package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/entities.py +127 -38
  151. package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/enums.py +4 -0
  152. package/plugin/vendor/reflexio/reflexio/models/api_schema/eval_overview_schema.py +64 -41
  153. package/plugin/vendor/reflexio/reflexio/models/api_schema/internal_schema.py +19 -0
  154. package/plugin/vendor/reflexio/reflexio/models/api_schema/pending_tool_call_schema.py +12 -5
  155. package/plugin/vendor/reflexio/reflexio/models/api_schema/retriever_schema.py +52 -0
  156. package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/converters.py +5 -1
  157. package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/entities.py +5 -1
  158. package/plugin/vendor/reflexio/reflexio/models/config_schema.py +88 -7
  159. package/plugin/vendor/reflexio/reflexio/models/structured_output.py +148 -0
  160. package/plugin/vendor/reflexio/reflexio/server/OVERVIEW.md +3 -3
  161. package/plugin/vendor/reflexio/reflexio/server/README.md +45 -32
  162. package/plugin/vendor/reflexio/reflexio/server/__init__.py +29 -5
  163. package/plugin/vendor/reflexio/reflexio/server/__main__.py +2 -2
  164. package/plugin/vendor/reflexio/reflexio/server/_auth.py +65 -2
  165. package/plugin/vendor/reflexio/reflexio/server/api.py +734 -85
  166. package/plugin/vendor/reflexio/reflexio/server/api_endpoints/precondition_checks.py +7 -0
  167. package/plugin/vendor/reflexio/reflexio/server/api_endpoints/request_context.py +3 -1
  168. package/plugin/vendor/reflexio/reflexio/server/billing_meter.py +173 -0
  169. package/plugin/vendor/reflexio/reflexio/server/billing_signals.py +64 -0
  170. package/plugin/vendor/reflexio/reflexio/server/cache/reflexio_cache.py +46 -16
  171. package/plugin/vendor/reflexio/reflexio/server/env_utils.py +65 -0
  172. package/plugin/vendor/reflexio/reflexio/server/llm/embedding_service.py +19 -4
  173. package/plugin/vendor/reflexio/reflexio/server/llm/litellm_client.py +264 -77
  174. package/plugin/vendor/reflexio/reflexio/server/llm/llm_utils.py +69 -7
  175. package/plugin/vendor/reflexio/reflexio/server/llm/providers/embedding_service_provider.py +75 -16
  176. package/plugin/vendor/reflexio/reflexio/server/llm/providers/openclaw_provider.py +280 -0
  177. package/plugin/vendor/reflexio/reflexio/server/llm/token_accounting.py +48 -0
  178. package/plugin/vendor/reflexio/reflexio/server/llm/tools.py +107 -17
  179. package/plugin/vendor/reflexio/reflexio/server/operation_limiter.py +360 -4
  180. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.6.0.prompt.md +1 -1
  181. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.7.0.prompt.md +85 -0
  182. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_aggregation/v2.2.0.prompt.md +1 -1
  183. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_aggregation/v2.3.0.prompt.md +253 -0
  184. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.3.2.prompt.md +1 -1
  185. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.3.3.prompt.md +74 -0
  186. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.2.3.prompt.md +1 -1
  187. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.3.0.prompt.md +266 -0
  188. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.4.0.prompt.md +266 -0
  189. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v3.3.0.prompt.md +1 -1
  190. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v3.4.0.prompt.md +137 -0
  191. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main/v1.2.0.prompt.md +1 -1
  192. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main/v1.3.0.prompt.md +34 -0
  193. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_update_instruction_start/v1.0.0.prompt.md +5 -12
  194. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_update_instruction_start/v1.1.0.prompt.md +5 -12
  195. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_update_instruction_start/v1.2.0.prompt.md +155 -0
  196. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/tagging/v1.0.0.prompt.md +23 -0
  197. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_manager.py +89 -32
  198. package/plugin/vendor/reflexio/reflexio/server/services/README.md +26 -11
  199. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/README.md +22 -0
  200. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/__init__.py +1 -0
  201. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/agent_success_evaluation_constants.py +4 -2
  202. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/agent_success_evaluation_utils.py +1 -0
  203. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/components/__init__.py +7 -0
  204. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/{agent_success_evaluator.py → components/evaluator.py} +3 -16
  205. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/regen_jobs.py +28 -72
  206. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/{group_evaluation_runner.py → runner.py} +12 -10
  207. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/{delayed_group_evaluator.py → scheduler.py} +10 -2
  208. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/{agent_success_evaluation_service.py → service.py} +36 -1
  209. package/plugin/vendor/reflexio/reflexio/server/services/base_generation_service.py +160 -0
  210. package/plugin/vendor/reflexio/reflexio/server/services/configurator/base_configurator.py +5 -0
  211. package/plugin/vendor/reflexio/reflexio/server/services/deduplication_utils.py +1 -1
  212. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/README.md +9 -0
  213. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/components/__init__.py +0 -0
  214. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/{rule_attribution.py → components/rule_attribution.py} +10 -9
  215. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/eval_sampler.py +15 -24
  216. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/service.py +238 -164
  217. package/plugin/vendor/reflexio/reflexio/server/services/extraction/README.md +31 -0
  218. package/plugin/vendor/reflexio/reflexio/server/services/extraction/outcome.py +13 -3
  219. package/plugin/vendor/reflexio/reflexio/server/services/extraction/pending_tool_call_dispatch.py +3 -2
  220. package/plugin/vendor/reflexio/reflexio/server/services/extraction/resumable_agent.py +32 -118
  221. package/plugin/vendor/reflexio/reflexio/server/services/extraction/resume_worker.py +39 -16
  222. package/plugin/vendor/reflexio/reflexio/server/services/generation_service.py +105 -23
  223. package/plugin/vendor/reflexio/reflexio/server/services/lineage/__init__.py +0 -0
  224. package/plugin/vendor/reflexio/reflexio/server/services/lineage/gc_scheduler.py +193 -0
  225. package/plugin/vendor/reflexio/reflexio/server/services/lineage/resolve.py +98 -0
  226. package/plugin/vendor/reflexio/reflexio/server/services/operation_state_utils.py +26 -0
  227. package/plugin/vendor/reflexio/reflexio/server/services/playbook/README.md +11 -12
  228. package/plugin/vendor/reflexio/reflexio/server/services/playbook/components/__init__.py +5 -0
  229. package/plugin/vendor/reflexio/reflexio/server/services/playbook/{playbook_aggregator.py → components/aggregator.py} +316 -178
  230. package/plugin/vendor/reflexio/reflexio/server/services/playbook/{playbook_consolidator.py → components/consolidator.py} +168 -62
  231. package/plugin/vendor/reflexio/reflexio/server/services/playbook/{playbook_extractor.py → components/extractor.py} +5 -1
  232. package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_edit_apply.py +74 -0
  233. package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_service_utils.py +33 -3
  234. package/plugin/vendor/reflexio/reflexio/server/services/playbook/{playbook_generation_service.py → service.py} +121 -19
  235. package/plugin/vendor/reflexio/reflexio/server/services/playbook/user_detail_stripping.py +84 -0
  236. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/README.md +10 -0
  237. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/models.py +2 -1
  238. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/optimizer.py +178 -30
  239. package/plugin/vendor/reflexio/reflexio/server/services/pre_retrieval/README.md +9 -0
  240. package/plugin/vendor/reflexio/reflexio/server/services/profile/components/__init__.py +17 -0
  241. package/plugin/vendor/reflexio/reflexio/server/services/profile/{profile_deduplicator.py → components/consolidator.py} +7 -6
  242. package/plugin/vendor/reflexio/reflexio/server/services/profile/{profile_extractor.py → components/extractor.py} +30 -22
  243. package/plugin/vendor/reflexio/reflexio/server/services/profile/profile_generation_service_utils.py +6 -13
  244. package/plugin/vendor/reflexio/reflexio/server/services/profile/{profile_generation_service.py → service.py} +32 -50
  245. package/plugin/vendor/reflexio/reflexio/server/services/reflection/__init__.py +4 -8
  246. package/plugin/vendor/reflexio/reflexio/server/services/reflection/components/__init__.py +7 -0
  247. package/plugin/vendor/reflexio/reflexio/server/services/reflection/reflection_service_utils.py +12 -1
  248. package/plugin/vendor/reflexio/reflexio/server/services/reflection/{reflection_service.py → service.py} +36 -16
  249. package/plugin/vendor/reflexio/reflexio/server/services/retrieval/relevance_floor.py +83 -6
  250. package/plugin/vendor/reflexio/reflexio/server/services/service_utils.py +36 -1
  251. package/plugin/vendor/reflexio/reflexio/server/services/shadow_comparison/README.md +8 -0
  252. package/plugin/vendor/reflexio/reflexio/server/services/shadow_comparison/outcome.py +1 -2
  253. package/plugin/vendor/reflexio/reflexio/server/services/storage/error.py +27 -0
  254. package/plugin/vendor/reflexio/reflexio/server/services/storage/retention.py +9 -9
  255. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/__init__.py +2 -0
  256. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_agent_run.py +4 -1
  257. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_base.py +642 -197
  258. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_extras.py +61 -104
  259. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_lineage.py +628 -0
  260. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_operations.py +38 -6
  261. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_playbook.py +978 -184
  262. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_profiles.py +519 -128
  263. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_requests.py +77 -3
  264. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_shadow_verdicts.py +23 -9
  265. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_stall_state.py +5 -2
  266. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/__init__.py +131 -20
  267. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_agent_run.py +2 -8
  268. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_base.py +5 -0
  269. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_extras.py +36 -53
  270. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_lineage.py +215 -0
  271. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_operations.py +20 -0
  272. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_playbook.py +243 -7
  273. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_profiles.py +121 -1
  274. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_requests.py +60 -0
  275. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_retrieval_log.py +51 -0
  276. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_shadow_verdicts.py +17 -0
  277. package/plugin/vendor/reflexio/reflexio/server/services/tagging/README.md +8 -0
  278. package/plugin/vendor/reflexio/reflexio/server/services/tagging/__init__.py +1 -0
  279. package/plugin/vendor/reflexio/reflexio/server/services/tagging/service.py +200 -0
  280. package/plugin/vendor/reflexio/reflexio/server/services/tagging/tagging_scheduler.py +149 -0
  281. package/plugin/vendor/reflexio/reflexio/server/services/unified_search_service.py +229 -32
  282. package/plugin/vendor/reflexio/reflexio/server/site_var/README.md +2 -1
  283. package/plugin/vendor/reflexio/reflexio/server/site_var/feature_flags.py +120 -1
  284. package/plugin/vendor/reflexio/reflexio/server/site_var/site_var_sources/feature_flags.json +4 -0
  285. package/plugin/vendor/reflexio/reflexio/server/site_var/site_var_sources/search_settings.json +5 -0
  286. package/plugin/vendor/reflexio/reflexio/server/tracing.py +30 -0
  287. package/plugin/vendor/reflexio/reflexio/server/usage_metrics.py +18 -0
  288. package/plugin/vendor/reflexio/reflexio/test_support/llm_mock.py +61 -26
  289. package/plugin/vendor/reflexio/reflexio/test_support/llm_model_registry.py +41 -4
  290. package/scripts/setup-claude-smart.sh +8 -3
  291. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/TESTING.md +0 -517
  292. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/hook/handler.js +0 -473
  293. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/package-lock.json +0 -2156
  294. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/package.json +0 -18
  295. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/hook/handler.ts +0 -241
  296. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/hook/setup.ts +0 -140
  297. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/publish.ts +0 -113
  298. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/search.ts +0 -52
  299. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/server.ts +0 -103
  300. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/sqlite-buffer.ts +0 -156
  301. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/user-id.ts +0 -134
  302. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/rules/reflexio.md +0 -24
  303. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/publish_clawhub.sh +0 -278
  304. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/references/HOOK.md +0 -164
  305. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/scripts/install.sh +0 -36
  306. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/scripts/uninstall.sh +0 -35
  307. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/publish.test.ts +0 -27
  308. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/search.test.ts +0 -31
  309. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/server.test.ts +0 -42
  310. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/setup.test.ts +0 -49
  311. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/sqlite-buffer.test.ts +0 -91
  312. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/user-id.test.ts +0 -50
  313. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/vitest.config.ts +0 -13
  314. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/group_aggregation.py +0 -192
  315. package/plugin/vendor/reflexio/reflexio/server/services/extraction/invariants.py +0 -303
  316. package/plugin/vendor/reflexio/reflexio/server/services/extraction/plan.py +0 -138
  317. package/plugin/vendor/reflexio/reflexio/server/services/extraction/tools.py +0 -1125
  318. /package/plugin/vendor/reflexio/reflexio/integrations/{__init__.py → openclaw/plugin/src/openclaw_smart/__init__.py} +0 -0
  319. /package/plugin/vendor/reflexio/reflexio/integrations/openclaw/{types → plugin/types}/openclaw.d.ts +0 -0
  320. /package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/{distribution.py → components/distribution.py} +0 -0
  321. /package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/{hero_state.py → components/hero_state.py} +0 -0
  322. /package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/{shadow_aggregation.py → components/shadow_aggregation.py} +0 -0
  323. /package/plugin/vendor/reflexio/reflexio/server/services/reflection/{reflection_extractor.py → components/extractor.py} +0 -0
@@ -0,0 +1,371 @@
1
+ # shellcheck shell=bash
2
+ # Shared helpers for openclaw-smart plugin scripts. Source, do not execute.
3
+
4
+ # openClaw hooks run with a minimal non-interactive PATH that often omits
5
+ # nvm/asdf/brew shims where `npm`, `uv`, etc. live. Pull the user's login-shell
6
+ # PATH the same way claude-mem does so hook-spawned scripts find them without
7
+ # the user having to mutate their global PATH. Best-effort — failures silent.
8
+ openclaw_smart_source_login_path() {
9
+ local _SHELL_PATH
10
+ if [ -n "${SHELL:-}" ] && [ -x "$SHELL" ]; then
11
+ if _SHELL_PATH="$("$SHELL" -lc 'printf %s "$PATH"' 2>/dev/null)"; then
12
+ [ -n "$_SHELL_PATH" ] && export PATH="$_SHELL_PATH:$PATH"
13
+ fi
14
+ fi
15
+ }
16
+
17
+ # Prepend the astral.sh installer's default bin directories to PATH so a
18
+ # freshly-installed `uv` is reachable before the user re-sources their
19
+ # shell rc. Prepend (not append) so the just-installed binary wins over
20
+ # any stale copy earlier in PATH. Literals only — no subshell, so safe
21
+ # under `set -u`.
22
+ openclaw_smart_prepend_astral_bins() {
23
+ export PATH="$HOME/.local/bin:$HOME/.cargo/bin:$PATH"
24
+ }
25
+
26
+ # Prepend openclaw-smart's private Node.js install, if present. The Setup
27
+ # hook installs Node here when the user does not already have a suitable
28
+ # node/npm on PATH, so later hook-spawned dashboard scripts can run without
29
+ # requiring nvm/brew/global npm to be visible from openClaw's shell.
30
+ openclaw_smart_prepend_node_bins() {
31
+ local _OC_NODE_ROOT
32
+ _OC_NODE_ROOT="$HOME/.openclaw-smart/node/current"
33
+ export PATH="$_OC_NODE_ROOT/bin:$_OC_NODE_ROOT:$PATH"
34
+ }
35
+
36
+ openclaw_smart_is_internal_invocation_env() {
37
+ if [ "${OPENCLAW_SMART_INTERNAL:-}" = "1" ]; then
38
+ return 0
39
+ fi
40
+ return 1
41
+ }
42
+
43
+ openclaw_smart_dashboard_unavailable_marker() {
44
+ printf '%s\n' "$HOME/.openclaw-smart/dashboard-unavailable"
45
+ }
46
+
47
+ openclaw_smart_node_recovery_hint() {
48
+ cat <<'EOF'
49
+ Recovery:
50
+ 1. Restart openClaw to let openclaw-smart retry its private Node.js install.
51
+ 2. If the retry is blocked by your network or OS policy, install Node.js 20.9+ manually:
52
+ EOF
53
+ if openclaw_smart_is_windows; then
54
+ cat <<'EOF'
55
+ - winget install OpenJS.NodeJS.LTS
56
+ - or download the LTS installer from https://nodejs.org/
57
+ EOF
58
+ elif [ "$(uname -s 2>/dev/null)" = "Darwin" ]; then
59
+ cat <<'EOF'
60
+ - brew install node
61
+ - or download the LTS installer from https://nodejs.org/
62
+ EOF
63
+ else
64
+ cat <<'EOF'
65
+ - use your distro package manager for nodejs/npm
66
+ - or download the LTS archive from https://nodejs.org/
67
+ EOF
68
+ fi
69
+ cat <<'EOF'
70
+ 3. Run /openclaw-smart:restart, then /openclaw-smart:dashboard.
71
+ EOF
72
+ }
73
+
74
+ openclaw_smart_write_dashboard_unavailable() {
75
+ local reason marker
76
+ reason="$1"
77
+ marker="$(openclaw_smart_dashboard_unavailable_marker)"
78
+ mkdir -p "$(dirname "$marker")"
79
+ {
80
+ printf 'openclaw-smart dashboard is unavailable: %s\n\n' "$reason"
81
+ printf 'The learning backend and hooks can still work; only the dashboard UI is affected.\n\n'
82
+ printf 'Private Node.js location: %s\n\n' "$HOME/.openclaw-smart/node/current"
83
+ openclaw_smart_node_recovery_hint
84
+ } > "$marker"
85
+ }
86
+
87
+ openclaw_smart_clear_dashboard_unavailable() {
88
+ rm -f "$(openclaw_smart_dashboard_unavailable_marker)"
89
+ }
90
+
91
+ # Return 0 (true) if running under a Windows-flavoured bash (Git Bash,
92
+ # MSYS, Cygwin). Used to gate POSIX-only primitives (setsid, process
93
+ # groups) and route around Windows-specific potholes (the python3 App
94
+ # Execution Alias stub at WindowsApps\python3.exe).
95
+ openclaw_smart_is_windows() {
96
+ case "$(uname -s 2>/dev/null)" in
97
+ MINGW*|MSYS*|CYGWIN*) return 0 ;;
98
+ *) return 1 ;;
99
+ esac
100
+ }
101
+
102
+ # Print the absolute path of a working python interpreter, or nothing
103
+ # (and return non-zero) if none is usable. On Windows, `python3` is
104
+ # usually the Microsoft Store "App Execution Alias" stub at
105
+ # %LocalAppData%\Microsoft\WindowsApps\python3.exe — `command -v python3`
106
+ # returns truthy but invoking it just prints a "Python was not found"
107
+ # message and exits non-zero. We probe with `-V` to filter the stub out
108
+ # and prefer `python` (the real interpreter when one is installed).
109
+ openclaw_smart_resolve_python() {
110
+ if openclaw_smart_is_windows; then
111
+ for cand in python python3; do
112
+ if command -v "$cand" >/dev/null 2>&1 && "$cand" -V >/dev/null 2>&1; then
113
+ command -v "$cand"
114
+ return 0
115
+ fi
116
+ done
117
+ return 1
118
+ fi
119
+ for cand in python3 python; do
120
+ if command -v "$cand" >/dev/null 2>&1 && "$cand" -V >/dev/null 2>&1; then
121
+ command -v "$cand"
122
+ return 0
123
+ fi
124
+ done
125
+ return 1
126
+ }
127
+
128
+ openclaw_smart_download() {
129
+ local url dest src _OC_PY
130
+ url="$1"
131
+ dest="$2"
132
+ mkdir -p "$(dirname "$dest")"
133
+ case "$url" in
134
+ file://*)
135
+ src="${url#file://}"
136
+ cp "$src" "$dest"
137
+ return $?
138
+ ;;
139
+ esac
140
+ if command -v curl >/dev/null 2>&1; then
141
+ curl -fsSL "$url" -o "$dest"
142
+ return $?
143
+ fi
144
+ if command -v wget >/dev/null 2>&1; then
145
+ wget -q -O "$dest" "$url"
146
+ return $?
147
+ fi
148
+ if command -v powershell >/dev/null 2>&1; then
149
+ URL="$url" DEST="$dest" powershell -NoProfile -ExecutionPolicy Bypass -Command \
150
+ '$ProgressPreference="SilentlyContinue"; Invoke-WebRequest -Uri $env:URL -OutFile $env:DEST'
151
+ return $?
152
+ fi
153
+ if command -v pwsh >/dev/null 2>&1; then
154
+ URL="$url" DEST="$dest" pwsh -NoProfile -Command \
155
+ '$ProgressPreference="SilentlyContinue"; Invoke-WebRequest -Uri $env:URL -OutFile $env:DEST'
156
+ return $?
157
+ fi
158
+ _OC_PY=$(openclaw_smart_resolve_python || true)
159
+ if [ -n "$_OC_PY" ]; then
160
+ "$_OC_PY" - "$url" "$dest" <<'PY'
161
+ import sys
162
+ import urllib.request
163
+
164
+ url, dest = sys.argv[1], sys.argv[2]
165
+ with urllib.request.urlopen(url, timeout=60) as response:
166
+ data = response.read()
167
+ with open(dest, "wb") as fh:
168
+ fh.write(data)
169
+ PY
170
+ return $?
171
+ fi
172
+ return 1
173
+ }
174
+
175
+ openclaw_smart_sha256_file() {
176
+ local path _OC_PY
177
+ path="$1"
178
+ if command -v sha256sum >/dev/null 2>&1; then
179
+ sha256sum "$path" | awk '{print $1}'
180
+ return "${PIPESTATUS[0]}"
181
+ fi
182
+ if command -v shasum >/dev/null 2>&1; then
183
+ shasum -a 256 "$path" | awk '{print $1}'
184
+ return "${PIPESTATUS[0]}"
185
+ fi
186
+ if command -v openssl >/dev/null 2>&1; then
187
+ openssl dgst -sha256 "$path" | awk '{print $NF}'
188
+ return "${PIPESTATUS[0]}"
189
+ fi
190
+ _OC_PY=$(openclaw_smart_resolve_python || true)
191
+ if [ -n "$_OC_PY" ]; then
192
+ "$_OC_PY" - "$path" <<'PY'
193
+ import hashlib
194
+ import sys
195
+
196
+ h = hashlib.sha256()
197
+ with open(sys.argv[1], "rb") as fh:
198
+ for chunk in iter(lambda: fh.read(1024 * 1024), b""):
199
+ h.update(chunk)
200
+ print(h.hexdigest())
201
+ PY
202
+ return $?
203
+ fi
204
+ return 1
205
+ }
206
+
207
+ # Return 0 if `node` exists and satisfies the minimum major/minor pair.
208
+ # Patch versions are intentionally ignored because our requirement is a
209
+ # floor, not an exact runtime pin.
210
+ openclaw_smart_node_satisfies() {
211
+ local min_major min_minor version major minor
212
+ min_major="$1"
213
+ min_minor="$2"
214
+ command -v node >/dev/null 2>&1 || return 1
215
+ version=$(node -v 2>/dev/null | sed 's/^v//') || return 1
216
+ major=$(printf '%s' "$version" | awk -F. '{print $1}')
217
+ minor=$(printf '%s' "$version" | awk -F. '{print $2}')
218
+ [ -n "$major" ] && [ -n "$minor" ] || return 1
219
+ case "$major:$minor" in
220
+ *[!0-9:]*|:*|*:) return 1 ;;
221
+ esac
222
+ [ "$major" -gt "$min_major" ] && return 0
223
+ [ "$major" -eq "$min_major" ] && [ "$minor" -ge "$min_minor" ]
224
+ }
225
+
226
+ openclaw_smart_resolve_npm() {
227
+ local cand
228
+ for cand in npm npm.cmd; do
229
+ if command -v "$cand" >/dev/null 2>&1; then
230
+ command -v "$cand"
231
+ return 0
232
+ fi
233
+ done
234
+ return 1
235
+ }
236
+
237
+ openclaw_smart_npm_available() {
238
+ local npm_bin
239
+ npm_bin=$(openclaw_smart_resolve_npm || true)
240
+ [ -n "$npm_bin" ] || return 1
241
+ "$npm_bin" --version >/dev/null 2>&1
242
+ }
243
+
244
+ openclaw_smart_log_max_bytes() {
245
+ printf '%s\n' "10000000"
246
+ }
247
+
248
+ openclaw_smart_trim_log_file() {
249
+ local file max_bytes size tmp
250
+ file="$1"
251
+ max_bytes="${2:-$(openclaw_smart_log_max_bytes)}"
252
+ case "$max_bytes" in
253
+ ''|*[!0-9]*) return 0 ;;
254
+ esac
255
+ [ -f "$file" ] || return 0
256
+ size=$(wc -c < "$file" 2>/dev/null | tr -d '[:space:]') || return 0
257
+ case "$size" in
258
+ ''|*[!0-9]*) return 0 ;;
259
+ esac
260
+ [ "$size" -le "$max_bytes" ] && return 0
261
+
262
+ tmp="${file}.trim.$$"
263
+ if tail -c "$max_bytes" "$file" > "$tmp" 2>/dev/null; then
264
+ # Rewrite the existing path instead of replacing it, so a process with
265
+ # this file already open in append mode keeps writing to the capped file.
266
+ cat "$tmp" > "$file"
267
+ fi
268
+ rm -f "$tmp"
269
+ }
270
+
271
+ openclaw_smart_append_capped_log() {
272
+ local file max_bytes
273
+ file="$1"
274
+ max_bytes="${2:-$(openclaw_smart_log_max_bytes)}"
275
+ shift 2
276
+ mkdir -p "$(dirname "$file")"
277
+ openclaw_smart_trim_log_file "$file" "$max_bytes"
278
+ printf '%s\n' "$*" >> "$file"
279
+ openclaw_smart_trim_log_file "$file" "$max_bytes"
280
+ }
281
+
282
+ # Spawn a command fully detached from the current shell so a hook timeout
283
+ # (openClaw's install/SessionStart budget) cannot kill it mid-flight.
284
+ # POSIX: setsid → python3 os.setsid → nohup (in that order of strength).
285
+ # Windows: nohup alone — Git Bash has no setsid, no process groups, and
286
+ # `os.setsid()` is POSIX-only; nohup ignores SIGHUP which is enough to
287
+ # survive the parent console closing. The python3 fallback is gated on a
288
+ # real-interpreter probe (-V) so the Windows App Execution Alias stub
289
+ # doesn't get invoked. Caller is responsible for redirecting stdout/stderr;
290
+ # we do not impose a log destination here. Stdin is closed so the child
291
+ # cannot inherit a tty. Use `$!` after this call to capture the pid.
292
+ openclaw_smart_spawn_detached() {
293
+ if openclaw_smart_is_windows; then
294
+ nohup "$@" < /dev/null &
295
+ return 0
296
+ fi
297
+ if command -v setsid >/dev/null 2>&1; then
298
+ setsid nohup "$@" < /dev/null &
299
+ elif _OC_PY=$(openclaw_smart_resolve_python) && [ -n "$_OC_PY" ]; then
300
+ "$_OC_PY" -c 'import os,sys; os.setsid(); os.execvp(sys.argv[1], sys.argv[1:])' \
301
+ "$@" < /dev/null &
302
+ else
303
+ nohup "$@" < /dev/null &
304
+ fi
305
+ }
306
+
307
+ # Terminate a process and (on POSIX) its whole process group, escalating
308
+ # from TERM to KILL after a short grace period. On Windows there are no
309
+ # POSIX process groups, so we use `taskkill /T /F /PID` which walks the
310
+ # child-process tree via the Windows job-object/parent-pid relationships
311
+ # — the closest equivalent to a group kill.
312
+ #
313
+ # Windows-specific subtlety: in Git Bash / MSYS, `$!` for a backgrounded
314
+ # job returns the MSYS pid (an internal counter), NOT the native Windows
315
+ # pid that taskkill needs. `ps -W` (or `-o winpid=`) exposes the WINPID
316
+ # column for the translation. If the lookup fails we fall back to
317
+ # treating the input as a native pid, so callers can pass either an MSYS
318
+ # pid (recorded via $!) or a Windows pid (from tasklist) interchangeably.
319
+ # The `//T //F //PID` syntax escapes Git Bash's MSYS path-mangling of
320
+ # arguments that begin with `/`.
321
+ openclaw_smart_kill_tree() {
322
+ pid="$1"
323
+ [ -z "$pid" ] && return 0
324
+ if openclaw_smart_is_windows; then
325
+ # Git Bash's `ps` is the procps fork, not BSD/Linux ps; it has no
326
+ # -o option but its default header is `PID PPID PGID WINPID TTY ...`,
327
+ # so column 4 of the data row is the Windows pid. awk extracts it
328
+ # without depending on -o support.
329
+ target=""
330
+ if command -v ps >/dev/null 2>&1; then
331
+ target=$(ps -p "$pid" 2>/dev/null | awk 'NR==2 {print $4}' | tr -d ' \r\n' || true)
332
+ fi
333
+ [ -z "$target" ] && target="$pid"
334
+ if command -v taskkill >/dev/null 2>&1; then
335
+ taskkill //T //F //PID "$target" >/dev/null 2>&1 || true
336
+ else
337
+ kill -TERM "$pid" 2>/dev/null || true
338
+ sleep 0.5
339
+ kill -KILL "$pid" 2>/dev/null || true
340
+ fi
341
+ return 0
342
+ fi
343
+ current_pgid=""
344
+ if command -v ps >/dev/null 2>&1; then
345
+ current_pgid=$(ps -o pgid= -p "$$" 2>/dev/null | tr -d ' ')
346
+ fi
347
+ if [ -n "$current_pgid" ] && [ "$pid" = "$current_pgid" ]; then
348
+ return 0
349
+ fi
350
+ if ! kill -TERM -- "-$pid" 2>/dev/null; then
351
+ kill -TERM "$pid" 2>/dev/null || true
352
+ sleep 0.5
353
+ kill -KILL "$pid" 2>/dev/null || true
354
+ return 0
355
+ fi
356
+ for _ in 1 2 3 4 5; do
357
+ kill -0 -- "-$pid" 2>/dev/null || return 0
358
+ sleep 0.2
359
+ done
360
+ kill -KILL -- "-$pid" 2>/dev/null || true
361
+ }
362
+
363
+ # Return 0 (true) if $1 names a pid file whose pid is currently alive.
364
+ # Silent on missing/empty/stale files.
365
+ openclaw_smart_pid_alive_file() {
366
+ pid_file="$1"
367
+ [ -f "$pid_file" ] || return 1
368
+ pid=$(cat "$pid_file" 2>/dev/null || echo "")
369
+ [ -n "$pid" ] || return 1
370
+ kill -0 "$pid" 2>/dev/null
371
+ }
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/env bash
2
+ # Run the backend command and stream its stdout/stderr into a capped log file.
3
+ set -eu
4
+
5
+ HERE="$(cd "$(dirname "$0")" && pwd)"
6
+ # shellcheck source=_lib.sh
7
+ . "$HERE/_lib.sh"
8
+
9
+ if [ "$#" -lt 4 ] || [ "$3" != "--" ]; then
10
+ exit 2
11
+ fi
12
+
13
+ LOG_FILE="$1"
14
+ MAX_BYTES="$2"
15
+ shift 3
16
+
17
+ mkdir -p "$(dirname "$LOG_FILE")"
18
+ openclaw_smart_trim_log_file "$LOG_FILE" "$MAX_BYTES"
19
+
20
+ STATUS_FILE="${TMPDIR:-/tmp}/openclaw-smart-backend-log-runner.$$.status"
21
+ rm -f "$STATUS_FILE"
22
+
23
+ (
24
+ set +e
25
+ "$@" 2>&1
26
+ printf '%s\n' "$?" > "$STATUS_FILE"
27
+ ) | while IFS= read -r line || [ -n "$line" ]; do
28
+ openclaw_smart_append_capped_log "$LOG_FILE" "$MAX_BYTES" "$line"
29
+ done
30
+
31
+ status=$(cat "$STATUS_FILE" 2>/dev/null || printf '1')
32
+ rm -f "$STATUS_FILE"
33
+ exit "$status"
@@ -0,0 +1,271 @@
1
+ #!/usr/bin/env bash
2
+ # Auto-start the reflexio FastAPI backend (port 8071) if it's not already
3
+ # running. Detached spawn, returns immediately so the SessionStart hook
4
+ # doesn't block the session.
5
+ #
6
+ # Port choice: openclaw-smart shares 8071/8072 with the sibling claude-smart
7
+ # plugin so the two plugins coexist on a single shared backend (one SQLite
8
+ # store, one extractor process) — not reflexio's 8061 default, which is
9
+ # reserved for a developer's own local reflexio instance.
10
+ #
11
+ # Subcommands:
12
+ # start probe /health; if nothing we recognize is on the port,
13
+ # spawn `uv run reflexio services start --only backend
14
+ # --no-reload` detached. Polls /health briefly so first
15
+ # use after session start lands on a warm server, then
16
+ # returns empty stdout regardless.
17
+ # stop SIGTERM the recorded process group, escalating to
18
+ # SIGKILL after a short grace period.
19
+ # session-end no-op by default; only stops the backend if
20
+ # OPENCLAW_SMART_BACKEND_STOP_ON_END=1 (opt-in — the
21
+ # backend is intended to be long-lived across sessions).
22
+ # status print "running on http://localhost:PORT" or "not running".
23
+ set -eu
24
+
25
+ HERE="$(cd "$(dirname "$0")" && pwd)"
26
+ # shellcheck source=_lib.sh
27
+ . "$HERE/_lib.sh"
28
+ openclaw_smart_source_login_path
29
+ openclaw_smart_prepend_astral_bins
30
+
31
+ CMD="${1:-start}"
32
+ PORT=8071
33
+ EMBEDDING_PORT="${EMBEDDING_PORT:-8072}"
34
+ # Pass through to `reflexio services start/stop` so the spawned backend
35
+ # binds to PORT.
36
+ export BACKEND_PORT="$PORT"
37
+ export EMBEDDING_PORT
38
+
39
+ # Default: route extraction through the active host CLI + ONNX embedder
40
+ # so openclaw-smart works without any LLM API key. Users can opt out by
41
+ # pre-exporting these to 0.
42
+ export OPENCLAW_SMART_USE_LOCAL_CLI="${OPENCLAW_SMART_USE_LOCAL_CLI:-1}"
43
+ export OPENCLAW_SMART_USE_LOCAL_EMBEDDING="${OPENCLAW_SMART_USE_LOCAL_EMBEDDING:-1}"
44
+ if [ "${OPENCLAW_SMART_USE_LOCAL_EMBEDDING:-}" = "1" ]; then
45
+ export REFLEXIO_EMBEDDING_PROVIDER="${REFLEXIO_EMBEDDING_PROVIDER:-local_service}"
46
+ export REFLEXIO_EMBEDDING_SERVICE_URL="${REFLEXIO_EMBEDDING_SERVICE_URL:-http://127.0.0.1:$EMBEDDING_PORT}"
47
+ fi
48
+ PLUGIN_ROOT="$(cd "$HERE/.." && pwd)"
49
+
50
+ # Pin the openclaw CLI explicitly so the reflexio backend's openclaw_provider
51
+ # can find it from a hook context whose PATH lacks the user's normal CLI dir.
52
+ if [ -z "${OPENCLAW_BIN:-}" ]; then
53
+ if _oc_cli_path=$(command -v openclaw 2>/dev/null) && [ -n "$_oc_cli_path" ]; then
54
+ export OPENCLAW_BIN="$_oc_cli_path"
55
+ elif [ -x "$HOME/.local/bin/openclaw" ]; then
56
+ export OPENCLAW_BIN="$HOME/.local/bin/openclaw"
57
+ fi
58
+ unset _oc_cli_path
59
+ fi
60
+
61
+ STATE_DIR="$HOME/.openclaw-smart"
62
+ PID_FILE="$STATE_DIR/backend.pid"
63
+ LOG_FILE="$STATE_DIR/backend.log"
64
+ LOG_MAX_BYTES="$(openclaw_smart_log_max_bytes)"
65
+ mkdir -p "$STATE_DIR"
66
+ openclaw_smart_trim_log_file "$LOG_FILE" "$LOG_MAX_BYTES"
67
+
68
+ emit_ok() { echo ''; }
69
+
70
+ emit_start_failure() {
71
+ reason="$1"
72
+ if py=$(openclaw_smart_resolve_python 2>/dev/null); then
73
+ "$py" - "$reason" <<'PY'
74
+ import json
75
+ import sys
76
+
77
+ reason = sys.argv[1].strip()
78
+ message = (
79
+ "> **openclaw-smart learning backend is not running.** "
80
+ "Interactions are being buffered locally, but learning will not publish "
81
+ "until the backend starts.\n"
82
+ )
83
+ if reason:
84
+ message += f">\n> Last startup error: `{reason}`\n"
85
+ message += (
86
+ ">\n> Make sure the openclaw CLI is on PATH so the local model provider "
87
+ "can be reached. Then run `/openclaw-smart:restart`."
88
+ )
89
+ print(json.dumps({"prependContext": message}))
90
+ PY
91
+ else
92
+ emit_ok
93
+ fi
94
+ }
95
+
96
+ # Tree-kill the recorded process.
97
+ kill_group() {
98
+ openclaw_smart_kill_tree "$1"
99
+ }
100
+
101
+ # True if /health returns 200. Reflexio's /health is a plain GET with no
102
+ # marker header, so we can't distinguish our backend from someone else's
103
+ # reflexio on the same port — if you run two reflexio instances on $PORT
104
+ # you'll get collision regardless of what we do here.
105
+ backend_healthy() {
106
+ command -v curl >/dev/null 2>&1 || return 1
107
+ curl -sf -o /dev/null "http://127.0.0.1:$PORT/health" 2>/dev/null
108
+ }
109
+
110
+ # True only if the recorded PID is alive AND /health responds. A stale
111
+ # PID file from a crashed backend is not enough — we must see the port
112
+ # actually answer, so next hook retries cleanly.
113
+ is_our_backend_running() {
114
+ if [ -f "$PID_FILE" ]; then
115
+ pid=$(cat "$PID_FILE" 2>/dev/null || echo "")
116
+ if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
117
+ backend_healthy && return 0
118
+ fi
119
+ fi
120
+ # Recover from a missing PID file if a foreign-but-functional reflexio
121
+ # is already serving — no need to start a second one.
122
+ backend_healthy && return 0
123
+ return 1
124
+ }
125
+
126
+ # True if *anything* is listening on the port (even non-HTTP). Used to
127
+ # avoid stomping on a foreign listener with a failed-to-start uvicorn.
128
+ port_occupied() {
129
+ (echo >"/dev/tcp/127.0.0.1/$PORT") 2>/dev/null
130
+ }
131
+
132
+ # Reap any reflexio/uvicorn listener still holding $PORT after the PID
133
+ # file kill. Filters by cmdline so we don't knock over an unrelated
134
+ # service a user has bound to $PORT — symmetric with start's refusal to
135
+ # stomp on a foreign listener. Silent on failure.
136
+ reap_port_listeners() {
137
+ command -v lsof >/dev/null 2>&1 || return 0
138
+ candidates=$(lsof -ti:"$PORT" 2>/dev/null) || candidates=""
139
+ [ -z "$candidates" ] && return 0
140
+ ours=""
141
+ for pid in $candidates; do
142
+ cmdline=$(ps -p "$pid" -o command= 2>/dev/null || true)
143
+ case "$cmdline" in
144
+ *reflexio*|*uvicorn*) ours="$ours $pid" ;;
145
+ esac
146
+ done
147
+ [ -z "$ours" ] && return 0
148
+ # shellcheck disable=SC2086
149
+ kill -TERM $ours 2>/dev/null || true
150
+ sleep 1
151
+ remaining=""
152
+ for pid in $ours; do
153
+ kill -0 "$pid" 2>/dev/null && remaining="$remaining $pid"
154
+ done
155
+ [ -z "$remaining" ] && return 0
156
+ # shellcheck disable=SC2086
157
+ kill -KILL $remaining 2>/dev/null || true
158
+ }
159
+
160
+ # Full shutdown: kill the recorded process group (if any) then sweep the
161
+ # port for surviving reflexio listeners. Used by both `stop` and the
162
+ # opt-in `session-end` path so a stale/missing PID file doesn't produce
163
+ # a silent no-op.
164
+ full_stop() {
165
+ if [ -f "$PID_FILE" ]; then
166
+ kill_group "$(cat "$PID_FILE" 2>/dev/null)"
167
+ rm -f "$PID_FILE"
168
+ fi
169
+ reap_port_listeners
170
+ }
171
+
172
+ case "$CMD" in
173
+ start)
174
+ if openclaw_smart_is_internal_invocation_env; then
175
+ emit_ok; exit 0
176
+ fi
177
+ # Opt-out: users who don't want the backend managed by the hook can
178
+ # set OPENCLAW_SMART_BACKEND_AUTOSTART=0.
179
+ if [ "${OPENCLAW_SMART_BACKEND_AUTOSTART:-1}" = "0" ]; then
180
+ emit_ok; exit 0
181
+ fi
182
+ if is_our_backend_running; then emit_ok; exit 0; fi
183
+ if port_occupied; then
184
+ # Something answered the TCP probe but /health didn't — don't
185
+ # start a second uvicorn on top of it.
186
+ openclaw_smart_append_capped_log "$LOG_FILE" "$LOG_MAX_BYTES" "[openclaw-smart] backend: port $PORT held by another process; skipping"
187
+ emit_ok; exit 0
188
+ fi
189
+ if ! command -v uv >/dev/null 2>&1; then
190
+ if [ "${OPENCLAW_SMART_BOOTSTRAPPING:-}" != "1" ] && [ -x "$PLUGIN_ROOT/scripts/smart-install.sh" ]; then
191
+ openclaw_smart_append_capped_log "$LOG_FILE" "$LOG_MAX_BYTES" "[openclaw-smart] backend: uv not on PATH; running installer"
192
+ OPENCLAW_SMART_BOOTSTRAPPING=1 bash "$PLUGIN_ROOT/scripts/smart-install.sh" >>"$STATE_DIR/install.log" 2>&1 || true
193
+ openclaw_smart_source_login_path
194
+ openclaw_smart_prepend_astral_bins
195
+ fi
196
+ if ! command -v uv >/dev/null 2>&1; then
197
+ openclaw_smart_append_capped_log "$LOG_FILE" "$LOG_MAX_BYTES" "[openclaw-smart] backend: uv not on PATH after installer; skipping"
198
+ emit_ok; exit 0
199
+ fi
200
+ fi
201
+ # The reflexio project root lives four levels above the plugin dir:
202
+ # plugin/ -> integrations/openclaw -> reflexio/integrations -> reflexio -> open_source/reflexio
203
+ REFLEXIO_PROJECT="$(cd "$PLUGIN_ROOT/../../../.." && pwd)"
204
+ cd "$REFLEXIO_PROJECT"
205
+
206
+ # Cap local interaction history to keep the SQLite store small for
207
+ # openclaw-smart users. Reflexio's library defaults are much higher
208
+ # (250k/50k) for server deployments; here we override only in the
209
+ # openclaw-smart plugin context. Users can still override via env.
210
+ export INTERACTION_CLEANUP_THRESHOLD="${INTERACTION_CLEANUP_THRESHOLD:-500}"
211
+ export INTERACTION_CLEANUP_DELETE_COUNT="${INTERACTION_CLEANUP_DELETE_COUNT:-200}"
212
+
213
+ # backend-log-runner.sh owns stdout/stderr capture so process output
214
+ # cannot grow backend.log past its cap.
215
+ #
216
+ # --workers: reflexio defaults to 2 (zero-downtime worker recycling
217
+ # for server deployments). For a single-user openClaw plugin that's
218
+ # pure overhead: ~1.1 GB extra RSS, periodic 5–10 s spawn hiccups
219
+ # during worker rotation, and SQLite can't accept concurrent writers
220
+ # anyway. Default to 1 here; opt in to N via
221
+ # OPENCLAW_SMART_BACKEND_WORKERS for power users running concurrent
222
+ # openClaw sessions or wanting zero-downtime recycling.
223
+ workers="${OPENCLAW_SMART_BACKEND_WORKERS:-1}"
224
+ openclaw_smart_spawn_detached bash "$HERE/backend-log-runner.sh" \
225
+ "$LOG_FILE" "$LOG_MAX_BYTES" -- \
226
+ uv run --project "$REFLEXIO_PROJECT" --quiet \
227
+ reflexio services start --only backend --no-reload --workers "$workers"
228
+ svc_pid=$!
229
+ # Record the spawned pid, not a pgid sampled with ps. On POSIX,
230
+ # setsid/python os.setsid make this pid the new process group leader;
231
+ # sampling immediately can race and capture the caller's pgid instead.
232
+ echo "$svc_pid" > "$PID_FILE"
233
+
234
+ # Give uvicorn up to ~10s to answer /health. The very first boot
235
+ # after a fresh checkout may be slower (LiteLLM import, chromadb
236
+ # warmup). We always return ok; the backend catches up in background
237
+ # if it needs to.
238
+ for _ in 1 2 3 4 5 6 7 8 9 10; do
239
+ backend_healthy && break
240
+ sleep 1
241
+ done
242
+ if ! backend_healthy; then
243
+ pid=$(cat "$PID_FILE" 2>/dev/null || echo "")
244
+ if [ -n "$pid" ] && ! kill -0 "$pid" 2>/dev/null; then
245
+ reason=$(tail -n 120 "$LOG_FILE" 2>/dev/null | grep -E "No LLM provider available|No generation-capable LLM provider available|CLI not found|skipping provider registration|Application startup failed" | tail -n 1 | sed 's/^[[:space:]]*//')
246
+ emit_start_failure "$reason"
247
+ exit 0
248
+ fi
249
+ fi
250
+ emit_ok
251
+ ;;
252
+ stop)
253
+ full_stop
254
+ emit_ok
255
+ ;;
256
+ session-end)
257
+ # Default: leave the backend running so learning keeps flowing
258
+ # between sessions. Opt in to teardown with
259
+ # OPENCLAW_SMART_BACKEND_STOP_ON_END=1.
260
+ if [ "${OPENCLAW_SMART_BACKEND_STOP_ON_END:-0}" = "1" ]; then
261
+ full_stop
262
+ fi
263
+ emit_ok
264
+ ;;
265
+ status)
266
+ if is_our_backend_running; then echo "running on http://localhost:$PORT"; else echo "not running"; fi
267
+ ;;
268
+ *)
269
+ emit_ok
270
+ ;;
271
+ esac