mirror of
https://github.com/CopilotKit/CopilotKit.git
synced 2026-09-14 16:26:20 +08:00
2e2fbf2181
`3df7d6764a` hardened `start_local_agent` so a pre-existing listener on 8080
could not be mistaken for the child it just spawned. That hardening never ran.
`main()`'s `--local` branch probed the port first and, on a successful TCP
connect, printed "Agent already running on localhost:8080" and skipped
`start_local_agent` entirely - the function is called from exactly one place,
the `else` of that same probe. So in the one scenario the hardening existed for,
a stranger owning 8080, the hardened code was unreachable and the tester chatted
with the stranger under a success banner, exit 0.
A bare TCP accept only establishes that *some* process is listening. It cannot
establish that the process is this example's agent. The fix removes the check
that made that inference:
- `main()` no longer probes 8080 on the start path at all. Adopting a listener
is now opt-in via `--use-running-agent`, and even then it is announced as
unverified ("did not start it and cannot verify it is an agent") rather than
as "Agent already running". The flag errors out when nothing is listening, and
argparse rejects it without `--local` instead of silently ignoring it.
- The port check moved into `start_local_agent`, before the "Starting local
agent" banner, where it now REFUSES on an occupied port instead of spawning a
child that cannot bind. Because `main()` no longer duplicates the probe, this
is the only port check on the start path, so it is genuinely reachable from
the shipped CLI - which is precisely what the previous attempt was not.
- With the pre-spawn refusal in place, the loop's `not port_already_busy` guard
became a provably-constant conjunct and was folded away. The durable half of
the earlier hardening, polling the child for liveness BEFORE looking at the
port, is unchanged and still reachable.
Same-pattern audit of the file found one more instance: `run_chat` printed
"[Completed in Xs]" purely because `invoke_agent` returned, which it also does
after an HTTP error. `invoke_agent` now returns a bool and the line reports
"[Failed in Xs]" when the exchange did not succeed. The request payload and the
streaming decoder are deliberately untouched (deferred).
Verified by driving the real `main()` via importlib against a foreign HTTP
server bound to 127.0.0.1:8080:
- pre-fix, `--local`: "Agent already running on localhost:8080" then
"Agent: I am a STRANGER on 8080, not the agentcore agent", exit 0.
- post-fix, `--local`: "Port 8080 is already accepting connections" plus how to
proceed, exit 1, nothing spawned.
- post-fix, `--local --use-running-agent`: talks to it, labelled unverified.
- post-fix, `--local --use-running-agent` with nothing listening: exit 1.
- ordinary path (port free, child really binds 8080): "Agent started
successfully", byte-identical to pre-fix output.
- child exits 1 with no listener: still caught in ~1s, not 30s.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>