claude-smart 0.2.45 → 0.2.46

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (322) hide show
  1. package/.claude-plugin/marketplace.json +3 -3
  2. package/README.md +34 -5
  3. package/bin/claude-smart.js +295 -5
  4. package/package.json +22 -3
  5. package/plugin/.claude-plugin/plugin.json +4 -2
  6. package/plugin/.codex-plugin/plugin.json +1 -1
  7. package/plugin/README.md +22 -2
  8. package/plugin/commands/clear-all.md +1 -0
  9. package/plugin/commands/dashboard.md +1 -0
  10. package/plugin/commands/learn.md +1 -0
  11. package/plugin/commands/restart.md +1 -0
  12. package/plugin/commands/show.md +1 -0
  13. package/plugin/dashboard/app/configure/env/page.tsx +40 -14
  14. package/plugin/dashboard/app/configure/server/page.tsx +51 -1
  15. package/plugin/dashboard/app/preferences/[id]/page.tsx +8 -8
  16. package/plugin/dashboard/app/skills/project/[id]/page.tsx +7 -3
  17. package/plugin/dashboard/app/skills/shared/[id]/page.tsx +10 -3
  18. package/plugin/dashboard/lib/claude-settings-file.ts +20 -10
  19. package/plugin/dashboard/lib/reflexio-client.ts +16 -0
  20. package/plugin/dashboard/lib/status.ts +10 -3
  21. package/plugin/dashboard/lib/types.ts +18 -6
  22. package/plugin/hooks/codex-hooks.json +0 -1
  23. package/plugin/opencode/assistant-buffer.ts +108 -0
  24. package/plugin/opencode/dist/assistant-buffer.js +96 -0
  25. package/plugin/opencode/dist/internal.js +12 -0
  26. package/plugin/opencode/dist/payload.js +85 -0
  27. package/plugin/opencode/dist/server.mjs +158 -0
  28. package/plugin/opencode/internal.ts +18 -0
  29. package/plugin/opencode/package.json +3 -0
  30. package/plugin/opencode/payload.ts +90 -0
  31. package/plugin/opencode/server.mts +174 -0
  32. package/plugin/opencode/tsconfig.json +13 -0
  33. package/plugin/pyproject.toml +20 -3
  34. package/plugin/scripts/_lib.sh +5 -1
  35. package/plugin/scripts/backend-service.sh +22 -2
  36. package/plugin/scripts/ensure-plugin-root.sh +4 -4
  37. package/plugin/scripts/hook_entry.sh +1 -1
  38. package/plugin/scripts/opencode-claude-compat +4 -0
  39. package/plugin/scripts/opencode-claude-compat.cmd +3 -0
  40. package/plugin/scripts/opencode-claude-compat.js +225 -0
  41. package/plugin/scripts/smart-install.sh +10 -8
  42. package/plugin/src/README.md +1 -1
  43. package/plugin/src/claude_smart/cli.py +304 -6
  44. package/plugin/src/claude_smart/env_config.py +12 -4
  45. package/plugin/src/claude_smart/events/session_start.py +26 -7
  46. package/plugin/src/claude_smart/events/stop.py +1 -1
  47. package/plugin/src/claude_smart/ids.py +1 -1
  48. package/plugin/src/claude_smart/reflexio_adapter.py +5 -5
  49. package/plugin/src/claude_smart/runtime.py +7 -1
  50. package/plugin/uv.lock +1 -1
  51. package/plugin/vendor/reflexio/.env.example +54 -30
  52. package/plugin/vendor/reflexio/README.md +14 -8
  53. package/plugin/vendor/reflexio/pyproject.toml +13 -1
  54. package/plugin/vendor/reflexio/reflexio/README.md +1 -0
  55. package/plugin/vendor/reflexio/reflexio/cli/README.md +9 -7
  56. package/plugin/vendor/reflexio/reflexio/cli/__main__.py +10 -1
  57. package/plugin/vendor/reflexio/reflexio/cli/bootstrap_config.py +22 -5
  58. package/plugin/vendor/reflexio/reflexio/cli/commands/interactions.py +34 -4
  59. package/plugin/vendor/reflexio/reflexio/cli/commands/profiles.py +12 -7
  60. package/plugin/vendor/reflexio/reflexio/cli/commands/services.py +13 -6
  61. package/plugin/vendor/reflexio/reflexio/cli/commands/setup_cmd.py +210 -50
  62. package/plugin/vendor/reflexio/reflexio/cli/commands/shortcuts.py +15 -2
  63. package/plugin/vendor/reflexio/reflexio/cli/env_loader.py +292 -22
  64. package/plugin/vendor/reflexio/reflexio/cli/log_format.py +29 -4
  65. package/plugin/vendor/reflexio/reflexio/cli/run_services.py +17 -8
  66. package/plugin/vendor/reflexio/reflexio/cli/stop_services.py +13 -10
  67. package/plugin/vendor/reflexio/reflexio/client/client.py +57 -13
  68. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/README.md +135 -257
  69. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/npm/openclaw-smart/bin/openclaw-smart.js +13 -0
  70. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/npm/openclaw-smart/package.json +15 -0
  71. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/README.md +38 -0
  72. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/index.ts +151 -110
  73. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/openclaw.plugin.json +20 -10
  74. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/package.json +28 -6
  75. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/pyproject.toml +42 -0
  76. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/_lib.sh +371 -0
  77. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/backend-log-runner.sh +33 -0
  78. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/backend-service.sh +271 -0
  79. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/cli.sh +68 -0
  80. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/dashboard-open.sh +15 -0
  81. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/ensure-plugin-root.sh +84 -0
  82. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/hook_entry.sh +106 -0
  83. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/npm-cli.js +219 -0
  84. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/smart-install.sh +269 -0
  85. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/clear-all/SKILL.md +8 -0
  86. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/dashboard/SKILL.md +8 -0
  87. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/learn/SKILL.md +10 -0
  88. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/reflexio/SKILL.md +15 -44
  89. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/restart/SKILL.md +6 -0
  90. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/show/SKILL.md +8 -0
  91. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/cli.py +634 -0
  92. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/context_format.py +224 -0
  93. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/context_inject.py +79 -0
  94. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/__init__.py +0 -0
  95. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/after_tool_call.py +160 -0
  96. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/agent_end.py +187 -0
  97. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/before_prompt_build.py +69 -0
  98. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/before_tool_call.py +30 -0
  99. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/session_end.py +36 -0
  100. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/session_start.py +130 -0
  101. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/hook.py +131 -0
  102. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/ids.py +94 -0
  103. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/internal_call.py +75 -0
  104. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/oc_cite.py +196 -0
  105. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/optimizer_assistant.py +272 -0
  106. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/publish.py +96 -0
  107. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/query_compose.py +66 -0
  108. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/reflexio_adapter.py +336 -0
  109. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/runtime.py +47 -0
  110. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/stall_banner.py +61 -0
  111. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/state.py +323 -0
  112. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/__init__.py +0 -0
  113. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/integration/__init__.py +0 -0
  114. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/integration/test_e2e_session_loop.py +190 -0
  115. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/integration/test_publish_to_local_reflexio_integration.py +112 -0
  116. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/integration/test_recursion_guard_integration.py +86 -0
  117. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/integration/test_search_inject_integration.py +144 -0
  118. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_cli.py +184 -0
  119. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_events_after_tool_call.py +142 -0
  120. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_events_agent_end.py +233 -0
  121. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_events_before_prompt_build.py +116 -0
  122. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_events_before_tool_call.py +35 -0
  123. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_events_session_end.py +47 -0
  124. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_events_session_start.py +109 -0
  125. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_hook_dispatch.py +117 -0
  126. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_ids.py +41 -0
  127. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_internal_call.py +50 -0
  128. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_oc_cite.py +88 -0
  129. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_publish.py +48 -0
  130. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_query_compose.py +48 -0
  131. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_reflexio_adapter.py +188 -0
  132. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_runtime.py +41 -0
  133. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_state.py +235 -0
  134. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests-ts/__mocks__/plugin-entry-stub.ts +6 -0
  135. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests-ts/test_npm_cli.test.ts +56 -0
  136. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests-ts/test_shim_dispatch.test.ts +171 -0
  137. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tsconfig.build.json +18 -0
  138. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/{tsconfig.json → plugin/tsconfig.json} +2 -2
  139. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/uv.lock +3835 -0
  140. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/vitest.config.ts +18 -0
  141. package/plugin/vendor/reflexio/reflexio/lib/_agent_playbook.py +238 -10
  142. package/plugin/vendor/reflexio/reflexio/lib/_config.py +11 -4
  143. package/plugin/vendor/reflexio/reflexio/lib/_generation.py +16 -5
  144. package/plugin/vendor/reflexio/reflexio/lib/_lineage_parity_readers.py +187 -0
  145. package/plugin/vendor/reflexio/reflexio/lib/_profiles.py +198 -6
  146. package/plugin/vendor/reflexio/reflexio/lib/_reflection.py +1 -1
  147. package/plugin/vendor/reflexio/reflexio/lib/_search.py +29 -20
  148. package/plugin/vendor/reflexio/reflexio/lib/_user_playbook.py +2 -1
  149. package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/entities.py +127 -38
  150. package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/enums.py +4 -0
  151. package/plugin/vendor/reflexio/reflexio/models/api_schema/eval_overview_schema.py +64 -41
  152. package/plugin/vendor/reflexio/reflexio/models/api_schema/internal_schema.py +19 -0
  153. package/plugin/vendor/reflexio/reflexio/models/api_schema/pending_tool_call_schema.py +12 -5
  154. package/plugin/vendor/reflexio/reflexio/models/api_schema/retriever_schema.py +52 -0
  155. package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/converters.py +5 -1
  156. package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/entities.py +5 -1
  157. package/plugin/vendor/reflexio/reflexio/models/config_schema.py +88 -7
  158. package/plugin/vendor/reflexio/reflexio/models/structured_output.py +148 -0
  159. package/plugin/vendor/reflexio/reflexio/server/OVERVIEW.md +3 -3
  160. package/plugin/vendor/reflexio/reflexio/server/README.md +45 -32
  161. package/plugin/vendor/reflexio/reflexio/server/__init__.py +29 -5
  162. package/plugin/vendor/reflexio/reflexio/server/__main__.py +2 -2
  163. package/plugin/vendor/reflexio/reflexio/server/_auth.py +65 -2
  164. package/plugin/vendor/reflexio/reflexio/server/api.py +734 -85
  165. package/plugin/vendor/reflexio/reflexio/server/api_endpoints/precondition_checks.py +7 -0
  166. package/plugin/vendor/reflexio/reflexio/server/api_endpoints/request_context.py +3 -1
  167. package/plugin/vendor/reflexio/reflexio/server/billing_meter.py +173 -0
  168. package/plugin/vendor/reflexio/reflexio/server/billing_signals.py +64 -0
  169. package/plugin/vendor/reflexio/reflexio/server/cache/reflexio_cache.py +46 -16
  170. package/plugin/vendor/reflexio/reflexio/server/env_utils.py +65 -0
  171. package/plugin/vendor/reflexio/reflexio/server/llm/embedding_service.py +19 -4
  172. package/plugin/vendor/reflexio/reflexio/server/llm/litellm_client.py +264 -77
  173. package/plugin/vendor/reflexio/reflexio/server/llm/llm_utils.py +69 -7
  174. package/plugin/vendor/reflexio/reflexio/server/llm/providers/embedding_service_provider.py +75 -16
  175. package/plugin/vendor/reflexio/reflexio/server/llm/providers/openclaw_provider.py +280 -0
  176. package/plugin/vendor/reflexio/reflexio/server/llm/token_accounting.py +48 -0
  177. package/plugin/vendor/reflexio/reflexio/server/llm/tools.py +107 -17
  178. package/plugin/vendor/reflexio/reflexio/server/operation_limiter.py +360 -4
  179. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.6.0.prompt.md +1 -1
  180. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.7.0.prompt.md +85 -0
  181. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_aggregation/v2.2.0.prompt.md +1 -1
  182. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_aggregation/v2.3.0.prompt.md +253 -0
  183. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.3.2.prompt.md +1 -1
  184. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.3.3.prompt.md +74 -0
  185. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.2.3.prompt.md +1 -1
  186. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.3.0.prompt.md +266 -0
  187. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.4.0.prompt.md +266 -0
  188. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v3.3.0.prompt.md +1 -1
  189. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v3.4.0.prompt.md +137 -0
  190. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main/v1.2.0.prompt.md +1 -1
  191. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main/v1.3.0.prompt.md +34 -0
  192. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_update_instruction_start/v1.0.0.prompt.md +5 -12
  193. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_update_instruction_start/v1.1.0.prompt.md +5 -12
  194. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_update_instruction_start/v1.2.0.prompt.md +155 -0
  195. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/tagging/v1.0.0.prompt.md +23 -0
  196. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_manager.py +89 -32
  197. package/plugin/vendor/reflexio/reflexio/server/services/README.md +26 -11
  198. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/README.md +22 -0
  199. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/__init__.py +1 -0
  200. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/agent_success_evaluation_constants.py +4 -2
  201. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/agent_success_evaluation_utils.py +1 -0
  202. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/components/__init__.py +7 -0
  203. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/{agent_success_evaluator.py → components/evaluator.py} +3 -16
  204. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/regen_jobs.py +28 -72
  205. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/{group_evaluation_runner.py → runner.py} +12 -10
  206. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/{delayed_group_evaluator.py → scheduler.py} +10 -2
  207. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/{agent_success_evaluation_service.py → service.py} +36 -1
  208. package/plugin/vendor/reflexio/reflexio/server/services/base_generation_service.py +160 -0
  209. package/plugin/vendor/reflexio/reflexio/server/services/configurator/base_configurator.py +5 -0
  210. package/plugin/vendor/reflexio/reflexio/server/services/deduplication_utils.py +1 -1
  211. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/README.md +9 -0
  212. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/components/__init__.py +0 -0
  213. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/{rule_attribution.py → components/rule_attribution.py} +10 -9
  214. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/eval_sampler.py +15 -24
  215. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/service.py +238 -164
  216. package/plugin/vendor/reflexio/reflexio/server/services/extraction/README.md +31 -0
  217. package/plugin/vendor/reflexio/reflexio/server/services/extraction/outcome.py +13 -3
  218. package/plugin/vendor/reflexio/reflexio/server/services/extraction/pending_tool_call_dispatch.py +3 -2
  219. package/plugin/vendor/reflexio/reflexio/server/services/extraction/resumable_agent.py +32 -118
  220. package/plugin/vendor/reflexio/reflexio/server/services/extraction/resume_worker.py +39 -16
  221. package/plugin/vendor/reflexio/reflexio/server/services/generation_service.py +105 -23
  222. package/plugin/vendor/reflexio/reflexio/server/services/lineage/__init__.py +0 -0
  223. package/plugin/vendor/reflexio/reflexio/server/services/lineage/gc_scheduler.py +193 -0
  224. package/plugin/vendor/reflexio/reflexio/server/services/lineage/resolve.py +98 -0
  225. package/plugin/vendor/reflexio/reflexio/server/services/operation_state_utils.py +26 -0
  226. package/plugin/vendor/reflexio/reflexio/server/services/playbook/README.md +11 -12
  227. package/plugin/vendor/reflexio/reflexio/server/services/playbook/components/__init__.py +5 -0
  228. package/plugin/vendor/reflexio/reflexio/server/services/playbook/{playbook_aggregator.py → components/aggregator.py} +316 -178
  229. package/plugin/vendor/reflexio/reflexio/server/services/playbook/{playbook_consolidator.py → components/consolidator.py} +168 -62
  230. package/plugin/vendor/reflexio/reflexio/server/services/playbook/{playbook_extractor.py → components/extractor.py} +5 -1
  231. package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_edit_apply.py +74 -0
  232. package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_service_utils.py +33 -3
  233. package/plugin/vendor/reflexio/reflexio/server/services/playbook/{playbook_generation_service.py → service.py} +121 -19
  234. package/plugin/vendor/reflexio/reflexio/server/services/playbook/user_detail_stripping.py +84 -0
  235. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/README.md +10 -0
  236. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/models.py +2 -1
  237. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/optimizer.py +178 -30
  238. package/plugin/vendor/reflexio/reflexio/server/services/pre_retrieval/README.md +9 -0
  239. package/plugin/vendor/reflexio/reflexio/server/services/profile/components/__init__.py +17 -0
  240. package/plugin/vendor/reflexio/reflexio/server/services/profile/{profile_deduplicator.py → components/consolidator.py} +7 -6
  241. package/plugin/vendor/reflexio/reflexio/server/services/profile/{profile_extractor.py → components/extractor.py} +30 -22
  242. package/plugin/vendor/reflexio/reflexio/server/services/profile/profile_generation_service_utils.py +6 -13
  243. package/plugin/vendor/reflexio/reflexio/server/services/profile/{profile_generation_service.py → service.py} +32 -50
  244. package/plugin/vendor/reflexio/reflexio/server/services/reflection/__init__.py +4 -8
  245. package/plugin/vendor/reflexio/reflexio/server/services/reflection/components/__init__.py +7 -0
  246. package/plugin/vendor/reflexio/reflexio/server/services/reflection/reflection_service_utils.py +12 -1
  247. package/plugin/vendor/reflexio/reflexio/server/services/reflection/{reflection_service.py → service.py} +36 -16
  248. package/plugin/vendor/reflexio/reflexio/server/services/retrieval/relevance_floor.py +83 -6
  249. package/plugin/vendor/reflexio/reflexio/server/services/service_utils.py +36 -1
  250. package/plugin/vendor/reflexio/reflexio/server/services/shadow_comparison/README.md +8 -0
  251. package/plugin/vendor/reflexio/reflexio/server/services/shadow_comparison/outcome.py +1 -2
  252. package/plugin/vendor/reflexio/reflexio/server/services/storage/error.py +27 -0
  253. package/plugin/vendor/reflexio/reflexio/server/services/storage/retention.py +9 -9
  254. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/__init__.py +2 -0
  255. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_agent_run.py +4 -1
  256. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_base.py +642 -197
  257. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_extras.py +61 -104
  258. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_lineage.py +628 -0
  259. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_operations.py +38 -6
  260. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_playbook.py +978 -184
  261. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_profiles.py +519 -128
  262. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_requests.py +77 -3
  263. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_shadow_verdicts.py +23 -9
  264. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_stall_state.py +5 -2
  265. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/__init__.py +131 -20
  266. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_agent_run.py +2 -8
  267. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_base.py +5 -0
  268. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_extras.py +36 -53
  269. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_lineage.py +215 -0
  270. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_operations.py +20 -0
  271. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_playbook.py +243 -7
  272. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_profiles.py +121 -1
  273. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_requests.py +60 -0
  274. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_retrieval_log.py +51 -0
  275. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_shadow_verdicts.py +17 -0
  276. package/plugin/vendor/reflexio/reflexio/server/services/tagging/README.md +8 -0
  277. package/plugin/vendor/reflexio/reflexio/server/services/tagging/__init__.py +1 -0
  278. package/plugin/vendor/reflexio/reflexio/server/services/tagging/service.py +200 -0
  279. package/plugin/vendor/reflexio/reflexio/server/services/tagging/tagging_scheduler.py +149 -0
  280. package/plugin/vendor/reflexio/reflexio/server/services/unified_search_service.py +229 -32
  281. package/plugin/vendor/reflexio/reflexio/server/site_var/README.md +2 -1
  282. package/plugin/vendor/reflexio/reflexio/server/site_var/feature_flags.py +120 -1
  283. package/plugin/vendor/reflexio/reflexio/server/site_var/site_var_sources/feature_flags.json +4 -0
  284. package/plugin/vendor/reflexio/reflexio/server/site_var/site_var_sources/search_settings.json +5 -0
  285. package/plugin/vendor/reflexio/reflexio/server/tracing.py +30 -0
  286. package/plugin/vendor/reflexio/reflexio/server/usage_metrics.py +18 -0
  287. package/plugin/vendor/reflexio/reflexio/test_support/llm_mock.py +61 -26
  288. package/plugin/vendor/reflexio/reflexio/test_support/llm_model_registry.py +41 -4
  289. package/scripts/setup-claude-smart.sh +8 -3
  290. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/TESTING.md +0 -517
  291. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/hook/handler.js +0 -473
  292. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/package-lock.json +0 -2156
  293. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/package.json +0 -18
  294. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/hook/handler.ts +0 -241
  295. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/hook/setup.ts +0 -140
  296. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/publish.ts +0 -113
  297. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/search.ts +0 -52
  298. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/server.ts +0 -103
  299. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/sqlite-buffer.ts +0 -156
  300. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/user-id.ts +0 -134
  301. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/rules/reflexio.md +0 -24
  302. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/publish_clawhub.sh +0 -278
  303. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/references/HOOK.md +0 -164
  304. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/scripts/install.sh +0 -36
  305. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/scripts/uninstall.sh +0 -35
  306. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/publish.test.ts +0 -27
  307. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/search.test.ts +0 -31
  308. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/server.test.ts +0 -42
  309. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/setup.test.ts +0 -49
  310. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/sqlite-buffer.test.ts +0 -91
  311. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/user-id.test.ts +0 -50
  312. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/vitest.config.ts +0 -13
  313. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/group_aggregation.py +0 -192
  314. package/plugin/vendor/reflexio/reflexio/server/services/extraction/invariants.py +0 -303
  315. package/plugin/vendor/reflexio/reflexio/server/services/extraction/plan.py +0 -138
  316. package/plugin/vendor/reflexio/reflexio/server/services/extraction/tools.py +0 -1125
  317. /package/plugin/vendor/reflexio/reflexio/integrations/{__init__.py → openclaw/plugin/src/openclaw_smart/__init__.py} +0 -0
  318. /package/plugin/vendor/reflexio/reflexio/integrations/openclaw/{types → plugin/types}/openclaw.d.ts +0 -0
  319. /package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/{distribution.py → components/distribution.py} +0 -0
  320. /package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/{hero_state.py → components/hero_state.py} +0 -0
  321. /package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/{shadow_aggregation.py → components/shadow_aggregation.py} +0 -0
  322. /package/plugin/vendor/reflexio/reflexio/server/services/reflection/{reflection_extractor.py → components/extractor.py} +0 -0
