claude-smart 0.2.45 → 0.2.47

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 (353) hide show
  1. package/.claude-plugin/marketplace.json +3 -3
  2. package/README.md +47 -10
  3. package/bin/claude-smart.js +535 -23
  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 +27 -6
  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/layout.tsx +20 -0
  16. package/plugin/dashboard/app/preferences/[id]/page.tsx +8 -8
  17. package/plugin/dashboard/app/skills/project/[id]/page.tsx +7 -3
  18. package/plugin/dashboard/app/skills/shared/[id]/page.tsx +10 -3
  19. package/plugin/dashboard/lib/claude-settings-file.ts +20 -10
  20. package/plugin/dashboard/lib/reflexio-client.ts +16 -0
  21. package/plugin/dashboard/lib/status.ts +10 -3
  22. package/plugin/dashboard/lib/types.ts +18 -6
  23. package/plugin/hooks/codex-hooks.json +0 -1
  24. package/plugin/opencode/assistant-buffer.ts +108 -0
  25. package/plugin/opencode/dist/assistant-buffer.js +96 -0
  26. package/plugin/opencode/dist/internal.js +12 -0
  27. package/plugin/opencode/dist/payload.js +85 -0
  28. package/plugin/opencode/dist/server.mjs +232 -0
  29. package/plugin/opencode/internal.ts +18 -0
  30. package/plugin/opencode/package.json +3 -0
  31. package/plugin/opencode/payload.ts +90 -0
  32. package/plugin/opencode/server.mts +251 -0
  33. package/plugin/opencode/tsconfig.json +13 -0
  34. package/plugin/pyproject.toml +25 -4
  35. package/plugin/scripts/_lib.sh +5 -1
  36. package/plugin/scripts/backend-service.sh +22 -2
  37. package/plugin/scripts/ensure-plugin-root.sh +4 -4
  38. package/plugin/scripts/hook_entry.sh +1 -1
  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 +17 -9
  43. package/plugin/src/README.md +1 -1
  44. package/plugin/src/claude_smart/cli.py +498 -12
  45. package/plugin/src/claude_smart/context_format.py +9 -9
  46. package/plugin/src/claude_smart/cs_cite.py +66 -19
  47. package/plugin/src/claude_smart/env_config.py +12 -4
  48. package/plugin/src/claude_smart/events/session_start.py +26 -7
  49. package/plugin/src/claude_smart/events/stop.py +1 -1
  50. package/plugin/src/claude_smart/ids.py +1 -1
  51. package/plugin/src/claude_smart/reflexio_adapter.py +5 -5
  52. package/plugin/src/claude_smart/runtime.py +7 -1
  53. package/plugin/uv.lock +5 -5
  54. package/plugin/vendor/reflexio/.env.example +54 -30
  55. package/plugin/vendor/reflexio/README.md +17 -11
  56. package/plugin/vendor/reflexio/pyproject.toml +13 -1
  57. package/plugin/vendor/reflexio/reflexio/README.md +4 -1
  58. package/plugin/vendor/reflexio/reflexio/cli/README.md +9 -7
  59. package/plugin/vendor/reflexio/reflexio/cli/__main__.py +10 -1
  60. package/plugin/vendor/reflexio/reflexio/cli/bootstrap_config.py +22 -5
  61. package/plugin/vendor/reflexio/reflexio/cli/commands/interactions.py +34 -4
  62. package/plugin/vendor/reflexio/reflexio/cli/commands/profiles.py +12 -7
  63. package/plugin/vendor/reflexio/reflexio/cli/commands/services.py +13 -6
  64. package/plugin/vendor/reflexio/reflexio/cli/commands/setup_cmd.py +212 -52
  65. package/plugin/vendor/reflexio/reflexio/cli/commands/shortcuts.py +15 -2
  66. package/plugin/vendor/reflexio/reflexio/cli/env_loader.py +292 -22
  67. package/plugin/vendor/reflexio/reflexio/cli/log_format.py +29 -4
  68. package/plugin/vendor/reflexio/reflexio/cli/run_services.py +17 -8
  69. package/plugin/vendor/reflexio/reflexio/cli/stop_services.py +13 -10
  70. package/plugin/vendor/reflexio/reflexio/cli/utils.py +44 -1
  71. package/plugin/vendor/reflexio/reflexio/client/client.py +154 -13
  72. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/README.md +135 -257
  73. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/npm/openclaw-smart/bin/openclaw-smart.js +13 -0
  74. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/npm/openclaw-smart/package.json +15 -0
  75. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/README.md +38 -0
  76. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/index.ts +151 -110
  77. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/openclaw.plugin.json +20 -10
  78. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/package.json +28 -6
  79. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/pyproject.toml +42 -0
  80. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/_lib.sh +371 -0
  81. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/backend-log-runner.sh +33 -0
  82. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/backend-service.sh +271 -0
  83. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/cli.sh +68 -0
  84. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/dashboard-open.sh +15 -0
  85. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/ensure-plugin-root.sh +84 -0
  86. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/hook_entry.sh +106 -0
  87. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/npm-cli.js +219 -0
  88. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/smart-install.sh +269 -0
  89. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/clear-all/SKILL.md +8 -0
  90. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/dashboard/SKILL.md +8 -0
  91. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/learn/SKILL.md +10 -0
  92. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/reflexio/SKILL.md +15 -44
  93. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/restart/SKILL.md +6 -0
  94. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/show/SKILL.md +8 -0
  95. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/cli.py +634 -0
  96. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/context_format.py +224 -0
  97. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/context_inject.py +79 -0
  98. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/__init__.py +0 -0
  99. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/after_tool_call.py +160 -0
  100. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/agent_end.py +187 -0
  101. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/before_prompt_build.py +69 -0
  102. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/before_tool_call.py +30 -0
  103. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/session_end.py +36 -0
  104. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/session_start.py +130 -0
  105. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/hook.py +131 -0
  106. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/ids.py +94 -0
  107. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/internal_call.py +75 -0
  108. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/oc_cite.py +196 -0
  109. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/optimizer_assistant.py +272 -0
  110. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/publish.py +96 -0
  111. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/query_compose.py +66 -0
  112. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/reflexio_adapter.py +336 -0
  113. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/runtime.py +47 -0
  114. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/stall_banner.py +61 -0
  115. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/state.py +323 -0
  116. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/__init__.py +0 -0
  117. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/integration/__init__.py +0 -0
  118. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/integration/test_e2e_session_loop.py +190 -0
  119. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/integration/test_publish_to_local_reflexio_integration.py +112 -0
  120. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/integration/test_recursion_guard_integration.py +86 -0
  121. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/integration/test_search_inject_integration.py +144 -0
  122. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_cli.py +184 -0
  123. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_events_after_tool_call.py +142 -0
  124. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_events_agent_end.py +233 -0
  125. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_events_before_prompt_build.py +116 -0
  126. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_events_before_tool_call.py +35 -0
  127. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_events_session_end.py +47 -0
  128. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_events_session_start.py +109 -0
  129. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_hook_dispatch.py +117 -0
  130. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_ids.py +41 -0
  131. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_internal_call.py +50 -0
  132. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_oc_cite.py +88 -0
  133. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_publish.py +48 -0
  134. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_query_compose.py +48 -0
  135. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_reflexio_adapter.py +188 -0
  136. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_runtime.py +41 -0
  137. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_state.py +235 -0
  138. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests-ts/__mocks__/plugin-entry-stub.ts +6 -0
  139. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests-ts/test_npm_cli.test.ts +56 -0
  140. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests-ts/test_shim_dispatch.test.ts +171 -0
  141. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tsconfig.build.json +18 -0
  142. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/{tsconfig.json → plugin/tsconfig.json} +2 -2
  143. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/uv.lock +3835 -0
  144. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/vitest.config.ts +18 -0
  145. package/plugin/vendor/reflexio/reflexio/lib/_agent_playbook.py +246 -10
  146. package/plugin/vendor/reflexio/reflexio/lib/_base.py +15 -0
  147. package/plugin/vendor/reflexio/reflexio/lib/_config.py +11 -4
  148. package/plugin/vendor/reflexio/reflexio/lib/_generation.py +17 -5
  149. package/plugin/vendor/reflexio/reflexio/lib/_lineage_parity_readers.py +187 -0
  150. package/plugin/vendor/reflexio/reflexio/lib/_profiles.py +224 -21
  151. package/plugin/vendor/reflexio/reflexio/lib/_reflection.py +1 -1
  152. package/plugin/vendor/reflexio/reflexio/lib/_search.py +52 -25
  153. package/plugin/vendor/reflexio/reflexio/lib/_user_playbook.py +11 -1
  154. package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/__init__.py +1 -0
  155. package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/entities.py +127 -38
  156. package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/enums.py +4 -0
  157. package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/governance.py +117 -0
  158. package/plugin/vendor/reflexio/reflexio/models/api_schema/eval_overview_schema.py +64 -41
  159. package/plugin/vendor/reflexio/reflexio/models/api_schema/internal_schema.py +19 -0
  160. package/plugin/vendor/reflexio/reflexio/models/api_schema/pending_tool_call_schema.py +12 -5
  161. package/plugin/vendor/reflexio/reflexio/models/api_schema/retriever_schema.py +97 -3
  162. package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/converters.py +5 -1
  163. package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/entities.py +5 -1
  164. package/plugin/vendor/reflexio/reflexio/models/config_schema.py +104 -7
  165. package/plugin/vendor/reflexio/reflexio/models/structured_output.py +148 -0
  166. package/plugin/vendor/reflexio/reflexio/server/OVERVIEW.md +3 -3
  167. package/plugin/vendor/reflexio/reflexio/server/README.md +58 -33
  168. package/plugin/vendor/reflexio/reflexio/server/__init__.py +29 -5
  169. package/plugin/vendor/reflexio/reflexio/server/__main__.py +2 -2
  170. package/plugin/vendor/reflexio/reflexio/server/api.py +899 -103
  171. package/plugin/vendor/reflexio/reflexio/server/api_endpoints/precondition_checks.py +7 -0
  172. package/plugin/vendor/reflexio/reflexio/server/api_endpoints/request_context.py +4 -2
  173. package/plugin/vendor/reflexio/reflexio/server/auth.py +90 -0
  174. package/plugin/vendor/reflexio/reflexio/server/billing_meter.py +173 -0
  175. package/plugin/vendor/reflexio/reflexio/server/billing_signals.py +64 -0
  176. package/plugin/vendor/reflexio/reflexio/server/cache/reflexio_cache.py +46 -16
  177. package/plugin/vendor/reflexio/reflexio/server/env_utils.py +65 -0
  178. package/plugin/vendor/reflexio/reflexio/server/extensions.py +213 -0
  179. package/plugin/vendor/reflexio/reflexio/server/llm/embedding_service.py +19 -4
  180. package/plugin/vendor/reflexio/reflexio/server/llm/litellm_client.py +264 -77
  181. package/plugin/vendor/reflexio/reflexio/server/llm/llm_utils.py +69 -7
  182. package/plugin/vendor/reflexio/reflexio/server/llm/model_defaults.py +4 -4
  183. package/plugin/vendor/reflexio/reflexio/server/llm/providers/claude_code_provider.py +1 -1
  184. package/plugin/vendor/reflexio/reflexio/server/llm/providers/embedding_service_provider.py +75 -16
  185. package/plugin/vendor/reflexio/reflexio/server/llm/providers/openclaw_provider.py +280 -0
  186. package/plugin/vendor/reflexio/reflexio/server/llm/rerank/cross_encoder_reranker.py +12 -1
  187. package/plugin/vendor/reflexio/reflexio/server/llm/token_accounting.py +48 -0
  188. package/plugin/vendor/reflexio/reflexio/server/llm/tools.py +107 -17
  189. package/plugin/vendor/reflexio/reflexio/server/operation_limiter.py +360 -4
  190. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.6.0.prompt.md +1 -1
  191. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.7.0.prompt.md +85 -0
  192. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_aggregation/v2.2.0.prompt.md +1 -1
  193. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_aggregation/v2.3.0.prompt.md +253 -0
  194. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.3.2.prompt.md +1 -1
  195. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.3.3.prompt.md +74 -0
  196. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.2.3.prompt.md +1 -1
  197. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.3.0.prompt.md +266 -0
  198. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.4.0.prompt.md +278 -0
  199. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v3.3.0.prompt.md +1 -1
  200. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v3.4.0.prompt.md +137 -0
  201. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main/v1.2.0.prompt.md +1 -1
  202. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main/v1.3.0.prompt.md +34 -0
  203. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_update_instruction_start/v1.0.0.prompt.md +5 -12
  204. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_update_instruction_start/v1.1.0.prompt.md +5 -12
  205. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_update_instruction_start/v1.2.0.prompt.md +155 -0
  206. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/tagging/v1.0.0.prompt.md +23 -0
  207. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_manager.py +89 -32
  208. package/plugin/vendor/reflexio/reflexio/server/services/README.md +28 -11
  209. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/README.md +22 -0
  210. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/__init__.py +1 -0
  211. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/agent_success_evaluation_constants.py +4 -2
  212. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/agent_success_evaluation_utils.py +1 -0
  213. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/components/__init__.py +7 -0
  214. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/{agent_success_evaluator.py → components/evaluator.py} +3 -16
  215. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/regen_jobs.py +28 -72
  216. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/{group_evaluation_runner.py → runner.py} +12 -10
  217. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/{delayed_group_evaluator.py → scheduler.py} +10 -2
  218. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/{agent_success_evaluation_service.py → service.py} +36 -1
  219. package/plugin/vendor/reflexio/reflexio/server/services/base_generation_service.py +160 -0
  220. package/plugin/vendor/reflexio/reflexio/server/services/configurator/base_configurator.py +5 -0
  221. package/plugin/vendor/reflexio/reflexio/server/services/deduplication_utils.py +1 -1
  222. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/README.md +9 -0
  223. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/components/__init__.py +0 -0
  224. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/{rule_attribution.py → components/rule_attribution.py} +10 -9
  225. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/eval_sampler.py +15 -24
  226. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/service.py +238 -164
  227. package/plugin/vendor/reflexio/reflexio/server/services/extraction/README.md +31 -0
  228. package/plugin/vendor/reflexio/reflexio/server/services/extraction/outcome.py +13 -3
  229. package/plugin/vendor/reflexio/reflexio/server/services/extraction/pending_tool_call_dispatch.py +3 -2
  230. package/plugin/vendor/reflexio/reflexio/server/services/extraction/resumable_agent.py +32 -118
  231. package/plugin/vendor/reflexio/reflexio/server/services/extraction/resume_worker.py +47 -16
  232. package/plugin/vendor/reflexio/reflexio/server/services/generation_service.py +105 -23
  233. package/plugin/vendor/reflexio/reflexio/server/services/governance/config.py +52 -0
  234. package/plugin/vendor/reflexio/reflexio/server/services/governance/service.py +378 -0
  235. package/plugin/vendor/reflexio/reflexio/server/services/governance/subject_refs.py +34 -0
  236. package/plugin/vendor/reflexio/reflexio/server/services/lineage/__init__.py +0 -0
  237. package/plugin/vendor/reflexio/reflexio/server/services/lineage/gc_scheduler.py +219 -0
  238. package/plugin/vendor/reflexio/reflexio/server/services/lineage/resolve.py +98 -0
  239. package/plugin/vendor/reflexio/reflexio/server/services/operation_state_utils.py +26 -0
  240. package/plugin/vendor/reflexio/reflexio/server/services/playbook/README.md +18 -11
  241. package/plugin/vendor/reflexio/reflexio/server/services/playbook/aggregation_prompt_processing.py +100 -0
  242. package/plugin/vendor/reflexio/reflexio/server/services/playbook/components/__init__.py +5 -0
  243. package/plugin/vendor/reflexio/reflexio/server/services/playbook/{playbook_aggregator.py → components/aggregator.py} +334 -178
  244. package/plugin/vendor/reflexio/reflexio/server/services/playbook/{playbook_consolidator.py → components/consolidator.py} +168 -62
  245. package/plugin/vendor/reflexio/reflexio/server/services/playbook/{playbook_extractor.py → components/extractor.py} +5 -1
  246. package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_edit_apply.py +74 -0
  247. package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_service_utils.py +33 -3
  248. package/plugin/vendor/reflexio/reflexio/server/services/playbook/{playbook_generation_service.py → service.py} +121 -19
  249. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/README.md +10 -0
  250. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/models.py +2 -1
  251. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/optimizer.py +178 -30
  252. package/plugin/vendor/reflexio/reflexio/server/services/pre_retrieval/README.md +9 -0
  253. package/plugin/vendor/reflexio/reflexio/server/services/profile/components/__init__.py +17 -0
  254. package/plugin/vendor/reflexio/reflexio/server/services/profile/{profile_deduplicator.py → components/consolidator.py} +7 -6
  255. package/plugin/vendor/reflexio/reflexio/server/services/profile/{profile_extractor.py → components/extractor.py} +31 -22
  256. package/plugin/vendor/reflexio/reflexio/server/services/profile/profile_generation_service_utils.py +6 -13
  257. package/plugin/vendor/reflexio/reflexio/server/services/profile/{profile_generation_service.py → service.py} +32 -50
  258. package/plugin/vendor/reflexio/reflexio/server/services/reflection/__init__.py +4 -8
  259. package/plugin/vendor/reflexio/reflexio/server/services/reflection/components/__init__.py +7 -0
  260. package/plugin/vendor/reflexio/reflexio/server/services/reflection/reflection_service_utils.py +12 -1
  261. package/plugin/vendor/reflexio/reflexio/server/services/reflection/{reflection_service.py → service.py} +36 -16
  262. package/plugin/vendor/reflexio/reflexio/server/services/retrieval/recency.py +211 -0
  263. package/plugin/vendor/reflexio/reflexio/server/services/retrieval/relevance_floor.py +99 -6
  264. package/plugin/vendor/reflexio/reflexio/server/services/service_utils.py +36 -1
  265. package/plugin/vendor/reflexio/reflexio/server/services/shadow_comparison/README.md +8 -0
  266. package/plugin/vendor/reflexio/reflexio/server/services/shadow_comparison/outcome.py +1 -2
  267. package/plugin/vendor/reflexio/reflexio/server/services/storage/error.py +31 -0
  268. package/plugin/vendor/reflexio/reflexio/server/services/storage/governance_validation.py +681 -0
  269. package/plugin/vendor/reflexio/reflexio/server/services/storage/retention.py +9 -9
  270. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/__init__.py +16 -2
  271. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_agent_run.py +4 -1
  272. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_base.py +644 -197
  273. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_extras.py +106 -119
  274. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_governance.py +1965 -0
  275. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_lineage.py +630 -0
  276. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_operations.py +38 -6
  277. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_playbook.py +31 -1369
  278. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_profiles.py +756 -210
  279. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_requests.py +148 -34
  280. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_shadow_verdicts.py +23 -9
  281. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_stall_state.py +5 -2
  282. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/__init__.py +13 -0
  283. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_agent.py +952 -0
  284. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_eval_results.py +189 -0
  285. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_optimization.py +247 -0
  286. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_source_linkage.py +145 -0
  287. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_user.py +838 -0
  288. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/__init__.py +150 -23
  289. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_agent_run.py +2 -8
  290. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_base.py +5 -0
  291. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_extras.py +40 -57
  292. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_governance.py +148 -0
  293. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_lineage.py +215 -0
  294. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_operations.py +20 -0
  295. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_playbook.py +4 -677
  296. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_profiles.py +134 -1
  297. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_requests.py +62 -0
  298. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_retrieval_log.py +51 -0
  299. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_shadow_verdicts.py +17 -0
  300. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/__init__.py +13 -0
  301. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_agent.py +365 -0
  302. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_eval_results.py +124 -0
  303. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_optimization.py +85 -0
  304. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_source_linkage.py +47 -0
  305. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_user.py +333 -0
  306. package/plugin/vendor/reflexio/reflexio/server/services/tagging/README.md +8 -0
  307. package/plugin/vendor/reflexio/reflexio/server/services/tagging/__init__.py +1 -0
  308. package/plugin/vendor/reflexio/reflexio/server/services/tagging/service.py +200 -0
  309. package/plugin/vendor/reflexio/reflexio/server/services/tagging/tagging_scheduler.py +149 -0
  310. package/plugin/vendor/reflexio/reflexio/server/services/unified_search_service.py +372 -34
  311. package/plugin/vendor/reflexio/reflexio/server/site_var/README.md +2 -1
  312. package/plugin/vendor/reflexio/reflexio/server/site_var/feature_flags.py +120 -1
  313. package/plugin/vendor/reflexio/reflexio/server/site_var/site_var_sources/feature_flags.json +4 -0
  314. package/plugin/vendor/reflexio/reflexio/server/site_var/site_var_sources/search_settings.json +5 -0
  315. package/plugin/vendor/reflexio/reflexio/server/tracing.py +30 -0
  316. package/plugin/vendor/reflexio/reflexio/server/usage_metrics.py +18 -0
  317. package/plugin/vendor/reflexio/reflexio/test_support/llm_mock.py +61 -26
  318. package/plugin/vendor/reflexio/reflexio/test_support/llm_model_registry.py +41 -4
  319. package/scripts/setup-claude-smart.sh +8 -3
  320. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/TESTING.md +0 -517
  321. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/hook/handler.js +0 -473
  322. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/package-lock.json +0 -2156
  323. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/package.json +0 -18
  324. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/hook/handler.ts +0 -241
  325. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/hook/setup.ts +0 -140
  326. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/publish.ts +0 -113
  327. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/search.ts +0 -52
  328. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/server.ts +0 -103
  329. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/sqlite-buffer.ts +0 -156
  330. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/user-id.ts +0 -134
  331. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/rules/reflexio.md +0 -24
  332. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/publish_clawhub.sh +0 -278
  333. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/references/HOOK.md +0 -164
  334. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/scripts/install.sh +0 -36
  335. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/scripts/uninstall.sh +0 -35
  336. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/publish.test.ts +0 -27
  337. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/search.test.ts +0 -31
  338. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/server.test.ts +0 -42
  339. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/setup.test.ts +0 -49
  340. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/sqlite-buffer.test.ts +0 -91
  341. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/user-id.test.ts +0 -50
  342. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/vitest.config.ts +0 -13
  343. package/plugin/vendor/reflexio/reflexio/server/_auth.py +0 -25
  344. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/group_aggregation.py +0 -192
  345. package/plugin/vendor/reflexio/reflexio/server/services/extraction/invariants.py +0 -303
  346. package/plugin/vendor/reflexio/reflexio/server/services/extraction/plan.py +0 -138
  347. package/plugin/vendor/reflexio/reflexio/server/services/extraction/tools.py +0 -1125
  348. /package/plugin/vendor/reflexio/reflexio/integrations/{__init__.py → openclaw/plugin/src/openclaw_smart/__init__.py} +0 -0
  349. /package/plugin/vendor/reflexio/reflexio/integrations/openclaw/{types → plugin/types}/openclaw.d.ts +0 -0
  350. /package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/{distribution.py → components/distribution.py} +0 -0
  351. /package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/{hero_state.py → components/hero_state.py} +0 -0
  352. /package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/{shadow_aggregation.py → components/shadow_aggregation.py} +0 -0
  353. /package/plugin/vendor/reflexio/reflexio/server/services/reflection/{reflection_extractor.py → components/extractor.py} +0 -0
