Skip to content

fix(minimax-oauth): refresh short-lived access tokens per request - #30619

Merged
teknium1 merged 2 commits into
mainfrom
hermes/hermes-87f551bc
May 22, 2026
Merged

fix(minimax-oauth): refresh short-lived access tokens per request#30619
teknium1 merged 2 commits into
mainfrom
hermes/hermes-87f551bc

Conversation

@teknium1

Copy link
Copy Markdown
Contributor

Summary

MiniMax OAuth sessions no longer break ~15 minutes in. The Anthropic SDK was caching the access token as a static string at client construction, so a session that resolved credentials once at startup kept sending the same bearer until MiniMax returned a 401 mid-conversation.

Root cause

hermes_cli/auth.py::_refresh_minimax_oauth_state() was already proactive — it refreshes when expires_at - now <= MINIMAX_OAUTH_REFRESH_SKEW_SECONDS (60s). But it only fired at session start, because resolve_minimax_oauth_runtime_credentials() returned a materialized string that the Anthropic SDK then froze on the wire. Nothing re-checked expiry between API calls.

MiniMax-issued access tokens are ~15 minutes. Outside the 60s skew window we never looked at them again.

Fix

Reuse the existing Entra-ID bearer-hook plumbing in agent/anthropic_adapter.py: when api_key is callable, build_anthropic_client builds a custom httpx.Client whose request event hook mints a fresh bearer per outbound request. That contract is already wired and tested for Azure Foundry.

New: build_minimax_oauth_token_provider() in hermes_cli/auth.py — a zero-arg callable that on each invocation re-reads auth.json, calls _refresh_minimax_oauth_state() (no-op when fresh, refreshes inside the skew window), and returns the access token. Wired in at agent/agent_init.py and agent/agent_runtime_helpers.py (the model-switch path), so for provider="minimax-oauth" the agent's _anthropic_api_key is the callable instead of a string. Every rebuild of the Anthropic client preserves that callable.

Refreshes persist to auth.json, so a refresh done by one process (CLI) is immediately visible to others (gateway, cron) on next request.

Why not at resolve_runtime_provider

Returning a callable from resolve_runtime_provider would break the auxiliary client paths that pass api_key straight to OpenAI(api_key=...) (string-only contract). The wire-up lives at the agent-init / model-switch boundary instead — main agent loop gets the callable, aux paths keep the string contract.

Steady-state cost

One auth.json read + one timestamp compare per outbound request. Refresh POST to /oauth/token fires only inside the 60s skew window.

Changes

File Δ
hermes_cli/auth.py +build_minimax_oauth_token_provider(), _minimax_oauth_quarantine_on_terminal_refresh() helper extracted, resolve_minimax_oauth_runtime_credentials(as_token_provider=...) opt-in kwarg
agent/agent_init.py Swap string → callable for minimax-oauth; guard _is_oauth_token() against non-string keys
agent/agent_runtime_helpers.py Same swap in the /model switch path
tests/test_minimax_oauth.py +8 tests: fresh / refresh / re-read each call / not-logged-in / terminal-quarantine / opt-in shape / default shape

Validation

Before After
Token refresh during session Never (until 401) On every request, if expiry < 60s
Auth.json refresh persisted Only at startup if expiring Per-request, visible cross-process
Aux client contract string string (unchanged)
Targeted test pass n/a 1746 / 1746 across tests/run_agent/, tests/agent/test_anthropic_adapter.py, tests/agent/test_minimax_provider.py, tests/hermes_cli/test_runtime_provider_resolution.py, tests/test_minimax_oauth.py

E2E: simulated a token with 30s of life left → callable fires POST to https://api.minimax.io/oauth/token, materializes new bearer, persists to auth.json. Full agent init with provider="minimax-oauth" shows agent._anthropic_api_key is callable, _is_anthropic_oauth stays False (no spurious Claude-Code identity headers), Anthropic client built with placeholder + http_client bearer hook.

Infographic

MiniMax OAuth token refresh per request

teknium1 added 2 commits May 22, 2026 14:42
MiniMax OAuth issues ~15-minute access tokens. The Anthropic SDK caches
api_key as a static string at client construction, so a session that
resolves credentials once at startup keeps sending the same bearer until
MiniMax returns 401 mid-session.

Swap the static string for a callable token provider, reusing the existing
Entra-ID bearer-hook infrastructure in build_anthropic_client. The callable
re-reads auth.json on each invocation and calls _refresh_minimax_oauth_state,
which is a no-op when the token still has more than 60s of life left and
refreshes proactively otherwise. Refreshes persist to auth.json so other
processes (gateway, cron) see them immediately.

