You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
fix(mobile): match and place pending chat echoes with desktop's rule - #33970
Stacked on #33969: review the commits after a7eaef8.
ELI5
After you send a message in a terminal chat, the phone shows a placeholder bubble until the agent records it. The phone now decides when that bubble is done with desktop's rule. A bubble that is never matched now sits at the bottom, as on desktop.
What Changed
Echo matching (native-chat Fix update checker 406 error and add menu feedback #8). Before: the phone counted matching user rows across the whole loaded window, with its own glue matcher. Paging in an older identical "yes" could clear a new echo before its row landed. If an earlier send never landed, a combined row could clear the sends after it. Now: the phone matches only rows after the point each message was sent, anchored at the first open send, with pasted image paths stripped. This is desktop's rule.
native-chat-pending-occurrence.ts moved as-is to src/shared/.
The rule part of native-chat-pending.ts is now src/shared/native-chat-pending-sends.ts, including the 8-send cap as NATIVE_CHAT_PENDING_SEND_LIMIT. The cache and launch-prompt code stay in the renderer.
The mobile matcher, counters, GLUE_SLIDE_BUDGET and the baseline module are deleted. A small adapter in mobile-native-chat-pending-echo.ts maps phone sends to NativeChatPendingSend.
Unmatched echo placement (Fix excessive re-renders causing UI lag #26). Before: an echo the TUI never recorded stayed where it was sent (A1, hi, A2). Now: transcript rows, then the streaming bubble, then unmatched echoes (A1, A2, hi), the order desktop's messageSortRank produces. The phone appends its three groups in that order rather than re-sorting rows the host already ordered, so compareMessages is not lifted.
Why
Desktop logic wins in the mobile/desktop parity audit. The phone's whole-window count could clear an echo early. One boundary-based rule fixes that and keeps both apps aligned.
Linked Issue
Part of the mobile/desktop parity audit
Visual Proof
No device captures for this fleet. The change adopts desktop's existing behavior: messagesAfterPendingBoundary and the occurrence rule in native-chat-pending-occurrence.ts, and pending rows' sort rank in native-chat-session-assembler.ts.
Testing
I manually tested these changes locally
Automated tests added/updated, or explained why not below
The desktop pending tests moved with the rule. Mobile tests that pinned the old matcher are deleted or rewritten. The "A never lands" case now expects desktop's result. New regression tests cover glued photo captions and folded image boundaries; both fail without their fix. Focused root tests (shared and renderer pending, delivery, assembler, subagent sections) and mobile src/session pass.
AI Disclosure
Review
Codex review, 3 rounds, all findings fixed:
Restored the last loaded row as the send point while reconnecting.
A photo caption glued to the next send no longer leaves that send stuck.
Folded image rows keep preview binding.
The preview echo type now carries sentAt.
Matching uses image-normalized rows like desktop.
Open sends are capped at 8 like desktop.
Agent skill upstream boundary
Not applicable, or this change copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.
Notes
Mobile behavior removed (desktop wins):
An unmatched echo no longer stays where it was sent; it sits at the bottom.
Matching no longer counts the whole loaded window.
If an earlier send never lands, a combined row no longer clears the sends after it.
An echo is hidden once its row lands and pruned when the agent replies, instead of being removed at once.
Sends made while the transcript is still loading no longer re-point at the first loaded row; they fall back to send time, as on desktop.
At most 8 open echoes per chat; the oldest is dropped.
Desktop: no behavior change. Code moved; pendingSendsAsMessages is split so the phone can call visibleNativeChatPendingSends.
Compatibility: phone-local state only, no wire change. As on desktop over SSH, a send made while loading compares the phone's clock with host row times.
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)
The change centralizes pending-send reconciliation and chat presentation logic in shared modules. Mobile send tracking now uses transcript boundaries and shared matching helpers, and pending messages are appended after folded messages. Mobile and renderer components use shared helpers for message copying, tool rows and runs, tool output, task counts, and diff formatting. Agent selection and transcript-readability checks also use shared availability helpers. The structured background-task view now receives foreground commands derived from session state.
Priority: Normal
Merge Risk: Moderate · up to 6cf4b
Resolve the stale-identity concern and the two pending-message reconciliation defects before merging; photo previews can attach to older messages, and an echoed send can remain pending.
Pre-merge checks: 4 · 1
Failed checks: 1 warning
Check name
Status
Explanation
Resolution
Docstring Coverage
Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Documentation-only edits and deletions within surv…
Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed checks: 4 passed
Check name
Status
Explanation
Title check
The title clearly and concisely describes the primary change: aligning mobile pending chat echo matching and placement with desktop behavior.
Linked Issues check
Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check
Check skipped because no linked issues were found for this pull request.
Description check
The description is detailed and covers the required ELI5, changes, rationale, testing, review, notes, and checklist sections. It identifies the parity audit but does not provide an actual linked issue…
Full details: Description check
Explanation
The description is detailed and covers the required ELI5, changes, rationale, testing, review, notes, and checklist sections. It identifies the parity audit but does not provide an actual linked issue under “Fixes #”. The Visual Proof section also does not use the exact N/A wording required for changes without visual impact.
Full details: Docstring Coverage
Explanation
Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 39 functions across 50 files. (1 skipped: 1 unsupported.)
Resolution
Write docstrings for the functions missing them to satisfy the coverage threshold.
Finishing Touches: 💡 2
📝 Generate docstrings: 💡
Commit to this branch
Create a new PR
🛠️ Fix failing CI checks: 💡
Commit to this branch
Create a new PR
Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
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.
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 7 remain after this review.
The reason will be displayed to describe this comment to others. Learn more.
Minor · Functional Correctness · Quick win
Apply the shared timestamp fallback to image-preview matching.
foldedBoundaryIndex returns -1 when afterMessageId is null. It returns undefined when the boundary row is paged out. In both cases the filter at Line 179 keeps every candidate. The shared rule in messagesAfterPendingBoundary behaves differently. For a null boundary without afterEmptyTranscriptSessionId, it keeps only rows after the send timestamp. For a missing boundary id, it also falls back to the send timestamp.
Trigger: the user sends a captioned photo while the transcript is hydrating. captureSendOrigin records afterMessageId: null because the tail is empty. Settled history then arrives with an older identical look[Image #1] prompt. findLandedImagePreviewEchoes binds the new photo's preview URI to the old row. retireLandedMobileNativeChatPending then strips images from the pending echo. The shared prunePendingSends still keeps the caption, because its timestamp filter excludes the old row. Result: the old turn shows the new thumbnail, and the new echo has no photo.
Fix: widen PendingImagePreviewEcho to include the boundary timestamp fields. Then apply the timestamp fallback when no index boundary applies. Callers pass MobileNativeChatPendingMessage, which already has these fields. The coding guideline says "Before writing new logic ... check whether an existing implementation already does the job." A better long-term fix is to export the timestamp predicate from native-chat-pending-sends.ts and reuse it here.
sed -n '1,180p' mobile/src/session/use-mobile-native-chat-active-resolution.ts
rg -n -F --glob '*.ts' --glob '*.tsx' -e 'activeChatAgentConfirmed' -e 'agentConfirmed' mobile/src src/renderer/src src/shared ||test"$?" -eq 1
sed -n '1,240p' src/renderer/src/components/native-chat/native-chat-pane-resolution.ts
sed -n '1,240p' src/shared/native-chat-availability.ts
Repository: stablyai/orca
Length of output: 10207
Script executed:
set -o pipefail
printf'%s\n''--- mobile native-chat controller ---'
sed -n '1,230p' mobile/src/session/use-mobile-native-chat-controller.ts
printf'%s\n''--- mobile native-chat message send ---'
sed -n '1,260p' mobile/src/session/use-mobile-native-chat-message-send.ts
printf'%s\n''--- terminal write lock ---'
sed -n '1,240p' mobile/src/session/native-chat/mobile-native-chat-terminal-write-lock.ts
printf'%s\n''--- native chat write operation ---'
sed -n '1,150p' mobile/src/session/mobile-session-write-operations.ts
Repository: stablyai/orca
Length of output: 25171
Keep the remembered identity ahead of a conflicting launch hint.
When live status is absent, nativeChatAvailabilityAgent selects the launch hint before the remembered identity. The resolver then marks the launch agent as confirmed and discards the remembered session.
useMobileNativeChatController enables terminal chat writes when activeChatAgentConfirmed is true. The message sender uses the same terminal handle while its agent reference contains the launch agent. A statusless frame can therefore send through the current terminal with the wrong agent identity.
Desktop uses the same shared precedence, but that does not remove the mobile path's explicit contract to retain identity until a new PTY handle replaces it. Preserve the remembered agent and session for a conflicting launch hint, and keep agentConfirmed false until live evidence replaces it. Launch-only tabs without a remembered identity may retain their existing behavior.
Files skipped from review as they are similar to previous changes: 1
src/renderer/src/i18n/en-runtime-required.json
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 4 remain after this review.
The reason will be displayed to describe this comment to others. Learn more.
Major · Data Integrity & Integration · Heavy lift
Do not bind a hydration-time photo to older history.
If a user sends a photo while the transcript is empty and unsettled, captureSendOrigin records no row boundary. When an older photo with the same caption arrives during hydration, findLandedImagePreviewEchoes can select it without checking sentAt. For an image-only send, retirement then removes the pending photo and attaches its local preview to the older row. Apply the send-time fallback to preview candidates when no authoritative boundary exists. (raw.githubusercontent.com)
The reason will be displayed to describe this comment to others. Learn more.
Major · Functional Correctness · Heavy lift
Preserve source-row send boundaries through image folding.
If the raw tail at send time is an image-source row, normalization can fold that row into a later prompt and remove its ID from echoRows. Shared retirement and visibility then fall back from the captured row boundary to timestamps. A following send glued into that prompt can remain pending when the prompt and source have the same timestamp, even after an assistant reply. Preserve the source-row boundary when matching folded rows, including sends represented in the folded prompt. (raw.githubusercontent.com)
…hed ones
Uses desktop's rule: rows before the send no longer retire an echo, a send that
never lands no longer lets later glued sends retire, and an unmatched echo sits
below later turns.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #33969: review the commits after a7eaef8.
ELI5
After you send a message in a terminal chat, the phone shows a placeholder bubble until the agent records it. The phone now decides when that bubble is done with desktop's rule. A bubble that is never matched now sits at the bottom, as on desktop.
What Changed
native-chat-pending-occurrence.tsmoved as-is tosrc/shared/.native-chat-pending.tsis nowsrc/shared/native-chat-pending-sends.ts, including the 8-send cap asNATIVE_CHAT_PENDING_SEND_LIMIT. The cache and launch-prompt code stay in the renderer.GLUE_SLIDE_BUDGETand the baseline module are deleted. A small adapter inmobile-native-chat-pending-echo.tsmaps phone sends toNativeChatPendingSend.messageSortRankproduces. The phone appends its three groups in that order rather than re-sorting rows the host already ordered, socompareMessagesis not lifted.Why
Desktop logic wins in the mobile/desktop parity audit. The phone's whole-window count could clear an echo early. One boundary-based rule fixes that and keeps both apps aligned.
Linked Issue
Part of the mobile/desktop parity audit
Visual Proof
No device captures for this fleet. The change adopts desktop's existing behavior:
messagesAfterPendingBoundaryand the occurrence rule innative-chat-pending-occurrence.ts, and pending rows' sort rank innative-chat-session-assembler.ts.Testing
The desktop pending tests moved with the rule. Mobile tests that pinned the old matcher are deleted or rewritten. The "A never lands" case now expects desktop's result. New regression tests cover glued photo captions and folded image boundaries; both fail without their fix. Focused root tests (shared and renderer pending, delivery, assembler, subagent sections) and mobile
src/sessionpass.AI Disclosure
Review
Codex review, 3 rounds, all findings fixed:
sentAt.Agent skill upstream boundary
Notes
Mobile behavior removed (desktop wins):
Desktop: no behavior change. Code moved;
pendingSendsAsMessagesis split so the phone can callvisibleNativeChatPendingSends.Compatibility: phone-local state only, no wire change. As on desktop over SSH, a send made while loading compares the phone's clock with host row times.
Checklist
N/Awith reasonpnpm lint,pnpm typecheck,pnpm test, andpnpm buildpass (or CI will cover; local preferred)