@@ -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] | None = None,
269
283
  ) -> list[UserProfile]:
270
284
  """
271
285
  Convert raw profile dicts from LLM to UserProfile objects.
@@ -274,11 +288,13 @@ 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
280
295
  """
281
296
  new_profiles = []
297
+ profile_source_interaction_ids = list(source_interaction_ids or [])
282
298
  for profile_content in raw_profiles:
283
299
  if (
284
300
  not isinstance(profile_content, dict)
@@ -307,6 +323,7 @@ class ProfileExtractor:
307
323
  expiration_timestamp=calculate_expiration_timestamp(now_ts, ttl),
308
324
  custom_features=custom_features or None,
309
325
  extractor_names=None,
326
+ source_interaction_ids=profile_source_interaction_ids,
310
327
  )
311
328
 
312
329
  new_profiles.append(added_profile)
@@ -342,11 +359,6 @@ class ProfileExtractor:
342
359
  self.config.context_prompt.strip() if self.config.context_prompt else ""
343
360
  ),
344
361
  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
362
  existing_profiles=existing_profiles,
351
363
  )
352
364
 
@@ -392,6 +404,7 @@ class ProfileExtractor:
392
404
  log_label="Profile extraction",
393
405
  )
394
406
  self._last_resumable_run_id = result.run_id
407
+ self._last_resumable_token_totals = sum_trace_tokens(result.trace)
395
408
  if not isinstance(result.output, StructuredProfilesOutput):
396
409
  logger.warning(
397
410
  "Profile extraction did not finish: %s", result.finished_reason
@@ -455,8 +468,4 @@ class ProfileExtractor:
455
468
  "time_to_live": "one_month",
456
469
  }
457
470
 
458
- # If metadata definition exists, add mock metadata
459
- if self.config.metadata_definition_prompt:
460
- mock_profile["metadata"] = "mock_metadata_value"
461
-
462
471
  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