Skip to content

fix(agents): match runtime policy entries when session provider is empty#85970

Merged
altaywtf merged 3 commits into
openclaw:mainfrom
potterdigital:fix/runtime-policy-empty-provider-match
May 24, 2026
Merged

fix(agents): match runtime policy entries when session provider is empty#85970
altaywtf merged 3 commits into
openclaw:mainfrom
potterdigital:fix/runtime-policy-empty-provider-match

Conversation

@potterdigital

@potterdigital potterdigital commented May 24, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Session entries persisted with a bare model id (no <provider>/ prefix) silently fell through agents.defaults.models["<provider>/<model>"].agentRuntime policy lookup. modelEntryMatchKind in src/agents/model-runtime-policy.ts evaluated normalizeProviderId("anthropic") !== normalizeProviderId("") → returned "none" for every provider-prefixed entry, so a configured agentRuntime: claude-cli was never matched.
  • Consequence on a production gateway running Claude subscription OAuth via anthropic:claude-cli: requests fell through to the embedded Anthropic-Messages harness, which sent the OAuth token (sk-ant-oat01-…) to /v1/messages and got back 404 model_not_found. All anthropic auth profiles cascaded into 40-min cooldown; every subsequent request surfaced All models failed (3): … No available auth profile for anthropic until cooldown expired. Concurrent cron/heartbeat runs that constructed a fresh runtime context with the qualified ref kept succeeding through provider=claude-cli.
  • Fix: treat empty/undefined caller provider as "no constraint" — the entry's slash-prefix is itself authoritative. Mirror in 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 on Object.entries order. Then short-circuit resolveCliRuntimeExecutionProvider (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.json sessions d7034f5d-ee47-4c1f-95eb-e10c5793c8df and b74095ee-b89b-4411-a991-46da76cd84cd.

Real behavior proof

Behavior addressed: session entries that persisted bare model ids (no <provider>/ prefix) silently fell through agentRuntime: claude-cli policy and routed Slack DMs to the embedded Anthropic-Messages harness, where Claude subscription OAuth tokens 404 against /v1/messages and 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-cli OAuth subscription auth (Claude Pro Max). Bundle-level equivalent of this patch applied to the installed dist/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:

  1. Applied the equivalent patch to the two installed bundle files, verified with node -c.
  2. systemctl --user restart openclaw-gateway.service.
  3. Sent a real Slack DM to the main agent from Brian's user account through the existing agent:main:slack:direct:u01q3fya3s4 channel: "Confirm your runtime: which provider+model is serving this reply, and was your auth profile in cooldown?"
  4. Tailed /tmp/openclaw/openclaw-2026-05-24.log for matching events.
  5. 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):

02:30:50.766  cli exec: provider=claude-cli model=claude-opus-4-7[1m] promptChars=834 trigger=user useResume=true session=present resumeSession=27c5793ef4a7 reuse=reusable
02:31:04.215  claude live session turn: provider=claude-cli model=claude-opus-4-7[1m] durationMs=13431 rawLines=44

Dobby's self-reported reply over Slack:

"Runtime: claude-cli/claude-opus-4-7[1m] via anthropic:claude-cli (OAuth, Claude Pro Max). Not in cooldown — auth healthy, fallbacks armed (opus-4-7, opus-4-6). Gateway came up 9m ago on 2026.5.20 (e510042)."

image image

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.tsTest Files 3 passed (3), Tests 28 passed (28).
  • node scripts/run-oxlint.mjs <touched files>exit=0.
  • pnpm tsgo:coreexit=0.
  • pnpm check:changed — typecheck core green; pre-existing src/plugins/document-extractors.runtime.test.ts(53,74): error TS2554 on clean origin/main e9ca3115f0 and 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-cli subprocess. provider=claude-cli, durationMs=13.4s, 44 raw output lines, no embedded run events, no errorPreview, no auth_profile_failure_state_updated, no lane task error. Concurrent cron jobs continue to succeed in parallel as they did before.

What was not tested: google-gemini-cli equivalent path — no failing session existed to replay, and the fix is structurally symmetric with claude-cli so the same defensive logic applies. Doctor's codex-route-warnings.ts callers — they always pass a qualified ref via parseModelRef, never empty, so the empty-provider arm of the patch never fires for them.

🤖 Generated with Claude Code

@openclaw-barnacle openclaw-barnacle Bot added agents Agent runtime and tooling size: S proof: supplied External PR includes structured after-fix real behavior proof. labels May 24, 2026
@clawsweeper

clawsweeper Bot commented May 24, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed May 24, 2026, 2:16 PM ET / 18:16 UTC.

Summary
The PR updates agent runtime policy matching and CLI alias resolution so providerless session models can match unique provider-prefixed agentRuntime entries, fail closed on ambiguous matches, and adds regression tests plus a changelog entry.

PR surface: Source +57, Tests +155, Docs +1. Total +213 across 5 files.

Reproducibility: yes. Current main source shows provider-prefixed agents.defaults.models entries cannot match when the caller provider is empty, and the PR adds focused test cases that exercise that exact path.

Merge readiness
Overall: 🐚 platinum hermit
Proof: 🦞 diamond lobster
Patch quality: 🐚 platinum hermit
Result: ready for maintainer review.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • none

Risk before merge

  • Existing configs with duplicate provider-prefixed policies for the same bare model now fail closed instead of resolving by object order; maintainers need to accept that upgrade behavior.
  • The patch changes runtime/auth selection for persisted providerless sessions, so maintainer review should confirm this is the intended compatibility path for Claude CLI and structurally similar CLI deployments.

Maintainer options:

  1. Accept the upgrade behavior and land (recommended)
    Maintainers can accept the unique-match plus fail-closed semantics because the diff carries the matched provider, rejects incompatible CLI aliases, and includes live proof plus focused tests.
  2. Require an operator diagnostic first
    If ambiguity needs to be visible at runtime, ask for a narrow warning or error path with a focused test before merge.
  3. Pause if providerless sessions should migrate only
    If runtime repair for providerless persisted sessions should live only in doctor or migration tooling, pause this PR and keep current runtime selection unchanged.

Next step before merge
No automated repair is needed; the remaining action is maintainer acceptance of the compatibility-sensitive auth/session routing behavior.

Security
Cleared: The diff touches auth-sensitive routing but does not add dependencies, CI changes, secret storage, token logging, or new supply-chain execution; merge risk is tracked separately.

Review details

Best 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 agents.defaults.models entries cannot match when the caller provider is empty, and the PR adds focused test cases that exercise that exact path.

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 changes

Label changes:

  • add proof: sufficient: Contributor real behavior proof is sufficient. The PR body supplies after-fix production gateway logs and Slack result text for the runtime path, plus focused test and Cloudflare proof after rebase.
  • add rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🦞 diamond lobster and patch quality is 🐚 platinum hermit.
  • remove rating: 🦀 challenger crab: Current PR rating is rating: 🐚 platinum hermit, so this older rating label is no longer current.

Label justifications:

  • P1: The reported bug can route Claude CLI/OAuth sessions through the wrong Anthropic runtime path and stall active agent workflows until auth cooldowns expire.
  • merge-risk: 🚨 compatibility: Providerless saved sessions and duplicate bare-model policy maps change from previous fallback/order behavior to unique-match or fail-closed behavior.
  • merge-risk: 🚨 auth-provider: The patch changes which provider/runtime pair handles Anthropic CLI OAuth-backed requests when persisted session provider data is empty.
  • merge-risk: 🚨 session-state: The affected path is resumed session execution, including providerless saved model ids and CLI session reuse.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🦞 diamond lobster and patch quality is 🐚 platinum hermit.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (logs): The PR body supplies after-fix production gateway logs and Slack result text for the runtime path, plus focused test and Cloudflare proof after rebase.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body supplies after-fix production gateway logs and Slack result text for the runtime path, plus focused test and Cloudflare proof after rebase.
Evidence reviewed

PR surface:

Source +57, Tests +155, Docs +1. Total +213 across 5 files.

View PR surface stats
Area Files Added Removed Net
Source 2 79 22 +57
Tests 2 155 0 +155
Docs 1 1 0 +1
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 5 235 22 +213

Acceptance criteria:

  • node scripts/run-vitest.mjs src/agents/model-runtime-policy.test.ts src/agents/model-runtime-aliases.test.ts
  • node scripts/run-oxlint.mjs src/agents/model-runtime-policy.ts src/agents/model-runtime-aliases.ts src/agents/model-runtime-policy.test.ts src/agents/model-runtime-aliases.test.ts
  • pnpm tsgo:core

