Skip to content

Recover native chat messages when terminal delivery is unconfirmed - #12643

Closed
brennanb2025 wants to merge 8 commits into
mainfrom
brennanb2025/fix-stale-native-chat-queue
Closed

brennanb2025 wants to merge 8 commits into
mainfrom
brennanb2025/fix-stale-native-chat-queue

Conversation

@brennanb2025

@brennanb2025 brennanb2025 commented Aug 5, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • confirm native-chat delivery from exact live UserPromptSubmit acknowledgements, with transcript fallback, instead of inferring terminal UI state or matching provider-specific strings
  • after 8 seconds without confirmation, remove the optimistic echo, restore the message ahead of newer composer edits, and keep the user in native chat with a persistent recovery card and optional Show terminal action
  • carry acknowledgement identity across desktop, mobile, relay, and SSH paths; ignore replayed or synthetic events, bound occurrence history, and assign duplicate prompt acknowledgements one-to-one

The recovery UI says:

Message wasn’t sent
The terminal may be waiting for input. Your message is still in the composer.
Show terminal

Validation

  • same-model senior review loop completed; final review returned CLEAN
  • desktop/main focused tests: 14 files, 639 tests passed
  • mobile focused tests: 9 files, 117 tests passed
  • additional lifecycle/runtime focused tests: 58 passed
  • renderer and mobile typechecks passed
  • oxlint, React Doctor, max-lines ratchet, localization catalog/extraction/coverage, and git diff --check passed
  • Electron end-to-end QA confirmed the optimistic echo clears, the exact message returns to the composer, native chat remains active, and Show terminal reveals the unresolved terminal input

Electron evidence

Unconfirmed message restored in native chat

Native chat recovery UI

Show terminal reveals the pending terminal interaction

Pending terminal interaction

@coderabbitai

coderabbitai Bot commented Aug 5, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The change adds prompt-submission metadata across agent hooks, relay transport, pane status history, IPC, and runtime projections. Renderer and mobile native chat now track optimistic sends, match prompt acknowledgements, restore unconfirmed messages after timeout, and expose delivery failure state. Both platforms render recovery notices that preserve the message and provide a terminal-navigation action. Tests cover digest generation, history bounds, acknowledgement ordering, send submission, timeout recovery, and recovery UI behavior.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 10.71% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly and concisely describes the primary change: recovering native chat messages when terminal delivery is unconfirmed.
Description check ✅ Passed The description includes the change summary, UI evidence, and extensive validation, but it omits explicit Security Audit, Notes, and complete AI Review Report sections.
✨ Finishing Touches 💡 1
📝 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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
mobile/src/session/mobile-native-chat-send.ts (1)

119-135: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Preserve the interactive-prompt refusal through draft clearing.

When the guarded clear receives refusedReason: 'interactive-prompt', this function converts it to false. The caller then restores the draft but reports "Message not sent" instead of the terminal-prompt recovery message.

Return a typed outcome or refusal reason from clearMobileNativeChatInput. Handle interactive-prompt like the body-send refusal. Add a seeded-launch-draft regression test.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 8e502315-0a0a-45ee-a8a6-d37b1f47da60

📥 Commits

Reviewing files that changed from the base of the PR and between eebaf47 and 60de278.

📒 Files selected for processing (16)
  • mobile/src/session/mobile-native-chat-image-send.test.ts
  • mobile/src/session/mobile-native-chat-image-send.ts
  • mobile/src/session/mobile-native-chat-send.test.ts
  • mobile/src/session/mobile-native-chat-send.ts
  • mobile/src/session/use-mobile-native-chat-message-send.test.ts
  • mobile/src/session/use-mobile-native-chat-message-send.ts
  • mobile/src/terminal/terminal-send-rpc-response.ts
  • src/main/runtime/orca-runtime.ts
  • src/main/runtime/rpc/methods/terminal.ts
  • src/main/runtime/rpc/terminal-send.test.ts
  • src/renderer/src/components/native-chat/NativeChatComposer.test.tsx
  • src/renderer/src/components/native-chat/NativeChatComposer.tsx
  • src/renderer/src/components/native-chat/native-chat-terminal-prompt-guard.ts
  • src/renderer/src/i18n/locales/en.json
  • src/shared/runtime-types.ts
  • src/shared/terminal-wait-blocked-detection.ts

@brennanb2025

Copy link
Copy Markdown
Contributor Author

Electron QA

Validated at 484bdd425b:

  • An ordinary native-chat send was blocked while the terminal model picker was active.
  • Orca returned to the terminal and displayed “Finish the terminal prompt before sending.”
  • No optimistic chat message appeared.
  • Returning to native chat showed the exact draft preserved.

Blocked send returns to the active terminal prompt

Native-chat draft remains preserved

Mobile QA

The PR mobile app loaded in the iOS simulator, and the focused mobile suite passed (49 tests). Live pairing to the isolated PR runtime was interrupted by a host daemon-version restart, so the target mobile workflow was not claimed as end-to-end validated.

@coderabbitai

coderabbitai Bot commented Aug 6, 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.

@brennanb2025 brennanb2025 changed the title Guard native chat sends while terminal prompts are active Recover native chat messages when terminal delivery is unconfirmed Aug 6, 2026

@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

🧹 Nitpick comments (1)
mobile/src/session/use-mobile-native-chat-delivery-recovery.test.ts (1)

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

Remove the deprecated react-test-renderer warning guard.

This hook test only renders a null Harness, so keep the existing state-based assertions and replace the act/create/update/unmount lifecycle with the supported Testing Library harness. Move the related react-test-renderer dependency cleanup into the broader mobile test migration.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 9baef488-a26f-4501-ac40-64fe7b89489d

📥 Commits

Reviewing files that changed from the base of the PR and between 211b2d1 and a18365f.

📒 Files selected for processing (50)
  • mobile/src/session/MobileNativeChatDeliveryRecoveryNotice.tsx
  • mobile/src/session/MobileNativeChatOverlay.tsx
  • mobile/src/session/MobileNativeChatView.test.ts
  • mobile/src/session/MobileNativeChatView.tsx
  • mobile/src/session/mobile-native-chat-controller-contract.ts
  • mobile/src/session/mobile-native-chat-pending-echo.ts
  • mobile/src/session/mobile-native-chat-prompt-delivery.test.ts
  • mobile/src/session/mobile-native-chat-prompt-delivery.ts
  • mobile/src/session/mobile-native-chat-view-styles.ts
  • mobile/src/session/use-mobile-native-chat-controller.ts
  • mobile/src/session/use-mobile-native-chat-delivery-recovery.test.ts
  • mobile/src/session/use-mobile-native-chat-delivery-recovery.ts
  • mobile/src/session/use-mobile-native-chat-drafts.ts
  • src/main/agent-hooks/hook-status-session-tabs-invalidation.test.ts
  • src/main/agent-hooks/hook-status-session-tabs-invalidation.ts
  • src/main/agent-hooks/server.test.ts
  • src/main/agent-hooks/server.ts
  • src/main/index.ts
  • src/main/runtime/orca-runtime-hook-agent-status-projection.test.ts
  • src/main/runtime/orca-runtime.ts
  • src/main/ssh/ssh-relay-session.ts
  • src/relay/agent-hook-server.test.ts
  • src/relay/agent-hook-server.ts
  • src/renderer/src/components/native-chat/NativeChatComposer.test.tsx
  • src/renderer/src/components/native-chat/NativeChatComposer.tsx
  • src/renderer/src/components/native-chat/NativeChatDeliveryRecoveryNotice.test.tsx
  • src/renderer/src/components/native-chat/NativeChatDeliveryRecoveryNotice.tsx
  • src/renderer/src/components/native-chat/NativeChatView.tsx
  • src/renderer/src/components/native-chat/native-chat-composer-types.ts
  • src/renderer/src/components/native-chat/native-chat-pending.ts
  • src/renderer/src/components/native-chat/native-chat-prompt-delivery.test.ts
  • src/renderer/src/components/native-chat/native-chat-prompt-delivery.ts
  • src/renderer/src/components/native-chat/native-chat-pty-send-queue.ts
  • src/renderer/src/components/native-chat/native-chat-runtime-send.ts
  • src/renderer/src/components/native-chat/use-native-chat-composer-attachments.ts
  • src/renderer/src/components/native-chat/use-native-chat-composer-target.ts
  • src/renderer/src/components/native-chat/use-native-chat-delivery-recovery.ts
  • src/renderer/src/components/native-chat/use-native-chat-pending-delivery.ts
  • src/renderer/src/components/native-chat/use-native-chat-send-lifecycle.test.tsx
  • src/renderer/src/components/native-chat/use-native-chat-send-lifecycle.ts
  • src/renderer/src/hooks/useIpcEvents.test.ts
  • src/renderer/src/hooks/useIpcEvents.ts
  • src/renderer/src/i18n/locales/en.json
  • src/renderer/src/store/slices/agent-prompt-submission-history.ts
  • src/renderer/src/store/slices/agent-status-prompt-submission.test.ts
  • src/renderer/src/store/slices/agent-status.ts
  • src/shared/agent-hook-listener.test.ts
  • src/shared/agent-hook-listener.ts
  • src/shared/agent-hook-relay.ts
  • src/shared/agent-status-types.ts