The wire-up lives at the agent-init / model-switch boundary rather than in
resolve_runtime_provider, so aux client paths that hand the api_key string
to OpenAI(api_key=...) are unaffected.
@github-actions

Copy link
Copy Markdown
Contributor

🔎 Lint report: hermes/hermes-87f551bc vs origin/main

ruff

Total: 0 on HEAD, 0 on base (➖ 0)

🆕 New issues: none

✅ Fixed issues: none

Unchanged: 0 pre-existing issues carried over.

ty (type checker)

Total: 8991 on HEAD, 8991 on base (➖ 0)

🆕 New issues: none

✅ Fixed issues: none

Unchanged: 4770 pre-existing issues carried over.

Diagnostics are surfaced as warnings — this check never fails the build.

Comment thread agent/agent_init.py Dismissed
@alt-glitch alt-glitch added type/bug Something isn't working P3 Low — cosmetic, nice to have provider/minimax MiniMax (Anthropic transport) area/auth Authentication, OAuth, credential pools comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint labels May 22, 2026
@teknium1
teknium1 merged commit a84cec6 into main May 22, 2026
19 of 20 checks passed
@teknium1
teknium1 deleted the hermes/hermes-87f551bc branch May 22, 2026 22:16
Gpapas pushed a commit to Gpapas/hermes-agent that referenced this pull request May 23, 2026
…usResearch#30619)

* fix(minimax-oauth): refresh short-lived access tokens per request

MiniMax OAuth issues ~15-minute access tokens. The Anthropic SDK caches
api_key as a static string at client construction, so a session that
resolves credentials once at startup keeps sending the same bearer until
MiniMax returns 401 mid-session.

Swap the static string for a callable token provider, reusing the existing
Entra-ID bearer-hook infrastructure in build_anthropic_client. The callable
re-reads auth.json on each invocation and calls _refresh_minimax_oauth_state,
which is a no-op when the token still has more than 60s of life left and
refreshes proactively otherwise. Refreshes persist to auth.json so other
processes (gateway, cron) see them immediately.

The wire-up lives at the agent-init / model-switch boundary rather than in
resolve_runtime_provider, so aux client paths that hand the api_key string
to OpenAI(api_key=...) are unaffected.

* docs: add infographic for minimax-oauth token refresh
Mucky010 pushed a commit to Mucky010/hermes-agent that referenced this pull request May 24, 2026
…usResearch#30619)

* fix(minimax-oauth): refresh short-lived access tokens per request

MiniMax OAuth issues ~15-minute access tokens. The Anthropic SDK caches
api_key as a static string at client construction, so a session that
resolves credentials once at startup keeps sending the same bearer until
MiniMax returns 401 mid-session.

Swap the static string for a callable token provider, reusing the existing
Entra-ID bearer-hook infrastructure in build_anthropic_client. The callable
re-reads auth.json on each invocation and calls _refresh_minimax_oauth_state,
which is a no-op when the token still has more than 60s of life left and
refreshes proactively otherwise. Refreshes persist to auth.json so other
processes (gateway, cron) see them immediately.

The wire-up lives at the agent-init / model-switch boundary rather than in
resolve_runtime_provider, so aux client paths that hand the api_key string
to OpenAI(api_key=...) are unaffected.

* docs: add infographic for minimax-oauth token refresh
mathias3 pushed a commit to mathias3/hermes-agent that referenced this pull request May 28, 2026
…usResearch#30619)

* fix(minimax-oauth): refresh short-lived access tokens per request

MiniMax OAuth issues ~15-minute access tokens. The Anthropic SDK caches
api_key as a static string at client construction, so a session that
resolves credentials once at startup keeps sending the same bearer until
MiniMax returns 401 mid-session.

Swap the static string for a callable token provider, reusing the existing
Entra-ID bearer-hook infrastructure in build_anthropic_client. The callable
re-reads auth.json on each invocation and calls _refresh_minimax_oauth_state,
which is a no-op when the token still has more than 60s of life left and
refreshes proactively otherwise. Refreshes persist to auth.json so other
processes (gateway, cron) see them immediately.

The wire-up lives at the agent-init / model-switch boundary rather than in
resolve_runtime_provider, so aux client paths that hand the api_key string
to OpenAI(api_key=...) are unaffected.

* docs: add infographic for minimax-oauth token refresh
Bryce-huang pushed a commit to wbkunlun/hermes-agent that referenced this pull request May 29, 2026
…usResearch#30619)

* fix(minimax-oauth): refresh short-lived access tokens per request

MiniMax OAuth issues ~15-minute access tokens. The Anthropic SDK caches
api_key as a static string at client construction, so a session that
resolves credentials once at startup keeps sending the same bearer until
MiniMax returns 401 mid-session.

Swap the static string for a callable token provider, reusing the existing
Entra-ID bearer-hook infrastructure in build_anthropic_client. The callable
re-reads auth.json on each invocation and calls _refresh_minimax_oauth_state,
which is a no-op when the token still has more than 60s of life left and
refreshes proactively otherwise. Refreshes persist to auth.json so other
processes (gateway, cron) see them immediately.

The wire-up lives at the agent-init / model-switch boundary rather than in
resolve_runtime_provider, so aux client paths that hand the api_key string
to OpenAI(api_key=...) are unaffected.

* docs: add infographic for minimax-oauth token refresh
#AI commit#
gweeteve pushed a commit to gweeteve/hermes-agent that referenced this pull request Jun 2, 2026
…usResearch#30619)

* fix(minimax-oauth): refresh short-lived access tokens per request

MiniMax OAuth issues ~15-minute access tokens. The Anthropic SDK caches
api_key as a static string at client construction, so a session that
resolves credentials once at startup keeps sending the same bearer until
MiniMax returns 401 mid-session.

Swap the static string for a callable token provider, reusing the existing
Entra-ID bearer-hook infrastructure in build_anthropic_client. The callable
re-reads auth.json on each invocation and calls _refresh_minimax_oauth_state,
which is a no-op when the token still has more than 60s of life left and
refreshes proactively otherwise. Refreshes persist to auth.json so other
processes (gateway, cron) see them immediately.

The wire-up lives at the agent-init / model-switch boundary rather than in
resolve_runtime_provider, so aux client paths that hand the api_key string
to OpenAI(api_key=...) are unaffected.

* docs: add infographic for minimax-oauth token refresh
Seven74AI pushed a commit to Seven74AI/hermes-agent that referenced this pull request Jun 13, 2026
…usResearch#30619)

* fix(minimax-oauth): refresh short-lived access tokens per request

MiniMax OAuth issues ~15-minute access tokens. The Anthropic SDK caches
api_key as a static string at client construction, so a session that
resolves credentials once at startup keeps sending the same bearer until
MiniMax returns 401 mid-session.

Swap the static string for a callable token provider, reusing the existing
Entra-ID bearer-hook infrastructure in build_anthropic_client. The callable
re-reads auth.json on each invocation and calls _refresh_minimax_oauth_state,
which is a no-op when the token still has more than 60s of life left and
refreshes proactively otherwise. Refreshes persist to auth.json so other
processes (gateway, cron) see them immediately.

The wire-up lives at the agent-init / model-switch boundary rather than in
resolve_runtime_provider, so aux client paths that hand the api_key string
to OpenAI(api_key=...) are unaffected.

* docs: add infographic for minimax-oauth token refresh
alt-glitch pushed a commit that referenced this pull request Jun 14, 2026
…0619)

* fix(minimax-oauth): refresh short-lived access tokens per request

MiniMax OAuth issues ~15-minute access tokens. The Anthropic SDK caches
api_key as a static string at client construction, so a session that
resolves credentials once at startup keeps sending the same bearer until
MiniMax returns 401 mid-session.

Swap the static string for a callable token provider, reusing the existing
Entra-ID bearer-hook infrastructure in build_anthropic_client. The callable
re-reads auth.json on each invocation and calls _refresh_minimax_oauth_state,
which is a no-op when the token still has more than 60s of life left and
refreshes proactively otherwise. Refreshes persist to auth.json so other
processes (gateway, cron) see them immediately.

The wire-up lives at the agent-init / model-switch boundary rather than in
resolve_runtime_provider, so aux client paths that hand the api_key string
to OpenAI(api_key=...) are unaffected.

* docs: add infographic for minimax-oauth token refresh
T02200059 pushed a commit to T02200059/hermes-agent that referenced this pull request Jun 18, 2026
…usResearch#30619)

* fix(minimax-oauth): refresh short-lived access tokens per request

MiniMax OAuth issues ~15-minute access tokens. The Anthropic SDK caches
api_key as a static string at client construction, so a session that
resolves credentials once at startup keeps sending the same bearer until
MiniMax returns 401 mid-session.

Swap the static string for a callable token provider, reusing the existing
Entra-ID bearer-hook infrastructure in build_anthropic_client. The callable
re-reads auth.json on each invocation and calls _refresh_minimax_oauth_state,
which is a no-op when the token still has more than 60s of life left and
refreshes proactively otherwise. Refreshes persist to auth.json so other
processes (gateway, cron) see them immediately.

