Commit Graph

4884 Commits

Author SHA1 Message Date
Neil fcdcab7cd4
Release simulator launch pending guard on tab creation failure (#7682) 2026-07-11 01:28:22 -07:00
Neil 5da02b0105
Bound GitHub project picker browse cache (#7681) 2026-07-11 01:27:59 -07:00
Neil 5952ad0623
Bound custom pet blob cache (#7671) 2026-07-11 01:27:32 -07:00
Neil 2ddb976f8a
Fix local preflight context cache leak (#7669) 2026-07-11 01:26:53 -07:00
Neil a5486b1351
Fix Codex title index cache leak (#7667) 2026-07-11 01:26:26 -07:00
Andre Cristo f6f497f50c
fix(win): resume quoted cmd.exe startup commands via stdin, not /K (#7978)
* fix(win): resume quoted cmd.exe startup commands via stdin, not /K

Resuming an AI Vault session into a cmd.exe tab on Windows failed with
"'...' is not recognized as an internal or external command" and never
ran the resume. The queued command for a cmd live shell is the
self-contained `cmd /d /s /c "cd /d ""cwd"" && claude ""--resume"" ""id"""`
form, which is correct when typed into cmd's interactive parser (its ""
doubling is cmd's convention). But the local PTY provider embedded it in
the `/K` launch argument, where node-pty's C-runtime argv escaping emits
backslash-escaped quotes (\") that cmd.exe cannot parse — the command
arrived as `\"cd /d \"\"cwd\"\" && ...` and was rejected wholesale.

Unlike PowerShell's -EncodedCommand, cmd.exe has no robust argv-quoting
path, so any startup command containing a double quote now falls back to
stdin delivery, where cmd's interactive parser handles the "" doubling
correctly (verified end-to-end against a real ConPTY via node-pty).
Quote-free commands keep the `/K` fast path.

* fix(win): preserve cmd resume command contracts

* fix(win): match copied resume commands to shell

---------

Co-authored-by: Jinwoo Hong <73622457+Jinwoo-H@users.noreply.github.com>
2026-07-11 00:59:17 -07:00
Neil 9f078567e5
Update mobile 0.0.26 Android download links (#8246) 2026-07-11 00:48:01 -07:00
Brennan Benson 4572b0a446
perf(worktrees): remove redundant creation probes (#7161)
Co-authored-by: OrcaWin <alpha-eng@stably.ai>
2026-07-11 00:41:28 -07:00
OrcaWin fa691a0dc8
fix(win): bound renderer launch-failed recovery (#8234) 2026-07-11 00:15:44 -07:00
Neil 76c0365110
Cancel stale mode 2031 retries across resubscribe (#8214) 2026-07-11 00:06:32 -07:00
Neil 15213c15e6
Send hidden terminal query replies without batching (#8213) 2026-07-11 00:05:54 -07:00
Rod Boev 04a040801b
fix(terminal): detect PR links with ANSI resets (#8216)
* fix(terminal): detect PR links with ANSI resets (#8126)

* fix(terminal): safely normalize PR link styling

---------

Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
2026-07-10 23:41:20 -07:00
Brennan Benson 31cff75a7b
feat(agent-status): show Claude subagent child rows and gate premature done (#8211)
* feat(agent-status): show Claude subagent child rows and gate premature done

A Claude pane that spawned background subagents/teammates showed a green
done check the moment the lead's turn ended, even while a background
review loop was still running. Orca now tracks the pane's live children
from Claude hook events and:

- keeps the pane 'working' while at least one child is working (Stop is
  gated; Claude wakes the lead when a child finishes, so the pane
  resolves to done on the follow-up Stop with an empty roster)
- renders the children as indented child rows under the pane's sidebar
  row (name/type + working/idle dot), reusing the existing lineage UI

Tracking is lifecycle-primary: SubagentStart/SubagentStop/TeammateIdle
(newly registered hooks) plus child-origin tool events (they carry
agent_id) own the roster. Stop's background_tasks is folded only where
unambiguous — verified live on Claude Code 2.1.207 that teammates report
status "running" while idle-alive and their task ids never match
lifecycle agent_ids, so the list cannot decide teammate working-ness.

Child-origin events no longer overwrite the lead's tool/prompt caches
(a live AskUserQuestion card survives child churn); a child's own
PermissionRequest records waitingAgentId so only that child's progress
or death clears the wait. The interrupted flag survives the gated
window, inferred interrupts sync the lead record and refuse while a
child works, and hydration reseeds the roster after a restart.

* fix(agent-status): drop identity icon on subagent child rows

The child's agentType carries its NAME (e.g. "pr-reviewer"), which is not
an iconable agent and rendered the unknown "?" glyph. Nesting under the
parent row already conveys identity.

* fix(agent-status): restore displaced lead state and reconcile phantom subagents

Four review findings from the adversarial pass on the subagent child-row
feature:

- Stash the lead state a child-induced wait displaces
  (ClaudeLeadTurnState.stateBeforeWait) and restore it when the wait
  clears, instead of inventing 'working' — a lead that had already
  stopped left the pane spinning forever after the roster drained,
  since the done-gate only ever downgrades done → working.
- Tag snapshot-seeded and background_tasks-recreated roster entries
  (backgroundTasksAuthoritative) and demote them when a PRESENT
  background_tasks list omits their id. A phantom child seeded before a
  restart could otherwise gate the pane 'working' indefinitely in teams
  sessions, whose task list is never empty. Live activity clears the
  tag so lifecycle-tracked teammates keep their state.
- Match teammate ids with a hyphen-free suffix after `a<name>-` so
  TeammateIdle for "lane" cannot idle "lane-hooks"'s rows or clear its
  pending permission wait.
- Route turn-boundary events (Stop/StopFailure/UserPromptSubmit) that
  carry a KNOWN child agent_id through the child-driven re-emit instead
  of adopting them as lead state, and tie the prompt-cache new-turn
  reset to lead-origin events so child refreshes can't blank the
  prompt label.

---------

Co-authored-by: Brennan Benson <brennanbenson@Brennans-MacBook-Pro.local>
2026-07-10 23:34:49 -07:00
Jinjing 931e402e93
Fix tui-idle detection for cursor-agent lanes with dismissed trust dialog (#8220)
* Fix tui-idle detection for Cursor Agent lanes with dismissed trust dialo

Cursor Agent's persistent TUI never emits an idle OSC title, so a
dismissed trust dialog stayed in scrollback and kept the wait blocked
even once Cursor reached its idle "→" prompt. Detect Cursor's active
prompt (busy or idle) to clear a stale trust hit, and detect its idle
state specifically (prompt present, no braille spinner) to satisfy
tui-idle, so busy lanes keep polling instead of returning a stale
blocked result.

* Add test coverage for Cursor Agent trust-dialog banner detection

Verify lastIndexOf picks the later ready banner over an earlier
"Cursor Agent" mention in the trust dialog body, for both the
idle-resolution and busy-detection cases.

* chore(i18n): sync localization catalog for aiVaultSessionLogOpen keys

Pre-existing gap from #8205 (ai-vault view log) that fails `pnpm lint`'s
localization-catalog check on origin/main; unrelated to the tui-idle fix but
committed here to unblock CI lint on this branch.

Co-authored-by: Orca <help@stably.ai>

---------

Co-authored-by: Orca <help@stably.ai>
2026-07-10 23:25:46 -07:00
Rod Boev b677b2a209
fix(github): recover oversized issue creation (#8217)
* fix(github): recover oversized issue creation (#7704)

* fix(github): preserve partial issue creation

---------

Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
2026-07-10 23:16:13 -07:00
Jinjing 66cad529a2
Improve chinese translation 2 (#8219)
* Improve zh/ja/ko/es translations for missing and untranslated i18n strin

- Translates several previously English-only entries (locked workspace, host removal, subagent labels, org/profile switcher dialogs) that were left untranslated in each locale file
- Fixes zh/ja/ko/es JSON EOF newline formatting

* Improve zh/ja/ko/es translation quality and terminology consistency

- Fix mistranslated/untranslated strings (English leftovers in es/ja,
  incorrect "role"→"forbidden" wording in ko)
- Standardize terminology: 子代理→子智能体, 中立→中性 (zh); 하위
  에이전트→서브에이전트 (ko); align count-phrase word order with
  natural ko/ko phrasing (e.g. "N개 X" → "X N개")
2026-07-10 23:06:56 -07:00
Jinwoo Hong 5b1fcb0ff0
Fix task workspace handoff after switching workspaces (#8226)
Co-authored-by: Orca <help@stably.ai>
2026-07-10 22:56:33 -07:00
Jinjing 6a176b89fc
fix(source-control): keep push enabled for same-repo linked review with real upstream (#8202)
* fix(source-control): keep push enabled for same-repo linked review with real upstream

Push/Force Push (and Pull/Sync/Publish) were wrongly disabled when a branch
had a genuine git upstream but an open linked PR whose push target had not
yet hydrated. `hasUsableHostedReviewPushTarget` hard-returned false for any
resolvable review link without a hydrated `pushTarget`, so
`resolveHostedReviewActionUpstreamStatus` synthesized `hasUpstream:false`
and the whole remote menu treated the branch as unpublished.

A same-repo review's head IS the checked-out branch, so a real upstream
already tracking that branch is that head and is safe to use before the
resolver hydrates a target. Treat that upstream as usable; keep blocking the
fork-head/cross-repo case (upstream tracks a differently-named head) and the
no-upstream case until the resolver proves the real target. Provider-agnostic
(GitHub PR / GitLab MR) and covers the SSH linked-review path.

Adds regression tests to source-control-hosted-review-push-target.test.ts and
a composed pipeline test in source-control-dropdown-items.test.ts.

* docs(source-control): trim verbose comments on linked-review push-target fix

Co-authored-by: Orca <help@stably.ai>

* refactor(source-control): use canonical gitRefTargetsBranchName for linked-review upstream match

Replace the hand-rolled upstreamTracksBranch leaf-split with the shared
gitRefTargetsBranchName primitive, which trims and rejects malformed
remote-qualified refs. The remote stays unknown until the resolver hydrates
the push target, so this remains a branch-leaf match; the strict remote+branch
check takes over once the target is known.

Co-authored-by: Orca <help@stably.ai>

---------

Co-authored-by: Orca <help@stably.ai>
2026-07-10 22:45:32 -07:00
Neil 4f7fe78921
perf(mobile): stop polling live worktree names (#8051)
* perf(mobile): replace worktree name polling with events

* fix(worktrees): push rename invalidation to remote clients

worktrees:updateMeta deliberately skips the renderer notifier (PR #209),
but paired mobile clients no longer poll for titles, so a manual rename
would never reach them. Emit the remote-only worktreesChanged client
event (with resolved-cache invalidation), gated on displayName so
per-click isUnread writes stay event-free.

Co-authored-by: Orca <help@stably.ai>

* test(worktrees): add missing runtimeStub type member for typecheck

Co-authored-by: Orca <help@stably.ai>

* fix(worktrees): derive rename event repoId with the shared non-throwing parser

getRepoIdFromWorktreeId matches the mobile client's event filter exactly
and cannot throw after the meta write already persisted.

Co-authored-by: Orca <help@stably.ai>

---------

Co-authored-by: Orca <help@stably.ai>
2026-07-10 22:44:59 -07:00
Siddiqui Qamar e3f0b99f4a
fix: detect noisy Windows ARM64 SSH platforms (#7965)
* fix: detect noisy Windows ARM64 SSH platforms

- Scan remote platform probe output line-by-line for supported OS/arch markers.

- Handle PowerShell/OpenSSH first-use CLIXML or banner noise before Windows ARM64.

- Add regression coverage for noisy Windows ARM64 detection.

* fix: harden SSH platform probe markers

---------

Co-authored-by: Jinwoo Hong <73622457+Jinwoo-H@users.noreply.github.com>
2026-07-10 22:31:53 -07:00
Neil 4df9457530
Fix delayed terminal color-scheme replies leaking into fish (#8206)
* fix(terminal): send mode 2031 replies immediately

* docs(terminal): name mode 2031 color replies accurately
2026-07-10 21:24:27 -07:00
Jinjing c5044042ea
Ai vault view log live tail (#8205)
* Add read-only View Log tabs so AI Vault agent sessions open inside Orca'

- Adds a `readOnly` flag on OpenFile that hard-blocks edits, autosave, dirty
  state, rename, and drafts, and persists/restores it safely across sessions
- Wires AI Vault's View Log/Open Log actions to open logs as permanent
  read-only local tabs instead of shelling out to the OS, gated to
  local, single-file, non-synthetic session paths
- Registers a dedicated `jsonl` Monaco language (JSON-style coloring without
  whole-document JSON validation) and forces read-only tabs to render as raw
  source, bypassing markdown/mermaid/csv/notebook viewers

* Add live-tail streaming for local AI Vault View Log tabs

- Extend the fs:readFile snapshot path with byte-stable file identity so a
  read-only tab can resume appending exactly where the snapshot left off.
- Add a ranged local log tail reader plus IPC (read/start/stop watch) that
  streams only newly appended bytes, detects truncation/rotation, and
  cleans up watchers on tab close or renderer destruction.
- Add a renderer-side UTF-8/line-boundary decoder and useLocalLogTail hook
  that appends completed lines into the existing Monaco model, falling
  back to a full reload on reset/rotation, keeping snapshot behavior
  unchanged when liveTail is not opted in.
- Thread the new `liveTail` flag through OpenFile/PersistedOpenFile,
  workspace session persistence/restore, and the AI Vault "View Log" open
  path so live tail survives restarts and stays read-only-safe.

* Remove stale planning brief for the live-tail feature

The AI-VAULT-VIEW-LOG-LIVE-TAIL.md pick-up brief is no longer needed now that the live-tail streaming work has landed (c7fdef7b1).
2026-07-10 21:22:18 -07:00
Neil 20c9e09cca
test(renderer): restore terminal-create fixture parity (#8208) 2026-07-10 21:18:56 -07:00
Neil 6b7732d511
perf: skip terminal wait scans for ordinary output (#8169)
* perf: skip terminal wait scans for ordinary output

* test: record terminal wait performance gate

* docs: clarify terminal wait allocation proof
2026-07-10 21:10:29 -07:00
Neil 84d375ae06
perf: avoid rescanning idle serve-sim PTY output (#8166)
* perf: avoid scanning idle serve-sim PTY output

* test: record serve-sim PTY performance gate

* fix(renderer): dedupe connection context import
2026-07-10 21:04:59 -07:00
Brennan Benson b902f19b0d
fix(terminal): send CSI-u Shift+Enter to Droid on Windows (#7620) (#7668)
* fix(terminal): send CSI-u Shift+Enter to kitty TUIs (droid) on Windows (#7620)

On Windows, Shift+Enter was always sent as the Alt+Enter byte ESC+CR (added in
#2418 for Codex, which reads win32-input-mode and ignores CSI-u). droid speaks
the kitty keyboard protocol, parses CSI-u directly, and treats ESC+CR as a plain
Enter — so Shift+Enter SUBMITTED the message instead of inserting a newline.
droid works in other terminals (Windows Terminal, Warp) because those honor
win32-input-mode / kitty; Orca (xterm.js) withholds kitty from local Windows
ConPTY panes and emits neither.

Make the Windows Shift+Enter byte pane-aware: latch whether a pane's program
advertised the kitty keyboard protocol (query CSI ? u, push CSI > .. u, or set
CSI = .. u) and send CSI-u (\x1b[13;2u) to those panes, keeping the
Codex-compatible ESC+CR for win32-input-mode-only TUIs. Non-Windows is unchanged
(always CSI-u).

Verified end-to-end against the real droid and Codex CLIs through the actual
production functions: droid now newlines, Codex still newlines.

* fix(terminal): route Windows Shift+Enter safely for Droid

---------

Co-authored-by: Neil <neil@stably.ai>
Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
2026-07-10 21:00:31 -07:00
Neil 2a01b41638
perf(main): make worktree path dedupe linear (#8177) 2026-07-10 20:52:24 -07:00
Neil 435de94102
perf(main): avoid hosted-review IPC fanout (#8171) 2026-07-10 20:52:22 -07:00
Neil 943fc781bb
perf(runtime): reuse unchanged PTY path history (#8163) 2026-07-10 20:52:17 -07:00
Neil 90afbc3cf3
perf(status-bar): avoid resource badge rebuilds on title churn (#8152) 2026-07-10 20:52:15 -07:00
Neil 141b9b7de2
perf(renderer): gate agent completion coordinator sync (#8149) 2026-07-10 20:52:13 -07:00
Neil a4fb828b3c
perf(renderer): scope folder review cache projections (#8136)
* perf(renderer): scope folder review cache projections

* fix(renderer): keep checks selector notifications constant-time
2026-07-10 20:51:59 -07:00
Neil 3afea9e9f2
perf(renderer): scope Markdown worktree path subscriptions (#8111) 2026-07-10 20:51:56 -07:00
Neil e0207cb360
perf(renderer): scope editor draft subscriptions by panel (#8105)
* perf(renderer): scope editor draft subscriptions by panel

* fix(renderer): keep conflict overview drafts live
2026-07-10 20:51:54 -07:00
Neil d617bda234
perf(renderer): scope active review cache subscriptions (#8097) 2026-07-10 20:51:51 -07:00
Neil 8b1054de19
perf(renderer): index runtime owner lookups (#8089) 2026-07-10 20:51:49 -07:00
Neil e5be88342d
perf(renderer): reuse connection context indexes (#8085)
* perf(renderer): reuse connection context indexes

* perf(renderer): reuse connection indexes in tab bars

* perf(renderer): reuse worktree index for diff comments

* perf(renderer): index remaining diff comment selectors
2026-07-10 20:51:47 -07:00
Neil e085a84df8
perf(renderer): keep pre-handler PTY buffering linear (#8082) 2026-07-10 20:51:44 -07:00
Neil 65b7d9a532
perf(renderer): compact ignored Cursor title effects (#8080) 2026-07-10 20:51:31 -07:00
Neil 106009a90c
perf(renderer): index terminal unified tab reads (#8076) 2026-07-10 20:51:28 -07:00
Neil 09196ba086
perf(renderer): scope floating workspace collections (#8071) 2026-07-10 20:51:26 -07:00
Neil 74e68afbb2
perf(renderer): share terminal agent type index (#8066) 2026-07-10 20:51:23 -07:00
Neil 818e182a0c
perf(renderer): gate worktree list review caches (#8061) 2026-07-10 20:51:20 -07:00
Neil b681a2c54a
perf(renderer): scope agent send-target control updates (#8055) 2026-07-10 20:51:18 -07:00
Neil fadb3f3730
perf(renderer): scope sidebar agent freshness by worktree (#8052) 2026-07-10 20:51:15 -07:00
Neil e825df8218
perf(memory): skip unrelated boot worktree scans (#8204)
* perf(memory): scope boot worktree scans to live sessions

* fix(memory): refresh daemon hydration snapshots

* fix(memory): rescan repos referenced only by the re-read snapshot

Repo selection previously came solely from the first daemon listing, so a
session that only became visible on the post-enumeration re-read (e.g. a
briefly unreachable adapter) could never be registered for the life of the
process. Loop enumeration until the latest daemon snapshot references no
unresolved repos, so selection and registration always share a snapshot.

Co-authored-by: Orca <help@stably.ai>

---------

Co-authored-by: Orca <help@stably.ai>
2026-07-10 20:10:08 -07:00
sonhyrd 2cfb028163
fix: Cap automationRuns retention to stop unbounded state file growth (#8119)
* Cap automationRuns retention to stop unbounded state file growth

`automationRuns` was the only unbounded durable collection in
`orca-data.json`, and the whole blob is re-serialized and rewritten on
every save. On a machine running four `* * * * *` automations it had
grown to 11,184 rows / 21 MB of a 28.5 MB file — all of them
`skipped_precheck` no-ops — so each synchronous `flush()` blocked the
Electron main thread for 190-210 ms and macOS filed 24 `disk writes`
diagnostic reports against Orca (12-30 MB/s sustained, 549 GB/session).

Prune to the newest 100 runs per automation, on load and on append. This
mirrors the retention that `pruneLocalTerminalScrollbackBuffers` and
`pruneWorkspaceSessionBrowserHistory` already apply to their
collections; `automationRuns` was simply missed.

The load-path prune marks state dirty, so an oversized file heals on
first load. Without that flag the shrink lives only in memory: the sole
load-time save trigger is `normalized.changed || loadNeedsSave ||
adaptedProjectGroups`, and `normalized` covers pane identity only. A
user who took the documented workaround (`automations edit --disabled`)
fires no runs, so nothing would ever rewrite the file.

Measured against the affected 28.5 MB file: flush() 190 ms -> 9 ms,
bytes written per save 28.5 MB -> 1.3 MB.

Pruning breaks the old `runNumber` derivation, which counted retained
runs, so every run after the cap would have been titled "run 101".
Carry the ordinal on the run itself and derive the next number from the
highest survivor. Legacy rows are numbered from the highest number their
automation already carries, not from their append position: a downgrade
to a pre-`runNumber` build appends unnumbered runs after pruned
survivors numbered 101+, and a position would reissue one of those,
giving two runs the same title.

Fixes #8118

* Never evict in-flight automation runs from retention

A dispatched run's completion can land hours later (renderer round-trip or
headless completion promise); pruning it makes updateAutomationRun throw
'Automation run not found.'. Only final-status runs are evictable now, with
the final-status predicate shared between retention and the service.

Co-authored-by: Orca <help@stably.ai>

* Skip the usage write when retention evicted the run mid-collection

markDispatchResult finalizes a run, awaits usage collection, then writes
usage by id. The run is final during that await, so a concurrent
create-time prune can evict it and the write threw 'Automation run not
found.' — in the headless path that cascaded into an unhandled rejection.

Co-authored-by: Orca <help@stably.ai>

* Pin backfill-before-prune ordering with true legacy fixture rows

The heal-on-load fixture rows carried runNumber, so swapping backfill and
prune passed every test while renumbering real legacy survivors 1..100 and
re-minting colliding titles. Seed unnumbered rows and assert healed ordinals.

Co-authored-by: Orca <help@stably.ai>

---------

Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
Co-authored-by: Orca <help@stably.ai>
2026-07-10 20:02:45 -07:00
Neil 66437d19c3
perf(hook-commands): skip unrelated store flushes (#8181) 2026-07-10 19:56:03 -07:00
Neil 5eda948a7a
perf(agent-startup): skip unrelated store flushes (#8179) 2026-07-10 19:56:00 -07:00
Neil 0eafb0e626
perf(ai-vault): index visible session pane lookups (#8173)
* perf(ai-vault): index visible session pane lookups

* perf(ai-vault): build pane index lazily
2026-07-10 19:55:57 -07:00