Merge 78924c5463 into da71eaebd2
This commit is contained in:
commit
22a533e501
@ -48,6 +48,7 @@ Notes:
|
|||||||
- `tools.exec.node` (default: unset)
|
- `tools.exec.node` (default: unset)
|
||||||
- `tools.exec.pathPrepend`: list of directories to prepend to `PATH` for exec runs.
|
- `tools.exec.pathPrepend`: list of directories to prepend to `PATH` for exec runs.
|
||||||
- `tools.exec.safeBins`: stdin-only safe binaries that can run without explicit allowlist entries.
|
- `tools.exec.safeBins`: stdin-only safe binaries that can run without explicit allowlist entries.
|
||||||
|
- `tools.exec.pty` (default: false): enable PTY mode by default for exec commands. Useful when commands hang without a TTY. Ignored when sandboxed.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
```json5
|
```json5
|
||||||
|
|||||||
@ -18,3 +18,28 @@ test("exec supports pty output", async () => {
|
|||||||
const text = result.content?.[0]?.text ?? "";
|
const text = result.content?.[0]?.text ?? "";
|
||||||
expect(text).toContain("ok");
|
expect(text).toContain("ok");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("exec uses pty when defaults.pty is true", async () => {
|
||||||
|
const tool = createExecTool({ allowBackground: false, pty: true });
|
||||||
|
// Note: pty is NOT passed in params - should use default
|
||||||
|
const result = await tool.execute("toolcall", {
|
||||||
|
command: 'node -e "process.stdout.write(String.fromCharCode(111,107))"',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.details.status).toBe("completed");
|
||||||
|
const text = result.content?.[0]?.text ?? "";
|
||||||
|
expect(text).toContain("ok");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("exec params.pty overrides defaults.pty", async () => {
|
||||||
|
const tool = createExecTool({ allowBackground: false, pty: true });
|
||||||
|
// Explicitly set pty: false to override default
|
||||||
|
const result = await tool.execute("toolcall", {
|
||||||
|
command: "echo override",
|
||||||
|
pty: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.details.status).toBe("completed");
|
||||||
|
const text = result.content?.[0]?.text ?? "";
|
||||||
|
expect(text).toContain("override");
|
||||||
|
});
|
||||||
|
|||||||
@ -134,6 +134,7 @@ export type ExecToolDefaults = {
|
|||||||
messageProvider?: string;
|
messageProvider?: string;
|
||||||
notifyOnExit?: boolean;
|
notifyOnExit?: boolean;
|
||||||
cwd?: string;
|
cwd?: string;
|
||||||
|
pty?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type { BashSandboxConfig } from "./bash-tools.shared.js";
|
export type { BashSandboxConfig } from "./bash-tools.shared.js";
|
||||||
@ -1295,7 +1296,7 @@ export function createExecTool(
|
|||||||
env,
|
env,
|
||||||
sandbox: undefined,
|
sandbox: undefined,
|
||||||
containerWorkdir: null,
|
containerWorkdir: null,
|
||||||
usePty: params.pty === true && !sandbox,
|
usePty: (params.pty ?? defaults?.pty) === true && !sandbox,
|
||||||
warnings,
|
warnings,
|
||||||
maxOutput,
|
maxOutput,
|
||||||
pendingMaxOutput,
|
pendingMaxOutput,
|
||||||
@ -1381,7 +1382,7 @@ export function createExecTool(
|
|||||||
const effectiveTimeout =
|
const effectiveTimeout =
|
||||||
typeof params.timeout === "number" ? params.timeout : defaultTimeoutSec;
|
typeof params.timeout === "number" ? params.timeout : defaultTimeoutSec;
|
||||||
const getWarningText = () => (warnings.length ? `${warnings.join("\n")}\n\n` : "");
|
const getWarningText = () => (warnings.length ? `${warnings.join("\n")}\n\n` : "");
|
||||||
const usePty = params.pty === true && !sandbox;
|
const usePty = (params.pty ?? defaults?.pty) === true && !sandbox;
|
||||||
const run = await runExecProcess({
|
const run = await runExecProcess({
|
||||||
command: params.command,
|
command: params.command,
|
||||||
workdir,
|
workdir,
|
||||||
|
|||||||
@ -92,6 +92,7 @@ function resolveExecConfig(cfg: OpenClawConfig | undefined) {
|
|||||||
approvalRunningNoticeMs: globalExec?.approvalRunningNoticeMs,
|
approvalRunningNoticeMs: globalExec?.approvalRunningNoticeMs,
|
||||||
cleanupMs: globalExec?.cleanupMs,
|
cleanupMs: globalExec?.cleanupMs,
|
||||||
notifyOnExit: globalExec?.notifyOnExit,
|
notifyOnExit: globalExec?.notifyOnExit,
|
||||||
|
pty: globalExec?.pty,
|
||||||
applyPatch: globalExec?.applyPatch,
|
applyPatch: globalExec?.applyPatch,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -269,6 +270,7 @@ export function createOpenClawCodingTools(options?: {
|
|||||||
approvalRunningNoticeMs:
|
approvalRunningNoticeMs:
|
||||||
options?.exec?.approvalRunningNoticeMs ?? execConfig.approvalRunningNoticeMs,
|
options?.exec?.approvalRunningNoticeMs ?? execConfig.approvalRunningNoticeMs,
|
||||||
notifyOnExit: options?.exec?.notifyOnExit ?? execConfig.notifyOnExit,
|
notifyOnExit: options?.exec?.notifyOnExit ?? execConfig.notifyOnExit,
|
||||||
|
pty: options?.exec?.pty ?? execConfig.pty,
|
||||||
sandbox: sandbox
|
sandbox: sandbox
|
||||||
? {
|
? {
|
||||||
containerName: sandbox.containerName,
|
containerName: sandbox.containerName,
|
||||||
|
|||||||
@ -180,6 +180,7 @@ const FIELD_LABELS: Record<string, string> = {
|
|||||||
"tools.exec.node": "Exec Node Binding",
|
"tools.exec.node": "Exec Node Binding",
|
||||||
"tools.exec.pathPrepend": "Exec PATH Prepend",
|
"tools.exec.pathPrepend": "Exec PATH Prepend",
|
||||||
"tools.exec.safeBins": "Exec Safe Bins",
|
"tools.exec.safeBins": "Exec Safe Bins",
|
||||||
|
"tools.exec.pty": "Exec PTY Mode",
|
||||||
"tools.message.allowCrossContextSend": "Allow Cross-Context Messaging",
|
"tools.message.allowCrossContextSend": "Allow Cross-Context Messaging",
|
||||||
"tools.message.crossContext.allowWithinProvider": "Allow Cross-Context (Same Provider)",
|
"tools.message.crossContext.allowWithinProvider": "Allow Cross-Context (Same Provider)",
|
||||||
"tools.message.crossContext.allowAcrossProviders": "Allow Cross-Context (Across Providers)",
|
"tools.message.crossContext.allowAcrossProviders": "Allow Cross-Context (Across Providers)",
|
||||||
@ -421,6 +422,8 @@ const FIELD_HELP: Record<string, string> = {
|
|||||||
"tools.exec.pathPrepend": "Directories to prepend to PATH for exec runs (gateway/sandbox).",
|
"tools.exec.pathPrepend": "Directories to prepend to PATH for exec runs (gateway/sandbox).",
|
||||||
"tools.exec.safeBins":
|
"tools.exec.safeBins":
|
||||||
"Allow stdin-only safe binaries to run without explicit allowlist entries.",
|
"Allow stdin-only safe binaries to run without explicit allowlist entries.",
|
||||||
|
"tools.exec.pty":
|
||||||
|
"Enable PTY mode by default for exec commands. Ignored when sandboxed. (default: false).",
|
||||||
"tools.message.allowCrossContextSend":
|
"tools.message.allowCrossContextSend":
|
||||||
"Legacy override: allow cross-context sends across all providers.",
|
"Legacy override: allow cross-context sends across all providers.",
|
||||||
"tools.message.crossContext.allowWithinProvider":
|
"tools.message.crossContext.allowWithinProvider":
|
||||||
|
|||||||
@ -183,6 +183,8 @@ export type ExecToolConfig = {
|
|||||||
cleanupMs?: number;
|
cleanupMs?: number;
|
||||||
/** Emit a system event and heartbeat when a backgrounded exec exits. */
|
/** Emit a system event and heartbeat when a backgrounded exec exits. */
|
||||||
notifyOnExit?: boolean;
|
notifyOnExit?: boolean;
|
||||||
|
/** Enable PTY mode by default for exec commands (ignored when sandboxed). */
|
||||||
|
pty?: boolean;
|
||||||
/** apply_patch subtool configuration (experimental). */
|
/** apply_patch subtool configuration (experimental). */
|
||||||
applyPatch?: {
|
applyPatch?: {
|
||||||
/** Enable apply_patch for OpenAI models (default: false). */
|
/** Enable apply_patch for OpenAI models (default: false). */
|
||||||
|
|||||||
@ -271,6 +271,7 @@ export const AgentToolsSchema = z
|
|||||||
approvalRunningNoticeMs: z.number().int().nonnegative().optional(),
|
approvalRunningNoticeMs: z.number().int().nonnegative().optional(),
|
||||||
cleanupMs: z.number().int().positive().optional(),
|
cleanupMs: z.number().int().positive().optional(),
|
||||||
notifyOnExit: z.boolean().optional(),
|
notifyOnExit: z.boolean().optional(),
|
||||||
|
pty: z.boolean().optional(),
|
||||||
applyPatch: z
|
applyPatch: z
|
||||||
.object({
|
.object({
|
||||||
enabled: z.boolean().optional(),
|
enabled: z.boolean().optional(),
|
||||||
@ -512,6 +513,7 @@ export const ToolsSchema = z
|
|||||||
timeoutSec: z.number().int().positive().optional(),
|
timeoutSec: z.number().int().positive().optional(),
|
||||||
cleanupMs: z.number().int().positive().optional(),
|
cleanupMs: z.number().int().positive().optional(),
|
||||||
notifyOnExit: z.boolean().optional(),
|
notifyOnExit: z.boolean().optional(),
|
||||||
|
pty: z.boolean().optional(),
|
||||||
applyPatch: z
|
applyPatch: z
|
||||||
.object({
|
.object({
|
||||||
enabled: z.boolean().optional(),
|
enabled: z.boolean().optional(),
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user