@lazyingart/agintiflow 0.20.172 → 0.20.174
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 +242 -23
- package/public/styles.css +154 -17
- package/scripts/smoke-cli-chat.js +6 -0
- package/scripts/smoke-web-api.js +5 -0
- package/scripts/smoke-web-ui.js +57 -3
- package/scripts/smoke-webapp-command.js +24 -2
- package/src/interactive-cli.js +105 -1
- 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.174",
|
|
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,33 +1782,234 @@ 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 embeddedWorkspaceChangesFromText(value = "") {
|
|
1800
|
+
const lines = String(value || "").split(/\r?\n/);
|
|
1801
|
+
const changes = [];
|
|
1802
|
+
let currentTool = "";
|
|
1803
|
+
|
|
1804
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
1805
|
+
const toolMatch = lines[index].match(/^\s*Tool:\s*(.+?)\s*$/i);
|
|
1806
|
+
if (toolMatch) {
|
|
1807
|
+
currentTool = toolMatch[1].trim();
|
|
1808
|
+
continue;
|
|
1809
|
+
}
|
|
1810
|
+
|
|
1811
|
+
const pathMatch = lines[index].match(/^\s*Path:\s*(.+?)\s*$/i);
|
|
1812
|
+
if (!pathMatch) continue;
|
|
1813
|
+
|
|
1814
|
+
let cursor = index + 1;
|
|
1815
|
+
while (cursor < lines.length && !/^\s*Diff:\s*$/i.test(lines[cursor]) && !/^\s*(?:Tool|Path):\s*/i.test(lines[cursor])) {
|
|
1816
|
+
cursor += 1;
|
|
1817
|
+
}
|
|
1818
|
+
if (!/^\s*Diff:\s*$/i.test(lines[cursor] || "")) continue;
|
|
1819
|
+
|
|
1820
|
+
const diffLines = [];
|
|
1821
|
+
cursor += 1;
|
|
1822
|
+
while (cursor < lines.length) {
|
|
1823
|
+
if (diffLines.length && /^\s*(?:Tool|Path):\s*/i.test(lines[cursor])) break;
|
|
1824
|
+
if (
|
|
1825
|
+
diffLines.length &&
|
|
1826
|
+
/^\s*(?:Output:|No command output|Blocked by guardrail\.|Mock run complete\.)/i.test(lines[cursor]) &&
|
|
1827
|
+
!/^[-+@ ]/.test(lines[cursor])
|
|
1828
|
+
) {
|
|
1829
|
+
break;
|
|
1830
|
+
}
|
|
1831
|
+
diffLines.push(lines[cursor]);
|
|
1832
|
+
cursor += 1;
|
|
1833
|
+
}
|
|
1834
|
+
while (diffLines.length && !diffLines[diffLines.length - 1].trim()) diffLines.pop();
|
|
1835
|
+
if (diffLines.length) {
|
|
1836
|
+
changes.push({
|
|
1837
|
+
toolName: currentTool || "write_file",
|
|
1838
|
+
path: pathMatch[1].trim(),
|
|
1839
|
+
diff: diffLines.join("\n"),
|
|
1840
|
+
});
|
|
1841
|
+
index = Math.max(index, cursor - 1);
|
|
1842
|
+
}
|
|
1843
|
+
}
|
|
1844
|
+
|
|
1845
|
+
return changes;
|
|
1846
|
+
}
|
|
1847
|
+
|
|
1848
|
+
function embeddedWorkspaceSummary(value = "") {
|
|
1849
|
+
const summary = [];
|
|
1850
|
+
let skippingDiff = false;
|
|
1851
|
+
for (const line of String(value || "").split(/\r?\n/)) {
|
|
1852
|
+
if (/^\s*Diff:\s*$/i.test(line)) {
|
|
1853
|
+
skippingDiff = true;
|
|
1854
|
+
continue;
|
|
1855
|
+
}
|
|
1856
|
+
if (skippingDiff && /^\s*(?:Tool|Path):\s*/i.test(line)) skippingDiff = false;
|
|
1857
|
+
if (skippingDiff) continue;
|
|
1858
|
+
if (/^\s*Path:\s*/i.test(line)) continue;
|
|
1859
|
+
summary.push(line);
|
|
1860
|
+
}
|
|
1861
|
+
return summary.join("\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
1862
|
+
}
|
|
1863
|
+
|
|
1864
|
+
function renderEmbeddedWorkspaceResult(value = "", entry = {}) {
|
|
1865
|
+
const changes = embeddedWorkspaceChangesFromText(value);
|
|
1866
|
+
if (changes.length === 0) return "";
|
|
1867
|
+
const summary = embeddedWorkspaceSummary(value);
|
|
1868
|
+
return `
|
|
1869
|
+
${summary ? `<div class="event-result-summary markdown-body">${renderMarkdown(summary)}</div>` : ""}
|
|
1870
|
+
<div class="embedded-change-list">
|
|
1871
|
+
${changes.map((change) => renderWorkspaceChangeEvent({ data: change, at: entry.at, content: change.path })).join("")}
|
|
1872
|
+
</div>
|
|
1873
|
+
`;
|
|
1874
|
+
}
|
|
1875
|
+
|
|
1876
|
+
function renderWorkspaceChangeEvent(entry) {
|
|
1877
|
+
const data = entry.data || {};
|
|
1878
|
+
const toolName = data.toolName || data.action || "file.changed";
|
|
1879
|
+
const isPatch = toolName === "apply_patch" || String(toolName).startsWith("apply_patch");
|
|
1880
|
+
const diff = String(data.diff || "");
|
|
1881
|
+
const maxDiffChars = 9000;
|
|
1882
|
+
const diffBlock = diff
|
|
1883
|
+
? `<pre class="change-diff event-diff">${renderDiffHtml(diff, maxDiffChars)}</pre>${
|
|
1884
|
+
diff.length > maxDiffChars ? `<div class="event-fold-note">... diff truncated for browser preview</div>` : ""
|
|
1885
|
+
}`
|
|
1886
|
+
: "";
|
|
1887
|
+
const hashes =
|
|
1888
|
+
data.beforeHash || data.afterHash
|
|
1889
|
+
? `<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>`
|
|
1890
|
+
: "";
|
|
1891
|
+
return `
|
|
1892
|
+
<article class="event-card ${isPatch ? "event-patch" : "event-write"}">
|
|
1893
|
+
<div class="event-card-meta">
|
|
1894
|
+
<span>${escapeHtml(isPatch ? "patch" : "write")}</span>
|
|
1895
|
+
<span>${escapeHtml(toolName)}</span>
|
|
1896
|
+
<span>${entry.at ? escapeHtml(new Date(entry.at).toLocaleString()) : ""}</span>
|
|
1897
|
+
</div>
|
|
1898
|
+
<strong class="event-path">${escapeHtml(data.path || entry.content || "")}</strong>
|
|
1899
|
+
${data.created ? `<div class="event-fold-note">created</div>` : ""}
|
|
1900
|
+
${hashes}
|
|
1901
|
+
${diffBlock || `<div class="event-fold-note">No diff preview recorded for this file event.</div>`}
|
|
1902
|
+
</article>
|
|
1903
|
+
`;
|
|
1904
|
+
}
|
|
1905
|
+
|
|
1906
|
+
function renderToolEvent(entry) {
|
|
1907
|
+
const data = entry.data || {};
|
|
1908
|
+
const message = entry.message || entry.eventType || "";
|
|
1909
|
+
const failed = message === "tool.failed" || data.ok === false || data.blocked || data.error;
|
|
1910
|
+
const skipped = message === "tool.skipped";
|
|
1911
|
+
const toolName = data.toolName || "tool";
|
|
1912
|
+
const args = data.args || {};
|
|
1913
|
+
const resultText = String(data.result || args.result || "");
|
|
1914
|
+
const embeddedResult = resultText ? renderEmbeddedWorkspaceResult(resultText, entry) : "";
|
|
1915
|
+
const argPreview =
|
|
1916
|
+
toolName === "finish"
|
|
1917
|
+
? ""
|
|
1918
|
+
: toolName === "run_command" && args.command
|
|
1919
|
+
? args.command
|
|
1920
|
+
: args.path || args.url || args.query || args.q || (Object.keys(args).length ? JSON.stringify(args) : "");
|
|
1921
|
+
const stdout = data.stdout ? outputPreviewText(data.stdout) : null;
|
|
1922
|
+
const stderr = data.stderr ? outputPreviewText(data.stderr) : null;
|
|
1923
|
+
return `
|
|
1924
|
+
<article class="event-card ${failed ? "event-failed" : skipped ? "event-muted" : toolName === "finish" ? "event-finish" : "event-tool"}">
|
|
1925
|
+
<div class="event-card-meta">
|
|
1926
|
+
<span>${escapeHtml(failed ? "tool failed" : skipped ? "tool skipped" : message === "tool.started" ? "tool" : "tool done")}</span>
|
|
1927
|
+
<span>${entry.at ? escapeHtml(new Date(entry.at).toLocaleString()) : ""}</span>
|
|
1928
|
+
</div>
|
|
1929
|
+
<strong class="event-path">${escapeHtml(toolName)}</strong>
|
|
1930
|
+
${argPreview ? `<code class="event-inline-code">${escapeHtml(argPreview)}</code>` : ""}
|
|
1931
|
+
${data.error || data.reason ? `<div class="event-fold-note">${escapeHtml(data.error || data.reason)}</div>` : ""}
|
|
1932
|
+
${embeddedResult || (toolName === "finish" && resultText ? `<div class="markdown-body">${renderMarkdown(resultText)}</div>` : "")}
|
|
1933
|
+
${stdout ? `<div class="log-stream-title">stdout</div><pre class="event-output">${escapeHtml(stdout.text)}</pre>` : ""}
|
|
1934
|
+
${stdout?.hidden > 0 ? `<div class="event-fold-note">... ${stdout.hidden} more stdout line(s) folded</div>` : ""}
|
|
1935
|
+
${stderr ? `<div class="log-stream-title">stderr</div><pre class="event-output">${escapeHtml(stderr.text)}</pre>` : ""}
|
|
1936
|
+
${stderr?.hidden > 0 ? `<div class="event-fold-note">... ${stderr.hidden} more stderr line(s) folded</div>` : ""}
|
|
1937
|
+
</article>
|
|
1938
|
+
`;
|
|
1939
|
+
}
|
|
1940
|
+
|
|
1941
|
+
function renderStatusEvent(entry) {
|
|
1942
|
+
const data = entry.data || {};
|
|
1943
|
+
const message = entry.message || entry.eventType || "event";
|
|
1944
|
+
const label = entry.eventLabel || message.replace(/^[^.]+\./, "");
|
|
1945
|
+
const content = data.result || data.error || data.reason || entry.content || "";
|
|
1946
|
+
const failed = message === "session.failed";
|
|
1947
|
+
const embeddedResult = content ? renderEmbeddedWorkspaceResult(content, entry) : "";
|
|
1948
|
+
return `
|
|
1949
|
+
<article class="event-card ${failed ? "event-failed" : message === "session.finished" && embeddedResult ? "event-finish" : "event-muted"}">
|
|
1950
|
+
<div class="event-card-meta">
|
|
1951
|
+
<span>${escapeHtml(label)}</span>
|
|
1952
|
+
<span>${entry.at ? escapeHtml(new Date(entry.at).toLocaleString()) : ""}</span>
|
|
1953
|
+
</div>
|
|
1954
|
+
${embeddedResult || (content ? `<div class="chat-content">${escapeHtml(content)}</div>` : "")}
|
|
1955
|
+
</article>
|
|
1956
|
+
`;
|
|
1957
|
+
}
|
|
1958
|
+
|
|
1959
|
+
function renderStructuredEvent(entry) {
|
|
1960
|
+
const message = entry.message || entry.eventType || "";
|
|
1961
|
+
if (message === "plan.created") return renderPlanEvent(entry);
|
|
1962
|
+
if (message === "command.output") return renderCommandOutputLog(entry);
|
|
1963
|
+
if (message === "file.changed") return renderWorkspaceChangeEvent(entry);
|
|
1964
|
+
if (message === "tool.blocked" && entry.data?.permissionAdvice) return renderPermissionApproval(entry);
|
|
1965
|
+
if (message === "tool.blocked") {
|
|
1966
|
+
return `
|
|
1967
|
+
<article class="event-card event-failed">
|
|
1968
|
+
<div class="event-card-meta">
|
|
1969
|
+
<span>permission</span>
|
|
1970
|
+
<span>${entry.at ? escapeHtml(new Date(entry.at).toLocaleString()) : ""}</span>
|
|
1971
|
+
</div>
|
|
1972
|
+
<strong class="event-path">${escapeHtml(entry.data?.toolName || "blocked")}</strong>
|
|
1973
|
+
<div class="chat-content">${escapeHtml(entry.data?.reason || entry.content || "Permission blocked.")}</div>
|
|
1974
|
+
</article>
|
|
1975
|
+
`;
|
|
1976
|
+
}
|
|
1977
|
+
if (message === "tool.started" || message === "tool.completed" || message === "tool.failed" || message === "tool.skipped") {
|
|
1978
|
+
return renderToolEvent(entry);
|
|
1979
|
+
}
|
|
1980
|
+
if (
|
|
1981
|
+
message === "budget.initialized" ||
|
|
1982
|
+
message === "conversation.continued" ||
|
|
1983
|
+
message === "conversation.queued_input_applied" ||
|
|
1984
|
+
message === "session.finished" ||
|
|
1985
|
+
message === "session.failed" ||
|
|
1986
|
+
message === "session.stopped"
|
|
1987
|
+
) {
|
|
1988
|
+
return renderStatusEvent(entry);
|
|
1989
|
+
}
|
|
1990
|
+
return "";
|
|
1991
|
+
}
|
|
1992
|
+
|
|
1785
1993
|
function renderLogs(run) {
|
|
1786
1994
|
logsEl.dataset.mode = "active";
|
|
1995
|
+
const structuredRunResult = run.result ? renderEmbeddedWorkspaceResult(run.result, { at: run.endedAt || run.updatedAt || "" }) : "";
|
|
1787
1996
|
const parts = [
|
|
1788
1997
|
`<div class="log-line">${escapeHtml(`status=${run.status} session=${run.sessionId} provider=${run.provider} model=${run.model}`)}</div>`,
|
|
1789
|
-
|
|
1998
|
+
structuredRunResult
|
|
1999
|
+
? `<article class="event-card event-finish"><div class="event-card-meta"><span>result</span></div>${structuredRunResult}</article>`
|
|
2000
|
+
: run.result
|
|
2001
|
+
? `<div class="log-line">${escapeHtml(`result=${run.result}`)}</div>`
|
|
2002
|
+
: "",
|
|
1790
2003
|
run.error ? `<div class="log-line error">${escapeHtml(`error=${run.error}`)}</div>` : "",
|
|
1791
2004
|
];
|
|
1792
2005
|
|
|
1793
2006
|
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
|
-
`);
|
|
2007
|
+
const structured = renderStructuredEvent(entry);
|
|
2008
|
+
if (structured) {
|
|
2009
|
+
parts.push(structured);
|
|
1805
2010
|
continue;
|
|
1806
2011
|
}
|
|
1807
2012
|
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
2013
|
if (entry.data && Object.keys(entry.data).length > 0) {
|
|
1813
2014
|
parts.push(`<pre class="log-json">${escapeHtml(JSON.stringify(entry.data, null, 2))}</pre>`);
|
|
1814
2015
|
}
|
|
@@ -2054,17 +2255,35 @@ function renderChat(chatEntries) {
|
|
|
2054
2255
|
chatThreadEl.innerHTML = lastChatEntries
|
|
2055
2256
|
.map((entry) => {
|
|
2056
2257
|
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 `
|
|
2258
|
+
return renderStructuredEvent(entry) || `
|
|
2062
2259
|
<article class="chat-item event" data-event-type="${escapeHtml(entry.eventType || "")}">
|
|
2063
|
-
<div class="chat-meta">${escapeHtml(
|
|
2064
|
-
<div class="chat-content">${content}</div>
|
|
2260
|
+
<div class="chat-meta">${escapeHtml(entry.eventLabel || entry.eventType || "event")}${entry.at ? ` · ${new Date(entry.at).toLocaleString()}` : ""}</div>
|
|
2261
|
+
<div class="chat-content">${escapeHtml(entry.content || "").replace(/\n/g, "<br>")}</div>
|
|
2065
2262
|
</article>
|
|
2066
2263
|
`;
|
|
2067
2264
|
}
|
|
2265
|
+
if (entry.role === "tool") {
|
|
2266
|
+
try {
|
|
2267
|
+
const parsed = JSON.parse(entry.content || "{}");
|
|
2268
|
+
const toolName = parsed.toolName || "tool";
|
|
2269
|
+
return renderToolEvent({
|
|
2270
|
+
role: "event",
|
|
2271
|
+
eventType: parsed.ok === false ? "tool.failed" : "tool.completed",
|
|
2272
|
+
message: parsed.ok === false ? "tool.failed" : "tool.completed",
|
|
2273
|
+
eventLabel: parsed.ok === false ? "tool failed" : "tool done",
|
|
2274
|
+
data: parsed,
|
|
2275
|
+
content: parsed.result || toolName,
|
|
2276
|
+
at: entry.at,
|
|
2277
|
+
});
|
|
2278
|
+
} catch {
|
|
2279
|
+
return `
|
|
2280
|
+
<article class="event-card event-tool">
|
|
2281
|
+
<div class="event-card-meta">tool${entry.at ? ` · ${new Date(entry.at).toLocaleString()}` : ""}</div>
|
|
2282
|
+
<pre class="event-output">${escapeHtml(entry.content || "")}</pre>
|
|
2283
|
+
</article>
|
|
2284
|
+
`;
|
|
2285
|
+
}
|
|
2286
|
+
}
|
|
2068
2287
|
const role = entry.role === "assistant" ? "assistant" : "user";
|
|
2069
2288
|
const label = role === "assistant" ? t("assistantLabel") : t("youLabel");
|
|
2070
2289
|
const content =
|
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,160 @@ 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-finish {
|
|
1268
|
+
border-color: rgba(16, 185, 129, 0.36);
|
|
1269
|
+
background:
|
|
1270
|
+
radial-gradient(circle at 0% 0%, rgba(16, 185, 129, 0.14), transparent 30%),
|
|
1271
|
+
rgba(236, 253, 245, 0.46);
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
#logs .event-finish {
|
|
1275
|
+
background:
|
|
1276
|
+
radial-gradient(circle at 0% 0%, rgba(16, 185, 129, 0.16), transparent 30%),
|
|
1277
|
+
rgba(6, 78, 59, 0.28);
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1280
|
+
.event-failed {
|
|
1281
|
+
border-color: rgba(248, 113, 113, 0.45);
|
|
1282
|
+
background: rgba(127, 29, 29, 0.12);
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1285
|
+
#logs .event-failed {
|
|
1286
|
+
background: rgba(127, 29, 29, 0.3);
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1289
|
+
.event-muted {
|
|
1290
|
+
border-color: rgba(148, 163, 184, 0.24);
|
|
1291
|
+
background: rgba(148, 163, 184, 0.1);
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
.event-card-meta {
|
|
1295
|
+
display: flex;
|
|
1296
|
+
flex-wrap: wrap;
|
|
1297
|
+
gap: 8px;
|
|
1298
|
+
margin-bottom: 8px;
|
|
1299
|
+
color: var(--muted);
|
|
1300
|
+
font-size: 0.78rem;
|
|
1301
|
+
font-weight: 800;
|
|
1302
|
+
letter-spacing: 0.04em;
|
|
1303
|
+
text-transform: uppercase;
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
#logs .event-card-meta {
|
|
1307
|
+
color: #99f6e4;
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
.event-path {
|
|
1311
|
+
display: block;
|
|
1312
|
+
margin: 4px 0 8px;
|
|
1313
|
+
font-family: "IBM Plex Mono", "SFMono-Regular", monospace;
|
|
1314
|
+
font-size: 0.84rem;
|
|
1315
|
+
overflow-wrap: anywhere;
|
|
1316
|
+
}
|
|
1317
|
+
|
|
1318
|
+
.event-inline-code {
|
|
1319
|
+
display: block;
|
|
1320
|
+
width: fit-content;
|
|
1321
|
+
max-width: 100%;
|
|
1322
|
+
margin: 4px 0 8px;
|
|
1323
|
+
padding: 6px 8px;
|
|
1324
|
+
border-radius: 8px;
|
|
1325
|
+
background: rgba(15, 23, 42, 0.08);
|
|
1326
|
+
color: #78350f;
|
|
1327
|
+
font-family: "IBM Plex Mono", "SFMono-Regular", monospace;
|
|
1328
|
+
overflow-wrap: anywhere;
|
|
1329
|
+
white-space: pre-wrap;
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1332
|
+
#logs .event-inline-code {
|
|
1333
|
+
background: rgba(255, 255, 255, 0.08);
|
|
1334
|
+
color: #fef3c7;
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1337
|
+
.event-output {
|
|
1338
|
+
margin: 4px 0 10px;
|
|
1339
|
+
padding: 10px;
|
|
1340
|
+
border-radius: 10px;
|
|
1341
|
+
background: rgba(15, 23, 42, 0.84);
|
|
1342
|
+
color: #e5e7eb;
|
|
1343
|
+
white-space: pre-wrap;
|
|
1344
|
+
overflow: auto;
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
.event-fold-note {
|
|
1348
|
+
margin: 4px 0 8px;
|
|
1349
|
+
color: var(--muted);
|
|
1350
|
+
font-size: 0.8rem;
|
|
1351
|
+
}
|
|
1352
|
+
|
|
1353
|
+
.event-result-summary {
|
|
1354
|
+
margin: 4px 0 12px;
|
|
1355
|
+
padding: 10px 12px;
|
|
1356
|
+
border-radius: 12px;
|
|
1357
|
+
background: rgba(15, 118, 110, 0.08);
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1360
|
+
#logs .event-result-summary {
|
|
1361
|
+
background: rgba(255, 255, 255, 0.08);
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
.embedded-change-list {
|
|
1365
|
+
display: grid;
|
|
1366
|
+
gap: 8px;
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1369
|
+
.embedded-change-list > .event-card {
|
|
1370
|
+
margin: 0;
|
|
1371
|
+
box-shadow: none;
|
|
1372
|
+
}
|
|
1373
|
+
|
|
1374
|
+
#logs .event-fold-note {
|
|
1375
|
+
color: #94a3b8;
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1378
|
+
.event-diff {
|
|
1379
|
+
max-height: 360px;
|
|
1380
|
+
}
|
|
1381
|
+
|
|
1245
1382
|
.chat-panel {
|
|
1246
1383
|
display: grid;
|
|
1247
1384
|
gap: 12px;
|
|
@@ -893,6 +893,12 @@ 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
|
+
}
|
|
899
|
+
if (!/\bfinish\b/.test(latest.stdout) || latest.stdout.includes('{"result":"Mock run complete')) {
|
|
900
|
+
throw new Error("bare aginti resume did not render finish-result diffs in formatted mode");
|
|
901
|
+
}
|
|
896
902
|
if (latest.stdout.includes("resume note=showing chat transcript only")) {
|
|
897
903
|
throw new Error("bare aginti resume regressed to chat-only history");
|
|
898
904
|
}
|
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,42 @@ 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
|
+
}
|
|
212
|
+
try {
|
|
213
|
+
await page.waitForFunction(
|
|
214
|
+
() =>
|
|
215
|
+
[...document.querySelectorAll(".event-finish .change-diff")].some((node) =>
|
|
216
|
+
(node.textContent || "").includes("+Created by AgInTiFlow mock mode.")
|
|
217
|
+
),
|
|
218
|
+
null,
|
|
219
|
+
{ timeout: 30000 }
|
|
220
|
+
);
|
|
221
|
+
} catch (error) {
|
|
222
|
+
const state = await page.locator("#run-state").evaluate((node) => node.dataset.status || "").catch(() => "unknown");
|
|
223
|
+
const logsTail = (await page.locator("#logs").innerText().catch(() => "")).slice(-1200);
|
|
224
|
+
const chatTail = (await page.locator("#chat-thread").innerText().catch(() => "")).slice(-1200);
|
|
225
|
+
throw new Error(`web UI did not render formatted finish-result diff event; state=${state}\nlogs tail:\n${logsTail}\nchat tail:\n${chatTail}`);
|
|
226
|
+
}
|
|
176
227
|
|
|
177
228
|
await page.click("#open-settings");
|
|
178
229
|
await page.waitForSelector("#settings-modal[open]");
|
|
@@ -208,6 +259,9 @@ try {
|
|
|
208
259
|
"running-status-toast",
|
|
209
260
|
"terminal-stop-button-hidden",
|
|
210
261
|
"new-session-resets-scope",
|
|
262
|
+
"formatted-plan-event-card",
|
|
263
|
+
"formatted-file-diff-event-card",
|
|
264
|
+
"formatted-finish-result-diff-card",
|
|
211
265
|
"settings-provider-dropdowns",
|
|
212
266
|
"settings-wrapper-dropdowns",
|
|
213
267
|
],
|
|
@@ -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
|
@@ -778,6 +778,71 @@ export function formatWorkspaceChange(change = {}) {
|
|
|
778
778
|
};
|
|
779
779
|
}
|
|
780
780
|
|
|
781
|
+
function embeddedWorkspaceChangesFromText(value = "") {
|
|
782
|
+
const lines = String(value || "").split(/\r?\n/);
|
|
783
|
+
const changes = [];
|
|
784
|
+
let currentTool = "";
|
|
785
|
+
|
|
786
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
787
|
+
const toolMatch = lines[index].match(/^\s*Tool:\s*(.+?)\s*$/i);
|
|
788
|
+
if (toolMatch) {
|
|
789
|
+
currentTool = toolMatch[1].trim();
|
|
790
|
+
continue;
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
const pathMatch = lines[index].match(/^\s*Path:\s*(.+?)\s*$/i);
|
|
794
|
+
if (!pathMatch) continue;
|
|
795
|
+
|
|
796
|
+
let cursor = index + 1;
|
|
797
|
+
while (cursor < lines.length && !/^\s*Diff:\s*$/i.test(lines[cursor]) && !/^\s*(?:Tool|Path):\s*/i.test(lines[cursor])) {
|
|
798
|
+
cursor += 1;
|
|
799
|
+
}
|
|
800
|
+
if (!/^\s*Diff:\s*$/i.test(lines[cursor] || "")) continue;
|
|
801
|
+
|
|
802
|
+
const diffLines = [];
|
|
803
|
+
cursor += 1;
|
|
804
|
+
while (cursor < lines.length) {
|
|
805
|
+
if (diffLines.length && /^\s*(?:Tool|Path):\s*/i.test(lines[cursor])) break;
|
|
806
|
+
if (
|
|
807
|
+
diffLines.length &&
|
|
808
|
+
/^\s*(?:Output:|No command output|Blocked by guardrail\.|Mock run complete\.)/i.test(lines[cursor]) &&
|
|
809
|
+
!/^[-+@ ]/.test(lines[cursor])
|
|
810
|
+
) {
|
|
811
|
+
break;
|
|
812
|
+
}
|
|
813
|
+
diffLines.push(lines[cursor]);
|
|
814
|
+
cursor += 1;
|
|
815
|
+
}
|
|
816
|
+
while (diffLines.length && !diffLines[diffLines.length - 1].trim()) diffLines.pop();
|
|
817
|
+
if (diffLines.length) {
|
|
818
|
+
changes.push({
|
|
819
|
+
toolName: currentTool || "write_file",
|
|
820
|
+
path: pathMatch[1].trim(),
|
|
821
|
+
diff: diffLines.join("\n"),
|
|
822
|
+
});
|
|
823
|
+
index = Math.max(index, cursor - 1);
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
return changes;
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
function embeddedWorkspaceSummary(value = "") {
|
|
831
|
+
const summary = [];
|
|
832
|
+
let skippingDiff = false;
|
|
833
|
+
for (const line of String(value || "").split(/\r?\n/)) {
|
|
834
|
+
if (/^\s*Diff:\s*$/i.test(line)) {
|
|
835
|
+
skippingDiff = true;
|
|
836
|
+
continue;
|
|
837
|
+
}
|
|
838
|
+
if (skippingDiff && /^\s*(?:Tool|Path):\s*/i.test(line)) skippingDiff = false;
|
|
839
|
+
if (skippingDiff) continue;
|
|
840
|
+
if (/^\s*Path:\s*/i.test(line)) continue;
|
|
841
|
+
summary.push(line);
|
|
842
|
+
}
|
|
843
|
+
return summary.join("\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
844
|
+
}
|
|
845
|
+
|
|
781
846
|
function printWorkspaceChange(change = {}) {
|
|
782
847
|
if (!change?.diff) return;
|
|
783
848
|
const formatted = formatWorkspaceChange(change);
|
|
@@ -787,6 +852,15 @@ function printWorkspaceChange(change = {}) {
|
|
|
787
852
|
for (const line of formatted.lines) outputLine(`${gutter}${line}`);
|
|
788
853
|
}
|
|
789
854
|
|
|
855
|
+
function printEmbeddedWorkspaceResult(result = "", { labelName = "finish", time = "" } = {}) {
|
|
856
|
+
const changes = embeddedWorkspaceChangesFromText(result);
|
|
857
|
+
if (changes.length === 0) return false;
|
|
858
|
+
const summary = embeddedWorkspaceSummary(result);
|
|
859
|
+
if (summary) printHistoryBlock(labelName, summary, { time, bg: ansi.systemBg });
|
|
860
|
+
for (const change of changes) printWorkspaceChange(change);
|
|
861
|
+
return true;
|
|
862
|
+
}
|
|
863
|
+
|
|
790
864
|
function printPreviewBlock(role, text, { time = "", bg = ansi.systemBg, maxLines = 5 } = {}) {
|
|
791
865
|
const header = [label(role, bg).trimEnd(), time ? color(time, ansi.dim) : ""].filter(Boolean).join(" ");
|
|
792
866
|
outputLine(header);
|
|
@@ -2201,6 +2275,24 @@ async function latestSession() {
|
|
|
2201
2275
|
}
|
|
2202
2276
|
|
|
2203
2277
|
function printHistoryEntry(entry) {
|
|
2278
|
+
if (entry.role === "tool") {
|
|
2279
|
+
const time = entry.at ? new Date(entry.at).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }) : "";
|
|
2280
|
+
try {
|
|
2281
|
+
const parsed = JSON.parse(entry.content || "{}");
|
|
2282
|
+
if (parsed.toolName === "finish" && printEmbeddedWorkspaceResult(parsed.result || parsed.args?.result || "", { labelName: "finish", time })) {
|
|
2283
|
+
return;
|
|
2284
|
+
}
|
|
2285
|
+
if (parsed.diff) {
|
|
2286
|
+
printWorkspaceChange(parsed);
|
|
2287
|
+
return;
|
|
2288
|
+
}
|
|
2289
|
+
outputLine(`${label(parsed.ok === false ? "fail" : "done", parsed.ok === false ? ansi.red : ansi.systemBg)} ${toolStatusDetails(parsed)}`);
|
|
2290
|
+
return;
|
|
2291
|
+
} catch {
|
|
2292
|
+
printHistoryBlock("tool", entry.content, { time, bg: ansi.systemBg });
|
|
2293
|
+
return;
|
|
2294
|
+
}
|
|
2295
|
+
}
|
|
2204
2296
|
const role = entry.role === "assistant" ? "aginti>" : entry.role === "user" ? "user>" : String(entry.role || "note");
|
|
2205
2297
|
const bg = role === "aginti>" ? ansi.agentBg : role === "user>" ? ansi.userBg : ansi.systemBg;
|
|
2206
2298
|
const time = entry.at ? new Date(entry.at).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }) : "";
|
|
@@ -2275,6 +2367,13 @@ function printResumeEvent(event = {}) {
|
|
|
2275
2367
|
}
|
|
2276
2368
|
if (type === "tool.completed" || type === "tool.failed" || type === "tool.skipped") {
|
|
2277
2369
|
const failed = type === "tool.failed" || data.ok === false || data.blocked || data.error;
|
|
2370
|
+
if (data.diff) {
|
|
2371
|
+
printWorkspaceChange(data);
|
|
2372
|
+
return true;
|
|
2373
|
+
}
|
|
2374
|
+
if (data.toolName === "finish" && printEmbeddedWorkspaceResult(data.result || data.args?.result || "", { labelName: "finish" })) {
|
|
2375
|
+
return true;
|
|
2376
|
+
}
|
|
2278
2377
|
if (data.toolName === "run_command") {
|
|
2279
2378
|
printCommandOutputLog({
|
|
2280
2379
|
command: data.args?.command || data.command || "",
|
|
@@ -2314,6 +2413,7 @@ function printResumeEvent(event = {}) {
|
|
|
2314
2413
|
return true;
|
|
2315
2414
|
}
|
|
2316
2415
|
if (type === "session.finished") {
|
|
2416
|
+
if (printEmbeddedWorkspaceResult(data.result || "", { labelName: "finish" })) return true;
|
|
2317
2417
|
outputLine(`${label("state", ansi.systemBg)} status=finished${data.result ? ` result=${compactLine(data.result, 86)}` : ""}`);
|
|
2318
2418
|
return true;
|
|
2319
2419
|
}
|
|
@@ -4174,7 +4274,11 @@ async function runPrompt(prompt, state, packageDir, { approvalDepth = 0 } = {})
|
|
|
4174
4274
|
} else if (type === "tool.started") {
|
|
4175
4275
|
printStatusEvent(state, "tool", toolStatusDetails(data));
|
|
4176
4276
|
} else if (type === "tool.completed" || type === "tool.failed") {
|
|
4177
|
-
|
|
4277
|
+
if (data.toolName === "finish" && printEmbeddedWorkspaceResult(data.result || data.args?.result || "", { labelName: "finish" })) {
|
|
4278
|
+
// The finish tool can recap a workspace diff; render it like the original write/patch event.
|
|
4279
|
+
} else {
|
|
4280
|
+
printStatusEvent(state, type === "tool.failed" || data.ok === false ? "tool_failed" : "tool_done", toolStatusDetails(data));
|
|
4281
|
+
}
|
|
4178
4282
|
} else if (type === "file.changed") {
|
|
4179
4283
|
printWorkspaceChange(data);
|
|
4180
4284
|
} else if (type === "tool.blocked") {
|
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
|
};
|