Skip to content

cli-shim: hermes model picker + runtime-resolver fix (builds on #26634) - #36084

Closed
Radu-Podila wants to merge 11 commits into
NousResearch:mainfrom
Radu-Podila:cli-shim-upstream
Closed

cli-shim: hermes model picker + runtime-resolver fix (builds on #26634)#36084
Radu-Podila wants to merge 11 commits into
NousResearch:mainfrom
Radu-Podila:cli-shim-upstream

Conversation

@Radu-Podila

Copy link
Copy Markdown

What does this PR do?

Builds on #26634 (the cli-shim local-CLI model provider) and makes it work
end-to-end. Two gaps in the original draft prevented cli-shim from being
usable: it couldn't be selected from the UI, and selecting it in config made
the agent crash at startup. This PR fixes both. Full credit for the cli-shim
feature itself goes to the author of #26634 — those commits are carried
unchanged; this PR only adds the two fixes on top.

Related Issue

Builds on / depends on #26634 (no separate issue filed).

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)
  • ✨ New feature (non-breaking change that adds functionality)
  • 🔒 Security fix
  • 📝 Documentation update
  • ✅ Tests (adding or improving test coverage)
  • ♻️ Refactor (no behavior change)
  • 🎯 New skill (bundled or hub)

Changes Made

  • hermes_cli/runtime_provider.py — add a cli-shim branch in
    resolve_runtime_provider (mirrors copilot-acp). feat(providers): cli-shim — shell out to claude/codex/gemini CLIs via OAuth (RFC) #26634 wired
    resolve_provider_client but not this path, so the TUI/gateway fell through
    to the API-key fallback, defaulted the provider to openrouter, and aborted
    with "Provider resolver returned an empty API key". Now returns
    base_url=cli://shim, api_key=cli-shim (placeholder), api_mode=chat_completions.
  • hermes_cli/models.py — exempt cli-shim from the external_process
    auto-inject skip so it appears in the hermes model picker.
  • hermes_cli/main.py — add _model_flow_cli_shim, a no-API-key selection
    flow that lists the profile's CLI model aliases and persists provider: cli-shim.
  • plugins/model-providers/cli-shim/__init__.py — add display_name /
    description so the picker row reads sensibly.

How to Test

  1. Have a logged-in claude CLI on the host (echo hi | claude --print --model sonnet).
  2. hermes model → select "CLI shim (claude/codex/gemini)" → pick
    claude-sonnet-cli. Confirm it writes model.provider: cli-shim to config.yaml.
  3. Start hermes and send a prompt — it should route through claude --print
    with no API key, instead of erroring at provider resolution.
  4. Sanity check the resolver directly:
    python -c "from hermes_cli.runtime_provider import resolve_runtime_provider as r; print(r(requested='cli-shim'))"
    provider/base_url/api_key = cli-shim / cli://shim / cli-shim.

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits (feat(cli-shim):, fix(cli-shim):)
  • I searched for existing PRs (this explicitly builds on feat(providers): cli-shim — shell out to claude/codex/gemini CLIs via OAuth (RFC) #26634)
  • My PR contains only changes related to this fix/feature
  • I've run pytest tests/ -q and all tests pass
  • I've added tests for my changes
  • I've tested on my platform: Linux (Fedora-based server, root install)

Documentation & Housekeeping

Screenshots / Logs

Before (provider: cli-shim, on #26634 alone):

⚠️ Provider resolver returned an empty API key. Set OPENROUTER_API_KEY

After: agent initializes on cli-shim and answers via claude --print with no API key.

Gradata and others added 10 commits May 13, 2026 18:15
Hermes agent loop calls create(stream=True) for most turns and iterates
the result with 'for chunk in stream'. Previously the shim ignored
stream= and returned the whole SimpleNamespace response, causing
'types.SimpleNamespace object is not iterable' downstream.

Now emit OpenAI-shaped chunks: one content delta, one per tool_call,
and a final chunk with finish_reason + usage. CLIs don't support
per-token streaming so each chunk carries the full payload.

This was the root cause of fleet-wide cli-shim failures after the
provider was wired up.
# Conflicts:
#	run_agent.py
cli-shim was excluded from the model picker because external_process auth was
skipped in the auto-inject list (non-api-key flows were left as an open
question in the RFC). This adds the missing picker UX:

- exempt cli-shim from the external_process auto-inject skip (models.py)
- add _model_flow_cli_shim (main.py): a no-API-key selection flow that lets
  the user pick among the profile's CLI model aliases and persists
  provider=cli-shim
- give the profile a display_name/description so the picker row reads well

Now `hermes model` -> "CLI shim (claude/codex/gemini)" sets
model.provider=cli-shim plus the chosen alias, no config.yaml hand-editing.
…eway path)

cli-shim wired up resolve_provider_client (client construction) but not
resolve_runtime_provider, which the TUI/gateway use via
_ensure_runtime_credentials to resolve credentials before building the
client. With provider=cli-shim the resolver fell through every named branch
to the API-key fallback, returned no key/base_url, defaulted
resolved_provider to openrouter, and bailed with:
  "Provider resolver returned an empty API key. Set OPENROUTER_API_KEY"

Add a cli-shim branch mirroring copilot-acp: base_url=cli://shim,
api_key=cli-shim placeholder, api_mode=chat_completions. The marker base_url
then routes client creation to CliShimClient as intended.
Pins both fixes:
- resolve_runtime_provider(requested="cli-shim") returns
  cli-shim / cli://shim / cli-shim and does NOT fall back to openrouter
  (regression test for the empty-API-key abort).
- cli-shim is surfaced in CANONICAL_PROVIDERS with a sensible picker label,
  and the profile reports external_process auth + its CLI model aliases.
@alt-glitch alt-glitch added type/feature New feature or request P3 Low — cosmetic, nice to have comp/cli CLI entry point, hermes_cli/, setup wizard comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/plugins Plugin system and bundled plugins labels May 31, 2026
…errors there)

A non-zero exit was reported as "CLI 'x' exited N: <stderr>", but Claude Code
prints fatal errors to stdout, not stderr — so the message was blank
("exited 1:") and the cause invisible. Include the stdout tail (preferring
stderr) in both _run_print and _run_codex_exec.
@teknium1

Copy link
Copy Markdown
Contributor

Thanks for the substantial work wiring the CLI-backed provider through runtime resolution and the model picker. This automated hermes-sweeper review is closing this under the standing in-tree provider-integration policy.

  • The PR adds plugins/model-providers/cli-shim/ and a cli-shim provider that shells out to third-party claude, codex, and gemini CLIs (plugins/model-providers/cli-shim/__init__.py:1-49; PR head 799e347ec05d).
  • Current policy requires new third-party/niche provider integrations to ship as standalone plugins, rather than adding a provider directory and core routing hooks to this repository (AGENTS.md:797-813). This is a coupling and maintenance decision, not a judgment on the implementation quality.
  • The supported path is a standalone plugin installed under ~/.hermes/plugins/model-providers/; that discovery path is already documented and user plugins can override bundled profiles (plugins/model-providers/README.md:19-26). Please consider publishing it as a separate plugin repository and sharing it in #plugins-skills-and-skins.

Closed as not-planned per standing maintainer policy (in-tree-provider-integration). This is a design-direction decision, not a code-quality judgment — see the Contribution Rubric in AGENTS.md for what the project is looking for. If you believe this policy was misapplied to your change, comment here and a maintainer will take a look.

@teknium1 teknium1 closed this Jul 13, 2026
@teknium1 teknium1 added the sweeper:not-planned Sweeper: closed per standing maintainer policy (design direction) label Jul 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/cli CLI entry point, hermes_cli/, setup wizard comp/plugins Plugin system and bundled plugins P3 Low — cosmetic, nice to have sweeper:not-planned Sweeper: closed per standing maintainer policy (design direction) type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants