Skip to content

fix(agents): prefer ordered eligible credentials in discovery map#110542

Open
wuqxuan wants to merge 2 commits into
openclaw:mainfrom
wuqxuan:fix/110488-discovery-credential-order
Open

fix(agents): prefer ordered eligible credentials in discovery map#110542
wuqxuan wants to merge 2 commits into
openclaw:mainfrom
wuqxuan:fix/110488-discovery-credential-order

Conversation

@wuqxuan

@wuqxuan wuqxuan commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Fixes #110488

What Problem This Solves

Fixes an issue where automatic/background credential discovery would pick the first same-provider auth profile in raw store insertion order, ignoring auth.order, OAuth-over-api_key preference, and OAuth expiry. That caused ChatGPT-mode background paths (for example automatic compaction) to fail with unusable credentials while the same session still worked through profile-aware interactive resolution.

Why This Change Was Made

Background discovery now builds its one-credential-per-provider map through the same canonical resolveAuthProfileOrder ranking used for interactive profile selection, and OAuth conversion rejects expired access tokens the same way tokens already do. Discovery still does not prompt for secrets or persist resolved credentials; session-specific authProfileOverride plumbing for every background caller remains a follow-up if needed beyond global ordering.

Compatibility: no config/schema changes; reuses existing order/eligibility helpers. Performance: one order resolve per provider present in the store. Usability: automatic paths fail less often when a usable OAuth profile exists after an earlier api_key. Security: expired OAuth is no longer surfaced as a usable discovery credential.

User Impact

Automatic compaction and other non-interactive model calls for a provider should select the same preferred eligible credential as interactive resolution when profiles are ordered by type or auth.order, instead of silently binding the first raw store entry.

Evidence

Verification host: local macOS (focused Vitest; isolated state dir for discovery runtime proof).

$ node scripts/run-vitest.mjs src/agents/agent-model-discovery.auth.test.ts
 ✓ |agents| src/agents/agent-model-discovery.auth.test.ts (10 tests)
 Test Files  1 passed (1)
      Tests  10 passed (10)

Coverage includes: OAuth preferred over earlier same-provider api_key; auth.order honored; expired OAuth skipped with fallthrough; configured SecretRef markers kept under ordered read-only discovery (openclaw-secret-ref-configured); keyRef-only providers visible to discoverAuthStorage({ readOnly: true }) and omitted at runtime.

Real behavior proof

  • Behavior addressed: provider-level discovery map prefers ordered eligible credentials (OAuth / auth.order) over raw store insertion order; expired OAuth is not converted as usable; read-only discovery keeps configured SecretRef markers after routing through resolveAuthProfileOrder with readinessMode: "read-only".
  • Environment: local macOS + openclaw checkout at PR head fbd95eaf28; focused Vitest agents lane; isolated OPENCLAW_STATE_DIR for a real discoverAuthStorage path against a temporary agent SQLite auth store (no live cloud secrets).
  • Current-main contrast: with api_key inserted before OAuth for the same provider, the discovery map kept the api_key; expired OAuth with expires > 0 still converted; this patch orders via the shared resolver and rejects expired OAuth.
  • Exact steps or command run after this patch:
    1. node scripts/run-vitest.mjs src/agents/agent-model-discovery.auth.test.ts (10/10 pass), including SecretRef marker + ordered keyRef-only cases.
    2. Runtime discovery proof (redacted): temporary agent dir with SQLite auth profiles — openai api_key first + later OAuth, plus fixture-ref keyRef-only profile; call resolveAgentCredentialMapFromStore and discoverAuthStorage with/without readOnly: true.
  • Evidence after fix (redacted runtime output at head fbd95eaf28):
{
  "head": "fbd95eaf28",
  "orderedDiscoveryMap": {
    "fixture-ref": { "type": "api_key", "key": "openclaw-secret-ref-configured" },
    "openai": { "type": "oauth", "access": "oauth-access-REDACTED", "refresh": "oauth-refresh-REDACTED" }
  },
  "executionMap": {
    "openai": { "type": "oauth", "access": "oauth-access-REDACTED", "refresh": "oauth-refresh-REDACTED" }
  },
  "readOnlyHasAuth": { "openai": true, "fixture-ref": true },
  "runtimeHasAuth": { "openai": true, "fixture-ref": false }
}
  • Control check: SecretRef-only providers stay visible only for read-only discovery; runtime discovery does not claim auth for unresolved SecretRefs; OAuth still wins over an earlier api_key for the same provider.
  • Observed result after fix: discovery map returns OAuth when it ranks above api_key; returns order-selected credentials when configured; returns the configured SecretRef marker for keyRef-only profiles under read-only discovery; runtime omit of SecretRef-only providers is unchanged.
  • What was not tested: live ChatGPT-mode compaction against a real OpenAI OAuth account; session authProfileOverride threading through every background caller.

