Skip to content

fix(agent-status): sync HTTP-hook status to remote clients on headless servers - #11212

Open
ElNelyo wants to merge 2 commits into
stablyai:mainfrom
ElNelyo:ElNelyo/Agent-status-synchronisation
Open

ElNelyo wants to merge 2 commits into
stablyai:mainfrom
ElNelyo:ElNelyo/Agent-status-synchronisation

Conversation

@ElNelyo

@ElNelyo ElNelyo commented Jul 28, 2026 •

Copy link
Copy Markdown
Contributor

ELI5

When Orca runs as a background server (no app window) and you connect from a laptop or phone, agents like OpenCode could be actively working while every worktree card showed no activity dot — remote views looked idle. This teaches the server to pass those agents’ working / needs-your-attention signals through to paired desktop and mobile clients, so the usual yellow spinner and amber dot appear exactly as they do locally. No new buttons or screens; existing indicators simply fill in where they were stuck blank.

Summary

On a remote Orca server running in headless --serve mode (no renderer window), the orange agent-activity dots never reach the paired desktop client for HTTP-hook-only agents like OpenCode — even though the agent is actively working and last-status.json updates live. The desktop worktree-card dot reads agentStatusByPaneKey, which for remote environments is populated only from session.tabs snapshots; in headless those snapshots never carried hook-only agent status. This mirrors the gap #7970 closed for OSC 9999, but no equivalent existed for the HTTP-hook path OpenCode uses.

Fixes #11210.

Screenshots

Visual change: the working/idle activity dot (yellow spinner / amber "needs you") now appears on the remote worktree card and Agents feed for OpenCode (and other HTTP-hook-only agents) running on a headless --serve server, where it previously never appeared. No new UI element is introduced — existing indicators are now populated.

(Screenshot to be attached from a paired Mac client on request; this change was verified on the live --serve host via worktree ps --json showing state: "working" while session.tabs lacked tab.agentStatus.)

Testing

  • pnpm typecheck (node + cli + web, clean)
  • pnpm lint (full pipeline incl. oxlint, switch-exhaustiveness, reliability-gates, max-lines-ratchet, localization — clean)
  • pnpm test — ran the affected scopes (src/main, src/shared): 19,692 passed. The 8 failures are pre-existing environmental integration tests unrelated to this change (Azure/Bitbucket/Gitea hosted-review real HTTP API calls, system-SSH transport, updater network check, one output-frame fuzz timeout on a slow CI box).
  • pnpm build — not run in this dev environment (Electron production build); change is logic-only with no build-graph impact.
  • Added high-quality tests in src/main/runtime/headless-hook-agent-status.test.ts: hook working surfaces in session.tabs without graph-synced agentStatus; stale status (past 30 min) is not surfaced; touchMobileSessionSnapshotsForPty republishes after a hook change.

AI Review Report

Reviewed the three coordinated changes against the existing OSC 9999 / synthetic-title push paths they mirror.

Risks checked:

  • Hot-path cost: the onAgentStatus listener now resolves a pane→pty id and calls touchMobileSessionSnapshotsForPty on every hook event. OpenCode throttles streamed parts to 250 ms server-side, and touchMobileSessionSnapshotsForPty uses the existing coalesced fanout (not an immediate emit), so the cost is bounded and matches the OSC/synthetic paths. No new per-event allocation beyond a pane-key map lookup.
  • Double source of status: getFreshRetainedAgentStatusForMobileTab now consults both the OSC retained store and the agent-hook cache. The hook cache is only read when the OSC store has no entry for the pane (the existing if (!retained) guard), so OSC-emitting agents are unaffected; the hook fallback applies only to hook-only agents. This complements (does not duplicate) upstream's getHookAgentRowForPane, which carries identity (providerSession/agentType) for native chat rather than live working/idle state.
  • Title classification broadening: classifyAgentTitle now also returns 'agent' for OpenCode's native OC | title. Verified this only affects the two suppression filters inside orca-runtime.ts (used to decide whether to keep/suppress hook status), and that stale-clearing still works: when the shell reclaims the pane the title stops being OC | and reverts to 'neutral', clearing the dot.
  • Public surface: touchMobileSessionSnapshotsForPty was made private → public; it is now also called from the hook listener in index.ts. No other behavioral change.

Cross-platform compatibility (macOS / Linux / Windows): the change is server-side runtime/IPC logic with no platform-specific code paths — no keyboard shortcuts, shortcut labels, file-path assumptions, shell behavior, or Electron platform APIs are touched. The pane-key/pty resolution and the agent-hook cache are platform-agnostic. Verified identical behavior is expected on all three platforms; the headless --serve topology (the bug scenario) is Linux, while the desktop client may be macOS/Windows.