@@ -0,0 +1,9 @@
1
+ # pre_retrieval
2
+
3
+ Compact search-preparation capability.
4
+
5
+ - `__init__.py` is the public import surface for query reformulation and document expansion.
6
+ - `_query_reformulator.py` rewrites user queries and can run a caller-provided search function.
7
+ - `_document_expander.py` enriches stored documents with related terms before indexing.
8
+
9
+ This package intentionally does not use `components/`: both implementation files are already focused, and storage adapters import the package-level public surface.
@@ -0,0 +1,17 @@
1
+ """Profile service components."""
2
+
3
+ from reflexio.server.services.profile.components.consolidator import (
4
+ ProfileConsolidator,
5
+ ProfileDeduplicationOutput,
6
+ ProfileDeletionDirective,
7
+ ProfileDuplicateGroup,
8
+ )
9
+ from reflexio.server.services.profile.components.extractor import ProfileExtractor
10
+
11
+ __all__ = [
12
+ "ProfileConsolidator",
13
+ "ProfileDeduplicationOutput",
14
+ "ProfileDeletionDirective",
15
+ "ProfileDuplicateGroup",
16
+ "ProfileExtractor",
17
+ ]
@@ -1,5 +1,5 @@
1
1
  """
2
- Profile deduplication service that merges duplicate profiles from multiple extractors
2
+ Profile consolidation service that merges duplicate profiles from multiple extractors
3
3
  and against existing profiles in the database using hybrid search and LLM.
4
4
  """
5
5
 
@@ -13,6 +13,7 @@ from pydantic import BaseModel, ConfigDict, Field
13
13
 
14
14
  from reflexio.models.api_schema.retriever_schema import SearchUserProfileRequest
15
15
  from reflexio.models.api_schema.service_schemas import Status, UserProfile
16
+ from reflexio.models.structured_output import StrictStructuredOutput
16
17
  from reflexio.server.api_endpoints.request_context import RequestContext
17
18
  from reflexio.server.llm.litellm_client import LiteLLMClient
18
19
  from reflexio.server.services.deduplication_utils import (
@@ -130,7 +131,7 @@ class ProfileDeletionDirective(BaseModel):
130
131
  )
131
132
 
132
133
 
133
- class ProfileDeduplicationOutput(BaseModel):
134
+ class ProfileDeduplicationOutput(StrictStructuredOutput):
134
135
  """
135
136
  Output schema for profile deduplication with NEW/EXISTING format.
136
137
 
@@ -165,9 +166,9 @@ class ProfileDeduplicationOutput(BaseModel):
165
166
  )
166
167
 
167
168
 
168
- class ProfileDeduplicator(BaseDeduplicator):
169
+ class ProfileConsolidator(BaseDeduplicator):
169
170
  """
170
- Deduplicates new profiles against each other and against existing profiles
171
+ Consolidates new profiles against each other and against existing profiles
171
172
  in the database using hybrid search (vector + FTS) and LLM-based merging.
