@marimo-team/islands 0.24.3-dev22 → 0.24.3-dev24
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/{common-DIyUaEO6.js → common-BzvhwpFa.js} +1 -1
- package/dist/main.js +2 -2
- package/dist/{reveal-component-GEGc56E6.js → reveal-component-D5xyD8xS.js} +1 -1
- package/package.json +1 -1
- package/src/components/chat/acp/__tests__/agent-panel.test.tsx +270 -0
- package/src/components/chat/acp/agent-panel.tsx +45 -44
|
@@ -32442,7 +32442,7 @@ function Iq() {
|
|
|
32442
32442
|
return a.warn("Failed to get version from mount config"), null;
|
|
32443
32443
|
}
|
|
32444
32444
|
}
|
|
32445
|
-
var Lq = Iq() || "0.24.3-
|
|
32445
|
+
var Lq = Iq() || "0.24.3-dev24", Rq = V(Lq), zq = V(!0);
|
|
32446
32446
|
V(null);
|
|
32447
32447
|
//#endregion
|
|
32448
32448
|
//#region src/core/wasm/state.ts
|
package/dist/main.js
CHANGED
|
@@ -6,7 +6,7 @@ import { n as x, r as S, t as C } from "./copy-C0N4m1lm.js";
|
|
|
6
6
|
import { $n as w, An as T, B as E, Bn as D, Bt as O, C as k, Ct as A, Dn as j, Dt as M, En as N, F as P, Fn as F, G as I, I as L, It as R, J as ee, Jt as z, K as te, Kn as B, L as ne, Lt as re, Mt as ie, Nn as ae, Nt as oe, On as se, Ot as ce, P as le, Pn as ue, Pt as de, Q as fe, Qn as pe, Qt as me, R as he, Rn as ge, Sn as _e, St as ve, U as ye, Un as be, Vn as xe, Vt as Se, W as Ce, Wn as we, Wt as Te, X as Ee, Xn as De, Y as Oe, Yn as ke, Yt as Ae, Z as je, Zn as Me, Zt as Ne, _ as Pe, _n as Fe, a as Ie, an as Le, ar as Re, at as ze, b as Be, bn as Ve, bt as He, cn as Ue, ct as We, dn as Ge, dt as Ke, et as qe, fn as Je, ft as Ye, g as Xe, i as Ze, ir as Qe, j as $e, jt as et, k as tt, kn as nt, kt as rt, ln as it, n as at, nr as ot, o as st, on as ct, ot as lt, q as ut, qt as dt, sn as ft, st as pt, tn as mt, tr as ht, un as gt, ut as _t, v as vt, wn as yt, wt as bt, xn as xt, xt as St, y as Ct, z as wt, zn as Tt } from "./html-to-image-CSU-_5BI.js";
|
|
7
7
|
import { o as Et, s as Dt, u as Ot } from "./dist-6ChWbo8z.js";
|
|
8
8
|
import { D as kt, E as At, O as jt, S as Mt, T as Nt, _ as Pt, a as Ft, c as It, d as Lt, f as Rt, g as zt, h as Bt, i as Vt, l as Ht, m as Ut, n as Wt, o as Gt, p as Kt, r as qt, s as Jt, t as Yt, u as Xt, v as Zt, w as Qt, x as $t, y as en } from "./spec-g740fOWu.js";
|
|
9
|
-
import { $ as tn, $t as nn, A as rn, At as an, B as on, Bt as sn, C as cn, Cn as ln, Ct as un, D as dn, Dn as fn, Dt as pn, E as mn, En as hn, Et as gn, F as _n, Ft as vn, G as yn, Gt as bn, H as xn, Ht as Sn, I as Cn, It as wn, J as Tn, Jt as En, K as Dn, Kt as On, L as kn, Lt as An, M as jn, N as Mn, Nt as Nn, O as Pn, Ot as Fn, P as In, Pt as Ln, Q as Rn, Qt as zn, R as Bn, Rt as Vn, S as Hn, Sn as Un, St as Wn, T as Gn, Tn as Kn, Tt as qn, U as Jn, Ut as Yn, V as Xn, Vt as Zn, W as Qn, Wt as $n, X as er, Xt as tr, Y as nr, Yt as rr, Z as ir, Zt as ar, _ as or, _n as sr, _t as cr, a as lr, an as ur, at as dr, b as fr, bn as pr, bt as mr, cn as hr, dn as gr, dt as _r, en as vr, et as yr, fn as br, ft as xr, g as Sr, gn as Cr, gt as wr, hn as Tr, ht as Er, i as Dr, in as Or, it as kr, j as Ar, k as jr, kt as Mr, l as Nr, ln as Pr, m as Fr, mn as Ir, mt as Lr, n as Rr, nn as zr, nt as Br, on as Vr, ot as Hr, pn as Ur, pt as Wr, q as Gr, qt as Kr, rn as qr, rt as Jr, s as Yr, sn as Xr, st as Zr, t as Qr, tn as $r, tt as ei, un as ti, v as ni, vt as ri, w as ii, wn as ai, wt as oi, x as si, xn as ci, xt as li, y as ui, yn as di, yt as fi, z as pi, zt as mi } from "./common-
|
|
9
|
+
import { $ as tn, $t as nn, A as rn, At as an, B as on, Bt as sn, C as cn, Cn as ln, Ct as un, D as dn, Dn as fn, Dt as pn, E as mn, En as hn, Et as gn, F as _n, Ft as vn, G as yn, Gt as bn, H as xn, Ht as Sn, I as Cn, It as wn, J as Tn, Jt as En, K as Dn, Kt as On, L as kn, Lt as An, M as jn, N as Mn, Nt as Nn, O as Pn, Ot as Fn, P as In, Pt as Ln, Q as Rn, Qt as zn, R as Bn, Rt as Vn, S as Hn, Sn as Un, St as Wn, T as Gn, Tn as Kn, Tt as qn, U as Jn, Ut as Yn, V as Xn, Vt as Zn, W as Qn, Wt as $n, X as er, Xt as tr, Y as nr, Yt as rr, Z as ir, Zt as ar, _ as or, _n as sr, _t as cr, a as lr, an as ur, at as dr, b as fr, bn as pr, bt as mr, cn as hr, dn as gr, dt as _r, en as vr, et as yr, fn as br, ft as xr, g as Sr, gn as Cr, gt as wr, hn as Tr, ht as Er, i as Dr, in as Or, it as kr, j as Ar, k as jr, kt as Mr, l as Nr, ln as Pr, m as Fr, mn as Ir, mt as Lr, n as Rr, nn as zr, nt as Br, on as Vr, ot as Hr, pn as Ur, pt as Wr, q as Gr, qt as Kr, rn as qr, rt as Jr, s as Yr, sn as Xr, st as Zr, t as Qr, tn as $r, tt as ei, un as ti, v as ni, vt as ri, w as ii, wn as ai, wt as oi, x as si, xn as ci, xt as li, y as ui, yn as di, yt as fi, z as pi, zt as mi } from "./common-BzvhwpFa.js";
|
|
10
10
|
import { c as hi, i as gi, n as _i, o as vi, t as yi } from "./useLifecycle-BGRBkTFT.js";
|
|
11
11
|
import { t as bi } from "./check-mcQyZmFG.js";
|
|
12
12
|
import { A as xi, C as Si, D as Ci, E as wi, F as Ti, I as Ei, M as Di, N as Oi, P as ki, R as Ai, S as ji, a as Mi, b as Ni, c as Pi, i as Fi, j as Ii, l as Li, n as Ri, o as zi, r as Bi, s as Vi, t as Hi, w as Ui, x as Wi } from "./select-UMOSCa3W.js";
|
|
@@ -29942,7 +29942,7 @@ function xR(e) {
|
|
|
29942
29942
|
}
|
|
29943
29943
|
//#endregion
|
|
29944
29944
|
//#region src/components/editor/renderers/slides-layout/slides-layout.tsx
|
|
29945
|
-
var SR = q.lazy(() => import("./reveal-component-
|
|
29945
|
+
var SR = q.lazy(() => import("./reveal-component-D5xyD8xS.js")), CR = (e) => {
|
|
29946
29946
|
let t = (0, Y.c)(33), { layout: n, setLayout: r, cells: i, mode: a } = e, o = fs(rt), s = a === "read" || o, c = fs(Oe) > 1, [l, u] = (0, q.useState)(null), d;
|
|
29947
29947
|
t[0] !== i || t[1] !== n ? (d = yP(i, n), t[0] = i, t[1] = n, t[2] = d) : d = t[2];
|
|
29948
29948
|
let { slideCells: f, skippedIds: p, noOutputIds: m, slideTypes: h, startCellIndex: g } = d, _;
|
|
@@ -4,7 +4,7 @@ import { t as o } from "./react-ofJbxM4x.js";
|
|
|
4
4
|
import { t as s } from "./compiler-runtime-Cj8ZWiNS.js";
|
|
5
5
|
import { kt as c, st as l } from "./html-to-image-CSU-_5BI.js";
|
|
6
6
|
import { u } from "./dist-6ChWbo8z.js";
|
|
7
|
-
import { _n as d, c as f, ct as p, g as m, h, l as g, lt as _, n as v, o as y, r as b, t as x, u as S, ut as C, vn as w, xn as T } from "./common-
|
|
7
|
+
import { _n as d, c as f, ct as p, g as m, h, l as g, lt as _, n as v, o as y, r as b, t as x, u as S, ut as C, vn as w, xn as T } from "./common-BzvhwpFa.js";
|
|
8
8
|
import { R as E } from "./select-UMOSCa3W.js";
|
|
9
9
|
import { Y as D } from "./dropdown-menu-C73aTomr.js";
|
|
10
10
|
import { t as O } from "./jsx-runtime-DgDPJV9S.js";
|
package/package.json
CHANGED
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
act,
|
|
5
|
+
fireEvent,
|
|
6
|
+
render,
|
|
7
|
+
screen,
|
|
8
|
+
waitFor,
|
|
9
|
+
} from "@testing-library/react";
|
|
10
|
+
import { Provider } from "jotai";
|
|
11
|
+
import type { ComponentProps } from "react";
|
|
12
|
+
import { JsonRpcError } from "use-acp";
|
|
13
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
14
|
+
import { MockRequestClient } from "@/__mocks__/requests";
|
|
15
|
+
import type { PromptInput } from "@/components/editor/ai/add-cell-with-ai";
|
|
16
|
+
import { TooltipProvider } from "@/components/ui/tooltip";
|
|
17
|
+
import { toast } from "@/components/ui/use-toast";
|
|
18
|
+
import { pendingAiPromptAtom } from "@/core/ai/state";
|
|
19
|
+
import { requestClientAtom } from "@/core/network/requests";
|
|
20
|
+
import { cwdAtom, filenameAtom } from "@/core/saving/file-state";
|
|
21
|
+
import { store } from "@/core/state/jotai";
|
|
22
|
+
import AgentPanel from "../agent-panel";
|
|
23
|
+
import { parseContextFromPrompt } from "../context-utils";
|
|
24
|
+
import {
|
|
25
|
+
addSession,
|
|
26
|
+
agentSessionStateAtom,
|
|
27
|
+
selectedTabAtom,
|
|
28
|
+
updateSessionExternalAgentSessionId,
|
|
29
|
+
} from "../state";
|
|
30
|
+
import type { ExternalAgentSessionId } from "../types";
|
|
31
|
+
|
|
32
|
+
const { agent, client } = vi.hoisted(() => {
|
|
33
|
+
const agent = {
|
|
34
|
+
initialize: vi.fn().mockResolvedValue({}),
|
|
35
|
+
prompt: vi.fn().mockResolvedValue({ stopReason: "end_turn" }),
|
|
36
|
+
cancel: vi.fn().mockResolvedValue({}),
|
|
37
|
+
};
|
|
38
|
+
return {
|
|
39
|
+
agent,
|
|
40
|
+
client: {
|
|
41
|
+
agent,
|
|
42
|
+
activeSessionId: "session-1",
|
|
43
|
+
connectionState: { status: "connected" },
|
|
44
|
+
notifications: [],
|
|
45
|
+
connect: vi.fn().mockResolvedValue(undefined),
|
|
46
|
+
disconnect: vi.fn(),
|
|
47
|
+
setActiveSessionId: vi.fn(),
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
vi.mock("use-acp", async (importOriginal) => ({
|
|
53
|
+
...(await importOriginal<typeof import("use-acp")>()),
|
|
54
|
+
useAcpClient: () => client,
|
|
55
|
+
}));
|
|
56
|
+
|
|
57
|
+
vi.mock("@/components/editor/ai/add-cell-with-ai", () => ({
|
|
58
|
+
PromptInput: ({
|
|
59
|
+
value,
|
|
60
|
+
onChange,
|
|
61
|
+
onAddFiles,
|
|
62
|
+
onSubmit,
|
|
63
|
+
}: ComponentProps<typeof PromptInput>) => (
|
|
64
|
+
<>
|
|
65
|
+
<textarea
|
|
66
|
+
aria-label="Prompt"
|
|
67
|
+
value={value}
|
|
68
|
+
onChange={(event) => onChange(event.target.value)}
|
|
69
|
+
onKeyDown={(event) => {
|
|
70
|
+
if (event.key === "Enter") {
|
|
71
|
+
onSubmit(undefined, value);
|
|
72
|
+
}
|
|
73
|
+
}}
|
|
74
|
+
/>
|
|
75
|
+
<input
|
|
76
|
+
type="file"
|
|
77
|
+
aria-label="Attachment"
|
|
78
|
+
onChange={(event) => onAddFiles?.([...(event.target.files ?? [])])}
|
|
79
|
+
/>
|
|
80
|
+
</>
|
|
81
|
+
),
|
|
82
|
+
}));
|
|
83
|
+
vi.mock("../session-tabs", () => ({ SessionTabs: () => null }));
|
|
84
|
+
vi.mock("../blocks", () => ({ ReadyToChatBlock: () => null }));
|
|
85
|
+
vi.mock("../context-utils", async (importOriginal) => ({
|
|
86
|
+
...(await importOriginal<typeof import("../context-utils")>()),
|
|
87
|
+
parseContextFromPrompt: vi
|
|
88
|
+
.fn()
|
|
89
|
+
.mockResolvedValue({ contextBlocks: [], attachmentBlocks: [] }),
|
|
90
|
+
}));
|
|
91
|
+
vi.mock("@/components/ui/use-toast", () => ({ toast: vi.fn() }));
|
|
92
|
+
|
|
93
|
+
function renderPanel() {
|
|
94
|
+
return render(
|
|
95
|
+
<Provider store={store}>
|
|
96
|
+
<TooltipProvider>
|
|
97
|
+
<AgentPanel />
|
|
98
|
+
</TooltipProvider>
|
|
99
|
+
</Provider>,
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function submitPrompt(prompt = "Explain this notebook") {
|
|
104
|
+
fireEvent.change(screen.getByRole("textbox", { name: "Prompt" }), {
|
|
105
|
+
target: { value: prompt },
|
|
106
|
+
});
|
|
107
|
+
fireEvent.keyDown(screen.getByRole("textbox", { name: "Prompt" }), {
|
|
108
|
+
key: "Enter",
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
describe("AgentPanel prompt submission", () => {
|
|
113
|
+
beforeEach(() => {
|
|
114
|
+
agent.prompt.mockReset().mockResolvedValue({ stopReason: "end_turn" });
|
|
115
|
+
vi.mocked(parseContextFromPrompt).mockReset().mockResolvedValue({
|
|
116
|
+
contextBlocks: [],
|
|
117
|
+
attachmentBlocks: [],
|
|
118
|
+
});
|
|
119
|
+
store.set(requestClientAtom, MockRequestClient.create());
|
|
120
|
+
store.set(filenameAtom, "notebook.py");
|
|
121
|
+
store.set(cwdAtom, "/notebooks");
|
|
122
|
+
store.set(pendingAiPromptAtom, null);
|
|
123
|
+
store.set(
|
|
124
|
+
agentSessionStateAtom,
|
|
125
|
+
updateSessionExternalAgentSessionId(
|
|
126
|
+
addSession({ sessions: [], activeTabId: null }, { agentId: "claude" }),
|
|
127
|
+
"session-1" as ExternalAgentSessionId,
|
|
128
|
+
),
|
|
129
|
+
);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
afterEach(() => {
|
|
133
|
+
store.set(pendingAiPromptAtom, null);
|
|
134
|
+
store.set(agentSessionStateAtom, { sessions: [], activeTabId: null });
|
|
135
|
+
store.set(filenameAtom, null);
|
|
136
|
+
store.set(cwdAtom, null);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it("preserves the draft, attachment and title when the notebook is unnamed", async () => {
|
|
140
|
+
store.set(filenameAtom, null);
|
|
141
|
+
renderPanel();
|
|
142
|
+
const title = store.get(selectedTabAtom)?.title;
|
|
143
|
+
fireEvent.change(screen.getByLabelText("Attachment"), {
|
|
144
|
+
target: {
|
|
145
|
+
files: [new File(["notes"], "notes.txt", { type: "text/plain" })],
|
|
146
|
+
},
|
|
147
|
+
});
|
|
148
|
+
submitPrompt();
|
|
149
|
+
|
|
150
|
+
expect(toast).toHaveBeenCalledWith({
|
|
151
|
+
title: "Notebook must be named",
|
|
152
|
+
description: "Please name the notebook to use the agent",
|
|
153
|
+
variant: "danger",
|
|
154
|
+
});
|
|
155
|
+
expect(agent.prompt).not.toHaveBeenCalled();
|
|
156
|
+
expect(screen.getByRole("textbox", { name: "Prompt" })).toHaveValue(
|
|
157
|
+
"Explain this notebook",
|
|
158
|
+
);
|
|
159
|
+
expect(screen.getByText("notes.txt")).toBeInTheDocument();
|
|
160
|
+
expect(store.get(selectedTabAtom)?.title).toBe(title);
|
|
161
|
+
expect(screen.queryByText("Agent is working...")).not.toBeInTheDocument();
|
|
162
|
+
|
|
163
|
+
act(() => store.set(filenameAtom, "notebook.py"));
|
|
164
|
+
fireEvent.keyDown(screen.getByRole("textbox", { name: "Prompt" }), {
|
|
165
|
+
key: "Enter",
|
|
166
|
+
});
|
|
167
|
+
await waitFor(() => expect(agent.prompt).toHaveBeenCalledOnce());
|
|
168
|
+
expect(agent.prompt).toHaveBeenCalledWith({
|
|
169
|
+
sessionId: "session-1",
|
|
170
|
+
prompt: expect.arrayContaining([
|
|
171
|
+
{ type: "text", text: "Explain this notebook" },
|
|
172
|
+
expect.objectContaining({ type: "resource_link", name: "notes.txt" }),
|
|
173
|
+
]),
|
|
174
|
+
});
|
|
175
|
+
await waitFor(() =>
|
|
176
|
+
expect(screen.queryByText("Agent is working...")).not.toBeInTheDocument(),
|
|
177
|
+
);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it.each([new Error("Agent unavailable"), "Agent unavailable"])(
|
|
181
|
+
"shows a rejected prompt and allows another submission after dismissal (%s)",
|
|
182
|
+
async (error) => {
|
|
183
|
+
agent.prompt.mockRejectedValueOnce(error);
|
|
184
|
+
renderPanel();
|
|
185
|
+
submitPrompt();
|
|
186
|
+
|
|
187
|
+
expect(await screen.findByText(/Agent unavailable/)).toBeInTheDocument();
|
|
188
|
+
fireEvent.click(screen.getByRole("button", { name: "Dismiss" }));
|
|
189
|
+
expect(screen.queryByText(/Agent unavailable/)).not.toBeInTheDocument();
|
|
190
|
+
submitPrompt("Try again");
|
|
191
|
+
await waitFor(() => expect(agent.prompt).toHaveBeenCalledTimes(2));
|
|
192
|
+
await waitFor(() =>
|
|
193
|
+
expect(
|
|
194
|
+
screen.queryByText("Agent is working..."),
|
|
195
|
+
).not.toBeInTheDocument(),
|
|
196
|
+
);
|
|
197
|
+
},
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
it("preserves the existing authentication error action and detail", async () => {
|
|
201
|
+
agent.prompt.mockRejectedValueOnce(
|
|
202
|
+
new JsonRpcError({
|
|
203
|
+
code: -32_000,
|
|
204
|
+
message: "Authentication required",
|
|
205
|
+
data: { message: "Sign in to the agent" },
|
|
206
|
+
}),
|
|
207
|
+
);
|
|
208
|
+
renderPanel();
|
|
209
|
+
submitPrompt();
|
|
210
|
+
|
|
211
|
+
expect(await screen.findByText("Sign in to the agent")).toBeInTheDocument();
|
|
212
|
+
expect(
|
|
213
|
+
screen.getByRole("button", { name: "Restart session" }),
|
|
214
|
+
).toBeInTheDocument();
|
|
215
|
+
expect(
|
|
216
|
+
screen.queryByRole("button", { name: "Dismiss" }),
|
|
217
|
+
).not.toBeInTheDocument();
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
it("clears busy state and reports failures while preparing prompt context", async () => {
|
|
221
|
+
vi.mocked(parseContextFromPrompt).mockRejectedValueOnce(
|
|
222
|
+
new Error("Context unavailable"),
|
|
223
|
+
);
|
|
224
|
+
renderPanel();
|
|
225
|
+
submitPrompt();
|
|
226
|
+
|
|
227
|
+
expect(await screen.findByText("Context unavailable")).toBeInTheDocument();
|
|
228
|
+
expect(agent.prompt).not.toHaveBeenCalled();
|
|
229
|
+
fireEvent.click(screen.getByRole("button", { name: "Dismiss" }));
|
|
230
|
+
expect(screen.queryByText("Agent is working...")).not.toBeInTheDocument();
|
|
231
|
+
submitPrompt();
|
|
232
|
+
await waitFor(() => expect(agent.prompt).toHaveBeenCalledOnce());
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
it("reports failures from a queued prompt through the same handler", async () => {
|
|
236
|
+
agent.prompt.mockRejectedValueOnce(new Error("Queued prompt failed"));
|
|
237
|
+
renderPanel();
|
|
238
|
+
act(() =>
|
|
239
|
+
store.set(pendingAiPromptAtom, {
|
|
240
|
+
prompt: "Help with this cell",
|
|
241
|
+
submit: true,
|
|
242
|
+
}),
|
|
243
|
+
);
|
|
244
|
+
|
|
245
|
+
expect(await screen.findByText("Queued prompt failed")).toBeInTheDocument();
|
|
246
|
+
expect(store.get(pendingAiPromptAtom)).toBeNull();
|
|
247
|
+
expect(agent.prompt).toHaveBeenCalledWith({
|
|
248
|
+
sessionId: "session-1",
|
|
249
|
+
prompt: expect.arrayContaining([
|
|
250
|
+
{ type: "text", text: "Help with this cell" },
|
|
251
|
+
]),
|
|
252
|
+
});
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
it("clears busy state when a cancelled prompt settles", async () => {
|
|
256
|
+
const response = Promise.withResolvers<{ stopReason: string }>();
|
|
257
|
+
agent.prompt.mockReturnValueOnce(response.promise);
|
|
258
|
+
renderPanel();
|
|
259
|
+
submitPrompt();
|
|
260
|
+
await waitFor(() => expect(agent.prompt).toHaveBeenCalledOnce());
|
|
261
|
+
expect(screen.getByText("Agent is working...")).toBeInTheDocument();
|
|
262
|
+
fireEvent.click(screen.getByRole("button", { name: "Stop" }));
|
|
263
|
+
await act(async () => response.resolve({ stopReason: "cancelled" }));
|
|
264
|
+
expect(agent.cancel).toHaveBeenCalledWith({ sessionId: "session-1" });
|
|
265
|
+
expect(screen.queryByText("Agent is working...")).not.toBeInTheDocument();
|
|
266
|
+
expect(
|
|
267
|
+
screen.queryByRole("button", { name: "Dismiss" }),
|
|
268
|
+
).not.toBeInTheDocument();
|
|
269
|
+
});
|
|
270
|
+
});
|
|
@@ -902,6 +902,18 @@ const AgentPanel: React.FC = () => {
|
|
|
902
902
|
return;
|
|
903
903
|
}
|
|
904
904
|
|
|
905
|
+
let absoluteFilename: string;
|
|
906
|
+
try {
|
|
907
|
+
absoluteFilename = getAbsoluteFilename();
|
|
908
|
+
} catch {
|
|
909
|
+
toast({
|
|
910
|
+
title: "Notebook must be named",
|
|
911
|
+
description: "Please name the notebook to use the agent",
|
|
912
|
+
variant: "danger",
|
|
913
|
+
});
|
|
914
|
+
return;
|
|
915
|
+
}
|
|
916
|
+
|
|
905
917
|
logger.debug("Submitting prompt to agent", {
|
|
906
918
|
sessionId: activeSessionId,
|
|
907
919
|
});
|
|
@@ -914,62 +926,51 @@ const AgentPanel: React.FC = () => {
|
|
|
914
926
|
setSessionState((prev) => updateSessionTitle(prev, prompt));
|
|
915
927
|
}
|
|
916
928
|
|
|
917
|
-
let absoluteFilename: string;
|
|
918
929
|
try {
|
|
919
|
-
|
|
920
|
-
} catch {
|
|
921
|
-
toast({
|
|
922
|
-
title: "Notebook must be named",
|
|
923
|
-
description: "Please name the notebook to use the agent",
|
|
924
|
-
variant: "danger",
|
|
925
|
-
});
|
|
926
|
-
return;
|
|
927
|
-
}
|
|
928
|
-
|
|
929
|
-
const promptBlocks: ContentBlock[] = [{ type: "text", text: prompt }];
|
|
930
|
+
const promptBlocks: ContentBlock[] = [{ type: "text", text: prompt }];
|
|
930
931
|
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
932
|
+
// Parse context from the prompt
|
|
933
|
+
const { contextBlocks, attachmentBlocks } =
|
|
934
|
+
await parseContextFromPrompt(prompt);
|
|
935
|
+
promptBlocks.push(...contextBlocks, ...attachmentBlocks);
|
|
935
936
|
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
937
|
+
// Add manually uploaded files as resource links
|
|
938
|
+
if (files && files.length > 0) {
|
|
939
|
+
const fileResourceLinks = await convertFilesToResourceLinks(files);
|
|
940
|
+
promptBlocks.push(...fileResourceLinks);
|
|
941
|
+
}
|
|
941
942
|
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
);
|
|
947
|
-
if (!hasGivenRules) {
|
|
948
|
-
promptBlocks.push(
|
|
949
|
-
{
|
|
950
|
-
type: "resource_link",
|
|
951
|
-
uri: absoluteFilename,
|
|
952
|
-
mimeType: "text/x-python",
|
|
953
|
-
name: absoluteFilename,
|
|
954
|
-
},
|
|
955
|
-
{
|
|
956
|
-
type: "resource",
|
|
957
|
-
resource: {
|
|
958
|
-
uri: "marimo_rules.md",
|
|
959
|
-
mimeType: "text/plain",
|
|
960
|
-
text: getAgentPrompt(absoluteFilename),
|
|
961
|
-
},
|
|
962
|
-
},
|
|
943
|
+
const hasGivenRules = notifications.some(
|
|
944
|
+
(notification) =>
|
|
945
|
+
notification.type === "session_notification" &&
|
|
946
|
+
notification.data.update.sessionUpdate === "user_message_chunk",
|
|
963
947
|
);
|
|
964
|
-
|
|
948
|
+
if (!hasGivenRules) {
|
|
949
|
+
promptBlocks.push(
|
|
950
|
+
{
|
|
951
|
+
type: "resource_link",
|
|
952
|
+
uri: absoluteFilename,
|
|
953
|
+
mimeType: "text/x-python",
|
|
954
|
+
name: absoluteFilename,
|
|
955
|
+
},
|
|
956
|
+
{
|
|
957
|
+
type: "resource",
|
|
958
|
+
resource: {
|
|
959
|
+
uri: "marimo_rules.md",
|
|
960
|
+
mimeType: "text/plain",
|
|
961
|
+
text: getAgentPrompt(absoluteFilename),
|
|
962
|
+
},
|
|
963
|
+
},
|
|
964
|
+
);
|
|
965
|
+
}
|
|
965
966
|
|
|
966
|
-
try {
|
|
967
967
|
await agent.prompt({
|
|
968
968
|
sessionId: activeSessionId,
|
|
969
969
|
prompt: promptBlocks,
|
|
970
970
|
});
|
|
971
971
|
} catch (error) {
|
|
972
972
|
logger.error("Failed to send prompt", { error });
|
|
973
|
+
setError(error instanceof Error ? error : String(error));
|
|
973
974
|
} finally {
|
|
974
975
|
setIsLoading(false);
|
|
975
976
|
}
|