Skip to content

Core integration plan: minimally-invasive opt-in claude_stream api_mode (restores tool-calls, Hermes skills, interactivity) #9

Description

@Ouroborosrex

Implementation plan for the Hermes-core half of #7 — turning the Claude Code provider from a one-shot engine into a first-class agentic + interactive provider — with the smallest, lowest-risk footprint. It mirrors the existing, accepted codex_app_server integration almost 1:1, including its solution to the "Hermes tools unreachable" problem.

The three limits to remove

  1. No streamed tool steps (Hermes sees only final text).
  2. Hermes' own tools/skills unreachable by the agent ("skills don't work").
  3. No interactivity (skill browsing / approvals → one-shot text dump).

Why this is safe (design principles)

  1. Opt-in, default-off. Config flag model.claude_runtime: stream_session, exactly like model.openai_runtime: codex_app_server. Unset → zero behavior change for anyone.
  2. Additive, not invasive. All substantive code in new files; edits to existing core files are a handful of small guarded hooks copied from the codex pattern.
  3. Short-circuit early. Hands the whole turn to a new path at the top of the loop (beside the existing codex_app_server short-circuit at conversation_loop.py:575), never entangling the chat_completions/anthropic dispatch.
  4. Reuse proven boundaries. Build on the M1 PoC (ClaudeStreamSession, M1 PoC: persistent multi-turn Claude Code stream-json session (#7) #8), the existing agent/transports/hermes_tools_mcp_server.py, and acp_adapter/edit_approval.py.
  5. Always-degradable. Any session/protocol failure falls back to the existing chat_completions shim (engine mode) — never hard-breaks a turn.
  6. Reversible. Remove the flag → today's behavior. The whole feature is one removable code path.

The key reuse: Hermes tools/skills over MCP (already solved for Codex)

agent/transports/hermes_tools_mcp_server.py already exposes Hermes' richer surface — web search, browser, delegate_task subagents, vision, memory, skills, cross-session search, image gen, TTS — to the codex app-server subprocess via stdio MCP, precisely because codex owns the loop and otherwise can't reach them. Claude Code has the identical need and the identical mechanism: spawn claude --mcp-config <hermes-tools-mcp> --strict-mcp-config, and Claude Code can call Hermes' tools/skills as MCP tools. This is what makes Hermes skills work again on the Claude path — and it's a copy of a working integration, not new ground.

Limit → fix map

Limit Fix
No streamed tool steps P2 (M2): map stream-json tool_use/tool_result/text deltas → Hermes streaming + tool-step rendering + usage/cost (parsing already proven in #8).
Hermes tools/skills unreachable P3: claude --mcp-config → reuse hermes_tools_mcp_server.py so the agent calls Hermes tools/skills over MCP.
Non-interactive P4 (M3): bridge stream-json permission/can_use_tool + MCP-tool calls to Hermes' approval/edit-approval UI → real back-and-forth (skill browsing & approvals prompt in Hermes).

Surgical change list (the entire core surface)

New files (no risk to existing code):

  • agent/claude_stream_runtime.pyrun_claude_stream_turn(...), mirror of agent/codex_runtime.run_codex_app_server_turn.
  • agent/transports/claude_stream_session.py — productionized ClaudeStreamSession from M1 PoC: persistent multi-turn Claude Code stream-json session (#7) #8 (persistent process, event parsing, session cache keyed by Hermes conversation id, cwd, lifecycle).

Guarded edits to existing files (small, additive, flag-gated):

  • hermes_cli/runtime_provider.py — add _maybe_apply_claude_stream_runtime(...) mirroring _maybe_apply_codex_app_server_runtime (~L280) + its call site (~L431); reads model.claude_runtime. (~12 lines)
  • agent/agent_init.py:317 — add "claude_stream" to the api_mode allow-set. (1 line)
  • agent/conversation_loop.py:575 — add if agent.api_mode == "claude_stream": return agent._run_claude_stream_turn(...) beside the codex short-circuit. (~4 lines)
  • run_agent.py (~L5340) — add a _run_claude_stream_turn forwarder mirroring _run_codex_app_server_turn. (~6 lines)

Total existing-core diff: ~4 guarded edits, ≤ ~25 lines, all behind the flag. Everything else is new files.

Phasing (each shippable, each behind the flag)

  • P1 – Turn round-trip: flag → ClaudeStreamSession → one Hermes turn end-to-end; fallback to shim on error.
  • P2 – Event mapping (M2): streamed tool steps + usage/cost in the Hermes UI.
  • P3 – Hermes tools/skills over MCP: --mcp-config + hermes_tools_mcp_server.py → skills/tools callable by the agent.
  • P4 – Permissions/interactivity (M3): approvals + skill browsing prompt in Hermes.
  • P5 – Lifecycle/hardening (M5): session cache, idle eviction, cancel/interrupt, crash-restart, concurrency, tests/CI.

Risk register

  • New-path bug breaks turns → default-off flag + degrade-to-shim fallback + phased/tested.
  • Diverges from upstream Hermes → additive + mirrors an accepted pattern ⇒ upstreamable as a small PR, or run on a thin fork; net diff is tiny.
  • MCP bridge over-exposes Hermes tools → curated allowlist (the codex server already curates a subset) + route writes through the approval bridge (P4).
  • Protocol drift across claude versions → defensive parsing (already in M1 PoC: persistent multi-turn Claude Code stream-json session (#7) #8) + pin/verify in CI.

Acceptance criteria

  • Flag off → byte-identical to today (no regressions); net diff to existing core ≤ ~25 lines, all guarded.
  • Flag on → a Hermes turn streams Claude Code's tool steps into the UI; multi-turn context persists in the right cwd; Hermes skills/tools are invocable by the agent (over MCP); interactive flows (approvals, skill browsing) prompt in Hermes instead of a text dump; usage/cost recorded; any failure degrades to the shim.

Relationship

Implements M2–M5 of #7. M0 (main) + M1 (#8) already de-risk the protocol. The provider profile + shim stay in this plugin; only the opt-in api_mode plumbing is upstream Hermes.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions