Skip to content

Add built-in OpenCode sidebar status integration - #4797

Closed
austinywang wants to merge 30 commits into
mainfrom
issue-1425-add-built-in-opencode-sidebar-status
Closed

austinywang wants to merge 30 commits into
mainfrom
issue-1425-add-built-in-opencode-sidebar-status

Conversation

@austinywang

@austinywang austinywang commented May 26, 2026 •

Copy link
Copy Markdown
Contributor

Closes #1425

Summary

  • add a bundled opencode wrapper that auto-installs cmux OpenCode hooks when launched inside a cmux terminal with a live socket
  • extend the generated OpenCode session plugin to map OpenCode runtime events to sidebar status states: Running, Retrying, Needs input, Idle, and Error
  • wire the wrapper into shell integration and the app Copy CLI phase, and add wrapper behavior coverage

Verification

  • Not run locally per workspace instruction. CI should run the added wrapper regression tests.

HQ build command

CMUX_SKIP_ZIG_BUILD=1 ./scripts/reload.sh --tag issue-1425-add-built-in-opencode-sidebar-status --launch


View with Codesmith Autofix with Codesmith
Need help on this PR? Tag @codesmith with what you need. Autofix is disabled.


Note

Medium Risk
Touches agent hook CLI, session lifecycle, hibernation, and shell PATH wrapping; mistakes could mis-report status or affect when idle agents are killed, but scope is mostly OpenCode-specific with regression tests.

Overview
Adds built-in OpenCode sidebar status via a bundled opencode wrapper and new runtime-status / runtime-notification hook paths for OpenCode only.

Inside cmux terminals, bash/zsh shell integration prefers the bundled wrapper, which pings the socket, runs hooks opencode install, exports launch metadata and CMUX_OPENCODE_PID, then execs the real binary (passthrough outside cmux or for help/version-style invocations). Executable resolution skips the shim when locating the real opencode.

The OpenCode session plugin now maps session events to sidebar states (running, retrying, needs input, idle, error), sends permission/question/error notifications, dedupes lifecycle transitions with a bounded per-session cache, and only fires stop on genuine idle transitions. The CLI handler updates session store, set_status, lifecycle, and async notifications from those runtime hooks.

Agent hibernation gains an error lifecycle (not hibernated; panel aggregation prefers error over needs-input). Docs/README note OpenCode’s wrapper-based integration; regression tests cover the wrapper and plugin behavior.

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


Summary by cubic

Adds built-in OpenCode sidebar status with a bundled opencode wrapper and new runtime hooks that auto-install in cmux terminals. Implements #1425 by mapping OpenCode events to Running/Retrying/Needs input/Idle/Error with notifications, PID tracking, and safer idle/error lifecycle.

  • New Features

    • Bundled opencode wrapper: installs only when CMUX_SOCKET_PATH is live and CMUX_SURFACE_ID is set; no-op outside cmux and for help/version/completion; exports CMUX_OPENCODE_PID and CMUX_AGENT_LAUNCH_*; bash/zsh/fish wrapper install; resolveOpenCodeExecutable skips the shim; included in Copy CLI; docs updated.
    • Runtime hooks: runtime-status/runtime-notification set Running/Retrying/Needs input/Idle/Error with icon/color/priority and PID; permission/question/error notifications with dedup; status parsing prefers OpenCode fields with token fallback; localized “Retrying”.
    • Hibernation + CLI: bounded per-session lifecycle cache; preserves Needs input/Error on session.updated; stop only on true idle transitions; adds error lifecycle (not hibernated) and gives Error precedence in panel aggregation; cleanup on archive/delete; CLI adds set_agent_lifecycle ... error and set_agent_pid.
  • Bug Fixes

    • Preserve OpenCode runtime status updates and handle edge cases to avoid state loss.
    • Harden prompt/idle handling to prevent false idle stops and ensure explicit errors surface as Error.

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

Review in cubic

Summary by CodeRabbit

  • New Features

    • Added OpenCode runtime status and notification hook support for agent lifecycle tracking
    • OpenCode wrapper automatic installation in bash and zsh shells
    • Added error state support for agent lifecycle management
  • Documentation

    • Updated agent hooks documentation to clarify OpenCode integration and automatic plugin installation behavior
    • Refined session restore documentation regarding bundled wrapper integration
  • Tests

    • Added regression tests for OpenCode wrapper and session plugin functionality

@vercel

vercel Bot commented May 26, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cmux Ready Ready Preview, Comment Jun 18, 2026 8:19pm
cmux-staging Building Building Preview, Comment Jun 18, 2026 8:19pm

@coderabbitai

coderabbitai Bot commented May 26, 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

Adds a bundled opencode CLI wrapper that, when launched inside a live cmux socket, installs a JavaScript session plugin translating OpenCode lifecycle events into cmux sidebar status updates (running, retrying, needs-input, idle, error) and notifications. Extends the agent hibernation lifecycle with an .error state, routes runtime-status and runtime-notification hook actions through the CLI, and wires the wrapper into bash/zsh shell integration and the Xcode build.

Changes

OpenCode wrapper and status integration

Layer / File(s) Summary
Error lifecycle state contract
Sources/AgentHibernation/AgentHibernationLifecycleState.swift, Sources/TerminalController.swift, Sources/Workspace.swift
Adds .error lifecycle case with string parsing, updates set_agent_lifecycle help/docstring to advertise error, and inserts .error precedence check before .needsInput in workspace state resolution.
Runtime hook action definitions
CLI/CMUXCLI+AgentHookDefinitions.swift
Extends CMUXCLI.AgentHookAction with runtimeStatus and runtimeNotification and maps runtime-status/runtime-notification CLI subcommand keys in the routing table.
OpenCode executable detection and resolution
CLI/CMUXCLI+ExecutableResolution.swift, CLI/cmux.swift
Adds isCmuxOpenCodeWrapper(at:) marker-string detector and resolveOpenCodeExecutable(searchPath:) resolver that skips wrapper paths; removes the duplicate resolver previously inlined in cmux.swift.
JavaScript runtime hook helpers and session status normalization
CLI/cmux.swift
Adds JS helpers for cmux binary selection, socket-arg wrapping, synchronous execution, and runtime-hook payload construction; implements status-word tokenizer mapping raw fields to standardized descriptors; adds bounded per-session lifecycle tracking with eviction/pruning and duplicate-error gating; reworks setStatus and sendHook.
JavaScript CMUXSessionRestore event dispatch
CLI/cmux.swift
Updates CMUXSessionRestore event handling to dispatch session-start/end/stop, permission.asked, question.asked, and session.error hooks from computed descriptors and lifecycle records with one-time error gating and lifecycle cleanup on deletion.
Swift OpenCode runtime descriptor and hook handler
CLI/cmux.swift
Adds OpenCodeRuntimeStatusDescriptor and normalization helpers; fixes .error notification status mapping; extends runGenericAgentHook for opencode .runtimeStatus and .runtimeNotification; adds opencode to PID env-var mapping.
Shell integration and build packaging
Resources/shell-integration/cmux-bash-integration.bash, Resources/shell-integration/cmux-zsh-integration.zsh, cmux.xcodeproj/project.pbxproj
Declares _CMUX_OPENCODE_WRAPPER and calls _cmux_install_cli_wrapper opencode in bash/zsh; registers opencode script as a build file, file reference, and resource group entry in the Xcode Copy CLI build phase.
Regression tests, localization, and docs
cmuxTests/OpenCodeHookRegressionTests.swift, cmuxTests/AgentHibernationTests.swift, Resources/Localizable.xcstrings, README.md, docs/agent-hooks.md
Adds wrapper socket-live, passthrough, and Node-driven session plugin deduplication regression tests with UNIX socket helpers; extends lifecycle tests for .error parsing and panel aggregation precedence; adds agent.generic.status.retrying in 19 locales; updates README and docs for wrapper behavior and needsInput lifecycle state.

Sequence Diagram(s)

sequenceDiagram
  participant Shell as Shell (bash/zsh)
  participant OpenCodeWrapper as opencode wrapper (JS)
  participant cmuxRunner as cmux CLI
  participant StatusNorm as Status Normalizer (JS)
  participant SessionMap as Session Lifecycle Map (JS)
  participant SwiftHook as runGenericAgentHook (Swift)
  participant TermCtrl as TerminalController (Swift)

  Shell->>OpenCodeWrapper: exec opencode (inside cmux socket)
  OpenCodeWrapper->>cmuxRunner: cmux --socket ping
  OpenCodeWrapper->>cmuxRunner: cmux --socket hooks opencode install --yes
  OpenCodeWrapper->>OpenCodeWrapper: export CMUX_OPENCODE_PID / CMUX_AGENT_LAUNCH_KIND
  Note over OpenCodeWrapper: OpenCode session events arrive
  OpenCodeWrapper->>StatusNorm: tokenize session.status / permission.asked / etc.
  StatusNorm-->>SessionMap: upsert lifecycle record (running/retrying/needs-input/error/idle)
  SessionMap-->>OpenCodeWrapper: phase + one-time error gate
  OpenCodeWrapper->>cmuxRunner: hooks opencode runtime-status <descriptor> --pid <pid>
  cmuxRunner->>SwiftHook: .runtimeStatus action
  SwiftHook->>TermCtrl: set_agent_pid + set_status
  OpenCodeWrapper->>cmuxRunner: hooks opencode runtime-notification <type> ...
  cmuxRunner->>SwiftHook: .runtimeNotification action
  SwiftHook->>TermCtrl: notify_target_async
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • manaflow-ai/cmux#4851: Extends the same CMUXCLI.AgentHookAction enum and subcommandActions routing table in CLI/CMUXCLI+AgentHookDefinitions.swift with a different agent's hook actions.
  • manaflow-ai/cmux#4165: This PR's .error lifecycle addition, TerminalController set_agent_lifecycle wiring, and Workspace state resolution extend the agent hibernation lifecycle system introduced in that PR.

Poem

🐇 Hop hop, the OpenCode runs in the night,
A wrapper now watches and sets the badge right—
Running, Retrying, Needs Input, Error, Idle too,
Each session tracked neatly in a bounded-map queue.
The sidebar now glows with each bolt-filled sprite,
And the bunny sleeps soundly—the status is bright! ✨


Important

Pre-merge checks failed

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

❌ Failed checks (3 errors, 1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Cmux Cache Substitution Correctness ❌ Error SESSION_LIFECYCLE cache in cmux.swift (JavaScript OpenCode plugin) uses guards like if (record.phase === "idle") return; in sendStopHookOnIdleTransition() that prevent the stop hook from being se... Either: (1) Add an event-driven or freshness-checked cache mechanism so SESSION_LIFECYCLE stays synchronized with the Swift-side persistent state, or (2) Document why cache staleness is harmless here (e.g., state always re-syncs via anot...
Cmux Algorithmic Complexity ❌ Error SESSION_LIFECYCLE pruning in CLI/cmux.swift (lines 26833-26846) uses nested loops: while loop that rescans the full Map on each iteration via a for loop over all items. This violates the rule again... Add code comments documenting SESSION_LIFECYCLE_LIMIT bounds and worst-case performance (e.g., O(N²) with N<256, ~50k max ops), or refactor pruneLifecycleRecords to use O(N) LRU eviction (e.g., track eviction pointer or use priority queu...
Cmux Swift File And Package Boundaries ❌ Error PR adds 580 lines to CLI/cmux.swift (already 34,422 lines), exceeding the 250-line threshold for oversized files without extraction exception. The 504-line embedded OpenCode plugin and supporting h... Extract the openCodeSessionPluginSource JavaScript string and OpenCode-specific hook handlers (runtimeStatus/runtimeNotification cases) into a dedicated file or SwiftPM package target, reducing cmux.swift by 200+ lines and establishing c...
Docstring Coverage ⚠️ Warning Docstring coverage is 10.81% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive The PR description covers the key summary points, verification approach, and HQ build command, but is missing detailed sections for testing methodology and checklist items. Clarify how the changes were tested (e.g., which regression tests were run, manual verification steps) and confirm checklist completion (local testing, test additions, documentation updates, review requests).
✅ Passed checks (16 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Add built-in OpenCode sidebar status integration' directly and concisely describes the main change: adding a bundled OpenCode wrapper with sidebar status functionality.
Linked Issues check ✅ Passed All core requirements from #1425 are implemented: bundled wrapper, runtime-status/notification hooks, sidebar status states (Running/Retrying/Needs input/Idle/Error), automatic integration, and regression tests.
Out of Scope Changes check ✅ Passed All changes align with PR objectives: OpenCode wrapper, runtime hooks, hibernation error state, shell integration, and comprehensive test coverage for the feature scope.
Cmux Swift Actor Isolation ✅ Passed PR introduces no Swift 6 actor isolation mistakes; modified AgentHibernationLifecycleState already has Sendable, new private enums/structs are properly scoped, added internal functions are pure hel...
Cmux Swift Blocking Runtime ✅ Passed Production code introduces no blocking synchronization (semaphores, locks, sleeps, polling). Test code uses DispatchSemaphore only in test-only scaffolding (subprocess wait), which is allowed by th...
Cmux Expensive Synchronous Load ✅ Passed No new expensive synchronous loaders added to main actor or interactive paths; runtimeStatus/runtimeNotification hooks follow existing patterns using file I/O in socket handler context, appropriate...
Cmux No Hacky Sleeps ✅ Passed The PR's setTimeout in opencode-plugin.js implements a legitimate timeout abstraction (120s bounded deadline) for socket request/response coordination with proper event handlers, not a race conditi...
Cmux Swift Concurrency ✅ Passed All new OpenCode hook code is synchronous with no DispatchQueue, Combine, completion handlers, or fire-and-forget Tasks. Test-only DispatchQueue usage is allowed per rules.
Cmux Swift @Concurrent ✅ Passed No Swift violations found. PR adds enum cases (runtimeStatus, runtimeNotification) and synchronous helper functions (isCmuxOpenCodeWrapper, resolveOpenCodeExecutable, openCodeRuntimeStatusDescripto...
Cmux Swift Logging ✅ Passed PR adds no logging violations: all print() statements in CLI code are user-facing command output (allowed); existing NSLog calls in Workspace.swift are #if DEBUG-guarded (allowed); no new problemat...
Cmux User-Facing Error Privacy ✅ Passed All user-facing error messages in the PR comply with privacy rules: error messages use generic product terms, follow established agent-integration patterns, and expose no internal provider names, e...
Cmux Full Internationalization ✅ Passed PR adds OpenCode runtime hook support with proper i18n: new localization key "agent.generic.status.retrying" includes complete translations for all 19 locales (matching "agent.generic.status.runnin...
Cmux Swiftui State Layout ✅ Passed PR only touches existing legacy ObservableObject state incidentally with a one-line read-only aggregation change; no new @Published/@Observable/@StateObject/@EnvironmentObject patterns, GeometryRea...
Cmux Architecture Rethink ✅ Passed PR adds runtimeStatus tracking to session store (proper ownership via withLockedState), local JS lifecycle cache with memory bounds, and per-agent hook functions. No timing repairs, split UI lifecy...
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed PR adds OpenCode hook integration via CLI/lifecycle changes without introducing or modifying standalone cmux-owned windows; NSWindow references are pre-existing parameters.
Cmux Source Artifacts ✅ Passed All 16 changed files are legitimate source artifacts: Swift code, tests, docs, localization, shell integration scripts, Xcode config, and bundled product resources (wrapper and plugin). No local to...
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue-1425-add-built-in-opencode-sidebar-status

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 and usage tips.

Comment thread CLI/cmux.swift
Comment thread CLI/cmux.swift Outdated
Comment thread CLI/cmux.swift
@greptile-apps

greptile-apps Bot commented May 26, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

Adds a bundled cmux-opencode-wrapper script and new runtime-status/runtime-notification CLI subcommands that map OpenCode session lifecycle events (Running, Retrying, Needs input, Idle, Error) to cmux sidebar states, with OS notifications for permissions, questions, and errors. All three previously-flagged issues (raw error forwarding, wall-clock deduplication, duplicate PBXproj UUIDs) are confirmed resolved.

  • Wrapper (Resources/bin/cmux-opencode-wrapper): auto-installs hooks via hooks opencode install --yes only when a live cmux socket is detected; passes through for help/version/completion invocations; exports CMUX_OPENCODE_PID and CMUX_AGENT_LAUNCH_* metadata; bash/zsh/fish PATH shims added; isCmuxOpenCodeWrapper sentinel skip prevents the shim from being exec'd as the real binary.
  • JS plugin changes: replaces wall-clock deduplication with a bounded per-session lifecycle cache (SESSION_LIFECYCLE, max 128 entries); descriptorForOpenCodeStatus parses structured status objects by checking typed fields first before falling back to full token scan; idle/error/needs-input phases block false stop hooks; sendHook now returns a boolean for retry-on-failure.
  • Swift changes: new .error lifecycle state (not hibernated; beats .needsInput in panel aggregation); setAgentPID and updated setAgentLifecycle CLI commands; openCodeRuntimeStatusDescriptor and openCodeRuntimeNotificationSummary helpers use localized strings; agent.generic.status.retrying added with all 19 locale translations.

Confidence Score: 5/5

Safe to merge — the three blocking issues from earlier review rounds are all confirmed resolved, and the new lifecycle state machine, wrapper passthrough logic, and panel aggregation ordering are correct.

The JS lifecycle cache uses bounded insertion-order eviction with a proper fallback for fully-pinned maps. The Swift runtimeStatus/runtimeNotification handlers follow existing patterns, localized strings are complete for every new key actually introduced by the PR, and the isCmuxOpenCodeWrapper inode-safe sentinel correctly prevents the shim from shadowing the real binary. Regression tests exercise the wrapper in both passthrough and hook-install paths using real processes and UNIX sockets.

No files require special attention.

Important Files Changed

Filename Overview
CLI/cmux.swift Adds ~590 lines: JS plugin lifecycle state machine, Swift runtimeStatus/runtimeNotification handlers, status descriptor helpers with localized strings, setAgentPID CLI command, and error lifecycle routing. All three previously-flagged issues resolved.
Resources/bin/cmux-opencode-wrapper New 157-line bash wrapper that conditionally installs cmux hooks, exports launch metadata, and execs the real opencode binary; passthrough logic for help/version/completion is correct, inode-based self-skip is sound.
Sources/AgentHibernation/AgentHibernationLifecycleState.swift Adds .error case to the lifecycle enum with correct Codable decoding and CLI parsing; error correctly excluded from hibernation candidates.
Sources/Workspace.swift Panel aggregation now gives .error higher precedence than .needsInput (after .running), consistent with the new lifecycle semantics.
Sources/TerminalController.swift Adds setAgentPID and updated setAgentLifecycle CLI commands; help text updated for error lifecycle value; logic is consistent with existing command patterns.
Resources/Localizable.xcstrings Adds agent.generic.status.retrying with full translations across all 19 supported locales; the 9 notification-status keys used in Swift pre-existed with en+ja only and are not worsened by this PR.
CLI/CMUXCLI+ExecutableResolution.swift Moves resolveOpenCodeExecutable here with a skip predicate that calls isCmuxOpenCodeWrapper to prevent the shim from being resolved as the real binary; sentinel detection is correct.
cmuxTests/OpenCodeHookRegressionTests.swift Adds wrapper install, passthrough, and lifecycle deduplication regression tests; test harness uses real processes and UNIX sockets for integration fidelity.
CLI/CMUXCLI+AgentHookDefinitions.swift Adds runtimeStatus and runtimeNotification to AgentHookAction enum and subcommandActions dispatch table; straightforward extension.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant OC as OpenCode process
    participant JS as Session plugin (JS)
    participant CMX as cmux CLI
    participant SW as cmux Swift (TerminalController)
    participant SB as Sidebar / Panel

    Note over OC,SB: Startup (inside cmux terminal)
    OC->>CMX: wrapper: ping (socket liveness)
    CMX-->>OC: ok
    OC->>CMX: hooks opencode install --yes
    OC->>OC: "export CMUX_OPENCODE_PID=$$"

    Note over OC,SB: Runtime events
    OC->>JS: session.created
    JS->>CMX: hooks opencode session-start
    CMX->>SW: .sessionStart handler
    SW->>SB: upsert session, set running status

    OC->>JS: permission.asked
    JS->>CMX: hooks opencode runtime-status needs-input --pid
    CMX->>SW: .runtimeStatus(needs-input)
    SW->>SB: set_agent_lifecycle needsInput + set_status
    JS->>CMX: hooks opencode runtime-notification permission
    CMX->>SW: .runtimeNotification(permission)
    SW->>SB: notify_target_async

    OC->>JS: session.status idle / session.idle
    JS->>CMX: hooks opencode stop
    CMX->>SW: .stop handler
    SW->>SB: setIdleStatus

    OC->>JS: session.error
    JS->>CMX: hooks opencode runtime-status error --pid
    CMX->>SW: .runtimeStatus(error)
    SW->>SB: set_agent_lifecycle error + set_status
    JS->>CMX: hooks opencode runtime-notification error
    CMX->>SW: .runtimeNotification(error)
    SW->>SB: notify_target_async
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant OC as OpenCode process
    participant JS as Session plugin (JS)
    participant CMX as cmux CLI
    participant SW as cmux Swift (TerminalController)
    participant SB as Sidebar / Panel

    Note over OC,SB: Startup (inside cmux terminal)
    OC->>CMX: wrapper: ping (socket liveness)
    CMX-->>OC: ok
    OC->>CMX: hooks opencode install --yes
    OC->>OC: "export CMUX_OPENCODE_PID=$$"

    Note over OC,SB: Runtime events
    OC->>JS: session.created
    JS->>CMX: hooks opencode session-start
    CMX->>SW: .sessionStart handler
    SW->>SB: upsert session, set running status

    OC->>JS: permission.asked
    JS->>CMX: hooks opencode runtime-status needs-input --pid
    CMX->>SW: .runtimeStatus(needs-input)
    SW->>SB: set_agent_lifecycle needsInput + set_status
    JS->>CMX: hooks opencode runtime-notification permission
    CMX->>SW: .runtimeNotification(permission)
    SW->>SB: notify_target_async

    OC->>JS: session.status idle / session.idle
    JS->>CMX: hooks opencode stop
    CMX->>SW: .stop handler
    SW->>SB: setIdleStatus

    OC->>JS: session.error
    JS->>CMX: hooks opencode runtime-status error --pid
    CMX->>SW: .runtimeStatus(error)
    SW->>SB: set_agent_lifecycle error + set_status
    JS->>CMX: hooks opencode runtime-notification error
    CMX->>SW: .runtimeNotification(error)
    SW->>SB: notify_target_async
Loading

Reviews (18): Last reviewed commit: "fix: address opencode hook review feedba..." | Re-trigger Greptile

Comment thread CLI/cmux.swift Outdated
Comment thread CLI/cmux.swift Outdated
Comment thread CLI/cmux.swift Outdated
Comment thread CLI/cmux.swift Outdated
Comment thread CLI/cmux.swift
Comment thread CLI/cmux.swift Outdated
Comment thread CLI/cmux.swift Outdated
Comment thread CLI/cmux.swift Outdated
@austinywang

Copy link
Copy Markdown
Contributor Author

Addressed the Greptile follow-up on the latest head: SESSION_LIFECYCLE is now bounded as an LRU cache with a production default limit, OpenCode error status uses an explicit error hibernation lifecycle instead of needsInput, and the behavioral regression tests cover parsing/aggregation plus runtime lifecycle eviction.

Comment thread CLI/cmux.swift Outdated
Comment thread CLI/cmux.swift
Comment thread Resources/bin/cmux-opencode-wrapper
Comment thread CLI/cmux.swift
Comment thread CLI/cmux.swift
Comment thread CLI/cmux.swift Outdated
coderabbitai[bot]
coderabbitai Bot previously requested changes Jun 6, 2026

@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: 5

🤖 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/OpenCodeHookRegressionTests.swift`:
- Around line 70-74: The socket path created in
OpenCodeHookRegressionTests.swift is too long (socketURL under a UUID-named temp
directory) and can be rejected by createUnixSocket; change the socketURL to use
a short path (e.g., place the socket directly in the system temp directory or
use a fixed short filename under root with a truncated UUID) so the full UNIX
socket path stays well under system limits; update the variable socketURL
accordingly (while keeping root, binDir, and logURL usage intact) and ensure
uniqueness of the socket name to avoid collisions when tests run concurrently.

In `@docs/agent-hooks.md`:
- Line 5: Update the lifecycle list in the agent-hooks documentation to include
the new "error" runtime state alongside the existing "running", "idle",
"needsInput", and "unknown" lifecycle values so the docs match the runtime
notifications; find the lifecycle enumeration or list wording in the agent-hooks
content (the paragraph that currently lists running, idle, needsInput, unknown)
and add "error" in the same format and ordering used for the other states.

In `@README.md`:
- Around line 271-272: Update the sentence that currently reads "Claude Code and
OpenCode are handled by bundled wrappers inside cmux terminals" to state that
the Claude wrapper is only enabled via Settings (per docs/agent-hooks.md) rather
than unconditionally; mention "Claude wrapper" and "cmux terminals" and
reference the Settings toggle so the README aligns with the hook documentation
and avoids implying unconditional handling.

In `@Resources/bin/opencode`:
- Around line 15-16: The script currently validates resolved executables with [[
-x "$candidate" ]] which allows directories named like the executables to pass;
change these checks to require a regular file and executable with [[ -f
"$candidate" && -x "$candidate" ]] for the candidate resolution around the
opencode/cmux checks (the tests using the candidate variable and the if [[
"$candidate" -ef "$0" ]] logic), and update the other similar checks referenced
(the other occurrences at the next candidate-resolution blocks) so all resolved
candidates are validated as regular executable files rather than just executable
paths.

In `@Sources/AgentHibernation/AgentHibernationLifecycleState.swift`:
- Line 8: Add a brief doc comment above the new enum case to clarify its
purpose: locate the AgentHibernationLifecycleState enum and add a /// comment
for the case error describing when it is used (for example: "/// Agent session
encountered an unrecoverable error and cannot continue hibernation lifecycle").
Ensure the comment is concise and mentions any relevant conditions or expected
behavior so other domains consuming AgentHibernationLifecycleState understand
the semantics of .error.
🪄 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

Run ID: cabacc3c-a82f-4f02-b723-f0ecdc037027

📥 Commits

Reviewing files that changed from the base of the PR and between 97cf213 and 753054e.

📒 Files selected for processing (15)
  • CLI/CMUXCLI+AgentHookDefinitions.swift
  • CLI/CMUXCLI+ExecutableResolution.swift
  • CLI/cmux.swift
  • README.md
  • Resources/Localizable.xcstrings
  • Resources/bin/opencode
  • Resources/shell-integration/cmux-bash-integration.bash
  • Resources/shell-integration/cmux-zsh-integration.zsh
  • Sources/AgentHibernation/AgentHibernationLifecycleState.swift
  • Sources/TerminalController.swift
  • Sources/Workspace.swift
  • cmux.xcodeproj/project.pbxproj
  • cmuxTests/AgentHibernationTests.swift
  • cmuxTests/OpenCodeHookRegressionTests.swift
  • docs/agent-hooks.md

Comment thread cmuxTests/OpenCodeHookRegressionTests.swift Outdated
Comment thread docs/agent-hooks.md
Comment thread README.md Outdated
Comment thread Resources/bin/opencode Outdated
Comment thread Sources/AgentHibernation/AgentHibernationLifecycleState.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: 1

Caution

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

⚠️ Outside diff range comments (2)
Resources/bin/opencode (2)

134-134: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Avoid exposing PATH in the wrapper error text.

This is production command output, so naming the PATH environment variable leaks implementation detail. Prefer generic copy like “Error: unable to locate the OpenCode executable.”

Suggested fix
-REAL_OPENCODE="$(find_real_opencode)" || { echo "Error: opencode not found in PATH" >&2; exit 127; }
+REAL_OPENCODE="$(find_real_opencode)" || { echo "Error: unable to locate the OpenCode executable" >&2; exit 127; }

As per coding guidelines: user-facing errors and command output must not expose environment variables.

🤖 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 `@Resources/bin/opencode` at line 134, The error message printed when
REAL_OPENCODE fails uses "opencode not found in PATH", which exposes an
environment variable; change the message produced in the failure branch of the
REAL_OPENCODE assignment (the find_real_opencode invocation) to a generic
user-facing string like "Error: unable to locate the OpenCode executable" or
similar that omits any mention of PATH or environment variables so the wrapper
output does not leak implementation details.

Source: Coding guidelines


49-56: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Honor the relay socket contract in remote cmux terminals.

cmux_socket_available() hard-requires CMUX_SOCKET_PATH to be a local UNIX socket via -S, but cmux remote shells intentionally expose the relay endpoint as 127.0.0.1:<relayPort>. Those sessions will never pass this guard, so the wrapper silently skips OpenCode hook installation even though cmux --socket … ping is already the real liveness check.

Suggested fix
 cmux_socket_available() {
-    local socket="${CMUX_SOCKET_PATH:-}"
-    [[ -n "$socket" && -S "$socket" ]] || return 1
+    local socket="${CMUX_SOCKET_PATH:-${CMUX_SOCKET:-}}"
+    [[ -n "$socket" ]] || return 1
 
     local cmux_bin
     cmux_bin="$(resolve_cmux_bin)" || return 1
     CMUXTERM_CLI_RESPONSE_TIMEOUT_SEC=0.75 \
         "$cmux_bin" --socket "$socket" ping >/dev/null 2>&1
 }

Based on learnings: remote shells intentionally export CMUX_SOCKET_PATH/CMUX_SOCKET as 127.0.0.1:<relayPort>, so wrapper liveness checks need to honor that relay contract.

Also applies to: 141-147

🤖 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 `@Resources/bin/opencode` around lines 49 - 56, cmux_socket_available()
currently enforces CMUX_SOCKET_PATH must be a local UNIX socket (-S), skipping
remote relay endpoints like 127.0.0.1:<port>; update the function to accept
either a UNIX socket or a host:port relay string (honor CMUX_SOCKET_PATH /
CMUX_SOCKET), detect whether the value is a socket path or contains ":"/is an
IP:port, and in both cases call resolve_cmux_bin then run
CMUXTERM_CLI_RESPONSE_TIMEOUT_SEC=0.75 "$cmux_bin" --socket "$socket" ping
>/dev/null 2>&1 to determine liveness; also apply the same change to the other
similar block (lines 141-147) so both checks consistently support relay
endpoints.

Source: Learnings

🤖 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 25598-25619: pruneLifecycleRecords can fail to reduce size if all
entries are un-evictable (phase "needs-input"/"error"), letting
SESSION_LIFECYCLE grow past SESSION_LIFECYCLE_LIMIT; change
pruneLifecycleRecords so that if the first pass finds no evictable entry it
falls back to forcibly removing the oldest entries until size <=
SESSION_LIFECYCLE_LIMIT (i.e., delete the oldest key(s) from SESSION_LIFECYCLE
unconditionally), keeping the existing canEvictLifecycleRecord check as the
preferred path but guaranteeing bounded size; update rememberLifecycleRecord to
rely on this revised pruneBehavior.

---

Outside diff comments:
In `@Resources/bin/opencode`:
- Line 134: The error message printed when REAL_OPENCODE fails uses "opencode
not found in PATH", which exposes an environment variable; change the message
produced in the failure branch of the REAL_OPENCODE assignment (the
find_real_opencode invocation) to a generic user-facing string like "Error:
unable to locate the OpenCode executable" or similar that omits any mention of
PATH or environment variables so the wrapper output does not leak implementation
details.
- Around line 49-56: cmux_socket_available() currently enforces CMUX_SOCKET_PATH
must be a local UNIX socket (-S), skipping remote relay endpoints like
127.0.0.1:<port>; update the function to accept either a UNIX socket or a
host:port relay string (honor CMUX_SOCKET_PATH / CMUX_SOCKET), detect whether
the value is a socket path or contains ":"/is an IP:port, and in both cases call
resolve_cmux_bin then run CMUXTERM_CLI_RESPONSE_TIMEOUT_SEC=0.75 "$cmux_bin"
--socket "$socket" ping >/dev/null 2>&1 to determine liveness; also apply the
same change to the other similar block (lines 141-147) so both checks
consistently support relay endpoints.
🪄 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

Run ID: 31259405-59fc-4794-9ab2-0438307e4956

📥 Commits

Reviewing files that changed from the base of the PR and between 753054e and a8a2411.

📒 Files selected for processing (6)
  • CLI/cmux.swift
  • README.md
  • Resources/bin/opencode
  • Sources/AgentHibernation/AgentHibernationLifecycleState.swift
  • cmuxTests/OpenCodeHookRegressionTests.swift
  • docs/agent-hooks.md

Comment thread CLI/cmux.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 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 57bf740. Configure here.

Comment thread CLI/cmux.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: 1

Caution

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

⚠️ Outside diff range comments (1)
cmuxTests/OpenCodeHookRegressionTests.swift (1)

400-413: ⚠️ Potential issue | 🟠 Major

Prevent test job hangs in runProcess by bounding stdout/stderr reads

cmuxTests/OpenCodeHookRegressionTests.swift currently times out only via process.terminate(), but it still unconditionally blocks on stdoutPipe.fileHandleForReading.readDataToEndOfFile() / stderrPipe... afterward. readDataToEndOfFile() waits for EOF, and if the child (or any descendant) keeps stdout/stderr pipe FDs open after SIGTERM, those reads can hang the entire CI job.

Drain stdout/stderr concurrently while the process runs, and on timeout escalate to a hard kill (SIGKILL) before finalizing captured output; ensure pipe readers complete with a bounded grace even when EOF never arrives.

🤖 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 `@cmuxTests/OpenCodeHookRegressionTests.swift` around lines 400 - 413, The
runProcess implementation blocks on
stdoutPipe.fileHandleForReading.readDataToEndOfFile() and
stderrPipe.fileHandleForReading.readDataToEndOfFile(), which can hang after a
terminate(); change it to drain stdout/stderr concurrently into buffers using
non-blocking readers (e.g., set readabilityHandler on
stdoutPipe.fileHandleForReading and stderrPipe.fileHandleForReading or use
readInBackgroundAndNotify) that append data into local Data/Strings while the
process runs, keep the existing DispatchSemaphore/process.waitUntilExit for
normal termination, and on timeout call process.terminate(), wait a short
bounded grace, then escalate to process.kill() (SIGKILL) if still running; after
escalate close the pipe file handles to force EOF and wait for the reader
callbacks to finish before creating the ProcessRunResult from the accumulated
buffers instead of calling readDataToEndOfFile().
🤖 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 25646-25653: The function markSessionUpdated currently prevents
recovery by returning false when record.phase is "needs-input" or "error";
remove that gating so a non-idle update can flip the lifecycle back to "active"
and clear errorNotified (i.e., delete the if (record.phase === "needs-input" ||
record.phase === "error") return false; check in markSessionUpdated and the
equivalent checks around the session-start emission at the other occurrence),
ensuring markSessionUpdated sets record.phase = "active" and
record.errorNotified = false for existing records so later prompts/retries
re-emit session-start and clear stale state.

---

Outside diff comments:
In `@cmuxTests/OpenCodeHookRegressionTests.swift`:
- Around line 400-413: The runProcess implementation blocks on
stdoutPipe.fileHandleForReading.readDataToEndOfFile() and
stderrPipe.fileHandleForReading.readDataToEndOfFile(), which can hang after a
terminate(); change it to drain stdout/stderr concurrently into buffers using
non-blocking readers (e.g., set readabilityHandler on
stdoutPipe.fileHandleForReading and stderrPipe.fileHandleForReading or use
readInBackgroundAndNotify) that append data into local Data/Strings while the
process runs, keep the existing DispatchSemaphore/process.waitUntilExit for
normal termination, and on timeout call process.terminate(), wait a short
bounded grace, then escalate to process.kill() (SIGKILL) if still running; after
escalate close the pipe file handles to force EOF and wait for the reader
callbacks to finish before creating the ProcessRunResult from the accumulated
buffers instead of calling readDataToEndOfFile().
🪄 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

Run ID: 29b8125b-87b8-4340-b66f-617536fccf96

📥 Commits

Reviewing files that changed from the base of the PR and between 57bf740 and ac9a1a5.

📒 Files selected for processing (2)
  • CLI/cmux.swift
  • cmuxTests/OpenCodeHookRegressionTests.swift

Comment thread CLI/cmux.swift
…in-opencode-sidebar-status

# Conflicts:
#	CLI/CMUXCLI+ExecutableResolution.swift
#	Resources/shell-integration/cmux-bash-integration.bash
#	Resources/shell-integration/cmux-zsh-integration.zsh
#	Sources/TerminalController.swift
Comment thread cmux.xcodeproj/project.pbxproj Outdated
@austinywang
austinywang dismissed coderabbitai[bot]’s stale review June 14, 2026 20:44

Stale CodeRabbit review from 2026-06-06. All listed findings were fixed and current-head review/checks supersede it.

@lawrencecchen lawrencecchen added the stale-revisit Closed after 30+ days without activity; preserved for possible revisit or reopening. label Sep 23, 2026
@github-project-automation github-project-automation Bot moved this from Todo to Done in cmux backlog Sep 23, 2026

This branch was successfully deployed

1 active deployment
Preview – cmux — e6fadb42 Deployed Jun 18, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

stale-revisit Closed after 30+ days without activity; preserved for possible revisit or reopening.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add built-in OpenCode sidebar status integration

3 participants