Skip to content

"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

Closed

Description

@serge-ivo

What the owner saw

Three times in 40 minutes, on the same agent:

2026-08-12 03:23:28 ❌ repo_find This machine's runner is too old to search this repository — it needs CLI 0.4.49 or newer. Run npm i -g @proagentstore/cli on that machine and restart pags up.
2026-08-12 04:03:31 ❌ repo_grep (same) — twice.

and the agent relayed it faithfully:

The search tools are unavailable right now — the local runner needs to be updated before I can read the code. The error says to run npm i -g @proagentstore/cli on your machine and restart pags up.

He has two machines connected, and he has already upgraded one of them to 0.4.49. The message cannot tell him which machine it means, and nothing on the surface tells him this agent is pinned to the other one. Following the instruction on the obvious machine — the one he is sitting at — changes nothing.

Measured, live

GET /v1/terminals/nodes, 2026-08-12 04:16 UTC, both connected:

node machineId runner aka
RLs-MacBook-Air 3901c9c4-… 0.4.49 Mac, RLs-MacBook-Air.local
Sergeys-Mac-mini.local 8de74159-… 0.4.45 —

Instance bd43f4de-ef35-4051-bdec-43f8571414a1 ("Chess coder 2"), config.runnerNode = "Sergeys-Mac-mini.local" — the 0.4.45 machine. On that node the instance reads connected: true, bound: true; the 0.4.49 machine holds live sockets for 15 of the owner's other instances.

REPO_SEARCH_MIN_CLI = "0.4.49" (workers/api/src/lib/connectors/repo-local.ts:53) — so repo_find and repo_grep are exactly the two tools the pinned machine cannot serve. Its other repo tools work: at 04:03:55 the same instance ran repo_read_file and repo_git successfully against the same checkout.

Mechanism — two correct decisions composing into an unactionable message

1. The pin is authoritative and deliberately never falls through. workers/api/src/lib/runner-client.ts:74-100, docstring and code:

"When pinned, that machine is authoritative: we do NOT silently fall back to another node when it's offline — doing so would run the agent somewhere the user didn't choose and misreport 'online' for the wrong machine."

That is right, and #379/#380/#461/#500 are the trail of bugs from getting it wrong. It is not the defect.

2. The remedy names no machine. workers/api/src/lib/connectors/repo-local.ts:57-61:

function runnerTooOld(e: unknown, what: string): string | null {
	const message = e instanceof Error ? e.message : String(e);
	if (!/→ 404|not found/i.test(message)) return null;
	return `This machine's runner is too old to ${what} — it needs CLI ${REPO_SEARCH_MIN_CLI} or newer. Run \`npm i -g @proagentstore/cli\` on that machine and restart \`pags up\`.`;
}

The comment above REPO_SEARCH_MIN_CLI gets the principle exactly right — "Named, not 'update the CLI' — a version without a number is one somebody has to go and find" — and then stops one step short: the version is named, the machine is not, and the machine is the part the reader has to go and find. Worse, "that machine" is a phrase the reader will resolve to the machine in front of them, which here is the 0.4.49 one that needs nothing.

This is the #513 / #517 class: a remedy the reader cannot execute. #517 fixed the case where the remedy was discarded; this is the case where it is relayed intact and is still not actionable.

What to do, cheapest first

  1. Name the machine, and its version. runnerTooOld is called from a handler that has already resolved the connection (resolveTarget → getBoundRunnerConn), and RunnerConn carries runnerNode. Thread it in:

    The runner on **Sergeys-Mac-mini.local** is too old to search this repository (it has 0.4.45; this needs 0.4.49 or newer). Run npm i -g @proagentstore/clion **Sergeys-Mac-mini.local** and restartpags up there.
    One parameter, one call site, and it is the whole difference between a message the owner can act on and one he cannot.

  2. Say the pin is why it is that machine. When config.runnerNode is set, append: "This agent is pinned to that machine (Settings → Runs on), so it will not use another one." Without this the owner has no way to discover the pin from the failure — and the pin is the only reason the upgraded machine is not being used.
  3. Say when a capable machine exists. GET /v1/terminals/nodes already computes every node's runnerVersion. When another of the owner's connected nodes satisfies REPO_SEARCH_MIN_CLI, name it: "RLs-MacBook-Air is connected and already runs 0.4.49 — repin this agent there, or upgrade the pinned machine." That turns a dead end into a choice.
  4. Generalise it. SWITCH_BRANCH_MIN_CLI in repo-policy-act.ts is the same pattern and will read the same way. One helper — "which machine, running what, needs what" — serves both.

Alternatives rejected

Acceptance

  • With an instance pinned to a node whose runner_version < REPO_SEARCH_MIN_CLI, repo_find/repo_grep return a message that contains the node name, its actual version, the required version, and — when the pin is set — that the agent is pinned there.
  • When another connected node meets the requirement, the message names it.
  • Unit test on the message builder (pure, as repoMissingMessage already is) covering: pinned + no capable alternative; pinned + capable alternative present; unpinned.

