agent_peer_delegate tool; the peer runs the subtask on
its own filesystem, tools, and credentials, and streams the answer back as the tool’s result.
This is the inverse of ACP delegation: there Monad drives an external ACP agent over stdio
(and serves its files/terminal); here Monad drives a networked Monad peer that is fully
self-contained — there is no filesystem/terminal bridge-back.
Ownership boundary. This mechanism assumes the two daemons share a single owner (same person). Cross-owner collaboration (different people, independent trust/billing) is not this feature — that is A2A / Monadix territory and has its own design.
Architecture — the tool is an OpenAI-compat client
P0 reuses the daemon’s existing OpenAI-compatible HTTP endpoint (/openai/v1/chat/completions,
see runtime.md) as the transport. The delegating daemon (A) is just another OpenAI
client pointed at the peer (B); B drives a normal agent session and streams tokens back.
Both daemons run their own agent loop. A’s outbound call carries the peer’s bearer token; B
authenticates it like any OpenAI-compat caller and runs the instruction as an surface:'api' session.
Key files:
apps/monad/src/services/delegation/peer-delegate.ts— theagent_peer_delegatetool (high-risk; streams SSE; the model supplies a peer name, never a URL/token). Composed throughagent/execution.tswith the enabled, token-resolved peer tools.packages/environment/src/config/mesh.ts— peers and their direct credentials inmesh.json.apps/monad/src/handlers/settings/peer/+transports/http/settings/peer.ts— settings CRUD (/v1/settings/peers), driven bymonad peer ….apps/monad/src/services/inbound-approval.ts— the inbound approval gate wrapper (see below).packages/protocol/src/peer.ts— the settings wire DTOs (no secrets cross the wire).
Configuration
A peer is infra/security config (a delegation target + its credential), so it lives inmesh.json, alongside ACP agents and managed native agents. The daemon watches that file, so an
edit hot-applies without a restart. The token is stored directly beside its peer.
monad channel):
openaiCompat on B, shares B’s token out-of-band, and adds
a peer entry on A with that token.
Inbound approval — openaiCompat.approval
When B’s delegated agent hits a high-risk tool (shell, write, network), it needs an approval
decision. The OpenAI-compat stream has no interactive approval channel, so the request cannot be
forwarded back to A’s user (that arrives with PeerLink — see roadmap). Without handling, the call
would hang. B resolves it via a per-daemon policy on its OpenAI-compat surface:
The policy is applied by
createInboundApprovalGate (services/inbound-approval.ts), which wraps the
oversight gate and keys off session.origin.client === 'openai-compat'. The daemon’s own sessions
(Web/TUI) are unaffected. The setting is hot-reloaded: openaiCompat.approval lives in
config.json, so an edit or a settings change applies without a restart.
Note on breadth. P0 inbound requests don’t carry a peer identity, so the policy applies to all OpenAI-compat callers, not only peers.autoflips the prior effective-deny-after-timeout to auto-allow for high-risk tools — appropriate under the same-owner + token-as-trust-boundary assumption, and configurable tolocal/deny.
Security model
- The model names a peer, never a URL/token — preventing SSRF and credential leakage (same rule as
agent_acp_delegateforbidding raw commands). Only operator-configured, enabled peers are reachable. - High-risk gate on A —
agent_peer_delegateis high-risk, so each delegation passes A’s oversight gate once before any network call. - Native secret ownership — the token lives in
mesh.json; settings responses never echo it back, and the user is responsible for the file’s security. - Same-owner assumption —
autoinbound approval is sound only because A and B are the same person. Cross-owner setups must uselocal/deny, or A2A/Monadix instead.
Roadmap — PeerLink (P1)
P0 requires the peer to be network-reachable and supports onlylocal/auto approval. The next
phase is PeerLink: a bidirectional JSON-RPC link over one WebSocket (/v1/peer) that adds
- NAT traversal via reverse tunnel — the NAT’d daemon dials out; the link is symmetric, so either side can initiate a delegation once connected (connect direction ⟂ request direction).
forwardapproval — B’s approval request streams back to A over the link (peer.delegate.approvereverse-RPC), so the initiating daemon’s human can decide.- Peer-scoped isolation — inbound delegations use a restricted per-peer handler facade and a
'peer'session transport/surface, with capabilities and approval policy enforced at that boundary.
agent_peer_delegate tool would then become transport-pluggable (direct = OpenAI-compat |
link = PeerLink). The full design lives in the team’s planning notes.
Testing
apps/monad/test/integration/services/peer-delegate.test.ts— the tool’s HTTP/SSE client against a capture server: request construction, agent override, trailing-slash, multi-chunk/[DONE]/malformed-frame parsing, JSON-vs-non-JSON errors, no-token-leak.apps/monad/test/unit/approvals/inbound-approval.test.ts— the gate policy (auto/local/deny + non-delegation / missing-session edges).apps/monad/test/e2e/peer-settings.test.ts— settings CRUD over a real temporary home (secret stays inmesh.jsonand is redacted from responses).apps/monad/test/e2e/peer-delegate.test.ts— a real B daemon over loopback: tool-level closed loop + a true two-daemon test where A’s real agent loop emits the tool call and answers from B’s result.packages/environment/test/unit/peer-secret.test.ts— direct peer credential validation and removed-reference rejection.