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,269 @@
|
|
|
1
|
+
"""Verification gate wrapper for agent adapters.
|
|
2
|
+
|
|
3
|
+
Wraps any AgentAdapter with post-execution verification gates, quick fixes,
|
|
4
|
+
fix attempt tracking, and escalation to blockers. This gives all execution
|
|
5
|
+
engines (built-in and external) the same self-correction capabilities that
|
|
6
|
+
ReactAgent has internally.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import logging
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from typing import Callable, Optional
|
|
14
|
+
|
|
15
|
+
from codeframe.core.adapters.agent_adapter import AgentAdapter, AgentEvent, AgentResult
|
|
16
|
+
from codeframe.core import blockers
|
|
17
|
+
from codeframe.core.fix_tracker import (
|
|
18
|
+
EscalationDecision,
|
|
19
|
+
FixAttemptTracker,
|
|
20
|
+
FixOutcome,
|
|
21
|
+
build_escalation_question,
|
|
22
|
+
)
|
|
23
|
+
from codeframe.core.gates import GateStatus
|
|
24
|
+
from codeframe.core.gates import run as run_gates
|
|
25
|
+
from codeframe.core.quick_fixes import apply_quick_fix, find_quick_fix
|
|
26
|
+
from codeframe.core.workspace import Workspace
|
|
27
|
+
|
|
28
|
+
logger = logging.getLogger(__name__)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class VerificationWrapper:
|
|
32
|
+
"""Wraps any AgentAdapter with post-execution verification gates.
|
|
33
|
+
|
|
34
|
+
After the inner adapter completes, runs verification gates (pytest, ruff, etc.).
|
|
35
|
+
If gates fail:
|
|
36
|
+
1. Try a pattern-based quick fix (no LLM needed)
|
|
37
|
+
2. If quick fix applied, re-run gates immediately
|
|
38
|
+
3. If no quick fix, re-invoke adapter with error context for self-correction
|
|
39
|
+
4. Track all fix attempts to detect loops
|
|
40
|
+
5. Escalate to blocker when fix tracker recommends it or retries exhausted
|
|
41
|
+
|
|
42
|
+
This is the same self-correction pattern that ReactAgent._run_final_verification()
|
|
43
|
+
uses, but decoupled from any specific engine so it wraps any adapter.
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
def __init__(
|
|
47
|
+
self,
|
|
48
|
+
inner: AgentAdapter,
|
|
49
|
+
workspace: Workspace,
|
|
50
|
+
max_correction_rounds: int = 5,
|
|
51
|
+
gate_names: Optional[list[str]] = None,
|
|
52
|
+
verbose: bool = False,
|
|
53
|
+
) -> None:
|
|
54
|
+
self._inner = inner
|
|
55
|
+
self._workspace = workspace
|
|
56
|
+
self._max_correction_rounds = max_correction_rounds
|
|
57
|
+
self._gate_names = gate_names # None = use default gates
|
|
58
|
+
self._verbose = verbose
|
|
59
|
+
self.fix_tracker = FixAttemptTracker()
|
|
60
|
+
|
|
61
|
+
@property
|
|
62
|
+
def name(self) -> str:
|
|
63
|
+
return f"verified-{self._inner.name}"
|
|
64
|
+
|
|
65
|
+
def run(
|
|
66
|
+
self,
|
|
67
|
+
task_id: str,
|
|
68
|
+
prompt: str,
|
|
69
|
+
workspace_path: Path,
|
|
70
|
+
on_event: Callable[[AgentEvent], None] | None = None,
|
|
71
|
+
) -> AgentResult:
|
|
72
|
+
"""Run the inner adapter, then verify with gates. Self-correct on failure."""
|
|
73
|
+
|
|
74
|
+
# Initial run
|
|
75
|
+
result = self._inner.run(task_id, prompt, workspace_path, on_event)
|
|
76
|
+
|
|
77
|
+
# Only verify if the adapter reported success
|
|
78
|
+
if result.status != "completed":
|
|
79
|
+
return result
|
|
80
|
+
|
|
81
|
+
# Run verification gates with self-correction loop
|
|
82
|
+
for round_num in range(self._max_correction_rounds):
|
|
83
|
+
if on_event:
|
|
84
|
+
on_event(AgentEvent(
|
|
85
|
+
type="verification",
|
|
86
|
+
data={
|
|
87
|
+
"round": round_num + 1,
|
|
88
|
+
"max_rounds": self._max_correction_rounds,
|
|
89
|
+
},
|
|
90
|
+
))
|
|
91
|
+
|
|
92
|
+
gate_result = run_gates(
|
|
93
|
+
self._workspace,
|
|
94
|
+
gates=self._gate_names,
|
|
95
|
+
verbose=self._verbose,
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
if gate_result.passed:
|
|
99
|
+
if on_event:
|
|
100
|
+
on_event(AgentEvent(type="verification_passed", data={}))
|
|
101
|
+
return result
|
|
102
|
+
|
|
103
|
+
# Gates failed — get structured error summary
|
|
104
|
+
error_summary = (
|
|
105
|
+
gate_result.get_error_summary() or
|
|
106
|
+
self._format_gate_errors(gate_result)
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
if on_event:
|
|
110
|
+
on_event(AgentEvent(
|
|
111
|
+
type="verification_failed",
|
|
112
|
+
data={
|
|
113
|
+
"round": round_num + 1,
|
|
114
|
+
"checks": len(gate_result.checks),
|
|
115
|
+
},
|
|
116
|
+
))
|
|
117
|
+
|
|
118
|
+
# 1. Record the attempt (outcome deferred until we know if fix works)
|
|
119
|
+
self.fix_tracker.record_attempt(error_summary, "verification_gate")
|
|
120
|
+
|
|
121
|
+
# 2. Check escalation based on prior history
|
|
122
|
+
escalation = self.fix_tracker.should_escalate(error_summary)
|
|
123
|
+
if escalation.should_escalate:
|
|
124
|
+
self.fix_tracker.record_outcome(
|
|
125
|
+
error_summary, "verification_gate", FixOutcome.FAILED,
|
|
126
|
+
)
|
|
127
|
+
return self._create_escalation_blocker(
|
|
128
|
+
task_id, error_summary, escalation,
|
|
129
|
+
last_output=result.output,
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
# 3. Try quick fix first (no adapter re-invocation needed)
|
|
133
|
+
if self._try_quick_fix(error_summary):
|
|
134
|
+
self.fix_tracker.record_outcome(
|
|
135
|
+
error_summary, "verification_gate", FixOutcome.SUCCESS,
|
|
136
|
+
)
|
|
137
|
+
self._verbose_print(
|
|
138
|
+
f"[VerificationWrapper] Quick fix applied (round {round_num + 1})"
|
|
139
|
+
)
|
|
140
|
+
continue # Re-run gates without re-invoking adapter
|
|
141
|
+
|
|
142
|
+
# 4. No quick fix — record failure and re-invoke adapter with error context
|
|
143
|
+
self.fix_tracker.record_outcome(
|
|
144
|
+
error_summary, "verification_gate", FixOutcome.FAILED,
|
|
145
|
+
)
|
|
146
|
+
formatted_errors = self._format_gate_errors(gate_result)
|
|
147
|
+
correction_prompt = (
|
|
148
|
+
f"{prompt}\n\n"
|
|
149
|
+
f"## Verification Gate Failures (Correction Round {round_num + 1})\n\n"
|
|
150
|
+
f"Your previous changes failed the following verification gates. "
|
|
151
|
+
f"Fix these issues:\n\n{formatted_errors}"
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
result = self._inner.run(
|
|
155
|
+
task_id, correction_prompt, workspace_path, on_event,
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
if result.status != "completed":
|
|
159
|
+
return result
|
|
160
|
+
|
|
161
|
+
# Final gate check after all correction rounds
|
|
162
|
+
gate_result = run_gates(
|
|
163
|
+
self._workspace,
|
|
164
|
+
gates=self._gate_names,
|
|
165
|
+
verbose=self._verbose,
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
if gate_result.passed:
|
|
169
|
+
return result
|
|
170
|
+
|
|
171
|
+
# All rounds exhausted — create blocker
|
|
172
|
+
error_summary = self._format_gate_errors(gate_result)
|
|
173
|
+
return self._create_exhaustion_blocker(
|
|
174
|
+
task_id, error_summary, last_output=result.output,
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
def _try_quick_fix(self, error_summary: str) -> bool:
|
|
178
|
+
"""Attempt a pattern-based quick fix for the gate error.
|
|
179
|
+
|
|
180
|
+
Returns True if a fix was successfully applied.
|
|
181
|
+
"""
|
|
182
|
+
fix = find_quick_fix(error_summary, repo_path=self._workspace.repo_path)
|
|
183
|
+
if fix is None:
|
|
184
|
+
return False
|
|
185
|
+
|
|
186
|
+
success, msg = apply_quick_fix(fix, self._workspace.repo_path)
|
|
187
|
+
if success:
|
|
188
|
+
self._verbose_print(f"[VerificationWrapper] Quick fix: {msg}")
|
|
189
|
+
return success
|
|
190
|
+
|
|
191
|
+
def _create_escalation_blocker(
|
|
192
|
+
self,
|
|
193
|
+
task_id: str,
|
|
194
|
+
error: str,
|
|
195
|
+
escalation: EscalationDecision,
|
|
196
|
+
last_output: str = "",
|
|
197
|
+
) -> AgentResult:
|
|
198
|
+
"""Create a blocker when fix tracker recommends escalation."""
|
|
199
|
+
question = build_escalation_question(
|
|
200
|
+
error, escalation.reason, self.fix_tracker,
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
try:
|
|
204
|
+
blockers.create(
|
|
205
|
+
workspace=self._workspace,
|
|
206
|
+
question=question,
|
|
207
|
+
task_id=task_id,
|
|
208
|
+
created_by="agent",
|
|
209
|
+
)
|
|
210
|
+
except Exception:
|
|
211
|
+
logger.warning("Failed to create escalation blocker", exc_info=True)
|
|
212
|
+
|
|
213
|
+
return AgentResult(
|
|
214
|
+
status="blocked",
|
|
215
|
+
output=last_output,
|
|
216
|
+
blocker_question=question,
|
|
217
|
+
error=f"Escalated to blocker: {escalation.reason}",
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
def _create_exhaustion_blocker(
|
|
221
|
+
self,
|
|
222
|
+
task_id: str,
|
|
223
|
+
error_summary: str,
|
|
224
|
+
last_output: str = "",
|
|
225
|
+
) -> AgentResult:
|
|
226
|
+
"""Create a blocker when all correction rounds are exhausted."""
|
|
227
|
+
question = (
|
|
228
|
+
f"Verification gates still failing after "
|
|
229
|
+
f"{self._max_correction_rounds} correction rounds.\n\n"
|
|
230
|
+
f"Errors:\n{error_summary[:500]}\n\n"
|
|
231
|
+
f"Please investigate and provide guidance."
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
try:
|
|
235
|
+
blockers.create(
|
|
236
|
+
workspace=self._workspace,
|
|
237
|
+
question=question,
|
|
238
|
+
task_id=task_id,
|
|
239
|
+
created_by="agent",
|
|
240
|
+
)
|
|
241
|
+
except Exception:
|
|
242
|
+
logger.warning("Failed to create exhaustion blocker", exc_info=True)
|
|
243
|
+
|
|
244
|
+
return AgentResult(
|
|
245
|
+
status="blocked",
|
|
246
|
+
output=last_output,
|
|
247
|
+
blocker_question=question,
|
|
248
|
+
error=(
|
|
249
|
+
f"Verification gates still failing after "
|
|
250
|
+
f"{self._max_correction_rounds} correction rounds:\n{error_summary}"
|
|
251
|
+
),
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
def _verbose_print(self, msg: str) -> None:
|
|
255
|
+
if self._verbose:
|
|
256
|
+
print(msg)
|
|
257
|
+
|
|
258
|
+
@staticmethod
|
|
259
|
+
def _format_gate_errors(gate_result) -> str:
|
|
260
|
+
"""Format gate check failures into a readable summary."""
|
|
261
|
+
|
|
262
|
+
lines: list[str] = []
|
|
263
|
+
for check in gate_result.checks:
|
|
264
|
+
if check.status == GateStatus.FAILED:
|
|
265
|
+
lines.append(f"### {check.name}: FAILED")
|
|
266
|
+
if check.output:
|
|
267
|
+
lines.append(f"```\n{check.output[:2000]}\n```")
|
|
268
|
+
lines.append("")
|
|
269
|
+
return "\n".join(lines) if lines else "Gate checks failed (no details available)"
|