codeframe-ai 0.9.0__py3-none-any.whl

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 (197) hide show
  1. codeframe/__init__.py +11 -0
  2. codeframe/__main__.py +20 -0
  3. codeframe/adapters/__init__.py +5 -0
  4. codeframe/adapters/e2b/__init__.py +13 -0
  5. codeframe/adapters/e2b/adapter.py +342 -0
  6. codeframe/adapters/e2b/budget.py +71 -0
  7. codeframe/adapters/e2b/credential_scanner.py +134 -0
  8. codeframe/adapters/llm/__init__.py +92 -0
  9. codeframe/adapters/llm/anthropic.py +414 -0
  10. codeframe/adapters/llm/base.py +444 -0
  11. codeframe/adapters/llm/mock.py +281 -0
  12. codeframe/adapters/llm/openai.py +483 -0
  13. codeframe/agents/__init__.py +8 -0
  14. codeframe/agents/dependency_resolver.py +714 -0
  15. codeframe/auth/__init__.py +16 -0
  16. codeframe/auth/api_key_router.py +238 -0
  17. codeframe/auth/api_keys.py +156 -0
  18. codeframe/auth/dependencies.py +358 -0
  19. codeframe/auth/manager.py +178 -0
  20. codeframe/auth/models.py +30 -0
  21. codeframe/auth/router.py +93 -0
  22. codeframe/auth/schemas.py +15 -0
  23. codeframe/auth/scopes.py +53 -0
  24. codeframe/cli/__init__.py +12 -0
  25. codeframe/cli/__main__.py +20 -0
  26. codeframe/cli/api_client.py +275 -0
  27. codeframe/cli/app.py +5688 -0
  28. codeframe/cli/auth.py +122 -0
  29. codeframe/cli/auth_commands.py +958 -0
  30. codeframe/cli/commands/__init__.py +5 -0
  31. codeframe/cli/config_commands.py +79 -0
  32. codeframe/cli/dashboard_commands.py +67 -0
  33. codeframe/cli/engines_commands.py +205 -0
  34. codeframe/cli/env_commands.py +409 -0
  35. codeframe/cli/helpers.py +56 -0
  36. codeframe/cli/hooks_commands.py +208 -0
  37. codeframe/cli/import_commands.py +129 -0
  38. codeframe/cli/pr_commands.py +549 -0
  39. codeframe/cli/proof_commands.py +415 -0
  40. codeframe/cli/stats_commands.py +311 -0
  41. codeframe/cli/telemetry_runtime.py +153 -0
  42. codeframe/cli/validators.py +123 -0
  43. codeframe/config/rate_limits.py +165 -0
  44. codeframe/core/__init__.py +15 -0
  45. codeframe/core/adapters/__init__.py +43 -0
  46. codeframe/core/adapters/agent_adapter.py +114 -0
  47. codeframe/core/adapters/builtin.py +326 -0
  48. codeframe/core/adapters/claude_code.py +62 -0
  49. codeframe/core/adapters/codex.py +393 -0
  50. codeframe/core/adapters/git_utils.py +40 -0
  51. codeframe/core/adapters/kilocode.py +126 -0
  52. codeframe/core/adapters/opencode.py +48 -0
  53. codeframe/core/adapters/streaming_chat.py +483 -0
  54. codeframe/core/adapters/subprocess_adapter.py +213 -0
  55. codeframe/core/adapters/verification_wrapper.py +269 -0
  56. codeframe/core/agent.py +2183 -0
  57. codeframe/core/agents_config.py +569 -0
  58. codeframe/core/api_key_service.py +211 -0
  59. codeframe/core/artifacts.py +428 -0
  60. codeframe/core/blocker_detection.py +218 -0
  61. codeframe/core/blockers.py +433 -0
  62. codeframe/core/checkpoints.py +481 -0
  63. codeframe/core/conductor.py +2255 -0
  64. codeframe/core/config.py +827 -0
  65. codeframe/core/config_watcher.py +268 -0
  66. codeframe/core/context.py +542 -0
  67. codeframe/core/context_packager.py +234 -0
  68. codeframe/core/credentials.py +735 -0
  69. codeframe/core/dependency_analyzer.py +229 -0
  70. codeframe/core/dependency_graph.py +290 -0
  71. codeframe/core/diagnostic_agent.py +712 -0
  72. codeframe/core/diagnostics.py +616 -0
  73. codeframe/core/editor.py +556 -0
  74. codeframe/core/engine_registry.py +256 -0
  75. codeframe/core/engine_stats.py +231 -0
  76. codeframe/core/environment.py +697 -0
  77. codeframe/core/events.py +375 -0
  78. codeframe/core/executor.py +1005 -0
  79. codeframe/core/fix_tracker.py +480 -0
  80. codeframe/core/gates.py +1322 -0
  81. codeframe/core/git.py +477 -0
  82. codeframe/core/github_connect_service.py +178 -0
  83. codeframe/core/github_integration_config.py +118 -0
  84. codeframe/core/github_issues_service.py +449 -0
  85. codeframe/core/hooks.py +184 -0
  86. codeframe/core/importers/__init__.py +1 -0
  87. codeframe/core/importers/ralph.py +540 -0
  88. codeframe/core/installer.py +650 -0
  89. codeframe/core/models.py +1026 -0
  90. codeframe/core/notifications_config.py +183 -0
  91. codeframe/core/planner.py +437 -0
  92. codeframe/core/prd.py +670 -0
  93. codeframe/core/prd_discovery.py +1118 -0
  94. codeframe/core/prd_stress_test.py +499 -0
  95. codeframe/core/progress.py +126 -0
  96. codeframe/core/proof/__init__.py +34 -0
  97. codeframe/core/proof/capture.py +79 -0
  98. codeframe/core/proof/evidence.py +56 -0
  99. codeframe/core/proof/ledger.py +574 -0
  100. codeframe/core/proof/models.py +162 -0
  101. codeframe/core/proof/obligations.py +103 -0
  102. codeframe/core/proof/runner.py +233 -0
  103. codeframe/core/proof/scope.py +81 -0
  104. codeframe/core/proof/stubs.py +156 -0
  105. codeframe/core/quick_fixes.py +558 -0
  106. codeframe/core/react_agent.py +1650 -0
  107. codeframe/core/reconciliation.py +183 -0
  108. codeframe/core/replay.py +788 -0
  109. codeframe/core/review.py +285 -0
  110. codeframe/core/runtime.py +1134 -0
  111. codeframe/core/sandbox/__init__.py +27 -0
  112. codeframe/core/sandbox/context.py +98 -0
  113. codeframe/core/sandbox/worktree.py +20 -0
  114. codeframe/core/schedule.py +396 -0
  115. codeframe/core/stall_detector.py +71 -0
  116. codeframe/core/stall_monitor.py +134 -0
  117. codeframe/core/state_machine.py +121 -0
  118. codeframe/core/streaming.py +502 -0
  119. codeframe/core/task_tree.py +400 -0
  120. codeframe/core/tasks.py +1022 -0
  121. codeframe/core/telemetry.py +232 -0
  122. codeframe/core/templates.py +221 -0
  123. codeframe/core/tools.py +942 -0
  124. codeframe/core/workspace.py +887 -0
  125. codeframe/core/worktrees.py +276 -0
  126. codeframe/git/__init__.py +5 -0
  127. codeframe/git/github_integration.py +505 -0
  128. codeframe/lib/__init__.py +0 -0
  129. codeframe/lib/audit_logger.py +248 -0
  130. codeframe/lib/metrics_tracker.py +800 -0
  131. codeframe/lib/quality/__init__.py +7 -0
  132. codeframe/lib/quality/complexity_analyzer.py +316 -0
  133. codeframe/lib/quality/owasp_patterns.py +284 -0
  134. codeframe/lib/quality/security_scanner.py +250 -0
  135. codeframe/lib/rate_limiter.py +312 -0
  136. codeframe/notifications/__init__.py +0 -0
  137. codeframe/notifications/webhook.py +380 -0
  138. codeframe/planning/__init__.py +30 -0
  139. codeframe/planning/issue_generator.py +219 -0
  140. codeframe/planning/prd_template_functions.py +137 -0
  141. codeframe/planning/prd_templates.py +975 -0
  142. codeframe/planning/task_scheduler.py +511 -0
  143. codeframe/planning/task_templates.py +533 -0
  144. codeframe/platform_store/__init__.py +5 -0
  145. codeframe/platform_store/database.py +277 -0
  146. codeframe/platform_store/repositories/__init__.py +24 -0
  147. codeframe/platform_store/repositories/api_key_repository.py +245 -0
  148. codeframe/platform_store/repositories/audit_repository.py +67 -0
  149. codeframe/platform_store/repositories/base.py +295 -0
  150. codeframe/platform_store/repositories/interactive_sessions.py +165 -0
  151. codeframe/platform_store/repositories/token_repository.py +598 -0
  152. codeframe/platform_store/repositories/workspace_registry_repository.py +175 -0
  153. codeframe/platform_store/schema_manager.py +321 -0
  154. codeframe/templates/AGENTS.md.default +94 -0
  155. codeframe/tui/__init__.py +5 -0
  156. codeframe/tui/app.py +256 -0
  157. codeframe/tui/data_service.py +103 -0
  158. codeframe/ui/__init__.py +0 -0
  159. codeframe/ui/dependencies.py +103 -0
  160. codeframe/ui/models.py +999 -0
  161. codeframe/ui/response_models.py +201 -0
  162. codeframe/ui/routers/__init__.py +5 -0
  163. codeframe/ui/routers/_helpers.py +29 -0
  164. codeframe/ui/routers/batches_v2.py +315 -0
  165. codeframe/ui/routers/blockers_v2.py +320 -0
  166. codeframe/ui/routers/checkpoints_v2.py +310 -0
  167. codeframe/ui/routers/costs_v2.py +322 -0
  168. codeframe/ui/routers/diagnose_v2.py +225 -0
  169. codeframe/ui/routers/discovery_v2.py +417 -0
  170. codeframe/ui/routers/environment_v2.py +284 -0
  171. codeframe/ui/routers/events_v2.py +75 -0
  172. codeframe/ui/routers/gates_v2.py +166 -0
  173. codeframe/ui/routers/git_v2.py +284 -0
  174. codeframe/ui/routers/github_integrations_v2.py +532 -0
  175. codeframe/ui/routers/interactive_sessions_v2.py +238 -0
  176. codeframe/ui/routers/pr_v2.py +709 -0
  177. codeframe/ui/routers/prd_v2.py +695 -0
  178. codeframe/ui/routers/proof_v2.py +755 -0
  179. codeframe/ui/routers/review_v2.py +360 -0
  180. codeframe/ui/routers/schedule_v2.py +214 -0
  181. codeframe/ui/routers/session_chat_ws.py +354 -0
  182. codeframe/ui/routers/settings_v2.py +562 -0
  183. codeframe/ui/routers/streaming_v2.py +155 -0
  184. codeframe/ui/routers/tasks_v2.py +1098 -0
  185. codeframe/ui/routers/templates_v2.py +232 -0
  186. codeframe/ui/routers/terminal_ws.py +267 -0
  187. codeframe/ui/routers/workspace_v2.py +527 -0
  188. codeframe/ui/server.py +568 -0
  189. codeframe/ui/shared.py +241 -0
  190. codeframe/workspace/__init__.py +5 -0
  191. codeframe/workspace/manager.py +249 -0
  192. codeframe_ai-0.9.0.dist-info/METADATA +517 -0
  193. codeframe_ai-0.9.0.dist-info/RECORD +197 -0
  194. codeframe_ai-0.9.0.dist-info/WHEEL +5 -0
  195. codeframe_ai-0.9.0.dist-info/entry_points.txt +3 -0
  196. codeframe_ai-0.9.0.dist-info/licenses/LICENSE +661 -0
  197. codeframe_ai-0.9.0.dist-info/top_level.txt +1 -0
