@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.
- package/dist/{ConnectedDataExplorerComponent-Le_OBfmt.js → ConnectedDataExplorerComponent-BBbOElk3.js} +4 -4
- package/dist/{ErrorBoundary-CWtCjh3m.js → ErrorBoundary--X0z83Yx.js} +2 -2
- package/dist/{abnfDiagram-VCTEODGH-Tf1ghyQQ.js → abnfDiagram-VCTEODGH-DhMT6Uhd.js} +2 -2
- package/dist/{architectureDiagram-5GKGNRK7-xlnbTG0T.js → architectureDiagram-5GKGNRK7-BVbEiJLq.js} +1 -1
- package/dist/assets/__vite-browser-external-C8gQho07.js +1 -0
- package/dist/assets/{worker-C1pfNBsx.js → worker-C6e9ERyd.js} +2 -2
- package/dist/{chat-ui-X-cWrL9u.js → chat-ui-BguxTQEL.js} +7 -7
- package/dist/{chunk-IMKFNOWR-Cmd4a5jy.js → chunk-IMKFNOWR-By5hSbJJ.js} +1 -1
- package/dist/{chunk-SHT3W25Y-mSNp-WpO.js → chunk-SHT3W25Y-DFrEYG54.js} +1 -1
- package/dist/{chunk-SVP7TREG-CWC49D8X.js → chunk-SVP7TREG-DFXGc9ew.js} +1 -1
- package/dist/{chunk-TICWLB2K-De9FINtG.js → chunk-TICWLB2K-DZdNIpoZ.js} +1 -1
- package/dist/{classDiagram-ZZMXUADV-DweTkYdp.js → classDiagram-ZZMXUADV-Cgj_eEQA.js} +2 -2
- package/dist/{classDiagram-v2-VYDZK3BY-A6MR2TiE.js → classDiagram-v2-VYDZK3BY-DLAUrSqj.js} +2 -2
- package/dist/{code-visibility-BZDPS5dP.js → code-visibility-uDyLo9_i.js} +4 -4
- package/dist/{common-rusLIanu.js → common-DWKY45Md.js} +9 -9
- package/dist/{cynefinDiagram-5FMLGOSQ-BSvo5le-.js → cynefinDiagram-5FMLGOSQ-Cikf9AKa.js} +1 -1
- package/dist/{dagre-GXQ25YYZ-tXRQslO6.js → dagre-GXQ25YYZ-C5KEi5_l.js} +1 -1
- package/dist/{data-grid-overlay-editor-Dc_HCUPe.js → data-grid-overlay-editor-CRnYWD1L.js} +1 -1
- package/dist/{diagram-S7CK7UJ4-r3t5VtwI.js → diagram-S7CK7UJ4-BaoBTQjR.js} +1 -1
- package/dist/{diagram-UQ7AKVKN-qgbC8YnT.js → diagram-UQ7AKVKN-DDufBsUA.js} +1 -1
- package/dist/{diagram-VX7I27RA-CiHxEwtp.js → diagram-VX7I27RA-CY6RALJ8.js} +1 -1
- package/dist/{diagram-Z3DM3KII-D95u15Ev.js → diagram-Z3DM3KII-Bc-LHsD6.js} +1 -1
- package/dist/{dropdown-menu-DCMbbQ35.js → dropdown-menu-drDjxJyV.js} +1 -1
- package/dist/{ebnfDiagram-PWID7BFC-P7e4bryK.js → ebnfDiagram-PWID7BFC-Cz0J-tcC.js} +2 -2
- package/dist/{erDiagram-RLTQ6QDP-CUHZcmRj.js → erDiagram-RLTQ6QDP-DTXTDgFu.js} +1 -1
- package/dist/{error-entries-mpoD5t79.js → error-entries-CZ1NQ6Kv.js} +4 -4
- package/dist/{feedback-button-C3gqiY5b.js → feedback-button-CDf1yRm_.js} +5 -5
- package/dist/{flowDiagram-HODETNUW-BGZ95AzI.js → flowDiagram-HODETNUW-RbwWf2zc.js} +2 -2
- package/dist/{formats-DI7HEVv7.js → formats-DbXnijtH.js} +1 -1
- package/dist/{glide-data-editor-defMDM3B.js → glide-data-editor-CvRiA49N.js} +5 -5
- package/dist/{globals-D9kDDdYk.js → globals-BR4CmGOS.js} +2 -2
- package/dist/{html-to-image-BoZthB4P.js → html-to-image-D_4_hQ8U.js} +5 -5
- package/dist/{infoDiagram-27XIBGKW-BF0YX70D.js → infoDiagram-27XIBGKW-DlmzZjmI.js} +1 -1
- package/dist/{ishikawaDiagram-5VMMS53U-_itsAAn-.js → ishikawaDiagram-5VMMS53U-EN3TYP_q.js} +1 -1
- package/dist/{kanban-definition-UXKFOSKX-VNW2Beqh.js → kanban-definition-UXKFOSKX-CK_-Ru7Q.js} +1 -1
- package/dist/main.js +22 -22
- package/dist/{mermaid-CZ6FrClS.js → mermaid-DU9ZRmkG.js} +34 -34
- package/dist/{mindmap-definition-YA3MSWOX-DR4ripvd.js → mindmap-definition-YA3MSWOX-C-hqgUvF.js} +1 -1
- package/dist/{number-overlay-editor-CR5KeIcI.js → number-overlay-editor-DiYGbxNa.js} +1 -1
- package/dist/{pegDiagram-XKGWAZYB-CFr3XlGl.js → pegDiagram-XKGWAZYB-BiMejYPB.js} +2 -2
- package/dist/{pieDiagram-E7YTZNPT-j91XYSLY.js → pieDiagram-E7YTZNPT-B1zCZ1eq.js} +1 -1
- package/dist/{process-output-DLS4Mqg_.js → process-output-DxSCHBWp.js} +2 -2
- package/dist/{railroadDiagram-O6MQD6OU-Cd47ZV9E.js → railroadDiagram-O6MQD6OU-BLcfaASl.js} +2 -2
- package/dist/{requirementDiagram-BXWQKSXE-eJH6gLtX.js → requirementDiagram-BXWQKSXE-DtBOf52-.js} +1 -1
- package/dist/{reveal-component-DxZaPN5S.js → reveal-component-D3DsXPIm.js} +6 -6
- package/dist/{sequenceDiagram-WJ2MYXX4-DdfFEIrm.js → sequenceDiagram-WJ2MYXX4-DIwF19_n.js} +1 -1
- package/dist/{spec-h69nC3SI.js → spec-DZDnnlgT.js} +1 -1
- package/dist/{stateDiagram-D77RDMKH-DkjDvip8.js → stateDiagram-D77RDMKH-sWk1AdYL.js} +2 -2
- package/dist/{stateDiagram-v2-MP3YSRHH-DBHACnOo.js → stateDiagram-v2-MP3YSRHH-CiNo-OI7.js} +2 -2
- package/dist/style.css +1 -1
- package/dist/{swimlanes-42K2YHIH-ogImlOmp.js → swimlanes-42K2YHIH-Bj3hiOY5.js} +1 -1
- package/dist/{swimlanesDiagram-VR7AAH4N-D6x-zsci.js → swimlanesDiagram-VR7AAH4N-BzEo2r_N.js} +2 -2
- package/dist/{timeline-definition-24CTP7MA-DQpLtsHD.js → timeline-definition-24CTP7MA-CrPQk42H.js} +1 -1
- package/dist/{toDate-CDzoofJo.js → toDate-Cu2pIZcC.js} +1 -1
- package/dist/{useAsyncData-Bn8SxUqM.js → useAsyncData-DEhLdRIu.js} +1 -1
- package/dist/{useDeepCompareMemoize-CC2tnPCk.js → useDeepCompareMemoize-RjZGVhBV.js} +1 -1
- package/dist/{useLifecycle-BJlxHqWa.js → useLifecycle-DtcB2s1z.js} +1 -1
- package/dist/{useTheme-DMfQYmlG.js → useTheme-CoEtr4mK.js} +1 -1
- package/dist/{vega-component-CovWHi60.js → vega-component-BhjTrP9-.js} +5 -5
- package/dist/{vennDiagram-4TSXK5OY-BD-SL_vq.js → vennDiagram-4TSXK5OY-BaYMoIpj.js} +1 -1
- package/dist/{wardleyDiagram-VM6X3IG4-BDgJV4ah.js → wardleyDiagram-VM6X3IG4-DjHeuj8p.js} +1 -1
- package/dist/{xychartDiagram-S5SC5T6Z-1hIHN6Rl.js → xychartDiagram-S5SC5T6Z-Bc_hcuXS.js} +1 -1
- package/package.json +1 -1
- package/src/components/chat/__tests__/notebook-access-selector.test.tsx +64 -0
- package/src/components/chat/chat-panel.tsx +9 -91
- package/src/components/chat/notebook-access-selector.stories.tsx +22 -0
- package/src/components/chat/notebook-access-selector.tsx +100 -0
- package/src/core/ai/tools/__tests__/edit-notebook-tool.test.ts +1 -5
- package/src/core/ai/tools/__tests__/registry.test.ts +17 -0
- package/src/core/ai/tools/base.ts +0 -6
- package/src/core/ai/tools/edit-notebook-tool.ts +1 -2
- package/src/core/ai/tools/registry.ts +0 -2
- package/src/core/config/__tests__/config-schema.test.ts +7 -2
- package/src/core/config/config-schema.ts +4 -1
- package/dist/assets/__vite-browser-external-DVeFf2e7.js +0 -1
- package/src/core/ai/tools/__tests__/run-cells-tool.test.ts +0 -656
- 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
|
|
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
|
-
});
|