* fix(review): keep the active review linked through a rebase
A detached HEAD (rebase, bisect, checkout of a commit) is no longer treated
as a branch switch. Only an attached observation can clear the active
review and push target, judged against the branch that last proved
ownership: the in-session branch before the detach, else the owning host's
rebase verdict from the listing, else unknown (never cleared). linkedItems
are never cleared.
- shared rule (hosted-review-branch-ownership) used by the renderer writer
and the runtime WorkspaceGitIdentity
- readRebaseOperation: spawn-free, fail-closed owner-side verdict; git am is
not a rebase
- listings annotate detached rows with the verdict
- explicit push-target pushes refuse while a rebase is active or unverifiable
- opt-in verifyRebaseOperation status verdict gates review AI launches at
the action boundary
* fix(review): gate mobile review actions and bound the rebase listing probe
- mobile fix-checks, resolve-comments and hosted conflict launches require a
fresh owner-side rebase verdict after agent resolution; local conflict
recovery and generic launches are unchanged
- the listing's detached-row rebase probe has a 2s budget; unread rows stay
unknown, and a checkout whose read outlived its budget gets no new one
until it settles
- an empty or malformed HEAD is unverifiable, not a proven detach
- the apply-backend real-Git test is gated on Git 2.26
* fix(mobile): let folder repo review actions launch without a Git verdict
Repo-backed folder workspaces have repo::path ids, so the mobile review-action
gate sent git.status for them and refused the launch. The gate now reuses the
repo the launch context already resolved and, like desktop's isFolderRepo
check, skips the verdict for folder repos; Git workspaces still need a fresh
'none'.
* fix(review): reconcile detached ownership per host and selection
* Fix review action rebase checks for the selected workspace
- Read fresh rebase state from the owning host without a full status scan.
- Rotate detached worktree probes so later worktrees receive checks.
- Avoid reopening the composer when a rebase check refuses launch.
* refactor(review): scope rebase fix to branch ownership
Preserve review associations through detached HEAD using the last confirmed attached branch. Keep unknown ownership conservative and retain host isolation and relink race protection.
Remove the background rebase scanner, rebase-verdict transport, and launch/push gating from this change. Keep the deferred gating work separately and replace the real-Git pipeline tests with focused metadata regressions.
Refreshing a managed Claude profile copied the personal ~/.claude.json
groveChoiceSeenAt map over the profile's map, dropping the profile
account's Consumer Terms confirmation. Every new terminal then made
Claude wait on a network check before starting.
Merge groveChoiceSeenAt per account like projects and mcpServers, and
keep whichever timestamp is newer for accounts present on both sides.
Fixes#40380
Co-authored-by: Neil <neil@stably.ai>
Commands render before skills in the slash menu, so typing an exact skill
name could leave a fuzzy command highlighted and Enter would pick the wrong
item. After each edit or caret move the picker now highlights the row whose
name ranks best against the query. Arrow-key navigation still owns the
highlight between edits, and a catalog refresh keeps the row the user
arrowed to (by id) instead of jumping.
Co-authored-by: Neil <neil@stably.ai>
A stray github.com or bitbucket.org entry in glab's config made the failed
`glab auth status --hostname <host>` probe print a status block headed by a
bare hostname, which the configured-host parser accepted as evidence. The
known-host inventory could likewise list github.com. Because GitLab has
precedence among forge providers, GitHub and Bitbucket repos were offered
GitLab merge requests and GitLab login prompts.
Before trusting any glab evidence, resolveProjectRefForRemote now returns null
when the remote's real host (aliases folded via normalizeForgeWebHost) is
github.com or bitbucket.org. Scp remotes on an SSH alias named e.g. `github`
are unaffected.
Fixes#40386
Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
The watcher deduped on the public payload (branch, displayName, terminals),
so moving focus between two workspaces that project to the same payload was
dropped. Dedupe on the focused worktree id as well; the emitted payload still
omits that id because it can embed a provider path.
Fixes#40502
Co-authored-by: Neil <neil@stably.ai>
* fix(chat): collapse orchestration notices and keep the rail human-only
* test(chat): assert human-only rail previews
* test(mobile): inspect compact orchestration mail through its disclosure
* test(mobile): keep orchestration coverage in a focused suite
* test: retain the agent source type used by message cases
* fix(chat): keep rail reply previews while ticks skip agent-written inputs
Restores the hover card's reply line (the issue asks to keep the existing
preview), drops the shared test fixture and folds the authorship cases
into the rail and outline tests.
* fix(chat): stop rereading an older host's outline as the history window moves
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Kelvin Amoaba <97001695+AmoabaKelvin@users.noreply.github.com>
The "Update shared content" button inherited Button's whitespace-nowrap and
fixed h-8, so long translations (e.g. Russian) ran past the button edge in
the w-80 share popover. Let the label wrap, and stack the idle and
"Updating…" labels in one grid cell so the button keeps its height while
publishing instead of resizing the popover mid-update.
Fixes#40202
Co-authored-by: Neil <neil@stably.ai>
Chat message tables inherited the shared document renderer's fixed
text-[13px], so they ignored the chat Text size setting. Add a chat-only
0.9285714286em (13/14) override so tables stay 13px at the default 14px
size and scale with the message like the heading overrides do.
Fixes#40389
On the phone, Codex's Standard/Fast speed was a Speed row opening a
list while Claude's Fast mode was a switch. A Fast-only speed now uses
the same switch row; more speeds, or a lone Flex, keep the list. The
lone-Fast rule moves to shared code so desktop and mobile follow one
rule.
* fix(right-sidebar): keep the toggle while a workspace is being created
The creation surface has no titlebar strip and the floating toggle only rendered
with workspace chrome, so closing the right sidebar during creation (or on a
failed creation, which stays up until dismissed) left no button to reopen it.
The toggle button, its shortcut and the menu item now share one rule,
canToggleRightSidebar, so the shortcut also works during creation.
* fix(source-control): read the latest review provider when eligibility fails
The failure fallback read the provider through useEffectEvent, which keeps the
first render's handler inside memo components in React 19.2. A provider that
settled after the probe started was ignored. Read it from a ref refreshed in a
layout effect instead.
* perf: defer unused workspace reference cache projection
* perf: select workspace reference cache identities directly
Resolve cache readers once per attachment request so store notifications only read raw entry identities. Keep rendering and fresh reads on the existing projection and routing paths.
Co-authored-by: Neil <neil@stably.ai>
* perf: isolate cached reference readers from store snapshots
Create memoized identity readers in a scope containing only cache name and key so they cannot retain a previous whole-store snapshot through sibling read closures.
Co-authored-by: Neil <neil@stably.ai>
* fix: preserve reference cache reader indexed types
Keep the state parameter generic so indexing a chosen cache retains the matching entry type. Runtime reader behavior is unchanged.
* fix: retain cache key correlation in the reader type
The second index loses the cache-key correlation across the record union. Assert the identical indexed return type with the structural proof documented; emitted reader code is unchanged.
---------
Co-authored-by: Neil <neil@stably.ai>
The dialog body is an overflow-y-auto scroll area with no padding, so the
focused search field's 3px ring was cut off at its edges. Give it 4px of
room with -m-1 p-1, as the Workspace Details dialog already does.
Tracked in #39381
worker-start defaulted --setup to run, so new worker worktrees ran the
setup script even when the project had setup turned off. Without an
explicit --setup, a worker now skips setup when the project skips it by
default; run-by-default and ask keep running it as before. Remote
workers resolve this on the host that owns the project.
Picking a model while a turn runs cleared the reported model, but the
running turn's next reply, still from the old model, restored it. The
composer pill prefers the reported model, so it named the old model
while the menu checked the new one. Replies from the turn that was
running at the pick no longer report a model; the next turn's replies
and init do.
* Keep saved agent models searchable while catalogs refresh
- Load saved catalogs independently of pending refreshes.
- Preserve edited presets until a completed catalog confirms removal.
* wip
* wip
* Preserve model discovery status when a read times out
Keep catalog responses provisional while discovery continues, seed account
catalogs when late results arrive, and retire queued discovery on shutdown.
* Simplify model catalog deadlines and picker state
* perf(activity): reuse tab indexes across status updates
* perf(activity): label tab-index cache temporary
Co-authored-by: Neil <neil@stably.ai>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* perf(activity): drop temporary label on tab-index cache
Co-authored-by: Neil <neil@stably.ai>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Neil <neil@stably.ai>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(native-chat): bring back the chat an unsaved terminal fork came from on Continue in Chat UI
* fix(agent-hooks): name a forked Claude terminal's session from its fork SessionStart
* fix(native-chat): bind a fork's origin chat to the fork's own conversation
* fix(native-chat): bind a fork to its launched pane and keep the stand-in chat's draft
* Simplify Settings chrome and organize General into focused views
* Preserve search and skill actions across Settings views
* Correct Settings validation leftovers
* Keep skill conflict details visible in compact Settings rows
* Refresh Settings search and protected copy catalogs
* Use Settings design tokens and button variants
* Keep Settings test setup and translation catalogs current
* Keep Settings search metadata independent of display labels
* Build repository search groups only for the selected pane
* Keep pending skill installs and server menu labels accurate
* Use the supported role locator in the skill regression
* Keep absent skill installation actions on their supported path
* Restore the Settings sidebar to its existing appearance
* Restore shared navigation title keys in pane headings
* Preserve the Settings shell entry animation
* Drop unused Voice labels after the main sync
* Preserve main Voice translations and the moved skill module import
* feat(agent-session): keep the permission mode a Claude session reports
Claude names its live permission mode on prompt, tool and Stop hooks. Keep the
root agent's latest value on the provider session, next to effort, so a resume
without a recorded launch can tell whether the session ran in bypass.
Ports the capture from #36359 by Nelef.
* fix(agent-launch): resume an unrecorded launch bare unless it reported bypass
Without a recorded launch, every restart path now resumes with no settings
Arguments, env or permission default; the configured Command still runs. A
session whose root agent last reported bypassPermissions keeps its bypass flag,
so a hand-typed claude --dangerously-skip-permissions keeps it while a bare
hand-typed claude never gains one (#29898).
Account-switch restart used the shared rule only for Claude; it now uses it
for every agent. Thanks to JustinCMR (#37304) for finding the account-switch
and cold-restore gaps.
* fix(terminal): keep the launch recipe of a window-started agent in main
A window spawn hands main its recipe, but main kept only the token and agent,
so restarts that main runs (workspace sleep capture, closed-worker reopen)
found no recipe and resumed bare. Keep it with the launch authority, the same
gate that admits the token.
* fix(orchestration): reopen a closed worker with the recipe it launched with
A closed worker's pane record goes away with its tab, so orchestration send
reopened it bare (#37693). The worker row now keeps the recipe Orca launched
the agent with, keeps it when a later capture of the same launch arrives
without one (main loses it on a daemon reattach), and a reopen prefers it over
a pane record that may hold another launch of the conversation.
Adapted from #37757 by Pr1p, without a new column.
* fix(terminal): keep a slept pane's earlier recipe only for the same launch
A host sleep capture without a recipe reused the pane's earlier record for the
same conversation. That is right after a daemon reattach, when Orca's launch
still runs but main lost its recipe, and wrong for an agent typed into the
shell that resumed the conversation: it never ran that launch.
* fix(agent-session): restore a paired pane with the recipe its host recorded
A client restoring a pane on a paired host sends only its recorded Arguments,
and the host filled the rest from its current settings. A saved restore now
replays the host's own record for that pane, or, with none at all, resumes
bare and keeps a bypass only when this host's hooks saw it reported. History
and other user-chosen resumes keep current settings.
* fix(terminal): replay a saved note only for the launch main still holds
A note belongs to the agent in a pane only while main holds that launch's
token: main admits it at spawn and retires it when the launch command
finishes, and a daemon reattach restores neither. Worker, host sleep and
hook/quit captures now take a note only from that attested launch, and no
longer copy an earlier record's note by agent or conversation match, which a
typed resume in the same shell also satisfies. With no attested launch the
unrecorded rule applies.
Replaces the inferred orcaLaunched / recipeLostPaneKeys flags and drops the
mock-only reopen and fixture-echo worker tests.
* fix(agent-status): take a pane's note from the launch main attests
The window carried a pane's saved note into each new record by agent and
conversation match, so an agent typed into the shell that resumed the same
conversation inherited an earlier launch's bypass. Main now sends the note of
the launch it still holds with each status, and the window records only that,
its own launch registration, or, for a restored replay of the very status a
record was written from, that record's note. /clear and compact keep the note
because main still holds the same launch.
* fix(orchestration): reopen a worker with its own recipe or none
A worker without its own note took the note of whichever pane record held the
same conversation. Its reopen now always passes a complete recipe: its own
note, or the unrecorded rule. The unrecorded bypass is also scoped to Claude,
whose hooks are the ones that report a root agent's permission mode.
* fix(agent-session): restore a paired pane from its source pane, never current settings
A saved restore on a paired host looked up the destination pane, which a Wake
opens as a new tab, and fell back to the host's current settings whenever the
client sent its partial Arguments. The client now names the source pane (to
hosts advertising agent-session.restore-source-pane.v1), and the host replays
its note for that pane or, with none, resumes bare keeping only a bypass its
own hooks saw there. Older hosts keep today's behavior.
* fix(terminal): re-arm a launch across a main restart while its agent still runs
The daemon outlives main, so it now keeps main's admitted launch (token and
note) and drops it when that launch's command finishes. On reattach main
re-arms the launch only when the same agent still runs in the foreground:
nothing can be typed into a daemon PTY while main is down, so a running agent
there is the one the unretired note launched. A shell foreground clears the
daemon's note; an unknown read, including every Windows read, declines.
Older daemons keep no note, so restore declines there as before.
* fix(terminal): the daemon retires a launch note when its command finishes
A launch can finish while main is down, so main's own retirement never sees it and a
hand-typed agent in that pane would later pass the reattach foreground check.
* fix(agent-status): a fresh Claude start never inherits the last process's permission mode
A hand-typed resume of the same conversation may run without the bypass the previous
process had, so only /clear and compact carry the mode forward.
* fix(agent-session): name a restore's source pane in the host's own keys
A mirrored pane's key carries the desktop tab prefix, so the host's exact lookup missed
its saved note.
* fix(terminal): retire a daemon launch note by its token from the one retirement owner
A delayed reattach result or a late retire could clear a newer launch's note. Retires now
name the token they retire, the reattach checks its PTY before clearing, and re-arming the
same launch keeps the daemon's completion scanner.
* fix(agent-status): only the launch owner's statement changes a pane's note
Main now states the attested note, or null for none, on every hook status. An OSC or
display update makes no statement, so after a completed turn it no longer erases an Orca
launch's note; a typed resume still drops it through main's null.
* fix(terminal): an attach leaves alone a launch main already holds
The renderer attaches a pane the CLI just launched before its agent starts, so the
attach saw the shell and retired the new launch's daemon note.
* fix(agent-status): a paired client keeps a pane's note only on its host owner's word
The host's session-tabs frame now says whether the Orca launch it admitted still runs on
the published incarnation, and the client applies that fact whichever writer wins the
status. With an owner's word the conversation no longer decides; an older host that says
nothing keeps the same-conversation rule.
* refactor(agent-status): one decision picks a pane's note for every reader
The live entry, quit capture, provider-session updates, account restart and cold
restore now ask the same owner-aware decision. An owner's none is final and, when it
turns, ends the pane's registry entry and saved note, locally and from a paired host's
frame. Cold restore and account restart take that answer for a live invocation and read
the saved record only when none is live. Inlines single-use wrappers.
* refactor(agent-status): a paired retirement test reaches the cold-restore command; trim comments
Replaces the projection and continuity tests with one transition: host frame retires the
launch, the next OSC keeps no note, and the cold-restore command has no old bypass.
* fix(agent-status): an owner's none retires the pane's matched launch registry entry
* chore: satisfy changed-code gates (type guards, SAFETY notes, fixed test clock)
* fix(agent-launch): a teams launch keeps its note; a legacy leaf id restores without throwing
* refactor(daemon): keep the session launch note on the process base so session.ts stays in budget
* fix(agent-launch): a restarted terminal main can't vouch for keeps main's earlier launch rule
* fix(agent-launch): read the pane's current terminal; a finished launch settles an undecided pane
* fix(agent-launch): undecided and held launches end with their process
A finished command, a fresh launch or a replacement process settles an undecided pane;
a newer attach or finish supersedes a foreground read still in flight; a legacy relay
reattach without an incarnation stays undecided; a held launch applies only to the
process it was recorded for.
* refactor(terminal): watch and arm a created terminal's launch in one call
Keeps terminal-create.ts within the 300-line budget after main's growth.
* refactor(terminal): keep main's launch evidence in one immutable record
Replace the undecided flag, the reattach check map and the re-arm's writes
to main's orchestration launch fields with one launchEvidence object
(unknown, gone for an incarnation, or held with its recipe), owned by
launch-evidence.ts.
- A fresh admitted launch is held; any other fresh process is gone.
- A finished command or confirmed exit is gone, sticky for its process:
no reattach, legacy or noted, can revive it.
- A replacement retires its predecessor's evidence without deciding the
newcomer, which may still prove its own launch through a reattach.
- A reattach note is held only after a foreground read sees its agent;
the evidence object fences that read.
- A gone launch clears its recipe and blocks both refill sites; a
deliberate sleep snapshot keeps its recipe past the launch it captured.
- launchToken, launchIncarnationId and launchAgent writes match main.
* fix(terminal): keep launch evidence through a first incarnation and sleep snapshots
- Learning a process's first incarnation binds its held or gone evidence
to it, so a later attach can neither revive a finished launch nor drop
a running one; a known X->Y replacement is fenced as before and now also
clears the predecessor's recipe.
- A status from no Orca launch leaves a sleep snapshot of its session for
the wake and never takes that snapshot's recipe into a live or quit
record.
* fix(terminal): record a runtime split's fresh shell as running no launch
* chore: rerun CI on main with the typecheck fix
A bare filename opened from the floating workspace on orca serve (or a
paired mobile client) was denied even in a trusted cwd, because the
runtime's file authorization added only the chat scratch root. Desktop
and runtime now share one floating-workspace root resolver: the
app-owned floating folder plus the configured cwd only when it is
picker-trusted. An untrusted cwd or the default ~ adds nothing, an
explicitly trusted home is allowed, and a folder above home never is.
The trusted cwd is an exact root, matched as spelled: the real target
must be inside it and the root is never followed through its own link,
including through the Unicode-equivalence fallback. The approval-time
realpath is the root's identity: loading settings and trusting another
folder only normalize existing entries' spelling and never re-resolve
them through links. The one-time legacy seed still canonicalizes once,
a trusted cwd is stored in its canonical spelling at load, ~ expands
against home's canonical path, and a non-string cwd resets to the
default at load.
Fixes#36950
* fix(daemon): reclaim an empty pid file instead of reading it as pid 0
Number('') is 0, so parseDaemonPidFile read an empty or whitespace-only
pid file as a legacy record for pid 0. killStaleDaemon then tried the
owned-record unlink, which never matches, and the next daemon failed
publishDaemonPidFile with EEXIST on every start.
Fixes#39092
* fix(daemon): reclaim a malformed pid record only once its endpoint is dead
publishDaemonPidFile creates the record before writing it, so a live
daemon's pid file is briefly empty. Now that an empty record parses as
malformed, killStaleDaemon must not reclaim it until the endpoint probe
shows the daemon is gone.
* fix(daemon): decline replacement over a malformed pid record on an inconclusive probe
A malformed record is kept when the endpoint probe returns unknown, and
a replacement daemon cannot publish its pid file over it. Report the
owner as unresolved in that case. A missing record keeps the existing
behavior, since the publisher re-probes before replacing.
* docs(daemon): drop stale notes that an empty pid record parses as PID 0
Refs #39092
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: WawasCode <khalatyanwatsche@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
Extract shared state/helpers into leaf modules (pure moves, re-exports kept)
so import/no-cycle can run at full depth with type-only imports ignored.
120 src + 8 mobile cycles -> 0. 10 suppressions remain on the browserManager
singleton hub, which needs DI to break.
* fix(agent-hooks): unwrap outer sh -c wrapper when capturing Antigravity hook process
* docs(agent-hooks): add docstrings and verify PreToolUse/PostToolUse/PostInvocation shell sequence
* test(antigravity): keep outer shell wrapper alive in shell-mode hook test
* fix(agent-hooks): require shell wrapper on Darwin and add OSTYPE=darwin unwrap tests
* fix(agent-hooks): match the macOS hook wrapper on argv[0] only
An agent whose prompt quotes the wrapper (/bin/sh ... ORCA_HOOK_AGENT_PID=) would
otherwise report its parent, the pane shell, as the agent. Drops the OSTYPE test
seam and keeps the unwrap test at the capture layer only.
---------
Co-authored-by: Ivan Ha <info.ivanha@gmail.com>
Co-authored-by: Kelvin Amoaba <97001695+AmoabaKelvin@users.noreply.github.com>