Comment on lines +19 to +30
const check: MobileNativeChatDeliveryCheck = {
pendingId: 'pending-1',
draftKey: 'draft',
pendingKey: 'pending',
text: 'hello',
normalizedText: 'hello',
baselineTailMessageId: null,
submittedAt: 20,
expectedDigest: mobileNativeChatPromptDigest('hello'),
baseline: { streamId: 'stream', sequence: 2, digest: 'old', receivedAt: 10 },
deadline: null
}

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 | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

rg -n -C 3 "submittedAt|export type MobileNativeChatDeliveryCheck" mobile/src/session/mobile-native-chat-prompt-delivery.ts mobile/src/session/mobile-native-chat-prompt-delivery.test.ts

Repository: stablyai/orca

Length of output: 2849


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "## Type definition"
sed -n '1,45p' mobile/src/session/mobile-native-chat-prompt-delivery.ts

echo
echo "## Test imports and type usages"
sed -n '1,70p' mobile/src/session/mobile-native-chat-prompt-delivery.test.ts
echo
sed -n '70,105p' mobile/src/session/mobile-native-chat-prompt-delivery.test.ts

echo
echo "## Check whether submittedAt is read/required anywhere"
rg -n "submittedAt|MobileNativeChatDeliveryCheck" mobile/src/session/mobile-native-chat-prompt-delivery.ts mobile/src/session/mobile-native-chat-prompt-delivery.test.ts

Repository: stablyai/orca

Length of output: 5794


Remove the stale submittedAt properties.

MobileNativeChatDeliveryCheck does not define submittedAt, and the implementation does not read it. Keep the named type literals clean at lines 26, 51, and 87.

Comment on lines +5 to +9
export function NativeChatDeliveryRecoveryNotice({
onShowTerminal
}: {
onShowTerminal?: () => void
}): React.JSX.Element {

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

Hide the terminal action when navigation is unavailable.

onShowTerminal is optional on Lines 5-9. Line 27 always renders an enabled button. If a caller omits the callback, “Show terminal” does nothing.

Render the button only when onShowTerminal exists. Alternatively, make onShowTerminal required and update all callers. Add a test for the callback-absent case.

Proposed fix
-        <Button type="button" variant="secondary" size="sm" onClick={onShowTerminal}>
-          {translate('components.native-chat.toggle.showTerminal', 'Show terminal')}
-        </Button>
+        {onShowTerminal ? (
+          <Button type="button" variant="secondary" size="sm" onClick={onShowTerminal}>
+            {translate('components.native-chat.toggle.showTerminal', 'Show terminal')}
+          </Button>
+        ) : null}

Also applies to: 27-29

Comment thread src/renderer/src/components/native-chat/use-native-chat-pending-delivery.ts Outdated
@brennanb2025

brennanb2025 commented Aug 6, 2026 •

Copy link
Copy Markdown
Contributor Author

Final-head review (7988d8e966) — land after fixes.

The same-model senior pass found one meaningful issue and fixed it: image-bearing sends no longer enter the text-only digest timeout, which could falsely restore an already delivered image message and invite a duplicate retry. The follow-up review returned clean.

Validation:

  • Desktop Electron: submitted text reached the terminal; without an acknowledgement, the optimistic echo disappeared, the exact draft restored once, the recovery card appeared, and Show terminal navigated to the unresolved terminal output.
  • SSH/Linux: a real remote PTY sent UserPromptSubmit through the deployed relay, received HTTP 204, and the renderer digest matched an independently computed SHA-256.
  • Mobile: focused prompt-delivery and recovery tests passed. No live mobile behavior claim is being made.
  • Local: 564 root tests, 14 mobile tests, web typecheck, oxlint, and diff checks passed.
  • CI: all required checks are green, including mobile, cross-version wire compatibility, package, and Windows package jobs; the PR is mergeable.

Performance remains bounded: eight-entry confirmation history/cache, one owned timeout only for confirmable text sends, cleanup on acknowledgement/unmount, and no polling or subprocess loop.

Desktop recovery:
desktop recovery

Terminal confirmation:
terminal confirmation

@brennanb2025

Copy link
Copy Markdown
Contributor Author

Closing this terminal-delivery recovery approach. Current chat sends use the host’s send result, and uncertain terminal delivery is already reported by #23788 without claiming the message was never sent.

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.

1 participant