fix(modes): presets own permissionProfile; build=unrestricted; default=normal

- Each preset now declares its own permissionProfile:
    ask  → normal   (conversational, can read/run safe commands)
    plan → normal   (structuring, not executing)
    build → unrestricted  (go do it, no permission prompts)

- setMode() calls for Shift+Tab and /mode now include permissionProfile
  so switching preset atomically sets all four axes.

- inferPresetName() includes permissionProfile in the match so status
  display shows 'build mode' only when permissions are also unrestricted.

- AutoSession default permissionProfile: 'restricted' → 'normal'
  (restricted was too conservative even for ask/chat use).

Flow: Ask (discuss) → Plan (structure) → Build (autonomous+unrestricted)
YOLO (Ctrl+Y) = build + autonomous + deep + unrestricted (turbo on top).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Mikael Hugo 2026-05-09 19:46:57 +02:00
parent 8432b626c2
commit fc60de80f5
4 changed files with 12 additions and 5 deletions

View file

@ -159,9 +159,10 @@ export class AutoSession {
runControl = "manual";
/**
* Current permission profile: restricted | normal | trusted | unrestricted.
* Defaults to "restricted" for safety.
* Defaults to "normal" ask mode should be able to read files and run
* safe commands without permission prompts. Use /mode build for unrestricted.
*/
permissionProfile = "restricted";
permissionProfile = "normal";
/**
* Current model mode: fast | smart | deep.
* Defaults to "smart".
@ -355,7 +356,7 @@ export class AutoSession {
// Mode state
this.workMode = "chat";
this.runControl = "manual";
this.permissionProfile = "restricted";
this.permissionProfile = "normal";
this.modelMode = "smart";
this.surface = "tui";
this.modeUpdatedAt = null;

View file

@ -465,6 +465,7 @@ function handleModeCommand(args, ctx) {
workMode: preset.workMode,
runControl: preset.runControl,
modelMode: preset.modelMode,
permissionProfile: preset.permissionProfile,
});
ctx.ui.notify(`Mode: ${prev}${name} (${preset.description})`, "info");
return true;

View file

@ -106,6 +106,7 @@ export const SF_MODE_PRESETS = Object.freeze({
workMode: "chat",
runControl: "manual",
modelMode: "fast",
permissionProfile: "normal",
},
plan: {
label: "Plan",
@ -113,13 +114,15 @@ export const SF_MODE_PRESETS = Object.freeze({
workMode: "plan",
runControl: "assisted",
modelMode: "smart",
permissionProfile: "normal",
},
build: {
label: "Build",
description: "Execute autonomously — SF biases toward action",
description: "Execute autonomously — SF biases toward action, no permission prompts",
workMode: "build",
runControl: "autonomous",
modelMode: "smart",
permissionProfile: "unrestricted",
},
});
@ -153,7 +156,8 @@ export function inferPresetName(mode) {
if (
mode.workMode === preset.workMode &&
mode.runControl === preset.runControl &&
mode.modelMode === preset.modelMode
mode.modelMode === preset.modelMode &&
mode.permissionProfile === preset.permissionProfile
) {
return name;
}

View file

@ -64,6 +64,7 @@ export default function steerableAutonomousExtension(api) {
workMode: preset.workMode,
runControl: preset.runControl,
modelMode: preset.modelMode,
permissionProfile: preset.permissionProfile,
});
ctx.ui.notify(`Mode → ${nextName} (${preset.description})`, "info");
} catch {