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.
- codeframe/__init__.py +11 -0
- codeframe/__main__.py +20 -0
- codeframe/adapters/__init__.py +5 -0
- codeframe/adapters/e2b/__init__.py +13 -0
- codeframe/adapters/e2b/adapter.py +342 -0
- codeframe/adapters/e2b/budget.py +71 -0
- codeframe/adapters/e2b/credential_scanner.py +134 -0
- codeframe/adapters/llm/__init__.py +92 -0
- codeframe/adapters/llm/anthropic.py +414 -0
- codeframe/adapters/llm/base.py +444 -0
- codeframe/adapters/llm/mock.py +281 -0
- codeframe/adapters/llm/openai.py +483 -0
- codeframe/agents/__init__.py +8 -0
- codeframe/agents/dependency_resolver.py +714 -0
- codeframe/auth/__init__.py +16 -0
- codeframe/auth/api_key_router.py +238 -0
- codeframe/auth/api_keys.py +156 -0
- codeframe/auth/dependencies.py +358 -0
- codeframe/auth/manager.py +178 -0
- codeframe/auth/models.py +30 -0
- codeframe/auth/router.py +93 -0
- codeframe/auth/schemas.py +15 -0
- codeframe/auth/scopes.py +53 -0
- codeframe/cli/__init__.py +12 -0
- codeframe/cli/__main__.py +20 -0
- codeframe/cli/api_client.py +275 -0
- codeframe/cli/app.py +5688 -0
- codeframe/cli/auth.py +122 -0
- codeframe/cli/auth_commands.py +958 -0
- codeframe/cli/commands/__init__.py +5 -0
- codeframe/cli/config_commands.py +79 -0
- codeframe/cli/dashboard_commands.py +67 -0
- codeframe/cli/engines_commands.py +205 -0
- codeframe/cli/env_commands.py +409 -0
- codeframe/cli/helpers.py +56 -0
- codeframe/cli/hooks_commands.py +208 -0
- codeframe/cli/import_commands.py +129 -0
- codeframe/cli/pr_commands.py +549 -0
- codeframe/cli/proof_commands.py +415 -0
- codeframe/cli/stats_commands.py +311 -0
- codeframe/cli/telemetry_runtime.py +153 -0
- codeframe/cli/validators.py +123 -0
- codeframe/config/rate_limits.py +165 -0
- codeframe/core/__init__.py +15 -0
- codeframe/core/adapters/__init__.py +43 -0
- codeframe/core/adapters/agent_adapter.py +114 -0
- codeframe/core/adapters/builtin.py +326 -0
- codeframe/core/adapters/claude_code.py +62 -0
- codeframe/core/adapters/codex.py +393 -0
- codeframe/core/adapters/git_utils.py +40 -0
- codeframe/core/adapters/kilocode.py +126 -0
- codeframe/core/adapters/opencode.py +48 -0
- codeframe/core/adapters/streaming_chat.py +483 -0
- codeframe/core/adapters/subprocess_adapter.py +213 -0
- codeframe/core/adapters/verification_wrapper.py +269 -0
- codeframe/core/agent.py +2183 -0
- codeframe/core/agents_config.py +569 -0
- codeframe/core/api_key_service.py +211 -0
- codeframe/core/artifacts.py +428 -0
- codeframe/core/blocker_detection.py +218 -0
- codeframe/core/blockers.py +433 -0
- codeframe/core/checkpoints.py +481 -0
- codeframe/core/conductor.py +2255 -0
- codeframe/core/config.py +827 -0
- codeframe/core/config_watcher.py +268 -0
- codeframe/core/context.py +542 -0
- codeframe/core/context_packager.py +234 -0
- codeframe/core/credentials.py +735 -0
- codeframe/core/dependency_analyzer.py +229 -0
- codeframe/core/dependency_graph.py +290 -0
- codeframe/core/diagnostic_agent.py +712 -0
- codeframe/core/diagnostics.py +616 -0
- codeframe/core/editor.py +556 -0
- codeframe/core/engine_registry.py +256 -0
- codeframe/core/engine_stats.py +231 -0
- codeframe/core/environment.py +697 -0
- codeframe/core/events.py +375 -0
- codeframe/core/executor.py +1005 -0
- codeframe/core/fix_tracker.py +480 -0
- codeframe/core/gates.py +1322 -0
- codeframe/core/git.py +477 -0
- codeframe/core/github_connect_service.py +178 -0
- codeframe/core/github_integration_config.py +118 -0
- codeframe/core/github_issues_service.py +449 -0
- codeframe/core/hooks.py +184 -0
- codeframe/core/importers/__init__.py +1 -0
- codeframe/core/importers/ralph.py +540 -0
- codeframe/core/installer.py +650 -0
- codeframe/core/models.py +1026 -0
- codeframe/core/notifications_config.py +183 -0
- codeframe/core/planner.py +437 -0
- codeframe/core/prd.py +670 -0
- codeframe/core/prd_discovery.py +1118 -0
- codeframe/core/prd_stress_test.py +499 -0
- codeframe/core/progress.py +126 -0
- codeframe/core/proof/__init__.py +34 -0
- codeframe/core/proof/capture.py +79 -0
- codeframe/core/proof/evidence.py +56 -0
- codeframe/core/proof/ledger.py +574 -0
- codeframe/core/proof/models.py +162 -0
- codeframe/core/proof/obligations.py +103 -0
- codeframe/core/proof/runner.py +233 -0
- codeframe/core/proof/scope.py +81 -0
- codeframe/core/proof/stubs.py +156 -0
- codeframe/core/quick_fixes.py +558 -0
- codeframe/core/react_agent.py +1650 -0
- codeframe/core/reconciliation.py +183 -0
- codeframe/core/replay.py +788 -0
- codeframe/core/review.py +285 -0
- codeframe/core/runtime.py +1134 -0
- codeframe/core/sandbox/__init__.py +27 -0
- codeframe/core/sandbox/context.py +98 -0
- codeframe/core/sandbox/worktree.py +20 -0
- codeframe/core/schedule.py +396 -0
- codeframe/core/stall_detector.py +71 -0
- codeframe/core/stall_monitor.py +134 -0
- codeframe/core/state_machine.py +121 -0
- codeframe/core/streaming.py +502 -0
- codeframe/core/task_tree.py +400 -0
- codeframe/core/tasks.py +1022 -0
- codeframe/core/telemetry.py +232 -0
- codeframe/core/templates.py +221 -0
- codeframe/core/tools.py +942 -0
- codeframe/core/workspace.py +887 -0
- codeframe/core/worktrees.py +276 -0
- codeframe/git/__init__.py +5 -0
- codeframe/git/github_integration.py +505 -0
- codeframe/lib/__init__.py +0 -0
- codeframe/lib/audit_logger.py +248 -0
- codeframe/lib/metrics_tracker.py +800 -0
- codeframe/lib/quality/__init__.py +7 -0
- codeframe/lib/quality/complexity_analyzer.py +316 -0
- codeframe/lib/quality/owasp_patterns.py +284 -0
- codeframe/lib/quality/security_scanner.py +250 -0
- codeframe/lib/rate_limiter.py +312 -0
- codeframe/notifications/__init__.py +0 -0
- codeframe/notifications/webhook.py +380 -0
- codeframe/planning/__init__.py +30 -0
- codeframe/planning/issue_generator.py +219 -0
- codeframe/planning/prd_template_functions.py +137 -0
- codeframe/planning/prd_templates.py +975 -0
- codeframe/planning/task_scheduler.py +511 -0
- codeframe/planning/task_templates.py +533 -0
- codeframe/platform_store/__init__.py +5 -0
- codeframe/platform_store/database.py +277 -0
- codeframe/platform_store/repositories/__init__.py +24 -0
- codeframe/platform_store/repositories/api_key_repository.py +245 -0
- codeframe/platform_store/repositories/audit_repository.py +67 -0
- codeframe/platform_store/repositories/base.py +295 -0
- codeframe/platform_store/repositories/interactive_sessions.py +165 -0
- codeframe/platform_store/repositories/token_repository.py +598 -0
- codeframe/platform_store/repositories/workspace_registry_repository.py +175 -0
- codeframe/platform_store/schema_manager.py +321 -0
- codeframe/templates/AGENTS.md.default +94 -0
- codeframe/tui/__init__.py +5 -0
- codeframe/tui/app.py +256 -0
- codeframe/tui/data_service.py +103 -0
- codeframe/ui/__init__.py +0 -0
- codeframe/ui/dependencies.py +103 -0
- codeframe/ui/models.py +999 -0
- codeframe/ui/response_models.py +201 -0
- codeframe/ui/routers/__init__.py +5 -0
- codeframe/ui/routers/_helpers.py +29 -0
- codeframe/ui/routers/batches_v2.py +315 -0
- codeframe/ui/routers/blockers_v2.py +320 -0
- codeframe/ui/routers/checkpoints_v2.py +310 -0
- codeframe/ui/routers/costs_v2.py +322 -0
- codeframe/ui/routers/diagnose_v2.py +225 -0
- codeframe/ui/routers/discovery_v2.py +417 -0
- codeframe/ui/routers/environment_v2.py +284 -0
- codeframe/ui/routers/events_v2.py +75 -0
- codeframe/ui/routers/gates_v2.py +166 -0
- codeframe/ui/routers/git_v2.py +284 -0
- codeframe/ui/routers/github_integrations_v2.py +532 -0
- codeframe/ui/routers/interactive_sessions_v2.py +238 -0
- codeframe/ui/routers/pr_v2.py +709 -0
- codeframe/ui/routers/prd_v2.py +695 -0
- codeframe/ui/routers/proof_v2.py +755 -0
- codeframe/ui/routers/review_v2.py +360 -0
- codeframe/ui/routers/schedule_v2.py +214 -0
- codeframe/ui/routers/session_chat_ws.py +354 -0
- codeframe/ui/routers/settings_v2.py +562 -0
- codeframe/ui/routers/streaming_v2.py +155 -0
- codeframe/ui/routers/tasks_v2.py +1098 -0
- codeframe/ui/routers/templates_v2.py +232 -0
- codeframe/ui/routers/terminal_ws.py +267 -0
- codeframe/ui/routers/workspace_v2.py +527 -0
- codeframe/ui/server.py +568 -0
- codeframe/ui/shared.py +241 -0
- codeframe/workspace/__init__.py +5 -0
- codeframe/workspace/manager.py +249 -0
- codeframe_ai-0.9.0.dist-info/METADATA +517 -0
- codeframe_ai-0.9.0.dist-info/RECORD +197 -0
- codeframe_ai-0.9.0.dist-info/WHEEL +5 -0
- codeframe_ai-0.9.0.dist-info/entry_points.txt +3 -0
- codeframe_ai-0.9.0.dist-info/licenses/LICENSE +661 -0
- codeframe_ai-0.9.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
"""V2 Interactive Sessions router.
|
|
2
|
+
|
|
3
|
+
Provides CRUD endpoints for persistent interactive agent sessions.
|
|
4
|
+
|
|
5
|
+
Routes:
|
|
6
|
+
POST /api/v2/sessions - Create session
|
|
7
|
+
GET /api/v2/sessions - List sessions (?workspace_path=&state=)
|
|
8
|
+
GET /api/v2/sessions/{id} - Get session
|
|
9
|
+
DELETE /api/v2/sessions/{id} - End/close session
|
|
10
|
+
POST /api/v2/sessions/{id}/messages - Add message to session
|
|
11
|
+
GET /api/v2/sessions/{id}/messages - Get message history (?limit=&offset=)
|
|
12
|
+
|
|
13
|
+
Auth note: auth is a project-wide gap tracked in #336. All other v2 routers also
|
|
14
|
+
lack auth — adding it only here would be inconsistent with the rest of the API.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import logging
|
|
18
|
+
from typing import Optional
|
|
19
|
+
|
|
20
|
+
from fastapi import APIRouter, HTTPException, Query, Request
|
|
21
|
+
from pydantic import BaseModel, field_validator
|
|
22
|
+
|
|
23
|
+
from codeframe.lib.rate_limiter import rate_limit_standard
|
|
24
|
+
|
|
25
|
+
logger = logging.getLogger(__name__)
|
|
26
|
+
|
|
27
|
+
router = APIRouter(prefix="/api/v2/sessions", tags=["sessions-v2"])
|
|
28
|
+
|
|
29
|
+
VALID_STATES = frozenset({"active", "paused", "ended"})
|
|
30
|
+
VALID_ROLES = frozenset(
|
|
31
|
+
{"user", "assistant", "tool_use", "tool_result", "thinking", "system", "error"}
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
# ============================================================================
|
|
36
|
+
# Pydantic Schemas
|
|
37
|
+
# ============================================================================
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class SessionCreate(BaseModel):
|
|
41
|
+
"""Request body for creating a new interactive agent session."""
|
|
42
|
+
|
|
43
|
+
workspace_path: str
|
|
44
|
+
task_id: Optional[str] = None
|
|
45
|
+
# agent_type is intentionally open-ended for extensibility (claude, codex, opencode, etc.)
|
|
46
|
+
agent_type: str = "claude"
|
|
47
|
+
model: Optional[str] = None
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class SessionResponse(BaseModel):
|
|
51
|
+
"""Response body for a single interactive agent session."""
|
|
52
|
+
|
|
53
|
+
id: str
|
|
54
|
+
workspace_path: str
|
|
55
|
+
task_id: Optional[str]
|
|
56
|
+
state: str
|
|
57
|
+
agent_type: str
|
|
58
|
+
model: Optional[str]
|
|
59
|
+
cost_usd: float
|
|
60
|
+
input_tokens: int
|
|
61
|
+
output_tokens: int
|
|
62
|
+
created_at: str
|
|
63
|
+
updated_at: str
|
|
64
|
+
ended_at: Optional[str]
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class MessageCreate(BaseModel):
|
|
68
|
+
"""Request body for adding a message to a session."""
|
|
69
|
+
|
|
70
|
+
role: str
|
|
71
|
+
content: str
|
|
72
|
+
metadata: Optional[dict] = None
|
|
73
|
+
|
|
74
|
+
@field_validator("role")
|
|
75
|
+
@classmethod
|
|
76
|
+
def validate_role(cls, v: str) -> str:
|
|
77
|
+
"""Validate that role is one of the allowed values."""
|
|
78
|
+
if v not in VALID_ROLES:
|
|
79
|
+
raise ValueError(f"role must be one of {sorted(VALID_ROLES)}")
|
|
80
|
+
return v
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class MessageResponse(BaseModel):
|
|
84
|
+
"""Response body for a single session message."""
|
|
85
|
+
|
|
86
|
+
id: str
|
|
87
|
+
session_id: str
|
|
88
|
+
role: str
|
|
89
|
+
content: str
|
|
90
|
+
metadata: Optional[dict]
|
|
91
|
+
created_at: str
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
# ============================================================================
|
|
95
|
+
# Helpers
|
|
96
|
+
# ============================================================================
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _get_repo(request: Request):
|
|
100
|
+
"""Get the InteractiveSessionRepository from app state.
|
|
101
|
+
|
|
102
|
+
Raises 503 if app.state.db is not available (server lifespan should set it).
|
|
103
|
+
For backward compat with tests that set app.state.db directly.
|
|
104
|
+
"""
|
|
105
|
+
db = getattr(request.app.state, "db", None)
|
|
106
|
+
if db is None:
|
|
107
|
+
raise HTTPException(
|
|
108
|
+
status_code=503,
|
|
109
|
+
detail="Database not available. Ensure the server is fully initialized.",
|
|
110
|
+
)
|
|
111
|
+
return db.interactive_sessions
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def _session_to_response(row: dict) -> SessionResponse:
|
|
115
|
+
"""Convert a DB row dict to a SessionResponse."""
|
|
116
|
+
return SessionResponse(
|
|
117
|
+
id=row["id"],
|
|
118
|
+
workspace_path=row["workspace_path"],
|
|
119
|
+
task_id=row.get("task_id"),
|
|
120
|
+
state=row["state"],
|
|
121
|
+
agent_type=row["agent_type"],
|
|
122
|
+
model=row.get("model"),
|
|
123
|
+
cost_usd=row.get("cost_usd", 0.0),
|
|
124
|
+
input_tokens=row.get("input_tokens", 0),
|
|
125
|
+
output_tokens=row.get("output_tokens", 0),
|
|
126
|
+
created_at=row["created_at"],
|
|
127
|
+
updated_at=row["updated_at"],
|
|
128
|
+
ended_at=row.get("ended_at"),
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
# ============================================================================
|
|
133
|
+
# Endpoints
|
|
134
|
+
# ============================================================================
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
@router.post("", status_code=201, response_model=SessionResponse)
|
|
138
|
+
@rate_limit_standard()
|
|
139
|
+
def create_session(body: SessionCreate, request: Request):
|
|
140
|
+
"""Create a new interactive agent session."""
|
|
141
|
+
repo = _get_repo(request)
|
|
142
|
+
session = repo.create(
|
|
143
|
+
workspace_path=body.workspace_path,
|
|
144
|
+
task_id=body.task_id,
|
|
145
|
+
agent_type=body.agent_type,
|
|
146
|
+
model=body.model,
|
|
147
|
+
)
|
|
148
|
+
return _session_to_response(session)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
@router.get("", response_model=list[SessionResponse])
|
|
152
|
+
@rate_limit_standard()
|
|
153
|
+
def list_sessions(
|
|
154
|
+
request: Request,
|
|
155
|
+
workspace_path: Optional[str] = Query(None),
|
|
156
|
+
state: Optional[str] = Query(None),
|
|
157
|
+
limit: int = Query(50, ge=1, le=200),
|
|
158
|
+
):
|
|
159
|
+
"""List sessions with optional filters by workspace_path and state."""
|
|
160
|
+
if state is not None and state not in VALID_STATES:
|
|
161
|
+
raise HTTPException(
|
|
162
|
+
status_code=422,
|
|
163
|
+
detail=f"Invalid state '{state}'. Must be one of {sorted(VALID_STATES)}",
|
|
164
|
+
)
|
|
165
|
+
repo = _get_repo(request)
|
|
166
|
+
sessions = repo.list(workspace_path=workspace_path, state=state, limit=limit)
|
|
167
|
+
return [_session_to_response(s) for s in sessions]
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
@router.get("/{session_id}", response_model=SessionResponse)
|
|
171
|
+
@rate_limit_standard()
|
|
172
|
+
def get_session(session_id: str, request: Request):
|
|
173
|
+
"""Get a session by ID."""
|
|
174
|
+
repo = _get_repo(request)
|
|
175
|
+
session = repo.get(session_id)
|
|
176
|
+
if session is None:
|
|
177
|
+
raise HTTPException(status_code=404, detail="Session not found")
|
|
178
|
+
return _session_to_response(session)
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
@router.delete("/{session_id}", response_model=SessionResponse)
|
|
182
|
+
@rate_limit_standard()
|
|
183
|
+
def end_session(session_id: str, request: Request):
|
|
184
|
+
"""End a session (sets state to 'ended' and records ended_at)."""
|
|
185
|
+
repo = _get_repo(request)
|
|
186
|
+
updated = repo.end(session_id)
|
|
187
|
+
if updated is None:
|
|
188
|
+
raise HTTPException(status_code=404, detail="Session not found")
|
|
189
|
+
return _session_to_response(updated)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
@router.post("/{session_id}/messages", status_code=201, response_model=MessageResponse)
|
|
193
|
+
@rate_limit_standard()
|
|
194
|
+
def add_message(session_id: str, body: MessageCreate, request: Request):
|
|
195
|
+
"""Add a message to a session's history."""
|
|
196
|
+
repo = _get_repo(request)
|
|
197
|
+
if repo.get(session_id) is None:
|
|
198
|
+
raise HTTPException(status_code=404, detail="Session not found")
|
|
199
|
+
message = repo.add_message(
|
|
200
|
+
session_id=session_id,
|
|
201
|
+
role=body.role,
|
|
202
|
+
content=body.content,
|
|
203
|
+
metadata=body.metadata,
|
|
204
|
+
)
|
|
205
|
+
return MessageResponse(
|
|
206
|
+
id=message["id"],
|
|
207
|
+
session_id=message["session_id"],
|
|
208
|
+
role=message["role"],
|
|
209
|
+
content=message["content"],
|
|
210
|
+
metadata=message.get("metadata"),
|
|
211
|
+
created_at=message["created_at"],
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
@router.get("/{session_id}/messages", response_model=list[MessageResponse])
|
|
216
|
+
@rate_limit_standard()
|
|
217
|
+
def get_messages(
|
|
218
|
+
session_id: str,
|
|
219
|
+
request: Request,
|
|
220
|
+
limit: int = Query(100, ge=1, le=500),
|
|
221
|
+
offset: int = Query(0, ge=0),
|
|
222
|
+
):
|
|
223
|
+
"""Get paginated message history for a session."""
|
|
224
|
+
repo = _get_repo(request)
|
|
225
|
+
if repo.get(session_id) is None:
|
|
226
|
+
raise HTTPException(status_code=404, detail="Session not found")
|
|
227
|
+
messages = repo.get_messages(session_id, limit=limit, offset=offset)
|
|
228
|
+
return [
|
|
229
|
+
MessageResponse(
|
|
230
|
+
id=m["id"],
|
|
231
|
+
session_id=m["session_id"],
|
|
232
|
+
role=m["role"],
|
|
233
|
+
content=m["content"],
|
|
234
|
+
metadata=m.get("metadata"),
|
|
235
|
+
created_at=m["created_at"],
|
|
236
|
+
)
|
|
237
|
+
for m in messages
|
|
238
|
+
]
|