alpiecode 10.5.2__tar.gz → 10.5.3__tar.gz
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.
- {alpiecode-10.5.2 → alpiecode-10.5.3}/PKG-INFO +1 -1
- {alpiecode-10.5.2 → alpiecode-10.5.3}/pyproject.toml +1 -1
- {alpiecode-10.5.2 → alpiecode-10.5.3}/src/alpiecode.egg-info/PKG-INFO +1 -1
- {alpiecode-10.5.2 → alpiecode-10.5.3}/src/alpiecode.egg-info/SOURCES.txt +3 -1
- {alpiecode-10.5.2 → alpiecode-10.5.3}/src/codeagent/__init__.py +1 -1
- {alpiecode-10.5.2 → alpiecode-10.5.3}/src/codeagent/orchestrator.py +55 -87
- {alpiecode-10.5.2 → alpiecode-10.5.3}/src/codeagent/progress.py +27 -2
- {alpiecode-10.5.2 → alpiecode-10.5.3}/src/codeagent/system1.py +115 -14
- alpiecode-10.5.3/src/codeagent/verifier.py +498 -0
- {alpiecode-10.5.2 → alpiecode-10.5.3}/test/test_task_assessment.py +101 -0
- alpiecode-10.5.3/test/test_verifier.py +197 -0
- {alpiecode-10.5.2 → alpiecode-10.5.3}/README.md +0 -0
- {alpiecode-10.5.2 → alpiecode-10.5.3}/setup.cfg +0 -0
- {alpiecode-10.5.2 → alpiecode-10.5.3}/src/alpiecode/__init__.py +0 -0
- {alpiecode-10.5.2 → alpiecode-10.5.3}/src/alpiecode.egg-info/dependency_links.txt +0 -0
- {alpiecode-10.5.2 → alpiecode-10.5.3}/src/alpiecode.egg-info/entry_points.txt +0 -0
- {alpiecode-10.5.2 → alpiecode-10.5.3}/src/alpiecode.egg-info/requires.txt +0 -0
- {alpiecode-10.5.2 → alpiecode-10.5.3}/src/alpiecode.egg-info/top_level.txt +0 -0
- {alpiecode-10.5.2 → alpiecode-10.5.3}/src/codeagent/agent.py +0 -0
- {alpiecode-10.5.2 → alpiecode-10.5.3}/src/codeagent/assessment.py +0 -0
- {alpiecode-10.5.2 → alpiecode-10.5.3}/src/codeagent/backends/__init__.py +0 -0
- {alpiecode-10.5.2 → alpiecode-10.5.3}/src/codeagent/backends/base.py +0 -0
- {alpiecode-10.5.2 → alpiecode-10.5.3}/src/codeagent/backends/local_backend.py +0 -0
- {alpiecode-10.5.2 → alpiecode-10.5.3}/src/codeagent/backends/openai_backend.py +0 -0
- {alpiecode-10.5.2 → alpiecode-10.5.3}/src/codeagent/cache.py +0 -0
- {alpiecode-10.5.2 → alpiecode-10.5.3}/src/codeagent/cli.py +0 -0
- {alpiecode-10.5.2 → alpiecode-10.5.3}/src/codeagent/client.py +0 -0
- {alpiecode-10.5.2 → alpiecode-10.5.3}/src/codeagent/compaction.py +0 -0
- {alpiecode-10.5.2 → alpiecode-10.5.3}/src/codeagent/config.py +0 -0
- {alpiecode-10.5.2 → alpiecode-10.5.3}/src/codeagent/context.py +0 -0
- {alpiecode-10.5.2 → alpiecode-10.5.3}/src/codeagent/discovery.py +0 -0
- {alpiecode-10.5.2 → alpiecode-10.5.3}/src/codeagent/doctor.py +0 -0
- {alpiecode-10.5.2 → alpiecode-10.5.3}/src/codeagent/dream_rsi.py +0 -0
- {alpiecode-10.5.2 → alpiecode-10.5.3}/src/codeagent/executor.py +0 -0
- {alpiecode-10.5.2 → alpiecode-10.5.3}/src/codeagent/extension/alpiecode.vsix +0 -0
- {alpiecode-10.5.2 → alpiecode-10.5.3}/src/codeagent/git_ops.py +0 -0
- {alpiecode-10.5.2 → alpiecode-10.5.3}/src/codeagent/github.py +0 -0
- {alpiecode-10.5.2 → alpiecode-10.5.3}/src/codeagent/guardian.py +0 -0
- {alpiecode-10.5.2 → alpiecode-10.5.3}/src/codeagent/guardrails.py +0 -0
- {alpiecode-10.5.2 → alpiecode-10.5.3}/src/codeagent/ipython_ext.py +0 -0
- {alpiecode-10.5.2 → alpiecode-10.5.3}/src/codeagent/local_model.py +0 -0
- {alpiecode-10.5.2 → alpiecode-10.5.3}/src/codeagent/media.py +0 -0
- {alpiecode-10.5.2 → alpiecode-10.5.3}/src/codeagent/memory.py +0 -0
- {alpiecode-10.5.2 → alpiecode-10.5.3}/src/codeagent/metrics.py +0 -0
- {alpiecode-10.5.2 → alpiecode-10.5.3}/src/codeagent/prompt.py +0 -0
- {alpiecode-10.5.2 → alpiecode-10.5.3}/src/codeagent/rephraser.py +0 -0
- {alpiecode-10.5.2 → alpiecode-10.5.3}/src/codeagent/server.py +0 -0
- {alpiecode-10.5.2 → alpiecode-10.5.3}/src/codeagent/session.py +0 -0
- {alpiecode-10.5.2 → alpiecode-10.5.3}/src/codeagent/tools.py +0 -0
- {alpiecode-10.5.2 → alpiecode-10.5.3}/src/codeagent/updater.py +0 -0
- {alpiecode-10.5.2 → alpiecode-10.5.3}/src/codeagent/vscode_installer.py +0 -0
- {alpiecode-10.5.2 → alpiecode-10.5.3}/test/test_context_and_trajectory.py +0 -0
- {alpiecode-10.5.2 → alpiecode-10.5.3}/test/test_dream_rsi.py +0 -0
|
@@ -38,6 +38,7 @@ src/codeagent/session.py
|
|
|
38
38
|
src/codeagent/system1.py
|
|
39
39
|
src/codeagent/tools.py
|
|
40
40
|
src/codeagent/updater.py
|
|
41
|
+
src/codeagent/verifier.py
|
|
41
42
|
src/codeagent/vscode_installer.py
|
|
42
43
|
src/codeagent/backends/__init__.py
|
|
43
44
|
src/codeagent/backends/base.py
|
|
@@ -46,4 +47,5 @@ src/codeagent/backends/openai_backend.py
|
|
|
46
47
|
src/codeagent/extension/alpiecode.vsix
|
|
47
48
|
test/test_context_and_trajectory.py
|
|
48
49
|
test/test_dream_rsi.py
|
|
49
|
-
test/test_task_assessment.py
|
|
50
|
+
test/test_task_assessment.py
|
|
51
|
+
test/test_verifier.py
|
|
@@ -17,7 +17,7 @@ Supports:
|
|
|
17
17
|
from pathlib import Path
|
|
18
18
|
from typing import Optional, Any, Dict
|
|
19
19
|
|
|
20
|
-
__version__ = "10.5.
|
|
20
|
+
__version__ = "10.5.3"
|
|
21
21
|
|
|
22
22
|
def run(task: str, workdir: str = ".", reasoning_level: str = "thinking", **kwargs):
|
|
23
23
|
"""Run an autonomous coding task programmatically."""
|
|
@@ -334,7 +334,11 @@ class AgentOrchestrator:
|
|
|
334
334
|
base_enable_thinking = task_context.enable_thinking
|
|
335
335
|
|
|
336
336
|
task_metrics.record_context_prep((time.time() - ctx_prep_start) * 1000.0)
|
|
337
|
-
#
|
|
337
|
+
# Extract explicitly named deliverable files from task prompt (e.g. auth.py, test_auth.py)
|
|
338
|
+
named_deliverables = list(dict.fromkeys(
|
|
339
|
+
re.findall(r"\b([a-zA-Z0-9_\-]+\.(?:py|js|ts|html|css|json|toml|sh))\b", task)
|
|
340
|
+
))
|
|
341
|
+
|
|
338
342
|
progress_monitor = ProgressMonitor()
|
|
339
343
|
turn = 0
|
|
340
344
|
prior_turn_had_error = False
|
|
@@ -368,6 +372,9 @@ class AgentOrchestrator:
|
|
|
368
372
|
# ── Stall Detection & Corrective Intervention ──
|
|
369
373
|
if progress_monitor.is_stalled(threshold=3):
|
|
370
374
|
advice = progress_monitor.get_stall_advice()
|
|
375
|
+
uncreated = [f for f in named_deliverables if f not in session_touched_files]
|
|
376
|
+
if uncreated:
|
|
377
|
+
advice += f"\nNote: The following files requested in the task have not been created yet: {', '.join(uncreated)}. Create them now using write_file."
|
|
371
378
|
session.context.add_user_message(advice)
|
|
372
379
|
yield AgentEvent("stall_intervention", {
|
|
373
380
|
"turn": turn,
|
|
@@ -675,97 +682,58 @@ class AgentOrchestrator:
|
|
|
675
682
|
verify_attempts = getattr(session, "_auto_verify_attempts", 0)
|
|
676
683
|
|
|
677
684
|
if has_code_files and not auto_verify_passed and verify_attempts < 2:
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
685
|
+
from .verifier import IntelligentVerificationEngine, format_replan_directive
|
|
686
|
+
v_engine = IntelligentVerificationEngine(session.workdir)
|
|
687
|
+
|
|
688
|
+
events_queue = []
|
|
689
|
+
v_res = v_engine.run_full_verification(
|
|
690
|
+
task=task,
|
|
691
|
+
session_touched_files=session_touched_files,
|
|
692
|
+
task_context=task_context,
|
|
693
|
+
run_bash_fn=session.executor.dispatch.get("bash", lambda a: ""),
|
|
694
|
+
is_offline=is_offline,
|
|
695
|
+
successful_commands=getattr(session.executor, "successful_bash_commands", None),
|
|
696
|
+
events_queue=events_queue,
|
|
697
|
+
)
|
|
698
|
+
|
|
699
|
+
for ev_name, ev_data in events_queue:
|
|
700
|
+
yield AgentEvent(ev_name, ev_data)
|
|
701
|
+
|
|
702
|
+
if not v_res.passed:
|
|
691
703
|
session._auto_verify_attempts = verify_attempts + 1
|
|
692
|
-
|
|
693
|
-
session.context.add_user_message(
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
"
|
|
698
|
-
"
|
|
699
|
-
"
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
704
|
+
replan_prompt = format_replan_directive(v_res, is_offline=is_offline)
|
|
705
|
+
session.context.add_user_message(replan_prompt)
|
|
706
|
+
|
|
707
|
+
v_type = "syntax" if v_res.tier == "syntax" else ("tests" if "test" in v_res.tier else v_res.tier)
|
|
708
|
+
v_event_data = {
|
|
709
|
+
"type": v_type,
|
|
710
|
+
"tier": v_res.tier,
|
|
711
|
+
"error_type": v_res.error_type,
|
|
712
|
+
"message": v_res.message,
|
|
713
|
+
"failing_file": v_res.failing_file,
|
|
714
|
+
}
|
|
715
|
+
if v_res.errors:
|
|
716
|
+
v_event_data["errors"] = v_res.errors
|
|
717
|
+
if v_res.command:
|
|
718
|
+
v_event_data["command"] = v_res.command
|
|
719
|
+
v_event_data["exit_code"] = v_res.exit_code
|
|
720
|
+
yield AgentEvent("verification_failed", v_event_data)
|
|
721
|
+
|
|
722
|
+
replan_trigger = "syntax_errors" if v_res.tier == "syntax" else ("test_failure" if "test" in v_res.tier else f"{v_res.tier}_failure")
|
|
723
|
+
replan_data = {
|
|
706
724
|
"turn": turn,
|
|
707
725
|
"strategy": strategy,
|
|
708
|
-
"trigger":
|
|
709
|
-
"details":
|
|
710
|
-
}
|
|
726
|
+
"trigger": replan_trigger,
|
|
727
|
+
"details": v_res.errors or v_res.message,
|
|
728
|
+
}
|
|
729
|
+
if v_res.command:
|
|
730
|
+
replan_data["command"] = v_res.command
|
|
731
|
+
replan_data["exit_code"] = v_res.exit_code
|
|
732
|
+
yield AgentEvent("replan", replan_data)
|
|
733
|
+
|
|
711
734
|
prior_turn_had_error = True
|
|
712
735
|
continue
|
|
713
736
|
|
|
714
|
-
# 2. Automated test verification if project has tests
|
|
715
|
-
if task_context.has_tests and not getattr(session, "_has_run_tests_in_session", False):
|
|
716
|
-
session._auto_verify_attempts = verify_attempts + 1
|
|
717
|
-
session._has_run_tests_in_session = True
|
|
718
|
-
test_cmd = "python3 -m pytest -q --tb=short" if task_context.project_type == "python" else "npm test" if task_context.project_type == "node" else None
|
|
719
|
-
if test_cmd:
|
|
720
|
-
yield AgentEvent("status", {
|
|
721
|
-
"phase": "verifying",
|
|
722
|
-
"message": f"Auto-running verification test suite ({test_cmd})...",
|
|
723
|
-
})
|
|
724
|
-
t_res = session.executor.dispatch.get("bash", lambda a: "")({"command": test_cmd})
|
|
725
|
-
t_exit = 0
|
|
726
|
-
try:
|
|
727
|
-
import json as _json
|
|
728
|
-
json_str = t_res
|
|
729
|
-
if "{" in json_str:
|
|
730
|
-
json_str = json_str[json_str.find("{"):]
|
|
731
|
-
t_data = _json.loads(json_str)
|
|
732
|
-
t_exit = t_data.get("exit_code", 0)
|
|
733
|
-
t_out = t_data.get("stdout", "")
|
|
734
|
-
if t_data.get("stderr"):
|
|
735
|
-
t_out += "\n" + t_data.get("stderr")
|
|
736
|
-
except Exception:
|
|
737
|
-
t_out = str(t_res)
|
|
738
|
-
if "exit_code=" in t_res:
|
|
739
|
-
import re as _re
|
|
740
|
-
m = _re.search(r"exit_code=(\d+)", t_res)
|
|
741
|
-
if m:
|
|
742
|
-
t_exit = int(m.group(1))
|
|
743
|
-
|
|
744
|
-
if t_exit != 0:
|
|
745
|
-
session.context.add_user_message(
|
|
746
|
-
f"[VERIFICATION FAILED -> RE-PLAN & REASON]\nTest suite failed (exit_code={t_exit}) after your changes:\n"
|
|
747
|
-
f"{t_out[:1200]}\n\n"
|
|
748
|
-
"Reasoning Path Directive:\n"
|
|
749
|
-
"1. Diagnose the root cause of the test failure.\n"
|
|
750
|
-
"2. Re-plan your implementation or fix the broken assertion/logic.\n"
|
|
751
|
-
"3. Apply targeted fixes with edit_file.\n"
|
|
752
|
-
"4. Verify the test suite passes before marking completion."
|
|
753
|
-
)
|
|
754
|
-
yield AgentEvent("verification_failed", {
|
|
755
|
-
"type": "tests",
|
|
756
|
-
"command": test_cmd,
|
|
757
|
-
"exit_code": t_exit,
|
|
758
|
-
})
|
|
759
|
-
yield AgentEvent("replan", {
|
|
760
|
-
"turn": turn,
|
|
761
|
-
"strategy": strategy,
|
|
762
|
-
"trigger": "test_failure",
|
|
763
|
-
"command": test_cmd,
|
|
764
|
-
"exit_code": t_exit,
|
|
765
|
-
})
|
|
766
|
-
prior_turn_had_error = True
|
|
767
|
-
continue
|
|
768
|
-
|
|
769
737
|
session._auto_verify_passed = True
|
|
770
738
|
|
|
771
739
|
# Cache if single-turn
|
|
@@ -863,7 +831,7 @@ class AgentOrchestrator:
|
|
|
863
831
|
turn=turn,
|
|
864
832
|
tool_calls=[{'name': tc.name, 'arguments': tc.arguments} for tc in tool_calls],
|
|
865
833
|
tool_results=[{'name': res.name, 'content': res.content[:200], 'duration_ms': res.duration_ms} for res in results],
|
|
866
|
-
error_types=[system1.triage_error(res.content)[0].value for res in results if system1.triage_error(res.content)[0].value != 'none'],
|
|
834
|
+
error_types=[system1.triage_error(res.content, tool_name=res.name)[0].value for res in results if system1.triage_error(res.content, tool_name=res.name)[0].value != 'none'],
|
|
867
835
|
had_error=prior_turn_had_error,
|
|
868
836
|
duration_ms=(time.time() - llm_call_start) * 1000.0,
|
|
869
837
|
tokens_estimated=max_tokens,
|
|
@@ -126,10 +126,25 @@ class ProgressMonitor:
|
|
|
126
126
|
snap.errors.append(content[:100])
|
|
127
127
|
|
|
128
128
|
# Determine progress
|
|
129
|
+
# Read-only bash inspection commands (cat, head, ls, wc, etc.) do NOT count as progress on their own
|
|
130
|
+
import re
|
|
131
|
+
read_only_verbs = {"cat", "head", "tail", "ls", "file", "wc", "stat", "echo", "pwd", "which", "type"}
|
|
132
|
+
has_productive_bash = False
|
|
133
|
+
for tc in tool_calls:
|
|
134
|
+
name = tc.get("name", "") if isinstance(tc, dict) else getattr(tc, "name", "")
|
|
135
|
+
if name == "bash":
|
|
136
|
+
cmd = (tc.get("arguments", {}) if isinstance(tc, dict) else getattr(tc, "arguments", {})).get("command", "")
|
|
137
|
+
# Strip shell redirections like 2>&1, >/dev/null, etc. before tokenizing
|
|
138
|
+
cleaned_cmd = re.sub(r"\d*>\s*&?\d*|\d*<\s*&?\d*", "", cmd)
|
|
139
|
+
parts = [p.strip() for p in re.split(r"\s*(?:&&|\|\||[;|])\s*", cleaned_cmd) if p.strip()]
|
|
140
|
+
is_pure_read = bool(parts) and all(p.split()[0].lower() in read_only_verbs for p in parts if p.split())
|
|
141
|
+
if not is_pure_read:
|
|
142
|
+
has_productive_bash = True
|
|
143
|
+
|
|
129
144
|
snap.had_progress = bool(
|
|
130
145
|
snap.files_created
|
|
131
146
|
or snap.files_modified
|
|
132
|
-
or (snap.bash_exit_codes and 0 in snap.bash_exit_codes)
|
|
147
|
+
or (snap.bash_exit_codes and 0 in snap.bash_exit_codes and has_productive_bash)
|
|
133
148
|
)
|
|
134
149
|
|
|
135
150
|
# No tools called at all = not progress (empty turn)
|
|
@@ -190,12 +205,22 @@ class ProgressMonitor:
|
|
|
190
205
|
f"Take a step back: what is the simplest path to a working solution?"
|
|
191
206
|
)
|
|
192
207
|
|
|
208
|
+
if all(
|
|
209
|
+
not s.files_created and not s.files_modified and any(t in ("read_file", "list_files", "bash") for t in s.tools_called)
|
|
210
|
+
for s in recent
|
|
211
|
+
):
|
|
212
|
+
return (
|
|
213
|
+
"[SYSTEM - PROGRESS MONITOR] Stalled in read-only inspection for 3 consecutive turns. "
|
|
214
|
+
"You are repeatedly viewing or inspecting files without creating or editing deliverables. "
|
|
215
|
+
"Stop inspecting. Proceed immediately to implementing the next deliverable: use write_file or edit_file to create remaining code/test files, or run verification tests."
|
|
216
|
+
)
|
|
217
|
+
|
|
193
218
|
if all(snap.errors or (snap.bash_exit_codes and 0 not in snap.bash_exit_codes) for snap in recent):
|
|
194
219
|
err_msg = f"\nLast error encountered: {last_error_text}\n" if last_error_text else ""
|
|
195
220
|
return (
|
|
196
221
|
"[SYSTEM - PROGRESS MONITOR] You have encountered errors for 3 consecutive turns. "
|
|
197
222
|
f"STOP retrying the same approach.{err_msg} Instead:\n"
|
|
198
|
-
"1. Use read_file to examine the FULL current state of the file(s) you
|
|
223
|
+
"1. Use read_file to examine the FULL current state of the file(s) you're editing\n"
|
|
199
224
|
"2. Identify the root cause of the error (not the symptom)\n"
|
|
200
225
|
"3. Make ONE comprehensive fix that addresses all issues\n"
|
|
201
226
|
"If the task approach is fundamentally wrong, start with a simpler design."
|
|
@@ -75,6 +75,8 @@ class System1Controller:
|
|
|
75
75
|
self._action_history: List[str] = []
|
|
76
76
|
self._loop_count: Dict[str, int] = {}
|
|
77
77
|
self._file_write_counts: Dict[str, int] = {}
|
|
78
|
+
self._file_read_counts: Dict[str, int] = {}
|
|
79
|
+
self._ls_count: int = 0
|
|
78
80
|
self.total_interventions: int = 0
|
|
79
81
|
self.total_llm_skips: int = 0
|
|
80
82
|
|
|
@@ -89,6 +91,8 @@ class System1Controller:
|
|
|
89
91
|
self._action_history.clear()
|
|
90
92
|
self._loop_count.clear()
|
|
91
93
|
self._file_write_counts.clear()
|
|
94
|
+
self._file_read_counts.clear()
|
|
95
|
+
self._ls_count = 0
|
|
92
96
|
|
|
93
97
|
# ── 1. Test Result Checking ─────────────────────────────────────────
|
|
94
98
|
|
|
@@ -141,13 +145,35 @@ class System1Controller:
|
|
|
141
145
|
|
|
142
146
|
# ── 2. Error Triage ─────────────────────────────────────────────────
|
|
143
147
|
|
|
144
|
-
def triage_error(self, err_text: str) -> Tuple[ErrorType, Optional[str], Optional[int]]:
|
|
148
|
+
def triage_error(self, err_text: str, tool_name: Optional[str] = None) -> Tuple[ErrorType, Optional[str], Optional[int]]:
|
|
145
149
|
"""Classify error into ErrorType and extract file & line."""
|
|
146
150
|
if not err_text or not err_text.strip():
|
|
147
151
|
return ErrorType.NONE, None, None
|
|
148
152
|
|
|
149
|
-
#
|
|
153
|
+
# 1. Read-only tools (read_file, list_files, file_search, read_symbol)
|
|
154
|
+
if tool_name in ("read_file", "list_files", "file_search", "read_symbol"):
|
|
155
|
+
stripped = err_text.strip()
|
|
156
|
+
# Only an error if the tool explicitly reported failure
|
|
157
|
+
if stripped.lower().startswith("error:") or stripped.startswith("FileNotFoundError") or "file not found" in stripped.lower():
|
|
158
|
+
return ErrorType.UNKNOWN, None, None
|
|
159
|
+
# Otherwise, read_file output is source code, NOT an execution error!
|
|
160
|
+
return ErrorType.NONE, None, None
|
|
161
|
+
|
|
162
|
+
# Numbered lines from _read_file: " 1 | ..."
|
|
163
|
+
if re.match(r"^\s*\d+\s*\|", err_text.lstrip()):
|
|
164
|
+
return ErrorType.NONE, None, None
|
|
165
|
+
|
|
166
|
+
# Successful write_file: "wrote N bytes to ..."
|
|
167
|
+
if err_text.startswith("wrote ") and " bytes to " in err_text:
|
|
168
|
+
return ErrorType.NONE, None, None
|
|
169
|
+
|
|
170
|
+
# Successful bash command: exit_code is 0 and no failure banner
|
|
171
|
+
if ('"exit_code": 0' in err_text or '"exit_code":0' in err_text) and "⚠️ COMMAND FAILED" not in err_text:
|
|
172
|
+
return ErrorType.NONE, None, None
|
|
173
|
+
|
|
150
174
|
lower_err = err_text.lower()
|
|
175
|
+
|
|
176
|
+
# Truncation error or syntax cutoff due to unexpected EOF
|
|
151
177
|
if "truncated" in lower_err or "unexpected end of input" in lower_err or "unexpected eof" in lower_err:
|
|
152
178
|
file_match = re.search(r"for\s+['\"]?([a-zA-Z0-9_\.\-\/]+)['\"]?", err_text) or re.search(r"in\s+['\"]?([a-zA-Z0-9_\.\-\/]+)['\"]?", err_text)
|
|
153
179
|
target = file_match.group(1) if file_match else None
|
|
@@ -166,22 +192,26 @@ class System1Controller:
|
|
|
166
192
|
return ErrorType.DEPENDENCY, None, None
|
|
167
193
|
|
|
168
194
|
# Assertion failure
|
|
169
|
-
if "assertionerror" in lower_err or "fail:" in lower_err:
|
|
195
|
+
if "assertionerror" in lower_err or "fail:" in lower_err or "assertion error" in lower_err:
|
|
170
196
|
return ErrorType.ASSERTION, None, None
|
|
171
197
|
|
|
172
198
|
# Timeout
|
|
173
199
|
if "timed out" in lower_err or "timeout" in lower_err:
|
|
174
200
|
return ErrorType.TIMEOUT, None, None
|
|
175
201
|
|
|
176
|
-
# Runtime / general error (
|
|
177
|
-
|
|
178
|
-
"
|
|
179
|
-
"
|
|
180
|
-
"
|
|
181
|
-
"
|
|
182
|
-
"
|
|
202
|
+
# Runtime / general error (detect actual execution crashes and tracebacks, not code words)
|
|
203
|
+
real_error_patterns = [
|
|
204
|
+
r"traceback \(most recent call last\):",
|
|
205
|
+
r"\b(nameerror|typeerror|valueerror|keyerror|indexerror|runtimeerror|oserror|ioerror|filenotfounderror|importerror|attributeerror|permissionerror|zerodivisionerror|recursionerror):\s",
|
|
206
|
+
r"⚠️\s*command failed",
|
|
207
|
+
r"^error:\s",
|
|
208
|
+
r"^fatal:\s",
|
|
209
|
+
r"command failed:",
|
|
210
|
+
r"\berrno\s+\d+",
|
|
211
|
+
r"permission denied",
|
|
212
|
+
r"no such file or directory",
|
|
183
213
|
]
|
|
184
|
-
if any(
|
|
214
|
+
if any(re.search(pat, lower_err, re.MULTILINE) for pat in real_error_patterns):
|
|
185
215
|
return ErrorType.UNKNOWN, None, None
|
|
186
216
|
|
|
187
217
|
# Normal output (no error detected) → do NOT treat as error
|
|
@@ -269,7 +299,66 @@ class System1Controller:
|
|
|
269
299
|
confidence=0.95,
|
|
270
300
|
)
|
|
271
301
|
|
|
272
|
-
|
|
302
|
+
# Inspection loop detection: repeatedly reading/inspecting the same file
|
|
303
|
+
if tool_name == "read_file":
|
|
304
|
+
path = str(tool_args.get("path") or tool_args.get("file") or tool_args.get("filepath") or "").strip()
|
|
305
|
+
if path:
|
|
306
|
+
count = self._file_read_counts.get(path, 0) + 1
|
|
307
|
+
self._file_read_counts[path] = count
|
|
308
|
+
if count >= 2:
|
|
309
|
+
self.total_interventions += 1
|
|
310
|
+
directive = (
|
|
311
|
+
f"[SYSTEM 1 DIRECTIVE] Inspection loop detected: You have already read and inspected '{path}'. "
|
|
312
|
+
f"Stop re-reading '{path}'. Proceed immediately to implementing the next deliverable: "
|
|
313
|
+
"create any remaining files (e.g. test files) using write_file, or run test verification."
|
|
314
|
+
)
|
|
315
|
+
return JevDecision(
|
|
316
|
+
action=ActionCategory.RETRY_WITH_DIRECTIVE,
|
|
317
|
+
error_type=ErrorType.NONE,
|
|
318
|
+
directive=directive,
|
|
319
|
+
confidence=0.95,
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
# Inspection loop detection for bash (cat, head, tail, ls)
|
|
323
|
+
if tool_name == "bash":
|
|
324
|
+
cmd = str(tool_args.get("command", "")).strip().lower()
|
|
325
|
+
if any(cmd.startswith(v) or f" {v}" in cmd for v in ("cat ", "head ", "tail ", "less ")):
|
|
326
|
+
parts = cmd.split()
|
|
327
|
+
for p in parts[1:]:
|
|
328
|
+
clean_p = p.strip("'\"")
|
|
329
|
+
if clean_p.endswith((".py", ".js", ".ts", ".html", ".css", ".json", ".toml")):
|
|
330
|
+
count = self._file_read_counts.get(clean_p, 0) + 1
|
|
331
|
+
self._file_read_counts[clean_p] = count
|
|
332
|
+
if count >= 2:
|
|
333
|
+
self.total_interventions += 1
|
|
334
|
+
directive = (
|
|
335
|
+
f"[SYSTEM 1 DIRECTIVE] Inspection loop detected: You are inspecting '{clean_p}' via bash. "
|
|
336
|
+
f"'{clean_p}' already exists and has been reviewed. Stop re-reading it. "
|
|
337
|
+
"Proceed immediately to implement the NEXT required file or run test verification."
|
|
338
|
+
)
|
|
339
|
+
return JevDecision(
|
|
340
|
+
action=ActionCategory.RETRY_WITH_DIRECTIVE,
|
|
341
|
+
error_type=ErrorType.NONE,
|
|
342
|
+
directive=directive,
|
|
343
|
+
confidence=0.95,
|
|
344
|
+
)
|
|
345
|
+
if cmd.startswith("ls") or " ls " in cmd or "ls -la" in cmd:
|
|
346
|
+
self._ls_count += 1
|
|
347
|
+
if self._ls_count >= 2:
|
|
348
|
+
self.total_interventions += 1
|
|
349
|
+
directive = (
|
|
350
|
+
"[SYSTEM 1 DIRECTIVE] Repeated directory listing detected. "
|
|
351
|
+
"Stop inspecting directory contents. Proceed immediately to creating the next required file "
|
|
352
|
+
"using write_file, or run test verification."
|
|
353
|
+
)
|
|
354
|
+
return JevDecision(
|
|
355
|
+
action=ActionCategory.RETRY_WITH_DIRECTIVE,
|
|
356
|
+
error_type=ErrorType.NONE,
|
|
357
|
+
directive=directive,
|
|
358
|
+
confidence=0.95,
|
|
359
|
+
)
|
|
360
|
+
|
|
361
|
+
err_type, target_file, target_line = self.triage_error(tool_result, tool_name=tool_name)
|
|
273
362
|
|
|
274
363
|
# 1. Loop detection (using Dream-RSI adaptive thresholds)
|
|
275
364
|
if err_type != ErrorType.NONE and self.detect_loop(tool_name, tool_args, tool_result, err_type=err_type):
|
|
@@ -288,10 +377,22 @@ class System1Controller:
|
|
|
288
377
|
target_line=target_line,
|
|
289
378
|
directive=directive,
|
|
290
379
|
)
|
|
380
|
+
elif tool_name == "bash":
|
|
381
|
+
cmd = str(tool_args.get("command", ""))
|
|
382
|
+
directive = (
|
|
383
|
+
f"[SYSTEM 1 DIRECTIVE] Repeated command failure loop detected for: '{cmd[:80]}'.\n"
|
|
384
|
+
"Do NOT re-run the same failing command. Fix the root cause in the source code using edit_file or adjust the command arguments."
|
|
385
|
+
)
|
|
386
|
+
return JevDecision(
|
|
387
|
+
action=ActionCategory.RETRY_WITH_DIRECTIVE,
|
|
388
|
+
confidence=0.95,
|
|
389
|
+
error_type=err_type,
|
|
390
|
+
directive=directive,
|
|
391
|
+
)
|
|
291
392
|
else:
|
|
292
393
|
directive = (
|
|
293
|
-
f"[SYSTEM 1
|
|
294
|
-
"
|
|
394
|
+
f"[SYSTEM 1 DIRECTIVE] Repeated failure loop detected for {tool_name}.\n"
|
|
395
|
+
"Do NOT repeat the same failing call. Proceed with an alternative action or next deliverable."
|
|
295
396
|
)
|
|
296
397
|
return JevDecision(
|
|
297
398
|
action=ActionCategory.RETRY_WITH_DIRECTIVE,
|