
























@@ -1,34 +1,18 @@
11import {
2-callGatewayTool,
32type AgentApprovalEventData,
43type EmbeddedRunAttemptParams,
5-type ExecApprovalDecision,
64} from "openclaw/plugin-sdk/agent-harness";
5+import {
6+mapExecDecisionToOutcome,
7+requestPluginApproval,
8+type AppServerApprovalOutcome,
9+waitForPluginApprovalDecision,
10+} from "./plugin-approval-roundtrip.js";
711import { isJsonObject, type JsonObject, type JsonValue } from "./protocol.js";
8129-const DEFAULT_CODEX_APPROVAL_TIMEOUT_MS = 120_000;
1013const PERMISSION_DESCRIPTION_MAX_LENGTH = 700;
1114const PERMISSION_SAMPLE_LIMIT = 2;
1215const PERMISSION_VALUE_MAX_LENGTH = 48;
13-14-export type AppServerApprovalOutcome =
15-| "approved-once"
16-| "approved-session"
17-| "denied"
18-| "unavailable"
19-| "cancelled";
20-21-type ApprovalRequestResult = {
22-id?: string;
23-status?: string;
24-decision?: ExecApprovalDecision | null;
25-};
26-27-type ApprovalWaitResult = {
28-id?: string;
29-decision?: ExecApprovalDecision | null;
30-};
31-3216export async function handleCodexAppServerApprovalRequest(params: {
3317method: string;
3418requestParams: JsonValue | undefined;
@@ -52,29 +36,14 @@ export async function handleCodexAppServerApprovalRequest(params: {
5236});
53375438try {
55-const timeoutMs = DEFAULT_CODEX_APPROVAL_TIMEOUT_MS;
56-const requestResult: ApprovalRequestResult | undefined = await callGatewayTool(
57-"plugin.approval.request",
58-{ timeoutMs: timeoutMs + 10_000 },
59-{
60-pluginId: "openclaw-codex-app-server",
61-title: context.title,
62-description: context.description,
63-severity: context.severity,
64-toolName: context.toolName,
65-toolCallId: context.itemId,
66-agentId: params.paramsForRun.agentId,
67-sessionKey: params.paramsForRun.sessionKey,
68-turnSourceChannel:
69-params.paramsForRun.messageChannel ?? params.paramsForRun.messageProvider,
70-turnSourceTo: params.paramsForRun.currentChannelId,
71-turnSourceAccountId: params.paramsForRun.agentAccountId,
72-turnSourceThreadId: params.paramsForRun.currentThreadTs,
73- timeoutMs,
74-twoPhase: true,
75-},
76-{ expectFinal: false },
77-);
39+const requestResult = await requestPluginApproval({
40+paramsForRun: params.paramsForRun,
41+title: context.title,
42+description: context.description,
43+severity: context.severity,
44+toolName: context.toolName,
45+toolCallId: context.itemId,
46+});
78477948const approvalId = requestResult?.id;
8049if (!approvalId) {
@@ -84,6 +53,7 @@ export async function handleCodexAppServerApprovalRequest(params: {
8453status: "unavailable",
8554title: context.title,
8655 ...context.eventDetails,
56+ ...approvalEventScope(params.method, "denied"),
8757message: "Codex app-server approval route unavailable.",
8858});
8959return buildApprovalResponse(params.method, context.requestParams, "denied");
@@ -102,11 +72,7 @@ export async function handleCodexAppServerApprovalRequest(params: {
1027210373const decision = Object.prototype.hasOwnProperty.call(requestResult, "decision")
10474 ? requestResult.decision
105- : await waitForApprovalDecision({
106- approvalId,
107- timeoutMs,
108-signal: params.signal,
109-});
75+ : await waitForPluginApprovalDecision({ approvalId, signal: params.signal });
11076const outcome = mapExecDecisionToOutcome(decision);
1117711278emitApprovalEvent(params.paramsForRun, {
@@ -124,6 +90,7 @@ export async function handleCodexAppServerApprovalRequest(params: {
12490 approvalId,
12591approvalSlug: approvalId,
12692 ...context.eventDetails,
93+ ...approvalEventScope(params.method, outcome),
12794message: approvalResolutionMessage(outcome),
12895});
12996return buildApprovalResponse(params.method, context.requestParams, outcome);
@@ -135,6 +102,7 @@ export async function handleCodexAppServerApprovalRequest(params: {
135102status: cancelled ? "failed" : "unavailable",
136103title: context.title,
137104 ...context.eventDetails,
105+ ...approvalEventScope(params.method, cancelled ? "cancelled" : "denied"),
138106message: cancelled
139107 ? "Codex app-server approval cancelled because the run stopped."
140108 : `Codex app-server approval route failed: ${formatErrorMessage(error)}`,
@@ -176,18 +144,12 @@ function matchesCurrentTurn(
176144turnId: string,
177145): boolean {
178146if (!requestParams) {
179-return true;
147+return false;
180148}
181149const requestThreadId =
182150readString(requestParams, "threadId") ?? readString(requestParams, "conversationId");
183151const requestTurnId = readString(requestParams, "turnId");
184-if (requestThreadId && requestThreadId !== threadId) {
185-return false;
186-}
187-if (requestTurnId && requestTurnId !== turnId) {
188-return false;
189-}
190-return true;
152+return requestThreadId === threadId && requestTurnId === turnId;
191153}
192154193155function buildApprovalContext(params: {
@@ -248,37 +210,6 @@ function buildApprovalContext(params: {
248210};
249211}
250212251-async function waitForApprovalDecision(params: {
252-approvalId: string;
253-timeoutMs: number;
254-signal?: AbortSignal;
255-}): Promise<ExecApprovalDecision | null | undefined> {
256-const waitPromise: Promise<ApprovalWaitResult | undefined> = callGatewayTool(
257-"plugin.approval.waitDecision",
258-{ timeoutMs: params.timeoutMs + 10_000 },
259-{ id: params.approvalId },
260-);
261-if (!params.signal) {
262-return (await waitPromise)?.decision;
263-}
264-let onAbort: (() => void) | undefined;
265-const abortPromise = new Promise<never>((_, reject) => {
266-if (params.signal!.aborted) {
267-reject(params.signal!.reason);
268-return;
269-}
270-onAbort = () => reject(params.signal!.reason);
271-params.signal!.addEventListener("abort", onAbort, { once: true });
272-});
273-try {
274-return (await Promise.race([waitPromise, abortPromise]))?.decision;
275-} finally {
276-if (onAbort) {
277-params.signal.removeEventListener("abort", onAbort);
278-}
279-}
280-}
281-282213function commandApprovalDecision(
283214requestParams: JsonObject | undefined,
284215outcome: AppServerApprovalOutcome,
@@ -528,27 +459,12 @@ function hasAvailableDecision(requestParams: JsonObject | undefined, decision: s
528459return available.includes(decision);
529460}
530461531-function mapExecDecisionToOutcome(
532-decision: ExecApprovalDecision | null | undefined,
533-): AppServerApprovalOutcome {
534-if (decision === "allow-once") {
535-return "approved-once";
536-}
537-if (decision === "allow-always") {
538-return "approved-session";
539-}
540-if (decision === null || decision === undefined) {
541-return "unavailable";
542-}
543-return "denied";
544-}
545-546462function approvalResolutionMessage(outcome: AppServerApprovalOutcome): string {
547463if (outcome === "approved-session") {
548464return "Codex app-server approval granted for the session.";
549465}
550466if (outcome === "approved-once") {
551-return "Codex app-server approval granted once.";
467+return "Codex app-server approval granted for this turn.";
552468}
553469if (outcome === "cancelled") {
554470return "Codex app-server approval cancelled.";
@@ -559,6 +475,19 @@ function approvalResolutionMessage(outcome: AppServerApprovalOutcome): string {
559475return "Codex app-server approval denied.";
560476}
561477478+function approvalScopeForOutcome(outcome: AppServerApprovalOutcome): "turn" | "session" {
479+return outcome === "approved-session" ? "session" : "turn";
480+}
481+482+function approvalEventScope(
483+method: string,
484+outcome: AppServerApprovalOutcome,
485+): Pick<AgentApprovalEventData, "scope"> {
486+return method === "item/permissions/requestApproval"
487+ ? { scope: approvalScopeForOutcome(outcome) }
488+ : {};
489+}
490+562491function approvalKindForMethod(method: string): AgentApprovalEventData["kind"] {
563492if (method.includes("commandExecution") || method.includes("execCommand")) {
564493return "exec";
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。