claude-smart 0.2.45 → 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 (322) 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 +5 -1
  35. package/plugin/scripts/backend-service.sh +22 -2
  36. package/plugin/scripts/ensure-plugin-root.sh +4 -4
  37. package/plugin/scripts/hook_entry.sh +1 -1
  38. package/plugin/scripts/opencode-claude-compat +4 -0
  39. package/plugin/scripts/opencode-claude-compat.cmd +3 -0
  40. package/plugin/scripts/opencode-claude-compat.js +225 -0
  41. package/plugin/scripts/smart-install.sh +10 -8
  42. package/plugin/src/README.md +1 -1
  43. package/plugin/src/claude_smart/cli.py +304 -6
  44. package/plugin/src/claude_smart/env_config.py +12 -4
  45. package/plugin/src/claude_smart/events/session_start.py +26 -7
  46. package/plugin/src/claude_smart/events/stop.py +1 -1
  47. package/plugin/src/claude_smart/ids.py +1 -1
  48. package/plugin/src/claude_smart/reflexio_adapter.py +5 -5
  49. package/plugin/src/claude_smart/runtime.py +7 -1
  50. package/plugin/uv.lock +1 -1
  51. package/plugin/vendor/reflexio/.env.example +54 -30
  52. package/plugin/vendor/reflexio/README.md +14 -8
  53. package/plugin/vendor/reflexio/pyproject.toml +13 -1
  54. package/plugin/vendor/reflexio/reflexio/README.md +1 -0
  55. package/plugin/vendor/reflexio/reflexio/cli/README.md +9 -7
  56. package/plugin/vendor/reflexio/reflexio/cli/__main__.py +10 -1
  57. package/plugin/vendor/reflexio/reflexio/cli/bootstrap_config.py +22 -5
  58. package/plugin/vendor/reflexio/reflexio/cli/commands/interactions.py +34 -4
  59. package/plugin/vendor/reflexio/reflexio/cli/commands/profiles.py +12 -7
  60. package/plugin/vendor/reflexio/reflexio/cli/commands/services.py +13 -6
  61. package/plugin/vendor/reflexio/reflexio/cli/commands/setup_cmd.py +210 -50
  62. package/plugin/vendor/reflexio/reflexio/cli/commands/shortcuts.py +15 -2
  63. package/plugin/vendor/reflexio/reflexio/cli/env_loader.py +292 -22
  64. package/plugin/vendor/reflexio/reflexio/cli/log_format.py +29 -4
  65. package/plugin/vendor/reflexio/reflexio/cli/run_services.py +17 -8
  66. package/plugin/vendor/reflexio/reflexio/cli/stop_services.py +13 -10
  67. package/plugin/vendor/reflexio/reflexio/client/client.py +57 -13
  68. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/README.md +135 -257
  69. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/npm/openclaw-smart/bin/openclaw-smart.js +13 -0
  70. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/npm/openclaw-smart/package.json +15 -0
  71. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/README.md +38 -0
  72. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/index.ts +151 -110
  73. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/openclaw.plugin.json +20 -10
  74. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/package.json +28 -6
  75. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/pyproject.toml +42 -0
  76. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/_lib.sh +371 -0
  77. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/backend-log-runner.sh +33 -0
  78. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/backend-service.sh +271 -0
  79. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/cli.sh +68 -0
  80. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/dashboard-open.sh +15 -0
  81. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/ensure-plugin-root.sh +84 -0
  82. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/hook_entry.sh +106 -0
  83. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/npm-cli.js +219 -0
  84. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/smart-install.sh +269 -0
  85. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/clear-all/SKILL.md +8 -0
  86. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/dashboard/SKILL.md +8 -0
  87. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/learn/SKILL.md +10 -0
  88. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/reflexio/SKILL.md +15 -44
  89. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/restart/SKILL.md +6 -0
  90. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/show/SKILL.md +8 -0
  91. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/cli.py +634 -0
  92. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/context_format.py +224 -0
  93. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/context_inject.py +79 -0
  94. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/__init__.py +0 -0
  95. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/after_tool_call.py +160 -0
  96. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/agent_end.py +187 -0
  97. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/before_prompt_build.py +69 -0
  98. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/before_tool_call.py +30 -0
  99. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/session_end.py +36 -0
  100. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/session_start.py +130 -0
  101. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/hook.py +131 -0
  102. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/ids.py +94 -0
  103. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/internal_call.py +75 -0
  104. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/oc_cite.py +196 -0
  105. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/optimizer_assistant.py +272 -0
  106. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/publish.py +96 -0
  107. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/query_compose.py +66 -0
  108. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/reflexio_adapter.py +336 -0
  109. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/runtime.py +47 -0
  110. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/stall_banner.py +61 -0
  111. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/state.py +323 -0
  112. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/__init__.py +0 -0
  113. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/integration/__init__.py +0 -0
  114. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/integration/test_e2e_session_loop.py +190 -0
  115. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/integration/test_publish_to_local_reflexio_integration.py +112 -0
  116. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/integration/test_recursion_guard_integration.py +86 -0
  117. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/integration/test_search_inject_integration.py +144 -0
  118. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_cli.py +184 -0
  119. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_events_after_tool_call.py +142 -0
  120. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_events_agent_end.py +233 -0
  121. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_events_before_prompt_build.py +116 -0
  122. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_events_before_tool_call.py +35 -0
  123. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_events_session_end.py +47 -0
  124. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_events_session_start.py +109 -0
  125. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_hook_dispatch.py +117 -0
  126. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_ids.py +41 -0
  127. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_internal_call.py +50 -0
  128. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_oc_cite.py +88 -0
  129. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_publish.py +48 -0
  130. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_query_compose.py +48 -0
  131. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_reflexio_adapter.py +188 -0
  132. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_runtime.py +41 -0
  133. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_state.py +235 -0
  134. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests-ts/__mocks__/plugin-entry-stub.ts +6 -0
  135. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests-ts/test_npm_cli.test.ts +56 -0
  136. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests-ts/test_shim_dispatch.test.ts +171 -0
  137. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tsconfig.build.json +18 -0
  138. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/{tsconfig.json → plugin/tsconfig.json} +2 -2
  139. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/uv.lock +3835 -0
  140. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/vitest.config.ts +18 -0
  141. package/plugin/vendor/reflexio/reflexio/lib/_agent_playbook.py +238 -10
  142. package/plugin/vendor/reflexio/reflexio/lib/_config.py +11 -4
  143. package/plugin/vendor/reflexio/reflexio/lib/_generation.py +16 -5
  144. package/plugin/vendor/reflexio/reflexio/lib/_lineage_parity_readers.py +187 -0
  145. package/plugin/vendor/reflexio/reflexio/lib/_profiles.py +198 -6
  146. package/plugin/vendor/reflexio/reflexio/lib/_reflection.py +1 -1
  147. package/plugin/vendor/reflexio/reflexio/lib/_search.py +29 -20
  148. package/plugin/vendor/reflexio/reflexio/lib/_user_playbook.py +2 -1
  149. package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/entities.py +127 -38
  150. package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/enums.py +4 -0
  151. package/plugin/vendor/reflexio/reflexio/models/api_schema/eval_overview_schema.py +64 -41
  152. package/plugin/vendor/reflexio/reflexio/models/api_schema/internal_schema.py +19 -0
  153. package/plugin/vendor/reflexio/reflexio/models/api_schema/pending_tool_call_schema.py +12 -5
  154. package/plugin/vendor/reflexio/reflexio/models/api_schema/retriever_schema.py +52 -0
  155. package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/converters.py +5 -1
  156. package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/entities.py +5 -1
  157. package/plugin/vendor/reflexio/reflexio/models/config_schema.py +88 -7
  158. package/plugin/vendor/reflexio/reflexio/models/structured_output.py +148 -0
  159. package/plugin/vendor/reflexio/reflexio/server/OVERVIEW.md +3 -3
  160. package/plugin/vendor/reflexio/reflexio/server/README.md +45 -32
  161. package/plugin/vendor/reflexio/reflexio/server/__init__.py +29 -5
  162. package/plugin/vendor/reflexio/reflexio/server/__main__.py +2 -2
  163. package/plugin/vendor/reflexio/reflexio/server/_auth.py +65 -2
  164. package/plugin/vendor/reflexio/reflexio/server/api.py +734 -85
  165. package/plugin/vendor/reflexio/reflexio/server/api_endpoints/precondition_checks.py +7 -0
  166. package/plugin/vendor/reflexio/reflexio/server/api_endpoints/request_context.py +3 -1
  167. package/plugin/vendor/reflexio/reflexio/server/billing_meter.py +173 -0
  168. package/plugin/vendor/reflexio/reflexio/server/billing_signals.py +64 -0
  169. package/plugin/vendor/reflexio/reflexio/server/cache/reflexio_cache.py +46 -16
  170. package/plugin/vendor/reflexio/reflexio/server/env_utils.py +65 -0
  171. package/plugin/vendor/reflexio/reflexio/server/llm/embedding_service.py +19 -4
  172. package/plugin/vendor/reflexio/reflexio/server/llm/litellm_client.py +264 -77
  173. package/plugin/vendor/reflexio/reflexio/server/llm/llm_utils.py +69 -7
  174. package/plugin/vendor/reflexio/reflexio/server/llm/providers/embedding_service_provider.py +75 -16
  175. package/plugin/vendor/reflexio/reflexio/server/llm/providers/openclaw_provider.py +280 -0
  176. package/plugin/vendor/reflexio/reflexio/server/llm/token_accounting.py +48 -0
  177. package/plugin/vendor/reflexio/reflexio/server/llm/tools.py +107 -17
  178. package/plugin/vendor/reflexio/reflexio/server/operation_limiter.py +360 -4
  179. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.6.0.prompt.md +1 -1
  180. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.7.0.prompt.md +85 -0
  181. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_aggregation/v2.2.0.prompt.md +1 -1
  182. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_aggregation/v2.3.0.prompt.md +253 -0
  183. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.3.2.prompt.md +1 -1
  184. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.3.3.prompt.md +74 -0
  185. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.2.3.prompt.md +1 -1
  186. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.3.0.prompt.md +266 -0
  187. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.4.0.prompt.md +266 -0
  188. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v3.3.0.prompt.md +1 -1
  189. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v3.4.0.prompt.md +137 -0
  190. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main/v1.2.0.prompt.md +1 -1
  191. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main/v1.3.0.prompt.md +34 -0
  192. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_update_instruction_start/v1.0.0.prompt.md +5 -12
  193. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_update_instruction_start/v1.1.0.prompt.md +5 -12
  194. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_update_instruction_start/v1.2.0.prompt.md +155 -0
  195. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/tagging/v1.0.0.prompt.md +23 -0
  196. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_manager.py +89 -32
  197. package/plugin/vendor/reflexio/reflexio/server/services/README.md +26 -11
  198. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/README.md +22 -0
  199. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/__init__.py +1 -0
  200. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/agent_success_evaluation_constants.py +4 -2
  201. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/agent_success_evaluation_utils.py +1 -0
  202. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/components/__init__.py +7 -0
  203. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/{agent_success_evaluator.py → components/evaluator.py} +3 -16
  204. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/regen_jobs.py +28 -72
  205. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/{group_evaluation_runner.py → runner.py} +12 -10
  206. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/{delayed_group_evaluator.py → scheduler.py} +10 -2
  207. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/{agent_success_evaluation_service.py → service.py} +36 -1
  208. package/plugin/vendor/reflexio/reflexio/server/services/base_generation_service.py +160 -0
  209. package/plugin/vendor/reflexio/reflexio/server/services/configurator/base_configurator.py +5 -0
  210. package/plugin/vendor/reflexio/reflexio/server/services/deduplication_utils.py +1 -1
  211. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/README.md +9 -0
  212. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/components/__init__.py +0 -0
  213. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/{rule_attribution.py → components/rule_attribution.py} +10 -9
  214. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/eval_sampler.py +15 -24
  215. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/service.py +238 -164
  216. package/plugin/vendor/reflexio/reflexio/server/services/extraction/README.md +31 -0
  217. package/plugin/vendor/reflexio/reflexio/server/services/extraction/outcome.py +13 -3
  218. package/plugin/vendor/reflexio/reflexio/server/services/extraction/pending_tool_call_dispatch.py +3 -2
  219. package/plugin/vendor/reflexio/reflexio/server/services/extraction/resumable_agent.py +32 -118
  220. package/plugin/vendor/reflexio/reflexio/server/services/extraction/resume_worker.py +39 -16
  221. package/plugin/vendor/reflexio/reflexio/server/services/generation_service.py +105 -23
  222. package/plugin/vendor/reflexio/reflexio/server/services/lineage/__init__.py +0 -0
  223. package/plugin/vendor/reflexio/reflexio/server/services/lineage/gc_scheduler.py +193 -0
  224. package/plugin/vendor/reflexio/reflexio/server/services/lineage/resolve.py +98 -0
  225. package/plugin/vendor/reflexio/reflexio/server/services/operation_state_utils.py +26 -0
  226. package/plugin/vendor/reflexio/reflexio/server/services/playbook/README.md +11 -12
  227. package/plugin/vendor/reflexio/reflexio/server/services/playbook/components/__init__.py +5 -0
  228. package/plugin/vendor/reflexio/reflexio/server/services/playbook/{playbook_aggregator.py → components/aggregator.py} +316 -178
  229. package/plugin/vendor/reflexio/reflexio/server/services/playbook/{playbook_consolidator.py → components/consolidator.py} +168 -62
  230. package/plugin/vendor/reflexio/reflexio/server/services/playbook/{playbook_extractor.py → components/extractor.py} +5 -1
  231. package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_edit_apply.py +74 -0
  232. package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_service_utils.py +33 -3
  233. package/plugin/vendor/reflexio/reflexio/server/services/playbook/{playbook_generation_service.py → service.py} +121 -19
  234. package/plugin/vendor/reflexio/reflexio/server/services/playbook/user_detail_stripping.py +84 -0
  235. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/README.md +10 -0
  236. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/models.py +2 -1
  237. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/optimizer.py +178 -30
  238. package/plugin/vendor/reflexio/reflexio/server/services/pre_retrieval/README.md +9 -0
  239. package/plugin/vendor/reflexio/reflexio/server/services/profile/components/__init__.py +17 -0
  240. package/plugin/vendor/reflexio/reflexio/server/services/profile/{profile_deduplicator.py → components/consolidator.py} +7 -6
  241. package/plugin/vendor/reflexio/reflexio/server/services/profile/{profile_extractor.py → components/extractor.py} +30 -22
  242. package/plugin/vendor/reflexio/reflexio/server/services/profile/profile_generation_service_utils.py +6 -13
  243. package/plugin/vendor/reflexio/reflexio/server/services/profile/{profile_generation_service.py → service.py} +32 -50
  244. package/plugin/vendor/reflexio/reflexio/server/services/reflection/__init__.py +4 -8
  245. package/plugin/vendor/reflexio/reflexio/server/services/reflection/components/__init__.py +7 -0
  246. package/plugin/vendor/reflexio/reflexio/server/services/reflection/reflection_service_utils.py +12 -1
  247. package/plugin/vendor/reflexio/reflexio/server/services/reflection/{reflection_service.py → service.py} +36 -16
  248. package/plugin/vendor/reflexio/reflexio/server/services/retrieval/relevance_floor.py +83 -6
  249. package/plugin/vendor/reflexio/reflexio/server/services/service_utils.py +36 -1
  250. package/plugin/vendor/reflexio/reflexio/server/services/shadow_comparison/README.md +8 -0
  251. package/plugin/vendor/reflexio/reflexio/server/services/shadow_comparison/outcome.py +1 -2
  252. package/plugin/vendor/reflexio/reflexio/server/services/storage/error.py +27 -0
  253. package/plugin/vendor/reflexio/reflexio/server/services/storage/retention.py +9 -9
  254. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/__init__.py +2 -0
  255. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_agent_run.py +4 -1
  256. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_base.py +642 -197
  257. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_extras.py +61 -104
  258. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_lineage.py +628 -0
  259. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_operations.py +38 -6
  260. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_playbook.py +978 -184
  261. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_profiles.py +519 -128
  262. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_requests.py +77 -3
  263. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_shadow_verdicts.py +23 -9
  264. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_stall_state.py +5 -2
  265. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/__init__.py +131 -20
  266. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_agent_run.py +2 -8
  267. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_base.py +5 -0
  268. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_extras.py +36 -53
  269. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_lineage.py +215 -0
  270. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_operations.py +20 -0
  271. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_playbook.py +243 -7
  272. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_profiles.py +121 -1
  273. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_requests.py +60 -0
  274. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_retrieval_log.py +51 -0
  275. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_shadow_verdicts.py +17 -0
  276. package/plugin/vendor/reflexio/reflexio/server/services/tagging/README.md +8 -0
  277. package/plugin/vendor/reflexio/reflexio/server/services/tagging/__init__.py +1 -0
  278. package/plugin/vendor/reflexio/reflexio/server/services/tagging/service.py +200 -0
  279. package/plugin/vendor/reflexio/reflexio/server/services/tagging/tagging_scheduler.py +149 -0
  280. package/plugin/vendor/reflexio/reflexio/server/services/unified_search_service.py +229 -32
  281. package/plugin/vendor/reflexio/reflexio/server/site_var/README.md +2 -1
  282. package/plugin/vendor/reflexio/reflexio/server/site_var/feature_flags.py +120 -1
  283. package/plugin/vendor/reflexio/reflexio/server/site_var/site_var_sources/feature_flags.json +4 -0
  284. package/plugin/vendor/reflexio/reflexio/server/site_var/site_var_sources/search_settings.json +5 -0
  285. package/plugin/vendor/reflexio/reflexio/server/tracing.py +30 -0
  286. package/plugin/vendor/reflexio/reflexio/server/usage_metrics.py +18 -0
  287. package/plugin/vendor/reflexio/reflexio/test_support/llm_mock.py +61 -26
  288. package/plugin/vendor/reflexio/reflexio/test_support/llm_model_registry.py +41 -4
  289. package/scripts/setup-claude-smart.sh +8 -3
  290. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/TESTING.md +0 -517
  291. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/hook/handler.js +0 -473
  292. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/package-lock.json +0 -2156
  293. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/package.json +0 -18
  294. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/hook/handler.ts +0 -241
  295. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/hook/setup.ts +0 -140
  296. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/publish.ts +0 -113
  297. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/search.ts +0 -52
  298. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/server.ts +0 -103
  299. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/sqlite-buffer.ts +0 -156
  300. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/user-id.ts +0 -134
  301. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/rules/reflexio.md +0 -24
  302. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/publish_clawhub.sh +0 -278
  303. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/references/HOOK.md +0 -164
  304. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/scripts/install.sh +0 -36
  305. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/scripts/uninstall.sh +0 -35
  306. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/publish.test.ts +0 -27
  307. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/search.test.ts +0 -31
  308. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/server.test.ts +0 -42
  309. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/setup.test.ts +0 -49
  310. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/sqlite-buffer.test.ts +0 -91
  311. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/user-id.test.ts +0 -50
  312. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/vitest.config.ts +0 -13
  313. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/group_aggregation.py +0 -192
  314. package/plugin/vendor/reflexio/reflexio/server/services/extraction/invariants.py +0 -303
  315. package/plugin/vendor/reflexio/reflexio/server/services/extraction/plan.py +0 -138
  316. package/plugin/vendor/reflexio/reflexio/server/services/extraction/tools.py +0 -1125
  317. /package/plugin/vendor/reflexio/reflexio/integrations/{__init__.py → openclaw/plugin/src/openclaw_smart/__init__.py} +0 -0
  318. /package/plugin/vendor/reflexio/reflexio/integrations/openclaw/{types → plugin/types}/openclaw.d.ts +0 -0
  319. /package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/{distribution.py → components/distribution.py} +0 -0
  320. /package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/{hero_state.py → components/hero_state.py} +0 -0
  321. /package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/{shadow_aggregation.py → components/shadow_aggregation.py} +0 -0
  322. /package/plugin/vendor/reflexio/reflexio/server/services/reflection/{reflection_extractor.py → components/extractor.py} +0 -0