172
173
 
173
174
  Follows the same pattern as PlaybookConsolidator.
@@ -182,12 +183,12 @@ class ProfileDeduplicator(BaseDeduplicator):
182
183
  output_pending_status: bool = False,
183
184
  ):
184
185
  """
185
- Initialize the profile deduplicator.
186
+ Initialize the profile consolidator.
186
187
 
187
188
  Args:
188
189
  request_context: Request context with storage and prompt manager
189
190
  llm_client: Unified LLM client for LLM calls
190
- output_pending_status: When True (rerun preview mode), the deduplicator
191
+ output_pending_status: When True (rerun preview mode), the consolidator
191
192
  searches against existing PENDING profiles instead of CURRENT
192
193
  profiles. This makes rerun idempotent against pending state and
193
194
  leaves the user's current profile set untouched. When False
@@ -2,6 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  import logging
4
4
  import os
5
+ import uuid
5
6
  from datetime import UTC, datetime
6
7
  from typing import TYPE_CHECKING
7
8
 
@@ -12,11 +13,11 @@ from reflexio.models.api_schema.service_schemas import (
12
13
  from reflexio.models.config_schema import ProfileExtractorConfig
13
14
  from reflexio.server.api_endpoints.request_context import RequestContext
14
15
  from reflexio.server.llm.litellm_client import LiteLLMClient
16
+ from reflexio.server.llm.token_accounting import RunTokenTotals, sum_trace_tokens
15
17
  from reflexio.server.services.extraction.outcome import ExtractionOutcome
16
18
  from reflexio.server.services.extraction.resumable_agent import (
17
19
  run_resumable_extraction_agent,
18
20
  )
19
- from reflexio.server.services.extraction.tools import new_profile_id
20
21
  from reflexio.server.services.extractor_config_utils import get_extractor_name
21
22
  from reflexio.server.services.extractor_interaction_utils import (
22
23
  get_effective_source_filter,
@@ -25,7 +26,7 @@ from reflexio.server.services.extractor_interaction_utils import (
25
26
  from reflexio.server.services.operation_state_utils import OperationStateManager
26
27
 
27
28
  if TYPE_CHECKING:
28
- from reflexio.server.services.profile.profile_generation_service import (
29
+ from reflexio.server.services.profile.service import (
29
30
  ProfileGenerationServiceConfig,
30
31
  )
31
32
  from reflexio.server.llm.model_defaults import ModelRole, resolve_model_name
@@ -50,13 +51,26 @@ PROFILE_EXTRACTION_MAX_RETRIES = 2
50
51
  MAX_EXISTING_PROFILES_FOR_CONTEXT = 5
51
52
 
52
53
 
54
+ def new_profile_id() -> str:
55
+ """Generate a short (12-char hex) profile id.
56
+
57
+ Format chosen for LLM copy fidelity: full ``str(uuid.uuid4())`` is 36
58
+ characters of hex+dashes, error-prone for smaller LLMs to copy verbatim.
59
+ Twelve hex chars is short enough for high-fidelity copy and long enough
60
+ that birthday-paradox collision probability is vanishingly small at any
61
+ realistic per-user scale (16^12 ~= 2.8e14 unique values; PRIMARY KEY
62
+ constraint catches the rare collision).
63
+ """
64
+ return uuid.uuid4().hex[:12]
65
+
66
+
53
67
  class ProfileExtractor:
54
68
  """
