Skip to content

fix(runtime): clear a stale agent wait when the tail is a settled ready prompt - #24308

Closed
innocarpe wants to merge 4 commits into
stablyai:mainfrom
innocarpe:fix-24187-stale-agent-wait
Closed

innocarpe wants to merge 4 commits into
stablyai:mainfrom
innocarpe:fix-24187-stale-agent-wait

Conversation

@innocarpe

@innocarpe innocarpe commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

ELI5

Orca used an old permission prompt from terminal history even after the live Codex screen was ready. It now checks the current screen when the runtime has a complete, up-to-date screen model.

What Changed

  • Keep clearing a blocked-wait stamp only when retained tail text proves a settled ready prompt.
  • Check the current emulator screen for blocked prompts before refusing a send. Use retained tail text when the full screen is unavailable or behind the latest PTY output.
  • Preserve refusal when the current screen still shows a blocked prompt.
  • Add a send regression test with an old blocked tail and a ready current screen.

Why

A retained tail can contain a prompt that has already left the visible screen. Clearing the timestamp alone cannot change the send decision because that decision reads the blocked text first. Checking a synchronized current screen fixes that stale refusal while keeping the existing fail-closed tail fallback when the screen cannot be trusted.

Linked Issue

Fixes #24187

Visual Proof

N/A — no UI layout changes; this changes which terminal screen evidence the send guard reads.

Testing

  • I manually tested these changes locally
  • Automated tests added/updated

Ran on macOS:

node node_modules/vitest/vitest.mjs run --config config/vitest.config.ts src/main/runtime/orca-runtime-tail-wait-memo.test.ts src/main/runtime/terminal-wait-detection.test.ts src/main/runtime/agent-prompt-submission-runtime.test.ts

Result: 3 test files passed, 78 tests passed. This did not include a manual live Codex pane check.

AI Disclosure

Codex (OpenAI GPT-6) assisted with this follow-up.

Review

The send guard and terminal status query now use the same current-screen-first evidence. The screen is trusted only when the headless model's output sequence matches the PTY output sequence; otherwise the existing retained-tail check remains in force.

Agent skill upstream boundary

  • Not applicable; this change does not alter agent skills or installer behavior.

Notes

The screen reader uses the current whole-screen headless model. Provider-preferred or stale models fall back to the existing tail checks. Cross-platform, SSH, and mobile environments were not exercised locally; CI is still awaiting maintainer approval.

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • Visual proof is N/A because there is no UI layout change
  • Self-reviewed the change for correctness and performance
  • Considered cross-platform and remote behavior; the fallback remains conservative when a current screen is unavailable
  • pnpm lint, pnpm typecheck, pnpm test, and pnpm build pass (CI is awaiting maintainer approval)

@greptile-apps

greptile-apps Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 5/5

[Medium risk] Refactors wait-state tracking in the runtime scheduler.

The PR appears safe to merge, though runtime-level coverage of the quiet-pane deferred check remains desirable.

Summary

This PR clears a stale blocked-wait stamp when a PTY tail becomes a settled ready prompt.

  • It applies the clearing logic during PTY data handling and deferred wait checks, including mirrored leaves.
  • The added test covers the clearing helper, but the previously requested runtime-level deferred-check coverage is still absent.
Diagram
%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A[PTY output] --> B[Update tail wait state]
  B --> C{Settled ready tail?}
  C -->|Yes, with blocked stamp| D[Clear PTY stamp]
  D --> E[Clear stamps on mirrored leaves]
  C -->|No| F[Retain blocked stamp]
Loading

Reviews (2) · Last reviewed commit: "fix(runtime): skip a ready-tail scan whe..."

@coderabbitai

coderabbitai Bot commented Oct 1, 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: 53a3fb43-4c01-437f-b58e-9692b172589f
📥 Commits

Reviewing files that changed from the base of the PR and between 433ce82c8b0de4405f0357792c007cd4fd7f8f05 and 3c6eef1b7f18d47b8b4b0370bde09dc0f9226207.

📒 Files selected for processing (4)
  • src/main/runtime/orca-runtime-on-pty-data.ts
  • src/main/runtime/orca-runtime-schedule-wait-blocked-check.ts
  • src/main/runtime/orca-runtime-tail-wait-memo.test.ts
  • src/main/runtime/terminal-wait-tail-state.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.

📝 Walkthrough

The runtime now resolves blocked-wait timestamps using the existing timestamp, newer blocked reasons, and the next tail state. A settled ready tail clears the timestamp, and the scheduled check clears matching leaf timestamps. Status queries now include live screen text when available. Blocked-prompt detection uses that text and falls back to retained wait text when the live screen is unavailable. Tests cover timestamp resolution and prompt submission when an old permission prompt remains in the retained tail.

Priority: ➖ Normal

Severity of issue fixed: Medium

Merge Risk: ⚪ Minimal · up to 3c6ee

A ready terminal screen can now avoid refusal caused by an old prompt in the retained tail. No actionable merge-blocking risk remains after normal checks.

Security architecture risk: 🔵 Low · up to 3c6ee

This changes when automated input can resume after a permission request appears to have cleared. Freshness checks and existing refusal controls limit the exposure, but screen interpretation and recovery behavior were not fully validated.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The demonstrated exposure is automated prompt bytes reaching the foreground program of the targeted terminal. Snapshot construction validates the handle's PTY binding, and submission retains generation checks. This evidence does not establish a new cross-tenant, cloud-service, credential, or infrastructure authority transition.

Trust Boundaries and Controls

  • observed — Terminal output remains the text source for blocked-prompt detection. The PR changes which representation is authoritative, not the detector's matching rules. Live permission titles and explicit status remain additional status evidence, while the writer rechecks permission activity before paste and submit stages.

Resilience and Maintainability Implications

  • observed — Scheduled checks reset carry when their PTY disappears. Cleanup cancels pending timers and deletes per-PTY check state; generation reset and disconnected-record pruning invoke cleanup. These controls constrain stale checks across interruption and PTY replacement.

Hardening Proposals

  • proposed — Define permission-detection equivalence between raw screen rows and draft-filtered projections for supported permission layouts, or use a dedicated security-relevant projection. This would prevent future presentation changes from silently changing send authority; it is not an observed bypass.
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning For #24187, the send guard and status query now use a synchronized live screen, with retained-tail fallback. The regression test covers an old blocked tail and a ready live screen. The wait-stamp logi… Include the matched blocked rule and line in the agent_prompt_blocked error and agentWait. Add automated tests for both outputs.
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 6 functions across 8 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the stale-wait clearing change and is concise enough to identify the main purpose.
Description check ✅ Passed The description covers the required sections, explains the behavior and rationale, links issue #24187, and reports tests and platform limitations. It also states why visual proof is not applicable.
Out of Scope Changes check ✅ Passed The live-screen check, conservative tail fallback, stale wait-stamp clearing, and related tests all address the stale blocked-wait behavior in #24187. No unrelated changes are shown.
Full details: Linked Issues check

Explanation

For #24187, the send guard and status query now use a synchronized live screen, with retained-tail fallback. The regression test covers an old blocked tail and a ready live screen. The wait-stamp logic also clears a stamp when settled-ready evidence is available. However, the blocked response and agentWait still do not expose the matched rule and line requested by the issue. The status query continues to return only the general permission status for this case.

  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Autopilot is currently an internal CodeRabbit preview.

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

@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

The new clear cannot change the agent_prompt_blocked decision it targets. Both waitBlockedAt consumers bail out when detectTerminalWaitBlockedReason(terminal.waitText) is non-null, which is mutually exclusive with this PR's clear condition — so waitBlockedAt is never read when the clear fires. Details inline on tailSettledReadyClearsBlockedWait.

