agentic-dev-loop 0.9.1__tar.gz → 0.9.2__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.
- {agentic_dev_loop-0.9.1/src/agentic_dev_loop.egg-info → agentic_dev_loop-0.9.2}/PKG-INFO +1 -1
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/pyproject.toml +1 -1
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/src/agentic_dev_loop/viz/server.py +52 -15
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2/src/agentic_dev_loop.egg-info}/PKG-INFO +1 -1
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/tests/test_hostboundary_integration.py +6 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/tests/test_isolation_integration.py +6 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/tests/test_viz.py +138 -4
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/LICENSE +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/README.md +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/setup.cfg +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/src/agentic_dev_loop/__init__.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/src/agentic_dev_loop/core/__init__.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/src/agentic_dev_loop/core/config.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/src/agentic_dev_loop/core/paths.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/src/agentic_dev_loop/core/results.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/src/agentic_dev_loop/core/verdict.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/src/agentic_dev_loop/engine/__init__.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/src/agentic_dev_loop/engine/actions.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/src/agentic_dev_loop/engine/agent_error_guide.md +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/src/agentic_dev_loop/engine/agent_error_guide.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/src/agentic_dev_loop/engine/agents.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/src/agentic_dev_loop/engine/allowlist.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/src/agentic_dev_loop/engine/brief_coverage.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/src/agentic_dev_loop/engine/brief_prompt.md +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/src/agentic_dev_loop/engine/brief_prompt.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/src/agentic_dev_loop/engine/cli.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/src/agentic_dev_loop/engine/containers.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/src/agentic_dev_loop/engine/deps.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/src/agentic_dev_loop/engine/dryrun.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/src/agentic_dev_loop/engine/egress_policy.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/src/agentic_dev_loop/engine/egress_proxy.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/src/agentic_dev_loop/engine/extensions/__init__.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/src/agentic_dev_loop/engine/extensions/aidlc.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/src/agentic_dev_loop/engine/forbidden.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/src/agentic_dev_loop/engine/graph.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/src/agentic_dev_loop/engine/hostboundary.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/src/agentic_dev_loop/engine/isolation.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/src/agentic_dev_loop/engine/llm.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/src/agentic_dev_loop/engine/loaders.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/src/agentic_dev_loop/engine/mounts.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/src/agentic_dev_loop/engine/outputs.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/src/agentic_dev_loop/engine/presets.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/src/agentic_dev_loop/engine/proc.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/src/agentic_dev_loop/engine/registry.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/src/agentic_dev_loop/engine/seccomp/__init__.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/src/agentic_dev_loop/engine/seccomp/chromium-userns.json +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/src/agentic_dev_loop/engine/secrets.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/src/agentic_dev_loop/engine/stages.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/src/agentic_dev_loop/engine/step_workspace.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/src/agentic_dev_loop/engine/sync_back.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/src/agentic_dev_loop/py.typed +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/src/agentic_dev_loop/viz/__init__.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/src/agentic_dev_loop/viz/__main__.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/src/agentic_dev_loop.egg-info/SOURCES.txt +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/src/agentic_dev_loop.egg-info/dependency_links.txt +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/src/agentic_dev_loop.egg-info/entry_points.txt +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/src/agentic_dev_loop.egg-info/requires.txt +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/src/agentic_dev_loop.egg-info/top_level.txt +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/tests/test_029_merge_on_conflict.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/tests/test_033_brief_step.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/tests/test_035_finalize_nudge.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/tests/test_036_banned_commands_and_lane_boundary.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/tests/test_041_brief_coverage.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/tests/test_046_brief_prompt.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/tests/test_actions.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/tests/test_agent_error_guide.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/tests/test_agents.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/tests/test_aidlc.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/tests/test_cli.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/tests/test_config.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/tests/test_containers.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/tests/test_deps.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/tests/test_dry_run.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/tests/test_e2e_fixtures.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/tests/test_egress_proxy.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/tests/test_graph.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/tests/test_hostboundary.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/tests/test_isolation.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/tests/test_isolation_pbt.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/tests/test_llm.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/tests/test_loaders.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/tests/test_module_contract.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/tests/test_outputs.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/tests/test_outputs_pbt.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/tests/test_proc.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/tests/test_publish.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/tests/test_qa_prompt.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/tests/test_registry.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/tests/test_repo_ascii.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/tests/test_results.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/tests/test_sdk_compat.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/tests/test_secrets.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/tests/test_secrets_integration.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/tests/test_secrets_pipeline.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/tests/test_source_resolution.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/tests/test_stages.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/tests/test_step_workspace.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/tests/test_sync_back.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/tests/test_trigger_script.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/tests/test_unit5_config.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/tests/test_unit5_pbt.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/tests/test_unit5_pure.py +0 -0
- {agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/tests/test_verdict.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 = "0.9.
|
|
7
|
+
version = "0.9.2"
|
|
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"
|
|
@@ -456,6 +456,10 @@ PAGE = """<!doctype html>
|
|
|
456
456
|
border-radius: 6px; margin-top: 10px; display: none; }
|
|
457
457
|
.banner-success { background: #e8f5e9; color: #2e7d32; }
|
|
458
458
|
.banner-failure { background: #ffebee; color: #c62828; }
|
|
459
|
+
#stream-banner { display: none; position: fixed; top: 0; left: 0; right: 0;
|
|
460
|
+
z-index: 9999; background: #b26a00; color: #ffffff;
|
|
461
|
+
text-align: center; padding: 8px 12px; font-size: 14px;
|
|
462
|
+
font-weight: 700; }
|
|
459
463
|
#divider { flex: 0 0 6px; cursor: col-resize; background: #e0e0e0; }
|
|
460
464
|
#divider:hover, #divider.dragging { background: #9e9e9e; }
|
|
461
465
|
#sidebar { flex: 0 0 320px; border-left: 1px solid #e0e0e0; padding: 16px;
|
|
@@ -525,6 +529,7 @@ PAGE = """<!doctype html>
|
|
|
525
529
|
onerror="window.__mermaidFailed=true"></script>
|
|
526
530
|
</head>
|
|
527
531
|
<body>
|
|
532
|
+
<div id="stream-banner">connection lost, reconnecting...</div>
|
|
528
533
|
<div id="graph-panel">
|
|
529
534
|
<div id="mermaid-container"><p>Loading graph…</p></div>
|
|
530
535
|
<div id="run-banner"></div>
|
|
@@ -561,6 +566,9 @@ PAGE = """<!doctype html>
|
|
|
561
566
|
var liveElapsed = {};
|
|
562
567
|
var runStartWall = null;
|
|
563
568
|
var totalTimer = null;
|
|
569
|
+
var lastEventAt = 0;
|
|
570
|
+
var reconnectAttempts = 0;
|
|
571
|
+
var runActive = false;
|
|
564
572
|
var topologyReady = false;
|
|
565
573
|
var pendingEvents = [];
|
|
566
574
|
var MAX_CONSOLE = 500;
|
|
@@ -614,11 +622,12 @@ PAGE = """<!doctype html>
|
|
|
614
622
|
if (runStartWall === null) {
|
|
615
623
|
line.textContent = "";
|
|
616
624
|
line.style.display = "none";
|
|
617
|
-
|
|
625
|
+
} else {
|
|
626
|
+
line.textContent = "total " + mmss(Math.max(0,
|
|
627
|
+
Math.round(Date.now() / 1000 - runStartWall)));
|
|
628
|
+
line.style.display = "block";
|
|
618
629
|
}
|
|
619
|
-
|
|
620
|
-
Math.round(Date.now() / 1000 - runStartWall)));
|
|
621
|
-
line.style.display = "block";
|
|
630
|
+
if (runActive && Date.now() - lastEventAt > 10000) { recover(); }
|
|
622
631
|
}
|
|
623
632
|
|
|
624
633
|
function statusOf(n) {
|
|
@@ -1055,10 +1064,11 @@ PAGE = """<!doctype html>
|
|
|
1055
1064
|
flushPending();
|
|
1056
1065
|
stepTimes = {};
|
|
1057
1066
|
liveElapsed = {};
|
|
1067
|
+
runActive = true;
|
|
1058
1068
|
if (totalTimer !== null) { clearInterval(totalTimer); totalTimer = null; }
|
|
1059
1069
|
runStartWall = (typeof ev.started_at === "number")
|
|
1060
1070
|
? ev.started_at : null;
|
|
1061
|
-
|
|
1071
|
+
totalTimer = setInterval(renderTotal, 1000);
|
|
1062
1072
|
renderTotal();
|
|
1063
1073
|
clearConsoles();
|
|
1064
1074
|
resetVerdicts();
|
|
@@ -1137,6 +1147,7 @@ PAGE = """<!doctype html>
|
|
|
1137
1147
|
updateAttempt();
|
|
1138
1148
|
break;
|
|
1139
1149
|
case "run_finished":
|
|
1150
|
+
runActive = false;
|
|
1140
1151
|
if (totalTimer !== null) { clearInterval(totalTimer); totalTimer = null; }
|
|
1141
1152
|
if (ev.elapsed_seconds !== undefined) {
|
|
1142
1153
|
runStartWall = null;
|
|
@@ -1194,16 +1205,42 @@ PAGE = """<!doctype html>
|
|
|
1194
1205
|
}
|
|
1195
1206
|
}, 5000);
|
|
1196
1207
|
|
|
1197
|
-
var es =
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1208
|
+
var es = null;
|
|
1209
|
+
|
|
1210
|
+
function connectStream() {
|
|
1211
|
+
var stream = new EventSource("/events");
|
|
1212
|
+
stream.onmessage = function (e) {
|
|
1213
|
+
lastEventAt = Date.now();
|
|
1214
|
+
reconnectAttempts = 0;
|
|
1215
|
+
el("stream-banner").style.display = "none";
|
|
1216
|
+
var ev;
|
|
1217
|
+
try { ev = JSON.parse(e.data); }
|
|
1218
|
+
catch (err) { logLine("bad event: " + e.data); return; }
|
|
1219
|
+
if (!topologyReady) { pendingEvents.push(ev); return; }
|
|
1220
|
+
try { handle(ev); }
|
|
1221
|
+
catch (err) { logLine("bad event: " + e.data); }
|
|
1222
|
+
};
|
|
1223
|
+
stream.onerror = function () { logLine("event stream interrupted"); };
|
|
1224
|
+
return stream;
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
es = connectStream();
|
|
1228
|
+
|
|
1229
|
+
function recover() {
|
|
1230
|
+
el("stream-banner").style.display = "block";
|
|
1231
|
+
reconnectAttempts++;
|
|
1232
|
+
if (reconnectAttempts > 3) {
|
|
1233
|
+
location.reload();
|
|
1234
|
+
return;
|
|
1235
|
+
}
|
|
1236
|
+
es.close();
|
|
1237
|
+
es = connectStream();
|
|
1238
|
+
lastEventAt = Date.now();
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
document.addEventListener("visibilitychange", function () {
|
|
1242
|
+
if (runActive && Date.now() - lastEventAt > 10000) { recover(); }
|
|
1243
|
+
});
|
|
1207
1244
|
|
|
1208
1245
|
// Grab-handle between the graph and the sidebar: drag to resize the
|
|
1209
1246
|
// sidebar (and with it the console panels, which stretch with its width).
|
|
@@ -46,6 +46,12 @@ def backend(tmp_path):
|
|
|
46
46
|
pytest.skip(f"integration image {TEST_IMAGE!r} not present locally")
|
|
47
47
|
ws = tmp_path / "ws"
|
|
48
48
|
ws.mkdir()
|
|
49
|
+
# 052: sync-back resolves ignored paths via `git check-ignore` against
|
|
50
|
+
# the workspace, which must be a git repo (production workspaces always
|
|
51
|
+
# are). The synthetic fixture workspace needs the same shape.
|
|
52
|
+
init = SubprocessRunner().run(["git", "init", "-q", str(ws)], timeout=30)
|
|
53
|
+
if init.exit_code != 0:
|
|
54
|
+
pytest.fail(f"git init failed: {init.stderr}")
|
|
49
55
|
# unit-5: every step resolves a mount set, so enforcement needs the
|
|
50
56
|
# materialised .git-free export (AE-SBX-017 otherwise). Wire the export
|
|
51
57
|
# the way startup_sandbox does (mirror test_isolation_integration's
|
|
@@ -192,6 +192,12 @@ class TestExportAndMountsE2E:
|
|
|
192
192
|
(ws / "src" / "a.py").write_text("orig")
|
|
193
193
|
(ws / "ro").mkdir()
|
|
194
194
|
(ws / "ro" / "f").write_text("read-only")
|
|
195
|
+
# 052: sync-back resolves ignored paths via `git check-ignore` against
|
|
196
|
+
# the workspace, which must be a git repo (production workspaces
|
|
197
|
+
# always are). The synthetic fixture workspace needs the same shape.
|
|
198
|
+
init = SubprocessRunner().run(["git", "init", "-q", str(ws)], timeout=30)
|
|
199
|
+
if init.exit_code != 0:
|
|
200
|
+
pytest.fail(f"git init failed: {init.stderr}")
|
|
195
201
|
export = tmp_path / "state" / "exports" / "r1"
|
|
196
202
|
ws_obj = StepWorkspace.materialise(ws, [], "linux", export)
|
|
197
203
|
profile = resolve_effective_profile(IsolationConfig(image=TEST_IMAGE))
|
|
@@ -854,6 +854,16 @@ class TestServer:
|
|
|
854
854
|
assert "ev.attempt !== verdictAttempt" not in PAGE
|
|
855
855
|
assert "currentVerdicts[k] = v" in PAGE
|
|
856
856
|
|
|
857
|
+
def test_page_constant_has_stream_watchdog(self):
|
|
858
|
+
assert "stream-banner" in PAGE
|
|
859
|
+
assert "connection lost" in PAGE
|
|
860
|
+
assert "visibilitychange" in PAGE
|
|
861
|
+
assert "location.reload" in PAGE
|
|
862
|
+
assert "lastEventAt" in PAGE
|
|
863
|
+
assert "connectStream" in PAGE
|
|
864
|
+
assert "recover" in PAGE
|
|
865
|
+
assert PAGE.count("new EventSource") == 1
|
|
866
|
+
|
|
857
867
|
|
|
858
868
|
# The page script is real browser JS embedded in the PAGE constant. Run it
|
|
859
869
|
# under node (skipped when node is absent) against a minimal DOM shim and feed
|
|
@@ -948,10 +958,12 @@ El.prototype.scrollIntoView = function () {};
|
|
|
948
958
|
|
|
949
959
|
var STATIC_IDS = ["subject", "attempt", "thread-id", "total", "verdicts",
|
|
950
960
|
"stages", "run-log", "mermaid-container", "run-banner",
|
|
951
|
-
"divider", "sidebar"];
|
|
961
|
+
"stream-banner", "divider", "sidebar"];
|
|
952
962
|
STATIC_IDS.forEach(function (id) { registry[id] = new El(id); });
|
|
953
963
|
var elCounter = 0;
|
|
954
964
|
var document = {
|
|
965
|
+
_listeners: {},
|
|
966
|
+
addEventListener: function (t, fn) { document._listeners[t] = fn; },
|
|
955
967
|
getElementById: function (id) {
|
|
956
968
|
if (registry[id]) { return registry[id]; }
|
|
957
969
|
for (var k in registry) {
|
|
@@ -966,13 +978,18 @@ var document = {
|
|
|
966
978
|
},
|
|
967
979
|
body: new El("body")
|
|
968
980
|
};
|
|
969
|
-
var
|
|
970
|
-
|
|
981
|
+
var closeCalls = 0;
|
|
982
|
+
var reloadCalls = 0;
|
|
983
|
+
var esInstances = [];
|
|
984
|
+
var window = { __mermaidFailed: false, innerWidth: 1000, _listeners: {} };
|
|
985
|
+
window.addEventListener = function (t, fn) { window._listeners[t] = fn; };
|
|
986
|
+
window.location = { reload: function () { reloadCalls++; } };
|
|
971
987
|
var mermaidCalls = [];
|
|
972
988
|
var esInstance = null;
|
|
973
989
|
|
|
974
990
|
globalThis.document = document;
|
|
975
991
|
globalThis.window = window;
|
|
992
|
+
globalThis.location = window.location;
|
|
976
993
|
var copiedText = null;
|
|
977
994
|
var intervalFn = null;
|
|
978
995
|
// node 21+ ships a read-only `navigator` global, so defineProperty is needed
|
|
@@ -1012,6 +1029,8 @@ globalThis.EventSource = function (url) {
|
|
|
1012
1029
|
this.url = url;
|
|
1013
1030
|
this.onmessage = null;
|
|
1014
1031
|
this.onerror = null;
|
|
1032
|
+
this.close = function () { closeCalls++; };
|
|
1033
|
+
esInstances.push(this);
|
|
1015
1034
|
esInstance = this;
|
|
1016
1035
|
};
|
|
1017
1036
|
|
|
@@ -1045,6 +1064,16 @@ function totalText() {
|
|
|
1045
1064
|
return { text: total.textContent, display: total.style.display };
|
|
1046
1065
|
}
|
|
1047
1066
|
|
|
1067
|
+
function watchdogState() {
|
|
1068
|
+
var banner = document.getElementById("stream-banner");
|
|
1069
|
+
return {
|
|
1070
|
+
instances: esInstances.length,
|
|
1071
|
+
closeCalls: closeCalls,
|
|
1072
|
+
reloadCalls: reloadCalls,
|
|
1073
|
+
bannerDisplay: banner ? banner.style.display : null
|
|
1074
|
+
};
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1048
1077
|
var snapshots = [];
|
|
1049
1078
|
async function main() {
|
|
1050
1079
|
// let the topology fetch microtask chain resolve so events are handled
|
|
@@ -1056,6 +1085,16 @@ async function main() {
|
|
|
1056
1085
|
if (intervalFn) { intervalFn(); }
|
|
1057
1086
|
return;
|
|
1058
1087
|
}
|
|
1088
|
+
if (ev.type === "__snapshot") {
|
|
1089
|
+
snapshots.push({ watchdog: watchdogState() });
|
|
1090
|
+
return;
|
|
1091
|
+
}
|
|
1092
|
+
if (ev.type === "__visibility") {
|
|
1093
|
+
fakeNow = ev.now;
|
|
1094
|
+
var visFn = document._listeners["visibilitychange"];
|
|
1095
|
+
if (visFn) { visFn(); }
|
|
1096
|
+
return;
|
|
1097
|
+
}
|
|
1059
1098
|
fire(ev);
|
|
1060
1099
|
if (ev.type === "state") {
|
|
1061
1100
|
snapshots.push({ ev: ev, badges: badges(), classes: nodeClasses(),
|
|
@@ -1268,11 +1307,106 @@ main();
|
|
|
1268
1307
|
]
|
|
1269
1308
|
snaps = self._run_page(node, events, tmp_path)
|
|
1270
1309
|
|
|
1271
|
-
assert snaps[0]["timer"] is
|
|
1310
|
+
assert snaps[0]["timer"] is True
|
|
1272
1311
|
assert snaps[0]["total"] == {"text": "", "display": "none"}
|
|
1273
1312
|
assert snaps[-1]["total"]["text"] == "total 1:35"
|
|
1274
1313
|
assert snaps[-1]["total"]["display"] == "block"
|
|
1275
1314
|
|
|
1315
|
+
def test_silence_while_run_active_recreates_stream(self, node, tmp_path):
|
|
1316
|
+
events = [
|
|
1317
|
+
{"type": "run_started", "subject": "s", "max_attempts": 5,
|
|
1318
|
+
"attempt": 1, "thread_id": "t", "started_at": 1000},
|
|
1319
|
+
{"type": "node_started", "node": "dev"},
|
|
1320
|
+
{"type": "heartbeat", "node": "dev", "elapsed_seconds": 5},
|
|
1321
|
+
{"type": "__tick", "now": 15000},
|
|
1322
|
+
{"type": "__snapshot"},
|
|
1323
|
+
]
|
|
1324
|
+
snaps = self._run_page(node, events, tmp_path)
|
|
1325
|
+
|
|
1326
|
+
wd = snaps[0]["watchdog"]
|
|
1327
|
+
assert wd["instances"] >= 2
|
|
1328
|
+
assert wd["closeCalls"] == 1
|
|
1329
|
+
assert wd["bannerDisplay"] == "block"
|
|
1330
|
+
assert wd["reloadCalls"] == 0
|
|
1331
|
+
|
|
1332
|
+
def test_silence_when_idle_does_not_recreate(self, node, tmp_path):
|
|
1333
|
+
events = [
|
|
1334
|
+
{"type": "run_started", "subject": "s", "max_attempts": 5,
|
|
1335
|
+
"attempt": 1, "thread_id": "t"},
|
|
1336
|
+
{"type": "run_finished", "success": True, "attempts": 1,
|
|
1337
|
+
"elapsed_seconds": 60},
|
|
1338
|
+
{"type": "__tick", "now": 120000},
|
|
1339
|
+
{"type": "__snapshot"},
|
|
1340
|
+
]
|
|
1341
|
+
snaps = self._run_page(node, events, tmp_path)
|
|
1342
|
+
|
|
1343
|
+
wd = snaps[0]["watchdog"]
|
|
1344
|
+
assert wd["instances"] == 1
|
|
1345
|
+
assert wd["bannerDisplay"] == "none"
|
|
1346
|
+
assert wd["reloadCalls"] == 0
|
|
1347
|
+
|
|
1348
|
+
def test_recovery_rearms_after_replay(self, node, tmp_path):
|
|
1349
|
+
events = [
|
|
1350
|
+
{"type": "run_started", "subject": "s", "max_attempts": 5,
|
|
1351
|
+
"attempt": 1, "thread_id": "t", "started_at": 1000},
|
|
1352
|
+
{"type": "node_started", "node": "dev"},
|
|
1353
|
+
{"type": "heartbeat", "node": "dev", "elapsed_seconds": 5},
|
|
1354
|
+
{"type": "__tick", "now": 15000},
|
|
1355
|
+
{"type": "__snapshot"},
|
|
1356
|
+
{"type": "run_started", "subject": "s", "max_attempts": 5,
|
|
1357
|
+
"attempt": 1, "thread_id": "t", "started_at": 1000},
|
|
1358
|
+
{"type": "node_started", "node": "dev"},
|
|
1359
|
+
{"type": "__tick", "now": 24000},
|
|
1360
|
+
{"type": "__snapshot"},
|
|
1361
|
+
{"type": "__tick", "now": 26000},
|
|
1362
|
+
{"type": "__snapshot"},
|
|
1363
|
+
]
|
|
1364
|
+
snaps = self._run_page(node, events, tmp_path)
|
|
1365
|
+
|
|
1366
|
+
assert snaps[0]["watchdog"]["instances"] >= 2
|
|
1367
|
+
assert snaps[1]["watchdog"]["instances"] == 2
|
|
1368
|
+
assert snaps[1]["watchdog"]["bannerDisplay"] == "none"
|
|
1369
|
+
assert snaps[2]["watchdog"]["instances"] >= 3
|
|
1370
|
+
assert snaps[2]["watchdog"]["closeCalls"] >= 2
|
|
1371
|
+
|
|
1372
|
+
def test_repeated_failures_reload(self, node, tmp_path):
|
|
1373
|
+
events = [
|
|
1374
|
+
{"type": "run_started", "subject": "s", "max_attempts": 5,
|
|
1375
|
+
"attempt": 1, "thread_id": "t", "started_at": 1000},
|
|
1376
|
+
{"type": "node_started", "node": "dev"},
|
|
1377
|
+
{"type": "__tick", "now": 11000},
|
|
1378
|
+
{"type": "__tick", "now": 22000},
|
|
1379
|
+
{"type": "__tick", "now": 33000},
|
|
1380
|
+
{"type": "__tick", "now": 44000},
|
|
1381
|
+
{"type": "__snapshot"},
|
|
1382
|
+
]
|
|
1383
|
+
snaps = self._run_page(node, events, tmp_path)
|
|
1384
|
+
|
|
1385
|
+
wd = snaps[0]["watchdog"]
|
|
1386
|
+
assert wd["reloadCalls"] == 1
|
|
1387
|
+
assert wd["instances"] == 4
|
|
1388
|
+
assert wd["closeCalls"] == 3
|
|
1389
|
+
|
|
1390
|
+
def test_visibility_return_triggers_recovery(self, node, tmp_path):
|
|
1391
|
+
events = [
|
|
1392
|
+
{"type": "run_started", "subject": "s", "max_attempts": 5,
|
|
1393
|
+
"attempt": 1, "thread_id": "t", "started_at": 1000},
|
|
1394
|
+
{"type": "node_started", "node": "dev"},
|
|
1395
|
+
{"type": "heartbeat", "node": "dev", "elapsed_seconds": 5},
|
|
1396
|
+
{"type": "__visibility", "now": 15000},
|
|
1397
|
+
{"type": "__snapshot"},
|
|
1398
|
+
{"type": "heartbeat", "node": "dev", "elapsed_seconds": 16},
|
|
1399
|
+
{"type": "__visibility", "now": 15000},
|
|
1400
|
+
{"type": "__snapshot"},
|
|
1401
|
+
]
|
|
1402
|
+
snaps = self._run_page(node, events, tmp_path)
|
|
1403
|
+
|
|
1404
|
+
assert snaps[0]["watchdog"]["instances"] >= 2
|
|
1405
|
+
assert snaps[0]["watchdog"]["closeCalls"] == 1
|
|
1406
|
+
assert snaps[0]["watchdog"]["reloadCalls"] == 0
|
|
1407
|
+
assert snaps[1]["watchdog"]["instances"] == 2
|
|
1408
|
+
assert snaps[1]["watchdog"]["bannerDisplay"] == "none"
|
|
1409
|
+
|
|
1276
1410
|
|
|
1277
1411
|
class TestDemo:
|
|
1278
1412
|
def test_run_started_carries_attempt(self, monkeypatch):
|
|
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-0.9.1 → agentic_dev_loop-0.9.2}/src/agentic_dev_loop/engine/agent_error_guide.md
RENAMED
|
File without changes
|
{agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/src/agentic_dev_loop/engine/agent_error_guide.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/src/agentic_dev_loop/engine/brief_coverage.py
RENAMED
|
File without changes
|
{agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/src/agentic_dev_loop/engine/brief_prompt.md
RENAMED
|
File without changes
|
{agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/src/agentic_dev_loop/engine/brief_prompt.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/src/agentic_dev_loop/engine/egress_policy.py
RENAMED
|
File without changes
|
{agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/src/agentic_dev_loop/engine/egress_proxy.py
RENAMED
|
File without changes
|
{agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/src/agentic_dev_loop/engine/extensions/__init__.py
RENAMED
|
File without changes
|
{agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/src/agentic_dev_loop/engine/extensions/aidlc.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/src/agentic_dev_loop/engine/hostboundary.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
|
{agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/src/agentic_dev_loop/engine/seccomp/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/src/agentic_dev_loop/engine/step_workspace.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/src/agentic_dev_loop.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/src/agentic_dev_loop.egg-info/entry_points.txt
RENAMED
|
File without changes
|
{agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/src/agentic_dev_loop.egg-info/requires.txt
RENAMED
|
File without changes
|
{agentic_dev_loop-0.9.1 → agentic_dev_loop-0.9.2}/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
|
|
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
|