[upstream #12823] fix(git): allow bounded override of worktree-add timeout - #143
Closed
innocarpe wants to merge 1687 commits into
Closed
[upstream #12823] fix(git): allow bounded override of worktree-add timeout#143innocarpe wants to merge 1687 commits into
innocarpe wants to merge 1687 commits into
Conversation
* fix(claude): hide managed hook consoles on Windows * fix(claude): harden managed hook invocation --------- Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
* fix: separate WSL skill copy and setup commands * fix: keep repair fallback on Windows host * fix: pin skill setup terminal runtime * fix: align skill setup retries with runtime --------- Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
* fix(cli): preserve WSL --deps quotes and parse task ids strictly PowerShell 5.1 native splat was stripping ASCII double quotes on the WSL bridge, so non-empty JSON --deps arrays failed while [] still worked. Pre-escape quotes before launching orca.exe, and recover quote-stripped task-id arrays while rejecting non-task-id and malformed CSV input (stablyai#12188). * fix(orchestration): narrow WSL deps recovery * fix(wsl): forward native CLI arguments losslessly * ci(windows): exercise WSL PowerShell argv boundary --------- Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
Forward-slash //wsl.localhost links opened as a different path string than the Files sidebar's backslash UNC, so file watchers did not refresh the editor. Canonicalize WSL UNC to \\wsl.localhost\... at map time. Fixes stablyai#13349
…et error (stablyai#13062) * fix(terminal): explain a dead terminal host instead of leaking a socket error When the daemon that owned a session has exited, resuming that terminal surfaced the raw connect error for its endpoint — on Windows a named pipe path, since a pipe disappears with its server process — in a red toast ending in "file an issue". The session is unrecoverable and the text is unactionable. Translate it at the daemon boundary into terminal_host_gone, following the existing terminal_pane_owner_unverified path, and humanize it in the toast. Suppress the issue link for a condition Orca can fully explain. Covers the remote-host case: the conversion sits in attachStablePaneOwner, the first catch a paired client's resume reaches before the error is serialized onto the wire. * fix(terminal): preserve host-gone errors across versions * fix(terminal): scope host-gone error matching * refactor(terminal): derive the host-gone test and replace forms from one source Two regex literals matched the same token with subtly different shapes (capturing vs non-capturing, consuming vs lookahead), so an edit to one could silently drift from the other. Build both from a single source string; the test form stays non-global so it carries no lastIndex state between calls. * test(terminal): cover host-gone regex boundaries * test(terminal): isolate leading host-gone boundaries
…3372) * fix(terminal): retain WebGL for recently hidden worktrees Suspending a hidden worktree disposes every pane's WebGL addon, so every switch-back presents DOM-renderer frames (unfloored ~5% wider advance, and the channel through which any poisoned cell metrics reach the screen) until reattach completes — 0.5-3s on loaded sessions. Keep the live addons for the most recently hidden worktrees instead: LRU capped at 6 contexts (Chromium allows ~16/page, visible worktrees use 1-4), least-recent evicted to dispose exactly as before. Reveal then has no renderer swap and nothing to flash. Window-occlusion callers pass no retention context and are unchanged. * fix(terminal): pause retained hidden cursor work * fix(terminal): preserve retained WebGL through deferred rebuilds
…ests (stablyai#5832) * feat(bitbucket): connect Bitbucket from Settings with encrypted credential storage Bitbucket Cloud was the only review provider with no in-app auth: GitHub and GitLab delegate to the gh/glab CLIs, but Bitbucket has no comparable first-party CLI, so the only option was ORCA_BITBUCKET_* env vars plus a restart (discussion stablyai#5364). Adds a Connect/Edit/Disconnect flow on the Bitbucket integration card, modeled on Linear and Jira: - Credentials are verified against /user before they are persisted, so a dead token is rejected inline instead of silently stored. - The secret is encrypted with safeStorage (0600 plaintext fallback when no OS keyring); non-secret metadata lives in a separate plaintext file so status reads render the connected account without decrypting. Opening Settings therefore never triggers a keychain prompt. - Env vars keep precedence over stored credentials, so existing headless and SSH setups are unaffected. Env-managed connections hide Disconnect. - connect/disconnect reset the preflight cache, so no relaunch is needed. The Bitbucket card moves to its own file to stay under the tsx max-lines cap. * feat(bitbucket): support creating pull requests from Orca Bitbucket was the only configured provider whose Create button reported "This repository provider does not support creating a pull request from Orca" — supportsReviewCreation was false and the forge provider had no createReview, so even a correctly authenticated setup was blocked. Adds createBitbucketPullRequest against POST /repositories/{ws}/{repo}/ pullrequests, using the same env-first / stored-credential resolution as PR lookups (extracted into resolve-auth.ts so both share one path). Bitbucket Cloud has no draft pull requests, so a draft request is rejected with a clear message rather than silently publishing a live PR. * fix(bitbucket): hide the draft toggle where drafts do not exist, plus review fixes Bitbucket Cloud has no draft pull requests, so the composer no longer offers the toggle for it and forces the flag off at submit — better than failing after the user has filled the form in. Review fixes: - writeFileSync's `mode` only applies when it creates the file, so rewriting a credential kept whatever permissions it already had. chmod after every write, for the secret and the metadata. - An explicit ORCA_BITBUCKET_API_BASE_URL now wins over a stored base URL. Env precedence is per-setting, not all-or-nothing. - Enter in the credentials dialog only submits from a text field, so it no longer hijacks Cancel and the docs link. - Replace the chmod-based delete-failure test with a mocked unlinkSync: file modes are not portable to Windows and elevated runners unlink anyway. * fix(bitbucket): stop a merged pull request from blocking the branch's next one Reported on stablyai#5832: with a merged PR on a branch, Create reported "Pull request already exists" and offered no way forward. The branch lookup queries every PR state and returns the most recently updated one, so a merged PR came back as the branch's current review and eligibility blocked on it. Bitbucket only discarded such a match on the repo default branch (stablyai#9171), while GitHub already drops any merged PR it matched by branch alone — "a merged PR without an explicit link is just a historical branch match, not implicit review context". Applies that rule to Bitbucket. An explicitly linked review still resolves through the linked-number fallback, so merging a PR Orca knows about keeps showing it. * fix(bitbucket): add bitbucket to the shared review-creation provider list Reported on stablyai#5832: on a Bitbucket repo with no existing PR, Create still said "This repository provider does not support creating a pull request from Orca", even after the forge provider gained createReview. There are two capability lists. Enabling supportsReviewCreation on the forge provider was necessary but not sufficient — the blocker and the whole renderer read the separate shared list, which never included bitbucket. Adds it, gives Bitbucket its own provider name so review copy stops saying "GitHub", and asserts the two lists agree so they cannot drift apart again. * fix(bitbucket): persist pull request links after creation * fix(bitbucket): fetch linked pull requests by number first * fix(i18n): use generated Bitbucket integration keys * test(bitbucket): cover forge creation delegation * fix(bitbucket): fall back when linked pull request is stale * docs(bitbucket): explain notFoundIsNull and fix a garbled permissions comment notFoundIsNull arrived without the rationale its sibling flag carries, and reads as a bare `true` at the only call site that opts in. * fix(bitbucket): address review findings before merge Two of these made the feature unusable in real setups: - Create PR checked GitHub authentication for Bitbucket. isProviderAuthenticated fell through to isGitHubAuthenticated, which was unreachable while Bitbucket could not create reviews at all. Anyone with Bitbucket connected but no `gh auth login` got auth_required with no way forward. - The draft flag was only gated in ChecksPanel, not the two SourceControl call sites. With "create as draft" saved as a default, the composer hides the toggle for Bitbucket, so the flag could not be cleared and creation failed every time. Bitbucket now ignores draft instead of rejecting it. Also: - Blocked-create copy said "GitHub is not authenticated. Run gh auth login" on Bitbucket repos, in both the main-process and renderer paths. - A decryption failure resolved to an anonymous config and queried anyway; a private repo answers 404, which reads as "no pull request" and offers Create for a branch that already has one. Requests now fail closed. - Hiding non-open implicit branch matches was too broad: a declined PR became permanently invisible off the default branch. Scoped to merged, restoring the default-branch rule (stablyai#9171) for the rest. - A failed disconnect rejected unhandled and the card silently re-rendered as connected; a partial delete left the secret live in memory for the session. - The credentials dialog refused to open on a remote runtime, so a local repo could never store a credential. Now only the storage note changes, matching the Jira dialog. --------- Co-authored-by: devatnull <59279509+devatnull@users.noreply.github.com>
Adds GitHub stacked pull request creation: a contextual "Stack this PR above #N" option that appears only when the selected base branch has an open PR, plus the main-process stack preflight and registration. Also reworks the create-review composer for cohesion: shadcn Checkbox and Label primitives, base label above a full-width searchable combobox with attached results, keyboard navigation, and a unified field skin, spacing and typography scale. Verified end to end against real GitHub: extending an existing stack and creating a new one.
…lyai#13864) * feat(sidebar): add jump-to-top button for hard upward scrolling Detect intentional hard scroll-up gestures (wheel or scrollbar drag) and offer a one-click jump-to-top affordance. Auto-hide after idle to avoid persistent visual clutter. Addresses the common case of fast navigation through long worktree lists ranked by agent activity. * test(sidebar): improve scroll-to-top detection for active gestures Only detect velocity from active scrollbar drag or touch, not programmatic scrolls. Return focus to list after jump-to-top. Add comprehensive hook tests with gesture simulation. Improve cumulative down-delta tracking for dismissal. * test(sidebar): add scroll-to-top gesture detection tests Add comprehensive test coverage for the hard-upward-scroll detection hook, verifying idle timer behavior, gesture suppression, scrollability checks, and cleanup on unmount. Extract the post-jump suppression window into a named constant for maintainability.
…ai#13545) * fix(windows): refresh persisted PATH ordering New terminals now preserve Orca-injected entries while adopting current machine and user PATH precedence, so newly installed tools are not shadowed by stale aliases. Invalidate the cached registry snapshot on Windows setting changes and guard in-flight refreshes from restoring stale cache data. * fix(windows): retry invalidated PATH reads * fix(windows): reuse current PATH refresh * fix(windows): preserve per-hive PATH fallback state --------- Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
* fix(claude): guard cold-restore resume selectors Persisted Claude default args or a custom command can carry their own --resume/-r/--continue/-c selectors (a bare picker default or a stale id). Cold restore appended the authoritative --resume <id> after them, typing a command with competing selectors into the restored pane (stablyai#12982). buildAgentResumeStartupPlan now routes Claude through a selector guard that tokenizes the base with the existing startup tokenizer, strips selectors in option position only (value-taking options keep dash-leading values), and appends exactly one authoritative selector, inserting before Claude's own -- terminator when present. Splicing is span-based so untouched bytes stay verbatim, wrapper commands are left alone, and any tokenization failure falls back to the previous append-only behavior. Launch paths, other agents, persistence, and the wire are unchanged. * fix(claude): harden resume selector guard against false matches Round-1 review findings: locate the claude executable by command position (index 0, after a wrapper --, or behind NAME=value assignments) so an argument merely ending in /claude can never be mistaken for it; stop matching the joined -r<id> form, which was ambiguous with dash-leading option values and forced an unmaintainable arity table (now deleted). Ambiguous shapes degrade to the pre-guard append-only behavior. * fix(claude): fail resume guard open on chained shell syntax Round-2 review findings: an unquoted operator or newline after the claude token means the base chains other commands, and splicing across that boundary handed the selector to the wrong command — detect it and fall back to plain appending. Also recognize claude behind PowerShell's & call operator, decouple the test oracle from the implementation's selector predicate, add Windows tokenizer span tests, and rename the module after its public API. * fix(claude): flag bare shell operators inside the tokenizers Round-3 review findings: the guard's operator scan compared raw source to token value, so one quote or escape anywhere in a token hid a shell-active operator outside the quotes and the splice crossed a live command boundary, losing the resume entirely. Both tokenizers now flag tokens carrying an unquoted, unescaped operator byte (or a word-leading # comment on posix/powershell) on their spans, where quote state actually lives, and the guard fails open on that flag. Also strengthens the redirect fail-open test to carry a stale selector, re-tokenizes each raw span in the shell span tests, and documents agent-resume-argv-drop as codex-only. * fix(claude): flag expansions and clamp separator backoff Round-4 review findings: unquoted multi-token expansions (backtick, $(, ${) split across whitespace, so removing only the recognized selector token left a broken construct tail — both tokenizers now raise the span flag (renamed bareShellSyntax) for those openers, on cmd also for operators between single quotes, which cmd does not treat as quoting. The separator backoff is clamped to the previous token's span end so a token ending in an escaped space can no longer donate its escape to the appended selector. * fix(claude): treat cmd single-quoted regions as unmodelable Round-5 review finding: cmd.exe has no single-quote syntax, so the Windows tokenizer's grouping of a single-quoted region diverges from what cmd parses — literal argv like 'claude ...--resume... old' was being read as a real selector and stripped, and a literal '--' as claude's terminator. Flag any cmd single-quoted token as bareShellSyntax so the guard fails open. * fix(claude): flag quoted expansions and scope assignment prefixes Round-6 review findings: the span flag was only evaluated in the unquoted branch, so an expansion opener inside double quotes went unflagged — and inside $(…)/backticks a nested quote re-opens a context this tokenizer does not model, so the splice could cut mid-construct (syntax error, or a silently mutated substitution body). Both tokenizers now flag those, and the flag is renamed divergesFromShell to say what it means. Restrict the NAME=value command-position prefix to posix, where that syntax exists. Drops two branches proven dead. * fix(claude): model shell-literal escapes and scan the whole base Round-7 review findings: (1) the divergence scan started after the claude token, so an expansion opened in a prefix — $(x; npx -- claude --resume s) — had its closer spliced away, producing a base bash cannot parse; it now covers every token including the executable, exempting only PowerShell's leading call operator. (2) posix drops a double-quoted backslash the shell keeps literal, and the Windows escape branch ran inside quoted regions where cmd/PowerShell keep the escape byte literal — both now flagged, so a literal can never be misread as a selector. (3) an unquoted line continuation hid a selector inside a token and skipped the newline gap check. Also removes a third provably dead branch and collapses the cut floor into the cut itself. * fix(claude): flag escapes the tokenizer models but the shell removes Round-8 review findings, all one family — escapes whose token value hides a selector the shell would see: a double-quoted line continuation (bash deletes both bytes), posix $'…'/$"…" quoting, a windows escaped newline, and a trailing unpaired escape. The last one was previously written off as pre-fix-identical, but once stripping happens the dangling escape swallows the separator and no exact --resume reaches claude at all — strictly worse than appending, so it must fail open. Also folds the three gap predicates into one scan. * fix(claude): stop over-flagging a literal dollar sign Round-9 review findings from both lanes: inside double quotes only $( and ${ open an expansion — $' and $" are literal there — and a trailing $ was flagged unconditionally because JS ''.includes('') is true. Both made the guard fail open on modelable bases, leaving the stale selector to compete, so stablyai#12982 went unfixed for them. Separately, cmd strips ^ before the child re-splits on the bare whitespace, so an escaped separator hides two real arguments and must fail open rather than drop one. * fix(claude): fail open on cmd caret-quotes and bare PowerShell syntax Round-10 review findings, both Windows-only (a bash oracle cannot reach them): cmd strips a caret before a quote and the child's parser then reads a bare quote delimiter, so the tokenizer's word boundaries stop matching argv — one case turned a working resume into no resume at all, another let a stale selector survive the splice. And bare (…)/{…} are live PowerShell syntax in argument position, so splicing through them emitted unbalanced output that PowerShell cannot parse. * fix(claude): fail open on the PowerShell stop-parsing token Round-11 review finding: after a bare --%, PowerShell passes the rest of the line to the child literally, so the guard stripped a real selector and then appended quoting that arrives as literal bytes — claude ends up with no exact --resume at all, worse than leaving the stale one. Quoted "--%" and cmd, where the token is ordinary, still splice. * fix(claude): model cmd backslash-escaped quotes Round-11 review finding: an odd run of backslashes before a quote makes it a literal byte to the child's CommandLineToArgvW parser, not a delimiter, so the tokenizer's word boundaries stopped matching argv. Orca manufactures that pattern itself — quoteStartupArg wraps every token in quotes without escaping a trailing backslash — so a pasted Windows path was enough to move the selector into a desynced region and leave claude with no resume flag. Also replaces a caret test case that was byte-identical before and after its own fix, and merges two stacked comment blocks. * fix(claude): fail open on PowerShell double-quoted escape sequences Round-12 finding: PowerShell expands backtick escapes only inside double quotes, so a sequence there produces a token value argv never sees — the guard could strip "-`r" plus the argument after it. Also narrows the stop-parsing comment: a quoted --% can engage stop-parsing before a parameter token, where the base is already mangled either way. * fix(claude): flag PowerShell escape sequences in bare arguments too Round-13 finding: the previous commit gated on quote === '"', but PowerShell's tokenizer calls Backtick() from ScanGenericToken, so it expands these sequences in unquoted arguments as well — bare -`r really is a control character, not -r. The guard read it as a selector and dropped it plus the argument after it. Widening to all PowerShell contexts measures 0 under-flag and 0 over-flag across the full printable matrix; the backtick-escaped-space idiom still splices. Also swaps a test case that was byte-identical with and without its own fix. * fix(claude): drop a token-leading PowerShell backtick before whitespace Round-14 observations, all pre-existing and measured: PowerShell drops a token-leading backtick together with the whitespace after it, emitting no token, so the tokenizer's extra token shifted the locator; and a backtick before a bare CR is a line continuation too. Flagging both takes the lane's 329k-base sweep from 87 bad to 0 with no new failures and the must-splice list byte-unchanged. Also corrects a comment that no longer listed every PowerShell divergence. * docs(claude): correct the bare-CR rationale in the tokenizer comment Round-15 verified against a real PowerShell 7.6.4 engine: a backtick before a bare CR is not a line continuation there — pwsh keeps the CR in the token. The flag stays because 5.1 is unverified and failing open costs nothing, but the comment now says that rather than claiming continuation.
…tablyai#13883) * fix(settings): read voice settings from a ref, not a stale closure updateVoiceSettings wrote from a closure captured at mount, so an async continuation could clobber a newer value. Uses the latest-ref pattern already in Settings.tsx:405-407. Found while investigating voice-microphone-selection.spec.ts:107 and confirmed NOT to be its cause — that failure has no established root cause. The spec gains a hydration barrier and toBeEnabled assertions here, which improve its failure message but do not fix it. Co-authored-by: Orca <help@stably.ai> * test(e2e): drop the voice spec changes from this PR The hydration poll was a no-op: prepareVoiceSettings awaits updateSettings inside the same page.evaluate, so the store is already committed before the poll samples it, in all three tests (the reload precedes it). Worse, its comment asserted that the Select is disabled until hydration — the CI accessibility snapshot at the moment of failure shows the switch checked and the combobox enabled, so that theory is refuted, and a wrong 'why' comment is worse than none. The toBeEnabled assertions go with it: the control was enabled when it hung, so they would not have caught the real failure either. Leaves this PR as what it actually is — a product bug fix. The spec's failure stays open and unexplained. Co-authored-by: Orca <help@stably.ai> * test(settings): type the clear-key mock as its API contract vi.fn(() => clearing) returned Promise<void> where the preload contract is Promise<{ configured: boolean }>. Vitest does not typecheck, so this passed locally while tsc -p tsconfig.tc.web.json failed — it would have gone red in CI's required typecheck job. Co-authored-by: Orca <help@stably.ai> * fix(settings): write the voice-settings ref in an effect, not during render React Doctor's 'Ref mutated during render' rule is blocking in CI's static analysis job, and it is right: React can replay or discard render work, so a render-time ref write can leak from UI that never commits. Moving it to an effect keeps the behavior this fix needs — every writer here (key-status probe, save-key, clear-key) runs post-commit, so the ref is current by the time any of them reads it. Test still discriminates: red without the fix, green with. Co-authored-by: Orca <help@stably.ai> --------- Co-authored-by: Orca <help@stably.ai>
…TA-3811) (stablyai#13667) While the auth document is on screen the WebContents UA is Firefox, so its cross-host subresource/XHR requests (gstatic, play.google.com, the sign-in challenge endpoints) reached the header layer carrying the Firefox UA yet still bearing Chromium client hints, which the else-branch rewrote to Chrome. That paired a Firefox UA with Chrome client hints on every non-auth Google host — a sharper cross-host identity tell than either signal alone, and a plausible cause of the password-submit challenge greying out and stalling. Strip client hints on any request already carrying the Firefox auth UA so the UA and hint surfaces tell one Firefox story for the whole flow. Gated on the same googleAuthOverride flag as the auth-host switch, so imported-native profiles are unaffected and the clean-Chrome default for non-Google sites (Cloudflare) is untouched. Extends tests/tools/google-signin-ua-probe.cjs with app-current/app-fixed modes that mirror the shipped code and log per-request identity; on the real accounts.google.com load they show 18 firefox-ua-with-chrome-hints cross-host mismatches before and 0 after.
… the only Google path (STA-3811) (stablyai#13670) * fix(browser): exclude Google cookies from imports (STA-3811) Imports never write and never remove a google.com-family cookie, on any path. Signing in directly inside Orca is the only Google session that survives, so the live jar always beats anything an import could plant. * fix(browser): roll back selective cookie clears
…#13866) * fix(github): fail closed on unavailable stack metadata * fix(github): validate REST pull request response shape * fix(github): allow omitted stack metadata * test(github): cover enterprise stack probe failure * test(github): cover null ordinary stack metadata --------- Co-authored-by: E2E Test <e2e@test.local>
…n stdin (stablyai#13379) Same hang class stablyai#11568 closed for .cmd: copilot-hook.ps1 ran [Console]::In.ReadToEnd() and kimi-hook.sh captured stdin before checking the Orca env, so a user-wide hook fired outside an Orca pane blocked forever when the caller abandoned the pipe — one stranded powershell.exe or bash.exe per hook event. Move the env guard (after the endpoint refresh, which can supply PORT/TOKEN) above the read on the Windows-local variants. The payload is discarded on the missing-env path anyway; the broken writer is the trade stablyai#11568 established. POSIX variants keep capture-first — those callers close stdin, and exiting mid-write there surfaces as EPIPE the agent can see (stablyai#8110). Kimi's remote install now asks for the posix variant explicitly. Verified on a real Windows host: pre-fix copilot with an abandoning caller never exits; post-fix both exit 0 immediately, and the env-present copilot path still consumes stdin and delivers the POST (paneKey + payload received by a local listener).
* Allow direct search with configured search engines Users can now search directly from the tab creation menu using their configured search engine. Forced search mode (`?` prefix) skips file and tab matching for guaranteed search. Refactored tab-entry operations into focused modules for clarity: forced-search parsing, network-safe selection, keyboard focus, copy strings, empty options, and props types. Search routes through the same workspace browser tab opening mechanism used for URLs, with safe title and query presentation that doesn't retain sensitive details. * Allow direct search with configured search engines - Permit search and URL navigation while file index loads; require explicit selection only when needed, not automatic opening - Block malformed IPv6 addresses in bracket notation to prevent misclassification - Support Kagi private-session links via searchUrlOptions - Improve error handling with accessibility: show error messages in status region, disable input during submission, display loading state - Fall back to local browser tab creation when remote creation fails instead of throwing; avoids remote availability blocking local search/navigation - Add error translations for all supported locales (es, ja, ko, zh) * Allow direct search with configured search engines - Extract tab create entry lifecycle to key-driven component remounting, replacing conditional state reset with useEffect cleanup - Consolidate network tab entry classification and request building into reusable helpers, eliminating duplicate logic - Simplify owner resolution by inlining logic directly into openWorkspaceBrowserTab - Replace custom surrogate-pair handling with native String.toWellFormed() for search queries - Disable explicit URL classification to prioritize search-engine queries over raw URLs * Allow direct search from quick-open tab bar entry - Single-token queries keep file matches ranked above search (quick-open intent) - Multi-word phrases promote search to top, since they cannot be file paths - Arm network actions once file index fails or text is unambiguous search - Cache prepared file index to avoid re-processing per keystroke - Generate specific tab titles (e.g. 'example.com/docs') instead of generic 'Open URL' - Surface opening workspace when launching browser tabs remotely * Use readOnly instead of disabled for pending search input Maintain keyboard focus during submission so arrow/Escape navigation continues to work. Use aria-busy to indicate loading state accessibly. Also fixes button hover styling when disabled and cleans up error message handling in the classifier. * Treat bare searches as prompts; refine path and IP classification - Bare search queries (e.g., "?") no longer display as error rows - Path prefixes with existing matches are no longer blocked mid-keystroke - Private IPv4 addresses now use http, public addresses use https - Ambiguous inputs with non-numeric ports fall through to search instead of blocking - Improve diagnostics by logging failure reasons in openFailure
…blyai#13893) Grouped/triage sections now read newest-first so recent discussion surfaces at the top, while Timeline keeps its oldest-first history order. - rename sortPRCommentGroupsForTimeline to sortPRCommentGroupsByRecency and add an order parameter - rank threads by their latest activity under newest-first so a fresh reply refreshes an old thread - break timestamp ties by numeric comment id in the sort direction, so a GitHub review batch sharing one timestamp orders correctly - sink groups with an unparseable createdAt to the bottom in both orders Co-authored-by: Orca <help@stably.ai>
…ken for network faults (stablyai#13887) * fix(bitbucket): commit credentials atomically and stop blaming the token for network faults STA-3941 (P0): a credential edit could destroy the last working pair. `writeFileSync` replaces in place, so a failed or interrupted write truncated the previous secret, and the secret and metadata files were published independently — a crash between them left a new secret paired with the old email, unusable on restart. - Write credential files through a temp + fsync + rename, so a reader sees either the old bytes or the complete new ones, never a truncated file. - Carry authMode/email/baseUrl inside the encrypted envelope and treat the plaintext metadata as display-only. A torn write can now only leave a stale displayed account, never an unusable credential. Envelopes written before this change still resolve through the metadata fallback. STA-3944 (P2): timeouts, DNS failures, 5xx and unparseable bodies collapsed to the same miss as a 401, so Orca told users their credentials were invalid when the host was simply unreachable — sending them to regenerate a working token. `/user` now reports rejected vs unreachable, connect explains which happened, and an unreachable host no longer renders as "Auth failed". Adds failure-injection coverage for a partial secret write, an interrupt between publishing the secret and the metadata, legacy envelopes, and the transport-vs-auth split. * fix(bitbucket): loop short writes and keep eligibility out of the keychain Review of the atomic writer found it ignored writeSync's return value. write(2) may return a short count, so a partial buffer could be fsynced and renamed into place — the same truncation the change exists to prevent, moved one step later. Write now loops to completion, with a test that fails without it. Also stops isBitbucketReviewCreationAuthenticated force-decrypting the stored secret. Create-PR eligibility is evaluated proactively for the sidebar, so that popped an OS keychain prompt just from opening a worktree. Presence is enough; creation itself still fails closed on an unusable credential.
Increase row height and the gap above the table so the Automations list feels less dense.
Collapsed threads visibly close on the host — this is the acknowledgement. An extra reply only adds noise. Snapshot the resolution target at launch to prevent silent drops if the panel navigates before agent delivery. Only reply for comments without resolution endpoints.
) * Fix smart sort ranking of done agents by completion time Completed entries stayed in the Done sort class indefinitely when same-state writes refreshed updatedAt without moving stateStartedAt. Introduce agentEntryCompletionAt() to use actual completion time for both age display and sort eligibility, ensuring consistent aging regardless of hook updates. * Fix smart sort ranking of done agents by completion time Co-authored-by: Orca <help@stably.ai> --------- Co-authored-by: Orca <help@stably.ai>
…ablyai#13901) Enable eleven oxlint rules that simplify code without changing behavior, and fix every existing violation. Each candidate was gated on measured cost rather than assumption, so rules that regressed runtime performance or type checking were dropped instead of suppressed. typescript/no-redundant-type-constituents is the largest addition: 113 sites, no autofix. Dead constituents are deleted. Where the redundant literal existed to document intent (`string | 'all'`), it is preserved as `(string & {})`, which keeps the autocomplete hint the original code was reaching for instead of flattening it away. The rule also caught a broken import — remote-shared-control-retirement-probe.ts pulled RuntimeStatus from src/shared/types, which does not export it, so the type silently degraded to `any`; no tsconfig covers that file, so tsc never saw it. oxlint stays at 1.77.0 rather than 1.78.0 because .npmrc sets minimum-release-age=4320 and 1.78.0 is younger than that window. Rules evaluated and rejected, with what disqualified each: - prefer-string-raw: String.raw is a runtime call, not a literal (184x slower) - prefer-string-replace-all: 26% slower - text-encoding-identifier-case: ~5% slower, reproducible - prefer-spread: [...str] is 110% slower than split('') and differs on surrogates - no-implicit-coercion: `!!x` narrows types and `Boolean(x)` does not (22 tsc errors) - prefer-arrow-callback: arrows are not constructible, breaking `new` on mocks - object-shorthand: rewrites source text asserted by a tracked reliability gate - switch-case-braces: pushes ten files past max-lines, which cannot be suppressed - no-useless-switch-case: drops `case undefined:` that switch-exhaustiveness-check needs - arrow-body-style: 115 violations have no fix, and it breaks max-lines - newline-after-import: false-positives on the leading-semicolon ASI idiom electron-vite-output-contract asserted on the literal Object.prototype.hasOwnProperty.call text; retarget it to Object.hasOwn, which rejects inherited keys identically.
* fix(workspaces): support full cleanup scans * feat(workspaces): persist cleanup snapshots * feat(workspaces): add cleanup filter model * refactor(workspaces): remove cleanup presets * feat(workspaces): rework cleanup dialog * fix(workspaces): keep cleanup row ordering render-pure * refactor(workspaces): simplify cleanup browsing * refactor(workspaces): show cleanup facts * refactor(workspaces): surface cleanup row facts * fix(workspaces): remove misleading cleanup count * fix(workspaces): preserve full scan semantics * fix(workspaces): scope snapshot persistence * fix(workspaces): preserve cleanup browse compatibility * fix(workspaces): reconcile cleanup dialog state * test(workspaces): update snapshot store fixtures * test(workspaces): preserve cleanup scan modes * perf(workspace-cleanup): stream scan progress and size results * fix(workspace-cleanup): address review feedback * fix(workspace-cleanup): preserve host-scoped cleanup metadata * fix(workspace-cleanup): declare review source dependencies * fix(workspace-cleanup): align size scan banner * fix(workspace-cleanup): shorten scan action * perf(workspace-cleanup): avoid redundant scan IO * perf(workspace-cleanup): bound restarted evidence scans * fix(workspace-cleanup): satisfy scan queue lint * perf(workspace-cleanup): bound scan and snapshot work * perf(workspace-cleanup): serialize final enrichment * test(workspace-cleanup): assert final enrichment drain * fix(workspace-cleanup): stop progress after renderer teardown * perf: batch workspace cleanup git evidence scans * perf(workspace-cleanup): stop redundant snapshot and scan work * fix(workspace-cleanup): resolve review findings across scan, store, and dialog Correctness: - Chunk git-evidence dispatches at the shared 500-target limit and exclude queued/in-flight ids from target selection, so fleets past the limit can no longer strand rows permanently mislabeled as checked-but-unknown. - Key destructive selection pruning on the user's filter state instead of the per-tick matched-set identity; streaming reclassification no longer silently deselects rows. - Clamp the facet clock to max(scannedAt, open time): a stale hydrated snapshot no longer misbuckets idle thresholds or keeps dead agents fresh; row labels use the same clock. - Supersede and cancel the previous broad scan when a new one starts (renderer registry and same-sender guard in main) instead of racing two fleet scans. - Gate snapshot persistence on hasTargetedWorkspaceCleanupScan so worktreeIds: [] can never persist an empty fleet snapshot. - Re-apply dismissals at set-time in progress application so a dismissal landing mid-enrichment is not clobbered. - Record a one-off local snapshot prune for single (unbatched) remote deletes so removed workspaces cannot resurrect from cache. - Strip .exe when normalizing foreground process names so Windows agent processes match. Performance: - Cache per-candidate facet and review-info objects on candidate identity; no-op streaming ticks reuse the previous rows array and skip every downstream pass; matched-set identity is stable under equal membership. - Compute facet counts/options only while the filter popover is open. - Equality-bail git-evidence publishes; structural (non-stringify) facet-group comparison memoized in the toolbar. - Identity-token fast path for the enrichment cache (cache hits skip both JSON.stringify signatures); prune viewed/dismissal records on removal and expiry; bound the superseded-scan-id set. - Restore the no-op bail in removeWorkspaceSpaceWorktrees (regression). - Abort main-side scans when the renderer is destroyed; module-scope controller maps survive handler re-registration. - Batch removal preflight into one targeted scan (with refreshActivity) per 500 ids instead of one scan per row. - Scan repos at concurrency 2, report discovered counts upfront for honest progress, share fs-activity probes per path (folder workspaces), read only the reflog tail, and skip the snapshot read-before-write via a remembered scannedAt. Split workspace-cleanup-worktree-listing, workspace-cleanup-facet-row-caches, and workspace-cleanup-selection-model out of files that crossed max-lines. * fix(workspace-cleanup): address verifier findings - Fall back to a full reflog read when the newest record exceeds the 8KB tail window, so an oversized subject cannot hide recent ref activity. - Bound the single-removal snapshot prune batch id with a UUID; embedding the unbounded worktreeId silently failed main's 128-char validation and skipped the prune for long remote ids. - Key the main-side broad-scan supersession by sender AND scan mode so legacy suggestion-only and full-workspace scans stay isolated, matching the renderer registry. * fix(workspace-cleanup): own facet caches with useMemo instead of render-time ref writes React Doctor (CI changed-lines gate) correctly flagged the three cache refs written during render. Each per-candidate cache now lives in one memo with the derived context it is keyed on, so the memo deps are the invalidation and interior fills stay content-addressed; the matched-set identity stabilization is dropped since its only consumer reads through a useEffectEvent and never keys on identity.
* test(e2e): stabilize terminal failure coverage * docs(e2e): classify failed-run findings * rm report
…stablyai#14469) macOS maps a control chord by physical key regardless of layout: measured with UCKeyTranslate, physical A/U under Control produce U+0001/U+0015 on 2SetHangul, Russian and Greek exactly as on ABC, though unmodified those keys give ㅁ/ㅕ, ф/г, α/θ. A native terminal inherits this by passing the OS characters through. The browser does not expose that translation. xterm's legacy encoder works anyway because it reads keyCode, which Chromium reports from the physical key, but its kitty encoder derives the key number from `key` and only consults `code` when Shift or Option is held. Ctrl is not in that gate, so a pane with the kitty protocol negotiated reports CSI-u for U+3141 rather than 'a' and the chord does nothing. Ctrl+C escaped this only via its hand-written ETX bypass. Recover the byte from `code` when `key` is non-ASCII, which reproduces the OS control table. An ASCII `key` stays authoritative so a Dvorak remap is honoured. KeyC is excluded: the interrupt policy owns it, and off macOS that policy declines to a selection so the copy binding wins. Fixes stablyai#13331
…orking (stablyai#14375) * fix(agent-status): stop start-less child stops from minting phantom working buildClaudeCachedLeadStatusPayload fell back to 'working' whenever the pane had no cached lead-turn state. That default is right for a spawn or a child tool call, but the same helper serves SubagentStop and TeammateIdle, which end work and prove the opposite. claudeLeadStateByPaneKey is in-memory only, so every app restart empties it. A Claude session that outlives the restart reports its next child event into an empty map and the pane latches 'working' with an empty roster -- no Stop ever clears it, and the 30-minute window only decays the sidebar dot, never the stored state. Fall back by the event's evidence: terminating child events resolve to 'done', which still gates up through resolveClaudePaneState when the roster or background work proves the pane is busy. * fix(agent-status): require evidence for child completion * fix(agent-status): publish matched teammate idle * fix(agent-status): preserve confirmed child work * fix(agent-status): retain live restored teammates * fix(agent-status): reap unconfirmed siblings after child drain * fix(agent-status): preserve unmatched restored children * fix(agent-status): wait for lead completion after child stop * fix(agent-status): persist restored child transitions --------- Co-authored-by: Brennan Benson <brennan@stably.ai>
…budget (stablyai#14160) * fix(runtime): cap remote git.diff and file previews at the transport budget A remote or mobile user who opens the diff of a large image loses their whole WebSocket, not just that request: the E2EE channel closes with 1013 when a reply exceeds the 4 MiB outbound envelope. Two producers can exceed it unaided. git.diff/branchDiff/commitDiff cap text with MAX_RENDERED_DIFF_COMBINED_CHARACTERS (6M chars) -- a *renderer* budget that sits above the transport limit -- and return base64 for previewable binaries bounded only by MAX_GIT_SHOW_BYTES, so a 10 MiB PNG changed in place is ~26.7 MiB in one envelope. files.readPreview inlines base64 up to 10 MiB, and mobile calls it for every image tab. Both now measure against a budget derived from the outbound limit. The check sits in orca-runtime-git.ts, downstream of the dedupe and of both the SSH-provider and local branches, so a payload forwarded verbatim by an old relay is covered by the same code and src/relay needs no change. Local and in-process callers pass no budget and keep full fidelity. Measuring raw bytes would not work, which is the whole reason this needs a module. JSON escaping turns one control byte into six (\u00XX), and binary-buffer.ts sniffs only for NUL in the first 8 KiB -- so a NUL-free file of 0x01-0x1f bytes is classified as *text*, would pass a raw-byte cap, and would then blow the envelope. The budget is escape-aware, with a three-branch fast path that keeps normal diffs at two native byteLength calls and scans only the ambiguous band. The SSH branch of readFileExplorerPreview had the same raw-vs-escaped gap: its stat gate sizes base64 binaries, but text crossed unbounded. It now honours the same decoded-text limit the local branch already enforced. No wire change: GitDiffResult is untouched -- no third kind, no new field. Old clients see an error for one request instead of a dropped connection. diff_too_large joins the structured passthrough codes and lands on an existing error arm in both mobile consumers and the desktop remote path; file_too_large was already handled on both. Instruments the 1013 close, which nothing measured before, so the incidence this cap is meant to drive to zero is finally observable. `emitter` separates a producer size bug from a wedged link. Known regression: remote image previews between ~3.096 and ~3.146 MB now return file_too_large. They only intermittently worked before -- above ~3.0 MB they killed the socket -- so this trades intermittent connection loss for a consistent error. Test: 10281 passed in src/main/runtime + src/shared + src/main/git; mobile 3427 passed. Each of the six budget-enforcement sites is independently mutation-killed. Escaping fixtures cover newline-dense, control-char, CJK, lone-surrogate and base64 content against native JSON.stringify. tsc clean for node, web and cli; oxlint clean. Co-authored-by: Orca <help@stably.ai> * fix(runtime): harden remote reply transport budgets * test(runtime): cover desktop remote preview budgets * test(runtime): close telemetry review gaps * chore(shared): repoint budget imports after the shared/types barrel removal Upstream stablyai#14447 dropped the shared/types barrel; GitDiffResult now lives in git-diff-compare-types and GlobalSettings in global-settings-types. Co-authored-by: Orca <help@stably.ai> * fix(ssh): surface an over-cap preview read as file_too_large The stream reader aborts an over-cap read with StreamProtocolError, whose numeric code falls through mapRuntimeError to a generic runtime_error carrying the raw "Reported totalSize N exceeds client cap M" string. Neither preview client recognizes that: runtime-file-client.ts and mobile-file-preview-response.ts both key on file_too_large. It also made the two file_too_large guards directly below the read unreachable on the streaming path. Gives the cap its own error type so the caller can translate it, keeping the bandwidth saving the cap exists for. A genuine protocol fault still propagates unmasked. Found by the readiness review. Mutation-verified: removing the translation fails exactly the new test. Co-authored-by: Orca <help@stably.ai> --------- Co-authored-by: Orca <help@stably.ai>
…ps (stablyai#14468)" (stablyai#14485) This reverts commit 2e91a74.
* fix(renderer): remove duplicate git history tooltips * test(renderer): harden tooltip regression coverage
…i#14463) Wrap kill and retireRejectedPty so a cleanup failure cannot replace the original split-authority error or skip the remaining teardown.
… repo (stablyai#14399) `resolveWorktreeSelector` resolved every selector kind from the whole-fleet snapshot, so a targeted `id:<repoId>::<path>` lookup fanned `git worktree list` across every registered repo to answer a question about one of them. With a cold scan cache -- app startup, or the first lookup after a mutation clears the snapshot -- that is one subprocess per repo, ~17ms each, to find a worktree whose owning repo the id already names. Measured on a ten-repo fleet: one `id:` lookup scans 10 repos before and 1 after. Scope only `id:`. Every other selector kind is matched across the fleet and its `selector_ambiguous` contract is defined over all repos, so scoping `branch:`, `name:`, `issue:`, or a bare selector would silently pick a winner where they correctly refuse today. A test pins that: `branch:main` across ten repos still throws `selector_ambiguous` and still scans all ten. Lineage stays correct because edges are intra-repo by construction. The scoped path returns null and falls back whenever that does not hold: a repo id registered on several execution hosts, an unknown repo id, or a worktree the scoped scan does not contain. A warm fleet snapshot always wins. Row resolution moves out of orca-runtime.ts into repo-worktree-row-resolution.ts, which owns no state -- the cache-aware scan and folder-workspace stamping are injected. orca-runtime.ts ends up 65 lines shorter than before despite the added feature.
…spellings (stablyai#14439) * fix(terminal): strip the captured shim dir across trailing-separator spellings The scrub compared the captured ORCA_ATTRIBUTION_SHIM_DIR to PATH entries literally, so a trailing-separator difference left the legacy shim directory on the spawned PATH. Same class already fixed in the generated wrappers. Also drops a dead default in the POSIX filter, consolidates comments that had accumulated across fixes, and normalizes the legacy directory once rather than per PATH entry in the cmd wrapper. The boundary scanner stays: a shim path can contain the PATH delimiter, which splitting would fragment. * fix(terminal): keep the git shim tombstone parseable on Windows The cmd wrapper carried two em dashes in comments. cmd.exe seeks through a batch file in bytes but advances by decoded character count, so those four extra UTF-8 bytes made it drop the first four characters of every line and the wrapper died with "The syntax of the command is incorrect." Also move the legacy-dir trailing-separator strip into a CALL body: cmd expands a whole line before evaluating `if defined`, so inline it ran its substring syntax against an unset variable and mangled the line. Rooted-path checks are shared by a single subroutine, a relative or drive-relative captured ORCA_REAL_* is rejected, and relative PATH entries are dropped from the exported PATH so the cwd cannot select spawned tools. The POSIX tombstone is deleted rather than written when no absolute interpreter can be verified. Verified on Windows 11 (cmd and PowerShell 5.1): normal lookup, relative and drive-relative ORCA_REAL_GIT, relative and drive-relative PATH entries, trailing separators, legacy shim dir, empty PATH, and a cwd-only PATH with a planted git.cmd/git.bat. Co-authored-by: Orca <help@stably.ai> * fix(terminal): stop the git shim tombstone re-expanding PATH data cmd re-expands a CALL command line, so path data handed to a subroutine as an argument got a second round of percent expansion. A PATH entry holding a literal %CD% became the current directory before the rooted-path guard saw it, and the wrapper then ran a planted git.cmd from that directory (exit 66, reproduced on Windows 11). Callers now pass the value in a variable, which is expanded once. Re-verified on Windows 11 across 15 cases: the %CD% entry is now dropped and the real git runs, and a PATH entry spelled 'C:\paren9 (x86)\cmd' still resolves, so the new for-block did not regress paths with parentheses. Co-authored-by: Orca <help@stably.ai> * fix(terminal): keep percent expressions out of the shim tombstone comments cmd expands variables inside rem, so a comment naming the working directory substituted a path into itself. Verified on Windows 11 that rem does not re-parse the result -- a cwd of 'C:\x&pwned&rem' executed nothing and the real git still ran -- so this was not exploitable, but rem handles separators differently inside a parenthesized block and this script now has some. A test now rejects any percent sign in an emitted rem line. Co-authored-by: Orca <help@stably.ai> * fix(terminal): pin shim tombstone shell state and directory identity Three fixes, each proven before and after. Delayed expansion: bare setlocal inherits the caller's state. Under a parent shell started with /V:ON, a literal !CD! PATH entry became the current directory and a planted git.cmd ran (exit 66), and a legitimate directory containing ! stopped resolving (exit 127). Both on Windows 11, both gone with setlocal DisableDelayedExpansion. Directory identity: the POSIX filter compared the legacy shim directory lexically while comparing its own directory with -ef, so a symlink or a <legacy>/../<legacy> spelling escaped the filter and the live attribution wrapper won the lookup. It now tests both. The env scrub had the same gap and now normalizes before its suffix test. Retained POSIX wrappers: with no absolute bash verifiable the wrapper was deleted, which strands a shell that already hashed the path on 127 instead of falling through to PATH. It now reuses the shebang of the wrapper it replaces, which is known to work on that host, and rejects /usr/bin/env so the ambient lookup stays closed. Deleting is the last resort. Two Windows test pins matched the wrong occurrence and stayed green with the guard they claimed to protect removed; they now assert the subroutine body. All five fixes were mutation-tested. Co-authored-by: Orca <help@stably.ai> * fix(terminal): require bash for a reused shebang and exclude slash-spelled dirs The retained-shebang fallback accepted any absolute executable that was not env, but the rendered body needs BASH_SOURCE, [[ and local, so a #!/bin/zsh wrapper was accepted and then exited 1 on 'BASH_SOURCE[0]: parameter not set'. It now requires bash, which also rejects /usr/bin/env as before. The PowerShell filter trimmed only backslashes while its rooted-path regex accepts forward slashes, so a wrapper or legacy directory spelled with a trailing / missed the lexical exclusion. Verified on Windows 11 that both spellings are now excluded and the real git still runs. The test that claimed to cover the shebang fallback only called the resolver directly, so deleting the wiring left the suite green on any host with /bin/bash. It now mocks the resolver to null and asserts through neutralizeLegacyTerminalShimDir that the wrapper survives with the retained shebang, and that a wrapper without a reusable one is still deleted. Both mutations are now killed. The Windows wrapper text assertions move to their own file rather than taking a max-lines exemption. Co-authored-by: Orca <help@stably.ai> * fix(terminal): stop the shim PATH scrub deleting a legitimate directory The previous round collapsed '..' lexically before classifying a PATH entry. That is not the same as resolving it: when <shim>/posix is a symlink, <shim>/posix/../posix lands elsewhere, so a legitimate directory was classified as the shim and removed, leaving git unresolvable. Reproduced with a symlinked shim/posix and a real git behind it. Resolving for real is not available here either -- this env is also built for remote and WSL panes whose paths name nothing on the local filesystem -- so the classifier is lexical again, deliberately. A '..' spelling that slips through costs nothing at runtime: that directory holds the pass-through tombstone, and the tombstone excludes its own directory by -ef, so the lookup still reaches the real git. Separately, pathEntrySpellings can only enumerate one added separator, so a captured directory spelled with two or more survived the literal removal. The split filter now also compares separator-stripped forms, which covers any number. Both changes are mutation-tested. Co-authored-by: Orca <help@stably.ai> * fix(terminal): stop a relative shim dir letting the cwd pick the binary The -ef identity test added two rounds ago resolves a relative right-hand operand against the wrapper's current directory, so a relative ORCA_ATTRIBUTION_SHIM_DIR let the cwd decide which PATH entry counted as the legacy directory and got a legitimate one skipped. Reproduced as SAFE vs LATER purely by changing the cwd. Identity is now attempted only for an absolute target; the lexical compare still covers the rest. The cmd wrapper had the same shape: full-path expansion made a relative captured value absolute against the cwd before PATH filtering. It now requires a rooted value and leaves the normalized form unset otherwise, which makes the reject subroutine a no-op. Verified on Windows 11 that two runs differing only in cwd now agree. Separately, trailing-separator stripping treated a backslash as a separator on POSIX, where it is a legal filename character, so '/tmp/captured\' and '/tmp/captured' compared equal and a real directory was deleted from PATH. The rule is platform-specific now; the cross-platform classifier still understands both styles because a Windows PATH reaches it through the remote env. Both fixes are mutation-tested. Co-authored-by: Orca <help@stably.ai> --------- Co-authored-by: Orca <help@stably.ai>
…e notifications (stablyai#14070) * fix(runtime): terminate renderer graph reload generations * fix(runtime): harden renderer reload teardown * fix(runtime): fence renderer graph publication ownership * test(runtime): register renderer graph reload gate * test(runtime): record live reload validation * fix(runtime): ignore cancelled renderer navigations * chore: preserve main formatting during branch sync * chore: satisfy changed-code quality gate * fix(runtime): restore cancelled renderer reloads * fix(runtime): preserve committed reload fencing * test(runtime): prove cancelled reload timeout * docs(reliability): record reload cancellation oracle --------- Co-authored-by: Jinwoo-H <jinwoo0825@gmail.com>
…inal input (stablyai#14500) * test(e2e): headless preedit-geometry coverage for Korean and CJK terminal input Both IME defects that shipped and were reverted passed a suite of ~3000 IME assertions, because every one of them checked bytes reaching the pty and a preedit rendered into a hidden overlay satisfies all of them while the user composes blind. The one arm that asserted real geometry was headful-gated and macOS-only, so it never ran in CI. Drives composition through CDP Input.imeSetComposition rather than a native input source, which removes the accessibility grant, the system input source and the visible window that forced that gate, so this runs in the ordinary electron-headless project. The load-bearing assertion is the composition overlay's real bounding rect. Verified to have teeth: with max-width 0 and overflow hidden injected, the active class, the textContent, display block and checkVisibility all still pass, and only the rect assertion fails. * test(e2e): restore the CDP composition drivers the preedit specs need The trimmed copy on main kept only the key-dispatch helpers, so the composition drivers the geometry specs import were missing. Adds them back: setImeComposition, commitImeText, dispatchImeProcessKey, composeHangulSyllable and dispatchResumedCompositionUpdate. The shared helpers are unchanged. Co-authored-by: Orca <help@stably.ai> --------- Co-authored-by: Orca <help@stably.ai>
… byte gap (stablyai#14374) A hidden-delivery byte gap can strand more than the SGR pen, and the reset stablyai#14241 added to the split alt-screen replay is undone before any content is painted: xterm answers `?1049l` with restoreCursor(), which reloads the pen, all four G-set designations, GL, origin mode and wraparound from the register saved at `?1049h`. - Bracket the buffer switch with the baseline: before, so `?1049h` banks grounded state rather than the gap's; after, so `?1049l`'s restore cannot reapply it. - Ground everything a serialized payload is diffed against, not just the pen: SGR, GL, all four G-sets, origin, autowrap, insert, the per-buffer scroll region, and the saved-cursor register. - Switch buffers only when the pane is actually on the other one. `?1049` is not a no-op otherwise — it still swaps the kitty flag registers, which would park the flags of an agent that negotiated them on the normal screen. - Return to the normal buffer when the gap ate the TUI's exit sequence; the restored history was painting into the alt buffer with scrollback left empty. - Restore the CAN stablyai#14241 dropped, so a control string the gap truncated is discarded instead of committed by the next ESC. - Ground the abandon path exactly once instead of twice. - Derive the parity/fuzz preambles from the same builder; they had drifted and were asserting against bytes production no longer emits.
…4492) * fix(linear): label Start workspace and add Open on Linear The issue page header used three unlabeled icons. Match the GitHub issue header: copy stays quiet, Open on Linear is an external-link control, and Start workspace is a labeled primary button. * Address PR review feedback (stablyai#14492) - Make the header source contract ignore Start workspace formatting
* Add global external worktree visibility defaults * Expand global worktree visibility source defaults * Fix host-scoped visibility settings races * Fix global worktree visibility integration * Enable source visibility defaults on mobile * Polish external worktree settings navigation * Clarify inherited worktree visibility settings * feat(sidebar): replace the inherited-visibility switch with a Show/Hide picker Each source row now shows a two-segment Show / Hide control preselected to the global setting, and explains itself only where the project actually disagrees: an "Overriding global setting: <value>" card names the value being ignored. Picking the segment global already holds drops the override instead of pinning a duplicate, so the same control both overrides and reverts, retiring the separate "Use global" link. The dialog footer now lists every inheritable source with its global value. * fix(sidebar): preserve reset for matching visibility overrides
…tal takes (STA-4297) (stablyai#14496) * fix(daemon): persist the pending-output counter across empty incremental takes (STA-4297) An empty incremental take advanced pendingOutputSeq without writing a log batch, so the in-memory counter ran permanently ahead of the log. The next warm reattach could not prove continuity and committed the live 1000-row window over a deep durable checkpoint. Advance the counter only for takes that get persisted: a snapshot take (stamped into the checkpoint) or one carrying records/overflow. This matches the layers below, which already treat an empty take as a no-op write. * test(daemon): keep empty-take coverage outcome-based
…ints (STA-4228) (stablyai#14497) * fix(daemon): bound the caller's wait on final durable-history checkpoints (STA-4228) shutdownWithHistoryLock threaded the caller's absolute deadline into ensureConnected and into the kill RPC, but awaited the final keep-history checkpoint between them with no bound at all. Worktree sleep supplies that deadline, so a stalled history write pinned the process-wide checkpoint tail and stranded Sleep Terminals until an app restart. Bound only the caller's wait. The checkpoint itself stays deadline-free: it remains the exclusive tail, runs to completion, and still commits, so nothing durable is cancelled or deferred. On expiry the caller stops awaiting, throws FinalCheckpointWaitExpiredError, and never falls through to the kill, so the PTY stays alive and the stop is reported unverified. * test(daemon): prove final checkpoint deadline outcomes
…tablyai#14385) * fix(daemon): bound checkpoint overlay fanout * fix(daemon): retain checkpoint admission through deadlines * docs(reliability): refresh checkpoint gate evidence * fix(daemon): prevent checkpoint admission starvation * docs(reliability): clarify checkpoint bounds * fix(daemon): distinguish checkpoint admission warnings * fix(daemon): bound checkpoint admission diagnostics
…i#14199) Devin documents config.json as JSONC. Installing hooks parsed it with jsonc-parser and then reserialized with JSON.stringify, silently dropping the user's comments, key order, and formatting on every install. Edit the original text with modify/applyEdits one hook event at a time so untouched entries keep their attached comments, and let both writers accept pre-serialized text so the shared atomic write and rolling backup are reused. The two existing tests asserted with JSON.parse, which could only pass once the comment had been stripped; both now parse as JSONC and assert the comment survives.
…CLI path (STA-4270) (stablyai#14458) * fix(codex): resolve the launch preflight to a verified absolute Orca CLI path (STA-4270) The Codex launch preflight carried a bare command name ('orca' / 'orca-dev') in ORCA_CODEX_LAUNCH_PREFLIGHT. The codex() wrapper that invokes it is emitted after the user's profile scripts are sourced, and those routinely rewrite PATH, so the name was resolved against a PATH Orca neither controls nor can predict. Resolve and verify the shipped CLI's absolute path instead, and return null when no path verifies so the preflight is skipped rather than run against an unidentified program. * test(codex): align bundled launcher fixture across CI hosts
* fix(agent-map): commit time slider changes * fix(agent-map): cancel invalidated slider commits * fix(agent-map): reconcile collapsed slider drags
…opping prompt (STA-3367) (stablyai#12853) * fix(agent-launch): wait longer for cold-boot Codex composer before dropping prompt (STA-3367) Continue-in-new-session pastes the handoff prompt once Codex renders its composer glyph, gated on an 8s readiness budget. A cold/first-run Codex can take longer than 8s to mount its composer, so the wait timed out and the prompt was silently dropped into an empty terminal. Marker-gated ready signals (Codex glyph, opencode show-cursor) are positive proofs: the paste fires only when the marker actually renders, so a longer budget can never paste prematurely — it only tolerates slow cold boots. Give those signals a 20s budget while the markerless quiet-window signal keeps 8s. * fix(agent-launch): share the composer-readiness budget across all three delivery owners (STA-3367) The cold-boot fix was correct but landed as a single-path exception, and it double-spent its own budget. Three follow-ups so the behavior is a system rule: 1. Split the PTY-spawn wait from the composer wait in pasteDraftWhenAgentReady. Both were handed the same budget, so a codex tab took up to 41s to report a dropped prompt. "Tab has a PTY" and "composer accepts input" are separate states: spawn keeps a fixed 8s, and the readiness budget now starts once the PTY exists, so a slow spawn can't shorten a cold composer's window. 2. Move the per-signal budget to draftPasteReadyBudgetMs() beside the shared readiness scanner. The budget is a property of the ready signal — only that module knows which signals are marker-gated — so all three delivery owners (renderer tab paste, renderer startup paste, main runtime startup paste) consume one policy instead of three hardcoded 8s constants. 3. Give the main-runtime startup paste the process-ownership fallback both renderer paths already have. It resolved null on budget expiry, silently dropping the prompt on worktree-create / CLI / remote-host delivery — the same STA-3367 failure, on the path the original fix didn't reach. Adds coverage for the main-runtime waiter, which had none. Test: vitest src/main/runtime src/shared src/renderer/src/lib src/renderer/src/components/terminal-pane — all green; tsc clean. * test(agent-launch): consume the shared readiness budget instead of restating it Hardcoding 20000 in the runtime waiter test meant it would keep passing if OrcaRuntimeService stopped consuming draftPasteReadyBudgetMs — the exact drift this PR exists to prevent. The literal values stay pinned once, in the scanner test. * refactor(agent-launch): collapse the readiness budget to one flat timeout The per-signal budget (marker 20s / quiet-window 8s) tied the timeout to how readiness is DETECTED. The budget is really a property of how slowly an agent can boot — a marker, a quiet window, and a process check all wait out the same cold start — so one number covers all three signals. Replaces draftPasteReadyBudgetMs() with DRAFT_PASTE_READY_TIMEOUT_MS: drops a constant, a branch, and two tests, and removes the only reason a delivery path needed to know which signal class it was using. Cost: a launch that never emits DECSET 2004 now surfaces its 'prompt not sent' toast at 20s instead of 8s. That is the failed-launch path only; successful markerless delivery still resolves on the 1.5s quiet window as before. * fix(agent-launch): constrain cold Codex readiness budget * fix(agent-launch): observe Codex readiness from PTY bind * fix(agent-launch): anchor early Codex prompt to TUI screen
Owner
Author
|
Upstream stablyai#12823 merged as |
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
Summary
Description Allow
ORCA_WORKTREE_ADD_TIMEOUT_MSto raise the worktree-add timeout above the 180s default (capped at 30 minutes) for slow large-repo checkouts. ## Focused fix - In scope: bounded env override forWORKTREE_ADD_TIMEOUT_MS- Out of scope: per-repo UI settingNote
innocarpe/orcamainuntil the upstream PR is merged.