From 274d300b92fff492af1bb005f5f74fd887100738 Mon Sep 17 00:00:00 2001 From: Charles Vien Date: Thu, 9 Jul 2026 14:00:13 -0700 Subject: [PATCH 1/4] map plan to read-only for codex cloud runs --- packages/agent/src/execution-mode.test.ts | 32 +++++++++++++++- packages/agent/src/execution-mode.ts | 37 ++++++++++++++++++- .../api-client/src/posthog-client.test.ts | 35 ++++++++++++++++++ packages/api-client/src/posthog-client.ts | 10 ++++- 4 files changed, 110 insertions(+), 4 deletions(-) diff --git a/packages/agent/src/execution-mode.test.ts b/packages/agent/src/execution-mode.test.ts index 669715968e..9b964d6f0e 100644 --- a/packages/agent/src/execution-mode.test.ts +++ b/packages/agent/src/execution-mode.test.ts @@ -1,5 +1,9 @@ import { describe, expect, it } from "vitest"; -import { getAvailableCodexModes, getAvailableModes } from "./execution-mode"; +import { + getAvailableCodexModes, + getAvailableModes, + resolveCloudInitialPermissionMode, +} from "./execution-mode"; describe("execution modes", () => { it("includes auto-accept permissions for claude sessions", () => { @@ -21,3 +25,29 @@ describe("execution modes", () => { ]); }); }); + +describe("resolveCloudInitialPermissionMode", () => { + it.each([ + ["codex", "auto", "auto"], + ["codex", "read-only", "read-only"], + ["codex", "full-access", "full-access"], + ["codex", "plan", "read-only"], + ["codex", "default", "auto"], + ["codex", "acceptEdits", "auto"], + ["codex", "bypassPermissions", "full-access"], + ["claude", "default", "default"], + ["claude", "acceptEdits", "acceptEdits"], + ["claude", "plan", "plan"], + ["claude", "bypassPermissions", "bypassPermissions"], + ["claude", "auto", "auto"], + ["claude", "read-only", "plan"], + ["claude", "full-access", "bypassPermissions"], + [undefined, "plan", "plan"], + [undefined, "read-only", "read-only"], + ] as const)( + "resolves %s adapter mode %s to %s", + (adapter, mode, expected) => { + expect(resolveCloudInitialPermissionMode(adapter, mode)).toBe(expected); + }, + ); +}); diff --git a/packages/agent/src/execution-mode.ts b/packages/agent/src/execution-mode.ts index d1a82a793c..fcc23a9b87 100644 --- a/packages/agent/src/execution-mode.ts +++ b/packages/agent/src/execution-mode.ts @@ -1,4 +1,4 @@ -import { CODEX_MODE_PRESETS } from "@posthog/shared"; +import { type Adapter, CODEX_MODE_PRESETS } from "@posthog/shared"; import { ALLOW_BYPASS } from "./utils/common"; export interface ModeInfo { @@ -81,3 +81,38 @@ export function getAvailableCodexModes(): ModeInfo[] { ? [...CODEX_MODE_PRESETS] : CODEX_MODE_PRESETS.filter((m) => m.id !== "full-access"); } + +// The cloud task-run API validates initial_permission_mode against a per-adapter +// enum that is narrower than the local pickers: codex accepts only its native +// modes (notably not "plan", which the local app-server adapter synthesizes). +// Degrade to the nearest mode with the same permission ceiling. +const CODEX_CLOUD_MODE_FALLBACKS: Record< + Exclude, + CodexNativeMode +> = { + default: "auto", + acceptEdits: "auto", + plan: "read-only", + bypassPermissions: "full-access", +}; + +const CLAUDE_CLOUD_MODE_FALLBACKS: Record< + Exclude, + CodeExecutionMode +> = { + "read-only": "plan", + "full-access": "bypassPermissions", +}; + +export function resolveCloudInitialPermissionMode( + adapter: Adapter | undefined, + mode: PermissionMode, +): PermissionMode { + if (adapter === "codex") { + return isCodexNativeMode(mode) ? mode : CODEX_CLOUD_MODE_FALLBACKS[mode]; + } + if (adapter === "claude") { + return isCodeExecutionMode(mode) ? mode : CLAUDE_CLOUD_MODE_FALLBACKS[mode]; + } + return mode; +} diff --git a/packages/api-client/src/posthog-client.test.ts b/packages/api-client/src/posthog-client.test.ts index ffb2a9f962..e16a949c35 100644 --- a/packages/api-client/src/posthog-client.test.ts +++ b/packages/api-client/src/posthog-client.test.ts @@ -77,6 +77,41 @@ describe("PostHogAPIClient", () => { ); }); + it("maps plan to read-only for cloud Codex runs", async () => { + const client = new PostHogAPIClient( + "http://localhost:8000", + async () => "token", + async () => "token", + 123, + ); + + const post = vi.fn().mockResolvedValue({ + id: "task-123", + title: "Task", + description: "Task", + created_at: "2026-04-14T00:00:00Z", + updated_at: "2026-04-14T00:00:00Z", + origin_product: "user_created", + }); + + (client as unknown as { api: { post: typeof post } }).api = { post }; + + await client.runTaskInCloud("task-123", "feature/codex-plan", { + adapter: "codex", + model: "gpt-5.4", + initialPermissionMode: "plan", + }); + + expect(post).toHaveBeenCalledWith( + "/api/projects/{project_id}/tasks/{id}/run/", + expect.objectContaining({ + body: expect.objectContaining({ + initial_permission_mode: "read-only", + }), + }), + ); + }); + it("rejects unsupported reasoning effort for cloud Codex runs", async () => { const client = new PostHogAPIClient( "http://localhost:8000", diff --git a/packages/api-client/src/posthog-client.ts b/packages/api-client/src/posthog-client.ts index 9d74ce0d2b..65ec798398 100644 --- a/packages/api-client/src/posthog-client.ts +++ b/packages/api-client/src/posthog-client.ts @@ -1,6 +1,9 @@ import "./generated.augment"; import { isSupportedReasoningEffort } from "@posthog/agent/adapters/reasoning-effort"; -import type { PermissionMode } from "@posthog/agent/execution-mode"; +import { + type PermissionMode, + resolveCloudInitialPermissionMode, +} from "@posthog/agent/execution-mode"; import type { Adapter, CloudRunSource, @@ -575,7 +578,10 @@ function buildCloudRunRequestBody( body.signal_report_id = options.signalReportId; } if (options?.initialPermissionMode) { - body.initial_permission_mode = options.initialPermissionMode; + body.initial_permission_mode = resolveCloudInitialPermissionMode( + options.adapter, + options.initialPermissionMode, + ); } if (options?.homeQuickAction) { body.home_quick_action = options.homeQuickAction; From 8d4a36814ebaf4ea99b6c7ecefdeda5bbeaac328 Mon Sep 17 00:00:00 2001 From: Charles Vien Date: Thu, 9 Jul 2026 14:10:16 -0700 Subject: [PATCH 2/4] only send permission mode with runtime adapter --- packages/agent/src/execution-mode.test.ts | 2 -- packages/agent/src/execution-mode.ts | 7 ++-- .../api-client/src/posthog-client.test.ts | 33 +++++++++++++++++++ packages/api-client/src/posthog-client.ts | 14 ++++---- 4 files changed, 43 insertions(+), 13 deletions(-) diff --git a/packages/agent/src/execution-mode.test.ts b/packages/agent/src/execution-mode.test.ts index 9b964d6f0e..4e69a9eee6 100644 --- a/packages/agent/src/execution-mode.test.ts +++ b/packages/agent/src/execution-mode.test.ts @@ -42,8 +42,6 @@ describe("resolveCloudInitialPermissionMode", () => { ["claude", "auto", "auto"], ["claude", "read-only", "plan"], ["claude", "full-access", "bypassPermissions"], - [undefined, "plan", "plan"], - [undefined, "read-only", "read-only"], ] as const)( "resolves %s adapter mode %s to %s", (adapter, mode, expected) => { diff --git a/packages/agent/src/execution-mode.ts b/packages/agent/src/execution-mode.ts index fcc23a9b87..3e406827e8 100644 --- a/packages/agent/src/execution-mode.ts +++ b/packages/agent/src/execution-mode.ts @@ -105,14 +105,11 @@ const CLAUDE_CLOUD_MODE_FALLBACKS: Record< }; export function resolveCloudInitialPermissionMode( - adapter: Adapter | undefined, + adapter: Adapter, mode: PermissionMode, ): PermissionMode { if (adapter === "codex") { return isCodexNativeMode(mode) ? mode : CODEX_CLOUD_MODE_FALLBACKS[mode]; } - if (adapter === "claude") { - return isCodeExecutionMode(mode) ? mode : CLAUDE_CLOUD_MODE_FALLBACKS[mode]; - } - return mode; + return isCodeExecutionMode(mode) ? mode : CLAUDE_CLOUD_MODE_FALLBACKS[mode]; } diff --git a/packages/api-client/src/posthog-client.test.ts b/packages/api-client/src/posthog-client.test.ts index e16a949c35..a82c0ed3aa 100644 --- a/packages/api-client/src/posthog-client.test.ts +++ b/packages/api-client/src/posthog-client.test.ts @@ -112,6 +112,39 @@ describe("PostHogAPIClient", () => { ); }); + it("omits the permission mode when no adapter is set", async () => { + const client = new PostHogAPIClient( + "http://localhost:8000", + async () => "token", + async () => "token", + 123, + ); + + const post = vi.fn().mockResolvedValue({ + id: "task-123", + title: "Task", + description: "Task", + created_at: "2026-04-14T00:00:00Z", + updated_at: "2026-04-14T00:00:00Z", + origin_product: "user_created", + }); + + (client as unknown as { api: { post: typeof post } }).api = { post }; + + await client.runTaskInCloud("task-123", "feature/no-adapter", { + initialPermissionMode: "plan", + }); + + expect(post).toHaveBeenCalledWith( + "/api/projects/{project_id}/tasks/{id}/run/", + expect.objectContaining({ + body: expect.not.objectContaining({ + initial_permission_mode: expect.anything(), + }), + }), + ); + }); + it("rejects unsupported reasoning effort for cloud Codex runs", async () => { const client = new PostHogAPIClient( "http://localhost:8000", diff --git a/packages/api-client/src/posthog-client.ts b/packages/api-client/src/posthog-client.ts index 65ec798398..795c3f1527 100644 --- a/packages/api-client/src/posthog-client.ts +++ b/packages/api-client/src/posthog-client.ts @@ -549,6 +549,14 @@ function buildCloudRunRequestBody( } body.reasoning_effort = options.reasoningLevel; } + // The API rejects initial_permission_mode unless runtime_adapter is set, + // and the mode enum is validated per adapter. + if (options.initialPermissionMode) { + body.initial_permission_mode = resolveCloudInitialPermissionMode( + options.adapter, + options.initialPermissionMode, + ); + } } if (options?.resumeFromRunId) { body.resume_from_run_id = options.resumeFromRunId; @@ -577,12 +585,6 @@ function buildCloudRunRequestBody( if (options?.signalReportId) { body.signal_report_id = options.signalReportId; } - if (options?.initialPermissionMode) { - body.initial_permission_mode = resolveCloudInitialPermissionMode( - options.adapter, - options.initialPermissionMode, - ); - } if (options?.homeQuickAction) { body.home_quick_action = options.homeQuickAction; } From e392aa4d27ba3d69ed1652091996ccf411a00681 Mon Sep 17 00:00:00 2001 From: Charles Vien Date: Thu, 9 Jul 2026 14:41:06 -0700 Subject: [PATCH 3/4] move cloud permission mode mapping to shared --- packages/agent/src/execution-mode.test.ts | 30 +--------- packages/agent/src/execution-mode.ts | 36 +----------- packages/api-client/src/posthog-client.ts | 11 ++-- packages/shared/src/execution-modes.test.ts | 26 +++++++++ packages/shared/src/execution-modes.ts | 63 +++++++++++++++++++++ packages/shared/src/index.ts | 1 + 6 files changed, 97 insertions(+), 70 deletions(-) create mode 100644 packages/shared/src/execution-modes.test.ts diff --git a/packages/agent/src/execution-mode.test.ts b/packages/agent/src/execution-mode.test.ts index 4e69a9eee6..669715968e 100644 --- a/packages/agent/src/execution-mode.test.ts +++ b/packages/agent/src/execution-mode.test.ts @@ -1,9 +1,5 @@ import { describe, expect, it } from "vitest"; -import { - getAvailableCodexModes, - getAvailableModes, - resolveCloudInitialPermissionMode, -} from "./execution-mode"; +import { getAvailableCodexModes, getAvailableModes } from "./execution-mode"; describe("execution modes", () => { it("includes auto-accept permissions for claude sessions", () => { @@ -25,27 +21,3 @@ describe("execution modes", () => { ]); }); }); - -describe("resolveCloudInitialPermissionMode", () => { - it.each([ - ["codex", "auto", "auto"], - ["codex", "read-only", "read-only"], - ["codex", "full-access", "full-access"], - ["codex", "plan", "read-only"], - ["codex", "default", "auto"], - ["codex", "acceptEdits", "auto"], - ["codex", "bypassPermissions", "full-access"], - ["claude", "default", "default"], - ["claude", "acceptEdits", "acceptEdits"], - ["claude", "plan", "plan"], - ["claude", "bypassPermissions", "bypassPermissions"], - ["claude", "auto", "auto"], - ["claude", "read-only", "plan"], - ["claude", "full-access", "bypassPermissions"], - ] as const)( - "resolves %s adapter mode %s to %s", - (adapter, mode, expected) => { - expect(resolveCloudInitialPermissionMode(adapter, mode)).toBe(expected); - }, - ); -}); diff --git a/packages/agent/src/execution-mode.ts b/packages/agent/src/execution-mode.ts index 3e406827e8..7ebf75e86b 100644 --- a/packages/agent/src/execution-mode.ts +++ b/packages/agent/src/execution-mode.ts @@ -1,4 +1,4 @@ -import { type Adapter, CODEX_MODE_PRESETS } from "@posthog/shared"; +import { CODEX_MODE_PRESETS, type ExecutionMode } from "@posthog/shared"; import { ALLOW_BYPASS } from "./utils/common"; export interface ModeInfo { @@ -68,7 +68,7 @@ export const CODEX_NATIVE_MODES = ["auto", "read-only", "full-access"] as const; export type CodexNativeMode = (typeof CODEX_NATIVE_MODES)[number]; /** Union of all permission mode IDs across adapters */ -export type PermissionMode = CodeExecutionMode | CodexNativeMode; +export type PermissionMode = ExecutionMode; export function isCodexNativeMode(mode: string): mode is CodexNativeMode { return (CODEX_NATIVE_MODES as readonly string[]).includes(mode); @@ -81,35 +81,3 @@ export function getAvailableCodexModes(): ModeInfo[] { ? [...CODEX_MODE_PRESETS] : CODEX_MODE_PRESETS.filter((m) => m.id !== "full-access"); } - -// The cloud task-run API validates initial_permission_mode against a per-adapter -// enum that is narrower than the local pickers: codex accepts only its native -// modes (notably not "plan", which the local app-server adapter synthesizes). -// Degrade to the nearest mode with the same permission ceiling. -const CODEX_CLOUD_MODE_FALLBACKS: Record< - Exclude, - CodexNativeMode -> = { - default: "auto", - acceptEdits: "auto", - plan: "read-only", - bypassPermissions: "full-access", -}; - -const CLAUDE_CLOUD_MODE_FALLBACKS: Record< - Exclude, - CodeExecutionMode -> = { - "read-only": "plan", - "full-access": "bypassPermissions", -}; - -export function resolveCloudInitialPermissionMode( - adapter: Adapter, - mode: PermissionMode, -): PermissionMode { - if (adapter === "codex") { - return isCodexNativeMode(mode) ? mode : CODEX_CLOUD_MODE_FALLBACKS[mode]; - } - return isCodeExecutionMode(mode) ? mode : CLAUDE_CLOUD_MODE_FALLBACKS[mode]; -} diff --git a/packages/api-client/src/posthog-client.ts b/packages/api-client/src/posthog-client.ts index 795c3f1527..a929819858 100644 --- a/packages/api-client/src/posthog-client.ts +++ b/packages/api-client/src/posthog-client.ts @@ -1,12 +1,9 @@ import "./generated.augment"; import { isSupportedReasoningEffort } from "@posthog/agent/adapters/reasoning-effort"; -import { - type PermissionMode, - resolveCloudInitialPermissionMode, -} from "@posthog/agent/execution-mode"; import type { Adapter, CloudRunSource, + ExecutionMode, PrAuthorshipMode, SeatData, StoredLogEntry, @@ -15,6 +12,7 @@ import type { import { DISMISSAL_REASON_OPTIONS, type DismissalReasonOptionValue, + resolveCloudInitialPermissionMode, SEAT_PRODUCT_KEY, } from "@posthog/shared"; import type { @@ -494,7 +492,7 @@ interface CloudRunOptions { autoPublish?: boolean; runSource?: CloudRunSource; signalReportId?: string; - initialPermissionMode?: PermissionMode; + initialPermissionMode?: ExecutionMode; homeQuickAction?: string; } @@ -549,8 +547,7 @@ function buildCloudRunRequestBody( } body.reasoning_effort = options.reasoningLevel; } - // The API rejects initial_permission_mode unless runtime_adapter is set, - // and the mode enum is validated per adapter. + // The API rejects initial_permission_mode without runtime_adapter and validates it per adapter. if (options.initialPermissionMode) { body.initial_permission_mode = resolveCloudInitialPermissionMode( options.adapter, diff --git a/packages/shared/src/execution-modes.test.ts b/packages/shared/src/execution-modes.test.ts new file mode 100644 index 0000000000..6a765b56ad --- /dev/null +++ b/packages/shared/src/execution-modes.test.ts @@ -0,0 +1,26 @@ +import { describe, expect, it } from "vitest"; +import { resolveCloudInitialPermissionMode } from "./execution-modes"; + +describe("resolveCloudInitialPermissionMode", () => { + it.each([ + ["codex", "auto", "auto"], + ["codex", "read-only", "read-only"], + ["codex", "full-access", "full-access"], + ["codex", "plan", "read-only"], + ["codex", "default", "auto"], + ["codex", "acceptEdits", "auto"], + ["codex", "bypassPermissions", "full-access"], + ["claude", "default", "default"], + ["claude", "acceptEdits", "acceptEdits"], + ["claude", "plan", "plan"], + ["claude", "bypassPermissions", "bypassPermissions"], + ["claude", "auto", "auto"], + ["claude", "read-only", "plan"], + ["claude", "full-access", "bypassPermissions"], + ] as const)( + "resolves %s adapter mode %s to %s", + (adapter, mode, expected) => { + expect(resolveCloudInitialPermissionMode(adapter, mode)).toBe(expected); + }, + ); +}); diff --git a/packages/shared/src/execution-modes.ts b/packages/shared/src/execution-modes.ts index 909fcf80ee..e7d7fde243 100644 --- a/packages/shared/src/execution-modes.ts +++ b/packages/shared/src/execution-modes.ts @@ -1,3 +1,6 @@ +import type { Adapter } from "./adapter"; +import type { ExecutionMode } from "./exec-types"; + export interface CodexModePreset { id: "plan" | "read-only" | "auto" | "full-access"; name: string; @@ -32,3 +35,63 @@ export const CODEX_MODE_PRESETS: readonly CodexModePreset[] = [ description: "Auto-approves all operations", }, ]; + +const CLAUDE_CLOUD_PERMISSION_MODES = [ + "default", + "acceptEdits", + "plan", + "bypassPermissions", + "auto", +] as const; + +const CODEX_CLOUD_PERMISSION_MODES = [ + "auto", + "read-only", + "full-access", +] as const; + +type ClaudeCloudPermissionMode = (typeof CLAUDE_CLOUD_PERMISSION_MODES)[number]; +type CodexCloudPermissionMode = (typeof CODEX_CLOUD_PERMISSION_MODES)[number]; + +function isClaudeCloudPermissionMode( + mode: ExecutionMode, +): mode is ClaudeCloudPermissionMode { + return (CLAUDE_CLOUD_PERMISSION_MODES as readonly string[]).includes(mode); +} + +function isCodexCloudPermissionMode( + mode: ExecutionMode, +): mode is CodexCloudPermissionMode { + return (CODEX_CLOUD_PERMISSION_MODES as readonly string[]).includes(mode); +} + +// The cloud API's per-adapter mode enums are narrower than the local presets (no "plan" for codex); degrade to the nearest permission ceiling. +const CODEX_CLOUD_MODE_FALLBACKS: Record< + Exclude, + CodexCloudPermissionMode +> = { + default: "auto", + acceptEdits: "auto", + plan: "read-only", + bypassPermissions: "full-access", +}; + +const CLAUDE_CLOUD_MODE_FALLBACKS: Record< + Exclude, + ClaudeCloudPermissionMode +> = { + "read-only": "plan", + "full-access": "bypassPermissions", +}; + +export function resolveCloudInitialPermissionMode( + adapter: Adapter, + mode: ExecutionMode, +): ExecutionMode { + if (adapter === "codex") { + if (isCodexCloudPermissionMode(mode)) return mode; + return CODEX_CLOUD_MODE_FALLBACKS[mode] ?? "auto"; + } + if (isClaudeCloudPermissionMode(mode)) return mode; + return CLAUDE_CLOUD_MODE_FALLBACKS[mode] ?? "default"; +} diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts index 6089a52c29..2fb5a06129 100644 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts @@ -85,6 +85,7 @@ export type { ExecutionMode } from "./exec-types"; export { CODEX_MODE_PRESETS, type CodexModePreset, + resolveCloudInitialPermissionMode, } from "./execution-modes"; export * from "./flags"; export * from "./git-domain"; From 49a4da1447f4014a2db10f90d254ce82342b4965 Mon Sep 17 00:00:00 2001 From: Charles Vien Date: Thu, 9 Jul 2026 14:41:18 -0700 Subject: [PATCH 4/4] cover createTaskRun permission mode paths --- .../api-client/src/posthog-client.test.ts | 96 +++++++++++++++++++ 1 file changed, 96 insertions(+) diff --git a/packages/api-client/src/posthog-client.test.ts b/packages/api-client/src/posthog-client.test.ts index a82c0ed3aa..2e9a96b991 100644 --- a/packages/api-client/src/posthog-client.test.ts +++ b/packages/api-client/src/posthog-client.test.ts @@ -245,6 +245,102 @@ describe("PostHogAPIClient", () => { ); }); + it("maps the permission mode per adapter when creating task runs", async () => { + const fetch = vi.fn().mockResolvedValue({ + ok: true, + json: async () => ({ id: "run-123", environment: "cloud" }), + }); + const client = new PostHogAPIClient( + "http://localhost:8000", + async () => "token", + async () => "token", + 123, + ); + + ( + client as unknown as { + api: { baseUrl: string; fetcher: { fetch: typeof fetch } }; + } + ).api = { + baseUrl: "http://localhost:8000", + fetcher: { fetch }, + }; + + await client.createTaskRun("task-123", { + environment: "cloud", + adapter: "claude", + model: "claude-opus-4-8", + initialPermissionMode: "read-only", + }); + + const body = JSON.parse(fetch.mock.calls[0][0].overrides.body as string); + expect(body.initial_permission_mode).toBe("plan"); + }); + + it("omits the permission mode from created task runs without an adapter", async () => { + const fetch = vi.fn().mockResolvedValue({ + ok: true, + json: async () => ({ id: "run-123", environment: "cloud" }), + }); + const client = new PostHogAPIClient( + "http://localhost:8000", + async () => "token", + async () => "token", + 123, + ); + + ( + client as unknown as { + api: { baseUrl: string; fetcher: { fetch: typeof fetch } }; + } + ).api = { + baseUrl: "http://localhost:8000", + fetcher: { fetch }, + }; + + await client.createTaskRun("task-123", { + environment: "cloud", + initialPermissionMode: "plan", + }); + + const body = JSON.parse(fetch.mock.calls[0][0].overrides.body as string); + expect(body).not.toHaveProperty("initial_permission_mode"); + }); + + it("omits the permission mode when none is selected", async () => { + const client = new PostHogAPIClient( + "http://localhost:8000", + async () => "token", + async () => "token", + 123, + ); + + const post = vi.fn().mockResolvedValue({ + id: "task-123", + title: "Task", + description: "Task", + created_at: "2026-04-14T00:00:00Z", + updated_at: "2026-04-14T00:00:00Z", + origin_product: "user_created", + }); + + (client as unknown as { api: { post: typeof post } }).api = { post }; + + await client.runTaskInCloud("task-123", "feature/no-mode", { + adapter: "codex", + model: "gpt-5.4", + }); + + expect(post).toHaveBeenCalledWith( + "/api/projects/{project_id}/tasks/{id}/run/", + expect.objectContaining({ + body: expect.not.objectContaining({ + initial_permission_mode: expect.anything(), + }), + }), + ); + }); + it("starts an existing cloud task run with run-scoped artifact ids", async () => { const fetch = vi.fn().mockResolvedValue({ ok: true,