Repository navigation
Sync upstream 58293445e (conflicts) - #3
Open
WrittenByAI wants to merge 277 commits into
Open
WrittenByAI wants to merge 277 commits into
WrittenByAI wants to merge 277 commits into
Conversation
…blyai#8709) Reconstruct remote POSIX commands with bounded printf arguments so non-POSIX SSH login shells can forward them without requiring remote base64. Preserve relay and system-SSH stdin, and centralize login-shell flag selection for csh/tcsh compatibility.\n\nValidated against real csh and tcsh OpenSSH targets with built-in and system SSH, including cold relay deployment, stdin upload, PTY I/O, file mutation, and reconnect.
…he sidebar roster (stablyai#8825) * fix(agent-status): reap finished Claude named agents/teammates from the sidebar roster stablyai#8522 stopped one-shot subagents from squatting as idle rows, but named background agents (Workflow/orchestration/ultracode lanes and agent-teams teammates) still piled up permanent "Idle - <type>" child rows for the rest of the session — the reported regression (11 idle rows under an "Orchestration Messages" pane, all idle 3-6h after finishing). Root cause, confirmed against live hook captures (claude 2.1.210): named agents get teammate-shaped ids (a<name>-<hex>) AND now appear in Stop's `background_tasks` as `type: "teammate"` entries whose status stays "running" forever — even after the agent finished. The old code read that shape as a "resumable teammate", so SubagentStop only marked it idle and the fold never reaped it (a present teammate task kept hasTeammateTypedTask true). The rows never left. Fix: the roster now tracks only WORKING children. - SubagentStop removes the child outright (teammate-shaped or not) — it is the reliable finish signal; the teammate task's "running" status is not. - TeammateIdle removes by name as the fallback when a SubagentStop is lost. - A lead Stop's background_tasks still reaps unlisted children: hyphen-free one-shots always, and teammate-shaped rows once a complete inventory shows no teammate-typed task at all. A live named agent whose id never appears is kept only while a teammate-typed task is still present (the done-gate). - Hydration drops persisted idle snapshots so a restart can't re-pile them. Verified live in a dev Electron instance driving a real Claude TUI that spawns four named background agents: pre-fix the pane resolves to done with four persistent "Idle - <probe>" rows; post-fix each row disappears the instant its agent finishes and the roster drains to empty (done, zero child rows). Tests: roster + row-lifecycle + hook-listener suites rewritten to the working-only semantics, grounded in the captured 2.1.210 hook stream (126 passing). Typecheck + oxlint clean. * fix(agent-status): prune persisted idle Claude children * fix(agent-status): persist Claude idle-row hydration cleanup * fix(agent-status): avoid ambiguous teammate idle cleanup * fix(agent-status): reconcile replacement children at roster cap
…pears (stablyai#8491) * fix: close Add Project host selector when an SSH credential prompt appears The SSH password/passphrase dialog (z-50) renders below popovers (z-60), so a host selector left open during a connect attempt covered the credential prompt and its input field. Close the selector as soon as a credential request is queued so the modal prompt is visible and usable. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix: raise SSH credential prompt above popovers instead of closing the host selector The credential prompt is a preemptive global modal, but DialogContent's default z-50 sits below popovers (z-60) and menus (z-70), so any surface left open while a prompt fires covers the focused password input — the Add Project host selector was one reproducible case (stablyai#8490). Raise the prompt's overlay/content to the frontmost tier (!z-[140] / !z-[150], matching link-routing-preference-dialog) so it renders above every popover/menu, and drop the host-selector close effect: the picker can stay open and is available again after cancel for an immediate retry. --------- Co-authored-by: jonghoonlee <jonghoonlee@inocras.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
…TA-1800) (stablyai#8706) * fix(terminal): kill agent descendant processes on session teardown (STA-1800) Agent CLIs spawn tool children in detached process groups that PTY SIGHUP can never reach. Killing an agent session (tab close, retire, sleep) left those children running as orphans — eight orphaned git processes burned ~8 cores for up to 11.5h under the agents-running keep-awake and drained a battery to 8%. New pty-descendant-termination module: snapshot the ppid tree BEFORE signalling (a dead root's descendants reparent to pid 1 and become unfindable), SIGTERM the root group and every descendant, then after a 2s grace SIGKILL survivors gated on a pid+start-time identity re-check so a recycled pid is never signalled. Snapshot is bounded and never rejects; failures degrade to today's shell-only kill. Wired for agent sessions only (plain terminals keep nohup semantics) at all three POSIX kill sites: local provider shutdown, daemon TerminalHost immediate kill (the pty:kill path — force-kill bypassed Session.kill entirely), and daemon Session graceful kill. Verified live in the built app: an agent pane with a detached-pgid child; the child survived on the unwired build (three control runs) and dies within ~5s with the fix. Windows ConPTY and SSH-hosted PTYs keep the previous foreground-tree contract (documented follow-ups). * fix(terminal): harden descendant teardown * fix(terminal): require fresh process snapshots * fix(terminal): close descendant teardown races * refactor(terminal): preserve teardown line budget * fix(terminal): keep descendant teardown fresh and identity-safe * docs(reliability): record integrated descendant E2E * fix(terminal): bound descendant teardown work * fix(terminal): share descendant snapshot indexes * docs(reliability): record descendant review evidence * revert: remove speculative descendant hardening
* fix(sidebar): persist manual project order across hosts Co-authored-by: Orca <help@stably.ai> * fix(sidebar): scope desktop repo reorder by host Co-authored-by: Orca <help@stably.ai> * fix(web): cover host-scoped repo reorder API Co-authored-by: Orca <help@stably.ai> --------- Co-authored-by: Orca <help@stably.ai>
…blyai#8862) * Preserve original markdown style when editing in the rich editor The rich markdown editor re-serialized the whole document into a canonical style on the first edit, so a one-character change rewrote every non-canonical construct (`_x_`->`*x*`, `__x__`->`**x**`, `* item`->`- item`, dropped trailing newline) and produced a large, unexpected diff. Reconcile the canonical getMarkdown() output back toward the original source bytes at every disk-bound serialize site (debounced autosave, flush, Cmd/Ctrl+S) so untouched regions keep their markup and only the edited region changes. A divergent-base fuzzy patch (diff-match-patch) carries the user's edit onto the original style; a safety re-parse requires the result to render-equal what the editor shows, else it falls back to today's canonical output — so content semantics can never change. The rich editor stays the default for markdown. Fixes stablyai#6080 * Bound markdown reconciliation diff work
…ammer a self-hosted forge (stablyai#8858) * fix(gitea): share and cache the /pulls scan so card refreshes can't hammer a self-hosted forge Each worktree card resolved its branch by paginating the repo's full /pulls listing (state=all, up to 5 pages) independently, with a 5s timeout. Gitea/Forgejo have no head-branch filter, self-hosted Forgejo takes ~5s per page (it loads reviewer data per PR), and a push event refreshes every card at once — so one repo turned into hundreds of near-simultaneous requests whose responses were aborted right as they completed, and the burst OOM-killed a 512Mi Forgejo pod (stablyai#8807). - Share one in-flight /pulls scan per repo across concurrent branch lookups and cache the result for 30s, so a burst costs a single page walk instead of one per worktree. - Raise the list-scan timeout to 15s so slow-but-successful pages are used instead of discarded and retried. - Invalidate the cached scan after Orca itself creates a PR (and before the post-create fallback lookup) so the new PR is seen immediately. Fixes stablyai#8807 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(gitea): don't let an in-flight scan re-cache a listing from before an invalidation Review feedback: if a PR is created while a card-refresh scan is in flight, the invalidation cleared the cache but the scan then finished and re-cached the pre-create listing, hiding the new PR for a full TTL. Track a per-repo generation: invalidation bumps it (and drops the in-flight promise), and a scan only writes the cache when its generation is still current. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(gitea): bound PR scan cache and retry failures Co-authored-by: Orca <help@stably.ai> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: Jinwoo-H <jinwoo0825@gmail.com> Co-authored-by: Orca <help@stably.ai>
…t label (STA-1860) (stablyai#8840) * fix(native-chat): deliver AskUserQuestion answer by option number, not label Claude Code's AskUserQuestion is an arrow-navigate selector: a bare Enter commits the HIGHLIGHTED default (the first option), and pasted label text does not move the highlight. Native chat delivered the answer as the option LABEL text + Enter, so every non-first pick was silently committed as the first option (STA-1860 Bug A). Deliver the answer by each option's stable 1-based number instead (the marker the selector commits on), paced so each keystroke lands after the selector step renders. Covers single/multi-select, multi-question, and free-text ('Type something') answers. Verified live against Claude Code 2.1.210. - native-chat-interactive-prompt.ts: buildAskAnswerKeys / AskAnswerSelection / AskAnswerKeyGroup / hasAskAnswer; formatAskAnswer kept (index-based) for the non-Claude label path - native-chat-runtime-send.ts: sendNativeChatAskAnswer (paced keystroke groups) replaces the old label-line sender - card + interactive hook thread structured { indices, other } selections STA-1860 * fix(native-chat): harden AskUserQuestion answer lifecycle * fix(native-chat): stop stale paced answer writes * fix(native-chat): cancel paced answers during commit Cancel stale AskUserQuestion timers before replacement UI can commit, and localize the multi-step sending state in every shipped locale.
…esses (stablyai#8849) * fix(ssh): AND lsof selectors so relay reset can't kill unrelated processes `forceStopRelayForTarget` resolved PIDs with `lsof -t -U "$sock"`. lsof ORs its selectors by default, so this selects every process holding ANY unix socket in addition to the socket's owner. On hosts where lsof cannot match AF_UNIX sockets by path, the path term matches nothing and the sweep degrades to all unix-socket holders — including systemd --user — which the TERM+KILL loop then takes down (stablyai#8762). Add `-a` to AND the unix-socket and path selectors: where lsof supports path matching the result is exactly the socket's holders (verified: 1 PID with -a vs 97 without on macOS); where it doesn't, the result is empty and the existing pgrep fallback — scoped to command lines that reference the relay's per-instance socket name — takes over. Fixes stablyai#8762 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * review: add relay reset process-safety coverage Exercise the generated POSIX reset script with controlled lsof and pgrep commands plus a mocked kill, and keep the selector rationale concise. Co-authored-by: Orca <help@stably.ai> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: Jinwoo-H <jinwoo0825@gmail.com> Co-authored-by: Orca <help@stably.ai>
* fix(status-bar): sync usage meters with display mode * docs(status-bar): correct barColor comment now that fill follows display mode --------- Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
…tablyai#8800) * fix(crash-reporting): coalesce repeated renderer error breadcrumbs The crash-breadcrumb ring holds only 30 entries, and renderer breadcrumbs arrived via the plain uncoalesced path. A repeating renderer error — like the ResizeObserver and SSH-rejection storms in stablyai#8260 (50+ identical events) — flushes the entire ring in seconds, erasing the pre-crash trail exactly when a crash report is about to snapshot it. Route renderer_error and renderer_unhandled_rejection breadcrumbs through the existing recordCoalescedCrashBreadcrumb, keyed by name plus message prefix with a 30s window. Repeats collapse into one entry carrying suppressedSinceLast, so a storm shows up as "error X fired N times" alongside 29 slots of surrounding context instead of 30 identical copies. Other breadcrumb names keep the plain path; telemetry trace spans are unchanged. Renderer breadcrumb routing tests move to a focused crash-reporting-renderer-breadcrumbs.test.ts (the main suite is at the max-lines ceiling). * fix(crash-reporting): coalesce durable renderer traces * fix: harden renderer crash guards * fix(crash-reporting): preserve distinct error sources
* fix(runtime): retain watcher and PTY teardown ownership * fix(runtime): restore watchers after interrupted cleanup * fix(runtime): prevent stale watcher revival * test(runtime): cover watcher shutdown ownership * test(daemon): model physical PTY exit * fix(daemon): keep shutdown terminating when disposal cannot prove exit A rejecting host.dispose() (unreapable child past its exit deadline) left the shutdown RPC without its process.nextTick(shutdown) and skipped socket cleanup in shutdown(), stranding the daemon as an unreachable orphan after the stale-daemon replacement flow unlinks its socket. Log and continue: daemon exit reparents the child to init instead of blocking on it. * fix(runtime): keep local watching alive after an idle-kill deadline miss An idle child that outlived the exit deadline set shutdownRequested on the shared desktop supervisor, which has no retire-and-replace path — every later subscribe rejected supervisor_disposed and the roots were cached unwatchable, silently ending local file watching for the session. The idle path owns zero records, so there is no double-watch hazard; the zombie keeps its capacity reservation until physical exit and the next subscribe gets a fresh child. * fix(renderer): resync replayed paired-web file watches Transparent replay removed the implicit resync the old close-and-rebuild path provided: a replayed files.watch only reports changes from its own native setup, so changes during the reconnect gap were silently lost. Deliver a conservative overflow to consumers once the replayed watch is ready, matching the overflow-after-interruption contract everywhere else. * fix(runtime): address teardown review findings * fix(runtime): retry watches after teardown deadlines * Fix PTY descendant leaks on forced teardown * Fix jitter-sensitive terminal lifecycle test
…ces (stablyai#8409) * fix(worktrees): stop surfacing prunable git worktrees as live workspaces A worktree still registered in git but whose directory was deleted (git's `prunable` state) was enumerated as a normal workspace, producing repeated pty:spawn DaemonProtocolError / fs:readDir ENOENT loops and a blank pane. - Parse the `prunable` porcelain field (Git >= 2.36) in both the main and relay worktree-list parsers. - For Git < 2.36 (no `prunable` field), probe each linked worktree path for existence on the fallback line-block path, skipping locked registrations to mirror git's own prunable rules. - Omit prunable worktrees from the detected-workspace enumeration only; removal/cleanup flows keep seeing them. - Extend the real-binary compatibility contract with the 2.36 `prunable` boundary. Fixes stablyai#8389 Claude-Session: https://claude.ai/code/session_018Rg1Bpq4GGwmz613hq6RSD * fix(worktrees): pin the prunable/locked porcelain annotations to their real Git 2.31 boundary The prunable and locked annotations landed in Git 2.31, five releases before `worktree list -z` (2.36); only -z defines the capability fallback boundary. Correct the compatibility contract so a future matrix entry in the 2.31-2.35 range passes, and reword the fallback comments: on 2.31-2.35 the annotations still parse and the existence probe is a backstop; only Git <2.31 relies on it outright. * fix(worktrees): omit prunable registrations from the Space scan A prunable registration has no directory to size or reclaim, so Space rendered it as a dead "Missing" row whose checkbox stayed disabled with no prune/remove affordance (reported on macOS after a reboot cleared /private/tmp under 16 registrations). Skip prunable entries in the scan, matching the workspace enumeration; removal flows list worktrees separately and still see them. --------- Co-authored-by: kaynan <kaynan.camargo@terceiro-sky.com.br> Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
…witch (stablyai#8760) * Fix PR checks sticking to a stale linked PR after a terminal branch switch A worktree's linked PR is a branch-scoped hint, but two refresh paths race when a terminal switches branches: the git-status identity path clears branch-scoped review links, while the worktree-listing path rehydrates the new branch together with the stale persisted link and clears nothing. When the listing lands first (the common case — worktree listing is much faster than git status), the identity path sees no branch change and the stale link survives. Every subsequent refresh then re-fetches the linked PR by exact number, which ignores the branch, so Checks stays pinned to the old branch's PR and the Refresh button cannot recover. Two-part fix: - Prevention: listing refreshes now route observed branch switches through updateWorktreeGitIdentity before merging, so the existing link clear and tombstone machinery runs no matter which refresh path wins. Gated on the entry still carrying branch-scoped review context so a stale listing row cannot roll back a newer branch identity. - Recovery: PRInfo now carries headRefName, and a fetch that returns the linked OPEN PR whose head branch matches neither the current branch, the worktree push target, nor the worktree HEAD clears the durable link and re-resolves by branch. Wired into both fetchPRForBranch and the background refresh coordinator, mirroring the merged-PR divergence clear. This also heals wedged workspaces persisted by earlier builds. * Harden stale PR recovery across refresh races * Avoid duplicate PR recovery refresh work * Index linked PR refresh aliases once
* Reopen closed tabs with Cmd+Shift+T, including terminals
Cmd/Ctrl+Shift+T now reopens the most recently closed tab of any kind
(terminal, browser, or editor) in true recency order, walking back
through close history on repeated presses — Chrome/Ghostty semantics.
Terminal tabs previously had no reopen at all, and the shortcut picked
browser-vs-editor by focused surface instead of recency.
Terminal closes capture a snapshot (startupCwd, shellOverride,
customTitle, color) on explicit user closes only; PTY exits, launch
cleanup, onboarding, and floating-panel closes are excluded. Reopen
spawns a fresh shell through the same createTab path as the "+" button.
A per-worktree cross-type kind list (new recently-closed-tabs slice)
orders the three existing per-type stacks; both new maps are purged on
worktree removal and re-keyed on rename with startupCwd prefix remap.
createNewTerminalTab moved to terminal-tab-create.ts to keep
terminal-tab-actions.ts under the max-lines budget.
Requested via Discord feedback; STA-1287.
Co-authored-by: Orca <help@stably.ai>
* Update close-tab assertions for captureRecentlyClosed flag
Floating-panel, launch-failure, and stale-agent-tab closes now thread
{ captureRecentlyClosed: false } so system-initiated closes don't pollute
the reopen stack. Update the affected test expectations to match the new
call shape.
Co-authored-by: Orca <help@stably.ai>
* fix(reopen): skip local terminal reopen on remote-runtime worktrees
reopenClosedTerminalTab recreated terminals via a raw local createTab
without the isWebRuntimeSessionActive guard every sibling terminal-create
path uses, so Cmd+Shift+T on a remote-runtime-owned worktree (paired-web
or desktop-attached headless-serve) spawned an unbacked local phantom tab
not tied to the host session. Guard at reopen time via the cycle-free
getRuntimeEnvironmentIdForWorktree — importing web-runtime-session into a
store slice creates a store-index init cycle. Remote terminal reopen stays
deferred; the cross-type dispatcher falls through to browser/editor.
Adds a regression test and corrects an inaccurate removal-path comment.
* fix(test): seed runtime host id for remote-reopen guard test
The prior test set settings.activeRuntimeEnvironmentId by spreading the
store's settings, which widened workspaceDir to string|undefined and broke
`pnpm typecheck` (GlobalSettings requires string). Seed the worktree with a
`runtime:` hostId instead — getRuntimeEnvironmentIdForWorktree reads it
directly, so no settings spread is needed.
* Remove unrelated formatter changes
---------
Co-authored-by: Orca <help@stably.ai>
Fixed misleading Korean translation of "on"/"off" and "word wrap"
* docs(readme): fix Korean translation issues * docs(readme): align agent list with English README across translations Replace OpenClaude with Pi in Spanish, Japanese, and Chinese READMEs to match the English README's agent list (Codex, ClaudeCode, OpenCode, Pi). Portuguese already had the correct list. 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: address pr-bug-scan validated finding from stablyai#6335 Replaced unguarded crypto.randomUUID() with the established createBrowserUuid() fallback in both markup shape-id call sites. * fix(review): cover markup crypto fallback call sites with regression tests PR stablyai#8804 replaced two unguarded crypto.randomUUID() calls with createBrowserUuid(), matching the convention established in stablyai#5471, but shipped without extending that PR's regression-test pattern to the new call sites. Add hook-level tests for useMarkupPointerHandlers.onPointerDown and useMarkupEditor.commitPendingText under a simulated non-secure-context crypto global. Co-authored-by: Orca <help@stably.ai> * fix(review): consolidate annotation bridge token onto createBrowserUuid BrowserPane had its own inline crypto.randomUUID guard with a Date.now()+Math.random() fallback, duplicating the getRandomValues-based fallback already centralized in createBrowserUuid() and used at every other call site in the renderer. Co-authored-by: Orca <help@stably.ai> --------- Co-authored-by: orca-bug-scan-bot <orca-bug-scan-bot@stably.ai> Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com> Co-authored-by: Orca <help@stably.ai>
* feat(mobile): edit saved host endpoints * fix(mobile): reject ambiguous numeric host addresses * fix(mobile): label edit host inputs * fix(mobile): make host edit save atomic and remove superseded mutators Two independent review rounds found the same class of foot-gun: a superseded mutator (updateHostEndpoint, then renameHost) left in host-store.ts after the atomic updateHostNameAndEndpoint refactor, with zero remaining callers. Either could be reintroduced by a future caller and silently regress the non-atomic name/endpoint race the atomic function was written to close, so both are removed. Also covers reconnect-rejection and endpoint-only save paths that were missing test coverage, and merges origin/main (stablyai#8789) so this lands without reverting the mobile terminal restore fix. Co-authored-by: Orca <help@stably.ai> * Simplify save-race comment and reword host-removed error message - Trims the redundant comment explaining the savingRef race guard down to one line. - Changes the "no longer saved" load-error copy to "was removed" for clearer phrasing, updating the matching test expectation. --------- Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com> Co-authored-by: Orca <help@stably.ai>
…blyai#8909) The native-smoke job ran bare `pnpm vitest run <files>` with no --config. There is no root-level vitest/vite config, so vitest fell back to its built-in defaults: a 5000ms testTimeout and no Windows worker cap. The real csc.exe launcher-compile tests (build-windows-cli-launcher and ssh-remote-cli-launcher) cold-start in 1.4-7.4s on hosted Windows and tipped over the 5s default, failing the PR check. Pass --config config/vitest.config.ts like every other vitest invocation in the repo (pnpm test, pr.yml) so the job gets the shared 30s timeout and Windows maxWorkers:4 cap (less CPU contention -> lower compile variance). Positional file args still filter to just the listed files.
…f a dead zone (stablyai#8891) * fix(sidebar): map header drags to the nearest boundary slot instead of a dead zone Fixes stablyai#8879 * fix(sidebar): bound header edge drops to measured content
…tablyai#8917) Move toggle heading commands (toggle-h1..h4) into their own "Toggle headings" group, separate from plain heading commands, so the slash menu distinguishes static headings from collapsible toggle headings. Also shorten toggle heading labels from "Toggle Heading N" to "Toggle HN" for consistency with the plain heading labels.
…blyai#8918) The remote CLI installer deleted the legacy orca.cmd bridge before it confirmed csc.exe existed, so a minimal Windows host without the .NET compiler lost its existing CLI command and got nothing back. Move the legacy-shim removal to run only after the compile and the launcher-existence guard both pass. Missing-compiler and compile-failure paths now exit non-zero while leaving the existing orca.cmd untouched; a successful upgrade still clears the unsafe %* bridge (orca.exe already shadows orca.cmd via PATHEXT). No %* forwarding is restored. Co-authored-by: Orca <help@stably.ai>
…API (stablyai#8680) * fix(issues): replace cursor-based pagination with page-number Search API Problem ======= Issue pagination (stablyai#8649) had two bugs: 1. Pages 6-16 were unreachable — clicking page 16 highlighted page 5; clicking 6/7 did nothing. The old cursor-based approach (updated:<CURSOR) broke with Search API's relevance sorting — pages after the first few returned no items even though more issues existed. 2. Issue numbers appeared out of order on loaded pages (e.g. stablyai#1082 between stablyai#1308 and stablyai#1499), because client-side sort used updatedAt instead of issue number. Root Cause ========== The pagination used two separate GitHub API strategies: - Initial page 0 load: REST endpoints (repos/:owner/:repo/issues, repos/:owner/:repo/pulls) sorted by updatedAt - Subsequent pages: Search API with cursor (updated:<DATE) These two sources returned items in different orders, causing items to go missing or appear on wrong pages across page boundaries. Solution ======== 1. Unified on GitHub Search API for all pages — initial load and pagination both use search/issues?q=...&page=N, eliminating the REST-vs-Search inconsistency. 2. Changed from cursor-based (update:<DATE) to page-number-based pagination (page=N), which the Search API supports natively. 3. Switched client-side sort from updatedAt to issue number (sortWorkItemsByNumber), matching GitHub's default Issues view. 4. Parallelized page fetches in handleLoadNextPage — clicking page 16 now fetches all intermediate pages concurrently (~2s) instead of sequentially (~30s). 5. Cleaned up dead legacy gh issue list / gh pr list code path, extracted quoteForSearch helper, shortened overlong comments. Files changed: 11 files, +140/-127 lines Closes stablyai#8649 * chore: remove unrelated merge formatting --------- Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
…eld (stablyai#9526) Co-authored-by: Orca <help@stably.ai> Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
Updated README to guide Windows users to the latest RC release, which includes critical Windows-specific bug fixes not yet in the stable build. Added prominent notices at the top of the download section and in the direct-download links.
Keep the callout secondary and clear that the RC has Windows-specific fixes.
…switch (stablyai#9002) (stablyai#9393) * perf(renderer): stop full durable-state save on every top-level view switch (stablyai#9002) Persist activeView in a tiny profile-scoped sidecar instead of mutating the monolithic recovery snapshot. Active-view-only updates now bypass the broad UI normalization and durable save scheduler, while a 100ms atomic writer coalesces rapid switches and a synchronous shutdown checkpoint closes the immediate-exit race. Legacy state remains a migration and downgrade fallback. Coordinate renderer shutdown capture through one guarded checkpoint so workspace sessions and the active-view preference both survive graceful reloads, restarts, and quit cancellation. Add a persistence-boundary test proving the sidecar stays below 64 bytes while orca-data.json remains byte-for-byte unchanged, plus repeated Windows Electron restart coverage and a path-normalization-safe restart fixture. * harden active-view sidecar: prototype-safe validator, race-free async swap, independent shutdown flush - isTopLevelView uses Object.hasOwn so a corrupt sidecar can't smuggle inherited keys (constructor/__proto__) through as a valid view. - writeAsync guards the generation check and rename synchronously (renameSync) so a shutdown flushOrThrow can no longer interleave and let a stale async rename clobber the freshly-written view. - shutdown checkpoint flushes the durable store and the active-view sidecar in independent try/catch blocks so one store's failure can't skip the other. Added regression tests for all three. Co-authored-by: Orca <help@stably.ai> --------- Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com> Co-authored-by: Orca <help@stably.ai>
badgen.net rate-limits against GitHub and paints "429" into the badge. Switch stars to shields.io (repo stargazers_count), use a static MIT license badge, and drop /stargazers links which now 404 for the public after GitHub's July 2026 stargazer access restrictions.
Add a native French README and put Chinese, Japanese, and Korean first in the language switcher.
Bump the Windows RC download notice and installer link to the newer RC.
…aemon/pty) (stablyai#9542) Collapse multi-line explanatory comment blocks into single-line "why" statements per AGENTS.md ("Document the Why, Briefly"): drop restatements of the code and mechanism narration; keep the non-obvious reason, external refs, and directives. Comments-only — verified no code changed via a Babel/esbuild comment-strip token-equality gate against origin/main; typecheck and oxlint clean. Area: main — core runtime, ipc, daemon, pty, providers. 73 files changed, 3206 insertions(+), 10475 deletions(-). Co-authored-by: Orca <help@stably.ai>
…roviders/…) (stablyai#9543) Collapse multi-line explanatory comment blocks into single-line "why" statements per AGENTS.md ("Document the Why, Briefly"): drop restatements of the code and mechanism narration; keep the non-obvious reason, external refs, and directives. Comments-only — verified no code changed via a Babel/esbuild comment-strip token-equality gate against origin/main; typecheck and oxlint clean. Area: main — git, source-control, providers & integrations. 40 files changed, 1432 insertions(+), 4473 deletions(-). Co-authored-by: Orca <help@stably.ai>
…oks/lib) (stablyai#9545) Collapse multi-line explanatory comment blocks into single-line "why" statements per AGENTS.md ("Document the Why, Briefly"): drop restatements of the code and mechanism narration; keep the non-obvious reason, external refs, and directives. Comments-only — verified no code changed via a Babel/esbuild comment-strip token-equality gate against origin/main; typecheck and oxlint clean. Area: renderer — store, hooks, lib, runtime. 36 files changed, 1673 insertions(+), 5677 deletions(-). Co-authored-by: Orca <help@stably.ai>
Collapse multi-line explanatory comment blocks into single-line "why" statements
per AGENTS.md ("Document the Why, Briefly"): drop restatements of the code and
mechanism narration; keep the non-obvious reason, external refs, and directives.
Comments-only — verified no code changed via a Babel/esbuild comment-strip
token-equality gate against origin/main; typecheck and oxlint clean.
Area: mobile. 11 files changed, 339 insertions(+), 1137 deletions(-).
Co-authored-by: Orca <help@stably.ai>
…stablyai#9544) Collapse multi-line explanatory comment blocks into single-line "why" statements per AGENTS.md ("Document the Why, Briefly"): drop restatements of the code and mechanism narration; keep the non-obvious reason, external refs, and directives. Comments-only — verified no code changed via a Babel/esbuild comment-strip token-equality gate against origin/main; typecheck and oxlint clean. Area: shared, cli, relay, preload. 26 files changed, 1039 insertions(+), 3300 deletions(-). Co-authored-by: Orca <help@stably.ai>
…blyai#9546) * refactor(comments): slim verbose comments in renderer components Collapse multi-line explanatory comment blocks into single-line "why" statements per AGENTS.md ("Document the Why, Briefly"): drop restatements of the code and mechanism narration; keep the non-obvious reason, external refs, and directives. Comments-only — verified no code changed via a Babel/esbuild comment-strip token-equality gate against origin/main; typecheck and oxlint clean. Area: renderer — components. 64 files changed, 2610 insertions(+), 8794 deletions(-). Co-authored-by: Orca <help@stably.ai> * fix(comments): restore comment markers pinned by source-boundary guard tests Four *-boundary.test.ts guards assert on exact comment strings in the source (e.g. '// Why: issue stablyai#4756 keeps project-view actions on the direct'). Slimming reworded them, breaking the guards. Restored the pinned prefixes while keeping the comments to one line. Guard suite (16 files/110 tests) green locally. Co-authored-by: Orca <help@stably.ai> --------- Co-authored-by: Orca <help@stably.ai>
…blyai#9478) * Add a mobile native-chat opt-in so users pick terminal vs chat once Mirror the notifications one-time opt-in for the native-chat default view. After pairing, a full-screen modal (modeled on notification-opt-in) lets the user choose whether supported agent sessions open in the terminal or in native chat, then persists the choice to the existing orca:defaultSessionView key. - Expose readDefaultSessionViewPreference() (tri-state; absent key = undecided) so the gate can prompt exactly once; loadDefaultSessionView() is unchanged. - shouldPresentSessionViewOptIn() gates the screen; the home focus effect shows it after the notification opt-in. - Settings -> Native chat toggle (already shipped) remains the recovery path. * fix(mobile): preserve onboarding flow after pairing * refine mobile session view opt-in copy * Unify mobile onboarding prompts
Stable v1.4.147 includes the Windows fixes, so point users at latest again.
…lyai#9469) * Stop attaching stale closed PRs/MRs to default-branch checkouts On the repo default branch, the implicit head-branch PR lookup (state=all) could attach a historical closed/merged PR whose head ref was the default branch name and show its wrong diffs and checks (stablyai#9171). Add a shared default-branch guard: an implicit branch-name match on the repository's default branch never surfaces a non-open review. Applied at the branch-lookup choke point of all five provider clients (GitHub, GitLab, Bitbucket, Azure DevOps, Gitea). Explicitly linked reviews are exempt; open reviews from the trunk stay visible; resolution is lazy (zero git calls unless a non-open candidate appears), TTL-cached, transport-aware (local/WSL/SSH), probe-time-bounded, and fails open. * Treat stuck-locked GitLab MRs as non-open in the default-branch guard Three code-review lanes flagged (one reproduced) that 'locked' — normally a seconds-long merge transition, but a known GitLab wedge state — leaked past the closed/merged-only check and would re-create the stablyai#9171 symptom for a stuck-locked MR whose source branch is the trunk. * Bound default-branch lookup to one refresh budget * Coalesce default-branch resolution probes
…i#9015) Move managed agent-hook filesystem work behind one relay RPC so high-latency SSH connects pay one WAN round trip instead of hundreds. Keep installers serial, lock shared account config across relay processes, and fence cancelled connection generations from replacement state. Co-authored-by: nasagong <zinho2000@gachon.ac.kr> Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
…blyai#9535) * fix: never surface coding-agent scratch worktrees in the sidebar Sub-agent runs (e.g. Claude Code worktree isolation) create throwaway git worktrees at tool-internal paths like <repo>/.claude/worktrees/. These showed up as workspace rows for repos whose non-Orca worktree visibility is 'show', and would otherwise ping the discovery card and new-worktrees inbox on every fan-out. Classify them as 'agent-scratch' via a curated path-segment matcher and suppress them unconditionally: sidebar, discovery card, inbox, dialog counts, add-handoff reveal trigger, and the metadata fallback. Selected checkouts and explicitly imported paths still win. Fixes stablyai#9388 * fix: preserve scratch worktree visibility boundaries * fix: hide scratch worktrees from linked checkouts * docs: explain scratch root normalization
…ai#9238) * feat(skills): ship orca-cli as a first-generation hybrid stub Convert the installable orca-cli SKILL.md from a full fat guide into a hybrid discovery stub: a safe CLI resolver, an `orca skills get orca-cli` pointer, and a bounded read-only fallback for pre-guide binaries. The version-matched command reference now lives only in the Orca binary (embedded guide table, served by `orca skills get`), so the distributed file can no longer drift from the binary that runs the commands. - generator projects STUB_TOPICS from skill-stubs/<name>.md, reusing the guide's own frontmatter so the routing/description surface is unchanged; the embedded full guide (bundled-skill-guides.ts) is untouched. - manifest regenerated: orca-cli releaseRevision 32->33 as an append-only snapshot; existing fat installs classify `outdated` and get the targeted `npx skills update` nudge (no in-app writes). - tests: command-guidance assertions repointed to the guide source (their home now), plus stub-projection + safety coverage. Only orca-cli converts; the other skills stay fat. Per notes/skill-freshness-design.md, the E.3 pointer-compliance spike and the E.5 RC window remain before any further thinning. allowed-tools is intentionally not added yet (frontmatter kept byte-identical to the guide). * fix(skills): distinguish guide lookup failures * chore(skills): refresh released skill mapping
…tablyai#9501) * feat(codex): backfill managed-home sessions into the real Codex home once per host Orca-launched Codex sessions currently land only in the Orca-managed runtime home, so the user's own `codex resume` picker and app history never see them (stablyai#4444, stablyai#8612). Backfill the managed sessions tree into the real ~/.codex/sessions/YYYY/MM/DD layout once per host: - hardlink first (one physical rollout log), copy as the cross-volume fallback; existing target files are always skipped, nothing in either home is deleted or moved - idempotent; per-file failures leave the completion marker unset so the next startup retries cheaply - JSONL audit log of every link/copy/failure under <userData>/codex-session-backfill/ - honors the custom Codex session source home override, mirroring the existing system->managed bridge WSL managed homes are distro-local and need an in-distro variant; that is a follow-up. * feat(codex): flag-gated system-default real-home routing scaffolding 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 stablyai#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 (stablyai#8606) for the native-home routing direction. Depends on the codex trust-rpc-grant plumbing for the real-home hook installer. * fix(codex): strip the daemon-inherited Orca CODEX_HOME override for real-home routing The daemon spawns PTYs from its own inherited environment and honors only spawnOptions.envToDelete, so mutating the sparse env object was not enough to strip an Orca-owned CODEX_HOME the daemon already carries. Add the strip to envToDelete for both daemon host-spawn paths, preserving a user-set CODEX_HOME. Verified live via CDP against a sandboxed dev instance (flag ON): an Orca-spawned pane reports empty CODEX_HOME/ORCA_CODEX_HOME, so Codex resolves its own ~/.codex. Adds daemon-path unit coverage (strip Orca-owned, preserve user-owned, no-op when flag OFF). * fix(codex): harden one-time session backfill * test(codex): cover staged cross-volume install * feat(codex): app-server trust-grant client, capability cache, and grant ledger Short-lived codex app-server JSON-RPC client (hooks/list + config/batchWrite, the same pair the Codex TUI 'Trust all' flow calls), run in a bundled ELECTRON_RUN_AS_NODE entry so synchronous launch prep can block on it with a hard deadline and guaranteed child reap. Capability cache modeled on GitCapabilityCache, scoped per execution host (native vs each WSL distro), with a narrow unknown-method/missing-subcommand unsupported predicate. The grant ledger records verified grants so steady-state launches skip the RPC. * fix(codex): grant managed hook trust via codex app-server RPCs in install/refresh 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. * test(codex): cover app-server trust grant client, cache, ledger, and lanes * test(codex): cover commit-message real-home override strip/preserve Adds the two cases for the headless commit-message Codex env under real-home routing: a nested-Orca-inherited Orca-owned CODEX_HOME is stripped, and a user-owned CODEX_HOME is preserved. * test(codex): WSL grant-lane coverage — in-distro invocation and fallback parity * feat(codex): real-home hook installer trusted via the codex app-server grant client With the real-home flag ON and the system-default selection, install Orca's status hook into the user's real ~/.codex before any pane spawns: - entry APPENDED LAST per managed event: codex hook trust keys are positional (source:event:group:handler), so appending keeps every user entry's position and trust record intact; user entries and unknown top-level hooks.json fields are preserved verbatim - trust is granted exclusively through the codex app-server client (hooks/list + config/batchWrite, verified by re-list); Orca never writes [hooks.state] into the user's real config.toml itself - if the grant lane is unavailable (old binary, unsupported RPC, verify failure), the appended entry is rolled back byte-exactly and the host keeps the managed-home lane end to end (PTY env, rate limits, commit messages) via a lane gate on the runtime-home service - one-time pristine backup of the user's hooks.json under Orca's userData; a rolling .bak sits next to the file (existing atomic writer) - hook opt-out sweeps Orca entries from the real home and drops Orca-owned trust records; flag-off downgrade re-arms the existing legacy system-home sweep, which removes the entry and its trust keys cleanly - the legacy system-home sweep is suppressed only while the real-home lane owns ~/.codex/hooks.json, so managed installs cannot delete the entry * fix(codex): resolve the trust-grant entry without requiring electron The grant bridge is reachable from plain-Node CLI entries, where the plain-node entry guard rejects any chunk containing require("electron"). Resolve the bundled session entry from __dirname (root chunk and chunks/ layouts) with an app.asar -> app.asar.unpacked rewrite for packaged runs, instead of electron's app path APIs. * fix(codex): keep session backfill off main thread Use asynchronous, sequential filesystem operations for the one-time rollout backfill, and avoid repeated target-directory probes. Treat inaccessible managed session roots as retryable failures instead of writing a false completion marker. * fix(codex): harden app-server trust grant fallback * fix(codex): install cross-volume session backfill copies atomically On a real Codex home whose filesystem supports no hardlinks (exFAT/FAT, some network mounts), the staged cross-volume copy was installed with a non-atomic copyFile(..., COPYFILE_EXCL) straight into the final rollout-*.jsonl name. An install interrupted mid-copy (app quit, crash, ENOSPC during the deferred run) could strand a truncated rollout that the next run then skips as already-present, defeating the staging design's own guarantee that a failed copy never leaves a partial session behind. Install the fully-staged copy with an atomic rename instead, guarded by an existence re-check so it keeps the never-overwrite contract (and the rename source is the same immutable managed rollout, so any clobber would be byte-identical). Cover the no-hardlink-support target and an interrupted install that must leave no partial in the user's sessions tree. * fix(codex): resolve grant entry from __dirname so plain-node CLI entries stay electron-free The build guard rejects any electron require reachable from plain-node entries; the bridge now maps app.asar to app.asar.unpacked by string replacement instead of consulting electron app paths. CLI typecheck project lists the new trust-grant module graph. * fix(codex): harden trust grant reconciliation * fix(codex): restore trust config permissions on rollback * fix(codex): harden real-home routing cleanup and retries * fix(codex): preserve unicode trust RPC responses * fix(codex): preserve remote env and complete real-home cleanup * fix(codex): preserve real-home lane invariants * test(terminal): isolate replacement idle reset assertion * fix(codex): preserve real-home dotfile links * fix(codex): preserve verified trust grants across launch prep * fix(codex): preserve dangling config symlinks on rollback * fix(codex): don't revoke a just-granted WSL home on a false 'missing' probe The async wsl.exe canonical-path settlement could report the runtime home 'missing' immediately after a verified RPC grant (a false negative — codex had just written and re-listed trust there), which drove the reconciliation 'remove' branch to delete all six granted [hooks.state] tables, leaving a bare [hooks.state] the launching pane read as 'hooks need review'. A 'missing' settlement now revokes only when no successful install ran this generation; a genuinely moved home still resolves to a different path and reinstalls. * test(codex): model codex config/batchWrite faithfully on Windows The grant-lane stub simulated codex by calling Orca's upsertHookTrustEntries, which writes both separator variants for a Windows key (a fallback-lane compat shim real codex never does) — fabricating duplicate tables and whitespace the RPC path never produces, so the byte-stable and no-duplicate assertions failed on win32. Replace it with a single-variant, blank-line-separated writer that matches the real 0.144.x binary's output. * feat(codex): collapse duplicate session listings across Codex roots Backfilled/bridged rollouts are hardlinked into both the real ~/.codex and Orca's managed runtime home, so AI Vault listed each session once per root (stablyai#7521). Dedup candidates by rollout file name pre-parse and parsed sessions by session id post-parse, keeping the canonical root: host real home first (unprefixed resume), then the managed runtime home, then other homes. Applies to local, WSL, and SSH-remote scans. * feat(codex): background sqlite index heal for backfilled sessions Codex's own state-DB metadata backfill is one-shot, so rollouts hardlinked in by Orca's session backfill never become visible to Codex's DB-driven surfaces. Extract the app-server stdio JSONL transport into codex-app-server-session (shared with the trust-grant client) and add a bounded, resumable background pass that drives Codex's lazy indexing via thread/read per backfilled session: recent-first, batched onto one short-lived server per batch with small concurrency, ledger + marker so steady-state startups are a no-op, stop-aware on quit, and capability-aware on CLIs without the app-server surface. * fix(codex): preserve session identity during dedup heal * fix(codex): preserve user trust during real-home cleanup * fix(codex): harden real-home heal boundaries * fix(codex): fail closed on unsafe backfill install * fix: harden real-home hook cleanup * fix(ai-vault): preserve execution boundaries and reap children * fix(codex): narrow app-server unsupported detection * fix(codex): bound user hook trust rebase retries per host The rebase lane ran a codex app-server session on every launch prep while a host was stuck (CLI without app-server support, or keys hooks/list cannot match). Gate the transaction on the shared capability cache and add the same 5-minute transient cooldown the grant lane uses, so sweep and legacy-cleanup retries cost plain fs reads instead of a codex session per pane spawn. * fix(codex): enforce real-home resume and heal boundaries * fix(codex): establish real-home lane before cleanup * fix(codex): stop index heal before delayed spawn * fix(codex): protect symlinked rolling backups * fix(ai-vault): preserve resume env deletion through drag * fix(codex): strip inherited Codex homes on mobile real-home resume The mobile resume surface types a bare real-home codex resume into a freshly created pane, but never asked for CODEX_HOME/ORCA_CODEX_HOME deletion at pane spawn, so an agentDefaultEnv-pinned or daemon-inherited Codex home rerouted the resume away from the user's real ~/.codex while the same session resumed correctly on desktop. Share the deletion helper from the AI Vault resume builders and forward it through the mobile launch and session.tabs.createTerminal call. * fix(codex): gate session migration on real-home lane * fix(codex): stop session backfill after opt-out * fix(codex): keep session heal failures retryable * fix(codex): keep session migration state recoverable * fix(codex): retry republished missing session heals * fix(codex): preserve hook symlink trust path * fix(codex): disambiguate POSIX trust paths * fix(codex): align hook trust source paths * fix(codex): harden trust grant lifecycle * fix(codex): restore envToDelete on client invocation type after base reconcile * test(codex): type child.stdout as PassThrough for oversized-output write * Assemble RC: reconcile app-server transport API across PRs Unify on the object RPC surface from the index-heal transport (stablyai#8921) while preserving the default-home env strip (stablyai#8828) and the narrowed missing-app-server capability signal (stablyai#8847): adapt the user-hook-trust-rebase consumer + tests, port envToDelete stripping into the shared session, and route stderr classification through the canonical capability-signal module. * RC: enable system-default real-home routing by default (flag ON) Flip codexSystemDefaultRealHomeEnabled to default ON for this RC's staged rollout (a user can still opt out by setting it false, which stays byte-identical to managed-home behavior). This is the only intended behavior difference between the RC branch and the individual PRs. Updates the two tests that assumed the prior OFF default. * fix(codex): snapshot hooks.json bytes+parse in one read to close real-home clobber race The install/sweep/legacy-cleanup paths parsed hooks.json, then did a separate later read to capture the previous bytes for the pre-write generation guard. A concurrent save (second Orca instance or the user editing the file) could land between the parse and that second read and be silently overwritten. readHooksJsonWithRaw returns the raw bytes and parse from a single read so the guard compares against exactly what it parsed. Adds a regression test that mutates hooks.json mid-RPC and asserts the sweep aborts without clobbering. * fix(codex): sanitize managed account config trust * fix(codex): guard OAuth add for custom providers * fix(codex): persist outgoing managed tokens before real-home lane takeover (PR-C) prepareForCodexLaunch returns null early for the real-home / system-default lane before syncForCurrentSelection runs. If a managed account is still recorded as synced when the selection has dropped to the system default (nulled without a sync pass, or auto-deselect on missing managed auth), a Codex-refreshed token stranded in the shared runtime home is never persisted to its canonical per-account home -> token loss. Read the outgoing managed account's refreshed token back before the real home takes over. The real-home lane implies host === null, so running the managed->system-default transition restores only Orca's runtime mirror from ~/.codex and never writes the real ~/.codex. It is a no-op once the selection has already been reconciled, so the normal select path does not double-write. * fix(codex): preserve refreshes across all default transitions * feat(codex): show system-default/real-home account identity in switcher (PR-B) The account switcher modeled the system-default Codex account as activeAccountId:null with no identity fields, so the null row rendered blank ("System default" / generic subtitle) even though its effective login is whatever ~/.codex/auth.json currently is. Add a CodexSystemDefaultIdentity descriptor {hasAuth, authKind, email, providerAccountId, workspaceLabel} to CodexRateLimitAccountsState, resolved live and READ-ONLY from ~/.codex by the accounts service and returned from listAccounts()/getSnapshot(). The settings switcher now renders the null (system-default) row as that real identity: the OAuth email when signed in, "Custom provider — no usage tracked." for env-key/custom-provider logins (auth.json with OPENAI_API_KEY, or an OPENAI_API_KEY env with no auth.json), and the generic fallback when signed out. Identity is host-scoped (per-distro WSL keeps the generic label). Orca never writes ~/.codex; managed-account switches only touch Orca-owned homes, so the system-default identity stays a stable, displayed source of truth. Usage already routes to the real home via getSystemCodexHomePath, so the switcher now attributes it to a real face. Tests (sandboxed temp homes only): OAuth email/provider resolution, api-key auth.json and env-key (no auth.json) as custom-provider, signed-out, and select/deselect of a managed account never mutating ~/.codex/auth.json. * fix(codex): parse multiline provider pins in OAuth guard * fix(codex): harden managed trust sanitization * fix(codex): harden system-default identity rendering * feat(codex): give each managed account a self-contained CODEX_HOME; retire shared mirror (PR-E) With the real-home flag ON, a host managed account now launches directly against its own codex-accounts/<id>/home instead of the shared runtime mirror + auth.json hot-swap: - codex-home-paths: syncSystemCodexResourcesIntoManagedHome links system resources into any managed home (ownership-marker discipline; never symlinks into / mutates ~/.codex). - runtime-home-service: prepareForCodexLaunch / prepareForRateLimitFetch / syncForCurrentSelection route the per-account home directly and skip the shared-home hot-swap + token read-back; each home keeps its own auth in place (fixes GAP-5 concurrent auth race). Session discovery scans every per-account home. - hook-service / hook-trust-promotion: install/getStatus/refresh accept a runtimeHomePath so hooks + RPC-granted trust land in the per-account home. - service: config mirror into a self-contained home uses the trust- preserving merge so granted hook/project trust survives account switches. - codex-session-root-dedup: rank codex-accounts/<id>/home as canonical managed alongside the shared runtime home. Flag-OFF and the system-default real-home (null) lane are unchanged; the nested-Orca CODEX_HOME===ORCA_CODEX_HOME daemon strip (stablyai#5370) is preserved. Sandboxed tests only; ~/.codex is never mutated. * fix(codex): validate per-account home ownership * fix(codex): keep managed rollouts discoverable across real-home opt-out WI-4 lossless migration/rollback validation for pre-E shared-mirror managed accounts. Session discovery gated the per-account home scan on the real-home flag, so opting back out (flag OFF) hid every rollout an account accumulated while the flag was ON — the data stayed on disk but vanished from the AI Vault until the flag flipped back on. Scan a managed host home whenever it holds a sessions/ tree, independent of the flag; a never-enabled install keeps its homes credential-only so opt-out stays byte-identical to today. Forward migration was already lossless (the shared mirror is always scanned) and the opt-out credential read-back already refuses to overwrite a fresher per-account token; add tests locking all three invariants. Sandboxed tests only; ~/.codex is never touched. * fix(codex): migrate stranded shared auth on E takeover * test(e2e): isolate Electron from developer Codex home * test(codex): add real-account validation harness * fix(codex): finish C and E matcher composition * fix(codex): bound validation harness shutdown * test(codex): isolate hook lifecycle user data * test(codex): cover realistic account-home migration * fix(codex): keep standalone home tripwire active * test(codex): fingerprint system auth in validation reports * fix(codex): bind managed homes to account ownership * fix(codex): normalize Windows trust source identity * fix(codex): make Windows trust upgrade transactional * test(codex): use TypeScript pipeline for validation scripts * test(codex): run validation modules through native node * test(codex): allow slow Windows tripwire startup * fix(codex): survive lingering Windows codex login processes in add-account On Windows, codex login can keep running (with descendants) after it has written auth.json, holding OS handles on the per-account managed home (log/codex-login.log). That made doAddAccount's post-login cleanup fail with ENOTEMPTY (rmSync) and left an orphaned codex-accounts/<id>/home. - runCodexLogin now watches for auth.json on Windows and force-kills the login process tree (taskkill /t) if it lingers past a short grace period; the forced exit is treated as a successful login. The 120s timeout path also kills the whole tree instead of only the direct child. macOS/Linux behavior is unchanged. - safeRemoveManagedHome now removes homes with rmSync maxRetries / retryDelay (mirroring the local-worktree-filesystem Windows policy) and no longer lets a cleanup failure mask the original add error. - run-codex-real-account-validation.mjs accepts --temp-parent / ORCA_CODEX_VALIDATION_TEMP_PARENT so the disposable root can live outside %USERPROFILE% on Windows, and fails with an actionable message before creating anything when the temp parent is inside the primary home. The real-home guard is unchanged. * fix(codex): preserve managed-account MCP .credentials.json on per-account-home migration (stablyai#8440) Codex file-mode MCP OAuth tokens live in $CODEX_HOME/.credentials.json, keyed by MCP server URL with no account identity of their own. The legacy shared-mirror -> per-account-home migration only carried auth.json, so an existing managed account with authed MCP servers had its tokens stranded on upgrade and silently needed re-auth. Carry the shared mirror's .credentials.json into the same identity-proven per-account home alongside auth.json: only into the single uniquely-matched active account (no cross-account leak), only when the destination has none yet (never clobber a newer file the account authed in its own home), atomic 0600, absent-source no-op. New MCP auth already lands in the per-account home since that home is CODEX_HOME. * fix(codex): preserve Windows reauthentication login flow * test(codex): build real-account validation harness cross-platform on Windows The harness built its app with execFileSync('npx', ['electron-vite', ...]), but npx resolves to a .cmd shim on Windows that execFileSync cannot launch (ENOENT), so the harness could not build its own app there and required --skip-build with a prebuilt out/main/index.js. Extract resolveElectronViteBuildCommand(repoRoot): it runs the repository-local electron-vite JS entry (node_modules/electron-vite/bin/electron-vite.js) with the current Node binary (process.execPath), which resolves identically on macOS, Linux, and Windows with no shell. It throws a clear error if the local entry is missing (install deps or pass --skip-build). --skip-build behavior is unchanged. Add regression coverage asserting the build command uses process.execPath and the repo-local JS entry (not npx), and that a missing entry fails clearly. * fix(codex): version the MCP creds migration independently of the auth marker The auth carry and the MCP .credentials.json carry (stablyai#8440) shared one existence-only v1 marker, so any build that stamped the auth-only marker first would strand the MCP store forever. The MCP carry now concludes via its own per-account-mcp-creds-migration-v1.json marker and runs even when the auth marker is already present; ordering is code-enforced instead of landing-discipline-enforced. Also isolate per-account read failures: one stale or deleted account home no longer aborts the whole migration. The broken account stays in the unique-identity ambiguity gate via its stored fields but is never read or written, so the active account still migrates. * fix(codex): fail corrupt managed auth.json without echoing credential bytes A raw JSON.parse SyntaxError from loadOAuthCredentials could carry auth file fragments into logs and the add/reauth error surface. Throw a sanitized error instead; filesystem errors still propagate unchanged. * fix(mobile): give the pairing runtime a disposable home for the E2E boot guard The main-process guard now refuses to start with ORCA_E2E_USER_DATA_DIR set but the real user home, and this was the one caller not updated — the temporary pairing runtime crashed before emitting its pairing URL. * test(codex): canonicalize harness containment guards and retry cleanup Resolve symlinks before the disposable-root containment checks so a symlinked temp parent cannot smuggle the throwaway home inside the primary home, and give the final cleanup rm Windows retry/force so a briefly lingering codex handle cannot strand the credential-bearing root. * test(codex): add lane-aware containment mode to the real-account harness The Windows gate-D run proved strict zero-event whole-profile containment is structurally unreachable with the real-home flag ON: system-default spawn sites deliberately delete CODEX_HOME so native codex resolves the real ~/.codex, and on Windows the binary ignores the USERPROFILE sandbox. Its own volatile runtime churn (root sqlite/WAL/SHM, tmp/, log/) is the shipped Phase-1 design, not a candidate defect. --lane-aware-containment records those designed events without aborting while every other real-home write — auth.json, config.toml, .credentials.json, hooks.json, sessions/, anything unknown — remains a hard violation and still aborts the run. Default behavior is unchanged (strict); the absolute zero-event claim stays carried by macOS runs, where HOME does sandbox native codex. * test(codex): allow the real-account harness to pin the real-home flag off --system-default-real-home off seeds and env-pins the flag OFF so every codex spawn gets an explicit managed CODEX_HOME and native codex never resolves the OS profile. This is the only Windows configuration where the strict zero-event whole-profile tripwire is reachable, and it matches the stable-rollout default; flag-ON runs keep lane-aware classification. * test(codex): correct the flag-off harness comment to kill-switch rationale The rollout ships all codex-home changes at once (no phased rollout), so flag OFF is the emergency kill-switch lane, not the stable default. * test(e2e): canonicalize the isolated E2E home path The disposable HOME lives under os.tmpdir(), whose spelling is an alias on CI (macOS /var symlink, Windows 8.3 RUNNER~1). Git canonicalizes worktree paths, so worktrees created under the aliased home never matched the app's listing — golden core flows and the packaged crash-survival harness failed with 'worktree created but not found in listing'. Resolve the home to its canonical spelling at creation in both the e2e helper and the packaged-app driver. * fix(codex): address CodeRabbit review on the landing PR - carry envToDelete through the mobile agent-resume startup plan so a real-home Codex resume cannot inherit an ambient CODEX_HOME - strip Orca-owned Codex overrides in the commit-message WSL fallback, matching the host fallback - strip ELECTRON_RUN_AS_NODE in the computer-e2e driver like every other home-isolation caller - drop the unused hooksEnabled parameter from isRealHomeCodexHookLaneUsable * feat(codex): ship real-home routing unconditionally, remove the rollout flag The codexSystemDefaultRealHomeEnabled setting is gone from types and constants and the helper no longer consults settings — the system-default real-home lane and per-account homes ship for everyone in one release. This also un-strands profiles that rc-era builds stamped with false (the setting had no UI, so every stored false was a seeded artifact that would have silently kept those users on the legacy mirror forever). The ORCA_CODEX_SYSTEM_DEFAULT_REAL_HOME env override survives strictly as a test-rig control: the containment harness pins the legacy lane for strict zero-event Windows runs, e2e home isolation pins lanes inside disposable homes, and the legacy-lane test suites now route their per-test lane selection through it. --------- Co-authored-by: OrcaWin <alpha-eng@stably.ai>
…follow-ups) (stablyai#9606) * fix(terminal): harden macOS login-preflight spawn path (stablyai#9301 follow-ups) Follow-ups to the stablyai#9301 two-pass review. Re-derived severities: none are release blockers; these are low-likelihood robustness/observability fixes. F1 macos-tcc-login-shell: only cache a conclusive PAM verdict. A probe our own SIGKILL timeout / ETIMEDOUT / maxBuffer killed proves nothing about PAM, so it no longer sticks the whole process into direct-spawn — it fails open for that spawn, re-probes next time, and self-heals once PAM answers. F2 daemon-entry: prepareMacosTccLoginShell returns the outcome; the daemon emits a structured macos-login-preflight {ok, reason} log on degrade, since detached daemons destroy stderr and lose the console.warn. F3 local-pty-provider: an atomic guard before the tracking write coalesces a concurrent same-session-id spawn onto the winner instead of orphaning an untracked, unkillable PTY. F4 daemon-server: preparations carry their clientId; a control-socket close cancels only that client's pending preps, so a mid-preflight disconnect no longer leaves a durable orphan daemon PTY. F7 warm the PAM probe at daemon startup (mirrors warmPwshAvailabilityCache); note the pipe-vs-PTY probe fidelity limit; test that kill with no pending prep still surfaces SessionNotFoundError. Deliberately skipped F5 (self-limits to next reload; a real fix needs speculative in-flight-generation tracking) and F6 (inherent to legacy v22 attach; self-heals once daemons are v23). * fix(terminal): bound preflight recovery work * fix(terminal): cancel preflight when daemon stream closes
…yai#9489) A busy PTY delivers up to ~200 terminal frames/s to the phone (the desktop coalesces output at a 5ms window), and each frame paid a full RN-bridge + WebView postMessage + WebKit IPC + xterm write + paint pipeline. Coalesce stream writes in the RN layer: leading-edge immediate delivery keeps keystroke echo instant, and sustained streams batch into at most ~21 WebView messages/s (48ms trailing window). Measured (iOS Simulator A/B at ~200 lines/s, only this file flipped): terminal WebContent CPU 8.0% -> 2.8%, app process 19.3% -> 15.2%, combined continuous CPU -34%. Ordering boundaries preserve today's semantics: resize/reflow flush pending bytes first; init/clear drop superseded pre-snapshot bytes; reload/content-process-termination/unmount clear the buffer. The notification-dispatch extraction from TerminalWebView is a verbatim move forced by the max-lines cap.
* fix(source-control): keep huge change sets responsive * Fix cancellation and retry handling for capped status * Harden capped status for conflict-heavy repositories * Harden capped status recovery and cancellation * fix(source-control): preserve capped status correctness * fix(source-control): translate submodule status at render time --------- Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
…anges (stablyai#7770) * Keep Jira linked work items attached when the composer project changes Repo/project switches in the new-workspace composer cleared every linked work item except Linear, so starting a workspace from a Jira task and then picking the actual implementation project silently dropped the ticket link. Route all three switch paths through a shared isRepoScopedLinkedWorkItem predicate: GitHub/GitLab sources stay repo-scoped and clear on a switch, Linear/Jira issues stay attached. * test(new-workspace): drop dead isLinearLinkedWorkItem, harden preserve-predicate coverage Follow-up to the Jira-preservation fix (review findings): - Remove isLinearLinkedWorkItem: no production consumer remains after all three composer switch paths route through shouldPreserveWorkspaceSourceOnRepoChange. - Pin the clear cases in workspace-source.test.ts (GitLab explicit + inferred, null) that both delegating paths depend on, not just GitHub. Co-authored-by: Orca <help@stably.ai> --------- Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com> Co-authored-by: Orca <help@stably.ai>
* feat(codex): drop "Not now" collapse from account-switch notice The Codex account-switch overlay had a "Not now" button that collapsed the loud dialog into a corner chip via component-local state. Remove the button and all of its collapse state: the useState/useEffect, the CollapsedRestartChip, and the now-dead collapse helpers. buildCodexRestartNoticeKey still backs the focus effect, so the module is renamed codex-restart-overlay-collapse.ts -> codex-restart-notice-key.ts and its tests trimmed to match. The orphaned "Not now" i18n key is pruned from all locale catalogs. * test(codex): lock restart notice actions
…-render churn (stablyai#9615) The UsagePercentageDisplayChangeNotice callout re-measures its fixed anchor on every ResizeObserver/resize delivery and calls setAnchorPosition with a fresh object each time. Equal-geometry deliveries (the common case) still churned a re-render apiece because the object identity changed. Bail when bottom/left are unchanged so React short-circuits before commit. Measured: 20 identical-geometry deliveries → 20 re-renders before, 1 after. This is defensive robustness/perf hardening for the status-bar subtree that surfaced in crash f75ed81b (React stablyai#185 in overlay.status-bar). It is NOT a proven fix for that crash — the reproduced stablyai#185 loop lives in radix Popper internals and is not unit-reproducible; this removes one churn source feeding that subtree.
Resolved by fork-sync agent; see report.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Upstream
58293445emerged into the fork with conflicts.Unresolved markers remain in: src/renderer/src/i18n/locales/en.json, src/renderer/src/i18n/locales/es.json, src/renderer/src/i18n/locales/ja.json, src/renderer/src/i18n/locales/ko.json, src/renderer/src/i18n/locales/zh.json
Fork files upstream also touched: package.json, pnpm-lock.yaml, src/main/github/client.ts, src/main/ipc/github.test.ts, src/main/ipc/github.ts, src/main/runtime/orca-runtime.ts, src/main/runtime/rpc/methods/github.ts, src/main/runtime/runtime-rpc.ts, src/preload/api-types.ts, src/preload/index.ts, src/renderer/src/assets/rich-markdown-editor.css, src/renderer/src/components/editor/EditorContent.tsx, src/renderer/src/components/right-sidebar/SourceControl.tsx, src/renderer/src/i18n/locales/en.json, src/renderer/src/i18n/locales/es.json, src/renderer/src/i18n/locales/ja.json, src/renderer/src/i18n/locales/ko.json, src/renderer/src/i18n/locales/zh.json, src/renderer/src/lib/language-detect.ts, src/renderer/src/store/slices/diffComments.test.ts, src/renderer/src/store/slices/diffComments.ts, src/renderer/src/web/web-preload-api.test.ts, src/renderer/src/web/web-preload-api.ts, src/shared/types.ts
Conflict resolution report — upstream merge
58293445eMerge of
origin/main(upstreamstablyai/orca) into fork branchsync/upstream-58293445e. Five conflicted files, all i18n locale JSON.Every conflict is the same shape and was resolved the same way: a pure
key union.
Summary
All five conflicts sit inside the
SourceControlcomponent namespace object(in
en.jsonit opens at line 9529 and closes just before"SourceControlAgentActionDialog"). Both branches appended new keys to thatsame object at the same spot — immediately after the sub-object that ends with
the
"783a808870"("Close") key — so Git could not tell the two insertionsapart and flagged a textual conflict, even though the changes are semantically
independent.
Fork side (HEAD) added two keys for its tldraw canvas feature:
"5c951f3e68": "Canvas""897d7c622d": "Canvas"Per CLAUDE.md ("Что добавляет форк: tldraw-канвасы"), the fork turns
.tldrfiles into a first-class document type with viewer, canvas notes, and
canvas→issue flows. These
"Canvas"labels are part of that feature. Thefork intentionally left them untranslated ("Canvas" verbatim) in every
locale, including es/ja/ko/zh.
Upstream side (origin/main) added one key:
"97e7124eac"→ "Could not refresh Source Control. Try again."(localized per file), a new Source Control refresh-failure message.
The two sets of keys are disjoint (
5c951f3e68,897d7c622d,97e7124eacare all distinct), and neither key appears elsewhere in the object, so merging
them is a lossless union.
Resolution
For each file I kept all three keys, ordering the fork's two
"Canvas"keys first (each with a trailing comma) followed by upstream's key (no trailing
comma, since it is now the last entry before the object's closing brace). This
preserves both upstream's new string and the fork's tldraw canvas labels.
Resulting block (values differ per locale):
Per-file upstream value kept:
"97e7124eac"valueCould not refresh Source Control. Try again.No se pudo actualizar Source Control. Vuelve a intentarlo.Source Control を更新できませんでした。もう一度お試しください。Source Control을 새로 고칠 수 없습니다. 다시 시도하세요.无法刷新 Source Control。请重试。The fork's
"5c951f3e68"/"897d7c622d"values are"Canvas"in all fivefiles (untranslated), matching the fork's pre-merge state.
Verification
grepandgit diff --checkboth clean).en.json— no duplicate-keycollision was introduced by the union.
What a human must still decide
Nothing blocking. One optional follow-up, not a merge decision:
"Canvas"keys untranslated in es/ja/ko/zh. That isthe fork's existing choice (the merge preserved it, not introduced it); if the
fork later wants localized canvas labels, that is a separate task independent
of this sync.
VERDICT: clean