Skip to content
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
16 changes: 5 additions & 11 deletions src/browser/components/CommandPalette/CommandPalette.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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";

Expand Down Expand Up @@ -67,17 +65,13 @@ export const CommandPalette: React.FC<CommandPaletteProps> = ({ getSlashContext
const slashContext = getSlashContext?.();
const slashWorkspaceId = slashContext?.workspaceId;

const [disableWorkspaceAgents] = usePersistedState<boolean>(
getDisableWorkspaceAgentsKey(slashWorkspaceId ?? GLOBAL_SCOPE_ID),
false,
{ listener: true }
);

const commandPanelRef = useRef<HTMLDivElement | null>(null);
const paletteOpenOriginRef = useRef<HTMLElement | null>(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<null | {
Expand Down Expand Up @@ -233,15 +227,15 @@ export const CommandPalette: React.FC<CommandPaletteProps> = ({ getSlashContext
if (slashWorkspaceId && value.trim().startsWith("/") && !query.trim().startsWith("/")) {
getAgentSkillsStore().ensureFresh({
workspaceId: slashWorkspaceId,
disableWorkspaceAgents,
disableWorkspaceAgents: false,
});
}
setQuery(value);
if (activePrompt) {
setPromptError(null);
}
},
[activePrompt, query, slashWorkspaceId, disableWorkspaceAgents]
[activePrompt, query, slashWorkspaceId]
);

const generalResults = useMemo(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -173,12 +172,6 @@ function installWorkspaceMenuBarTestDoubles() {
spyOn(APIModule, "useAPI").mockImplementation(
() => ({ api: mockApi }) as unknown as ReturnType<typeof APIModule.useAPI>
);
spyOn(AgentContextModule, "useAgent").mockImplementation(
() =>
({ disableWorkspaceAgents: false }) as unknown as ReturnType<
typeof AgentContextModule.useAgent
>
);
spyOn(WorkspaceContextModule, "useWorkspaceActions").mockImplementation(
() =>
({
Expand Down
4 changes: 1 addition & 3 deletions src/browser/components/WorkspaceMenuBar/WorkspaceMenuBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -154,7 +153,6 @@ export const WorkspaceMenuBar: React.FC<WorkspaceMenuBarProps> = ({
onOpenTerminal,
}) => {
const { api } = useAPI();
const { disableWorkspaceAgents } = useAgent();
const { preflightArchiveWorkspace, archiveWorkspace, archivingWorkspaceIds, setWorkspacePinned } =
useWorkspaceActions();
const isArchiving = archivingWorkspaceIds.has(workspaceId);
Expand Down Expand Up @@ -198,7 +196,7 @@ export const WorkspaceMenuBar: React.FC<WorkspaceMenuBarProps> = ({
setUnrelatedMessagingWorkspaceId(null);
}
const unrelatedMessagingModalOpen = unrelatedMessagingWorkspaceId === workspaceId;
const skillList = useAgentSkills({ workspaceId, disableWorkspaceAgents });
const skillList = useAgentSkills({ workspaceId, disableWorkspaceAgents: false });
const moreActionsButtonRef = useRef<HTMLButtonElement | null>(null);
const menuBarRef = useRef<HTMLDivElement | null>(null);
const [moreMenuOpen, setMoreMenuOpen] = useState(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,6 @@ function SyncHarness(props: { workspaceId: string; agentId: string }) {
loadFailed: false,
refresh: () => Promise.resolve(),
refreshing: false,
disableWorkspaceAgents: false,
setDisableWorkspaceAgents: noop,
}}
>
<WorkspaceModeAISync workspaceId={props.workspaceId} />
Expand Down
61 changes: 8 additions & 53 deletions src/browser/contexts/AgentContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -46,12 +41,6 @@ export interface AgentContextValue {
refresh: () => Promise<void>;
/** 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<SetStateAction<boolean>>;
/** True when workspace metadata locks agent selection changes. */
isAgentSelectionLocked?: boolean;
}
Expand Down Expand Up @@ -116,21 +105,6 @@ function AgentProviderWithState(props: {
const explicitScopedAgentId =
typeof scopedAgentId === "string" && scopedAgentId.trim().length > 0 ? scopedAgentId : null;

const [disableWorkspaceAgents, setDisableWorkspaceAgents] = usePersistedState<boolean>(
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<SetStateAction<string>> = useCallback(
(value) => {
const prev = readScopedAiDefault(scopeId, "agentId");
Expand Down Expand Up @@ -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) {
Expand All @@ -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);
Expand All @@ -208,7 +168,6 @@ function AgentProviderWithState(props: {
if (
current.projectPath === projectPath &&
current.workspaceId === workspaceId &&
current.disableWorkspaceAgents === workspaceAgentsDisabled &&
isMountedRef.current
) {
setAgents([]);
Expand All @@ -225,22 +184,22 @@ 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;
if (!isMountedRef.current) return;

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
Expand Down Expand Up @@ -348,8 +307,6 @@ function AgentProviderWithState(props: {
loadFailed,
refresh,
refreshing,
disableWorkspaceAgents,
setDisableWorkspaceAgents,
isAgentSelectionLocked: isCurrentAgentLocked,
}),
[
Expand All @@ -361,8 +318,6 @@ function AgentProviderWithState(props: {
loadFailed,
refresh,
refreshing,
disableWorkspaceAgents,
setDisableWorkspaceAgents,
isCurrentAgentLocked,
]
);
Expand Down
2 changes: 0 additions & 2 deletions src/browser/contexts/ThinkingContext.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,6 @@ const agentContextValue: AgentContextValue = {
loadFailed: false,
refresh: () => Promise.resolve(),
refreshing: false,
disableWorkspaceAgents: false,
setDisableWorkspaceAgents: () => undefined,
};

const ThinkingSetterComponent: React.FC = () => {
Expand Down
3 changes: 0 additions & 3 deletions src/browser/features/ChatInput/CreationControls.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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(),
Expand Down Expand Up @@ -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(),
Expand Down
4 changes: 0 additions & 4 deletions src/browser/features/ChatInput/CreationControls.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -796,7 +794,6 @@ function CreationControlsContent(props: CreationControlsProps) {
}
}}
defaultMode={props.defaultRuntimeMode}
onSetDefault={props.onSetDefaultRuntime}
disabled={props.disabled}
runtimeAvailabilityState={runtimeAvailabilityState}
runtimeEnablement={props.runtimeEnablement}
Expand Down Expand Up @@ -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}
Expand Down
6 changes: 1 addition & 5 deletions src/browser/features/ChatInput/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -668,7 +668,6 @@ const ChatInputInner: React.FC<ChatInputProps> = (props) => {
variant,
workspaceId,
projectPath: atMentionProjectPath,
disableWorkspaceAgents: sendMessageOptions.disableWorkspaceAgents === true,
});
const { agentSkillDescriptors, handleInputCaretChange, mcpPromptDescriptors } =
composerSuggestions;
Expand Down Expand Up @@ -883,7 +882,6 @@ const ChatInputInner: React.FC<ChatInputProps> = (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
Expand Down Expand Up @@ -2238,9 +2236,7 @@ const ChatInputInner: React.FC<ChatInputProps> = (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
Expand Down
9 changes: 2 additions & 7 deletions src/browser/features/ChatInput/useComposerSuggestions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down
Loading
Loading