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
@@ -0,0 +1,333 @@
1
+ """Abstract user playbook CRUD + search declarations."""
2
+
3
+ from abc import abstractmethod
4
+
5
+ from reflexio.models.api_schema.common import BlockingIssue
6
+ from reflexio.models.api_schema.domain import Status, UserPlaybook
7
+ from reflexio.models.api_schema.retriever_schema import SearchUserPlaybookRequest
8
+ from reflexio.models.config_schema import SearchOptions
9
+
10
+
11
+ class UserPlaybookStoreMixin:
12
+ """Abstract user playbook CRUD + search methods."""
13
+
14
+ @abstractmethod
15
+ def save_user_playbooks(self, user_playbooks: list[UserPlaybook]) -> None:
16
+ raise NotImplementedError
17
+
18
+ @abstractmethod
19
+ def get_user_playbooks(
20
+ self,
21
+ limit: int = 100,
22
+ user_id: str | None = None,
23
+ playbook_name: str | None = None,
24
+ agent_version: str | None = None,
25
+ status_filter: list[Status | None] | None = None,
26
+ start_time: int | None = None,
27
+ end_time: int | None = None,
28
+ include_embedding: bool = False,
29
+ tags: list[str] | None = None,
30
+ offset: int = 0,
31
+ user_playbook_id: int | None = None,
32
+ request_id: str | None = None,
33
+ query: str | None = None,
34
+ ) -> list[UserPlaybook]:
35
+ """Get user playbooks from storage.
36
+
37
+ Args:
38
+ limit (int): Maximum number of playbooks to return
39
+ user_playbook_id (int, optional): Exact user playbook ID to retrieve.
40
+ user_id (str, optional): The user ID to filter by. If None, returns playbooks for all users.
41
+ request_id (str, optional): Request ID that generated the playbook.
42
+ query (str, optional): Case-insensitive text filter across visible fields.
43
+ playbook_name (str, optional): The playbook name to filter by. If None, returns all user playbooks.
44
+ agent_version (str, optional): The agent version to filter by. If None, returns all agent versions.
45
+ status_filter (list[Optional[Status]], optional): List of status values to filter by.
46
+ Can include None (current), Status.PENDING (from rerun), Status.ARCHIVED (old).
47
+ If None, returns playbooks with all statuses.
48
+ start_time (int, optional): Unix timestamp. Only return playbooks created at or after this time.
49
+ end_time (int, optional): Unix timestamp. Only return playbooks created at or before this time.
50
+ include_embedding (bool): If True, fetch and parse embedding vectors. Defaults to False.
51
+ tags (list[str], optional): Match playbooks having any of these tags.
52
+ offset (int): Number of matching rows to skip. Defaults to 0.
53
+
54
+ Returns:
55
+ list[UserPlaybook]: List of user playbook objects
56
+ """
57
+ raise NotImplementedError
58
+
59
+ @abstractmethod
60
+ def count_user_playbooks(
61
+ self,
62
+ user_id: str | None = None,
63
+ playbook_name: str | None = None,
64
+ min_user_playbook_id: int | None = None,
65
+ agent_version: str | None = None,
66
+ status_filter: list[Status | None] | None = None,
67
+ ) -> int:
68
+ """Count user playbooks in storage efficiently.
69
+
70
+ Args:
71
+ user_id (str, optional): The user ID to filter by. If None, counts playbooks for all users.
72
+ playbook_name (str, optional): The playbook name to filter by. If None, counts all user playbooks.
73
+ min_user_playbook_id (int, optional): Only count playbooks with user_playbook_id greater than this value.
74
+ agent_version (str, optional): The agent version to filter by. If None, counts all agent versions.
75
+ status_filter (list[Optional[Status]], optional): List of status values to filter by.
76
+ Can include None (current), Status.PENDING (from rerun), Status.ARCHIVED (old).
77
+ If None, returns playbooks with all statuses.
78
+
79
+ Returns:
80
+ int: Count of user playbooks matching the filters
81
+ """
82
+ raise NotImplementedError
83
+
84
+ @abstractmethod
85
+ def count_user_playbooks_by_session(self, session_id: str) -> int:
86
+ """Count user playbooks linked to a session via request_id -> requests.session_id.
87
+
88
+ Args:
89
+ session_id (str): The session ID to count user playbooks for
90
+
91
+ Returns:
92
+ int: Count of user playbooks linked to the session
93
+ """
94
+ raise NotImplementedError
95
+
96
+ @abstractmethod
97
+ def delete_all_user_playbooks(self) -> None:
98
+ """Delete all user playbooks from storage."""
99
+ raise NotImplementedError
100
+
101
+ @abstractmethod
102
+ def delete_all_user_playbooks_by_playbook_name(
103
+ self, playbook_name: str, agent_version: str | None = None
104
+ ) -> None:
105
+ """Delete all user playbooks by playbook name from storage.
106
+
107
+ Args:
108
+ playbook_name (str): The playbook name to delete
109
+ agent_version (str, optional): The agent version to filter by. If None, deletes all agent versions.
110
+ """
111
+ raise NotImplementedError
112
+
113
+ @abstractmethod
114
+ def delete_user_playbook(self, user_playbook_id: int) -> None:
115
+ """Delete a user playbook by ID.
116
+
117
+ Args:
118
+ user_playbook_id (int): The ID of the user playbook to delete
119
+ """
120
+ raise NotImplementedError
121
+
122
+ @abstractmethod
123
+ def update_all_user_playbooks_status(
124
+ self,
125
+ old_status: Status | None,
126
+ new_status: Status | None,
127
+ agent_version: str | None = None,
128
+ playbook_name: str | None = None,
129
+ ) -> int:
130
+ """Update all user playbooks with old_status to new_status atomically.
131
+
132
+ Args:
133
+ old_status: The current status to match (None for CURRENT)
134
+ new_status: The new status to set (None for CURRENT)
135
+ agent_version: Optional filter by agent version
136
+ playbook_name: Optional filter by playbook name
137
+
138
+ Returns:
139
+ int: Number of user playbooks updated
140
+ """
141
+ raise NotImplementedError
142
+
143
+ @abstractmethod
144
+ def delete_all_user_playbooks_by_status(
145
+ self,
146
+ status: Status,
147
+ agent_version: str | None = None,
148
+ playbook_name: str | None = None,
149
+ ) -> int:
150
+ """Delete all user playbooks with the given status atomically.
151
+
152
+ Args:
153
+ status: The status of user playbooks to delete
154
+ agent_version: Optional filter by agent version
155
+ playbook_name: Optional filter by playbook name
156
+
157
+ Returns:
158
+ int: Number of user playbooks deleted
159
+ """
160
+ raise NotImplementedError
161
+
162
+ @abstractmethod
163
+ def delete_user_playbooks_by_ids(
164
+ self, user_playbook_ids: list[int], *, emit_hard_delete: bool = True
165
+ ) -> int:
166
+ """Delete user playbooks by their IDs.
167
+
168
+ Args:
169
+ user_playbook_ids: List of user_playbook_id values to delete
170
+ emit_hard_delete: When True (default), append a ``hard_delete``
171
+ lineage event per id (genuine erasure). Set False for rollback
172
+ cleanup of a never-live row (e.g. a lost supersede CAS), so no
173
+ spurious audit event is recorded.
174
+
175
+ Returns:
176
+ int: Number of user playbooks deleted
177
+ """
178
+ raise NotImplementedError
179
+
180
+ @abstractmethod
181
+ def get_user_playbooks_by_ids(
182
+ self,
183
+ user_id: str,
184
+ user_playbook_ids: list[int],
185
+ status_filter: list[Status | None] | None = None,
186
+ ) -> list[UserPlaybook]:
187
+ """Fetch the subset of a user's playbooks whose ids are in the list.
188
+
189
+ Server-side filter on (``user_id``, ``user_playbook_id IN (...)``)
190
+ so callers (e.g. the reflection service resolving a small set of
191
+ cited playbook ids) avoid scanning every playbook for the user.
192
+
193
+ Args:
194
+ user_id (str): Owning user id.
195
+ user_playbook_ids (list[int]): Playbook ids to fetch. Empty
196
+ list returns ``[]`` without hitting storage.
197
+ status_filter (list[Status | None] | None): Statuses to
198
+ include. ``None`` (default) means CURRENT only — same
199
+ default as ``get_user_playbooks`` for consistency.
200
+
201
+ Returns:
202
+ list[UserPlaybook]: Matching playbooks. Order is unspecified.
203
+ Ids that do not exist (or do not match the user / status
204
+ filter) are silently omitted.
205
+ """
206
+ raise NotImplementedError
207
+
208
+ @abstractmethod
209
+ def get_user_playbook_by_id(
210
+ self, user_playbook_id: int, *, include_tombstones: bool = False
211
+ ) -> UserPlaybook | None:
212
+ """Fetch one user playbook by primary key.
213
+
214
+ Args:
215
+ user_playbook_id: The user_playbook_id to look up.
216
+ include_tombstones: When False (default), MERGED/SUPERSEDED rows
217
+ return None. Set to True for lineage resolution (resolve_current).
218
+
219
+ Returns:
220
+ The UserPlaybook if found and not filtered, otherwise None.
221
+ """
222
+ raise NotImplementedError
223
+
224
+ @abstractmethod
225
+ def get_user_playbooks_by_ids_any_user(
226
+ self,
227
+ user_playbook_ids: list[int],
228
+ status_filter: list[Status | None] | None = None,
229
+ ) -> list[UserPlaybook]:
230
+ """Fetch user playbooks by ids without requiring a single owner id."""
231
+ raise NotImplementedError
232
+
233
+ @abstractmethod
234
+ def archive_user_playbook_by_id(self, user_id: str, user_playbook_id: int) -> bool:
235
+ """Atomically archive a single user playbook by id, only if CURRENT.
236
+
237
+ Flips the row's ``status`` from ``None`` (CURRENT) to
238
+ ``Status.ARCHIVED``. No-op when the playbook does not exist, has
239
+ a different ``user_id``, or is already non-current.
240
+
241
+ Args:
242
+ user_id (str): Owning user id; used as a guard so callers
243
+ cannot accidentally archive another user's playbook.
244
+ user_playbook_id (int): The user_playbook_id to archive.
245
+
246
+ Returns:
247
+ bool: True if a row was archived; False otherwise.
248
+ """
249
+ raise NotImplementedError
250
+
251
+ @abstractmethod
252
+ def has_user_playbooks_with_status(
253
+ self,
254
+ status: Status | None,
255
+ agent_version: str | None = None,
256
+ playbook_name: str | None = None,
257
+ ) -> bool:
258
+ """Check if any user playbooks exist with given status and filters.
259
+
260
+ Args:
261
+ status: The status to check for (None for CURRENT)
262
+ agent_version: Optional filter by agent version
263
+ playbook_name: Optional filter by playbook name
264
+
265
+ Returns:
266
+ bool: True if any matching user playbooks exist
267
+ """
268
+ raise NotImplementedError
269
+
270
+ @abstractmethod
271
+ def update_user_playbook(
272
+ self,
273
+ user_playbook_id: int,
274
+ playbook_name: str | None = None,
275
+ content: str | None = None,
276
+ trigger: str | None = None,
277
+ rationale: str | None = None,
278
+ blocking_issue: BlockingIssue | None = None,
279
+ tags: list[str] | None = None,
280
+ ) -> None:
281
+ """Update editable fields of a user playbook. Only non-None fields are updated.
282
+
283
+ Args:
284
+ user_playbook_id (int): The ID of the user playbook to update
285
+ playbook_name (str, optional): New playbook name
286
+ content (str, optional): New content text
287
+ trigger (str, optional): New trigger text
288
+ rationale (str, optional): New rationale text
289
+ blocking_issue (BlockingIssue, optional): New blocking issue
290
+ tags (list[str], optional): Replacement tags
291
+
292
+ Raises:
293
+ ValueError: If user playbook with the given ID is not found
294
+ """
295
+ raise NotImplementedError
296
+
297
+ @abstractmethod
298
+ def supersede_user_playbooks_by_ids(
299
+ self, user_playbook_ids: list[int], request_id: str
300
+ ) -> int:
301
+ """Soft-delete user playbooks by setting status to SUPERSEDED.
302
+
303
+ Eligible rows (CURRENT, PENDING, or ARCHIVED; not already MERGED /
304
+ SUPERSEDED) are transitioned to SUPERSEDED and emit one status_change
305
+ lineage event under the shared request id. This is the user-playbook
306
+ analogue of the existing agent/profile soft-supersede helpers and
307
+ preserves dead-source content for point-in-time attribution reads.
308
+
309
+ Args:
310
+ user_playbook_ids (list[int]): User playbook ids to supersede.
311
+ request_id (str): Shared request id for all emitted lineage events.
312
+
313
+ Returns:
314
+ int: Number of user playbooks actually updated.
315
+ """
316
+ raise NotImplementedError
317
+
318
+ @abstractmethod
319
+ def search_user_playbooks(
320
+ self,
321
+ request: SearchUserPlaybookRequest,
322
+ options: SearchOptions | None = None,
323
+ ) -> list[UserPlaybook]:
324
+ """Search user playbooks with advanced filtering including semantic search.
325
+
326
+ Args:
327
+ request (SearchUserPlaybookRequest): Search request with query, filters, and pagination
328
+ options (SearchOptions, optional): Engine-level search parameters (e.g. pre-computed embedding)
329
+
330
+ Returns:
331
+ list[UserPlaybook]: List of matching user playbook objects
332
+ """
333
+ raise NotImplementedError
@@ -0,0 +1,8 @@
1
+ # tagging
2
+
3
+ Compact post-generation entity tagging capability.
4
+
5
+ - `service.py` tags profiles and playbooks with the configured tagging prompts.
6
+ - `tagging_scheduler.py` debounces post-publish tagging and rebuilds request context for background execution.
7
+
8
+ This package intentionally does not use `components/`: the service and scheduler are the only module responsibilities.
@@ -0,0 +1 @@
1
+ """Post-generation entity tagging services."""
@@ -0,0 +1,200 @@
1
+ from __future__ import annotations
2
+
3
+ import logging
4
+ import os
5
+
6
+ from pydantic import ConfigDict, Field
7
+
8
+ from reflexio.models.api_schema.service_schemas import (
9
+ AgentPlaybook,
10
+ UserPlaybook,
11
+ UserProfile,
12
+ )
13
+ from reflexio.models.structured_output import StrictStructuredOutput
14
+ from reflexio.server.api_endpoints.request_context import RequestContext
15
+ from reflexio.server.llm.litellm_client import LiteLLMClient
16
+ from reflexio.server.llm.model_defaults import ModelRole, resolve_model_name
17
+ from reflexio.server.services.service_utils import log_llm_messages, log_model_response
18
+ from reflexio.server.site_var.site_var_manager import SiteVarManager
19
+
20
+ logger = logging.getLogger(__name__)
21
+
22
+ TAGGING_PROMPT_ID = "tagging"
23
+
24
+ # Upper bound on how many current playbooks a single tagging pass will scan for
25
+ # untagged entries. Tagging is idempotent (already-tagged entities are skipped),
26
+ # so steady-state passes only tag newly generated entities; this bound just keeps
27
+ # a one-time backfill from being silently truncated at the storage default of 100.
28
+ _TAGGING_FETCH_LIMIT = 1000
29
+
30
+
31
+ class TagsOutput(StrictStructuredOutput):
32
+ tags: list[str] = Field(default_factory=list)
33
+
34
+ model_config = ConfigDict(
35
+ extra="allow",
36
+ json_schema_extra={"additionalProperties": False},
37
+ )
38
+
39
+
40
+ class TaggingService:
41
+ def __init__(
42
+ self,
43
+ llm_client: LiteLLMClient,
44
+ request_context: RequestContext,
45
+ ) -> None:
46
+ self.client = llm_client
47
+ self.request_context = request_context
48
+ self.configurator = request_context.configurator
49
+ self.storage = request_context.storage
50
+
51
+ model_setting = SiteVarManager().get_site_var("llm_model_setting")
52
+ site_var = model_setting if isinstance(model_setting, dict) else {}
53
+ config = self.configurator.get_config()
54
+ llm_config = config.llm_config if config else None
55
+ api_key_config = config.api_key_config if config else None
56
+ self.model_name = resolve_model_name(
57
+ ModelRole.GENERATION,
58
+ site_var_value=site_var.get("default_generation_model_name"),
59
+ config_override=llm_config.generation_model_name if llm_config else None,
60
+ api_key_config=api_key_config,
61
+ )
62
+
63
+ def run(
64
+ self,
65
+ *,
66
+ user_id: str,
67
+ agent_version: str,
68
+ tag_profiles: bool = True,
69
+ tag_playbooks: bool = True,
70
+ ) -> None:
71
+ if self.storage is None:
72
+ return
73
+
74
+ config = self.configurator.get_config()
75
+ if config is None:
76
+ return
77
+
78
+ profile_prompt = getattr(
79
+ config.profile_extractor_config, "tagging_definition_prompt", None
80
+ )
81
+ user_playbook_prompt = getattr(
82
+ config.user_playbook_extractor_config, "tagging_definition_prompt", None
83
+ )
84
+
85
+ if tag_profiles and profile_prompt:
86
+ self._tag_profiles(
87
+ user_id=user_id, tagging_definition_prompt=profile_prompt
88
+ )
89
+ if tag_playbooks and user_playbook_prompt:
90
+ self._tag_user_playbooks(
91
+ user_id=user_id,
92
+ agent_version=agent_version,
93
+ tagging_definition_prompt=user_playbook_prompt,
94
+ )
95
+ self._tag_agent_playbooks(
96
+ agent_version=agent_version,
97
+ tagging_definition_prompt=user_playbook_prompt,
98
+ )
99
+
100
+ def _tag_profiles(self, *, user_id: str, tagging_definition_prompt: str) -> None:
101
+ profiles = self.storage.get_user_profile(user_id) # type: ignore[union-attr]
102
+ for profile in profiles:
103
+ if profile.tags is not None:
104
+ continue # already tagged (incl. empty result) — tag each entity once
105
+ tags = self._generate_tags(
106
+ tagging_definition_prompt=tagging_definition_prompt,
107
+ content=self._profile_content(profile),
108
+ )
109
+ self.storage.update_user_profile_tags( # type: ignore[union-attr]
110
+ user_id, profile.profile_id, tags
111
+ )
112
+
113
+ def _tag_user_playbooks(
114
+ self,
115
+ *,
116
+ user_id: str,
117
+ agent_version: str,
118
+ tagging_definition_prompt: str,
119
+ ) -> None:
120
+ playbooks = self.storage.get_user_playbooks( # type: ignore[union-attr]
121
+ limit=_TAGGING_FETCH_LIMIT,
122
+ user_id=user_id,
123
+ agent_version=agent_version,
124
+ status_filter=[None],
125
+ )
126
+ for playbook in playbooks:
127
+ if playbook.tags is not None:
128
+ continue # already tagged (incl. empty result) — tag each entity once
129
+ tags = self._generate_tags(
130
+ tagging_definition_prompt=tagging_definition_prompt,
131
+ content=self._playbook_content(playbook),
132
+ )
133
+ self.storage.update_user_playbook( # type: ignore[union-attr]
134
+ playbook.user_playbook_id,
135
+ tags=tags,
136
+ )
137
+
138
+ def _tag_agent_playbooks(
139
+ self,
140
+ *,
141
+ agent_version: str,
142
+ tagging_definition_prompt: str,
143
+ ) -> None:
144
+ playbooks = self.storage.get_agent_playbooks( # type: ignore[union-attr]
145
+ limit=_TAGGING_FETCH_LIMIT,
146
+ agent_version=agent_version,
147
+ status_filter=[None],
148
+ )
149
+ for playbook in playbooks:
150
+ if playbook.tags is not None:
151
+ continue # already tagged (incl. empty result) — tag each entity once
152
+ tags = self._generate_tags(
153
+ tagging_definition_prompt=tagging_definition_prompt,
154
+ content=self._playbook_content(playbook),
155
+ )
156
+ self.storage.update_agent_playbook( # type: ignore[union-attr]
157
+ playbook.agent_playbook_id,
158
+ tags=tags,
159
+ )
160
+
161
+ def _generate_tags(
162
+ self, *, tagging_definition_prompt: str, content: str
163
+ ) -> list[str]:
164
+ if os.getenv("MOCK_LLM_RESPONSE", "").lower() == "true":
165
+ return ["example_tag"]
166
+
167
+ prompt = self.request_context.prompt_manager.render_prompt(
168
+ TAGGING_PROMPT_ID,
169
+ {
170
+ "tagging_definition_prompt": tagging_definition_prompt.strip(),
171
+ "content": content,
172
+ },
173
+ )
174
+ messages = [{"role": "user", "content": prompt}]
175
+ log_llm_messages(logger, "Tagging", messages)
176
+ response = self.client.generate_chat_response(
177
+ messages=messages,
178
+ model=self.model_name,
179
+ response_format=TagsOutput,
180
+ )
181
+ log_model_response(logger, "Tagging response", response)
182
+ if not isinstance(response, TagsOutput):
183
+ logger.warning(
184
+ "Unexpected response type from tagging LLM: %s", type(response)
185
+ )
186
+ return []
187
+ return [tag.strip() for tag in response.tags if tag.strip()]
188
+
189
+ @staticmethod
190
+ def _profile_content(profile: UserProfile) -> str:
191
+ return profile.content
192
+
193
+ @staticmethod
194
+ def _playbook_content(playbook: UserPlaybook | AgentPlaybook) -> str:
195
+ parts = [playbook.content]
196
+ if playbook.trigger:
197
+ parts.append(f"Trigger: {playbook.trigger}")
198
+ if playbook.rationale:
199
+ parts.append(f"Rationale: {playbook.rationale}")
200
+ return "\n".join(part for part in parts if part)
@@ -0,0 +1,149 @@
1
+ """Singleton scheduler for deferred, off-publish-path entity tagging.
2
+
3
+ Tagging runs an LLM call per newly generated profile/playbook, so it must not
4
+ block the publish request. This scheduler mirrors
5
+ :class:`GroupEvaluationScheduler`: a single daemon thread with a min-heap, where
6
+ each publish upserts the fire time for its ``(org_id, user_id, agent_version)``
7
+ key. Rapid successive publishes for the same key debounce into a single tagging
8
+ pass; when the timer fires, the tagging callback runs on its own daemon thread.
9
+
10
+ Tagging is idempotent (already-tagged entities are skipped), so a deferred pass
11
+ naturally handles both newly generated entities and the one-time backfill that
12
+ happens when a tagging definition is first configured.
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import heapq
18
+ import logging
19
+ import os
20
+ import threading
21
+ import time
22
+ from collections.abc import Callable
23
+
24
+ from reflexio.server.api_endpoints.request_context import RequestContext
25
+ from reflexio.server.llm.litellm_client import LiteLLMClient
26
+ from reflexio.server.services.tagging.service import TaggingService
27
+
28
+ logger = logging.getLogger(__name__)
29
+
30
+ # Delay before a tagging pass fires after the last publish for a key. Long enough
31
+ # to debounce a burst of publishes into one pass, short enough that tags appear
32
+ # promptly. Kept as a patch point for tests.
33
+ TAGGING_DELAY_SECONDS = 15
34
+ IS_TEST_ENV = os.environ.get("IS_TEST_ENV", "false").strip().lower() == "true"
35
+ _EFFECTIVE_DELAY_SECONDS = 1 if IS_TEST_ENV else TAGGING_DELAY_SECONDS
36
+
37
+ # (org_id, user_id, agent_version)
38
+ TaggingKey = tuple[str, str, str]
39
+
40
+
41
+ class TaggingScheduler:
42
+ """Singleton scheduler that fires entity tagging after a short debounce."""
43
+
44
+ _instance = None
45
+ _lock = threading.Lock()
46
+
47
+ @classmethod
48
+ def get_instance(cls) -> TaggingScheduler:
49
+ if cls._instance is None:
50
+ with cls._lock:
51
+ if cls._instance is None:
52
+ cls._instance = cls()
53
+ return cls._instance
54
+
55
+ def __init__(self) -> None:
56
+ self._scheduled: dict[TaggingKey, tuple[float, Callable]] = {}
57
+ self._heap: list[tuple[float, TaggingKey]] = []
58
+ self._mutex = threading.Lock()
59
+ self._wake_event = threading.Event()
60
+ self._thread = threading.Thread(
61
+ target=self._scheduler_loop, daemon=True, name="tagging-scheduler"
62
+ )
63
+ self._thread.start()
64
+ logger.info("TaggingScheduler started")
65
+
66
+ def schedule(self, key: TaggingKey, callback: Callable) -> None:
67
+ """Schedule or reschedule a tagging pass for ``key`` (slides the fire time forward)."""
68
+ fire_time = time.monotonic() + _EFFECTIVE_DELAY_SECONDS
69
+ with self._mutex:
70
+ self._scheduled[key] = (fire_time, callback)
71
+ heapq.heappush(self._heap, (fire_time, key))
72
+ self._wake_event.set()
73
+
74
+ def _scheduler_loop(self) -> None:
75
+ while True:
76
+ try:
77
+ with self._mutex:
78
+ next_fire_time = self._heap[0][0] if self._heap else None
79
+
80
+ if next_fire_time is None:
81
+ self._wake_event.wait()
82
+ self._wake_event.clear()
83
+ continue
84
+
85
+ wait_seconds = next_fire_time - time.monotonic()
86
+ if wait_seconds > 0:
87
+ self._wake_event.wait(timeout=wait_seconds)
88
+ self._wake_event.clear()
89
+ continue
90
+
91
+ with self._mutex:
92
+ while self._heap and self._heap[0][0] <= time.monotonic():
93
+ fire_time, key = heapq.heappop(self._heap)
94
+
95
+ current = self._scheduled.get(key)
96
+ if current is None:
97
+ continue
98
+ current_fire_time, callback = current
99
+ if abs(current_fire_time - fire_time) > 0.001:
100
+ # Superseded by a newer schedule for the same key.
101
+ continue
102
+
103
+ del self._scheduled[key]
104
+ t = threading.Thread(
105
+ target=self._run_callback,
106
+ args=(key, callback),
107
+ daemon=True,
108
+ name=f"tagging-{key[1][:20]}",
109
+ )
110
+ t.start()
111
+ except Exception:
112
+ logger.exception("Error in tagging scheduler loop")
113
+ time.sleep(1)
114
+
115
+ @staticmethod
116
+ def _run_callback(key: TaggingKey, callback: Callable) -> None:
117
+ try:
118
+ logger.info("Firing tagging for key=%s", key)
119
+ callback()
120
+ logger.info("Completed tagging for key=%s", key)
121
+ except Exception:
122
+ logger.exception("Tagging callback failed for key=%s", key)
123
+
124
+
125
+ def schedule_tagging(
126
+ *,
127
+ org_id: str,
128
+ user_id: str,
129
+ agent_version: str,
130
+ request_context: RequestContext,
131
+ llm_client: LiteLLMClient,
132
+ ) -> None:
133
+ """Enqueue a deferred tagging pass for the given user/agent off the publish path."""
134
+ if not user_id:
135
+ return
136
+
137
+ key: TaggingKey = (org_id, user_id, agent_version)
138
+ storage_base_dir = getattr(request_context, "storage_base_dir", None)
139
+
140
+ def callback() -> None:
141
+ fresh_context = RequestContext(
142
+ org_id=org_id,
143
+ storage_base_dir=storage_base_dir,
144
+ )
145
+ TaggingService(llm_client=llm_client, request_context=fresh_context).run(
146
+ user_id=user_id, agent_version=agent_version
147
+ )
148
+
149
+ TaggingScheduler.get_instance().schedule(key, callback)