























@@ -1,5 +1,9 @@
11import { beforeAll, beforeEach, describe, expect, it, vi, type Mock } from "vitest";
22import type { OpenClawConfig } from "../../config/config.js";
3+import {
4+clearApprovalNativeRouteStateForTest,
5+createApprovalNativeRouteReporter,
6+} from "../../infra/approval-native-route-coordinator.js";
37import type { SessionBindingRecord } from "../../infra/outbound/session-binding-service.js";
48import type {
59AcpRuntime,
@@ -701,7 +705,14 @@ describe("dispatchReplyFromConfig", () => {
701705}),
702706outbound: {
703707deliveryMode: "direct",
704-shouldSuppressLocalPayloadPrompt: ({ payload }: { payload: ReplyPayload }) =>
708+shouldSuppressLocalPayloadPrompt: ({
709+ payload,
710+ hint,
711+}: {
712+payload: ReplyPayload;
713+hint?: { nativeRouteActive?: boolean };
714+}) =>
715+hint?.nativeRouteActive === true &&
705716Boolean(
706717payload.channelData &&
707718typeof payload.channelData === "object" &&
@@ -719,6 +730,7 @@ describe("dispatchReplyFromConfig", () => {
719730},
720731]),
721732);
733+clearApprovalNativeRouteStateForTest();
722734acpManagerRuntimeMocks.getAcpSessionManager.mockReset();
723735acpManagerRuntimeMocks.getAcpSessionManager.mockReturnValue(createMockAcpSessionManager());
724736resetInboundDedupe();
@@ -2581,7 +2593,7 @@ describe("dispatchReplyFromConfig", () => {
25812593expect(replyResolver).toHaveBeenCalledTimes(1);
25822594});
258325952584-it("suppresses local discord exec approval tool prompts when discord approvals are enabled", async () => {
2596+it("keeps local discord exec approval tool prompts when the native runtime is inactive", async () => {
25852597setNoAbort();
25862598const cfg = {
25872599channels: {
@@ -2616,12 +2628,67 @@ describe("dispatchReplyFromConfig", () => {
2616262826172629await dispatchReplyFromConfig({ ctx, cfg, dispatcher, replyResolver });
261826302619-expect(dispatcher.sendToolResult).not.toHaveBeenCalled();
2631+expect(dispatcher.sendToolResult).toHaveBeenCalledWith(
2632+expect.objectContaining({ text: "Approval required." }),
2633+);
26202634expect(dispatcher.sendFinalReply).toHaveBeenCalledWith(
26212635expect.objectContaining({ text: "done" }),
26222636);
26232637});
262426382639+it("suppresses local discord exec approval tool prompts when the native runtime is active", async () => {
2640+setNoAbort();
2641+const cfg = {
2642+channels: {
2643+discord: {
2644+enabled: true,
2645+execApprovals: {
2646+enabled: true,
2647+approvers: ["123"],
2648+},
2649+},
2650+},
2651+} as OpenClawConfig;
2652+const reporter = createApprovalNativeRouteReporter({
2653+handledKinds: new Set(["exec"]),
2654+channel: "discord",
2655+channelLabel: "Discord",
2656+accountId: "default",
2657+requestGateway: async <T>() => ({ ok: true }) as T,
2658+});
2659+reporter.start();
2660+try {
2661+const dispatcher = createDispatcher();
2662+const ctx = buildTestCtx({
2663+Provider: "discord",
2664+Surface: "discord",
2665+AccountId: "default",
2666+});
2667+const replyResolver = vi.fn(async (_ctx: MsgContext, options?: GetReplyOptions) => {
2668+await options?.onToolResult?.({
2669+text: "Approval required.",
2670+channelData: {
2671+execApproval: {
2672+approvalId: "12345678-1234-1234-1234-123456789012",
2673+approvalSlug: "12345678",
2674+allowedDecisions: ["allow-once", "allow-always", "deny"],
2675+},
2676+},
2677+});
2678+return { text: "done" } as ReplyPayload;
2679+});
2680+2681+await dispatchReplyFromConfig({ ctx, cfg, dispatcher, replyResolver });
2682+2683+expect(dispatcher.sendToolResult).not.toHaveBeenCalled();
2684+expect(dispatcher.sendFinalReply).toHaveBeenCalledWith(
2685+expect.objectContaining({ text: "done" }),
2686+);
2687+} finally {
2688+await reporter.stop();
2689+}
2690+});
2691+26252692it("deduplicates same-agent inbound replies across main and direct session keys", async () => {
26262693setNoAbort();
26272694const cfg = emptyConfig;
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。