fix(agents): match runtime policy entries when session provider is empty#85970
Conversation
|
Codex review: needs maintainer review before merge. Reviewed May 24, 2026, 2:16 PM ET / 18:16 UTC. Summary PR surface: Source +57, Tests +155, Docs +1. Total +213 across 5 files. Reproducibility: yes. Current main source shows provider-prefixed 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 detailsBest possible solution: Land the focused resolver change after maintainer acceptance of the fail-closed ambiguity behavior, keeping the provider/runtime validation and regression coverage. Do we have a high-confidence way to reproduce the issue? Yes. Current main source shows provider-prefixed Is this the best way to solve the issue? Yes. Carrying the matched policy provider into CLI alias validation is narrower than a migration-only workaround and preserves mismatch rejection, precedence, and ambiguity fail-closed behavior. Codex review notes: model gpt-5.5, reasoning high; reviewed against af0776987136. Label changesLabel changes:
Label justifications:
Evidence reviewedPR surface: Source +57, Tests +155, Docs +1. Total +213 across 5 files. View PR surface stats
Acceptance criteria:
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
|
|
ClawSweeper PR egg ✨ Hatched: 🥚 common Frosted Branchling Hatch commandComment Hatchability rules:
Rarity: 🥚 common. What is this egg doing here?
|
467585d to
f774505
Compare
|
@clawsweeper re-review Addressed both findings:
Two new tests added (one per finding). vitest + oxlint + tsgo:core + check:changed all green. |
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
b568e7a to
02f0e8b
Compare
02f0e8b to
c221773
Compare
c221773 to
f5daa84
Compare
|
@clawsweeper re-review Prepared again after rebasing onto current main.
Please re-review this latest head before merge. |
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
|
Merged via squash.
Thanks @potterdigital! |
Summary
<provider>/prefix) silently fell throughagents.defaults.models["<provider>/<model>"].agentRuntimepolicy lookup.modelEntryMatchKindinsrc/agents/model-runtime-policy.tsevaluatednormalizeProviderId("anthropic") !== normalizeProviderId("")→ returned"none"for every provider-prefixed entry, so a configuredagentRuntime: claude-cliwas never matched.anthropic:claude-cli: requests fell through to the embedded Anthropic-Messages harness, which sent the OAuth token (sk-ant-oat01-…) to/v1/messagesand got back 404model_not_found. All anthropic auth profiles cascaded into 40-min cooldown; every subsequent request surfacedAll models failed (3): … No available auth profile for anthropicuntil cooldown expired. Concurrent cron/heartbeat runs that constructed a fresh runtime context with the qualified ref kept succeeding throughprovider=claude-cli.modelKeyIsProviderWildcard. When the caller provider is empty and multiple distinct providers would match the same bare model id, refuse to silently pick — return{}instead of relying onObject.entriesorder. Then short-circuitresolveCliRuntimeExecutionProvider(src/agents/model-runtime-aliases.ts) on known CLI aliases when caller provider is empty, because the${provider}:${runtime}Map lookup can't hit with empty provider.Refs
agents/main/sessions/sessions.jsonsessionsd7034f5d-ee47-4c1f-95eb-e10c5793c8dfandb74095ee-b89b-4411-a991-46da76cd84cd.Real behavior proof
Behavior addressed: session entries that persisted bare model ids (no
<provider>/prefix) silently fell throughagentRuntime: claude-clipolicy and routed Slack DMs to the embedded Anthropic-Messages harness, where Claude subscription OAuth tokens 404 against/v1/messagesand cascade all anthropic auth profiles into cooldown.Real environment tested: Production OpenClaw 2026.5.20 gateway running on Proxmox LXC CT 112 (Debian, Node 24.13.0). Carries 8 active agents (main/Dobby, gabe, gabe-voice, researcher, forge, architect, marcus, scribe), 27 cron jobs, live Slack channel (
agent:main:slack:direct:u01q3fya3s4),anthropic:claude-cliOAuth subscription auth (Claude Pro Max). Bundle-level equivalent of this patch applied to the installeddist/model-runtime-policy-pAmabiPO.js+dist/model-runtime-aliases-CF-sDqQj.js, gateway restarted, real Slack DM sent.Exact steps or command run after this patch:
node -c.systemctl --user restart openclaw-gateway.service.mainagent from Brian's user account through the existingagent:main:slack:direct:u01q3fya3s4channel: "Confirm your runtime: which provider+model is serving this reply, and was your auth profile in cooldown?"/tmp/openclaw/openclaw-2026-05-24.logfor matching events.node scripts/run-vitest.mjs src/agents/model-runtime-policy.test.ts src/agents/model-runtime-aliases.test.ts— 28 tests pass.Evidence after fix:
Gateway log lines triggered by the Slack DM (timestamps CDT):
Dobby's self-reported reply over Slack:
Test suite outputs (paired with the real-environment evidence above, not standalone):
node scripts/run-vitest.mjs src/agents/model-runtime-policy.test.ts src/agents/model-runtime-aliases.test.ts—Test Files 3 passed (3), Tests 28 passed (28).node scripts/run-oxlint.mjs <touched files>—exit=0.pnpm tsgo:core—exit=0.pnpm check:changed— typecheck core green; pre-existingsrc/plugins/document-extractors.runtime.test.ts(53,74): error TS2554on cleanorigin/main e9ca3115f0and unrelated to this patch (verified by stash/un-stash).Observed result after fix: same Slack DM lane that was 404-cascading and cooldown-spinning yesterday now routes through the
claude-clisubprocess.provider=claude-cli,durationMs=13.4s, 44 raw output lines, noembedded runevents, noerrorPreview, noauth_profile_failure_state_updated, nolane task error. Concurrent cron jobs continue to succeed in parallel as they did before.What was not tested:
google-gemini-cliequivalent path — no failing session existed to replay, and the fix is structurally symmetric withclaude-cliso the same defensive logic applies. Doctor'scodex-route-warnings.tscallers — they always pass a qualified ref viaparseModelRef, never empty, so the empty-provider arm of the patch never fires for them.🤖 Generated with Claude Code