What I checked:

  • Current main root cause: Current main requires the provider prefix on a model policy key to equal params.provider, so an empty caller provider cannot match anthropic/<model> entries; the CLI alias resolver also keys runtime lookup by the empty provider. (src/agents/model-runtime-policy.ts:84, af0776987136)
  • Shipped behavior check: The latest release baseline commit contains the same strict provider comparison and empty-provider CLI lookup behavior, so the reported behavior is shipped and not only a current-main artifact. (src/agents/model-runtime-policy.ts:84, a374c3a5bfd5)
  • PR policy resolver fix: The PR head treats an empty caller provider as unconstrained, carries matchedProvider, and makes same-scope provider ambiguity terminal before wildcard fallback. (src/agents/model-runtime-policy.ts:83, f5daa8498868)
  • PR CLI alias fix: The PR head uses the matched policy provider as the effective provider for CLI runtime alias lookup when the caller provider is empty, while returning undefined when no compatible provider is known. (src/agents/model-runtime-aliases.ts:284, f5daa8498868)
  • Regression coverage: The added tests cover empty-provider exact matches, non-empty provider mismatch rejection, provider wildcard matching, agent-specific precedence over defaults, ambiguity fail-closed behavior, and incompatible CLI runtime aliases. (src/agents/model-runtime-policy.test.ts:240, f5daa8498868)
  • User-facing contract: The docs say exact provider/model runtime policy wins, provider wildcards apply next, and Claude CLI deployments should keep anthropic/<model> model refs with model-scoped agentRuntime.id: "claude-cli". Public docs: docs/gateway/config-agents.md. (docs/gateway/config-agents.md:498, af0776987136)

