LocalShell Web Supervisor (LWS)

August 23, 2026 ยท View on GitHub

LocalShell Web Supervisor is a local reliability and orchestration layer for long-running browser-driven agents that use Local Shell MCP for durable local execution.

The central rule is simple:

A web conversation is a replaceable worker lease. The durable task is not the conversation.

A browser turn, a Local Shell tool call, and the actual Git/workspace state can fail or finish independently. LWS keeps those truth domains separate so a stalled or interrupted web turn does not automatically cause a duplicate commit, write, upload, or retry.

What LWS does

LWS provides a fail-closed control plane around browser-based agent work:

  • durable SQLite task and worker registry;
  • Local Shell MCP logical-session, Goal, in-flight-call, continuation, and tracked-job reconciliation;
  • read-only workspace/Git observation;
  • browser/DOM observation and exact-window Windows UI Automation fencing;
  • optional CDP network-lifecycle evidence without retaining headers, cookies, request bodies, or response bodies;
  • deterministic health/stall classification across browser, Local Shell, and workspace evidence;
  • write-ahead recovery actions with replay suppression;
  • worker generations, leases, handoff, takeover, supersession, and durable completion;
  • parent/child task scheduling with persisted prompts and worktree metadata;
  • explicitly gated initial child-conversation creation in a confirmed web project;
  • guarded replacement-worker takeover coordinated with supported Local Shell MCP session takeover;
  • low-noise resident watchdog support for narrowly recognized delivery failures and an opt-in 25m20 hard-overrun continuation rule;
  • browser-memory and active/park/probe planning.

LWS is deliberately conservative. Unknown external outcomes are reconciled, not replayed.

Provider boundary

The task, Local Shell, Git, worker-protocol, scheduler, and recovery layers are provider-neutral. The current browser adapter is tested against chatgpt.com in normal Chrome and therefore contains a small amount of provider-specific URL and accessibility logic.

That adapter does not reconstruct private service endpoints, copy authentication material, or use browser cookies/tokens as an execution API. Provider-specific browser behavior is treated as an observation/mutation adapter behind the durable control plane.

Safety model

LWS is reliability infrastructure for work the user already authorized. It is not an authentication bypass or a private web-client implementation.

Core invariants:

  • never infer completion from a Send/Stop button alone;
  • never treat continue or resend as idempotent;
  • reconcile Local Shell state and actual workspace/Git state before recovery;
  • bind browser mutation to exact task/worker/window identity;
  • persist mutation authority before an external side effect;
  • if an open/send/takeover result is ambiguous, reconcile instead of replaying it;
  • never inspect, copy, or move credentials, cookies, tokens, passwords, or session secrets;
  • never click or type in unrelated browser tabs;
  • automatic replacement-conversation creation and automatic live-worker page eviction remain disabled.

See docs/SAFETY.md for the detailed contract.

Installation

LWS requires Python 3.11+.

python -m pip install -e .
lws --version

For source-tree development without installation:

$env:PYTHONPATH = "src"
python -m lws --help

The implicit registry now lives in per-user durable state outside the source checkout, so deleting/recloning the repository does not discard supervisor history. On Windows the default is %LOCALAPPDATA%\LocalShellWebSupervisor\registry.sqlite3; on POSIX it is under $XDG_STATE_HOME/localshell-web-supervisor or ~/.local/state/localshell-web-supervisor. LWS_STATE_HOME relocates this implicit state root. --db and LWS_DB remain exact overrides. If an older checkout still has .lws/registry.sqlite3, LWS migrates it with a consistent SQLite backup only when no fresh watchdog lease fences that legacy database; otherwise that invocation keeps using the legacy path to avoid split-brain. Runtime databases remain ignored by Git and are never published as source.

Basic task registration

lws register `
  --task-id example `
  --project demo `
  --objective "finish the demo safely" `
  --cwd D:\work\demo `
  --session-id s_example `
  --conversation-url https://chatgpt.com/c/example

lws inspect example --uia
lws reconcile example --uia
lws recommend example --uia

The conversation URL is worker metadata. The durable Local Shell logical session and the real workspace are the execution truth.

Parent/child scheduling

Persist a child assignment before opening or adopting a browser conversation:

lws child-create PARENT_TASK `
  --child-key worker-a `
  --child-task-id CHILD_TASK `
  --project demo `
  --objective "implement isolated feature A" `
  --cwd D:\worktrees\feature-a `
  --expected-branch agent/feature-a `
  --base-ref ABC123 `
  --web-project-url https://chatgpt.com/g/g-p-0123456789abcdef0123456789abcdef `
  --prompt-file .lws\prompts\feature-a.txt `
  --json

child-create performs no browser, Local Shell, or Git mutation. For Chinese/non-ASCII paths or prompts in automation, prefer a UTF-8 JSON --contract-file (or ASCII-safe --contract-b64) instead of raw PowerShell command-line arguments; LWS rejects obvious pre-ingestion replacement-character/broken-Windows-path corruption.

For an existing child conversation:

lws child-adopt CHILD_TASK --conversation-url https://chatgpt.com/g/g-p-.../c/... --json

For explicitly gated initial child creation:

lws child-spawn-arm CHILD_TASK --json

lws child-spawn-open SPAWN_ATTEMPT `
  --enable-normal-browser-mutation `
  --confirm-child CHILD_TASK `
  --json

lws child-spawn-send SPAWN_ATTEMPT `
  --enable-normal-browser-mutation `
  --confirm-child CHILD_TASK `
  --wait-cooldown `
  --json

If either external mutation has an unknown outcome, do not rerun it:

lws child-spawn-reconcile SPAWN_ATTEMPT --json

A child should bind its Local Shell durable session back to LWS:

lws child-bind-session CHILD_TASK --session-id s_child

For a persisted batch, use a bounded dispatcher pool instead of opening one window per child:

lws child-dispatch-batch PARENT_TASK `
  --max-windows 2 `
  --enable-normal-browser-mutation `
  --confirm-parent PARENT_TASK `
  --json

The command is intentionally one-shot. It advances every child that is safe to advance, pauses normally when all dispatcher slots belong to children that have not bound a durable LSM session yet, and can be invoked again after those bindings appear. It reuses the exact HWND only after LSM binding or terminal completion, stops before replaying any ambiguous open/send outcome, and closes exact terminal child pages when no undispatched child still needs the slot. Use --keep-terminal-pages to retain terminal pages for inspection.

After independent verification, finish the durable child with a concrete completion reference:

lws child-complete CHILD_TASK --completion-ref commit:ABCDEF123456 --json
lws child-status PARENT_TASK --json

See docs/CHILD_SCHEDULER.md for the complete workflow.

Replacement workers

A missing browser window alone is not enough evidence to replace a worker. Replacement requires fresh Local Shell/workspace evidence and no unresolved external mutation.

The high-level flow is:

replacement-register
    -> replacement-arm
    -> replacement-submit
    -> one supported Local Shell MCP session takeover
    -> replacement-complete

The Local Shell takeover call is made exactly once after write-ahead authorization. If its result is lost or ambiguous, the next step is reconciliation, not another takeover call.

Resident watchdog

The default watchdog is advisory. Two browser-mutation modes are opt-in:

lws watchdog-start --auto-recover-timeouts --auto-continue-overruns --overrun-after 1520
lws watchdog-status
lws watchdog-restart --auto-recover-timeouts --auto-continue-overruns --overrun-after 1520
lws watchdog-stop

--auto-recover-timeouts remains limited to recognized delivery errors and retains the exact-window, Local Shell, workspace, semantic-fence, action-lock, cooldown, and recovery-budget checks. --auto-continue-overruns treats 1520 seconds (25m20s) as a hard work-turn wall clock. Each cycle first discovers visible top-level normal-Chrome chatgpt.com/.../c/... windows by bounded URL/HWND/PID identity and assigns one stable watch-chat-<conversation-id> owner per conversation; legacy LWS task aliases pointing at the same URL are deduplicated behind that owner. An unfinished conversation is sampled before the deadline and may receive one fenced continue after the deadline once generation has returned to a usable composer and two stable exact-window reconciliation samples agree. A submitted or ambiguous continuation durably resets the per-turn clock and cannot be replayed after a watchdog restart; a new observed manual generation also resets the clock. These periodic wall-clock nudges do not consume the bounded fault-recovery budget. A proven pre-send failure, including a rate-limit modal, enters cooldown instead of being retried every scan. Auto-discovery is intentionally limited to top-level Chrome windows whose active address bar exposes the conversation URL; it does not inspect inactive tabs or conversation text.

Detached Windows startup uses DETACHED_PROCESS, CREATE_NEW_PROCESS_GROUP, and CREATE_NO_WINDOW, redirects standard handles, and writes its default log beside the registry rather than inside the checkout. Restart is cooperative and fail-closed: replacement launch occurs only after the prior resident PID is gone and its exact stop fence can be cleared.

Architecture

LWS separates four layers:

web conversation / browser evidence
             |
             v
      LocalShell Web Supervisor
      - registry
      - reconciliation
      - worker leases/generations
      - scheduler/replacement
      - mutation write-ahead logs
             |
             +------> Local Shell MCP durable sessions / Goals / jobs
             |
             +------> actual workspace / Git state

The failure domains are intentionally independent. Browser UI is evidence, not durable execution state.

Additional design documents:

Older version-spec documents are retained as design history. The Git history intentionally preserves the project's earlier name and development record.

Local/private state

.lws/ is intentionally ignored. It may contain:

  • registry databases;
  • browser observations and exact-window bindings;
  • action/replacement/spawn write-ahead records;
  • watchdog logs;
  • local experiment fixtures.

Do not publish .lws/, browser profiles, storage-state files, cookies, tokens, session secrets, or machine-specific dumps.

Development checks

$env:PYTHONPATH = "src"
python -m pytest -q
git diff --check

Run a secret/privacy scan before publishing changes.

License

MIT. See LICENSE.