@lazyingart/agintiflow 0.20.172 → 0.20.173
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/package.json +1 -1
- package/public/app.js +131 -22
- package/public/styles.css +120 -17
- package/scripts/smoke-cli-chat.js +3 -0
- package/scripts/smoke-web-api.js +5 -0
- package/scripts/smoke-web-ui.js +41 -3
- package/scripts/smoke-webapp-command.js +24 -2
- package/src/interactive-cli.js +4 -0
- package/web.js +9 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lazyingart/agintiflow",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.173",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "AgInTiFlow is a project-aware agent workspace for hybrid wet-dry R&D, hardware-aware intelligence, software automation, and industrial workflows.",
|
|
6
6
|
"license": "Apache-2.0",
|
package/public/app.js
CHANGED
|
@@ -1782,6 +1782,131 @@ function renderPermissionApproval(entry) {
|
|
|
1782
1782
|
`;
|
|
1783
1783
|
}
|
|
1784
1784
|
|
|
1785
|
+
function renderPlanEvent(entry) {
|
|
1786
|
+
const data = entry.data || {};
|
|
1787
|
+
const plan = data.plan || entry.content || "";
|
|
1788
|
+
return `
|
|
1789
|
+
<article class="event-card event-plan">
|
|
1790
|
+
<div class="event-card-meta">
|
|
1791
|
+
<span>plan</span>
|
|
1792
|
+
<span>${entry.at ? escapeHtml(new Date(entry.at).toLocaleString()) : ""}</span>
|
|
1793
|
+
</div>
|
|
1794
|
+
<div class="markdown-body">${renderMarkdown(plan)}</div>
|
|
1795
|
+
</article>
|
|
1796
|
+
`;
|
|
1797
|
+
}
|
|
1798
|
+
|
|
1799
|
+
function renderWorkspaceChangeEvent(entry) {
|
|
1800
|
+
const data = entry.data || {};
|
|
1801
|
+
const toolName = data.toolName || data.action || "file.changed";
|
|
1802
|
+
const isPatch = toolName === "apply_patch" || String(toolName).startsWith("apply_patch");
|
|
1803
|
+
const diff = String(data.diff || "");
|
|
1804
|
+
const maxDiffChars = 9000;
|
|
1805
|
+
const diffBlock = diff
|
|
1806
|
+
? `<pre class="change-diff event-diff">${renderDiffHtml(diff, maxDiffChars)}</pre>${
|
|
1807
|
+
diff.length > maxDiffChars ? `<div class="event-fold-note">... diff truncated for browser preview</div>` : ""
|
|
1808
|
+
}`
|
|
1809
|
+
: "";
|
|
1810
|
+
const hashes =
|
|
1811
|
+
data.beforeHash || data.afterHash
|
|
1812
|
+
? `<div class="event-card-meta"><span>before=${escapeHtml((data.beforeHash || "new").slice(0, 10))}</span><span>after=${escapeHtml((data.afterHash || "").slice(0, 10))}</span></div>`
|
|
1813
|
+
: "";
|
|
1814
|
+
return `
|
|
1815
|
+
<article class="event-card ${isPatch ? "event-patch" : "event-write"}">
|
|
1816
|
+
<div class="event-card-meta">
|
|
1817
|
+
<span>${escapeHtml(isPatch ? "patch" : "write")}</span>
|
|
1818
|
+
<span>${escapeHtml(toolName)}</span>
|
|
1819
|
+
<span>${entry.at ? escapeHtml(new Date(entry.at).toLocaleString()) : ""}</span>
|
|
1820
|
+
</div>
|
|
1821
|
+
<strong class="event-path">${escapeHtml(data.path || entry.content || "")}</strong>
|
|
1822
|
+
${data.created ? `<div class="event-fold-note">created</div>` : ""}
|
|
1823
|
+
${hashes}
|
|
1824
|
+
${diffBlock || `<div class="event-fold-note">No diff preview recorded for this file event.</div>`}
|
|
1825
|
+
</article>
|
|
1826
|
+
`;
|
|
1827
|
+
}
|
|
1828
|
+
|
|
1829
|
+
function renderToolEvent(entry) {
|
|
1830
|
+
const data = entry.data || {};
|
|
1831
|
+
const message = entry.message || entry.eventType || "";
|
|
1832
|
+
const failed = message === "tool.failed" || data.ok === false || data.blocked || data.error;
|
|
1833
|
+
const skipped = message === "tool.skipped";
|
|
1834
|
+
const toolName = data.toolName || "tool";
|
|
1835
|
+
const args = data.args || {};
|
|
1836
|
+
const argPreview =
|
|
1837
|
+
toolName === "run_command" && args.command
|
|
1838
|
+
? args.command
|
|
1839
|
+
: args.path || args.url || args.query || args.q || (Object.keys(args).length ? JSON.stringify(args) : "");
|
|
1840
|
+
const stdout = data.stdout ? outputPreviewText(data.stdout) : null;
|
|
1841
|
+
const stderr = data.stderr ? outputPreviewText(data.stderr) : null;
|
|
1842
|
+
return `
|
|
1843
|
+
<article class="event-card ${failed ? "event-failed" : skipped ? "event-muted" : "event-tool"}">
|
|
1844
|
+
<div class="event-card-meta">
|
|
1845
|
+
<span>${escapeHtml(failed ? "tool failed" : skipped ? "tool skipped" : message === "tool.started" ? "tool" : "tool done")}</span>
|
|
1846
|
+
<span>${entry.at ? escapeHtml(new Date(entry.at).toLocaleString()) : ""}</span>
|
|
1847
|
+
</div>
|
|
1848
|
+
<strong class="event-path">${escapeHtml(toolName)}</strong>
|
|
1849
|
+
${argPreview ? `<code class="event-inline-code">${escapeHtml(argPreview)}</code>` : ""}
|
|
1850
|
+
${data.error || data.reason ? `<div class="event-fold-note">${escapeHtml(data.error || data.reason)}</div>` : ""}
|
|
1851
|
+
${stdout ? `<div class="log-stream-title">stdout</div><pre class="event-output">${escapeHtml(stdout.text)}</pre>` : ""}
|
|
1852
|
+
${stdout?.hidden > 0 ? `<div class="event-fold-note">... ${stdout.hidden} more stdout line(s) folded</div>` : ""}
|
|
1853
|
+
${stderr ? `<div class="log-stream-title">stderr</div><pre class="event-output">${escapeHtml(stderr.text)}</pre>` : ""}
|
|
1854
|
+
${stderr?.hidden > 0 ? `<div class="event-fold-note">... ${stderr.hidden} more stderr line(s) folded</div>` : ""}
|
|
1855
|
+
</article>
|
|
1856
|
+
`;
|
|
1857
|
+
}
|
|
1858
|
+
|
|
1859
|
+
function renderStatusEvent(entry) {
|
|
1860
|
+
const data = entry.data || {};
|
|
1861
|
+
const message = entry.message || entry.eventType || "event";
|
|
1862
|
+
const label = entry.eventLabel || message.replace(/^[^.]+\./, "");
|
|
1863
|
+
const content = data.result || data.error || data.reason || entry.content || "";
|
|
1864
|
+
const failed = message === "session.failed";
|
|
1865
|
+
return `
|
|
1866
|
+
<article class="event-card ${failed ? "event-failed" : "event-muted"}">
|
|
1867
|
+
<div class="event-card-meta">
|
|
1868
|
+
<span>${escapeHtml(label)}</span>
|
|
1869
|
+
<span>${entry.at ? escapeHtml(new Date(entry.at).toLocaleString()) : ""}</span>
|
|
1870
|
+
</div>
|
|
1871
|
+
${content ? `<div class="chat-content">${escapeHtml(content)}</div>` : ""}
|
|
1872
|
+
</article>
|
|
1873
|
+
`;
|
|
1874
|
+
}
|
|
1875
|
+
|
|
1876
|
+
function renderStructuredEvent(entry) {
|
|
1877
|
+
const message = entry.message || entry.eventType || "";
|
|
1878
|
+
if (message === "plan.created") return renderPlanEvent(entry);
|
|
1879
|
+
if (message === "command.output") return renderCommandOutputLog(entry);
|
|
1880
|
+
if (message === "file.changed") return renderWorkspaceChangeEvent(entry);
|
|
1881
|
+
if (message === "tool.blocked" && entry.data?.permissionAdvice) return renderPermissionApproval(entry);
|
|
1882
|
+
if (message === "tool.blocked") {
|
|
1883
|
+
return `
|
|
1884
|
+
<article class="event-card event-failed">
|
|
1885
|
+
<div class="event-card-meta">
|
|
1886
|
+
<span>permission</span>
|
|
1887
|
+
<span>${entry.at ? escapeHtml(new Date(entry.at).toLocaleString()) : ""}</span>
|
|
1888
|
+
</div>
|
|
1889
|
+
<strong class="event-path">${escapeHtml(entry.data?.toolName || "blocked")}</strong>
|
|
1890
|
+
<div class="chat-content">${escapeHtml(entry.data?.reason || entry.content || "Permission blocked.")}</div>
|
|
1891
|
+
</article>
|
|
1892
|
+
`;
|
|
1893
|
+
}
|
|
1894
|
+
if (message === "tool.started" || message === "tool.completed" || message === "tool.failed" || message === "tool.skipped") {
|
|
1895
|
+
return renderToolEvent(entry);
|
|
1896
|
+
}
|
|
1897
|
+
if (
|
|
1898
|
+
message === "budget.initialized" ||
|
|
1899
|
+
message === "conversation.continued" ||
|
|
1900
|
+
message === "conversation.queued_input_applied" ||
|
|
1901
|
+
message === "session.finished" ||
|
|
1902
|
+
message === "session.failed" ||
|
|
1903
|
+
message === "session.stopped"
|
|
1904
|
+
) {
|
|
1905
|
+
return renderStatusEvent(entry);
|
|
1906
|
+
}
|
|
1907
|
+
return "";
|
|
1908
|
+
}
|
|
1909
|
+
|
|
1785
1910
|
function renderLogs(run) {
|
|
1786
1911
|
logsEl.dataset.mode = "active";
|
|
1787
1912
|
const parts = [
|
|
@@ -1791,24 +1916,12 @@ function renderLogs(run) {
|
|
|
1791
1916
|
];
|
|
1792
1917
|
|
|
1793
1918
|
for (const entry of run.logs || []) {
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
}
|
|
1798
|
-
if (entry.message === "plan.created" && entry.data?.plan) {
|
|
1799
|
-
parts.push(`
|
|
1800
|
-
<article class="log-plan">
|
|
1801
|
-
<div class="log-plan-title">${escapeHtml(`[${entry.at}] plan`)}</div>
|
|
1802
|
-
<div class="markdown-body">${renderMarkdown(entry.data.plan)}</div>
|
|
1803
|
-
</article>
|
|
1804
|
-
`);
|
|
1919
|
+
const structured = renderStructuredEvent(entry);
|
|
1920
|
+
if (structured) {
|
|
1921
|
+
parts.push(structured);
|
|
1805
1922
|
continue;
|
|
1806
1923
|
}
|
|
1807
1924
|
parts.push(`<div class="log-line">${escapeHtml(`[${entry.at}] ${entry.kind}: ${entry.message}`)}</div>`);
|
|
1808
|
-
if (entry.message === "tool.blocked" && entry.data?.permissionAdvice) {
|
|
1809
|
-
parts.push(renderPermissionApproval(entry));
|
|
1810
|
-
continue;
|
|
1811
|
-
}
|
|
1812
1925
|
if (entry.data && Object.keys(entry.data).length > 0) {
|
|
1813
1926
|
parts.push(`<pre class="log-json">${escapeHtml(JSON.stringify(entry.data, null, 2))}</pre>`);
|
|
1814
1927
|
}
|
|
@@ -2054,14 +2167,10 @@ function renderChat(chatEntries) {
|
|
|
2054
2167
|
chatThreadEl.innerHTML = lastChatEntries
|
|
2055
2168
|
.map((entry) => {
|
|
2056
2169
|
if (entry.role === "event") {
|
|
2057
|
-
|
|
2058
|
-
const content = entry.markdown
|
|
2059
|
-
? `<div class="markdown-body">${renderMarkdown(entry.content)}</div>`
|
|
2060
|
-
: escapeHtml(entry.content || "").replace(/\n/g, "<br>");
|
|
2061
|
-
return `
|
|
2170
|
+
return renderStructuredEvent(entry) || `
|
|
2062
2171
|
<article class="chat-item event" data-event-type="${escapeHtml(entry.eventType || "")}">
|
|
2063
|
-
<div class="chat-meta">${escapeHtml(
|
|
2064
|
-
<div class="chat-content">${content}</div>
|
|
2172
|
+
<div class="chat-meta">${escapeHtml(entry.eventLabel || entry.eventType || "event")}${entry.at ? ` · ${new Date(entry.at).toLocaleString()}` : ""}</div>
|
|
2173
|
+
<div class="chat-content">${escapeHtml(entry.content || "").replace(/\n/g, "<br>")}</div>
|
|
2065
2174
|
</article>
|
|
2066
2175
|
`;
|
|
2067
2176
|
}
|
package/public/styles.css
CHANGED
|
@@ -1155,23 +1155,6 @@ button.danger {
|
|
|
1155
1155
|
color: #fecaca;
|
|
1156
1156
|
}
|
|
1157
1157
|
|
|
1158
|
-
.log-plan {
|
|
1159
|
-
margin: 0 0 12px;
|
|
1160
|
-
padding: 12px;
|
|
1161
|
-
border: 1px solid rgba(94, 234, 212, 0.22);
|
|
1162
|
-
border-radius: 14px;
|
|
1163
|
-
background: rgba(15, 23, 42, 0.54);
|
|
1164
|
-
}
|
|
1165
|
-
|
|
1166
|
-
.log-plan-title {
|
|
1167
|
-
margin-bottom: 8px;
|
|
1168
|
-
color: #99f6e4;
|
|
1169
|
-
font-size: 0.82rem;
|
|
1170
|
-
font-weight: 800;
|
|
1171
|
-
letter-spacing: 0.04em;
|
|
1172
|
-
text-transform: uppercase;
|
|
1173
|
-
}
|
|
1174
|
-
|
|
1175
1158
|
.log-json,
|
|
1176
1159
|
.log-command pre {
|
|
1177
1160
|
margin: 8px 0 12px;
|
|
@@ -1242,6 +1225,126 @@ button.danger {
|
|
|
1242
1225
|
padding: 0 12px 8px;
|
|
1243
1226
|
}
|
|
1244
1227
|
|
|
1228
|
+
.event-card {
|
|
1229
|
+
width: auto;
|
|
1230
|
+
max-width: 100%;
|
|
1231
|
+
margin: 0 0 12px;
|
|
1232
|
+
padding: 12px;
|
|
1233
|
+
border: 1px solid rgba(20, 184, 166, 0.22);
|
|
1234
|
+
border-radius: 16px;
|
|
1235
|
+
background:
|
|
1236
|
+
radial-gradient(circle at 0% 0%, rgba(20, 184, 166, 0.14), transparent 28%),
|
|
1237
|
+
rgba(255, 255, 255, 0.46);
|
|
1238
|
+
box-shadow: 0 10px 24px rgba(15, 23, 42, 0.07);
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
#logs .event-card {
|
|
1242
|
+
background:
|
|
1243
|
+
radial-gradient(circle at 0% 0%, rgba(20, 184, 166, 0.1), transparent 26%),
|
|
1244
|
+
rgba(15, 23, 42, 0.52);
|
|
1245
|
+
color: #e5e7eb;
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
.event-plan {
|
|
1249
|
+
border-color: rgba(20, 184, 166, 0.34);
|
|
1250
|
+
background:
|
|
1251
|
+
radial-gradient(circle at 0% 0%, rgba(20, 184, 166, 0.18), transparent 28%),
|
|
1252
|
+
linear-gradient(180deg, rgba(240, 253, 250, 0.9), rgba(255, 251, 235, 0.74));
|
|
1253
|
+
}
|
|
1254
|
+
|
|
1255
|
+
#logs .event-plan {
|
|
1256
|
+
background: rgba(15, 23, 42, 0.58);
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
.event-patch {
|
|
1260
|
+
border-color: rgba(192, 132, 252, 0.38);
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
.event-write {
|
|
1264
|
+
border-color: rgba(59, 130, 246, 0.28);
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
.event-failed {
|
|
1268
|
+
border-color: rgba(248, 113, 113, 0.45);
|
|
1269
|
+
background: rgba(127, 29, 29, 0.12);
|
|
1270
|
+
}
|
|
1271
|
+
|
|
1272
|
+
#logs .event-failed {
|
|
1273
|
+
background: rgba(127, 29, 29, 0.3);
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
.event-muted {
|
|
1277
|
+
border-color: rgba(148, 163, 184, 0.24);
|
|
1278
|
+
background: rgba(148, 163, 184, 0.1);
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
.event-card-meta {
|
|
1282
|
+
display: flex;
|
|
1283
|
+
flex-wrap: wrap;
|
|
1284
|
+
gap: 8px;
|
|
1285
|
+
margin-bottom: 8px;
|
|
1286
|
+
color: var(--muted);
|
|
1287
|
+
font-size: 0.78rem;
|
|
1288
|
+
font-weight: 800;
|
|
1289
|
+
letter-spacing: 0.04em;
|
|
1290
|
+
text-transform: uppercase;
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
#logs .event-card-meta {
|
|
1294
|
+
color: #99f6e4;
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1297
|
+
.event-path {
|
|
1298
|
+
display: block;
|
|
1299
|
+
margin: 4px 0 8px;
|
|
1300
|
+
font-family: "IBM Plex Mono", "SFMono-Regular", monospace;
|
|
1301
|
+
font-size: 0.84rem;
|
|
1302
|
+
overflow-wrap: anywhere;
|
|
1303
|
+
}
|
|
1304
|
+
|
|
1305
|
+
.event-inline-code {
|
|
1306
|
+
display: block;
|
|
1307
|
+
width: fit-content;
|
|
1308
|
+
max-width: 100%;
|
|
1309
|
+
margin: 4px 0 8px;
|
|
1310
|
+
padding: 6px 8px;
|
|
1311
|
+
border-radius: 8px;
|
|
1312
|
+
background: rgba(15, 23, 42, 0.08);
|
|
1313
|
+
color: #78350f;
|
|
1314
|
+
font-family: "IBM Plex Mono", "SFMono-Regular", monospace;
|
|
1315
|
+
overflow-wrap: anywhere;
|
|
1316
|
+
white-space: pre-wrap;
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1319
|
+
#logs .event-inline-code {
|
|
1320
|
+
background: rgba(255, 255, 255, 0.08);
|
|
1321
|
+
color: #fef3c7;
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
.event-output {
|
|
1325
|
+
margin: 4px 0 10px;
|
|
1326
|
+
padding: 10px;
|
|
1327
|
+
border-radius: 10px;
|
|
1328
|
+
background: rgba(15, 23, 42, 0.84);
|
|
1329
|
+
color: #e5e7eb;
|
|
1330
|
+
white-space: pre-wrap;
|
|
1331
|
+
overflow: auto;
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1334
|
+
.event-fold-note {
|
|
1335
|
+
margin: 4px 0 8px;
|
|
1336
|
+
color: var(--muted);
|
|
1337
|
+
font-size: 0.8rem;
|
|
1338
|
+
}
|
|
1339
|
+
|
|
1340
|
+
#logs .event-fold-note {
|
|
1341
|
+
color: #94a3b8;
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
.event-diff {
|
|
1345
|
+
max-height: 360px;
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1245
1348
|
.chat-panel {
|
|
1246
1349
|
display: grid;
|
|
1247
1350
|
gap: 12px;
|
|
@@ -893,6 +893,9 @@ try {
|
|
|
893
893
|
if (!latest.stdout.includes(" plan ")) {
|
|
894
894
|
throw new Error("bare aginti resume did not replay saved plan/run context");
|
|
895
895
|
}
|
|
896
|
+
if (!latest.stdout.includes(" write ") || !latest.stdout.includes("+Created by AgInTiFlow mock mode.")) {
|
|
897
|
+
throw new Error("bare aginti resume did not replay formatted file-change diff context");
|
|
898
|
+
}
|
|
896
899
|
if (latest.stdout.includes("resume note=showing chat transcript only")) {
|
|
897
900
|
throw new Error("bare aginti resume regressed to chat-only history");
|
|
898
901
|
}
|
package/scripts/smoke-web-api.js
CHANGED
|
@@ -257,6 +257,11 @@ try {
|
|
|
257
257
|
if (fileRun.status !== "finished") throw new Error(`mock file run failed: ${fileRun.error || "unknown error"}`);
|
|
258
258
|
const hello = await fs.readFile(path.join(runtimeDir, "notes", "hello.md"), "utf8");
|
|
259
259
|
if (!hello.includes("Created by AgInTiFlow mock mode.")) throw new Error("mock file run did not create requested path");
|
|
260
|
+
const fileChat = await fetchJson(`/api/sessions/${encodeURIComponent(fileRunStart.sessionId)}/chat`);
|
|
261
|
+
const fileChange = fileChat.timeline?.find((entry) => entry.role === "event" && entry.eventType === "file.changed");
|
|
262
|
+
if (!fileChange?.data?.diff || !fileChange.data.diff.includes("+Created by AgInTiFlow mock mode.")) {
|
|
263
|
+
throw new Error("chat timeline did not preserve structured file-change diff data");
|
|
264
|
+
}
|
|
260
265
|
|
|
261
266
|
const safeRunStart = await fetchJson("/api/runs", {
|
|
262
267
|
method: "POST",
|
package/scripts/smoke-web-ui.js
CHANGED
|
@@ -56,7 +56,7 @@ async function waitForRunState(page, status, timeout = 20000) {
|
|
|
56
56
|
);
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
async function waitForTerminalRunState(page, timeout =
|
|
59
|
+
async function waitForTerminalRunState(page, timeout = 90000) {
|
|
60
60
|
await page.waitForFunction(
|
|
61
61
|
() => ["finished", "failed", "stopped"].includes(document.querySelector("#run-state")?.dataset.status || ""),
|
|
62
62
|
null,
|
|
@@ -92,6 +92,7 @@ try {
|
|
|
92
92
|
if (await page.locator("#run-defaults-card").evaluate((node) => node.open)) throw new Error("run defaults should start folded");
|
|
93
93
|
await page.locator("#commandCwd").fill(runtimeDir.slice(0, Math.max(runtimeDir.lastIndexOf("/"), 1)));
|
|
94
94
|
await page.waitForFunction(() => document.querySelectorAll("#command-cwd-suggestions option").length > 0);
|
|
95
|
+
await page.locator("#commandCwd").fill(runtimeDir);
|
|
95
96
|
if ((await page.locator(".project-status-chip").count()) < 4) throw new Error("project folder status did not render structured chips");
|
|
96
97
|
await page.locator("#run-defaults-card summary").click();
|
|
97
98
|
if (await page.locator("#veniceModeToggle").isChecked()) throw new Error("Venice quick mode should default off");
|
|
@@ -143,6 +144,14 @@ try {
|
|
|
143
144
|
await page.selectOption("#routingMode", "manual");
|
|
144
145
|
await page.selectOption("#provider", "mock");
|
|
145
146
|
await page.selectOption("#model", "mock-agent");
|
|
147
|
+
await page.selectOption("#enableScs", "off");
|
|
148
|
+
if (await page.locator("#aapsModeToggle").isChecked()) await page.locator("label:has(#aapsModeToggle)").click();
|
|
149
|
+
await page.selectOption("#taskProfile", "auto");
|
|
150
|
+
await page.evaluate(() => {
|
|
151
|
+
const input = document.querySelector("#allowShellTool");
|
|
152
|
+
input.checked = false;
|
|
153
|
+
input.dispatchEvent(new Event("change", { bubbles: true }));
|
|
154
|
+
});
|
|
146
155
|
await page.fill("#maxSteps", "4");
|
|
147
156
|
await page.selectOption("#dynamicSteps", "off");
|
|
148
157
|
await page.fill("#chat-input", "Say hello from the web UI composer in one concise sentence.");
|
|
@@ -151,18 +160,24 @@ try {
|
|
|
151
160
|
if (firstPayload.goal !== "Say hello from the web UI composer in one concise sentence.") {
|
|
152
161
|
throw new Error("composer text was not used as the first run goal");
|
|
153
162
|
}
|
|
154
|
-
if (firstPayload.enableScs !== "
|
|
163
|
+
if (firstPayload.enableScs !== "off" || firstPayload.taskProfile !== "auto") {
|
|
155
164
|
throw new Error(`CLI mode payload mismatch: ${runPayloads.at(-1) || ""}`);
|
|
156
165
|
}
|
|
157
166
|
if (firstPayload.dynamicSteps !== "off") {
|
|
158
167
|
throw new Error(`dynamic steps payload mismatch: ${runPayloads.at(-1) || ""}`);
|
|
159
168
|
}
|
|
169
|
+
if (firstPayload.allowShellTool !== false) {
|
|
170
|
+
throw new Error(`shell tool should be disabled for deterministic renderer smoke: ${runPayloads.at(-1) || ""}`);
|
|
171
|
+
}
|
|
160
172
|
if (firstPayload.veniceMode !== false || firstPayload.routeProvider !== "deepseek" || firstPayload.mainProvider !== "deepseek") {
|
|
161
173
|
throw new Error(`default route/main payload mismatch after Venice off: ${runPayloads.at(-1) || ""}`);
|
|
162
174
|
}
|
|
163
175
|
await waitForRunState(page, "running");
|
|
176
|
+
await page.waitForSelector(".event-plan", { timeout: 12000 });
|
|
177
|
+
if (!(await page.locator(".event-plan .markdown-body").first().innerText()).trim()) {
|
|
178
|
+
throw new Error("web run log did not render the plan as a formatted event card");
|
|
179
|
+
}
|
|
164
180
|
if (!(await page.locator("#stop-run").isVisible())) throw new Error("stop button did not appear while run was active");
|
|
165
|
-
await page.click("#stop-run");
|
|
166
181
|
await waitForTerminalRunState(page);
|
|
167
182
|
if (await page.locator("#stop-run").isVisible()) throw new Error("stop button stayed visible after terminal run state");
|
|
168
183
|
if (!(await page.locator(".toast").first().isVisible().catch(() => false))) {
|
|
@@ -173,6 +188,27 @@ try {
|
|
|
173
188
|
await waitForRunState(page, "idle");
|
|
174
189
|
const resetSubmit = await page.locator("#chat-submit").innerText();
|
|
175
190
|
if (!/start new run/i.test(resetSubmit)) throw new Error("new session did not reset composer to start mode");
|
|
191
|
+
if (await page.locator("#aapsModeToggle").isChecked()) await page.locator("label:has(#aapsModeToggle)").click();
|
|
192
|
+
await page.selectOption("#taskProfile", "code");
|
|
193
|
+
await page.selectOption("#enableScs", "off");
|
|
194
|
+
await page.fill("#chat-input", "Create notes/web-ui-format.md with a short formatted event smoke message.");
|
|
195
|
+
await page.click("#chat-submit");
|
|
196
|
+
await waitForTerminalRunState(page);
|
|
197
|
+
try {
|
|
198
|
+
await page.waitForFunction(
|
|
199
|
+
() =>
|
|
200
|
+
[...document.querySelectorAll(".event-write .change-diff")].some((node) =>
|
|
201
|
+
(node.textContent || "").includes("+Created by AgInTiFlow mock mode.")
|
|
202
|
+
),
|
|
203
|
+
null,
|
|
204
|
+
{ timeout: 30000 }
|
|
205
|
+
);
|
|
206
|
+
} catch (error) {
|
|
207
|
+
const state = await page.locator("#run-state").evaluate((node) => node.dataset.status || "").catch(() => "unknown");
|
|
208
|
+
const logsTail = (await page.locator("#logs").innerText().catch(() => "")).slice(-1200);
|
|
209
|
+
const chatTail = (await page.locator("#chat-thread").innerText().catch(() => "")).slice(-1200);
|
|
210
|
+
throw new Error(`web UI did not render formatted file-change diff event; state=${state}\nlogs tail:\n${logsTail}\nchat tail:\n${chatTail}`);
|
|
211
|
+
}
|
|
176
212
|
|
|
177
213
|
await page.click("#open-settings");
|
|
178
214
|
await page.waitForSelector("#settings-modal[open]");
|
|
@@ -208,6 +244,8 @@ try {
|
|
|
208
244
|
"running-status-toast",
|
|
209
245
|
"terminal-stop-button-hidden",
|
|
210
246
|
"new-session-resets-scope",
|
|
247
|
+
"formatted-plan-event-card",
|
|
248
|
+
"formatted-file-diff-event-card",
|
|
211
249
|
"settings-provider-dropdowns",
|
|
212
250
|
"settings-wrapper-dropdowns",
|
|
213
251
|
],
|
|
@@ -51,6 +51,26 @@ async function killPort(port) {
|
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
async function stopChild(child) {
|
|
55
|
+
if (!child || child.exitCode !== null) return;
|
|
56
|
+
try {
|
|
57
|
+
child.stdin?.write("/exit\n");
|
|
58
|
+
} catch {
|
|
59
|
+
// The CLI may have closed stdin after the final prompt; fall back to process signals.
|
|
60
|
+
}
|
|
61
|
+
const exited = await Promise.race([
|
|
62
|
+
new Promise((resolve) => child.once("exit", () => resolve(true))),
|
|
63
|
+
delay(2000).then(() => false),
|
|
64
|
+
]);
|
|
65
|
+
if (exited || child.exitCode !== null) return;
|
|
66
|
+
child.kill("SIGTERM");
|
|
67
|
+
const terminated = await Promise.race([
|
|
68
|
+
new Promise((resolve) => child.once("exit", () => resolve(true))),
|
|
69
|
+
delay(2000).then(() => false),
|
|
70
|
+
]);
|
|
71
|
+
if (!terminated && child.exitCode === null) child.kill("SIGKILL");
|
|
72
|
+
}
|
|
73
|
+
|
|
54
74
|
async function runCase({ port, env = {}, expectHeader, label }) {
|
|
55
75
|
const output = { stdout: "", stderr: "" };
|
|
56
76
|
const child = spawn(process.execPath, [path.join(repoRoot, "bin/aginti-cli.js"), "chat", "--provider", "mock", "--routing", "manual", "--port", String(port)], {
|
|
@@ -109,8 +129,9 @@ async function runCase({ port, env = {}, expectHeader, label }) {
|
|
|
109
129
|
child.stdin.write(`/webapp ${active.port}\n`);
|
|
110
130
|
await waitFor(() => latestWebappEvent(output.stdout, "started|reused")?.port === active.port, child, `${label} /webapp restart after stop`, output);
|
|
111
131
|
} finally {
|
|
112
|
-
child
|
|
132
|
+
await stopChild(child);
|
|
113
133
|
await killPort(port);
|
|
134
|
+
await delay(250);
|
|
114
135
|
}
|
|
115
136
|
}
|
|
116
137
|
|
|
@@ -133,5 +154,6 @@ try {
|
|
|
133
154
|
} finally {
|
|
134
155
|
await killPort(autoPort);
|
|
135
156
|
await killPort(manualPort);
|
|
136
|
-
await
|
|
157
|
+
await delay(250);
|
|
158
|
+
await fs.rm(runtimeDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
|
137
159
|
}
|
package/src/interactive-cli.js
CHANGED
|
@@ -2275,6 +2275,10 @@ function printResumeEvent(event = {}) {
|
|
|
2275
2275
|
}
|
|
2276
2276
|
if (type === "tool.completed" || type === "tool.failed" || type === "tool.skipped") {
|
|
2277
2277
|
const failed = type === "tool.failed" || data.ok === false || data.blocked || data.error;
|
|
2278
|
+
if (data.diff) {
|
|
2279
|
+
printWorkspaceChange(data);
|
|
2280
|
+
return true;
|
|
2281
|
+
}
|
|
2278
2282
|
if (data.toolName === "run_command") {
|
|
2279
2283
|
printCommandOutputLog({
|
|
2280
2284
|
command: data.args?.command || data.command || "",
|
package/web.js
CHANGED
|
@@ -129,6 +129,7 @@ function timelineEntryForEvent(event = {}) {
|
|
|
129
129
|
role: "event",
|
|
130
130
|
eventType: type,
|
|
131
131
|
eventLabel: "plan",
|
|
132
|
+
data,
|
|
132
133
|
content: data.plan,
|
|
133
134
|
markdown: true,
|
|
134
135
|
at,
|
|
@@ -139,6 +140,7 @@ function timelineEntryForEvent(event = {}) {
|
|
|
139
140
|
role: "event",
|
|
140
141
|
eventType: type,
|
|
141
142
|
eventLabel: "tool",
|
|
143
|
+
data,
|
|
142
144
|
content: toolTimelineSummary(data),
|
|
143
145
|
at,
|
|
144
146
|
};
|
|
@@ -151,6 +153,7 @@ function timelineEntryForEvent(event = {}) {
|
|
|
151
153
|
role: "event",
|
|
152
154
|
eventType: type,
|
|
153
155
|
eventLabel: failed ? "tool failed" : "tool done",
|
|
156
|
+
data,
|
|
154
157
|
content: `${toolTimelineSummary(data)}${stdout}${stderr}`,
|
|
155
158
|
at,
|
|
156
159
|
};
|
|
@@ -160,6 +163,7 @@ function timelineEntryForEvent(event = {}) {
|
|
|
160
163
|
role: "event",
|
|
161
164
|
eventType: type,
|
|
162
165
|
eventLabel: "permission",
|
|
166
|
+
data,
|
|
163
167
|
content: data.permissionAdvice?.summary || data.reason || data.toolName || "Permission blocked.",
|
|
164
168
|
at,
|
|
165
169
|
};
|
|
@@ -169,6 +173,7 @@ function timelineEntryForEvent(event = {}) {
|
|
|
169
173
|
role: "event",
|
|
170
174
|
eventType: type,
|
|
171
175
|
eventLabel: data.toolName === "apply_patch" ? "patch" : "write",
|
|
176
|
+
data,
|
|
172
177
|
content: [data.toolName || data.action || "file.changed", data.path || "", data.created ? "created" : "updated"]
|
|
173
178
|
.filter(Boolean)
|
|
174
179
|
.join(" "),
|
|
@@ -180,6 +185,7 @@ function timelineEntryForEvent(event = {}) {
|
|
|
180
185
|
role: "event",
|
|
181
186
|
eventType: type,
|
|
182
187
|
eventLabel: "budget",
|
|
188
|
+
data,
|
|
183
189
|
content: `${data.currentMaxSteps || data.initialMaxSteps || data.maxSteps || "unknown"} steps`,
|
|
184
190
|
at,
|
|
185
191
|
};
|
|
@@ -189,6 +195,7 @@ function timelineEntryForEvent(event = {}) {
|
|
|
189
195
|
role: "event",
|
|
190
196
|
eventType: type,
|
|
191
197
|
eventLabel: "continued",
|
|
198
|
+
data,
|
|
192
199
|
content: data.prompt || "",
|
|
193
200
|
at,
|
|
194
201
|
};
|
|
@@ -198,6 +205,7 @@ function timelineEntryForEvent(event = {}) {
|
|
|
198
205
|
role: "event",
|
|
199
206
|
eventType: type,
|
|
200
207
|
eventLabel: "queued input",
|
|
208
|
+
data,
|
|
201
209
|
content: data.priority === "asap" ? "ASAP queued input applied" : "Queued input applied",
|
|
202
210
|
at,
|
|
203
211
|
};
|
|
@@ -207,6 +215,7 @@ function timelineEntryForEvent(event = {}) {
|
|
|
207
215
|
role: "event",
|
|
208
216
|
eventType: type,
|
|
209
217
|
eventLabel: type.replace("session.", ""),
|
|
218
|
+
data,
|
|
210
219
|
content: data.result || data.error || data.reason || type,
|
|
211
220
|
at,
|
|
212
221
|
};
|