Summary
The Orchestra project needs to use exec_server as a DeepAgents BaseSandbox backend. The current exec_server has compatibility gaps that need to be addressed in ubuntu/index.js.
Orchestra spec: ruska-ai/orchestra — specs/spec-d-mcp-sandbox.md (section: "Required exec_server Changes")
Gap Analysis
| What BaseSandbox needs |
exec_server currently provides |
Gap |
ExecuteResponse(output, exit_code: int, truncated: bool) |
Single text blob: "stdout:\n...\nstderr:\n...\nexit_code: N" |
exit_code embedded in text, not structured |
upload_files([(path, bytes)]) |
Not supported |
No file upload tool |
download_files([path]) |
Not supported |
No file download tool |
id property |
Not exposed |
No sandbox identifier |
Tool param: cmd |
Tool param: cmd |
Match |
Key insight: BaseSandbox's inherited methods (read, write, edit, grep_raw, glob_info, ls_info) construct Python heredoc shell commands and pass them to execute(). They depend on exit_code being a proper integer field — they do not parse it from output text.
Required Changes
a) Return structured exit_code
Change execCommandHandler to return exit_code as structured _meta metadata, not embedded in text:
// Current (text-embedded):
output.push(`exit_code: ${error.code ?? 1}`);
// Proposed (structured via _meta):
return {
content: [
{ type: "text", text: output.join("\n") || "(no output)" }
],
_meta: {
exit_code: error?.code ?? 0,
truncated: false
}
};
Text output should contain only stdout/stderr, not the exit_code: line.
b) Add upload_file tool
server.tool("upload_file", "Write base64-encoded content to a file",
{ path: z.string(), content_base64: z.string() },
async ({ path, content_base64 }) => {
const dir = require("path").dirname(path);
await fs.promises.mkdir(dir, { recursive: true });
const buffer = Buffer.from(content_base64, "base64");
await fs.promises.writeFile(path, buffer);
return {
content: [{ type: "text", text: `Written ${buffer.length} bytes to ${path}` }],
_meta: { exit_code: 0 }
};
}
);
c) Add download_file tool
server.tool("download_file", "Read a file and return as base64",
{ path: z.string() },
async ({ path }) => {
const buffer = await fs.promises.readFile(path);
return {
content: [{ type: "text", text: buffer.toString("base64") }],
_meta: { exit_code: 0 }
};
}
);
d) Expose sandbox ID
Include a unique identifier in the initialize response's serverInfo:
serverInfo: {
name: "exec-server",
version: "1.0.0",
sandbox_id: process.env.SANDBOX_ID || `sandbox-${process.pid}`
}
Migration Path
- Phase 1 (now): Orchestra adapter parses text-embedded
exit_code and uses shell fallbacks for file ops — works with current exec_server unchanged.
- Phase 2: Update exec_server with structured
_meta, upload_file, and download_file tools — Orchestra adapter automatically prefers these when available.
- Phase 3: Remove text-embedded
exit_code from exec_server output once all consumers are updated.
Acceptance Criteria
Related
Summary
The Orchestra project needs to use
exec_serveras a DeepAgentsBaseSandboxbackend. The current exec_server has compatibility gaps that need to be addressed inubuntu/index.js.Orchestra spec:
ruska-ai/orchestra—specs/spec-d-mcp-sandbox.md(section: "Required exec_server Changes")Gap Analysis
ExecuteResponse(output, exit_code: int, truncated: bool)"stdout:\n...\nstderr:\n...\nexit_code: N"upload_files([(path, bytes)])download_files([path])idpropertycmdcmdKey insight:
BaseSandbox's inherited methods (read,write,edit,grep_raw,glob_info,ls_info) construct Python heredoc shell commands and pass them toexecute(). They depend onexit_codebeing a proper integer field — they do not parse it from output text.Required Changes
a) Return structured
exit_codeChange
execCommandHandlerto returnexit_codeas structured_metametadata, not embedded in text:Text output should contain only stdout/stderr, not the
exit_code:line.b) Add
upload_filetoolc) Add
download_filetoold) Expose sandbox ID
Include a unique identifier in the
initializeresponse'sserverInfo:Migration Path
exit_codeand uses shell fallbacks for file ops — works with current exec_server unchanged._meta,upload_file, anddownload_filetools — Orchestra adapter automatically prefers these when available.exit_codefrom exec_server output once all consumers are updated.Acceptance Criteria
exec_commandreturns_meta.exit_codeas structured integer (not in text)upload_filetool available for base64 file writesdownload_filetool available for base64 file readsinitializeresponseRelated