Handshake protocol between two agents
Agents also need to "make contracts" with each other. request_id is the contract number.
Why an agreement?
In s09, send_message can upload any content. But when two agents want to reach a consensus about something (such as "I want to shut down, can I?"), just sending a string is not enough - you need:
- The request has a clear identity (request_id) so that the response is accurate.
- State machine: pending → approved | rejected, the consequences of each state are clear.
- Consistent fields: Both parties know what
approve: truemeans.
This is why shutdown_request / shutdown_response / plan_approval / plan_approval_response exist.
Shutdown protocol entire process
lead wants alice to get off work:
lead: # Send shutdown_request, the 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: # Read the mailbox in the next loop and see shutdown_request # Decision: finish the work at hand before returning and approve tool_use("shutdown_response", request_id=req_id, approve=True) lead: # Receive shutdown_response, update tracker to approved shutdown_requests[req_id]["status"] = "approved" # The alice thread detects shutdown acceptance and sets status=shutdown to exit the loop
Shutdown FSM visualization
Watch each step of the state machine. Alice can also reject - saying "I am doing critical work and cannot stop now".
Plan Approval · Same model, different domain
teammate submits a plan before launching a big move and please lead for approval:
alice: tool_use("plan_approval", plan="I plan to rewrite all auth modules with jwt") # alice continues idle and waits for lead lead: # See alice’s plan_approval_response request tool_use("plan_approval", request_id="...", approve=False, feedback="Don't touch auth yet, we will do an overall refactor next month")
Looking at the s10 source code, you will find: The two protocols use exactly the same request_id tracking mode - just the dictionary names are changed (shutdown_requests vs plan_requests). This is "one model, two domains".
Why not abstract a generic Protocol base class? s10 is deliberately not abstract. Reason: There are only two protocols now, and the benefits of abstraction are not obvious yet; if you write two explicitly, each one can be understood independently. of three).