Skip to content

docs(runtime): define runner client backend gate#2972

Merged
1 commit merged into
nesquena:masterfrom
Michaelyklam:docs/issue-1925-runner-client-gate
May 26, 2026
Merged

docs(runtime): define runner client backend gate#2972
1 commit merged into
nesquena:masterfrom
Michaelyklam:docs/issue-1925-runner-client-gate

Conversation

@Michaelyklam

Copy link
Copy Markdown
Contributor

Thinking Path

  • RFC: Make WebUI a thin observability/control client over Hermes Agent runtime #1925 has already shipped the journal/replay baseline, RuntimeAdapter seam, control routing, RunnerRuntimeAdapter facade, runner-local selection, and the default-off /api/chat/start route-selection harness.
  • PR feat(runtime): wire runner route selection harness #2794 shipped in v0.51.129, so runner-local now fails closed with a bounded not-configured response rather than silently falling back to WebUI-owned execution.
  • The next risky step is replacing that bounded 501 with a supervised/local runner client backend.
  • Before code lands, the RFC should pin the gate: how the runner owns execution/lifecycle, how restart/reattach proves ownership moved, which controls are required, and which WebUI-owned runtime-surrogate state remains prohibited.

What Changed

  • Marked Slice 4e as shipped in the runtime-adapter RFC.
  • Added a new Slice 4f gate for the supervised local runner client backend.
  • Scoped Slice 4f around explicit runner configuration, durable runner-owned run/session lookup, journal/cursor replay, cancel as the first required live control, stable browser response contracts, and bounded unsupported-control behavior.
  • Added a source-level regression test that keeps the Slice 4f gate text present in docs/rfcs/hermes-run-adapter-contract.md.
  • Added an Unreleased changelog entry.

Why It Matters

This keeps the #1925 ladder moving without jumping straight from route-selection plumbing to a runner implementation. The key guardrail stays intact: WebUI should be thin in execution ownership, not thin in product scope, and the runner boundary should not recreate STREAMS, CANCEL_FLAGS, cached AIAgent, approval/clarify callback queues, or active-run discovery caches under new names.

Refs #1925.

Verification

  • env -u HERMES_CONFIG_PATH -u HERMES_WEBUI_HOST /home/michael/.hermes/hermes-agent/venv/bin/python -m pytest tests/test_runtime_adapter_seam.py -q30 passed
  • env -u HERMES_CONFIG_PATH -u HERMES_WEBUI_HOST /home/michael/.hermes/hermes-agent/venv/bin/python -m pytest tests/ -q6587 passed, 6 skipped, 3 xpassed, 8 subtests passed
  • git diff --check → clean

Risks / Follow-ups

  • Docs/test-only; no runtime behavior changes.
  • This does not add the supervised runner client/backend yet, does not enable runner mode by default, and does not claim production execution survives WebUI restart.
  • If accepted, the next implementation slice can replace the current runner-local bounded 501 path only when a supervised runner client is explicitly configured.

Model Used

OpenAI Codex / GPT-5.5 via Hermes Agent, with terminal/file tools and repository tests.

@Michaelyklam Michaelyklam force-pushed the docs/issue-1925-runner-client-gate branch from a679d5c to 1b05d60 Compare May 26, 2026 11:06
@nesquena-hermes

Copy link
Copy Markdown
Collaborator

Summary

Docs-only RFC advance for the #1925 RuntimeAdapter ladder. Marks Slice 4e as shipped via #2794 (v0.51.129), adds a new Slice 4f gate that constrains what a future supervised local runner client backend may and may not do, and pins both the status update and the new gate text via static regression assertions in tests/test_runtime_adapter_seam.py. No runtime code touched.

Code reference

Slice 4e status banner sits where the slice header already lives (docs/rfcs/hermes-run-adapter-contract.md:905-914 in the PR HEAD):

Status as of 2026-05-24: shipped in v0.51.129 via #2794. The route-selection
harness now makes adapter mode selection explicit: `legacy-direct` remains the
default, `legacy-journal` still delegates to the existing journaled legacy path,
and `runner-local` returns a bounded not-configured response instead of silently
starting an in-process legacy run.

