claude-smart 0.2.45 → 0.2.46

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (322) hide show
  1. package/.claude-plugin/marketplace.json +3 -3
  2. package/README.md +34 -5
  3. package/bin/claude-smart.js +295 -5
  4. package/package.json +22 -3
  5. package/plugin/.claude-plugin/plugin.json +4 -2
  6. package/plugin/.codex-plugin/plugin.json +1 -1
  7. package/plugin/README.md +22 -2
  8. package/plugin/commands/clear-all.md +1 -0
  9. package/plugin/commands/dashboard.md +1 -0
  10. package/plugin/commands/learn.md +1 -0
  11. package/plugin/commands/restart.md +1 -0
  12. package/plugin/commands/show.md +1 -0
  13. package/plugin/dashboard/app/configure/env/page.tsx +40 -14
  14. package/plugin/dashboard/app/configure/server/page.tsx +51 -1
  15. package/plugin/dashboard/app/preferences/[id]/page.tsx +8 -8
  16. package/plugin/dashboard/app/skills/project/[id]/page.tsx +7 -3
  17. package/plugin/dashboard/app/skills/shared/[id]/page.tsx +10 -3
  18. package/plugin/dashboard/lib/claude-settings-file.ts +20 -10
  19. package/plugin/dashboard/lib/reflexio-client.ts +16 -0
  20. package/plugin/dashboard/lib/status.ts +10 -3
  21. package/plugin/dashboard/lib/types.ts +18 -6
  22. package/plugin/hooks/codex-hooks.json +0 -1
  23. package/plugin/opencode/assistant-buffer.ts +108 -0
  24. package/plugin/opencode/dist/assistant-buffer.js +96 -0
  25. package/plugin/opencode/dist/internal.js +12 -0
  26. package/plugin/opencode/dist/payload.js +85 -0
  27. package/plugin/opencode/dist/server.mjs +158 -0
  28. package/plugin/opencode/internal.ts +18 -0
  29. package/plugin/opencode/package.json +3 -0
  30. package/plugin/opencode/payload.ts +90 -0
  31. package/plugin/opencode/server.mts +174 -0
  32. package/plugin/opencode/tsconfig.json +13 -0
  33. package/plugin/pyproject.toml +20 -3
  34. package/plugin/scripts/_lib.sh +5 -1
  35. package/plugin/scripts/backend-service.sh +22 -2
  36. package/plugin/scripts/ensure-plugin-root.sh +4 -4
  37. package/plugin/scripts/hook_entry.sh +1 -1
  38. package/plugin/scripts/opencode-claude-compat +4 -0
  39. package/plugin/scripts/opencode-claude-compat.cmd +3 -0
  40. package/plugin/scripts/opencode-claude-compat.js +225 -0
  41. package/plugin/scripts/smart-install.sh +10 -8
  42. package/plugin/src/README.md +1 -1
  43. package/plugin/src/claude_smart/cli.py +304 -6
  44. package/plugin/src/claude_smart/env_config.py +12 -4
  45. package/plugin/src/claude_smart/events/session_start.py +26 -7
  46. package/plugin/src/claude_smart/events/stop.py +1 -1
  47. package/plugin/src/claude_smart/ids.py +1 -1
  48. package/plugin/src/claude_smart/reflexio_adapter.py +5 -5
  49. package/plugin/src/claude_smart/runtime.py +7 -1
  50. package/plugin/uv.lock +1 -1
  51. package/plugin/vendor/reflexio/.env.example +54 -30
  52. package/plugin/vendor/reflexio/README.md +14 -8
  53. package/plugin/vendor/reflexio/pyproject.toml +13 -1
  54. package/plugin/vendor/reflexio/reflexio/README.md +1 -0
  55. package/plugin/vendor/reflexio/reflexio/cli/README.md +9 -7
  56. package/plugin/vendor/reflexio/reflexio/cli/__main__.py +10 -1
  57. package/plugin/vendor/reflexio/reflexio/cli/bootstrap_config.py +22 -5
  58. package/plugin/vendor/reflexio/reflexio/cli/commands/interactions.py +34 -4
  59. package/plugin/vendor/reflexio/reflexio/cli/commands/profiles.py +12 -7
  60. package/plugin/vendor/reflexio/reflexio/cli/commands/services.py +13 -6
  61. package/plugin/vendor/reflexio/reflexio/cli/commands/setup_cmd.py +210 -50
  62. package/plugin/vendor/reflexio/reflexio/cli/commands/shortcuts.py +15 -2
  63. package/plugin/vendor/reflexio/reflexio/cli/env_loader.py +292 -22
  64. package/plugin/vendor/reflexio/reflexio/cli/log_format.py +29 -4
  65. package/plugin/vendor/reflexio/reflexio/cli/run_services.py +17 -8
  66. package/plugin/vendor/reflexio/reflexio/cli/stop_services.py +13 -10
  67. package/plugin/vendor/reflexio/reflexio/client/client.py +57 -13
  68. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/README.md +135 -257
  69. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/npm/openclaw-smart/bin/openclaw-smart.js +13 -0
  70. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/npm/openclaw-smart/package.json +15 -0
  71. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/README.md +38 -0
  72. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/index.ts +151 -110
  73. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/openclaw.plugin.json +20 -10
  74. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/package.json +28 -6
  75. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/pyproject.toml +42 -0
  76. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/_lib.sh +371 -0
  77. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/backend-log-runner.sh +33 -0
  78. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/backend-service.sh +271 -0
  79. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/cli.sh +68 -0
  80. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/dashboard-open.sh +15 -0
  81. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/ensure-plugin-root.sh +84 -0
  82. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/hook_entry.sh +106 -0
  83. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/npm-cli.js +219 -0
  84. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/smart-install.sh +269 -0
  85. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/clear-all/SKILL.md +8 -0
  86. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/dashboard/SKILL.md +8 -0
  87. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/learn/SKILL.md +10 -0
  88. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/reflexio/SKILL.md +15 -44
  89. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/restart/SKILL.md +6 -0
  90. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/show/SKILL.md +8 -0
  91. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/cli.py +634 -0
  92. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/context_format.py +224 -0
  93. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/context_inject.py +79 -0
  94. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/__init__.py +0 -0
  95. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/after_tool_call.py +160 -0
  96. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/agent_end.py +187 -0
  97. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/before_prompt_build.py +69 -0
  98. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/before_tool_call.py +30 -0
  99. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/session_end.py +36 -0
  100. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/session_start.py +130 -0
  101. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/hook.py +131 -0
  102. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/ids.py +94 -0
  103. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/internal_call.py +75 -0
  104. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/oc_cite.py +196 -0
  105. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/optimizer_assistant.py +272 -0
  106. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/publish.py +96 -0
  107. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/query_compose.py +66 -0
  108. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/reflexio_adapter.py +336 -0
  109. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/runtime.py +47 -0
  110. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/stall_banner.py +61 -0
  111. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/state.py +323 -0
  112. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/__init__.py +0 -0
  113. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/integration/__init__.py +0 -0
  114. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/integration/test_e2e_session_loop.py +190 -0
  115. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/integration/test_publish_to_local_reflexio_integration.py +112 -0
  116. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/integration/test_recursion_guard_integration.py +86 -0
  117. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/integration/test_search_inject_integration.py +144 -0
  118. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_cli.py +184 -0
  119. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_events_after_tool_call.py +142 -0
  120. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_events_agent_end.py +233 -0
  121. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_events_before_prompt_build.py +116 -0
  122. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_events_before_tool_call.py +35 -0
  123. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_events_session_end.py +47 -0
  124. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_events_session_start.py +109 -0
  125. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_hook_dispatch.py +117 -0
  126. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_ids.py +41 -0
  127. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_internal_call.py +50 -0
  128. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_oc_cite.py +88 -0
  129. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_publish.py +48 -0
  130. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_query_compose.py +48 -0
  131. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_reflexio_adapter.py +188 -0
  132. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_runtime.py +41 -0
  133. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_state.py +235 -0
  134. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests-ts/__mocks__/plugin-entry-stub.ts +6 -0
  135. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests-ts/test_npm_cli.test.ts +56 -0
  136. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests-ts/test_shim_dispatch.test.ts +171 -0
  137. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tsconfig.build.json +18 -0
  138. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/{tsconfig.json → plugin/tsconfig.json} +2 -2
  139. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/uv.lock +3835 -0
  140. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/vitest.config.ts +18 -0
  141. package/plugin/vendor/reflexio/reflexio/lib/_agent_playbook.py +238 -10
  142. package/plugin/vendor/reflexio/reflexio/lib/_config.py +11 -4
  143. package/plugin/vendor/reflexio/reflexio/lib/_generation.py +16 -5
  144. package/plugin/vendor/reflexio/reflexio/lib/_lineage_parity_readers.py +187 -0
  145. package/plugin/vendor/reflexio/reflexio/lib/_profiles.py +198 -6
  146. package/plugin/vendor/reflexio/reflexio/lib/_reflection.py +1 -1
  147. package/plugin/vendor/reflexio/reflexio/lib/_search.py +29 -20
  148. package/plugin/vendor/reflexio/reflexio/lib/_user_playbook.py +2 -1
  149. package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/entities.py +127 -38
  150. package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/enums.py +4 -0
  151. package/plugin/vendor/reflexio/reflexio/models/api_schema/eval_overview_schema.py +64 -41
  152. package/plugin/vendor/reflexio/reflexio/models/api_schema/internal_schema.py +19 -0
  153. package/plugin/vendor/reflexio/reflexio/models/api_schema/pending_tool_call_schema.py +12 -5
  154. package/plugin/vendor/reflexio/reflexio/models/api_schema/retriever_schema.py +52 -0
  155. package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/converters.py +5 -1
  156. package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/entities.py +5 -1
  157. package/plugin/vendor/reflexio/reflexio/models/config_schema.py +88 -7
  158. package/plugin/vendor/reflexio/reflexio/models/structured_output.py +148 -0
  159. package/plugin/vendor/reflexio/reflexio/server/OVERVIEW.md +3 -3
  160. package/plugin/vendor/reflexio/reflexio/server/README.md +45 -32
  161. package/plugin/vendor/reflexio/reflexio/server/__init__.py +29 -5
  162. package/plugin/vendor/reflexio/reflexio/server/__main__.py +2 -2
  163. package/plugin/vendor/reflexio/reflexio/server/_auth.py +65 -2
  164. package/plugin/vendor/reflexio/reflexio/server/api.py +734 -85
  165. package/plugin/vendor/reflexio/reflexio/server/api_endpoints/precondition_checks.py +7 -0
  166. package/plugin/vendor/reflexio/reflexio/server/api_endpoints/request_context.py +3 -1
  167. package/plugin/vendor/reflexio/reflexio/server/billing_meter.py +173 -0
  168. package/plugin/vendor/reflexio/reflexio/server/billing_signals.py +64 -0
  169. package/plugin/vendor/reflexio/reflexio/server/cache/reflexio_cache.py +46 -16
  170. package/plugin/vendor/reflexio/reflexio/server/env_utils.py +65 -0
  171. package/plugin/vendor/reflexio/reflexio/server/llm/embedding_service.py +19 -4
  172. package/plugin/vendor/reflexio/reflexio/server/llm/litellm_client.py +264 -77
  173. package/plugin/vendor/reflexio/reflexio/server/llm/llm_utils.py +69 -7
  174. package/plugin/vendor/reflexio/reflexio/server/llm/providers/embedding_service_provider.py +75 -16
  175. package/plugin/vendor/reflexio/reflexio/server/llm/providers/openclaw_provider.py +280 -0
  176. package/plugin/vendor/reflexio/reflexio/server/llm/token_accounting.py +48 -0
  177. package/plugin/vendor/reflexio/reflexio/server/llm/tools.py +107 -17
  178. package/plugin/vendor/reflexio/reflexio/server/operation_limiter.py +360 -4
  179. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.6.0.prompt.md +1 -1
  180. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.7.0.prompt.md +85 -0
  181. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_aggregation/v2.2.0.prompt.md +1 -1
  182. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_aggregation/v2.3.0.prompt.md +253 -0
  183. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.3.2.prompt.md +1 -1
  184. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.3.3.prompt.md +74 -0
  185. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.2.3.prompt.md +1 -1
  186. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.3.0.prompt.md +266 -0
  187. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.4.0.prompt.md +266 -0
  188. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v3.3.0.prompt.md +1 -1
  189. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v3.4.0.prompt.md +137 -0
  190. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main/v1.2.0.prompt.md +1 -1
  191. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main/v1.3.0.prompt.md +34 -0
  192. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_update_instruction_start/v1.0.0.prompt.md +5 -12
  193. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_update_instruction_start/v1.1.0.prompt.md +5 -12
  194. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_update_instruction_start/v1.2.0.prompt.md +155 -0
  195. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/tagging/v1.0.0.prompt.md +23 -0
  196. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_manager.py +89 -32
  197. package/plugin/vendor/reflexio/reflexio/server/services/README.md +26 -11
  198. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/README.md +22 -0
  199. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/__init__.py +1 -0
  200. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/agent_success_evaluation_constants.py +4 -2
  201. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/agent_success_evaluation_utils.py +1 -0
  202. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/components/__init__.py +7 -0
  203. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/{agent_success_evaluator.py → components/evaluator.py} +3 -16
  204. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/regen_jobs.py +28 -72
  205. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/{group_evaluation_runner.py → runner.py} +12 -10
  206. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/{delayed_group_evaluator.py → scheduler.py} +10 -2
  207. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/{agent_success_evaluation_service.py → service.py} +36 -1
  208. package/plugin/vendor/reflexio/reflexio/server/services/base_generation_service.py +160 -0
  209. package/plugin/vendor/reflexio/reflexio/server/services/configurator/base_configurator.py +5 -0
  210. package/plugin/vendor/reflexio/reflexio/server/services/deduplication_utils.py +1 -1
  211. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/README.md +9 -0
  212. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/components/__init__.py +0 -0
  213. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/{rule_attribution.py → components/rule_attribution.py} +10 -9
  214. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/eval_sampler.py +15 -24
  215. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/service.py +238 -164
  216. package/plugin/vendor/reflexio/reflexio/server/services/extraction/README.md +31 -0
  217. package/plugin/vendor/reflexio/reflexio/server/services/extraction/outcome.py +13 -3
  218. package/plugin/vendor/reflexio/reflexio/server/services/extraction/pending_tool_call_dispatch.py +3 -2
  219. package/plugin/vendor/reflexio/reflexio/server/services/extraction/resumable_agent.py +32 -118
  220. package/plugin/vendor/reflexio/reflexio/server/services/extraction/resume_worker.py +39 -16
  221. package/plugin/vendor/reflexio/reflexio/server/services/generation_service.py +105 -23
  222. package/plugin/vendor/reflexio/reflexio/server/services/lineage/__init__.py +0 -0
  223. package/plugin/vendor/reflexio/reflexio/server/services/lineage/gc_scheduler.py +193 -0
  224. package/plugin/vendor/reflexio/reflexio/server/services/lineage/resolve.py +98 -0
  225. package/plugin/vendor/reflexio/reflexio/server/services/operation_state_utils.py +26 -0
  226. package/plugin/vendor/reflexio/reflexio/server/services/playbook/README.md +11 -12
  227. package/plugin/vendor/reflexio/reflexio/server/services/playbook/components/__init__.py +5 -0
  228. package/plugin/vendor/reflexio/reflexio/server/services/playbook/{playbook_aggregator.py → components/aggregator.py} +316 -178
  229. package/plugin/vendor/reflexio/reflexio/server/services/playbook/{playbook_consolidator.py → components/consolidator.py} +168 -62
  230. package/plugin/vendor/reflexio/reflexio/server/services/playbook/{playbook_extractor.py → components/extractor.py} +5 -1
  231. package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_edit_apply.py +74 -0
  232. package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_service_utils.py +33 -3
  233. package/plugin/vendor/reflexio/reflexio/server/services/playbook/{playbook_generation_service.py → service.py} +121 -19
  234. package/plugin/vendor/reflexio/reflexio/server/services/playbook/user_detail_stripping.py +84 -0
  235. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/README.md +10 -0
  236. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/models.py +2 -1
  237. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/optimizer.py +178 -30
  238. package/plugin/vendor/reflexio/reflexio/server/services/pre_retrieval/README.md +9 -0
  239. package/plugin/vendor/reflexio/reflexio/server/services/profile/components/__init__.py +17 -0
  240. package/plugin/vendor/reflexio/reflexio/server/services/profile/{profile_deduplicator.py → components/consolidator.py} +7 -6
  241. package/plugin/vendor/reflexio/reflexio/server/services/profile/{profile_extractor.py → components/extractor.py} +30 -22
  242. package/plugin/vendor/reflexio/reflexio/server/services/profile/profile_generation_service_utils.py +6 -13
  243. package/plugin/vendor/reflexio/reflexio/server/services/profile/{profile_generation_service.py → service.py} +32 -50
  244. package/plugin/vendor/reflexio/reflexio/server/services/reflection/__init__.py +4 -8
  245. package/plugin/vendor/reflexio/reflexio/server/services/reflection/components/__init__.py +7 -0
  246. package/plugin/vendor/reflexio/reflexio/server/services/reflection/reflection_service_utils.py +12 -1
  247. package/plugin/vendor/reflexio/reflexio/server/services/reflection/{reflection_service.py → service.py} +36 -16
  248. package/plugin/vendor/reflexio/reflexio/server/services/retrieval/relevance_floor.py +83 -6
  249. package/plugin/vendor/reflexio/reflexio/server/services/service_utils.py +36 -1
  250. package/plugin/vendor/reflexio/reflexio/server/services/shadow_comparison/README.md +8 -0
  251. package/plugin/vendor/reflexio/reflexio/server/services/shadow_comparison/outcome.py +1 -2
  252. package/plugin/vendor/reflexio/reflexio/server/services/storage/error.py +27 -0
  253. package/plugin/vendor/reflexio/reflexio/server/services/storage/retention.py +9 -9
  254. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/__init__.py +2 -0
  255. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_agent_run.py +4 -1
  256. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_base.py +642 -197
  257. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_extras.py +61 -104
  258. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_lineage.py +628 -0
  259. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_operations.py +38 -6
  260. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_playbook.py +978 -184
  261. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_profiles.py +519 -128
  262. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_requests.py +77 -3
  263. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_shadow_verdicts.py +23 -9
  264. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_stall_state.py +5 -2
  265. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/__init__.py +131 -20
  266. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_agent_run.py +2 -8
  267. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_base.py +5 -0
  268. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_extras.py +36 -53
  269. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_lineage.py +215 -0
  270. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_operations.py +20 -0
  271. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_playbook.py +243 -7
  272. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_profiles.py +121 -1
  273. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_requests.py +60 -0
  274. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_retrieval_log.py +51 -0
  275. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_shadow_verdicts.py +17 -0
  276. package/plugin/vendor/reflexio/reflexio/server/services/tagging/README.md +8 -0
  277. package/plugin/vendor/reflexio/reflexio/server/services/tagging/__init__.py +1 -0
  278. package/plugin/vendor/reflexio/reflexio/server/services/tagging/service.py +200 -0
  279. package/plugin/vendor/reflexio/reflexio/server/services/tagging/tagging_scheduler.py +149 -0
  280. package/plugin/vendor/reflexio/reflexio/server/services/unified_search_service.py +229 -32
  281. package/plugin/vendor/reflexio/reflexio/server/site_var/README.md +2 -1
  282. package/plugin/vendor/reflexio/reflexio/server/site_var/feature_flags.py +120 -1
  283. package/plugin/vendor/reflexio/reflexio/server/site_var/site_var_sources/feature_flags.json +4 -0
  284. package/plugin/vendor/reflexio/reflexio/server/site_var/site_var_sources/search_settings.json +5 -0
  285. package/plugin/vendor/reflexio/reflexio/server/tracing.py +30 -0
  286. package/plugin/vendor/reflexio/reflexio/server/usage_metrics.py +18 -0
  287. package/plugin/vendor/reflexio/reflexio/test_support/llm_mock.py +61 -26
  288. package/plugin/vendor/reflexio/reflexio/test_support/llm_model_registry.py +41 -4
  289. package/scripts/setup-claude-smart.sh +8 -3
  290. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/TESTING.md +0 -517
  291. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/hook/handler.js +0 -473
  292. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/package-lock.json +0 -2156
  293. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/package.json +0 -18
  294. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/hook/handler.ts +0 -241
  295. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/hook/setup.ts +0 -140
  296. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/publish.ts +0 -113
  297. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/search.ts +0 -52
  298. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/server.ts +0 -103
  299. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/sqlite-buffer.ts +0 -156
  300. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/user-id.ts +0 -134
  301. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/rules/reflexio.md +0 -24
  302. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/publish_clawhub.sh +0 -278
  303. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/references/HOOK.md +0 -164
  304. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/scripts/install.sh +0 -36
  305. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/scripts/uninstall.sh +0 -35
  306. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/publish.test.ts +0 -27
  307. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/search.test.ts +0 -31
  308. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/server.test.ts +0 -42
  309. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/setup.test.ts +0 -49
  310. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/sqlite-buffer.test.ts +0 -91
  311. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/user-id.test.ts +0 -50
  312. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/vitest.config.ts +0 -13
  313. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/group_aggregation.py +0 -192
  314. package/plugin/vendor/reflexio/reflexio/server/services/extraction/invariants.py +0 -303
  315. package/plugin/vendor/reflexio/reflexio/server/services/extraction/plan.py +0 -138
  316. package/plugin/vendor/reflexio/reflexio/server/services/extraction/tools.py +0 -1125
  317. /package/plugin/vendor/reflexio/reflexio/integrations/{__init__.py → openclaw/plugin/src/openclaw_smart/__init__.py} +0 -0
  318. /package/plugin/vendor/reflexio/reflexio/integrations/openclaw/{types → plugin/types}/openclaw.d.ts +0 -0
  319. /package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/{distribution.py → components/distribution.py} +0 -0
  320. /package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/{hero_state.py → components/hero_state.py} +0 -0
  321. /package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/{shadow_aggregation.py → components/shadow_aggregation.py} +0 -0
  322. /package/plugin/vendor/reflexio/reflexio/server/services/reflection/{reflection_extractor.py → components/extractor.py} +0 -0
