mirror of
https://github.com/stablyai/orca.git
synced 2026-09-22 16:02:32 +00:00
c498d763cd345deda64cb00d7fcbdd3a2ae3b133
366
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
8859e73980 | perf(main): retire stale worktree marker probes (#13437) | ||
|
|
f2b2ece831 | perf(renderer): reuse locale collators (#13444) | ||
|
|
3b1017c4fb |
Add nightly cut (#13410)
* Add daily macOS dev build release channel Publish once-daily signed macOS builds from main at a dedicated cadence, separate from hourly (too noisy) and release branches (too infrequent). Builds are notarized and installable via the updater, but unvetted — published to stablyai/orca-daily rather than the main repo to avoid evicting stable/RC entries from the releases feed. * fix lint * fix commit * Add third token mint to daily macOS build workflow The upload step's 2x45m retry budget can outlive the one-hour token, so a third is minted after it for verify and cleanup operations. Release notes are moved to a file to ensure consistency between draft creation and publish. Daily channel description updated with specific UTC release time. |
||
|
|
bf406720a0 |
fix(i18n): restore the Japanese renderings the brand revert left in Latin (#12934)
* fix(i18n): restore the Japanese renderings the brand revert left in Latin Before #12113 landed the canonical-rendering guard, the brand-mistranslation revert treated ターミナル/エージェント/コミット/リポジトリ as machine-translation errors and rewrote them back to English on every repair run. The guard stops new damage, but ~700 values still carry it, so the Japanese UI reads "この terminal を閉じると、agent の現在の作業が停止します。" This heals what the catalog already holds and closes the defects the same pipeline introduced elsewhere: - Relocalize the four generic terms inside Japanese sentences. Both sides are anchored on an adjacent Japanese character and reject a `-`, word character or `.` neighbour, so `--agent`, agents.md and "Agent SDK" keep their spelling, and the 和欧間スペース survives in front of an interpolation. - Preserve style blocks and command/identifier values in English. MT had rewritten a CSS selector to [データスラッシュメニュー], `background:` to `背景:` and a keyframe name to ブラウザフラッシュ, and had turned `pnpm install` into `pnpmインストール` and a toast dedup id into 陳腐なエージェント行. The same values are restored in ko, zh and es. - Drop the phrase fixes that stripped ~してください from validation messages, which left 30 prompts ending in a bare noun. - Settle terminology and typography: 紛争→競合, 資格情報→認証情報, 未知→不明, プロフィール→プロファイル, the full-width ellipsis, no separator space inside compound katakana, and one long-vowel form per word. - Fix literal-translation errors (ナメクジ for slug, ミスター for MR, ランニング for Running, 高い/中くらい for priority labels) and complete the truncated Kimi status-bar description. - Translate 570 values that were still English, 485 of them fragments the catalog had left untouched and 85 newly added strings. The catalog is regenerated with `repair-locale-catalog.mjs` and the run is idempotent: a second pass reports 0 leaf updates. * fix(i18n): keep the leading space in concatenated Japanese fragments * fix(i18n): close the self-review findings in the Japanese repair rules Seven defects the relocalization pass introduced or left behind: - `git commit` and `orca terminal` are two-word commands, and the position guards only looked at the character before the term, so the second word was katakana-ized ("git コミットが失敗したとき"). A command-head lookbehind covers git/gh/glab/orca/npm/pnpm/npx/yarn/docker/kubectl. A following Latin word now also blocks the rule, so "Agent SDK" keeps its spelling as the comment claims. - `on: 'オン'` matched every "on" in the catalog, including the preposition in the external-automation delete confirmation, which rendered as "外部ソース オン myhost". Moved to a new ja key-override module so the toggle states and the preposition can differ; the module keeps locale-key-overrides.mjs under max-lines. - The phrase fixes write Cookie and fast-forward back in Latin, but neither was in CJK_LATIN_SPACED_TERMS, so the 和欧間スペース was missing in five values. - Three overrides spelled a half-width `...` that the ellipsis phrase fix rewrites anyway, so the comment described the opposite of the behavior. - Two descriptions render as [text] <code> [text]. The Japanese closed the sentence with 。 and dropped the "such as" / "like" hand-off, leaving the code span outside the sentence in the Git and quick-command panes. - `' vs {{value0}}'` and `' · {{value0}} external'` were missed by the previous leading-space fix, so "3 変更されたファイルvs main" rendered without a gap. Adds three regression cases: two-word commands, the Latin-only label, and the spacing for terms the phrase fixes reintroduce. * fix(i18n): correct the Japanese an external review flagged 73 findings from a ChatGPT review of the full changed-value list, each reproduced against the shipped catalog before being fixed: - Two commands were translated into text that does not run: `pnpm playwright test` became `pnpm プレイライトテスト` and `gh auth login` became `GH 認証ログイン`. Both are pinned to English. A third value corrupted an identifier, rendering `packages/web` as `package/web`. - Two descriptions carried a stale translation with no relation to their English source, and one had another row's text entirely. - Syntax was misread in six values: `Command line Orca runs when…` read as "the command line runs", `Let programs … copy` as "copy the programs", `Dim files matched by .gitignore` kept `Dim` as a noun, and `powers live quota reads` became 強化. - Wording that changed the warning: `diffs may miss recent commits` read as the commits being lost, `before merging is unblocked` as un-merging, and `the newer disk content` as new content. - `host` was rendered as サーバー in eight values even though it covers SSH hosts, `worktrees` as ワークスペース, and `on this host` as リモート. - Git vocabulary translated to its everyday sense: `upstream`→上流, `staged changes`→段階的な変更; and identifiers `lan`/`deploy` were localized. - Instructions to the user had lost their imperative (…をインストールします), three validation messages still ended in a bare noun, and two completion notices read as future tense. - Assorted breakage: 窓 for a desktop window, 分割分割線, ターミナルパネル for Terminal Panes, オプション for the macOS Option key, Herme for Hermes, and a reversed noun phrase in the repo-icon import error. The `Open` action needed a key override: bare "Open" is the PR/issue state in 16 of 18 places, and only the browser download row and the checks panel use it as the verb, matching what ko/zh/es already do. Three fixes had to be reworded rather than written literally: the existing 新しい→新規 and 実験的→実験的機能 phrase fixes run after value overrides and turned 新しい名前 into 新規名前 and 実験的な into 実験的機能な. Not fixed: `{{value0}} site{{value1}} connected` still shows the plural-suffix placeholder, which needs the code change already listed in the PR notes. * fix(i18n): sweep the whole catalog for the defect classes the reviews found The external review covered a sample. This runs each of its finding classes as a detector over all 11,857 values and fixes what they turn up, as durable pipeline rules rather than one-off value edits where the class recurs: - host は サーバー ではない. Orca's "host" covers SSH hosts and this computer, so a phrase fix rewrites サーバー to ホスト whenever the English says host and does not also say server, where the two are deliberately distinct (15 values). - worktree joins the guarded generic-term list, so the seven values still reading "worktree を削除" match the 205 that already say ワークツリー. - Git vocabulary and brands restored from their everyday sense: 上流→upstream, 起源→origin, 段階的な変更→ステージ済みの変更, エルメス/ヘルメス→Hermes, パワーシェル→PowerShell, アヒルアヒル→DuckDuckGo. All five terms are added to CJK_LATIN_SPACED_TERMS so the restored Latin keeps its 和欧間スペース. - 14 more code values pinned to English: Tailwind class strings (size-4 text-muted-foreground → サイズ 4 テキストミュート前景), git refs (origin/main → 原点/メイン), sample hosts (example.com → 例.com) and spec fixtures (dashboard.spec.ts → ダッシュボードの仕様). - 12 instructions regained their predicate (…を選択。 → …を選択してください。), and 窓のぼかし, macOSのオプションキー, 中くらいのセクション見出し are corrected. Checked and deliberately not changed: toast notifications that end in 〜しました (237) read correctly for a completion notice, and setting descriptions ending in 〜します (200) describe what the setting does rather than instructing the user. Sound preset names stay katakana, matching the rest of that list. Adds three regression cases covering the host rule and its server exception, the Git/brand restorations, and the newly pinned class strings and refs. * fix(i18n): keep Agent in Latin in the Japanese catalog Japanese developer UIs conventionally leave Agent unlocalized — it names Orca's own concept rather than the everyday word — so the ja catalog now writes it in Latin and only normalizes the case, so no sentence mixes "agent" and "Agent". The 和欧間スペース comes from the existing spaced-term list. This is the one term where ja diverges from locale-generic-ui-terms.mjs, which lists エージェント as the expected rendering; ターミナル, コミット and リポジトリ follow it exactly. Three test expectations are updated to match, and the PR description flags the divergence so a maintainer can ask for it to be reverted — it is a single rule in locale-ja-phrase-fixes.mjs. The guards are unchanged, so `--agent`, agents.md, `orca agent` and "Agent SDK" keep their spelling. * fix(i18n): preserve selectors with no declaration block, and sharpen progress labels CodeRabbit was right that `STYLE_BLOCK` only matched a selector when it carried a declaration block or an attribute selector. `div.pricing-grid > div.card.starter:nth-of-type(1) > a.cta` was unprotected and only survived because MT happened to leave it alone. A value now also counts as style when every whitespace token is selector-shaped and at least two carry a class, id, pseudo or attribute. The first attempt at that threshold counted a sentence-final period as a selector join and froze 259 ordinary two-sentence strings in English; the marker must now be followed by a letter, so `Show live workspace ports. Click it for …` stays translated. Both directions are pinned by tests. The predicate moved to locale-style-values.mjs to keep locale-translation-policy under max-lines. A DeepL cross-check of the whole changed-value list surfaced six more: - Progress labels had lost their 〜中: `Creating...` read 作成…, `Reopening...` read 再開…, `Thinking…` read 考え… - `Hide from sidebar` / `Show in sidebar` carried a stale 左サイドバーから Orca Mobileを削除 in one of six places, with the 和欧間スペース missing too - `Recent or tab strip.` was 最近のまたはタブストリップ。, which is not Japanese Everything else DeepL flagged was this PR's settled terminology (Agent, Issue, ホスト, 競合, fast-forward) or a fragment where DeepL had no surrounding context. * fix(i18n): treat proper nouns as brands, and cut the comment noise Review feedback, all verified against the catalog: - Hermes, PowerShell, Mermaid, Claude Code and VS Code are proper nouns, so they belong in BRAND_MISTRANSLATIONS with the other product names, not in the ja phrase fixes. Claude コードセッション, マーメイドダイアグラム and VS コードで開く were unfixed until now because nothing covered them. - Sweeping every proper noun against the catalog found more the same way: Orca IDE rendered as OrcaIDE, and Git had no 和欧間スペース in eight values, because neither term was in CJK_LATIN_SPACED_TERMS. - Settings-search keywords are lowercase, so the brand revert (case-sensitive) cannot reach them. windows read 窓, gitignore read ギティ無視, component read 成分 (the chemistry sense), compose read 作曲する, and neovim/hermes/powershell were transliterated. Pinned by value. - The override sources still spelled エージェント in 34 places even though the shipped value is Agent, so the file no longer said what it produced. The 代理人 rule also ran after the Agent rule, so a future MT 代理人 would have stayed katakana; it now maps straight to Agent. - Two of my own overrides had no matching English source left and were dead. Comment volume is cut from 80 added lines to 25. The locale modules carry 2-8 comment lines each, and this PR was running an order of magnitude over that; what is left is one line per genuinely non-obvious constraint. BRAND_MISTRANSLATIONS moves to its own module to keep locale-translation-policy under max-lines. * fix(i18n): give every English string one Japanese rendering 215 English sources had two or more Japanese forms in the catalog, so the same button read 削除 in one place and 削除する in another. Several of the variants were also wrong outright: Hide read 隠れる, Sort read 選別, Run read 走る, and "Don't ask again" read 二度と聞かないでください. Picks follow the catalog majority — action labels drop する, completion notices use 〜しました, status labels are 体言, and デフォルト / フィルター / スコープ / ディストリビューション / 並べ替え win their pairs. A value override is keyed on the English string, so one entry makes every occurrence agree. Three key overrides contradicted the value they now share and were realigned; seven pairs remain and are deliberate, where the same English is a different thing per call site (Cursor the product vs the caret, Open the PR state vs the action, Forward the port vs the browser button). Grab mode picks a page element and hands it to the AI, but read 掴む as a button and グラブモード in the web-client notice while the rest of the feature said 取得. Also from CodeRabbit: a single dotted, colon or bracketed token — button.primary, a:hover, wsl.exe, localhost:3000 — is code whether it names a selector, a file or a host, so it is preserved too. That caught localhost:3000 reading ローカルホスト:3000. And an override still spelled `Agent 、` with a space before the Japanese comma. The unified map lives in locale-ja-unified-values.mjs to keep the override file under max-lines. * fix(i18n): scope the catalog change to ja, and drop the zh-only Terminal form The ja brand list carried 端子 as a Terminal mistranslation, but 端子 is the zh rendering and never appears in ja — the Japanese one is 端末, which the phrase fixes already rewrite to ターミナル. Listing 端末 here instead would be wrong in the other direction, because this list reverts to Latin. The round-5 expectation moves to 端末 → ターミナル, which is behaviour that can actually occur. The ko/zh/es identifier restorations are pulled back out; they are real bugs (pr-view read PR视图, pnpm install read pnpm 설치) but they belong in their own PR rather than a Japanese one. One zh line has to stay: without it, verify-localization-catalog refuses the new stale-agent-row-{{value0}} entry because repair would rewrite the Chinese text to English. That value is a toast dedup id, not copy. * chore(i18n): regenerate the Japanese catalog on the current base The branch point moved forward 68 commits, which added 45 keys to en.json. The catalog is rebuilt from that base so the repair run stays idempotent, and the one string the new keys left in English is translated. * fix(i18n): preserve the code strings rendered inside <code> and font-mono @smwbev scanned by call-site context rather than value shape — a translate() that renders inside <code> or a font-mono element is code — and found values the shape-based list missed. Reproduced against the catalog and fixed here: - {prompt} read {プロンプト}. It is the substitution token for the commit-message prompt template, so a translated one never substitutes. - /goal read /ゴール, which is not a slash command. - npm run dev read npm 実行開発, in the same font-mono placeholder role as pnpm install. - nbformat read nbフォーマット. upstream and upstream/main were already covered. The remaining values in the scan break in zh rather than ja, but the entries are locale-agnostic, so orca.yaml, LIN-329, GH #1799 and orca · zsh are pinned here too and #13124 restores the Chinese catalog. * fix(i18n): repair Japanese translations of code, CLI, and URLs Code samples, CLI arguments, URLs, and template variables must remain executable. Adds to NEVER_TRANSLATE_VALUES to prevent future mistakes, fixes ja.json mistranslations, and adds regression tests. --------- Co-authored-by: m4air <m4air@m4airs-Air.localdomain> |
||
|
|
69ca0154b6 | fix(git): bypass WSL login shells for status reads (#13207) | ||
|
|
6858e072cf |
fix(terminal): agent pane auto-launch lost under fish + Starship (STA-3417) (#12840)
* fix(terminal): extend the shell-ready startup barrier to fish (STA-3417) Fish never emitted the OSC 777 shell-ready marker, so agent launch commands were written into the PTY while fish/Starship were still initializing: the daemon path wrote them synchronously at session create and the local path blind-wrote ~30ms after the first output byte. The command was echoed by the kernel but never executed. - shell-templates: shared fish --init-command that emits the marker once on the first fish_prompt event (the earliest point fish's own reader owns the PTY, mirroring zsh's zle-line-init marker) - daemon shell-ready: fish joins the startup barrier so the launch command queues until the marker (timeout fallback unchanged) - local-pty-shell-ready: fish launch config gains the marker wrapper - codex-startup-delivery/tui-agent-startup: omp/pi/opencode plans now request shell-ready delivery (codex parity) so the SSH renderer path also waits for the prompt; plain payload-free codex stays on the markerless fast path * fix(terminal): answer DA1 past the shell-ready barrier The barrier queues all inbound input until the ready marker, including the renderer's DA1 reply. A shell that withholds its first prompt until DA1 is answered — fish waits 10s — therefore never emits the marker that would release the reply it is waiting for. Measured: 10.37s to launch an agent, versus 0.35s once the reply lands. Answer DA1 from the daemon while the barrier holds, writing straight to the subprocess so the reply bypasses the queue, and consume the query so the renderer's xterm cannot also reply. Released on ready, timeout, or dispose, handing DA1 back to the renderer for steady state. Consolidates the identical DA1 handler the ConPTY override already used. * fix(terminal): prevent duplicate startup DA1 replies |
||
|
|
850342a3e0 |
fix(ci): run the root-directory guard on stock macOS bash 3.2 (#12879)
* fix(ci): run the root-directory guard on stock macOS bash 3.2 The guard script builds its base-tree lookup with `declare -A`, which needs bash 4+. Its test spawns plain `bash` from PATH, and stock macOS has shipped /bin/bash 3.2 since 2007, so on any Mac without a Homebrew bash the script exits 2 before asserting anything and the default `pnpm test` suite fails 3 of the guard's 4 cases. Machines with a Homebrew bash on PATH never see it, which is why it went unnoticed. Replace the associative array with a plain-array linear scan. Root directories number in the dozens, so the O(n^2) membership check is negligible, and the NUL-delimited reads that protect unusual filenames stay as they were. The empty-array expansion is guarded for `set -u` under bash 3.2. All four guard tests now pass with /bin/bash 3.2; behavior under CI's bash 5 is unchanged. * fix(ci): run the root-directory guard under node instead of bash The guard is the only check in the repo written in shell, and it used `declare -A`, which stock macOS `/bin/bash` 3.2 does not have — so the guard's own test suite failed 3 of 4 cases on any Mac without a Homebrew bash. CI never noticed because runners ship bash 5. Porting it to node removes the interpreter-version variable instead of working around one construct: node is what the sibling script in this directory already uses, it is the runtime that runs the test, and the NUL-delimited read is the same shape as check-changed-code-quality.mjs. It also drops a latent false pass — a failing `git ls-tree` inside the shell's `< <(...)` was not caught by `pipefail`, so the read loop saw nothing and the guard reported success. `execFileSync` throws instead, which is why the two `git rev-parse --verify` probes are no longer needed. Output and exit codes are otherwise unchanged; the usage line now prints node's script path where the shell printed `$0`. Tests pin each guarantee and fail when it is reverted: NUL-delimited reads so odd paths are reported unmangled, exit 2 on bad usage, and git's own 128 with no node stack trace when a sha does not resolve. * fix(ci): keep root entry bytes intact and fence guard output git pathnames are arbitrary bytes, but the guard read ls-tree with encoding 'utf8', so every invalid sequence collapsed to U+FFFD. That mangled the reported name and, because the replacement is not injective, let two different entries compare equal — a genuinely new root entry could be waved through as pre-existing. Read the bytes as latin1 and write them back unchanged. The blocked-entry list is also attacker-controlled and went straight to stdout. The runner trims leading whitespace before matching '::', so an indented entry name still parses as a workflow command, and a pathname may embed a newline. Wrap the list in ::stop-commands:: with a random resume token so only the guard's own annotation is acted on. --------- Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> |
||
|
|
3ec48a74d5 |
Gate artifact publishing behind off-by-default capability (#13368)
* fix(artifacts): gate agent artifact publishing behind an off-by-default capability Public artifact sharing was reachable by any agent through `orca artifacts share`: the Artifacts settings toggle only controlled sidebar visibility, and nothing in the main process checked a capability before minting a public URL. Add `artifactSharingEnabled` (default off) and enforce it in ArtifactCloudService.share/update — before auth, network, or the share-record write — so the CLI, relay-forwarded remote CLI, and IPC paths are all denied. The denial carries a stable `artifact_sharing_disabled` code plus next steps through the RPC error allowlist, so the CLI prints actionable guidance. list, unshare, and delete stay ungated: turning publishing off must not strand already-published links. The capability is absent from the `settings.update` RPC schema, so an agent cannot grant it to itself — only the desktop UI can. Co-authored-by: Orca <help@stably.ai> * fix(artifacts): gate agent artifact publishing behind an off-by-default Publishing is blocked until enabled in Settings → Artifacts. CLI preflights the capability before reading files to avoid unnecessary uploads. RPC surface rejects capability grants so callers cannot self-grant. UI shows opt-in workflow and recovery path when publishing is off. Web clients mirror the host's setting read-only. --------- Co-authored-by: Orca <help@stably.ai> |
||
|
|
2dc172f666 |
Support live toggle of agent status hooks with WSL relay gating (#13361)
* fix(agent-hooks): gate WSL relay reattach on agentStatusHooksEnabled Spawn only ensures the guest relay distro when agent status hooks are enabled, but reattach called ensureForDistro unconditionally — so a disabled setting reinstalled guest hooks on every local WSL reattach. Pass the same isAgentStatusHooksEnabled gate through all three reattach call sites as a required argument so a new site cannot skip it. Co-authored-by: Orca <help@stably.ai> * Gate WSL relay at manager level for live toggle support - Move agentStatusHooksEnabled check from reattach call sites to centralized isWslHookRelayAllowed gate - Add non-permanent dispose mode so manager can revive relays when setting is re-enabled - Watch setting changes and dispose live relays when agent status hooks are disabled mid-session * Restore WSL relays when re-enabling agent status hooks Extract guest install logic to `wsl-hook-relay-guest-install.ts` for modularity and add `resumeStoppedRelays()` to restart relays when hooks are re-enabled. Track distros stopped during a hooks-off teardown, but skip resuming those the user has shut down (which would unwantedly boot a stopped distro). Strengthen the disposed check with state identity to prevent respawning untracked relays. Abandon in-flight launches when hooks are switched off so no relay exists after opting out. --------- Co-authored-by: Orca <help@stably.ai> |
||
|
|
17cfc968cf |
Revert the terminal IME composition-ownership change (#13282)
* Revert "test(ime): restore coverage the composition-ownership change removed (#13168)" This reverts commit |
||
|
|
3e6b93f0d2 |
test(wsl): drive hook-relay reattach bench through real PTY spawn (#13260)
* test(wsl): drive hook-relay reattach bench through real PTY spawn Follow-up to #13139: stop calling ensureWslHookRelayForReattach from the benchmark and instead reattach a surviving WSL PTY via main's registerPtyHandlers path, so a missing or misplaced integration in pty.ts fails the bench. * refactor(bench): isolate reattach relay refresh measurement and verify s - Wrap benchmark in try-finally for reliable cleanup - Add jiti module graph duplication detection to catch missing pty.ts integration - Track relay refreshes only during reattach phase to avoid false positives from earlier phases - Disable agent-status hooks during PTY spawn (reattach path doesn't gate on them) - Improve error messages and make cleanup safe with optional chaining |
||
|
|
982570648a | fix(wsl): refresh hook relay on PTY reattach (#13139) | ||
|
|
17b3dff3c4 |
refactor(terminal): return IME composition ownership to xterm (#13128)
* fix(terminal): return IME composition ownership to xterm * fix(mobile): derive terminal input from native replacement ranges * test(mobile): record iOS Japanese IME traces * fix(mobile): preserve native IME replacement ranges * fix(xterm): flush queued application input after IME commit * test(terminal): pin Korean intermediate commit * test: pin Windows IME shortcut ownership * test: replay IBus number candidate commit * fix: preserve native macOS input-method punctuation * refactor(terminal): remove stale mac focus override * fix(mobile): preserve soft keyboard deletion ranges * fix: keep IME-owned palette chords in renderer * fix: stop carried IME shortcuts at renderer owner * fix: preserve carried IME shortcut dispatch * fix: narrow main-owned shortcut actions * test(mobile): pin Japanese IME replacement traces * test(terminal): retain paired native IME trace * fix(chat): preserve browser IME composition ownership * fix(chat): retain macOS IME confirm gesture * fix(chat): expire unmatched IME confirm carry * fix(chat): isolate IME confirmation expiry * fix(chat): retain active IME confirmation * refactor(terminal): remove dead composition handler * feat(ime): add shared Enter-ownership seams for CJK composition The confirming Enter of a CJK composition arrives as two keydowns and the orderings differ by platform: Windows/Linux redispatch the unmarked Enter/13 before keyup, macOS delivers keyup first. A guard reading only isComposing or keyCode 229 misses the redispatch, so surfaces submitted on a confirm. Adds useImeEnterGestureOwnership (carry token, next-frame expiry), a shared ImeEnterGuardedForm for native implicit submission, and the cmdk seam covering 18 CommandInput surfaces at one site. A chorded Enter arms the carry but is never swallowed — the reverse would eat a user's deliberate Cmd/Ctrl+Enter. Both failure modes are pinned by ime-enter-gesture-ownership-contract.test.ts. Co-authored-by: Orca <help@stably.ai> * refactor(terminal): consolidate native input listeners and parked-screen owner Extracts the shared native-input listener installer and renames the parked-screen detector for what it actually does, replacing per-call-site duplication. The listener installer keeps a forgetOptionKeyLocationOnBlur flag so per-window semantics are preserved rather than flattened. Net deletion; no behaviour change intended. Co-authored-by: Orca <help@stably.ai> * test(terminal): pin recorded IME shapes as regression tests Nine regression tests built from hashed affected-platform captures, each with a paired ordinary negative and a discriminating mutation verified to take the file from all-passing to exactly one failure. Covers the Windows MS-Korean Shift family (#12179, #11878, #12151, #11946, #12152) and the Korean TUI line-break rows (STA-3237, STA-3222, STA-3129). STA-3237 pins the empirical 3-Shift / 2-active-composition / 2-newline ratio the device run established — the third Shift produces nothing because Space has already committed. That ratio is not derivable from a static capture. Co-authored-by: Orca <help@stably.ai> * fix(ime): guard Enter-commit surfaces against CJK confirm Applies the Enter-ownership guards across the surfaces whose Enter commits something: publishes, clones, pairs, installs, posts, or persists. Tiered deliberately rather than uniformly. Irreversible and remote-effect sites take the carry token, which also blocks the unmarked redispatch. Locally reversible sites take the oracle check with a one-line comment naming the residual, because a spurious commit there costs one undo. Three numeric fields are left unguarded with the reason in-code: Chromium blanks number inputs at compositionstart, so a confirm-Enter only ever reaches an empty-draft reset. Measured with a CDP probe rather than assumed — a guard that cannot fire is noise. Co-authored-by: Orca <help@stably.ai> * test(ime): teeth-check the Enter guards on every guarded surface One suite per guarded surface, each verified by deleting the guard and confirming the test fails. A green guard test without that check is unverified, not verified. Two shapes pass vacuously in happy-dom and are avoided here: native implicit form submission never fires, and blur() is inert on an unfocused element. Both made "the commit did not happen" assertions pass with the guard removed, so the suites assert the guard's contract directly instead. Co-authored-by: Orca <help@stably.ai> * fix(mobile): keep iOS Korean commits whole through the live-input path iOS Korean reports isComposing: false on every event, so it bypasses the composition guard entirely. The strict owner rejected UIKit's transformed post-change field and sent only the leading jamo — the reported symptom. Prefers the authoritative same-event field text over the predicted text when the supplied operation cannot produce it. Generic: no Korean special-case, no locale classifier, no normalization. Adds the RN-target-keyed submit carry alongside it. Co-authored-by: Orca <help@stably.ai> * test(e2e): make IME capture harnesses fail loudly instead of silently Four instruments recorded silence as success, so a void run scored as a clean one: - readTerminalImeBoundaryTrace returned an empty trace when the probe never installed, making every "nothing leaked" negative pass vacuously - summarizeLatencies([]) returned a perfect zero distribution that passed all three latency thresholds - the macOS Vietnamese spec pinned an input-source ID that does not exist, and failed as though the operator had chosen the wrong source - the expectedLineCount=1 prefix property was undocumented and one edit from silently downgrading a PTY assertion Input sources now resolve by enumeration and name the near-matches on failure. Co-authored-by: Orca <help@stably.ai> * test(terminal): cover Cangjie cancellation and fix a cross-namespace assertion Adds #11951's recorded Cangjie cancel shape to the existing cancellation suite, which covered Pinyin and Sogou but not Cangjie. One keystroke then Backspace arriving as deleteContentBackward with data: null, so the stale preedit is the only thing a fallback could replay. Verified against the historical pre-6cd944c62b3 bundle: the positive fails with ['尸'] where [] is expected, while the ordinary negative stays green. Also fixes the Vietnamese spec, which asserted a TIS-space input-source ID against getKeyboardInputSourceId(). Those two Orca APIs report the same source in different namespaces — TIS nests it under VietnameseIM, the app API does not. The resolver stays as an installation precondition; the assertion matches the leaf. Co-authored-by: Orca <help@stably.ai> * test(e2e): add a real-IME macOS arm for the Korean chord commit The existing korean-ime-terminal-shift-enter-commit spec synthesizes composition over CDP: Input.imeSetComposition sets the preedit directly and Input.insertText performs the commit. Asserting the IME produced events you injected yourself is circular, so that spec cannot certify real-IME behaviour. This arm selects 2-Set Korean via TIS, reads it back live, and injects through System Events key codes, so the OS owns the preedit, the commit instant, and isComposing. PTY byte expectations are preserved verbatim. Covers 2 of the original 4 cases by design. The other two are the Windows/Linux redispatch-before-keyup ordering, which macOS cannot produce and which cannot be selected -- the OS decides it. Reintroducing synthesis to "restore coverage" would reintroduce the circularity. Co-authored-by: Orca <help@stably.ai> * test(e2e): assert the macOS chord arm at the PTY boundary, not the renderer The byte expectations were transcribed from korean-ime-terminal-shift-enter-commit :364/:383, which assert against onData -- a renderer boundary where the terminator is CR. This spec reads the PTY child, where the tty has already converted CR to LF. Names both forms per row rather than swapping the constant, so the conversion reads as evidence that the capture reached past the renderer, as #11936 and #11951 record. Ctrl+Enter's CSI-u sequence is unaffected and is identical at both boundaries. Co-authored-by: Orca <help@stably.ai> * test(e2e): measure composer-to-onData latency and stop dropping IME keystrokes Two defects in the echo latency probe. It hooked onWriteParsed and onRender but never onData, so it measured key->parse->render echo rather than the composer-vs-onData delta the latency rows need. Adds a third hook feeding its own sample set. And `event.key.length !== 1` silently dropped IME keystrokes: Pinyin and Cangjie keydowns arrive as key:'Process' (length 7). Replayed over the captured corpus, the old filter accepted 580 of 4137 Chinese IME keydowns -- it was discarding 80% of them. The new filter matches the shape the owner itself branches on. Attribution charges each onData to the latest keydown rather than a FIFO head, because composing jamo emit no onData at all and a queue would credit a whole composition to its first keystroke. The consumer now asserts sample count before any percentile, so a zero-sample run cannot render as a flawless distribution. Co-authored-by: Orca <help@stably.ai> * test(terminal): pin the WSL shifted-jamo newline shape for #11919 In Korean 2-set, Shift types ordinary letters -- the double consonants and the compound vowels. Each such keystroke reaches Chromium as key='Process', keyCode=229, shiftKey=true. The v1.4.163 classifier matched exactly that pattern with no code guard, so it called those keystrokes Enter, rewrote them to a synthetic Shift+Enter, and injected a newline into the middle of the word -- with no Enter key pressed. That is why the reporters said "no modifier key pressed": they had not chorded Shift+Enter, but they had pressed Shift, to type the double consonant. Asserts the row's own recorded capture: 40 immediate keydowns, exactly 3 of them Shift-carrying inside a single syllable, and an onData stream with one newline per Enter press and none mid-word. Two ordinary negatives keep it from being a blanket mute -- the same session's non-IME keydowns still reach shortcut policy, and an ordinary Shift+Enter still resolves through the real policy. Co-authored-by: Orca <help@stably.ai> * test(terminal): pin the composition commit lag that made Korean type one behind macOS Korean 2-Set commits syllable N only when the first jamo of N+1 arrives, so compositionend and compositionstart land in the same task. A composition-start handler cancelled the pending finalizer that was the only path to triggerDataEvent and ended the session without emitting bytes, so every committed syllable reached onData exactly one syllable late and the backlog cleared only at a Space or Enter. Types continuously with no Enter and no Space -- either would flush the backlog and hide it -- and samples onData at every syllable boundary. Paired with a length-matched ASCII arm that stays green throughout, so the positive is a fact about composition rather than about timing in general. Bisected to a single call site across five builds: pristine, 1.4.155 and 1.4.162 pass, 1.4.163 fails, removing the one call repairs it, restoring it fails identically. That window is exactly the reporter's "started immediately after updating". Co-authored-by: Orca <help@stably.ai> * test(mobile): cover the send-queue abort that silently drops queued keystrokes One failed send in use-terminal-live-input-commit aborts every keystroke queued behind it, with the error swallowed by .catch(() => false). The existing test resolves(true) on every send, so the failure branch was uncovered. Four arms: the abort itself, an ordinary negative on the healthy path, a throwing sender, and a liveness control proving the queue recovers once the chain settles. Deleting the abort takes 4 passed to 3 failed, with the ordinary negative correctly surviving. Scope is stated in the docblock: this is a transport send-queue abort, reachable only via a real disconnect or RPC error. REQUEST_TIMEOUT_MS is 30s, so latency alone cannot reach the branch — consistent with #7094's symptom class, not proven to be its cause. * test(terminal): pin that daemon snapshot/restore cannot disturb a composition Two independent reporters attributed broken Korean composition to the always-on PTY daemon repainting terminal state over the preedit. The attribution is wrong on ancestry — the daemon shipped three months before the version both call good — but the boundary was never actually tested. Runs the real applyMainBufferSnapshot choreography against a live composition, including the full 2J/3J/H wipe plus the resize and alt-screen branches. textarea.value, selectionStart/End, compositionView.textContent and .active all survive byte-identical, and interleaving a restore between every jamo of 문제 still commits 문제 at onData. Also pins that the uncommitted preedit is absent from the captured snapshot: it lives in the textarea, never the buffer, so a restore has nothing stale to echo back. Injecting one textarea.value = '' into the restore fails exactly the three restore-boundary tests. * test(terminal): pin that Cmd tears down a composition where Ctrl and Shift do not xterm's composition keydown exempts only keyCode 16/17/18 (Shift/Ctrl/Alt) plus 20/229. macOS Meta — 91/93/224 — is absent, so a Cmd press mid-composition takes _finalizeComposition(false): the overlay goes dark and never recovers, because compositionstart is not re-fired. The user composes the rest of the word blind. Linux and Windows users press Ctrl and are exempt. xterm already has a Meta-aware modifier predicate in wasModifierKeyOnlyEvent, so this is an internal inconsistency rather than a deliberate choice. Owns no reported row and is version-neutral: 5/5 on both 1.4.162 and 1.4.163. The branch is unexercised in all 328 recorded traces, so this is a hazard pin, not a regression guard. Only the teardown is asserted; the likely duplicated commit needs a compositionend the IME kept alive across the Cmd, which no capture contains. Deleting the exemption fails exactly the three paired negatives; adding Meta to it fails exactly the two Cmd arms. * test(native-chat): characterize preedit loss when a question card replaces the composer An AskUserQuestion card fully replaces the composer by design, but the in-flight composition goes with it: the composer unmounts before compositionend reaches it, so the preedit is never committed to the draft. The committed text survives only because the draft is cached and restored via defaultValue. Node identity changes, value 'abc' is preserved, the 가 is gone. Drives the real NativeChatView -> SessionGate -> InteractiveCard -> questionActive swap -> Composer -> ComposerField, flipped by writing the same store field an AskUserQuestion hook event writes. Flipping questionActive to false fails exactly this test and nothing else across 639 native-chat tests, so the path was entirely unguarded. CHARACTERIZATION TEST: it asserts the loss. Fixing the defect — committing the preedit before the swap, or keeping the composer mounted — will make this file fail. Update the expectations to the new contract rather than working around them. Owns no reported row. #12118/STA-3219 flicker is keyed to token counters, which provably do not remount, and a question card arrives once per question. * test(terminal): pin the duplicated commit when Meta interrupts a composition _finalizeComposition(false) sends textarea.value.substring(start, end) but cannot clear the IME-owned textarea, so a later compositionend re-sends the same range. Meta reaches that path because CompositionHelper exempts only Shift/Ctrl/Alt; xterm's own wasModifierKeyOnlyEvent covers Meta four ways, so the omission is an internal inconsistency rather than a choice. Companion to the modifier-exemption guard, which deliberately pins only the overlay teardown. This pins the data consequence. HAZARD PIN: owns no reported row. The trigger is unverified on hardware — no capture in the corpus contains a Meta-during-composition gesture, and whether macOS keeps the composition alive across it is unmeasured. The duplication follows from the code given that sequence; whether users reach the sequence is the open half. An earlier premise that Space (keyCode 32) reaches this path was refuted by a corpus scan: 0 of 731 evidence files carry a keyCode-32 Space while composing, against 171 at 229, and 229 returns early. * test(terminal): characterize the syllable lost when the textarea blurs mid-composition CoreBrowserTerminal._handleTextAreaBlur clears the helper textarea unconditionally — "Text can safely be removed on blur" — while CompositionHelper._finalizeComposition reads the committed text back out of that same value from a deferred timeout. By the time it runs the value is empty, the substring is '', and triggerDataEvent never sees the syllable. xterm checks composition state in _syncTextArea and omits the same check here. Six cases. Blurring mid-composition loses the syllable in every ordering, including compositionend-before-blur, which is Chromium's real order — so it is not an ordering artifact. A bare textarea.blur() with no Orca code loses it too, which places the owner upstream: Orca's unguarded release on outside pointerdown is one trigger, not the cause. Committing 한 then blurring mid-가 yields ['한'] where ['한','가'] is correct: one syllable gone, surrounding text intact. Teeth checked by inverting — adding an Orca-side composition guard flips exactly the three cases that route through the release path and leaves the bare-blur and no-blur cases green, which is the scope split: a fix in regular-terminal-focus-ownership alone would not close this. HAZARD PIN, but unlike the others this one has a real production injector — clicking outside the terminal mid-composition. Owns no reported row. The shape matches #9738's report; the injector does not, and a shape match with a mismatched injector is not an owner. * test(terminal): say which arm the STA-3237 fixture came from The recorded keydowns are wave 4's A-shift-unmarked-only — the arm that emits no PTY bytes. Nothing in the file said so, so two readers concluded the row's events fail the owner's predicate and that STA-3237 and STA-3222 were different defects. They share an owner; the arm that fires is Process/229+Shift, absent from this bubble-phase trace because the owner claims it in the capture phase. Also corrects "code-blind": the v1.4.163 policy emits \x1b\r only for a shift-only key:'Enter', and a jamo keydown reaches that branch solely via the isTerminalImeProcessEnter rewrite. The mock is deliberately wider so the ownership guard stays under test if that rewrite moves. Comments only — no assertion, fixture value, or mock behaviour changed. * test(e2e): track the input-source selector the macOS specs shell out to Five tracked macOS IME specs ran `swift .tmp/select-input-source.swift`, a file that is gitignored and existed only on one machine. Anyone else checking out the repo — or the same machine after .tmp is cleaned — could not run them, and they are the capture drivers for the macOS rows that are blocked waiting for exactly those runs. Moves it to tests/e2e/ beside its callers. The chord spec now resolves it from __dirname rather than reaching two levels up into .tmp. * test(terminal): pin the CJK repaint decision against the reporter's own output #12164 comment 1 and #5921 report agent output with double-width glyphs rendering duplicated character-by-character while ASCII in the same line stays clean. No IME, no composition, no keystroke — the user never types the CJK. Segmenting all three verbatim samples into maximal same-risk-class runs gives 33 runs and zero violations of "this run is corrupted iff the production detector flags it": 17 wide runs all corrupted, 16 narrow runs all byte-identical. The paired negative is co-located in the same line rather than in a separate run — the reporter supplied it without knowing. Doubling is asserted as present, not uniform: 자바스크립트 and 시스템 each leave a jamo undoubled, which is a repaint-region boundary artifact rather than a per-character transform. The discriminating arm is in the test rather than a source mutation: |
||
|
|
cf16eac7f6 |
fix(agent-hooks): keep Node 18 relay companion loadable (#13135)
Co-authored-by: Jinwoo-H <Jinwoo-H@users.noreply.github.com> |
||
|
|
bba32bd00c |
fix(daemon): let the publisher replace a dead endpoint, not a third party (#12882)
Terminals froze app-wide several times daily, needing a manual pkill. libuv unlinks the pathname a server bound to when it closes, with no ownership check, so a departing daemon deleted whichever socket then sat at the canonical path — including a live replacement's. The replacement kept hosting PTYs no client could reach. #12709 fixed that mechanism; this replaces the shape around it. Two invariants: only a daemon publishing itself onto the canonical endpoint may mutate that entry, and only by replacing one it has itself just proven dead; and no actor removes a name it did not create. Publish binds a private name, takes the canonical one with an exclusive link, and on EEXIST proves the incumbent dead by connecting before replacing it in a single rename. Only 'connected' means occupied and only refused/missing prove death — a timeout proves nothing and declines. Deletes the claim sweeper, the reclaim tail of killStaleDaemon, and three unfenced unlinkSync(socketPath) calls in the launcher. Measured: rename exposed no gap across 6,525 darwin / 8,004 linux probes of a live handover, where unlink-then-link gapped on 200 of 200. Verified on all three platforms: full suite on macOS and Linux, and daemon restart e2e on a real windows-2022 host. Contract in src/main/daemon/AGENTS.md. |
||
|
|
de4f272b31 |
fix(i18n): standardize Chinese status bar usage labels (#12881)
* fix(i18n): standardize Chinese status bar usage labels Signed-off-by: ousugo <dkzyxh@gmail.com> * fix(i18n): align Antigravity usage description Signed-off-by: ousugo <dkzyxh@gmail.com> * fix(i18n): standardize the zh status bar usage labels the menu actually renders The status bar item menu renders "<Brand> Usage" for eight providers. Claude, Codex and Gemini read 使用情况; Antigravity, OpenCode Go, Kimi, MiniMax and Grok read 使用量, so one dropdown showed two words for one concept. Register the decision where the repo already keeps it — the zh block of locale-value-overrides.mjs already pins Claude/Codex/Gemini Usage — so the repair pass enforces it instead of the catalog drifting again, and add the missing Kimi entry to BRAND_MISTRANSLATIONS so 基米 can no longer come back. --------- Signed-off-by: ousugo <dkzyxh@gmail.com> Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Co-authored-by: m4air <m4air@m4airs-Air.localdomain> |
||
|
|
bc1e049b3f |
fix(terminal): defer metric option writes to unmeasurable panes (#12944)
* fix(terminal): defer metric option writes to unmeasurable panes Writing fontSize/fontFamily/fontWeight/lineHeight makes xterm re-measure cell size against the pane's current box. A hidden or mid-layout pane can measure a wrong-but-nonzero size, which latches (hasValidSize) and mis-keys the shared WebGL glyph atlas until a manual resize — the stuck variant of the P0 bold/blurry-font reports. Metric writes now land only on measurable panes; otherwise the latest values park per-pane and flush on the next safe fit or reveal (with a refit on the light tab-resume path, which otherwise skips fitting). Measurability helpers move to pane-fit-measurability.ts to stay under the pane-fit.ts line cap. * fix(terminal): key metric deferral by terminal, not pane view getPanes() returns a fresh toPublicPane() wrapper per call, so a WeakMap keyed on ManagedPane never matched across call sites: deferred metric options were dropped, not deferred. Key on pane.terminal, which is carried by reference and dies with the pane. Also from review: - flushDeferredPaneMetricOptionsIfMeasurable checks the pending WeakMap before the measurability probe, so the common no-deferral case costs zero forced style/layout on every reveal. - applyTerminalAppearance skips the apply (and the probe) when all five values are already live and nothing is parked; any settings write re-runs the pass over every mounted pane, and arming a no-op deferral would trigger a refit on the next reveal. - fitRevealedPane flushes first: its pixel/grid checks can both no-op and return without fitting, stranding parked options. - Font zoom folds its direct fontSize write into any pending deferral so the flush inside safeFit cannot clobber the user's zoom. Corrects comments that asserted a cell-size re-measure mechanism xterm does not have: CharSizeService measures via OffscreenCanvas TextMetrics, independent of the pane box, and only fontSize/fontFamily re-measure. Test fixtures now allocate a fresh pane view per getPanes() call, which is what production does and what hid the keying bug. * fix(terminal): re-check the fit floor after a metric flush performSafeFit evaluated the min cols/rows gate with the pre-flush cell size, then flushed and fit unconditionally. A large font jump on a narrow pane passes the gate at the old size and lands under it at the new one, so fit() pinned the PTY to the tiny grid the floor exists to reject. Re-check after a flush that actually landed. The parked values still apply, so the pane is never stuck on stale metrics; only the fit is skipped. * fix(terminal): route a reveal metric flush through the stable fit fitRevealedPane's new flush branch called safeFit directly, which is exactly what the function's contract forbids on reveal: resumeRendering has just re-attached WebGL, whose cell metrics transiently differ from the DOM renderer's, so a raw fit can propose a one-column-off grid and reflow — and xterm's wrap/unwrap is not a perfect inverse, leaving a diff-painting inline TUI corrupted. A landed flush leaves pixels unchanged with a diverged grid, the same shape as a snapshot resize, so it takes the same steady-grid repair. A real resize still fits synchronously, after the flush. Reachable via window wake, which calls fitAllRevealedPanes with no pre-flush loop. * fix(terminal): gate metric writes on the pixel box, not the fit floor canApplyPaneMetricOptions reused canMeasurePaneForFit, whose >=8 cols / >=4 rows floor exists to stop a fit pinning the PTY to a sliver. But the divider clamp is 50px, which clears the 48px pixel floor and proposes ~5 cols — so a pane dragged to the clamp deferred every font change and never flushed: it never hides, and its box never changes, so no reveal and no ResizeObserver entry ever arrives. It rendered a stale font until widened, where pre-PR the write was unconditional. Gate metric writes on display plus the pixel box only. Hidden panes and the transient worktree-switch overlay are near-zero, so they still defer — the deferral's purpose is unchanged. The cols/rows floor stays on the fit, including the post-flush re-check in performSafeFit. Apply and flush share the same predicate, so no "applies but never flushes" state can open up. * fix(terminal): flush heavy reveal metrics after WebGL resume |
||
|
|
c9485fdded |
fix(computer): fence macOS HID coordinate clicks (#12981)
Co-authored-by: Jinwoo-H <Jinwoo-H@users.noreply.github.com> |
||
|
|
f2d62a7887 |
fix(i18n): localize the status bar Resource Manager tooltip and remote-host count (#12478)
* fix(i18n): localize the status bar Resource Manager tooltip and remote-host count The Resource Manager tooltip/aria label and the SSH segment's host count were assembled from bare English literals inside helper functions, so they stayed English under every non-English UI language while the labels around them translated. Route them through the catalog with _one/_other plural keys and whole-line messages (locales reorder and repunctuate the summary), and add en/es/ja/ko/zh entries. Root cause of the miss: audit-localization-coverage bailed on any ancestor binary expression whose operator was not `+`, which hid every string under a `cond && <JSX/>` guard or a `?? 'fallback'` — including this segment's 'Connecting…'. Only comparison operands are code, so keep `??`, `||` and `&&` walking, and localize the four real strings that surfaced. Co-authored-by: Orca <help@stably.ai> * fix(status-bar): flag the space-scan tooltip row instead of matching its English text The tooltip tinted a row with `line === 'Space scan ready'`, so routing that copy through the catalog silently dropped the tint in every translated build. Return `{ text, emphasized }` and let the segment read the flag. Adopted from #12439 by @smwbev. Co-authored-by: Evgenii <smwbev@users.noreply.github.com> Co-authored-by: Orca <help@stably.ai> * fix(status-bar): key Resource Manager tooltip rows by role instead of array index Co-authored-by: Orca <help@stably.ai> --------- Co-authored-by: Orca <help@stably.ai> |
||
|
|
8ddf575fe6 |
Revert "Remove source control group order preference (#12785)" (#12955)
This reverts commit
|
||
|
|
a7ffb244e4 |
perf(terminal): bound the reattach payload agent-signal scan (#12681)
hasCursorAgentReattachPayloadScreenSignal built a char-by-char copy of the entire reattach payload so it could read the last header plus 5000 chars. On a 2MB daemon snapshot that cost 17.5ms of synchronous renderer main-thread work — ~75% of what xterm then spends parsing the same bytes — and the miss case paid it in full for a result that is always false. Two changes, both matching existing in-tree precedent: bound the scan to a 256KB tail (as the kitty tracker already bounds its own scan), and strip via the shared precompiled CSI_SEQUENCE_PATTERN instead of a hand-rolled loop, which is also faster in V8 because it copies spans rather than building a rope per character. 2MB snapshot, header hit 17.5ms -> 0.80ms (22x) 2MB snapshot, miss 8.7ms -> 0.52ms (17x) 200KB snapshot, header hit 1.5ms -> 0.62ms (2.4x) config/scripts/terminal-reattach-payload-scan-benchmark.mjs reproduces this and asserts every candidate agrees with the baseline before timing it. It also records a negative result: porting the daemon mouse mirror's includes() pre-filter to the kitty tracker makes reattach slower, because snapshots always contain the introducer. Adds guards for the two behaviours a future shortcut would silently break: a CSI-split header must still match, and a header behind the tail bound must not. Also byte-pins POST_REPLAY_REATTACH_RESET_KEEP_MOUSE, which shipped unpinned. Co-authored-by: Orca <help@stably.ai> |
||
|
|
c2da0e47f9 |
fix(computer): deliver macOS coordinate clicks via the HID event tap (STA-3433) (#12839)
Mouse events posted with CGEventPostToPid reach the target app with no window association, so AppKit never routes the press to a view: hover states fire but the control is never activated, and the mouseUp is dropped outright when posted back-to-back. Post click events to the HID event tap instead (as keyboard synthesis already does), pace them, and stamp mouseEventClickState so multi-clicks register. Synthetic clicks now also report verification unverified/synthetic_input from the helper itself, matching the other synthetic actions. |
||
|
|
b0ba51831c | Add per-worker model and effort overrides (#12851) | ||
|
|
7da9368b78 |
fix(terminal): fence detached daemon endpoint ownership (#12709)
* fix(terminal): fence daemon endpoint ownership * fix(terminal): clean failed daemon PID claims * fix(terminal): close daemon ownership review gaps * test(daemon): release startup IPC in boot smoke * test(daemon): mirror production stdio in boot smoke * fix(daemon): exit after rpc shutdown cleanup * fix(terminal): make the socket name the daemon endpoint authority The reported failure was a live daemon hosting PTYs that nothing could reach: terminals acknowledged input and never ran it, listings diverged from reality, and restarting the app never helped because the detached helper survived. The ownership fence added for it could not fire in the sequence that produces the split brain. libuv unlinks the pathname a server bound to when that server closes, with no ownership check. A daemon that lost its endpoint name therefore deleted whichever socket then sat at that path — including a live replacement's — stranding a daemon that still hosted every session. Bind a private same-directory name and hard-link it into place instead: libuv can only ever unlink our own bind name, the exclusive link is a kernel-enforced endpoint claim, and the canonical name is removed only under an inode ownership check. The bind name replaces the basename rather than extending it, so it cannot overflow sun_path. killStaleDaemon removed the PID record unconditionally immediately before every fork, so the exclusive PID claim was always uncontested at bind time. It also unlinked a live daemon's endpoint whenever a connect probe merely timed out, and treated a `ps` timeout as proof of PID recycling. Now only positive evidence of a dead endpoint authorizes reclaiming it, SIGKILL is confirmed rather than assumed, and a daemon that cannot be proven stopped keeps its record and endpoint while the launcher refuses to fork beside it. A daemon whose endpoint was taken over now retires itself, draining rather than killing, so an unreachable orphan stops being permanent. A repaired PID record re-derives entryPath, appVersion and the Linux incarnation markers from the authenticated owner instead of dropping them; without appVersion a healthy daemon read as a permanently stale bundle and, on Windows, went unpinned against daemon-host pruning. Repair failure now fails open — abandoning a healthy daemon over a pid file write cost every persistent terminal on the machine. Also: treat only ENOENT as an unclaimed record so a Windows file lock is not reported as an ownership conflict; settle start() before close() so an accepted connection cannot defer it forever; sweep abandoned claim and bind names; and type the endpoint-identity seam so a rename cannot silently disable the fence. Adds a real-process handover smoke that reproduces the failure with two daemons racing one endpoint, and wires it into the native-smoke job. * fix(daemon): retire only on proven endpoint ownership loss The ownership watchdog read a null identity for any stat failure, so a transient EACCES or EIO on the runtime directory would retire a daemon that was still serving every terminal on the machine. Distinguish "the entry is gone" from "the probe failed" and act only on the former. Also require the loss to persist across two polls: a replacement publishes by unlink-then-link, and a single observation can land in that gap. * fix(daemon): source repaired ownership metadata from the authenticated hello Adversarial review found three defects in the previous two commits. Re-deriving entryPath from the owner's command line truncated it at the first space. A command line is a single space-joined string, so `C:\Program Files\Orca\...` and `/Applications/Orca 2.app/...` came back as `"C:\Program` and `/Applications/Orca`. getDaemonLaunchIdentity treats a present entryPath as authoritative, so a healthy daemon read as `different_app_path` and was killed and re-forked — worse than the missing-metadata case the derivation was added to fix. Carry entryPath and appVersion as optional fields on the daemon hello identity instead: the daemon already has both from its own argv, and per docs/reference/remote-wire-compatibility.md a new optional field is safe because every reader falls back when it is absent. This also removes a synchronous `ps` spawn from the Electron main thread during startup. `start()` rolled back the PID record even when it never published one. Losing the endpoint link now runs that path, and the ownership-checked unlink briefly renames the incumbent's record aside — enough to strand a live daemon's ownership. Roll back only what we actually wrote. publishDaemonSocketPath read its identity from the canonical name after linking, so a concurrent unlink returned null: no ownership watchdog and no endpoint cleanup on any shutdown path. Read it from the bound name before linking, which shares the inode. Refusing to fork beside an unconfirmed daemon left the user with no daemon at all and no in-app recovery, since restart re-entered the same fence. We have just proved something answers the endpoint, so adopt it in degraded mode: live sessions keep working, fresh terminals run locally. SIGTERM is also individually guarded now — an EPERM fell into the blanket catch and reported "nothing alive", authorizing the very duplicate this fence exists to prevent. Also reset the ownership-loss streak on an inconclusive probe so the confirmations are consecutive, and sweep scratch names before the launch so a failed launch still reclaims them. |
||
|
|
ae1ed5e886 |
Remove source control group order preference (#12785)
* Reorder source control to show staged changes first by default Stages are closest to the commit action and most relevant to the commit workflow. Merges untracked files into Changes visually while preserving their Git area. Removes the untracked-first preset and includes migration logic for existing user settings. * Drop source control group order user preference Remove the sourceControlGroupOrder setting and related UI, migrations, and persistence logic. The source control view now always displays sections in the order: staged changes, unstaged changes, untracked files. * Reorder source control to show changes before staged Aligns with the edit-stage-commit workflow by showing unstaged changes (active edits) before staged changes (queued for commit). |
||
|
|
4c49989c2e |
refactor(codex): delete the unreachable managed shared-mirror lane (#12614)
PR 9501 shipped real-home routing for the host system default, and the env override that could turn it back off was never a shipped control. The managed-account half of the shared runtime mirror has been unreachable since: every host account routes to its own self-contained CODEX_HOME before that code runs. Delete the flag module and its env plumbing plus the managed branch of syncForCurrentSelection and the six helpers only it called. The three lanes that still use the shared mirror -- Windows, a custom CODEX_HOME, and a hook-lane gate that reports unusable -- are untouched, as are every legacy migration and the WSL read-back helpers. |
||
|
|
38ba22ecd1 |
fix(browser): align cookie import safeguards (#12607)
* fix(browser): align cookie import safeguards * fix(browser): preserve sessions on failed cookie imports * fix(browser): bound single-label cookie replacement * fix(browser): preserve host-only parent cookies * fix(build): bundle cookie scope parser |
||
|
|
fde816e4ee | move folders (#12758) | ||
|
|
d72daf8153 |
fix(release): admit PTY consumer in SSH watcher isolation gate (#12754)
#12746 only delivers pty.data after an authenticated openClient grant. Update the macOS release harness to use a credentialed --connect client and open a legacy session-owner so the gate still exercises watcher isolation without timing out on the initial PTY echo. |
||
|
|
fb27702100 |
feat(updater): restart hourly build numbers per version, restyle the timestamp (#12587)
The number answers "which build of 1.4.163 is this", so carrying it across versions made it meaningless — 1.4.164 opened at 38 for no reason a reader could see. It now counts titles matching the base version being built, so a version bump restarts the series at 01. Deriving it moves from workflow jq into the script, because the number depends on the base version and only the script knows which base the published tags resolved to. Timestamps go from `07-31 13:54` to `Jul 31, 1:54PM`, still Pacific. Co-authored-by: Orca <help@stably.ai> |
||
|
|
4734428654 |
fix(i18n): stop the repair policy de-localizing CJK UI labels
The build-time repair layer carried overrides that rewrite already-correct CJK values back to English. Most are inert against today's catalogs but fire on the next regeneration, so they read as latent regressions rather than policy: - zh workspace status picker (Play/Flag/Zinc/Rose/Emerald/Amber/Violet/Sky/ Blue/Neutral) and `sheet`/`page` were pinned to English while every sibling option, and ko/ja/es, stay translated — half a Chinese picker. - The zh `蓝色的`/`琥珀色`/`中性的` phrase fixes correctly flagged the adjectival 的 form but replaced it with English instead of the bare color noun. - ja `Play` was pinned to English though the catalog already reads 再生. - A value-wide zh `Open: '进行中'` mapped every "Open" to "in progress", including the button that opens an MCP config file. "Open" is a verb (打开) on buttons and a state (开放) beside 已关闭, so no single mapping fits. Catalog corrections in the same area: - The GitHub/PR state picker key override read 진행 중 / 进行中 for ko and zh while ja already had the correct オープン; now 열림 / 开放, matching 닫힘 / 已关闭 on the sibling entry. - The terminal cursor-color group is the on-screen cursor, not the Cursor editor; ja already had カーソル, ko/zh now get 커서/光标 instead of "Cursor". - Tailwind swatch labels 天空 (the sky) and 锌 (the metal) do not read as colors; now 天蓝/锌灰, and ja 空 becomes 空色. - The ko disk-usage heading was pinned to bare "Space" while its own description says 저장 공간; both now use 저장 공간. Two policy tests pinned the Play de-localization. They diagnosed the input correctly — 玩 / 遊ぶ are wrong for a play icon — so the expectations move to 播放 / 再生 rather than English. Destructive drift (localized -> English on regeneration) drops from 40 to 21 for zh, 6 to 5 for ja, and 5 to 3 for ko. What remains is deliberate: search qualifiers, path and filename literals, and product names. |
||
|
|
f9a5920954 |
fix(i18n): stop repairCatalog crashing on un-bootstrapped locale keys (#11728)
en.json carries ~190 keys per locale that the locale catalogs have not been bootstrapped with yet, so every repair-locale-catalog run threw a TypeError before doing any work. Skip missing leaves instead. Split out of #11728 so the crash fix can land without the catalog regeneration, which still needs native-speaker review. Co-authored-by: Turtle-Hwan <turtlehwan@gmail.com> Co-authored-by: Orca <help@stably.ai> |
||
|
|
aa001101f2 |
fix(i18n): repair the stale SshTargetForm ko override
The override for SshTargetForm.137e88ce8d held a truncated relay-TTL sentence ending mid-clause at "최대:", which renders "Timeout after disconnect (seconds)" — the sibling key 55c56cf2c7 — not its own English source. #12192 only corrected the terminals token inside that wrong sentence. Point it at the value ko.json already ships, so a catalog repair cannot overwrite the correct string with the wrong one. |
||
|
|
a202d92772 |
fix(localization): stop the repair policy rewriting translated generic terms to English (#12192)
Fixes #12113. shouldPreserveEnglishValue keyed on the English value, so any key whose source string equalled a NEVER_TRANSLATE_VALUES entry was forced back to English on every repair run — agent, commit, repo, terminal and Continue were all on that list. Measured on a clean checkout: ko 279, ja 461, zh 1180, es 479 values rewritten, the large majority destroying translator work. 17 generic terms move into locale-generic-ui-terms.mjs, and the brand revert now skips a term's canonical rendering, so genuinely nonsensical forms (zh 回购, ja/zh 端子, es Comprometerse) still fire while 터미널/커밋/エージェント survive. Brand, path, and code tokens are untouched — MD -> 医学博士 and HEAD -> CABEZA are why that list still earns its keep. No catalog values change; every file is under config/scripts/. Co-authored-by: AnddyAgudelo <44873492+AnddyAgudelo@users.noreply.github.com> |
||
|
|
705e17a2a0 |
fix(i18n): correct semantic errors in Korean UI copy (#11169)
Nine ko values said something other than the English source. The riskiest is
SourceControl.6d7f2a47e5 "Discard folder", rendered as 폴더 삭제 ("delete
folder") next to a sibling delete-untracked action. Others: "Only branches Orca
named itself" read as "branches named Orca"; "staged changes" as 단계적
("phased"); "first-party cloud" as the mojibake 1方클라우드; "discard the
deletion" as "the deletion is deleted"; "Stage all changes" as a sentence
meaning "prepare"; and Recipes as 조리법 (cooking recipes).
EphemeralVmsPane.skillTitle is dropped from the PR's test and override — the
key was renamed to cloudVmSkillTitle on main, so the assertion would resolve to
undefined.
Co-authored-by: ShinSungkyu <kxu4583@naver.com>
|
||
|
|
5adc5d06c8 |
fix(i18n): pin the Orca Mobile "New" badge override and correct ja
The badge value is pinned in locale-key-overrides.mjs, so the ko/zh fix from #10664 would have been reverted by the next catalog repair. ja carried the same defect — 新規 reads as "create new" — and is corrected alongside. |
||
|
|
eed74724ac |
fix(i18n): localize automation contextual tour (#12270)
The shared Automation tour copy was rendered without passing through translate(), and the overlay surface hardcoded its default Next and Done labels. Copy is keyed off the step id rather than its position, so inserting a step ahead of them cannot shift the text onto the wrong step. Co-authored-by: 5Hyeons <ohs2251@naver.com> |
||
|
|
2548b816c0 |
Keep the app responsive when security software slows process creation (#12217)
* fix(ports): keep the app responsive when security software slows process creation Orca ran the workspace port scan's probe commands (lsof/ps on macOS, netstat + powershell.exe on Windows) directly in the Electron main process. libuv performs process creation inline on the calling event loop, which in the main process is the browser UI thread, so an endpoint-security module hooking CreateProcessW froze the whole window for the length of the spawn. The same stall also produced a false diagnosis: the 4s command watchdog was armed before execFile (local-workspace-port-scanner.ts:389 -> :410), so its deadline had already passed by the time the command started. Every scan on a hooked host reported a command timeout, tripping the 60s -> 5min backoff and the "Port scanning is temporarily paused after a command timeout" banner even though the commands themselves were healthy. Probe commands now run on a lazily created, unref'd worker thread with FIFO one-at-a-time dispatch, and the watchdog is armed after execFile returns so it measures the command rather than the spawn. Node's own execFile timeout kill (killed: true) is classified as a command timeout, keeping the backoff working for genuine hangs. A scan that observes a stalled spawn skips its optional metadata commands for that cycle, capping a hooked-host scan at roughly one stall instead of three. Closes #11161 * fix(ports): keep advertised URLs when a stalled spawn skips port metadata Review follow-up on #11161. The stalled-spawn early return handed scanWorkspacePorts raw ports with no cwd/commandLine, so every port failed attribution and reconcileAdvertisedUrls told the watcher each worktree's listeners had vanished. shouldEvictAfterScan then deleted every cached advertised URL and broadcast a removal event; those URLs are only ever captured from live PTY output, so the dev-server link was gone until the server restarted. The scanners now report metadataAvailable, and reconciliation is skipped for a scan that never gathered attribution evidence. The skip is also no longer self-perpetuating: on an EDR-hooked host every spawn stalls, so gating purely on the current scan's spawnMs made every port permanently external (Stop refused with 'Only workspace-owned local processes can be stopped here.'). Metadata is now re-probed on the scan after a skip, matching what the comment and test name already claimed. Co-authored-by: Orca <help@stably.ai> * test(windows): stop a temp-dir lock from failing the CLI launcher smoke test The native launcher assertions passed on windows-latest, but teardown's rmSync raced Windows' release of the image handle on the exe the test had just executed and threw EPERM, failing the job. Cleanup now retries and, on Windows only, tolerates a residual lock code instead of reporting it as a launcher regression. Co-authored-by: Orca <help@stably.ai> * fix(ports): scope the metadata skip away from attribution-dependent scans The metadata skip was a process-wide parity flag, so Stop and the localhost-label allowlist could land on a degraded cycle and reject a port the panel had just shown as workspace-owned. Give those callers an explicit requireMetadata option, and carry the previous cycle's listener metadata forward so a skipped background scan no longer republishes workspace ports as external. Also pin the watchdog ordering: the stall in the execution test was shorter than the watchdog budget, so a watchdog armed before execFile still passed. * build: guard worker-thread entries against electron imports (#11161) Electron's module is not registered on worker threads, so require("electron") throws "Cannot find module 'electron'" inside a main-process worker and kills it at startup (verified on Electron 43.1.0). plain-node-entry-guard covered only forked plain-Node entries, so the five worker entries relied on hand-written "must stay electron-free" comments. The port-scan probe worker is one import away from port-scan-command-client.ts, which deliberately contains require('electron'). A violation there fails closed at runtime while every unit test still passes, because the client's require is try/caught on the main thread. Covers stt-worker, warp-theme-parser-worker, session-scanner-opencode-sqlite-worker-entry, main-thread-hang-watchdog-entry and port-scan-command-worker-entry. The scan is transitive over the emitted chunk graph, so a shared chunk that reaches electron is caught too. Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com> * test(windows): retry teardown for main's duplicate-PATH launcher fixture Main's new csc-compiled harness runs an exe from the temp tree, which is exactly the image-handle/AV lock the merged-in removeFixtureTree retry exists for; its bare rmSync would report a teardown lock as a launcher failure. Co-authored-by: Orca <help@stably.ai> * test(ports): pin the packaged-asar worker entry path resolveWorkerEntryPath's packaged branch never runs in dev or e2e, so the path construction had no coverage. Split the electron read out of it and unit-test both layouts. Co-authored-by: Orca <help@stably.ai> --------- Co-authored-by: Orca <help@stably.ai> Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> |
||
|
|
e8d4818b5a |
test(env): keep happy-dom MutationObserver callbacks alive across GC (#12438)
happy-dom holds each observer's internal callback in a WeakRef, so a GC pause under sharded CI load permanently kills a still-connected observer. That is what made the activity portal readiness test flake: once the observer died, no amount of re-applying ready DOM could release the latch. Co-authored-by: Orca <help@stably.ai> |
||
|
|
50594c55a9 |
Stop the Windows Orca CLI from crashing when the environment carries both PATH and Path (#12218)
* fix(windows): stop the Orca CLI dying on a duplicated PATH/Path environment The packaged Windows `orca.exe` launcher read `ProcessStartInfo.EnvironmentVariables`, whose lazy getter copies the case-sensitive process block into a case-insensitive dictionary via `.Add`. An inherited block carrying both `PATH` and `Path` threw `ArgumentException: Item has already been added. Key in dictionary: 'PATH'`, so every `orca` invocation exited 1 before Electron started (native/windows-cli-launcher/OrcaCliLauncher.cs:46, printed at :67). The launcher now mutates its own environment with `Environment.SetEnvironmentVariable` and never touches either `ProcessStartInfo` env property, so `CreateProcess` passes a NULL environment block and the child inherits the live one verbatim. Orca was also minting the duplicate itself. `applyTerminalAttributionEnv` read `baseEnv.PATH` and unconditionally wrote `baseEnv.PATH`, so a Windows PTY that inherited `Path` got a second spelling; which one the child resolved was non-deterministic. `createLaunchEnv` did the same and, because its read always missed on Windows, shipped Agent Teams terminals a `PATH` containing only the tmux shim dir. `resolvePathEnvKey` (extracted from the existing precedent in windows-environment-path.ts) now drives every PATH read and write in the PTY env pipeline, and attribution collapses Windows onto the single OS-resolved spelling. Off Windows the resolver always returns `PATH`, so POSIX behavior is unchanged and a case-sensitive POSIX `Path` variable is never touched. Closes #12046 * test(windows): track the launcher's own-environment marker The #12046 fix moved ORCA_WINDOWS_PACKAGED_CLI_LAUNCHER and ORCA_CLI_COMMAND off ProcessStartInfo.EnvironmentVariables, but this asset test still pinned the old dictionary writes and failed. Co-authored-by: Orca <help@stably.ai> * fix(windows): follow the host block's PATH spelling on sparse daemon env patches Resolving a path-less Windows env to `Path` handed the daemon's own `{...process.env, ...opts.env}` merge both spellings when the host block spelt `PATH`. Fall back to the host block's own key, and collapse again inside the daemon since that merge happens after attribution. Co-authored-by: Orca <help@stably.ai> * fix(windows): resolve the live PATH spelling by block order, not casing Win32 resolves a duplicated variable by taking the first case-insensitive match in the block, so `resolvePathEnvKey`'s hardcoded `Path`-first preference targeted the shadowed spelling on the reporter's own `["PATH","Path"]` block. Drop the attribution-side collapse with it: it deleted the other spelling's value, and deleting the live key promotes the shadowed one, so an env that stripped down to empty lost both. * chore: drop unrelated merge formatting --------- Co-authored-by: Orca <help@stably.ai> Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> |
||
|
|
8ab7d8a110 |
fix(updater): base dev builds on published tags, not main's package.json (#12376)
main's version only moves on `release:` commits, and stable patches are cut from release branches that never merge back. On 2026-08-03 main read 1.4.165-rc.0 for twenty hours while 1.4.165, 1.4.166 and 1.4.167 all shipped, so every hourly built in that window was stamped 1.4.165-hourly.* while carrying code newer than 1.4.167 — and sorted below the stable its user was already running. Resolve the base from the main repo's published tags instead, taking the patch above the highest shipped stable. package.json stays a floor for the case where main leads the tags. Co-authored-by: Orca <help@stably.ai> |
||
|
|
c9c1683838 |
fix(ci): seed the adhoc release repo so releases can be tagged (#12375)
gh repo create leaves the repo empty, and publishing a release creates a git tag that needs a commit to point at. The adhoc build got all the way through notarization and upload before failing on "Repository is empty". Co-authored-by: Orca <help@stably.ai> |
||
|
|
f4b2b782b5 |
feat(orchestration): coordinator-driven release of settled worker terminals (STA-905) (#12355)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> |
||
|
|
128e3e335e | fix(packaging): prune non-target native binaries (#12174) | ||
|
|
339045b150 |
fix(runtime): coalesce concurrent host terminal focus (#11841)
Bound exclusive host navigation to a generation-aware latest-wins single-flight so bulk open and switch fan-out stay responsive on large remote fleets. Add freeze repro harnesses and navigated settlement. |
||
|
|
5887b36eff |
fix(updater): recover Linux .deb/.rpm installs that fail escalation (#12183)
* fix(updater): recover Linux .deb/.rpm installs that fail escalation A `.deb` install fails with `No authentication agent found` when the session has no polkit agent. Orca reported "Quit and reopen Orca, then try again" — wrong advice — and its only action was Retry Download, discarding a verified 160 MB package that was still in the updater cache. Keep the one-click install path, but make a failed root-package install recoverable without downloading again: - Retain the downloaded package and its expected SHA-512 from the `update-downloaded` event, mirroring electron-updater's cache-name rule. - Capture the child stderr that BaseUpdater logs but drops from the `error` event, redact it (ANSI, control bytes, `<home>`, `<package>`, `<user>`, 1 KiB cap), and classify the failure. Classification reads the original text — redaction can rewrite a matched phrase. - Send a structured `linux-package-install` recovery status and render a dedicated card: Copy Install Command / Try Automatic Install Again / Show Package. - Revalidate on every action: cache containment, lstat, streamed SHA-512, timingSafeEqual. Concurrent requests coalesce into one hash pass. - Build the command from fixed tokens plus one POSIX-single-quoted absolute path, resolving sudo and the package manager only from /usr/bin, /bin, /usr/sbin, /sbin. Orca never runs it. - Disable `autoInstallOnAppQuit` for .deb/.rpm so an ordinary quit cannot trigger the same failing escalation after the UI is gone. Extracts the error-card presentation into UpdateErrorCardContent so UpdateCard does not absorb another stateful surface. Lifecycle breadcrumbs carry package type, reason, exit code and version — never a path, command, username or raw child output. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Improve Linux package install recovery diagnostics - Distinguish invalid-package-path errors from missing package manager - Expand ANSI escape sequence stripping to handle OSC hyperlinks and DCS - Prevent generic error logs from overwriting specific diagnostic verdicts - Add error handling for shell.openUrl in update UI - Fix test isolation with proper afterEach hooks --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
8c5371ebad |
fix(worktrees): respect Windows shell for setup runners (#6967)
* Honor configured shells during worktree setup
* Align setup launch paths with selected Windows shells
* Carry setup shell selection through deferred launches
* Prove Windows setup shell routing at its real adapters
* Ground remote PowerShell proof in the real writer
* Preserve Git Bash across deferred setup launches
* Harden Windows setup runner shell selection
- Resolve remote PowerShell binary without local pwsh probe: for SSH/remote
Windows worktrees, isPwshAvailable() reflects only the LOCAL host, so an
'auto' implementation could route the remote runner to a pwsh.exe the remote
lacks. Add resolveSetupRunnerShell(..., { probeLocalPwsh: false }) so remote
auto keeps the always-present powershell.exe; explicit pwsh.exe still honored.
- Preserve native exit codes in the PowerShell runner by checking
$LASTEXITCODE before $?, so a failing native command surfaces its real code
instead of a generic exit 1; $? still catches cmdlet soft-failures.
- Write the PowerShell runner with a UTF-8 BOM so Windows PowerShell 5.1 (the
new default powershell.exe) reads it as UTF-8 instead of ANSI, preventing
non-ASCII setup-script corruption.
- Add unit tests for the remote-probe behavior.
* Restore setup-shell scope narrowing over the rebase
The force-pushed rebase dropped five review-fix commits that were already
on this branch; this reapplies their combined effect on top of the new
base and the hardening commit:
- Keep SSH setup shell selection remote-owned (no local terminalWindowsShell
or pwsh routing for remote hosts; supersedes the probeLocalPwsh guard)
- Preserve cmd setup compatibility outside POSIX shells (no .ps1 runner
family, so the BOM/exit-code hardening is no longer applicable)
- Route WSL setup runners from the project runtime
- Avoid blocking PowerShell probes during setup creation
- Correct SSH and WSL background setup fixtures
* Satisfy the changed-code gates for the setup-shell runner
- createWorktreeRunnerScript took 7 positional parameters, tripping the
changed-code max-params gate; move it to a single options object.
- hooks-runner.test.ts deep-equals the createSetupRunnerScript result, so
assert the cmd shell now returned for native Windows worktrees.
* Carry the setup launch shell through observed and issue runners
- buildObservedSetupCommand takes the runner's launch shell so WSL-routed
Windows-drive setup replays use /mnt/c instead of Git Bash /c
- resolveSetupRunnerShell gates the posix runner on the same Git Bash
resolution the PTY uses, so a missing or non-MSYS bash keeps the cmd runner
- issue-command runners carry their launch shell, and the renderer passes it
when building the queued command
- treat a bare `bash` shell setting as POSIX like `bash.exe`
Co-authored-by: Orca <help@stably.ai>
* fix(worktrees): close counsel P1 gaps for Windows setup shells
Route windowless/headless creates through the shell-aware setup runner when a
PTY controller is available, existence-check explicit Git Bash paths before
committing to .sh runners, thread the resolved shell into issue-command
runners, and document the intentional Git Bash interpreter flip with a narrow
scope table.
* Convert setup env to MSYS form and harden the bare cmd runner launch
C3: a Git Bash setup runner now receives ORCA_*/CONDUCTOR_*/GHOSTX_* path
values in /c/... form, matching the runner path and the shell's own HOME/PWD.
C5: extension-less `bash` resolves to Git Bash everywhere, matching how
resolveWindowsShellStartupFamily already classifies it.
C7: runner paths carrying characters that cannot be quoted on a cmd command
line launch through a delayed-expansion PowerShell shim instead, and the batch
runner disables inherited delayed expansion so `!` in setup lines survives.
Co-authored-by: Orca <help@stably.ai>
* docs: note MSYS ORCA_* paths and bare bash Git Bash resolution
Keep the setup-shell release note aligned with C3 env conversion and C5 bare
bash resolution so the published claim matches runtime behavior.
* revert: drop windows-setup-shell doc allowlist and AGENTS link
Keep the counsel P1/P2 product fixes without expanding the docs allowlist
or AGENTS.md guidance surface.
* fix(plugins): contain Parcel unsubscribe rejections under Vitest
Dev plugin watchers fire-and-forget unsubscribe, and in-process Parcel
can reject when temp watch roots are already deleted. Catch those
rejections so they cannot fail the suite as unhandled errors.
* fix(plugins): keep in-process unsubscribe rejection surface
Swallowing Parcel unsubscribe errors broke mocked unsubscribe tests
that return non-Promises and expect rejections. Contain failures only
in PluginDevWatcher fire-and-forget paths.
---------
Co-authored-by: OrcaWin <alpha-eng@stably.ai>
Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
Co-authored-by: Orca <help@stably.ai>
|
||
|
|
98ae8e4c8c |
Allow clearing all agents from AI Vault session history filter (#12128)
* Allow clearing all agents from AI Vault session history filter Add "Select all" / "Clear" buttons so users can quickly isolate one agent without unchecking each box individually. Previously, at least one agent had to remain enabled; now users can filter to zero agents and re-enable selectively. * Address PR #12128 review feedback - Make Select all / Clear real DropdownMenuItems so Radix roving focus reaches them by keyboard. - Rename the zero-agent empty state to a neutral "No agents selected" now that zero agents is a valid filter. - Use 모두 해제 for the Korean Clear label instead of 지우기 (erase). Co-authored-by: Orca <help@stably.ai> --------- Co-authored-by: Orca <help@stably.ai> |
||
|
|
484273844a |
feat(updater): add an adhoc release channel for branch builds (#12051)
* feat(updater): add an adhoc release channel for branch builds Hourly covers main. This covers everything that is not main yet: a dispatchable macOS build of an unlanded branch, published to stablyai/orca-adhoc, so the team can run an experimental feature for a few days instead of reasoning about it from a diff. Adhoc sits at the bottom of the version order — 'adhoc' < 'hourly' < 'rc' < stable — so no routine check can walk anyone onto somebody's branch; only an explicit pinned jump reaches one. It gets its own repo rather than sharing orca-hourly's, because a branch build must not appear in the list a developer riding main is looking at. Signed and notarized exactly like hourly, for the same reason: macOS anchors a notarized app's TCC grants on identifier + team, so an unnotarized build reads as a new client and silently loses file access under Documents/Desktop/Downloads. Tags stamp to the second rather than the minute. Hourly runs under a concurrency group and cannot overlap itself; adhoc builds are dispatched on demand, so two people cutting from different branches inside one minute is ordinary — and a minute-resolution tag would collide and fail the second build after its whole pack-and-notarize run. Channel-specific behaviour now derives from one DEDICATED_REPO_CHANNELS list: repo mapping, macOS-only support, and UpdateSource. The RPC schema that validates releaseChannelOverride was a hand-copied enum missing the new channel, which would have rejected the override on its way to the main process; it reads the predicate now. * fix(updater): merge the duplicated shared/types import Co-authored-by: Orca <help@stably.ai> * fix(ci): default the adhoc build ref to the dispatch branch The Actions UI puts its own "Use workflow from" branch picker directly above the ref field, and picking a branch there is what most people read as "build this". Making the field optional means the obvious action is also the correct one; naming a branch explicitly still wins, so main's copy of the workflow runs rather than a stale one on an old branch. Co-authored-by: Orca <help@stably.ai> --------- Co-authored-by: Orca <help@stably.ai> |
||
|
|
5e9186f5ff |
chore(mobile): import the shared marine-creature corpus instead of mirroring it (#12090)
mobile/src/constants/marine-creatures.ts was a hand-maintained copy of src/shared/marine-creatures.ts, identical except for a comment header. The copy existed because Metro only watched mobile/ and could not resolve repo-root modules; mobile/metro.config.js:11 added src/shared to watchFolders five weeks later, and ~195 mobile files already import from src/shared. The renderer collapsed its copy to a re-export at the same time; mobile was the leftover. Point the one consumer at the shared corpus and delete the mirror, the bespoke regex-scraping parity test that policed it, and the now-stale max-lines baseline entry. No behavior change: same exported symbol, byte-identical name list. |