Skip to content

fix(copilot): token exchange + 401 auth recovery + live context + ACP HOME env#15114

Merged
teknium1 merged 5 commits into
mainfrom
hermes/hermes-172af8ae
Apr 24, 2026
Merged

fix(copilot): token exchange + 401 auth recovery + live context + ACP HOME env#15114
teknium1 merged 5 commits into
mainfrom
hermes/hermes-172af8ae

Conversation

@teknium1

Copy link
Copy Markdown
Contributor

Summary

Consolidated salvage of 4 GitHub Copilot PRs — token exchange, 401 auth recovery, live context-length resolution, and HOME env propagation for ACP subprocesses. Attribution preserved via rebase-merge.

Changes

  • hermes_cli/copilot_auth.py + hermes_cli/auth.py + agent/credential_pool.py — add exchange_copilot_token() which calls GET https://api.github.com/copilot_internal/v2/token to trade a raw GitHub token for a short-lived Copilot API token (the semicolon-separated tid=...;exp=...;sku=... format). Previous code used the raw GitHub token as Authorization: Bearer, which only works for some account types. Cherry-pick from @difujia (fix(copilot): exchange raw GitHub token for Copilot API JWT #12876).
  • run_agent.py — add _try_refresh_copilot_client_credentials() paralleling the existing Nous/Anthropic/Codex refresh paths. On HTTP 401 from Copilot, refresh credentials and rebuild the OpenAI client instead of failing the request. Cherry-pick from @l0hde (feat(copilot): add 401 auth recovery with automatic token refresh and client rebuild #10179).
  • agent/model_metadata.py + hermes_cli/models.py — probe Copilot's live /models endpoint for max_prompt_tokens on a per-account basis. Catches account-specific models (e.g. claude-opus-4.6-1m) that don't exist in models.dev, and returns the provider-enforced limit rather than the nominal marketing number. Cached 1h in-process. Cherry-pick from @difujia (fix(copilot): wire live /models max_prompt_tokens into context-window resolver #12840).
  • agent/copilot_acp_client.py — pass HOME env var into Copilot ACP subprocess. Without this, the subprocess inherits an empty or stale HOME and fails to locate config. Includes fallback chain: get_subprocess_home()$HOMEexpanduser("~") → pwd lookup → /tmp. Cherry-pick from @MestreY0d4-Uninter (fix: set HOME for Copilot ACP subprocesses #11285), with /home/openclaw hardcoded fallback replaced by /tmp.

Credit

Validation

scripts/run_tests.sh tests/hermes_cli/test_copilot_token_exchange.py tests/hermes_cli/test_copilot_context.py tests/hermes_cli/test_copilot_auth.py tests/hermes_cli/test_copilot_in_model_list.py tests/agent/test_copilot_acp_client.py tests/agent/test_credential_pool.py tests/run_agent/test_run_agent_codex_responses.py tests/hermes_cli/test_model_switch_copilot_api_mode.py — 137/137 passing.

All four PRs cherry-picked cleanly with no conflicts against current main.

Not included in this salvage

MestreY0d4-Uninter and others added 5 commits April 24, 2026 05:08
Pass an explicit HOME into Copilot ACP child processes so delegated ACP runs do not fail when the ambient environment is missing HOME.

Prefer the per-profile subprocess home when available, then fall back to HOME, expanduser('~'), pwd.getpwuid(...), and /home/openclaw. Add regression tests for both profile-home preference and clean HOME fallback.

Refs #11068.
… client rebuild

When using GitHub Copilot as provider, HTTP 401 errors could cause
Hermes to silently fall back to the next model in the chain instead
of recovering. This adds a one-shot retry mechanism that:

1. Re-resolves the Copilot token via the standard priority chain
   (COPILOT_GITHUB_TOKEN -> GH_TOKEN -> GITHUB_TOKEN -> gh auth token)
2. Rebuilds the OpenAI client with fresh credentials and Copilot headers
3. Retries the failed request before falling back

The fix handles the common case where the gho_* OAuth token remains
valid but the httpx client state becomes stale (e.g. after startup
race conditions or long-lived sessions).

Key design decisions:
- Always rebuild client even if token string unchanged (recovers stale state)
- Uses _apply_client_headers_for_base_url() for canonical header management
- One-shot flag guard prevents infinite 401 loops (matches existing pattern
  used by Codex/Nous/Anthropic providers)
- No token exchange via /copilot_internal/v2/token (returns 404 for some
  account types; direct gho_* auth works reliably)

Tests: 3 new test cases covering end-to-end 401->refresh->retry,
client rebuild verification, and same-token rebuild scenarios.
Docs: Updated providers.md with Copilot auth behavior section.
Raw GitHub tokens (gho_/github_pat_/ghu_) are now exchanged for
short-lived Copilot API tokens via /copilot_internal/v2/token before
being used as Bearer credentials. This is required to access
internal-only models (e.g. claude-opus-4.6-1m with 1M context).

Implementation:
- exchange_copilot_token(): calls the token exchange endpoint with
  in-process caching (dict keyed by SHA-256 fingerprint), refreshed
  2 minutes before expiry. No disk persistence — gateway is long-running
  so in-memory cache is sufficient.
- get_copilot_api_token(): convenience wrapper with graceful fallback —
  returns exchanged token on success, raw token on failure.
- Both callers (hermes_cli/auth.py and agent/credential_pool.py) now
  pipe the raw token through get_copilot_api_token() before use.

12 new tests covering exchange, caching, expiry, error handling,
fingerprinting, and caller integration. All 185 existing copilot/auth
tests pass.

Part 2 of #7731.
… resolver

The Copilot provider resolved context windows via models.dev static data,
which does not include account-specific models (e.g. claude-opus-4.6-1m
with 1M context). This adds the live Copilot /models API as a higher-
priority source for copilot/copilot-acp/github-copilot providers.

New helper get_copilot_model_context() in hermes_cli/models.py extracts
capabilities.limits.max_prompt_tokens from the cached catalog. Results
are cached in-process for 1 hour.

In agent/model_metadata.py, step 5a queries the live API before falling
through to models.dev (step 5b). This ensures account-specific models
get correct context windows while standard models still have a fallback.

Part 1 of #7731.
Refs: #7272
@teknium1 teknium1 merged commit b3aed6c into main Apr 24, 2026
10 of 12 checks passed
@teknium1 teknium1 deleted the hermes/hermes-172af8ae branch April 24, 2026 12:09
@alt-glitch alt-glitch added type/bug Something isn't working P1 High — major feature broken, no workaround provider/copilot GitHub Copilot (ACP + Chat) area/auth Authentication, OAuth, credential pools comp/agent Core agent loop, run_agent.py, prompt builder comp/cli CLI entry point, hermes_cli/, setup wizard comp/acp Agent Communication Protocol adapter labels Apr 24, 2026
tunings-lab added a commit to tunings-lab/hermes-agent that referenced this pull request Jun 10, 2026
feat(copilot): consolidate GHE enterprise support on top of upstream NousResearch#15114
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/acp Agent Communication Protocol adapter comp/agent Core agent loop, run_agent.py, prompt builder comp/cli CLI entry point, hermes_cli/, setup wizard P1 High — major feature broken, no workaround provider/copilot GitHub Copilot (ACP + Chat) type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants