Skip to content

feat(orchestration): subscribe terminal mailboxes to idle pointers - #22115

Open
abruption wants to merge 20 commits into
stablyai:mainfrom
abruption:feat/terminal-mailbox-subscription
Open

abruption wants to merge 20 commits into
stablyai:mainfrom
abruption:feat/terminal-mailbox-subscription

Conversation

@abruption

@abruption abruption commented Sep 22, 2026 •

Copy link
Copy Markdown
Contributor

ELI5

An agent can receive durable Orca mail today, but an idle bare terminal has no safe signal that mail is waiting. This adds an explicit receiver subscription: after the agent opts in, Orca may type a short inbox pointer into that same terminal when it is live and idle. The message body remains in the inbox.

What Changed

  • Add orchestration subscribe, orchestration subscription status, and orchestration unsubscribe for the current Orca-launched terminal.
  • Bind each ephemeral subscription to the verified host, terminal handle, pane, PTY, and process incarnation without retaining launch tokens or SSH attachment secrets.
  • Reuse the existing mailbox pointer reservation/coalescing path for bare term_* recipients.
  • Recheck exact ownership, authority, writability, live idle state, prompt settlement, and agent identity before pointer and Enter writes.
  • Keep message enqueue/read/ack state separate from wake status and generation-fence asynchronous completion.
  • Bind retry receipts to the verified receiver and separate historical mutation receipts from current subscription status.
  • Add CLI/RPC documentation and focused local, managed SSH, WSL, and live-agent E2E coverage.
  • Integrate with the current unified RPC dispatcher and workspace permissions. Resolve verified native PTYs on the execution host without relying on a connected renderer.
  • Preserve managed Run mailbox redelivery when a terminal handle has no direct subscription, and keep non-TUI agent identities on the no-auto-Enter path after main's runtime refactors.

Why

orchestration send durably stores mail, but an idle Claude Code or Codex terminal may not inspect its inbox until a person gives it another turn. Sender-driven raw PTY input cannot prove receiver intent and can duplicate or misroute input. Receiver opt-in preserves durable mail as the source of truth while reusing Orca's existing pointer delivery and safety gates.

Linked Issue

Fixes #18206

Visual Proof

N/A — this adds CLI/RPC behavior and hidden terminal delivery; it does not change rendered UI.

Testing

  • I manually tested these changes locally
  • Automated tests added/updated, or explained why not below

Current-main conflict refresh (2026-10-18, main baseline 33e6785fe2c3):

  • Primary-agent rerun: 13 focused files / 189 tests passed, including managed mailbox redrive, subscription authority, mutation replay, pointer delivery and certified-exit behavior.
  • Node and CLI typechecks, changed-code quality and main-relative diff checks passed. RPC parameter catalog verification passed.
  • Reused main's runtime layers, current SSH bridge verifier and capabilities. Unknown/non-TUI identities still do not receive automatic Enter; no new provider support is claimed.
  • No live macOS/Windows/WSL/SSH or vendor-TUI journey was rerun for this conflict refresh. The earlier results below remain historical measurements on their stated baseline.

Previous integration validation (2026-10-14, main baseline efbe3210d68d):

  • 179 focused tests passed in 16 files with pnpm test:node, covering subscription authority, native PTYs without a renderer, pointer staging/submission, ambiguous-write reservations, mutation replay, CLI/RPC contracts, and PTY exit handling.
  • pnpm tc:node, pnpm tc:cli, RPC catalog verification, changed-code quality, and diff checks passed.
  • Rebuilt hidden macOS Electron/real-PTY tests passed 2/2 (17.6 seconds): receiver registration/delivery without marking mail read, and Cursor pointer-only delivery without Enter.
  • Native Ubuntu 24.04 ARM64 SSH E2E passed 1/1 (5.5 minutes), using the real managed-server install, launch proof, CLI, and PTY with a deterministic agent fixture. It checks working-state deferral, viewer disconnect/reconnect without input, the same PTY and subscription after reconnect, and exactly one pointer plus one Enter after idle; the body is not injected and mail remains unread. The native Linux prebuild/smoke and packaged-server template checks also passed.

The SSH fixture now uses the current managed-server architecture. The execution host can resolve an explicitly subscribed native PTY without an attached renderer; the existing identity, ownership, settled-idle, and write-reservation checks still apply. The portable CI fixture uses Docker, while this validation ran over SSH against a native Ubuntu ARM64 host.

Historical validation before this main merge included 14 macOS Electron/PTY cases, live Codex 0.155.0 and Claude Code 2.1.278, macOS/Linux SSH, Windows SSH/ConPTY, and WSL. Those broader platform/provider cases were not all rerun on the updated architecture and are not claimed as fresh results. The old relay-specific #22112 dependency no longer describes the current managed-server path.

Antigravity CLI 1.2.7 remains outside the v1 automatic-wake scope: earlier live runs lost current terminal authority after subscription, so mail stayed unread and no pointer or Enter was written. The reference document directs those sessions to inspect the inbox explicitly.

At the previous integration, the full repository E2E TypeScript check reported 264 diagnostics outside this feature's changed files; no fresh clean-base comparison or full-repository pass was established. That full E2E typecheck was not rerun in this conflict refresh. Fresh live platform/vendor-TUI coverage remains for follow-up/CI.

AI Disclosure

OpenAI Codex assisted with implementation, test construction, review, and validation. Independent Codex sessions reviewed the authority, lifecycle, mutation-replay, and ambiguous-write boundaries through five read-only review passes.

Review

Please focus on the receiver authority binding, same-incarnation ambiguous-write fence, completed mutation replay semantics, and the distinction between durable enqueue and best-effort wake status.

Agent skill upstream boundary

  • Not applicable, or this change follows docs/reference/agent-skill-sharing-upstream-boundary.md and copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.

Notes

  • Registration is receiver-initiated and process-local. Runtime restart, process replacement, PTY exit, handle remint, or changed host authority requires explicit registration again.
  • Cursor and unknown agent identities receive pointer-only behavior with no automatic Enter.

Checklist

  • This PR is small and focused
  • I explained what changed and why (ELI5, the user-facing before/after, the mechanism, and why over the alternatives)
  • Before/after screenshots or videos attached for UI changes, or N/A with reason
  • Self-reviewed for correctness, security, and performance
  • Cross-platform, SSH/remote, and path/shortcut impact considered (or N/A)
  • pnpm lint, pnpm typecheck, pnpm test, and pnpm build pass (or CI will cover; local preferred)

@abruption
abruption marked this pull request as ready for review September 27, 2026 03:30
@coderabbitai

coderabbitai Bot commented Sep 27, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Important

Review skipped

We couldn't safely recover the incremental review. No full review was started, and the last reviewed checkpoint was preserved. Retry later, or explicitly request a full review by commenting @coderabbitai full review.

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

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Advanced

Run ID: 9dba0083-8067-4d4a-821f-a2d9e8bd8d02

📥 Commits

Reviewing files that changed from the base of the PR and between 4cc16de and 03088de.

📒 Files selected for processing (12)
  • src/main/runtime/orca-runtime-on-pty-exit.ts
  • src/main/runtime/orca-runtime-verify-orchestration-compatibility-caller.ts
  • src/main/runtime/orchestration-compatibility-host-scope.test.ts
  • src/main/runtime/orchestration-compatibility-host-scope.ts
  • src/main/runtime/orchestration/formatter.ts
  • src/main/runtime/orchestration/terminal-mailbox-subscriptions.ts
  • src/main/runtime/pty-exit-agent-status-reconciliation.test.ts
  • src/main/runtime/rpc/methods/orchestration/messaging/subscription-methods.test.ts
  • src/main/runtime/rpc/rpc-streaming-dispatcher.ts
  • src/main/runtime/terminal-mailbox-subscription-authority.test.ts
  • tests/e2e/orchestration-terminal-mailbox-subscription-ssh.spec.ts
  • tests/e2e/orchestration-terminal-mailbox-subscription-wsl.spec.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 8 remain after this review.

📝 Walkthrough

Adds subscribe, unsubscribe, and status commands for terminal mailbox subscriptions. The runtime binds subscriptions to verified terminal identity and tracks their lifecycle in memory. Pointer delivery and Enter submission now use subscription matching, generation checks, and terminal-state gates. The change also adds mutation replay handling, CLI and RPC tests, end-to-end coverage for local, live-agent, SSH, and WSL scenarios, and reference documentation.

Priority: ➖ Normal

Merge Risk: ⚪ Minimal · up to 03088

The subscription changes are mergeable after normal checks; no issue identified here prevents a replacement terminal from subscribing or receiving pending mail.

Security architecture risk: 🟡 Moderate · up to 03088

Terminal subscriptions enable automatic input in opted-in terminals, including remote terminals. The reviewed path has substantial identity and delivery checks, and no security bypass was established, but this is a consequential new authority boundary.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The newly attackable outcome is input to an opted-in terminal, potentially on a local, WSL, or SSH host. The inspected checks constrain delivery to the bound terminal and mailbox rather than permitting a sender-selected input target.

Trust Boundaries and Controls

  • observed — The receiver proves current launch authority before subscribing. Before input, delivery checks subscription and mailbox ownership, writability, live idle and settled state; automatic Enter also requires matching recognized agent identity.

Resilience and Maintainability Implications

  • observed — Generation checks reject completion after subscription replacement or removal, while durable reservation and flight checks limit stale Enter settlement. The displayed status record itself checks generation but not flight identity.

Hardening Proposals

  • proposed — Fence wake-status updates to the current delivery flight as well as the subscription generation, so late completion cannot obscure a newer blocked or ambiguous state. This is a status-integrity improvement, not an established input-authorization bypass.
Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 2.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 50 functions across 40 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Linked Issues check Passed The PR implements the subscription option in issue #18206. Verified terminal identities can subscribe, inspect status, and unsubscribe. Live, idle terminal mailboxes can receive a short pointer while …
Out of Scope Changes check Passed The changed production files implement terminal mailbox subscriptions, pointer delivery, capability negotiation, RPC and CLI exposure, identity validation, replay handling, and delivery-state safety f…
Title check Passed The title is concise, specific, and accurately describes the primary change: receiver-initiated subscriptions for terminal mailbox pointers.
Description check Passed The description includes all required sections, links issue #18206, explains the change and rationale, documents testing and limitations, and records AI use. It also clearly states that full repositor…
Full details: Linked Issues check

Explanation

The PR implements the subscription option in issue #18206. Verified terminal identities can subscribe, inspect status, and unsubscribe. Live, idle terminal mailboxes can receive a short pointer while the message remains in the durable inbox. The implementation preserves opt-in behavior and adds authority checks, replay handling, generation fences, deferred outcomes, and automated unit, RPC, CLI, and end-to-end tests. The separate terminal send --enter stalled-payload addendum is not required for this subscription objective.

Full details: Out of Scope Changes check

Explanation

The changed production files implement terminal mailbox subscriptions, pointer delivery, capability negotiation, RPC and CLI exposure, identity validation, replay handling, and delivery-state safety for issue #18206. Documentation and test helpers support the feature. The supplied whole-PR summary shows no demonstrated unrelated production change.

Full details: Description check

Explanation

The description includes all required sections, links issue #18206, explains the change and rationale, documents testing and limitations, and records AI use. It also clearly states that full repository and fresh platform coverage were not completed.

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 4


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Advanced

Run ID: 1667cf5e-352c-44a4-a9ea-d5e37b1405ce

📥 Commits

Reviewing files that changed from the base of the PR and between 3eb1ade and 4cc16de.