The new gate (Slice 4f, lines 957-1015 in the PR HEAD) restates the five acceptance tests and six scope items. The most consequential one is the no-globals guardrail:

3. **No runtime-surrogate globals.** The main WebUI server does not gain new
   module-level maps for runner-owned streams, cancel flags, approval/clarify
   callbacks, cached agents, goal state, queue schedulers, or child-process run
   registries. Supervision state belongs to the runner client/backend boundary.

And the pin test at tests/test_runtime_adapter_seam.py:571-585 mirrors the ten substring assertions you'd want to catch silent dilution — header, "501 path replaced only when configured", "Restart/reattach proves ownership moved", the chat-start whitelist phrasing, the unsupported-control safe-result phrasing, etc.

Diagnosis / Recommendation

The doc evolution is consistent with the chain of #2416#2424#2599#2744#2794 already recorded at docs/rfcs/hermes-run-adapter-contract.md:76-111. The Slice 4f scope is the right shape: it names the things a runner client must own (durable run id, session-to-run lookup, ordered journal replay, cancel as first live control) and the things it must not import into the main WebUI process (STREAMS, CANCEL_FLAGS, cached AIAgent, approval/clarify callback queues, active-run caches).

One observation, not a blocker: this lands while #2959 (the contract-routing review-gate issue) is still open. #2959 is asking for PRs that change contract semantics to declare a Contract Routing / Contract Change block in the PR body. By that framing, this is a contract-routing PR for the runtime-adapter-contract family — not a Contract Change (4e/4f acceptance tests are net-new, not inverted). The PR body's "Thinking Path / What Changed / Why It Matters" structure already covers the same ground, so this is just a labeling note for when #2959 lands.

The static regression test approach (substring assertions over the RFC body) is the same approach already used for 4c/4d/4e, so consistency wins out over fragility concerns. If a reviewer later wants to reword the scope bullets, they'll have to update the test in the same PR, which is the intended forcing function.

Two minor doc nits worth a pass:

  • "Status as of 2026-05-24" is fine, but PR feat(runtime): wire runner route selection harness #2794 was tagged into v0.51.129 — if you want the date and the tag to agree, docs/rfcs/hermes-run-adapter-contract.md could either drop the date or link to the v0.51.129 CHANGELOG entry (CHANGELOG.md:152).
  • The new Slice 4f bullet "no permanent WebUI-owned active-run discovery cache that duplicates runner or future Hermes Runtime API responsibility" implicitly invokes the hermes-agent-side Runtime API contract. If that contract has a canonical reference (e.g. an agent-side RFC or ~/.hermes/hermes-agent/... doc), linking to it here would prevent two future ladder slices from rediscovering the same boundary in different words.

Test plan

pytest tests/test_runtime_adapter_seam.py -q per the PR body — the new test_rfc_defines_slice4f_supervised_local_runner_client_gate plus the augmented 4e assertion both pass against the new RFC text. No production code paths exercised, so no smoke/UI verification needed. LGTM.

@nesquena-hermes nesquena-hermes closed this pull request by merging all changes into nesquena:master in a6c65de May 26, 2026
@nesquena-hermes

Copy link
Copy Markdown
Collaborator

