NAVIGATION
ELOWEN DOCUMENTATION

Last updated: 10 October 2026

Browse documentation · Managed project execution
Developer reference

Managed project execution

SandboxControl.runCommand(input, actor?, options?) executes one bounded command over the plugin's prepareExecution result. Core supplies this method when resolving the Sandbox control; providers implement preparation, not a second runner. Input is the existing command/cwd/leaseKind/projectRef shape. An explicit actor carries accountUserId and roots and retains preparation's confinement and tenancy rules. Without an actor the existing turn scope applies. Example: sandbox.runCommand({ command: { type: 'argv', file: 'git', args: ['status'] }, cwd, leaseKind: 'editor', projectRef }, { accountUserId, roots: [] }, { timeout: 30000, outputLimit: { combined: 8 * 1024 * 1024 } }).

Core owns capture, 5-second lease heartbeats, cancellation and release. The default timeout is 10 seconds and the default limit is 1 MiB per stream; callers may instead specify a combined byte limit. Decode and sanitize captured stdout/stderr after collection. Completed commands resolve with stdout, stderr, code and signal, including nonzero exits. Timeout, abort, overflow, transport and cleanup failures reject with SandboxCommandError and sanitized partial output; they never invent an exit code. Cleanup failures remain failures. Missing Sandbox resolves undefined and callers report unavailable. This seam is for one-shot commands such as Editor Git reads; long-lived LSP services retain prepareExecution's raw session.

SandboxCommandOptions.onStart is an optional synchronous callback after preparation, returned-target validation and initial abort rejection, immediately before starting the command. Without it, execution is unchanged. A callback failure rejects the command and retains cancellation and lease cleanup; it receives no credential or subprocess handle. GitHub bundle creation uses it to mark guest bundle ownership only after preparation is accepted. Refused preparation needs no guest cleanup; an attempted command may leave temporary bytes. Completed nonzero commands release their lease without being cancelled again.

Managed project execution

The Sandbox control's prepareExecution() returns the union SandboxPreparedExecution: LocalPreparedExecution for direct or confined host execution, or ManagedPreparedExecution for a managed Project. Managed preparation exposes start(): Promise<ManagedExecutionSession> and cancel(); the session provides stdin, stdout, stderr, and a settled closed promise. The session that start() resolves to is ManagedExecutionSession: stdin, stdout, stderr and closed (src/plugins/api.ts, ManagedExecutionSession). Managed consumers must use this start() contract and must not expect the { launch, stdin, cancel } shape, which belongs only to LocalPreparedExecution (direct or confined execution, src/plugins/api.ts).

The host supplies privilegedTransport to buildBrainCore explicitly; the daemon forwards it through buildApp. Without it, Sandbox machine-runtime calls reject locally with no privileged transport in this process, and daemon-mode Sites gateway calls report unavailable with that detail. Runners receive only the machine gateway: the delegated runner builds its core with migrate: false (src/subagent/runner.ts), which leaves the plugin-service manager and the published Sites control null (src/daemon/brainCore.ts). Registry loading never deactivates the published gateway because Sites is absent, disabled, or failed to load. These controls still enforce their existing consumer restrictions and stamp their own domains; a supplied transport does not broaden plugin access.

The machine runtime gateway runs managed sessions through the persistent privileged worker. Worker protocol 2 uses framed control messages plus direct socket-stdio attachment with exec-start, exec-accepted, exec-attach, exec-started, exec-exit, and exec-cancel. onInterrupted records interruption before the execution promises settle. The core-only sandboxArtifacts control is composed with the Sandbox control only inside core; plugins cannot resolve it directly.