The computer marks a takeover refusal on the body: 409 { error, humanHasControl: true }, from the acting path (agent-computer/src/index.ts:938) and from navigate (:721). The surface reads that flag to decide what the model hears next (app/src/lib/copilot/computer-tools.tsx:89-92): with it, a person has control; without it, staleRefs, which the snapshot tool's own description turns into "If an action reports that your refs are stale, the page changed: call this again and use the new refs."
The flag does not survive the server. throwMappedError (server/src/computer/client.ts:208) maps every 409 to StaleSnapshotError, which carries a message and nothing else, and each route then renders { error: describe(error) }. So the branch in the surface is unreachable, and a Bot refused because somebody deliberately took the browser is told to take a fresh snapshot and repeat the action it was just stopped from doing.
Driven end to end against a listener answering exactly what the computer answers, through the real gateway and transport:
status: 409
body: {"error":"A person has taken control of this computer."}
humanHasControl survived the relay: false
Both ends already know about this distinction. Only the middle drops it.
Where it shows
Every route that renders an error loses it, which is nine call sites, so it is not only the acting path. Navigate is the one to notice: it does not go through the shared acting helper, and it is the second place the computer flags a takeover.
Two smaller things found alongside, neither worth its own report:
statusFor (server/src/computer/routes.ts) has a branch for "a person holding the wheel, or a person driving before taking it", keyed on a ComputerUnavailableError whose message matches /control/i. For the shipped computer that is unreachable, since both control refusals are 409 and 409 was claimed by StaleSnapshotError before it.
- The computer's other control refusal,
TAKE_CONTROL_FIRST for a person driving before taking the wheel (agent-computer/src/index.ts:624), carries no flag at all. Different surface: the person's own UI, whose client discards the error anyway (app/src/lib/computers/control.ts:91).
Why nothing caught it
The computer sets the flag and has tests. The surface consumes it and has the branch. Nothing tested the relay between them, so it read as covered from either end.
A fix is up in a PR.
The computer marks a takeover refusal on the body:
409 { error, humanHasControl: true }, from the acting path (agent-computer/src/index.ts:938) and from navigate (:721). The surface reads that flag to decide what the model hears next (app/src/lib/copilot/computer-tools.tsx:89-92): with it, a person has control; without it,staleRefs, which the snapshot tool's own description turns into "If an action reports that your refs are stale, the page changed: call this again and use the new refs."The flag does not survive the server.
throwMappedError(server/src/computer/client.ts:208) maps every 409 toStaleSnapshotError, which carries a message and nothing else, and each route then renders{ error: describe(error) }. So the branch in the surface is unreachable, and a Bot refused because somebody deliberately took the browser is told to take a fresh snapshot and repeat the action it was just stopped from doing.Driven end to end against a listener answering exactly what the computer answers, through the real gateway and transport:
Both ends already know about this distinction. Only the middle drops it.
Where it shows
Every route that renders an error loses it, which is nine call sites, so it is not only the acting path. Navigate is the one to notice: it does not go through the shared acting helper, and it is the second place the computer flags a takeover.
Two smaller things found alongside, neither worth its own report:
statusFor(server/src/computer/routes.ts) has a branch for "a person holding the wheel, or a person driving before taking it", keyed on aComputerUnavailableErrorwhose message matches/control/i. For the shipped computer that is unreachable, since both control refusals are 409 and 409 was claimed byStaleSnapshotErrorbefore it.TAKE_CONTROL_FIRSTfor a person driving before taking the wheel (agent-computer/src/index.ts:624), carries no flag at all. Different surface: the person's own UI, whose client discards the error anyway (app/src/lib/computers/control.ts:91).Why nothing caught it
The computer sets the flag and has tests. The surface consumes it and has the branch. Nothing tested the relay between them, so it read as covered from either end.
A fix is up in a PR.