fix: align exec tool config and test timeouts

This commit is contained in:
Peter Steinberger
2026-01-18 05:10:58 +00:00
parent 436c5fd751
commit 1d8614c7c2
5 changed files with 52 additions and 3 deletions

View File

@@ -6,6 +6,7 @@ Docs: https://docs.clawd.bot
### Changes ### Changes
- Exec: add host/security/ask routing for gateway + node exec. - Exec: add host/security/ask routing for gateway + node exec.
- Exec: add `/exec` directive for per-session exec defaults (host/security/ask/node).
- macOS: migrate exec approvals to `~/.clawdbot/exec-approvals.json` with per-agent allowlists and skill auto-allow toggle. - macOS: migrate exec approvals to `~/.clawdbot/exec-approvals.json` with per-agent allowlists and skill auto-allow toggle.
- macOS: add approvals socket UI server + node exec lifecycle events. - macOS: add approvals socket UI server + node exec lifecycle events.
- Slash commands: replace `/cost` with `/usage off|tokens|full` to control per-response usage footer; `/usage` no longer aliases `/status`. (Supersedes #1140) — thanks @Nachx639. - Slash commands: replace `/cost` with `/usage off|tokens|full` to control per-response usage footer; `/usage` no longer aliases `/status`. (Supersedes #1140) — thanks @Nachx639.

View File

@@ -55,6 +55,7 @@ beforeEach(() => {
killed: false, killed: false,
}); });
ensureAuthProfileStore.mockReset().mockReturnValue({ version: 1, profiles: {} }); ensureAuthProfileStore.mockReset().mockReturnValue({ version: 1, profiles: {} });
loadClawdbotPlugins.mockReset().mockReturnValue({ plugins: [], diagnostics: [] });
migrateLegacyConfig.mockReset().mockImplementation((raw: unknown) => ({ migrateLegacyConfig.mockReset().mockImplementation((raw: unknown) => ({
config: raw as Record<string, unknown>, config: raw as Record<string, unknown>,
changes: ["Moved routing.allowFrom → channels.whatsapp.allowFrom."], changes: ["Moved routing.allowFrom → channels.whatsapp.allowFrom."],
@@ -131,6 +132,7 @@ const runCommandWithTimeout = vi.fn().mockResolvedValue({
}); });
const ensureAuthProfileStore = vi.fn().mockReturnValue({ version: 1, profiles: {} }); const ensureAuthProfileStore = vi.fn().mockReturnValue({ version: 1, profiles: {} });
const loadClawdbotPlugins = vi.fn().mockReturnValue({ plugins: [], diagnostics: [] });
const legacyReadConfigFileSnapshot = vi.fn().mockResolvedValue({ const legacyReadConfigFileSnapshot = vi.fn().mockResolvedValue({
path: "/tmp/clawdbot.json", path: "/tmp/clawdbot.json",
@@ -173,9 +175,8 @@ vi.mock("../agents/skills-status.js", () => ({
})); }));
vi.mock("../plugins/loader.js", () => ({ vi.mock("../plugins/loader.js", () => ({
loadClawdbotPlugins: () => ({ plugins: [], diagnostics: [] }), loadClawdbotPlugins,
})); }));
vi.mock("../config/config.js", async (importOriginal) => { vi.mock("../config/config.js", async (importOriginal) => {
const actual = await importOriginal(); const actual = await importOriginal();
return { return {

View File

@@ -387,3 +387,32 @@ export type ToolsConfig = {
}; };
}; };
}; };
export type ExecToolConfig = {
/** Exec host routing (default: sandbox). */
host?: "sandbox" | "gateway" | "node";
/** Exec security mode (default: deny). */
security?: "deny" | "allowlist" | "full";
/** Exec ask mode (default: on-miss). */
ask?: "off" | "on-miss" | "always";
/** Default node binding for exec.host=node (node id/name). */
node?: string;
/** Default time (ms) before an exec command auto-backgrounds. */
backgroundMs?: number;
/** Default timeout (seconds) before auto-killing exec commands. */
timeoutSec?: number;
/** How long to keep finished sessions in memory (ms). */
cleanupMs?: number;
/** Emit a system event and heartbeat when a backgrounded exec exits. */
notifyOnExit?: boolean;
/** apply_patch subtool configuration (experimental). */
applyPatch?: {
/** Enable apply_patch for OpenAI models (default: false). */
enabled?: boolean;
/**
* Optional allowlist of model ids that can use apply_patch.
* Accepts either raw ids (e.g. "gpt-5.2") or full ids (e.g. "openai/gpt-5.2").
*/
allowModels?: string[];
};
};

View File

@@ -183,6 +183,24 @@ export const AgentToolsSchema = z
allowFrom: ElevatedAllowFromSchema, allowFrom: ElevatedAllowFromSchema,
}) })
.optional(), .optional(),
exec: z
.object({
host: z.enum(["sandbox", "gateway", "node"]).optional(),
security: z.enum(["deny", "allowlist", "full"]).optional(),
ask: z.enum(["off", "on-miss", "always"]).optional(),
node: z.string().optional(),
backgroundMs: z.number().int().positive().optional(),
timeoutSec: z.number().int().positive().optional(),
cleanupMs: z.number().int().positive().optional(),
notifyOnExit: z.boolean().optional(),
applyPatch: z
.object({
enabled: z.boolean().optional(),
allowModels: z.array(z.string()).optional(),
})
.optional(),
})
.optional(),
sandbox: z sandbox: z
.object({ .object({
tools: ToolPolicySchema, tools: ToolPolicySchema,

View File

@@ -11,7 +11,7 @@ export default defineConfig({
}, },
}, },
test: { test: {
testTimeout: 20_000, testTimeout: 30_000,
include: [ include: [
"src/**/*.test.ts", "src/**/*.test.ts",
"extensions/**/*.test.ts", "extensions/**/*.test.ts",