@marimo-team/islands 0.25.3-dev189 → 0.25.3-dev191

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 (77) hide show
  1. package/dist/{ConnectedDataExplorerComponent-Le_OBfmt.js → ConnectedDataExplorerComponent-BBbOElk3.js} +4 -4
  2. package/dist/{ErrorBoundary-CWtCjh3m.js → ErrorBoundary--X0z83Yx.js} +2 -2
  3. package/dist/{abnfDiagram-VCTEODGH-Tf1ghyQQ.js → abnfDiagram-VCTEODGH-DhMT6Uhd.js} +2 -2
  4. package/dist/{architectureDiagram-5GKGNRK7-xlnbTG0T.js → architectureDiagram-5GKGNRK7-BVbEiJLq.js} +1 -1
  5. package/dist/assets/__vite-browser-external-C8gQho07.js +1 -0
  6. package/dist/assets/{worker-C1pfNBsx.js → worker-C6e9ERyd.js} +2 -2
  7. package/dist/{chat-ui-X-cWrL9u.js → chat-ui-BguxTQEL.js} +7 -7
  8. package/dist/{chunk-IMKFNOWR-Cmd4a5jy.js → chunk-IMKFNOWR-By5hSbJJ.js} +1 -1
  9. package/dist/{chunk-SHT3W25Y-mSNp-WpO.js → chunk-SHT3W25Y-DFrEYG54.js} +1 -1
  10. package/dist/{chunk-SVP7TREG-CWC49D8X.js → chunk-SVP7TREG-DFXGc9ew.js} +1 -1
  11. package/dist/{chunk-TICWLB2K-De9FINtG.js → chunk-TICWLB2K-DZdNIpoZ.js} +1 -1
  12. package/dist/{classDiagram-ZZMXUADV-DweTkYdp.js → classDiagram-ZZMXUADV-Cgj_eEQA.js} +2 -2
  13. package/dist/{classDiagram-v2-VYDZK3BY-A6MR2TiE.js → classDiagram-v2-VYDZK3BY-DLAUrSqj.js} +2 -2
  14. package/dist/{code-visibility-BZDPS5dP.js → code-visibility-uDyLo9_i.js} +4 -4
  15. package/dist/{common-rusLIanu.js → common-DWKY45Md.js} +9 -9
  16. package/dist/{cynefinDiagram-5FMLGOSQ-BSvo5le-.js → cynefinDiagram-5FMLGOSQ-Cikf9AKa.js} +1 -1
  17. package/dist/{dagre-GXQ25YYZ-tXRQslO6.js → dagre-GXQ25YYZ-C5KEi5_l.js} +1 -1
  18. package/dist/{data-grid-overlay-editor-Dc_HCUPe.js → data-grid-overlay-editor-CRnYWD1L.js} +1 -1
  19. package/dist/{diagram-S7CK7UJ4-r3t5VtwI.js → diagram-S7CK7UJ4-BaoBTQjR.js} +1 -1
  20. package/dist/{diagram-UQ7AKVKN-qgbC8YnT.js → diagram-UQ7AKVKN-DDufBsUA.js} +1 -1
  21. package/dist/{diagram-VX7I27RA-CiHxEwtp.js → diagram-VX7I27RA-CY6RALJ8.js} +1 -1
  22. package/dist/{diagram-Z3DM3KII-D95u15Ev.js → diagram-Z3DM3KII-Bc-LHsD6.js} +1 -1
  23. package/dist/{dropdown-menu-DCMbbQ35.js → dropdown-menu-drDjxJyV.js} +1 -1
  24. package/dist/{ebnfDiagram-PWID7BFC-P7e4bryK.js → ebnfDiagram-PWID7BFC-Cz0J-tcC.js} +2 -2
  25. package/dist/{erDiagram-RLTQ6QDP-CUHZcmRj.js → erDiagram-RLTQ6QDP-DTXTDgFu.js} +1 -1
  26. package/dist/{error-entries-mpoD5t79.js → error-entries-CZ1NQ6Kv.js} +4 -4
  27. package/dist/{feedback-button-C3gqiY5b.js → feedback-button-CDf1yRm_.js} +5 -5
  28. package/dist/{flowDiagram-HODETNUW-BGZ95AzI.js → flowDiagram-HODETNUW-RbwWf2zc.js} +2 -2
  29. package/dist/{formats-DI7HEVv7.js → formats-DbXnijtH.js} +1 -1
  30. package/dist/{glide-data-editor-defMDM3B.js → glide-data-editor-CvRiA49N.js} +5 -5
  31. package/dist/{globals-D9kDDdYk.js → globals-BR4CmGOS.js} +2 -2
  32. package/dist/{html-to-image-BoZthB4P.js → html-to-image-D_4_hQ8U.js} +5 -5
  33. package/dist/{infoDiagram-27XIBGKW-BF0YX70D.js → infoDiagram-27XIBGKW-DlmzZjmI.js} +1 -1
  34. package/dist/{ishikawaDiagram-5VMMS53U-_itsAAn-.js → ishikawaDiagram-5VMMS53U-EN3TYP_q.js} +1 -1
  35. package/dist/{kanban-definition-UXKFOSKX-VNW2Beqh.js → kanban-definition-UXKFOSKX-CK_-Ru7Q.js} +1 -1
  36. package/dist/main.js +22 -22
  37. package/dist/{mermaid-CZ6FrClS.js → mermaid-DU9ZRmkG.js} +34 -34
  38. package/dist/{mindmap-definition-YA3MSWOX-DR4ripvd.js → mindmap-definition-YA3MSWOX-C-hqgUvF.js} +1 -1
  39. package/dist/{number-overlay-editor-CR5KeIcI.js → number-overlay-editor-DiYGbxNa.js} +1 -1
  40. package/dist/{pegDiagram-XKGWAZYB-CFr3XlGl.js → pegDiagram-XKGWAZYB-BiMejYPB.js} +2 -2
  41. package/dist/{pieDiagram-E7YTZNPT-j91XYSLY.js → pieDiagram-E7YTZNPT-B1zCZ1eq.js} +1 -1
  42. package/dist/{process-output-DLS4Mqg_.js → process-output-DxSCHBWp.js} +2 -2
  43. package/dist/{railroadDiagram-O6MQD6OU-Cd47ZV9E.js → railroadDiagram-O6MQD6OU-BLcfaASl.js} +2 -2
  44. package/dist/{requirementDiagram-BXWQKSXE-eJH6gLtX.js → requirementDiagram-BXWQKSXE-DtBOf52-.js} +1 -1
  45. package/dist/{reveal-component-DxZaPN5S.js → reveal-component-D3DsXPIm.js} +6 -6
  46. package/dist/{sequenceDiagram-WJ2MYXX4-DdfFEIrm.js → sequenceDiagram-WJ2MYXX4-DIwF19_n.js} +1 -1
  47. package/dist/{spec-h69nC3SI.js → spec-DZDnnlgT.js} +1 -1
  48. package/dist/{stateDiagram-D77RDMKH-DkjDvip8.js → stateDiagram-D77RDMKH-sWk1AdYL.js} +2 -2
  49. package/dist/{stateDiagram-v2-MP3YSRHH-DBHACnOo.js → stateDiagram-v2-MP3YSRHH-CiNo-OI7.js} +2 -2
  50. package/dist/style.css +1 -1
  51. package/dist/{swimlanes-42K2YHIH-ogImlOmp.js → swimlanes-42K2YHIH-Bj3hiOY5.js} +1 -1
  52. package/dist/{swimlanesDiagram-VR7AAH4N-D6x-zsci.js → swimlanesDiagram-VR7AAH4N-BzEo2r_N.js} +2 -2
  53. package/dist/{timeline-definition-24CTP7MA-DQpLtsHD.js → timeline-definition-24CTP7MA-CrPQk42H.js} +1 -1
  54. package/dist/{toDate-CDzoofJo.js → toDate-Cu2pIZcC.js} +1 -1
  55. package/dist/{useAsyncData-Bn8SxUqM.js → useAsyncData-DEhLdRIu.js} +1 -1
  56. package/dist/{useDeepCompareMemoize-CC2tnPCk.js → useDeepCompareMemoize-RjZGVhBV.js} +1 -1
  57. package/dist/{useLifecycle-BJlxHqWa.js → useLifecycle-DtcB2s1z.js} +1 -1
  58. package/dist/{useTheme-DMfQYmlG.js → useTheme-CoEtr4mK.js} +1 -1
  59. package/dist/{vega-component-CovWHi60.js → vega-component-BhjTrP9-.js} +5 -5
  60. package/dist/{vennDiagram-4TSXK5OY-BD-SL_vq.js → vennDiagram-4TSXK5OY-BaYMoIpj.js} +1 -1
  61. package/dist/{wardleyDiagram-VM6X3IG4-BDgJV4ah.js → wardleyDiagram-VM6X3IG4-DjHeuj8p.js} +1 -1
  62. package/dist/{xychartDiagram-S5SC5T6Z-1hIHN6Rl.js → xychartDiagram-S5SC5T6Z-Bc_hcuXS.js} +1 -1
  63. package/package.json +1 -1
  64. package/src/components/chat/__tests__/notebook-access-selector.test.tsx +64 -0
  65. package/src/components/chat/chat-panel.tsx +9 -91
  66. package/src/components/chat/notebook-access-selector.stories.tsx +22 -0
  67. package/src/components/chat/notebook-access-selector.tsx +100 -0
  68. package/src/core/ai/tools/__tests__/edit-notebook-tool.test.ts +1 -5
  69. package/src/core/ai/tools/__tests__/registry.test.ts +17 -0
  70. package/src/core/ai/tools/base.ts +0 -6
  71. package/src/core/ai/tools/edit-notebook-tool.ts +1 -2
  72. package/src/core/ai/tools/registry.ts +0 -2
  73. package/src/core/config/__tests__/config-schema.test.ts +7 -2
  74. package/src/core/config/config-schema.ts +4 -1
  75. package/dist/assets/__vite-browser-external-DVeFf2e7.js +0 -1
  76. package/src/core/ai/tools/__tests__/run-cells-tool.test.ts +0 -656
  77. package/src/core/ai/tools/run-cells-tool.ts +0 -303
@@ -170,7 +170,10 @@ export const UserConfigSchema = z
170
170
  enabled: z.boolean().prefault(true),
171
171
  rules: z.string().prefault(""),
172
172
  max_tokens: z.number().int().positive().nullable().optional(),
173
- mode: z.enum(COPILOT_MODES).prefault("manual"),
173
+ mode: z
174
+ .enum(COPILOT_MODES)
175
+ .transform((mode) => (mode === "manual" ? "ask" : mode))
176
+ .prefault("ask"),
174
177
  inline_tooltip: z.boolean().prefault(false),
175
178
  allow_provider_config: z.boolean().prefault(true),
176
179
  open_ai: AiConfigSchema.optional(),
@@ -1 +0,0 @@
1
- import{t as e}from"./worker-C1pfNBsx.js";var t=e(((e,t)=>{t.exports={}}));export default t();
@@ -1,656 +0,0 @@
1
- /* Copyright 2026 Marimo. All rights reserved. */
2
-
3
- import { getDefaultStore } from "jotai";
4
- import { beforeEach, describe, expect, it, vi } from "vitest";
5
- import { MockNotebook } from "@/__mocks__/notebook";
6
- import { cellId } from "@/__tests__/branded";
7
- import { notebookAtom } from "@/core/cells/cells";
8
- import type { CellId } from "@/core/cells/ids";
9
- import { RunStaleCellsTool } from "../run-cells-tool";
10
-
11
- // Mock runCells
12
- vi.mock("@/components/editor/cell/useRunCells", () => ({
13
- runCells: vi.fn(),
14
- }));
15
-
16
- // Mock getCellContextData
17
- vi.mock("../../context/providers/cell-output", () => ({
18
- getCellContextData: vi.fn(),
19
- }));
20
-
21
- import { runCells } from "@/components/editor/cell/useRunCells";
22
- import { getCellContextData } from "../../context/providers/cell-output";
23
-
24
- describe("RunStaleCellsTool", () => {
25
- let store: ReturnType<typeof getDefaultStore>;
26
- let tool: RunStaleCellsTool;
27
- let cellId1: CellId;
28
- let cellId2: CellId;
29
- let cellId3: CellId;
30
- let toolContext: {
31
- addStagedCell: ReturnType<typeof vi.fn>;
32
- createCell: ReturnType<typeof vi.fn>;
33
- prepareForRun: ReturnType<typeof vi.fn>;
34
- sendRun: ReturnType<typeof vi.fn>;
35
- store: ReturnType<typeof getDefaultStore>;
36
- };
37
-
38
- beforeEach(() => {
39
- store = getDefaultStore();
40
- toolContext = {
41
- addStagedCell: vi.fn(),
42
- createCell: vi.fn(),
43
- prepareForRun: vi.fn(),
44
- sendRun: vi.fn().mockResolvedValue(null),
45
- store,
46
- };
47
-
48
- tool = new RunStaleCellsTool({ postExecutionDelay: 0 });
49
-
50
- cellId1 = cellId("cell-1");
51
- cellId2 = cellId("cell-2");
52
- cellId3 = cellId("cell-3");
53
-
54
- // Reset mocks
55
- vi.clearAllMocks();
56
- });
57
-
58
- describe("no stale cells", () => {
59
- it("should return success message when no stale cells exist", async () => {
60
- const notebook = MockNotebook.notebookState({
61
- cellData: {
62
- [cellId1]: {
63
- code: "x = 1",
64
- edited: false,
65
- lastCodeRun: "x = 1",
66
- lastExecutionTime: 100,
67
- },
68
- },
69
- });
70
- // Make sure cell is not stale by setting runtime state
71
- notebook.cellRuntime[cellId1] = {
72
- ...notebook.cellRuntime[cellId1],
73
- runElapsedTimeMs: 100 as never,
74
- status: "idle",
75
- };
76
- store.set(notebookAtom, notebook);
77
-
78
- const result = await tool.handler({}, toolContext as never);
79
-
80
- expect(result.status).toBe("success");
81
- expect(result.message).toBe("No stale cells found.");
82
- expect(result.cellsToOutput).toBeUndefined();
83
- expect(vi.mocked(runCells)).not.toHaveBeenCalled();
84
- });
85
- });
86
-
87
- describe("running stale cells", () => {
88
- it("should run stale cells and return their outputs", async () => {
89
- const notebook = MockNotebook.notebookState({
90
- cellData: {
91
- [cellId1]: {
92
- code: "x = 1",
93
- edited: true,
94
- lastCodeRun: null,
95
- },
96
- [cellId2]: {
97
- code: "y = 2",
98
- edited: false,
99
- lastCodeRun: "y = 2",
100
- lastExecutionTime: 100,
101
- },
102
- [cellId3]: {
103
- code: "z = 3",
104
- edited: true,
105
- lastCodeRun: "z = 2",
106
- },
107
- },
108
- });
109
- // Make cellId2 not stale
110
- notebook.cellRuntime[cellId2] = {
111
- ...notebook.cellRuntime[cellId2],
112
- runElapsedTimeMs: 100 as never,
113
- status: "idle",
114
- };
115
- store.set(notebookAtom, notebook);
116
-
117
- // Mock runCells to update cell runtime status
118
- vi.mocked(runCells).mockImplementation(async () => {
119
- const updatedNotebook = store.get(notebookAtom);
120
- updatedNotebook.cellRuntime[cellId1] = {
121
- ...updatedNotebook.cellRuntime[cellId1],
122
- status: "idle",
123
- };
124
- updatedNotebook.cellRuntime[cellId3] = {
125
- ...updatedNotebook.cellRuntime[cellId3],
126
- status: "idle",
127
- };
128
- store.set(notebookAtom, updatedNotebook);
129
- });
130
-
131
- // Mock getCellContextData to return outputs
132
- vi.mocked(getCellContextData).mockImplementation((cellId) => {
133
- if (cellId === cellId1) {
134
- return {
135
- cellOutput: {
136
- outputType: "text",
137
- processedContent: "1",
138
- imageUrl: null,
139
- output: {},
140
- },
141
- consoleOutputs: null,
142
- cellName: "cell1",
143
- } as never;
144
- }
145
- if (cellId === cellId3) {
146
- return {
147
- cellOutput: {
148
- outputType: "text",
149
- processedContent: "3",
150
- imageUrl: null,
151
- output: {},
152
- },
153
- consoleOutputs: null,
154
- cellName: "cell3",
155
- } as never;
156
- }
157
- return {} as never;
158
- });
159
-
160
- const result = await tool.handler({}, toolContext as never);
161
-
162
- expect(result.status).toBe("success");
163
- expect(result.cellsToOutput).toBeDefined();
164
- expect(result.cellsToOutput).toHaveProperty(cellId1);
165
- expect(result.cellsToOutput).toHaveProperty(cellId3);
166
- expect(result.cellsToOutput).not.toHaveProperty(cellId2);
167
-
168
- // Verify runCells was called with correct parameters
169
- expect(vi.mocked(runCells)).toHaveBeenCalledWith({
170
- cellIds: [cellId1, cellId3],
171
- sendRun: toolContext.sendRun,
172
- prepareForRun: toolContext.prepareForRun,
173
- notebook,
174
- });
175
- });
176
-
177
- it("should handle cells with no output", async () => {
178
- const notebook = MockNotebook.notebookState({
179
- cellData: {
180
- [cellId1]: { code: "x = 1", edited: true },
181
- },
182
- });
183
- store.set(notebookAtom, notebook);
184
-
185
- // Mock runCells
186
- vi.mocked(runCells).mockImplementation(async () => {
187
- const updatedNotebook = store.get(notebookAtom);
188
- updatedNotebook.cellRuntime[cellId1] = {
189
- ...updatedNotebook.cellRuntime[cellId1],
190
- status: "idle",
191
- };
192
- store.set(notebookAtom, updatedNotebook);
193
- });
194
-
195
- // Mock getCellContextData to return no outputs
196
- vi.mocked(getCellContextData).mockReturnValue({
197
- cellOutput: null,
198
- consoleOutputs: null,
199
- cellName: "cell1",
200
- } as never);
201
-
202
- const result = await tool.handler({}, toolContext as never);
203
-
204
- expect(result.status).toBe("success");
205
- expect(result.cellsToOutput).toBeDefined();
206
- expect(result.cellsToOutput?.[cellId1]).toBeNull();
207
- });
208
-
209
- it("should handle cells with console output", async () => {
210
- const notebook = MockNotebook.notebookState({
211
- cellData: {
212
- [cellId1]: { code: 'print("hello")', edited: true },
213
- },
214
- });
215
- store.set(notebookAtom, notebook);
216
-
217
- // Mock runCells
218
- vi.mocked(runCells).mockImplementation(async () => {
219
- const updatedNotebook = store.get(notebookAtom);
220
- updatedNotebook.cellRuntime[cellId1] = {
221
- ...updatedNotebook.cellRuntime[cellId1],
222
- status: "idle",
223
- };
224
- store.set(notebookAtom, updatedNotebook);
225
- });
226
-
227
- // Mock getCellContextData to return console output
228
- vi.mocked(getCellContextData).mockReturnValue({
229
- cellOutput: null,
230
- consoleOutputs: [
231
- {
232
- outputType: "text",
233
- processedContent: "hello",
234
- imageUrl: null,
235
- output: {},
236
- },
237
- ],
238
- cellName: "cell1",
239
- } as never);
240
-
241
- const result = await tool.handler({}, toolContext as never);
242
-
243
- expect(result.status).toBe("success");
244
- expect(result.cellsToOutput).toBeDefined();
245
- expect(result.cellsToOutput?.[cellId1]?.consoleOutput).toContain("hello");
246
- expect(result.message).toContain("Console output");
247
- });
248
-
249
- it("should handle cells with media output", async () => {
250
- const notebook = MockNotebook.notebookState({
251
- cellData: {
252
- [cellId1]: { code: "plt.plot()", edited: true },
253
- },
254
- });
255
- store.set(notebookAtom, notebook);
256
-
257
- // Mock runCells
258
- vi.mocked(runCells).mockImplementation(async () => {
259
- const updatedNotebook = store.get(notebookAtom);
260
- updatedNotebook.cellRuntime[cellId1] = {
261
- ...updatedNotebook.cellRuntime[cellId1],
262
- status: "idle",
263
- };
264
- store.set(notebookAtom, updatedNotebook);
265
- });
266
-
267
- // Mock getCellContextData to return media output
268
- vi.mocked(getCellContextData).mockReturnValue({
269
- cellOutput: {
270
- outputType: "media",
271
- processedContent: null,
272
- imageUrl: "https://example.com/image.png",
273
- output: { mimetype: "image/png" },
274
- },
275
- consoleOutputs: null,
276
- cellName: "cell1",
277
- } as never);
278
-
279
- const result = await tool.handler({}, toolContext as never);
280
-
281
- expect(result.status).toBe("success");
282
- expect(result.cellsToOutput).toBeDefined();
283
- expect(result.cellsToOutput?.[cellId1]?.cellOutput).toContain(
284
- "image/png",
285
- );
286
- expect(result.cellsToOutput?.[cellId1]?.cellOutput).toContain(
287
- "https://example.com/image.png",
288
- );
289
- });
290
-
291
- it("should handle both cell output and console output", async () => {
292
- const notebook = MockNotebook.notebookState({
293
- cellData: {
294
- [cellId1]: { code: 'x = 1; print("debug")', edited: true },
295
- },
296
- });
297
- store.set(notebookAtom, notebook);
298
-
299
- // Mock runCells
300
- vi.mocked(runCells).mockImplementation(async () => {
301
- const updatedNotebook = store.get(notebookAtom);
302
- updatedNotebook.cellRuntime[cellId1] = {
303
- ...updatedNotebook.cellRuntime[cellId1],
304
- status: "idle",
305
- };
306
- store.set(notebookAtom, updatedNotebook);
307
- });
308
-
309
- // Mock getCellContextData to return both outputs
310
- vi.mocked(getCellContextData).mockReturnValue({
311
- cellOutput: {
312
- outputType: "text",
313
- processedContent: "1",
314
- imageUrl: null,
315
- output: {},
316
- },
317
- consoleOutputs: [
318
- {
319
- outputType: "text",
320
- processedContent: "debug",
321
- imageUrl: null,
322
- output: {},
323
- },
324
- ],
325
- cellName: "cell1",
326
- } as never);
327
-
328
- const result = await tool.handler({}, toolContext as never);
329
-
330
- expect(result.status).toBe("success");
331
- expect(result.cellsToOutput).toBeDefined();
332
- expect(result.cellsToOutput?.[cellId1]?.cellOutput).toContain("1");
333
- expect(result.cellsToOutput?.[cellId1]?.consoleOutput).toContain("debug");
334
- });
335
-
336
- it("should handle cell output with object output", async () => {
337
- const notebook = MockNotebook.notebookState({
338
- cellData: {
339
- [cellId1]: { code: "x = {'a': 1, 'b': 2}", edited: true },
340
- },
341
- });
342
- store.set(notebookAtom, notebook);
343
-
344
- // Mock runCells
345
- vi.mocked(runCells).mockImplementation(async () => {
346
- const updatedNotebook = store.get(notebookAtom);
347
- updatedNotebook.cellRuntime[cellId1] = {
348
- ...updatedNotebook.cellRuntime[cellId1],
349
- status: "idle",
350
- };
351
- store.set(notebookAtom, updatedNotebook);
352
- });
353
-
354
- // Mock getCellContextData to return object output
355
- vi.mocked(getCellContextData).mockReturnValue({
356
- cellOutput: {
357
- outputType: "text",
358
- processedContent: null,
359
- imageUrl: null,
360
- output: { data: JSON.stringify({ a: 1, b: 2 }) },
361
- },
362
- consoleOutputs: null,
363
- cellName: "cell1",
364
- } as never);
365
-
366
- const result = await tool.handler({}, toolContext as never);
367
-
368
- expect(result.status).toBe("success");
369
- expect(result.cellsToOutput).toBeDefined();
370
- expect(result.cellsToOutput?.[cellId1]?.cellOutput).toEqual(
371
- 'Output:\n{"a":1,"b":2}',
372
- );
373
- });
374
-
375
- it("should return success when all stale cells have no output", async () => {
376
- const notebook = MockNotebook.notebookState({
377
- cellData: {
378
- [cellId1]: { code: "x = 1", edited: true },
379
- [cellId2]: { code: "y = 2", edited: true },
380
- },
381
- });
382
- store.set(notebookAtom, notebook);
383
-
384
- // Mock runCells
385
- vi.mocked(runCells).mockImplementation(async () => {
386
- const updatedNotebook = store.get(notebookAtom);
387
- updatedNotebook.cellRuntime[cellId1] = {
388
- ...updatedNotebook.cellRuntime[cellId1],
389
- status: "idle",
390
- };
391
- updatedNotebook.cellRuntime[cellId2] = {
392
- ...updatedNotebook.cellRuntime[cellId2],
393
- status: "idle",
394
- };
395
- store.set(notebookAtom, updatedNotebook);
396
- });
397
-
398
- // Mock getCellContextData to return no outputs for both cells
399
- vi.mocked(getCellContextData).mockReturnValue({
400
- cellOutput: null,
401
- consoleOutputs: null,
402
- cellName: "cell1",
403
- } as never);
404
-
405
- const result = await tool.handler({}, toolContext as never);
406
-
407
- expect(result.status).toBe("success");
408
- expect(result.cellsToOutput).toBeDefined();
409
- // Both cells should be in the output as null
410
- expect(result.cellsToOutput?.[cellId1]).toBeNull();
411
- expect(result.cellsToOutput?.[cellId2]).toBeNull();
412
- });
413
- });
414
-
415
- describe("output truncation", () => {
416
- it("should summarize text/html output instead of dumping raw content", async () => {
417
- const notebook = MockNotebook.notebookState({
418
- cellData: {
419
- [cellId1]: { code: "fig.show()", edited: true },
420
- },
421
- });
422
- store.set(notebookAtom, notebook);
423
-
424
- vi.mocked(runCells).mockImplementation(async () => {
425
- const updatedNotebook = store.get(notebookAtom);
426
- updatedNotebook.cellRuntime[cellId1] = {
427
- ...updatedNotebook.cellRuntime[cellId1],
428
- status: "idle",
429
- };
430
- store.set(notebookAtom, updatedNotebook);
431
- });
432
-
433
- const largeHtml = `<div>${"x".repeat(2_000_000)}</div>`;
434
- vi.mocked(getCellContextData).mockReturnValue({
435
- cellOutput: {
436
- outputType: "text",
437
- processedContent: null,
438
- imageUrl: null,
439
- output: { mimetype: "text/html", data: largeHtml },
440
- },
441
- consoleOutputs: null,
442
- cellName: "cell1",
443
- } as never);
444
-
445
- const result = await tool.handler({}, toolContext as never);
446
-
447
- expect(result.status).toBe("success");
448
- const output = result.cellsToOutput?.[cellId1]?.cellOutput ?? "";
449
- expect(output).toContain("HTML Output:");
450
- expect(output).toContain("text/html");
451
- expect(output.length).toBeLessThan(200);
452
- expect(output).not.toContain(largeHtml);
453
- });
454
-
455
- it("should truncate large text output to MAX_TEXT_OUTPUT_CHARS", async () => {
456
- const notebook = MockNotebook.notebookState({
457
- cellData: {
458
- [cellId1]: { code: "print(big_string)", edited: true },
459
- },
460
- });
461
- store.set(notebookAtom, notebook);
462
-
463
- vi.mocked(runCells).mockImplementation(async () => {
464
- const updatedNotebook = store.get(notebookAtom);
465
- updatedNotebook.cellRuntime[cellId1] = {
466
- ...updatedNotebook.cellRuntime[cellId1],
467
- status: "idle",
468
- };
469
- store.set(notebookAtom, updatedNotebook);
470
- });
471
-
472
- const largeText = "a".repeat(10_000);
473
- vi.mocked(getCellContextData).mockReturnValue({
474
- cellOutput: {
475
- outputType: "text",
476
- processedContent: largeText,
477
- imageUrl: null,
478
- output: { mimetype: "text/plain", data: largeText },
479
- },
480
- consoleOutputs: null,
481
- cellName: "cell1",
482
- } as never);
483
-
484
- const result = await tool.handler({}, toolContext as never);
485
-
486
- const output = result.cellsToOutput?.[cellId1]?.cellOutput ?? "";
487
- expect(output).toContain("[TRUNCATED:");
488
- expect(output).toContain("Full output visible in the notebook UI.");
489
- // Output should be capped (2000 chars content + "Output:\n" prefix + truncation message)
490
- expect(output.length).toBeLessThan(2200);
491
- });
492
-
493
- it("should omit output for cells that exceed total output budget", async () => {
494
- const cellIds = Array.from({ length: 25 }, (_, i) =>
495
- cellId(`budget-cell-${i}`),
496
- );
497
- const cellData: Record<string, { code: string; edited: boolean }> = {};
498
- for (const id of cellIds) {
499
- cellData[id] = { code: "x = 1", edited: true };
500
- }
501
-
502
- const notebook = MockNotebook.notebookState({ cellData });
503
- store.set(notebookAtom, notebook);
504
-
505
- vi.mocked(runCells).mockImplementation(async () => {
506
- const updatedNotebook = store.get(notebookAtom);
507
- for (const id of cellIds) {
508
- updatedNotebook.cellRuntime[id] = {
509
- ...updatedNotebook.cellRuntime[id],
510
- status: "idle",
511
- };
512
- }
513
- store.set(notebookAtom, updatedNotebook);
514
- });
515
-
516
- // Each cell produces ~2008 chars of formatted output ("Output:\n" + 2000 chars).
517
- // After 20 cells the running total exceeds MAX_TOOL_OUTPUT_CHARS (40,000).
518
- const content = "a".repeat(2000);
519
- vi.mocked(getCellContextData).mockReturnValue({
520
- cellOutput: {
521
- outputType: "text",
522
- processedContent: content,
523
- imageUrl: null,
524
- output: { mimetype: "text/plain", data: content },
525
- },
526
- consoleOutputs: null,
527
- cellName: "cell",
528
- } as never);
529
-
530
- const result = await tool.handler({}, toolContext as never);
531
-
532
- expect(result.cellsToOutput?.[cellIds[0]]?.cellOutput).toContain(
533
- "Output:",
534
- );
535
- expect(result.cellsToOutput?.[cellIds[24]]?.cellOutput).toBe(
536
- "Cell executed (output omitted due to context limits).",
537
- );
538
- });
539
-
540
- it("should use higher truncation limit for error outputs", async () => {
541
- const notebook = MockNotebook.notebookState({
542
- cellData: {
543
- [cellId1]: { code: "raise Exception()", edited: true },
544
- },
545
- });
546
- store.set(notebookAtom, notebook);
547
-
548
- vi.mocked(runCells).mockImplementation(async () => {
549
- const updatedNotebook = store.get(notebookAtom);
550
- updatedNotebook.cellRuntime[cellId1] = {
551
- ...updatedNotebook.cellRuntime[cellId1],
552
- status: "idle",
553
- };
554
- store.set(notebookAtom, updatedNotebook);
555
- });
556
-
557
- // 2500 chars sits between MAX_TEXT_OUTPUT_CHARS (2000) and MAX_ERROR_OUTPUT_CHARS (3000)
558
- const errorContent = "E".repeat(2500);
559
- vi.mocked(getCellContextData).mockReturnValue({
560
- cellOutput: {
561
- outputType: "text",
562
- processedContent: errorContent,
563
- imageUrl: null,
564
- output: {
565
- mimetype: "application/vnd.marimo+error",
566
- data: errorContent,
567
- },
568
- },
569
- consoleOutputs: null,
570
- cellName: "cell1",
571
- } as never);
572
-
573
- const result = await tool.handler({}, toolContext as never);
574
-
575
- const output = result.cellsToOutput?.[cellId1]?.cellOutput ?? "";
576
- expect(output).not.toContain("[TRUNCATED:");
577
- expect(output).toContain(errorContent);
578
- });
579
-
580
- it("should truncate large console output", async () => {
581
- const notebook = MockNotebook.notebookState({
582
- cellData: {
583
- [cellId1]: { code: 'print("x" * 10000)', edited: true },
584
- },
585
- });
586
- store.set(notebookAtom, notebook);
587
-
588
- vi.mocked(runCells).mockImplementation(async () => {
589
- const updatedNotebook = store.get(notebookAtom);
590
- updatedNotebook.cellRuntime[cellId1] = {
591
- ...updatedNotebook.cellRuntime[cellId1],
592
- status: "idle",
593
- };
594
- store.set(notebookAtom, updatedNotebook);
595
- });
596
-
597
- const largeConsoleText = "x".repeat(10_000);
598
- vi.mocked(getCellContextData).mockReturnValue({
599
- cellOutput: null,
600
- consoleOutputs: [
601
- {
602
- outputType: "text",
603
- processedContent: largeConsoleText,
604
- imageUrl: null,
605
- output: { mimetype: "text/plain", data: largeConsoleText },
606
- },
607
- ],
608
- cellName: "cell1",
609
- } as never);
610
-
611
- const result = await tool.handler({}, toolContext as never);
612
-
613
- const consoleOutput =
614
- result.cellsToOutput?.[cellId1]?.consoleOutput ?? "";
615
- expect(consoleOutput).toContain("[TRUNCATED:");
616
- expect(consoleOutput.length).toBeLessThan(2200);
617
- });
618
- });
619
-
620
- describe("cell execution completion", () => {
621
- it("should complete immediately if cells are already idle", async () => {
622
- const notebook = MockNotebook.notebookState({
623
- cellData: {
624
- [cellId1]: { code: "x = 1", edited: true },
625
- },
626
- });
627
-
628
- // Set cell runtime to already be idle
629
- notebook.cellRuntime[cellId1] = {
630
- ...notebook.cellRuntime[cellId1],
631
- status: "idle",
632
- };
633
- store.set(notebookAtom, notebook);
634
-
635
- // Mock runCells to do nothing (cells already idle)
636
- vi.mocked(runCells).mockResolvedValue();
637
-
638
- // Mock getCellContextData
639
- vi.mocked(getCellContextData).mockReturnValue({
640
- cellOutput: {
641
- outputType: "text",
642
- processedContent: "1",
643
- imageUrl: null,
644
- output: {},
645
- },
646
- consoleOutputs: null,
647
- cellName: "cell1",
648
- } as never);
649
-
650
- const result = await tool.handler({}, toolContext as never);
651
-
652
- expect(result.status).toBe("success");
653
- expect(result.cellsToOutput).toBeDefined();
654
- });
655
- });
656
- });