Skip to content

fix: bind omp lifecycle reports to the pane's agent process - #3026

Closed
caner-akca wants to merge 1 commit into
herdrdev:masterfrom
caner-akca:issue/2851-omp-reporter-lease
Closed

fix: bind omp lifecycle reports to the pane's agent process#3026
caner-akca wants to merge 1 commit into
herdrdev:masterfrom
caner-akca:issue/2851-omp-reporter-lease

Conversation

@caner-akca

Copy link
Copy Markdown

Fixes the three defects reported in #2851.

The takeover

The Oh My Pi extension enables itself from the inherited pane environment, so a nested OMP session started inside a pane reported as that pane's agent. Its session claim replaced the live one, authority re-anchored to the child, and when the child finished the pane read idle while its own agent was still mid-turn. agent wait returned that idle, and the parent's later reports were then rejected as stale, so polling never recovered.

Reports carry no trustworthy owner, so the server now takes one from the kernel. src/ipc.rs reads the connected peer's process id — LOCAL_PEERPID on macOS, SO_PEERCRED on Linux, GetNamedPipeClientProcessId on Windows — and src/api/server.rs stamps it onto pane.report_agent / pane.report_agent_session. The field is #[serde(skip)] #[schemars(skip)], following the existing owner precedent, so it can never come from a payload and the JSON schema is unchanged.

The peer id must be read while the connection is fresh: Darwin invalidates it once the peer closes, and lifecycle reporters open one short-lived connection per report.

TerminalState stores that id on HookAuthority. A report for a leased source from any other process is refused while that authority is live. Keeping the lease on the authority record means it is released exactly when the authority is, so a restarted agent is never gated by its predecessor's process id.

Only herdr:omp is leased. Its extension runs inside the OMP process and reuses it for every report, which is what makes process identity meaningful. Shell-asset integrations report from a new process each time and keep the previous first-writer behavior.

Identity decides alone — the session ref is deliberately not consulted, because a nested reporter can read and replay the pane's published ref, which would itself be a takeover route.

This is an accidental-cross-talk boundary, not a security boundary: process ids are reusable and Windows' client id can be misreported. Herdr's socket is same-user and mode 0600, so this guards against unrelated processes, not an attacker already running as the user.

The wait

Standalone agent wait remembered a status seen in an event and pasted it over a freshly probed snapshot, returning a current state_change_seq with a stale status — a result the caller had no field left to detect. The substitution is removed; the wait still wakes on every status event but reports only what a live probe observed. agent prompt --wait is unaffected; it already ran this way.

The hook

Every non-retryable agent_end scheduled idle, including intermediate loop ends. Upstream marks an end that has already queued a continuation with willContinue, so those now return early. The field is optional, so === true degrades to existing behavior on older builds. OMP_INTEGRATION_VERSION 8 -> 9.

Testing

  • just ci passes on this base. Two live_handoff tests fail in my environment for want of an agent binary on PATH; I confirmed they fail identically on a pristine checkout of the same base.
  • New coverage: nine TerminalState unit tests for the lease (foreign reporter refused through session claims, bare state reports, replayed session refs; lease released on every authority teardown and on agent change; non-leased sources keep first-writer replacement), a CLI test that a transient blip does not complete a wait, and a bun test that willContinue suppresses idle while a real terminal end still settles the pane.
  • Verified against the reported scenario end to end: a nested session reporting startup plus idle against a live parent no longer ends the parent's wait. I also confirmed the guard is what does the work by temporarily disabling the leased-source predicate and watching the failure return.

Scope

This closes the nested-session takeover and the two wait/hook defects. It does not attempt general reporter authentication for unrelated processes beyond the lease described above.

@kangal-bot kangal-bot added the ai-review Trigger automated AI reviews for pull requests admitted by the PR gate label Aug 20, 2026
@coderabbitai

coderabbitai Bot commented Aug 20, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: fc31194e-82d6-4469-8b7c-13f09e03fa66

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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.

@caner-akca
caner-akca marked this pull request as ready for review August 20, 2026 10:02
@greptile-apps

greptile-apps Bot commented Aug 20, 2026

Copy link
Copy Markdown

Greptile Summary

The PR binds Oh My Pi lifecycle authority to the reporting process, makes standalone agent waits return freshly probed state, and avoids publishing idle for turns with scheduled continuations.

  • Reads peer process identity from Unix sockets or Windows named pipes and propagates it through lifecycle reports.
  • Leases live OMP hook authority to its reporter while preserving existing behavior for other integrations.
  • Treats status events as wake-up signals for standalone waits.
  • Updates the OMP extension and its integration version for willContinue.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
src/ipc.rs Adds platform-specific retrieval of the connected local client's process ID.
src/api/server.rs Captures peer identity before reading the request and stamps it only onto lifecycle-report methods.
src/terminal/state.rs Stores and enforces the OMP reporter lease on hook authority, with focused teardown and replacement coverage.
src/api/wait.rs Removes stale event-status substitution and returns status only from a current probe, except for the existing release-final-status path.
src/integration/assets/omp/herdr-agent-state.ts Suppresses idle reporting for intermediate ends that have an automatic continuation scheduled.
tests/cli/agent_wait.rs Adds an end-to-end regression test proving a transient idle event does not complete a wait whose current state is working.

Sequence Diagram

sequenceDiagram
    participant OMP as OMP process
    participant API as Local API server
    participant App as App event handling
    participant State as TerminalState
    participant Wait as agent wait
    OMP->>API: pane.report_agent / session
    API->>API: Read kernel peer PID
    API->>App: Stamped lifecycle report
    App->>State: Apply report with reporter PID
    alt Same process or no live lease
        State->>State: Update lifecycle authority
        State-->>Wait: Publish status event
        Wait->>State: Probe current agent state
        State-->>Wait: Fresh snapshot
    else Foreign process while lease is live
        State->>State: Reject report
    end
Loading

Reviews (2): Last reviewed commit: "fix: bind omp lifecycle reports to the p..." | Re-trigger Greptile

A nested Oh My Pi session inherits the pane's herdr environment, so its
extension reported as the pane's agent and re-anchored lifecycle authority
at its own session. When the nested session finished, the pane read idle
while its own agent was still mid-turn, and `agent wait` returned that.

The server now reads the connected peer's process id from the kernel and
stamps it onto lifecycle reports, so it can never be supplied by a payload.
A leased reporter source claims the pane with its authority, and reports
from any other process are refused for as long as that authority is live.
The lease is stored on the authority record, so it is released exactly when
the authority is and a restarted agent is never gated by its predecessor.
Only omp is leased: its extension runs in-process and reuses that process
for every report. Shell-asset integrations report from a new process each
time and keep the previous first-writer behavior.

Standalone `agent wait` no longer substitutes a status observed in an event
into a later snapshot. That returned a status the same snapshot could
already contradict, leaving callers no way to detect the bad result.

The omp hook also treated every non-retryable `agent_end` as idle. Upstream
marks an end that has already scheduled a continuation with `willContinue`,
so those no longer settle the pane.

refs herdrdev#2851
@caner-akca
caner-akca force-pushed the issue/2851-omp-reporter-lease branch from d6bcd75 to eceda2b Compare August 20, 2026 10:18
@ogulcancelik

Copy link
Copy Markdown
Collaborator

thanks for investigating. the pid lease is too large for this issue, and agent wait should return when an agent reports idle or done. i’m closing this; please send a new pr with only the willContinue fix.

@kangal-bot kangal-bot removed the ai-review Trigger automated AI reviews for pull requests admitted by the PR gate label Aug 20, 2026
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.

3 participants