@tutti-os/agent-gui 0.0.3 → 0.0.4

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.
@@ -101,7 +101,8 @@ function getExplicitWindowTestAgentActivityRuntime() {
101
101
  // agentActivityHost.tsx
102
102
  import {
103
103
  createContext as createContext2,
104
- useContext as useContext2
104
+ useContext as useContext2,
105
+ useMemo
105
106
  } from "react";
106
107
 
107
108
  // host/agentHostApi.ts
@@ -132,7 +133,10 @@ function AgentActivityHostProvider({
132
133
  agentHostApi,
133
134
  children
134
135
  }) {
135
- const resolvedAgentHostApi = agentHostApi ? toAgentHostRuntimeApi(agentHostApi) : null;
136
+ const resolvedAgentHostApi = useMemo(
137
+ () => agentHostApi ? toAgentHostRuntimeApi(agentHostApi) : null,
138
+ [agentHostApi]
139
+ );
136
140
  currentAgentHostApi = resolvedAgentHostApi;
137
141
  return /* @__PURE__ */ jsx2(AgentActivityRuntimeProvider, { runtime: agentActivityRuntime, children: /* @__PURE__ */ jsx2(AgentActivityHostContext.Provider, { value: resolvedAgentHostApi, children }) });
138
142
  }
@@ -183,42 +187,6 @@ function getExplicitWindowTestAgentHostApi() {
183
187
  return testAgentHostApi ? toAgentHostRuntimeApi(testAgentHostApi) : null;
184
188
  }
185
189
 
186
- // app/renderer/assets/icons/agents/manage-agent-claude-code.png
187
- var manage_agent_claude_code_default = "./manage-agent-claude-code-F6VPQETB.png";
188
-
189
- // app/renderer/assets/icons/agents/manage-agent-codex.png
190
- var manage_agent_codex_default = "./manage-agent-codex-SC63MZAW.png";
191
-
192
- // app/renderer/assets/icons/agents/manage-agent-gemini.png
193
- var manage_agent_gemini_default = "./manage-agent-gemini-O4KAJFIM.png";
194
-
195
- // app/renderer/assets/icons/agents/manage-agent-hermes.png
196
- var manage_agent_hermes_default = "./manage-agent-hermes-QGDHBNRJ.png";
197
-
198
- // app/renderer/assets/icons/agents/manage-agent-nextop.png
199
- var manage_agent_nextop_default = "./manage-agent-nextop-UFAQ22K2.png";
200
-
201
- // app/renderer/assets/icons/agents/manage-agent-openclaw.png
202
- var manage_agent_openclaw_default = "./manage-agent-openclaw-24U7O6CA.png";
203
-
204
- // app/renderer/assets/icons/agents/claude-rounded.png
205
- var claude_rounded_default = "./claude-rounded-F6VPQETB.png";
206
-
207
- // app/renderer/assets/icons/agents/codex-rounded.png
208
- var codex_rounded_default = "./codex-rounded-SC63MZAW.png";
209
-
210
- // app/renderer/assets/icons/agents/gemini-rounded.png
211
- var gemini_rounded_default = "./gemini-rounded-O4KAJFIM.png";
212
-
213
- // app/renderer/assets/icons/agents/hermes-rounded.png
214
- var hermes_rounded_default = "./hermes-rounded-QGDHBNRJ.png";
215
-
216
- // app/renderer/assets/icons/agents/nextop-doc-rounded.png
217
- var nextop_doc_rounded_default = "./nextop-doc-rounded-UFAQ22K2.png";
218
-
219
- // app/renderer/assets/icons/agents/openclaw-rounded.png
220
- var openclaw_rounded_default = "./openclaw-rounded-24U7O6CA.png";
221
-
222
190
  // shared/managedAgentProviders.ts
223
191
  function normalizeManagedAgentProvider(provider) {
224
192
  const normalized = provider?.trim().toLowerCase().replace(/[_\s]+/gu, "-") ?? "";
@@ -234,25 +202,47 @@ function normalizeManagedAgentProvider(provider) {
234
202
  }
235
203
  }
236
204
 
205
+ // managedAgentIconAssets.ts
206
+ function agentIconUrl(fileName) {
207
+ return new URL(
208
+ `./app/renderer/assets/icons/agents/${fileName}`,
209
+ import.meta.url
210
+ ).href;
211
+ }
212
+ var manageAgentClaudeCodeUrl = agentIconUrl(
213
+ "manage-agent-claude-code.png"
214
+ );
215
+ var manageAgentCodexUrl = agentIconUrl("manage-agent-codex.png");
216
+ var manageAgentGeminiUrl = agentIconUrl("manage-agent-gemini.png");
217
+ var manageAgentHermesUrl = agentIconUrl("manage-agent-hermes.png");
218
+ var manageAgentNextopUrl = agentIconUrl("manage-agent-nextop.png");
219
+ var manageAgentOpenclawUrl = agentIconUrl("manage-agent-openclaw.png");
220
+ var claudeRoundedUrl = agentIconUrl("claude-rounded.png");
221
+ var codexRoundedUrl = agentIconUrl("codex-rounded.png");
222
+ var geminiRoundedUrl = agentIconUrl("gemini-rounded.png");
223
+ var hermesRoundedUrl = agentIconUrl("hermes-rounded.png");
224
+ var nextopDocRoundedUrl = agentIconUrl("nextop-doc-rounded.png");
225
+ var openclawRoundedUrl = agentIconUrl("openclaw-rounded.png");
226
+
237
227
  // shared/managedAgentIcons.ts
238
228
  var MANAGED_AGENT_ICON_URLS = {
239
- "claude-code": manage_agent_claude_code_default,
240
- codex: manage_agent_codex_default,
241
- gemini: manage_agent_gemini_default,
242
- hermes: manage_agent_hermes_default,
243
- nextop: manage_agent_nextop_default,
244
- openclaw: manage_agent_openclaw_default
229
+ "claude-code": manageAgentClaudeCodeUrl,
230
+ codex: manageAgentCodexUrl,
231
+ gemini: manageAgentGeminiUrl,
232
+ hermes: manageAgentHermesUrl,
233
+ nextop: manageAgentNextopUrl,
234
+ openclaw: manageAgentOpenclawUrl
245
235
  };
246
236
  var MANAGED_AGENT_ICON_ROUNDED_URLS = {
247
- "claude-code": claude_rounded_default,
248
- codex: codex_rounded_default,
249
- gemini: gemini_rounded_default,
250
- hermes: hermes_rounded_default,
251
- nextop: nextop_doc_rounded_default,
252
- openclaw: openclaw_rounded_default
237
+ "claude-code": claudeRoundedUrl,
238
+ codex: codexRoundedUrl,
239
+ gemini: geminiRoundedUrl,
240
+ hermes: hermesRoundedUrl,
241
+ nextop: nextopDocRoundedUrl,
242
+ openclaw: openclawRoundedUrl
253
243
  };
254
- var MANAGED_AGENT_ROUNDED_ICON_FALLBACK_URL = nextop_doc_rounded_default;
255
- var MANAGED_AGENT_ICON_FALLBACK_URL = manage_agent_nextop_default;
244
+ var MANAGED_AGENT_ROUNDED_ICON_FALLBACK_URL = nextopDocRoundedUrl;
245
+ var MANAGED_AGENT_ICON_FALLBACK_URL = manageAgentNextopUrl;
256
246
  function managedAgentRoundedIconUrl(provider) {
257
247
  return MANAGED_AGENT_ICON_ROUNDED_URLS[normalizeManagedAgentProvider(provider)] ?? MANAGED_AGENT_ROUNDED_ICON_FALLBACK_URL;
258
248
  }
@@ -478,7 +468,7 @@ function numericValue(value) {
478
468
  import {
479
469
  useCallback as useCallback2,
480
470
  useEffect as useEffect2,
481
- useMemo,
471
+ useMemo as useMemo2,
482
472
  useRef as useRef2,
483
473
  useState
484
474
  } from "react";
@@ -754,7 +744,7 @@ function ApprovalPromptSurface({
754
744
  labels
755
745
  }) {
756
746
  "use memo";
757
- const promptDetails = useMemo(
747
+ const promptDetails = useMemo2(
758
748
  () => formatToolDetails(prompt.input ?? null),
759
749
  [prompt.input]
760
750
  );
@@ -762,7 +752,7 @@ function ApprovalPromptSurface({
762
752
  prompt.title,
763
753
  promptDetails.length
764
754
  );
765
- const details = useMemo(
755
+ const details = useMemo2(
766
756
  () => filterDuplicatePromptTitleDetails(promptDetails, visiblePromptTitle),
767
757
  [promptDetails, visiblePromptTitle]
768
758
  );
@@ -774,7 +764,7 @@ function ApprovalPromptSurface({
774
764
  const feedbackTextareaRef = useRef2(null);
775
765
  const agentHostApi = useOptionalAgentHostApi() ?? getOptionalAgentHostApi();
776
766
  const isDarwin = isDarwinPlatform(agentHostApi?.meta?.platform);
777
- const feedbackOptionId = useMemo(
767
+ const feedbackOptionId = useMemo2(
778
768
  () => approvalFeedbackOptionId(prompt.options),
779
769
  [prompt.options]
780
770
  );
@@ -1055,7 +1045,7 @@ function AskUserPromptSurface({
1055
1045
  const freeText = question ? freeTextByQuestionId[question.id] ?? "" : "";
1056
1046
  const canAdvance = question !== null && (selected.length > 0 || freeText.trim() !== "" || question.options.length === 0);
1057
1047
  const isLast = index >= prompt.questions.length - 1;
1058
- const payload = useMemo(() => {
1048
+ const payload = useMemo2(() => {
1059
1049
  const answersByQuestionId = {};
1060
1050
  const answers = [];
1061
1051
  for (const current of prompt.questions) {
@@ -1379,12 +1369,13 @@ function interactiveOptionLabel(label, description) {
1379
1369
  // shared/AgentMessageMarkdown.tsx
1380
1370
  import {
1381
1371
  createContext as createContext3,
1382
- startTransition,
1372
+ startTransition as startTransition2,
1383
1373
  useCallback as useCallback3,
1384
- useEffect as useEffect3,
1374
+ useEffect as useEffect4,
1385
1375
  useContext as useContext3,
1386
- useMemo as useMemo2,
1387
- useState as useState2
1376
+ memo,
1377
+ useMemo as useMemo3,
1378
+ useState as useState3
1388
1379
  } from "react";
1389
1380
 
1390
1381
  // app/renderer/components/ZoomableImage.tsx
@@ -1685,13 +1676,320 @@ function isDirectAgentGeneratedImagePath(path) {
1685
1676
  return /\.(?:png|jpe?g|gif|webp|bmp)$/i.test(path);
1686
1677
  }
1687
1678
 
1679
+ // shared/streamingMarkdownTailStabilizer.ts
1680
+ var DEFAULT_MAX_TAIL_CHARS = 4096;
1681
+ function stabilizeStreamingMarkdownTail(content, options) {
1682
+ if (!options.streaming || content.length === 0) {
1683
+ return { content, changed: false };
1684
+ }
1685
+ const maxTailChars = Math.max(
1686
+ 256,
1687
+ options.maxTailChars ?? DEFAULT_MAX_TAIL_CHARS
1688
+ );
1689
+ const tailStart = Math.max(0, content.length - maxTailChars);
1690
+ const tail = content.slice(tailStart);
1691
+ const fence = findOpenFence(tail);
1692
+ if (fence) {
1693
+ return {
1694
+ content: `${content}
1695
+ ${fence.marker.repeat(fence.length)}`,
1696
+ changed: true,
1697
+ reason: "open-fence"
1698
+ };
1699
+ }
1700
+ const incompleteLink = stabilizeIncompleteTailLink(content);
1701
+ if (incompleteLink) {
1702
+ return incompleteLink;
1703
+ }
1704
+ const listMarker = stabilizeDanglingListMarker(content);
1705
+ if (listMarker) {
1706
+ return listMarker;
1707
+ }
1708
+ const tableRow = stabilizePartialTableRow(content);
1709
+ if (tableRow) {
1710
+ return tableRow;
1711
+ }
1712
+ const inlineCode = findOpenInlineCodeSpan(tail);
1713
+ if (inlineCode) {
1714
+ return {
1715
+ content: `${content}${"`".repeat(inlineCode.length)}`,
1716
+ changed: true,
1717
+ reason: "open-inline-code"
1718
+ };
1719
+ }
1720
+ const emphasis = stabilizeTrailingEmphasisMarker(content);
1721
+ if (emphasis) {
1722
+ return emphasis;
1723
+ }
1724
+ return { content, changed: false };
1725
+ }
1726
+ function findOpenFence(tail) {
1727
+ let openFence = null;
1728
+ for (const line of tail.replace(/\r\n?/g, "\n").split("\n")) {
1729
+ const fence = parseFenceLine(line);
1730
+ if (!fence) {
1731
+ continue;
1732
+ }
1733
+ if (!openFence) {
1734
+ openFence = fence;
1735
+ continue;
1736
+ }
1737
+ if (fence.marker === openFence.marker && fence.length >= openFence.length) {
1738
+ openFence = null;
1739
+ }
1740
+ }
1741
+ return openFence;
1742
+ }
1743
+ function parseFenceLine(line) {
1744
+ let index = 0;
1745
+ while (index < line.length && line[index] === " " && index < 4) {
1746
+ index += 1;
1747
+ }
1748
+ const marker = line[index];
1749
+ if (marker !== "`" && marker !== "~") {
1750
+ return null;
1751
+ }
1752
+ let length = 0;
1753
+ while (line[index + length] === marker) {
1754
+ length += 1;
1755
+ }
1756
+ return length >= 3 ? { marker, length } : null;
1757
+ }
1758
+ function stabilizeIncompleteTailLink(content) {
1759
+ const lineStart = content.lastIndexOf("\n") + 1;
1760
+ const line = content.slice(lineStart);
1761
+ const linkStart = line.lastIndexOf("[");
1762
+ const imageStart = line.lastIndexOf("![");
1763
+ const openBracketIndex = imageStart >= 0 && imageStart + 1 === linkStart ? imageStart : linkStart;
1764
+ if (openBracketIndex < 0) {
1765
+ return null;
1766
+ }
1767
+ const absoluteOpenIndex = lineStart + openBracketIndex;
1768
+ const suffix = content.slice(absoluteOpenIndex);
1769
+ const closeLabelIndex = suffix.indexOf("]");
1770
+ if (closeLabelIndex < 0) {
1771
+ const label2 = suffix.startsWith("![") ? suffix.slice(2) : suffix.slice(1);
1772
+ return {
1773
+ content: `${content.slice(0, absoluteOpenIndex)}${label2}`,
1774
+ changed: true,
1775
+ reason: "incomplete-link-label"
1776
+ };
1777
+ }
1778
+ if (suffix[closeLabelIndex + 1] !== "(" || suffix.includes(")")) {
1779
+ return null;
1780
+ }
1781
+ const label = suffix.startsWith("![") ? suffix.slice(2, closeLabelIndex) : suffix.slice(1, closeLabelIndex);
1782
+ return {
1783
+ content: `${content.slice(0, absoluteOpenIndex)}${label}`,
1784
+ changed: true,
1785
+ reason: "incomplete-link-target"
1786
+ };
1787
+ }
1788
+ function stabilizeDanglingListMarker(content) {
1789
+ const lineStart = content.lastIndexOf("\n") + 1;
1790
+ const line = content.slice(lineStart);
1791
+ const trimmed = line.trim();
1792
+ const isBullet = trimmed === "-" || trimmed === "*" || trimmed === "+";
1793
+ const isOrdered = trimmed.length >= 2 && trimmed.endsWith(".") && [...trimmed.slice(0, -1)].every((char) => char >= "0" && char <= "9");
1794
+ if (!isBullet && !isOrdered) {
1795
+ return null;
1796
+ }
1797
+ return {
1798
+ content: content.slice(0, lineStart),
1799
+ changed: true,
1800
+ reason: "dangling-list-marker"
1801
+ };
1802
+ }
1803
+ function stabilizePartialTableRow(content) {
1804
+ if (content.endsWith("\n")) {
1805
+ return null;
1806
+ }
1807
+ const lines = content.replace(/\r\n?/g, "\n").split("\n");
1808
+ const currentLine = lines.at(-1) ?? "";
1809
+ const previousLine = lines.at(-2) ?? "";
1810
+ if (!currentLine.includes("|") || !previousLine.includes("|")) {
1811
+ return null;
1812
+ }
1813
+ if (currentLine.trimEnd().endsWith("|")) {
1814
+ return null;
1815
+ }
1816
+ if (countChar(previousLine, "|") < 2) {
1817
+ return null;
1818
+ }
1819
+ return {
1820
+ content: `${content} |`,
1821
+ changed: true,
1822
+ reason: "partial-table-row"
1823
+ };
1824
+ }
1825
+ function findOpenInlineCodeSpan(tail) {
1826
+ const lastParagraph = tail.slice(tail.lastIndexOf("\n\n") + 2);
1827
+ let openLength = 0;
1828
+ for (let index = 0; index < lastParagraph.length; index += 1) {
1829
+ if (lastParagraph[index] !== "`") {
1830
+ continue;
1831
+ }
1832
+ let length = 1;
1833
+ while (lastParagraph[index + length] === "`") {
1834
+ length += 1;
1835
+ }
1836
+ if (length >= 3) {
1837
+ index += length - 1;
1838
+ continue;
1839
+ }
1840
+ openLength = openLength === length ? 0 : length;
1841
+ index += length - 1;
1842
+ }
1843
+ return openLength > 0 ? { length: openLength } : null;
1844
+ }
1845
+ function stabilizeTrailingEmphasisMarker(content) {
1846
+ const marker = content.at(-1);
1847
+ if (marker !== "*" && marker !== "_") {
1848
+ return null;
1849
+ }
1850
+ const previous = content.at(-2);
1851
+ const nextContent = previous === marker ? content.slice(0, -2) : content.slice(0, -1);
1852
+ return {
1853
+ content: nextContent,
1854
+ changed: true,
1855
+ reason: "trailing-emphasis-marker"
1856
+ };
1857
+ }
1858
+ function countChar(value, char) {
1859
+ let count = 0;
1860
+ for (const current of value) {
1861
+ if (current === char) {
1862
+ count += 1;
1863
+ }
1864
+ }
1865
+ return count;
1866
+ }
1867
+
1868
+ // shared/useStreamingVisibleText.ts
1869
+ import {
1870
+ startTransition,
1871
+ useEffect as useEffect3,
1872
+ useRef as useRef3,
1873
+ useState as useState2
1874
+ } from "react";
1875
+ var DEFAULT_FRAME_MS = 24;
1876
+ var DEFAULT_MAX_CHARS_PER_SECOND = 6e3;
1877
+ var DEFAULT_TRAILING_FLUSH_CHARS = 0;
1878
+ function useStreamingVisibleText(sourceText, options) {
1879
+ const {
1880
+ enabled,
1881
+ frameMs = DEFAULT_FRAME_MS,
1882
+ maxCharsPerSecond = DEFAULT_MAX_CHARS_PER_SECOND,
1883
+ trailingFlushChars = DEFAULT_TRAILING_FLUSH_CHARS
1884
+ } = options;
1885
+ const [visibleText, setVisibleText] = useState2(sourceText);
1886
+ const sourceRef = useRef3(sourceText);
1887
+ const visibleRef = useRef3(visibleText);
1888
+ const timerRef = useRef3(null);
1889
+ useEffect3(() => {
1890
+ visibleRef.current = visibleText;
1891
+ }, [visibleText]);
1892
+ useEffect3(
1893
+ () => () => {
1894
+ clearStreamingVisibleTextTimer(timerRef);
1895
+ },
1896
+ []
1897
+ );
1898
+ useEffect3(() => {
1899
+ sourceRef.current = sourceText;
1900
+ if (!enabled) {
1901
+ clearStreamingVisibleTextTimer(timerRef);
1902
+ visibleRef.current = sourceText;
1903
+ setVisibleText(sourceText);
1904
+ return;
1905
+ }
1906
+ if (sourceText === visibleRef.current || timerRef.current !== null) {
1907
+ return;
1908
+ }
1909
+ timerRef.current = setTimeout(
1910
+ () => {
1911
+ timerRef.current = null;
1912
+ const nextVisibleText = advanceStreamingVisibleText({
1913
+ visibleText: visibleRef.current,
1914
+ sourceText: sourceRef.current,
1915
+ frameMs,
1916
+ maxCharsPerSecond,
1917
+ trailingFlushChars
1918
+ });
1919
+ if (nextVisibleText === visibleRef.current) {
1920
+ return;
1921
+ }
1922
+ visibleRef.current = nextVisibleText;
1923
+ startTransition(() => {
1924
+ setVisibleText(nextVisibleText);
1925
+ });
1926
+ },
1927
+ Math.max(1, frameMs)
1928
+ );
1929
+ return void 0;
1930
+ }, [
1931
+ enabled,
1932
+ frameMs,
1933
+ maxCharsPerSecond,
1934
+ sourceText,
1935
+ trailingFlushChars,
1936
+ visibleText
1937
+ ]);
1938
+ return enabled ? visibleText : sourceText;
1939
+ }
1940
+ function advanceStreamingVisibleText({
1941
+ visibleText,
1942
+ sourceText,
1943
+ frameMs = DEFAULT_FRAME_MS,
1944
+ maxCharsPerSecond = DEFAULT_MAX_CHARS_PER_SECOND,
1945
+ trailingFlushChars = DEFAULT_TRAILING_FLUSH_CHARS
1946
+ }) {
1947
+ if (visibleText === sourceText) {
1948
+ return visibleText;
1949
+ }
1950
+ const prefixLength = sourceText.startsWith(visibleText) ? visibleText.length : commonPrefixLength(visibleText, sourceText);
1951
+ const stablePrefix = sourceText.slice(0, prefixLength);
1952
+ const remainingLength = sourceText.length - prefixLength;
1953
+ if (remainingLength <= trailingFlushChars) {
1954
+ return sourceText;
1955
+ }
1956
+ const charsPerFrame = Math.max(
1957
+ 1,
1958
+ Math.ceil(Math.max(1, maxCharsPerSecond) * Math.max(1, frameMs) / 1e3)
1959
+ );
1960
+ return sourceText.slice(
1961
+ 0,
1962
+ Math.min(sourceText.length, stablePrefix.length + charsPerFrame)
1963
+ );
1964
+ }
1965
+ function commonPrefixLength(left, right) {
1966
+ const maxLength = Math.min(left.length, right.length);
1967
+ for (let index = 0; index < maxLength; index += 1) {
1968
+ if (left.charCodeAt(index) !== right.charCodeAt(index)) {
1969
+ return index;
1970
+ }
1971
+ }
1972
+ return maxLength;
1973
+ }
1974
+ function clearStreamingVisibleTextTimer(timerRef) {
1975
+ if (timerRef.current === null) {
1976
+ return;
1977
+ }
1978
+ clearTimeout(timerRef.current);
1979
+ timerRef.current = null;
1980
+ }
1981
+
1688
1982
  // shared/AgentMessageMarkdown.tsx
1689
- import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
1983
+ import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
1690
1984
  var COLLAPSED_LINE_LIMIT = 8;
1691
1985
  var APPROX_CHARS_PER_LINE = 34;
1692
1986
  var DEFERRED_LONG_MARKDOWN_CHAR_THRESHOLD = 4096;
1987
+ var STREAMING_MARKDOWN_EMERGENCY_PLAIN_CHAR_THRESHOLD = 96e3;
1693
1988
  var DEFERRED_LONG_MARKDOWN_FALLBACK_DELAY_MS = 80;
1694
1989
  var DEFERRED_LONG_MARKDOWN_IDLE_TIMEOUT_MS = 700;
1990
+ var STREAMING_MARKDOWN_FRAME_MS = 24;
1991
+ var STREAMING_MARKDOWN_MAX_CHARS_PER_SECOND = 6e3;
1992
+ var STREAMING_MARKDOWN_TAIL_FLUSH_CHARS = 0;
1695
1993
  var PLAIN_SESSION_MENTION_AGENT_LABELS = [
1696
1994
  "Claude Code",
1697
1995
  "Nexight",
@@ -1718,34 +2016,47 @@ function AgentMessageMarkdown({
1718
2016
  inline = false,
1719
2017
  normalizePlainIssueMentionTitle = false,
1720
2018
  deferLongContentRender = false,
1721
- enableImageZoom = false
2019
+ enableImageZoom = false,
2020
+ streaming = false
1722
2021
  }) {
1723
2022
  "use memo";
1724
2023
  const { t } = useTranslation();
2024
+ const visibleContent = useStreamingVisibleText(content, {
2025
+ enabled: streaming,
2026
+ frameMs: STREAMING_MARKDOWN_FRAME_MS,
2027
+ maxCharsPerSecond: STREAMING_MARKDOWN_MAX_CHARS_PER_SECOND,
2028
+ trailingFlushChars: STREAMING_MARKDOWN_TAIL_FLUSH_CHARS
2029
+ });
2030
+ const stabilizedContent = useMemo3(
2031
+ () => stabilizeStreamingMarkdownTail(visibleContent, {
2032
+ streaming
2033
+ }).content,
2034
+ [streaming, visibleContent]
2035
+ );
1725
2036
  const workspaceRoot = workspaceLinkContext?.workspaceRoot ?? null;
1726
2037
  const basePath = workspaceLinkContext?.basePath ?? null;
1727
2038
  const workspaceLinkSource = workspaceLinkContext?.source ?? null;
1728
- const [isExpanded, setIsExpanded] = useState2(false);
2039
+ const [isExpanded, setIsExpanded] = useState3(false);
1729
2040
  const resolvedExpandLabel = expandLabel ?? t("agentHost.workspaceAgentMessageExpand");
1730
- const shouldCollapse = collapsible && isLikelyLongerThanLineLimit(content);
2041
+ const shouldCollapse = collapsible && isLikelyLongerThanLineLimit(stabilizedContent);
1731
2042
  const isCollapsed = shouldCollapse && !isExpanded;
1732
2043
  const ContainerTag = inline ? "span" : "div";
1733
- const contentSignature = useMemo2(
1734
- () => hashMarkdownProfilerContent(content),
1735
- [content]
2044
+ const contentSignature = useMemo3(
2045
+ () => hashMarkdownProfilerContent(stabilizedContent),
2046
+ [stabilizedContent]
1736
2047
  );
1737
- const normalizedContent = useMemo2(
2048
+ const normalizedContent = useMemo3(
1738
2049
  () => linkBareLocalAbsolutePaths(
1739
2050
  normalizeMentionMarkdownLinks(
1740
2051
  normalizePlainIssueMentionTitle ? normalizePlainIssueMentionTitleContent(
1741
- normalizePlainSessionMentionTitle(content)
1742
- ) : normalizePlainSessionMentionTitle(content)
2052
+ normalizePlainSessionMentionTitle(stabilizedContent)
2053
+ ) : normalizePlainSessionMentionTitle(stabilizedContent)
1743
2054
  )
1744
2055
  ),
1745
- [content, normalizePlainIssueMentionTitle]
2056
+ [normalizePlainIssueMentionTitle, stabilizedContent]
1746
2057
  );
1747
2058
  const isMentionOnly = isMentionOnlyMarkdownContent(normalizedContent);
1748
- const shouldDeferMarkdownRender = deferLongContentRender && !inline && content.length >= DEFERRED_LONG_MARKDOWN_CHAR_THRESHOLD && !isExpanded;
2059
+ const shouldDeferMarkdownRender = deferLongContentRender && !inline && content.length >= (streaming ? STREAMING_MARKDOWN_EMERGENCY_PLAIN_CHAR_THRESHOLD : DEFERRED_LONG_MARKDOWN_CHAR_THRESHOLD) && !isExpanded;
1749
2060
  const markdownRenderReady = useDeferredMarkdownRenderReady(
1750
2061
  contentSignature,
1751
2062
  shouldDeferMarkdownRender
@@ -1780,7 +2091,7 @@ function AgentMessageMarkdown({
1780
2091
  },
1781
2092
  [handleLinkClick]
1782
2093
  );
1783
- const markdownComponents = useMemo2(
2094
+ const markdownComponents = useMemo3(
1784
2095
  () => ({
1785
2096
  a: (props) => /* @__PURE__ */ jsx6(
1786
2097
  MarkdownLink,
@@ -1836,7 +2147,13 @@ function AgentMessageMarkdown({
1836
2147
  "data-agent-mention-only": isMentionOnly ? "true" : void 0,
1837
2148
  "data-collapsed": isCollapsed ? "true" : "false",
1838
2149
  onClickCapture: handleAnchorClickCapture,
1839
- children: markdownRenderReady ? /* @__PURE__ */ jsx6(
2150
+ children: markdownRenderReady ? streaming ? /* @__PURE__ */ jsx6(
2151
+ StreamingMarkdownBlocks,
2152
+ {
2153
+ content: normalizedContent,
2154
+ components: markdownComponents
2155
+ }
2156
+ ) : /* @__PURE__ */ jsx6(
1840
2157
  ReactMarkdown,
1841
2158
  {
1842
2159
  remarkPlugins: [remarkGfm],
@@ -1868,6 +2185,92 @@ function AgentMessageMarkdown({
1868
2185
  }
1869
2186
  );
1870
2187
  }
2188
+ function StreamingMarkdownBlocks({
2189
+ content,
2190
+ components
2191
+ }) {
2192
+ const blocks = useMemo3(
2193
+ () => splitStreamingMarkdownBlocks(content),
2194
+ [content]
2195
+ );
2196
+ return /* @__PURE__ */ jsx6(Fragment2, { children: blocks.map((block, index) => /* @__PURE__ */ jsx6(
2197
+ MemoizedMarkdownBlock,
2198
+ {
2199
+ content: block.content,
2200
+ components
2201
+ },
2202
+ `${index}:${hashMarkdownProfilerContent(block.initialKeyContent)}`
2203
+ )) });
2204
+ }
2205
+ var MemoizedMarkdownBlock = memo(function MemoizedMarkdownBlock2({
2206
+ content,
2207
+ components
2208
+ }) {
2209
+ return /* @__PURE__ */ jsx6(
2210
+ ReactMarkdown,
2211
+ {
2212
+ remarkPlugins: [remarkGfm],
2213
+ rehypePlugins: [[rehypeSanitize, MARKDOWN_SANITIZE_SCHEMA]],
2214
+ urlTransform: markdownUrlTransform,
2215
+ components,
2216
+ children: content
2217
+ }
2218
+ );
2219
+ });
2220
+ function splitStreamingMarkdownBlocks(content) {
2221
+ const normalized = content.replace(/\r\n?/g, "\n");
2222
+ if (!normalized) {
2223
+ return [{ content: "", initialKeyContent: "" }];
2224
+ }
2225
+ const lines = normalized.split("\n");
2226
+ const blocks = [];
2227
+ const current = [];
2228
+ let fence = null;
2229
+ for (const line of lines) {
2230
+ current.push(line);
2231
+ const lineFence = parseStreamingFence(line);
2232
+ if (lineFence) {
2233
+ if (!fence) {
2234
+ fence = lineFence;
2235
+ } else if (lineFence.marker === fence.marker && lineFence.length >= fence.length) {
2236
+ fence = null;
2237
+ }
2238
+ continue;
2239
+ }
2240
+ if (!fence && line.trim() === "") {
2241
+ pushStreamingMarkdownBlock(blocks, current);
2242
+ }
2243
+ }
2244
+ pushStreamingMarkdownBlock(blocks, current);
2245
+ return blocks.length > 0 ? blocks : [{ content: normalized, initialKeyContent: normalized }];
2246
+ }
2247
+ function pushStreamingMarkdownBlock(blocks, lines) {
2248
+ if (lines.length === 0) {
2249
+ return;
2250
+ }
2251
+ const content = lines.join("\n");
2252
+ if (!content) {
2253
+ lines.length = 0;
2254
+ return;
2255
+ }
2256
+ blocks.push({
2257
+ content,
2258
+ initialKeyContent: content
2259
+ });
2260
+ lines.length = 0;
2261
+ }
2262
+ function parseStreamingFence(line) {
2263
+ const trimmed = line.trimStart();
2264
+ const marker = trimmed[0];
2265
+ if (marker !== "`" && marker !== "~") {
2266
+ return null;
2267
+ }
2268
+ let length = 0;
2269
+ while (trimmed[length] === marker) {
2270
+ length += 1;
2271
+ }
2272
+ return length >= 3 ? { marker, length } : null;
2273
+ }
1871
2274
  function resolveMarkdownAnchorHref(target) {
1872
2275
  if (!(target instanceof Element)) {
1873
2276
  return null;
@@ -1907,11 +2310,11 @@ function activateMarkdownLinkFromPointer(event, href, onLinkClick) {
1907
2310
  activateMarkdownLink(event, href, onLinkClick);
1908
2311
  }
1909
2312
  function useDeferredMarkdownRenderReady(contentSignature, shouldDefer) {
1910
- const [readySignature, setReadySignature] = useState2(
2313
+ const [readySignature, setReadySignature] = useState3(
1911
2314
  shouldDefer ? null : contentSignature
1912
2315
  );
1913
2316
  const renderReady = !shouldDefer || readySignature === contentSignature;
1914
- useEffect3(() => {
2317
+ useEffect4(() => {
1915
2318
  if (!shouldDefer) {
1916
2319
  setReadySignature(contentSignature);
1917
2320
  return;
@@ -1923,7 +2326,7 @@ function useDeferredMarkdownRenderReady(contentSignature, shouldDefer) {
1923
2326
  if (canceled) {
1924
2327
  return;
1925
2328
  }
1926
- startTransition(() => {
2329
+ startTransition2(() => {
1927
2330
  setReadySignature(contentSignature);
1928
2331
  });
1929
2332
  };
@@ -2232,10 +2635,10 @@ function MarkdownImage({
2232
2635
  const canReadWorkspaceImage = Boolean(workspacePath && readWorkspaceImage);
2233
2636
  const shouldEnableZoom = enableZoom && !isInsideLink;
2234
2637
  const resolvedSrc = typeof src === "string" ? resolveRenderableMarkdownImageSrc(src) : src;
2235
- const [state, setState] = useState2(
2638
+ const [state, setState] = useState3(
2236
2639
  () => canReadWorkspaceImage && workspacePath ? peekCachedMarkdownImageState(workspacePath) ?? { status: "loading" } : null
2237
2640
  );
2238
- useEffect3(() => {
2641
+ useEffect4(() => {
2239
2642
  if (!workspacePath || !readWorkspaceImage) {
2240
2643
  setState(null);
2241
2644
  return;
@@ -2784,9 +3187,9 @@ function workspaceAgentActivityStatusLabel(status, t) {
2784
3187
  import {
2785
3188
  forwardRef as forwardRef2,
2786
3189
  useCallback as useCallback4,
2787
- useEffect as useEffect4,
2788
- useRef as useRef3,
2789
- useState as useState3
3190
+ useEffect as useEffect5,
3191
+ useRef as useRef4,
3192
+ useState as useState4
2790
3193
  } from "react";
2791
3194
  import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
2792
3195
  var MIN_THUMB_HEIGHT = 24;
@@ -2799,14 +3202,14 @@ function CustomScrollbar({
2799
3202
  syncKey
2800
3203
  }) {
2801
3204
  "use memo";
2802
- const trackRef = useRef3(null);
2803
- const dragStateRef = useRef3(null);
2804
- const [scrollbarState, setScrollbarState] = useState3({
3205
+ const trackRef = useRef4(null);
3206
+ const dragStateRef = useRef4(null);
3207
+ const [scrollbarState, setScrollbarState] = useState4({
2805
3208
  scrollable: false,
2806
3209
  thumbHeight: 0,
2807
3210
  thumbTop: 0
2808
3211
  });
2809
- const [dragging, setDragging] = useState3(false);
3212
+ const [dragging, setDragging] = useState4(false);
2810
3213
  const syncScrollbarState = useCallback4(() => {
2811
3214
  const viewport = getViewport();
2812
3215
  if (!viewport) {
@@ -2908,7 +3311,7 @@ function CustomScrollbar({
2908
3311
  },
2909
3312
  [getViewport, scrollbarState.scrollable, scrollbarState.thumbHeight]
2910
3313
  );
2911
- useEffect4(() => {
3314
+ useEffect5(() => {
2912
3315
  if (!dragging) {
2913
3316
  return;
2914
3317
  }
@@ -2933,7 +3336,7 @@ function CustomScrollbar({
2933
3336
  window.removeEventListener("mouseup", handleMouseUp);
2934
3337
  };
2935
3338
  }, [dragging, getViewport, syncScrollbarState]);
2936
- useEffect4(() => {
3339
+ useEffect5(() => {
2937
3340
  const viewport = getViewport();
2938
3341
  if (!viewport) {
2939
3342
  setScrollbarState({ scrollable: false, thumbHeight: 0, thumbTop: 0 });
@@ -2987,7 +3390,7 @@ var CustomScrollArea = forwardRef2(function CustomScrollArea2({
2987
3390
  ...viewportProps
2988
3391
  }, forwardedRef) {
2989
3392
  "use memo";
2990
- const viewportRef = useRef3(null);
3393
+ const viewportRef = useRef4(null);
2991
3394
  const getViewport = useCallback4(() => viewportRef.current, []);
2992
3395
  return /* @__PURE__ */ jsxs5(
2993
3396
  "div",
@@ -3072,4 +3475,4 @@ export {
3072
3475
  normalizeWorkspaceAgentActivityDisplayStatus,
3073
3476
  workspaceAgentActivityStatusLabel
3074
3477
  };
3075
- //# sourceMappingURL=chunk-AF5CXBJN.js.map
3478
+ //# sourceMappingURL=chunk-5Q36BEUM.js.map