bangonit 0.4.3 → 0.5.2
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/README.md +2 -1
- package/app/desktopapp/dist/main/index.js +10 -4
- package/app/desktopapp/dist/main/ipc.js +9 -24
- package/app/desktopapp/dist/main/preload.js +0 -7
- package/app/desktopapp/dist/main/tabs.js +10 -5
- package/app/desktopapp/package.json +0 -1
- package/app/replay/dist/replay.js +2 -2
- package/app/webapp/.next/standalone/app/webapp/.next/BUILD_ID +1 -1
- package/app/webapp/.next/standalone/app/webapp/.next/app-build-manifest.json +11 -11
- package/app/webapp/.next/standalone/app/webapp/.next/app-path-routes-manifest.json +1 -1
- package/app/webapp/.next/standalone/app/webapp/.next/build-manifest.json +3 -3
- package/app/webapp/.next/standalone/app/webapp/.next/prerender-manifest.json +1 -1
- package/app/webapp/.next/standalone/app/webapp/.next/required-server-files.json +1 -1
- package/app/webapp/.next/standalone/app/webapp/.next/server/app/_not-found/page.js +1 -1
- package/app/webapp/.next/standalone/app/webapp/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/app/webapp/.next/standalone/app/webapp/.next/server/app/_not-found.html +1 -1
- package/app/webapp/.next/standalone/app/webapp/.next/server/app/_not-found.rsc +1 -1
- package/app/webapp/.next/standalone/app/webapp/.next/server/app/api/chat/route.js +1 -1
- package/app/webapp/.next/standalone/app/webapp/.next/server/app/api/screenshot/route.js +1 -1
- package/app/webapp/.next/standalone/app/webapp/.next/server/app/app/page.js +6 -6
- package/app/webapp/.next/standalone/app/webapp/.next/server/app/app/page_client-reference-manifest.js +1 -1
- package/app/webapp/.next/standalone/app/webapp/.next/server/app/app.html +1 -1
- package/app/webapp/.next/standalone/app/webapp/.next/server/app/app.rsc +2 -2
- package/app/webapp/.next/standalone/app/webapp/.next/server/app/index.html +1 -1
- package/app/webapp/.next/standalone/app/webapp/.next/server/app/index.rsc +1 -1
- package/app/webapp/.next/standalone/app/webapp/.next/server/app/page.js +1 -1
- package/app/webapp/.next/standalone/app/webapp/.next/server/app/page_client-reference-manifest.js +1 -1
- package/app/webapp/.next/standalone/app/webapp/.next/server/app-paths-manifest.json +1 -1
- package/app/webapp/.next/standalone/app/webapp/.next/server/chunks/679.js +1 -1
- package/app/webapp/.next/standalone/app/webapp/.next/server/chunks/708.js +3 -3
- package/app/webapp/.next/standalone/app/webapp/.next/server/middleware-build-manifest.js +1 -1
- package/app/webapp/.next/standalone/app/webapp/.next/server/pages/404.html +1 -1
- package/app/webapp/.next/standalone/app/webapp/.next/server/pages/500.html +1 -1
- package/app/webapp/.next/standalone/app/webapp/.next/server/server-reference-manifest.json +1 -1
- package/app/webapp/.next/standalone/app/webapp/.next/static/chunks/app/app/page-533a30559a8f39fa.js +1 -0
- package/app/webapp/.next/standalone/app/webapp/.next/static/chunks/app/layout-40f50d9380154ecf.js +1 -0
- package/app/webapp/.next/{static/chunks/main-app-106dd83f859b9dfa.js → standalone/app/webapp/.next/static/chunks/main-app-76384b941f0b51cb.js} +1 -1
- package/app/webapp/.next/standalone/app/webapp/package.json +2 -6
- package/app/webapp/.next/standalone/app/webapp/server.js +1 -1
- package/app/webapp/.next/standalone/package.json +18 -6
- package/app/webapp/.next/static/chunks/app/app/page-533a30559a8f39fa.js +1 -0
- package/app/webapp/.next/static/chunks/app/layout-40f50d9380154ecf.js +1 -0
- package/app/webapp/.next/{standalone/app/webapp/.next/static/chunks/main-app-106dd83f859b9dfa.js → static/chunks/main-app-76384b941f0b51cb.js} +1 -1
- package/app/webapp/package.json +2 -6
- package/app/webapp/skills/document-review.md +1 -0
- package/app/webapp/skills/gmail.md +2 -0
- package/app/webapp/src/app/globals.css +8 -3
- package/app/webapp/src/app/layout.tsx +2 -8
- package/app/webapp/src/shared/api/chat.ts +49 -25
- package/app/webapp/src/shared/api/screenshot.ts +11 -10
- package/app/webapp/src/shared/components/AppShell.tsx +80 -109
- package/app/webapp/src/shared/components/SessionView.tsx +335 -248
- package/app/webapp/src/shared/components/VirtualCursor.tsx +13 -14
- package/app/webapp/src/shared/lib/browser/cursor.ts +2 -7
- package/app/webapp/src/shared/lib/browser/index.ts +56 -36
- package/app/webapp/src/shared/lib/browser/mouse.ts +86 -21
- package/app/webapp/src/shared/lib/browser/navigate.ts +1 -4
- package/app/webapp/src/shared/lib/browser/recorder.ts +12 -5
- package/app/webapp/src/shared/lib/browser/screenshot.ts +4 -4
- package/app/webapp/src/shared/lib/browser/snapshot.ts +9 -5
- package/app/webapp/src/shared/lib/browser/tabs.ts +1 -1
- package/app/webapp/src/shared/lib/browser/types.ts +3 -2
- package/app/webapp/src/shared/lib/browser/wait.ts +1 -1
- package/app/webapp/src/shared/lib/recorder/session-recorder.ts +1 -1
- package/app/webapp/src/shared/types/global.d.ts +8 -19
- package/app/webapp/tailwind.config.js +1 -3
- package/bin/src/cli/bangonit.js +270 -177
- package/package.json +18 -6
- package/app/webapp/.next/standalone/app/webapp/.next/static/chunks/app/app/page-03dbc2fc67c26b74.js +0 -1
- package/app/webapp/.next/standalone/app/webapp/.next/static/chunks/app/layout-57acb80d8da0067a.js +0 -1
- package/app/webapp/.next/static/chunks/app/app/page-03dbc2fc67c26b74.js +0 -1
- package/app/webapp/.next/static/chunks/app/layout-57acb80d8da0067a.js +0 -1
- /package/app/webapp/.next/standalone/app/webapp/.next/static/{z2gRF0NKwztPLZ9d7ok06 → kz1a_SRPtSly3Fe8wHKDq}/_buildManifest.js +0 -0
- /package/app/webapp/.next/standalone/app/webapp/.next/static/{z2gRF0NKwztPLZ9d7ok06 → kz1a_SRPtSly3Fe8wHKDq}/_ssgManifest.js +0 -0
- /package/app/webapp/.next/static/{z2gRF0NKwztPLZ9d7ok06 → kz1a_SRPtSly3Fe8wHKDq}/_buildManifest.js +0 -0
- /package/app/webapp/.next/static/{z2gRF0NKwztPLZ9d7ok06 → kz1a_SRPtSly3Fe8wHKDq}/_ssgManifest.js +0 -0
|
@@ -50,7 +50,7 @@ export default function SessionView({
|
|
|
50
50
|
const [tabs, setTabs] = useState<Tab[]>(
|
|
51
51
|
initialTabs?.map((t) => ({ ...t, initialUrl: t.url })) || [
|
|
52
52
|
{ id: 0, url: "about:blank", title: "", initialUrl: "about:blank" },
|
|
53
|
-
]
|
|
53
|
+
],
|
|
54
54
|
);
|
|
55
55
|
const [activeTabId, setActiveTabId] = useState(initialActiveTabId ?? 0);
|
|
56
56
|
const [urlInput, setUrlInput] = useState(() => {
|
|
@@ -62,7 +62,9 @@ export default function SessionView({
|
|
|
62
62
|
const [devtoolsOpen, setDevtoolsOpen] = useState(false);
|
|
63
63
|
const webviewRefs = useRef<Map<number, any>>(new Map());
|
|
64
64
|
// Browser is always view-only — block all user input
|
|
65
|
-
const [todos, setTodos] = useState<{ content: string; status: "pending" | "in_progress" | "completed" }[]>(
|
|
65
|
+
const [todos, setTodos] = useState<{ content: string; status: "pending" | "in_progress" | "completed" }[]>(
|
|
66
|
+
initialTodos || [],
|
|
67
|
+
);
|
|
66
68
|
const messagesEndRef = useRef<HTMLDivElement>(null);
|
|
67
69
|
const hasSubmittedRef = useRef(false);
|
|
68
70
|
const nextTabId = useRef(initialTabs ? Math.max(...initialTabs.map((t) => t.id)) + 1 : 1);
|
|
@@ -91,17 +93,20 @@ export default function SessionView({
|
|
|
91
93
|
"X-Client-Platform": navigator.platform,
|
|
92
94
|
}),
|
|
93
95
|
}),
|
|
94
|
-
[]
|
|
96
|
+
[],
|
|
95
97
|
);
|
|
96
98
|
|
|
97
99
|
const stoppedRef = useRef(false);
|
|
98
100
|
|
|
99
101
|
// Stream agent output to main process for CLI
|
|
100
|
-
const emit = useCallback(
|
|
101
|
-
|
|
102
|
-
|
|
102
|
+
const emit = useCallback(
|
|
103
|
+
(type: string, text: string) => {
|
|
104
|
+
window.bangonit?.emitAgentOutput?.({ agentId, type, text, name: agentName });
|
|
105
|
+
},
|
|
106
|
+
[agentId, agentName],
|
|
107
|
+
);
|
|
103
108
|
|
|
104
|
-
const { messages,
|
|
109
|
+
const { messages, sendMessage, addToolOutput, status, error } = useChat({
|
|
105
110
|
transport,
|
|
106
111
|
messages: initialMessages,
|
|
107
112
|
sendAutomaticallyWhen: ({ messages }) =>
|
|
@@ -157,22 +162,37 @@ export default function SessionView({
|
|
|
157
162
|
if (tc.toolName === "browser") {
|
|
158
163
|
try {
|
|
159
164
|
const input = tc.input as any;
|
|
160
|
-
const label = (input?.actions || [])
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
165
|
+
const label = (input?.actions || [])
|
|
166
|
+
.map((a: any) => {
|
|
167
|
+
if (a.action === "navigate") return `navigate ${a.url}`;
|
|
168
|
+
if (a.action === "type") return `type "${(a.text || "").slice(0, 30)}"`;
|
|
169
|
+
if (a.action === "press") return `press ${a.key}`;
|
|
170
|
+
if (a.action === "mouse")
|
|
171
|
+
return `mouse ${(a.mouseActions || []).map((s: any) => `${s.action} ${s.ref || `${s.x},${s.y}`}`).join(" > ")}`;
|
|
172
|
+
return a.action;
|
|
173
|
+
})
|
|
174
|
+
.join(", ");
|
|
167
175
|
emit("tool", `browser: ${label}${input?.observe ? ` [${input.observe}]` : ""}`);
|
|
168
|
-
sessionRecorderRef.current?.addEvent({
|
|
176
|
+
sessionRecorderRef.current?.addEvent({
|
|
177
|
+
ts: Date.now(),
|
|
178
|
+
agentId,
|
|
179
|
+
type: "tool-start",
|
|
180
|
+
data: { tool: "browser", label },
|
|
181
|
+
});
|
|
169
182
|
const bt = browserToolsRef.current;
|
|
170
|
-
const raw = bt
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
183
|
+
const raw = bt
|
|
184
|
+
? await bt.exec({
|
|
185
|
+
actions: input?.actions || [],
|
|
186
|
+
observe: input?.observe || "snapshot",
|
|
187
|
+
prompts: input?.prompts,
|
|
188
|
+
})
|
|
189
|
+
: { error: "Error: browser tools not available" };
|
|
190
|
+
sessionRecorderRef.current?.addEvent({
|
|
191
|
+
ts: Date.now(),
|
|
192
|
+
agentId,
|
|
193
|
+
type: "tool-end",
|
|
194
|
+
data: { tool: "browser" },
|
|
195
|
+
});
|
|
176
196
|
addToolOutput({
|
|
177
197
|
tool: tc.toolName,
|
|
178
198
|
toolCallId: tc.toolCallId,
|
|
@@ -192,11 +212,6 @@ export default function SessionView({
|
|
|
192
212
|
},
|
|
193
213
|
});
|
|
194
214
|
|
|
195
|
-
const stop = useCallback(() => {
|
|
196
|
-
stoppedRef.current = true;
|
|
197
|
-
rawStop();
|
|
198
|
-
}, [rawStop]);
|
|
199
|
-
|
|
200
215
|
const isLoading = status === "submitted" || status === "streaming";
|
|
201
216
|
|
|
202
217
|
// Clear stopped flag when a new request starts
|
|
@@ -228,10 +243,11 @@ export default function SessionView({
|
|
|
228
243
|
if (messages.length === 0) return;
|
|
229
244
|
const lastMsg = messages[messages.length - 1];
|
|
230
245
|
if (lastMsg.role !== "assistant") return;
|
|
231
|
-
const text =
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
246
|
+
const text =
|
|
247
|
+
lastMsg.parts
|
|
248
|
+
?.filter((p: any) => p.type === "text")
|
|
249
|
+
.map((p: any) => p.text)
|
|
250
|
+
.join("") || "";
|
|
235
251
|
if (lastMsg.id !== lastStreamedMsgIdRef.current) {
|
|
236
252
|
lastStreamedMsgIdRef.current = lastMsg.id;
|
|
237
253
|
lastStreamedLenRef.current = 0;
|
|
@@ -273,7 +289,9 @@ export default function SessionView({
|
|
|
273
289
|
nudgeTimerRef.current = null;
|
|
274
290
|
if (!nudgedRef.current && !isDone) {
|
|
275
291
|
nudgedRef.current = true;
|
|
276
|
-
sendMessage({
|
|
292
|
+
sendMessage({
|
|
293
|
+
text: "You must call the report_result tool to finalize this test run. Call it now with the result (pass or fail) and a summary.",
|
|
294
|
+
});
|
|
277
295
|
}
|
|
278
296
|
}, 5000);
|
|
279
297
|
return () => {
|
|
@@ -293,59 +311,55 @@ export default function SessionView({
|
|
|
293
311
|
}
|
|
294
312
|
}, [messages, agentId]);
|
|
295
313
|
|
|
296
|
-
// Persist messages, tabs, and todos on change
|
|
297
|
-
useEffect(() => {
|
|
298
|
-
if (messages.length > 0) {
|
|
299
|
-
window.bangonit?.setAgentSession(agentId, {
|
|
300
|
-
messages,
|
|
301
|
-
initialPrompt,
|
|
302
|
-
tabs: tabs.filter((t) => !t.isPopup).map(({ id, url, title }) => ({ id, url, title })),
|
|
303
|
-
activeTabId: tabs.find((t) => t.id === activeTabId)?.isPopup ? tabs.find((t) => !t.isPopup)?.id ?? 0 : activeTabId,
|
|
304
|
-
todos,
|
|
305
|
-
});
|
|
306
|
-
}
|
|
307
|
-
}, [messages, tabs, activeTabId, agentId, initialPrompt, todos]);
|
|
308
|
-
|
|
309
314
|
// Create a new tab
|
|
310
|
-
const createTab = useCallback(
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
315
|
+
const createTab = useCallback(
|
|
316
|
+
(url: string) => {
|
|
317
|
+
const id = nextTabId.current++;
|
|
318
|
+
setTabs((prev) => [...prev, { id, url: "about:blank", title: "", initialUrl: url }]);
|
|
319
|
+
setActiveTabId(id);
|
|
320
|
+
setUrlInput(url === "about:blank" ? "" : url);
|
|
321
|
+
window.bangonit?.setActiveTab(agentId, id);
|
|
322
|
+
},
|
|
323
|
+
[agentId],
|
|
324
|
+
);
|
|
317
325
|
|
|
318
326
|
// Close a tab by its ID
|
|
319
|
-
const closeTab = useCallback(
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
327
|
+
const closeTab = useCallback(
|
|
328
|
+
(tabId: number) => {
|
|
329
|
+
setTabs((prev) => {
|
|
330
|
+
const idx = prev.findIndex((t) => t.id === tabId);
|
|
331
|
+
if (idx === -1) return prev;
|
|
332
|
+
const remaining = prev.filter((t) => t.id !== tabId);
|
|
333
|
+
if (remaining.length === 0) return prev;
|
|
334
|
+
setActiveTabId((current) => {
|
|
335
|
+
if (current === tabId) {
|
|
336
|
+
const newActive = remaining[Math.max(0, idx - 1)].id;
|
|
337
|
+
const newTab = remaining.find((t) => t.id === newActive);
|
|
338
|
+
if (newTab) setUrlInput(newTab.url === "about:blank" ? "" : newTab.url);
|
|
339
|
+
window.bangonit?.setActiveTab(agentId, newActive);
|
|
340
|
+
return newActive;
|
|
341
|
+
}
|
|
342
|
+
return current;
|
|
343
|
+
});
|
|
344
|
+
return remaining;
|
|
334
345
|
});
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
346
|
+
},
|
|
347
|
+
[agentId],
|
|
348
|
+
);
|
|
338
349
|
|
|
339
350
|
// Switch active tab
|
|
340
|
-
const selectTab = useCallback(
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
351
|
+
const selectTab = useCallback(
|
|
352
|
+
(tabId: number) => {
|
|
353
|
+
setActiveTabId(tabId);
|
|
354
|
+
window.bangonit?.setActiveTab(agentId, tabId);
|
|
355
|
+
setTabs((prev) => {
|
|
356
|
+
const tab = prev.find((t) => t.id === tabId);
|
|
357
|
+
if (tab) setUrlInput(tab.url === "about:blank" ? "" : tab.url);
|
|
358
|
+
return prev;
|
|
359
|
+
});
|
|
360
|
+
},
|
|
361
|
+
[agentId],
|
|
362
|
+
);
|
|
349
363
|
|
|
350
364
|
// Stable refs for callbacks
|
|
351
365
|
const createTabRef = useRef(createTab);
|
|
@@ -357,47 +371,68 @@ export default function SessionView({
|
|
|
357
371
|
|
|
358
372
|
useEffect(() => {
|
|
359
373
|
const cleanups: (() => void)[] = [];
|
|
360
|
-
const push = (fn: (() => void) | undefined) => {
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
374
|
+
const push = (fn: (() => void) | undefined) => {
|
|
375
|
+
if (fn) cleanups.push(fn);
|
|
376
|
+
};
|
|
377
|
+
|
|
378
|
+
push(
|
|
379
|
+
window.bangonit?.onTabUpdated((data: any) => {
|
|
380
|
+
if (data.agentId !== agentId) return;
|
|
381
|
+
const isBlank = data.url === "about:blank";
|
|
382
|
+
const isBlankTitle = data.title === "about:blank";
|
|
383
|
+
setTabs((prev) =>
|
|
384
|
+
prev.map((t) =>
|
|
385
|
+
t.id === data.tabId
|
|
386
|
+
? {
|
|
387
|
+
...t,
|
|
388
|
+
...(!isBlank && data.url !== undefined && { url: data.url }),
|
|
389
|
+
...(!isBlankTitle && data.title !== undefined && { title: data.title }),
|
|
390
|
+
}
|
|
391
|
+
: t,
|
|
392
|
+
),
|
|
393
|
+
);
|
|
394
|
+
if (data.url !== undefined && !isBlank) {
|
|
395
|
+
setActiveTabId((current) => {
|
|
396
|
+
if (current === data.tabId) setUrlInput(data.url);
|
|
397
|
+
return current;
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
}),
|
|
401
|
+
);
|
|
402
|
+
|
|
403
|
+
push(
|
|
404
|
+
window.bangonit?.onOpenNewTab((data: any) => {
|
|
405
|
+
if (data.agentId !== agentId) return;
|
|
406
|
+
createTabRef.current(data.url);
|
|
407
|
+
}),
|
|
408
|
+
);
|
|
409
|
+
push(
|
|
410
|
+
window.bangonit?.onPopupOpened((data: any) => {
|
|
411
|
+
if (data.agentId !== agentId) return;
|
|
412
|
+
setTabs((prev) => [
|
|
413
|
+
...prev,
|
|
414
|
+
{
|
|
415
|
+
id: data.tabId,
|
|
416
|
+
url: data.url || "about:blank",
|
|
417
|
+
title: "",
|
|
418
|
+
initialUrl: data.url || "about:blank",
|
|
419
|
+
isPopup: true,
|
|
420
|
+
},
|
|
421
|
+
]);
|
|
422
|
+
}),
|
|
423
|
+
);
|
|
424
|
+
push(
|
|
425
|
+
window.bangonit?.onCloseTab((data: any) => {
|
|
426
|
+
if (data.agentId !== agentId) return;
|
|
427
|
+
closeTabRef.current(data.tabId);
|
|
428
|
+
}),
|
|
429
|
+
);
|
|
430
|
+
push(
|
|
431
|
+
window.bangonit?.onSelectTab((data: any) => {
|
|
432
|
+
if (data.agentId !== agentId) return;
|
|
433
|
+
selectTabRef.current(data.tabId);
|
|
434
|
+
}),
|
|
435
|
+
);
|
|
401
436
|
// Mouse lock/unlock events unused — input blocked while test is running
|
|
402
437
|
|
|
403
438
|
return () => cleanups.forEach((fn) => fn?.());
|
|
@@ -417,40 +452,45 @@ export default function SessionView({
|
|
|
417
452
|
}, [messages]);
|
|
418
453
|
|
|
419
454
|
// Register a webview's webContents with the main process
|
|
420
|
-
const handleWebviewRef = useCallback(
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
455
|
+
const handleWebviewRef = useCallback(
|
|
456
|
+
(el: any, tabId: number, initialUrl: string) => {
|
|
457
|
+
if (!el) return;
|
|
458
|
+
webviewRefs.current.set(tabId, el);
|
|
459
|
+
const register = () => {
|
|
460
|
+
if (registeredTabsRef.current.has(tabId)) return;
|
|
461
|
+
try {
|
|
462
|
+
const wcId = el.getWebContentsId?.();
|
|
463
|
+
if (wcId) {
|
|
464
|
+
registeredTabsRef.current.add(tabId);
|
|
465
|
+
window.bangonit?.registerTab(agentId, tabId, wcId, initialUrl, planDir);
|
|
466
|
+
if (tabId === activeTabId) {
|
|
467
|
+
window.bangonit?.setActiveTab(agentId, tabId);
|
|
468
|
+
}
|
|
432
469
|
}
|
|
470
|
+
} catch (e) {
|
|
471
|
+
console.error(e);
|
|
433
472
|
}
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
473
|
+
};
|
|
474
|
+
register();
|
|
475
|
+
el.addEventListener("did-attach", register);
|
|
476
|
+
el.addEventListener("close", () => closeTabRef.current(tabId));
|
|
477
|
+
// Forward browser console messages to main process + session recorder (once per webview)
|
|
478
|
+
if (!el._consoleListenerAttached) {
|
|
479
|
+
el._consoleListenerAttached = true;
|
|
480
|
+
el.addEventListener("console-message", (e: any) => {
|
|
481
|
+
window.bangonit?.emitConsoleMessage?.({
|
|
482
|
+
agentId,
|
|
483
|
+
level: e.level,
|
|
484
|
+
message: e.message,
|
|
485
|
+
url: e.sourceId || "",
|
|
486
|
+
line: e.lineNumber || 0,
|
|
487
|
+
});
|
|
488
|
+
sessionRecorderRef.current?.addConsoleLog(agentId, e.level, e.message, e.sourceId || "", e.lineNumber || 0);
|
|
449
489
|
});
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
490
|
+
}
|
|
491
|
+
},
|
|
492
|
+
[agentId, activeTabId],
|
|
493
|
+
);
|
|
454
494
|
|
|
455
495
|
const formatTime = (s: number) => `${Math.floor(s / 60)}:${(s % 60).toString().padStart(2, "0")}`;
|
|
456
496
|
|
|
@@ -467,7 +507,10 @@ export default function SessionView({
|
|
|
467
507
|
className={`flex items-center gap-1.5 px-3 py-2 text-xs border-r border-zinc-800 min-w-0 max-w-[200px] shrink-0
|
|
468
508
|
${tab.id === activeTabId ? "bg-zinc-800 text-zinc-200" : "text-zinc-500 hover:text-zinc-300 hover:bg-zinc-850"}`}
|
|
469
509
|
>
|
|
470
|
-
<span className="truncate">
|
|
510
|
+
<span className="truncate">
|
|
511
|
+
{tab.isPopup ? "↗ " : ""}
|
|
512
|
+
{tab.title || (tab.url && tab.url !== "about:blank" ? tab.url : "New Tab")}
|
|
513
|
+
</span>
|
|
471
514
|
</button>
|
|
472
515
|
))}
|
|
473
516
|
</div>
|
|
@@ -481,35 +524,38 @@ export default function SessionView({
|
|
|
481
524
|
|
|
482
525
|
{/* Webviews */}
|
|
483
526
|
<div className="flex-1 relative bg-white overflow-hidden" onWheel={(e) => e.stopPropagation()}>
|
|
484
|
-
{tabs.map((tab) =>
|
|
485
|
-
tab.
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
527
|
+
{tabs.map((tab) =>
|
|
528
|
+
tab.isPopup ? (
|
|
529
|
+
tab.id === activeTabId && (
|
|
530
|
+
<div
|
|
531
|
+
key={tab.id}
|
|
532
|
+
className="absolute inset-0 flex items-center justify-center bg-zinc-900 text-zinc-400 text-sm z-[1]"
|
|
533
|
+
>
|
|
534
|
+
Popup window open — {tab.title || tab.url}
|
|
535
|
+
</div>
|
|
536
|
+
)
|
|
537
|
+
) : (
|
|
538
|
+
<webview
|
|
539
|
+
key={tab.id}
|
|
540
|
+
ref={(el: any) => handleWebviewRef(el, tab.id, tab.initialUrl)}
|
|
541
|
+
src="about:blank"
|
|
542
|
+
partition={`agent-${agentId}`}
|
|
543
|
+
allowpopups=""
|
|
544
|
+
style={{
|
|
545
|
+
width: "100%",
|
|
546
|
+
height: "100%",
|
|
547
|
+
position: "absolute",
|
|
548
|
+
backgroundColor: "white",
|
|
549
|
+
top: 0,
|
|
550
|
+
left: 0,
|
|
551
|
+
pointerEvents: tab.id === activeTabId ? "auto" : "none",
|
|
552
|
+
zIndex: tab.id === activeTabId ? 1 : 0,
|
|
553
|
+
}}
|
|
554
|
+
/>
|
|
555
|
+
),
|
|
512
556
|
)}
|
|
557
|
+
{!tabs.find((t) => t.id === activeTabId)?.isPopup && <VirtualCursor agentId={agentId} />}
|
|
558
|
+
{!isDone && <div className="absolute inset-0 z-10 cursor-not-allowed" />}
|
|
513
559
|
</div>
|
|
514
560
|
</div>
|
|
515
561
|
|
|
@@ -523,17 +569,16 @@ export default function SessionView({
|
|
|
523
569
|
|
|
524
570
|
{/* Test result banner */}
|
|
525
571
|
{testResult && (
|
|
526
|
-
<div
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
}`}
|
|
572
|
+
<div
|
|
573
|
+
className={`px-4 py-2 text-sm border-b flex items-center gap-3 ${
|
|
574
|
+
testResult.result === "pass" ? "bg-green-950/30 border-green-900/50" : "bg-red-950/30 border-red-900/50"
|
|
575
|
+
}`}
|
|
576
|
+
>
|
|
530
577
|
<span className={`font-medium ${testResult.result === "pass" ? "text-green-400" : "text-red-400"}`}>
|
|
531
578
|
{testResult.result === "pass" ? "PASSED" : "FAILED"}
|
|
532
579
|
</span>
|
|
533
580
|
<span className="text-zinc-500 text-xs">{formatTime(elapsedSeconds)}</span>
|
|
534
|
-
{testResult.summary &&
|
|
535
|
-
<span className="text-zinc-400 text-xs truncate flex-1">{testResult.summary}</span>
|
|
536
|
-
)}
|
|
581
|
+
{testResult.summary && <span className="text-zinc-400 text-xs truncate flex-1">{testResult.summary}</span>}
|
|
537
582
|
</div>
|
|
538
583
|
)}
|
|
539
584
|
|
|
@@ -541,11 +586,18 @@ export default function SessionView({
|
|
|
541
586
|
{todos.length > 0 && (
|
|
542
587
|
<div className="text-xs space-y-1 px-4 py-2 border-b border-zinc-800 bg-zinc-900/80 shrink-0 max-h-48 overflow-y-auto">
|
|
543
588
|
{todos.map((todo, i) => (
|
|
544
|
-
<div
|
|
589
|
+
<div
|
|
590
|
+
key={i}
|
|
591
|
+
className={`flex items-center gap-2 ${todo.status === "completed" ? "text-zinc-500" : "text-zinc-300"}`}
|
|
592
|
+
>
|
|
545
593
|
<span className="shrink-0">
|
|
546
|
-
{todo.status === "completed" ?
|
|
594
|
+
{todo.status === "completed" ? (
|
|
595
|
+
"✓"
|
|
596
|
+
) : todo.status === "in_progress" ? (
|
|
547
597
|
<span className="inline-block w-2 h-2 bg-blue-500 rounded-full animate-pulse" />
|
|
548
|
-
) :
|
|
598
|
+
) : (
|
|
599
|
+
"○"
|
|
600
|
+
)}
|
|
549
601
|
</span>
|
|
550
602
|
<span className={todo.status === "completed" ? "line-through" : ""}>{todo.content}</span>
|
|
551
603
|
</div>
|
|
@@ -570,8 +622,12 @@ export default function SessionView({
|
|
|
570
622
|
<div className="px-4 py-2 border-t border-zinc-800 flex items-center gap-3 shrink-0">
|
|
571
623
|
{isDone ? (
|
|
572
624
|
<>
|
|
573
|
-
<span
|
|
574
|
-
|
|
625
|
+
<span
|
|
626
|
+
className={`inline-block w-2 h-2 rounded-full shrink-0 ${testResult!.result === "pass" ? "bg-green-500" : "bg-red-500"}`}
|
|
627
|
+
/>
|
|
628
|
+
<span
|
|
629
|
+
className={`text-xs font-medium ${testResult!.result === "pass" ? "text-green-400" : "text-red-400"}`}
|
|
630
|
+
>
|
|
575
631
|
{testResult!.result === "pass" ? "Passed" : "Failed"} in {formatTime(elapsedSeconds)}
|
|
576
632
|
</span>
|
|
577
633
|
<div className="flex-1" />
|
|
@@ -584,10 +640,11 @@ export default function SessionView({
|
|
|
584
640
|
setDevtoolsOpen(!devtoolsOpen);
|
|
585
641
|
}
|
|
586
642
|
}}
|
|
587
|
-
className={`text-xs px-2 py-1 rounded border transition-colors ${
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
643
|
+
className={`text-xs px-2 py-1 rounded border transition-colors ${
|
|
644
|
+
devtoolsOpen
|
|
645
|
+
? "border-blue-600 text-blue-400 bg-blue-950/30"
|
|
646
|
+
: "border-zinc-700 hover:border-zinc-500 text-zinc-500 hover:text-zinc-300"
|
|
647
|
+
}`}
|
|
591
648
|
title="Toggle DevTools"
|
|
592
649
|
>
|
|
593
650
|
DevTools
|
|
@@ -604,8 +661,12 @@ export default function SessionView({
|
|
|
604
661
|
</>
|
|
605
662
|
) : hasStarted ? (
|
|
606
663
|
<>
|
|
607
|
-
<span
|
|
608
|
-
|
|
664
|
+
<span
|
|
665
|
+
className={`inline-block w-2 h-2 rounded-full shrink-0 ${paused ? "bg-amber-500" : "bg-blue-500 animate-pulse"}`}
|
|
666
|
+
/>
|
|
667
|
+
<span className="text-xs text-zinc-400">
|
|
668
|
+
{paused ? "Paused" : "Working"} {formatTime(elapsedSeconds)}
|
|
669
|
+
</span>
|
|
609
670
|
<div className="flex-1" />
|
|
610
671
|
<button
|
|
611
672
|
onClick={() => {
|
|
@@ -616,10 +677,11 @@ export default function SessionView({
|
|
|
616
677
|
setDevtoolsOpen(!devtoolsOpen);
|
|
617
678
|
}
|
|
618
679
|
}}
|
|
619
|
-
className={`text-xs px-2 py-1 rounded border transition-colors ${
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
680
|
+
className={`text-xs px-2 py-1 rounded border transition-colors ${
|
|
681
|
+
devtoolsOpen
|
|
682
|
+
? "border-blue-600 text-blue-400 bg-blue-950/30"
|
|
683
|
+
: "border-zinc-700 hover:border-zinc-500 text-zinc-500 hover:text-zinc-300"
|
|
684
|
+
}`}
|
|
623
685
|
title="Toggle DevTools"
|
|
624
686
|
>
|
|
625
687
|
DevTools
|
|
@@ -695,30 +757,41 @@ function ToolCallView({ part }: { part: any }) {
|
|
|
695
757
|
const isRunning = state === "input-streaming" || state === "input-available";
|
|
696
758
|
const toolName = part.toolName || (typeof part.type === "string" ? part.type.replace(/^tool-/, "") : "");
|
|
697
759
|
|
|
698
|
-
const command =
|
|
699
|
-
|
|
700
|
-
?
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
760
|
+
const command =
|
|
761
|
+
state === "input-streaming"
|
|
762
|
+
? ""
|
|
763
|
+
: toolName === "browser"
|
|
764
|
+
? (() => {
|
|
765
|
+
const parts = (input.actions || []).map((a: any) => {
|
|
766
|
+
switch (a.action) {
|
|
767
|
+
case "navigate":
|
|
768
|
+
return `navigate ${a.url || ""}`;
|
|
769
|
+
case "mouse": {
|
|
770
|
+
const summary = (a.mouseActions || [])
|
|
771
|
+
.map((s: any) =>
|
|
772
|
+
s.action === "wait"
|
|
773
|
+
? `wait ${s.ms}ms`
|
|
774
|
+
: s.action === "wheel"
|
|
775
|
+
? `wheel ${s.ref || `${s.x},${s.y}`}`
|
|
776
|
+
: `${s.action} ${s.ref || `${s.x},${s.y}`}`,
|
|
777
|
+
)
|
|
778
|
+
.join(" → ");
|
|
779
|
+
return `mouse ${summary}`;
|
|
780
|
+
}
|
|
781
|
+
case "type":
|
|
782
|
+
return `type "${(a.text || "").slice(0, 40)}"`;
|
|
783
|
+
case "press":
|
|
784
|
+
return `press ${a.key || ""}`;
|
|
785
|
+
case "tabs":
|
|
786
|
+
return `tabs ${a.tabAction || ""}${a.tabId ? " " + a.tabId : ""}`;
|
|
787
|
+
default:
|
|
788
|
+
return a.action || "";
|
|
789
|
+
}
|
|
790
|
+
});
|
|
791
|
+
if (input.observe) parts.push(input.observe);
|
|
792
|
+
return parts.join(", ");
|
|
793
|
+
})()
|
|
794
|
+
: input.command || (Object.keys(input).length > 0 ? JSON.stringify(input) : "");
|
|
722
795
|
|
|
723
796
|
return (
|
|
724
797
|
<div className="text-sm">
|
|
@@ -738,30 +811,44 @@ function ToolCallView({ part }: { part: any }) {
|
|
|
738
811
|
</button>
|
|
739
812
|
{expanded && (
|
|
740
813
|
<pre className="mt-1 px-3 py-2 bg-zinc-900/50 rounded-lg border border-zinc-800 text-xs whitespace-pre-wrap overflow-x-auto max-h-60 overflow-y-auto">
|
|
741
|
-
{command &&
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
814
|
+
{command && (
|
|
815
|
+
<>
|
|
816
|
+
<span className="text-zinc-300 select-all">$ {command}</span>
|
|
817
|
+
{"\n"}
|
|
818
|
+
</>
|
|
819
|
+
)}
|
|
820
|
+
{result != null &&
|
|
821
|
+
(() => {
|
|
822
|
+
if (toolName === "browser" && typeof result === "string") {
|
|
823
|
+
try {
|
|
824
|
+
const parsed = JSON.parse(result);
|
|
825
|
+
if (parsed.textOutput !== undefined) {
|
|
826
|
+
return (
|
|
827
|
+
<>
|
|
828
|
+
<span className="text-zinc-500">{parsed.textOutput}</span>
|
|
829
|
+
{parsed.imageOutput && (
|
|
830
|
+
<>
|
|
831
|
+
{"\n"}
|
|
832
|
+
<span className={parsed.imageOutput.type === "changed" ? "text-blue-400" : "text-zinc-600"}>
|
|
833
|
+
{parsed.imageOutput.type === "changed"
|
|
834
|
+
? "[screenshot: changed]"
|
|
835
|
+
: "[screenshot: unchanged]"}
|
|
836
|
+
</span>
|
|
837
|
+
</>
|
|
838
|
+
)}
|
|
839
|
+
</>
|
|
840
|
+
);
|
|
841
|
+
}
|
|
842
|
+
} catch (e) {
|
|
843
|
+
console.error(e);
|
|
760
844
|
}
|
|
761
|
-
}
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
845
|
+
}
|
|
846
|
+
return (
|
|
847
|
+
<span className="text-zinc-500">
|
|
848
|
+
{typeof result === "string" ? result : JSON.stringify(result, null, 2)}
|
|
849
|
+
</span>
|
|
850
|
+
);
|
|
851
|
+
})()}
|
|
765
852
|
</pre>
|
|
766
853
|
)}
|
|
767
854
|
</div>
|