Skip to content

feat: start agents on linked machines per server settings - #43

Merged
nickrroberts merged 4 commits into
mainfrom
feat/multi-machine-agents
Sep 29, 2026
Merged

nickrroberts merged 4 commits into
mainfrom
feat/multi-machine-agents

Conversation

@nickrroberts

Copy link
Copy Markdown
Collaborator

What

Coordinators (and standing agents) can start one-off agents on other machines linked to their server, such as a Mac Mini on the tailnet. Where a new agent runs follows a server-owned setting. Spec: docs/internals/multi-machine-agents.md.

Spec summary

  • Model. The coordinator's server (home) is an HTTP client of a peer. It uses only routes every server already serves (/api/orchestration/shell, /threads/:id, /dispatch) with a bearer token limited to orchestration:read orchestration:operate. The remote agent is a normal thread in a matching Project on the peer. Nothing is added to the peer.
  • Settings. ServerSettings.agentPlacement: mode local (default, today's behaviour) / single / balanced, per-machine preference (Prefer, Normal, Less often, Manual only, the same vocabulary as the client Load balancing), fallback to this machine, and a linked-machine map. New section Settings > Connections > Agent machines. Tokens live in the secret store, never in settings. Link with the same pairing link a client uses.
  • Placement. Minimize (load + 1) / weight over reachable machines that have a matching Project. Load is running agents on the machine plus recent placements not yet visible. Ties are deterministic. One shared 10-agent cap per Project across machines. Offline machines are ineligible. An explicit machine never falls back.
  • Project and context. Match by repository identity, else title plus folder name. The agent runs in the peer's folder and reads that machine's AGENTS.md. Nothing is created on the peer.
  • Tool API. cp_agent_create gains an optional machine, bounded by settings. Results and list entries name the machine. cp_agent_read, cp_agent_stop and cp_thread_send work on remote agents by id or title. Standing agents stay local. Runtime mode is never looser than the coordinator's or caller's.
  • Results. A bridge polls each machine's shell snapshot. A finished request is appended into the asking thread as the same cp-push: message the local reactor uses, and the reactor is nudged (enqueueProject) to start it.

Review

A sub-agent reviewed the spec against the code (2 blockers, 12 should-fix). Addressed: the reactor never started an idle recipient after a bridge append (now enqueued); start failures never produced a result (now delivered as failed); opaque peer errors; 401 holds instead of failing agents; unlink cannot revoke the peer session; explicit trust statement plus a six-command allowlist; secret names encoded; per-entry settings map; create ordering with registry first and peer calls outside the create lock; ownership and routing for remote agents; per-machine polling; version check; peer create omits createdByThreadId and replyTo. Details at the end of the spec.

Local results (no remote CI)

Compared against origin/main at the same commit (b7744df).

  • Typecheck, per package: contracts 2, desktop 2, mobile 10 errors on both main and this branch (pre-existing, identical). shared, client-runtime, server, web: 0 on both.
  • Lint: exit 0, 731 warnings and 2 errors on both.
  • Tests, root pnpm test: identical to main. Only failure is build-desktop-artifact.test.ts (cross-architecture Windows probe), on both. The branch adds 5 passing tests each in contracts and shared.
  • Server (apps/server, full run): 51 failed | 5585 passed on the branch. The same 10 files and 51 tests fail on main (ThreadSettlementReactor storage cleanup x37, entrypoint symlink, AgentSessionScanner, Antigravity, CodexDriver, CursorProvider, ProviderRegistry, providerMaintenance, UsageService). Environment related and unrelated to this change.
  • New tests: 24 server files, 339 tests in agentMachines, mcp and reactors (placement, project matching, remote state, store, client, service, remote agents, bridge, handlers, thread send) plus 19 web logic tests, all passing.
  • Live check: two real servers. The peer client did descriptor, token exchange, shell, create, thread fetch, allowlist and bad-token cases against a real peer. In the real web app, linking a peer through the RPC from Settings showed it as Connected. Screenshots were taken locally; not attached.
  • Not verified live: a real cp_agent_create through an MCP session onto a peer, and the Add machine dialog beyond the successful link.

Decisions for Nick

  1. This machine is a candidate in Balance with its own preference. Manual only makes it coordination-only.
  2. One shared cap of 10 per Project across machines.
  3. Fallback to this machine is on by default when the chosen machine is unavailable, never for an explicit machine.
  4. Projects are not auto-created on the peer. A machine without a matching Project is skipped.
  5. Standing agents stay local.
  6. Remote agents show in the peer's sidebar, not the coordinator's Project (they appear in cp_agent_list). A local mirror row is a follow-up.
  7. Load is running agents, not CPU or memory (host resources are RPC only).
  8. Server-owned settings, separate from the client Load balancing switch; they do not sync.
  9. Older servers show a failed check in the section instead of hiding it; hiding needs a capability flag.
  10. Linking grants operate scope (the home can run any client command on the peer, mitigated by an allowlist), and tokens last 30 days with no refresh.

🤖 Generated with Claude Code

@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:XXL labels Sep 29, 2026
@nickrroberts
nickrroberts force-pushed the feat/multi-machine-agents branch from d068f90 to e5eed26 Compare September 29, 2026 22:15
nickrroberts and others added 4 commits September 29, 2026 16:15
Coordinators and standing agents can start one-off agents on other machines
linked to their server. Placement (this machine, one machine, or balanced by
load) is a server-owned setting under Settings > Connections > Agent machines.
Results, read, stop and send work across machines through a registry and a
bridge that pushes results into the asking thread.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
cp_agent_settle now works on a remote agent: it settles the peer thread when
no request is in flight, archives on request, and marks the record settled.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Follows the coordinator-owned settling rule: the bridge no longer settles a
remote agent after its result. cp_agent_settle does it, on the peer.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@nickrroberts
nickrroberts force-pushed the feat/multi-machine-agents branch from e5eed26 to c057681 Compare September 29, 2026 22:26
@nickrroberts
nickrroberts merged commit e79b4d2 into main Sep 29, 2026
3 of 12 checks passed
@nickrroberts
nickrroberts deleted the feat/multi-machine-agents branch September 29, 2026 22:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant