agentic-dev-loop 1.0.0a30__tar.gz → 1.0.0a31__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.
Potentially problematic release.
This version of agentic-dev-loop might be problematic. Click here for more details.
- {agentic_dev_loop-1.0.0a30/src/agentic_dev_loop.egg-info → agentic_dev_loop-1.0.0a31}/PKG-INFO +23 -1
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/README.md +22 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/pyproject.toml +2 -1
- agentic_dev_loop-1.0.0a31/src/agentic_dev_loop/engine/agent_error_guide.md +6 -0
- agentic_dev_loop-1.0.0a31/src/agentic_dev_loop/engine/agent_error_guide.py +26 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/src/agentic_dev_loop/engine/stages.py +14 -12
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31/src/agentic_dev_loop.egg-info}/PKG-INFO +23 -1
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/src/agentic_dev_loop.egg-info/SOURCES.txt +3 -0
- agentic_dev_loop-1.0.0a31/tests/test_agent_error_guide.py +184 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/LICENSE +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/setup.cfg +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/src/agentic_dev_loop/__init__.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/src/agentic_dev_loop/core/__init__.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/src/agentic_dev_loop/core/config.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/src/agentic_dev_loop/core/paths.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/src/agentic_dev_loop/core/results.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/src/agentic_dev_loop/core/verdict.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/src/agentic_dev_loop/engine/__init__.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/src/agentic_dev_loop/engine/actions.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/src/agentic_dev_loop/engine/agents.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/src/agentic_dev_loop/engine/cli.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/src/agentic_dev_loop/engine/deps.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/src/agentic_dev_loop/engine/dryrun.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/src/agentic_dev_loop/engine/egress_policy.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/src/agentic_dev_loop/engine/egress_proxy.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/src/agentic_dev_loop/engine/extensions/__init__.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/src/agentic_dev_loop/engine/extensions/aidlc.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/src/agentic_dev_loop/engine/graph.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/src/agentic_dev_loop/engine/hostboundary.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/src/agentic_dev_loop/engine/isolation.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/src/agentic_dev_loop/engine/llm.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/src/agentic_dev_loop/engine/loaders.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/src/agentic_dev_loop/engine/mounts.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/src/agentic_dev_loop/engine/outputs.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/src/agentic_dev_loop/engine/presets.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/src/agentic_dev_loop/engine/proc.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/src/agentic_dev_loop/engine/registry.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/src/agentic_dev_loop/engine/seccomp/__init__.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/src/agentic_dev_loop/engine/seccomp/chromium-userns.json +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/src/agentic_dev_loop/engine/secrets.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/src/agentic_dev_loop/engine/step_workspace.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/src/agentic_dev_loop/engine/sync_back.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/src/agentic_dev_loop/py.typed +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/src/agentic_dev_loop/viz/__init__.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/src/agentic_dev_loop/viz/__main__.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/src/agentic_dev_loop/viz/server.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/src/agentic_dev_loop.egg-info/dependency_links.txt +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/src/agentic_dev_loop.egg-info/entry_points.txt +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/src/agentic_dev_loop.egg-info/requires.txt +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/src/agentic_dev_loop.egg-info/top_level.txt +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/tests/test_028_declared_caches.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/tests/test_029_merge_on_conflict.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/tests/test_032_stop_when_green.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/tests/test_033_brief_step.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/tests/test_actions.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/tests/test_agents.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/tests/test_aidlc.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/tests/test_cli.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/tests/test_config.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/tests/test_deps.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/tests/test_dry_run.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/tests/test_e2e_fixtures.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/tests/test_egress_proxy.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/tests/test_graph.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/tests/test_hostboundary.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/tests/test_hostboundary_integration.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/tests/test_isolation.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/tests/test_isolation_integration.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/tests/test_isolation_pbt.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/tests/test_llm.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/tests/test_loaders.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/tests/test_module_contract.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/tests/test_outputs.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/tests/test_outputs_pbt.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/tests/test_proc.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/tests/test_publish.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/tests/test_qa_prompt.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/tests/test_registry.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/tests/test_repo_ascii.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/tests/test_results.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/tests/test_sdk_compat.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/tests/test_secrets.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/tests/test_secrets_integration.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/tests/test_secrets_pipeline.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/tests/test_source_resolution.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/tests/test_stages.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/tests/test_step_workspace.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/tests/test_sync_back.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/tests/test_trigger_script.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/tests/test_unit5_config.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/tests/test_unit5_pbt.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/tests/test_unit5_pure.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/tests/test_verdict.py +0 -0
- {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/tests/test_viz.py +0 -0
{agentic_dev_loop-1.0.0a30/src/agentic_dev_loop.egg-info → agentic_dev_loop-1.0.0a31}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agentic-dev-loop
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.0a31
|
|
4
4
|
Summary: Turn an LLM into a gated, sandboxed, resumable construction loop over your codebase.
|
|
5
5
|
Author: Luke Wilson
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -541,6 +541,28 @@ dotnet restore --locked-mode # installs exactly packages.lock.json
|
|
|
541
541
|
Keep the lockfile tracked (do not gitignore it) and re-run the locked restore
|
|
542
542
|
to prove it holds from a clean cache before setting `deps_policy: locked`.
|
|
543
543
|
|
|
544
|
+
## Agent-facing error guide
|
|
545
|
+
|
|
546
|
+
The engine bundles an always-on guide for the engine error codes an agent
|
|
547
|
+
can actually observe while running terminal commands. The guide lives at
|
|
548
|
+
`src/agentic_dev_loop/engine/agent_error_guide.md`, versioned with the engine
|
|
549
|
+
code that emits the codes, and is injected into every agent step's task
|
|
550
|
+
context at assembly time. There is no opt-in and no opt-out in v1; command
|
|
551
|
+
and action steps never receive it because no agent reads them.
|
|
552
|
+
|
|
553
|
+
Each agent-visible code has exactly one table row, shaped like this:
|
|
554
|
+
|
|
555
|
+
| Code | Meaning | Correct response |
|
|
556
|
+
| --- | --- | --- |
|
|
557
|
+
| AE-SBX-016 | The loop refused to sync an edit because the real repo changed underneath on a path this workspace also touches; the file in this workspace is stale and its edits will not be applied. | The command is not the problem: retry once, do not edit or work around the named path, and mention the code in your final response if it persists. |
|
|
558
|
+
|
|
559
|
+
The guide is consumer-agnostic: it explains the general mechanism and never
|
|
560
|
+
names a consumer or a consumer domain rule ID. If an ID must appear inside a
|
|
561
|
+
quoted engine message, the placeholder `<domain-rule-id>` is used. Any engine
|
|
562
|
+
change that adds or changes an agent-visible error code must update the guide
|
|
563
|
+
in the same commit; the test suite enforces the pairing by parsing the
|
|
564
|
+
agent-visible `SandboxError` raise sites against the bundled table.
|
|
565
|
+
|
|
544
566
|
## Host boundary (security model)
|
|
545
567
|
|
|
546
568
|
The orchestrator, tool layer, and the `commit` action run in the **host
|
|
@@ -499,6 +499,28 @@ dotnet restore --locked-mode # installs exactly packages.lock.json
|
|
|
499
499
|
Keep the lockfile tracked (do not gitignore it) and re-run the locked restore
|
|
500
500
|
to prove it holds from a clean cache before setting `deps_policy: locked`.
|
|
501
501
|
|
|
502
|
+
## Agent-facing error guide
|
|
503
|
+
|
|
504
|
+
The engine bundles an always-on guide for the engine error codes an agent
|
|
505
|
+
can actually observe while running terminal commands. The guide lives at
|
|
506
|
+
`src/agentic_dev_loop/engine/agent_error_guide.md`, versioned with the engine
|
|
507
|
+
code that emits the codes, and is injected into every agent step's task
|
|
508
|
+
context at assembly time. There is no opt-in and no opt-out in v1; command
|
|
509
|
+
and action steps never receive it because no agent reads them.
|
|
510
|
+
|
|
511
|
+
Each agent-visible code has exactly one table row, shaped like this:
|
|
512
|
+
|
|
513
|
+
| Code | Meaning | Correct response |
|
|
514
|
+
| --- | --- | --- |
|
|
515
|
+
| AE-SBX-016 | The loop refused to sync an edit because the real repo changed underneath on a path this workspace also touches; the file in this workspace is stale and its edits will not be applied. | The command is not the problem: retry once, do not edit or work around the named path, and mention the code in your final response if it persists. |
|
|
516
|
+
|
|
517
|
+
The guide is consumer-agnostic: it explains the general mechanism and never
|
|
518
|
+
names a consumer or a consumer domain rule ID. If an ID must appear inside a
|
|
519
|
+
quoted engine message, the placeholder `<domain-rule-id>` is used. Any engine
|
|
520
|
+
change that adds or changes an agent-visible error code must update the guide
|
|
521
|
+
in the same commit; the test suite enforces the pairing by parsing the
|
|
522
|
+
agent-visible `SandboxError` raise sites against the bundled table.
|
|
523
|
+
|
|
502
524
|
## Host boundary (security model)
|
|
503
525
|
|
|
504
526
|
The orchestrator, tool layer, and the `commit` action run in the **host
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "agentic-dev-loop"
|
|
7
|
-
version = "1.0.
|
|
7
|
+
version = "1.0.0a31"
|
|
8
8
|
description = "Turn an LLM into a gated, sandboxed, resumable construction loop over your codebase."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.12"
|
|
@@ -68,6 +68,7 @@ where = ["src"]
|
|
|
68
68
|
|
|
69
69
|
[tool.setuptools.package-data]
|
|
70
70
|
agentic_dev_loop = ["py.typed"]
|
|
71
|
+
"agentic_dev_loop.engine" = ["*.md"]
|
|
71
72
|
"agentic_dev_loop.engine.seccomp" = ["*.json"]
|
|
72
73
|
|
|
73
74
|
[tool.pytest.ini_options]
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
| Code | Meaning | Correct response |
|
|
2
|
+
| --- | --- | --- |
|
|
3
|
+
| AE-SBX-008 | The sandbox refused a git mutation command. Only the single commit step may change git state; agents never commit, branch, tag, or otherwise mutate git. | Do not run the command or any git mutation again. Continue without touching git and leave committing to the commit step. |
|
|
4
|
+
| AE-SBX-010 | The sandbox could not create the execution container for the command, so the command did not run. | The command is not the problem: retry once; if it persists, mention the code in your final response and stop. |
|
|
5
|
+
| AE-SBX-016 | The loop refused to sync an edit because the real repo changed underneath on a path this workspace also touches; the file in this workspace is stale and its edits will not be applied. | The command is not the problem: retry once, do not edit or work around the named path, and mention the code in your final response if it persists. |
|
|
6
|
+
| AE-SBX-018 | The network egress proxy for this step stopped, so the sandbox refused the command rather than run without enforcement. | The command is not the problem: do not retry it, mention the code in your final response, and stop. |
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""Bundled agent-facing error guide (034).
|
|
2
|
+
|
|
3
|
+
The guide table is an engine-owned resource, versioned with the engine so the
|
|
4
|
+
codes and their guidance cannot drift. Every agent step's task context gets
|
|
5
|
+
the table injected at assembly time; command and action steps never do (no
|
|
6
|
+
agent reads them).
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from importlib.resources import files
|
|
12
|
+
|
|
13
|
+
GUIDE_RESOURCE = "agent_error_guide.md"
|
|
14
|
+
GUIDE_HEADING = "Agent-facing error guide"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def load_guide() -> str:
|
|
18
|
+
"""The bundled guide table text."""
|
|
19
|
+
return (files("agentic_dev_loop.engine")
|
|
20
|
+
.joinpath(GUIDE_RESOURCE)
|
|
21
|
+
.read_text(encoding="utf-8"))
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def guide_section() -> str:
|
|
25
|
+
"""The guide block injected into an agent step's task context."""
|
|
26
|
+
return f"## {GUIDE_HEADING}\n\n{load_guide()}"
|
{agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/src/agentic_dev_loop/engine/stages.py
RENAMED
|
@@ -32,6 +32,7 @@ from typing import TYPE_CHECKING
|
|
|
32
32
|
from langgraph.graph import END
|
|
33
33
|
from openhands.sdk import Conversation
|
|
34
34
|
|
|
35
|
+
from .agent_error_guide import guide_section
|
|
35
36
|
from .agents import load_agent, register_sandbox_terminal
|
|
36
37
|
from agentic_dev_loop.core.config import TERMINALS, PipelineConfig, Route, StepConfig, route_target
|
|
37
38
|
from agentic_dev_loop.core.paths import _has_wildcards
|
|
@@ -364,12 +365,12 @@ def _artifact_file(runtime: Runtime, step_name: str) -> str | None:
|
|
|
364
365
|
def build_context(runtime: Runtime, docs: dict[str, str], subject: str,
|
|
365
366
|
notes: list[str] | None = None,
|
|
366
367
|
artifacts: dict[str, str] | None = None) -> str:
|
|
367
|
-
"""Assemble the attempt's shared message:
|
|
368
|
-
|
|
369
|
-
section generated from step defs (a prompt
|
|
370
|
-
skip them), accumulated stage artifacts
|
|
371
|
-
notes."""
|
|
372
|
-
parts = [f"You are working on {subject}.\n"]
|
|
368
|
+
"""Assemble the attempt's shared message: the always-on agent-facing error
|
|
369
|
+
guide (034), subject, doc bundle, the canonical dependency-policy paragraph
|
|
370
|
+
(021), the expected-output-paths section generated from step defs (a prompt
|
|
371
|
+
merely naming files lets agents skip them), accumulated stage artifacts
|
|
372
|
+
(027), and previous-attempt notes."""
|
|
373
|
+
parts = [f"You are working on {subject}.\n", guide_section()]
|
|
373
374
|
for name, content in docs.items():
|
|
374
375
|
parts.append(f"## {name}\n\n{content}")
|
|
375
376
|
if artifacts:
|
|
@@ -541,13 +542,14 @@ def brief_inputs_hash(runtime: Runtime, step: StepConfig,
|
|
|
541
542
|
def build_brief_context(runtime: Runtime, step: StepConfig,
|
|
542
543
|
docs: dict[str, str],
|
|
543
544
|
doc_sources: dict[str, str] | None = None) -> str:
|
|
544
|
-
"""Assemble the brief agent's task message (033): the
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
and
|
|
548
|
-
attempt."""
|
|
545
|
+
"""Assemble the brief agent's task message (033): the always-on
|
|
546
|
+
agent-facing error guide (034), the resolved unit/plan docs (re-read from
|
|
547
|
+
disk where sources are known), the closed ``brief_inputs.include`` file
|
|
548
|
+
set, and the env-facts block. Prior verdicts and feedback are deliberately
|
|
549
|
+
absent - the notes channel delivers them per attempt."""
|
|
549
550
|
sections, env_facts = _brief_sections(runtime, step, docs, doc_sources)
|
|
550
|
-
parts = [
|
|
551
|
+
parts = [guide_section()]
|
|
552
|
+
parts += [f"## {name}\n\n{content}" for name, content in sections.items()]
|
|
551
553
|
if env_facts:
|
|
552
554
|
parts.append("## Environment facts\n\n"
|
|
553
555
|
+ "\n".join(f"- {fact}" for fact in env_facts))
|
{agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31/src/agentic_dev_loop.egg-info}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agentic-dev-loop
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.0a31
|
|
4
4
|
Summary: Turn an LLM into a gated, sandboxed, resumable construction loop over your codebase.
|
|
5
5
|
Author: Luke Wilson
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -541,6 +541,28 @@ dotnet restore --locked-mode # installs exactly packages.lock.json
|
|
|
541
541
|
Keep the lockfile tracked (do not gitignore it) and re-run the locked restore
|
|
542
542
|
to prove it holds from a clean cache before setting `deps_policy: locked`.
|
|
543
543
|
|
|
544
|
+
## Agent-facing error guide
|
|
545
|
+
|
|
546
|
+
The engine bundles an always-on guide for the engine error codes an agent
|
|
547
|
+
can actually observe while running terminal commands. The guide lives at
|
|
548
|
+
`src/agentic_dev_loop/engine/agent_error_guide.md`, versioned with the engine
|
|
549
|
+
code that emits the codes, and is injected into every agent step's task
|
|
550
|
+
context at assembly time. There is no opt-in and no opt-out in v1; command
|
|
551
|
+
and action steps never receive it because no agent reads them.
|
|
552
|
+
|
|
553
|
+
Each agent-visible code has exactly one table row, shaped like this:
|
|
554
|
+
|
|
555
|
+
| Code | Meaning | Correct response |
|
|
556
|
+
| --- | --- | --- |
|
|
557
|
+
| AE-SBX-016 | The loop refused to sync an edit because the real repo changed underneath on a path this workspace also touches; the file in this workspace is stale and its edits will not be applied. | The command is not the problem: retry once, do not edit or work around the named path, and mention the code in your final response if it persists. |
|
|
558
|
+
|
|
559
|
+
The guide is consumer-agnostic: it explains the general mechanism and never
|
|
560
|
+
names a consumer or a consumer domain rule ID. If an ID must appear inside a
|
|
561
|
+
quoted engine message, the placeholder `<domain-rule-id>` is used. Any engine
|
|
562
|
+
change that adds or changes an agent-visible error code must update the guide
|
|
563
|
+
in the same commit; the test suite enforces the pairing by parsing the
|
|
564
|
+
agent-visible `SandboxError` raise sites against the bundled table.
|
|
565
|
+
|
|
544
566
|
## Host boundary (security model)
|
|
545
567
|
|
|
546
568
|
The orchestrator, tool layer, and the `commit` action run in the **host
|
{agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/src/agentic_dev_loop.egg-info/SOURCES.txt
RENAMED
|
@@ -16,6 +16,8 @@ src/agentic_dev_loop/core/results.py
|
|
|
16
16
|
src/agentic_dev_loop/core/verdict.py
|
|
17
17
|
src/agentic_dev_loop/engine/__init__.py
|
|
18
18
|
src/agentic_dev_loop/engine/actions.py
|
|
19
|
+
src/agentic_dev_loop/engine/agent_error_guide.md
|
|
20
|
+
src/agentic_dev_loop/engine/agent_error_guide.py
|
|
19
21
|
src/agentic_dev_loop/engine/agents.py
|
|
20
22
|
src/agentic_dev_loop/engine/cli.py
|
|
21
23
|
src/agentic_dev_loop/engine/deps.py
|
|
@@ -48,6 +50,7 @@ tests/test_029_merge_on_conflict.py
|
|
|
48
50
|
tests/test_032_stop_when_green.py
|
|
49
51
|
tests/test_033_brief_step.py
|
|
50
52
|
tests/test_actions.py
|
|
53
|
+
tests/test_agent_error_guide.py
|
|
51
54
|
tests/test_agents.py
|
|
52
55
|
tests/test_aidlc.py
|
|
53
56
|
tests/test_cli.py
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
"""Agent-facing error guide (034).
|
|
2
|
+
|
|
3
|
+
The guide is an engine-owned bundled resource injected into every agent
|
|
4
|
+
step's task context. These tests pin the injection point, the one-line-per-code
|
|
5
|
+
table shape, the consumer-agnostic ASCII content, and the pairing between the
|
|
6
|
+
guide and the agent-visible ``SandboxError`` codes emitted by the engine.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import ast
|
|
12
|
+
import re
|
|
13
|
+
from collections import Counter
|
|
14
|
+
from dataclasses import replace
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
|
|
17
|
+
import pytest
|
|
18
|
+
from openhands.sdk import LLM
|
|
19
|
+
|
|
20
|
+
from agentic_dev_loop.core.config import PipelineConfig
|
|
21
|
+
from agentic_dev_loop.engine.agent_error_guide import (
|
|
22
|
+
GUIDE_HEADING,
|
|
23
|
+
GUIDE_RESOURCE,
|
|
24
|
+
guide_section,
|
|
25
|
+
load_guide,
|
|
26
|
+
)
|
|
27
|
+
from agentic_dev_loop.engine.registry import default_registry
|
|
28
|
+
from agentic_dev_loop.engine.stages import (
|
|
29
|
+
Runtime,
|
|
30
|
+
build_brief_context,
|
|
31
|
+
build_context,
|
|
32
|
+
make_agent_node,
|
|
33
|
+
make_command_node,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
REPO_ROOT = Path(__file__).resolve().parents[1]
|
|
37
|
+
ISOLATION = REPO_ROOT / "src" / "agentic_dev_loop" / "engine" / "isolation.py"
|
|
38
|
+
|
|
39
|
+
# The source-of-truth agent-visible codes. AE-SBX-012/013/017/019 are emitted
|
|
40
|
+
# from the same Sandbox.run surface but are internal: sticky infra failures
|
|
41
|
+
# (012/019) stop the run before the agent can respond, and 013/017 are
|
|
42
|
+
# engine-invariant/config errors that never describe a command the agent can
|
|
43
|
+
# correct.
|
|
44
|
+
AGENT_VISIBLE_CODES = {"AE-SBX-008", "AE-SBX-010", "AE-SBX-016", "AE-SBX-018"}
|
|
45
|
+
INTERNAL_SANDBOX_CODES = {"AE-SBX-012", "AE-SBX-013", "AE-SBX-017", "AE-SBX-019"}
|
|
46
|
+
|
|
47
|
+
_CODE_RE = re.compile(r"AE-SBX-\d{3}")
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def make_cfg(steps, **over):
|
|
51
|
+
base = {"name": "t", "llm": {"model": "m", "api_key_env": "K"},
|
|
52
|
+
"agents_dir": "agents", "lockfiles": ["uv.lock"], "steps": steps}
|
|
53
|
+
base.update(over)
|
|
54
|
+
return PipelineConfig.model_validate(base)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def make_state(**over):
|
|
58
|
+
state = {"subject": "plan plan.md", "docs": {"plan.md": "THE PLAN"},
|
|
59
|
+
"artifacts": {}, "attempt": 1, "max_attempts": 3, "notes": [],
|
|
60
|
+
"failed": False, "outcome": None, "commit_allowed": False,
|
|
61
|
+
"step_verdicts": {}, "retry_target": None, "thread_id": "t1"}
|
|
62
|
+
state.update(over)
|
|
63
|
+
return state
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@pytest.fixture
|
|
67
|
+
def runtime(tmp_path, fake_sandbox):
|
|
68
|
+
state_dir = tmp_path / ".pr"
|
|
69
|
+
state_dir.mkdir()
|
|
70
|
+
cfg = make_cfg([{"name": "dev", "agent": "dev",
|
|
71
|
+
"produces": ["implementation-summary.md"],
|
|
72
|
+
"on_pass": "success"}])
|
|
73
|
+
return Runtime(cfg=cfg, registry=default_registry(),
|
|
74
|
+
llm=LLM(model="openai/test-model", api_key="sk-test"),
|
|
75
|
+
agents_dir=tmp_path / "agents", state_dir=state_dir,
|
|
76
|
+
workspace=tmp_path, sandbox=fake_sandbox)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _guide_codes(text: str) -> list[str]:
|
|
80
|
+
codes = []
|
|
81
|
+
for line in text.splitlines():
|
|
82
|
+
stripped = line.strip()
|
|
83
|
+
if not stripped.startswith("|"):
|
|
84
|
+
continue
|
|
85
|
+
cells = [c.strip() for c in stripped.strip("|").split("|")]
|
|
86
|
+
if cells and _CODE_RE.fullmatch(cells[0]):
|
|
87
|
+
codes.append(cells[0])
|
|
88
|
+
return codes
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _agent_path_error_codes() -> set[str]:
|
|
92
|
+
"""Every AE-SBX code raised on the Sandbox.run surface an agent terminal
|
|
93
|
+
action can reach, parsed from the engine source."""
|
|
94
|
+
tree = ast.parse(ISOLATION.read_text(encoding="utf-8"), filename=str(ISOLATION))
|
|
95
|
+
target_nodes: list[ast.AST] = []
|
|
96
|
+
for node in tree.body:
|
|
97
|
+
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
|
|
98
|
+
if node.name == "_guard_git":
|
|
99
|
+
target_nodes.append(node)
|
|
100
|
+
elif isinstance(node, ast.ClassDef) and node.name == "DockerBackend":
|
|
101
|
+
for child in node.body:
|
|
102
|
+
if isinstance(child, (ast.FunctionDef, ast.AsyncFunctionDef)):
|
|
103
|
+
if child.name in {"run", "_run_legacy", "_run_profile",
|
|
104
|
+
"_exec", "_start_profile", "_sync_back"}:
|
|
105
|
+
target_nodes.append(child)
|
|
106
|
+
|
|
107
|
+
found: set[str] = set()
|
|
108
|
+
for node in target_nodes:
|
|
109
|
+
for child in ast.walk(node):
|
|
110
|
+
if isinstance(child, ast.Raise) and isinstance(child.exc, ast.Call):
|
|
111
|
+
fn = child.exc.func
|
|
112
|
+
if isinstance(fn, ast.Name) and fn.id == "SandboxError":
|
|
113
|
+
for part in ast.walk(child.exc):
|
|
114
|
+
if isinstance(part, ast.Constant) and isinstance(part.value, str):
|
|
115
|
+
found.update(_CODE_RE.findall(part.value))
|
|
116
|
+
return found
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
class TestBundledGuide:
|
|
120
|
+
def test_loads_from_package_resource(self):
|
|
121
|
+
from importlib.resources import files
|
|
122
|
+
|
|
123
|
+
expected = (files("agentic_dev_loop.engine")
|
|
124
|
+
.joinpath(GUIDE_RESOURCE)
|
|
125
|
+
.read_text(encoding="utf-8"))
|
|
126
|
+
assert load_guide() == expected
|
|
127
|
+
assert "AE-SBX-016" in load_guide()
|
|
128
|
+
|
|
129
|
+
def test_section_wraps_table(self):
|
|
130
|
+
section = guide_section()
|
|
131
|
+
assert section.startswith(f"## {GUIDE_HEADING}\n\n")
|
|
132
|
+
assert "AE-SBX-016" in section
|
|
133
|
+
|
|
134
|
+
def test_ascii_and_consumer_agnostic(self):
|
|
135
|
+
text = load_guide()
|
|
136
|
+
assert text.isascii()
|
|
137
|
+
assert "consumer" not in text.lower()
|
|
138
|
+
|
|
139
|
+
def test_every_visible_code_has_exactly_one_line(self):
|
|
140
|
+
codes = _guide_codes(load_guide())
|
|
141
|
+
assert Counter(codes) == Counter(AGENT_VISIBLE_CODES)
|
|
142
|
+
|
|
143
|
+
def test_engine_agent_path_codes_pair_with_guide(self):
|
|
144
|
+
visible = _agent_path_error_codes() - INTERNAL_SANDBOX_CODES
|
|
145
|
+
assert visible == AGENT_VISIBLE_CODES
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
class TestInjection:
|
|
149
|
+
def test_agent_context_contains_guide_before_docs(self, runtime):
|
|
150
|
+
ctx = build_context(runtime, {"plan.md": "THE PLAN"}, "plan plan.md")
|
|
151
|
+
assert f"## {GUIDE_HEADING}" in ctx
|
|
152
|
+
assert "AE-SBX-016" in ctx
|
|
153
|
+
assert ctx.index(f"## {GUIDE_HEADING}") < ctx.index("## plan.md")
|
|
154
|
+
|
|
155
|
+
def test_brief_context_contains_guide_before_docs(self, runtime):
|
|
156
|
+
(runtime.workspace / "AGENTS.md").write_text("REPO RULES")
|
|
157
|
+
step = make_cfg([{
|
|
158
|
+
"name": "brief", "agent": "brief", "produces": "attempt-brief.md",
|
|
159
|
+
"brief_inputs": {"include": ["AGENTS.md"],
|
|
160
|
+
"env_facts": ["offline"]},
|
|
161
|
+
"on_pass": "success",
|
|
162
|
+
}]).steps[0]
|
|
163
|
+
ctx = build_brief_context(runtime, step, {"plan.md": "THE PLAN"})
|
|
164
|
+
assert f"## {GUIDE_HEADING}" in ctx
|
|
165
|
+
assert "AE-SBX-016" in ctx
|
|
166
|
+
assert ctx.index(f"## {GUIDE_HEADING}") < ctx.index("## plan.md")
|
|
167
|
+
|
|
168
|
+
def test_agent_node_message_contains_guide(self, runtime, fake_agents):
|
|
169
|
+
fake_agents.set("dev", [("respond", "done")])
|
|
170
|
+
make_agent_node(runtime, runtime.cfg.steps[0])(make_state())
|
|
171
|
+
(message,) = fake_agents.messages_for("dev")
|
|
172
|
+
assert f"## {GUIDE_HEADING}" in message
|
|
173
|
+
assert "AE-SBX-016" in message
|
|
174
|
+
|
|
175
|
+
def test_command_step_notes_do_not_gain_guide(self, runtime):
|
|
176
|
+
cfg = make_cfg([
|
|
177
|
+
{"name": "dev", "agent": "dev", "on_pass": "test"},
|
|
178
|
+
{"name": "test", "command": "false", "on_pass": "success",
|
|
179
|
+
"on_fail": {"goto": "dev", "retry": True}},
|
|
180
|
+
])
|
|
181
|
+
rt = replace(runtime, cfg=cfg)
|
|
182
|
+
update = make_command_node(rt, cfg.steps[1])(make_state())
|
|
183
|
+
assert "notes" in update
|
|
184
|
+
assert all(f"## {GUIDE_HEADING}" not in note for note in update["notes"])
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/src/agentic_dev_loop/core/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/src/agentic_dev_loop/core/results.py
RENAMED
|
File without changes
|
{agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/src/agentic_dev_loop/core/verdict.py
RENAMED
|
File without changes
|
{agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/src/agentic_dev_loop/engine/__init__.py
RENAMED
|
File without changes
|
{agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/src/agentic_dev_loop/engine/actions.py
RENAMED
|
File without changes
|
{agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/src/agentic_dev_loop/engine/agents.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/src/agentic_dev_loop/engine/dryrun.py
RENAMED
|
File without changes
|
{agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/src/agentic_dev_loop/engine/egress_policy.py
RENAMED
|
File without changes
|
{agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/src/agentic_dev_loop/engine/egress_proxy.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/src/agentic_dev_loop/engine/graph.py
RENAMED
|
File without changes
|
{agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/src/agentic_dev_loop/engine/hostboundary.py
RENAMED
|
File without changes
|
{agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/src/agentic_dev_loop/engine/isolation.py
RENAMED
|
File without changes
|
|
File without changes
|
{agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/src/agentic_dev_loop/engine/loaders.py
RENAMED
|
File without changes
|
{agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/src/agentic_dev_loop/engine/mounts.py
RENAMED
|
File without changes
|
{agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/src/agentic_dev_loop/engine/outputs.py
RENAMED
|
File without changes
|
{agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/src/agentic_dev_loop/engine/presets.py
RENAMED
|
File without changes
|
|
File without changes
|
{agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/src/agentic_dev_loop/engine/registry.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/src/agentic_dev_loop/engine/secrets.py
RENAMED
|
File without changes
|
|
File without changes
|
{agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/src/agentic_dev_loop/engine/sync_back.py
RENAMED
|
File without changes
|
|
File without changes
|
{agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/src/agentic_dev_loop/viz/__init__.py
RENAMED
|
File without changes
|
{agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/src/agentic_dev_loop/viz/__main__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/src/agentic_dev_loop.egg-info/requires.txt
RENAMED
|
File without changes
|
{agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/src/agentic_dev_loop.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a31}/tests/test_hostboundary_integration.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|