Security Audit

  • Input handling: no new untrusted-input surface. getPtyIdForPaneKey(paneKey) resolves an internal pane key to a pty id via the existing trusted registry; the hook cache (getAgentStatusSnapshotFn) is already the trusted convergence point for SSH-relayed and local hook payloads. The new code only reads from it.
  • Command execution: none introduced. No exec/spawn/shell paths added.
  • Path handling: none. No filesystem writes or path construction.
  • Auth: unchanged. The agent-hook HTTP server still authenticates via X-Orca-Agent-Hook-Token; the websocket E2EE handshake and runtime auth token are untouched. The change only re-routes already-authenticated in-process status data into an existing snapshot projection.
  • Secrets: none added or logged.
  • IPC: the change adds one main→runtime call (touchMobileSessionSnapshotsForPty) and reuses the existing onAgentStatus callback; no new renderer-facing IPC channel or new remotely-reachable RPC method is exposed. Remote/mobile clients receive status only through the existing authenticated session.tabs subscription.
  • Dependencies: none added or changed.

No follow-up security work required.

Notes

  • Server-side only; no desktop- or mobile-client change required — all paired desktop clients benefit automatically.
  • SSH / WSL relay: unaffected — the agent-hook cache is already the relay convergence point (ingestRemote).
  • Git / provider compatibility: N/A (no git or provider code touched).
  • Environment note (not part of the PR): the dev host's pnpm shim was broken during verification; sub-pnpm steps were run via a corepack-backed PATH shim. CI is unaffected.

…s servers (stablyai#11210)

OpenCode and other HTTP-hook-only agents never emit OSC 9999, so on a
`--serve` runtime with no renderer their working/idle status reached the
agent-hook cache (and worktree.ps) but never session.tabs — the only feed
the desktop worktree-card dot reads. Mirror the stablyai#7970 OSC fix for the hook
path:

- getFreshRetainedAgentStatusForMobileTab also consults the agent-hook
  cache by pane key, so headless hook-only agents surface in the
  session.tabs projection.
- classifyAgentTitle treats OpenCode's native `OC |` title as agent-owned
  so the hook working state isn't suppressed as a reclaimed shell.
- the onAgentStatus hook listener resolves the pane PTY and calls
  touchMobileSessionSnapshotsForPty to republish, matching the OSC 9999 /
  synthetic-title push paths.
@coderabbitai

coderabbitai Bot commented Jul 28, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The runtime now falls back to fresh agent-hook snapshots when retained OSC 9999 status is unavailable, including support for OpenCode native titles. The agent-hook listener resolves the pane’s PTY and triggers mobile session snapshot republishing for headless operation. New tests cover fresh hook status, stale-status filtering, and republishing after hook payload changes.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes match #11210 by projecting fresh hook status, classifying OpenCode's OC | title as agent-owned, and republishing snapshots on hook updates.
Out of Scope Changes check ✅ Passed No unrelated behavior is introduced; the code and tests stay focused on headless hook-status propagation to session.tabs.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Title check ✅ Passed The title clearly and concisely describes synchronizing HTTP-hook agent status to remote clients on headless servers.
Description check ✅ Passed The description clearly covers the change, rationale, linked issue, testing, security, and compatibility, but visual proof and checklist items remain incomplete.

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.

…s-synchronisation

# Conflicts:
#	src/main/runtime/orca-runtime.ts
@euclodius

Copy link
Copy Markdown

An adjacent OMP case helps distinguish the boundary of this headless snapshot projection fix.

Using Windows paired client 1.4.224 + Linux headless server 1.4.224 + fresh OMP 18.10.4, a pane split during an isolated sleep 45 turn removed the accepted working hook entry before the tool finished. The reporter subsequently sent tool/turn completion events and received HTTP 204, but the host cache stayed empty. The WSL connection was direct loopback WebSocket, not SSH.

This is upstream authority/state loss, unlike the original #11210 case where the host had correct hook state and only session.tabs projection was missing. Projection cannot restore an entry that the host has retired. The relevant current-turn reports/fixes are #24436/#26933 and #24732/#24196; client-side parking is a separate #15442/#15443 concern.

I have not tested this PR branch or reproduced the original OpenCode-only projection failure on 1.4.224. The maintainer's #11210 reply points to the unified store work in #19785. Please keep the host-retention and client-publication checks separate when determining which parts of this PR remain necessary; this OMP observation is not a request to expand a projection fix into a second lifecycle implementation.

Detailed isolated reproduction: comment on #24436.


AI assistance disclosure: prepared with an OpenAI coding assistant via OMP, using my reported behavior and local runtime diagnostics/source inspection. Reproduction and validation limits are stated above.

This branch has not been deployed

No deployments
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.

Remote (headless --serve) agent activity dots never reach desktop clients for HTTP-hook-only agents (OpenCode)

3 participants