@poncho-ai/cli 0.6.0 → 0.6.2

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.
Files changed (50) hide show
  1. package/.turbo/turbo-build.log +7 -7
  2. package/dist/chunk-2TLKQG7R.js +5360 -0
  3. package/dist/chunk-3FY4LP2E.js +4981 -0
  4. package/dist/chunk-3WQANPEG.js +5086 -0
  5. package/dist/chunk-44DXWF6D.js +5450 -0
  6. package/dist/chunk-62G3MI43.js +5316 -0
  7. package/dist/chunk-6J2JICGH.js +5135 -0
  8. package/dist/chunk-6KLC6MWK.js +5357 -0
  9. package/dist/chunk-6LG2DUWF.js +5181 -0
  10. package/dist/chunk-ASAXSYEZ.js +5179 -0
  11. package/dist/chunk-B5B5LAR2.js +5181 -0
  12. package/dist/chunk-C7T4EJNQ.js +4934 -0
  13. package/dist/chunk-EPG7ZYDE.js +5452 -0
  14. package/dist/chunk-FMRRGTJX.js +5041 -0
  15. package/dist/chunk-HHMFEU26.js +5451 -0
  16. package/dist/chunk-JRJY6LUC.js +5178 -0
  17. package/dist/chunk-O7SJY7YQ.js +5177 -0
  18. package/dist/chunk-PYP4SKOI.js +5125 -0
  19. package/dist/chunk-VECWMU7E.js +5276 -0
  20. package/dist/chunk-XEBDWQI6.js +5178 -0
  21. package/dist/chunk-YW2D7Z22.js +5360 -0
  22. package/dist/chunk-YZXMEO2T.js +5177 -0
  23. package/dist/cli.js +1 -1
  24. package/dist/index.d.ts +18 -2
  25. package/dist/index.js +11 -1
  26. package/dist/run-interactive-ink-6EJ6Z5HE.js +494 -0
  27. package/dist/run-interactive-ink-72BHZB7Q.js +494 -0
  28. package/dist/run-interactive-ink-BNRIM52Y.js +494 -0
  29. package/dist/run-interactive-ink-BZNBOELJ.js +494 -0
  30. package/dist/run-interactive-ink-GODBXZF3.js +494 -0
  31. package/dist/run-interactive-ink-J4AISGNQ.js +494 -0
  32. package/dist/run-interactive-ink-K75SE2J2.js +494 -0
  33. package/dist/run-interactive-ink-M2XKKPIJ.js +494 -0
  34. package/dist/run-interactive-ink-MITWAF7L.js +494 -0
  35. package/dist/run-interactive-ink-NR5BRFUF.js +494 -0
  36. package/dist/run-interactive-ink-OGNG6UYE.js +494 -0
  37. package/dist/run-interactive-ink-P3VNJEXK.js +494 -0
  38. package/dist/run-interactive-ink-PHLW5YWV.js +494 -0
  39. package/dist/run-interactive-ink-PVU3XABN.js +494 -0
  40. package/dist/run-interactive-ink-SLSK7BY5.js +494 -0
  41. package/dist/run-interactive-ink-TRPYQYHG.js +494 -0
  42. package/dist/run-interactive-ink-U2RPRBIR.js +494 -0
  43. package/dist/run-interactive-ink-U2WTGZJ3.js +494 -0
  44. package/dist/run-interactive-ink-UHBFYNNB.js +494 -0
  45. package/dist/run-interactive-ink-XQDUN6OS.js +494 -0
  46. package/dist/run-interactive-ink-XUHSJCGH.js +494 -0
  47. package/package.json +1 -1
  48. package/src/index.ts +322 -27
  49. package/src/web-ui.ts +350 -33
  50. package/test/cli.test.ts +232 -1
