- Late child/parent settlements now report individually instead of
being batched into a single combined result, so toasts and
onLateResult callbacks reflect actual per-candidate outcomes.
- Post-batch reconciler retains only unresolved provisional-ancestor
state instead of the full batch arrays, and releases it once no
provisional ancestor remains, avoiding unbounded retention for
long-hung IPC promises.
- Unblocked ancestors retried post-batch now go through the same
timeout/settlement tracking as in-batch removals, including
further late settlement if the retry itself hangs.
- Validate each remote path segment (name, symlink target, directory
entry) via assertSafeRemotePathSegment before it reaches the remote
filesystem, since Windows canonicalizes reserved device names and
NTFS streams in ways POSIX checks miss.
- Extract directory pre-scan/upload logic into
filesystem-import-ssh-directory.ts and add captureLocalUploadRoot to
detect a selected root swapped out from under an in-progress import.
- Add path-safety tests covering traversal, reserved Windows names,
and TOCTOU root replacement.
* fix(terminal): preserve Linux IME candidate digits
* fix(terminal): preserve overlapping Linux IME keys
* docs(terminal): document Linux IME candidate state
* docs(terminal): describe IME candidate event handling
* docs(terminal): clarify Linux IME state callbacks
* fix(terminal): harden Linux IME candidate fallback
Co-authored-by: Orca <help@stably.ai>
---------
Co-authored-by: yuqili03 <yuqili03@deeproute.ai>
Co-authored-by: Jinwoo-H <jinwoo0825@gmail.com>
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>
* feat(diff): add Previous/Next change navigation buttons to Diff View
Add up/down navigation buttons to the single-file Diff View toolbar so
users can jump between change regions (hunks) without scrolling, matching
IntelliJ/Android Studio. The buttons bridge the Monaco diff editor to the
header via an instance-scoped DiffNavigationProvider and call Monaco's
goToDiff('previous'|'next'); they disable when the file has no changes.
Closes#6215
Co-authored-by: Orca <help@stably.ai>
* test(diff): cover stale onDidUpdateDiff guard in fast-swap
Address CodeRabbit nitpick: prove that an update fired from a replaced
editor is ignored after a new editor registers (the subscription is
disposed on re-register). The fake editor's dispose now clears its
callback, matching real Monaco subscription semantics.
Co-authored-by: Orca <help@stably.ai>
* fix(diff): isolate navigation registration updates
* fix(diff): use arrows for change navigation
* fix(editor): tighten header action spacing
---------
Co-authored-by: Orca <help@stably.ai>
Co-authored-by: Brennan Benson <brennanbenson@Brennans-MacBook-Pro.local>
* perf(renderer): sweep suppressedPtyExitIds and pendingCodexPaneRestartIds on bulk worktree purge
Both are ptyId-keyed maps cleared alongside codexRestartNoticeByPtyId on the
normal pty-exit path, but buildWorktreePurgeState (remove-project / external
authoritative-scan / hydration-stale reconcile) runs no terminal teardown and
only swept codexRestartNoticeByPtyId and migrationUnsupportedByPtyId. A
suppression / pending-restart flag planted just before a doomed pty's exit
therefore stranded one entry per removed pty for the renderer session.
Add both to omitByPtyId, matching their two swept siblings; extend the
bulk-purge leak regression test to cover them.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(renderer): cover dormant PTYs in bulk purge
* Fix worktree-purge PTY id leaks: proportional alias scan and stricter su
- purgeWorktreeTerminalState now short-circuits the surviving-alias scan
once every doomed remote handle alias is matched, instead of scanning
every surviving tab's terminals on each purge.
- Repo removal (removeRepo) no longer force-marks killed PTY ids as
suppressed/pending-restart; those tabs are gone before async exit
events fire, so retaining the guards just leaked entries.
- Update tests to cover raw remote-handle aliasing against surviving
local PTY ids and to assert the tightened cleanup behavior.
* Fix environment-scoped remote PTY id handling for guards and lifecycle s
- Canonicalize legacy remote PTY ids at attach time so guard/restart/notice
maps stay scoped per runtime environment instead of colliding on raw
aliases shared across environments.
- Migrate suppressedPtyExitIds, pendingCodexPaneRestartIds,
codexRestartNoticeByPtyId, and migrationUnsupportedByPtyId entries when a
tab's PTY id transitions from a legacy alias to its scoped id.
- Narrow shutdown/purge guard sets to renderer-visible PTY ids only, since
raw exact-stop RPC handles never emit pane exit callbacks and previously
caused cross-environment leakage and unnecessary alias scanning.
- Prevent a late PTY exit from resurrecting a ptyIdsByTabId entry for a tab
whose worktree/repo was already purged.
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
* fix(i18n): use useTranslation hook in SidebarNav for reactive language switching
Replace plain translate() calls with the useTranslation() hook so the
React.memo-wrapped SidebarNav re-renders when the i18n language changes
after async catalog loading, fixing sidebar labels staying in English
on non-English locale startup.
Closes#7530
* fix(i18n): make sidebar language subscription race-safe
* refactor(i18n): use standard sidebar language subscription
---------
Co-authored-by: 文小松 <wenxiaosong@wenxiaosongdeMac-mini.local>
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>
Three independent root causes all produce unhandled Promise rejections
that accumulate in the renderer heap and eventually exhaust it:
1. node-pty Windows backend: WindowsTerminal.kill(signal) throws
"Signals not supported on windows." for any signal argument.
Add killPtyProcess() helper in pty-handler.ts that catches the
Windows signal error and falls back to kill() with no argument.
Update isPtyAlreadyGoneError() in main/ipc/pty.ts to classify
the Windows signal error as an already-gone PTY so it does not
re-throw and propagate to the renderer as an unhandled rejection.
2. Uncaught pty.kill() calls in renderer: three call sites in repos.ts,
pty-transport.ts fire-and-forget window.api.pty.kill() without
.catch(). On SSH disconnect or Windows PTY teardown these produce
unhandled rejections that the crash-diagnostics breadcrumb system
retains in memory without releasing. Add .catch(() => {}) to each.
3. ResizeObserver loop: TerminalPane.scheduleSync ran syncPaneTitleOverlayRects
inside a single requestAnimationFrame, which fires while the browser
is still dispatching ResizeObserver notifications. The resulting React
state update re-triggers the observer, producing a "ResizeObserver loop
completed" error on every resize and potentially freezing the renderer
thread. Switch to a double-rAF so the work defers to the next paint
frame after the observation cycle completes. Also suppress the
ResizeObserver loop ErrorEvent in crash-diagnostics.ts so that
any that slip through do not accumulate in the breadcrumb buffer.
Add .catch(() => callback(undefined)) to the Promise.all in
terminal-link-handlers.ts so SSH-disconnect errors don't flood
the unhandled-rejection count.
Co-Authored-By: Claude <noreply@anthropic.com>
* 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>
Automatically resolve and require explicit push targets for linked
GitHub PRs and GitLab MRs before allowing remote actions.
This prevents operations like push or sync from targeting helper
upstreams or falling back to default behaviors when the review target
is unavailable. Also, recover missing push targets during metadata
saves to heal existing linked PRs.
* fix(codex): share global AGENTS.md into managed runtime homes
Orca launches Codex with a managed CODEX_HOME, so host and WSL sessions otherwise lose the user's global instructions. Share AGENTS.md through the existing ownership-aware link-or-copy path, with a narrow WSL launch sync that avoids copying unrelated resource directories.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(codex): copy global AGENTS.md into WSL runtime homes
Symlinking across the \wsl.localhost 9P boundary stores a Windows UNC
target the distro cannot resolve, so the global AGENTS.md silently would
not load inside WSL when the host symlink succeeded. Copy it like the
config mirror already does across the same boundary.
Co-authored-by: Orca <help@stably.ai>
* Fix WSL global-instruction sync to dereference symlinks and skip redunda
- Copy AGENTS.md contents (not the symlink) into WSL runtime homes, since
a host-side symlink is unusable inside the distro
- Skip rewriting the fallback copy when source contents are unchanged,
avoiding needless writes across the UNC boundary on every Codex launch
- Handle non-regular-file sources and malformed/undeletable marker
directories without blocking sync or stranding stale instructions
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
Co-authored-by: Orca <help@stably.ai>