Regression risk

The message is relayed verbatim into chat and is now longer; #517's own finding was that a long remedy gets truncated away before the owner sees it. Whatever cap applies to a tool-result line must keep the machine name — put it in the first clause, not after the version numbers. Test that the truncated form still names the machine.

Related: #508 (added repo_find/repo_grep and REPO_SEARCH_MIN_CLI), #513 and #517 (remedies the reader cannot execute), #379 / #461 (the pinned-machine diagnosis this should reuse).

Activity

  1. added
    bugSomething isn't working
    backendBackend / Worker / API work
    on Aug 12, 2026
  2. serge-ivo commented on Aug 12, 2026

    @serge-ivo
    ContributorAuthor

    Line-number correction (the file is being edited concurrently): REPO_SEARCH_MIN_CLI = "0.4.49" is workers/api/src/lib/connectors/repo-local.ts:54, not :53. The runnerTooOld message is at :57-61 (the template string itself on :60), re-checked against b590eee.

  3. serge-ivo commented on Aug 15, 2026

    @serge-ivo
    ContributorAuthor

    Shipped in 1494244. All three acceptance criteria met; closing.

    What the message says now

    Before:

    This machine's runner is too old to search this repository — it needs CLI 0.4.49 or newer. Run npm i -g @proagentstore/cli on that machine and restart pags up.

    After, for the account in this issue:

    Sergeys-Mac-mini.local needs a newer runner to search this repository (it has 0.4.45) — this needs CLI 0.4.49 or newer. Run npm i -g @proagentstore/cli on Sergeys-Mac-mini.local, then restart pags up there. This agent is pinned to that machine (Settings → Runs on), so it will not use another one. RLs-MacBook-Air is connected and already runs 0.4.51 — repin this agent there (Settings → Runs on), or upgrade Sergeys-Mac-mini.local.

    The pin is not relaxed. Your rejected alternative was the right call and runner-client.ts:74-100 still refuses to fall through: a checkout lives on one machine, and searching a different machine's copy returns confidently wrong results — worse than an honest refusal. What changed is that the refusal explains itself.

    The three criteria

    1. Node name, its version, the required version, and the pin — all in the message, from runtimeConnectivity, which already computed every one of them for diagnoseAttachment. Nothing new is measured.
    2. A capable machine is named. Read account-wide rather than per-instance, deliberately: on your account the upgraded laptop held live sockets for fifteen other instances, so a per-instance query would have found nothing and the message would have been a dead end that looked complete.
    3. Unit tests on the pure builder — pinned + no alternative; pinned + alternative; unpinned; no version reported; no node resolved. Plus the fact-assembly half, mocked at the two seams it reads through.

    Two judgement calls worth stating

    "Connected" is only said when the relay says so. A candidate machine is called connected only if it holds a live socket for this instance right now. status is never cleared on disconnect (#238), so a laptop shut months ago still reads registered — offering that as "connected and ready" would be this exact bug one machine over. A registration we cannot confirm reads "last reported 0.4.51" instead. Same reason cliAtLeast treats an unreported version as not capable: naming a machine that turns out to be older converts a dead end into a wild goose chase.

    The machine name is in the first clause, asserted at an 80-character cut. That is your regression note taken literally — this message is longer than the one it replaces and #517's finding was that a long remedy gets truncated before you see it.

    AC4 — generalised

    SWITCH_BRANCH_MIN_CLI had independently written the same machine-less remedy ("this machine's runner has no switch-branch endpoint"). classifyRunnerError now takes the node — which runRepoPolicyRemediation already holds on conn, so it costs no query — and builds its clause from the same module. Same rule, different grammar: that one is spliced into a work card's prose, so it is a fragment rather than a sentence. Both asserted.

    The diagnosis never throws: a failed read degrades to the original sentence rather than turning a tool refusal into a 500.

    Note on scope

    repo-local.ts was being edited concurrently by another session, so the change there is three lines (the call site and the import) and all the substance is in the new lib/runner-upgrade.ts.

    Verified

    pnpm typecheck (11 projects), 9,156 unit tests, biome --error-on-warnings, all six script gates, check-migrations --require-history, docs:drift, both tsconfig.test.json gates. CI green on 1494244; API and Host workers deployed.

    Not verified live, and I am naming it rather than implying otherwise: reproducing it needs a machine on a runner older than 0.4.49 with an instance pinned to it, and both your machines are now past that. The message builder is pure and fully asserted, and the facts it reads come from runtimeConnectivity, which the console's own runner banner already depends on — but no repo_find has actually returned this string in production. If you ever see it, it should name the Mac mini.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    backendBackend / Worker / API workbugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions