agentic-dev-loop 1.0.0a18__tar.gz → 1.0.0a20__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.0a18/src/agentic_dev_loop.egg-info → agentic_dev_loop-1.0.0a20}/PKG-INFO +1 -1
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/pyproject.toml +1 -1
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/src/agentic_dev_loop/engine/egress_proxy.py +17 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/src/agentic_dev_loop/engine/isolation.py +15 -14
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/src/agentic_dev_loop/engine/step_workspace.py +1 -1
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/src/agentic_dev_loop/engine/sync_back.py +10 -2
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20/src/agentic_dev_loop.egg-info}/PKG-INFO +1 -1
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/tests/test_egress_proxy.py +77 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/tests/test_hostboundary_integration.py +38 -17
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/tests/test_isolation.py +48 -23
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/tests/test_step_workspace.py +20 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/tests/test_sync_back.py +19 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/LICENSE +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/README.md +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/setup.cfg +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/src/agentic_dev_loop/__init__.py +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/src/agentic_dev_loop/core/__init__.py +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/src/agentic_dev_loop/core/config.py +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/src/agentic_dev_loop/core/results.py +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/src/agentic_dev_loop/core/verdict.py +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/src/agentic_dev_loop/engine/__init__.py +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/src/agentic_dev_loop/engine/actions.py +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/src/agentic_dev_loop/engine/agents.py +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/src/agentic_dev_loop/engine/cli.py +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/src/agentic_dev_loop/engine/deps.py +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/src/agentic_dev_loop/engine/dryrun.py +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/src/agentic_dev_loop/engine/egress_policy.py +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/src/agentic_dev_loop/engine/extensions/__init__.py +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/src/agentic_dev_loop/engine/extensions/aidlc.py +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/src/agentic_dev_loop/engine/graph.py +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/src/agentic_dev_loop/engine/hostboundary.py +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/src/agentic_dev_loop/engine/llm.py +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/src/agentic_dev_loop/engine/loaders.py +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/src/agentic_dev_loop/engine/mounts.py +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/src/agentic_dev_loop/engine/outputs.py +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/src/agentic_dev_loop/engine/presets.py +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/src/agentic_dev_loop/engine/proc.py +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/src/agentic_dev_loop/engine/registry.py +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/src/agentic_dev_loop/engine/seccomp/__init__.py +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/src/agentic_dev_loop/engine/seccomp/chromium-userns.json +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/src/agentic_dev_loop/engine/secrets.py +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/src/agentic_dev_loop/engine/stages.py +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/src/agentic_dev_loop/py.typed +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/src/agentic_dev_loop/viz/__init__.py +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/src/agentic_dev_loop/viz/__main__.py +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/src/agentic_dev_loop/viz/server.py +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/src/agentic_dev_loop.egg-info/SOURCES.txt +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/src/agentic_dev_loop.egg-info/dependency_links.txt +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/src/agentic_dev_loop.egg-info/entry_points.txt +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/src/agentic_dev_loop.egg-info/requires.txt +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/src/agentic_dev_loop.egg-info/top_level.txt +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/tests/test_actions.py +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/tests/test_agents.py +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/tests/test_aidlc.py +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/tests/test_cli.py +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/tests/test_config.py +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/tests/test_deps.py +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/tests/test_dry_run.py +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/tests/test_e2e_fixtures.py +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/tests/test_graph.py +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/tests/test_hostboundary.py +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/tests/test_isolation_integration.py +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/tests/test_isolation_pbt.py +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/tests/test_llm.py +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/tests/test_loaders.py +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/tests/test_module_contract.py +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/tests/test_outputs.py +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/tests/test_outputs_pbt.py +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/tests/test_proc.py +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/tests/test_publish.py +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/tests/test_qa_prompt.py +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/tests/test_registry.py +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/tests/test_repo_ascii.py +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/tests/test_results.py +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/tests/test_sdk_compat.py +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/tests/test_secrets.py +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/tests/test_secrets_integration.py +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/tests/test_secrets_pipeline.py +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/tests/test_source_resolution.py +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/tests/test_stages.py +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/tests/test_trigger_script.py +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/tests/test_unit5_config.py +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/tests/test_unit5_pbt.py +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/tests/test_unit5_pure.py +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/tests/test_verdict.py +0 -0
- {agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/tests/test_viz.py +0 -0
|
@@ -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.0a20"
|
|
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"
|
{agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/src/agentic_dev_loop/engine/egress_proxy.py
RENAMED
|
@@ -131,6 +131,13 @@ class EgressProxy:
|
|
|
131
131
|
_write(writer, b"HTTP/1.1 403 Forbidden\r\nContent-Length: 0\r\n\r\n")
|
|
132
132
|
return
|
|
133
133
|
self._record(host, True)
|
|
134
|
+
# Drain the rest of the CONNECT request (the header block after the
|
|
135
|
+
# request line). Clients send the whole request in one write, so the
|
|
136
|
+
# headers are already buffered here; relaying them to the upstream as
|
|
137
|
+
# if they were the first tunnel bytes corrupts the TLS handshake (the
|
|
138
|
+
# upstream answers with a decode alert - the restore step's SSL
|
|
139
|
+
# TLSV1_ALERT_DECODE_ERROR).
|
|
140
|
+
await _drain_headers(reader)
|
|
134
141
|
conn = await _open(host, port)
|
|
135
142
|
if conn is None:
|
|
136
143
|
_write(writer, b"HTTP/1.1 502 Bad Gateway\r\nContent-Length: 0\r\n\r\n")
|
|
@@ -222,6 +229,16 @@ async def _forward_headers(reader, upstream, host: str) -> None:
|
|
|
222
229
|
await upstream.drain()
|
|
223
230
|
|
|
224
231
|
|
|
232
|
+
async def _drain_headers(reader) -> None:
|
|
233
|
+
"""Consume a request's header block (the lines after the request line,
|
|
234
|
+
terminated by a blank line). The CONNECT path must not forward these to
|
|
235
|
+
the upstream tunnel - only the post-200 bytes belong there."""
|
|
236
|
+
while True:
|
|
237
|
+
line = await reader.readline()
|
|
238
|
+
if not line or line in (b"\r\n", b"\n"):
|
|
239
|
+
break
|
|
240
|
+
|
|
241
|
+
|
|
225
242
|
async def _relay_both(client_reader, client_writer, upstream_reader,
|
|
226
243
|
upstream_writer) -> None:
|
|
227
244
|
await asyncio.gather(
|
{agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/src/agentic_dev_loop/engine/isolation.py
RENAMED
|
@@ -1271,16 +1271,20 @@ def resolve_image_ref(image: str, environ: Mapping[str, str] | None = None) -> s
|
|
|
1271
1271
|
|
|
1272
1272
|
|
|
1273
1273
|
def startup_sandbox(cfg: PipelineConfig, *, workspace: Path,
|
|
1274
|
+
state_dir: str | Path,
|
|
1274
1275
|
runner: CommandRunner | None = None,
|
|
1275
|
-
refresh: bool | None = None
|
|
1276
|
-
state_dir: str | Path | None = None) -> Sandbox:
|
|
1276
|
+
refresh: bool | None = None) -> Sandbox:
|
|
1277
1277
|
"""Eager startup sequence (Q2): probe → image → materialise export →
|
|
1278
1278
|
backend → ensure_image → create+start → pre-provision every declared setup
|
|
1279
1279
|
(stamp-cached).
|
|
1280
1280
|
|
|
1281
1281
|
``state_dir`` (the run state dir) hosts the .git-free export under
|
|
1282
|
-
``exports/<run-id>/``, materialised here (BR-2/BR-3)
|
|
1283
|
-
|
|
1282
|
+
``exports/<run-id>/``, materialised here (BR-2/BR-3). It is required:
|
|
1283
|
+
the runtime always resolves a mount set for steps, and enforcement needs
|
|
1284
|
+
the export (AE-SBX-017), so there is no legacy fallback through this
|
|
1285
|
+
entry point. The legacy workspace model is reachable only through the
|
|
1286
|
+
direct Sandbox API (DockerBackend without an export + requests carrying
|
|
1287
|
+
``mount_set=None``), which is integration-tests-only."""
|
|
1284
1288
|
seccomp_profile = resolved_seccomp_profile(cfg)
|
|
1285
1289
|
probe_docker(runner)
|
|
1286
1290
|
profile = resolve_effective_profile(cfg.isolation)
|
|
@@ -1290,16 +1294,13 @@ def startup_sandbox(cfg: PipelineConfig, *, workspace: Path,
|
|
|
1290
1294
|
sandbox_venv_dir = resolve_sandbox_venv_dir(
|
|
1291
1295
|
cfg.isolation.sandbox_venv_dir if cfg.isolation else None, workspace)
|
|
1292
1296
|
|
|
1293
|
-
export_root
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
state_dir=str(state_dir))
|
|
1301
|
-
export_root = str(ws.export_root)
|
|
1302
|
-
cache_mounts = ws.mount_points()[1:] # skip the export root itself
|
|
1297
|
+
export_root = str(Path(state_dir) / "exports" / uuid.uuid4().hex)
|
|
1298
|
+
cache_paths = discover_cache_paths(workspace, sandbox_venv_dir)
|
|
1299
|
+
ws = StepWorkspace.materialise(
|
|
1300
|
+
workspace, cache_paths, sys.platform, export_root,
|
|
1301
|
+
state_dir=str(state_dir))
|
|
1302
|
+
export_root = str(ws.export_root)
|
|
1303
|
+
cache_mounts = ws.mount_points()[1:] # skip the export root itself
|
|
1303
1304
|
|
|
1304
1305
|
backend = DockerBackend(
|
|
1305
1306
|
image=image, workspace=str(workspace), iso_state_dir=str(iso_state_dir),
|
|
@@ -140,7 +140,7 @@ class StepWorkspace:
|
|
|
140
140
|
src = repo / rel
|
|
141
141
|
dst = self._export_root / rel
|
|
142
142
|
dst.parent.mkdir(parents=True, exist_ok=True)
|
|
143
|
-
shutil.copy2(src, dst)
|
|
143
|
+
shutil.copy2(src, dst, follow_symlinks=False)
|
|
144
144
|
for cache in self._plan.cache_mounts:
|
|
145
145
|
(self._export_root / cache.relative).mkdir(parents=True,
|
|
146
146
|
exist_ok=True)
|
{agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/src/agentic_dev_loop/engine/sync_back.py
RENAMED
|
@@ -105,6 +105,12 @@ def plan_sync_back(export_changes: Mapping[str, str],
|
|
|
105
105
|
|
|
106
106
|
|
|
107
107
|
def _digest(path: Path) -> str:
|
|
108
|
+
if path.is_symlink():
|
|
109
|
+
# Track the link target, never the resolved content: the export can
|
|
110
|
+
# legitimately hold dangling symlinks (host-darwin venv entries,
|
|
111
|
+
# scratch links) and resolving them against the host would crash.
|
|
112
|
+
return "link:" + hashlib.sha256(
|
|
113
|
+
os.readlink(path).encode("utf-8")).hexdigest()
|
|
108
114
|
return hashlib.sha256(path.read_bytes()).hexdigest()
|
|
109
115
|
|
|
110
116
|
|
|
@@ -112,7 +118,9 @@ def _snapshot(root: Path,
|
|
|
112
118
|
cache_paths: list[str] | tuple[str, ...] = ()) -> dict[str, str]:
|
|
113
119
|
"""rel-path -> sha256 for every file under root, excluding ``.git``,
|
|
114
120
|
host-only dirs, and declared caches (BR-3: caches are bind-mounted, not
|
|
115
|
-
copied, so their contents are never part of the export).
|
|
121
|
+
copied, so their contents are never part of the export). Symlinks are
|
|
122
|
+
tracked by their link target (never resolved against the host), so
|
|
123
|
+
dangling links snapshot fine."""
|
|
116
124
|
cache = [_parts(c) for c in cache_paths]
|
|
117
125
|
out: dict[str, str] = {}
|
|
118
126
|
for dirpath, dirnames, filenames in os.walk(root):
|
|
@@ -180,7 +188,7 @@ def sync_back(export_root: str | Path, real_repo: str | Path,
|
|
|
180
188
|
src = export_root / rel
|
|
181
189
|
dst = real_repo / rel
|
|
182
190
|
dst.parent.mkdir(parents=True, exist_ok=True)
|
|
183
|
-
shutil.copy2(src, dst)
|
|
191
|
+
shutil.copy2(src, dst, follow_symlinks=False)
|
|
184
192
|
for rel in plan.to_delete:
|
|
185
193
|
(real_repo / rel).unlink(missing_ok=True)
|
|
186
194
|
|
|
@@ -5,6 +5,7 @@ unmatched hostname is denied and logged as an enforcement event (BR-6)."""
|
|
|
5
5
|
import http.server
|
|
6
6
|
import socket
|
|
7
7
|
import threading
|
|
8
|
+
import time
|
|
8
9
|
|
|
9
10
|
import pytest
|
|
10
11
|
|
|
@@ -126,3 +127,79 @@ def test_connect_port_constraint_matches_exact_port():
|
|
|
126
127
|
p.stop()
|
|
127
128
|
finally:
|
|
128
129
|
server.shutdown()
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
class _RawTcpServer:
|
|
133
|
+
"""A bare TCP listener that records every byte it receives."""
|
|
134
|
+
|
|
135
|
+
def __init__(self):
|
|
136
|
+
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
137
|
+
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|
138
|
+
self.sock.bind(("127.0.0.1", 0))
|
|
139
|
+
self.sock.listen(1)
|
|
140
|
+
self.port = self.sock.getsockname()[1]
|
|
141
|
+
self.received = bytearray()
|
|
142
|
+
self._thread = threading.Thread(target=self._accept, daemon=True)
|
|
143
|
+
self._thread.start()
|
|
144
|
+
|
|
145
|
+
def _accept(self):
|
|
146
|
+
try:
|
|
147
|
+
conn, _ = self.sock.accept()
|
|
148
|
+
except OSError:
|
|
149
|
+
return
|
|
150
|
+
with conn:
|
|
151
|
+
conn.settimeout(5.0)
|
|
152
|
+
while True:
|
|
153
|
+
try:
|
|
154
|
+
chunk = conn.recv(65536)
|
|
155
|
+
except socket.timeout:
|
|
156
|
+
break
|
|
157
|
+
if not chunk:
|
|
158
|
+
break
|
|
159
|
+
self.received.extend(chunk)
|
|
160
|
+
|
|
161
|
+
def close(self):
|
|
162
|
+
try:
|
|
163
|
+
self.sock.close()
|
|
164
|
+
except OSError:
|
|
165
|
+
pass
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def test_connect_headers_drained_not_tunnelled():
|
|
169
|
+
"""The CONNECT request's header block must be consumed by the proxy, not
|
|
170
|
+
relayed into the tunnel. Clients send the request line, headers and the
|
|
171
|
+
first TLS bytes in one write; leaking the headers corrupts the handshake
|
|
172
|
+
(the upstream answers with a TLS decode alert)."""
|
|
173
|
+
upstream = _RawTcpServer()
|
|
174
|
+
try:
|
|
175
|
+
policy = EgressPolicy(targets=(TargetEntry(host="127.0.0.1"),))
|
|
176
|
+
p = EgressProxy(policy=policy)
|
|
177
|
+
p.start()
|
|
178
|
+
try:
|
|
179
|
+
tls_bytes = b"\x16\x03\x01" + b"post-200-bytes"
|
|
180
|
+
payload = (
|
|
181
|
+
f"CONNECT 127.0.0.1:{upstream.port} HTTP/1.1\r\n"
|
|
182
|
+
"Host: 127.0.0.1\r\n"
|
|
183
|
+
"User-Agent: pip\r\n"
|
|
184
|
+
"Proxy-Connection: keep-alive\r\n"
|
|
185
|
+
"\r\n"
|
|
186
|
+
).encode("latin-1") + tls_bytes
|
|
187
|
+
with socket.create_connection(("127.0.0.1", p._port), timeout=5.0) as s:
|
|
188
|
+
s.sendall(payload)
|
|
189
|
+
s.settimeout(5.0)
|
|
190
|
+
buf = b""
|
|
191
|
+
while b"\r\n\r\n" not in buf:
|
|
192
|
+
chunk = s.recv(65536)
|
|
193
|
+
if not chunk:
|
|
194
|
+
break
|
|
195
|
+
buf += chunk
|
|
196
|
+
assert b"200 Connection Established" in buf
|
|
197
|
+
deadline = time.monotonic() + 5.0
|
|
198
|
+
while (time.monotonic() < deadline
|
|
199
|
+
and len(upstream.received) < len(tls_bytes)):
|
|
200
|
+
time.sleep(0.05)
|
|
201
|
+
assert bytes(upstream.received) == tls_bytes
|
|
202
|
+
finally:
|
|
203
|
+
p.stop()
|
|
204
|
+
finally:
|
|
205
|
+
upstream.close()
|
{agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/tests/test_hostboundary_integration.py
RENAMED
|
@@ -5,6 +5,7 @@ never silently skipped in CI."""
|
|
|
5
5
|
from __future__ import annotations
|
|
6
6
|
|
|
7
7
|
import shutil
|
|
8
|
+
from pathlib import Path
|
|
8
9
|
|
|
9
10
|
import pytest
|
|
10
11
|
from openhands.sdk import LLM
|
|
@@ -16,6 +17,7 @@ from agentic_dev_loop.engine.isolation import (
|
|
|
16
17
|
)
|
|
17
18
|
from agentic_dev_loop.engine.registry import Registry
|
|
18
19
|
from agentic_dev_loop.engine.stages import Runtime, make_action_node
|
|
20
|
+
from agentic_dev_loop.engine.step_workspace import StepWorkspace
|
|
19
21
|
|
|
20
22
|
pytestmark = pytest.mark.docker
|
|
21
23
|
|
|
@@ -44,16 +46,24 @@ def backend(tmp_path):
|
|
|
44
46
|
pytest.skip(f"integration image {TEST_IMAGE!r} not present locally")
|
|
45
47
|
ws = tmp_path / "ws"
|
|
46
48
|
ws.mkdir()
|
|
49
|
+
# unit-5: every step resolves a mount set, so enforcement needs the
|
|
50
|
+
# materialised .git-free export (AE-SBX-017 otherwise). Wire the export
|
|
51
|
+
# the way startup_sandbox does (mirror test_isolation_integration's
|
|
52
|
+
# _export_backend).
|
|
53
|
+
export = tmp_path / "state" / "exports" / "r1"
|
|
54
|
+
ws_obj = StepWorkspace.materialise(ws, [], "linux", export)
|
|
47
55
|
profile = resolve_effective_profile(IsolationConfig(image=TEST_IMAGE))
|
|
48
56
|
b = DockerBackend(image=TEST_IMAGE, workspace=str(ws),
|
|
49
|
-
iso_state_dir=str(tmp_path / "state"), profile=profile
|
|
57
|
+
iso_state_dir=str(tmp_path / "state"), profile=profile,
|
|
58
|
+
export_root=str(export),
|
|
59
|
+
cache_mounts=ws_obj.mount_points()[1:])
|
|
50
60
|
b.ensure_image()
|
|
51
61
|
b.create_execution_container()
|
|
52
|
-
yield b, ws
|
|
62
|
+
yield b, ws, export
|
|
53
63
|
b.close()
|
|
54
64
|
|
|
55
65
|
|
|
56
|
-
def _runtime(backend, ws, script, tmp_path):
|
|
66
|
+
def _runtime(backend, ws, export, script, tmp_path):
|
|
57
67
|
reg = Registry()
|
|
58
68
|
reg.add_sandbox_action("probe", script)
|
|
59
69
|
cfg = PipelineConfig.model_validate({
|
|
@@ -61,32 +71,43 @@ def _runtime(backend, ws, script, tmp_path):
|
|
|
61
71
|
"llm": {"model": "openai/test-model", "api_key_env": "K"},
|
|
62
72
|
"agents_dir": "agents",
|
|
63
73
|
"lockfiles": ["uv.lock"],
|
|
64
|
-
|
|
74
|
+
# The workspace is a declared-rw mount (BR-1.2): the consumer can
|
|
75
|
+
# write inside the sandbox, and sync-back (BR-4) carries it to ws.
|
|
76
|
+
"steps": [{"name": "probe", "action": "probe", "on_pass": "success",
|
|
77
|
+
"mounts": [{"path": ".", "mode": "rw"}]}],
|
|
65
78
|
})
|
|
66
79
|
runtime = Runtime(cfg=cfg, registry=reg,
|
|
67
80
|
llm=LLM(model="openai/test-model", api_key="sk-test"),
|
|
68
81
|
agents_dir=tmp_path / "agents", state_dir=tmp_path / "state",
|
|
69
|
-
workspace=ws, sandbox=backend)
|
|
82
|
+
workspace=ws, sandbox=backend, sandbox_cwd=Path(export))
|
|
70
83
|
return runtime, cfg.steps[0]
|
|
71
84
|
|
|
72
85
|
|
|
73
86
|
class TestConsumerActionSentinel:
|
|
74
87
|
def test_action_code_cannot_create_host_file(self, backend, tmp_path):
|
|
75
|
-
b, ws = backend
|
|
88
|
+
b, ws, export = backend
|
|
76
89
|
sentinel = tmp_path / "host-sentinel.txt"
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
90
|
+
# The marker is written first: it proves the consumer code really ran
|
|
91
|
+
# inside the sandbox, so this test can never pass vacuously when the
|
|
92
|
+
# action dies pre-exec. Then the escape attempt.
|
|
93
|
+
script = (f'python -c "open(\\"ran-in-sandbox.txt\\", \\"w\\").write(\\"ran\\"); '
|
|
94
|
+
f'open({str(sentinel)!r}, \\"w\\").write(\\"pwned\\")"')
|
|
95
|
+
runtime, step = _runtime(b, ws, export, script, tmp_path)
|
|
96
|
+
make_action_node(runtime, step)({"subject": "s", "attempt": 1,
|
|
97
|
+
"step_verdicts": {}})
|
|
98
|
+
# The escape attempt targets a path that exists on the host but is not
|
|
99
|
+
# the export mount: the container never sees it, so it fails inside
|
|
100
|
+
# the container (or lands in its own filesystem), never on the host.
|
|
101
|
+
assert (ws / "ran-in-sandbox.txt").read_text() == "ran"
|
|
84
102
|
assert not sentinel.exists()
|
|
85
103
|
|
|
86
104
|
def test_action_can_still_write_inside_workspace(self, backend, tmp_path):
|
|
87
|
-
b, ws = backend
|
|
105
|
+
b, ws, export = backend
|
|
88
106
|
script = 'python -c "open(\\"in-sandbox.txt\\", \\"w\\").write(\\"ok\\")"'
|
|
89
|
-
runtime, step = _runtime(b, ws, script, tmp_path)
|
|
90
|
-
make_action_node(runtime, step)({"subject": "s", "attempt": 1,
|
|
91
|
-
|
|
107
|
+
runtime, step = _runtime(b, ws, export, script, tmp_path)
|
|
108
|
+
update = make_action_node(runtime, step)({"subject": "s", "attempt": 1,
|
|
109
|
+
"step_verdicts": {}})
|
|
110
|
+
# The action genuinely executed (verdict pass) and its declared-rw
|
|
111
|
+
# write synced back to the real workspace (BR-4).
|
|
112
|
+
assert update["step_verdicts"]["probe"] == "pass"
|
|
92
113
|
assert (ws / "in-sandbox.txt").read_text() == "ok"
|
|
@@ -975,6 +975,24 @@ class TestStartupSandbox:
|
|
|
975
975
|
"isolation": {"state_dir": str(tmp_path / "iso-state")},
|
|
976
976
|
})
|
|
977
977
|
|
|
978
|
+
def test_state_dir_required(self, tmp_path, monkeypatch):
|
|
979
|
+
"""The run state_dir is mandatory: the runtime always resolves a
|
|
980
|
+
mount set for steps, so enforcement needs the materialised export
|
|
981
|
+
(AE-SBX-017). Omitting state_dir fails at the call - never silently
|
|
982
|
+
degrades to the legacy workspace model."""
|
|
983
|
+
from agentic_dev_loop.engine.isolation import startup_sandbox
|
|
984
|
+
|
|
985
|
+
ws = tmp_path / "ws"
|
|
986
|
+
ws.mkdir()
|
|
987
|
+
cfg = self._cfg(tmp_path)
|
|
988
|
+
monkeypatch.setattr("agentic_dev_loop.engine.isolation.shutil.which",
|
|
989
|
+
lambda *a: "/usr/bin/docker")
|
|
990
|
+
monkeypatch.setenv("AGENTIC_DEV_LOOP_DEV_IMAGE", "img:tag")
|
|
991
|
+
runner = FakeRunner()
|
|
992
|
+
with pytest.raises(TypeError, match="state_dir"):
|
|
993
|
+
startup_sandbox(cfg, workspace=ws, runner=runner) # type: ignore[call-arg]
|
|
994
|
+
assert runner.calls == []
|
|
995
|
+
|
|
978
996
|
def test_sequence_and_provision(self, tmp_path, monkeypatch):
|
|
979
997
|
from agentic_dev_loop.engine import isolation
|
|
980
998
|
from agentic_dev_loop.engine.isolation import startup_sandbox
|
|
@@ -992,7 +1010,7 @@ class TestStartupSandbox:
|
|
|
992
1010
|
ok(""), # docker run -d --rm (exec container)
|
|
993
1011
|
ok(""), # docker run provision
|
|
994
1012
|
)
|
|
995
|
-
sandbox = startup_sandbox(cfg, workspace=ws, runner=runner)
|
|
1013
|
+
sandbox = startup_sandbox(cfg, workspace=ws, state_dir=tmp_path / "run-state", runner=runner)
|
|
996
1014
|
try:
|
|
997
1015
|
kinds = [c[1] for c in runner.calls]
|
|
998
1016
|
assert kinds == ["version", "info", "image", "run", "run"]
|
|
@@ -1006,9 +1024,10 @@ class TestStartupSandbox:
|
|
|
1006
1024
|
sandbox.close()
|
|
1007
1025
|
|
|
1008
1026
|
def test_network_declaration_starts_second_container(self, tmp_path, monkeypatch):
|
|
1009
|
-
"""014:
|
|
1010
|
-
|
|
1011
|
-
|
|
1027
|
+
"""014/unit-5: startup creates only the base network-none export
|
|
1028
|
+
container. The bridge-network container for a network-declaring step
|
|
1029
|
+
starts lazily in the profile pool on the first networked request
|
|
1030
|
+
(covered by test_isolation_integration), never eagerly at startup."""
|
|
1012
1031
|
from agentic_dev_loop.core.config import PipelineConfig
|
|
1013
1032
|
from agentic_dev_loop.engine.isolation import startup_sandbox
|
|
1014
1033
|
|
|
@@ -1030,15 +1049,13 @@ class TestStartupSandbox:
|
|
|
1030
1049
|
ok("1.54"), # probe version
|
|
1031
1050
|
ok("linux"), # probe ostype
|
|
1032
1051
|
ok(""), # image inspect
|
|
1033
|
-
ok(""), # docker run -d --rm (
|
|
1034
|
-
ok(""), # docker run -d --rm (networked exec)
|
|
1052
|
+
ok(""), # docker run -d --rm (base export container)
|
|
1035
1053
|
)
|
|
1036
|
-
sandbox = startup_sandbox(cfg, workspace=ws, runner=runner)
|
|
1054
|
+
sandbox = startup_sandbox(cfg, workspace=ws, state_dir=tmp_path / "run-state", runner=runner)
|
|
1037
1055
|
try:
|
|
1038
1056
|
run_argv = [c for c in runner.calls if c[1] == "run"]
|
|
1039
|
-
assert len(run_argv) ==
|
|
1057
|
+
assert len(run_argv) == 1
|
|
1040
1058
|
assert "--network" in run_argv[0] and "none" in run_argv[0]
|
|
1041
|
-
assert "--network" in run_argv[1] and "bridge" in run_argv[1]
|
|
1042
1059
|
finally:
|
|
1043
1060
|
sandbox.close()
|
|
1044
1061
|
|
|
@@ -1059,20 +1076,24 @@ class TestStartupSandbox:
|
|
|
1059
1076
|
|
|
1060
1077
|
# Without refresh the stamp is a hit: no provision container runs.
|
|
1061
1078
|
runner = FakeRunner(ok("1.54"), ok("linux"), ok(""), ok(""))
|
|
1062
|
-
sandbox = startup_sandbox(cfg, workspace=ws, runner=runner)
|
|
1079
|
+
sandbox = startup_sandbox(cfg, workspace=ws, state_dir=tmp_path / "run-state", runner=runner)
|
|
1063
1080
|
sandbox.close()
|
|
1064
1081
|
assert not [c for c in runner.calls if "python -m venv .venv-sandbox" in c]
|
|
1065
1082
|
|
|
1066
1083
|
# With refresh the stamp is ignored: provision re-runs.
|
|
1067
1084
|
runner2 = FakeRunner(ok("1.54"), ok("linux"), ok(""), ok(""), ok(""))
|
|
1068
|
-
sandbox2 = startup_sandbox(cfg, workspace=ws,
|
|
1085
|
+
sandbox2 = startup_sandbox(cfg, workspace=ws,
|
|
1086
|
+
state_dir=tmp_path / "run-state",
|
|
1087
|
+
runner=runner2, refresh=True)
|
|
1069
1088
|
sandbox2.close()
|
|
1070
1089
|
assert [c for c in runner2.calls if "python -m venv .venv-sandbox" in c]
|
|
1071
1090
|
|
|
1072
1091
|
def test_relocated_venv_dir_flows_into_payloads(self, tmp_path, monkeypatch):
|
|
1073
1092
|
"""022: a set `sandbox_venv_dir` is resolved and threaded into BOTH the
|
|
1074
|
-
exec and provision payloads
|
|
1075
|
-
|
|
1093
|
+
exec and provision payloads. The exec (base export) container maps it
|
|
1094
|
+
into the export at `.venv-sandbox`; the provision container maps it
|
|
1095
|
+
into the real workspace path. The workspace stays free of
|
|
1096
|
+
`.venv-sandbox` noise either way."""
|
|
1076
1097
|
from agentic_dev_loop.engine.isolation import startup_sandbox
|
|
1077
1098
|
|
|
1078
1099
|
ws = tmp_path / "ws"
|
|
@@ -1090,14 +1111,18 @@ class TestStartupSandbox:
|
|
|
1090
1111
|
ok(""), # docker run -d --rm (exec container)
|
|
1091
1112
|
ok(""), # docker run provision
|
|
1092
1113
|
)
|
|
1093
|
-
sandbox = startup_sandbox(cfg, workspace=ws, runner=runner)
|
|
1114
|
+
sandbox = startup_sandbox(cfg, workspace=ws, state_dir=tmp_path / "run-state", runner=runner)
|
|
1094
1115
|
try:
|
|
1095
|
-
|
|
1096
|
-
for
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1116
|
+
root = sandbox.export_root
|
|
1117
|
+
exec_argv = [c for c in runner.calls
|
|
1118
|
+
if c[:2] == ["docker", "run"] and "--detach" in c][0]
|
|
1119
|
+
prov_argv = [c for c in runner.calls
|
|
1120
|
+
if c[:2] == ["docker", "run"] and "--detach" not in c][0]
|
|
1121
|
+
exec_target = f"{venv_dir}:{root}/.venv-sandbox"
|
|
1122
|
+
prov_target = f"{venv_dir}:{ws}/.venv-sandbox"
|
|
1123
|
+
assert exec_argv[exec_argv.index(exec_target) - 1] == "-v"
|
|
1124
|
+
assert prov_argv[prov_argv.index(prov_target) - 1] == "-v"
|
|
1125
|
+
assert f"{ws}/.venv:ro" in prov_argv
|
|
1101
1126
|
finally:
|
|
1102
1127
|
sandbox.close()
|
|
1103
1128
|
|
|
@@ -1121,7 +1146,7 @@ class TestStartupSandbox:
|
|
|
1121
1146
|
ok(""), # image inspect
|
|
1122
1147
|
ok(""), # docker run -d --rm (exec container)
|
|
1123
1148
|
)
|
|
1124
|
-
sandbox = startup_sandbox(cfg, workspace=ws, runner=runner)
|
|
1149
|
+
sandbox = startup_sandbox(cfg, workspace=ws, state_dir=tmp_path / "run-state", runner=runner)
|
|
1125
1150
|
try:
|
|
1126
1151
|
run_argv = [c for c in runner.calls if c[:2] == ["docker", "run"]][0]
|
|
1127
1152
|
resolved = chromium_userns_profile_path()
|
|
@@ -1146,7 +1171,7 @@ class TestStartupSandbox:
|
|
|
1146
1171
|
monkeypatch.setenv("AGENTIC_DEV_LOOP_DEV_IMAGE", "img:tag")
|
|
1147
1172
|
runner = FakeRunner()
|
|
1148
1173
|
with pytest.raises(ValueError, match="bundled:chromium-userns"):
|
|
1149
|
-
startup_sandbox(cfg, workspace=ws, runner=runner)
|
|
1174
|
+
startup_sandbox(cfg, workspace=ws, state_dir=tmp_path / "run-state", runner=runner)
|
|
1150
1175
|
assert runner.calls == []
|
|
1151
1176
|
|
|
1152
1177
|
def test_in_workspace_sandbox_venv_dir_fails_loud(self, tmp_path, monkeypatch):
|
|
@@ -1161,7 +1186,7 @@ class TestStartupSandbox:
|
|
|
1161
1186
|
monkeypatch.setenv("AGENTIC_DEV_LOOP_DEV_IMAGE", "img:tag")
|
|
1162
1187
|
runner = FakeRunner(ok("1.54"), ok("linux"))
|
|
1163
1188
|
with pytest.raises(SandboxError, match="AE-SBX-015"):
|
|
1164
|
-
startup_sandbox(cfg, workspace=ws, runner=runner)
|
|
1189
|
+
startup_sandbox(cfg, workspace=ws, state_dir=tmp_path / "run-state", runner=runner)
|
|
1165
1190
|
|
|
1166
1191
|
|
|
1167
1192
|
class TestBundledSeccompProfile:
|
|
@@ -32,6 +32,26 @@ def test_materialise_copies_except_git_and_host_only(tmp_path):
|
|
|
32
32
|
assert not (export / ".venv").exists()
|
|
33
33
|
|
|
34
34
|
|
|
35
|
+
def test_materialise_preserves_symlinks_without_resolving(tmp_path):
|
|
36
|
+
repo = _repo(tmp_path)
|
|
37
|
+
(repo / "links").mkdir()
|
|
38
|
+
(repo / "links" / "dangling").symlink_to("nowhere")
|
|
39
|
+
(repo / "links" / "valid").symlink_to("../src/a.py")
|
|
40
|
+
export = tmp_path / "state" / "exports" / "run1"
|
|
41
|
+
# Completing materialise also proves the baseline snapshot survives the
|
|
42
|
+
# dangling link (it runs _snapshot on the export after the copy).
|
|
43
|
+
StepWorkspace.materialise(repo, [], "linux", export)
|
|
44
|
+
dangling = export / "links" / "dangling"
|
|
45
|
+
valid = export / "links" / "valid"
|
|
46
|
+
# Symlinks copy as symlinks, never resolved against the host: the
|
|
47
|
+
# dangling one stays dangling, the valid one keeps its relative target.
|
|
48
|
+
assert dangling.is_symlink()
|
|
49
|
+
assert os.readlink(dangling) == "nowhere"
|
|
50
|
+
assert not dangling.exists()
|
|
51
|
+
assert valid.is_symlink()
|
|
52
|
+
assert os.readlink(valid) == "../src/a.py"
|
|
53
|
+
|
|
54
|
+
|
|
35
55
|
def test_missing_cache_is_loud(tmp_path):
|
|
36
56
|
repo = _repo(tmp_path)
|
|
37
57
|
export = tmp_path / "state" / "exports" / "run1"
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
only, .git never touched, conflicts loud."""
|
|
3
3
|
|
|
4
4
|
import hashlib
|
|
5
|
+
import os
|
|
5
6
|
|
|
6
7
|
import pytest
|
|
7
8
|
|
|
@@ -100,6 +101,24 @@ def test_whole_tree_dot_syncs_back(tmp_path):
|
|
|
100
101
|
assert (ws / "docs" / "new.md").read_text() == "new"
|
|
101
102
|
|
|
102
103
|
|
|
104
|
+
def test_symlink_syncs_as_symlink_never_resolved(tmp_path):
|
|
105
|
+
"""A symlink the sandbox creates in a declared-rw path syncs back as a
|
|
106
|
+
symlink, including a dangling one (whose target only makes sense inside
|
|
107
|
+
the export). Resolving it against the host would crash or corrupt."""
|
|
108
|
+
ws = _workspace(tmp_path)
|
|
109
|
+
exp = _export(tmp_path, ws)
|
|
110
|
+
(exp / "links").mkdir()
|
|
111
|
+
(exp / "links" / "to-src").symlink_to("../src/a.py")
|
|
112
|
+
(exp / "links" / "dangling").symlink_to("no-such-target")
|
|
113
|
+
report = sync_back(exp, ws, declared_rw=["."])
|
|
114
|
+
assert report.conflicts == ()
|
|
115
|
+
assert set(report.synced_paths) == {"links/to-src", "links/dangling"}
|
|
116
|
+
assert (ws / "links" / "to-src").is_symlink()
|
|
117
|
+
assert os.readlink(ws / "links" / "to-src") == "../src/a.py"
|
|
118
|
+
assert (ws / "links" / "dangling").is_symlink()
|
|
119
|
+
assert os.readlink(ws / "links" / "dangling") == "no-such-target"
|
|
120
|
+
|
|
121
|
+
|
|
103
122
|
def test_cache_not_deleted(tmp_path):
|
|
104
123
|
"""BR-3/F2: a bind-mounted cache is absent from the export but present in
|
|
105
124
|
the real repo - a ``.``-rw sync-back must never classify it as a deletion
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/src/agentic_dev_loop/core/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/src/agentic_dev_loop/core/results.py
RENAMED
|
File without changes
|
{agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/src/agentic_dev_loop/core/verdict.py
RENAMED
|
File without changes
|
{agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/src/agentic_dev_loop/engine/__init__.py
RENAMED
|
File without changes
|
{agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/src/agentic_dev_loop/engine/actions.py
RENAMED
|
File without changes
|
{agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/src/agentic_dev_loop/engine/agents.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/src/agentic_dev_loop/engine/dryrun.py
RENAMED
|
File without changes
|
{agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/src/agentic_dev_loop/engine/egress_policy.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/src/agentic_dev_loop/engine/graph.py
RENAMED
|
File without changes
|
{agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/src/agentic_dev_loop/engine/hostboundary.py
RENAMED
|
File without changes
|
|
File without changes
|
{agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/src/agentic_dev_loop/engine/loaders.py
RENAMED
|
File without changes
|
{agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/src/agentic_dev_loop/engine/mounts.py
RENAMED
|
File without changes
|
{agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/src/agentic_dev_loop/engine/outputs.py
RENAMED
|
File without changes
|
{agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/src/agentic_dev_loop/engine/presets.py
RENAMED
|
File without changes
|
|
File without changes
|
{agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/src/agentic_dev_loop/engine/registry.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/src/agentic_dev_loop/engine/secrets.py
RENAMED
|
File without changes
|
{agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/src/agentic_dev_loop/engine/stages.py
RENAMED
|
File without changes
|
|
File without changes
|
{agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/src/agentic_dev_loop/viz/__init__.py
RENAMED
|
File without changes
|
{agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/src/agentic_dev_loop/viz/__main__.py
RENAMED
|
File without changes
|
|
File without changes
|
{agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/src/agentic_dev_loop.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/src/agentic_dev_loop.egg-info/requires.txt
RENAMED
|
File without changes
|
{agentic_dev_loop-1.0.0a18 → agentic_dev_loop-1.0.0a20}/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
|
|
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
|