diff --git a/src/browser/components/AgentModePicker/AgentModePicker.test.tsx b/src/browser/components/AgentModePicker/AgentModePicker.test.tsx index 32aa55886a8..a14e6b93229 100644 --- a/src/browser/components/AgentModePicker/AgentModePicker.test.tsx +++ b/src/browser/components/AgentModePicker/AgentModePicker.test.tsx @@ -47,14 +47,9 @@ const CUSTOM_AGENT: AgentDefinitionDescriptor = { subagentRunnable: false, }; -const noop = () => { - // intentional noop for tests -}; const defaultContextProps = { currentAgent: undefined, isAgentSelectionLocked: false, - disableWorkspaceAgents: false, - setDisableWorkspaceAgents: noop, }; let cleanupDom: (() => void) | null = null; diff --git a/src/browser/components/CommandPalette/CommandPalette.tsx b/src/browser/components/CommandPalette/CommandPalette.tsx index 58b9865b3d1..0df74799940 100644 --- a/src/browser/components/CommandPalette/CommandPalette.tsx +++ b/src/browser/components/CommandPalette/CommandPalette.tsx @@ -2,7 +2,6 @@ import React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useSta import { Command } from "cmdk"; import { useCommandRegistry } from "@/browser/contexts/CommandRegistryContext"; import { useExperimentValue } from "@/browser/hooks/useExperiments"; -import { usePersistedState } from "@/browser/hooks/usePersistedState"; import { getAgentSkillsStore, useAgentSkills } from "@/browser/stores/AgentSkillsStore"; import type { CommandAction } from "@/browser/contexts/CommandRegistryContext"; import { @@ -17,7 +16,6 @@ import { resolveSlashCommandExperimentValue } from "@/browser/utils/slashCommand import { getSlashCommandSuggestions } from "@/browser/utils/slashCommands/suggestions"; import { CUSTOM_EVENTS, createCustomEvent } from "@/common/constants/events"; import { EXPERIMENT_IDS } from "@/common/constants/experiments"; -import { getDisableWorkspaceAgentsKey, GLOBAL_SCOPE_ID } from "@/common/constants/storage"; import { filterCommandsByPrefix } from "@/browser/utils/commandPaletteFiltering"; import { rankByPaletteQuery } from "@/browser/utils/commandPaletteRanking"; @@ -67,17 +65,13 @@ export const CommandPalette: React.FC = ({ getSlashContext const slashContext = getSlashContext?.(); const slashWorkspaceId = slashContext?.workspaceId; - const [disableWorkspaceAgents] = usePersistedState( - getDisableWorkspaceAgentsKey(slashWorkspaceId ?? GLOBAL_SCOPE_ID), - false, - { listener: true } - ); - const commandPanelRef = useRef(null); const paletteOpenOriginRef = useRef(null); const { isOpen, initialQuery, close, getActions, addRecent, recent } = useCommandRegistry(); const agentSkills = useAgentSkills( - isOpen && slashWorkspaceId ? { workspaceId: slashWorkspaceId, disableWorkspaceAgents } : null + isOpen && slashWorkspaceId + ? { workspaceId: slashWorkspaceId, disableWorkspaceAgents: false } + : null ).skills; const [query, setQuery] = useState(""); const [activePrompt, setActivePrompt] = useState = ({ getSlashContext if (slashWorkspaceId && value.trim().startsWith("/") && !query.trim().startsWith("/")) { getAgentSkillsStore().ensureFresh({ workspaceId: slashWorkspaceId, - disableWorkspaceAgents, + disableWorkspaceAgents: false, }); } setQuery(value); @@ -241,7 +235,7 @@ export const CommandPalette: React.FC = ({ getSlashContext setPromptError(null); } }, - [activePrompt, query, slashWorkspaceId, disableWorkspaceAgents] + [activePrompt, query, slashWorkspaceId] ); const generalResults = useMemo(() => { diff --git a/src/browser/components/WorkspaceMenuBar/WorkspaceMenuBar.test.tsx b/src/browser/components/WorkspaceMenuBar/WorkspaceMenuBar.test.tsx index ad41aa8a807..27ecbff91e1 100644 --- a/src/browser/components/WorkspaceMenuBar/WorkspaceMenuBar.test.tsx +++ b/src/browser/components/WorkspaceMenuBar/WorkspaceMenuBar.test.tsx @@ -7,7 +7,6 @@ import { installDom } from "../../../../tests/ui/dom"; import { restoreModulesAfterSuite } from "../../../../tests/ui/moduleMocks"; import * as RealDialogModule from "@/browser/components/Dialog/Dialog"; import * as APIModule from "@/browser/contexts/API"; -import * as AgentContextModule from "@/browser/contexts/AgentContext"; import * as WorkspaceContextModule from "@/browser/contexts/WorkspaceContext"; import * as ProjectContextModule from "@/browser/contexts/ProjectContext"; import * as WorkspaceStoreModule from "@/browser/stores/WorkspaceStore"; @@ -173,12 +172,6 @@ function installWorkspaceMenuBarTestDoubles() { spyOn(APIModule, "useAPI").mockImplementation( () => ({ api: mockApi }) as unknown as ReturnType ); - spyOn(AgentContextModule, "useAgent").mockImplementation( - () => - ({ disableWorkspaceAgents: false }) as unknown as ReturnType< - typeof AgentContextModule.useAgent - > - ); spyOn(WorkspaceContextModule, "useWorkspaceActions").mockImplementation( () => ({ diff --git a/src/browser/components/WorkspaceMenuBar/WorkspaceMenuBar.tsx b/src/browser/components/WorkspaceMenuBar/WorkspaceMenuBar.tsx index d938cec2c4f..10ab5a40830 100644 --- a/src/browser/components/WorkspaceMenuBar/WorkspaceMenuBar.tsx +++ b/src/browser/components/WorkspaceMenuBar/WorkspaceMenuBar.tsx @@ -56,7 +56,6 @@ import { ArchiveIcon } from "../icons/ArchiveIcon/ArchiveIcon"; import { SkillIndicator } from "../SkillIndicator/SkillIndicator"; import { WorkspaceLinks } from "../WorkspaceLinks/WorkspaceLinks"; import { useAPI } from "@/browser/contexts/API"; -import { useAgent } from "@/browser/contexts/AgentContext"; import { useWorkspaceActions, useWorkspaceContext } from "@/browser/contexts/WorkspaceContext"; import { useProjectContext } from "@/browser/contexts/ProjectContext"; @@ -154,7 +153,6 @@ export const WorkspaceMenuBar: React.FC = ({ onOpenTerminal, }) => { const { api } = useAPI(); - const { disableWorkspaceAgents } = useAgent(); const { preflightArchiveWorkspace, archiveWorkspace, archivingWorkspaceIds, setWorkspacePinned } = useWorkspaceActions(); const isArchiving = archivingWorkspaceIds.has(workspaceId); @@ -198,7 +196,7 @@ export const WorkspaceMenuBar: React.FC = ({ setUnrelatedMessagingWorkspaceId(null); } const unrelatedMessagingModalOpen = unrelatedMessagingWorkspaceId === workspaceId; - const skillList = useAgentSkills({ workspaceId, disableWorkspaceAgents }); + const skillList = useAgentSkills({ workspaceId, disableWorkspaceAgents: false }); const moreActionsButtonRef = useRef(null); const menuBarRef = useRef(null); const [moreMenuOpen, setMoreMenuOpen] = useState(false); diff --git a/src/browser/components/WorkspaceModeAISync/WorkspaceModeAISync.test.tsx b/src/browser/components/WorkspaceModeAISync/WorkspaceModeAISync.test.tsx index 2a78bbd69ad..e8a884524c6 100644 --- a/src/browser/components/WorkspaceModeAISync/WorkspaceModeAISync.test.tsx +++ b/src/browser/components/WorkspaceModeAISync/WorkspaceModeAISync.test.tsx @@ -33,8 +33,6 @@ function SyncHarness(props: { workspaceId: string; agentId: string }) { loadFailed: false, refresh: () => Promise.resolve(), refreshing: false, - disableWorkspaceAgents: false, - setDisableWorkspaceAgents: noop, }} > diff --git a/src/browser/contexts/AgentContext.tsx b/src/browser/contexts/AgentContext.tsx index 98fc3a1d707..8f865ad6db2 100644 --- a/src/browser/contexts/AgentContext.tsx +++ b/src/browser/contexts/AgentContext.tsx @@ -14,14 +14,9 @@ import { import { useAPI } from "@/browser/contexts/API"; import { useOptionalWorkspaceMetadata } from "@/browser/contexts/WorkspaceContext"; import type { FrontendWorkspaceMetadata } from "@/common/types/workspace"; -import { usePersistedState } from "@/browser/hooks/usePersistedState"; import { CUSTOM_EVENTS, createCustomEvent } from "@/common/constants/events"; import { matchesKeybind, KEYBINDS } from "@/browser/utils/ui/keybinds"; -import { - getProjectScopeId, - getDisableWorkspaceAgentsKey, - GLOBAL_SCOPE_ID, -} from "@/common/constants/storage"; +import { getProjectScopeId, GLOBAL_SCOPE_ID } from "@/common/constants/storage"; import { useUserPreferences } from "@/browser/stores/AppConfigStore"; import { readScopedAiDefault, @@ -46,12 +41,6 @@ export interface AgentContextValue { refresh: () => Promise; /** True while a refresh is in progress */ refreshing: boolean; - /** - * When true, agents are loaded from projectPath only (ignoring workspace worktree). - * Useful for unbricking when iterating on agent files in a workspace. - */ - disableWorkspaceAgents: boolean; - setDisableWorkspaceAgents: Dispatch>; /** True when workspace metadata locks agent selection changes. */ isAgentSelectionLocked?: boolean; } @@ -116,21 +105,6 @@ function AgentProviderWithState(props: { const explicitScopedAgentId = typeof scopedAgentId === "string" && scopedAgentId.trim().length > 0 ? scopedAgentId : null; - const [disableWorkspaceAgents, setDisableWorkspaceAgents] = usePersistedState( - getDisableWorkspaceAgentsKey(scopeId), - false, - { listener: true } - ); - - // The UI toggle for disableWorkspaceAgents was removed — clear persisted - // true values so users who had it enabled aren't stranded with no way to - // re-enable workspace agents. - useEffect(() => { - if (disableWorkspaceAgents) { - setDisableWorkspaceAgents(false); - } - }, [disableWorkspaceAgents, setDisableWorkspaceAgents]); - const setAgentId: Dispatch> = useCallback( (value) => { const prev = readScopedAiDefault(scopeId, "agentId"); @@ -161,20 +135,11 @@ function AgentProviderWithState(props: { const fetchParamsRef = useRef({ projectPath: props.projectPath, workspaceId: props.workspaceId, - disableWorkspaceAgents, }); const fetchAgents = useCallback( - async ( - projectPath: string | undefined, - workspaceId: string | undefined, - workspaceAgentsDisabled: boolean - ) => { - fetchParamsRef.current = { - projectPath, - workspaceId, - disableWorkspaceAgents: workspaceAgentsDisabled, - }; + async (projectPath: string | undefined, workspaceId: string | undefined) => { + fetchParamsRef.current = { projectPath, workspaceId }; if (!api || (!projectPath && !workspaceId)) { if (isMountedRef.current) { @@ -186,16 +151,11 @@ function AgentProviderWithState(props: { } try { - const result = await api.agents.list({ - projectPath, - workspaceId, - disableWorkspaceAgents: workspaceAgentsDisabled || undefined, - }); + const result = await api.agents.list({ projectPath, workspaceId }); const current = fetchParamsRef.current; if ( current.projectPath === projectPath && current.workspaceId === workspaceId && - current.disableWorkspaceAgents === workspaceAgentsDisabled && isMountedRef.current ) { setAgents(result); @@ -208,7 +168,6 @@ function AgentProviderWithState(props: { if ( current.projectPath === projectPath && current.workspaceId === workspaceId && - current.disableWorkspaceAgents === workspaceAgentsDisabled && isMountedRef.current ) { setAgents([]); @@ -225,8 +184,8 @@ function AgentProviderWithState(props: { setAgents([]); setLoaded(false); setLoadFailed(false); - void fetchAgents(props.projectPath, props.workspaceId, disableWorkspaceAgents); - }, [fetchAgents, props.projectPath, props.workspaceId, disableWorkspaceAgents]); + void fetchAgents(props.projectPath, props.workspaceId); + }, [fetchAgents, props.projectPath, props.workspaceId]); const refresh = useCallback(async () => { if (!props.projectPath && !props.workspaceId) return; @@ -234,13 +193,13 @@ function AgentProviderWithState(props: { setRefreshing(true); try { - await fetchAgents(props.projectPath, props.workspaceId, disableWorkspaceAgents); + await fetchAgents(props.projectPath, props.workspaceId); } finally { if (isMountedRef.current) { setRefreshing(false); } } - }, [fetchAgents, props.projectPath, props.workspaceId, disableWorkspaceAgents]); + }, [fetchAgents, props.projectPath, props.workspaceId]); // Project-scoped providers should inherit the global default agent until a // project-scoped preference is explicitly set. Child/subagent workspaces keep @@ -348,8 +307,6 @@ function AgentProviderWithState(props: { loadFailed, refresh, refreshing, - disableWorkspaceAgents, - setDisableWorkspaceAgents, isAgentSelectionLocked: isCurrentAgentLocked, }), [ @@ -361,8 +318,6 @@ function AgentProviderWithState(props: { loadFailed, refresh, refreshing, - disableWorkspaceAgents, - setDisableWorkspaceAgents, isCurrentAgentLocked, ] ); diff --git a/src/browser/contexts/ThinkingContext.test.tsx b/src/browser/contexts/ThinkingContext.test.tsx index 7c51d4cdb6a..3167e7d4050 100644 --- a/src/browser/contexts/ThinkingContext.test.tsx +++ b/src/browser/contexts/ThinkingContext.test.tsx @@ -83,8 +83,6 @@ const agentContextValue: AgentContextValue = { loadFailed: false, refresh: () => Promise.resolve(), refreshing: false, - disableWorkspaceAgents: false, - setDisableWorkspaceAgents: () => undefined, }; const ThinkingSetterComponent: React.FC = () => { diff --git a/src/browser/features/ChatInput/CreationControls.stories.tsx b/src/browser/features/ChatInput/CreationControls.stories.tsx index 620578cd37a..ab0435bcd3b 100644 --- a/src/browser/features/ChatInput/CreationControls.stories.tsx +++ b/src/browser/features/ChatInput/CreationControls.stories.tsx @@ -90,7 +90,6 @@ const BASE_ARGS = { value: RUNTIME_MODE.WORKTREE, defaultMode: RUNTIME_MODE.WORKTREE, onChange: fn(), - onSetDefault: fn(), runtimeAvailabilityState: getLoadedRuntimeAvailability({ available: true }), } satisfies RuntimeButtonGroupProps; @@ -124,7 +123,6 @@ const DEVCONTAINER_BASE_CREATION_CONTROLS_PROPS: Omit< coderConfigFallback: {}, sshHostFallback: "devbox.internal", defaultRuntimeMode: RUNTIME_MODE.WORKTREE, - onSetDefaultRuntime: fn(), disabled: false, projectPath: "/home/user/projects/my-app", userProjects: new Map(), @@ -340,7 +338,6 @@ const CREATION_ERROR_BASE_CONTROLS_PROPS = { sshHostFallback: "devbox", defaultRuntimeMode: RUNTIME_MODE.WORKTREE, onSelectedRuntimeChange: fn(), - onSetDefaultRuntime: fn(), disabled: false, projectPath: CREATION_PROJECT_PATH, userProjects: new Map(), diff --git a/src/browser/features/ChatInput/CreationControls.tsx b/src/browser/features/ChatInput/CreationControls.tsx index 8bf79d084a9..2a381b1fe8e 100644 --- a/src/browser/features/ChatInput/CreationControls.tsx +++ b/src/browser/features/ChatInput/CreationControls.tsx @@ -109,7 +109,6 @@ interface CreationControlsProps { defaultRuntimeMode: RuntimeChoice; /** Set the currently selected runtime (discriminated union) */ onSelectedRuntimeChange: (runtime: ParsedRuntime) => void; - onSetDefaultRuntime: (mode: RuntimeChoice) => void; disabled: boolean; /** Owning project path used for runtime/settings scoping (always the parent). */ projectPath: string; @@ -146,7 +145,6 @@ export interface RuntimeButtonGroupProps { value: RuntimeChoice; onChange: (mode: RuntimeChoice) => void; defaultMode: RuntimeChoice; - onSetDefault: (mode: RuntimeChoice) => void; disabled?: boolean; runtimeAvailabilityState?: RuntimeAvailabilityState; runtimeEnablement?: RuntimeEnablement; @@ -796,7 +794,6 @@ function CreationControlsContent(props: CreationControlsProps) { } }} defaultMode={props.defaultRuntimeMode} - onSetDefault={props.onSetDefaultRuntime} disabled={props.disabled} runtimeAvailabilityState={runtimeAvailabilityState} runtimeEnablement={props.runtimeEnablement} @@ -1086,7 +1083,6 @@ export function CreationControls(props: CreationControlsProps) { sshHostFallback={props.sshHostFallback} defaultRuntimeMode={props.defaultRuntimeMode} onSelectedRuntimeChange={props.onSelectedRuntimeChange} - onSetDefaultRuntime={props.onSetDefaultRuntime} disabled={props.disabled} projectPath={props.projectPath} selectedProjectPath={props.selectedProjectPath} diff --git a/src/browser/features/ChatInput/index.tsx b/src/browser/features/ChatInput/index.tsx index 956aa7f6698..5b967066fde 100644 --- a/src/browser/features/ChatInput/index.tsx +++ b/src/browser/features/ChatInput/index.tsx @@ -668,7 +668,6 @@ const ChatInputInner: React.FC = (props) => { variant, workspaceId, projectPath: atMentionProjectPath, - disableWorkspaceAgents: sendMessageOptions.disableWorkspaceAgents === true, }); const { agentSkillDescriptors, handleInputCaretChange, mcpPromptDescriptors } = composerSuggestions; @@ -883,7 +882,6 @@ const ChatInputInner: React.FC = (props) => { sshHostFallback: creationState.sshHostFallback, defaultRuntimeMode: creationState.defaultRuntimeMode, onSelectedRuntimeChange: creationState.setSelectedRuntime, - onSetDefaultRuntime: creationState.setDefaultRuntimeChoice, disabled: isSendInFlight, projectPath: creationParentProjectPath, // Surface the actually-targeted project (possibly a sub-project) to @@ -2238,9 +2236,7 @@ const ChatInputInner: React.FC = (props) => { ? { kind: "workspace", workspaceId, - disableWorkspaceAgents: - sendMessageOptions.disableWorkspaceAgents === true || - transferredDraftProjectDiscovery, + disableWorkspaceAgents: transferredDraftProjectDiscovery, } : null; // Captured before command resolution so the row the new workspace opens with shows what was diff --git a/src/browser/features/ChatInput/useComposerSuggestions.ts b/src/browser/features/ChatInput/useComposerSuggestions.ts index 59ae31425af..0a61612bea3 100644 --- a/src/browser/features/ChatInput/useComposerSuggestions.ts +++ b/src/browser/features/ChatInput/useComposerSuggestions.ts @@ -157,12 +157,10 @@ interface UseComposerSuggestionsOptions { variant: "creation" | "workspace"; workspaceId: string | null; projectPath: string | null; - disableWorkspaceAgents: boolean; } export function useComposerSuggestions(options: UseComposerSuggestionsOptions) { - const { disableWorkspaceAgents, input, inputRef, projectPath, setInput, variant, workspaceId } = - options; + const { input, inputRef, projectPath, setInput, variant, workspaceId } = options; const { api } = useAPI(); const memoryEnabled = useExperimentValue(EXPERIMENT_IDS.MEMORY); const memoryConsolidationEnabled = useExperimentValue(EXPERIMENT_IDS.MEMORY_CONSOLIDATION); @@ -212,10 +210,7 @@ export function useComposerSuggestions(options: UseComposerSuggestionsOptions) { const listId = useId(); const skillDiscovery: AgentSkillsDiscovery | null = variant === "workspace" && workspaceId - ? { - workspaceId, - disableWorkspaceAgents: disableWorkspaceAgents || transferredDraftProjectDiscovery, - } + ? { workspaceId, disableWorkspaceAgents: transferredDraftProjectDiscovery } : variant === "creation" && projectPath ? { projectPath } : null; diff --git a/src/browser/features/ChatInput/useCreationWorkspace.test.tsx b/src/browser/features/ChatInput/useCreationWorkspace.test.tsx index a671aa76aee..9c7490e13e5 100644 --- a/src/browser/features/ChatInput/useCreationWorkspace.test.tsx +++ b/src/browser/features/ChatInput/useCreationWorkspace.test.tsx @@ -27,11 +27,7 @@ import type { WorkspaceChatMessage } from "@/common/orpc/types"; import type { DraftEvent, DraftUpdateInput } from "@/common/orpc/schemas/drafts"; import { toDraftAttachmentMetadata } from "@/common/utils/drafts"; -import { - CODER_RUNTIME_PLACEHOLDER, - type CoderWorkspaceConfig, - type ParsedRuntime, -} from "@/common/types/runtime"; +import type { CoderWorkspaceConfig, ParsedRuntime } from "@/common/types/runtime"; import type { RuntimeChoice } from "@/browser/utils/runtimeUi"; import type { FrontendWorkspaceMetadata, @@ -2509,39 +2505,9 @@ function createDraftSettingsHarness( } }); - const setDefaultRuntimeChoice = mock((choice: RuntimeChoice) => { - state.defaultRuntimeMode = choice; - // Update selected runtime to match new default - if (choice === "coder") { - state.selectedRuntime = { - mode: "ssh", - host: CODER_RUNTIME_PLACEHOLDER, - coder: { existingWorkspace: false }, - }; - state.runtimeString = `ssh ${CODER_RUNTIME_PLACEHOLDER}`; - return; - } - if (choice === "ssh") { - const host = state.selectedRuntime.mode === "ssh" ? state.selectedRuntime.host : ""; - state.selectedRuntime = { mode: "ssh", host }; - state.runtimeString = host ? `ssh ${host}` : "ssh"; - } else if (choice === "docker") { - const image = state.selectedRuntime.mode === "docker" ? state.selectedRuntime.image : ""; - state.selectedRuntime = { mode: "docker", image }; - state.runtimeString = image ? `docker ${image}` : "docker"; - } else if (choice === "local") { - state.selectedRuntime = { mode: "local" }; - state.runtimeString = undefined; - } else { - state.selectedRuntime = { mode: "worktree" }; - state.runtimeString = undefined; - } - }); - return { state, setSelectedRuntime, - setDefaultRuntimeChoice, setTrunkBranch, getRuntimeString, snapshot(): { @@ -2549,7 +2515,6 @@ function createDraftSettingsHarness( coderConfigFallback: CoderWorkspaceConfig; sshHostFallback: string; setSelectedRuntime: typeof setSelectedRuntime; - setDefaultRuntimeChoice: typeof setDefaultRuntimeChoice; setTrunkBranch: typeof setTrunkBranch; getRuntimeString: typeof getRuntimeString; } { @@ -2567,7 +2532,6 @@ function createDraftSettingsHarness( coderConfigFallback: state.coderConfigFallback, sshHostFallback: state.sshHostFallback, setSelectedRuntime, - setDefaultRuntimeChoice, setTrunkBranch, getRuntimeString, }; diff --git a/src/browser/features/ChatInput/useCreationWorkspace.ts b/src/browser/features/ChatInput/useCreationWorkspace.ts index 8a13df182b4..68aa92745a4 100644 --- a/src/browser/features/ChatInput/useCreationWorkspace.ts +++ b/src/browser/features/ChatInput/useCreationWorkspace.ts @@ -175,8 +175,6 @@ interface UseCreationWorkspaceReturn { defaultRuntimeMode: RuntimeChoice; /** Set the currently selected runtime (discriminated union) */ setSelectedRuntime: (runtime: ParsedRuntime) => void; - /** Set the default runtime choice for this project (persists via checkbox) */ - setDefaultRuntimeChoice: (choice: RuntimeChoice) => void; toast: Toast | null; setToast: (toast: Toast | null) => void; isSending: boolean; @@ -291,14 +289,8 @@ export function useCreationWorkspace({ useState({ status: "loading" }); // Centralized draft workspace settings with automatic persistence - const { - settings, - coderConfigFallback, - sshHostFallback, - setSelectedRuntime, - setDefaultRuntimeChoice, - setTrunkBranch, - } = useDraftWorkspaceSettings(projectPath, branches, recommendedTrunk); + const { settings, coderConfigFallback, sshHostFallback, setSelectedRuntime, setTrunkBranch } = + useDraftWorkspaceSettings(projectPath, branches, recommendedTrunk); const creationAgentId = resolveCreationAgentId(settings.agentId, agentBaseById); // Persist draft workspace name generation state per draft (so multiple drafts don't share a @@ -482,9 +474,8 @@ export function useCreationWorkspace({ const normalizedTitle = typeof identity.title === "string" ? identity.title.trim() : ""; const createTitle = normalizedTitle || undefined; - // Read send options fresh from localStorage at send time to avoid - // race conditions with React state updates (requestAnimationFrame batching - // in usePersistedState can delay state updates after model selection). + // Read send options at send time: render state can lag a pick made just + // before sending. // Override agentId from current draft settings so first-send uses the same // project/global/default resolution chain as the creation UI. const sendMessageOptions = { @@ -1015,7 +1006,6 @@ export function useCreationWorkspace({ sshHostFallback, defaultRuntimeMode: settings.defaultRuntimeMode, setSelectedRuntime, - setDefaultRuntimeChoice, toast, setToast, isSending, diff --git a/src/browser/features/Tools/ProposePlanToolCall.test.tsx b/src/browser/features/Tools/ProposePlanToolCall.test.tsx index 407b7acbb7d..9c5a49a9ce0 100644 --- a/src/browser/features/Tools/ProposePlanToolCall.test.tsx +++ b/src/browser/features/Tools/ProposePlanToolCall.test.tsx @@ -232,8 +232,6 @@ function wrapToolCall(content: JSX.Element, agentId = "plan") { loadFailed: false, refresh: () => Promise.resolve(), refreshing: false, - disableWorkspaceAgents: false, - setDisableWorkspaceAgents: noop, }} > {content} diff --git a/src/browser/hooks/persistedStateBudget.test.ts b/src/browser/hooks/persistedStateBudget.test.ts index 01ee91a166a..a9cf06578e9 100644 --- a/src/browser/hooks/persistedStateBudget.test.ts +++ b/src/browser/hooks/persistedStateBudget.test.ts @@ -13,13 +13,10 @@ import { installDom } from "../../../tests/ui/dom"; import { updatePersistedState } from "@/browser/hooks/usePersistedState"; import * as storageModule from "@/common/constants/storage"; import { - GLOBAL_SCOPE_ID, PERSISTED_KEY_REGISTRY, - getDisableWorkspaceAgentsKey, getDraftScopeId, getPersistedKeyKind, getPersistedKeyRegistration, - getProjectScopeId, type PersistedKeyRegistration, } from "@/common/constants/storage"; @@ -44,12 +41,6 @@ const MODEL_COUNT = 50; const NON_EVICTABLE_CEILING_CHARS = 2.5 * 1024 * 1024; const LOCAL_STORAGE_BUDGET_CEILING_CHARS = 3.5 * 1024 * 1024; -/** - * Workspace keys that creation flows also write under project and global scope ids (creation - * defaults), so they are counted once per project plus once for the global scope. - */ -const PROJECT_SCOPED_WORKSPACE_KEYS = [getDisableWorkspaceAgentsKey]; - function projectPath(index: number): string { const prefix = `/Users/someone/src/github.com/org/project-${index}/`; return prefix + "p".repeat(PROJECT_PATH_CHARS - prefix.length); @@ -64,7 +55,6 @@ const workspaceIds = Array.from({ length: WORKSPACE_COUNT }, (_, index) => works const draftScopeIds = Array.from({ length: DRAFT_SCOPE_COUNT }, (_, index) => getDraftScopeId(projectPaths[index % PROJECT_COUNT], `${index}`.padStart(36, "d")) ); -const projectScopeIds = [...projectPaths.map(getProjectScopeId), GLOBAL_SCOPE_ID]; /** Every concrete key the worst-case model writes for one registration. */ function modelledKeys(entry: PersistedKeyRegistration): string[] { @@ -75,11 +65,7 @@ function modelledKeys(entry: PersistedKeyRegistration): string[] { : entry.scopes === "webview" ? workspaceIds : [...workspaceIds, ...draftScopeIds]; - const keys = scopeIds.map(entry.getKey); - if (PROJECT_SCOPED_WORKSPACE_KEYS.includes(entry.getKey)) { - keys.push(...projectScopeIds.map(entry.getKey)); - } - return keys; + return scopeIds.map(entry.getKey); } if (entry.match === "exact") return [entry.key]; switch (entry.instances) { diff --git a/src/browser/hooks/useDraftWorkspaceSettings.test.tsx b/src/browser/hooks/useDraftWorkspaceSettings.test.tsx index 41c278170e8..10bd53cfdeb 100644 --- a/src/browser/hooks/useDraftWorkspaceSettings.test.tsx +++ b/src/browser/hooks/useDraftWorkspaceSettings.test.tsx @@ -5,8 +5,6 @@ import React from "react"; import { APIProvider, type APIClient } from "@/browser/contexts/API"; import { ProjectProvider } from "@/browser/contexts/ProjectContext"; import { ThinkingProvider } from "@/browser/contexts/ThinkingContext"; -import { readPersistedState, updatePersistedState } from "@/browser/hooks/usePersistedState"; -import { getRuntimeKey } from "@/common/constants/storage"; import { CODER_RUNTIME_PLACEHOLDER } from "@/common/types/runtime"; import { useDraftWorkspaceSettings } from "./useDraftWorkspaceSettings"; import { createTestApiClient, createTestPreferencesConfig } from "@/browser/testUtils"; @@ -252,7 +250,6 @@ describe("useDraftWorkspaceSettings", () => { const projectPath = "/tmp/project"; getAppConfigStore().updateOptimistically({ defaultRuntime: "coder" }); - updatePersistedState(getRuntimeKey(projectPath), "ssh dev@host"); const lastRuntimeConfig = { ssh: { host: "dev@host", @@ -277,42 +274,6 @@ describe("useDraftWorkspaceSettings", () => { }); }); - test("persists Coder default string when toggling default", async () => { - const projectPath = "/tmp/project"; - - const lastRuntimeConfig = { - ssh: { - host: "dev@host", - coderEnabled: false, - coderConfig: { existingWorkspace: false }, - }, - }; - - const wrapper = createWrapper(projectPath, runtimeConfigPrefs(projectPath, lastRuntimeConfig)); - - const { result } = renderHook(() => useDraftWorkspaceSettings(projectPath, ["main"], "main"), { - wrapper, - }); - - act(() => { - result.current.setDefaultRuntimeChoice("coder"); - }); - - await waitFor(() => { - expect(result.current.settings.selectedRuntime).toEqual({ - mode: "ssh", - host: CODER_RUNTIME_PLACEHOLDER, - coder: { existingWorkspace: false }, - }); - }); - - const defaultRuntimeString = readPersistedState( - getRuntimeKey(projectPath), - undefined - ); - expect(defaultRuntimeString).toBe(`ssh ${CODER_RUNTIME_PLACEHOLDER}`); - }); - test("exposes persisted Coder config as fallback when re-selecting Coder", async () => { const projectPath = "/tmp/project"; const savedCoderConfig = { existingWorkspace: true, workspaceName: "saved-workspace" }; diff --git a/src/browser/hooks/useDraftWorkspaceSettings.ts b/src/browser/hooks/useDraftWorkspaceSettings.ts index 08d47da95e0..ca1a52c6fba 100644 --- a/src/browser/hooks/useDraftWorkspaceSettings.ts +++ b/src/browser/hooks/useDraftWorkspaceSettings.ts @@ -1,5 +1,4 @@ import { useState, useEffect, useRef, useCallback } from "react"; -import { usePersistedState } from "./usePersistedState"; import { useReasoningMode } from "./useReasoningMode"; import { useThinkingLevel } from "./useThinkingLevel"; import { normalizeSelectedModel } from "@/common/utils/ai/models"; @@ -15,14 +14,12 @@ import { import type { RuntimeChoice } from "@/browser/utils/runtimeUi"; import { readOptionField, - readRuntimeOptionDefaults, readSshOptionDefaults, type RuntimeOptionDefaults, updateRuntimeOptionDefaults, useRuntimeOptionDefaults, writeSshOptionDefaults, } from "@/browser/utils/runtimeOptionDefaults"; -import { getRuntimeKey } from "@/common/constants/storage"; import { updateUserPreferences, useAppConfig, @@ -220,8 +217,6 @@ export function useDraftWorkspaceSettings( sshHostFallback: string; /** Set the currently selected runtime (discriminated union) */ setSelectedRuntime: (runtime: ParsedRuntime) => void; - /** Set the default runtime choice for this project (persists via checkbox) */ - setDefaultRuntimeChoice: (choice: RuntimeChoice) => void; setTrunkBranch: (branch: string) => void; getRuntimeString: () => string | undefined; } { @@ -262,15 +257,6 @@ export function useDraftWorkspaceSettings( const rawGlobalDefaultRuntime = useAppConfig((config) => config.defaultRuntime); const globalDefaultRuntime = normalizeRuntimeChoice(rawGlobalDefaultRuntime); - // Project-scoped default runtime (persisted when the creation tooltip checkbox is used). - // Legacy per-project default (only write-side used by setDefaultRuntimeChoice; reads - // now come from settingsDefaultRuntime above). - const [, setDefaultRuntimeString] = usePersistedState( - getRuntimeKey(projectPath), - undefined, - { listener: true } - ); - const hasProjectRuntimeOverrides = projectConfig?.runtimeOverridesEnabled === true || Boolean(projectConfig?.runtimeEnablement) || @@ -279,9 +265,6 @@ export function useDraftWorkspaceSettings( ? (projectConfig?.defaultRuntime ?? globalDefaultRuntime ?? RUNTIME_MODE.WORKTREE) : (globalDefaultRuntime ?? RUNTIME_MODE.WORKTREE); - // Always use the Settings-configured default as the canonical source of truth. - // The old per-project localStorage key (getRuntimeKey) is now stale since the creation - // tooltip default toggle was removed; new defaults come from the Runtimes settings panel. const parsedDefault = buildRuntimeFromChoice(settingsDefaultRuntime); const defaultRuntimeMode: RuntimeMode = parsedDefault?.mode ?? RUNTIME_MODE.WORKTREE; @@ -512,38 +495,6 @@ export function useDraftWorkspaceSettings( } }; - // Setter for default runtime choice (persists via checkbox in tooltip) - const setDefaultRuntimeChoice = (choice: RuntimeChoice) => { - // Defaults should only change when the checkbox is toggled, not when last-used SSH flips. - const freshRuntimeConfigs = readRuntimeOptionDefaults(projectPath); - const freshSshState = readSshRuntimeState(freshRuntimeConfigs); - - const newMode = choice === "coder" ? RUNTIME_MODE.SSH : choice; - const sshConfig: SshRuntimeConfig = - choice === "coder" - ? { - host: CODER_RUNTIME_PLACEHOLDER, - coder: freshSshState.coderConfig ?? DEFAULT_CODER_CONFIG, - } - : { - host: freshSshState.host, - coder: undefined, - }; - - const newRuntime = buildRuntimeForMode( - newMode, - sshConfig, - lastDockerImage, - lastShareCredentials, - defaultDevcontainerConfigPath, - lastDevcontainerShareCredentials - ); - const newRuntimeString = buildRuntimeString(newRuntime); - setDefaultRuntimeString(newRuntimeString); - // Also update selection to match new default - setSelectedRuntimeState(newRuntime); - }; - // Helper to get runtime string for IPC calls const getRuntimeString = (): string | undefined => { return buildRuntimeString(selectedRuntime); @@ -562,7 +513,6 @@ export function useDraftWorkspaceSettings( coderConfigFallback, sshHostFallback, setSelectedRuntime, - setDefaultRuntimeChoice, setTrunkBranch, getRuntimeString, }; diff --git a/src/browser/hooks/useSendMessageOptions.ts b/src/browser/hooks/useSendMessageOptions.ts index 5832afb7bd4..924762350a4 100644 --- a/src/browser/hooks/useSendMessageOptions.ts +++ b/src/browser/hooks/useSendMessageOptions.ts @@ -44,7 +44,7 @@ export function useAutoRoutingSelection( export function useSendMessageOptions(workspaceId: string): SendMessageOptionsWithBase { const [thinkingLevel] = useThinkingLevel(); const [reasoningMode] = useReasoningMode(); - const { agentId, agents, disableWorkspaceAgents } = useAgent(); + const { agentId, agents } = useAgent(); const { options: providerOptions } = useProviderOptions(); const baseModel = useWorkspaceAiSelection( @@ -62,7 +62,6 @@ export function useSendMessageOptions(workspaceId: string): SendMessageOptionsWi reasoningMode, model: baseModel, providerOptions, - disableWorkspaceAgents, autoModelRouting, autoThinkingLevel, }); diff --git a/src/browser/utils/chatCommands.test.ts b/src/browser/utils/chatCommands.test.ts index 8cf8ee23f94..efb960dcf50 100644 --- a/src/browser/utils/chatCommands.test.ts +++ b/src/browser/utils/chatCommands.test.ts @@ -1,7 +1,8 @@ -import { describe, expect, test, beforeEach, mock, spyOn } from "bun:test"; +import { afterEach, describe, expect, test, beforeEach, mock, spyOn } from "bun:test"; import type { HistoryEditPrecondition, SendMessageOptions } from "@/common/orpc/types"; import { MODEL_KEY_MAX_CHARS } from "@/common/constants/storage"; import { + createNewWorkspace, executeCompaction, parseRuntimeString, prepareCompactionMessage, @@ -15,6 +16,9 @@ import { import { parseCommand } from "./slashCommands/parser"; import type { ReviewNoteData } from "@/common/types/review"; import { useWorkspaceStoreRaw, workspaceStore } from "@/browser/stores/WorkspaceStore"; +import { getAppConfigStore } from "@/browser/stores/AppConfigStore"; +import { createTestApiClient } from "@/browser/testUtils"; +import type { RuntimeConfig, RuntimeEnablementId } from "@/common/types/runtime"; import type { FrontendWorkspaceMetadata } from "@/common/types/workspace"; import { EDIT_HISTORY_CHANGED_MESSAGE, @@ -1623,3 +1627,39 @@ describe("prepareCompactionMessage", () => { expect(metadata.parsed.followUpContent?.reviews).toHaveLength(1); }); }); + +describe("createNewWorkspace", () => { + afterEach(() => { + getAppConfigStore().updateOptimistically({ defaultRuntime: undefined }); + }); + + test("/new applies only a configured local default runtime, the project's first", async () => { + const runtimeConfigs: Array = []; + let projectDefaultRuntime: RuntimeEnablementId | undefined; + const client = createTestApiClient({ + projects: { + list: () => + Promise.resolve([["/repo", { workspaces: [], defaultRuntime: projectDefaultRuntime }]]), + }, + workspace: { + create: (input: { runtimeConfig?: RuntimeConfig }) => { + runtimeConfigs.push(input.runtimeConfig); + return Promise.resolve({ success: false as const, error: "stop after create" }); + }, + }, + }); + + for (const [globalDefault, projectDefault] of [ + ["local", undefined], + ["ssh", undefined], + ["local", "worktree"], + ["worktree", "local"], + ] as const) { + getAppConfigStore().updateOptimistically({ defaultRuntime: globalDefault }); + projectDefaultRuntime = projectDefault; + await createNewWorkspace({ client, projectPath: "/repo", trunkBranch: "main" }); + } + + expect(runtimeConfigs).toEqual([{ type: "local" }, undefined, undefined, { type: "local" }]); + }); +}); diff --git a/src/browser/utils/chatCommands.ts b/src/browser/utils/chatCommands.ts index 53548c6e3b6..0cc8603f256 100644 --- a/src/browser/utils/chatCommands.ts +++ b/src/browser/utils/chatCommands.ts @@ -63,10 +63,9 @@ import { getExplicitGatewayPrefix, normalizeToCanonical } from "@/common/utils/a import type { QueueDispatchMode } from "@/browser/features/ChatInput/types"; import type { ChatAttachment } from "../features/ChatInput/ChatAttachments"; import { dispatchWorkspaceSwitch } from "./workspaceEvents"; -import { getRuntimeKey } from "@/common/constants/storage"; import { copyWorkspaceStorage } from "@/browser/utils/workspaceStorage"; import { copyPendingAiSelection } from "@/browser/utils/aiSelectionIntent"; -import { readPersistedRawString } from "@/browser/hooks/usePersistedState"; +import { getAppConfigStore } from "@/browser/stores/AppConfigStore"; import { buildCompactionMessageText } from "@/common/utils/compaction/compactionPrompt"; import { getProviderModelEntryId } from "@/common/utils/providers/modelEntries"; import { isCustomProviderConfig } from "@/common/utils/providers/customProviders"; @@ -1381,18 +1380,18 @@ export async function createNewWorkspace( effectiveTrunk = recommendedTrunk ?? "main"; } - // Use saved default runtime preference if not explicitly provided - let effectiveRuntime = options.runtime; - if (effectiveRuntime === undefined) { - const runtimeKey = getRuntimeKey(options.projectPath); - const savedRuntime = readPersistedRawString(runtimeKey); - if (savedRuntime) { - effectiveRuntime = savedRuntime; - } + // /new cannot ask for the host, image or config path the other runtimes need, so only a + // configured local default applies; anything else gets the backend's worktree default. + let runtime = options.runtime; + if (runtime === undefined) { + // A project default replaces the global one, as in the creation UI. + const projects = new Map(await options.client.projects.list()); + const defaultRuntime = + projects.get(options.projectPath)?.defaultRuntime ?? + getAppConfigStore().getSnapshot()?.defaultRuntime; + if (defaultRuntime === RUNTIME_MODE.LOCAL) runtime = defaultRuntime; } - - // Parse runtime config if provided. - const runtimeConfig = parseRuntimeString(effectiveRuntime); + const runtimeConfig = parseRuntimeString(runtime); const result = await options.client.workspace.create({ projectPath: options.projectPath, diff --git a/src/browser/utils/legacyLocalStorageCleanup.test.ts b/src/browser/utils/legacyLocalStorageCleanup.test.ts index 31c20bae658..7f7dfb70d42 100644 --- a/src/browser/utils/legacyLocalStorageCleanup.test.ts +++ b/src/browser/utils/legacyLocalStorageCleanup.test.ts @@ -29,6 +29,10 @@ describe("removeDroppedCacheKeys", () => { "model:__project__/repo", "thinkingLevel:model:openai:gpt-5.2", "reasoningMode:0123456789", + "pinnedAgentId:0123456789", + "autoCompaction:enabled:0123456789", + "disableWorkspaceAgents:0123456789", + "runtime:/repo", ]; for (const key of [...droppedKeys, ...keptKeys]) { window.localStorage.setItem(key, JSON.stringify({ value: key })); diff --git a/src/browser/utils/legacyLocalStorageCleanup.ts b/src/browser/utils/legacyLocalStorageCleanup.ts index b7cfea571f2..acfe53bf0d9 100644 --- a/src/browser/utils/legacyLocalStorageCleanup.ts +++ b/src/browser/utils/legacyLocalStorageCleanup.ts @@ -20,6 +20,10 @@ const DROPPED_CACHE_KEY_PREFIXES = [ "model:", "thinkingLevel:", "reasoningMode:", + "pinnedAgentId:", + "autoCompaction:enabled:", + "disableWorkspaceAgents:", + "runtime:", ] as const; /** diff --git a/src/browser/utils/messages/buildSendMessageOptions.ts b/src/browser/utils/messages/buildSendMessageOptions.ts index b90f529efaa..6b698c2b6cb 100644 --- a/src/browser/utils/messages/buildSendMessageOptions.ts +++ b/src/browser/utils/messages/buildSendMessageOptions.ts @@ -9,7 +9,6 @@ export interface SendMessageOptionsInput { reasoningMode: OpenAIReasoningMode; agentId: string; providerOptions: MuxProviderOptions; - disableWorkspaceAgents?: boolean; /** Composer Auto selection; only real user sends set it (compaction/resume paths leave it unset). */ autoModelRouting?: boolean; /** Composer thinking level set to Auto; independent of autoModelRouting. */ @@ -34,7 +33,6 @@ export function buildSendMessageOptions(input: SendMessageOptionsInput): SendMes model: input.model, agentId: input.agentId, providerOptions: input.providerOptions, - disableWorkspaceAgents: input.disableWorkspaceAgents ? true : undefined, autoModelRouting: input.autoModelRouting ? true : undefined, autoThinkingLevel: input.autoThinkingLevel ? true : undefined, }; diff --git a/src/browser/utils/messages/compactionOptions.ts b/src/browser/utils/messages/compactionOptions.ts index 66de914de42..37929f81b5e 100644 --- a/src/browser/utils/messages/compactionOptions.ts +++ b/src/browser/utils/messages/compactionOptions.ts @@ -22,7 +22,7 @@ import { * transforms workspace defaults. Both initial sends and stream resumption * use this function to ensure consistent behavior. * - * @param baseOptions - Workspace default options (from localStorage or useSendMessageOptions) + * @param baseOptions - Workspace default options (from getSendOptionsFromStorage or useSendMessageOptions) * @param compactData - Compaction request metadata from /compact command * @returns Final SendMessageOptions with compaction overrides applied */ diff --git a/src/browser/utils/messages/sendOptions.ts b/src/browser/utils/messages/sendOptions.ts index b2a39ed2276..faffdca793e 100644 --- a/src/browser/utils/messages/sendOptions.ts +++ b/src/browser/utils/messages/sendOptions.ts @@ -1,5 +1,3 @@ -import { getDisableWorkspaceAgentsKey } from "@/common/constants/storage"; -import { readPersistedState } from "@/browser/hooks/usePersistedState"; import { buildSendMessageOptions } from "@/browser/utils/messages/buildSendMessageOptions"; import type { SendMessageOptions } from "@/common/orpc/types"; import type { MuxProviderOptions } from "@/common/types/providerOptions"; @@ -18,7 +16,7 @@ function getProviderOptions(): MuxProviderOptions { } /** - * Non-hook equivalent of useSendMessageOptions — reads current preferences from localStorage. + * Non-hook equivalent of useSendMessageOptions: resolves the current picks and preferences. * Used by compaction, resume, idle-compaction, and plan execution outside React context. */ export function getSendOptionsFromStorage(workspaceId: string): SendMessageOptions { @@ -27,11 +25,6 @@ export function getSendOptionsFromStorage(workspaceId: string): SendMessageOptio const providerOptions = getProviderOptions(); - const disableWorkspaceAgents = readPersistedState( - getDisableWorkspaceAgentsKey(workspaceId), - false - ); - // Same gate as useAutoRoutingSelection: a saved true must not reach the backend // once the experiment is off. const autoRoutingEnabled = @@ -44,7 +37,6 @@ export function getSendOptionsFromStorage(workspaceId: string): SendMessageOptio ...selection, agentId, providerOptions, - disableWorkspaceAgents, autoModelRouting, autoThinkingLevel, }); diff --git a/src/browser/utils/workspaceStorage.test.ts b/src/browser/utils/workspaceStorage.test.ts index 52d2e248d3d..4f673ac2377 100644 --- a/src/browser/utils/workspaceStorage.test.ts +++ b/src/browser/utils/workspaceStorage.test.ts @@ -15,7 +15,6 @@ import { getPersistedKeyRegistration, getWorkspaceNameStateKey, getDesktopPopoutKey, - getDisableWorkspaceAgentsKey, getMCPTestResultsKey, getPinnedTodoExpandedKey, getReviewFileFilterKey, @@ -43,7 +42,6 @@ describe("deleteWorkspaceStorage", () => { const workspaceId = "ws-delete-missing"; const otherWorkspaceKey = getPinnedTodoExpandedKey("ws-other"); const keys = [ - getDisableWorkspaceAgentsKey, getPinnedTodoExpandedKey, getSubAgentTasksExpandedKey, getRightSidebarLayoutKey, diff --git a/src/common/constants/storage.ts b/src/common/constants/storage.ts index 110ee37ab6b..2a88d5ce44d 100644 --- a/src/common/constants/storage.ts +++ b/src/common/constants/storage.ts @@ -261,31 +261,6 @@ export function getAutoRetryKey(workspaceId: string): string { return `${workspaceId}-autoRetry`; } -/** - * Get the localStorage key for the pinned third agent id for a scope. - * Format: "pinnedAgentId:{scopeId}" - */ -export function getPinnedAgentIdKey(scopeId: string): string { - return `pinnedAgentId:${scopeId}`; -} -/** - * Get the localStorage key for "disable workspace agents" toggle per scope. - * When true, workspace-specific agents are disabled - only built-in and global agents are loaded. - * Useful for "unbricking" when iterating on agent files in a workspace worktree. - * Format: "disableWorkspaceAgents:{scopeId}" - */ -export function getDisableWorkspaceAgentsKey(scopeId: string): string { - return `disableWorkspaceAgents:${scopeId}`; -} -/** - * Get the localStorage key for the default runtime for a project - * 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 { - return `runtime:${projectPath}`; -} - /** * Get the localStorage key for whether to show the "Initialize with AGENTS.md" nudge for a project. * Set to true when a project is first added; cleared when user dismisses or runs /init. @@ -651,14 +626,6 @@ export function getDesktopPopoutKey(workspaceId: string): string { return `desktop-popout:${workspaceId}`; } -/** - * Get the localStorage key for auto-compaction enabled preference per workspace - * Format: "autoCompaction:enabled:{workspaceId}" - */ -export function getAutoCompactionEnabledKey(workspaceId: string): string { - return `autoCompaction:enabled:${workspaceId}`; -} - /** localStorage-backed LRU caches (see src/browser/utils/lruCache.ts). */ export const SESSION_COST_CACHE_ENTRY_PREFIX = "session-cost:"; export const SESSION_COST_CACHE_INDEX_KEY = "session-cost-index"; @@ -858,7 +825,6 @@ export const PERSISTED_KEY_REGISTRY: readonly PersistedKeyRegistration[] = [ // The VS Code webview composer's unsent text. Longer drafts still work for the session (kept in // memory); the webview then persists only the last text that fit. workspaceKey(getInputKey, "draft", false, 8192, "webview"), - workspaceKey(getPinnedAgentIdKey, "ui", true, 128), workspaceKey(getReviewSelectedHunkKey, "workspace-scoped", true, 256), workspaceKey( getFileTreeExpandStateKey, @@ -868,7 +834,6 @@ export const PERSISTED_KEY_REGISTRY: readonly PersistedKeyRegistration[] = [ ), workspaceKey(getReviewSearchStateKey, "workspace-scoped", true, REVIEW_SEARCH_STATE_MAX_CHARS), workspaceKey(getReviewImmersiveKey, "workspace-scoped", true, 16), - workspaceKey(getAutoCompactionEnabledKey, "ui", true, 16), workspaceKey(getWorkspaceLastReadKey, "workspace-scoped", true, 32), // Not a cache: a status_set result compacted out of history cannot be re-derived after reload // (see StreamingMessageAggregator.loadPersistedAgentStatus), so it must never be evicted. @@ -892,7 +857,6 @@ export const PERSISTED_KEY_REGISTRY: readonly PersistedKeyRegistration[] = [ workspaceKey(getPendingDraftSkillDiscoveryKey, "workspace-scoped", false, 16), // Per-workspace keys that deleteWorkspaceStorage used to miss, leaving orphans behind. - workspaceKey(getDisableWorkspaceAgentsKey, "ui", false, 16), workspaceKey(getPinnedTodoExpandedKey, "ui", false, 16), workspaceKey(getSubAgentTasksExpandedKey, "ui", false, 16), workspaceKey(getRightSidebarLayoutKey, "ui", false, RIGHT_SIDEBAR_LAYOUT_MAX_CHARS), @@ -941,7 +905,6 @@ export const PERSISTED_KEY_REGISTRY: readonly PersistedKeyRegistration[] = [ globalKey(EXPANDED_TASK_GROUPS_KEY, "ui", SIDEBAR_EXPANSION_MAP_MAX_CHARS), // Project-scoped families ("{prefix}{projectPath}"). - globalPrefix(projectPrefix(getRuntimeKey), "ui", 256, "project"), globalPrefix(projectPrefix(getAgentsInitNudgeKey), "ui", 16, "project"), globalPrefix(projectPrefix(getArchivedWorkspacesExpandedKey), "ui", 16, "project"), globalPrefix(projectPrefix(getBrowserSelectedSessionKey), "ui", 256, "project"), diff --git a/src/common/types/runtime.ts b/src/common/types/runtime.ts index 0100fed42dd..b46ea0a955f 100644 --- a/src/common/types/runtime.ts +++ b/src/common/types/runtime.ts @@ -113,7 +113,7 @@ export type ParsedRuntime = | { mode: "devcontainer"; configPath: string; shareCredentials?: boolean }; /** - * Parse runtime string from localStorage or UI input into structured result. + * Parse runtime string from a CLI flag or UI input into structured result. * Format: "ssh " -> { mode: "ssh", host: "" } * "docker " -> { mode: "docker", image: "" } * "worktree" -> { mode: "worktree" } @@ -121,7 +121,6 @@ export type ParsedRuntime = * undefined/null -> { mode: "worktree" } (default) * * Note: "ssh" or "docker" without arguments returns null (invalid). - * Use this for UI state management (localStorage, form inputs). */ export function parseRuntimeModeAndHost(runtime: string | null | undefined): ParsedRuntime | null { if (!runtime) {