Preserve OMP session identity and exact resume paths across cold restoration, AI Vault, mobile, WSL/SSH, and host-authority routes. Add mixed-version fallback and completed-session recovery coverage.
* fix(worktree): stop terminal removal fence error flashing on delete
Deleting a worktree kills its PTYs for the filesystem teardown, then runs
git worktree remove (~1s). During that window a doomed pane races a fresh
respawn that main correctly fences with TerminalRemovalInProgressError, but
the renderer surfaced that internal fence verbatim as a pane error banner
until the worktree unmounted.
- startFreshSpawn: skip the respawn when the pane's own worktree is being
deleted (isDeleting) — no shell should spawn into a directory being removed
and the pane is about to unmount.
- reportError: swallow the removal fence at the single pane-error sink so it
never reaches the banner, covering the parent-removal-fences-child case the
own-worktree skip cannot see.
- Share the fence messages between main (thrown) and renderer (recognized) via
worktree-removal-fence-error.ts so the thrown text and predicate can't drift.
* test(worktree): assert onError callback captured before invoking
Optional invocation let the fence-suppression test false-pass if the
transport onError wiring broke; require the callback so a broken wire
fails loudly. Addresses CodeRabbit review on #10240.
* fix(app): bound the wake/quit paths implicated in the phone-session-ended freeze (#9447)
- relay-transport: waitForClose now times out (5s) so a half-open post-sleep
socket can't wedge runtimeRpc.stop()
- will-quit: race teardown against a 20s deadline so app.quit() always runs
(Force Quit was the only escape when any teardown member never settled)
- terminal-fit-restore: local restoreTerminalFit invoke gets the same 15s
bound as the remote path so the held-fit modal buttons can't pin disabled
* fix(app): close wake recovery timeout gaps
* fix(relay): drop late frames after forced teardown
* fix(relay): fence detached socket callbacks
* fix(app): close timeout resource gaps
* fix(relay): detach retired mobile transports
* fix(types): exclude absent stat overloads
* fix(runtime): expire wedged terminal restore dedupe
* fix(runtime): keep restore retries on one reclaim
* chore(skills): refresh bundled skill manifests
* fix(window): fence quit acknowledgements by request
* fix(relay): bound revoked device socket cleanup
* feat(agent-rows): opt-in conversation-name labels for agent rows
Sidebar worktree-card and dashboard agent rows always show the last
message sent to the agent, so rows relabel on every turn and a 'continue'
prompt becomes the row's name. Add an opt-in Agents setting that labels
rows with the conversation name instead, resolved with the tab bar's
precedence: manual rename, quick-command label, OpenCode session title,
generated title (behind its existing setting), then the agent-set live
title. Live titles count only when they carry a real name - status
decoration is stripped, and pure status, identity-echo, spinner+cwd, and
placeholder titles fall back to the last-message label. Subagent child
rows keep their own descriptions.
Locale note: sync:localization-catalog also restored parity for keys
already missing on main (add-host, sleep-worktree copy).
* fix(agent-rows): read the live tab so late renames and titles surface
Row data patches live entries in place and keeps the tab snapshot from
row creation, so a rename or agent-set title landing after the row was
built never reached the conversation-name resolver. Select the current
tab from the store in the hook; retained rows without a live tab keep
the snapshot fallback.
* fix(agent-rows): reject status labels and bound lookup work
Reject native provider/status titles and Windows/UNC cwd frames so conversation-name mode falls back to the user's last message instead of relabeling rows with identity or path text. Keep default-off and subagent rows off the tab map, and share a WeakMap-backed tab index across mounted rows to avoid repeated linear scans on store writes.
* feat(agent-rows): make conversation names the default
Always prefer a usable conversation name for sidebar and dashboard agent rows, falling back to the last message when the resolver rejects a title. Remove the preference, settings UI, search copy, and catalog entries. Keep subagent labels unchanged and reject generic Terminal N placeholders so partial tab snapshots cannot hide meaningful prompts.
* chore(i18n): sync catalog after main merge
* fix(agent-rows): preserve same-tab child labels
* feat(diagnostics): name what grew in renderer OOM crash reports
Renderer OOMs are the dominant crash class (heap pinned at the ~3.5GB V8
ceiling in crash-channel reports) but renderer_memory breadcrumbs carry only
heap totals, so reports say "it grew" without saying what.
Add a one-shot renderer_memory_highwater breadcrumb at 60%/80% of the heap
limit carrying leak-diagnosis counts: DOM node census, terminal element
count, and per-subsystem counts from a new contributor registry. The store
registers the first contributor, reporting its 20 largest top-level
collections. Counts only, capped per contributor; zero work on the 60s
sample path while below threshold.
* fix(diagnostics): retain renderer heap profiles
* test(agent-status): remove subagent row order race
* fix(diagnostics): bound aggregate heap profile work
* fix(diagnostics): bound heap profile contributor calls
* fix(types): avoid overloaded stat return inference
* fix(diagnostics): fully bound heap profile registry
* chore(skills): refresh release snapshot manifest
* fix(diagnostics): retain browser counts in heap profiles
* fix(quick-open): guide rg install after fallback budget errors
* fix(quick-open): show local host wording for local install guidance
The install-rg guidance component hardcoded 'on the remote', so the new
local fallback path told local users to install ripgrep 'on the remote'
— wrong for the exact case #9627 targets. Parse the location out of the
message and render the matching wording; add the local locale string and
a render test that guards against the 'on the remote' regression. Also
harden the reason capture against a stray ')' in the error text.
Co-authored-by: Orca <help@stably.ai>
---------
Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
Co-authored-by: Orca <help@stably.ai>
Credits remote terminal output only after parse or intentional discard, with bounded adaptive windows, fair draining, recovery cleanup, and RTT/parser benchmarks.
Adds a keyboard command that opens the "Send notes to an agent" picker for
the active worktree's AI diff-review notes, enabling a fully keyboard-driven
review flow. Unbound by default; users assign it in Settings → Keyboard
Shortcuts.
- New `sourceControl.sendReviewNotes` command (scope global, unbound). Set
`conflictGroup: 'editor'` so Settings warns on collisions with editor chords
(e.g. Add Review Note), not just global ones.
- Dispatched from App.tsx's existing global capture handler so it respects the
terminal-shortcut policy, the shortcut-recorder guard, and defaultPrevented.
- Store thunk `openDiffNotesSendMenuForActiveWorktree` reveals Source Control
and requests the notes send menu open; no-op when there are no unsent notes.
- Menu opens via a nonce-based store request consumed on mount, TTL-bounded so
a request the menu never consumed can't reopen it on a later remount.
Co-authored-by: Orca <help@stably.ai>
Allow the existing "Open in" entries to launch a configured VS Code
launcher against an SSH-backed worktree via Remote-SSH:
code --remote ssh-remote+<authority> <remote-path>
- Split the blanket SSH/runtime block into a capability model: file
managers and non-VS Code launchers stay local-only (disabled with
"Local only" metadata); a recognized VS Code command is enabled and
forwarded with connectionId over a typed object IPC.
- Main process stays authoritative: rejects active/owned runtimes,
resolves the SshTarget from the persisted Store, derives the authority
(config alias, or username@host on port 22, or ssh-alias-required on a
non-default port), validates POSIX/Windows absolute remote paths without
local stat/normalize, and rejects non-VS Code and compound commands
before spawn.
- Authority and remote path are passed as separate argv; getSpawnArgsForWindows
remains the cmd/bat shim boundary and fails closed on metacharacters.
- Same capability rules across the worktree menu, Explorer overflow, and
the source-control entry context menu.
Refs STA-2386
Closes#9999
Production crash diagnostics measured ~128 `git worktree list` execs/min
(9,400 in one 80-minute session, ~16% of wall-clock in git subprocesses):
the resolved-worktree scan fans out over every registered repo on a 30s
cache TTL, and most registered repos on the affected installs were
agent-CLI scratch repos (~/.codex-tmp capsules, vendor imports, skill
checkouts) that need no freshness.
Classify agent-scratch repo roots with a curated shared matcher and stamp
their scan-cache entries with a 5-minute TTL instead of 30s. Orca-driven
mutations still bypass the TTL via the per-repo generation bump, so only
passive pickup of external changes slows for scratch repos. Expected
steady-state reduction on the measured install: ~82% fewer git spawns.
* fix(mobile): persist per-device tab selection so worktree return restores the last open tab
A phone's tab selection lived only in the host's in-memory
ClientSessionTabSelectionStore. Any host restart wiped it, and the
per-device projection then fell back to deterministic topology, so
returning to a worktree on mobile always landed on the first tab
instead of the tab last opened on the phone.
Persist the per-device selections in the Store (keyed deviceId ->
worktreeId), hydrate them when the runtime constructs, and guard
projection so an early empty snapshot after restart cannot wipe a
hydrated selection before tabs arrive. Selections are pruned with the
worktree/repo and on device revoke, and malformed persisted payloads
degrade to empty instead of throwing.
* fix(mobile): harden persisted tab selection cleanup
* fix(mobile): preserve tab selection across worktree rename
* fix(agent-status): map codex request_user_input questions to waiting
Codex 0.145 asks user questions via the auto-allowed request_user_input
tool (experimental default_mode_request_user_input): PreToolUse fires
while blocked on the answer with no Stop, so Orca showed the pane as
working/idle instead of Needs You. Map that PreToolUse to waiting
(mirrors grok's ask_user_question), exempt question waits from the codex
yolo auto-approval suppressor, and deliver native-chat answers to the
digit-commit selector by option number (typed labels are ignored and
Enter commits the highlighted first option). Older codex versions emit
no such event and are unchanged.
* fix(native-chat): preserve codex question answer semantics
* fix(agent-status): keep Claude in-process teammates visible as idle sidebar rows
Claude Code 2.1.21x runs named Agent-tool agents as turn-based in-process
teammates: SubagentStop and TeammateIdle fire at every TURN end while the
teammate stays alive awaiting mail (verified live on 2.1.217). Treating
those events as finish signals deleted the child row seconds after each
burst, so the sidebar showed no subagents for most of a teammate's life.
Root-cause fix: the roster now tracks a working/idle state per child.
- One-shot children (hyphen-free ids) keep remove-on-stop: their
SubagentStop is a true finish.
- Teammate-shaped rows park as idle on SubagentStop/TeammateIdle and
revive to working via the next SubagentStart (same lifecycle id,
first-observed startedAt preserved).
- Idle rows never gate the pane 'working' (#8825's done-gate rule).
- Only TeammateIdle-confirmed idle rows survive a complete lead-Stop
fold; a stopped workflow lane wearing a teammate-shaped id is reaped
there (or immediately, once a fold tagged it listedAsSubagentTask), so
the pre-#8825 idle pile cannot rebuild.
- At the wire cap, the oldest idle row is evicted to admit a working
spawn; working children are never displaced.
- Hydrate keeps pruning idle snapshots: idle-teammate liveness cannot be
proven across a restart, and a live teammate re-earns its row.
* fix(agent-status): restore inventory-confirmed workflow lanes
* perf(rate-limits): throttle statusline usage posts to one curl per pane per 15s
The managed Claude statusline script posted on every statusLine tick
carrying rate_limits — ~3 curl spawns/sec per streaming pane, multiplied
across concurrent panes. The service drops same-value posts inside its
30s dedupe window anyway, so most spawns bought nothing.
Gate the post on a per-pane stamp file: POSIX compares date +%s against
the stamp; Windows uses an all-builtin seconds-of-day parse of %TIME%
(octal-safe, no extra process). Both fail open — unparseable time,
garbage stamp, or midnight wrap posts rather than darkening the live
usage feed — and the stamp only advances when a post actually fires, so
skipped ticks never defer the next one.
Measured: 30 rapid rate-limit ticks spawn 1 curl (was 30).
* perf(rate-limits): keep throttled statusline ticks process-free
Use Claude's monotonic session duration for the POSIX throttle so skipped ticks do not replace curl churn with date churn, while retaining a fail-open date fallback. Key temp files by the stable leaf UUID so path-like or long host tab IDs cannot disable the throttle.
* fix(rate-limits): preserve throttle isolation across upgrades
* fix(rate-limits): reject leading-zero stamp values before arithmetic
All-digits validation still admitted values like 008, which are invalid
octal inside $(( )) — and dash treats that expansion error as fatal, so
the script died before rewriting the stamp and the pane's live usage
feed stayed dark until the file was deleted. Allow-list canonical
decimals (same pattern as the duration parse) on both the stamp and the
computed clock so malformed values fail open to posting.
Verified under dash: the old digits-only check aborts at the arithmetic
(Illegal number: 008); the allow-list survives and posts.
* perf(rate-limits): eliminate POSIX statusline cat churn
* test(rate-limits): exercise overlapping statusline ticks
The stamp check/write is deliberately lock-free (a lock could wedge the
feed closed; fail-open is the contract), so a truly concurrent burst may
post more than once, bounded by overlap width — an exact at-most-one
assertion would be flaky by design. Assert the invariants that do hold:
every overlapping run exits 0, the raced stamp lands valid, and it
throttles the following ticks.
* fix(naming): remove identifier-first name post-processing
Workspace display names and tab titles were being rewritten after
generation by prompt-scanning heuristics from #8238 — a stray "#1" in
prose became a workspace named "#1 - Fix", and the rewrite ran
downstream of generation so user naming instructions couldn't override
it. Per the same principle as #9088, naming defaults stay minimal and
user overrides own the style.
- Delete work-item-reference.ts and display-name-from-work.ts (+tests).
- Auto-rename display names return to the humanized branch slug; tab
titles return to the cleaned first prompt clause.
- Explicit create-from-work-item naming returns to action-first
("Review PR 1234").
- Keep #8238's URL-before-markdown strip-order bugfix in tab titles,
with regression tests adjusted to the natural expectations.
* test(naming): pin incidental marker regression
* feat: continue agent work in a new session
* fix: harden new-session continuation
* fix: source last prompt from provider-authenticated transcript records
Preview user entries can be tool results or harness-injected skill text,
so the continuation prompt's last-prompt hint now comes from the vault
scanner's provider-authenticated lastUserPrompt. Also softens the
continuation instructions for already-complete tasks and adds a cost
warning to the full-transcript option.
* fix: move Continue in New Session row action into the hover group
Edge-usage action; keep the resting row at three icons and reveal it with
the other session actions on hover, matching Resume's gating.
---------
Co-authored-by: jz.feng <jz.feng@aftership.com>
Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
* fix(dashboard-popout): zoom the pop-out window, not the main window behind it
The View menu's zoom handlers always sent terminal:zoom to the main
window, so zooming while the dashboard pop-out was focused zoomed the
window behind it. The pop-out also never applied the persisted
uiZoomLevel, so it always rendered at 100% in a zoomed app.
The pop-out now applies uiZoomLevel on dom-ready and follows app-zoom
changes while open (main-window zoom, settings control, mobile ui.set).
Menu zoom routes to the pop-out when it is the focused window, stepping
its own webContents zoom, and a narrow before-input-event handler
resolves the zoom.in/out/reset chords (honoring keybinding overrides)
since the pop-out has no renderer-side shortcut plumbing. The step/clamp
constants move to shared/ui-zoom-level.ts so main and renderer share one
definition.
* fix(dashboard-popout): isolate window zoom
* fix(dashboard-popout): deny unused permissions
* docs(dashboard-popout): clarify wheel zoom path
* feat(linear): add MCP-style save issue
* fix(linear): harden save issue parity
* fix(linear): close save issue contract gaps
* docs(linear): bundle project discovery with save issue
* fix(rate-limits): unstick Claude "Limited" usage and feed live usage from session statuslines
The OAuth usage endpoint's 429 Retry-After (~50 min) was ignored, so the
30s-15min automated retry lanes kept landing inside the throttle window and
the status bar stayed on a bare "Limited" indefinitely while Claude itself
worked fine.
- Respect Retry-After on 429: carry it through usageMetadata.retryAtMs and
gate automated refetches (activation lane, poll cycles) until it expires;
user-directed refreshes still bypass.
- Keep the last-known usage snapshot visible through rate-limited windows
(24h) instead of dropping it after the generic 30-minute stale threshold.
- Add a managed Claude statusLine command that forwards each session's
rate_limits (Claude Code >=2.1.80) to a new /statusline/claude loopback
route, feeding live usage windows with zero usage-endpoint calls; OAuth
polling pauses while the live feed is fresh. User-owned statusLine
settings are never overwritten.
Generated with [Devin](https://devin.ai)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix(rate-limits): keep last-known window when a statusline post carries only one
Statusline payloads may report five_hour and seven_day independently; a
partial post must not wipe the other bar to null. Also document the
seconds-vs-ms epoch heuristic.
Addresses CodeRabbit review on #9617.
Generated with [Devin](https://devin.ai)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix(rate-limits): unstick Claude usage with live statusline feed
The OAuth polling endpoint is rate-limited; Claude's status often shows
"Limited" until the next poll cycle, even when quota remains. Live posts
from the statusline command update usage within 100ms, eliminating false
"Limited" displays during active sessions.
Manages install lifecycle via marker to respect user deletions. Handles
Windows payload buffering and guards before curl spawn. Protects against
live-post/OAuth-fetch races and cross-attribution during account switches.
Gracefully tolerates schema drift in statusline parsing.
* test(rate-limits): assert stale outgoing post doesn't affect incoming
Capture usedPercent before ingesting and assert it remains unchanged,
rather than checking for a specific value. This is more precise and less
brittle when testing session switch isolation.
---------
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: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
* feat(dashboard): open-worktree dialog control + finished-time parity
Pop-out agent dashboard polish:
- Terminal dialog: replace the footer Close button with the standard top-right
X, rename the primary action from Focus worktree to Open worktree, and prefix
it with an open-in-window icon.
- Card time column: finished agents now read time-since-finish (matching the
left worktree sidebar) instead of time-since-start. Extracts the sidebar's
finished-timestamp logic into a shared helper so the two surfaces cannot
drift, and threads finishedAt through the dashboard snapshot contract.
* fix(dashboard): thread finishedAt through payload validator + test fixture
CI typecheck caught a DashboardCard fixture in dashboard-payload-validation.test.ts
missing the new finishedAt field; also validate finishedAt in the relay guard.
* feat(dashboard): swap pending-question emoji for MessageCircleQuestion icon
The amber pending-question chip used a raw hand emoji (renders differently
per-OS and off the design system); use the lucide chat-with-question icon.
The code editor was hard-wired to the terminal font, so Nerd Font 'Mono' CJK glyphs overlapped in the editor. Adds an opt-in editorFontFamily setting; default is empty so the resolved value is byte-identical to today for every existing user.
Closes#9628
* Access the Floating Workspace from mobile (#8405)
Surface the desktop Floating Workspace (the global, repo-less scratchpad
of terminal tabs under the synthetic `global-floating-terminal` id) on the
mobile app so a Claude session left running there is reachable from a phone.
Adds a terminal-icon button to the mobile host header (phone + tablet
sidebar) that opens the existing Session screen for the floating id. The
sentinel already had host-side RPC support (#5946: local runtime, homedir
cwd, explicit-id fast paths in session.tabs.*); this wires up the mobile
surface and gates it on a new `floatingWorkspaceEnabled` status flag so the
entry hides on hosts that predate it or where the feature is disabled.
The Session screen learns an `isFloatingWorkspaceRoute` flag (mirroring the
existing `folder:` route pattern) that hides repo-backed surfaces — Files,
Source Control, PR/checks, agent history — skips the diff-comment and GitHub
probes, routes terminal URL taps to the phone browser, and limits the New
Tab drawer to terminals + agents (browser/markdown creation resolves a real
worktree host-side and stays desktop-only). useLiveWorktreeName
short-circuits for the sentinel so it no longer polls worktree.show forever.
Extracted the host status.get gating into a useHostStatusGates hook to keep
the host screen under the max-lines ratchet.
* Harden mobile Floating Workspace routing
* Fix mobile host gate reuse race
* Harden floating mobile session polling
* fix(mobile): harden floating workspace route reuse
* fix(mobile): skip floating workspace repo lookup
* fix(mobile): clarify floating workspace header action
* fix(github): load PR diffs for Enterprise remotes
* fix(github): encode PR content paths by segment
* Fix PR review actions failing on GitHub Enterprise remotes
- Threads GitHub host identity (not just owner/repo) through the client,
work-item-details, issues, and RPC layers so gh commands target the
correct Enterprise server instead of silently falling back to github.com
- Adds a shared github-api-repository helper to resolve/host-qualify repo
identity consistently across REST, GraphQL, and CLI shorthand calls
- Scopes the gh rate-limit breaker and singleton rate-limit snapshot by
host/runtime so a github.com block or probe can't affect GHES or WSL
- Coalesces concurrent host-auth probes and paginates PR file fetching
beyond 100 results
- Propagates `host` through renderer PR caches, checks-panel keys, and
preload IPC types so Enterprise and github.com data never collide
* Route gh host qualification through runner options instead of argv sniff
Move GHES/GH_HOST resolution from parsing --hostname/--repo out of gh argv to an explicit options.host passed through ghExecFileAsync, since SSH-backed repos spawn gh with no cwd and argv sniffing couldn't reliably detect the target host. The runner now injects --hostname and qualifies --repo/-R at spawn time from options.host, and rate-limit scoping/guards use the same explicit host instead of inferring it. Also adds a shared githubRepoIdentityKey helper to keep cache/store keys consistent with the new host-aware repository identity.
* Fix gh CLI GHES host pinning and rate-limit scope leaks
- Pin `--host` on every gh call site so a process-level GH_HOST can't
silently redirect requests, and qualify `-R`/`-R=` repo shorthand
alongside the existing `--repo=` handling.
- Check the target scope for an active rate-limit block before each
WSL/native or host fallback retry, not just on the initial attempt,
so a blocked scope can't be hit again through a fallback path.
- Compute idempotency once per call instead of re-deriving it after
fallback reassigns args.
* Fix GitHub Enterprise host identity loss across PR/work-item paths
- Thread `host` through mobile PR RPC params, IPC work-item lookups, and
RPC schemas so GHES identity survives the renderer/mobile/main boundary
instead of silently falling back to a same-named github.com repo.
- Qualify `--repo`/`-R` args for github.com too (not just GHES), since
gh resolves bare shorthand against a process-level GH_HOST that can
redirect pinned github.com commands.
- Cache `getOriginGitHubApiRepository` to avoid a per-call uncached
`git remote get-url` round trip on connection-backed repos.
- Add a local-fork fallback in `getWorkItemDetails` so PRs living on a
base repo (not visible via the origin slug) still resolve via cwd.
- Centralize the github.com-vs-GHES host predicate in
`isDefaultGitHubHost` so cache keys, quota scoping, and identity
checks can't drift out of sync.
* Make repository identity host-aware across all GitHub surfaces
Generalize the auth-gated enterprise resolver to any remote and build a
cached hosted-identity family (origin/issue/candidates/source) on top of
it, then migrate every github.com-only consumer: Tasks listing/counting,
branch-to-PR discovery, push targets, fork upstream, issue operations,
Projects, web links, avatars, and PR-link facts. Scope the rate-limit
breaker probe per runtime:host and classify WSL UNC cwds correctly.
Co-authored-by: Orca <help@stably.ai>
* Fix expected slug to include host field in GitHub PR link test
Updates the smart-source paste-intent test fixture to match the
repository slug shape that now carries a `host` field, keeping GHES
host identity intact through the paste-intent parsing path.
* Surface per-host gh auth state for GitHub Enterprise
diagnoseGhAuth accepts the host a surface needs credentials for, scopes
the account/scope diagnosis to that host, and reports whether gh has any
login there; GhAuthErrorHelp renders host-qualified login/refresh
commands so an unauthenticated GHES host stops masquerading as a
github.com scope problem. Also fixes the mobile paste-intent expectation
for host-carrying parsed links.
Co-authored-by: Orca <help@stably.ai>
* Bound GHES identity caches and preserve non-default ports in host identity
Cap the origin-repo and host-auth caches like ownerRepoCache; keep ports
from remote/link URLs so GHES on a non-default port is a distinct
identity; make positional github.com slugs explicit against GH_HOST;
compare work-item sources by host-aware identity key; bail cwd-less
branch lookups when no repository candidate resolved; thread host
through the renderer work-item slug lookup.
Co-authored-by: Orca <help@stably.ai>
* Thread GitHub host through issue detail requests
Incorporates ghes-issue-host-support (ed6bb96ef): one hosted issue
repository identity is resolved before the details fan-out so comments,
timeline, participants, and mention lookups cannot drift across hosts,
with SSH guards so unresolved issue/PR repositories never fall through
to gh's default host.
Co-authored-by: Orca <help@stably.ai>
* Scope remaining GitHub rate-limit accounting
* Resolve typed PR lookups across hosted repository candidates
getWorkItem's PR path probes upstream-then-origin hosted candidates
instead of origin alone, so fork checkouts resolve the base repo's PR
with the right host; issue detail resolution reuses the up-front hosted
identity and keeps the SSH unresolved-host guards.
Co-authored-by: Orca <help@stably.ai>
* Refactor GitHub repository execution setup
* Carry host on smart-submit link intents
Co-authored-by: Orca <help@stably.ai>
* Carry the project host on GitHub item dialog origins
Co-authored-by: Orca <help@stably.ai>
* Keep GHES web ports but drop SSH transport ports in host identity
Supersedes PR #9118 on this branch: http(s) remote ports identify the
Enterprise web/API endpoint and are preserved, while ssh/git transport
ports (including ssh.github.com:443) never leak into gh's host identity.
Replaces the ssh.github.com:443 special case with the structural
protocol split and ports the PR's parsing test suite.
Co-authored-by: Orca <help@stably.ai>
* Support GitHub Enterprise diffs and mutations with host-scoped caches
Parse GitHub host identity from work-item URLs and carry it through PR/issue mutations, labels, and assignments. Bound rate-limit and scope-probe caches (1024 and 512 entries) to prevent unbounded growth when interacting with multiple GHES instances. Normalize repository identity keys to include host so github.com and GHES slugs don't collide in cache and equality checks.
* Support GitHub Enterprise diffs and mutations with host-scoped caches
- Carry host identity through PR mutations and reads so fork PRs on
different GHES instances don't collide in cache or state tracking.
- Validate host authentication before routing requests to unconfigured
Enterprise servers; ambient credentials must never reach untrusted hosts.
- Scope rate-limit guards and spend tracking per host so GHES quota stays
independent from github.com quota.
- Respect explicit --hostname arguments in gh CLI calls ahead of GH_HOST or
ambient defaults, so breaker state follows the actual request target.
- Detect implicit WSL runtimes from UNC paths for consistent host auth and
execution-options scoping across mobile and desktop clients.
* Support GitHub Enterprise work-item diffs with host-scoped execution
Enterprise PRs must use their selected host consistently across diff, comments,
and file-content loads. Validate repository slugs before authenticated execution
to prevent path-injection via renderer overrides. Scope project browsing cache
and rate-limit tracking by host to prevent cross-host pollution. Use parsed
URLs as authoritative over ambient hosts for project resolution.
* Support GitHub Enterprise work-item diffs with host-scoped execution
Preserve host identity on PR/issue work items throughout the mutation and diff
pipeline so Enterprise instances (including ported endpoints like
github.acme.test:8443) can execute mutations without ambiguity. Rate-limit gh
commands by the pre-qualified --repo host, cache auth state per ported host,
and surface Enterprise hosts in project metadata and error messages.
* fix(review): drop dead rateLimitGuard/noteRateLimitSpend re-export
Both callers (project-view.ts, mutations.ts) moved to the host-scoped
repositoryRateLimitGuard/noteRepositoryRateLimitSpend; the bucket-only
re-export in internals.ts had zero importers left.
Co-authored-by: Orca <help@stably.ai>
* fix(ci): split Enterprise host work-item tests under max-lines
Move GHES/SSH host-routing cases out of work-item-details.test.ts so
the suite stays within the 800-line test max-lines budget.
* test(github): align mocks with host-scoped repository resolution
- Route origin repository resolution through getOwnerRepoForRemote, not getOwnerRepo, to match production path
- Pin github.com host on origin results so host-less fixtures pass host gate in resolveGitHubApiRepository
- Add generation-based invalidation to prevent stale slug-cache writes from in-flight resolutions
- Fix ref-sync race in ProjectPicker: use useLayoutEffect so committed tree owns browse cache key
- Defer handledCrossRepoUrlRef assignment in SmartWorkspaceNameField until resolution succeeds
- Update Enterprise host routing: found work items must not silently fall back to default host when unresolved
- Normalize GHES avatar URLs: accept explicit port 443 as canonical form, not a fallback trigger
---------
Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
Co-authored-by: Orca <help@stably.ai>
* fix(accounts): follow the runtime for WSL provider account detection (#9537)
On Windows + WSL, provider-account detection (usage recognition and the
status-bar account switcher) was pinned to the Windows host even when the
project runs in WSL, so WSL accounts were never recognized and the WSL
switcher group never appeared.
Root cause: `localAccountRuntime` hard-defaulted to 'host', which
short-circuited `getInitialClaude/CodexRateLimitTarget` before the existing
"follow the global Windows runtime default" branch could run. That branch was
therefore dead for every real user.
Fix: add an 'auto' value for `localAccountRuntime`, make it the default, and
migrate the untouched legacy 'host' default to 'auto' once (guarded by
`localAccountRuntimeDefaultedToAutoForAllUsers`; explicit 'wsl' is preserved).
'auto' resolves via a shared `resolveLocalAccountRuntimeTarget` helper: on a
windows-host default it stays host (no behavior change); on a WSL default it
follows WSL, so WSL accounts are recognized and the WSL group appears.
Wired the shared resolver into the managed-account default target, the
status-bar WSL-group gate, and the Accounts settings location toggle.
Note: detection follows the global Windows runtime default, not the live
active project's runtime (the fetch target is a single global value); the
latter is a larger follow-up.
* fix(accounts): align auto runtime consumers
* fix(accounts): keep runtime polling aligned with settings
---------
Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
* feat(status-bar): consolidate agent usage into a single roster popover
The footer usage cluster is now one quiet, borderless control: each agent
shows its tightest window as neutral text (letter badges at icon-only
width), and clicking anywhere opens a consolidated Usage popover listing
every agent worst-first — icon, name, plan, soonest reset, and per-window
threshold-colored bars. Claude/Codex rows drill into their existing
account switcher, runtime toggle, and Codex reset credits via a submenu
variant of ProviderDetailsMenu; all other providers drill into their
detail panel. Row actions and the footer links route to Settings.
Also: barColor's <60% band goes green -> neutral so color is reserved for
approaching limits (threshold color now lives only in the popover, the
always-visible bar stays monochrome), Codex plan_type is surfaced as
ProviderRateLimits.planType for the "Codex · Plus" label, and the Fable
weekly window is labeled "Fable" so it no longer collides with "wk".
* fix(status-bar): single-line sign-in row + review dedup
Signed-out roster rows now read as one line (name · "not signed in" ·
right-aligned Sign in) instead of a floating button over an orphaned
second line. Review follow-ups: the non-submenu ProviderDetailsMenu
branch reuses the extracted panelBody, and the icon-only letter badge is
one shared component so its has-data dot condition can't drift.
* fix(status-bar): harden usage roster interactions
* chore: remove unrelated formatting change
* chore(skills): refresh bundle manifest for rc.2
* fix(status-bar): preserve usage roster semantics
* fix(status-bar): preserve roster menu interactions
* chore: remove unrelated formatting changes
* fix(status-bar): keep usage reset countdowns live
* feat(status-bar): toggle compact usage summary
* fix(status-bar): simplify usage mode toggle
* feat(status-bar): replace usage footer toggle with Compact/Detailed segmented control
Swap the bottom-of-popover on/off switch for a SettingsSegmentedControl at
the top of the Usage popover (view-switcher pattern), so both modes are
named and discoverable on first open. Reuses the repo's canonical
Compact/Detailed vocabulary from the Workspace card-layout control.