mirror of
https://github.com/stablyai/orca.git
synced 2026-10-31 17:17:38 +00:00
ea532813b1f8464b4de54c81663152ee07cb8da1
40
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
917ef9c737 | feat(browser): install Chrome extensions from the Web Store into Orca's browser (#30940) | ||
|
|
7324dada30 |
perf(dev): −1.2 GB idle RSS per pnpm dev instance (Rolldown threads + mimalloc purge) (#33836)
* perf(dev): size the dev server's Rolldown runtime for many parallel instances ~93% of idle electron-vite RSS is Rolldown's native mimalloc arenas. The dev wrapper now gives electron-vite MIMALLOC_PURGE_DELAY=0 and 4 Rolldown/Rayon threads; a serve-only renderer plugin strips exactly those keys before Electron spawns, so dev Orca's terminals never inherit them. * fix(dev): add the runtime-env plugin only when the dev wrapper set its keys Keeps createDevServeConfig returning the renderer config unchanged when there is no port and nothing to strip, as electron-vite-output-contract pins. --------- Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> |
||
|
|
7d6603aa17 |
[comment audit] Complete repository-wide comment cleanup (#33249)
* [comment audit] Align legacy failure-read counts with cached display * [comment audit] Condense shared provider lifecycle and browser recovery comments [skip ci] * [comment audit] Condense shared generation, path and preview comments [skip ci] * [comment audit] Condense host, file and Git contract rationale [skip ci] * [comment audit] Tighten Git lifecycle and provider contract comments [skip ci] * [comment audit] Clarify shared host authority and launch isolation [skip ci] * [comment audit] simplify local access mobile and native chat contracts [skip ci] * [comment audit] simplify native chat presentation and state contracts [skip ci] * [comment audit] simplify native runtime and provider contracts [skip ci] * [comment audit] simplify migration and orchestration contracts [skip ci] * [comment audit] simplify plugin trust and process capture rationale [skip ci] * [comment audit] simplify terminal delivery and remote compatibility rationale [skip ci] * [comment audit] clarify remote recovery and launch wire rationale [skip ci] * [comment audit] shorten shared RPC compatibility and runtime evidence comments [skip ci] * [comment audit] simplify secure storage shell startup and sidebar rationale [skip ci] * [comment audit] clarify sidebar, skill and SSH invariants [skip ci] * [comment audit] condense structured chat delivery and timing rationale [skip ci] * [comment audit] condense terminal replay and telemetry invariants [skip ci] * [comment audit] clarify launch permission and terminal delivery rationale [skip ci] * [comment audit] clarify workspace ownership and layout contracts [skip ci] * [comment audit] wait for fitted and painted SSH terminal links * [comment audit] clarify layout persistence and session ownership [skip ci] * [comment audit] clarify host routing and workspace lifecycle contracts [skip ci] * [comment audit] clarify startup persistence and app lifetime comments [skip ci] * [comment audit] clarify recovery and activity boundaries [skip ci] * [comment audit] condense activity and artifact invariants [skip ci] * [comment audit] condense automation authority contracts [skip ci] * [comment audit] simplify automation scheduling and history contracts [skip ci] * [comment audit] clarify browser chrome and annotation lifecycles [skip ci] * [comment audit] clarify browser routing and retained guest ownership [skip ci] * [comment audit] clarify browser recovery and document ownership comments [skip ci] * [comment audit] restore hosted runners required by native IME fixtures * [comment audit] shorten preview, palette and dashboard invariants [skip ci] * [comment audit] clarify terminal preview, dashboard and retention comments [skip ci] * [comment audit] clarify diff-note recovery and editor lifecycle comments [skip ci] * [comment audit] shorten rich-text and combined-diff ownership comments [skip ci] * [comment audit] shorten autosave, draft recovery and model ownership comments [skip ci] * [comment audit] shorten save-queue, notebook and Markdown constraints [skip ci] * [comment audit] shorten Monaco, PDF and rich Markdown safeguards [skip ci] * [comment audit] clarify rich Markdown source and selection safeguards [skip ci] * [comment audit] clarify editor loading and emulator stream lifetimes [skip ci] * [comment audit] trim emulator and feature tour narration [skip ci] * [comment audit] clarify floating workspace and GitHub cache ownership [skip ci] * [comment audit] trim provider controls and Linear filter explanations [skip ci] * [comment audit] clarify pairing and chat resume lifetimes [skip ci] * [comment audit] clarify chat delivery and transcript lifetimes [skip ci] * [comment audit] clarify session options and host-owned status [skip ci] * [comment audit] clarify draft durability and live-status precedence [skip ci] * [comment audit] Trim chat send and model ownership explanations [skip ci] * [comment audit] Shorten transcript ownership and send recovery comments [skip ci] * [comment audit] Simplify native chat composer and transcript hook comments [skip ci] * [comment audit] Shorten native chat scroll and structured session explanations [skip ci] * [comment audit] Trim workspace picker and onboarding commentary [skip ci] * [comment audit] Simplify pet rendering, review cache and quick open comments [skip ci] * [comment audit] Trim sidebar and session history explanations [skip ci] * [comment audit] Condense session resume and review checks comments [skip ci] * [comment audit] Shorten checks state and file explorer refresh comments [skip ci] * [comment audit] Trim plugin bridge and source-control action rationale [skip ci] * [comment audit] Trim source-control listing and review rationale [skip ci] * [comment audit] Shorten sidebar lifecycle and explorer safeguards [skip ci] * [comment audit] Condense Settings account and appearance rationale [skip ci] * [comment audit] Trim pairing and repository Settings explanations [skip ci] * [comment audit] Condense Settings search SSH and terminal safeguards [skip ci] * [comment audit] Shorten Settings voice and provider ownership comments [skip ci] * [comment audit] Condense Settings draft persistence and host-state rationale [skip ci] * [comment audit] Trim Settings hooks and sidebar setup rationale [skip ci] * [comment audit] Condense sidebar deletion and board-filter safeguards [skip ci] * [comment audit] Condense worktree cards, host ownership and Markdown contracts [skip ci] * [comment audit] Condense sidebar drag, folder and filtering contracts [skip ci] * [comment audit] Condense sidebar parity, sleep and host ownership contracts [skip ci] * [comment audit] Prune focus-read baseline after comment cleanup * [comment audit] Clarify sidebar visibility, board drops and agent evidence [skip ci] * [comment audit] Tighten sidebar listing, drag and reveal contracts [skip ci] * [comment audit] Clarify viewport geometry and skill update safeguards [skip ci] * [comment audit] Tighten skill inventory, usage and host status comments [skip ci] * [comment audit] Clarify usage ownership and resource kill safeguards [skip ci] * [comment audit] Tighten tab selection, rename and search contracts [skip ci] * [comment audit] Tighten tab-strip geometry and drag ownership comments [skip ci] * [comment audit] Tighten Tasks recovery, pagination and mutation comments [skip ci] * [comment audit] Tighten provider loading and terminal recovery comments [skip ci] * [comment audit] Tighten completion, cadence and PTY ownership comments [skip ci] * [comment audit] Clarify terminal restore and parked-pane boundaries [skip ci] * [comment audit] Tighten PTY startup and replay explanations [skip ci] * [comment audit] Clarify PTY identity and delivery ownership [skip ci] * [comment audit] Shorten remote reconnect and input replay rationale [skip ci] * [comment audit] Condense terminal parking clipboard and file link comments [skip ci] * [comment audit] Clarify IME ownership and trim trace history [skip ci] * [comment audit] Shorten pane recovery and parked watcher explanations [skip ci] * [comment audit] Condense snapshot replay and visibility recovery rationale [skip ci] * [comment audit] Shorten terminal parking and keyboard ownership rationale [skip ci] * [comment audit] Trim renderer lifecycle and menu rationale [skip ci] * [comment audit] Condense workspace cleanup ownership and settlement notes [skip ci] * [comment audit] Clarify renderer hook evidence and source ownership [skip ci] * [comment audit] Tighten IPC ownership and reconnect explanations [skip ci] * [comment audit] Trim renderer focus, paste and attention comments [skip ci] * [comment audit] Shorten scroll, locale and agent lifecycle comments [skip ci] * [comment audit] Trim agent launch, resume and ownership commentary [skip ci] * [comment audit] Shorten account, browser and editor comments [skip ci] * [comment audit] Clarify host-mirror waits, previews and input ownership [skip ci] * [comment audit] Condense launch recovery and editor tokenization comments [skip ci] * [comment audit] Condense workspace selection and terminal pane rationale [skip ci] * [comment audit] Condense terminal fitting and rendering recovery comments [skip ci] * [comment audit] Condense terminal input and host routing explanations [skip ci] * [comment audit] Condense ownership and sleeping-session recovery rationale [skip ci] * [comment audit] Condense structured launch and terminal link explanations [skip ci] * [comment audit] Condense terminal ownership and workspace file routing rationale [skip ci] * [comment audit] Condense session persistence and workspace activation rationale [skip ci] * [comment audit] Condense workspace routing and browser ownership rationale [skip ci] * [comment audit] Condense mirror recovery and layout authority rationale [skip ci] * [comment audit] Condense runtime transport and file safety rationale [skip ci] * [comment audit] Condense runtime graph and structured session rationale [skip ci] * [comment audit] Condense browser creation and remote tab recovery rationale [skip ci] * [comment audit] Condense snapshot authority and publisher recovery rationale [skip ci] * [comment audit] Condense frame, startup and provider cache rationale [skip ci] * [comment audit] Condense host twin and agent lifecycle rationale [skip ci] * [comment audit] Condense browser authority and editor cleanup rationale [skip ci] * [comment audit] Condense editor buffer identity and review cache rationale [skip ci] * [comment audit] Condense provider lifetime and repository ownership rationale [skip ci] * [comment audit] Condense reconnect, hydration and terminal retirement rationale [skip ci] * [comment audit] Condense recovery, reconciliation and persistence rationale [skip ci] * [comment audit] Condense cleanup ownership and listing admission rationale [skip ci] * [comment audit] Condense teardown ordering and terminal ownership rationale [skip ci] * [comment audit] Condense recovery ledger and web host-boundary rationale [skip ci] * [comment audit] Condense web reconnect and credential lifecycle rationale [skip ci] * [comment audit] Condense cloud rollout and assignment safety rationale [skip ci] * [comment audit] Condense relay session and database recovery rationale [skip ci] * [comment audit] Condense relay placement and protocol compatibility rationale [skip ci] * [comment audit] Condense build and packaging constraints [skip ci] * [comment audit] Condense CI gates and validation isolation rationale [skip ci] * [comment audit] Condense catalog identity and browser validation rationale [skip ci] * [comment audit] Condense mobile browser and frame-budget rationale [skip ci] * [comment audit] Shorten mobile route and browser test explanations [skip ci] * [comment audit] Condense editor and session seam test rationale [skip ci] * [comment audit] Trim session and terminal closure history [skip ci] * [comment audit] Shorten terminal browser and census explanations [skip ci] * [comment audit] Preserve root-only E2E diagnostic baseline * [comment audit] Condense mobile seam and HTML preview test rationale [skip ci] * [comment audit] Condense preview instrumentation and render harness rationale [skip ci] * [comment audit] Condense bundle contracts and native-build constraints [skip ci] * [comment audit] Shorten native packaging and CI routing rationale [skip ci] * [comment audit] Condense release tooling and benchmark constraints [skip ci] * [comment audit] Condense packaging and platform gate rationale [skip ci] * [comment audit] Shorten mobile route and history rationale [skip ci] * [comment audit] Condense mobile browser and component rationale [skip ci] * [comment audit] Shorten drawer and workspace-form rationale [skip ci] * [comment audit] Trim mobile review and editor-document explanations [skip ci] * [comment audit] Condense editor lifecycle and Markdown explanations [skip ci] * [comment audit] Trim mobile file, diagnostics and dictation commentary [skip ci] * [comment audit] Simplify mobile host-list and workspace-view explanations [skip ci] * [comment audit] Clarify mobile shell lifetime and bridge delivery constraints [skip ci] * [comment audit] Condense bridge stream, native request and audio rationale [skip ci] * [comment audit] Reduce bridge schema and page-client commentary [skip ci] * [comment audit] Tighten mobile page bridge contracts [skip ci] * [comment audit] Clarify mobile cache and session constraints [skip ci] * [comment audit] Condense mobile route and lifecycle rationale [skip ci] * [comment audit] Tighten mobile shell hook timing rationale [skip ci] * [comment audit] Clarify route handoff and notification compatibility [skip ci] * [comment audit] Clarify push registration and dictation cleanup constraints [skip ci] * [comment audit] Condense keyboard geometry and native media ownership rationale [skip ci] * [comment audit] Tighten mobile chat scroll and composer concurrency rationale [skip ci] * [comment audit] Condense mobile reply compatibility and upload ownership rationale [skip ci] * [comment audit] Condense optimistic echo and scroll ownership rationale [skip ci] * [comment audit] Tighten stream replay and queued session ownership rationale [skip ci] * [comment audit] Condense mobile composer and reply-schema constraints [skip ci] * [comment audit] Clarify mobile transcript recovery and session ownership [skip ci] * [comment audit] Clarify mobile queue and source-control compatibility [skip ci] * [comment audit] Condense storage and task launch constraints [skip ci] * [comment audit] Clarify task schemas and terminal document ownership [skip ci] * [comment audit] Tighten terminal input and viewport rationale [skip ci] * [comment audit] Shorten terminal and RPC recorder contracts [skip ci] * [comment audit] Condense recorder engine and connection contracts [skip ci] * [comment audit] Shorten host credential and relay recovery comments [skip ci] * [comment audit] Tighten pairing recovery and bundle transport rationale [skip ci] * [comment audit] Clarify RPC delivery and workspace catalog boundaries [skip ci] * [comment audit] Condense catalog and relay entry contracts [skip ci] * [comment audit] Shorten shutdown and SSH relay delivery contracts [skip ci] * [comment audit] Condense WSL relay and E2E regression rationale [skip ci] * [comment audit] Shorten cross-version harness and renderer fixture explanations [skip ci] * [comment audit] Condense paired-host and E2E lifecycle rationale [skip ci] * [comment audit] Shorten layout oracle and regression explanations [skip ci] * [comment audit] Condense paired recovery and ownership regression contracts [skip ci] * [comment audit] Shorten restart, sidebar and SSH lifecycle contracts [skip ci] * [comment audit] Condense terminal rendering and IME regression rationale [skip ci] * [comment audit] Shorten terminal recovery and benchmark contracts [skip ci] * [comment audit] Condense tooling and Windows lifecycle proof rationale [skip ci] * [comment audit] Condense workflow routing and release rationale [skip ci] * [comment audit] Condense mobile security and installer contracts [skip ci] * [comment audit] Shorten native rationale and stylesheet workarounds [skip ci] * [comment audit] Shorten Windows visibility probe rationale [skip ci] * [comment audit] Condense embedded release and signing rationale [skip ci] * [comment audit] Retain current main test behavior in final reconciliation [skip ci] * [comment audit] Complete final comment review across current source inventory * [comment audit] Handle consolidated audit reports before final CI * [comment audit] Preserve credential storage census detail [skip ci] * [comment audit] Scale broad changed E2E selections with verified timing shards * [comment audit] Align E2E assertions with browser close and settled session metadata * [comment audit] Match existing E2Es to current resource and windowing contracts * [comment audit] Observe the shared server outage notice in reconnect coverage * [comment audit] Make existing renderer probes measure settled tab and PTY work * [comment audit] Align palette E2E selector with task search * [comment audit] Refresh remaining palette search selectors * [comment audit] Keep selector correction within selected audit specs * [comment audit] Await emoji fixture settings inside renderer evaluation * [comment audit] Cancel fixture fit retries between terminal tests --------- Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> |
||
|
|
9bed1b47aa |
perf(dev): serve the renderer with bundled dev mode by default (#33449)
* perf(dev): serve the renderer with bundled dev mode by default * test(dev): expect bundled renderer dev by default |
||
|
|
f66c4c0276 |
build(dev): add opt-in ORCA_DEV_BUNDLED=1 for Vite bundled renderer dev mode (#33433)
Serves the renderer through Vite's experimental bundled dev mode when the variable is set. Strict execution order keeps the React Refresh preamble ahead of chunks shared by the four HTML entries. Default dev behavior is unchanged. |
||
|
|
fef00e4461 |
feat(dev): give each worktree its own pnpm dev profile and ports (#33416)
Linked worktrees now get a per-worktree userData profile under
orca-dev-wt/<hash>, a deterministic collision-checked renderer port
(15200+) and CDP port (9333+), so parallel `pnpm dev` runs no longer hit
the single-instance lock. The main checkout keeps the shared orca-dev
profile; --shared-profile / --worktree-profile / ORCA_DEV_PROFILE override.
The runner prints `ORCA_DEV_INSTANCE {json}` on stdout and mirrors it to
out/dev-instance.json so agents can attach over CDP. orca-dev.mjs and the
terminal wrappers follow the profile the worktree's dev app used.
Adds profile seeding (pnpm dev:seed-profile, pnpm dev --seed-profile,
pnpm dev:fresh --seed-from): an allowlisted slim copy of projects,
workspaces, settings, SSH targets, and the account session, written as
orca-data.json for Orca's own import; the source is only read.
Storage reclaim removes per-worktree profiles once their worktree is gone
and no app, runtime, or daemon still uses them. dev-fresh-profile.sh is
replaced by a cross-platform dev-fresh-profile.mjs.
|
||
|
|
95df4c74ed | refactor: group Parcel watcher process ownership (#33401) | ||
|
|
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> |
||
|
|
d3dbd9683f |
Update Orca's skills with Orca's own installer instead of npx skills (#31688)
* Add Orca's own bundled-skill planner and installer (not wired in yet) One planner decides what installing Orca's bundled skills does to each target folder; execute re-plans under per-path locks and runs only a plan whose digest still matches; a shared summary derives badge, wording and exit code. A copy counts as Orca's untouched one only when its SKILL.md bytes match a version some build shipped (resources/skills/released-digests.json, generated from git history and checked add-only). Nothing calls this yet; the npx path is unchanged. * Report left-alone folders as warnings and keep backups in the home A leave is no longer a failure: the outcome line says what happened plus "N left alone: <reason>", and the exit code is 1 only for a failed or changed op. Backups for every scope go to <home>/.agents/.orca-skill-backups on the executing host, so a project Replace never writes into the repo; moving there falls back to copy, verify, then remove across filesystems. * Update Orca's skills with Orca's own installer instead of npx skills The app's Update skills dialog, badge, nudge and status pills now read the bundled-skill plan and summary, and Update executes the reviewed plan's digest. `orca skills install|update` runs the same planner in-process with --dry-run, --force, --json and per-summary exit codes. Removes both npx spawn sites, the freshness inventory modules and the npx round-trip CI. * Update only where the skill already is; one provider-root rule for app and CLI installedOnly now drops every location that holds nothing (keeping the shared slot only when an existing agent link points at it), so Update never adds a skill for another agent. The app and CLI resolve the Claude skills folder through the same rule in localBundledSkillInput; a test asserts both plan identically. * Compact Update skills dialog: one row per skill, locations named by agent summarizeBundledSkillPlan now returns each skill's locations, labelled by agent (or "Shared folder") with a ~-collapsed path; the dialog shows one row per skill that needs something, collapses up-to-date skills into one line, and lists every location behind "Show all locations". The result reads "Updated N skills · M left alone". Under installedOnly a Replace with no shared copy replaces in place instead of adding one. * fix(skills): keep location paths readable by putting the state under the path * fix(skills): hold the last plan in state, not a ref mutated during render Split the update dialog's rows, headline and footer into components, move the Update label next to the other plan copy, and use z.strictObject for the request schemas. * Escape non-ASCII characters in bundled-skill regex and wording The orcad bundle rejects a literal non-ASCII character in a regex. * fix(cli): escape non-ASCII apostrophes in the skills install help text * test(orcad): check the bundled CLI plans a skill install without npx Drop the deleted roundtrip workflow's group from the closed-PR cancellation fixture. * Make bundled-skill test fixtures link folders the way the product does on Windows Directory links in fixtures are junctions on Windows, which need no Developer Mode; the one file-link fixture is skipped there. * Resolve legacy skill agent keys canonically * Reject stale skill replacements when auxiliary content changes * Keep ordinary bundled skill classification shallow * Leave unreadable skill replacements alone without aborting plans * Pass force-all through shallow planning and describe unreadable copies * Clarify skill update statuses and folder details * Keep selected skill replacements in place with one status * Report completed skill replacements with their backup confirmation * Clarify available skill updates and skipped copies * Keep skill update results in preview order * Show each skill skip reason once * Count skipped skills instead of skipped folders * Make installer input and link tests work on Windows * Bound bundled skill classification reads and parse names linearly * Keep workspace skill roots inside the resolved workspace * Compare workspace skill roots with native path semantics * Restrict bundled skill backups to their owner on POSIX * Keep recovered skills needing attention until they are updated * Count completed skill updates in the status tooltip * Sync the runtime localization catalog |
||
|
|
8e9690468a |
fix(orcad): ship computer use with the server and say when it is unavailable (#31958)
* fix(orcad): ship computer use with the server and say when it is unavailable * test(computer): drop the type assertion in the sidecar app-environment mock * fix(cli): carry the server reprovision hint into --json computer failures |
||
|
|
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 |
||
|
|
51530bb86c |
build: declare dependencies that only hoisting supplied (#30225)
* build: declare dependencies that only hoisting supplied Under pnpm's global virtual store each package sees only its declared dependencies. Several published packages relied on hoisting: - jest-dom's vitest matcher augmentation could not find vitest (1645 TS2339 errors in tc:web), and react libraries' .d.ts could not find @types/react, so lucide-react, dnd-kit, cmdk, sonner and others typed as `any`. Other .d.ts phantoms (electron, vfile, prosemirror-model, d3-selection, type-fest) degraded silently the same way. - electron-vite resolves electron/package.json and the binary itself. packageExtensions declares these, which also holds in the hoisted layout. The markdown alias now resolves decode-named-character-reference through mdast-util-from-markdown, the package that actually declares it. * build: let addMissingPeerTypes supply @types/react to react peers pnpm 12.12 links a peer's @types package next to every package that peer-depends on it, which replaces the hand-listed @types/react peers. The regression test now reads pnpm-workspace.yaml, so it checks every packageExtensions entry instead of a hand-picked seven. |
||
|
|
7f8faae800 |
feat(space): open disk usage in a separate window (#29513)
* feat(space): open disk usage in a separate window * perf(space): move local disk scan off Electron main thread * fix(space): satisfy full PR quality gates |
||
|
|
b3dfd54827 |
perf(build): skip unnecessary work and reclaim Vite caches (#29479)
* perf(build): remove unnecessary development and config work * feat(dev): reclaim inactive worktree Vite caches --------- Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> |
||
|
|
7005af3451 |
perf: avoid dev reloads for excluded package files (#29481)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> |
||
|
|
bd56d8d096 |
perf(dev): skip unchanged process-host package rebuilds (#29317)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> |
||
|
|
de5470a900 |
fix(rate-limits): start the Codex usage probe off the main thread so focus doesn't freeze the UI (#27707)
* fix(rate-limits): spawn the Codex usage probe off the main thread On Windows, creating a cold standalone codex.exe can spend seconds in code integrity checks on the calling thread. The usage probe runs on window focus, so that froze the UI. The probe's child is now created and piped on a worker thread and proxied back to the main thread. Fixes #27362 * refactor(rate-limits): type the worker's children through @orca/process-host * fix(rate-limits): kill an orphaned Codex probe by pid when its worker dies Once the worker thread that owns the probe's child is gone, kill() can no longer reach the child through it. The proxy now falls back to a pid-addressed kill; the Codex probe uses the app-server tree kill so a node launcher's codex.exe child goes too, while SIGTERM stays a graceful root signal. * refactor(build): move the main build's entry map out of electron.vite.config.ts The new worker entry pushed electron.vite.config.ts past the 300-line limit. The Rollup input map for the main build now lives in its own module. * refactor(rate-limits): drop the orphan pid kill and trim the worker child-process stand-in A worker thread's death closes its children's pipes, so an abandoned Codex probe already gets stdin EOF, its graceful stop. The pid-addressed orphan kill was redundant with that and with terminateCodexProbeChild's own gated Windows tree kill. Its hard kill also bypassed the tree-kill gate's recycled-pid refusal by turning the handle-addressed fallback into a pid kill. - Remove orphan-pid-kill, killOrphanedCodexProbe, the injected killOrphan, and the orphaned flag. kill() now reports false once a child is no longer tracked. - Process-creation failure is one 'spawn-error' event that the stand-in expands into error/exit/close, replacing the duplicated triple and the exported exit code. Stdin errors arrive as child errors, as the probe already treated them. - Fold both suites into one over a real MessageChannel and real children. Add a real-worker test showing that terminating or crashing the worker lets the child exit on stdin EOF. * test(rate-limits): run the worker child-process suite under Node The suite asserts Node behavior that Electron main relies on: a failed spawn closes with a null signal, and a dead worker thread closes its children's pipes. Bun reports an undefined signal and, on Linux, leaves the pipes open, so the Bun lane failed on runtime differences, not on the product code. * test(rate-limits): count a reaped-by-nobody zombie as an exited child On Linux and macOS a child whose worker thread died gets stdin EOF and exits, but libuv reaps a child only through the handle on the loop that spawned it. Once that loop is gone, nothing waits on the pid, so it stays a zombie and kill(pid, 0) still succeeds. The real-worker tests now treat a zombie as exited, and still require the child's EOF marker. --------- Co-authored-by: Orca Worker <orca-worker@localhost> |
||
|
|
47b94ef64a |
fix(daemon): keep tweetnacl out of the relocated Windows daemon-host graph; guard daemon externals at build (#27504)
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. |
||
|
|
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> |
||
|
|
5cafefe726 |
Phase 3: every SSH host runs a managed Orca server (orcad), replacing the relay (#24863)
* Revert "revert: take the 26 Phase 3 (#16741 port) PRs back out of main (#24559)" This reverts commit |
||
|
|
8464b151c7 |
Claude account profiles: dormant routing and consumers (Step 2 of 4) (#24351)
* feat(claude): add dormant profile setup and history sharing * fix(claude): make profile setup one gated, typed, fail-safe entry Review round 1 of the dormant profile setup found that the pieces could be called without their safety checks, that one failed write or an unreadable bookkeeping file could silently stop sharing for good, and that Windows prompt history could bring back history the user cleared. - One entry, provisionClaudeAccountProfile: the profile gate (namespace, no linked components, outside ~/.claude and ~/.config/claude, and an ownership marker beside the home naming the account and target) runs first and refuses before creating anything; then history sharing, config provisioning, and the hook install after the settings merge. Results come back per surface with closed warning codes instead of message text. - The sharing ledger is keyed by surface name, records a value only after its write succeeded, and an unreadable ledger starts empty and is rewritten instead of blocking every surface. - The profile state file goes through the same locked writer as folder trust (Claude's <file>.lock plus the in-process queue), generalized as updateClaudeGlobalConfig. Onboarding and trust are still applied when the personal state file is unreadable. - WSL descriptors build guest POSIX paths; the state-file path style follows the injected platform. - Orca's managed statusLine has one owner in a profile: the settings merge never shares it, a user's own statusLine is shared over it, and the profile installer follows the default home's slot so a default opt-out reaches every profile. remove() takes the same destination; the remote installer cannot accept one. - Prompt history compares file identity (bigint dev+ino) on every platform, never drains the shared file into itself, drains retained copies in generation order, never reuses a stale cursor, and on Windows keeps a replaced default's old copy aside instead of replaying it. Directory merges keep going past a failed entry. * fix(claude): share the user's own hooks and keep merged history whole A user's own Claude hooks in ~/.claude (notifications, formatters) did not run under a managed account, because the whole hooks key stayed private. They are now shared like any other settings key: Orca's own hook entries and its managed statusLine are stripped from both the personal value and the profile's current value before the per-key ledger comparison, so they never travel through the merge and never make the key look user-owned. Orca entries already in the profile are kept on write, and the profile hook installer adds them on top as before. Prompt history: merged bytes that lack a final newline are terminated, so Claude's next record no longer fuses onto the last merged line. When a CLI rewrote the profile's history file (old records plus new), only the lines past the part it shares with the default history are added, instead of the whole file again. * fix(claude): close review round 2 gaps in profile setup Hooks and statusLine sharing: - When ~/.claude holds only Orca's hook entries, the user's shared hooks now read as an empty value instead of a missing key. Removing the user's last own hook in ~/.claude therefore reaches profiles that never edited it, and deleting the only shared hook inside a profile stays deleted. - A custom statusLine Orca shared, and the profile never edited, goes away when the default home drops it. When a shared custom line replaced Orca's line in a profile, the profile's statusline marker is dropped so Orca's line comes back once the default returns to it; a profile that opted out stays opted out. No other key gains deletion. - install/remove/getStatus with a profile directory refuse when it is the default home, or its settings.json resolves to the default one, instead of editing System Default's hooks and opt-out state. - The profile statusline rule reads the default settings under the userHome passed to the setup entry, not os.homedir(). Profile state and ownership: - A malformed `projects` value skips only folder trust (new warning code trust-refused); onboarding and shared keys still apply. - The ownership marker stores only host-local facts (account, runtime, distro). The execution host id is the caller's view of the host, so it stays in the in-memory descriptor and is not compared. Prompt history interruption paths: - With no cursor yet, a retained copy starts past the bytes it shares with the default history, so an interrupted share no longer replays the whole history. - A retained name for the shared file itself is removed with its cursor instead of lingering until a later scrub makes it look new. - The Windows link record is read three-state: unreadable stops the share instead of reading as "no link". If the record cannot be written after linking, the fresh link is undone. - An unreadable retained copy is reported and no longer blocks linking. * build(cli): list the new Claude hook modules in the CLI project hook-service.ts and hook-settings.ts are compiled into the packaged CLI project, which lists every file explicitly. The statusline policy and profile destination modules they now import were missing, so the CLI typecheck failed with TS6307. The CLI still loads hook-service through the existing managed-agent-hook-controls build entry, which bundles both modules; neither imports electron. * fix(claude): close review round 3 regressions in profile setup - A profile whose hooks hold only Orca's entries and that sharing never recorded is no longer treated as a user edit, so the user's first own hook in ~/.claude reaches it (for example when the profile was set up before ~/.claude had any hooks). - A retained prompt-history file is removed as a second name for the shared file only when the default history does not itself link to it; otherwise it holds the only copy and is kept. - Default-home checks compare file identity: the profile hook destination check uses device and inode, and the profile/default separation check resolves on-disk case, so a case-only alias of ~/.claude is refused on case-insensitive filesystems. - A test pins that an unreadable leftover session tree no longer blocks linking. * fix(claude): let shared keys leave a profile when ~/.claude drops them QA found that removing a setting from ~/.claude never reached a managed account: deleting the whole `hooks` block left the user's hook running there. Only statusLine followed the default away. Every shared key now follows the same rule through the existing per-key ledger: when a key disappears from ~/.claude/settings.json (or mcpServers/theme from the personal state file), it is removed from the profile if the profile still holds exactly what Orca last shared. A value changed inside the account is kept. Keys Orca never shared, including denylisted ones, are never touched. Deleting the whole hooks block removes the user's shared hooks and keeps Orca's own entries. A missing source counts as empty; an unreadable source removes nothing. * feat(claude): add dormant profile routing and account consumers * fix(claude): drop the dormant profile selection RPC; clients negotiate by capability Restores the inline mobile allowlist so its source-scan guard sees every accounts.* method again, and the generated params catalog to generator order. * fix(claude): guard the claude shell function and honour a hand-exported config dir The function is defined only in a routed pane where claude is a real executable (the codex function's guard), re-reads the pointer only while CLAUDE_CONFIG_DIR is unset or still Orca's injected twin, accepts Git Bash drive paths, and starts on its own line after the fish/PowerShell codex text. * fix(claude): spawn-time profile env, total account listing, setup at lifecycle triggers Round-1 review fixes for the dormant profile routing: - Panes get the selected profile's CLAUDE_CONFIG_DIR plus an Orca twin at spawn, so nested shells and scripts inherit the account; System Default injects nothing and its home is the inherited CLAUDE_CONFIG_DIR. - An absent routing owner is System Default, never a throw; AI Vault and session-search scans receive profile roots from their parent, and the capability is advertised only where an owner is installed. - Account listing never throws: per-account readiness, a stale pointer is republished in the background and reported on the snapshot. - Profiles are set up at select and startup; a launch only sets up one that never was, and a worker fault on a prepared profile is a warning. The Claude version probe is cached per binary identity. - Pre-trust goes through the existing deadline- and realpath-guarded writer against the launch env's profile config. - Skill discovery keeps a caller's Claude root and a broken Claude selection no longer fails other providers. - The durable record carries a provider-neutral launchAccountHome, read through one helper by the launch fallback and the model catalog. * test(claude): pin the version-probe cache, launch-account record and temp-home readers * test(claude): pin dormant bash rc text alongside fish and PowerShell * test(claude): read the fish launch init without a nullable index * fix(claude): withdraw the profile pointer when a selection cannot be published A pointer left naming the previous account would launch it silently; a missing pointer makes the claude function refuse visibly. A newer selection that raced the failed one keeps its pointer. * fix(claude): read the fish profile pointer with read -z for fish older than 3.4 Shell tests skip system config and abort unless claude resolves to the fake. * fix(claude): only the newest publish withdraws the pointer; total config dir lookup - An overtaken publish that fails leaves the newer selection's pointer. - The runtime config dir falls back to the legacy home for an unresolvable account or a WSL target, so skill roots never fail for other providers. - WSL guest reader roots merge verbatim, never realpathed on this thread. - History readers include ~/.claude, where step-1 setup pools profile history. - System Default ignores a config dir an outer Orca injected (twin-marked). * fix(claude): System Default launches and probes use the structured create resolver A Claude agent-env CLAUDE_CONFIG_DIR the create path stored is now the home the launch pins and the model probe accepts. * test(claude): type the System Default launch record as an agent-session record * fix(claude): install profile hook scripts under the setup job's home A worker thread's os.homedir() ignores its own env, so the hook and statusline scripts now go under the home the job names. The worker test pins the process HOME to a sentinel, refuses to run unless the worker sees it, and asserts nothing lands there. * test(claude): skip shell cases whose shell the runner lacks * fix(claude): remove env vars in the PowerShell claude function instead of setting null On .NET 9+ (pwsh 7.5+) SetEnvironmentVariable with $null creates an empty variable, so stripped auth vars reached claude as empty strings and the restore left CLAUDE_CONFIG_DIR empty in the user's session. * fix(claude): give plain fish tabs the claude function through the codex hand-off Main now gives a plain fish tab Orca's codex function through a vendor_conf.d snippet instead of a -C init. The claude function only rode the -C path, so a plain fish tab would not re-read the account selection per invocation once profiles are on. Define it at the first prompt beside codex; it stays empty while the profile gate is off. * fix(claude): share personal rules, themes, workflows and keybindings into account profiles A managed account launches Claude with its own config folder, so user-level rules/, custom themes/ (which a shared `custom:<slug>` theme points at), personal workflows/ and keybindings.json silently stopped applying. Link the three directories like skills and commands, and copy keybindings.json with the same edit-preserving ledger as CLAUDE.md. routines/ stays unshared: routines belong to the claude.ai account and the folder holds per-run state. * test(claude): wait for the running child to read its account before switching The test switched the selection after a fixed 20 ms, so under load the backgrounded claude had not yet read the pointer and picked up the new account. The stand-in now marks when it has started, and the test waits for that mark (bounded) before switching. * fix(claude): import the personal CLAUDE.md into account profiles instead of copying it Claude also loads ~/.claude/CLAUDE.md as a parent folder's memory for any project under home, so a copied account CLAUDE.md made every such session read the user's instructions twice (checked live with Claude 2.1.288). An @~/.claude/CLAUDE.md import resolves to the same real file, which Claude loads once from home, from projects under home and from folders outside it. * refactor(claude): simplify account profile setup toward the prior art - Windows keeps each account's history private; drop the hardlink, link record and conflict-copy machinery that only Windows reached. - Share hooks and statusLine as ordinary settings keys: Orca writes the same entries into every folder, so the installer finds them present. Drops the Orca-entry carve-out, the per-profile statusline follow logic and its marker. - Unreadable ledger is just an empty ledger. - Share from the user's own CLAUDE_CONFIG_DIR when they set one (marked so Orca's injected value is never mistaken for it), and refuse a profile at or around it. - Pin the one canonical profile path spelling in a test. * refactor(claude): route launches through one account router, superset-shaped Replace the routing service, owner interface, setup worker thread, reader-root merging, persisted launch account and capability string with one ClaudeProfileRouter: the pointer is written first and setup runs best-effort after it (superset's order); a missing pointer means System default. The claude shell function re-reads the pointer on every launch, refuses only a selected account whose folder is missing, and prints a note when the user's own CLAUDE_CONFIG_DIR overrides the selected account in that terminal. Still dormant: claudeProfileRoutingEnabled() is false. * test(claude): type router test settings instead of casting * fix(claude): run account setup on a worker thread, never Electron main publish() writes the pointer and starts setup in the background, so neither startup nor an account switch blocks on a history merge. Each setup runs in a one-shot worker (the profile-state backup worker's pattern); one setup per account at a time, reused by later requests. A launch waits only for a folder that was never set up, and refuses with a clear message if that setup fails. * fix(claude): do not await the synchronous pointer publish * test(claude): give the routing launch test the merged resolver deps and handle shape * fix(claude-accounts): dedupe merged prompt history, drop drained copies, link setup folders by path - Prompt-history drain appends only lines the shared file lacks, so a purge never re-adds lines. - A set-aside history copy whose saved offset reaches its end is deleted on the next run. - Setup folders link to the default home's own entry, not its resolved target. - The profile gate and folder creation run once, in provisionClaudeAccountProfile. - installHooks receives only configDir; drop a duplicate test key that fails CI. * fix(claude-accounts): refuse a routed resume whose transcript is in another account; zsh claude function; setup timeout - With account routing, a chat resume checks its transcript is in the launch folder; a missing one with a stored leaf refuses with historyInOtherAccount instead of starting fresh. - The launch folder of a selected account comes from prepareLaunch(); the resolver stays for System default. - zsh panes get the claude function like bash, fish and PowerShell (empty while routing is off). - The setup worker is terminated after 60 s so a later launch can retry. - Document that the setup marker means setup started, not finished. * fix(claude-accounts): refuse a routed resume only when the transcript is found in another folder A transcript found in no known folder keeps the old stored-leaf resume. * fix(claude-accounts): record installed hooks as Orca-shared; skip symlink tests on Windows After Orca installs its hooks into an account, record the account's hooks in the settings ledger so a later run can still bring the user's own hooks in. Tests that create real symlinks now skip on Windows. * fix(claude-accounts): trim the which-account file in the PowerShell claude function Co-Authored-By: Claude <noreply@anthropic.com> * test(claude-accounts): spell the user's own config folder as an absolute path on every platform Co-Authored-By: Claude <noreply@anthropic.com> * test(claude): skip the POSIX-only WSL profile test on Windows A WSL profile's data root is a POSIX path, so building one from a Windows temp dir fails the absolute-path check there. * fix(claude): check the transcript before the account-switch recheck when no router is installed Keeps the switched-off path identical to main: the recheck stays the last await. --------- Co-authored-by: Jinwoo-H <jinwoo0825@gmail.com> Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
b5814d058e |
Avoid rescanning shared chunks in the plain Node build guard (#24717)
Reuse a successful unchanged chunk-code verdict within one synchronous writeBundle invocation, while preserving traversal and current-code reads. |
||
|
+33 |
5e6587197a |
fix(opencode): keep shared-session TUI activity with its owning pane (#24962)
* fix(opencode): bind legacy attach status to its TUI pane Adapt the official v1 TUI API to the existing pane reporter and learn structural session identity only after execution-host admission. Preserve known creators, gate new reports to capable hosts, and register the legacy TUI entry without changing user settings or overlay targets. Credit werlang for the same-directory investigation in #22838 and #22847; no source from that PR is copied. (cherry picked from commit d9eeb028a23a66b78b08538a5ebf6052499ec24f) * fix(opencode): fence legacy TUI viewers before creator attribution Use the execution host's existing admission policy on the physical TUI pane and launch token before rewriting to a session creator or mutating the launch-token cache. Preserve live viewers, frozen server stamps and OpenCode 2 behavior. Add HTTP regressions for retired panes, closed tabs, replaced tokens and relay retirement, plus current-launch, alias and compatibility controls. Follow-up to #22838 and @werlang's original #22847; preserves the original intentional creator attribution without copying that PR's source. * fix(opencode): preserve current plugin exports in legacy TUI wrapper Keep the current server, setup, id and other default export entries when adding the legacy TUI entry. Cover their preservation and seed the ACL fixtures with the separate installed TUI/config bytes. This completes the current-main port of the legacy identity replacement for issue #22838, carrying @werlang's original PR #22847 contribution. * fix(opencode): retry timed-out SSH plugin updates (#24666) Preserve bounded retry behavior and the current-main status-envelope fields. Original-PR: #24124 Reviewed-source: |
||
|
|
c1d403a47b |
feat(cli): link GitHub and GitLab items on worktree create and set
Expose and validate the existing workspace link fields, preserving omitted values and provider identity checks. Related: https://github.com/stablyai/orca/pull/22609 Co-authored-by: marco song <marco.song@mvlchain.io> Co-authored-by: SongMarco <20613630+SongMarco@users.noreply.github.com> Co-authored-by: Luca Critelli <lucacri@gmail.com> Co-authored-by: Neil <neil@stably.ai> |
||
|
|
40e60b7385 |
refactor(ai-vault): delete the unused session-scanner worker thread (#24607)
* refactor(ai-vault): delete the unused session-scanner worker thread Production always scans through the forked session-scanner service process; the worker thread was reachable only under NODE_ENV=test or the undocumented ORCA_AI_VAULT_SERVICE_PROCESS=0 switch, and nothing fell back to it on service failure. Remove the thread (spawn, client, protocol, entry, tests), its build entry, knip and plain-node-guard listings, and the backend switch, so session-scanner-background always routes to the service. - Move the scan options type to the service protocol as AiVaultServiceScanOptions. - Tests now mock session-scanner-service-spawn, the seam production calls. - Repoint the hot-path listing reliability gate from the worker-client test to the service-client test, which covers the same bounded-queue, cancellation, and fault-restart properties for the real executor. STA-9122 * test(ai-vault): cover the service's Claude-vs-OMP subagent lister choice Runs the real service entry and subagent reader, replacing only the two per-agent listers, so a swapped lister choice fails. STA-9122 |
||
|
|
f8a31edd6f |
refactor(cursor): move the desktop-login read onto the shared foreign SQLite reader worker (#24603)
* refactor(sqlite): rename the OpenCode SQLite worker entry to foreign-sqlite-reader (STA-9122) The worker thread that reads OpenCode's database off the main thread is about to read other apps' databases too, so its entry is renamed to what it is: src/main/foreign-sqlite-readers/foreign-sqlite-reader-entry.ts, built as out/main/foreign-sqlite-reader-entry.js. Why now: #24572 fixed the Cursor focus freeze with a second, dedicated worker. Rather than grow one worker per foreign app, the next commit moves Cursor onto this entry and deletes that worker. This commit is the rename only; #24572's cursor-desktop-profile-worker-entry lines stay until then. It moves out of ai-vault/ into a new foreign-sqlite-readers/ module because it will no longer be session-scanner code; the module will own the readers, their dispatch, protocol and main-process client. The entry still routes only OpenCode kinds in this commit. The OpenCode dispatch, protocol and process entry stay in ai-vault/ and stay OpenCode-only, because the SSH/WSL relay reader bundles them (build-relay.mjs). Every reference is updated: electron.vite.config.ts input key, knip entry, the plain-node entry guard and its test, the asarUnpack list (the scanner service still spawns this entry under ELECTRON_RUN_AS_NODE), and the electron-builder test that reads the filename. The filename and the beside-or-one-up (Rollup chunks) lookup now live in foreign-sqlite-reader-entry-path.ts, which the OpenCode spawn reuses, plus an Electron-main resolver that uses the packaged app.asar path. * fix(cursor): move the desktop-login read from its dedicated worker onto the foreign SQLite reader (STA-9122) #24572 fixed the Cursor focus freeze (#24360) with a dedicated worker (rate-limits/cursor-desktop-profile-worker*.ts). Orca already runs OpenCode's database reads on a worker, and more foreign-app SQLite reads are coming, so keeping one worker per app means one entry, build input, asarUnpack line, knip entry and guard line each. This keeps one pattern instead: Cursor's state.vscdb read runs on the shared foreign SQLite reader entry, and the dedicated worker, its entry and its config lines are deleted. What moves: - The read itself is a pure cursorProfile reader in foreign-sqlite-readers/readers/ (was rate-limits/cursor-desktop-state-db.ts), run only on the worker. A separate dispatch owns the new kinds and refuses an unknown kind. The entry routes OpenCode kinds to the untouched OpenCode dispatch, so the relay's OpenCode reader stays byte-identical. - ForeignSqliteReaderClient gives each reader its own WorkerThreadRequestQueue lane (own lazily started, idle-torn-down thread; one shared factory) with in-flight dedupe per database path. Any failure resolves to the reader's existing failure value and never falls back to the main thread. Kept from #24572, so every reader gets them: - Await worker retirement before respawning. Worker.terminate() cannot interrupt a native SQLite call (e.g. a WAL-index rebuild), so the old thread lives on until that call returns; respawning at once stacked a new thread on the same work for every timed-out read (#24572 measured three live workers). This belongs in the shared host, which fire-and-forgot terminate(): LazyWorkerThreadHost now takes awaitRetirement and refuses to spawn until the terminated worker settles, and the queue fails calls closed meanwhile. Opt-in, because pure-JS clients (session scanner abort, port scan) respawn right after an abort. A rejected terminate() also ends retirement, so it cannot latch the reader off (raised in #24572's review). - 10 s Cursor timeout, 2 consecutive deaths, a queue cap of 8. - #24572's worker tests, rewritten against the shared client: responsive caller plus coalesced probes, unavailable worker without path leaks, stalled-worker recovery, no respawn before retirement, dispose settles. Tests: reader, dispatch, client (timeout, 10 s default, crash, malformed, unavailable without a main-thread read, dedupe, queue cap, own thread per reader), queue retirement (stalled and rejected terminate), import boundary, and an event-loop test reading a ~50 MB WAL with no -shm on a real worker. * test(sqlite): walk the reader import boundary with the shared source-tree scan (STA-9122) * fix(sqlite): key reader dedupe on a caller-supplied key, not the path alone (STA-9122) |
||
|
|
1aa0860f7e |
Keep large Markdown previews responsive (#24880)
* Keep large Markdown previews responsive * Fix large preview review navigation and Find budgets * Initialize preview scroll caches once and check viewport visibility * Restore large previews after loaded rows are measured * Refresh loaded Markdown rows after viewport changes * Keep Markdown revisions visible and reuse bounded search text |
||
|
|
0a81211994 |
fix(cursor): read desktop login outside the main thread (#24572)
* fix(cursor): read desktop login outside the main thread * fix(cursor): await native worker retirement before respawning |
||
|
|
cfa43e7eab |
fix(codex): opening a terminal no longer strips Codex hooks from the real ~/.codex (#23552)
* fix(codex): a real-home restore leaves a file alone once someone else changed it Orca writes ~/.codex/hooks.json (and a trust rebase writes config.toml), then runs a Codex trust session for up to 10 s, then restores the original bytes if the session fails. The restore wrote unconditionally, so a save that landed during the session, from the user or another Orca, was silently reverted. Each restore now compares first: it writes the original back only while the file still holds the generation Orca's mutation left, and otherwise logs and leaves it alone. This covers the real-home install and opt-out sweep (restoreRealHomeHooksJson), the legacy sweep's hooks restore, and config.toml rollback (restoreCodexTrustConfig). For hooks.json the generation is the exact bytes Orca wrote. For a config.toml that a trust rebase changed it is the file as the rebase left it. When Codex itself wrote config.toml inside the session that just failed, Orca never knew those bytes, so that rollback compares against the file as the session settled. The next commit keeps other Orca instances out of that window; a user edit made during such a session can still be rolled back. * fix(codex): serialize real-home Codex writes across Orca instances Every Orca on one HOME (a dev and a packaged app, or an offline CLI) writes the same ~/.codex/hooks.json, config.toml and ~/.orca/agent-hooks/codex-hook.sh. The per-file lane that orders capture, mutate and restore was in-process only, so another instance could write inside this one's restore window, or undo it. The lane for the user's real config.toml now also holds the existing crash-safe managed-hook install lock (~/.orca/managed-hook-install.lock, the one relay installers take for the same home). It is taken only by the outermost acquire, because the lock file is not reentrant and grants and trust rebases nest inside an install. Managed-home installs, the real-home install and opt-out sweep, and the legacy sweep all enter through it. Compare-and-swap on restore stays as the backstop. A lock that cannot be taken within its 10 s wait fails that install, which is already best effort: launch prep logs it, and the real-home lane falls back to the managed lane until its retry. * fix(codex): opening a terminal no longer strips the shared Codex entry from ~/.codex Every Orca instance on one HOME writes the same status-hook entry into the user's ~/.codex/hooks.json, with its trust in config.toml. Launch prep runs on every pane spawn, and under a managed Codex account it ran the legacy system sweep. That sweep matched Orca entries by script file name, so it removed the current shared entry and the trust blocks the grant ledger recorded. On a live laptop hooks.json went 4139 -> 18 bytes about 150 ms before a new pane opened. With hooks off, the real-home lane's launch prep swept the same way. Now nothing automatic removes the current entry or its trust: - The legacy sweep removes only an enumerated list of retired command forms that no build writes any more (#1019's double-quoted form, #1536's exec-guarded form, and Windows' per-userData bare path), plus their trust. - ensureRealHomeCodexHookState with hooks off writes nothing; that covers launch prep, session resume and startup. - Only the user's explicit opt-out (codexHookService.remove()) strips the entry and its ledger-recorded trust from the real home. - The sweep-suppression gate existed only to stop the sweep from deleting the current entry, so it is deleted with its main-process wiring. Startup with hooks off already skipped the real-home install; with this change the first pane's launch prep with hooks off also leaves ~/.codex untouched. * fix(codex): a pane's prepare-codex only repairs a home its own HOME's app installed On macOS a pane starts through login(1), so it gets the user's real HOME even when its Orca app runs with another one. The pane's `codex()` preflight installed hooks in the CLI process with that real HOME: it rewrote ~/.orca/agent-hooks/codex-hook.sh, promoted trust into the real config.toml, and wrote the real HOME's script path into the app's managed home. The preflight now acts only when the managed home's hooks already run this process's own shared script, which proves the app that installed them shares its HOME. Otherwise it writes nothing; the app installed the home at spawn. Why not a no-op: the preflight was added (#14326) because trust can go stale between opening a pane and typing `codex`, for example in a pane that survives an app update, and Codex then stops in hook review. For a same-HOME pane it still repairs that. Why keep promotion: the install drops runtime trust the system config does not back, so skipping promotion would delete approvals the user gave inside Orca-launched Codex. * test(agent-hooks): await every installer in the refresher coverage test The test fired each managed installer without awaiting it and read ~/.orca/agent-hooks straight after. Codex's install now takes the cross-process real-home lock before it writes its script, so the script landed after the read. Await the installers, and stub Codex's trust sessions so the awaited install cannot start a real `codex app-server`. * fix(codex): retire the two real-home command forms the list missed The real-home lane wrote two Codex hook forms into ~/.codex that no build writes any more and that the enumerated retired list did not name: - POSIX, #9501 until #10885: the file-guarded form draining with a bare `cat`. - Windows, #9501 until #10221 took Windows off the real-home lane: the encoded PowerShell launcher for a non-cmd-safe script path. The file-name sweep removed both before; the enumerated sweep left them in place, trusted, still passing the script's exit status to Codex. Both now match as frozen literals. Also corrects the startup ordering comment: the real-home install runs first so its in-slot upgrade lands before the managed install's sweep retires the prior command; nothing re-arms a legacy sweep any more. * fix(codex): take the real-home lock only when a write is needed The previous commit made every entry to the real-home config lane take the cross-process lock. That lane runs on every pane spawn and every typed `codex` preflight, so the steady state paid an owner probe (a `ps` spawn on macOS) and could wait up to 10 s behind another instance's trust session, even though it wrote nothing. Each real-home writer now compares the desired state with the files on disk first, without the lock. Only when a write is needed does it take the lock, re-read and recheck, then write: - real-home install: the planned hooks.json, the shared script and the ledger-recorded grant are compared; the locked path re-plans from disk. - legacy sweep: locks only when a retired entry is present; the sweep re-reads. - approval promotion: locks only when there is something to promote; the promotions are recomputed under the lock. - the shared ~/.orca/agent-hooks script: locks only when its bytes differ. The explicit opt-out always takes the lock. The lock is reentrant through async context, since grants and rebases nest inside an install, so the config-lane option the previous commit added is removed. * fix(codex): a shared script without its exec bit is not the steady state The compare-first check matched the shared ~/.orca/agent-hooks script on bytes alone. writeManagedScript also restores 0755 on every call, and the POSIX hook guard skips a script that is not executable, so a script whose mode was lost (a dotfiles restore, a plain copy) now stayed that way: every Codex hook drained stdin and reported nothing until an app restart refreshed the script. The check now also requires the mode the writer sets, so that case takes the lock and the write path repairs it. * test(codex): the retired encoded launcher never matches today's shared one The shared encoded Windows launcher is still current for other agents, so the comment claiming today's launcher is never encoded was wrong. What keeps the retired matcher off it is the exact payload: since #14825 the shared launcher prefixes its payload and drops -ExecutionPolicy Bypass. Pin that with a case. * fix(codex): the pane step recognises its own script under a home path with an apostrophe The same-HOME check looked for the script path wrapped in bare single quotes, but both hook writers escape an apostrophe inside the quotes. A home such as C:\Users\O'Brien never matched, so the pane-step repair never ran there. * fix(codex): the trust-RPC escape hatch still keeps the real home off its lane The no-write check reported a recorded grant as current, so with ORCA_DISABLE_CODEX_TRUST_RPC set the real-home lane stayed in use. The grant itself refuses before reading its ledger; the check now does the same. * fix(codex): the shared script write no longer waits on the real-home lock The write is atomic and skips identical bytes; waiting behind another instance's trust session could only fail a pane's managed-home install. * fix(codex): an in-Orca approval survives a launch that cannot get the real-home lock The install drops runtime trust the system config does not back, so a promotion skipped for want of the lock lost the approval for good. It now writes unlocked, as it did before the lock existed. * refactor(codex): take the cross-process real-home lock back out The lock fixed no observed failure. The three that were observed each have their own fix in this series: the legacy sweep matches only frozen retired command forms, hooks-off launch prep writes nothing, and a pane's prepare-codex repairs only a home its own HOME's app installed. The lock instead brought its own defects: a steady-state spawn waiting behind another instance's trust session, a compare-first split to avoid that, a script write and an approval promotion that could fail for want of the lock. Removed, with their tests: the real-home write lock and its async-context reentrancy, the plan/compare split that kept it off steady-state spawns, the compare-first legacy sweep, the locked approval promotion and its unlocked fallback, the compare-first shared script write (writeManagedScript already skips identical bytes and restores the exec bit), and the CLI tsconfig entries the lock pulled in. Kept: the retired-forms matcher, the hooks-off no-op, removal only on an explicit opt-out, the pane own-script check, and the compare-and-swap rollbacks. Every instance now writes identical bytes idempotently. * fix(codex): an opt-out that cannot read hooks.json keeps Orca's trust and ledger The opt-out swept the real-home entry, then dropped Orca's ledger-proven trust whenever a ledger existed, even when the sweep could not read hooks.json. The entry could still be there, now untrusted, and the ledger that proves ownership was gone for the retry. Drop that trust only after a sweep that read the file. * refactor(agent-hooks): one predicate for whether an agent's status hooks are on "Global switch on and this agent not turned off" was spelled out separately in the startup controls, the settings reconcile, the retained-home reconcile, the WSL preflight RPC, the CLI preflight and the OpenCode plugin selection. They now share one function, in a module light enough for the CLI's per-launch Codex preflight to load. The PTY spawn env derives the Codex flag from the switch and opt-out list it already carries, the same way it does for OpenCode and Pi, instead of receiving a second copy. * fix(codex): launch and resume prep honour Codex's per-agent hook opt-out Turning Codex off in the per-agent hook settings removes Orca's Codex hook entry, but launch prep and session resume read only the global hooks switch, so the next Codex launch or resume wrote the entry straight back into the real ~/.codex or the account's home. Both now read the per-agent predicate, which the PTY spawn env and startup already honoured. * fix(codex): turning Codex off per agent clears the real ~/.codex entry While the real-home lane owns ~/.codex/hooks.json, the legacy system-home sweep stands down. That gate read only the global switch, so turning Codex off per agent ran remove() with the sweep still suppressed and left Orca's entry in the real ~/.codex. The gate now reads the per-agent predicate, the same as turning every hook off. * test(codex): cover the system ~/.codex sweep gate for Codex turned off The gate that lets the legacy system-home sweep run was an inline closure in startup, so reverting it to the global switch left CI green. It is now a pure function beside the gate it feeds, with a table test and a remove() test on a seeded ~/.codex: turning Codex off strips Orca's entry and keeps user hooks; with Codex on the entry stays. * fix(cli): keep the agent-status hooks predicate loadable by the packaged CLI The CLI's prepare-codex handler imported the predicate from src/main, but the Electron build rebuilds out/main from its declared entries only, so the packaged `orca agent hooks` commands could not load it (package jobs and the CLI bundle-parity test were red). The predicate reads only settings, so it now lives in src/shared, which the CLI compiles itself. * feat(codex): every Orca build writes one frozen Codex hook command The Codex hook command was built from this build's wrapper, so two builds on one HOME disagreed about the bytes of the shared ~/.codex entry and kept rewriting it, with a Codex trust session each time. The command is now fixed per form and carries its form number: - POSIX: one command with no path in it. It runs the shared script only in an Orca pane with hooks on (pane key and hook port set), drains stdin everywhere else, and always exits 0. A branch for a per-build script root is written now and stays dormant until Orca sets ORCA_AGENT_HOOK_ROOT, so that change will not move these bytes. - Windows: the bare forward-slash path to the shared .cmd, which runs under PowerShell 7 and 5.1, Codex's hook hosts. A profile path that is not one PowerShell token gets a plain PowerShell form with the same branches. The literals live in the form module, so a change to the shared hook constants cannot move them; goldens pin the bytes. Every form keeps `agent-hooks/codex-hook.*` in plain text, so older builds still recognize it. * fix(codex): one main-process owner adds the real-home entry; nothing restores files Each Orca writer of ~/.codex decided what Orca's entry must be from its own build and instance, then removed or reverted whatever differed: launch prep rewrote any Orca-shaped entry to this build's command and stripped Orca entries from events this build does not use, and a failed trust session restored hooks.json and config.toml from snapshots. With several instances and builds on one HOME, every disagreement became a deletion or a revert. The main process is now the one writer, and its writes are add-only: - A launch or resume adds Orca's frozen entry to an event that has none and leaves every Orca entry it finds, so a running older build is never fought. - App start also converts an older Orca form to the frozen command, once, in its own slot: one hooks.json write (one .bak) and one trust grant per home. - A newer form is never rewritten or appended beside, and Orca entries in events this build does not use are kept. - After a failed trust grant, only an entry this call wrote that is still untrusted is withdrawn, putting back the handler it replaced. Both files are re-read, so a concurrent edit, or the identical entry another Orca trusted meanwhile, survives. Deleted: the compare-and-swap hooks.json restore, the config.toml snapshot restore after a grant session and after a user-trust re-key, and the rollback module. A grant session writes trust only at Orca's own keys, and every caller settles those keys itself. A failed re-key of moved user hooks now keeps the write and reports it; Codex lists those hooks for review. * fix(codex): the pane CLI asks the app to prepare its Codex home `orca agent hooks prepare-codex` ran Codex's install inside the pane. That process can have the real HOME (login(1)) and runs outside the app's in-process queues, so it was a second writer of ~/.codex and ~/.orca beside the app. A check that the home ran "its own script" guarded it. The pane step now only asks the app, over the same kind of local RPC the WSL pane step already uses (agentHooks.prepareCodexForPane). The app checks that the pane's CODEX_HOME is one its own userData owns, reads its own hooks setting, and installs on its own queue. An app that is not running, or is too old to know the method, makes the step a no-op, as it is on WSL. The own-script check and the CLI's settings read are gone, and the preflight module leaves the CLI bundle. * fix(codex): delete the pane step on native hosts The previous commit had `orca agent hooks prepare-codex` ask the app to prepare the pane's Codex home. The case it existed for (#14326, a pane that survives an app update with stale hook trust) did not reproduce, and no other desktop agent host writes agent config from a terminal or launch wrapper. - Deleted: the agentHooks.prepareCodexForPane RPC method, its params and catalog entry, and prepareManagedCodexHomeBeforeShellLaunch with its module, tests and CLI build entry. - `agent hooks prepare-codex` is a no-op on native hosts. It stays for one release so shell wrappers from older builds, which still call it, exit 0. - WSL panes are unchanged: they still ask the app over agentHooks.prepareCodexForWslPane. The shell wrappers and ORCA_CODEX_LAUNCH_PREFLIGHT stay, because WSL panes use the same wrappers and variable (forwarded through WSLENV). A native pane still starts the CLI once per `codex` it runs; skipping that is a follow-up. * test(codex): a failed trust session keeps concurrent edits to both files QA case 9 at host level, on a real file system in a temp HOME: Codex's trust session fails after another writer saved hooks.json and config.toml. - Both saves survive, and no Orca entry is left that Codex would list for review: this call's entry is withdrawn. - A failed one-time conversion puts the older Orca entry back in its slot and keeps both saves. Both tests fail on the previous head, which restored config.toml from a snapshot and left the untrusted entries in hooks.json. Removing the withdrawal turns both red. * feat(codex): read whether an Orca entry's stored trust is still current A Codex release that changes how it hashes a hook leaves Orca's stored trust stale: the entry is present, but Codex lists it as modified. Checking only whether the entry is missing cannot see that. readOrcaEntryTrust sorts a present entry into four states: - trusted: the stored hash is the current one; - untrusted: there is no stored hash; - stale: the stored hash is not the current one; - disabled: the user turned the entry off. The caller can pass Codex's current hash, for example one a grant recorded. The failed-grant withdrawal now uses it, and also keeps an entry the user turned off. Nothing re-grants on 'stale' yet. * fix(codex): a slow Codex start retries on the next launch, never for minutes On a loaded Mac a cold `codex app-server` took over 10 s (QA case 4). The grant timed out, the entry was withdrawn, and a 5-minute cooldown in both the grant and the real-home install then refused every retry. - The native session deadline is 30 s, the same as WSL's. - A timeout starts no cooldown in the grant or in the real-home install. The next launch retries. Other failures keep their cooldown. - Launches that queue behind a slow session share one follow-up run, so a launch waits for at most two sessions, not one per earlier launch. Tests: a 15 s cold start still grants and keeps the entry; after a timeout, the next launch runs a session at once; four queued launches run two sessions. Each is red on the previous head, and each mechanism was removed in turn to confirm its test turns red. * fix(codex): Orca's automatic writes never move a user hook Codex keys a hook's trust by its position in hooks.json. App start's collapse of Orca duplicates removed every Orca entry and appended one at the end. That moved any user hook that followed a removed entry, so the write waited on a session to re-key the moved hook's trust. App start now: - converts the first Orca entry that sits in a plain slot to the frozen command, in place; - drops any other Orca entry only when that moves no user hook; - keeps a duplicate that a user hook follows, and trusts every frozen copy, so none is listed for review; - appends only when no frozen entry is left. Tests check user positions and user trust blocks byte-for-byte for each automatic write: add-missing (append), the one-time conversion (in place), a trailing duplicate, a duplicate before a user hook, and older duplicates normalized to one entry. The three collapse cases fail on the previous head. Removing the position check, or the in-place conversion, turns its tests red. Only the explicit opt-out still removes an entry that user hooks follow. * fix(codex): removing an Orca entry never waits on a Codex session Removing an Orca entry from ~/.codex/hooks.json moves every user hook behind it up a slot, and Codex keys trust by slot. The retired-form sweep, the opt-out and a failed-grant withdrawal all asked a `codex app-server` session to list the old trust before writing, and to re-key it afterwards. A timeout there threw before the write and latched a 5-minute cooldown, so a slow cold start blocked the retired-form sweep at boot (QA case 4). Each moved hook's [hooks.state] block now moves to its new key, body bytes unchanged, straight after the hooks.json write. Codex hashes a hook's content, not its position or its file path, so the moved block stays exactly as valid as it was: a trusted hook stays trusted, an untrusted one stays untrusted, and one the user turned off stays off. No removal waits on or depends on a session. A failed config.toml write keeps the hooks write and logs. Deleted: the inspect and repair sessions, their client, and their cooldown. The generation guards on the hooks.json writes stay, for other processes. Tests: the retired sweep removes the retired entry and carries the trust of the user hook behind it while every Codex session times out (red on the previous head); the opt-out carries an appended user hook's trust; the move carries trusted, disabled and untrusted states byte for byte. Removing the move turns all of them red. * fix(codex): a Codex launch never waits on Codex's approval of Orca's entry A launch on the real-home lane awaited Codex's trust grant for the entry it had just added. A cold `codex app-server` on a loaded Mac took over 10 s, so the launch could wait that long, and a failure then latched a 5-minute cooldown. - Codex's approval runs in the background, with a 30 s cold-start budget. - A launch uses the real home only when the ledger shows trust is already current. Otherwise it goes to the managed home at once, and the next launch picks up the finished grant. - A launch that arrives while a grant runs does no work and does not queue behind it. - A resume into the real home has no managed home to fall back to. It waits for the grant, but no longer than the 10 s a launch always could. - A background grant that times out starts no cooldown; the next launch retries. Any other failure backs off for 10 s instead of 5 minutes. Success is what the ledger remembers. - A failed grant still withdraws only what that install added and is still unapproved. The log now says how many entries it took back and when the next try comes. Managed-home grants keep their 10 s deadline and stay on launch prep, as before; they fall back to Orca-computed trust. Tests: - A 15 s start: the launch returns in under a second on the managed home, a second launch starts no session, the grant lands in the background, and the next launch uses the real home. - A timeout sets no cooldown, withdraws its adds and logs it. - Another failure retries after 10 s, not before. - A resume waits only as long as allowed. - Case 9 checks the log line and the retry. Making the launch await the grant, a 10 s budget, either timeout cooldown, and a 5-minute backoff were each tried, and each turns its test red. * fix(codex): move a hook's trust only when every stored key has the known shape Orca now edits Codex's trust store directly when a removal moves a user hook. Three safeguards keep that honest: - Fail safe. If any [hooks.state] key in config.toml does not have the shape `<path>:<event>:<group>:<handler>`, nothing moves and Codex asks the user to review. That shape was checked unchanged from Codex 0.141 to 0.158. - Targeted. The file is read immediately before the atomic rename, and only the moved keys' blocks change. Every other byte stays, and no snapshot is restored. - Verbatim. Each block's body moves as Codex wrote it, including fields Orca does not know. No hash is ever computed, and a hook with no block gets none. Tests: - An unknown key shape stops every move. - Everything except the moved block survives byte for byte, and the moved body keeps an unknown field. - In case 9, a hook the user approved during the failed session keeps its approval when the withdrawal moves it, beside the concurrent project edit. Removing the shape check, or writing a computed block instead of the stored body, turns these tests red. * refactor(codex): keep only the trust read the failed-grant withdrawal uses A capture across Codex 0.141, 0.150 and 0.158, switching in all six directions, showed Orca's entry keeps the same hash and stays trusted. A Codex upgrade does not make its trust stale, so nothing needs to re-grant on staleness. readOrcaEntryTrust keeps the four states the withdrawal needs, but loses the parameter that let a caller pass a different current hash, and the test for a Codex that hashes differently. * fix(codex): native panes no longer start the Orca CLI before each codex The pane step is a no-op on native hosts, but native panes still carried ORCA_CODEX_LAUNCH_PREFLIGHT, so every `codex` typed in a pane started the Orca CLI for nothing. Only a packaged Windows build's WSL pane now gets the variable; the app prepares every native Codex home itself. The resolver loses the dev-launcher path and its userDataPath option, which only native panes used. Tests: a native macOS, Linux and Windows pane gets no preflight, packaged or not, even with the bundled CLI present; a WSL pane still gets the verified absolute launcher. Letting native panes through again turns them red. * chore(cli): say when the native prepare-codex no-op can go Native pane wrappers from builds up to v1.4.216 still call it. It can be deleted once no supported build's wrapper does. * test(codex): check the WSL launcher path instead of asserting it * fix(codex): a launch no longer waits behind the background real-home approval The background grant ran its whole codex app-server session inside the shared ~/.codex/config.toml lane, and on a cold host its session was also the shared capability probe. A launch sent to the managed home then waited on both: the managed install and the project-trust write queue on that lane, and the managed install's own grant waited for the probe. On a cold app-server that was up to 30 s per launch. The lane was held across the session only to protect the retired capture-and-restore. Codex writes its own records, so the lane is now taken only around Orca's own pre-grant write. The background grant runs its session without publishing it as the shared probe, and the whole grant is bounded by its deadline, so a hang outside the session cannot leave the lane 'granting'. * fix(codex): a failed re-grant no longer strips Codex's own approval of Orca's entries Before each trust session, the grant deleted every Orca record whose hash matched the one Orca computes. That exists because a managed home's fallback writes Orca-computed trust under both Windows path-separator spellings, and Codex rewrites only its own spelling, so the other copy would linger. On failure the managed and WSL fallbacks write that trust back, and before this fold a snapshot restore covered it. The real ~/.codex has neither: Orca never writes computed trust there (the real-home lane does not run on Windows at all), so a matching record there is Codex's own approval. After a ledger miss (another Orca profile, a Codex update, a lost ledger) and a failed session, nothing put it back, and every Orca entry showed "Hooks need review". The clear now runs only for homes whose fallback writes that trust. * fix(codex): a real-home resume spawns only once Orca's entry is approved or withdrawn A resume that must run in ~/.codex waited at most 10 s for the background approval, then spawned anyway. On a cold app-server that left Codex beside an unapproved Orca entry, so the resumed pane showed hook review. The resume now waits for the grant to settle. Settled means Codex approved the entry, or the grant failed and withdrew its own unapproved write; the grant's deadline bounds the wait (30 s, the cold-start budget), and a failed approval never fails the resume. Why this over the alternatives: - Spawning at 10 s keeps the review prompt this fold exists to remove. - Withdrawing at 10 s from the resume races the still-running session: Codex can write the frozen entry's hash after the withdrawal, and for a converted entry that marks the older command Orca put back as modified. - A resume cannot use the managed home: the session lives in ~/.codex. So the only states that cannot race Codex are the grant's own settle. The cost is a longer worst case on a cold app-server (up to the 30 s deadline, plus any managed-home install that holds the config.toml lane); a warm approval takes seconds, and an approved entry costs no wait. * fix(codex): keep the 5-minute trust cooldown for launch-path grants The fold shortened the host's trust-grant cooldown from 5 minutes to 10 seconds for every grant. That was meant for the background ~/.codex approval, which blocks no launch. The managed-home and WSL grants run inline on the launch path, so with a hung app-server every launch more than 10 s after the last failure paid the full inline timeout again (10 s native, 30 s WSL). Cooldowns are now kept per lane: inline grants keep 5 minutes, the background grant retries after 10 s, and neither lane's failure cools the other down. A success, or a proven-missing surface, still clears both. The real-home install's own retries (an unreadable hooks.json, unknown keys) are back on the 5-minute interval they had before the fold. The cooldown moves to its own module so the grant stays within the file limit. * fix(codex): a failed grant withdraws the exact copy it wrote The withdrawal re-found "this call's" entry by command, taking the first frozen handler in the event. When app start converted a later slot while an earlier frozen copy sat in a matcher group (which conversion skips), a failed grant acted on that earlier copy: it put the older command into it, or skipped it, and left the converted, unapproved copy in place. Each write now records where its handler landed, after any duplicate drops, and the withdrawal acts only on that slot. A copy that has since moved is left alone; the next launch's grant retries it. * fix(codex): the failed-grant withdrawal checks hooks.json is unchanged before writing The install and the retired-form sweep both refuse to replace ~/.codex/hooks.json if it changed since they read it. The withdrawal did not: a save landing between its read and its atomic replace was lost. The window is small, since the withdrawal is synchronous, but it now carries the same guard. * refactor(codex): drop rationale left over from the snapshot restore; name the trust-move module for what it does Comments on the config.toml lanes still justified them by a grant's capture-and-restore window, which the fold deleted, and the trust-write deadline still counted a grant session holding the lane. They now give the reason that remains: Orca's own multi-step reads and writes, and managed-home installs that hold the lane across their inline grant. codex-user-hook-trust-rebase no longer rebases through Codex; it moves stored trust records, so it is now codex-user-hook-trust-moves. The grant test that pinned two sessions on one config.toml to run one at a time is removed: its reason was an interleaved capture and restore. Callers that write config.toml around a grant hold their own lane, which the nested installer test still covers. * build(cli): list the trust-grant cooldown module in the CLI program The CLI's agent-hooks handler loads the hook controls, which reach the Codex trust grant; the CLI project is composite, so every module in that graph must be listed. * docs(codex): say which Windows hosts each hook command form runs under Codex runs a hook under the turn's shell (PowerShell 7 or 5.1 in every captured session) and, with no single local turn shell, under %COMSPEC% /C. The bare forward-slash path ran under all three in the Windows host census. The PowerShell form used for a profile path with a space does not parse under cmd.exe; no form valid in all three hosts has been run for such a path, so the form stays and the gap is stated here and in the PR. * test(codex): type the withdrawal seam without an assertion * fix(codex): a real-home resume starts at once, trusting Orca's entries for that process A resume that must run in ~/.codex waited for Codex's background approval of Orca's newly written hook entry: up to 30-40 s on a cold app-server. That made the user's resume wait on bookkeeping, and the alternatives (start at 10 s with Codex's hook review showing, or withdraw the entry and race Codex's own write) were worse. Codex reads hook trust from its session-flag config layer as well as the user's config.toml, merged per key, and has since hook trust shipped. So the resume no longer waits. When Orca's own frozen entries in ~/.codex are untrusted (or hold a stale hash), the resume command carries `-c hooks.state={'<key>'={trusted_hash='<hash>'},...}` for exactly those entries: the key under both the logical and the real path of ~/.codex (Codex keys an explicit CODEX_HOME by its real path), and the hash of that entry's content, so it can trust nothing else at that slot. The user's hooks are never included, nothing is written, and the background approval still runs for later plain `codex` launches. An approved entry adds nothing; a Codex known to lack hook trust gets nothing. One inline table, because Codex splits a `-c` key on every `.` and the key holds `.codex/hooks.json`. TOML literal strings keep `"` out of Windows native-argument quoting. The flag goes before `resume <id>`, quoted for the pane's shell (portable Unix, PowerShell or cmd), in the launch command and in the setup-sequenced copy of it; a cmd line whose path cmd would expand, or a key with an apostrophe, is left unchanged. SSH and WSL resumes get no preparation, so no local path reaches them. * Revert "fix(codex): a real-home resume starts at once, trusting Orca's entries for that process" This reverts commit |
||
|
|
28d617d0d0 |
fix: close abandoned PDF.js development asset streams (#23054)
* fix: close abandoned PDF.js development asset streams * test: keep casting safety comments attached after formatting * fix(i18n): restore diff note draft catalog entries * fix: guard closed PDF streams and preserve foreign listeners * fix(i18n): make AI the recipient of diff notes * test(browser): report the phase of WebRTC probe timeouts * test: pin the historical hourly package version fixture * test: provide the historical package version through build options --------- Co-authored-by: OrcaWin <alpha-eng@stably.ai> Co-authored-by: m4air <m4air@m4airs-Air.localdomain> |
||
|
|
8416e8de10 |
refactor(persistence): retire ordinary JSON profile writes (#23202)
* refactor(persistence): retire ordinary JSON profile writes Require SQLite for writable profiles and keep import, compatibility export, and recovery in a documented legacy-json boundary. * fix(cli): preserve dynamic profile imports in release output * test(persistence): exercise SQL races and verify packaged CLI imports * test(persistence): consolidate shared fixture imports * test(persistence): close SQLite fixtures before cleanup and await launcher output * test(automations): use SQLite fixtures for dispatch fencing and skip coalescing --------- Co-authored-by: m4air <m4air@m4airs-Air.localdomain> |
||
|
|
82412dab8b |
Persist profile state in SQLite with background writes (#22612)
Migrate profile state to SQLite and move writes and backups into a background worker. Acknowledge terminal, SSH and automation changes only after durable saves. Preserve JSON import, recovery, rollback and compatibility exports. Validate migration, worker failures, maintenance, cross-profile moves and terminal lifetime races with unit, integration and end-to-end coverage. |
||
|
|
54a19c2ba3 |
fix(pdf): render CJK text with pdf.js resources
Merged after PR-specific checks passed. CI failures are unrelated baseline findings in ClientHostedBrowserPagePane.markup.test.tsx and pane-title-update-global-scan-budget.test.tsx. |
||
|
|
631b51f508 |
perf(usage): run the Claude/Codex/OpenCode usage scans on a worker thread (#21114)
* perf(codex-usage): resume rollout scans at the last parsed byte Codex rollout files are append-only and grow all day, but any append changed both mtime and size, so `canReuse` discarded the cached entry and the scanner re-read the whole file from byte 0 on the Electron main process. On one real corpus that was 6.59 GB re-read per cycle across 26.63 GB / 21,110 files. Each parsed file now persists a resume point: the offset just past the last newline-terminated line, the parse context at that offset (session id, cwd, model, running totals), a sha256 of the 4 KiB before it, and the file's dev:ino. A grown file resumes there and merges the appended rollup into the cached one; anything unproven falls back to a full reparse — truncation, an in-place rewrite, rotation, a counted tail with no trailing newline, a legacy copied-session suffix offset, or a file that must reclaim deferred fork claims. Resume never depends on mtime equality, so a coarse-mtime filesystem cannot hide an append. Fixture: a 75,737-byte rollout with a 758-byte append re-read 76,495 bytes before and 8,950 after (the append plus two bounded 4 KiB boundary windows). Also bounds the automation-attribution force predicate for both Codex and Claude: it keyed on `lastScanError`, so a persistently failing scan forced a fresh full rescan on every single lookup. It now keys on the most recent scan attempt, which is one forced scan per run regardless of outcome. * perf(usage): run the Claude/Codex/OpenCode usage scans on a worker thread The three first-party usage scans walk whole rollout and transcript corpora and read OpenCode's SQLite synchronously, all on the Electron main process. They rarely produce a long stall — the JSONL reader streams, so it yields to the loop between chunks — but they pin the main-process event loop at ~95% utilization for the scan's whole duration, which is what every IPC message, timer and window event then queues behind. Move that work to one lazily-spawned, unref'd worker thread shared by all three providers, following the OpenCode SQLite scanner precedent (#8864). Measured on a synthetic 4,000-rollout corpus (25.8 MB cache): a cold scan drops from 2,147 ms of main-thread time to 31 ms, and a steady-state incremental scan from 165 ms to 64 ms. The worker is stateless and the cache crosses the boundary both ways. That costs ~64 ms of structured clone at this corpus size, against 2,147 ms saved on the cold path, and it keeps the persisted cache the single source of truth — a worker-owned copy would need an invalidation protocol and a second resident copy of the same multi-MB array. Failure is closed, never a silent empty result: a worker that cannot spawn, times out, or crash-loops rejects, and the store records the scan error and keeps the previous projection. Two clients already carried the same FIFO/timeout/crash-cap machinery, so extract it once as WorkerThreadRequestQueue (with the packaged entry-path resolver as worker-thread-entry-path) and move all three onto it, rather than adding a third copy. Their existing tests pass unchanged. The oracle is event-loop utilization on the calling thread, not a stopwatch: usage-scan-worker-event-loop.test.ts runs the same scan both ways and asserts the worker leg leaves the caller idle while the main-thread leg does not, so CI load moves both legs together (#18788). * test(usage): compare the two scan arms instead of two fixed thresholds The event-loop oracle claimed to be self-calibrating — its header said "the ratio is self-calibrating, so CI load moves both legs together (#18788) instead of tipping a fixed millisecond threshold." It computed no ratio. Two separate `it()` blocks each asserted an absolute threshold against its own arm, run separately, so load moved them independently. The comment described a test nobody wrote, and the flake it promised was impossible is the one that landed: `activeRatio > 0.8` on the calling-thread arm measured 0.764 on an ubuntu runner. Fixing the comment is not enough, because the fraction is the wrong quantity. CPU contention drags the calling-thread arm's active/wall fraction *down* toward the worker's, since the loop parks waiting on a contended libuv pool. A 4-vCPU Linux container measured that arm at 0.175-0.756 across twenty runs, idle and loaded — never once above 0.8. Active *milliseconds* move the other way: contention stretches the caller's JS time far more than it stretches the worker arm's fixed post-and-deserialize cost, so the gap widens under load. Merge the two arms into one case over one corpus and assert the worker arm costs the caller under a fifth of the inline arm's active milliseconds. Same twenty Linux runs: 10.9x-83.6x, passing throughout. Keep the presence preconditions on both arms — an arm that silently scanned nothing satisfies the comparison trivially — and extend them to the calling-thread arm, which previously checked only file and session counts. * fix(ports): name the dropped command when the probe queue is full The shared-queue extraction turned `Port scan command queue is full; dropped ${command}.` into a constant string, because `describeFull` was given no way to see the request. Pile-up is per-probe, so the name is the only thing in that log that identifies which of lsof/ps/netstat was shed. Pass the rejected request to `describeFull` and restore the name. The request is built before the cap check so it exists to be named; the id it burns is a correlation token, so a gap costs nothing. The existing overflow test asserted only the error class, which is why the regression escaped a 29-test suite. It now dispatches the overflow under a different command than the accepted ones and asserts the message text, so a message that names the wrong request fails too. Also add a direct WorkerThreadRequestQueue test. Three subsystems share the queue and each client test only sees the parts its own protocol exercises, with `queueCap` reachable from port-scan alone. Covers one-at-a-time FIFO dispatch, the deadline starting at dispatch rather than enqueue, the consecutive-death cap, and both points where that count clears. And record the child-process hazard at the usage worker entry. `terminate()` reaps nothing the thread spawned, and OpenCode discovery reaches a fork today: `wslGated*` forks the WSL transcript sidecar for a `\\wsl$\...` path, which a Windows `OPENCODE_DB` or `XDG_DATA_HOME` can be. One scan through that entry with a UNC `OPENCODE_DB` forked a sidecar that outlived `terminate()`. * test(ai-vault): assert the OpenCode worker messages exactly, not by fragment Checked every message string in the two clients the shared-queue extraction rewrote against origin/main. Only the port-scan queue-full one regressed (fixed in the previous commit); the OpenCode SQLite client's four messages render identically, the remaining source diffs being renames — `error.message` to `lastError`, `call.timeoutMs` and `CALL_DEADLINE_MS` to `timeoutMs`. `session-scanner-worker-client.ts` was not touched by the extraction. But its suite could not have caught it either. `/timed out/`, `/exited with code/` and a bare `rejects.toThrow()` all still match a message that has lost its interpolated value, which is the same blind spot that let the port-scan regression through. Assert the rendered text instead: the timeout names its deadline, the exit names its code, and the crash-loop drain still carries the text of the fault that killed the run. * fix(usage): correct the worker entry's child-process note The previous note said `worker.terminate()` leaves a forked sidecar orphaned. It does not, and the reproduction that appeared to show it used a stub sidecar missing the `process.on('disconnect', () => process.exit(0))` the real entry has. With a faithful one: the sidecar lives exactly as long as the thread and is gone within 2s of `terminate()`, because tearing the thread down closes the IPC channel it owned. Two worker lifecycles forked two sidecars and leaked neither, and the pre-worker main-thread path reaps its sidecar the same way, on host exit. What is true and worth recording: a fork is reachable from this bundle at all, which is easy to miss; it survives only as long as the channel does; and the sidecar is now re-forked per worker lifecycle instead of pooled for the app's life. State those, and warn that a future child which does not exit on channel close would not get the same free cleanup. * fix(usage): kill a wedged scan worker on no progress, not on wall clock `USAGE_SCAN_TIMEOUT_MS` was a 10-minute deadline on the whole scan. A cold scan of a real history is legitimately minutes — 637 s measured on a 30 GB corpus with 300 worktrees before the per-cwd memo, ~51 s after — so a larger corpus or a slower disk crosses it. Crossing it killed the worker, recorded a scan error and left the cache unadvanced, so the next refresh started cold and died at the same point, forever. The deadline is now a no-progress window. The worker posts a file counter as it walks the corpus (`UsageScanWorkerProgress`, rate-limited to one message a second), and `WorkerThreadRequestQueue` re-arms the active call's timer on each one via the new optional `isProgress`. Clients that do not pass it keep the plain wall-clock deadline. `MAX_CONSECUTIVE_DEATHS` and idle teardown are unchanged. * refactor(usage): report scan progress as a file count, not one call per file Claude's scanner walks batches, so a per-file callback made it loop just to bump a counter. |
||
|
|
26721bd632 |
fix(codex): stop blocking the main thread on trust grants (#16441) (#16594)
* fix(codex): stop blocking the main thread on trust grants (#16441) Codex hook trust was granted by blocking the Electron main thread on `spawnSync` of a bundled ELECTRON_RUN_AS_NODE entry for the whole app-server deadline: 15s native, 35s WSL, ~45s on the real-home path (rebase inspect + repair + grant). Cold start and every Codex pane launch showed "Not Responding"; the reported event-loop gap was 15,049 ms. The subprocess only ever existed to donate an event loop to a deliberately blocked parent — `runCodexHookTrustGrantSession` was already the real async implementation. Make the callers async and the fork is unnecessary, so the bridge, the forked entry and its envelope are deleted along with their build/knip/tsconfig registrations. The CLI `agent hooks prepare-codex` handler is already async, so it awaits the in-process session and saves a process spawn per managed-home shell. `resolveCodexTrustGrantHost` is async too; the WSL identity probe moves from `execFileSync` to `runProcess`, dropping that file from the child-process import allowlist. Status reads keep a synchronous native-only stamp path. Two invariants that held only because the lane blocked: - Overlapping capability probes were impossible by construction. `GitCapabilityCache`'s dedupe engine is extracted to a shared `CapabilityProbeCache` and `CodexAppServerCapabilityCache` now inherits it, so concurrent launches against a cold host share one app-server session instead of one each. - Two grants on one `config.toml` could not interleave capture and restore. A reentrant per-file lane now serializes the whole install sequence (managed, WSL runtime, real-home ensure, legacy sweep) and the grant and rebase inside it. Cold-start work moves off the critical path: retained-home reconciliation (N sequential sessions) is fire-and-forget behind the daemon provider, and the startup real-home ensure chains into managed hook reconciliation instead of blocking app init. Every preserved semantic is unchanged: never throws, the ORCA_DISABLE_CODEX_TRUST_RPC kill switch, ledger hits, backfill-pending and cooldown fallbacks, config rollback on every failure path, pre-grant self-computed trust removal, the verify-failure taxonomy, diagnostics and telemetry. * fix(codex): widen the trust-config lane to every config.toml writer Review follow-ups on #16441's async trust grant: - `markCodexProjectTrusted` now runs inside the runtime+system config.toml lanes, so a project-trust write can no longer land inside a hook grant's capture->restore window and be silently reverted. Its callers await it. - `install`/`refreshRuntimeUserHooks`/`remove` hold the system config.toml lane as well as the runtime one — they promote approvals into ~/.codex/config.toml and mirror it back. Lock order is runtime-before-system everywhere. - The real-home ensure chain resumes after a rejection instead of returning the same rejected promise to every later pane launch, and resolving the real home is now inside the module's never-throws boundary. - `buildSpawnEnv` awaits inside a cancelable pending-spawn registration, so shutdown during the (now long) env build stops the PTY from launching. `prepareLocalPtySpawn` generalizes into `awaitCancelableLocalPtySpawn`. - CapabilityProbeCache drops the test-only `nowMs` passthrough; its probe backstop comment now describes what it actually guards. - Preflight is a plain async function; the trust dispatch in orca-runtime collapses into one `markWorkspaceTrustedForAgent`. * test(codex): exercise the trust-config lane under real concurrency The async grant makes two pane launches overlap for the first time. These drive the real modules end to end on real files: a rollback swallowing a sibling's grant, a markCodexProjectTrusted write landing inside a capture -> restore window, shared capability-probe dedupe on a cold host, the host-scoped transient cooldown, and reentrancy from inside an installer. Each was verified to fail against a deliberately broken implementation (lane removed, dedupe disabled, cooldown made global, reentrancy pass- through disabled). * test(codex): stop hook-service suites spawning the developer's real codex The forked grant bundle never existed under vitest, so the RPC lane was unreachable in tests on main. Running it in-process makes these suites spawn a real `codex app-server` when one is installed: 38 spawns and two failures in hook-service-runtime-trust-repair on a machine with codex, green in CI where there is none. Stand in for the missing binary so both environments exercise the same fallback lane. * docs(codex): scope the trust-RPC kill switch comment to what it actually gates The comment read as though the flag forces the fallback lane everywhere. It gates the managed grant only: the real-home rebase still runs its own inspect/repair app-server sessions when Orca's insertion shifts a user's hook positions, and never reads the flag. Verified by exercise, not by reading — with the flag set, both inspect-user-hook-trust and repair-user-hook-trust still ran. Pre-existing: main has no check there either, it just blocked the main thread while doing it. Widening the flag to cover the rebase is a follow-up; this only stops the comment promising something the constant does not do. |
||
|
|
4ec6bbf588 |
Kill hung WSL transcript filesystem operations via child process with route quarantine (#15381)
* fix(native-chat): kill hung WSL operations via child process
Stalled UNC file operations hold libuv permits even after the gate
timeout expires, blocking Chat tab recovery. Two stalled operations
fill both permits and freeze all WSL access until restart.
Fork file I/O for UNC paths into a separate child process. On deadline
expiry, kill the process to force the hung syscall to exit. This frees
the permit for the affected tab's next read. Temporarily quarantine the
stalled route to avoid retry storms.
* chore: drop internal review artifact from the repo root
* fix(native-chat): harden the WSL transcript fs sidecar
Review follow-ups on the sidecar isolation change:
- Only the deadline may abort running gate work. The sole waiter's
same-duration timeout fired first, killed healthy children on caller
abandonment, and settled the task before the deadline could quarantine
a stalled route - leaving the back-off dead for every dedupe:false op.
- Resolve the fork entry from out/main/chunks too: the resolver compiles
into a shared chunk, and the scanner service child has no
process.resourcesPath, so packaged WSL vault scans threw entry-not-found
(masked as an empty tree).
- Allowlist the fork env instead of spreading process.env; ambient
NODE_OPTIONS would halt or --require code into every child.
- Wrap transport faults (spawn failure, child death) in
WslTranscriptFsError('unavailable') so discovery reports them as scan
issues instead of misreading them as missing paths or empty trees.
- Gate the vitest in-process fallback on the vitest worker global so a
leaked VITEST=true cannot revert production to in-process UNC syscalls.
- Reap idle sidecar processes after 60s instead of holding them for the
app session.
- Split 'open' into its own protocol union member so the reusable-call
Exclude actually strips it from the pooled-process API.
- Guard kill('SIGKILL') against the teardown race where an exiting child
emits an unlistened 'error', and dispatch reads by handle kind before
path spelling.
* fix(native-chat): probe stalled WSL routes instead of a fixed quarantine
Remaining review follow-ups:
- Escalating route quarantine: first strike lifts after 5s so a distro
that was cold-booting when its op hit the deadline recovers on the
next poll (~35s total instead of ~90s); repeat stalls double the
back-off toward the prior 2x-timeout cap, and any settle the deadline
did not force clears the strikes. Queued same-route tasks fail fast
at quarantine instead of stranding one waiter deadline per file in
sequential scans.
- Single request implementation: the vitest in-process fallback now runs
the child's own dispatcher (WslTranscriptFsProcessOperations + decode),
so unit suites exercise exactly what the forked process executes and
the per-call-site fallback closures are gone. Dirent fixtures gained
the full kind-flag set the serializer reads.
- Dropped the production-dead per-route close queue; UNC FileHandles
(test fallback only) mirror the process-handle close contract.
- Error class, messages, and factories move to wsl-transcript-fs-error
(re-exported from the gate) to keep the gate under the lines budget.
* fix(native-chat): harden WSL transcript fs with route quarantine strike
Extract quarantine logic into a dedicated module with strike decay: stalls older
than 5 minutes restart from base back-off, and concurrent-lane timeouts count as
one incident. Allow joining live in-flight tasks on quarantined routes (they cost
no new I/O). Preserve quarantine across transport faults (child death). Handle
file shrinking during tail reads by detecting short reads and returning empty.
Defer file closes that arrive mid-read instead of refusing, preventing slot
leaks. Separate process slot and boundary-finding concerns into focused modules.
* fix(native-chat): enforce route quarantine windows and isolate lanes per
A late result arriving after the deadline was incorrectly lifting the route
quarantine, allowing subsequent work to start before the back-off period
expired. Now late results are correctly recognized as stale and never cut
the quarantine short.
Process work is now isolated per (route, priority) lane so a scan stall
cannot block exact reads on the same distro. Each lane gets its own client
and process pool; late results and handle faults stay scoped to their lane.
Tests now fake performance.now() alongside timers (the quarantine clock
depends on it) and wait for the full back-off window to expire rather than
advancing by 0. Gate state is reset between test cases since late releases
never lift the quarantine.
---------
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
|
||
|
|
4c69e45552 |
Strengthen plain-node-entry-guard with entry name validation (#12761)
* Strengthen plain-node-entry-guard with entry name validation - Add buildStart hook to validate guarded entry names exist in rollup inputs, preventing stale names from silently stopping guards - Extend electron require detection to subpaths (electron/main, etc) - Improve smoke test signal/exit handling and use constants - Add comprehensive tests for entry validation and new behaviors * Add SIGKILL escalation to plain-node-entry-guard timeout Switch from spawnSync to async spawn to properly handle daemons that trap SIGTERM. spawnSync's timeout only sends the signal and waits, so a daemon that ignores SIGTERM causes the build to hang. The new runDaemonEntry function escalates to SIGKILL after a grace period to enforce the deadline. Configurable timeouts and grace periods via SmokeTimings type; closeBundle hook becomes async to support the change. |
||
|
|
5df2ddbc9c |
perf(ai-vault): isolate tab title resolution (#13377)
* perf(ai-vault): isolate tab title resolution * fix(ai-vault): preserve background scan caches * fix(ai-vault): resolve nested worker from chunks |
||
|
|
fde816e4ee | move folders (#12758) |