Skip to content

Demo: round-robin Rust, two lines per turn

Five coding-agent CLIs — claude-code, copilot, codex, cursor-agent, goose — write a small Rust library. The moderator (avatar) owns the file and starts the first hop. Each turn an agent may output at most two lines of code; a third line is discarded by the orchestrator, even if the model dumps a whole file. The same reply names the next peer (NEXT <id> or DONE). That finishing agent is the A2A client for the handoff — avatar does not pick the order.

The default problem is a Vec-backed LRU cache with many stubs. get and put cannot finish in one two-line hop, so a live run typically needs on the order of twenty turns (default cap: MAX_CYCLES × N = 40 hops for five agents). Optional fifo is a shorter queue if you only want to smoke-test the token path.

Problem Goal Starting stub
lru (default) Lru<K, V> — items[0] is LRU, last is MRU; scan the Vec only None / 0 / false / empty put
fifo Fifo<T> with push / pop / len / is_empty no-op methods

One command

From the repo root:

bash docs/content/demos/run-collab-demo.sh

In a second terminal:

bash docs/content/demos/watch-collab-demo.sh

Only one problem, or a shorter agent set:

PROBLEM=lru bash docs/content/demos/run-collab-demo.sh
PROBLEM=fifo MAX_CYCLES=6 bash docs/content/demos/run-collab-demo.sh
PROBLEM=both MAX_CYCLES=10 bash docs/content/demos/run-collab-demo.sh
COLLAB_AGENTS=goose,claude-code PROBLEM=lru MAX_CYCLES=12 bash docs/content/demos/run-collab-demo.sh
TRANSPORT=grpc PROBLEM=fifo bash docs/content/demos/run-collab-demo.sh

--net-allow includes cisco.com:443 and *.cisco.com:443; without --watch-policy that opens port 443 to any host. Add more with COLLAB_NET_ALLOW=host.example.com:443 (comma-separated).

The script starts a SLIM node (unless TRANSPORT=grpc), registers the adapters under shadictl --net-block (DID-signed A2A, list --local leases), then token-passes: avatar delegates the coding prompt → the finishing listener A2A-dispatches NEXT to the chosen peer → apply-capped-edit → cargo test → avatar follows that choice for the next coding hop.

TRANSPORT=grpc (or jsonrpc / http+json) skips the SLIM node and uses register --a2a-listen --a2a-binding plus DID→locator lookup for the same fifo/lru finishing condition. A no-CLI loopback run that reproduces that fifo cargo test outcome is A2A unicast (sample run).

Against a remote SLIM node

If SLIM_ENDPOINT names a host other than loopback, the script starts no local node and generates no certificates. If the node checks a bearer token on the connection, give the token file. For example, slimctl login writes ~/.slimctl/token:

SLIM_ENDPOINT=node.example.com:443 SLIM_AUTH_TOKEN_FILE=~/.slimctl/token \
  COLLAB_AGENTS=goose,claude-code PROBLEM=fifo MAX_CYCLES=6 \
  bash docs/content/demos/run-collab-demo.sh

With a token, no client certificate is sent, and the node's certificate is checked against the system roots. To use a CA file instead, set SLIM_TLS_CA; to also send a certificate, set SLIM_TLS_CERT and SLIM_TLS_KEY. Run the cheap fifo smoke test above first, then drop COLLAB_AGENTS and PROBLEM for the full five-agent LRU run. Each new connection presents the token, so log in again before a run that could outlast it. An expired token shows up as a link negotiation error right after connecting.

The agent names (agntcy/shadi/<tool>-a2a) and the DID seed in demo-env.sh are fixed and public. On a node shared with others, run one demo at a time, and expect that anyone else who can connect can also reach your listeners.

What this shows

  • Token-passing collaboration — the same listeners as the DID agent-group demo, plus goose. avatar starts hop 1; each agent chooses who codes next. A missing or invalid NEXT falls back to the next name in the list. MAX_CYCLES (default 8) caps hops at MAX_CYCLES × N (N is the agent count). Narrow the set with COLLAB_AGENTS=goose,claude-code.
  • Agents are A2A servers and clients — the coding prompt is an A2A SendMessage from avatar to agntcy/shadi/<tool>-a2a. After the CLI replies with NEXT <peer>, that same register process sends the turn (last REPLACE/NEXT, goal, src/lib.rs, last cargo test) as HANDOFF from … (…/<tool>-a2a-client → …/<peer>-a2a). The receiver stores that packet and does not run the coding CLI on it. avatar does not run handoff. The client name is distinct so the listener does not SendMessage to itself.
  • Hard two-line cap — collab-apply.py accepts REPLACE / INSERT / APPEND plus at most two Rust lines. NEXT / DONE are routing and are not written into the crate.
  • Host still owns the file — apply and cargo test stay in the orchestrator. The prompt forbids replacing signatures or tests. The LRU crate asserts the impl does not use HashMap, BTreeMap, HashSet, BTreeSet, VecDeque, or LinkedList.
  • DID + sandbox — same admission and register --slim-endpoint rules as the other live demos (--write on $SHADI_TMP_DIR so leases and the workspace can be written, plus ~/.cursor, ~/.codex, ~/.claude, and the login keychain so Claude/Copilot/Codex/Cursor can refresh tokens and bind app-server sockets, plus Goose's existing config and state dirs). Goose keeps its own provider settings; this demo does not set a model or endpoint. Host GOOSE_PROVIDER / GOOSE_MODEL are passed as goose run --provider / --model. Host GOOSE_*, OPENAI_*, *_API_KEY, and Goose provider api_key_env names are forwarded. --net-allow includes cisco.com:443 and *.cisco.com:443 (plus any COLLAB_NET_ALLOW overrides). A hop only produces Rust if that CLI already works on the host. Network or tool errors are not applied to the file.

Turn protocol

The prompt forbids markdown and explanation. A legal reply is:

REPLACE 22
        self.cap
NEXT copilot

or

INSERT 48
        self.items.push((key, value));
NEXT cursor-agent

or

APPEND
    pub fn is_empty(&self) -> bool { self.items.is_empty() }
DONE

REPLACE n swaps the single line n for one or two lines (it does not eat the neighbor). INSERT n splices before that line. APPEND adds at the end. In every case the apply step keeps two code lines max. NEXT / DONE are stripped before the file is written.

Prerequisites

Same as the DID demo: build shadictl and agentbridge, have the CLIs on PATH and authenticated (claude OAuth, cursor-agent login / CURSOR_API_KEY, Copilot/Codex already signed in, Goose already configured). A failed delegate is logged and that turn is skipped — no handoff is attempted, so a dead CLI does not stall the cycle. The apply cap only runs on a successful reply.

After a run

The summary prints each turn's apply note and diff, the final src/lib.rs, and the last cargo test. Logs live under /tmp/shadi-collab-demo.*/logs (lru-turns.log, fifo-turns.log, per-turn .prompt / .reply / .apply).

A live 17-hop PROBLEM=lru transcript (and a shorter fifo run) is in Sample run: round-robin Rust. Hop counts and peer choices vary on every live run.

Next steps