diff --git a/src/agents/bash-tools.exec-approval-request.ts b/src/agents/bash-tools.exec-approval-request.ts index 7c28827c051..2b2fd7d9a5b 100644 --- a/src/agents/bash-tools.exec-approval-request.ts +++ b/src/agents/bash-tools.exec-approval-request.ts @@ -7,7 +7,7 @@ import { callGatewayTool } from "./tools/gateway.js"; export type RequestExecApprovalDecisionParams = { id: string; - command: string; + command?: string; commandArgv?: string[]; systemRunPlan?: SystemRunApprovalPlan; env?: Record; @@ -35,8 +35,8 @@ function buildExecApprovalRequestToolParams( ): ExecApprovalRequestToolParams { return { id: params.id, - command: params.command, - commandArgv: params.commandArgv, + ...(params.command ? { command: params.command } : {}), + ...(params.commandArgv ? { commandArgv: params.commandArgv } : {}), systemRunPlan: params.systemRunPlan, env: params.env, cwd: params.cwd, @@ -150,7 +150,7 @@ export async function requestExecApprovalDecision( type HostExecApprovalParams = { approvalId: string; - command: string; + command?: string; commandArgv?: string[]; systemRunPlan?: SystemRunApprovalPlan; env?: Record; diff --git a/src/agents/bash-tools.exec-host-node.ts b/src/agents/bash-tools.exec-host-node.ts index 97eb4218035..c3a23197f0a 100644 --- a/src/agents/bash-tools.exec-host-node.ts +++ b/src/agents/bash-tools.exec-host-node.ts @@ -125,7 +125,7 @@ export async function executeNodeHostCommand( throw new Error("invalid system.run.prepare response"); } const runArgv = prepared.plan.argv; - const runRawCommand = prepared.plan.rawCommand ?? prepared.cmdText; + const runRawCommand = prepared.plan.commandText; const runCwd = prepared.plan.cwd ?? params.workdir; const runAgentId = prepared.plan.agentId ?? params.agentId; const runSessionKey = prepared.plan.sessionKey ?? params.sessionKey; @@ -238,8 +238,6 @@ export async function executeNodeHostCommand( // Register first so the returned approval ID is actionable immediately. const registration = await registerExecApprovalRequestForHostOrThrow({ approvalId, - command: prepared.cmdText, - commandArgv: prepared.plan.argv, systemRunPlan: prepared.plan, env: nodeEnv, workdir: runCwd, @@ -391,7 +389,7 @@ export async function executeNodeHostCommand( warningText, approvalSlug, approvalId, - command: prepared.cmdText, + command: prepared.plan.commandText, cwd: runCwd, host: "node", nodeId, diff --git a/src/agents/openclaw-tools.camera.test.ts b/src/agents/openclaw-tools.camera.test.ts index 83c4d3e48d6..5d3f14772fd 100644 --- a/src/agents/openclaw-tools.camera.test.ts +++ b/src/agents/openclaw-tools.camera.test.ts @@ -135,11 +135,10 @@ function setupNodeInvokeMock(params: { function createSystemRunPreparePayload(cwd: string | null) { return { payload: { - cmdText: "echo hi", plan: { argv: ["echo", "hi"], cwd, - rawCommand: "echo hi", + commandText: "echo hi", agentId: null, sessionKey: null, }, @@ -662,10 +661,9 @@ describe("nodes run", () => { onApprovalRequest: (approvalParams) => { expect(approvalParams).toMatchObject({ id: expect.any(String), - command: "echo hi", - commandArgv: ["echo", "hi"], systemRunPlan: expect.objectContaining({ argv: ["echo", "hi"], + commandText: "echo hi", }), nodeId: NODE_ID, host: "node", diff --git a/src/agents/tools/nodes-tool.test.ts b/src/agents/tools/nodes-tool.test.ts index 99780a16238..ddde0b850e1 100644 --- a/src/agents/tools/nodes-tool.test.ts +++ b/src/agents/tools/nodes-tool.test.ts @@ -97,11 +97,11 @@ describe("createNodesTool screen_record duration guardrails", () => { if (payload?.command === "system.run.prepare") { return { payload: { - cmdText: "echo hi", plan: { argv: ["bash", "-lc", "echo hi"], cwd: null, - rawCommand: null, + commandText: 'bash -lc "echo hi"', + commandPreview: "echo hi", agentId: null, sessionKey: null, }, diff --git a/src/agents/tools/nodes-tool.ts b/src/agents/tools/nodes-tool.ts index 9c335c012b4..e57ff735cdf 100644 --- a/src/agents/tools/nodes-tool.ts +++ b/src/agents/tools/nodes-tool.ts @@ -664,7 +664,7 @@ export function createNodesTool(options?: { } const runParams = { command: prepared.plan.argv, - rawCommand: prepared.plan.rawCommand ?? prepared.cmdText, + rawCommand: prepared.plan.commandText, cwd: prepared.plan.cwd ?? cwd, env, timeoutMs: commandTimeoutMs, @@ -699,8 +699,6 @@ export function createNodesTool(options?: { { ...gatewayOpts, timeoutMs: APPROVAL_TIMEOUT_MS + 5_000 }, { id: approvalId, - command: prepared.cmdText, - commandArgv: prepared.plan.argv, systemRunPlan: prepared.plan, cwd: prepared.plan.cwd ?? cwd, nodeId, diff --git a/src/cli/daemon-cli/lifecycle.test.ts b/src/cli/daemon-cli/lifecycle.test.ts index 3f0ed6d531c..f1e87fc4938 100644 --- a/src/cli/daemon-cli/lifecycle.test.ts +++ b/src/cli/daemon-cli/lifecycle.test.ts @@ -36,17 +36,16 @@ const renderGatewayPortHealthDiagnostics = vi.fn(() => ["diag: unhealthy port"]) const renderRestartDiagnostics = vi.fn(() => ["diag: unhealthy runtime"]); const resolveGatewayPort = vi.fn(() => 18789); const findGatewayPidsOnPortSync = vi.fn<(port: number) => number[]>(() => []); -const probeGateway = - vi.fn< - (opts: { - url: string; - auth?: { token?: string; password?: string }; - timeoutMs: number; - }) => Promise<{ - ok: boolean; - configSnapshot: unknown; - }> - >(); +const probeGateway = vi.fn< + (opts: { + url: string; + auth?: { token?: string; password?: string }; + timeoutMs: number; + }) => Promise<{ + ok: boolean; + configSnapshot: unknown; + }> +>(); const isRestartEnabled = vi.fn<(config?: { commands?: unknown }) => boolean>(() => true); const loadConfig = vi.fn(() => ({})); diff --git a/src/cli/nodes-cli.coverage.test.ts b/src/cli/nodes-cli.coverage.test.ts index cba8a8de7fb..81d0f17c07c 100644 --- a/src/cli/nodes-cli.coverage.test.ts +++ b/src/cli/nodes-cli.coverage.test.ts @@ -186,11 +186,10 @@ describe("nodes-cli coverage", () => { }); expect(invoke?.params?.timeoutMs).toBe(5000); const approval = getApprovalRequestCall(); - expect(approval?.params?.["commandArgv"]).toEqual(["echo", "hi"]); expect(approval?.params?.["systemRunPlan"]).toEqual({ argv: ["echo", "hi"], cwd: "/tmp", - rawCommand: "echo hi", + commandText: "echo hi", commandPreview: null, agentId: "main", sessionKey: null, @@ -221,11 +220,10 @@ describe("nodes-cli coverage", () => { runId: expect.any(String), }); const approval = getApprovalRequestCall(); - expect(approval?.params?.["commandArgv"]).toEqual(["/bin/sh", "-lc", "echo hi"]); expect(approval?.params?.["systemRunPlan"]).toEqual({ argv: ["/bin/sh", "-lc", "echo hi"], cwd: null, - rawCommand: '/bin/sh -lc "echo hi"', + commandText: '/bin/sh -lc "echo hi"', commandPreview: "echo hi", agentId: "main", sessionKey: null, diff --git a/src/cli/nodes-cli/register.invoke.ts b/src/cli/nodes-cli/register.invoke.ts index 71a3e2361e4..0bd1fdad895 100644 --- a/src/cli/nodes-cli/register.invoke.ts +++ b/src/cli/nodes-cli/register.invoke.ts @@ -189,7 +189,6 @@ async function maybeRequestNodesRunApproval(params: { opts: NodesRunOpts; nodeId: string; agentId: string | undefined; - preparedCmdText: string; approvalPlan: ReturnType["plan"]; hostSecurity: ExecSecurity; hostAsk: ExecAsk; @@ -215,8 +214,6 @@ async function maybeRequestNodesRunApproval(params: { params.opts, { id: approvalId, - command: params.preparedCmdText, - commandArgv: params.approvalPlan.argv, systemRunPlan: params.approvalPlan, cwd: params.approvalPlan.cwd, nodeId: params.nodeId, @@ -272,7 +269,7 @@ function buildSystemRunInvokeParams(params: { command: "system.run", params: { command: params.approvalPlan.argv, - rawCommand: params.approvalPlan.rawCommand, + rawCommand: params.approvalPlan.commandText, cwd: params.approvalPlan.cwd, env: params.nodeEnv, timeoutMs: params.timeoutMs, @@ -403,7 +400,6 @@ export function registerNodesInvokeCommands(nodes: Command) { opts, nodeId, agentId, - preparedCmdText: preparedContext.prepared.cmdText, approvalPlan, hostSecurity: approvals.hostSecurity, hostAsk: approvals.hostAsk, diff --git a/src/discord/monitor/exec-approvals.ts b/src/discord/monitor/exec-approvals.ts index 79635bd5ebe..87dc0c9a07d 100644 --- a/src/discord/monitor/exec-approvals.ts +++ b/src/discord/monitor/exec-approvals.ts @@ -16,6 +16,7 @@ import type { DiscordExecApprovalConfig } from "../../config/types.discord.js"; import { GatewayClient } from "../../gateway/client.js"; import { createOperatorApprovalsGatewayClient } from "../../gateway/operator-approvals-client.js"; import type { EventFrame } from "../../gateway/protocol/index.js"; +import { resolveExecApprovalCommandDisplay } from "../../infra/exec-approval-command-display.js"; import { getExecApprovalApproverDmNoticeText } from "../../infra/exec-approval-reply.js"; import type { ExecApprovalDecision, @@ -257,12 +258,11 @@ function createExecApprovalRequestContainer(params: { accountId: string; actionRow?: Row