claude-smart 0.2.47 → 0.2.48

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/README.md +1 -1
  3. package/package.json +1 -1
  4. package/plugin/.claude-plugin/plugin.json +1 -1
  5. package/plugin/.codex-plugin/plugin.json +1 -1
  6. package/plugin/dashboard/app/sessions/[sessionId]/page.tsx +36 -2
  7. package/plugin/dashboard/package-lock.json +61 -390
  8. package/plugin/dashboard/package.json +2 -2
  9. package/plugin/pyproject.toml +1 -1
  10. package/plugin/uv.lock +1 -1
  11. package/plugin/vendor/reflexio/.env.example +7 -0
  12. package/plugin/vendor/reflexio/pyproject.toml +2 -1
  13. package/plugin/vendor/reflexio/reflexio/README.md +8 -5
  14. package/plugin/vendor/reflexio/reflexio/lib/_config.py +23 -18
  15. package/plugin/vendor/reflexio/reflexio/lib/_interactions.py +16 -1
  16. package/plugin/vendor/reflexio/reflexio/lib/_search.py +15 -11
  17. package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/enums.py +1 -0
  18. package/plugin/vendor/reflexio/reflexio/models/config_schema.py +24 -3
  19. package/plugin/vendor/reflexio/reflexio/server/README.md +25 -8
  20. package/plugin/vendor/reflexio/reflexio/server/__init__.py +21 -2
  21. package/plugin/vendor/reflexio/reflexio/server/api.py +133 -3273
  22. package/plugin/vendor/reflexio/reflexio/server/api_endpoints/README.md +4 -3
  23. package/plugin/vendor/reflexio/reflexio/server/api_endpoints/publisher_api.py +16 -1
  24. package/plugin/vendor/reflexio/reflexio/server/cache/reflexio_cache.py +62 -36
  25. package/plugin/vendor/reflexio/reflexio/server/deployment_profile.py +69 -0
  26. package/plugin/vendor/reflexio/reflexio/server/llm/_litellm_embedding.py +424 -0
  27. package/plugin/vendor/reflexio/reflexio/server/llm/_litellm_json_extraction.py +249 -0
  28. package/plugin/vendor/reflexio/reflexio/server/llm/_litellm_structured_output.py +195 -0
  29. package/plugin/vendor/reflexio/reflexio/server/llm/_litellm_subprocess.py +152 -0
  30. package/plugin/vendor/reflexio/reflexio/server/llm/_litellm_text_generation.py +980 -0
  31. package/plugin/vendor/reflexio/reflexio/server/llm/_litellm_types.py +110 -0
  32. package/plugin/vendor/reflexio/reflexio/server/llm/litellm_client.py +73 -1819
  33. package/plugin/vendor/reflexio/reflexio/server/llm/providers/claude_code_provider.py +56 -4
  34. package/plugin/vendor/reflexio/reflexio/server/middleware.py +244 -0
  35. package/plugin/vendor/reflexio/reflexio/server/operation_limiter.py +9 -1
  36. package/plugin/vendor/reflexio/reflexio/server/rate_limit.py +79 -0
  37. package/plugin/vendor/reflexio/reflexio/server/routes/__init__.py +6 -0
  38. package/plugin/vendor/reflexio/reflexio/server/routes/_common.py +25 -0
  39. package/plugin/vendor/reflexio/reflexio/server/routes/_metering.py +98 -0
  40. package/plugin/vendor/reflexio/reflexio/server/routes/braintrust.py +129 -0
  41. package/plugin/vendor/reflexio/reflexio/server/routes/config.py +210 -0
  42. package/plugin/vendor/reflexio/reflexio/server/routes/evaluation.py +549 -0
  43. package/plugin/vendor/reflexio/reflexio/server/routes/interactions.py +259 -0
  44. package/plugin/vendor/reflexio/reflexio/server/routes/playbooks.py +578 -0
  45. package/plugin/vendor/reflexio/reflexio/server/routes/profiles.py +423 -0
  46. package/plugin/vendor/reflexio/reflexio/server/routes/provenance.py +345 -0
  47. package/plugin/vendor/reflexio/reflexio/server/routes/search.py +349 -0
  48. package/plugin/vendor/reflexio/reflexio/server/routes/system.py +261 -0
  49. package/plugin/vendor/reflexio/reflexio/server/scheduling.py +132 -0
  50. package/plugin/vendor/reflexio/reflexio/server/services/README.md +3 -2
  51. package/plugin/vendor/reflexio/reflexio/server/services/base_generation/__init__.py +38 -0
  52. package/plugin/vendor/reflexio/reflexio/server/services/base_generation/_batch_progress.py +298 -0
  53. package/plugin/vendor/reflexio/reflexio/server/services/base_generation/_config_filter.py +152 -0
  54. package/plugin/vendor/reflexio/reflexio/server/services/base_generation/_extraction_lifecycle.py +243 -0
  55. package/plugin/vendor/reflexio/reflexio/server/services/base_generation/_should_run.py +299 -0
  56. package/plugin/vendor/reflexio/reflexio/server/services/base_generation/_status_change.py +273 -0
  57. package/plugin/vendor/reflexio/reflexio/server/services/base_generation/_usage_billing.py +258 -0
  58. package/plugin/vendor/reflexio/reflexio/server/services/base_generation_service.py +17 -1183
  59. package/plugin/vendor/reflexio/reflexio/server/services/deduplication_utils.py +8 -1
  60. package/plugin/vendor/reflexio/reflexio/server/services/extraction/resume_scheduler.py +26 -41
  61. package/plugin/vendor/reflexio/reflexio/server/services/generation_service.py +232 -123
  62. package/plugin/vendor/reflexio/reflexio/server/services/lineage/gc_scheduler.py +362 -81
  63. package/plugin/vendor/reflexio/reflexio/server/services/playbook/components/aggregator.py +68 -490
  64. package/plugin/vendor/reflexio/reflexio/server/services/playbook/components/aggregator_clustering.py +184 -0
  65. package/plugin/vendor/reflexio/reflexio/server/services/playbook/components/aggregator_postprocessing.py +130 -0
  66. package/plugin/vendor/reflexio/reflexio/server/services/playbook/components/aggregator_prompt_formatting.py +212 -0
  67. package/plugin/vendor/reflexio/reflexio/server/services/playbook/components/consolidator.py +258 -69
  68. package/plugin/vendor/reflexio/reflexio/server/services/publish_learning_worker.py +288 -0
  69. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/__init__.py +29 -4
  70. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_agent_run.py +10 -1167
  71. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_base.py +56 -351
  72. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_governance.py +0 -1513
  73. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_lineage.py +6 -1
  74. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_profiles.py +7 -1281
  75. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_share_links.py +30 -0
  76. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/agent_run/__init__.py +9 -0
  77. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/agent_run/_agent_run_store.py +506 -0
  78. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/agent_run/_pending_tool_call_store.py +704 -0
  79. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/agent_run/_run_tool_dependency_store.py +123 -0
  80. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/base/__init__.py +6 -0
  81. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/base/_deletion.py +263 -0
  82. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/base/_fts_vec.py +132 -0
  83. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/governance/__init__.py +13 -0
  84. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/governance/_audit.py +122 -0
  85. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/governance/_erase_execution.py +465 -0
  86. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/governance/_purge.py +387 -0
  87. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/governance/_rebuild_hide.py +332 -0
  88. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/governance/_subject_barrier.py +511 -0
  89. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_agent.py +6 -3
  90. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_user.py +13 -7
  91. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/profiles/__init__.py +9 -0
  92. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/profiles/_interaction_store.py +263 -0
  93. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/profiles/_profile_store.py +896 -0
  94. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/profiles/_search.py +270 -0
  95. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/__init__.py +33 -8
  96. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_agent_run.py +64 -370
  97. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_share_links.py +20 -0
  98. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/agent_run/__init__.py +9 -0
  99. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/agent_run/_agent_run_store.py +86 -0
  100. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/agent_run/_models.py +195 -0
  101. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/agent_run/_pending_tool_call_store.py +148 -0
  102. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/agent_run/_run_tool_dependency_store.py +38 -0
  103. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/governance/__init__.py +13 -0
  104. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/governance/_audit.py +29 -0
  105. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/governance/_erase_execution.py +30 -0
  106. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/governance/_purge.py +74 -0
  107. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/governance/_rebuild_hide.py +32 -0
  108. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/governance/_subject_barrier.py +49 -0
  109. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/profiles/__init__.py +9 -0
  110. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/profiles/_interaction_store.py +73 -0
  111. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/{_profiles.py → profiles/_profile_store.py} +44 -86
  112. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/profiles/_search.py +32 -0
  113. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_governance.py +0 -148