Reviewed changes

  • resolveWaitBlockedAt / tailSettledReadyClearsBlockedWait — new helpers in terminal-wait-tail-state.ts that stamp waitBlockedAt on a newer blocked reason, clear it on a settled ready prompt, and otherwise preserve it.
  • Tail paths rerouted through the resolver — orca-runtime-on-pty-data.ts:189 (memoized leaf path) and orca-runtime-schedule-wait-blocked-check.ts:81 (deferred check) now compute the stamp via resolveWaitBlockedAt.
  • Deferred-clear leaf propagation — orca-runtime-schedule-wait-blocked-check.ts:89-95 nulls waitBlockedAt on leaves that mirror the pty's tailBuffer after a deferred clear.
  • Tests — an exact-array case [1,1,1,null,5,5], a table of negative cases for tailSettledReadyClearsBlockedWait, and the memoization sim authoring the new resolver.

ℹ️ Nitpicks

  • The deferred clear loop in runWaitBlockedCheck (orca-runtime-schedule-wait-blocked-check.ts:89-95) is the part that actually mutates leaf state, but no test exercises it — the new tests only cover the pure resolver. A test that drives a deferred check and asserts the mirroring leaf is cleared would pin the behavior the PR description highlights.
  • resolveWaitBlockedAt calls tailSettledReadyClearsBlockedWait even when current === null, so a candidate-bearing tail now lowercases and scans the full waitText per chunk even when there is nothing to clear.

Test command from the PR body verified locally: 64 passed.

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

@innocarpe
innocarpe force-pushed the fix-24187-stale-agent-wait branch from 780ac74 to cf6ad0b Compare October 1, 2026 09:52
@innocarpe

Copy link
Copy Markdown
Contributor Author

Sync update (cf6ad0b005)

Address review: a null wait stamp no longer scans the tail, and the deferred check clears a leaf that shares the PTY tail without waiting for another chunk. The permission refusal still returns before reading the stamp whenever the same blocked signal is absent, so this does not retarget that gate.

@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 follow-up commit addresses both prior nitpicks.

Reviewed changes

  • Hot-path guard — resolveWaitBlockedAt now runs tailSettledReadyClearsBlockedWait only when the stamp is non-null, so a candidate-bearing tail no longer pays for the ready-prompt scan on every chunk (terminal-wait-tail-state.ts:122-123).
  • Deferred leaf-clear extracted — the inline mirrored-leaf loop in runWaitBlockedCheck became clearMirroredLeafWaitStamps, with the same early-return and reference-equality semantics (terminal-wait-tail-state.ts:131-147, orca-runtime-schedule-wait-blocked-check.ts:90-96).
  • Tests — a clearMirroredLeafWaitStamps unit test and a resolveWaitBlockedAt null/set case; 66 passed locally with the PR's test command.

Not approving: the earlier finding that the clear cannot reach the agent_prompt_blocked refusal is still open and unaddressed. It is tracked in its own thread, so I am leaving that thread open rather than restating it here.

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

@innocarpe
innocarpe force-pushed the fix-24187-stale-agent-wait branch from cf6ad0b to 433ce82 Compare October 4, 2026 11:26

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


ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: ae4b2f4e-e912-4855-8274-1aee35eb7b32
📥 Commits

Reviewing files that changed from the base of the PR and between cf6ad0b00563482a279abd95d879e80304edc3ea and 433ce82c8b0de4405f0357792c007cd4fd7f8f05.

📒 Files selected for processing (6)
  • src/main/runtime/agent-prompt-submission-runtime.test.ts
  • src/main/runtime/orca-runtime-resolve-authoritative-terminal-wait-permission.ts
  • src/main/runtime/orca-runtime-stop-requested-pty-ids.ts
  • src/main/runtime/orca-runtime-tail-wait-memo.test.ts
  • src/main/runtime/runtime-terminal-agent-status-query.ts
  • src/main/runtime/terminal-wait-tail-state.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 7 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.

✅ No new issues found.

