mirror of
https://github.com/stablyai/orca.git
synced 2026-11-15 02:17:31 +00:00
cf5f427d43722017ae981e6f0fc659b1514c3eae
120
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
cf5f427d43 |
Keep worktree branches unless CLI deletion is requested (#35948)
* Keep CLI-removed worktree branches unless requested * Bind branch policy checks to removal connections * Remove redundant local default removal test row * Negotiate worktree branch policy on the mutation connection * Fix removal integration compiler boundaries and type imports * Use checked annotations in paired removal fixtures * Reset CLI integration exit status across test runtimes |
||
|
|
a856bc8af2 |
fix(cli): preserve runtime access across Linux PID namespaces (#35769)
* fix(cli): preserve runtime access across Linux PID namespaces * fix(cli): give retry advice after runtime replacement * fix(cli): preserve private offline admission policy * test(cli): align admission and launcher contracts * test(cli): prune resolved wall-clock allowance * test(cli): narrow dropped runtime errors without casts * test(cli): use explicit extension for hook service import * test(cli): replay recorded Linux sandbox denial * test: verify packaged Linux desktop CLI ownership * test: handle denied FUSE mounts in desktop fixture * test: use privileged PID namespace in desktop fixture |
||
|
|
0ed5f4bb21 |
fix(orcad): materialize the app's agent-browser driver into the local slot (#34150)
* fix(orcad): materialize the app's agent-browser driver into the local slot Replaces #34111's ORCA_AGENT_BROWSER_DRIVER hand-off, Windows profile copy and claimed snapshot store. Packaging now references the app's own resources/agent-browser-<target> from the orcad template manifest (packagedBrowser, like packagedRipgrep), so the materialized slot carries it and every local orcad launch finds it beside orcad-server.js. macOS signs the driver before hashing; Windows reseals it after SignPath. The resolver now also tries the musl driver name on Linux. Refs #33846 * build(orcad): let a checkout keep template drivers with ORCAD_OMIT_AGENT_BROWSER=0 |
||
|
|
7a9abe48f3 | refactor: group remote runtime client modules (#34133) | ||
|
|
77130982fa |
test: remove 47 low-value cases and 13 suites (#34136)
* test: remove 37 low-value cases and 3 suites * test: delete ten more renderer wiring and copy suites * test: drop deleted suite from its contract entry point --------- Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> |
||
|
|
067c614764 |
fix(orcad): use the app's bundled agent-browser driver for orca serve (#34111)
* fix(orcad): find the app's bundled agent-browser driver for orca serve Desktop orcad templates omit the driver for size, so orcad materialized from an installed app reported driver_missing with ORCA_BROWSER_EXECUTABLE set. orca serve now hands orcad the app's resources copy via ORCA_AGENT_BROWSER_DRIVER; the slot's own driver still wins for standalone artifacts. Fixes #33846 * test(serve): expect the app's agent-browser driver handed to orcad * fix(serve): run a profile copy of the driver on Windows so app updates stay replaceable * fix(serve): never lend the installed Windows driver when staging fails * fix(serve): keep another launch's in-progress driver copy * refactor(serve): share claimed snapshot publication for the Windows driver copy |
||
|
|
95384a63bd |
fix(terminal): frame CLI prompts for every recognized agent, not just Claude and Codex (#33571)
`orca terminal send --text … --enter` now uses the bracketed paste + Enter agent-prompt path for every recognized agent (not just Claude/Codex), so Pi's #/@ completion and Grok's y/n trust question no longer swallow or answer typed prompts. Agents whose process names collide with ordinary tools (orca, acli, cn, vibe) still need title/hook evidence. A send into a permission/trust menu is refused with a hint to send --text and --enter separately. Refs #20695 Refs #18399 |
||
|
|
3ff1f00d08 |
test: verify retry identities and complete boundary outputs (#33491)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> |
||
|
|
42f59f3460 |
Allow saving quick command lists beyond forty (#32972)
* Allow saving quick command lists beyond forty Preserve compatibility by limiting older clients to readable commands * Cover quick command persistence across client capabilities * Reject edits to hidden quick commands from stale clients |
||
|
|
bc9ba76989 |
test: verify runtime delivery and remove real-time socket waits (#32988)
* test: exercise runtime request routing with deterministic socket deadlines * test: isolate local status and await redaction stream completion --------- Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> |
||
|
|
0008e0cdf5 |
fix(orcad): add --reclaim-stale-lock for a lock no start can judge after a reboot on a shared home (#32915)
* fix(orcad): add --reclaim-stale-lock for a lock no start may judge On a Linux host with no machine id whose ~/.orca is on a network or FUSE home, a lock left by the host's own previous boot names a PID that is evidence only on the machine that issued it, so every start after a reboot refused it, and the only way out was deleting orcad.lock by hand. Starts still refuse it: losing contact is not proof of exit, and a home other machines may share must never be taken over automatically. Now: - `orca serve --reclaim-stale-lock` (and `orcad --reclaim-stale-lock`) checks that the lock was written on this host, in an earlier boot, at least 2 minutes ago, and that nothing answers at its holder's endpoints. It prints the pid, host, boot and age and warns about other machines sharing the home, and clears the lock only with --yes, through the normal race-safe reclaim of that exact record. - Lock records now name their host, as evidence for that command. - The refusal has its own code, orcad_instance_lock_holder_unverifiable, names the command, and exits 3, so `orca serve --supervise` waits on it instead of giving up, and the desktop refuses on it like a held lock. - orcad advertises orcad.stale-lock-reclaim.v1 so a client can offer the command on hosts that have it. Fixes #32177 Co-authored-by: Jesse Sanford <108698+jessesanford@users.noreply.github.com> * fix(orcad): let --reclaim-stale-lock clear a reclaimer cut short by the reboot, and probe IPv6 A reclaimer from the same earlier boot on this host that the reboot cut short left a recovery owner the command could never judge, so --yes always failed. The confirmation now covers such an owner too, and only one from this host's earlier boot. An IPv6 WebSocket endpoint is probed by its bare address. Co-authored-by: Jesse Sanford <108698+jessesanford@users.noreply.github.com> * fix(cli): ship the stale-lock reclaim module with packaged builds, and refuse an unreachable lock The CLI loads main modules from out/main after the main build replaces it, so the reclaim module becomes a stable plain-Node entry and is unpacked from the asar. A lock that cannot be stat'ed for any reason other than absence (a permission error, say) is now a refusal, not "nothing to reclaim". Co-authored-by: Jesse Sanford <108698+jessesanford@users.noreply.github.com> * fix(orcad): make --reclaim-stale-lock name the real data root and report a status - The refusal prints a runnable command for the data root it refused: an orcad names its own runtime and entry plus `--data-root <dir>`, since managed hosts have neither `orca` nor `orcad` on PATH and their data root is not the default. The hint is Linux-only. - Both entry points take `--data-root` and `--json` and report one of none, eligible, refused or reclaimed, each with its own exit code (4, 2, 1, 0), so a missing lock never reads as a recovery. - The evidence says nothing answers on this machine, and that the host name and boot checks cannot rule out another machine with the same name; --yes stays the gate. - wss endpoints default to port 443, a same-boot branch that could never run is gone, and the supervisor's standby line points at the command. Co-authored-by: Jesse Sanford <108698+jessesanford@users.noreply.github.com> * fix(orcad): point the eligible message at the same command plus --yes Co-authored-by: Jesse Sanford <108698+jessesanford@users.noreply.github.com> * fix(orcad): refuse an empty --data-root, and name the right program in reclaim hints - `--data-root` with no usable value (a trailing flag, an empty or blank string) prints usage and exits 1 on both entry points, never falling back to the default profile. - A refusal raised while the reclaim command itself runs names `orca serve --reclaim-stale-lock`, not the binary the CLI runs under; an orcad still names its own runtime and entry. - The supervisor's standby hint includes this profile's --data-root. - The unreadable-lock test is skipped when running as root. Co-authored-by: Jesse Sanford <108698+jessesanford@users.noreply.github.com> * test(cli): reuse the existing client stub for the --data-root refusal test Co-authored-by: Jesse Sanford <108698+jessesanford@users.noreply.github.com> --------- Co-authored-by: Jesse Sanford <108698+jessesanford@users.noreply.github.com> |
||
|
|
88b47bc7d8 |
[comment audit] Shorten CLI runtime and command-spec comments (#32283)
* chore: shorten process-host comments * chore: audit CLI parsing and host-routing comments * chore: remove stale host-selector restriction comment * chore: shorten remaining CLI root comments * Shorten CLI handler comments * Shorten CLI runtime and command specification comments --------- Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> |
||
|
|
ab1563e6f3 |
fix(cli): negotiate agent launches with paired runtimes (#32029)
* fix(cli): negotiate agent launches with paired runtimes * refactor(cli): share paired client capability list across request paths Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
fece03b5ac |
fix(orcad): voice dictation on headless orca serve hosts (#31217)
* fix(orcad): voice dictation on headless orca serve hosts
Mobile dictation against an orca serve host answered speech_unavailable
since serve moved to orcad: orcad never installed the speech service
factories, and the model manager and download imported Electron.
The model download now takes a host request opener (Chromium net on the
desktop, Node https on orcad, with redirects mapped to the same event),
the STT worker ships in the orcad slot, and orca serve hands orcad the
installed app's on-device recognizer. A host without a recognizer (an
SSH or WSL managed slot) lists only cloud models and sends a phone that
picked an on-device model to the setup sheet. Mobile names the fix when
an older server still has no speech at all.
The parity test now requires both roots to install speech.
Reported-by: delorean2020 (@delorean2020)
* fix(orcad): retry Node download drops, stage the recognizer on Windows, review fixes
- Node https errors (ECONNRESET, ETIMEDOUT, ...) are retryable, so an
interrupted orcad model download resumes (Codex P2).
- On Windows orca serve hands orcad a per-version copy of the recognizer
in the profile, so a loaded addon never locks the app's files against
an update (Opus).
- Drop a recognizer candidate path that could never match; one SAFETY
helper per test file instead of repeated casts; comments.
* fix(orcad): cloud speech is ready only with a key this host can decrypt; prune superseded recognizer copies
Codex round 2: a desktop-sealed OpenAI key exists on disk but orcad's
secret store cannot open it, so the catalog reported cloud models ready
and dictation failed at the end. orcad's model manager now proves the
key decrypts. The desktop keeps its existence check (no keychain prompt).
* fix(orcad): never prune staged recognizers before the profile lock; copy linked packages as real files
Codex round 3. ref-oss check: versioned copies are cleaned only by the
process that owns them, so the pre-lock prune is dropped rather than
patched.
* fix(orcad): remove a partial recognizer copy when staging fails (Codex round 4)
* fix(speech): never hand out another store's ciphertext as the OpenAI key (Codex round 5)
* refactor(orcad): no recognizer copy on Windows; Windows orcad keeps cloud dictation only
Design check after review round 5: every remaining finding came from the
Windows copy that dodges addon file locks. Dropping it shrinks the
surface to a path lookup; Linux and macOS serve keep on-device models.
* fix(speech): name the fix when an old server refuses a mic tap; always set the recognizer env; retry ENOTFOUND (final Opus review)
* fix(managed-server): probe install completeness relative to the slot dir
CI: shipping stt-worker.js added one more absolute slot path to the
install-complete probe, pushing it further past the exec-size budget.
Testing each artifact relative to the slot (inside a subshell cd) cuts
the probe from ~12.2 KB to ~3.6 KB, so it drops off the over-budget
list instead of raising a ceiling.
* fix(mobile): pin host.platform's read to a platform type, independent of which zod copy resolves
With mobile's own dependencies installed (which CI does for any PR that
touches mobile/), the e2e program resolves two zod copies, and the
salvaged platform arm from src/shared inferred as {}. The schema now
narrows through an explicit platform guard.
* fix(mobile): keep the host.platform read line intact for its RPC mutant; narrow in a second step
Mobile verify: the recorded-RPC mutant anchors on the read line, which
the previous guard rewrote, so the mutant could not apply.
* refactor(cli): app-executable resolution in its own module; drop the platform guard now zod is aligned
launch.ts went one line past max-lines with main; resolveAppRoot,
resolveForegroundOrcaExecutable, getExecutableAppArgs and
stripElectronRunAsNode move to orca-app-executable.ts (re-exported, so
callers are unchanged). With mobile and root on one zod (#31260), the
host.platform schema no longer needs the extra narrowing step.
* refactor(cli): import app-executable helpers from their module; no re-exports from launch.ts
|
||
|
|
d7ef7741d1 |
fix(serve): restart the macOS serve supervisor from the updated app (#31200)
* refactor(serve): move the update handoff file reads and writes into their own module Move-only: the supervisor module is at its line budget, and the handoff file IO is a separate concern from the child supervision loop. Claude-Session: serve-supervisor-reexec * fix(serve): restart the macOS serve supervisor from the updated app After a remote update on macOS, the node-mode supervisor kept running the old Orca executable, which the installer moved aside and deleted. Under a LaunchAgent the system charges every descendant's privacy permissions (Accessibility, Screen Recording, files) to that supervisor, so once its path no longer resolved, screen capture and computer use failed for the whole served tree while the permission check still reported them granted. Once the new bundle is verified on disk and no stop is pending, the supervisor now re-runs itself in place (same pid) from the installed executable instead of spawning the replacement itself. The handoff stays install-requested, so startup's existing resume path spawns, verifies and completes it. A --supervise supervisor that runs from its copy outside the app restarts through the app's own CLI entry, so it copies the new release aside again rather than mixing old scripts with the new executable. If the re-run fails it logs and starts the replacement as before. Only applies when serving our own executable on macOS; Linux, Windows, orcad and overridden executables are unchanged. Claude-Session: serve-supervisor-reexec * fix(serve): restart a supervisor whose executable the install replaced Three review fixes on top of the update restart: - Decide the restart by file identity instead of which path observed the install. The supervisor records the device and inode of its executable at start; after any install wait (including resuming an interrupted update at startup) it restarts only if the installed executable is a different file. A supervisor that started on the old app before the swap now restarts too, and the process after a restart never restarts again. - A relocated --supervise supervisor releases its marker on the copy it is leaving, so the next relocation can delete that copy. Before, every update kept one more full copy of the app's scripts for the life of the service. - A failed process.execve aborts the process natively rather than throwing, so the fallback never ran. The restart now checks first that the executable can run and the script entry exists, and otherwise logs and starts the replacement directly as before. Claude-Session: serve-supervisor-reexec * fix(serve): restart a parked serve service whose app a desktop install replaced When the desktop app's Update & Restart parks an `orca serve --supervise` service for another profile, it writes no update handoff for that profile. After the install the service's outer loop just starts its next server, so neither update restart point ran and the supervisor kept the deleted executable. Every server start now runs the same identity check right after the park/stop gate and before spawning, so a supervisor whose executable was replaced restarts in place first. The check is a no-op when the executable is unchanged, so first starts and the process after a restart do not loop. The test drives the real park and commit functions, so the CLI typecheck project now includes that updater module (it imports only shared code). Claude-Session: serve-supervisor-reexec * refactor(serve): check for a replaced app in the one place every server start passes The restart check ran at three call sites and was threaded through the update loop as a hook, and each review round found another start path it missed. Every path (first start, resumed update, after an install, after a park or crash) spawns its server through the function spawnElectronServe builds, after its stop and park gates, so the check now lives in that spawn and nothing else carries it. The restart moves to its own module, the hook and the handoff-file move it forced are gone, and the tests drive serveOrcaApp instead of the hook. Claude-Session: serve-supervisor-reexec |
||
|
|
4e483ae2e6 |
perf(serve): clone the supervisor's code copy in one step on macOS (#31303)
* perf(serve): copy the supervisor's code aside with one cp clone on macOS orca serve --supervise copies the app's unpacked code and node_modules out of the app before re-running itself from the copy. Node's fs.cp clones those ~6,000 files one at a time, which took 8-15 s in the foreground and around 35 s under the service manager's throttled I/O. One /bin/cp -cR per tree does the same copy in about 1.5 s. The clone-then-plain-copy fallback the terminal host copy already used now lives in src/shared/macos-tree-clone.ts, and both copies call it. Linux keeps fs.cp with reflink cloning, and Windows does not support --supervise. Claude-Session: serve-snapshot-clone * fix(serve): let the macOS tree copy run without a time limit and throw on failure The shared cp helper ran under runProcess's 30 s default time limit, which the old fs.cp copy never had. At background I/O priority a large tree can outlast it, so the supervisor would quietly give up and run from the app itself. The helper now runs cp with no limit; the terminal helper copy stays bounded by its deadline signal. The helper moves to src/main beside the asar-safe rm and uses it directly, so it no longer takes an rm option. It throws instead of returning a boolean, so copyBundle is gone. The relocation reads process.platform instead of a platform dependency, and its copy test now runs on every platform but Windows. Claude-Session: serve-snapshot-clone * fix(build): keep the macOS tree copy module where the packaged CLI loads it The serve supervisor's relocation code in the CLI now imports the macOS tree copy module from the main-process sources. The CLI is compiled file by file, but the later main-process build replaces out/main and keeps only the files it is told to build as entries, so the packaged CLI failed at load with "Cannot find module '../../main/macos-tree-clone'" on every platform, and the CLI runtime verifier rejected the build. Register the module as a CLI main entry, like the other main modules the CLI imports, and unpack it from the app archive. Its asar-safe removal dependency is bundled into a shared main chunk, which is already unpacked. The build output contract test now covers both. Claude-Session: serve-snapshot-clone * fix(build): unpack the asar-safe rm the CLI's tree clone imports The compiled CLI layout keeps out/main/macos-tree-clone.js requiring ./asar-transparent-fs directly, and that file was still packed, so the packaged CLI failed to load (caught by packaged-cli-assets in CI). The bundled build only passed because the main build inlines it into an unpacked chunk. Claude-Session: serve-snapshot-clone |
||
|
|
63246eba3a |
fix(pairing): adding a server you already saved updates it instead of saving it twice (#31080)
* fix(pairing): pairing a saved server again refreshes its entry instead of saving it twice * fix(pairing): only Settings refreshes a saved server in place; other add paths refuse the duplicate * fix(pairing): a same-key refresh keeps the server's workspaces and tabs; raw add refuses; match any endpoint key * fix(pairing): refuse a code for a hidden saved server; one choke point publishes verified status only for the pairing still saved * refactor(pairing): verified-pairing publish in its own module; an unreadable store skips the stamp instead of failing a saved pairing * fix(pairing): match a host key however its Base64 is spelled * fix(pairing): a refresh keeps the saved key spelling so the host fingerprint stays put |
||
|
|
536a32ae82 |
fix(updater): a live orcad's browser helper pauses for macOS desktop installs (#31059)
* fix(updater): a live orcad's browser helper pauses for macOS desktop installs orcad starts its browser helper from the installed Orca.app, so ShipIt waits for it, and the installer refused it as an unregistered blocker with no supported way out. orcad now registers the helper in the existing serve install park: it stops the helper for the install and starts it again from the new bundle once the hold ends. The refusal card for a helper an older orcad still runs says what to do instead of "stop the server". Reported-by: @routesmith * fix(orcad): retry a failed helper restart, hold through a mid-swap bundle, catalog the guidance * fix(orcad): a new park cancels a slow helper restart; reset backoff per install |
||
|
|
148944c020 |
refactor: de-barrel remote-runtime-client, runtime-terminal-stream and runtime-terminal-inspection (D2, D3) (#30684)
* refactor: de-barrel remote-runtime-client and runtime-terminal-stream (D2) importers name the defining modules; the two vi.mock factories that named a re-export move to the defining module (pty-id spy) or drop the dead key (RemoteRuntimeClientError) * refactor(renderer): de-barrel runtime-terminal-inspection (D3) importers name runtime-terminal-input-recording, runtime-terminal-verified-input and shared/remote-runtime-pty-id; 22 vi.mock factories move the re-exported keys to those modules (pty-id keeps its other exports via importOriginal) * test: drop the duplicated verified-input mock (Opus P3) |
||
|
|
d3d6162f5c | test(orcad): orca serve's argv makes orcad serve the web client and 404 other paths (#30120) (#30222) | ||
|
|
d71e3e9d45 |
fix(serve): orca serve on orcad serves the browser client and answers plain HTTP (#30120)
* fix(serve): orca serve on orcad serves the browser client and answers plain HTTP orcad, the default local orca serve host since the compatibility launcher, was never handed the app's web client directory, so its HTTP server had no request listener: every GET hung and no Web client URL was printed (#30100). The CLI now passes the app's unpacked out/web to orcad via --web-client-root, a server without a web client answers plain HTTP with 404 instead of hanging, and packaging fails when the unpacked web client or a file it loads is missing. * test(serve): pin orca serve handing orcad the app's web client root |
||
|
|
20fca451c5 |
test(serve): wait for supervisor signal readiness (#29527)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> |
||
|
|
0fac887dbc |
feat(terminal): show a paired server's terminal in this desktop on terminal switch (#28023)
* fix(terminal): log a failed pane reveal instead of rethrowing it The bridge already replies false when the reveal throws, so the rethrow only surfaced as an uncaught renderer error after the caller had its answer. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(terminal): refuse terminal.reveal from paired clients It moves this host's window. terminal.focus lets a paired client do that only by asking for host navigation, and the callers this method serves (the CLI and companion apps on the same machine) never arrive as paired clients. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * feat(terminal): show a paired server's terminal in this desktop on terminal switch A paired `orca serve` host has no window, so `orca terminal switch --environment <name>` resolved the handle there, answered `navigated: false`, and the desktop stayed put (#24688). Add `terminal.reveal`, a desktop runtime method that names a pane by worktree, tab and leaf, as the owning host knows them, and runs the Agent Dashboard's click-to-focus sequence before raising the window. When a paired server cannot navigate, `terminal switch` asks the desktop on this machine to reveal the terminal instead. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test(e2e): terminal switch on a paired server moves the desktop on this machine Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test: update terminal manifest count after main and assert the revealed pane renders Main added a terminal method since this branch was cut, so the characterization count is 39 with terminal.reveal. The e2e now proves the target pane is visible in the DOM, not only selected in the store. Co-authored-by: Velm14 <Velm14@users.noreply.github.com> --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Co-authored-by: Neil <neil@stably.ai> Co-authored-by: Velm14 <Velm14@users.noreply.github.com> |
||
|
|
1faa210d4c |
fix(serve): reap orphaned processes when orca serve is PID 1 (#29390)
* fix(serve): reap orphaned processes when orca serve is PID 1 * fix(serve): keep AppImage, eval launches and exit status intact under the PID 1 reaper |
||
|
|
ac0d8c4fd0 |
test: consolidate CLI regressions and simplify IPC lifecycle proof (#29298)
* test: batch lint fixtures and remove repeated runtime scan * test(cli): consolidate owner coverage and remove false decoding proof * test(renderer): retain IPC lifecycle behavior without copied inventories * test(git): keep checkout progress proof within file limit * fix(lint): classify the shared mobile chat harness as test support --------- Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> |
||
|
|
a54236751e |
fix(serve): start Linux headless serve on X11 so an inherited Wayland session can't abort it (#29260)
* fix(serve): pin headless Linux serve to X11 so an inherited Wayland session can't abort startup Fixes #25400 * fix(serve): put --ozone-platform=x11 on the launch argv for Linux headless serve Chromium caches its Ozone platform in PreSandboxStartup, before main JS runs, so app.commandLine changes cannot move it off Wayland. Pin X11 on the CLI and orcad serve spawns, and re-exec in-process serve (same pid) when Electron already resolved Wayland. |
||
|
|
7c23f79622 |
fix(quick-commands): save agent prompts whole, bounded by what a chat message carries (#24488)
* fix(quick-commands): save agent prompts whole, bounded by what a chat message carries Agent quick-command prompts were silently cut to 6,000 characters on save: the editor accepted a long paste, settings normalization trimmed it, and reopening showed the shortened copy. The 6,000 was a Windows launch speed threshold, not a limit on prompts. - Storage no longer trims. A save is refused, never trimmed, when a prompt's chat text block is over 256 KiB of JSON (measured on its real escaped size, the measure the chat send schema applies) or the list is too large for one paired reply. Host and desktop store both check before writing. - The editor shows the size against the limit and blocks Save past it, and stays open with the text when the host refuses the save. - terminal.quick-commands.long-prompts.v1: hosts withhold prompts over 6,000 characters from clients that do not advertise it (older clients reject a whole list holding one); editors keep an older host's 6,000 cap. - The phone editor no longer drops pasted text past 6,000 via maxLength. Launch delivery of long prompts is left to the launch-prompt series (#24257, #23962, #24080); this branch's earlier paste-after-ready delivery duplicated it and is removed. * fix(quick-commands): drop a stale duplicate doc comment * fix(quick-commands): advertise long prompts to this build's own host too * fix(quick-commands): keep submitted editor immutable until save settles * test(quick-commands): keep storage and save lifecycle coverage focused * fix(quick-commands): keep portalled choices within pending form ownership * Preserve retained credentials when settings validation refuses a save --------- Co-authored-by: OrcaWin <alpha-eng@stably.ai> |
||
|
|
0504670961 |
Fix CLI delivery and fresh launch environments for managed SSH agents (#26464)
* fix: refresh host environments before structured agent command resolution * fix: ship the owning server CLI for managed remote agents * test: include the shipped CLI in remote install fixtures * Bind packaged agent CLI calls to their execution host * Keep owning-server CLI binding scoped to managed agents * Serve the last shell environment to chat starts and refresh it in the background Only the first chat start waits for the login shell; later starts get the last snapshot and refresh it in the background once it is 10 s old. On Windows hosts without the registry addon (managed server slots), the saved PATH is reread with reg.exe. The Windows server CLI launcher stager now checks the target architecture, and the CLI help says ambient selectors are ignored inside a managed server. * Keep the last good shell environment when a refresh fails, and load the Windows profile once A login-shell capture now says whether the shell produced the env or Orca fell back to its own, and only a real capture replaces the snapshot. Windows loads the PowerShell profile once per process and refreshes only the saved PATH. The reg.exe fallback queries the whole key so a missing user Path no longer drops the saved PATH. The first capture starts when the resolvers are created. * Keep the last good login-shell env for other readers while a forced refresh runs * Mark the Claude launch resolver as a reviewed router-aware module It only types the chat command resolver it injects; main's launch-routing guard (#26801) matched the name. * Keep a settled login-shell env for readers during a refresh and share the first run - Any settled cached entry, a fallback included, now serves other readers while a forced capture runs; only a real capture replaces it. - Native chat's first capture shares a run a startup reader already began, so the login shell runs once per process at startup on macOS/Linux. - Type the injected Claude resolver without naming resolveClaudeCommand. - Cover the runtime's Claude command options, the owning-host --host check, and the PTY flag drop. * Drop an inherited owning-host flag from daemon terminal panes The persistent terminal daemon merges its own environment into every pane, so an inherited ORCA_CLI_OWNING_HOST reached desktop panes even though main strips it from the spawn env. Drop it with the other per-launch CLI variables. * Route agents to their own server through ordinary launch defaults; start the first chat after an install The native-chat agent environment now sets the owning server's ORCA_USER_DATA_PATH and empty ORCA_ENVIRONMENT / ORCA_PAIRING_CODE / ORCA_REMOTE_PAIRING, which the CLI already reads as unset; empty rather than deleted, because the spawn merges the launch overlay over the server's own env. The ORCA_CLI_OWNING_HOST flag, the CLI's skip of ambient selectors, the launchers' export of it, the terminal clean-up, the help-text exception and the WSL launcher guard are gone, so a person's terminal follows their own settings as on main. A launch that finds no program in the shell snapshot waits for one shared refresh (at most one shell timeout) and resolves again; create support rereads the shell once before refusing. On Windows a failed PowerShell profile load is retried on the next refresh instead of being kept until restart. * One shared shell reread when an agent's program is missing The pre-create check for OpenCode, OMP and Pi forced its own login-shell capture on every refusal: not shared with launches, one per concurrent check, triggered by an installed-but-too-old version too, and an older capture finishing last could overwrite a newer cached environment. On Windows, every launch miss also reran a failed PowerShell profile load, bypassing the 10-second limit. - One saved shell environment per host: chat launches, their misses, the pre-create check and AI commit messages read it and share one refresh. - The pre-create check resolves the agent's program the way a launch does, so only a missing program waits for the refresh; registrations declare how to resolve their launch and check its version. - A forced login-shell capture joins one already running for the same shell, so captures cannot land out of order. - On Windows, a miss within 10 s of a failed PowerShell load rereads only the saved PATH. * One owner for the cached shell environment Review found two more races in the reread-on-miss machinery, both from the host wrapper keeping its own snapshot beside the login-shell cache: - A launch that found no program could join a reread that started before the agent was installed, and spend its one retry on the old PATH. - On Windows, saved-PATH rereads renewed the snapshot's age, so a failed PowerShell profile was never retried while launches kept missing. The login-shell cache is now the only owner of the captured environment, the run in progress and which result wins (the newest run started; a fallback never replaces a captured env). It takes three options instead of `force`: - startedAfter: a lookup that found nothing waits for a run that started no earlier than the lookup did, joining one if it has, else starting one. - refreshAfterMs: serve the last result and rerun in the background once the last attempt is older. - keepCaptured: a captured env is kept for good; only a fallback is rerun (Windows' PowerShell profile). The host wrapper keeps only policy: the 10 s interval, on Windows the saved PATH laid over the profile env on every read (a miss forces a fresh registry read), and the settings overlays. Its snapshot, pending promise, staleness and Windows "loaded" bookkeeping are gone. A lookup's start time travels with the miss (resolveAgainAfterRefreshOnMissingProgram passes it). * Read an injected shell environment once per runtime, as main does The previous commit read an injected environment on every acquisition, which broke main's 'caches shell exports but re-reads configured overrides for a resume' contract. It is read once when the resolvers are made again. * Model lists reread the environment on a missing program, as launches do Live QA: the first new chat after installing Claude showed "Claude wasn't found on the computer running this chat". The chat launch itself reread the login shell and started the new binary, but Claude's model-list check ran first, with the environment read before the install, and never retried: catalog probes were deliberately built without the miss refresh. The retry now lives in each agent's one shared lookup of its binary and environment, which a launch and a model listing both call: resolveCodexStructuredInvocation, resolveAcpLaunchInvocation, a new shared resolvePiRpcInvocation, and resolveClaudeChildEnvSources (which already had it). Each catalog probe is given the runtime's refreshAfterMissingProgram, and the catalog context no longer strips it, so a listing and a launch reread the same single-owner environment the same way. * Pass optional launch lookups through directly instead of conditional spreads * Read the host shell env through resolveLoginShellEnvironment; drop the extra wait cap CI on the merge with main failed main's new codex-workspace-relative-path test (#26684), which mocks the login-shell module with only resolveLoginShellEnvironment: the wrapper called captureLoginShellEnvironment and imported the spawn-timeout constant. The wrapper only needs the env, so it reads through resolveLoginShellEnvironment again, as on main. The miss wait no longer races its own 5 s timer: the shell run it waits for is already killed at the spawn timeout, so the second cap was a guard over the same bound. The constant goes back to private, as on main. * Test that a hung login shell holds a launch miss for at most one shell timeout The bound lives in the one owner: the shell run is killed at the spawn timeout (5 s) and settles to a fallback, which never replaces a captured snapshot. A mutation raising that timeout fails this test. * A program found through a relative PATH entry is not missing Main's #26684 runs Codex through a workspace-relative PATH entry (`bin`, `node_modules/.bin`). The lookup searches such an entry against Orca's own directory and returns the bare name; the spawn then finds the program in the chat's directory. Counting that bare name as missing made every such launch wait for a login-shell reread. The retry now fires only when the lookup truly found nothing: a bare name back from a PATH whose entries are all absolute. Each agent's one lookup tells it which PATH it searched. * Count a relative PATH entry as found only when it holds the program; Windows miss waits for a saved PATH read started after it looked A bare name with a relative PATH entry is checked from the launch's working directory, so an agent in neither entry still gets its one reread. On Windows the miss no longer joins a registry read that began before the lookup. |
||
|
|
b724e3a24d |
fix(linux): a supervised AppImage serve follows a replaced AppImage (#28244)
* fix(linux): a supervised AppImage serve restarts from a replaced AppImage Swapping the AppImage file never reached a supervised serve: it runs the copy it unpacked, and even a full service restart went back through the stable launcher to that same copy. The extraction cache now records which AppImage each namespace came from. Before each start the supervisor compares that file's identity with the build it runs; once a settled new file is there and runs on this host, it execs the new AppImage with its own arguments (same pid), whose serve unpacks the new build and re-points the stable launcher. Fixes BUG-4 from the real-host Linux test run. * fix(linux): try a replaced AppImage once, and let it past an old build's give-up - A give-up recorded against the old build no longer stops the supervisor before it checks for a replaced AppImage. - The supervisor records the AppImage identity it is about to exec in the profile. If that build dies before its own crash brake, later starts of the old build keep running it instead of exec'ing the same broken file until systemd's start limit disables the service. A supervisor running that build, a further replacement, or --force clears the record. * test(cli): compare the namespace listing order-independently --------- Co-authored-by: m4air <m4air@Mac.localdomain> |
||
|
|
97fac546df |
fix(linux): a supervised serve run from the AppImage stays in place instead of re-exec'ing (#28270)
Since a direct `Orca.AppImage serve --supervise` now runs the CLI supervisor (#28227), the supervisor's relocation re-exec ran from the AppImage mount. An exec from node mode closes every inherited descriptor, including the AppImage runtime's keepalive pipe, so the runtime unmounted the AppImage during the exec and the new image failed with 'libffmpeg.so: cannot open shared object file' (exit 127) on every start. Relocation exists so an update cannot swap the app under a running supervisor; an AppImage's mount cannot change during the run, so it stays in place. Co-authored-by: m4air <m4air@Mac.localdomain> |
||
|
|
5929350944 |
fix(linux): direct AppImage serve honours the chosen profile and --supervise (#28227)
* fix(linux): run a direct AppImage serve through the CLI so it keeps its profile and supervisor A direct `Orca.AppImage serve` stayed in the Electron process and skipped the CLI's serve front end, so it ignored ORCA_USER_DATA_PATH and silently dropped --supervise. It now execs into the bundled CLI (same pid, so service managers and signals still reach it), translating a leading --user-data-dir into ORCA_USER_DATA_PATH. Also lists terminal-daemon and reference as CLI commands so a direct binary launch of them reaches the CLI. Fixes BUG-1, BUG-3 and BUG-10 from the real-host Linux test run. * fix(linux): drop desktop switches anywhere in a direct serve launch * fix(linux): keep a direct serve's --no-sandbox for the Electron server the CLI starts --------- Co-authored-by: m4air <m4air@Mac.localdomain> |
||
|
|
bc519c57fa |
fix(serve): say what headless serve actually needs, and stop repeating the supervisor's restart line (#28230)
- A headless serve with no X display told the user to run `orca-ide serve`, which they just ran. Serve now gets its own message: it needs an X display, Wayland alone is not enough, and which X virtual framebuffer package to install (now including Arch's xorg-server-xvfb). - The supervisor's restart line read 'Orca serve Orca serve exited via SIGKILL.; restarting'. - The supervisor and 'serve service status' gave different advice after giving up; both now give the same, correct advice. Fixes BUG-2 and BUG-9 from the real-host Linux test run. Co-authored-by: m4air <m4air@Mac.localdomain> |
||
|
|
cb92b6ce57 |
feat(serve): run the --supervise supervisor from a copy outside the app (#28007)
* feat(serve): run the --supervise supervisor from a copy outside the app * fix(serve): record supervisor copy users beside the copies so a cleanup never races a fresh copy * fix(serve): remove a partial supervisor copy when copying fails * test(serve): skip the unreadable-copy case where mode 000 does not block reads --------- Co-authored-by: m4air <m4air@Mac.localdomain> |
||
|
|
dedea9c76c |
fix(updater): clear a cancelled macOS install request before staging the next update (#28010)
* fix(updater): clear a cancelled macOS install request before staging the next update * fix(updater): match only Orca's ShipIt and recheck owners right before clearing --------- Co-authored-by: m4air <m4air@Mac.localdomain> |
||
|
|
65a1611926 |
fix(serve): explain what to run when an SSH host's orca cannot start or install a server (#28003)
* fix(serve): explain what to run when an SSH host's orca cannot start or install a server * fix(serve): keep an app launcher on PATH usable from a server terminal; share the app-CLI check --------- Co-authored-by: m4air <m4air@Mac.localdomain> |
||
|
|
93e0b80be7 |
refactor(cli): serve children spawn through process-host (#27760)
Co-authored-by: m4air <m4air@Mac.localdomain> |
||
|
|
411d5c24cb |
feat(serve): orca serve service install|status|uninstall (#27744)
* feat(serve): orca serve service install|status|uninstall for launchd and systemd user units * fix(serve): redact pairing secrets in the supervisor instead of an inherited env, validate the port and launcher at install * fix(serve): keep the redaction module off a direct child_process import * fix(serve): redact the JSON readiness offer and survive a closed log pipe --------- Co-authored-by: m4air <m4air@Mac.localdomain> |
||
|
|
6c3ce444bc |
feat(updater): auto-park registered serve supervisors during a desktop install (#27722)
* feat(updater): auto-park registered serve supervisors during a desktop install * test(serve): drop a type assertion in the bundle park test * fix(updater): stop any server started during an install hold and re-list blockers right before commit --------- Co-authored-by: m4air <m4air@Mac.localdomain> |
||
|
|
78144e91cc |
feat(serve): supervised mode for orca serve --supervise (#27716)
* feat(serve): supervised mode for orca serve --supervise Restart the server with jittered backoff instead of exiting when it dies, with a crash-loop brake whose exhaustion is durable per profile and build, an install hold while the executable is missing or ShipIt replaces the bundle, and standby while another Orca serves the profile. Only an explicit --supervise turns it on; Windows refuses it for now. * fix(serve): let a supervised orcad serve wait for a live peer instead of counting it * fix(serve): gate update replacements on the install hold, honor stop during the bundle wait, keep a give-up while the executable is missing * fix(serve): hand a removed executable back to the service manager and key the brake on the app archive --------- Co-authored-by: m4air <m4air@Mac.localdomain> |
||
|
|
34e2e5f469 |
refactor(remote): clients connect through a paired-connection handle, not a pairing offer (#27533)
Desktop, CLI, web and runtime transports now take a PairedRuntimeConnection (endpoint, pinned host key, credential) instead of a PairingOffer. Offers stay on the pairing input path (codes, QR, links, environment add) and are converted with pairedRuntimeConnectionFromOffer. The credential is token-only today; request envelopes still carry the same deviceToken, so the wire is unchanged. This lets device-key and ticket credentials replace the token later without touching every connect call site again. Co-authored-by: m4air <m4air@Mac.localdomain> |
||
|
|
8bc4cef855 |
fix(serve): decide a duplicate or doomed Electron serve before exec (#27472)
On macOS a second Electron main aborts inside _RegisterApplication before any Orca JavaScript can apply the one-Orca-per-profile rule, so supervisors and agents retrying 'orca serve' loop on SIGABRT (#14541). The CLI now checks the profile before it execs Electron serve. A runtime that answers, or whose socket accepts, is live; one whose socket neither accepts nor refuses, or that refuses this CLI's credentials, is unverifiable; both refuse with exit code 3, the single-instance code systemd units already key off. Only a definitive refusal (ENOENT or ECONNREFUSED) lets it start. A fresh profile keeps the old synchronous launch. A macOS startup abort is remembered per environment, so a later separate 'orca serve' from the same launchd session refuses for 30 minutes with exit 78 instead of aborting again; --force overrides, and a serve that reaches readiness clears it. Adapted from #14768; the Launch Services diagnostic wording and the exit code 3 contract come from #14542. Co-authored-by: m4air <m4air@Mac.localdomain> Co-authored-by: Jinwoo-H <jinwoo0825@gmail.com> Co-authored-by: thiagomsoares <5190162+thiagomsoares@users.noreply.github.com> |
||
|
|
5d7f48e00b |
fix(serve): keep the isolated profile off the login keychain (#27463)
* fix(serve): keep the isolated profile off the login keychain The E2E/pairing runtime hands the app a fresh HOME, so there is no login keychain for OSCrypt to read. On the serve path the secret-protection report runs inline before readiness and probes safeStorage on the main thread, where Chromium's keychain lookup creates the item and waits on an authorization reply that never arrives, so `orca serve` never advertises (#25453). The sidecar already launches with --use-mock-keychain for exactly this profile; share that decision and give the foreground serve process the same switches. * test(serve): read spawn args without type assertions Co-authored-by: Paolo <paovalletta@hotmail.it> * fix(serve): only an isolated userData profile drops the keychain The sidecar always runs on a fresh profile, so either E2E variable is enough there. The foreground serve process opens the selected profile's sealed secrets, and a HOME override alone still selects the user's real profile, so it now needs ORCA_E2E_USER_DATA_DIR. Co-authored-by: Paolo <paovalletta@hotmail.it> --------- Co-authored-by: Paolo <paovalletta@hotmail.it> Co-authored-by: m4air <m4air@Mac.localdomain> |
||
|
|
061cb226e2 |
refactor(orcad): shared serve-option validation; no daemon barrel (#27443)
orcad now checks its flags with the same validator Electron serve and the CLI use, so contradictory pairing flags fail with the same message instead of one silently winning. The CLI launcher drops its duplicate recipe check (the CLI handler already ran the shared validator). Removes the daemon-init and cli/runtime/index re-export barrels and the parsePtySessionId / OrcadInstanceLockError re-exports; callers and mocks import the defining modules. Co-authored-by: m4air <m4air@Mac.localdomain> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
616e9c3e3a |
refactor(cli): extract the serve supervisor's after-exit decision into a pure policy (#27446)
* refactor(cli): extract the serve supervisor's after-exit decision into a pure policy * refactor(cli): type the exit signal without a child_process import --------- Co-authored-by: m4air <m4air@Mac.localdomain> |
||
|
|
6ad1a670a3 |
refactor(rpc): delete the SSH-bridge caller scope (#27440)
Nothing mints an SSH-bridge credential since the relay client was removed (#27214), so the 'ssh-bridge' caller kind, its host binders, result filter, the CLI bridge-mode policy and the ORCA_SSH_BRIDGE_CREDENTIAL env read could never run. The allowRemoteCliControl target field stays for older peers. Co-authored-by: m4air <m4air@Mac.localdomain> |
||
|
|
fff0bb0d06 |
feat(runtime): the host refuses a request meant for another runtime (#27224)
A CLI launched from an Orca terminal now sends, with each request, the runtime it verified before sending (expectedRuntimeSource: the launching profile's installation id plus the verified runtime id). The host checks it before parsing params or running any handler and refuses a mismatch with runtime_source_mismatch, so a runtime that took over the endpoint between the CLI's check and its send cannot apply the request. Optional field: an old CLI sends none and an old host ignores it (the CLI's pre-send probe stays the fence there). Hosts advertise runtime-source-fence.v1. Co-authored-by: m4air <m4air@Mac.localdomain> |
||
|
|
7aa91f730a |
fix(cli): check the runtime source before sending (#27206)
* fix(cli): check the runtime source before sending A terminal Orca launches now carries which Orca launched it (ORCA_RUNTIME_SOURCE_ID = the profile's installation id, ORCA_RUNTIME_SOURCE_INCARNATION = the launching runtime id). Before a request is sent, the CLI compares that with the runtime the profile's runtime record names. Same runtime: sent as before. Different runtime: a read-only status.get asks which profile it serves; the same profile (a restart or update) is accepted, anything else is refused before the request reaches it. The vars cross into WSL through WSLENV. The SSH bridge stamps the bridging runtime itself rather than trusting the remote env. The existing orchestration compatibility stamp is untouched. * fix(orcad): drop an inherited runtime source at startup * fix(cli): an explicit switch to another profile is not fenced The stamp also carries the launching runtime's userData path. A CLI that dials a different profile than the one that launched its terminal, and not the platform default an unpinned terminal falls back to, was pointed there on purpose (orca-dev from a packaged Orca's terminal), so the fence does not apply. * refactor(cli): reuse the shared userData path comparison --------- Co-authored-by: m4air <m4air@Mac.localdomain> |
||
|
|
1912c90141 |
Extract subprocess handling into @orca/process-host (#26963)
Extract subprocess execution and process contracts into @orca/process-host with explicit public entry points, independent checks, and one Node 18 compatible CommonJS runtime. Migrate application consumers and integrate package builds, dependency selection, import enforcement, and runtime resource staging. Preserve daemon startup ownership and profile isolation, and qualify development rebuild recovery and concurrent output publication. Remove unnecessary aliases, test forwarding exports, and a duplicate output scan. Validation: full typechecks, changed-code quality, package and artifact checks, native Windows builds/types plus 306 runtime tests, and independent native reviews. Matched full E2E controls on original main and unchanged reviewed head both pass all 17 applicable tests (nine skips). Earlier intermittent E2E failures are documented in the PR; no assertions were weakened. The final combined-tree audit preserves both branches with no import-boundary violations. |
||
|
|
5f62f498f3 |
refactor(orcad-daemon-startup-cli): clean up Phase 3-3.8 code (#26892)
* refactor(orcad-daemon-startup-cli): clean up Phase 3-3.8 code - One helper (getAllDaemonAdapters / listEveryDaemonGeneration) for enumerating daemon generations, replacing five hand-rolled copies. - Shared serve pairing/endpoint readiness for Electron serve and orcad. - One option list builds both --serve-* and orcad serve argv. - holdDaemonAdoptionLease takes an options object; reuse childProcessHasExited. - Drop orcad-entry's dead re-exports and repeated version fallback. - Reuse the shared withTimeout in daemon retirement. - Share the install-wait step in the serve update supervisor. - Trim stale or misplaced comments. Behavior unchanged; production code is net -132 lines. * test(serve): keep real serveOptionArgs in launch mock; pin Electron serve argv --------- Co-authored-by: m4air <m4air@Mac.localdomain> Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local> |
||
|
|
e3639ef8d6 |
Phase 3.5: fix the top SSH/remote issues at the root cause (#26483)
* fix(remote-runtime): stop arm64 Linux servers dropping clients every heartbeat (empty WebSocket write EFAULT) (#26200) * fix(remote-runtime): never write an empty WebSocket payload on its own (arm64 Electron EFAULT) Patch ws@8.22.0 Sender.sendFrame so a frame with an empty payload is written as its header alone. Under Electron on arm64 kernels with 39-bit virtual addresses, socket.write() of a zero-length buffer fails with EFAULT and kills the connection, so every empty heartbeat ping, auto-pong and close dropped the remote runtime with 1006. Wire bytes are unchanged. * test(remote-runtime): run the ws empty-payload guard on real Node Bun resolves 'ws' to its built-in client, so under the Bun test project the guard exercised Bun's implementation instead of the patched package. --------- Co-authored-by: m4air <m4air@Mac.localdomain> * fix(orcad): ship the session scanner child so agent session history lists on orcad hosts (#26309) orcad built every forked child except session-scanner-service-entry.js, so aiVault.listSessions always failed on an orcad host. The resolver also skipped the adjacent entry whenever isPackaged was true, so shipping the file alone would not have been enough. - build every ORCAD_CHILD_ENTRY_POINTS entry from one loop and add the scanner - add it to ORCAD_ARTIFACTS so a deploy missing it fails integrity checks - share one forked-child path resolver (asar layout vs flat app root) between the watcher and the scanner - smoke-fork the built scanner during build-orcad under both runtimes Fixes #24105 Co-authored-by: m4air <m4air@Mac.localdomain> * fix(remote-runtime): one owner for a remote pane's binding and recovery (#26308) A remote pane no longer latches in connecting on terminal_not_found, holds keystrokes typed during same-terminal recovery instead of dropping them, reports transport-owned retries so the pane is not remounted mid-retry, and no longer infers absence from an inventory that predates the leaf. Co-authored-by: m4air <m4air@Mac.localdomain> * ci(e2e): move apt off the Azure mirror on runner images that use a mirror list (#26335) Ubuntu 24.04 runner images resolve apt sources through /etc/apt/apt-mirrors.txt, so rewriting only the source lists left package downloads on azure.archive.ubuntu.com, which intermittently fails ('Ign:' on every package). Rewrite the mirror list too, and retry the install once with --fix-missing. Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local> * fix(terminal): remote snapshot replay keeps scrollback it cannot replace (#26319) Desktop subscribers of a remote terminal received screen-only snapshots, and every pushed snapshot replay cleared the pane's scrollback with ESC[3J. A reconnect, a remount, or a recovery after a dropped frame therefore wiped history the host never resent. - Host: desktop subscribe snapshots use the same scrollback-row ladder as mobile, and SnapshotStart now carries an optional scrollbackRows field. - Client: a pushed snapshot clears scrollback only when the host says it carries history; screen-only images (scrollbackRows 0, or older hosts that omit the field) repaint the screen and keep the pane's history. Co-authored-by: m4air <m4air@Mac.localdomain> Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(daemon): clean launch env, validate client frames, fit the socket path (#26306) * fix(daemon): clean launch env, validate client frames, fit the socket path - Drop Chromium's DBUS_SESSION_BUS_ADDRESS=disabled: marker from the daemon, its systemd-run scope launch and every PTY env (#25580, #21119). - Use the durable user-manager scope only when that manager outlives the caller: lingering on, or the caller already runs inside it (#24201). - Shape-check hello and request frames, and catch request rejections, so a malformed frame closes one connection instead of the daemon (#17841). - Move the daemon socket to a short per-uid private dir when the data root would overflow sun_path (#17840). * fix(daemon): validate a relocated socket dir before any connect, not just before bind --------- Co-authored-by: m4air <m4air@Mac.localdomain> Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local> * fix(ssh): keep crediting a PTY after its recovery delivery is canceled (#26341) A canceled recovery delivery released its source spans while the renderer and model could still settle them; the stale projection then threw on every later ACK, so the next delivery was never credited and the relay paused the PTY for good. Settling or transferring a span of a closed delivery is now a no-op. Co-authored-by: m4air <m4air@Mac.localdomain> * fix(rpc): deny-by-default caller permissions and a scoped SSH bridge credential (#26337) * fix(rpc): deny-by-default caller permissions and a scoped SSH bridge credential Every RPC method now declares a required permission, and RpcDispatcher checks it against the caller's scope on every transport (owner, mobile, paired runtime, SSH bridge). The SSH relay bridge no longer spawns the client CLI with owner authority: each invocation gets a short-lived credential bound to its SSH target, revoked when the child exits. Without the new per-host opt-in, that credential reaches only its own host's terminals; with it, workspace methods but never desktop control, accounts, settings writes, skill install/share, pairing or host admin. Paired runtime clients need a pairing-time grant (orca serve --grant-desktop-control) for computer.* actions. Fixes #23835 Fixes #25259 Refs #18269 * fix(rpc): let a bridged SSH worker report to its own coordinator; allowlist paired-runtime permissions Without the per-host opt-in, orchestration send/ask/reply/check/inbox now pass when the caller is a terminal on the bridged host, binding pane key, payload Dispatch, run and recipient to that caller's own orchestration (recipient off-host only as the creator of its active Dispatch). Paired runtime clients get an explicit standard permission set; pairing-admin and desktop-control are grant-only. * fix(rpc): resolve a bridged SSH caller's own Run and Dispatch mailboxes before admission Mail routing stores a worker's follow-ups under dispatch:<id> and worker questions under run:<id>, so comparing the original recipient to the caller's terminal handle refused an SSH worker replying to its coordinator, an SSH coordinator answering its own Run's question, and explicit sends to the caller's own run:/dispatch: address. The bridge now resolves the exact mailboxes the caller's live pane and process read (as check does) and admits replies and recipients against those. * refactor(rpc): parse SSH bridge selectors with a typed schema instead of Reflect.get --------- Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local> * fix(projects): remove a project only on the host the user picked (#26340) * fix(projects): remove a project only on the host the user picked removeProject now requires a hostId and matches that host's row exactly. The bare-id fallbacks (unique candidate, focused host) could resolve to another host's live project and delete it. Orphaned SSH purge and group deletion now pass each row's own host. Fixes #13071 * fix(projects): group delete removes only the group's member rows Same-id rows on other hosts outside the group were removed because membership was rebuilt from bare ids after the cascade cleared it. Capture (id, host) member pairs before deletion and remove only those. * fix(projects): never let a stale catalog widen a project removal to every host The renderer chose id-only `repos:remove` when its own catalog showed no same-id row on another host. A stale catalog (another window, a paired client, or a pending refresh) hid that row, and main then deleted every host's registration and saved workspace metadata. - The local catalog route now always calls `repos:removeForHost` with the owner row's host, for local, direct SSH, git and folder projects. - `Store.removeProject` refuses an id that rows on more than one host share, so no id-only caller can delete another host's row. - Setup rollbacks remove the just-created row by its host. --------- Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local> * fix(serve): load SSH targets on a headless serve; a user Quit only closes serve-hosted windows (#26348) * fix(serve): load SSH targets on a headless serve; a user Quit only closes serve-hosted windows - launchServeMode registers the SSH handler layer before RPC binds, so a windowless 'orca serve' lists and connects the profile's SSH targets (#25886, #8489). - Under serve, the app-menu Quit/Exit, tray Quit and renderer-recovery Quit close the desktop windows instead of quitting the process (#15537). Signals, supervisor stops, relaunches and update installs still quit normally; on macOS a signal reaches before-quit the same way Cmd-Q does, so the guard lives on the user commands, not in before-quit. * fix(serve): SSH profiles serve on Electron; native macOS quits and frozen windows no longer trap or stop serve - orca serve: a profile with saved SSH targets stays on the Electron host by default, since orcad has no SSH stack yet (#25886, #8489). ORCA_SERVE_RUNTIME=orcad still forces orcad. - macOS serve: Dock > Quit, app-switcher Cmd-Q and other native terminates only close the desktop windows (#15537). Signals, supervisor stops, relaunches, update installs and logout/shutdown still quit. - The serve signal listeners now reclaim SIGINT/SIGTERM/SIGHUP from Electron's native handler. Any signal that had a Node listener before app ready was left with Electron's handler, so these listeners never ran. - A serve user Quit closing a frozen renderer's window arms the same 10s ack deadline as a desktop quit, and skips minimize-to-tray. * fix(serve): stay on Electron when the profile's SSH targets cannot be read yet A valid older-schema profile is rejected by the read-only probe, and the selector took that as "no SSH", so a default serve before the first migration started orcad with an empty SSH registry. Fail closed instead. * refactor(window): type user-Quit close marks as a named window slice, not object * test(serve): pass the desktop-control grant default in the headless SSH launch fixture * test(serve): run the serve-selection SQLite fixtures in the Node runtime project --------- Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local> * fix(terminal): keep the main-process terminal tail linear under TUI redraw floods (#26316) * fix(terminal): keep the main-process terminal tail linear under TUI redraw floods Full-width in-place row rewrites rebuilt the row string per character (O(width^2)), a long chunk past the line cap spliced the row array once per newline (O(lines x cap)), and multi-frame chunks summed every cursor-up so they fell back to the whole-tail path. Rows now hold mutable cells joined once, capped rows are dropped in batches, and the redraw window uses the net cursor reach. Fixes #11315 * fix(terminal): do not rebuild a redraw row the cursor only revisits Rows stayed cell arrays once touched and re-joined on every newline, so a cursor revisiting one wide row thousands of times cost O(width) per newline. Runs now splice into the row string, a row switches to cells only after 32 edits between snapshots, and cells are joined once and dropped. * fix(terminal): keep one cached row per chunk so unchanged redraw revisits cost O(1) A row now keeps its cells, right-trimmed snapshot and content end across newlines and only invalidates them on a real content change; identical writes leave it clean. Adds a work-accounting matrix over restore, the append path and live onPtyData, plus a per-character row oracle fuzz. * fix(terminal): return redraw rows to flat strings with cached snapshots Cells made every changed snapshot a full-width join, which was far slower than the old flat-string splice. Rows are flat strings again, written one run at a time; each keeps a cached right-trimmed snapshot and content end that only a real change invalidates, so identical rewrites and cursor-only revisits stay O(1). A wall-time test now times every review probe against a vendored copy of the old row model on restore, append and onPtyData. * test(terminal): read runtime leaf and PTY tail through typed members --------- Co-authored-by: m4air <m4air@Mac.localdomain> Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local> * fix(ssh): relay fallback runs Orca's pinned Node by default, never a host npm install (#26303) * fix(ssh): relay fallback runs the pinned Node ladder by default, never a host npm install A host whose connect falls back to the relay route now gets the A/B/C ladder (Orca's uploaded Node and prebuilt addons) unless it opts into Host Node. A refused rung C steps to rung D with an actionable reason instead of compiling native deps on the host. * fix(ssh): mark the opt-in Host Node relay runtime as unsupported on the SSH host card The relay fallback now defaults to Orca's pinned Node ladder; the host-npm path is reachable only by an explicit per-host Runtime or ORCA_SSH_REMOTE_RUNTIME override. When a relay actually launches on it, main publishes hostNodeRuntime on the connection state (optional, admitted field-by-field for paired clients) and the SSH host card shows an unsupported-configuration warning. * fix(ssh): keep the relay runtime ladder honest about what it cannot verify - NixOS's stub loader refusing Orca's generic Linux Node (exit 127, 'Could not start dynamically linked executable') is classified wrong_libc, so the ladder steps on to the host Node instead of failing the connect at rung A. - In plain SSH mode the shell provider answers absent only for shells it saw exit; a PTY id it never minted (one an earlier relay may still run) is unverifiable, never exited. - The rung D message offers Host Node only when the host's own Node refused the prebuilt addons, and calls it an unsupported configuration; a download, unresolved-host or Windows refusal gets no Host Node advice. * fix(ssh): keep the host-Node relay where the ladder can't judge the host A client without Orca's runtime artifacts (a dev, e2e or source build with no orcad template, or an offline client) refused rungs A and C with artifacts_unavailable, and the connect settled on rung D. That reason describes the client, so it now lands on the host-Node relay it had before the ladder, marked unsupported, and persists no host decision. A proved noexec still lands on D. Windows hosts have no rung B or C yet, so any rung A refusal there keeps the host-Node relay too. The rung D message is worded from the host OS: Windows gets no noexec-mount wording and is offered Host Node. * fix(ssh): a host-Node fallback with no Node settles rung D, and mutable refusals expire The host-Node relay the ladder chooses in place of D (a client artifact gap, or a Windows rung A refusal) hard-failed the connect when the host had no Node: the resolver threw a plain Error, which plain SSH mode does not admit. The fallback now proves host Node with a strict probe first; an answered "no Node" settles D with a classified, host-OS worded reason, and an unanswered probe stays a retryable failure. A gap no longer rewrites D to host Node once rung C proved host_node_missing. Mutable pinned-runtime refusals (missing_lib, security_software, and noexec on Windows) are no longer persisted and replay from memory for ten minutes only, so a repaired host gets rung A back. The in-memory refusal cache moves to ssh-relay-pinned-refusal-cache.ts. * fix(ssh): no host does worse than the pre-ladder host-Node default Restructure the relay runtime ladder around one invariant: A, B and C are tried first, and any refusal past them falls back to exactly the pre-ladder default, the host-Node relay marked unsupported. That covers an unidentified libc, a client artifact gap, Windows, mutable refusals and a C N-API floor the npm build would not have hit. Rung D is reached only when that fallback proves the host has no usable Node, or a proved noexec home would defeat it too. Strict host-Node resolution now treats only a command that ran and exited as an answer, so a refused channel (MaxSessions), session limit or timeout stays a retryable failure instead of a 'no Node' verdict. The launch reuses the Node the fallback probe found. Mutable refusals (missing_lib, security_software, Windows noexec) persist again with their proof time and replay for 24 hours, so a host that still blocks Orca's runtime is not re-uploaded to on every connect, while a repaired one gets rung A back. A table-driven test asserts the invariant per host class. * fix(ssh): every answered rung failure reaches the host-Node default, and D only past it Two shortcuts skipped the pre-ladder default. An answered but unclassified install failure (tar ENOSPC, an extract or hash marker, an unverified Windows promote, a self-test that failed outside every refusal class) aborted the connect on every attempt; it now steps down as a non-persisted install_failed. Only unanswered failures (abort, timeout, channel-open, session limit, an unconfirmed termination, SFTP connection loss, ssh exit 255) stay retryable, and nothing steps down once a relay launch was issued. A proved noexec no longer jumps to D: exec denial can be per binary (AppArmor allowing /usr/bin/node), so it skips B and C, which load from the same tree, and reaches the host-Node fallback. D now follows only that fallback answering with a failure, so its outcome is a superset of the default's by construction. The never-worse test is now the cross-product of every A, B, C and host-Node answer on Linux and Windows, plus named review cases. * fix(ssh): system-ssh upload failures and a broken cached runtime step down before launch System-ssh directory upload and system sftp threw plain Errors that dropped the host's exit status, so a remote tar ENOSPC or a Windows sftp permission refusal aborted the connect instead of stepping down. The remote side now throws SystemSshCommandExitError (ssh exit 255, signals and lost-connection diagnostics stay retryable), and a local tar failure is still blamed on the client rather than the remote tar that saw its truncated stream. The warm runtime check only tested that the verified executable existed, so a cached runtime that the host's exec policy or a removed library had since broken was launched and hung on every reconnect. The same command now also runs it and reports a refusal the way promotion does, so it steps down before any launch, with no extra round trip. * fix(ssh): keep the remote answer when local tar only failed writing to it When the host's tar exits early (ENOSPC, a refusal), BSD tar on macOS exits 1 with 'Write error' and no signal, so the upload rethrew the local error and dropped the typed remote exit, aborting the connect. The remote answer now wins whenever local tar finished, was signalled, failed after ssh closed, or reported a write or pipe error; a local tar that failed on its own input first is still the client's fault. * test(ssh): hostile-host cells allow read-only version probes, never installs; probe each Node once The noexec-home cell failed on 'npm --version': under the never-worse ladder a proved noexec reaches the strict host-Node fallback, whose probe asks npm its version before settling D. The cells now judge install and compile calls (npm install/ci, node-gyp, cc, make) and allow '<tool> --version' / '-v'. The probe ran twice because the login shell named the Node the path probe had already rejected. Candidate checks are now memoized per resolution, for the host-Node fallback and rung C alike. --------- Co-authored-by: m4air <m4air@Mac.localdomain> Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local> * chore(i18n): regenerate the runtime-required English catalog after the main merge * fix(serve): serve on the profile the CLI selected, and refuse any other before the instance lock (#26424) Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local> * fix(remote-runtime): open terminal tabs reattach after a managed server update re-pairs the host (#26433) * fix(remote-runtime): a remote pane rebinds after a server update re-pairs its host A pane captured the host's pairing revision once, when it mounted. A forced managed-server update re-pairs the host under the same environment id, so every later request from that pane was refused as stale, and the refusal counted as fatal: the pane stopped retrying and Reconnect replayed the same stale revision. Each request now carries the current revision, a pairing refusal re-reads the catalog before the retry, and the pane treats it like lost contact. * fix(remote-runtime): close a stream left on the old pairing and ignore a 0x0 grid hold A stream that reopened before main re-paired stayed open while sendFrame refused its stale revision, so typed keys vanished. A revision change now closes it as recoverable so the pane rebinds on the new pairing. A just-restarted server also published a 0x0 hold that shrank the visible pane to one cell; holds with no grid are ignored. * fix(remote-runtime): a pane follows only its own machine's re-pair A re-pair under the same environment id to a different host key published its revision before the catalog retired the old tabs, and the pane recovered at the new revision, so its next call was authenticated as the replacement peer. The catalog now classifies each re-pair before publishing revisions: a replaced or removed machine's terminal streams end for good first, and a pane only advances its pairing along a proven same-host rotation (same registration and host key). An unresolved identity holds the pane until it is proven. --------- Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local> * refactor(rpc-permissions): Phase 3.5 elegance pass (#26471) * refactor(rpc-permissions): drop never-read incarnation and invocation id from bridge credentials * refactor(rpc-permissions): define runtime device grants once and reuse bridge scope fixtures * refactor(rpc-permissions): decide terminal host membership in one predicate and share the opt-in hint * refactor(rpc-permissions): parse bridge selectors once per call * refactor(rpc-permissions): admit and bind unopted bridge methods from one table --------- Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local> * refactor(remote-pane-binding): Phase 3.5 elegance pass (#26470) * refactor(remote-pane-binding): keep the pane's pairing revision in one slot * refactor(remote-pane-binding): share one hold decision across the input entry points * refactor(remote-pane-binding): define the persisted-handle adopt once in attachPane * refactor(remote-pane-binding): build the pushed-snapshot meta once * refactor(remote-pane-binding): drop the test-only peerReplacedEnvironmentIds wrapper * test(remote-pane-binding): share the input and subscribe frame readers via stream fixtures --------- Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local> * refactor(relay-ladder): Phase 3.5 elegance pass (#26474) * refactor(relay-ladder): put the host-Node fallback in the ladder before D The Auto ladder now lists legacy as the rung before D, so the step function is one rule plus the next rung. Deletes nextRelayRuntimeStep, the hostOs step context and the mutable hostNodeFallback flag (now a constructor-time laddered field). Windows still ends at legacy: B and C refuse there before any host I/O. * refactor(relay-ladder): one rung-D reason and message over reachable states D is reached only from the host-Node fallback, so its refusal is host_node_missing or install_failed. Merges the reason and message functions, drops three message branches that could not run, and folds noexecRemembered/noexecProved into one noexec field. * test(relay-ladder): one never-worse cross-product with a remembered axis Drops the named cases (each matches a cross-product outcome), the transport case that only checked the model against itself, and the third case the rank check already implies. Adds rung B's noexec answer and per-rung remembered flags for A and B, the only rungs that replay a cached refusal. * refactor(ssh): one answered-miss rethrow for the node resolution probes Four catch blocks repeated the same session-limit, abort and strict checks. They now call rethrowUnlessAnsweredMiss and keep their own fallthrough. Also drops a stale 'Caches nothing' comment. * refactor(ssh): share the host-answered system-ssh exit rule; flatten the upload throw cascade isHostAnsweredSystemSshExit owns the 255/null transport rule for both the upload pipeline and isAnsweredHostFailure. settleUploadPipeline drops a branch that could not run: a failed tar is judged once, then ssh, then pipe. --------- Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local> * refactor(serve-daemon-misc): Phase 3.5 elegance pass (#26478) * refactor(ssh-pty): build obligation transitions behind one closed-delivery guard * refactor(build-orcad): table-drive the shipped worker smokes * refactor(daemon): pass scope probe seams as one deps object and share cgroup line parsing * refactor(repos): route every project removal through removeProjectForHost * refactor(build-orcad): type the worker smoke table for the type-aware gate --------- Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local> Co-authored-by: m4air <m4air@Mac.localdomain> * refactor(terminal-tail): Phase 3.5 elegance pass (#26465) * refactor(terminal-tail): share mulberry32 and compare whole results in redraw equivalence tests * refactor(terminal-tail): keep one splice ceiling at the onPtyData entry point --------- Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local> * fix(terminal): bound the backing a redraw row can pin across chained erases (#26547) Each erase that keeps at least half the row skipped the copy, so a chain of them left nested slices pinning the original wide row. Rows now track an upper bound on the string they slice and copy once the cumulative shrink passes half. Co-authored-by: m4air <m4air@Mac.localdomain> --------- Co-authored-by: m4air <m4air@Mac.localdomain> Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local> Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> |