Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion knip.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"project": ["**/*.ts"]
},
"webview-ui": {
"entry": ["src/index.tsx"],
"entry": ["src/index.tsx", "src/browser-panel.tsx"],
"project": ["src/**/*.{ts,tsx}", "../src/shared/*.ts"]
},
"packages/{build,cloud,evals,ipc,telemetry,types}": {
Expand Down
1 change: 1 addition & 0 deletions packages/types/src/message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ export const clineSays = [
"shell_integration_warning",
"browser_action",
"browser_action_result",
"browser_session_status",
"mcp_server_request_started",
"mcp_server_response",
"subtask_result",
Expand Down
28 changes: 26 additions & 2 deletions src/core/assistant-message/presentAssistantMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -352,8 +352,32 @@ export async function presentAssistantMessage(cline: Task) {
return text.replace(tagRegex, "")
}

if (block.name !== "browser_action") {
await cline.browserSession.closeBrowser()
// Keep browser open during an active session so other tools can run.
// Session is active if we've seen any browser_action_result and the last browser_action is not "close".
try {
const messages = cline.clineMessages || []
const hasStarted = messages.some((m: any) => m.say === "browser_action_result")
let isClosed = false
for (let i = messages.length - 1; i >= 0; i--) {
const m = messages[i]
if (m.say === "browser_action") {
try {
const act = JSON.parse(m.text || "{}")
isClosed = act.action === "close"
} catch {}
break
}
}
const sessionActive = hasStarted && !isClosed
// Only auto-close when no active browser session is present, and this isn't a browser_action
if (!sessionActive && block.name !== "browser_action") {
await cline.browserSession.closeBrowser()
}
} catch {
// On any unexpected error, fall back to conservative behavior
if (block.name !== "browser_action") {
await cline.browserSession.closeBrowser()
}
}

if (!block.partial) {
Expand Down
17 changes: 17 additions & 0 deletions src/core/environment/__tests__/getEnvironmentDetails.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,9 @@ describe("getEnvironmentDetails", () => {
deref: vi.fn().mockReturnValue(mockProvider),
[Symbol.toStringTag]: "WeakRef",
} as unknown as WeakRef<ClineProvider>,
browserSession: {
isSessionActive: vi.fn().mockReturnValue(false),
} as any,
}

// Mock other dependencies.
Expand Down Expand Up @@ -390,4 +393,18 @@ describe("getEnvironmentDetails", () => {
const result = await getEnvironmentDetails(cline as Task)
expect(result).toContain("REMINDERS")
})
it("should include Browser Session Status when inactive", async () => {
const result = await getEnvironmentDetails(mockCline as Task)
expect(result).toContain("# Browser Session Status")
expect(result).toContain("Inactive - Browser is not launched")
})

it("should include Browser Session Status with current viewport when active", async () => {
;(mockCline.browserSession as any).isSessionActive = vi.fn().mockReturnValue(true)
;(mockCline.browserSession as any).getViewportSize = vi.fn().mockReturnValue({ width: 1280, height: 720 })

const result = await getEnvironmentDetails(mockCline as Task)
expect(result).toContain("Active - A browser session is currently open and ready for browser_action commands")
expect(result).toContain("Current viewport size: 1280x720 pixels.")
})
})
32 changes: 32 additions & 0 deletions src/core/environment/getEnvironmentDetails.ts
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,38 @@ export async function getEnvironmentDetails(cline: Task, includeFileDetails: boo
}
}

// Add browser session status - Always show to prevent LLM from trying browser actions when no session is active
const isBrowserActive = cline.browserSession.isSessionActive()

// Build viewport info for status (prefer actual viewport if available, else fallback to configured setting)
const configuredViewport = (state?.browserViewportSize as string | undefined) ?? "900x600"
let configuredWidth: number | undefined
let configuredHeight: number | undefined
if (configuredViewport.includes("x")) {
const parts = configuredViewport.split("x").map((v) => Number(v))
configuredWidth = parts[0]
configuredHeight = parts[1]
}

let actualWidth: number | undefined
let actualHeight: number | undefined
// Use optional chaining to avoid issues with tests that stub browserSession
const vp = isBrowserActive ? (cline.browserSession as any).getViewportSize?.() : undefined
if (vp) {
actualWidth = vp.width
actualHeight = vp.height
}

const width = actualWidth ?? configuredWidth
const height = actualHeight ?? configuredHeight
const viewportInfo = isBrowserActive && width && height ? `\nCurrent viewport size: ${width}x${height} pixels.` : ""

details += `\n# Browser Session Status\n${
isBrowserActive
? "Active - A browser session is currently open and ready for browser_action commands"
: "Inactive - Browser is not launched. Using any browser action except the browser_action with action='launch' to start a new session will result in an error."
}${viewportInfo}\n`

if (includeFileDetails) {
details += `\n\n# Current Workspace Directory (${cline.cwd.toPosix()}) Files\n`
const isDesktop = arePathsEqual(cline.cwd, path.join(os.homedir(), "Desktop"))
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 1 addition & 5 deletions src/core/prompts/sections/rules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,5 @@ ${getEditingInstructions(diffStrategy)}
- At the end of each user message, you will automatically receive environment_details. This information is not written by the user themselves, but is auto-generated to provide potentially relevant context about the project structure and environment. While this information can be valuable for understanding the project context, do not treat it as a direct part of the user's request or response. Use it to inform your actions and decisions, but don't assume the user is explicitly asking about or referring to this information unless they clearly do so in their message. When using environment_details, explain your actions clearly to ensure the user understands, as they may not be aware of these details.
- Before executing commands, check the "Actively Running Terminals" section in environment_details. If present, consider how these active processes might impact your task. For example, if a local development server is already running, you wouldn't need to start it again. If no active terminals are listed, proceed with command execution as normal.
- MCP operations should be used one at a time, similar to other tool usage. Wait for confirmation of success before proceeding with additional operations.
- It is critical you wait for the user's response after each tool use, in order to confirm the success of the tool use. For example, if asked to make a todo app, you would create a file, wait for the user's response it was created successfully, then create another file if needed, wait for the user's response it was created successfully, etc.${
supportsComputerUse
? " Then if you want to test your work, you might use browser_action to launch the site, wait for the user's response confirming the site was launched along with a screenshot, then perhaps e.g., click a button to test functionality if needed, wait for the user's response confirming the button was clicked along with a screenshot of the new state, before finally closing the browser."
: ""
}`
- It is critical you wait for the user's response after each tool use, in order to confirm the success of the tool use. For example, if asked to make a todo app, you would create a file, wait for the user's response it was created successfully, then create another file if needed, wait for the user's response it was created successfully, etc.`
}
Loading