feat: flag Agents with a newer published version; read npm facts from the catalog - #63
Merged
Merged
Conversation
…facts from the catalog Two things, because the second is what made the first possible to write correctly. ## The dot There was no way to know an Agent was out of date. lockedVersion, which versionNote compares against, is hardcoded to nil in status.go -- agents.lock.json deliberately records no versions -- so isBehind never returned true and the "behind" branch of versionNote was unreachable. Knowing required asking the registry, which nothing did. install.LatestVersion asks, and says nothing when it cannot. Every failure is silent on purpose: this decorates a button, so an offline machine, a rate-limited registry, or a captive portal leaves the row exactly as it was rather than turning a status poll into an error or claiming "up to date" it cannot support. nil means "say nothing", not "current". It streams rather than decoding into a struct, and that is not premature. The abbreviated document for opencode-ai is 13 MB because it carries an entry per published version, while dist-tags sits in the first 24 bytes. Reading all of it forced a choice between a size cap that truncated the JSON -- unexpected EOF with the answer long since read -- and no cap, letting a registry decide how much memory this spends. Streaming to the key needs neither. Answers are cached for 30 minutes and at most three requests run at once. The UI polls status; without the cache that is a request per Agent per poll, which is both wasteful and the fastest way to be rate-limited. Only installed npm Agents are asked about: Aider comes from PyPI through uv, and there is nothing to update on a machine that does not have the Agent. ## Where npm facts come from (#61) The set of npm-managed Agents was a literal in AgentManageRow, beside a second literal mapping each to its package name. Both had already drifted: OpenClaw shipped as an npm Agent in #53 and was in neither, so it silently lost its update button and its package name in the details -- with no test or build to catch it, because nothing tied the two lists to agents.lock.json. The catalog carries packageManager and packageName now, from the same manifest the installer reads. The update button also ignored whether the Agent was installed, though the launch button in the same component did not. `npm update -g` on a package that was never installed exits 0 and does nothing, so the task centre reported "update complete" for an Agent that was still missing -- worse than an error, because it looks like it worked. The button is now hidden, and UpdateAgent refuses, since the CLI and a restored task card do not go through the UI. Verified against the real registry: codex 0.146.0 -> 0.146.1 and openclaw 2026.6.5 -> 2026.7.1-2 show a dot, claude-code, kilo-cli and opencode are current and show none, and aider is never asked.
golang.org/x/mod arrived with the OTA updater in #62 and its manifest entry was written by hand, without the `modification` key every other dependency carries. The generator emits it unconditionally -- an empty value is how the bundle states "redistributed unmodified" -- so the tracked file and a fresh generation differed by that one line and the Release compliance job failed. This is main's failure, not this branch's: `--check` fails the same way on a clean checkout of main at bd143bb, and it has been red since #62 merged. The fix is running the generator, which is what the job's own error message asks for.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #61.
Adds the update dot, and fixes #61 along the way — the second is what makes the first correct rather than a third hardcoded list.
The dot
Nothing could tell an Agent was out of date.
lockedVersion, whichversionNotecompares against, is hardcoded tonilinstatus.go—agents.lock.jsondeliberately records no versions — soisBehindnever returned true and the "behind" branch ofversionNotewas unreachable. Knowing meant asking the registry, which nothing did.install.LatestVersionasks, and says nothing when it cannot. Every failure is silent by design: this decorates a button, so an offline machine, a rate-limited registry, or a captive portal leaves the row exactly as it was rather than turning a status poll into an error — or claiming "up to date" on no evidence.nilmeans say nothing, not current.It streams instead of decoding into a struct, and that is not premature. opencode-ai's abbreviated document is 13 MB because it carries an entry per published version, while
dist-tagssits in the first 24 bytes. Reading all of it forced a choice between a size cap that truncated the JSON (unexpected EOF, with the answer long since read) and no cap at all, letting a registry decide the memory bill. I hit exactly this: opencode-ai returned""under a 4 MB cap while openclaw at 0.6 MB worked fine.Costs are bounded: answers cached 30 minutes, at most 3 concurrent requests, one deadline for the batch. The UI polls status — without the cache that is one request per Agent per poll. Only installed npm Agents are asked about; Aider comes from PyPI through uv, and there is nothing to update on a machine without the Agent.
Where npm facts come from (#61)
The npm-managed set was a literal in
AgentManageRow, beside a second literal mapping each Agent to its package name. Both had drifted: OpenClaw shipped as an npm Agent in #53 and was in neither, so it silently lost its update button and its package name in the details — nothing tied those lists toagents.lock.json, so no test or build could catch it. The catalog now carriespackageManagerandpackageNamefrom the same manifest the installer reads.The update button also ignored
installed, though the launch button in the same component did not.npm update -gon a never-installed package exits 0 and does nothing, so the task centre reported "更新完成" for an Agent that was still missing — worse than an error, because it looks like it worked. The button is now hidden, andUpdateAgentrefuses, since the CLI and a restored task card do not go through the UI.Verification
Against the real npm registry, on a machine with all five installed:
OpenClaw showing a dot is the #61 bug fixed and the feature working in one row.
go test ./...andgo vet ./...pass;pnpm run test26 files / 197 tests pass;pnpm run buildandtsc --noEmitpasstsccaught three test fixtures missing the new field that vitest had not — those are the one-line additions to the page tests.Note
lockedVersionis left alone. It means "the version the lock pins", which is a different thing from "the newest published version", and conflating them would be wrong even though it is currently alwaysnil.🤖 Generated with Claude Code