Reviewed changes — the delta since the prior pullfrog review (cf6ad0b → 433ce82): one follow-up commit that retargets the agent_prompt_blocked gate at the live screen instead of the retained tail.

  • Live-screen-first blocked gate — RuntimeTerminalAgentStatusSnapshot gained liveScreenText; RuntimeTerminalAgentStatusQuery.readStatus and resolveAuthoritativeTerminalWaitPermission now run detectTerminalWaitBlockedReason(terminal.liveScreenText ?? terminal.waitText), so a settled live composer no longer inherits the tail's stale blocked phrase.
  • Caught-up screen wiring — the new readLiveScreenLines returns the whole-grid projection only when the headless emulator's outputSequence matches the PTY's; otherwise null keeps the retained-tail fallback, so provider-preferred, hydration-pending, and SSH/remote panes stay fail-closed.
  • Send regression test — a driving send with an old "Hooks need review" blocked tail and a settled ready live screen is now accepted.

This addresses the open concern from the earlier review: both consumers now read the current screen, so the clear no longer needs to reach the refusal. Tests pass locally (78 across the three focused files).

Pullfrog  | View workflow run | Using deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏

@innocarpe
innocarpe force-pushed the fix-24187-stale-agent-wait branch from 433ce82 to 3c6eef1 Compare October 4, 2026 15:36

@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 — the delta since the prior pullfrog review (433ce82 → 3c6eef1): one commit that makes the settled-ready tail clear actually reachable.

  • Ready-prompt inspection on stamped tails — computeTerminalTailWaitState gains inspectTailForReadyPrompt; when set it rebuilds the retained tail, records settledReadyPrompt via isKnownReadyPromptSettled, and still returns the empty waitText so no rebuilt 256 KiB string is retained.
  • Clear now fires on the recorded decision — tailSettledReadyClearsBlockedWait returns true when settledReadyPrompt === true, which the previous empty-waitText fast path could never produce, so a stale stamp on a clean ready tail now clears.
  • Flag gated on an existing stamp — onPtyData and runWaitBlockedCheck pass waitBlockedAt !== null, so the extra scan only runs while a stamp is present.
  • Test — a unit case proving the stamped state clears while the unstamped fast path does not.

waitText and signal outputs are unchanged across every branch — only the new optional field differs — so the already-reviewed live-screen consumers see no behavior change. Extended the focused run to seven wait-related files: 184 passed.

Pullfrog  | View workflow run | Using deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏

@innocarpe
innocarpe force-pushed the fix-24187-stale-agent-wait branch 3 times, most recently from bd3c6eb to 9c7c13b Compare October 22, 2026 00:08
innocarpe and others added 4 commits October 22, 2026 09:30
…dy prompt

A blocked stamp stayed after the dialog left, so a later send to an idle
Codex composer was still refused. Clear it only when the tail is a settled
ready prompt, and keep it while the tail is empty, inconclusive, or blocked.
…lear

The deferred check still clears a leaf that shares the PTY tail, and that path is now tested without another output chunk. A null stamp no longer pays for the ready-prompt scan.

Co-authored-by: Cursor <cursoragent@cursor.com>
@innocarpe
innocarpe force-pushed the fix-24187-stale-agent-wait branch from 9c7c13b to 07d1f42 Compare October 22, 2026 00:30
nwparker added a commit that referenced this pull request Oct 25, 2026
…ine tail kept after the TUI erased it (#33485)

A Codex (or Antigravity/Cline/Prime Agent) dialog drawn and erased in place no longer leaves the pane "blocked" forever when no hook/OSC 7501 clears it. The line tail still decides; the live screen may only clear a tail block when caught up, quiet 3s, no blocker anywhere on screen, and the agent's composer is ready — then the stale block stamp is cleared so the send's own echo doesn't re-block. Also defers the daemon re-attach seed's output sequence until the seed lands.

Fixes #24187
Supersedes #24308

Co-authored-by: Wooseong Kim <2222333+innocarpe@users.noreply.github.com>
@nwparker

Copy link
Copy Markdown
Contributor

Thanks @innocarpe! Your core idea (check the current screen before refusing a send) landed in #33485, merged with your co-author credit, with extra guards so the screen can only clear a block — never miss a dialog that's really open. Closing in favor of it.

@nwparker nwparker closed this Oct 25, 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.

[Bug]: agent_prompt_blocked persists on an idle Codex composer after #18817: the wait state isn't re-evaluated while the pane is quiet

2 participants