fix(agents): inherit default agent model catalog for secondary agents#90903
fix(agents): inherit default agent model catalog for secondary agents#90903thinhkhang97 wants to merge 1 commit into
Conversation
|
Codex review: found issues before merge. Reviewed June 30, 2026, 1:35 PM ET / 17:35 UTC. Summary PR surface: Source +94, Tests +254. Total +348 across 4 files. Reproducibility: yes. source-level. Current main prewarms generated plugin catalogs only for the default agent, ModelRegistry reads plugin catalogs from the current agent dir, and the PR body supplies live after-fix output for the secondary-agent Gemini case; I did not run a live repro in this read-only review. Review metrics: 2 noteworthy metrics.
Stored data model Root-cause cluster Members:
Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything. Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Risk before merge
Maintainer options:
Next step before merge
Security Review findings
Review detailsBest possible solution: Land a rebased patch that preserves current main catalog error isolation and model Do we have a high-confidence way to reproduce the issue? Yes, source-level. Current main prewarms generated plugin catalogs only for the default agent, ModelRegistry reads plugin catalogs from the current agent dir, and the PR body supplies live after-fix output for the secondary-agent Gemini case; I did not run a live repro in this read-only review. Is this the best way to solve the issue? No as submitted. Read-through inheritance is a plausible repair, but the branch must be rebased to preserve current main model-registry invariants and needs maintainer acceptance for inherited provider metadata crossing agent directories. Full review comments:
Overall correctness: patch is incorrect AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against 5c4e478df4ef. Label changesLabel justifications:
Evidence reviewedPR surface: Source +94, Tests +254. Total +348 across 4 files. View PR surface stats
Security concerns:
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
Review history (1 earlier review cycle)
|
8a68265 to
addff0c
Compare
|
@clawsweeper re-review please. Updated since the last (failed) review pass:
The two failing checks ( |
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
1de5362 to
7a65c7c
Compare
|
Correction to my note above (the "verified on main commits" line was the wrong way to check — those heavy checks are PR-gated, not run on I reproduced the failing checks on a clean checkout of
This PR changes only The change's own surface is green: targeted I'm a fork contributor and can't re-run the inherited-from-main check failures. Would appreciate a review when there's bandwidth. |
7a65c7c to
8514259
Compare
Model discovery only runs for the default/main agent at gateway startup, so generated plugin model catalogs (e.g. plugins/google/catalog.json) are only populated under the default agent dir. A secondary agent created with `openclaw agents add` gets an apiKey-only google catalog with no models, so a Google/Gemini model authenticated via GEMINI_API_KEY fails at runtime with `FailoverError: Unknown model: google/gemini-2.5-flash`. The default agent works. Add read-through inheritance for generated plugin model catalogs, mirroring the existing auth-profile read-through (`inheritedAuthDir`). Implementation is local-first with a per-provider gap-fill gate: - The agent's OWN catalogs (root models.json + local plugin catalogs) load first and establish its full local state. - Inheritance is gated per provider: a provider the local agent already has any model for is excluded from the inherited load entirely — it contributes no provider request config, no per-model headers, and no models for that provider. So "local always wins" never depends on write order, and an inherited catalog can neither read nor overwrite the local agent's request config/headers. - Only providers the secondary agent does not configure at all are inherited (config + headers + models), which is exactly the agent that is living on inheritance. apiKey values in catalogs are env refs (e.g. GEMINI_API_KEY) resolved per-agent, so no secret material crosses the agent boundary. - A broken/invalid inherited catalog is skipped, never breaking the local agent. `discoverModels` threads `inheritedAgentDir` through to the registry; `discoverCachedAgentStores` passes the resolved default agent dir and folds the inherited catalog files into the discovery-cache fingerprint so changes to the default agent's catalog invalidate the secondary agent's cached snapshot. The default agent never inherits from itself (path-equality guard).
8514259 to
5ddb7fa
Compare
|
This pull request has been automatically marked as stale due to inactivity. |
fix(agents): inherit default agent model catalog for secondary agents
Summary
Secondary (non-default) agents created with
openclaw agents addfail at runtimewith
FailoverError: Unknown model: google/gemini-2.5-flashwhen using aGoogle/Gemini model authenticated via
GEMINI_API_KEY. The default/mainagentworks with the same model and key.
Root cause: model discovery only runs for the default agent at gateway
startup, so generated plugin model catalogs (
plugins/<provider>/catalog.json)are only populated under the default agent dir. A secondary agent gets an
apiKey-only google catalog with no models.
ModelRegistry.loadCustomModels()loads plugin catalogs only from the current agent dir, with no read-through to
the default agent — unlike auth profiles, which already inherit via
inheritedAuthDir.The fix
Add read-through inheritance for generated plugin model catalogs, mirroring the
existing auth-profile read-through (
inheritedAuthDir). Implementation islocal-first with a per-provider gap-fill gate:
ModelRegistryaccepts an optionalinheritedAgentDir. The agent's OWNcatalogs (root models.json + local plugin catalogs) load first and establish
its full local state (models + provider request config + per-model headers).
any model for is excluded from the inherited load entirely — it contributes no
provider request config, no per-model headers, and no models. So "local always
wins" never depends on write order, and the inherited catalog can neither read
nor overwrite the local agent's request config/headers (closes the
cross-agent boundary concern). Provider request config (baseUrl/api/auth/
headers) is per-provider with a single slot, so this also avoids pairing an
inherited model with a local provider's credential.
(config + headers + models) — exactly the agent living on inheritance.
apiKeyvalues in catalogs are env refs (e.g.
GEMINI_API_KEY) resolved per-agent, sono secret material crosses the agent boundary.
discoverModelsthreadsinheritedAgentDirthrough to the registry.discoverCachedAgentStorespasses the resolved default agent dir, and theinherited catalog files are folded into the discovery-cache fingerprint so
changes to the default agent's catalog invalidate the secondary agent's cached
snapshot.
The default agent never inherits from itself (path-equality guard), so its
behavior is unchanged.
A heavier alternative — running model discovery for every agent at startup — is
the deeper root-cause fix but is costly (per-agent network discovery, needs to
be lazy) and much larger in scope; noting it as a possible follow-up.
Files
src/agents/sessions/model-registry.ts—inheritedAgentDiroption + read-through mergesrc/agents/agent-model-discovery.ts— thread option throughdiscoverModelssrc/agents/embedded-agent-runner/model-discovery-cache.ts— pass default dir + fingerprintsrc/agents/sessions/model-registry.test.ts— 6 new testsReal behavior proof (required for external PRs)
openclaw agents addcould not use
google/gemini-2.5-flash(authed viaGEMINI_API_KEY) and failedevery turn with
Unknown model: google/gemini-2.5-flash, while the defaultmainagent worked with the same model and key.gateway), Google Gemini via
GEMINI_API_KEY, default modelgoogle/gemini-2.5-flash, with a realmainagent plus a secondary agent(
english-tutor).ran
pnpm build, restarted the gateway, emptied the secondary agent's googleplugin catalog to the apiKey-only state (the state
agents addleaves behind),then ran a turn through the running gateway — the same lane a Telegram DM uses:
openclaw agent --agent english-tutor -m "Hi Lexi, correct this: I has two cats yesterday." --jsondirs (
discoverModels):google/gemini-2.5-flashvia read-through inheritance, through the nativegoogle-generative-aitransport, and produced a real model reply — with nomanual catalog seeding. Before the patch the identical gateway turn returned
Unknown model: google/gemini-2.5-flash. Re-verified end-to-end on the finalbuild with the per-provider gating: a secondary agent that configures no
provider of its own inherits the default agent's google catalog and resolves
the model (a provider the secondary configures itself would instead be
local-owned and not inherited).
Full
pnpm testwas run; 4 shards each reported 1 failing test, all unrelatedto this change (macOS
/private/varpath-normalization / packaged-worker /fixture assertions; none of those files reference the changed modules).
workstation. The live gateway turn was invoked via the CLI
agentcommand(which routes through the running gateway on the same agent lane as a Telegram
DM) rather than by sending an actual Telegram message.
openclaw agent --agent <secondary> -m "..."→GatewayClientRequestError: FailoverError: Unknown model: google/gemini-2.5-flash(default
mainagent returned"status": "ok"for the same model).Tests and validation
Supplemental (not a substitute for the real behavior proof above):
New tests cover: (1) a secondary agent inherits the default agent's catalog
models; (2) no inheritance without
inheritedAgentDir; (3) the secondary agent'sown catalog wins over inherited models; (4)+(5) an inherited catalog never
overwrites a local provider's request config/headers and never leaks sibling
models into a provider the local agent owns — asserted at the request
resolution level (
getApiKeyAndHeadersreturns the local headers), for both theroot models.json and local plugin catalog paths; (6) an inherited-only
provider resolves with the inherited
baseUrland headers.CI status (rebased onto current
origin/maine5a9c60851)This branch is rebased onto
origin/mainHEAD (e5a9c60851). Most of the CIfailures present on the earlier base have since been fixed upstream. Current state:
check-lintpnpm lint:coreexit 0check-additional-runtime-topology-architecturepnpm check:architecture— 0 cyclescheck-additional-extension-bundledpnpm lint:extensions:bundledexit 0build-artifactscheck-test-typesorigin/mainThe single remaining
check-test-typesfailure is pre-existing onmainandunrelated to this PR — it is two type errors in an extension test:
Verified by checking out clean
origin/main(e5a9c60851) with none of this PR'scommits and running
pnpm tsgo:extensions:test→ the identical two errors, exit 2.This PR changes only
src/agents/**and does not touchextensions/qa-lab/**(
git diff --name-only origin/main HEADlists four files, all undersrc/agents/),so it cannot introduce this failure. The change's own surface is green: targeted
vitestformodel-registry/agent-model-discoverypasses andtsgoreports noerrors in the changed files.
AI assistance
Drafted with Claude Code (Opus); reviewed and validated by me. Session log
available on request.