@marimo-team/frontend 0.15.5 → 0.16.0

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 (223) hide show
  1. package/dist/assets/{ConnectedDataExplorerComponent-Cn5-l2X1.js → ConnectedDataExplorerComponent-BErMbWvG.js} +1 -1
  2. package/dist/assets/{ImageComparisonComponent-CEXMKKA4.js → ImageComparisonComponent-fTHv1Ih0.js} +1 -1
  3. package/dist/assets/{VegaLite-Bt14Ds9k.js → VegaLite-Bdi-TyfY.js} +6 -6
  4. package/dist/assets/_baseEach-CNBxBxvS.js +1 -0
  5. package/dist/assets/_baseMap-D1WHjKrd.js +1 -0
  6. package/dist/assets/_baseUniq-CCgDNtZb.js +1 -0
  7. package/dist/assets/_createAggregator-DcD0kTA5.js +1 -0
  8. package/dist/assets/agent-panel-Crv430aI.js +268 -0
  9. package/dist/assets/agent-panel-D92Mfy1i.css +1 -0
  10. package/dist/assets/{any-language-editor-DiwNT6zp.js → any-language-editor-CQh552Wu.js} +1 -1
  11. package/dist/assets/architectureDiagram-W76B3OCA-BAJeBxzt.js +36 -0
  12. package/dist/assets/{between-horizontal-start-FyewyCGn.js → between-horizontal-start-Boxgxbt_.js} +1 -1
  13. package/dist/assets/{blockDiagram-QIGZ2CNN-BrOkAf_c.js → blockDiagram-QIGZ2CNN-CL-1svEK.js} +1 -1
  14. package/dist/assets/{c4Diagram-FPNF74CW-BHPzDxE2.js → c4Diagram-FPNF74CW-BbEqbCTl.js} +5 -5
  15. package/dist/assets/channel-_2eNSz0n.js +1 -0
  16. package/dist/assets/chat-panel-CXh5Wl6C.js +3 -0
  17. package/dist/assets/{chunk-4BX2VUAB-DLxaCNYh.js → chunk-4BX2VUAB-C--8TXeE.js} +1 -1
  18. package/dist/assets/{chunk-55IACEB6-DdzvO3HR.js → chunk-55IACEB6-Bj00HDqq.js} +1 -1
  19. package/dist/assets/{chunk-FMBD7UC4-R5o-nSiG.js → chunk-FMBD7UC4-C-lhB6hN.js} +1 -1
  20. package/dist/assets/{chunk-K7UQS3LO-DxaMrGgG.js → chunk-K7UQS3LO-B-pGTXPt.js} +1 -1
  21. package/dist/assets/{chunk-QN33PNHL-DqS9-FYm.js → chunk-QN33PNHL-DqUzGhvm.js} +1 -1
  22. package/dist/assets/{chunk-QZHKN3VN-BZ-TzajS.js → chunk-QZHKN3VN-TntJHfSk.js} +1 -1
  23. package/dist/assets/{chunk-TVAH2DTR-BsgP2dyv.js → chunk-TVAH2DTR-HUJb1psV.js} +1 -1
  24. package/dist/assets/{chunk-TZMSLE5B-D-h3ahXI.js → chunk-TZMSLE5B-BK3C__t3.js} +1 -1
  25. package/dist/assets/{circle-play-CQtRZ-rT.js → circle-play-DBLOv1Yu.js} +1 -1
  26. package/dist/assets/classDiagram-KNZD7YFC-BGmh9POF.js +1 -0
  27. package/dist/assets/classDiagram-v2-RKCZMP56-BGmh9POF.js +1 -0
  28. package/dist/assets/{clear-button-BY6Z_ViL.js → clear-button-BeoFbEKH.js} +1 -1
  29. package/dist/assets/clone-BFDSPAj3.js +1 -0
  30. package/dist/assets/command-palette-CXZiSv0I.js +1 -0
  31. package/dist/assets/common-C7oJcmCT.js +1 -0
  32. package/dist/assets/{compile-Ct_jzdKr.js → compile-7L0MwhyI.js} +1 -1
  33. package/dist/assets/cose-bilkent-S5V4N54A-BMkGLcVC.js +1 -0
  34. package/dist/assets/dagre-5GWH7T2D-BJtRienS.js +4 -0
  35. package/dist/assets/{data-grid-overlay-editor-BN_wulc3.js → data-grid-overlay-editor-DBkmGtNs.js} +1 -1
  36. package/dist/assets/datasources-panel-B7FbYLiy.js +1 -0
  37. package/dist/assets/{dependency-graph-panel-BOmSCZf7.js → dependency-graph-panel-DEdOxp2X.js} +4 -4
  38. package/dist/assets/diagram-N5W7TBWH-CmECY3nb.js +24 -0
  39. package/dist/assets/diagram-QEK2KX5R-DMOVSNKD.js +43 -0
  40. package/dist/assets/diagram-S2PKOQOG-BiJ96PNQ.js +24 -0
  41. package/dist/assets/{documentation-panel-BxjJO_Gw.js → documentation-panel-xULhaEv3.js} +1 -1
  42. package/dist/assets/edit-page-BrYda9VE.js +129 -0
  43. package/dist/assets/{ellipsis-vertical-UHbmjI2n.js → ellipsis-vertical-BBqXIlc2.js} +1 -1
  44. package/dist/assets/{empty-state-BIBXzY_0.js → empty-state-B3dA3G5P.js} +1 -1
  45. package/dist/assets/{erDiagram-AWTI2OKA-E84mAle_.js → erDiagram-AWTI2OKA-MP1DiFRo.js} +1 -1
  46. package/dist/assets/{error-panel-MEvQ6K7h.js → error-panel-Cc1sv-Ag.js} +1 -1
  47. package/dist/assets/file-explorer-panel-Bw59Kva1.js +1 -0
  48. package/dist/assets/{flowDiagram-PVAE7QVJ-DfbIRSAW.js → flowDiagram-PVAE7QVJ-BX7caPp7.js} +1 -1
  49. package/dist/assets/{ganttDiagram-OWAHRB6G-DR4HZ1z_.js → ganttDiagram-OWAHRB6G-B462g4Yf.js} +3 -3
  50. package/dist/assets/gitGraphDiagram-NY62KEGX-CGgvZ9-9.js +65 -0
  51. package/dist/assets/{glide-data-editor-nNmo1lPq.js → glide-data-editor-C0gUFZON.js} +4 -4
  52. package/dist/assets/graph-CHRVBzY5.js +1 -0
  53. package/dist/assets/{home-page-9eW6qida.js → home-page-Fb2osjys.js} +3 -3
  54. package/dist/assets/{index-DMomwMcN.js → index-BVgAenPd.js} +1 -1
  55. package/dist/assets/{index-B8llrTSo.js → index-BY93Ejhl.js} +1 -1
  56. package/dist/assets/{index-BFSnz7iM.js → index-C-8WADat.js} +1 -1
  57. package/dist/assets/{index-CPN7TRA1.js → index-C-GhZ7ti.js} +1 -1
  58. package/dist/assets/{index-DyLSuOH1.js → index-C1v_Z9et.js} +1 -1
  59. package/dist/assets/{index-VPWqq2Pg.js → index-C4Tn5NvJ.js} +1 -1
  60. package/dist/assets/{index-BAH034Ue.js → index-C77h_TXN.js} +1 -1
  61. package/dist/assets/{index-Dt9UWeWn.js → index-CQDrxQ0j.js} +1 -1
  62. package/dist/assets/{index-DWOaniGT.js → index-CWMgowgL.js} +1 -1
  63. package/dist/assets/{index-B1_GXGaP.js → index-Clbi_Yaq.js} +1 -1
  64. package/dist/assets/{index-B7yXbrLa.js → index-CpTPJo4k.js} +1 -1
  65. package/dist/assets/{index-CknhX2Vy.css → index-Cx0bsY1w.css} +1 -1
  66. package/dist/assets/{index-DqzMPAC8.js → index-D1vmG6DS.js} +2 -2
  67. package/dist/assets/{index-c6If577Q.js → index-D9UKkrr2.js} +1 -1
  68. package/dist/assets/{index-CB2pnVQG.js → index-DEQvTChO.js} +1 -1
  69. package/dist/assets/{index-OC46250R.js → index-DKEudB02.js} +205 -197
  70. package/dist/assets/{index-CSgxTUzD.js → index-DRMm6SNo.js} +1 -1
  71. package/dist/assets/{index-Bq516OmX.js → index-DoRmcrKM.js} +1 -1
  72. package/dist/assets/{index-DSU75csX.js → index-lYa_leQE.js} +1 -1
  73. package/dist/assets/{index-BLu5CX6z.js → index-vmICa5KN.js} +1 -1
  74. package/dist/assets/{index-uacyUula.js → index-z9bohSQJ.js} +1 -1
  75. package/dist/assets/infoDiagram-STP46IZ2-CVyrdLc8.js +2 -0
  76. package/dist/assets/isEmpty-DU_ogP_D.js +1 -0
  77. package/dist/assets/{journeyDiagram-BIP6EPQ6-BBiFyygf.js → journeyDiagram-BIP6EPQ6-C6EgLP_Q.js} +1 -1
  78. package/dist/assets/{kanban-definition-6OIFK2YF-DhgA6Nt6.js → kanban-definition-6OIFK2YF-BXzYO1yj.js} +4 -4
  79. package/dist/assets/layout-jihVw5-i.js +1 -0
  80. package/dist/assets/linear-C4blANlC.js +1 -0
  81. package/dist/assets/{links-CbvGxbsJ.js → links-D59GIweI.js} +3 -3
  82. package/dist/assets/{logs-panel-B9SmTZAW.js → logs-panel-D401qzZh.js} +1 -1
  83. package/dist/assets/markdown-renderer-Cd9eYyaL.js +263 -0
  84. package/dist/assets/{agent-panel-DpQ6muj-.css → markdown-renderer-ClyzDMmG.css} +1 -1
  85. package/dist/assets/mermaid-BEVuRz_O.js +1 -0
  86. package/dist/assets/{mermaid.core-4nVOEVX3.js → mermaid.core-CaSnaLH0.js} +41 -41
  87. package/dist/assets/min-DUMu_zeK.js +1 -0
  88. package/dist/assets/{mindmap-definition-Q6HEUPPD-CVLQNn1q.js → mindmap-definition-Q6HEUPPD-BXUM5MT2.js} +2 -2
  89. package/dist/assets/{number-overlay-editor-CzRzXLcd.js → number-overlay-editor-4uWXGlPG.js} +1 -1
  90. package/dist/assets/{outline-panel-uvsS-YEQ.js → outline-panel-DIzkvm2I.js} +1 -1
  91. package/dist/assets/packages-panel-CJL0MVlj.js +1 -0
  92. package/dist/assets/{pieDiagram-ADFJNKIX-C5IQ5DBZ.js → pieDiagram-ADFJNKIX-Dxt5PVNo.js} +3 -3
  93. package/dist/assets/{quadrantDiagram-LMRXKWRM-CFXFnQxx.js → quadrantDiagram-LMRXKWRM-D4pUaA31.js} +1 -1
  94. package/dist/assets/{react-plotly-mzdv02_Y.js → react-plotly-cJZ0VWBq.js} +1 -1
  95. package/dist/assets/{requirementDiagram-4UW4RH46-D9bPC89T.js → requirementDiagram-4UW4RH46-DVRTjgas.js} +1 -1
  96. package/dist/assets/run-page-BUEnMC9w.js +1 -0
  97. package/dist/assets/sankeyDiagram-GR3RE2ED-CVFnD9C-.js +10 -0
  98. package/dist/assets/scratchpad-panel-BIgRENkI.js +1 -0
  99. package/dist/assets/secrets-panel-xY5-V_BD.js +1 -0
  100. package/dist/assets/{sequenceDiagram-C3RYC4MD-6N7_hY4k.js → sequenceDiagram-C3RYC4MD-_lY4ZN_S.js} +4 -4
  101. package/dist/assets/{slides-component-EcjC8sDK.js → slides-component-Xjymwj7X.js} +1 -1
  102. package/dist/assets/snippets-panel-CTPYW41n.js +1 -0
  103. package/dist/assets/sortBy-BNZKwiq_.js +1 -0
  104. package/dist/assets/state-C4NiC9tO.js +1 -0
  105. package/dist/assets/stateDiagram-KXAO66HF-Da0JQWCn.js +1 -0
  106. package/dist/assets/stateDiagram-v2-UMBNRL4Z-D5lYZOOt.js +1 -0
  107. package/dist/assets/storage-CMdLzB_c.js +26 -0
  108. package/dist/assets/terminal-BPwTkXae.js +10 -0
  109. package/dist/assets/time-Dv5_Ouz_.js +1 -0
  110. package/dist/assets/{timeline-definition-XQNQX7LJ-BEaynAiY.js → timeline-definition-XQNQX7LJ-Dxh5Zu2e.js} +1 -1
  111. package/dist/assets/tracing-BCIurUfa.js +2 -0
  112. package/dist/assets/{tracing-panel-BmuHLPrY.js → tracing-panel-DAzrzNmm.js} +2 -2
  113. package/dist/assets/{trash-UBqfK4mR.js → trash-Dc6DSjz_.js} +1 -1
  114. package/dist/assets/{tree-XiEycetl.js → tree-jheoerAX.js} +1 -1
  115. package/dist/assets/{treemap-75Q7IDZK-CnuVFbBG.js → treemap-75Q7IDZK-IgpxeGaf.js} +21 -21
  116. package/dist/assets/{ts-tags-CloPe9IY.js → ts-tags-DxCDHihD.js} +1 -1
  117. package/dist/assets/variable-panel-DYAiLBmF.js +1 -0
  118. package/dist/assets/{vega-component-DsTH4tuX.js → vega-component-BpfpiPKI.js} +1 -1
  119. package/dist/assets/{xychartDiagram-6GGTOJPD-Dcz3O-A3.js → xychartDiagram-6GGTOJPD-CmNigJ31.js} +1 -1
  120. package/dist/index.html +2 -2
  121. package/package.json +8 -4
  122. package/src/__tests__/mocks.ts +43 -0
  123. package/src/components/app-config/user-config-form.tsx +32 -0
  124. package/src/components/chat/acp/__tests__/__snapshots__/prompt.test.ts.snap +55 -23
  125. package/src/components/chat/acp/__tests__/context-utils.test.ts +222 -0
  126. package/src/components/chat/acp/__tests__/prompt.test.ts +1 -1
  127. package/src/components/chat/acp/__tests__/state.test.ts +2 -6
  128. package/src/components/chat/acp/agent-docs.tsx +33 -6
  129. package/src/components/chat/acp/agent-panel.css +0 -18
  130. package/src/components/chat/acp/agent-panel.tsx +397 -72
  131. package/src/components/chat/acp/agent-selector.tsx +7 -1
  132. package/src/components/chat/acp/blocks.tsx +40 -10
  133. package/src/components/chat/acp/common.tsx +10 -2
  134. package/src/components/chat/acp/context-utils.ts +127 -0
  135. package/src/components/chat/acp/prompt.ts +34 -10
  136. package/src/components/chat/acp/state.ts +1 -1
  137. package/src/components/chat/acp/types.ts +8 -0
  138. package/src/components/chat/chat-panel.tsx +23 -88
  139. package/src/components/chat/chat-utils.ts +127 -1
  140. package/src/components/chat/markdown-renderer.css +39 -0
  141. package/src/components/chat/markdown-renderer.tsx +7 -38
  142. package/src/components/chat/tool-call-accordion.tsx +113 -23
  143. package/src/components/editor/Cell.tsx +6 -0
  144. package/src/components/editor/actions/name-cell-input.tsx +6 -1
  145. package/src/components/editor/actions/useCellActionButton.tsx +3 -1
  146. package/src/components/editor/ai/__tests__/completion-utils.test.ts +178 -1
  147. package/src/components/editor/ai/add-cell-with-ai.tsx +68 -66
  148. package/src/components/editor/ai/ai-completion-editor.tsx +29 -26
  149. package/src/components/editor/ai/completion-handlers.tsx +44 -6
  150. package/src/components/editor/ai/completion-utils.ts +92 -0
  151. package/src/components/editor/ai/transport/chat-transport.tsx +36 -0
  152. package/src/components/editor/cell/StagedAICell.tsx +51 -0
  153. package/src/components/editor/cell/cell-actions.tsx +2 -1
  154. package/src/components/terminal/__tests__/state.test.ts +207 -0
  155. package/src/components/terminal/hooks.ts +41 -0
  156. package/src/components/terminal/state.ts +75 -0
  157. package/src/components/terminal/terminal.tsx +334 -13
  158. package/src/components/terminal/theme.tsx +56 -0
  159. package/src/core/ai/__tests__/staged-cells.test.ts +356 -0
  160. package/src/core/ai/staged-cells.ts +208 -0
  161. package/src/core/cells/cells.ts +1 -1
  162. package/src/core/codemirror/lsp/federated-lsp.ts +1 -1
  163. package/src/core/islands/main.ts +2 -2
  164. package/src/core/kernel/messages.ts +8 -12
  165. package/src/core/saving/__tests__/filename.test.ts +37 -0
  166. package/src/core/static/__tests__/download-html.test.ts +43 -1
  167. package/src/core/websocket/useMarimoWebSocket.tsx +2 -2
  168. package/src/css/app/Cell.css +11 -0
  169. package/src/plugins/core/RenderHTML.tsx +36 -2
  170. package/src/plugins/core/__test__/RenderHTML.test.ts +72 -0
  171. package/src/plugins/core/registerReactComponent.tsx +28 -0
  172. package/src/plugins/impl/FileBrowserPlugin.tsx +8 -2
  173. package/src/stories/cell.stories.tsx +1 -1
  174. package/src/stories/layout/vertical/one-column.stories.tsx +1 -1
  175. package/src/utils/__tests__/cell-urls.test.ts +29 -0
  176. package/src/utils/__tests__/filenames.test.ts +18 -0
  177. package/src/utils/__tests__/path.test.ts +38 -0
  178. package/src/utils/__tests__/urls.test.ts +56 -1
  179. package/src/utils/errors.ts +9 -0
  180. package/dist/assets/_baseEach-C1FLm7WW.js +0 -1
  181. package/dist/assets/_baseMap-DBVArUYD.js +0 -1
  182. package/dist/assets/_baseUniq-Dk7ZPJ3N.js +0 -1
  183. package/dist/assets/_createAggregator-Bn38fDd3.js +0 -1
  184. package/dist/assets/agent-panel-COUYnuIK.js +0 -475
  185. package/dist/assets/architectureDiagram-W76B3OCA-DBzWQKKu.js +0 -36
  186. package/dist/assets/channel-CjhbjOv4.js +0 -1
  187. package/dist/assets/chat-panel-BPXKoTnZ.js +0 -7
  188. package/dist/assets/chat-panel-Brrs_eeH.css +0 -1
  189. package/dist/assets/classDiagram-KNZD7YFC-DHs5cFzy.js +0 -1
  190. package/dist/assets/classDiagram-v2-RKCZMP56-DHs5cFzy.js +0 -1
  191. package/dist/assets/clone-DM1YNjEn.js +0 -1
  192. package/dist/assets/command-palette-S0bzQp7v.js +0 -1
  193. package/dist/assets/common-B8U9k2Ly.js +0 -1
  194. package/dist/assets/cose-bilkent-S5V4N54A-wz1Sfx7j.js +0 -1
  195. package/dist/assets/dagre-5GWH7T2D-BfpcVBgq.js +0 -4
  196. package/dist/assets/datasources-panel-DfuURLJw.js +0 -1
  197. package/dist/assets/diagram-N5W7TBWH-Bf0oqqQh.js +0 -24
  198. package/dist/assets/diagram-QEK2KX5R-ZTc3qikh.js +0 -43
  199. package/dist/assets/diagram-S2PKOQOG-tLScBy7Z.js +0 -24
  200. package/dist/assets/edit-page-DJ8kJZ9w.js +0 -129
  201. package/dist/assets/file-explorer-panel-CzNUJ63G.js +0 -1
  202. package/dist/assets/gitGraphDiagram-NY62KEGX-C1t6QtVa.js +0 -65
  203. package/dist/assets/graph-CssCVWIq.js +0 -1
  204. package/dist/assets/index-DcCIe7np.js +0 -28
  205. package/dist/assets/infoDiagram-STP46IZ2-CwiAoz9f.js +0 -2
  206. package/dist/assets/layout-DpQrxGW-.js +0 -1
  207. package/dist/assets/linear-NsreOeBF.js +0 -1
  208. package/dist/assets/mermaid-DSt0r6IQ.js +0 -1
  209. package/dist/assets/min-D259kI3t.js +0 -1
  210. package/dist/assets/packages-panel-xMz9W2hW.js +0 -1
  211. package/dist/assets/run-page-Bb68qdhQ.js +0 -1
  212. package/dist/assets/sankeyDiagram-GR3RE2ED-BSJOau8E.js +0 -10
  213. package/dist/assets/scratchpad-panel-BF4BO-U4.js +0 -1
  214. package/dist/assets/secrets-panel-CdIX44dQ.js +0 -1
  215. package/dist/assets/snippets-panel-Dco9h0rb.js +0 -1
  216. package/dist/assets/sortBy-aLGA-PGK.js +0 -1
  217. package/dist/assets/stateDiagram-KXAO66HF-Bd68WT3b.js +0 -1
  218. package/dist/assets/stateDiagram-v2-UMBNRL4Z-BXz_GSwb.js +0 -1
  219. package/dist/assets/storage-CGlP4lCF.js +0 -26
  220. package/dist/assets/terminal-CxkHubcu.js +0 -9
  221. package/dist/assets/time-D2nr1UgQ.js +0 -1
  222. package/dist/assets/tracing-kTqHxa7q.js +0 -2
  223. package/dist/assets/variable-panel-noTnH-AQ.js +0 -1
@@ -20,7 +20,6 @@ import {
20
20
  XIcon,
21
21
  } from "lucide-react";
22
22
  import React from "react";
23
- import { Streamdown } from "streamdown";
24
23
  import { mergeToolCalls } from "use-acp";
25
24
  import { JsonOutput } from "@/components/editor/output/JsonOutput";
26
25
  import { Button } from "@/components/ui/button";
@@ -33,12 +32,14 @@ import { logNever } from "@/utils/assertNever";
33
32
  import { cn } from "@/utils/cn";
34
33
  import { type Base64String, base64ToDataURL } from "@/utils/json/base64";
35
34
  import { Strings } from "@/utils/strings";
35
+ import { MarkdownRenderer } from "../markdown-renderer";
36
36
  import { SimpleAccordion } from "./common";
37
37
  import type {
38
38
  AgentNotificationEvent,
39
39
  AgentThoughtNotificationEvent,
40
40
  ConnectionChangeNotificationEvent,
41
41
  ContentBlockOf,
42
+ CurrentModeUpdateNotificationEvent,
42
43
  ErrorNotificationEvent,
43
44
  PlanNotificationEvent,
44
45
  SessionNotificationEventData,
@@ -307,7 +308,7 @@ export const AgentMessagesBlock = (props: {
307
308
  export const ContentBlocks = (props: { data: ContentBlock[] }) => {
308
309
  const renderBlock = (block: ContentBlock) => {
309
310
  if (block.type === "text") {
310
- return <Streamdown>{block.text}</Streamdown>;
311
+ return <MarkdownRenderer content={block.text} />;
311
312
  }
312
313
  if (block.type === "image") {
313
314
  return <ImageBlock data={block} />;
@@ -371,7 +372,7 @@ export const ResourceBlock = (props: { data: ContentBlockOf<"resource"> }) => {
371
372
  </span>
372
373
  </PopoverTrigger>
373
374
  <PopoverContent className="max-h-96 overflow-y-auto scrollbar-thin">
374
- <Streamdown>{props.data.resource.text}</Streamdown>
375
+ <MarkdownRenderer content={props.data.resource.text} />
375
376
  </PopoverContent>
376
377
  </Popover>
377
378
  );
@@ -495,6 +496,18 @@ export const SessionNotificationsBlock = <
495
496
  return <PlansBlock data={items} />;
496
497
  }
497
498
 
499
+ if (kind === "available_commands_update") {
500
+ return null; // nothing to show
501
+ }
502
+ if (kind === "current_mode_update") {
503
+ const lastItem = items.at(-1);
504
+ if (lastItem?.sessionUpdate !== "current_mode_update") {
505
+ return null;
506
+ } else {
507
+ return <CurrentModeBlock data={lastItem} />;
508
+ }
509
+ }
510
+
498
511
  return (
499
512
  <SimpleAccordion title={items[0].sessionUpdate}>
500
513
  <JsonOutput data={items} format="tree" className="max-h-64" />
@@ -509,6 +522,13 @@ export const SessionNotificationsBlock = <
509
522
  );
510
523
  };
511
524
 
525
+ export const CurrentModeBlock = (props: {
526
+ data: CurrentModeUpdateNotificationEvent;
527
+ }) => {
528
+ const { currentModeId } = props.data;
529
+ return <div>Mode: {currentModeId}</div>;
530
+ };
531
+
512
532
  export const ToolNotificationsBlock = (props: {
513
533
  data: Array<ToolCallNotificationEvent | ToolCallUpdateNotificationEvent>;
514
534
  }) => {
@@ -635,19 +655,29 @@ export const ToolBodyBlock = (props: {
635
655
  | Omit<ToolCallNotificationEvent, "sessionUpdate">
636
656
  | Omit<ToolCallUpdateNotificationEvent, "sessionUpdate">;
637
657
  }) => {
638
- const content = props.data.content
658
+ const { content, locations, status, kind, rawInput } = props.data;
659
+ const textContent = content
639
660
  ?.filter((item) => item.type === "content")
640
661
  .map((item) => item.content);
641
- const diffs = props.data.content?.filter((item) => item.type === "diff");
642
- const locations = props.data.locations;
643
- const isFailed = props.data.status === "failed";
662
+ const diffs = content?.filter((item) => item.type === "diff");
663
+ const isFailed = status === "failed";
644
664
  const hasLocations = locations && locations.length > 0;
645
665
 
646
- if (content?.length === 0 && diffs?.length === 0 && hasLocations) {
666
+ // Completely empty
667
+ if (!content && !hasLocations && rawInput) {
668
+ // Show rawInput
669
+ return (
670
+ <pre className="bg-[var(--slate-2)] p-1 text-muted-foreground border border-[var(--slate-4)] rounded text-xs overflow-auto scrollbar-thin max-h-64">
671
+ <JsonOutput data={rawInput} format="tree" />
672
+ </pre>
673
+ );
674
+ }
675
+
676
+ if (content?.length && hasLocations) {
647
677
  return (
648
678
  <div className="flex flex-col gap-2 pr-2">
649
679
  <span className="text-xs text-muted-foreground">
650
- {capitalize(props.data.kind || "")}{" "}
680
+ {capitalize(kind || "")}{" "}
651
681
  {locations?.map((item) => item.path).join(", ")}
652
682
  </span>
653
683
  </div>
@@ -657,7 +687,7 @@ export const ToolBodyBlock = (props: {
657
687
  return (
658
688
  <div className="flex flex-col gap-2 pr-2">
659
689
  {locations && <LocationsBlock data={locations} />}
660
- {content && <ContentBlocks data={content} />}
690
+ {textContent && <ContentBlocks data={textContent} />}
661
691
  {diffs && !isFailed && <DiffBlocks data={diffs} />}
662
692
  </div>
663
693
  );
@@ -1,5 +1,6 @@
1
1
  /* Copyright 2024 Marimo. All rights reserved. */
2
2
 
3
+ import type { RequestPermissionResponse } from "@zed-industries/agent-client-protocol";
3
4
  import {
4
5
  CheckCircleIcon,
5
6
  Loader2,
@@ -151,7 +152,7 @@ ConnectionStatus.displayName = "ConnectionStatus";
151
152
 
152
153
  interface PermissionRequestProps {
153
154
  permission: NonNullable<AgentPendingPermission>;
154
- onResolve: (option: unknown) => void;
155
+ onResolve: (option: RequestPermissionResponse) => void;
155
156
  }
156
157
 
157
158
  export const PermissionRequest: React.FC<PermissionRequestProps> = memo(
@@ -179,7 +180,14 @@ export const PermissionRequest: React.FC<PermissionRequestProps> = memo(
179
180
  ? "text-[var(--blue-10)]"
180
181
  : "text-[var(--red-10)]"
181
182
  }
182
- onClick={() => onResolve(option)}
183
+ onClick={() =>
184
+ onResolve({
185
+ outcome: {
186
+ outcome: "selected",
187
+ optionId: option.optionId,
188
+ },
189
+ })
190
+ }
183
191
  >
184
192
  {option.kind.startsWith("allow") && (
185
193
  <CheckCircleIcon className="h-3 w-3 mr-1" />
@@ -0,0 +1,127 @@
1
+ /* Copyright 2024 Marimo. All rights reserved. */
2
+
3
+ import type { ContentBlock } from "@zed-industries/agent-client-protocol";
4
+ import type { FileUIPart } from "ai";
5
+ import { getAIContextRegistry } from "@/core/ai/context/context";
6
+ import { store } from "@/core/state/jotai";
7
+ import { blobToString } from "@/utils/fileToBase64";
8
+ import { Logger } from "@/utils/Logger";
9
+
10
+ export interface ContextParseResult {
11
+ contextBlocks: ContentBlock[];
12
+ attachmentBlocks: ContentBlock[];
13
+ }
14
+
15
+ /**
16
+ * Converts File objects to agent protocol resource_link content blocks
17
+ */
18
+ export async function convertFilesToResourceLinks(
19
+ files: File[],
20
+ ): Promise<ContentBlock[]> {
21
+ const resourceLinks: ContentBlock[] = [];
22
+
23
+ for (const file of files) {
24
+ try {
25
+ const dataUrl = await blobToString(file, "dataUrl");
26
+ resourceLinks.push({
27
+ type: "resource_link",
28
+ uri: dataUrl,
29
+ mimeType: file.type,
30
+ name: file.name,
31
+ });
32
+ } catch (error) {
33
+ Logger.error("Error converting file to resource link", {
34
+ fileName: file.name,
35
+ error,
36
+ });
37
+ }
38
+ }
39
+
40
+ return resourceLinks;
41
+ }
42
+
43
+ /**
44
+ * Converts AI context registry attachments to agent protocol resource_link content blocks
45
+ */
46
+ async function convertAiAttachmentsToResourceLinks(
47
+ attachments: FileUIPart[],
48
+ ): Promise<ContentBlock[]> {
49
+ const resourceLinks: ContentBlock[] = [];
50
+
51
+ for (const attachment of attachments) {
52
+ resourceLinks.push({
53
+ type: "resource_link",
54
+ uri: attachment.url,
55
+ mimeType: attachment.mediaType,
56
+ name: attachment.filename ?? attachment.url,
57
+ });
58
+ }
59
+
60
+ return resourceLinks;
61
+ }
62
+
63
+ /**
64
+ * Parses context from the prompt value and returns content blocks for agent prompts.
65
+ * Extracts context references using @ notation and converts them to resource blocks.
66
+ * Also handles attachments from the AI context registry.
67
+ */
68
+ export async function parseContextFromPrompt(
69
+ promptValue: string,
70
+ ): Promise<ContextParseResult> {
71
+ const contextBlocks: ContentBlock[] = [];
72
+ const attachmentBlocks: ContentBlock[] = [];
73
+
74
+ // Skip if no '@' in the input
75
+ if (!promptValue.includes("@")) {
76
+ return { contextBlocks, attachmentBlocks };
77
+ }
78
+
79
+ try {
80
+ const registry = getAIContextRegistry(store);
81
+ const contextIds = registry.parseAllContextIds(promptValue);
82
+
83
+ if (contextIds.length === 0) {
84
+ return { contextBlocks, attachmentBlocks };
85
+ }
86
+
87
+ // Get context string for the registry
88
+ const contextString = registry.formatContextForAI(contextIds);
89
+
90
+ if (contextString.trim()) {
91
+ // Create a resource block with the context information
92
+ contextBlocks.push({
93
+ type: "resource",
94
+ resource: {
95
+ uri: "context.md",
96
+ mimeType: "text/markdown",
97
+ text: contextString,
98
+ },
99
+ });
100
+ }
101
+
102
+ // Get attachments from the AI context registry
103
+ try {
104
+ const aiAttachments = await registry.getAttachmentsForContext(contextIds);
105
+ if (aiAttachments.length > 0) {
106
+ const resourceLinks =
107
+ await convertAiAttachmentsToResourceLinks(aiAttachments);
108
+ attachmentBlocks.push(...resourceLinks);
109
+ Logger.debug("Added AI context attachments", {
110
+ count: aiAttachments.length,
111
+ });
112
+ }
113
+ } catch (error) {
114
+ Logger.error("Error getting AI context attachments", { error });
115
+ }
116
+
117
+ Logger.debug("Parsed context for agent", {
118
+ contextIds,
119
+ contextLength: contextString.length,
120
+ attachmentCount: attachmentBlocks.length,
121
+ });
122
+ } catch (error) {
123
+ Logger.error("Error parsing context for agent", { error });
124
+ }
125
+
126
+ return { contextBlocks, attachmentBlocks };
127
+ }
@@ -3,7 +3,7 @@
3
3
  export function getAgentPrompt(filename: string) {
4
4
  return `
5
5
  I am currently editing a marimo notebook.
6
- You can read or write to the notebook at @${filename}
6
+ You can read or write to the notebook at ${filename}
7
7
 
8
8
  If you make edits to the notebook, only edit the contents inside the function decorator with @app.cell.
9
9
  marimo will automatically handle adding the parameters and return statement of the function. For example,
@@ -11,7 +11,7 @@ export function getAgentPrompt(filename: string) {
11
11
 
12
12
  \`\`\`
13
13
  @app.cell
14
- def __():
14
+ def _():
15
15
  <your code here>
16
16
  return
17
17
  \`\`\`
@@ -37,6 +37,7 @@ export function getAgentPrompt(filename: string) {
37
37
  7. The last expression in a cell is automatically displayed, just like in Jupyter notebooks.
38
38
  8. Don't include comments in markdown cells
39
39
  9. Don't include comments in SQL cells
40
+ 10. Never define anything using \`global\`.
40
41
 
41
42
  ## Reactivity
42
43
 
@@ -46,6 +47,7 @@ export function getAgentPrompt(filename: string) {
46
47
  - UI elements trigger updates when their values change without explicit callbacks
47
48
  - UI element values are accessed through \`.value\` attribute
48
49
  - You cannot access a UI element's value in the same cell where it's defined
50
+ - Cells prefixed with an underscore (e.g. _my_var) are local to the cell and cannot be accessed by other cells
49
51
 
50
52
  ## Best Practices
51
53
 
@@ -115,14 +117,29 @@ export function getAgentPrompt(filename: string) {
115
117
 
116
118
  - \`mo.md(text)\` - display markdown
117
119
  - \`mo.stop(predicate, output=None)\` - stop execution conditionally
120
+ - \`mo.output.append(value)\` - append to the output when it is not the last expression
121
+ - \`mo.output.replace(value)\` - replace the output when it is not the last expression
118
122
  - \`mo.Html(html)\` - display HTML
119
123
  - \`mo.image(image)\` - display an image
120
124
  - \`mo.hstack(elements)\` - stack elements horizontally
121
125
  - \`mo.vstack(elements)\` - stack elements vertically
122
126
  - \`mo.tabs(elements)\` - create a tabbed interface
123
127
 
128
+
129
+
124
130
  ## Examples
125
131
 
132
+ <example title="Markdown ccell">
133
+ ${formatCells([
134
+ `
135
+ mo.md("""
136
+ # Hello world
137
+ This is a _markdown_ **cell**.
138
+ """)
139
+ `,
140
+ ])}
141
+ </example>
142
+
126
143
  <example title="Basic UI with reactivity">
127
144
  ${formatCells([
128
145
  `
@@ -207,6 +224,19 @@ export function getAgentPrompt(filename: string) {
207
224
  ])}
208
225
  </example>
209
226
 
227
+ <example title="Conditional Outputs">
228
+ ${formatCells([
229
+ `
230
+ mo.stop(not data.value, mo.md("No data to display"))
231
+
232
+ if mode.value == "scatter":
233
+ mo.output.replace(render_scatter(data.value))
234
+ else:
235
+ mo.output.replace(render_bar_chart(data.value))
236
+ `,
237
+ ])}
238
+ </example>
239
+
210
240
  <example title="Interactive chart with Altair">
211
241
  ${formatCells([
212
242
  `
@@ -256,25 +286,19 @@ export function getAgentPrompt(filename: string) {
256
286
  }
257
287
 
258
288
  function formatCells(cells: string[]) {
259
- // Option 1:
260
- // return cells.map((cell) => {
261
- // return `# Cell ${cell}`;
262
- // });
263
-
264
289
  const indent = " ";
265
290
  const indentCode = (code: string) => {
266
291
  return code
267
292
  .trim()
268
293
  .split("\n")
269
- .map((line) => indent + line)
294
+ .map((line) => (indent + line).trimEnd())
270
295
  .join("\n");
271
296
  };
272
297
 
273
- // Option 2:
274
298
  const formatCell = (cell: string) => {
275
299
  return `
276
300
  @app.cell
277
- def __():
301
+ def _():
278
302
  ${indentCode(cell)}
279
303
  return
280
304
  `;
@@ -259,5 +259,5 @@ export function getAgentSessionSupport(
259
259
  export function getAgentConnectionCommand(agentId: ExternalAgentId): string {
260
260
  const port = AGENT_CONFIG[agentId].port;
261
261
  const command = AGENT_CONFIG[agentId].command;
262
- return `npx supergateway --stdio\\\n "${command}" \\\n --outputTransport ws --port ${port} `;
262
+ return `npx stdio-to-ws "${command}" --port ${port}`;
263
263
  }
@@ -2,10 +2,16 @@
2
2
  import type { ContentBlock } from "@zed-industries/agent-client-protocol";
3
3
  import type { groupNotifications, useAcpClient } from "use-acp";
4
4
 
5
+ export type SessionMode = ReturnType<typeof useAcpClient>["sessionMode"];
6
+
5
7
  export type NotificationEvent = Awaited<
6
8
  ReturnType<typeof groupNotifications>
7
9
  >[number][number];
8
10
 
11
+ export type AvailableCommands = ReturnType<
12
+ typeof useAcpClient
13
+ >["availableCommands"];
14
+
9
15
  export type AgentConnectionState = ReturnType<
10
16
  typeof useAcpClient
11
17
  >["connectionState"];
@@ -49,6 +55,8 @@ export type ToolCallNotificationEvent = NotificationDataOf<"tool_call">;
49
55
  export type ToolCallUpdateNotificationEvent =
50
56
  NotificationDataOf<"tool_call_update">;
51
57
  export type PlanNotificationEvent = NotificationDataOf<"plan">;
58
+ export type CurrentModeUpdateNotificationEvent =
59
+ NotificationDataOf<"current_mode_update">;
52
60
 
53
61
  export type ContentBlockType = ContentBlock["type"];
54
62
  export type ContentBlockOf<T extends ContentBlockType> = Extract<
@@ -48,7 +48,6 @@ import { DEFAULT_AI_MODEL } from "@/core/config/config-schema";
48
48
  import { FeatureFlagged } from "@/core/config/feature-flag";
49
49
  import { useRequestClient } from "@/core/network/requests";
50
50
  import { useRuntimeManager } from "@/core/runtime/config";
51
- import type { ChatMessage } from "@/plugins/impl/chat/types";
52
51
  import { ErrorBanner } from "@/plugins/impl/common/error-banner";
53
52
  import { cn } from "@/utils/cn";
54
53
  import { timeAgo } from "@/utils/dates";
@@ -59,7 +58,6 @@ import { PromptInput } from "../editor/ai/add-cell-with-ai";
59
58
  import {
60
59
  addContextCompletion,
61
60
  CONTEXT_TRIGGER,
62
- getAICompletionBodyWithAttachments,
63
61
  } from "../editor/ai/completion-utils";
64
62
  import { PanelEmptyState } from "../editor/chrome/panels/empty-state";
65
63
  import { CopyClipboardIcon } from "../icons/copy-icon";
@@ -68,8 +66,11 @@ import { Tooltip, TooltipProvider } from "../ui/tooltip";
68
66
  import { toast } from "../ui/use-toast";
69
67
  import { AttachmentRenderer, FileAttachmentPill } from "./chat-components";
70
68
  import {
69
+ buildCompletionRequestBody,
71
70
  convertToFileUIPart,
72
71
  generateChatTitle,
72
+ handleToolCall,
73
+ hasPendingToolCalls,
73
74
  isLastMessageReasoning,
74
75
  } from "./chat-utils";
75
76
  import { MarkdownRenderer } from "./markdown-renderer";
@@ -233,6 +234,7 @@ const ChatMessageDisplay: React.FC<ChatMessageProps> = memo(
233
234
  index={i}
234
235
  toolName={part.type}
235
236
  result={part.output}
237
+ className="my-2"
236
238
  state={part.state}
237
239
  />
238
240
  );
@@ -265,6 +267,7 @@ const ChatMessageDisplay: React.FC<ChatMessageProps> = memo(
265
267
  toolName={part.type}
266
268
  result={part.output}
267
269
  state={part.state}
270
+ className="my-2"
268
271
  />
269
272
  );
270
273
 
@@ -282,10 +285,11 @@ const ChatMessageDisplay: React.FC<ChatMessageProps> = memo(
282
285
  Logger.error("Unhandled part type:", part.type);
283
286
  try {
284
287
  return (
285
- <MarkdownRenderer
286
- key={i}
287
- content={JSON.stringify(part, null, 2)}
288
- />
288
+ <div className="text-xs text-muted-foreground" key={i}>
289
+ <MarkdownRenderer
290
+ content={JSON.stringify(part, null, 2)}
291
+ />
292
+ </div>
289
293
  );
290
294
  } catch (error) {
291
295
  Logger.error("Error rendering part:", part.type, error);
@@ -429,7 +433,7 @@ const ChatInputFooter: React.FC<ChatInputFooterProps> = memo(
429
433
  </>
430
434
  )}
431
435
 
432
- <Tooltip content="Submit">
436
+ <Tooltip content={isLoading ? "Stop" : "Submit"}>
433
437
  <Button
434
438
  variant="text"
435
439
  size="sm"
@@ -565,80 +569,20 @@ const ChatPanelBody = () => {
565
569
  id: chatId,
566
570
  } = useChat({
567
571
  id: activeChatId,
568
- // Only automatically submit if we have tool calls but no text response yet
569
- sendAutomaticallyWhen: ({ messages }) => {
570
- if (messages.length === 0) {
571
- return false;
572
- }
573
-
574
- const lastMessage = messages[messages.length - 1];
575
- const parts = lastMessage.parts;
576
-
577
- if (parts.length === 0) {
578
- return false;
579
- }
580
-
581
- // Only auto-send if the last message is an assistant message
582
- // Because assistant messages are the ones that can have tool calls
583
- if (lastMessage.role !== "assistant") {
584
- return false;
585
- }
586
-
587
- const toolParts = parts.filter((part) =>
588
- part.type.startsWith("tool-"),
589
- ) as ToolUIPart[];
590
-
591
- const hasCompletedToolCalls = toolParts.some(
592
- (part) => part.state === "output-available",
593
- );
594
-
595
- // Check if the last part has any text content
596
- const lastPart = parts[parts.length - 1];
597
- const hasTextContent =
598
- lastPart.type === "text" && lastPart.text?.trim().length > 0;
599
-
600
- // Only auto-send if we have completed tool calls and there is no reply yet
601
- return hasCompletedToolCalls && !hasTextContent;
602
- },
572
+ sendAutomaticallyWhen: ({ messages }) => hasPendingToolCalls(messages),
603
573
  messages: activeChat?.messages || [], // initial messages
604
574
  transport: new DefaultChatTransport({
605
575
  api: runtimeManager.getAiURL("chat").toString(),
606
576
  headers: runtimeManager.headers(),
607
577
  prepareSendMessagesRequest: async (options) => {
608
- // Map from parts to a single string
609
- function toContent(parts: UIMessage["parts"]): string {
610
- return parts
611
- .map((part) => (part.type === "text" ? part.text : ""))
612
- .join("\n");
613
- }
614
-
615
- const input = toContent(options.messages.flatMap((m) => m.parts));
616
- const completionBody = await getAICompletionBodyWithAttachments({
617
- input,
618
- });
619
-
620
- // Map from UIMessage to our ChatMessage type
621
- // If it's the last message, add the attachments from the completion body
622
- function mapMessage(m: UIMessage, isLastMessage: boolean): ChatMessage {
623
- const parts = m.parts;
624
- if (isLastMessage) {
625
- parts.push(...completionBody.attachments);
626
- }
627
- return {
628
- role: m.role,
629
- content: toContent(m.parts),
630
- parts: parts,
631
- };
632
- }
578
+ const completionBody = await buildCompletionRequestBody(
579
+ options.messages,
580
+ );
633
581
 
634
582
  return {
635
583
  body: {
636
584
  ...options,
637
- ...completionBody.body,
638
- messages: options.messages.map((m, idx) => ({
639
- ...m,
640
- ...mapMessage(m, idx === options.messages.length - 1),
641
- })),
585
+ ...completionBody,
642
586
  },
643
587
  };
644
588
  },
@@ -653,24 +597,15 @@ const ChatPanelBody = () => {
653
597
  });
654
598
  },
655
599
  onToolCall: async ({ toolCall }) => {
656
- try {
657
- const response = await invokeAiTool({
600
+ await handleToolCall({
601
+ invokeAiTool,
602
+ addToolResult,
603
+ toolCall: {
658
604
  toolName: toolCall.toolName,
659
- arguments: toolCall.input as Record<string, never>,
660
- });
661
- addToolResult({
662
- tool: toolCall.toolName,
663
605
  toolCallId: toolCall.toolCallId,
664
- output: response.result || response.error,
665
- });
666
- } catch (error) {
667
- Logger.error("Tool call failed:", error);
668
- addToolResult({
669
- tool: toolCall.toolName,
670
- toolCallId: toolCall.toolCallId,
671
- output: `Error: ${error instanceof Error ? error.message : String(error)}`,
672
- });
673
- }
606
+ input: toolCall.input as Record<string, never>,
607
+ },
608
+ });
674
609
  },
675
610
  onError: (error) => {
676
611
  Logger.error("An error occurred:", error);