claude-smart 0.2.46 → 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 (170) 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/dashboard/app/sessions/[sessionId]/page.tsx +36 -2
  10. package/plugin/dashboard/package-lock.json +61 -390
  11. package/plugin/dashboard/package.json +2 -2
  12. package/plugin/opencode/dist/server.mjs +76 -2
  13. package/plugin/opencode/server.mts +79 -2
  14. package/plugin/pyproject.toml +6 -2
  15. package/plugin/scripts/smart-install.sh +7 -1
  16. package/plugin/src/claude_smart/cli.py +210 -22
  17. package/plugin/src/claude_smart/context_format.py +9 -9
  18. package/plugin/src/claude_smart/cs_cite.py +66 -19
  19. package/plugin/uv.lock +5 -5
  20. package/plugin/vendor/reflexio/.env.example +7 -0
  21. package/plugin/vendor/reflexio/README.md +3 -3
  22. package/plugin/vendor/reflexio/pyproject.toml +2 -1
  23. package/plugin/vendor/reflexio/reflexio/README.md +11 -6
  24. package/plugin/vendor/reflexio/reflexio/cli/bootstrap_config.py +1 -1
  25. package/plugin/vendor/reflexio/reflexio/cli/commands/setup_cmd.py +2 -2
  26. package/plugin/vendor/reflexio/reflexio/cli/utils.py +44 -1
  27. package/plugin/vendor/reflexio/reflexio/client/client.py +97 -0
  28. package/plugin/vendor/reflexio/reflexio/lib/_agent_playbook.py +8 -0
  29. package/plugin/vendor/reflexio/reflexio/lib/_base.py +15 -0
  30. package/plugin/vendor/reflexio/reflexio/lib/_config.py +23 -18
  31. package/plugin/vendor/reflexio/reflexio/lib/_generation.py +9 -8
  32. package/plugin/vendor/reflexio/reflexio/lib/_interactions.py +16 -1
  33. package/plugin/vendor/reflexio/reflexio/lib/_profiles.py +27 -16
  34. package/plugin/vendor/reflexio/reflexio/lib/_search.py +27 -5
  35. package/plugin/vendor/reflexio/reflexio/lib/_user_playbook.py +9 -0
  36. package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/__init__.py +1 -0
  37. package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/enums.py +1 -0
  38. package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/governance.py +117 -0
  39. package/plugin/vendor/reflexio/reflexio/models/api_schema/retriever_schema.py +45 -3
  40. package/plugin/vendor/reflexio/reflexio/models/config_schema.py +37 -0
  41. package/plugin/vendor/reflexio/reflexio/server/README.md +38 -9
  42. package/plugin/vendor/reflexio/reflexio/server/__init__.py +21 -2
  43. package/plugin/vendor/reflexio/reflexio/server/api.py +274 -3267
  44. package/plugin/vendor/reflexio/reflexio/server/api_endpoints/README.md +4 -3
  45. package/plugin/vendor/reflexio/reflexio/server/api_endpoints/publisher_api.py +16 -1
  46. package/plugin/vendor/reflexio/reflexio/server/api_endpoints/request_context.py +1 -1
  47. package/plugin/vendor/reflexio/reflexio/server/{_auth.py → auth.py} +2 -0
  48. package/plugin/vendor/reflexio/reflexio/server/cache/reflexio_cache.py +62 -36
  49. package/plugin/vendor/reflexio/reflexio/server/deployment_profile.py +69 -0
  50. package/plugin/vendor/reflexio/reflexio/server/extensions.py +213 -0
  51. package/plugin/vendor/reflexio/reflexio/server/llm/_litellm_embedding.py +424 -0
  52. package/plugin/vendor/reflexio/reflexio/server/llm/_litellm_json_extraction.py +249 -0
  53. package/plugin/vendor/reflexio/reflexio/server/llm/_litellm_structured_output.py +195 -0
  54. package/plugin/vendor/reflexio/reflexio/server/llm/_litellm_subprocess.py +152 -0
  55. package/plugin/vendor/reflexio/reflexio/server/llm/_litellm_text_generation.py +980 -0
  56. package/plugin/vendor/reflexio/reflexio/server/llm/_litellm_types.py +110 -0
  57. package/plugin/vendor/reflexio/reflexio/server/llm/litellm_client.py +73 -1819
  58. package/plugin/vendor/reflexio/reflexio/server/llm/model_defaults.py +4 -4
  59. package/plugin/vendor/reflexio/reflexio/server/llm/providers/claude_code_provider.py +57 -5
  60. package/plugin/vendor/reflexio/reflexio/server/llm/rerank/cross_encoder_reranker.py +12 -1
  61. package/plugin/vendor/reflexio/reflexio/server/middleware.py +244 -0
  62. package/plugin/vendor/reflexio/reflexio/server/operation_limiter.py +9 -1
  63. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.4.0.prompt.md +14 -2
  64. package/plugin/vendor/reflexio/reflexio/server/rate_limit.py +79 -0
  65. package/plugin/vendor/reflexio/reflexio/server/routes/__init__.py +6 -0
  66. package/plugin/vendor/reflexio/reflexio/server/routes/_common.py +25 -0
  67. package/plugin/vendor/reflexio/reflexio/server/routes/_metering.py +98 -0
  68. package/plugin/vendor/reflexio/reflexio/server/routes/braintrust.py +129 -0
  69. package/plugin/vendor/reflexio/reflexio/server/routes/config.py +210 -0
  70. package/plugin/vendor/reflexio/reflexio/server/routes/evaluation.py +549 -0
  71. package/plugin/vendor/reflexio/reflexio/server/routes/interactions.py +259 -0
  72. package/plugin/vendor/reflexio/reflexio/server/routes/playbooks.py +578 -0
  73. package/plugin/vendor/reflexio/reflexio/server/routes/profiles.py +423 -0
  74. package/plugin/vendor/reflexio/reflexio/server/routes/provenance.py +345 -0
  75. package/plugin/vendor/reflexio/reflexio/server/routes/search.py +349 -0
  76. package/plugin/vendor/reflexio/reflexio/server/routes/system.py +261 -0
  77. package/plugin/vendor/reflexio/reflexio/server/scheduling.py +132 -0
  78. package/plugin/vendor/reflexio/reflexio/server/services/README.md +5 -2
  79. package/plugin/vendor/reflexio/reflexio/server/services/base_generation/__init__.py +38 -0
  80. package/plugin/vendor/reflexio/reflexio/server/services/base_generation/_batch_progress.py +298 -0
  81. package/plugin/vendor/reflexio/reflexio/server/services/base_generation/_config_filter.py +152 -0
  82. package/plugin/vendor/reflexio/reflexio/server/services/base_generation/_extraction_lifecycle.py +243 -0
  83. package/plugin/vendor/reflexio/reflexio/server/services/base_generation/_should_run.py +299 -0
  84. package/plugin/vendor/reflexio/reflexio/server/services/base_generation/_status_change.py +273 -0
  85. package/plugin/vendor/reflexio/reflexio/server/services/base_generation/_usage_billing.py +258 -0
  86. package/plugin/vendor/reflexio/reflexio/server/services/base_generation_service.py +17 -1183
  87. package/plugin/vendor/reflexio/reflexio/server/services/deduplication_utils.py +8 -1
  88. package/plugin/vendor/reflexio/reflexio/server/services/extraction/resume_scheduler.py +26 -41
  89. package/plugin/vendor/reflexio/reflexio/server/services/extraction/resume_worker.py +8 -0
  90. package/plugin/vendor/reflexio/reflexio/server/services/generation_service.py +232 -123
  91. package/plugin/vendor/reflexio/reflexio/server/services/governance/config.py +52 -0
  92. package/plugin/vendor/reflexio/reflexio/server/services/governance/service.py +378 -0
  93. package/plugin/vendor/reflexio/reflexio/server/services/governance/subject_refs.py +34 -0
  94. package/plugin/vendor/reflexio/reflexio/server/services/lineage/gc_scheduler.py +385 -78
  95. package/plugin/vendor/reflexio/reflexio/server/services/playbook/README.md +9 -1
  96. package/plugin/vendor/reflexio/reflexio/server/services/playbook/aggregation_prompt_processing.py +100 -0
  97. package/plugin/vendor/reflexio/reflexio/server/services/playbook/components/aggregator.py +121 -525
  98. package/plugin/vendor/reflexio/reflexio/server/services/playbook/components/aggregator_clustering.py +184 -0
  99. package/plugin/vendor/reflexio/reflexio/server/services/playbook/components/aggregator_postprocessing.py +130 -0
  100. package/plugin/vendor/reflexio/reflexio/server/services/playbook/components/aggregator_prompt_formatting.py +212 -0
  101. package/plugin/vendor/reflexio/reflexio/server/services/playbook/components/consolidator.py +258 -69
  102. package/plugin/vendor/reflexio/reflexio/server/services/playbook/service.py +9 -9
  103. package/plugin/vendor/reflexio/reflexio/server/services/profile/components/extractor.py +3 -2
  104. package/plugin/vendor/reflexio/reflexio/server/services/publish_learning_worker.py +288 -0
  105. package/plugin/vendor/reflexio/reflexio/server/services/retrieval/recency.py +211 -0
  106. package/plugin/vendor/reflexio/reflexio/server/services/retrieval/relevance_floor.py +29 -13
  107. package/plugin/vendor/reflexio/reflexio/server/services/storage/error.py +4 -0
  108. package/plugin/vendor/reflexio/reflexio/server/services/storage/governance_validation.py +681 -0
  109. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/__init__.py +43 -6
  110. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_agent_run.py +10 -1167
  111. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_base.py +58 -351
  112. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_extras.py +49 -19
  113. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_governance.py +452 -0
  114. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_lineage.py +11 -4
  115. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_playbook.py +1 -2133
  116. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_profiles.py +7 -1126
  117. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_requests.py +73 -33
  118. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_share_links.py +30 -0
  119. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/agent_run/__init__.py +9 -0
  120. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/agent_run/_agent_run_store.py +506 -0
  121. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/agent_run/_pending_tool_call_store.py +704 -0
  122. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/agent_run/_run_tool_dependency_store.py +123 -0
  123. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/base/__init__.py +6 -0
  124. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/base/_deletion.py +263 -0
  125. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/base/_fts_vec.py +132 -0
  126. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/governance/__init__.py +13 -0
  127. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/governance/_audit.py +122 -0
  128. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/governance/_erase_execution.py +465 -0
  129. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/governance/_purge.py +387 -0
  130. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/governance/_rebuild_hide.py +332 -0
  131. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/governance/_subject_barrier.py +511 -0
  132. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/__init__.py +13 -0
  133. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_agent.py +955 -0
  134. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_eval_results.py +189 -0
  135. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_optimization.py +247 -0
  136. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_source_linkage.py +145 -0
  137. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_user.py +844 -0
  138. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/profiles/__init__.py +9 -0
  139. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/profiles/_interaction_store.py +263 -0
  140. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/profiles/_profile_store.py +896 -0
  141. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/profiles/_search.py +270 -0
  142. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/__init__.py +50 -9
  143. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_agent_run.py +64 -370
  144. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_extras.py +4 -4
  145. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_playbook.py +0 -909
  146. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_requests.py +2 -0
  147. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_share_links.py +20 -0
  148. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/agent_run/__init__.py +9 -0
  149. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/agent_run/_agent_run_store.py +86 -0
  150. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/agent_run/_models.py +195 -0
  151. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/agent_run/_pending_tool_call_store.py +148 -0
  152. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/agent_run/_run_tool_dependency_store.py +38 -0
  153. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/governance/__init__.py +13 -0
  154. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/governance/_audit.py +29 -0
  155. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/governance/_erase_execution.py +30 -0
  156. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/governance/_purge.py +74 -0
  157. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/governance/_rebuild_hide.py +32 -0
  158. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/governance/_subject_barrier.py +49 -0
  159. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/__init__.py +13 -0
  160. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_agent.py +365 -0
  161. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_eval_results.py +124 -0
  162. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_optimization.py +85 -0
  163. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_source_linkage.py +47 -0
  164. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_user.py +333 -0
  165. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/profiles/__init__.py +9 -0
  166. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/profiles/_interaction_store.py +73 -0
  167. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/{_profiles.py → profiles/_profile_store.py} +57 -86
  168. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/profiles/_search.py +32 -0
  169. package/plugin/vendor/reflexio/reflexio/server/services/unified_search_service.py +153 -12
  170. package/plugin/vendor/reflexio/reflexio/server/services/playbook/user_detail_stripping.py +0 -84
@@ -0,0 +1,32 @@
1
+ from __future__ import annotations
2
+
3
+ from abc import ABC, abstractmethod
4
+
5
+
6
+ class RebuildHideMixin(ABC):
7
+ """Backend-neutral governance rebuild-hide store contract.
8
+
9
+ Extracted verbatim from ``_governance.py`` (the RebuildHide bucket): the two
10
+ public methods (``hide_governance_agent_playbooks_for_rebuild``,
11
+ ``apply_governance_agent_playbook_rebuild``). The residual ``GovernanceMixin``
12
+ ABC stays composed alongside this mixin.
13
+ """
14
+
15
+ @abstractmethod
16
+ def hide_governance_agent_playbooks_for_rebuild(self, purge_id: str) -> list[int]:
17
+ raise NotImplementedError
18
+
19
+ @abstractmethod
20
+ def apply_governance_agent_playbook_rebuild(
21
+ self,
22
+ purge_id: str,
23
+ agent_playbook_id: int,
24
+ remaining_source_windows: list[dict[str, object]],
25
+ content: str | None,
26
+ trigger: str | None,
27
+ rationale: str | None,
28
+ blocking_issue: dict[str, object] | None,
29
+ expanded_terms: str | None,
30
+ tags: list[str] | None,
31
+ ) -> None:
32
+ raise NotImplementedError
@@ -0,0 +1,49 @@
1
+ from __future__ import annotations
2
+
3
+ from abc import ABC, abstractmethod
4
+
5
+ from reflexio.models.api_schema.domain.governance import (
6
+ AuditEvent,
7
+ PurgeOperation,
8
+ SubjectWriteBarrier,
9
+ )
10
+
11
+
12
+ class SubjectBarrierMixin(ABC):
13
+ """Backend-neutral subject-erasure-barrier store contract.
14
+
15
+ Extracted verbatim from ``_governance.py`` (the SubjectBarrier bucket). The
16
+ residual ``GovernanceMixin`` ABC stays composed alongside this mixin and
17
+ holds the remaining rebuild-hide / governance-erase-execution / purge-complete
18
+ abstract methods.
19
+ """
20
+
21
+ @abstractmethod
22
+ def begin_subject_erasure_barrier(
23
+ self, subject_ref: str, purge_id: str
24
+ ) -> SubjectWriteBarrier:
25
+ raise NotImplementedError
26
+
27
+ @abstractmethod
28
+ def assert_subject_writable(self, subject_ref: str) -> None:
29
+ raise NotImplementedError
30
+
31
+ @abstractmethod
32
+ def complete_subject_erasure_barrier_after_empty_check(
33
+ self, purge_id: str, audit_event: AuditEvent
34
+ ) -> PurgeOperation:
35
+ raise NotImplementedError
36
+
37
+ @abstractmethod
38
+ def fail_subject_erasure_barrier(
39
+ self,
40
+ subject_ref: str,
41
+ purge_id: str,
42
+ error_code: str,
43
+ error_detail: str,
44
+ ) -> SubjectWriteBarrier:
45
+ raise NotImplementedError
46
+
47
+ @abstractmethod
48
+ def get_subject_write_barrier(self, subject_ref: str) -> SubjectWriteBarrier | None:
49
+ raise NotImplementedError
@@ -0,0 +1,13 @@
1
+ from ._agent import AgentPlaybookStoreMixin
2
+ from ._eval_results import AgentEvaluationResultStoreMixin
3
+ from ._optimization import OptimizationJobStoreMixin
4
+ from ._source_linkage import PlaybookSourceLinkageMixin
5
+ from ._user import UserPlaybookStoreMixin
6
+
7
+ __all__ = [
8
+ "AgentEvaluationResultStoreMixin",
9
+ "AgentPlaybookStoreMixin",
10
+ "OptimizationJobStoreMixin",
11
+ "PlaybookSourceLinkageMixin",
12
+ "UserPlaybookStoreMixin",
13
+ ]
@@ -0,0 +1,365 @@
1
+ """Abstract agent playbook CRUD + search declarations."""
2
+
3
+ import logging
4
+ from abc import abstractmethod
5
+
6
+ from reflexio.models.api_schema.common import BlockingIssue
7
+ from reflexio.models.api_schema.domain import (
8
+ AgentPlaybook,
9
+ PlaybookStatus,
10
+ Status,
11
+ )
12
+ from reflexio.models.api_schema.domain.entities import LineageEvent
13
+ from reflexio.models.api_schema.retriever_schema import (
14
+ SearchAgentPlaybookRequest,
15
+ )
16
+ from reflexio.models.config_schema import SearchOptions
17
+ from reflexio.server.tracing import capture_anomaly
18
+
19
+ from .._playbook import AGGREGATE_REASON_PREFIX
20
+
21
+ logger = logging.getLogger(__name__)
22
+
23
+ _AGGREGATE_EVENT_EMIT_ATTEMPTS = 3
24
+
25
+
26
+ class AgentPlaybookStoreMixin:
27
+ """Abstract agent playbook CRUD + search methods."""
28
+
29
+ @abstractmethod
30
+ def save_agent_playbooks(
31
+ self, agent_playbooks: list[AgentPlaybook]
32
+ ) -> list[AgentPlaybook]:
33
+ """Save agent playbooks with embeddings.
34
+
35
+ Args:
36
+ agent_playbooks (list[AgentPlaybook]): List of agent playbook objects to save
37
+
38
+ Returns:
39
+ list[AgentPlaybook]: Saved agent playbooks with agent_playbook_id populated from storage
40
+ """
41
+ raise NotImplementedError
42
+
43
+ def save_agent_playbook_with_aggregate_event(
44
+ self,
45
+ agent_playbook: AgentPlaybook,
46
+ *,
47
+ source_ids: list[str],
48
+ request_id: str,
49
+ run_mode: str,
50
+ ) -> AgentPlaybook:
51
+ """Persist an agent playbook AND its ``op=aggregate`` lineage event.
52
+
53
+ Backends SHOULD override this so the row insert and the event commit in ONE
54
+ transaction (the event is the sole record of the run->playbook membership for
55
+ reconstruction). This base default is a non-atomic save-then-emit fallback
56
+ with bounded retry + loud (level=error) on final failure.
57
+
58
+ Args:
59
+ agent_playbook (AgentPlaybook): The playbook to persist.
60
+ source_ids (list[str]): IDs of the source entities that produced this playbook.
61
+ request_id (str): The aggregation run ID (used as the lineage event request_id).
62
+ run_mode (str): The aggregation run mode (e.g. ``full_archive`` or ``incremental``).
63
+
64
+ Returns:
65
+ AgentPlaybook: The saved playbook with ``agent_playbook_id`` populated.
66
+
67
+ Raises:
68
+ ValueError: If ``request_id`` is empty (would produce an unreconstructable event).
69
+ """
70
+ if not request_id or not request_id.strip():
71
+ raise ValueError(
72
+ "save_agent_playbook_with_aggregate_event requires a non-empty request_id"
73
+ )
74
+ saved = self.save_agent_playbooks([agent_playbook])[0]
75
+ event = LineageEvent(
76
+ org_id=self.org_id, # type: ignore[attr-defined]
77
+ entity_type="agent_playbook",
78
+ entity_id=str(saved.agent_playbook_id),
79
+ op="aggregate",
80
+ prov_relation="wasDerivedFrom",
81
+ source_ids=source_ids,
82
+ actor="aggregator",
83
+ request_id=request_id,
84
+ reason=f"{AGGREGATE_REASON_PREFIX}{run_mode}",
85
+ )
86
+ # The row is already committed; this default is non-atomic (SQLite overrides it to
87
+ # make the INSERT + event one transaction). The event is the sole reconstruction signal
88
+ # for the run, so make the emit durable: bounded retry (idempotent on retrying the
89
+ # same row's emit — entity_id is a fresh autoincrement per run, so this is NOT
90
+ # cross-run idempotency), and on final failure fail LOUD at level=error so the gap
91
+ # is paged + backfillable rather than silently lost. Never raise — the playbook
92
+ # itself is saved and must not be lost.
93
+ for attempt in range(_AGGREGATE_EVENT_EMIT_ATTEMPTS):
94
+ try:
95
+ self.append_lineage_event(event) # type: ignore[attr-defined]
96
+ return saved
97
+ except Exception: # noqa: BLE001
98
+ logger.warning(
99
+ "aggregate lineage event append failed (attempt %d/%d) for agent_playbook %s",
100
+ attempt + 1,
101
+ _AGGREGATE_EVENT_EMIT_ATTEMPTS,
102
+ saved.agent_playbook_id,
103
+ exc_info=True,
104
+ )
105
+ capture_anomaly(
106
+ "lineage.aggregate.append_failed",
107
+ level="error",
108
+ entity_id=str(saved.agent_playbook_id),
109
+ org_id=self.org_id, # type: ignore[attr-defined]
110
+ request_id=request_id,
111
+ )
112
+ return saved
113
+
114
+ @abstractmethod
115
+ def get_agent_playbooks(
116
+ self,
117
+ limit: int = 100,
118
+ playbook_name: str | None = None,
119
+ agent_version: str | None = None,
120
+ status_filter: list[Status | None] | None = None,
121
+ playbook_status_filter: list[PlaybookStatus] | None = None,
122
+ tags: list[str] | None = None,
123
+ agent_playbook_id: int | None = None,
124
+ query: str | None = None,
125
+ start_time: int | None = None,
126
+ end_time: int | None = None,
127
+ ) -> list[AgentPlaybook]:
128
+ """Get agent playbooks from storage.
129
+
130
+ Args:
131
+ limit (int): Maximum number of agent playbooks to return
132
+ agent_playbook_id (int, optional): Exact agent playbook ID to retrieve.
133
+ query (str, optional): Case-insensitive text filter across visible fields.
134
+ playbook_name (str, optional): The playbook name to filter by. If None, returns all agent playbooks.
135
+ agent_version (str, optional): The agent version to filter by. If None, returns all versions.
136
+ start_time (int, optional): Unix timestamp. Only return playbooks created at or after this time.
137
+ end_time (int, optional): Unix timestamp. Only return playbooks created at or before this time.
138
+ status_filter (list[Optional[Status]], optional): List of Status values to filter by. None in the list means CURRENT status.
139
+ playbook_status_filter (Optional[list[PlaybookStatus]]): List of PlaybookStatus values to filter by.
140
+ If None, returns all playbook statuses.
141
+ tags (list[str], optional): Match playbooks having any of these tags.
142
+
143
+ Returns:
144
+ list[AgentPlaybook]: List of agent playbook objects
145
+ """
146
+ raise NotImplementedError
147
+
148
+ @abstractmethod
149
+ def get_agent_playbook_by_id(
150
+ self, agent_playbook_id: int, *, include_tombstones: bool = False
151
+ ) -> AgentPlaybook | None:
152
+ """Fetch one agent playbook by primary key.
153
+
154
+ Args:
155
+ agent_playbook_id: The agent_playbook_id to look up.
156
+ include_tombstones: When False (default), MERGED/SUPERSEDED rows
157
+ return None. Set to True for lineage resolution (resolve_current).
158
+
159
+ Returns:
160
+ The AgentPlaybook if found and not filtered, otherwise None.
161
+ """
162
+ raise NotImplementedError
163
+
164
+ @abstractmethod
165
+ def delete_all_agent_playbooks(self) -> None:
166
+ """Delete all agent playbooks from storage."""
167
+ raise NotImplementedError
168
+
169
+ @abstractmethod
170
+ def delete_agent_playbook(self, agent_playbook_id: int) -> None:
171
+ """Delete an agent playbook by ID.
172
+
173
+ Args:
174
+ agent_playbook_id (int): The ID of the agent playbook to delete
175
+ """
176
+ raise NotImplementedError
177
+
178
+ @abstractmethod
179
+ def delete_all_agent_playbooks_by_playbook_name(
180
+ self, playbook_name: str, agent_version: str | None = None
181
+ ) -> None:
182
+ """Delete all agent playbooks by playbook name from storage.
183
+
184
+ Args:
185
+ playbook_name (str): The playbook name to delete
186
+ agent_version (str, optional): The agent version to filter by. If None, deletes all agent versions.
187
+ """
188
+ raise NotImplementedError
189
+
190
+ @abstractmethod
191
+ def update_agent_playbook_status(
192
+ self, agent_playbook_id: int, playbook_status: PlaybookStatus
193
+ ) -> None:
194
+ """Update the status of a specific agent playbook.
195
+
196
+ Args:
197
+ agent_playbook_id (int): The ID of the agent playbook to update
198
+ playbook_status (PlaybookStatus): The new status to set
199
+
200
+ Raises:
201
+ ValueError: If agent playbook with the given ID is not found
202
+ """
203
+ raise NotImplementedError
204
+
205
+ @abstractmethod
206
+ def update_agent_playbook(
207
+ self,
208
+ agent_playbook_id: int,
209
+ playbook_name: str | None = None,
210
+ content: str | None = None,
211
+ trigger: str | None = None,
212
+ rationale: str | None = None,
213
+ blocking_issue: BlockingIssue | None = None,
214
+ playbook_status: PlaybookStatus | None = None,
215
+ tags: list[str] | None = None,
216
+ ) -> None:
217
+ """Update editable fields of an agent playbook. Only non-None fields are updated.
218
+
219
+ Args:
220
+ agent_playbook_id (int): The ID of the agent playbook to update
221
+ playbook_name (str, optional): New playbook name
222
+ content (str, optional): New content text
223
+ trigger (str, optional): New trigger text
224
+ rationale (str, optional): New rationale text
225
+ blocking_issue (BlockingIssue, optional): New blocking issue
226
+ playbook_status (PlaybookStatus, optional): New playbook status
227
+ tags (list[str], optional): Replacement tags
228
+
229
+ Raises:
230
+ ValueError: If agent playbook with the given ID is not found
231
+ """
232
+ raise NotImplementedError
233
+
234
+ @abstractmethod
235
+ def archive_agent_playbooks_by_playbook_name(
236
+ self, playbook_name: str, agent_version: str | None = None
237
+ ) -> None:
238
+ """Archive non-APPROVED agent playbooks by setting their status field to 'archived'.
239
+ APPROVED agent playbooks are left untouched to preserve user-approved playbooks.
240
+
241
+ Args:
242
+ playbook_name (str): The playbook name to archive
243
+ agent_version (str, optional): The agent version to filter by. If None, archives all agent versions.
244
+ """
245
+ raise NotImplementedError
246
+
247
+ @abstractmethod
248
+ def archive_agent_playbooks_by_ids(self, agent_playbook_ids: list[int]) -> None:
249
+ """Archive non-APPROVED agent playbooks by IDs, setting their status field to 'archived'.
250
+ APPROVED agent playbooks are left untouched. No-op if agent_playbook_ids is empty.
251
+
252
+ Args:
253
+ agent_playbook_ids (list[int]): List of agent playbook IDs to archive
254
+ """
255
+ raise NotImplementedError
256
+
257
+ @abstractmethod
258
+ def supersede_agent_playbooks_by_ids(
259
+ self, agent_playbook_ids: list[int], request_id: str
260
+ ) -> int:
261
+ """Soft-delete agent playbooks by setting status to SUPERSEDED, emitting set-based lineage.
262
+
263
+ For each eligible id (not APPROVED, not already tombstoned), updates status to
264
+ SUPERSEDED and emits one status_change event under the shared request_id.
265
+ Atomic: mutation and event in one commit, guarded on rowcount.
266
+ FTS/vec rows are NOT removed.
267
+
268
+ Args:
269
+ agent_playbook_ids (list[int]): Agent playbook ids to supersede.
270
+ request_id (str): Shared request id for all emitted lineage events.
271
+
272
+ Returns:
273
+ int: Number of agent playbooks actually updated.
274
+ """
275
+ raise NotImplementedError
276
+
277
+ @abstractmethod
278
+ def supersede_agent_playbooks_by_playbook_name(
279
+ self, playbook_name: str, agent_version: str | None, request_id: str
280
+ ) -> int:
281
+ """Soft-delete archived agent playbooks by name/version via SUPERSEDED status.
282
+
283
+ Selects rows with playbook_name matching and status='archived', then
284
+ soft-supersedes each one with a status_change lineage event under request_id.
285
+ Atomic: one commit at the end.
286
+ FTS/vec rows are NOT removed.
287
+
288
+ Args:
289
+ playbook_name (str): Playbook name to supersede.
290
+ agent_version (str | None): Agent version filter. None matches all versions.
291
+ request_id (str): Shared request id for all emitted lineage events.
292
+
293
+ Returns:
294
+ int: Number of agent playbooks actually updated.
295
+ """
296
+ raise NotImplementedError
297
+
298
+ @abstractmethod
299
+ def restore_archived_agent_playbooks_by_playbook_name(
300
+ self, playbook_name: str, agent_version: str | None = None
301
+ ) -> None:
302
+ """Restore archived agent playbooks by setting their status field to null.
303
+
304
+ Args:
305
+ playbook_name (str): The playbook name to restore
306
+ agent_version (str, optional): The agent version to filter by. If None, restores all agent versions.
307
+ """
308
+ raise NotImplementedError
309
+
310
+ @abstractmethod
311
+ def restore_archived_agent_playbooks_by_ids(
312
+ self, agent_playbook_ids: list[int]
313
+ ) -> None:
314
+ """Restore archived agent playbooks by IDs, setting their status field to null.
315
+ No-op if agent_playbook_ids is empty.
316
+
317
+ Args:
318
+ agent_playbook_ids (list[int]): List of agent playbook IDs to restore
319
+ """
320
+ raise NotImplementedError
321
+
322
+ @abstractmethod
323
+ def delete_archived_agent_playbooks_by_playbook_name(
324
+ self, playbook_name: str, agent_version: str | None = None
325
+ ) -> None:
326
+ """Permanently delete agent playbooks that have status='archived'.
327
+
328
+ Args:
329
+ playbook_name (str): The playbook name to delete
330
+ agent_version (str, optional): The agent version to filter by. If None, deletes all agent versions.
331
+ """
332
+ raise NotImplementedError
333
+
334
+ @abstractmethod
335
+ def delete_agent_playbooks_by_ids(
336
+ self, agent_playbook_ids: list[int], *, emit_hard_delete: bool = True
337
+ ) -> None:
338
+ """Permanently delete agent playbooks by their IDs.
339
+ No-op if agent_playbook_ids is empty.
340
+
341
+ Args:
342
+ agent_playbook_ids (list[int]): List of agent playbook IDs to delete
343
+ emit_hard_delete: When True (default), append a ``hard_delete``
344
+ lineage event per id (genuine erasure). Set False for rollback
345
+ cleanup of a never-live row (e.g. a lost supersede CAS), so no
346
+ spurious audit event is recorded.
347
+ """
348
+ raise NotImplementedError
349
+
350
+ @abstractmethod
351
+ def search_agent_playbooks(
352
+ self,
353
+ request: SearchAgentPlaybookRequest,
354
+ options: SearchOptions | None = None,
355
+ ) -> list[AgentPlaybook]:
356
+ """Search agent playbooks with advanced filtering including semantic search.
357
+
358
+ Args:
359
+ request (SearchAgentPlaybookRequest): Search request with query, filters, and pagination
360
+ options (SearchOptions, optional): Engine-level search parameters (e.g. pre-computed embedding)
361
+
362
+ Returns:
363
+ list[AgentPlaybook]: List of matching agent playbook objects
364
+ """
365
+ raise NotImplementedError
@@ -0,0 +1,124 @@
1
+ """Abstract agent success evaluation result store declarations."""
2
+
3
+ from abc import abstractmethod
4
+
5
+ from reflexio.models.api_schema.domain import (
6
+ AgentSuccessEvaluationResult,
7
+ )
8
+
9
+
10
+ class AgentEvaluationResultStoreMixin:
11
+ """Abstract agent success evaluation result store methods."""
12
+
13
+ # ==============================
14
+ # Agent Success Evaluation methods
15
+ # ==============================
16
+
17
+ @abstractmethod
18
+ def save_agent_success_evaluation_results(
19
+ self, results: list[AgentSuccessEvaluationResult]
20
+ ) -> None:
21
+ """Save agent success evaluation results to storage.
22
+
23
+ Args:
24
+ results (list[AgentSuccessEvaluationResult]): List of agent success evaluation results to save
25
+ """
26
+ raise NotImplementedError
27
+
28
+ @abstractmethod
29
+ def get_agent_success_evaluation_results(
30
+ self, limit: int = 100, agent_version: str | None = None
31
+ ) -> list[AgentSuccessEvaluationResult]:
32
+ """Get agent success evaluation results from storage.
33
+
34
+ Args:
35
+ limit (int): Maximum number of results to return
36
+ agent_version (str, optional): The agent version to filter by. If None, returns all results.
37
+
38
+ Returns:
39
+ list[AgentSuccessEvaluationResult]: List of agent success evaluation result objects
40
+ """
41
+ raise NotImplementedError
42
+
43
+ def get_agent_success_evaluation_results_in_window(
44
+ self,
45
+ from_ts: int,
46
+ to_ts: int,
47
+ agent_version: str | None = None,
48
+ limit: int | None = None,
49
+ ) -> list[AgentSuccessEvaluationResult]:
50
+ """Return eval results in ``[from_ts, to_ts]``.
51
+
52
+ Default implementation filters the existing latest-results method.
53
+ SQL backends should override so callers do not depend on an arbitrary
54
+ latest-row cap.
55
+ """
56
+ rows = self.get_agent_success_evaluation_results(
57
+ limit=limit or 10_000,
58
+ agent_version=agent_version,
59
+ )
60
+ return [r for r in rows if from_ts <= r.created_at <= to_ts]
61
+
62
+ def get_agent_success_evaluation_result_ids(
63
+ self,
64
+ user_id: str,
65
+ session_id: str,
66
+ evaluation_name: str,
67
+ agent_version: str,
68
+ ) -> list[int]:
69
+ """Return result ids for one eval identity tuple."""
70
+ rows = self.get_agent_success_evaluation_results(
71
+ limit=10_000,
72
+ agent_version=agent_version,
73
+ )
74
+ return [
75
+ r.result_id
76
+ for r in rows
77
+ if r.user_id == user_id
78
+ and r.session_id == session_id
79
+ and r.evaluation_name == evaluation_name
80
+ ]
81
+
82
+ @abstractmethod
83
+ def delete_all_agent_success_evaluation_results(self) -> None:
84
+ """Delete all agent success evaluation results from storage."""
85
+ raise NotImplementedError
86
+
87
+ @abstractmethod
88
+ def delete_agent_success_evaluation_results_for_session(
89
+ self,
90
+ user_id: str,
91
+ session_id: str,
92
+ evaluation_name: str,
93
+ agent_version: str,
94
+ ) -> int:
95
+ """Delete stored results for (user_id, session_id, evaluation_name, agent_version).
96
+
97
+ Args:
98
+ user_id (str): User whose session results to clear.
99
+ session_id (str): Session whose results to clear.
100
+ evaluation_name (str): Which evaluator's results to clear.
101
+ agent_version (str): Agent version scope.
102
+
103
+ Returns:
104
+ int: Number of rows deleted.
105
+ """
106
+ raise NotImplementedError
107
+
108
+ @abstractmethod
109
+ def delete_agent_success_evaluation_results_by_ids(
110
+ self, result_ids: list[int]
111
+ ) -> int:
112
+ """Delete agent success eval results matching specific result_ids.
113
+
114
+ Used by the regenerate flow to remove only the prior-run rows after the
115
+ new rows have been saved durably (so an LLM/save failure cannot leave
116
+ the session with zero rows).
117
+
118
+ Args:
119
+ result_ids (list[int]): Primary-key result_ids to delete.
120
+
121
+ Returns:
122
+ int: Number of rows deleted.
123
+ """
124
+ raise NotImplementedError
@@ -0,0 +1,85 @@
1
+ """Abstract playbook optimization job store declarations."""
2
+
3
+ from abc import abstractmethod
4
+
5
+ from reflexio.models.api_schema.domain import (
6
+ PlaybookOptimizationCandidate,
7
+ PlaybookOptimizationEvaluation,
8
+ PlaybookOptimizationEvent,
9
+ PlaybookOptimizationJob,
10
+ )
11
+
12
+
13
+ class OptimizationJobStoreMixin:
14
+ """Abstract playbook optimization job/candidate/evaluation store methods."""
15
+
16
+ # ==============================
17
+ # Playbook optimization methods
18
+ # ==============================
19
+
20
+ @abstractmethod
21
+ def create_playbook_optimization_job(
22
+ self, job: PlaybookOptimizationJob
23
+ ) -> PlaybookOptimizationJob:
24
+ """Persist a playbook optimization job and return it with id populated."""
25
+ raise NotImplementedError
26
+
27
+ @abstractmethod
28
+ def update_playbook_optimization_job(
29
+ self,
30
+ job_id: int,
31
+ *,
32
+ status: str | None = None,
33
+ best_candidate_id: int | None = None,
34
+ successor_target_id: int | None = None,
35
+ decision_reason: str | None = None,
36
+ metadata_json: str | None = None,
37
+ ) -> None:
38
+ """Update mutable fields on a playbook optimization job."""
39
+ raise NotImplementedError
40
+
41
+ @abstractmethod
42
+ def insert_playbook_optimization_candidate(
43
+ self, candidate: PlaybookOptimizationCandidate
44
+ ) -> PlaybookOptimizationCandidate:
45
+ """Persist an optimizer candidate and return it with id populated."""
46
+ raise NotImplementedError
47
+
48
+ @abstractmethod
49
+ def list_playbook_optimization_candidates(
50
+ self, job_id: int
51
+ ) -> list[PlaybookOptimizationCandidate]:
52
+ """List optimizer candidates for a job."""
53
+ raise NotImplementedError
54
+
55
+ @abstractmethod
56
+ def update_playbook_optimization_candidate(
57
+ self,
58
+ candidate_id: int,
59
+ *,
60
+ aggregate_score: float | None = None,
61
+ is_winner: bool | None = None,
62
+ ) -> None:
63
+ """Update mutable optimizer candidate result fields."""
64
+ raise NotImplementedError
65
+
66
+ @abstractmethod
67
+ def insert_playbook_optimization_evaluation(
68
+ self, evaluation: PlaybookOptimizationEvaluation
69
+ ) -> PlaybookOptimizationEvaluation:
70
+ """Persist an optimizer evaluation and return it with id populated."""
71
+ raise NotImplementedError
72
+
73
+ @abstractmethod
74
+ def list_playbook_optimization_evaluations(
75
+ self, job_id: int
76
+ ) -> list[PlaybookOptimizationEvaluation]:
77
+ """List optimizer evaluations for a job."""
78
+ raise NotImplementedError
79
+
80
+ @abstractmethod
81
+ def insert_playbook_optimization_event(
82
+ self, event: PlaybookOptimizationEvent
83
+ ) -> PlaybookOptimizationEvent:
84
+ """Persist an optimizer callback/event and return it with id populated."""
85
+ raise NotImplementedError