Merged in Release DJ / v0.51.138 (stage-batch20 — 7-PR ultra-safe batch with PRs #2948 #2949 #2950 #2960 #2972 #2975 #2982).

Thanks @Michaelyklam! 🚢

franksong2702 pushed a commit to franksong2702/hermes-webui-fork that referenced this pull request May 27, 2026
# Conflicts:
#	CHANGELOG.md
eleboucher pushed a commit to eleboucher/homelab that referenced this pull request May 27, 2026
…➔ 0.51.145) (#668)

This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [ghcr.io/nesquena/hermes-webui](https://github.com/nesquena/hermes-webui) | patch | `0.51.134` → `0.51.145` |

---

> ⚠️ **Warning**
>
> Some dependencies could not be looked up. Check the [Dependency Dashboard](issues/567) for more information.

---

### Release Notes

<details>
<summary>nesquena/hermes-webui (ghcr.io/nesquena/hermes-webui)</summary>

### [`v0.51.145`](https://github.com/nesquena/hermes-webui/blob/HEAD/CHANGELOG.md#v051145--2026-05-26--Release-DQ-stage-batch27--sidebar-running-state-preservation)

[Compare Source](nesquena/hermes-webui@v0.51.144...v0.51.145)

##### Fixed

- Sidebar session rows now preserve the server-reported running state when merging stale optimistic first-turn cache entries, so active background sessions keep their spinner and can later transition to unread correctly. ([#&#8203;2999](nesquena/hermes-webui#2999), [#&#8203;3001](nesquena/hermes-webui#3001))

### [`v0.51.144`](https://github.com/nesquena/hermes-webui/blob/HEAD/CHANGELOG.md#v051144--2026-05-26--Release-DP-stage-batch26--single-PR-terminal-supervisor-hardening)

[Compare Source](nesquena/hermes-webui@v0.51.143...v0.51.144)

##### Fixed

- Embedded workspace terminals now use a dedicated supervisor thread for shell spawning to eliminate timeout-vs-spawn race conditions. If a `start_terminal()` call times out (5s) but the supervisor's `subprocess.Popen` completes later, the late-arriving process is now reaped via `_reap_abandoned_spawn()` (SIGHUP → wait → SIGKILL escalation) instead of being orphaned. Adds 8 Linux-only concurrency regression tests covering concurrent spawns, Popen-failure recovery, repeated-failure-survival, and the timeout-race late-commit path. ([#&#8203;2880](nesquena/hermes-webui#2880))

### [`v0.51.143`](https://github.com/nesquena/hermes-webui/blob/HEAD/CHANGELOG.md#v051143--2026-05-26--Release-DO-stage-batch25--single-PR-workspace-markdown-scheme)

[Compare Source](nesquena/hermes-webui@v0.51.142...v0.51.143)

##### Added

- Chat markdown links using `workspace://path/to/file` now open the target in the workspace preview pane instead of navigating away from the WebUI. Renderable via both the settled `renderMd()` and live `streaming-markdown` paths. Workspace path existence is verified via `/api/list` before opening; missing files surface a `file_open_failed` status toast. ([#&#8203;2881](nesquena/hermes-webui#2881), [#&#8203;2938](nesquena/hermes-webui#2938))

### [`v0.51.142`](https://github.com/nesquena/hermes-webui/blob/HEAD/CHANGELOG.md#v051142--2026-05-26--Release-DN-stage-batch24--4-PR-fresh-today-batch)

[Compare Source](nesquena/hermes-webui@v0.51.141...v0.51.142)

##### Added

- New `GET /api/crons/delivery-options` endpoint surfaces the agent's full delivery-platform registry. The cron-create UI now reads delivery options dynamically (including telegram, discord, slack, feishu, wecom, signal, etc.) instead of a 4-option hardcoded select, and the deliver field is editable for existing jobs. ([#&#8203;2996](nesquena/hermes-webui#2996))

##### Fixed

- Browser chat fallback now merges `fallback_providers` with the legacy `fallback_model` in Hermes CLI/gateway order, so WebUI can continue to a later non-Codex fallback when the primary Codex provider path fails. Duplicate provider/model/base\_url routes are de-duplicated. ([#&#8203;2993](nesquena/hermes-webui#2993))
- WebUI streaming sessions created under a non-default profile now attach `session_search` to that profile's `state.db` instead of the server's default profile database. ([#&#8203;2965](nesquena/hermes-webui#2965), [#&#8203;2995](nesquena/hermes-webui#2995))
- `client_secret` deny-list entry in the models-cache auth.json fingerprint now carries an inline rationale comment, and the `_write_auth` test helper docstring matches its actual contract. Follow-up to [#&#8203;2964](nesquena/hermes-webui#2964) review nits. ([#&#8203;2994](nesquena/hermes-webui#2994))

### [`v0.51.141`](https://github.com/nesquena/hermes-webui/blob/HEAD/CHANGELOG.md#v051141--2026-05-26--Release-DM-stage-batch23--4-PR-second-hold-bucket-pass)

[Compare Source](nesquena/hermes-webui@v0.51.140...v0.51.141)

##### Added

- WebUI can now opt into a `webui_prefill_messages_script` / `HERMES_WEBUI_PREFILL_MESSAGES_SCRIPT` hook for dynamic browser-turn prefill context from local notes or recall systems. The script output is capped at 256 KiB, normalized to ephemeral prefill messages, and browser status still hides message bodies while redacting script errors.
- Added a read-only WebUI/CLI session source switch in the chat sidebar when agent session sync is enabled. WebUI conversations stay in the default list, while imported CLI/agent sessions are surfaced under a separate `CLI sessions` tab with counts so large CLI histories do not clutter the normal conversation list. (Refs [#&#8203;2351](nesquena/hermes-webui#2351))

##### Fixed

- Compact tool activity now keeps visible interim assistant progress in the live Session timeline instead of making that progress effectively collapsed-only inside Activity details. The interim assistant stream path creates and flushes a visible assistant segment before resetting for later tool/compression activity.

### [`v0.51.140`](https://github.com/nesquena/hermes-webui/blob/HEAD/CHANGELOG.md#v051140--2026-05-26--Release-DL-stage-batch22--5-PR-hold-bucket-reassessment)

[Compare Source](nesquena/hermes-webui@v0.51.139...v0.51.140)

##### Fixed

- Live streamed assistant Markdown now treats underscores in ordinary text and identifiers literally, matching the settled message renderer while preserving asterisk-based emphasis.
- Models cache no longer churns every \~14 minutes when the auth.json credential pool refreshes. The auth.json fingerprint now hashes provider-identity fields (active\_provider, credential\_pool ids, base\_url, auth\_type) and excludes pure credential-rotation fields (access\_token, refresh\_token, expires\_at, last\_status, request\_count, updated\_at). Real provider/model-set changes still invalidate the cache. (RCA t\_16551f61)
- Paginated `/api/session?msg_limit=N` windows now anchor on the newest renderable transcript row instead of the raw message-array tail. Long sessions whose newest rows are only hidden tool-result entries no longer open to an empty visible transcript with non-empty `S.messages`.
- `_loadOlderMessages()` now requests a larger cumulative tail window (`msg_limit=current+30`) rather than a separate `msg_before` page. Suffix-continuity check guards against races and falls back to the legacy index-page request when the loaded transcript is no longer the suffix of the new server response.

##### Performance

- `renderMessages()` now caches `renderMd()` / `_renderUserFencedBlocks()` output keyed on message text, caches the visible-message scan across renders, scopes the question→assistant lookup to the visible window, and Prism-highlights only newly-mounted code blocks. Long sessions with many messages no longer freeze the browser tab on every render.

### [`v0.51.139`](https://github.com/nesquena/hermes-webui/blob/HEAD/CHANGELOG.md#v051139--2026-05-25--Release-DK-stage-batch21--5-PR-tier-2-batch)

[Compare Source](nesquena/hermes-webui@v0.51.138...v0.51.139)

##### Added

- Image lightbox now supports prev/next navigation when multiple images are present in the same message. Click `‹` / `›` buttons or use `←` / `→` keyboard arrows to browse; an image counter (`1 / 5`) is shown at the bottom. ([#&#8203;2967](nesquena/hermes-webui#2967))

##### Fixed

- Session switching now keeps the initial metadata fetch on `/api/session?messages=0&resolve_model=0` and defers stale model/provider repair until after the new session is assigned, so first paint does not block on cold model catalog hydration.
- Metadata-only session loads now use a cheap state summary instead of full transcript reconciliation, while still detecting real external state.db growth and ignoring restamped replay rows that would otherwise retrigger refresh polling.
- Session transcript reconciliation now precomputes visible-duplicate lookup state instead of recomputing loose-content normalization for every state.db row, reducing long-session tail-load latency without changing the append-only merge contract.
- Vendored KaTeX CSS, JavaScript, and fonts locally so math rendering no longer triggers CSP font reports for `cdn.jsdelivr.net` font files.
- Vendored `js-yaml` locally so YAML tree-view loading no longer triggers CSP script reports for `cdnjs.cloudflare.com`.
- Session delete now prunes the deleted row from `_index.json` in place instead of discarding the whole index, and composer draft saves skip the sidebar index entirely. Both reduce churn on new-session sends after a delete or while typing.

##### Changed

- Sidebar chat search now highlights matching title text and shows a subtle content preview for body-only matches.

### [`v0.51.138`](https://github.com/nesquena/hermes-webui/blob/HEAD/CHANGELOG.md#v051138--2026-05-25--Release-DJ-stage-batch20--7-PR-ultra-safe-batch)

[Compare Source](nesquena/hermes-webui@v0.51.137...v0.51.138)

##### Added

- **PR [#&#8203;2972](nesquena/hermes-webui#2972 by [@&#8203;Michaelyklam](https://github.com/Michaelyklam) (refs [#&#8203;1925](nesquena/hermes-webui#1925)) — Advance the runtime-adapter RFC after the Slice 4e route-selection harness shipped in v0.51.129. The RFC now defines the Slice 4f supervised local runner client backend gate: replace the bounded `runner-local` 501 only when explicitly configured, prove restart/reattach from durable runner/journal state, preserve the public chat-start field whitelist, require cancel as the first live runner-owned control, and keep active-run discovery/supervision out of new WebUI process-local runtime-surrogate globals.

##### Changed

- Contributor guidance now requires explicit `Contract Routing` for contract-affecting PRs and `Contract Change` when a PR intentionally changes an existing product, runtime, or review contract. Contract tests must move with the corresponding docs instead of silently redefining behavior by themselves, with the current static coverage documented as advisory rather than a GitHub policy gate.
- Removing a provider key now surfaces the server's specific CSRF rejection reason ("Session expired - reload the page", "Cross-origin mismatch - check reverse proxy headers", or the fallback "Cross-origin request rejected") when the underlying POST is rejected with 403, instead of swallowing all three into one generic toast. (Refs [#&#8203;2572](nesquena/hermes-webui#2572))
- Tool cards in the transcript now use a slightly stronger border and a 2px left edge so tool output stays visually distinct from final assistant prose without requiring hover. ([#&#8203;2867](nesquena/hermes-webui#2867))
- Tasks panel "Gateway not configured" banner now includes a direct link to the new `docs/docker.md#scheduled-jobs-and-the-gateway-daemon` section that walks through running the gateway container so scheduled cron jobs actually tick. (Refs [#&#8203;2785](nesquena/hermes-webui#2785))
- WebUI structured request logs now include `remote` (client IP) and an optional `forwarded_for` field when an `X-Forwarded-For` header is present, making failed-login and unauthorized-access logs usable by downstream security tooling like fail2ban behind a reverse proxy.

##### Internal

- Test cleanup in `tests/test_issue1894_provider_overlap.py`: canonicalize the `opencode-go` base URL to `opencode.ai/zen/go/v1` (matching `hermes_cli/auth.py` and `api/config.py`), drop a vestigial `# noqa: N801`, and convert section banner comments to per-test docstrings.

### [`v0.51.137`](https://github.com/nesquena/hermes-webui/blob/HEAD/CHANGELOG.md#v051137--2026-05-25--Release-DI-stage-batch19--6-PR-medium-risk-batch)

[Compare Source](nesquena/hermes-webui@v0.51.136...v0.51.137)

##### Added

- Operators can now set `HERMES_WEBUI_CSP_CONNECT_EXTRA` to append validated extra origins to the report-only CSP `connect-src` directive for reverse-proxy or tunnel deployments.

##### Fixed

- Trim session-level tool call payloads to the returned message window for paginated `/api/session` loads, so long tool-heavy sessions do not send historical tool call summaries during ordinary session switching.
- Sidebar compression lineage collapse now prefers the current continuation tip over a preserved parent snapshot when both rows share the same backend segment count. This keeps reloads after context compression from reopening the older parent transcript and making the active conversation appear to disappear.
- Reloading a stale `/session/<parent>` compression URL now resolves to the visible continuation tip from the sidebar payload instead of reopening the archived parent snapshot.
- Undo, retry, and explicit session truncation now persist a sidecar truncation watermark, preventing older `state.db` rows from reappearing after the WebUI transcript was intentionally shortened.
- Chat uploads with the same filename in one session now keep distinct attachment files instead of overwriting the earlier upload.
- Compression reference card no longer disappears behind the "Load earlier messages" cutoff after subsequent turns. The post-compression anchor is now calculated from the position of the last `[CONTEXT COMPACTION]` marker in the transcript instead of pointing at the visible tail, so the anchor stays at the compression boundary regardless of how many turns have been added since.

### [`v0.51.136`](https://github.com/nesquena/hermes-webui/blob/HEAD/CHANGELOG.md#v051136--2026-05-25--Release-DH-stage-batch18--5-PR-streaming--session-index-batch)

[Compare Source](nesquena/hermes-webui@v0.51.135...v0.51.136)

##### Fixed

- When the session index is missing, WebUI now starts a background rebuild while preserving the first sidebar full-scan result, so the index is primed for later requests without temporarily hiding existing sessions.
- Live token-usage hints now cap the cumulative in-flight tool-result prompt estimate per assistant turn, preventing many large tool callbacks from temporarily inflating the context ring before exact provider accounting arrives.
- Streaming checkpoint saves now run under the session's profile environment, so periodic background checkpoints no longer risk falling back to the process-global profile when a WebUI tab is using a non-default profile.
- Chat streaming now keeps a single live EventSource registered per session/stream, preventing reconnect or context-compaction paths from stacking subscribers and rendering one assistant token stream multiple times.
- Streaming visible-progress text emitted simultaneously through token, reasoning, and interim-assistant callbacks now renders once in the chat transcript instead of duplicating inside Thinking cards.

### [`v0.51.135`](https://github.com/nesquena/hermes-webui/blob/HEAD/CHANGELOG.md#v051135--2026-05-25--Release-DG-stage-batch17--9-PR-small-fix-batch)

[Compare Source](nesquena/hermes-webui@v0.51.134...v0.51.135)

##### Added

- Added a proposed canonical session resolution RFC covering URL routes, query parameters, localStorage, sidebar rows, and compression-lineage IDs so future session-routing fixes have one review contract.

##### Fixed

- Browser session links that use the API-style `?session_id=<id>` query parameter now open the requested conversation instead of falling back to the last locally stored session.
- Gateway status now treats existing messaging-session metadata as configured when `gateway.status` is unavailable, avoiding a misleading "Gateway not configured" warning for multi-container deployments with active gateway sessions.
- Session sidebar Archive/Delete menu actions now repaint from local sidebar state immediately after the server confirms the mutation, instead of waiting for the full `/api/sessions` refresh before the row disappears.
- Clarification dialogs now reserve transcript space while open or collapsed, so the question prompt no longer covers the assistant text needed to answer it.
- Chat uploads now send the absolute server-side path for image attachments in the agent text context, restoring immediate tool access (e.g. `vision_analyze`) to files uploaded in the current turn.
- Pending uploaded-file user turns no longer double-render when both the optimistic bubble and the server's pending-message hydration produce the same `[Attached files: ...]` suffix.

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about these updates again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xMDEuMSIsInVwZGF0ZWRJblZlciI6IjQzLjEwMS4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJyZW5vdmF0ZS9jb250YWluZXIiLCJ0eXBlL3BhdGNoIl19-->

Reviewed-on: https://git.erwanleboucher.dev/eleboucher/homelab/pulls/668
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants