Fix import adapter configuration forms

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
Dotta 2026-03-16 15:41:06 -05:00
parent fed94d18f3
commit 0b76b1aced
2 changed files with 88 additions and 88 deletions

View file

@ -60,6 +60,10 @@ type AgentConfigFormProps = {
onSaveActionChange?: (save: (() => void) | null) => void; onSaveActionChange?: (save: (() => void) | null) => void;
onCancelActionChange?: (cancel: (() => void) | null) => void; onCancelActionChange?: (cancel: (() => void) | null) => void;
hideInlineSave?: boolean; hideInlineSave?: boolean;
showAdapterTypeField?: boolean;
showAdapterTestEnvironmentButton?: boolean;
showCreateRunPolicySection?: boolean;
hideInstructionsFile?: boolean;
/** "cards" renders each section as heading + bordered card (for settings pages). Default: "inline" (border-b dividers). */ /** "cards" renders each section as heading + bordered card (for settings pages). Default: "inline" (border-b dividers). */
sectionLayout?: "inline" | "cards"; sectionLayout?: "inline" | "cards";
} & ( } & (
@ -163,6 +167,10 @@ export function AgentConfigForm(props: AgentConfigFormProps) {
const { mode, adapterModels: externalModels } = props; const { mode, adapterModels: externalModels } = props;
const isCreate = mode === "create"; const isCreate = mode === "create";
const cards = props.sectionLayout === "cards"; const cards = props.sectionLayout === "cards";
const showAdapterTypeField = props.showAdapterTypeField ?? true;
const showAdapterTestEnvironmentButton = props.showAdapterTestEnvironmentButton ?? true;
const showCreateRunPolicySection = props.showCreateRunPolicySection ?? true;
const hideInstructionsFile = props.hideInstructionsFile ?? false;
const { selectedCompanyId } = useCompany(); const { selectedCompanyId } = useCompany();
const queryClient = useQueryClient(); const queryClient = useQueryClient();
@ -285,6 +293,7 @@ export function AgentConfigForm(props: AgentConfigFormProps) {
adapterType === "codex_local" || adapterType === "codex_local" ||
adapterType === "gemini_local" || adapterType === "gemini_local" ||
adapterType === "opencode_local" || adapterType === "opencode_local" ||
adapterType === "pi_local" ||
adapterType === "cursor"; adapterType === "cursor";
const uiAdapter = useMemo(() => getUIAdapter(adapterType), [adapterType]); const uiAdapter = useMemo(() => getUIAdapter(adapterType), [adapterType]);
@ -312,6 +321,7 @@ export function AgentConfigForm(props: AgentConfigFormProps) {
eff: eff as <T>(group: "adapterConfig", field: string, original: T) => T, eff: eff as <T>(group: "adapterConfig", field: string, original: T) => T,
mark: mark as (group: "adapterConfig", field: string, value: unknown) => void, mark: mark as (group: "adapterConfig", field: string, value: unknown) => void,
models, models,
hideInstructionsFile,
}; };
// Section toggle state — advanced always starts collapsed // Section toggle state — advanced always starts collapsed
@ -478,69 +488,73 @@ export function AgentConfigForm(props: AgentConfigFormProps) {
? <h3 className="text-sm font-medium">Adapter</h3> ? <h3 className="text-sm font-medium">Adapter</h3>
: <span className="text-xs font-medium text-muted-foreground">Adapter</span> : <span className="text-xs font-medium text-muted-foreground">Adapter</span>
} }
<Button {showAdapterTestEnvironmentButton && (
type="button" <Button
variant="outline" type="button"
size="sm" variant="outline"
className="h-7 px-2.5 text-xs" size="sm"
onClick={() => testEnvironment.mutate()} className="h-7 px-2.5 text-xs"
disabled={testEnvironment.isPending || !selectedCompanyId} onClick={() => testEnvironment.mutate()}
> disabled={testEnvironment.isPending || !selectedCompanyId}
{testEnvironment.isPending ? "Testing..." : "Test environment"} >
</Button> {testEnvironment.isPending ? "Testing..." : "Test environment"}
</Button>
)}
</div> </div>
<div className={cn(cards ? "border border-border rounded-lg p-4 space-y-3" : "px-4 pb-3 space-y-3")}> <div className={cn(cards ? "border border-border rounded-lg p-4 space-y-3" : "px-4 pb-3 space-y-3")}>
<Field label="Adapter type" hint={help.adapterType}> {showAdapterTypeField && (
<AdapterTypeDropdown <Field label="Adapter type" hint={help.adapterType}>
value={adapterType} <AdapterTypeDropdown
onChange={(t) => { value={adapterType}
if (isCreate) { onChange={(t) => {
// Reset all adapter-specific fields to defaults when switching adapter type if (isCreate) {
const { adapterType: _at, ...defaults } = defaultCreateValues; // Reset all adapter-specific fields to defaults when switching adapter type
const nextValues: CreateConfigValues = { ...defaults, adapterType: t }; const { adapterType: _at, ...defaults } = defaultCreateValues;
if (t === "codex_local") { const nextValues: CreateConfigValues = { ...defaults, adapterType: t };
nextValues.model = DEFAULT_CODEX_LOCAL_MODEL; if (t === "codex_local") {
nextValues.dangerouslyBypassSandbox = nextValues.model = DEFAULT_CODEX_LOCAL_MODEL;
DEFAULT_CODEX_LOCAL_BYPASS_APPROVALS_AND_SANDBOX; nextValues.dangerouslyBypassSandbox =
} else if (t === "gemini_local") { DEFAULT_CODEX_LOCAL_BYPASS_APPROVALS_AND_SANDBOX;
nextValues.model = DEFAULT_GEMINI_LOCAL_MODEL; } else if (t === "gemini_local") {
} else if (t === "cursor") { nextValues.model = DEFAULT_GEMINI_LOCAL_MODEL;
nextValues.model = DEFAULT_CURSOR_LOCAL_MODEL; } else if (t === "cursor") {
} else if (t === "opencode_local") { nextValues.model = DEFAULT_CURSOR_LOCAL_MODEL;
nextValues.model = ""; } else if (t === "opencode_local") {
nextValues.model = "";
}
set!(nextValues);
} else {
// Clear all adapter config and explicitly blank out model + effort/mode keys
// so the old adapter's values don't bleed through via eff()
setOverlay((prev) => ({
...prev,
adapterType: t,
adapterConfig: {
model:
t === "codex_local"
? DEFAULT_CODEX_LOCAL_MODEL
: t === "gemini_local"
? DEFAULT_GEMINI_LOCAL_MODEL
: t === "cursor"
? DEFAULT_CURSOR_LOCAL_MODEL
: "",
effort: "",
modelReasoningEffort: "",
variant: "",
mode: "",
...(t === "codex_local"
? {
dangerouslyBypassApprovalsAndSandbox:
DEFAULT_CODEX_LOCAL_BYPASS_APPROVALS_AND_SANDBOX,
}
: {}),
},
}));
} }
set!(nextValues); }}
} else { />
// Clear all adapter config and explicitly blank out model + effort/mode keys </Field>
// so the old adapter's values don't bleed through via eff() )}
setOverlay((prev) => ({
...prev,
adapterType: t,
adapterConfig: {
model:
t === "codex_local"
? DEFAULT_CODEX_LOCAL_MODEL
: t === "gemini_local"
? DEFAULT_GEMINI_LOCAL_MODEL
: t === "cursor"
? DEFAULT_CURSOR_LOCAL_MODEL
: "",
effort: "",
modelReasoningEffort: "",
variant: "",
mode: "",
...(t === "codex_local"
? {
dangerouslyBypassApprovalsAndSandbox:
DEFAULT_CODEX_LOCAL_BYPASS_APPROVALS_AND_SANDBOX,
}
: {}),
},
}));
}
}}
/>
</Field>
{testEnvironment.error && ( {testEnvironment.error && (
<div className="rounded-md border border-destructive/30 bg-destructive/10 px-3 py-2 text-xs text-destructive"> <div className="rounded-md border border-destructive/30 bg-destructive/10 px-3 py-2 text-xs text-destructive">
@ -634,8 +648,10 @@ export function AgentConfigForm(props: AgentConfigFormProps) {
? "codex" ? "codex"
: adapterType === "gemini_local" : adapterType === "gemini_local"
? "gemini" ? "gemini"
: adapterType === "cursor" : adapterType === "pi_local"
? "agent" ? "pi"
: adapterType === "cursor"
? "agent"
: adapterType === "opencode_local" : adapterType === "opencode_local"
? "opencode" ? "opencode"
: "claude" : "claude"
@ -794,7 +810,7 @@ export function AgentConfigForm(props: AgentConfigFormProps) {
)} )}
{/* ---- Run Policy ---- */} {/* ---- Run Policy ---- */}
{isCreate ? ( {isCreate && showCreateRunPolicySection ? (
<div className={cn(!cards && "border-b border-border")}> <div className={cn(!cards && "border-b border-border")}>
{cards {cards
? <h3 className="text-sm font-medium flex items-center gap-2 mb-3"><Heart className="h-3 w-3" /> Run Policy</h3> ? <h3 className="text-sm font-medium flex items-center gap-2 mb-3"><Heart className="h-3 w-3" /> Run Policy</h3>
@ -815,7 +831,7 @@ export function AgentConfigForm(props: AgentConfigFormProps) {
/> />
</div> </div>
</div> </div>
) : ( ) : !isCreate ? (
<div className={cn(!cards && "border-b border-border")}> <div className={cn(!cards && "border-b border-border")}>
{cards {cards
? <h3 className="text-sm font-medium flex items-center gap-2 mb-3"><Heart className="h-3 w-3" /> Run Policy</h3> ? <h3 className="text-sm font-medium flex items-center gap-2 mb-3"><Heart className="h-3 w-3" /> Run Policy</h3>
@ -881,7 +897,7 @@ export function AgentConfigForm(props: AgentConfigFormProps) {
</CollapsibleSection> </CollapsibleSection>
</div> </div>
</div> </div>
)} ) : null}
</div> </div>
); );

View file

@ -14,6 +14,7 @@ import { queryKeys } from "../lib/queryKeys";
import { MarkdownBody } from "../components/MarkdownBody"; import { MarkdownBody } from "../components/MarkdownBody";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { EmptyState } from "../components/EmptyState"; import { EmptyState } from "../components/EmptyState";
import { AgentConfigForm } from "../components/AgentConfigForm";
import { cn } from "../lib/utils"; import { cn } from "../lib/utils";
import { import {
ArrowRight, ArrowRight,
@ -27,7 +28,6 @@ import {
import { Field, adapterLabels } from "../components/agent-config-primitives"; import { Field, adapterLabels } from "../components/agent-config-primitives";
import { defaultCreateValues } from "../components/agent-config-defaults"; import { defaultCreateValues } from "../components/agent-config-defaults";
import { getUIAdapter, listUIAdapters } from "../adapters"; import { getUIAdapter, listUIAdapters } from "../adapters";
import { ClaudeLocalAdvancedFields } from "../adapters/claude-local/config-fields";
import type { CreateConfigValues } from "@paperclipai/adapter-utils"; import type { CreateConfigValues } from "@paperclipai/adapter-utils";
import { import {
type FileTreeNode, type FileTreeNode,
@ -488,7 +488,6 @@ function AdapterPickerList({
{agents.map((agent) => { {agents.map((agent) => {
const selectedType = adapterOverrides[agent.slug] ?? agent.adapterType; const selectedType = adapterOverrides[agent.slug] ?? agent.adapterType;
const isExpanded = expandedSlugs.has(agent.slug); const isExpanded = expandedSlugs.has(agent.slug);
const uiAdapter = getUIAdapter(selectedType);
const vals = configValues[agent.slug] ?? { ...defaultCreateValues, adapterType: selectedType }; const vals = configValues[agent.slug] ?? { ...defaultCreateValues, adapterType: selectedType };
return ( return (
@ -531,31 +530,16 @@ function AdapterPickerList({
</div> </div>
{isExpanded && ( {isExpanded && (
<div className="border-t border-border bg-accent/10 px-4 py-3 space-y-3"> <div className="border-t border-border bg-accent/10 px-4 py-3 space-y-3">
<uiAdapter.ConfigFields <AgentConfigForm
mode="create" mode="create"
isCreate
adapterType={selectedType}
values={vals} values={vals}
set={(patch) => onChangeConfig(agent.slug, patch)} onChange={(patch) => onChangeConfig(agent.slug, patch)}
config={{}} showAdapterTypeField={false}
eff={() => "" as any} showAdapterTestEnvironmentButton={false}
mark={() => {}} showCreateRunPolicySection={false}
models={[]}
hideInstructionsFile hideInstructionsFile
sectionLayout="cards"
/> />
{selectedType === "claude_local" && (
<ClaudeLocalAdvancedFields
mode="create"
isCreate
adapterType={selectedType}
values={vals}
set={(patch) => onChangeConfig(agent.slug, patch)}
config={{}}
eff={() => "" as any}
mark={() => {}}
models={[]}
/>
)}
</div> </div>
)} )}
</div> </div>