Add multi-agent browser tab isolation (Phase 1 MVP)#1
Open
Conversation
Introduce BrowserAgentSession system that enables multiple AI agents to share imported Chrome profiles while maintaining cookie isolation via clone-on-first-use WKWebsiteDataStore cloning. New files: - BrowserAgentSession.swift: session struct (agent UUID, source profile, data store ID) - BrowserAgentSessionStore.swift: session lifecycle management with Task-based clone serialization, on-disk manifest for crash-recovery GC, and WKWebsiteDataStore.remove(forIdentifier:) cleanup Modified: - BrowserPanel: agentSessionId property, agent data store in init, block switchToProfile() on agent-owned panels - SessionPersistence: agentSessionId field (agent panels discarded on restore) - Workspace: newBrowserSurface gains agentSessionId/agentDataStoreId params - TerminalController: 4 new socket commands: - browser.agent_session.open: create session with cookie clone - browser.agent_session.tab: add tab to existing session - browser.agent_session.dispose: close tabs + remove data store - browser.agent_session.list: enumerate sessions Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Address all must-fix issues from 3-agent code review: - Fix caller identity: verify surface UUID exists in live workspace before trusting params['caller']['surface_id'] - Fix session snapshot: include agentSessionId in persistence - Fix session restore: skip agent-owned panels on app restart - Fix degenerate session: getOrCreate returns nil at capacity instead of untracked orphan session - Fix session limit: remove premature guard that blocked reuse of existing sessions at capacity - Fix list command: wrap in v2MainSync, restrict to caller's own sessions by default - Wire GC: call garbageCollect() on app launch in AppDelegate - Wire disconnect: track agent surfaces per connection, call handleAgentDisconnect on socket close - Implement tabCount: enumerate workspace panels for real count Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Restrict browser.agent_session.list to caller's own sessions only; explicit agent_surface_id filter must match caller UUID - Move disconnect handler tracking after processCommand succeeds and verify method == "browser.agent_session.open" via parsed JSON (prevents string-injection DoS) - Update tabCount documentation Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
cmux browser agent-session open --profile <uuid> [--url <url>] cmux browser agent-session tab --session <uuid> [--url <url>] cmux browser agent-session dispose --session <uuid> cmux browser agent-session list [--json] Caller context (CMUX_SURFACE_ID, CMUX_WORKSPACE_ID) is automatically included from environment variables for agent identity resolution. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- WKProcessPoolCookieIsolationTests: verify cookies set in one WKWebsiteDataStore are NOT visible in another sharing the same WKProcessPool (foundation of agent session isolation) - BrowserAgentSessionStoreTests: CRUD, dedup, capacity limit, disconnect cleanup - BrowserPanelAgentOwnershipTests: switchToProfile blocked on agent-owned panels Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- rb-001: Only track agent surfaces for disconnect cleanup after system.identify (long-lived connections), not one-shot CLI commands - rb-002: Preserve agent cookie isolation in reattachToWorkspace by keeping existing websiteDataStore when agentSessionId is set - rb-003: Dispose iterates ALL windows to close agent panels, not just the resolved TabManager's window - rb-004: handleAgentDisconnect closes orphaned BrowserPanel tabs across all windows before disposing sessions - rb-006: Add injectable manifestURL to BrowserAgentSessionStore; tests use temp directory instead of production path Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- rr-001: Dispose session on panel creation failure paths to prevent orphaned sessions consuming slots - rr-002: Count cloneInFlight toward maxConcurrentSessions to prevent concurrent opens from exceeding the limit - rr-003: Cancel matching cloneInFlight tasks on dispose; check Task.isCancelled before appending to prevent zombie sessions - rr-004: Hoist ISO8601DateFormatter outside .map closure Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The browser subcommand parser was treating "agent-session" as a surface handle instead of a verb, causing "Unsupported browser subcommand" errors. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Multi-agent browser tab isolation (Phase 1 MVP) — enables multiple AI agents to share imported Chrome profiles while maintaining complete cookie isolation via clone-on-first-use WKWebsiteDataStore cloning.
What's included
browser.agent_session.open/.tab/.dispose/.listcmux browser agent-session open|tab|dispose|listArchitecture
E2E Verification
Cookie isolation verified with httpbin.org:
agent1_token=secret123→ only visible in Agent 1's sessionagent2_token=xyz789→ only visible in Agent 2's sessionAgent 1 cookies (only agent1_token)
Agent 2 cookies (only agent2_token)
Overview — both agents side by side
Review History
2 rounds of 3-agent (Codex + Claude + Cursor Agent) review board:
Test plan
cmux browser agent-session open→ set cookies → verify isolation →disposegh workflow run test-e2e.yml🤖 Generated with Claude Code