@agent-native/core 0.32.2 → 0.32.17

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 (240) hide show
  1. package/README.md +3 -1
  2. package/dist/agent/run-store.d.ts.map +1 -1
  3. package/dist/agent/run-store.js +48 -10
  4. package/dist/agent/run-store.js.map +1 -1
  5. package/dist/agent/thread-data-builder.d.ts +12 -0
  6. package/dist/agent/thread-data-builder.d.ts.map +1 -1
  7. package/dist/agent/thread-data-builder.js +104 -6
  8. package/dist/agent/thread-data-builder.js.map +1 -1
  9. package/dist/cli/app-skill.js +2 -2
  10. package/dist/cli/app-skill.js.map +1 -1
  11. package/dist/cli/code-agent-executor.d.ts.map +1 -1
  12. package/dist/cli/code-agent-executor.js +6 -1
  13. package/dist/cli/code-agent-executor.js.map +1 -1
  14. package/dist/cli/code-agent-output-smoother.d.ts +7 -0
  15. package/dist/cli/code-agent-output-smoother.d.ts.map +1 -0
  16. package/dist/cli/code-agent-output-smoother.js +111 -0
  17. package/dist/cli/code-agent-output-smoother.js.map +1 -0
  18. package/dist/cli/connect.d.ts.map +1 -1
  19. package/dist/cli/connect.js +5 -0
  20. package/dist/cli/connect.js.map +1 -1
  21. package/dist/cli/migrate.d.ts.map +1 -1
  22. package/dist/cli/migrate.js +17 -42
  23. package/dist/cli/migrate.js.map +1 -1
  24. package/dist/cli/skills.d.ts +23 -2
  25. package/dist/cli/skills.d.ts.map +1 -1
  26. package/dist/cli/skills.js +405 -41
  27. package/dist/cli/skills.js.map +1 -1
  28. package/dist/cli/templates-meta.d.ts.map +1 -1
  29. package/dist/cli/templates-meta.js +7 -105
  30. package/dist/cli/templates-meta.js.map +1 -1
  31. package/dist/client/AgentPanel.d.ts.map +1 -1
  32. package/dist/client/AgentPanel.js +41 -7
  33. package/dist/client/AgentPanel.js.map +1 -1
  34. package/dist/client/AgentTaskCard.d.ts.map +1 -1
  35. package/dist/client/AgentTaskCard.js +0 -28
  36. package/dist/client/AgentTaskCard.js.map +1 -1
  37. package/dist/client/AssistantChat.d.ts +8 -23
  38. package/dist/client/AssistantChat.d.ts.map +1 -1
  39. package/dist/client/AssistantChat.js +359 -205
  40. package/dist/client/AssistantChat.js.map +1 -1
  41. package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
  42. package/dist/client/MultiTabAssistantChat.js +254 -14
  43. package/dist/client/MultiTabAssistantChat.js.map +1 -1
  44. package/dist/client/agent-chat-adapter.d.ts.map +1 -1
  45. package/dist/client/agent-chat-adapter.js +14 -9
  46. package/dist/client/agent-chat-adapter.js.map +1 -1
  47. package/dist/client/agent-chat.d.ts +24 -0
  48. package/dist/client/agent-chat.d.ts.map +1 -1
  49. package/dist/client/agent-chat.js +73 -0
  50. package/dist/client/agent-chat.js.map +1 -1
  51. package/dist/client/assistant-ui-recovery.d.ts +34 -0
  52. package/dist/client/assistant-ui-recovery.d.ts.map +1 -0
  53. package/dist/client/assistant-ui-recovery.js +122 -0
  54. package/dist/client/assistant-ui-recovery.js.map +1 -0
  55. package/dist/client/composer/PromptComposer.d.ts.map +1 -1
  56. package/dist/client/composer/PromptComposer.js +7 -1
  57. package/dist/client/composer/PromptComposer.js.map +1 -1
  58. package/dist/client/composer/TiptapComposer.d.ts +7 -1
  59. package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
  60. package/dist/client/composer/TiptapComposer.js +22 -2
  61. package/dist/client/composer/TiptapComposer.js.map +1 -1
  62. package/dist/client/frame-protocol.d.ts +6 -2
  63. package/dist/client/frame-protocol.d.ts.map +1 -1
  64. package/dist/client/frame-protocol.js.map +1 -1
  65. package/dist/client/index.d.ts +2 -1
  66. package/dist/client/index.d.ts.map +1 -1
  67. package/dist/client/index.js +2 -1
  68. package/dist/client/index.js.map +1 -1
  69. package/dist/client/org/OrgSwitcher.d.ts.map +1 -1
  70. package/dist/client/org/OrgSwitcher.js +2 -1
  71. package/dist/client/org/OrgSwitcher.js.map +1 -1
  72. package/dist/client/progress/RunsTray.d.ts +13 -3
  73. package/dist/client/progress/RunsTray.d.ts.map +1 -1
  74. package/dist/client/progress/RunsTray.js +105 -36
  75. package/dist/client/progress/RunsTray.js.map +1 -1
  76. package/dist/client/route-warmup.d.ts +61 -0
  77. package/dist/client/route-warmup.d.ts.map +1 -0
  78. package/dist/client/route-warmup.js +456 -0
  79. package/dist/client/route-warmup.js.map +1 -0
  80. package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
  81. package/dist/client/settings/SettingsPanel.js +2 -1
  82. package/dist/client/settings/SettingsPanel.js.map +1 -1
  83. package/dist/client/settings/useBuilderStatus.d.ts +5 -0
  84. package/dist/client/settings/useBuilderStatus.d.ts.map +1 -1
  85. package/dist/client/settings/useBuilderStatus.js +10 -4
  86. package/dist/client/settings/useBuilderStatus.js.map +1 -1
  87. package/dist/client/use-action.d.ts +1 -0
  88. package/dist/client/use-action.d.ts.map +1 -1
  89. package/dist/client/use-action.js +22 -4
  90. package/dist/client/use-action.js.map +1 -1
  91. package/dist/code-agents/background-run.d.ts +2 -0
  92. package/dist/code-agents/background-run.d.ts.map +1 -1
  93. package/dist/code-agents/background-run.js.map +1 -1
  94. package/dist/db/client.d.ts +1 -1
  95. package/dist/db/client.d.ts.map +1 -1
  96. package/dist/db/client.js +25 -1
  97. package/dist/db/client.js.map +1 -1
  98. package/dist/deploy/build.d.ts +4 -0
  99. package/dist/deploy/build.d.ts.map +1 -1
  100. package/dist/deploy/build.js +171 -14
  101. package/dist/deploy/build.js.map +1 -1
  102. package/dist/deploy/immutable-assets.d.ts +1 -0
  103. package/dist/deploy/immutable-assets.d.ts.map +1 -1
  104. package/dist/deploy/immutable-assets.js +1 -0
  105. package/dist/deploy/immutable-assets.js.map +1 -1
  106. package/dist/index.browser.d.ts +1 -1
  107. package/dist/index.browser.d.ts.map +1 -1
  108. package/dist/index.browser.js +1 -1
  109. package/dist/index.browser.js.map +1 -1
  110. package/dist/index.d.ts +1 -1
  111. package/dist/index.d.ts.map +1 -1
  112. package/dist/index.js +1 -1
  113. package/dist/index.js.map +1 -1
  114. package/dist/mcp/connect-route.d.ts.map +1 -1
  115. package/dist/mcp/connect-route.js +118 -82
  116. package/dist/mcp/connect-route.js.map +1 -1
  117. package/dist/progress/routes.d.ts.map +1 -1
  118. package/dist/progress/routes.js +1 -0
  119. package/dist/progress/routes.js.map +1 -1
  120. package/dist/progress/store.d.ts +13 -0
  121. package/dist/progress/store.d.ts.map +1 -1
  122. package/dist/progress/store.js +18 -0
  123. package/dist/progress/store.js.map +1 -1
  124. package/dist/progress/types.d.ts +2 -0
  125. package/dist/progress/types.d.ts.map +1 -1
  126. package/dist/progress/types.js.map +1 -1
  127. package/dist/scripts/db/wipe-leaked-builder-keys.d.ts +2 -2
  128. package/dist/scripts/db/wipe-leaked-builder-keys.d.ts.map +1 -1
  129. package/dist/scripts/db/wipe-leaked-builder-keys.js +14 -3
  130. package/dist/scripts/db/wipe-leaked-builder-keys.js.map +1 -1
  131. package/dist/server/action-routes.d.ts +1 -0
  132. package/dist/server/action-routes.d.ts.map +1 -1
  133. package/dist/server/action-routes.js +36 -2
  134. package/dist/server/action-routes.js.map +1 -1
  135. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  136. package/dist/server/agent-chat-plugin.js +123 -25
  137. package/dist/server/agent-chat-plugin.js.map +1 -1
  138. package/dist/server/agent-discovery.d.ts.map +1 -1
  139. package/dist/server/agent-discovery.js +14 -1
  140. package/dist/server/agent-discovery.js.map +1 -1
  141. package/dist/server/agent-teams-run-queue.d.ts +80 -0
  142. package/dist/server/agent-teams-run-queue.d.ts.map +1 -0
  143. package/dist/server/agent-teams-run-queue.js +208 -0
  144. package/dist/server/agent-teams-run-queue.js.map +1 -0
  145. package/dist/server/agent-teams.d.ts +67 -0
  146. package/dist/server/agent-teams.d.ts.map +1 -1
  147. package/dist/server/agent-teams.js +607 -180
  148. package/dist/server/agent-teams.js.map +1 -1
  149. package/dist/server/auth-marketing.d.ts.map +1 -1
  150. package/dist/server/auth-marketing.js +0 -64
  151. package/dist/server/auth-marketing.js.map +1 -1
  152. package/dist/server/auth.d.ts.map +1 -1
  153. package/dist/server/auth.js +67 -14
  154. package/dist/server/auth.js.map +1 -1
  155. package/dist/server/builder-browser.d.ts +12 -2
  156. package/dist/server/builder-browser.d.ts.map +1 -1
  157. package/dist/server/builder-browser.js +24 -0
  158. package/dist/server/builder-browser.js.map +1 -1
  159. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  160. package/dist/server/core-routes-plugin.js +66 -5
  161. package/dist/server/core-routes-plugin.js.map +1 -1
  162. package/dist/server/credential-provider.d.ts +10 -0
  163. package/dist/server/credential-provider.d.ts.map +1 -1
  164. package/dist/server/credential-provider.js +82 -3
  165. package/dist/server/credential-provider.js.map +1 -1
  166. package/dist/server/csrf.d.ts.map +1 -1
  167. package/dist/server/csrf.js +3 -0
  168. package/dist/server/csrf.js.map +1 -1
  169. package/dist/server/index.d.ts +1 -0
  170. package/dist/server/index.d.ts.map +1 -1
  171. package/dist/server/index.js +1 -0
  172. package/dist/server/index.js.map +1 -1
  173. package/dist/server/onboarding-html.d.ts +1 -0
  174. package/dist/server/onboarding-html.d.ts.map +1 -1
  175. package/dist/server/onboarding-html.js +14 -1
  176. package/dist/server/onboarding-html.js.map +1 -1
  177. package/dist/server/self-dispatch.d.ts +44 -0
  178. package/dist/server/self-dispatch.d.ts.map +1 -0
  179. package/dist/server/self-dispatch.js +113 -0
  180. package/dist/server/self-dispatch.js.map +1 -0
  181. package/dist/server/social-og-image.d.ts +14 -0
  182. package/dist/server/social-og-image.d.ts.map +1 -0
  183. package/dist/server/social-og-image.js +251 -0
  184. package/dist/server/social-og-image.js.map +1 -0
  185. package/dist/server/ssr-handler.d.ts +1 -1
  186. package/dist/server/ssr-handler.d.ts.map +1 -1
  187. package/dist/server/ssr-handler.js +27 -11
  188. package/dist/server/ssr-handler.js.map +1 -1
  189. package/dist/shared/cache-control.d.ts +7 -0
  190. package/dist/shared/cache-control.d.ts.map +1 -1
  191. package/dist/shared/cache-control.js +7 -0
  192. package/dist/shared/cache-control.js.map +1 -1
  193. package/dist/shared/index.d.ts +1 -1
  194. package/dist/shared/index.d.ts.map +1 -1
  195. package/dist/shared/index.js +1 -1
  196. package/dist/shared/index.js.map +1 -1
  197. package/dist/shared/route-warmup-config.d.ts +28 -0
  198. package/dist/shared/route-warmup-config.d.ts.map +1 -0
  199. package/dist/shared/route-warmup-config.js +58 -0
  200. package/dist/shared/route-warmup-config.js.map +1 -0
  201. package/dist/shared/social-meta.d.ts +5 -0
  202. package/dist/shared/social-meta.d.ts.map +1 -1
  203. package/dist/shared/social-meta.js +36 -2
  204. package/dist/shared/social-meta.js.map +1 -1
  205. package/dist/shared/streaming-text-smoothing.d.ts +12 -0
  206. package/dist/shared/streaming-text-smoothing.d.ts.map +1 -0
  207. package/dist/shared/streaming-text-smoothing.js +52 -0
  208. package/dist/shared/streaming-text-smoothing.js.map +1 -0
  209. package/dist/styles/agent-native.css +4 -4
  210. package/dist/templates/default/AGENTS.md +9 -4
  211. package/dist/templates/default/DEVELOPING.md +15 -1
  212. package/dist/templates/workspace-core/AGENTS.md +7 -3
  213. package/dist/templates/workspace-root/AGENTS.md +7 -3
  214. package/dist/vite/client.d.ts +13 -0
  215. package/dist/vite/client.d.ts.map +1 -1
  216. package/dist/vite/client.js +26 -0
  217. package/dist/vite/client.js.map +1 -1
  218. package/dist/vite/index.d.ts +1 -0
  219. package/dist/vite/index.d.ts.map +1 -1
  220. package/dist/vite/index.js.map +1 -1
  221. package/docs/content/client.md +62 -1
  222. package/docs/content/code-agents-ui.md +6 -13
  223. package/docs/content/context-awareness.md +186 -21
  224. package/docs/content/deployment.md +8 -11
  225. package/docs/content/dispatch.md +1 -1
  226. package/docs/content/external-agents.md +32 -2
  227. package/docs/content/migration-workbench.md +4 -21
  228. package/docs/content/multi-app-workspace.md +1 -1
  229. package/docs/content/recurring-jobs.md +1 -1
  230. package/docs/content/security.md +0 -1
  231. package/docs/content/sharing.md +1 -3
  232. package/docs/content/skills-guide.md +12 -10
  233. package/docs/content/template-assets.md +21 -1
  234. package/docs/content/template-design.md +23 -5
  235. package/docs/content/template-dispatch.md +1 -1
  236. package/package.json +2 -1
  237. package/src/templates/default/AGENTS.md +9 -4
  238. package/src/templates/default/DEVELOPING.md +15 -1
  239. package/src/templates/workspace-core/AGENTS.md +7 -3
  240. package/src/templates/workspace-root/AGENTS.md +7 -3
