Commit Graph
10202 Commits
Author SHA1 Message Date
NeilandOrca bd490b14aa [P2] fix(terminal): always close the bracketed-paste frame and hold the pty lock through submit (#11816)
Co-authored-by: Orca <help@stably.ai>
2026-07-31 14:05:07 -07:00
NeilandOrca a51248e42a [P2] fix(mobile): put the PR sidebar and branch chip on the shared check classifier (#11815)
Co-authored-by: Orca <help@stably.ai>
2026-07-31 14:03:35 -07:00
NeilandOrca ea40c87315 [P2] fix(skills): keep the npx preflight in the forced-PowerShell setup terminal on Windows (#11814)
Co-authored-by: Orca <help@stably.ai>
2026-07-31 14:03:26 -07:00
NeilandOrca 81fee4d6a4 [P2] fix(checks): give the PR page and work-item dialog the shared check-count labels (#11813)
Co-authored-by: Orca <help@stably.ai>
2026-07-31 14:03:03 -07:00
NeilandOrca b998f7b13e [P2] fix(updater): route hourly release-notes links to the hourly repo and let the disabled channel tooltip open (#11812)
Co-authored-by: Orca <help@stably.ai>
2026-07-31 14:02:30 -07:00
Brennan Benson e698241aab fix(mobile): remember custom pairing addresses (#11741)
* fix(mobile): remember custom pairing address

* fix(mobile): stabilize custom pairing address sync

* fix(mobile): update pairing refresh refs after commit

* feat(mobile): manage saved custom pairing addresses

* fix(mobile): harden custom address selection
2026-07-31 13:03:11 -07:00
129d8b32bb fix(codex): trust extended-length resume paths (#10337)
* fix(codex): trust extended-length resume paths

* test(codex): cover both sides of extended-length resume folding

The provenance fix folds the extended drive spelling on the rollout path and
the trusted sessions root, and runs per entry in the legacy id scan. Only the
rollout-path side had coverage, so pin the rest:

- normal-form rollout under an extended-length trusted home (root side)
- compressed .zst sibling derived from an extended-length persisted path,
  where a folded comparison copy could leak into the returned path
- legacy id-scan entries: extended-length accepted, device namespace refused

All three fail with the production change reverted.

* docs(codex): state accurately what the resume path fold rejects

The helper's comment called the rejected spellings "arbitrary device
namespaces", but \?\UNC\ is the extended-length form of \server\share\ —
a network share, not a device. Reading it as a security decision hides that
UNC is simply not folded yet, so say what is actually excluded.

Also trim the two new test comments to one line to match the file.

* fix(codex): reject rollout alternate data streams

* fix(codex): preserve resume provenance guard

---------

Co-authored-by: OrcaWin <alpha-eng@stably.ai>
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-07-31 11:59:05 -07:00
Rod BoevandOrcaWin 271776f233 fix(codex): keep shell-profile-only Windows CODEX_HOME on the managed lane (#10221)
* fix(codex): keep shell-profile-only Windows CODEX_HOME on the managed lane

* test(codex): harden platform probe regression coverage

* test(codex): isolate Windows home ownership coverage

* fix(codex): preserve managed home in constrained PowerShell

* fix(powershell): continue constrained startup commands

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-07-31 11:58:46 -07:00
Brennan Benson 5d88c1093f docs(i18n): adopt gettext PO as the canonical translation source (#11478)
* docs(i18n): adopt gettext PO as the canonical translation source

Records the revised translation-source architecture decision: gettext PO
replaces the original constrained XLIFF 2.0 profile after tooling,
contributor-workflow, and repository-evidence review. Documents the
read-only compiler / post-merge reconciler split, the four-state entry
taxonomy, message ID and placeholder policy, mobile and plugin-language-pack
scope, and the field-by-field PR B acceptance gate. Links the decision from
the localization audit and corrects its allowlist description.

* docs(i18n): add mobile dual-projection and bridge-ID constraints

From PR 11446 owner review: mobile needs two deterministic projections
(i18next JSON + pre-JS native metadata with a documented native fallback
rule and the zh/zh-Hans locale-ID mapping); the hash-ID grandfather clause
is date-scoped to the desktop catalog so freshly minted bridge IDs must be
renamed in a dedicated adjacent change; OS permission prompts and native
app metadata join the human-review list; imported bridge provenance must
be classified in PR C.
2026-07-31 11:46:25 -07:00
github-actions[bot] b0c5bb5586 release: v1.4.163-rc.3 v1.4.163-rc.3 2026-07-31 18:28:35 +00:00
Brennan Benson f3f5a928f6 Fix duplicate terminal renderers after layout restore (#11726)
* Fix duplicate terminal PTY layout ownership

* Fix rootless duplicate PTY replay

* Normalize mirrored terminal PTY ownership

* Handle repeated terminal layout leaf ownership

* Preserve terminal ownership through duplicate layout repair

* Repair dangling PTY layout selection

* Preserve terminal ownership repair metadata

* Preserve rootless pending terminal focus

* Bound duplicate terminal layout pruning

* Preserve agent authority during layout repair

* Fix hydrated terminal pane authority repair
2026-07-31 11:25:30 -07:00
Jinjing 1c8908b791 Fix orchestration gate authorization to scope by Run binding (#11802)
* fix(orchestration): gate methods route calls to the caller's Run with `f

Gates are Run-scoped state; every gate command now resolves the caller's active Run
(via pane binding or explicit --from flag) and authorizes within that Run's scope.
Settled adopted work no longer requires --takeover-legacy, and the legacy coordinator
fence respects both binding-based and attestation-based proof of authority.

* fix(orchestration): gate methods route calls to the caller's Run with at

Gate and run methods now verify that declared terminal handles match the caller's
attested identity, preventing spoofing of other coordinators. Extracted shared
`resolveRunScope` to enforce one authorization rule across all orchestration
mutations. Added comprehensive regression tests for #11745.
2026-07-31 10:56:20 -07:00
NeilandOrca 79251d7a98 [P2] fix(release,settings): restore signing preflight portability, bootstrap diagnostics, and skill re-check (#11692)
* fix(release): restore the SignPath composite action when cutting from an older ref

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

* fix(startup): record a durable diagnostic before the bootstrap fatal-exit guard exits

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

* fix(settings): make agent-skill Re-check rescan skill freshness

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

* fix(startup): keep the bootstrap fatal diagnostic when the log override is unwritable

Create the parent directory an overridden ORCA_BOOTSTRAP_FATAL_LOG names and fall
back to the default location when that path still cannot be opened, so a missing
parent no longer costs the only account of the failure. Also pins the Re-check
freshness rescan to the completed install scan rather than the click.

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

* refactor(settings): move the post-recheck surface sync out of the panel

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

* fix(startup): retain diagnostics without node fs

* fix(skills): keep freshness scoped to the local runtime

* fix(settings): register freshness status translations

* fix(settings): scope and sequence skill freshness refreshes

* fix(settings): refresh freshness across runtime transitions

---------

Co-authored-by: Orca <help@stably.ai>
2026-07-31 06:08:26 -07:00
Neil 886fa7b438 [P0] fix(relay): stop the retired exit record from double-broadcasting pty.exit (#11660)
* fix(relay): stop the retired exit record from double-broadcasting pty.exit

Retiring the publication record at cancel or grace-expiry time routed the pending
exit through pty-handler's unconditional broadcast, so subscribers that already
received the exit from the record's legacy projection got a second copy (which
fans out to a whole-provider teardown on the consumer side).

- Track the legacy exit projection in a RelayPtySourceLegacyExitIndex keyed by
  pty incarnation so it outlives the delivery record; the handler's fallback now
  re-targets only source-owner clients when subscribers already have the exit.
- Cover the previously untested defense layers: B2 same-client re-attach healing,
  B3 deferred retire-on-append-failure (retire plus the microtask deferral), and
  publishPendingExit's `accepts(id) &&` sub-guard.
- Harden layer D's capacity fan-out (it sits outside the catch it depends on),
  route cancelExact's bare-timer probe through snapshotIfKnown, and keep
  appendDenied counting cancel-induced append rejections.

* test(relay): pin the retired-record exit dedup and stop its index leaking

The legacy-exit dedup layer shipped with zero coverage: deleting pty-handler's
`publishExitAfterRetire` wiring left the whole relay suite green, because the
send scheduler's partial-progress branch keeps a projected record alive so B1
handles the common case and the fallback is never reached.

- Cover `RelayPtySourceLegacyExitIndex` directly, pin pty-handler's fallback
  (a retired record re-targets its own exit instead of broadcasting a duplicate
  to subscribers that already have it, and still broadcasts when nothing was
  projected), and drive the reachable retire path: B2's re-attach retires a
  record whose projection only the index remembers.
- Forget the index entry when `exitPublicationSettled` prunes a healthy exit.
  `remember()` only re-ran from the exit path, which B0 short-circuits after a
  settled exit, so every source-mode PTY exit leaked one Map row for the
  daemon's lifetime and would re-publish on any later fallback for that id.

* fix(relay): contain retired exit publication faults

* fix(relay): retain projected exits across owner faults

* fix(relay): retire faulted exit deliveries
2026-07-31 05:44:53 -07:00
NeilandOrca 651f707ce0 [P1] fix(mobile): restore pairing self-heal and recover a wedged handshake (#11690)
* fix(mobile): restore pairing self-heal and recover a wedged handshake

readPairingKeychainItem threw when an Android presence record pointed at a
SecureStore entry that read back null. Android reports absent and undecryptable
identically, so the keystore fault the presence record was added to survive
latched every caller out of its own orphan cleanup: the pairing journal store
never reached its null-secret branch, stale winner-stamped metadata survived,
and every later QR scan failed with "mobile relay pairing recovery pending".
Report absent instead and drop the stale presence claim, still without falling
back to the superseded older generation.

The handshake-timeout path closed the socket with no handleSocketClosed
fallback, unlike the connect-timeout and activity-probe paths. When React
Native omits onclose for a wedged transport the client stayed in 'handshaking'
forever with no reconnect armed.

* fix(mobile): keep the presence pin when a recorded keychain item reads null

Clearing the presence record on the self-heal removed the only thing that
stops readPairingKeychainItem's generation walk, so the next read fell back to
the superseded value under an older generation -- exactly what #11430's
presence record exists to prevent, and reachable for host device tokens and
relay resume bundles after an Android encrypt rotation. Return null and leave
the record in place; the null return alone unlatches every caller's orphan
cleanup, and delete/re-pair already clear or re-stamp the record.

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

* fix(mobile): date synthesized socket closes in transport diagnostics

Move the log-only close clocks behind handleSocketClosed's stale guard so a
synthesized close records them and a late onclose can't clobber the replacement.

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

* fix(mobile): account for delayed synthesized closes

---------

Co-authored-by: Orca <help@stably.ai>
2026-07-31 05:01:20 -07:00
NeilandOrca fdb58695e9 [P1] fix(checks): stop skipped and manual checks reporting as failures (#11700)
* fix(checks): stop skipped and manual checks reporting as failures

Route every check-classification surface through one shared helper so
desktop renderer, desktop main and mobile agree on the same verdict.

- GitLab `manual` jobs and pipelines are neutral again, not action_required/failure
- `skipped` counts as passed everywhere, including mobile
- a neutral check no longer demotes a summary that has passing checks

* fix(checks): move the check-classification parity test into the renderer project

The parity table lived in src/shared but imported a renderer module, and both
config/tsconfig.node.json and config/tsconfig.cli.json are composite projects
that include src/shared without that renderer path, so `pnpm typecheck` failed
with TS6307 on two of its three projects. Only the web project spans both trees.

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

* fix(checks): stop the Tasks-grid pill contradicting its own verdict

The checks pill's label, tone and icon all read one ProviderCheckSummary, but
getChecksLabel short-circuited on the raw `neutral` counter while the tone and
icon key off `state`. After the classification fix a PR with 19 success + 1
neutral renders an emerald CheckCircle2 pill that reads "1 unresolved", and
mobile's own label (which keys off `state`) reads "19/20 passed" for the same
summary.

Move the label into src/shared/provider-check-summary.ts so desktop and mobile
cannot fork it again, and key it off `state`.

Also covers deriveWorkItemCheckSummary, the desktop-main producer of the summary
that reaches the Tasks grid and the relay-paired mobile client. It was rewritten
here with no test at all; the parity table stands in derivePRCheckStatusFromRollup,
which is a different normalizer. The new main-process test drives getWorkItem with
a real statusCheckRollup fixture, pinning the StatusContext `state` fallback that
would otherwise be deletable with the whole suite still green.

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

* fix(gitlab): route the pipeline job-array rollup through the shared check classifier

The array path in derivePipelineStatus kept its own copy of the rollup rules, so
manual-only read green and one unrecognized job status demoted a passing pipeline
to neutral — both disagreeing with every other check surface.

Also retry the packaged-CLI smoke temp cleanup on Windows: the copied Orca.exe can
still be locked by AV/indexers after every assertion passed, failing the package job.

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

* fix(gitlab): stop the skipped pipeline string diverging from the Checks tab

- classifyPipelineString now counts a skipped pipeline as passing, matching
  the per-check classifier; canceled stays neutral and is pinned as an
  explicit, sign-off-pending divergence.
- Pin the production string path (head_pipeline.status) in the parity table
  and note that the job-array branch has no production caller yet.
- Count skipped checks in the Checks panel's passing header so it agrees
  with the checks pill.
- Correct the packaged-CLI smoke retry comment: the EBUSY is the smoke's own
  just-exited Electron process, not AV/indexers.

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

* fix(checks): finish cross-surface check parity and back out the skipped MR-card flip

Review follow-ups on the check-classification PR.

- PullRequestPage and GitHubItemDialog kept private copies of getCheckCounts /
  getChecksSummaryLabel that still counted only `success` as passing, so a
  2-success/3-skipped PR read "2 passing · 3 skipped" there and "5 passing" in
  the sidebar. Both copies move to pr-check-counts.ts, which routes the passing
  bucket through classifyCheckOutcome; action_required keeps its own amber
  bucket. The summary icon now keys off passing count, so an all-neutral PR
  stops painting a green tick above "0 of N checks passing".
- The sidebar checks header and triage strip still called
  `{status: completed, conclusion: null}` pending, contradicting the grey
  "Unresolved checks" pill. Both now read summarizeProviderChecks and render an
  unresolved chip/strip instead of an amber spinner that can never resolve.
- classifyPipelineString('skipped') is reverted to neutral. That flip painted
  MR cards green for pipelines that never ran, on the only GitLab path with
  production callers, and contradicted the same function's deferral of
  `canceled`. Both tone changes stay deferred, pinned by one test.
- classifyPipelineString('manual') resolves to pending rather than neutral: a
  blocked pipeline is outstanding, and neutral let the worktree card fall
  through to its emerald `open` default while GitLab still refuses the merge.
- TaskPage's checks pill helpers move to task-page-checks-pill.ts so the
  "1 unresolved on a green pill" fix is actually pinned by a test.
- smoke-packaged-cli no longer lets an EBUSY cleanup replace the real failure.

* fix(checks): stop completed unknown checks from spinning

---------

Co-authored-by: Orca <help@stably.ai>
2026-07-31 04:58:15 -07:00
NeilandOrca bc6a6e9ff0 [P2] fix(daemon): keep audit-only evidence off the main thread and out of false negatives (#11695)
* fix(daemon): keep audit-only evidence off the main thread and out of false negatives

Three audit-only daemon evidence defects:

- The Windows CIM probe emitted the "absent" marker whenever $p was empty, so a
  non-terminating Get-CimInstance failure (Winmgmt down, corrupt WMI repository,
  access denied) exited 0 with {"exists":false} and was recorded as proof the
  daemon process was gone. The script now sets $ErrorActionPreference and reports
  query failure explicitly; only a query that ran and found nothing yields
  'missing', everything else stays 'unavailable'.
- probeMacosProcess read the process start time through execFileSync('ps') on the
  Electron main thread. It now uses the async readMacosProcessStartedAtMs, matching
  the async command-line sibling in the same probe.
- daemon_audit_eligibility fired on every successful inventory, so routine
  listProcesses traffic could drain the shared 1,000-event per-session telemetry
  ceiling. Repeated identical observations now collapse to one heartbeat per
  5 minutes; a changed observation still emits immediately.

The evidence stays strictly audit-only — no lifecycle, routing or recovery path
consumes the observation.

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

* fix(daemon): take the linux audit start-time read off the main thread

The linux branch of the audit-only evidence probe still bottomed out in
getProcessStartedAtMs, i.e. two readFileSync calls plus an
execFileSync('getconf', ['CLK_TCK']) spawn on the Electron main thread —
the same hazard the darwin half of this change removed. The `async`
wrapper hid it from the type signature and from the main-thread
regression test, which only covered darwin.

That path is reached whenever the pid record carries no start ticks or
boot id: legacy bare-integer pid files (the upgrade population this audit
exists to observe) and any host where /proc boot-id or start-tick reads
fail.

readLinuxProcessStartedAtMs now reads procfs asynchronously and caches
CLK_TCK for the process lifetime (it is fixed for the kernel's lifetime),
retrying only after a failed probe. The main-thread test now covers linux
with a ticks-less pid record.

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

* fix(daemon): measure the audit heartbeat window on a monotonic clock

A backward wall-clock jump (NTP correction, VM resume) made elapsed time negative, which read as "still inside the window" and suppressed daemon_audit_eligibility heartbeats until wall time caught up. Rate-limit arithmetic now runs on performance.now() and treats any backward movement as eligible.

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

* fix(daemon): guard the audit rate limiter and scope the CLK_TCK cache

The rate-limited tracker is the only production emitter, but its payload
serialization and clock read sat outside the guard that keeps audit
telemetry from affecting daemon availability — a throw there propagates
out of listProcesses' try and turns a successful inventory into a
reported inventory failure.

Also key the CLK_TCK cache by the runner that produced it: the value
belongs to the host executing getconf, not the module.

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

* test(daemon): make audit CI synchronization deterministic

---------

Co-authored-by: Orca <help@stably.ai>
2026-07-31 04:56:14 -07:00
LailyandOrcaWin 60d2493bbb fix: route server project adds to selected runtime (#11346)
* fix: route server project adds to selected runtime

* fix: preserve selected runtime for nested project scans

* fix: retain nested review runtime ownership

* fix: preserve nested runtime owner through completion

* fix: preserve captured add repo owners

* refactor: isolate add repo nested review controller

* test: cover selected runtime project adds

* fix: preserve selected project host ownership

* fix: pin SSH nested import completion

* fix: fence missing SSH repo refreshes

* test: cover selected runtime reconnect routing

* test: register selected runtime project routing gate

* fix: preserve selected host across all project adds

* fix: isolate selected-host project catalogs

* fix: preserve host-qualified workspace identity

* test: expect local folder host identity

* test: preserve host-qualified activation assertions

* fix: fence folder refreshes by host identity

* fix: preserve runtime owner across project refresh events

* test: fence selected-runtime reconnect oracle

* fix(runtime): preserve selected host during session activation

* test(runtime): force same-id paired terminal activation

* chore(reliability): register prior selected-runtime evidence

* test(runtime): seed isolated Git identity

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-07-31 04:32:23 -07:00
NeilandOrca d34bbd7917 fix(orchestration): route the legacy coordinator gate at the caller's own Run (#11745)
* fix(orchestration): route the legacy gate at the caller's own Run

The retained-legacy-coordinator gate treated an unnamed Run as the adopted
Run, so callers with no relation to it were fenced with legacy_read_only,
and the adopted Run's NULL coordinator made the owner escape hatch
unreachable.

Resolve the caller's bound Run first and keep the adopted Run only as the
unbound fallback, and treat an unclaimed adopted Run as free — the same
rule bindingMatches() already applies 100 lines down.

* refactor(orchestration): pass the open db handle into boundRunId

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

---------

Co-authored-by: Orca <help@stably.ai>
2026-07-31 02:48:07 -07:00
NeilandOrca f936e7fc9c feat(editor): richer rich-markdown table keyboard (Tab, Enter, Backspace) (#11724)
Co-authored-by: Orca <help@stably.ai>
2026-07-31 01:57:41 -07:00
OrcaWin 9a2676023c fix(orchestration): prefer current authority over legacy fallback (#11737) 2026-07-31 01:56:13 -07:00
Jinwoo HongandOrcaWin 0cc54b73d6 fix(mobile): harden attachments and compact agent statuses (#11671)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-07-31 01:54:09 -07:00
Neil f0221cd419 feat: show GPU acceleration in About panel (#11722) 2026-07-31 01:24:21 -07:00
NeilandOrca 9d473c8c5b fix(windows): stop rejecting .cmd spawns under Program Files (x86) (#11686)
The move of hasUnsafeWindowsBatchSyntax into src/shared/windows-batch-spawn.ts
silently added `(` and `)` to the cmd.exe denylist, so every .cmd shim or
argument path containing parentheses became unspawnable across nine call sites.
Parentheses only group commands and cannot chain one without a separator the
guard already rejects, so they are dropped again.

The rejected character set is now the single source for the user-facing error
strings, and `orca account add` translates the sentinel into a real message.

Co-authored-by: Orca <help@stably.ai>
2026-07-31 01:01:35 -07:00
Brennan Benson f23b3308a5 fix(mobile): route external mouse click and drag to the terminal (#11473)
* fix(mobile): route external mouse click and drag to the terminal

The terminal WebView suppresses mousedown/click at capture so xterm's own
mouse handling stays inert (its onData bytes are dropped by the mobile
bridge). That left hardware mouse clicks and drags with no path at all:
touch taps reached mouse-aware TUIs and drove selection, while a Bluetooth
mouse or trackpad click did nothing (#8818; wheel half landed in #11247).

Add a pointer-event router on the terminal surface (pointerType 'mouse',
left button only) that mirrors touch semantics:

- plain click: same pipeline as a touch tap (links/file paths first, then
  tracking-mode press+release reports, else keyboard focus), and a click
  on an active selection dismisses it like touch does
- drag with mouse tracking: press at the anchor, per-cell motion reports
  (drag/any modes), release on pointerup or pointercancel
- drag without tracking: character-anchored selection reusing the touch
  handle-drag plumbing (edge scroll, handles, copy pill)

Widen the RN gesture-input grammar to pass left-drag motion reports
(SGR button 32, default-encoding byte 64) through the existing
validation and rate limiting.

Mock server: echo the subscribe viewport and serialize scrollback so the
session screen leaves the resubscribe loop, serve the session-tabs
subscribe stream, and add a MOCK_TUI=1 mouse-tracking scenario plus a
[SEND] byte log - the rig used to reproduce and verify this fix on an
Android emulator.

Fixes #8818

* fix(mobile): capture the mouse pointer and clear stale gestures on pointerdown

A drag leaving the terminal surface dropped pointermove/pointerup without
pointer capture, stranding the gesture; a pointerup lost outside the
WebView could leave a tracked press latched until the next gesture.

* fix(mobile): end mouse gestures whose pointerup never reached the surface

Capture the mouse pointer on pointerdown so a drag that leaves the surface
keeps delivering pointermove/pointerup; when capture is unavailable and the
release is lost anyway, synthesize the release from the next buttons==0
pointermove or the next pointerdown, so a tracking TUI is never left with
the left button latched down.

* fix(mock-server): clear the terminal stream interval on resubscribe and unsubscribe

* fix(mobile): synthesize the lost-pointerup release at the pointer's current cell

* test(mobile): split terminal mouse click and drag coverage

* test(mobile): satisfy changed-line quality checks

* fix(mobile): cancel stale mock terminal callbacks

* refactor(mobile): extract mouse report cell mapping
2026-07-31 00:54:40 -07:00
Brennan Benson 48e52540d1 fix(macos): verify Full Disk Access and re-arm notices (#11716)
* fix(macos): verify full disk access status

* perf(macos): keep full disk probe off main thread
2026-07-31 00:51:39 -07:00
Brennan Benson 4f00b21186 fix(mobile): restore Codex chat session identity (#11636)
* fix(mobile): restore Codex chat session identity

* fix(mobile): reconcile native chat session ownership
2026-07-31 00:40:11 -07:00
Brennan Benson 451baa1bc4 fix(mobile): clear state after closing final tab (#11637)
* fix(mobile): clear state after closing final tab

* fix(mobile): clear terminal on empty snapshots

* fix(mobile): preserve terminal during transient empty snapshot
2026-07-31 00:39:44 -07:00
NeilandOrca 336cef3185 [P1] fix(sidebar): drop onto the indicated board position in virtualized lanes (#11688)
The sidebar->board drop translation still read the mounted DOM cards, while
getCardDropTarget now indexes the lane's virtual layout, so a searched and
scrolled lane resolved the drop to the wrong full-lane position.

Co-authored-by: Orca <help@stably.ai>
2026-07-31 00:39:34 -07:00
OrcaWin fe979a402a Fix SSH port-forward rows disappearing after hydration (#11713) 2026-07-31 00:36:09 -07:00
Brennan Benson 037f7a07d3 fix(mobile): open host editor reliably (#11635) 2026-07-31 00:29:01 -07:00
Neil 11936f08ab perf(renderer): lazy load Linear setup dialog (#11687) 2026-07-31 00:25:52 -07:00
Brennan Benson e467b3ff7b fix(remote): stabilize shared control and terminal parking (#11656)
* fix(remote): stabilize shared control and terminal parking

* fix(remote): harden parking review edge cases

* fix(terminal): restore parked local floating buffer

* fix(ci): drop superseded paired parking evidence

* fix(terminal): preserve floating park watchers

* fix(ci): include web client in paired e2e artifact

* fix(ci): reuse renderer build for paired e2e
2026-07-31 00:22:53 -07:00
Eugenio Jesus Jose ValeirasandOrcaWin ef9e6ab9a8 fix(board): stop truncating workflows longer than 12 columns (#11605)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-07-30 23:58:42 -07:00
Neil 94cf2f1422 feat(updater): mark the hourly channel macOS-only in the picker (#11708)
The hourly workflow builds only macOS artifacts, so on Linux and Windows the
channel had nothing to install while still looking selectable.

Adds isChannelSupportedOnPlatform in the shared module so the picker and the
main-process check cannot drift. The segment is disabled with an Apple glyph
and a tooltip rather than hidden, so a dev who has heard of the channel sees
that it exists and why it is unavailable instead of not finding it.

A persisted 'hourly' override arriving off-macOS (settings sync, or a profile
carried from a Mac) now falls back to stable rather than rendering a selected
segment the user cannot act on. checkForPinnedBuild rejects the channel too,
since IPC is reachable regardless of what the renderer disables.
2026-07-30 23:17:19 -07:00
OrcaWin aa6f945001 fix(terminal): serialize startup context with user paste (#11612)
* fix(agents): prevent startup paste from submitting user input

* fix(terminal): cancel startup submit on middle-click paste

* fix(terminal): cancel startup context on first user input

* fix(terminal): record activity only for accepted paste

* fix(terminal): serialize bracketed paste operations

* Revert "fix(terminal): cancel startup context on first user input"

This reverts commit 5459ce7957.

* Revert "fix(agents): prevent startup paste from submitting user input"

This reverts commit affbf333e2.

* Revert "fix(terminal): cancel startup submit on middle-click paste"

This reverts commit 6775751aa8.

* test(terminal): cover startup and user paste ordering

* perf(terminal): keep uncontended paste delivery synchronous
2026-07-30 22:59:32 -07:00
Neil d5c4d953ec perf: coalesce cancellable git status reads (#11691) 2026-07-30 22:55:48 -07:00
Neil f998f7ec62 feat(updater): add hourly dev channel and build switching (#11250)
* feat(updater): add hourly dev channel and build switching

Adds an hourly macOS build channel plus a dev-only surface for switching
update channels and jumping to any published build, including older ones.

Hourly builds publish to a separate stablyai/orca-hourly repo. The routine
update path resolves tags from the main repo's releases atom feed, which
exposes only its 10 newest entries — 24 hourly tags a day would evict every
stable/RC entry there and leave real users with nothing to update to.

Hourly artifacts carry the release bundle id and Developer ID signature so
Squirrel.Mac can swap them in place; only notarization is skipped, which
in-place updates never check.

Version tails are stripped to the base (1.4.160-hourly.<stamp>, not
1.4.160-rc.3-hourly.<stamp>) so hourlies sort below both rc.N and stable and
are reachable only by an explicit pinned jump, never by an ordinary check.

The picker is revealed by Option-clicking the Updates header, matching the
Help menu's existing hidden admin affordance. Pinned jumps set allowDowngrade
and release the feed on every settle path so a jump can never leave background
checks permanently deferred.

* chore(hourly): create orca-hourly and add token provisioning script

Adds setup-hourly-release-token.sh, which provisions HOURLY_RELEASE_TOKEN
without the value ever reaching stdout, argv, or shell history: it is read
with `read -rs`, passed to gh through GH_TOKEN in the environment rather than
as an argument (argv is world-readable via ps), piped into `gh secret set` on
stdin, and scrubbed by an EXIT trap.

Verification creates and deletes a draft release in orca-hourly to prove
Contents:write for real rather than trusting the permission checkbox. Drafts
are absent from the releases atom feed, so the probe cannot disturb users.

Refuses to run without a controlling terminal instead of falling through
having set nothing, and refuses to run under xtrace, which would echo the
token on every expansion.

* fix(updater): address review feedback on the hourly channel

Renderer:
- Guard listBuilds against out-of-order responses. activeChannel flips once
  getVersion resolves, and rapid channel clicks stack requests, so a slower
  earlier load could land last and fill the list with builds from a channel
  the picker was no longer showing.
- Selecting the running build's own channel now clears the override instead
  of pinning it. There was previously no way back to "follow this build's
  channel", so merely opening the panel left background checks pinned.
- Validate releaseChannelOverride on hydration, matching every other
  enum-like field in that function.

Main:
- Exclude pinned jumps from recordCompletedUpdateCheck() in update-available.
  A dev browsing the picker was persisting lastUpdateCheckAt and suppressing
  the next real background check for a full day.
- parseHourlyVersionStamp now anchors on the whole version and round-trips
  the parsed fields. It accepted garbage prefixes, and Date.UTC rolled
  impossible dates forward, so ...hourly.202602300000 rendered as March 2.

Workflow:
- Publish into a draft and flip it live only after the manifest check. The
  window between creating the release and verifying its assets previously
  exposed a tag the picker would offer and the download would 404 on; a
  draft is invisible to listReleaseBuilds, so a job that dies in that
  window — including a hard kill by the job timeout, which runs no cleanup
  step — leaves nothing user-visible behind.
- Add a failure handler that discards the draft, gated on the publish step
  not having succeeded so a later prune failure cannot delete a live release.
- Align retry budgets with the job timeout (was 60min against a worst case
  of ~185min, so a mid-retry kill skipped the cleanup that step exists for).
- Exclude drafts from the freshness and retention queries.
- persist-credentials: false; the job only reads this repo and never pushes.

* refactor(hourly): authenticate with a GitHub App instead of a PAT

A fine-grained PAT expires, and the hourly build would then fail silently on
a schedule nobody watches. A GitHub App's private key has no expiry, so this
is set up once. It is also owned by the org rather than by the person who
created it, so the credential survives that person leaving.

The workflow mints a short-lived installation token via
actions/create-github-app-token and passes it as GH_TOKEN. Installation
tokens live one hour, which is ample: this job runs no tests, no
notarization, and no Windows signing, so it is pack + upload. The retry
budgets and job timeout are re-sized to that reality rather than copied from
the release pipeline, whose 3x45 publish budget exists for notarization and
SignPath.

setup-hourly-release-token.sh now provisions HOURLY_RELEASE_APP_ID and
HOURLY_RELEASE_APP_PRIVATE_KEY. The key is redirected from a file straight
into `gh secret set` on stdin, so its contents never enter a shell variable,
argv, or the terminal.

* fix(hourly): make the xtrace guard fire and cover cancelled runs

The xtrace guard disabled tracing before testing for it, so `[[ -o xtrace ]]`
read the state the previous line had just cleared and never fired. `bash -x`
ran straight through, tracing exactly the key handling the guard exists to
prevent. Test first, then disable.

The draft cleanup only ran on failure(), but a run stopped from the Actions
UI is cancelled(), not failed — a manual cancel mid-publish stranded the
draft. Cover both.
2026-07-30 22:53:02 -07:00
Brennan Benson 0ffdb79d34 fix(skills): surface failed setup commands and retry them (#11630)
* fix(skills): surface failed setup commands and retry them

* fix(skills): preserve failed setup diagnostics

* fix(skills): retire failed terminal before retry
2026-07-30 22:44:49 -07:00
Brennan Benson e1092291ae fix(settings): keep copied skill commands bare for POSIX-family Windows shells (#11599)
The cmd.exe npx preflight added in #10453 silently no-ops when pasted
into Git Bash: MSYS rewrites the leading /d /s /c switches into drive
paths, so cmd.exe starts an interactive session and never runs the
payload. Skip the wrapper when the configured Windows shell resolves to
the posix family, matching the shell the copied command actually lands
in.
2026-07-30 22:41:51 -07:00
Neil 73e243705f fix: stabilize new worktree task source tabs (#11701) 2026-07-30 22:31:00 -07:00
github-actions[bot] a2d0e77143 release: v1.4.163-rc.1 v1.4.163-rc.1 2026-07-31 04:49:20 +00:00
Jinjing 239c027693 Keep Create PR intent running when review lookup is unavailable (#11678)
* Fix Create PR preparation with unavailable lookup

* test(source-control): align dirty+unavailable intent expectation

Create PR preparation is allowed when review lookup is unavailable; only final create stays fail-closed. Update the local-blocker snapshot test to match.

* Keep Create PR intent running when hosted review lookup fails

A failed or timed-out hosted-review eligibility lookup no longer aborts
Create PR intent mid-run. Local prep (stage/commit/push) continues, the
branch-ahead refresh is deferred until after eligibility resolves, and the
final create preflight still fails closed to prevent duplicate reviews.

Also gate generated PR title/body on eligibility and thread the provider
through the intent run token so an unavailable lookup falls back to the
inferred remote host.

* fix(source-control): align dirty+unavailable intent expectation

Local preparation (stage/commit changes) is safe without review-lookup authority; remote actions stay blocked. Prevents dirty trees from dead-ending at sync-first when lookup is unavailable.

* fix(source-control): distinguish loading state from unavailable lookup

Require head branch presence in shouldAttemptCreateHostedReviewForIntent to
separate real unavailable-lookup results from loading placeholders, which
share the same outcome/reason pair but lack a branch name.

* test(activity): drive portal readiness latch release with explicit rAF

Wall-clock setTimeout waits for requestAnimationFrame were flaky under
CI load (shard 15/16), leaving status stuck at loading instead of ready.

* Distinguish expected absence from git errors in remote removal

Why: swallowing all errors silently masks genuine git failures.
Check presence explicitly instead, so setup/teardown can still
skip when origin is absent while letting real errors surface.
2026-07-30 21:45:44 -07:00
JinjingandClaude Opus 5 0515e57c60 fix(browser): clear grab mode operation queue on teardown (#11679)
A pending grab mode chain could outlive its guest: registerBrowserHandlers()
and browser:unregisterGuest cleared grabModeIntentByPageId but left
grabModeOperationByPageId intact. An in-flight executeJavaScript against a
destroyed guest would then block every later operation queued behind it for
that page, including after a workspace restart or browserPageId reuse.

Addresses review feedback on #11661.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 21:05:13 -07:00
Jinjing aeda3c6e32 fix(browser): resolve grab mode race conditions and teardown (#11661)
- Serialize grab mode operations to prevent concurrent state changes
- Wait for guest registration with timeout when page hasn't registered yet
- Tear down armed overlay when disabling before selection starts
- Distinguish injection-failed from not-ready errors in UI
- Track arm generations to cancel stale operations after page switches
- Add localized error messages for each failure reason
2026-07-30 21:00:36 -07:00
OrcaWin 6ae19be723 [P0] fix(terminal): pause hidden paired output (#11665) 2026-07-30 20:58:11 -07:00
Brennan Benson 8fef6db292 Fix duplicate remote worktree delete failures (#11623)
* Fix duplicate worktree delete attempts

* Complete delete flow selector mock

* Normalize duplicate delete selections
2026-07-30 20:32:40 -07:00
Jinjing b5abab8b09 Virtualize workspace board lanes for faster open (#11269)
* Virtualize workspace board lanes and defer card render for instant open

* fix(review): harden kanban virtualization interaction edges

Cancel deferred card mount on close, re-apply marquee preview after remounts,
query drag styles from live DOM, drop re-exports/casts, and cover edge cases.

* Remove virtualizer from effect deps to avoid unnecessary re-runs

* Fix stale closure in kanban area-selection and card-drag handlers

- Refresh area selection measurements on pointer up to avoid stale cache
- Read worktree IDs ref directly in drag handler to close stale closure

* Remove itemIds ref for correct virtualizer layout memoization

The card list kept itemIds in a mutable ref that was manually synced on every render, so the layout registration effect never re-ran when the lane contents changed, leaving stale measurements. Read itemIds directly and declare it as an effect dependency so layout registration stays in sync with the items.
2026-07-30 19:39:33 -07:00
NeilandOrca cc078a5021 perf(main): move hang watchdog into a worker thread (#11488)
* perf(main): add watchdog boundary memory benchmark

Add a repeatable Electron 43 RSS harness that measures the production-built watchdog entry across the child-process and worker-thread boundaries. Record per-trial samples, the median, revision, runtime, and settling procedure for reproducible PR evidence.

* perf(main): move hang watchdog into a worker thread

Keep main-thread hang detection independent of the blocked Electron event loop without paying for a second ELECTRON_RUN_AS_NODE process. Preserve the marker and telemetry contract while moving timing configuration and heartbeats onto a bundled worker entry.

* test(main): smoke packaged hang watchdog worker

* fix(main): make packaged watchdog smoke able to fail

The smoke reported failure only through process.exitCode, but its finally
block quit Electron gracefully, and Electron takes its status from the
browser exit code. Every failure mode — entry missing from app.asar, worker
error, marker timeout, non-zero worker exit — exited 0 with the diagnostic
discarded on stderr, so the required PR check could never go red.

Propagate a real status via app.exit, assert the success line in stdout, and
surface stderr. Verified against a packaged tree with the entry removed:
exit 0 before, exit 1 after.

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

---------

Co-authored-by: Orca <help@stably.ai>
2026-07-30 19:33:05 -07:00
Neil 14de3fa14d fix(computer): reap mac helper after client loss (#11493)
* perf(computer): add mac helper owner-loss benchmark

Measure the release helper's resident memory before and after its owner-session deadline. Record exact revisions, per-trial RSS, retained state, and clean-exit latency so lifecycle reclamation is reproducible.

* fix(computer): reap mac helper after client loss

Bind the detached macOS helper lifetime to authenticated socket ownership. Reap the helper after its final authenticated client disconnects, and add a startup deadline for sessions that never authenticate.

* test(computer): harden owner benchmark cleanup

* test(computer): make owner benchmark cleanup failure-safe

* test(computer): close remaining owner cleanup races
2026-07-30 19:24:34 -07:00