Likely related people:

  • @steipete: The available history shows Peter Steinberger authored the release commit that added src/agents/model-runtime-policy.ts and src/agents/model-runtime-aliases.ts in the shipped v2026.5.22 baseline. (role: introduced behavior; confidence: medium; commits: a374c3a5bfd5; files: src/agents/model-runtime-policy.ts, src/agents/model-runtime-aliases.ts)
  • @vincentkoc: Current-main blame and log history in this checkout point to Vincent Koc carrying the resolver comparison and alias files in a later main commit. (role: recent area contributor; confidence: medium; commits: 98e09e881794; files: src/agents/model-runtime-policy.ts, src/agents/model-runtime-aliases.ts)
  • @altaywtf: Altay force-pushed the latest reviewed head, authored the ambiguity follow-up commits, and posted the rebased Cloudflare proof context for this runtime-routing fix. (role: PR shepherd and recent reviewer; confidence: medium; commits: fd48e52c9f2d, f5daa8498868; files: src/agents/model-runtime-policy.ts, src/agents/model-runtime-aliases.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

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 keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. P1 High-priority user-facing bug, regression, or broken workflow. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 session-state 🚨 May lose, corrupt, stale, or mis-associate session, agent, or context state. merge-risk: 🚨 auth-provider 🚨 May break OAuth, tokens, provider routing, model choice, or credentials. labels May 24, 2026
@clawsweeper

clawsweeper Bot commented May 24, 2026

Copy link
Copy Markdown
Contributor

ClawSweeper PR egg

✨ Hatched: 🥚 common Frosted Branchling

Hatch command

Comment @clawsweeper hatch when this PR is hatchable.

Hatchability rules:

  • Merged PRs are hatchable.
  • Open PRs are hatchable when they are status: 👀 ready for maintainer look, status: 🚀 automerge armed, or labeled clawsweeper:automerge.
  • Closed unmerged PRs are hatchable only when one of those hatchable labels is still present in the durable record.

Rarity: 🥚 common.
Trait: sparkles near resolved comments.
Image traits: location flaky test forest; accessory green check lantern; palette seafoam, black, and opal; mood watchful; pose guarding a tiny green check; shell frosted glass shell; lighting bright celebratory glints; background tiny shells and proof notes.
Share on X: post this hatch
Copy: My PR egg hatched a 🥚 common Frosted Branchling in ClawSweeper.

What is this egg doing here?
  • Eggs appear after the PR passes real-behavior proof. It is here for vibes, not verdicts: it does not change labels, ratings, merge decisions, or automation.
  • The shell reacts to review momentum: open follow-up work warms it up, re-review makes it wobble, and a clean final review lets it hatch.
  • Hatchability usually comes from sufficient real-behavior proof, no blocking P0/P1/P2 findings, no security attention needed, and clean correctness. A merged PR is already final, so merge makes the egg hatchable independently.
  • The hatch is seeded from this repository and PR number, so the same PR keeps the same creature; the reviewed head SHA can only change safe visual details.
  • Rarity is just collectible sparkle: 🥚 common, 🌱 uncommon, 💎 rare, ✨ glimmer, and 🌈 legendary.

@potterdigital
potterdigital force-pushed the fix/runtime-policy-empty-provider-match branch from 467585d to f774505 Compare May 24, 2026 08:13
@potterdigital

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

Addressed both findings:

  • [P1] resolveAgentModelEntryRuntimePolicy now resolves ambiguity within a single model-map scope. Returns from the first scope with matches, preserving documented agent-list > defaults precedence.
  • [P2] ResolvedModelRuntimePolicy.matchedProvider carries the entry's provider forward; resolveCliRuntimeExecutionProvider uses it as effectiveProvider for the CLI_RUNTIME_BY_PROVIDER lookup. Misconfigured pairings like openrouter/X: claude-cli no longer route to claude-cli on empty caller provider.

Two new tests added (one per finding). vitest + oxlint + tsgo:core + check:changed all green.

@clawsweeper

clawsweeper Bot commented May 24, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

Re-review progress:

@clawsweeper clawsweeper Bot added rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. and removed rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. labels May 24, 2026
@altaywtf altaywtf self-assigned this May 24, 2026
@altaywtf
altaywtf requested review from a team as code owners May 24, 2026 15:46
@github-actions github-actions Bot added the dependencies-changed PR changes dependency-related files label May 24, 2026
@openclaw-barnacle openclaw-barnacle Bot added docs Improvements or additions to documentation channel: telegram Channel integration: telegram app: android App: android gateway Gateway runtime scripts Repository scripts labels May 24, 2026
@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🦞 diamond lobster Very strong PR readiness with only minor maintainer review expected. and removed rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. labels May 24, 2026
@altaywtf
altaywtf force-pushed the fix/runtime-policy-empty-provider-match branch from b568e7a to 02f0e8b Compare May 24, 2026 16:19
@openclaw-barnacle openclaw-barnacle Bot removed the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 24, 2026
@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🦀 challenger crab Exceptional PR readiness: strong proof, clean patch, and convincing validation. and removed rating: 🦞 diamond lobster Very strong PR readiness with only minor maintainer review expected. labels May 24, 2026
@altaywtf
altaywtf force-pushed the fix/runtime-policy-empty-provider-match branch from 02f0e8b to c221773 Compare May 24, 2026 18:02
@openclaw-barnacle openclaw-barnacle Bot removed the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 24, 2026
@altaywtf
altaywtf force-pushed the fix/runtime-policy-empty-provider-match branch from c221773 to f5daa84 Compare May 24, 2026 18:08
@altaywtf

Copy link
Copy Markdown
Member

@clawsweeper re-review

Prepared again after rebasing onto current main.

  • Head: f5daa84
  • Base included: af07769
  • Cloudflare proof: cbx_2c3fc3b5cbde passed focused runtime-policy Vitest targets, touched-file oxlint, and pnpm tsgo:core
  • Prep artifacts refreshed: .local/prep.md and .local/prep.env

Please re-review this latest head before merge.

@clawsweeper

clawsweeper Bot commented May 24, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

Re-review progress:

@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. and removed rating: 🦀 challenger crab Exceptional PR readiness: strong proof, clean patch, and convincing validation. labels May 24, 2026
@github-actions github-actions Bot added the dependencies-changed PR changes dependency-related files label May 24, 2026
@openclaw-barnacle openclaw-barnacle Bot added scripts Repository scripts docker Docker and sandbox tooling and removed size: M proof: sufficient ClawSweeper judged the real behavior proof convincing. labels May 24, 2026
@altaywtf

Copy link
Copy Markdown
Member

Merged via squash.

Thanks @potterdigital!

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

Labels

agents Agent runtime and tooling merge-risk: 🚨 auth-provider 🚨 May break OAuth, tokens, provider routing, model choice, or credentials. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 session-state 🚨 May lose, corrupt, stale, or mis-associate session, agent, or context state. P1 High-priority user-facing bug, regression, or broken workflow. proof: supplied External PR includes structured after-fix real behavior proof. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. size: M status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants