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
@@ -1,12 +1,12 @@
1
1
  """Request CRUD methods for SQLite storage."""
2
2
 
3
- import json
4
3
  import sqlite3
5
4
  from typing import Any
6
5
 
7
6
  from reflexio.models.api_schema.internal_schema import (
8
7
  RequestInteractionDataModel,
9
8
  SessionDescriptor,
9
+ SessionFirstRequest,
10
10
  )
11
11
  from reflexio.models.api_schema.service_schemas import (
12
12
  Request,
@@ -15,6 +15,7 @@ from reflexio.models.api_schema.service_schemas import (
15
15
  from ._base import (
16
16
  SQLiteStorageBase,
17
17
  _epoch_to_iso,
18
+ _iso_to_epoch,
18
19
  _row_to_interaction,
19
20
  _row_to_request,
20
21
  )
@@ -39,7 +40,7 @@ class RequestMixin:
39
40
  created_at_iso = _epoch_to_iso(request.created_at)
40
41
  self._execute(
41
42
  """INSERT OR REPLACE INTO requests
42
- (request_id, user_id, created_at, source, agent_version, session_id, metadata)
43
+ (request_id, user_id, created_at, source, agent_version, session_id, evaluation_only)
43
44
  VALUES (?,?,?,?,?,?,?)""",
44
45
  (
45
46
  request.request_id,
@@ -48,7 +49,7 @@ class RequestMixin:
48
49
  request.source,
49
50
  request.agent_version,
50
51
  request.session_id,
51
- json.dumps(request.metadata, sort_keys=True),
52
+ 1 if request.evaluation_only else 0,
52
53
  ),
53
54
  )
54
55
 
@@ -261,3 +262,76 @@ class RequestMixin:
261
262
  )
262
263
  for r in rows
263
264
  ]
