fix(exec): pass undefined instead of null for optional approval params
TypeBox Type.Optional(Type.String()) accepts string|undefined but NOT null. Discord exec was failing with 'resolvedPath must be string' because callers passed null explicitly. Web UI worked because it skipped the approval request. Fixes exec approval validation error in Discord-triggered sessions.
This commit is contained in:
@@ -896,8 +896,8 @@ export function createExecTool(
|
||||
security: hostSecurity,
|
||||
ask: hostAsk,
|
||||
agentId: defaults?.agentId,
|
||||
resolvedPath: null,
|
||||
sessionKey: defaults?.sessionKey ?? null,
|
||||
resolvedPath: undefined,
|
||||
sessionKey: defaults?.sessionKey,
|
||||
timeoutMs: DEFAULT_APPROVAL_TIMEOUT_MS,
|
||||
},
|
||||
)) as { decision?: string } | null;
|
||||
@@ -1060,7 +1060,7 @@ export function createExecTool(
|
||||
const approvalSlug = createApprovalSlug(approvalId);
|
||||
const expiresAtMs = Date.now() + DEFAULT_APPROVAL_TIMEOUT_MS;
|
||||
const contextKey = `exec:${approvalId}`;
|
||||
const resolvedPath = analysis.segments[0]?.resolution?.resolvedPath ?? null;
|
||||
const resolvedPath = analysis.segments[0]?.resolution?.resolvedPath;
|
||||
const noticeSeconds = Math.max(1, Math.round(approvalRunningNoticeMs / 1000));
|
||||
const commandText = params.command;
|
||||
const effectiveTimeout =
|
||||
@@ -1082,7 +1082,7 @@ export function createExecTool(
|
||||
ask: hostAsk,
|
||||
agentId: defaults?.agentId,
|
||||
resolvedPath,
|
||||
sessionKey: defaults?.sessionKey ?? null,
|
||||
sessionKey: defaults?.sessionKey,
|
||||
timeoutMs: DEFAULT_APPROVAL_TIMEOUT_MS,
|
||||
},
|
||||
)) as { decision?: string } | null;
|
||||
|
||||
@@ -267,8 +267,8 @@ export function registerNodesInvokeCommands(nodes: Command) {
|
||||
security: hostSecurity,
|
||||
ask: hostAsk,
|
||||
agentId,
|
||||
resolvedPath: null,
|
||||
sessionKey: null,
|
||||
resolvedPath: undefined,
|
||||
sessionKey: undefined,
|
||||
timeoutMs: 120_000,
|
||||
})) as { decision?: string } | null;
|
||||
const decision =
|
||||
|
||||
@@ -1,10 +1,54 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { ExecApprovalManager } from "../exec-approval-manager.js";
|
||||
import { createExecApprovalHandlers } from "./exec-approval.js";
|
||||
import { validateExecApprovalRequestParams } from "../protocol/index.js";
|
||||
|
||||
const noop = () => {};
|
||||
|
||||
describe("exec approval handlers", () => {
|
||||
describe("ExecApprovalRequestParams validation", () => {
|
||||
it("accepts request with resolvedPath omitted", () => {
|
||||
const params = {
|
||||
command: "echo hi",
|
||||
cwd: "/tmp",
|
||||
host: "node",
|
||||
};
|
||||
expect(validateExecApprovalRequestParams(params)).toBe(true);
|
||||
});
|
||||
|
||||
it("accepts request with resolvedPath as string", () => {
|
||||
const params = {
|
||||
command: "echo hi",
|
||||
cwd: "/tmp",
|
||||
host: "node",
|
||||
resolvedPath: "/usr/bin/echo",
|
||||
};
|
||||
expect(validateExecApprovalRequestParams(params)).toBe(true);
|
||||
});
|
||||
|
||||
it("accepts request with resolvedPath as undefined", () => {
|
||||
const params = {
|
||||
command: "echo hi",
|
||||
cwd: "/tmp",
|
||||
host: "node",
|
||||
resolvedPath: undefined,
|
||||
};
|
||||
expect(validateExecApprovalRequestParams(params)).toBe(true);
|
||||
});
|
||||
|
||||
// This documents the TypeBox/AJV behavior that caused the Discord exec bug:
|
||||
// Type.Optional(Type.String()) does NOT accept null, only string or undefined.
|
||||
it("rejects request with resolvedPath as null", () => {
|
||||
const params = {
|
||||
command: "echo hi",
|
||||
cwd: "/tmp",
|
||||
host: "node",
|
||||
resolvedPath: null,
|
||||
};
|
||||
expect(validateExecApprovalRequestParams(params)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
it("broadcasts request + resolve", async () => {
|
||||
const manager = new ExecApprovalManager();
|
||||
const handlers = createExecApprovalHandlers(manager);
|
||||
|
||||
Reference in New Issue
Block a user