@@ -0,0 +1,41 @@
1
+ """Tests for openclaw_smart.ids."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from unittest.mock import patch
6
+
7
+ from openclaw_smart import ids
8
+
9
+
10
+ def test_resolve_project_id_uses_git_toplevel(tmp_path):
11
+ with patch("openclaw_smart.ids._resolve_from_git", return_value="my-repo"):
12
+ assert ids.resolve_project_id(str(tmp_path)) == "my-repo"
13
+
14
+
15
+ def test_resolve_project_id_falls_back_to_cwd_basename(tmp_path):
16
+ with patch("openclaw_smart.ids._resolve_from_git", return_value=None):
17
+ assert ids.resolve_project_id(str(tmp_path)) == tmp_path.name
18
+
19
+
20
+ def test_resolve_project_id_with_fallback_uses_git(tmp_path):
21
+ with patch("openclaw_smart.ids._resolve_from_git", return_value="my-repo"):
22
+ assert (
23
+ ids.resolve_project_id_with_fallback(str(tmp_path), "agent-x") == "my-repo"
24
+ )
25
+
26
+
27
+ def test_resolve_project_id_with_fallback_uses_agent_id(tmp_path):
28
+ with patch("openclaw_smart.ids._resolve_from_git", return_value=None):
29
+ assert (
30
+ ids.resolve_project_id_with_fallback(str(tmp_path), "agent-x") == "agent-x"
31
+ )
32
+
33
+
34
+ def test_resolve_project_id_with_fallback_uses_literal(tmp_path):
35
+ with patch("openclaw_smart.ids._resolve_from_git", return_value=None):
36
+ assert ids.resolve_project_id_with_fallback(str(tmp_path), None) == "openclaw"
37
+
38
+
39
+ def test_resolve_project_id_with_fallback_empty_agent_id_uses_literal(tmp_path):
40
+ with patch("openclaw_smart.ids._resolve_from_git", return_value=None):
41
+ assert ids.resolve_project_id_with_fallback(str(tmp_path), "") == "openclaw"
@@ -0,0 +1,50 @@
1
+ """Tests for openclaw_smart.internal_call."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from openclaw_smart import internal_call
6
+
7
+
8
+ def test_guard_fires_when_env_set(monkeypatch):
9
+ monkeypatch.setenv("OPENCLAW_SMART_INTERNAL", "1")
10
+ assert internal_call.is_internal_invocation({}) is True
11
+
12
+
13
+ def test_guard_inactive_by_default(monkeypatch):
14
+ monkeypatch.delenv("OPENCLAW_SMART_INTERNAL", raising=False)
15
+ assert internal_call.is_internal_invocation({}) is False
16
+
17
+
18
+ def test_guard_fires_when_cwd_inside_reflexio(monkeypatch, tmp_path):
19
+ monkeypatch.delenv("OPENCLAW_SMART_INTERNAL", raising=False)
20
+ repo = tmp_path / "reflexio-repo"
21
+ inner = repo / "src" / "module"
22
+ inner.mkdir(parents=True)
23
+ monkeypatch.setattr(internal_call, "_REFLEXIO_DIR", repo.resolve())
24
+ assert internal_call.is_internal_invocation({"cwd": str(inner)}) is True
25
+
26
+
27
+ def test_guard_inactive_when_cwd_outside_reflexio(monkeypatch, tmp_path):
28
+ monkeypatch.delenv("OPENCLAW_SMART_INTERNAL", raising=False)
29
+ repo = tmp_path / "reflexio-repo"
30
+ other = tmp_path / "other"
31
+ repo.mkdir()
32
+ other.mkdir()
33
+ monkeypatch.setattr(internal_call, "_REFLEXIO_DIR", repo.resolve())
34
+ assert internal_call.is_internal_invocation({"cwd": str(other)}) is False
35
+
36
+
37
+ def test_guard_handles_workspace_dir_key(monkeypatch, tmp_path):
38
+ """openClaw payloads use ``workspaceDir`` rather than ``cwd``."""
39
+ monkeypatch.delenv("OPENCLAW_SMART_INTERNAL", raising=False)
40
+ repo = tmp_path / "reflexio-repo"
41
+ inner = repo / "src"
42
+ inner.mkdir(parents=True)
43
+ monkeypatch.setattr(internal_call, "_REFLEXIO_DIR", repo.resolve())
44
+ assert internal_call.is_internal_invocation({"workspaceDir": str(inner)}) is True
45
+
46
+
47
+ def test_guard_inactive_when_cwd_missing(monkeypatch):
48
+ monkeypatch.delenv("OPENCLAW_SMART_INTERNAL", raising=False)
49
+ assert internal_call.is_internal_invocation({"cwd": ""}) is False
50
+ assert internal_call.is_internal_invocation({"cwd": None}) is False
@@ -0,0 +1,88 @@
1
+ """Tests for openclaw_smart.oc_cite."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from openclaw_smart import oc_cite
6
+
7
+
8
+ def test_rank_id_skill_with_fingerprint():
9
+ tag = oc_cite.rank_id("playbook", 1, "abc12345")
10
+ assert tag == "s1-abc1"
11
+
12
+
13
+ def test_rank_id_preference_with_fingerprint():
14
+ tag = oc_cite.rank_id("profile", 2, "DEAD-beef-1234")
15
+ assert tag == "p2-dead"
16
+
17
+
18
+ def test_rank_id_without_real_id_omits_fingerprint():
19
+ assert oc_cite.rank_id("playbook", 3) == "s3"
20
+ assert oc_cite.rank_id("profile", 4) == "p4"
21
+
22
+
23
+ def test_rank_id_with_non_alnum_real_id():
24
+ assert oc_cite.rank_id("playbook", 1, "---") == "s1"
25
+
26
+
27
+ def test_rank_id_rejects_unknown_kind():
28
+ import pytest
29
+
30
+ with pytest.raises(ValueError):
31
+ oc_cite.rank_id("something-else", 1)
32
+
33
+
34
+ def test_parse_citation_command_extracts_ids():
35
+ cmd = "oc-cite s1-ab12 p2-cd34"
36
+ assert oc_cite.parse_citation_command(cmd) == ["s1-ab12", "p2-cd34"]
37
+
38
+
39
+ def test_parse_citation_command_accepts_prefix():
40
+ assert oc_cite.parse_citation_command("oc-cite oc:s1-ab12") == ["s1-ab12"]
41
+
42
+
43
+ def test_parse_citation_command_rejects_chained_commands():
44
+ assert oc_cite.parse_citation_command("oc-cite s1 && echo x") == []
45
+
46
+
47
+ def test_parse_citation_command_rejects_path_with_args():
48
+ # Path-prefixed binary is fine; extra trailing junk is not.
49
+ assert oc_cite.parse_citation_command("/usr/bin/oc-cite s1-ab12") == ["s1-ab12"]
50
+ assert oc_cite.parse_citation_command("oc-cite s1 garbage extra") == []
51
+
52
+
53
+ def test_parse_text_citations_single():
54
+ text = "Answer body...\n✨ 1 openclaw-smart learning applied [oc:s1-ab12]"
55
+ assert oc_cite.parse_text_citations(text) == ["s1-ab12"]
56
+
57
+
58
+ def test_parse_text_citations_multi():
59
+ text = "Done.\n✨ 2 openclaw-smart learnings applied [oc:s1-ab12,p2-cd34]"
60
+ assert oc_cite.parse_text_citations(text) == ["s1-ab12", "p2-cd34"]
61
+
62
+
63
+ def test_parse_text_citations_no_marker_returns_empty():
64
+ text = "Body that mentions [oc:s1-ab12] but no marker line."
65
+ assert oc_cite.parse_text_citations(text) == []
66
+
67
+
68
+ def test_parse_text_citations_last_wins():
69
+ text = (
70
+ "✨ 1 openclaw-smart learning applied [oc:s1-aaaa]\n"
71
+ "more text\n"
72
+ "✨ 1 openclaw-smart learning applied [oc:s2-bbbb]"
73
+ )
74
+ assert oc_cite.parse_text_citations(text) == ["s2-bbbb"]
75
+
76
+
77
+ def test_citation_instruction_uses_oc_prefix():
78
+ assert "[oc:" in oc_cite.CITATION_INSTRUCTION
79
+ assert "[cs:" not in oc_cite.CITATION_INSTRUCTION
80
+ assert "openclaw-smart" in oc_cite.CITATION_INSTRUCTION
81
+
82
+
83
+ def test_ensure_installed_returns_install_path(monkeypatch, tmp_path):
84
+ monkeypatch.setattr(oc_cite, "_INSTALL_DIR", tmp_path / "bin")
85
+ monkeypatch.setattr(oc_cite, "INSTALL_PATH", tmp_path / "bin" / "oc-cite")
86
+ # Source script likely doesn't exist yet — should still return target path.
87
+ result = oc_cite.ensure_installed()
88
+ assert result == tmp_path / "bin" / "oc-cite"
@@ -0,0 +1,48 @@
1
+ """Tests for openclaw_smart.publish."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+
7
+ import pytest
8
+ from openclaw_smart import publish, state
9
+
10
+
11
+ @pytest.fixture(autouse=True)
12
+ def isolate_state_dir(monkeypatch, tmp_path):
13
+ sessions = tmp_path / "sessions"
14
+ monkeypatch.setenv("OPENCLAW_SMART_STATE_DIR", str(sessions))
15
+ return sessions
16
+
17
+
18
+ class _Adapter:
19
+ def __init__(self) -> None:
20
+ self.calls = 0
21
+
22
+ def publish(self, **_kwargs) -> bool: # noqa: ANN003
23
+ self.calls += 1
24
+ return True
25
+
26
+
27
+ def test_publish_unpublished_serializes_with_lock_and_stamps_watermark(
28
+ isolate_state_dir,
29
+ ):
30
+ state.append("s1", {"role": "User", "content": "hi"})
31
+ adapter = _Adapter()
32
+
33
+ status, count = publish.publish_unpublished(
34
+ session_id="s1",
35
+ project_id="proj",
36
+ force_extraction=False,
37
+ skip_aggregation=False,
38
+ adapter=adapter,
39
+ )
40
+
41
+ assert (status, count) == ("ok", 1)
42
+ assert adapter.calls == 1
43
+ assert (isolate_state_dir / "s1.publish.lock").exists()
44
+ records = [
45
+ json.loads(line)
46
+ for line in (isolate_state_dir / "s1.jsonl").read_text().splitlines()
47
+ ]
48
+ assert records[-1] == {"published_up_to": 1}
@@ -0,0 +1,48 @@
1
+ """Tests for openclaw_smart.query_compose."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from openclaw_smart import query_compose
6
+
7
+
8
+ def test_edit_tool_query_uses_basename_and_snippet():
9
+ q = query_compose.from_tool_call(
10
+ "Edit", {"file_path": "src/auth.py", "new_string": "import oauth2"}
11
+ )
12
+ assert "auth.py" in q
13
+ assert "import oauth2" in q
14
+ # Full directory path is dropped — only basename survives.
15
+ assert "src/" not in q
16
+
17
+
18
+ def test_write_tool_uses_content_fallback():
19
+ q = query_compose.from_tool_call(
20
+ "Write", {"file_path": "notes.md", "content": "release plan"}
21
+ )
22
+ assert "notes.md" in q
23
+ assert "release plan" in q
24
+
25
+
26
+ def test_bash_tool_query_uses_first_line():
27
+ q = query_compose.from_tool_call("Bash", {"command": "pytest tests/\nexit 0"})
28
+ assert "pytest tests/" in q
29
+ assert "exit 0" not in q
30
+
31
+
32
+ def test_empty_input_returns_empty_for_known_tool():
33
+ assert query_compose.from_tool_call("Edit", {}) == ""
34
+ assert query_compose.from_tool_call("Bash", {}) == ""
35
+
36
+
37
+ def test_unknown_tool_returns_empty():
38
+ assert query_compose.from_tool_call("UnknownTool", {"foo": "bar"}) == ""
39
+
40
+
41
+ def test_long_snippet_is_truncated():
42
+ long_text = "a" * 1000
43
+ q = query_compose.from_tool_call(
44
+ "Edit", {"file_path": "x.py", "new_string": long_text}
45
+ )
46
+ # Basename + space + snippet truncated to 400 chars
47
+ assert "a" * 400 in q
48
+ assert len(q) <= len("x.py ") + 400
@@ -0,0 +1,188 @@
1
+ """Tests for openclaw_smart.reflexio_adapter."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from unittest.mock import MagicMock, patch
6
+
7
+ from openclaw_smart.reflexio_adapter import Adapter
8
+
9
+
10
+ def test_default_url_is_8071():
11
+ # 8071/8072 matches claude-smart so the two plugins share one local
12
+ # reflexio backend; 8061 is reserved for a developer's own instance.
13
+ adapter = Adapter()
14
+ assert adapter.url == "http://localhost:8071/"
15
+
16
+
17
+ def test_env_var_overrides_url(monkeypatch):
18
+ monkeypatch.setenv("REFLEXIO_URL", "http://example.com:9000/")
19
+ adapter = Adapter()
20
+ assert adapter.url == "http://example.com:9000/"
21
+
22
+
23
+ def test_explicit_url_wins_over_env(monkeypatch):
24
+ monkeypatch.setenv("REFLEXIO_URL", "http://example.com:9000/")
25
+ adapter = Adapter(url="http://other:7000/")
26
+ assert adapter.url == "http://other:7000/"
27
+
28
+
29
+ def test_get_client_returns_none_on_construction_failure():
30
+ adapter = Adapter()
31
+ with patch(
32
+ "openclaw_smart.reflexio_adapter.ReflexioClient",
33
+ side_effect=ConnectionError,
34
+ create=True,
35
+ ):
36
+ # ReflexioClient is imported lazily inside _get_client; we patch on
37
+ # the imported module to mimic that path.
38
+ pass
39
+ with patch.dict(
40
+ "sys.modules",
41
+ {"reflexio": MagicMock(ReflexioClient=MagicMock(side_effect=ConnectionError))},
42
+ ):
43
+ assert adapter._get_client() is None
44
+
45
+
46
+ def test_publish_returns_true_for_empty_interactions():
47
+ adapter = Adapter()
48
+ assert adapter.publish(session_id="s", project_id="p", interactions=[]) is True
49
+
50
+
51
+ def test_publish_returns_false_when_client_none():
52
+ adapter = Adapter()
53
+ with patch.object(adapter, "_get_client", return_value=None):
54
+ assert (
55
+ adapter.publish(
56
+ session_id="s",
57
+ project_id="p",
58
+ interactions=[{"role": "User", "content": "x"}],
59
+ )
60
+ is False
61
+ )
62
+
63
+
64
+ def test_publish_passes_openclaw_agent_version():
65
+ fake_client = MagicMock()
66
+ adapter = Adapter()
67
+ with patch.object(adapter, "_get_client", return_value=fake_client):
68
+ adapter.publish(
69
+ session_id="s1",
70
+ project_id="proj",
71
+ interactions=[{"role": "User", "content": "x"}],
72
+ )
73
+ kwargs = fake_client.publish_interaction.call_args[1]
74
+ assert kwargs["agent_version"] == "openclaw"
75
+ assert kwargs["user_id"] == "proj"
76
+ assert kwargs["session_id"] == "s1"
77
+ assert kwargs["wait_for_response"] is False
78
+
79
+
80
+ def test_publish_forwards_force_extraction():
81
+ fake_client = MagicMock()
82
+ adapter = Adapter()
83
+ with patch.object(adapter, "_get_client", return_value=fake_client):
84
+ adapter.publish(
85
+ session_id="s1",
86
+ project_id="p",
87
+ interactions=[{"role": "User"}],
88
+ force_extraction=True,
89
+ skip_aggregation=True,
90
+ )
91
+ kwargs = fake_client.publish_interaction.call_args[1]
92
+ assert kwargs["force_extraction"] is True
93
+ assert kwargs["skip_aggregation"] is True
94
+
95
+
96
+ def test_publish_returns_false_on_exception():
97
+ fake_client = MagicMock()
98
+ fake_client.publish_interaction.side_effect = RuntimeError("boom")
99
+ adapter = Adapter()
100
+ with patch.object(adapter, "_get_client", return_value=fake_client):
101
+ assert (
102
+ adapter.publish(
103
+ session_id="s",
104
+ project_id="p",
105
+ interactions=[{"role": "User"}],
106
+ )
107
+ is False
108
+ )
109
+
110
+
111
+ def test_search_all_degrades_to_empty():
112
+ adapter = Adapter()
113
+ with patch.object(adapter, "_get_client", return_value=None):
114
+ u, a, p = adapter.search_all(project_id="p", query="q", top_k=3)
115
+ assert u == [] and a == [] and p == []
116
+
117
+
118
+ def test_search_all_passes_agent_version():
119
+ fake_client = MagicMock()
120
+ fake_client.search.return_value = MagicMock(
121
+ user_playbooks=[], agent_playbooks=[], profiles=[]
122
+ )
123
+ adapter = Adapter()
124
+ with patch.object(adapter, "_get_client", return_value=fake_client):
125
+ adapter.search_all(project_id="p", query="q", top_k=5)
126
+ kwargs = fake_client.search.call_args[1]
127
+ assert kwargs["agent_version"] == "openclaw"
128
+ assert kwargs["user_id"] == "p"
129
+
130
+
131
+ def test_fetch_user_playbooks_degrades_to_empty():
132
+ adapter = Adapter()
133
+ with patch.object(adapter, "_get_client", return_value=None):
134
+ assert adapter.fetch_user_playbooks(project_id="p") == []
135
+
136
+
137
+ def test_fetch_agent_playbooks_filters_rejected():
138
+ fake_client = MagicMock()
139
+ fake_client.search_agent_playbooks.return_value = MagicMock(
140
+ agent_playbooks=[
141
+ {"id": "a", "playbook_status": "approved"},
142
+ {"id": "b", "playbook_status": "rejected"},
143
+ {"id": "c", "playbook_status": "pending"},
144
+ ]
145
+ )
146
+ adapter = Adapter()
147
+ with patch.object(adapter, "_get_client", return_value=fake_client):
148
+ result = adapter.fetch_agent_playbooks(top_k=5)
149
+ ids = [item["id"] for item in result]
150
+ assert "b" not in ids
151
+ assert "a" in ids and "c" in ids
152
+
153
+
154
+ def test_apply_extraction_defaults_skips_when_already_matching():
155
+ fake_client = MagicMock()
156
+ config = MagicMock(window_size=10, stride_size=5)
157
+ fake_client.get_config.return_value = config
158
+ adapter = Adapter()
159
+ with patch.object(adapter, "_get_client", return_value=fake_client):
160
+ assert adapter.apply_extraction_defaults(window_size=10, stride_size=5) is True
161
+ fake_client.set_config.assert_not_called()
162
+
163
+
164
+ def test_apply_extraction_defaults_writes_when_different():
165
+ fake_client = MagicMock()
166
+ config = MagicMock(window_size=99, stride_size=99)
167
+ fake_client.get_config.return_value = config
168
+ adapter = Adapter()
169
+ with patch.object(adapter, "_get_client", return_value=fake_client):
170
+ adapter.apply_extraction_defaults(window_size=10, stride_size=5)
171
+ assert config.window_size == 10
172
+ assert config.stride_size == 5
173
+ fake_client.set_config.assert_called_once_with(config)
174
+
175
+
176
+ def test_fetch_stall_state_returns_none_when_client_none():
177
+ adapter = Adapter()
178
+ with patch.object(adapter, "_get_client", return_value=None):
179
+ assert adapter.fetch_stall_state() is None
180
+
181
+
182
+ def test_mark_stall_notified_swallows_errors():
183
+ fake_client = MagicMock()
184
+ fake_client.mark_stall_notified.side_effect = RuntimeError
185
+ adapter = Adapter()
186
+ with patch.object(adapter, "_get_client", return_value=fake_client):
187
+ # Must not raise
188
+ adapter.mark_stall_notified()
@@ -0,0 +1,41 @@
1
+ """Tests for openclaw_smart.runtime."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import pytest
6
+ from openclaw_smart import runtime
7
+
8
+
9
+ @pytest.fixture(autouse=True)
10
+ def _reset_host(monkeypatch):
11
+ """Ensure each test starts with the module-level host unset."""
12
+ monkeypatch.setattr(runtime, "_current_host", None)
13
+ monkeypatch.delenv(runtime.HOST_ENV, raising=False)
14
+ yield
15
+
16
+
17
+ def test_default_host_is_openclaw():
18
+ assert runtime.host() == runtime.HOST_OPENCLAW
19
+ assert runtime.is_openclaw() is True
20
+
21
+
22
+ def test_set_host_sets_module_state():
23
+ runtime.set_host("openclaw")
24
+ assert runtime.host() == "openclaw"
25
+ assert runtime.is_openclaw()
26
+
27
+
28
+ def test_set_host_falls_back_for_unknown_value():
29
+ runtime.set_host("definitely-not-real")
30
+ assert runtime.host() == runtime.HOST_OPENCLAW
31
+
32
+
33
+ def test_set_host_writes_env_var():
34
+ runtime.set_host("openclaw")
35
+ import os
36
+
37
+ assert os.environ.get(runtime.HOST_ENV) == "openclaw"
38
+
39
+
40
+ def test_agent_version_is_openclaw():
41
+ assert runtime.agent_version() == "openclaw"