diff --git a/ui/src/pages/AgentDetail.tsx b/ui/src/pages/AgentDetail.tsx
index dd434ffb..49c7e2bd 100644
--- a/ui/src/pages/AgentDetail.tsx
+++ b/ui/src/pages/AgentDetail.tsx
@@ -62,7 +62,10 @@ import {
ChevronRight,
ChevronDown,
ArrowLeft,
+ HelpCircle,
} from "lucide-react";
+import { Collapsible, CollapsibleTrigger, CollapsibleContent } from "@/components/ui/collapsible";
+import { TooltipProvider } from "@/components/ui/tooltip";
import { Input } from "@/components/ui/input";
import { AgentIcon, AgentIconPicker } from "../components/AgentIconPicker";
import { RunTranscriptView, type TranscriptMode } from "../components/transcript/RunTranscriptView";
@@ -198,10 +201,10 @@ function scrollToContainerBottom(container: ScrollContainer, behavior: ScrollBeh
container.scrollTo({ top: container.scrollHeight, behavior });
}
-type AgentDetailView = "dashboard" | "prompts" | "configuration" | "skills" | "runs" | "budget";
+type AgentDetailView = "dashboard" | "instructions" | "configuration" | "skills" | "runs" | "budget";
function parseAgentDetailView(value: string | null): AgentDetailView {
- if (value === "prompts") return value;
+ if (value === "instructions" || value === "prompts") return "instructions";
if (value === "configure" || value === "configuration") return "configuration";
if (value === "skills") return "skills";
if (value === "budget") return "budget";
@@ -601,8 +604,8 @@ export function AgentDetail() {
return;
}
const canonicalTab =
- activeView === "prompts"
- ? "prompts"
+ activeView === "instructions"
+ ? "instructions"
: activeView === "configuration"
? "configuration"
: activeView === "skills"
@@ -724,8 +727,8 @@ export function AgentDetail() {
if (urlRunId) {
crumbs.push({ label: "Runs", href: `/agents/${canonicalAgentRef}/runs` });
crumbs.push({ label: `Run ${urlRunId.slice(0, 8)}` });
- } else if (activeView === "prompts") {
- crumbs.push({ label: "Prompts" });
+ } else if (activeView === "instructions") {
+ crumbs.push({ label: "Instructions" });
} else if (activeView === "configuration") {
crumbs.push({ label: "Configuration" });
} else if (activeView === "skills") {
@@ -761,7 +764,7 @@ export function AgentDetail() {
return ;
}
const isPendingApproval = agent.status === "pending_approval";
- const showConfigActionBar = (activeView === "configuration" || activeView === "prompts") && (configDirty || configSaving);
+ const showConfigActionBar = (activeView === "configuration" || activeView === "instructions") && (configDirty || configSaving);
return (
@@ -888,7 +891,7 @@ export function AgentDetail() {
)}
- {activeView === "prompts" && (
+ {activeView === "instructions" && (
Instructions Bundle
- `AGENTS.md` is the entry file. Sibling files like `HEARTBEAT.md`, `SOUL.md`, `TOOLS.md`, and arbitrary custom files live in the same bundle.
+ Configure your agent's behavior with instructions
@@ -1742,81 +1745,12 @@ function PromptsTab({
- {(bundle?.legacyPromptTemplateActive || bundle?.legacyBootstrapPromptTemplateActive) && (
-
- Legacy inline prompt state is still present. `promptTemplate` now appears as a deprecated virtual file entry so it is visible without masquerading as the live `AGENTS.md`.
-
- )}
-
{(bundle?.warnings ?? []).map((warning) => (
{warning}
))}
-
-
- Mode
-
- setBundleDraft((current) => ({
- mode: "managed",
- rootPath: current?.rootPath ?? bundle?.rootPath ?? "",
- entryFile: current?.entryFile ?? bundle?.entryFile ?? "AGENTS.md",
- }))}
- >
- Managed
-
- setBundleDraft((current) => ({
- mode: "external",
- rootPath: current?.rootPath ?? bundle?.rootPath ?? "",
- entryFile: current?.entryFile ?? bundle?.entryFile ?? "AGENTS.md",
- }))}
- >
- External
-
-
-
-
- Entry file
- {
- const nextEntryFile = event.target.value || "AGENTS.md";
- if (selectedOrEntryFile === currentEntryFile) {
- setSelectedFile(nextEntryFile);
- }
- setBundleDraft((current) => ({
- mode: current?.mode ?? bundle?.mode ?? "managed",
- rootPath: current?.rootPath ?? bundle?.rootPath ?? "",
- entryFile: nextEntryFile,
- }));
- }}
- className="font-mono text-sm"
- />
-
-
- Root path
- setBundleDraft((current) => ({
- mode: current?.mode ?? bundle?.mode ?? "managed",
- rootPath: event.target.value,
- entryFile: current?.entryFile ?? bundle?.entryFile ?? "AGENTS.md",
- }))}
- disabled={currentMode === "managed"}
- className="font-mono text-sm"
- placeholder={currentMode === "managed" ? "Managed by Paperclip" : "/absolute/path/to/agent/prompts"}
- />
-
-
@@ -1847,22 +1781,6 @@ function PromptsTab({
className="font-mono text-sm"
/>
-
- {["HEARTBEAT.md", "SOUL.md", "TOOLS.md"].map((filePath) => (
- {
- setSelectedFile(filePath);
- if (!fileOptions.includes(filePath)) setDraft("");
- }}
- >
- {filePath}
-
- ))}
-
{
const file = bundle?.files.find((entry) => entry.path === node.path);
if (!file) return null;
+ if (file.deprecated) {
+ return (
+
+
+
+ virtual file
+
+
+
+ Legacy inline prompt — this deprecated virtual file preserves the old promptTemplate content
+
+
+ );
+ }
return (
-
- {file.deprecated ? "deprecated" : file.isEntryFile ? "entry" : `${file.size}b`}
+
+ {file.isEntryFile ? "entry" : `${file.size}b`}
);
}}
/>
+
+
+
+
+ Advanced
+
+
+
+
+
+ Mode
+
+
+
+
+
+ Managed: Paperclip stores and serves the instructions bundle. External: you provide a path on disk where the instructions live.
+
+
+
+
+ setBundleDraft((current) => ({
+ mode: "managed",
+ rootPath: current?.rootPath ?? bundle?.rootPath ?? "",
+ entryFile: current?.entryFile ?? bundle?.entryFile ?? "AGENTS.md",
+ }))}
+ >
+ Managed
+
+ setBundleDraft((current) => ({
+ mode: "external",
+ rootPath: current?.rootPath ?? bundle?.rootPath ?? "",
+ entryFile: current?.entryFile ?? bundle?.entryFile ?? "AGENTS.md",
+ }))}
+ >
+ External
+
+
+
+
+
+
+
@@ -1937,6 +1983,7 @@ function PromptsTab({
) : isMarkdown(selectedOrEntryFile) ? (
setDraft(value ?? "")}
placeholder="# Agent instructions"