mirror of
https://github.com/stablyai/orca.git
synced 2026-10-30 16:47:34 +00:00
efcf8f0888d7db170126b428facacb3cb4e0f682
1205
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
efcf8f0888 |
fix(mobile): keep terminal scroll gestures from becoming taps (#31445)
Fixes #25457 Fixes #25458 Supersedes community PR #25432, community PR #25435. Original contributor credit is preserved below. Co-authored-by: Prateek Rungta <prungta2@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
251bbf7bb0 |
fix(mobile): mount sessions with saved preferences (#31443)
Fixes #30517 Supersedes community PR #30518. Original contributor credit is preserved below. Co-authored-by: Wooseong Kim <innocarpe@gmail.com> |
||
|
|
11a1cc4d8b |
fix(mobile): load folder workspace agent presets on the correct host (#31442)
Fixes #16215 Supersedes community PR #16216. Original contributor credit is preserved below. Co-authored-by: iBakuman <67883044+iBakuman@users.noreply.github.com> |
||
|
|
7a2057e65c |
fix(mobile): retire old touch gestures before snapshot replay (#31437)
Fixes #30300 Supersedes community PR #30312. Original contributor credit is preserved below. Co-authored-by: Wooseong Kim <innocarpe@gmail.com> |
||
|
|
21322a56aa |
fix(mobile): cancel stale shortcut list drags (#31436)
Fixes #30302 Supersedes community PR #30314. Original contributor credit is preserved below. Co-authored-by: Wooseong Kim <innocarpe@gmail.com> |
||
|
|
ea401d7164 |
fix(mobile): restore dictation after iOS audio interruptions (#31435)
Fixes #27498 Supersedes community PR #30297. Original contributor credit is preserved below. Co-authored-by: Wooseong Kim <innocarpe@gmail.com> |
||
|
|
0dbc6aa3c4 |
fix(mobile): select Unicode words at rendered cell boundaries (#31434)
Fixes #30301 Supersedes community PR #30325. Original contributor credit is preserved below. Co-authored-by: Wooseong Kim <innocarpe@gmail.com> |
||
|
|
8b9ba72c3b |
fix(mobile): retire stale terminal stream fits and callbacks (#31432)
Retire old terminal stream callbacks and delayed fits across reconnects, unsubscribe, terminal replacement, and synchronous termination. Preserve fresh output and the latest accepted fit. Fixes #30505 Adapted from community PR #30506. Co-authored-by: Wooseong Kim <innocarpe@gmail.com> |
||
|
|
46b83a9529 |
fix(mobile): drag terminal selection with the long-press finger (#31433)
Let the finger that creates a long-press selection extend it through the existing selection-drag dispatcher. Verified with native iPhone touches and focused lifecycle tests. Fixes #30554 Adapted from community PR #30555. Co-authored-by: Wooseong Kim <innocarpe@gmail.com> |
||
|
|
45ee56e1a5 |
fix(mobile): preserve native iOS dictation placeholders (#31356)
Preserve UIKit recognition placeholders during equal-text Fabric synchronization and reset recognition state when the native input is recycled. Adapted from community PR #26644 by Shahar Mor. Verified with matched source-built native Debug lifecycle probes for both input classes; physical spoken dictation remains in the final manual batch. Co-authored-by: Shahar Mor <shaharmor1@gmail.com> |
||
|
|
6de974ca5b |
Mobile chat: remove the Tools toggle and its open-everything mode (#31188)
* fix(mobile): remove the chat's Tools toggle and its open-everything mode * test(mobile): keep the scroll-ownership tests' runs drawn without the Tools default * test(mobile): the scroll-ownership harness opens settled turns as the old Tools default did |
||
|
|
12e0b37293 |
fix(mobile): keep dictation controls in sync with host settings (#31349)
Honor enabled voice settings across terminal/chat controls and capture. Refresh hold/toggle state on connection, focus and foreground while fencing stale replies. Adversarial review: 63 existing tests plus six disable/late-finish/unmount/settings ownership probes pass; final required CI and aggregate gate pass. Existing native screenshots and microphone lifecycle exercise are attached to the PR. Fixes #30307. Fixes #30511. Adapted from community #30316 and #30513 by @innocarpe. Co-authored-by: Wooseong Kim <innocarpe@gmail.com> |
||
|
|
fa35698db5 |
fix(mobile): keep keyboard accessory row height stable (#31363)
fix(mobile): keep keyboard accessory row height stable Stretch the dismiss control within the accessory row so showing it no longer adds height and needlessly reduces the terminal viewport. Adapted from community PR #23511 by @innocarpe. Co-authored-by: Wooseong Kim <innocarpe@gmail.com> Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> |
||
|
|
f19ce3635b |
fix(mobile): show the bundled whale on DeepSeek Build tabs (#31359)
fix(mobile): reuse DeepSeek whale for Build tabs Adapted from community PR #30525 with a focused component regression and native current-shell validation. test(mobile): use checked icon renderer guard test(mobile): keep icon diagnostic with review evidence Native before/after captures cover this low-impact asset mapping; retain the local fail/pass diagnostic outside the product diff. Use checked optional type for mobile icon lookup Co-authored-by: Wooseong Kim <innocarpe@gmail.com> |
||
|
|
3fd90e94e0 |
fix(mobile): preserve live palettes on identical themes (#31355)
fix(mobile): skip identical terminal theme assignments Preserve live TUI palette changes and contrast caches on repeated normalized host themes. Keep explicit foreground recovery repaints and published desktop contrast overrides. Co-authored-by: bbingz <zzb@gxsmjx.com> |
||
|
|
3ed0bb4dfc |
fix(mobile): match home-indicator inset to terminal input panel (#31354)
fix(mobile): paint terminal dock bottom safe area Adapted from community PR #30318. Co-authored-by: Wooseong Kim <innocarpe@gmail.com> |
||
|
|
6537842f0c |
fix(mobile): blend workspace header into status-bar safe area (#31353)
fix(mobile): match workspace header safe-area surface Adapted from community PR #30531 with native iOS simulator before/after validation. Co-authored-by: Wooseong Kim <innocarpe@gmail.com> |
||
|
|
03f4ce3d6b |
fix(mobile): show the session title beside the tabs on a landscape phone (#30879)
The served session page (react-native-web) reads flex: 0 as flex-basis 0%, so the title block collapsed to zero width beside the tab strip. flex: -1 is natural width capped at 160, shrinking only when the row overflows, on both web and native. |
||
|
|
fece03b5ac |
fix(orcad): voice dictation on headless orca serve hosts (#31217)
* fix(orcad): voice dictation on headless orca serve hosts
Mobile dictation against an orca serve host answered speech_unavailable
since serve moved to orcad: orcad never installed the speech service
factories, and the model manager and download imported Electron.
The model download now takes a host request opener (Chromium net on the
desktop, Node https on orcad, with redirects mapped to the same event),
the STT worker ships in the orcad slot, and orca serve hands orcad the
installed app's on-device recognizer. A host without a recognizer (an
SSH or WSL managed slot) lists only cloud models and sends a phone that
picked an on-device model to the setup sheet. Mobile names the fix when
an older server still has no speech at all.
The parity test now requires both roots to install speech.
Reported-by: delorean2020 (@delorean2020)
* fix(orcad): retry Node download drops, stage the recognizer on Windows, review fixes
- Node https errors (ECONNRESET, ETIMEDOUT, ...) are retryable, so an
interrupted orcad model download resumes (Codex P2).
- On Windows orca serve hands orcad a per-version copy of the recognizer
in the profile, so a loaded addon never locks the app's files against
an update (Opus).
- Drop a recognizer candidate path that could never match; one SAFETY
helper per test file instead of repeated casts; comments.
* fix(orcad): cloud speech is ready only with a key this host can decrypt; prune superseded recognizer copies
Codex round 2: a desktop-sealed OpenAI key exists on disk but orcad's
secret store cannot open it, so the catalog reported cloud models ready
and dictation failed at the end. orcad's model manager now proves the
key decrypts. The desktop keeps its existence check (no keychain prompt).
* fix(orcad): never prune staged recognizers before the profile lock; copy linked packages as real files
Codex round 3. ref-oss check: versioned copies are cleaned only by the
process that owns them, so the pre-lock prune is dropped rather than
patched.
* fix(orcad): remove a partial recognizer copy when staging fails (Codex round 4)
* fix(speech): never hand out another store's ciphertext as the OpenAI key (Codex round 5)
* refactor(orcad): no recognizer copy on Windows; Windows orcad keeps cloud dictation only
Design check after review round 5: every remaining finding came from the
Windows copy that dodges addon file locks. Dropping it shrinks the
surface to a path lookup; Linux and macOS serve keep on-device models.
* fix(speech): name the fix when an old server refuses a mic tap; always set the recognizer env; retry ENOTFOUND (final Opus review)
* fix(managed-server): probe install completeness relative to the slot dir
CI: shipping stt-worker.js added one more absolute slot path to the
install-complete probe, pushing it further past the exec-size budget.
Testing each artifact relative to the slot (inside a subshell cd) cuts
the probe from ~12.2 KB to ~3.6 KB, so it drops off the over-budget
list instead of raising a ceiling.
* fix(mobile): pin host.platform's read to a platform type, independent of which zod copy resolves
With mobile's own dependencies installed (which CI does for any PR that
touches mobile/), the e2e program resolves two zod copies, and the
salvaged platform arm from src/shared inferred as {}. The schema now
narrows through an explicit platform guard.
* fix(mobile): keep the host.platform read line intact for its RPC mutant; narrow in a second step
Mobile verify: the recorded-RPC mutant anchors on the read line, which
the previous guard rewrote, so the mutant could not apply.
* refactor(cli): app-executable resolution in its own module; drop the platform guard now zod is aligned
launch.ts went one line past max-lines with main; resolveAppRoot,
resolveForegroundOrcaExecutable, getExecutableAppArgs and
stripElectronRunAsNode move to orca-app-executable.ts (re-exported, so
callers are unchanged). With mobile and root on one zod (#31260), the
host.platform schema no longer needs the extra narrowing step.
* refactor(cli): import app-executable helpers from their module; no re-exports from launch.ts
|
||
|
|
ecc4a6cacf |
fix(mobile): show embedded images in Markdown previews (#31304)
Render standalone embedded images in Markdown file previews and open them in the existing full-screen viewer. Resolve relative paths through authorized paired-host reads. Thanks to @dudupii for reporting and diagnosing #24468 and implementing the broader solution in #24469; related editor integration is preserved in #25594. This independently implemented preview fix leaves editor image serialization, inline images, and pinch zoom as separate work. Validation: focused tests, iPhone simulator proof, and all CI checks passed. |
||
|
|
a237b031df |
fix(mobile): open relative Markdown file links (#31298)
Open relative Markdown file links in the mobile file preview with workspace and execution-host scoping. Thanks to @tomasklime for the report and diagnosis in #30368. Covers read-mode links; rich-editor links remain separate. Validation: focused tests, iPhone simulator proof, and all CI checks passed. |
||
|
|
513c24bc16 |
fix(mobile): preserve unsent terminal commands across workspace navigation (#31314)
Persist unsent buffered terminal commands per paired host, workspace, execution host, and surface across navigation. Preserve newer typing during storage hydration and clear successfully sent drafts. Validation: draft/storage/bridge tests, signed iPhone simulator build, and all CI checks passed. |
||
|
|
4f70d6cee5 |
fix(mobile): refresh staged files when returning from review (#31295)
Refresh Source Control when returning from review so staged files and commit availability reflect completed mutations. Validation: focused loader tests, iPhone simulator proof, and all CI checks passed. |
||
|
|
a308277f9a |
fix(deps): root and mobile resolve one zod, and CI fails if they drift (#31260)
* fix(deps): root and mobile resolve one zod, and CI fails if they drift
Mobile type-checks src/shared zod schemas. With mobile on zod 4.6.5 and the
root on 4.6.7, TypeScript treated them as unrelated packages, so wrapped
schemas inferred {} (tests/e2e/helpers/phone-feed-list.ts:154) whenever a
job installed mobile deps.
* fix(deps): guard every package mobile's tsconfig remaps, not only zod
* test(ci): pin the shared-dependency version check to the static phase
|
||
|
|
c93514db5b |
refactor(protocol): each area owns its published runtime capability list (#30641)
* refactor(protocol): each area owns its published runtime capability list RUNTIME_CAPABILITIES now spreads one array per area instead of a flat hand-appended list, and runtime-capability-constants.ts is split into the areas that own each constant. The published set is unchanged and pinned by an exact sorted test. * test: preserve capability baseline while allowing additions |
||
|
|
f812c67a05 |
feat(native-chat): Codex chats list and run the skills Codex loaded (#30937)
* feat(native-chat): Codex chats list and run the skills Codex loaded A Codex chat's skill list now comes from Codex itself (`skills/list`), live while the chat runs and refreshed when Codex says its skills changed, and from a short-lived Codex check while the chat is at rest. Plugin skills keep Codex's `plugin:skill` names, disabled skills drop out, and each skill file is its own row. A skill picked from the list reaches Codex as that exact skill, beside the unchanged `$name` text, on hosts that take it. * test(native-chat): account for Codex's skill list call; send without a trailing argument * fix: align skill probes with chat launches and preserve picked mentions * fix: distinguish skill choices in phone accessibility labels * fix: clear picked skill files when queued text changes * fix(native-chat): refresh resting commands when launch settings change * fix: keep WSL agent refresh on its execution host * fix: preserve skill selection at read and send boundaries * fix: narrow agent identity for selected skill notice * fix: use checked agent predicate for skill notice label * test: retain locale in composer translation mocks |
||
|
|
749aadcbc8 |
fix(mobile): pick a cloud dictation model from the phone (#31221)
* fix(mobile): pick a cloud dictation model from the phone The dictation setup sheet and Voice settings showed OpenAI models as read-only text, so a host that lists only cloud models (Windows orca serve, managed SSH or WSL servers) left the phone with nothing to pick. A ready cloud row is now selectable through the same speech.dictation.setup modelId write as a downloaded model; a cloud row without a key says to add it on the paired computer instead of "Set up on desktop". Refs #31174 Reported-by: delorean2020 (@delorean2020) * style(mobile): template literal on the re-indented delete label * test(mobile): complete the setup fixtures for the tests typecheck ratchet (Codex) * fix(mobile): short not-ready label that cannot squeeze the model name; button roles and labels |
||
|
|
fb21729157 |
chore(mobile): bump to 0.0.58 and Android versionCode 22 (#31194)
Matches the iOS 0.0.58 build. |
||
|
|
ad1bcfe502 |
A chat's model, effort and Fast belong to the chat, not to a model (#30561)
* fix(native-chat): a chat's model, effort and Fast belong to the chat, not to a model
Structured chats kept their option values per model on the client, so a value
the host saved without a model (a resumed chat that never picked one, Codex
Fast saved on the conversation) had nowhere to live. The client either dropped
it or labelled the chat with the listing's default model to hold it.
- One flat record per chat holds model, effort, speed and Fast independently;
a model pick changes only the model and the host reconciles the rest.
- Controls come from the model they apply to, separately from the values; with
no model known, a saved Codex speed still shows and Standard turns it off.
- The options answer marks values a catalog default supplied
(current.defaulted). A resumed chat never names or sends such a model as its
own; a new chat still shows the default the host says it runs. An answer
without the mark reads as before.
- Every send, including a new chat's first message, carries the chat's own
model and effort and held picks, never a default read off the picker.
- A held launch pick supersedes only an earlier pick of the same option.
Tracked in STA-10947.
* fix(native-chat): a new chat's first send still pins the default model it shows
A new chat that never picked a model carries the default its picker names, as
before, so it keeps the model it actually ran after the CLI's default changes.
Only a resumed chat's defaulted model is never named or sent.
Tracked in STA-10947.
* fix(native-chat): narrow the host catalog before reading whether its listing names the default
The flag lives only on a listed catalog, so the check confirms the catalog is not the unknown
answer before reading it; the first-frame test reads the chat's flat model value.
* fix(native-chat): a reopened chat whose saved model was refused records the model it now runs
After a retired saved model is reset, the options answer no longer marks the model the child runs
as a default, so the next send records it instead of replaying the refused model on every reopen.
* fix(native-chat): a new chat names and pins the default it shows, and a model pick keeps only what the model offers
A chat this view started names the default model the host fills in, whichever of the model list and
the options answer lands first, and its first send carries the shown model and effort. A resumed
chat still names and sends only its own values. A model pick, held or live, keeps an effort, speed
or Fast only where the new model offers it, so a value the model cannot take is never shown, sent
or remembered.
* test(native-chat): give the no-Fast model fixture its required default flag
* fix(native-chat): an open chat's model pick leaves reconciling to the host
A live pick no longer drops values on the client; the host's reply, when it carries its full
value set, replaces the chat's own values as a read does. Only a pick held before launch, with no
host yet, keeps just what the picked model offers.
* fix(native-chat): a pick reply only adds the values it names
A pick's reply carries only the values the host set or was asked for, so it can't speak for the
rest: it no longer removes values it leaves out. Joined tests pin real reply shapes and a new
Claude chat in a fresh Claude home naming and pinning Opus High.
* fix(native-chat): a tier the drawn model doesn't offer shows as Standard
A Speed value another listed model offers but the named model doesn't is drawn as Standard, which
is what the host reconciles it to, while the chat's record keeps the host's value. Effort, a tier
no listed model offers, an unlisted model and a stand-in model's controls still show what the host
holds. After a lost pick reply the picker no longer shows the old tier as a raw extra choice.
* fix(native-chat): a never-sent chat names and pins its default after a reopen
The options answer no longer marks the default model and effort as stand-ins for a chat this host
started that has sent nothing, so a chat reopened after a restart or on the phone names them and
its first send pins them, as before. A chat taken from history, or one that has sent, keeps the mark.
* fix(native-chat): narrow the missing record as null and type the new-chat fixture's handle chain
* test(native-chat): the unloaded-turn context test's record store answers null for a missing record
* fix(native-chat): a chat's speed shows what its next turn runs
A Codex tier the drawn model lacks shows as Standard, which is how Codex runs it, except Flex, which
Codex passes regardless: established Flex keeps showing, and after a model pick the host hasn't
answered it shows no selection until the reply or a read. Each Codex turn sends the same effective
tier without rewriting the chat's saved one, and a new chat's create carries held picks as the
picker draws them, settling them so they aren't applied again after publish.
* fix(native-chat): one speed rule for picker, host and turns; never-sent needs proof; create keeps held picks
A Codex tier the model doesn't list shows and runs as Standard under one rule shared by the picker,
the host's live reconciliation, each turn and the at-rest answer, which keeps the saved tier for a
model that offers it. An older host's Flex shows as it reports it. A chat counts as never sent only
when no turn, rewind or untrustworthy journal says otherwise. A new chat's create carries held picks
as the picker draws them but keeps them as the user made them until create succeeds, so a refused
create and retry still has the user's own pick, and a newer pick still applies after publish.
* test(native-chat): type the resting journal mock and cut overlapping option tests
One typed resting-journal helper replaces six inline mocks and the broad object parameters the
low-evidence check rejects. Cuts tests the joined flows already pin: the resumed list-or-answer
ordering test, the restart-after-first-send view test, the held effort before a model pick, and
the model-pick and restart loop of the saved-Fast test.
* test(native-chat): cut option assertions the joined flows already pin
Drops the Codex Standard row (the joined tier test pins it), the dispatched-source and literal
effort-choice checks, and the resumed view test restart and model-pick steps; folds the pick table.
* fix(native-chat): an at-rest pick reply and answer name the speed the next start runs
The reply to a pick made at rest now carries the tier the chat model runs, through the same rule as
the at-rest answer, so an older app whose follow-up read fails shows what the next turn sends; the
saved record keeps the pick. A saved tier no listed model offers (a Flex from the user Codex config)
is the agent configured tier at rest too, as the live rule and the picker already read it. Restores
the pin that an accepted Codex reply naming Standard shows with no later read.
* fix(native-chat): a reopened chat with no saved model names its default at once
A reopened chat that never saved a model showed just "Model" until a send started its agent, while
main names the list default straight away. The picker now names the default the host marks for every
chat, as main does. Whether a send carries that default is a separate rule: a new chat sends it, so
its first send pins it; a reopened chat does not, so its next start still runs the agent's own.
* fix(native-chat): a chat with no saved model keeps the model its first turn ran
A chat that never saved a model followed whatever the agent's default was at each start, so a chat
resumed from history or a terminal could quietly switch models after a restart. When a message is
handed to the agent on such a chat, the host now saves the model that turn ran: the message's own
model when it carried one, otherwise the model the agent reports running. A default the host filled
in, no model at all, or a model the agent would refuse as a choice on later sends, is never saved. The write sets the model only while none is saved, inside
the record store's transaction, so a pick made meanwhile always wins and effort, speed and Fast are
untouched. One host step covers Claude, Codex, ACP agents and Pi. No new saved field.
* fix(native-chat): a chat with no saved choices keeps the model and effort its first turn ran
A Codex message that left the model or effort unset was filled from the model list's defaults, not
from what the running thread uses. Once the host saved the model a chat ran, the next Codex turn
carried that model alone and ran the list's default effort for it (a configured xhigh became
medium), and a message carrying only an effort ran the list's default model while the chat saved
the thread's. A Codex message now resolves an unset model or effort to the thread's own, and only a
switch to a different model takes that model's default effort, the same rule the other agents use.
When a chat with no saved model records what its first turn ran, it now records the effort, service
tier and Fast that turn ran too, in the same one-time store write: each only where the chat has none
saved, only from the message's own value or the agent's real report, never a filled-in default, so
a later change to the agent's configured effort no longer pairs the kept model with an effort it
never ran. No new saved field and no wire change.
A Codex follow-up sent while a chat's first turn runs is compared with what that turn actually runs,
so it steers instead of waiting for a new turn just because the host saved the model meanwhile.
Also fixes the delivery loop's optional recording hook type.
* fix(native-chat): key what a child ran by its provider child type
* fix(native-chat): record each option a turn ran where none is saved, and drop what a model pick can't run
A Codex message that only switched the tier set the thread's effort as a lasting override, so a later
switch to a model with no listed efforts was refused; the effort is again written only where the
model brought one or an earlier override needs replacing.
Recording what a turn ran now works per option: a chat that already saved its model still records
the effort it ran, and so on for each option it has none saved for. Tier and Fast come only from the
agent's own report, since a message steered into a running turn runs that turn's, and Fast is kept
only when the agent reports it actually routed on: an off state may only mean Fast is unavailable.
A model pick drops a saved effort, or Fast, that the newly picked model's listed row does not offer,
in the one host step both resting and live picks go through, so no later send carries a choice the
agent would refuse. The saved tier is kept, as before: it runs as the model's effective tier.
* fix(native-chat): save only options a turn really ran, and only ones the chat's model runs
A chat with a saved model recorded the effort its agent's settings named, not the one the turn
ran: with "effortLevel": "high" in Claude's settings and Haiku saved (no effort control), the chat
saved high and every later send was refused. A Codex thread effort its saved model doesn't list
did the same.
What a turn ran now comes from the agent's own dispatch: Claude reports the choices it applied and
the effort the CLI resolved for the model it applies, never the settings level alone; Codex reports
its final request over the thread's own model and effort, and a steer reports its running turn.
This evidence lives only in memory on the dispatch result; agents without it keep using their own
reports.
One admission rule now decides every saved option, inside the ordered record write: under a model
the saved catalog lists, an effort must be one it offers and Fast must be one it replays (Claude
needs positive support). A late record of a turn that ran on another model than the one the chat
now has saves none of that turn's options. A model pick drops inherited options its new model
doesn't run, an explicit pick or a new chat's option the model doesn't offer is refused with the
existing option refusal, and unknown catalogs or unlisted models pass as before. The saved tier
keeps its effective-tier rule, and nothing already saved is cleaned up later.
* fix(native-chat): an attach that founds a chat admits its options against the saved catalog too
* fix(native-chat): Claude and Codex always name what a turn ran, and picks are judged inside the write
The handover type was misnamed, which broke the typecheck. Claude and Codex now attach their
dispatch evidence even when it names nothing, so a Claude turn dispatched before its settings
readback never records the settings effort it did not run; the agent's own report still names the
model and stands in only for agents without dispatch evidence. Recording leaves the saved-model
and set-if-unset checks to the store's one admission rule. An explicit effort or Fast pick is now
checked inside the ordered write, with the same refusal, and only at rest: a running chat's own
menu can offer more than its saved catalog row (Pi's probe menu stops at high).
* test(native-chat): Codex admission tests accept the dispatch's run evidence
* fix(native-chat): a new chat's options are admitted against the folder list its picker showed
* fix(native-chat): a new chat's admission reads the list its picker read, resolved once by the create
* chore: re-run CI against main's formatting fix (#30918)
* chore: re-run CI against main's formatting fix (#30948)
* test(native-chat): explain the seed-options stub host cast
* fix(native-chat): attach keeps its two-argument call when there are no attach options
* Revert "fix(native-chat): attach keeps its two-argument call when there are no attach options"
This reverts commit
|
||
|
|
38034167e7 |
fix(mobile): keep the chat composer's controls inside its row (#28983)
* fix(mobile): keep the chat composer's controls inside its row The model pill never shrank (React Native items default to flexShrink 0), so a long model name slid under Mic and Send. The permission pill now shows only its mode icon, resolved from one shared mode->icon-name table that desktop uses too. * fix(mobile): resolve the permission icon only when a pill renders * style: format the permission pill test * fix(desktop): keep the permission icons a static record |
||
|
|
c88e9d7054 |
fix(mobile): reopen the phone chat's live feed from its last row when it ends (#29007)
* fix(mobile): reopen the phone chat's live feed from its last row when it ends The phone opened a chat's live transcript stream once per connection and never reopened it when the stream ended or failed while the connection stayed up, so the chat silently froze on old rows. Move desktop's reopen-from-cursor read into src/shared and run the phone on it too, with an app-foreground retry. * fix(mobile): retry the chat feed on the app-resume signal client-context already relays Module mocking react-native in the shared vitest setup is rejected; publish the existing revival nudge instead, the way desktop's host-contact signal feeds its read. * test(mobile): let the phone's coalesced live read apply each frame before comparing |
||
|
|
6c7a6ddd54 |
fix(native-chat): save every chat reply and tool output whole; bound only the copy sent to clients (#29982)
* fix(native-chat): save every chat reply and tool output whole; bound only the copy sent to clients Orca cut Codex replies, every agent's reasoning, plans, prompts and all tool output to a 16 KB head when it saved them, and threw the rest away. The limit existed so a reconnecting client is not replayed a huge row, but it was applied at save time, so every transport limit became permanent data loss. The journal now keeps what the agent said. One outbound form, applied to every history page, live batch and catch-up, sends a client the same 16 KB head of each tool output and diff it always got, and marks the item `shortened`. A new capability-gated `agentSession.item` RPC returns the whole item, and the desktop and phone show "Show full output" / "Show full message" when the copy was cut. Items past half a page are shortened and marked instead of replaced with "too large to show". Two bounds stay, both named: a 4 MiB ceiling per text (head and tail), applied where every provider's rows enter the write queue so one write can never stall a chat, and a preview on Grok/OpenCode/OMP/Pi calls while they run, whose closing row is built from the whole value the translator held. * test(native-chat): an over-budget history item is shortened and marked, not replaced * fix(native-chat): format, keep the sink and projection under the line limit, update method count and tests * fix(chat): preserve complete writes and keep detail current * test(chat): align final tail reservation with captured content * Prototype source-batch journal ownership for Claude * Pin the resolved iterator promise in ownership experiment * Revert "Pin the resolved iterator promise in ownership experiment" This reverts commit |
||
|
|
703b3134d7 | fix(mobile): keep session tab icons full size beside long titles (#31169) | ||
|
|
fbc7488276 | perf(mobile): prepare the worktree checkout while the new-workspace form is open (#31167) | ||
|
|
d406e0c8ab |
feat(native-chat): edit a queued message's images on desktop: remove, paste, drop or attach (#30815)
* feat(native-chat): a queued message's Save can replace its images (host, wire and capability) * feat(native-chat): edit a queued message's images on desktop: remove, attach, paste or drop * refactor(native-chat): share the queued edit's image rules and drop the rarely-shown Save reasons * fix(native-chat): a queued edit's refusal says the edit wasn't saved, and its wire takes a full image set An expired upload refused on Save read "The command didn't run": the queued message update fell through to the command write kind. It now has its own kind whose sentence is "Your edit wasn't saved." The images param caps at MAX_BLOCKS, matching the shared edit rule that counts them with the text. * fix(native-chat): a queued edit keeps image markers right and owns drops and pastes on its card - Removing a kept image drops its [Image #k] marker from the edit's text and renumbers the later ones (shared rule, reused by the phone), so the agent never reads a marker that names the wrong image. - A file dragged from Orca's own tree onto the editing card goes to the edit, like a Finder/Explorer drop; the chat box overlay no longer covers the card. - Cmd/Ctrl+V with focus on the card's buttons goes to the edit, not the chat box. - A "changed" answer says "Your edit is in the chat box" only when something went there; otherwise it says the edit wasn't saved. - The empty hint counts an uploading image as content and is not shown for agents without image input; the list grows only while thumbnails show. - Every hand-off releases the edit's preview URLs. * fix(native-chat): an in-app drop on the editing card goes through the chat box's owner check The edit no longer runs its own copy of the in-app drop check, which resolved the target host through a path that can read the Active Server setting. The chat box's claim checks the drop and routes it by the chat's owner as before, then hands the files to the editing card's sink when the drop landed on it. Also: the kept-image id list is readonly where it is read, and the marker rule looks up the remaining images in a Set. * fix(native-chat): only an edit that takes images keeps its card's pastes, and no drop joins a Save in flight The pane paste bridge no longer skips every paste inside a queued editor: the card's own paste handler already claims image and file pastes, which the bridge respects. A menu paste is left to the card only when the edit takes images (the card is a drop owner), so an older host or an agent without image input behaves as before, and text pasted with focus on the card's buttons goes to the chat box. An in-app drop handed to the card while its Save is in flight adds nothing. * fix(native-chat): a failed capability re-check keeps the host's last answer, and an edit takes no files before its lease A re-check after regained contact that failed wrote "unknown" over a known answer for the same host, so features the host had reported (such as editing a queued message's images) vanished on a connection blip, mid-edit. A failed check now keeps the last answer; a different host still starts at unknown. Paste, Attach and OS or in-app drops into a queued edit are now blocked while it acquires its lease, as typing already is. * fix(native-chat): an image change is never saved as text alone when the host stops reading as taking images Reverts the shared capability hook to keep main's behavior: a failed re-check reads as unknown. Instead the queued edit derives from live state that an edit whose image set changed can't be saved while the host doesn't read as taking images, so a connection blip never saves renumbered markers over the old images or drops added ones. Save comes back once the host answers again. * fix(native-chat): keep queued image markers and drop feedback in sync * fix(native-chat): derive queued image markers without changing edit text * test(native-chat): the pane drop test's intake append returns the paths it didn't take * fix(native-chat): route menu paste to the focused queued edit * fix(native-chat): honor queued edit paste ownership before focus redirection * fix(native-chat): preserve native paste without clipboard access * fix(native-chat): keep paste in the owning queued edit |
||
|
|
b887483e55 |
fix(sidebar): the phone reads projects and project groups from main's sidebar feed, installed with the rows (#31102)
* fix(sidebar): the phone reads projects and project groups from main's sidebar feed, installed with the rows The phone built its project sections from a catalog the desktop window published through a side channel (mobileRelay.projectSections). That copy changed on a timer after the event that told the phone to re-read it, and a re-read asked for during a read in flight was dropped, so the phone could keep an old grouping (a project under the wrong group, a group missing) indefinitely. Main now publishes the catalog (repos with their group and order, project groups, projects and host setups, every host's including paired servers') as a projectCatalog domain of the sidebar feed, from its own store and its own reads of each paired server. The phone reads it from the same install as the rows. mobileRelay.projectSections stays, answered from the same builder, for pages that predate the domain; a page on a desktop without the domain still reads it, now re-reading when an invalidation lands mid-read. The phone e2e list helper runs the page's real refresh lifecycle instead of re-reading on every assertion, and covers a group move while a read is held. * test(mobile): stand AppState in for the screen hooks' react-native mocks, and tolerate a world with no project rows in the parity catalog * fix(sidebar): main files a server's copy of a grouped project under its group, and reads a server's catalog only on catalog events - The window gives an ungrouped paired-server repo the group of an existing copy of the same project (applyInheritedProjectGroups). That function now lives in src/shared and main's catalog builder folds each server's repos through it, so the phone files the copy where the desktop does. - A paired server's change watch now says whether the event can carry a catalog change. Only ready, a replay and reposChanged mark the catalog stale; worktreesChanged (pins, unread, sort stamps) reads rows only. - The parity fixture now builds the server side from raw server answers, folded through the window's mergeFetchedReposForHost and through main's own server read and builder, with a server copy of a grouped project and a partial saved manual order. It fails without the inheritance. * fix(sidebar): parse a paired server's catalog rows with a schema instead of reading keys reflectively * fix(mobile): sync the feed ref after render and re-read queued project sections without an await loop * fix(mobile): spell the AppState shape the refresh reads, so the e2e helper pulls in no React Native types * test(sidebar): give the merged builder test's feed domains a project catalog --------- Co-authored-by: E2E Test <e2e@test.local> |
||
|
|
94e55a2f3d |
fix(mobile): phone list resends lost option changes, trusts the feed's row facts, and retries a failed feed read (#31088)
* fix(mobile): resend lost Workspace option changes on reconnect, draw feed-owned row fields from the feed, retry a failed feed read Co-Authored-By: Claude <noreply@anthropic.com> * fix(mobile): end a feed retry streak only on install, draw the feed's comment Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: E2E Test <e2e@test.local> Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
fadcb8a988 |
fix(sidebar): keep project names short and show their machine (#28250)
Keep same-name projects short across execution hosts and show their machine beside host-owned headers, including when collapsed. Reuse existing host identities and filtered section membership; retain global ownership for shared projects. Includes tooltip, narrow-sidebar, alias and filtering regression fixes with visual proof in the PR. Co-authored-by: KAPUIST <thsxornjs12@gmail.com> |
||
|
|
99379d1458 | fix(mobile): let an add or removal replace corrupt stored terminal keys (#30977) | ||
|
|
b2e678a391 | fix(native-chat): keep /model, /effort, /clear and /compact in the / menu after an agent reports its commands (#30956) | ||
|
|
883583b4c8 | fix(native-chat): hide history before /clear (#30902) | ||
|
|
f3a1238a34 |
perf(mobile): shorten output batching during terminal scroll gestures (#30319)
Co-authored-by: Neil <neil@stably.ai> |
||
|
|
4a33cd8e73 |
fix(native-chat): a usage limit reads as a pause with its reset time and a Continue, not a failure (#30396)
* fix(native-chat): a usage limit reads as a pause with its reset time and a Continue, not a failure Claude, Codex and Grok chats that hit the account's usage limit now end the turn on one warning row that names the limit and, in the reader's clock, when it resets, with Continue to pick the turn back up. Each adapter reads its protocol's typed limit signal into a `usageLimitReached` failure fact; older clients keep the host's sentence. * fix(native-chat): keep the failure words and chat session files within their line budgets Moves the attachment-problem sentences to their own file and hands the composer's send straight to Continue. * fix(native-chat): drop a check the shown branch already rules out * fix(i18n): ship the usage-limit failure words in the runtime catalog * fix(native-chat): reconcile usage failures by owning turn and typed cause * fix(codex): keep snapshot import type-only * fix(claude): open refusal turns without opening empty placeholders * fix(native-chat): bind usage Continue to the latest idle failed turn * test(native-chat): isolate stale Continue from ordinary delivery progress * test(native-chat): use a braced journal assertion * test(native-chat): build failure rows through their word factory * fix(claude): retain terminal failure evidence through result reconciliation * fix(native-chat): complete usage Continue through the composer sender * test(native-chat): provide the composer options surface in Continue coverage * test(native-chat): use typed failure words in renderer fixtures * test(native-chat): match the remote send payload apart from transport options * fix(native-chat): replace the previous sender notice on Continue * fix(native-chat): clear superseded Continue notices before rendering * fix(native-chat): preserve ordinary composer send arguments * fix(claude): resolve terminal ownership and preserve resultless quota failures * fix(claude): keep repeated terminal detail unchanged and complete test fixtures * fix(claude): resolve deferred terminal replays after owning journal writes * refactor(native-chat): share tombstone append within the sink line budget * fix(claude): retain both input and echo correlation on the live turn * fix(claude): preserve unowned result completion after terminal resolution * fix(claude): allow unassociated successful goal completion * fix(claude): keep terminal completion on the live turn * fix(claude): keep typed notices separate from quota failures * fix(claude): preserve context-limit failures as ordinary errors * fix(native-chat): keep quota failures visible in collapsed turns * fix(native-chat): return keyboard Continue focus to the composer * test(native-chat): keep quota fixture context stable * test(native-chat): hoist quota context to module scope * fix(i18n): use one Chinese word for the agent in both Continue tooltips |
||
|
|
b0c55557f0 |
Keep phone chat history in one window while preserving reading position (#29271)
* fix(mobile): phone page loads older history only from the Load earlier tap
On the phone page a scroll near the top paged in older history, often while iOS
was still bouncing the list past the top. The place-hold then wrote scrollTop
while WebKit owned the scroll, and the list stopped scrolling until the reader
left the chat. The page now loads older history only from the existing Load
earlier control (the native app keeps its drag paging), and the place-hold
never writes while the list is bounced past either end.
* fix(mobile): a reconnect no longer drops the older page the reader asked for
A replayed snapshot cancelled any in-flight "Load earlier messages" page, even
when the replay merged onto the same window and cursor, and every other lost
page (host refusal, error reply, transport drop) was swallowed, so the control
went back to its label as if nothing had been tried.
A page now lands unless its window was replaced or the cursor it was sent from
changed. A page that is lost shows one line under the control,
"Couldn't load earlier messages. Tap to try again.", cleared by the next tap or
a page that lands.
* fix(mobile): keep the failed-page copy out of raw JSX text
* fix(mobile): name the failed-page copy
* fix(mobile): clear the failed-page line when the window is replaced
* docs(mobile): the older-page read's comment names the retry line a refusal now shows
* feat(mobile): one older-page loader that owns the spinner, the budget and the outcome
* fix(mobile): terminal-backed chats read older pages through the shared loader, bounded to one tap's budget
* fix(mobile): agent-session chats read older pages through the shared loader
A failed page now shows the retry line instead of an invisible chat error,
history that can never be read ends paging, and one tap shares one budget.
* fix(mobile): the Load earlier control renders the loader's outcome for both chat types
A retryable failure keeps the button with the retry line; history that can never
be read swaps the button for one line saying so; scrolling at the top no longer
re-fires a failed read.
* style(mobile): format the read-back guard
* test(mobile): a failed agent-session older page keeps the chat readable, retries on a tap, and ends at unreadable history
* test(mobile): the older-page read now records its per-tap budget in the page goldens
Hand-updated: only the nativeChat.readSession options change (absent -> 15 s
budget spanning connect); every value is re-hashed and re-keyed as the recorder does.
* fix(mobile): every history reset ends an agent-session tap quietly, as the subscription's reload replaces the window
Only a final read refusal (damaged history, or saved by a newer Orca) ends a tap at
'Earlier messages can't be loaded.'; the test now drives that reachable path.
* fix(mobile): history that can't be loaded stays a line until the window is replaced
* fix(mobile): a page row keeps its key when older messages join it
A folded row's id is its first message's, so an older page that brings the
start of a tool run (or an older sibling of a roster) replaced the displayed
row: the list remounted it and the page's history split lost its first row.
Rows now carry a presentation key that stays while any of their messages
draw, handed on from the last committed render by the raw message ids each
row draws, and a vanished key resolves to the row that took its messages.
* fix(mobile): the page holds a parked reader's message, and leaves a following one to the follow owner
The place-hold fired on the first drawn row's id, which a kept row key no
longer changes, and held the lowest row on screen even while the reader
followed the end, so at the 2,000 cap it wrote against the follow owner.
It now fires when the source window's first message changes (an older page,
a cap trim), only for a reader who is not following, and puts back the
first tagged row on screen, else the nearest row the update kept, through
the row keys. iOS bounce still gets no write.
* fix(mobile): an open tool run keeps the reader's lines when older calls join it
With the run's row kept, older calls joining its front pushed the lines the
reader was on behind Show more. The reveal now counts the lines added above
its first line apart from what Show more asked for, so those lines stay
shown, each diff keeps its share, and Show less only undoes Show more. Lines
and the run are tagged, so the page's hold puts the line being read back
where it was. The flow test opens a chat inside a silent run, loads the rest
from an old host's cursorless reply, then lands a reply and a send.
* fix(mobile): an open tool run reads its first line from the pairs it already drew
Pairing the run again for its first line on every render broke the bounded
pairing a 2,049-call run relies on (one pairing per reveal). The flow test's
icon mock names every icon the run reads.
* fix(mobile): a cap trim that splits the top run leaves the run its key
Row-key hand-on walked the current rows oldest first, so when a trim left a
run's first result alone above it, that orphan took the run's key and the open
run remounted closed. Walk newest row first so the run keeps it.
* fix(mobile): a row's key belongs to the messages it is made of, not to the results it shows
A cap trim that split the top run gave the run's key to whichever split-off result row a
position-ordered scan met first: the result above it for calls made one at a time, the one
below it for parallel calls. The run then remounted closed and the reader's line went.
Each row is now identified by its own sources (a run by its assistant and call messages, a
roster by its group, a lone result row by its results) and keeps the key of the one previous
row it continues; results are content only. A key no row kept resolves to the row that took
in what it drew. If identity ever came from two rows, neither inherits.
* fix(mobile): the page holds the first part of what the reader saw that is still the same part
The hold took a line only from the first row on screen and otherwise trusted
that row's container. A result row absorbed by its run, a roster the fold moved
to an older sibling, or a trimmed line then moved the reader. It now lists the
tool lines on screen across every row (a run header only when no line shows,
a row top only when neither), and puts back the first one still drawn: a run
header only in the row that kept its key, a row top only while the same
message places that row. One write, skipped under half a pixel.
* test(mobile): pin the run-header rule in the reading-place flow; fold the hold's last cases into it
An open run whose header is above the screen holds nothing: with only its
Show more on screen, the rows after it keep their height when older calls
join. Removing the header-on-screen check moves them 840 px. The collapsed
case now checks the same rows. The iOS bounce and native cases move into
the flow test over its page model, so the hold's separate fake scroller
goes.
* fix(mobile): the page's follow owner pins to the live end before paint when the transcript grows
RN Web reports content size a task after layout, so with no browser scroll
anchoring a following reader saw one frame far above the end after Load earlier.
One ResizeObserver over the page scroller and its content, bound to the list's
mount, calls the same pin, which reads the live end and writes only when short of it.
* test(mobile): one page follow flow pins growth, the late size report and layout to the live end
The page's follow tests fold into one flow so the view test stays within its
line limit; the place-hold reads the follow owner through one prop bag so the
view does too.
* fix(mobile): only the page asks the list for its scrollable node
The native list never needs the resize observer, so it shouldn't reach for
the page-only node at attach.
* fix(mobile): a following reader on the page keeps the end as the browser's scroll anchor
At the 2,000-message cap a trim plus an append of the same height changes no size, so the
follow owner hears nothing, and Chromium's scroll anchoring keeps a visible message still,
scrolling the reader one message off the end each time. While the reader follows, the page's
scroller now takes the transcript out of anchoring and leaves a 1 px end marker as the anchor.
Parked readers and native lists are unchanged.
* test(mobile): move the cap-anchoring flow into its own view test file
MobileNativeChatView.test.ts is at the 800-line limit for test files.
* fix(mobile): the cap-anchoring test passes every required view prop; the style augmentation names why it is an interface
* fix(mobile): type the page's anchoring style locally instead of augmenting ViewStyle
* style(mobile): wrap the page scroller's styles
* test(mobile): the cap-anchoring contract joins the page describe, and one scroll-event helper replaces the copied event literals
* fix(mobile): a chat switch on the page opens the next chat at its newest message
The chat view stays mounted across chats, so a reader parked in one chat left
the follow owner parked for the next. On a scope change the page now resumes
following and pins to the scroller's live end before paint; the place-hold
drops what it held for the previous chat. Native keeps its current switch
behavior.
* fix(mobile): the phone page draws only the messages near the screen, in one list
On the page, RN Web's list decided which rows were in the document by index and
drew them in batches after paint: a chat opened climbing through its history
before reaching the newest message, and rows paged in above the reader were
drawn in the list's header instead, a second path where iOS stopped finger
scrolling at the seam.
The page now draws the transcript in one keyed flow: Load earlier, a top
spacer, the rows near the screen, a bottom spacer, then the live status and
waiting messages. Which rows are drawn is named by row key, so a page landing
or a cap trim never changes it. Rows drawn above the reader take their height
out of the top spacer, so nothing moves and nothing is written while the reader
touches or scrolls; a correction the spacer can't take (the place-hold's, or
making room above) is written once the reader is at rest. The follow owner
still owns the end and pins at commit. Native keeps its FlatList and props.
* refactor(mobile): the page window's coverage decision is its own pure module; the view reads the follow owner through one handle
* fix(mobile): rows arriving in a chat the page drew empty open at its end, not all at once
* fix(mobile): the page window draws from every scroll event, not RN Web's throttled report
* style(mobile): one-line comments in the page window
* refactor(mobile): the chat view reads the follow owner through one handle, keeping the file under its line cap
* fix(mobile): the page's spacer holds only measured rows, and older pages land once the reader stops
The phone page's windowed list sized its top spacer from estimated row heights. On iPhone a scroll
write stops a fling, so the estimate error could only be removed at rest: reaching the top showed
thousands of pixels of empty space, and an older page landing mid-fling grew a row above the reader
and moved what they were reading.
Now the spacer stands only for rows measured for how they draw now; rows never measured are left out
and measured in place only while following or once the reader has stopped (no touch, no scroll of
theirs for 150 ms). A page that arrives while the reader scrolls waits for that moment. Rows report
their heights through the page's one ResizeObserver, coverage is arithmetic over those heights, and
one at-rest rule puts the row the reader was reading back when anything above it changed size.
Unchanged rows keep their objects across a re-fold, so a height stays valid until its row changes.
* refactor(mobile): the page window's row registry, height scans and sync render in their own modules, under the line caps
* fix(mobile): an opened tool run, its lines and Show more stay as the reader left them when the page window unmounts the row
The phone page now draws only rows near the screen, so a row the reader scrolled past unmounts. Its
tool run's open state, each line's open state and Show more lived in the row's own state and reset on
remount, closing what the reader opened and drawing the row at a height its measurement no longer
matched. They now live in one per-chat map the page list provides (the desktop transcript's
mechanism); rows rendered elsewhere keep their own state as before.
* style(mobile): format
* fix(mobile): quality rules: explicit listener cleanup, no side effects in a state updater, kept disclosures scoped by a keyed provider
* fix(mobile): the page window moves only on measured rows, and never off the end while following
At 4x CPU the end pin's scroll event reached the coverage pass before the observer had measured the
drawn rows, so they were summed at the first-guess height, read as above the screen, and the window
shrank to the last row; the browser's clamp then looked like the reader scrolling up and a long chat
opened at its oldest message. Coverage now waits until every drawn row has a measured height (the
observer reports them before paint), and a following reader's window always reaches the end.
* style(mobile): format
* fix(mobile): the reading place is taken only from the reader's scrolls, so Load earlier at the top keeps it
A write's scroll event can arrive after an older page's rows were drawn above and before the correction; re-taking the place and covering then kept the pushed position.
* fix(mobile): only the reader's touch, wheel, pointer or key ends following on the page
Scroll to latest after a parked turn could land at the top: the window drew the end, the document shrank and regrew, and a scroll event iOS delivered after rows grew above read as the reader moving up, so following ended and the window drew where the scroll had been clamped. A move up now ends following only within a second of the reader's input on the list.
* fix(mobile): a scroll the browser makes when the page shrinks is not the reader's, so Collapse keeps the place
Collapsing tools while parked shrinks the rows above as they are re-measured; the browser clamps the scroll, and its event (delivered before the resize callback) re-took the reading place with nothing on screen and moved the window, so the reader ended at the bottom. A scroll with no input of the reader's and no scroll of theirs just before is now layout's.
* fix(mobile): the page decides once whether a scroll was the reader's, from what explains it
Three bugs today had one cause: a scroll the reader didn't make was taken as theirs (a write's late event, a clamp after the document shrank, a scroll delivered after rows changed). A scroll is now the reader's only when nothing else explains the move: not the page's write, not a commit whose resize callback hasn't run, not rows above the reading place, not a clamp to a shorter end. The window and both follow transitions use that one verdict; the reading place is re-taken after each resize callback, so a correction that couldn't land is never written later; no write lands while iOS bounces past an end. Replaces the one-second input window.
* fix(mobile): a jump away from the end while following draws where it lands, not every row to the end
The jump's scroll event covered with following still set, which keeps every row from the new start to the end: up to 1,603 rows mounted in one 1.8 s task before the follow change trimmed them.
* fix(mobile): Tools and text size keep a parked reader's place on the page
Toggling Tools regroups the rows, so the window's reading place (a row key) can vanish and nothing put the reader back: they moved about 20 messages. The place-hold, which already follows what the reader was reading through a regroup, now also runs when how rows draw changes.
* perf(mobile): measuring rows costs the same at any chat length
Each settled moment walked every measured row (resolving and comparing its state) while following, so the fill grew slower as it went (27-43 ms per round at 4x). Now one round checks rows within three screens of the reader, fills the next rows just above the measured run directly, and walks every row at most once a second; a round measures 12 rows, not 24.
* style(mobile): window hook back under its line cap
* fix(mobile): iOS bouncing past the end is the reader's scroll, not a clamp
The clamp test matched any position at or past the end, so every rubber-band frame read as layout: the page counted the reader as settled mid-bounce and an older page landed during it, with a one-to-two-frame blank. A clamp now lands exactly on the new end from a position the old one allowed.
* fix(mobile): a drawn measuring group waits for its measurement, so a landing while following doesn't redraw 30 times
After a landing while following, the next choice ran before the browser measured the group just drawn: the group sat at the top, so nothing looked owed above it and the group came down unmeasured, then went up again, 28 times in 180 ms until the pin landed. The choice now waits while the group's rows have never been measured, and re-renders only when it changes. Choosing what to measure moves to the heights; the re-check timer to the motion.
* fix(mobile): a correction puts back what layout moved, never the reader's own scroll
The browser can run a resize callback before the scroll event of a wheel, key or scrollbar move; the correction measured the reading row's move on screen, which included that scroll, and wrote it back (300 px per wheel tick while rows above were measured). The correction now puts back only the row's move within the content, keeps the scroll, and undoes only a clamp. A scroll that lands while a commit is unreconciled is left for the resize callback to tell apart, so a reader's move there still counts as theirs.
* fix(mobile): the end pin waits for the reader's move to be counted; measuring stays cheap while a reply streams
- The resize callback pinned to the end before telling apart a move no scroll event had reported, so a reader's first scroll up while following could be written back. The list now counts that move (which may end following) and then runs the pin.
- The nearby stale walk checked every measured row before its three-screen reach applied (2,236 checks per streamed update at 4x); the reach now bounds the walk.
- Measuring rounds draw 8 rows and leave 32 ms between rounds, so a streamed reply gets frames while rows are measured.
* fix(mobile): Collapse keeps the place when the browser hasn't applied a clamp yet
Collapsing shrank rows above a parked reader in rounds; when a round left the scroll past the new end before the browser clamped it, the write guard took it for an iOS end bounce and skipped the correction, and the window re-seated at the end. Corrections wait for rest, so past-the-end there is a pending clamp, not a bounce; only above-the-top stays guarded.
* fix(mobile): no second correction when the browser's own scroll anchoring already kept the row
On Chromium, rows landing above a parked reader are held on screen by the browser's scroll anchoring, which moves the scroll. The resize callback counted that move as the reader's, kept it, and put the row's content shift back on top: the row left the screen by 1.4-2.8k px. When the reading row is where it was on screen, the move is layout's and nothing is owed.
* fix(mobile): the page alone keeps a parked reader's place; the transcript is out of browser scroll anchoring
On Chromium the browser's scroll anchoring and the page's own correction both answered the same layout change, and the overlap can't be measured after the fact: a reader's wheel tick in the frame of a landing still got the landing put back twice (0.9-1.6k px). The transcript is now never a browser anchor, as on WebKit, and the end marker anchors only while following; the overlap guess (placeHeld) is gone.
* fix(mobile): no browser anchor while following either; only the page moves the scroll
The 1 px end marker stayed a browser anchor while following, so rows measured in above made the browser move the scroll to keep the end; the resize callback counted that move as the reader's and every measuring round waited for rest (spinner 36 s instead of 2.7 s on a 2,000-row open). The end pin already holds a following reader before paint and a shrink clamps onto the end, so the marker and its anchoring go.
* fix(mobile): on Chromium the page keeps a reader's place while they scroll, as the common list does
With the transcript out of browser scroll anchoring, corrections that waited for rest left Chromium readers (Android, desktop) seeing rows above change size mid-scroll, worse than main. Where a scroll write keeps momentum (every engine but WebKit), the resize callback's correction and the place-hold now write at once, as the common list does; WebKit, which stops a fling on any write and has no anchoring, still waits for rest.
* fix(mobile): a parked reader's place is the part of the row at the screen's top, not the row's own top
With browser anchoring off, growth inside the partly visible top row above the reading point (a tool card or image getting taller) left the row's top in place, so nothing corrected it: the text moved by the growth (240 px), where main's browser anchoring held it. The page now anchors the part of that row at the screen's top, as the browser's anchoring would pick; a following reader keeps the row's top, which the end pin makes enough.
* fix(mobile): on Chromium the browser keeps a parked reader's place; the page owns only following and a regroup
The page kept a parked reader's place itself with a saved element at the screen's top, which a
replaced card inside the top row took away (+240 px, main 0). Where the browser has scroll
anchoring it now holds the parked place, as main does; the end pin holds a following reader; the
place-hold takes the scroll for a regroup or a move to another row, with anchoring off from before
the update until its correction. Only input that goes the move's way makes a move the reader's
there, so the browser's moves never end or resume following. WebKit is unchanged. While following,
measuring waits for a streaming reply to pause.
* test(mobile): the page's own hold is tested as on iOS; layout settles before the reader's wheel
* style(mobile): format the page's anchoring change
* fix(mobile): the browser chooses its anchor again once the reader rests, after a scroll that landed on a spacer
A long jump can land where only a spacer is drawn; spacers are never an anchor, so Chromium chose
none and kept none after rows mounted there, until the next scroll (growth above then moved the
reader +240 px; main 0). At rest after a scroll the page nudges the scroll by a pixel and back, so
the browser chooses from the rows drawn now.
* fix(mobile): at the very top the page keeps a parked reader's place, where Chromium's anchoring never adjusts
Parked at scroll offset 0 while older rows were still being measured above, each measured group
pushed the reader down 0.9-1.2k px: Chromium makes no anchoring adjustment at offset 0, and the
page wrote no correction there. The page now takes its anchor and corrects only at the very top;
everywhere else the browser keeps the place, so the two never both move it.
* fix(mobile): after each commit the browser chooses its anchor from the rows laid out, not the container around them
Chromium keeps a partly visible container as its anchor when nothing inside qualifies; rows it
never laid out don't, and spacers never do. After a long scroll onto a spacer the anchor was the
transcript's wrapper, whose top never moves, until the next scroll: growth in the first ~150 ms
moved the reader +240 px (main 0). After each commit, while the browser holds the place, the page
lays the rows out and scrolls a pixel and back (its own writes), so Chromium chooses among them.
The settle-time re-choose goes.
* fix(mobile): the re-choose nudge is not taken as the page's own writes, so a reader's scroll landing there still counts
The nudge ends where it started, so the scroll event after it reports only what else moved the
scroll. Recorded as the page's own targets, it swallowed the End key's move to the end (within a
pixel of a target), which then didn't resume following (main and f044 did).
* fix(mobile): preserve native chat state across page windows
* test(mobile): use typed RPC fake for chat paging
* fix(mobile): on iOS a Tools toggle keeps the reader's place even while the list still glides
The page's WebKit place-hold wrote its correction only at rest, so a regroup committed within
150 ms of a reader scroll (or mid-glide after a tap outside the list) skipped it, and nothing
redid it: the text the reader was on moved by the regrouped rows' change and stayed there.
Unlike a source change, whose new rows stay out of the spacer until measured at rest, a regroup
resizes drawn rows at once. It now corrects at once, as main's hold always did.
* Preserve reading positions within phone chat scroll boxes
* Correct phone chat type declarations
* fix(mobile): a browser notice elsewhere on the phone page no longer fails a terminal that is still loading
On the page each terminal document listens for window errors, and before ready a reported error is
fatal ("Terminal failed to load"). The window is the whole app's, so the browser's ResizeObserver
loop notice, which the chat list's resize work can raise and which carries no error, reached a
terminal mounted in the background and failed it. The guest reporter now forwards only events with
an error behind them.
* fix(mobile): a parked reader stays parked when a collapse below them clamps the phone page to its end
After every resize the page attached following whenever the scroll sat within 1 px of the end,
whatever put it there. A collapse below a reader parked near the end makes WebKit clamp onto the
shorter end, so the next reply pulled them down. Following now comes only from classified motion
(the reader's own move, or a move toward the end), as on main; the resize callback only pins.
* refactor(mobile): the chat's row list, page window or native FlatList, is its own component
Main's composer and tab changes took MobileNativeChatView past its line cap once merged; the
platform choice of list and its wiring move out unchanged.
* fix(mobile): only a move toward the end makes a parked phone-page reader follow
A clamp onto a shorter end that lands while the reader's finger is still down from tapping a
collapse below them is classified as the reader's move, and any reader move ending at the end
attached following. Following now attaches only on a move down toward the end, as on main; the
test covers the clamp with and without a finger down.
* fix(mobile): a slow drag up detaches a following phone-page reader
Detach needed a single scroll event moving up more than 1px, so a slow drag that reports many 1px
moves never detached, and the next reply pulled the reader back to the end. Any upward move of the
reader's that leaves the end now detaches. Found by tracing the remaining iPhone jump.
* test(mobile): reconcile each step of the slow-drag detach test, as the browser's frame does
* test(mobile): run the slow-drag detach test as iOS Safari, where the bug was seen
* Fix following after a slow return to the chat end
* fix(mobile): closing a tool line forgets its nested command and output scroll positions
A nested box's address is the run's key, then the line's key:command, so the
old prefix match on the closed key never reached it and a reopened line
restored a stale scroll position.
---------
Co-authored-by: Orca Worker <orca-worker@localhost>
Co-authored-by: OrcaWin <alpha-eng@stably.ai>
|
||
|
|
53191250e8 |
Accept messages while any agent starts, and bound each startup step (#28046)
* Finish structured agent readiness before delivering held work * Publish the Codex process before protocol startup * Publish ACP agent processes before session startup * Publish Pi at spawn and settle signed-out starts once * Adapt permission fixtures to startup lifecycle * Keep model discovery for chats before their first start * Use one host readiness promise for provider operations * Await provider opening in the execution host * Await Claude initialization in host-owned open * Observe Claude readiness through the host status * Keep optional readback behind host readiness * Recheck launch permission after host readiness * Preserve the awaited child during permission revalidation * Await Codex protocol open under host ownership * Await ACP protocol open in the host * Derive ACP opening from its protocol session facts * Exercise unanswered ACP options with an explicit silent route * Await Pi open and fail signed-out construction * Read Pi catalog facts from awaited opening * Keep the Pi fixture exit observer * Record failed owned opening before the first message * Abort a matching provider open when its child exits * Cover provider exit during an unanswered host open * Cover explicit failure facts on a host-stopped child * Capture message choices per accepted send and preserve picker state * Apply accepted Claude message choices at prompt handover * Apply captured message choices at provider dispatch * Bind native chat choices to accepted message dispatch * Reset Codex turn choices for each captured message * Resolve Codex message defaults from native facts * Align message choice and Pi default fixtures with provider dispatch * Align option fixtures with saved message choices * Match Codex message choices to the active turn origin * fix: let pending commands reach handover refusal * Abort pre-prompt message choice writes with host-owned wait * Verify message choices through provider input boundaries * Keep legacy mobile test hosts explicit about message choices * Keep startup waits cancellable and preserve operation order * Derive Codex startup exits from host phase * Open Codex protocol in background-task test fixtures * Keep ACP startup ownership until handshake completes * Run ACP opening catalog fixtures with Node SQLite * Verify saved ACP picks without live option writes * Keep ACP catalog bookkeeping from failing an options read * Restore signed-out restart and fail Pi startup faults * Run Pi opening catalog fixtures with Node SQLite * Preserve message choices across cold clients and kept cards * Remove the unused provider option restore ledger * Clarify launch choices and legacy option hydration * Remove remaining restore-ledger forwarding and assertions * Remove unused ACP and Pi restore bookkeeping * Keep live choices and delivery order while agents start * Drop retired startup revision from Codex opening * Keep live-option cancellation until message choices and clarify catalog reads * Hold ACP option replies in live-write cancellation regressions * Remove retired Pi startup revision and match saved-pick behavior * Check running choices and retained rewind messages after delivery integration * Apply startup picks and let parked messages release operations [skip ci] * Remove unused Codex opening report field [skip ci] * Keep ACP restoration facts local to the provider [skip ci] * Apply Pi startup picks after readiness and remove unused open facts [skip ci] * Cover goal changes during the held Codex message wait [skip ci] * Save a waiting startup pick after Stop ends its opening [skip ci] * Save choices when startup ends or a replacement is still opening * Check saved choices separately from Claude startup reports * Release scripted Codex startup before awaiting queued picks * Check the saved picker selection in per-message choice fixtures * Exercise current choices and eager client capabilities in runtime fixtures * Reuse runtime fixture reads and type the fake connection directly * Align startup fixtures and preserve observed Claude exits * Honor queued send authorization in Claude runtime fixtures * Open Codex protocol sessions before Stop fixture dispatch * Wait for the opened Codex child before runtime notifications * Keep repeated permission picks usable and align Codex fixtures * Validate pending picks against the live option state * Order startup picks before later messages and retain flow regressions * Keep reviewed lifecycle contracts while pruning duplicate tests * Save picks after a proven exit without requiring a child * Type the startup reply fixture against the Claude control contract * Keep Codex startup flow contracts and remove overlapping adapter tests * Restore the committed dependency lockfile after test tooling * Keep startup recovery flows and remove duplicate Pi handshake cases * Cover both Pi startup choices in the delivery flow * Match the Pi prompt entry recorded by the scripted wire * Honor the before-input callback in the startup reply fixture * Keep message choice flows and remove overlapping selector tests * Keep startup option menus independent and show the saved choice * Exercise startup picks across real menus and the runtime * Match startup picker fixture to saved message choices * Keep saved effort visible when picking a model during startup * Include the running-turn scope in repeated Stop fixture * Use the current attach contract in import fixtures * Keep the Codex journal translator within its line limit * Match ACP startup fixtures to saved picks and late Stop behavior * Check startup effort after saved picker choices are acknowledged * Complete Stop target types and keep the effort regression concise * Use requested picker values in the effort regression acknowledgement * Require owned provider spawn and one host-awaited open (from #28055) * Restore the live checks the startup cleanup's deleted tests pinned One assertion each, in the surviving suites: a stale-generation exit is ignored, a failed create lets its sink go, a readback before open is saved, an idle stop that fails is retried, a no-pick Pi start teaches its default, an older client's pre-spawn failure and packed answer, an undrivable chat stays readable, the resting slash menu stays on its own host, the main agent reads apart from subagents, restore publishes status, naming runs off the host's status feed, and a throwing logger never reaches the operation. Also corrects the reliability gate note that claimed the send-idempotency suite replaced the refusal-retry tests. * Read a rewind in doubt outside the chat's serialize and apply it in the commit The started settle and the attach's ready-child path read the provider's history first, then apply what it proves in a short commit that re-checks the rewind it read. * Accept messages while the agent starts: run the start outside the chat's serialize The chat's serialize was both where a send was admitted and where its agent started, so every slow step of a start (closing-child join, lease reconcile, recovery probes, resolvers, history read, spawn, sink drain, provider dispatch) held every later send. A start now registers in one short step before its first wait: its abort controller (which Stop, close and quit already reach outside the lock), its startup clock (the 10-minute ceiling now counts from here) and quit's drain. Every wait after runs outside the lock, raced with that signal, and every write is a commit that first re-checks the start is still the session's. A start that loses the check is late: its spawn is closed through the post-acquisition failure path and nothing is handed to it. - The delivery loop's prepare returns a start handle; the loop awaits the start outside, then re-prepares. Handover writes the pending row and begins the dispatch abort handle in one commit, calls the provider outside after re-checking the child, and resolves in a second commit. - Ready operations claim a start instead of running one in their step. - A client attach runs the same steps under one serialize, as before. - An exit reported between spawn and publish is held for the publish commit, which ends the child it indexes. - An unproven close still fails only what was queued when its start was asked for. * Test that a send is accepted at every wait an agent start makes One whole-flow test holds the start at each wait in turn (closing-child join, reconcile probe, recovery re-probe, resolver, spawn, sink drain, open, started recovery, pending row, provider call): a second send is accepted at once, a Stop is recorded without waiting and nothing reaches the provider, and what was sent goes to the published child once, in order. Also quit before spawn, concurrent sends, a lease left recovering, and an idle sweep racing a send. * Bound each startup step on its own and drop the startup silence rule A start no longer ends after 60 s without output: output is not protocol progress. Each protocol step now has its own bound where its handshake lives, and one 10-minute ceiling from registration stays as the outer bound. - Codex: initialize 15 s and thread start/resume on the connection's 30 s default again, instead of the startup ceiling. - ACP: initialize 60 s, authenticate 120 s, session new/load/resume/fork 300 s; turns stay unbounded and option writes keep their caller's bound. - Claude: the initialize answer has a 60 s bound; past it the open fails with the ordinary startup-failure words and the host ends the child. - The onOutput plumbing that only fed the silence rule is removed. * Count Pi ready once it reports its state Pi's start no longer waits for its model listing and slash commands. The open returns after get_state and the unknown-provider check; the listing then arrives as an options report (with the catalog listing the start resolved) and the commands as a later publish. An empty model list no longer fails the start as signed out: a signed-out Pi runs the unknown provider, which still fails it, and a prompt it cannot run is refused with the sign-in words by the existing prompt path. get_state keeps the connection's 30 s request bound instead of the startup ceiling. * Test that model and effort picked while a chat is created ride its first message A slow host's create reply arrives after the user picked a model and effort. The picker's view layers the launch's held picks over its seed and the host's saved selection, and every send carries what the picker shows, so the first message carries the picks whether the host's status names its selection or not, and while the last pick is still on its way to the host. * Start a phone's new empty chat on its first message The phone's new chat is an agent.launch with no prompt, which always created the chat with its agent started, while desktop chats start on the first message. The phone now advertises the create-without-start capability, and a launch with no prompt from a caller advertising it creates the chat at rest, keyed in the ledger by its create intent as a client's create is. Launches with a prompt, the CLI (which advertises nothing), orchestration workers and ensure still start at once; an older host ignores the capability and stays eager. * Take main's lockfile; the branch changes no dependencies * Restore main's exact text in files the merge only reformatted The pre-commit formatter rewrote 37 of main's files staged by the merge, though this branch never changed them, pushing two past the line limit. Committed without the hook because the hook would reformat main's text again; CI lints the result. * Fit the startup tests to the owned-spawn, host-awaited-open contract Test doubles gain the mandatory open and an acquisition generation, the removed started event gives way to the rig's proveStart, and two literal types are tightened for CI's typecheck. A Stop during a held start writes no host note on this branch: the taken-back send draws its own stopped-before-start row, which the startup-attempt test now asserts. * Fit the remaining CI checks to the startup limits and current releases - Run agent-launch-at-rest.test.ts in the SQLite Node runtime project. - Pin the send-options cross-version case to v1.4.228, the first published release with agent-session.send-options.v1; v1.4.226 was never pushed. - Load the baseline's isStructuredTab from src/shared (moved in v1.4.228), falling back to the renderer path for older baselines. - End the never-proven start with a short startup ceiling: the silence limit it relied on is gone, and the ceiling counts from registration. * Read the option stamp from the chat's one host-execution selection * fix: preserve startup authority and accepted agent choices * Retain dispatch authority and reconcile accepted options * fix(native-chat): preserve accepted choices and cancellation authority * Restore established client selection flow * Preserve provider output observation and typed startup fixtures * fix: preserve initial Claude launch choices and effort readback * test: complete Claude catalog session fixture * Preserve incoming main files after merge formatting * Preserve compact handover through startup merge * Type instant startup fixtures before their open is supplied * Keep browser sign-in under its existing expiry owner * Finish the main merge: retired-model display, start rewind cut, new tests A model the Claude CLI retired is reported again, so the picker shows what the child runs and the next send moves the chat off it. The start reads a rewind in doubt against the handle it answered with, the record its commit then applies to. Main's tests follow the start's open: the admission fixture holds a real turn, and a sign-in close during setup fails the open. * Type the start's rewind record as the store returns it * Type the Sleep test's held open as the provider open it replaces * Sleep counts a chat stopped once the start it aborted leaves it at rest A start runs outside the chat's lane, so Sleep's step can run before the start it aborted has settled; it then re-reads the chat once that start is done, instead of reporting it unverified. * Keep Sleep's start-abort bookkeeping beside the rest of Sleep's stop |
||
|
|
20fc7af557 |
test(relay): drill a memory cell through a database outage, and pin phone renewal (#30666)
* feat(relay): add the per-cell phonePath switch, effective only in reserve mode Step 6 of the relay redesign keys every phone-path change on this switch. The cell reports the effective value in runtime status, and the flag tool refuses phonePath=memory unless the cell is reserve, and a reserve flip that would turn phones over unnamed. * feat(relay): resume paired phones from one device read on memory cells With phonePath=memory in reserve mode, a phone resume is admitted from one device read and the cell's own seat, and takes no activity leases. A refused device closes 4401, a failed read 4503, and only a missing device row falls through to the invite path. A phone whose host has no seat gets one assignment read: 4409 when the row names another cell or the read fails, otherwise 4404. * feat(relay): keep a memory resume's basis in the cell and confirm it from one read A phone resumed on the memory path keeps its connection basis in the cell, not in relay_connection_bases. Its confirm rereads the device row once, renews it only when less than 29 days remain, and always answers a current-version confirm renewed:true with the expiry the database holds. Confirms route by where their basis lives, so a flip between accept and confirm completes either way. Retries answer from a 10-min in-memory result map. * test(relay): roll a memory phone cell back with confirms in flight Flipping back by admitMode=db, phonePath=db or a dead-man trip leaves in-flight confirms to finish on their memory basis, writes no phone lease and leaks none, and evacuation still refuses the cell while Postgres records it as reserve. * feat(relay): batch a memory cell's resume-confirmed audit rows Memory confirms buffer their audit rows and the cell writes them as one multi-row insert every 5 s or 500 rows, idempotent by a per-event id. The buffer holds at most 5,000 rows and drops a row older than 10 minutes rather than retrying it, counting and logging both kinds of drop. * feat(relay): count and log phone-path failures, and every attach deadline The runtime metrics line gains phone refusals by close code, attach deadlines, accept database errors, resume confirm failures by code, completed accepts by phone path and the resume audit drop total. Every attach-deadline 4404 now logs what the cell knew of the desktop's control (pong age, ping outstanding, control age and round trip), so a half-open control can be told from a slow data dial. * feat(relay): close an attach-deadline 4404 with the attach-timeout reason The phone close reason from #30607 now rides the close frame of the 4404 a cell sends when the desktop's data leg misses the attach deadline, so OTA phones can redial the same cell once instead of waiting out the host-offline delay. The failed relay-hello frame is unchanged, and old phones ignore the reason. * test(relay): drill a memory cell through a database outage, and pin phone renewal A blocked database leaves splices forwarding, closes new resumes 4503 and fails confirms cleanly, lets an in-flight confirm finish on its memory basis after a flip back, and drops audit rows past 10 minutes instead of writing them late. On the phone, a relay-only session lives 31 days under the once-a-day renewal rule, and would not if a not-due confirm answered renewed:false. * fix(relay-ops): store phonePath=db as no key, so old images keep taking writes An explicit phonePath=db left in the switch file would make the flag tool refuse every later write after an image rollback, including a watcher's admitMode=db flip back. * fix(relay): log a failed no-seat read, recheck the switch after the device read A failed assignment read for a phone with no seat now logs a rate-limited line before its 4409. A switch that turned off during the device read sends the phone down today's path. resolveResume reuses the one-read lookup, so the expiry and grace rules have one copy. * fix(relay): log a failed memory renewal; test flips, replays and basis cleanup A renewal write that fails still answers the stored expiry, but now logs a rate-limited line so a persistently failing update is visible before phones age toward re-pairing. * test(relay): assert a rolled-back memory splice issues no statement when it closes * fix(relay): flush resume audit early only on crossing the batch size, drop the shutdown flush A failing database no longer takes a 500-row insert per confirm; retries wait for the 5 s timer. The shutdown flush raced the pool closing, so it is gone and the at most 5 s loss is documented. A stopped buffer no longer re-arms its timer. * test(relay): assert the aged audit rows were dropped, not written --------- Co-authored-by: E2E Test <e2e@test.local> |
||
|
|
04548087a5 |
fix(mobile): edit a queued message in a plain text box with compact cancel and save buttons (#30382)
* fix(mobile): edit a queued message in a plain text field with compact cancel and save buttons * fix(mobile): the queued edit field grows from one line to four in the phone's web page * fix(mobile): the queued edit's cancel and save buttons take 44pt-tall taps, and the queue no longer jumps while typing - Measure the edit field's height in a hidden copy of the textarea instead of collapsing the live one: WebKit clamped the queue list's scroll position on every keystroke. It also re-measures when the field's width changes (rotation). - react-native-web drops hitSlop, so the X and check were 28pt targets. Each button is now a 44pt-tall target reaching its neighbours, with the 28pt square drawn inside; the layout looks the same. - Announce saving with aria-busy, which react-native-web maps. - List the new web sibling in web-overrides.json. |
||
|
|
cf13e0cf9d |
fix(sidebar): phone feed list leftovers before the flip (dark) (#30631)
* fix(sidebar): phone feed list leftovers before the flip (dark) Bump the feed's semantics version for P11's desktop facts; mark switch-on phone page bundles so packaging and orcad staging refuse them; show the page's last-listed rows before the first feed read; draw the host's pending creations; pass the listed host's own label into host sections instead of the running device's platform. * fix(sidebar): review fixes for the phone feed leftovers (dark) A dark orcad may stage the dark page (only a marker of an off switch is refused); the bundle tree writer leaves the marker; the pre-feed list keeps the view's filters; a creation names its project only outside that project's header and survives search when its project matches; the feed view and row labels read one resolved desktop platform. * fix(sidebar): second review fixes for the phone feed leftovers (dark) Test the off-switch marker check across switches; resolve the desktop platform once for rows and headers; a section search narrows keeps only matching rows. |
||
|
|
f184c13bd7 |
Align root Node types with the supported Node 24 runtime (#30617)
* Align root Node types with the supported Node 24 runtime * Preserve mobile rejection listeners with Node 24 types |
||
|
|
9ea5434371 |
fix(mobile): faster relay recovery after an app switch and on a late desktop data link (#30607)
* fix(mobile): recover a relay that died during an app switch in one short probe, and retry an attach timeout through the director once Refs STA-11130. Co-Authored-By: Claude <noreply@anthropic.com> * fix(mobile): redial the same cell once on attach-timeout instead of re-resolving Aligns with the relay step 6 design: reason string attach-timeout, no director re-resolve (the director names the cell the host is seated on). Co-Authored-By: Claude <noreply@anthropic.com> * fix(mobile): probe decisively only on a real background return, 4s, and jitter the attach-timeout redial Co-Authored-By: Claude <noreply@anthropic.com> * test(mobile): replace type assertions with a typed fake in the relay probe tests Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> |