package/src/web-ui.ts CHANGED
@@ -839,6 +839,7 @@ export const renderWebUiHtml = (options?: { agentName?: string }): string => {
839
839
  .assistant-content td {
840
840
  padding: 10px 12px;
841
841
  border-bottom: 1px solid rgba(255,255,255,0.06);
842
+ width: 100%;
842
843
  min-width: 100px;
843
844
  }
844
845
  .assistant-content tr:last-child td {
@@ -1024,6 +1025,23 @@ export const renderWebUiHtml = (options?: { agentName?: string }): string => {
1024
1025
  color: #ededed;
1025
1026
  opacity: 0.5;
1026
1027
  }
1028
+ .thinking-status {
1029
+ display: inline-flex;
1030
+ align-items: center;
1031
+ gap: 8px;
1032
+ margin-top: 2px;
1033
+ color: #8a8a8a;
1034
+ font-size: 14px;
1035
+ line-height: 1.65;
1036
+ font-weight: 400;
1037
+ }
1038
+ .thinking-status-label {
1039
+ color: #8a8a8a;
1040
+ font-size: 14px;
1041
+ line-height: 1.65;
1042
+ font-weight: 400;
1043
+ white-space: nowrap;
1044
+ }
1027
1045
 
1028
1046
  /* Composer */
1029
1047
  .composer {
@@ -1220,6 +1238,7 @@ export const renderWebUiHtml = (options?: { agentName?: string }): string => {
1220
1238
  activeConversationId: null,
1221
1239
  activeMessages: [],
1222
1240
  isStreaming: false,
1241
+ isMessagesPinnedToBottom: true,
1223
1242
  confirmDeleteId: null,
1224
1243
  approvalRequestsInFlight: {}
1225
1244
  };
@@ -1590,8 +1609,21 @@ export const renderWebUiHtml = (options?: { agentName?: string }): string => {
1590
1609
  }
1591
1610
  };
1592
1611
 
1593
- const renderMessages = (messages, isStreaming = false) => {
1594
- const createThinkingIndicator = () => {
1612
+ const isNearBottom = (element, threshold = 64) => {
1613
+ if (!element) return true;
1614
+ return (
1615
+ element.scrollHeight - element.clientHeight - element.scrollTop <= threshold
1616
+ );
1617
+ };
1618
+
1619
+ const renderMessages = (messages, isStreaming = false, options = {}) => {
1620
+ const previousScrollTop = elements.messages.scrollTop;
1621
+ const shouldStickToBottom =
1622
+ options.forceScrollBottom === true || state.isMessagesPinnedToBottom;
1623
+
1624
+ const createThinkingIndicator = (label) => {
1625
+ const status = document.createElement("div");
1626
+ status.className = "thinking-status";
1595
1627
  const spinner = document.createElement("span");
1596
1628
  spinner.className = "thinking-indicator";
1597
1629
  const starFrames = ["✶", "✸", "✹", "✺", "✹", "✷"];
@@ -1601,12 +1633,20 @@ export const renderWebUiHtml = (options?: { agentName?: string }): string => {
1601
1633
  frame = (frame + 1) % starFrames.length;
1602
1634
  spinner.textContent = starFrames[frame];
1603
1635
  }, 70);
1604
- return spinner;
1636
+ status.appendChild(spinner);
1637
+ if (label) {
1638
+ const text = document.createElement("span");
1639
+ text.className = "thinking-status-label";
1640
+ text.textContent = label;
1641
+ status.appendChild(text);
1642
+ }
1643
+ return status;
1605
1644
  };
1606
1645
 
1607
1646
  elements.messages.innerHTML = "";
1608
1647
  if (!messages || !messages.length) {
1609
1648
  elements.messages.innerHTML = '<div class="empty-state"><div class="assistant-avatar">' + agentInitial + '</div><div>How can I help you today?</div></div>';
1649
+ elements.messages.scrollTop = 0;
1610
1650
  return;
1611
1651
  }
1612
1652
  const col = document.createElement("div");
@@ -1638,7 +1678,7 @@ export const renderWebUiHtml = (options?: { agentName?: string }): string => {
1638
1678
  errorEl.innerHTML = "<strong>Error</strong><br>" + escapeHtml(m._error);
1639
1679
  content.appendChild(errorEl);
1640
1680
  } else if (shouldRenderEmptyStreamingIndicator) {
1641
- content.appendChild(createThinkingIndicator());
1681
+ content.appendChild(createThinkingIndicator(getThinkingStatusLabel(m)));
1642
1682
  } else {
1643
1683
  // Check for sections in _sections (streaming) or metadata.sections (stored)
1644
1684
  const sections = m._sections || (m.metadata && m.metadata.sections);
@@ -1712,7 +1752,7 @@ export const renderWebUiHtml = (options?: { agentName?: string }): string => {
1712
1752
  (!m._currentText || m._currentText.length === 0)
1713
1753
  ) {
1714
1754
  const waitIndicator = document.createElement("div");
1715
- waitIndicator.appendChild(createThinkingIndicator());
1755
+ waitIndicator.appendChild(createThinkingIndicator(getThinkingStatusLabel(m)));
1716
1756
  content.appendChild(waitIndicator);
1717
1757
  }
1718
1758
  }
@@ -1724,7 +1764,14 @@ export const renderWebUiHtml = (options?: { agentName?: string }): string => {
1724
1764
  col.appendChild(row);
1725
1765
  });
1726
1766
  elements.messages.appendChild(col);
1727
- elements.messages.scrollTop = elements.messages.scrollHeight;
1767
+ if (shouldStickToBottom) {
1768
+ elements.messages.scrollTop = elements.messages.scrollHeight;
1769
+ state.isMessagesPinnedToBottom = true;
1770
+ return;
1771
+ }
1772
+ if (options.preserveScroll !== false) {
1773
+ elements.messages.scrollTop = previousScrollTop;
1774
+ }
1728
1775
  };
1729
1776
 
1730
1777
  const loadConversations = async () => {
@@ -1740,13 +1787,20 @@ export const renderWebUiHtml = (options?: { agentName?: string }): string => {
1740
1787
  payload.conversation.messages || [],
1741
1788
  payload.conversation.pendingApprovals || payload.pendingApprovals || [],
1742
1789
  );
1743
- renderMessages(state.activeMessages);
1790
+ renderMessages(state.activeMessages, false, { forceScrollBottom: true });
1744
1791
  elements.prompt.focus();
1745
1792
  };
1746
1793
 
1747
1794
  const streamConversationEvents = (conversationId) => {
1748
1795
  return new Promise((resolve) => {
1749
1796
  const localMessages = state.activeMessages || [];
1797
+ const renderIfActiveConversation = (streaming) => {
1798
+ if (state.activeConversationId !== conversationId) {
1799
+ return;
1800
+ }
1801
+ state.activeMessages = localMessages;
1802
+ renderMessages(localMessages, streaming);
1803
+ };
1750
1804
  let assistantMessage = localMessages[localMessages.length - 1];
1751
1805
  if (!assistantMessage || assistantMessage.role !== "assistant") {
1752
1806
  assistantMessage = {
@@ -1764,6 +1818,7 @@ export const renderWebUiHtml = (options?: { agentName?: string }): string => {
1764
1818
  if (!assistantMessage._sections) assistantMessage._sections = [];
1765
1819
  if (!assistantMessage._currentText) assistantMessage._currentText = "";
1766
1820
  if (!assistantMessage._currentTools) assistantMessage._currentTools = [];
1821
+ if (!assistantMessage._activeActivities) assistantMessage._activeActivities = [];
1767
1822
  if (!assistantMessage._pendingApprovals) assistantMessage._pendingApprovals = [];
1768
1823
  if (!assistantMessage.metadata) assistantMessage.metadata = {};
1769
1824
  if (!assistantMessage.metadata.toolActivity) assistantMessage.metadata.toolActivity = [];
@@ -1800,10 +1855,14 @@ export const renderWebUiHtml = (options?: { agentName?: string }): string => {
1800
1855
  }
1801
1856
  assistantMessage.content += chunk;
1802
1857
  assistantMessage._currentText += chunk;
1803
- renderMessages(localMessages, true);
1858
+ renderIfActiveConversation(true);
1804
1859
  }
1805
1860
  if (eventName === "tool:started") {
1806
1861
  const toolName = payload.tool || "tool";
1862
+ const startedActivity = addActiveActivityFromToolStart(
1863
+ assistantMessage,
1864
+ payload,
1865
+ );
1807
1866
  if (assistantMessage._currentText.length > 0) {
1808
1867
  assistantMessage._sections.push({
1809
1868
  type: "text",
@@ -1811,33 +1870,67 @@ export const renderWebUiHtml = (options?: { agentName?: string }): string => {
1811
1870
  });
1812
1871
  assistantMessage._currentText = "";
1813
1872
  }
1814
- const toolText = "- start \\x60" + toolName + "\\x60";
1873
+ const detail =
1874
+ startedActivity && typeof startedActivity.detail === "string"
1875
+ ? startedActivity.detail.trim()
1876
+ : "";
1877
+ const toolText =
1878
+ "- start \\x60" +
1879
+ toolName +
1880
+ "\\x60" +
1881
+ (detail ? " (" + detail + ")" : "");
1815
1882
  assistantMessage._currentTools.push(toolText);
1816
1883
  assistantMessage.metadata.toolActivity.push(toolText);
1817
- renderMessages(localMessages, true);
1884
+ renderIfActiveConversation(true);
1818
1885
  }
1819
1886
  if (eventName === "tool:completed") {
1820
1887
  const toolName = payload.tool || "tool";
1888
+ const activeActivity = removeActiveActivityForTool(
1889
+ assistantMessage,
1890
+ toolName,
1891
+ );
1821
1892
  const duration =
1822
1893
  typeof payload.duration === "number" ? payload.duration : null;
1894
+ const detail =
1895
+ activeActivity && typeof activeActivity.detail === "string"
1896
+ ? activeActivity.detail.trim()
1897
+ : "";
1898
+ const meta = [];
1899
+ if (duration !== null) meta.push(duration + "ms");
1900
+ if (detail) meta.push(detail);
1823
1901
  const toolText =
1824
1902
  "- done \\x60" +
1825
1903
  toolName +
1826
1904
  "\\x60" +
1827
- (duration !== null ? " (" + duration + "ms)" : "");
1905
+ (meta.length > 0 ? " (" + meta.join(", ") + ")" : "");
1828
1906
  assistantMessage._currentTools.push(toolText);
1829
1907
  assistantMessage.metadata.toolActivity.push(toolText);
1830
- renderMessages(localMessages, true);
1908
+ renderIfActiveConversation(true);
1831
1909
  }
1832
1910
  if (eventName === "tool:error") {
1833
1911
  const toolName = payload.tool || "tool";
1912
+ const activeActivity = removeActiveActivityForTool(
1913
+ assistantMessage,
1914
+ toolName,
1915
+ );
1834
1916
  const errorMsg = payload.error || "unknown error";
1835
- const toolText = "- error \\x60" + toolName + "\\x60: " + errorMsg;
1917
+ const detail =
1918
+ activeActivity && typeof activeActivity.detail === "string"
1919
+ ? activeActivity.detail.trim()
1920
+ : "";
1921
+ const toolText =
1922
+ "- error \\x60" +
1923
+ toolName +
1924
+ "\\x60" +
1925
+ (detail ? " (" + detail + ")" : "") +
1926
+ ": " +
1927
+ errorMsg;
1836
1928
  assistantMessage._currentTools.push(toolText);
1837
1929
  assistantMessage.metadata.toolActivity.push(toolText);
1838
- renderMessages(localMessages, true);
1930
+ renderIfActiveConversation(true);
1839
1931
  }
1840
1932
  if (eventName === "run:completed") {
1933
+ assistantMessage._activeActivities = [];
1841
1934
  if (
1842
1935
  !assistantMessage.content ||
1843
1936
  assistantMessage.content.length === 0
@@ -1860,14 +1953,15 @@ export const renderWebUiHtml = (options?: { agentName?: string }): string => {
1860
1953
  });
1861
1954
  assistantMessage._currentText = "";
1862
1955
  }
1863
- renderMessages(localMessages, false);
1956
+ renderIfActiveConversation(false);
1864
1957
  }
1865
1958
  if (eventName === "run:error") {
1959
+ assistantMessage._activeActivities = [];
1866
1960
  const errMsg =
1867
1961
  payload.error?.message || "Something went wrong";
1868
1962
  assistantMessage.content = "";
1869
1963
  assistantMessage._error = errMsg;
1870
- renderMessages(localMessages, false);
1964
+ renderIfActiveConversation(false);
1871
1965
  }
1872
1966
  } catch (error) {
1873
1967
  console.error("SSE reconnect event error:", eventName, error);
@@ -1876,7 +1970,9 @@ export const renderWebUiHtml = (options?: { agentName?: string }): string => {
1876
1970
  }
1877
1971
  };
1878
1972
  processChunks().finally(() => {
1879
- state.activeMessages = localMessages;
1973
+ if (state.activeConversationId === conversationId) {
1974
+ state.activeMessages = localMessages;
1975
+ }
1880
1976
  resolve(undefined);
1881
1977
  });
1882
1978
  }).catch(() => {
@@ -1951,6 +2047,159 @@ export const renderWebUiHtml = (options?: { agentName?: string }): string => {
1951
2047
  assistantMessage.metadata.toolActivity.push(line);
1952
2048
  };
1953
2049
 
2050
+ const ensureActiveActivities = (assistantMessage) => {
2051
+ if (!Array.isArray(assistantMessage._activeActivities)) {
2052
+ assistantMessage._activeActivities = [];
2053
+ }
2054
+ return assistantMessage._activeActivities;
2055
+ };
2056
+
2057
+ const getStringInputField = (input, key) => {
2058
+ if (!input || typeof input !== "object") {
2059
+ return "";
2060
+ }
2061
+ const value = input[key];
2062
+ return typeof value === "string" ? value.trim() : "";
2063
+ };
2064
+
2065
+ const describeToolStart = (payload) => {
2066
+ const toolName = payload && typeof payload.tool === "string" ? payload.tool : "tool";
2067
+ const input = payload && payload.input && typeof payload.input === "object" ? payload.input : {};
2068
+
2069
+ if (toolName === "activate_skill") {
2070
+ const skillName = getStringInputField(input, "name") || "skill";
2071
+ return {
2072
+ kind: "skill",
2073
+ tool: toolName,
2074
+ label: "Activating " + skillName + " skill",
2075
+ detail: "skill: " + skillName,
2076
+ };
2077
+ }
2078
+
2079
+ if (toolName === "run_skill_script") {
2080
+ const scriptPath = getStringInputField(input, "script");
2081
+ const skillName = getStringInputField(input, "skill");
2082
+ if (scriptPath && skillName) {
2083
+ return {
2084
+ kind: "tool",
2085
+ tool: toolName,
2086
+ label: "Running script " + scriptPath + " in " + skillName + " skill",
2087
+ detail: "script: " + scriptPath + ", skill: " + skillName,
2088
+ };
2089
+ }
2090
+ if (scriptPath) {
2091
+ return {
2092
+ kind: "tool",
2093
+ tool: toolName,
2094
+ label: "Running script " + scriptPath,
2095
+ detail: "script: " + scriptPath,
2096
+ };
2097
+ }
2098
+ }
2099
+
2100
+ if (toolName === "read_skill_resource") {
2101
+ const resourcePath = getStringInputField(input, "path");
2102
+ const skillName = getStringInputField(input, "skill");
2103
+ if (resourcePath && skillName) {
2104
+ return {
2105
+ kind: "tool",
2106
+ tool: toolName,
2107
+ label: "Reading " + resourcePath + " from " + skillName + " skill",
2108
+ detail: "path: " + resourcePath + ", skill: " + skillName,
2109
+ };
2110
+ }
2111
+ if (resourcePath) {
2112
+ return {
2113
+ kind: "tool",
2114
+ tool: toolName,
2115
+ label: "Reading " + resourcePath,
2116
+ detail: "path: " + resourcePath,
2117
+ };
2118
+ }
2119
+ }
2120
+
2121
+ if (toolName === "read_file") {
2122
+ const path = getStringInputField(input, "path");
2123
+ if (path) {
2124
+ return {
2125
+ kind: "tool",
2126
+ tool: toolName,
2127
+ label: "Reading " + path,
2128
+ detail: "path: " + path,
2129
+ };
2130
+ }
2131
+ }
2132
+
2133
+ return {
2134
+ kind: "tool",
2135
+ tool: toolName,
2136
+ label: "Running " + toolName + " tool",
2137
+ detail: "",
2138
+ };
2139
+ };
2140
+
2141
+ const addActiveActivityFromToolStart = (assistantMessage, payload) => {
2142
+ const activities = ensureActiveActivities(assistantMessage);
2143
+ const activity = describeToolStart(payload);
2144
+ activities.push(activity);
2145
+ return activity;
2146
+ };
2147
+
2148
+ const removeActiveActivityForTool = (assistantMessage, toolName) => {
2149
+ if (!toolName || !Array.isArray(assistantMessage._activeActivities)) {
2150
+ return null;
2151
+ }
2152
+ const activities = assistantMessage._activeActivities;
2153
+ const idx = activities.findIndex((item) => item && item.tool === toolName);
2154
+ if (idx >= 0) {
2155
+ return activities.splice(idx, 1)[0] || null;
2156
+ }
2157
+ return null;
2158
+ };
2159
+
2160
+ const getThinkingStatusLabel = (assistantMessage) => {
2161
+ const activities = Array.isArray(assistantMessage?._activeActivities)
2162
+ ? assistantMessage._activeActivities
2163
+ : [];
2164
+ const labels = [];
2165
+ activities.forEach((item) => {
2166
+ if (!item || typeof item.label !== "string") {
2167
+ return;
2168
+ }
2169
+ const label = item.label.trim();
2170
+ if (!label || labels.includes(label)) {
2171
+ return;
2172
+ }
2173
+ labels.push(label);
2174
+ });
2175
+ if (labels.length === 1) {
2176
+ return labels[0];
2177
+ }
2178
+ if (labels.length === 2) {
2179
+ return labels[0] + ", " + labels[1];
2180
+ }
2181
+ if (labels.length > 2) {
2182
+ return labels[0] + ", " + labels[1] + " +" + (labels.length - 2) + " more";
2183
+ }
2184
+
2185
+ if (Array.isArray(assistantMessage?._currentTools)) {
2186
+ const tick = String.fromCharCode(96);
2187
+ const startPrefix = "- start " + tick;
2188
+ for (let idx = assistantMessage._currentTools.length - 1; idx >= 0; idx -= 1) {
2189
+ const item = String(assistantMessage._currentTools[idx] || "");
2190
+ if (item.startsWith(startPrefix)) {
2191
+ const rest = item.slice(startPrefix.length);
2192
+ const endIdx = rest.indexOf(tick);
2193
+ const toolName = (endIdx >= 0 ? rest.slice(0, endIdx) : rest).trim();
2194
+ if (toolName) {
2195
+ return "Running " + toolName + " tool";
2196
+ }
2197
+ }
2198
+ }
2199
+ }
2200
+ return "Thinking...";
2201
+ };
2202
+
1954
2203
  const autoResizePrompt = () => {
1955
2204
  const el = elements.prompt;
1956
2205
  el.style.height = "auto";
@@ -1972,18 +2221,27 @@ export const renderWebUiHtml = (options?: { agentName?: string }): string => {
1972
2221
  _sections: [], // Array of {type: 'text'|'tools', content: string|array}
1973
2222
  _currentText: "",
1974
2223
  _currentTools: [],
2224
+ _activeActivities: [],
1975
2225
  _pendingApprovals: [],
1976
2226
  metadata: { toolActivity: [] }
1977
2227
  };
1978
2228
  localMessages.push(assistantMessage);
1979
2229
  state.activeMessages = localMessages;
1980
- renderMessages(localMessages, true);
2230
+ renderMessages(localMessages, true, { forceScrollBottom: true });
1981
2231
  setStreaming(true);
1982
2232
  let conversationId = state.activeConversationId;
1983
2233
  try {
1984
2234
  if (!conversationId) {
1985
2235
  conversationId = await createConversation(messageText, { loadConversation: false });
1986
2236
  }
2237
+ const streamConversationId = conversationId;
2238
+ const renderIfActiveConversation = (streaming) => {
2239
+ if (state.activeConversationId !== streamConversationId) {
2240
+ return;
2241
+ }
2242
+ state.activeMessages = localMessages;
2243
+ renderMessages(localMessages, streaming);
2244
+ };
1987
2245
  const response = await fetch("/api/conversations/" + encodeURIComponent(conversationId) + "/messages", {
1988
2246
  method: "POST",
1989
2247
  credentials: "include",
@@ -2013,45 +2271,96 @@ export const renderWebUiHtml = (options?: { agentName?: string }): string => {
2013
2271
  }
2014
2272
  assistantMessage.content += chunk;
2015
2273
  assistantMessage._currentText += chunk;
2016
- renderMessages(localMessages, true);
2274
+ renderIfActiveConversation(true);
2017
2275
  }
2018
2276
  if (eventName === "tool:started") {
2019
2277
  const toolName = payload.tool || "tool";
2278
+ const startedActivity = addActiveActivityFromToolStart(
2279
+ assistantMessage,
2280
+ payload,
2281
+ );
2020
2282
  // If we have text accumulated, push it as a text section
2021
2283
  if (assistantMessage._currentText.length > 0) {
2022
2284
  assistantMessage._sections.push({ type: "text", content: assistantMessage._currentText });
2023
2285
  assistantMessage._currentText = "";
2024
2286
  }
2025
- const toolText = "- start \\x60" + toolName + "\\x60";
2287
+ const detail =
2288
+ startedActivity && typeof startedActivity.detail === "string"
2289
+ ? startedActivity.detail.trim()
2290
+ : "";
2291
+ const toolText =
2292
+ "- start \\x60" + toolName + "\\x60" + (detail ? " (" + detail + ")" : "");
2026
2293
  assistantMessage._currentTools.push(toolText);
2027
2294
  if (!assistantMessage.metadata) assistantMessage.metadata = {};
2028
2295
  if (!assistantMessage.metadata.toolActivity) assistantMessage.metadata.toolActivity = [];
2029
2296
  assistantMessage.metadata.toolActivity.push(toolText);
2030
- renderMessages(localMessages, true);
2297
+ renderIfActiveConversation(true);
2031
2298
  }
2032
2299
  if (eventName === "tool:completed") {
2033
2300
  const toolName = payload.tool || "tool";
2301
+ const activeActivity = removeActiveActivityForTool(
2302
+ assistantMessage,
2303
+ toolName,
2304
+ );
2034
2305
  const duration = typeof payload.duration === "number" ? payload.duration : null;
2035
- const toolText = "- done \\x60" + toolName + "\\x60" + (duration !== null ? " (" + duration + "ms)" : "");
2306
+ const detail =
2307
+ activeActivity && typeof activeActivity.detail === "string"
2308
+ ? activeActivity.detail.trim()
2309
+ : "";
2310
+ const meta = [];
2311
+ if (duration !== null) meta.push(duration + "ms");
2312
+ if (detail) meta.push(detail);
2313
+ const toolText =
2314
+ "- done \\x60" + toolName + "\\x60" + (meta.length > 0 ? " (" + meta.join(", ") + ")" : "");
2036
2315
  assistantMessage._currentTools.push(toolText);
2037
2316
  if (!assistantMessage.metadata) assistantMessage.metadata = {};
2038
2317
  if (!assistantMessage.metadata.toolActivity) assistantMessage.metadata.toolActivity = [];
2039
2318
  assistantMessage.metadata.toolActivity.push(toolText);
2040
- renderMessages(localMessages, true);
2319
+ renderIfActiveConversation(true);
2041
2320
  }
2042
2321
  if (eventName === "tool:error") {
2043
2322
  const toolName = payload.tool || "tool";
2323
+ const activeActivity = removeActiveActivityForTool(
2324
+ assistantMessage,
2325
+ toolName,
2326
+ );
2044
2327
  const errorMsg = payload.error || "unknown error";
2045
- const toolText = "- error \\x60" + toolName + "\\x60: " + errorMsg;
2328
+ const detail =
2329
+ activeActivity && typeof activeActivity.detail === "string"
2330
+ ? activeActivity.detail.trim()
2331
+ : "";
2332
+ const toolText =
2333
+ "- error \\x60" +
2334
+ toolName +
2335
+ "\\x60" +
2336
+ (detail ? " (" + detail + ")" : "") +
2337
+ ": " +
2338
+ errorMsg;
2046
2339
  assistantMessage._currentTools.push(toolText);
2047
2340
  if (!assistantMessage.metadata) assistantMessage.metadata = {};
2048
2341
  if (!assistantMessage.metadata.toolActivity) assistantMessage.metadata.toolActivity = [];
2049
2342
  assistantMessage.metadata.toolActivity.push(toolText);
2050
- renderMessages(localMessages, true);
2343
+ renderIfActiveConversation(true);
2051
2344
  }
2052
2345
  if (eventName === "tool:approval:required") {
2053
2346
  const toolName = payload.tool || "tool";
2054
- const toolText = "- approval required \\x60" + toolName + "\\x60";
2347
+ const activeActivity = removeActiveActivityForTool(
2348
+ assistantMessage,
2349
+ toolName,
2350
+ );
2351
+ const detailFromPayload = describeToolStart(payload);
2352
+ const detail =
2353
+ (activeActivity && typeof activeActivity.detail === "string"
2354
+ ? activeActivity.detail.trim()
2355
+ : "") ||
2356
+ (detailFromPayload && typeof detailFromPayload.detail === "string"
2357
+ ? detailFromPayload.detail.trim()
2358
+ : "");
2359
+ const toolText =
2360
+ "- approval required \\x60" +
2361
+ toolName +
2362
+ "\\x60" +
2363
+ (detail ? " (" + detail + ")" : "");
2055
2364
  assistantMessage._currentTools.push(toolText);
2056
2365
  if (!assistantMessage.metadata) assistantMessage.metadata = {};
2057
2366
  if (!assistantMessage.metadata.toolActivity) assistantMessage.metadata.toolActivity = [];
@@ -2076,7 +2385,7 @@ export const renderWebUiHtml = (options?: { agentName?: string }): string => {
2076
2385
  });
2077
2386
  }
2078
2387
  }
2079
- renderMessages(localMessages, true);
2388
+ renderIfActiveConversation(true);
2080
2389
  }
2081
2390
  if (eventName === "tool:approval:granted") {
2082
2391
  const toolText = "- approval granted";
@@ -2091,7 +2400,7 @@ export const renderWebUiHtml = (options?: { agentName?: string }): string => {
2091
2400
  (req) => req.approvalId !== approvalId,
2092
2401
  );
2093
2402
  }
2094
- renderMessages(localMessages, true);
2403
+ renderIfActiveConversation(true);
2095
2404
  }
2096
2405
  if (eventName === "tool:approval:denied") {
2097
2406
  const toolText = "- approval denied";
@@ -2106,9 +2415,10 @@ export const renderWebUiHtml = (options?: { agentName?: string }): string => {
2106
2415
  (req) => req.approvalId !== approvalId,
2107
2416
  );
2108
2417
  }
2109
- renderMessages(localMessages, true);
2418
+ renderIfActiveConversation(true);
2110
2419
  }
2111
2420
  if (eventName === "run:completed") {
2421
+ assistantMessage._activeActivities = [];
2112
2422
  if (!assistantMessage.content || assistantMessage.content.length === 0) {
2113
2423
  assistantMessage.content = String(payload.result?.response || "");
2114
2424
  }
@@ -2121,21 +2431,24 @@ export const renderWebUiHtml = (options?: { agentName?: string }): string => {
2121
2431
  assistantMessage._sections.push({ type: "text", content: assistantMessage._currentText });
2122
2432
  assistantMessage._currentText = "";
2123
2433
  }
2124
- renderMessages(localMessages, false);
2434
+ renderIfActiveConversation(false);
2125
2435
  }
2126
2436
  if (eventName === "run:error") {
2437
+ assistantMessage._activeActivities = [];
2127
2438
  const errMsg = payload.error?.message || "Something went wrong";
2128
2439
  assistantMessage.content = "";
2129
2440
  assistantMessage._error = errMsg;
2130
- renderMessages(localMessages, false);
2441
+ renderIfActiveConversation(false);
2131
2442
  }
2132
2443
  } catch (error) {
2133
2444
  console.error("SSE event handling error:", eventName, error);
2134
2445
  }
2135
2446
  });
2136
2447
  }
2137
- // Update the state with our local messages (don't reload and lose tool chips)
2138
- state.activeMessages = localMessages;
2448
+ // Update active state only if user is still on this conversation.
2449
+ if (state.activeConversationId === streamConversationId) {
2450
+ state.activeMessages = localMessages;
2451
+ }
2139
2452
  await loadConversations();
2140
2453
  // Don't reload the conversation - we already have the latest state with tool chips
2141
2454
  } finally {
@@ -2303,6 +2616,10 @@ export const renderWebUiHtml = (options?: { agentName?: string }): string => {
2303
2616
  }
2304
2617
  });
2305
2618
 
2619
+ elements.messages.addEventListener("scroll", () => {
2620
+ state.isMessagesPinnedToBottom = isNearBottom(elements.messages);
2621
+ }, { passive: true });
2622
+
2306
2623
  document.addEventListener("click", (event) => {
2307
2624
  if (!(event.target instanceof Node)) {
2308
2625
  return;