Skip to content

feat(claw-server): close finished-agent tabs via reconciliation sweep - #2085

Merged
Dani Akash (DaniAkash) merged 3 commits into
mainfrom
feat/agent-tab-cleanup-on-finish
Aug 4, 2026
Merged

feat(claw-server): close finished-agent tabs via reconciliation sweep#2085
Dani Akash (DaniAkash) merged 3 commits into
mainfrom
feat/agent-tab-cleanup-on-finish

Conversation

@DaniAkash

Copy link
Copy Markdown
Contributor

What

Closes the browser tabs an agent opened once its session finishes. Ending a session already released the durable tab-ownership ledger, but nothing closed the actual Chrome tabs, so agent tabs accumulated over time. The only server-driven close was at the tab-group level, gated on the long retention window and on the tabs having been grouped, so ungrouped tabs, popup children, crashes, and CDP disconnects all leaked tabs.

How

A periodic reconciliation sweep closes any tab that was agent-owned but now has no live owner, once a short grace has elapsed.

  • One predicate, derived from the durable ledger: a tab with ownership history and no open row, whose most recent release is older than the grace cutoff. This cannot touch a tab that was never agent-owned (no history) or one a live session still owns (an open row), and it catches popup children by construction (they carry inherited ownership rows).
  • Reconciliation, not best-effort: the sweep intersects the ledger predicate with the browser's currently open tabs, so it only ever acts on tabs that still exist and is safe to run repeatedly. It survives crashes (open rows are reconciled to released on startup), CDP disconnects (a pass no-ops while the browser is unreachable and retries on the next tick), and ungrouped tabs.
  • Foreground tab is spared: the tab the user is actively viewing is skipped and closed on a later pass once they move off it.
  • Grace window: collapse-on-finish is unchanged, so the user still gets a brief look at the result; the tabs then close after the grace. Defaults to three minutes, tunable via CLAW_TAB_CLEANUP_GRACE_MS.

Tests

  • Ledger orphan query: released-past-cutoff is returned; an open owner, a within-grace release, and a tab transferred to a live session are all excluded.
  • Close policy: closes orphaned + live + inactive tabs only; spares the foreground tab; skips tabs the browser no longer lists.
  • Dependency-boundary allowlist updated for the tab_cleanup -> browser service edge, with a guarding test.
  • Full crate suite, clippy, and rustfmt clean.

Notes

Legacy target-scoped claims (pre-tab-id ownership) are out of scope; the sweep operates on the current tab-id ownership model. On-reconnect cleanup is covered by the next periodic tick.

Ending a session released its tab-ownership ledger but never closed the
Chrome tabs the agent opened, so agent tabs accumulated. Only the tab group
was ever closed by the server, and only after the long retention window and
only when the tabs were grouped, so ungrouped tabs, popup children, crashes,
and CDP disconnects all leaked tabs.

Add a periodic reconciliation sweep that closes any tab which was agent-owned
but now has no live owner, once a short configurable grace has elapsed. The
predicate is derived from the durable ledger (a tab with history and no open
row), so it cannot touch a user tab or a tab a live session still owns, and it
catches popup children by construction. The sweep intersects with the browser's
open tabs, so it only ever acts on tabs that still exist and is safe to run
repeatedly, surviving crashes and disconnects. The foreground tab is spared and
closed on a later pass once the user moves off it. Grace defaults to three
minutes and is tunable via CLAW_TAB_CLEANUP_GRACE_MS.
@greptile-apps

greptile-apps Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR adds durable-ledger-based reconciliation that periodically closes released agent-owned tabs after a configurable grace period.

  • Adds the orphaned ownership query and its ledger tests.
  • Adds a runtime cleanup task that intersects orphaned IDs with live browser pages and attempts to spare active tabs.
  • Adds cleanup configuration and updates service dependency-boundary coverage.

Confidence Score: 4/5

The PR should not merge until the cleanup revalidates live ownership and foreground state immediately before closing each tab.

The new destructive reconciliation acts on ownership and active-tab snapshots that can change during awaited work, allowing it to close a newly claimed agent tab or the tab a user has just activated.

Files Needing Attention: packages/browseros-agent/apps/claw-server-rust/src/services/tab_cleanup.rs

Important Files Changed

Filename Overview
packages/browseros-agent/apps/claw-server-rust/src/services/tab_cleanup.rs Implements the reconciliation close policy, but both ownership and foreground protections can become stale before the destructive close.
packages/browseros-agent/apps/claw-server-rust/src/db/session_tabs.rs Adds a sound aggregate query for released tab IDs with no remaining open owner, plus focused query tests.
packages/browseros-agent/apps/claw-server-rust/src/runtime.rs Registers cleanup as a managed periodic background task using the existing shutdown and sweep interval machinery.
packages/browseros-agent/apps/claw-server-rust/src/config.rs Adds a positive, configurable three-minute cleanup grace with consistent test configuration updates.
packages/browseros-agent/apps/claw-server-rust/tests/dependency_boundaries.rs Explicitly permits and tests the new tab_cleanup-to-browser service dependency.

Sequence Diagram

sequenceDiagram
    participant Agent
    participant Ledger
    participant Sweep as Cleanup sweep
    participant Browser
    participant User
    Sweep->>Ledger: Query released orphan tab IDs
    Ledger-->>Sweep: Orphan snapshot
    Agent->>Ledger: Enqueue a new live claim
    Sweep->>Browser: List pages and active state
    User->>Browser: Activate a candidate tab
    Sweep->>Browser: Close page from stale snapshots
    Note over Sweep,Browser: Ownership and active state are not revalidated
Loading
Prompt To Fix All With AI
### Issue 1
packages/browseros-agent/apps/claw-server-rust/src/services/tab_cleanup.rs:72-76
**Stale ownership closes claimed tabs**

When a live session claims an orphaned tab after the ledger query but before the close, the sweep acts on the stale orphan snapshot and closes the newly owned tab, interrupting the running agent session.

### Issue 2
packages/browseros-agent/apps/claw-server-rust/src/services/tab_cleanup.rs:72-76
**Stale foreground state closes active tab**

When the user activates an orphaned tab after the page listing reports it inactive but before the close command, the sweep uses the stale active-state snapshot and closes the tab the user is viewing.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Reviews (1): Last reviewed commit: "feat(claw-server): close finished-agent ..." | Re-trigger Greptile

@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

✅ Tests passed — 2543/2547

Suite Passed Failed Skipped
agent 367/367 0 0
build 34/34 0 0
claw-app 297/297 0 0
claw-mcp 109/109 0 0
claw-onboard 87/87 0 0
claw-server-rust-quality passed 0 0
claw-server-rust 555/555 0 0
server-agent 313/313 0 0
server-api 170/170 0 0
server-browser 10/10 0 0
server-integration 10/10 0 0
server-lib 299/300 0 1
server-root 38/41 0 3
server-tools 254/254 0 0

passed = ran successfully but emits no JUnit counts (a lint/format gate).

View workflow run

The cleanup sweep decided on an orphan snapshot and a page listing, then closed
later, so between them a live session could reclaim a tab or the user could
bring one to the foreground and the sweep would still close it. Re-check both
the instant before each close: skip a tab that now has an open owner and skip
the current foreground tab (read fresh via get_active). This shrinks the window
to a negligible residual; CDP offers no atomic close-if-unowned-and-inactive, so
a tiny irreducible gap remains by nature.
…n env var

Drop CLAW_TAB_CLEANUP_GRACE_MS and the tab_cleanup_grace config field; the grace
is a single sensible default (3 minutes) defined next to the sweep that uses it.
One fewer knob to reason about, and the value lives where the behavior does.
@DaniAkash
Dani Akash (DaniAkash) merged commit 434cf7d into main Aug 4, 2026
23 checks passed
@DaniAkash
Dani Akash (DaniAkash) deleted the feat/agent-tab-cleanup-on-finish branch August 4, 2026 13:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant