Skip to content

feat(panel): let the agent clear a browser panel's shared context - #94

Open
budet-b wants to merge 2 commits into
callstackincubator:mainfrom
budet-b:feat/clear-panel-context
Open

budet-b wants to merge 2 commits into
callstackincubator:mainfrom
budet-b:feat/clear-panel-context

Conversation

@budet-b

@budet-b budet-b commented Nov 9, 2026 •

Copy link
Copy Markdown

Problem

The agent can read what a browser panel shares (mobile_read_panel_context), but has no way to clear it. Notes the user adds to the panel stay in the shared context after the agent has acted on them, so every later read returns the same already-handled notes again.

Solution

A new MCP tool, mobile_clear_panel_context, in three pieces:

  • Server (packages/core/src/browser-panel.ts): the tool forgets each browser-panel tab's stored context and increments a per-tab clears counter. It only forgets what the agent has read: each store bumps a per-tab revision, reading stamps it, and a tab whose content was stored or changed since the last read keeps it (the result says so, and tells the agent to read again). A clear with no connected tab, or no shared content, says there is nothing to clear. GET <base>context returns { clears } (it previously answered 405), and storing context now returns the count it was stored under.
  • Page (packages/mcp-app/src/browser-panel.ts): while its tab has stored content, the page polls the clears count from its own session's context endpoint every 2 seconds and hands it to the panel in the host context under the mobile-dev/contextClears key (CONTEXT_CLEARS in packages/shared/src/panel-target.ts). It ignores counts its current content was stored under, so a clear that raced a fresh note cannot drop that note. A tab that never shared anything does not poll.
  • Panel (packages/ui/src/model-context.ts): PanelContext.hostChanged() drops the notes the panel still displays when the counter increases, then republishes, so the user's device selection survives the clear and stays readable. The value seen on open is treated as a baseline, not as a clear.

Details that came out of self-review:

  • The tool is annotated idempotentHint: false (each call moves the counter, and a blind retry could drop notes added in between) alongside destructiveHint: true.
  • The page only pushes host-context updates once the bridge is connected, so an early theme change or clear cannot race the bridge handshake.
  • Telemetry follows the sibling store path: the server counts cleared context updates (browser_panel.context_update with outcome cleared) and the panel counts ui.context.agent_clear.

Why polling?

The browser-panel transport deliberately keeps no idle stream (tool and resource responses use per-request SSE only), so the server has nothing to push the clear notification on. A 2-second poll of the existing context endpoint seemed like the smallest change consistent with that design; it only runs while the tab has stored content, and the page already pings every 30 seconds regardless.

Tests

  • packages/core/test/browser-panel.test.ts: the clear flow end to end over HTTP — unread notes are kept, read notes clear, a changed note survives a stale clear, a tab with nothing stored is not told to drop anything, the kept/cleared mixed case across two tabs, the store response carries the count, and the idempotence annotation.
  • packages/ui/test/context-clears.test.ts: 5 tests for the panel-side behavior (drop on increment, baseline on open, republish after a drop, republish with no notes so the devices survive).
  • tooling/hosts/browser-panel.spec.ts: a new Playwright test drives the real page through read → clear → 2-second poll → republish and checks the device context stays readable.
  • Docs updated: CONTRIBUTING.md and apps/claude/skills/mobile-dev/SKILL.md.

Verified

  • The full unit suite passes locally (npm test: 1259 pass, 0 fail, 1 skipped — twice in a row), as do the targeted files (node --test packages/core/test/browser-panel.test.ts packages/ui/test/context-clears.test.ts packages/ui/test/log-context.test.ts: 42 pass, 0 fail).
  • The Playwright browser host project passes (playwright test -c tooling/hosts browser-panel.spec.ts --project=browser: 7 passed), including the new end-to-end clear test.
  • tsc --noEmit reports no new errors against the repository's current baseline.
  • Manually end to end (first commit): built and packaged the Claude plugin (npm run build + npm run package:claude), installed it in Claude Code, and confirmed the agent can clear the panel's notes and the open panel drops them. Not repeated after the second commit.

Not verified / known noise

  • The visual regression suite (test:visual:docker) was not run; this change adds no visual difference I'm aware of, but I have not confirmed that with screenshots.

Open questions for maintainers

  • Is a 2-second poll of the context endpoint acceptable while a tab has stored content, or would you prefer a longer interval (or piggybacking on the existing 30-second ping)?
  • Would you rather deliver the clear through a different mechanism entirely? Happy to rework the notification path if you have a preferred direction.
  • The read-gate means a concurrent user note can make a clear report "nothing cleared" and ask for a re-read; if you would rather clear unconditionally and accept the data-loss window, that is a small change.

🤖 Generated with Claude Code

The agent can read what a browser panel shares (mobile_read_panel_context) but not
clear it, so notes it already acted on stay in the panel and are read again.

Add mobile_clear_panel_context: the server forgets the tabs' context and counts the
clear per tab; the page polls that count (the server keeps no idle stream to push
on) and reports it to the panel in the host context, where PanelContext drops the
notes it still shows.
@vercel

vercel Bot commented Nov 9, 2026

Copy link
Copy Markdown

@budet-b is attempting to deploy a commit to the Margelo Team on Vercel.

A member of the Team first needs to authorize it.

mobile_clear_panel_context destroyed notes the agent never saw and left
the device selection unreadable until the user changed it:

- The server forgets a tab's context only when it was read since its
  last store; newer notes are kept and the result says so. A clear with
  no connected tab or no shared content says there is nothing to clear
  instead of reusing the read tool's guidance.
- Storing context returns the count it was stored under; the page polls
  only while its tab has stored content and ignores counts its content
  was stored under, so a clear racing a fresh note no longer drops it.
- The panel republishes after dropping cleared notes, so the device
  selection survives the clear, and host context updates wait for the
  bridge to connect.
- The tool no longer advertises idempotence, the server counts cleared
  context updates and the panel counts ui.context.agent_clear.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@budet-b
budet-b marked this pull request as ready for review November 9, 2026 17:43

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant