Files
Maxim 2e2fbf2181 fix(agentcore): stop --local adopting an unidentified listener on port 8080
`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>
2026-08-24 19:24:57 +02:00
..