⛔ Files ignored due to path filters (1)
  • src/shared/rpc-contract/rpc-params-catalog.generated.ts is excluded by !**/*.generated.*
📒 Files selected for processing (35)
  • docs/reference/terminal-mailbox-subscriptions.md
  • src/cli/handler-group-manifest.ts
  • src/cli/handlers/orchestration.ts
  • src/cli/handlers/orchestration/subscription-handlers.test.ts
  • src/cli/handlers/orchestration/subscription-handlers.ts
  • src/cli/specs/orchestration.ts
  • src/main/runtime/orca-runtime-stop-requested-pty-ids.ts
  • src/main/runtime/orca-runtime-write-orchestration-pointer-pty.ts
  • src/main/runtime/orchestration/formatter.ts
  • src/main/runtime/orchestration/mailbox-pointer-delivery-contract.ts
  • src/main/runtime/orchestration/mailbox-pointer-delivery.ts
  • src/main/runtime/orchestration/mailbox-pointer-resume.ts
  • src/main/runtime/orchestration/mailbox-pointer-stage.ts
  • src/main/runtime/orchestration/mailbox-pointer-submit.test.ts
  • src/main/runtime/orchestration/mailbox-pointer-submit.ts
  • src/main/runtime/orchestration/terminal-mailbox-subscriptions.test.ts
  • src/main/runtime/orchestration/terminal-mailbox-subscriptions.ts
  • src/main/runtime/rpc/methods/orchestration.ts
  • src/main/runtime/rpc/methods/orchestration/messaging/subscription-methods.test.ts
  • src/main/runtime/rpc/methods/orchestration/messaging/subscription-methods.ts
  • src/main/runtime/rpc/orchestration-mutation-executor.ts
  • src/main/runtime/rpc/orchestration-mutation-receipt.ts
  • src/main/runtime/terminal-mailbox-subscription-authority.test.ts
  • src/shared/orchestration-rpc-contract.test.ts
  • src/shared/orchestration-rpc-contract.ts
  • src/shared/protocol-version.ts
  • src/shared/rpc-contract/terminal-subscription-params.ts
  • src/shared/terminal-mailbox-subscription.ts
  • tests/e2e/helpers/orchestration-mail-pane-agent.ts
  • tests/e2e/helpers/orchestration-mail-pane-python-agent.ts
  • tests/e2e/helpers/ssh-test-target-connection.ts
  • tests/e2e/orchestration-terminal-mailbox-subscription-live-agents.spec.ts
  • tests/e2e/orchestration-terminal-mailbox-subscription-ssh.spec.ts
  • tests/e2e/orchestration-terminal-mailbox-subscription-wsl.spec.ts
  • tests/e2e/orchestration-terminal-mailbox-subscription.spec.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Important

This PR's documented replay contract — "a completed replay does not reapply the ephemeral mutation" — holds on the unary dispatcher but not on the streaming/WebSocket dispatcher, which never forwards the replay marker the new subscription handlers read. One-line fix below, with a reachability caveat.

Reviewed changes

  • Receiver-initiated subscription verbs — orchestration subscribe|unsubscribe|subscription status CLI handlers and RPC methods, capability-gated on orchestration.terminal-subscription.v1.
  • Authority binding — TerminalMailboxSubscriptions binds host scope, handle, pane key, PTY, and process incarnation; getTerminalMailboxSubscriptionBinding re-verifies the caller and current dispatch authority before every read/write and fails closed on any drift.
  • Bare-terminal pointer delivery — isTerminalMailbox gates threaded through delivery/stage/submit/resume; existing run:/dispatch: behavior is unchanged.
  • Generation fence — async pointer/Enter completions carry the registration generation and are dropped once a re-registration moves it.
  • Durable replay binding — subscribe/unsubscribe payload hashes include the verified receiver binding; completed replays return current status plus historicalReplay.
  • Formatter, docs, and E2E — bare-terminal inbox pointer text, a reference doc, and local/SSH/WSL/live-agent E2E coverage.

⚠️ Streaming/WebSocket dispatch drops the replay marker, so a replayed subscription mutation re-applies

A completed replay of orchestration.subscribe/orchestration.unsubscribe that arrives over the streaming dispatcher re-enters the handler's fresh branch: it re-runs register/remove and deliverPendingMessagesForHandle instead of returning the current-status-plus-historicalReplay split. This is the exact invariant the PR documents and asks reviewers to focus on. Reachability requires a streaming/WebSocket caller that can supply valid orchestrationCompatibilityEvidence — I could not confirm such a caller exists in production (mobile is allowlisted away from these methods and I did not verify the CLI's remote transport), so treat this as a conformance gap to confirm or guard rather than a proven outage.

Technical details
# Subscription replays re-apply over the streaming dispatcher

## Affected sites
- `src/main/runtime/rpc/rpc-streaming-dispatcher.ts:125-157` — the `invoke` context passes `recordMutationReceipt` but not `replayedMutationReceipt`.
- `src/main/runtime/rpc/dispatcher-unary-method-invocation.ts:72` — the unary path does pass `replayedMutationReceipt: mutation?.replayedReceipt`, which is why the unary replay tests pass.
- `src/main/runtime/rpc/methods/orchestration/messaging/subscription-methods.ts:17-27` / `43-53` — the handler only takes the "current status + historicalReplay" path when `replayedMutationReceipt !== undefined`.
- `src/main/runtime/rpc/orchestration-mutation-receipt.ts` (`shouldObserveCompletedMutation`) returns `true` for subscription methods, which is what forces the handler to run on replay; the streaming dispatcher then re-applies because the marker is missing.
- `src/main/runtime/runtime-rpc/runtime-rpc-websocket-dispatch.ts:135` routes every paired WebSocket request through `dispatchStreaming`.

## Required outcome
A completed replay of `orchestration.subscribe`/`orchestration.unsubscribe` must not re-apply the ephemeral mutation and must return the historical/current split on every dispatch path, not just the unary one.

## Suggested approach
Pass `replayedMutationReceipt: mutation?.replayedReceipt` in the streaming `invoke` context so both dispatchers build the same handler context.

## Open questions for the human
- Is there any production caller that reaches `orchestration.subscribe`/`unsubscribe` via `dispatchStreaming` (paired web/desktop scope, remote CLI transport) while able to present a valid launch token? If not, a short comment stating the unary-only assumption would also resolve it.

ℹ️ Nitpicks

  • src/main/runtime/orchestration/formatter.ts:117 re-implements the term_* pattern inline instead of importing isTerminalMailbox, so the handle grammar now lives in two places and can drift.
  • TerminalMailboxSubscriptions.hostScopesEqual (terminal-mailbox-subscriptions.ts:35-40) compares with JSON.stringify while the runtime already exposes orchestrationCompatibilityHostScopesEqual; both sides are built from one shape today, so this is only a key-order/drift nit.

Pullfrog  | Fix it ➔ | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

ℹ️ No new issues in this delta. The replay-consistency gap raised against 4cc16dee is unchanged by 8f457cf6fa and remains open.

Reviewed changes

Since the prior review (4cc16dee), one commit landed: 8f457cf6fa — fix(orchestration): preserve subscriptions until certified exit.

  • Certified-exit gate — onPtyExit now retires mailbox subscriptions only when process death is certified (exitCode >= 0, or hostExitConfirmed/providerExitObserved); an unverifiable exit leaves the subscription in host_unverifiable rather than proven_exited.
  • Delivery-uncertainty preservation — refreshCurrent only restores host_unverifiable → active when the reason is current_authority_unverifiable, so delivery-derived uncertainty (current_target_unverifiable, pty_not_writable, live_idle_not_observed) is no longer silently cleared.
  • Tests — new pty-exit-agent-status-reconciliation.test.ts pins the retire-only-on-certified-exit matrix; the authority tests pin preservation and authority-restore.
  • E2E hardening — the SSH lane no longer defaults to a hardcoded host and skips when none is configured; WSL cleanup moved into finally so a pre-launch failure still tears down.

Pullfrog  | Fix it ➔ | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✅ No new issues found.

Reviewed changes

Since the prior pullfrog review (8f457cf6fa), one commit landed: 03088de5f9 — fix(orchestration): preserve subscription replay over streaming RPC. It closes the replay-consistency gap raised against 4cc16dee.

  • Streaming replay marker restored — RpcStreamingDispatcher now forwards replayedMutationReceipt: mutation?.replayedReceipt into the invoke context, so a replayed orchestration.subscribe/orchestration.unsubscribe over the streaming/WebSocket path returns live status plus historicalReplay instead of re-applying the ephemeral mutation. The parameterized test proves this: removing the line makes the streaming case fail (historicalReplay absent, stale subscribed value).
  • Host-scope comparison unified — the inline JSON.stringify check and the runtime verifier's copy were replaced by a shared orchestrationCompatibilityHostScopesEqual (src/main/runtime/orchestration-compatibility-host-scope.ts), now used by the verifier and by TerminalMailboxSubscriptions.matches/canManage, eliminating key-order drift.
  • Formatter reuses the canonical predicate — formatMessagePointer calls isTerminalMailbox rather than re-inlining the term_* regex.
  • Tests — new orchestration-compatibility-host-scope.test.ts; subscription-methods.test.ts now exercises both transports via it.each(['unary', 'streaming']).

All four CodeRabbit threads and both prior pullfrog nitpicks are addressed in this delta.

Pullfrog  | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

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.

Structured orchestration messages: optional wake of the recipient terminal, or an Agent-session subscription to Run/handle mailboxes

1 participant