fix(codex): require human approval for app-server commands (#103457)

* fix(codex): require human approval for app-server commands

* docs(exec): clarify Codex approval fallback

(cherry picked from commit a97633a9ad)
This commit is contained in:
Peter Steinberger
2026-07-10 08:07:18 +01:00
committed by Dallin Romney
parent c1a87890b9
commit addcb1a5a8
6 changed files with 50 additions and 420 deletions
+3
View File
@@ -205,6 +205,9 @@ Examples that strict mode catches:
In strict mode these commands need reviewer or explicit approval. With
`tools.exec.mode: "auto"`, the reviewer may grant one low-risk execution when
the command has an enforceable plan; otherwise OpenClaw asks a human.
`Codex app-server` command approvals that reach the reviewer fallback ask a
human because their approval requests do not expose an enforceable resolved
executable.
`allow-always` does not persist new allowlist entries for inline-eval commands.
### `tools.exec.commandHighlighting`
+22
View File
@@ -139,6 +139,28 @@ Example:
}
```
### Modes
`tools.exec.mode` is the normalized policy knob. Setting it derives `security`/`ask` and cannot be combined with explicit `tools.exec.security`/`tools.exec.ask`.
| Mode | security | ask | Behavior |
| ----------- | ----------- | --------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `deny` | `deny` | `off` | Exec is denied. |
| `allowlist` | `allowlist` | `off` | Only allowlisted/safe-bin commands run; nothing else is asked. |
| `ask` | `allowlist` | `on-miss` | Allowlist matches run directly; everything else asks a human. |
| `auto` | `allowlist` | `on-miss` | Allowlist/safe-bin matches run directly; everything else routes through OpenClaw's native auto reviewer before asking a human. |
| `full` | `full` | `off` | No approval gate. |
`ask`/`ask=always` still asks a human every time regardless of mode.
Auto-review approval is single-use. On the gateway, OpenClaw supplies the resolved executable path to the reviewer and pins execution to that same path. Commands that cannot be reduced to one enforceable execution plan—such as heredocs, shell expansions, or unsupported wrapper quoting—fall back to human approval even if the model would otherwise allow them.
Codex app-server command approvals that are not already decided by explicit runtime or native policy use the human approval route. OpenClaw does not run its configured exec reviewer for these requests because Codex does not expose an enforceable resolved executable that can bind the review decision to the command Codex runs.
### Inline eval (`strictInlineEval`)
When `tools.exec.strictInlineEval` is `true`, inline interpreter-eval forms require reviewer or explicit approval: `python -c`, `node -e`, `ruby -e`, `perl -e`, `php -r`, `lua -e`, `osascript -e`, and similar forms across other supported interpreters and command carriers (`awk`, `find -exec`, `make`, `sed`, `xargs`, and more). In `mode=auto`, the normal exec approval path may let the native auto reviewer allow a clearly low-risk one-off command; direct node-host `system.run` calls still require an explicit approval because they cannot hand the command to a human approval route. If the reviewer asks, the request goes to a human. `allow-always` can still persist benign interpreter/script invocations, but inline-eval forms do not become durable allow rules.
### PATH handling
- `host=gateway`: merges your login-shell `PATH` into the exec environment. `env.PATH` overrides are
@@ -250,9 +250,8 @@ describe("Codex app-server approval bridge", () => {
findApprovalEvent(params, { status: "approved", approvalId: "plugin:approval-1" });
});
it("uses the configured OpenClaw exec auto-review model before plugin approvals", async () => {
it("keeps configured exec auto-review on the human approval route", async () => {
const params = createParams();
params.workspaceDir = "/workspace";
params.config = {
tools: {
exec: {
@@ -269,6 +268,9 @@ describe("Codex app-server approval bridge", () => {
rationale: "read-only version check",
risk: "low",
});
mockCallGatewayTool
.mockResolvedValueOnce({ id: "plugin:approval-auto-review", status: "accepted" })
.mockResolvedValueOnce({ id: "plugin:approval-auto-review", decision: "deny" });
const result = await handleCodexAppServerApprovalRequest({
method: "item/commandExecution/requestApproval",
@@ -281,43 +283,15 @@ describe("Codex app-server approval bridge", () => {
paramsForRun: params,
threadId: "thread-1",
turnId: "turn-1",
execPolicy: { mode: "auto" },
execReviewerAgentId: "main",
internalExecAutoReview: true,
});
expect(result).toEqual({ decision: "accept" });
expect(mockCallGatewayTool).not.toHaveBeenCalled();
expect(mockReviewExecRequestWithConfiguredModel).toHaveBeenCalledWith({
cfg: params.config,
agentId: "main",
reviewer: {
model: "openai/gpt-5.5-mini",
timeoutMs: 12_000,
},
input: {
command: "node --version",
argv: ["node", "--version"],
cwd: "/workspace",
envKeys: undefined,
host: "codex-app-server",
reason: "approval-required",
analysis: {
parsed: true,
allowlistMatched: false,
inlineEval: false,
},
agent: {
id: "main",
sessionKey: "agent:main:session-1",
},
},
});
findApprovalEvent(params, {
status: "approved",
message:
"Codex app-server command approval granted by OpenClaw exec auto-reviewer: read-only version check",
});
expect(result).toEqual({ decision: "decline" });
expect(mockReviewExecRequestWithConfiguredModel).not.toHaveBeenCalled();
expect(mockCallGatewayTool.mock.calls.map(([method]) => method)).toEqual([
"plugin.approval.request",
"plugin.approval.waitDecision",
]);
findApprovalEvent(params, { status: "denied", approvalId: "plugin:approval-auto-review" });
});
it("falls back to plugin approval when no exec auto-review model is configured", async () => {
@@ -344,8 +318,6 @@ describe("Codex app-server approval bridge", () => {
paramsForRun: params,
threadId: "thread-1",
turnId: "turn-1",
execPolicy: { mode: "auto" },
internalExecAutoReview: true,
});
expect(result).toEqual({ decision: "accept" });
@@ -393,8 +365,6 @@ describe("Codex app-server approval bridge", () => {
paramsForRun: params,
threadId: "thread-1",
turnId: "turn-1",
execPolicy: { mode: "auto" },
internalExecAutoReview: true,
});
expect(result).toEqual({ decision: "accept" });
@@ -439,8 +409,6 @@ describe("Codex app-server approval bridge", () => {
paramsForRun: params,
threadId: "thread-1",
turnId: "turn-1",
execPolicy: { mode: "auto" },
internalExecAutoReview: true,
});
expect(result).toEqual({ decision: "accept" });
@@ -548,8 +516,6 @@ describe("Codex app-server approval bridge", () => {
paramsForRun: params,
threadId: "thread-1",
turnId: "turn-1",
execPolicy: { mode: "auto" },
internalExecAutoReview: true,
});
expect(result).toEqual({ decision: "accept" });
@@ -602,8 +568,6 @@ describe("Codex app-server approval bridge", () => {
paramsForRun: params,
threadId: "thread-1",
turnId: "turn-1",
execPolicy: { mode: "auto" },
internalExecAutoReview: true,
});
expect(result).toEqual({ decision: "accept" });
@@ -614,7 +578,7 @@ describe("Codex app-server approval bridge", () => {
]);
});
it("keeps exec auto-review when only an agent-specific alias matches the OpenAI reviewer", async () => {
it("keeps agent-scoped exec reviewer configuration on the human approval route", async () => {
const params = createParams();
params.config = {
agents: {
@@ -643,6 +607,9 @@ describe("Codex app-server approval bridge", () => {
rationale: "real OpenAI reviewer",
risk: "low",
});
mockCallGatewayTool
.mockResolvedValueOnce({ id: "plugin:approval-agent-reviewer", status: "accepted" })
.mockResolvedValueOnce({ id: "plugin:approval-agent-reviewer", decision: "allow-once" });
const result = await handleCodexAppServerApprovalRequest({
method: "item/commandExecution/requestApproval",
@@ -655,22 +622,14 @@ describe("Codex app-server approval bridge", () => {
paramsForRun: params,
threadId: "thread-1",
turnId: "turn-1",
execPolicy: { mode: "auto" },
execReviewerAgentId: "main",
internalExecAutoReview: true,
});
expect(result).toEqual({ decision: "accept" });
expect(mockReviewExecRequestWithConfiguredModel).toHaveBeenCalledWith(
expect.objectContaining({
cfg: params.config,
agentId: "main",
reviewer: {
model: "openai/gpt-5.5-mini@work",
},
}),
);
expect(mockCallGatewayTool).not.toHaveBeenCalled();
expect(mockReviewExecRequestWithConfiguredModel).not.toHaveBeenCalled();
expect(mockCallGatewayTool.mock.calls.map(([method]) => method)).toEqual([
"plugin.approval.request",
"plugin.approval.waitDecision",
]);
});
it("falls back to plugin approval when OpenAI reviewer uses a custom environment base URL", async () => {
@@ -706,8 +665,6 @@ describe("Codex app-server approval bridge", () => {
paramsForRun: params,
threadId: "thread-1",
turnId: "turn-1",
execPolicy: { mode: "auto" },
internalExecAutoReview: true,
});
expect(result).toEqual({ decision: "accept" });
@@ -758,8 +715,6 @@ describe("Codex app-server approval bridge", () => {
paramsForRun: params,
threadId: "thread-1",
turnId: "turn-1",
execPolicy: { mode: "auto" },
internalExecAutoReview: true,
});
expect(result).toEqual({ decision: "accept" });
@@ -810,8 +765,6 @@ describe("Codex app-server approval bridge", () => {
paramsForRun: params,
threadId: "thread-1",
turnId: "turn-1",
execPolicy: { mode: "auto" },
internalExecAutoReview: true,
});
expect(result).toEqual({ decision: "accept" });
@@ -858,8 +811,6 @@ describe("Codex app-server approval bridge", () => {
paramsForRun: params,
threadId: "thread-1",
turnId: "turn-1",
execPolicy: { mode: "auto" },
internalExecAutoReview: true,
});
expect(result).toEqual({ decision: "acceptForSession" });
@@ -902,8 +853,6 @@ describe("Codex app-server approval bridge", () => {
paramsForRun: params,
threadId: "thread-1",
turnId: "turn-1",
execPolicy: { mode: "auto" },
internalExecAutoReview: true,
});
expect(result).toEqual({ decision: "accept" });
@@ -951,8 +900,6 @@ describe("Codex app-server approval bridge", () => {
paramsForRun: params,
threadId: "thread-1",
turnId: "turn-1",
execPolicy: { mode: "auto" },
internalExecAutoReview: true,
});
expect(result).toEqual({ decision: "accept" });
@@ -998,8 +945,6 @@ describe("Codex app-server approval bridge", () => {
paramsForRun: params,
threadId: "thread-1",
turnId: "turn-1",
execPolicy: { mode: "auto" },
internalExecAutoReview: true,
});
expect(result).toEqual({ decision: "accept" });
@@ -1049,8 +994,6 @@ describe("Codex app-server approval bridge", () => {
paramsForRun: params,
threadId: "thread-1",
turnId: "turn-1",
execPolicy: { mode: "auto" },
internalExecAutoReview: true,
});
expect(result).toEqual({
@@ -1067,7 +1010,7 @@ describe("Codex app-server approval bridge", () => {
]);
});
it("falls back to plugin approval when the exec auto-review model asks", async () => {
it("does not invoke the exec auto-review model before plugin approval", async () => {
const params = createParams();
params.config = {
tools: {
@@ -1099,71 +1042,16 @@ describe("Codex app-server approval bridge", () => {
paramsForRun: params,
threadId: "thread-1",
turnId: "turn-1",
execPolicy: { mode: "auto" },
internalExecAutoReview: true,
});
expect(result).toEqual({ decision: "accept" });
expect(mockReviewExecRequestWithConfiguredModel).toHaveBeenCalledTimes(1);
expect(mockReviewExecRequestWithConfiguredModel).not.toHaveBeenCalled();
expect(mockCallGatewayTool.mock.calls.map(([method]) => method)).toEqual([
"plugin.approval.request",
"plugin.approval.waitDecision",
]);
});
it("cancels command approvals when the run aborts during exec auto-review", async () => {
const params = createParams();
params.config = {
tools: {
exec: {
mode: "auto",
reviewer: {
model: "openai/gpt-5.5-mini",
},
},
},
} as EmbeddedRunAttemptParams["config"];
const abortController = new AbortController();
mockReviewExecRequestWithConfiguredModel.mockImplementationOnce(
() =>
new Promise((resolve) => {
setTimeout(
() =>
resolve({
decision: "allow-once",
rationale: "late allow",
risk: "low",
}),
50,
);
}),
);
const resultPromise = handleCodexAppServerApprovalRequest({
method: "item/commandExecution/requestApproval",
requestParams: {
threadId: "thread-1",
turnId: "turn-1",
itemId: "cmd-auto-review-abort",
command: "node --version",
},
paramsForRun: params,
threadId: "thread-1",
turnId: "turn-1",
execPolicy: { mode: "auto" },
internalExecAutoReview: true,
signal: abortController.signal,
});
abortController.abort(new Error("run stopped"));
await expect(resultPromise).resolves.toEqual({ decision: "cancel" });
expect(mockCallGatewayTool).not.toHaveBeenCalled();
findApprovalEvent(params, {
status: "failed",
message: "Codex app-server approval cancelled because the run stopped.",
});
});
it("normalizes prefixed channel targets for OpenClaw tool policy context", async () => {
const params = createParams();
params.messageChannel = "telegram";
@@ -1,7 +1,3 @@
import {
buildExecAutoReviewInputForShellCommand,
reviewExecRequestWithConfiguredModel,
} from "openclaw/plugin-sdk/agent-harness-exec-review-runtime";
/**
* Bridges Codex app-server approval requests into OpenClaw policy hooks and
* plugin approval UX.
@@ -18,13 +14,9 @@ import {
type NativeHookRelayRegistrationHandle,
runBeforeToolCallHook,
} from "openclaw/plugin-sdk/agent-harness-runtime";
import { normalizeAgentId } from "openclaw/plugin-sdk/routing";
import { normalizeTrimmedStringList } from "openclaw/plugin-sdk/string-coerce-runtime";
import { formatCodexDisplayText } from "../command-formatters.js";
import {
isTrustedCodexModelBackedOpenAIProvider,
type OpenClawExecPolicyForCodexAppServer,
} from "./config.js";
import { resolveCodexToolAbortTerminalReason } from "./dynamic-tool-execution.js";
import {
approvalRequestExplicitlyUnavailable,
mapExecDecisionToOutcome,
@@ -81,9 +73,6 @@ export async function handleCodexAppServerApprovalRequest(params: {
NativeHookRelayRegistrationHandle,
"allowedEvents" | "generation" | "relayId"
>;
execPolicy?: Pick<OpenClawExecPolicyForCodexAppServer, "mode">;
execReviewerAgentId?: string;
internalExecAutoReview?: boolean;
autoApprove?: boolean;
signal?: AbortSignal;
}): Promise<JsonValue | undefined> {
@@ -149,29 +138,8 @@ export async function handleCodexAppServerApprovalRequest(params: {
});
return buildApprovalResponse(params.method, context.requestParams, "approved-session");
}
const autoReviewOutcome = await runInternalExecAutoReviewForApprovalRequest({
enabled: params.internalExecAutoReview === true && params.execPolicy?.mode === "auto",
method: params.method,
requestParams,
paramsForRun: params.paramsForRun,
context,
agentId: params.execReviewerAgentId,
signal: params.signal,
});
if (autoReviewOutcome?.outcome === "approved-once") {
emitApprovalEvent(params.paramsForRun, {
phase: "resolved",
kind: context.kind,
status: "approved",
title: context.title,
...context.eventDetails,
...approvalEventScope(params.method, autoReviewOutcome.outcome),
message: autoReviewOutcome.reason,
});
return buildApprovalResponse(params.method, context.requestParams, autoReviewOutcome.outcome);
}
// Native hook/model policy did not decide; fall back to the OpenClaw
// approval route so user-facing runs still get an approval prompt.
// Codex app-server approval requests do not expose an enforceable resolved
// executable, so unresolved requests must stay on the human approval route.
const requestResult = await requestPluginApproval({
paramsForRun: params.paramsForRun,
title: context.title,
@@ -381,244 +349,6 @@ type ApprovalPolicyOutcome =
| { outcome: "approved-once" | "approved-session" }
| { outcome: "no-decision" };
async function runInternalExecAutoReviewForApprovalRequest(params: {
enabled: boolean;
method: string;
requestParams: JsonObject | undefined;
paramsForRun: EmbeddedRunAttemptParams;
context: ApprovalContext;
agentId?: string;
signal?: AbortSignal;
}): Promise<{ outcome: "approved-once"; reason: string } | undefined> {
if (!params.enabled || params.method !== "item/commandExecution/requestApproval") {
return undefined;
}
if (hasCommandApprovalCapabilityAmendments(params.requestParams)) {
return undefined;
}
const input = await buildAppServerExecAutoReviewInput({
requestParams: params.requestParams,
paramsForRun: params.paramsForRun,
});
if (!input) {
return undefined;
}
const reviewerConfig = resolveExecReviewerConfig(params.paramsForRun, params.agentId);
if (
!canUseInternalExecAutoReviewReviewer(
reviewerConfig,
params.paramsForRun.config,
process.env,
params.paramsForRun.agentDir,
)
) {
return undefined;
}
const decision = await waitForInternalExecAutoReviewDecision({
signal: params.signal,
promise: reviewExecRequestWithConfiguredModel({
cfg: params.paramsForRun.config,
agentId: params.agentId ?? params.paramsForRun.agentId,
reviewer: reviewerConfig,
input,
}),
});
if (decision.decision !== "allow-once" || decision.risk !== "low") {
return undefined;
}
return {
outcome: "approved-once",
reason: `Codex app-server command approval granted by OpenClaw exec auto-reviewer: ${formatCodexDisplayText(
decision.rationale,
)}`,
};
}
async function waitForInternalExecAutoReviewDecision(params: {
signal?: AbortSignal;
promise: Promise<Awaited<ReturnType<typeof reviewExecRequestWithConfiguredModel>>>;
}): Promise<Awaited<ReturnType<typeof reviewExecRequestWithConfiguredModel>>> {
if (!params.signal) {
return params.promise;
}
if (params.signal.aborted) {
throw toCodexAppServerApprovalCancellationError(params.signal.reason);
}
let onAbort: (() => void) | undefined;
const abortPromise = new Promise<never>((_, reject) => {
onAbort = () => reject(toCodexAppServerApprovalCancellationError(params.signal?.reason));
params.signal?.addEventListener("abort", onAbort, { once: true });
});
try {
return await Promise.race([params.promise, abortPromise]);
} finally {
if (onAbort) {
params.signal.removeEventListener("abort", onAbort);
}
}
}
function toCodexAppServerApprovalCancellationError(reason: unknown): Error {
if (reason instanceof Error) {
return reason;
}
return new Error(
typeof reason === "string" && reason.trim() ? reason : "Codex app-server approval cancelled.",
);
}
async function buildAppServerExecAutoReviewInput(params: {
requestParams: JsonObject | undefined;
paramsForRun: EmbeddedRunAttemptParams;
}) {
const command = readString(params.requestParams, "command");
if (!command) {
return undefined;
}
return buildExecAutoReviewInputForShellCommand({
command,
cwd: readString(params.requestParams, "cwd") ?? params.paramsForRun.workspaceDir ?? null,
host: "codex-app-server",
agent: {
id: params.paramsForRun.agentId ?? null,
sessionKey: params.paramsForRun.sessionKey ?? null,
},
});
}
function hasCommandApprovalCapabilityAmendments(requestParams: JsonObject | undefined): boolean {
return (
hasNonEmptyJsonObject(requestParams?.additionalPermissions) ||
hasNonEmptyJsonObject(requestParams?.networkApprovalContext) ||
hasNonEmptyJsonObject(requestParams?.proposedExecpolicyAmendment) ||
hasNonEmptyArray(requestParams?.proposedExecpolicyAmendment) ||
hasNonEmptyArray(requestParams?.proposedNetworkPolicyAmendments) ||
findAvailableCommandAmendmentDecision(requestParams) !== undefined ||
commandAcceptDecisionUnavailable(requestParams)
);
}
function commandAcceptDecisionUnavailable(requestParams: JsonObject | undefined): boolean {
const available = requestParams?.availableDecisions;
return Array.isArray(available) && !available.includes("accept");
}
function hasNonEmptyJsonObject(value: unknown): boolean {
return isJsonObject(value) && Object.keys(value).length > 0;
}
function hasNonEmptyArray(value: unknown): boolean {
return Array.isArray(value) && value.length > 0;
}
function resolveExecReviewerConfig(
params: EmbeddedRunAttemptParams,
agentId?: string,
): Record<string, unknown> | undefined {
const configRoot = readUnknownRecord(params.config);
const globalExec = readUnknownRecord(readUnknownRecord(configRoot?.tools)?.exec);
const agentExec = resolveAgentExecConfig(configRoot, agentId ?? params.agentId);
return readUnknownRecord(agentExec?.reviewer) ?? readUnknownRecord(globalExec?.reviewer);
}
function canUseInternalExecAutoReviewReviewer(
reviewerConfig: Record<string, unknown> | undefined,
config: EmbeddedRunAttemptParams["config"] | undefined,
env: NodeJS.ProcessEnv | undefined,
agentDir: string | undefined,
): boolean {
const model = readExecReviewerModelRef(reviewerConfig);
const slashIndex = model?.indexOf("/") ?? -1;
if (!model || slashIndex <= 0) {
return false;
}
if (configuredAgentModelAliasMatches(config, model)) {
return false;
}
const provider = model.slice(0, slashIndex).trim().toLowerCase();
if (provider !== "openai") {
return false;
}
return isTrustedCodexModelBackedOpenAIProvider({
config,
env,
agentDir,
model: model.slice(slashIndex + 1).trim(),
});
}
function readExecReviewerModelRef(
reviewerConfig: Record<string, unknown> | undefined,
): string | undefined {
const model = reviewerConfig?.model;
if (typeof model === "string") {
return model.trim() || undefined;
}
const primary = readUnknownRecord(model)?.primary;
return typeof primary === "string" && primary.trim() ? primary.trim() : undefined;
}
function configuredAgentModelAliasMatches(
config: EmbeddedRunAttemptParams["config"] | undefined,
modelRef: string,
): boolean {
const normalizedModelRef = normalizeExecReviewerAliasRef(modelRef);
const agents = readUnknownRecord(readUnknownRecord(config)?.agents);
return agentModelAliasMatches(readUnknownRecord(agents?.defaults), normalizedModelRef);
}
function agentModelAliasMatches(
agentConfig: Record<string, unknown> | undefined,
normalizedModelRef: string,
): boolean {
const models = readUnknownRecord(agentConfig?.models);
if (!models) {
return false;
}
for (const entry of Object.values(models)) {
const alias = readUnknownRecord(entry)?.alias;
if (typeof alias === "string" && normalizeExecReviewerAliasRef(alias) === normalizedModelRef) {
return true;
}
}
return false;
}
function normalizeExecReviewerAliasRef(modelRef: string): string {
const trimmed = modelRef.trim().toLowerCase();
const slashIndex = trimmed.indexOf("/");
const authProfileIndex = trimmed.indexOf("@", slashIndex + 1);
return authProfileIndex > 0 ? trimmed.slice(0, authProfileIndex) : trimmed;
}
function resolveAgentExecConfig(
configRoot: Record<string, unknown> | undefined,
agentId: string | undefined,
): Record<string, unknown> | undefined {
const normalizedAgentId = agentId ? normalizeAgentId(agentId) : undefined;
if (!normalizedAgentId) {
return undefined;
}
const agentList = readUnknownRecord(configRoot?.agents)?.list;
if (!Array.isArray(agentList)) {
return undefined;
}
for (const entry of agentList) {
const record = readUnknownRecord(entry);
if (typeof record?.id !== "string" || normalizeAgentId(record.id) !== normalizedAgentId) {
continue;
}
return readUnknownRecord(readUnknownRecord(record.tools)?.exec);
}
return undefined;
}
function readUnknownRecord(value: unknown): Record<string, unknown> | undefined {
return value && typeof value === "object" && !Array.isArray(value)
? (value as Record<string, unknown>)
: undefined;
}
async function runOpenClawToolPolicyForApprovalRequest(params: {
method: string;
requestParams: JsonObject | undefined;
@@ -142,7 +142,6 @@ import {
resolveOpenClawExecPolicyForCodexAppServer,
shouldAutoApproveCodexAppServerApprovals,
type CodexAppServerRuntimeOptions,
type OpenClawExecPolicyForCodexAppServer,
} from "./config.js";
import {
type CodexProjectedContextRange,
@@ -2154,9 +2153,6 @@ export async function runCodexAppServerAttempt(
threadId: thread.threadId,
turnId,
nativeHookRelay,
execPolicy,
execReviewerAgentId: sessionAgentId,
internalExecAutoReview: appServer.approvalsReviewer === "user",
autoApprove: shouldAutoApproveCodexAppServerApprovals(appServer),
signal: runAbortController.signal,
});
@@ -3434,9 +3430,6 @@ function handleApprovalRequest(params: {
threadId: string;
turnId: string;
nativeHookRelay?: NativeHookRelayRegistrationHandle;
execPolicy?: Pick<OpenClawExecPolicyForCodexAppServer, "mode">;
execReviewerAgentId?: string;
internalExecAutoReview?: boolean;
autoApprove?: boolean;
signal?: AbortSignal;
}): Promise<JsonValue | undefined> {
@@ -3447,9 +3440,6 @@ function handleApprovalRequest(params: {
threadId: params.threadId,
turnId: params.turnId,
nativeHookRelay: params.nativeHookRelay,
execPolicy: params.execPolicy,
execReviewerAgentId: params.execReviewerAgentId,
internalExecAutoReview: params.internalExecAutoReview,
autoApprove: params.autoApprove,
signal: params.signal,
});
@@ -321,9 +321,6 @@ export async function runCodexAppServerSideQuestion(
threadId: childThreadId,
turnId,
nativeHookRelay,
execPolicy,
execReviewerAgentId: sessionAgentId,
internalExecAutoReview: modelScopedAppServer.approvalsReviewer === "user",
autoApprove: shouldAutoApproveCodexAppServerApprovals({
approvalPolicy,
networkProxy: modelScopedAppServer.networkProxy,