Repository navigation
sync: upstream main 2026-10-11c (56 commits → v1.4.224-0) - #19
Merged
Merged
Conversation
…blyai#26516) A desktop relays alerts for agents on its paired servers, but the push named only the server's worktree, so a tap opened it on the desktop and bounced. The gateway accepts an optional runtime execution host and forwards it. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
…ted runner (stablyai#27393) The Asia load runner is registered to the old repository, so asia-east2 load could not run. Where the load comes from does not change what c4 does with it.
…y deletion (stablyai#27392) The relay server deletion removed src/relay/ from the cross-host qualification prefixes, but the scope tests merged alongside it still used src/relay/ paths as their cross-host example. Use src/main/ssh/, which still carries that rule. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
…tablyai#27402) One hosted runner tops out near 2,048 connections to one cell (4 shards each stalled at 512 with control_open_timeout), short of the 2,840 the Asia flip-back test needs.
… name (stablyai#26842) * fix(zsh): keep deferred line-init defined for plugins that call it by name Fixes stablyai#26029 Co-Authored-By: petersindex <petersindex@users.noreply.github.com> * test(zsh): keep the named-call regression test out of the relay module --------- Co-authored-by: m4air <m4air@Mac.localdomain> Co-authored-by: petersindex <petersindex@users.noreply.github.com>
…i#26848) Normalize the incoming host slice and the saved partition before retirement and membership rebasing read their terminal maps. Fixes stablyai#25924 Co-authored-by: m4air <m4air@Mac.localdomain>
…ai#26415) * feat(mobile): show SSH host health on the workspace row host badge Rows on an SSH host the desktop cannot reach looked healthy on the phone. The badge now adds the desktop sidebar's own word ("devbox · Disconnected"), derived by the same shared function from the connection status ssh.listTargetSummaries already sends; old desktops keep today's badge. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * feat(mobile): name an unhealthy SSH host even in a single-host list The desktop card shows SSH status on any host count, so the phone now reads target summaries whenever a repo is on SSH and badges an unhealthy host alone too. Labels and health share one target-to-host keying, and a hook-level test pins the reply-to-row wiring. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): clear stale SSH host badges and announce them without the dot A catalog that no longer spans hosts or holds an SSH repo now clears the health and label maps, so a host that left cannot keep badging rows. The badge gets a screen-reader label ("devbox, Disconnected") instead of reading the middle dot. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): type the single-host health fixtures inline so the test tsc passes Fixtures hoisted into consts widened their health maps to string, which the now-required hostHealthById rejects under the tests-typecheck ratchet; passing them inline keeps the union. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): re-record the repo-metadata goldens for SSH host health The settled metadata state now carries hostHealthById, and an SSH-only catalog now reads the target summaries so a lone unhealthy host can be badged. Scoped re-record via rpc-recording.mts. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
…ces (stablyai#26427) * feat(runtime): let a paired desktop register its phones as delegated mobile devices A desktop paired to this runtime can now call pairing.delegatedMobileDevice.sync to make each of its phones an ordinary mobile-scope device it owns, so the phone mirrors that host through the desktop. Children are never pending, inherit the parent's reach, and die with the parent's grant. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * refactor(runtime): move the mobile device revoke primitive below dispatch Dispatch binds the caller-bound delegated-device context, so the revoke path it needs now lives in a layer under it as a pure move; this drops the abstract method seam. No behaviour change. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(runtime): keep a runtime grant until its delegated phones are revoked A failed child cleanup now leaves the parent in place so a retry can still find the children, and revokeRuntimeAccess lives beside the revoke it cascades to. Adds restart, rename and bounds coverage, shares credential minting, and drops the timing-dependent census. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(runtime): fail a delegated sync whose dropped phone cannot be revoked Matches the parent revoke: a child whose cleanup could not be saved is kept and the sync errors before minting anything, so the desktop retries instead of believing the server matches its list. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(runtime): refuse pairing admin to phones delegated through a paired desktop A delegated child phone could call the mobile-allowlisted pairing-admin methods (push registration, relay provisioning) that its parent desktop is never granted. The caller scope now marks delegated phones and denies them that permission. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
…tion (stablyai#26528) * fix(browser): retry a screencast frame a viewer's socket refused An idle page sends no next frame, so a refused last frame left the viewer on a stale picture. The desktop's relay leg to a server uses legacy E2EE, which drops on a full socket where a direct v2 phone queues; retrying the newest pending frame restores parity. Retries never count toward ghost eviction. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(e2ee): queue legacy binary replies with text instead of dropping them Legacy E2EE (every direct phone, the desktop's server link) sent binary straight to the socket and silently dropped it once the outbound budget was full, so terminals lost output and binary could overtake queued text. Binary now shares the ordered text queue and closes on overflow like v2; the lossy screencast opts into dropWhenBacklogged and its retry delivers the newest frame after a drain. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(e2ee): keep a backlogged screencast viewer instead of evicting it as a ghost A lossy frame skipped on a busy socket returned false like a dead socket, so a long backlog evicted a live viewer. Skips now return 'backlogged', which retries without counting toward eviction; legacy judges idleness on the sealed size before encrypting, so skipped frames cost no encryption. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(browser): expect the ready gate to forward screencast send options The pre-ready gate now passes the per-frame send options through to the socket sender, so the replayed startup frame arrives with an options slot. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(browser): share the streamed-guest double across screencast tests Main's renderer throttle lease now calls setBackgroundThrottling on the guest, so the bare { isDestroyed } guest in two branch tests threw. One shared double keeps all three screencast suites on the shape the lease needs. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
…lyai#26522) In a server workspace the desktop and the server mint different ids for a split-off group, so a drag into it named a group the server didn't know and was silently dropped. The desktop now names the server group that holds another tab of the target group, falling back to its own id as before. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
…of failing the run (stablyai#27379) * test(relay): load generator can reopen a closed phone splice instead of failing the run (--splice-retry-max-seconds) * fix(relay-load): declare a reconnect after an established control, as desktops do Without it the director's sticky lane is never reached, so a cell restart under load cannot exercise the sticky re-booking. * fix(relay-load): close a control after 75 s of silence, as desktops do The load balancer keeps a dead backend's client websocket open, so a stopped cell's simulated hosts never noticed and never re-assigned.
…her browsers (stablyai#27371) * fix(browser): double-click in the address bar selects a word, like other browsers The click that focuses the address bar selects the whole URL. Chromium treats a press inside a selection as a possible text drag, so the second press of a double-click never picked a word, and re-clicking a blurred bar restored its old selection instead of selecting all. Clear that selection before the press (focusing first, as Firefox's urlbar does) so single, double and triple clicks match Chrome and Firefox. * refactor(browser): keep address-bar click selection to one flag inside its hook The focusing press now focuses and clears the selection itself, so focus can always select all and the bar no longer shares the press flag with its focus and blur handlers. A second press only meets a range our click made, so event.detail === 2 replaces the select-all flag.
… fourth (stablyai#27403) Staging auth mints principal sets for shards 0-3, so the second hosted runner's shards 4-7 were refused. Their hosts stay distinct by peer index; each set carries the same host count as the proven 4 x 710 run.
…i#26849) Co-authored-by: m4air <m4air@Mac.localdomain> Co-authored-by: noan98 <187374956+noan98@users.noreply.github.com>
…h the desktop (hidden) (stablyai#26435) * feat(runtime): let a paired desktop register its phones as delegated mobile devices A desktop paired to this runtime can now call pairing.delegatedMobileDevice.sync to make each of its phones an ordinary mobile-scope device it owns, so the phone mirrors that host through the desktop. Children are never pending, inherit the parent's reach, and die with the parent's grant. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * refactor(runtime): move the mobile device revoke primitive below dispatch Dispatch binds the caller-bound delegated-device context, so the revoke path it needs now lives in a layer under it as a pure move; this drops the abstract method seam. No behaviour change. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(runtime): keep a runtime grant until its delegated phones are revoked A failed child cleanup now leaves the parent in place so a retry can still find the children, and revokeRuntimeAccess lives beside the revoke it cascades to. Adds restart, rename and bounds coverage, shares credential minting, and drops the timing-dependent census. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(runtime): fail a delegated sync whose dropped phone cannot be revoked Matches the parent revoke: a child whose cleanup could not be saved is kept and the sync errors before minting anything, so the desktop retries instead of believing the server matches its list. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * feat(mobile): relay a paired phone's server-workspace requests through the desktop A phone mirrors its desktop, including workspaces on the desktop's configured servers. The desktop now forwards a request that names `executionHost: runtime:<env>` to that server over one socket per (phone, server), signed in as the phone's delegated device; replies pass through untouched except stream ids. Hidden until the phone client ships: the relay capability is not advertised yet. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * docs(mobile): state the desktop relay contract in its entry module main dropped docs/reference, so the relay's contract lives beside the code. Also pins that a server whose delegated sync fails (e.g. delegated_device_revoke_failed) is unavailable until a later request syncs it again. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): pin the relay's configured-server adapter The production hosts adapter was only typechecked; this pins that it resolves configured servers by exact id only (never a name or a removed server), fences by pairing revision and runtime identity, and stops reporting retirements once unsubscribed. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * refactor(mobile): tighten the desktop relay after elegance review Reuses the shared E2EE socket opener, folds relay state into fewer fields, never sends a request whose link ended while opening, passes failure codes through, relays only this desktop's own phones, and drops the unused host-state reader until S2 needs one. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): bound relay frames and stop sticky relay verdicts Restores the 8 MiB frame cap on every remote runtime socket, re-checks an update-needed server on each new link so an in-place upgrade relays, answers a phone the server just declined without syncing again, and ends a link whose mirrored capability update cannot be queued. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): run targeted desktop-owned calls on the desktop The phone tags every call on a workspace screen with its execution host, so a paired-desktop method carrying `runtime:<env>` now runs locally instead of failing 'forbidden'. The Mac stays the only reader of the routing table. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(ci): run the remote-runtime WebSocket opener test on Node Under the bun vitest project, `ws` resolves to Bun's built-in client, which ignores maxPayload, so the frame-cap test hung. The opener only ships on Node/Electron, so run its test in the Node runtime project like its siblings. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * refactor(mobile-relay): derive the phone allowlist from the routing table Adding a phone method now takes one edit (its route). A server-targeted call on a legacy non-E2EE socket is refused instead of running locally, and only execution-host methods enter the relay. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
…tablyai#27396) Compose typed file search and listing with narrow workspace/store dependencies and preserve the existing runtime command facade. Move the mobile inventory cache to its owner, remove four inheritance classes and six unchecked-file entries, and delete unused process bookkeeping while retaining producer-owned cancellation. Preserve file access, provider capability fallbacks, host isolation, remote negotiation, budgets and cache lifetime. Independent audits verify moved method bodies, parameters, defaults and literals. Local tests and matched native Windows controls, typechecks, quality checks and CI qualification are recorded in the PR.
…can run it (stablyai#27307) (stablyai#27404) Co-authored-by: m4air <m4air@Mac.localdomain>
Co-authored-by: m4air <m4air@Mac.localdomain>
… scans (stablyai#26941) Runtime setup create/update/delete changed the store without telling open clients, and a path/kind change or deletion left cached worktree scans answering for the old registration. Publish reposChanged after each successful mutation and run the shared worktree-change invalidators on path/kind changes and deletes. Fixes stablyai#26562 Co-authored-by: m4air <m4air@Mac.localdomain>
…ailure (stablyai#26940) ssh2 can deliver the exec ack and the exit status in one chunk, so 'exit' fires before the browse handler subscribes. Fall back to the status that 'close' repeats when no exit was observed. Co-authored-by: m4air <m4air@Mac.localdomain> Co-authored-by: suharvest <suharvest@users.noreply.github.com>
…or the phone (stablyai#26442) * feat(mobile): show SSH host health on the workspace row host badge Rows on an SSH host the desktop cannot reach looked healthy on the phone. The badge now adds the desktop sidebar's own word ("devbox · Disconnected"), derived by the same shared function from the connection status ssh.listTargetSummaries already sends; old desktops keep today's badge. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * feat(mobile): name an unhealthy SSH host even in a single-host list The desktop card shows SSH status on any host count, so the phone now reads target summaries whenever a repo is on SSH and badges an unhealthy host alone too. Labels and health share one target-to-host keying, and a hook-level test pins the reply-to-row wiring. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): clear stale SSH host badges and announce them without the dot A catalog that no longer spans hosts or holds an SSH repo now clears the health and label maps, so a host that left cannot keep badging rows. The badge gets a screen-reader label ("devbox, Disconnected") instead of reading the middle dot. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * feat(runtime): let a paired desktop register its phones as delegated mobile devices A desktop paired to this runtime can now call pairing.delegatedMobileDevice.sync to make each of its phones an ordinary mobile-scope device it owns, so the phone mirrors that host through the desktop. Children are never pending, inherit the parent's reach, and die with the parent's grant. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * refactor(runtime): move the mobile device revoke primitive below dispatch Dispatch binds the caller-bound delegated-device context, so the revoke path it needs now lives in a layer under it as a pure move; this drops the abstract method seam. No behaviour change. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(runtime): keep a runtime grant until its delegated phones are revoked A failed child cleanup now leaves the parent in place so a retry can still find the children, and revokeRuntimeAccess lives beside the revoke it cascades to. Adds restart, rename and bounds coverage, shares credential minting, and drops the timing-dependent census. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(runtime): fail a delegated sync whose dropped phone cannot be revoked Matches the parent revoke: a child whose cleanup could not be saved is kept and the sync errors before minting anything, so the desktop retries instead of believing the server matches its list. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * feat(mobile): relay a paired phone's server-workspace requests through the desktop A phone mirrors its desktop, including workspaces on the desktop's configured servers. The desktop now forwards a request that names `executionHost: runtime:<env>` to that server over one socket per (phone, server), signed in as the phone's delegated device; replies pass through untouched except stream ids. Hidden until the phone client ships: the relay capability is not advertised yet. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): type the single-host health fixtures inline so the test tsc passes Fixtures hoisted into consts widened their health maps to string, which the now-required hostHealthById rejects under the tests-typecheck ratchet; passing them inline keeps the union. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * docs(mobile): state the desktop relay contract in its entry module main dropped docs/reference, so the relay's contract lives beside the code. Also pins that a server whose delegated sync fails (e.g. delegated_device_revoke_failed) is unavailable until a later request syncs it again. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): pin the relay's configured-server adapter The production hosts adapter was only typechecked; this pins that it resolves configured servers by exact id only (never a name or a removed server), fences by pairing revision and runtime identity, and stops reporting retirements once unsubscribed. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): re-record the repo-metadata goldens for SSH host health The settled metadata state now carries hostHealthById, and an SSH-only catalog now reads the target summaries so a lone unhealthy host can be badged. Scoped re-record via rpc-recording.mts. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * refactor(mobile): tighten the desktop relay after elegance review Reuses the shared E2EE socket opener, folds relay state into fewer fields, never sends a request whose link ended while opening, passes failure codes through, relays only this desktop's own phones, and drops the unused host-state reader until S2 needs one. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): bound relay frames and stop sticky relay verdicts Restores the 8 MiB frame cap on every remote runtime socket, re-checks an update-needed server on each new link so an in-place upgrade relays, answers a phone the server just declined without syncing again, and ends a link whose mirrored capability update cannot be queued. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): run targeted desktop-owned calls on the desktop The phone tags every call on a workspace screen with its execution host, so a paired-desktop method carrying `runtime:<env>` now runs locally instead of failing 'forbidden'. The Mac stays the only reader of the routing table. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * feat(mobile): list the desktop's configured servers and their last workspace rows for the phone The phone needs the same host list the desktop shows, including servers that are offline or too old to relay to. mobileRelay.hosts.list/worktrees answer from the desktop's own status and its own worktree.ps fetches, cached per server and fenced by pairing, never from relayed replies. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * refactor(mobile): list server rows through one desktop fetch, with no side effect on listing The phone now always lists a server's rows through mobileRelay.hosts.worktrees, so the desktop's own fetch is both the live source and the cache, and stamping stays in main. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): fetch a server's full workspace list within a bound, and forget removed servers The desktop now asks for the phone's full-catalog limit, answers within 5s with the last rows when a server hangs, drops a server's rows when it is removed or re-paired, and checks the server's identity after the fetch so a reply from before a re-pair is never served. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): list managed servers as the desktop sidebar does, and hide rows mid-delete A server Orca manages on an SSH host now lists once under that host's name, or not at all while the host is reached over the SSH relay, using the sidebar's own folding. Rows the server is deleting are left out at reply time, as a phone asking the server itself would see. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): keep a disconnected server's last rows, and never join a re-paired server's fetch Retirement also fires on a manual disconnect or managed-server update, which wrongly emptied the phone's list; rows are now pruned only once a server is no longer configured. Fetches are shared per server identity, so a poll after a re-pair asks the new server right away. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(ci): run the remote-runtime WebSocket opener test on Node Under the bun vitest project, `ws` resolves to Bun's built-in client, which ignores maxPayload, so the frame-cap test hung. The opener only ships on Node/Electron, so run its test in the Node runtime project like its siblings. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * refactor(mobile-relay): derive the phone allowlist from the routing table Adding a phone method now takes one edit (its route). A server-targeted call on a legacy non-E2EE socket is refused instead of running locally, and only execution-host methods enter the relay. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * refactor(mobile-relay): drop unread freshness fields from server workspace lists The phone reads only the rows, so stale, fetchedAt, totalCount and truncated were dead wire weight. The desktop still serves a server's last rows while the server is unreachable. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(runtime): classify the mobileRelay RPC namespace as host-owned main now requires every runtime RPC namespace in the host-operation ownership table; the relay-hosts methods are served by the desktop the phone is paired to.
…roxy (stablyai#26942) Use the host HTTP client for usage, reset-credit reads and redemption, keep those account requests cookie-free, and pass configured proxy settings to the native Codex probe. WSL probes keep the guest's own proxy environment. Co-authored-by: petersindex <287557875+petersindex@users.noreply.github.com> Co-authored-by: mmarabel <166927047+mmarabel@users.noreply.github.com>
…ver sticks on pulling (stablyai#27409) * fix(ssh): remove the relay-only remote workspace sync so reconnect never sticks on pulling * refactor(ssh): drop snapshot apply tokens and terminal finalization only the sync used * test: drop leftover sync-status mock --------- Co-authored-by: m4air <m4air@Mac.localdomain>
Co-authored-by: m4air <m4air@Mac.localdomain>
…rns (stablyai#26954) Co-authored-by: m4air <m4air@Mac.localdomain>
…l-list read (stablyai#27405) * fix(relay): keep seat polls off the cell-list read and report event-loop freezes The seat poller no longer waits on its database cell-list read before polling cells once it has a list, so a stalled read cannot starve the polls that feed a reserve cell's dead-man. Runtime metrics add eventLoopDelayMsMax: a single long freeze is one sample, so p99 read 68 ms across a 134 s staging freeze. Adds a pool test against a database that accepts and never answers, and a real-Postgres stop/start recovery test. * fix(relay): state the seat-poller gain precisely, justify the AddressInfo cast
…ng), adoptable deploy shape, event-loop stall alert (stablyai#27406) * fix(relay): director reaches Cloud SQL through a proxy sidecar (staging), gated event-loop stall alert On Cloud Run's built-in /cloudsql socket a connect() blocks the director's whole event loop until the connector's upstream dial ends, 10 s per attempt while the instance starts. Staging froze both directors for 134 s on 2026-10-11 and production four directors for 40-50 s on 2026-09-28. relay_director_cloud_sql_proxy_sidecar runs the Cloud SQL Auth Proxy beside the director on 127.0.0.1:5432, as the GCE cells already do, with its own TCP database URL secret. relay_event_loop_stall_alert adds the event_loop_ms_max metric and a 5 s stall alert. Both default off, so production plans unchanged; staging turns both on. * fix(relay): adopt a reviewed Terraform director shape on deploy; counter-based stall alert; cheaper probed sidecar - deploy-relay-blue-green: --shape-baseline-revision names the 0% revision a Terraform apply created; the deploy preserves that shape instead of the serving one. It must be the latest created, unserved revision on the serving image. Wired into the staging and production director workflows behind ADOPT_RELAY_DIRECTOR_TERRAFORM_SHAPE. - Event-loop stall alert is now a gated counter metric on eventLoopDelayMsMax>=5000. - Sidecar gets a /liveness probe and 0.25 vCPU (Cloud Run rejects only a total under 1 vCPU with concurrency above 1; checked with validateOnly against the API). - staging.tfvars pins the serving director's concurrency 1000 and timeout 3600 s, so the adopted apply shape does not fall to the 80 / 30 s defaults. * fix(relay): shape adoption refuses identity, topology, pruning and image changes; GOMAXPROCS=1 on the sidecar parseArguments now refuses --shape-baseline-revision with a runtime identity bootstrap, a director topology change or revision pruning, so every caller is covered, and the deploy refuses a shape baseline whose image differs from the serving one. Both director workflows also refuse the combinations up front. The proxy sidecar runs with GOMAXPROCS=1 to match its 0.25 vCPU quota. The long-hold alert comment is back above its resource.
…tablyai#27412) * ci: drop the relay Windows process-tree job from desktop packaging * ci: refuse relay-era tags in a directly dispatched mac release build --------- Co-authored-by: m4air <m4air@Mac.localdomain>
…ge (stablyai#27416) Co-authored-by: m4air <m4air@Mac.localdomain>
stablyai#27396 deleted the write-only registry that consumed it; nothing sets the option now, and its doc comment implied a cancellation path that no longer exists.
…tablyai#27419) Co-authored-by: m4air <m4air@Mac.localdomain>
* Add manual worktree storage cleanup command * Validate cleanup exclusions and preserve failure diagnostics
…on (stablyai#27422) Container devpts instances reuse pts numbers, so selecting kill targets by tty alone could SIGKILL same-uid processes in other namespaces. On Linux, require each candidate's /proc session id to equal the PTY shell's pid. Fixes stablyai#27337 Co-authored-by: m4air <m4air@Mac.localdomain>
Fold the empty owner-routing baseline into the focus-read ratchet. The three deleted focus-routing helpers stay banned as a forbidden-name list. Co-authored-by: m4air <m4air@Mac.localdomain>
…ablyai#27423) A layout stream's cleanup is now registered under a key that includes the subscriber's connection, and layout.unsubscribe builds that key from the caller's own connection, matching session.tabs.unsubscribe. Another connection can no longer end a stream by passing its id.
…away (stablyai#23459) * fix(linux): stop the AppImage crashpad handler before the mount goes away Crashpad's handler runs from the AppImage FUSE mount and outlives the main process, so it SIGBUSes once the runtime unmounts it. On a validated AppImage running from its own mount, SIGKILL this launch's handler from the main process's exit hook. Fixes stablyai#22542 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs: clarify AppImage Crashpad exit hook --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The relay client is gone, so the three relay-ladder cells and the @orcad-cli-relay-kept matrix case could not run. Delete them, their last helper (ssh-test-target-connection), a stale path filter and the unused build:relay step; rename the runner to invoke-windows-host-cells.ps1. Co-authored-by: m4air <m4air@Mac.localdomain>
…tablyai#27424) * feat(daemon): observe-only fork gate for automatic terminal restores * fix(daemon): keep a canceled fork charged until reaped; never strand a user resume --------- Co-authored-by: m4air <m4air@Mac.localdomain>
…stablyai#27435) Co-authored-by: Sergey Tiron <1ivi41337@gmail.com>
…blyai#27428) * feat(daemon): pace automatic terminal restores on headless hosts * fix(daemon): cap the paced-restore window below remote client timeouts --------- Co-authored-by: m4air <m4air@Mac.localdomain>
Removes seven modules that only their own tests import, dead orcad exports and protocol constants, spells orcad's configuration exit code once, reuses the shared errorMessage helper, and drops links to the deleted docs/reference folder. Co-authored-by: m4air <m4air@Mac.localdomain> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Nothing mints an SSH-bridge credential since the relay client was removed (stablyai#27214), so the 'ssh-bridge' caller kind, its host binders, result filter, the CLI bridge-mode policy and the ORCA_SSH_BRIDGE_CREDENTIAL env read could never run. The allowRemoteCliControl target field stays for older peers. Co-authored-by: m4air <m4air@Mac.localdomain>
…yai#27433) * feat(daemon): pause automatic restores after a same-boot crash * fix(daemon): per-version crash marker, owner-only writes, orderly login retirement, no Windows boot-time guess --------- Co-authored-by: m4air <m4air@Mac.localdomain>
…chable macOS keychain (stablyai#27441) * fix(serve): report secrets unavailable instead of hanging on an unreachable macOS keychain * fix(serve): classify keychain probe by exit code, skip under a mock keychain, ratchet direct safeStorage imports --------- Co-authored-by: m4air <m4air@Mac.localdomain>
Co-authored-by: m4air <m4air@Mac.localdomain>
…tablyai#27437) * feat(daemon): process-growth breaker that pauses automatic restores per host and names the agent * fix(daemon): breaker trips only on sustained growth, pauses in memory, observes on desktop * fix(daemon): sample fast only while a tree is rising or tripped --------- Co-authored-by: m4air <m4air@Mac.localdomain>
…ion (stablyai#27436) Co-authored-by: Wooseong Kim <innocarpe@gmail.com>
… history seed (stablyai#27442) Co-authored-by: m4air <m4air@Mac.localdomain>
…answer (stablyai#27445) * fix(runtime): reuse cached worktree rows when the admin probe cannot answer * test(runtime): wait for the fingerprint probe to settle instead of a fixed delay --------- Co-authored-by: m4air <m4air@Mac.localdomain>
…o a pure policy (stablyai#27446) * refactor(cli): extract the serve supervisor's after-exit decision into a pure policy * refactor(cli): type the exit signal without a child_process import --------- Co-authored-by: m4air <m4air@Mac.localdomain>
…ablyai#27443) orcad now checks its flags with the same validator Electron serve and the CLI use, so contradictory pairing flags fail with the same message instead of one silently winning. The CLI launcher drops its duplicate recipe check (the CLI handler already ran the shared validator). Removes the daemon-init and cli/runtime/index re-export barrels and the parsePtySessionId / OrcadInstanceLockError re-exports; callers and mocks import the defining modules. Co-authored-by: m4air <m4air@Mac.localdomain> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…blyai#27450) The pane listener subscribed to 'orca:wake-hibernated-agents-worktree' while the dispatcher fires WAKE_HIBERNATED_AGENTS_WORKTREE_EVENT ('orca-wake-...'), so the in-place wake never ran. Extract the listener and bind it to the shared constant; the dispatcher test now drives the real listener instead of a stand-in. Co-authored-by: m4air <m4air@Mac.localdomain>
# Conflicts: # config/scripts/node-server-change-scope.test.mjs # src/main/orcad/orcad-entry.ts # src/main/runtime/rpc/methods/index.ts # src/main/runtime/runtime-rpc/runtime-rpc-lifecycle.ts # src/main/runtime/runtime-rpc/runtime-rpc-mobile-method-allowlist.ts # src/renderer/src/components/automations/use-automations-page-store-state.ts
Upstream's new relayed-stream-carrier census requires every execution-host streaming method to name how replies carry a stream id. Fork pet.speak.subscribe is now routed as execution-host (same as nativeChat), so the census failed on PR Checks shard 5/5. JSON speak events have no host-allocated stream id.
Bun aliases `fs` to `node:fs`. Upstream doc-preview-file-access reads O_NOFOLLOW at import time, so the daemon-init fs stub without `constants` failed shard 5/5 after the pet.speak carrier fix.
8 of 9 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
ELI5
This branch copies 56 new commits from upstream
stablyai/orcamainonto the fork's working branchfork-main, so the fork can later cut desktop tagv1.4.224-0. It does not merge itself, does not cut a tag, and does not deploy.What Changed
Before:
fork-main(e51d0a0ef4f, PR #18) was 55 commits behindstablyai/orcamainat brief time (6d516517115). Fetch moved upstream one more commit (#27450), so this merge is 56 commits.upstream/mainwas not an ancestor offork-main, sofork-next-desktop-tag.mjs --writerefused.After:
sync/upstream-2026-10-11cis a--no-ffmerge ofupstream/main@4468a5375f2intofork-main. Six conflicts were resolved; fork features (pet speech RPCs, orcad host-adapter extraction, automationagentFlags,activeViewEscape on the Automations page) stay. Upstream wins for SSH change-scope fixtures, mobile-relay host listing, phone-connection cleanup, folder-workspace automation targets, and files the fork does not customize.Mechanism: merge commit
71905c442dcplus notes indocs/audits/sync-notes-2026-10-11c.md.Why
The tag script's ancestor guard must stay on. A follow-up merge (this PR) is the same path as #15/#17/#18: keep fork deltas, take upstream product changes, record resolutions, let GitHub CI run the full matrix.
Linked Issue
N/A — fork maintainer sync; no upstream issue.
Visual Proof
N/A — no new fork UI. Ingests upstream commits (folder-workspace automation targets, mobile relay hosts, daemon process-growth breaker, phone wake for hibernated panes). Conflict resolutions preserve existing fork pet-speech and Automations Escape behavior.
Testing
tsc --noEmit -p config/tsconfig.tc.cli.jsonclean viaheavy-runvitest
--maxWorkers=1: node-server-change-scope (98), mobile RPC routing/allowlist/pet-speak (13), automations folder hook + orcad host adapters (7)Full
pnpm tc(node+web) and electron-builder not run locally. GitHub CI is the full matrix.Automated tests added/updated, or explained why not below
I manually tested these changes locally
AI Disclosure
Review
Agent skill upstream boundary
Notes
Coolify HOLD / no auto-deploy. Do not merge this PR from CI. Do not cut
v1.4.224-0from this PR. Do not touch Coolify. Coord will escalate the fork merge to newbie.Conflict resolutions (see
docs/audits/sync-notes-2026-10-11c.md):src/main/ssh/pinned-runtime-materializer*daemon-provider-stateand secret-protection-gapMOBILE_RELAY_HOSTS_METHODSandPET_SPEAK_METHODSclosePhoneConnectionplus fork pet-speech cleanuppet.speak.*to execution-host routesactiveViewplus upstream folder workspaces and tab/pty maps