Skip to content

A Bot refused because a person took the wheel is told its refs are stale, and tries again #163

Description

@beardthelion

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions