diff --git a/browser_extension/README.md b/browser_extension/README.md
index a8aef0dcf2..67ba034e7a 100644
--- a/browser_extension/README.md
+++ b/browser_extension/README.md
@@ -27,7 +27,7 @@ icon to open the panel.
- Only the active tab of the panel's window is read or acted on.
- Reading and screenshots run without asking. Each click, type and navigation waits for the
chat's own approval card, which lives in Windmill's frame where the page cannot reach it.
- YOLO auto-approval is unavailable in the panel.
+ The chat's Yolo mode skips the card, and its warning says pages can try to steer it.
- Approval runs in two steps. When the card opens, the extension pins the target (tab,
document and element, the element held in the extension's isolated world) and describes
it for the card. After approval it acts only on that target, and refuses if the tab is no
diff --git a/browser_extension/package.json b/browser_extension/package.json
index 9a851c2bdb..436cefd554 100644
--- a/browser_extension/package.json
+++ b/browser_extension/package.json
@@ -3,7 +3,7 @@
"private": true,
"version": "0.1.0",
"scripts": {
- "build": "rm -rf dist && tsc && cp static/* dist/"
+ "build": "rm -rf dist && tsc && cp -r static/. dist/"
},
"devDependencies": {
"@types/chrome": "^0.3.4",
diff --git a/browser_extension/src/panel.ts b/browser_extension/src/panel.ts
index 0d15e1ebf1..5f3f0608d3 100644
--- a/browser_extension/src/panel.ts
+++ b/browser_extension/src/panel.ts
@@ -210,6 +210,7 @@ function pageAction(
el.innerText ||
input.placeholder ||
(input.type === 'submit' || input.type === 'button' ? input.value : '') ||
+ el.title ||
input.name ||
''
)
diff --git a/browser_extension/static/icons/icon128.png b/browser_extension/static/icons/icon128.png
new file mode 100644
index 0000000000..8453ee5e8c
Binary files /dev/null and b/browser_extension/static/icons/icon128.png differ
diff --git a/browser_extension/static/icons/icon16.png b/browser_extension/static/icons/icon16.png
new file mode 100644
index 0000000000..a68832bfd8
Binary files /dev/null and b/browser_extension/static/icons/icon16.png differ
diff --git a/browser_extension/static/icons/icon32.png b/browser_extension/static/icons/icon32.png
new file mode 100644
index 0000000000..8febd12a3e
Binary files /dev/null and b/browser_extension/static/icons/icon32.png differ
diff --git a/browser_extension/static/icons/icon48.png b/browser_extension/static/icons/icon48.png
new file mode 100644
index 0000000000..2ce98ef753
Binary files /dev/null and b/browser_extension/static/icons/icon48.png differ
diff --git a/browser_extension/static/manifest.json b/browser_extension/static/manifest.json
index 8149c16370..752ac3b7ad 100644
--- a/browser_extension/static/manifest.json
+++ b/browser_extension/static/manifest.json
@@ -5,8 +5,25 @@
"description": "Windmill AI sessions in the side panel, with tools that act on the active tab.",
"permissions": ["sidePanel", "storage", "scripting", "tabs"],
"host_permissions": [""],
- "background": { "service_worker": "background.js", "type": "module" },
- "action": { "default_title": "Open Windmill" },
- "side_panel": { "default_path": "panel.html" },
- "options_page": "options.html"
+ "background": {
+ "service_worker": "background.js",
+ "type": "module"
+ },
+ "action": {
+ "default_title": "Open Windmill",
+ "default_icon": {
+ "16": "icons/icon16.png",
+ "32": "icons/icon32.png"
+ }
+ },
+ "side_panel": {
+ "default_path": "panel.html"
+ },
+ "options_page": "options.html",
+ "icons": {
+ "16": "icons/icon16.png",
+ "32": "icons/icon32.png",
+ "48": "icons/icon48.png",
+ "128": "icons/icon128.png"
+ }
}
diff --git a/frontend/src/lib/components/copilot/chat/AIChatDisplay.svelte b/frontend/src/lib/components/copilot/chat/AIChatDisplay.svelte
index d5406aeca2..1254e19be6 100644
--- a/frontend/src/lib/components/copilot/chat/AIChatDisplay.svelte
+++ b/frontend/src/lib/components/copilot/chat/AIChatDisplay.svelte
@@ -1277,6 +1277,12 @@ the panel, or the Escape-to-stop focus check would wrongly reject them. -->
? 'This can result in edits being applied or tools being called without user confirmation.'
: 'This can result in tools being called without user confirmation.'}
+ {#if chatHost.browserTools}
+
+ That includes clicking, typing and navigating on your page, and a page you
+ visit can try to steer the assistant into doing so.
+
+ {/if}
{#if yoloBypassedTools.length > 0}
Bypassed in current mode:
diff --git a/frontend/src/lib/components/copilot/chat/AIChatManager.svelte.ts b/frontend/src/lib/components/copilot/chat/AIChatManager.svelte.ts
index dd49970051..bc9a8e986d 100644
--- a/frontend/src/lib/components/copilot/chat/AIChatManager.svelte.ts
+++ b/frontend/src/lib/components/copilot/chat/AIChatManager.svelte.ts
@@ -737,10 +737,7 @@ export class AIChatManager implements ChatViewHost {
(this.autonomyMode === AIAutonomyMode.ACCEPT_EDIT ||
this.autonomyMode === AIAutonomyMode.YOLO)
)
- // Never in the side panel: page content in context could steer an unconfirmed workspace tool.
- autoAcceptToolConfirmationsAvailable = $derived(
- !this.browserTools && supportsAutoAcceptToolConfirmations(this.mode)
- )
+ autoAcceptToolConfirmationsAvailable = $derived(supportsAutoAcceptToolConfirmations(this.mode))
autoAcceptToolConfirmationsActive = $derived(
this.autonomyMode === AIAutonomyMode.YOLO && this.autoAcceptToolConfirmationsAvailable
)
diff --git a/frontend/src/lib/components/copilot/chat/chatViewHost.ts b/frontend/src/lib/components/copilot/chat/chatViewHost.ts
index bdacb57615..b9712c1a6f 100644
--- a/frontend/src/lib/components/copilot/chat/chatViewHost.ts
+++ b/frontend/src/lib/components/copilot/chat/chatViewHost.ts
@@ -157,6 +157,8 @@ export interface ChatViewHost {
readonly autoAcceptEditsAvailable: boolean
readonly autoAcceptToolConfirmationsAvailable: boolean
readonly planModeAvailable: boolean
+ /** Hosted in the browser extension's side panel, with tools acting on the user's tab. */
+ readonly browserTools?: boolean
attachedFiles: AttachedFilesStore
artifacts: SessionArtifactsStore
openArtifact?: (artifactId: string, name: string, version?: ArtifactVersionTarget) => void
diff --git a/frontend/src/lib/components/copilot/chat/global/browserTools.ts b/frontend/src/lib/components/copilot/chat/global/browserTools.ts
index 4452653b6f..716f8976b2 100644
--- a/frontend/src/lib/components/copilot/chat/global/browserTools.ts
+++ b/frontend/src/lib/components/copilot/chat/global/browserTools.ts
@@ -18,8 +18,9 @@ type Action = 'click' | 'type' | 'navigate'
let parentOrigin: string | undefined
let connection: Promise | undefined
const pending = new Map void; reject: (e: Error) => void }>()
-/** By tool call: the extension's description of the target, made when the approval card opens. */
-const prepared = new Map>()
+/** By the call's own `args` object, the one object the validation, the approval card and the
+ * tool all receive: the target the extension pinned and described before the card opened. */
+const prepared = new WeakMap