@@ -1,15 +1,17 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import React, { useState, useRef, useEffect, useCallback, useMemo, forwardRef, useImperativeHandle, } from "react";
3
- import { AssistantRuntimeProvider, useLocalRuntime, useThreadRuntime, useThread, useAui, useComposer, useComposerRuntime, useMessageRuntime, ThreadPrimitive, MessagePrimitive, } from "@assistant-ui/react";
3
+ import { AssistantRuntimeProvider, useLocalRuntime, useThreadRuntime, useThread, useAui, useComposer, useComposerRuntime, useMessagePartText, useMessageRuntime, ThreadPrimitive, MessagePrimitive, } from "@assistant-ui/react";
4
4
  import { CompositeAttachmentAdapter } from "@assistant-ui/react";
5
- import { MarkdownTextPrimitive } from "@assistant-ui/react-markdown";
6
5
  import ReactMarkdown, { defaultUrlTransform } from "react-markdown";
7
6
  import remarkGfm from "remark-gfm";
8
7
  import { createAgentChatAdapter, } from "./agent-chat-adapter.js";
8
+ import { appendAgentChatContextToMessage, formatAgentChatContextItemsForPrompt, normalizeAgentChatContextItem, } from "./agent-chat.js";
9
9
  import { useAgentDynamicSuggestions, } from "./dynamic-suggestions.js";
10
+ import { initialSmoothStreamingGraphemeCount, SMOOTH_STREAMING_COMMIT_INTERVAL_MS, smoothStreamingPunctuationDelayMs, smoothStreamingRevealCount, splitStreamingTextGraphemes, } from "../shared/streaming-text-smoothing.js";
10
11
  import { getActiveRun } from "./active-run-state.js";
11
12
  import { AgentAutoContinueSignal, readSSEStreamRaw, } from "./sse-event-processor.js";
12
13
  import { captureError } from "./analytics.js";
14
+ import { AssistantMessageListErrorBoundary, AssistantUiStaleIndexErrorBoundary, } from "./assistant-ui-recovery.js";
13
15
  import { cn } from "./utils.js";
14
16
  import { writeClipboardText } from "./clipboard.js";
15
17
  import { useNearBottomAutoscroll } from "./conversation/index.js";
@@ -31,6 +33,7 @@ import { AgentComposerFrame } from "./composer/AgentComposerFrame.js";
31
33
  import { isPastedTextAttachmentName } from "./composer/pasted-text.js";
32
34
  import { PastedTextChip } from "./composer/PastedTextChip.js";
33
35
  import { IconMessage, IconX, IconPlayerStop, IconCheck, IconChevronDown, IconCopy, IconTerminal, IconLoader2, IconCircleX, IconSquareFilled, IconClock, IconFile, IconFolder, IconFileText, IconCheckbox, IconMail, IconUser, IconPresentation, IconStack2, IconMessageChatbot, IconLock, IconArrowBackUp, IconExternalLink, IconDots, IconGitFork, IconId, IconQuote, IconGauge, IconArrowRight, IconSettings, IconAlertTriangle, IconRefresh, IconPlayerPlay, IconClipboardList, IconSearch, IconArrowsMaximize, IconArrowsMinimize, IconPlus, } from "@tabler/icons-react";
36
+ export { AssistantMessageListErrorBoundary, AssistantUiStaleIndexErrorBoundary, assistantUiRecoverableRenderErrorKind, isAssistantUiRecoverableRenderError, isAssistantUiStaleIndexError, } from "./assistant-ui-recovery.js";
34
37
  class DownscalingImageAttachmentAdapter {
35
38
  accept = "image/*";
36
39
  async add(state) {
@@ -340,6 +343,9 @@ const markdownStyles = `
340
343
  .agent-markdown table { border-collapse: collapse; margin: 0.5em 0; font-size: 0.875em; }
341
344
  .agent-markdown th, .agent-markdown td { border: 1px solid hsl(var(--border, 0 0% 20%)); padding: 0.35em 0.65em; text-align: left; }
342
345
  .agent-markdown th { font-weight: 600; background: hsl(var(--muted, 0 0% 15%)); color: hsl(var(--foreground, 0 0% 90%)); }
346
+ .agent-markdown[data-streaming="true"] > :last-child:not(pre):not(table)::after { content: ""; display: inline-block; width: 0.42em; height: 1em; margin-left: 0.12em; border-radius: 999px; background: currentColor; opacity: 0.35; transform: translateY(0.16em); animation: agent-markdown-stream-caret 1.15s ease-in-out infinite; }
347
+ @keyframes agent-markdown-stream-caret { 0%, 100% { opacity: 0.2; } 50% { opacity: 0.58; } }
348
+ @media (prefers-reduced-motion: reduce) { .agent-markdown[data-streaming="true"] > :last-child:not(pre):not(table)::after { animation: none; opacity: 0.28; } }
343
349
  `;
344
350
  /**
345
351
  * Pending selection context — written to application_state when the user
@@ -735,17 +741,171 @@ function markdownUrlTransform(value) {
735
741
  return value;
736
742
  return defaultUrlTransform(value);
737
743
  }
738
- function MarkdownText() {
744
+ const TextStreamingContext = React.createContext(false);
745
+ function usePrefersReducedMotion() {
746
+ const [prefersReducedMotion, setPrefersReducedMotion] = useState(() => typeof window !== "undefined" && window.matchMedia
747
+ ? window.matchMedia("(prefers-reduced-motion: reduce)").matches
748
+ : false);
749
+ useEffect(() => {
750
+ if (typeof window === "undefined" || !window.matchMedia)
751
+ return undefined;
752
+ const media = window.matchMedia("(prefers-reduced-motion: reduce)");
753
+ const handleChange = () => setPrefersReducedMotion(media.matches);
754
+ handleChange();
755
+ if (typeof media.addEventListener === "function") {
756
+ media.addEventListener("change", handleChange);
757
+ return () => media.removeEventListener("change", handleChange);
758
+ }
759
+ media.addListener(handleChange);
760
+ return () => media.removeListener(handleChange);
761
+ }, []);
762
+ return prefersReducedMotion;
763
+ }
764
+ function sliceGraphemes(targetText, graphemes, count) {
765
+ if (count >= graphemes.length)
766
+ return targetText;
767
+ if (count <= 0)
768
+ return "";
769
+ return graphemes.slice(0, count).join("");
770
+ }
771
+ function useSmoothStreamingText(targetText, streaming, resetKey) {
772
+ const prefersReducedMotion = usePrefersReducedMotion();
773
+ const [visibleText, setVisibleText] = useState(() => {
774
+ if (!streaming || prefersReducedMotion)
775
+ return targetText;
776
+ const graphemes = splitStreamingTextGraphemes(targetText);
777
+ return sliceGraphemes(targetText, graphemes, initialSmoothStreamingGraphemeCount(graphemes));
778
+ });
779
+ const visibleTextRef = useRef(visibleText);
780
+ const visibleCountRef = useRef(splitStreamingTextGraphemes(visibleText).length);
781
+ const targetTextRef = useRef(targetText);
782
+ const targetGraphemesRef = useRef(splitStreamingTextGraphemes(targetText));
783
+ const frameRef = useRef(null);
784
+ const lastCommitAtRef = useRef(0);
785
+ const pauseUntilRef = useRef(0);
786
+ const resetKeyRef = useRef(resetKey);
787
+ const stepRef = useRef(() => { });
788
+ const commitVisibleCount = useCallback((nextCount) => {
789
+ const graphemes = targetGraphemesRef.current;
790
+ const boundedCount = Math.max(0, Math.min(nextCount, graphemes.length));
791
+ const nextText = sliceGraphemes(targetTextRef.current, graphemes, boundedCount);
792
+ visibleCountRef.current = boundedCount;
793
+ if (visibleTextRef.current !== nextText) {
794
+ visibleTextRef.current = nextText;
795
+ setVisibleText(nextText);
796
+ }
797
+ }, []);
798
+ const cancelFrame = useCallback(() => {
799
+ if (frameRef.current != null &&
800
+ typeof window !== "undefined" &&
801
+ typeof window.cancelAnimationFrame === "function") {
802
+ window.cancelAnimationFrame(frameRef.current);
803
+ }
804
+ frameRef.current = null;
805
+ pauseUntilRef.current = 0;
806
+ }, []);
807
+ const scheduleFrame = useCallback(() => {
808
+ if (frameRef.current != null)
809
+ return;
810
+ if (typeof window === "undefined" ||
811
+ typeof window.requestAnimationFrame !== "function") {
812
+ commitVisibleCount(targetGraphemesRef.current.length);
813
+ return;
814
+ }
815
+ frameRef.current = window.requestAnimationFrame((time) => {
816
+ frameRef.current = null;
817
+ stepRef.current(time);
818
+ });
819
+ }, [commitVisibleCount]);
820
+ stepRef.current = (time) => {
821
+ const targetGraphemes = targetGraphemesRef.current;
822
+ const backlog = targetGraphemes.length - visibleCountRef.current;
823
+ if (backlog <= 0) {
824
+ pauseUntilRef.current = 0;
825
+ return;
826
+ }
827
+ if (pauseUntilRef.current > time) {
828
+ scheduleFrame();
829
+ return;
830
+ }
831
+ const lastCommitAt = lastCommitAtRef.current || time - SMOOTH_STREAMING_COMMIT_INTERVAL_MS;
832
+ if (time - lastCommitAt < SMOOTH_STREAMING_COMMIT_INTERVAL_MS &&
833
+ backlog > 1) {
834
+ scheduleFrame();
835
+ return;
836
+ }
837
+ const revealCount = smoothStreamingRevealCount({
838
+ backlog,
839
+ elapsedMs: Math.min(120, Math.max(8, time - lastCommitAt)),
840
+ });
841
+ if (revealCount > 0) {
842
+ const nextCount = visibleCountRef.current + revealCount;
843
+ commitVisibleCount(nextCount);
844
+ lastCommitAtRef.current = time;
845
+ const nextBacklog = targetGraphemes.length - visibleCountRef.current;
846
+ const pauseMs = smoothStreamingPunctuationDelayMs(targetGraphemes[visibleCountRef.current - 1], nextBacklog);
847
+ pauseUntilRef.current = pauseMs > 0 ? time + pauseMs : 0;
848
+ }
849
+ if (visibleCountRef.current < targetGraphemes.length) {
850
+ scheduleFrame();
851
+ }
852
+ else {
853
+ pauseUntilRef.current = 0;
854
+ }
855
+ };
856
+ useEffect(() => {
857
+ const targetGraphemes = splitStreamingTextGraphemes(targetText);
858
+ targetTextRef.current = targetText;
859
+ targetGraphemesRef.current = targetGraphemes;
860
+ const keyChanged = resetKeyRef.current !== resetKey;
861
+ resetKeyRef.current = resetKey;
862
+ if (!streaming || prefersReducedMotion) {
863
+ cancelFrame();
864
+ commitVisibleCount(targetGraphemes.length);
865
+ return;
866
+ }
867
+ const visibleNoLongerMatchesTarget = visibleTextRef.current.length > 0 &&
868
+ !targetText.startsWith(visibleTextRef.current);
869
+ if (keyChanged ||
870
+ visibleNoLongerMatchesTarget ||
871
+ visibleCountRef.current > targetGraphemes.length) {
872
+ commitVisibleCount(initialSmoothStreamingGraphemeCount(targetGraphemes));
873
+ lastCommitAtRef.current = 0;
874
+ pauseUntilRef.current = 0;
875
+ }
876
+ if (visibleCountRef.current < targetGraphemes.length) {
877
+ scheduleFrame();
878
+ }
879
+ }, [
880
+ targetText,
881
+ streaming,
882
+ prefersReducedMotion,
883
+ resetKey,
884
+ cancelFrame,
885
+ commitVisibleCount,
886
+ scheduleFrame,
887
+ ]);
888
+ useEffect(() => cancelFrame, [cancelFrame]);
889
+ return visibleText;
890
+ }
891
+ function SmoothMarkdownText({ text, streaming, resetKey, statusType = "complete", }) {
739
892
  useEffect(() => {
740
893
  injectMarkdownStyles();
741
894
  }, []);
742
- return (_jsx(MarkdownTextPrimitive
743
- // assistant-ui's smooth renderer can briefly read past its token tap
744
- // cache while React is reconciling streamed messages.
745
- , {
746
- // assistant-ui's smooth renderer can briefly read past its token tap
747
- // cache while React is reconciling streamed messages.
748
- smooth: false, className: "agent-markdown break-words", remarkPlugins: [remarkGfm], components: markdownComponents, urlTransform: markdownUrlTransform }));
895
+ const visibleText = useSmoothStreamingText(text, streaming, resetKey);
896
+ const isVisuallyStreaming = streaming && visibleText !== text;
897
+ return (_jsx("div", { className: "agent-markdown break-words", "data-status": statusType, "data-streaming": isVisuallyStreaming ? "true" : undefined, children: _jsx(ReactMarkdown, { remarkPlugins: [remarkGfm], components: markdownComponents, urlTransform: markdownUrlTransform, children: visibleText }) }));
898
+ }
899
+ function MarkdownText() {
900
+ const textPart = useMessagePartText();
901
+ const messageRuntime = useMessageRuntime();
902
+ const message = messageRuntime.getState();
903
+ const thread = useThread();
904
+ const textStreaming = React.useContext(TextStreamingContext);
905
+ const lastMessage = thread.messages[thread.messages.length - 1];
906
+ const isLastAssistantMessage = message.role === "assistant" && lastMessage?.id === message.id;
907
+ const statusType = textPart.status?.type ?? message.status?.type ?? "complete";
908
+ return (_jsx(SmoothMarkdownText, { text: textPart.text, streaming: textStreaming && isLastAssistantMessage, resetKey: `${message.id}:${statusType}`, statusType: statusType }));
749
909
  }
750
910
  // ─── Composer Attachment Preview ─────────────────────────────────────────────
751
911
  function getImageAttachmentSrc(attachment) {
@@ -1068,7 +1228,7 @@ function ReconnectStreamMessage({ content }) {
1068
1228
  const chatRunning = React.useContext(ChatRunningContext);
1069
1229
  return (_jsx("div", { className: "flex justify-start", children: _jsx("div", { className: "max-w-[95%] text-sm leading-relaxed text-foreground space-y-1", children: content.map((part, i) => {
1070
1230
  if (part.type === "text") {
1071
- return (_jsx("div", { className: "agent-markdown break-words", children: _jsx(ReactMarkdown, { remarkPlugins: [remarkGfm], components: markdownComponents, urlTransform: markdownUrlTransform, children: part.text }) }, `reconnect-text-${i}`));
1231
+ return (_jsx(SmoothMarkdownText, { text: part.text, streaming: chatRunning, resetKey: `reconnect-text-${i}`, statusType: chatRunning ? "running" : "complete" }, `reconnect-text-${i}`));
1072
1232
  }
1073
1233
  if (part.type === "tool-call") {
1074
1234
  return (_jsx(ToolCallDisplay, { toolName: part.toolName, argsText: part.argsText, args: part.args, result: part.result, mcpApp: part.mcpApp, isRunning: part.result === undefined && chatRunning }, `reconnect-tool-${i}`));
@@ -1151,74 +1311,6 @@ function UserMessageText({ text }) {
1151
1311
  export function displayableUserMessageText(text) {
1152
1312
  return text.replace(/<context>[\s\S]*?<\/context>\n?/g, "").trim();
1153
1313
  }
1154
- export function isAssistantUiStaleIndexError(error) {
1155
- const message = error instanceof Error ? error.message : String(error ?? "");
1156
- return /^tapClientLookup: Index \d+ out of bounds \(length: \d+\)$/.test(message);
1157
- }
1158
- export class AssistantUiStaleIndexErrorBoundary extends React.Component {
1159
- state = {
1160
- error: null,
1161
- retryToken: 0,
1162
- };
1163
- retryTimer = null;
1164
- static getDerivedStateFromError(error) {
1165
- return {
1166
- error: error instanceof Error ? error : new Error(String(error ?? "")),
1167
- };
1168
- }
1169
- componentDidCatch(error, info) {
1170
- if (!isAssistantUiStaleIndexError(error))
1171
- return;
1172
- captureError(error, {
1173
- tags: {
1174
- component: this.props.componentName ?? "AssistantChat",
1175
- recoverable: "assistant-ui-stale-message-index",
1176
- },
1177
- extra: {
1178
- resetKey: this.props.resetKey,
1179
- componentStack: info.componentStack,
1180
- },
1181
- });
1182
- if (this.retryTimer)
1183
- return;
1184
- this.retryTimer = setTimeout(() => {
1185
- this.retryTimer = null;
1186
- this.setState((state) => {
1187
- if (!state.error || !isAssistantUiStaleIndexError(state.error)) {
1188
- return null;
1189
- }
1190
- return { error: null, retryToken: state.retryToken + 1 };
1191
- });
1192
- }, 0);
1193
- }
1194
- componentDidUpdate(prevProps) {
1195
- if (this.state.error &&
1196
- isAssistantUiStaleIndexError(this.state.error) &&
1197
- prevProps.resetKey !== this.props.resetKey) {
1198
- this.setState((state) => ({
1199
- error: null,
1200
- retryToken: state.retryToken + 1,
1201
- }));
1202
- }
1203
- }
1204
- componentWillUnmount() {
1205
- if (this.retryTimer) {
1206
- clearTimeout(this.retryTimer);
1207
- }
1208
- }
1209
- render() {
1210
- if (this.state.error) {
1211
- if (!isAssistantUiStaleIndexError(this.state.error)) {
1212
- throw this.state.error;
1213
- }
1214
- return null;
1215
- }
1216
- return (_jsx(React.Fragment, { children: this.props.children }, `${this.props.resetKey}:${this.state.retryToken}`));
1217
- }
1218
- }
1219
- export function AssistantMessageListErrorBoundary({ resetKey, children, }) {
1220
- return (_jsx(AssistantUiStaleIndexErrorBoundary, { resetKey: resetKey, componentName: "AssistantMessageList", children: children }));
1221
- }
1222
1314
  function UserMessageAttachments() {
1223
1315
  const messageRuntime = useMessageRuntime();
1224
1316
  const msg = messageRuntime.getState();
@@ -1865,7 +1957,7 @@ function ensureMessageMetadata(repo) {
1865
1957
  // Re-export for backwards compatibility
1866
1958
  import { extractThreadMeta, normalizeThreadRepository, } from "../agent/thread-data-builder.js";
1867
1959
  export { extractThreadMeta };
1868
- const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateText, suggestions, dynamicSuggestions, emptyStateAddon, showHeader = true, onSwitchToCli, className, apiUrl, tabId, browserTabId, threadId, contextScope, onMessageCountChange, onSaveThread, onGenerateTitle, composerSlot, composerAreaClassName, composerPlaceholder, composerLayoutVariant = "default", centerComposerWhenEmpty = false, emptyStateDisplay = "default", composerToolbarSlot, composerExtraActionButton, composerDisabled = false, composerDisabledPlaceholder, isNewThread, onSlashCommand, execMode, onExecModeChange, planModeDisabled, planModeDisabledReason, selectedModel, defaultModel, selectedEngine, selectedEffort, availableModels, onModelChange, onEffortChange, onForkChat, onConnectProvider, plusMenuMode = "full", providerStatusChecksEnabled = true, loadHistoryRepository, historyReloadKey, }, ref) {
1960
+ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateText, suggestions, dynamicSuggestions, emptyStateAddon, showHeader = true, onSwitchToCli, className, apiUrl, tabId, browserTabId, threadId, contextScope, onMessageCountChange, onSaveThread, onGenerateTitle, composerSlot, composerAreaClassName, composerPlaceholder, composerLayoutVariant = "default", centerComposerWhenEmpty = false, emptyStateDisplay = "default", composerToolbarSlot, composerExtraActionButton, composerDisabled = false, composerDisabledPlaceholder, isNewThread, onSlashCommand, execMode, onExecModeChange, planModeDisabled, planModeDisabledReason, selectedModel, defaultModel, selectedEngine, selectedEffort, availableModels, onModelChange, onEffortChange, onForkChat, onConnectProvider, plusMenuMode = "full", providerStatusChecksEnabled = true, loadHistoryRepository, historyReloadKey, externalStreaming = false, }, ref) {
1869
1961
  const thread = useThread();
1870
1962
  const threadRuntime = useThreadRuntime();
1871
1963
  const composerRuntime = useComposerRuntime();
@@ -1983,6 +2075,38 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
1983
2075
  const [authError, setAuthError] = useState(null);
1984
2076
  const [authSessionAvailable, setAuthSessionAvailable] = useState(false);
1985
2077
  const [queuedMessages, setQueuedMessages] = useState([]);
2078
+ const [composerContextItems, setComposerContextItems] = useState([]);
2079
+ const composerContextItemsRef = useRef([]);
2080
+ const updateComposerContextItems = useCallback((updater) => {
2081
+ setComposerContextItems((previous) => {
2082
+ const next = updater(previous);
2083
+ composerContextItemsRef.current = next;
2084
+ return next;
2085
+ });
2086
+ }, []);
2087
+ const stageComposerContextItem = useCallback((rawItem) => {
2088
+ const item = normalizeAgentChatContextItem(rawItem);
2089
+ if (!item)
2090
+ return;
2091
+ updateComposerContextItems((previous) => {
2092
+ const index = previous.findIndex((current) => current.key === item.key);
2093
+ if (index === -1)
2094
+ return [...previous, item];
2095
+ return previous.map((current, currentIndex) => currentIndex === index ? item : current);
2096
+ });
2097
+ }, [updateComposerContextItems]);
2098
+ const removeComposerContextItem = useCallback((key) => {
2099
+ updateComposerContextItems((previous) => previous.filter((item) => item.key !== key));
2100
+ }, [updateComposerContextItems]);
2101
+ const buildComposerContextSubmission = useCallback((text) => {
2102
+ const context = formatAgentChatContextItemsForPrompt(composerContextItemsRef.current);
2103
+ if (!context)
2104
+ return { text, includesContext: false };
2105
+ return {
2106
+ text: appendAgentChatContextToMessage(text, context),
2107
+ includesContext: true,
2108
+ };
2109
+ }, []);
1986
2110
  // Tracks the JSON of the last queue we successfully persisted so the
1987
2111
  // debounced save effect can skip no-op writes (e.g. restore-from-server
1988
2112
  // on mount, or queue state that hasn't actually changed).
@@ -2009,6 +2133,7 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
2009
2133
  // to an active run the same as running, UNLESS the user has explicitly
2010
2134
  // clicked stop (forceStopped).
2011
2135
  const isRunning = !forceStopped && (isRuntimeRunning || isReconnecting);
2136
+ const textStreaming = isRunning || externalStreaming;
2012
2137
  // UI-only running state — drives the stop button and thinking indicator.
2013
2138
  const showRunningInUI = isRunning;
2014
2139
  const wasRunningRef = useRef(false);
@@ -2844,7 +2969,7 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
2844
2969
  threadId,
2845
2970
  threadRuntime,
2846
2971
  ]);
2847
- const addToQueue = useCallback(async (text, images, references, attachments, requestMode, intent = "queued", recoveryAction) => {
2972
+ const addToQueue = useCallback(async (text, images, references, attachments, requestMode, intent = "queued", recoveryAction, includeComposerContext = false) => {
2848
2973
  materializeFrozenReconnectContent();
2849
2974
  setShowContinue(false);
2850
2975
  setLoopLimitInfo(null);
@@ -2862,6 +2987,10 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
2862
2987
  // exists to stop streaming from yanking the viewport, not to swallow
2863
2988
  // direct sends.
2864
2989
  markNearBottom();
2990
+ const submitted = includeComposerContext
2991
+ ? buildComposerContextSubmission(text)
2992
+ : { text, includesContext: false };
2993
+ const submittedText = submitted.text;
2865
2994
  const queuedAttachments = await serializeQueuedAttachments(attachments);
2866
2995
  const imageAttachments = createAgentImageAttachments(images);
2867
2996
  const messageAttachments = [
@@ -2885,7 +3014,7 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
2885
3014
  id: typeof crypto !== "undefined" && crypto.randomUUID
2886
3015
  ? crypto.randomUUID()
2887
3016
  : `q-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
2888
- text,
3017
+ text: submittedText,
2889
3018
  images,
2890
3019
  attachments: messageAttachments.length > 0 ? messageAttachments : undefined,
2891
3020
  references,
@@ -2897,19 +3026,37 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
2897
3026
  else {
2898
3027
  threadRuntime.append({
2899
3028
  role: "user",
2900
- content: [{ type: "text", text }],
3029
+ content: [{ type: "text", text: submittedText }],
2901
3030
  ...(messageAttachments.length > 0
2902
3031
  ? { attachments: messageAttachments }
2903
3032
  : {}),
2904
3033
  ...createUserMessageRunConfig(references, effectiveRequestMode, recoveryAction),
2905
3034
  });
2906
3035
  }
2907
- }, [execMode, isRunning, materializeFrozenReconnectContent, threadRuntime]);
3036
+ if (submitted.includesContext) {
3037
+ updateComposerContextItems(() => []);
3038
+ }
3039
+ }, [
3040
+ buildComposerContextSubmission,
3041
+ execMode,
3042
+ isRunning,
3043
+ materializeFrozenReconnectContent,
3044
+ threadRuntime,
3045
+ updateComposerContextItems,
3046
+ ]);
2908
3047
  // Expose imperative handle
2909
3048
  useImperativeHandle(ref, () => ({
2910
3049
  sendMessage(text, images) {
2911
3050
  addToQueue(text, images);
2912
3051
  },
3052
+ prefillMessage(text) {
3053
+ tiptapRef.current?.setText(text);
3054
+ tiptapRef.current?.focus();
3055
+ },
3056
+ setComposerContextItem(item) {
3057
+ stageComposerContextItem(item);
3058
+ tiptapRef.current?.focus();
3059
+ },
2913
3060
  sendRecoveryMessage(text, recoveryAction, images) {
2914
3061
  addToQueue(text, images, undefined, undefined, undefined, "queued", recoveryAction);
2915
3062
  },
@@ -2934,7 +3081,13 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
2934
3081
  messageCount: messages.length,
2935
3082
  };
2936
3083
  },
2937
- }), [addToQueue, messages.length, thread.isRunning, threadRuntime]);
3084
+ }), [
3085
+ addToQueue,
3086
+ messages.length,
3087
+ stageComposerContextItem,
3088
+ thread.isRunning,
3089
+ threadRuntime,
3090
+ ]);
2938
3091
  const autoscrollFollowKey = useMemo(() => [
2939
3092
  messages.map(messageFollowKey).join(";"),
2940
3093
  `q:${queuedMessages.map(queuedMessageFollowKey).join("|")}`,
@@ -2942,7 +3095,7 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
2942
3095
  ].join(";;"), [messages, queuedMessages, reconnectContent]);
2943
3096
  const { scrollRef, isNearBottomRef, showScrollToBottom, markNearBottom, scrollToBottom, scrollToBottomAfterPaint, } = useNearBottomAutoscroll({
2944
3097
  followKey: autoscrollFollowKey,
2945
- streaming: isRunning,
3098
+ streaming: textStreaming,
2946
3099
  });
2947
3100
  const scrollToBottomWhileLayoutSettles = useCallback(() => {
2948
3101
  scrollToBottomAfterPaint();
@@ -2977,10 +3130,10 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
2977
3130
  }
2978
3131
  }, [isRestoring, scrollToBottomWhileLayoutSettles]);
2979
3132
  useEffect(() => {
2980
- if (!isRunning && isNearBottomRef.current) {
3133
+ if (!textStreaming && isNearBottomRef.current) {
2981
3134
  scrollToBottomAfterPaint();
2982
3135
  }
2983
- }, [isRunning, scrollToBottomAfterPaint]);
3136
+ }, [textStreaming, scrollToBottomAfterPaint]);
2984
3137
  const { isDevMode: cpDevMode } = useDevMode(apiUrl);
2985
3138
  const checkpointCtx = useMemo(() => ({ apiUrl, devMode: cpDevMode, threadId }), [apiUrl, cpDevMode, threadId]);
2986
3139
  const messageActionsCtx = useMemo(() => ({ onForkChat }), [onForkChat]);
@@ -3044,123 +3197,124 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
3044
3197
  queryKey: ["guided-questions"],
3045
3198
  ...(browserTabId ? { browserTabId } : {}),
3046
3199
  });
3047
- return (_jsx(CheckpointContext.Provider, { value: checkpointCtx, children: _jsx(MessageActionsContext.Provider, { value: messageActionsCtx, children: _jsx(ChatRunningContext.Provider, { value: isRunning, children: _jsxs("div", { "data-agent-empty-state": centeredEmptyState ? "centered" : undefined, className: cn("relative flex flex-1 flex-col h-full min-h-0 text-foreground", className), onDragEnter: handleChatDragEnter, onDragOver: handleChatDragOver, onDragLeave: handleChatDragLeave, onDropCapture: handleChatDropCapture, onDrop: handleChatDrop, children: [dropActive && (_jsx("div", { "aria-hidden": "true", className: "pointer-events-none absolute inset-0 z-50 flex items-center justify-center rounded-md border-2 border-dashed border-primary/70 bg-primary/5 backdrop-blur-[1px]", children: _jsx("span", { className: "rounded-md bg-background/90 px-3 py-1.5 text-xs font-medium text-foreground shadow-sm", children: "Drop to attach" }) })), showHeader && (_jsxs("div", { className: "flex h-11 shrink-0 items-center justify-between border-b border-border px-4", children: [_jsx("span", { className: "text-[13px] font-medium text-muted-foreground", children: "Agent" }), _jsx("div", { className: "flex items-center gap-1", children: onSwitchToCli && (_jsx(TooltipProvider, { delayDuration: 200, children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsxs("button", { onClick: onSwitchToCli, "aria-label": "Switch to CLI", className: "flex items-center gap-1 text-[12px] text-muted-foreground hover:text-foreground px-2 py-1 rounded-md hover:bg-accent", children: [_jsx(IconTerminal, { className: "h-3.5 w-3.5" }), "CLI"] }) }), _jsx(TooltipContent, { children: "Switch to CLI" })] }) })) })] })), _jsx("div", { ref: scrollRef, className: "agent-chat-scroll flex-1 overflow-y-auto overflow-x-hidden min-h-0", children: authError ? (_jsxs("div", { className: "flex flex-col items-center justify-center h-full px-4 gap-3", children: [_jsx("div", { className: "flex h-10 w-10 items-center justify-center rounded-full bg-destructive/10", children: _jsx(IconLock, { className: "h-5 w-5 text-destructive" }) }), _jsxs("div", { className: "text-center max-w-[280px]", children: [_jsx("p", { className: "text-sm font-medium text-foreground mb-1", children: authSessionAvailable
3048
- ? "Chat session needs refresh"
3049
- : authError.sessionExpired
3050
- ? "Session expired"
3051
- : "Authentication required" }), _jsx("p", { className: "text-xs text-muted-foreground leading-relaxed", children: authSessionAvailable
3052
- ? "You're signed in, but this chat connection needs to reconnect."
3053
- : authError.sessionExpired
3054
- ? "Your session may have expired. Log out and log back in to reconnect."
3055
- : "You need to log in to use the agent." })] }), _jsxs("div", { className: "flex gap-2", children: [!authError.sessionExpired && !authSessionAvailable && (_jsx("button", { onClick: () => {
3056
- const ret = window.location.pathname + window.location.search;
3057
- window.location.href =
3058
- agentNativePath("/_agent-native/sign-in") +
3059
- `?return=${encodeURIComponent(ret)}`;
3060
- }, className: "text-xs text-background bg-foreground hover:opacity-90 px-3 py-1.5 rounded-md", children: "Log in" })), authError.sessionExpired && !authSessionAvailable && (_jsx("button", { onClick: async () => {
3061
- try {
3062
- await fetch(agentNativePath("/_agent-native/auth/logout"), {
3063
- method: "POST",
3064
- });
3065
- }
3066
- catch { }
3067
- window.location.reload();
3068
- }, className: "text-xs text-destructive hover:text-destructive/80 px-3 py-1.5 rounded-md border border-destructive/30 hover:bg-destructive/10", children: "Log out" })), _jsx("button", { onClick: () => {
3069
- setAuthError(null);
3070
- window.location.reload();
3071
- }, className: authSessionAvailable
3072
- ? "text-xs text-background bg-foreground hover:opacity-90 px-3 py-1.5 rounded-md"
3073
- : "text-xs text-muted-foreground hover:text-foreground px-3 py-1.5 rounded-md border border-border hover:bg-accent", children: "Refresh chat" })] })] })) : missingApiKey && messages.length === 0 ? (_jsx("div", { className: "flex flex-col items-center justify-center h-full px-2", children: _jsx(BuilderSetupCard, { onConnected: handleBuilderConnected, bouncePulse: missingKeyBouncePulse }) })) : isRestoring ? (_jsxs("div", { className: "flex flex-col gap-3 p-4", children: [_jsx("div", { className: "flex justify-end", children: _jsx("div", { className: "h-8 w-32 rounded-lg bg-muted animate-pulse" }) }), _jsxs("div", { className: "flex flex-col gap-1.5", children: [_jsx("div", { className: "h-4 w-48 rounded bg-muted animate-pulse" }), _jsx("div", { className: "h-4 w-64 rounded bg-muted animate-pulse" }), _jsx("div", { className: "h-4 w-40 rounded bg-muted animate-pulse" })] })] })) : messages.length === 0 && !isReconnecting ? (_jsxs("div", { className: cn("agent-empty-state", emptyStateDisplay === "hidden"
3074
- ? "sr-only"
3075
- : "flex h-full flex-col items-center justify-center gap-4 px-4 py-16"), children: [_jsx("div", { className: "flex h-10 w-10 items-center justify-center rounded-full bg-muted", children: _jsx(IconMessage, { className: "h-5 w-5 text-muted-foreground" }) }), _jsx("p", { className: "text-sm text-muted-foreground text-center max-w-[240px]", children: emptyStateText ?? "How can I help you?" }), emptyStateAddon, resolvedSuggestions && resolvedSuggestions.length > 0 && (_jsx("div", { className: "flex flex-col gap-1.5 w-full max-w-[280px]", children: resolvedSuggestions.map((suggestion) => (_jsx("button", { onClick: () => {
3076
- threadRuntime.append({
3077
- role: "user",
3078
- content: [{ type: "text", text: suggestion }],
3079
- });
3080
- }, className: "w-full rounded-lg border border-border px-3 py-2 text-left text-[13px] text-muted-foreground hover:bg-accent hover:text-foreground", children: suggestion }, suggestion))) }))] })) : (_jsxs("div", { className: "agent-thread-content flex flex-col gap-4 px-4 py-4", children: [_jsx(AssistantMessageListErrorBoundary, { resetKey: messageListResetKey, children: _jsx(ThreadPrimitive.Messages, { components: {
3081
- UserMessage,
3082
- AssistantMessage,
3083
- } }) }), missingApiKey && (_jsx(BuilderSetupCard, { onConnected: handleBuilderConnected, bouncePulse: missingKeyBouncePulse })), visibleLoopLimit && !showRunningInUI && (_jsx(LoopLimitContinueCard, { info: visibleLoopLimit, onContinue: () => {
3084
- setShowContinue(false);
3085
- setLoopLimitInfo(null);
3086
- addToQueue("Continue from where you left off.", undefined, undefined, undefined, undefined, "queued", "continue");
3087
- } })), shouldShowRunError && visibleRunError && (_jsx(RunErrorRecoveryCard, { info: visibleRunError, onContinue: () => {
3088
- setRunErrorInfo(null);
3089
- addToQueue("Continue from where you stopped. Use the partial work above, verify what succeeded, and finish the original request. Do not rerun the exact same failed tool input unless the failure was transient or the user explicitly asked for an exact rerun. Prefer dedicated app actions over raw database edits when they exist.", undefined, undefined, undefined, undefined, "queued", "continue");
3090
- }, onRetry: () => {
3091
- setRunErrorInfo(null);
3092
- addToQueue(lastUserText
3093
- ? `Retry the previous request from a clean approach. Do not rerun the exact same failed tool input unless the failure was transient or the user explicitly asked for an exact rerun. If a provider query failed because of schema, syntax, or type mismatch, diagnose the error and adjust the query first.\n\nOriginal request:\n\n${lastUserText}`
3094
- : "Retry the previous request from a clean approach. Do not rerun the exact same failed tool input unless the failure was transient or the user explicitly asked for an exact rerun. If a provider query failed because of schema, syntax, or type mismatch, diagnose the error and adjust the query first.", undefined, undefined, undefined, undefined, "queued", "retry");
3095
- }, onFork: onForkChat, onDismiss: () => {
3096
- if (visibleRunErrorKey) {
3097
- setDismissedRunErrorKey(visibleRunErrorKey);
3098
- }
3099
- setRunErrorInfo(null);
3100
- } })), (isReconnecting || reconnectFrozen) &&
3101
- reconnectContent.length > 0 && (_jsx(ReconnectStreamMessage, { content: reconnectContent })), queuedMessages.map((msg) => {
3102
- const displayText = msg.text
3103
- .replace(/<context>[\s\S]*?<\/context>\n?/g, "")
3104
- .trim();
3105
- return (_jsx("div", { className: "flex justify-end group", children: _jsxs("div", { className: "relative max-w-[85%] rounded-lg bg-accent/50 text-foreground/60 px-3 py-2 text-sm leading-relaxed whitespace-pre-wrap break-words", children: [_jsxs("div", { className: "flex items-center gap-1.5 text-[10px] text-muted-foreground mb-1 font-medium uppercase tracking-wide", children: [_jsx(IconClock, { className: "h-3 w-3" }), "Queued"] }), displayText, msg.images && msg.images.length > 0 && (_jsx("div", { className: "flex flex-wrap gap-1.5 mt-1.5", children: msg.images.map((img, j) => (_jsx("img", { src: img, alt: "", className: "h-12 w-12 rounded object-cover border border-border/50" }, j))) })), _jsx("button", { type: "button", onClick: () => setQueuedMessages((prev) => prev.filter((m) => m.id !== msg.id)), "aria-label": "Remove from queue", className: "absolute -top-2 -right-2 flex h-5 w-5 items-center justify-center rounded-full border border-border bg-background text-muted-foreground opacity-0 group-hover:opacity-100 focus-visible:opacity-100 hover:text-foreground hover:bg-accent shadow-sm", children: _jsx(IconX, { className: "h-3 w-3" }) })] }) }, msg.id));
3106
- })] })) }), showScrollToBottom && (_jsx("div", { className: "shrink-0 flex justify-center -mb-1", children: _jsx("button", { type: "button", onClick: scrollToBottom, className: "flex h-7 w-7 items-center justify-center rounded-full border border-border bg-background shadow-sm hover:bg-accent", "aria-label": "Scroll to bottom", children: _jsx(IconChevronDown, { className: "h-3.5 w-3.5 text-muted-foreground" }) }) })), composerSlot, guidedQuestions && guidedQuestions.length > 0 && (_jsx("div", { className: "shrink-0 px-3 pb-2 pt-1", children: _jsx("div", { className: "rounded-lg border border-border bg-card/60 shadow-sm", children: _jsx(GuidedQuestionFlow, { questions: guidedQuestions, onSubmit: handleGuidedQuestionsSubmit, onSkip: handleGuidedQuestionsSkip, ...(guidedQuestionsTitle
3107
- ? { title: guidedQuestionsTitle }
3108
- : {}), ...(guidedQuestionsDescription
3109
- ? { description: guidedQuestionsDescription }
3110
- : {}), ...(guidedQuestionsSkipLabel
3111
- ? { skipLabel: guidedQuestionsSkipLabel }
3112
- : {}), ...(guidedQuestionsSubmitLabel
3113
- ? { submitLabel: guidedQuestionsSubmitLabel }
3114
- : {}), className: "h-auto items-stretch justify-stretch bg-transparent" }) }) })), showPlanModeCallout && (_jsx(PlanModeCallout, { canImplementPlan: canImplementPlan, onImplementPlan: handleImplementPlan, onSwitchToAct: handleSwitchToAct })), _jsx(SelectionAttachedPill, {}), showRunningInUI && (_jsx(RunningActivityStatus, { steps: activitySteps, label: isReconnecting
3115
- ? "Reconnecting"
3116
- : SHOW_AGENT_ACTIVITY_STEPS
3117
- ? (activityLabel ?? "Thinking")
3118
- : "Thinking" })), _jsxs(AgentComposerFrame, { layoutVariant: composerLayoutVariant, className: cn(composerAreaClassName, missingApiKey && "cursor-pointer", isComposerDisabled && "opacity-70"), onClick: missingApiKey
3119
- ? () => setMissingKeyBouncePulse((p) => p + 1)
3120
- : undefined, children: [_jsx(ComposerAttachmentPreviewStrip, {}), _jsx(TiptapComposer, { focusRef: tiptapRef, disabled: isComposerDisabled, placeholder: missingApiKey
3121
- ? "Connect an AI engine above to start chatting…"
3122
- : composerDisabled
3123
- ? (composerDisabledPlaceholder ??
3124
- "Open Desktop to use this chat.")
3125
- : isRunning
3126
- ? queuedMessages.length > 0
3127
- ? `${queuedMessages.length} queued — send a follow-up...`
3128
- : "Send a follow-up..."
3129
- : composerPlaceholder, onSubmit: isRunning
3130
- ? (text, references, attachments, options) => void addToQueue(text, undefined, references.length > 0 ? references : undefined, attachments, undefined, options?.intent ?? "immediate")
3131
- : undefined, onSlashCommand: onSlashCommand, execMode: execMode, onExecModeChange: onExecModeChange, planModeDisabled: planModeDisabled, planModeDisabledReason: planModeDisabledReason, selectedModel: selectedModel ?? defaultModel, selectedEffort: selectedEffort, availableModels: availableModels, onModelChange: onModelChange, onEffortChange: onEffortChange, onConnectProvider: onConnectProvider, toolbarSlot: composerToolbarSlot, plusMenuMode: plusMenuMode, layoutVariant: composerLayoutVariant, providerConnectStatusEnabled: providerStatusChecksEnabled, draftScope: threadId || tabId, interceptBuildRequestsForBuilder: true, extraActionButton: composerExtraActionButton || showRunningInUI ? (_jsxs(_Fragment, { children: [composerExtraActionButton, showRunningInUI && (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", onClick: () => {
3132
- // Nuclear stop: flip forceStopped so isRunning is false
3133
- // immediately. This unblocks submission even if the
3134
- // runtime or reconnect state is stuck.
3135
- setForceStopped(true);
3136
- const activeRun = getActiveRun();
3137
- const runIdToAbort = reconnectRunIdRef.current ?? activeRun?.runId;
3138
- userStoppedRunRef.current = {
3139
- at: Date.now(),
3140
- ...(runIdToAbort
3141
- ? { runId: runIdToAbort }
3142
- : {}),
3143
- };
3144
- setRunErrorInfo(null);
3145
- setDismissedRunErrorKey(null);
3146
- if (runIdToAbort) {
3147
- fetch(`${apiUrl}/runs/${encodeURIComponent(runIdToAbort)}/abort`, { method: "POST" }).catch(() => { });
3148
- }
3149
- if (isReconnecting) {
3150
- reconnectAbortRef.current?.abort();
3151
- reconnectAbortRef.current = null;
3152
- reconnectRunIdRef.current = null;
3153
- setIsReconnecting(false);
3154
- setReconnectFrozen(reconnectContent.length > 0);
3155
- }
3156
- threadRuntime.cancelRun();
3157
- window.dispatchEvent(new CustomEvent("agentNative.chatRunning", {
3158
- detail: {
3159
- isRunning: false,
3160
- tabId: tabId || threadId,
3161
- },
3162
- }));
3163
- }, className: "shrink-0 flex h-7 w-7 items-center justify-center rounded-md bg-muted text-foreground hover:bg-muted/80", children: _jsx(IconPlayerStop, { className: "h-3.5 w-3.5" }) }) }), _jsx(TooltipContent, { children: "Stop generating" })] }))] })) : undefined })] })] }) }) }) }));
3200
+ return (_jsx(CheckpointContext.Provider, { value: checkpointCtx, children: _jsx(MessageActionsContext.Provider, { value: messageActionsCtx, children: _jsx(ChatRunningContext.Provider, { value: isRunning, children: _jsx(TextStreamingContext.Provider, { value: textStreaming, children: _jsxs("div", { "data-agent-empty-state": centeredEmptyState ? "centered" : undefined, className: cn("relative flex flex-1 flex-col h-full min-h-0 text-foreground", className), onDragEnter: handleChatDragEnter, onDragOver: handleChatDragOver, onDragLeave: handleChatDragLeave, onDropCapture: handleChatDropCapture, onDrop: handleChatDrop, children: [dropActive && (_jsx("div", { "aria-hidden": "true", className: "pointer-events-none absolute inset-0 z-50 flex items-center justify-center rounded-md border-2 border-dashed border-primary/70 bg-primary/5 backdrop-blur-[1px]", children: _jsx("span", { className: "rounded-md bg-background/90 px-3 py-1.5 text-xs font-medium text-foreground shadow-sm", children: "Drop to attach" }) })), showHeader && (_jsxs("div", { className: "flex h-11 shrink-0 items-center justify-between border-b border-border px-4", children: [_jsx("span", { className: "text-[13px] font-medium text-muted-foreground", children: "Agent" }), _jsx("div", { className: "flex items-center gap-1", children: onSwitchToCli && (_jsx(TooltipProvider, { delayDuration: 200, children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsxs("button", { onClick: onSwitchToCli, "aria-label": "Switch to CLI", className: "flex items-center gap-1 text-[12px] text-muted-foreground hover:text-foreground px-2 py-1 rounded-md hover:bg-accent", children: [_jsx(IconTerminal, { className: "h-3.5 w-3.5" }), "CLI"] }) }), _jsx(TooltipContent, { children: "Switch to CLI" })] }) })) })] })), _jsx("div", { ref: scrollRef, className: "agent-chat-scroll flex-1 overflow-y-auto overflow-x-hidden min-h-0", children: authError ? (_jsxs("div", { className: "flex flex-col items-center justify-center h-full px-4 gap-3", children: [_jsx("div", { className: "flex h-10 w-10 items-center justify-center rounded-full bg-destructive/10", children: _jsx(IconLock, { className: "h-5 w-5 text-destructive" }) }), _jsxs("div", { className: "text-center max-w-[280px]", children: [_jsx("p", { className: "text-sm font-medium text-foreground mb-1", children: authSessionAvailable
3201
+ ? "Chat session needs refresh"
3202
+ : authError.sessionExpired
3203
+ ? "Session expired"
3204
+ : "Authentication required" }), _jsx("p", { className: "text-xs text-muted-foreground leading-relaxed", children: authSessionAvailable
3205
+ ? "You're signed in, but this chat connection needs to reconnect."
3206
+ : authError.sessionExpired
3207
+ ? "Your session may have expired. Log out and log back in to reconnect."
3208
+ : "You need to log in to use the agent." })] }), _jsxs("div", { className: "flex gap-2", children: [!authError.sessionExpired && !authSessionAvailable && (_jsx("button", { onClick: () => {
3209
+ const ret = window.location.pathname + window.location.search;
3210
+ window.location.href =
3211
+ agentNativePath("/_agent-native/sign-in") +
3212
+ `?return=${encodeURIComponent(ret)}`;
3213
+ }, className: "text-xs text-background bg-foreground hover:opacity-90 px-3 py-1.5 rounded-md", children: "Log in" })), authError.sessionExpired && !authSessionAvailable && (_jsx("button", { onClick: async () => {
3214
+ try {
3215
+ await fetch(agentNativePath("/_agent-native/auth/logout"), {
3216
+ method: "POST",
3217
+ });
3218
+ }
3219
+ catch { }
3220
+ window.location.reload();
3221
+ }, className: "text-xs text-destructive hover:text-destructive/80 px-3 py-1.5 rounded-md border border-destructive/30 hover:bg-destructive/10", children: "Log out" })), _jsx("button", { onClick: () => {
3222
+ setAuthError(null);
3223
+ window.location.reload();
3224
+ }, className: authSessionAvailable
3225
+ ? "text-xs text-background bg-foreground hover:opacity-90 px-3 py-1.5 rounded-md"
3226
+ : "text-xs text-muted-foreground hover:text-foreground px-3 py-1.5 rounded-md border border-border hover:bg-accent", children: "Refresh chat" })] })] })) : missingApiKey && messages.length === 0 ? (_jsx("div", { className: "flex flex-col items-center justify-center h-full px-2", children: _jsx(BuilderSetupCard, { onConnected: handleBuilderConnected, bouncePulse: missingKeyBouncePulse }) })) : isRestoring ? (_jsxs("div", { className: "flex flex-col gap-3 p-4", children: [_jsx("div", { className: "flex justify-end", children: _jsx("div", { className: "h-8 w-32 rounded-lg bg-muted animate-pulse" }) }), _jsxs("div", { className: "flex flex-col gap-1.5", children: [_jsx("div", { className: "h-4 w-48 rounded bg-muted animate-pulse" }), _jsx("div", { className: "h-4 w-64 rounded bg-muted animate-pulse" }), _jsx("div", { className: "h-4 w-40 rounded bg-muted animate-pulse" })] })] })) : messages.length === 0 && !isReconnecting ? (_jsxs("div", { className: cn("agent-empty-state", emptyStateDisplay === "hidden"
3227
+ ? "sr-only"
3228
+ : "flex h-full flex-col items-center justify-center gap-4 px-4 py-16"), children: [_jsx("div", { className: "flex h-10 w-10 items-center justify-center rounded-full bg-muted", children: _jsx(IconMessage, { className: "h-5 w-5 text-muted-foreground" }) }), _jsx("p", { className: "text-sm text-muted-foreground text-center max-w-[240px]", children: emptyStateText ?? "How can I help you?" }), emptyStateAddon, resolvedSuggestions && resolvedSuggestions.length > 0 && (_jsx("div", { className: "flex flex-col gap-1.5 w-full max-w-[280px]", children: resolvedSuggestions.map((suggestion) => (_jsx("button", { onClick: () => {
3229
+ threadRuntime.append({
3230
+ role: "user",
3231
+ content: [{ type: "text", text: suggestion }],
3232
+ });
3233
+ }, className: "w-full rounded-lg border border-border px-3 py-2 text-left text-[13px] text-muted-foreground hover:bg-accent hover:text-foreground", children: suggestion }, suggestion))) }))] })) : (_jsxs("div", { className: "agent-thread-content flex flex-col gap-4 px-4 py-4", children: [_jsx(AssistantMessageListErrorBoundary, { resetKey: messageListResetKey, children: _jsx(ThreadPrimitive.Messages, { components: {
3234
+ UserMessage,
3235
+ AssistantMessage,
3236
+ } }) }), missingApiKey && (_jsx(BuilderSetupCard, { onConnected: handleBuilderConnected, bouncePulse: missingKeyBouncePulse })), visibleLoopLimit && !showRunningInUI && (_jsx(LoopLimitContinueCard, { info: visibleLoopLimit, onContinue: () => {
3237
+ setShowContinue(false);
3238
+ setLoopLimitInfo(null);
3239
+ addToQueue("Continue from where you left off.", undefined, undefined, undefined, undefined, "queued", "continue");
3240
+ } })), shouldShowRunError && visibleRunError && (_jsx(RunErrorRecoveryCard, { info: visibleRunError, onContinue: () => {
3241
+ setRunErrorInfo(null);
3242
+ addToQueue("Continue from where you stopped. Use the partial work above, verify what succeeded, and finish the original request. Do not rerun the exact same failed tool input unless the failure was transient or the user explicitly asked for an exact rerun. Prefer dedicated app actions over raw database edits when they exist.", undefined, undefined, undefined, undefined, "queued", "continue");
3243
+ }, onRetry: () => {
3244
+ setRunErrorInfo(null);
3245
+ addToQueue(lastUserText
3246
+ ? `Retry the previous request from a clean approach. Do not rerun the exact same failed tool input unless the failure was transient or the user explicitly asked for an exact rerun. If a provider query failed because of schema, syntax, or type mismatch, diagnose the error and adjust the query first.\n\nOriginal request:\n\n${lastUserText}`
3247
+ : "Retry the previous request from a clean approach. Do not rerun the exact same failed tool input unless the failure was transient or the user explicitly asked for an exact rerun. If a provider query failed because of schema, syntax, or type mismatch, diagnose the error and adjust the query first.", undefined, undefined, undefined, undefined, "queued", "retry");
3248
+ }, onFork: onForkChat, onDismiss: () => {
3249
+ if (visibleRunErrorKey) {
3250
+ setDismissedRunErrorKey(visibleRunErrorKey);
3251
+ }
3252
+ setRunErrorInfo(null);
3253
+ } })), (isReconnecting || reconnectFrozen) &&
3254
+ reconnectContent.length > 0 && (_jsx(ReconnectStreamMessage, { content: reconnectContent })), queuedMessages.map((msg) => {
3255
+ const displayText = msg.text
3256
+ .replace(/<context>[\s\S]*?<\/context>\n?/g, "")
3257
+ .trim();
3258
+ return (_jsx("div", { className: "flex justify-end group", children: _jsxs("div", { className: "relative max-w-[85%] rounded-lg bg-accent/50 text-foreground/60 px-3 py-2 text-sm leading-relaxed whitespace-pre-wrap break-words", children: [_jsxs("div", { className: "flex items-center gap-1.5 text-[10px] text-muted-foreground mb-1 font-medium uppercase tracking-wide", children: [_jsx(IconClock, { className: "h-3 w-3" }), "Queued"] }), displayText, msg.images && msg.images.length > 0 && (_jsx("div", { className: "flex flex-wrap gap-1.5 mt-1.5", children: msg.images.map((img, j) => (_jsx("img", { src: img, alt: "", className: "h-12 w-12 rounded object-cover border border-border/50" }, j))) })), _jsx("button", { type: "button", onClick: () => setQueuedMessages((prev) => prev.filter((m) => m.id !== msg.id)), "aria-label": "Remove from queue", className: "absolute -top-2 -right-2 flex h-5 w-5 items-center justify-center rounded-full border border-border bg-background text-muted-foreground opacity-0 group-hover:opacity-100 focus-visible:opacity-100 hover:text-foreground hover:bg-accent shadow-sm", children: _jsx(IconX, { className: "h-3 w-3" }) })] }) }, msg.id));
3259
+ })] })) }), showScrollToBottom && (_jsx("div", { className: "shrink-0 flex justify-center -mb-1", children: _jsx("button", { type: "button", onClick: scrollToBottom, className: "flex h-7 w-7 items-center justify-center rounded-full border border-border bg-background shadow-sm hover:bg-accent", "aria-label": "Scroll to bottom", children: _jsx(IconChevronDown, { className: "h-3.5 w-3.5 text-muted-foreground" }) }) })), composerSlot, guidedQuestions && guidedQuestions.length > 0 && (_jsx("div", { className: "shrink-0 px-3 pb-2 pt-1", children: _jsx("div", { className: "rounded-lg border border-border bg-card/60 shadow-sm", children: _jsx(GuidedQuestionFlow, { questions: guidedQuestions, onSubmit: handleGuidedQuestionsSubmit, onSkip: handleGuidedQuestionsSkip, ...(guidedQuestionsTitle
3260
+ ? { title: guidedQuestionsTitle }
3261
+ : {}), ...(guidedQuestionsDescription
3262
+ ? { description: guidedQuestionsDescription }
3263
+ : {}), ...(guidedQuestionsSkipLabel
3264
+ ? { skipLabel: guidedQuestionsSkipLabel }
3265
+ : {}), ...(guidedQuestionsSubmitLabel
3266
+ ? { submitLabel: guidedQuestionsSubmitLabel }
3267
+ : {}), className: "h-auto items-stretch justify-stretch bg-transparent" }) }) })), showPlanModeCallout && (_jsx(PlanModeCallout, { canImplementPlan: canImplementPlan, onImplementPlan: handleImplementPlan, onSwitchToAct: handleSwitchToAct })), _jsx(SelectionAttachedPill, {}), showRunningInUI && (_jsx(RunningActivityStatus, { steps: activitySteps, label: isReconnecting
3268
+ ? "Reconnecting"
3269
+ : SHOW_AGENT_ACTIVITY_STEPS
3270
+ ? (activityLabel ?? "Thinking")
3271
+ : "Thinking" })), _jsxs(AgentComposerFrame, { layoutVariant: composerLayoutVariant, className: cn(composerAreaClassName, missingApiKey && "cursor-pointer", isComposerDisabled && "opacity-70"), onClick: missingApiKey
3272
+ ? () => setMissingKeyBouncePulse((p) => p + 1)
3273
+ : undefined, children: [_jsx(ComposerAttachmentPreviewStrip, {}), _jsx(TiptapComposer, { focusRef: tiptapRef, disabled: isComposerDisabled, placeholder: missingApiKey
3274
+ ? "Connect an AI engine above to start chatting…"
3275
+ : composerDisabled
3276
+ ? (composerDisabledPlaceholder ??
3277
+ "Open Desktop to use this chat.")
3278
+ : isRunning
3279
+ ? queuedMessages.length > 0
3280
+ ? `${queuedMessages.length} queued — send a follow-up...`
3281
+ : "Send a follow-up..."
3282
+ : composerPlaceholder, onSubmit: isRunning || composerContextItems.length > 0
3283
+ ? (text, references, attachments, options) => void addToQueue(text, undefined, references.length > 0 ? references : undefined, attachments, undefined, options?.intent ?? "immediate", undefined, true)
3284
+ : undefined, onSlashCommand: onSlashCommand, execMode: execMode, onExecModeChange: onExecModeChange, planModeDisabled: planModeDisabled, planModeDisabledReason: planModeDisabledReason, selectedModel: selectedModel ?? defaultModel, selectedEffort: selectedEffort, availableModels: availableModels, onModelChange: onModelChange, onEffortChange: onEffortChange, onConnectProvider: onConnectProvider, toolbarSlot: composerToolbarSlot, contextItems: composerContextItems, onRemoveContextItem: removeComposerContextItem, plusMenuMode: plusMenuMode, layoutVariant: composerLayoutVariant, providerConnectStatusEnabled: providerStatusChecksEnabled, draftScope: threadId || tabId, interceptBuildRequestsForBuilder: true, extraActionButton: composerExtraActionButton || showRunningInUI ? (_jsxs(_Fragment, { children: [composerExtraActionButton, showRunningInUI && (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", onClick: () => {
3285
+ // Nuclear stop: flip forceStopped so isRunning is false
3286
+ // immediately. This unblocks submission even if the
3287
+ // runtime or reconnect state is stuck.
3288
+ setForceStopped(true);
3289
+ const activeRun = getActiveRun();
3290
+ const runIdToAbort = reconnectRunIdRef.current ??
3291
+ activeRun?.runId;
3292
+ userStoppedRunRef.current = {
3293
+ at: Date.now(),
3294
+ ...(runIdToAbort
3295
+ ? { runId: runIdToAbort }
3296
+ : {}),
3297
+ };
3298
+ setRunErrorInfo(null);
3299
+ setDismissedRunErrorKey(null);
3300
+ if (runIdToAbort) {
3301
+ fetch(`${apiUrl}/runs/${encodeURIComponent(runIdToAbort)}/abort`, { method: "POST" }).catch(() => { });
3302
+ }
3303
+ if (isReconnecting) {
3304
+ reconnectAbortRef.current?.abort();
3305
+ reconnectAbortRef.current = null;
3306
+ reconnectRunIdRef.current = null;
3307
+ setIsReconnecting(false);
3308
+ setReconnectFrozen(reconnectContent.length > 0);
3309
+ }
3310
+ threadRuntime.cancelRun();
3311
+ window.dispatchEvent(new CustomEvent("agentNative.chatRunning", {
3312
+ detail: {
3313
+ isRunning: false,
3314
+ tabId: tabId || threadId,
3315
+ },
3316
+ }));
3317
+ }, className: "shrink-0 flex h-7 w-7 items-center justify-center rounded-md bg-muted text-foreground hover:bg-muted/80", children: _jsx(IconPlayerStop, { className: "h-3.5 w-3.5" }) }) }), _jsx(TooltipContent, { children: "Stop generating" })] }))] })) : undefined })] })] }) }) }) }) }));
3164
3318
  });
3165
3319
  export const AssistantChat = forwardRef(function AssistantChat({ apiUrl = agentNativePath("/_agent-native/agent-chat"), tabId, browserTabId, threadId, contextScope, ...props }, ref) {
3166
3320
  const modelRef = useRef(props.selectedModel);