fix: validate session spawn timeout

This commit is contained in:
Peter Steinberger
2026-05-28 19:41:11 -04:00
parent f2843d3d79
commit 9a4aa438bb
2 changed files with 24 additions and 12 deletions

View File

@@ -446,6 +446,24 @@ describe("sessions_spawn tool", () => {
expect(spawnArgs.runTimeoutSeconds).toBe(2);
});
it.each([
[{ runTimeoutSeconds: 1.5 }, "runTimeoutSeconds must be a non-negative integer"],
[{ runTimeoutSeconds: -1 }, "runTimeoutSeconds must be a non-negative integer"],
[{ timeoutSeconds: "1sec" }, "timeoutSeconds must be a non-negative integer"],
])("rejects invalid timeout override %o", async (params, message) => {
const tool = createSessionsSpawnTool({
agentSessionKey: "agent:main:main",
});
await expect(
tool.execute("call-invalid-timeout", {
task: "do thing",
...params,
}),
).rejects.toThrow(message);
expect(hoisted.spawnSubagentDirectMock).not.toHaveBeenCalled();
});
it("passes inherited workspaceDir from tool context, not from tool args", async () => {
const tool = createSessionsSpawnTool({
agentSessionKey: "agent:main:main",

View File

@@ -35,6 +35,7 @@ import type { AnyAgentTool } from "./common.js";
import {
jsonResult,
normalizeToolModelOverride,
readNonNegativeIntegerParam,
readStringParam,
ToolInputError,
} from "./common.js";
@@ -169,9 +170,9 @@ function createSessionsSpawnToolSchema(params: {
model: Type.Optional(Type.String()),
thinking: Type.Optional(Type.String()),
cwd: Type.Optional(Type.String()),
runTimeoutSeconds: Type.Optional(Type.Number({ minimum: 0 })),
runTimeoutSeconds: Type.Optional(Type.Integer({ minimum: 0 })),
// Back-compat: older callers used timeoutSeconds for this tool.
timeoutSeconds: Type.Optional(Type.Number({ minimum: 0 })),
timeoutSeconds: Type.Optional(Type.Integer({ minimum: 0 })),
...(params.threadAvailable
? {
thread: Type.Optional(
@@ -342,17 +343,10 @@ export function createSessionsSpawnTool(
if (runtime === "acp" && context === "fork") {
throw new Error('context="fork" is only supported for runtime="subagent".');
}
// Back-compat: older callers used timeoutSeconds for this tool.
const timeoutSecondsCandidate =
typeof params.runTimeoutSeconds === "number"
? params.runTimeoutSeconds
: typeof params.timeoutSeconds === "number"
? params.timeoutSeconds
: undefined;
const runTimeoutSeconds =
typeof timeoutSecondsCandidate === "number" && Number.isFinite(timeoutSecondsCandidate)
? Math.max(0, Math.floor(timeoutSecondsCandidate))
: undefined;
readNonNegativeIntegerParam(params, "runTimeoutSeconds") ??
// Back-compat: older callers used timeoutSeconds for this tool.
readNonNegativeIntegerParam(params, "timeoutSeconds");
const thread = params.thread === true;
const attachments = Array.isArray(params.attachments)
? (params.attachments as Array<{