File size: 7,300 Bytes
b8df5df
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
#!/bin/bash
# End-to-end test of the remote-agent protocol against a throwaway server.
SC=/tmp/claude-1000/-data-workspaces-agent-manager/bb5087f8-c501-4fae-ba56-6c57f9ab745e/scratchpad
B=localhost:7901
pass=0; fail=0
ok()   { pass=$((pass+1)); echo "  PASS  $1"; }
bad()  { fail=$((fail+1)); echo "  FAIL  $1"; echo "        got: $2"; }
check() { if [ "$2" = "$3" ]; then ok "$1"; else bad "$1" "$2 (want $3)"; fi; }

echo "== 1. creation (the §8 flow: name it, queue a task) =="
R=$(curl -s -X POST -H 'content-type: application/json' \
  -d '{"cli":"remote","name":"laptop","prompt":"have a look at the failing test in trl/trainer"}' $B/api/sessions)
ID=$(echo "$R" | jq -r .id)
check "path is remote-agents/<slug>" "$(echo "$R" | jq -r .path)" "remote-agents/laptop"
check "remote slug minted"          "$(echo "$R" | jq -r .remote.name)" "laptop"
check "queued prompt is seq 1"      "$(curl -s $B/api/remote/laptop/ping | jq -r .seq)" "1"
check "first message is on disk"    "$(ls $SC/td3/workspaces/remote-agents/laptop/ | tr '\n' ' ')" "00001-user.md README.md "

echo "== 2. ping does not fake liveness (nothing is polling yet) =="
check "state before any poll" "$(curl -s $B/api/tree | jq -r '.sessions[]|select(.cli=="remote")|.state')" "stopped"

echo "== 3. hello + the blocking stream returns queued work IMMEDIATELY =="
curl -s -X POST -H 'content-type: application/json' \
  -d '{"harness":"claude","cwd":"~/src/trl","host":"macbook"}' $B/api/remote/laptop/hello >/dev/null
S=$(date +%s)
OUT=$(curl -s -N --max-time 30 "$B/api/remote/laptop/stream?since=0&wait=20")
EL=$(( $(date +%s) - S ))
check "returned at once, not after wait" "$([ $EL -le 3 ] && echo fast || echo "slow:${EL}s")" "fast"
check "first line is :connected"  "$(echo "$OUT" | head -1)" ":connected"
check "delivers the queued task"   "$(echo "$OUT" | tail -1 | jq -r '.messages[0].text')" "have a look at the failing test in trl/trainer"
check "cursor advances"            "$(echo "$OUT" | tail -1 | jq -r .seq)" "1"

echo "== 4. state while work is outstanding = working =="
check "outstanding -> working" "$(curl -s $B/api/tree | jq -r '.sessions[]|select(.cli=="remote")|.state')" "working"

echo "== 5. the agent replies; state flips to listening (waiting) =="
curl -s -X POST -H 'content-type: text/plain' --data-binary @- $B/api/remote/laptop/messages >/dev/null <<'EOF'
It's the tokenizer fixture — `pad_token` is None on the Qwen config.
EOF
check "answered -> waiting" "$(curl -s $B/api/tree | jq -r '.sessions[]|select(.cli=="remote")|.state')" "waiting"
check "agent message on disk" "$([ -f $SC/td3/workspaces/remote-agents/laptop/00003-agent.md ] && echo yes || echo no)" "yes"

echo "== 6. its own words are never echoed back to it =="
check "since=1 gives the agent nothing" \
  "$(curl -s "$B/api/remote/laptop/messages?since=1&agent=1" | jq -r '.messages|length')" "0"
check "but the UI sees all of it" \
  "$(curl -s "$B/api/sessions/$ID/remote" | jq -r '.messages|length')" "3"

echo "== 7. a wait that expires returns empty (the normal idle state) =="
S=$(date +%s)
OUT=$(curl -s -N --max-time 20 "$B/api/remote/laptop/stream?since=9&wait=5")
EL=$(( $(date +%s) - S ))
check "waited ~5s"        "$([ $EL -ge 4 ] && [ $EL -le 9 ] && echo yes || echo "no:${EL}s")" "yes"
check "empty, not an error" "$(echo "$OUT" | tail -1 | jq -r '.messages|length')" "0"

echo "== 8. the Overview reply box reaches a remote agent (deliver shim) =="
curl -s -X POST -H 'content-type: application/json' -d '{"text":"fix it and run the suite"}' $B/api/sessions/$ID/input >/dev/null
check "delivered as a user message" \
  "$(curl -s "$B/api/remote/laptop/messages?since=3&agent=1" | jq -r '.messages[0].text')" "fix it and run the suite"
check "attributed to the operator" \
  "$(curl -s "$B/api/remote/laptop/messages?since=3&agent=1" | jq -r '.messages[0].from')" "lvwerra"

echo "== 9. an in-Space agent can message the laptop (agent-to-agent, free) =="
curl -s -X POST -H 'content-type: application/json' -d '{"cli":"shell","name":"helper"}' $B/api/sessions >/dev/null
H=$(curl -s $B/api/tree | jq -r '.sessions[]|select(.name=="helper")|.id')
curl -s -X POST -H 'content-type: text/plain' --data-binary 'can you check the tokenizer too?' \
  "$B/api/agents/$ID/prompt?from=$H" >/dev/null
check "peer prefix preserved" \
  "$(curl -s "$B/api/remote/laptop/messages?since=4&agent=1" | jq -r '.messages[0].text')" "[message from helper:] can you check the tokenizer too?"
check "peer recorded in from:" \
  "$(curl -s "$B/api/remote/laptop/messages?since=4&agent=1" | jq -r '.messages[0].from')" "helper"

echo "== 10. Disconnect closes an OPEN poll at once (not at the end of wait) =="
( curl -s -N --max-time 40 "$B/api/remote/laptop/stream?since=99&wait=30" > $SC/openpoll.txt ) &
sleep 2
S=$(date +%s)
curl -s -X POST -H 'content-type: application/json' -d '{"paused":true}' $B/api/sessions/$ID/remote/paused >/dev/null
wait
EL=$(( $(date +%s) - S ))
check "poll closed immediately"  "$([ $EL -le 3 ] && echo yes || echo "no:${EL}s")" "yes"
check "and it said stop:true"    "$(tail -1 $SC/openpoll.txt | jq -r .stop)" "true"
check "paused -> stopped"         "$(curl -s $B/api/tree | jq -r '.sessions[]|select(.cli=="remote")|.state')" "stopped"
check "a new poll is refused too" "$(curl -s "$B/api/remote/laptop/stream?since=0&wait=5" | jq -r .stop)" "true"
check "posting is refused"        "$(curl -s -o /dev/null -w '%{http_code}' -X POST -H 'content-type: text/plain' --data-binary 'hi' $B/api/remote/laptop/messages)" "409"

echo "== 11. reconnect =="
curl -s -X POST -H 'content-type: application/json' -d '{"paused":false}' $B/api/sessions/$ID/remote/paused >/dev/null
check "unpaused, nothing polling -> stopped" "$(curl -s $B/api/tree | jq -r '.sessions[]|select(.cli=="remote")|.state')" "stopped"
check "stream works again" "$(curl -s -N --max-time 10 "$B/api/remote/laptop/stream?since=0&wait=5" | tail -1 | jq -r '.messages|length>0')" "true"

echo "== 12. no terminal, no spawn, no usage inflation =="
check "/ws refuses it" "$(node -e "
import('/app/server/node_modules/ws/index.js').then(({default:pkg})=>{
  const {WebSocket}=pkg; const ws=new WebSocket('ws://$B/ws?session=$ID');
  ws.on('message',m=>{console.log(String(m).trim());process.exit(0)});
  ws.on('error',()=>{console.log('error');process.exit(0)});
  setTimeout(()=>{console.log('timeout');process.exit(0)},5000);
})" 2>/dev/null)" "[this pane has no terminal — it talks to an agent elsewhere]"
check "not spawnable by an agent" \
  "$(curl -s -X POST -H 'content-type: text/plain' --data-binary 'go' "$B/api/agents?from=$H&cli=remote" | jq -r '.error|split(" ")[0:6]|join(" ")')" \
  "a remote agent has to be"
check "absent from the spawn catalog" \
  "$(curl -s "$B/api/agents?from=$H" | jq -r '[.clis[].id]|index("remote")')" "null"
check "excluded from token usage" \
  "$(curl -s $B/api/traces | jq -r '[.sessions[]|select(.cli=="remote")]|length')" "0"
check "but present in the Overview" \
  "$(curl -s $B/api/meta | jq -r '[.sessions[]|select(.cli=="remote")]|length')" "1"
check "with a folder-built digest" \
  "$(curl -s $B/api/meta | jq -r '.sessions[]|select(.cli=="remote")|.digest.lastAssistantText' | head -c 20)" "It's the tokenizer f"

echo
echo "  $pass passed, $fail failed"
[ $fail -eq 0 ] || exit 1