mirror of
https://github.com/stablyai/orca.git
synced 2026-10-24 08:03:58 +00:00
split-github-client
8878
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
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> |
||
|
|
01bcc8dca2 |
fix(terminal): claim printable keydowns structurally so committed IME text survives (#13288)
* fix(terminal): show a preedit the IME resumes without a compositionstart Typing 2-Set Korean shows committed syllables but not the in-progress jamo, so the user composes each syllable blind. Long-standing hole in the vendored terminal library, not a regression: the same test fails identically against the bundle this branch starts from. The `.active` class that CSS keys `display: block` off is added only in `compositionstart` and dropped in `_finalizeComposition`. Some IMEs (observed on Windows/WSL Korean) resume a composition with a bare `compositionupdate` and no second `compositionstart`, by which point `compositionend` has already hidden the overlay, so the resumed preedit is written into a hidden element and never positioned. `updateCompositionElements` also early-returned on `!_isComposing`, so it would not lay the overlay out either. Re-show the overlay on an update that carries data, and key the layout guard on the shown overlay instead. `_isComposing` is deliberately left alone, so no commit bookkeeping changes and `onData` stays byte-identical. The two guards are equivalent on every pre-existing path: `compositionstart` sets both, `_finalizeComposition` clears both. The bundle hunks are the same two edits applied to the shipped minified output; the sourcemaps are carried through unchanged. * test(terminal): prove the resumed-preedit fix against a recorded Windows capture The synthetic test pins the shape; this replays events a real Microsoft Korean IME emitted on Windows/WSL. The capture holds three compositionupdates that resume a composition with no second compositionstart — the exact ordering that wrote the preedit into a hidden overlay. Without the fix all three report shown:false; with it all three are visible. Fixture derived from the sealed 11919-windows-wsl-current capture, which is read-only and unmodified. Co-authored-by: Orca <help@stably.ai> * test(terminal): stop the recorded Hangul fixture pinning a derivation artifact The capture logs each event twice — a dispatch record and a batched next-frame re-log. Deriving from both replayed every event twice, which made three compositionupdates appear to land after a session had ended. Filtered to dispatch records the capture holds zero resumes and 11 balanced sessions, so the previous toHaveLength(3) was pinning an artifact of the derivation. Re-scoped to what the capture does prove: the preedit stays visible across all 37 real updates. Verified by reverting the patch that this passes either way, so it is coverage and the synthetic test remains the discriminator. Both facts are now stated in the file. Co-authored-by: Orca <help@stably.ai> * fix(terminal): restore the preedit visibility patch onto its own branch The previous commit accidentally reverted it: checking main's patch and lockfile into the worktree to test whether a test discriminates also stages them, so the commit that followed swept them up. Co-authored-by: Orca <help@stably.ai> * fix(terminal): claim printable keydowns structurally so committed text survives Co-authored-by: Orca <help@stably.ai> * chore(reliability-gates): retarget the IME forwarding gate after the allowlist removal The gate listed terminal-ime-input-source.test.ts, which went with the input-source allowlist. Points at the substituted-text commit test instead, which covers what the gate is actually protecting: text committed outside a composition session reaching the pty exactly once. Co-authored-by: Orca <help@stably.ai> * docs(terminal): record why withholding a claimed keydown needs no timer The predicate withholds a keydown's byte until the commit arrives, so a key the IME eats without committing would be dropped. Measured across the recorded corpus that case does not occur, and the browser marks IME-owned presses on the keydown itself. Both facts belong next to the predicate rather than only in a handoff note, since the obvious fix for the imagined gap is a timer, and a timer here once wrote a newline the user never typed. Co-authored-by: Orca <help@stably.ai> * test(terminal): pin the kitty all-keys-as-escape-codes hole explicitly Flag 8 asks for every printable key as an escape code; this path sends the committed text raw instead. That is a deliberate trade, not an oversight, but it was untested — the suite only covered the disambiguate flag. Pinning it makes the choice visible and records the gate to use if it ever needs closing. Co-authored-by: Orca <help@stably.ai> * fix(terminal): keep the kitty key-release report for presses that reached the pty Claiming the keyup unconditionally suppressed xterm's release report. That was sized for the old design, which claimed only a short punctuation list; the structural claim takes every printable keydown, so on macOS an app that negotiated kitty report_event_types stopped seeing releases for ordinary typing and would treat every printable key as held down. Suppress the release only when the press put nothing on the wire — swallowed by the input source, or owned by a composition transaction. xterm emits nothing from keyup unless kitty report_event_types (or win32 input mode) is on, so letting it through is inert everywhere else. Co-authored-by: Orca <help@stably.ai> --------- Co-authored-by: Orca <help@stably.ai> |
||
|
|
a47b9d1167 |
Show a preedit the IME resumes without a compositionstart (#13284)
* fix(terminal): show a preedit the IME resumes without a compositionstart Typing 2-Set Korean shows committed syllables but not the in-progress jamo, so the user composes each syllable blind. Long-standing hole in the vendored terminal library, not a regression: the same test fails identically against the bundle this branch starts from. The `.active` class that CSS keys `display: block` off is added only in `compositionstart` and dropped in `_finalizeComposition`. Some IMEs (observed on Windows/WSL Korean) resume a composition with a bare `compositionupdate` and no second `compositionstart`, by which point `compositionend` has already hidden the overlay, so the resumed preedit is written into a hidden element and never positioned. `updateCompositionElements` also early-returned on `!_isComposing`, so it would not lay the overlay out either. Re-show the overlay on an update that carries data, and key the layout guard on the shown overlay instead. `_isComposing` is deliberately left alone, so no commit bookkeeping changes and `onData` stays byte-identical. The two guards are equivalent on every pre-existing path: `compositionstart` sets both, `_finalizeComposition` clears both. The bundle hunks are the same two edits applied to the shipped minified output; the sourcemaps are carried through unchanged. * test(terminal): prove the resumed-preedit fix against a recorded Windows capture The synthetic test pins the shape; this replays events a real Microsoft Korean IME emitted on Windows/WSL. The capture holds three compositionupdates that resume a composition with no second compositionstart — the exact ordering that wrote the preedit into a hidden overlay. Without the fix all three report shown:false; with it all three are visible. Fixture derived from the sealed 11919-windows-wsl-current capture, which is read-only and unmodified. Co-authored-by: Orca <help@stably.ai> * test(terminal): stop the recorded Hangul fixture pinning a derivation artifact The capture logs each event twice — a dispatch record and a batched next-frame re-log. Deriving from both replayed every event twice, which made three compositionupdates appear to land after a session had ended. Filtered to dispatch records the capture holds zero resumes and 11 balanced sessions, so the previous toHaveLength(3) was pinning an artifact of the derivation. Re-scoped to what the capture does prove: the preedit stays visible across all 37 real updates. Verified by reverting the patch that this passes either way, so it is coverage and the synthetic test remains the discriminator. Both facts are now stated in the file. Co-authored-by: Orca <help@stably.ai> * fix(terminal): restore the preedit visibility patch onto its own branch The previous commit accidentally reverted it: checking main's patch and lockfile into the worktree to test whether a test discriminates also stages them, so the commit that followed swept them up. Co-authored-by: Orca <help@stably.ai> --------- Co-authored-by: Orca <help@stably.ai> |
||
|
|
eacba6654f |
fix: pr-bug-scan validated finding from #4213 (#4311)
* fix: address pr-bug-scan validated finding from #4213 On per-file read/size error, skip the file and continue merging instead of returning found:false; preserve first error for the all-failed case. * add tests --------- Co-authored-by: orca-bug-scan-bot <orca-bug-scan-bot@stably.ai> Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com> |
||
|
|
e20845eedb |
fix(cursor): accept BOM-prefixed hook JSON (#12652)
* fix(cursor): accept BOM-prefixed hook JSON * test(cursor): pin the hook BOM allowance to one leading U+FEFF Document why the BOM strip exists and cover the narrowness the fix claims: a double BOM, a whitespace-then-BOM prefix, and a BOM inside the JSON body are all still rejected. --------- Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> |
||
|
|
8331fe5995 |
docs: update GitHub star history chart (#13380)
Refresh README star history graphic to the latest ~40K stars curve (through August). |
||
|
|
427f178a75 | fix(mobile): load the repo list in GitHub Project mode (#12972) | ||
|
|
68b6e9f70d |
Reland canonical PTY hydration without evicting live legacy rows (#13132)
* fix(terminal): restore canonical terminal rows from unified sessions During session hydration, non-canonical terminal rows (closed/stale tabs) were being restored alongside canonical ones. Filter to only canonical terminal rows from unified sessions, treating legacy rows as terminal backing data only. * fix(terminal): preserve independent legacy PTY rows * fix(terminal): stop canonical PTY filter from evicting live legacy rows Two regressions in the hydrateWorkspaceSession canonical-PTY filter: 1. canonicalPtyIds was built before isValidTerminalTabId ran, so a web-client mirror row with a "::" id seeded its PTY into the set and then evicted the valid local row owning the same PTY - wiping every terminal in the worktree and orphaning the daemon PTY, persisted back to disk on the next write. 2. `.some()` dropped an entire legacy split tab for a single overlapping leaf, discarding panes whose PTYs nothing else referenced. Only rows that survive the id check now claim ownership, and only pure duplicates (every PTY already canonical, guarded against PTY-less rows) drop. * fix(terminal): deduplicate rows with correct orphaned PTY handling - Extract row hydration to terminal-session-row-hydration.ts - Distinguish mounted vs orphaned PTY bindings when scoring ownership - Strip shared PTYs from retained rows to prevent duplicate reconnection - Follow dropped row focus to canonical twin; clean up orphaned agent sessions * fix(terminal): clear sleeping-agent records for invalid-tab-id rows Rows with invalid tab IDs are dropped like subsumed rows but were not getting their sleeping-agent records cleaned up. Extract common test mocks into a bootstrap file to reduce duplication. * fix(terminal): anchor retained rows on remaining leaf PTYs When a canonical row claims a retained row's tab-level PTY, the row still owns leaf-level PTYs in split panes. Anchor on the surviving leaf PTY so the orphan sweep doesn't delete the row before its pane reattaches (#10486). Also cleans up sleeping agent sessions for released leaves and improves PTY categorization. * persisted ptyIdsByLeafId keys are unvalidated strings, and makePaneKey throws on non-UUIDs. |
||
|
|
63af0bdd24 |
feat(agents): add Prime Agent as a supported TUI agent with session history (#12935)
* feat(agents): add Prime Agent as a supported TUI agent with session history Wire Prime Intellect's prime-agent CLI (a Pi fork) into the desktop and mobile agent catalogs following the Trae registration pattern, and into the Agent Session History browser following the OMP pattern: - types.ts, tui-agent-config.ts: register 'prime-agent' with argv prompt injection behind a `--` separator (its own help documents `--` as "treat all following arguments as messages"; without it, prompts starting with `help`/`agents`/`-…` dispatch as subcommands or flags), plus csi-u Shift+Enter encoding matching the Pi TUI it embeds. - agent-kind.ts, telemetry-events.ts, agent-status-types.ts, agent-type-label.ts, tui-agent-display-names.ts, tui-agent-selection.ts, skills-cli-agent-keys.ts: standard per-agent registrations. - agent-headless-command.ts: `-p/--print` one-shot runs share the print-mode matcher with Claude/Trae so they are not mistaken for live interactive panes. - agent-process-recognition: the npm shim launches a generic bundled cli.js, so only the exact package path is an authoritative identity (same as Pi and cursor-agent). The three per-agent regex branches are now one table in agent-node-entrypoint-identities.ts — the module was at its max-lines budget and a table makes the next agent one entry. - AI Vault: sessions are Pi's message-graph JSONL under ~/.prime/agent/sessions (override PRIME_AGENT_CODING_AGENT_DIR — Prime Agent brands Pi's env contract instead of sharing PI_CODING_AGENT_DIR); parsed by the shared message-graph parser with incremental append-resume; discovered locally, in WSL homes, and over remote SSH; resumes by absolute transcript path (`prime-agent --resume <path>`) like OMP, with session-id fallback. - skill-discovery-sources.ts: ~/.prime/agent/skills home source. - Catalog, i18n (en/es/ja/ko/zh), mobile registries, and a bundled 64x64 favicon (required by mobile's offline-icon invariant). Scanner-test fixtures for OMP and Prime Agent move into session-scanner-test-fixtures.ts and the incremental fixture into its own module, keeping every touched file inside its max-lines budget without ratchet bumps. * fix(ai-vault): map custom Prime Agent roots to their sessions child PRIME_AGENT_CODING_AGENT_DIR is consumed verbatim by the CLI as its agent config dir, with transcripts always in <agentDir>/sessions — unlike PI_CODING_AGENT_DIR's <home>/agent/sessions shape the shared normalizer models. A custom root with a non-special basename (or a `.prime` leaf) was therefore scanned as-is instead of its sessions child. Dedicated normalizePrimeAgentSessionsDir appends `sessions` to every configured root, taking only an explicit `.../sessions` path as-is; the shared Pi/OMP normalizer drops the `.prime` widening it no longer needs. Raised in review on #12935. * fix(ai-vault): guard degenerate Prime Agent roots and cover the remote source normalizePrimeAgentSessionsDir stripped a filesystem-root value ('/' or '//') to '', which then joined into the relative root 'sessions' and would walk the main-process cwd. session-scanner-roots.ts already carries this guard for the OMP variant; apply the same fallback here. The remote SSH source had no test: deleting jsonlSource('prime-agent', ...) left the suite green, unlike the local path which is pinned by the AI_VAULT_AGENTS exhaustiveness assertion in session-scanner.test.ts. Add a case that fixes the .prime/agent/sessions root segments, the .jsonl extension, and parser routing. Raised in review on #12935. * fix(ai-vault): honor Prime Agent's sessions-root env and non-interactive modes Verified against upstream PrimeIntellect-ai/prime-agent source rather than inferred from the CLI's help text. config.ts getSessionsDir() reads PRIME_AGENT_SESSION_DIR (and its legacy PRIME_AGENT_CODING_AGENT_SESSION_DIR alias) ahead of the agent dir and uses it verbatim; setting either left the vault silently empty. It also appends `sessions` to the agent dir unconditionally, with no basename escape hatch, so PRIME_AGENT_CODING_AGENT_DIR=/data/sessions writes to /data/sessions/sessions while Orca scanned /data/sessions. getAgentDir() and the session-dir override both run through expandTildePath, so a `~` value set outside a shell resolves. cli/args.ts also spells the non-interactive runs `--mode json|rpc|acp|daemon`, which the shared print-mode matcher does not know, so those panes were counted as live interactive agents and the paste-submit path would write user text into a JSON-RPC/ACP stream. Match upstream exactly: only the space-separated form, since `--mode=json` is not parsed by the CLI and does start the TUI. Raised in review on #12935. * fix(ai-vault): keep Prime Agent roots absolute and remote segments posix Two holes in the previous commit. The degenerate-root guard only rejected pure-separator values, so a relative env value still resolved against the main-process cwd: PRIME_AGENT_CODING_AGENT_DIR='.' scanned '<cwd>/sessions' and, worse, PRIME_AGENT_SESSION_DIR='.' scanned the cwd itself. Require an absolute path in both branches and fall back to the default otherwise. remotePrimeAgentSessionsSegments() built its segments with the local-platform join, so on a Windows client scanning a posix SSH host it produced '\.prime\agent\sessions' and split('/') collapsed it to one bogus segment — remote discovery would have found nothing. Remote roots are posix regardless of client platform, so keep them literal. Pi and OMP are unaffected: their normalizer returns a '.../sessions' input unchanged and never joins. Raised in review on #12935. * test(ai-vault): pin Windows drive roots to the Prime Agent default fallback 'C:\' and 'C:/' strip to the drive-relative 'C:', which isAbsolute rejects on every platform — assert they land in the default fallback so a looser truthiness check can't reintroduce a 'C:sessions' scan root. Raised in review on #12935. * test(ai-vault): pin the drive-relative root form and state what the posix runner can assert --------- Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> |
||
|
|
45c1cb979a |
fix(orchestration): release context-only dispatches (#13376)
* fix(orchestration): release context-only dispatches Refs #13005 * test(orchestration): align PTY readiness timeout --------- Co-authored-by: Jinwoo-H <Jinwoo-H@users.noreply.github.com> |
||
|
|
98cc730289 |
fix(terminal): keep the reattach grid push alive for a hidden pane (#13155)
* fix(terminal): keep the reattach grid push alive for a hidden pane A restored Floating Workspace pane is display:none for its whole PTY reattach, because the panel always mounts closed after a restart. The display:none guard in safeFitAndThen dropped the reattach continuation outright, so the PTY never received the client grid and no explicit SIGWINCH was sent to repaint the restored TUI after the replay. Parking the continuation is not an option: the structural replay coordinator holds live PTY bytes behind its promise, so a closed panel would stall output indefinitely. Settle the promise immediately instead and move the continuation to a per-pane deferred bucket that the first measurable fit drains. Deferral is opt-in so the bounded-degradation contract still covers every other caller, and the continuation re-checks mobile PTY ownership at fire time now that the wait is unbounded. * fix(terminal): preserve replacement deferred grid push |
||
|
|
383665ebb8 |
Taskpage pure extract (#13367)
* refactor(task-page): extract pure task-kind, jira, and pagination helpers Moves seven closed sets of pure helpers out of TaskPage.tsx (13485 -> 13308 lines) into domain-named sibling modules. Function bodies are byte-identical cut/paste; the only production edits in TaskPage.tsx are the removed blocks and the new import statements. - task-page-github-task-kind.ts: isPRFocusedTaskView, normalizeGitHubTaskPreset, getGitHubTaskKind, getDefaultPresetForGitHubTaskKind, scopeGitHubTaskSearch - task-page-jira-create-fields.ts: the Jira create-field visibility, allowed-value, and payload builders - task-page-jira-project-selection.ts: getJiraProjectSelectionKey, compareJiraProjectsByDisplayLabel - task-page-jira-status-tone.ts: getJiraStatusTone - task-page-pr-delta-summary.ts: formatPRDelta - task-page-pagination-page-numbers.ts: getPageNumbers - task-page-string-set-equality.ts: areStringSetsEqual Each module gets a characterization test suite that pins current behavior, including the quirks (case-sensitive matching, truthiness-based option payload fallbacks, allowedValues winning over schema type). Quirks are documented, not fixed. github-enterprise-slug-routing-boundary.test.ts anchored its source-text sections on `function formatPRDelta` and `function getPageNumbers`; both moved, so the sentinels advance to the next declarations. The bounded sections and their assertions are unchanged. No intentional behavior change. * refactor(task-page): clarify helper logic and consolidate tests - Add comment explaining quoted-form parsing in GitHub task scope - Refine test descriptions and characterization comments for clarity - Add test case for quoted `is:"issue"` form in task scoping - Consolidate test files as part of pure-function extraction * improvements * Remove obvious comment from getPageNumbers The function name and implementation are self-documenting; the comment restates what the code already expresses clearly. |
||
|
|
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 |
||
|
|
b09f7bd431 |
fix(terminal): wait for shell-ready before every SSH startup command (#12963)
* fix(terminal): wait for shell-ready before every SSH startup command Renderer-delivered SSH launches wrote the startup command after a flat 50ms, so a remote shell still reaching its prompt could drop it. Only Codex plans opted into waiting. Gate on whether a startup command exists rather than on which agent it names: an unready shell drops whatever is written to it regardless. The relay already gates its own delivery on the marker it armed; this mirrors that for the renderer path, bounded by the existing 1.5s fallback. * fix(terminal): arm SSH marker for every startup * fix(terminal): cancel stale SSH startup writes |
||
|
|
43bad61a93 |
fix(terminal): deliver SIGWINCH to the PTY foreground process group (#13164)
* fix(terminal): deliver SIGWINCH to the PTY foreground process group A real window-size change is delivered by the kernel to the terminal's foreground process group, but Orca signalled the pty's root pid. The root is never in that group: the shell setpgid's away for job control, a foreground TUI forms a third group again, and on macOS the root is login(1), which neither handles nor forwards SIGWINCH. So the explicit reattach repaint signal reached a running TUI on no platform, and on macOS reached nothing at all. Resolve the tty's tpgid and signal the negated group instead, pinned to the slave device captured at spawn so a recycled pid cannot aim at a real terminal. Scoped to SIGWINCH: destructive signals keep the narrower root-pid target and the descendant-sweep identity machinery. POSIX-only, so Windows keeps its current behavior where a negative pid is invalid and SIGTERM means terminate. Applied on the relay too, so SSH hosts get it; that is host-local with no wire change. * fix(terminal): bound foreground group lookup latency * fix(terminal): preserve SIGWINCH fallback on signal errors |
||
|
|
c19bac80d6 |
fix(terminal): restore the floating workspace open and maximized (#13258)
* fix(terminal): restore the floating workspace open and maximized
The panel's open and maximized flags were never persisted, so every restart
dropped the user into a closed, default-sized panel that they reopened and
re-maximized by hand. On a typical window that is 113 columns to 211, and
113/211 is the ~55% left band in the bug report.
That column jump reflows the xterm buffer. Rows written at the narrow width
carry wrapped continuations that unwrap into the wide grid, leaving
interleaved tails and stacked status lines under a live relative-cursor TUI.
The live region recovers on the next repaint; the reflowed scrollback never
does. Correct PTY sizing cannot undo it, so the fix is to not make the jump.
Persist both flags, restore maximized geometry in the bounds initializer so
the first paint is already final, and hold the panel's terminals until the
viewport settles - the window restores its saved bounds and only then
maximizes, so mounting earlier fits terminals to a grid it is about to leave.
* fix(terminal): stop a boot-time flag read from wiping the restored open state
Settings hydrate asynchronously, so floatingTerminalEnabled reads false on
every boot before it resolves. The feature-off effect force-closes the panel,
and that close was being persisted - overwriting the user's restored open
preference with a value they never chose.
Measured on a real restart: storage ended as {"maximized":true,"open":false}
after a session where both were true, so the panel came back closed and the
restore did nothing.
Persist only while the feature is enabled, which is the only state in which
the value reflects a real user choice.
* fix(terminal): only force-close the floating workspace on a hydrated flag-off
The feature-off effect fires on every boot while settings are still
undefined, so it closed the restored panel before the real flag value
arrived. The previous commit stopped that close from being persisted, but
the React state was still discarded, so the panel never actually reopened.
Measured on a real restart: storage kept open:true and maximized:true, yet
the workspace stayed closed. Gate the close on settings having hydrated -
only a real flag-off is a disable.
* fix(terminal): treat null settings as unhydrated in the floating-panel gate
The settings slice initializes to null, not undefined, so the hydration
selector read "hydrated" on the very first render and the feature-off
close still fired at boot - measured again as storage keeping open:true
while the panel stayed closed. Check for null.
* fix(terminal): preserve floating restore bounds after restart
|
||
|
|
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> |
||
|
|
9a6c5cddc9 |
fix(agent-hooks): exit Windows hooks when Orca env is missing (#11568)
* fix(agent-hooks): exit Windows hooks when Orca env is missing * test(agent-hooks): pin the Windows missing-env exit as a deliberate stdin handoff The Windows-only executable matrix still asserted every managed .cmd drains its writer, which the missing-env fast exit intentionally stops doing. That assertion never runs in CI (vitest is ubuntu-only), so it would only fail on a Windows dev machine. - Accept a broken writer for .cmd hooks on the missing-env path; keep the no-error contract for the PowerShell and Git Bash hooks that still read stdin first. - Correct the drain-epilogue comment: claude-hook.cmd is the remaining in-script jump to it. * docs(agent-hooks): cite #11549 and the stdin-ownership trade in the guard comment * test(agent-hooks): stop overstating what the live matrix pins runHookProcess always closes stdin, so the executable case cannot reproduce the abandoned-pipe hang; say so and name the guard strings as the real gate. Also record that the Devin skip guard still routes to the drain above these guards, so #11549 stays reachable for Devin outside an Orca pane. * docs(agent-hooks): record why Windows batch diverges from the POSIX stdin policy The two templates now answer the same question opposite ways and nothing states the rule, so name the constraint (batch has no bounded stdin reader) and the evidence (POSIX callers have not shown the abandon; Windows callers have). * test(agent-hooks): stop the live matrix name and comment from over-claiming The renamed case covers two missing-script launchers that still read to EOF, so 'without stranding a reader' was false for them. Payload size was also the wrong reason for the .cmd exit — the statusline script buffers the same size through more.com; the real constraint is that any read-to-EOF hangs on an abandoned pipe. Also record that the broken-writer relaxation is only sound because hookEnvironment() strips every ORCA_* var, since the branch keys on .cmd. * test(agent-hooks): gate the rule, not the three guards that exist today The revert pin was three exact string denials, so a fourth ORCA_* guard — copied from the nearest in-repo pattern, which still routes to the drain — would have reintroduced #11549 with the suite green. Assert instead that no ORCA_* guard in a generated .cmd routes to the more.com drain. Verified both directions: reverting the source fails it, and adding a simulated ORCA_TAB_ID guard that gotos the drain fails it too. Does not match the Devin skip, which is not an ORCA_* guard and whose caller is a live Orca pane. * docs(agent-hooks): name the exceptions so the stated rule is not self-contradicting The previous wording set a rule that claude/statusline-script.ts breaks 25 lines away in the same export surface, and said nothing about the Windows .ps1 and Git Bash hooks. State the rule for generated .cmd, then name every hook that still reads first and why — the read is the payload capture, not a drain. Batch is the exception because it streams to curl, so owning stdin buys nothing there. * test(agent-hooks): close the if-not-defined bypass in the drain gate The guard-spelling regex only covered `if "%VAR%"==""`, but `if not defined VAR` is equally house idiom here — every Windows hook opens with `if defined ORCA_AGENT_HOOK_ENDPOINT`. A fourth guard written that way routed to the drain with the suite green. Match the whole line instead of one spelling, which also covers `goto` without its colon. Probed all eight cases: the five bypass spellings are caught, and the Devin skip, the endpoint refresh, and the fixed guard are correctly exempt. * docs(agent-hooks): separate the two reasons a hook is exempt from the .cmd rule The list conflated them. POSIX, copilot .ps1 and kimi .sh read first because the read is the payload capture. claude/statusline-script.ts does route its guard to the drain — it is exempt because its caller closes stdin, which the in-pane capture already proves. Grouping them under 'the read is the capture' would let a future author reach the right conclusion from the wrong premise. * fix(agent-hooks): let the missing-Orca-env exit outrank the Claude/Devin skip claude-hook.cmd checked DEVIN_PROJECT_DIR before the Orca env guards, and that check jumps to the more.com drain. Outside an Orca pane the caller is not Orca's PTY, so it can abandon stdin and leave more.com waiting forever — the #11549 hang this guard reordering is meant to close. Both branches end the hook, so the only behavior that changes is Devin outside an Orca pane: it now exits 0 instead of parking in more.com. In-pane Devin still reaches the skip and still owns stdin. Depends on #11568. * refactor(agent-hooks): cut the review additions back to house idiom The three-line guard fix needed a three-line comment, not a thirteen-line taxonomy adjudicating three other files. Swap the hand-rolled drain gate and broken-writer set for the combinators already used repo-wide: not.toMatch (94 uses) and toContain on a literal. --------- Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> |
||
|
|
371cc26ca1 |
fix(settings): emit Windows font family names as UTF-8 (#12602)
* fix(settings): emit Windows font family names as UTF-8 Windows PowerShell 5.1 can write localized font names with the console code page while Node always decodes stdout as UTF-8, which garbles Korean and other non-ASCII family names in the font picker. Force UTF-8 OutputEncoding before enumerating InstalledFontCollection (#12590). * test(settings): assert UTF-8 pin precedes Windows font enumeration Lock script order so OutputEncoding is set before InstalledFontCollection enumeration, preventing a silent regression of the mojibake fix. * refactor(settings): cut the Windows font UTF-8 pin to the standard shape `$OutputEncoding` only governs bytes piped to a native executable's stdin; this script pipes to ForEach-Object, so it was inert. Drop it, and drop the script-builder export whose only consumer was a test — the one-shot `-Command` shape now matches windows-foreground-process-rows and ssh-browse, while the BOM-less `UTF8Encoding::new($false)` spelling matches powershell-osc133-bootstrap and antigravity/hook-service. The test reaches the script through the public listSystemFontFamilies path and pins the assignment as the script's first statement, so it fails on removal, on a stdout write above it, and on a swapped encoding. --------- 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> |
||
|
|
d3cb02f6a9 |
fix(grok): carry reasoning effort for models outside the seed catalog (#13365)
Launch resolves options from the static seed, so a discovered model id had no options and silently dropped --reasoning-effort. unknownModelOptions keeps the effort menu for those ids. Leave the multi-host launch gate unchanged. |
||
|
|
082cc31703 |
Exclude idle current tabs from Cmd+J recent; add live attention badges (#13299)
* Show attention badges on recent chats in Cmd+J palette Keep current tabs visible only when they have a scannable badge (working, permission, unread, done). Snapshot unread maps alongside status maps to freeze recent-section membership on open instead of churning with live updates. Unify badge logic across tab strip and palette, and extract test fixtures for reuse between suites. * Exclude idle current tabs from Cmd+J recent; add live attention badges Current tabs no longer appear in Recent Chats when idle—only working, blocked, or unread agents keep the current slot visible. `done` no longer admits current tabs; the user watched it complete on screen, so that slot goes elsewhere. Add live attention badges to recent rows (working/permission/unread/done), matching the tab-bar ladder. Freeze recent-section membership at open-time to keep row order and inclusion synchronized instead of changing live. * minor fix |
||
|
|
394e4bf1c0 |
Polish Artifacts management UI (#13356)
* refactor(artifacts): polish artifact management UI * fix(artifacts): address UI polish review --------- Co-authored-by: Jinwoo-H <Jinwoo-H@users.noreply.github.com> |
||
|
|
bd9addb449 | fix(tasks): restore GitHub page and scroll position on reopen (#13096) | ||
|
|
34f2a62cda |
fix(pty): stop color-scheme 997 replies from painting cooked prompts (#13309)
Route cooked-echo-risk terminal replies through bounded echo-safe delivery across local, daemon, and SSH relay PTYs. Preserve repeated valid replies, bypass the daemon startup input gate, and keep ordinary input plus latency-critical replies on their existing paths. Closes #13137 Co-authored-by: bbingz <zzb@gxsmjx.com> |
||
|
|
9f7522dee9 | fix(workspace): stabilize smart entry keyboard flow (#13319) | ||
|
|
970696a008 |
fix(sidebar): show Cursor rows and stop a stray "claude" title hijacking OpenCode (#12466)
* fix(sidebar): show Cursor rows and stop a stray "claude" title hijacking OpenCode Two defects in the same title-resolution path. **#10258** — Cursor's only native OSC title is the literal `cursor agent`, which both title trackers dropped unconditionally. A hookless Cursor pane therefore had neither a status entry nor any title carrying Cursor identity, so the worktree card showed nothing at all. **#8940** — two owner-blind paths let an incidental `claude` token anywhere in an OpenCode session or task title outrank the pane's known owner, so the tab icon and sidebar row flipped to Claude Code. #10258: let the literal through exactly once as identity, so a restored or mobile tab keeps its Cursor row instead of vanishing. #8940: require an *identity frame* — after stripping status decoration the title must PRESENT Claude, not merely mention it — before a Claude title may reclaim a pane from its prior identity, and make the sidebar row builder owner-aware. > These two are in one PR because they share the `ownerAgentType` plumbing through `buildTitleDerivedAgentRow` — split apart, neither half compiles on its own. Fixes #10258 Fixes #8940 Co-authored-by: Orca <help@stably.ai> * test(e2e): add recordable proof for sidebar-agent-row-identity Fails on origin/main, passes on this branch. Test: sidebar keeps a Cursor pane visible and an OpenCode pane out of Claude Code hands Co-authored-by: Orca <help@stably.ai> * fix(terminal): preserve restored Cursor identity * test(terminal): cover restored Cursor redraw suppression * refactor(terminal): tighten Cursor identity handling and Claude frame matching Review follow-ups on the title-resolution path: - pty-transport dropped a native Cursor literal that main emits whenever a non-Cursor title preceded it, re-introducing the #10258 blank row in the renderer path. The pre-filter now projects the predecessor the drain will actually see, and defers to the drain gate while facts are still queued. - applyTrackedPtyTitle threaded the cursor flag through 12 sites, including ptyRecordChanged bookkeeping the sole caller ignores. Force the status null once, and the activity-gated effects fall out unchanged. - isClaudeIdentityFrameTitle missed a multiplexer-wrapped Claude title ("zsh | Claude Code"), costing a genuine Claude pane its identity. Reuse the ' | ' segment split that agent-title-owner already had inline. - Keep title normalization on launchAgent: it only rewrites within an identity group (OMP wraps Pi), so a split does not make it wrong, and the hook-row path normalizes the same way. - Drop the tab.ptyId tracker fallback, which read a pty that the pane identity check had just rejected. Co-authored-by: Orca <help@stably.ai> --------- Co-authored-by: Orca <help@stably.ai> |
||
|
|
ea8881a3c7 | fix: keep cross-project dialog actions in bounds (#13317) | ||
|
|
c6ded160b2 |
Revert the Korean Won to backquote mapping (#13312)
* Revert "fix(terminal): detach the Korean input-source probe when its setting goes off (#13283)" This reverts commit |
||
|
|
78f434dd85 |
fix(agents): deliver grok launch drafts on its composer frame (8s → 0.7s) (#13308)
* fix(agents): deliver grok launch drafts on its composer frame
Grok has no --prefill-style flag, so a launch draft (e.g. the issue URL of
a worktree created from a GitHub issue) always goes through Orca's
paste-after-ready path. That path used the default readiness signal: DECSET
2004 plus 1.5s of PTY silence. Grok shimmers its startup logo at ~12fps
until the session opens, so the quiet window never settled and the draft
fell through to the 8s hard timeout before it appeared in the composer.
Gate grok on its own composer glyph instead, anchored on the alternate-screen
switch rather than DECSET 2004: the shell that runs the launch command emits
2004 too, and its prompt may itself be the same glyph (starship, pure), so a
Codex-style anchor could paste into the shell. Grok keeps the quiet window
armed as a fallback because it renders differentially and paints the glyph
once, so a late-attaching scanner would otherwise wait out the hard timeout.
Measured against grok 1.0.0 driving the real scanner over a zsh -> grok PTY:
draft delivery moves from 8003ms to 689ms, with the URL landing unsubmitted
in the composer exactly as before.
* fix(agents): keep grok's quiet-window floor on DECSET 2004
The composer-glyph marker is anchored on the alternate-screen switch, but grok
can render inline (`--no-alt-screen`, `--minimal`, `[ui] screen_mode =
"minimal"`), where 1049h never arrives. Anchoring the quiet-window fallback
there too left those launches with no delivery path at all: readiness never
resolved, and the main-process caller drops the draft when it resolves null —
so the issue URL vanished instead of arriving late.
Give the signal two independent anchors: the marker still waits for the
alt-screen switch (so a starship/pure shell prompt can't trip it), while the
quiet window arms off DECSET 2004 exactly as the default signal does. Inline and
legacy-Windows-console launches keep their pre-existing timing; alt-screen
launches keep the fast marker path.
Verified on grok 1.0.0 over a real zsh -> grok PTY: alt-screen delivers at 687ms
via the marker, inline at 1949ms via the quiet window (the default signal
measures 1861ms on the same launch), URL landing unsubmitted in both. Adds a
recorded inline-mode trace fixture so the no-1049h path stays covered.
* fix(agents): revoke grok's alt-screen anchor when the screen is handed back
The composer-glyph anchor latched forever: once \x1b[?1049h had been seen, any
later `❯` counted as grok's composer. Two ways that pastes the launch draft into
the user's shell instead of into grok:
- grok enters the alternate screen and then dies before painting a composer;
the shell prompt that follows is `❯` under starship or pure.
- a pager or editor started from the user's shell rc enters and leaves the
alternate screen before grok is ever launched, arming the anchor against the
shell's own prompt.
Track the anchor in stream order instead of as a latch: \x1b[?1049l revokes it,
re-entering re-arms it, and a marker only counts inside a segment where the
anchor is actually held. The chunk is walked segment by segment so ordering
within a single PTY packet is honored, with a 7-char carry — one short of the
escape sequence — so a split sequence rejoins without re-walking scanned output
into a second transition. Signals with no `markerAnchorEnd` (codex, opencode,
the default) keep their existing latch semantics untouched.
Also makes the trace-replay test model the hard timeout: the real waiters settle
at 8s, so a marker landing after that is not a delivery time.
|
||
|
|
e172a51649 |
test(terminal): assert preedit visibility across the recorded IME traces (#13286)
Two IME defects shipped past this suite because every assertion here was about bytes reaching the PTY. A preedit written into a hidden overlay types blind and still satisfies all of them. Samples the composition overlay at each recorded compositionupdate and requires it to be shown, so the existing recorded corpus now covers what the user sees rather than only what the shell receives. Co-authored-by: Orca <help@stably.ai> |
||
|
|
5a84dbb564 |
fix(terminal): detach the Korean input-source probe when its setting goes off (#13283)
Gating the prefetch call site stopped the probe from ever attaching, but not from surviving. prefetchKoreanInputSource installs global focus/keydown/keyup listeners behind an idempotent listenerAttached guard, and the only teardown was test-only — so disabling the setting mid-session left the listeners live and still spawning defaults export | plutil | plutil on keyboard activity. Syncs in the effect body rather than its cleanup: cleanup also runs on tab switches and on any dep-identity change, so disposing there would detach and re-probe constantly. Also replaces the initial-probe abort guard with an epoch. It compared window identity, which is dead code in production where window is a singleton, so a stop/start could let a sleeping probe loop wake and race the new one. 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 |
||
|
|
17eefef502 |
[Tabs] Preserve host routing and reduce search churn (#13114)
* fix tab search host routing and churn
* Fix open-tab search to resolve hosts from worktree when active host unkn
- Use worktree.hostId to resolve execution host instead of defaulting to LOCAL_EXECUTION_HOST_ID
- Correctly populate search results for remote-only worktrees when activeWorkspaceExecutionHostId is null
- Remove automatic focus of terminal tabs after search activation
* Prevent stale tab results when user keeps typing ahead of deferred searc
- useOpenTabSearch now returns {query, results} to track which query the results describe
- Gate tab results on query match so stale results don't appear on user's screen
- Add live region (role=status) for accessibility of tab switch error messages
- Distinguish missing-worktree from missing-page errors in browser page activation
- Improve host resolution to prefer active host when worktree and repo don't specify one
* Re-pin entry to deferred tab results that rank higher
Track whether selection auto-follows the top-ranked result or was
manually positioned. Re-pin entry to tabs when they rank higher,
but preserve manual selection.
* Consolidate browser focus requests and simplify selection state
- Extract requestBrowserFocus to handle queueing + event dispatch atomically
- Simplify omnibox selection tracking with single pinnedOptionId state
- Optimize host resolution in tab search to compute once per query
* Report dead browser workspaces correctly and fold dedupe case by host
Two readiness-checklist fixes for open-tab search:
- Browser page activation checked page/workspace before the worktree, but
deleting a worktree purges its browser workspaces and pages too, so a dead
workspace surfaced as "Browser page no longer exists". Check the worktree
first; routing already maps missing-worktree to the workspace wording.
- Editor-tab/file dedupe compared paths with separator normalization only, so
a Windows worktree offered both "Switch to tab" and "Open file" for the same
path in different case. Fold by the worktree path's syntax via the new
isCaseInsensitiveRuntimeRoot, keeping WSL, POSIX and SSH roots case-sensitive,
and add NFC so a macOS NFD listing matches an editor's composed path.
* Fix tab deduplication and resolve worktree host collisions
- Only editor tabs should suppress file entries; check contentType instead
of relying on path being empty for non-editor tabs.
- Add executionHostId to simulator search results to disambiguate when
the same worktree id exists on multiple execution hosts.
|
||
|
|
8a773a5e3f |
Focus search inputs for immediate typing (#13264)
* Focus search inputs for immediate typing - Autofocus inputs in AutomationListSearchField, SettingsSidebar, and WorktreeParentPickerPopover - Only autofocus Settings search when opening directly, not via deep-link - Use modal mode and explicit focus management in popover for proper restoration - Forward CommandInput ref and add autofocus test coverage * Restore focus when closing worktree parent picker popover - Find the nearest focusable ancestor of the anchor row to restore focus to instead of letting it drop on the detached input element - Simplify focus assertion in AutomationListSearchField test to verify actual focus behavior rather than autofocus attribute presence |
||
|
|
05c30166f4 |
fix(ci): stop hourly prune from deleting the just-published release (#13266)
* fix(ci): stop hourly prune from deleting the just-published release Hourly prune sorted non-draft releases by createdAt, but nearly every orca-hourly release shares one createdAt from bulk import. At the retain cap, stable sort + reverse put the newest release past the window and immediately deleted it with --cleanup-tag. Sort by publishedAt (tagName as tie-break) and hard-skip the tag this run just published so prune cannot self-delete. * fix(ci): harden hourly prune protect without retain+1 drift Review found that skipping only in the delete loop under-prunes when sort is wrong, and excluding TAG before the retain slice would permanently keep retain+1 releases. Force this run's tag to the front of the sorted list before slicing so it always has a retain seat and oldest builds still prune. Also gate prune on publish_live success and warn if TAG still appears stale. |
||
|
|
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 |
||
|
|
b11354aaa7 |
fix(cmd-j): short-screen fit and overflow copy for larger palette (#13123)
* fix(cmd-j): fit large palette on short screens and sync overflow copy Cap dialog/list height against the viewport so the input, filter chips, and footer stay visible after the larger Cmd+J shell, and align the English catalog overflow hint with the multi-primary “scroll or keep typing” wording. * fix(cmd-j): re-emit section headers for interleaved palette remainder ro When both open tabs and worktrees overflow their first-screen slice, the layout interleaves remainder rows. The trailing-section header renders before the leading remainder, so unlabeled rows read as the wrong section. Re-emit headers before each remainder with a distinct suffix for React keys. Refactor type-alias matching into a reusable utility that prefers earliest match position over declaration order. * fix(cmd-j): stabilize palette memos and gate heavy builders when closed P1-a: Move quickActionContext filtering into a useMemo with stable primitive deps (activeView, activeWorktreeId, sshConnectionStates, etc.) instead of calling buildQuickActionContext() inline every render — the fresh object identity defeated the middleItems memo on every keystroke. P1-b: Guard browserSortedWorktrees, browserPageEntries, simulatorTabEntries, and workspaceTabEntries on paletteStatusInputsActive so the always-mounted palette stops rebuilding full open-tab indexes after every store write while closed. P2-a: Derive sortedWorktrees from browserSortedWorktrees by filtering out archived worktrees — both called sortWorktreesSmart with identical deps, so one sort + filter replaces two. P2-b: Pre-index agent metadata by tabId once per build via buildAgentMetadataTabIndex, replacing the O(tabs × map entries) scan in collectAgentMetadataForTerminal with O(1) lookups per tab. P2-e: Assert in the interleaved test that rendered selectable row order matches orderMultiPrimaryPaletteItems, keeping listEntries and the layout function as a single source of truth. Co-authored-by: Orca <help@stably.ai> --------- Co-authored-by: Orca <help@stably.ai> |
||
|
|
8a061b9f04 |
Allow automation deletion without SSH connection (#13261)
* Allow automation deletion without SSH connection Remove external source entries from the list and only show jobs. This allows users to delete and manage automations even when the remote host is not connected. * Update test: list jobs from unavailable automation manager Remove availability constraints (status, error, canManage) from test to verify jobs can be listed regardless of manager connection state. |
||
|
|
04f7123d26 |
fix(terminal): repair stale-dpr WebGL canvas backing on reveal and fit (#13159)
* fix(terminal): repair stale-dpr WebGL canvas backing on reveal and fit When devicePixelRatio changes while a pane is hidden (window moved between retina and non-retina displays, worktree then revealed), xterm's WebGL renderer re-measures cell dimensions but its canvas keeps the old backing store — the addon's device-pixel observer misses changes that land while the element has no box. The browser composites the stale-scale bitmap into the css box: half/double-size or smeared text until a manual resize. Reproduced deterministically (2160px backing behind a 1080px css box at dpr 1) — this is the mechanism behind the field reports of a normal pane going blurry after switching back to a worktree. A repair check now runs on every successful fit (via the fit-success hook) and on the light tab-resume path (which never fits): when the canvas backing diverges from cssWidth x devicePixelRatio beyond rounding tolerance, it replays xterm's own dpr + resize path to rebuild the backing at the current scale, then refreshes. Verified live: the same break sequence now self-heals on reveal with no user action. A webgl-canvas-dpr-repair diagnostic records each repair with the stale and expected backing widths. * fix(terminal): keep dpr repair off the layout path |
||
|
|
06144ca26c |
fix(worktrees): guard lineage pruning from failed scans (#13248)
* fix(worktrees): guard lineage pruning from failed scans * fix(worktrees): back off failed local resolution scans |
||
|
|
6da7b8e9cf |
Show local and remote Quick Commands by host (#13094)
* feat(quick-commands): support remote host collections * fix(quick-commands): address remote host review * Preserve local Quick Commands UI --------- Co-authored-by: Jinwoo-H <Jinwoo-H@users.noreply.github.com> |
||
|
|
54a9b5840b | Update README downloads badge | ||
|
|
915050f30a |
fix(native-chat): resolve WSL Codex transcripts so Chat UI renders responses (#12473)
* fix(native-chat): resolve WSL Codex transcripts so Chat UI renders responses Codex reports a guest Linux transcript path. On a Windows host `existsSync` resolved it against the current drive (`C:\home\...`) and discarded it, and the id-based fallback only searched host roots. `resolveSessionFilePath` returned null forever while the watcher reported `watching: true`, leaving Chat UI permanently empty. Translate the guest path to its host-readable UNC twin, classifying **before** any `existsSync` probe on win32 so the `C:\home` false positive cannot fire. Adapted from #10639 with one required correction: it uses the **async cached** WSL seams (`listWslDistrosAsync` / `getWslHomeAsync`) rather than the `execFileSync` ones, which would stall the Electron main thread for up to 5s per tick of the resolve-poll loop on a cold distro. Fixes #10326 Co-authored-by: Orca <help@stably.ai> * fix(native-chat): stop the WSL transcript probe firing every poll tick Three follow-ups from review of the WSL Codex transcript fix: - The UNC translation was retried on every fast resolve-poll tick (measured 10 sync UNC stats per 100ms) because only a successful result was memoized. Gate the retry to the slow fallback cadence. - A non-empty WSL home list was cached for the process lifetime, so a distro that was still booting during the first probe stayed excluded forever. Expire both branches; getWslHomeAsync caches successes, so a refresh only re-spawns wsl.exe for the distros that actually failed. - codexSessionsDirs enumerated every distro's home eagerly, waking distros the user left stopped even for native-Windows panes. Make the WSL roots a lazy tier consulted only after the host's own Codex roots miss. The resolve-poll suite became platform-dependent and only passed off Windows; pin the platform and add explicit win32 coverage. --------- Co-authored-by: Orca <help@stably.ai> Co-authored-by: OrcaWin <alpha-eng@stably.ai> |
||
|
|
d0baa20d0e |
fix(terminal): resolve WSL file links from the execution runtime, not the worktree path (#12465)
`mapTerminalFilePath` derived the WSL distro only from the *shape* of `worktreePath`. A worktree on a native Windows drive whose project runs under the WSL runtime gets a shell whose paths are POSIX, so no distro was found, the path went verbatim to a Win32 stat probe, and the candidate was dropped — no underline, no tooltip, inert Ctrl+click. Resolve the pane's distro from the execution runtime, falling back to the old worktree-shape derivation so existing behaviour is unchanged. Note the half of #8156 covered by merged #8215 (worktree on the WSL filesystem) was already fixed; this closes the remaining gap. Fixes #8156 Co-authored-by: Orca <help@stably.ai> |
||
|
|
434959965a |
fix(terminal): stop the Korean gate caching an unknown input source as negative (#13182)
On a fresh session the Won rewrite silently did not fire — real-hardware capture on macOS 26.5.2 with 2-Set Korean read 3 of 3 Backquote presses as e2 82 a9 at the PTY, and the feature only started working after the first press or an input-source round trip. Cause: the reader returns null for 'no signal' — the IPC is not exposed yet at startup — as well as for a genuinely absent source, and refreshInputSourceId committed that as isKoreanInputSourceId(null) === false. An unknown became a confirmed negative that nothing retried, because a keystroke-triggered refresh is async and cannot classify the press that triggered it. Leaves the cache unknown on a null read and retries the startup probe with bounded backoff, so the gate is warm before the first key. Bounded because each probe spawns defaults export | plutil | plutil. Co-authored-by: Orca <help@stably.ai> |
||
|
|
d64ccc71bd |
fix(terminal): break the ConPTY foreground livelock that froze a repainting TUI (#12463)
`holdForeground` and `coalesceForeground` each cancelled the other's fallback timer on the Windows ConPTY DEC 2026 (synchronized output) path. A continuously repainting TUI such as Codex therefore left the foreground latch stuck open, so every later chunk was held instead of coalesced and output never reached the visible pane until the tab was refreshed. Break the mutual cancellation and mirror the hidden path's scan on the foreground path, carrying a marker tail so a ConPTY-split DEC 2026 marker is still detected. Nothing was wrong with Flutter — it was simply a long-running command behind a repainting TUI. Fixes #8754 Co-authored-by: Orca <help@stably.ai> |
||
|
|
42fc5375e8 |
perf(terminal): gate the Korean input-source probe on its setting (#13181)
prefetchKoreanInputSource ran under a bare isMac check, so every macOS user paid for it. Each refresh shells out to `defaults export | plutil | plutil` — four processes in the main process — and input-source toggle keys pass force: true, so one Caps Lock press costs two probes. terminalKoreanWonToBackquote defaults to false. Threads the setting through KeyboardHandlersDeps and into the effect's dependencies, so enabling it mid-session still warms the cache before the first Backquote. Co-authored-by: Orca <help@stably.ai> |