|
| 1 | +# Deterministic Record and Replay of Hermes Runs |
| 2 | + |
| 3 | +**Class:** Ecosystem integration · **Confidence:** High · **Demo status:** Runnable |
| 4 | + |
| 5 | +## Pain Point |
| 6 | + |
| 7 | +A Hermes run that goes wrong is hard to examine twice. Re-running the same |
| 8 | +prompt gives a different answer, because the model is sampled and the system |
| 9 | +prompt carries the date and session; and the transcript on screen is not the |
| 10 | +request that was sent — the 7,742-character system prompt, the 19 tool |
| 11 | +schemas, and the exact SSE the provider streamed back are all invisible from |
| 12 | +the terminal. So "why did it do that?" gets answered from memory rather than |
| 13 | +from evidence, and a fix cannot be checked against the run that motivated it. |
| 14 | + |
| 15 | +Worse for autonomous work: to ask "would a different model have made the same |
| 16 | +call here?" you have to re-run the whole job on the other model and hope the |
| 17 | +divergence reproduces. |
| 18 | + |
| 19 | +## What It Does |
| 20 | + |
| 21 | +OrcaReplay records a Hermes run at the process and socket boundary, then |
| 22 | +re-executes it with the network switched off, serving the recorded exchanges |
| 23 | +back byte for byte. From any checkpoint the run can be forked onto a different |
| 24 | +model to see where behaviour diverges. |
| 25 | + |
| 26 | +Capture sits **outside** Hermes. Nothing is installed into `~/.hermes`, no |
| 27 | +plugin is loaded, and `config.yaml` is not touched — which matters here, |
| 28 | +because Hermes has no base-URL variable to move. `OPENAI_BASE_URL` appears in |
| 29 | +one place in the shipped source, an advisory warning for when it is set and |
| 30 | +the provider is not `custom`; the origin itself lives in `config.yaml` under |
| 31 | +`model.base_url`. So capture terminates the TLS Hermes established itself, |
| 32 | +with a certificate authority minted for the single run and deleted when it |
| 33 | +ends. |
| 34 | + |
| 35 | +What one turn produced, from the verified run below: |
| 36 | + |
| 37 | +- The full request: 42,305 bytes, 19 tools, a 7,742-character system prompt. |
| 38 | +- The response: status 200, streamed, 10,615 input and 88 output tokens. |
| 39 | +- Every shell command Hermes ran, with exit codes, and a per-turn diff of the |
| 40 | + files it touched, on one timeline with the model calls. |
| 41 | +- `orca replay` reproducing all of it offline. |
| 42 | + |
| 43 | +One Hermes detail worth knowing, because it looks like a problem and is not. |
| 44 | +Hermes makes two different kinds of HTTP call with two different libraries: |
| 45 | +the model catalogue through `requests`, and the completion through httpx under |
| 46 | +the OpenAI SDK. httpx pins certifi, so the usual `SSL_CERT_FILE` looks as |
| 47 | +though it cannot reach the call that matters. It does — Hermes resolves that |
| 48 | +client's CA itself, and the chain in `agent/ssl_verify.py` reads |
| 49 | +`HERMES_CA_BUNDLE`, then `SSL_CERT_FILE`, then `REQUESTS_CA_BUNDLE`, then |
| 50 | +`CURL_CA_BUNDLE`. So a recorder that sets the standard variables is trusted by |
| 51 | +both clients with nothing Hermes-specific added. |
| 52 | + |
| 53 | +## Setup |
| 54 | + |
| 55 | +Node 20 or newer. The Hermes adapter is on the `hermes-adapter` branch, |
| 56 | +pending review in the PR linked below; until it lands in a release, build from |
| 57 | +the checkout: |
| 58 | + |
| 59 | +```bash |
| 60 | +git clone https://github.com/Continuum-AI-Corp/OrcaReplay.git |
| 61 | +cd OrcaReplay |
| 62 | +git checkout hermes-adapter # or main, once PR #33 has landed |
| 63 | +npm ci && npm run build |
| 64 | +``` |
| 65 | + |
| 66 | +Record a run. `--tls-intercept` is required — there is no origin to redirect — |
| 67 | +and `opencode.ai` is named explicitly because it is a website as much as a |
| 68 | +gateway, so it is not in the default allowlist: |
| 69 | + |
| 70 | +```bash |
| 71 | +node packages/cli/dist/cli.js record hermes \ |
| 72 | + --tls-intercept --tls-hosts '+opencode.ai' -- \ |
| 73 | + --provider opencode-free -m nemotron-3.5-lightning-free \ |
| 74 | + -z 'Compute 212000 plus 8 and reply with only the number.' |
| 75 | +``` |
| 76 | + |
| 77 | +For Hermes on Nous' own models, drop `--tls-hosts`: |
| 78 | +`inference-api.nousresearch.com` is in the default list. |
| 79 | +`portal.nousresearch.com` deliberately is not — that is where the API key is |
| 80 | +issued, and it stays tunnelled and unread. |
| 81 | + |
| 82 | +Then, with no network: |
| 83 | + |
| 84 | +```bash |
| 85 | +node packages/cli/dist/cli.js replay <run-id> # reproduce it exactly |
| 86 | +node packages/cli/dist/cli.js show <run-id> # the timeline |
| 87 | +node packages/cli/dist/cli.js replay <run-id> --from 3 --model claude-sonnet-5 |
| 88 | +``` |
| 89 | + |
| 90 | +`-z` is the non-interactive flag. Without it Hermes opens a session and waits, |
| 91 | +and the recording ends when you do. |
| 92 | + |
| 93 | +## Prompts |
| 94 | + |
| 95 | +The prompt used for the verified run — arithmetic on purpose, so the answer is |
| 96 | +checkable and the run is short: |
| 97 | + |
| 98 | +```text |
| 99 | +Compute 212000 plus 8 and reply with only the number. |
| 100 | +``` |
| 101 | + |
| 102 | +Hermes answered `212008`. The point of the example is not the arithmetic: it |
| 103 | +is that the recording holds the 7,742-character system prompt and 19 tool |
| 104 | +schemas that the terminal never showed, and that `orca replay` returns the |
| 105 | +same bytes with the network off. |
| 106 | + |
| 107 | +A second run, to see the same question answered by the recording rather than |
| 108 | +the provider: |
| 109 | + |
| 110 | +```bash |
| 111 | +node packages/cli/dist/cli.js replay <run-id> |
| 112 | +``` |
| 113 | + |
| 114 | +reported `reused=3/3`, exit 0. It also reported three `major` divergences, |
| 115 | +which is the honest result and worth stating: Hermes' system prompt carries |
| 116 | +the date and session identity, so the replayed request has an identical |
| 117 | +trailing message and a different prefix. Divergence reporting is doing its |
| 118 | +job — that is what a dynamic system prompt looks like on the wire. |
| 119 | + |
| 120 | +## Skills Needed |
| 121 | + |
| 122 | +None of Hermes'. No plugin, no skill, no gateway, and no change to |
| 123 | +`config.yaml` — the recorder launches `hermes` as a child process and reads |
| 124 | +the socket. |
| 125 | + |
| 126 | +- Node 20+ for the recorder. |
| 127 | +- The provider used above, `opencode-free`, is served anonymously, so the run |
| 128 | + needs no API key and no credential enters the recording. Any configured |
| 129 | + Hermes provider works; a credential is forwarded upstream and never written |
| 130 | + to the trace. |
| 131 | +- Verified on Windows against **Hermes Agent v0.21.0 (2026.8.31)**, installed |
| 132 | + from git. |
| 133 | + |
| 134 | +## Sources |
| 135 | + |
| 136 | +- [Hermes configuration reference](https://hermes-agent.nousresearch.com/docs/user-guide/configuration) |
| 137 | + — the official docs for the behaviour this whole use case turns on: "When |
| 138 | + `base_url` is set, Hermes ignores the provider and calls that endpoint |
| 139 | + directly... When only `provider` is set, Hermes uses that provider's built-in |
| 140 | + auth and base URL." The origin is a config key, not an environment variable, |
| 141 | + which is why capture happens at the transport instead of by redirection. |
| 142 | +- [OrcaReplay PR #33](https://github.com/Continuum-AI-Corp/OrcaReplay/pull/33) |
| 143 | + — the adapter, with the verification transcript: the recorded sizes, token |
| 144 | + counts, and the `orca replay` result quoted above. Also documents the |
| 145 | + `OPENAI_BASE_URL` finding, established against a listener with |
| 146 | + `model.base_url` pointed at a dead port. |
| 147 | +- [`packages/adapters/src/hermes.ts`](https://github.com/Continuum-AI-Corp/OrcaReplay/blob/54839339ff7ca00c844f47bb043d0c6a85adc9ad/packages/adapters/src/hermes.ts) |
| 148 | + and its recorded contract |
| 149 | + [`fixtures/harness/hermes.json`](https://github.com/Continuum-AI-Corp/OrcaReplay/blob/54839339ff7ca00c844f47bb043d0c6a85adc9ad/packages/adapters/fixtures/harness/hermes.json), |
| 150 | + which carries the date the adapter was last confirmed against a running |
| 151 | + Hermes. |
| 152 | +- [`prompt/HERMES/`](https://github.com/Continuum-AI-Corp/OrcaReplay/tree/54839339ff7ca00c844f47bb043d0c6a85adc9ad/prompt/HERMES) |
| 153 | + — the captured system prompt as recorded, scrubbed of local paths. |
| 154 | +- `agent/ssl_verify.py` in Hermes itself, for the CA resolution order quoted |
| 155 | + above — the reason the standard `SSL_CERT_FILE` is enough and no |
| 156 | + Hermes-specific variable is needed. |
0 commit comments