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
@@ -4,6 +4,7 @@ Prompt management using file system prompt bank with markdown frontmatter files.
4
4
 
5
5
  import logging
6
6
  import re
7
+ from collections.abc import Sequence
7
8
  from pathlib import Path
8
9
  from typing import Any
9
10
 
@@ -75,28 +76,37 @@ class PromptManager:
75
76
 
76
77
  def __init__(
77
78
  self,
78
- prompt_bank_path: str | None = None,
79
+ prompt_bank_path: str | Path | None = None,
79
80
  version_override: dict[str, str] | None = None,
81
+ extra_prompt_bank_paths: Sequence[str | Path] | None = None,
80
82
  ):
81
83
  """
82
84
  Initialize the PromptManager.
83
85
 
84
86
  Args:
85
- prompt_bank_path (str, optional): Path to the prompt bank directory.
87
+ prompt_bank_path (str | Path, optional): Path to the primary prompt bank directory.
86
88
  version_override (dict[str, str], optional): Map of prompt_id → version string to override the active version.
89
+ extra_prompt_bank_paths (Sequence[str | Path], optional): Additional prompt bank directories.
87
90
  """
88
91
  if prompt_bank_path is None:
89
92
  current_dir = Path(__file__).parent
90
93
  self.prompt_bank_path = current_dir / "prompt_bank"
91
94
  else:
92
95
  self.prompt_bank_path = Path(prompt_bank_path)
96
+ self.prompt_bank_paths = [self.prompt_bank_path]
97
+ if extra_prompt_bank_paths:
98
+ self.prompt_bank_paths.extend(
99
+ Path(path) for path in extra_prompt_bank_paths
100
+ )
93
101
 
94
102
  self.version_override = version_override
95
103
 
96
- if not self.prompt_bank_path.exists():
97
- logger.warning("Prompt bank path does not exist: %s", self.prompt_bank_path)
104
+ for path in self.prompt_bank_paths:
105
+ if not path.exists():
106
+ logger.warning("Prompt bank path does not exist: %s", path)
98
107
 
99
108
  self._cache: dict[str, Prompt] = {}
109
+ self._validate_unique_prompt_ids()
100
110
 
101
111
  # ==============================
102
112
  # Public methods
@@ -148,13 +158,13 @@ class PromptManager:
148
158
  Returns:
149
159
  list[str]: List of version strings.
150
160
  """
151
- prompt_dir = self.prompt_bank_path / prompt_id
152
- if not prompt_dir.is_dir():
153
- return []
154
- return [
155
- p.name.removeprefix("v").removesuffix(".prompt.md")
156
- for p in sorted(prompt_dir.glob("v*.prompt.md"))
157
- ]
161
+ versions: list[str] = []
162
+ for prompt_dir in self._prompt_dirs(prompt_id):
163
+ versions.extend(
164
+ p.name.removeprefix("v").removesuffix(".prompt.md")
165
+ for p in sorted(prompt_dir.glob("v*.prompt.md"))
166
+ )
167
+ return versions
158
168
 
159
169
  def get_active_version(self, prompt_id: str) -> str | None:
160
170
  """
@@ -177,21 +187,65 @@ class PromptManager:
177
187
  Returns:
178
188
  list[str]: List of prompt IDs.
179
189
  """
180
- if not self.prompt_bank_path.exists():
181
- return []
182
- return [
183
- item.name
184
- for item in self.prompt_bank_path.iterdir()
185
- if item.is_dir() and any(item.glob("v*.prompt.md"))
186
- ]
190
+ prompt_ids: list[str] = []
191
+ seen: set[str] = set()
192
+ for prompt_bank_path in self.prompt_bank_paths:
193
+ if not prompt_bank_path.exists():
194
+ continue
195
+ for item in prompt_bank_path.iterdir():
196
+ if (
197
+ item.is_dir()
198
+ and item.name not in seen
199
+ and any(item.glob("v*.prompt.md"))
200
+ ):
201
+ prompt_ids.append(item.name)
202
+ seen.add(item.name)
203
+ return prompt_ids
187
204
 
188
205
  # ==============================
189
206
  # Private methods
190
207
  # ==============================
191
208
 
209
+ def _prompt_dirs(self, prompt_id: str) -> list[Path]:
210
+ """Return configured prompt directories for a prompt ID."""
211
+ return [
212
+ prompt_bank_path / prompt_id
213
+ for prompt_bank_path in self.prompt_bank_paths
214
+ if (prompt_bank_path / prompt_id).is_dir()
215
+ ]
216
+
217
+ def _validate_unique_prompt_ids(self) -> None:
218
+ """Reject prompt IDs that appear in more than one configured prompt bank."""
219
+ locations: dict[str, list[Path]] = {}
220
+ for prompt_bank_path in self.prompt_bank_paths:
221
+ if not prompt_bank_path.exists():
222
+ continue
223
+ for item in prompt_bank_path.iterdir():
224
+ if item.is_dir() and any(item.glob("v*.prompt.md")):
225
+ locations.setdefault(item.name, []).append(item)
226
+
227
+ duplicates = {
228
+ prompt_id: paths for prompt_id, paths in locations.items() if len(paths) > 1
229
+ }
230
+ if duplicates:
231
+ details = "; ".join(
232
+ f"{prompt_id}: {', '.join(str(path) for path in paths)}"
233
+ for prompt_id, paths in sorted(duplicates.items())
234
+ )
235
+ raise ValueError(f"Duplicate prompt_id found in prompt banks: {details}")
236
+
192
237
  def _load_prompt(self, prompt_id: str, version: str) -> Prompt | None:
193
238
  """Load a single prompt file by prompt_id and version string."""
194
- path = self.prompt_bank_path / prompt_id / f"v{version}.prompt.md"
239
+ path: Path | None = None
240
+ for prompt_dir in self._prompt_dirs(prompt_id):
241
+ candidate = prompt_dir / f"v{version}.prompt.md"
242
+ if candidate.is_file():
243
+ path = candidate
244
+ break
245
+ if path is None:
246
+ logger.warning("Prompt file not found: %s v%s", prompt_id, version)
247
+ return None
248
+
195
249
  try:
196
250
  raw = path.read_text(encoding="utf-8")
197
251
  except FileNotFoundError:
@@ -217,8 +271,8 @@ class PromptManager:
217
271
 
218
272
  def _find_active_version(self, prompt_id: str) -> str | None:
219
273
  """Scan .prompt.md files to find the latest one with active: true."""
220
- prompt_dir = self.prompt_bank_path / prompt_id
221
- if not prompt_dir.is_dir():
274
+ prompt_dirs = self._prompt_dirs(prompt_id)
275
+ if not prompt_dirs:
222
276
  return None
223
277
 
224
278
  def _semver_key(p: Path) -> tuple[int, ...]:
@@ -239,17 +293,20 @@ class PromptManager:
239
293
  return (0,)
240
294
 
241
295
  latest: str | None = None
242
- for path in sorted(
243
- prompt_dir.glob("v*.prompt.md"), key=_semver_key, reverse=True
244
- ):
245
- try:
246
- raw = path.read_text(encoding="utf-8")
247
- meta, _ = _parse_frontmatter(raw)
248
- if meta.get("active"):
249
- latest = path.name.removeprefix("v").removesuffix(".prompt.md")
250
- break
251
- except (ValueError, OSError):
252
- continue
296
+ for prompt_dir in prompt_dirs:
297
+ for path in sorted(
298
+ prompt_dir.glob("v*.prompt.md"), key=_semver_key, reverse=True
299
+ ):
300
+ try:
301
+ raw = path.read_text(encoding="utf-8")
302
+ meta, _ = _parse_frontmatter(raw)
303
+ if meta.get("active"):
304
+ latest = path.name.removeprefix("v").removesuffix(".prompt.md")
305
+ break
306
+ except (ValueError, OSError):
307
+ continue
308
+ if latest:
309
+ break
253
310
  return latest
254
311
 
255
312
  def _get_prompt(self, prompt_id: str, version: str | None = None) -> Prompt | None:
@@ -5,6 +5,19 @@ Description: Core business-logic layer — LLM orchestration, extraction, evalua
5
5
 
6
6
  **Service Boundary**: services own the LLM/extraction/evaluation/storage logic; API endpoints only authenticate, build `RequestContext`, and delegate into `Reflexio` or a focused service helper.
7
7
 
8
+ ## LLM Pipeline Module Contract
9
+
10
+ LLM/pipeline modules use a shared vocabulary across OSS and enterprise:
11
+ `service.py` for request-path entry points, `runner.py` for manual/background
12
+ workflow entry points, `scheduler.py` for periodic/deferred execution,
13
+ `config.py` for module-owned config, `models.py` for module-local data shapes,
14
+ and `components/` for internal extractors, judges, proposers, aggregators,
15
+ evaluators, gates, and resolvers.
16
+
17
+ Files are optional. Do not create empty files only to satisfy the vocabulary.
18
+ Complete cutover migrations update consumers, tests, docs, and monkeypatch
19
+ strings before deleting old import paths in the same PR.
20
+
8
21
  ## Orchestration & Base Infrastructure
9
22
 
10
23
  | File | Purpose |
@@ -13,32 +26,34 @@ Description: Core business-logic layer — LLM orchestration, extraction, evalua
13
26
  | `base_generation_service.py` | `BaseGenerationService` — abstract base; the **Service Pattern** (load configs → create actors → run in parallel → save results). Per-extractor timeout `EXTRACTOR_TIMEOUT_SECONDS = 300`. |
14
27
  | `operation_state_utils.py` | `OperationStateManager` — all `_operation_state` access (progress, concurrency locks, extractor/aggregator bookmarks, cluster fingerprints, cancellation). |
15
28
  | `extractor_config_utils.py`, `extractor_interaction_utils.py` | Filter extractors by source / `allow_manual_trigger` / names; per-extractor stride + window + bookmark handling. |
16
- | `deduplication_utils.py`, `service_utils.py`, `embedding_text.py` | LLM dedup helpers (used by `ProfileDeduplicator` + `PlaybookConsolidator`), message construction / JSON extraction / response logging, embedding text builders. |
29
+ | `deduplication_utils.py`, `service_utils.py`, `embedding_text.py` | LLM dedup helpers (used by `ProfileConsolidator` + `PlaybookConsolidator`), message construction / JSON extraction / response logging, embedding text builders. |
17
30
 
18
31
  ## Generation Services
19
32
 
20
33
  | Directory | Entry class | Key files |
21
34
  |-----------|-------------|-----------|
22
- | `profile/` | `ProfileGenerationService` | `profile_extractor.py`, `profile_deduplicator.py`, `profile_updater.py` |
23
- | `playbook/` | `PlaybookGenerationService` | `playbook_extractor.py`, `playbook_consolidator.py`, `playbook_aggregator.py` (cluster-fingerprint change detection) — has its own [README](playbook/README.md) |
24
- | `agent_success_evaluation/` | `AgentSuccessEvaluationService` | `agent_success_evaluator.py` (session-level), `delayed_group_evaluator.py` (`GroupEvaluationScheduler`, 10-min defer), `group_evaluation_runner.py`, `regen_jobs.py` |
25
- | `reflection/` | `ReflectionService` | `reflection_extractor.py` — post-horizon reflection; runs **before** extraction so extractors read post-reflection state |
35
+ | `profile/` | `ProfileGenerationService` | `service.py`, `components/extractor.py`, `components/consolidator.py` |
36
+ | `playbook/` | `PlaybookGenerationService` | `components/extractor.py`, `components/consolidator.py`, `components/aggregator.py` (cluster-fingerprint change detection) — has its own [README](playbook/README.md) |
37
+ | `agent_success_evaluation/` | `AgentSuccessEvaluationService` | `service.py` (session-level service), `runner.py` (`run_group_evaluation`), `scheduler.py` (`GroupEvaluationScheduler`, 10-min defer), `regen_jobs.py`, `components/evaluator.py` |
38
+ | `reflection/` | `ReflectionService` | `service.py`, `components/extractor.py` — post-horizon reflection; runs **before** extraction so extractors read post-reflection state |
26
39
 
27
40
  ## Async Extraction
28
41
 
29
42
  | Directory | Purpose |
30
43
  |-----------|---------|
31
- | `extraction/` | Resumable extraction agent: `resumable_agent.py`, `resume_scheduler.py`, `resume_worker.py`, `pending_tool_call_dispatch.py` (`ask_human`), `tools.py`, `plan.py`, `agent_run_records.py`, `invariants.py`. Long-horizon / tool-mediated extraction continues outside the request path. |
44
+ | `extraction/` | Shared async extraction runtime: `resumable_agent.py`, `resume_scheduler.py`, `resume_worker.py`, `pending_tool_call_dispatch.py` (`ask_human`), `prior_answer_search.py`, `agent_run_records.py`, and `outcome.py`. Long-horizon / tool-mediated extraction continues outside the request path. See [README](extraction/README.md). |
32
45
 
33
46
  ## Evaluation, Search & Integrations
34
47
 
35
48
  | Path | Purpose |
36
49
  |------|---------|
37
- | `shadow_comparison/` | `ShadowComparisonJudge` per-turn regular-vs-shadow verdicts written to a separate table (session-level shadow was retracted due to trajectory contamination). |
38
- | `evaluation_overview/` | Dashboard rollups: `service.py`, `hero_state.py`, `distribution.py`, `group_aggregation.py`, `rule_attribution.py`, `shadow_aggregation.py`, `eval_sampler.py`. |
39
- | `playbook_optimizer/` | Scenario-based playbook optimization: `optimizer.py`, `scheduler.py`, `rollout.py`, `judge.py`, `scenario_resolver.py`, `gepa_adapter.py`, `assistant_webhook.py`. |
50
+ | `shadow_comparison/` | `ShadowComparisonJudge` (`judge.py`) plus pure outcome helpers (`outcome.py`) - per-turn regular-vs-shadow verdicts written to a separate table. Compact by design; see [README](shadow_comparison/README.md). |
51
+ | `evaluation_overview/` | Dashboard/read-side rollups: `service.py` entry point, `components/` aggregation helpers, and root `eval_sampler.py` shared with regenerate jobs. See [README](evaluation_overview/README.md). |
52
+ | `playbook_optimizer/` | Scenario-based playbook optimization: mature flat package with `optimizer.py`, `scheduler.py`, `rollout.py`, `judge.py`, `models.py`, `scenario_resolver.py`, `gepa_adapter.py`, and `assistant_webhook.py`. See [README](playbook_optimizer/README.md). |
40
53
  | `braintrust/` | Braintrust export/sync: `service.py`, `client.py`, `_cron.py`, `_encryption.py`. |
41
- | `pre_retrieval/` | `QueryReformulator` (`_query_reformulator.py`) + `_document_expander.py` — query rewrite & doc expansion for recall. |
54
+ | `lineage/` | Current-record resolution and tombstone GC: `resolve.py`, `gc_scheduler.py`. |
55
+ | `pre_retrieval/` | `QueryReformulator` (`_query_reformulator.py`) + `DocumentExpander` (`_document_expander.py`) - query rewrite and doc expansion for recall. Compact by design; see [README](pre_retrieval/README.md). |
56
+ | `tagging/` | `TaggingService` (`service.py`) + deferred `tagging_scheduler.py` - post-generation profile/playbook tagging. Compact by design; see [README](tagging/README.md). |
42
57
  | `unified_search_service.py` | `run_unified_search()` — two-phase parallel search across profiles / agent playbooks / user playbooks. |
43
58
  | `retrieval/` | `relevance_floor.py` — result relevance thresholding. |
44
59
 
@@ -46,7 +61,7 @@ Description: Core business-logic layer — LLM orchestration, extraction, evalua
46
61
 
47
62
  | Path | Purpose |
48
63
  |------|---------|
49
- | `storage/` | `storage_base/` (`BaseStorage` split by domain) + `sqlite_storage/` + `retention*.py`. Access via `request_context.storage` only. |
64
+ | `storage/` | `storage_base/` (`BaseStorage` split by domain, including `_lineage.py`) + `sqlite_storage/` (including lineage/tombstones) + `retention*.py`. Access via `request_context.storage` only. |
50
65
  | `configurator/` | `DefaultConfigurator` — loads YAML config and creates the storage backend. |
51
66
 
52
67
  ## Key Rules
@@ -0,0 +1,22 @@
1
+ # agent_success_evaluation
2
+
3
+ Session-level agent success evaluation module.
4
+
5
+ ## Module Shape
6
+
7
+ - `service.py`: `AgentSuccessEvaluationService`, the request-path service that runs configured evaluators and saves result rows.
8
+ - `runner.py`: `run_group_evaluation(...)`, the background/manual workflow entry point that loads a session, runs the service, dispatches shadow comparison, and marks operation state.
9
+ - `scheduler.py`: `GroupEvaluationScheduler`, the deferred inactivity scheduler.
10
+ - `components/evaluator.py`: `AgentSuccessEvaluator`, the LLM evaluator component.
11
+ - `regen_jobs.py`: regeneration job planning and execution; remains root-level because API/admin regenerate flows import it directly.
12
+ - `_eval_health.py`: producer/scheduler health counters.
13
+ - `agent_success_evaluation_constants.py`: prompt/model output constants.
14
+ - `agent_success_evaluation_utils.py`: request DTO and prompt-message construction helpers.
15
+
16
+ ## Prompt IDs
17
+
18
+ - Owns `agent_success_evaluation`.
19
+ - Keeps historical/configured prompt ID `agent_success_evaluation_with_comparison` stable where prompt mapping tests require it.
20
+
21
+ Do not reintroduce the deleted service/evaluator/runner/scheduler legacy
22
+ module files.
@@ -0,0 +1 @@
1
+ """Agent success evaluation service package."""
@@ -3,7 +3,9 @@
3
3
  from dataclasses import dataclass
4
4
  from typing import Literal
5
5
 
6
- from pydantic import BaseModel, ConfigDict, Field
6
+ from pydantic import ConfigDict, Field
7
+
8
+ from reflexio.models.structured_output import StrictStructuredOutput
7
9
 
8
10
 
9
11
  @dataclass(frozen=True)
@@ -14,7 +16,7 @@ class AgentSuccessEvaluationConstants:
14
16
  AGENT_SUCCESS_EVALUATION_PROMPT_ID = "agent_success_evaluation"
15
17
 
16
18
 
17
- class AgentSuccessEvaluationOutput(BaseModel):
19
+ class AgentSuccessEvaluationOutput(StrictStructuredOutput):
18
20
  """
19
21
  Unified output schema for agent success evaluation.
20
22
 
@@ -19,6 +19,7 @@ from reflexio.server.services.service_utils import (
19
19
  class AgentSuccessEvaluationRequest(BaseModel):
20
20
  """Request schema for agent success evaluation"""
21
21
 
22
+ user_id: str = ""
22
23
  session_id: str
23
24
  agent_version: str
24
25
  request_interaction_data_models: list[RequestInteractionDataModel]
@@ -0,0 +1,7 @@
1
+ """Internal components for agent success evaluation."""
2
+
3
+ from reflexio.server.services.agent_success_evaluation.components.evaluator import (
4
+ AgentSuccessEvaluator,
5
+ )
6
+
7
+ __all__ = ["AgentSuccessEvaluator"]
@@ -1,7 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import logging
4
- import random
5
4
  from datetime import UTC, datetime
6
5
  from typing import TYPE_CHECKING
7
6
 
@@ -34,7 +33,7 @@ from reflexio.server.services.service_utils import (
34
33
  from reflexio.server.site_var.site_var_manager import SiteVarManager
35
34
 
36
35
  if TYPE_CHECKING:
37
- from reflexio.server.services.agent_success_evaluation.agent_success_evaluation_service import (
36
+ from reflexio.server.services.agent_success_evaluation.service import (
38
37
  AgentSuccessGenerationServiceConfig,
39
38
  )
40
39
 
@@ -104,7 +103,7 @@ class AgentSuccessEvaluator:
104
103
 
105
104
  Treats all request_interaction_data_models as a single conversation.
106
105
  Applies source filtering based on extractor config.
107
- Applies sampling rate once per group.
106
+ Sampling is handled by callers before invoking the evaluator.
108
107
 
109
108
  Returns:
110
109
  List of AgentSuccessEvaluationResult objects (single result for the group)
@@ -130,19 +129,6 @@ class AgentSuccessEvaluator:
130
129
  # No matching interactions after source filter
131
130
  return []
132
131
 
133
- # Check sampling rate once per group
134
- if self.config.sampling_rate < 1.0:
135
- random_value = random.random() # noqa: S311
136
- if random_value >= self.config.sampling_rate:
137
- logger.info(
138
- "Skipping evaluation for session %s due to sampling rate. "
139
- "sampling_rate=%s, random_value=%.3f",
140
- self.service_config.session_id,
141
- self.config.sampling_rate,
142
- random_value,
143
- )
144
- return []
145
-
146
132
  result = self._evaluate_group(request_interaction_data_models)
147
133
  return [result] if result else []
148
134
 
@@ -288,6 +274,7 @@ class AgentSuccessEvaluator:
288
274
  request_interaction_data_models
289
275
  )
290
276
  return AgentSuccessEvaluationResult(
277
+ user_id=self.service_config.user_id,
291
278
  session_id=self.service_config.session_id,
292
279
  agent_version=self.service_config.agent_version,
293
280
  evaluation_name=get_extractor_name(self.config),
@@ -13,12 +13,12 @@ import time
13
13
  import uuid
14
14
  from concurrent.futures import Future, ThreadPoolExecutor, as_completed
15
15
  from dataclasses import dataclass, field
16
- from typing import Any, Literal
16
+ from typing import Literal
17
17
 
18
18
  from reflexio.models.api_schema.internal_schema import SessionDescriptor
19
19
  from reflexio.server.api_endpoints.request_context import RequestContext
20
20
  from reflexio.server.llm.litellm_client import LiteLLMClient
21
- from reflexio.server.services.agent_success_evaluation.group_evaluation_runner import (
21
+ from reflexio.server.services.agent_success_evaluation.runner import (
22
22
  run_group_evaluation,
23
23
  )
24
24
  from reflexio.server.services.evaluation_overview.eval_sampler import (
@@ -170,74 +170,16 @@ class RegenJobRegistry:
170
170
  REGEN_JOBS = RegenJobRegistry()
171
171
 
172
172
 
173
- def _load_first_request(
174
- storage: BaseStorage,
175
- user_id: str,
176
- session_id: str,
177
- cache: dict[str, tuple[int, dict[str, Any]]],
178
- ) -> tuple[int, dict[str, Any]]:
179
- """Return the (created_at, metadata) of a session's earliest request, memoized.
180
-
181
- The regen worker can see multiple SessionDescriptors for the same
182
- session_id (one per distinct agent_version/source tuple). This
183
- helper guarantees each session_id triggers at most one storage call,
184
- mirroring the amortized pattern F2's evaluation overview service
185
- uses.
186
-
187
- Args:
188
- storage (BaseStorage): Storage backend bound to the request_context.
189
- user_id (str): Owner of the session's requests.
190
- session_id (str): Session whose first-request data to return.
191
- cache (dict[str, tuple[int, dict[str, Any]]]): Memoization dict
192
- keyed by session_id; updated in place.
193
-
194
- Returns:
195
- tuple[int, dict[str, Any]]: ``(first_created_at, first_metadata)``.
196
- Falls back to ``(0, {})`` when the session has no requests (the
197
- descriptor is still kept so the regen worker can attempt
198
- evaluation; the sampler simply treats it as the epoch-zero day
199
- bucket and the untagged group).
200
- """
201
- cached = cache.get(session_id)
202
- if cached is not None:
203
- return cached
204
- try:
205
- requests = storage.get_requests_by_session(user_id, session_id)
206
- except Exception as e: # noqa: BLE001 — per-session resilience boundary
207
- logger.warning(
208
- "Failed to fetch requests for session %s during F3 sampler candidate "
209
- "discovery (%s); falling back to (created_at=0, metadata={}). The "
210
- "session will be retried in the per-session loop below.",
211
- session_id,
212
- e,
213
- )
214
- cache[session_id] = (0, {})
215
- return cache[session_id]
216
- if not requests:
217
- logger.warning(
218
- "Session %s has no requests in storage despite being returned by "
219
- "get_session_ids_in_window (possible race or contract violation); "
220
- "falling back to (created_at=0, metadata={}).",
221
- session_id,
222
- )
223
- cache[session_id] = (0, {})
224
- return cache[session_id]
225
- first = min(requests, key=lambda r: r.created_at)
226
- cache[session_id] = (first.created_at, first.metadata or {})
227
- return cache[session_id]
228
-
229
-
230
173
  def _build_sample_candidates(
231
174
  storage: BaseStorage,
232
175
  descriptors: list[SessionDescriptor],
233
176
  ) -> list[SampleCandidate]:
234
- """Convert raw SessionDescriptors into SampleCandidates with metadata.
177
+ """Convert raw SessionDescriptors into SampleCandidates.
235
178
 
236
- Reads the first-request data for each distinct session_id at most
237
- once via ``_load_first_request``'s cache, then assembles one
238
- ``SampleCandidate`` per descriptor. ``created_at`` is sourced from
239
- the first request's timestamp so the day-bucket stratum aligns with
240
- the session's wall clock (not the regen window's edges).
179
+ Reads first-request data for all distinct session_ids in one storage call,
180
+ then assembles one ``SampleCandidate`` per descriptor. ``created_at`` is
181
+ sourced from the first request's timestamp so the day-bucket stratum aligns
182
+ with the session's wall clock (not the regen window's edges).
241
183
 
242
184
  Args:
243
185
  storage (BaseStorage): Storage backend used to load per-session data.
@@ -248,20 +190,34 @@ def _build_sample_candidates(
248
190
  list[SampleCandidate]: One candidate per descriptor, ready for the
249
191
  pure ``sample_candidates`` function.
250
192
  """
251
- cache: dict[str, tuple[int, dict[str, Any]]] = {}
193
+ pairs = sorted({(sd.user_id, sd.session_id) for sd in descriptors})
194
+ try:
195
+ first_requests = storage.get_first_requests_by_user_session_pairs(pairs)
196
+ except Exception as e: # noqa: BLE001 — discovery should not abort the whole job
197
+ logger.warning(
198
+ "Failed to bulk fetch first requests during F3 sampler candidate "
199
+ "discovery (%s); falling back to epoch-zero source buckets.",
200
+ e,
201
+ )
202
+ first_requests = {}
203
+
252
204
  candidates: list[SampleCandidate] = []
253
205
  for sd in descriptors:
254
- created_at, metadata = _load_first_request(
255
- storage, sd.user_id, sd.session_id, cache
256
- )
206
+ first = first_requests.get((sd.user_id, sd.session_id))
207
+ if first is None:
208
+ logger.warning(
209
+ "Session %s has no first request in storage despite being returned by "
210
+ "get_session_ids_in_window; falling back to (created_at=0, source='').",
211
+ sd.session_id,
212
+ )
257
213
  candidates.append(
258
214
  SampleCandidate(
259
215
  session_id=sd.session_id,
260
216
  user_id=sd.user_id,
261
217
  agent_version=sd.agent_version,
262
218
  source=sd.source,
263
- created_at=created_at,
264
- first_request_metadata=metadata,
219
+ created_at=first.created_at if first else 0,
220
+ first_request_source=(first.source or "") if first else "",
265
221
  )
266
222
  )
267
223
  return candidates
@@ -328,7 +284,7 @@ def run_regen(
328
284
 
329
285
  Step 1 enumerates all candidate sessions in ``[from_ts, to_ts]`` via
330
286
  ``storage.get_session_ids_in_window``. Step 2 stratifies them by
331
- (day-bucket x F2 group) and samples up to
287
+ (day-bucket x first request source) and samples up to
332
288
  ``config.eval_sample_n_per_stratum`` per stratum — giving the regen
333
289
  pipeline predictable cost regardless of traffic volume. Step 3
334
290
  dispatches the sampled subset through a ``ThreadPoolExecutor`` whose
@@ -15,15 +15,16 @@ from reflexio.server.api_endpoints.request_context import RequestContext
15
15
  from reflexio.server.llm.litellm_client import LiteLLMClient
16
16
  from reflexio.server.services.agent_success_evaluation import _eval_health
17
17
  from reflexio.server.services.agent_success_evaluation._eval_health import SkipReason
18
- from reflexio.server.services.agent_success_evaluation.agent_success_evaluation_service import (
19
- AgentSuccessEvaluationService,
20
- )
21
18
  from reflexio.server.services.agent_success_evaluation.agent_success_evaluation_utils import (
22
19
  AgentSuccessEvaluationRequest,
23
20
  )
24
- from reflexio.server.services.agent_success_evaluation.delayed_group_evaluator import (
21
+ from reflexio.server.services.agent_success_evaluation.scheduler import (
25
22
  _EFFECTIVE_DELAY_SECONDS,
26
23
  )
24
+ from reflexio.server.services.agent_success_evaluation.service import (
25
+ AgentSuccessEvaluationService,
26
+ )
27
+ from reflexio.server.services.extractor_config_utils import get_extractor_name
27
28
  from reflexio.server.services.shadow_comparison.judge import ShadowComparisonJudge
28
29
 
29
30
  logger = logging.getLogger(__name__)
@@ -166,13 +167,13 @@ def run_group_evaluation(
166
167
  # afterwards cannot remove the new rows.
167
168
  old_result_ids: list[int] = []
168
169
  if force_regenerate:
169
- # Eval rows per session are tiny (usually 1), so pulling a
170
- # generous limit and filtering in-process is cheap and avoids adding
171
- # a third query shape to the storage contract.
172
- prior_rows = storage.get_agent_success_evaluation_results( # type: ignore[reportOptionalMemberAccess]
173
- limit=10000, agent_version=agent_version
170
+ config = request_context.configurator.get_config()
171
+ old_result_ids = storage.get_agent_success_evaluation_result_ids( # type: ignore[reportOptionalMemberAccess]
172
+ user_id=user_id,
173
+ session_id=session_id,
174
+ evaluation_name=get_extractor_name(config),
175
+ agent_version=agent_version,
174
176
  )
175
- old_result_ids = [r.result_id for r in prior_rows if r.session_id == session_id]
176
177
 
177
178
  logger.info(
178
179
  "Running group evaluation for session=%s with %d requests and %d interactions"
@@ -185,6 +186,7 @@ def run_group_evaluation(
185
186
  )
186
187
 
187
188
  evaluation_request = AgentSuccessEvaluationRequest(
189
+ user_id=user_id,
188
190
  session_id=session_id,
189
191
  agent_version=agent_version,
190
192
  source=source,
@@ -18,9 +18,17 @@ from reflexio.server.services.agent_success_evaluation import _eval_health
18
18
 
19
19
  logger = logging.getLogger(__name__)
20
20
 
21
- # Delay in seconds before evaluating a group after the last request
22
- GROUP_EVALUATION_DELAY_SECONDS = 600 # 10 minutes
21
+ # Default delay in seconds before evaluating a group after the last request.
22
+ _DEFAULT_DELAY_SECONDS = 600 # 10 minutes
23
23
 
24
+ # Inactivity delay before a session is evaluated. Override the default via the
25
+ # GROUP_EVALUATION_DELAY_SECONDS environment variable (in seconds).
26
+ GROUP_EVALUATION_DELAY_SECONDS = int(
27
+ os.environ.get("GROUP_EVALUATION_DELAY_SECONDS", str(_DEFAULT_DELAY_SECONDS))
28
+ )
29
+
30
+ # Kept as a patch point for tests. Production should use the full inactivity
31
+ # delay unless a test deliberately lowers this constant.
24
32
  IS_TEST_ENV = os.environ.get("IS_TEST_ENV", "false").strip() == "true"
25
33
  _EFFECTIVE_DELAY_SECONDS = 30 if IS_TEST_ENV else GROUP_EVALUATION_DELAY_SECONDS
26
34