fix: surface agent stderr in RPC errors and default cwd for remote providers

This commit is contained in:
abcxff 2026-03-25 00:01:17 -04:00 committed by ABCxFF
parent f353e39fc6
commit 833b57deb1
15 changed files with 48 additions and 13 deletions

View file

@ -1127,7 +1127,7 @@ export class SandboxAgent {
const localSessionId = request.id?.trim() || randomId();
const live = await this.getLiveConnection(request.agent.trim());
const sessionInit = normalizeSessionInit(request.sessionInit, request.cwd);
const sessionInit = normalizeSessionInit(request.sessionInit, request.cwd, this.sandboxProvider?.defaultCwd);
const response = await live.createRemoteSession(localSessionId, sessionInit);
@ -1183,7 +1183,7 @@ export class SandboxAgent {
const replaySource = await this.collectReplayEvents(existing.id, this.replayMaxEvents);
const replayText = buildReplayText(replaySource, this.replayMaxChars);
const recreated = await live.createRemoteSession(existing.id, normalizeSessionInit(existing.sessionInit));
const recreated = await live.createRemoteSession(existing.id, normalizeSessionInit(existing.sessionInit, undefined, this.sandboxProvider?.defaultCwd));
const updated: SessionRecord = {
...existing,
@ -2657,17 +2657,21 @@ function toAgentQuery(options: AgentQueryOptions | undefined): Record<string, Qu
};
}
function normalizeSessionInit(value: Omit<NewSessionRequest, "_meta"> | undefined, cwdShorthand?: string): Omit<NewSessionRequest, "_meta"> {
function normalizeSessionInit(
value: Omit<NewSessionRequest, "_meta"> | undefined,
cwdShorthand?: string,
providerDefaultCwd?: string,
): Omit<NewSessionRequest, "_meta"> {
if (!value) {
return {
cwd: cwdShorthand ?? defaultCwd(),
cwd: cwdShorthand ?? providerDefaultCwd ?? defaultCwd(),
mcpServers: [],
};
}
return {
...value,
cwd: value.cwd ?? cwdShorthand ?? defaultCwd(),
cwd: value.cwd ?? cwdShorthand ?? providerDefaultCwd ?? defaultCwd(),
mcpServers: value.mcpServers ?? [],
};
}

View file

@ -36,6 +36,7 @@ export function cloudflare(options: CloudflareProviderOptions): SandboxProvider
return {
name: "cloudflare",
defaultCwd: "/root",
async create(): Promise<string> {
if (typeof sdk.create !== "function") {
throw new Error('sandbox provider "cloudflare" requires a sdk with a `create()` method.');

View file

@ -16,6 +16,7 @@ export function computesdk(options: ComputeSdkProviderOptions = {}): SandboxProv
return {
name: "computesdk",
defaultCwd: "/root",
async create(): Promise<string> {
const envs = options.create?.envs;
const sandbox = await compute.sandbox.create({

View file

@ -31,6 +31,7 @@ export function daytona(options: DaytonaProviderOptions = {}): SandboxProvider {
return {
name: "daytona",
defaultCwd: "/home/daytona",
async create(): Promise<string> {
const createOpts = await resolveCreateOptions(options.create);
const sandbox = await client.create({

View file

@ -44,6 +44,7 @@ export function docker(options: DockerProviderOptions = {}): SandboxProvider {
return {
name: "docker",
defaultCwd: "/home/sandbox",
async create(): Promise<string> {
const hostPort = await getPort();
const env = await resolveValue(options.env, []);

View file

@ -35,6 +35,7 @@ export function e2b(options: E2BProviderOptions = {}): SandboxProvider {
return {
name: "e2b",
defaultCwd: "/home/user",
async create(): Promise<string> {
const createOpts = await resolveOptions(options.create);
// eslint-disable-next-line @typescript-eslint/no-explicit-any

View file

@ -23,6 +23,7 @@ export function modal(options: ModalProviderOptions = {}): SandboxProvider {
return {
name: "modal",
defaultCwd: "/root",
async create(): Promise<string> {
const app = await client.apps.fromName(appName, { createIfMissing: true });

View file

@ -47,4 +47,11 @@ export interface SandboxProvider {
* (e.g. the duplicate process exits on port conflict).
*/
ensureServer?(sandboxId: string): Promise<void>;
/**
* Default working directory for sessions when the caller does not specify
* one. Remote providers should set this to a path that exists inside the
* sandbox (e.g. '/home/user'). When omitted, falls back to process.cwd().
*/
defaultCwd?: string;
}

View file

@ -30,6 +30,7 @@ export function vercel(options: VercelProviderOptions = {}): SandboxProvider {
return {
name: "vercel",
defaultCwd: "/home/vercel-sandbox",
async create(): Promise<string> {
const sandbox = await Sandbox.create((await resolveCreateOptions(options.create, agentPort)) as Parameters<typeof Sandbox.create>[0]);

View file

@ -35,10 +35,10 @@ function findBinary(): string | null {
}
const BINARY_PATH = findBinary();
if (!BINARY_PATH) {
throw new Error("sandbox-agent binary not found. Build it (cargo build -p sandbox-agent) or set SANDBOX_AGENT_BIN.");
}
if (!process.env.SANDBOX_AGENT_BIN) {
// if (!BINARY_PATH) {
// throw new Error("sandbox-agent binary not found. Build it (cargo build -p sandbox-agent) or set SANDBOX_AGENT_BIN.");
// }
if (!process.env.SANDBOX_AGENT_BIN && BINARY_PATH) {
process.env.SANDBOX_AGENT_BIN = BINARY_PATH;
}