Skip to content

feat(agents): run agents in a hidden page of their own on macOS - #1744

Closed
loganj wants to merge 3 commits into
agent-host-page-communitiesfrom
agent-host-window
Closed

loganj wants to merge 3 commits into
agent-host-page-communitiesfrom
agent-host-window

Conversation

@loganj

@loganj loganj commented Oct 27, 2026

Copy link
Copy Markdown
Contributor

🤖 Stacked on #1743. It also carries #1735's two commits unchanged, because the agents page depends on page-owned native work being tied to its own webview. Part of the work to run agents apart from the UI, so that a frozen or hidden main window cannot stall them.

What this does

On macOS, Buzz now opens a hidden agents webview at launch. It never shows, it is never throttled, and it lives until Quit. Agents now run there and no longer in the main window:

  • The agents page (agents.html, src/app/agents-host.ts) runs:
  • The main window runs only the UI parts of plugins. It reaches the service over the agents port, so its screens show what the service has, and a hidden or busy main window no longer holds up an agent turn.
  • ctx.agents in the main window is a facade, ScreensAgents. A plugin's agent types and tools registered there go to the service under that plugin's identity, and they are withdrawn when the plugin stops. Members that only an agent entry can use, such as wake, handle and storage, throw an error that points the plugin to its agent entry.
  • Plugins load per page: PluginRuntime takes parts (both, ui or agent). In the agents page, a plugin that has no agent entry stays active with nothing to run, so the contributions forwarded under its identity are still admitted.

Other platforms keep the in-page service, so nothing changes there.

How the two pages talk

Over a native command pair, agents_port_listen and agents_port_send (src-tauri/src/agents_port.rs). Each page opens its end with a Tauri channel. A frame goes from the webview it came from to the other page of the pair, and to no other page. A page that reloads replaces its end. The existing event port handles ordering, sessions and reloads on top of this transport.

I did not use Tauri events. The repo's link tests forbid core:event permissions, and they require the main capability to cover only main.

Permissions

The agents page has its own capability, capabilities/agents.json. It applies on macOS only, to the agents webview only. It allows the relay, agent, plugin host, classifier and agents port commands. It does not allow identity setup and export, nsec backup, pairing, sign-out, huddles, notifications, the dock, unread badges, deep links, terminals, the browser, the updater, window controls or plugin import and update. The native MCP test overlay now lists it too.

Known gaps

  • If a plugin's agent entry fails in the agents page, the main window's plugin UI does not show it, and Retry there does not reach it.
  • If the agents window cannot be created, the main window waits for the service and never gets it.
  • The platform check in JS (agentsRunApart) has to match the Rust cfg(target_os = "macos").
  • I have not yet run the live check: a frozen and a hidden main window while an agent works.

Evidence

  • Rust:
    • agents_port::frames_go_only_to_the_other_page_of_the_pair covers routing, a missed early frame, a reloaded end and a refused third page.
    • native_command_permissions_isolate_webviews checks the agents page's allowed and denied commands, and that no command is allowed from remote content. It fails if agents.json grants terminal_spawn, or if a port grant is missing.
    • The full cargo test --lib passes (567 passed), and cargo clippy --all-targets -D warnings and cargo fmt --check are clean.
  • JS:
    • runtime.test.mjs adds "a page runs only the part of each plugin it is for".
    • port.test.ts checks that the service is told the communities and is told again after it reloads.
    • screens.test.ts checks tool forwarding under the plugin until it stops, relay lookup, and the refusals.
    • Each new test fails when the code under it is mutated.
    • deep-links and external-links pass.
    • tsc and Biome are clean, and the design checks pass. The full vitest run passes except four emoji and avatar-picker tests. Those fail only in this worktree, where Vite denies the symlinked node_modules.

🤖 Generated with Claude Code

loganj and others added 3 commits October 27, 2026 13:06
Host processes, MCP servers and held agent removals belonged to "the
page", which meant the main webview: a load there stopped all of it.
Each now records the page in the webview that started it, and a page
load stops only that webview's work. With main the only webview that
starts any, behavior is unchanged; a hidden agents webview can then
outlive a reload of the UI page.

Co-authored-by: Larry <627498bd4bd1f281a16431e3c6cce3b5c25b6692798c78672298aefbf2f8f8b5@buzz.block.builderlab.xyz>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Logan Johnson <loganj@squareup.com>
A page load in the main webview cancelled every relay upload, and a
page load in another webview cancelled none. Each upload now records the
webview whose page began it, and a page load or a closed window stops
only that webview's uploads. Quit still stops them all.

Co-authored-by: Larry <627498bd4bd1f281a16431e3c6cce3b5c25b6692798c78672298aefbf2f8f8b5@buzz.block.builderlab.xyz>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Logan Johnson <loganj@squareup.com>
On macOS, Buzz now opens a hidden, never-throttled "agents" webview at
launch. It runs the agent service, the agent entries of plugins, local
agents and their activity, and connects to the joined communities with
sessions that keep nothing on the device. The main window runs only the
UI parts of plugins and reaches the service over the agents port, so a
hidden or busy main window no longer holds agents up.

The two pages talk over a native command pair, agents_port_listen and
agents_port_send. It routes each frame from one page's webview to the
other's, so neither page needs app event permissions. The agents page
has its own macOS-only capability with the relay, agent, plugin host
and classifier commands, and none of the screen, window, account setup,
terminal, browser or notification commands.

In the main window, ctx.agents is a facade that forwards plugin agent
types and tools to the service under the plugin's identity, and refuses
the members only an agent entry can use. Other platforms keep the
in-page service.

Co-authored-by: Larry <627498bd4bd1f281a16431e3c6cce3b5c25b6692798c78672298aefbf2f8f8b5@buzz.block.builderlab.xyz>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Logan Johnson <loganj@squareup.com>
loganj added a commit that referenced this pull request Oct 29, 2026
The Runner becomes its own service, apart from the Manager, and on macOS
it runs in a hidden "agents" webview that the system never throttles,
with its storage and the plugins' agent entries. The screens' page keeps
the Manager. Only JSON crosses, over the native agents port, on two
lanes: "agents" for the Manager and Runner link, and "page" for where
agent storage is, the followed communities, and local activity.

- The Runner owns presence, keep-awake, agent hooks and the reconnect
  of its own connections.
  A failed relay is retried at most every 30 s, whichever owner asks.
- The Manager waits at most 10 s for the Runner's first decision.
- The agents page starts the Runner once the screens' page says where
  agent storage is; the screens' page still loads and moves it first.
- Other platforms keep the Runner in the screens' page.

Includes #1735 (page-owned native work), #1743 and #1744's native page.
Squashes #1892 and #1893.

Signed-off-by: Logan Johnson <loganj@squareup.com>
Co-authored-by: Larry <627498bd4bd1f281a16431e3c6cce3b5c25b6692798c78672298aefbf2f8f8b5@buzz.block.builderlab.xyz>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
loganj added a commit that referenced this pull request Oct 29, 2026
The Runner becomes its own service, apart from the Manager, and on macOS
it runs in a hidden "agents" webview that the system never throttles,
with its storage and the plugins' agent entries. The screens' page keeps
the Manager. Only JSON crosses, over the native agents port, on two
lanes: "agents" for the Manager and Runner link, and "page" for where
agent storage is, the followed communities, and local activity.

- The Runner owns presence, keep-awake, agent hooks and the reconnect
  of its own connections.
  A failed relay is retried at most every 30 s, whichever owner asks.
- The Manager waits at most 10 s for the Runner's first decision.
- The agents page starts the Runner once the screens' page says where
  agent storage is; the screens' page still loads and moves it first.
- Other platforms keep the Runner in the screens' page.

Includes #1735 (page-owned native work), #1743 and #1744's native page.
Squashes #1892 and #1893.

Signed-off-by: Logan Johnson <loganj@squareup.com>
Co-authored-by: Larry <627498bd4bd1f281a16431e3c6cce3b5c25b6692798c78672298aefbf2f8f8b5@buzz.block.builderlab.xyz>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@loganj

loganj commented Oct 29, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Closing this PR because #1893 (#1893) replaces it. #1893 has the hidden agents page on macOS, agents.html, the agents capability, agents_port.rs and page.rs. The Manager and Runner cross only over link.ts, which replaces port.ts and ScreensAgents.

@loganj loganj closed this Oct 29, 2026
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