Run agent-host Sessions in per-Session views - #360
Merged
Merged
Conversation
This comment has been minimized.
This comment has been minimized.
SaladDay
force-pushed
the
aos/agent-host
branch
from
October 1, 2026 04:19
aaab09e to
c33527c
Compare
The gateway's MCP config is now the effective agent.MCPBinding list, so installed HTTP bindings reach it with their bearer token and HTTP headers. The MCP relay injects the bearer and each header in place of the Harness's credential headers and same-named headers, and withholds every injected value from response headers and trailers. Non-HTTP bindings, invalid headers and a header that would replace the bearer are rejected.
Config.Proxy selects the generic proxy listener. Without it nothing listens at ProxyPort and Endpoints.Proxy is empty, so a ViewProxyNone view has no generic proxy; the model and MCP listeners keep their fixed ports either way.
Add apps/daemon/internal/agenthost: Run admits a Session before any effect, allocates its uid and Session directory, points the model provider and HTTP MCP at the Session's credential gateway and calls the kind's view Executor factory. Each ViewSession.Launch builds one sessionview view over the world that worldfs serves from the Session's Link attachment, with the gateway in the view's network namespace. The agent host owns the attachment's lease and fails the Session when the attachment closes, a Link request fails without retry or the world is lost. Teardown releases the Executor, the view, the process broker, the attachment, the Session directory and the uid in that order. Sweep removes Session directories a previous agent host left. Other platforms return ErrUnsupported. The process broker stays behind a local seam until the broker lands. clirunner exports DefaultKillTimeout so a view launch uses the same default as Start and FromHandle. Tests: admission rejects before any dial or directory, the view Executor receives the gateway request, Sweep, and a privileged suite (OAC_TEST_AGENTHOST=1) that runs a Session against oac-sandbox-io.
Add the agenthost row to the repository map and state, where the effective MCP bindings are described, that an agent-host view's gateway holds HTTP binding credentials.
The gateway now rejects, before the Session starts, an HTTP MCP binding that injects a bearer token or any HTTP header over a non-https server URL, so no injected value crosses the sandbox's network in plaintext. An MCP listener serves only its server URL's path. The Harness's URL carries no query, since a query may hold a credential; the listener relays each request to exactly the server URL, query included, and refuses a request with a query (400) or for another path (404). model-execution.md states both rules under Credential gateway.
- Sweep sends SIGKILL, through a pidfd after rereading the uids, to every process whose real, effective, saved or fs uid lies in Config.UIDs, rescans /proc until none remains and returns ErrTeardown after a bound. Allocation skips a uid any running process holds. The package comment states the residual races. - A view's owning handle ends the Session's ownership inside Wait: the gateway stops, a lost world or a world whose Stop failed fails the Session as ErrWorld, and the view slot is freed before the clirunner.Process reports the end, so an immediate relaunch finds the slot free. A world Stop error after a failed sessionview.Start fails the Session too. Run reports every such error. - Run tears down first and decides its result afterwards, so a failure recorded while Executor.Close waits counts. The link owner ignores what the Link reports once it begins closing the attachment. - Admission validates the Open the Session will send with Link encoding, so a zero epoch, an invalid resource kind or an oversized grant is ErrInvalidSession before any effect.
A binding whose server URL has a query now needs https, like one that injects a bearer token or headers, and the gateway withholds the query and each parameter value, as sent and decoded, from response headers and trailers. A server URL with userinfo gets its own error.
Sweep and uid allocation now scan every thread, so a process whose leader thread is a zombie still counts. Sweep ends a task in a view by killing its PID namespace's init, found by walking up the task's ancestors with pidfds, so a process forked while the scan runs dies with the namespace. A task in the agent host's own namespace is killed directly and reported as ErrTeardown if it survives the bound.
…utor will not close Each Turn's output consumer starts before StartTurn, and a nil Turn's channel is closed. The Turn's Done waits for settlement and any required Executor.Close, after an Error envelope when the Turn failed, and nothing is published when Close fails. Teardown joins the forwarder, so nothing reaches Output after Run returns. A failed Close ends the views and is retried once; if it still fails, Run returns ErrTeardown and keeps the Session directory and the uid for the next Sweep. The driving mirrors dispatch's prepared execution.
A process in a view could fork and exit as each uid scan passed, so the scan could miss a view entirely. sessionview.EndLeftoverViews finds each view by its launcher, the process whose command line is exactly the launcher's and which is PID 1 of a PID namespace directly below /proc's, kills it through a pidfd confirmed after opening, and waits with a bound for it to exit; the kernel kills the rest of the view first. Sweep calls it before the uid scan, which now only ends host-namespace leftovers, and drops the namespace-init walk. procfs.end closes its pidfd on every path.
A function call waits for a result, and a view Session's Input carries only new Turns, so the result could never arrive. Admission now rejects function tools and tool search with ErrUnsupported before any effect.
Close may deliver the Turn's Done, for example when StartTurn failed but left native work running. turn now reads the forwarder's terminal again after the forwarder has finished, so that Done is published instead of being replaced by a synthesized one.
Ending a previous agent host's views by launcher command line and PID namespace cannot prove ownership, and an exiting launcher is already invisible to the scan. Restart recovery returns with an owned cgroup per view; until then the agent host recovers nothing an earlier agent-host process left, and a new one must not reuse its StateDir or UIDs. Sweep, sessionview.EndLeftoverViews and the /proc kill and wait code go. Allocation keeps its thread scan, which only detects a uid conflict. A Session whose Executor will not close keeps its directory, and its uid stays in use until the agent host exits.
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.
Lane L5a (plan row 9b): the
agenthostlibrary. It assembles the merged pieces into Sessions whose Harness runs on the agent host, in a per-Session view:View(Declare agent-host Session views in the Runtime–Harness protocol #350) and the adapter views (Declare the Codex agent-host view #354–Declare the MiniMax Code agent-host view #356);There is no CLI entry yet.
oac-daemon agent-hostarrives in M2, with the Core wire.Commits
MCPHTTPServersandLocalEnvironment.MCP) carry their bearer and headers into the gateway.contracts/agents-api/model-execution.md§ Credential gateway.gateway.Config.Proxy).apps/daemon/internal/agenthost).View;gateway.Planmust succeed.modelprovider.Placeholder. The request carries no MCP;ViewSession.MCPandViewSession.Proxytake their place.ErrViewHandoffhappen before any Link effect.Launch: it opens the Link attachment, serves the world, starts the gateway in the view's network namespace and starts the process broker.ErrAttachmentDirty).agenthost.Err*kind.clirunner.DefaultKillTimeoutis exported, and view launches use it forProcess.Grace.environments.md§ Effective bindings that links to the gateway rule.Not in this PR
processBrokerseam.ErrProcessBroker.cgroup.kill, then wait for the cgroup to be empty) in sessionview, as plan row 9c.Tests
go build ./...andgo vet ./..., plus theGOOS=darwinandGOOS=windowsbuilds of agenthost.go test -racefor agenthost, clirunner and gateway.ErrViewHandoffbefore the adapter runs;OAC_TEST_AGENTHOST=1, in a container against a staticoac-sandbox-io, with a test adapter):ErrLinkand releases everything.Review rounds
Four blind-review rounds, each in a fresh session:
dispatch, so output drains from the start and Done is held until settlement and Close;Sweepand the heuristic kill code were removed (4338269). Allocation still skips any uid that a live thread holds.