feat: add elevated bash mode
This commit is contained in:
@@ -119,6 +119,19 @@ describe("bash tool backgrounding", () => {
|
||||
expect(status).toBe("failed");
|
||||
});
|
||||
|
||||
it("rejects elevated requests when not allowed", async () => {
|
||||
const customBash = createBashTool({
|
||||
elevated: { enabled: true, allowed: false, defaultLevel: "off" },
|
||||
});
|
||||
|
||||
await expect(
|
||||
customBash.execute("call1", {
|
||||
command: "echo hi",
|
||||
elevated: true,
|
||||
}),
|
||||
).rejects.toThrow("elevated is not available right now.");
|
||||
});
|
||||
|
||||
it("logs line-based slices and defaults to last lines", async () => {
|
||||
const result = await bashTool.execute("call1", {
|
||||
command:
|
||||
|
||||
@@ -26,6 +26,7 @@ import {
|
||||
killProcessTree,
|
||||
sanitizeBinaryOutput,
|
||||
} from "./shell-utils.js";
|
||||
import { logInfo } from "../logger.js";
|
||||
|
||||
const CHUNK_LIMIT = 8 * 1024;
|
||||
const DEFAULT_MAX_OUTPUT = clampNumber(
|
||||
@@ -53,6 +54,7 @@ export type BashToolDefaults = {
|
||||
backgroundMs?: number;
|
||||
timeoutSec?: number;
|
||||
sandbox?: BashSandboxConfig;
|
||||
elevated?: BashElevatedDefaults;
|
||||
};
|
||||
|
||||
export type ProcessToolDefaults = {
|
||||
@@ -66,6 +68,12 @@ export type BashSandboxConfig = {
|
||||
env?: Record<string, string>;
|
||||
};
|
||||
|
||||
export type BashElevatedDefaults = {
|
||||
enabled: boolean;
|
||||
allowed: boolean;
|
||||
defaultLevel: "on" | "off";
|
||||
};
|
||||
|
||||
const bashSchema = Type.Object({
|
||||
command: Type.String({ description: "Bash command to execute" }),
|
||||
workdir: Type.Optional(
|
||||
@@ -85,6 +93,11 @@ const bashSchema = Type.Object({
|
||||
description: "Timeout in seconds (optional, kills process on expiry)",
|
||||
}),
|
||||
),
|
||||
elevated: Type.Optional(
|
||||
Type.Boolean({
|
||||
description: "Run on the host with elevated permissions (if allowed)",
|
||||
}),
|
||||
),
|
||||
});
|
||||
|
||||
export type BashToolDetails =
|
||||
@@ -131,6 +144,7 @@ export function createBashTool(
|
||||
yieldMs?: number;
|
||||
background?: boolean;
|
||||
timeout?: number;
|
||||
elevated?: boolean;
|
||||
};
|
||||
|
||||
if (!params.command) {
|
||||
@@ -149,7 +163,24 @@ export function createBashTool(
|
||||
const startedAt = Date.now();
|
||||
const sessionId = randomUUID();
|
||||
const warnings: string[] = [];
|
||||
const sandbox = defaults?.sandbox;
|
||||
const elevatedDefaults = defaults?.elevated;
|
||||
const elevatedRequested =
|
||||
typeof params.elevated === "boolean"
|
||||
? params.elevated
|
||||
: elevatedDefaults?.defaultLevel === "on";
|
||||
if (elevatedRequested) {
|
||||
if (!elevatedDefaults?.enabled || !elevatedDefaults.allowed) {
|
||||
throw new Error("elevated is not available right now.");
|
||||
}
|
||||
logInfo(
|
||||
`bash: elevated command (${sessionId.slice(0, 8)}) ${truncateMiddle(
|
||||
params.command,
|
||||
120,
|
||||
)}`,
|
||||
);
|
||||
}
|
||||
|
||||
const sandbox = elevatedRequested ? undefined : defaults?.sandbox;
|
||||
const rawWorkdir = params.workdir?.trim() || process.cwd();
|
||||
let workdir = rawWorkdir;
|
||||
let containerWorkdir = sandbox?.containerWorkdir;
|
||||
|
||||
@@ -34,6 +34,7 @@ import {
|
||||
} from "../process/command-queue.js";
|
||||
import { CONFIG_DIR, resolveUserPath } from "../utils.js";
|
||||
import { resolveClawdisAgentDir } from "./agent-paths.js";
|
||||
import type { BashElevatedDefaults } from "./bash-tools.js";
|
||||
import { DEFAULT_MODEL, DEFAULT_PROVIDER } from "./defaults.js";
|
||||
import { ensureClawdisModelsJson } from "./models-config.js";
|
||||
import {
|
||||
@@ -390,6 +391,7 @@ export async function runEmbeddedPiAgent(params: {
|
||||
model?: string;
|
||||
thinkLevel?: ThinkLevel;
|
||||
verboseLevel?: VerboseLevel;
|
||||
bashElevated?: BashElevatedDefaults;
|
||||
timeoutMs: number;
|
||||
runId: string;
|
||||
abortSignal?: AbortSignal;
|
||||
@@ -495,7 +497,10 @@ export async function runEmbeddedPiAgent(params: {
|
||||
const contextFiles = buildBootstrapContextFiles(bootstrapFiles);
|
||||
const promptSkills = resolvePromptSkills(skillsSnapshot, skillEntries);
|
||||
const tools = createClawdisCodingTools({
|
||||
bash: params.config?.agent?.bash,
|
||||
bash: {
|
||||
...params.config?.agent?.bash,
|
||||
elevated: params.bashElevated,
|
||||
},
|
||||
sandbox,
|
||||
surface: params.surface,
|
||||
sessionKey: params.sessionKey ?? params.sessionId,
|
||||
|
||||
Reference in New Issue
Block a user