55
69
  Extract user profile information from interactions.
56
70
 
57
71
  This class analyzes user interactions to extract new user profile information.
58
72
  It focuses purely on extraction — deduplication against existing profiles
59
- is handled separately by ProfileDeduplicator.
73
+ is handled separately by ProfileConsolidator.
60
74
  """
61
75
 
62
76
  def __init__(
@@ -83,6 +97,7 @@ class ProfileExtractor:
83
97
  self.service_config: ProfileGenerationServiceConfig = service_config
84
98
  self.agent_context = agent_context
85
99
  self._last_resumable_run_id: str | None = None
100
+ self._last_resumable_token_totals: RunTokenTotals | None = None
86
101
 
87
102
  # Get LLM config overrides from configuration
88
103
  config = self.request_context.configurator.get_config()
@@ -230,20 +245,17 @@ class ProfileExtractor:
230
245
  ) from e
231
246
 
232
247
  logger.info("Generated raw profiles: %s", raw_profiles)
233
- if isinstance(raw_profiles, ExtractionOutcome):
234
- user_profiles = self._convert_raw_to_user_profiles(
235
- raw_profiles=raw_profiles.items,
236
- user_id=self.service_config.user_id,
237
- request_id=self.service_config.request_id,
238
- )
239
- self._update_operation_state(request_interaction_data_models)
240
- return ExtractionOutcome.completed(
241
- user_profiles, run_id=raw_profiles.run_id
242
- )
248
+ source_interaction_ids = [
249
+ interaction.interaction_id
250
+ for request_model in request_interaction_data_models
251
+ for interaction in request_model.interactions
252
+ if interaction.interaction_id
253
+ ]
243
254
  user_profiles = self._convert_raw_to_user_profiles(
244
255
  raw_profiles=raw_profiles or [],
245
256
  user_id=self.service_config.user_id,
246
257
  request_id=self.service_config.request_id,
258
+ source_interaction_ids=source_interaction_ids,
247
259
  )
248
260
  if raw_profiles:
249
261
  # Update operation state (bookmark) only when output was produced.
@@ -258,6 +270,7 @@ class ProfileExtractor:
258
270
  return ExtractionOutcome.completed(
259
271
  user_profiles,
260
272
  run_id=self._last_resumable_run_id,
273
+ token_totals=self._last_resumable_token_totals,
261
274
  )
262
275
  return user_profiles or None
263
276
 
@@ -266,6 +279,7 @@ class ProfileExtractor:
266
279
  raw_profiles: list[dict],
267
280
  user_id: str,
268
281
  request_id: str,
282
+ source_interaction_ids: list[int],
269
283
  ) -> list[UserProfile]:
270
284
  """
271
285
  Convert raw profile dicts from LLM to UserProfile objects.
@@ -274,6 +288,7 @@ class ProfileExtractor:
274
288
  raw_profiles: List of profile dicts with content, time_to_live, and optional metadata
275
289
  user_id: User ID
276
290
  request_id: Request ID
291
+ source_interaction_ids: Stored interaction ids used as the extraction window
277
292
 
278
293
  Returns:
279
294
  List of UserProfile objects
@@ -307,6 +322,7 @@ class ProfileExtractor:
307
322
  expiration_timestamp=calculate_expiration_timestamp(now_ts, ttl),
308
323
  custom_features=custom_features or None,
309
324
  extractor_names=None,
325
+ source_interaction_ids=source_interaction_ids,
310
326
  )
311
327
 
312
328
  new_profiles.append(added_profile)
@@ -342,11 +358,6 @@ class ProfileExtractor:
342
358
  self.config.context_prompt.strip() if self.config.context_prompt else ""
343
359
  ),
344
360
  extraction_definition_prompt=self.config.extraction_definition_prompt.strip(),
345
- metadata_definition_prompt=(
346
- self.config.metadata_definition_prompt.strip()
347
- if self.config.metadata_definition_prompt
348
- else None
349
- ),
350
361
  existing_profiles=existing_profiles,
351
362
  )
352
363
 
@@ -392,6 +403,7 @@ class ProfileExtractor:
392
403
  log_label="Profile extraction",
393
404
  )
394
405
  self._last_resumable_run_id = result.run_id
406
+ self._last_resumable_token_totals = sum_trace_tokens(result.trace)
395
407
  if not isinstance(result.output, StructuredProfilesOutput):
396
408
  logger.warning(
397
409
  "Profile extraction did not finish: %s", result.finished_reason
@@ -455,8 +467,4 @@ class ProfileExtractor:
455
467
  "time_to_live": "one_month",
456
468
  }
