claude-smart 0.2.48 → 0.2.50

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 (193) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/README.md +11 -40
  3. package/bin/claude-smart.js +393 -55
  4. package/package.json +3 -2
  5. package/plugin/.claude-plugin/plugin.json +1 -1
  6. package/plugin/.codex-plugin/plugin.json +1 -1
  7. package/plugin/.coverage +0 -0
  8. package/plugin/README.md +4 -3
  9. package/plugin/dashboard/app/api/reflexio/[...path]/route.ts +4 -2
  10. package/plugin/dashboard/app/dashboard/page.tsx +6 -1
  11. package/plugin/dashboard/app/preferences/[id]/page.tsx +18 -6
  12. package/plugin/dashboard/app/preferences/page.tsx +32 -35
  13. package/plugin/dashboard/app/sessions/[sessionId]/page.tsx +16 -1
  14. package/plugin/dashboard/app/sessions/page.tsx +2 -0
  15. package/plugin/dashboard/app/skills/page.tsx +65 -50
  16. package/plugin/dashboard/app/skills/project/[id]/page.tsx +17 -8
  17. package/plugin/dashboard/app/skills/shared/[id]/page.tsx +1 -6
  18. package/plugin/dashboard/components/common/host-badge.tsx +118 -0
  19. package/plugin/dashboard/components/common/learning-application-badge.tsx +34 -0
  20. package/plugin/dashboard/components/common/learnings-badge.tsx +1 -1
  21. package/plugin/dashboard/components/common/page-header.tsx +3 -3
  22. package/plugin/dashboard/lib/config-file.ts +5 -1
  23. package/plugin/dashboard/lib/host-attribution.ts +62 -0
  24. package/plugin/dashboard/lib/session-reader.ts +40 -2
  25. package/plugin/dashboard/lib/types.ts +7 -1
  26. package/plugin/opencode/dist/server.mjs +60 -2
  27. package/plugin/opencode/server.mts +64 -2
  28. package/plugin/pyproject.toml +2 -2
  29. package/plugin/scripts/_lib.sh +255 -7
  30. package/plugin/scripts/backend-python-runner.py +46 -0
  31. package/plugin/scripts/backend-service.sh +766 -123
  32. package/plugin/scripts/codex-hook.js +79 -229
  33. package/plugin/scripts/dashboard-open.sh +6 -4
  34. package/plugin/scripts/dashboard-service.sh +118 -137
  35. package/plugin/scripts/ensure-plugin-root.sh +106 -8
  36. package/plugin/scripts/hook_entry.sh +3 -0
  37. package/plugin/scripts/opencode-claude-compat.js +8 -1
  38. package/plugin/scripts/smart-install.sh +116 -21
  39. package/plugin/src/README.md +1 -1
  40. package/plugin/src/claude_smart/cli.py +93 -29
  41. package/plugin/src/claude_smart/context_inject.py +3 -0
  42. package/plugin/src/claude_smart/env_config.py +56 -11
  43. package/plugin/src/claude_smart/events/post_tool.py +2 -1
  44. package/plugin/src/claude_smart/events/session_end.py +2 -1
  45. package/plugin/src/claude_smart/events/stop.py +3 -0
  46. package/plugin/src/claude_smart/events/user_prompt.py +2 -1
  47. package/plugin/src/claude_smart/internal_call.py +5 -2
  48. package/plugin/src/claude_smart/optimizer_assistant.py +59 -13
  49. package/plugin/src/claude_smart/publish.py +59 -7
  50. package/plugin/src/claude_smart/reflexio_adapter.py +137 -14
  51. package/plugin/src/claude_smart/runtime.py +15 -6
  52. package/plugin/src/claude_smart/state.py +211 -52
  53. package/plugin/uv.lock +5 -5
  54. package/plugin/vendor/reflexio/.env.example +13 -0
  55. package/plugin/vendor/reflexio/reflexio/README.md +7 -3
  56. package/plugin/vendor/reflexio/reflexio/__init__.py +12 -0
  57. package/plugin/vendor/reflexio/reflexio/cli/README.md +3 -0
  58. package/plugin/vendor/reflexio/reflexio/client/client.py +166 -9
  59. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/state.py +10 -3
  60. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_state.py +28 -0
  61. package/plugin/vendor/reflexio/reflexio/lib/_search.py +41 -0
  62. package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/entities.py +195 -25
  63. package/plugin/vendor/reflexio/reflexio/models/api_schema/eval_overview_schema.py +7 -1
  64. package/plugin/vendor/reflexio/reflexio/models/api_schema/internal_schema.py +2 -1
  65. package/plugin/vendor/reflexio/reflexio/models/api_schema/retriever_schema.py +63 -4
  66. package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/converters.py +1 -0
  67. package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/entities.py +8 -1
  68. package/plugin/vendor/reflexio/reflexio/models/config_schema.py +1 -1
  69. package/plugin/vendor/reflexio/reflexio/server/README.md +22 -4
  70. package/plugin/vendor/reflexio/reflexio/server/__init__.py +18 -8
  71. package/plugin/vendor/reflexio/reflexio/server/__main__.py +6 -0
  72. package/plugin/vendor/reflexio/reflexio/server/api.py +79 -5
  73. package/plugin/vendor/reflexio/reflexio/server/billing_meter.py +263 -3
  74. package/plugin/vendor/reflexio/reflexio/server/callback_executor.py +164 -0
  75. package/plugin/vendor/reflexio/reflexio/server/llm/_litellm_embedding.py +81 -81
  76. package/plugin/vendor/reflexio/reflexio/server/llm/_litellm_subprocess.py +28 -0
  77. package/plugin/vendor/reflexio/reflexio/server/llm/_litellm_text_generation.py +63 -6
  78. package/plugin/vendor/reflexio/reflexio/server/llm/embedding_service.py +19 -52
  79. package/plugin/vendor/reflexio/reflexio/server/llm/litellm_client.py +1 -0
  80. package/plugin/vendor/reflexio/reflexio/server/llm/model_defaults.py +28 -0
  81. package/plugin/vendor/reflexio/reflexio/server/llm/providers/claude_code_provider.py +9 -1
  82. package/plugin/vendor/reflexio/reflexio/server/llm/providers/embedder_warmup.py +329 -0
  83. package/plugin/vendor/reflexio/reflexio/server/llm/providers/embedding_service_provider.py +85 -10
  84. package/plugin/vendor/reflexio/reflexio/server/llm/providers/local_embedding_provider.py +199 -2
  85. package/plugin/vendor/reflexio/reflexio/server/llm/providers/nomic_embedding_provider.py +77 -9
  86. package/plugin/vendor/reflexio/reflexio/server/org_fanout.py +184 -0
  87. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/document_expansion/v1.0.0.prompt.md +1 -1
  88. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/document_expansion/v1.1.0.prompt.md +32 -0
  89. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.3.3.prompt.md +1 -1
  90. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.4.0.prompt.md +63 -0
  91. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/query_reformulation/v1.0.0.prompt.md +1 -1
  92. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/query_reformulation/v2.0.0.prompt.md +30 -0
  93. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/retrieved_learning_impact/v1.0.0.prompt.md +51 -0
  94. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/retrieved_learning_relevance/v1.0.0.prompt.md +39 -0
  95. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/shadow_comparison/v1.0.0.prompt.md +1 -1
  96. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/shadow_comparison/v1.1.0.prompt.md +43 -0
  97. package/plugin/vendor/reflexio/reflexio/server/routes/config.py +3 -3
  98. package/plugin/vendor/reflexio/reflexio/server/routes/evaluation.py +122 -28
  99. package/plugin/vendor/reflexio/reflexio/server/routes/interactions.py +63 -2
  100. package/plugin/vendor/reflexio/reflexio/server/routes/system.py +22 -3
  101. package/plugin/vendor/reflexio/reflexio/server/scheduling.py +64 -3
  102. package/plugin/vendor/reflexio/reflexio/server/services/README.md +6 -4
  103. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/README.md +3 -2
  104. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/_eval_health.py +41 -0
  105. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/components/retrieved_learning_evaluator.py +554 -0
  106. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/regen_jobs.py +35 -1
  107. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/runner.py +253 -101
  108. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/scheduler.py +5 -7
  109. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/service.py +27 -0
  110. package/plugin/vendor/reflexio/reflexio/server/services/base_generation/_extraction_lifecycle.py +11 -5
  111. package/plugin/vendor/reflexio/reflexio/server/services/base_generation/_should_run.py +4 -4
  112. package/plugin/vendor/reflexio/reflexio/server/services/base_generation/_usage_billing.py +6 -2
  113. package/plugin/vendor/reflexio/reflexio/server/services/base_generation_service.py +255 -74
  114. package/plugin/vendor/reflexio/reflexio/server/services/deduplication_utils.py +72 -0
  115. package/plugin/vendor/reflexio/reflexio/server/services/deferred_learning_plan.py +270 -0
  116. package/plugin/vendor/reflexio/reflexio/server/services/durable_learning/__init__.py +13 -0
  117. package/plugin/vendor/reflexio/reflexio/server/services/durable_learning/scheduler.py +142 -0
  118. package/plugin/vendor/reflexio/reflexio/server/services/durable_learning/worker.py +262 -0
  119. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/components/hero_state.py +2 -11
  120. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/components/rule_attribution.py +6 -2
  121. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/service.py +17 -13
  122. package/plugin/vendor/reflexio/reflexio/server/services/extraction/agent_run_records.py +18 -3
  123. package/plugin/vendor/reflexio/reflexio/server/services/extraction/outcome.py +12 -1
  124. package/plugin/vendor/reflexio/reflexio/server/services/extraction/prior_answer_search.py +1 -1
  125. package/plugin/vendor/reflexio/reflexio/server/services/extraction/resumable_agent.py +2 -1
  126. package/plugin/vendor/reflexio/reflexio/server/services/extraction/resume_scheduler.py +1 -3
  127. package/plugin/vendor/reflexio/reflexio/server/services/extraction/resume_worker.py +66 -0
  128. package/plugin/vendor/reflexio/reflexio/server/services/generation_service.py +640 -80
  129. package/plugin/vendor/reflexio/reflexio/server/services/governance/service.py +2 -0
  130. package/plugin/vendor/reflexio/reflexio/server/services/lineage/gc_scheduler.py +179 -99
  131. package/plugin/vendor/reflexio/reflexio/server/services/lineage/vector_backfill_sweep.py +139 -0
  132. package/plugin/vendor/reflexio/reflexio/server/services/operation_state_utils.py +66 -27
  133. package/plugin/vendor/reflexio/reflexio/server/services/playbook/aggregation_trigger.py +177 -0
  134. package/plugin/vendor/reflexio/reflexio/server/services/playbook/components/aggregator.py +68 -2
  135. package/plugin/vendor/reflexio/reflexio/server/services/playbook/components/consolidator.py +360 -49
  136. package/plugin/vendor/reflexio/reflexio/server/services/playbook/components/extractor.py +27 -30
  137. package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_edit_apply.py +8 -1
  138. package/plugin/vendor/reflexio/reflexio/server/services/playbook/service.py +137 -69
  139. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/optimizer.py +20 -1
  140. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/scheduler.py +13 -6
  141. package/plugin/vendor/reflexio/reflexio/server/services/pre_retrieval/_query_reformulator.py +40 -25
  142. package/plugin/vendor/reflexio/reflexio/server/services/profile/components/consolidator.py +12 -39
  143. package/plugin/vendor/reflexio/reflexio/server/services/profile/components/extractor.py +30 -35
  144. package/plugin/vendor/reflexio/reflexio/server/services/profile/service.py +166 -66
  145. package/plugin/vendor/reflexio/reflexio/server/services/reflection/service.py +457 -107
  146. package/plugin/vendor/reflexio/reflexio/server/services/retrieval/session_dedup.py +127 -0
  147. package/plugin/vendor/reflexio/reflexio/server/services/retrieval/temporal.py +104 -0
  148. package/plugin/vendor/reflexio/reflexio/server/services/shadow_comparison/dispatcher.py +139 -0
  149. package/plugin/vendor/reflexio/reflexio/server/services/shadow_comparison/judge.py +16 -6
  150. package/plugin/vendor/reflexio/reflexio/server/services/shadow_comparison/worker.py +137 -0
  151. package/plugin/vendor/reflexio/reflexio/server/services/storage/governance_validation.py +12 -0
  152. package/plugin/vendor/reflexio/reflexio/server/services/storage/lifecycle_filters.py +54 -0
  153. package/plugin/vendor/reflexio/reflexio/server/services/storage/retention.py +41 -3
  154. package/plugin/vendor/reflexio/reflexio/server/services/storage/retention_mixin.py +40 -2
  155. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/__init__.py +2 -0
  156. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_base.py +190 -2
  157. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_extras.py +11 -4
  158. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_governance.py +28 -1
  159. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_learning_jobs.py +414 -0
  160. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_lineage.py +11 -5
  161. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_requests.py +8 -3
  162. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_shadow_verdicts.py +4 -0
  163. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/base/_deletion.py +16 -5
  164. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/base/_fts_vec.py +86 -2
  165. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/governance/_erase_execution.py +104 -42
  166. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/governance/_subject_barrier.py +7 -1
  167. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_agent.py +59 -7
  168. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_eval_results.py +430 -5
  169. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_source_linkage.py +8 -3
  170. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_user.py +57 -12
  171. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/profiles/_interaction_store.py +197 -12
  172. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/profiles/_profile_store.py +70 -11
  173. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/__init__.py +17 -0
  174. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_commit_scope.py +9 -0
  175. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_extras.py +9 -2
  176. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_learning_jobs.py +269 -0
  177. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_operations.py +7 -0
  178. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_retrieval_log.py +3 -1
  179. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_shadow_verdicts.py +4 -0
  180. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/agent_run/_models.py +12 -1
  181. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/evaluation_state_keys.py +78 -0
  182. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_agent.py +38 -0
  183. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_eval_results.py +171 -0
  184. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_user.py +52 -1
  185. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/profiles/_interaction_store.py +82 -1
  186. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/profiles/_profile_store.py +74 -2
  187. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/retrieved_learning_state.py +226 -0
  188. package/plugin/vendor/reflexio/reflexio/server/services/tagging/tagging_scheduler.py +5 -6
  189. package/plugin/vendor/reflexio/reflexio/server/services/unified_search_service.py +209 -29
  190. package/plugin/vendor/reflexio/reflexio/server/usage_metrics.py +3 -0
  191. package/plugin/vendor/reflexio/reflexio/test_support/llm_mock.py +61 -0
  192. package/plugin/vendor/reflexio/reflexio/test_support/llm_model_registry.py +33 -0
  193. package/plugin/vendor/reflexio/reflexio/server/services/search/__init__.py +0 -0
@@ -30,7 +30,6 @@ resolve through the composed MRO.
30
30
 
31
31
  from __future__ import annotations
32
32
 
33
- import json
34
33
  import sqlite3
35
34
  import threading
36
35
  from collections.abc import Callable
@@ -50,6 +49,14 @@ from reflexio.server.services.storage.governance_validation import (
50
49
  _successful_erase_identity,
51
50
  _validate_governance_purge_id,
52
51
  )
52
+ from reflexio.server.services.storage.storage_base.evaluation_state_keys import (
53
+ GRADE_ON_DEMAND_CACHE_PREFIX,
54
+ build_agent_success_marker_key,
55
+ build_grade_on_demand_session_prefix,
56
+ )
57
+ from reflexio.server.services.storage.storage_base.retrieved_learning_state import (
58
+ build_retrieved_learning_state_key,
59
+ )
53
60
 
54
61
  from .._governance import _row_to_audit_event, _row_to_purge_operation
55
62
 
@@ -157,13 +164,6 @@ class GovernanceEraseExecutionMixin:
157
164
  raise ValueError(
158
165
  "Current user playbooks no longer match prepared purge snapshot"
159
166
  )
160
- request_ids = [
161
- str(row["request_id"])
162
- for row in self.conn.execute(
163
- "SELECT request_id FROM requests WHERE user_id = ?",
164
- (user_id,),
165
- ).fetchall()
166
- ]
167
167
  profile_rows = self.conn.execute(
168
168
  "SELECT rowid, profile_id FROM profiles WHERE user_id = ?",
169
169
  (user_id,),
@@ -183,40 +183,12 @@ class GovernanceEraseExecutionMixin:
183
183
  )
184
184
  purge_upb_ids = [int(entity_id) for entity_id in purge_upb_str_ids]
185
185
  delete_upb_ids = [int(entity_id) for entity_id in delete_upb_str_ids]
186
- erased_entity_ids = [
187
- *request_ids,
188
- *[str(interaction_id) for interaction_id in interaction_ids],
189
- *all_profile_ids,
190
- *[str(user_playbook_id) for user_playbook_id in raw_upb_ids],
191
- ]
192
- if erased_entity_ids:
193
- erased_entity_id_set = set(erased_entity_ids)
194
- lineage_source_event_ids: list[int] = []
195
- for row in self.conn.execute(
196
- "SELECT event_id, source_ids FROM lineage_event WHERE org_id = ?",
197
- (self.org_id,),
198
- ).fetchall():
199
- try:
200
- source_ids = json.loads(str(row["source_ids"] or "[]"))
201
- except json.JSONDecodeError:
202
- source_ids = []
203
- if any(
204
- str(source_id) in erased_entity_id_set for source_id in source_ids
205
- ):
206
- lineage_source_event_ids.append(int(row["event_id"]))
207
- deps._delete_in_chunks(
208
- "lineage_event", "event_id", lineage_source_event_ids
209
- )
210
- placeholders = ",".join("?" for _ in erased_entity_ids)
211
- self.conn.execute(
212
- f"""DELETE FROM lineage_event
213
- WHERE org_id = ?
214
- AND (
215
- request_id IN ({placeholders})
216
- OR entity_id IN ({placeholders})
217
- )""", # noqa: S608
218
- [self.org_id, *erased_entity_ids, *erased_entity_ids],
219
- )
186
+ # SEC-016: retain the content-free lineage_event skeleton on erase to
187
+ # match Supabase (which never enumerates lineage_event for erasure).
188
+ # lineage_event has no PII/content column, no foreign keys, and no
189
+ # FTS/vec shadow tables, so leaving these rows in place is safe and
190
+ # preserves the audit/lineage skeleton. Content-bearing entities are
191
+ # still deleted/purged below.
220
192
  delete_profile_rowids = [
221
193
  profile_rowid_by_id[profile_id]
222
194
  for profile_id in delete_profile_ids
@@ -240,6 +212,23 @@ class GovernanceEraseExecutionMixin:
240
212
  WHERE user_id = ?""",
241
213
  (user_id,),
242
214
  )
215
+ rle_results_cur = self.conn.execute(
216
+ """DELETE FROM retrieved_learning_evaluation
217
+ WHERE user_id = ?""",
218
+ (user_id,),
219
+ )
220
+ # Snapshot the user's session ids BEFORE requests are deleted — the
221
+ # evaluation _operation_state namespaces are keyed by session.
222
+ session_ids = [
223
+ str(row["session_id"])
224
+ for row in self.conn.execute(
225
+ "SELECT DISTINCT session_id FROM requests WHERE user_id = ?",
226
+ (user_id,),
227
+ ).fetchall()
228
+ ]
229
+ eval_operation_states = self._delete_evaluation_operation_states_locked(
230
+ user_id, session_ids
231
+ )
243
232
  requests_cur = self.conn.execute(
244
233
  "DELETE FROM requests WHERE user_id = ?",
245
234
  (user_id,),
@@ -280,10 +269,75 @@ class GovernanceEraseExecutionMixin:
280
269
  "profiles": len(delete_profile_ids),
281
270
  "requests": requests_cur.rowcount,
282
271
  "agent_success_evaluation_results": eval_results_cur.rowcount,
272
+ # Enterprise-only tables absent from the OSS SQLite backend;
273
+ # reported as zero so the delete target matrix stays complete.
274
+ "offline_tuner_reward_labels": 0,
275
+ "offline_tuner_reward_label_targets_by_target_owner": 0,
276
+ "retrieved_learning_evaluation_results": rle_results_cur.rowcount,
277
+ "evaluation_operation_states": eval_operation_states,
283
278
  "purged_profiles": purged_profiles,
284
279
  "purged_user_playbooks": purged_user_playbooks,
285
280
  }
286
281
 
282
+ def _delete_evaluation_operation_states_locked(
283
+ self, user_id: str, session_ids: list[str]
284
+ ) -> int:
285
+ """Scrub the three evaluation ``_operation_state`` namespaces.
286
+
287
+ Deletes, for every session the user owns: the retrieved-learning
288
+ generation/completion state, the ``agent_success_group_eval`` marker
289
+ (pre-existing RTBF gap), and the ``grade_on_demand`` cache rows
290
+ (pre-existing RTBF gap). Exact keys are used for the first two; the
291
+ grade cache is matched by the injective per-session key prefix so any
292
+ agent_version / evaluation_name variant is covered.
293
+
294
+ Returns:
295
+ int: Number of state rows deleted.
296
+ """
297
+ if not session_ids:
298
+ return 0
299
+ deleted = 0
300
+ exact_keys = [
301
+ build_retrieved_learning_state_key(user_id, session_id)
302
+ for session_id in session_ids
303
+ ] + [
304
+ build_agent_success_marker_key(self.org_id, user_id, session_id)
305
+ for session_id in session_ids
306
+ ]
307
+ chunk_size = 500
308
+ for start in range(0, len(exact_keys), chunk_size):
309
+ chunk = exact_keys[start : start + chunk_size]
310
+ placeholders = ",".join("?" for _ in chunk)
311
+ cur = self.conn.execute(
312
+ f"DELETE FROM _operation_state WHERE service_name IN ({placeholders})", # noqa: S608
313
+ chunk,
314
+ )
315
+ deleted += cur.rowcount
316
+ # Grade-cache keys embed agent_version/evaluation_name, so enumerate
317
+ # the namespace and match each session's injective prefix in Python
318
+ # (LIKE-escaping free-form session ids is not safe).
319
+ prefixes = tuple(
320
+ build_grade_on_demand_session_prefix(self.org_id, session_id)
321
+ for session_id in session_ids
322
+ )
323
+ grade_keys = [
324
+ str(row["service_name"])
325
+ for row in self.conn.execute(
326
+ "SELECT service_name FROM _operation_state WHERE service_name LIKE ?",
327
+ (f"{GRADE_ON_DEMAND_CACHE_PREFIX}::%",),
328
+ ).fetchall()
329
+ if str(row["service_name"]).startswith(prefixes)
330
+ ]
331
+ for start in range(0, len(grade_keys), chunk_size):
332
+ chunk = grade_keys[start : start + chunk_size]
333
+ placeholders = ",".join("?" for _ in chunk)
334
+ cur = self.conn.execute(
335
+ f"DELETE FROM _operation_state WHERE service_name IN ({placeholders})", # noqa: S608
336
+ chunk,
337
+ )
338
+ deleted += cur.rowcount
339
+ return deleted
340
+
287
341
  def apply_governance_user_data_delete(
288
342
  self, purge_id: str, user_id: str
289
343
  ) -> dict[str, int]:
@@ -294,6 +348,14 @@ class GovernanceEraseExecutionMixin:
294
348
  "profiles": "profile",
295
349
  "requests": "request",
296
350
  "agent_success_evaluation_results": "agent_success_evaluation_result",
351
+ "offline_tuner_reward_labels": "offline_tuner_reward_label",
352
+ "offline_tuner_reward_label_targets_by_target_owner": (
353
+ "offline_tuner_reward_label_target_by_target_owner"
354
+ ),
355
+ "retrieved_learning_evaluation_results": (
356
+ "retrieved_learning_evaluation_result"
357
+ ),
358
+ "evaluation_operation_states": "evaluation_operation_state",
297
359
  "purged_profiles": "profile_purge",
298
360
  "purged_user_playbooks": "user_playbook_purge",
299
361
  }
@@ -166,6 +166,7 @@ class SubjectBarrierMixin:
166
166
  "profiles",
167
167
  "user_playbooks",
168
168
  "agent_success_evaluation_result",
169
+ "retrieved_learning_evaluation",
169
170
  ):
170
171
  row = self.conn.execute(
171
172
  f"""SELECT 1 FROM {table}
@@ -198,9 +199,14 @@ class SubjectBarrierMixin:
198
199
  request_id_column="request_id",
199
200
  ):
200
201
  return True
201
- return self._legacy_user_id_rows_remain_locked(
202
+ if self._legacy_user_id_rows_remain_locked(
202
203
  table="agent_success_evaluation_result",
203
204
  subject_ref=subject_ref,
205
+ ):
206
+ return True
207
+ return self._legacy_user_id_rows_remain_locked(
208
+ table="retrieved_learning_evaluation",
209
+ subject_ref=subject_ref,
204
210
  )
205
211
 
206
212
  def begin_subject_erasure_barrier(
@@ -19,6 +19,9 @@ from reflexio.models.api_schema.service_schemas import (
19
19
  Status,
20
20
  )
21
21
  from reflexio.models.config_schema import SearchMode, SearchOptions
22
+ from reflexio.server.services.storage.lifecycle_filters import (
23
+ validate_include_inactive,
24
+ )
22
25
  from reflexio.server.services.storage.storage_base._playbook import (
23
26
  AGGREGATE_REASON_PREFIX,
24
27
  )
@@ -87,6 +90,7 @@ class AgentPlaybookStoreMixin:
87
90
  _fts_upsert: Any
88
91
  _vec_upsert: Any
89
92
  _delete_playbook_search_rows: Any
93
+ _own_transaction: Any
90
94
 
91
95
  def _index_agent_playbook_fts_vec(self, ap: AgentPlaybook) -> None:
92
96
  """Update the FTS and vector indexes for a single agent playbook row.
@@ -178,7 +182,8 @@ class AgentPlaybookStoreMixin:
178
182
  created_at_iso = _epoch_to_iso(ap.created_at)
179
183
  with self._lock:
180
184
  self._insert_agent_playbook_row(self.conn, ap, created_at_iso)
181
- self.conn.commit()
185
+ if self._own_transaction():
186
+ self.conn.commit()
182
187
 
183
188
  self._index_agent_playbook_fts_vec(ap)
184
189
  saved.append(ap)
@@ -228,6 +233,7 @@ class AgentPlaybookStoreMixin:
228
233
 
229
234
  created_at_iso = _epoch_to_iso(ap.created_at)
230
235
  with self._lock:
236
+ own_txn = self._own_transaction()
231
237
  try:
232
238
  self._insert_agent_playbook_row(self.conn, ap, created_at_iso)
233
239
  _append_event_stmt(
@@ -242,9 +248,11 @@ class AgentPlaybookStoreMixin:
242
248
  request_id=request_id,
243
249
  reason=f"{AGGREGATE_REASON_PREFIX}{run_mode}",
244
250
  )
245
- self.conn.commit()
251
+ if own_txn:
252
+ self.conn.commit()
246
253
  except Exception:
247
- self.conn.rollback()
254
+ if own_txn:
255
+ self.conn.rollback()
248
256
  raise
249
257
 
250
258
  # FTS/vec indexing AFTER commit — these helpers self-commit and must
@@ -339,6 +347,46 @@ class AgentPlaybookStoreMixin:
339
347
  row = self._fetchone(sql, (agent_playbook_id,))
340
348
  return _row_to_agent_playbook(row) if row else None
341
349
 
350
+ @SQLiteStorageBase.handle_exceptions
351
+ def get_agent_playbooks_by_ids(
352
+ self,
353
+ agent_playbook_ids: list[int],
354
+ *,
355
+ status_filter: list[Status | None] | None = None,
356
+ playbook_status_filter: list[PlaybookStatus] | None = None,
357
+ include_inactive: bool = False,
358
+ ) -> list[AgentPlaybook]:
359
+ validate_include_inactive(
360
+ include_inactive=include_inactive,
361
+ status_filter=status_filter,
362
+ playbook_status_filter=playbook_status_filter,
363
+ )
364
+ if not agent_playbook_ids:
365
+ return []
366
+ unique_ids = list(dict.fromkeys(agent_playbook_ids))
367
+ rows: list[sqlite3.Row] = []
368
+ for start in range(0, len(unique_ids), 900):
369
+ chunk = unique_ids[start : start + 900]
370
+ placeholders = ",".join("?" for _ in chunk)
371
+ query = (
372
+ "SELECT * FROM agent_playbooks "
373
+ f"WHERE agent_playbook_id IN ({placeholders})"
374
+ )
375
+ params: list[Any] = list(chunk)
376
+ if not include_inactive:
377
+ if status_filter is not None:
378
+ fragment, status_params = _build_status_sql(status_filter)
379
+ query += f" AND {fragment}"
380
+ params.extend(status_params)
381
+ else:
382
+ query += " AND status IS NULL"
383
+ if playbook_status_filter:
384
+ status_placeholders = ",".join("?" for _ in playbook_status_filter)
385
+ query += f" AND playbook_status IN ({status_placeholders})"
386
+ params.extend(status.value for status in playbook_status_filter)
387
+ rows.extend(self._fetchall(query, params))
388
+ return [_row_to_agent_playbook(row) for row in rows]
389
+
342
390
  @SQLiteStorageBase.handle_exceptions
343
391
  def delete_all_agent_playbooks(self) -> None:
344
392
  batch_request_id = uuid.uuid4().hex
@@ -609,7 +657,8 @@ class AgentPlaybookStoreMixin:
609
657
  to_status="archived",
610
658
  status_namespace="lifecycle_status",
611
659
  )
612
- self.conn.commit()
660
+ if self._own_transaction():
661
+ self.conn.commit()
613
662
 
614
663
  @SQLiteStorageBase.handle_exceptions
615
664
  def archive_agent_playbooks_by_ids(self, agent_playbook_ids: list[int]) -> None:
@@ -648,7 +697,8 @@ class AgentPlaybookStoreMixin:
648
697
  to_status="archived",
649
698
  status_namespace="lifecycle_status",
650
699
  )
651
- self.conn.commit()
700
+ if self._own_transaction():
701
+ self.conn.commit()
652
702
 
653
703
  @SQLiteStorageBase.handle_exceptions
654
704
  def supersede_agent_playbooks_by_ids(
@@ -712,7 +762,8 @@ class AgentPlaybookStoreMixin:
712
762
  request_id=request_id,
713
763
  )
714
764
  updated += 1
715
- self.conn.commit()
765
+ if self._own_transaction():
766
+ self.conn.commit()
716
767
  return updated
717
768
 
718
769
  @SQLiteStorageBase.handle_exceptions
@@ -773,7 +824,8 @@ class AgentPlaybookStoreMixin:
773
824
  request_id=request_id,
774
825
  )
775
826
  updated += 1
776
- self.conn.commit()
827
+ if self._own_transaction():
828
+ self.conn.commit()
777
829
  return updated
778
830
 
779
831
  @SQLiteStorageBase.handle_exceptions