feat(acp): hosts install a managed runtime over RPC, and mark agents whose program Orca provides (#29024)

* feat(acp): hosts install a managed runtime over RPC, and mark agents whose program Orca provides

managedRuntime.status / managedRuntime.install / managedRuntime.cancel, negotiated by
managed-runtime.install.v1, act on the host that receives them, the one that runs the agent's
chats, with no workspace needed. A status read naming the revision it shows is a long-poll on its
own call-queue lane, so every client sees another's start, progress and outcome.
agentSession.agents marks an agent whose program Orca provides on that host.

* fix(runtime): install status says a copy outside Orca's runs only when a start would really run it

* fix(runtime): install status is pushed to subscribers instead of long-polled, and Cancel answers with the outcome

managedRuntime.subscribe {agent} sends a full snapshot on subscribe, then one
snapshot per install state change, and closes with the transport. It replaces
the managedRuntime.status long-poll and its slot registrations.
This commit is contained in:
Brennan Benson
2026-10-16 18:47:12 -07:00
committed by GitHub
parent 07558ba8ce
commit 634ca45fbb
14 changed files with 261 additions and 10 deletions
@@ -17,7 +17,6 @@ import {
} from './managed-runtime-release'
import { installFailureText, installRuntimeRelease } from './managed-runtime-release-install'
const STATUS_WAIT_MS = 25_000
const DEADLINE_MS = 45 * 60_000
export type ManagedRuntimeInstallationDeps = {
@@ -46,14 +45,13 @@ export class ManagedRuntimeInstallation {
this.mutation = this.reclaim()
}
/** At once without `shown`, else once the revision passes it or after a bounded wait. Disk and
* PATH are reread on a first read and when the phase changes, never for byte progress alone. */
async status(shown?: number, signal?: AbortSignal): Promise<ManagedRuntimeInstallState> {
await this.state.waitForChange(shown, STATUS_WAIT_MS, signal)
/** Disk and PATH are reread on a `fresh` read and when the phase changes, never for byte
* progress alone. */
async status(fresh = true): Promise<ManagedRuntimeInstallState> {
const { value: operation, revision } = this.state.get()
const phase = `${operation?.id}:${operation?.phase}`
let facts = this.facts
if (shown === undefined || facts?.phase !== phase) {
if (fresh || facts?.phase !== phase) {
const read = this.readInstalledFacts()
facts = { phase, read }
this.facts = facts
@@ -65,6 +63,11 @@ export class ManagedRuntimeInstallation {
return { agent: this.agent, revision, ...(await facts.read), operation }
}
/** Calls `onChange` after every state revision until the returned unsubscribe. */
subscribe(onChange: () => void): () => void {
return this.state.subscribe(onChange)
}
private async readInstalledFacts(): Promise<InstalledFacts> {
const pin = this.runtime.pin()
const { release, broken, unreadable } = resolveInstalledRuntimeRelease(this.root, pin)