Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 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
7 changes: 7 additions & 0 deletions desktop/src/features/agents/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,13 @@ with a TypeScript lookup table or an id comparison in a component.
harnesses always keep the field. Gate: `defaults hides model when optional
harness has empty discovery` (and the failed-discovery counterpart) in
`onboarding-agent-defaults.spec.ts`.
9. **The defaults modal is progressively disclosed.** An unset global config
starts with an explicit harness choice instead of displaying the backend
fallback as though it were saved. The `progressive-defaults` disclosure
preset shows Provider after harness selection, then reveals Model, Effort,
and Advanced only after a provider is configured. Harnesses whose runtime
metadata has no provider field skip that gate. Reveals animate their height
through Motion and become immediate when reduced motion is requested.

## The tests that enforce this

Expand Down
86 changes: 71 additions & 15 deletions desktop/src/features/agents/ui/AgentAiConfigurationMode.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,67 @@
import type * as React from "react";
import { Tabs, TabsList, TabsTrigger } from "@/shared/ui/tabs";
import type { AgentAiConfigurationMode } from "./agentAiConfigurationPolicy";
import { AgentAiDefaultsNotice } from "./AgentAiDefaults";
import type { InheritedDefault } from "./bakedEnvHelpers";

export type { AgentAiConfigurationMode } from "./agentAiConfigurationPolicy";

export function HarnessModelDefaultNotice({
harness,
model,
}: {
harness: string;
model?: string | null;
}) {
return (
<div className="text-sm" data-testid="agent-harness-defaults-notice">
<span className="text-muted-foreground">Model</span>{" "}
<span className="text-foreground">
<dl
className="grid grid-cols-[auto_minmax(0,1fr)] gap-x-3 text-sm"
data-testid="agent-harness-defaults-notice"
>
<dt className="text-muted-foreground">Harness</dt>
<dd className="truncate text-foreground">
{harness || "Not configured"}
</dd>
<dt className="text-muted-foreground">Model</dt>
<dd className="truncate text-foreground">
{model?.trim() || "Harness default"}
</span>
</div>
</dd>
</dl>
);
}

export function AgentCreateAiDefaultsSummary({
canChooseProvider,
harness,
inheritedModel,
inheritedProvider,
isConfigured,
model,
onEditDefaults,
triggerRef,
}: {
canChooseProvider: boolean;
harness: string;
inheritedModel: InheritedDefault;
inheritedProvider: InheritedDefault;
isConfigured: boolean;
model?: string | null;
onEditDefaults: () => void;
triggerRef?: React.Ref<HTMLButtonElement>;
}) {
return canChooseProvider ? (
<AgentAiDefaultsNotice
isConfigured={isConfigured}
onEditDefaults={onEditDefaults}
triggerRef={triggerRef}
explicitModel=""
explicitProvider=""
harness={harness}
inheritedModel={inheritedModel}
inheritedProvider={inheritedProvider}
/>
) : (
<HarnessModelDefaultNotice harness={harness} model={model} />
);
}

Expand All @@ -36,22 +83,31 @@ export function AgentAiConfigurationModeField({
}
value={mode}
>
<TabsList>
<TabsTrigger value="defaults">
<TabsList className="relative isolate grid h-9 w-full grid-cols-2 overflow-hidden rounded-lg bg-muted p-0.5">
<div
aria-hidden="true"
className="absolute bottom-0.5 left-0.5 top-0.5 z-0 rounded-md bg-background shadow-sm transition-transform duration-[250ms] ease-out"
style={{
transform: `translateX(${mode === "custom" ? 100 : 0}%)`,
width: "calc((100% - 4px) / 2)",
}}
/>
<TabsTrigger
className="relative z-10 h-full rounded-md bg-transparent text-xs font-medium shadow-none transition-colors data-[state=active]:bg-transparent data-[state=active]:shadow-none"
value="defaults"
>
{needsProviderSelection
? "Use agent defaults"
: "Use harness defaults"}
</TabsTrigger>
<TabsTrigger value="custom">Customize for this agent</TabsTrigger>
<TabsTrigger
className="relative z-10 h-full rounded-md bg-transparent text-xs font-medium shadow-none transition-colors data-[state=active]:bg-transparent data-[state=active]:shadow-none"
value="custom"
>
Customize for this agent
</TabsTrigger>
</TabsList>
</Tabs>
{mode === "custom" ? (
<p className="text-xs text-muted-foreground">
{needsProviderSelection
? "Provider and model changes apply only to this agent."
: "Model changes apply only to this agent."}
</p>
) : null}
</div>
);
}
36 changes: 36 additions & 0 deletions desktop/src/features/agents/ui/AgentAiDefaults.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,26 +26,62 @@ export function formatAiDefaultsSummary({
}

export function AgentAiDefaultsNotice({
isConfigured = true,
onEditDefaults,
triggerRef,
explicitModel,
explicitProvider,
harness,
inheritedModel,
inheritedProvider,
}: {
isConfigured?: boolean;
onEditDefaults: () => void;
triggerRef?: React.Ref<HTMLButtonElement>;
explicitModel: string;
explicitProvider: string;
harness?: string;
inheritedModel: InheritedDefault;
inheritedProvider: InheritedDefault;
}) {
const provider = explicitProvider.trim() || inheritedProvider.value;
const model = explicitModel.trim() || inheritedModel.value;

if (!isConfigured) {
return (
<div
className="flex items-center justify-between gap-3 rounded-xl border border-border/70 bg-muted/30 px-3 py-2.5"
data-testid="agent-ai-defaults-notice"
>
<p className="text-sm font-medium text-foreground">
Global defaults not set
</p>
<Button
className="shrink-0"
data-testid="set-ai-defaults"
onClick={onEditDefaults}
ref={triggerRef}
size="sm"
type="button"
variant="outline"
>
Set
</Button>
</div>
);
}

return (
<div className="space-y-1" data-testid="agent-ai-defaults-notice">
<dl className="grid grid-cols-[auto_minmax(0,1fr)] gap-x-3 text-sm">
{harness !== undefined ? (
<>
<dt className="text-muted-foreground">Harness</dt>
<dd className="truncate text-foreground">
{harness || "Not configured"}
</dd>
</>
) : null}
<dt className="text-muted-foreground">Provider</dt>
<dd className="truncate text-foreground">
{provider ? providerLabel(provider) : "Not configured"}
Expand Down
Loading
Loading