diff --git a/src/browser/components/ChatInput/CreationControls.tsx b/src/browser/components/ChatInput/CreationControls.tsx
index 3ad806fe42..dd01b02022 100644
--- a/src/browser/components/ChatInput/CreationControls.tsx
+++ b/src/browser/components/ChatInput/CreationControls.tsx
@@ -1,15 +1,21 @@
import React from "react";
import { RUNTIME_MODE, type RuntimeMode } from "@/common/types/runtime";
-import { TooltipWrapper, Tooltip } from "../Tooltip";
import { Select } from "../Select";
+import { RuntimeIconSelector } from "../RuntimeIconSelector";
interface CreationControlsProps {
branches: string[];
trunkBranch: string;
onTrunkBranchChange: (branch: string) => void;
runtimeMode: RuntimeMode;
+ defaultRuntimeMode: RuntimeMode;
sshHost: string;
- onRuntimeChange: (mode: RuntimeMode, host: string) => void;
+ /** Called when user clicks a runtime icon to select it (does not persist) */
+ onRuntimeModeChange: (mode: RuntimeMode) => void;
+ /** Called when user checks "Default for project" checkbox (persists) */
+ onSetDefaultRuntime: (mode: RuntimeMode) => void;
+ /** Called when user changes SSH host */
+ onSshHostChange: (host: string) => void;
disabled: boolean;
}
@@ -25,40 +31,14 @@ export function CreationControls(props: CreationControlsProps) {
return (
- {/* Runtime Selector - first */}
-
-
-
+ {/* Runtime Selector - icon-based with tooltips */}
+
{/* Trunk Branch Selector - hidden for Local runtime */}
{showTrunkBranchSelector && (
@@ -86,7 +66,7 @@ export function CreationControls(props: CreationControlsProps) {
props.onRuntimeChange(RUNTIME_MODE.SSH, e.target.value)}
+ onChange={(e) => props.onSshHostChange(e.target.value)}
placeholder="user@host"
disabled={props.disabled}
className="bg-separator text-foreground border-border-medium focus:border-accent w-32 rounded border px-1 py-0.5 text-xs focus:outline-none disabled:opacity-50"
diff --git a/src/browser/components/ChatInput/index.tsx b/src/browser/components/ChatInput/index.tsx
index 7588eb53f9..994b83214c 100644
--- a/src/browser/components/ChatInput/index.tsx
+++ b/src/browser/components/ChatInput/index.tsx
@@ -1382,8 +1382,11 @@ export const ChatInput: React.FC
= (props) => {
trunkBranch={creationState.trunkBranch}
onTrunkBranchChange={creationState.setTrunkBranch}
runtimeMode={creationState.runtimeMode}
+ defaultRuntimeMode={creationState.defaultRuntimeMode}
sshHost={creationState.sshHost}
- onRuntimeChange={creationState.setRuntimeOptions}
+ onRuntimeModeChange={creationState.setRuntimeMode}
+ onSetDefaultRuntime={creationState.setDefaultRuntimeMode}
+ onSshHostChange={creationState.setSshHost}
disabled={creationState.isSending || isSending}
/>
)}
diff --git a/src/browser/components/ChatInput/useCreationWorkspace.test.tsx b/src/browser/components/ChatInput/useCreationWorkspace.test.tsx
index 53ce12c51d..172f45f5fc 100644
--- a/src/browser/components/ChatInput/useCreationWorkspace.test.tsx
+++ b/src/browser/components/ChatInput/useCreationWorkspace.test.tsx
@@ -473,41 +473,58 @@ function createDraftSettingsHarness(
sshHost: string;
trunkBranch: string;
runtimeString?: string | undefined;
+ defaultRuntimeMode?: RuntimeMode;
}>
) {
const state = {
runtimeMode: initial?.runtimeMode ?? ("local" as RuntimeMode),
+ defaultRuntimeMode: initial?.defaultRuntimeMode ?? ("worktree" as RuntimeMode),
sshHost: initial?.sshHost ?? "",
trunkBranch: initial?.trunkBranch ?? "main",
runtimeString: initial?.runtimeString,
} satisfies {
runtimeMode: RuntimeMode;
+ defaultRuntimeMode: RuntimeMode;
sshHost: string;
trunkBranch: string;
runtimeString: string | undefined;
};
- const setRuntimeOptions = mock((mode: RuntimeMode, host: string) => {
+ const setTrunkBranch = mock((branch: string) => {
+ state.trunkBranch = branch;
+ });
+
+ const getRuntimeString = mock(() => state.runtimeString);
+
+ const setRuntimeMode = mock((mode: RuntimeMode) => {
state.runtimeMode = mode;
- state.sshHost = host;
- const trimmedHost = host.trim();
+ const trimmedHost = state.sshHost.trim();
state.runtimeString = mode === "ssh" ? (trimmedHost ? `ssh ${trimmedHost}` : "ssh") : undefined;
});
- const setTrunkBranch = mock((branch: string) => {
- state.trunkBranch = branch;
+ const setDefaultRuntimeMode = mock((mode: RuntimeMode) => {
+ state.defaultRuntimeMode = mode;
+ state.runtimeMode = mode;
+ const trimmedHost = state.sshHost.trim();
+ state.runtimeString = mode === "ssh" ? (trimmedHost ? `ssh ${trimmedHost}` : "ssh") : undefined;
});
- const getRuntimeString = mock(() => state.runtimeString);
+ const setSshHost = mock((host: string) => {
+ state.sshHost = host;
+ });
return {
state,
- setRuntimeOptions,
+ setRuntimeMode,
+ setDefaultRuntimeMode,
+ setSshHost,
setTrunkBranch,
getRuntimeString,
snapshot(): {
settings: DraftWorkspaceSettings;
- setRuntimeOptions: typeof setRuntimeOptions;
+ setRuntimeMode: typeof setRuntimeMode;
+ setDefaultRuntimeMode: typeof setDefaultRuntimeMode;
+ setSshHost: typeof setSshHost;
setTrunkBranch: typeof setTrunkBranch;
getRuntimeString: typeof getRuntimeString;
} {
@@ -516,12 +533,15 @@ function createDraftSettingsHarness(
thinkingLevel: "medium",
mode: "exec",
runtimeMode: state.runtimeMode,
+ defaultRuntimeMode: state.defaultRuntimeMode,
sshHost: state.sshHost,
trunkBranch: state.trunkBranch,
};
return {
settings,
- setRuntimeOptions,
+ setRuntimeMode,
+ setDefaultRuntimeMode,
+ setSshHost,
setTrunkBranch,
getRuntimeString,
};
diff --git a/src/browser/components/ChatInput/useCreationWorkspace.ts b/src/browser/components/ChatInput/useCreationWorkspace.ts
index 98eea859bb..fcbf4a96b7 100644
--- a/src/browser/components/ChatInput/useCreationWorkspace.ts
+++ b/src/browser/components/ChatInput/useCreationWorkspace.ts
@@ -46,8 +46,14 @@ interface UseCreationWorkspaceReturn {
trunkBranch: string;
setTrunkBranch: (branch: string) => void;
runtimeMode: RuntimeMode;
+ defaultRuntimeMode: RuntimeMode;
sshHost: string;
- setRuntimeOptions: (mode: RuntimeMode, host: string) => void;
+ /** Set the currently selected runtime mode (does not persist) */
+ setRuntimeMode: (mode: RuntimeMode) => void;
+ /** Set the default runtime mode for this project (persists via checkbox) */
+ setDefaultRuntimeMode: (mode: RuntimeMode) => void;
+ /** Set the SSH host (persisted separately from runtime mode) */
+ setSshHost: (host: string) => void;
toast: Toast | null;
setToast: (toast: Toast | null) => void;
isSending: boolean;
@@ -72,8 +78,14 @@ export function useCreationWorkspace({
const [isSending, setIsSending] = useState(false);
// Centralized draft workspace settings with automatic persistence
- const { settings, setRuntimeOptions, setTrunkBranch, getRuntimeString } =
- useDraftWorkspaceSettings(projectPath, branches, recommendedTrunk);
+ const {
+ settings,
+ setRuntimeMode,
+ setDefaultRuntimeMode,
+ setSshHost,
+ setTrunkBranch,
+ getRuntimeString,
+ } = useDraftWorkspaceSettings(projectPath, branches, recommendedTrunk);
// Get send options from shared hook (uses project-scoped storage key)
const sendMessageOptions = useSendMessageOptions(getProjectScopeId(projectPath));
@@ -180,8 +192,11 @@ export function useCreationWorkspace({
trunkBranch: settings.trunkBranch,
setTrunkBranch,
runtimeMode: settings.runtimeMode,
+ defaultRuntimeMode: settings.defaultRuntimeMode,
sshHost: settings.sshHost,
- setRuntimeOptions,
+ setRuntimeMode,
+ setDefaultRuntimeMode,
+ setSshHost,
toast,
setToast,
isSending,
diff --git a/src/browser/components/RuntimeBadge.tsx b/src/browser/components/RuntimeBadge.tsx
index 5d895eb76e..e70bdf3bde 100644
--- a/src/browser/components/RuntimeBadge.tsx
+++ b/src/browser/components/RuntimeBadge.tsx
@@ -4,6 +4,7 @@ import type { RuntimeConfig } from "@/common/types/runtime";
import { isSSHRuntime, isWorktreeRuntime, isLocalProjectRuntime } from "@/common/types/runtime";
import { extractSshHostname } from "@/browser/utils/ui/runtimeBadge";
import { TooltipWrapper, Tooltip } from "./Tooltip";
+import { SSHIcon, WorktreeIcon, LocalIcon } from "./icons/RuntimeIcons";
interface RuntimeBadgeProps {
runtimeConfig?: RuntimeConfig;
@@ -12,72 +13,6 @@ interface RuntimeBadgeProps {
isWorking?: boolean;
}
-/** Server rack icon for SSH runtime */
-function SSHIcon() {
- return (
-
- );
-}
-
-/** Git branch icon for worktree runtime */
-function WorktreeIcon() {
- return (
-
- );
-}
-
-/** Folder icon for local project-dir runtime */
-function LocalIcon() {
- return (
-
- );
-}
-
// Runtime-specific color schemes - each type has consistent colors in idle/working states
// Idle: subtle with visible colored border for discrimination
// Working: brighter colors with pulse animation
diff --git a/src/browser/components/RuntimeIconSelector.tsx b/src/browser/components/RuntimeIconSelector.tsx
new file mode 100644
index 0000000000..e95da78d86
--- /dev/null
+++ b/src/browser/components/RuntimeIconSelector.tsx
@@ -0,0 +1,133 @@
+import React from "react";
+import { cn } from "@/common/lib/utils";
+import { RUNTIME_MODE, type RuntimeMode } from "@/common/types/runtime";
+import { SSHIcon, WorktreeIcon, LocalIcon } from "./icons/RuntimeIcons";
+import { TooltipWrapper, Tooltip } from "./Tooltip";
+
+interface RuntimeIconSelectorProps {
+ value: RuntimeMode;
+ onChange: (mode: RuntimeMode) => void;
+ /** The persisted default runtime for this project */
+ defaultMode: RuntimeMode;
+ /** Called when user checks "Default for project" in tooltip */
+ onSetDefault: (mode: RuntimeMode) => void;
+ disabled?: boolean;
+ className?: string;
+}
+
+// Runtime-specific color schemes matching RuntimeBadge
+// Selected (active) uses the "working" styling, unselected uses "idle"
+const RUNTIME_STYLES = {
+ ssh: {
+ idle: "bg-transparent text-muted border-blue-500/30 hover:border-blue-500/50",
+ active: "bg-blue-500/20 text-blue-400 border-blue-500/60",
+ },
+ worktree: {
+ idle: "bg-transparent text-muted border-purple-500/30 hover:border-purple-500/50",
+ active: "bg-purple-500/20 text-purple-400 border-purple-500/60",
+ },
+ local: {
+ idle: "bg-transparent text-muted border-muted/30 hover:border-muted/50",
+ active: "bg-muted/30 text-foreground border-muted/60",
+ },
+} as const;
+
+const RUNTIME_INFO: Record = {
+ local: {
+ label: "Local",
+ description: "Work directly in project directory (no isolation)",
+ },
+ worktree: {
+ label: "Worktree",
+ description: "Git worktree in ~/.mux/src (isolated)",
+ },
+ ssh: {
+ label: "SSH",
+ description: "Remote clone on SSH host",
+ },
+};
+
+interface RuntimeIconButtonProps {
+ mode: RuntimeMode;
+ isSelected: boolean;
+ isDefault: boolean;
+ onClick: () => void;
+ onSetDefault: () => void;
+ disabled?: boolean;
+}
+
+function RuntimeIconButton(props: RuntimeIconButtonProps) {
+ const info = RUNTIME_INFO[props.mode];
+ const styles = RUNTIME_STYLES[props.mode];
+ const stateStyle = props.isSelected ? styles.active : styles.idle;
+
+ const Icon =
+ props.mode === RUNTIME_MODE.SSH
+ ? SSHIcon
+ : props.mode === RUNTIME_MODE.WORKTREE
+ ? WorktreeIcon
+ : LocalIcon;
+
+ return (
+
+
+
+ {info.label}
+ {info.description}
+
+
+
+ );
+}
+
+/**
+ * Runtime selector using icons with tooltips.
+ * Shows Local, Worktree, and SSH options as clickable icons.
+ * Selected runtime uses "active" styling (brighter colors).
+ * Each tooltip has a "Default for project" checkbox to persist the preference.
+ */
+export function RuntimeIconSelector(props: RuntimeIconSelectorProps) {
+ const modes: RuntimeMode[] = [RUNTIME_MODE.LOCAL, RUNTIME_MODE.WORKTREE, RUNTIME_MODE.SSH];
+
+ return (
+
+ {modes.map((mode) => (
+ props.onChange(mode)}
+ onSetDefault={() => props.onSetDefault(mode)}
+ disabled={props.disabled}
+ />
+ ))}
+
+ );
+}
diff --git a/src/browser/components/icons/RuntimeIcons.tsx b/src/browser/components/icons/RuntimeIcons.tsx
new file mode 100644
index 0000000000..51ae7b7c72
--- /dev/null
+++ b/src/browser/components/icons/RuntimeIcons.tsx
@@ -0,0 +1,75 @@
+import React from "react";
+
+interface IconProps {
+ size?: number;
+ className?: string;
+}
+
+/** Server rack icon for SSH runtime */
+export function SSHIcon({ size = 10, className }: IconProps) {
+ return (
+
+ );
+}
+
+/** Git branch icon for worktree runtime */
+export function WorktreeIcon({ size = 10, className }: IconProps) {
+ return (
+
+ );
+}
+
+/** Folder icon for local project-dir runtime */
+export function LocalIcon({ size = 10, className }: IconProps) {
+ return (
+
+ );
+}
diff --git a/src/browser/hooks/useDraftWorkspaceSettings.ts b/src/browser/hooks/useDraftWorkspaceSettings.ts
index fffb6304fc..b1aab7dd75 100644
--- a/src/browser/hooks/useDraftWorkspaceSettings.ts
+++ b/src/browser/hooks/useDraftWorkspaceSettings.ts
@@ -1,4 +1,4 @@
-import { useEffect } from "react";
+import { useState, useEffect } from "react";
import { usePersistedState } from "./usePersistedState";
import { useThinkingLevel } from "./useThinkingLevel";
import { useMode } from "@/browser/contexts/ModeContext";
@@ -29,7 +29,10 @@ export interface DraftWorkspaceSettings {
mode: UIMode;
// Workspace creation settings (project-specific)
+ /** Currently selected runtime for this workspace creation (may differ from default) */
runtimeMode: RuntimeMode;
+ /** Persisted default runtime for this project (used to initialize selection) */
+ defaultRuntimeMode: RuntimeMode;
sshHost: string;
trunkBranch: string;
}
@@ -49,7 +52,11 @@ export function useDraftWorkspaceSettings(
recommendedTrunk: string | null
): {
settings: DraftWorkspaceSettings;
- setRuntimeOptions: (mode: RuntimeMode, host: string) => void;
+ /** Set the currently selected runtime mode (does not persist) */
+ setRuntimeMode: (mode: RuntimeMode) => void;
+ /** Set the default runtime mode for this project (persists via checkbox) */
+ setDefaultRuntimeMode: (mode: RuntimeMode) => void;
+ setSshHost: (host: string) => void;
setTrunkBranch: (branch: string) => void;
getRuntimeString: () => string | undefined;
} {
@@ -65,13 +72,25 @@ export function useDraftWorkspaceSettings(
{ listener: true }
);
- // Project-scoped runtime preference (persisted per project)
- const [runtimeString, setRuntimeString] = usePersistedState(
+ // Project-scoped default runtime (worktree by default, only changed via checkbox)
+ const [defaultRuntimeString, setDefaultRuntimeString] = usePersistedState(
getRuntimeKey(projectPath),
- undefined,
+ undefined, // undefined means worktree (the app default)
{ listener: true }
);
+ // Parse default runtime string into mode (worktree when undefined)
+ const { mode: defaultRuntimeMode } = parseRuntimeModeAndHost(defaultRuntimeString);
+
+ // Currently selected runtime mode for this session (initialized from default)
+ // This allows user to select a different runtime without changing the default
+ const [selectedRuntimeMode, setSelectedRuntimeMode] = useState(defaultRuntimeMode);
+
+ // Sync selected mode when default changes (e.g., from checkbox or project switch)
+ useEffect(() => {
+ setSelectedRuntimeMode(defaultRuntimeMode);
+ }, [defaultRuntimeMode]);
+
// Project-scoped trunk branch preference (persisted per project)
const [trunkBranch, setTrunkBranch] = usePersistedState(
getTrunkBranchKey(projectPath),
@@ -87,9 +106,6 @@ export function useDraftWorkspaceSettings(
{ listener: true }
);
- // Parse runtime string into mode (host from runtime string is ignored in favor of lastSshHost)
- const { mode: runtimeMode } = parseRuntimeModeAndHost(runtimeString);
-
// Initialize trunk branch from backend recommendation or first branch
useEffect(() => {
if (!trunkBranch && branches.length > 0) {
@@ -98,19 +114,27 @@ export function useDraftWorkspaceSettings(
}
}, [branches, recommendedTrunk, trunkBranch, setTrunkBranch]);
- // Setter for runtime options (updates persisted runtime mode and SSH host separately)
- const setRuntimeOptions = (newMode: RuntimeMode, newHost: string) => {
- const newRuntimeString = buildRuntimeString(newMode, newHost);
- setRuntimeString(newRuntimeString);
- // Always persist the SSH host separately so it's remembered across mode switches
- if (newHost) {
- setLastSshHost(newHost);
- }
+ // Setter for selected runtime mode (changes current selection, does not persist)
+ const setRuntimeMode = (newMode: RuntimeMode) => {
+ setSelectedRuntimeMode(newMode);
+ };
+
+ // Setter for default runtime mode (persists via checkbox in tooltip)
+ const setDefaultRuntimeMode = (newMode: RuntimeMode) => {
+ const newRuntimeString = buildRuntimeString(newMode, lastSshHost);
+ setDefaultRuntimeString(newRuntimeString);
+ // Also update selection to match new default
+ setSelectedRuntimeMode(newMode);
+ };
+
+ // Setter for SSH host (persisted separately so it's remembered across mode switches)
+ const setSshHost = (newHost: string) => {
+ setLastSshHost(newHost);
};
- // Helper to get runtime string for IPC calls
+ // Helper to get runtime string for IPC calls (uses selected mode, not default)
const getRuntimeString = (): string | undefined => {
- return buildRuntimeString(runtimeMode, lastSshHost);
+ return buildRuntimeString(selectedRuntimeMode, lastSshHost);
};
return {
@@ -118,11 +142,14 @@ export function useDraftWorkspaceSettings(
model,
thinkingLevel,
mode,
- runtimeMode,
+ runtimeMode: selectedRuntimeMode,
+ defaultRuntimeMode,
sshHost: lastSshHost,
trunkBranch,
},
- setRuntimeOptions,
+ setRuntimeMode,
+ setDefaultRuntimeMode,
+ setSshHost,
setTrunkBranch,
getRuntimeString,
};
diff --git a/src/browser/hooks/useStartWorkspaceCreation.test.ts b/src/browser/hooks/useStartWorkspaceCreation.test.ts
index d7bab6c673..ae1330a6db 100644
--- a/src/browser/hooks/useStartWorkspaceCreation.test.ts
+++ b/src/browser/hooks/useStartWorkspaceCreation.test.ts
@@ -1,7 +1,6 @@
import { describe, expect, test } from "bun:test";
import {
getFirstProjectPath,
- normalizeRuntimePreference,
persistWorkspaceCreationPrefill,
type StartWorkspaceCreationDetail,
} from "./useStartWorkspaceCreation";
@@ -10,7 +9,6 @@ import {
getModelKey,
getPendingScopeId,
getProjectScopeId,
- getRuntimeKey,
getTrunkBranchKey,
} from "@/common/constants/storage";
import type { ProjectConfig } from "@/node/config";
@@ -20,25 +18,6 @@ import type { updatePersistedState } from "@/browser/hooks/usePersistedState";
type PersistFn = typeof updatePersistedState;
type PersistCall = [string, unknown, unknown?];
-describe("normalizeRuntimePreference", () => {
- test("returns undefined for local or empty runtime", () => {
- expect(normalizeRuntimePreference(undefined)).toBeUndefined();
- expect(normalizeRuntimePreference(" ")).toBeUndefined();
- expect(normalizeRuntimePreference("local")).toBeUndefined();
- expect(normalizeRuntimePreference("LOCAL")).toBeUndefined();
- });
-
- test("normalizes ssh runtimes", () => {
- expect(normalizeRuntimePreference("ssh")).toBe("ssh");
- expect(normalizeRuntimePreference("ssh host")).toBe("ssh host");
- expect(normalizeRuntimePreference("SSH user@host")).toBe("ssh user@host");
- });
-
- test("returns trimmed custom runtime", () => {
- expect(normalizeRuntimePreference(" custom-runtime ")).toBe("custom-runtime");
- });
-});
-
describe("persistWorkspaceCreationPrefill", () => {
const projectPath = "/tmp/project";
@@ -57,7 +36,7 @@ describe("persistWorkspaceCreationPrefill", () => {
startMessage: "Ship it",
model: "provider/model",
trunkBranch: " main ",
- runtime: " ssh dev ",
+ runtime: " ssh dev ", // runtime is NOT persisted - it's a one-time override
};
const { persist, calls } = createPersistSpy();
@@ -71,14 +50,14 @@ describe("persistWorkspaceCreationPrefill", () => {
expect(callMap.get(getInputKey(getPendingScopeId(projectPath)))).toBe("Ship it");
expect(callMap.get(getModelKey(getProjectScopeId(projectPath)))).toBe("provider/model");
expect(callMap.get(getTrunkBranchKey(projectPath))).toBe("main");
- expect(callMap.get(getRuntimeKey(projectPath))).toBe("ssh dev");
+ // runtime is intentionally not persisted - default can only be changed via icon selector
+ expect(calls.length).toBe(3);
});
test("clears persisted values when empty strings are provided", () => {
const detail: StartWorkspaceCreationDetail = {
projectPath,
trunkBranch: " ",
- runtime: " ",
};
const { persist, calls } = createPersistSpy();
@@ -90,7 +69,6 @@ describe("persistWorkspaceCreationPrefill", () => {
}
expect(callMap.get(getTrunkBranchKey(projectPath))).toBeUndefined();
- expect(callMap.get(getRuntimeKey(projectPath))).toBeUndefined();
});
test("no-op when detail is undefined", () => {
diff --git a/src/browser/hooks/useStartWorkspaceCreation.ts b/src/browser/hooks/useStartWorkspaceCreation.ts
index 10d55de5dd..e5b7bda373 100644
--- a/src/browser/hooks/useStartWorkspaceCreation.ts
+++ b/src/browser/hooks/useStartWorkspaceCreation.ts
@@ -8,41 +8,12 @@ import {
getModelKey,
getPendingScopeId,
getProjectScopeId,
- getRuntimeKey,
getTrunkBranchKey,
} from "@/common/constants/storage";
-import { RUNTIME_MODE, SSH_RUNTIME_PREFIX } from "@/common/types/runtime";
export type StartWorkspaceCreationDetail =
CustomEventPayloads[typeof CUSTOM_EVENTS.START_WORKSPACE_CREATION];
-export function normalizeRuntimePreference(runtime: string | undefined): string | undefined {
- if (!runtime) {
- return undefined;
- }
-
- const trimmed = runtime.trim();
- if (!trimmed) {
- return undefined;
- }
-
- const lower = trimmed.toLowerCase();
- if (lower === RUNTIME_MODE.LOCAL) {
- return undefined;
- }
-
- if (lower === RUNTIME_MODE.SSH) {
- return RUNTIME_MODE.SSH;
- }
-
- if (lower.startsWith(SSH_RUNTIME_PREFIX)) {
- const host = trimmed.slice(SSH_RUNTIME_PREFIX.length).trim();
- return host ? `${RUNTIME_MODE.SSH} ${host}` : RUNTIME_MODE.SSH;
- }
-
- return trimmed;
-}
-
export function getFirstProjectPath(projects: Map): string | null {
const iterator = projects.keys().next();
return iterator.done ? null : iterator.value;
@@ -75,10 +46,8 @@ export function persistWorkspaceCreationPrefill(
);
}
- if (detail.runtime !== undefined) {
- const normalizedRuntime = normalizeRuntimePreference(detail.runtime);
- persist(getRuntimeKey(projectPath), normalizedRuntime);
- }
+ // Note: runtime is intentionally NOT persisted here - it's a one-time override.
+ // The default runtime can only be changed via the icon selector.
}
interface UseStartWorkspaceCreationOptions {
diff --git a/src/browser/utils/chatCommands.ts b/src/browser/utils/chatCommands.ts
index 89643d5dcb..f7fbdaab40 100644
--- a/src/browser/utils/chatCommands.ts
+++ b/src/browser/utils/chatCommands.ts
@@ -499,7 +499,7 @@ export async function createNewWorkspace(
effectiveTrunk = recommendedTrunk ?? "main";
}
- // Use saved runtime preference if not explicitly provided
+ // Use saved default runtime preference if not explicitly provided
let effectiveRuntime = options.runtime;
if (effectiveRuntime === undefined) {
const runtimeKey = getRuntimeKey(options.projectPath);
diff --git a/src/common/constants/storage.ts b/src/common/constants/storage.ts
index 09b4724162..5987a6be42 100644
--- a/src/common/constants/storage.ts
+++ b/src/common/constants/storage.ts
@@ -110,7 +110,7 @@ export function getModeKey(workspaceId: string): string {
/**
* Get the localStorage key for the default runtime for a project
- * Stores the last successfully used runtime config when creating a workspace
+ * Defaults to worktree if not set; can only be changed via the "Default for project" checkbox.
* Format: "runtime:{projectPath}"
*/
export function getRuntimeKey(projectPath: string): string {