* feat(mobile): make Markdown easier to read on the phone
Line height 19 → 21 for body, list and quote text (desktop's 1.6 prose
leading), h1 18/25 and h2 16/23 so heading levels differ, an 8pt gap
between list items, a hairline border on inline code, and denser table
cells (8×4). Existing theme tokens only.
* fix(mobile): announce GitHub task boxes as read-only checkboxes
Screen readers now hear each task marker as a checkbox with its checked
state, disabled because the text can't be edited there.
* fix(mobile): size table columns for the denser cell padding
* fix(mobile): use ARIA props for the task box so pages announce its state
react-native-web ignores accessibilityState, so the box read as unchecked
and became a tab stop. role, aria-checked and aria-disabled reach the DOM
and native alike; focusable={false} keeps it out of the tab order.
* feat(native-chat): let users hide the per-turn changed files card
Adds a Changed files summary setting under Settings > Chat > Behavior (on by
default) and a Hide button on the card that turns it off everywhere, with a
toast linking back to the setting.
* fix(native-chat): confirm Hide only after the setting saves
Show Hidden only once the setting is saved and report a failed save instead;
keep the card header one line tall; cover saved settings without the key.
* test(native-chat): import getDefaultSettings from shared constants
* feat(native-chat): reveal Hide on card hover and move the setting to Appearance
The card's Hide control is now an icon with a Hide tooltip that appears on
hover or keyboard focus, and the Changed files summary switch lives in
Settings > Chat UI > Appearance, where Reset restores it.
* fix(native-chat): put the Hide reveal classes on a wrapper, not the Button
Button owns its effects; the hover and keyboard-focus reveal now lives on a
wrapper span, as other row actions do.
* fix(checks): show a check's state in its icon tooltip, not beside it
The status word repeated what the icon already shows. The icon now carries
the state as its tooltip and its accessible name.
* fix(checks): draw the status icon from the same state its label names
A finished check without a conclusion spun as pending while its tooltip said
Neutral. Both now come from one conclusion, matching the header counts.
* feat(checks): show when each check ran and how long it took
Check rows in the right-sidebar Checks panel now show how long a
finished check took and how long ago it finished (or started, while it
runs), with exact start and finish times on hover. GitHub check runs,
legacy commit statuses and GitLab jobs carry provider run times; rows
without times show nothing.
A GitLab pipeline that ran on a commit the merge request has since moved
past says so. The GitHub rollup read keys its 60s cache by the expected
head, so a push never serves the previous head's checks.
* test(checks): bound the clock work of check time labels
* perf(checks): rebuild check time text only when its data changes
Clock ticks now move only each row's age; exact times, duration and copy
are built once per data or language change. Move the run-time mapping
tests to the mapping layer to keep the client test file within budget.
* fix(checks): annotate the gh pr checks reply instead of casting it
* fix(checks): say when a running check started; key the rollup cache by a query comment
A running row reads 'started 5m ago' so its age isn't read as when it
ran. The expected head now rides in a GraphQL comment, which servers
ignore but gh's cache key includes. Add the new copy to the English
catalog and test behavior rather than formatted copy.
* fix(checks): let check times truncate before names in a narrow sidebar
At the narrowest sidebar the time text never shrank, squeezing check
names to nothing and pushing status labels out of their rows. The time
now sits beside the name, gives up width first, and the name keeps a
minimum width.
* fix(checks): rebuild check time copy when translation catalogs change
* fix(checks): reserve room for a running check's growing start age
* test(checks): keep REST check-run times covered through the schema
* fix(checks): read the checks rollup without gh's disk cache
gh's cache key ignores the PR head, so after a push it answered with the
previous head's checks, and it never evicts expired entries. Orca's own
head-keyed checks cache and read coalescing already bound the requests.
* fix(checks): hide check times in rows too narrow to show them
Below a 17rem row the time text is hidden, so the narrowest sidebar keeps
main's name, status and link layout; wider rows truncate the time before
the name, which keeps a 36px minimum.
* fix(checks): truncate a check's duration before its age
The age answers when a check ran, so in a tight row the duration now
gives up its width first.
* test(checks): type the rollup fixture
* fix(checks): show check times only in rows with room for them
Between a 17rem and 19rem row the times squeezed names to their minimum
and clipped; hold them back until 19rem, which the default 350px sidebar
clears.
* fix(checks): keep a minimum name width only while check times show
Rows too narrow for times, including the narrower folder-workspace parent
PR rows, now lay out exactly as before.
* fix(checks): show a running check's elapsed time instead of its start age
'started 10m ago' reserved so much room that running check names shrank
to a few characters at the default sidebar width; beside a running status
the elapsed time ('10m') says the same thing in a quarter of the space.
* test(checks): use a type import for the mocked module
* feat(gitlab): filter Tasks merge requests to ones you wrote or review
Tasks > GitLab > MRs could only filter by state, so on busy projects you
had to scan every open MR to find yours. A second set of chips (Everyone,
Mine, Needs review) now combines with the state chips, so "my merged MRs"
works too.
The filter runs on the host that owns the repo's GitLab sign-in, looks up
the signed-in user fresh on the project's own host, and filters by
author_id or reviewer_id in one request per project. A failed lookup
shows an error instead of everyone's MRs, and older paired servers that
would drop the filter ask to be updated.
Co-authored-by: Yash Kakade <yashkakade2015@gmail.com>
* fix(gitlab): keep filtered MR lists honest while loading and on old servers
- The reviewer chip reads "Reviewing": GitLab lists MRs you were asked to
review even after you reviewed them, so "Needs review" overpromised.
- Rows belong to the query that loaded them: after a chip change the list
shows loading, never the previous filter's rows with Start enabled.
- A refresh checks each paired server's filter support once, not once per
project; failed checks show the real error instead of "update the server",
and the web client sends the filtered list to the runtime it checked.
- Signed-out GitLab gets a sign-in message, and an unresolved SSH project
under a filter shows an error instead of an empty list.
Co-authored-by: Yash Kakade <yashkakade2015@gmail.com>
* fix(gitlab): pin filtered MR lists to the server that confirmed support
A refresh now shares one support check per paired server and pairing, and
the filtered list is sent to the exact runtime and pairing that answered,
so a server replaced mid-refresh can't serve "Mine" unchecked. The web
client scopes its shared check to the current pairing and reuses the
existing result unwrapping. The capability test now counts real status
probes: one per server per refresh for 12 or 120 projects.
Co-authored-by: Yash Kakade <yashkakade2015@gmail.com>
* fix(gitlab): discard filter-support answers from a replaced pairing
A paired server re-paired while Orca was asking whether it supports the
MR filter no longer leaves "update the server" from the old pairing's
answer; the check fails with the usual pairing-changed error instead.
Signed-out detection no longer matches a 401 inside a host or port.
Co-authored-by: Yash Kakade <yashkakade2015@gmail.com>
* test(gitlab): type the filter-change test's props without a cast
---------
Co-authored-by: Yash Kakade <yashkakade2015@gmail.com>
The message list re-rendered on every visible-range and scrolling-state change, and each render serialized every slot key and rebuilt the message rail from every slot even when the slots were unchanged. Both now recompute only when the slots change.
Read the /clear floor only when a reveal must decide whether to page, and skip the section partition for an open roster whose children have no rows yet.
Co-authored-by: gsxdsm <gsxdsm@users.noreply.github.com>
* feat(native-chat): holding Cmd/Ctrl turns Send into what Cmd/Ctrl+Enter will do
While the chord's modifier is held alone in a busy chat's composer, Send
becomes a labelled Steer, Queue or Steer queued button, and with an empty
composer the queued card it would steer is highlighted. Releasing the key,
pressing any other key, leaving the composer or the window clears it.
* fix(native-chat): the held-Cmd preview promises only what Cmd/Ctrl+Enter will do
The queue offers its head-card steer only while that card can go (not
being edited, no steer already on its way), Shift or Alt held first and an
open picker show no preview, and a draft Send can't take keeps its button
and reason. The card highlight follows the composer's Steer queued button
in CSS, dropping the state and callback that carried it.
* fix(native-chat): only the composer holding a preview listens for window blur
Every busy chat's composer added window and document listeners; now only
the one showing a held-Cmd preview does, and a hold that outlasts a Cmd+Tab
never shows.
* fix(native-chat): use the Button's own size for the held-Cmd pill, and clear its timer in cleanup
* fix(native-chat): no held-Cmd preview while an IME composes
The IME owns Enter during composition, so the chord shows nothing then. The
listener-count test goes: the work it guarded no longer exists.
* test(native-chat): clean up between held-Cmd preview tests
* fix(native-chat): the held-Cmd delay timer lives in its own effect
The key listener records whether the modifier is down; a separate effect
owns the delay's timer and its cleanup, as React Doctor requires.
* fix(native-chat): a double-click on the held-Cmd button acts once
Sending a draft can turn the button into Steer queued before the second
press, which would steer a queued card the person never chose.
* fix(native-chat): the held-Cmd button is a pill as tall as Send
Terminals a paired desktop or mobile client opened on a headless serve
host got a workspace-less serve-<uuid> session id, so the host inferred
their owner from the cwd. Folder workspaces that share a path then traded
those terminals whenever one was listed, and deleting a workspace stopped
terminals shown in another.
Serve ids now carry the workspace like every other daemon id. Inventory
refresh keeps a persisted layout binding even when a targeted listing does
not include its workspace, so restored serve-<uuid> terminals stay put.
A cwd guess is recorded as such and never drives a worktree-scoped stop.
Fixes#36716
Reported-by: Guy Shechter (@shechter)
Co-authored-by: Bing.Z <zzb@gxsmjx.com>
A tab that vanished from a desktop left no trace of which path removed it.
Each path now logs one concise line with counts and ids, and changes no
behaviour:
- a host frame dropping any of a worktree's mirrored terminal tabs
(rate-limited to one line per host and worktree every 30s, counting
skipped drops);
- a runtime retiring the terminal surfaces of an exited pty;
- the load-time sweep of deregistered repo residue.
Refs STA-12725
Keep Claude plugin status and toast frames out of native chat, and give terminal-input requests an actionable failure with safe conversation recovery. Preserve genuine outside replies while hiding internal repair content.
Tracked in STA-13468. Built on #37480 by @agustin2606.
Co-authored-by: agustin2606 <agustinvarela2606@gmail.com>
* fix(workspace): wake a suspended recipe VM on the host when the workspace activates
Sleeping a recipe VM workspace already runs on the host: the sleep
execution calls the platform's suspendVm, which runs the recipe's
suspend. Waking it did not — the recipe's resume only ran through the
window IPC handler `ephemeralVm:resumeWorkspace`, which the host's own
desktop renderer calls when a sleeping workspace is clicked. So a
slept VM workspace could not be woken from a headless `orca serve`
(no window), a paired client (whose renderer calls its own machine's
IPC), or the CLI; the phone hit the same wall (#34679).
Extract the wake core into `resumeEphemeralVmWorkspace` (mirroring the
host-side `suspendEphemeralVmWorkspace`), keep the window IPC a thin
caller, add the pairing `resumeVm` to WorkspaceSleepPlatform next to
`suspendVm`, and fire a queued VM wake from `activateManagedWorktree`,
the activation path every caller shares. The wake goes through the
workspace lifecycle queue like any wake: a sleep still running sees
the reopen and keeps the VM up, and a wake behind an already-finished
suspend resumes it. The wake is fire-and-forget — a recipe resume can
outlast activation timeouts — so failures land in the log and the
runtime status (resume_failed), and the next activation retries.
Desktop windows keep their IPC path; a host-side wake and a window
resume can overlap only when a phone/web/CLI activation races a window
click, the same double-resume exposure a sidebar double-click already
had.
Refs #36923
Generated-by: GLM-5.3-Flash (ZCode)
* fix(workspace): give recipe VM wakes one owner and serialize them with suspends
The window's resume IPC now goes through the runtime's WorkspaceSleepCoordinator.wakeVm.
A wake already waiting or running is joined, so one reopening runs the recipe's resume
once, and each workspace's VM suspends and resumes one at a time: opening a workspace
while its sleep is still suspending the VM now resumes it afterwards instead of racing
the suspend and leaving it asleep. Lifecycle queueing alone could not give this, since
it is off in shipped builds.
The activation-path wake is dropped: recipe VM workspaces are owned by the VM's own
server, so the host's worktree.activate never resolves them. Waking on remote
activation is a follow-up at the environment-forwarding layer.
Refs #36923
Co-authored-by: ggbdpq <ggbdpq@gmail.com>
* test(workspace): a click after a suspend started wakes the VM again
Refs #36923
* fix(workspace): serialize recipe VM wakes instead of joining them
A click could join a wake that ran before a sleep the user asked for after it,
so the VM ended up suspended though the last action was opening it. Each wake
now queues behind the VM operations before it; the recipe's resume only runs
on a suspended VM, so a wake behind another finds it already up.
Refs #36923
* fix(workspace): wake a recipe VM outside the lifecycle queue; a sleep's reopen check starts at its request
The lifecycle queue waits to observe the workspace's host before running a
command, and a suspended VM is that host, so a queued wake could never resume
it. VM operations are already serialized per workspace, so the wake runs
outside the queue. A sleep now notes reopens from the moment it is requested,
so opening the workspace while that sleep waits its turn keeps the VM up.
Refs #36923
* fix(workspace): check for a reopen when a queued VM suspend actually runs
A suspend waiting behind a resume had already checked for reopens, so opening
a slept pane during that wait still suspended the VM. The sleep now hands its
reopen check to the serialized suspend, which checks it right before it runs.
Refs #36923
---------
Co-authored-by: ggbdpq <ggbdpq@gmail.com>
* fix(remote): drop a restored agent's row when its server closes the tab
A server that restarted publishes the agents it restored without their
worktree. The mirror judges every agent row by worktree and connection,
so such a row counted as another runtime's: when the server closed the
tab, the retraction sweep skipped it and planted no retention marker,
and the desktop kept a greyed "finished" row for a pane that no longer
existed. Republishing never touched it; only restarting the desktop did.
Stamp the frame's worktree on mirrored agent entries that arrive without
one, as the mirror already does for the connection.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(remote): name the worktree on every agent a session-tabs frame publishes
A pane restored after a server restart has no live PTY, and its hydrated hook
row may name no worktree, so the host published its agent without one. Paired
clients judge row ownership by worktree, and older ones kept such a row as a
greyed retained agent after the server closed its tab. Every terminal tab in a
session-tabs frame belongs to that frame's worktree, so stamp it there.
Fixes#38434
* test(remote): load the restored-agent test through the moved runtime fixtures
Fixes#38434
* fix(remote): give every reader of a host frame's agent status the frame's worktree
Older hosts omit the worktree on agents restored after a restart. The store
patch filled it in, but completion notifications compared the host entry
without it against the stored one, so they never matched and a paired client
missed those agents' working and done notifications. remapHostAgentStatus now
fills the frame's worktree, so both readers see the same owner.
Fixes#38434
* refactor(remote): build a frame's agent status in its own helper
Keeps the session-tabs projection under the file-length limit.
Fixes#38434
* test(remote): fix the restored-agent test's clock; type the frame status helper without a cast
Fixes#38434
---------
Co-authored-by: registered2nd <39094401+registered2nd@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
After an orca serve restart, the hook row that recorded a Claude permission
wait is hydrated as restoredUnconfirmed and the explicit-status selector
skipped it, so the send guard saw no evidence and typed into the open menu.
No input reaches a pane while its runtime is down, so a dialog open at
shutdown is still open. A restored permission row (not provider-session-only,
inside the 30-minute window) now stands until a current screen shows no
dialog:
- The current screen is the caught-up headless grid, or, while that grid may
hold only the bytes since reattach, the provider's current frame. An
unreadable, blank or outdated screen keeps the wait (fail closed).
- Status reads, prompt writes and the onPermission wait hold read the
screen first when a restored wait stands, so a dialog answered after the
restart lifts it without waiting on a hook.
- Once a screen rules the dialog out, the restored wait is spent for that
PTY; later output, such as the prompt's own echo, cannot revive it.
Restored working/idle rows stay unconfirmed. Host-only change.
Fixes#35962
The daemon adapter's inventory listing reported every surviving session's
title as "shell", though the listing carries no title. That made-up value
became the fallback tab title when adoption ran before the attach seed, and a
terminal.create retry that adopted a survivor after a restart recorded it as
the terminal's observed title and returned it.
That create-retry adoption also never attached the session's stream, so the
adopted terminal's output never reached main: lastOutputAt stayed null and its
real title never landed until some later inventory refresh attached it.
- The daemon listing reports no title. The attach snapshot's lastTitle and the
stream stay the single source, as for every other terminal.
- A create retry that adopts a survivor attaches it through the same main
attachment inventory adoption uses.
- A new runtime record treats an empty title as unknown, as an update already
does, so it never reads as a manual title.
Inventory adoption already attaches survivors (#36137); this closes the create
retry path and drops the made-up title. No wire change: old and new daemons
send the same listing.
Fixes#22809
* feat(native-chat): open a subagent from the agents strip
Clicking an agent in the strip above the composer now opens its
activity in place in the conversation: the transcript expands the
"Kicked off" list and the child's section and lands on its newest
activity. A child with no activity yet lands on its entry; one whose
spawn is older than the loaded window pages history in (abandoned by
a reader scroll, never past the latest /clear). Stop stays its own
button; command and monitor rows are unchanged.
The transcript's navigation (rail picks, diff reveals and now subagent
reveals) moves into one hook sharing one cancellable history loop.
Supersedes #38470.
Co-authored-by: gsxdsm <gsxdsm@users.noreply.github.com>
* fix(native-chat): find a rowless sibling's entry in the row its roster folds into
Siblings' roster items fold into the first sibling's row, so a later
sibling's own roster item id names a row that is never drawn; a reveal
of that agent before its first activity paged all history and landed
nothing. Resolve the drawn row naming the agent instead.
Co-authored-by: gsxdsm <gsxdsm@users.noreply.github.com>
* test(native-chat): check where a strip reveal lands
Measure the landing: the end of the child's newest drawn row reaches
the bottom of the view, past an open sibling's rows. Fails if the
reveal does not scroll, aligns the row's start, or picks the child's
first row. The reader-scroll cancel now holds a real page in flight.
Co-authored-by: gsxdsm <gsxdsm@users.noreply.github.com>
* fix(native-chat): keep an end-aligned reveal pinned while rows above it measure
The virtualizer reconciles a jump only until it holds for a frame, and
size changes are compensated only for rows wholly above the viewport.
An end-aligned reveal lands with the visible rows above its target
still at their estimates; when they measure taller, the target's end
was pushed below the view (seen in QA on a first click into a closed
roster). While an end-aligned jump is held, compensate growth in the
target and every row above it; a reader scroll ends the hold.
Co-authored-by: gsxdsm <gsxdsm@users.noreply.github.com>
* fix(native-chat): end a reveal's end pin when the reader acts on the content
The held end alignment compensated growth above its row until the next
scroll; Enter on a disclosure above the revealed row then scrolled what
the reader had just opened out of view. A key on the content now hands
the view back, as a press already did. Pin the late-measure test to the
one row it grows.
Co-authored-by: gsxdsm <gsxdsm@users.noreply.github.com>
* fix(native-chat): hold a reveal's end pin by row identity
The end pin held a slot index; a section closing or a sibling's new row
before it shifted indexes, so growth of an unrelated row could move the
view. Hold the row's key, resolve its index once per measurement pass,
and let go when the row is no longer drawn. Any key on the transcript
now releases the pin, including a cancelled Space.
Co-authored-by: gsxdsm <gsxdsm@users.noreply.github.com>
* fix(native-chat): find a held end row in one read while it stays put
The key lookup walked the measurements once per resize, so a streaming
revealed row cost work growing with the transcript every frame. Read
the held index first and scan only when rows moved before it, keeping
the found index. Pass the strip its session's tasks as one prop.
Co-authored-by: gsxdsm <gsxdsm@users.noreply.github.com>
* fix(native-chat): rebase the virtualizer target when the reader takes an end landing
Co-authored-by: gsxdsm <gsxdsm@users.noreply.github.com>
---------
Co-authored-by: gsxdsm <gsxdsm@users.noreply.github.com>
Strict snaps cannot create dot-directories in $HOME, so a snap-launched
ORCA_BROWSER_EXECUTABLE could never lock its profile under ~/.config/orca.
Detect launchers that dispatch through snapd and place the profile in the
snap's common data directory, and report an unwritable profile directly
instead of the driver's --no-sandbox hint.
Fixes#35999
Agent sleep only treated the host window's own foreground tabs and phone
driver locks as in use. A paired desktop showing a tab, or anyone typing
into it from another client, never reached the planner, so the host slept
the tab the user was looking at.
The runtime now answers "is this PTY in use elsewhere": a remote view
that reports hidden panes (it pauses output while hidden) and is not
paused, a phone driver, and the last user input from any client. The host
window asks its main process over IPC for its own PTYs; a client planning
runtime-hosted tabs gets the same facts from terminal.list
(includeAttention). The planner treats a shown PTY like a phone-driven one
and folds remote input into its input guard.
Clients too old to report hidden panes never count as showing a tab, and
hosts too old to report attention leave sleep as it was, so a missing
signal never turns into never-sleep.
Fixes#36427
Inert refactor: move the same-store parent rule and merged resolver into src/shared/worktree/workspace-lineage-graph.ts and add one row identity rule. No behaviour change.
#37294 outlined the host pill on the selected workspace card at 80% of the
sidebar text color, so in dark mode the pill's border outshone its own label
and the branch name. Tint the sidebar foreground 40% into the sidebar
surface instead, the style guide's recipe for a tint from existing tokens.
The outline stays visible on the selected wash (about 2.5:1 dark, 2.3:1
light by default), stays neutral in accent-colored themes, follows each
theme's sidebar colors, and sits below the label's contrast.
Fixes#38641
* fix(browser): negotiate a larger tunnel flow-control window for high-latency links
Embedded-browser tabs routed through a paired runtime (e.g. another Mac over
Tailscale) were capped by a fixed 256 KB per-stream flow-control window. That
window also bounds per-stream throughput to window / RTT: on a ~90 ms link the
ceiling is ~2.9 MB/s per connection regardless of real bandwidth, while on a
sub-millisecond LAN the same window allows ~256 MB/s, so the limit is invisible
locally and dominant remotely.
The window value is validated symmetrically on both ends (each side rejects a
credit grant above its own constant), so simply raising the constant would break
browsing between mismatched Orca versions. Instead negotiate it: each side
advertises its maximum (client in the attach params, host in the ready event)
and both use min(own, peer), falling back to the legacy 256 KB window whenever
either side does not advertise. Advertising peers settle on a 2 MB window (~8x
the high-RTT ceiling). Retained buffers and the outbound memory budget scale to
cover the larger window.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(browser): track granted receive credit and scale the session retained limit
Review fixes for the window negotiation: both ends still initialized
receiveCredit to the legacy 256 KB constant while announcing the negotiated
window, so a peer legitimately using the announced 2 MB window tripped credit
validation — the client closed the tunnel and the session treated the traffic
as a protocol violation. Initialize credit to zero and assign it where the
grant is announced, so tracking always matches the announcement; a conformance
test now drives a full negotiated window in both directions unsettled.
Also scale the per-session retained-byte limit with the negotiated window
(16 windows, floored at the previous fixed 8 MB) so concurrent streams on a
slow link keep their stream-count headroom instead of being shed at four.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(browser): scale the pending-chunk cap with the negotiated window
The 256-chunk pending caps paired with the legacy 256 KB window at 1 KiB
frames. Under a negotiated 2 MB window, more than 256 small unsettled frames
fit inside the byte credit, and the receiver treated that permitted traffic
as an overflow — the session closed on the destination write path and the
client fenced the whole tunnel on the receive path. Derive the cap from the
maximum window at the same 1 KiB-per-chunk pairing (2048), keeping the flood
bound for tinier frames. The conformance regression now drives a full
negotiated window of 1 KiB frames in both directions.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(browser): size claim capacity to the byte budget at chunk granularity
Each retained chunk consumes one claim, so the fixed 16,384-claim host limit
exhausted at eight stalled 2 MB windows of 1 KiB frames — 16 MiB of a 64 MiB
byte budget — and shed the next stream as a receive-buffer overflow. Derive
the claim caps from the byte budgets at the same 1 KiB-per-chunk pairing the
pending caps use, so claim exhaustion never precedes byte exhaustion for
in-protocol frames. Claims are counters, not stored entries, so the higher
caps retain no extra memory.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(browser): bound larger tunnel windows with connection-level credit
Fixes#38053
Rework the window negotiation onto a window.v2 capability: the client
advertises network.browserTunnel.window.v2 and the host echoes it on
ready. v2 tunnels add a connection window per direction (16 MiB to the
client, 4 MiB to the host) acknowledged on receipt over stream 0, so the
queued bytes on either WebSocket stay well below its overflow cap however
many streams grow, and a stalled reader cannot starve other streams.
Older peers keep the legacy 256 KiB / 256-chunk bounds.
Streams start at the legacy 256 KiB window and grow toward 2 MiB by the
bytes their reader consumes, drawing on the connection window. Growth is
charged only while a stream holds unread data: it is released when the
reader drains and recharged when data arrives again, so idle keep-alive
sockets don't hold a fresh download at legacy speed. Connection
acknowledgements are sent after the Data frame is dispatched, so a
synchronous peer cannot reorder its next frame ahead.
Tradeoff: because idle streams are not charged, an idle grown stream that
wakes and then stalls can exceed the growth pool by its own growth. The
per-stream memory budgets still fail that stream rather than the tunnel.
Co-authored-by: jeky22 <38278199+jeky22@users.noreply.github.com>
* fix(browser): keep grown tunnel windows charged until a stream is genuinely idle
Fixes#38053
Growth was released whenever a reader drained, and Chromium drains at
once, so the connection pool barely bounded growth: 16 grown downloads
that stalled together reached the 32 MiB client budget and were reset,
and 8 stalled uploads passed the host's 12 MiB retained limit.
Growth now stays charged while a stream holds it. A stream gives its
charge up only once it is idle (drained, nothing received for 1 s) and
another stream needs the pool, so kept-alive sockets still don't slow a
fresh download. A woken idle stream is charged again; while that
overcommits the pool, consuming streams withhold credit to shrink back
toward the legacy window. Zero-credit updates are not sent.
* fix(browser): charge each tunnel frame a credit floor and complete empty writes
Fixes#38053
The WebSocket queues cap frames as well as bytes, so a connection window
spent on 1 KiB frames could queue 4,096 frames and close a paired
transport capped at 2,048. Each DATA frame now costs at least 8 KiB of
connection credit on both sides, bounding queued data frames to 2,048
toward the client and 512 toward the host.
An empty write found no credit to spend and waited forever, blocking
every later write; it now completes at once.
---------
Co-authored-by: jeky22 <38278199+jeky22@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* fix(cli): use the terminal's worktree when cwd is outside every worktree
Browser, terminal and computer commands run from an unmanaged cwd fell back to the worktree focused in the window. They now use the calling terminal's ORCA_WORKTREE_ID or folder ORCA_WORKSPACE_ID before that default. Fixes#37899.
* fix(cli): resolve the terminal worktree against the catalog
Match ORCA_WORKTREE_ID on the caller's host so ids shared across hosts
go out as identity:, accept folder: ids from either terminal env var,
and keep the runtime default when worktree.list cannot be read.
* fix(cli): refuse a stale terminal worktree instead of using the focused one
When ORCA_WORKTREE_ID names a worktree the catalog no longer lists on the
caller's host, fail with a --worktree hint rather than silently targeting the
workspace focused in Orca. Clear the terminal's workspace env in the test setup
so CLI tests run the same inside an Orca terminal.
* fix(cli): keep the floating terminal on the runtime default
The floating terminal's ORCA_WORKTREE_ID is a sentinel with no catalog row, so it must not hit the stale-workspace error. Word that error as what the CLI can tell: the workspace is not listed, which also covers hidden worktrees.
* fix(cli): ignore terminal workspace ids from another Orca profile
A shell from the installed app that targets a dev build or throwaway serve carries worktree ids that runtime never issued. Reuse the runtime-source fence rule to skip them, so those commands keep the target runtime's default instead of failing.
* test(cli): drop tests that pass without the terminal-workspace fallback
Remove the list-failure and cwd-precedence cases, which pass on main, and the SSH stubs the folder case never reads.
---------
Co-authored-by: Kelvin Amoaba <97001695+AmoabaKelvin@users.noreply.github.com>
* feat(mobile): render GitHub alerts, task boxes, inline tags, images and footnotes
GitHub text on the phone now draws what desktop and github.com do:
> [!NOTE]-style alerts (desktop's marker rule, shared), read-only task
boxes, <sub>/<sup>/<kbd>/<ins> and <b>/<i>/<del>/<code> inline tags, https
<img> and Markdown images at their authored width, raised footnote numbers,
and #123 links in the PR view into the PR's own repository.
* fix(mobile): keep linked badges and commented images, read only pixel widths
An <a href> around inline content becomes a link, so a badge keeps its
target; images inside links, comments, icons by height, and percentage
widths stay as before. Footnote numbers follow the text scale.
* fix(mobile): keep images inside headings and styles in place, read link hrefs as HTML does
Only images that a p, div, picture or center places are cut out of HTML,
in one pass; an image in a heading, link or bold stays in its element.
Inline <a> hrefs use the shared attribute reader (quotes and entities),
<code> keeps only the text of tags inside it, and alert titles follow the
text scale.
* feat(mobile): usage-limit card with the host's automatic continue, Continue and Switch Account
* refactor(mobile): give the notice disclosure its own file to fit the usage-limit status
* fix(mobile): drop the cancel failure once the host shows the cut cancelled
* fix(mobile): keep the usage-limit status up while a later re-ask is pending
A re-ask at a new journal cursor no longer blanks the reset line and pills;
the cut keeps its last answer. Don't continue reports its disabled state.
* perf(runtime): hash granted file downloads once instead of per page
Each 384 KiB page of a digest-pinned granted file re-read and re-hashed the
whole file (~270 MiB of work for 10 MiB) on the host main process. Sequential
pages now feed a running SHA-256 checked against the pinned digest on the EOF
page; the client discards the download on failure. Out-of-order, procfs, and
concurrent pagings keep the whole-file check.
* perf(runtime): verify each granted-file page against per-block digests
Replace the cross-page running digest with 64 KiB block digests taken at grant
time from the same read as the whole-file digest. Each page now reads only the
blocks it touches and is checked on its own, so every page is verified before it
is returned (as before), with no per-grant paging state and no reliance on the
client discarding a download after a failed EOF page.
darwinVersionForMacos kept only the major of LSMinimumSystemVersion, so a future
15.4 floor would stamp Darwin 24.0.0 and still offer the update to macOS 15.0.
Darwin minors drift from macOS minors, so non-.0 floors now fail the build until
a verified mapping is added.
On pages adding more than 200 nodes per check, the detector walked the whole
document for shadow roots every 500 ms. Whole-document walks now run at most
once per 5 s on their own timer; form-gone checks keep their cadence, and
typing or focus still discovers a field's shadow roots immediately.
The path filter config/scripts/*dependency-security* stopped matching after the
scripts moved under config/scripts/dependency-security/, so scanner-only PRs
skipped the audit.
Show Codex file-change proposals in approval cards and keep verbose operation details collapsed by default on desktop and phone, with reason, access and answer choices visible.
Preserve approval identity and answer behavior, keyboard focus, and scroll position when details expand or collapse.
`orca terminal read term_abc` failed with a bare "Unknown command" because the
handle landed in the command path. When a term_/structworker_ handle follows a
command that accepts a target --terminal flag, suggest the flag form instead.
Caller-identity --terminal commands are skipped. The terminal-handle prefix
check moves to a dependency-light shared leaf reused by the session-address
codec.
Fixes#38312
* fix(mobile): unify diagnostics navigation
* fix(mobile): reuse settings bar and bottom insets
Address Orca PR #37904 reporter feedback in comment 6620705890.
* fix(mobile): match diagnostics content spacing to Settings screens
Reuse settingsScreenStyles for the container and content padding, and drop
the leftover bottom spacer now that the bottom inset pads the scroll.
---------
Co-authored-by: terminalchai <213856599+terminalchai@users.noreply.github.com>
Co-authored-by: Kelvin Amoaba <97001695+AmoabaKelvin@users.noreply.github.com>
* fix(git): refuse unsafe branch switches in an existing workspace
The phone's Switch branch ran `git checkout` with no checks: uncommitted
files moved to the other branch, and a switch could land under a working
agent or mid-rebase. One host check now covers every client: it refuses,
with a plain reason, when files are uncommitted, a merge/rebase/
cherry-pick/revert is in progress, HEAD is detached or unborn, another
git action is running, or an agent in the checkout is working or waiting
for approval. Checks and the checkout run under a per-checkout lock.
After a switch the host clears the old branch's review links and push
target and tells every client, so a phone-only host stays correct, and
records that the user chose the branch so first-work naming never
renames it. The open Commits list follows the new branch.
* fix(git): review round 1 repairs for the guarded branch switch
- check out through the provider so the user's switch keeps the interactive git queue
- a landed switch that Windows left files behind on now tells the user instead of reporting success
- never let a Windows yes/no prompt hold the switch lock
- refuse submodule edits that submodule ignore config hides from status
- record the switch on the target host's metadata row and never fail a landed switch on bookkeeping
- read workspace paths from their ids instead of scanning the catalog on every switch
- admit agent rows like the sidebar roster (listed tab, or a live terminal by pane, tab or handle)
- move the host-side switch hooks into their own module
* test: type the new switch test fakes instead of casting them
* fix(git): second repair round for the guarded branch switch
- report files Windows left behind from git's own unlink warnings, not a status re-read, so
ordinary branch differences and submodule commit changes are never called failures
- the phone refreshes its branch and files after a switch that landed with a warning
- refuse only edits inside a submodule its ignore config hides, never a commit mismatch alone,
reading status without optional locks in the interactive queue
- read the agent process probe when a switch runs, not while the runtime is constructed
- check the agent's checkout path before terminal liveness and build the tab list once per switch
- the runtime wiring test no longer reaches real git
* fix(git): scope the switch's agent check to the checkout and keep landed-switch outcomes honest
- find candidate agents by checkout path first; build terminal liveness once, only from this
checkout's terminals and its workspaces' saved tabs; probe candidates together and re-read
the snapshot once; no work for unrelated workspaces (contract test at N and 10N)
- a checkout that fails after HEAD moved is recorded as landed; files left behind are named only
when git reports it could not replace them, otherwise git's own error is shown
- parse git's unlink warnings for paths with apostrophes
- the phone keeps the refusal or warning visible while it reloads the branch
- cover a submodule that is only at another commit
* fix(git): name git's own error when a switch lands after a failing hook; use the shared unlink parser
* fix(git): keep the switch's agent check flat for unrelated folder workspaces
- read folder workspace directories once per switch instead of a linear search per row and terminal
- re-read each candidate's current status by pane key instead of rebuilding the whole snapshot
- skip the hidden-submodule status pass in repos without submodules
- the cost contract covers folder workspaces, many in-checkout agents and PTY passes
- a real-git test covers a switch that lands before a failing post-checkout hook
* fix(git): never quote git's progress line as a landed switch's error; fixed clock in the cost test
* fix(git): decide the hidden-submodule check from the index, not a visible .gitmodules
* perf(git): read only index modes for the submodule shortcut so large repos stay under the output cap
On a multi-question card only a click on a step tab changed question.
Left and Right now step to the previous or next question while an
option row or a step tab has focus, keep every answer, and stop at the
first and last question. Inside the answer field they still move the
caret.
Co-authored-by: Michael Schaecher <276865230+big-sheq@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
On macOS, privacy protection can stop Orca from copying another browser's
cookie database. The copy fails with EPERM, and Orca told the user to close
the browser, which does not help. Chromium and Firefox imports now show the
Full Disk Access hint that Safari imports already had.
Co-authored-by: Kelvin Amoaba <97001695+AmoabaKelvin@users.noreply.github.com>