























@@ -7,6 +7,10 @@ const sharedClientMocks = vi.hoisted(() => ({
77getSharedCodexAppServerClient: vi.fn(),
88}));
9910+const execApprovalsRuntimeMocks = vi.hoisted(() => ({
11+loadExecApprovals: vi.fn(() => ({ version: 1 as const, agents: {} })),
12+}));
13+1014const agentRuntimeMocks = vi.hoisted(() => ({
1115ensureAuthProfileStore: vi.fn(),
1216loadAuthProfileStoreForSecretsRuntime: vi.fn(),
@@ -53,6 +57,14 @@ vi.mock("./app-server/shared-client.js", () => ({
5357getLeasedSharedCodexAppServerClient: sharedClientMocks.getSharedCodexAppServerClient,
5458releaseLeasedSharedCodexAppServerClient: vi.fn(),
5559}));
60+vi.mock("openclaw/plugin-sdk/exec-approvals-runtime", async (importOriginal) => {
61+const actual =
62+await importOriginal<typeof import("openclaw/plugin-sdk/exec-approvals-runtime")>();
63+return {
64+ ...actual,
65+loadExecApprovals: execApprovalsRuntimeMocks.loadExecApprovals,
66+};
67+});
5668vi.mock("openclaw/plugin-sdk/agent-runtime", () => agentRuntimeMocks);
57695870import {
@@ -78,6 +90,8 @@ describe("codex conversation binding", () => {
78907991afterEach(async () => {
8092sharedClientMocks.getSharedCodexAppServerClient.mockReset();
93+execApprovalsRuntimeMocks.loadExecApprovals.mockReset();
94+execApprovalsRuntimeMocks.loadExecApprovals.mockReturnValue({ version: 1, agents: {} });
8195agentRuntimeMocks.ensureAuthProfileStore.mockReset();
8296agentRuntimeMocks.loadAuthProfileStoreForSecretsRuntime.mockReset();
8397agentRuntimeMocks.resolveApiKeyForProfile.mockReset();
@@ -312,6 +326,38 @@ describe("codex conversation binding", () => {
312326expect(requests).toEqual([]);
313327});
314328329+it("applies host exec approval floors to configless native bind threads", async () => {
330+const sessionFile = path.join(tempDir, "session.jsonl");
331+const requests: Array<{ method: string; params: Record<string, unknown> }> = [];
332+execApprovalsRuntimeMocks.loadExecApprovals.mockReturnValue({
333+version: 1,
334+defaults: {
335+security: "deny",
336+ask: "off",
337+},
338+agents: {},
339+});
340+sharedClientMocks.getSharedCodexAppServerClient.mockResolvedValue({
341+request: vi.fn(async (method: string, requestParams: Record<string, unknown>) => {
342+requests.push({ method, params: requestParams });
343+return {
344+thread: { id: "thread-new", sessionId: "session-1", cwd: tempDir },
345+model: "gpt-5.4-mini",
346+};
347+}),
348+});
349+350+await expect(
351+startCodexConversationThread({
352+ sessionFile,
353+workspaceDir: tempDir,
354+model: "gpt-5.4-mini",
355+}),
356+).rejects.toThrow("tools.exec.mode=deny");
357+expect(execApprovalsRuntimeMocks.loadExecApprovals).toHaveBeenCalled();
358+expect(requests).toEqual([]);
359+});
360+315361it("clears the Codex app-server sidecar when a pending bind is denied", async () => {
316362const sessionFile = path.join(tempDir, "session.jsonl");
317363const sidecar = `${sessionFile}.codex-app-server.json`;
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。