diff --git a/apps/mobile/src/features/threads/NewTaskDraftScreen.tsx b/apps/mobile/src/features/threads/NewTaskDraftScreen.tsx
index b73e874169c..975309f4d2a 100644
--- a/apps/mobile/src/features/threads/NewTaskDraftScreen.tsx
+++ b/apps/mobile/src/features/threads/NewTaskDraftScreen.tsx
@@ -169,10 +169,13 @@ export function NewTaskDraftScreen(props: {
? "Approve actions"
: flow.runtimeMode === "auto-accept-edits"
? "Auto-accept edits"
- : "Full access",
+ : flow.runtimeMode === "auto"
+ ? "Auto"
+ : "Full access",
subactions: [
{ id: "options:runtime:approval-required", title: "Approve actions" },
{ id: "options:runtime:auto-accept-edits", title: "Auto-accept edits" },
+ { id: "options:runtime:auto", title: "Auto" },
{ id: "options:runtime:full-access", title: "Full access" },
].map((option) => {
const value = option.id.replace("options:runtime:", "");
diff --git a/apps/mobile/src/features/threads/ThreadComposer.tsx b/apps/mobile/src/features/threads/ThreadComposer.tsx
index e13115d205f..f91c32cb5b7 100644
--- a/apps/mobile/src/features/threads/ThreadComposer.tsx
+++ b/apps/mobile/src/features/threads/ThreadComposer.tsx
@@ -30,6 +30,7 @@ import { useThemeColor } from "../../lib/useThemeColor";
import { AppText as Text } from "../../components/AppText";
import { ComposerAttachmentStrip } from "../../components/ComposerAttachmentStrip";
+import { ErrorBanner } from "../../components/ErrorBanner";
import { ControlPill, ControlPillMenu } from "../../components/ControlPill";
import { ProviderIcon } from "../../components/ProviderIcon";
import type { DraftComposerImageAttachment } from "../../lib/composerImages";
@@ -499,10 +500,13 @@ export const ThreadComposer = memo(function ThreadComposer(props: ThreadComposer
? "Approve actions"
: currentRuntimeMode === "auto-accept-edits"
? "Auto-accept edits"
- : "Full access",
+ : currentRuntimeMode === "auto"
+ ? "Auto"
+ : "Full access",
subactions: [
{ id: "options:runtime:approval-required", title: "Approve actions" },
{ id: "options:runtime:auto-accept-edits", title: "Auto-accept edits" },
+ { id: "options:runtime:auto", title: "Auto" },
{ id: "options:runtime:full-access", title: "Full access" },
].map((option) => {
const value = option.id.replace("options:runtime:", "");
@@ -806,6 +810,12 @@ export const ThreadComposer = memo(function ThreadComposer(props: ThreadComposer
automatically.
) : null}
+ {/* ponytail: no reliable client-side model capability registry exists for auto-mode support */}
+ {props.selectedThread.session?.lastError ? (
+
+
+
+ ) : null}
{
);
});
+ it.effect("uses native auto permissions for auto claude sessions", () => {
+ const harness = makeHarness();
+ return Effect.gen(function* () {
+ const adapter = yield* ClaudeAdapter;
+ yield* adapter.startSession({
+ threadId: THREAD_ID,
+ provider: ProviderDriverKind.make("claudeAgent"),
+ runtimeMode: "auto",
+ });
+
+ const createInput = harness.getLastCreateQueryInput();
+ assert.equal(createInput?.options.permissionMode, "auto");
+ assert.equal(createInput?.options.allowDangerouslySkipPermissions, undefined);
+ }).pipe(
+ Effect.provideService(Random.Random, makeDeterministicRandomService()),
+ Effect.provide(harness.layer),
+ );
+ });
+
it.effect("forwards claude effort levels into query options", () => {
const harness = makeHarness();
return Effect.gen(function* () {
@@ -3461,6 +3480,7 @@ describe("ClaudeAdapterLive", () => {
{ runtimeMode: "full-access", expectedBase: "bypassPermissions" },
{ runtimeMode: "approval-required", expectedBase: "default" },
{ runtimeMode: "auto-accept-edits", expectedBase: "acceptEdits" },
+ { runtimeMode: "auto", expectedBase: "auto" },
])(
"restores $expectedBase permission mode after plan turn ($runtimeMode)",
({ runtimeMode, expectedBase }) => {
diff --git a/apps/server/src/provider/Layers/ClaudeAdapter.ts b/apps/server/src/provider/Layers/ClaudeAdapter.ts
index 74bb11b833c..e620cde1833 100644
--- a/apps/server/src/provider/Layers/ClaudeAdapter.ts
+++ b/apps/server/src/provider/Layers/ClaudeAdapter.ts
@@ -2980,6 +2980,7 @@ export const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (
const effectiveEffort = getEffectiveClaudeAgentEffort(effort, modelSelection?.model);
const runtimeModeToPermission: Record = {
"auto-accept-edits": "acceptEdits",
+ auto: "auto",
"full-access": "bypassPermissions",
};
const permissionMode = runtimeModeToPermission[input.runtimeMode];
diff --git a/apps/server/src/provider/Layers/CodexSessionRuntime.test.ts b/apps/server/src/provider/Layers/CodexSessionRuntime.test.ts
index 93d0ecc8d2b..214c84204b5 100644
--- a/apps/server/src/provider/Layers/CodexSessionRuntime.test.ts
+++ b/apps/server/src/provider/Layers/CodexSessionRuntime.test.ts
@@ -176,6 +176,51 @@ describe("buildTurnStartParams", () => {
});
});
+ it("maps runtimeMode 'auto' to the same conservative policy as auto-accept-edits", () => {
+ const params = Effect.runSync(
+ buildTurnStartParams({
+ threadId: "provider-thread-1",
+ runtimeMode: "auto",
+ prompt: "Implement it",
+ model: "gpt-5.3-codex",
+ interactionMode: "default",
+ attachments: [
+ {
+ type: "image",
+ url: "data:image/png;base64,abc",
+ },
+ ],
+ }),
+ );
+
+ assert.deepStrictEqual(params, {
+ threadId: "provider-thread-1",
+ approvalPolicy: "on-request",
+ sandboxPolicy: {
+ type: "workspaceWrite",
+ },
+ input: [
+ {
+ type: "text",
+ text: "Implement it",
+ },
+ {
+ type: "image",
+ url: "data:image/png;base64,abc",
+ },
+ ],
+ model: "gpt-5.3-codex",
+ collaborationMode: {
+ mode: "default",
+ settings: {
+ model: "gpt-5.3-codex",
+ reasoning_effort: "medium",
+ developer_instructions: CODEX_DEFAULT_MODE_DEVELOPER_INSTRUCTIONS,
+ },
+ },
+ });
+ });
+
it("omits collaboration mode when interaction mode is absent", () => {
const params = Effect.runSync(
buildTurnStartParams({
@@ -642,4 +687,20 @@ describe("buildThreadStartParams visual-plan MCP", () => {
});
assert.equal(params.config, undefined);
});
+
+ it("maps runtimeMode 'auto' to the same conservative policy as auto-accept-edits", () => {
+ const params = buildThreadStartParams({
+ cwd: "/tmp/project",
+ runtimeMode: "auto",
+ model: undefined,
+ serviceTier: undefined,
+ visualPlanMcp: undefined,
+ });
+
+ assert.deepStrictEqual(params, {
+ cwd: "/tmp/project",
+ approvalPolicy: "on-request",
+ sandbox: "workspace-write",
+ });
+ });
});
diff --git a/apps/server/src/provider/Layers/CodexSessionRuntime.ts b/apps/server/src/provider/Layers/CodexSessionRuntime.ts
index d6b62f76074..64ca83a0120 100644
--- a/apps/server/src/provider/Layers/CodexSessionRuntime.ts
+++ b/apps/server/src/provider/Layers/CodexSessionRuntime.ts
@@ -320,6 +320,7 @@ function runtimeModeToThreadConfig(input: RuntimeMode): {
approvalPolicy: "untrusted",
sandbox: "read-only",
};
+ case "auto":
case "auto-accept-edits":
return {
approvalPolicy: "on-request",
@@ -376,6 +377,7 @@ function runtimeModeToTurnSandboxPolicy(
return {
type: "readOnly",
};
+ case "auto":
case "auto-accept-edits":
return {
type: "workspaceWrite",
diff --git a/apps/web/src/components/chat/ChatComposer.tsx b/apps/web/src/components/chat/ChatComposer.tsx
index 711272ffe84..04288dc739c 100644
--- a/apps/web/src/components/chat/ChatComposer.tsx
+++ b/apps/web/src/components/chat/ChatComposer.tsx
@@ -92,6 +92,7 @@ import {
LockIcon,
LockOpenIcon,
PenLineIcon,
+ SparklesIcon,
XIcon,
} from "lucide-react";
import { proposedPlanTitle } from "../../proposedPlan";
@@ -132,6 +133,11 @@ const runtimeModeConfig: Record<
description: "Auto-approve edits, ask before other actions.",
icon: PenLineIcon,
},
+ auto: {
+ label: "Auto",
+ description: "Auto-approves safe actions; asks before risky ones.",
+ icon: SparklesIcon,
+ },
"full-access": {
label: "Full access",
description: "Allow commands and edits without prompts.",
diff --git a/apps/web/src/components/chat/CompactComposerControlsMenu.tsx b/apps/web/src/components/chat/CompactComposerControlsMenu.tsx
index fb43335e3ae..ebae48c2740 100644
--- a/apps/web/src/components/chat/CompactComposerControlsMenu.tsx
+++ b/apps/web/src/components/chat/CompactComposerControlsMenu.tsx
@@ -74,6 +74,7 @@ export const CompactComposerControlsMenu = memo(function CompactComposerControls
>
Supervised
Auto-accept edits
+ Auto
Full access
{props.showPlanToggle ? (
diff --git a/packages/contracts/src/orchestration.ts b/packages/contracts/src/orchestration.ts
index e910173f842..f1152117e07 100644
--- a/packages/contracts/src/orchestration.ts
+++ b/packages/contracts/src/orchestration.ts
@@ -127,6 +127,7 @@ export type ModelSelection = typeof ModelSelection.Type;
export const RuntimeMode = Schema.Literals([
"approval-required",
"auto-accept-edits",
+ "auto",
"full-access",
]);
export type RuntimeMode = typeof RuntimeMode.Type;