@@ -0,0 +1,215 @@
1
+ from abc import abstractmethod
2
+ from typing import Literal
3
+
4
+ from reflexio.models.api_schema.domain.entities import LineageContext, LineageEvent
5
+
6
+ EntityType = Literal["user_playbook", "agent_playbook", "profile"]
7
+
8
+
9
+ class LineageEventMixin:
10
+ """Abstract storage interface for the append-only, content-free lineage log."""
11
+
12
+ @abstractmethod
13
+ def append_lineage_event(self, event: LineageEvent) -> int:
14
+ """Append an event; idempotent on (org_id, entity_type, entity_id, op, request_id).
15
+
16
+ Args:
17
+ event (LineageEvent): The fully-formed event to persist. ``event_id``
18
+ may be 0; the storage layer assigns a real id on insert. On a
19
+ duplicate ``(org_id, entity_type, entity_id, op, request_id)`` the existing row
20
+ is returned unchanged.
21
+
22
+ Returns:
23
+ int: The assigned or existing ``event_id``.
24
+
25
+ Note:
26
+ This method deliberately does NOT enforce a non-empty ``request_id``.
27
+ System and GC events (e.g. ``hard_delete`` from TTL GC, ``status_change``
28
+ from internal transitions) legitimately use an auto-generated UUID that
29
+ need not be tied to a user-facing request id. Callers that require
30
+ request-scoped lineage (``merge_records``, ``supersede_record``) enforce
31
+ non-empty ``request_id`` themselves.
32
+ """
33
+ raise NotImplementedError
34
+
35
+ @abstractmethod
36
+ def get_lineage_events(
37
+ self,
38
+ *,
39
+ entity_type: str | None = None,
40
+ entity_id: str | None = None,
41
+ org_id: str | None = None,
42
+ request_id: str | None = None,
43
+ ) -> list[LineageEvent]:
44
+ """Retrieve lineage events, optionally filtered.
45
+
46
+ Args:
47
+ entity_type (str | None): Filter to events for this entity type. If
48
+ None, no entity_type filter is applied.
49
+ entity_id (str | None): Filter to events for this entity id. If None,
50
+ no entity_id filter is applied.
51
+ org_id (str | None): Filter to events for this org. If None, no
52
+ org_id filter is applied.
53
+ request_id (str | None): Filter to events for this request id. If
54
+ None, no request_id filter is applied.
55
+
56
+ Returns:
57
+ list[LineageEvent]: Matching events ordered by ``event_id`` ascending.
58
+
59
+ Note:
60
+ Enterprise/Supabase overrides must also apply the ``request_id`` filter
61
+ to maintain contract parity with the SQLite implementation (B3b T3).
62
+ """
63
+ raise NotImplementedError
64
+
65
+ @abstractmethod
66
+ def merge_records(
67
+ self,
68
+ *,
69
+ entity_type: EntityType,
70
+ survivor_id: str,
71
+ source_ids: list[str],
72
+ context: LineageContext,
73
+ ) -> None:
74
+ """Soft-delete each source into the survivor in one atomic transaction.
75
+
76
+ Sets ``status=MERGED`` and ``merged_into=survivor_id`` on each source
77
+ whose status is not already a tombstone (MERGED or SUPERSEDED). Appends
78
+ a single ``merge`` lineage event keyed on ``survivor_id``. Idempotent —
79
+ re-running on already-tombstoned sources is a no-op.
80
+
81
+ Args:
82
+ entity_type (str): One of ``"user_playbook"``, ``"agent_playbook"``,
83
+ or ``"profile"``.
84
+ survivor_id (str): The id of the record that survives the merge.
85
+ source_ids (list[str]): Ids of records to tombstone as merged.
86
+ context (LineageContext): Caller-supplied intent (actor, reason, etc.).
87
+
88
+ Raises:
89
+ ValueError: If ``context.request_id`` is empty or whitespace-only.
90
+ """
91
+ raise NotImplementedError
92
+
93
+ @abstractmethod
94
+ def supersede_record(
95
+ self,
96
+ *,
97
+ entity_type: EntityType,
98
+ incumbent_id: str,
99
+ successor_id: str,
100
+ context: LineageContext,
101
+ ) -> bool:
102
+ """Atomically replace the incumbent with the successor if incumbent is CURRENT.
103
+
104
+ Sets ``status=SUPERSEDED`` and ``superseded_by=successor_id`` on the
105
+ incumbent **only** when its ``status IS NULL`` (CURRENT). Appends a
106
+ ``revise`` lineage event keyed on ``successor_id`` when the guard
107
+ succeeds. Returns ``False`` without mutating anything when the incumbent
108
+ is not CURRENT (its status is already set).
109
+
110
+ Args:
111
+ entity_type (str): One of ``"user_playbook"``, ``"agent_playbook"``,
112
+ or ``"profile"``.
113
+ incumbent_id (str): The id of the record to supersede.
114
+ successor_id (str): The id of the record that replaces the incumbent.
115
+ context (LineageContext): Caller-supplied intent (actor, reason, etc.).
116
+
117
+ Returns:
118
+ bool: ``True`` if the incumbent was CURRENT and was superseded;
119
+ ``False`` if the incumbent was not CURRENT and no mutation occurred.
120
+
121
+ Raises:
122
+ ValueError: If ``context.request_id`` is empty or whitespace-only.
123
+ """
124
+ raise NotImplementedError
125
+
126
+ @abstractmethod
127
+ def has_inbound_lineage_refs(
128
+ self, *, entity_type: EntityType, entity_id: str
129
+ ) -> bool:
130
+ """Return True if any row points at ``entity_id`` via merged_into/superseded_by.
131
+
132
+ Org-scoped but deliberately NOT user_id-scoped: a cross-user chain
133
+ (one user's tombstone pointing at another user's survivor) must be
134
+ detected so the survivor is purged, not hard-deleted, on erasure.
135
+
136
+ Args:
137
+ entity_type (EntityType): One of ``"user_playbook"``, ``"agent_playbook"``,
138
+ or ``"profile"``.
139
+ entity_id (str): The entity's primary key to check for inbound refs.
140
+
141
+ Returns:
142
+ bool: True if any row in the entity's table has ``merged_into == entity_id``
143
+ OR ``superseded_by == entity_id``; False otherwise.
144
+ """
145
+ raise NotImplementedError
146
+
147
+ @abstractmethod
148
+ def purge_content(self, *, entity_type: EntityType, entity_id: str) -> bool:
149
+ """Blank a record's PII body, keep its lineage skeleton, emit op=purge.
150
+
151
+ Keeps only ``{pk, status, merged_into, superseded_by, retired_at}`` and
152
+ non-PII bookkeeping columns (timestamps, TTL); blanks every other column
153
+ (text → ``''``, nullable → ``NULL``; ``user_id`` → ``''``). Irreversible.
154
+ Emits a single ``op=purge`` lineage event with a deterministic
155
+ ``request_id="purge_{entity_id}"`` so repeated calls are idempotent —
156
+ an already-blank row re-runs without recording a duplicate event.
157
+ Returns ``True`` if a row existed (whether or not new blanking was done).
158
+
159
+ Args:
160
+ entity_type (EntityType): One of ``"user_playbook"`` or ``"profile"``.
161
+ ``"agent_playbook"`` is **not** supported (agent playbooks have no
162
+ ``user_id`` and are out of scope for content purge) — implementations
163
+ must raise ``ValueError`` for that value.
164
+ entity_id (str): The entity's primary key as a string.
165
+
166
+ Returns:
167
+ bool: ``True`` if the row exists; ``False`` if the id had no matching row.
168
+
169
+ Raises:
170
+ ValueError: If ``entity_type`` is not a recognized entity type or is
171
+ ``"agent_playbook"``.
172
+ """
173
+ raise NotImplementedError
174
+
175
+ @abstractmethod
176
+ def gc_expired_tombstones(
177
+ self, *, entity_type: str, older_than_epoch: int, limit: int = 1000
178
+ ) -> int:
179
+ """Hard-delete tombstone rows that are older than the given epoch cutoff.
180
+
181
+ Emits one ``hard_delete`` lineage event per deleted row before deleting it,
182
+ all within a single atomic transaction. Rows on legal hold are skipped.
183
+
184
+ Args:
185
+ entity_type (str): One of ``"user_playbook"``, ``"agent_playbook"``,
186
+ or ``"profile"``.
187
+ older_than_epoch (int): Unix timestamp. Rows whose age column value
188
+ is strictly less than this cutoff are eligible.
189
+ limit (int): Maximum number of rows to delete in one call. Defaults
190
+ to 1000.
191
+
192
+ Returns:
193
+ int: The number of rows physically deleted.
194
+
195
+ Raises:
196
+ ValueError: If ``entity_type`` is not a recognized entity type.
197
+ """
198
+ raise NotImplementedError
199
+
200
+ def list_org_ids(self) -> list[str]:
201
+ """Return every distinct org_id known to this storage instance.
202
+
203
+ Used by :class:`LineageGCScheduler` to enumerate all tenants so GC
204
+ runs for every org, not just the bootstrap org.
205
+
206
+ Returns:
207
+ list[str]: Distinct org ids, order unspecified.
208
+
209
+ Raises:
210
+ NotImplementedError: If the backend has not yet implemented this
211
+ method (enterprise backends owe this in B2 Task 6).
212
+ """
213
+ raise NotImplementedError(
214
+ f"{type(self).__name__} does not implement list_org_ids"
215
+ )
@@ -168,3 +168,23 @@ class OperationMixin:
168
168
  - 'state' (dict): The current operation state after the operation
