diff --git a/agent/credential_pool_admin.py b/agent/credential_pool_admin.py
index 0dd6482043..127970d448 100644
--- a/agent/credential_pool_admin.py
+++ b/agent/credential_pool_admin.py
@@ -67,6 +67,23 @@ class CredentialPoolAdminMixin:
self._current_id = None
return removed
+ def move_entry(self, credential_id: str, priority: int) -> Optional[PooledCredential]:
+ """Place an entry at a clamped zero-based position and persist contiguous priorities."""
+ from agent.credential_pool import _normalize_pool_priorities
+
+ with self._lock:
+ entry = self._find(lambda e: e.id == credential_id)
+ if entry is None:
+ return None
+ others = [e for e in self._entries if e.id != credential_id]
+ others.insert(max(0, min(int(priority), len(others))), entry)
+ entries = [replace(e, priority=p) for p, e in enumerate(others)]
+ # Apply load-time ordering now so the reported position survives reload.
+ _normalize_pool_priorities(self.provider, entries)
+ self._entries = sorted(entries, key=lambda e: e.priority)
+ self._persist()
+ return self._find(lambda e: e.id == credential_id)
+
def resolve_target(self, target: Any) -> Tuple[Optional[int], Optional[PooledCredential], Optional[str]]:
raw = str(target or "").strip()
if not raw:
diff --git a/hermes_cli/_parser.py b/hermes_cli/_parser.py
index 37ff7a8e09..97bacacc5e 100644
--- a/hermes_cli/_parser.py
+++ b/hermes_cli/_parser.py
@@ -74,6 +74,7 @@ Examples:
hermes auth list List pooled credentials
hermes auth remove
Remove pooled credential by index, id, or label
hermes auth reset [t] Clear exhaustion status for a provider, or one credential
+ hermes auth priority
Move a pooled credential to priority n (0 = tried first)
hermes model Select default model
hermes fallback [list] Show fallback provider chain
hermes fallback add Add a fallback provider (same picker as `hermes model`)
diff --git a/hermes_cli/auth_commands.py b/hermes_cli/auth_commands.py
index 4da1852e19..7daf2f8097 100644
--- a/hermes_cli/auth_commands.py
+++ b/hermes_cli/auth_commands.py
@@ -349,6 +349,14 @@ def auth_add_command(args) -> None:
if not is_custom:
_unsuppress_provider_sources(provider)
+ wanted_priority = getattr(args, "priority", None)
+ before = {entry.id for entry in pool.entries()}
+ _add_credential(args, provider, pool, requested_type)
+ if wanted_priority is not None:
+ _place_added_credential(provider, before, int(wanted_priority))
+
+
+def _add_credential(args, provider: str, pool, requested_type: str) -> None:
if requested_type == AUTH_TYPE_API_KEY:
_add_api_key_credential(args, provider, pool)
return
@@ -379,6 +387,64 @@ def auth_add_command(args) -> None:
print(f'Added {provider} OAuth credential #{len(pool.entries())}: "{entry.label}"')
+def _place_added_credential(provider: str, before_ids: set, priority: int) -> None:
+ """Move the credential `auth add` just created to *priority*.
+
+ Every add path (api key, OAuth spec, Nous) persists through the pool, so the
+ new row is the one id that was not there before the add. Reloading rather
+ than reusing the add's pool object keeps this correct for paths that write
+ their own store.
+ """
+ pool = load_pool(provider)
+ entries = pool.entries()
+ added = [entry for entry in entries if entry.id not in before_ids]
+ if len(added) == 1:
+ target = added[0]
+ elif not added and len(entries) == 1:
+ # The add updated the sole existing row in place (a repeat Nous login).
+ target = entries[0]
+ else:
+ # The credential was saved; only the placement is unresolved, so do not fail.
+ print(f"note: could not identify the credential just added to {provider}; set its "
+ f"priority with `hermes auth priority {provider} {priority}`.",
+ file=sys.stderr)
+ return
+ moved = pool.move_entry(target.id, priority)
+ _report_priority(provider, pool, moved, priority, "Placed", "at")
+
+
+def _report_priority(provider: str, pool, moved, requested: int, verb: str, prep: str) -> None:
+ """Print the effective priority and say why it differs from the request, if it does."""
+ print(f'{verb} {provider} credential "{moved.label}" {prep} priority {moved.priority} '
+ f"(#{moved.priority + 1} in `hermes auth list {provider}`)")
+ size = len(pool.entries())
+ if moved.priority != requested:
+ if requested < 0 or requested >= size:
+ reason = f"the pool has {size} credentials, so it was clamped"
+ else:
+ reason = "anthropic keeps manually added credentials ahead of seeded ones"
+ print(f"note: requested priority {requested}; effective priority is {moved.priority} "
+ f"because {reason}.", file=sys.stderr)
+ strategy = get_pool_strategy(provider)
+ if strategy != STRATEGY_FILL_FIRST:
+ print(f"note: {provider} uses the {strategy} strategy; priority only orders "
+ f"fill_first selection.", file=sys.stderr)
+
+
+def auth_priority_command(args) -> None:
+ """`hermes auth priority `: reorder one pooled credential."""
+ provider = _normalize_provider(getattr(args, "provider", ""))
+ pool = load_pool(provider)
+ index, matched, error = pool.resolve_target(getattr(args, "target", None))
+ if matched is None or index is None:
+ raise SystemExit(f"{error} Provider: {provider}.")
+ requested = int(getattr(args, "priority"))
+ moved = pool.move_entry(matched.id, requested)
+ if moved is None:
+ raise SystemExit(f'No credential matching "{getattr(args, "target", None)}" for provider {provider}.')
+ _report_priority(provider, pool, moved, requested, "Set", "to")
+
+
def auth_list_command(args) -> None:
provider_filter = _normalize_provider(getattr(args, "provider", "") or "")
if provider_filter:
@@ -665,7 +731,8 @@ def _interactive_strategy() -> None:
_AUTH_ACTIONS = {
"add": auth_add_command, "list": auth_list_command, "remove": auth_remove_command,
- "reset": auth_reset_command, "status": auth_status_command, "logout": auth_logout_command,
+ "reset": auth_reset_command, "priority": auth_priority_command, "status": auth_status_command,
+ "logout": auth_logout_command,
"spotify": auth_spotify_command}
diff --git a/hermes_cli/console_engine.py b/hermes_cli/console_engine.py
index db658435bb..1e4778c7b0 100644
--- a/hermes_cli/console_engine.py
+++ b/hermes_cli/console_engine.py
@@ -292,7 +292,7 @@ _CLI_FAMILIES: dict[str, tuple[_CliSurface, str]] = {
"memory": (_sub("memory", "build_memory_parser", "cmd_memory"), "status, *off, *reset"),
"auth": (
_sub("auth", "build_auth_parser", "cmd_auth"),
- "list, status, *reset, *add, *remove, *logout, spotify status, *spotify login, "
+ "list, status, *reset, *priority, *add, *remove, *logout, spotify status, *spotify login, "
"*spotify logout"),
"pairing": (
_sub("pairing", "build_pairing_parser", "cmd_pairing"),
diff --git a/hermes_cli/subcommands/auth.py b/hermes_cli/subcommands/auth.py
index 62e4d5d151..61cb8c953f 100644
--- a/hermes_cli/subcommands/auth.py
+++ b/hermes_cli/subcommands/auth.py
@@ -16,6 +16,10 @@ def build_auth_parser(subparsers, *, cmd_auth: Callable) -> None:
"--type", dest="auth_type", choices=["oauth", "api-key", "api_key"],
help="Credential type to add")
auth_add.add_argument("--label", help="Optional display label")
+ auth_add.add_argument(
+ "--priority", type=int,
+ help="Place the new credential at this priority (0 = tried first under fill_first); "
+ "appends last when omitted")
auth_add.add_argument("--api-key", help="API key value (otherwise prompted securely)")
auth_add.add_argument("--portal-url", help="Nous portal base URL")
auth_add.add_argument("--inference-url", help="Nous inference base URL")
@@ -39,6 +43,11 @@ def build_auth_parser(subparsers, *, cmd_auth: Callable) -> None:
auth_reset.add_argument(
"target", nargs="?",
help="Optional credential index, entry id, or exact label; clears every credential when omitted")
+ auth_priority = auth_subparsers.add_parser(
+ "priority", help="Move a pooled credential to a priority (0 = tried first under fill_first)")
+ auth_priority.add_argument("provider", help="Provider id")
+ auth_priority.add_argument("target", help="Credential index, entry id, or exact label")
+ auth_priority.add_argument("priority", type=int, help="New priority; others are renumbered")
auth_status = auth_subparsers.add_parser("status", help="Show auth status for a provider")
auth_status.add_argument("provider", help="Provider id")
auth_logout = auth_subparsers.add_parser(
diff --git a/website/docs/reference/cli-commands.md b/website/docs/reference/cli-commands.md
index 0a48663380..c7c0a3d4b6 100644
--- a/website/docs/reference/cli-commands.md
+++ b/website/docs/reference/cli-commands.md
@@ -581,7 +581,9 @@ hermes auth list # Show all pools
hermes auth list openrouter # Show specific provider
hermes auth add openrouter --api-key sk-or-v1-xxx # Add API key
hermes auth add anthropic --type oauth # Add OAuth credential
+hermes auth add openai-codex --type oauth --priority 0 # Add an account and try it first
hermes auth remove openrouter 2 # Remove by index
+hermes auth priority openrouter backup-key 0 # Move a credential to the front of fill_first order
hermes auth reset openrouter # Clear cooldowns
hermes auth reset openrouter 2 # Clear the cooldown on one credential
hermes auth status anthropic # Show auth status for a provider
@@ -589,7 +591,7 @@ hermes auth logout anthropic # Log out and clear sto
hermes auth spotify # Authenticate Hermes with Spotify via PKCE
```
-Subcommands: `add`, `list`, `remove`, `reset`, `status`, `logout`, `spotify`. When called with no subcommand, launches the interactive management wizard.
+Subcommands: `add`, `list`, `remove`, `reset`, `priority`, `status`, `logout`, `spotify`. When called with no subcommand, launches the interactive management wizard.
## `hermes status`
diff --git a/website/docs/user-guide/features/credential-pools.md b/website/docs/user-guide/features/credential-pools.md
index cedaaa89a4..1c9aee9860 100644
--- a/website/docs/user-guide/features/credential-pools.md
+++ b/website/docs/user-guide/features/credential-pools.md
@@ -116,6 +116,8 @@ Type [1/2]:
| `hermes auth add ` | Add a credential (prompts for type and key) |
| `hermes auth add --type api-key --api-key ` | Add an API key non-interactively |
| `hermes auth add --type oauth` | Add an OAuth credential via browser login |
+| `hermes auth add --priority 0` | Add a credential and place it first in the `fill_first` order |
+| `hermes auth priority ` | Move a credential to priority `n` (0 = tried first); the rest are renumbered |
| `hermes auth remove ` | Remove credential by 1-based index |
| `hermes auth reset ` | Clear all cooldowns/exhaustion status |
| `hermes auth reset ` | Clear the cooldown on one credential by index, id, or label |
@@ -132,7 +134,7 @@ credential_pool_strategies:
| Strategy | Behavior |
|----------|----------|
-| `fill_first` (default) | Use the first healthy key until it's exhausted, then move to the next |
+| `fill_first` (default) | Use the first healthy key until it's exhausted, then move to the next; order is each credential's `priority` (`hermes auth priority` changes it) |
| `round_robin` | Cycle through keys evenly, rotating after each selection |
| `least_used` | Always pick the key with the lowest request count |
| `random` | Random selection among healthy keys |