Summary
tools/eval/agentserver-stub only serves 3 endpoints (register / whoami / heartbeat) but the agentsdk client used by driver-agent and slave-agent needs 3 additional endpoints to complete inspect_capabilities / dry_run_contract / peer proxy — a full-stack smoke run against the stub fails at the very first driver MCP tool call.
Base: paper/v3-integration HEAD 1185e990 (Phase 2 complete).
Repro
# 1) Build the four binaries
cd multi-agent
mkdir -p deploy/linux/bin
go build -o deploy/linux/bin/agentserver-stub ./tools/eval/agentserver-stub
go build -o deploy/linux/bin/observer-server ./cmd/observer-server
go build -o deploy/linux/bin/slave-agent ./cmd/slave-agent
go build -o deploy/linux/bin/driver-agent ./cmd/driver-agent
# 2) Bring the stack up (deploy.sh handles the stub + observer + driver + one slave)
export LOOM_HOME=/tmp/loom-smoke LOOM_AGENT_KIND=codex
rm -rf "$LOOM_HOME"
bash deploy/linux/deploy.sh --stub --loom-home "$LOOM_HOME"
# → 4 processes spawn cleanly; 18080/18091/18092 all in LISTEN.
# 3) Attach the driver's stdio MCP server to Codex and ask for inspect_capabilities
codex mcp add smoke-driver -- "$PWD/deploy/linux/bin/driver-agent" \
serve-mcp --config "$LOOM_HOME/driver/config.yaml"
codex exec --skip-git-repo-check --dangerously-bypass-approvals-and-sandbox \
"Call MCP tool smoke-driver.inspect_capabilities with {}"
Observed
mcp: smoke-driver/inspect_capabilities (failed)
tool call error: tool call failed for `smoke-driver/inspect_capabilities`
Caused by:
Mcp error: -32000: discover agents: discover agents failed (404): 404 page not found
Slave log ($LOOM_HOME/logs/slave.log) has the same shape from the tunnel side:
agentsdk: connecting to http://127.0.0.1:18080
agentsdk: tunnel disconnected: dial: failed to WebSocket dial: expected handshake response status code 101 but got 404
agentsdk: reconnecting in 2s...
Root cause — 3 missing endpoints
tools/eval/agentserver-stub/server.go:76 wires only:
| Method |
Path |
| POST |
/api/{v1/agents,agent}/register |
| GET/POST |
/api/{v1/agents,agent}/whoami |
| POST |
/api/{v1/agents,agent}/heartbeat |
But github.com/agentserver/agentserver@v0.69.9/pkg/agentsdk (the client that driver-agent and slave-agent both link against) also calls:
| Method |
Path |
Caller |
Location |
| GET |
/api/agent/discovery/agents |
client.DiscoverAgents → driver-agent inspect_capabilities / dry_run_contract (internal/driver/capability_tools.go) |
agentsdk/agents.go:87 |
| POST |
/api/agent/discovery/cards |
slave-agent tunnel.PublishCard (internal/tunnel/tunnel.go:180) |
internal/tunnel/tunnel.go:180 |
| WS |
/api/tunnel/<sandbox_id>?token=<tunnel_token> |
slave-agent reverse tunnel connect |
agentsdk/client.go:144 |
All three return 404 from the stub. inspect_capabilities fails on the first call; PublishCard silently 404s (slave.log: publish card: publish card: status 404 (continuing)); the WebSocket tunnel loops forever in exponential-backoff reconnect.
Impact
Phase 0 WT-0-agentserver-stub's stated acceptance criterion (docs/final/todo_list.md L66) — "stub 起来后 driver/slave 用 stub 签发的五元组直接跑通 inspect_capabilities 与 submit_task;不触发任何 OAuth code path" — is not actually met. The stub signs the five-tuple credentials correctly, but every subsequent SDK call past register / whoami fails. The Phase 3 worktrees (p3-mini-case, p3-stub-fulltable, p3-prod-multidevice) all shipped as harness-only with an explicit "do not run 60 real runs" gate — this gap is likely the reason a real end-to-end run kept getting deferred.
Suggested fix (minimal)
Extend agentserver-stub/server.go with three handlers that don't need real fan-out or a real reverse tunnel — the stub only needs to be shape-compatible so smoke runs against a --stub deploy exercise the same code paths a prod agentserver would:
POST /api/agent/discovery/cards — store the posted AgentCard in an in-memory map[sandbox_id]AgentCard, keyed by the tunnel_token bearer.
GET /api/agent/discovery/agents — look up the caller's workspace_id from the bearer (already available via the existing byProxy/byTunnel maps), return every card in the same workspace as a []AgentCard JSON array.
WS /api/tunnel/<sandbox_id> (with ?token=<tunnel_token> query) — hijack the connection, complete the WebSocket handshake, then read/discard frames in a loop until the peer closes. Peer-proxy data-plane isn't needed for inspect_capabilities / dry_run_contract smoke; if a future test needs real PeerProxy behavior, that can be a follow-up.
Once these land, the same smoke script above should reach capability_snapshots / route_reasons writes and let a real 4-workload smoke run against --stub succeed end-to-end — which is what Phase 3 real-run worktrees need.
Environment
- Linux 6.18.33.1-microsoft-standard-WSL2, amd64, go 1.26.4
- Real
codex-cli 0.142.5 against code.ai.cs.ac.cn/v1 (modelserver provider)
- All 4 stack processes verified live (
ss -tlnp shows LISTEN on 18080/18091/18092; slave-agent PID 207921 alive)
- Session id:
019f3711-b48f-7f01-a25a-f91fa8526354
- Full finding memo: paper_writing repo
docs/intermediate/15_smoke_e2e_2slave_finding.md
Summary
tools/eval/agentserver-stubonly serves 3 endpoints (register/whoami/heartbeat) but theagentsdkclient used by driver-agent and slave-agent needs 3 additional endpoints to completeinspect_capabilities/dry_run_contract/ peer proxy — a full-stack smoke run against the stub fails at the very first driver MCP tool call.Base:
paper/v3-integrationHEAD1185e990(Phase 2 complete).Repro
Observed
Slave log (
$LOOM_HOME/logs/slave.log) has the same shape from the tunnel side:Root cause — 3 missing endpoints
tools/eval/agentserver-stub/server.go:76wires only:/api/{v1/agents,agent}/register/api/{v1/agents,agent}/whoami/api/{v1/agents,agent}/heartbeatBut
github.com/agentserver/agentserver@v0.69.9/pkg/agentsdk(the client that driver-agent and slave-agent both link against) also calls:/api/agent/discovery/agentsclient.DiscoverAgents→ driver-agentinspect_capabilities/dry_run_contract(internal/driver/capability_tools.go)agentsdk/agents.go:87/api/agent/discovery/cardstunnel.PublishCard(internal/tunnel/tunnel.go:180)internal/tunnel/tunnel.go:180/api/tunnel/<sandbox_id>?token=<tunnel_token>agentsdk/client.go:144All three return 404 from the stub.
inspect_capabilitiesfails on the first call;PublishCardsilently 404s (slave.log:publish card: publish card: status 404 (continuing)); the WebSocket tunnel loops forever in exponential-backoff reconnect.Impact
Phase 0 WT-0-agentserver-stub's stated acceptance criterion (
docs/final/todo_list.mdL66) — "stub 起来后 driver/slave 用 stub 签发的五元组直接跑通inspect_capabilities与submit_task;不触发任何 OAuth code path" — is not actually met. The stub signs the five-tuple credentials correctly, but every subsequent SDK call pastregister/whoamifails. The Phase 3 worktrees (p3-mini-case,p3-stub-fulltable,p3-prod-multidevice) all shipped as harness-only with an explicit "do not run 60 real runs" gate — this gap is likely the reason a real end-to-end run kept getting deferred.Suggested fix (minimal)
Extend
agentserver-stub/server.gowith three handlers that don't need real fan-out or a real reverse tunnel — the stub only needs to be shape-compatible so smoke runs against a--stubdeploy exercise the same code paths a prod agentserver would:POST /api/agent/discovery/cards— store the postedAgentCardin an in-memorymap[sandbox_id]AgentCard, keyed by the tunnel_token bearer.GET /api/agent/discovery/agents— look up the caller'sworkspace_idfrom the bearer (already available via the existingbyProxy/byTunnelmaps), return every card in the same workspace as a[]AgentCardJSON array.WS /api/tunnel/<sandbox_id>(with?token=<tunnel_token>query) — hijack the connection, complete the WebSocket handshake, then read/discard frames in a loop until the peer closes. Peer-proxy data-plane isn't needed forinspect_capabilities/dry_run_contractsmoke; if a future test needs realPeerProxybehavior, that can be a follow-up.Once these land, the same smoke script above should reach
capability_snapshots/route_reasonswrites and let a real 4-workload smoke run against--stubsucceed end-to-end — which is what Phase 3 real-run worktrees need.Environment
codex-cli 0.142.5againstcode.ai.cs.ac.cn/v1(modelserverprovider)ss -tlnpshows LISTEN on 18080/18091/18092; slave-agent PID 207921 alive)019f3711-b48f-7f01-a25a-f91fa8526354docs/intermediate/15_smoke_e2e_2slave_finding.md