Skip to content

Declare agent-host Session views in the Runtime–Harness protocol - #350

Merged
SaladDay merged 4 commits into
feature/agent-outside-sandboxfrom
aos/view-declaration
Oct 1, 2026
Merged

SaladDay merged 4 commits into
feature/agent-outside-sandboxfrom
aos/view-declaration

Conversation

@SaladDay

@SaladDay SaladDay commented Oct 1, 2026 •

Copy link
Copy Markdown
Collaborator

Lane L5p of the agent-outside-sandbox workstream. It is a Runtime–Harness protocol change: a Harness declares how it runs in an agent-host Session view. Its loop runs on the agent host, its file and process operations go to the sandbox, and its model and MCP traffic goes through the Session gateway. Every adapter declares View: nil in this PR; the adapter lanes fill it in.

Protocol (apps/daemon/internal/agent/harness.go, registry.go)

  • Runtime.View *View:
    • the closure mounts, overlays and masks;
    • the local executables, shims and shim paths;
    • forwarded environment;
    • the proxy mode (ViewProxyEnv or ViewProxyNone, with an invalid zero value);
    • a ViewExecutorFactory that receives a ViewSession (home, proxy and Launch).
  • View.Validate() checks a declaration without touching the host, and an invalid declaration gives ErrInvalidView.
  • Registry.RegisterView validates the view and wraps its factory so Prepare runs first, like the other factories.
  • Registry.ResolveView returns a copy. A kind without a view gives ErrUnsupportedOperation; an unknown kind gives ErrUnsupportedKind.
  • TestPublicHarnessContractDeclarations requires every declaration to state View explicitly.

Supporting changes

  • clirunner:
    • Handle, HandleOptions and FromHandle provide a handle-backed process. Cancel sends TERM, then closes the handle after KillTimeout.
    • Process.ExitCode() is the exit-status accessor, so Codex no longer reads exec.Cmd.
  • sessionview:
    • Spec.StagingParent is required, an existing absolute directory, with no temp-dir fallback.
    • PrivateRoot and ShimDir are exported.
  • harness-onboarding.md:
    • A registration-order row for RegisterView.
    • A "Run in an agent-host view" section that links to the credential gateway rule.

Checks

  • Tests on agent, clirunner, codex (race), claudesdk, mcode and sessionview.
  • The privileged sessionview tests in a container.
  • Vet and gofmt; linux, darwin and windows builds.

Blind review fixes

Two blind review rounds; their fixes are in the three follow-up commits.

  • Credential handoff:
    • The agent host resolves MCP once, from the request and LocalEnvironment.MCP, and passes it in ViewSession.MCP: HTTP bindings at gateway URLs with no credentials.
    • The view request carries no other MCP source.
    • Declaration.ConnectionOptions marks native options that carry connections (Codex and MiniMax: mcp_servers, env). The view rejects them with ErrViewHandoff.
    • Before the factory runs, the prepared model provider must be a loopback gateway URL with modelprovider.Placeholder.
  • Validation: ForwardEnv rejects proxy variables and names the view owns (HOME, PATH, TMPDIR, LANG, LD_LIBRARY_PATH).
  • Layout: the view layout is defined once in harness.go (ViewPrivateRoot, ViewShimName, ViewHomeName, ViewRunName, ViewReserved). sessionview imports it, and agent no longer imports sessionview.
  • Handle:
    • Cancelling an exited process does nothing, and a cancel racing a successful exit leaves it a success.
    • Wait closes stdin, stdout and stderr once.
    • An interrupted exit 0 returns the context error, as exec.CommandContext does.
  • Descendant grace: View.Signal reaches every process in the view. When the leader exits, remaining processes get one TERM, never a second one, and the remaining grace.

@SaladDay
SaladDay force-pushed the aos/view-declaration branch from 1bbb307 to 3984693 Compare October 1, 2026 02:14
@SaladDay
SaladDay merged commit ae76864 into feature/agent-outside-sandbox Oct 1, 2026
1 check passed
@SaladDay
SaladDay deleted the aos/view-declaration branch October 1, 2026 02:14
Runtime gains a View declaration: closure mounts, overlays, masks, the
local exec allowlist, shims, forwarded variables, the proxy mode and a
view Executor factory. View.Validate checks it without touching the host,
Registry.RegisterView records it with configuration validation, and
ResolveView returns it or wraps ErrUnsupportedOperation. Codex, Claude
and MiniMax declare View: nil, and the declaration test requires the
field in every adapter.

clirunner.Process gains a handle-backed constructor, FromHandle, and an
ExitCode accessor; Codex reads the exit through the accessor. The
sessionview Spec takes a StagingParent instead of the system temporary
directory, and exports PrivateRoot and ShimDir for the declaration.

Harness onboarding documents the View field, RegisterView and running
in an agent-host view.
Cancelling a handle-backed process that has ended does nothing, so the
handle is not closed before Wait drains output, and Handle.Close never
closes the stdio ends the Process owns. Wait closes stdin, stdout,
stderr and the handle once, and returns the context error when
cancellation interrupted a process that then exited 0, keeping the exit
code, as exec.CommandContext does.
The agent host resolves the Session's MCP once, from the public
declarations and the installed Environment MCP, into ViewSession.MCP.
HTTP bindings point at the gateway with no bearer and no headers, the
request carries no MCP, and the registered view factory rejects a
session or request that breaks this.

harness.go is the one definition of the view layout: ViewPrivateRoot,
the bin, home and run names, /proc, /dev and ViewReserved. sessionview
imports it and drops its own constants, and agent no longer imports
sessionview. ForwardEnv rejects the variables the view or broker sets,
including proxy variables in any case, and the tool environment wins
over a forwarded variable.

A view signal reaches every process in the view, and when the process
exits while others remain, the launcher sends them TERM and waits up to
Process.Grace before it exits.
Declaration.ConnectionOptions marks the AgentOptions keys that carry MCP,
endpoints, credentials or environment values; Codex and MiniMax mark
mcp_servers and env. The view Executor wrapper checks once, before the
factory, that the prepared model provider is the loopback gateway with the
placeholder key, that no connection option is set and that MCP arrives only
credential-free in ViewSession.MCP, and returns ErrViewHandoff otherwise.

Handle.Signal reports a process that has exited with os.ErrProcessDone, so
a cancel marks the process interrupted only when its TERM reached the running
process. sessionview tracks the Harness's exit apart from the drain: the
launcher acknowledges each signal and delivers none once the Harness is
reaped, View.Signal then returns ErrExited, and the drain keeps the grace
remaining since an earlier TERM without sending a second one.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant