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,332 @@
1
+ """SQLite governance rebuild-hide store methods.
2
+
3
+ Extracted verbatim from ``_governance.py`` (the RebuildHide bucket): the two
4
+ public methods (``hide_governance_agent_playbooks_for_rebuild``,
5
+ ``apply_governance_agent_playbook_rebuild``) plus the three RebuildHide-owned
6
+ privates (``_replace_agent_playbook_source_windows_locked``,
7
+ ``_delete_agent_playbook_search_rows_locked``,
8
+ ``_upsert_agent_playbook_search_rows_locked``) and the module-level
9
+ ``_build_agent_playbook_source_window_rows`` helper.
10
+
11
+ ``apply_governance_agent_playbook_rebuild`` preserves its ordering byte-for-byte:
12
+ verify the planned rebuild target + hide_for_rebuild complete, then either
13
+ UPDATE the playbook and refresh FTS/vec (windows remain) OR hard-delete the
14
+ playbook and emit the ``hard_delete`` lineage event (no windows remain), then
15
+ record the ``rebuild_without_erased_sources`` target complete, all committing
16
+ together. ``_emit_hard_delete_playbook`` is function-imported LOCAL-in-method
17
+ (from ``.._playbook``) to avoid an import cycle.
18
+
19
+ The residual ``SQLiteGovernanceMixin`` stays composed alongside this mixin and
20
+ permanently holds the shared infra (``conn``, ``_lock``, ``org_id``,
21
+ ``_deps()``), the delete-target / hide-for-rebuild / prepared-snapshot
22
+ validators, and the module-level helpers (``_json_dumps``, ``_json_loads``),
23
+ which are imported here rather than duplicated. ``_record_purge_target_locked``
24
+ (PurgeOperationStore) resolves through the composed MRO.
25
+ """
26
+
27
+ from __future__ import annotations
28
+
29
+ import json
30
+ import sqlite3
31
+ import threading
32
+ from collections.abc import Callable
33
+ from typing import TYPE_CHECKING, cast
34
+
35
+ from reflexio.models.api_schema.domain import AgentPlaybookSourceWindow
36
+ from reflexio.models.api_schema.domain.enums import Status
37
+ from reflexio.server.services.storage.governance_validation import (
38
+ _canonicalize_governance_windows,
39
+ _parse_governance_window_list,
40
+ _validate_governance_purge_id,
41
+ )
42
+
43
+ from .._governance import _json_dumps, _json_loads
44
+
45
+ if TYPE_CHECKING:
46
+ from .._governance import _SQLiteGovernanceDeps
47
+
48
+
49
+ def _build_agent_playbook_source_window_rows(
50
+ agent_playbook_id: int, windows: list[AgentPlaybookSourceWindow]
51
+ ) -> list[tuple[int, int, str]]:
52
+ by_id: dict[int, list[int]] = {}
53
+ for window in windows:
54
+ ids = by_id.setdefault(window.user_playbook_id, [])
55
+ seen = set(ids)
56
+ for source_id in window.source_interaction_ids:
57
+ if source_id not in seen:
58
+ ids.append(source_id)
59
+ seen.add(source_id)
60
+ return [
61
+ (
62
+ agent_playbook_id,
63
+ user_playbook_id,
64
+ _json_dumps(source_interaction_ids) or "[]",
65
+ )
66
+ for user_playbook_id, source_interaction_ids in by_id.items()
67
+ ]
68
+
69
+
70
+ class RebuildHideMixin:
71
+ """SQLite governance rebuild-hide store primitives."""
72
+
73
+ # Type hints for instance attributes/methods provided via MRO by the
74
+ # co-composed residual SQLiteGovernanceMixin / SQLiteStorageBase.
75
+ conn: sqlite3.Connection
76
+ _lock: threading.RLock
77
+ org_id: str
78
+ _deps: Callable[[], _SQLiteGovernanceDeps]
79
+
80
+ # Provided via MRO by the co-composed PurgeOperationStoreMixin (purge bucket);
81
+ # reached here by the cross-bucket rebuild-hide method.
82
+ _record_purge_target_locked: Callable[..., None]
83
+
84
+ def _replace_agent_playbook_source_windows_locked(
85
+ self, agent_playbook_id: int, windows: list[AgentPlaybookSourceWindow]
86
+ ) -> None:
87
+ self.conn.execute(
88
+ "DELETE FROM agent_playbook_source_user_playbooks WHERE agent_playbook_id = ?",
89
+ (agent_playbook_id,),
90
+ )
91
+ source_window_rows = _build_agent_playbook_source_window_rows(
92
+ agent_playbook_id, windows
93
+ )
94
+ if source_window_rows:
95
+ self.conn.executemany(
96
+ """INSERT OR IGNORE INTO agent_playbook_source_user_playbooks
97
+ (agent_playbook_id, user_playbook_id, source_interaction_ids)
98
+ VALUES (?, ?, ?)""",
99
+ source_window_rows,
100
+ )
101
+
102
+ def _delete_agent_playbook_search_rows_locked(self, agent_playbook_id: int) -> None:
103
+ self.conn.execute(
104
+ "DELETE FROM agent_playbooks_fts WHERE rowid = ?",
105
+ (agent_playbook_id,),
106
+ )
107
+ if self._deps()._has_sqlite_vec:
108
+ self.conn.execute(
109
+ "DELETE FROM agent_playbooks_vec WHERE rowid = ?",
110
+ (agent_playbook_id,),
111
+ )
112
+
113
+ def _upsert_agent_playbook_search_rows_locked(
114
+ self,
115
+ *,
116
+ agent_playbook_id: int,
117
+ trigger: str | None,
118
+ content: str,
119
+ expanded_terms: str | None,
120
+ embedding: list[float],
121
+ ) -> None:
122
+ self._delete_agent_playbook_search_rows_locked(agent_playbook_id)
123
+ fts_parts = [trigger or "", content]
124
+ if expanded_terms:
125
+ fts_parts.append(expanded_terms)
126
+ self.conn.execute(
127
+ "INSERT INTO agent_playbooks_fts(rowid, search_text) VALUES (?, ?)",
128
+ (
129
+ agent_playbook_id,
130
+ " ".join(part for part in fts_parts if part) or "",
131
+ ),
132
+ )
133
+ if self._deps()._has_sqlite_vec and embedding:
134
+ self.conn.execute(
135
+ "INSERT INTO agent_playbooks_vec(rowid, embedding) VALUES (?, ?)",
136
+ (agent_playbook_id, json.dumps(embedding)),
137
+ )
138
+
139
+ def hide_governance_agent_playbooks_for_rebuild(self, purge_id: str) -> list[int]:
140
+ purge_id = _validate_governance_purge_id("purge_id", purge_id)
141
+ with self._lock:
142
+ try:
143
+ self.conn.execute("BEGIN IMMEDIATE")
144
+ target_rows = self.conn.execute(
145
+ """SELECT target_ref
146
+ FROM purge_operation_targets
147
+ WHERE org_id = ? AND purge_id = ?
148
+ AND target_name = 'agent_playbook'
149
+ AND phase = 'rebuild_without_erased_sources'
150
+ AND target_ref != ''
151
+ AND status != 'complete'
152
+ ORDER BY CAST(target_ref AS INTEGER) ASC""",
153
+ (self.org_id, purge_id),
154
+ ).fetchall()
155
+ agent_playbook_ids = [int(row["target_ref"]) for row in target_rows]
156
+ if not agent_playbook_ids:
157
+ self.conn.commit()
158
+ return []
159
+ placeholders = ",".join("?" for _ in agent_playbook_ids)
160
+ self.conn.execute(
161
+ f"""UPDATE agent_playbooks
162
+ SET status = ?
163
+ WHERE agent_playbook_id IN ({placeholders})""",
164
+ [Status.ARCHIVE_IN_PROGRESS.value, *agent_playbook_ids],
165
+ )
166
+ for agent_playbook_id in agent_playbook_ids:
167
+ self._record_purge_target_locked(
168
+ purge_id=purge_id,
169
+ target_name="agent_playbook",
170
+ target_ref=str(agent_playbook_id),
171
+ phase="hide_for_rebuild",
172
+ status="complete",
173
+ detail=None,
174
+ deleted_count=0,
175
+ error_detail=None,
176
+ )
177
+ self._record_purge_target_locked(
178
+ purge_id=purge_id,
179
+ target_name="agent_playbook",
180
+ target_ref=str(agent_playbook_id),
181
+ phase="rebuild_without_erased_sources",
182
+ status="running",
183
+ detail=None,
184
+ deleted_count=0,
185
+ error_detail=None,
186
+ )
187
+ self.conn.commit()
188
+ except Exception:
189
+ self.conn.rollback()
190
+ raise
191
+ return agent_playbook_ids
192
+
193
+ def apply_governance_agent_playbook_rebuild(
194
+ self,
195
+ purge_id: str,
196
+ agent_playbook_id: int,
197
+ remaining_source_windows: list[dict[str, object]],
198
+ content: str | None,
199
+ trigger: str | None,
200
+ rationale: str | None,
201
+ blocking_issue: dict[str, object] | None,
202
+ expanded_terms: str | None,
203
+ tags: list[str] | None,
204
+ ) -> None:
205
+ purge_id = _validate_governance_purge_id("purge_id", purge_id)
206
+ windows = _parse_governance_window_list(
207
+ "remaining_source_windows", remaining_source_windows
208
+ )
209
+ canonical_remaining_windows = [window.model_dump() for window in windows]
210
+ content_value = content or ""
211
+ trigger_value = trigger or None
212
+ embedding_text = trigger_value or content_value
213
+ embedding = (
214
+ self._deps()._get_embedding(embedding_text) if embedding_text else []
215
+ )
216
+ with self._lock:
217
+ try:
218
+ self.conn.execute("BEGIN")
219
+ rebuild_target_row = self.conn.execute(
220
+ """SELECT status, detail
221
+ FROM purge_operation_targets
222
+ WHERE org_id = ? AND purge_id = ? AND target_name = 'agent_playbook'
223
+ AND target_ref = ? AND phase = 'rebuild_without_erased_sources'""",
224
+ (self.org_id, purge_id, str(agent_playbook_id)),
225
+ ).fetchone()
226
+ if rebuild_target_row is None:
227
+ raise ValueError("planned rebuild target does not exist")
228
+ if rebuild_target_row["status"] == "complete":
229
+ raise ValueError("planned rebuild target is already complete")
230
+ rebuild_detail = _json_loads(rebuild_target_row["detail"])
231
+ if not isinstance(rebuild_detail, dict) or not {
232
+ "original_source_windows",
233
+ "previous_lifecycle_status",
234
+ "remaining_source_windows",
235
+ }.issubset(rebuild_detail):
236
+ raise ValueError(
237
+ "planned rebuild target is missing source window detail"
238
+ )
239
+ planned_remaining_windows = _canonicalize_governance_windows(
240
+ "planned remaining_source_windows",
241
+ cast(
242
+ list[dict[str, object]],
243
+ rebuild_detail["remaining_source_windows"],
244
+ ),
245
+ )
246
+ if planned_remaining_windows != canonical_remaining_windows:
247
+ raise ValueError(
248
+ "remaining_source_windows must match the planned rebuild target"
249
+ )
250
+ previous_lifecycle_status = cast(
251
+ str | None, rebuild_detail["previous_lifecycle_status"]
252
+ )
253
+ hide_target_row = self.conn.execute(
254
+ """SELECT status
255
+ FROM purge_operation_targets
256
+ WHERE org_id = ? AND purge_id = ? AND target_name = 'agent_playbook'
257
+ AND target_ref = ? AND phase = 'hide_for_rebuild'""",
258
+ (self.org_id, purge_id, str(agent_playbook_id)),
259
+ ).fetchone()
260
+ if hide_target_row is None or hide_target_row["status"] != "complete":
261
+ raise ValueError("hide_for_rebuild target must be complete")
262
+ if windows:
263
+ cur = self.conn.execute(
264
+ """UPDATE agent_playbooks
265
+ SET content = ?, trigger = ?, rationale = ?, blocking_issue = ?,
266
+ embedding = ?, expanded_terms = ?, tags = ?, status = ?
267
+ WHERE agent_playbook_id = ?""",
268
+ (
269
+ content_value,
270
+ trigger_value,
271
+ rationale,
272
+ json.dumps(blocking_issue)
273
+ if blocking_issue is not None
274
+ else None,
275
+ _json_dumps(embedding),
276
+ expanded_terms,
277
+ _json_dumps(tags),
278
+ previous_lifecycle_status,
279
+ agent_playbook_id,
280
+ ),
281
+ )
282
+ if cur.rowcount == 0:
283
+ raise ValueError(
284
+ f"Agent playbook with ID {agent_playbook_id} not found"
285
+ )
286
+ self._replace_agent_playbook_source_windows_locked(
287
+ agent_playbook_id, windows
288
+ )
289
+ self._upsert_agent_playbook_search_rows_locked(
290
+ agent_playbook_id=agent_playbook_id,
291
+ trigger=trigger_value,
292
+ content=content_value,
293
+ expanded_terms=expanded_terms,
294
+ embedding=embedding,
295
+ )
296
+ else:
297
+ from .._playbook import _emit_hard_delete_playbook
298
+
299
+ self._delete_agent_playbook_search_rows_locked(agent_playbook_id)
300
+ self.conn.execute(
301
+ "DELETE FROM agent_playbook_source_user_playbooks WHERE agent_playbook_id = ?",
302
+ (agent_playbook_id,),
303
+ )
304
+ cur = self.conn.execute(
305
+ "DELETE FROM agent_playbooks WHERE agent_playbook_id = ?",
306
+ (agent_playbook_id,),
307
+ )
308
+ if cur.rowcount == 0:
309
+ raise ValueError(
310
+ f"Agent playbook with ID {agent_playbook_id} not found"
311
+ )
312
+ _emit_hard_delete_playbook(
313
+ self.conn,
314
+ org_id=self.org_id,
315
+ entity_type="agent_playbook",
316
+ entity_id=str(agent_playbook_id),
317
+ request_id=purge_id,
318
+ )
319
+ self._record_purge_target_locked(
320
+ purge_id=purge_id,
321
+ target_name="agent_playbook",
322
+ target_ref=str(agent_playbook_id),
323
+ phase="rebuild_without_erased_sources",
324
+ status="complete",
325
+ detail=None,
326
+ deleted_count=0,
327
+ error_detail=None,
328
+ )
329
+ self.conn.commit()
330
+ except Exception:
331
+ self.conn.rollback()
332
+ raise