Files
orca/config/scripts
Jinwoo Hong 3a011e48d1 fix(native-chat): load long chats on the phone by keeping every update within its limit (#28203)
* fix(native-chat): fit chat stream pages in one mobile bridge frame

A long native chat failed to open on a phone with "the shell ended this
stream (overflow)". The host sized each history page to ~1.94 MiB (half
the relay cap), but the mobile app ends any stream whose event frame is
over 640 KiB, so the opening snapshot of a long chat never arrived.
Pages now fit that frame; older rows load through Load earlier as before.

The page also words a stream the app ended in plain language instead of
the protocol's reason.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* refactor(native-chat): keep the outline on the relay page share; plainer end words

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* test(mobile): read the bridge frame cap from its shared definition

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* feat(native-chat): bounded frames for the phone's chat stream

A phone page now asks the host (when it advertises
agent-session.bounded-frames.v1) to keep every agentSession.subscribe
frame within the bridge's 640 KiB frame:

- each row is cut to what the phone draws (tool output to its detail
  length unless it draws as a diff, tool inputs to the diff view's
  length) and then to one row's share of a page, its long strings cut to
  a common length; the row is marked `truncated` with its full size
- history frames carry rows and metadata held to a fixed ceiling; the
  draft queue and the background-task roster go in frames of their own;
  the command list is not sent, since the phone reads it from options
- the queue's admission bound counts whole cards and is sized to one
  frame, so a phone always gets every card whole

Other subscribers get the frames they always did.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* refactor(native-chat): bounded frames review round 1

- the phone waits for the host's capabilities before opening the
  stream, so its opening frame is never sent unbounded
- rows are bounded once, by the reader that sizes the page; the item
  form is an options field and always a function
- one cut rule (`cutLongStrings`) serves the display trims and the fit
- a side list rides an empty history batch rather than a second frame;
  commands changes send a bounded subscriber nothing
- queue admission measures the candidate list once; a legacy queue of
  more cards than fit lists the ones it sends first

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* refactor(native-chat): bounded frames review round 2

- an over-budget legacy queue cuts its text to one common length and
  marks the cards it cut, and no longer names a next card it left out
- one predicate says whether a subscriber is owed the `/` list
- a lighter invariant fixture

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* fix(native-chat): type the shortening guards against named shapes

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* refactor(native-chat): bound every phone's chat stream at the host

The host now bounds frames for every phone (clientKind 'mobile') at the RPC
boundary, so the subscribe param, the host capability and the phone's wait
for the capability probe are gone. Phones of every version already ignore
the stream's slash commands, and a phone reached through the desktop relay
signs in as a phone too.

- A live gap of more than one page reaches a phone as the newest page, so
  no publish outruns the bridge's unacked window; older rows load on scroll.
- Queue admission sizes each card as wide as a failed send can make it.
- Rows are bounded only as a page takes them; the unread row marker is gone
  and a shortened queue card is marked `truncated: true`.
- A bounded subscriber no longer reads or tracks the slash commands.
- The stream-end words are back to main's: `overflow` also covers a page
  that stopped acking, which "too large" would misstate.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* fix(native-chat): count every card in line and never shorten one

Queue admission now counts every card the queue can publish again without
passing admission: waiting and returned cards, and sent cards whose delivery
is not yet accepted, which a refusal or a Stop puts back. The fixed per-card
reserve for a failed send's words is gone.

When a phone's list is over its frame room anyway (an older build's larger
queue, or a card that grew after admission), the phone gets the cards it
sends first, whole, and the rest once those leave. Cards are never shortened,
so no app version can Edit a partial copy over the full draft, and the
`truncated` card flag is gone.

Bounded frames strip only the roster from an event; no event carries the
other side lists.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* refactor(native-chat): bound only phone frames; keep main's queue and page sizes

Queue admission is main's again. The phone's list is bounded where it is
sent: the cards it sends first, whole, so every frame fits for any queue
size and no card is shortened.

History pages keep main's size for every reader but phone subscribe frames.
A reader is now its item form plus its page budget, so desktop pages, the
outline and phone history replies (chunked by the app's bridge) are as on
main, and only bounded frames use the bridge frame's share.

Drops the queue's `nextQueuedMessageId` rewrite, which no phone reads, and a
test that duplicated the prefix check.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* refactor(native-chat): bound phone rows by size alone; final tidy

Rows the journal already caps at a 16 KiB head gain little from a second,
display-shaped cut, and the diff check behind it passed a character count as
a line count. A row is now held to its frame share by shortening alone, and
native-chat-diff is back to main's.

Unexports file-local bounds, fixes a capability-era test comment, and adds
tests for string shortening and a phone's stop-note refresh.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* test(native-chat): assert a phone's stop-note refresh pages

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* test(native-chat): run the bounded-frames suite in the Node runtime project

It opens a real SQLite journal.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

* fix(native-chat): bound legacy dispatch reasons on read

Rows written before the write-side bound carry a provider's full error text,
which every page and phone frame attaches with its submission.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
2026-10-13 13:39:34 -04:00
..
2026-05-15 05:44:25 -04:00