From b0524412c4c68a5620d32ac5276783bd689a0ba2 Mon Sep 17 00:00:00 2001 From: dotta Date: Wed, 18 Mar 2026 14:20:06 -0500 Subject: [PATCH] Default advanced toggle open when instructions mode is External When the agent instructions tab is in "External" mode, the advanced collapsible section now defaults to open so users don't have to manually expand it to see the mode and path settings. Co-Authored-By: Paperclip Co-Authored-By: Claude Opus 4.6 --- ui/src/pages/AgentDetail.tsx | 130 ++++++++++++++++++++++++++--------- 1 file changed, 99 insertions(+), 31 deletions(-) diff --git a/ui/src/pages/AgentDetail.tsx b/ui/src/pages/AgentDetail.tsx index 7d251cbf..fe4cadff 100644 --- a/ui/src/pages/AgentDetail.tsx +++ b/ui/src/pages/AgentDetail.tsx @@ -72,6 +72,7 @@ import { RunTranscriptView, type TranscriptMode } from "../components/transcript import { isUuidLike, type Agent, + type AgentSkillEntry, type AgentSkillSnapshot, type BudgetPolicySummary, type HeartbeatRun, @@ -82,7 +83,11 @@ import { } from "@paperclipai/shared"; import { redactHomePathUserSegments, redactHomePathUserSegmentsInValue } from "@paperclipai/adapter-utils"; import { agentRouteRef } from "../lib/utils"; -import { applyAgentSkillSnapshot, arraysEqual } from "../lib/agent-skills-state"; +import { + applyAgentSkillSnapshot, + arraysEqual, + isReadOnlyUnmanagedSkillEntry, +} from "../lib/agent-skills-state"; const runStatusIcons: Record = { succeeded: { icon: CheckCircle2, color: "text-green-600 dark:text-green-400" }, @@ -1798,7 +1803,7 @@ function PromptsTab({ )} - + Advanced @@ -2159,8 +2164,11 @@ function AgentSkillsTab({ name: string; description: string | null; detail: string | null; + locationLabel: string | null; + originLabel: string | null; linkTo: string | null; - adapterEntry: AgentSkillSnapshot["entries"][number] | null; + readOnly: boolean; + adapterEntry: AgentSkillEntry | null; }; const queryClient = useQueryClient(); @@ -2242,6 +2250,10 @@ function AgentSkillsTab({ () => new Map((companySkills ?? []).map((skill) => [skill.key, skill])), [companySkills], ); + const companySkillKeys = useMemo( + () => new Set((companySkills ?? []).map((skill) => skill.key)), + [companySkills], + ); const adapterEntryByKey = useMemo( () => new Map((skillSnapshot?.entries ?? []).map((entry) => [entry.key, entry])), [skillSnapshot], @@ -2256,7 +2268,10 @@ function AgentSkillsTab({ name: skill.name, description: skill.description, detail: adapterEntryByKey.get(skill.key)?.detail ?? null, + locationLabel: adapterEntryByKey.get(skill.key)?.locationLabel ?? null, + originLabel: adapterEntryByKey.get(skill.key)?.originLabel ?? null, linkTo: `/skills/${skill.id}`, + readOnly: false, adapterEntry: adapterEntryByKey.get(skill.key) ?? null, })), [adapterEntryByKey, companySkills], @@ -2273,12 +2288,33 @@ function AgentSkillsTab({ name: companySkill?.name ?? entry.key, description: companySkill?.description ?? null, detail: entry.detail ?? null, + locationLabel: entry.locationLabel ?? null, + originLabel: entry.originLabel ?? null, linkTo: companySkill ? `/skills/${companySkill.id}` : null, + readOnly: false, adapterEntry: entry, }; }), [companySkillByKey, skillSnapshot], ); + const unmanagedSkillRows = useMemo( + () => + (skillSnapshot?.entries ?? []) + .filter((entry) => isReadOnlyUnmanagedSkillEntry(entry, companySkillKeys)) + .map((entry) => ({ + id: `external:${entry.key}`, + key: entry.key, + name: entry.runtimeName ?? entry.key, + description: null, + detail: entry.detail ?? null, + locationLabel: entry.locationLabel ?? null, + originLabel: entry.originLabel ?? null, + linkTo: null, + readOnly: true, + adapterEntry: entry, + })), + [companySkillKeys, skillSnapshot], + ); const desiredOnlyMissingSkills = useMemo( () => skillDraft.filter((key) => !companySkillByKey.has(key)), [companySkillByKey, skillDraft], @@ -2348,6 +2384,51 @@ function AgentSkillsTab({ const renderSkillRow = (skill: SkillRow) => { const adapterEntry = skill.adapterEntry ?? adapterEntryByKey.get(skill.key); const required = Boolean(adapterEntry?.required); + const rowClassName = cn( + "flex items-start gap-3 border-b border-border px-3 py-3 text-sm last:border-b-0", + skill.readOnly ? "bg-muted/20" : "hover:bg-accent/20", + ); + const body = ( +
+
+
+ {skill.name} +
+ {skill.linkTo ? ( + + View + + ) : null} +
+ {skill.description && ( + + {skill.description} + + )} + {skill.readOnly && skill.originLabel && ( +

{skill.originLabel}

+ )} + {skill.readOnly && skill.locationLabel && ( +

Location: {skill.locationLabel}

+ )} + {skill.detail && ( +

{skill.detail}

+ )} +
+ ); + + if (skill.readOnly) { + return ( +
+ + {body} +
+ ); + } + const checked = required || skillDraft.includes(skill.key); const disabled = required || skillSnapshot?.mode === "unsupported"; const checkbox = ( @@ -2364,11 +2445,9 @@ function AgentSkillsTab({ className="mt-0.5 disabled:cursor-not-allowed disabled:opacity-60" /> ); + return ( -