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,14 +1,21 @@
1
1
  import logging
2
+ from collections import defaultdict
2
3
 
3
4
  logger = logging.getLogger(__name__)
4
5
 
5
6
  from datetime import UTC, datetime
7
+ from typing import Protocol
6
8
 
7
9
  from reflexio.lib._base import (
8
10
  STORAGE_NOT_CONFIGURED_MSG,
9
11
  ReflexioBase,
10
12
  _require_storage,
11
13
  )
14
+ from reflexio.models.api_schema.domain.entities import (
15
+ LineageEvent,
16
+ ProfileChangeLog,
17
+ UserProfile,
18
+ )
12
19
  from reflexio.models.api_schema.retriever_schema import (
13
20
  GetProfileStatisticsResponse,
14
21
  GetUserProfilesRequest,
@@ -36,7 +43,7 @@ from reflexio.models.api_schema.service_schemas import (
36
43
  UpgradeProfilesRequest,
37
44
  UpgradeProfilesResponse,
38
45
  )
39
- from reflexio.server.services.profile.profile_generation_service import (
46
+ from reflexio.server.services.profile.service import (
40
47
  ProfileGenerationService,
41
48
  )
42
49
  from reflexio.server.tracing import profile_step
@@ -199,15 +206,21 @@ class ProfilesMixin(ReflexioBase):
199
206
  )
200
207
 
201
208
  def get_profile_change_logs(self) -> ProfileChangeLogResponse:
202
- """Get profile change logs.
209
+ """Get profile change logs, served from the lineage reconstruction.
210
+
211
+ The change-log view is rebuilt on demand from ``lineage_event`` linkage
212
+ joined to survivor/tombstone content via
213
+ :func:`reconstruct_profile_change_log`. The legacy ``profile_change_logs``
214
+ table is no longer written (B3 Task 6) or read (B3 Task 7); it is retained
215
+ frozen until its removal in Task 8.
203
216
 
204
217
  Returns:
205
- ProfileChangeLogResponse: Response containing profile change logs
218
+ ProfileChangeLogResponse: Response containing the reconstructed
219
+ profile change logs.
206
220
  """
207
221
  if not self._is_storage_configured():
208
222
  return ProfileChangeLogResponse(success=True, profile_change_logs=[])
209
- changelogs = self._get_storage().get_profile_change_logs()
210
- return ProfileChangeLogResponse(success=True, profile_change_logs=changelogs)
223
+ return reconstruct_profile_change_log(self._get_storage())
211
224
 
212
225
  @_require_storage(DeleteUserProfileResponse)
213
226
  def delete_profile(
@@ -401,7 +414,7 @@ class ProfilesMixin(ReflexioBase):
401
414
  status_filter = [None] # Default to current profiles
402
415
 
403
416
  profiles = self._get_storage().get_user_profile(
404
- request.user_id, status_filter=status_filter
417
+ request.user_id, status_filter=status_filter, tags=request.tags
405
418
  )
406
419
  profiles = sorted(
407
420
  profiles, key=lambda x: x.last_modified_timestamp, reverse=True
@@ -543,3 +556,182 @@ class ProfilesMixin(ReflexioBase):
543
556
  return GetProfileStatisticsResponse(
544
557
  success=False, msg=f"Failed to get profile statistics: {str(e)}"
545
558
  )
559
+
560
+
561
+ # ---------------------------------------------------------------------------
562
+ # Standalone read-side reconstruction (Phase B3 Task 2)
563
+ # ---------------------------------------------------------------------------
564
+
565
+
566
+ class ChangeLogReadStorage(Protocol):
567
+ """The storage read surface ``reconstruct_profile_change_log`` requires.
568
+
569
+ Both the real ``BaseStorage`` backends and the read-only
570
+ ``RestStorageReader`` satisfy this structurally — no inheritance required —
571
+ so reconstruction can run against either without an unsound ``cast`` to
572
+ ``BaseStorage``.
573
+ """
574
+
575
+ org_id: str
576
+
577
+ def get_lineage_events(
578
+ self,
579
+ *,
580
+ entity_type: str | None = ...,
581
+ entity_id: str | None = ...,
582
+ org_id: str | None = ...,
583
+ request_id: str | None = ...,
584
+ ) -> list[LineageEvent]: ...
585
+
586
+ def get_distinct_generated_from_request_ids(self) -> list[str]: ...
587
+
588
+ def get_profiles_by_generated_from_request_id(
589
+ self, request_id: str
590
+ ) -> list[UserProfile]: ...
591
+
592
+ def get_all_generated_profiles(self) -> list[UserProfile]: ...
593
+
594
+ def get_profile_by_id(
595
+ self, profile_id: str, *, include_tombstones: bool = ...
596
+ ) -> UserProfile | None: ...
597
+
598
+
599
+ def reconstruct_profile_change_log(
600
+ storage: ChangeLogReadStorage,
601
+ *,
602
+ limit: int = 100,
603
+ ) -> ProfileChangeLogResponse:
604
+ """Rebuild the ProfileChangeLog view using time-travel-stable signals.
605
+
606
+ Uses two immutable / stable signals to classify every dedup run:
607
+
608
+ * **added(R)** — profiles whose ``generated_from_request_id == R``. This
609
+ column is set at creation and never changes, so it correctly classifies
610
+ a profile as "added in run R" even if it is later tombstoned in run R2.
611
+ Tombstones are included so the content is available.
612
+
613
+ * **removed(R)** — entity_ids of ``status_change`` lineage events with
614
+ ``to_status == "superseded"`` and ``request_id == R``. This is the
615
+ exact signature emitted by ``supersede_profiles_by_ids`` (the dedup
616
+ soft-delete path). It is distinct from reflection which emits
617
+ ``op="revise"``, so reflection events are never mis-counted as removals.
618
+
619
+ Groups are formed over the union of request_ids from both signals.
620
+ Request_id ``""`` is skipped — it would merge unrelated runs.
621
+ A row is emitted only when ``added or removed`` is non-empty (matching
622
+ legacy semantics: the legacy table was written only when
623
+ ``all_new_profiles or superseded_profiles``).
624
+
625
+ When a removed profile's tombstone has been physically purged (GDPR GC),
626
+ it is silently omitted from ``removed_profiles`` rather than crashing.
627
+
628
+ Args:
629
+ storage (ChangeLogReadStorage): Storage read surface to query (any
630
+ BaseStorage backend or the read-only ``RestStorageReader``).
631
+ limit (int): Maximum number of reconstructed entries to return.
632
+ Defaults to 100.
633
+
634
+ Returns:
635
+ ProfileChangeLogResponse: ``success=True`` with reconstructed rows
636
+ ordered most-recent first (by max event ``created_at`` in each
637
+ request_id group), capped at ``limit``.
638
+ """
639
+ if limit <= 0:
640
+ return ProfileChangeLogResponse(success=True, profile_change_logs=[])
641
+
642
+ all_events = storage.get_lineage_events(
643
+ entity_type="profile", org_id=storage.org_id
644
+ )
645
+
646
+ # Dedup soft-delete signature: status_change to_status=="superseded".
647
+ # Each such event records one profile removed in the dedup run ``request_id``.
648
+ # Distinct from reflection which emits op="revise" — so revise events are
649
+ # never counted as removals here.
650
+ removal_by_req: dict[str, list[str]] = defaultdict(list)
651
+ sort_key: dict[str, tuple[int, int]] = {} # request_id -> (created_at, event_id)
652
+ for evt in all_events:
653
+ key = evt.request_id
654
+ if not key:
655
+ continue # skip empty-string request_ids — never merge unrelated runs
656
+ cur = sort_key.get(key, (0, 0))
657
+ if (evt.created_at, evt.event_id) > cur:
658
+ sort_key[key] = (evt.created_at, evt.event_id)
659
+ if evt.op == "status_change" and evt.to_status == "superseded":
660
+ removal_by_req[key].append(evt.entity_id)
661
+
662
+ # Resolve the "added" side for every run in ONE bulk read, grouped by each
663
+ # profile's immutable generated_from_request_id. This replaces a read per
664
+ # candidate request_id, which fanned out over the org's whole dedup history
665
+ # before the limit slice below — a hot-path N+1 on network-backed storage now
666
+ # that the live endpoint serves this reconstruction.
667
+ added_by_req: dict[str, list] = defaultdict(list)
668
+ for profile in storage.get_all_generated_profiles():
669
+ added_by_req[profile.generated_from_request_id].append(profile)
670
+
671
+ # Candidate request_ids are the UNION of:
672
+ # (a) lineage EVENT request_ids — runs that produced a dedup removal
673
+ # (status_change/superseded event); and
674
+ # (b) request_ids stamped on profile rows (the keys of added_by_req) —
675
+ # discovers ADD-ONLY dedup runs (new profiles, nothing superseded) that
676
+ # emit no lineage event. ``get_all_generated_profiles`` already excludes
677
+ # the empty-string sentinel, so unrelated runs are never merged.
678
+ #
679
+ # This closes the reconstruction-completeness gap for add-only runs: the
680
+ # legacy `add_profile_change_log` fired whenever `all_new_profiles or
681
+ # superseded_profiles` was non-empty, so a run with only adds was still
682
+ # recorded. The (b) path mirrors that.
683
+ #
684
+ # For add-only runs (in set (b) but not (a)), no event timestamp exists.
685
+ # We derive their sort key from the max `last_modified_timestamp` of the
686
+ # profiles in that group — set at creation, giving a sensible most-recent-first
687
+ # ordering relative to event-timestamped runs. The secondary key is 0.
688
+ candidate_req_ids: set[str] = set(sort_key.keys()) | set(added_by_req.keys())
689
+
690
+ def _effective_sort_key(req_id: str) -> tuple[int, int]:
691
+ """Return (timestamp, event_id) for sorting; for add-only runs fall back to
692
+ the max last_modified_timestamp of the profiles in that group."""
693
+ if req_id in sort_key:
694
+ return sort_key[req_id]
695
+ profiles = added_by_req.get(req_id, [])
696
+ max_ts = max((p.last_modified_timestamp for p in profiles), default=0)
697
+ return (max_ts, 0)
698
+
699
+ sorted_keys = sorted(
700
+ candidate_req_ids,
701
+ key=_effective_sort_key,
702
+ reverse=True,
703
+ )[:limit]
704
+
705
+ logs: list[ProfileChangeLog] = []
706
+ for req_id in sorted_keys:
707
+ # added: profiles whose generated_from_request_id == req_id (any status,
708
+ # include tombstones — a profile added in R1 and tombstoned in R2 is still
709
+ # "added in R1").
710
+ added = added_by_req[req_id]
711
+
712
+ # removed: dedup-superseded profiles from this run's lineage events.
713
+ removed: list = []
714
+ for entity_id in removal_by_req.get(req_id, []):
715
+ profile = storage.get_profile_by_id(entity_id, include_tombstones=True)
716
+ if profile is not None:
717
+ removed.append(profile)
718
+ # Tombstone physically purged (GDPR GC) → silently omit; no crash.
719
+
720
+ if not added and not removed:
721
+ # No dedup activity for this request_id — skip to match legacy semantics.
722
+ continue
723
+
724
+ ts, _ = _effective_sort_key(req_id)
725
+ user_id = added[0].user_id if added else removed[0].user_id
726
+ logs.append(
727
+ ProfileChangeLog(
728
+ id=0,
729
+ user_id=user_id,
730
+ request_id=req_id,
731
+ created_at=ts,
732
+ added_profiles=added,
733
+ removed_profiles=removed,
734
+ )
735
+ )
736
+
737
+ return ProfileChangeLogResponse(success=True, profile_change_logs=logs)
@@ -41,7 +41,7 @@ class ReflectionMixin(ReflexioBase):
41
41
 
42
42
  # Local import keeps the heavy service module out of facade
43
43
  # cold-start when reflection is disabled.
44
- from reflexio.server.services.reflection.reflection_service import (
44
+ from reflexio.server.services.reflection.service import (
45
45
  ReflectionService,
46
46
  )
47
47
 
@@ -13,6 +13,7 @@ from reflexio.models.api_schema.retriever_schema import (
13
13
  )
14
14
  from reflexio.server.llm.model_defaults import ModelRole, resolve_model_name
15
15
  from reflexio.server.site_var.site_var_manager import SiteVarManager
16
+ from reflexio.server.tracing import profile_step
16
17
 
17
18
  _LOGGER = logging.getLogger(__name__)
18
19
 
@@ -136,32 +137,40 @@ class SearchMixin(ReflexioBase):
136
137
  if isinstance(request, dict):
137
138
  request = UnifiedSearchRequest(**request)
138
139
 
139
- config = self.request_context.configurator.get_config()
140
+ with profile_step(
141
+ "search.prepare",
142
+ enabled=bool(request.enable_reformulation),
143
+ has_conversation_history=bool(request.conversation_history),
144
+ search_mode=request.search_mode,
145
+ ):
146
+ config = self.request_context.configurator.get_config()
147
+ config_llm_config = config.llm_config if config else None
148
+
149
+ # Resolve pre_retrieval_model_name: config override -> site var -> auto-detect.
150
+ model_setting = SiteVarManager().get_site_var("llm_model_setting")
151
+ site_var = model_setting if isinstance(model_setting, dict) else {}
152
+ api_key_config = config.api_key_config if config else None
153
+
154
+ pre_retrieval_model_name = resolve_model_name(
155
+ ModelRole.PRE_RETRIEVAL,
156
+ site_var_value=site_var.get("pre_retrieval_model_name"),
157
+ config_override=config_llm_config.pre_retrieval_model_name
158
+ if config_llm_config
159
+ else None,
160
+ api_key_config=api_key_config,
161
+ )
162
+ storage = self._get_storage()
163
+ prompt_manager = self.request_context.prompt_manager
164
+ retrieval_floor = config.retrieval_floor if config else None
140
165
 
141
166
  from reflexio.server.services.unified_search_service import run_unified_search
142
167
 
143
- config_llm_config = config.llm_config if config else None
144
-
145
- # Resolve pre_retrieval_model_name: config override → site var → auto-detect
146
- model_setting = SiteVarManager().get_site_var("llm_model_setting")
147
- site_var = model_setting if isinstance(model_setting, dict) else {}
148
- api_key_config = config.api_key_config if config else None
149
-
150
- pre_retrieval_model_name = resolve_model_name(
151
- ModelRole.PRE_RETRIEVAL,
152
- site_var_value=site_var.get("pre_retrieval_model_name"),
153
- config_override=config_llm_config.pre_retrieval_model_name
154
- if config_llm_config
155
- else None,
156
- api_key_config=api_key_config,
157
- )
158
-
159
168
  return run_unified_search(
160
169
  request=request,
161
170
  org_id=org_id,
162
- storage=self._get_storage(),
171
+ storage=storage,
163
172
  llm_client=self.llm_client,
164
- prompt_manager=self.request_context.prompt_manager,
173
+ prompt_manager=prompt_manager,
165
174
  pre_retrieval_model_name=pre_retrieval_model_name,
166
- retrieval_floor=config.retrieval_floor if config else None,
175
+ retrieval_floor=retrieval_floor,
167
176
  )
@@ -24,7 +24,7 @@ from reflexio.models.api_schema.service_schemas import (
24
24
  UpgradeUserPlaybooksResponse,
25
25
  )
26
26
  from reflexio.models.config_schema import SearchOptions
27
- from reflexio.server.services.playbook.playbook_generation_service import (
27
+ from reflexio.server.services.playbook.service import (
28
28
  PlaybookGenerationService,
29
29
  )
30
30
  from reflexio.server.tracing import profile_step
@@ -57,6 +57,7 @@ class UserPlaybookMixin(ReflexioBase):
57
57
  playbook_name=request.playbook_name,
58
58
  agent_version=request.agent_version,
59
59
  status_filter=request.status_filter,
60
+ tags=request.tags,
60
61
  )
61
62
  return GetUserPlaybooksResponse(
62
63
  success=True,
@@ -118,6 +118,11 @@ __all__ = [
118
118
  "ShareLink",
119
119
  "AdminInvalidateCacheRequest",
120
120
  "AdminInvalidateCacheResponse",
121
+ "PlaybookRetrievalLogItem",
122
+ "PlaybookRetrievalLog",
123
+ "LineageEvent",
124
+ "LineageContext",
125
+ "RecordRef",
121
126
  ]
122
127
 
123
128
  # ===============================
@@ -179,11 +184,7 @@ class Request(BaseModel):
179
184
  """A user-issued request that begins or continues a session.
180
185
 
181
186
  A Request is the unit of work the agent reacts to. Multiple Requests
182
- share a ``session_id`` to form a multi-turn session. The optional
183
- ``metadata`` dict carries per-request key/value annotations stamped by
184
- customer integration code — distinct from ``playbook_metadata`` (a
185
- JSON-encoded string) used by ``Playbook``-family entities elsewhere in
186
- this module.
187
+ share a ``session_id`` to form a multi-turn session.
187
188
 
188
189
  Attributes:
189
190
  request_id (str): Unique identifier for this request.
@@ -192,22 +193,10 @@ class Request(BaseModel):
192
193
  to the current UTC time.
193
194
  source (str): Free-form origin tag (integration name, etc.).
194
195
  agent_version (str): The agent version that handled this request.
195
- session_id (str | None): Session this request belongs to, or None
196
- if the request is not part of a multi-turn session.
197
- metadata (dict[str, Any]): Free-form per-request annotations.
198
- Always a dict — never None. Conventional keys:
199
-
200
- - ``reflexio_retrieval_enabled`` (bool): F2 group-by signal;
201
- customer integration code stamps this to indicate whether
202
- Reflexio retrieval was active for the session. Read by the
203
- ``/api/get_evaluation_overview`` aggregator from the FIRST
204
- request of each session for sticky group assignment.
205
-
206
- Distinct from ``playbook_metadata`` on ``Playbook``-family
207
- entities (which is a JSON-encoded string, not a dict).
208
- On-the-wire serialization assumes JSON-encodable values;
209
- non-JSON values like ``datetime`` or ``set`` will fail at
210
- the storage / API boundary.
196
+ session_id (str): Non-empty session this request belongs to.
197
+ evaluation_only (bool): Whether this request is stored for
198
+ session-level evaluation only and must be excluded from
199
+ profile/playbook learning windows.
211
200
  """
212
201
 
213
202
  request_id: str
@@ -215,8 +204,8 @@ class Request(BaseModel):
215
204
  created_at: int = Field(default_factory=lambda: int(datetime.now(UTC).timestamp()))
216
205
  source: str = ""
217
206
  agent_version: str = ""
218
- session_id: str | None = None
219
- metadata: dict[str, Any] = Field(default_factory=dict)
207
+ session_id: NonEmptyStr
208
+ evaluation_only: bool = False
220
209
 
221
210
 
222
211
  # information about the user profile generated from the user interaction
@@ -237,10 +226,14 @@ class UserProfile(BaseModel):
237
226
  None # Retained provenance data column (merged on dedup); new profiles write None.
238
227
  )
239
228
  expanded_terms: str | None = None
229
+ tags: list[str] | None = None # None = not yet tagged; [] = tagged, no match
230
+ source_interaction_ids: list[int] = Field(default_factory=list)
240
231
  embedding: EmbeddingVector = []
241
232
  source_span: str | None = None
242
233
  notes: str | None = None
243
234
  reader_angle: str | None = None
235
+ merged_into: str | None = None
236
+ superseded_by: str | None = None
244
237
 
245
238
 
246
239
  # user playbook for agents
@@ -261,10 +254,13 @@ class UserPlaybook(BaseModel):
261
254
  source: str | None = None # source of the interaction that generated this playbook
262
255
  source_interaction_ids: list[int] = Field(default_factory=list)
263
256
  expanded_terms: str | None = None
257
+ tags: list[str] | None = None # None = not yet tagged; [] = tagged, no match
264
258
  embedding: EmbeddingVector = []
265
259
  source_span: str | None = None
266
260
  notes: str | None = None
267
261
  reader_angle: str | None = None
262
+ merged_into: int | None = None
263
+ superseded_by: int | None = None
268
264
 
269
265
 
270
266
  class ProfileChangeLog(BaseModel):
@@ -274,7 +270,6 @@ class ProfileChangeLog(BaseModel):
274
270
  created_at: int = Field(default_factory=lambda: int(datetime.now(UTC).timestamp()))
275
271
  added_profiles: list[UserProfile]
276
272
  removed_profiles: list[UserProfile]
277
- mentioned_profiles: list[UserProfile]
278
273
 
279
274
 
280
275
  class AgentPlaybook(BaseModel):
@@ -289,10 +284,13 @@ class AgentPlaybook(BaseModel):
289
284
  playbook_status: PlaybookStatus = PlaybookStatus.PENDING
290
285
  playbook_metadata: str = ""
291
286
  expanded_terms: str | None = None
287
+ tags: list[str] | None = None # None = not yet tagged; [] = tagged, no match
292
288
  embedding: EmbeddingVector = []
293
289
  status: Status | None = (
294
290
  None # used for tracking intermediate states during playbook aggregation. Status.ARCHIVED for playbooks during aggregation process, None for current playbooks
295
291
  )
292
+ merged_into: int | None = None
293
+ superseded_by: int | None = None
296
294
 
297
295
 
298
296
  class PlaybookOptimizationJob(BaseModel):
@@ -330,6 +328,7 @@ class PlaybookOptimizationCandidate(BaseModel):
330
328
  parent_candidate_ids: list[int] = Field(default_factory=list)
331
329
  aggregate_score: float | None = None
332
330
  is_winner: bool = False
331
+ metadata_json: str = "{}"
333
332
  created_at: int = Field(default_factory=lambda: int(datetime.now(UTC).timestamp()))
334
333
 
335
334
 
@@ -384,6 +383,7 @@ class AgentPlaybookSourceWindow(BaseModel):
384
383
 
385
384
  class AgentSuccessEvaluationResult(BaseModel):
386
385
  result_id: int = 0
386
+ user_id: str = ""
387
387
  agent_version: str
388
388
  session_id: str
389
389
  is_success: bool
@@ -398,6 +398,100 @@ class AgentSuccessEvaluationResult(BaseModel):
398
398
  embedding: EmbeddingVector = []
399
399
 
400
400
 
401
+ class PlaybookRetrievalLogItem(BaseModel):
402
+ """One retrieved agent playbook plus the serve-time attribution snapshot."""
403
+
404
+ retrieval_log_item_id: int = 0
405
+ retrieval_log_id: int = 0
406
+ ordinal: int
407
+ agent_playbook_id: int
408
+ source_user_playbook_ids: list[int] = Field(default_factory=list)
409
+ source_interaction_ids_by_user_playbook_id: dict[str, list[int]] = Field(
410
+ default_factory=dict
411
+ )
412
+
413
+
414
+ class PlaybookRetrievalLog(BaseModel):
415
+ """A retrieval-log header with ordered item rows.
416
+
417
+ Used by retrieval-capture consumers to correlate retrieval decisions with
418
+ downstream outcomes. ``retrieval_log_id`` is assigned by the storage layer;
419
+ ``shown_items`` stores ids and serve-time attribution snapshots only.
420
+ """
421
+
422
+ retrieval_log_id: int = 0
423
+ request_id: str
424
+ session_id: str
425
+ interaction_id: int | None = None
426
+ user_id: str
427
+ query: str | None = None
428
+ agent_version: str | None = None
429
+ shown_items: list[PlaybookRetrievalLogItem] = Field(default_factory=list)
430
+ created_at: int = 0
431
+
432
+
433
+ class LineageEvent(BaseModel):
434
+ """Append-only, content-free provenance record. NEVER carries content/PII.
435
+
436
+ Attributes:
437
+ event_id (int): PK assigned by storage (0 = not yet persisted).
438
+ org_id (str): Owning org (tenant) — required for RLS / isolation.
439
+ entity_type (str): One of "profile" | "user_playbook" | "agent_playbook".
440
+ entity_id (str): The affected record's id, stringified (profile_id is str).
441
+ op (str): create|revise|merge|aggregate|archive|soft_delete|hard_delete|purge|status_change.
442
+ prov_relation (str): W3C PROV relation (see spec §14).
443
+ source_ids (list[str]): Records merged/superseded into entity_id.
444
+ actor (str): Who/what triggered it (consolidator|reflection|offline_optimizer|...).
445
+ request_id (str): Triggering request — part of the idempotency key.
446
+ reason (str): Free-text rationale (no PII).
447
+ created_at (int): Unix epoch seconds (0 = unset; storage stamps it).
448
+ """
449
+
450
+ event_id: int = 0
451
+ org_id: str
452
+ entity_type: str
453
+ entity_id: str
454
+ op: str
455
+ prov_relation: str = ""
456
+ source_ids: list[str] = []
457
+ actor: str = ""
458
+ request_id: str = ""
459
+ reason: str = ""
460
+ created_at: int = 0
461
+ from_status: str | None = None
462
+ to_status: str | None = None
463
+ status_namespace: str | None = None
464
+
465
+
466
+ class LineageContext(BaseModel):
467
+ """Caller-supplied intent the storage layer can't infer.
468
+
469
+ Required for merge/supersede/aggregate; optional for create/revise/archive.
470
+ """
471
+
472
+ op_kind: str
473
+ actor: str = ""
474
+ source_ids: list[str] = []
475
+ reason: str = ""
476
+ request_id: str | None = None
477
+
478
+
479
+ class RecordRef(BaseModel):
480
+ """Result of resolve_current — the live survivor's id and whether its body was purged.
481
+
482
+ Attributes:
483
+ id: Primary key of the live survivor record.
484
+ is_purged: True when the survivor's content body has been blanked by
485
+ ``purge_content`` (GDPR/erasure). Any consumer that dereferences the
486
+ resolved record's content MUST treat ``is_purged=True`` as "erased —
487
+ skip or treat as absent." Reading blank content as if it were valid
488
+ is a silent data-quality bug.
489
+ """
490
+
491
+ id: str
492
+ is_purged: bool = False
493
+
494
+
401
495
  class ShareLink(BaseModel):
402
496
  """A shareable public link that maps a token to a resource within an org.
403
497
 
@@ -563,26 +657,21 @@ class PublishUserInteractionRequest(BaseModel):
563
657
  agent_version: str = (
564
658
  "" # this is used for aggregating interactions for generating agent playbooks
565
659
  )
566
- session_id: str | None = None # used for grouping requests together
660
+ session_id: NonEmptyStr # used for grouping requests together
567
661
  skip_aggregation: bool = (
568
662
  False # when True, extract profiles/playbooks but skip aggregation
569
663
  )
570
664
  force_extraction: bool = False # when True, bypass all extraction gates (stride_size, cheap pre-filter, LLM should_run) and always run extractors
665
+ evaluation_only: bool = False # when True, store for evaluation and permanently exclude from profile/playbook extraction
571
666
  override_learning_stall: bool = False # when True, run extraction even if a provider auth/billing stall is recorded
572
- metadata: dict[str, Any] = Field(default_factory=dict)
573
- """Per-request annotations stamped by customer integration code.
574
-
575
- Mirrors ``Request.metadata`` — the publish path copies this dict
576
- onto the ``Request`` row it creates so the eval pipeline (and the
577
- F2 sticky-group aggregator in particular) can read it back from
578
- the first request of each session.
579
667
 
580
- Conventional keys:
581
- - ``reflexio_retrieval_enabled`` (bool): F2 group assignment signal.
582
-
583
- Defaults to ``{}`` (never None) for backward compatibility — existing
584
- callers that don't pass ``metadata`` keep working unchanged.
585
- """
668
+ @model_validator(mode="after")
669
+ def validate_evaluation_only(self) -> Self:
670
+ if self.evaluation_only and self.force_extraction:
671
+ raise ValueError("evaluation_only cannot be combined with force_extraction")
672
+ if self.evaluation_only and not self.session_id:
673
+ raise ValueError("evaluation_only publishes require session_id")
674
+ return self
586
675
 
587
676
 
588
677
  # publish user interaction response
@@ -42,6 +42,10 @@ class Status(str, Enum): # noqa: UP042 - CURRENT=None is not compatible with St
42
42
  ARCHIVE_IN_PROGRESS = (
43
43
  "archive_in_progress" # temporary status during downgrade operation
44
44
  )
45
+ MERGED = "merged" # tombstone: consolidated into a survivor (merged_into set)
46
+ SUPERSEDED = (
47
+ "superseded" # tombstone: replaced by a new version (superseded_by set)
48
+ )
45
49
 
46
50
 
47
51
  class OperationStatus(StrEnum):