@bitseek/hermes-webui 0.1.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +213 -0
- package/bin/hermes-webui.mjs +588 -0
- package/package.json +25 -0
- package/scripts/sync-vendor.mjs +74 -0
- package/templates/launchd/com.bitseek.hermes-webui.plist +21 -0
- package/templates/systemd/hermes-webui.service +13 -0
- package/templates/windows/hermes-webui-task.ps1 +3 -0
- package/vendor/agent-frontend-shell/.bitseek-source.json +6 -0
- package/vendor/agent-frontend-shell/.dockerignore +7 -0
- package/vendor/agent-frontend-shell/.env.docker.example +89 -0
- package/vendor/agent-frontend-shell/.env.example +34 -0
- package/vendor/agent-frontend-shell/.github/FUNDING.yml +3 -0
- package/vendor/agent-frontend-shell/.github/workflows/browser-smoke.yml +42 -0
- package/vendor/agent-frontend-shell/.github/workflows/docker-smoke.yml +233 -0
- package/vendor/agent-frontend-shell/.github/workflows/native-windows-startup.yml +132 -0
- package/vendor/agent-frontend-shell/.github/workflows/release.yml +57 -0
- package/vendor/agent-frontend-shell/.github/workflows/tests.yml +88 -0
- package/vendor/agent-frontend-shell/.vscode/launch.json +59 -0
- package/vendor/agent-frontend-shell/.vscode/settings.json +13 -0
- package/vendor/agent-frontend-shell/AGENTS.md +80 -0
- package/vendor/agent-frontend-shell/ARCHITECTURE.md +1658 -0
- package/vendor/agent-frontend-shell/BUGS.md +52 -0
- package/vendor/agent-frontend-shell/CHANGELOG.md +7295 -0
- package/vendor/agent-frontend-shell/CONTRIBUTING.md +205 -0
- package/vendor/agent-frontend-shell/CONTRIBUTORS.md +107 -0
- package/vendor/agent-frontend-shell/DESIGN.md +173 -0
- package/vendor/agent-frontend-shell/Dockerfile +91 -0
- package/vendor/agent-frontend-shell/LICENSE +21 -0
- package/vendor/agent-frontend-shell/README-CUSTOM.md +76 -0
- package/vendor/agent-frontend-shell/README.md +705 -0
- package/vendor/agent-frontend-shell/ROADMAP.md +351 -0
- package/vendor/agent-frontend-shell/SPRINTS.md +147 -0
- package/vendor/agent-frontend-shell/TESTING.md +1932 -0
- package/vendor/agent-frontend-shell/THEMES.md +170 -0
- package/vendor/agent-frontend-shell/api/__init__.py +1 -0
- package/vendor/agent-frontend-shell/api/agent_health.py +392 -0
- package/vendor/agent-frontend-shell/api/agent_sessions.py +782 -0
- package/vendor/agent-frontend-shell/api/auth.py +592 -0
- package/vendor/agent-frontend-shell/api/background.py +87 -0
- package/vendor/agent-frontend-shell/api/clarify.py +238 -0
- package/vendor/agent-frontend-shell/api/commands.py +124 -0
- package/vendor/agent-frontend-shell/api/compression_anchor.py +134 -0
- package/vendor/agent-frontend-shell/api/config.py +5178 -0
- package/vendor/agent-frontend-shell/api/dashboard_probe.py +255 -0
- package/vendor/agent-frontend-shell/api/extensions.py +253 -0
- package/vendor/agent-frontend-shell/api/gateway_chat.py +435 -0
- package/vendor/agent-frontend-shell/api/gateway_watcher.py +230 -0
- package/vendor/agent-frontend-shell/api/goals.py +608 -0
- package/vendor/agent-frontend-shell/api/helpers.py +474 -0
- package/vendor/agent-frontend-shell/api/kanban_bridge.py +1255 -0
- package/vendor/agent-frontend-shell/api/metering.py +194 -0
- package/vendor/agent-frontend-shell/api/models.py +4210 -0
- package/vendor/agent-frontend-shell/api/oauth.py +770 -0
- package/vendor/agent-frontend-shell/api/onboarding.py +1046 -0
- package/vendor/agent-frontend-shell/api/passkeys.py +365 -0
- package/vendor/agent-frontend-shell/api/profiles.py +1499 -0
- package/vendor/agent-frontend-shell/api/providers.py +2175 -0
- package/vendor/agent-frontend-shell/api/request_diagnostics.py +160 -0
- package/vendor/agent-frontend-shell/api/rollback.py +320 -0
- package/vendor/agent-frontend-shell/api/routes.py +13990 -0
- package/vendor/agent-frontend-shell/api/run_journal.py +284 -0
- package/vendor/agent-frontend-shell/api/runner_client.py +156 -0
- package/vendor/agent-frontend-shell/api/runtime_adapter.py +431 -0
- package/vendor/agent-frontend-shell/api/session_discoverability.py +640 -0
- package/vendor/agent-frontend-shell/api/session_events.py +45 -0
- package/vendor/agent-frontend-shell/api/session_lifecycle.py +208 -0
- package/vendor/agent-frontend-shell/api/session_ops.py +207 -0
- package/vendor/agent-frontend-shell/api/session_recovery.py +655 -0
- package/vendor/agent-frontend-shell/api/skill_usage.py +32 -0
- package/vendor/agent-frontend-shell/api/startup.py +128 -0
- package/vendor/agent-frontend-shell/api/state_sync.py +187 -0
- package/vendor/agent-frontend-shell/api/streaming.py +7048 -0
- package/vendor/agent-frontend-shell/api/system_health.py +167 -0
- package/vendor/agent-frontend-shell/api/terminal.py +410 -0
- package/vendor/agent-frontend-shell/api/turn_journal.py +214 -0
- package/vendor/agent-frontend-shell/api/updates.py +1261 -0
- package/vendor/agent-frontend-shell/api/upload.py +322 -0
- package/vendor/agent-frontend-shell/api/usage.py +26 -0
- package/vendor/agent-frontend-shell/api/workspace.py +867 -0
- package/vendor/agent-frontend-shell/api/workspace_git.py +1261 -0
- package/vendor/agent-frontend-shell/api/worktrees.py +357 -0
- package/vendor/agent-frontend-shell/bootstrap.py +492 -0
- package/vendor/agent-frontend-shell/ctl.sh +427 -0
- package/vendor/agent-frontend-shell/docker-compose.custom.yml +26 -0
- package/vendor/agent-frontend-shell/docker-compose.three-container.yml +168 -0
- package/vendor/agent-frontend-shell/docker-compose.two-container.yml +147 -0
- package/vendor/agent-frontend-shell/docker-compose.yml +57 -0
- package/vendor/agent-frontend-shell/docker_init.bash +459 -0
- package/vendor/agent-frontend-shell/docs/CONTRACTS.md +207 -0
- package/vendor/agent-frontend-shell/docs/EXTENSIONS.md +212 -0
- package/vendor/agent-frontend-shell/docs/ISSUES.md +23 -0
- package/vendor/agent-frontend-shell/docs/UIUX-GUIDE.md +196 -0
- package/vendor/agent-frontend-shell/docs/advanced-chat-setup.md +83 -0
- package/vendor/agent-frontend-shell/docs/docker.md +337 -0
- package/vendor/agent-frontend-shell/docs/onboarding-agent-checklist.md +207 -0
- package/vendor/agent-frontend-shell/docs/onboarding.md +202 -0
- package/vendor/agent-frontend-shell/docs/remote-access.md +75 -0
- package/vendor/agent-frontend-shell/docs/rfcs/README.md +53 -0
- package/vendor/agent-frontend-shell/docs/rfcs/agent-source-boundary.md +70 -0
- package/vendor/agent-frontend-shell/docs/rfcs/canonical-session-resolution.md +124 -0
- package/vendor/agent-frontend-shell/docs/rfcs/hermes-run-adapter-contract.md +1079 -0
- package/vendor/agent-frontend-shell/docs/rfcs/turn-journal.md +195 -0
- package/vendor/agent-frontend-shell/docs/rfcs/webui-run-state-consistency-contract.md +157 -0
- package/vendor/agent-frontend-shell/docs/supervisor.md +280 -0
- package/vendor/agent-frontend-shell/docs/troubleshooting.md +132 -0
- package/vendor/agent-frontend-shell/docs/ui-ux/index.html +863 -0
- package/vendor/agent-frontend-shell/docs/ui-ux/two-stage-proposal.html +768 -0
- package/vendor/agent-frontend-shell/docs/why-hermes.md +489 -0
- package/vendor/agent-frontend-shell/docs/workspace-git.md +92 -0
- package/vendor/agent-frontend-shell/docs/wsl-autostart.md +126 -0
- package/vendor/agent-frontend-shell/eslint.runtime-guard.config.mjs +35 -0
- package/vendor/agent-frontend-shell/extensions/bitseek-design-system.md +330 -0
- package/vendor/agent-frontend-shell/extensions/branding/assets/apple-touch-icon.png +0 -0
- package/vendor/agent-frontend-shell/extensions/branding/assets/empty-logo.svg +739 -0
- package/vendor/agent-frontend-shell/extensions/branding/assets/favicon-192.png +0 -0
- package/vendor/agent-frontend-shell/extensions/branding/assets/favicon-32.png +0 -0
- package/vendor/agent-frontend-shell/extensions/branding/assets/favicon-512.png +0 -0
- package/vendor/agent-frontend-shell/extensions/branding/assets/favicon-512.svg +745 -0
- package/vendor/agent-frontend-shell/extensions/branding/assets/favicon.ico +0 -0
- package/vendor/agent-frontend-shell/extensions/branding/assets/favicon.svg +745 -0
- package/vendor/agent-frontend-shell/extensions/branding/assets/titlebar-icon-v2.svg +751 -0
- package/vendor/agent-frontend-shell/extensions/branding/assets/titlebar-icon-v3.svg +739 -0
- package/vendor/agent-frontend-shell/extensions/branding/assets/titlebar-icon.svg +745 -0
- package/vendor/agent-frontend-shell/extensions/branding/branding.js +112 -0
- package/vendor/agent-frontend-shell/extensions/branding/config.json +14 -0
- package/vendor/agent-frontend-shell/extensions/branding/manifest.json +53 -0
- package/vendor/agent-frontend-shell/extensions/index.js +67 -0
- package/vendor/agent-frontend-shell/extensions/loader/hermes-loader.js +77 -0
- package/vendor/agent-frontend-shell/extensions/manifest.json +16 -0
- package/vendor/agent-frontend-shell/extensions/pages/ai-teammates/page.css +333 -0
- package/vendor/agent-frontend-shell/extensions/pages/ai-teammates/page.js +487 -0
- package/vendor/agent-frontend-shell/extensions/pages/manifest.json +6 -0
- package/vendor/agent-frontend-shell/extensions/pages/registry.css +56 -0
- package/vendor/agent-frontend-shell/extensions/pages/registry.js +302 -0
- package/vendor/agent-frontend-shell/extensions/themes/bitseek/index.css +93 -0
- package/vendor/agent-frontend-shell/extensions/themes/bitseek/index.js +98 -0
- package/vendor/agent-frontend-shell/install.sh +63 -0
- package/vendor/agent-frontend-shell/mcp_server.py +567 -0
- package/vendor/agent-frontend-shell/package.json +12 -0
- package/vendor/agent-frontend-shell/pyproject.toml +56 -0
- package/vendor/agent-frontend-shell/pytest.ini +3 -0
- package/vendor/agent-frontend-shell/requirements.txt +5 -0
- package/vendor/agent-frontend-shell/server.py +624 -0
- package/vendor/agent-frontend-shell/start.ps1 +210 -0
- package/vendor/agent-frontend-shell/start.sh +65 -0
- package/vendor/agent-frontend-shell/static/apple-touch-icon.png +0 -0
- package/vendor/agent-frontend-shell/static/boot.js +1990 -0
- package/vendor/agent-frontend-shell/static/commands.js +1402 -0
- package/vendor/agent-frontend-shell/static/favicon-192.png +0 -0
- package/vendor/agent-frontend-shell/static/favicon-32.png +0 -0
- package/vendor/agent-frontend-shell/static/favicon-512.png +0 -0
- package/vendor/agent-frontend-shell/static/favicon-512.svg +18 -0
- package/vendor/agent-frontend-shell/static/favicon.ico +0 -0
- package/vendor/agent-frontend-shell/static/favicon.svg +20 -0
- package/vendor/agent-frontend-shell/static/i18n.js +15389 -0
- package/vendor/agent-frontend-shell/static/icons.js +92 -0
- package/vendor/agent-frontend-shell/static/index.html +1506 -0
- package/vendor/agent-frontend-shell/static/login.js +177 -0
- package/vendor/agent-frontend-shell/static/manifest.json +53 -0
- package/vendor/agent-frontend-shell/static/messages.js +3521 -0
- package/vendor/agent-frontend-shell/static/onboarding.js +800 -0
- package/vendor/agent-frontend-shell/static/panels.js +7995 -0
- package/vendor/agent-frontend-shell/static/pwa-startup.js +83 -0
- package/vendor/agent-frontend-shell/static/sessions.js +5165 -0
- package/vendor/agent-frontend-shell/static/style.css +4774 -0
- package/vendor/agent-frontend-shell/static/sw.js +173 -0
- package/vendor/agent-frontend-shell/static/terminal.js +632 -0
- package/vendor/agent-frontend-shell/static/ui.js +8997 -0
- package/vendor/agent-frontend-shell/static/vendor/js-yaml/4.1.0/js-yaml.min.js +2 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_AMS-Regular.ttf +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_AMS-Regular.woff +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_AMS-Regular.woff2 +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Caligraphic-Bold.ttf +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Caligraphic-Bold.woff +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Caligraphic-Regular.ttf +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Caligraphic-Regular.woff +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Fraktur-Bold.ttf +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Fraktur-Bold.woff +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Fraktur-Regular.ttf +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Fraktur-Regular.woff +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Main-Bold.ttf +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Main-Bold.woff +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Main-Bold.woff2 +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Main-BoldItalic.ttf +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Main-BoldItalic.woff +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Main-Italic.ttf +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Main-Italic.woff +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Main-Italic.woff2 +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Main-Regular.ttf +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Main-Regular.woff +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Main-Regular.woff2 +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Math-BoldItalic.ttf +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Math-BoldItalic.woff +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Math-Italic.ttf +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Math-Italic.woff +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Math-Italic.woff2 +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_SansSerif-Bold.ttf +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_SansSerif-Bold.woff +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_SansSerif-Italic.ttf +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_SansSerif-Italic.woff +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_SansSerif-Regular.ttf +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_SansSerif-Regular.woff +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Script-Regular.ttf +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Script-Regular.woff +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Script-Regular.woff2 +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Size1-Regular.ttf +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Size1-Regular.woff +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Size1-Regular.woff2 +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Size2-Regular.ttf +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Size2-Regular.woff +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Size2-Regular.woff2 +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Size3-Regular.ttf +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Size3-Regular.woff +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Size3-Regular.woff2 +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Size4-Regular.ttf +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Size4-Regular.woff +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Size4-Regular.woff2 +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Typewriter-Regular.ttf +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Typewriter-Regular.woff +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/katex.min.css +1 -0
- package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/katex.min.js +1 -0
- package/vendor/agent-frontend-shell/static/vendor/smd.min.js +29 -0
- package/vendor/agent-frontend-shell/static/workspace.js +680 -0
|
@@ -0,0 +1,1079 @@
|
|
|
1
|
+
# Hermes Run Adapter Contract and Migration Gates
|
|
2
|
+
|
|
3
|
+
- **Status:** Proposed
|
|
4
|
+
- **Author:** @Michaelyklam
|
|
5
|
+
- **Updated by:** @franksong2702
|
|
6
|
+
- **Created:** 2026-05-11
|
|
7
|
+
- **Revised:** 2026-05-23
|
|
8
|
+
- **Tracking issue:** [#1925](https://github.com/nesquena/hermes-webui/issues/1925)
|
|
9
|
+
|
|
10
|
+
## Credit and Scope
|
|
11
|
+
|
|
12
|
+
This RFC codifies the direction discussed in #1925. It does not introduce an
|
|
13
|
+
implementation. The central guardrail comes from Michael Lam's review framing:
|
|
14
|
+
|
|
15
|
+
> the adapter should be a protocol translator, not a runtime surrogate.
|
|
16
|
+
|
|
17
|
+
The product boundary from #1925 is:
|
|
18
|
+
|
|
19
|
+
> WebUI should be thin in execution ownership, not thin in product scope.
|
|
20
|
+
|
|
21
|
+
That means WebUI remains the full browser workbench for sessions, workspace
|
|
22
|
+
files, chat rendering, tool cards, approvals, status, diagnostics, and controls.
|
|
23
|
+
The change is that long-lived execution ownership should move behind an explicit
|
|
24
|
+
runtime boundary instead of remaining scattered through the main WebUI request
|
|
25
|
+
process.
|
|
26
|
+
|
|
27
|
+
This document is intentionally a reviewable spec and migration gate. It should be
|
|
28
|
+
accepted before any implementation PR for this adapter direction changes the
|
|
29
|
+
streaming hot path, introduces a runner process, or moves a new approval /
|
|
30
|
+
clarify / queue / goal control path. Narrow current-path bug fixes that do not
|
|
31
|
+
introduce a new runtime boundary can still proceed under the WebUI run-state
|
|
32
|
+
consistency contract and the relevant issue scope.
|
|
33
|
+
|
|
34
|
+
## Problem
|
|
35
|
+
|
|
36
|
+
Browser-originated chat turns are still executed inside the WebUI server process.
|
|
37
|
+
The current path creates process-local stream state, starts background agent
|
|
38
|
+
threads, constructs or reuses `AIAgent`, and owns callback state for token, tool,
|
|
39
|
+
reasoning, approval, clarify, cancellation, and terminal events.
|
|
40
|
+
|
|
41
|
+
That shape works, but it makes the WebUI process the owner of active runtime
|
|
42
|
+
truth. Consequences include:
|
|
43
|
+
|
|
44
|
+
- restarting WebUI can orphan active work,
|
|
45
|
+
- reconnect depends on process-local state rather than a durable run/event view,
|
|
46
|
+
- cancellation and stale writeback bugs recur around ownership boundaries,
|
|
47
|
+
- approvals and clarify prompts are tied to live callbacks,
|
|
48
|
+
- future Hermes runtime APIs cannot be adopted cleanly because WebUI lacks a
|
|
49
|
+
single adapter boundary.
|
|
50
|
+
|
|
51
|
+
The immediate goal is not to build a sidecar. The immediate goal is to define the
|
|
52
|
+
browser contract, classify current runtime state, and gate the first reversible
|
|
53
|
+
journal slice.
|
|
54
|
+
|
|
55
|
+
## Current Gate State — 2026-05-23
|
|
56
|
+
|
|
57
|
+
Slice 1 is now past the first active validation gate:
|
|
58
|
+
|
|
59
|
+
- #2283 shipped the run-journal replay layer in v0.51.71.
|
|
60
|
+
- A 100-trial synthetic replay/restart validation pass against current
|
|
61
|
+
`origin/master` passed on 2026-05-16. The matrix covered completed-run replay,
|
|
62
|
+
interrupted stale-pending recovery, fresh-pending grace handling, StreamChannel
|
|
63
|
+
reconnect ordering, duplicate-prevention merge behavior, many-session recovery,
|
|
64
|
+
large-journal derivation, and stream-to-turn-id lifecycle linking.
|
|
65
|
+
- The focused regression set
|
|
66
|
+
`tests/test_turn_journal.py tests/test_turn_journal_lifecycle.py tests/test_stale_stream_pending_recovery.py`
|
|
67
|
+
also passed on the same worktree.
|
|
68
|
+
- #2393, shipped through v0.51.76, capped live chat token SSE transports to the
|
|
69
|
+
selected conversation pane. Background sessions now rely on existing
|
|
70
|
+
status/replay/reattach behavior instead of keeping one live `/api/chat/stream`
|
|
71
|
+
EventSource per active session.
|
|
72
|
+
|
|
73
|
+
This evidence did not prove the future runner/sidecar path. It did unblock the
|
|
74
|
+
adapter-seam work:
|
|
75
|
+
|
|
76
|
+
- #2416 shipped the Slice 2 RuntimeAdapter seam contract.
|
|
77
|
+
- #2424 shipped the default-off `legacy-journal` RuntimeAdapter seam in
|
|
78
|
+
v0.51.81.
|
|
79
|
+
- #2438 shipped the response-shape parity follow-up in v0.51.83, keeping the
|
|
80
|
+
adapter flag from expanding `/api/chat/start`'s public JSON contract.
|
|
81
|
+
- #2469 shipped the Slice 3a cancel-control gate in v0.51.85.
|
|
82
|
+
- #2479 shipped the first Slice 3a implementation in v0.51.86, routing Stop
|
|
83
|
+
Generation through `RuntimeAdapter.cancel_run(...)` only when
|
|
84
|
+
`HERMES_WEBUI_RUNTIME_ADAPTER=legacy-journal` is enabled.
|
|
85
|
+
- #2487 shipped the Slice 3b approval/clarify gate, and #2496 shipped approval /
|
|
86
|
+
clarify response routing through the adapter seam in v0.51.89.
|
|
87
|
+
- #2509 shipped the Slice 3c queue/continue + goal gate in v0.51.90.
|
|
88
|
+
- #2544 shipped the first Slice 3c implementation in v0.51.91. The goal
|
|
89
|
+
route now uses `RuntimeAdapter.update_goal(...)` only when
|
|
90
|
+
`HERMES_WEBUI_RUNTIME_ADAPTER=legacy-journal` is enabled, while preserving the
|
|
91
|
+
legacy-direct response shape and leaving post-turn goal evaluation in the
|
|
92
|
+
existing agent loop.
|
|
93
|
+
- #2560 shipped the queue-staging clarification in v0.51.92. The RFC now treats
|
|
94
|
+
`queue_message(...)` as a staged protocol method only; `/queue` remains
|
|
95
|
+
browser-side queue/drain behavior, and no server-side queue endpoint or queue
|
|
96
|
+
scheduler should be added merely for adapter symmetry.
|
|
97
|
+
- #2575 shipped the Slice 4a runner/sidecar contract gate in v0.51.93.
|
|
98
|
+
- #2599 shipped the Slice 4b `RunnerRuntimeAdapter` facade in v0.51.94. The
|
|
99
|
+
facade normalizes an injected runner client's start / observe / status /
|
|
100
|
+
control responses without owning `AIAgent`, streams, cancellation flags,
|
|
101
|
+
approval queues, clarify queues, goal state, or cached-agent tables.
|
|
102
|
+
- #2627 shipped the Slice 4c runner-backend harness gate in v0.51.96.
|
|
103
|
+
- #2696 shipped the first Slice 4c implementation in v0.51.105: the default-off
|
|
104
|
+
`runner-local` adapter selection point and `build_runtime_adapter(...)`
|
|
105
|
+
factory wiring around an injected runner client. Live browser chat routes still
|
|
106
|
+
stay on the legacy backend, and no supervised runner process exists yet.
|
|
107
|
+
- #2744 shipped the Slice 4d supervised runner route gate in v0.51.108.
|
|
108
|
+
- The next implementation slice is a default-off runner route-selection harness
|
|
109
|
+
for `/api/chat/start`. It should only engage when `runner-local` is explicitly
|
|
110
|
+
selected, return a bounded not-configured error until a supervised runner
|
|
111
|
+
client exists, keep `legacy-direct` / `legacy-journal` fallback intact, pass
|
|
112
|
+
explicit profile/workspace/model payloads instead of mutating WebUI process
|
|
113
|
+
globals, and avoid recreating `STREAMS` / `CANCEL_FLAGS` / approval queues /
|
|
114
|
+
clarify queues under new names.
|
|
115
|
+
|
|
116
|
+
The next gate is runner-backend plumbing, not queue implementation
|
|
117
|
+
by default. Queue / continue routing should only move before Slice 4 if a future
|
|
118
|
+
maintainer decision identifies an existing server-side legacy entry point and
|
|
119
|
+
pins its response shape, ordering, and idempotency contract. Otherwise, keeping
|
|
120
|
+
`queue_message(...)` staged is the honest boundary while execution ownership
|
|
121
|
+
moves out of the main WebUI request process.
|
|
122
|
+
|
|
123
|
+
## Goals
|
|
124
|
+
|
|
125
|
+
- Preserve the current rich WebUI workbench experience.
|
|
126
|
+
- Make the browser-facing event/control contract explicit.
|
|
127
|
+
- Classify every current runtime-owned state primitive as `runner process`,
|
|
128
|
+
`journal`, `adapter API surface`, or `WebUI presentation cache`.
|
|
129
|
+
- Identify future backend mapping: existing Hermes runtime API, missing Hermes
|
|
130
|
+
API, or temporary WebUI compatibility shim.
|
|
131
|
+
- Define acceptance tests that must survive any migration.
|
|
132
|
+
- Define reversible implementation slices, starting with an append-only
|
|
133
|
+
in-process event journal / replay layer.
|
|
134
|
+
|
|
135
|
+
## Non-goals
|
|
136
|
+
|
|
137
|
+
- Do not implement the adapter in this RFC.
|
|
138
|
+
- Do not introduce a runner process or sidecar in the first implementation slice.
|
|
139
|
+
- Do not change `_run_agent_streaming` control flow in the first journal slice.
|
|
140
|
+
- Do not recreate `STREAMS`, cached `AIAgent` objects, callback queues, or
|
|
141
|
+
cancellation flags under new names.
|
|
142
|
+
- Do not reduce WebUI product scope or move normal workbench UX out of WebUI.
|
|
143
|
+
- Do not depend on Hermes Agent shipping a WebUI-specific runtime connector before
|
|
144
|
+
WebUI can improve its own boundary.
|
|
145
|
+
|
|
146
|
+
## Artifact 1: Browser Event and Control Contract
|
|
147
|
+
|
|
148
|
+
This is the compatibility contract the browser depends on, regardless of whether
|
|
149
|
+
the backend is today's in-process streaming path, an in-process journaled path, a
|
|
150
|
+
future WebUI-managed runner, or a future Hermes `/v1/runs` backend.
|
|
151
|
+
|
|
152
|
+
The current inventory should be derived from `static/messages.js` consumers and
|
|
153
|
+
SSE/event production in `api/streaming.py`. Future edits to those files should
|
|
154
|
+
update this RFC or the implementation contract that replaces it.
|
|
155
|
+
|
|
156
|
+
### Event Envelope
|
|
157
|
+
|
|
158
|
+
Every replayable runtime event should be representable with:
|
|
159
|
+
|
|
160
|
+
```json
|
|
161
|
+
{
|
|
162
|
+
"event_id": "run_123:42",
|
|
163
|
+
"seq": 42,
|
|
164
|
+
"run_id": "run_123",
|
|
165
|
+
"session_id": "20260514_...",
|
|
166
|
+
"type": "tool.updated",
|
|
167
|
+
"created_at": 1778750000.0,
|
|
168
|
+
"terminal": false,
|
|
169
|
+
"payload": {}
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
Required semantics:
|
|
174
|
+
|
|
175
|
+
- `seq` is monotonic per run.
|
|
176
|
+
- `event_id` is stable enough to use as an SSE `id:` value or equivalent cursor.
|
|
177
|
+
- Reconnect supports `Last-Event-ID` or `after_seq`.
|
|
178
|
+
- Replay is at-least-once; WebUI deduplicates by `run_id` + `seq` or `event_id`.
|
|
179
|
+
- Terminal runs can replay their final `done`, `cancelled`, or `error` state.
|
|
180
|
+
|
|
181
|
+
### Event Families
|
|
182
|
+
|
|
183
|
+
| Event family | Required payload | Browser responsibility | Runtime source of truth |
|
|
184
|
+
|---|---|---|---|
|
|
185
|
+
| `run.started` / `status` | lifecycle state, controls available, session id, workspace/profile/model/toolset summary | render active state and controls | runtime run state |
|
|
186
|
+
| `token.delta` | assistant message id or segment id, delta text, content type | append visible assistant text | runtime model output stream |
|
|
187
|
+
| `reasoning.delta` / `reasoning.done` | reasoning block id, delta/final text, visibility metadata | render thinking/progress UI | runtime reasoning events |
|
|
188
|
+
| `progress` | concise phase/status text, optional tool context | render activity/progress text | runtime progress callbacks |
|
|
189
|
+
| `tool.started` | tool call id, name, sanitized arguments, start time | open/update tool card | runtime tool lifecycle |
|
|
190
|
+
| `tool.updated` | stdout/stderr/structured partial data, progress metadata | update tool card | runtime tool lifecycle |
|
|
191
|
+
| `tool.done` | result, status/exit code, duration, error flag | finalize tool card | runtime tool lifecycle |
|
|
192
|
+
| `approval.requested` | approval id, action summary, risk metadata, available choices | show approval widget | runtime approval state |
|
|
193
|
+
| `approval.resolved` | approval id, choice, resulting status | close/update approval widget | runtime approval state |
|
|
194
|
+
| `clarify.requested` | clarify id, question, choices/input mode | show clarify widget | runtime clarify state |
|
|
195
|
+
| `clarify.resolved` | clarify id, answer metadata/status | close/update clarify widget | runtime clarify state |
|
|
196
|
+
| `title.updated` | title text, source/confidence | update title surfaces | session/title subsystem |
|
|
197
|
+
| `usage.updated` / `usage.final` | tokens, cost, model/provider, duration where available | update usage surfaces | runtime usage accounting |
|
|
198
|
+
| `error` | stable error code, safe message, redacted diagnostics, terminal flag | render error and final state | runtime terminal/error state |
|
|
199
|
+
| `done` | final lifecycle state, usage, terminal result/error summary, last seq | finalize run UI | runtime terminal state |
|
|
200
|
+
|
|
201
|
+
### Reconnect Metadata
|
|
202
|
+
|
|
203
|
+
Every active or terminal run must expose:
|
|
204
|
+
|
|
205
|
+
- `run_id`
|
|
206
|
+
- `session_id`
|
|
207
|
+
- `status`: `queued`, `running`, `awaiting_approval`, `awaiting_clarify`,
|
|
208
|
+
`paused`, `cancelling`, `cancelled`, `failed`, `completed`, or `expired`
|
|
209
|
+
- last committed event cursor / `last_event_id`
|
|
210
|
+
- terminal state and final result/error when finished
|
|
211
|
+
- currently available controls
|
|
212
|
+
- pending approval/clarify ids, if any
|
|
213
|
+
- session-to-active-run mapping for the current WebUI session
|
|
214
|
+
|
|
215
|
+
### Controls
|
|
216
|
+
|
|
217
|
+
| Control | Required semantics | Target owner |
|
|
218
|
+
|---|---|---|
|
|
219
|
+
| observe | attach to live events and replay from cursor | adapter API surface backed by runtime/journal |
|
|
220
|
+
| status | poll lifecycle state when SSE/WebSocket is unavailable | adapter API surface backed by runtime/journal |
|
|
221
|
+
| cancel | request graceful cancellation; terminal event follows | runner/runtime control plane |
|
|
222
|
+
| queue / continue | append follow-up work according to Hermes semantics | runner/runtime control plane |
|
|
223
|
+
| approval | resolve pending approval by id with supported choices | runner/runtime control plane |
|
|
224
|
+
| clarify | answer pending clarify request by id | runner/runtime control plane |
|
|
225
|
+
| goal | set/status/pause/resume/clear goal where capability exists | runtime command/capability plane |
|
|
226
|
+
|
|
227
|
+
WebUI may keep presentation state such as expanded rows, selected tabs, and local
|
|
228
|
+
scroll position. WebUI must not privately mutate runtime truth for these controls.
|
|
229
|
+
|
|
230
|
+
## Artifact 2: Runtime State Inventory and Classifier
|
|
231
|
+
|
|
232
|
+
Classifications:
|
|
233
|
+
|
|
234
|
+
- `runner process`: should be owned by the eventual execution runner / runtime
|
|
235
|
+
backend, not the main WebUI request process.
|
|
236
|
+
- `journal`: should be captured in append-only durable events for replay and
|
|
237
|
+
diagnostics.
|
|
238
|
+
- `adapter API surface`: should be exposed through a WebUI-owned boundary that
|
|
239
|
+
can later switch backend implementations.
|
|
240
|
+
- `WebUI presentation cache`: may remain local because it is not execution truth.
|
|
241
|
+
|
|
242
|
+
| Current primitive | Current legacy source of truth | Target classification | Future backend mapping | Slice 1 handling | Notes / gap |
|
|
243
|
+
|---|---|---|---|---|---|
|
|
244
|
+
| `STREAMS` / `STREAMS_LOCK` | `api.state_sync` process memory | adapter API surface + presentation fan-out | WebUI runner or future Hermes run observation API | keep live path; mirror events into journal | Must stop being authoritative for active run existence. |
|
|
245
|
+
| `CANCEL_FLAGS` | `api.state_sync` process memory | runner process | cancel/interrupt endpoint or runner control | no control-flow change | Final cancel state must return as a replayable event. |
|
|
246
|
+
| cached `AIAgent` objects / `AGENT_INSTANCES` | `api/config.py` process memory | runner process | runner-owned Hermes integration | unchanged | Moving this is deferred until after journal proof. |
|
|
247
|
+
| background thread lifecycle | `_run_agent_streaming` in `api/streaming.py` | runner process | runner-owned execution lifecycle | unchanged | Slice 1 must not rewrite thread/control flow. |
|
|
248
|
+
| token / partial text buffers | streaming callbacks and browser SSE state | journal + presentation cache | replayable runtime events | append emitted events | Browser can cache rendered state, but replay must rebuild it. |
|
|
249
|
+
| reasoning buffers | streaming callbacks and UI rendering state | journal + presentation cache | replayable reasoning events | append emitted events | Thinking cards must survive reconnect. |
|
|
250
|
+
| tool buffers / live tool calls | WebUI streaming callbacks | journal + presentation cache | replayable tool lifecycle events | append emitted events | WebUI owns rendering, not tool execution state. |
|
|
251
|
+
| approval callbacks / queues | live Python callbacks | runner process + adapter API surface + journal | approval state/control endpoint | journal request/resolution events only | Pending approval must eventually survive WebUI restart. |
|
|
252
|
+
| clarify callbacks / queues | live Python callbacks | runner process + adapter API surface + journal | clarify state/control endpoint | journal request/resolution events only | Pending clarify must eventually survive WebUI restart. |
|
|
253
|
+
| per-request `HERMES_HOME` env mutation lock | `api/streaming.py` / config helpers | runner process | runner/profile execution context | unchanged | Long-term runner must isolate profile env without process-global mutation. |
|
|
254
|
+
| session-to-active-run mapping | session JSON + active stream ids + memory | journal + adapter API surface | runtime run registry/session mapping | journal run metadata | Reopen session must discover active/completed run. |
|
|
255
|
+
| title generation state | WebUI callbacks/session saves | journal + presentation cache | runtime/session title event | append title events | WebUI may display title updates after event receipt. |
|
|
256
|
+
| usage accounting state | WebUI callbacks/session saves | journal + presentation cache | runtime usage event/source of truth | append usage events | Avoid divergent WebUI-only accounting. |
|
|
257
|
+
| command capability metadata | WebUI command registry + Hermes command assumptions | adapter API surface | runtime command/capability metadata | unchanged | Unknown command support should not be guessed by WebUI. |
|
|
258
|
+
| voice mode state | browser/UI + streaming path | presentation cache + adapter API surface | runtime input/control capability | unchanged | Acceptance tests must pin voice behavior before migration. |
|
|
259
|
+
| project/workspace context | WebUI session/workspace state + env mutation | adapter API surface + runner process | runtime run context | unchanged | Must preserve workspace-aware chat and project context. |
|
|
260
|
+
|
|
261
|
+
Unclassified state is a design blocker. If an implementation slice discovers a
|
|
262
|
+
runtime primitive that does not fit this table, update the RFC before landing code.
|
|
263
|
+
|
|
264
|
+
## Artifact 3: Acceptance Test Catalog
|
|
265
|
+
|
|
266
|
+
These are the user-observable behaviors that must survive the migration. The
|
|
267
|
+
catalog should become automated tests where practical. Where full automation is
|
|
268
|
+
not feasible in the first slice, the PR must include the strongest practical
|
|
269
|
+
diagnostic or manual validation plan.
|
|
270
|
+
|
|
271
|
+
| Behavior | Acceptance criterion | Why it matters | First slice that must prove it |
|
|
272
|
+
|---|---|---|---|
|
|
273
|
+
| Journal replay after refresh/reconnect | reconnect or restart after events have been journaled can replay from cursor without duplicate transcript/tool/reasoning state | proves the browser contract is replayable and duplicate-safe | journal/replay slice |
|
|
274
|
+
| Terminal replay | completed/failed/cancelled runs replay terminal state and do not duplicate transcript content | prevents stale spinner and duplicate-message regressions | journal/replay slice |
|
|
275
|
+
| Interrupted/stale run diagnostics | if WebUI restarts while execution is still owned by the WebUI process, replay shows the last journaled state and a clear interrupted/stale diagnostic instead of pretending the run kept executing | keeps slice 1 honest before a runner exists | journal/replay slice |
|
|
276
|
+
| Execution survives WebUI restart | active execution outlives the main WebUI process, reconnect discovers the active run, ordered replay catches up, and controls such as cancel still work | proves execution ownership actually moved out of the request process | runner/sidecar or external-runtime slice |
|
|
277
|
+
| Cancel during tool call | cancel emits one terminal cancelled state and no stale writeback | catches historical stream ownership races | control migration slice |
|
|
278
|
+
| Cancel during reasoning | partial/reasoning content is preserved cleanly and final state is not provider-error | catches cancellation classification regressions | control migration slice |
|
|
279
|
+
| Approval request/response | approval survives observation, browser response reaches runtime, result is replayable | approval callbacks are cross-cutting and easy to orphan | approval migration slice |
|
|
280
|
+
| Clarify request/response | clarify survives observation, browser response reaches runtime, result is replayable | same risk as approval, different UI/control path | clarify migration slice |
|
|
281
|
+
| Slash commands | `/compress`, `/branch`, `/retry`, and other supported commands keep current semantics | command behavior should not be reimplemented ad hoc | command capability slice |
|
|
282
|
+
| Model switch mid-session | provider/model changes route through the correct runtime context | prevents provider/source-of-truth drift | adapter control slice |
|
|
283
|
+
| Workspace context | run receives the session workspace and attachments context | preserves workbench value | adapter control slice |
|
|
284
|
+
| Multi-profile isolation | profile-specific runs write/read the correct Hermes home and memory | protects #2134-family isolation concerns | runner/profile slice |
|
|
285
|
+
| Queue/continue | follow-up input during live/resumable work obeys Hermes semantics | prevents parallel continuation model | control migration slice |
|
|
286
|
+
| Goal continuation | goal status/control survives the adapter boundary | goal logic is lifecycle-sensitive | goal capability slice |
|
|
287
|
+
| Voice mode | voice-originated input uses the same run/event/control contract | prevents alternate input path drift | adapter parity slice |
|
|
288
|
+
| Projects context | project metadata remains visible and correct across run replay | preserves session/workbench organization | adapter parity slice |
|
|
289
|
+
|
|
290
|
+
## Artifact 4: Slicing Plan and Reversibility
|
|
291
|
+
|
|
292
|
+
### Slice 0: Spec PR
|
|
293
|
+
|
|
294
|
+
Scope:
|
|
295
|
+
|
|
296
|
+
- this RFC update,
|
|
297
|
+
- no runtime behavior change,
|
|
298
|
+
- no streaming hot-path code change.
|
|
299
|
+
|
|
300
|
+
Revert path: revert the docs PR.
|
|
301
|
+
|
|
302
|
+
### Slice 1: Append-only journal/replay beside the legacy path
|
|
303
|
+
|
|
304
|
+
Pre-authorized only after this spec is reviewed and accepted in #1925.
|
|
305
|
+
|
|
306
|
+
Scope:
|
|
307
|
+
|
|
308
|
+
- add an append-only event journal alongside existing callback paths,
|
|
309
|
+
- capture the event families in Artifact 1,
|
|
310
|
+
- persist run metadata, cursor, terminal state, and safe diagnostic fields,
|
|
311
|
+
- allow reconnect to replay from a cursor and then continue live observation,
|
|
312
|
+
- keep `_run_agent_streaming` control flow unchanged,
|
|
313
|
+
- keep cancellation, approval, clarify, queue, and goal behavior unchanged.
|
|
314
|
+
|
|
315
|
+
Non-goals:
|
|
316
|
+
|
|
317
|
+
- no runner process,
|
|
318
|
+
- no sidecar,
|
|
319
|
+
- no adapter interface that changes control flow,
|
|
320
|
+
- no replacement of `STREAMS` as the live delivery path,
|
|
321
|
+
- no speculative rewrite of agent construction/caching.
|
|
322
|
+
|
|
323
|
+
Revert path:
|
|
324
|
+
|
|
325
|
+
- disable journal writes/replay behind one small integration seam,
|
|
326
|
+
- retain legacy WebUI streaming path unchanged.
|
|
327
|
+
|
|
328
|
+
Success criterion:
|
|
329
|
+
|
|
330
|
+
1. Start a non-trivial WebUI run.
|
|
331
|
+
2. Refresh/reconnect the browser, or restart WebUI after events have already been
|
|
332
|
+
journaled.
|
|
333
|
+
3. Rediscover the run from journal metadata.
|
|
334
|
+
4. Replay from cursor without duplicate visible transcript content.
|
|
335
|
+
5. Render the same already-journaled token/reasoning/tool/status/terminal state
|
|
336
|
+
the workbench would have rendered without the reconnect.
|
|
337
|
+
6. If WebUI restarted while execution was still owned by the WebUI process, show
|
|
338
|
+
an explicit interrupted/stale diagnostic rather than claiming the active run
|
|
339
|
+
kept executing.
|
|
340
|
+
|
|
341
|
+
### Slice 2: Adapter interface over the journaled legacy path
|
|
342
|
+
|
|
343
|
+
Status as of 2026-05-17: shipped. PR #2416 defined the adapter-seam contract,
|
|
344
|
+
PR #2424 added the default-off `LegacyJournalRuntimeAdapter` seam, and PR #2438
|
|
345
|
+
kept `/api/chat/start` response shape identical between `legacy-direct` and the
|
|
346
|
+
flagged `legacy-journal` path. Slice 2 remains a reversible boundary change, not
|
|
347
|
+
a sidecar or execution-ownership move.
|
|
348
|
+
|
|
349
|
+
Scope:
|
|
350
|
+
|
|
351
|
+
- introduce the `RuntimeAdapter` interface only after Slice 1 proves replay,
|
|
352
|
+
- implement the first backend as a thin facade over the still-legacy path plus
|
|
353
|
+
journal,
|
|
354
|
+
- keep the browser event contract stable,
|
|
355
|
+
- keep controls routed to existing code until a later control-specific slice.
|
|
356
|
+
|
|
357
|
+
Revert path: switch the feature flag back to direct legacy path.
|
|
358
|
+
|
|
359
|
+
#### Slice 2 interface contract
|
|
360
|
+
|
|
361
|
+
The Slice 2 seam should introduce a deliberately small `RuntimeAdapter` boundary
|
|
362
|
+
without changing the execution backend. The first implementation is a
|
|
363
|
+
`LegacyJournalRuntimeAdapter` that delegates to the existing WebUI-owned
|
|
364
|
+
streaming path and reads the Slice 1 journal for status/replay. That makes the
|
|
365
|
+
adapter a protocol translator over the current backend, not a new runtime owner.
|
|
366
|
+
|
|
367
|
+
Minimal interface shape:
|
|
368
|
+
|
|
369
|
+
```python
|
|
370
|
+
class RuntimeAdapter:
|
|
371
|
+
def start_run(self, request: StartRunRequest) -> RunStartResult: ...
|
|
372
|
+
def observe_run(self, run_id: str, *, cursor: str | None = None) -> RunEventStream: ...
|
|
373
|
+
def get_run(self, run_id: str) -> RunStatus: ...
|
|
374
|
+
def cancel_run(self, run_id: str) -> ControlResult: ...
|
|
375
|
+
def respond_approval(self, run_id: str, approval_id: str, choice: str) -> ControlResult: ...
|
|
376
|
+
def respond_clarify(self, run_id: str, clarify_id: str, response: str) -> ControlResult: ...
|
|
377
|
+
def queue_message(self, run_id: str, message: str, *, mode: str = "queue") -> ControlResult: ...
|
|
378
|
+
def update_goal(
|
|
379
|
+
self,
|
|
380
|
+
session_id: str,
|
|
381
|
+
action: Literal["set", "pause", "resume", "clear", "status", "edit"],
|
|
382
|
+
text: str | None = None,
|
|
383
|
+
) -> ControlResult: ...
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
`queue_message` is named for the legacy queued-message payload shape: it accepts
|
|
387
|
+
follow-up chat text rather than arbitrary runtime input. The method name does not
|
|
388
|
+
require a new HTTP route. Today `/queue` is primarily browser-side queue/drain
|
|
389
|
+
behavior; the adapter method enters the protocol so a later queue/continue slice
|
|
390
|
+
has a typed control surface, but route wiring remains deliberately staged until
|
|
391
|
+
the exact legacy entry point and ordering/idempotency contract are explicit.
|
|
392
|
+
|
|
393
|
+
For `update_goal`, the `action` argument is the bounded adapter capability label.
|
|
394
|
+
During the legacy-journal slice, the legacy goal parser still receives the full
|
|
395
|
+
`text` payload and remains authoritative for details such as the body of
|
|
396
|
+
`set <goal text>`. Future slices must not route goal semantics from `action`
|
|
397
|
+
alone; doing so would drop the goal body and change `/api/goal` behavior.
|
|
398
|
+
|
|
399
|
+
Required data classes / payload fields:
|
|
400
|
+
|
|
401
|
+
| Type | Required fields | Notes |
|
|
402
|
+
|---|---|---|
|
|
403
|
+
| `StartRunRequest` | `session_id`, `message`, `attachments`, `workspace`, `profile`, `provider`, `model`, `toolsets`, `source`, `metadata` | Mirrors current `/api/chat/start` inputs without introducing new behavior. |
|
|
404
|
+
| `RunStartResult` | `run_id`, `session_id`, `stream_id`, `status`, `started_at`, `cursor`, `active_controls` | `stream_id` may remain the legacy stream id during Slice 2. |
|
|
405
|
+
| `RunStatus` | `run_id`, `session_id`, `status`, `last_event_id`, `terminal_state`, `active_controls`, `pending_approval_id`, `pending_clarify_id` | Backed by live legacy state plus journal/session metadata. |
|
|
406
|
+
| `RunEventStream` | ordered events matching Artifact 1, resumable from cursor | Can be implemented by existing SSE + journal replay at first. |
|
|
407
|
+
| `ControlResult` | `accepted`, `status`, `event_id`, `safe_message`, optional internal `payload` | Controls may still call existing handlers in Slice 2. Public HTTP responses must not leak adapter-only fields unless a later RFC expands them. |
|
|
408
|
+
|
|
409
|
+
The interface is intentionally narrower than a runner. It does not own `AIAgent`,
|
|
410
|
+
tool execution, callback queues, cancellation flags, approval callbacks, or
|
|
411
|
+
clarify callbacks in Slice 2. Those remain in the legacy path until their
|
|
412
|
+
individual migration slices.
|
|
413
|
+
|
|
414
|
+
#### Slice 2 feature flag and revert contract
|
|
415
|
+
|
|
416
|
+
Slice 2 should be guarded by one WebUI-local setting/environment flag, for
|
|
417
|
+
example `HERMES_WEBUI_RUNTIME_ADAPTER=legacy-journal` with default
|
|
418
|
+
`legacy-direct` until the seam is proven. The flag selects only the route/adapter
|
|
419
|
+
entry point:
|
|
420
|
+
|
|
421
|
+
```text
|
|
422
|
+
legacy-direct -> current /api/chat/start and /api/chat/stream path
|
|
423
|
+
legacy-journal -> RuntimeAdapter facade over the same legacy execution path + journal
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
Reverting must be operationally boring:
|
|
427
|
+
|
|
428
|
+
1. set the flag back to `legacy-direct`,
|
|
429
|
+
2. restart WebUI if needed,
|
|
430
|
+
3. existing session transcripts and journal files remain readable,
|
|
431
|
+
4. no migration or data deletion is required.
|
|
432
|
+
|
|
433
|
+
The PR that introduces the seam should include a source-level regression that
|
|
434
|
+
the default path remains `legacy-direct` and that the adapter flag is the only
|
|
435
|
+
way the new entry point is selected.
|
|
436
|
+
|
|
437
|
+
#### Slice 2 backend mapping
|
|
438
|
+
|
|
439
|
+
| Adapter method | Slice 2 backend | Explicit non-goal |
|
|
440
|
+
|---|---|---|
|
|
441
|
+
| `start_run` | call the existing chat-start preparation and legacy `_run_agent_streaming` path | do not move `AIAgent` construction or thread ownership |
|
|
442
|
+
| `observe_run` | combine existing live SSE fan-out with journal replay cursor semantics | do not build a second renderer or event protocol |
|
|
443
|
+
| `get_run` | derive status from session metadata, live stream presence, and journal terminal state | do not make `STREAMS` authoritative for durable run existence |
|
|
444
|
+
| `cancel_run` | delegate to existing cancel handler/control path | do not redesign cancellation semantics yet |
|
|
445
|
+
| `respond_approval` | delegate to existing approval response path | do not persist approval callbacks in the main server as a new adapter-owned queue |
|
|
446
|
+
| `respond_clarify` | delegate to existing clarify response path | do not persist clarify callbacks in the main server as a new adapter-owned queue |
|
|
447
|
+
| `queue_message` | delegate to existing queue/continue path when that slice is accepted | do not invent a parallel continuation buffer or run scheduler |
|
|
448
|
+
| `update_goal` | delegate to existing goal command/control path when that slice is accepted | do not move goal evaluation or continuation ownership into the adapter |
|
|
449
|
+
|
|
450
|
+
Any implementation that needs a new long-lived queue, agent cache, cancellation
|
|
451
|
+
registry, or callback registry inside the main WebUI process is out of scope for
|
|
452
|
+
Slice 2 and should become a spec amendment before code lands.
|
|
453
|
+
|
|
454
|
+
#### Slice 2 acceptance tests
|
|
455
|
+
|
|
456
|
+
Before the seam is enabled by default, tests should prove at least:
|
|
457
|
+
|
|
458
|
+
- the `RuntimeAdapter` interface exists and all methods are implemented by the
|
|
459
|
+
`LegacyJournalRuntimeAdapter`;
|
|
460
|
+
- the default route remains the legacy direct path unless the adapter flag is
|
|
461
|
+
explicitly enabled;
|
|
462
|
+
- `start_run` returns the same browser-facing `stream_id` / `session_id` shape as
|
|
463
|
+
`/api/chat/start` for a synthetic request;
|
|
464
|
+
- `observe_run(..., cursor=...)` preserves the existing journal replay ordering
|
|
465
|
+
and duplicate-prevention behavior;
|
|
466
|
+
- `get_run` distinguishes live stream, completed, failed, cancelled, and stale /
|
|
467
|
+
interrupted states using current live state plus journal/session metadata;
|
|
468
|
+
- `cancel_run` delegates to the current cancellation path and still emits one
|
|
469
|
+
terminal result;
|
|
470
|
+
- approval and clarify methods are present but documented as delegated legacy
|
|
471
|
+
controls until their migration slices;
|
|
472
|
+
- disabling the flag returns to the old route path without changing session or
|
|
473
|
+
journal data.
|
|
474
|
+
|
|
475
|
+
These tests are adapter-seam tests, not runner-survives-restart tests. The
|
|
476
|
+
execution-survives-WebUI-restart gate remains deferred to Slice 4.
|
|
477
|
+
|
|
478
|
+
### Slice 3: Control migration
|
|
479
|
+
|
|
480
|
+
Status as of 2026-05-18: Slice 3a cancel routing shipped in v0.51.86 via #2479,
|
|
481
|
+
Slice 3b approval/clarify routing shipped in v0.51.89 via #2496 / #2507, and
|
|
482
|
+
the Slice 3c queue/continue + goal gate shipped in v0.51.90 via #2509.
|
|
483
|
+
Cancel was the smallest control-plane migration because it already had one clear
|
|
484
|
+
browser affordance, one active-run target, and an existing legacy handler to
|
|
485
|
+
delegate to. Approval and clarify then proved the same protocol-translator shape
|
|
486
|
+
for user-mediated callback controls. Queue/continue and goal are the final
|
|
487
|
+
pre-runner control migration because they can change run lifecycle semantics
|
|
488
|
+
rather than just resolve an already-pending control.
|
|
489
|
+
|
|
490
|
+
Scope:
|
|
491
|
+
|
|
492
|
+
- move cancel first,
|
|
493
|
+
- then approval,
|
|
494
|
+
- then clarify,
|
|
495
|
+
- then queue/continue and goal controls,
|
|
496
|
+
- each control gets its own acceptance tests and rollback path.
|
|
497
|
+
|
|
498
|
+
Revert path: per-control feature flags or route-level fallback to legacy control
|
|
499
|
+
handlers.
|
|
500
|
+
|
|
501
|
+
#### Slice 3a: Cancel control gate
|
|
502
|
+
|
|
503
|
+
The first control migration should route Stop Generation through the
|
|
504
|
+
`RuntimeAdapter.cancel_run(...)` seam while preserving the current legacy cancel
|
|
505
|
+
semantics. It should not introduce a new cancellation registry, worker-owned
|
|
506
|
+
signal table, or sidecar boundary. During this slice, `cancel_run` is still a
|
|
507
|
+
protocol translator over the existing cancellation path.
|
|
508
|
+
|
|
509
|
+
Acceptance properties:
|
|
510
|
+
|
|
511
|
+
1. **Same visible result as legacy Stop Generation.** A cancelled turn still
|
|
512
|
+
emits one terminal cancelled/interrupted state and preserves any already
|
|
513
|
+
streamed partial assistant content according to the existing cancellation
|
|
514
|
+
contract.
|
|
515
|
+
2. **Adapter flag is behavior-preserving.** With
|
|
516
|
+
`HERMES_WEBUI_RUNTIME_ADAPTER=legacy-journal`, Stop Generation uses
|
|
517
|
+
`RuntimeAdapter.cancel_run(...)`; with the default `legacy-direct` path, the
|
|
518
|
+
current route remains available as fallback.
|
|
519
|
+
3. **No new runtime-surrogate state.** The implementation must not add a second
|
|
520
|
+
`CANCEL_FLAGS`-like map, cached `AIAgent` table, long-lived queue, or local
|
|
521
|
+
callback registry inside the main WebUI process.
|
|
522
|
+
4. **Journal/status coherence.** After cancellation, replay and session reload
|
|
523
|
+
classify the turn as cancelled/interrupted rather than stale/unknown, and the
|
|
524
|
+
terminal state is visible through the same journal/session diagnostic surface
|
|
525
|
+
used by Slice 1.
|
|
526
|
+
5. **Idempotent duplicate cancel.** Repeating cancel for the same run should be
|
|
527
|
+
safe: one terminal result is recorded, later attempts return a bounded
|
|
528
|
+
`ControlResult` such as `not-active` rather than creating extra terminal
|
|
529
|
+
events or resurrecting stale stream state.
|
|
530
|
+
|
|
531
|
+
Suggested regression coverage:
|
|
532
|
+
|
|
533
|
+
- a route/source test proving the flagged cancel path calls the adapter seam and
|
|
534
|
+
the default path remains the legacy route;
|
|
535
|
+
- an adapter unit test proving `cancel_run` delegates exactly once and returns a
|
|
536
|
+
bounded `ControlResult` for unsupported/not-active runs;
|
|
537
|
+
- an existing cancellation preservation suite run (for example the partial-output
|
|
538
|
+
and cancelled-turn status tests) under the adapter flag or an equivalent
|
|
539
|
+
synthetic harness;
|
|
540
|
+
- a replay/session-load assertion that a cancelled run's terminal state remains
|
|
541
|
+
classifiable from the journal/session surface after reload.
|
|
542
|
+
|
|
543
|
+
Non-goals for Slice 3a:
|
|
544
|
+
|
|
545
|
+
- no approval or clarify migration;
|
|
546
|
+
- no queue/continue or goal migration;
|
|
547
|
+
- no runner process, sidecar, or execution-survives-WebUI-restart claim;
|
|
548
|
+
- no public `/api/chat/start` response-shape expansion for adapter-only fields.
|
|
549
|
+
|
|
550
|
+
#### Slice 3b: Approval and clarify control gate
|
|
551
|
+
|
|
552
|
+
The next control migration should cover approval and clarify together as one
|
|
553
|
+
gate, but not necessarily one implementation commit. They are distinct browser
|
|
554
|
+
widgets, but architecturally they share the same high-risk shape: the agent loop
|
|
555
|
+
pauses on a live callback, the browser presents a user-mediated decision, and the
|
|
556
|
+
runtime must resume from a bounded response without orphaning callback state.
|
|
557
|
+
|
|
558
|
+
During Slice 3b, `RuntimeAdapter.respond_approval(...)` and
|
|
559
|
+
`RuntimeAdapter.respond_clarify(...)` remain protocol translators over the
|
|
560
|
+
existing legacy callback paths. They must not create a second approval queue,
|
|
561
|
+
clarify queue, callback registry, pending-prompt table, or runner-owned wait loop
|
|
562
|
+
inside the main WebUI process.
|
|
563
|
+
|
|
564
|
+
Acceptance properties:
|
|
565
|
+
|
|
566
|
+
1. **Same visible result as legacy approval / clarify.** Existing approval cards,
|
|
567
|
+
clarify prompts, choices, denial paths, and resumed-agent behavior remain
|
|
568
|
+
unchanged for users. The adapter flag changes only the route/control entry
|
|
569
|
+
point.
|
|
570
|
+
2. **Stable response contracts.** Existing approval and clarify HTTP endpoints
|
|
571
|
+
keep their current browser-facing response shapes. Adapter-only fields such as
|
|
572
|
+
internal status strings, callback ids, or active-control metadata must not leak
|
|
573
|
+
into public responses unless a later RFC explicitly expands the contract.
|
|
574
|
+
3. **Bounded missing-prompt behavior.** Responding to a non-existent, already
|
|
575
|
+
resolved, stale, or expired approval/clarify id returns a bounded
|
|
576
|
+
`ControlResult` such as `not-active` / `expired` / `unsupported`; it must not
|
|
577
|
+
block the request, recreate a callback, or synthesize a success path.
|
|
578
|
+
4. **Replayable request and resolution events.** Approval/clarify request and
|
|
579
|
+
resolution events remain journal-visible so reload/reconnect can show the last
|
|
580
|
+
safe state. Slice 3b does not have to make pending approvals survive a WebUI
|
|
581
|
+
process restart while execution is still in-process; that property belongs to
|
|
582
|
+
the runner/sidecar gate.
|
|
583
|
+
5. **No new runtime-surrogate state.** The implementation must not add new
|
|
584
|
+
process-local global maps, long-lived queues, or callback registries under
|
|
585
|
+
adapter-specific names. If the existing legacy callback path needs more state
|
|
586
|
+
to satisfy the route, stop and amend this RFC before landing code.
|
|
587
|
+
6. **Idempotent duplicate responses.** Repeating the same approve/deny/clarify
|
|
588
|
+
response is safe: the runtime accepts at most one response for the pending
|
|
589
|
+
request, records one resolution event, and later attempts return bounded
|
|
590
|
+
not-active/expired status without resuming the run twice.
|
|
591
|
+
|
|
592
|
+
Suggested regression coverage:
|
|
593
|
+
|
|
594
|
+
- route/source tests proving flagged approval and clarify response paths call the
|
|
595
|
+
adapter seam while the default path remains the existing legacy handler;
|
|
596
|
+
- adapter unit tests proving `respond_approval` and `respond_clarify` delegate
|
|
597
|
+
exactly once, return accepted/not-active/unsupported `ControlResult` values, and
|
|
598
|
+
never expose unsafe internal strings to the browser response;
|
|
599
|
+
- journal/session-load assertions that request and resolution events remain
|
|
600
|
+
replayable and renderable after reconnect;
|
|
601
|
+
- duplicate-response tests for approval and clarify ids;
|
|
602
|
+
- existing approval/clarify UI/static tests under default legacy mode to prove no
|
|
603
|
+
browser contract drift.
|
|
604
|
+
|
|
605
|
+
Non-goals for Slice 3b:
|
|
606
|
+
|
|
607
|
+
- no queue/continue or goal migration;
|
|
608
|
+
- no runner process, sidecar, or execution-survives-WebUI-restart claim;
|
|
609
|
+
- no persistence of pending approval/clarify callbacks outside the current legacy
|
|
610
|
+
callback model;
|
|
611
|
+
- no change to approval risk classification, allowed choices, or clarify prompt
|
|
612
|
+
UX;
|
|
613
|
+
- no public chat-start/status response-shape expansion for adapter-only fields.
|
|
614
|
+
|
|
615
|
+
#### Slice 3c: Queue/continue and goal control gate
|
|
616
|
+
|
|
617
|
+
The next control migration should specify queue/continue and goal before any code
|
|
618
|
+
routes those actions through `RuntimeAdapter`. They may ship as separate
|
|
619
|
+
implementation PRs, but they should share one gate because both affect what the
|
|
620
|
+
agent does after the current user turn instead of merely resolving a pending
|
|
621
|
+
prompt. Queue/continue controls append or schedule follow-up input against live
|
|
622
|
+
or resumable work; goal controls set, pause, resume, clear, or inspect a
|
|
623
|
+
standing cross-turn objective. Both can accidentally create a second continuation
|
|
624
|
+
model if WebUI buffers or evaluates them independently.
|
|
625
|
+
|
|
626
|
+
During Slice 3c, `RuntimeAdapter.queue_message(...)` and
|
|
627
|
+
`RuntimeAdapter.update_goal(...)` should remain protocol translators over the
|
|
628
|
+
existing legacy queue/goal paths. They must not create a WebUI-owned run queue,
|
|
629
|
+
goal evaluator, continuation scheduler, agent loop, or sidecar substitute inside
|
|
630
|
+
the main WebUI process.
|
|
631
|
+
|
|
632
|
+
`RuntimeAdapter.update_goal(...)` controls goal state mutations only. Post-turn
|
|
633
|
+
goal evaluation and the decision to continue remain in the existing agent
|
|
634
|
+
conversation loop until the later runner/sidecar slice moves execution
|
|
635
|
+
ownership; Slice 3c must not move that evaluator into WebUI or the adapter.
|
|
636
|
+
|
|
637
|
+
Acceptance properties:
|
|
638
|
+
|
|
639
|
+
1. **Same visible result as legacy queue/continue and goal.** Existing `/queue`
|
|
640
|
+
and `/goal` semantics, browser status affordances, paused/resumed states, and
|
|
641
|
+
post-turn continuation behavior remain unchanged for users. The adapter flag
|
|
642
|
+
changes only the route/control entry point.
|
|
643
|
+
2. **Stable response contracts.** Existing queue/continue and goal HTTP or
|
|
644
|
+
command responses keep their current browser-facing shapes. Adapter-only run
|
|
645
|
+
metadata, internal status, or capability details must not leak into public
|
|
646
|
+
responses unless a later RFC explicitly expands the contract.
|
|
647
|
+
3. **Bounded unavailable-control behavior.** Requests for a missing run,
|
|
648
|
+
unsupported profile, inactive session, paused/cleared goal, or stale queued
|
|
649
|
+
continuation return bounded `ControlResult` states such as `not-active`,
|
|
650
|
+
`unsupported`, or `conflict`; they must not create a phantom run, resurrect a
|
|
651
|
+
dead stream, or silently enqueue work against the wrong session.
|
|
652
|
+
4. **Replayable lifecycle/status evidence.** Queue/continue submission, goal
|
|
653
|
+
status changes, and resulting post-turn continuation decisions remain visible
|
|
654
|
+
through the journal/session diagnostic surface where the legacy path already
|
|
655
|
+
emits equivalent state. Slice 3c does not have to make queued follow-ups or
|
|
656
|
+
goals survive a WebUI process restart while execution is still in-process;
|
|
657
|
+
that stronger property belongs to the runner/sidecar gate.
|
|
658
|
+
5. **No new runtime-surrogate state.** The implementation must not add a second
|
|
659
|
+
process-local queue, goal table, scheduler, cached-agent registry, or
|
|
660
|
+
continuation loop under adapter-specific names. If the existing legacy path
|
|
661
|
+
cannot support the route without new ownership state, stop and amend this RFC
|
|
662
|
+
before landing code.
|
|
663
|
+
6. **Ordering and idempotency are explicit.** Repeating the same queue/continue
|
|
664
|
+
request should not duplicate follow-up work unless the legacy path already
|
|
665
|
+
defines that behavior. Goal pause/resume/clear/status operations should be
|
|
666
|
+
safe to repeat and should report one coherent state.
|
|
667
|
+
|
|
668
|
+
Suggested regression coverage:
|
|
669
|
+
|
|
670
|
+
- route/source tests proving flagged queue/continue and goal paths call the
|
|
671
|
+
adapter seam while the default path remains the existing legacy handler;
|
|
672
|
+
- adapter unit tests proving `queue_message` and `update_goal` delegate exactly
|
|
673
|
+
once, return accepted/not-active/unsupported/conflict `ControlResult` values,
|
|
674
|
+
and do not expose unsafe internal strings to browser responses;
|
|
675
|
+
- ordering/idempotency tests for repeated queue/continue and repeated goal
|
|
676
|
+
pause/resume/clear/status operations;
|
|
677
|
+
- journal/session-load assertions that queue/goal state remains diagnosable after
|
|
678
|
+
reconnect where the legacy path currently emits state;
|
|
679
|
+
- existing queue/goal UI/static tests under default legacy mode to prove no
|
|
680
|
+
browser contract drift.
|
|
681
|
+
|
|
682
|
+
Non-goals for Slice 3c:
|
|
683
|
+
|
|
684
|
+
- no runner process, sidecar, or execution-survives-WebUI-restart claim;
|
|
685
|
+
- no durable WebUI-owned queue or goal scheduler;
|
|
686
|
+
- no migration of `AIAgent` construction, post-turn goal evaluation, or the
|
|
687
|
+
agent continuation loop out of the legacy path;
|
|
688
|
+
- no change to `/goal` command semantics, queue ordering semantics, or supported
|
|
689
|
+
capability metadata;
|
|
690
|
+
- no public chat-start/status response-shape expansion for adapter-only fields.
|
|
691
|
+
|
|
692
|
+
### Slice 4: Runner process / sidecar boundary
|
|
693
|
+
|
|
694
|
+
Slice 4 is the first gate that may move active execution ownership out of the
|
|
695
|
+
main WebUI request process. It should start as a docs/test contract PR before any
|
|
696
|
+
runner code lands. Slice 1's journal/replay layer has shipped and passed active
|
|
697
|
+
validation, Slice 2's default-off adapter seam has shipped, and Slice 3's
|
|
698
|
+
cancel/approval/clarify/goal control routing has proven the protocol-translator
|
|
699
|
+
pattern. Queue remains staged unless maintainers explicitly ask for a separate
|
|
700
|
+
pre-runner queue route.
|
|
701
|
+
|
|
702
|
+
The Slice 4 implementation must not make the adapter a new runtime surrogate.
|
|
703
|
+
The runner boundary may own active execution, process supervision, run lifecycle,
|
|
704
|
+
and callback state, but those responsibilities must be centralized behind the
|
|
705
|
+
adapter/runner contract rather than recreated as scattered globals in the main
|
|
706
|
+
WebUI server.
|
|
707
|
+
|
|
708
|
+
Scope:
|
|
709
|
+
|
|
710
|
+
- move long-lived execution out of the main WebUI request process,
|
|
711
|
+
- runner owns active execution state,
|
|
712
|
+
- main WebUI server observes/replays through the adapter/journal,
|
|
713
|
+
- future Hermes CLI/Python/local API or `/v1/runs` backends can be evaluated
|
|
714
|
+
behind the adapter.
|
|
715
|
+
|
|
716
|
+
Revert path: disable runner backend and fall back to journaled legacy backend.
|
|
717
|
+
|
|
718
|
+
#### Slice 4a: Runner contract gate
|
|
719
|
+
|
|
720
|
+
Before runner code lands, define a narrow contract that covers:
|
|
721
|
+
|
|
722
|
+
1. **Backend selection and rollback.** The existing `legacy-direct` and
|
|
723
|
+
`legacy-journal` paths remain available. Any new runner backend is
|
|
724
|
+
feature-flagged, default-off, and revertible by switching the adapter mode back
|
|
725
|
+
to `legacy-journal` without deleting sessions or journal files.
|
|
726
|
+
2. **Process ownership.** The runner, not the main WebUI request process, owns
|
|
727
|
+
`AIAgent` construction/reuse, active run execution, cancellation flags,
|
|
728
|
+
approval/clarify callback wait state, and post-turn continuation evaluation
|
|
729
|
+
for runs assigned to that backend.
|
|
730
|
+
3. **Durable observation.** The main WebUI server observes through
|
|
731
|
+
`RuntimeAdapter.observe_run(...)`, `get_run(...)`, and the journal cursor. A
|
|
732
|
+
WebUI restart must not be required for the runner to finish writing ordered
|
|
733
|
+
events and terminal state.
|
|
734
|
+
4. **Restart/reattach success criterion.** Start a long-running run, restart only
|
|
735
|
+
`hermes-webui.service`, reload the session, rediscover the active or terminal
|
|
736
|
+
runner-owned run, replay/catch up from cursor without duplicate transcript /
|
|
737
|
+
tool / reasoning state, and preserve cancel if the run is still active.
|
|
738
|
+
5. **Control parity.** Cancel, approval, clarify, goal status/control, and any
|
|
739
|
+
accepted queue/continue behavior route through adapter methods with stable
|
|
740
|
+
browser response shapes. Unsupported controls return bounded `ControlResult`
|
|
741
|
+
states instead of silently falling back to stale in-process state.
|
|
742
|
+
6. **Profile/workspace isolation.** Runner startup receives explicit profile,
|
|
743
|
+
workspace, attachments, model/provider, toolset, and source metadata rather
|
|
744
|
+
than relying on process-global environment mutation in the WebUI server.
|
|
745
|
+
|
|
746
|
+
Suggested contract tests before implementation:
|
|
747
|
+
|
|
748
|
+
- source/RFC tests proving Slice 4 remains feature-flagged and default-off;
|
|
749
|
+
- a fake-runner adapter test that simulates WebUI restart by discarding server
|
|
750
|
+
process-local state while preserving runner/journal state, then verifies
|
|
751
|
+
`get_run` and replay recover the same terminal state;
|
|
752
|
+
- a control-parity fixture proving unsupported runner controls return bounded
|
|
753
|
+
`ControlResult` values and do not fall back to legacy `STREAMS` /
|
|
754
|
+
`CANCEL_FLAGS` state;
|
|
755
|
+
- a profile/workspace payload test proving runner requests carry explicit context
|
|
756
|
+
fields without mutating global `os.environ` in the main WebUI process.
|
|
757
|
+
|
|
758
|
+
Non-goals for Slice 4a:
|
|
759
|
+
|
|
760
|
+
- no removal of the legacy in-process backend;
|
|
761
|
+
- no default-on runner mode;
|
|
762
|
+
- no public chat-start/status response-shape expansion;
|
|
763
|
+
- no new server-side queue endpoint or scheduler just for adapter symmetry;
|
|
764
|
+
- no dependency on Hermes Agent shipping `/v1/runs` before WebUI can validate the
|
|
765
|
+
local runner boundary.
|
|
766
|
+
|
|
767
|
+
#### Slice 4b: Runner adapter client facade
|
|
768
|
+
|
|
769
|
+
Status as of 2026-05-20: shipped in v0.51.94 via #2599.
|
|
770
|
+
|
|
771
|
+
The first code slice after the Slice 4a contract should be a small
|
|
772
|
+
`RunnerRuntimeAdapter` facade that delegates to an injected runner client. This
|
|
773
|
+
is still not the runner process itself. Its job is to pin the adapter-facing
|
|
774
|
+
normalization rules before route wiring or process supervision lands:
|
|
775
|
+
|
|
776
|
+
- `start_run` forwards a `StartRunRequest` carrying explicit session, profile,
|
|
777
|
+
workspace, attachments, model/provider, toolset, source, and metadata payloads;
|
|
778
|
+
- `observe_run` and `get_run` normalize runner responses into `RunEventStream`
|
|
779
|
+
and `RunStatus` so a recreated WebUI server can observe the same runner-owned
|
|
780
|
+
state without relying on process-local `STREAMS`;
|
|
781
|
+
- controls normalize accepted / not-active / unsupported outcomes into bounded
|
|
782
|
+
`ControlResult` values;
|
|
783
|
+
- the facade itself owns no `AIAgent`, worker thread, cancellation registry,
|
|
784
|
+
approval queue, clarify queue, goal scheduler, or server-side queue.
|
|
785
|
+
|
|
786
|
+
The implementation remains default-off until a later slice adds an actual runner
|
|
787
|
+
client/backend and explicit route selection.
|
|
788
|
+
|
|
789
|
+
#### Slice 4c: Feature-flagged runner backend and restart/reattach harness
|
|
790
|
+
|
|
791
|
+
Status as of 2026-05-21: shipped in v0.51.105 via #2696. The code adds a
|
|
792
|
+
default-off `runner-local` adapter selection point plus factory wiring for an
|
|
793
|
+
injected runner client, while keeping live browser chat routes on the legacy
|
|
794
|
+
backend. The restart/reattach harness remains synthetic/fake-runner based until a
|
|
795
|
+
later slice introduces a supervised runner process.
|
|
796
|
+
|
|
797
|
+
After the facade exists, the next narrow implementation slice should add a real
|
|
798
|
+
runner-client/backend selection point and a synthetic restart/reattach harness,
|
|
799
|
+
without routing normal browser chat to that backend yet.
|
|
800
|
+
|
|
801
|
+
Scope:
|
|
802
|
+
|
|
803
|
+
- add a concrete runner-client factory behind an explicit mode such as
|
|
804
|
+
`HERMES_WEBUI_RUNTIME_ADAPTER=runner-local`, while preserving `legacy-direct`
|
|
805
|
+
and `legacy-journal` as the default/revert paths;
|
|
806
|
+
- validate that `StartRunRequest` carries explicit session, profile, workspace,
|
|
807
|
+
attachments, provider/model, toolset, source, and metadata fields into the
|
|
808
|
+
runner boundary without relying on WebUI process-global environment mutation;
|
|
809
|
+
- prove a recreated WebUI adapter can rediscover runner-owned status and replay
|
|
810
|
+
ordered events from the runner/journal surface after process-local state is
|
|
811
|
+
discarded;
|
|
812
|
+
- keep controls bounded through `ControlResult` values, with unsupported controls
|
|
813
|
+
returning `unsupported` / `not-active` rather than falling back to stale legacy
|
|
814
|
+
`STREAMS` or callback queues;
|
|
815
|
+
- keep the live `/api/chat/start` path on the legacy backend until the runner
|
|
816
|
+
backend has a passing restart/reattach harness and maintainer approval to wire
|
|
817
|
+
route selection.
|
|
818
|
+
|
|
819
|
+
Acceptance tests for Slice 4c:
|
|
820
|
+
|
|
821
|
+
1. **Default-off selection.** `legacy-direct` remains the default; `runner-local`
|
|
822
|
+
or any later runner mode is selected only by an explicit feature flag.
|
|
823
|
+
2. **No route-shape drift.** Adding the runner backend does not expand public
|
|
824
|
+
`/api/chat/start`, cancel, approval, clarify, goal, or status response shapes
|
|
825
|
+
while the route remains legacy-backed.
|
|
826
|
+
3. **Restart/reattach harness.** A fake or local runner fixture can start a run,
|
|
827
|
+
discard the first WebUI adapter instance, recreate the adapter, and still
|
|
828
|
+
observe ordered events plus terminal/live status from durable runner-owned
|
|
829
|
+
state.
|
|
830
|
+
4. **Control bounds.** Cancel / approval / clarify / queue / goal controls route
|
|
831
|
+
through the runner client only when the runner backend is selected, and
|
|
832
|
+
unsupported controls return bounded `ControlResult` values without consulting
|
|
833
|
+
legacy process-local state.
|
|
834
|
+
5. **No runtime-surrogate globals.** The main WebUI server must not gain new
|
|
835
|
+
module-level maps for runner-owned streams, cancellation flags, pending
|
|
836
|
+
approval/clarify callbacks, cached agents, or goal/queue schedulers.
|
|
837
|
+
|
|
838
|
+
Non-goals for Slice 4c:
|
|
839
|
+
|
|
840
|
+
- no default-on runner backend;
|
|
841
|
+
- no removal of the legacy in-process backend;
|
|
842
|
+
- no public response-shape expansion;
|
|
843
|
+
- no live chat route switch to the runner backend before the restart/reattach
|
|
844
|
+
harness is reviewed;
|
|
845
|
+
- no server-side queue endpoint or queue scheduler just for adapter symmetry.
|
|
846
|
+
|
|
847
|
+
#### Slice 4d: Supervised runner backend route gate
|
|
848
|
+
|
|
849
|
+
Status as of 2026-05-23: shipped in v0.51.108 via #2744. The gate remains a
|
|
850
|
+
docs/test contract: it defines the default-off route-selection requirements but
|
|
851
|
+
does not itself route live chat to a runner backend.
|
|
852
|
+
|
|
853
|
+
After `runner-local` selection exists, the next reviewable gate should define the
|
|
854
|
+
first supervised/local runner backend and the route-selection harness before live
|
|
855
|
+
browser chat can use it. This is still a contract/test slice first: no default-on
|
|
856
|
+
runner mode, no removal of `legacy-direct` or `legacy-journal`, and no claim that
|
|
857
|
+
production WebUI turns survive restart until the harness demonstrates it.
|
|
858
|
+
|
|
859
|
+
Scope:
|
|
860
|
+
|
|
861
|
+
- define the runner process/client lifecycle: how a run is spawned, supervised,
|
|
862
|
+
observed, and terminated without placing new active-run maps in the main WebUI
|
|
863
|
+
request process;
|
|
864
|
+
- define the route-selection point for `/api/chat/start` without changing the
|
|
865
|
+
public response shape while the default remains legacy-backed;
|
|
866
|
+
- specify how runner-owned events become WebUI journal events with stable
|
|
867
|
+
cursors, terminal state, and replay ordering;
|
|
868
|
+
- specify cancellation, approval, clarify, goal, and staged queue behavior as
|
|
869
|
+
runner-client `ControlResult` responses, with unsupported controls bounded and
|
|
870
|
+
visible rather than silently falling back to legacy process-local callbacks;
|
|
871
|
+
- carry the runtime API gap matrix forward: missing Hermes-owned capabilities
|
|
872
|
+
such as active-run discovery, session-to-run lookup, command capability
|
|
873
|
+
metadata, artifact events, and provider/tool routing should remain explicit
|
|
874
|
+
gaps or temporary adapter state, not private WebUI runtime replicas.
|
|
875
|
+
|
|
876
|
+
Acceptance tests for Slice 4d:
|
|
877
|
+
|
|
878
|
+
1. **Route remains default-off.** Unset `HERMES_WEBUI_RUNTIME_ADAPTER` and
|
|
879
|
+
`legacy-direct` keep `/api/chat/start` on the existing path; `runner-local`
|
|
880
|
+
is the only mode allowed to select the runner route.
|
|
881
|
+
2. **Restart/reattach harness proves ownership moved.** A fake or local runner
|
|
882
|
+
starts a run, the first WebUI server/adapter instance is discarded, a new
|
|
883
|
+
adapter instance discovers the same active or terminal run, replay catches up
|
|
884
|
+
from cursor, and cancel remains available if the run is still active.
|
|
885
|
+
3. **No public response-shape drift.** Chat start and control responses remain
|
|
886
|
+
stable while adapter-only fields stay internal or explicitly documented as a
|
|
887
|
+
new contract revision.
|
|
888
|
+
4. **No runtime-surrogate globals.** The main WebUI server does not gain new
|
|
889
|
+
module-level maps for runner-owned streams, cancel flags, approval/clarify
|
|
890
|
+
callbacks, cached agents, goal state, or queue schedulers.
|
|
891
|
+
5. **Explicit context payloads.** Runner startup carries session, profile,
|
|
892
|
+
workspace, attachments, provider/model, toolsets, source, and metadata as
|
|
893
|
+
payload fields rather than depending on process-global environment mutation in
|
|
894
|
+
the WebUI server.
|
|
895
|
+
|
|
896
|
+
Non-goals for Slice 4d:
|
|
897
|
+
|
|
898
|
+
- no default-on runner backend;
|
|
899
|
+
- no removal of the legacy in-process backends;
|
|
900
|
+
- no server-side queue endpoint or scheduler just for adapter symmetry;
|
|
901
|
+
- no permanent WebUI-owned active-run discovery cache when the missing capability
|
|
902
|
+
belongs in a runner or future Hermes Runtime API;
|
|
903
|
+
- no broad UI/product surface migration; WebUI remains the rich workbench while
|
|
904
|
+
only execution ownership moves.
|
|
905
|
+
|
|
906
|
+
#### Slice 4e: Default-off runner chat-start route-selection harness
|
|
907
|
+
|
|
908
|
+
Status as of 2026-05-24: shipped in v0.51.129 via #2794. The route-selection
|
|
909
|
+
harness now makes adapter mode selection explicit: `legacy-direct` remains the
|
|
910
|
+
default, `legacy-journal` still delegates to the existing journaled legacy path,
|
|
911
|
+
and `runner-local` returns a bounded not-configured response instead of silently
|
|
912
|
+
starting an in-process legacy run.
|
|
913
|
+
|
|
914
|
+
The first implementation after the Slice 4d gate should wire the
|
|
915
|
+
`/api/chat/start` selection point to the existing `RuntimeAdapter` factory
|
|
916
|
+
without adding a supervised runner process yet. The harness must make the
|
|
917
|
+
selection behavior explicit: `legacy-direct` stays default, `legacy-journal`
|
|
918
|
+
continues to delegate to the legacy in-process stream path, and `runner-local`
|
|
919
|
+
does not silently fall back to legacy when no runner client is configured.
|
|
920
|
+
|
|
921
|
+
Scope:
|
|
922
|
+
|
|
923
|
+
- route `/api/chat/start` through `build_runtime_adapter(...)` when an adapter
|
|
924
|
+
mode is explicitly selected;
|
|
925
|
+
- keep the successful browser response whitelisted to legacy-compatible fields
|
|
926
|
+
such as `stream_id`, `session_id`, `pending_started_at`, `turn_id`, `title`,
|
|
927
|
+
and effective model/provider metadata;
|
|
928
|
+
- return a bounded not-configured error for `runner-local` until a supervised
|
|
929
|
+
runner client/backend lands;
|
|
930
|
+
- pass the existing explicit `StartRunRequest` payload fields across the seam.
|
|
931
|
+
|
|
932
|
+
Acceptance tests for Slice 4e:
|
|
933
|
+
|
|
934
|
+
1. **Default remains legacy-direct.** With no adapter env var, `/api/chat/start`
|
|
935
|
+
keeps using `_start_chat_stream_for_session(...)` directly.
|
|
936
|
+
2. **Legacy-journal remains behavior-preserving.** The flagged legacy adapter
|
|
937
|
+
still delegates to the same stream-start helper and preserves the public
|
|
938
|
+
response shape.
|
|
939
|
+
3. **Runner-local does not fallback silently.** If `runner-local` is selected but
|
|
940
|
+
no runner client exists, the route returns a bounded error instead of starting
|
|
941
|
+
a WebUI-owned legacy run behind the operator's back.
|
|
942
|
+
4. **No adapter-internal response drift.** `run_id`, `status`, and
|
|
943
|
+
`active_controls` remain internal until a later contract explicitly exposes
|
|
944
|
+
them.
|
|
945
|
+
5. **No runtime-surrogate globals.** The harness does not add runner-owned stream,
|
|
946
|
+
cancel, approval, clarify, cached-agent, goal, or queue maps to the main WebUI
|
|
947
|
+
process.
|
|
948
|
+
|
|
949
|
+
Non-goals for Slice 4e:
|
|
950
|
+
|
|
951
|
+
- no supervised runner process yet;
|
|
952
|
+
- no default-on runner mode;
|
|
953
|
+
- no execution-survives-WebUI-restart claim for production chat turns;
|
|
954
|
+
- no removal of `legacy-direct` or `legacy-journal`;
|
|
955
|
+
- no server-side queue endpoint or queue scheduler just for adapter symmetry.
|
|
956
|
+
|
|
957
|
+
#### Slice 4f: Supervised local runner client backend gate
|
|
958
|
+
|
|
959
|
+
Status as of 2026-05-28: client transport proposed in #3073 behind
|
|
960
|
+
`HERMES_WEBUI_RUNNER_BASE_URL`; it should be described as under review until a
|
|
961
|
+
release PR actually ships it.
|
|
962
|
+
`runner-local` still remains default-off and returns the bounded not-configured
|
|
963
|
+
path unless that endpoint is explicitly configured. When configured, WebUI uses a
|
|
964
|
+
JSON HTTP client boundary for start / observe / status / controls and bridges
|
|
965
|
+
observed runner events through the existing SSE stream route rather than adding
|
|
966
|
+
main-process runner-owned maps.
|
|
967
|
+
This bridge is intentionally a WebUI consumer transport seam: the configured
|
|
968
|
+
runner must emit events that are already compatible with the browser SSE event
|
|
969
|
+
names/payloads, or a later runner-owned normalization layer must translate
|
|
970
|
+
Hermes runtime families such as `token.delta`, `tool.started`, and `done` before
|
|
971
|
+
they reach this route.
|
|
972
|
+
|
|
973
|
+
After the route-selection harness ships, the next reviewable step is not to make
|
|
974
|
+
`runner-local` the default. It is to define the first concrete supervised/local
|
|
975
|
+
runner client backend that can replace the bounded 501 path under the existing
|
|
976
|
+
feature flag and prove execution ownership has moved out of the main WebUI
|
|
977
|
+
request process.
|
|
978
|
+
|
|
979
|
+
This slice is a contract gate before backend code lands. The goal is to pin the
|
|
980
|
+
minimum runner client behavior so the implementation cannot become a renamed
|
|
981
|
+
`STREAMS` / `CANCEL_FLAGS` / cached `AIAgent` surrogate inside `api/routes.py`.
|
|
982
|
+
|
|
983
|
+
Scope:
|
|
984
|
+
|
|
985
|
+
- define the runner client process boundary and lifecycle: how `start_run`
|
|
986
|
+
spawns or hands off work, how the child is supervised, and how terminal state
|
|
987
|
+
is recorded without a main-process active-run dictionary;
|
|
988
|
+
- require a durable runner-owned run id plus session-to-run lookup that a freshly
|
|
989
|
+
restarted WebUI process can discover without consulting old `STREAMS` entries;
|
|
990
|
+
- require ordered event replay through the existing journal/cursor surface, so
|
|
991
|
+
token, reasoning, progress, tool, usage, error, and done events render through
|
|
992
|
+
the same browser path as legacy replay;
|
|
993
|
+
- define cancel as the first required live control for active runner-owned runs,
|
|
994
|
+
with approval, clarify, goal, and queue either mapped to explicit runner
|
|
995
|
+
capabilities or returned as bounded unsupported/conflict `ControlResult`
|
|
996
|
+
values;
|
|
997
|
+
- keep profile, workspace, attachments, provider/model, toolset, source, and
|
|
998
|
+
metadata as explicit payload fields at the runner boundary rather than
|
|
999
|
+
depending on process-global WebUI environment mutation.
|
|
1000
|
+
|
|
1001
|
+
Acceptance tests for Slice 4f:
|
|
1002
|
+
|
|
1003
|
+
1. **501 path replaced only when configured.** Unset adapter mode and
|
|
1004
|
+
`legacy-journal` behavior stay unchanged; `runner-local` uses the supervised
|
|
1005
|
+
runner client only when the backend is explicitly configured.
|
|
1006
|
+
2. **Restart/reattach proves ownership moved.** Start a runner-owned run,
|
|
1007
|
+
discard/restart the WebUI server process, rediscover the active or terminal
|
|
1008
|
+
run from durable runner/journal state, replay from cursor without duplicates,
|
|
1009
|
+
and preserve cancel if the run is still active.
|
|
1010
|
+
3. **No runtime-surrogate globals.** The main WebUI server does not gain new
|
|
1011
|
+
module-level maps for runner-owned streams, cancel flags, approval/clarify
|
|
1012
|
+
callbacks, cached agents, goal state, queue schedulers, or child-process run
|
|
1013
|
+
registries. Supervision state belongs to the runner client/backend boundary.
|
|
1014
|
+
4. **Stable browser contracts.** Successful chat-start responses remain limited
|
|
1015
|
+
to the legacy-compatible field whitelist unless a later contract revision
|
|
1016
|
+
explicitly exposes `run_id`, `status`, or `active_controls`.
|
|
1017
|
+
5. **Bounded control gaps.** Unsupported runner controls return safe
|
|
1018
|
+
`unsupported`, `not-active`, or `conflict` results; they must not fall back to
|
|
1019
|
+
legacy callback queues for a runner-owned run.
|
|
1020
|
+
|
|
1021
|
+
Non-goals for Slice 4f:
|
|
1022
|
+
|
|
1023
|
+
- no default-on runner mode;
|
|
1024
|
+
- no removal of the legacy in-process backends;
|
|
1025
|
+
- no broad WebUI product-surface migration;
|
|
1026
|
+
- no server-side queue scheduler just for adapter symmetry;
|
|
1027
|
+
- no permanent WebUI-owned active-run discovery cache that duplicates runner or
|
|
1028
|
+
future Hermes Runtime API responsibility.
|
|
1029
|
+
|
|
1030
|
+
## First Meaningful Success Criteria
|
|
1031
|
+
|
|
1032
|
+
The first meaningful milestones are deliberately split.
|
|
1033
|
+
|
|
1034
|
+
### Journal / Replay Gate
|
|
1035
|
+
|
|
1036
|
+
This gate belongs to Slice 1. It does not prove active execution survives a WebUI
|
|
1037
|
+
process restart, because execution is still owned by the WebUI process in this
|
|
1038
|
+
slice.
|
|
1039
|
+
|
|
1040
|
+
It proves:
|
|
1041
|
+
|
|
1042
|
+
1. A WebUI run emits append-only journal events with stable cursors.
|
|
1043
|
+
2. Browser refresh/reconnect can replay already-journaled events from cursor.
|
|
1044
|
+
3. Terminal `done`, `error`, or `cancelled` state replays without duplicate
|
|
1045
|
+
transcript content.
|
|
1046
|
+
4. Tool/reasoning/status state can be reconstructed from replayed journal events.
|
|
1047
|
+
5. If WebUI restarts before execution ownership has moved out of process, the UI
|
|
1048
|
+
can show a clear interrupted/stale diagnostic for the last journaled run state.
|
|
1049
|
+
|
|
1050
|
+
### Execution-Survives-WebUI-Restart Gate
|
|
1051
|
+
|
|
1052
|
+
This stronger gate belongs to the runner/sidecar or external-runtime slice, not
|
|
1053
|
+
Slice 1. It proves execution ownership has actually moved out of the main WebUI
|
|
1054
|
+
request process:
|
|
1055
|
+
|
|
1056
|
+
1. Start a long-running run from WebUI.
|
|
1057
|
+
2. Restart only `hermes-webui`.
|
|
1058
|
+
3. Keep the active run executing outside the restarted WebUI process.
|
|
1059
|
+
4. Reload the browser/session.
|
|
1060
|
+
5. Rediscover the active run and replay/catch up from cursor.
|
|
1061
|
+
6. Preserve the rendered workbench state without duplicate transcript content.
|
|
1062
|
+
7. If the run is still active, cancellation still works.
|
|
1063
|
+
|
|
1064
|
+
If this works without moving runtime ownership into a new pile of process-local
|
|
1065
|
+
globals, the architecture is moving in the right direction.
|
|
1066
|
+
|
|
1067
|
+
## Open Questions
|
|
1068
|
+
|
|
1069
|
+
- What exact storage format should Slice 1 use: SQLite run/event tables, JSONL,
|
|
1070
|
+
or a hybrid with transcript-derived checkpoints?
|
|
1071
|
+
- How long should event replay be retained after terminal state?
|
|
1072
|
+
- Which event fields must be redacted before journal persistence?
|
|
1073
|
+
- Should the journal live under the WebUI state dir, the session dir, or a
|
|
1074
|
+
future runtime-specific subdirectory?
|
|
1075
|
+
- What is the minimum set of synthetic event fixtures needed to compare legacy
|
|
1076
|
+
rendering with replay rendering?
|
|
1077
|
+
- Which controls need route-level feature flags before migration?
|
|
1078
|
+
- If Hermes Agent later ships a durable `/v1/runs` API, which adapter fields map
|
|
1079
|
+
directly and which remain WebUI presentation concerns?
|