169
169
  """
170
170
  raise NotImplementedError
171
+
172
+ @abstractmethod
173
+ def clear_in_progress_lock_if_owner(
174
+ self,
175
+ state_key: str,
176
+ request_id: str,
177
+ cleared_state: dict,
178
+ ) -> bool:
179
+ """Atomically clear an in-progress lock only if ``request_id`` still owns it.
180
+
181
+ Args:
182
+ state_key: Operation-state row key for the lock.
183
+ request_id: Request attempting to release the lock.
184
+ cleared_state: Replacement operation-state payload to write on success.
185
+
186
+ Returns:
187
+ bool: ``True`` when the caller still owned the lock and the clear
188
+ was applied, else ``False``.
189
+ """
190
+ raise NotImplementedError
@@ -1,4 +1,6 @@
1
+ import logging
1
2
  from abc import abstractmethod
3
+ from collections.abc import Sequence
2
4
 
3
5
  from reflexio.models.api_schema.common import BlockingIssue
4
6
  from reflexio.models.api_schema.domain import (
@@ -13,11 +15,22 @@ from reflexio.models.api_schema.domain import (
13
15
  Status,
14
16
  UserPlaybook,
15
17
  )
18
+ from reflexio.models.api_schema.domain.entities import LineageEvent
16
19
  from reflexio.models.api_schema.retriever_schema import (
17
20
  SearchAgentPlaybookRequest,
18
21
  SearchUserPlaybookRequest,
19
22
  )
20
23
  from reflexio.models.config_schema import SearchOptions
24
+ from reflexio.server.tracing import capture_anomaly
25
+
26
+ logger = logging.getLogger(__name__)
27
+
28
+ _AGGREGATE_EVENT_EMIT_ATTEMPTS = 3
29
+
30
+ # Shared prefix for aggregate lineage event reasons.
31
+ # Consumers: storage_base (here), sqlite_storage/_playbook.py, and lib/_agent_playbook.py
32
+ # (which imports this constant to keep the parser and producers in sync).
33
+ AGGREGATE_REASON_PREFIX = "aggregate:"
21
34
 
22
35
 
23
36
  class PlaybookMixin:
@@ -42,6 +55,7 @@ class PlaybookMixin:
42
55
  start_time: int | None = None,
43
56
  end_time: int | None = None,
44
57
  include_embedding: bool = False,
58
+ tags: list[str] | None = None,
45
59
  ) -> list[UserPlaybook]:
46
60
  """Get user playbooks from storage.
47
61
 
@@ -56,6 +70,7 @@ class PlaybookMixin:
56
70
  start_time (int, optional): Unix timestamp. Only return playbooks created at or after this time.
57
71
  end_time (int, optional): Unix timestamp. Only return playbooks created at or before this time.
58
72
  include_embedding (bool): If True, fetch and parse embedding vectors. Defaults to False.
73
+ tags (list[str], optional): Match playbooks having any of these tags.
59
74
 
60
75
  Returns:
61
76
  list[UserPlaybook]: List of user playbook objects
@@ -166,11 +181,17 @@ class PlaybookMixin:
166
181
  raise NotImplementedError
167
182
 
168
183
  @abstractmethod
169
- def delete_user_playbooks_by_ids(self, user_playbook_ids: list[int]) -> int:
184
+ def delete_user_playbooks_by_ids(
185
+ self, user_playbook_ids: list[int], *, emit_hard_delete: bool = True
186
+ ) -> int:
170
187
  """Delete user playbooks by their IDs.
171
188
 
172
189
  Args:
173
190
  user_playbook_ids: List of user_playbook_id values to delete
191
+ emit_hard_delete: When True (default), append a ``hard_delete``
192
+ lineage event per id (genuine erasure). Set False for rollback
193
+ cleanup of a never-live row (e.g. a lost supersede CAS), so no
194
+ spurious audit event is recorded.
174
195
 
175
196
  Returns:
176
197
  int: Number of user playbooks deleted
@@ -206,8 +227,19 @@ class PlaybookMixin:
206
227
  raise NotImplementedError
207
228
 
208
229
  @abstractmethod
209
- def get_user_playbook_by_id(self, user_playbook_id: int) -> UserPlaybook | None:
210
- """Fetch one user playbook by id, regardless of owner."""
230
+ def get_user_playbook_by_id(
231
+ self, user_playbook_id: int, *, include_tombstones: bool = False
232
+ ) -> UserPlaybook | None:
233
+ """Fetch one user playbook by primary key.
234
+
235
+ Args:
236
+ user_playbook_id: The user_playbook_id to look up.
237
+ include_tombstones: When False (default), MERGED/SUPERSEDED rows
238
+ return None. Set to True for lineage resolution (resolve_current).
239
+
240
+ Returns:
241
+ The UserPlaybook if found and not filtered, otherwise None.
242
+ """
211
243
  raise NotImplementedError
212
244
 
213
245
  @abstractmethod
@@ -274,6 +306,77 @@ class PlaybookMixin:
274
306
  """
275
307
  raise NotImplementedError
276
308
 
309
+ def save_agent_playbook_with_aggregate_event(
310
+ self,
311
+ agent_playbook: AgentPlaybook,
312
+ *,
313
+ source_ids: list[str],
314
+ request_id: str,
315
+ run_mode: str,
316
+ ) -> AgentPlaybook:
317
+ """Persist an agent playbook AND its ``op=aggregate`` lineage event.
318
+
319
+ Backends SHOULD override this so the row insert and the event commit in ONE
320
+ transaction (the event is the sole record of the run->playbook membership for
321
+ reconstruction). This base default is a non-atomic save-then-emit fallback
322
+ with bounded retry + loud (level=error) on final failure.
323
+
324
+ Args:
325
+ agent_playbook (AgentPlaybook): The playbook to persist.
326
+ source_ids (list[str]): IDs of the source entities that produced this playbook.
327
+ request_id (str): The aggregation run ID (used as the lineage event request_id).
328
+ run_mode (str): The aggregation run mode (e.g. ``full_archive`` or ``incremental``).
329
+
330
+ Returns:
331
+ AgentPlaybook: The saved playbook with ``agent_playbook_id`` populated.
332
+
333
+ Raises:
334
+ ValueError: If ``request_id`` is empty (would produce an unreconstructable event).
335
+ """
336
+ if not request_id or not request_id.strip():
337
+ raise ValueError(
338
+ "save_agent_playbook_with_aggregate_event requires a non-empty request_id"
339
+ )
340
+ saved = self.save_agent_playbooks([agent_playbook])[0]
341
+ event = LineageEvent(
342
+ org_id=self.org_id, # type: ignore[attr-defined]
343
+ entity_type="agent_playbook",
344
+ entity_id=str(saved.agent_playbook_id),
345
+ op="aggregate",
346
+ prov_relation="wasDerivedFrom",
347
+ source_ids=source_ids,
348
+ actor="aggregator",
349
+ request_id=request_id,
350
+ reason=f"{AGGREGATE_REASON_PREFIX}{run_mode}",
351
+ )
352
+ # The row is already committed; this default is non-atomic (SQLite overrides it to
353
+ # make the INSERT + event one transaction). The event is the sole reconstruction signal
354
+ # for the run, so make the emit durable: bounded retry (idempotent on retrying the
355
+ # same row's emit — entity_id is a fresh autoincrement per run, so this is NOT
356
+ # cross-run idempotency), and on final failure fail LOUD at level=error so the gap
357
+ # is paged + backfillable rather than silently lost. Never raise — the playbook
358
+ # itself is saved and must not be lost.
359
+ for attempt in range(_AGGREGATE_EVENT_EMIT_ATTEMPTS):
360
+ try:
361
+ self.append_lineage_event(event) # type: ignore[attr-defined]
362
+ return saved
363
+ except Exception: # noqa: BLE001
364
+ logger.warning(
365
+ "aggregate lineage event append failed (attempt %d/%d) for agent_playbook %s",
366
+ attempt + 1,
367
+ _AGGREGATE_EVENT_EMIT_ATTEMPTS,
368
+ saved.agent_playbook_id,
369
+ exc_info=True,
370
+ )
371
+ capture_anomaly(
372
+ "lineage.aggregate.append_failed",
373
+ level="error",
374
+ entity_id=str(saved.agent_playbook_id),
375
+ org_id=self.org_id, # type: ignore[attr-defined]
376
+ request_id=request_id,
377
+ )
378
+ return saved
379
+
277
380
  @abstractmethod
278
381
  def get_agent_playbooks(
279
382
  self,
@@ -282,6 +385,7 @@ class PlaybookMixin:
282
385
  agent_version: str | None = None,
283
386
  status_filter: list[Status | None] | None = None,
284
387
  playbook_status_filter: list[PlaybookStatus] | None = None,
388
+ tags: list[str] | None = None,
285
389
  ) -> list[AgentPlaybook]:
286
390
  """Get agent playbooks from storage.
287
391
 
@@ -292,6 +396,7 @@ class PlaybookMixin:
292
396
  status_filter (list[Optional[Status]], optional): List of Status values to filter by. None in the list means CURRENT status.
293
397
  playbook_status_filter (Optional[list[PlaybookStatus]]): List of PlaybookStatus values to filter by.
294
398
  If None, returns all playbook statuses.
399
+ tags (list[str], optional): Match playbooks having any of these tags.
295
400
 
296
401
  Returns:
297
402
  list[AgentPlaybook]: List of agent playbook objects
@@ -299,8 +404,19 @@ class PlaybookMixin:
299
404
  raise NotImplementedError
300
405
 
301
406
  @abstractmethod
302
- def get_agent_playbook_by_id(self, agent_playbook_id: int) -> AgentPlaybook | None:
303
- """Fetch one agent playbook by id."""
407
+ def get_agent_playbook_by_id(
408
+ self, agent_playbook_id: int, *, include_tombstones: bool = False
409
+ ) -> AgentPlaybook | None:
410
+ """Fetch one agent playbook by primary key.
411
+
412
+ Args:
413
+ agent_playbook_id: The agent_playbook_id to look up.
414
+ include_tombstones: When False (default), MERGED/SUPERSEDED rows
415
+ return None. Set to True for lineage resolution (resolve_current).
416
+
417
+ Returns:
418
+ The AgentPlaybook if found and not filtered, otherwise None.
419
+ """
304
420
  raise NotImplementedError
305
421
 
306
422
  @abstractmethod
@@ -354,6 +470,7 @@ class PlaybookMixin:
354
470
  rationale: str | None = None,
355
471
  blocking_issue: BlockingIssue | None = None,
356
472
  playbook_status: PlaybookStatus | None = None,
473
+ tags: list[str] | None = None,
357
474
  ) -> None:
358
475
  """Update editable fields of an agent playbook. Only non-None fields are updated.
