What I'm trying to do
Run an AI SDK harness (@ai-sdk/harness + @ai-sdk/harness-claude-code) against the sandbox an eve agent already has, from inside an authored tool — so the harness works in the checkout the agent has already cloned, behind the network policy the agent has already brokered.
The two abstractions look built to meet. eve's SandboxSession is defined in terms of the AI SDK's own type (dist/src/shared/sandbox-session.d.ts):
import type { Experimental_SandboxSession as AiSdkSandbox } from "ai";
export interface SandboxSession extends Pick<AiSdkSandbox,
"run" | "spawn" | "readFile" | "readBinaryFile" | "readTextFile"
| "writeFile" | "writeBinaryFile" | "writeTextFile"> { … }
and the harness's provider contract extends the same base:
interface HarnessV1NetworkSandboxSession extends Experimental_SandboxSession { … }
The gap
HarnessV1NetworkSandboxSession needs three members SandboxSession doesn't have:
| Required by the harness |
On eve's SandboxSession |
| the 8 I/O methods |
✅ identical types |
id |
✅ |
setNetworkPolicy? |
✅ |
defaultWorkingDirectory |
derivable from resolvePath |
ports: readonly number[] |
❌ |
getPortUrl({ port, protocol }) |
❌ |
stop() |
❌ |
getPortUrl is the load-bearing one. Claude Code and Codex are bridge-backed adapters: they start a bridge process inside the sandbox and connect to it over a publicly-reachable URL for a sandbox-exposed port. Without a way to resolve that URL there is no way to implement HarnessV1SandboxProvider over an eve sandbox at all.
Notably the creation side already works. VercelSandboxCreateOptions is
Omit<Parameters<typeof Vercel.Sandbox.create>[0],
"name" | "onResume" | "persistent" | "runtime" | "signal">
so ports is already forwarded — vercel({ ports: [4000] }) creates a sandbox with the port exposed. There's just no accessor to turn that into a URL, and no way to reach the underlying @vercel/sandbox instance to call .domain(port) directly.
What would unblock it
Exposing ports and getPortUrl on SandboxSession for backends that can support them (vercel(), microsandbox()) would be enough on its own — stop is less important, since eve owning session lifecycle seems deliberate and a provider can reasonably no-op it.
Keeping them optional would fit the existing shape: setNetworkPolicy is already documented as backend-dependent (just-bash rejects it), and ports would be the same kind of capability — present on the backends that have a firewall and a public hostname, absent on the ones that don't.
An escape hatch to the native backend handle would also work, but seems worse: it would undo the backend-agnostic design that makes /workspace mean one thing across all four backends.
Why it's worth doing
The alternative today is letting the harness create its own sandbox alongside eve's. That means two sandboxes per agent run, and the second one has none of the first's setup — so the repo has to be re-cloned into it, which means the git credential has to go into that sandbox rather than being injected at the firewall by eve's credential brokering. Losing brokering is the part that stings; it's the main reason to want the harness in eve's sandbox rather than beside it.
Versions
eve@0.25.2
@ai-sdk/harness@1.0.60, @ai-sdk/harness-claude-code@1.0.61, @ai-sdk/sandbox-vercel@1.0.60
Happy to send a PR if you think optional ports/getPortUrl on the session is the right shape.
What I'm trying to do
Run an AI SDK harness (
@ai-sdk/harness+@ai-sdk/harness-claude-code) against the sandbox an eve agent already has, from inside an authored tool — so the harness works in the checkout the agent has already cloned, behind the network policy the agent has already brokered.The two abstractions look built to meet. eve's
SandboxSessionis defined in terms of the AI SDK's own type (dist/src/shared/sandbox-session.d.ts):and the harness's provider contract extends the same base:
The gap
HarnessV1NetworkSandboxSessionneeds three membersSandboxSessiondoesn't have:SandboxSessionidsetNetworkPolicy?defaultWorkingDirectoryresolvePathports: readonly number[]getPortUrl({ port, protocol })stop()getPortUrlis the load-bearing one. Claude Code and Codex are bridge-backed adapters: they start a bridge process inside the sandbox and connect to it over a publicly-reachable URL for a sandbox-exposed port. Without a way to resolve that URL there is no way to implementHarnessV1SandboxProviderover an eve sandbox at all.Notably the creation side already works.
VercelSandboxCreateOptionsisso
portsis already forwarded —vercel({ ports: [4000] })creates a sandbox with the port exposed. There's just no accessor to turn that into a URL, and no way to reach the underlying@vercel/sandboxinstance to call.domain(port)directly.What would unblock it
Exposing
portsandgetPortUrlonSandboxSessionfor backends that can support them (vercel(),microsandbox()) would be enough on its own —stopis less important, since eve owning session lifecycle seems deliberate and a provider can reasonably no-op it.Keeping them optional would fit the existing shape:
setNetworkPolicyis already documented as backend-dependent (just-bashrejects it), and ports would be the same kind of capability — present on the backends that have a firewall and a public hostname, absent on the ones that don't.An escape hatch to the native backend handle would also work, but seems worse: it would undo the backend-agnostic design that makes
/workspacemean one thing across all four backends.Why it's worth doing
The alternative today is letting the harness create its own sandbox alongside eve's. That means two sandboxes per agent run, and the second one has none of the first's setup — so the repo has to be re-cloned into it, which means the git credential has to go into that sandbox rather than being injected at the firewall by eve's credential brokering. Losing brokering is the part that stings; it's the main reason to want the harness in eve's sandbox rather than beside it.
Versions
eve@0.25.2@ai-sdk/harness@1.0.60,@ai-sdk/harness-claude-code@1.0.61,@ai-sdk/sandbox-vercel@1.0.60Happy to send a PR if you think optional
ports/getPortUrlon the session is the right shape.