This PR was authored with AI assistance (Grok).

@wuqxuan
wuqxuan requested a review from a team as a code owner July 18, 2026 07:48
@openclaw-barnacle openclaw-barnacle Bot added agents Agent runtime and tooling size: S labels Jul 18, 2026
@clawsweeper clawsweeper Bot added rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. 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: 🚨 auth-provider 🚨 May break OAuth, tokens, provider routing, model choice, or credentials. labels Jul 18, 2026
@clawsweeper

clawsweeper Bot commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed July 18, 2026, 5:41 AM ET / 09:41 UTC.

Summary
The branch makes agent credential discovery select the ordered eligible auth profile per provider, rejects expired OAuth credentials, and adds focused discovery regression tests.

PR surface: Source +38, Tests +146. Total +184 across 3 files.

Reproducibility: yes. in source: current main retains the first convertible same-provider profile during raw store iteration, while the shared resolver already supplies the expected ordered eligible candidates. The contributor also supplied a redacted after-fix runtime run against a temporary SQLite auth store.

Review metrics: none identified.

Root-cause cluster
Relationship: fixed_by_candidate
Canonical: #110488
Summary: This PR is the concrete candidate fix for the linked report about provider-level discovery ignoring auth order, OAuth preference, and expiry.

Members:

Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything.

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:

  • Obtain ordinary review from an auth-profile area owner before merging the intentional credential-selection compatibility change.

Risk before merge

  • [P1] Merging intentionally changes which same-provider credential non-interactive paths select; deployments that had come to rely on insertion-order selection will instead follow the established auth.order and eligibility policy.

Maintainer options:

  1. Accept canonical ordering for background discovery (recommended)
    Merge the focused change so automatic model calls use the same configured ordering and credential-eligibility rules as interactive profile resolution.
  2. Pause for an explicit compatibility policy
    Keep insertion-order behavior only if maintainers intentionally want non-interactive calls to diverge from auth.order and profile eligibility.

Next step before merge

  • [P2] No concrete automated repair remains; this needs ordinary owner review of the intentional background credential-selection compatibility change.

Security
Cleared: The focused TypeScript-only auth-selection change adds no dependency, workflow, secret-resolution, permission, or supply-chain surface; it strengthens expiry filtering and retains marker-only handling for unresolved SecretRefs.

Review details

Best possible solution:

Merge the discovery bridge only after ordinary owner review confirms that background callers should share the canonical auth-profile ordering policy, preserving the focused regressions for OAuth expiry and read-only SecretRef handling.

Do we have a high-confidence way to reproduce the issue?

Yes in source: current main retains the first convertible same-provider profile during raw store iteration, while the shared resolver already supplies the expected ordered eligible candidates. The contributor also supplied a redacted after-fix runtime run against a temporary SQLite auth store.

Is this the best way to solve the issue?

Yes. Reusing resolveAuthProfileOrder is the narrowest maintainable fix because it centralizes ordering, compatibility, eligibility, cooldown, and read-only SecretRef behavior instead of duplicating a discovery-specific ranking policy.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 39528edd745a.

Label changes

Label changes:

  • add proof: sufficient: Contributor real behavior proof is sufficient. The PR includes redacted after-fix output from a real discovery call against a temporary SQLite auth store, showing ordered OAuth selection and the intended read-only versus runtime SecretRef behavior.
  • add rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🦞 diamond lobster and patch quality is 🐚 platinum hermit.
  • add status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (live_output): The PR includes redacted after-fix output from a real discovery call against a temporary SQLite auth store, showing ordered OAuth selection and the intended read-only versus runtime SecretRef behavior.
  • remove rating: 🦪 silver shellfish: Current PR rating is rating: 🐚 platinum hermit, so this older rating label is no longer current.
  • remove status: 📣 needs proof: Current PR status label is status: 👀 ready for maintainer look.

Label justifications:

  • P1: The patch addresses a reported regression where background agent operations can repeatedly select unusable credentials while interactive resolution succeeds.
  • merge-risk: 🚨 compatibility: The branch changes the credential-selection behavior for existing same-provider profile stores from insertion order to the established ordering contract.
  • merge-risk: 🚨 auth-provider: The changed selection and OAuth-expiry handling directly determine which provider credential background model calls receive.
  • 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 (live_output): The PR includes redacted after-fix output from a real discovery call against a temporary SQLite auth store, showing ordered OAuth selection and the intended read-only versus runtime SecretRef behavior.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR includes redacted after-fix output from a real discovery call against a temporary SQLite auth store, showing ordered OAuth selection and the intended read-only versus runtime SecretRef behavior.
Evidence reviewed

PR surface:

Source +38, Tests +146. Total +184 across 3 files.

View PR surface stats
Area Files Added Removed Net
Source 2 47 9 +38
Tests 1 148 2 +146
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 3 195 11 +184

What I checked:

  • Current-main behavior: Current main iterates Object.values(store.profiles), skips a provider once a credential has been stored, and therefore binds discovery to raw store insertion order rather than auth-profile ordering. (src/agents/agent-auth-credentials.ts:605, 39528edd745a)
  • Canonical ordering contract: The shared resolver applies configured/store auth order, provider compatibility, credential eligibility, read-only unresolved-reference handling, cooldown ordering, and OAuth/token/api-key preference. Reusing it avoids a second selection policy in discovery. (src/agents/auth-profiles/order.ts:7, 39528edd745a)
  • Changed discovery boundary: The PR passes discovery configuration into the credential-map builder so auth.order is considered by the non-interactive discovery path; the branch also adds focused coverage for OAuth precedence, configured ordering, expiry fallthrough, and read-only SecretRef markers. (src/agents/agent-auth-discovery.ts:52, fbd95eaf2818)
  • Prior-review continuity: The earlier P1 finding required preservation of SecretRef placeholders during read-only discovery. The current head adds assertions for configured marker retention and the previous completed review cycle recorded no remaining findings. (src/agents/agent-model-discovery.auth.test.ts:130, fbd95eaf2818)
  • Real behavior proof: The PR body provides redacted after-fix output from a temporary SQLite auth store showing OAuth selected over an earlier API key, read-only SecretRef visibility retained, and runtime omission of unresolved SecretRef-only credentials; the focused test command reports 10 passing tests and the PR's real-behavior-proof checks succeeded. (src/agents/agent-model-discovery.auth.test.ts:130, fbd95eaf2818)
  • Feature-history routing: Earlier auth-profile history identifies the shared ordering/model-auth area as primarily associated with commits b04c838c15e5, 9afcbbec5e60, and 9082fc37f3ad; related credential-semantics work includes cec404225d, 1c200ca7ae3c, and 1769fb2aa1. (src/agents/auth-profiles/order.ts:7, 39528edd745a)

Likely related people:

  • steipete: Feature-history evidence associates this person with the central auth-profile ordering and model-auth design work. (role: primary area contributor; confidence: high; commits: b04c838c15e5, 9afcbbec5e60, 9082fc37f3ad; files: src/agents/auth-profiles/order.ts, src/agents/model-auth.ts)
  • joshavant: Related history connects this person to SecretRef and shared credential-conversion semantics relevant to read-only discovery. (role: adjacent credential-semantics contributor; confidence: medium; commits: cec404225d, 1c200ca7ae3c, 1769fb2aa1; files: src/agents/pi-auth-credentials.ts, src/agents/auth-profiles/order.ts, src/agents/model-auth.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.
Review history (2 earlier review cycles)
  • reviewed 2026-07-18T07:52:01.970Z sha 738d74f :: needs real behavior proof before merge. :: [P1] Preserve SecretRef placeholders in read-only discovery
  • reviewed 2026-07-18T08:58:29.221Z sha fbd95ea :: needs maintainer review before merge. :: none

@wuqxuan

wuqxuan commented Jul 18, 2026

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

Strengthened the SecretRef read-only regression coverage: ordered discovery now asserts the configured openclaw-secret-ref-configured marker for keyRef/tokenRef profiles, including a keyRef-first + auth.order case. Updated Evidence and Real behavior proof with a redacted runtime discoverAuthStorage run against a temporary agent SQLite store (OAuth preferred over earlier api_key; SecretRef-only provider visible only in read-only discovery). Focused Vitest: 10/10 pass at head fbd95eaf28.

@clawsweeper

clawsweeper Bot commented Jul 18, 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. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. and removed rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Jul 18, 2026
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. P1 High-priority user-facing bug, regression, or broken workflow. proof: sufficient ClawSweeper judged the real behavior proof convincing. 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.

Provider-level credential discovery ignores auth.order, OAuth preference, and expiry — silently breaks ChatGPT-mode background paths

1 participant