Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion apps/mobile/src/features/threads/NewTaskDraftScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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:", "");
Expand Down
12 changes: 11 additions & 1 deletion apps/mobile/src/features/threads/ThreadComposer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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:", "");
Expand Down Expand Up @@ -806,6 +810,12 @@ export const ThreadComposer = memo(function ThreadComposer(props: ThreadComposer
automatically.
</Text>
) : null}
{/* ponytail: no reliable client-side model capability registry exists for auto-mode support */}
{props.selectedThread.session?.lastError ? (
<View style={{ paddingTop: 10 }}>
<ErrorBanner message={props.selectedThread.session.lastError} />
</View>
) : null}
</View>

<ImageViewing
Expand Down
20 changes: 20 additions & 0 deletions apps/server/src/provider/Layers/ClaudeAdapter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,25 @@ describe("ClaudeAdapterLive", () => {
);
});

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* () {
Expand Down Expand Up @@ -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 }) => {
Expand Down
1 change: 1 addition & 0 deletions apps/server/src/provider/Layers/ClaudeAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2980,6 +2980,7 @@ export const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (
const effectiveEffort = getEffectiveClaudeAgentEffort(effort, modelSelection?.model);
const runtimeModeToPermission: Record<string, PermissionMode> = {
"auto-accept-edits": "acceptEdits",
auto: "auto",
"full-access": "bypassPermissions",
};
const permissionMode = runtimeModeToPermission[input.runtimeMode];
Expand Down
61 changes: 61 additions & 0 deletions apps/server/src/provider/Layers/CodexSessionRuntime.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand Down Expand Up @@ -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",
});
});
});
2 changes: 2 additions & 0 deletions apps/server/src/provider/Layers/CodexSessionRuntime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,7 @@ function runtimeModeToThreadConfig(input: RuntimeMode): {
approvalPolicy: "untrusted",
sandbox: "read-only",
};
case "auto":
case "auto-accept-edits":
return {
approvalPolicy: "on-request",
Expand Down Expand Up @@ -376,6 +377,7 @@ function runtimeModeToTurnSandboxPolicy(
return {
type: "readOnly",
};
case "auto":
case "auto-accept-edits":
return {
type: "workspaceWrite",
Expand Down
6 changes: 6 additions & 0 deletions apps/web/src/components/chat/ChatComposer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ import {
LockIcon,
LockOpenIcon,
PenLineIcon,
SparklesIcon,
XIcon,
} from "lucide-react";
import { proposedPlanTitle } from "../../proposedPlan";
Expand Down Expand Up @@ -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.",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ export const CompactComposerControlsMenu = memo(function CompactComposerControls
>
<MenuRadioItem value="approval-required">Supervised</MenuRadioItem>
<MenuRadioItem value="auto-accept-edits">Auto-accept edits</MenuRadioItem>
<MenuRadioItem value="auto">Auto</MenuRadioItem>
<MenuRadioItem value="full-access">Full access</MenuRadioItem>
</MenuRadioGroup>
{props.showPlanToggle ? (
Expand Down
1 change: 1 addition & 0 deletions packages/contracts/src/orchestration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Loading