Commit Graph
16079 Commits
Author SHA1 Message Date
Brennan BensonandOrca Worker a75cdc8287 fix(chat): match terminal pane split and close menus (#28969)
* Transfer keyboard focus when a sibling chat pane opens

* fix(native-chat): hand focus to newly opened chat composer

* fix(native-chat): keep tab bar controller within line limit

* fix(chat): split into a fresh conversation beside the current chat

* test(chat): declare the split fixture store without an assertion

* fix(chat): route the split to the source conversation host

* test(chat): keep the split regression focused on the launch contract

* test(chat): give the command palette split fixture its conversation agent

* test(chat): seed the legacy command fixture without expanding its asserted shape

* test(chat): isolate pane identity commands from the split launch boundary

* Prevent chat splits from starting a terminal on host refusal

* Cancel structured launches when their destination pane closes

* Model live destination groups in agent launch test fixture

* Capture split chat focus intent at the user action

* feat(chat): add the terminal pane hover action cluster

* fix(chat): keep pane controls above the conversation surface

* refactor(chat): share the tab resume launch hook and format pane controls

* style(chat): document reuse of the terminal header button styles

* refactor(ui): share the pane title button variants between terminals and chats

* fix(chat): keep header actions scoped to the displayed chat

* fix(chat): abandon Resume when its destination pane closes

* test(chat): declare destination fixtures without type assertions

* fix(chat): keep Resume visible when its source pane closes

* fix(chat): reuse tab menu Resume placement unchanged

* test: fix Resume regression harness setup

* feat(chat): match terminal pane split and close context actions

* style(chat): format the pane context menu

* style(chat): format the structured pane command fixture

* test(chat): remove the obsolete tab-movement menu stubs

* Match terminal close-pane menu styling

* fix(chat): share the pane action close label in the body menu

* test(chat): match structured pane action fixture returns

* fix(chat): hand input to the split pane during admission

* fix(chat): Split opens a fresh chat beside the current one

Split Right/Down on a native chat used to move the chat into a new pane.
It now opens an empty pane beside the chat and launches a new chat of the
same agent on the same host into it through the New Chat launch, so the
new composer takes focus at once. A launch that never opens a tab closes
the empty pane. Drops the earlier admission and focus-transfer changes
that main now covers.

* fix(chat): Split palette search words describe a new chat, not a move

* fix: remove misleading chat move shortcut hints

* fix(chat): a new chat stays in its pane until its host lists it

A host tab list built before a chat's create landed can arrive after the
create reply. Once the reply settled the launch, nothing protected the new
tab, so that older list dropped it: Split's new pane collapsed and focus
moved to another chat, and the next list re-added the chat elsewhere.

Keep a small in-memory record for a chat this client created that no host
list has shown yet. While it exists, a list that omits the chat keeps its
tab. The record ends when an accepted list from that host includes the
chat, when the tab closes, on workspace teardown, or 30 seconds after the
reply, when the worktree is re-read from its host (including an unchanged
version) so a chat the host really closed is dropped then. The launch
lifecycle, status, persistence and settlement are unchanged.

* test(chat): type the faked create replies without an assertion

* Keep new chat protection tied to accepted host lists

* Update paired chat close regression to current suppression API

* fix(chat): Portuguese Split palette words describe a new chat

* test(chat): use the destination group option in split fixture

* Avoid History projection in native chat header and currentness checks

* fix(native-chat): keep Find below pane controls

* fix(ui): keep noninteractive tooltip layers pointer-transparent

* fix: derive chat pane menu state inside mounted content

* test: use complete settings fixture for chat menu contract

* ci: refresh chat menu checks after base correction

* ci: refresh chat menu checks after base type fixes

* test(chat): focus retained menu lifetime contract

---------

Co-authored-by: Orca Worker <orca-worker@localhost>
2026-10-23 22:12:54 -07:00
Brennan Benson f04b831a61 Record Stop immediately and settle subagents from exit proof (#28051)
* Observe Claude readiness through the host status

* Keep optional readback behind host readiness

* Recheck launch permission after host readiness

* Preserve the awaited child during permission revalidation

* Await Codex protocol open under host ownership

* Await ACP protocol open in the host

* Derive ACP opening from its protocol session facts

* Exercise unanswered ACP options with an explicit silent route

* Await Pi open and fail signed-out construction

* Read Pi catalog facts from awaited opening

* Keep the Pi fixture exit observer

* Record failed owned opening before the first message

* Abort a matching provider open when its child exits

* Cover provider exit during an unanswered host open

* Cover explicit failure facts on a host-stopped child

* Capture message choices per accepted send and preserve picker state

* Apply accepted Claude message choices at prompt handover

* Apply captured message choices at provider dispatch

* Bind native chat choices to accepted message dispatch

* Reset Codex turn choices for each captured message

* Resolve Codex message defaults from native facts

* Align message choice and Pi default fixtures with provider dispatch

* Align option fixtures with saved message choices

* Match Codex message choices to the active turn origin

* fix: let pending commands reach handover refusal

* Abort pre-prompt message choice writes with host-owned wait

* Verify message choices through provider input boundaries

* Keep legacy mobile test hosts explicit about message choices

* Align Stop close causes and request-note fixtures

* test: align ACP Stop note fixtures with settled behavior

* Verify close intent reaches the native adapter

* Record the Stop request before waiting for interruption

* Keep Stop cleanup independent of request-note persistence

* Keep startup waits cancellable and preserve operation order

* Derive Codex startup exits from host phase

* Open Codex protocol in background-task test fixtures

* Keep ACP startup ownership until handshake completes

* Run ACP opening catalog fixtures with Node SQLite

* Verify saved ACP picks without live option writes

* Keep ACP catalog bookkeeping from failing an options read

* Restore signed-out restart and fail Pi startup faults

* Run Pi opening catalog fixtures with Node SQLite

* Preserve message choices across cold clients and kept cards

* Remove the unused provider option restore ledger

* Clarify launch choices and legacy option hydration

* Remove remaining restore-ledger forwarding and assertions

* Remove unused ACP and Pi restore bookkeeping

* Report late Codex exits and keep card Cancel notes scoped

* Remove the Pi close wrapper left by unused cause plumbing

* Update Stop tests for the simplified close contract and Node journals

* Update remaining close and interrupt assertions after cause removal

* Keep live choices and delivery order while agents start

* Drop retired startup revision from Codex opening

* Keep live-option cancellation until message choices and clarify catalog reads

* Hold ACP option replies in live-write cancellation regressions

* Remove retired Pi startup revision and match saved-pick behavior

* Check running choices and retained rewind messages after delivery integration

* Use observed root exit in Codex close reports

* Apply startup picks and let parked messages release operations [skip ci]

* Remove unused Codex opening report field [skip ci]

* Keep ACP restoration facts local to the provider [skip ci]

* Apply Pi startup picks after readiness and remove unused open facts [skip ci]

* Cover goal changes during the held Codex message wait [skip ci]

* Save a waiting startup pick after Stop ends its opening [skip ci]

* Save choices when startup ends or a replacement is still opening

* Check saved choices separately from Claude startup reports

* Release scripted Codex startup before awaiting queued picks

* Check the saved picker selection in per-message choice fixtures

* Exercise current choices and eager client capabilities in runtime fixtures

* Keep accepted Stop requests in runtime settlement assertions

* Reuse runtime fixture reads and type the fake connection directly

* Type the Codex child-proof fixture connection directly

* Align startup fixtures and preserve observed Claude exits

* Honor queued send authorization in Claude runtime fixtures

* Open Codex protocol sessions before Stop fixture dispatch

* Wait for the opened Codex child before runtime notifications

* Keep repeated permission picks usable and align Codex fixtures

* Validate pending picks against the live option state

* Order startup picks before later messages and retain flow regressions

* Keep reviewed lifecycle contracts while pruning duplicate tests

* Save picks after a proven exit without requiring a child

* Type the startup reply fixture against the Claude control contract

* Keep Codex startup flow contracts and remove overlapping adapter tests

* Restore the committed dependency lockfile after test tooling

* Keep startup recovery flows and remove duplicate Pi handshake cases

* Cover both Pi startup choices in the delivery flow

* Match the Pi prompt entry recorded by the scripted wire

* Honor the before-input callback in the startup reply fixture

* Keep message choice flows and remove overlapping selector tests

* Retain Stop flows and remove repeated reducer and adapter cases

* Keep Stop note writes independent of full session context

* Keep startup option menus independent and show the saved choice

* Exercise startup picks across real menus and the runtime

* Match startup picker fixture to saved message choices

* Keep saved effort visible when picking a model during startup

* Include the running-turn scope in repeated Stop fixture

* Use the current attach contract in import fixtures

* Keep the Codex journal translator within its line limit

* Match ACP startup fixtures to saved picks and late Stop behavior

* Check startup effort after saved picker choices are acknowledged

* Complete Stop target types and keep the effort regression concise

* Use requested picker values in the effort regression acknowledgement

* Exercise Stop receipt replay across a resumed provider child

* Require owned provider spawn and one host-awaited open (from #28055)

* Restore the live checks the startup cleanup's deleted tests pinned

One assertion each, in the surviving suites: a stale-generation exit is
ignored, a failed create lets its sink go, a readback before open is saved,
an idle stop that fails is retried, a no-pick Pi start teaches its default,
an older client's pre-spawn failure and packed answer, an undrivable chat
stays readable, the resting slash menu stays on its own host, the main agent
reads apart from subagents, restore publishes status, naming runs off the
host's status feed, and a throwing logger never reaches the operation.

Also corrects the reliability gate note that claimed the send-idempotency
suite replaced the refusal-retry tests.

* Read a rewind in doubt outside the chat's serialize and apply it in the commit

The started settle and the attach's ready-child path read the provider's
history first, then apply what it proves in a short commit that re-checks
the rewind it read.

* Accept messages while the agent starts: run the start outside the chat's serialize

The chat's serialize was both where a send was admitted and where its agent
started, so every slow step of a start (closing-child join, lease
reconcile, recovery probes, resolvers, history read, spawn, sink drain,
provider dispatch) held every later send.

A start now registers in one short step before its first wait: its abort
controller (which Stop, close and quit already reach outside the lock),
its startup clock (the 10-minute ceiling now counts from here) and quit's
drain. Every wait after runs outside the lock, raced with that signal, and
every write is a commit that first re-checks the start is still the
session's. A start that loses the check is late: its spawn is closed
through the post-acquisition failure path and nothing is handed to it.

- The delivery loop's prepare returns a start handle; the loop awaits the
  start outside, then re-prepares. Handover writes the pending row and
  begins the dispatch abort handle in one commit, calls the provider
  outside after re-checking the child, and resolves in a second commit.
- Ready operations claim a start instead of running one in their step.
- A client attach runs the same steps under one serialize, as before.
- An exit reported between spawn and publish is held for the publish
  commit, which ends the child it indexes.
- An unproven close still fails only what was queued when its start was
  asked for.

* Test that a send is accepted at every wait an agent start makes

One whole-flow test holds the start at each wait in turn (closing-child
join, reconcile probe, recovery re-probe, resolver, spawn, sink drain,
open, started recovery, pending row, provider call): a second send is
accepted at once, a Stop is recorded without waiting and nothing reaches
the provider, and what was sent goes to the published child once, in
order. Also quit before spawn, concurrent sends, a lease left recovering,
and an idle sweep racing a send.

* Bound each startup step on its own and drop the startup silence rule

A start no longer ends after 60 s without output: output is not protocol
progress. Each protocol step now has its own bound where its handshake
lives, and one 10-minute ceiling from registration stays as the outer bound.

- Codex: initialize 15 s and thread start/resume on the connection's 30 s
  default again, instead of the startup ceiling.
- ACP: initialize 60 s, authenticate 120 s, session new/load/resume/fork
  300 s; turns stay unbounded and option writes keep their caller's bound.
- Claude: the initialize answer has a 60 s bound; past it the open fails
  with the ordinary startup-failure words and the host ends the child.
- The onOutput plumbing that only fed the silence rule is removed.

* Count Pi ready once it reports its state

Pi's start no longer waits for its model listing and slash commands.
The open returns after get_state and the unknown-provider check; the
listing then arrives as an options report (with the catalog listing the
start resolved) and the commands as a later publish. An empty model list
no longer fails the start as signed out: a signed-out Pi runs the unknown
provider, which still fails it, and a prompt it cannot run is refused
with the sign-in words by the existing prompt path.

get_state keeps the connection's 30 s request bound instead of the
startup ceiling.

* Test that model and effort picked while a chat is created ride its first message

A slow host's create reply arrives after the user picked a model and effort. The picker's view
layers the launch's held picks over its seed and the host's saved selection, and every send
carries what the picker shows, so the first message carries the picks whether the host's status
names its selection or not, and while the last pick is still on its way to the host.

* Start a phone's new empty chat on its first message

The phone's new chat is an agent.launch with no prompt, which always created the chat with its
agent started, while desktop chats start on the first message. The phone now advertises the
create-without-start capability, and a launch with no prompt from a caller advertising it creates
the chat at rest, keyed in the ledger by its create intent as a client's create is. Launches with
a prompt, the CLI (which advertises nothing), orchestration workers and ensure still start at once;
an older host ignores the capability and stays eager.

* Take main's lockfile; the branch changes no dependencies

* Restore main's exact text in files the merge only reformatted

The pre-commit formatter rewrote 37 of main's files staged by the merge, though this branch never changed them, pushing two past the line limit. Committed without the hook because the hook would reformat main's text again; CI lints the result.

* Fit the startup tests to the owned-spawn, host-awaited-open contract

Test doubles gain the mandatory open and an acquisition generation, the
removed started event gives way to the rig's proveStart, and two literal
types are tightened for CI's typecheck. A Stop during a held start writes
no host note on this branch: the taken-back send draws its own
stopped-before-start row, which the startup-attempt test now asserts.

* Fit the remaining CI checks to the startup limits and current releases

- Run agent-launch-at-rest.test.ts in the SQLite Node runtime project.
- Pin the send-options cross-version case to v1.4.228, the first published release with agent-session.send-options.v1; v1.4.226 was never pushed.
- Load the baseline's isStructuredTab from src/shared (moved in v1.4.228), falling back to the renderer path for older baselines.
- End the never-proven start with a short startup ceiling: the silence limit it relied on is gone, and the ceiling counts from registration.

* Read the option stamp from the chat's one host-execution selection

* fix: preserve startup authority and accepted agent choices

* Retain dispatch authority and reconcile accepted options

* fix(native-chat): preserve accepted choices and cancellation authority

* Restore established client selection flow

* Preserve provider output observation and typed startup fixtures

* fix: preserve initial Claude launch choices and effort readback

* test: complete Claude catalog session fixture

* Preserve incoming main files after merge formatting

* Preserve compact handover through startup merge

* Type instant startup fixtures before their open is supplied

* Keep browser sign-in under its existing expiry owner

* Finish the main merge: retired-model display, start rewind cut, new tests

A model the Claude CLI retired is reported again, so the picker shows what the child runs and the
next send moves the chat off it. The start reads a rewind in doubt against the handle it answered
with, the record its commit then applies to. Main's tests follow the start's open: the admission
fixture holds a real turn, and a sign-in close during setup fails the open.

* Type the start's rewind record as the store returns it

* Type the Sleep test's held open as the provider open it replaces

* Sleep counts a chat stopped once the start it aborted leaves it at rest

A start runs outside the chat's lane, so Sleep's step can run before the start it aborted has
settled; it then re-reads the chat once that start is done, instead of reporting it unverified.

* Keep Sleep's start-abort bookkeeping beside the rest of Sleep's stop

* Keep the listed background Stop lifetime cases

The two #29905 cases lived in the turns test this branch removed. They pin that a listed Stop never ends a helper lifetime it did not capture: a same-id restart during acceptance, or a refused helper that appears mid Stop-all, which must not close the agent.

* Reconcile test registries and queued provider work accessor

* Update CI scope fixtures after removed connection test

* Bound native chat Stop protocol steps and omit empty startup feedback

* Supply current session fence in empty-start Stop test

* Align goal cancellation test with accepted-message feedback

* test(native-chat): cover distinct repeated Stop through Codex adapter

* fix(native-chat): save dispatch proof before Stop closes the child

* ci: retrigger checks for Stop dispatch repair

* Keep accepted Stop effects through dispatch settlement

* Capture earlier Stop evidence before writing request feedback

* Preserve named Stop runtime effects across turn advancement

* Combine projection imports in named Stop regression
2026-10-23 22:10:55 -07:00
Kelvin Amoaba 785daec1e1 fix(browser): show profiles changed outside the window in the profile menu without a reload (#32723) 2026-10-24 05:06:34 +00:00
Brennan Benson 2e278adeac Chat worker errors alert the orchestration coordinator (#31941)
* feat(orchestration): a chat worker's turn-ending error alerts its coordinator

A native-chat agent working a Dispatch whose turn ends in an error (usage limit, signed out,
provider refused, agent died mid-turn, failed start) now sends one high-priority escalation to
the Run that dispatched it, carrying the error sentence the chat recorded plus the agent's own
detail. The task stays assigned to the chat. Terminal workers already escalated on exit; both
now share one escalation helper.

On by default; a new switch on the Agent Orchestration settings page turns it off. It only
works for agents in Chat UI, so it is disabled while Chat UI is off.

* fix(native-chat): observe committed worker request failures

* Fix chat error alert fixture types and catalogs

* Keep chat failure alerts scoped to their request and journal lifetime

* fix: capture pending chat failure diagnostics before escalation

* fix: type chat failure observation fixtures and journal reads

* fix(orchestration): use checked database during worker exit

* test: bound chat failure observation and diagnostic work

* test: check ACP acquisition generation before capture

* test: bound native worker end history traversal

* test: type native exit fixture record lookup

* test: follow the journal context clear signature

* test: bound legacy display work for worker failure alerts

* test: type shared request failure rig mocks explicitly

* test: cover paired web chat error preference hiding
2026-10-23 22:05:46 -07:00
OrcaWinandOrcaWin 2eb0bb637d test: strengthen hook, release, and mobile transport coverage (#33031)
* test: exercise hook and decorative title limits

* test: reject non-string launch preferences

* test: preserve live builds on release rejection

* test: verify restored child and expiry session state

* test: strengthen mobile transport lifecycle coverage

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-23 21:53:27 -07:00
Kelvin Amoaba 5dc197e5ec feat(browser): history controls: Chrome-style Escape, removable suggestions, history view (#31846) 2026-10-24 04:46:47 +00:00
Kelvin Amoaba 1753f03e33 fix(browser): render dark-scheme documents on a dark canvas in doc previews (#32975) 2026-10-24 04:45:13 +00:00
Kelvin Amoaba f01eaa0f62 fix(browser): let pages use files picked, saved or dropped through File System Access (#32970) 2026-10-24 04:44:46 +00:00
OrcaWinandOrcaWin cc929aaadb test: strengthen hook, navigation, and release coverage (#33028)
* test: strengthen hook identity and transport assertions

* test: strengthen mobile handoff and release policy coverage

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-23 21:22:38 -07:00
NeilandBingxin Xu cd2eeebe3e perf(ssh): opt-in proxy connection reuse for system SSH on Windows (#33030)
* perf(ssh): reuse one proxy connection for system SSH on Windows

Windows OpenSSH cannot multiplex (ControlMaster fails with "getsockname
failed: Not a socket"), so every system-SSH exec for a ProxyCommand or
ProxyJump target re-ran the proxy handshake. Hold one `ssh -N -L` through
the real proxy per session and dial later execs over 127.0.0.1 with
HostName/Port/ProxyCommand=none/ProxyJump=none/HostKeyAlias overrides.

The route starts after connect, is proven with a login before use, falls
back to per-command proxying on any failure, honors
systemSshConnectionReuse: false, and is torn down with its proxy child.

Rebased from #31731 (commits b1cdd46, 3835c2d, 6268f2f) onto current main,
including the pending-route cancellation fix from b301b6a.

Co-authored-by: Neil <neil@stably.ai>

* refactor(ssh): stop the loopback forward through process-host's tree kill

Reuse signalProcessTree instead of a hand-rolled taskkill-then-kill
fallback: it already joins an in-flight tree kill, refuses a reaped PID,
and falls back to killing the root, so the route keeps no parallel copy
of that logic.

* fix(ssh): harden the Windows loopback route against review findings

- Manual targets: take the forward's sshd port and the HostKeyAlias from
  the stored endpoint; `ssh -G` ran on the label and named another host.
- Hold the forward with ServerAlive probes so a silently dead proxy ends
  it and commands fall back instead of stalling on 127.0.0.1.
- Never route forwards: their argv must stay stable for orphan recovery,
  and the route's own forward must take the real proxy.
- Upload sessions read the route per file, so an exited route's port is
  not dialled after it closes.
- Skip the route for security-key identities, whose forward and probe
  logins would each ask for an unprompted touch.

* fix(ssh): keep the loopback route for manual targets no Host block claims

The unclaimed-alias endpoint rewrite only applies to config-backed targets,
so manual targets with a configHost no block names stay eligible.

* fix(ssh): keep the held loopback forward non-interactive

The forward is an optional background login; with BatchMode=no a password or
passphrase login raised an extra prompt right after connect, which the
non-interactive probe then discarded anyway.

* fix(ssh): admit the loopback route only when it changes nothing but the route

Overriding HostName to 127.0.0.1 changes which ssh_config blocks match, so a
`Match host <real host>` block could silently switch the remote User (or
identities, certificates, known-hosts files) for every routed command. Run
`ssh -G` with and without the route options and refuse the route on any
difference outside HostName/Port/HostKeyAlias/ProxyCommand/ProxyJump.

Also ask the agent with `ssh-add -L` and refuse the route when it may hold a
security key, so the background forward and probe never request a touch.
Both checks run through process-host and fail closed.

* feat(ssh): make Windows proxy connection reuse an opt-in per-target setting

The loopback route rewrites HostName, which ssh_config can key settings on,
so it no longer turns on by itself: a new optional systemSshProxyConnectionReuse
field (default off, Windows only) enables it from the SSH host's Advanced
settings. Old clients and servers ignore or omit the field.

Admission stays as the safety net and now:
- takes the forward's sshd port and HostKeyAlias from the direct `ssh -G`,
  so a manual host's own Host block cannot send the route to another sshd;
- refuses any non-routing value with a `%` token, which `ssh -G` can leave
  unexpanded while a routed login expands `%h` to 127.0.0.1;
- checks the security-key and agent identities of every ProxyJump hop;
- asserts the routed config really dials 127.0.0.1 with no proxy left.

* fix(ssh): offer proxy reuse only for hosts this desktop dials, refuse ${VAR} paths

- The switch now appears only for this desktop's own SSH hosts. A paired
  server's add-host dialog never shows or sends it, so an older server's
  strict schema is never handed the new field, and a client's platform no
  longer decides a server's setting.
- Admission also refuses `${VAR}` references, which `ssh -G` prints
  unexpanded while a login expands them.
- Advanced no longer auto-opens just for the opt-in.

---------

Co-authored-by: Bingxin Xu <xubingxin.xbx@gmail.com>
2026-10-23 21:22:08 -07:00
Neil 77efa5aeeb refactor: colocate remote runtime error classification (#33024) 2026-10-23 21:08:15 -07:00
OrcaWinandOrcaWin 2fd28bdf82 test: strengthen stateful assertions and remove redundant checks (#33020)
* test: exercise relay conflicts and full transcript previews

* test: strengthen stateful witnesses and remove redundant checks

* test: use type-only imports for mock RPC signatures

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-23 21:00:55 -07:00
8604a4d570 fix(bitbucket): recognize ssh.bitbucket.org remotes (#31002)
* fix(bitbucket): recognize the SSH host alias

* fix(bitbucket): recognize the SSH host alias on current main

Apply Chen's original SSH alias fix to current main, preserving canonical
Bitbucket browser links and adding case/port and lookalike-host coverage.

Co-authored-by: Chen <zwq19980411@gmail.com>

---------

Co-authored-by: Neil <neil@stably.ai>
2026-10-23 20:43:39 -07:00
df97aa3a6f fix(gitlab): recognize SSH-over-443 remotes (#30998)
* fix(gitlab): recognize SSH-over-443 remotes

* fix(gitlab): canonicalize SSH-over-443 project and review URLs

Reuse the existing GitLab transport-host normalizer in project identity,
hosted links, and manual merge-request links. Cover alias variants,
lookalikes, nested groups, and canonical encoded links.

Fixes #30876

Co-authored-by: lorenzozanee <wyz0707@proton.me>

* test(gitlab): cover alias identity consumers

---------

Co-authored-by: Neil <neil@stably.ai>
Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
2026-10-23 20:30:43 -07:00
AnmolandNeil 9b15b2bbec fix(ai-vault): show Copilot sessions under their workspace with the original prompt as title (#31160)
* fix(ai-vault): show Copilot sessions under their workspace with the original prompt as title

Copilot sessions in Agent Session History showed "Unknown location" and
titles prefixed with an injected <current_datetime> tag.

The Copilot parser only learned a session's folder from the
"Folder X has been added to trusted folders" notice, which Copilot does
not write for every session. It now reads the launch directory, git
root and branch from session.start's context, and the trust notice only
fills a folder the start did not name, so resume runs in the start
directory as it does for other agents.

User messages now take the original `content` before
`transformedContent`, which carries context Copilot injects.
`current_datetime` joins the hidden blocks so a transformed-only
message still gets a clean title.

Bump the session parse cache schema so histories cached with the old
parser are re-read once instead of keeping the wrong folder and title.

Fixes #30985

* fix(ai-vault): refresh cached Copilot metadata and search text

---------

Co-authored-by: Neil <neil@stably.ai>
2026-10-23 20:30:39 -07:00
9f079b12b3 fix(terminal): re-probe stale "missing" path results so terminal links go live (#6282)
* fix(terminal): re-probe stale "missing" path results so links go live

The terminal link provider caches filesystem existence per pane, but the
cache had no expiry on negatives. A path printed a moment before its file
exists (e.g. an agent announcing a file it is about to write) got cached
as "missing" and stayed un-clickable forever, even after the file landed —
while a later mention of the same path resolved fine.

Give negative results a short TTL: a cached "missing" older than ~10s is
treated as a cache miss so the next hover re-probes the filesystem.
Positives stay cached (files rarely vanish mid-session, and a stale
positive fails gracefully on open). Cache entries become
{ exists, checkedAt }; both readers (the link provider and the click
fallback) go through the shared read helper so the TTL applies uniformly.

Refs #5024

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(terminal): preserve negative path cache expiry

* fix(terminal): ignore stale negative link probes

Overlapping hover scans can re-probe the same expired missing path. Stamp each probe with its start time and drop a result older than the cached one, so a late "missing" no longer hides a link a newer probe found.

* fix(terminal): order same-millisecond link probes with a sequence

* fix(terminal): retain expired link probe ordering

---------

Co-authored-by: gatsby74 <166927047+gatsby74@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
Co-authored-by: Neil <neil@stably.ai>
2026-10-23 20:30:35 -07:00
NeilandChen 71be80ca8a fix(automations): list a web client's paired server once, not again as Desktop (#33019)
* fix(automations): list a web client's paired server once, not again as Desktop

A paired web client's local runtime calls go to its server, so the desktop
authority and the saved runtime are one runtime. The Automations catalog
projected both and every automation appeared twice (Local <OS> + Orca Server).

The catalog now folds the desktop authority into the saved runtime that
getPairedWebClientEnvironmentId names, and a saved Desktop host filter falls
back to All hosts instead of waiting forever for an entry that never comes.

Co-authored-by: Chen <zwq19980411@gmail.com>

* fix(automations): move a saved Desktop host filter onto the paired server

On a paired web client the Desktop host is the server, so a saved Desktop
selection now names the server's matching host instead of being dropped with
a fallback notice on every visit.

Co-authored-by: Chen <zwq19980411@gmail.com>

* fix(automations): route web run links and sole-host choice through the folded server

Codex review: with Desktop folded into the paired server, a workspace run link
carrying hostId 'local' never matched a row and the run page stayed loading,
and the sole create host could be picked before the server's SSH targets
loaded. Pending navigation on a paired web client now names the server's
runtime, and desktop SSH hydration follows the runtime Desktop folded into.

Co-authored-by: Chen <zwq19980411@gmail.com>

* fix(automations): match web run links against the folded server at use time

Resolving the pending navigation's host when it is matched, not when it is
stored, also covers a link opened before the server environment list loads.
Replaces the store-setter rewrite from the previous commit.

Co-authored-by: Chen <zwq19980411@gmail.com>

---------

Co-authored-by: Chen <zwq19980411@gmail.com>
2026-10-23 20:26:57 -07:00
OrcaWinandOrcaWin 4d22306d25 test: remove workflow string checks and strengthen provider assertions (#33017)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-23 20:17:20 -07:00
cedce2aa9f feat(cli): add orca ports to list a workspace's detected ports (#33016)
* feat(cli): add orca ports to list a workspace's detected ports

Scripts and agents had no way to read the ports Orca already detects for
a workspace, so finding a dev server on a random port meant redoing the
lsof scan by hand. `orca ports` reuses the host-scoped
workspacePorts.scanHost RPC, keeps the rows owned by the current (or
--worktree) workspace, and prints port, pid, process, connect host,
protocol, how the owner was matched, and the advertised URL. --all shows
every listener on that host. An older runtime without scanHost gets an
"update Orca" error instead of a raw method_not_found.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(cli): resolve folder workspaces in orca ports and match rows by path

Run inside a native folder, `orca ports` failed with selector_not_found
(or picked an enclosing git worktree) because it only searched the
worktree list. It now resolves the cwd like `agent launch`, native
folders first, and shows that host's listeners. Rows are matched on the
owner's path instead of its id, so every instance of a repo-backed
folder workspace keeps its ports even though attribution names only the
first instance as owner. `--all` adds the worktree id when two owners
share a display name, and the skill guide notes that only workspace
rows carry `owner` and `advertisedUrl`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(cli): refuse orca ports for folder workspaces without --all; document the command

Co-authored-by: Manuel Meurer <manuel@meurer.io>

* fix(cli): recognise native folder path selectors in orca ports; note SSH scan limits

Co-authored-by: Manuel Meurer <manuel@meurer.io>

* fix(cli): refuse a folder path shared by several folder workspaces in orca ports

Co-authored-by: Manuel Meurer <manuel@meurer.io>

* fix(cli): refuse a path a worktree and a folder workspace share in orca ports

Co-authored-by: Manuel Meurer <manuel@meurer.io>

---------

Co-authored-by: Manuel Meurer <manuel@meurer.io>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 20:05:47 -07:00
OrcaWinandOrcaWin e12e8bafef test: strengthen hook lifecycle and transcript boundaries (#33015)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-23 19:56:27 -07:00
Neil 82534d0183 refactor: colocate remote runtime passthrough socket (#33009) 2026-10-23 19:46:31 -07:00
OrcaWinandOrcaWin 1fd5020739 test: exercise real platform, provider, and spool boundaries (#33010)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-23 19:40:55 -07:00
Kelvin Amoaba 117e186147 fix(browser): remote tunnel matches Chromium for IPv6 bypass rules and <-loopback> (#32665) 2026-10-24 02:35:00 +00:00
Kelvin Amoaba 33bd67e057 fix(notifications): tapping a phone push opens the agent that sent it (#31773) 2026-10-24 02:34:14 +00:00
de37a4cbe8 fix(workspace-board): close the board when a right-sidebar tab is clicked (#30063)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Kelvin Amoaba <97001695+AmoabaKelvin@users.noreply.github.com>
Co-authored-by: Neil <neil@stably.ai>
2026-10-24 02:32:00 +00:00
OrcaWinandOrcaWin 3b380ddfcf fix: reset Muse hook state and strengthen regression tests (#33008)
* fix: reset Muse hook state and strengthen boundary tests

* test: require live permission, replay, and recency controls

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-23 19:25:11 -07:00
Neil 4975e2451e fix(git): preserve binary output through termination barriers (#32633) 2026-10-23 19:21:53 -07:00
NeilandArnobrizwan 1c0acb65fe fix(explorer): preserve opened folder links across refreshes (#32587)
With automatic symlink following disabled, re-reading a parent lists its links as files and discards the directory flag set when a user opened one. Keep that flag while a successful same-owner child listing is cached, in both direct reads and expanded-directory refreshes.

Mark inferred folder rows separately so a failed child read can clear them without changing host-confirmed directories. Preserve normal deleted-link and missing-target recovery behavior. Add parent/full refresh, failed read, ownership and Windows drive-root regression coverage.

Refs #29069.

Co-authored-by: Arnobrizwan <147672066+Arnobrizwan@users.noreply.github.com>
2026-10-23 19:21:48 -07:00
b6d1f63e01 fix(editor): resume media previews from their last playback position (#32480)
* fix(editor): resume media previews from their last playback position

Switching tabs unmounts the editor surface, so the media element was
recreated and every video or audio preview restarted from zero. Record
the playback time on teardown and seek back once metadata loads.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(editor): resume media previews from their last playback position

Switching tabs unmounts the editor surface, so the media element was
recreated and every video or audio preview restarted from zero. Record
the playback time on teardown and seek back once metadata loads.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(editor): verify media remount playback and cleanup

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
2026-10-23 19:21:44 -07:00
NeilandCubatica c1d980c111 fix(runtime): retire predecessor exit evidence on replacement (#32341)
* fix(runtime): retire predecessor exit evidence on incarnation change

When a PTY id is re-registered with a different, known incarnation, the handle was already invalidated but lastExitCode/lastExitCause (pty and leaves) still carried the predecessor exit, so a live replacement reported the old stop. Clear them at that same proven-replacement point. Same or unknown incarnation keeps the evidence (fail closed).

* fix(runtime): retire exit evidence for replacement incarnations

Clear predecessor exit evidence on the PTY and its indexed leaves when
registration or provider spawn proves a different known incarnation owns it.
Cover both paths so an earlier spawn cannot hide the transition from later
registration. Keep same and uncertain incarnation evidence conservative.

Adapted from Cubatica's original fix and regression cases in #24513; extend
the coverage to spawn-first replacement on the current runtime modules.

Co-authored-by: Cubatica <4414418+Cubatica@users.noreply.github.com>

---------

Co-authored-by: Cubatica <4414418+Cubatica@users.noreply.github.com>
2026-10-23 19:21:40 -07:00
Ibrahem MahyobandNeil 8a63b4344a fix(terminal): focus the neighboring pane when a split pane closes (#32238)
* fix(terminal): focus the neighboring pane when a split pane closes

* test(terminal): cover focus after closing a split pane in the app

* fix(terminal): focus the neighboring pane when a split pane closes

* test(terminal): cover focus after closing a split pane in the app

* test(terminal): keep pane-close parity exclusions evidence based

---------

Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
2026-10-23 19:21:36 -07:00
d11208c2f1 fix(terminal): shape Bengali and other Indic clusters in the WebGL renderer (#32210)
* fix(terminal): shape Bengali and other Indic clusters in the WebGL renderer

xterm gives each Brahmic spacing vowel sign its own cell (matching
wcwidth/string-width, which TUIs align on), and the WebGL renderer draws
every cell in isolation. Pre-base vowels and conjuncts were never
composed, so Bengali rendered as detached marks on dotted circles.

Join each multi-cell Indic cluster through the existing lazy character
joiner so the browser shapes it as one run. Cell widths are unchanged,
so TUI layout and borders keep aligning.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(terminal): drop redundant Indic predicate test, make fresh-array test meaningful

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* fix(terminal): stop Indic joiner over-joining Tamil/chillu and registering for non-joining text

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* fix(terminal): keep a trailing Indic cluster prefix between lazy-joiner writes

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-authored-by: Neil <neil@stably.ai>
2026-10-23 19:21:32 -07:00
ba9eb7fcae fix(editor): let Cmd/Ctrl +/- zoom an open image (#32184)
* fix(editor): let Cmd/Ctrl +/- zoom an open image

The zoom shortcut on an image tab changed the hidden editor font zoom,
because only PdfViewer took the app zoom request. ImageViewer in the
command-owning pane now takes it too: it zooms the popup while that is
open and the inline image otherwise. Intrinsic-layout images in a
combined diff keep editor font zoom. The request module gets a
viewer-neutral name now that it serves PDFs and images.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(editor): zoom a combined-diff image's popup from the shortcut

Intrinsic-layout images skipped the app zoom request even while their
popup was open, so Cmd/Ctrl +/- left the popup unchanged in a combined
diff. Skip them only while the popup is closed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(editor): let Cmd/Ctrl +/- zoom an open image

The zoom shortcut on an image tab changed the hidden editor font zoom,
because only PdfViewer took the app zoom request. ImageViewer in the
command-owning pane now takes it too: it zooms the popup while that is
open and the inline image otherwise. Intrinsic-layout images in a
combined diff keep editor font zoom. The request module gets a
viewer-neutral name now that it serves PDFs and images.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(editor): zoom a combined-diff image's popup from the shortcut

Intrinsic-layout images skipped the app zoom request even while their
popup was open, so Cmd/Ctrl +/- left the popup unchanged in a combined
diff. Skip them only while the popup is closed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(editor): give image popups exclusive app zoom

* fix(editor): scope viewer zoom to the focused workspace surface

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Neil <neil@stably.ai>
2026-10-23 19:21:28 -07:00
Chris BornandNeil 24431a201b fix(mobile): start dictation from agent chat tabs (#32105)
* fix(mobile): start dictation from agent chat tabs

The mobile mic only started dictation when a terminal was active. An
agent chat tab has no terminal handle, so a tap there returned without
starting, opening setup, or showing an error.

A chat transcript goes to the chat composer, not a terminal, so chat
tabs now only need a connected desktop. Terminal tabs keep the
terminal input gate.

Fixes #26003

* test(mobile): verify dictation delivery and reconnect safety

* test(mobile): satisfy dictation React Doctor checks

---------

Co-authored-by: Neil <neil@stably.ai>
2026-10-23 19:21:23 -07:00
Steve FranciaandNeil 004ad7525d fix(floating-terminal): hide orchestration setup prompt when CLI registration is unsupported (#32085)
Co-authored-by: Neil <neil@stably.ai>
2026-10-23 19:21:19 -07:00
Dave ChoiandNeil 60c58ea7e2 fix(runtime): collapse mirrored Markdown aliases with shared backing identity (#31984)
* fix(runtime): collapse mirrored editor tab variants that differ only by environment

A snapshot that already lists one file under several per-environment host tab ids
(editor:<worktree>:<env>:<path> or the bare path) fanned out into one mirrored tab
per variant. Keep one minted variant per (worktree, mode, path) when building
mirrored editor tabs; tabs with opaque ids (deliberate splits) are untouched.
Protects receivers from older peers that still republish mirrored files and from
snapshots that are already polluted.

* fix(runtime): match encoded editor tab ids and keep the navigation target when collapsing variants

Host tab ids come from buildOwnedEditorFileId, which URL-encodes the worktree,
environment and path, so the raw-path predicate never matched a real variant.
Each candidate is now checked by rebuilding it with buildOwnedEditorFileId, and
the tab a pending focus/follow intent targets wins its bucket ahead of an active
one. Tests build their ids the same way.

* fix(runtime): preserve editor identity while collapsing aliases

* test(runtime): keep headless mirror regression across project boundaries

---------

Co-authored-by: Neil <neil@stably.ai>
2026-10-23 19:21:14 -07:00
zhangxaochenandNeil c91a40c43d Keep mobile agent icons visible on retained routes (#31948)
* fix(mobile): preserve agent icons across stacked routes and tab updates

Closes #31939

* fix(renderer): address review feedback on mobile agent icon stability

* fix(renderer): fall back to customTitle when saved leafTitle is blank

* Keep mobile agent icons painted across retained routes

Use instance-specific OMP gradients and preserve bundled icon fallback during web image reload failures. Add a real mobile-web route-retention pixel regression.

Adapted from zhangxaochen’s scoped rendering fixes in #31948; unproven session equality and title changes are excluded.

Co-authored-by: zhangxaochen <zhangxaochen@163.com>

---------

Co-authored-by: Neil <neil@stably.ai>
2026-10-23 19:21:08 -07:00
Samuel VilaandNeil 46a0844c99 fix(markdown-preview): preserve requested line across Preview teardown (#31676)
* fix(markdown-preview): keep the #L line when a link opens a file in another worktree

The Preview scheduled the line reveal on frames it cancels when it unmounts, and opening a file in another worktree unmounts it. Schedule the reveal through the store-level helper Rich mode already uses.

* test(markdown-preview): assert the cross-worktree reveal carries the opened file's id

The editor applies a pending reveal only when its fileId matches the open
record, so register an owner-qualified record from openFile and check the
reveal targets it rather than the path fallback.

* fix(markdown-preview): preserve line reveals across preview teardown

Adopt Samuel Vila’s app-owned editor reveal scheduling and strengthen teardown and superseding-navigation coverage.

Co-authored-by: Samuel Vila <svila.smr@gmail.com>

---------

Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
2026-10-23 19:21:03 -07:00
fancivezandNeil 2fb3de3405 fix(git): close subprocess stdin for SSH-signed commits (#30961)
Co-authored-by: Neil <neil@stably.ai>
2026-10-23 19:20:58 -07:00
eb9b225695 fix(history): retain session row measurements while hidden (#30751)
* fix(ai-vault): prevent virtualized session rows from collapsing to 0px and overlapping

* fix: preserve measured vault row sizes while hidden

* test: cover vault estimates receiving real measurements

* fix(history): retain session row measurements while hidden

Use the existing hidden-row measurer so native zero-size notifications cannot corrupt cached session geometry or move the restored list. Adapt the original regression to cover measured expanded and collapsed offsets across hidden notifications and subsequent visible sizes.

Co-authored-by: Xulin Wu <wuxulin@bytedance.com>

---------

Co-authored-by: Xulin Wu <wuxulin@bytedance.com>
Co-authored-by: Neil <neil@stably.ai>
2026-10-23 19:20:53 -07:00
アンソヌandNeil 3368edd111 fix(github): keep submitted comments cleared (#29533)
* fix: clear GitHub comment composer after submission

* docs: document comment composer and submission harness

* fix(github): keep submitted comments cleared

Suppress Tiptap content updates caused only by editability changes, and
exercise actual comment submission, failures, retries and external values.

Co-authored-by: s34485 <an_sunwoo@cyberagent.co.jp>

* Type the comment composer test store selector

Derive the named test owner settings from AppState while retaining the same runtime fixture. Fix the anti-slop no-object-parameters CI failure.

---------

Co-authored-by: Neil <neil@stably.ai>
2026-10-23 19:20:49 -07:00
Kyle PandNeil 9ff0b53ec6 fix(markdown): keep readable documents after traversal errors (#27774)
* Keep readable Markdown documents after traversal errors

* Document partial Markdown listing behavior

---------

Co-authored-by: Neil <neil@stably.ai>
2026-10-23 19:20:42 -07:00
221eb1ce8b fix(computer-use): trust the dev app's bundle id in the helper's peer check (#26500)
pnpm dev launches the app as com.stablyai.orca.dev, but the macOS helper's
isTrustedOrcaApplication only trusted com.stablyai.orca, com.github.Electron
and ids starting with com.stablyai.orca.dev. (with the trailing dot). Every
computer-use request from a dev build failed with "computer-use agent peer is
not authorized".

Move the bundle id rule into TrustedOrcaApplication in
OrcaComputerUseMacOSCore so it is tested, and trust com.stablyai.orca.dev
exactly as well. Look-alikes such as com.stablyai.orca.devtools stay
untrusted.

Fixes #26498

Co-authored-by: petersindex <287557875+petersindex@users.noreply.github.com>
Co-authored-by: Neil <neil@stably.ai>
2026-10-23 19:20:38 -07:00
Suat KocarandNeil ad4c25cda2 fix(linear): list issue comments oldest first (#25622)
Linear returns an issue's comments newest first and getIssueComments
passed that order through, so the activity feed showed the newest comment
on top while comments posted from Orca were appended at the bottom and
jumped to the top on reload. Sort by createdAt, oldest first, like the
GitLab notes path does.

Fixes #25573

Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
2026-10-23 19:20:34 -07:00
Wooseong KimandNeil fea8bb7f5b fix(cli): preserve runtime recovery steps (#24800)
Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
2026-10-23 19:20:30 -07:00
actureandNeil 339397829e fix(i18n): correct Chinese technical mistranslations (#24174)
Co-authored-by: Neil <neil@stably.ai>
2026-10-23 19:20:26 -07:00
hwantageandNeil f00a2800b2 fix(ui): align multiline filter toggle row text and icons to top-left (#16054)
* fix(ui): align multiline filter toggle row text and icons to top-left

- Add `text-left` and `items-start` to FilterToggleRow button and label wrapper.

- Add `mt-0.5` top margin to icon and switch indicator for optical alignment with multiline text.

- Resolves center-aligned wrapped text and vertically misaligned toggles under long localized labels (Korean, Japanese, Spanish, etc.).

* fix(ui): align multiline filter toggle labels with their controls

---------

Co-authored-by: Neil <neil@stably.ai>
2026-10-23 19:20:21 -07:00
JeongUk ParkandNeil 95e18f519a fix(sidebar): resume workspace cycling after closing the last tab (#10555)
* fix(sidebar): keep Cmd+Shift+Arrow cycling in place after closing a workspace

Closing a workspace's last tab clears activeWorktreeId (landing fallback), so
cycling had no anchor and re-entered the sidebar from its far end — every
subsequent Cmd+Shift+Down/Up restarted there instead of continuing from where
the user was.

Resolve the anchor from the nav-history cursor when nothing is selected. A
selection that is present but uncyclable still passes through untouched, so a
row inside a collapsed group keeps entering from the matching end (#10513).
History is read via getState() at press time; subscribing would re-render the
virtualized sidebar on every visit.

* fix(sidebar): include folder workspaces in Cmd+Shift+Arrow cycling

getCyclableWorktreeIds dropped every non-item row, so a folder workspace sitting
in the sidebar could not be reached with the arrow chord even though Cmd/Ctrl+1-9
already numbers it. Cycling also activated through activateAndRevealWorktree,
which has no route for a `folder:` key.

Build the rotation with getRenderedWorktreesInSidebarOrder — the helper Cmd+1-9
already uses, so both shortcuts cover the same rows in the same visual order —
and activate through activateAndRevealWorkspace, which dispatches on the key.
Rows still come from what the sidebar rendered, so collapsed groups stay out.

* fix(sidebar): keep the cycle anchor host-qualified after a close clears the selection

Closing a workspace's last tab nulls both the selection and its host, and
nav history stores no host, so the history anchor cycling falls back to
resolved to the first rendered row with that id. With the same worktree
id on two hosts, closing the twin on the second host made the shortcut
cycle from the first host's row.

The keyboard hook now remembers the last selected (workspace, host) pair
and hands that host to the anchor when the anchor is that workspace. Any
other history anchor stays host-less as before. Nav history is persisted,
so its entry format is left alone.

---------

Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
2026-10-23 19:20:17 -07:00
e4f5e1ddf1 fix(gitea): treat HTTP 404 as no-PR in the hosted-review branch lookup (#10411)
* fix(gitea): treat HTTP 404 as no-PR in throwing hosted-review lookup

hostedReview:forBranch always uses the throwing Gitea lookup variant, and
requestJsonAtBase threw on any non-OK status. A 404 (PR deleted, repo absent
on the forge, or Gitea masking a private repo) rejected the IPC handler and
spammed the main-process log on every refresh for every unaffiliated branch.

404 means the resource is absent — an accepted "no PR", not a transport or
auth failure. Map it to null even with throwOnFailure; 401/403/5xx/network
errors still throw so creation eligibility keeps reporting 'unavailable'.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(gitea): scope the 404 no-PR exemption to the linked-PR fetch

A blanket status !== 404 exemption in the shared request helper also
covered the repo-level /pulls scan, where a 404 means a wrong API base
URL, a repo absent from the forge, or a token without scope. Collapsing
that to "no PR" would make a misconfigured Gitea host look permanently
PR-less instead of surfacing reviewLookupOutcome: 'unavailable'.

Gate the exemption on an explicit allowNotFound request option, set only
on the single-resource /pulls/{n} fetch.

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

* test(gitea): pin the 200-with-empty-list no-PR shape alongside the 404 case

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

* fix(gitea): preserve deleted linked pull request absence handling

Refresh the original Donach PR and its prior maintainer correction on current main, keeping the HTTP404 exemption limited to the linked pull resource.

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
Co-authored-by: Orca <help@stably.ai>
2026-10-23 19:20:13 -07:00
39fc4f7688 fix(editor): render authored Markdown breaks as rich-editor line breaks (#8995)
* fix(editor): render <br> in markdown table cells as line breaks in rich mode

The rich Markdown editor encoded every inline HTML tag — including `<br>` and
`<br/>` — into an inert raw-HTML atom that displayed the literal tag text. In a
table cell, where a blank-line break is impossible, this meant `<br>` showed up
as raw "<br>" instead of breaking the line.

Render `<br>`/`<br/>` raw inline nodes as real break elements while keeping
`renderMarkdown` emitting the verbatim tag, so the value round-trips exactly on
save. A native hardBreak is intentionally not used: it serializes to a space
inside table cells and would silently drop the tag. The raw-source node's
parse/render DOM logic is extracted into a dedicated module to keep it cohesive
and within the file's line budget.

Closes #8838

* fix(editor): validate preserved break source

Use the editor model's supported DOM parse-rule type instead of a private duplicate. Exercise the rendered-DOM round trip and reject forged preservation attributes before they can become hidden Markdown source.

* test(editor): assert forged break marker exists

* test(editor): preserve authored rich markdown break spelling

---------

Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
Co-authored-by: Neil <neil@stably.ai>
2026-10-23 19:20:09 -07:00