359
476
 
@@ -365,6 +482,7 @@ class PlaybookMixin:
365
482
  rationale (str, optional): New rationale text
366
483
  blocking_issue (BlockingIssue, optional): New blocking issue
367
484
  playbook_status (PlaybookStatus, optional): New playbook status
485
+ tags (list[str], optional): Replacement tags
368
486
 
369
487
  Raises:
370
488
  ValueError: If agent playbook with the given ID is not found
@@ -380,6 +498,7 @@ class PlaybookMixin:
380
498
  trigger: str | None = None,
381
499
  rationale: str | None = None,
382
500
  blocking_issue: BlockingIssue | None = None,
501
+ tags: list[str] | None = None,
383
502
  ) -> None:
384
503
  """Update editable fields of a user playbook. Only non-None fields are updated.
385
504
 
@@ -390,12 +509,34 @@ class PlaybookMixin:
390
509
  trigger (str, optional): New trigger text
391
510
  rationale (str, optional): New rationale text
392
511
  blocking_issue (BlockingIssue, optional): New blocking issue
512
+ tags (list[str], optional): Replacement tags
393
513
 
394
514
  Raises:
395
515
  ValueError: If user playbook with the given ID is not found
396
516
  """
397
517
  raise NotImplementedError
398
518
 
519
+ @abstractmethod
520
+ def supersede_user_playbooks_by_ids(
521
+ self, user_playbook_ids: list[int], request_id: str
522
+ ) -> int:
523
+ """Soft-delete user playbooks by setting status to SUPERSEDED.
524
+
525
+ Eligible rows (CURRENT, PENDING, or ARCHIVED; not already MERGED /
526
+ SUPERSEDED) are transitioned to SUPERSEDED and emit one status_change
527
+ lineage event under the shared request id. This is the user-playbook
528
+ analogue of the existing agent/profile soft-supersede helpers and
529
+ preserves dead-source content for point-in-time attribution reads.
530
+
531
+ Args:
532
+ user_playbook_ids (list[int]): User playbook ids to supersede.
533
+ request_id (str): Shared request id for all emitted lineage events.
534
+
535
+ Returns:
536
+ int: Number of user playbooks actually updated.
537
+ """
538
+ raise NotImplementedError
539
+
399
540
  @abstractmethod
400
541
  def archive_agent_playbooks_by_playbook_name(
401
542
  self, playbook_name: str, agent_version: str | None = None
@@ -419,6 +560,47 @@ class PlaybookMixin:
419
560
  """
420
561
  raise NotImplementedError
421
562
 
563
+ @abstractmethod
564
+ def supersede_agent_playbooks_by_ids(
565
+ self, agent_playbook_ids: list[int], request_id: str
566
+ ) -> int:
567
+ """Soft-delete agent playbooks by setting status to SUPERSEDED, emitting set-based lineage.
568
+
569
+ For each eligible id (not APPROVED, not already tombstoned), updates status to
570
+ SUPERSEDED and emits one status_change event under the shared request_id.
571
+ Atomic: mutation and event in one commit, guarded on rowcount.
572
+ FTS/vec rows are NOT removed.
573
+
574
+ Args:
575
+ agent_playbook_ids (list[int]): Agent playbook ids to supersede.
576
+ request_id (str): Shared request id for all emitted lineage events.
577
+
578
+ Returns:
579
+ int: Number of agent playbooks actually updated.
580
+ """
581
+ raise NotImplementedError
582
+
583
+ @abstractmethod
584
+ def supersede_agent_playbooks_by_playbook_name(
585
+ self, playbook_name: str, agent_version: str | None, request_id: str
586
+ ) -> int:
587
+ """Soft-delete archived agent playbooks by name/version via SUPERSEDED status.
588
+
589
+ Selects rows with playbook_name matching and status='archived', then
590
+ soft-supersedes each one with a status_change lineage event under request_id.
591
+ Atomic: one commit at the end.
592
+ FTS/vec rows are NOT removed.
593
+
594
+ Args:
595
+ playbook_name (str): Playbook name to supersede.
596
+ agent_version (str | None): Agent version filter. None matches all versions.
597
+ request_id (str): Shared request id for all emitted lineage events.
598
+
599
+ Returns:
600
+ int: Number of agent playbooks actually updated.
601
+ """
602
+ raise NotImplementedError
603
+
422
604
  @abstractmethod
423
605
  def restore_archived_agent_playbooks_by_playbook_name(
424
606
  self, playbook_name: str, agent_version: str | None = None
@@ -461,6 +643,13 @@ class PlaybookMixin:
461
643
  """Return source user playbook ids for an agent playbook."""
462
644
  raise NotImplementedError
463
645
 
646
+ @abstractmethod
647
+ def get_source_user_playbook_ids_for_agent_playbooks(
648
+ self, agent_playbook_ids: Sequence[int]
649
+ ) -> dict[int, list[int]]:
650
+ """Return source user playbook ids keyed by agent playbook id."""
651
+ raise NotImplementedError
652
+
464
653
  @abstractmethod
465
654
  def set_source_windows_for_agent_playbook(
466
655
  self,
@@ -557,12 +746,18 @@ class PlaybookMixin:
557
746
  raise NotImplementedError
558
747
 
559
748
  @abstractmethod
560
- def delete_agent_playbooks_by_ids(self, agent_playbook_ids: list[int]) -> None:
749
+ def delete_agent_playbooks_by_ids(
750
+ self, agent_playbook_ids: list[int], *, emit_hard_delete: bool = True
751
+ ) -> None:
561
752
  """Permanently delete agent playbooks by their IDs.
562
753
  No-op if agent_playbook_ids is empty.
563
754
 
564
755
  Args:
565
756
  agent_playbook_ids (list[int]): List of agent playbook IDs to delete
757
+ emit_hard_delete: When True (default), append a ``hard_delete``
758
+ lineage event per id (genuine erasure). Set False for rollback
759
+ cleanup of a never-live row (e.g. a lost supersede CAS), so no
760
+ spurious audit event is recorded.
566
761
  """
567
762
  raise NotImplementedError
568
763
 
@@ -634,6 +829,45 @@ class PlaybookMixin:
634
829
  """
635
830
  raise NotImplementedError
636
831
 
832
+ def get_agent_success_evaluation_results_in_window(
833
+ self,
834
+ from_ts: int,
835
+ to_ts: int,
836
+ agent_version: str | None = None,
837
+ limit: int | None = None,
838
+ ) -> list[AgentSuccessEvaluationResult]:
839
+ """Return eval results in ``[from_ts, to_ts]``.
840
+
841
+ Default implementation filters the existing latest-results method.
842
+ SQL backends should override so callers do not depend on an arbitrary
843
+ latest-row cap.
844
+ """
845
+ rows = self.get_agent_success_evaluation_results(
846
+ limit=limit or 10_000,
847
+ agent_version=agent_version,
848
+ )
849
+ return [r for r in rows if from_ts <= r.created_at <= to_ts]
850
+
851
+ def get_agent_success_evaluation_result_ids(
852
+ self,
853
+ user_id: str,
854
+ session_id: str,
855
+ evaluation_name: str,
856
+ agent_version: str,
857
+ ) -> list[int]:
858
+ """Return result ids for one eval identity tuple."""
859
+ rows = self.get_agent_success_evaluation_results(
860
+ limit=10_000,
861
+ agent_version=agent_version,
862
+ )
863
+ return [
864
+ r.result_id
865
+ for r in rows
866
+ if r.user_id == user_id
867
+ and r.session_id == session_id
868
+ and r.evaluation_name == evaluation_name
869
+ ]
870
+
637
871
  @abstractmethod
638
872
  def delete_all_agent_success_evaluation_results(self) -> None:
639
873
  """Delete all agent success evaluation results from storage."""
@@ -642,13 +876,15 @@ class PlaybookMixin:
642
876
  @abstractmethod
643
877
  def delete_agent_success_evaluation_results_for_session(
644
878
  self,
879
+ user_id: str,
645
880
  session_id: str,
646
881
  evaluation_name: str,
647
882
  agent_version: str,
648
883
  ) -> int:
649
- """Delete stored results for (session_id, evaluation_name, agent_version).
884
+ """Delete stored results for (user_id, session_id, evaluation_name, agent_version).
650
885
 
651
886
  Args:
887
+ user_id (str): User whose session results to clear.
652
888
  session_id (str): Session whose results to clear.
653
889
  evaluation_name (str): Which evaluator's results to clear.
654
890
  agent_version (str): Agent version scope.