claude-smart 0.2.46 → 0.2.47

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/README.md +19 -11
  3. package/bin/claude-smart.js +290 -68
  4. package/package.json +1 -1
  5. package/plugin/.claude-plugin/plugin.json +1 -1
  6. package/plugin/.codex-plugin/plugin.json +1 -1
  7. package/plugin/README.md +11 -10
  8. package/plugin/dashboard/app/layout.tsx +20 -0
  9. package/plugin/opencode/dist/server.mjs +76 -2
  10. package/plugin/opencode/server.mts +79 -2
  11. package/plugin/pyproject.toml +6 -2
  12. package/plugin/scripts/smart-install.sh +7 -1
  13. package/plugin/src/claude_smart/cli.py +210 -22
  14. package/plugin/src/claude_smart/context_format.py +9 -9
  15. package/plugin/src/claude_smart/cs_cite.py +66 -19
  16. package/plugin/uv.lock +5 -5
  17. package/plugin/vendor/reflexio/README.md +3 -3
  18. package/plugin/vendor/reflexio/pyproject.toml +1 -1
  19. package/plugin/vendor/reflexio/reflexio/README.md +3 -1
  20. package/plugin/vendor/reflexio/reflexio/cli/bootstrap_config.py +1 -1
  21. package/plugin/vendor/reflexio/reflexio/cli/commands/setup_cmd.py +2 -2
  22. package/plugin/vendor/reflexio/reflexio/cli/utils.py +44 -1
  23. package/plugin/vendor/reflexio/reflexio/client/client.py +97 -0
  24. package/plugin/vendor/reflexio/reflexio/lib/_agent_playbook.py +8 -0
  25. package/plugin/vendor/reflexio/reflexio/lib/_base.py +15 -0
  26. package/plugin/vendor/reflexio/reflexio/lib/_generation.py +9 -8
  27. package/plugin/vendor/reflexio/reflexio/lib/_profiles.py +27 -16
  28. package/plugin/vendor/reflexio/reflexio/lib/_search.py +23 -5
  29. package/plugin/vendor/reflexio/reflexio/lib/_user_playbook.py +9 -0
  30. package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/__init__.py +1 -0
  31. package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/governance.py +117 -0
  32. package/plugin/vendor/reflexio/reflexio/models/api_schema/retriever_schema.py +45 -3
  33. package/plugin/vendor/reflexio/reflexio/models/config_schema.py +16 -0
  34. package/plugin/vendor/reflexio/reflexio/server/README.md +14 -2
  35. package/plugin/vendor/reflexio/reflexio/server/api.py +176 -29
  36. package/plugin/vendor/reflexio/reflexio/server/api_endpoints/request_context.py +1 -1
  37. package/plugin/vendor/reflexio/reflexio/server/{_auth.py → auth.py} +2 -0
  38. package/plugin/vendor/reflexio/reflexio/server/extensions.py +213 -0
  39. package/plugin/vendor/reflexio/reflexio/server/llm/model_defaults.py +4 -4
  40. package/plugin/vendor/reflexio/reflexio/server/llm/providers/claude_code_provider.py +1 -1
  41. package/plugin/vendor/reflexio/reflexio/server/llm/rerank/cross_encoder_reranker.py +12 -1
  42. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.4.0.prompt.md +14 -2
  43. package/plugin/vendor/reflexio/reflexio/server/services/README.md +3 -1
  44. package/plugin/vendor/reflexio/reflexio/server/services/extraction/resume_worker.py +8 -0
  45. package/plugin/vendor/reflexio/reflexio/server/services/governance/config.py +52 -0
  46. package/plugin/vendor/reflexio/reflexio/server/services/governance/service.py +378 -0
  47. package/plugin/vendor/reflexio/reflexio/server/services/governance/subject_refs.py +34 -0
  48. package/plugin/vendor/reflexio/reflexio/server/services/lineage/gc_scheduler.py +45 -19
  49. package/plugin/vendor/reflexio/reflexio/server/services/playbook/README.md +9 -1
  50. package/plugin/vendor/reflexio/reflexio/server/services/playbook/aggregation_prompt_processing.py +100 -0
  51. package/plugin/vendor/reflexio/reflexio/server/services/playbook/components/aggregator.py +129 -111
  52. package/plugin/vendor/reflexio/reflexio/server/services/playbook/service.py +9 -9
  53. package/plugin/vendor/reflexio/reflexio/server/services/profile/components/extractor.py +3 -2
  54. package/plugin/vendor/reflexio/reflexio/server/services/retrieval/recency.py +211 -0
  55. package/plugin/vendor/reflexio/reflexio/server/services/retrieval/relevance_floor.py +29 -13
  56. package/plugin/vendor/reflexio/reflexio/server/services/storage/error.py +4 -0
  57. package/plugin/vendor/reflexio/reflexio/server/services/storage/governance_validation.py +681 -0
  58. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/__init__.py +14 -2
  59. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_base.py +2 -0
  60. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_extras.py +49 -19
  61. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_governance.py +1965 -0
  62. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_lineage.py +5 -3
  63. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_playbook.py +1 -2133
  64. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_profiles.py +262 -107
  65. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_requests.py +73 -33
  66. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/__init__.py +13 -0
  67. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_agent.py +952 -0
  68. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_eval_results.py +189 -0
  69. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_optimization.py +247 -0
  70. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_source_linkage.py +145 -0
  71. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_user.py +838 -0
  72. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/__init__.py +19 -3
  73. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_extras.py +4 -4
  74. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_governance.py +148 -0
  75. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_playbook.py +0 -909
  76. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_profiles.py +13 -0
  77. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_requests.py +2 -0
  78. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/__init__.py +13 -0
  79. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_agent.py +365 -0
  80. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_eval_results.py +124 -0
  81. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_optimization.py +85 -0
  82. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_source_linkage.py +47 -0
  83. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_user.py +333 -0
  84. package/plugin/vendor/reflexio/reflexio/server/services/unified_search_service.py +153 -12
  85. package/plugin/vendor/reflexio/reflexio/server/services/playbook/user_detail_stripping.py +0 -84
@@ -1,913 +1,4 @@
1
- import logging
2
- from abc import abstractmethod
3
- from collections.abc import Sequence
4
-
5
- from reflexio.models.api_schema.common import BlockingIssue
6
- from reflexio.models.api_schema.domain import (
7
- AgentPlaybook,
8
- AgentPlaybookSourceWindow,
9
- AgentSuccessEvaluationResult,
10
- PlaybookOptimizationCandidate,
11
- PlaybookOptimizationEvaluation,
12
- PlaybookOptimizationEvent,
13
- PlaybookOptimizationJob,
14
- PlaybookStatus,
15
- Status,
16
- UserPlaybook,
17
- )
18
- from reflexio.models.api_schema.domain.entities import LineageEvent
19
- from reflexio.models.api_schema.retriever_schema import (
20
- SearchAgentPlaybookRequest,
21
- SearchUserPlaybookRequest,
22
- )
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
1
  # Shared prefix for aggregate lineage event reasons.
31
2
  # Consumers: storage_base (here), sqlite_storage/_playbook.py, and lib/_agent_playbook.py
32
3
  # (which imports this constant to keep the parser and producers in sync).
33
4
  AGGREGATE_REASON_PREFIX = "aggregate:"
34
-
35
-
36
- class PlaybookMixin:
37
- """Mixin for playbook and agent success evaluation methods."""
38
-
39
- # ==============================
40
- # User Playbook methods
41
- # ==============================
42
-
43
- @abstractmethod
44
- def save_user_playbooks(self, user_playbooks: list[UserPlaybook]) -> None:
45
- raise NotImplementedError
46
-
47
- @abstractmethod
48
- def get_user_playbooks(
49
- self,
50
- limit: int = 100,
51
- user_id: str | None = None,
52
- playbook_name: str | None = None,
53
- agent_version: str | None = None,
54
- status_filter: list[Status | None] | None = None,
55
- start_time: int | None = None,
56
- end_time: int | None = None,
57
- include_embedding: bool = False,
58
- tags: list[str] | None = None,
59
- ) -> list[UserPlaybook]:
60
- """Get user playbooks from storage.
61
-
62
- Args:
63
- limit (int): Maximum number of playbooks to return
64
- user_id (str, optional): The user ID to filter by. If None, returns playbooks for all users.
65
- playbook_name (str, optional): The playbook name to filter by. If None, returns all user playbooks.
66
- agent_version (str, optional): The agent version to filter by. If None, returns all agent versions.
67
- status_filter (list[Optional[Status]], optional): List of status values to filter by.
68
- Can include None (current), Status.PENDING (from rerun), Status.ARCHIVED (old).
69
- If None, returns playbooks with all statuses.
70
- start_time (int, optional): Unix timestamp. Only return playbooks created at or after this time.
71
- end_time (int, optional): Unix timestamp. Only return playbooks created at or before this time.
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.
74
-
75
- Returns:
76
- list[UserPlaybook]: List of user playbook objects
77
- """
78
- raise NotImplementedError
79
-
80
- @abstractmethod
81
- def count_user_playbooks(
82
- self,
83
- user_id: str | None = None,
84
- playbook_name: str | None = None,
85
- min_user_playbook_id: int | None = None,
86
- agent_version: str | None = None,
87
- status_filter: list[Status | None] | None = None,
88
- ) -> int:
89
- """Count user playbooks in storage efficiently.
90
-
91
- Args:
92
- user_id (str, optional): The user ID to filter by. If None, counts playbooks for all users.
93
- playbook_name (str, optional): The playbook name to filter by. If None, counts all user playbooks.
94
- min_user_playbook_id (int, optional): Only count playbooks with user_playbook_id greater than this value.
95
- agent_version (str, optional): The agent version to filter by. If None, counts all agent versions.
96
- status_filter (list[Optional[Status]], optional): List of status values to filter by.
97
- Can include None (current), Status.PENDING (from rerun), Status.ARCHIVED (old).
98
- If None, returns playbooks with all statuses.
99
-
100
- Returns:
101
- int: Count of user playbooks matching the filters
102
- """
103
- raise NotImplementedError
104
-
105
- @abstractmethod
106
- def count_user_playbooks_by_session(self, session_id: str) -> int:
107
- """Count user playbooks linked to a session via request_id -> requests.session_id.
108
-
109
- Args:
110
- session_id (str): The session ID to count user playbooks for
111
-
112
- Returns:
113
- int: Count of user playbooks linked to the session
114
- """
115
- raise NotImplementedError
116
-
117
- @abstractmethod
118
- def delete_all_user_playbooks(self) -> None:
119
- """Delete all user playbooks from storage."""
120
- raise NotImplementedError
121
-
122
- @abstractmethod
123
- def delete_all_user_playbooks_by_playbook_name(
124
- self, playbook_name: str, agent_version: str | None = None
125
- ) -> None:
126
- """Delete all user playbooks by playbook name from storage.
127
-
128
- Args:
129
- playbook_name (str): The playbook name to delete
130
- agent_version (str, optional): The agent version to filter by. If None, deletes all agent versions.
131
- """
132
- raise NotImplementedError
133
-
134
- @abstractmethod
135
- def delete_user_playbook(self, user_playbook_id: int) -> None:
136
- """Delete a user playbook by ID.
137
-
138
- Args:
139
- user_playbook_id (int): The ID of the user playbook to delete
140
- """
141
- raise NotImplementedError
142
-
143
- @abstractmethod
144
- def update_all_user_playbooks_status(
145
- self,
146
- old_status: Status | None,
147
- new_status: Status | None,
148
- agent_version: str | None = None,
149
- playbook_name: str | None = None,
150
- ) -> int:
151
- """Update all user playbooks with old_status to new_status atomically.
152
-
153
- Args:
154
- old_status: The current status to match (None for CURRENT)
155
- new_status: The new status to set (None for CURRENT)
156
- agent_version: Optional filter by agent version
157
- playbook_name: Optional filter by playbook name
158
-
159
- Returns:
160
- int: Number of user playbooks updated
161
- """
162
- raise NotImplementedError
163
-
164
- @abstractmethod
165
- def delete_all_user_playbooks_by_status(
166
- self,
167
- status: Status,
168
- agent_version: str | None = None,
169
- playbook_name: str | None = None,
170
- ) -> int:
171
- """Delete all user playbooks with the given status atomically.
172
-
173
- Args:
174
- status: The status of user playbooks to delete
175
- agent_version: Optional filter by agent version
176
- playbook_name: Optional filter by playbook name
177
-
178
- Returns:
179
- int: Number of user playbooks deleted
180
- """
181
- raise NotImplementedError
182
-
183
- @abstractmethod
184
- def delete_user_playbooks_by_ids(
185
- self, user_playbook_ids: list[int], *, emit_hard_delete: bool = True
186
- ) -> int:
187
- """Delete user playbooks by their IDs.
188
-
189
- Args:
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.
195
-
196
- Returns:
197
- int: Number of user playbooks deleted
198
- """
199
- raise NotImplementedError
200
-
201
- @abstractmethod
202
- def get_user_playbooks_by_ids(
203
- self,
204
- user_id: str,
205
- user_playbook_ids: list[int],
206
- status_filter: list[Status | None] | None = None,
207
- ) -> list[UserPlaybook]:
208
- """Fetch the subset of a user's playbooks whose ids are in the list.
209
-
210
- Server-side filter on (``user_id``, ``user_playbook_id IN (...)``)
211
- so callers (e.g. the reflection service resolving a small set of
212
- cited playbook ids) avoid scanning every playbook for the user.
213
-
214
- Args:
215
- user_id (str): Owning user id.
216
- user_playbook_ids (list[int]): Playbook ids to fetch. Empty
217
- list returns ``[]`` without hitting storage.
218
- status_filter (list[Status | None] | None): Statuses to
219
- include. ``None`` (default) means CURRENT only — same
220
- default as ``get_user_playbooks`` for consistency.
221
-
222
- Returns:
223
- list[UserPlaybook]: Matching playbooks. Order is unspecified.
224
- Ids that do not exist (or do not match the user / status
225
- filter) are silently omitted.
226
- """
227
- raise NotImplementedError
228
-
229
- @abstractmethod
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
- """
243
- raise NotImplementedError
244
-
245
- @abstractmethod
246
- def get_user_playbooks_by_ids_any_user(
247
- self,
248
- user_playbook_ids: list[int],
249
- status_filter: list[Status | None] | None = None,
250
- ) -> list[UserPlaybook]:
251
- """Fetch user playbooks by ids without requiring a single owner id."""
252
- raise NotImplementedError
253
-
254
- @abstractmethod
255
- def archive_user_playbook_by_id(self, user_id: str, user_playbook_id: int) -> bool:
256
- """Atomically archive a single user playbook by id, only if CURRENT.
257
-
258
- Flips the row's ``status`` from ``None`` (CURRENT) to
259
- ``Status.ARCHIVED``. No-op when the playbook does not exist, has
260
- a different ``user_id``, or is already non-current.
261
-
262
- Args:
263
- user_id (str): Owning user id; used as a guard so callers
264
- cannot accidentally archive another user's playbook.
265
- user_playbook_id (int): The user_playbook_id to archive.
266
-
267
- Returns:
268
- bool: True if a row was archived; False otherwise.
269
- """
270
- raise NotImplementedError
271
-
272
- @abstractmethod
273
- def has_user_playbooks_with_status(
274
- self,
275
- status: Status | None,
276
- agent_version: str | None = None,
277
- playbook_name: str | None = None,
278
- ) -> bool:
279
- """Check if any user playbooks exist with given status and filters.
280
-
281
- Args:
282
- status: The status to check for (None for CURRENT)
283
- agent_version: Optional filter by agent version
284
- playbook_name: Optional filter by playbook name
285
-
286
- Returns:
287
- bool: True if any matching user playbooks exist
288
- """
289
- raise NotImplementedError
290
-
291
- # ==============================
292
- # Agent Playbook methods
293
- # ==============================
294
-
295
- @abstractmethod
296
- def save_agent_playbooks(
297
- self, agent_playbooks: list[AgentPlaybook]
298
- ) -> list[AgentPlaybook]:
299
- """Save agent playbooks with embeddings.
300
-
301
- Args:
302
- agent_playbooks (list[AgentPlaybook]): List of agent playbook objects to save
303
-
304
- Returns:
305
- list[AgentPlaybook]: Saved agent playbooks with agent_playbook_id populated from storage
306
- """
307
- raise NotImplementedError
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
-
380
- @abstractmethod
381
- def get_agent_playbooks(
382
- self,
383
- limit: int = 100,
384
- playbook_name: str | None = None,
385
- agent_version: str | None = None,
386
- status_filter: list[Status | None] | None = None,
387
- playbook_status_filter: list[PlaybookStatus] | None = None,
388
- tags: list[str] | None = None,
389
- ) -> list[AgentPlaybook]:
390
- """Get agent playbooks from storage.
391
-
392
- Args:
393
- limit (int): Maximum number of agent playbooks to return
394
- playbook_name (str, optional): The playbook name to filter by. If None, returns all agent playbooks.
395
- agent_version (str, optional): The agent version to filter by. If None, returns all versions.
396
- status_filter (list[Optional[Status]], optional): List of Status values to filter by. None in the list means CURRENT status.
397
- playbook_status_filter (Optional[list[PlaybookStatus]]): List of PlaybookStatus values to filter by.
398
- If None, returns all playbook statuses.
399
- tags (list[str], optional): Match playbooks having any of these tags.
400
-
401
- Returns:
402
- list[AgentPlaybook]: List of agent playbook objects
403
- """
404
- raise NotImplementedError
405
-
406
- @abstractmethod
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
- """
420
- raise NotImplementedError
421
-
422
- @abstractmethod
423
- def delete_all_agent_playbooks(self) -> None:
424
- """Delete all agent playbooks from storage."""
425
- raise NotImplementedError
426
-
427
- @abstractmethod
428
- def delete_agent_playbook(self, agent_playbook_id: int) -> None:
429
- """Delete an agent playbook by ID.
430
-
431
- Args:
432
- agent_playbook_id (int): The ID of the agent playbook to delete
433
- """
434
- raise NotImplementedError
435
-
436
- @abstractmethod
437
- def delete_all_agent_playbooks_by_playbook_name(
438
- self, playbook_name: str, agent_version: str | None = None
439
- ) -> None:
440
- """Delete all agent playbooks by playbook name from storage.
441
-
442
- Args:
443
- playbook_name (str): The playbook name to delete
444
- agent_version (str, optional): The agent version to filter by. If None, deletes all agent versions.
445
- """
446
- raise NotImplementedError
447
-
448
- @abstractmethod
449
- def update_agent_playbook_status(
450
- self, agent_playbook_id: int, playbook_status: PlaybookStatus
451
- ) -> None:
452
- """Update the status of a specific agent playbook.
453
-
454
- Args:
455
- agent_playbook_id (int): The ID of the agent playbook to update
456
- playbook_status (PlaybookStatus): The new status to set
457
-
458
- Raises:
459
- ValueError: If agent playbook with the given ID is not found
460
- """
461
- raise NotImplementedError
462
-
463
- @abstractmethod
464
- def update_agent_playbook(
465
- self,
466
- agent_playbook_id: int,
467
- playbook_name: str | None = None,
468
- content: str | None = None,
469
- trigger: str | None = None,
470
- rationale: str | None = None,
471
- blocking_issue: BlockingIssue | None = None,
472
- playbook_status: PlaybookStatus | None = None,
473
- tags: list[str] | None = None,
474
- ) -> None:
475
- """Update editable fields of an agent playbook. Only non-None fields are updated.
476
-
477
- Args:
478
- agent_playbook_id (int): The ID of the agent playbook to update
479
- playbook_name (str, optional): New playbook name
480
- content (str, optional): New content text
481
- trigger (str, optional): New trigger text
482
- rationale (str, optional): New rationale text
483
- blocking_issue (BlockingIssue, optional): New blocking issue
484
- playbook_status (PlaybookStatus, optional): New playbook status
485
- tags (list[str], optional): Replacement tags
486
-
487
- Raises:
488
- ValueError: If agent playbook with the given ID is not found
489
- """
490
- raise NotImplementedError
491
-
492
- @abstractmethod
493
- def update_user_playbook(
494
- self,
495
- user_playbook_id: int,
496
- playbook_name: str | None = None,
497
- content: str | None = None,
498
- trigger: str | None = None,
499
- rationale: str | None = None,
500
- blocking_issue: BlockingIssue | None = None,
501
- tags: list[str] | None = None,
502
- ) -> None:
503
- """Update editable fields of a user playbook. Only non-None fields are updated.
504
-
505
- Args:
506
- user_playbook_id (int): The ID of the user playbook to update
507
- playbook_name (str, optional): New playbook name
508
- content (str, optional): New content text
509
- trigger (str, optional): New trigger text
510
- rationale (str, optional): New rationale text
511
- blocking_issue (BlockingIssue, optional): New blocking issue
512
- tags (list[str], optional): Replacement tags
513
-
514
- Raises:
515
- ValueError: If user playbook with the given ID is not found
516
- """
517
- raise NotImplementedError
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
-
540
- @abstractmethod
541
- def archive_agent_playbooks_by_playbook_name(
542
- self, playbook_name: str, agent_version: str | None = None
543
- ) -> None:
544
- """Archive non-APPROVED agent playbooks by setting their status field to 'archived'.
545
- APPROVED agent playbooks are left untouched to preserve user-approved playbooks.
546
-
547
- Args:
548
- playbook_name (str): The playbook name to archive
549
- agent_version (str, optional): The agent version to filter by. If None, archives all agent versions.
550
- """
551
- raise NotImplementedError
552
-
553
- @abstractmethod
554
- def archive_agent_playbooks_by_ids(self, agent_playbook_ids: list[int]) -> None:
555
- """Archive non-APPROVED agent playbooks by IDs, setting their status field to 'archived'.
556
- APPROVED agent playbooks are left untouched. No-op if agent_playbook_ids is empty.
557
-
558
- Args:
559
- agent_playbook_ids (list[int]): List of agent playbook IDs to archive
560
- """
561
- raise NotImplementedError
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
-
604
- @abstractmethod
605
- def restore_archived_agent_playbooks_by_playbook_name(
606
- self, playbook_name: str, agent_version: str | None = None
607
- ) -> None:
608
- """Restore archived agent playbooks by setting their status field to null.
609
-
610
- Args:
611
- playbook_name (str): The playbook name to restore
612
- agent_version (str, optional): The agent version to filter by. If None, restores all agent versions.
613
- """
614
- raise NotImplementedError
615
-
616
- @abstractmethod
617
- def restore_archived_agent_playbooks_by_ids(
618
- self, agent_playbook_ids: list[int]
619
- ) -> None:
620
- """Restore archived agent playbooks by IDs, setting their status field to null.
621
- No-op if agent_playbook_ids is empty.
622
-
623
- Args:
624
- agent_playbook_ids (list[int]): List of agent playbook IDs to restore
625
- """
626
- raise NotImplementedError
627
-
628
- # ==============================
629
- # Playbook optimization methods
630
- # ==============================
631
-
632
- @abstractmethod
633
- def set_source_user_playbook_ids_for_agent_playbook(
634
- self, agent_playbook_id: int, user_playbook_ids: list[int]
635
- ) -> None:
636
- """Persist the source user playbook ids that produced an agent playbook."""
637
- raise NotImplementedError
638
-
639
- @abstractmethod
640
- def get_source_user_playbook_ids_for_agent_playbook(
641
- self, agent_playbook_id: int
642
- ) -> list[int]:
643
- """Return source user playbook ids for an agent playbook."""
644
- raise NotImplementedError
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
-
653
- @abstractmethod
654
- def set_source_windows_for_agent_playbook(
655
- self,
656
- agent_playbook_id: int,
657
- source_windows: list[AgentPlaybookSourceWindow],
658
- ) -> None:
659
- """Persist replayable source windows that produced an agent playbook."""
660
- raise NotImplementedError
661
-
662
- @abstractmethod
663
- def get_source_windows_for_agent_playbook(
664
- self, agent_playbook_id: int
665
- ) -> list[AgentPlaybookSourceWindow]:
666
- """Return replayable source windows for an agent playbook."""
667
- raise NotImplementedError
668
-
669
- @abstractmethod
670
- def create_playbook_optimization_job(
671
- self, job: PlaybookOptimizationJob
672
- ) -> PlaybookOptimizationJob:
673
- """Persist a playbook optimization job and return it with id populated."""
674
- raise NotImplementedError
675
-
676
- @abstractmethod
677
- def update_playbook_optimization_job(
678
- self,
679
- job_id: int,
680
- *,
681
- status: str | None = None,
682
- best_candidate_id: int | None = None,
683
- successor_target_id: int | None = None,
684
- decision_reason: str | None = None,
685
- metadata_json: str | None = None,
686
- ) -> None:
687
- """Update mutable fields on a playbook optimization job."""
688
- raise NotImplementedError
689
-
690
- @abstractmethod
691
- def insert_playbook_optimization_candidate(
692
- self, candidate: PlaybookOptimizationCandidate
693
- ) -> PlaybookOptimizationCandidate:
694
- """Persist an optimizer candidate and return it with id populated."""
695
- raise NotImplementedError
696
-
697
- @abstractmethod
698
- def list_playbook_optimization_candidates(
699
- self, job_id: int
700
- ) -> list[PlaybookOptimizationCandidate]:
701
- """List optimizer candidates for a job."""
702
- raise NotImplementedError
703
-
704
- @abstractmethod
705
- def update_playbook_optimization_candidate(
706
- self,
707
- candidate_id: int,
708
- *,
709
- aggregate_score: float | None = None,
710
- is_winner: bool | None = None,
711
- ) -> None:
712
- """Update mutable optimizer candidate result fields."""
713
- raise NotImplementedError
714
-
715
- @abstractmethod
716
- def insert_playbook_optimization_evaluation(
717
- self, evaluation: PlaybookOptimizationEvaluation
718
- ) -> PlaybookOptimizationEvaluation:
719
- """Persist an optimizer evaluation and return it with id populated."""
720
- raise NotImplementedError
721
-
722
- @abstractmethod
723
- def list_playbook_optimization_evaluations(
724
- self, job_id: int
725
- ) -> list[PlaybookOptimizationEvaluation]:
726
- """List optimizer evaluations for a job."""
727
- raise NotImplementedError
728
-
729
- @abstractmethod
730
- def insert_playbook_optimization_event(
731
- self, event: PlaybookOptimizationEvent
732
- ) -> PlaybookOptimizationEvent:
733
- """Persist an optimizer callback/event and return it with id populated."""
734
- raise NotImplementedError
735
-
736
- @abstractmethod
737
- def delete_archived_agent_playbooks_by_playbook_name(
738
- self, playbook_name: str, agent_version: str | None = None
739
- ) -> None:
740
- """Permanently delete agent playbooks that have status='archived'.
741
-
742
- Args:
743
- playbook_name (str): The playbook name to delete
744
- agent_version (str, optional): The agent version to filter by. If None, deletes all agent versions.
745
- """
746
- raise NotImplementedError
747
-
748
- @abstractmethod
749
- def delete_agent_playbooks_by_ids(
750
- self, agent_playbook_ids: list[int], *, emit_hard_delete: bool = True
751
- ) -> None:
752
- """Permanently delete agent playbooks by their IDs.
753
- No-op if agent_playbook_ids is empty.
754
-
755
- Args:
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.
761
- """
762
- raise NotImplementedError
763
-
764
- # ==============================
765
- # Search methods
766
- # ==============================
767
-
768
- @abstractmethod
769
- def search_user_playbooks(
770
- self,
771
- request: SearchUserPlaybookRequest,
772
- options: SearchOptions | None = None,
773
- ) -> list[UserPlaybook]:
774
- """Search user playbooks with advanced filtering including semantic search.
775
-
776
- Args:
777
- request (SearchUserPlaybookRequest): Search request with query, filters, and pagination
778
- options (SearchOptions, optional): Engine-level search parameters (e.g. pre-computed embedding)
779
-
780
- Returns:
781
- list[UserPlaybook]: List of matching user playbook objects
782
- """
783
- raise NotImplementedError
784
-
785
- @abstractmethod
786
- def search_agent_playbooks(
787
- self,
788
- request: SearchAgentPlaybookRequest,
789
- options: SearchOptions | None = None,
790
- ) -> list[AgentPlaybook]:
791
- """Search agent playbooks with advanced filtering including semantic search.
792
-
793
- Args:
794
- request (SearchAgentPlaybookRequest): Search request with query, filters, and pagination
795
- options (SearchOptions, optional): Engine-level search parameters (e.g. pre-computed embedding)
796
-
797
- Returns:
798
- list[AgentPlaybook]: List of matching agent playbook objects
799
- """
800
- raise NotImplementedError
801
-
802
- # ==============================
803
- # Agent Success Evaluation methods
804
- # ==============================
805
-
806
- @abstractmethod
807
- def save_agent_success_evaluation_results(
808
- self, results: list[AgentSuccessEvaluationResult]
809
- ) -> None:
810
- """Save agent success evaluation results to storage.
811
-
812
- Args:
813
- results (list[AgentSuccessEvaluationResult]): List of agent success evaluation results to save
814
- """
815
- raise NotImplementedError
816
-
817
- @abstractmethod
818
- def get_agent_success_evaluation_results(
819
- self, limit: int = 100, agent_version: str | None = None
820
- ) -> list[AgentSuccessEvaluationResult]:
821
- """Get agent success evaluation results from storage.
822
-
823
- Args:
824
- limit (int): Maximum number of results to return
825
- agent_version (str, optional): The agent version to filter by. If None, returns all results.
826
-
827
- Returns:
828
- list[AgentSuccessEvaluationResult]: List of agent success evaluation result objects
829
- """
830
- raise NotImplementedError
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
-
871
- @abstractmethod
872
- def delete_all_agent_success_evaluation_results(self) -> None:
873
- """Delete all agent success evaluation results from storage."""
874
- raise NotImplementedError
875
-
876
- @abstractmethod
877
- def delete_agent_success_evaluation_results_for_session(
878
- self,
879
- user_id: str,
880
- session_id: str,
881
- evaluation_name: str,
882
- agent_version: str,
883
- ) -> int:
884
- """Delete stored results for (user_id, session_id, evaluation_name, agent_version).
885
-
886
- Args:
887
- user_id (str): User whose session results to clear.
888
- session_id (str): Session whose results to clear.
889
- evaluation_name (str): Which evaluator's results to clear.
890
- agent_version (str): Agent version scope.
891
-
892
- Returns:
893
- int: Number of rows deleted.
894
- """
895
- raise NotImplementedError
896
-
897
- @abstractmethod
898
- def delete_agent_success_evaluation_results_by_ids(
899
- self, result_ids: list[int]
900
- ) -> int:
901
- """Delete agent success eval results matching specific result_ids.
902
-
903
- Used by the regenerate flow to remove only the prior-run rows after the
904
- new rows have been saved durably (so an LLM/save failure cannot leave
905
- the session with zero rows).
906
-
907
- Args:
908
- result_ids (list[int]): Primary-key result_ids to delete.
909
-
910
- Returns:
911
- int: Number of rows deleted.
912
- """
913
- raise NotImplementedError