Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 13 additions & 7 deletions agent-governance-typescript/src/sandbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ function validateResourceName(value: string, label: string): void {

export class DockerSandboxProvider implements SandboxProvider {
private readonly image: string;
private readonly containers = new Map<string, string>(); // sessionId -> containerId
private readonly containers = new Map<string, { containerId: string; config: SandboxConfig }>(); // sessionId -> { containerId, config }

constructor(image: string = 'python:3.11-slim') {
this.image = image;
Expand Down Expand Up @@ -188,7 +188,7 @@ export class DockerSandboxProvider implements SandboxProvider {
.toString()
.trim();

this.containers.set(sessionId, containerId);
this.containers.set(sessionId, { containerId, config: cfg });

return {
agentId,
Expand All @@ -208,22 +208,26 @@ export class DockerSandboxProvider implements SandboxProvider {
): Promise<ExecutionHandle> {
validateResourceName(agentId, 'agentId');

const containerId = this.containers.get(sessionId);
if (!containerId) {
const sessionData = this.containers.get(sessionId);
if (!sessionData) {
throw new Error(`No active session '${sessionId}' for agent '${agentId}'`);
}

const { containerId, config } = sessionData;
const executionId = randomUUID();
const startTime = Date.now();

// Use the configured timeoutSeconds from the session config
const timeoutMs = config.timeoutSeconds * 1000;

return new Promise<ExecutionHandle>((resolve) => {
const encoded = Buffer.from(code).toString('base64');
const execArgs = [
'exec', containerId, 'python3', '-c',
`import base64; exec(base64.b64decode('${encoded}').decode())`,
];

execFile('docker', execArgs, { timeout: 60_000 }, (error, stdout, stderr) => {
execFile('docker', execArgs, { timeout: timeoutMs }, (error, stdout, stderr) => {
const durationSeconds = (Date.now() - startTime) / 1000.0;
// Node's ExecException.code can be: a numeric exit code (child exited
// non-zero), `null` (child killed by a signal — `error.signal` is set
Expand Down Expand Up @@ -263,11 +267,13 @@ export class DockerSandboxProvider implements SandboxProvider {
async destroySession(agentId: string, sessionId: string): Promise<void> {
validateResourceName(agentId, 'agentId');

const containerId = this.containers.get(sessionId);
if (!containerId) {
const sessionData = this.containers.get(sessionId);
if (!sessionData) {
return; // already destroyed or never existed
}

const { containerId } = sessionData;

try {
execFileSync('docker', ['rm', '-f', containerId], {
stdio: 'pipe',
Expand Down
Loading