457
469
 
458
- # If metadata definition exists, add mock metadata
459
- if self.config.metadata_definition_prompt:
460
- mock_profile["metadata"] = "mock_metadata_value"
461
-
462
470
  return [mock_profile]
@@ -14,6 +14,7 @@ from reflexio.models.api_schema.service_schemas import (
14
14
  ProfileTimeToLive,
15
15
  UserProfile,
16
16
  )
17
+ from reflexio.models.structured_output import StrictStructuredOutput
17
18
  from reflexio.server.prompt.prompt_manager import PromptManager
18
19
  from reflexio.server.services.service_utils import (
19
20
  MessageConstructionConfig,
@@ -74,7 +75,6 @@ class ProfileAddItem(BaseModel):
74
75
  Attributes:
75
76
  content (str): The profile content based on content definition
76
77
  time_to_live (str): Time to live for the profile - one of: 'one_day', 'one_week', 'one_month', 'one_quarter', 'one_year', 'infinity'
77
- metadata (str, optional): Metadata extracted for the profile based on metadata definition
78
78
  """
79
79
 
80
80
  content: str = Field(description="Profile content based on content definition")
@@ -83,10 +83,6 @@ class ProfileAddItem(BaseModel):
83
83
  ] = Field(
84
84
  description="Time to live for the profile - determines when the profile expires"
85
85
  )
86
- metadata: str | None = Field(
87
- default=None,
88
- description="Metadata extracted for the profile based on metadata definition",
89
- )
90
86
  source_span: str | None = Field(
91
87
  default=None,
92
88
  description="Verbatim excerpt from the source that most directly supports this profile item",
@@ -107,7 +103,7 @@ class ProfileAddItem(BaseModel):
107
103
  )
108
104
 
109
105
 
110
- class ProfileUpdateOutput(BaseModel):
106
+ class ProfileUpdateOutput(StrictStructuredOutput):
111
107
  """
112
108
  Legacy output schema for profile_update_main prompt (kept for backward compatibility).
113
109
  Represents the complete set of profile updates including additions, deletions, and mentions.
@@ -120,7 +116,7 @@ class ProfileUpdateOutput(BaseModel):
120
116
 
121
117
  add: list[ProfileAddItem] | None = Field(
122
118
  default=None,
123
- description="List of new profiles to be added with their content, time to live, and optional metadata",
119
+ description="List of new profiles to be added with their content and time to live",
124
120
  )
125
121
  delete: list[str] | None = Field(
126
122
  default=None,
@@ -138,18 +134,18 @@ class ProfileUpdateOutput(BaseModel):
138
134
  )
139
135
 
140
136
 
141
- class StructuredProfilesOutput(BaseModel):
137
+ class StructuredProfilesOutput(StrictStructuredOutput):
142
138
  """
143
139
  Output schema for extraction-only profile extraction.
144
140
  Only extracts profiles — no delete/mention operations.
145
141
 
146
142
  Attributes:
147
- profiles (list[ProfileAddItem], optional): List of extracted profiles with content, time_to_live, and optional metadata
143
+ profiles (list[ProfileAddItem], optional): List of extracted profiles with content and time_to_live
148
144
  """
149
145
 
150
146
  profiles: list[ProfileAddItem] | None = Field(
151
147
  default=None,
152
- description="List of extracted profiles with content, time_to_live, and optional metadata",
148
+ description="List of extracted profiles with content and time_to_live",
153
149
  )
154
150
 
155
151
  model_config = ConfigDict(
@@ -225,7 +221,6 @@ def construct_profile_extraction_messages_from_sessions(
225
221
  agent_context_prompt: str,
226
222
  context_prompt: str,
227
223
  extraction_definition_prompt: str,
228
- metadata_definition_prompt: str | None = None,
229
224
  ) -> list[dict]:
230
225
  """
231
226
  Construct LLM messages for profile extraction from sessions.
@@ -241,7 +236,6 @@ def construct_profile_extraction_messages_from_sessions(
241
236
  agent_context_prompt: Context about the agent for system message
242
237
  context_prompt: Additional context for system message
243
238
  extraction_definition_prompt: Definition of what profiles should contain
244
- metadata_definition_prompt: Optional definition for profile metadata
245
239
 
246
240
  Returns:
247
241
  list[dict]: List of messages ready for profile extraction
@@ -259,7 +253,6 @@ def construct_profile_extraction_messages_from_sessions(
259
253
  "agent_context_prompt": agent_context_prompt,
260
254
  "context_prompt": context_prompt,
261
255
  "extraction_definition_prompt": extraction_definition_prompt,
262
- "metadata_definition_prompt": metadata_definition_prompt,
263
256
  },
264
257
  )
265
258
 
@@ -5,7 +5,6 @@ from __future__ import annotations
5
5
  import logging
6
6
  import uuid
7
7
  from dataclasses import dataclass
8
- from datetime import UTC, datetime
9
8
  from typing import TYPE_CHECKING, Any
10
9
 
11
10
  if TYPE_CHECKING:
@@ -14,11 +13,9 @@ if TYPE_CHECKING:
14
13
 
15
14
  from reflexio.models.api_schema.internal_schema import RequestInteractionDataModel
16
15
  from reflexio.models.api_schema.service_schemas import (
17
- DeleteUserProfileRequest,
18
16
  DowngradeProfilesResponse,
19
17
  ManualProfileGenerationRequest,
20
18
  ManualProfileGenerationResponse,
21
- ProfileChangeLog,
22
19
  RerunProfileGenerationRequest,
23
20
  RerunProfileGenerationResponse,
24
21
  Status,
@@ -30,7 +27,7 @@ from reflexio.server.services.base_generation_service import (
30
27
  BaseGenerationService,
31
28
  StatusChangeOperation,
32
29
  )
33
- from reflexio.server.services.profile.profile_extractor import ProfileExtractor
30
+ from reflexio.server.services.profile.components.extractor import ProfileExtractor
34
31
  from reflexio.server.services.profile.profile_generation_service_utils import (
35
32
  ProfileGenerationRequest,
36
33
  ProfileGenerationServiceConstants,
@@ -38,7 +35,7 @@ from reflexio.server.services.profile.profile_generation_service_utils import (
38
35
  from reflexio.server.services.service_utils import (
39
36
  format_sessions_to_history_string,
40
37
  )
41
- from reflexio.server.tracing import sentry_tags
38
+ from reflexio.server.tracing import capture_anomaly, sentry_tags
42
39
 
43
40
  logger = logging.getLogger(__name__)
44
41
 
@@ -81,6 +78,9 @@ class ProfileGenerationService(
81
78
  ):
82
79
  """Service to generate user profiles from interactions"""
83
80
 
81
+ # Profile generation produces learnings — opt in to ② Learning billing.
82
+ EMITS_LEARNING_BILLING: bool = True
83
+
84
84
  def __init__(
85
85
  self,
86
86
  llm_client: LiteLLMClient,
@@ -154,24 +154,23 @@ class ProfileGenerationService(
154
154
  request_id = self.service_config.request_id # type: ignore[reportOptionalMemberAccess]
155
155
 
156
156
  existing_ids_to_delete: list[str] = []
157
- superseded_profiles: list[UserProfile] = []
158
157
 
159
158
  # Always run deduplicator when enabled and there are new profiles
160
159
  if all_new_profiles:
161
160
  from reflexio.server.site_var.feature_flags import is_deduplicator_enabled
162
161
 
163
162
  if is_deduplicator_enabled(self.org_id):
164
- from reflexio.server.services.profile.profile_deduplicator import (
165
- ProfileDeduplicator,
163
+ from reflexio.server.services.profile.components.consolidator import (
164
+ ProfileConsolidator,
166
165
  )
167
166
 
168
- deduplicator = ProfileDeduplicator(
167
+ consolidator = ProfileConsolidator(
169
168
  request_context=self.request_context,
170
169
  llm_client=self.client,
171
170
  output_pending_status=self.output_pending_status,
172
171
  )
173
- all_new_profiles, existing_ids_to_delete, superseded_profiles = (
174
- deduplicator.deduplicate(all_new_profiles, user_id, request_id)
172
+ all_new_profiles, existing_ids_to_delete, _superseded_profiles = (
173
+ consolidator.deduplicate(all_new_profiles, user_id, request_id)
175
174
  )
176
175
  logger.info(
177
176
  "Profile updates after deduplication: %d profiles, %d existing to delete",
@@ -203,59 +202,42 @@ class ProfileGenerationService(
203
202
  )
204
203
  return
205
204
 
206
- # Delete superseded existing profiles
205
+ # Always soft-supersede superseded existing profiles (never hard-delete on
206
+ # the dedup path). This emits the lineage events that the profile change log
207
+ # is reconstructed from (the legacy `profile_change_logs` table is no longer
208
+ # written — see reconstruct_profile_change_log).
207
209
  if existing_ids_to_delete:
208
- for profile_id in existing_ids_to_delete:
210
+ if not request_id:
211
+ # An empty request_id makes the removal unreconstructable (the lineage
212
+ # events are keyed on it). Fail loud and skip removal entirely — never
213
+ # silently hard-delete.
214
+ capture_anomaly(
215
+ "lineage.dedup.missing_request_id",
216
+ level="error",
217
+ org_id=self.org_id,
218
+ user_id=user_id,
219
+ )
220
+ else:
209
221
  try:
210
- self.storage.delete_user_profile( # type: ignore[reportOptionalMemberAccess]
211
- DeleteUserProfileRequest(
212
- user_id=user_id,
213
- profile_id=profile_id,
214
- )
222
+ self.storage.supersede_profiles_by_ids( # type: ignore[reportOptionalMemberAccess]
223
+ user_id=user_id,
224
+ profile_ids=existing_ids_to_delete,
225
+ request_id=request_id,
215
226
  )
216
- except Exception as e: # noqa: PERF203
227
+ except Exception as e:
217
228
  with sentry_tags(
218
229
  subsystem="profile_generation",
219
- op="delete_superseded_profile",
230
+ op="supersede_profiles",
220
231
  org_id=self.org_id,
221
232
  user_id=user_id,
222
233
  request_id=request_id,
223
- profile_id=profile_id,
224
234
  error_type=type(e).__name__,
225
235
  ):
226
236
  logger.exception(
227
- "Failed to delete superseded profile %s for user %s",
228
- profile_id,
237
+ "Failed to soft-delete superseded profiles for user %s",
229
238
  user_id,
230
239
  )
231
240
 
232
- # Create profile changelog post-deduplication
233
- if all_new_profiles or superseded_profiles:
234
- try:
235
- profile_change_log = ProfileChangeLog(
236
- id=0, # Auto-generated by storage
237
- user_id=user_id,
238
- request_id=request_id,
239
- created_at=int(datetime.now(UTC).timestamp()),
240
- added_profiles=all_new_profiles,
241
- removed_profiles=superseded_profiles,
242
- mentioned_profiles=[],
243
- )
244
- self.storage.add_profile_change_log(profile_change_log) # type: ignore[reportOptionalMemberAccess]
245
- except Exception as e:
246
- with sentry_tags(
247
- subsystem="profile_generation",
248
- op="add_profile_change_log",
249
- org_id=self.org_id,
250
- user_id=user_id,
251
- request_id=request_id,
252
- error_type=type(e).__name__,
253
- ):
254
- logger.exception(
255
- "Failed to add profile change log for user %s",
256
- user_id,
257
- )
258
-
259
241
  def check_and_update_profiles(self, profiles: list[UserProfile]) -> None:
260
242
  """check if the profiles are expired and update them if they are"""
261
243
  raise NotImplementedError
@@ -1,17 +1,13 @@
1
- """Reflection service: critique-and-revise of cited memories after publish."""
1
+ """Reflection service package."""
2
2
 
3
- from reflexio.server.services.reflection.reflection_service import ReflectionService
4
3
  from reflexio.server.services.reflection.reflection_service_utils import (
5
- ReflectionDecision,
6
- ReflectionOutput,
7
- ReflectionResult,
4
+ REFLECTION_OPERATION_NAME,
8
5
  ReflectionServiceRequest,
9
6
  )
7
+ from reflexio.server.services.reflection.service import ReflectionService
10
8
 
11
9
  __all__ = [
10
+ "REFLECTION_OPERATION_NAME",
12
11
  "ReflectionService",
13
12
  "ReflectionServiceRequest",
14
- "ReflectionDecision",
15
- "ReflectionOutput",
16
- "ReflectionResult",
17
13
  ]
@@ -0,0 +1,7 @@
1
+ """Reflection service internal components."""
2
+
3
+ from reflexio.server.services.reflection.components.extractor import (
4
+ ReflectionExtractor,
5
+ )
6
+
7
+ __all__ = ["ReflectionExtractor"]
@@ -11,11 +11,14 @@ in light of how they were applied across the window.
11
11
 
12
12
  from __future__ import annotations
13
13
 
14
+ import uuid
14
15
  from typing import Literal
15
16
 
16
17
  from pydantic import BaseModel, Field
17
18
 
18
19
  from reflexio.models.api_schema.domain.enums import ProfileTimeToLive
20
+ from reflexio.models.api_schema.validators import NonEmptyStr
21
+ from reflexio.models.structured_output import StrictStructuredOutput
19
22
 
20
23
  REFLECTION_OPERATION_NAME = "reflection"
21
24
 
@@ -29,6 +32,13 @@ class ReflectionServiceRequest(BaseModel):
29
32
 
30
33
  Args:
31
34
  user_id (str): User to scope the bookmark and window to.
35
+ request_id (NonEmptyStr): The publish pass's own request id; used as the
36
+ lineage event ``request_id`` on revise events so B3
37
+ reconstruction can link revisions back to the triggering pass.
38
+ Defaults to a fresh UUID hex so two passes on the same profile
39
+ with no explicit request_id produce distinct lineage events.
40
+ Empty strings and whitespace-only values are rejected at
41
+ construction (``ValidationError``), before any storage write.
32
42
  agent_version (str): Agent version of the current publish; copied
33
43
  into replacement playbooks.
34
44
  source (str | None): Optional source filter for the window.
@@ -37,6 +47,7 @@ class ReflectionServiceRequest(BaseModel):
37
47
  """
38
48
 
39
49
  user_id: str
50
+ request_id: NonEmptyStr = Field(default_factory=lambda: uuid.uuid4().hex)
40
51
  agent_version: str = ""
41
52
  source: str | None = None
42
53
 
@@ -88,7 +99,7 @@ class ReflectionDecision(BaseModel):
88
99
  reason: str = ""
89
100
 
90
101
 
91
- class ReflectionOutput(BaseModel):
102
+ class ReflectionOutput(StrictStructuredOutput):
92
103
  """Structured LLM output for one reflection pass."""
93
104
 
94
105
  decisions: list[ReflectionDecision] = Field(default_factory=list)
@@ -34,12 +34,14 @@ from typing import TYPE_CHECKING
34
34
  from reflexio.models.api_schema.domain.entities import (
35
35
  Citation,
36
36
  Interaction,
37
+ LineageContext,
37
38
  UserPlaybook,
38
39
  UserProfile,
39
40
  )
40
41
  from reflexio.server.llm.litellm_client import LiteLLMClient
41
42
  from reflexio.server.services.operation_state_utils import OperationStateManager
42
- from reflexio.server.services.reflection.reflection_extractor import (
43
+ from reflexio.server.services.playbook.playbook_edit_apply import apply_playbook_edit
44
+ from reflexio.server.services.reflection.components.extractor import (
43
45
  ReflectionExtractor,
44
46
  )
45
47
  from reflexio.server.services.reflection.reflection_service_utils import (
@@ -446,15 +448,19 @@ class ReflectionService:
446
448
  decision: ReflectionDecision,
447
449
  cited: UserProfile,
448
450
  ) -> bool:
449
- """Insert the replacement profile, then archive the cited row.
451
+ """Insert the replacement profile, then supersede the cited row.
450
452
 
451
453
  Insert-first ordering means that if ``add_user_profile`` raises,
452
454
  the cited row stays current and the per-decision exception
453
455
  handler reports ``failed_count``. Only after the new row is
454
- durable do we flip the cited row to ARCHIVED — and if *that*
455
- fails we log at ERROR rather than silently dropping the user's
456
- data, leaving a transient duplicate that downstream dedup can
457
- clean up.
456
+ durable do we atomically supersede the cited row via
457
+ ``supersede_record``, which sets ``status=SUPERSEDED`` with a
458
+ ``superseded_by`` pointer and appends a ``revise`` lineage event.
459
+ If the CAS guard fails (incumbent no longer CURRENT), the
460
+ just-inserted successor is deleted (not audited); if that delete
461
+ itself raises the exception propagates to the caller. If
462
+ ``supersede_record`` raises, log at ERROR and accept a transient
463
+ duplicate rather than silently dropping user data.
458
464
  """
459
465
  storage = self.request_context.storage
460
466
  if storage is None:
@@ -481,14 +487,22 @@ class ReflectionService:
481
487
  new_content=new_profile.content,
482
488
  )
483
489
  storage.add_user_profile(cited.user_id, [new_profile])
490
+ ctx = LineageContext(
491
+ op_kind="revise",
492
+ actor="reflection",
493
+ request_id=request.request_id,
494
+ )
484
495
  try:
485
- archived = storage.archive_profile_by_id(
486
- user_id=request.user_id, profile_id=cited.profile_id
496
+ superseded = storage.supersede_record(
497
+ entity_type="profile",
498
+ incumbent_id=str(cited.profile_id),
499
+ successor_id=str(new_profile.profile_id),
500
+ context=ctx,
487
501
  )
488
502
  except Exception as exc: # noqa: BLE001
489
503
  with sentry_tags(
490
504
  subsystem="reflection",
491
- op="archive_after_insert",
505
+ op="supersede_after_insert",
492
506
  kind="profile",
493
507
  org_id=self.request_context.org_id,
494
508
  user_id=cited.user_id,
@@ -503,7 +517,12 @@ class ReflectionService:
503
517
  new_profile.profile_id,
504
518
  )
505
519
  return True
506
- if not archived:
520
+ if not superseded:
521
+ # lost the CAS race: incumbent was no longer CURRENT — drop the
522
+ # just-added successor (never live) without emitting an audit event.
523
+ storage.delete_profiles_by_ids(
524
+ [new_profile.profile_id], emit_hard_delete=False
525
+ )
507
526
  with sentry_tags(
508
527
  subsystem="reflection",
509
528
  op="archive_after_insert_noop",
@@ -579,11 +598,13 @@ class ReflectionService:
579
598
  (cited.content or "")[:120].replace('"', "'"),
580
599
  (decision.new_rationale or "")[:120].replace('"', "'"),
581
600
  )
582
- storage.save_user_playbooks([new_playbook])
583
601
  try:
584
- archived = storage.archive_user_playbook_by_id(
585
- user_id=owning_user_id,
586
- user_playbook_id=cited.user_playbook_id,
602
+ archived = apply_playbook_edit(
603
+ storage,
604
+ incumbent_id=cited.user_playbook_id,
605
+ new_playbook=new_playbook,
606
+ source=new_playbook.source or "reflection",
607
+ request_id=request.request_id,
587
608
  )
588
609
  except Exception as exc: # noqa: BLE001
589
610
  with sentry_tags(
@@ -603,7 +624,7 @@ class ReflectionService:
603
624
  new_playbook.user_playbook_id,
604
625
  )
605
626
  return True
606
- if not archived:
627
+ if archived < 0:
607
628
  with sentry_tags(
608
629
  subsystem="reflection",
609
630
  op="archive_after_insert_noop",
@@ -799,5 +820,4 @@ def _filter_citations_by_horizon(
799
820
  citation=cite, position=position, has_full_horizon=False
800
821
  )
801
822
  )
802
- # else: deferred — not included
803
823
  return out