Skip to content

feat(auth): support named credentials with manual selection (#76937) - #76987

Open
webtecnica wants to merge 1 commit into
NousResearch:mainfrom
webtecnica:feat/76937-named-credential-selection
Open

feat(auth): support named credentials with manual selection (#76937)#76987
webtecnica wants to merge 1 commit into
NousResearch:mainfrom
webtecnica:feat/76937-named-credential-selection

Conversation

@webtecnica

Copy link
Copy Markdown
Contributor

Summary

Adds optional named credentials to the credential pool, enabling manual selection of which API key to use per task/session — not just passive auto-rotation on 402/429 errors.

Change

  • New optional name field on PooledCredential (distinct from display label), set via hermes auth add <provider> --api-key ... --name daily
  • New default_auth config map (provider -> credential name) under config.yaml, with schema entry
  • Pool selection honors the named credential when configured, falling back to the existing auto-rotate behavior for unnamed keys
  • CLI wiring: --name prompt/flag in auth_commands.py + auth.py persistence + subcommand passthrough
  • Tests: tests/agent/test_credential_pool_named_selection.py (new) + tests/hermes_cli/test_auth_commands.py (extended)

Verification

  • pytest tests/agent/test_credential_pool_named_selection.py — 28 passed
  • pytest tests/agent/test_credential_pool.py — 96 passed
  • pytest tests/hermes_cli/test_auth_commands.py — 14 passed

Closes #76937

@alt-glitch alt-glitch added type/feature New feature or request comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/cli CLI entry point, hermes_cli/, setup wizard area/auth Authentication, OAuth, credential pools P3 Low — cosmetic, nice to have sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades labels Aug 2, 2026

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for building the credential persistence and fallback mechanics. The current implementation is a useful base, but it does not yet provide the per-task/session selection requested in #76937.

Problems

  • #76937 proposes hermes chat ... --auth <name>. This PR adds --name only to hermes auth add (hermes_cli/subcommands/auth.py:+31) and reads one profile-wide default_auth[provider] preference (agent/credential_pool.py:+492, +618), so concurrent sessions cannot choose different keys.
  • Delegated children bypass the preference: the PR narrows normal selection (agent/credential_pool.py:+1810), but CredentialPool.acquire_lease() chooses from all available entries (agent/credential_pool.py:1971-1985) and tools/delegate_tool.py:1992 uses that path.
  • A name is not unique: the new helper only strips it, while the selection helper accepts all matching entries. Duplicate --name daily credentials therefore do not identify one key.

Suggested changes

  • Add and propagate a session-scoped selector, preserving a global default only as fallback.
  • Apply the constraint to leasing and cover delegated children, duplicate names, and two simultaneous sessions.
  • Document the finalized CLI/config behavior.

Automated hermes-sweeper review.

@@ -28,6 +28,11 @@ def build_auth_parser(subparsers, *, cmd_auth: Callable) -> None:
help="Credential type to add",
)
auth_add.add_argument("--label", help="Optional display label")
auth_add.add_argument(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This only names credentials at creation time. #76937 asks for hermes chat ... --auth <name> selection per task/session, but this diff adds no session-level selector or propagation into agent construction; a profile-wide config preference cannot let concurrent sessions choose different keys.

Comment thread agent/credential_pool.py
def _select_unlocked(self, *, refresh: bool = True) -> Optional[PooledCredential]:
available = self._available_entries(clear_expired=True, refresh=refresh)
if not available:
self._current_id = None
self._log_no_available_entries()
return None

# Manual selection (#76937): pin to the configured default_auth
# credential while it is available; fall back to auto-rotate below
# when it is missing or exhausted.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please apply this narrowing to acquire_lease() as well. tools/delegate_tool.py:1992 leases child credentials through that method, and its current implementation chooses from all available entries, so delegated tasks can bypass the selected credential.

@GottZ

GottZ commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

This was generated by AI during triage.

Summary

One PR addresses #76937. #76987 adds named-credential persistence and profile-wide default selection, but its diff does not implement the requested per-task/session selector and does not apply the selection constraint to delegated credential leases.

Related pull requests

Suggested consolidation

Keep #76987 open with a salvage path: retain its credential naming, persistence, profile-wide fallback, and tests, then add and propagate a session-scoped selector, apply the same narrowing to CredentialPool.acquire_lease(), and define or enforce unique credential-name semantics. This follows the visible keep_open review on #76987; there are no duplicate PRs to close.

Complex graph

flowchart LR
    classDef open fill:#dbeafe,stroke:#1d4ed8,color:#1e3a8a
    classDef merged fill:#dcfce7,stroke:#15803d,color:#14532d
    classDef closed fill:#e5e7eb,stroke:#6b7280,color:#1f2937
    classDef unverified fill:#f3f4f6,stroke:#9ca3af,color:#374151
    classDef best stroke-width:3px,stroke:#b45309
    classDef target stroke-width:3px,stroke:#4338ca
    I76937(["issue #76937 (open)"])
    P76987["PR #76987 (open)"]
    P76987 -->|best fix| I76937
    class I76937 open
    class P76987 open
    class P76987 best
    class P76987 target
    click I76937 "https://github.com/NousResearch/hermes-agent/issues/76937"
    click P76987 "https://github.com/NousResearch/hermes-agent/pull/76987"
Loading

Graph: solid arrow = fixes / best fix, dashed arrow = partial or unverified (see edge label); boxed group = PRs duplicating each other; amber border = best fix; indigo border = target; gray node = closed (state tag in the node label).

Cross-PR triage: Reviewed 1 pull request and 1 issue in this complex. Each diff was read against this issue; Assessment working set: 27 kB of PR diffs, 3 kB of issue/PR text, 2 kB of discussion (3 comments), 2 verify verdicts. verdicts reflect diff content, not PR titles. Part of an automated triage batch.

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 comp/cli CLI entry point, hermes_cli/, setup wizard P3 Low — cosmetic, nice to have sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Manual API key selection in credential pool (not just auto-rotate on error)

4 participants