claude-smart 0.2.47 → 0.2.48

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 (113) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/README.md +1 -1
  3. package/package.json +1 -1
  4. package/plugin/.claude-plugin/plugin.json +1 -1
  5. package/plugin/.codex-plugin/plugin.json +1 -1
  6. package/plugin/dashboard/app/sessions/[sessionId]/page.tsx +36 -2
  7. package/plugin/dashboard/package-lock.json +61 -390
  8. package/plugin/dashboard/package.json +2 -2
  9. package/plugin/pyproject.toml +1 -1
  10. package/plugin/uv.lock +1 -1
  11. package/plugin/vendor/reflexio/.env.example +7 -0
  12. package/plugin/vendor/reflexio/pyproject.toml +2 -1
  13. package/plugin/vendor/reflexio/reflexio/README.md +8 -5
  14. package/plugin/vendor/reflexio/reflexio/lib/_config.py +23 -18
  15. package/plugin/vendor/reflexio/reflexio/lib/_interactions.py +16 -1
  16. package/plugin/vendor/reflexio/reflexio/lib/_search.py +15 -11
  17. package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/enums.py +1 -0
  18. package/plugin/vendor/reflexio/reflexio/models/config_schema.py +24 -3
  19. package/plugin/vendor/reflexio/reflexio/server/README.md +25 -8
  20. package/plugin/vendor/reflexio/reflexio/server/__init__.py +21 -2
  21. package/plugin/vendor/reflexio/reflexio/server/api.py +133 -3273
  22. package/plugin/vendor/reflexio/reflexio/server/api_endpoints/README.md +4 -3
  23. package/plugin/vendor/reflexio/reflexio/server/api_endpoints/publisher_api.py +16 -1
  24. package/plugin/vendor/reflexio/reflexio/server/cache/reflexio_cache.py +62 -36
  25. package/plugin/vendor/reflexio/reflexio/server/deployment_profile.py +69 -0
  26. package/plugin/vendor/reflexio/reflexio/server/llm/_litellm_embedding.py +424 -0
  27. package/plugin/vendor/reflexio/reflexio/server/llm/_litellm_json_extraction.py +249 -0
  28. package/plugin/vendor/reflexio/reflexio/server/llm/_litellm_structured_output.py +195 -0
  29. package/plugin/vendor/reflexio/reflexio/server/llm/_litellm_subprocess.py +152 -0
  30. package/plugin/vendor/reflexio/reflexio/server/llm/_litellm_text_generation.py +980 -0
  31. package/plugin/vendor/reflexio/reflexio/server/llm/_litellm_types.py +110 -0
  32. package/plugin/vendor/reflexio/reflexio/server/llm/litellm_client.py +73 -1819
  33. package/plugin/vendor/reflexio/reflexio/server/llm/providers/claude_code_provider.py +56 -4
  34. package/plugin/vendor/reflexio/reflexio/server/middleware.py +244 -0
  35. package/plugin/vendor/reflexio/reflexio/server/operation_limiter.py +9 -1
  36. package/plugin/vendor/reflexio/reflexio/server/rate_limit.py +79 -0
  37. package/plugin/vendor/reflexio/reflexio/server/routes/__init__.py +6 -0
  38. package/plugin/vendor/reflexio/reflexio/server/routes/_common.py +25 -0
  39. package/plugin/vendor/reflexio/reflexio/server/routes/_metering.py +98 -0
  40. package/plugin/vendor/reflexio/reflexio/server/routes/braintrust.py +129 -0
  41. package/plugin/vendor/reflexio/reflexio/server/routes/config.py +210 -0
  42. package/plugin/vendor/reflexio/reflexio/server/routes/evaluation.py +549 -0
  43. package/plugin/vendor/reflexio/reflexio/server/routes/interactions.py +259 -0
  44. package/plugin/vendor/reflexio/reflexio/server/routes/playbooks.py +578 -0
  45. package/plugin/vendor/reflexio/reflexio/server/routes/profiles.py +423 -0
  46. package/plugin/vendor/reflexio/reflexio/server/routes/provenance.py +345 -0
  47. package/plugin/vendor/reflexio/reflexio/server/routes/search.py +349 -0
  48. package/plugin/vendor/reflexio/reflexio/server/routes/system.py +261 -0
  49. package/plugin/vendor/reflexio/reflexio/server/scheduling.py +132 -0
  50. package/plugin/vendor/reflexio/reflexio/server/services/README.md +3 -2
  51. package/plugin/vendor/reflexio/reflexio/server/services/base_generation/__init__.py +38 -0
  52. package/plugin/vendor/reflexio/reflexio/server/services/base_generation/_batch_progress.py +298 -0
  53. package/plugin/vendor/reflexio/reflexio/server/services/base_generation/_config_filter.py +152 -0
  54. package/plugin/vendor/reflexio/reflexio/server/services/base_generation/_extraction_lifecycle.py +243 -0
  55. package/plugin/vendor/reflexio/reflexio/server/services/base_generation/_should_run.py +299 -0
  56. package/plugin/vendor/reflexio/reflexio/server/services/base_generation/_status_change.py +273 -0
  57. package/plugin/vendor/reflexio/reflexio/server/services/base_generation/_usage_billing.py +258 -0
  58. package/plugin/vendor/reflexio/reflexio/server/services/base_generation_service.py +17 -1183
  59. package/plugin/vendor/reflexio/reflexio/server/services/deduplication_utils.py +8 -1
  60. package/plugin/vendor/reflexio/reflexio/server/services/extraction/resume_scheduler.py +26 -41
  61. package/plugin/vendor/reflexio/reflexio/server/services/generation_service.py +232 -123
  62. package/plugin/vendor/reflexio/reflexio/server/services/lineage/gc_scheduler.py +362 -81
  63. package/plugin/vendor/reflexio/reflexio/server/services/playbook/components/aggregator.py +68 -490
  64. package/plugin/vendor/reflexio/reflexio/server/services/playbook/components/aggregator_clustering.py +184 -0
  65. package/plugin/vendor/reflexio/reflexio/server/services/playbook/components/aggregator_postprocessing.py +130 -0
  66. package/plugin/vendor/reflexio/reflexio/server/services/playbook/components/aggregator_prompt_formatting.py +212 -0
  67. package/plugin/vendor/reflexio/reflexio/server/services/playbook/components/consolidator.py +258 -69
  68. package/plugin/vendor/reflexio/reflexio/server/services/publish_learning_worker.py +288 -0
  69. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/__init__.py +29 -4
  70. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_agent_run.py +10 -1167
  71. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_base.py +56 -351
  72. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_governance.py +0 -1513
  73. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_lineage.py +6 -1
  74. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_profiles.py +7 -1281
  75. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_share_links.py +30 -0
  76. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/agent_run/__init__.py +9 -0
  77. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/agent_run/_agent_run_store.py +506 -0
  78. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/agent_run/_pending_tool_call_store.py +704 -0
  79. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/agent_run/_run_tool_dependency_store.py +123 -0
  80. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/base/__init__.py +6 -0
  81. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/base/_deletion.py +263 -0
  82. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/base/_fts_vec.py +132 -0
  83. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/governance/__init__.py +13 -0
  84. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/governance/_audit.py +122 -0
  85. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/governance/_erase_execution.py +465 -0
  86. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/governance/_purge.py +387 -0
  87. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/governance/_rebuild_hide.py +332 -0
  88. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/governance/_subject_barrier.py +511 -0
  89. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_agent.py +6 -3
  90. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_user.py +13 -7
  91. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/profiles/__init__.py +9 -0
  92. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/profiles/_interaction_store.py +263 -0
  93. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/profiles/_profile_store.py +896 -0
  94. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/profiles/_search.py +270 -0
  95. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/__init__.py +33 -8
  96. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_agent_run.py +64 -370
  97. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_share_links.py +20 -0
  98. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/agent_run/__init__.py +9 -0
  99. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/agent_run/_agent_run_store.py +86 -0
  100. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/agent_run/_models.py +195 -0
  101. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/agent_run/_pending_tool_call_store.py +148 -0
  102. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/agent_run/_run_tool_dependency_store.py +38 -0
  103. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/governance/__init__.py +13 -0
  104. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/governance/_audit.py +29 -0
  105. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/governance/_erase_execution.py +30 -0
  106. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/governance/_purge.py +74 -0
  107. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/governance/_rebuild_hide.py +32 -0
  108. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/governance/_subject_barrier.py +49 -0
  109. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/profiles/__init__.py +9 -0
  110. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/profiles/_interaction_store.py +73 -0
  111. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/{_profiles.py → profiles/_profile_store.py} +44 -86
  112. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/profiles/_search.py +32 -0
  113. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_governance.py +0 -148
@@ -0,0 +1,270 @@
1
+ """Interaction + profile search methods for SQLite storage.
2
+
3
+ Extracted verbatim from ``_profiles.py`` (the Search bucket). Profile CRUD lives
4
+ in ``profiles._profile_store``; interaction CRUD in ``profiles._interaction_store``.
5
+ The shared ``_build_tags_sql`` helper stays module-level in ``_profiles`` (it is
6
+ used by both this bucket and ProfileStore) and is imported here.
7
+ """
8
+
9
+ import logging
10
+ from typing import Any
11
+
12
+ from reflexio.models.api_schema.retriever_schema import (
13
+ SearchInteractionRequest,
14
+ SearchUserProfileRequest,
15
+ )
16
+ from reflexio.models.api_schema.service_schemas import (
17
+ Interaction,
18
+ Status,
19
+ UserProfile,
20
+ )
21
+ from reflexio.models.config_schema import SearchMode
22
+
23
+ from .._base import (
24
+ SQLiteStorageBase,
25
+ _build_status_sql,
26
+ _effective_search_mode,
27
+ _epoch_now,
28
+ _row_to_interaction,
29
+ _row_to_profile,
30
+ _sanitize_fts_query,
31
+ _true_rrf_merge,
32
+ _vector_rank_rows,
33
+ )
34
+ from .._profiles import _build_tags_sql
35
+
36
+ logger = logging.getLogger(__name__)
37
+
38
+
39
+ class ProfileSearchMixin:
40
+ """Mixin providing profile + interaction search."""
41
+
42
+ # Type hints for instance attributes/methods provided by SQLiteStorageBase via MRO
43
+ _fetchall: Any
44
+
45
+ # ------------------------------------------------------------------
46
+ # Search — Interactions & Profiles
47
+ # ------------------------------------------------------------------
48
+
49
+ @SQLiteStorageBase.handle_exceptions
50
+ def search_interaction(
51
+ self,
52
+ search_interaction_request: SearchInteractionRequest,
53
+ query_embedding: list[float] | None = None,
54
+ ) -> list[Interaction]:
55
+ req = search_interaction_request
56
+ has_query = bool(req.query)
57
+ match_count = req.most_recent_k or 10
58
+ mode = _effective_search_mode(req.search_mode, query_embedding, req.query)
59
+
60
+ conditions: list[str] = ["i.user_id = ?"]
61
+ params: list[str | int | float] = [req.user_id]
62
+
63
+ if req.request_id:
64
+ conditions.append("i.request_id = ?")
65
+ params.append(req.request_id)
66
+ if req.start_time:
67
+ conditions.append("i.created_at >= ?")
68
+ params.append(req.start_time.timestamp())
69
+ if req.end_time:
70
+ conditions.append("i.created_at <= ?")
71
+ params.append(req.end_time.timestamp())
72
+
73
+ where_clause = " AND ".join(conditions)
74
+ overfetch = match_count * 5 if mode != SearchMode.FTS else match_count
75
+
76
+ # Vector-only: rank by embedding similarity
77
+ if (
78
+ mode in (SearchMode.VECTOR, SearchMode.HYBRID)
79
+ and query_embedding
80
+ and not has_query
81
+ ):
82
+ vector_limit = match_count * 10
83
+ sql = f"""SELECT i.* FROM interactions i
84
+ WHERE {where_clause}
85
+ ORDER BY i.created_at DESC
86
+ LIMIT ?"""
87
+ rows = self._fetchall(sql, (*params, vector_limit))
88
+ rows = _vector_rank_rows(rows, query_embedding, match_count)
89
+ elif has_query:
90
+ # FTS search (with optional HYBRID re-ranking)
91
+ fts_query = _sanitize_fts_query(req.query) # type: ignore[arg-type]
92
+ fts_conditions = ["interactions_fts MATCH ?", *conditions]
93
+ fts_where = " AND ".join(fts_conditions)
94
+ fts_params: list[str | int | float] = [fts_query, *params, overfetch]
95
+ sql = f"""SELECT i.* FROM interactions i
96
+ JOIN interactions_fts f ON i.interaction_id = f.rowid
97
+ WHERE {fts_where}
98
+ ORDER BY bm25(interactions_fts, 1.0, 2.0)
99
+ LIMIT ?"""
100
+ fts_rows = self._fetchall(sql, tuple(fts_params))
101
+
102
+ if mode == SearchMode.HYBRID and query_embedding:
103
+ vec_limit = match_count * 10
104
+ vec_sql = f"""SELECT i.* FROM interactions i
105
+ WHERE {where_clause}
106
+ ORDER BY i.created_at DESC
107
+ LIMIT ?"""
108
+ vec_candidates = self._fetchall(vec_sql, (*params, vec_limit))
109
+ vec_rows = _vector_rank_rows(vec_candidates, query_embedding, overfetch)
110
+ rows = _true_rrf_merge(
111
+ fts_rows,
112
+ vec_rows,
113
+ "interaction_id",
114
+ match_count,
115
+ )
116
+ else:
117
+ rows = fts_rows[:match_count]
118
+ else:
119
+ if req.most_recent_k:
120
+ # No query — just fetch most recent interactions by time
121
+ sql = f"""SELECT i.* FROM interactions i
122
+ WHERE {where_clause}
123
+ ORDER BY i.created_at DESC
124
+ LIMIT ?"""
125
+ rows = self._fetchall(sql, (*params, req.most_recent_k))
126
+ return [_row_to_interaction(r) for r in reversed(rows)]
127
+ return []
128
+
129
+ interactions = [_row_to_interaction(r) for r in rows]
130
+ if req.most_recent_k:
131
+ sorted_ints = sorted(interactions, key=lambda x: x.created_at, reverse=True)
132
+ return list(reversed(sorted_ints[: req.most_recent_k]))
133
+ return interactions
134
+
135
+ @SQLiteStorageBase.handle_exceptions
136
+ def search_user_profile( # noqa: C901
137
+ self,
138
+ search_user_profile_request: SearchUserProfileRequest,
139
+ status_filter: list[Status | None] | None = None,
140
+ query_embedding: list[float] | None = None,
141
+ ) -> list[UserProfile]:
142
+ if status_filter is None:
143
+ status_filter = [None]
144
+
145
+ req = search_user_profile_request
146
+ match_count = req.top_k or 10
147
+ current_ts = _epoch_now()
148
+ has_query = bool(req.query)
149
+ mode = _effective_search_mode(req.search_mode, query_embedding, req.query)
150
+ has_embedding = query_embedding is not None
151
+ logger.info(
152
+ "Profile search: requested_mode=%s, effective_mode=%s, has_query=%s, has_embedding=%s, user_id=%s",
153
+ req.search_mode,
154
+ mode,
155
+ has_query,
156
+ has_embedding,
157
+ req.user_id,
158
+ )
159
+
160
+ conditions: list[str] = ["p.expiration_timestamp >= ?"]
161
+ params: list[object] = [current_ts]
162
+
163
+ if req.user_id:
164
+ conditions.append("p.user_id = ?")
165
+ params.append(req.user_id)
166
+ if req.start_time:
167
+ conditions.append("p.last_modified_timestamp >= ?")
168
+ params.append(int(req.start_time.timestamp()))
169
+ if req.end_time:
170
+ conditions.append("p.last_modified_timestamp <= ?")
171
+ params.append(int(req.end_time.timestamp()))
172
+ if req.source:
173
+ conditions.append("LOWER(p.source) = LOWER(?)")
174
+ params.append(req.source)
175
+ if status_filter is not None:
176
+ frag, sparams = _build_status_sql(status_filter)
177
+ conditions.append(frag)
178
+ params.extend(sparams)
179
+ tag_frag, tag_params = _build_tags_sql("p", req.tags)
180
+ if tag_frag:
181
+ conditions.append(tag_frag)
182
+ params.extend(tag_params)
183
+
184
+ where_clause = " AND ".join(conditions)
185
+ overfetch = match_count * 5 if mode != SearchMode.FTS else match_count
186
+
187
+ # Pure vector search: fetch all candidates, rank by cosine similarity
188
+ if mode == SearchMode.VECTOR and query_embedding:
189
+ if req.generated_from_request_id:
190
+ conditions.append("p.generated_from_request_id = ?")
191
+ params.append(req.generated_from_request_id)
192
+ where_clause = " AND ".join(conditions)
193
+ sql = f"""SELECT p.* FROM profiles p
194
+ WHERE {where_clause}
195
+ ORDER BY p.last_modified_timestamp DESC"""
196
+ rows = self._fetchall(sql, tuple(params))
197
+ logger.info(
198
+ "VECTOR search: %d candidates fetched, ranking by embedding", len(rows)
199
+ )
200
+ rows = _vector_rank_rows(rows, query_embedding, match_count)
201
+ elif has_query:
202
+ fts_query = _sanitize_fts_query(req.query) # type: ignore[arg-type]
203
+ sql = f"""SELECT p.* FROM profiles p
204
+ JOIN profiles_fts f ON p.profile_id = f.profile_id
205
+ WHERE profiles_fts MATCH ?
206
+ AND {where_clause}
207
+ ORDER BY bm25(profiles_fts, 0.0, 1.0)
208
+ LIMIT ?"""
209
+ params_list: list[object] = [fts_query, *params, overfetch]
210
+ fts_rows = self._fetchall(sql, tuple(params_list))
211
+ logger.info("FTS search: %d results from BM25", len(fts_rows))
212
+
213
+ if mode == SearchMode.HYBRID and query_embedding:
214
+ logger.info("HYBRID merging FTS + vector results via RRF")
215
+ vec_limit = match_count * 10
216
+ vec_sql = f"""SELECT p.* FROM profiles p
217
+ WHERE {where_clause}
218
+ ORDER BY p.last_modified_timestamp DESC
219
+ LIMIT ?"""
220
+ vec_candidates = self._fetchall(vec_sql, (*params, vec_limit))
221
+ vec_rows = _vector_rank_rows(vec_candidates, query_embedding, overfetch)
222
+ rows = _true_rrf_merge(
223
+ fts_rows,
224
+ vec_rows,
225
+ "profile_id",
226
+ match_count,
227
+ )
228
+ else:
229
+ rows = fts_rows
230
+ elif query_embedding:
231
+ # HYBRID without query text: rank by embedding only
232
+ if req.generated_from_request_id:
233
+ conditions.append("p.generated_from_request_id = ?")
234
+ params.append(req.generated_from_request_id)
235
+ where_clause = " AND ".join(conditions)
236
+ sql = f"""SELECT p.* FROM profiles p
237
+ WHERE {where_clause}
238
+ ORDER BY p.last_modified_timestamp DESC"""
239
+ rows = self._fetchall(sql, tuple(params))
240
+ logger.info(
241
+ "HYBRID (no query text) search: %d candidates, ranking by embedding",
242
+ len(rows),
243
+ )
244
+ rows = _vector_rank_rows(rows, query_embedding, match_count)
245
+ else:
246
+ if req.generated_from_request_id:
247
+ conditions.append("p.generated_from_request_id = ?")
248
+ params.append(req.generated_from_request_id)
249
+ where_clause = " AND ".join(conditions)
250
+ sql = f"""SELECT p.* FROM profiles p
251
+ WHERE {where_clause}
252
+ ORDER BY p.last_modified_timestamp DESC
253
+ LIMIT ?"""
254
+ params_list = [*params, overfetch]
255
+ rows = self._fetchall(sql, tuple(params_list))
256
+
257
+ profiles = [_row_to_profile(r) for r in rows]
258
+ logger.info("Profile search: %d profiles before post-filtering", len(profiles))
259
+
260
+ # Apply filters that can't easily go into SQL
261
+ filtered: list[UserProfile] = []
262
+ for profile in profiles:
263
+ if req.custom_feature and (
264
+ req.custom_feature.lower() not in str(profile.custom_features).lower()
265
+ ):
266
+ continue
267
+ filtered.append(profile)
268
+ if len(filtered) >= match_count:
269
+ break
270
+ return filtered
@@ -23,15 +23,20 @@ from ._agent_run import (
23
23
  )
24
24
  from ._base import BaseStorageCore, matches_status_filter
25
25
  from ._extras import ExtrasMixin
26
- from ._governance import GovernanceMixin
27
26
  from ._lineage import EntityType, LineageEventMixin
28
27
  from ._operations import OperationMixin
29
- from ._profiles import ProfileMixin
30
28
  from ._requests import RequestMixin
31
29
  from ._retrieval_log import RetrievalLogMixin
32
30
  from ._shadow_verdicts import ShadowVerdictsMixin
33
31
  from ._share_links import ShareLinkMixin
34
32
  from ._stall_state import StallStateMixin
33
+ from .governance import (
34
+ AuditEventStoreMixin,
35
+ GovernanceEraseExecutionMixin,
36
+ PurgeOperationStoreMixin,
37
+ RebuildHideMixin,
38
+ SubjectBarrierMixin,
39
+ )
35
40
  from .playbook import (
36
41
  AgentEvaluationResultStoreMixin,
37
42
  AgentPlaybookStoreMixin,
@@ -39,11 +44,14 @@ from .playbook import (
39
44
  PlaybookSourceLinkageMixin,
40
45
  UserPlaybookStoreMixin,
41
46
  )
47
+ from .profiles import InteractionStoreMixin, ProfileSearchMixin, ProfileStoreMixin
42
48
 
43
49
 
44
50
  class BaseStorage(
45
51
  AgentRunMixin,
46
- ProfileMixin,
52
+ ProfileStoreMixin,
53
+ InteractionStoreMixin,
54
+ ProfileSearchMixin,
47
55
  RequestMixin,
48
56
  AgentPlaybookStoreMixin,
49
57
  UserPlaybookStoreMixin,
@@ -51,7 +59,11 @@ class BaseStorage(
51
59
  OptimizationJobStoreMixin,
52
60
  AgentEvaluationResultStoreMixin,
53
61
  RetrievalLogMixin,
54
- GovernanceMixin,
62
+ AuditEventStoreMixin,
63
+ PurgeOperationStoreMixin,
64
+ SubjectBarrierMixin,
65
+ GovernanceEraseExecutionMixin,
66
+ RebuildHideMixin,
55
67
  LineageEventMixin,
56
68
  OperationMixin,
57
69
  ExtrasMixin,
@@ -162,8 +174,8 @@ class BaseStorage(
162
174
  interaction_count = len(self.get_user_interaction(user_id))
163
175
 
164
176
  # All statuses a user's row can have — including tombstones (SUPERSEDED,
165
- # MERGED). Erasure MUST reach every row the user owns regardless of
166
- # status; the old filter excluded tombstones, leaving them in the DB
177
+ # MERGED, EXPIRED). Erasure MUST reach every row the user owns regardless
178
+ # of status; the old filter excluded tombstones, leaving them in the DB
167
179
  # after clear_user_data (GDPR regression).
168
180
  _all_statuses: list[Status | None] = [
169
181
  None, # CURRENT
@@ -172,6 +184,7 @@ class BaseStorage(
172
184
  Status.ARCHIVE_IN_PROGRESS,
173
185
  Status.SUPERSEDED,
174
186
  Status.MERGED,
187
+ Status.EXPIRED,
175
188
  ]
176
189
 
177
190
  # Snapshot user_playbook ids for the user and partition into
@@ -190,10 +203,14 @@ class BaseStorage(
190
203
  )
191
204
 
192
205
  # Snapshot profile ids for the user and partition into
193
- # purge vs delete sets.
206
+ # purge vs delete sets. include_expired=True drops the
207
+ # expiration_timestamp >= now guard so EXPIRED tombstones (expiry in
208
+ # the past) are enumerated and reached by the erasure path.
194
209
  raw_profile_ids = [
195
210
  p.profile_id
196
- for p in self.get_user_profile(user_id, status_filter=_all_statuses)
211
+ for p in self.get_user_profile(
212
+ user_id, status_filter=_all_statuses, include_expired=True
213
+ )
197
214
  if p.profile_id is not None
198
215
  ]
199
216
  purge_profile_ids, delete_profile_ids = self._partition_purge_vs_delete(
@@ -257,10 +274,18 @@ __all__ = [
257
274
  "PendingToolCallStatus",
258
275
  "PendingToolCallUpsertResult",
259
276
  "AgentEvaluationResultStoreMixin",
277
+ "AuditEventStoreMixin",
278
+ "PurgeOperationStoreMixin",
279
+ "SubjectBarrierMixin",
280
+ "GovernanceEraseExecutionMixin",
281
+ "RebuildHideMixin",
260
282
  "AgentPlaybookStoreMixin",
261
283
  "OptimizationJobStoreMixin",
262
284
  "PlaybookSourceLinkageMixin",
263
285
  "UserPlaybookStoreMixin",
286
+ "InteractionStoreMixin",
287
+ "ProfileSearchMixin",
288
+ "ProfileStoreMixin",
264
289
  "RetrievalLogMixin",
265
290
  "PriorAnswerMatch",
266
291
  "RunToolDependencyKind",