Skip to content

fix(terminal): submit large agent-prompt pastes after TUI ingest (STA-2912) - #15698

Open
brennanb2025 wants to merge 1 commit into
mainfrom
brennanb2025/sta-2912-inject-large-payload
Open

brennanb2025 wants to merge 1 commit into
mainfrom
brennanb2025/sta-2912-inject-large-payload

Conversation

@brennanb2025

@brennanb2025 brennanb2025 commented Aug 21, 2026 •

Copy link
Copy Markdown
Contributor
Files Added Deleted Net
Test 6 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​360 $\color{#cf222e}{\Huge{\mathbf{−}}}$​3 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​357
Prod 12 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​569 $\color{#cf222e}{\Huge{\mathbf{−}}}$​34 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​535

ELI5

Orca could paste a large task into Codex and press Enter before Codex had actually turned the paste into its [Pasted Content N chars] composer item. The write succeeded, so worker-start reported a live worker even though the prompt was parked forever. This change waits for positive, delivery-owned screen evidence before submitting and refuses to report success when that evidence is unavailable.

What changed

  • For Codex prompts over 1,000 normalized Unicode scalar values, capture the current visible-grid marker multiset before paste, then require exactly one matching [Pasted Content N chars] delta before sending Enter.
  • Retry Enter at most three times while that owned marker delta remains; success requires a newer screen revision in which the delta is gone.
  • Fail closed on missing, stale, ambiguous, or unreachable screen state, and use settled PTY writes so contact loss cannot be mistaken for delivery.
  • Add a capability-negotiated, incarnation-checked visible-screen snapshot RPC for direct SSH relay PTYs.
  • Require paired worker servers to advertise orchestration.prompt-submit-verification.v1 before federated worker-start creates any home-side dispatch state.
  • Preserve the existing Claude, Grok, other-agent, and small-Codex submission paths.

Why

The trigger is not a concatenated PTY write: paste and Enter were already separate. The failure is an ordering race under slow paste ingestion, and PTY quietness or cursor-show output does not prove that Codex owns the paste yet. The collapsed-paste marker is the positive signal available from Codex today.

Linked issue

Fixes STA-2912
Fixes #11343

Author: @BrennanKB5

Acceptance proof

One identical untracked black-box test was overlaid on three isolated checkouts. Test SHA-256: 17e61e73ba247bb0d70886254dfdf85bcd5f462b078408aeaa52575f2edf7257.

  • Merge base 3d74f5fe0315d8125b969704a8830a9a6f2ee9ba: failed because prematureEnters was 1 before the marker appeared.
  • Fixed 70db5e8313b7cc6fa2543e1210e91332d47ab6db: passed.
  • Production-neutered 876e34c1ea6663fc19d70b87d0e4e1737b4ba731: failed on the same prematureEnters === 0 assertion with received value 1.

Validation

  • pnpm vitest run --config config/vitest.config.ts <15 affected files> — 1,389 passed, 1 skipped.
  • pnpm typecheck — passed.
  • node config/scripts/check-changed-code-quality.mjs — passed with 0 findings.
  • node config/scripts/check-react-doctor-changed.mjs — passed with 0 issues.
  • pnpm exec oxfmt --check <18 changed files> — passed.
  • Focused coverage includes delayed/split markers, exact normalization, ambiguity, missing screens, bounded retries, unsettled writes, SSH capability/incarnation/empty-screen behavior, relay snapshot ordering, and federated no-effects preflight.

Limitations

  • The verification depends on Codex's current [Pasted Content N chars] composer representation for large pastes; if Codex changes that representation, Orca fails closed instead of claiming the worker started.
  • Claude, Grok, and small-Codex paths were regression-tested through the shared runtime suites, but fresh credentialed CLI workers were not launched manually in this worker environment.
  • No renderer UI changed, so visual proof is not applicable.

@coderabbitai

coderabbitai Bot commented Aug 21, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The change adds Codex paste-ingestion detection and verified submission with bounded Enter retries. PTY relays maintain headless terminal emulators and expose synchronized snapshots with sequence and incarnation metadata. SSH providers negotiate and validate snapshot support. Prompt writes use centralized validation and settlement checks. Federated worker startup now requires the prompt-submit verification runtime capability.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes address issue #11343 by separating submission, verifying Codex ingestion, retrying Enter, and preventing false success reports.
Out of Scope Changes check ✅ Passed The SSH snapshot, capability negotiation, federation checks, and tests directly support reliable prompt-submit verification.
Docstring Coverage ✅ Passed Docstring check was indeterminate for this PR — some files could not be analyzed in time. Not blocking.
Title check ✅ Passed The title clearly and concisely describes the main change: fixing large agent-prompt paste submission after TUI ingestion.
Description check ✅ Passed The description explains the problem, implementation, rationale, linked issues, validation, and limitations; minor template sections are omitted.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

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.

@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

🧹 Nitpick comments (2)
src/main/runtime/agent-prompt-submission-verification.ts (1)

3-4: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use one concise comment.

Replace the two-line comment with one line that states the Codex-specific reason.

Proposed fix
-// Why: framed paste and Enter must not share a PTY write; this is the same
-// settle native chat uses between an image paste and the following submit.
+// Keep paste and Enter separate so Codex does not absorb Enter as pasted content.

As per coding guidelines, comments must be concise, non-obvious, and brief.

Source: Coding guidelines

src/main/runtime/agent-prompt-paste-submit-runtime.test.ts (1)

1-10: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use one platform-native worktree path across all fixtures.

Build the path with join(tmpdir(), 'worktree-a') and reuse it in the mocked Git responses and runtime-rpc-worktree-store-fixtures.ts; changing only WORKTREE_PATH leaves mismatched selectors and metadata IDs on Windows.

Source: Coding guidelines


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: ed77de03-24e8-4e89-ae1c-b14fbf243384

📥 Commits

Reviewing files that changed from the base of the PR and between 012e9f4 and 9d75eb5.

📒 Files selected for processing (12)
  • src/main/runtime/agent-prompt-paste-submit-runtime.test.ts
  • src/main/runtime/agent-prompt-submission-verification.test.ts
  • src/main/runtime/agent-prompt-submission-verification.ts
  • src/main/runtime/orca-runtime.ts
  • src/main/runtime/orchestration/agent-prompt-send-accepted.ts
  • src/main/runtime/rpc/methods/orchestration-composed-workers.test.ts
  • src/main/runtime/rpc/methods/orchestration-federation.ts
  • src/main/runtime/rpc/methods/orchestration-tasks-dispatch.test.ts
  • src/main/runtime/rpc/methods/orchestration-workers.ts
  • src/main/runtime/rpc/methods/orchestration.ts
  • src/shared/agent-prompt-paste-blob.test.ts
  • src/shared/agent-prompt-paste-blob.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 1 remains after this review.

Comment thread src/shared/agent-prompt-paste-blob.ts Outdated
Comment on lines +14 to +17
const combined = `${carry}${chunk}`
return {
detected: isUnsubmittedAgentPasteBlob(combined),
carry: combined.slice(-PASTE_BLOB_CARRY_MAX)

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.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Prevent completed paste markers from remaining in parser state.

  • src/shared/agent-prompt-paste-blob.ts#L14-L17: clear carry after a complete marker or retain only an unmatched marker prefix.
  • src/shared/agent-prompt-paste-blob.test.ts#L14-L19: add a marker-free follow-up chunk and assert that detected is false.
📍 Affects 2 files
  • src/shared/agent-prompt-paste-blob.ts#L14-L17 (this comment)
  • src/shared/agent-prompt-paste-blob.test.ts#L14-L19

@brennanb2025
brennanb2025 force-pushed the brennanb2025/sta-2912-inject-large-payload branch from 9d75eb5 to 70db5e8 Compare August 23, 2026 23:40
@coderabbitai

coderabbitai Bot commented Aug 23, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

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


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 1a58e682-4390-465f-b6f8-2afc35103bb1

📥 Commits

Reviewing files that changed from the base of the PR and between 3d74f5f and 70db5e8.

📒 Files selected for processing (18)
  • src/main/ipc/pty.ts
  • src/main/providers/pty-provider-contract.ts
  • src/main/providers/ssh-pty-provider-output-state.ts
  • src/main/providers/ssh-pty-provider.test.ts
  • src/main/providers/ssh-pty-provider.ts
  • src/main/providers/ssh-pty-visible-screen-snapshot.ts
  • src/main/runtime/agent-prompt-submission-runtime.test.ts
  • src/main/runtime/orca-runtime.ts
  • src/main/runtime/rpc/methods/orchestration-federated-worker-start.ts
  • src/main/runtime/rpc/methods/orchestration-migration-behavior.test.ts
  • src/main/runtime/rpc/methods/orchestration-prompt-submit-capability.ts
  • src/relay/pty-handler-output-streaming.test.ts
  • src/relay/pty-handler-spawn-admission.test.ts
  • src/relay/pty-handler.ts
  • src/shared/agent-prompt-codex-ingest.test.ts
  • src/shared/agent-prompt-codex-ingest.ts
  • src/shared/protocol-version.ts
  • src/shared/pty-visible-screen-snapshot.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 5 remain after this review.

Comment on lines +16 to +18
canProvide(): boolean {
return this.supported
}

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Report capability only for the requested PTY.

IPtyProvider.canProvideAuthoritativeBufferSnapshot is an exact-PTY contract. After a successful host probe, the current code returns true for an unknown or exited PTY, but get() returns null because no incarnation is stored.

  • src/main/providers/ssh-pty-visible-screen-snapshot.ts#L16-L18: accept an app PTY ID and require a mapped relay PTY incarnation.
  • src/main/providers/ssh-pty-provider.ts#L80-L81: pass id to visibleScreenSnapshots.canProvide(id).
Proposed fix
-  canProvide(): boolean {
-    return this.supported
+  canProvide(id: string): boolean {
+    return this.supported && this.readIncarnation(this.toRelayPtyId(id)) !== undefined
   }
-  canProvideAuthoritativeBufferSnapshot = (_id: string): boolean =>
-    this.visibleScreenSnapshots.canProvide()
+  canProvideAuthoritativeBufferSnapshot = (id: string): boolean =>
+    this.visibleScreenSnapshots.canProvide(id)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
canProvide(): boolean {
return this.supported
}
canProvide(id: string): boolean {
return this.supported && this.readIncarnation(this.toRelayPtyId(id)) !== undefined
}
Suggested change
canProvide(): boolean {
return this.supported
}
canProvideAuthoritativeBufferSnapshot = (id: string): boolean =>
this.visibleScreenSnapshots.canProvide(id)
📍 Affects 2 files
  • src/main/providers/ssh-pty-visible-screen-snapshot.ts#L16-L18 (this comment)
  • src/main/providers/ssh-pty-provider.ts#L80-L81

Comment on lines +19648 to +19674
private async readCodexPasteScreen(
ptyId: string,
generation: number
): Promise<RuntimeVisibleTerminalState | null> {
if (!this.providerSnapshotPreferredPtys.has(ptyId)) {
const screen = await this.readHeadlessVisibleTerminalState(ptyId)
return screen?.generation === generation ? screen : null
}
const snapshot = await this.captureProviderTerminalBuffer(
ptyId,
{ scrollbackRows: 0 },
generation
)
if (
!snapshot ||
this.getPtyLifecycleGeneration(ptyId) !== generation ||
this.getPtyOutputSequence(ptyId) > snapshot.seq
) {
return null
}
return {
lines: await this.parseVisibleSnapshotLines(snapshot),
isAlternateScreen: snapshot.alternateScreen ?? false,
sequence: snapshot.seq,
generation
}
}

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.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify every other provider-screen reader goes through the bounded wrapper.
rg -n -C4 'captureProviderTerminalBuffer|serializeProviderTerminalBuffer' src/main/runtime/orca-runtime.ts

Repository: stablyai/orca

Length of output: 5345


🏁 Script executed:

#!/bin/bash
sed -n '13260,13325p;13520,13555p;19420,19680p' src/main/runtime/orca-runtime.ts

Repository: stablyai/orca

Length of output: 12337


🏁 Script executed:

#!/bin/bash
rg -n -C6 'function withTimeout|const withTimeout|withTimeout\\(|VISIBLE_TERMINAL_SNAPSHOT_TIMEOUT_MS|readCodexPasteScreen\\(' src/main/runtime/orca-runtime.ts

Repository: stablyai/orca

Length of output: 380


🏁 Script executed:

#!/bin/bash
rg -n -C6 -F \
  -e 'function withTimeout' \
  -e 'const withTimeout' \
  -e 'withTimeout<' \
  -e 'VISIBLE_TERMINAL_SNAPSHOT_TIMEOUT_MS' \
  -e 'readCodexPasteScreen(' \
  src/main/runtime/orca-runtime.ts

Repository: stablyai/orca

Length of output: 5714


🏁 Script executed:

#!/bin/bash
rg -n -C5 -F 'function withTimeout' src
rg -n -C5 -F 'withTimeout = ' src
rg -n -C4 -F 'export function withTimeout' src

Repository: stablyai/orca

Length of output: 3838


🏁 Script executed:

#!/bin/bash
sed -n '1,40p' src/shared/promise-timeout-fallback.ts
sed -n '13317,13425p' src/main/runtime/orca-runtime.ts
rg -n -C4 -F 'assertAgentPromptRequestActive' src/main/runtime/orca-runtime.ts

Repository: stablyai/orca

Length of output: 8797


Bound the provider snapshot read in readCodexPasteScreen.

captureProviderTerminalBuffer can await an unresponsive provider indefinitely. This bypasses the timeout used by serializeProviderTerminalBuffer, so the prompt poll and abort checks cannot complete while the read is pending. Route the read through serializeProviderTerminalBuffer with VISIBLE_TERMINAL_SNAPSHOT_TIMEOUT_MS, and make the read observe options.signal.

Comment on lines +61 to +71
export function countCodexPasteMarker(lines: readonly string[], marker: string): number {
let count = 0
const visibleText = lines.join('')
let cursor = 0
while (cursor <= visibleText.length - marker.length) {
const match = visibleText.indexOf(marker, cursor)
if (match === -1) {
break
}
count += 1
cursor = match + marker.length

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.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- target implementation ---'
cat -n src/shared/agent-prompt-codex-ingest.ts | sed -n '1,100p'

printf '%s\n' '--- call sites ---'
rg -n --glob '*.{ts,tsx,js,jsx}' 'countCodexPasteMarker' .

printf '%s\n' '--- standalone behavior probe ---'
python3 - <<'PY'
def count(lines, marker, max_steps=1000):
    visible_text = ''.join(lines)
    cursor = 0
    occurrences = 0
    steps = 0
    while cursor <= len(visible_text) - len(marker):
        steps += 1
        if steps > max_steps:
            return ("non-terminating", occurrences, cursor)
        match = visible_text.find(marker, cursor)
        if match == -1:
            break
        occurrences += 1
        cursor = match + len(marker)
    return ("terminated", occurrences, cursor)

for lines, marker in [(["abc"], ""), (["abc"], "b"), ([""], ""), (["abc"], "z")]:
    print(repr(lines), repr(marker), count(lines, marker))
PY

Repository: stablyai/orca

Length of output: 3713


🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- existing tests ---'
cat -n src/shared/agent-prompt-codex-ingest.test.ts | sed -n '55,95p'

printf '%s\n' '--- runtime caller ---'
cat -n src/main/runtime/orca-runtime.ts | sed -n '19600,19655p'

printf '%s\n' '--- ingest argument flow ---'
rg -n -C 4 --glob '*.{ts,tsx}' 'ingest\.marker|findCodexPasteMarkerDelta|collectCodexPasteMarkers' src

Repository: stablyai/orca

Length of output: 12848


Reject an empty marker before the scan.

When marker is empty, indexOf returns the current cursor and cursor does not advance. The loop does not terminate.

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.

[Bug]: dispatch --inject to Codex never submits large payloads — trailing Enter is concatenated into the bracketed-paste PTY write (v1.4.159)

1 participant