codeframe/ui/models.py ADDED
@@ -0,0 +1,999 @@
1
+ """Pydantic models for CodeFRAME API requests and responses.
2
+
3
+ Task: cf-11.1 - Request/Response models for project creation
4
+ Enhanced: cf-119 - OpenAPI documentation with examples
5
+ """
6
+
7
+ from enum import Enum
8
+ from pydantic import BaseModel, Field, model_validator, ConfigDict
9
+ from typing import Literal, Optional, List
10
+
11
+
12
+ class SourceType(str, Enum):
13
+ """Supported project source types."""
14
+
15
+ GIT_REMOTE = "git_remote"
16
+ LOCAL_PATH = "local_path"
17
+ UPLOAD = "upload"
18
+ EMPTY = "empty"
19
+
20
+
21
+ class ProjectCreateRequest(BaseModel):
22
+ """Request model for creating a new project."""
23
+
24
+ model_config = ConfigDict(
25
+ json_schema_extra={
26
+ "example": {
27
+ "name": "my-web-app",
28
+ "description": "A modern web application with React frontend and FastAPI backend",
29
+ "source_type": "git_remote",
30
+ "source_location": "https://github.com/example/my-web-app.git",
31
+ "source_branch": "main",
32
+ "workspace_name": "my-web-app-workspace"
33
+ }
34
+ }
35
+ )
36
+
37
+ # Required
38
+ name: str = Field(
39
+ ...,
40
+ min_length=1,
41
+ max_length=100,
42
+ description="Project name (unique identifier, 1-100 characters)"
43
+ )
44
+ description: str = Field(
45
+ ...,
46
+ min_length=1,
47
+ max_length=500,
48
+ description="Project description explaining the purpose and scope (1-500 characters)"
49
+ )
50
+
51
+ # Optional - source configuration
52
+ source_type: Optional[SourceType] = Field(
53
+ default=SourceType.EMPTY,
54
+ description="Source type for project initialization: 'git_remote' (clone from URL), "
55
+ "'local_path' (link existing directory), 'upload' (upload files), "
56
+ "'empty' (start fresh)"
57
+ )
58
+ source_location: Optional[str] = Field(
59
+ default=None,
60
+ description="Source location - Git URL for 'git_remote', filesystem path for 'local_path', "
61
+ "or upload filename for 'upload'. Required unless source_type is 'empty'."
62
+ )
63
+ source_branch: Optional[str] = Field(
64
+ default="main",
65
+ description="Git branch to clone (only used when source_type is 'git_remote')"
66
+ )
67
+
68
+ # Optional - workspace naming (auto-generated if not provided)
69
+ workspace_name: Optional[str] = Field(
70
+ default=None,
71
+ description="Custom workspace directory name. If not provided, auto-generated from project name."
72
+ )
73
+
74
+ @model_validator(mode="after")
75
+ def validate_source(self):
76
+ """Validate source_location is provided when source_type requires it."""
77
+ if self.source_type != SourceType.EMPTY and not self.source_location:
78
+ raise ValueError(f"source_location required when source_type={self.source_type}")
79
+ return self
80
+
81
+
82
+ class ReviewRequest(BaseModel):
83
+ """Request model for triggering code review.
84
+
85
+ Sprint 9 - User Story 1: Review Agent API (T056)
86
+ """
87
+
88
+ model_config = ConfigDict(
89
+ json_schema_extra={
90
+ "example": {
91
+ "task_id": 42,
92
+ "project_id": 1,
93
+ "files_modified": [
94
+ "src/components/Button.tsx",
95
+ "src/utils/validation.ts",
96
+ "tests/components/Button.test.tsx"
97
+ ]
98
+ }
99
+ }
100
+ )
101
+
102
+ task_id: int = Field(..., description="Task ID associated with the code changes to review")
103
+ project_id: int = Field(..., description="Project ID containing the task")
104
+ files_modified: List[str] = Field(
105
+ ...,
106
+ description="List of file paths that were modified and should be reviewed"
107
+ )
108
+
109
+
110
+ class QualityGatesRequest(BaseModel):
111
+ """Request model for triggering quality gates.
112
+
113
+ Sprint 10 - Phase 3: Quality Gates API (T065)
114
+ """
115
+
116
+ model_config = ConfigDict(
117
+ json_schema_extra={
118
+ "example": {
119
+ "gate_types": ["tests", "linting", "type_check"]
120
+ }
121
+ }
122
+ )
123
+
124
+ gate_types: Optional[List[str]] = Field(
125
+ default=None,
126
+ description="Optional list of gate types to run. If not provided, all gates run. "
127
+ "Valid values: 'tests' (run test suite), 'type_check' (mypy/tsc), "
128
+ "'coverage' (code coverage), 'code_review' (AI review), 'linting' (ruff/eslint)"
129
+ )
130
+
131
+
132
+ class ProjectResponse(BaseModel):
133
+ """Response model for project data.
134
+
135
+ Task: cf-11.1 - Create ProjectResponse model
136
+ Updated cf-17.1: Added phase field for project phase tracking
137
+ """
138
+
139
+ model_config = ConfigDict(
140
+ from_attributes=True,
141
+ json_schema_extra={
142
+ "example": {
143
+ "id": 1,
144
+ "name": "my-web-app",
145
+ "status": "running",
146
+ "phase": "active",
147
+ "created_at": "2026-02-03T10:30:00Z",
148
+ "config": {
149
+ "tech_stack": "Python with FastAPI, React frontend",
150
+ "git_initialized": True
151
+ }
152
+ }
153
+ }
154
+ )
155
+
156
+ id: int = Field(..., description="Unique project ID (auto-generated)")
157
+ name: str = Field(..., description="Project name as specified during creation")
158
+ status: str = Field(
159
+ ...,
160
+ description="Project execution status: 'init' (created), 'running' (agents active), "
161
+ "'paused' (manually paused), 'completed' (all tasks done), 'failed' (error state)"
162
+ )
163
+ phase: str = Field(
164
+ default="discovery",
165
+ description="Project lifecycle phase: 'discovery' (analyzing codebase), "
166
+ "'planning' (generating tasks), 'active' (development in progress), "
167
+ "'review' (code review), 'complete' (finished)"
168
+ )
169
+ created_at: str = Field(..., description="ISO 8601 timestamp of project creation (e.g., '2026-02-03T10:30:00Z')")
170
+ config: Optional[dict] = Field(
171
+ default=None,
172
+ description="Optional project configuration including tech_stack, git settings, etc."
173
+ )
174
+
175
+
176
+ class CheckpointCreateRequest(BaseModel):
177
+ """Request model for creating a checkpoint (Sprint 10 Phase 4, T093)."""
178
+
179
+ model_config = ConfigDict(
180
+ json_schema_extra={
181
+ "example": {
182
+ "name": "pre-refactor-auth",
183
+ "description": "Checkpoint before refactoring authentication module",
184
+ "trigger": "manual"
185
+ }
186
+ }
187
+ )
188
+
189
+ name: str = Field(
190
+ ...,
191
+ min_length=1,
192
+ max_length=100,
193
+ description="Checkpoint name for identification (1-100 characters)"
194
+ )
195
+ description: Optional[str] = Field(
196
+ None,
197
+ max_length=500,
198
+ description="Optional description explaining the checkpoint purpose (max 500 characters)"
199
+ )
200
+ trigger: str = Field(
201
+ default="manual",
202
+ description="Trigger type: 'manual' (user-initiated), 'auto' (scheduled), "
203
+ "'phase_transition' (automatic on phase change)"
204
+ )
205
+
206
+
207
+ class CheckpointResponse(BaseModel):
208
+ """Response model for a checkpoint (Sprint 10 Phase 4, T092-T094)."""
209
+
210
+ model_config = ConfigDict(
211
+ json_schema_extra={
212
+ "example": {
213
+ "id": 5,
214
+ "project_id": 1,
215
+ "name": "pre-refactor-auth",
216
+ "description": "Checkpoint before refactoring authentication module",
217
+ "trigger": "manual",
218
+ "git_commit": "a1b2c3d4e5f6",
219
+ "database_backup_path": "/backups/project_1/checkpoint_5.db",
220
+ "context_snapshot_path": "/backups/project_1/checkpoint_5_context.json",
221
+ "metadata": {
222
+ "task_count": 15,
223
+ "completed_tasks": 8,
224
+ "phase": "active"
225
+ },
226
+ "created_at": "2026-02-03T14:30:00Z"
227
+ }
228
+ }
229
+ )
230
+
231
+ id: int = Field(..., description="Unique checkpoint ID")
232
+ project_id: int = Field(..., description="Project this checkpoint belongs to")
233
+ name: str = Field(..., description="Checkpoint name")
234
+ description: Optional[str] = Field(None, description="Optional checkpoint description")
235
+ trigger: str = Field(..., description="What triggered checkpoint creation (manual/auto/phase_transition)")
236
+ git_commit: str = Field(..., description="Git commit hash at checkpoint time")
237
+ database_backup_path: str = Field(..., description="Path to database backup file")
238
+ context_snapshot_path: str = Field(..., description="Path to context snapshot file")
239
+ metadata: dict = Field(..., description="Checkpoint metadata including task counts, phase, etc.")
240
+ created_at: str = Field(..., description="ISO 8601 timestamp of checkpoint creation")
241
+
242
+
243
+ class RestoreCheckpointRequest(BaseModel):
244
+ """Request model for restoring a checkpoint (Sprint 10 Phase 4, T096-T097)."""
245
+
246
+ model_config = ConfigDict(
247
+ json_schema_extra={
248
+ "example": {
249
+ "confirm_restore": True
250
+ }
251
+ }
252
+ )
253
+
254
+ confirm_restore: bool = Field(
255
+ default=False,
256
+ description="If False, returns diff preview only. If True, actually restores the checkpoint. "
257
+ "Use False first to review changes before committing to restore."
258
+ )
259
+
260
+
261
+ class CheckpointDiffResponse(BaseModel):
262
+ """Response model for checkpoint diff (Sprint 10 Phase 4)."""
263
+
264
+ model_config = ConfigDict(
265
+ json_schema_extra={
266
+ "example": {
267
+ "files_changed": 12,
268
+ "insertions": 245,
269
+ "deletions": 89,
270
+ "diff": "diff --git a/src/auth.py b/src/auth.py\n--- a/src/auth.py\n+++ b/src/auth.py\n@@ -10,6 +10,8 @@..."
271
+ }
272
+ }
273
+ )
274
+
275
+ files_changed: int = Field(..., description="Number of files changed since checkpoint")
276
+ insertions: int = Field(..., description="Total number of lines inserted since checkpoint")
277
+ deletions: int = Field(..., description="Total number of lines deleted since checkpoint")
278
+ diff: str = Field(..., description="Full git diff output showing all changes since checkpoint")
279
+
280
+
281
+ # Multi-Agent Per Project API Models (Phase 3)
282
+
283
+
284
+ class AgentAssignmentRequest(BaseModel):
285
+ """Request model for assigning an agent to a project."""
286
+
287
+ model_config = ConfigDict(
288
+ json_schema_extra={
289
+ "example": {
290
+ "agent_id": "backend-agent-001",
291
+ "role": "primary_backend"
292
+ }
293
+ }
294
+ )
295
+
296
+ agent_id: str = Field(
297
+ ...,
298
+ min_length=1,
299
+ max_length=100,
300
+ description="Agent ID to assign to the project (1-100 characters)"
301
+ )
302
+ role: str = Field(
303
+ default="worker",
304
+ min_length=1,
305
+ max_length=50,
306
+ description="Agent's role in this project. Common roles: 'lead' (orchestrator), "
307
+ "'primary_backend', 'frontend', 'test', 'code_reviewer', 'worker' (default)"
308
+ )
309
+
310
+
311
+ class AgentRoleUpdateRequest(BaseModel):
312
+ """Request model for updating an agent's role on a project."""
313
+
314
+ model_config = ConfigDict(
315
+ json_schema_extra={
316
+ "example": {
317
+ "role": "code_reviewer"
318
+ }
319
+ }
320
+ )
321
+
322
+ role: str = Field(
323
+ ...,
324
+ min_length=1,
325
+ max_length=50,
326
+ description="New role for the agent. Common roles: 'lead', 'primary_backend', "
327
+ "'secondary_backend', 'frontend', 'test', 'code_reviewer'"
328
+ )
329
+
330
+
331
+ class AgentMetricsResponse(BaseModel):
332
+ """Response model for agent maturity metrics."""
333
+
334
+ model_config = ConfigDict(
335
+ json_schema_extra={
336
+ "example": {
337
+ "task_count": 25,
338
+ "completed_count": 22,
339
+ "completion_rate": 0.88,
340
+ "avg_test_pass_rate": 0.95,
341
+ "self_correction_rate": 0.76,
342
+ "maturity_score": 0.85,
343
+ "last_assessed": "2026-02-03T15:00:00Z"
344
+ }
345
+ }
346
+ )
347
+
348
+ task_count: Optional[int] = Field(None, description="Total number of tasks ever assigned to this agent")
349
+ completed_count: Optional[int] = Field(None, description="Number of tasks successfully completed")
350
+ completion_rate: Optional[float] = Field(
351
+ None,
352
+ description="Task completion rate as decimal (0.0-1.0). Calculated as completed_count/task_count."
353
+ )
354
+ avg_test_pass_rate: Optional[float] = Field(
355
+ None,
356
+ description="Average test pass rate across all completed tasks (0.0-1.0)"
357
+ )
358
+ self_correction_rate: Optional[float] = Field(
359
+ None,
360
+ description="Rate of first-attempt success without requiring fixes (0.0-1.0)"
361
+ )
362
+ maturity_score: Optional[float] = Field(
363
+ None,
364
+ description="Weighted overall maturity score combining all metrics (0.0-1.0)"
365
+ )
366
+ last_assessed: Optional[str] = Field(
367
+ None,
368
+ description="ISO 8601 timestamp of when metrics were last calculated"
369
+ )
370
+
371
+
372
+ class AgentAssignmentResponse(BaseModel):
373
+ """Response model for agent assignment data."""
374
+
375
+ model_config = ConfigDict(
376
+ json_schema_extra={
377
+ "example": {
378
+ "agent_id": "backend-agent-001",
379
+ "type": "backend",
380
+ "provider": "claude",
381
+ "maturity_level": "senior",
382
+ "status": "working",
383
+ "current_task_id": 42,
384
+ "last_heartbeat": "2026-02-03T15:30:00Z",
385
+ "metrics": {
386
+ "task_count": 25,
387
+ "completed_count": 22,
388
+ "completion_rate": 0.88,
389
+ "maturity_score": 0.85
390
+ },
391
+ "assignment_id": 7,
392
+ "role": "primary_backend",
393
+ "assigned_at": "2026-02-01T09:00:00Z",
394
+ "unassigned_at": None,
395
+ "is_active": True
396
+ }
397
+ }
398
+ )
399
+
400
+ agent_id: str = Field(..., description="Unique agent identifier")
401
+ type: str = Field(
402
+ ...,
403
+ description="Agent specialization type: 'lead' (orchestrator), 'backend', 'frontend', 'test', 'review'"
404
+ )
405
+ provider: Optional[str] = Field(
406
+ None,
407
+ description="LLM provider powering the agent: 'claude' (Anthropic), 'gpt4' (OpenAI)"
408
+ )
409
+ maturity_level: Optional[str] = Field(
410
+ None,
411
+ description="Agent maturity level based on performance: 'junior', 'mid', 'senior', 'expert'"
412
+ )
413
+ status: Optional[str] = Field(
414
+ None,
415
+ description="Current agent status: 'idle' (waiting for tasks), 'working' (executing task), "
416
+ "'blocked' (waiting for human input), 'offline' (not available)"
417
+ )
418
+ current_task_id: Optional[int] = Field(
419
+ None,
420
+ description="ID of the task currently being executed (null if idle)"
421
+ )
422
+ last_heartbeat: Optional[str] = Field(
423
+ None,
424
+ description="ISO 8601 timestamp of last agent activity"
425
+ )
426
+ metrics: Optional[AgentMetricsResponse] = Field(
427
+ None,
428
+ description="Agent performance metrics (null if never assessed)"
429
+ )
430
+ assignment_id: int = Field(..., description="Unique ID for this project-agent assignment")
431
+ role: str = Field(..., description="Agent's assigned role within this specific project")
432
+ assigned_at: str = Field(..., description="ISO 8601 timestamp when agent was assigned to project")
433
+ unassigned_at: Optional[str] = Field(
434
+ None,
435
+ description="ISO 8601 timestamp when agent was removed from project (null if still active)"
436
+ )
437
+ is_active: bool = Field(..., description="True if agent is currently assigned and active on project")
438
+
439
+
440
+ class ProjectAssignmentResponse(BaseModel):
441
+ """Response model for project assignment data (from agent perspective)."""
442
+
443
+ model_config = ConfigDict(
444
+ json_schema_extra={
445
+ "example": {
446
+ "project_id": 1,
447
+ "name": "my-web-app",
448
+ "description": "A modern web application",
449
+ "status": "running",
450
+ "phase": "active",
451
+ "role": "primary_backend",
452
+ "assigned_at": "2026-02-01T09:00:00Z",
453
+ "unassigned_at": None,
454
+ "is_active": True
455
+ }
456
+ }
457
+ )
458
+
459
+ project_id: int = Field(..., description="Unique project identifier")
460
+ name: str = Field(..., description="Project name")
461
+ description: Optional[str] = Field(None, description="Project description/purpose")
462
+ status: str = Field(..., description="Project execution status (init/running/paused/completed/failed)")
463
+ phase: str = Field(..., description="Project lifecycle phase (discovery/planning/active/review/complete)")
464
+ role: str = Field(..., description="Agent's assigned role within this project")
465
+ assigned_at: str = Field(..., description="ISO 8601 timestamp of assignment")
466
+ unassigned_at: Optional[str] = Field(None, description="ISO 8601 timestamp of removal (null if active)")
467
+ is_active: bool = Field(..., description="True if this assignment is currently active")
468
+
469
+
470
+ # ============================================================================
471
+ # Core Endpoint Response Models (Phase 2 OpenAPI Documentation)
472
+ # ============================================================================
473
+
474
+
475
+ class TaskResponse(BaseModel):
476
+ """Response model for task data."""
477
+
478
+ model_config = ConfigDict(
479
+ json_schema_extra={
480
+ "example": {
481
+ "id": 42,
482
+ "project_id": 1,
483
+ "title": "Implement user authentication endpoint",
484
+ "description": "Create POST /api/auth/login endpoint with JWT token generation",
485
+ "status": "in_progress",
486
+ "priority": 2,
487
+ "workflow_step": 3,
488
+ "assigned_to": "backend-agent-001",
489
+ "depends_on": "41",
490
+ "requires_mcp": False,
491
+ "created_at": "2026-02-03T10:00:00Z",
492
+ "updated_at": "2026-02-03T14:30:00Z"
493
+ }
494
+ }
495
+ )
496
+
497
+ id: int = Field(..., description="Unique task ID")
498
+ project_id: int = Field(..., description="Project this task belongs to")
499
+ title: str = Field(..., description="Task title/summary")
500
+ description: str = Field(default="", description="Detailed task description")
501
+ status: str = Field(
502
+ ...,
503
+ description="Task status: 'pending' (awaiting assignment), 'assigned' (agent assigned), "
504
+ "'in_progress' (being worked on), 'blocked' (waiting for human), "
505
+ "'completed' (done), 'failed' (error)"
506
+ )
507
+ priority: int = Field(
508
+ ...,
509
+ description="Task priority (0=critical, 1=high, 2=medium, 3=low, 4=backlog)"
510
+ )
511
+ workflow_step: int = Field(default=1, description="Workflow step number for ordering")
512
+ assigned_to: Optional[str] = Field(None, description="Agent ID currently assigned to task")
513
+ depends_on: Optional[str] = Field(None, description="Comma-separated list of task IDs this depends on")
514
+ requires_mcp: bool = Field(default=False, description="Whether task requires MCP server access")
515
+ created_at: str = Field(..., description="ISO 8601 timestamp of task creation")
516
+ updated_at: Optional[str] = Field(None, description="ISO 8601 timestamp of last update")
517
+
518
+
519
+ class TaskListResponse(BaseModel):
520
+ """Response model for paginated task list."""
521
+
522
+ model_config = ConfigDict(
523
+ json_schema_extra={
524
+ "example": {
525
+ "tasks": [
526
+ {
527
+ "id": 42,
528
+ "project_id": 1,
529
+ "title": "Implement user authentication",
530
+ "status": "in_progress",
531
+ "priority": 2
532
+ }
533
+ ],
534
+ "total": 25
535
+ }
536
+ }
537
+ )
538
+
539
+ tasks: List[dict] = Field(..., description="List of task objects")
540
+ total: int = Field(..., description="Total number of tasks matching filter (before pagination)")
541
+
542
+
543
+ class ProjectListResponse(BaseModel):
544
+ """Response model for project list."""
545
+
546
+ model_config = ConfigDict(
547
+ json_schema_extra={
548
+ "example": {
549
+ "projects": [
550
+ {
551
+ "id": 1,
552
+ "name": "my-web-app",
553
+ "status": "running",
554
+ "phase": "active",
555
+ "created_at": "2026-02-01T09:00:00Z"
556
+ }
557
+ ]
558
+ }
559
+ }
560
+ )
561
+
562
+ projects: List[dict] = Field(..., description="List of project objects accessible to the user")
563
+
564
+
565
+ class ProjectStatusResponse(BaseModel):
566
+ """Response model for project status endpoint."""
567
+
568
+ model_config = ConfigDict(
569
+ json_schema_extra={
570
+ "example": {
571
+ "project_id": 1,
572
+ "name": "my-web-app",
573
+ "status": "running",
574
+ "phase": "active",
575
+ "workflow_step": 3,
576
+ "progress": {
577
+ "total_tasks": 25,
578
+ "completed_tasks": 15,
579
+ "in_progress_tasks": 3,
580
+ "blocked_tasks": 1,
581
+ "completion_percentage": 60.0
582
+ }
583
+ }
584
+ }
585
+ )
586
+
587
+ project_id: int = Field(..., description="Project ID")
588
+ name: str = Field(..., description="Project name")
589
+ status: str = Field(..., description="Project execution status")
590
+ phase: str = Field(..., description="Project lifecycle phase")
591
+ workflow_step: int = Field(default=1, description="Current workflow step")
592
+ progress: dict = Field(..., description="Progress metrics including task counts and completion percentage")
593
+
594
+
595
+ class ActivityItemResponse(BaseModel):
596
+ """Response model for a single activity log item."""
597
+
598
+ model_config = ConfigDict(
599
+ json_schema_extra={
600
+ "example": {
601
+ "id": 100,
602
+ "action": "task_completed",
603
+ "timestamp": "2026-02-03T14:30:00Z",
604
+ "details": {
605
+ "task_id": 42,
606
+ "task_title": "Implement authentication",
607
+ "agent_id": "backend-agent-001"
608
+ }
609
+ }
610
+ }
611
+ )
612
+
613
+ id: int = Field(..., description="Activity log entry ID")
614
+ action: str = Field(..., description="Action type (task_created, task_completed, blocker_created, etc.)")
615
+ timestamp: str = Field(..., description="ISO 8601 timestamp of when action occurred")
616
+ details: Optional[dict] = Field(None, description="Additional action-specific details")
617
+
618
+
619
+ class ActivityListResponse(BaseModel):
620
+ """Response model for activity log list."""
621
+
622
+ model_config = ConfigDict(
623
+ json_schema_extra={
624
+ "example": {
625
+ "activity": [
626
+ {
627
+ "id": 100,
628
+ "action": "task_completed",
629
+ "timestamp": "2026-02-03T14:30:00Z",
630
+ "details": {"task_id": 42}
631
+ }
632
+ ]
633
+ }
634
+ }
635
+ )
636
+
637
+ activity: List[dict] = Field(..., description="List of activity log items, most recent first")
638
+
639
+
640
+ class PRDResponse(BaseModel):
641
+ """Response model for PRD (Product Requirements Document) endpoint."""
642
+
643
+ model_config = ConfigDict(
644
+ json_schema_extra={
645
+ "example": {
646
+ "project_id": "1",
647
+ "prd_content": "# My Web App PRD\n\n## Overview\nA modern web application...",
648
+ "generated_at": "2026-02-01T10:00:00Z",
649
+ "updated_at": "2026-02-03T14:00:00Z",
650
+ "status": "available"
651
+ }
652
+ }
653
+ )
654
+
655
+ project_id: str = Field(..., description="Project ID (as string for API consistency)")
656
+ prd_content: str = Field(..., description="PRD content in Markdown format")
657
+ generated_at: str = Field(..., description="ISO 8601 timestamp of initial PRD generation")
658
+ updated_at: str = Field(..., description="ISO 8601 timestamp of last PRD update")
659
+ status: str = Field(
660
+ ...,
661
+ description="PRD status: 'available' (ready to use), 'generating' (being created), "
662
+ "'not_found' (no PRD exists)"
663
+ )
664
+
665
+
666
+ class IssueResponse(BaseModel):
667
+ """Response model for a single issue."""
668
+
669
+ model_config = ConfigDict(
670
+ json_schema_extra={
671
+ "example": {
672
+ "id": "issue-1",
673
+ "issue_number": "1",
674
+ "title": "User authentication not working",
675
+ "description": "Login endpoint returns 500 error",
676
+ "status": "open",
677
+ "priority": 1,
678
+ "depends_on": [],
679
+ "proposed_by": "human",
680
+ "created_at": "2026-02-03T09:00:00Z",
681
+ "updated_at": "2026-02-03T14:00:00Z",
682
+ "completed_at": None
683
+ }
684
+ }
685
+ )
686
+
687
+ id: str = Field(..., description="Issue ID")
688
+ issue_number: str = Field(..., description="Human-readable issue number")
689
+ title: str = Field(..., description="Issue title")
690
+ description: str = Field(..., description="Detailed issue description")
691
+ status: str = Field(..., description="Issue status (open, in_progress, resolved, closed)")
692
+ priority: int = Field(..., description="Issue priority (0=critical to 4=backlog)")
693
+ depends_on: List[str] = Field(default_factory=list, description="List of issue IDs this depends on")
694
+ proposed_by: str = Field(..., description="Who created the issue: 'agent' or 'human'")
695
+ created_at: str = Field(..., description="ISO 8601 creation timestamp")
696
+ updated_at: str = Field(..., description="ISO 8601 last update timestamp")
697
+ completed_at: Optional[str] = Field(None, description="ISO 8601 completion timestamp (null if open)")
698
+
699
+
700
+ class IssuesListResponse(BaseModel):
701
+ """Response model for issues list endpoint."""
702
+
703
+ model_config = ConfigDict(
704
+ json_schema_extra={
705
+ "example": {
706
+ "issues": [
707
+ {
708
+ "id": "issue-1",
709
+ "issue_number": "1",
710
+ "title": "User authentication not working",
711
+ "status": "open"
712
+ }
713
+ ],
714
+ "total_issues": 5,
715
+ "total_tasks": 25
716
+ }
717
+ }
718
+ )
719
+
720
+ issues: List[dict] = Field(..., description="List of issue objects")
721
+ total_issues: int = Field(..., description="Total number of issues")
722
+ total_tasks: int = Field(..., description="Total number of tasks across all issues")
723
+
724
+
725
+ class SessionStateResponse(BaseModel):
726
+ """Response model for session state endpoint."""
727
+
728
+ model_config = ConfigDict(
729
+ json_schema_extra={
730
+ "example": {
731
+ "last_session": {
732
+ "summary": "Completed authentication module, started on API endpoints",
733
+ "timestamp": "2026-02-02T18:00:00Z"
734
+ },
735
+ "next_actions": [
736
+ "Complete /api/users endpoint",
737
+ "Add input validation",
738
+ "Write unit tests"
739
+ ],
740
+ "progress_pct": 45.5,
741
+ "active_blockers": [
742
+ {"id": 3, "title": "Need database credentials"}
743
+ ]
744
+ }
745
+ }
746
+ )
747
+
748
+ last_session: dict = Field(
749
+ ...,
750
+ description="Summary of the last session including timestamp and what was accomplished"
751
+ )
752
+ next_actions: List[str] = Field(
753
+ default_factory=list,
754
+ description="List of recommended next actions for the current session"
755
+ )
756
+ progress_pct: float = Field(
757
+ default=0.0,
758
+ description="Overall project progress as percentage (0.0-100.0)"
759
+ )
760
+ active_blockers: List[dict] = Field(
761
+ default_factory=list,
762
+ description="List of active blockers requiring human attention"
763
+ )
764
+
765
+
766
+ class AgentStartResponse(BaseModel):
767
+ """Response model for agent start/pause/resume operations."""
768
+
769
+ model_config = ConfigDict(
770
+ json_schema_extra={
771
+ "example": {
772
+ "message": "Starting Lead Agent for project 1",
773
+ "status": "starting"
774
+ }
775
+ }
776
+ )
777
+
778
+ message: str = Field(..., description="Human-readable status message")
779
+ status: str = Field(
780
+ ...,
781
+ description="Operation status: 'starting' (agent launching), 'running' (already active), "
782
+ "'completed' (discovery finished), 'paused', 'resumed'"
783
+ )
784
+
785
+
786
+ class BlockerResponse(BaseModel):
787
+ """Response model for blocker data."""
788
+
789
+ model_config = ConfigDict(
790
+ json_schema_extra={
791
+ "example": {
792
+ "id": 5,
793
+ "project_id": 1,
794
+ "task_id": 42,
795
+ "blocker_type": "SYNC",
796
+ "title": "Database credentials needed",
797
+ "description": "Cannot connect to production database without credentials",
798
+ "status": "PENDING",
799
+ "priority": "high",
800
+ "created_at": "2026-02-03T10:00:00Z",
801
+ "expires_at": "2026-02-03T16:00:00Z",
802
+ "resolved_at": None,
803
+ "answer": None
804
+ }
805
+ }
806
+ )
807
+
808
+ id: int = Field(..., description="Unique blocker ID")
809
+ project_id: int = Field(..., description="Project this blocker belongs to")
810
+ task_id: Optional[int] = Field(None, description="Task that created the blocker (if any)")
811
+ blocker_type: str = Field(
812
+ ...,
813
+ description="Blocker type: 'SYNC' (blocks task execution), 'ASYNC' (can continue with workaround)"
814
+ )
815
+ title: str = Field(..., description="Brief blocker title")
816
+ description: str = Field(..., description="Detailed description of what's blocking progress")
817
+ status: str = Field(
818
+ ...,
819
+ description="Blocker status: 'PENDING' (awaiting resolution), 'RESOLVED' (answered), "
820
+ "'EXPIRED' (timed out)"
821
+ )
822
+ priority: str = Field(..., description="Blocker priority: 'critical', 'high', 'medium', 'low'")
823
+ created_at: str = Field(..., description="ISO 8601 creation timestamp")
824
+ expires_at: Optional[str] = Field(None, description="ISO 8601 expiration timestamp (for timed blockers)")
825
+ resolved_at: Optional[str] = Field(None, description="ISO 8601 resolution timestamp")
826
+ answer: Optional[str] = Field(None, description="Human-provided answer/resolution")
827
+
828
+
829
+ class BlockerListResponse(BaseModel):
830
+ """Response model for blocker list endpoint."""
831
+
832
+ model_config = ConfigDict(
833
+ json_schema_extra={
834
+ "example": {
835
+ "blockers": [
836
+ {
837
+ "id": 5,
838
+ "title": "Database credentials needed",
839
+ "status": "PENDING",
840
+ "blocker_type": "SYNC"
841
+ }
842
+ ],
843
+ "total": 3,
844
+ "pending_count": 2,
845
+ "sync_count": 1,
846
+ "async_count": 2
847
+ }
848
+ }
849
+ )
850
+
851
+ blockers: List[dict] = Field(..., description="List of blocker objects")
852
+ total: int = Field(..., description="Total number of blockers")
853
+ pending_count: int = Field(..., description="Number of blockers in PENDING status")
854
+ sync_count: int = Field(..., description="Number of SYNC (blocking) blockers")
855
+ async_count: int = Field(..., description="Number of ASYNC (non-blocking) blockers")
856
+
857
+
858
+ class BlockerMetricsResponse(BaseModel):
859
+ """Response model for blocker metrics endpoint."""
860
+
861
+ model_config = ConfigDict(
862
+ json_schema_extra={
863
+ "example": {
864
+ "avg_resolution_time_seconds": 3600.5,
865
+ "expiration_rate_percent": 15.0,
866
+ "total_blockers": 20,
867
+ "resolved_count": 15,
868
+ "expired_count": 3,
869
+ "pending_count": 2,
870
+ "sync_count": 8,
871
+ "async_count": 12
872
+ }
873
+ }
874
+ )
875
+
876
+ avg_resolution_time_seconds: Optional[float] = Field(
877
+ None,
878
+ description="Average time to resolve blockers in seconds (null if no resolved blockers)"
879
+ )
880
+ expiration_rate_percent: float = Field(
881
+ ...,
882
+ description="Percentage of blockers that expired without resolution"
883
+ )
884
+ total_blockers: int = Field(..., description="Total number of blockers ever created")
885
+ resolved_count: int = Field(..., description="Number of blockers successfully resolved")
886
+ expired_count: int = Field(..., description="Number of blockers that expired")
887
+ pending_count: int = Field(..., description="Number of blockers currently pending")
888
+ sync_count: int = Field(..., description="Total SYNC blockers")
889
+ async_count: int = Field(..., description="Total ASYNC blockers")
890
+
891
+
892
+ class ErrorResponse(BaseModel):
893
+ """Generic error response model for API errors."""
894
+
895
+ model_config = ConfigDict(
896
+ json_schema_extra={
897
+ "example": {
898
+ "detail": "Project 99 not found"
899
+ }
900
+ }
901
+ )
902
+
903
+ detail: str = Field(..., description="Error message describing what went wrong")
904
+
905
+
906
+ # ============================================================================
907
+ # Settings (issue #554)
908
+ # ============================================================================
909
+
910
+
911
+ AgentType = Literal["claude_code", "codex", "opencode", "react"]
912
+ AGENT_TYPES: tuple[AgentType, ...] = ("claude_code", "codex", "opencode", "react")
913
+
914
+
915
+ class AgentTypeModelConfig(BaseModel):
916
+ """Default model for a single agent type."""
917
+
918
+ agent_type: AgentType = Field(
919
+ ..., description="One of: claude_code, codex, opencode, react"
920
+ )
921
+ default_model: str = Field(
922
+ default="",
923
+ description="Model identifier (e.g. 'claude-opus-4', 'gpt-4o'); empty string means unset",
924
+ )
925
+
926
+
927
+ class AgentSettings(BaseModel):
928
+ """Agent settings shared by GET response and PUT request.
929
+
930
+ Defaults match `EnvironmentConfig.agent_budget.max_iterations` so that a
931
+ fresh workspace round-trips its real defaults through GET.
932
+ """
933
+
934
+ agent_models: List[AgentTypeModelConfig] = Field(
935
+ ..., description="Default model per agent type"
936
+ )
937
+ max_turns: int = Field(
938
+ default=100, gt=0, description="Maximum turns per task (must be > 0)"
939
+ )
940
+ max_cost_usd: Optional[float] = Field(
941
+ default=None, ge=0, description="Maximum cost per task in USD"
942
+ )
943
+
944
+
945
+ class AgentSettingsResponse(AgentSettings):
946
+ """Response shape for GET /api/v2/settings."""
947
+
948
+
949
+ class UpdateAgentSettingsRequest(AgentSettings):
950
+ """Request shape for PUT /api/v2/settings."""
951
+
952
+
953
+ # ============================================================================
954
+ # API Key Management (issue #555)
955
+ # ============================================================================
956
+
957
+
958
+ KeyProvider = Literal["LLM_ANTHROPIC", "LLM_OPENAI", "GIT_GITHUB"]
959
+ KEY_PROVIDERS: tuple[KeyProvider, ...] = ("LLM_ANTHROPIC", "LLM_OPENAI", "GIT_GITHUB")
960
+
961
+ KeySource = Literal["environment", "stored", "none"]
962
+
963
+
964
+ class StoreKeyRequest(BaseModel):
965
+ """Request shape for PUT /api/v2/settings/keys/{provider}."""
966
+
967
+ value: str = Field(..., min_length=1, description="The credential value to store")
968
+
969
+
970
+ class KeyStatusResponse(BaseModel):
971
+ """Status of a single API key. Never includes the plaintext value."""
972
+
973
+ provider: KeyProvider = Field(..., description="One of: LLM_ANTHROPIC, LLM_OPENAI, GIT_GITHUB")
974
+ stored: bool = Field(..., description="True if a key is available (env or storage)")
975
+ source: KeySource = Field(
976
+ ..., description="Where the key comes from: environment, stored, or none"
977
+ )
978
+ last_four: Optional[str] = Field(
979
+ default=None,
980
+ description="Last 4 characters of the key for display (None if no key available)",
981
+ )
982
+
983
+
984
+ class VerifyKeyRequest(BaseModel):
985
+ """Request shape for POST /api/v2/settings/verify-key."""
986
+
987
+ provider: KeyProvider = Field(..., description="Which provider's key to verify")
988
+ value: Optional[str] = Field(
989
+ default=None,
990
+ description="The key value to verify; if None, the stored/env key is used",
991
+ )
992
+
993
+
994
+ class VerifyKeyResponse(BaseModel):
995
+ """Result of a live verification attempt against a provider."""
996
+
997
+ provider: KeyProvider
998
+ valid: bool = Field(..., description="True if the provider accepted the key")
999
+ message: str = Field(..., description="Human-readable result message")