Codex's own state-DB metadata backfill is one-shot, so rollouts hardlinked in
by Orca's session backfill never become visible to Codex's DB-driven surfaces.
Extract the app-server stdio JSONL transport into codex-app-server-session
(shared with the trust-grant client) and add a bounded, resumable background
pass that drives Codex's lazy indexing via thread/read per backfilled session:
recent-first, batched onto one short-lived server per batch with small
concurrency, ledger + marker so steady-state startups are a no-op, stop-aware
on quit, and capability-aware on CLIs without the app-server surface.
Backfilled/bridged rollouts are hardlinked into both the real ~/.codex and
Orca's managed runtime home, so AI Vault listed each session once per root
(#7521). Dedup candidates by rollout file name pre-parse and parsed sessions
by session id post-parse, keeping the canonical root: host real home first
(unprefixed resume), then the managed runtime home, then other homes. Applies
to local, WSL, and SSH-remote scans.
The grant-lane stub simulated codex by calling Orca's upsertHookTrustEntries,
which writes both separator variants for a Windows key (a fallback-lane compat
shim real codex never does) — fabricating duplicate tables and whitespace the
RPC path never produces, so the byte-stable and no-duplicate assertions failed
on win32. Replace it with a single-variant, blank-line-separated writer that
matches the real 0.144.x binary's output.
The async wsl.exe canonical-path settlement could report the runtime home
'missing' immediately after a verified RPC grant (a false negative — codex
had just written and re-listed trust there), which drove the reconciliation
'remove' branch to delete all six granted [hooks.state] tables, leaving a bare
[hooks.state] the launching pane read as 'hooks need review'. A 'missing'
settlement now revokes only when no successful install ran this generation; a
genuinely moved home still resolves to a different path and reinstalls.
The build guard rejects any electron require reachable from plain-node
entries; the bridge now maps app.asar to app.asar.unpacked by string
replacement instead of consulting electron app paths. CLI typecheck project
lists the new trust-grant module graph.
On a real Codex home whose filesystem supports no hardlinks (exFAT/FAT,
some network mounts), the staged cross-volume copy was installed with a
non-atomic copyFile(..., COPYFILE_EXCL) straight into the final
rollout-*.jsonl name. An install interrupted mid-copy (app quit, crash,
ENOSPC during the deferred run) could strand a truncated rollout that the
next run then skips as already-present, defeating the staging design's own
guarantee that a failed copy never leaves a partial session behind.
Install the fully-staged copy with an atomic rename instead, guarded by an
existence re-check so it keeps the never-overwrite contract (and the rename
source is the same immutable managed rollout, so any clobber would be
byte-identical). Cover the no-hardlink-support target and an interrupted
install that must leave no partial in the user's sessions tree.
Use asynchronous, sequential filesystem operations for the one-time rollout backfill, and avoid repeated target-directory probes. Treat inaccessible managed session roots as retryable failures instead of writing a false completion marker.
The grant bridge is reachable from plain-Node CLI entries, where the
plain-node entry guard rejects any chunk containing require("electron").
Resolve the bundled session entry from __dirname (root chunk and chunks/
layouts) with an app.asar -> app.asar.unpacked rewrite for packaged runs,
instead of electron's app path APIs.
With the real-home flag ON and the system-default selection, install Orca's
status hook into the user's real ~/.codex before any pane spawns:
- entry APPENDED LAST per managed event: codex hook trust keys are positional
(source:event:group:handler), so appending keeps every user entry's position
and trust record intact; user entries and unknown top-level hooks.json fields
are preserved verbatim
- trust is granted exclusively through the codex app-server client
(hooks/list + config/batchWrite, verified by re-list); Orca never writes
[hooks.state] into the user's real config.toml itself
- if the grant lane is unavailable (old binary, unsupported RPC, verify
failure), the appended entry is rolled back byte-exactly and the host keeps
the managed-home lane end to end (PTY env, rate limits, commit messages)
via a lane gate on the runtime-home service
- one-time pristine backup of the user's hooks.json under Orca's userData;
a rolling .bak sits next to the file (existing atomic writer)
- hook opt-out sweeps Orca entries from the real home and drops Orca-owned
trust records; flag-off downgrade re-arms the existing legacy system-home
sweep, which removes the entry and its trust keys cleanly
- the legacy system-home sweep is suppressed only while the real-home lane
owns ~/.codex/hooks.json, so managed installs cannot delete the entry
Adds the two cases for the headless commit-message Codex env under real-home
routing: a nested-Orca-inherited Orca-owned CODEX_HOME is stripped, and a
user-owned CODEX_HOME is preserved.
Host and WSL installs now grant trust for Orca's managed status hooks through
codex's own hooks/list -> config/batchWrite -> re-list verify, scoped to
exactly the managed entries; the previous computeTrustedHash lane is the
unchanged fallback for incapable/erroring CLIs. getStatus and the removal
paths recognize ledger-recorded codex hashes so drift between codex's real
algorithm and the replica no longer misreports or strands trust. SSH remote
install is untouched by design.
Short-lived codex app-server JSON-RPC client (hooks/list + config/batchWrite,
the same pair the Codex TUI 'Trust all' flow calls), run in a bundled
ELECTRON_RUN_AS_NODE entry so synchronous launch prep can block on it with a
hard deadline and guaranteed child reap. Capability cache modeled on
GitCapabilityCache, scoped per execution host (native vs each WSL distro),
with a narrow unknown-method/missing-subcommand unsupported predicate. The
grant ledger records verified grants so steady-state launches skip the RPC.
The daemon spawns PTYs from its own inherited environment and honors only
spawnOptions.envToDelete, so mutating the sparse env object was not enough to
strip an Orca-owned CODEX_HOME the daemon already carries. Add the strip to
envToDelete for both daemon host-spawn paths, preserving a user-set CODEX_HOME.
Verified live via CDP against a sandboxed dev instance (flag ON): an
Orca-spawned pane reports empty CODEX_HOME/ORCA_CODEX_HOME, so Codex resolves
its own ~/.codex. Adds daemon-path unit coverage (strip Orca-owned, preserve
user-owned, no-op when flag OFF).
Staged internal flag (default OFF, no settings UI): route the SYSTEM-DEFAULT
Codex account at the user's real ~/.codex instead of Orca's managed runtime
home. Flag OFF is byte-identical to today; managed (multi-account) selections
are unchanged in either state.
Routing (flag ON + host system default = no managed account):
- CodexRuntimeHomeService.prepareForCodexLaunch / prepareForRateLimitFetch
return null so the PTY/env layer injects no managed CODEX_HOME and the
rate-limit fetcher + auth-presence gate fall back to ~/.codex (the background
poller stops spawning Codex against the managed home — the #5370 auth war).
- buildPtyHostEnv strips only a nested-Orca-inherited Orca-owned override
(CODEX_HOME matching the private ORCA_CODEX_HOME marker), preserving a
user-set CODEX_HOME. Shell-ready re-exports already no-op without the marker.
- The headless commit-message Codex path strips the same inherited override.
Hook install for the real-home lane (append-last into ~/.codex/hooks.json,
trust via the app-server client) lands with the trust plumbing; the managed
hook install is skipped for this lane meanwhile.
Credit @jellychoco (#8606) for the native-home routing direction.
Depends on the codex trust-rpc-grant plumbing for the real-home hook installer.
Orca-launched Codex sessions currently land only in the Orca-managed
runtime home, so the user's own `codex resume` picker and app history
never see them (#4444, #8612). Backfill the managed sessions tree into
the real ~/.codex/sessions/YYYY/MM/DD layout once per host:
- hardlink first (one physical rollout log), copy as the cross-volume
fallback; existing target files are always skipped, nothing in either
home is deleted or moved
- idempotent; per-file failures leave the completion marker unset so the
next startup retries cheaply
- JSONL audit log of every link/copy/failure under
<userData>/codex-session-backfill/
- honors the custom Codex session source home override, mirroring the
existing system->managed bridge
WSL managed homes are distro-local and need an in-distro variant; that
is a follow-up.
Two renderer paths let Promise rejections escape unhandled, where the
crash-breadcrumb system retains them and grows the heap until the renderer
white-screens (#8260):
- The terminal file-link provider fires a `void Promise.all(...)` whose async
mappers probe path existence over SSH. On disconnect these reject with
"Remote connection dropped/reconnecting"; add a `.catch` that resolves the
callback with no links so the rejection is handled.
- "ResizeObserver loop completed" is a benign Chromium quirk; suppress it in
crash-diagnostics so it stops filling the breadcrumb buffer and inflating
the renderer error count.
Scope note: the original fix for #8260 also proposed Windows pty:kill signal
handling (killPtyProcess / isPtyAlreadyGoneError) and a ResizeObserver
double-rAF. Those landed on main independently (Windows kill handling by
v1.4.135; the pane-title-overlay rect-equality guard from #2756 already breaks
the loop at the source), so this change carries only the two guards still
missing upstream.
Co-authored-by: Yixian HU <yixian@YixiandeMacBook-Air.local>
Co-authored-by: Claude <noreply@anthropic.com>
* fix(speech): resume interrupted voice model downloads and surface real errors
Voice model downloads that died late in the transfer (flaky networks,
scanning proxies) failed at the UI's 90% progress cap and restarted from
byte zero on every retry, so affected users could never complete any
model download (#8620 / STA-1775).
- Retry transient download failures (net::ERR_* transfer cuts, resets,
timeouts, HTTP 5xx/429) with an HTTP Range resume from the bytes
already on disk, restarting from the canonical URL each attempt since
signed CDN redirect URLs expire; bounded backoff, gives up after
repeated zero-progress attempts with a diagnosable error.
- Correct catalog sizeBytes to exact upstream asset sizes (parakeet
v2/v3 are 482/487 MB, not 170/180; paraformer is 1,047 MB, not 115) —
also the progress denominator when content-length is missing.
- Show the underlying failure cause in the download-error toast and log
it in main; it was previously invisible everywhere.
All 7 pinned catalog SHA-256 hashes verified against current upstream
assets. Live-verified end to end: a transfer killed mid-body resumes
with a 206 and completes through checksum, extraction, and validation.
Fixes#8620
* fix(speech): harden resumed model downloads
* fix(speech): validate resumed download completion
* fix(speech): keep segmented download resumes progressing
* fix(speech): don't abandon a download that keeps making progress
The retry loop gave up after 8 total failures regardless of progress, so
a large model (e.g. the ~1GB Paraformer) over a network that resets every
few hundred MB was abandoned mid-download even though every attempt was
resuming and advancing — the exact 'stuck then fails' symptom, just later.
Replace the all-time failure budget and the separate advancing-segment
cap with a single rule: give up only on a genuine stall (repeated
attempts with no forward progress) or an absolute request ceiling that
bounds a pathological tiny-segment server. Any download that keeps
advancing now runs to completion.
Verified live under Electron 43: a transfer reset mid-stream on every
attempt but advancing each time previously gave up at 67% after 8
attempts; it now completes. All prior resume/cancel/timeout scenarios
still pass.
* Add in-place screenshot markup for the browser pane
Adds a Draw button to the browser toolbar (local and remote/SSH panes) that
freezes the visible viewport into a still image and overlays a drawing canvas.
Users can mark it up with pen, highlighter, arrow, rectangle, ellipse, and text
(color, thickness, undo/redo), then copy the composited PNG to the clipboard to
paste into their agent — reusing the existing clipboard screenshot path, so it
works for local and remote agents alike.
The drawing layer is a renderer-side canvas, so the base image is the only
environment-specific piece: local panes use webview.capturePage(), remote panes
snapshot the already-displayed screencast frame. Drawing model, compositing, and
shape rendering are isolated, unit-tested modules.
* Fix browser markup clipboard, transparency, text input, and toggle
- Copy now always produces a PNG (the clipboard handler accepts PNG only;
a JPEG fallback silently produced an empty clipboard) and targets the
clipboard size limit so realistic viewports stay full-resolution.
- Flatten the captured base image onto opaque white so transparent page
backgrounds no longer ghost the live view through the frozen backdrop.
- Keep markup text-input keystrokes local (focus the field explicitly and
stop propagation) so the browser pane's global handlers can't swallow them.
- Make the Draw toolbar button toggle markup mode off on a second click.
* Add markup text sizing, object re-editing, and text-input fixes
- Add a font-size control (text tool) to the markup toolbar.
- Add a Select tool to re-edit committed shapes: click to select, drag to
move, change color/width/font-size of the selection, Delete to remove, and
double-click text to re-edit its content. Undo/redo now snapshots the whole
shape list so every edit (not just adding) is reversible.
- Fix the text input: transparent ink-colored field (no dark theme box over the
screenshot) and ignore Enter during IME composition so Japanese conversion no
longer commits the annotation early.
- Split the grown overlay logic into focused modules (editor hook, pointer +
keyboard hooks, canvas renderer, document edits, hit-testing) to stay within
the file-size budget.
* Improve markup text commit and move toolbar to the bottom
- Clicking elsewhere while a text box is open now commits the text and
switches to the Select tool, instead of discarding it (the input
unmounted before its blur fired) and opening a new box at the click.
- Move the drawing toolbar to the bottom (stacked above the actions bar)
and open the color/size popover upward.
* Fix markup text re-editing alignment, doubling, and selection bounds
- Hide the text shape being re-edited from the canvas so the live input
isn't doubled by its committed render.
- Drop the input's padding/border and use the same font as the canvas so
the typing preview sits exactly where the text renders (no shift);
field-sizing keeps the box hugging the text.
- Estimate text width with full-width (CJK) awareness so the selection box
and hit bounds track Japanese text instead of clipping it.
- Move the font-size control out of the color popover into its own toolbar
button showing the current size.
* Unify markup text selection and edit framing
Draw the selection box on the canvas even while a text shape is being
re-edited (the shape stays hidden so it isn't doubled), and strip the box
off the text input so it renders only the editable glyphs. The frame is now
identical whether selecting or editing, and the text overlays the same
position with the same font, so there's no shift between the two.
* Align markup text-edit baseline with the committed render
Force the text input's line-height to 1 and zero its height/padding so its
top edge matches the canvas textBaseline:'top' render — the editing text no
longer sits slightly lower than the committed/selected text.
* Address review: markup hit-testing, clear reset, and async guards
- Hit-test highlights against their rendered (4x) thickness so the select
tool doesn't miss large highlight strokes.
- Reset transient editor state (pending text, selection, in-progress drag) on
Clear all so a pending input blur can't re-add text and no stale selection
lingers over the emptied canvas.
- Guard markup completion with the capture token so a stale onDeliver can't
reset or error a session the user already cancelled or restarted.
- Skip the grab-element keyboard shortcut while the markup overlay is open,
matching the already-disabled grab toolbar buttons.
- Localize the markup error messages and surface them via a toast (the
controller's error was otherwise never shown).
* Trim browser markup to a draw-only tool and fix two delivery bugs
Reshape the screenshot-markup overlay to match its actual use — a throwaway
scribble the user copies once into their agent — and drop the re-editable
vector-editor machinery that made it an outlier in scope.
- Remove the select/move/restyle/re-edit subsystem: hit-testing, per-shape
document edits, drag preview, selection frame, double-click text re-edit,
Delete-to-remove, and the CJK text-width heuristic / multi-line text plumbing
that only fed selection bounds. Keep pen, highlighter, arrow, rect, ellipse,
and text with color/thickness/font-size and undo/redo/clear.
- Fix silent data loss on HiDPI: the compositor budgeted only bytes, so a large
Retina viewport could produce a PNG under the byte limit but over the
clipboard handler's pixel ceiling, which the handler drops silently while the
UI still reported success. Composite now scales down to fit maxPixels too.
- Fix a stranded UI after a capture failure: the controller stayed "active" with
no overlay and an inert Escape. It now returns to idle (capture failure) or
back to the drawing surface (compose failure) after the error toast.
Net ~660 fewer lines and 3 fewer modules; delivery (freeze → composite PNG →
existing clipboard-image paste) is unchanged. Unit tests updated.
* Add a one-time discovery hint for the browser Draw button
Highlight the new screenshot-markup Draw button the first time it's usable so
users — including existing ones — notice the tool. A localStorage-gated,
show-once tooltip + highlight ring, independent of the browser contextual tour
(which is capped at three steps and only shows to users who haven't seen it).
- New use-markup-draw-hint hook: opens once per install when the button is
enabled and idle, auto-dismisses, and dismisses on click; skips cleanly when
storage is unavailable.
- MarkupDrawButton forces its tooltip open with the discovery copy and rings the
button while the hint shows.
- Localize the hint (drawHint) across en/es/ja/ko/zh; normalize the markup key
order alphabetically.
* Align the markup copy button label with "Copy Screenshot"
The browser pane's two image-copy actions read inconsistently in English —
"Copy Screenshot" (the grab flow) vs "Copy markup" (the overlay). Match the new
markup button to the existing one: "Copy Markup". English only; other locales
already use their own sentence-case convention consistently, so they're
unchanged.
* Drop the markup Draw hint auto-timeout; dismiss on act only
The one-time Draw-button hint no longer auto-dismisses on a timer the user might
miss. It now stays open until the user clicks Draw or the button stops being
usable (grab started, markup open, or a blank tab), which also fixes a case
where losing eligibility mid-hint left the forced-open tooltip stuck over a
disabled button.
* Optimize markup rendering and harden compose delivery
Performance:
- Rasterize committed shapes once into an offscreen layer; per-frame paint blits
that layer and draws only the in-progress shape, so a fast pointermove stream
never re-strokes the whole scene.
- Encode the composite PNG off the main thread via canvas.toBlob (a large
synchronous toDataURL froze the renderer), with a rAF yield so the "composing"
UI paints first.
Correctness / review fixes:
- Don't overwrite the clipboard after cancel: re-check the capture token between
the (now async) compose and onDeliver, closing the window where Escape during
composing still delivered the markup.
- Keep the offscreen layer and visible canvas in lockstep by threading one
measured devicePixelRatio through both paints, and re-measure on window resize
so a monitor move (dpr change without a CSS-box change) repaints at the new
scale. Blit stretched to the canvas box so a transient mismatch never clips.
- Drop the stale "or JPEG fallback" note; compose is PNG-only.
* Fix test fixtures for markup pixel-budget clamping to use a truly oversi
The old 3840×2160×dpr2 fixture (33.2M px) was already under the 32×1024×1024
(~33.55M) ceiling, so the "never over budget" assertions weren't exercising
the clamp path. Switch to 4000×3000×dpr2 (48M px), which genuinely exceeds
the ceiling.
---------
Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
* Surface Grok unified-billing monthly usage instead of a permanent warning
Unified-billing Grok accounts have no weekly credits: the
/billing?format=credits view returns a config without
creditUsagePercent, so the status bar was stuck on 'Grok billing
response did not include credit usage' even though the account has a
real quota. The default (format-less) /billing view reports it as an
included monthly budget (monthlyLimit/used with the billing period).
When the credits view has no weekly credit usage, read the default view
and surface monthly usage as the provider's 30-day window (already
supported by the tooltip and chip visibility for OpenCode Go). If the
fallback read fails, the previous 'unavailable' presentation stands
rather than escalating to an error chip.
* Review fixes: chip renders monthly-only usage; fallback failures keep stale data
- StatusBar ProviderSegment: monthly window is chip-visible when it is the
sole window (Grok unified billing); fetching/error no-data guards and the
icon-only dot now count monthly, matching tooltip.tsx. OpenCode Go chips
are unchanged (monthly stays tooltip-only next to session/weekly).
- grok-fetcher: monthly-fallback request failures propagate as 'error' so
applyStalePolicy keeps the last good monthly snapshot; 'unavailable' is
reserved for a successful response without monthly fields.
* Settings: show Grok monthly usage row for unified-billing accounts
Why: the Grok accounts section only rendered the weekly-credits row, so
unified-billing accounts showed a signed-in state with no usage at all.
* Use generated localization keys for Grok monthly copy
* Stop usage chips flashing and back off retries for failing providers
Two behaviors made the status bar unusable when any provider was
persistently failing (bad auth, unsupported plan):
1. Every refetch repainted all providers as 'fetching', so a settled
error chip flashed to a loading "…" chip and back on every cycle.
withFetchingStatus now keeps settled states (ok/error/unavailable)
visible until the new result lands; only providers with no settled
state (first load, explicit account-switch clear) show loading.
2. Error providers on the fast activation-retry lane (claude/codex/
grok) were retried every 30s on any focus/show/restore event —
forever. Repeated hits drove Claude's tight-budget usage endpoint
into 429s, flipping the chip between 'Limited' and its actual error.
Retries now back off exponentially per consecutive applied failure
(30s, 60s, 120s, … capped at the 15-minute poll cadence) and reset
on success or account/target switch.
* Count full fetches as failure-lane retries and keep Grok pane refresh feedback
- Stamp failing providers' activation-retry clocks when a stale-driven full
fetch runs, so the individual failure lane does not fire a redundant retry
right after the full fetch already retried them.
- GrokUsagePane: manual refresh spinner/disable is now renderer-local, since
settled snapshots no longer repaint as 'fetching' during refetches.
- Strengthen the backoff-reset test so it distinguishes a reset streak from a
stale retry timestamp (CodeRabbit), and add a regression test for the
full-fetch retry stamping.