agent-browser: frontend-agnostic realm-cached auto-login + per-session isolation (WIP)#2355
Open
yardenhochman wants to merge 14 commits into
Open
agent-browser: frontend-agnostic realm-cached auto-login + per-session isolation (WIP)#2355yardenhochman wants to merge 14 commits into
yardenhochman wants to merge 14 commits into
Conversation
…er U9) Frontend-agnostic adapter contract so each consuming frontend declares its envs/realms/auth-state-machine/token-contract in one config; the browse engine carries no app-specific auth knowledge. Enables isolated per-session browser + realm-cached auto-login for agent sessions. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ache (agent-browser U1/U2) Direct-POST auth state machine driven by the frontend adapter (no form-driving, no in-browser biometric). Caches the validated auth object under the frontend repo's .auth/ keyed by (realm,userName), chmod 600; realm-sharing envs reuse one token. Expiry from the JWT exp claim, falling back to defaultTtlMs. Credentials and TOTP flow through injectable providers so the state machine is unit-tested without live calls or real secrets. Never logs password/OTP/bearer. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Pre-server subcommand that acquires-or-reuses a cached bearer for a frontend env via the repo's .agent-browser.config.mjs adapter. Runs CLI-side so op/Touch-ID prompt in the user's terminal, not the daemon. Emits a machine-parseable, secret-free summary (ENV/REALM/APPORIGIN/EXPIRES/CACHED). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Merging to
After your PR is submitted to the merge queue, this comment will be automatically updated with its status. If the PR fails, failure details will also be posted here |
…ion extraction (agent-browser U4) Pins tab-ownership + global-buffer semantics that the default/legacy session must reproduce unchanged after the isolation refactor. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…cade (agent-browser U4) BrowserManager holds Map<sessionId,SessionState> + the shared Browser. Per-tab / per-context fields (context, pages, tabs, headers, UA, viewport, dialog policy, ownership, guardrails, and the three observability buffers) become private accessors into the current session's bundle, so existing method bodies operate on the active session unchanged. A lazily-created default session reproduces pre-U4 single-session behavior for callers that send no session. wirePageEvents binds each page's async listeners to the session that OWNED the page at wire-time, so console/network/dialog events never cross-contaminate when the active session switches. Adds ensureSession/closeSession/getBuffers/ getAllSessions. read-commands resolves buffers via bm.getBuffers(). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…agent-browser U4) handleCommandInternal (the choke point every caller passes through) resolves an optional `session` field to an isolated context via ensureSession before the command runs and restores the prior session in a finally, so a subsequent no-session command lands on the default. Chain subcommands inherit the pinned session; batch forwards session per subcommand. flushBuffers iterates every session with per-session cursors, tagging non-default lines by session id. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…nt-browser U4) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…-auth command (agent-browser U5) injectOriginScopedStorage adds a per-session addInitScript that writes localStorage[key] ONLY when location.origin === appOrigin, so a bearer never leaks to a non-app origin (R5). Injections are recorded on the session and re-applied on context recreation (viewport/scale) so auth isn't silently dropped. Exposed as the `inject-auth <appOrigin> <storageKey> <authJson>` write command, running within the routed session. Payload must be a non-empty JSON object; adapter-level shape validation already happened at acquire. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… (agent-browser U5) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…sion buffers (agent-browser U4) The shared test wrapper dropped the browserManager arg; console/network/dialog reads now resolve buffers per-session via bm.getBuffers(), so the wrapper must forward it. Buffer-seeding in the console-errors suite targets the session buffer. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
env [path] -> isolated headless session with a cached-or-acquired token injected origin-scoped, then navigated. Acquires CLI-side (op/Touch-ID in the terminal), auto-generates a hidden session id (<user>-<worktree>-<shortid>; --session overrides), ensures the daemon, then POSTs inject-auth + goto with the session. Emits a parseable, secret-free summary (SESSION/ENV/REALM/URL/EXPIRES/CACHED). --headed is deferred to Phase 2. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…owser U7) The zombie-lock bug: a live Chromium holding the profile SingletonLock, owned such that process.kill(pid,0) returns EPERM, was misread as dead so cleanup skipped it. EPERM = exists-but-unsignalable = alive; only ESRCH is dead. The full headed profile-per-session + reaper rig remains Phase-2 follow-up. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…nt-browser U8) trace start / trace stop <path> record a per-session Playwright trace on the session's context; show-trace <path> opens it in Playwright's viewer (CLI-side, no daemon). Completes the mode-map's 'watch what happened' path without a live headed browser. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…rsing --print emits the bare bearer on stdout (secret-free summary moves to stderr) so shell capture like BACKEND_JWT=$(browse agent-token <env> --print) works; default output stays summary-only with no bearer. agent-open no longer treats the --session flag's value as a positional nav path. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
yardenhochman
marked this pull request as ready for review
July 26, 2026 11:19
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.
What
Adds an agent-browser capability to
browse: each agent session gets a fast,isolated browser that is already logged in to the right frontend env — no
shared tabs, no profile-lock deadlocks, no login form.
The engine is frontend-agnostic: each consuming frontend ships one
.agent-browser.config.mjsadapter (envs, realm groupings, auth state machine,token contract). No app-specific auth lives in the engine — adding a frontend is
config-only.
Shipped in this PR
Auto-login (realm-cached token)
frontend-adapter.ts— adapter schema + loader/validator.token-manager.ts— direct-POST auth state machine (no form-driving) + cachekeyed by
(realm, user), chmod 600, expiry from the JWTexp(fallback TTL),injectable creds/TOTP providers for unit testing without live calls or secrets.
browse agent-token <env>— pre-server CLI command; op/Touch-ID prompt in theuser's terminal, secret-free
ENV/REALM/APPORIGIN/EXPIRES/CACHEDsummary.Per-session isolation
SessionStatefacade overBrowserManager: one sharedBrowser+Map<sessionId, SessionState>; per-session context/pages/headers/UA/viewport/dialog/ownership/buffers, exposed as accessors so existing method bodies are
untouched. Lazy
defaultsession preserves all pre-existing callers.cross-contaminate when the active session switches.
handleCommandInternalchoke point (+ batch/chain);per-session log flush.
inject-auth— origin-scopedlocalStorageinjection (writes only whenlocation.origin === appOrigin; re-applied on context recreation).browse agent-open <env> [path]— end-to-end: cached-or-acquired token,isolated session, origin-scoped inject, navigate.
trace start|stop+browse show-trace— per-session Playwright tracing.isProcessAlivenow treatsEPERMas alive (was the zombie-lock bug: a liveChromium holding
SingletonLockmisread as dead and skipped by cleanup).Deferred (follow-up, per plan Scope Boundaries)
Full headed profile-per-session + hardened reaper rig, and the
agent-browser initnew-frontend scaffolder (auth-contract auto-detect + realm probe).
Tests
bun test browse/test/green (frontend-adapter, token-acquire, token-cache,session-characterization, session-isolation, inject-auth, tracing,
is-process-alive-eperm + full existing suite). Auth contract locked against a live
backend first (no CSRF/cookie; JWT-
explifetime; step-1{mfaRequired:true}).Context isolation, origin-scoped no-leak, and tracing verified live.
🤖 Generated with Claude Code