Repository navigation
feat(agent-command): let local agents discover advertised plugin tools - #2110
Conversation
03b14fe to
b5f0f7f
Compare
b5f0f7f to
23550f7
Compare
wesbillman
left a comment
There was a problem hiding this comment.
🤖 Star Lord’s automated source review via Wes’s account, with independent Rocket, Mantis and Groot coverage.
Reviewed head 23550f7c664792983741d53a868618453ffdef31 against base ca836a3109be30a7fd46db97bdcceaf32ebabf65 (merge base 923a80f4aeed4ab7a671c4df62f10b3a81bf15bc).
[P2] Remove the internal coordination link from the public PR description. The “Initiative context” section links an internal status application and names its internal task. This repository and PR are public. Please remove that link/task context or replace it with a public-safe summary; I have deliberately not repeated the address here. This concerns public PR material, not contributor attribution.
The inline findings cover stale advertisements trusting a replacement loopback listener (P2) and external helper help directing callers to a different executable (P3). Optional follow-ups: use a shorter discovery-specific timeout so one stalled plugin does not delay every healthy command by up to 60 seconds; add a cross-link to the new advertisement contract from the plugin architecture guide. These are not required changes.
Source-only: no helper, tests, builds or app workflows were executed by this review. The new Rust integration tests add no browser cases and use the appropriate native boundary; their fixture does not itself enforce second-initialize shutdown, so the PR’s separately reported host/agent exercises remain author-reported evidence. Windows behavior, packaged/plugin lifetime and runtime latency remain unverified here. The one CI snapshot had Rust/tool integration successful, two WebKit shards still running and Windows validation skipped. The linked issue could not be read, so intent was assessed from the PR description and repository docs. No attached images were present in the description. This COMMENT is not an approval or merge recommendation.
23550f7 to
d7bd3dd
Compare
|
On review 5729028348's body:
Reopen if you'd like either optional item taken here. — Posted by Claude Code (claude-opus-5-5) on behalf of kylez |
wesbillman
left a comment
There was a problem hiding this comment.
🤖 Star Lord’s automated source follow-up via Wes’s account, with independent Rocket, Mantis and Groot coverage.
Reviewed head d7bd3dda6f8869eb6174be3fda278b1f13719007 against base ca836a3109be30a7fd46db97bdcceaf32ebabf65, comparing the fix with previously reviewed head 23550f7c664792983741d53a868618453ffdef31.
[P2] The existing listener-authentication finding remains partially unresolved: #2110 (comment). In crates/agent-command/src/main.rs:258–262, ownership is sampled once before sequential catalog requests. A server can close and its port change owners while discovery awaits another server; the cached port still passes :274–276, and :293 sends the saved headers to the replacement. The host closes connections after each request (src-tauri/src/host_mcp.rs:264–267), so the later tool call (main.rs:169–175) also needs a current authenticated connection. Bind server/peer identity verification to the actual connection before sending saved credentials or tool arguments; refreshing the initial snapshot only narrows the race. Add coverage for a listener changing ownership after discovery starts—the new wildcard-listener case covers only the state before the snapshot. This is follow-up evidence on the existing finding, not a second issue.
The prior external --help finding is repaired in source, and the public description’s internal coordination link has been removed. No additional actionable findings. Optional: preserve the underlying connection error when the ownership check cannot run, so sandbox failures remain easier to diagnose.
Source-only: no tests, builds, helper or app workflows executed. The native integration tests are the appropriate layer; this follow-up adds no browser cases. Runtime behavior, Windows safety and the reported macOS latency remain unverified here. The one CI snapshot still had Rust, JavaScript and browser work running; Windows validation was skipped. The referenced issue could not be read (403); intent was assessed from the PR and repository docs. No attached images were present. This COMMENT is not an approval or merge recommendation.
d7bd3dd to
e8da62f
Compare
wesbillman
left a comment
There was a problem hiding this comment.
🤖 Star Lord’s automated source follow-up via Wes’s account.
Reviewed head e8da62f68152949d310cbde3b5285576b50692f4 against base ca836a3109be30a7fd46db97bdcceaf32ebabf65, comparing with previously reviewed head d7bd3dda6f8869eb6174be3fda278b1f13719007.
This revision only clarifies the installed macOS app name in docs/agents.md and docs/buzz-help.md (3 added lines, 2 removed). Implementation and tests are unchanged. The documented helper location matches the configured resource layout; no new actionable findings in this documentation follow-up.
The existing P2 listener-authentication finding remains unresolved because its implementation is unchanged: previous follow-up and repair guidance. No duplicate inline finding added.
Solo follow-up under the limited-change exception; the preceding implementation review had Rocket, Mantis and Groot coverage. Source-only: no tests, builds, helper or app workflows executed. No browser cases added or removed and no focus behavior changed. Public description inspected; no attached images. The CI snapshot shows Chromium shard 8/9 and the required aggregate failing, with Windows skipped; failure cause and runtime behavior were not investigated in this documentation review. This COMMENT is not an approval or merge recommendation.
e8da62f to
5d0c8a7
Compare
wesbillman
left a comment
There was a problem hiding this comment.
🤖 Star Lord’s automated source follow-up via Wes’s account, with independent Rocket, Mantis and Groot coverage.
Reviewed head 5d0c8a7a3bce604286826800f663db1dfb3d23be against base ca836a3109be30a7fd46db97bdcceaf32ebabf65, comparing endpoint trees with previously reviewed e8da62f68152949d310cbde3b5285576b50692f4.
The fix now checks listener ownership before each advertised request, including the later tool call, and reports failures to run the check. That repairs the discovery-length stale snapshot and missing call-time check. No new actionable findings.
The existing P2 listener-authentication finding remains partially unresolved: original finding. In crates/agent-command/src/main.rs:81–96, the ownership observation still precedes a separate connection and send. If the original server closes and another OS user obtains the port between them, tool arguments can reach that replacement and its output can be trusted. The PR now accurately discloses this narrower race; disclosure does not establish that the peer is authenticated. The remaining repair is connection-bound server identity verification before sending credentials or arguments, potentially requiring a host/transport change as the author notes. This updates the existing finding rather than adding a duplicate. The final scope/risk decision belongs to the human maintainers.
Source-only: no tests, builds, helper or app workflows executed. Tests are unchanged; the wildcard-listener case checks ownership before discovery, not a transition between discovery and a call. Native integration is the appropriate regression layer; zero browser cases added or removed, and no UI focus surface changed. The one CI snapshot had Rust/tool integration successful, browser shards still running and Windows validation skipped. Platform behavior and the author-reported macOS latency remain unverified here. Public description and commit inspected; no attached images or new public-material findings. NEXUS-4115 was not independently consulted this cycle; intent was assessed from the PR and repository docs. This COMMENT is not an approval or merge recommendation.
5d0c8a7 to
69fa4c5
Compare
A plugin can spawn the bundled helper with `--advertise` inside its existing app-held MCP connection; the helper saves that session to a same-user-private file. A local agent Buzz did not start (Claude Code, Codex, Pi, Nexus) runs the installed helper with no inherited connection and calls the newest advertised session that still answers, dropping stale ones. Before sending an advertised session anything, the helper confirms on macOS and Linux that a process of this account accepted that same connection; Windows reports advertised sessions as unsupported. Inherited connections keep precedence. Fixes NEXUS-4115 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Kyle Zemek <kylez@squareup.com>
69fa4c5 to
0f0526d
Compare
Co-authored-by: Codex <noreply@openai.com> * origin/main: (26 commits) AIDA-2336: Show when worktree folders are required (#2165) AIDA-2326: Reveal full emoji completion names (#2172) feat(agent-command): let local agents discover advertised plugin tools (#2110) feat(bestie): a Bestie tab on your Bestie's agent page (#2034) fix(relay): notify verified profile changes once per replay (#2209) feat(shortcuts): open Shortcuts settings with Mod+/ (#2022) Re-send live channel subscriptions on access grants, not every minute (#2207) perf(profiles): count verified namesakes once per publish (#2159) feat(claude-code): start sessions with the agent's recent notes (#2040) fix(relay): skip status, emoji, read-state and reminder re-reads a continuous replay covers (#2204) Support agent credentials through the macOS debug helper (#2174) refactor: centralize relay search debounce and require two characters (#2200) fix(relay): drop unused profile exemptions from config and agent reads (#2202) fix(relay): skip full profile and roster re-reads after a continuous reconnect (#2203) Notify when Solo chat agents finish (#2011) feat(mcp): start a Solo chat with channel_create (#2128) fix(relay): keep profiles when channel access ends (#2187) Wait for thread readiness before keyboard input (#2181) fix(builderlab): unify the development sign-in (#2109) feat(host): add byte requests, private saves and POST dispatch protection (#2084) ... Signed-off-by: Daniel Rouleau <drouleau@squareup.com>
TL;DR
Lets any local agent of the signed-in user (Claude Code, Codex, Pi, Nexus) call a plugin's app-held MCP tools without inheriting a connection from Buzz. A plugin publishes one of its existing
host.serveMcpsessions with the bundledbuzz-agent-command --advertise. A fresh agent then runs the installed helper by absolute path, and the helper finds that session. There is no pairing, token copy or per-agent MCP configuration.Contract for plugins
const server = await host.serveMcp(handler).{ "id": "agent-access", "program": "buzz-agent-command", "bundled": true, "args": ["--advertise"] }inhost.processes(withhost.mcp: true). Then callawait host.spawn("agent-access", { mcp: server }).buzz-agent-command --launch <bundled helper> --advertise. The outer helper claims the server's one session. The inner helper writes that session (loopback URL, bearer andmcp-session-id) to a random*.jsonin~/.buzz-foundation/agent-connections(directory0700, file0600), then exits.hiddenPaths.Contract for agents
<installed helper> tools,<installed helper> <tool> --field valueand<installed helper> call <tool> '<JSON object>'behave asbuzzdoes inside an agent command.tools/list. It merges the catalogs, so the newest server wins a name both offer (also across plugins), and routes each call to the server that offers the tool. A tool call stops asking at the newest server that offers it, so an older server that accepts and never answers does not hold it for the 60 s request timeout;toolsstill asks every advertisement.lsof -a -u <euid> -iTCP@127.0.0.1:<caller port> -sTCP:ESTABLISHED -Fpunmust show the server's side (127.0.0.1:<server>-><caller port>), and every listed process must carry that numeric UID. This does not rely on what non-rootlsofcan see./proc/net/tcprow for the server's side must be established, owned by the euid and attached to a socket (non-zero inode). Before accept, Linux 6.12 shows that row with the overflow UID and inode 0.lsofrun is killed and reaped after 10 s, and the whole request has a 60 s deadline. Missing, partial or failed evidence is a denial.Could not confirm that this user's Buzz serves an advertised port.lsofadds about 0.5 s per request on macOS.Cargo.lock; no versions changed. The helper drops reqwest, whose connector does not expose the socket to check.--help(or no arguments) prints the usage with the path the helper was run as and exits 0, so an agent can discover the commands from the helper itself.Could not reach Buzz: <cause>. The agent's sandbox must allow loopback connections: Codex's read-only and workspace-write modes block them unless network access is enabled.BUZZ_AGENT_CONNECTIONstill takes precedence. Buzz-started agents and the existing--launchpath behave as before, on every platform, and skip the ownership check./Applications/Buzz.app/Contents/Resources/bin/buzz-agent-command(macOS; the release bundle name, while some installs use another name such asBuzz Foundation.app),bin\buzz-agent-command.exe(Windows) and/usr/lib/Buzz Foundation/bin/buzz-agent-command(Linux).The host, FOUNDATION files and the relay
buzzCLI are unchanged. No account key is read or written.Verification
crates/agent-command/tests/discovery.rsruns the real helper binary against a loopback server that copies the host's token, single-session claim and 404 behaviour. It covers:--launch … --advertisepublish path, with private modes;Unit tests cover the
lsofand/proc/net/tcppredicates: both sides present, only the caller's side (pre-accept), a wrong or missing UID, a foreign process group next to ours, a non-established state and an unaccepted (inode 0) row.On the previous head, these mutations failed the tests: reversing the newest-first order, dropping the pruning, dropping the owner and mode check, pruning on any connection error, and asking every advertisement before a call. The tests also passed there inside Codex's workspace-write sandbox with network access.
Real other-user check (one-off Blox boxes). A listener run as
nobodythroughsudostands in for a foreign process on a reused port. A second run as the current user is the control. The same script also applied each mutation and re-ran the crate tests. Kyle's account was not touched.nobodylistener-uOn macOS, the
nobodylistener was still denied with both the-uselection and the parser's UID check removed: non-rootlsofthere did not list another user's socket. So the macOS real negative proves the end-to-end denial, and the unit test is what guards the UID checks.Locally on macOS,
cargo clippy(deny warnings), the crate tests (including the sandbox case) andscripts/test-agent-command.mjspass.Outside CI:
host_mcptest, not committed, drove the debug helper against the realHostMcpServers. It covered--launch <helper> --advertise, a freshtools, and a tool call (tools/list, thentools/callwith its arguments), all through the new client and ownership check.HostMcpServers. It served two windows' servers, each advertised with--launch <helper> --advertise. A freshenv -icall reached the newest server, including four concurrent calls. Aftercloseon the newest server, calls fell back to the older one and the stale file was removed. Afterstop_all(page reload or app exit), calls failed with "No running Buzz…" and no files remained. A re-served server advertised and answered again. A second spawn on a claimed server stopped that server.codex exec(gpt-6-luna, low) ran with no inherited connection and was told only the helper's installed-layout path. It ran--help, listed the tools, created a Solo chat and listed chats, all exiting 0, against a loopback fixture with the host's token and session rules.Not covered here
.exeto a bundled program name, so a plugin must declarebuzz-agent-command.exethere. Calls through advertisements fail as unsupported on Windows. Neither has been exercised there.Fixes NEXUS-4115
🤖 Generated with Claude Code