fix: describe sandboxed elevated in prompt

This commit is contained in:
Peter Steinberger
2026-01-10 21:37:04 +01:00
parent 3389231ecb
commit 46e00ad5e7
6 changed files with 101 additions and 3 deletions

View File

@@ -11,6 +11,7 @@
- CLI: add `clawdbot update` (safe-ish git checkout update) + `--update` shorthand. (#673) — thanks @fm1randa.
### Fixes
- Agents/System: clarify sandboxed runtime in system prompt and surface elevated availability when sandboxed.
- Auto-reply: prefer `RawBody` for command/directive parsing (WhatsApp + Discord) and prevent fallback runs from clobbering concurrent session updates. (#643) — thanks @mcinteerj.
- WhatsApp: fix group reactions by preserving message IDs and sender JIDs in history; normalize participant phone numbers to JIDs in outbound reactions. (#640) — thanks @mcinteerj.
- WhatsApp: expose group participant IDs to the model so reactions can target the right sender.

View File

@@ -19,6 +19,7 @@ The prompt is intentionally compact and uses fixed sections:
- **Clawdbot Self-Update**: how to run `config.apply` and `update.run`.
- **Workspace**: working directory (`agents.defaults.workspace`).
- **Workspace Files (injected)**: indicates bootstrap files are included below.
- **Sandbox** (when enabled): indicates sandboxed runtime, sandbox paths, and whether elevated bash is available.
- **Time**: UTC default + the users local time (already converted).
- **Reply Tags**: optional reply tag syntax for supported providers.
- **Heartbeats**: heartbeat prompt and ack behavior.

View File

@@ -61,6 +61,47 @@ describe("buildEmbeddedSandboxInfo", () => {
browserNoVncUrl: "http://localhost:6080",
});
});
it("includes elevated info when allowed", () => {
const sandbox = {
enabled: true,
sessionKey: "session:test",
workspaceDir: "/tmp/clawdbot-sandbox",
agentWorkspaceDir: "/tmp/clawdbot-workspace",
workspaceAccess: "none",
containerName: "clawdbot-sbx-test",
containerWorkdir: "/workspace",
docker: {
image: "clawdbot-sandbox:bookworm-slim",
containerPrefix: "clawdbot-sbx-",
workdir: "/workspace",
readOnlyRoot: true,
tmpfs: ["/tmp"],
network: "none",
user: "1000:1000",
capDrop: ["ALL"],
env: { LANG: "C.UTF-8" },
},
tools: {
allow: ["bash"],
deny: ["browser"],
},
} satisfies SandboxContext;
expect(
buildEmbeddedSandboxInfo(sandbox, {
enabled: true,
allowed: true,
defaultLevel: "on",
}),
).toEqual({
enabled: true,
workspaceDir: "/tmp/clawdbot-sandbox",
workspaceAccess: "none",
agentWorkspaceMount: undefined,
elevated: { allowed: true, defaultLevel: "on" },
});
});
});
describe("resolveSessionAgentIds", () => {

View File

@@ -479,6 +479,10 @@ type EmbeddedSandboxInfo = {
agentWorkspaceMount?: string;
browserControlUrl?: string;
browserNoVncUrl?: string;
elevated?: {
allowed: boolean;
defaultLevel: "on" | "off";
};
};
function resolveSessionLane(key: string) {
@@ -552,8 +556,10 @@ function describeUnknownError(error: unknown): string {
export function buildEmbeddedSandboxInfo(
sandbox?: Awaited<ReturnType<typeof resolveSandboxContext>>,
bashElevated?: BashElevatedDefaults,
): EmbeddedSandboxInfo | undefined {
if (!sandbox?.enabled) return undefined;
const elevatedAllowed = Boolean(bashElevated?.enabled && bashElevated.allowed);
return {
enabled: true,
workspaceDir: sandbox.workspaceDir,
@@ -562,6 +568,14 @@ export function buildEmbeddedSandboxInfo(
sandbox.workspaceAccess === "ro" ? "/agent" : undefined,
browserControlUrl: sandbox.browser?.controlUrl,
browserNoVncUrl: sandbox.browser?.noVncUrl,
...(elevatedAllowed
? {
elevated: {
allowed: true,
defaultLevel: bashElevated?.defaultLevel ?? "off",
},
}
: {}),
};
}
@@ -887,7 +901,10 @@ export async function compactEmbeddedPiSession(params: {
provider: runtimeProvider,
capabilities: runtimeCapabilities,
};
const sandboxInfo = buildEmbeddedSandboxInfo(sandbox);
const sandboxInfo = buildEmbeddedSandboxInfo(
sandbox,
params.bashElevated,
);
const reasoningTagHint = provider === "ollama";
const userTimezone = resolveUserTimezone(
params.config?.agents?.defaults?.userTimezone,
@@ -1264,7 +1281,10 @@ export async function runEmbeddedPiAgent(params: {
node: process.version,
model: `${provider}/${modelId}`,
};
const sandboxInfo = buildEmbeddedSandboxInfo(sandbox);
const sandboxInfo = buildEmbeddedSandboxInfo(
sandbox,
params.bashElevated,
);
const reasoningTagHint = provider === "ollama";
const userTimezone = resolveUserTimezone(
params.config?.agents?.defaults?.userTimezone,

View File

@@ -169,4 +169,21 @@ describe("buildAgentSystemPrompt", () => {
expect(prompt).toContain("provider=telegram");
expect(prompt).toContain("capabilities=inlineButtons");
});
it("describes sandboxed runtime and elevated when allowed", () => {
const prompt = buildAgentSystemPrompt({
workspaceDir: "/tmp/clawd",
sandboxInfo: {
enabled: true,
workspaceDir: "/tmp/sandbox",
workspaceAccess: "ro",
agentWorkspaceMount: "/agent",
elevated: { allowed: true, defaultLevel: "on" },
},
});
expect(prompt).toContain("You are running in a sandboxed runtime");
expect(prompt).toContain("User can toggle with /elevated on|off.");
expect(prompt).toContain("Current elevated level: on");
});
});

View File

@@ -31,6 +31,10 @@ export function buildAgentSystemPrompt(params: {
agentWorkspaceMount?: string;
browserControlUrl?: string;
browserNoVncUrl?: string;
elevated?: {
allowed: boolean;
defaultLevel: "on" | "off";
};
};
}) {
const toolSummaries: Record<string, string> = {
@@ -219,7 +223,7 @@ export function buildAgentSystemPrompt(params: {
params.sandboxInfo?.enabled ? "## Sandbox" : "",
params.sandboxInfo?.enabled
? [
"Tool execution is isolated in a Docker sandbox.",
"You are running in a sandboxed runtime (tools execute in Docker).",
"Some tools may be unavailable due to sandbox policy.",
params.sandboxInfo.workspaceDir
? `Sandbox workspace: ${params.sandboxInfo.workspaceDir}`
@@ -237,6 +241,20 @@ export function buildAgentSystemPrompt(params: {
params.sandboxInfo.browserNoVncUrl
? `Sandbox browser observer (noVNC): ${params.sandboxInfo.browserNoVncUrl}`
: "",
params.sandboxInfo.elevated?.allowed
? "Elevated bash is available for this session."
: "",
params.sandboxInfo.elevated?.allowed
? "User can toggle with /elevated on|off."
: "",
params.sandboxInfo.elevated?.allowed
? "You may also send /elevated on|off when needed."
: "",
params.sandboxInfo.elevated?.allowed
? `Current elevated level: ${
params.sandboxInfo.elevated.defaultLevel
} (on runs bash on host; off runs in sandbox).`
: "",
]
.filter(Boolean)
.join("\n")