@@ -0,0 +1,578 @@
1
+ """Playbook route handlers (extracted from api.py, Tier3 A2)."""
2
+
3
+ import logging
4
+ from typing import TYPE_CHECKING
5
+
6
+ if TYPE_CHECKING:
7
+ pass
8
+
9
+ from fastapi import (
10
+ APIRouter,
11
+ BackgroundTasks,
12
+ Depends,
13
+ Request,
14
+ )
15
+
16
+ from reflexio.models.api_schema.retriever_schema import (
17
+ GetAgentPlaybooksRequest,
18
+ GetAgentPlaybooksViewResponse,
19
+ GetPlaybookApplicationStatsRequest,
20
+ GetPlaybookApplicationStatsResponse,
21
+ GetUserPlaybooksRequest,
22
+ GetUserPlaybooksViewResponse,
23
+ UpdateAgentPlaybookRequest,
24
+ UpdateAgentPlaybookResponse,
25
+ UpdatePlaybookStatusRequest,
26
+ UpdatePlaybookStatusResponse,
27
+ UpdateUserPlaybookRequest,
28
+ UpdateUserPlaybookResponse,
29
+ )
30
+ from reflexio.models.api_schema.service_schemas import (
31
+ AddAgentPlaybookRequest,
32
+ AddAgentPlaybookResponse,
33
+ AddUserPlaybookRequest,
34
+ AddUserPlaybookResponse,
35
+ BulkDeleteResponse,
36
+ DeleteAgentPlaybookRequest,
37
+ DeleteAgentPlaybookResponse,
38
+ DeleteAgentPlaybooksByIdsRequest,
39
+ DeleteUserPlaybookRequest,
40
+ DeleteUserPlaybookResponse,
41
+ DeleteUserPlaybooksByIdsRequest,
42
+ DowngradeUserPlaybooksRequest,
43
+ DowngradeUserPlaybooksResponse,
44
+ ManualPlaybookGenerationRequest,
45
+ ManualPlaybookGenerationResponse,
46
+ PlaybookAggregationChangeLogResponse,
47
+ RerunPlaybookGenerationRequest,
48
+ RerunPlaybookGenerationResponse,
49
+ RunPlaybookAggregationRequest,
50
+ RunPlaybookAggregationResponse,
51
+ UpgradeUserPlaybooksRequest,
52
+ UpgradeUserPlaybooksResponse,
53
+ )
54
+ from reflexio.models.api_schema.ui.converters import (
55
+ to_agent_playbook_view,
56
+ to_user_playbook_view,
57
+ )
58
+ from reflexio.server.api_endpoints import (
59
+ publisher_api,
60
+ )
61
+ from reflexio.server.auth import (
62
+ default_billing_gate,
63
+ default_get_caller_type,
64
+ default_get_org_id,
65
+ )
66
+ from reflexio.server.cache import reflexio_cache
67
+ from reflexio.server.rate_limit import limiter
68
+ from reflexio.server.routes._common import _run_limited_api
69
+ from reflexio.server.routes._metering import (
70
+ _meter_applied_learnings,
71
+ )
72
+
73
+ logger = logging.getLogger(__name__)
74
+ router = APIRouter()
75
+
76
+
77
+ @router.post(
78
+ "/api/add_user_playbook",
79
+ response_model=AddUserPlaybookResponse,
80
+ response_model_exclude_none=True,
81
+ )
82
+ @limiter.limit("60/minute") # Rate limit for write operations
83
+ def add_user_playbook_endpoint(
84
+ request: Request,
85
+ payload: AddUserPlaybookRequest,
86
+ org_id: str = Depends(default_get_org_id),
87
+ ) -> AddUserPlaybookResponse:
88
+ """Add user playbook directly to storage.
89
+
90
+ Args:
91
+ request (Request): The HTTP request object (for rate limiting)
92
+ payload (AddUserPlaybookRequest): The request containing user playbooks
93
+ org_id (str): Organization ID
94
+
95
+ Returns:
96
+ AddUserPlaybookResponse: Response containing success status, message, and added count
97
+ """
98
+ return publisher_api.add_user_playbook(org_id=org_id, request=payload)
99
+
100
+
101
+ @router.post(
102
+ "/api/add_agent_playbook",
103
+ response_model=AddAgentPlaybookResponse,
104
+ response_model_exclude_none=True,
105
+ )
106
+ @limiter.limit("60/minute") # Rate limit for write operations
107
+ def add_agent_playbook_endpoint(
108
+ request: Request,
109
+ payload: AddAgentPlaybookRequest,
110
+ org_id: str = Depends(default_get_org_id),
111
+ ) -> AddAgentPlaybookResponse:
112
+ """Add agent playbook directly to storage.
113
+
114
+ Args:
115
+ request (Request): The HTTP request object (for rate limiting)
116
+ payload (AddAgentPlaybookRequest): The request containing agent playbooks
117
+ org_id (str): Organization ID
118
+
119
+ Returns:
120
+ AddAgentPlaybookResponse: Response containing success status, message, and added count
121
+ """
122
+ return publisher_api.add_agent_playbook(org_id=org_id, request=payload)
123
+
124
+
125
+ @router.get(
126
+ "/api/playbook_aggregation_change_logs",
127
+ response_model=PlaybookAggregationChangeLogResponse,
128
+ )
129
+ def get_playbook_aggregation_change_logs(
130
+ playbook_name: str,
131
+ agent_version: str,
132
+ org_id: str = Depends(default_get_org_id),
133
+ ) -> PlaybookAggregationChangeLogResponse:
134
+ return reflexio_cache.get_reflexio(
135
+ org_id=org_id
136
+ ).get_playbook_aggregation_change_logs(
137
+ playbook_name=playbook_name,
138
+ agent_version=agent_version,
139
+ )
140
+
141
+
142
+ @router.delete(
143
+ "/api/delete_agent_playbook",
144
+ response_model=DeleteAgentPlaybookResponse,
145
+ response_model_exclude_none=True,
146
+ )
147
+ def delete_agent_playbook(
148
+ request: DeleteAgentPlaybookRequest,
149
+ org_id: str = Depends(default_get_org_id),
150
+ ) -> DeleteAgentPlaybookResponse:
151
+ return publisher_api.delete_agent_playbook(org_id=org_id, request=request)
152
+
153
+
154
+ @router.delete(
155
+ "/api/delete_user_playbook",
156
+ response_model=DeleteUserPlaybookResponse,
157
+ response_model_exclude_none=True,
158
+ )
159
+ def delete_user_playbook(
160
+ request: DeleteUserPlaybookRequest,
161
+ org_id: str = Depends(default_get_org_id),
162
+ ) -> DeleteUserPlaybookResponse:
163
+ return publisher_api.delete_user_playbook(org_id=org_id, request=request)
164
+
165
+
166
+ @router.delete(
167
+ "/api/delete_agent_playbooks_by_ids",
168
+ response_model=BulkDeleteResponse,
169
+ response_model_exclude_none=True,
170
+ )
171
+ def delete_agent_playbooks_by_ids(
172
+ request: DeleteAgentPlaybooksByIdsRequest,
173
+ org_id: str = Depends(default_get_org_id),
174
+ ) -> BulkDeleteResponse:
175
+ """Delete multiple agent playbooks by their IDs.
176
+
177
+ Args:
178
+ request (DeleteAgentPlaybooksByIdsRequest): Request containing list of agent playbook IDs to delete
179
+ org_id (str): Organization ID
180
+
181
+ Returns:
182
+ BulkDeleteResponse: Response containing success status and deleted count
183
+ """
184
+ return publisher_api.delete_agent_playbooks_by_ids_bulk(
185
+ org_id=org_id, request=request
186
+ )
187
+
188
+
189
+ @router.delete(
190
+ "/api/delete_user_playbooks_by_ids",
191
+ response_model=BulkDeleteResponse,
192
+ response_model_exclude_none=True,
193
+ )
194
+ def delete_user_playbooks_by_ids(
195
+ request: DeleteUserPlaybooksByIdsRequest,
196
+ org_id: str = Depends(default_get_org_id),
197
+ ) -> BulkDeleteResponse:
198
+ """Delete multiple user playbooks by their IDs.
199
+
200
+ Args:
201
+ request (DeleteUserPlaybooksByIdsRequest): Request containing list of user playbook IDs to delete
202
+ org_id (str): Organization ID
203
+
204
+ Returns:
205
+ BulkDeleteResponse: Response containing success status and deleted count
206
+ """
207
+ return publisher_api.delete_user_playbooks_by_ids_bulk(
208
+ org_id=org_id, request=request
209
+ )
210
+
211
+
212
+ @router.delete(
213
+ "/api/delete_all_playbooks",
214
+ response_model=BulkDeleteResponse,
215
+ response_model_exclude_none=True,
216
+ )
217
+ @limiter.limit("10/minute")
218
+ def delete_all_playbooks(
219
+ request: Request,
220
+ org_id: str = Depends(default_get_org_id),
221
+ ) -> BulkDeleteResponse:
222
+ """Delete all playbooks (both user and agent).
223
+
224
+ Args:
225
+ org_id (str): Organization ID
226
+
227
+ Returns:
228
+ BulkDeleteResponse: Response containing success status and deleted count
229
+ """
230
+ return publisher_api.delete_all_playbooks_bulk(org_id=org_id)
231
+
232
+
233
+ @router.delete(
234
+ "/api/delete_all_user_playbooks",
235
+ response_model=BulkDeleteResponse,
236
+ response_model_exclude_none=True,
237
+ )
238
+ @limiter.limit("10/minute")
239
+ def delete_all_user_playbooks(
240
+ request: Request,
241
+ org_id: str = Depends(default_get_org_id),
242
+ ) -> BulkDeleteResponse:
243
+ """Delete all user playbooks (user only, not agent).
244
+
245
+ Args:
246
+ org_id (str): Organization ID
247
+
248
+ Returns:
249
+ BulkDeleteResponse: Response containing success status and deleted count
250
+ """
251
+ return publisher_api.delete_all_user_playbooks_bulk(org_id=org_id)
252
+
253
+
254
+ @router.delete(
255
+ "/api/delete_all_agent_playbooks",
256
+ response_model=BulkDeleteResponse,
257
+ response_model_exclude_none=True,
258
+ )
259
+ @limiter.limit("10/minute")
260
+ def delete_all_agent_playbooks(
261
+ request: Request,
262
+ org_id: str = Depends(default_get_org_id),
263
+ ) -> BulkDeleteResponse:
264
+ """Delete all agent playbooks (agent only, not user).
265
+
266
+ Args:
267
+ org_id (str): Organization ID
268
+
269
+ Returns:
270
+ BulkDeleteResponse: Response containing success status and deleted count
271
+ """
272
+ return publisher_api.delete_all_agent_playbooks_bulk(org_id=org_id)
273
+
274
+
275
+ @router.post(
276
+ "/api/run_playbook_aggregation",
277
+ response_model=RunPlaybookAggregationResponse,
278
+ response_model_exclude_none=True,
279
+ )
280
+ @limiter.limit("10/minute") # Strict limit for expensive operations
281
+ def run_playbook_aggregation(
282
+ request: Request,
283
+ payload: RunPlaybookAggregationRequest,
284
+ org_id: str = Depends(default_get_org_id),
285
+ ) -> RunPlaybookAggregationResponse:
286
+ return _run_limited_api(
287
+ org_id,
288
+ "aggregation",
289
+ lambda: publisher_api.run_playbook_aggregation(org_id=org_id, request=payload),
290
+ )
291
+
292
+
293
+ @router.post(
294
+ "/api/get_user_playbooks",
295
+ response_model=GetUserPlaybooksViewResponse,
296
+ response_model_exclude_none=True,
297
+ )
298
+ def get_user_playbooks(
299
+ request: GetUserPlaybooksRequest,
300
+ org_id: str = Depends(default_get_org_id),
301
+ ) -> GetUserPlaybooksViewResponse:
302
+ """Get user playbooks with internal fields filtered out.
303
+
304
+ Args:
305
+ request (GetUserPlaybooksRequest): The get request
306
+ org_id (str): Organization ID
307
+
308
+ Returns:
309
+ GetUserPlaybooksViewResponse: Response containing user playbooks without internal fields
310
+ """
311
+ reflexio = reflexio_cache.get_reflexio(org_id=org_id)
312
+ response = reflexio.get_user_playbooks(request)
313
+ return GetUserPlaybooksViewResponse(
314
+ success=response.success,
315
+ user_playbooks=[to_user_playbook_view(rf) for rf in response.user_playbooks],
316
+ msg=response.msg,
317
+ )
318
+
319
+
320
+ @router.post(
321
+ "/api/get_agent_playbooks",
322
+ response_model=GetAgentPlaybooksViewResponse,
323
+ response_model_exclude_none=True,
324
+ )
325
+ @limiter.limit("120/minute") # Rate limit for read operations
326
+ def get_agent_playbooks(
327
+ request: Request,
328
+ payload: GetAgentPlaybooksRequest,
329
+ org_id: str = Depends(default_get_org_id),
330
+ caller_type: str = Depends(default_get_caller_type),
331
+ _gate: None = Depends(default_billing_gate("application")), # noqa: B008
332
+ ) -> GetAgentPlaybooksViewResponse:
333
+ """Get agent playbooks with internal fields filtered out.
334
+
335
+ Args:
336
+ request (Request): The HTTP request object (for rate limiting)
337
+ payload (GetAgentPlaybooksRequest): The get request
338
+ org_id (str): Organization ID
339
+ caller_type (str): Billing caller classification (injected via dependency).
340
+
341
+ Returns:
342
+ GetAgentPlaybooksViewResponse: Response containing agent playbooks without internal fields
343
+ """
344
+ reflexio = reflexio_cache.get_reflexio(org_id=org_id)
345
+ response = reflexio.get_agent_playbooks(payload)
346
+ resp = GetAgentPlaybooksViewResponse(
347
+ success=response.success,
348
+ agent_playbooks=[to_agent_playbook_view(fb) for fb in response.agent_playbooks],
349
+ msg=response.msg,
350
+ )
351
+ _meter_applied_learnings(
352
+ org_id=org_id,
353
+ caller_type=caller_type,
354
+ surfaced_count=len(resp.agent_playbooks),
355
+ request_id=getattr(payload, "request_id", None),
356
+ session_id=getattr(payload, "session_id", None),
357
+ )
358
+ return resp
359
+
360
+
361
+ @router.put(
362
+ "/api/update_agent_playbook_status",
363
+ response_model=UpdatePlaybookStatusResponse,
364
+ response_model_exclude_none=True,
365
+ )
366
+ def update_agent_playbook_status_endpoint(
367
+ request: UpdatePlaybookStatusRequest,
368
+ org_id: str = Depends(default_get_org_id),
369
+ ) -> UpdatePlaybookStatusResponse:
370
+ """Update the status of a specific playbook.
371
+
372
+ Args:
373
+ request (UpdatePlaybookStatusRequest): The update request
374
+ org_id (str): Organization ID
375
+
376
+ Returns:
377
+ UpdatePlaybookStatusResponse: Response containing success status and message
378
+ """
379
+ return publisher_api.update_agent_playbook_status(org_id=org_id, request=request)
380
+
381
+
382
+ @router.put(
383
+ "/api/update_agent_playbook",
384
+ response_model=UpdateAgentPlaybookResponse,
385
+ response_model_exclude_none=True,
386
+ )
387
+ def update_agent_playbook_endpoint(
388
+ request: UpdateAgentPlaybookRequest,
389
+ org_id: str = Depends(default_get_org_id),
390
+ ) -> UpdateAgentPlaybookResponse:
391
+ """Update editable fields of a specific agent playbook.
392
+
393
+ Args:
394
+ request (UpdateAgentPlaybookRequest): The update request
395
+ org_id (str): Organization ID
396
+
397
+ Returns:
398
+ UpdateAgentPlaybookResponse: Response containing success status and message
399
+ """
400
+ return publisher_api.update_agent_playbook(org_id=org_id, request=request)
401
+
402
+
403
+ @router.put(
404
+ "/api/update_user_playbook",
405
+ response_model=UpdateUserPlaybookResponse,
406
+ response_model_exclude_none=True,
407
+ )
408
+ def update_user_playbook_endpoint(
409
+ request: UpdateUserPlaybookRequest,
410
+ org_id: str = Depends(default_get_org_id),
411
+ ) -> UpdateUserPlaybookResponse:
412
+ """Update editable fields of a specific user playbook.
413
+
414
+ Args:
415
+ request (UpdateUserPlaybookRequest): The update request
416
+ org_id (str): Organization ID
417
+
418
+ Returns:
419
+ UpdateUserPlaybookResponse: Response containing success status and message
420
+ """
421
+ return publisher_api.update_user_playbook(org_id=org_id, request=request)
422
+
423
+
424
+ @router.post(
425
+ "/api/get_playbook_application_stats",
426
+ response_model=GetPlaybookApplicationStatsResponse,
427
+ response_model_exclude_none=True,
428
+ )
429
+ def get_playbook_application_stats(
430
+ request: GetPlaybookApplicationStatsRequest,
431
+ org_id: str = Depends(default_get_org_id),
432
+ ) -> GetPlaybookApplicationStatsResponse:
433
+ """Get per-rule citation counts aggregated from interactions.
434
+
435
+ Returns one row per cited (kind, real_id) over the look-back window,
436
+ sorted by applied_count descending. Lets the dashboard show users a
437
+ per-rule "track record" — how often each playbook or profile has been
438
+ applied and when it last fired.
439
+
440
+ Args:
441
+ request (GetPlaybookApplicationStatsRequest): Request containing
442
+ days_back.
443
+ org_id (str): Organization ID.
444
+
445
+ Returns:
446
+ GetPlaybookApplicationStatsResponse: Response containing aggregated
447
+ stats.
448
+ """
449
+ reflexio = reflexio_cache.get_reflexio(org_id=org_id)
450
+ return reflexio.get_playbook_application_stats(request)
451
+
452
+
453
+ @router.post(
454
+ "/api/rerun_playbook_generation",
455
+ response_model=RerunPlaybookGenerationResponse,
456
+ response_model_exclude_none=True,
457
+ )
458
+ @limiter.limit("5/minute") # Strict limit for expensive operations
459
+ def rerun_playbook_generation_endpoint(
460
+ request: Request,
461
+ payload: RerunPlaybookGenerationRequest,
462
+ background_tasks: BackgroundTasks,
463
+ org_id: str = Depends(default_get_org_id),
464
+ ) -> RerunPlaybookGenerationResponse:
465
+ """Rerun playbook generation with filtered interactions.
466
+
467
+ Args:
468
+ request (Request): The HTTP request object (for rate limiting)
469
+ payload (RerunPlaybookGenerationRequest): Request containing agent_version, time filters, and optional playbook_name
470
+ background_tasks (BackgroundTasks): Background task runner
471
+ org_id (str): Organization ID
472
+
473
+ Returns:
474
+ RerunPlaybookGenerationResponse: Response containing success status and playbooks generated count
475
+ """
476
+ # Create Reflexio instance
477
+ reflexio = reflexio_cache.get_reflexio(org_id=org_id)
478
+
479
+ # Run the long-running task in the background to avoid proxy timeout
480
+ # Client polls get_operation_status for progress
481
+ background_tasks.add_task(reflexio.rerun_playbook_generation, payload)
482
+
483
+ return RerunPlaybookGenerationResponse(
484
+ success=True, msg="Playbook generation started"
485
+ )
486
+
487
+
488
+ @router.post(
489
+ "/api/manual_playbook_generation",
490
+ response_model=ManualPlaybookGenerationResponse,
491
+ response_model_exclude_none=True,
492
+ )
493
+ @limiter.limit("5/minute") # Strict limit for expensive operations
494
+ def manual_playbook_generation_endpoint(
495
+ request: Request,
496
+ payload: ManualPlaybookGenerationRequest,
497
+ org_id: str = Depends(default_get_org_id),
498
+ ) -> ManualPlaybookGenerationResponse:
499
+ """Manually trigger playbook generation with window-sized interactions and CURRENT output.
500
+
501
+ Runs with auto_run=False, which bypasses the regular stride/should_run
502
+ gates. Only playbook extraction is triggered. Each extractor uses its own
503
+ window_size_override when present, falling back to the global window_size.
504
+ Output is CURRENT playbooks only.
505
+
506
+ Args:
507
+ request (Request): The HTTP request object (for rate limiting)
508
+ payload (ManualPlaybookGenerationRequest): Request containing agent_version, source, and playbook_name
509
+ org_id (str): Organization ID
510
+
511
+ Returns:
512
+ ManualPlaybookGenerationResponse: Response containing success status and playbooks generated count
513
+ """
514
+ # Create Reflexio instance
515
+ reflexio = reflexio_cache.get_reflexio(org_id=org_id)
516
+
517
+ # Call manual_playbook_generation
518
+ return reflexio.manual_playbook_generation(payload)
519
+
520
+
521
+ @router.post(
522
+ "/api/upgrade_all_user_playbooks",
523
+ response_model=UpgradeUserPlaybooksResponse,
524
+ response_model_exclude_none=True,
525
+ )
526
+ def upgrade_all_user_playbooks_endpoint(
527
+ request: UpgradeUserPlaybooksRequest,
528
+ org_id: str = Depends(default_get_org_id),
529
+ ) -> UpgradeUserPlaybooksResponse:
530
+ """Upgrade all user playbooks by deleting old ARCHIVED, archiving CURRENT, and promoting PENDING.
531
+
532
+ This operation performs three atomic steps:
533
+ 1. Delete all ARCHIVED user playbooks (old archived from previous upgrades)
534
+ 2. Archive all CURRENT user playbooks → ARCHIVED (save current state for potential rollback)
535
+ 3. Promote all PENDING user playbooks → CURRENT (activate new user playbooks)
536
+
537
+ Args:
538
+ request (UpgradeUserPlaybooksRequest): The upgrade request with optional agent_version and playbook_name filters
539
+ org_id (str): Organization ID
540
+
541
+ Returns:
542
+ UpgradeUserPlaybooksResponse: Response containing success status and counts
543
+ """
544
+ # Create Reflexio instance
545
+ reflexio = reflexio_cache.get_reflexio(org_id=org_id)
546
+
547
+ # Call upgrade_all_user_playbooks with request
548
+ return reflexio.upgrade_all_user_playbooks(request=request)
549
+
550
+
551
+ @router.post(
552
+ "/api/downgrade_all_user_playbooks",
553
+ response_model=DowngradeUserPlaybooksResponse,
554
+ response_model_exclude_none=True,
555
+ )
556
+ def downgrade_all_user_playbooks_endpoint(
557
+ request: DowngradeUserPlaybooksRequest,
558
+ org_id: str = Depends(default_get_org_id),
559
+ ) -> DowngradeUserPlaybooksResponse:
560
+ """Downgrade all user playbooks by archiving CURRENT and restoring ARCHIVED.
561
+
562
+ This operation performs three atomic steps:
563
+ 1. Mark all CURRENT user playbooks → ARCHIVE_IN_PROGRESS (temporary status)
564
+ 2. Restore all ARCHIVED user playbooks → CURRENT
565
+ 3. Move all ARCHIVE_IN_PROGRESS user playbooks → ARCHIVED
566
+
567
+ Args:
568
+ request (DowngradeUserPlaybooksRequest): The downgrade request with optional agent_version and playbook_name filters
569
+ org_id (str): Organization ID
570
+
571
+ Returns:
572
+ DowngradeUserPlaybooksResponse: Response containing success status and counts
573
+ """
574
+ # Create Reflexio instance
575
+ reflexio = reflexio_cache.get_reflexio(org_id=org_id)
576
+
577
+ # Call downgrade_all_user_playbooks with request
578
+ return reflexio.downgrade_all_user_playbooks(request=request)