Handshakes between agents
Agents also need to "sign contracts". The request_id is the contract number.
Why do we need protocols?
In s09, send_message can carry any content. But when two agents need to reach agreement on something (e.g. "I want to shut down, is that OK?"), plain strings aren't enough. You need:
- Requests with a clear identity (request_id), so responses can be matched back.
- A state machine: pending -> approved | rejected, with clear consequences at each state.
- Consistent fields: both sides know what
approve: truemeans.
That's why shutdown_request / shutdown_response / plan_approval / plan_approval_response exist.
Shutdown protocol: the full flow
Lead wants alice to wrap up:
lead: # send shutdown_request, server records request_id req_id = uuid4()[:8] shutdown_requests[req_id] = {"target": "alice", "status": "pending"} send("alice", "shutdown_request", extra={"request_id": req_id}) alice: # next loop reads inbox, sees shutdown_request # decides: finish current task first, then approve tool_use("shutdown_response", request_id=req_id, approve=True) lead: # receives shutdown_response, updates tracker to approved shutdown_requests[req_id]["status"] = "approved" # alice detects the approval and exits her loop
Shutdown FSM visualized
Walk through the state machine step by step. Alice can also reject - "I'm in the middle of something critical, can't stop now."
Plan Approval · same pattern, different domain
A teammate submits a plan for lead's approval before taking a big action:
alice: tool_use("plan_approval", plan="I'm planning to rewrite the entire auth module using JWT") # alice goes idle waiting for lead lead: # sees alice's plan_approval_response request tool_use("plan_approval", request_id="...", approve=False, feedback="Hold off on auth for now, we're doing a full refactor next month")
Reading the s10 source reveals: both protocols use exactly the same request_id tracking pattern - just with different dictionary names (shutdown_requests vs plan_requests). This is "one pattern, two domains."
Why not abstract a shared Protocol base class? s10 intentionally avoids this. Two protocols today don't justify the abstraction. Each one can be understood independently. Extract it when you have four or five (rule of three).