@lazyingart/agintiflow 0.8.4 → 0.8.6
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.
- package/README.md +2 -2
- package/docs/agent-runtime-pipe.md +4 -0
- package/package.json +1 -1
- package/public/app.js +55 -1
- package/public/index.html +4 -1
- package/public/styles.css +9 -0
- package/src/agent-runner.js +280 -25
- package/src/docker-sandbox.js +3 -1
- package/src/guardrails.js +21 -0
- package/src/interactive-cli.js +221 -75
- package/src/model-client.js +86 -16
- package/src/static-preview-server.js +105 -0
- package/src/workspace-tools.js +1 -1
- package/web.js +27 -0
package/README.md
CHANGED
|
@@ -54,7 +54,7 @@ aginti
|
|
|
54
54
|
aginti chat
|
|
55
55
|
```
|
|
56
56
|
|
|
57
|
-
Inside chat, type normal requests such as `write a small Python CLI app with tests`. The default is
|
|
57
|
+
Inside chat, type normal requests such as `write a small Python CLI app with tests`. The default is Docker workspace mode with approved package installs, so coding, plotting, and LaTeX tasks can set up project-local tools without touching the host. Use `/help` for commands, `/latex on` for PDF work, `/docker off` only when you intentionally want host mode, `/sessions` to list project runs, and `/resume latest` or `/resume <session-id>` to continue work. Esc or Ctrl+C stops the active run cleanly and prints the resume command.
|
|
58
58
|
|
|
59
59
|
Launch the local web UI from an installed package:
|
|
60
60
|
|
|
@@ -340,7 +340,7 @@ Package policy values:
|
|
|
340
340
|
|
|
341
341
|
Toolchain commands such as `python3 plot.py`, `latexmk -pdf paper.tex`, and `pdflatex -interaction=nonstopmode -halt-on-error paper.tex` are allowlisted only when the shell tool is enabled. In Docker mode the project folder is mounted as `/workspace`; any file written to `/workspace/report.pdf` appears on the host as `<your-project>/report.pdf`. CLI runs print both the host workspace and the Docker mapping before execution. File and canvas tools accept both normal relative paths and Docker virtual paths like `/workspace/report.pdf`, while other absolute host paths remain blocked.
|
|
342
342
|
|
|
343
|
-
The web chat mirrors the CLI session store. Enter sends, Ctrl+J inserts a newline,
|
|
343
|
+
The web chat mirrors the CLI session store. Enter sends, Ctrl+J inserts a newline, Tab submits/queues the message, and Esc stops the active run. Queued input is written to `.sessions/<session-id>/inbox.jsonl`; the running agent drains that pipe between steps and after tool calls. Generated local sites should use the built-in `preview_workspace` or `open_workspace_file` tools; AgInTiFlow avoids transient localhost servers inside Docker because those containers stop between commands and their ports are not host-published.
|
|
344
344
|
|
|
345
345
|
Safe preflight endpoints:
|
|
346
346
|
|
|
@@ -9,6 +9,10 @@ AgInTiFlow keeps CLI and web runs equivalent by using the project folder as the
|
|
|
9
9
|
|
|
10
10
|
When a run is active, the web chat and `aginti queue <session-id> "..."` append messages to the inbox instead of trying to mutate the running process directly. The runner drains the inbox at safe boundaries: before each model step and after tool execution. This mirrors the event-queue style used by mature agent UIs while keeping the backend decoupled from any specific frontend.
|
|
11
11
|
|
|
12
|
+
Runs can be stopped without corrupting session state. The CLI listens for Esc or Ctrl+C during an active run, and the web UI exposes a Stop button plus Esc. Both paths send an abort signal, persist `session.stopped`, and leave the session resumable through `aginti resume <session-id>`.
|
|
13
|
+
|
|
12
14
|
Default execution is Docker workspace mode with package installs approved inside the sandbox. The project is mounted at `/workspace`; persistent agent toolchain folders are mounted at `/aginti-home`, `/aginti-cache`, and `/aginti-env` from `~/.agintiflow/docker/`. Python, conda, and other language-level environments should be installed under `/aginti-env` so they survive across runs. Apt/apk package changes are ephemeral unless the Docker image is rebuilt.
|
|
13
15
|
|
|
16
|
+
Generated local websites should use `preview_workspace` or `open_workspace_file`. The preview tool serves the host workspace on an automatically selected `127.0.0.1` port and opens it in the browser. AgInTiFlow blocks common transient Docker preview commands such as `python -m http.server` because each shell tool call runs in a short-lived container with no published host port.
|
|
17
|
+
|
|
14
18
|
The failed `f(f(x)) = f'(x)` LaTeX task exposed three issues: host-mode command policy blocked setup/path commands, a 15-step budget was too small for iterative numerical work plus TeX output, and follow-up input could not be queued while the agent was running. The current runtime defaults and inbox pipe address those without hardcoding that specific math task.
|
package/package.json
CHANGED
package/public/app.js
CHANGED
|
@@ -76,6 +76,9 @@ const translations = {
|
|
|
76
76
|
dockerImageLabel: "Docker image",
|
|
77
77
|
dockerImagePlaceholder: "agintiflow-sandbox:latest",
|
|
78
78
|
startRunButton: "Start run",
|
|
79
|
+
stopRunButton: "Stop",
|
|
80
|
+
stoppingRun: "Stopping...",
|
|
81
|
+
stopRunFailed: "Failed to stop run.",
|
|
79
82
|
runOutputTitle: "Run output",
|
|
80
83
|
noRunStarted: "No run started.",
|
|
81
84
|
conversationTitle: "Conversation",
|
|
@@ -643,6 +646,7 @@ const packageInstallPolicyField = document.querySelector("#packageInstallPolicy"
|
|
|
643
646
|
const packageWarningEl = document.querySelector("#package-warning");
|
|
644
647
|
const logsEl = document.querySelector("#logs");
|
|
645
648
|
const runMetaEl = document.querySelector("#run-meta");
|
|
649
|
+
const stopRunButton = document.querySelector("#stop-run");
|
|
646
650
|
const keyStatusEl = document.querySelector("#key-status");
|
|
647
651
|
const allowWrapperToolsField = document.querySelector("#allowWrapperTools");
|
|
648
652
|
const preferredWrapperField = document.querySelector("#preferredWrapper");
|
|
@@ -697,6 +701,7 @@ let routingPresets = {};
|
|
|
697
701
|
let taskProfiles = [];
|
|
698
702
|
let projectInfo = null;
|
|
699
703
|
let currentSessionId = "";
|
|
704
|
+
let currentRunStatus = "";
|
|
700
705
|
let pollTimer = null;
|
|
701
706
|
let saveTimer = null;
|
|
702
707
|
let lastChatEntries = [];
|
|
@@ -728,6 +733,12 @@ function setLogs(text, mode = "active") {
|
|
|
728
733
|
logsEl.textContent = text;
|
|
729
734
|
}
|
|
730
735
|
|
|
736
|
+
function updateStopRunButton() {
|
|
737
|
+
if (!stopRunButton) return;
|
|
738
|
+
stopRunButton.hidden = currentRunStatus !== "running";
|
|
739
|
+
stopRunButton.disabled = currentRunStatus !== "running";
|
|
740
|
+
}
|
|
741
|
+
|
|
731
742
|
function renderKeyStatus(status = lastKeyStatus) {
|
|
732
743
|
lastKeyStatus = status;
|
|
733
744
|
if (!status) return;
|
|
@@ -949,6 +960,7 @@ function applyLanguage(language, { persist = true } = {}) {
|
|
|
949
960
|
renderSessionManager();
|
|
950
961
|
renderChat(lastChatEntries);
|
|
951
962
|
renderArtifactShell();
|
|
963
|
+
updateStopRunButton();
|
|
952
964
|
if (persist) schedulePreferenceSave();
|
|
953
965
|
}
|
|
954
966
|
|
|
@@ -1429,11 +1441,13 @@ async function refreshRun() {
|
|
|
1429
1441
|
const response = await fetch(`/api/runs/${encodeURIComponent(currentSessionId)}`);
|
|
1430
1442
|
if (!response.ok) return;
|
|
1431
1443
|
const run = await response.json();
|
|
1444
|
+
currentRunStatus = run.status || "";
|
|
1445
|
+
updateStopRunButton();
|
|
1432
1446
|
runMetaEl.textContent = `${run.status} · ${run.sessionId}`;
|
|
1433
1447
|
renderLogs(run);
|
|
1434
1448
|
await refreshArtifacts({ loadSelected: artifactTunnelDialog?.open });
|
|
1435
1449
|
|
|
1436
|
-
if (run.status === "finished" || run.status === "failed") {
|
|
1450
|
+
if (run.status === "finished" || run.status === "failed" || run.status === "stopped") {
|
|
1437
1451
|
clearInterval(pollTimer);
|
|
1438
1452
|
pollTimer = null;
|
|
1439
1453
|
await refreshChat();
|
|
@@ -1443,6 +1457,22 @@ async function refreshRun() {
|
|
|
1443
1457
|
}
|
|
1444
1458
|
}
|
|
1445
1459
|
|
|
1460
|
+
async function stopCurrentRun() {
|
|
1461
|
+
if (!currentSessionId || currentRunStatus !== "running") return;
|
|
1462
|
+
stopRunButton.disabled = true;
|
|
1463
|
+
chatStatusEl.textContent = t("stoppingRun");
|
|
1464
|
+
const response = await fetch(`/api/runs/${encodeURIComponent(currentSessionId)}/stop`, {
|
|
1465
|
+
method: "POST",
|
|
1466
|
+
});
|
|
1467
|
+
if (!response.ok) {
|
|
1468
|
+
const data = await response.json().catch(() => ({}));
|
|
1469
|
+
chatStatusEl.textContent = data.error || t("stopRunFailed");
|
|
1470
|
+
updateStopRunButton();
|
|
1471
|
+
return;
|
|
1472
|
+
}
|
|
1473
|
+
await refreshRun();
|
|
1474
|
+
}
|
|
1475
|
+
|
|
1446
1476
|
async function refreshChat() {
|
|
1447
1477
|
if (!currentSessionId) {
|
|
1448
1478
|
renderChat([]);
|
|
@@ -1771,6 +1801,24 @@ chatInputEl.addEventListener("keydown", (event) => {
|
|
|
1771
1801
|
}
|
|
1772
1802
|
});
|
|
1773
1803
|
|
|
1804
|
+
stopRunButton?.addEventListener("click", () => {
|
|
1805
|
+
stopCurrentRun().catch((error) => {
|
|
1806
|
+
chatStatusEl.textContent = String(error);
|
|
1807
|
+
updateStopRunButton();
|
|
1808
|
+
});
|
|
1809
|
+
});
|
|
1810
|
+
|
|
1811
|
+
window.addEventListener("keydown", (event) => {
|
|
1812
|
+
if (event.key !== "Escape" || currentRunStatus !== "running") return;
|
|
1813
|
+
const tag = event.target?.tagName?.toLowerCase();
|
|
1814
|
+
if (tag === "select" || tag === "button") return;
|
|
1815
|
+
event.preventDefault();
|
|
1816
|
+
stopCurrentRun().catch((error) => {
|
|
1817
|
+
chatStatusEl.textContent = String(error);
|
|
1818
|
+
updateStopRunButton();
|
|
1819
|
+
});
|
|
1820
|
+
});
|
|
1821
|
+
|
|
1774
1822
|
form.addEventListener("submit", async (event) => {
|
|
1775
1823
|
event.preventDefault();
|
|
1776
1824
|
|
|
@@ -1802,6 +1850,8 @@ form.addEventListener("submit", async (event) => {
|
|
|
1802
1850
|
}
|
|
1803
1851
|
|
|
1804
1852
|
currentSessionId = data.sessionId;
|
|
1853
|
+
currentRunStatus = "running";
|
|
1854
|
+
updateStopRunButton();
|
|
1805
1855
|
sessionSelectEl.value = currentSessionId;
|
|
1806
1856
|
runMetaEl.textContent = `${t("runningStatus").replace("...", "")} · ${currentSessionId}`;
|
|
1807
1857
|
await refreshSessions();
|
|
@@ -1817,6 +1867,8 @@ form.addEventListener("submit", async (event) => {
|
|
|
1817
1867
|
sessionSelectEl.addEventListener("change", async () => {
|
|
1818
1868
|
currentSessionId = sessionSelectEl.value;
|
|
1819
1869
|
if (!currentSessionId) {
|
|
1870
|
+
currentRunStatus = "";
|
|
1871
|
+
updateStopRunButton();
|
|
1820
1872
|
runMetaEl.textContent = "";
|
|
1821
1873
|
setLogs(t("noRunSelected"), "empty");
|
|
1822
1874
|
renderChat([]);
|
|
@@ -1862,6 +1914,8 @@ chatFormEl.addEventListener("submit", async (event) => {
|
|
|
1862
1914
|
}
|
|
1863
1915
|
|
|
1864
1916
|
currentSessionId = data.sessionId;
|
|
1917
|
+
currentRunStatus = data.queued ? currentRunStatus : "running";
|
|
1918
|
+
updateStopRunButton();
|
|
1865
1919
|
chatInputEl.value = "";
|
|
1866
1920
|
chatStatusEl.textContent = data.queued ? t("queuedStatus") : t("runningStatus");
|
|
1867
1921
|
if (data.queued) {
|
package/public/index.html
CHANGED
|
@@ -307,7 +307,10 @@
|
|
|
307
307
|
<section class="panel output-panel">
|
|
308
308
|
<div class="header-row">
|
|
309
309
|
<h2 data-i18n="runOutputTitle">Run output</h2>
|
|
310
|
-
<div
|
|
310
|
+
<div class="run-header-actions">
|
|
311
|
+
<button id="stop-run" type="button" class="secondary danger" data-i18n="stopRunButton" hidden>Stop</button>
|
|
312
|
+
<div id="run-meta" class="subtle"></div>
|
|
313
|
+
</div>
|
|
311
314
|
</div>
|
|
312
315
|
<pre id="logs" class="logs" data-i18n="noRunStarted">No run started.</pre>
|
|
313
316
|
</section>
|
package/public/styles.css
CHANGED
|
@@ -346,6 +346,15 @@ button.danger {
|
|
|
346
346
|
justify-content: space-between;
|
|
347
347
|
}
|
|
348
348
|
|
|
349
|
+
.run-header-actions {
|
|
350
|
+
display: flex;
|
|
351
|
+
min-width: 0;
|
|
352
|
+
flex-wrap: wrap;
|
|
353
|
+
gap: 10px;
|
|
354
|
+
align-items: center;
|
|
355
|
+
justify-content: flex-end;
|
|
356
|
+
}
|
|
357
|
+
|
|
349
358
|
.sandbox-card {
|
|
350
359
|
display: grid;
|
|
351
360
|
gap: 12px;
|