Skip to content

Add first-class Hermes restore and lifecycle support - #9540

Merged
austinywang merged 76 commits into
mainfrom
issue-9520-hermes-first-class
Aug 10, 2026
Merged

austinywang merged 76 commits into
mainfrom
issue-9520-hermes-first-class

Conversation

@austinywang

@austinywang austinywang commented Aug 4, 2026 •

Copy link
Copy Markdown
Contributor

Closes #9520

Summary

  • register Hermes Agent as a native Vault capability and recover fresh-session IDs from the unique active CLI/TUI row in state.db, scoped by cwd
  • preserve explicit --resume/-r IDs, captured launch flags, HERMES_HOME, native Hermes identity, and cwd-aware session indexing
  • make hook routing table-driven, route Hermes callbacks through the per-launch ambient cmux context, and update the Hermes consent allowlist
  • keep the existing turn-end, permission, feed, status, hibernation, session-index, dock, and restore paths on their shared agent lifecycle implementations
  • bootstrap Hermes's fresh TUI gateway and compute-host Python processes so prompt, response, approval, feed, and turn-end hooks keep firing on second and subsequent messages

Hook architecture

Hermes 0.20.0 shell hooks remain the single consent-controlled event path for CLI/TUI and Gateway sessions. They expose the lifecycle, LLM, tool, and approval events cmux needs and are governed by shell-hooks-allowlist.json, so this PR does not add a duplicate Python plugin or Gateway-only HOOK.yaml bridge.

The TUI has one process-boundary exception: Hermes's Python launcher creates hermes-tui-active-session-*.json only after the outer cmux wrapper execs Hermes, and the fresh TUI gateway cannot inherit the launcher's process-local hook registry. Interactive TUI launches therefore receive an invocation-private TMPDIR plus a scoped HERMES_PYTHON bootstrap. The bootstrap activates only for tui_gateway.entry and tui_gateway.compute_host, registering prompt, response, approval, feed, and turn-end callbacks inside the processes that execute every turn. It filters the cmux lifecycle callbacks owned by the watcher, which accepts exactly one non-symlink active-session file, validates its session ID, and feeds the initial session, /new transitions, and finalization into the shared cmux hooks hermes-agent lifecycle path with the launch's workspace, surface, and tagged socket attribution. Classic --cli launches skip both bridges.

The watcher blocks on an anonymous pipe driven by one persistent 100 ms ticker, which is compatible with macOS Bash 3.2. It uses neither fractional read -t nor a filesystem FIFO; if the ticker fails, cleanup waits until Hermes has exited so a live TMPDIR is never removed.

Safety

The state.db lookup is read-only, bounded to two rows, and fails closed for missing cwd/schema, database errors, multiple active rows, or multiple fresh panes sharing one state-store/cwd key. Custom Vault registrations cannot acquire the built-in Hermes persisted-store capability.

Validation

  • earlier review regression test-only commit: 20fe7baa8a; fixes: 65e56c4f54
  • TUI lifecycle bridge: test-only 3d3a4e16ac fails without the bridge; fix 7d6ba1fc58 passes
  • macOS Bash 3.2 watcher: test-only bbbc36769c records 1.24 CPU-seconds while idle; anonymous-pipe ticker fix 3e263ff6da passes
  • repeated TUI completions: test-only 656823b0bd fails when fresh gateway processes lack callbacks; scoped gateway bootstrap f568ecb99b passes
  • real Hermes 0.20 TUI on exact pushed HEAD: two consecutive turns each emitted prompt-submit, agent-response, session-end, agentHook.stop.notify, and a notification-store record
  • tagged Debug build on exact pushed HEAD: hermes-review
  • cmux-unit scheme build succeeded with the GlobalISel workaround
  • native arm64 CMUXAgentLaunch: 285/285 tests passed
  • focused CmuxTerminal literal-glob behavior: 1/1 passed (standalone SwiftPM also emits its pre-existing Ghostty static-library naming diagnostic)
  • Hermes wrapper suite, Xcode project normalization, test wiring, workspace package grouping, Package.resolved policy, localization catalog parsing/placeholder parity, and git diff --check passed
  • localization audit: the four affected keys have translated values and matching placeholders in all 20 supported locales

View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.


Summary by cubic

Adds first‑class Hermes restore and lifecycle with durable state.db indexing, a TUI gateway bridge, profile‑pinned resumes, and shared agent command shims. Restores are routed through the managed wrapper for deterministic hooks and now keep older snapshots compatible by decoding persisted Hermes registrations. Addresses #9520.

  • New Features

    • Registers Hermes as a native Vault agent with persisted hermesStateDB (cwd-aware when available); only interactive CLI/TUI runs are restorable; resumes are pinned to the owning profile/HERMES_HOME.
    • Bridges TUI lifecycle via cmux-hermes-agent-wrapper, cmux-hermes-python-wrapper, and cmux-hermes-sitecustomize.py; watcher keeps active-session IDs correct and dedupes Hermes notifications.
    • Adds shared per-surface command shims for hermes, claude, and codex; routes Hermes ~/.local/bin profile aliases through the wrapper using a cached alias catalog.
    • Routes Hermes Vault restore launches through the managed wrapper using CMUX_HERMES_AGENT_WRAPPER_SHIM, ensuring hook reinstallation and deterministic profile pinning.
  • Bug Fixes

    • Validates Hermes session existence on quit/restore and clears or re‑arms bindings; re‑arms resumes after a completed turn or when a hook record is reused.
    • Rejects stale Hermes Python wrappers, constrains Python entrypoint detection to the TUI, preserves native launch when PATH‑shadowed, and parses attached short resume flags (e.g., -r<session>).
    • Decodes and migrates persisted Hermes Vault registrations in app snapshots so older snapshots load and restore correctly.

Written for commit e5cdb71. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features

    • Added built-in Hermes support for session detection, persistence, resuming, and working-directory tracking.
    • Added automatic Hermes hook setup and shared command-shim support for supported agents.
    • Added compatibility with older session databases lacking working-directory metadata.
  • Improvements

    • Improved safeguards against ambiguous matches and cross-pane session sharing.
    • Improved hook cleanup, approval handling, and dispatch validation.
    • Added clearer feedback when hook setup cannot find a target.
  • Tests

    • Expanded coverage for Hermes workflows, hooks, session matching, compatibility, and command shims.

Note

High Risk
Large surface area touching session identity, restore/exec paths, hook subprocess routing, and SQLite-backed recovery; mistakes could break resume or mis-attribute hooks across workspaces, though changes are heavily tested.

Overview
Adds first-class Hermes Agent support across hooks, Vault indexing, terminal shims, and restore—so cmux can attribute lifecycle events, persist resumable checkpoints, and relaunch through the managed wrapper like Claude and Codex.

Hooks and routing generalize agent hook dispatch via HookDispatch (ambient vs pinned CLI/socket); Grok and Antigravity move to pinned markers. Hermes gets YAML hook install with config-dir creation, locked allowlist updates, deduped notifications, session-end as a turn boundary, and safer session-ID resolution (approval payloads, TUI active-session file in private TMPDIR, never treating the surface UUID as Hermes identity). Pinned hook install now fails clearly when no running app/socket is available.

Restore and durability extend AgentRestoreLaunch / AgentRestorePlanner for Hermes (wrapper shim env, profile/HERMES_HOME pinning, Codex preflights). cmux restore can repair corrupt Hermes checkpoints via HermesLegacySessionIdentityRecovery against hook state and read-only state.db snapshots; resume bindings validate session existence before pinning.

Vault / indexing adds cwd-aware Hermes session search, batched recovery inspection, WAL-safe snapshot reads, and explicit --resume / -r parsing on the persisted session store.

Terminal replaces Claude-only PATH shims with a shared agent command shim set (claude, codex, hermes) plus Hermes profile-alias discovery through a cached catalog; CLI executable resolution skips shim paths generically.

Reviewed by Cursor Bugbot for commit 12ea44f. Bugbot is set up for automated code reviews on this repo. Configure here.

@coderabbitai

coderabbitai Bot commented Aug 4, 2026 •

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The PR adds first-class Hermes support with persisted-session restoration, cwd-aware indexing, configurable hooks, generic agent command shims, wrapper execution, and integration coverage.

Changes

Hermes support integration

