Commit Graph
10867 Commits
Author SHA1 Message Date
Neil e488bb0dda merge PR 20054 relay-pty-churn-gc-probes 2026-09-11 22:28:47 -07:00
Neil d2f9cc28df merge PR 20053 remote-connect-bound 2026-09-11 22:28:32 -07:00
Neil 1139b11758 merge PR 20052 relay-abort-index 2026-09-11 22:27:20 -07:00
Neil c3260fc78a merge PR 20045 ssh-relay-orphan-sweep-visibility 2026-09-11 22:26:56 -07:00
Neil c504cc674f merge PR 19952 mobile-pairing-scope-refusal-fallback 2026-09-11 22:26:25 -07:00
Neil 6ef099d4a8 merge PR 19947 ssh-failed-target-resume-coverage 2026-09-11 22:26:01 -07:00
Neil f8e2bef14d merge PR 19944 emptied-workspace-reconnect-survival 2026-09-11 22:25:35 -07:00
Neil 9f1fa5edbf merge PR 19879 relay-handshake-frame-containment 2026-09-11 22:25:15 -07:00
Neil 8ba1d34f78 merge PR 19878 host-active-tab-not-user-intent 2026-09-11 22:24:51 -07:00
Neil e4e9a1ae10 merge PR 19877 remote-tab-reveal-vs-resume docs 2026-09-11 22:24:30 -07:00
Neil 0f5008aaf1 merge PR 19864 remote-codex-home-hook-install 2026-09-11 22:24:13 -07:00
Neil 4f2be11df2 merge PR 19861 relay-protocol-version-n 2026-09-11 22:23:05 -07:00
Neil c24f1bef4f merge PR 19860 terminal-surface-lost-inventory 2026-09-11 22:22:53 -07:00
Neil 5b6cc18f1d merge PR 19572 ssh-partition-adoption 2026-09-11 22:22:37 -07:00
Neil b0e8234beb merge PR 19414 linear-session-identity 2026-09-11 22:17:01 -07:00
Neil 4f72a92cb7 merge PR 19394 ephemeral-vm ssh relay reattach 2026-09-11 22:15:20 -07:00
Neil 6004f4fc7a merge PR 20051 worktree-home-guard-path-authority (incl 19865) 2026-09-11 22:15:10 -07:00
Neil 3720bc882e merge PR 20050 wsl-suite-home-isolation (incl 19957) 2026-09-11 22:14:59 -07:00
Neil 14959682d3 merge PR 20048 win-conpty-prebuild-prune (incl 20047) 2026-09-11 22:14:51 -07:00
Neil 3e73e4c88f merge PR 19873 reconnect-churn-19647-writer 2026-09-11 22:14:45 -07:00
Neil d932801933 merge PR 20059 handle-gap-outage-not-a-verdict (incl 19882) 2026-09-11 22:14:18 -07:00
Neil 0ebbdd913c merge PR 20096 status-writer-one-nulling-rule (incl 20095, 20085) 2026-09-11 22:12:54 -07:00
Neil 604db50484 merge PR 20065 mobile-relay-status-unreadable 2026-09-11 22:12:24 -07:00
Neil 9c825229c0 merge PR 20064 relay-transient-demand-wake 2026-09-11 22:12:16 -07:00
Neil f477b0edb7 merge PR 20063 relay-retry-chain-containment 2026-09-11 22:11:21 -07:00
Neil 890cced8cc merge PR 20061 relay-broker-wait-concurrency 2026-09-11 22:11:16 -07:00
Neil 530b34f472 merge PR 19963 relay-lifecycle-fences 2026-09-11 22:11:12 -07:00
Neil 09525ab782 merge PR 20055 relay-unreadable-session-taxonomy 2026-09-11 22:11:07 -07:00
Neil 4b763b8fb5 merge PR 19870 lan-mode-paired-devices 2026-09-11 22:10:32 -07:00
Jinwoo Hong 341b13cf67 Restore mobile push and fix cold-start dismissals (#20068)
* Restore mobile push for delivery validation

* fix(mobile): register push task before headless startup

* Add authenticated mobile push test and fix iOS release entitlements

* Mock push-test transport in notification consent tests

* Fix slept workspace test for structured remount result

* Fix mobile notification review findings

* Pad Android notification icon to prevent square cropping

* fix(mobile): present visible Android data pushes in foreground

* test: use deterministic clock for teardown deadline

* fix(mobile): present foreground pushes through Expo public APIs

* fix(mobile): check push eligibility before foreground scheduling

* fix(mobile): register push from shared host connection lifecycle
2026-09-12 01:03:57 -04:00
Jinwoo Hong 9f7fd9a270 fix(relay): reuse canary across completed rollout batches (#20214) 2026-09-12 00:46:46 -04:00
20ab995065 fix(codex): reconcile marketplace and plugin tables through the config mirror (#20150)
Scalar promotion omitted the marketplace and plugin tables, and the mirror rebuilt
ordinary config from canonical while only trust sections survived, so a managed-account
registration and refreshed provider metadata were both destroyed at the same boundary.

Registrations now reconcile through one baseline-aware pass before the canonical->runtime
copy: a runtime-only table is promoted, a table the canonical config removed since the
last mirror stays removed, canonical wins on an identity change, marketplace refresh
metadata is promoted only for a strictly newer valid timestamp with its paired revision,
and a plugin `enabled` toggle promotes only when the runtime alone changed it.

The settings baseline gains an optional `registrations` map at version 3. Absent means
never mirrored, which makes the v2 upgrade lossless; an older build rejects version 3 and
rebuilds, so downgrade is a safe degrade.

Verified end to end against a real codex-cli binary, which wrote the registration into a
managed home and read the promoted result back: `No marketplace plugins found.` becomes
`ponytail@ponytail  installed, enabled`.

Fixes #10489
Fixes #11770

Co-authored-by: BsTiger <96857444+Bongseop-Kim@users.noreply.github.com>
Co-authored-by: Rod Boev <rod.boev@gmail.com>
2026-09-11 21:22:10 -07:00
Neilandmaoking 438e0f4f5a fix(hooks): stop orphaned managed markers from consuming user TOML (#20148)
A managed block missing its end marker was treated as Orca-owned through EOF,
so uninstall/reinstall deleted appended user tables. The same shape existed a
second time in the Codex legacy profile cleanup.

Ownership is now two separate claims: a marker pair proves extent, and a
provider that can recognize its own emitted tables owns them wherever they
sit. An orphaned marker owns only its own line. Recognition uses the same
test for remove, install and status, so a table Orca cannot see is never one
it leaves running.

Co-authored-by: maoking <secretxierluo@gmail.com>

Fixes #18861
2026-09-11 21:22:06 -07:00
Jinwoo Hong f7238ce469 fix(relay): bound idle rehome polling and skip disabled scans (#20203)
* fix(relay): bound idle rehome polling and skip disabled scans

* test(relay): align sweep jitter expectation with polling budget
2026-09-11 23:58:24 -04:00
Brennan BensonandMerge Sim 76c8e91d4a fix(e2e): run worktree first-paint probe on a mapped window (#20197)
Co-authored-by: Merge Sim <sim@local>
2026-09-11 20:25:49 -07:00
Neil 3b13ce09a5 fix(source-control): pass the Antigravity prompt to agy --print (#20147) 2026-09-11 20:20:47 -07:00
Brennan BensonandMerge Sim 556a7772ed fix(e2e): remove four real flake sources and one caret race (#20169)
Four E2E specs failed once each across six main runs. Each traces to a
timing boundary the test could not control, not to product instability:

- linear-url-workspace-entry: pasted before X selection ownership landed,
  delivering stale text. Gate on a clipboard read-back.
- native-chat-first-flush-race: a bare 1_500ms sleep is exactly
  UNFLUSHED_SETTLE_MS, so it straddled the boundary deciding which of two
  hydration paths carried the test. Observe the not-yet-flushed read
  instead; a notFound is never cached, so this cannot perturb hydration.
- orchestration-idle-mail-delivery: asserted that a PTY -> daemon -> main
  round trip beats a 500ms production heuristic. Use the existing
  ORCA_E2E_ORCHESTRATION_POINTER_ENTER_DELAY_MS knob.
- tasks-page: the probe timeout was the one figure in the file not derived
  from GITHUB_TASK_SEARCH_IDLE_MS.

worktree.spec.ts exposed a real product race rather than a test bug: the
emoji caret-restore frame stayed armed through ordinary typing, so a
late frame could yank the caret back mid-input. Cancel it on the
non-emoji onChange path.

Also repairs a stale assertion: #20025 changed
remountTerminalTabForRecovery to return a result object and updated the
sibling call site but missed this one, so the comparison to `true` could
never pass. It is a deterministic break, not a flake.

Co-authored-by: Merge Sim <sim@local>
2026-09-11 18:37:16 -07:00
Jinwoo Hong 113e58f34e feat(relay): support protocol 3 in cell rollout gates (#20174)
* feat(relay): support protocol 3 in cell rollout gates

* fix(relay): validate and prove protocol-3 cell rollouts

* docs(relay): clarify regional capability deployment prerequisite

* test(relay): cover protocol-3 plans across rollout cells
2026-09-11 21:27:13 -04:00
Brennan BensonandMerge Sim 70a588c8bf fix(workspaces): complete a worktree create when a post-create step throws (#20175)
* fix(workspaces): complete a worktree create when a post-create step throws

executeWorktreeCreation's try/catch ends once createWorktree resolves, and all
three callers fire it with a bare void and no .catch. completeWorktreeCreation
is the only thing that removes the pending creation, so a throw in that tail
left pendingWorktreeCreations and activePendingCreationId set: the creation
surface stayed up, workspaceChromeActive went false, and the finished workspace
rendered no tab chrome while its panes mounted invisibly behind the panel. It
only cleared when the user switched workspaces, because setActiveWorktree nulls
the pointer. Silently -- no toast, no error state.

activateAndRevealWorktree, ensureWorktreeHasInitialTerminal and
ensureWebRuntimeWorktreeTerminalAfterWake are all synchronous with no internal
guard; launchStructuredWorktreeSession guards only its awaited launch, and that
catch's comment already names this stranding hazard.

The worktree exists past that point, so each follow-up step is now guarded
individually and falls back to the values the skip paths already used; control
flow always reaches completion. The structured-launch cancelled/visibility
returns keep their semantics, and a throw there is treated as a failed launch,
matching what that module already returns for 'failed'. A .catch backstop on
the three call sites turns anything that still escapes -- including
prepareRequestForCreate, whose VM await has try/finally with no catch -- into a
visible error state plus toast.

Ablated: with the guards removed the new suite fails 4 of 5, the survivor being
the no-throw control.

* fix(workspaces): recover terminal after partial activation

* fix(workspaces): preserve stamped launch tab on recovery

* test(workspaces): cover recovered agent tab delivery

* test(workspaces): name recovered agent delivery coverage

---------

Co-authored-by: Merge Sim <sim@local>
2026-09-11 18:07:15 -07:00
Brennan BensonandMerge Sim 353a1c8038 fix(terminal): re-run deferred tab admission when a plan installs mid-activation (#20176)
planColdActivationTabDeferral can install an empty allowed set, deferring every
tab so the pane filter renders none. The drain that undoes that,
useActivationDeferredTabAdmission, depends only on backgroundMountRevision and
renderedActiveWorktreeId while reading the deferred set from a mutable ref, and
the install bumps neither: the only revision producers are the drain itself and
the background-mount event path, which the activation plan never reaches.

So this pass strands the workspace with zero panes: the worktree is already
rendered-active while the startup gate is closed, which resets
lastActivationWorktreeIdRef, then the gate opens on the same worktree and
installs a plan. Nothing re-runs the drain until the user switches workspaces
and back. The hook's own comment anticipates this launch shape and relies on
re-reading on growth, but that re-read only happens on a dep change.

applyTerminalColdActivation now returns activationDeferralPlanRevision, backed
by a ref in the parking foundation and incremented only when the plan actually
installs, which the admission effect takes as a dep. A ref rather than state
because the pass runs during render, where a setState would be a render-phase
update.

The 4-tab admission cap is deliberately untouched: it reproduces the warm set
an eager activation used to mount, so steady-state pane and WebGL-context
population is unchanged.

Ablated: with the change reverted the new suite's drain case fails on the
deferred set surviving the timers; the precondition and the away-and-back
control pass either way.

Co-authored-by: Merge Sim <sim@local>
2026-09-11 18:03:05 -07:00
Brennan BensonandMerge Sim 6252f8149b fix(browser): decode Chromium SameSite storage values (135 cookies silently lost per profile) (#20076)
* fix(browser): decode Chromium SameSite storage values

* fix(browser): document Firefox's real SameSite domain and pin its default-arm inputs

Third-reviewer finding: the replacement comment reproduced the failure mode this
PR exists to kill. It said "Firefox's moz_cookies uses the same 0/1/2 values",
which is true of the overlap and silently wrong about the rest of the domain.

Firefox writes 256 (nsICookie SAMESITE_UNSET) for every cookie with no SameSite
attribute -- the most common shape in a modern profile -- NULL on pre-v10 rows,
and 0 for explicit None OR a legacy unset row the schema-15 migration left
behind, which are not distinguishable in the column. All of those must land on
unspecified, so the default arm is load-bearing for Firefox rather than
incidental. A later reader making the switch exhaustive against the old comment
would have regressed Firefox silently.

Also pins the inputs that were riding the default untested: 256, and the
non-integer arrivals (null, undefined, NaN) that the `?? -1` scan fallback and
pre-v10 Firefox rows produce. Decoder behaviour is unchanged; 11/11 pass.

---------

Co-authored-by: Merge Sim <sim@local>
2026-09-11 15:59:56 -07:00
Neil 9c918b93a6 fix(deps): take Electron 43.7.0 for the glibc environ use-after-free (#20089) 2026-09-11 15:43:43 -07:00
Brennan BensonandMerge Sim da5d555259 refactor(agent-status): delete the runtime's retained row store (PR 1b) (#19785)
* docs(agent-status): plan PR 1b at file level

Names the five RuntimeAgentRowStore call sites and what each becomes, why
terminalHandle has to be stamped before the store can go, and the one
intended behavior change.

* feat(agent-status): stamp the pane terminal handle on hook-server rows

The runtime's retained row store carried the pty binding two readers need. Put
that fact on the row that already owns the pane instead, resolved through the
same lookup the renderer-facing IPC boundary runs, so the two surfaces cannot
disagree about which terminal a pane is.

Carried forward when a later write resolves no handle (only main's OSC parse
can), and never persisted: a handle belongs to the runtime that issued it.

* refactor(agent-status): route the session-tabs republish off the store

`retain()` was not only a duplicate store: its boolean return was the signal
that republished `session.tabs` for a status-only transition, which no title
change covers (#7970). `hook-status-session-tabs-invalidation.ts` already
mirrors that change set plus hook restore provenance, so route the signal off
the store rather than keep a second comparator.

Adds the status-drop arm a user dismissal emits, which the pane-clear fan-out
deliberately skips — now load-bearing, because a dismissed row leaves the
listing at once.

Installed on both hosts. orcad had neither the OSC producer nor this signal, so
its runtime observed agent status and published it nowhere; deleting the
retained copy without wiring it would list no PTY agents there at all.

* refactor(agent-status): delete the runtime's duplicate retained row store

`RuntimeAgentRowStore` held the same payload the hook server already holds, so
the same pane could legitimately read differently in the sidebar, in
`worktree ps`, and on the phone. Both of its readers move onto the store's
snapshot in `runtime-hook-agent-row-selection.ts`, and
`collectRuntimeWorktreePtyAgentSources` loses the retained-versus-hook
reconciliation that only existed because two stores could disagree.

`ConnectedPtyEvidence` trades its flat pty-id set for `ptyIdByTerminalHandle`,
which is how a row still resolves the connected PTY behind it — the
working-terminal rollup's match key, and the last rescue for a row whose pane
binding a controller incarnation nulled under it.

The one intended behavior change: a row the user dismisses on the desktop
leaves `worktree ps` and mobile at once instead of lingering until the pty
exits. One store means one dismissal.

The suites written against the retained store are rewired to a real
AgentHookServer rather than deleted, so each still asserts the listing
behavior it named.

* docs(agent-status): record what PR 1b landed

Past tense, plus two corrections to the plan: `terminalHandle` is not the pty
id (they are different identifiers, and the explicit-status reader was already
comparing against a real handle), and the legacy numeric pane key is a
consequence the plan did not name.

* fix(agent-status): harden single-store lifecycle

* fix(agent-status): preserve mobile terminal rejoin

* fix(agent-status): preserve unverifiable remote rows

* fix(agent-status): own PTY row lifecycle in hook server

* fix(agent-status): preserve state and renew freshness

* fix(agent-status): ignore freshness for dismissed identity rows

* fix(agent-status): fence orcad observed identities

* fix(orcad): always release daemon adapter on cleanup

* fix(agent-status): cover remint and headless lifecycle edges

* fix agent status identity recovery gaps

* fix(agent-status): suppress duplicate child-only row mutation

* test(runtime): preserve hook store wiring in transcript harness

---------

Co-authored-by: Merge Sim <sim@local>
2026-09-11 15:28:16 -07:00
Jinwoo Hong 729491597f feat(desktop): measure relay regions and reconnect after idle cutover (#20106) 2026-09-11 11:43:48 -07:00
Jinwoo Hong 1d7bb47a11 test(relay): harden regional rehome race coverage (#20136) 2026-09-11 11:42:03 -07:00
Jinwoo Hong cd9aa43a2c feat(relay): correct regional placement only when the source is idle (#20105)
* feat(relay): correct regional placement only at an idle source

* test(relay): lock source activity capacity semantics
2026-09-11 14:25:10 -04:00
Brennan BensonandMerge Sim 9a56797486 fix(mobile): surface host create warnings and terminal-create errors (#20125)
* fix(mobile): surface host create warnings and terminal-create errors

A workspace created from the phone could land on "No tabs in this session"
with a bare red "Failed to create terminal" and no way to tell why. Two
independent drops hid the host's own explanation:

- createWorktreeWithNameRetry returned only {worktreeId, name}, discarding
  worktree.create's `warning`, and hostNewWorktreeSessionRoute built the
  session route with only `name` + `created=1`. The session screen has always
  had the banner (MobileSessionContentRow + createWarningState) -- only the
  tasks create path ever fed it, so the New Workspace path could never report
  a startup terminal that failed to spawn.
- handleCreateTerminal collapsed every failure to the literal
  'Failed to create terminal', throwing away response.error.message.

Both now propagate, so the daemon's pty-allocation hint ("Your system cannot
allocate any more pty devices.") reaches the phone instead of dying in the
main process. Behaviour is otherwise unchanged: a blank warning is still
omitted from the route, and a host that gives no reason still reads
'Failed to create terminal'.

* test(mobile): refresh route parity baselines

---------

Co-authored-by: Merge Sim <sim@local>
2026-09-11 11:21:31 -07:00
Jinwoo Hong ec9c3e0550 Fix remote hosted review browser routing (#20030)
* Fix remote hosted review browser routing

* Fix remote review modifier hint

* Address review feedback and fix routing test types

* Avoid assuming active runtime owns workspace links

* Align runtime routing regression expectation

* Respect explicit local link ownership
2026-09-11 14:20:06 -04:00
Brennan BensonandMerge Sim fb19c969a6 feat(native-chat): show when a Codex goal is set, changed, or cleared (#19923)
* feat(native-chat): show when a Codex goal is set, changed, or cleared

Codex never emits the model's `create_goal` call as an item, so
`thread/goal/updated` is the only truthful evidence that a goal exists. Both
goal notifications were classified `status-chrome`, which meant no typed
handler read them and no row was written -- the only thing reaching the reader
was the model's own prose. That prose can be wrong: in a session where no goal
was ever created the model still wrote "Goal created: ...".

Classify both frames as timeline-substantive and give them a sentence, so the
reader can tell a goal that exists from one the model merely claimed. Status is
translated rather than echoed, and an unrecognised future status still reads as
"Goal updated: <objective>" instead of the bare opcode.

Codex re-sends the goal as its token and time counters climb, so rows are
deduped on what a reader would notice -- objective, status and budget. A live
session sent the same goal three times in one turn with only accounting moving.

* fix(native-chat): write the goal signature separator as an escape, not a raw NUL

A literal NUL byte in the source made git classify the file as binary, which
hid its diff from review. The string built at runtime is unchanged.

* fix(native-chat): make Codex goal rows retry-safe

* fix(native-chat): preserve Codex goal identity on resume

* fix(codex): ignore empty goal clear snapshots

* fix(codex): preserve goal lifecycle across rewinds

---------

Co-authored-by: Merge Sim <sim@local>
2026-09-11 11:00:09 -07:00
Jinwoo Hong 08a24efaba fix(push): preserve distinct Android alerts while offline (#20066) 2026-09-11 13:23:04 -04:00