The agent tells the owner to run pags up when pags up is already running and the pin is the problem
Live, instance f8ddc272-0390-4826-8812-94989e3d2ebd, 2026-08-12:
07:44:05 chat.in "Retry"
07:44:10 chat.out "The runner is currently offline — I can't start new work until it's
connected. Run `pags up` in your terminal to bring it back online…"
No tool.call between them (agent_trace, and instance_activity shows chat.message → chat.response with no tool.called). The refusal came from the system prompt, not a tool.
The owner's pags up was running the whole time, on RLs-MacBook-Air (runner row created 03:07:22, live). The instance was pinned to Sergeys-Mac-mini.local. At 07:55:10 the same start_work succeeded — the only variable that changed was the pin.
Mechanism
workers/api/src/agent-think.ts:534-535:
const boundConn = await getBoundRunnerConn(env, state.agentId, userId).catch(() => null);
const runnerOnline = boundConn ? await relayConnected(env, state.agentId, boundConn.runnerNode ?? null) : false;
systemPrompt += runnerStatusPrompt(selfModel, runnerOnline);
getBoundRunnerConn is pin-authoritative and correctly returns null — the mini and the Air carry different machine_ids, so aliasNodesFor cannot rescue it (runner-client.ts:96-101). The routing verdict is right.
Then it is collapsed to a boolean, and lib/agent-style-prompt.ts:89-99 turns that boolean into a fixed remedy:
const remedy = model.hasRunner
? ` If the user wants to run, search, or fix code, tell them to start the runner first with \`pags up\`…`
grep -c diagnoseAttachment workers/api/src/agent-think.ts workers/api/src/lib/agent-style-prompt.ts → 0 and 0.
The correct sentence already exists and is deliberately remedy-free
lib/runtime-attachment.ts:69-79 produces: "This agent is pinned to Sergeys-Mac-mini.local, which isn't connected. RLs-MacBook-Air is connected — set 'Runs on' to RLs-MacBook-Air (or Automatic)" — and carries no remedy string, precisely because pags up is the wrong advice in this state.
The chat prompt is a fourth construction site for "why is there no runner", and the only one never wired to that diagnosis. 07ec7af (#461) and e6e330d (#468) fixed forwarding for the three adapters; this path was not in their scope, so those commits could not have reached it.
Same class as #271/#321/#524 — a message that cannot name the machine it means — in a file none of them touched.
Acceptance criteria
- When the runner is unavailable because of a pin, the agent says so and names both machines, instead of prescribing
pags up.
- The chat prompt consumes
diagnoseAttachment rather than re-deriving a boolean — one construction site, not four.
- Where the true remedy is "change Runs on", the agent does not offer a remedy the platform knows is wrong. Note
runtime-attachment.ts returns remedy: null for this case on purpose; respect that rather than substituting one.
- A genuinely absent runner still gets the
pags up advice — this must not become a generic softening.
- A test with pin=A, live=B asserting the reply names the pin.
Verified vs inferred
A second, separable gap this exposed: the repin is invisible — no agent_events row, no activity entry. A configuration change that silently decides whether every tool call routes anywhere should leave a record.
The agent tells the owner to run
pags upwhenpags upis already running and the pin is the problemLive, instance
f8ddc272-0390-4826-8812-94989e3d2ebd, 2026-08-12:No
tool.callbetween them (agent_trace, andinstance_activityshowschat.message→chat.responsewith notool.called). The refusal came from the system prompt, not a tool.The owner's
pags upwas running the whole time, onRLs-MacBook-Air(runner row created 03:07:22, live). The instance was pinned toSergeys-Mac-mini.local. At 07:55:10 the samestart_worksucceeded — the only variable that changed was the pin.Mechanism
workers/api/src/agent-think.ts:534-535:getBoundRunnerConnis pin-authoritative and correctly returnsnull— the mini and the Air carry differentmachine_ids, soaliasNodesForcannot rescue it (runner-client.ts:96-101). The routing verdict is right.Then it is collapsed to a boolean, and
lib/agent-style-prompt.ts:89-99turns that boolean into a fixed remedy:grep -c diagnoseAttachment workers/api/src/agent-think.ts workers/api/src/lib/agent-style-prompt.ts→ 0 and 0.The correct sentence already exists and is deliberately remedy-free
lib/runtime-attachment.ts:69-79produces: "This agent is pinned to Sergeys-Mac-mini.local, which isn't connected. RLs-MacBook-Air is connected — set 'Runs on' to RLs-MacBook-Air (or Automatic)" — and carries no remedy string, precisely becausepags upis the wrong advice in this state.The chat prompt is a fourth construction site for "why is there no runner", and the only one never wired to that diagnosis.
07ec7af(#461) ande6e330d(#468) fixed forwarding for the three adapters; this path was not in their scope, so those commits could not have reached it.Same class as #271/#321/#524 — a message that cannot name the machine it means — in a file none of them touched.
Acceptance criteria
pags up.diagnoseAttachmentrather than re-deriving a boolean — one construction site, not four.runtime-attachment.tsreturnsremedy: nullfor this case on purpose; respect that rather than substituting one.pags upadvice — this must not become a generic softening.Verified vs inferred
start_worksucceeding at 07:55:10; the three code paths above; thatdiagnoseAttachmentappears in neither file; that the two machines have differentmachine_ids (measured on "Run npm i -g @proagentstore/cli on that machine" names no machine — the owner has two runners, already upgraded one, and the agent is pinned to the other #524).Sergeys-Mac-mini.localat 07:44:10 specifically.PUT /v1/instances/:id/runner-node(instances.ts:499-511) writes the config key and logs nothing, so the repin moment is unrecoverable. The inference rests on "Run npm i -g @proagentstore/cli on that machine" names no machine — the owner has two runners, already upgraded one, and the agent is pinned to the other #524 measuring that pin at 04:16 UTC, an independent read earlier the same session, and the 07:44-refuses / 07:55-succeeds pair.A second, separable gap this exposed: the repin is invisible — no
agent_eventsrow, no activity entry. A configuration change that silently decides whether every tool call routes anywhere should leave a record.