mirror of
https://github.com/stablyai/orca.git
synced 2026-10-30 00:27:39 +00:00
2f4e9aed70dd6d95615674e98f51e98332f1374d
5001
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
2f4e9aed70 |
fix(worktrees): address review feedback on the worktrees slice split (#14669)
* fix(worktrees): address review feedback on the worktrees slice split Follow-ups to #14643. All of these predate the split (the constructs moved verbatim from worktrees.ts), so they are behavior fixes, not refactor fallout. - fetchAllWorktrees: route runtime-scope-forbidden errors through the toast in the hydration path, matching the fast path - updateWorktreeMeta / ensureHostedReviewPushTarget: use trySettingsForWorktreeOwner so an ambiguous owner skips push-target resolution instead of throwing past the { ok, error } contract - updateWorktreeMeta: compare linkedPR with ?? null so an unknown worktree does not trigger a spurious hosted-review refresh (matches the four sibling providers) - persistWorktreeMeta: localize the two runtime-capability messages - buildWorktreeRenameState: fix a doc comment that contradicted the code - setWorktreesPinnedAndReveal: skip updateWorktreesMeta on an empty map - pruneLastVisitedTimestamps: treat an empty worktreesByRepo list as unhydrated, and clear activeWorkspaceKey alongside a stale activeWorktreeId - forceDeletePreservedBranch: fail closed when several retained cleanups match and no host was given, rather than routing to the active runtime - purgeOrphanedRuntimeSshProjects: drop blank target ids so a repo with no connectionId cannot match - markWorktreesDeleting: make the skip guard phase-aware so queued rows are promoted to deleting - worktree purge: validate right-sidebar tabs with normalizeRightSidebarRoute so pr-checks and plugin panels are not silently dropped * test(worktrees): add edge case tests from review feedback Add defensive tests covering phase-aware deletion state transitions, tab preservation across worktree purges, graceful handling of ambiguous owner scenarios, and stale workspace cleanup. These catch edge cases that could lead to silent failures or invalid state transitions. * fix(worktrees): preserve workspace key pointing at live worktree Replace overly broad scope check with direct equality check, so pruning only drops the stale worktree's own workspace key while preserving keys pointing to other live worktrees or folder workspaces. * fix(worktrees): clear a legacy unprefixed active workspace key on prune pruneLastVisitedTimestamps only dropped the `worktree:`-prefixed derived key, so sessions predating the prefix kept a phantom workspace selected. Match the purge path, which already treats a bare worktree id as a workspace key. * chore(i18n): re-key the two runtime-capability strings to the canonical scheme The keys added when persistWorktreeMeta was localized were hand-written. Derive them the way localize-renderer-strings.mjs does — module path plus sha1(path:text) — so a drift audit against the generator does not flag them. * fix(worktrees): stop updateWorktreeLineage rejecting past its never-rejects contract The sidebar's "Remove parent link" (WorktreeContextMenu.handleRemoveParentLink) awaits updateWorktreeLineage in a bare `void Promise.all(...)` with no catch, so the action must always resolve. Two paths broke that: - settingsForWorktreeOwner ran outside the try and throws on an ambiguous owner. Skip with warnAmbiguousOwnerOnce instead, matching the ensure-push-target fix. - the recovery refresh inside the catch has no internal try/catch, so a failing lineage RPC rejected out of the handler meant to absorb the failure. assignWorktreeParent keeps rethrowing (both its callers catch and toast), but its recovery refresh is now best-effort too so it can't mask the original cause. Also: localize the new ambiguous-host preserved-branch throw (it reaches a toast description; the throw above it stays verbatim because it mirrors a main-process message), and drop blank SSH target ids once so both purge lookups agree. * fix(worktrees): surface un-nest failures instead of silencing them Supersedes the approach in |
||
|
|
470ef65bd7 |
fix(editor): address review feedback on the split editor slice (#14850)
* fix(editor): address review feedback on the split editor slice - Localize the conflict-placeholder guidance string (en/es/ja/ko/zh). - Filter target-worktree tabs by migrated tab id so an owner transition cannot leave two tabs sharing one id. - Resolve a pending editor reveal by fileId first; the oldFilePath scan could pick another worktree's rekey. - Return before opening a workspace editor item when conflict metadata is missing, so no tab is created for a file that never entered openFiles. - Skip a persisted open file whose resolved id is already used; the session schema allows repeated (path, worktree, runtime) tuples. * fix(editor): remove migrated tab ids from sibling groups When tabs migrate to a target group during editor owner transition, the same tab IDs can be left in sibling groups, causing state corruption. Strip these IDs from all sibling groups to ensure each tab ID exists only once across the editor layout. |
||
|
|
31e9f4af30 |
Refactor: split editor.ts into modular state actions (#14847)
* refactor: split editor.ts under 400 lines Move editor slice types, file-id/tab helpers, and action factories under src/renderer/src/store/slices/editor/. The source file is now a public barrel. No intentional behavior change. * refactor: split editor-chrome-slice into state modules - Move EditorDraftState, ExplorerDirState, and RightSidebarState type definitions into their respective action files - Simplify state creator return types from Pick<EditorSlice, ...> to specific state types - Improve modularity by colocating types with implementations |
||
|
|
1f483c3e4e | fix(tab-bar): exclude disabled agents from new-tab search (#14824) | ||
|
|
306c5d545f |
refactor(source-control): split the dropdown action resolver under the max-lines budget (#14835)
`source-control-dropdown-items.ts` was 524 counted lines behind an `eslint-disable max-lines`. It splits along the seams the resolver already had: - `source-control-dropdown-item-types` — the row union, consumed by CommitArea, the composer and the action dispatcher without pulling in the state machine. - `source-control-dropdown-labels` — count/label/title wording. - `source-control-dropdown-action-context` — the branch, upstream and review facts every row reads, derived once so rows cannot disagree about them. - `source-control-dropdown-commit-items` / `-remote-items` / `-review-items` — the three row groups, each keeping its own disabled-reason ladder intact. `resolveDropdownItems` is now just the entry order plus the conflict-abort and hosted-review-busy passes. Verified output-identical to the pre-split resolver: a differential harness ran both implementations over 16,380 generated input combinations (every upstream shape × PR state × conflict operation × blocked reason × staged count × provider) and compared entries deeply. The harness was scaffolding and is not committed. |
||
|
|
6854cceb90 |
refactor(panes,tabs): split pane manager and tab-group modules under the max-lines budget (#14760)
The two tab-group hooks, the pane manager, worktree activation, and the terminal pane context menu each carried a file-level `eslint-disable max-lines` and ran 461-745 counted lines against a 300-line budget. AGENTS.md calls for splitting rather than suppressing, and config/max-lines-baseline.txt is a shrink-only ratchet, so this removes all five suppressions and prunes their entries (341 -> 335). Pure move, no behavior change. useTabDragSplit is cut into gesture lifecycle, hover preview and drop commit; useTabGroupWorkspaceModel into item projections plus the tab-close, close-scope, activation and creation command sets; the pane manager into host, tree mutations, pane creation, drag wiring, reparent frame tracking, layout sweeps and rendering diagnostics. react-hooks exhaustive-deps stays at zero warnings, matching HEAD. Dependency additions are only stable identifiers -- refs and callbacks that became parameters -- and no `.current` dereference was added to any dependency array. Verified: oxlint clean, ratchet passes, typecheck clean, full unit suite green (the three remaining failures are pre-existing load flakes in untouched files, each green when re-run serially), no new runtime import cycles among 1020 modules, no barrel files, and no lint suppression added anywhere. |
||
|
|
cc19692f93 |
refactor(editor): split Monaco, autosave and notebook modules under the max-lines budget (#14748)
The four editor modules, the diff-comment decorator and the file-type icon table each carried a file-level `eslint-disable max-lines` and ran 319-806 counted lines against 300/400-line budgets. AGENTS.md calls for splitting rather than suppressing, and config/max-lines-baseline.txt is a shrink-only ratchet, so this removes all six suppressions and prunes their entries (341 -> 334). Pure move, no behavior change. MonacoEditor is cut along its own seams -- mount, input bindings, markdown annotations, decorations, content sync, view-state persistence and reveal scheduling -- with the markdown overlay becoming its own component. useEditorPanelContentState splits into file and diff content loaders plus the active-tab load and reload triggers. When the mount module came in at 370 counted lines, over the 300 ceiling, it was split again into its parameter types and its input bindings rather than carrying a suppression. Hook usage is identical to HEAD across all three React split families: the same counts of every hook type between each original and its extracted modules, so no hook was added, dropped, or converted to a plain function. react-hooks exhaustive-deps stays at zero warnings, matching HEAD. Verified: oxlint clean, ratchet passes, typecheck clean, full unit suite green (the four remaining failures are pre-existing load flakes in untouched files, each green when re-run serially), no new runtime import cycles among 884 modules, and no lint suppression added anywhere. |
||
|
|
98450718f7 |
refactor(right-sidebar): split file explorer and sidebar under the max-lines budget (#14741)
The six right-sidebar modules and the remote file browser each carried a file-level `eslint-disable max-lines` and ran 347-797 counted lines against 300/400-line budgets. AGENTS.md calls for splitting rather than suppressing, and config/max-lines-baseline.txt is a shrink-only ratchet, so this removes all seven suppressions and prunes their entries (341 -> 333). Pure move, no behavior change. Two renderer-specific hazards were found and fixed rather than shipped. First, effect and ref LIFETIME. FileExplorer's `if (!worktreePath) return` sits above the files pane, so moving the worktree-reset effect into that pane made its guard ref `lastResetWorktreePathRef` die on any render where worktreePath was transiently null (workspace-list refresh, store rehydrate, remote worktree reload). On remount the guard read null, so the reset fired even when returning to the SAME worktree -- wiping dirCache, collapsing every expanded directory, clearing the name filter and undo history, and forcing a full re-read over SSH. The tree-load effects now live in a hook called from FileExplorer above the early return, and the pane is purely presentational with zero hooks. That also restores the original parent-effect ordering, which had shifted because React flushes child effects before parent effects. Second, extracting a hook silently degrades dependency analysis: `setX` setters that the linter knew were stable when created locally become opaque parameters, producing 8 new react-hooks/exhaustive-deps warnings where src/renderer had zero. Those are fixed by listing the genuinely stable identifiers (useState setters and ref OBJECTS). No `.current` dereference was added to any dependency array, since that would change callback identity as the ref mutates. Verified: oxlint clean with exhaustive-deps back to zero, ratchet passes, typecheck clean, full unit suite green on the first pass, no new runtime import cycles, no lint suppression added, and hook usage identical to HEAD across both split families. |
||
|
|
97b71c2285 |
refactor(usage): split AI-usage scanners and stores under the max-lines budget (#14668)
The three usage scanners and their stores, plus the renderer usage-overview model, each carried a file-level `eslint-disable max-lines` and had grown to 338-769 counted lines against a 300-line budget. AGENTS.md calls for splitting rather than suppressing, and config/max-lines-baseline.txt is a shrink-only ratchet, so this removes all seven suppressions and prunes their entries (341 -> 334). Each file is cut along the seams it already had -- and that several of the suppression comments named out loud: filesystem discovery / record parsing / attribution / aggregation for the scanners, and pricing policy / scope filters / rollups / session rows / automation attribution for the stores. Pure move, no behavior change. Code is relocated verbatim; the only edits are import plumbing and, where a private class method became a free function, the mechanical `this.state` -> `state` parameter threading. Every converted call site passes `this.state` at call time and the automation path takes a live `getState: () => this.state` getter, so no state is snapshotted. No barrel exports: each new module owns real logic and importers point at the owner. Verified: oxlint clean, ratchet passes, typecheck clean, full unit suite green (remaining failures are pre-existing load flakes in untouched files, each green when re-run serially), no import cycles among the 64 affected modules, and a statement-level diff of every split confirms the moves are verbatim. |
||
|
|
83117f2860 |
refactor(integrations): split issue-tracker clients under the max-lines budget (#14704)
The GitLab, GitHub, Jira and Linear integration modules, their two IPC registrars, and the shared GitHub project types each carried a file-level `eslint-disable max-lines` and ran 351-614 counted lines against a 300-line budget. AGENTS.md calls for splitting rather than suppressing, and config/max-lines-baseline.txt is a shrink-only ratchet, so this removes all eight suppressions and prunes their entries (341 -> 333). Pure move, no behavior change. Each client is cut along the seam it already had: per-operation modules for the issue APIs (create / update / comment / field options), and for Jira the request queue, site credential store, authenticated request, and site identity. The two IPC registrars keep their own handlers and delegate the rest to per-domain sub-registrars, so they remain real entry points rather than re-export shims. The IPC surface is proved intact rather than assumed: comparing (method, channel) multisets between HEAD and the split gives 52 registrations across 52 distinct channels on both sides. Provider-neutrality is preserved -- GitLab and GitHub keep separate, parallel module layouts rather than being merged behind a shared abstraction. Verified: oxlint clean, ratchet passes, typecheck clean, full unit suite green (the one remaining failure is a pre-existing load flake in an untouched file, green when re-run serially), no new runtime import cycles among 744 modules, and no lint suppression added anywhere. |
||
|
|
93ab6e142e |
refactor(source-control): extract modules to half SourceControl.tsx (#14396)
* docs(source-control): plan half-size extraction * refactor(source-control): extract modules to half SourceControl.tsx * move git decoration token comment to correct component * delete plan doc * test: add useSourceControlBranchCompare and git-history hook tests Comprehensive unit tests covering the scheduling, stale response filtering, and visibility logic of the extracted branch-compare and git-history hooks. * refactor(source-control): internationalize UI strings Add translate() support for all hardcoded strings throughout source control UI, extract reusable SourceControlTreeDirectoryHeader component, improve error handling in bulk operations with logging and user-facing toasts, and add proper return type annotations to hooks. * fix(source-control): satisfy react-doctor rules in extracted modules Reset worktree-scoped hook state during render instead of in an effect, and give dropdown separators stable ids so the changed-code quality gate stops flagging the extracted SourceControl modules. * docs: add JSDoc comments to source-control hooks and components Clarify the purpose, behavior, and constraints of test-harness functions, directory-row components, and the git-history hook to help maintainers understand the extracted and refactored source-control module. * refactor: organize source-control into lifecycle dest folders * fix(source-control): clear remaining react-doctor findings Reset worktree- and history-scoped state during render, keep Cmd/Ctrl selection updates free of setter side effects, and key graph paths by swimlane/parent id. Also add the PR LoC helper scripts the quality workflow fetches from the branch head. * fix(source-control): refetch git history when owner host changes Track activeRuntimeEnvironmentId as a stable key in useSourceControlGitHistory so that when the owner host changes but the worktree and path remain the same, the git history panel correctly refetches from the new host instead of keeping stale commits from the previous one. Add ownerHostKey to the useEffect dependency array to trigger refetch on host changes. Include JSDoc documentation for related components and expand test coverage to verify the host-change scenario. * fix(source-control): refetch git history when owner host changes Track activeRuntimeEnvironmentId as a stable key in useSourceControlGitHistory so that when the owner host changes but the worktree and path remain the same, the git history panel correctly refetches from the new host instead of keeping stale commits from the previous one. Add ownerHostKey to the useEffect dependency array to trigger refetch on host changes. Include JSDoc documentation for related components and expand test coverage to verify the host-change scenario. * refactor(source-control): consolidate bulk mutation error handling Extracts repeated error reporting into a dedicated helper function and applies it consistently across all bulk stage/unstage handlers, including two that were previously missing error handling. |
||
|
|
7aaa7c6f5b |
refactor(sidebar): group worktree-list files by domain (#14486)
* refactor(sidebar): group worktree-list files by domain Follow-up to #14465 / #14467. Keep the landed extract and reorganize the flat worktree-list dump into drag/, headers/, reveal/, rows/, scroll/, and viewport/. Fold tiny modules into their owners, move leftover sidebar-root files into the module, and retarget imports and source-path tests. Layout-only; no behavior change. * fix(sidebar): merge duplicate virtual-rows imports Inlining virtual-row-dom-attributes left a second import from the same module, which fails audit:code-quality:native --deny-warnings. * refactor(sidebar): condense indentation comments Shorten explanations to focus on the essential why, removing redundant detail and improving readability without changing functionality. * refactor: organize worktree-list into lifecycle dest folders * fix react doctor * fix: update reliability-gates path after worktree-list reorg host-filtering.test.ts moved from viewport/ to listing/; keep the runtime-routing.active-server-preference gate pointing at the real file. * Extract workspace status colors to design tokens Define theme-aware color tokens for workspace PR-state indicators (done, in-review, in-progress) to ensure consistent identity across theme switches. Update references to use the new tokens and refactor EmptyState button to use the Button component. * fix(sidebar): stop mutating refs during worktree-list render React Doctor fails static analysis when refs are written in render. Commit reused array identity and the Smart live-signal latch after paint, and return the attention map from the sort memo instead of stashing it on a render-time ref. |
||
|
|
35b308f46e | Remove worktree deletion success toasts (#14724) | ||
|
|
c0a775454a |
fix(terminal): stop the hidden-output restore re-arming itself after dispose (#14749)
`requestHiddenOutputRestoreIfNeeded` tracked its in-flight task with a `.finally` handler that re-armed the restore. The task body is `while (!disposed)`, so once the pane is disposed it exits immediately, the handler runs at once and re-arms again — an unbounded self-feeding promise chain that consumed ~4GB in ~12s and starved the microtask queue. The pane is gone at that point and there is nothing to restore, so the handler now returns early when disposed. This was only invisible because the 25k-line pty-connection suite happened to run a later test that tore the loop down; splitting that file into focused suites left the arming tests at the end of a file and turned it into a reproducible OOM. The regression test drives the restore into its armed state, disposes the pane and counts how many times the chain re-reads `isVisibleRef`. Before this change it cycled 196 times and OOM-killed the worker; after, it settles immediately. |
||
|
|
a234123751 |
feat(shortcuts): warn when macOS Mission Control captures digit chords (#14734)
* feat(shortcuts): warn when macOS Mission Control captures digit chords Mission Control's Switch to Desktop shortcuts (Ctrl+digit by default, present whenever the user has multiple Spaces) are consumed by WindowServer before the app receives the event, so Orca's digit-range shortcuts silently do nothing and the app can never observe the press. Detect the conflict instead: probe com.apple.symbolichotkeys through the live-prefs pipeline on Shortcuts pane mount and surface a standing conflict warning on the affected rows, counted into the Conflicts stat. Non-Darwin and the web client return no chords, and any probe failure yields an empty result so a missing signal can never show a false warning. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(shortcuts): harden Mission Control conflict warnings --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com> |
||
|
|
c4e397bcdc |
perf(renderer): index web session editor tab reconciliation (#8098)
Replace the per-editor-tab Array.find over the worktree's unified tabs with a lazily-built id/fileId map, and swap two O(n^2) includes-in-a-loop scans for Set membership. The map is only materialized when a snapshot actually carries a mirrored editor tab, so terminal-only snapshots pay nothing. |
||
|
|
5b7f44278a |
fix(workspace-cleanup): refuse removal when the owning host is not certain (STA-4343) (#14731)
* fix(workspace-cleanup): refuse removal when the owning host is not certain (STA-4343) * fix(workspace-cleanup): distinguish host collisions * fix(workspace-cleanup): recheck host at removal boundary |
||
|
|
2eb3e11327 | fix(terminal): make close and handles incarnation-stable (STA-4327) (#14590) | ||
|
|
9367169888 |
refactor(tests): split every oversized test file off the max-lines suppression list (#14728)
* refactor(tests): split oversized test files off the max-lines suppression list Every `*.test.ts`/`*.spec.ts` that carried an `eslint/oxlint-disable max-lines` directive is now split into focused, behavior-scoped suites that fit the 800-line test budget, with shared setup extracted into co-located `*-test-harness.ts` / `*-test-fixtures.ts` modules (300-line budget). 83 files became ~930; the largest output is 797 effective lines. `orca-runtime.test.ts` is intentionally untouched. Test bodies were moved by scripted line-range slicing rather than retyped, so assertions are byte-identical. The only permitted body edits were mechanical rebinding where a shared value moved into a harness (e.g. `tmpHome` -> `homes.tmpHome`). Registries that enumerate test files were updated in lockstep: - config/max-lines-baseline.txt: pruned 341 -> 258 entries (all 83 removed). - config/reliability-gates.jsonc: 33 gates repointed at the split files, with assertionRefs split per file where a gate's coverage now spans several. - .github/workflows/pr.yml: the real-zsh lane now lists the 4 split files that actually exercise zsh, so they keep running in the dedicated shell lane. Also renamed agent-hooks `server-test-fixtures.ts` to `server.test-fixtures.ts` so the global-fetch call-site audit keeps skipping it, and added `.js` extensions to the CLI suites' dynamic harness imports (node16 resolution) to unbreak `build:cli`. Verification: full suite 52,449 passing vs 52,448 at baseline with zero assertions lost; `pnpm lint`, `pnpm typecheck`, and `pnpm build:cli` all exit 0; the terminal-pane e2e spec runs 31/31 headless. * refactor(tests): split hook-idle arbitration suite that oxfmt pushed over budget The pre-commit oxfmt pass reflowed pty-connection-hook-idle-arbitration.test.ts to 811 effective lines, 11 over the test budget. Split the hook-completion side effect and replacement-agent veto cases into their own suite; both files now sit well under the cap and the 15 tests are unchanged. * test: port upstream test changes into the split files after rebase Rebasing onto main surfaced 27 tests that main had added to files this branch deleted, plus edits to tests that had already moved. Taking the deletion side of those modify/delete conflicts would have dropped that coverage silently, so each upstream change is ported into the split file that now owns the behavior — for example main's six orchestration mailbox tests land across orchestration-runs, -send, and -check. Also repoints `orchestration.notification-mailbox-consistency`, a gate main added after this branch's gate remap, at those same three split files, and re-prunes the max-lines baseline against main's (257 entries). Verified: all 27 upstream test titles present; full suite 52,761 passing with the only diff vs baseline being 12 tests main itself removed and 3 that moved from skipped to passing; lint and typecheck exit 0. * fix(test): flush pending continuations before tearing down terminal test globals CI shard 5/16 failed on both Node 24 and 26 with `ReferenceError: window is not defined` from pty-connection.ts, surfacing through pty-connection-daemon-snapshot-replay.test.ts. The reattach/settle chains `await` a real promise and then touch `window.api`. Under fake timers those continuations cannot run, so they only become schedulable once restoreTerminalTestGlobals() switches back to real timers — which previously happened immediately before `delete globalThis.window`, so a late continuation threw and failed the whole file. Flush async ticks in that window instead. This is latent in the source rather than new: the pre-split 25k-line file kept running other tests after these, which gave the chains time to settle before teardown. Splitting the file moved teardown directly behind them. * fix(test): keep an inert window after terminal test teardown instead of deleting it The async-tick flush was not enough: the reattach/settle chain can resolve after teardown regardless of how long we drain, so CI shard 5/16 still failed with `ReferenceError: window is not defined` from pty-connection.ts. A real renderer never loses `window`, so deleting it was the artificial part. Swap in an inert proxy whose properties resolve to callables and whose calls resolve to undefined, making a late `window.api.pty.*` call a harmless no-op. The next test replaces it wholesale via installTerminalTestGlobals(), and no test asserts that `window` is absent. |
||
|
|
a663f1bd00 |
fix(terminal): hold a cursor chord until the composing syllable commits (#14730)
* fix(terminal): hold a cursor chord until the composing syllable commits The composed glyph reaches the pty from the composition session-end handler, which runs after the chord's keydown. Only Enter was held for that, so every other chord went straight out on the transport and overtook the text it was typed after: with 가나 on the line, typing 가나다 and pressing Cmd+Left left 다가나, the composing 다 landed at the cursor's destination. Defer any sendInput chord while a composition is live or its session has not yet flushed. Korean 2-Set shows the shape most clearly — the platform replays the chord unmarked after keyup, so isComposing is already false while the session is still pending. No fallback timer on this path. A newline arriving late still arrives, which is what that timer is for; a chord arriving mid-preedit is the corruption the wait exists to prevent, and a conversion can hold its candidate window open for seconds. Dropping the chord costs one keypress, firing early costs a line. Pane commands are unaffected: they are not sendInput actions. Fixes #12871 * test(e2e): pin the composing-chord order at the pty The unit coverage asserts the handler's ordering against a synthetic transport. This asserts it where it is actually observable: the committed glyph and the chord reach the pty by two different routes, and only their merged order is visible to the shell. Verified to discriminate — against keyboard-handlers.ts from main the same spec reads 01 eb8ba4 0a, the chord ahead of the syllable, which is the reported corruption byte-for-byte. * refactor(terminal): add the composing-chord deferral without touching the Enter path Nesting the new branch inside the Enter condition re-indented the whole Enter block, which is the kind of diff that can silently change it. Keeping them as sibling conditions leaves the Enter path out of the diff entirely. * test(e2e): pin the renderer to macOS for the Cmd+Left chord Cmd+Left resolves to \x01 only under the macOS branch of the shortcut policy, so on a Linux shard the chord produced no byte and the spec passed by measuring nothing — it failed in CI for that reason, not for the behaviour under test. Pinning the platform is the established pattern for these specs, and expectImePlatformPolicy fails loudly if the override does not take. |
||
|
|
54645250e1 |
fix: keep remote visibility hydration off local startup path (#14674)
* fix: defer remote visibility defaults after startup * fix: preserve deferred visibility hydration * fix: retain hydration on no-op runtime selection |
||
|
|
375b735e9c |
fix(agent-launch): preserve cold Codex startup drafts (#14688)
* fix(agent-launch): preserve cold Codex startup drafts * fix(agent-launch): honor startup draft readiness budgets |
||
|
|
34eca1ecc7 |
Update mobile 0.0.43 Android download links (#14696)
* Update mobile 0.0.43 Android download links * Sync French and Portuguese APK labels to 0.0.43 |
||
|
|
ab9d1a29a9 |
fix(worktree): never reissue a generated workspace name (#14350)
* fix(worktree): never reissue a generated workspace name
Generated workspace names were deduped only against currently-live
worktrees, so deleting a workspace returned its name to the pool. A later
workspace could draw the same name, land on the same directory path, and
inherit the previous occupant's agent conversation history — coding-agent
CLIs key their prompt history and transcripts by cwd.
Names are now retired permanently per repo. The registry is written in
main with the name Git actually used (the create loop can advance past a
requested name on collision), and seeded once per run from workspace
directories and surviving agent transcript buckets so already-spent names
are excluded from the start. Suggestions degrade to -2, -3 variants
instead of recycling, and those variants retire too.
User-typed names are untouched: retirement filters suggestions only.
* fix(mobile): honor retired workspace names, on one shared implementation
Mobile hand-duplicated the desktop name-suggestion algorithm and deduped
only against live workspaces, so a phone could still be offered a name
whose deleted workspace left agent conversation state behind at that path.
Both platforms now call one shared selector in src/shared, so the two can
no longer drift. The host publishes retired names as an optional field on
the existing worktree.list response, and mobile fetches them per selected
repo while the create sheet is open — mirroring the desktop hook.
Mobile never calls worktree.list for its catalog (it uses worktree.ps,
which carries rows only), so this is a targeted request rather than a
change to the catalog or its cache. Hosts predating the field omit it and
mobile falls back to live-only dedupe, which is the pre-change behavior.
* fix(worktree): close retirement consistency gaps
* test(worktree): cover retirement runtime contracts
* fix(worktree): retire generated collision names
* fix(worktree): enforce retired names at creation
* refactor(ai-vault): extract the Claude project-dir encoder
The bucket-name encoder and its scope-boundary check were private to the
session scanner, so a second consumer had to reimplement them — and got the
per-character encoding wrong. Move both to a shared module with direct tests.
* fix(worktree): make the retirement seed scan actually match buckets
The bucket encoder collapsed runs of non-alphanumerics while the real one
emits a dash per character, so every dot-path bucket missed and the Windows
default workspace root (C:\...) matched nothing at all. Reuse the shared
encoder and its boundary check, which also stops a repo absorbing a sibling
whose path merely shares its prefix.
Also:
- Derive the workspace leaf by stripping the known encoded parent instead of
guessing from trailing dash segments, which retired the parent directory's
name whenever a workspace was named numerically.
- Reuse isAutoGeneratedCreatureBranchName so the -10 and -100 tiers retire.
- Drop the .codex/sessions root: Codex keeps the cwd inside the transcript
rather than in a directory name, so the scan could only ever see a year
folder. Reading transcript contents is not a trade this feature justifies,
so the gap is documented instead.
- Honor CLAUDE_CONFIG_DIR, which relocates the bucket root.
- Delete the unused retirableLeafName export.
Tests write buckets with the real per-character encoding against a fake home,
covering POSIX, dot-directory, Windows drive and WSL UNC roots; all three
platform cases fail against the previous encoder.
* fix(worktree): retire only generated names, keyed by cwd namespace
Two problems in the host-side registry.
Retirement fired for every create, including names the user typed. The
creature pool contains ordinary words — orca, runner, sole, molly, oscar — so
typing a retired 'nautilus' silently produced directory and branch
'nautilus-2' and burned the name for good. Creates now carry an explicit
nameWasGenerated flag; both the skip and the retire are gated on it, and it
defaults to false so CLI and automation callers are unaffected.
The registry was keyed by repo id, but both readers already discarded the id
and unioned by the cwd collision key, because the collision this prevents is
on the path. Keying by that namespace directly fixes several things at once:
entries no longer orphan when a repo is removed, remove/re-add no longer loses
every retirement for an unchanged path, the missing removeProject prune is
moot, and the backfill promise no longer merges into only the first repo id it
saw. The feature is unreleased, so no migration is needed.
Also:
- Memoize the collision key. It runs computeWorktreePath, which for a WSL repo
is a blocking execFileSync('wsl.exe') whose failure path is uncached, and
the previous code recomputed it once per repo on every create and every
listRetiredNames call.
- Drop retiredNamesByRepo from the worktree list result. It had no readers and
leaked onto 'orca worktree list --json', and its awaited backfill sat on CLI
selector resolution. The dedicated listRetiredNames RPC keeps its consumers.
- Make the three RuntimeStore methods required. RuntimeStore is file-private
with two constructors, so the 'older embedders' the optionality protected do
not exist, and the optional chain silently returned no retirements.
- Revert the unrelated forceDeleteBranch rewrite, and make room under the
file's line budget by extracting the create-args mapping instead.
* fix(worktree): send name provenance and stop gating Create on the fetch
Desktop and mobile now mark a create as generated-name only when the user
typed nothing and the composer fell back to the suggestion, so the host knows
which names it may retire.
Remove the retired-names loading gate from every create path. The host already
skips retired candidates before doing any git work, so the client gate bought
nothing while it could disable Create for the length of a full mobile
reconnect ladder (the wait had no timeout) and blank the desktop button
between queued creates. The suggestion still waits; the button never does.
Also make the web client call worktree.listRetiredNames instead of hardcoding
an empty list — the method is registered and mobile-allowlisted, so the
comment claiming no wire call existed was wrong — and filter the mobile
response to strings so a malformed row cannot throw during normalization.
* fix(worktree): key retirement by repo id and prune it with the repo
Reverts the collision-key storage key. It was a function of workspaceDir,
nestWorkspaces, worktreeBasePath and repo.path, so toggling any one of those
orphaned every retirement for every affected repo at once — trading a rare
churn (remove/re-add) for a common one. The read path already unions by cwd
namespace at query time, so cross-repo sharing never depended on the storage
key.
Instead, address the growth and orphaning directly:
- Drop the registry in removeProject, and in removeProjectForHost once the last
host's copy of the repo id is gone, alongside the sparse-preset deletes that
already follow this convention.
- Bound each repo's registry. The cap sits far above the 552-name pool because
evicting inside it would reissue a name whose agent state is still on disk;
only -2/-3 tier accumulation can ever reach it.
- Carry retirements through profile transfer, re-keyed to the destination repo
id and dropped from the source, mirroring sparsePresetsByRepo.
Separately, fix the backfill merge: the scan promise is cached per cwd
namespace, but it closed over the first repo id that triggered it, so a second
repo in the same namespace received nothing. The scan stays shared; the merge
moves out of the cached promise and runs for whichever repo asked.
Local repos re-seed on re-add through that backfill. SSH repos do not — the
scan cannot see the execution host — which is now stated in the module.
* docs(worktree): spell out why the retirement bound sits above the pool
Names the trap directly: the neighbouring 50/200 bounds cap histories, so
lowering this one to match them would silently start reissuing names whose
agent state is still on disk. Also states that oldest-first eviction is a
deliberate least-bad choice rather than a neutral one.
* fix(worktree): send name provenance from the web runtime client
This client hand-enumerates worktree.create params, so the new optional field
was silently dropped and typecheck could not see it. On web and paired-desktop
the host therefore never received it: generated names were never retired, and
the host-side skip that backstops a stale suggestion was disabled too. The same
client does fetch retired names for suggestions, so it was filtering against a
registry nothing ever wrote to.
The test asserts both directions, and fails without the fix.
* fix(worktree): retire names that took more than one collision suffix
isAutoGeneratedCreatureBranchName strips exactly one trailing -N, which is
right for auto-rename eligibility but wrong here. Once the pool is spent the
suggester emits nautilus-2, and a collision on that yields nautilus-2-3 —
which a single strip leaves as nautilus-2, not a pool name, so retirement
no-opped at exactly the tier where every base name is already gone. Strip
repeated suffixes locally rather than moving the auto-rename predicate.
* perf(worktree): keep the retirement backfill off the blocking WSL probe
The backfill runs on composer repo-select, not just at create time, and it
derived the probe path synchronously — which for a WSL repo with a mirrored
workspace dir reaches getWslHome and its blocking execFileSync('wsl.exe').
A stopped distro froze the main process for up to 5s on composer open.
Adds an async twin of computeWorktreePath and uses it for the probe. Resolving
the home there also warms the shared cache, so later sync callers are free.
Also stops memoizing the collision key when the WSL home is still unresolved:
only the success path is cached upstream, so caching the fallback namespace
would strand the repo there for the rest of the session.
* fix(worktree): hold retired names across a refresh instead of blanking
refreshKey changes on every workspace-list mutation, so create-multiple
refetches after each create and the hook returned an empty list until the
refetch landed — precisely the window in which resetForNextCreate clears the
name field and a fresh suggestion is drawn. Keep the previous answer while
revalidating and reset only when the repo changes; a failed refresh keeps what
was already loaded rather than un-retiring everything.
Also makes the returned array referentially stable, so the suggestion memo
downstream stops rerunning on every refetch.
* refactor(worktree): put the retired-name cache rules on one implementation
The desktop and mobile hooks that fetch retired names had already drifted
four ways. The transports genuinely differ (IPC vs RPC), but the caching
rules must not, and mobile's copy reset to [] on any error -- which
un-retires every name for the rest of the sheet session, the one outcome
retirement exists to prevent.
Moves the rules into src/shared/worktree/retired-name-cache: response
normalization, the never-leak-across-repos rule, and the hold-previous-on-
failure rule. Pure, no React, because src/shared is on the main process's
import graph. Each platform keeps its own transport and effect.
Mobile moves up to desktop's behavior: it now holds the previous answer
through a failed refresh, and refetches when the workspace list changes
instead of never refetching after mount.
Also drops the unused `loading` return. Neither platform consumed it; its
only consumer was the Create-button gate reviewed out earlier, and removing
it makes that regression unexpressible.
* fix(worktree): import shared types from their real modules
Main dropped the src/shared/types barrel, so the retirement module's import
resolved locally but not against the PR's merge base.
* refactor(worktree): bound the retirement registry by tier compaction, not eviction
Retirement is a correctness guarantee — a spent name's directory may still hold
agent conversation state keyed by that cwd — so the 2000-entry cap was the wrong
shape: reaching it handed a name back. At the owner's measured rate (~6.6 pool
names retired per day in one repo) the cap was ~9 months out.
Names come from a fixed 552-entry pool and the suggester only reaches tier N+1
once every tier-N name is taken, so a completed tier is exactly a set that no
longer needs listing. A row is now a watermark plus the names above it: reads
answer at-or-below the watermark with no lookup, and compaction drops the 552
entries the watermark now covers. Bounded at one pool per repo forever, with no
eviction and nothing un-retired.
Tiers can complete out of order (a create-time collision can spend `nautilus-2`
while tier 1 is open), so compaction loops and higher-tier names simply wait.
The RPC result carries the watermark beside the names as a new field; a client
predating it reads the names only and under-retires the compacted tiers, which
degrades to the pre-retirement behavior rather than breaking.
* fix(worktree): preserve generated name retirement across failures
|
||
|
|
aaa877d2a2 |
fix(native-chat): stop glued rapid sends from pinning queued bubbles (#14663)
* fix(native-chat): stop glued rapid sends from pinning queued bubbles Trim the draft at the send boundary so the PTY body and the optimistic echo's match key agree, and bound the glue matcher to rows after the oldest open echo's send boundary. Fixes #14262 * fix(native-chat): preserve exact prompt payloads |
||
|
|
2252de0f7f |
fix(workspaces): gate the Jira palette match on the issue's tenant (#14671)
* fix(workspaces): gate the Jira palette match on the issue's tenant Pasting a Jira issue URL matched any worktree whose linked item carried the same `jiraIdentifier`, with no comparison of the site it came from. Jira issue keys are per-project, not per-tenant, so every tenant with a PROJ project has a PROJ-123 — pasting one tenant's URL could jump to a worktree tracking a different tenant's issue entirely. The stored linked URL is the only tenant evidence available here, so where it exists it now decides, comparing origin and site path the way the fallback already did. This also covers path-scoped Jira Server installs sharing one host. The bare identifier still matches when no URL was stored, since it is then the only evidence there is. This mirrors the check `isWorkspaceLinkedItemSourceContextMatch` already makes on the same fields. * test(workspaces): cover the reachable Jira identifier fallback The fallback fixture used a blank url, a shape `normalizeWorkspaceLinkedItem` rejects outright, so it pinned a state that cannot reach the palette. The reachable way to have no tenant evidence is a url that is present but is not a Jira browse link, which is now what the test uses. Also covers a pasted url carrying Jira's `atlOrigin` tracking query, since the matcher compares pathname only. |
||
|
|
3908978ba4 |
preserve agent badges when searching for tabs (#14677)
* fix: preserve agent badges when searching for tabs A searched-for tab is exactly when its agent status matters—the map covers all open tabs, not just recent ones. * test: scope recent-tabs search test assertions to matched tab - Make setCommandQuery checking explicit to prevent skipped assertions - Scope badge verification to the specific queried tab instead of global query - Add check that only the searched tab appears in results |
||
|
|
7558fb064a |
fix(browser): keep browser guests painting when the workbench is hidden (#14599)
* fix(browser): keep browser guests painting when the workbench is hidden
Chromium never paints inside a display:none subtree, so an Electron <webview>
stops emitting CDP screencast frames the moment any ancestor is parked that way.
Orca already models this per pane (browser-page-paintability.ts) and per worktree
surface, using opacity:0 so a phone- or agent-driven page keeps compositing — but
three ancestors above those layers still used `hidden` unconditionally:
- the App-level terminal workbench container, hidden whenever activeView is not
'terminal' (opening Settings froze every mobile browser pane),
- Terminal's root, hidden when there is no active worktree,
- the split-surface wrapper, hidden when the active worktree has no layout.
A pane-level escape hatch cannot override an ancestor, so all of them have to
agree. Share one predicate across the chain and swap `hidden` for an out-of-flow
transparent layer while a remote controller needs frames.
The predicate ORs automation visibility with the mobile driver, matching the
per-worktree gate. That term is load-bearing, not symmetry: agent-browser
commands acquire a visibility lease and then capture, so gating on the mobile
driver alone left automation from a non-workspace view capturing a blank surface.
Mobile: a stream can report `ready` and then deliver no frames, which cleared the
loading indicator and left an unexplained black rectangle. Key it off actually
having pixels. That also retires the `ready` state and its ref.
Co-authored-by: Kaylee Williams <65376239+KayleeWilliams@users.noreply.github.com>
* fix(browser): keep paint retention off store hot paths
---------
Co-authored-by: Kaylee Williams <65376239+KayleeWilliams@users.noreply.github.com>
|
||
|
|
73bbe20ca7 |
fix(i18n): correct Chinese translations (#14299)
* fix(i18n): correct Chinese translations
* fix(i18n): keep startsIn a location label, not a countdown
'Starts in:' renders immediately before request.initialCwd, a directory
path, so the string labels a LOCATION. The new value read as a time delay.
Peer locales agree: ja 開始場所, ko 시작 위치, es 'Se inicia en:'.
* fix(i18n): keep zh terminology aligned with the rest of the locale
- thinking: revert 思考中 -> 思考; it names the Thinking session option
(alongside Model/Effort/Fast mode) and feeds 切换{{value0}}, so a
progressive-aspect status reads as ungrammatical there.
- review pills/filters/fixture: revert 审查 -> 评审. English "Draft review"
was rendering as 草稿审查 on the dashboard card but 草稿评审 in the PR-checks
row summary; zh.json uses 评审 for the review artifact ~70x vs ~6x 审查.
- notAgent.subtitle: revert 编程智能体 -> 编码智能体, matching 16 other
"coding agent" keys.
* fix(i18n): translate drop folder prompt correctly
* fix translation
---------
Co-authored-by: DaQun <1.404848e+07+DaQun@users.noreply.github.com>
Co-authored-by: Brennan Benson <brennan@stably.ai>
Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
|
||
|
|
8b31910ac1 |
Split worktrees slice (#14643)
* refactor: split worktrees.ts under 400 lines Move the worktree slice into dest modules under store/slices/worktrees/ and leave a thin public barrel so existing imports keep working. * refactor: nest worktrees dest modules by domain Group the extracted slice files into catalog, refresh, create, remove, and related domain folders instead of a flat dest dump. * refactor: group worktrees dest modules by lifecycle Collapse the 12 noun folders into listing, create, teardown, metadata, and session so dest layout matches the slice methods. * rm duplicated mport |
||
|
|
83d79f3846 |
fix(workspace-cleanup): make the Filters panel scrollable (#14629)
* fix(workspace-cleanup): make the filter panel scrollable The height cap sat on the ScrollArea Root, but the Radix viewport inside is h-full — under an indefinite root height that collapses to auto, so the viewport grew to full content height with nothing to scroll while the root clipped at 420px. The last facet groups (Archived, and the tail of Workspace status) were unreachable behind the footer. Move the cap to the viewport, which is what the ScrollArea wrapper's viewportClassName exists for. Adds a regression test that pins the cap to the viewport; it fails against the previous markup. * fix(workspace-cleanup): keep filter footer visible |
||
|
|
a3b472d050 |
refactor(renderer): decompose App.tsx into an app-shell module (#14607)
* refactor(renderer): decompose App.tsx into an app-shell module App.tsx was 2831 lines behind an `eslint-disable max-lines` and a grandfathered entry in the max-lines ratchet baseline. It is now 92 lines: a root element, the shared providers, and three children. The body is split by concern into src/renderer/src/app-shell/: - use-app-chrome-layout — titlebar/sidebar/workbench layout derivations - use-floating-workspace-panel — overlay open state, persistence, return focus - use-app-startup-hydration — the boot chain (order preserved verbatim) - use-app-session-persistence — session writer + shutdown checkpoint - use-persisted-ui-writer / use-document-appearance / use-runtime-graph-sync / use-window-visibility-effects - use-onboarding-and-feature-tips — first-run education gating - use-global-keybindings + app-command-handlers — window shortcut dispatch - use-app-shell-services — app-level subscriptions that outlive any surface - AppWorkspaceShell / AppRootSurfaces / AppBackgroundServices / titlebar parts Two startup branches that no longer needed to sit inline moved to src/renderer/src/startup/: startup-ssh-connection-restore and startup-degraded-recovery. No behavior change. Sibling order of root overlays and modals is preserved so stacking is unchanged; the sidebar's virtualized scroll refs still live above the sidebar's remount boundary. The source-assertion tests in app-startup-routing.test.ts follow the code to its new files. The one that claimed to check "first-window startup services before terminal reconnect" was matching the degraded-recovery block, not the success path; it now asserts the success path, and the degraded ordering keeps its own dedicated test. Removes the disable comment and drops App.tsx from config/max-lines-baseline.txt. * refactor(renderer): move app-shell ref writes out of render React Doctor's changed-lines purity gate flags three render-phase ref writes in the new app-shell files. The patterns predate the split, but moving them into new files brings them into the gate. - use-app-chrome-layout: the terminal-workbench latch becomes state set during render (the pattern use-lazy-modal-mounts already uses). The `canMountTerminalWorkbenchNow ||` term keeps the current render correct, so the latch only has to be visible to the next one. - use-app-startup-hydration: the onboarding callback ref syncs in an effect declared before the boot chain, so it lands first on mount. The callback is a `useCallback([])`, so its identity never actually changes. - use-global-keybindings: the shortcut-state mirror syncs in useLayoutEffect, which commits before any key event can read it. Key events are discrete, so handlers always observe committed state. All three now hold committed state only, so nothing can leak from a render React discards — the behavior the gate is protecting. pnpm run check:react-doctor:changed: 0 errors (was 3). |
||
|
|
e12b22c435 |
fix(terminal): bracket agent-pane pastes so a pasted newline can't submit the draft (#14456)
* fix(terminal): bracket agent-pane pastes so a pasted newline can't submit A paste of <=64KB is handed to xterm's term.paste(), which rewrites newlines to CR and wraps in ESC[200~/ESC[201~ only when its parser observed DECSET 2004. Windows ConPTY never forwards that mode, so an unbracketed pasted newline reaches a TUI agent as Enter and submits the draft parked in its composer. The existing force-bracket guard keyed on isWindowsUserAgent() - the client's platform - but the ConPTY can be on a remote host, so the guard was off in exactly the configuration that needs it. Gate on the pane's own TUI agent instead, applied to all four paste entry points; middle-click primary selection had no force flag at all. The agent status row is retained deliberately and is not lifecycle-driven, so it can outlive the agent. Veto on shell-confirmed foreground (OSC 133;D) and on a row rehydrated across an app restart. The freshness TTL and a state check are both unusable here - an idle-but-live agent sits at done and still needs bracketing. Refs STA-4294 * fix(terminal): key agent-paste bracketing on live evidence, not shellForeground Measured against a real pane: shellForeground is republished only at OSC 133 boundaries, so a shell without 133 integration leaves it latched true while an agent owns the foreground. Vetoing on it silently reinstated the submit bug the parent commit fixes - the parked draft was sent on paste with the gate in place. Prefer process-confirmed agent identity when present, keep the restart-rehydrated veto, and drop the shellForeground veto. Erring toward bracketing costs a literal ESC[200~ in a non-2004 program; erring the other way sends the user's draft. Refs STA-4294 * docs(terminal): record why paste bracketing keys on the agent, not the mode bit Measured in real ptys before taking the obvious alternative. A tri-state on DECSET 2004 (observed-on / observed-off / never-observed) does not work: zsh 5.9, fish 4.8.1 and bash >= 5.1 announce and withdraw the mode cleanly, but macOS /bin/bash 3.2, /bin/sh, bash 4.4 and any shell with bracketed paste disabled emit nothing at all, byte-identical to a bare `cat`. Silence cannot be read as consent. Agent identity disambiguates it in the one direction that matters: agents always enable the mode, so silence on an agent pane means the announcement was lost in transit, never an opt-out. Also measured: bracketing a program that never negotiated is worse than useless - the markers land as literal payload bytes and ICRNL still turns the CR into a submit - so the gate stays narrow. Refs STA-4294 * fix(terminal): guard the paste pane key and document the evidence policy Readiness review follow-ups, none behaviour-changing for reachable inputs. makePaneKey throws on a malformed leaf/tab id. It is unreachable today (pane.leafId is a minted UUID and the same pair is already called unguarded from a hotter site), but the failure mode was bad: the throw escapes before the paste helper's catch is attached, so the paste would be a silent no-op with no error surface. Degrade to the pre-fix path instead, with a test. Also record two things a future reader needs: the process-confirmed branch is dead for remote-runtime and SSH panes because foreground tracking is disabled there, so a remote pane's status row is its only evidence; and why this resolver deliberately omits the shellForeground/routingRevoked/routingTrusted gates its two siblings enforce - they route input bytes, this only wraps a paste whose payload is ESC-sanitized downstream. Refs STA-4294 * fix(terminal): encode Windows agent paste newlines as input records * fix protected paste handling in dashboard previews |
||
|
|
19bc1ccf7e | Keep other-client workspace filter visible during catalog hydration (#14595) | ||
|
|
500b72d8ef |
fix(vm): harden provisioned root ownership and cleanup (#14477)
* fix(vm): verify provisioned root ownership * test(vm): retry transient removal menu * test(vm): stabilize provisioned root teardown * fix(vm): clarify recipe-owned cleanup * fix(vm): pin provisioned root source commit * fix(vm): make runtime cleanup user-cancellable |
||
|
|
32f46f9a24 | fix(vm): keep failed cleanup retryable (#14476) | ||
|
|
3a212584ec |
feat(native-chat): Extra high grok effort per model (#14577)
* feat(native-chat): offer Extra high grok effort per model Slice grok's reasoning-effort menu by each model's advertised ceiling so 4.6 can reach xhigh while 4.5 stays at high, and keep the untouched default at high so launch argv does not silently escalate. * fix(grok): parse dashed rows in grok models listing Grok stars only the default model and dashes the rest. A star-only bullet dropped 4.5 from the picker once discovered models were authoritative. |
||
|
|
9bb8836bb6 |
fix(agent-launch): wait longer for cold-boot Codex composer before dropping prompt (STA-3367) (#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
|
||
|
|
e2d309e9cd |
fix(agent-map): commit slider changes once (STA-4281) (#14442)
* fix(agent-map): commit time slider changes * fix(agent-map): cancel invalidated slider commits * fix(agent-map): reconcile collapsed slider drags |
||
|
|
552dedd26b | Keep remote workspace filter visible (#14579) | ||
|
|
83e2123582 |
Add global worktree visibility source defaults (#14276)
* 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 |
||
|
|
cb42b60849 |
fix(linear): label Start workspace and add Open on Linear (#14492)
* 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 (#14492) - Make the header source contract ignore Start workspace formatting |
||
|
|
87bafb5e6a |
fix(terminal): ground the emulator to the snapshot's baseline after a byte gap (#14374)
A hidden-delivery byte gap can strand more than the SGR pen, and the reset #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 #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. |
||
|
|
92b6ffd17d |
Terminate renderer graph reload generations and contain disposed-frame notifications (#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> |
||
|
|
78ca45e2ae |
fix(renderer): remove duplicate git history tooltips (#14453)
* fix(renderer): remove duplicate git history tooltips * test(renderer): harden tooltip regression coverage |
||
|
|
d03eb3218e |
Revert "fix(renderer): stop git history hover from showing two tooltips (#14468)" (#14485)
This reverts commit
|
||
|
|
b6d36c26f4 |
fix(terminal): resolve Ctrl chords positionally on a non-Latin layout (#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 #13331 |
||
|
|
b3efd48c7f |
test(e2e): stabilize terminal failure coverage (#14466)
* test(e2e): stabilize terminal failure coverage * docs(e2e): classify failed-run findings * rm report |
||
|
|
9e5ee5ef8e |
feat(workspaces): rework cleanup discovery and dialog (#13413)
* 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. |
||
|
|
2e91a74deb |
fix(renderer): stop git history hover from showing two tooltips (#14468)
Remove native title attributes from commit rows, subjects, and ref badges so only the managed tooltip appears. |