Host and WSL installs now grant trust for Orca's managed status hooks through
codex's own hooks/list -> config/batchWrite -> re-list verify, scoped to
exactly the managed entries; the previous computeTrustedHash lane is the
unchanged fallback for incapable/erroring CLIs. getStatus and the removal
paths recognize ledger-recorded codex hashes so drift between codex's real
algorithm and the replica no longer misreports or strands trust. SSH remote
install is untouched by design.
Staged internal flag (default OFF, no settings UI): route the SYSTEM-DEFAULT
Codex account at the user's real ~/.codex instead of Orca's managed runtime
home. Flag OFF is byte-identical to today; managed (multi-account) selections
are unchanged in either state.
Routing (flag ON + host system default = no managed account):
- CodexRuntimeHomeService.prepareForCodexLaunch / prepareForRateLimitFetch
return null so the PTY/env layer injects no managed CODEX_HOME and the
rate-limit fetcher + auth-presence gate fall back to ~/.codex (the background
poller stops spawning Codex against the managed home — the #5370 auth war).
- buildPtyHostEnv strips only a nested-Orca-inherited Orca-owned override
(CODEX_HOME matching the private ORCA_CODEX_HOME marker), preserving a
user-set CODEX_HOME. Shell-ready re-exports already no-op without the marker.
- The headless commit-message Codex path strips the same inherited override.
Hook install for the real-home lane (append-last into ~/.codex/hooks.json,
trust via the app-server client) lands with the trust plumbing; the managed
hook install is skipped for this lane meanwhile.
Credit @jellychoco (#8606) for the native-home routing direction.
Depends on the codex trust-rpc-grant plumbing for the real-home hook installer.
* Surface Grok unified-billing monthly usage instead of a permanent warning
Unified-billing Grok accounts have no weekly credits: the
/billing?format=credits view returns a config without
creditUsagePercent, so the status bar was stuck on 'Grok billing
response did not include credit usage' even though the account has a
real quota. The default (format-less) /billing view reports it as an
included monthly budget (monthlyLimit/used with the billing period).
When the credits view has no weekly credit usage, read the default view
and surface monthly usage as the provider's 30-day window (already
supported by the tooltip and chip visibility for OpenCode Go). If the
fallback read fails, the previous 'unavailable' presentation stands
rather than escalating to an error chip.
* Review fixes: chip renders monthly-only usage; fallback failures keep stale data
- StatusBar ProviderSegment: monthly window is chip-visible when it is the
sole window (Grok unified billing); fetching/error no-data guards and the
icon-only dot now count monthly, matching tooltip.tsx. OpenCode Go chips
are unchanged (monthly stays tooltip-only next to session/weekly).
- grok-fetcher: monthly-fallback request failures propagate as 'error' so
applyStalePolicy keeps the last good monthly snapshot; 'unavailable' is
reserved for a successful response without monthly fields.
* Settings: show Grok monthly usage row for unified-billing accounts
Why: the Grok accounts section only rendered the weekly-credits row, so
unified-billing accounts showed a signed-in state with no usage at all.
* Use generated localization keys for Grok monthly copy
* Classify Codex app-server chatgpt-auth-required as an auth error
When auth.json holds only an OPENAI_API_KEY (no ChatGPT tokens), the
app-server RPC rejects account/rateLimits/read with "chatgpt
authentication required to read rate limits". That string matched none
of CODEX_AUTH_ERROR_PATTERNS, so fetchCodexRateLimits fell through to
the hidden PTY /status probe, which cannot render usage for such
accounts and burned the full 15s PTY timeout on every refresh cycle,
surfacing as a permanent "Refresh failed — PTY timeout" status chip.
Classify the message as an auth error so the RPC result is returned
directly (fast, accurate) and no PTY is spawned.
* Keep auth-required usage errors from rendering as a rate-limit Limited label
The new Codex app-server error 'chatgpt authentication required to read
rate limits' mentions rate limits only as the object it failed to read,
but the status bar's rate-limit classifier matched the phrase and
labeled the chip 'Limited'. Classify authentication-required messages as
auth failures so they get the standard softened refresh copy instead.
* Suppress Git Credential Manager OAuth popup on git clone (fixes#7652)
Orca's git runner disables the interactive credential prompt on every git
call that goes through gitExecFileAsync/gitStreamStdout, but the two raw
'git clone' spawns (desktop repos:clone and the runtime clone path) passed
no env, so they inherited process.env with no guard. On Windows a clone
that needs GitHub auth then makes Git Credential Manager pop its
'Connect to GitHub' OAuth window, and in a network-restricted intranet the
browser/device flow never completes while git's credential retry re-pops it.
Apply nonInteractiveGitEnv() to both clone spawns so the prompt is
suppressed (GCM_INTERACTIVE=never, credential.interactive=false,
GIT_TERMINAL_PROMPT=0). The credential *helper* is kept, so cached-token
clones for private repos still work; only the interactive fallback popup is
disabled and the clone fails fast with a clear error instead.
* Suppress GCM OAuth popup in agent terminals and setup hooks too (#7652)
The clone-spawn fix stopped Orca's own managed git from popping Git
Credential Manager, but git run in terminals and setup scripts inherited
process.env with no guard. That is the more likely source of the reported
loop: agents are told to run 'git pull --rebase'/'git fetch'/retry 'git
push' (preamble + conflict/push-failure prompts), and each retry re-pops
GCM's 'Connect to GitHub' window in a network-restricted intranet.
Apply the credential-prompt guard to:
- setup/archive/hook scripts (hooks.ts non-WSL exec env), which run
unattended on worktree create/archive.
- the shared PTY host env (buildPtyHostEnv), via a small
applyTerminalGitCredentialPromptGuard helper. Agent terminals are
guarded unconditionally (they cannot dismiss a GUI popup); user
terminals are guarded by default via the new
terminalSuppressGitCredentialPrompt setting so power users can opt out.
The credential helper is kept, so cached gh auth still works; only the
interactive fallback prompt is disabled. Verified end-to-end in a real
Orca terminal (GIT_TERMINAL_PROMPT=0 + GCM_INTERACTIVE=never by default;
absent when the opt-out is set).
* Scope user-terminal credential guard to Windows, add settings toggle, forward guard into WSL (#7652)
* Retrigger PR checks (Actions dropped the synchronize dispatch for 57e7ce249)
* Keep shell locale out of the terminal/hook credential guard (#7652 review fix)
* Fix Fable review findings: guard WSL hook branch, wire settings search, catalog keyword keys, sparse-env askpass, one-shot agent classification (#7652)
* fix(terminal): harden Git credential popup guard
* test(pty): cover SSH credential guard setting
* fix(git): guard remote clones and setup runners
* fix(git): scope credential guards to unattended work
---------
Co-authored-by: Brennan Benson <brennanbenson@Brennans-MacBook-Pro.local>
* Fix stacked-worktree PR creation targeting a local-only parent branch
- Resolve the eligibility default base to a remote-tracking ref instead
of blindly trusting the submitted parent branch, since a stacked
worktree's base is often a local-only branch the remote can't resolve
- Add a create-time hard block (base_not_on_remote) so a stale or
unpushed submitted base fails with actionable copy instead of the
provider's opaque error
- Update the dialog's default-base resolution and blocked-action/
dropdown copy to match the new remote-validated default
* Split hosted-review-creation.test.ts to fix max-lines lint error
Moved getHostedReviewCreationEligibility tests to a separate file (hosted-review-creation-eligibility.test.ts) to reduce the original file size from 880 to 579 lines, satisfying the max-lines lint constraint.
Co-authored-by: Orca <help@stably.ai>
* Fix Create PR intent flow to use remote-validated eligibility default fo
Prefer eligibilityDefaultBaseRef over the raw compare base when resolving
the review base for the one-click Create PR intent flow, since eligibility
is recomputed from the same compare base right before creation and already
corrects a local-only stacked parent to the repo default. Falls back to
the compare base only when eligibility supplies no default.
* Simplify base-ref remote existence check into a single for-each-ref call
Combine the wildcard and exact-tracking-ref lookups into one for-each-ref
invocation with multiple patterns instead of two sequential git calls,
removing the redundant rev-parse fallback path.
---------
Co-authored-by: Orca <help@stably.ai>
* fix(runtime): preserve terminals during headless desktop activation
* rm design doc
* Fix desktop activation launch ordering and blocked-window status resolut
- Check desktopWindowStatus before spawning the Orca app so a blocked
runtime no longer launches a doomed second instance.
- Reuse resolveDesktopWindowStatus for remote runtime status so it
honors the same authoritativeWindowId fallback as local status.
- Re-check the authoritative window at spawn time instead of trusting
a possibly-stale snapshot, since it can be destroyed mid-await.
- Harden the e2e activation spec against silent spawn failures.
---------
Co-authored-by: bbingz <zzb@gxsmjx.com>
* feat(mobile): show usage reset countdown on accounts screen
Surface the rate-limit reset time ("5h resets in 3h 54m · 7d resets in
6d 7h") under the usage bars on the mobile accounts screen, matching the
desktop status-bar tooltip copy. The resetsAt timestamps already arrive
in the accounts.subscribe snapshot; this only adds the presentation.
Claude-Session: https://claude.ai/code/session_01FvjvCsc9QoyQALqvxkvDqQ
* docs(mobile): JSDoc for new usage reset selectors
Claude-Session: https://claude.ai/code/session_01FvjvCsc9QoyQALqvxkvDqQ
* refactor(mobile): per-bar reset countdown instead of combined line
Drop the redundant "5h/7d" prefixes — each countdown now renders under
its own bar ("Resets in 3h 54m"), matching the desktop tooltip copy
exactly.
Claude-Session: https://claude.ai/code/session_01FvjvCsc9QoyQALqvxkvDqQ
* Extract shared reset-countdown formatter for desktop and mobile
- Move duration/countdown formatting out of tooltip.tsx into
src/shared/rate-limit-reset-format.ts so mobile's account-usage-state
can reuse it instead of a duplicated copy (with tests).
- Re-export formatResetCountdown from tooltip.tsx to avoid touching
existing import paths.
- Resend the pairing deep link once more in start-emulator.mjs since
the first can arrive before the Expo app's JS router is ready.
---------
Co-authored-by: kaynan <kaynan.camargo@terceiro-sky.com.br>
Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
* feat(mobile): start a workspace from a branch, issue/PR, or Linear ticket
Unify mobile workspace creation with desktop. The "+" Create Workspace
modal now has a primary "Start from" field that opens a tabbed search
drawer (Branch · GitHub · GitLab · Linear), letting a user start a
workspace from an existing/new git branch, a GitHub issue/PR, a GitLab
issue/MR, or a Linear ticket — in addition to the default blank workspace.
No new backend is required: the search RPCs (github.listWorkItems,
gitlab.listWorkItems, linear.searchIssues/listIssues, repo.searchRefs) and
the worktree.create linked-item params were already used by the mobile
Tasks screen. This surfaces them in the create flow, reusing the existing
pure modules (buildTaskWorkspaceCreateParams, shouldResolveHostedReviewStartPoint,
filterAvailableTaskProviders).
Details:
- New pure modules: workspace-source-selection, use-workspace-source-search,
source-workspace-create, worktree-create-retry, blank-workspace-create
(the blank/retry path extracted from the modal for reuse + line budget).
- New UI: WorkspaceSourcePickerDrawer (+ row) and SetupHookTrustDrawer
(extracted from the modal).
- Older paired desktops (missing the mobile.tasks.v1 capability) degrade to
Branch + Blank only; GitLab/Linear tabs appear only when available.
- GitHub/GitLab sources pin their repo; switching repos resets the source.
PR/MR sources resolve their base branch at create time; SSH repos gate
search until connected (Linear search is repo/SSH-independent).
* fix(mobile): hydrate settings/trust before availability probes settle
Review fixes for #7985: setTrustedOrcaHooks/setRuntimeSettings no longer
wait on status.get/preflight.check/linear.status (a first-open
preflight.check can take seconds, widening the spurious setup-trust
re-prompt window). Also adds param-parity tests for createBlankWorkspace
and a GitLab MR base-resolve test.
* feat(mobile): match desktop's Smart source picker exactly
Rework the mobile create-workspace source picker to be a faithful port of
desktop's Smart picker instead of the earlier divergent "Start from" drawer.
The mobile field is now the workspace-name input AND the source search, with the
exact desktop tabs — Smart · GitHub · Linear · GitLab · Branch · Name. "Smart"
fans out across GitHub + GitLab + Linear + branches, prepends a "Use '<name>'"
row, and resolves pasted URLs / #123 / STA-42 to exact items (with a cross-repo
switch prompt). Selecting a source shows a pill and moves the editable name into
Advanced. The invented "Blank workspace" concept is removed — the neutral state
is just a typed/empty name (blank submit still yields a creature name).
DRY: the pure desktop logic (smart-workspace-source-results, -command-value,
github-links, gitlab-links, work-item-link-query-bounds, github-work-item-identity)
moves to src/shared/new-workspace/ with re-export shims at the old renderer paths,
so both renderer and mobile share one implementation. composer-branch-selection
and workspace-name were already shared and are reused directly.
Two read-only lookup RPCs are allowlisted for mobile so pasted GitLab URLs and
cross-repo GitHub URLs resolve to exact items (github.workItemByOwnerRepo,
gitlab.workItemByPath).
New mobile modules are split for max-lines: use-mobile-composer-source (selection
state + desktop-parity handlers, PR/MR base resolve), use-smart-workspace-source
+ smart-source-fan-out/-search-requests/-paste-intent (RPC orchestration),
composer-linked-work-item / work-item-lookup-text / mobile-smart-source-modes
(pure logic), and SmartWorkspaceSourceField/Drawer/Row + SmartWorkspaceAdvancedFields.
Replaces WorkspaceSourcePickerDrawer/Row, workspace-source-selection,
use-workspace-source-search, and MobileWorkspaceNameInput.
Reviewed by three adversarial agents + re-reviewed after fixes: GitHub search now
returns issues AND PRs (not issues-only), Linear defaults to assigned, create-branch
preserves slashy names, cross-repo PR base resolves against the item's own repo,
displayName is suppressed for user-edited names, and the smart-mode GitHub fan-out
respects availability. tsc/oxlint/max-lines-ratchet clean; 1328 mobile tests pass.
* fix(mobile): keep smart source drawer fully visible
* refactor: share workspace creation behavior across clients
* fix: address workspace creation review findings
---------
Co-authored-by: Brennan Benson <brennanbenson@Brennans-MacBook-Pro.local>
* Remove finished and killed claude subagents from the sidebar roster
Finished one-shot subagents stayed in the pane roster as permanent idle
rows (dozens per ultracode/workflow session), and a subagent killed
without its SubagentStop hook stayed 'working' forever, pinning the pane
working. SubagentStop now removes one-shot entries (teammates only idle:
they are alive and resumable), and a lead Stop's present background_tasks
list is treated as authoritative for non-teammates - unlisted entries are
removed. Teammates are identified by their name-embedding agent ids
(a<name>-<hex>, verified against live hook captures).
* Fix Claude subagent roster reconciliation
* Drop named workflow lanes that share the teammate id shape
Workflow/named one-shot agents report name-embedding lifecycle ids
(afinder-C-<hex>, agent_type = the label), indistinguishable by shape
from resumable teammates - so the roster retained them as idle rows
(observed live: a 32-row 12h-old pile). Unlike teammates they ARE
listed id-exact as subagent-typed background tasks, including inside
their own SubagentStop payload. Use that corroboration to remove them
on stop, reclassify task-listed teammate-shaped entries as one-shots,
and reap teammate-shaped leftovers when a complete inventory lists no
teammate-typed task at all (a teams session always lists its teammates,
even idle ones).
* Fall back to a merge when a divergent pull has no reconciliation strateg
- Git 2.27+ refuses `git pull` on divergent branches unless pull.rebase or
pull.ff is configured. Retry with `--no-rebase` (Git's historical default)
so pulls succeed out of the box on fresh hosts.
- Skip the fallback whenever the caller already specified a reconciliation
strategy (e.g. --ff-only, --rebase) so explicit policies still fail as
expected on divergence.
- Applied identically in the local git pull path and the relay/SSH git
handler so both surfaces behave the same way.
* Refactor divergent-pull merge fallback into shared helper
Extracts the retry-as-merge logic (duplicated between local git and
relay SSH pull paths) into `runPullWithDivergenceFallback` in
git-remote-error.ts, so both callers share one implementation and
test coverage.
* Prevent index churn from refreshing worktrees
* Cover IPC contract in worktree reliability gate
* Refresh background worktree heads without re-entering structural fanout
External commits, amends, and soft resets in non-active worktrees now reach
store rows through spawn-free Git metadata reads diffed in the watcher's
existing debounce, emitted only on real head moves. HEAD reflog appends become
status-only triggers, config.worktree becomes structural for sparse-flag
freshness, and the non-darwin poller gains a periodic ungated index re-stat
so in-place rewrites on coarse-mtime filesystems cannot be missed forever.
* Reject unsafe symref paths and validate object ids in the head reader
Ref content comes from repo files an attacker can craft. Backslash segments
traverse on Windows where join treats them as separators, and colons are
forbidden in Git ref names; both now fail isSafeRefName before any path is
built. Resolved values are additionally emitted only when they match a hex
SHA-1/SHA-256 object id, so no file content can leak through the identity
event even in principle.
---------
Co-authored-by: Brennan Benson <brennanbenson@Brennans-MacBook-Pro.local>
Co-authored-by: Brennan Benson <>
* fix(agent-status): surface Claude tool failures
* Fix compact sidebar hiding tool-failure errors behind stale tool name
Extract a shared clearActiveToolFieldsUpdate() helper and apply it to
Cursor's postToolUseFailure, Copilot's PostToolUseFailure/ErrorOccurred,
and Grok's post_tool_use_failure events, matching the existing Claude
behavior so the failure message surfaces instead of the last tool name.
* fix(agents): bundle agent icons instead of loading them from Google's favicon service (#8451)
Agents without a hand-authored SVG glyph loaded their icon live from
Google's favicon service (www.google.com/s2/favicons). That service is
unreachable in some regions (e.g. mainland China) and offline, so ~23
agent icons rendered as broken images on the agent settings page, the
terminal title bar, and the status bar.
Bundle each favicon as a build-time asset under resources/agent-icons/
and render it via a new agent id -> URL map (agent-favicon-assets.ts).
The remote favicon service now only serves as a last-resort fallback for
any future agent that lacks a bundled icon. Follows the same pattern as
#7373, which bundled the OpenCode mark.
* fix(agents): bundle mobile agent icons too; drop dead omp faviconDomain (#8451)
Mobile had the same offline/region bug: MobileAgentIcon rendered every
non-glyph agent from Google's favicon service. It actually affected more
agents than desktop, since mobile lacks hand-authored glyphs for
Copilot, OpenCode, Kilocode, Droid, and OpenClaude — all fell through to
the favicon path.
Bundle the 28 favicon-path icons under mobile/assets/agent-icons/ and
render them via a Metro static require() map (mobile-agent-icon-assets.ts).
A node-env invariant test asserts every favicon-path agent ships a
bundled PNG and is wired into the map.
Also remove omp's vestigial faviconDomain from the desktop catalog — omp
renders the hand-authored OmpIcon glyph, so the favicon fallback was
never reachable.
* refactor(agents): share one set of bundled agent icons between desktop and mobile
Desktop and mobile each shipped their own copy of the favicon PNGs (23 +
28, with 23 byte-identical duplicates). Consolidate them into a single
source of truth at src/shared/agent-icons/, reachable by both bundlers:
- Desktop (Vite) imports them via `?url`.
- Mobile (Metro) requires them; Metro already watches src/shared via
metro.config.js sharedRoot, so no config change is needed.
The two per-platform maps stay separate because the import syntax differs
(`?url` string vs `require()` asset ref), but they now point at the same
files. Verified with a real `expo export`: Metro bundles all 28 shared
icons from src/shared/agent-icons.
* fix(agent-status): label Cursor by identity, not a bare "cursor" token
The worktree card, status bar, and mobile all derive an agent label from the
terminal title via getAgentLabel / resolveTerminalTitleAgentType. Both matched
Cursor with `titleHasAgentName(title, 'cursor')`, a whole-token match. But
`cursor` is ordinary editor vocabulary, so a Claude/Codex tab working on Orca's
own code (title like `⠋ preserve cursor visibility across replays`) got
mislabeled as Cursor. The generic braille-spinner Claude fallback even had a
`!lower.includes('cursor')` guard that then dropped the title to no label at
all.
Gate Cursor on its closed identity title set (`isCursorAgentTitle`) instead —
the same predicate @cursor orchestration routing uses. A real cursor-agent
terminal still resolves as Cursor across working/idle/permission; a non-Cursor
tab that merely mentions a text cursor reverts to its true agent. Relax the
braille guard to the same predicate so those titles land on Claude, not null.
Makes display consistent with routing (the follow-up flagged in #8436).
* refactor(agent-status): address review on Cursor identity labeling
- Trim the four Cursor `// Why:` comments in both parallel resolvers
(agent-title-identity.ts, terminal-title-agent-type.ts) to AGENTS.md's
one-to-two-line rule; use identical wording so future drift is visible.
- Add a direct isClaudeAgent assertion in terminal-title-agent-type.test.ts
pinning that file's parallel copy (previously only covered transitively),
plus Cursor Agent / "Cursor - action required" activity-facet assertions.
Co-authored-by: Orca <help@stably.ai>
---------
Co-authored-by: Orca <help@stably.ai>
* feat(diff): add F7/Shift+F7 keyboard navigation for diff changes
Stacks on the Previous/Next change buttons (#6668) to add keyboard
navigation for single-file diffs, matching VS Code / JetBrains diff review.
- Register editor.nextChange (F7) / editor.previousChange (Shift+F7) in the
keybinding registry (Editors group) so they show in Settings and stay
rebindable.
- Teach the keybinding normalizer function keys (F1-F24) and make them
first-class in the bare-key safety model (safe standalone or with Shift,
opt-in per action) - F7 was previously unbindable.
- Install a capture-phase listener from DiffNavigationProvider so keyboard
and the existing header buttons share one goToDiff path; works on
read-only and editable single-file diffs.
- Translate the Previous/Next change strings for es/ja/ko/zh.
Refs #6215
* test(diff): cover change navigation shortcuts
* fix(diff): use shortcut chips in navigation tooltips
---------
Co-authored-by: Brennan Benson <brennanbenson@Brennans-MacBook-Pro.local>
* fix(pr-comments): let users mark comment authors as bots for the Humans/Bots filter
Some review bots post from regular user accounts that defeat both provider
bot metadata and login heuristics, so their comments were misclassified as
human. Adds a persisted prBotAuthorOverrides setting with a "Mark author as
bot" comment action, applied consistently across desktop and mobile.
Fixes#7597
Generated with [Devin](https://devin.ai)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix(pr-comments): address review feedback on bot-author overrides
- Cap sanitized prBotAuthorOverrides at 500 entries so malformed payloads
can't bloat GlobalSettings or slow comment classification
- Reuse the shared normalizePRCommentAuthorLogin in isBotPRComment on
desktop and mobile instead of duplicating the normalization inline
- Pass botAuthorOverrides from CommentRow to CommentMoreMenu instead of
re-subscribing per menu instance
- Re-fetch mobile bot-author overrides alongside each PR refetch so they
don't stay a stale one-shot snapshot for the whole session
Generated with [Devin](https://devin.ai)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix(pr-comments): harden bot author override sync
* fix(pr-comments): bound and recover override updates
* fix(pr-comments): merge overrides from canonical settings
* fix(pr-comments): make bot override updates atomic
* fix(pr-comments): surface rejected bot overrides
* fix(i18n): translate bot override warning
* fix(i18n): translate bot author actions
---------
Co-authored-by: Dzmitry Bachko <dbachko@users.noreply.github.com>
Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Brennan Benson <brennanbenson@Brennans-MacBook-Pro.local>
* Surface the Pi CLI's real error when branch auto-naming and commit generation fail
Pi failures (missing provider credentials, HTTP 4xx/5xx, connection errors)
previously collapsed to the generic 'Pi CLI command failed with code 1.'
because extractAgentErrorMessage only recognized Error:-prefixed lines.
Add two stderr-only extraction passes for pi's failure formats and narrow
the unix-path redactor so pi's /login remedy token survives redaction.
Fixes the OP variant of STA-1492 (#7808).
* Replace per-CLI failure parsing with sanitized raw output excerpts
Every agent CLI formats errors differently, and the parsing passes only
ever covered the vendors someone had already debugged (the pi passes
fixed pi and nothing else). Show the output itself instead: a positional
excerpt (first two non-empty lines plus the last one) of stderr, falling
back to stdout when stderr is silent, path-redacted and capped as
before. Exit-0 runs with empty stdout now stay 'returned an empty
<result>' instead of misreporting a command failure. The sanitizer also
strips Cf characters (bidi overrides) now that provider-controlled
bodies flow through verbatim, and OSC sequences are stripped alongside
CSI.
* Keep the full CLI output of failed branch-name generation for on-demand viewing
The persisted rename-failed badge carries only a sanitized excerpt (it
syncs to paired clients), so the complete diagnosis was previously
buried in the main-process log. Failed generations now capture their
full stdout/stderr (bounded to 64 KiB per stream, head and tail) into a
main-memory-only store keyed by worktree — never persisted or synced.
The rename-failed dialog fetches it on demand and shows it in place of
the excerpt, ANSI/control/bidi-stripped; after a restart or on paired
web clients it falls back to the excerpt. Renderer-bound generation
results explicitly drop the capture so IPC payload shapes are
unchanged.
* Cover the rename-failed dialog's full-output fetch and excerpt fallback
* Split the folder-workspace title rename flow into its own module
first-work-branch-rename.ts sat at the max-lines ceiling; the folder
title flow is a self-contained concept and main's concurrent additions
to this file pushed the CI merge ref over the limit.
* Harden the failure-output capture and dialog against review findings
- Flatten bounded captures so V8 sliced strings no longer retain the
multi-megabyte parent stream in the capture map (128 MiB -> ~4 MiB in
a 32-entry probe).
- Require an OSC terminator and stop its char class at newlines so an
unterminated/boundary-truncated sequence can no longer swallow the
omission marker and diagnostic tail.
- Exclude stdout from the persisted branch-name failure detail (it can
echo the prompt into synced metadata); the full local-only capture
still keeps it for the dialog.
- Refetch and reset the dialog's full output when the persisted error
changes so an open dialog never shows or copies a stale run.
- Report signal-terminated generators (null exit code) as 'was
terminated before exiting' instead of 'code null'.
* Redact JSON-escaped Windows drive paths without breaking scheme URLs
Provider JSON bodies double backslashes (C:\\Users\\name), which slipped past the drive-letter redaction into the persisted, client-synced failure excerpt. Allow repeated backslashes only — a URL's :// must stay single so remedy links survive.
* Fix branch rename failure IPC re-registration
---------
Co-authored-by: Brennan Benson <brennanbenson@Brennans-MacBook-Pro.local>
* fix(orchestration): complete worker tasks and improve coordinator UX
* Fix orchestration lifecycle sender resolution and peek/check compat hand
- Lifecycle sends (worker_done/heartbeat) now use ORCA_TERMINAL_HANDLE
verbatim, skipping the liveness probe and pane remint that could
block delivery during restarts or mismatch stale-runtime assignee
handles.
- --peek now round-trips as {peek:true, unread:false} so older runtimes
that strip unknown params degrade to non-destructive "all" instead of
mark-read, with client-side filtering to restore peek semantics and a
clear error when --peek --wait can't be honored.
- Reject combined read-mode flags (--unread/--peek/--all) before calling
the runtime.
- Distinguish suppressed (already-consumed) lifecycle messages from
ignored ones so send doesn't wake --wait waiters for stale heartbeats.
- Fix task summary truncation to avoid splitting UTF-16 surrogate pairs
and to not misreport whitespace normalization as truncation.
* Add shared helper to abbreviate orchestration task specs for brief listi
- Normalizes whitespace and caps spec length at 160 chars, flagging
truncation separately from whitespace-only changes
- Truncates on UTF-16 code point boundaries to avoid splitting
surrogate pairs and emitting malformed strings
* Add pane-key identity to worker_done/heartbeat reconciliation and server
- Records the sender's pane key on messages and dispatch contexts so
worker_done/heartbeat ownership can be verified by the remint-stable
pane leaf instead of the terminal handle, which is reissued across
restarts.
- Rejects lifecycle messages from a genuinely foreign pane while still
tolerating handle remints, tab break-outs, and older CLIs that lack
pane identity.
- Moves task-spec abbreviation server-side (orchestration.taskList
--brief) so full specs no longer cross SSH/relay transports, with a
client-side fallback for older runtimes; consolidates the shared
abbreviation helper under src/shared.
- Adds a stderr warning when a pre-peek runtime's --peek response hits
the 100-row cap, since older unread messages may be missing.
* Isolate ORCA_PANE_KEY in CLI test beforeEach to fix leaked senderPaneKey
Co-authored-by: Orca <help@stably.ai>
* Fix pane-key remint bypassing dispatch mutual-exclusion lock
- Dispatch locking only matched on assignee_handle, so a reminted
terminal handle (tab break-out) could open a second concurrent
dispatch on the same pane.
- Add leaf-UUID-based pane key comparison (parsePaneKey) as a
secondary lock, falling back to exact handle match for legacy
rows without pane keys.
* Update orchestration skill docs for lifecycle authority and CLI flag add
- Clarify that dispatch lifecycle is tied to taskId+dispatchId verified against
the dispatched pane, not the terminal handle, since handles can be reminted
after restart
- Document new `check --peek`/`--all` and `task-list --brief` flags, with
fallback guidance for older CLIs that reject them
- Note that a valid worker_done auto-completes the task/dispatch, so workers
shouldn't also call task-update manually
---------
Co-authored-by: Orca <help@stably.ai>
Persist and safely restore the active top-level view on startup. Unknown, removed, legacy, or unavailable views fall back to the terminal, while cross-window UI sync cannot navigate the current window.\n\nCloses #8264
* fix(settings): make WSL skill commands pasteable (#7795)
* Fix WSL skill commands so PowerShell 7 pastes match PowerShell 5.1 argv
- Encode the WSL login-shell script as base64 and decode/eval it inside
the sh -c invocation, avoiding raw nested quotes at the paste boundary
- Scope $PSNativeCommandArgumentPassing = 'Legacy' to the invocation so
PS 5.1 and PS 7 both hand wsl.exe the same escaped argv
- Extract powershell-native-argument.ts as the shared quoting module and
reuse it from ssh-remote-powershell.ts
* test(runtime): stub getRepo in mobile-tab startup cwd test
Main's #7892 made listMobileSessionTabs validate selectors via
this.store?.getRepo; the mock store here only defined
getWorkspaceSession, so the merged CI build threw 'getRepo is not a
function'. Return null (wt-1 is a worktree id, not a repo).
Co-authored-by: Orca <help@stably.ai>
---------
Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
Co-authored-by: Orca <help@stably.ai>
* Fix Codex WSL project trust conflating case-distinct Linux paths
normalizeCodexProjectPathForLookup lowercased the entire Windows/UNC
path, including the case-sensitive Linux portion under \\wsl$\<distro>.
Two distinct WSL project dirs (.../Repo vs .../repo) collapsed onto one
trust key, and the mirror dedupe (codex-config-mirror) inherited it.
Preserve case for the Linux path after the case-insensitive
\\wsl$\<distro> / \\wsl.localhost\<distro> share prefix; true Windows
drive letters and normal UNC shares still case-fold as before.
Co-authored-by: Orca <help@stably.ai>
* Apply the same WSL case-fold fix to hook-trust key lookup
normalizeHookTrustKeyForLookup had the identical latent bug: on a win32
host it lowercased the whole Windows-shaped path, folding the
case-sensitive Linux tail of a \\wsl$\<distro> UNC hook path — contrary
to its own comment that WSL sources stay case-sensitive.
Extract foldWindowsCaseInsensitivePath (shared with the project-path
normalizer): fold only the case-insensitive drive/\\wsl$ share prefix,
preserve the Linux tail. Existing suffix (event:group:handler) is already
lowercase, so behavior is unchanged there.
Co-authored-by: Orca <help@stably.ai>
* Fix Codex WSL trust revocation and hook-key case folding
- Case-drifted or share-spelling-varied WSL revocations were being
ignored on merge, letting stale "trusted" entries survive; fold
revocation lookups fully so matching errs toward revoked.
- Hook-trust key folding was gated on process.platform === 'win32',
so WSL/SSH-remote hook sources weren't folded when Orca itself ran
on macOS/Linux; fold by path shape instead, via the new shared
foldWslUncPathCaseInsensitiveParts helper (also covers /mnt drvfs
automounts and wsl$/wsl.localhost share aliasing).
* Fix Codex WSL trust key case-folding regressions
- Don't fold case-variant `/MNT` dirs as if they were the drvfs
automount; only literal lowercase `/mnt/<drive>` folds.
- Preserve an exact-cased trusted project entry in ~/.codex during
runtime merge instead of letting a loosely-matched, case-drifted
revocation clobber a user's re-granted trust on every mirror pass.
- Minor cleanup: inline foldWindowsCaseInsensitivePath and hoist the
repeated normalizeHookTrustKeyForLookup call in findTrustBlockRanges.
* Refactor case-fold WSL trust test to assert real serializer output
Extract path variables and assert against escapeTomlString(incomingPath) instead of a hardcoded escaped string literal, so the fixture can't silently drift from the actual TOML header serialization.
---------
Co-authored-by: Orca <help@stably.ai>
* fix: work-item naming, usage % rounding, and terminal/delete copy
Address prod-release-scan P2s:
- #8238: recognize Bitbucket Server (/projects|users/.../repos/.../pull-requests/N)
and Azure DevOps (/_git/REPO/pullrequest/N) PR URL shapes in
work-item-reference, alongside Bitbucket Cloud; graceful fallback preserved.
- #7574: getDisplayedUsagePercentage now rounds the used value before taking the
`remaining` complement, so the compact status bar (raw usedPercent) and tooltip
(pre-rounded clampUsedPercent) can no longer disagree by 1% at a .5 fraction.
clampUsedPercent moves to the shared module as the single rounding source.
- #7459: mixed remote+local batch delete confirm no longer claims the whole
batch is a permanent "remote host" delete — it now states remote items are
permanent while local items move to the Trash/Recycle Bin.
- #8322: right-click-to-paste settings copy is platform-aware — "Control-click"
on macOS, "Ctrl+right-click" on Windows/Linux — matching the ctrlKey gate.
Localization catalog synced (also picks up pre-existing UsagePercentageDisplayChangeNotice drift).
* Fix NaN% usage bar and label for non-finite provider values
Non-finite usedPercent inputs (NaN/Infinity) propagated through Math.round/min/max into the CSS bar width (`NaN%`) and displayed copy. clampUsedPercent now short-circuits to 0 in that case, with a test covering the divergence from getDisplayedUsagePercentage for the 'remaining' case.
Pi awaits its extension event handlers, so an awaited loopback status
post that stalls (Orca restarting / receiver unavailable) blocked the
running Pi turn and disconnected it. Make post() fire-and-forget with a
latest-only pending slot drained by a single active request and a 1s
AbortController timeout, so a stalled receiver can never hold the turn.
Also stop treating session_shutdown as turn completion: Pi emits it on
reload/new/resume/fork while the PTY stays alive, so only agent_end
proves done (real exit is cleared by PTY teardown). Split the generated
handler registrations into agent-status-handler-source.ts.
Co-authored-by: Orca <help@stably.ai>
* docs: design Grok orchestration group
* docs: plan Grok orchestration group implementation
* fix: add Grok orchestration group
* test(orchestration): accept Windows skill newlines
* Fix @grok orchestration group matching and remove stale planning docs
- Reuse the shared buildAgentNameRe matcher in groups.ts instead of a
divergent local regex, so orchestration groups honor the same
Windows launcher-suffix rule (grok.exe/.cmd/.bat/.ps1) as the rest
of Orca's agent-title detection.
- Add test coverage for real Grok OSC title shapes (spinner-collapsed,
session titles) and Windows launcher-suffix titles.
- Delete the now-completed design and implementation-plan docs for
the Grok orchestration group work.
---------
Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
Include fallbackGitHubPR alongside linkedGitHubPR/linkedGitLabMR when
determining whether a hosted review link resolves to a push target.
Worktrees without persisted linkedPR metadata (e.g. child worktrees)
were incorrectly blocked with "target unavailable" despite having a
real matching upstream, since their PR was only known via the queue
fallback. Also splits hasPositiveHostedReviewNumberLink to build on
the resolvable subset so the two helpers can't drift.
* Fix F3: use known-tag predicate for explicit-turn decisions
isHarnessInjectedUserTurnText matches any multi-word kebab tag, which is
safe for non-destructive UI classification but wrong for state-transition
decisions: a real prompt starting with a custom <my-element> paste looked
like machinery in resolvePrompt/hasExplicitUserPrompt. Combined with the
post-interrupt working-suppression in agent-hooks/server.ts, that left the
agent visibly done after Ctrl+C. Switch both explicit-turn callsites to the
known-tag isKnownHarnessInjectedUserTurnText predicate; known harness tags
and Grok <user_query> behave unchanged.
Co-authored-by: Orca <help@stably.ai>
* Retire broad harness-tag matcher; unify on known-tag predicate
The broad isHarnessInjectedUserTurnText had one remaining consumer: session
title selection in session-scanner-primary-parsers.ts. Switch it to the
known-tag predicate too — a real first turn that pastes a custom <my-element>
now titles the session instead of being demoted to the meta (fallback) title.
All observed first-turn machinery (system-reminder, caveat, command-name,
task-notification, …) is already in the known list, so the only behavior
change is that unknown, uncatalogued kebab tags stay user turns. Delete the
now-unused broad predicate and fold its coverage into the known-predicate
tests.
Co-authored-by: Orca <help@stably.ai>
* Fix bare <channel> tag being misclassified as harness machinery
Only the attributed `<channel source=…>` form is emitted by the harness;
a bare `<channel>` is legitimate user-pasted RSS/XML content. Remove
'channel' from the known-tag set (which matched any <channel ...>) and
rely solely on the existing '<channel source=' prefix rule.
---------
Co-authored-by: Orca <help@stably.ai>
- gh file-fetch failures (rate limit, auth, unresolved remote) previously
returned an empty array, which the Files tab rendered as "No files
changed." — indistinguishable from a real empty PR
- getPRFiles now returns null on failure; work-item-details surfaces this
as filesUnavailable so GitHubItemDialog and PullRequestPage can show a
retry action instead of a misleading empty state
* feat(status-bar): notify upgraded users usage meters show % used
Show a one-time status-bar callout when upgraded profiles still use the
new percent-used default. Brand-new profiles and users who already chose
remaining stay quiet; dismissing or changing the setting is permanent.
* Add settings deep-link to expand Appearance's Window accordion for one-s
- Replaces the searchQuery-based redirect (fragile, flashed filter UI) with a
dedicated appearanceAccordionDeepLink store field that force-opens the
correct accordion and scrolls to the target row
- Rebuilds the status-bar change notice as a plain elevated card instead of
a Popover, since PopoverContent's glass/backdrop-filter defaults fought
the opaque callout styling and needed heavy overrides
- Simplifies the light/dark card CSS tokens accordingly
* Reposition status-bar usage-change notice via fixed-position portal
Portal the one-shot callout to document.body with fixed positioning
anchored via getBoundingClientRect, since the status-bar's overflow-hidden
flex ancestors clipped or mispositioned the previous in-tree absolute
layout.
* Refine status-bar usage notice styling and test coverage
- Replace hand-tuned light/dark card colors with existing design
tokens (--popover, --border) and the documented floating elevation,
so the callout stays in sync with the design system instead of
duplicating its own palette
- Add tests covering dismiss via X button, "Got it", and Escape
- Scope the foreground-process confirm assertion to the pane's ptyId
so an unrelated pane's delayed confirm can't cause a false failure
* fix(naming): lead workspace and tab names with the work-item identifier
Auto-generated workspace names, branch-rename display names, and tab
titles now lead with the referenced PR/MR/issue/ticket — e.g.
`PR 1033 - Review` instead of a paraphrase like "Review community pr
1094" that buried or dropped the number. Identifiers are the highest-
signal, most searchable token, so leading with them makes the sidebar
and tabs scannable and disambiguates same-verb work items.
- New shared `work-item-reference.ts` extracts the identifier from the
raw prompt: URLs are validated by path structure (owner/repo/pull/N,
GitLab's `/-/` marker) so GitHub Enterprise / self-hosted GitLab still
resolve while stray `/pull/<n>` paths (CDN, docs) do not; a ticket-
prefix denylist keeps `SHA-256` / `UTF-8` / `ISO-8601` from being read
as Jira/Linear keys.
- Reconciles the existing create-from-work-item naming (was action-first
`Review PR 1033`) with the first-work auto-rename onto one identifier-
first format via a shared `formatIdentifierFirst`, so the two paths
can't drift.
- Fixes a pre-existing tab-title bug where markdown punctuation was
stripped before URLs, splitting a GitLab `merge_requests` URL at its
underscore and leaking "requests" into the title.
* fix(naming): keep emphasis-wrapped URLs intact in generated names
The URL/markdown strip reorder left the tab-title URL strip anchored on
\b, which fails when a URL is wrapped in markdown emphasis (_...pull/5_)
and leaked URL fragments into the title. Drop the \b anchor and trim
trailing markdown emphasis (*_~) in the URL identifier parser, keeping
interior underscores (merge_requests) intact. Adds regression tests.
Co-authored-by: Orca <help@stably.ai>
---------
Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
Co-authored-by: Orca <help@stably.ai>
Add a persisted Appearance setting that switches provider usage labels between percent used and percent remaining while keeping meter fill consumption-based.
Cover desktop and web persistence, current providers, settings search, localized copy, and regression tests.
Co-authored-by: gatsby74 <166927047+gatsby74@users.noreply.github.com>