agentic-dev-loop 1.0.0a46__tar.gz → 1.0.0a48__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.0a46/src/agentic_dev_loop.egg-info → agentic_dev_loop-1.0.0a48}/PKG-INFO +6 -5
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/README.md +5 -4
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/pyproject.toml +1 -1
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/engine/isolation.py +66 -21
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48/src/agentic_dev_loop.egg-info}/PKG-INFO +6 -5
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/tests/test_isolation.py +80 -2
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/tests/test_isolation_integration.py +79 -1
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/LICENSE +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/setup.cfg +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/__init__.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/core/__init__.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/core/config.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/core/paths.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/core/results.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/core/verdict.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/engine/__init__.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/engine/actions.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/engine/agent_error_guide.md +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/engine/agent_error_guide.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/engine/agents.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/engine/allowlist.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/engine/brief_coverage.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/engine/brief_prompt.md +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/engine/brief_prompt.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/engine/cli.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/engine/containers.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/engine/deps.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/engine/dryrun.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/engine/egress_policy.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/engine/egress_proxy.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/engine/extensions/__init__.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/engine/extensions/aidlc.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/engine/forbidden.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/engine/graph.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/engine/hostboundary.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/engine/llm.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/engine/loaders.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/engine/mounts.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/engine/outputs.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/engine/presets.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/engine/proc.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/engine/registry.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/engine/seccomp/__init__.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/engine/seccomp/chromium-userns.json +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/engine/secrets.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/engine/stages.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/engine/step_workspace.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/engine/sync_back.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/py.typed +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/viz/__init__.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/viz/__main__.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/viz/server.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop.egg-info/SOURCES.txt +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop.egg-info/dependency_links.txt +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop.egg-info/entry_points.txt +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop.egg-info/requires.txt +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop.egg-info/top_level.txt +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/tests/test_028_declared_caches.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/tests/test_029_merge_on_conflict.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/tests/test_033_brief_step.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/tests/test_035_finalize_nudge.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/tests/test_036_banned_commands_and_lane_boundary.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/tests/test_041_brief_coverage.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/tests/test_046_brief_prompt.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/tests/test_actions.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/tests/test_agent_error_guide.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/tests/test_agents.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/tests/test_aidlc.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/tests/test_cli.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/tests/test_config.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/tests/test_containers.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/tests/test_deps.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/tests/test_dry_run.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/tests/test_e2e_fixtures.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/tests/test_egress_proxy.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/tests/test_graph.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/tests/test_hostboundary.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/tests/test_hostboundary_integration.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/tests/test_isolation_pbt.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/tests/test_llm.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/tests/test_loaders.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/tests/test_module_contract.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/tests/test_outputs.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/tests/test_outputs_pbt.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/tests/test_proc.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/tests/test_publish.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/tests/test_qa_prompt.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/tests/test_registry.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/tests/test_repo_ascii.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/tests/test_results.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/tests/test_sdk_compat.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/tests/test_secrets.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/tests/test_secrets_integration.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/tests/test_secrets_pipeline.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/tests/test_source_resolution.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/tests/test_stages.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/tests/test_step_workspace.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/tests/test_sync_back.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/tests/test_trigger_script.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/tests/test_unit5_config.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/tests/test_unit5_pbt.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/tests/test_unit5_pure.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/tests/test_verdict.py +0 -0
- {agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/tests/test_viz.py +0 -0
{agentic_dev_loop-1.0.0a46/src/agentic_dev_loop.egg-info → agentic_dev_loop-1.0.0a48}/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.0a48
|
|
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
|
|
@@ -384,10 +384,11 @@ steps:
|
|
|
384
384
|
structurally enforced.** A networked step runs on a per-run internal
|
|
385
385
|
docker network with no route to the host. Its traffic is forced
|
|
386
386
|
through a containerized egress proxy (injected via
|
|
387
|
-
`HTTP_PROXY`/`HTTPS_PROXY`, pointed at the proxy
|
|
388
|
-
on the internal network
|
|
389
|
-
|
|
390
|
-
|
|
387
|
+
`HTTP_PROXY`/`HTTPS_PROXY`, pointed at the proxy's internal IP
|
|
388
|
+
on the internal network; each distinct egress policy gets its own
|
|
389
|
+
proxy with a distinct internal IP); a request to an undeclared host
|
|
390
|
+
is denied and logged as an enforcement event. The engine never adds
|
|
391
|
+
an undeclared target. `egress: { class: browser }` is the explicit
|
|
391
392
|
broad-but-logged class (every connection logged, nothing filtered); command
|
|
392
393
|
and agent steps default to a strict allowlist.
|
|
393
394
|
- **Command, agent, and sandbox-action steps** may declare it. The host-side
|
|
@@ -340,10 +340,11 @@ steps:
|
|
|
340
340
|
structurally enforced.** A networked step runs on a per-run internal
|
|
341
341
|
docker network with no route to the host. Its traffic is forced
|
|
342
342
|
through a containerized egress proxy (injected via
|
|
343
|
-
`HTTP_PROXY`/`HTTPS_PROXY`, pointed at the proxy
|
|
344
|
-
on the internal network
|
|
345
|
-
|
|
346
|
-
|
|
343
|
+
`HTTP_PROXY`/`HTTPS_PROXY`, pointed at the proxy's internal IP
|
|
344
|
+
on the internal network; each distinct egress policy gets its own
|
|
345
|
+
proxy with a distinct internal IP); a request to an undeclared host
|
|
346
|
+
is denied and logged as an enforcement event. The engine never adds
|
|
347
|
+
an undeclared target. `egress: { class: browser }` is the explicit
|
|
347
348
|
broad-but-logged class (every connection logged, nothing filtered); command
|
|
348
349
|
and agent steps default to a strict allowlist.
|
|
349
350
|
- **Command, agent, and sandbox-action steps** may declare it. The host-side
|
|
@@ -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.0a48"
|
|
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.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/engine/isolation.py
RENAMED
|
@@ -31,7 +31,7 @@ import sys
|
|
|
31
31
|
import time
|
|
32
32
|
import uuid
|
|
33
33
|
from abc import ABC, abstractmethod
|
|
34
|
-
from collections.abc import Mapping
|
|
34
|
+
from collections.abc import Iterable, Mapping
|
|
35
35
|
from dataclasses import dataclass
|
|
36
36
|
from datetime import datetime, timezone
|
|
37
37
|
from pathlib import Path, PurePosixPath
|
|
@@ -299,10 +299,10 @@ def proxy_container_name(run_id: str, profile_key: str) -> str:
|
|
|
299
299
|
return f"{run_id}-proxy-{profile_key}"
|
|
300
300
|
|
|
301
301
|
|
|
302
|
-
def proxy_url(
|
|
303
|
-
"""The sandbox-reachable proxy URL: the proxy
|
|
304
|
-
|
|
305
|
-
return f"http://{
|
|
302
|
+
def proxy_url(proxy_host: str, port: int = EGRESS_PROXY_PORT) -> str:
|
|
303
|
+
"""The sandbox-reachable proxy URL: the proxy's internal address on the
|
|
304
|
+
internal network (038, D3). Never 127.0.0.1 or the host-gateway."""
|
|
305
|
+
return f"http://{proxy_host}:{port}"
|
|
306
306
|
|
|
307
307
|
|
|
308
308
|
def egress_events_path(iso_state_dir: str | Path, run_id: str) -> Path:
|
|
@@ -310,8 +310,10 @@ def egress_events_path(iso_state_dir: str | Path, run_id: str) -> Path:
|
|
|
310
310
|
return Path(iso_state_dir) / f"{EGRESS_EVENTS_PREFIX}-{run_id}.jsonl"
|
|
311
311
|
|
|
312
312
|
|
|
313
|
-
def
|
|
314
|
-
"""
|
|
313
|
+
def proxy_usable_ips(subnets: str) -> list[str]:
|
|
314
|
+
"""Usable non-gateway IPv4 host addresses from the first viable subnet
|
|
315
|
+
(048 D1): ``hosts[1:]``, skipping the gateway at ``hosts[0]``. Empty when
|
|
316
|
+
no subnet offers a non-gateway host."""
|
|
315
317
|
for token in subnets.split():
|
|
316
318
|
try:
|
|
317
319
|
network = ipaddress.ip_network(token, strict=False)
|
|
@@ -322,8 +324,8 @@ def first_usable_ip(subnets: str) -> str | None:
|
|
|
322
324
|
hosts = list(network.hosts())
|
|
323
325
|
if len(hosts) < 2:
|
|
324
326
|
continue
|
|
325
|
-
return str(hosts[1]
|
|
326
|
-
return
|
|
327
|
+
return [str(host) for host in hosts[1:]]
|
|
328
|
+
return []
|
|
327
329
|
|
|
328
330
|
|
|
329
331
|
|
|
@@ -964,6 +966,7 @@ class DockerBackend(Sandbox):
|
|
|
964
966
|
self._run_net = run_network_name(self._run_id)
|
|
965
967
|
self._network_created = False
|
|
966
968
|
self._proxy_containers: list[str] = []
|
|
969
|
+
self._proxy_ips: dict[str, str] = {}
|
|
967
970
|
self._egress_events_path = egress_events_path(self._iso_state_dir,
|
|
968
971
|
self._run_id)
|
|
969
972
|
self._sandbox_venv_dir = sandbox_venv_dir # 022 relocated venv (opt-in)
|
|
@@ -1309,9 +1312,9 @@ class DockerBackend(Sandbox):
|
|
|
1309
1312
|
proxy_url_value: str | None = None
|
|
1310
1313
|
if network and self._export_root is not None:
|
|
1311
1314
|
proxy_name = proxy_container_name(self._run_id, key[0][:16])
|
|
1312
|
-
self._start_proxy_container(proxy_name, policy,
|
|
1313
|
-
|
|
1314
|
-
proxy_url_value = proxy_url(
|
|
1315
|
+
proxy_ip = self._start_proxy_container(proxy_name, policy,
|
|
1316
|
+
self._dial_rewrites)
|
|
1317
|
+
proxy_url_value = proxy_url(proxy_ip)
|
|
1315
1318
|
payload = docker_create_payload(
|
|
1316
1319
|
run_id=self._run_id, image=self._image, profile=self._profile,
|
|
1317
1320
|
workspace=self._workspace, network=network,
|
|
@@ -1334,7 +1337,7 @@ class DockerBackend(Sandbox):
|
|
|
1334
1337
|
started=True)
|
|
1335
1338
|
|
|
1336
1339
|
def _start_proxy_container(self, name: str, policy: EgressPolicy,
|
|
1337
|
-
dial_rewrites: dict[str, str] | None = None) ->
|
|
1340
|
+
dial_rewrites: dict[str, str] | None = None) -> str:
|
|
1338
1341
|
"""Create one containerized proxy (038 D2/D5, 039 D1-D4): host-access
|
|
1339
1342
|
network at create time with a pinned internal IP, then attach the internal
|
|
1340
1343
|
network; the proxy binds that address only and is readiness probed from
|
|
@@ -1350,7 +1353,7 @@ class DockerBackend(Sandbox):
|
|
|
1350
1353
|
policy_json = egress_proxy_module.policy_to_json(policy)
|
|
1351
1354
|
dial_rewrites = dict(dial_rewrites or {})
|
|
1352
1355
|
dial_rewrites_json = json.dumps(dial_rewrites, sort_keys=True)
|
|
1353
|
-
bind_host = self._proxy_internal_ip()
|
|
1356
|
+
bind_host = self._proxy_internal_ip(name)
|
|
1354
1357
|
payload = docker_proxy_payload(
|
|
1355
1358
|
run_id=self._run_id, image=self._image, name=name,
|
|
1356
1359
|
policy_json=policy_json, dial_rewrites_json=dial_rewrites_json,
|
|
@@ -1379,22 +1382,63 @@ class DockerBackend(Sandbox):
|
|
|
1379
1382
|
f"{bind_host}:{EGRESS_PROXY_PORT} after "
|
|
1380
1383
|
f"{PROXY_READY_ATTEMPTS} readiness probes")
|
|
1381
1384
|
self._proxy_containers.append(name)
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
+
return bind_host
|
|
1386
|
+
|
|
1387
|
+
def _proxy_internal_ip(self, proxy_name: str) -> str:
|
|
1388
|
+
"""Return the per-proxy pinned IPv4 address on the internal network
|
|
1389
|
+
(048 D1): inspect the subnet and the addresses containers already hold
|
|
1390
|
+
on it, then allocate the next free host (profile containers join the
|
|
1391
|
+
run network with a daemon auto-assigned IP, so those must be skipped
|
|
1392
|
+
or the later ``network connect --ip`` collides)."""
|
|
1385
1393
|
inspect = self._runner.run(
|
|
1386
1394
|
["docker", "network", "inspect", "-f",
|
|
1387
|
-
"{{range .IPAM.Config}}{{.Subnet}} {{end}}"
|
|
1395
|
+
"{{range .IPAM.Config}}{{.Subnet}} {{end}}|"
|
|
1396
|
+
"{{range .Containers}}{{.IPv4Address}} {{end}}",
|
|
1397
|
+
self._run_net],
|
|
1388
1398
|
timeout=30)
|
|
1389
1399
|
if inspect.exit_code != 0:
|
|
1390
1400
|
raise SandboxError(
|
|
1391
1401
|
f"AE-SBX-021: internal network inspect failed: "
|
|
1392
1402
|
f"{inspect.stderr.strip() or inspect.stdout.strip()}")
|
|
1393
|
-
|
|
1394
|
-
if
|
|
1403
|
+
subnets, sep, in_use_part = inspect.stdout.partition("|")
|
|
1404
|
+
in_use = in_use_part.split() if sep else ()
|
|
1405
|
+
return self._allocate_proxy_ip(subnets, proxy_name, in_use)
|
|
1406
|
+
|
|
1407
|
+
def _allocate_proxy_ip(self, subnets: str, proxy_name: str,
|
|
1408
|
+
in_use: Iterable[str] = ()) -> str:
|
|
1409
|
+
"""Allocate one internal IPv4 address per proxy (048 D1/D2). An
|
|
1410
|
+
existing allocation for ``proxy_name`` is reused; otherwise the first
|
|
1411
|
+
free non-gateway host wins (the first proxy keeps ``hosts[1]``).
|
|
1412
|
+
Addresses in ``in_use`` (held by other containers on the network) are
|
|
1413
|
+
skipped alongside the already-allocated proxy IPs. Raises when the
|
|
1414
|
+
subnet has no free host instead of letting the daemon emit a
|
|
1415
|
+
confusing collision error."""
|
|
1416
|
+
existing = self._proxy_ips.get(proxy_name)
|
|
1417
|
+
if existing is not None:
|
|
1418
|
+
return existing
|
|
1419
|
+
hosts = proxy_usable_ips(subnets)
|
|
1420
|
+
if not hosts:
|
|
1395
1421
|
raise SandboxError(
|
|
1396
1422
|
f"AE-SBX-021: internal network has no usable IPv4 subnet: "
|
|
1397
|
-
f"{
|
|
1423
|
+
f"{subnets.strip()!r}")
|
|
1424
|
+
taken = set(self._proxy_ips.values())
|
|
1425
|
+
for token in in_use:
|
|
1426
|
+
addr = token.split("/", 1)[0].strip()
|
|
1427
|
+
try:
|
|
1428
|
+
ip = ipaddress.ip_address(addr)
|
|
1429
|
+
except ValueError:
|
|
1430
|
+
continue
|
|
1431
|
+
if isinstance(ip, ipaddress.IPv4Address):
|
|
1432
|
+
taken.add(str(ip))
|
|
1433
|
+
free = [host for host in hosts if host not in taken]
|
|
1434
|
+
if not free:
|
|
1435
|
+
raise SandboxError(
|
|
1436
|
+
f"AE-SBX-021: internal network {subnets.strip()!r} has "
|
|
1437
|
+
f"{len(hosts)} usable proxy address(es) but "
|
|
1438
|
+
f"{len(self._proxy_ips) + 1} egress proxies were requested - "
|
|
1439
|
+
f"no free address to assign")
|
|
1440
|
+
ip = free[0]
|
|
1441
|
+
self._proxy_ips[proxy_name] = ip
|
|
1398
1442
|
return ip
|
|
1399
1443
|
|
|
1400
1444
|
def _wait_proxy_ready(self, name: str, host: str) -> bool:
|
|
@@ -1541,6 +1585,7 @@ class DockerBackend(Sandbox):
|
|
|
1541
1585
|
for name in self._proxy_containers:
|
|
1542
1586
|
self._rm_container(name)
|
|
1543
1587
|
self._proxy_containers.clear()
|
|
1588
|
+
self._proxy_ips.clear()
|
|
1544
1589
|
if self._network_created:
|
|
1545
1590
|
self._runner.run(["docker", "network", "rm", self._run_net], timeout=60)
|
|
1546
1591
|
self._network_created = False
|
{agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48/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.0a48
|
|
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
|
|
@@ -384,10 +384,11 @@ steps:
|
|
|
384
384
|
structurally enforced.** A networked step runs on a per-run internal
|
|
385
385
|
docker network with no route to the host. Its traffic is forced
|
|
386
386
|
through a containerized egress proxy (injected via
|
|
387
|
-
`HTTP_PROXY`/`HTTPS_PROXY`, pointed at the proxy
|
|
388
|
-
on the internal network
|
|
389
|
-
|
|
390
|
-
|
|
387
|
+
`HTTP_PROXY`/`HTTPS_PROXY`, pointed at the proxy's internal IP
|
|
388
|
+
on the internal network; each distinct egress policy gets its own
|
|
389
|
+
proxy with a distinct internal IP); a request to an undeclared host
|
|
390
|
+
is denied and logged as an enforcement event. The engine never adds
|
|
391
|
+
an undeclared target. `egress: { class: browser }` is the explicit
|
|
391
392
|
broad-but-logged class (every connection logged, nothing filtered); command
|
|
392
393
|
and agent steps default to a strict allowlist.
|
|
393
394
|
- **Command, agent, and sandbox-action steps** may declare it. The host-side
|
|
@@ -1581,7 +1581,7 @@ class TestUnit5RuntimeEnforcement:
|
|
|
1581
1581
|
|
|
1582
1582
|
def test_profile_container_mounts_and_proxy_env(self, tmp_path):
|
|
1583
1583
|
"""038: a step's mount set + egress policy key a pooled container;
|
|
1584
|
-
the proxy env points at the proxy container's internal
|
|
1584
|
+
the proxy env points at the proxy container's internal IP
|
|
1585
1585
|
(never 127.0.0.1 or the host-gateway), the proxy binds a pinned
|
|
1586
1586
|
internal IP after a readiness probe, and the exec cwd is the export
|
|
1587
1587
|
root."""
|
|
@@ -1633,7 +1633,7 @@ class TestUnit5RuntimeEnforcement:
|
|
|
1633
1633
|
if a.startswith("HTTP_PROXY=")
|
|
1634
1634
|
or a.startswith("HTTPS_PROXY=")]
|
|
1635
1635
|
assert proxy_envs and all(
|
|
1636
|
-
|
|
1636
|
+
"http://172.18.0.2:3128" in a for a in proxy_envs)
|
|
1637
1637
|
assert run_argv[run_argv.index("--workdir") + 1] == str(export)
|
|
1638
1638
|
inspect = runner.calls[6]
|
|
1639
1639
|
assert inspect == ["docker", "inspect", "-f",
|
|
@@ -2434,6 +2434,84 @@ class TestNetworkEgressEnforcement:
|
|
|
2434
2434
|
|
|
2435
2435
|
|
|
2436
2436
|
|
|
2437
|
+
class TestProxyIpAllocation:
|
|
2438
|
+
"""048: per-proxy internal IP allocation - distinct ``--ip`` per proxy,
|
|
2439
|
+
the first proxy keeps ``hosts[1]``, subnet exhaustion fails fast, and a
|
|
2440
|
+
failed attach surfaces the daemon stderr."""
|
|
2441
|
+
|
|
2442
|
+
def test_two_proxies_get_distinct_ips(self, tmp_path):
|
|
2443
|
+
runner = FakeRunner(
|
|
2444
|
+
ok(""), ok("172.18.0.0/16 "), ok("proxy"), ok(""), ok(""),
|
|
2445
|
+
ok("172.18.0.0/16 "), ok("proxy"), ok(""), ok(""))
|
|
2446
|
+
backend = _backend(tmp_path, runner, network=True)
|
|
2447
|
+
backend._start_proxy_container("r-proxy-aaa", EgressPolicy())
|
|
2448
|
+
backend._start_proxy_container("r-proxy-bbb", EgressPolicy())
|
|
2449
|
+
connects = [c for c in backend._runner.calls if "--ip" in c]
|
|
2450
|
+
assert [c[c.index("--ip") + 1] for c in connects] == \
|
|
2451
|
+
["172.18.0.2", "172.18.0.3"]
|
|
2452
|
+
runs = [c for c in backend._runner.calls if "--host" in c]
|
|
2453
|
+
assert [c[c.index("--host") + 1] for c in runs] == \
|
|
2454
|
+
["172.18.0.2", "172.18.0.3"]
|
|
2455
|
+
|
|
2456
|
+
def test_allocation_reused_for_same_name(self, tmp_path):
|
|
2457
|
+
backend = _backend(tmp_path, FakeRunner())
|
|
2458
|
+
first = backend._allocate_proxy_ip("172.18.0.0/16 ", "p1")
|
|
2459
|
+
again = backend._allocate_proxy_ip("172.18.0.0/16 ", "p1")
|
|
2460
|
+
assert first == "172.18.0.2"
|
|
2461
|
+
assert again == first
|
|
2462
|
+
|
|
2463
|
+
def test_exhaustion_raises_naming_subnet_and_count(self, tmp_path):
|
|
2464
|
+
backend = _backend(tmp_path, FakeRunner())
|
|
2465
|
+
backend._allocate_proxy_ip("172.18.0.0/30 ", "p1")
|
|
2466
|
+
with pytest.raises(SandboxError,
|
|
2467
|
+
match=r"1 usable proxy address.*2 egress proxies"):
|
|
2468
|
+
backend._allocate_proxy_ip("172.18.0.0/30 ", "p2")
|
|
2469
|
+
|
|
2470
|
+
def test_in_use_addresses_are_skipped(self, tmp_path):
|
|
2471
|
+
"""048 fix: addresses held by other containers on the network (the
|
|
2472
|
+
profile containers auto-join it) must be skipped, not re-pinned."""
|
|
2473
|
+
backend = _backend(tmp_path, FakeRunner())
|
|
2474
|
+
first = backend._allocate_proxy_ip("172.18.0.0/16 ", "p1")
|
|
2475
|
+
second = backend._allocate_proxy_ip(
|
|
2476
|
+
"172.18.0.0/16 ", "p2", in_use=["172.18.0.3/16"])
|
|
2477
|
+
assert first == "172.18.0.2"
|
|
2478
|
+
assert second == "172.18.0.4"
|
|
2479
|
+
|
|
2480
|
+
def test_in_use_parsing_ignores_malformed_and_ipv6_tokens(self, tmp_path):
|
|
2481
|
+
backend = _backend(tmp_path, FakeRunner())
|
|
2482
|
+
backend._allocate_proxy_ip("172.18.0.0/16 ", "p1")
|
|
2483
|
+
second = backend._allocate_proxy_ip(
|
|
2484
|
+
"172.18.0.0/16 ", "p2",
|
|
2485
|
+
in_use=["172.18.0.2/16", "172.18.0.3/16", "fd00::2/64",
|
|
2486
|
+
"garbage"])
|
|
2487
|
+
assert second == "172.18.0.4"
|
|
2488
|
+
|
|
2489
|
+
def test_second_proxy_skips_container_held_ips(self, tmp_path):
|
|
2490
|
+
"""048 fix: the inspect output reports in-use container addresses;
|
|
2491
|
+
the second proxy skips the first proxy's IP AND the auto-assigned
|
|
2492
|
+
profile-container IP (the CI AE-SBX-020 collision)."""
|
|
2493
|
+
runner = FakeRunner(
|
|
2494
|
+
ok(""), ok("172.18.0.0/16 |"), ok("proxy"), ok(""), ok(""),
|
|
2495
|
+
ok("172.18.0.0/16 |172.18.0.2/16 172.18.0.3/16 "),
|
|
2496
|
+
ok("proxy"), ok(""), ok(""))
|
|
2497
|
+
backend = _backend(tmp_path, runner, network=True)
|
|
2498
|
+
backend._start_proxy_container("r-proxy-aaa", EgressPolicy())
|
|
2499
|
+
backend._start_proxy_container("r-proxy-bbb", EgressPolicy())
|
|
2500
|
+
connects = [c for c in backend._runner.calls if "--ip" in c]
|
|
2501
|
+
assert [c[c.index("--ip") + 1] for c in connects] == \
|
|
2502
|
+
["172.18.0.2", "172.18.0.4"]
|
|
2503
|
+
|
|
2504
|
+
def test_connect_failure_message_carries_stderr_tail(self, tmp_path):
|
|
2505
|
+
runner = FakeRunner(
|
|
2506
|
+
ok(""), ok("172.18.0.0/16 "), ok("proxy"),
|
|
2507
|
+
fail(1, "Error response from daemon: Address already in use"))
|
|
2508
|
+
backend = _backend(tmp_path, runner, network=True)
|
|
2509
|
+
with pytest.raises(SandboxError,
|
|
2510
|
+
match="Address already in use") as exc:
|
|
2511
|
+
backend._start_proxy_container("r-proxy-abc", EgressPolicy())
|
|
2512
|
+
assert "AE-SBX-020" in str(exc.value)
|
|
2513
|
+
|
|
2514
|
+
|
|
2437
2515
|
class TestRegisterBeforeCreate:
|
|
2438
2516
|
"""042 D1: every create site appends its registry record before the
|
|
2439
2517
|
create command, so a failed create still leaves a stale entry the close()
|
|
@@ -3,16 +3,18 @@ reason when no daemon or integration image is present; never silently skipped
|
|
|
3
3
|
in CI (the workflows pre-install Docker and the image)."""
|
|
4
4
|
from __future__ import annotations
|
|
5
5
|
|
|
6
|
+
import http.server
|
|
6
7
|
import json
|
|
7
8
|
import os
|
|
8
9
|
import shutil
|
|
9
10
|
import subprocess
|
|
10
11
|
import sys
|
|
12
|
+
import threading
|
|
11
13
|
|
|
12
14
|
import pytest
|
|
13
15
|
|
|
14
16
|
from agentic_dev_loop.core.config import IsolationConfig, MountSpec
|
|
15
|
-
from agentic_dev_loop.engine.egress_policy import EgressPolicy
|
|
17
|
+
from agentic_dev_loop.engine.egress_policy import EgressPolicy, TargetEntry
|
|
16
18
|
from agentic_dev_loop.engine.isolation import (
|
|
17
19
|
CONTAINER_REGISTRY, DockerBackend, SandboxExecutionRequest, SandboxError,
|
|
18
20
|
SubprocessRunner, probe_docker, process_start, read_stamp,
|
|
@@ -409,3 +411,79 @@ class TestReaperE2E:
|
|
|
409
411
|
"--format", "{{.Names}}"],
|
|
410
412
|
timeout=30)
|
|
411
413
|
assert name not in inspect.stdout
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
class TestTwoProxyDistinctIpsE2E:
|
|
418
|
+
"""048: two distinct egress policies on one run network each get their
|
|
419
|
+
own proxy container (distinct internal IP) and both relay an allowed
|
|
420
|
+
loopback dial through the host gateway."""
|
|
421
|
+
|
|
422
|
+
def _target(self, body):
|
|
423
|
+
class _Handler(http.server.BaseHTTPRequestHandler):
|
|
424
|
+
def do_GET(self):
|
|
425
|
+
payload = body.encode()
|
|
426
|
+
self.send_response(200)
|
|
427
|
+
self.send_header("Content-Length", str(len(payload)))
|
|
428
|
+
self.end_headers()
|
|
429
|
+
self.wfile.write(payload)
|
|
430
|
+
|
|
431
|
+
def log_message(self, *args):
|
|
432
|
+
pass
|
|
433
|
+
|
|
434
|
+
server = http.server.ThreadingHTTPServer(("0.0.0.0", 0), _Handler)
|
|
435
|
+
threading.Thread(target=server.serve_forever, daemon=True).start()
|
|
436
|
+
return server
|
|
437
|
+
|
|
438
|
+
def _backend_with_export(self, tmp_path):
|
|
439
|
+
reason = _docker_available()
|
|
440
|
+
if reason is not None:
|
|
441
|
+
pytest.skip(reason)
|
|
442
|
+
ws = tmp_path / "ws"
|
|
443
|
+
(ws / "src").mkdir(parents=True)
|
|
444
|
+
(ws / "src" / "a.py").write_text("orig")
|
|
445
|
+
export = tmp_path / "state" / "exports" / "r1"
|
|
446
|
+
StepWorkspace.materialise(ws, [], "linux", export)
|
|
447
|
+
profile = resolve_effective_profile(IsolationConfig(image=TEST_IMAGE))
|
|
448
|
+
b = DockerBackend(image=TEST_IMAGE, workspace=str(ws),
|
|
449
|
+
iso_state_dir=str(tmp_path / "state"), profile=profile,
|
|
450
|
+
export_root=str(export))
|
|
451
|
+
b.ensure_image()
|
|
452
|
+
b.create_execution_container()
|
|
453
|
+
return b, export
|
|
454
|
+
|
|
455
|
+
def test_two_proxies_attach_and_relay(self, tmp_path):
|
|
456
|
+
server_a = self._target("hello-from-a")
|
|
457
|
+
server_b = self._target("hello-from-b")
|
|
458
|
+
b, export = self._backend_with_export(tmp_path)
|
|
459
|
+
try:
|
|
460
|
+
cases = [
|
|
461
|
+
(EgressPolicy(targets=(TargetEntry(
|
|
462
|
+
host="host.docker.internal",
|
|
463
|
+
port=server_a.server_address[1]),)),
|
|
464
|
+
server_a.server_address[1], "hello-from-a"),
|
|
465
|
+
(EgressPolicy(targets=(TargetEntry(
|
|
466
|
+
host="host.docker.internal",
|
|
467
|
+
port=server_b.server_address[1]),)),
|
|
468
|
+
server_b.server_address[1], "hello-from-b"),
|
|
469
|
+
]
|
|
470
|
+
ms = resolve_mount_set([MountSpec(path=".", mode="ro")])
|
|
471
|
+
for policy, port, body in cases:
|
|
472
|
+
code = (
|
|
473
|
+
"import urllib.request;"
|
|
474
|
+
f"print(urllib.request.urlopen('http://host.docker.internal:{port}/').read().decode())"
|
|
475
|
+
)
|
|
476
|
+
req = SandboxExecutionRequest(
|
|
477
|
+
argv=["python", "-c", code],
|
|
478
|
+
cwd=str(export),
|
|
479
|
+
env={"PATH": "/usr/local/bin:/usr/bin:/bin", "HOME": "/tmp"},
|
|
480
|
+
network=True, mount_set=ms, egress_policy=policy)
|
|
481
|
+
result = b.run(req)
|
|
482
|
+
assert result.exit_code == 0, result.stderr
|
|
483
|
+
assert body in result.stdout
|
|
484
|
+
ips = sorted(b._proxy_ips.values())
|
|
485
|
+
assert len(ips) == 2 and ips[0] != ips[1]
|
|
486
|
+
finally:
|
|
487
|
+
b.close()
|
|
488
|
+
server_a.shutdown()
|
|
489
|
+
server_b.shutdown()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/core/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/core/results.py
RENAMED
|
File without changes
|
{agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/core/verdict.py
RENAMED
|
File without changes
|
{agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/engine/__init__.py
RENAMED
|
File without changes
|
{agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/engine/actions.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/engine/agents.py
RENAMED
|
File without changes
|
{agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/engine/allowlist.py
RENAMED
|
File without changes
|
|
File without changes
|
{agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/engine/brief_prompt.md
RENAMED
|
File without changes
|
{agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/engine/brief_prompt.py
RENAMED
|
File without changes
|
|
File without changes
|
{agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/engine/containers.py
RENAMED
|
File without changes
|
|
File without changes
|
{agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/engine/dryrun.py
RENAMED
|
File without changes
|
{agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/engine/egress_policy.py
RENAMED
|
File without changes
|
{agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/engine/egress_proxy.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/engine/forbidden.py
RENAMED
|
File without changes
|
{agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/engine/graph.py
RENAMED
|
File without changes
|
{agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/engine/hostboundary.py
RENAMED
|
File without changes
|
|
File without changes
|
{agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/engine/loaders.py
RENAMED
|
File without changes
|
{agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/engine/mounts.py
RENAMED
|
File without changes
|
{agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/engine/outputs.py
RENAMED
|
File without changes
|
{agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/engine/presets.py
RENAMED
|
File without changes
|
|
File without changes
|
{agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/engine/registry.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/engine/secrets.py
RENAMED
|
File without changes
|
{agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/engine/stages.py
RENAMED
|
File without changes
|
|
File without changes
|
{agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/engine/sync_back.py
RENAMED
|
File without changes
|
|
File without changes
|
{agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/viz/__init__.py
RENAMED
|
File without changes
|
{agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop/viz/__main__.py
RENAMED
|
File without changes
|
|
File without changes
|
{agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/src/agentic_dev_loop.egg-info/requires.txt
RENAMED
|
File without changes
|
{agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/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
|
{agentic_dev_loop-1.0.0a46 → agentic_dev_loop-1.0.0a48}/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
|