Files
openclaw/src/agents/bash-tools.schemas.ts
sholomsbs33 7c58151445 fix(agents): validate process actions at the schema boundary (#81157)
Make the process action enum authoritative for model-facing validation and direct executor calls, rejecting malformed actions before session lookup.

Refs #69582.


Punchcard-Session: clear-orchard-timber-c2

Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
Co-authored-by: adone0 <vladyslav.yavorskyi@outlook.com>
2026-08-13 00:25:03 +08:00

112 lines
3.8 KiB
TypeScript

/**
* TypeBox schemas for shell/process tools exposed to model providers.
*
* Keep these schemas provider-friendly: flat fields, string enums, and explicit
* descriptions that match runtime validation.
*/
import { Type } from "typebox";
import { optionalStringEnum, stringEnum } from "./schema/typebox.js";
const EXEC_TOOL_HOST_VALUES = ["auto", "sandbox", "gateway", "node"] as const;
const PROCESS_TOOL_ACTIONS = [
"list",
"poll",
"log",
"write",
"send-keys",
"submit",
"paste",
"kill",
"clear",
"remove",
] as const;
/** Parameters accepted by the exec tool. */
export const execSchema = Type.Object({
command: Type.String({ description: "Shell command to execute" }),
workdir: Type.Optional(
Type.String({
description: "Working directory; omit for default. Blank/whitespace is invalid.",
}),
),
env: Type.Optional(Type.Record(Type.String(), Type.String())),
yieldMs: Type.Optional(
Type.Number({
description: "Milliseconds before backgrounding; default 10000.",
}),
),
background: Type.Optional(Type.Boolean({ description: "Run in background immediately" })),
timeoutSeconds: Type.Optional(
Type.Number({
description: "Timeout in seconds.",
}),
),
pty: Type.Optional(
Type.Boolean({
description: "Use PTY for TTY-required CLIs and coding agents.",
}),
),
elevated: Type.Optional(
Type.Boolean({
description: "Run on host with elevated permissions if allowed.",
}),
),
host: optionalStringEnum(EXEC_TOOL_HOST_VALUES, {
description: "Exec host/target (auto|sandbox|gateway|node).",
}),
security: Type.Optional(
Type.String({
description: "Ignored per call; tools.exec.security and host approvals decide.",
}),
),
ask: Type.Optional(
Type.String({
description:
"Uses tools.exec.ask and host approvals; channel-origin calls cannot override host ask=off.",
}),
),
node: Type.Optional(
Type.String({
description: "Node id/name for host=node.",
}),
),
});
/** Parameters exposed by node-only exec surfaces. */
export const nodeExecSchema = Type.Object({
command: execSchema.properties.command,
workdir: execSchema.properties.workdir,
env: execSchema.properties.env,
timeoutSeconds: execSchema.properties.timeoutSeconds,
host: optionalStringEnum(["node"] as const, {
description: "Exec target. Only node is available on this tool surface.",
}),
node: execSchema.properties.node,
});
/** Parameters accepted by the process-control tool. */
export const processSchema = Type.Object({
action: stringEnum(PROCESS_TOOL_ACTIONS, {
description: "Process action (list|poll|log|write|send-keys|submit|paste|kill|clear|remove)",
}) as unknown as Type.TString,
sessionId: Type.Optional(Type.String({ description: "Session id for actions other than list" })),
data: Type.Optional(Type.String({ description: "Data to write for write" })),
keys: Type.Optional(
Type.Array(Type.String(), { description: "Key tokens to send for send-keys" }),
),
hex: Type.Optional(Type.Array(Type.String(), { description: "Hex bytes to send for send-keys" })),
literal: Type.Optional(Type.String({ description: "Literal string for send-keys" })),
text: Type.Optional(Type.String({ description: "Text to paste for paste" })),
bracketed: Type.Optional(Type.Boolean({ description: "Wrap paste in bracketed mode" })),
eof: Type.Optional(Type.Boolean({ description: "Close stdin after write" })),
offset: Type.Optional(Type.Number({ description: "Log offset" })),
limit: Type.Optional(Type.Number({ description: "Log length" })),
timeout: Type.Optional(
Type.Number({
description:
"For poll: wait up to this many milliseconds before returning; max 30000 ms, higher values are clamped to 30000",
minimum: 0,
}),
),
});