Layer / File(s) Summary
Hermes session resolution and indexing
Sources/VaultAgentRegistry.swift, Sources/VaultPersistedSessionResolver.swift, Sources/VaultAgentProcessScanner.swift, Packages/macOS/CMUXAgentLaunch/.../HermesAgent*
Adds Hermes registration, persisted-store decoding, explicit resume parsing, cwd-based state-database resolution, ambiguity handling, and cwd-aware session indexing.
Hook dispatch and approval ownership
CLI/CMUXCLI+AgentHookDefinitions.swift, CLI/CMUXCLI+HermesAgentHooks.swift, CLI/cmux.swift, Packages/macOS/CMUXAgentLaunch/.../HermesAgentHook*
Adds configurable dispatch markers, Hermes approval parsing, CMUX-owned command detection, stale approval cleanup, and automatic-approval filtering.
Generic agent command shims
Packages/macOS/CmuxTerminal/..., Packages/macOS/CmuxTerminalCore/..., Sources/AgentExecutableResolver.swift, Sources/TerminalSurfaceRuntimeWiring.swift
Replaces Claude-specific shim state with shared Claude, Codex, and Hermes shim descriptors, installation, lifecycle management, runtime wiring, and executable filtering.
Wrapper and integration validation
Resources/bin/cmux-hermes-agent-wrapper, cmuxTests/*Hermes*, tests/test_hermes_wrapper_hooks.py, cmux.xcodeproj/project.pbxproj, .github/workflows/ci.yml
Adds Hermes wrapper behavior, session and hook tests, shared shim test updates, Xcode project registration, and CI execution.

Estimated code review effort: 5 (Critical) | ~100 minutes

Possibly related PRs

Suggested reviewers: lawrencecchen, azooz2003-bit


Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (5 errors, 2 warnings)

Check name Status Explanation Resolution
Cmux Swift Blocking Runtime ❌ Error New production HermesAgentStateDBResolver.swift calls sqlite3_busy_timeout(database, 50), which can sleep and retry while state.db is busy. Remove the busy timeout and fail closed on SQLITE_BUSY, or replace it with a non-blocking SQLite completion or unlock-notification path.
Cmux Expensive Synchronous Load ❌ Error The PR adds synchronous Hermes state.db resolution in process scanning; ProcessDetectedResumeIndexes.loadSynchronously() can call it from @MainActor AppDelegate termination/snapshot paths. Move Hermes state.db lookup behind an off-main Task.detached/background resolver or cached SharedLiveAgentIndex result before close and termination snapshot paths.
Cmux Swift Package Boundaries ❌ Error The PR adds pure persisted-session parsing and ambiguity/caching logic in app-target Sources, including VaultPersistedSessionResolver, while only the SQLite resolver is in CMUXAgentLaunch. Extract the persisted-session domain into a small CmuxVaultCore target; expose CmuxVaultAgentPersistedSessionStore (or a persisted-session resolver protocol) and keep scanner wiring in Sources.
Cmux User-Facing Error Privacy ❌ Error The new pinned-hook error names provider hooks and exposes CMUX_SOCKET_PATH and CMUX_TAG in user-visible localized error text, violating the rule's provider and environment-variable restrictions. Use generic cmux terms in the error and recovery copy. Move CMUX_SOCKET_PATH/CMUX_TAG guidance to explicit advanced help, and keep provider/path diagnostics out of user-visible errors.
Cmux Full Internationalization ❌ Error Hermes production code uses catalog keys with incomplete coverage: configDirectoryIsFile has 2/20 locales and sessionIndex.agent.hermesAgent has 3/20; only the new pinned-target key is complete. Add translated entries for all 20 supported locales in Resources/Localizable.xcstrings for cli.hooks.error.configDirectoryIsFile and sessionIndex.agent.hermesAgent, then verify Hermes CLI output uses localized APIs.
Linked Issues check ⚠️ Warning The PR covers native Hermes registration, cwd-scoped restore, pinned shell hooks, indexing, and lifecycle paths, but not the linked issue's Gateway/Plugin hook integration. Implement the required Gateway/Plugin hook integration, or split that requirement into a separately linked issue and document the remaining scope before merging.
Docstring Coverage ⚠️ Warning Docstring coverage is 5.83% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (18 passed)
Check name Status Explanation
Out of Scope Changes check ✅ Passed The generalized shim and hook-routing changes support Hermes integration and shared agent infrastructure without introducing an unrelated product feature.
Cmux Swift Actor Isolation ✅ Passed Changed production code adds immutable Sendable values and explicit @MainActor UI-task boundaries; no new shared mutable Sendable reference, async service protocol, or implicit MainActor model issu...
Cmux Browser Automation Off-Main ✅ Passed The branch changes no socket browser router or policy files; existing wait-capable browser.* methods remain in socketWorkerMethods and have ControlCommandExecutionPolicyTests coverage.
Cmux Cache Substitution Correctness ✅ Passed VaultPersistedSessionResolver is per-scan; a cold lookup reads state.db, and the scanner calls each unique key once before building the restore snapshot, so no stale long-lived cache substitutes th...
Cmux No Hacky Sleeps ✅ Passed The wrapper uses a bounded Perl alarm to terminate a wedged installer, with coverage in test_stalled_installer_is_bounded; the only sleep is deterministic fake-installer test scaffolding.
Cmux Algorithmic Complexity ✅ Passed The new Hermes resolver is dictionary-cached and SQL-bounded with LIMIT 2; cwd candidates and shim definitions are fixed-size. The process-registration scan existed before this PR and was not worse...
Cmux Swift Concurrency ✅ Passed The new shim lifecycle stores and cancels both Tasks; it generalizes the existing Claude pattern. The diff adds no background Dispatch, Combine state, or internal completion-handler API.
Cmux Swift @Concurrent ✅ Passed The PR places file-heavy shim installation in a compiler-gated @concurrent @Sendable closure; lifecycle methods remain @MainActor synchronous, and no added nonisolated async function lacks an expli...
Cmux Swiftpm Lockfiles ✅ Passed PR delta changes no Package.swift, Package.resolved, or package .gitignore; Xcode package-reference sections are unchanged, and workflow change only adds a Hermes test.
Cmux Swift Logging ✅ Passed The feature diff adds only intended CLI output and a #if DEBUG diagnostic using the existing shortened-session helper; no new release logging, ad hoc diagnostics, or secret exposure appears.
Cmux Swiftui State Layout ✅ Passed The base-to-HEAD Swift diff adds no SwiftUI/state/layout patterns, and all 47 changed Swift paths are non-SwiftUI; the rule's prohibited cases are not applicable.
Cmux Architecture Rethink ✅ Passed The shim lifecycle remains one MainActor TerminalSurface owner and generalizes the existing Claude path; new Hermes resolution is bounded, fail-closed, per-scan, and adds no sleeps, polling, locks,...
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed The PR adds no standalone window or close-shortcut code; the Swift auxiliary-window lint passed and checked 35 identifiers.
Cmux Source Artifacts ✅ Passed The 52 PR paths are source, tests, config, localization, or intentional scripts; no artifact directories or binary/log files were added, and test logs/databases are runtime temporary fixtures.
Cmux No Test Or Debug Seam In Production Source ✅ Passed The feature diff adds no test/debug seam in production Sources; the existing DEBUG loadHermesAgentEntriesForTesting member is unchanged, and new resolver APIs have production callers.
Cmux No Ambient Global State ✅ Passed The diff adds no file-scope API functions, mutable globals, or singleton state. New runtime state is instance-owned by VaultPersistedSessionResolver and constructable Hermes resolver types; static...
Title check ✅ Passed The title clearly summarizes the main change: adding first-class Hermes restore and lifecycle support.
Description check ✅ Passed The description clearly explains the scope, architecture, safety considerations, and extensive validation, although some template sections are omitted.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue-9520-hermes-first-class

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@cmuxTests/HermesFirstClassSupportTests.swift`:
- Around line 414-421: The fixture currently writes cwd twice, with rewriteCwds
overwriting per-row values. Update makeFixture and its call sites to pass each
row’s cwd into StateRow, insert it during fixture creation, and remove the
rewriteCwds helper and all calls to it so StateRow.cwd is the sole owner of the
value.
- Around line 437-456: Update runProcess so it reads the combined stdout and
stderr pipe before calling process.waitUntilExit(), preventing the child from
blocking when output exceeds the pipe buffer; retain the existing status and
decoded output return behavior.
- Line 215: Update the socketPath initialization in the test fixture to derive
its value from the per-run root directory instead of using the fixed /tmp path.
Keep the resulting path consistent with the config.yaml and consent allowlist
assertions so concurrent test runs remain isolated.

In
`@Packages/macOS/CMUXAgentLaunch/Tests/CMUXAgentLaunchTests/Vault/Providers/HermesAgent/HermesAgentIndexTests.swift`:
- Around line 70-86: Implement cwdFilter handling in
HermesAgentIndex.loadSessions so non-nil filters return matching sessions
instead of an empty result, using canonical-path comparison to support symlinked
repository paths. Apply this production-loader change for
Packages/macOS/CMUXAgentLaunch/Tests/CMUXAgentLaunchTests/Vault/Providers/HermesAgent/HermesAgentIndexTests.swift
lines 70-86 (direct repository-path filter) and lines 105-113 (canonicalized
symlink-path filter); both test sites require no direct changes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: faf94b9e-8ad0-436a-a788-8916e19d31b0

📥 Commits

Reviewing files that changed from the base of the PR and between 7dcb2bf and 0792812.

📒 Files selected for processing (3)
  • Packages/macOS/CMUXAgentLaunch/Tests/CMUXAgentLaunchTests/Vault/Providers/HermesAgent/HermesAgentIndexTests.swift
  • cmux.xcodeproj/project.pbxproj
  • cmuxTests/HermesFirstClassSupportTests.swift

Comment thread cmuxTests/HermesFirstClassSupportTests.swift Outdated
Comment thread cmuxTests/HermesFirstClassSupportTests.swift Outdated
Comment thread cmuxTests/HermesFirstClassSupportTests.swift

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@CLI/CMUXCLI`+AgentHookCatalog.swift:
- Line 177: Update the Hermes agent hook installation around the .pinned
dispatch and pinnedAgentHookSocketPath() so it requires CMUX_SOCKET_PATH or
CMUX_TAG before enabling pinned routing. Fail installation when neither variable
is available, or retain ambient dispatch until the installer explicitly supplies
one, ensuring Hermes callbacks cannot run without the intended CMUX socket.

In
`@Packages/macOS/CMUXAgentLaunch/Sources/CMUXAgentLaunch/HermesAgentHookConfig.swift`:
- Around line 371-374: Update isCmuxOwnedCommand to use boundary-aware matching
for the expected cmux-hermes-agent-hook-v2 and hooks hermes-agent command forms
instead of arbitrary substring checks. Include the cmux hooks feed --source
hermes-agent --event <value> form in the uninstall owner classification so feed
approvals are removed during cleanup.

In `@Sources/VaultPersistedSessionResolver.swift`:
- Around line 21-51: Refactor registerFreshProcesses so it reuses the scanner’s
existing derivation of VaultObservedAgentProcess, normalized cwd, and matched
registry registration instead of recomputing them. Update the scanner/resolver
interface to pass those derived values into registerFreshProcesses, or extract a
shared helper used by both paths; remove the duplicate Self.normalized logic and
preserve identical matching and uniqueness behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 680ab72e-709f-4dc5-9121-3b92d71a398f

📥 Commits

Reviewing files that changed from the base of the PR and between 0792812 and b32dfb7.

📒 Files selected for processing (14)
  • CLI/CMUXCLI+AgentHookCatalog.swift
  • CLI/CMUXCLI+AgentHookDefinitions.swift
  • Packages/macOS/CMUXAgentLaunch/Sources/CMUXAgentLaunch/HermesAgentHookConfig.swift
  • Packages/macOS/CMUXAgentLaunch/Sources/CMUXAgentLaunch/Vault/Providers/HermesAgent/HermesAgentIndex.swift
  • Packages/macOS/CMUXAgentLaunch/Sources/CMUXAgentLaunch/Vault/Providers/HermesAgent/HermesAgentStateDBResolver.swift
  • Sources/CmuxVaultAgentPersistedSessionStore.swift
  • Sources/CmuxVaultAgentRegistration+Hermes.swift
  • Sources/CmuxVaultAgentRegistry+ProcessMatching.swift
  • Sources/HermesAgentIndex.swift
  • Sources/SessionIndexRegisteredAgents.swift
  • Sources/VaultAgentProcessScanner.swift
  • Sources/VaultAgentRegistry.swift
  • Sources/VaultPersistedSessionResolver.swift
  • cmux.xcodeproj/project.pbxproj

Comment thread CLI/CMUXCLI+AgentHookCatalog.swift Outdated
Comment thread Sources/VaultPersistedSessionResolver.swift Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
CLI/CMUXCLI+AgentHookCatalog.swift (1)

162-180: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Restore pinned Hermes Agent dispatch.

AgentHookDef defaults dispatch to .ambient. The shared generator now selects routing only from this field. Hermes Agent omits the field.

The installed Hermes command therefore uses the ambient branch. When Hermes does not preserve CMUX_SURFACE_ID, that branch returns the no-op. The install also bypasses validateHookInstallDispatch(for:), so it can write hooks without a target socket.

Set Hermes Agent to .pinned with its stable marker. Keep coverage that rejects a no-target install before configuration mutation. This regresses the previously addressed Hermes routing issue.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@CLI/CMUXCLI`+AgentHookCatalog.swift around lines 162 - 180, Update the Hermes
Agent definition in AgentHookCatalog to set dispatch to .pinned and provide its
stable hook marker, rather than relying on AgentHookDef’s ambient default.
Ensure the Hermes installation path invokes validateHookInstallDispatch(for:)
and rejects missing target sockets before mutating configuration, preserving
coverage for that no-target failure.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@CLI/cmux.swift`:
- Around line 29559-29561: The unconditional validateHookInstallDispatch call in
installAgentHooks currently aborts runSetupHooks for the first missing pinned
socket and prevents later agents from being processed. Update the validation
flow so a pinned-socket failure is handled only for the affected agent without
propagating the error to the overall setup command, while preserving
installation and per-agent status processing for agents with .noSocket or .any
policies.

In `@Resources/bin/cmux-hermes-agent-wrapper`:
- Around line 195-203: Bound the synchronous hooks hermes-agent install
invocation in the wrapper so it cannot delay exec "$REAL_HERMES" indefinitely.
Inspect Resources/bin/cmux-claude-wrapper for the existing timeout pattern and
apply the same mechanism to both CMUX_SOCKET_PATH branches around the installer
command, preserving the existing non-blocking failure behavior.
- Around line 89-111: Align hermes_known_subcommand and
hermes_option_consumes_value with the documented Hermes CLI v0.19.0 surface:
remove acp, console, serve, desktop, gui, and lsp from the administrative
subcommand list, and remove -m, --model, --provider, -t, --toolsets, and -s from
value-consuming options while retaining supported entries. Add a reference to
the v0.19.0 CLI documentation above both functions for future auditing.

In `@Resources/Localizable.xcstrings`:
- Around line 40263-40279: Expand the localizations for
cli.hooks.error.pinnedTargetMissing to include translated stringUnit entries for
every locale already supported in Resources/Localizable.xcstrings, preserving
the existing English and Japanese translations and matching the catalog’s
established locale set and formatting.

In `@Sources/VaultPersistedSessionResolver.swift`:
- Around line 26-33: Update the persisted-store owner counting logic around
freshProcessCountByKey so matching processes are counted regardless of whether
explicitSessionID returns a value; retain explicit flags for that process’s
session-resolution behavior. Add a regression test covering one explicit-session
process, one bare Hermes process, and one active state.db row, asserting the
bare process fails closed.

In `@tests/test_hermes_wrapper_hooks.py`:
- Around line 168-178: Update the WrapperResult construction to guard the
real_env_log read just like cmux_env_log, returning an empty environment when
the log file is absent. Preserve normal read_environment behavior when
real_env_log exists so wrapper failures produce a result with captured stderr
instead of raising FileNotFoundError.

---

Outside diff comments:
In `@CLI/CMUXCLI`+AgentHookCatalog.swift:
- Around line 162-180: Update the Hermes Agent definition in AgentHookCatalog to
set dispatch to .pinned and provide its stable hook marker, rather than relying
on AgentHookDef’s ambient default. Ensure the Hermes installation path invokes
validateHookInstallDispatch(for:) and rejects missing target sockets before
mutating configuration, preserving coverage for that no-target failure.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: e5e5e52e-0330-4ce9-a568-d21391e30ca2

📥 Commits

Reviewing files that changed from the base of the PR and between b32dfb7 and 5d286ed.

📒 Files selected for processing (43)
  • .github/workflows/ci.yml
  • CLI/CMUXCLI+AgentHookCatalog.swift
  • CLI/CMUXCLI+AgentHookDefinitions.swift
  • CLI/CMUXCLI+ExecutableResolution.swift
  • CLI/CMUXCLI+HermesAgentHooks.swift
  • CLI/cmux.swift
  • Packages/macOS/CMUXAgentLaunch/Sources/CMUXAgentLaunch/HermesAgentHookCommandOwnership.swift
  • Packages/macOS/CMUXAgentLaunch/Sources/CMUXAgentLaunch/HermesAgentHookConfig.swift
  • Packages/macOS/CMUXAgentLaunch/Tests/CMUXAgentLaunchTests/HermesAgentHookConfigTests.swift
  • Packages/macOS/CmuxTerminal/Sources/CmuxTerminal/Runtime/TerminalSurfaceRuntimeFilesystem.swift
  • Packages/macOS/CmuxTerminal/Sources/CmuxTerminal/Spawn/TerminalSurface+AgentCommandShims.swift
  • Packages/macOS/CmuxTerminal/Sources/CmuxTerminal/Spawn/TerminalSurface+StartupEnvironment.swift
  • Packages/macOS/CmuxTerminal/Sources/CmuxTerminal/Spawn/TerminalSurfaceAgentCommandShimDefinition.swift
  • Packages/macOS/CmuxTerminal/Sources/CmuxTerminal/Surface/TerminalSurface+AgentCommandShimLifecycle.swift
  • Packages/macOS/CmuxTerminal/Sources/CmuxTerminal/Surface/TerminalSurface+ClaudeCommandShimLifecycle.swift
  • Packages/macOS/CmuxTerminal/Sources/CmuxTerminal/Surface/TerminalSurface+RuntimeLifecycle.swift
  • Packages/macOS/CmuxTerminal/Sources/CmuxTerminal/Surface/TerminalSurface+RuntimeSurfaceCreation.swift
  • Packages/macOS/CmuxTerminal/Sources/CmuxTerminal/Surface/TerminalSurface.swift
  • Packages/macOS/CmuxTerminal/Tests/CmuxTerminalTests/ManualAgentCommandShimInstaller.swift
  • Packages/macOS/CmuxTerminal/Tests/CmuxTerminalTests/TerminalSurfaceCloseConfirmationTests.swift
  • Packages/macOS/CmuxTerminal/Tests/CmuxTerminalTests/TerminalSurfaceCommandShimPermissionsTests.swift
  • Packages/macOS/CmuxTerminal/Tests/CmuxTerminalTests/TerminalSurfaceExplicitInputTests.swift
  • Packages/macOS/CmuxTerminal/Tests/CmuxTerminalTests/TerminalSurfaceFontSizeLineageTests.swift
  • Packages/macOS/CmuxTerminal/Tests/CmuxTerminalTests/TerminalSurfacePortalHostVacancyTests.swift
  • Packages/macOS/CmuxTerminal/Tests/CmuxTerminalTests/TerminalSurfaceRendererPresentationTests.swift
  • Packages/macOS/CmuxTerminal/Tests/CmuxTerminalTests/TerminalSurfaceRestoreSpawnSchedulerTests.swift
  • Packages/macOS/CmuxTerminal/Tests/CmuxTerminalTests/TerminalSurfaceTeardownCallbackLifetimeTests.swift
  • Packages/macOS/CmuxTerminalCore/Sources/CmuxTerminalCore/SurfaceValues/TerminalSurfaceAgentCommandShim.swift
  • Packages/macOS/CmuxTerminalCore/Sources/CmuxTerminalCore/SurfaceValues/TerminalSurfaceAgentCommandShimSet.swift
  • Packages/macOS/CmuxTerminalCore/Sources/CmuxTerminalCore/SurfaceValues/TerminalSurfaceClaudeCommandShim.swift
  • Packages/macOS/CmuxTerminalCore/Sources/CmuxTerminalCore/SurfaceValues/TerminalSurfaceCodexCommandShim.swift
  • Resources/Localizable.xcstrings
  • Resources/bin/cmux-hermes-agent-wrapper
  • Sources/AgentExecutableResolver.swift
  • Sources/TerminalSurfaceRuntimeWiring.swift
  • Sources/VaultAgentProcessScanner.swift
  • Sources/VaultPersistedSessionResolver.swift
  • cmux.xcodeproj/project.pbxproj
  • cmuxTests/AgentExecutableResolverTests.swift
  • cmuxTests/CLIGenericHookPersistenceTests.swift
  • cmuxTests/GhosttyTerminalStartupEnvironmentTests.swift
  • cmuxTests/HermesFirstClassSupportTests.swift
  • tests/test_hermes_wrapper_hooks.py
💤 Files with no reviewable changes (4)
  • Packages/macOS/CmuxTerminalCore/Sources/CmuxTerminalCore/SurfaceValues/TerminalSurfaceCodexCommandShim.swift
  • Packages/macOS/CmuxTerminal/Sources/CmuxTerminal/Surface/TerminalSurface+ClaudeCommandShimLifecycle.swift
  • Packages/macOS/CmuxTerminalCore/Sources/CmuxTerminalCore/SurfaceValues/TerminalSurfaceClaudeCommandShim.swift
  • Packages/macOS/CmuxTerminal/Sources/CmuxTerminal/Spawn/TerminalSurface+StartupEnvironment.swift

Comment thread CLI/cmux.swift Outdated
Comment thread Resources/bin/cmux-hermes-agent-wrapper Outdated
Comment thread Resources/bin/cmux-hermes-agent-wrapper Outdated
Comment thread Resources/Localizable.xcstrings
Comment thread Sources/VaultPersistedSessionResolver.swift Outdated
Comment thread tests/test_hermes_wrapper_hooks.py

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/test_hermes_wrapper_hooks.py`:
- Around line 67-68: Replace timing-based assertions in the installer-delay
regression tests with synchronization on a test-controlled installer signal and
a deadline-bounded poll of real_args_log or another Hermes-launch predicate.
Start the wrapper asynchronously, assert the launch signal rather than
elapsed_seconds, then release or clean up the blocked installer and await
completion. Update the related cases around installer_delay_seconds, timeout
handling, and the referenced test blocks consistently, without fixed sleeps or
latency-based correctness checks.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: d3e97702-0519-47ad-b936-3acf8659fd4b

📥 Commits

Reviewing files that changed from the base of the PR and between 5d286ed and 9f9872e.

📒 Files selected for processing (2)
  • cmuxTests/HermesFirstClassSupportTests.swift
  • tests/test_hermes_wrapper_hooks.py

Comment thread tests/test_hermes_wrapper_hooks.py Outdated
@cursor

cursor Bot commented Aug 6, 2026

Copy link
Copy Markdown

Bugbot is paused — on-demand spend limit reached

Bugbot uses usage-based billing for this team and has hit its on-demand spend limit.

A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue.

@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@austinywang

Copy link
Copy Markdown
Contributor Author

Addressing the outside-diff Hermes dispatch finding: Hermes intentionally remains .ambient, not .pinned. The per-surface wrapper installs one environment-driven hook definition, and Hermes v0.19.0 hook subprocesses inherit CMUX_SURFACE_ID, CMUX_WORKSPACE_ID, CMUX_SOCKET_PATH, and CMUX_BUNDLED_CLI_PATH; the tagged real-agent dogfood and test_hermes_wrapper_hooks.py verify that attribution. The generated hook fails closed to a no-op without a cmux surface. Pinning the persistent ~/.hermes/config.yaml command would instead create a last-writer-wins global socket and cross-route simultaneously running Hermes sessions across cmux instances/tags. The no-target ambient install is therefore safe and deliberate; the wrapper supplies the live target for every interactive launch.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@CLI/CMUXCLI`+HermesAgentHooks.swift:
- Around line 82-85: Update the createDirectory error handling in the
config-directory setup to throw a distinct user-facing CLIError for directory
creation failures, directing the user to check parent-directory permissions or
read-only filesystem conditions. Keep configDirectoryFileError unchanged for the
existing Lines 77-80 file-conflict path.

In
`@Packages/macOS/CmuxTerminal/Sources/CmuxTerminal/Spawn/TerminalSurface`+AgentCommandShims.swift:
- Around line 96-111: Disable pathname expansion around the PATH-splitting loop
in the generated shell shim, while preserving the intentional IFS-based word
splitting of ${PATH:-}. Update the shell state surrounding the `for cmux_entry
in ${PATH:-}` loop in the command shim generation flow, then restore the
previous globbing state afterward so subsequent command execution is unaffected.

In `@Resources/bin/cmux-hermes-agent-wrapper`:
- Around line 93-96: Update the timeout validation in the
CMUX_HERMES_AGENT_HOOK_INSTALL_TIMEOUT_SECONDS handling to reject zero and other
non-positive values, including decimal representations such as 0.0, while
preserving acceptance of valid positive timeouts and the existing best-effort
skip behavior.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 2b885252-e05d-45c2-a26e-2eb169921ffb

📥 Commits

Reviewing files that changed from the base of the PR and between 6bb1d7b and edce9f5.

📒 Files selected for processing (52)
  • .github/workflows/ci.yml
  • CLI/CMUXCLI+AgentHookCatalog.swift
  • CLI/CMUXCLI+AgentHookDefinitions.swift
  • CLI/CMUXCLI+ExecutableResolution.swift
  • CLI/CMUXCLI+HermesAgentHooks.swift
  • CLI/cmux.swift
  • Packages/macOS/CMUXAgentLaunch/Sources/CMUXAgentLaunch/HermesAgentHookCommandOwnership.swift
  • Packages/macOS/CMUXAgentLaunch/Sources/CMUXAgentLaunch/HermesAgentHookConfig.swift
  • Packages/macOS/CMUXAgentLaunch/Sources/CMUXAgentLaunch/Vault/Providers/HermesAgent/HermesAgentIndex.swift
  • Packages/macOS/CMUXAgentLaunch/Sources/CMUXAgentLaunch/Vault/Providers/HermesAgent/HermesAgentStateDBResolver.swift
  • Packages/macOS/CMUXAgentLaunch/Tests/CMUXAgentLaunchTests/HermesAgentHookConfigTests.swift
  • Packages/macOS/CMUXAgentLaunch/Tests/CMUXAgentLaunchTests/Vault/Providers/HermesAgent/HermesAgentIndexTests.swift
  • Packages/macOS/CmuxTerminal/Sources/CmuxTerminal/Runtime/TerminalSurfaceRuntimeFilesystem.swift
  • Packages/macOS/CmuxTerminal/Sources/CmuxTerminal/Spawn/TerminalSurface+AgentCommandShims.swift
  • Packages/macOS/CmuxTerminal/Sources/CmuxTerminal/Spawn/TerminalSurface+StartupEnvironment.swift
  • Packages/macOS/CmuxTerminal/Sources/CmuxTerminal/Spawn/TerminalSurfaceAgentCommandShimDefinition.swift
  • Packages/macOS/CmuxTerminal/Sources/CmuxTerminal/Surface/TerminalSurface+AgentCommandShimLifecycle.swift
  • Packages/macOS/CmuxTerminal/Sources/CmuxTerminal/Surface/TerminalSurface+ClaudeCommandShimLifecycle.swift
  • Packages/macOS/CmuxTerminal/Sources/CmuxTerminal/Surface/TerminalSurface+RuntimeLifecycle.swift
  • Packages/macOS/CmuxTerminal/Sources/CmuxTerminal/Surface/TerminalSurface+RuntimeSurfaceCreation.swift
  • Packages/macOS/CmuxTerminal/Sources/CmuxTerminal/Surface/TerminalSurface.swift
  • Packages/macOS/CmuxTerminal/Tests/CmuxTerminalTests/ManualAgentCommandShimInstaller.swift
  • Packages/macOS/CmuxTerminal/Tests/CmuxTerminalTests/TerminalSurfaceCloseConfirmationTests.swift
  • Packages/macOS/CmuxTerminal/Tests/CmuxTerminalTests/TerminalSurfaceCommandShimPermissionsTests.swift
  • Packages/macOS/CmuxTerminal/Tests/CmuxTerminalTests/TerminalSurfaceExplicitInputTests.swift
  • Packages/macOS/CmuxTerminal/Tests/CmuxTerminalTests/TerminalSurfaceFontSizeLineageTests.swift
  • Packages/macOS/CmuxTerminal/Tests/CmuxTerminalTests/TerminalSurfacePortalHostVacancyTests.swift
  • Packages/macOS/CmuxTerminal/Tests/CmuxTerminalTests/TerminalSurfaceRendererPresentationTests.swift
  • Packages/macOS/CmuxTerminal/Tests/CmuxTerminalTests/TerminalSurfaceRestoreSpawnSchedulerTests.swift
  • Packages/macOS/CmuxTerminal/Tests/CmuxTerminalTests/TerminalSurfaceTeardownCallbackLifetimeTests.swift
  • Packages/macOS/CmuxTerminalCore/Sources/CmuxTerminalCore/SurfaceValues/TerminalSurfaceAgentCommandShim.swift
  • Packages/macOS/CmuxTerminalCore/Sources/CmuxTerminalCore/SurfaceValues/TerminalSurfaceAgentCommandShimSet.swift
  • Packages/macOS/CmuxTerminalCore/Sources/CmuxTerminalCore/SurfaceValues/TerminalSurfaceClaudeCommandShim.swift
  • Packages/macOS/CmuxTerminalCore/Sources/CmuxTerminalCore/SurfaceValues/TerminalSurfaceCodexCommandShim.swift
  • Resources/Localizable.xcstrings
  • Resources/bin/cmux-hermes-agent-wrapper
  • Sources/AgentExecutableResolver.swift
  • Sources/CmuxVaultAgentPersistedSessionStore.swift
  • Sources/CmuxVaultAgentRegistration+Hermes.swift
  • Sources/CmuxVaultAgentRegistry+ProcessMatching.swift
  • Sources/HermesAgentIndex.swift
  • Sources/SessionIndexRegisteredAgents.swift
  • Sources/TerminalSurfaceRuntimeWiring.swift
  • Sources/VaultAgentProcessScanner.swift
  • Sources/VaultAgentRegistry.swift
  • Sources/VaultPersistedSessionResolver.swift
  • cmux.xcodeproj/project.pbxproj
  • cmuxTests/AgentExecutableResolverTests.swift
  • cmuxTests/CLIGenericHookPersistenceTests.swift
  • cmuxTests/GhosttyTerminalStartupEnvironmentTests.swift
  • cmuxTests/HermesFirstClassSupportTests.swift
  • tests/test_hermes_wrapper_hooks.py
💤 Files with no reviewable changes (4)
  • Packages/macOS/CmuxTerminalCore/Sources/CmuxTerminalCore/SurfaceValues/TerminalSurfaceClaudeCommandShim.swift
  • Packages/macOS/CmuxTerminal/Sources/CmuxTerminal/Spawn/TerminalSurface+StartupEnvironment.swift
  • Packages/macOS/CmuxTerminal/Sources/CmuxTerminal/Surface/TerminalSurface+ClaudeCommandShimLifecycle.swift
  • Packages/macOS/CmuxTerminalCore/Sources/CmuxTerminalCore/SurfaceValues/TerminalSurfaceCodexCommandShim.swift

Comment thread CLI/CMUXCLI+HermesAgentHooks.swift Outdated
Comment thread Resources/bin/cmux-hermes-agent-wrapper
Comment thread CLI/CMUXCLI+HermesAgentHooks.swift
@austinywang

Copy link
Copy Markdown
Contributor Author

Review closeout for 65e56c4f54:

  • Removed the SQLite busy timeout so a locked Hermes store fails closed immediately.
  • Kept persisted-store reads off the synchronous termination path: it now consumes SharedLiveAgentIndex cache state and fails closed on a cold cache.
  • Moved the persisted-session store and resolver into CMUXAgentLaunch, documented the public package surface, and added package-level behavior coverage.
  • Replaced the pinned-hook failure with generic, privacy-safe recovery copy.
  • Completed all four affected localization keys across all 20 supported locales with placeholder parity.

The remaining inline findings are also fixed: Hermes mkdir failures now distinguish permissions from a conflicting file, shim PATH splitting preserves literal glob characters and caller shell state, and zero installer timeouts cannot disable the deadline.

Warning triage:

  • Hermes CLI/TUI/Gateway sessions already traverse the same plugin event manager shell-hook surface. Adding a second Gateway bridge would duplicate callbacks and bypass the existing consent allowlist.
  • All public package symbols moved or introduced by this closeout have DocC comments. Inflating the branch-wide ratio with comments on private implementation functions would not improve the API contract.

Validation: tagged Debug build, cmux-unit build, 285 native arm64 CMUXAgentLaunch tests, the focused shim test, wrapper suite, project/test wiring and package-policy checks, and the 20-locale audit.

Comment thread cmuxTests/HermesFirstClassSupportTests.swift
Comment thread Sources/CmuxVaultAgentRegistration+Hermes.swift

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 0ff8b44. Configure here.

@austinywang

Copy link
Copy Markdown
Contributor Author

Verified on HEAD e5cdb71 with the tagged hermes-resume-final app: the actual Vault context-menu action for the top Hermes row resumed the existing durable session through the managed wrapper, restored the live transcript, and emitted a fresh Hermes session-start hook. The standalone Hermes wrapper regression harness also passes. Prior verification covered 41 Hermes CMUXAgentLaunch tests and two clean quit/relaunch autoresume cycles. No session-not-found marker appeared on the current build.

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.

First-class Hermes Agent support: restore, auto-resume, notifications, and full Codex feature parity

1 participant