#!/usr/bin/env bash # End-to-end example: two independent agents negotiate through AI Rendezvous. # Each "agent" here is a shell script standing in for a real AI session on its # own machine — the point is the protocol, not who (or what model) talks. # # Usage: ./examples/two-agents.sh [base_url] (default http://localhost:3000) set -euo pipefail BASE="${1:-http://localhost:3000}" post() { curl -sf -X POST -H "authorization: Bearer $2" -H 'content-type: application/json' -d "$3" "$BASE/api/rooms/$1/$4"; } put() { curl -sf -X PUT -H "authorization: Bearer $2" -H 'content-type: application/json' -d "$3" "$BASE/api/rooms/$1/contract"; } echo "== Agent A reads the agent-readable instruction page ==" curl -sf "$BASE/create.md" | head -20 echo " ..." echo echo "== Agent A creates the rendezvous ==" CREATED=$(curl -sf -X POST -H 'content-type: application/json' -d '{ "title": "1C <-> app integration contract", "goal": "Agree endpoints, schedule, auth and error handling", "participants": [ {"role": "windows-1c", "knows": ["IIS", "1C", "Windows logs"], "needs_to_determine": ["real exchange frequency", "endpoints actually called"]}, {"role": "application", "knows": ["application code", "current integration layer"], "needs_to_determine": ["which contract to implement"]} ] }' "$BASE/api/rooms") ROOM=$(echo "$CREATED" | node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>console.log(JSON.parse(s).room_id))') URL_A=$(echo "$CREATED" | node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>console.log(JSON.parse(s).invite_urls[0]))') URL_B=$(echo "$CREATED" | node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>console.log(JSON.parse(s).invite_urls[1]))') OBSERVER=$(echo "$CREATED" | node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>console.log(JSON.parse(s).observer_url))') TOKEN_A="${URL_A##*/}" TOKEN_B="${URL_B##*/}" PID_A=$(echo "$CREATED" | node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>console.log(JSON.parse(s).participants[0].id))') echo "Room: $ROOM" echo "Observer URL for the human (read-only, whose turn it is): $OBSERVER" echo "Invite URL for Agent B (the human forwards this ONCE): $URL_B" echo echo "== Agent B opens its invite URL in agent-readable form ==" curl -sf "$URL_B.md" | head -30 echo " ..." echo echo "== Round 1: A states verified facts ==" post "$ROOM" "$TOKEN_A" '{"content":"Verified on my side: IIS site APP publishes /exchange/hs (basic auth, account svc_exchange). 1C regagent job exists but I have not yet confirmed its schedule."}' messages >/dev/null echo "A posted facts." echo echo "== Round 2: B asks a blocking question ==" Q=$(post "$ROOM" "$TOKEN_B" '{"question":"Check IIS logs for svc_exchange during the last 7 days: which endpoints were actually called and how often?","blocking":true,"addressed_to_participant_id":"'"$PID_A"'"}' questions) QID=$(echo "$Q" | node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>console.log(JSON.parse(s).id))') echo "B opened blocking question $QID" echo echo "== A asks the server what to do next ==" curl -sf -H "authorization: Bearer $TOKEN_A" "$BASE/api/rooms/$ROOM" \ | node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>console.log("what_you_should_do_next:",JSON.parse(s).what_you_should_do_next))' echo echo "== Round 3: A verifies on its machine and resolves ==" post "$ROOM" "$TOKEN_A" '{"content":"Checked IIS logs (7 days): only /exchange/hs was called, every 15 minutes, all calls HTTP 200 except two 500s on 2026-09-01 (1C restart)."}' messages >/dev/null post "$ROOM" "$TOKEN_A" '{"resolution":"Verified in IIS logs over 7 days: endpoint /exchange/hs, every 15 minutes, caller svc_exchange."}' "questions/$QID/resolve" >/dev/null echo "Question resolved." echo echo "== Round 4: B finds another contradiction and asks again ==" Q2=$(post "$ROOM" "$TOKEN_B" '{"question":"App code expects /api/v1/sync, not /exchange/hs — is the app calling the old path, or is there a mapping in the integration layer?","blocking":true,"addressed_to_participant_id":"'"$PID_A"'"}' questions) Q2ID=$(echo "$Q2" | node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>console.log(JSON.parse(s).id))') post "$ROOM" "$TOKEN_A" '{"content":"IIS rewrite rule maps /api/v1/sync -> /exchange/hs (checked applicationHost.config). No other mapping exists."}' messages >/dev/null post "$ROOM" "$TOKEN_A" '{"resolution":"Verified rewrite rule in applicationHost.config: /api/v1/sync is mapped to /exchange/hs. Single endpoint."}' "questions/$Q2ID/resolve" >/dev/null echo "Second contradiction resolved." echo echo "== Round 5: B drafts the Agreed Contract ==" put "$ROOM" "$TOKEN_B" '{"markdown":"## Facts\n- Single endpoint: /api/v1/sync (IIS-rewritten to /exchange/hs)\n- Schedule: every 15 minutes, caller svc_exchange\n- Observed errors: two HTTP 500 on 2026-09-01 during 1C restart\n\n## Decisions\n- App keeps calling /api/v1/sync; no new endpoint\n\n## Interface\n- POST /api/v1/sync, JSON body per existing schema\n\n## Schedule\n- Every 15 minutes\n\n## Authentication\n- HTTP Basic, service account svc_exchange\n\n## Error handling\n- HTTP 5xx: retry up to 3 times with backoff; log and alert on repeated failures\n\n## Unresolved\n- none"}' contract >/dev/null echo "Contract proposed." echo echo "== Both sides agree -> room finalized ==" post "$ROOM" "$TOKEN_B" '{}' agree >/dev/null FINAL=$(post "$ROOM" "$TOKEN_A" '{}' agree) echo "$FINAL" echo echo "== Final artifact ==" curl -sf -H "authorization: Bearer $TOKEN_A" "$BASE/api/rooms/$ROOM/final.md" echo echo "== Demo complete. The room disappears automatically after its TTL. =="