265
+
266
+ @SQLiteStorageBase.handle_exceptions
267
+ def get_first_requests_by_session_ids(
268
+ self, session_ids: list[str]
269
+ ) -> dict[str, SessionFirstRequest]:
270
+ if not session_ids:
271
+ return {}
272
+ out: dict[str, SessionFirstRequest] = {}
273
+ ids = sorted(set(session_ids))
274
+ chunk_size = 500
275
+ for i in range(0, len(ids), chunk_size):
276
+ chunk = ids[i : i + chunk_size]
277
+ ph = ",".join("?" for _ in chunk)
278
+ rows = self._fetchall(
279
+ f"""SELECT session_id, user_id, source, created_at
280
+ FROM (
281
+ SELECT session_id, user_id, source, created_at,
282
+ ROW_NUMBER() OVER (
283
+ PARTITION BY session_id
284
+ ORDER BY created_at ASC, request_id ASC
285
+ ) AS rn
286
+ FROM requests
287
+ WHERE session_id IN ({ph})
288
+ )
289
+ WHERE rn = 1""", # noqa: S608
290
+ chunk,
291
+ )
292
+ for row in rows:
293
+ session_id = row["session_id"]
294
+ out[session_id] = SessionFirstRequest(
295
+ session_id=session_id,
296
+ user_id=row["user_id"],
297
+ source=row["source"] or "",
298
+ created_at=_iso_to_epoch(row["created_at"]),
299
+ )
300
+ return out
301
+
302
+ @SQLiteStorageBase.handle_exceptions
303
+ def get_first_requests_by_user_session_pairs(
304
+ self, pairs: list[tuple[str, str]]
305
+ ) -> dict[tuple[str, str], SessionFirstRequest]:
306
+ if not pairs:
307
+ return {}
308
+ out: dict[tuple[str, str], SessionFirstRequest] = {}
309
+ pair_list = sorted(set(pairs))
310
+ chunk_size = 300
311
+ for i in range(0, len(pair_list), chunk_size):
312
+ chunk = pair_list[i : i + chunk_size]
313
+ values = ",".join("(?, ?)" for _ in chunk)
314
+ params = [value for pair in chunk for value in pair]
315
+ rows = self._fetchall(
316
+ f"""SELECT session_id, user_id, source, created_at
317
+ FROM (
318
+ SELECT session_id, user_id, source, created_at,
319
+ ROW_NUMBER() OVER (
320
+ PARTITION BY user_id, session_id
321
+ ORDER BY created_at ASC, request_id ASC
322
+ ) AS rn
323
+ FROM requests
324
+ WHERE (user_id, session_id) IN ({values})
325
+ )
326
+ WHERE rn = 1""", # noqa: S608
327
+ params,
328
+ )
329
+ for row in rows:
330
+ key = (row["user_id"], row["session_id"])
331
+ out[key] = SessionFirstRequest(
332
+ session_id=row["session_id"],
333
+ user_id=row["user_id"],
334
+ source=row["source"] or "",
335
+ created_at=_iso_to_epoch(row["created_at"]),
336
+ )
337
+ return out
@@ -13,13 +13,6 @@ from reflexio.models.api_schema.eval_overview_schema import (
13
13
 
14
14
  from ._base import SQLiteStorageBase, _epoch_to_iso
15
15
 
16
- # Maximum epoch seconds that ``datetime.fromtimestamp`` can represent (year
17
- # 9999-12-31). Callers passing sentinel "open" upper bounds like
18
- # ``sys.maxsize`` or ``10**12`` would otherwise overflow ``_epoch_to_iso``;
19
- # clamping to this value yields the same query semantics (≥ everything) with
20
- # a valid ISO string.
21
- _MAX_SAFE_EPOCH_TS = 253_402_300_799 # 9999-12-31T23:59:59Z
22
-
23
16
 
24
17
  def _parse_dt(value: str) -> datetime:
25
18
  """
@@ -164,8 +157,8 @@ class ShadowVerdictsMixin:
164
157
  list[ShadowComparisonVerdict]: Verdicts in chronological order
165
158
  (ascending ``created_at``).
166
159
  """
167
- from_iso = _epoch_to_iso(max(0, min(from_ts, _MAX_SAFE_EPOCH_TS)))
168
- to_iso = _epoch_to_iso(max(0, min(to_ts, _MAX_SAFE_EPOCH_TS)))
160
+ from_iso = _epoch_to_iso(from_ts)
161
+ to_iso = _epoch_to_iso(to_ts)
169
162
  rows = self._fetchall(
170
163
  """SELECT * FROM shadow_comparison_verdicts
171
164
  WHERE created_at >= ? AND created_at <= ?
@@ -175,6 +168,27 @@ class ShadowVerdictsMixin:
175
168
  )
176
169
  return [_row_to_verdict(r) for r in rows]
177
170
 
171
+ @SQLiteStorageBase.handle_exceptions
172
+ def get_recent_shadow_comparison_verdicts(
173
+ self,
174
+ from_ts: int,
175
+ to_ts: int,
176
+ judge_prompt_version: str,
177
+ limit: int,
178
+ ) -> list[ShadowComparisonVerdict]:
179
+ from_iso = _epoch_to_iso(from_ts)
180
+ to_iso = _epoch_to_iso(to_ts)
181
+ safe_limit = max(0, limit)
182
+ rows = self._fetchall(
183
+ """SELECT * FROM shadow_comparison_verdicts
184
+ WHERE created_at >= ? AND created_at <= ?
185
+ AND judge_prompt_version = ?
186
+ ORDER BY created_at DESC
187
+ LIMIT ?""",
188
+ (from_iso, to_iso, judge_prompt_version, safe_limit),
189
+ )
190
+ return [_row_to_verdict(r) for r in rows]
191
+
178
192
  @SQLiteStorageBase.handle_exceptions
179
193
  def delete_shadow_comparison_verdicts_by_session(self, session_id: str) -> int:
180
194
  """
@@ -11,7 +11,7 @@ from __future__ import annotations
11
11
  import logging
12
12
  import sqlite3
13
13
  from dataclasses import dataclass
14
- from datetime import datetime
14
+ from datetime import UTC, datetime
15
15
  from typing import Any
16
16
 
17
17
  from reflexio.models.api_schema.stall_state_schema import StallReason
@@ -163,10 +163,13 @@ def _parse_ts(raw: str | None) -> datetime | None:
163
163
  if raw is None:
164
164
  return None
165
165
  try:
166
- return datetime.fromisoformat(raw)
166
+ parsed = datetime.fromisoformat(raw)
167
167
  except ValueError:
168
168
  _LOGGER.warning("Malformed timestamp in stall_state: %r", raw)
169
169
  return None
170
+ if parsed.tzinfo is None:
171
+ parsed = parsed.replace(tzinfo=UTC)
172
+ return parsed
170
173
 
171
174
 
172
175
  class SQLiteStallStateMixin:
@@ -23,10 +23,12 @@ from ._agent_run import (
23
23
  )
24
24
  from ._base import BaseStorageCore, matches_status_filter
25
25
  from ._extras import ExtrasMixin
26
+ from ._lineage import EntityType, LineageEventMixin
26
27
  from ._operations import OperationMixin
27
28
  from ._playbook import PlaybookMixin
28
29
  from ._profiles import ProfileMixin
29
30
  from ._requests import RequestMixin
31
+ from ._retrieval_log import RetrievalLogMixin
30
32
  from ._shadow_verdicts import ShadowVerdictsMixin
31
33
  from ._share_links import ShareLinkMixin
32
34
  from ._stall_state import StallStateMixin
@@ -37,6 +39,8 @@ class BaseStorage(
37
39
  ProfileMixin,
38
40
  RequestMixin,
39
41
  PlaybookMixin,
42
+ RetrievalLogMixin,
43
+ LineageEventMixin,
40
44
  OperationMixin,
41
45
  ExtrasMixin,
42
46
  ShareLinkMixin,
@@ -46,6 +50,65 @@ class BaseStorage(
46
50
  ):
47
51
  """Base class for storage."""
48
52
 
53
+ def _is_lineage_tombstone(self, entity_type: EntityType, entity_id: str) -> bool:
54
+ """Return True when the entity row is a tombstone (merged_into or superseded_by set).
55
+
56
+ Uses the portable ``include_tombstones=True`` getters so the check
57
+ works for every backend without backend-specific SQL.
58
+
59
+ Args:
60
+ entity_type (EntityType): ``"profile"`` or ``"user_playbook"``.
61
+ entity_id (str): The entity's primary key as a string.
62
+
63
+ Returns:
64
+ bool: True if the row has ``merged_into`` or ``superseded_by`` set.
65
+ """
66
+ if entity_type == "profile":
67
+ row = self.get_profile_by_id(entity_id, include_tombstones=True)
68
+ if row is None:
69
+ return False
70
+ return bool(row.merged_into or row.superseded_by)
71
+ if entity_type == "user_playbook":
72
+ row = self.get_user_playbook_by_id(int(entity_id), include_tombstones=True)
73
+ if row is None:
74
+ return False
75
+ return bool(row.merged_into or row.superseded_by)
76
+ return False
77
+
78
+ def _partition_purge_vs_delete(
79
+ self, entity_type: EntityType, ids: list[str]
80
+ ) -> tuple[list[str], list[str]]:
81
+ """Split entity ids into purge-eligible and hard-delete sets.
82
+
83
+ An entity is purge-eligible when it is a tombstone (``merged_into`` or
84
+ ``superseded_by`` is set) **or** is pointed to by another row
85
+ (``has_inbound_lineage_refs`` returns True). All other ids go to the
86
+ hard-delete set.
87
+
88
+ Uses only portable storage reads so this helper works for every
89
+ backend (SQLite, Supabase, Postgres). Callers that need
90
+ backend-specific efficiency may override ``clear_user_data`` instead.
91
+
92
+ Args:
93
+ entity_type (EntityType): ``"profile"`` or ``"user_playbook"``.
94
+ ids (list[str]): Entity primary keys as strings.
95
+
96
+ Returns:
97
+ tuple[list[str], list[str]]: ``(purge_ids, delete_ids)`` where
98
+ ``purge_ids`` are content-purge candidates and ``delete_ids``
99
+ are safe to hard-delete.
100
+ """
101
+ purge_ids: list[str] = []
102
+ delete_ids: list[str] = []
103
+ for eid in ids:
104
+ if self._is_lineage_tombstone(
105
+ entity_type, eid
106
+ ) or self.has_inbound_lineage_refs(entity_type=entity_type, entity_id=eid):
107
+ purge_ids.append(eid)
108
+ else:
109
+ delete_ids.append(eid)
110
+ return purge_ids, delete_ids
111
+
49
112
  def clear_user_data(self, user_id: str) -> dict[str, int]:
50
113
  """Delete all rows scoped to a single ``user_id``.
51
114
 
@@ -57,38 +120,72 @@ class BaseStorage(
57
120
  parallel tasks without one task's clear-all nuking another
58
121
  in-flight task's rows.
59
122
 
123
+ **Lineage-aware erasure:** rows that are tombstones (``merged_into``
124
+ or ``superseded_by`` is set) *or* are pointed to by another row
125
+ (``has_inbound_lineage_refs`` returns True) are content-purged
126
+ (skeleton kept, body blanked) rather than hard-deleted. Standalone
127
+ rows with no lineage involvement are hard-deleted.
128
+
60
129
  The default implementation composes existing per-user / by-ids
61
130
  primitives so any backend that implements those (sqlite, supabase,
62
131
  postgres, ...) gets correct behaviour for free.
63
132
  Subclasses MAY override for atomic / transactional efficiency.
64
133
 
134
+ **Atomicity note (default path only):** this default implementation is
135
+ NOT wrapped in a single transaction. Each ``purge_content`` call commits
136
+ independently, so a crash mid-erasure may leave some purge-eligible rows
137
+ with PII intact until ``clear_user_data`` is re-invoked. The call is
138
+ idempotent — re-invoking it is safe and will finish the erasure.
139
+
65
140
  Args:
66
141
  user_id (str): The user id whose rows should be deleted.
67
142
 
68
143
  Returns:
69
- dict[str, int]: Per-entity deletion counts with keys
70
- ``interactions``, ``user_playbooks``, ``profiles``, and
71
- ``requests``. Counts reflect pre-deletion totals for the
72
- user (i.e. how many rows existed for that user).
144
+ dict[str, int]: Per-entity counts with keys ``interactions``,
145
+ ``user_playbooks``, ``profiles``, ``requests``,
146
+ ``purged_profiles``, and ``purged_user_playbooks``.
147
+ ``profiles`` and ``user_playbooks`` reflect hard-deleted
148
+ counts; purged rows are counted separately.
73
149
  """
74
150
  interaction_count = len(self.get_user_interaction(user_id))
75
151
 
76
- # Snapshot user_playbook ids for the user so we can both count
77
- # and delete via the existing bulk-by-ids primitive (no per-user
78
- # playbook delete primitive exists at the mixin level).
79
- user_playbook_ids = [
80
- up.user_playbook_id
152
+ # All statuses a user's row can have including tombstones (SUPERSEDED,
153
+ # MERGED). Erasure MUST reach every row the user owns regardless of
154
+ # status; the old filter excluded tombstones, leaving them in the DB
155
+ # after clear_user_data (GDPR regression).
156
+ _all_statuses: list[Status | None] = [
157
+ None, # CURRENT
158
+ Status.ARCHIVED,
159
+ Status.PENDING,
160
+ Status.ARCHIVE_IN_PROGRESS,
161
+ Status.SUPERSEDED,
162
+ Status.MERGED,
163
+ ]
164
+
165
+ # Snapshot user_playbook ids for the user and partition into
166
+ # purge vs delete sets.
167
+ raw_upb_ids = [
168
+ str(up.user_playbook_id)
81
169
  for up in self.get_user_playbooks(
82
170
  user_id=user_id,
83
171
  limit=1_000_000,
84
- status_filter=[None, Status.ARCHIVED, Status.PENDING],
172
+ status_filter=_all_statuses,
85
173
  )
86
174
  if up.user_playbook_id is not None
87
175
  ]
88
- profile_count = len(
89
- self.get_user_profile(
90
- user_id, status_filter=[None, Status.ARCHIVED, Status.PENDING]
91
- )
176
+ purge_upb_ids, delete_upb_ids = self._partition_purge_vs_delete(
177
+ "user_playbook", raw_upb_ids
178
+ )
179
+
180
+ # Snapshot profile ids for the user and partition into
181
+ # purge vs delete sets.
182
+ raw_profile_ids = [
183
+ p.profile_id
184
+ for p in self.get_user_profile(user_id, status_filter=_all_statuses)
185
+ if p.profile_id is not None
186
+ ]
187
+ purge_profile_ids, delete_profile_ids = self._partition_purge_vs_delete(
188
+ "profile", raw_profile_ids
92
189
  )
93
190
 
94
191
  # Snapshot request ids for the user so we can both count and
@@ -103,30 +200,43 @@ class BaseStorage(
103
200
  ]
104
201
 
105
202
  # Delete in dependency-safe order: interactions first (they
106
- # reference requests), then user playbooks (also reference
107
- # requests), then profiles, then the requests themselves.
203
+ # reference requests), then user playbooks, then profiles, then
204
+ # requests. Only the delete-sets are hard-deleted; purge-sets are
205
+ # content-purged below.
108
206
  self.delete_all_interactions_for_user(user_id)
109
207
  deleted_user_playbooks = (
110
- self.delete_user_playbooks_by_ids(user_playbook_ids)
111
- if user_playbook_ids
208
+ self.delete_user_playbooks_by_ids([int(i) for i in delete_upb_ids])
209
+ if delete_upb_ids
112
210
  else 0
113
211
  )
114
- self.delete_all_profiles_for_user(user_id)
212
+ deleted_profiles = (
213
+ self.delete_profiles_by_ids(delete_profile_ids) if delete_profile_ids else 0
214
+ )
115
215
  deleted_requests = (
116
216
  self.delete_requests_by_ids(request_ids) if request_ids else 0
117
217
  )
118
218
 
219
+ # Content-purge the purge-eligible rows after the hard-deletes.
220
+ for pid in purge_profile_ids:
221
+ self.purge_content(entity_type="profile", entity_id=pid)
222
+ for upid in purge_upb_ids:
223
+ self.purge_content(entity_type="user_playbook", entity_id=upid)
224
+
119
225
  return {
120
226
  "interactions": interaction_count,
121
227
  "user_playbooks": deleted_user_playbooks,
122
- "profiles": profile_count,
228
+ "profiles": deleted_profiles,
123
229
  "requests": deleted_requests,
230
+ "purged_profiles": len(purge_profile_ids),
231
+ "purged_user_playbooks": len(purge_upb_ids),
124
232
  }
125
233
 
126
234
 
127
235
  __all__ = [
128
236
  "AgentBinding",
129
237
  "AgentRunMixin",
238
+ "EntityType",
239
+ "LineageEventMixin",
130
240
  "AgentRunRecord",
131
241
  "AgentRunStatus",
132
242
  "BaseStorage",
@@ -135,6 +245,7 @@ __all__ = [
135
245
  "PendingToolCallStatus",
136
246
  "PendingToolCallUpsertResult",
137
247
  "PlaybookMixin",
248
+ "RetrievalLogMixin",
138
249
  "PriorAnswerMatch",
139
250
  "RunToolDependencyKind",
140
251
  "RunToolDependencyRecord",
@@ -11,6 +11,8 @@ from datetime import datetime
11
11
  from enum import StrEnum
12
12
  from typing import Any
13
13
 
14
+ from reflexio.models.api_schema.pending_tool_call_schema import PendingToolCallStatus
15
+
14
16
  _WHITESPACE_RE = re.compile(r"\s+")
15
17
 
16
18
 
@@ -28,14 +30,6 @@ class AgentRunStatus(StrEnum):
28
30
  CANCELLED = "cancelled"
29
31
 
30
32
 
31
- class PendingToolCallStatus(StrEnum):
32
- PENDING = "pending"
33
- RESOLVED = "resolved"
34
- EXPIRED = "expired"
35
- SUPERSEDED = "superseded"
36
- CANCELLED = "cancelled"
37
-
38
-
39
33
  NOT_APPLICABLE_ANSWER = "User does not have information about this question."
40
34
 
41
35
 
@@ -40,6 +40,11 @@ class BaseStorageCore(ABC): # noqa: B024
40
40
  # Capability flag — backends that implement `_get_embedding` set this to True.
41
41
  supports_embedding: ClassVar[bool] = False
42
42
 
43
+ # Capability flag — backends that can serve every unified-search arm in a
44
+ # single database round trip expose a `unified_hybrid_search` method and
45
+ # set this to True (see reflexio.server.services.unified_search_service).
46
+ supports_unified_hybrid_search: ClassVar[bool] = False
47
+
43
48
  def __init__(self, org_id: str, base_dir: str | None = None) -> None:
44
49
  self.org_id = org_id
45
50
  if base_dir is None:
@@ -6,14 +6,13 @@ from reflexio.models.api_schema.braintrust_schema import (
6
6
  )
7
7
  from reflexio.models.api_schema.domain import (
8
8
  Interaction,
9
- PlaybookAggregationChangeLog,
10
- ProfileChangeLog,
11
9
  )
10
+ from reflexio.models.api_schema.internal_schema import SessionCitation
12
11
  from reflexio.models.api_schema.retriever_schema import PlaybookApplicationStat
13
12
 
14
13
 
15
14
  class ExtrasMixin:
16
- """Mixin for dashboard, profile change logs, playbook aggregation change logs, and misc methods."""
15
+ """Mixin for dashboard, profile change logs, and misc methods."""
17
16
 
18
17
  # ==============================
19
18
  # Dashboard methods
@@ -72,56 +71,6 @@ class ExtrasMixin:
72
71
  """
73
72
  raise NotImplementedError
74
73
 
75
- # ==============================
76
- # Profile Change Log methods
77
- # ==============================
78
-
79
- @abstractmethod
80
- def add_profile_change_log(self, profile_change_log: ProfileChangeLog) -> None:
81
- """Add a profile change log entry."""
82
- raise NotImplementedError
83
-
84
- @abstractmethod
85
- def get_profile_change_logs(self, limit: int = 100) -> list[ProfileChangeLog]:
86
- """Get profile change logs for an organization."""
87
- raise NotImplementedError
88
-
89
- @abstractmethod
90
- def delete_profile_change_log_for_user(self, user_id: str) -> None:
91
- """Delete all profile change logs for a user."""
92
- raise NotImplementedError
93
-
94
- @abstractmethod
95
- def delete_all_profile_change_logs(self) -> None:
96
- """Delete all profile change logs."""
97
- raise NotImplementedError
98
-
99
- # ==============================
100
- # Playbook Aggregation Change Log methods
101
- # ==============================
102
-
103
- @abstractmethod
104
- def add_playbook_aggregation_change_log(
105
- self, change_log: PlaybookAggregationChangeLog
106
- ) -> None:
107
- """Add a playbook aggregation change log entry."""
108
- raise NotImplementedError
109
-
110
- @abstractmethod
111
- def get_playbook_aggregation_change_logs(
112
- self,
113
- playbook_name: str,
114
- agent_version: str,
115
- limit: int = 100,
116
- ) -> list[PlaybookAggregationChangeLog]:
117
- """Get playbook aggregation change logs filtered by playbook_name and agent_version."""
118
- raise NotImplementedError
119
-
120
- @abstractmethod
121
- def delete_all_playbook_aggregation_change_logs(self) -> None:
122
- """Delete all playbook aggregation change logs."""
123
- raise NotImplementedError
124
-
125
74
  # ==============================
126
75
  # Misc methods
127
76
  # ==============================
@@ -171,6 +120,40 @@ class ExtrasMixin:
171
120
  """
172
121
  return []
173
122
 
123
+ def get_citations_by_session_ids(
124
+ self,
125
+ session_ids: list[str],
126
+ ) -> list[SessionCitation]:
127
+ """Return rule/profile citations for the requested sessions.
128
+
129
+ Default implementation uses ``get_interactions_by_session`` so legacy
130
+ backends keep working. SQL backends should override with a bulk
131
+ request/interactions join.
132
+ """
133
+ out: list[SessionCitation] = []
134
+ for session_id in set(session_ids):
135
+ for interaction in self.get_interactions_by_session(session_id):
136
+ for cite in getattr(interaction, "citations", []) or []:
137
+ if isinstance(cite, dict):
138
+ kind = cite.get("kind")
139
+ real_id = cite.get("real_id")
140
+ title = cite.get("title") or ""
141
+ else:
142
+ kind = getattr(cite, "kind", None)
143
+ real_id = getattr(cite, "real_id", None)
144
+ title = getattr(cite, "title", "") or ""
145
+ if kind and real_id:
146
+ out.append(
147
+ SessionCitation(
148
+ user_id="",
149
+ session_id=session_id,
150
+ kind=str(kind),
151
+ real_id=str(real_id),
152
+ title=str(title),
153
+ )
154
+ )
155
+ return out
156
+
174
157
  # ==============================
175
158
  # Braintrust connector (default no-ops; backends override)
176
159
  # ==============================