fix: tolerate missing sandbox config in embedded runner

This commit is contained in:
Peter Steinberger
2026-01-03 21:30:40 +00:00
parent c533593d8e
commit 5493772910
2 changed files with 74 additions and 8 deletions

View File

@@ -0,0 +1,47 @@
import { describe, expect, it } from "vitest";
import type { SandboxContext } from "./sandbox.js";
import { buildEmbeddedSandboxInfo } from "./pi-embedded-runner.js";
describe("buildEmbeddedSandboxInfo", () => {
it("returns undefined when sandbox is missing", () => {
expect(buildEmbeddedSandboxInfo()).toBeUndefined();
});
it("maps sandbox context into prompt info", () => {
const sandbox = {
enabled: true,
sessionKey: "session:test",
workspaceDir: "/tmp/clawdis-sandbox",
containerName: "clawdis-sbx-test",
containerWorkdir: "/workspace",
docker: {
image: "clawdis-sandbox:bookworm-slim",
containerPrefix: "clawdis-sbx-",
workdir: "/workspace",
readOnlyRoot: true,
tmpfs: ["/tmp"],
network: "none",
user: "1000:1000",
capDrop: ["ALL"],
env: { LANG: "C.UTF-8" },
},
tools: {
allow: ["bash"],
deny: ["browser"],
},
browser: {
controlUrl: "http://localhost:9222",
noVncUrl: "http://localhost:6080",
containerName: "clawdis-sbx-browser-test",
},
} satisfies SandboxContext;
expect(buildEmbeddedSandboxInfo(sandbox)).toEqual({
enabled: true,
workspaceDir: "/tmp/clawdis-sandbox",
browserControlUrl: "http://localhost:9222",
browserNoVncUrl: "http://localhost:6080",
});
});
});

View File

@@ -48,6 +48,7 @@ import {
} from "./pi-embedded-subscribe.js";
import { extractAssistantText } from "./pi-embedded-utils.js";
import { createClawdisCodingTools } from "./pi-tools.js";
import { resolveSandboxContext } from "./sandbox.js";
import {
applySkillEnvOverrides,
applySkillEnvOverridesFromSnapshot,
@@ -103,6 +104,12 @@ const DEFAULT_OAUTH_DIR = path.join(CONFIG_DIR, "credentials");
let oauthStorageConfigured = false;
type OAuthStorage = Record<string, OAuthCredentials>;
type EmbeddedSandboxInfo = {
enabled: boolean;
workspaceDir?: string;
browserControlUrl?: string;
browserNoVncUrl?: string;
};
function resolveSessionLane(key: string) {
const cleaned = key.trim() || "main";
@@ -114,6 +121,18 @@ function resolveGlobalLane(lane?: string) {
return cleaned ? cleaned : "main";
}
export function buildEmbeddedSandboxInfo(
sandbox?: Awaited<ReturnType<typeof resolveSandboxContext>>,
): EmbeddedSandboxInfo | undefined {
if (!sandbox?.enabled) return undefined;
return {
enabled: true,
workspaceDir: sandbox.workspaceDir,
browserControlUrl: sandbox.browser?.controlUrl,
browserNoVncUrl: sandbox.browser?.noVncUrl,
};
}
function resolveClawdisOAuthPath(): string {
const overrideDir =
process.env.CLAWDIS_OAUTH_DIR?.trim() || DEFAULT_OAUTH_DIR;
@@ -410,6 +429,12 @@ export async function runEmbeddedPiAgent(params: {
config: params.config,
entries: skillEntries,
});
const sandboxSessionKey = params.sessionKey?.trim() || params.sessionId;
const sandbox = await resolveSandboxContext({
config: params.config,
sessionKey: sandboxSessionKey,
workspaceDir: resolvedWorkspace,
});
restoreSkillEnv = params.skillsSnapshot
? applySkillEnvOverridesFromSnapshot({
snapshot: params.skillsSnapshot,
@@ -426,6 +451,7 @@ export async function runEmbeddedPiAgent(params: {
const promptSkills = resolvePromptSkills(skillsSnapshot, skillEntries);
const tools = createClawdisCodingTools({
bash: params.config?.agent?.bash,
sandbox,
surface: params.surface,
});
const machineName = await getMachineDisplayName();
@@ -436,14 +462,7 @@ export async function runEmbeddedPiAgent(params: {
node: process.version,
model: `${provider}/${modelId}`,
};
const sandboxInfo = sandbox?.enabled
? {
enabled: true,
workspaceDir: sandbox.workspaceDir,
browserControlUrl: sandbox.browser?.controlUrl,
browserNoVncUrl: sandbox.browser?.noVncUrl,
}
: undefined;
const sandboxInfo = buildEmbeddedSandboxInfo(sandbox);
const reasoningTagHint = provider === "ollama";
const systemPrompt = buildSystemPrompt({
appendPrompt: buildAgentSystemPromptAppend({