The wire-up lives at the agent-init / model-switch boundary rather than in
resolve_runtime_provider, so aux client paths that hand the api_key string
to OpenAI(api_key=...) are unaffected.

* docs: add infographic for minimax-oauth token refresh
liuchanchen pushed a commit to liuchanchen/hermes-agent that referenced this pull request Jun 23, 2026
…usResearch#30619)

* fix(minimax-oauth): refresh short-lived access tokens per request

MiniMax OAuth issues ~15-minute access tokens. The Anthropic SDK caches
api_key as a static string at client construction, so a session that
resolves credentials once at startup keeps sending the same bearer until
MiniMax returns 401 mid-session.

Swap the static string for a callable token provider, reusing the existing
Entra-ID bearer-hook infrastructure in build_anthropic_client. The callable
re-reads auth.json on each invocation and calls _refresh_minimax_oauth_state,
which is a no-op when the token still has more than 60s of life left and
refreshes proactively otherwise. Refreshes persist to auth.json so other
processes (gateway, cron) see them immediately.

The wire-up lives at the agent-init / model-switch boundary rather than in
resolve_runtime_provider, so aux client paths that hand the api_key string
to OpenAI(api_key=...) are unaffected.

* docs: add infographic for minimax-oauth token refresh
waefrebeorn pushed a commit to waefrebeorn/slermes that referenced this pull request Jul 2, 2026
…usResearch#30619)

* fix(minimax-oauth): refresh short-lived access tokens per request

MiniMax OAuth issues ~15-minute access tokens. The Anthropic SDK caches
api_key as a static string at client construction, so a session that
resolves credentials once at startup keeps sending the same bearer until
MiniMax returns 401 mid-session.

Swap the static string for a callable token provider, reusing the existing
Entra-ID bearer-hook infrastructure in build_anthropic_client. The callable
re-reads auth.json on each invocation and calls _refresh_minimax_oauth_state,
which is a no-op when the token still has more than 60s of life left and
refreshes proactively otherwise. Refreshes persist to auth.json so other
processes (gateway, cron) see them immediately.

The wire-up lives at the agent-init / model-switch boundary rather than in
resolve_runtime_provider, so aux client paths that hand the api_key string
to OpenAI(api_key=...) are unaffected.

* docs: add infographic for minimax-oauth token refresh
donbowman pushed a commit to donbowman/hermes-agent that referenced this pull request Jul 13, 2026
…usResearch#30619)

* fix(minimax-oauth): refresh short-lived access tokens per request

MiniMax OAuth issues ~15-minute access tokens. The Anthropic SDK caches
api_key as a static string at client construction, so a session that
resolves credentials once at startup keeps sending the same bearer until
MiniMax returns 401 mid-session.

Swap the static string for a callable token provider, reusing the existing
Entra-ID bearer-hook infrastructure in build_anthropic_client. The callable
re-reads auth.json on each invocation and calls _refresh_minimax_oauth_state,
which is a no-op when the token still has more than 60s of life left and
refreshes proactively otherwise. Refreshes persist to auth.json so other
processes (gateway, cron) see them immediately.

The wire-up lives at the agent-init / model-switch boundary rather than in
resolve_runtime_provider, so aux client paths that hand the api_key string
to OpenAI(api_key=...) are unaffected.

* docs: add infographic for minimax-oauth token refresh
Gravezzz pushed a commit to Gravezzz/hermes-agent that referenced this pull request Jul 21, 2026
…usResearch#30619)

* fix(minimax-oauth): refresh short-lived access tokens per request

MiniMax OAuth issues ~15-minute access tokens. The Anthropic SDK caches
api_key as a static string at client construction, so a session that
resolves credentials once at startup keeps sending the same bearer until
MiniMax returns 401 mid-session.

Swap the static string for a callable token provider, reusing the existing
Entra-ID bearer-hook infrastructure in build_anthropic_client. The callable
re-reads auth.json on each invocation and calls _refresh_minimax_oauth_state,
which is a no-op when the token still has more than 60s of life left and
refreshes proactively otherwise. Refreshes persist to auth.json so other
processes (gateway, cron) see them immediately.

The wire-up lives at the agent-init / model-switch boundary rather than in
resolve_runtime_provider, so aux client paths that hand the api_key string
to OpenAI(api_key=...) are unaffected.

* docs: add infographic for minimax-oauth token refresh
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/auth Authentication, OAuth, credential pools comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint P3 Low — cosmetic, nice to have provider/minimax MiniMax (Anthropic transport) type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants