@@ -19,6 +19,7 @@ import type { ToolPart } from "@opencode-ai/sdk/v2"
1919import type * as Tool from "@/tool/tool"
2020import type { ApplyPatchTool } from "@/tool/apply_patch"
2121import type { ShellTool as BashTool } from "@/tool/shell"
22+ import type { BrowserExecuteTool } from "@/tool/browser-execute"
2223import type { EditTool } from "@/tool/edit"
2324import type { GlobTool } from "@/tool/glob"
2425import type { GrepTool } from "@/tool/grep"
@@ -94,6 +95,7 @@ type ToolPermissionCtx = {
9495type ToolDefs = {
9596 invalid : typeof InvalidTool
9697 bash : typeof BashTool
98+ browser_execute : typeof BrowserExecuteTool
9799 write : typeof WriteTool
98100 edit : typeof EditTool
99101 apply_patch : typeof ApplyPatchTool
@@ -682,6 +684,40 @@ function scrollBashFinal(p: ToolProps<typeof BashTool>): string {
682684 return `bash completed (exit ${ code } )${ time ? ` · ${ time } ` : "" } `
683685}
684686
687+ // Mimic a JS REPL: "> " on the first line, " " on continuations. Skip
688+ // leading/trailing blank lines so a snippet that starts with "\n" doesn't
689+ // render an empty "> " row. Matches the TUI BrowserExecute renderer.
690+ function promptedCode ( code : string ) : string {
691+ const src = code . replace ( / ^ \n + | \n + $ / g, "" )
692+ if ( ! src ) {
693+ return ""
694+ }
695+
696+ return src
697+ . split ( "\n" )
698+ . map ( ( line , i ) => ( i === 0 ? "> " : " " ) + line )
699+ . join ( "\n" )
700+ }
701+
702+ function scrollBrowserExecuteStart ( p : ToolProps < typeof BrowserExecuteTool > ) : string {
703+ const desc = p . input . description || "Browser execute"
704+ const code = promptedCode ( p . input . code ?? "" )
705+ if ( ! code ) {
706+ return `# ${ desc } `
707+ }
708+
709+ return `# ${ desc } \n${ code } `
710+ }
711+
712+ function scrollBrowserExecuteProgress ( p : ToolProps < typeof BrowserExecuteTool > ) : string {
713+ return stripAnsi ( p . frame . raw ) . replace ( / ^ \n + / , "" ) . replace ( / \n + $ / , "" )
714+ }
715+
716+ function scrollBrowserExecuteFinal ( p : ToolProps < typeof BrowserExecuteTool > ) : string {
717+ const time = span ( p . frame . state )
718+ return time ? `browser_execute completed · ${ time } ` : "browser_execute completed"
719+ }
720+
685721function scrollReadStart ( p : ToolProps < typeof ReadTool > ) : string {
686722 const file = toolPath ( p . input . filePath )
687723 const extra = info ( p . frame . input , [ "filePath" ] )
@@ -973,6 +1009,16 @@ function permBash(p: ToolPermissionProps<typeof BashTool>): ToolPermissionInfo {
9731009 }
9741010}
9751011
1012+ function permBrowserExecute ( p : ToolPermissionProps < typeof BrowserExecuteTool > ) : ToolPermissionInfo {
1013+ const title = p . input . description || "Browser execute"
1014+ const code = promptedCode ( p . input . code ?? "" )
1015+ return {
1016+ icon : "#" ,
1017+ title,
1018+ lines : code ? code . split ( "\n" ) : p . patterns . map ( ( item ) => `- ${ item } ` ) ,
1019+ }
1020+ }
1021+
9761022function permTask ( p : ToolPermissionProps < typeof TaskTool > ) : ToolPermissionInfo {
9771023 const type = p . input . subagent_type || "general"
9781024 const desc = p . input . description
@@ -1042,6 +1088,19 @@ const TOOL_RULES = {
10421088 } ,
10431089 permission : permBash ,
10441090 } ,
1091+ browser_execute : {
1092+ view : {
1093+ output : true ,
1094+ final : false ,
1095+ } ,
1096+ run : runBrowserExecute ,
1097+ scroll : {
1098+ start : scrollBrowserExecuteStart ,
1099+ progress : scrollBrowserExecuteProgress ,
1100+ final : scrollBrowserExecuteFinal ,
1101+ } ,
1102+ permission : permBrowserExecute ,
1103+ } ,
10451104 write : {
10461105 view : {
10471106 output : false ,
@@ -1277,6 +1336,15 @@ function runBash(p: ToolProps<typeof BashTool>): ToolInline {
12771336 }
12781337}
12791338
1339+ function runBrowserExecute ( p : ToolProps < typeof BrowserExecuteTool > ) : ToolInline {
1340+ return {
1341+ icon : ">" ,
1342+ title : p . input . description || "Browser execute" ,
1343+ mode : "block" ,
1344+ body : p . frame . status === "completed" ? text ( p . frame . state . output ) . trim ( ) : undefined ,
1345+ }
1346+ }
1347+
12801348export function toolView ( name ?: string ) : ToolView {
12811349 return (
12821350 rule ( name ) ?. view ?? {
0 commit comments