Skip to content
Merged
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
5 changes: 5 additions & 0 deletions .changeset/agent-description-ui.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@truefoundry/trueforge-ui": patch
---

Round-trip agent description through save/load and show it in the library and agent details.
2 changes: 1 addition & 1 deletion packages/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"@assistant-ui/core": "0.2.22",
"@assistant-ui/react": "0.14.27",
"@assistant-ui/store": "0.2.21",
"@truefoundry/assistant-ui-runtime": "0.1.36",
"@truefoundry/assistant-ui-runtime": "0.1.37",
"@truefoundry/trueforge-sdk": "workspace:*",
"@truefoundry/trueforge-ui": "workspace:*",
"monaco-editor": "^0.52.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/trueforge-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@
"@openuidev/react-headless": "^0.9.4",
"@openuidev/react-lang": "^0.2.9",
"@openuidev/react-ui": "^0.13.2",
"@truefoundry/assistant-ui-runtime": "0.1.36",
"@truefoundry/assistant-ui-runtime": "0.1.37",
"@truefoundry/trueforge-sdk": "workspace:*",
"chart.js": "^4.5.1",
"clsx": "^2.1.1",
Expand Down
3 changes: 3 additions & 0 deletions packages/trueforge-ui/src/atoms/AgentOverflowMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export function cloneAgentName(agentName: string): string {

export type AgentOverflowMenuProps = {
agentName: string;
description?: string;
agentSpec?: AgentSpec;
/** Edit / Clone / Delete when composer is enabled. */
canMutate: boolean;
Expand All @@ -37,6 +38,7 @@ type PendingAction = 'clone' | 'delete' | null;

export function AgentOverflowMenu({
agentName,
description,
agentSpec,
canMutate,
canUse = true,
Expand Down Expand Up @@ -72,6 +74,7 @@ export function AgentOverflowMenu({
try {
await builder.saveAgent({
agentName: clonedName,
...(description === undefined ? {} : { description }),
agentSpec,
intent: 'create',
});
Expand Down
19 changes: 17 additions & 2 deletions packages/trueforge-ui/src/atoms/AgentsLibrary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -189,10 +189,14 @@ export function AgentLibraryRow({
const skillsTitle = skillNames.length ? skillNames.join(', ') : `${skillsCount} skills`;
const hasConfiguration = modelLabel != null || skillsCount > 0 || mcpCount > 0;
const hasNoSchedules = scheduleSummary != null && scheduleSummary.count === 0;
const storedDescription = agent.description?.trim() || null;
// Create falls back to name when description is missing; don't echo it under the title.
const description = storedDescription != null && storedDescription !== agent.name ? storedDescription : null;

return (
<TableRow className={hasNoSchedules ? 'group' : undefined}>
<TableCell className="text-text-primary font-medium">
{/* Fixed width so truncate works; 24rem = 1.5× the prior min-w-64 name column. */}
<TableCell className="text-text-primary w-96 max-w-96 font-medium">
{onOpen == null ? (
<span className="block truncate">{agent.name}</span>
) : (
Expand All @@ -205,6 +209,15 @@ export function AgentLibraryRow({
<span className="block truncate">{agent.name}</span>
</button>
)}
{description ? (
<Tooltip
content={description}
className="max-w-sm whitespace-normal text-left"
triggerClassName="block min-w-0 w-full max-w-full"
>
<span className="text-text-secondary block truncate text-xs font-normal">{description}</span>
</Tooltip>
) : null}
</TableCell>
<TableCell>
{hasConfiguration ? (
Expand Down Expand Up @@ -276,6 +289,7 @@ export function AgentLibraryRow({
</PermissionGuard>
<AgentOverflowMenu
agentName={agent.name}
{...(storedDescription != null ? { description: storedDescription } : {})}
{...(spec != null ? { agentSpec: spec } : {})}
canMutate={canMutate}
canUse={canUseAgent}
Expand Down Expand Up @@ -440,6 +454,7 @@ export function AgentsLibrary({ onSelectAgent }: AgentsLibraryProps) {
isCreateAgent: true,
agentId: libraryAgentId(agent),
agentName: agent.name,
...(agent.description === undefined ? {} : { description: agent.description }),
agentSpec,
});
};
Expand Down Expand Up @@ -519,7 +534,7 @@ export function AgentsLibrary({ onSelectAgent }: AgentsLibraryProps) {
<Table>
<TableHeader>
<TableRow className="hover:bg-transparent">
<TableHead>Agent name</TableHead>
<TableHead className="w-96 min-w-96">Agent name</TableHead>
<TableHead>Configuration</TableHead>
{showCreatedByColumn ? <TableHead>Created by</TableHead> : null}
{showSchedulesColumn ? <TableHead className="w-[14rem]">Schedules</TableHead> : null}
Expand Down
11 changes: 10 additions & 1 deletion packages/trueforge-ui/src/atoms/SaveAgentButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,13 +86,15 @@ function SaveAgentButtonContent({
const [open, setOpen] = useState(false);
const [intent, setIntent] = useState<SaveIntent>('create');
const [name, setName] = useState('');
const [description, setDescription] = useState('');
const [draftSpec, setDraftSpec] = useState<AgentSpec | null>(null);
const [saving, setSaving] = useState(false);
const [error, setError] = useState<string | null>(null);

const close = () => {
if (saving) return;
setOpen(false);
setDescription('');
setDraftSpec(null);
setError(null);
};
Expand All @@ -111,6 +113,7 @@ function SaveAgentButtonContent({
const currentName = shell?.mode.status === 'active' ? (shell.mode.agentName ?? shell.mode.agentId ?? '') : '';
setIntent(currentName ? 'update' : 'create');
setName(currentName);
setDescription(currentName && shell?.mode.status === 'active' ? (shell.mode.description ?? '') : '');
setDraftSpec(cloneAgentSpec(latestAgentSpec));
setOpen(true);
};
Expand All @@ -120,11 +123,14 @@ function SaveAgentButtonContent({
if (intent === 'update' && !canManageAgent) return;
const normalizedName = name.trim();
if (!normalizedName || !draftSpec.model.name.trim()) return;
const normalizedDescription = description.trim();
if (!normalizedDescription) return;
setSaving(true);
setError(null);
try {
const result = await builder.saveAgent({
agentName: normalizedName,
...(normalizedDescription ? { description: normalizedDescription } : {}),
Comment thread
harshil-2096 marked this conversation as resolved.
agentSpec: draftSpec,
intent,
sessionId: draftSessionId,
Expand All @@ -133,10 +139,12 @@ function SaveAgentButtonContent({
shell?.bindMutableAgent({
agentId: result.agentId ?? normalizedName,
agentName: normalizedName,
...(normalizedDescription ? { description: normalizedDescription } : {}),
agentSpec: draftSpec,
});
shell?.invalidateAgentsList();
setOpen(false);
setDescription('');
setDraftSpec(null);
} catch (caught) {
setError(getErrorMessage(caught, 'Could not save agent'));
Expand Down Expand Up @@ -180,11 +188,12 @@ function SaveAgentButtonContent({
<SaveAgentForm
intent={intent}
name={name}
description={description}
spec={draftSpec}
saving={saving}
error={error}
onNameChange={setName}
onChange={setDraftSpec}
onDescriptionChange={setDescription}
onCancel={close}
onSave={() => void save()}
/>
Expand Down
33 changes: 19 additions & 14 deletions packages/trueforge-ui/src/atoms/SaveAgentForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,25 @@ import { Button } from './primitives/Button.js';
export type SaveAgentFormProps = {
intent: 'create' | 'update';
name: string;
description: string;
spec: AgentSpec;
saving: boolean;
error: string | null;
onNameChange: (name: string) => void;
onChange: (spec: AgentSpec) => void;
onDescriptionChange: (description: string) => void;
onCancel: () => void;
onSave: () => void;
};

export function SaveAgentForm({
intent,
name,
description,
spec,
saving,
error,
onNameChange,
onDescriptionChange,
onCancel,
onSave,
}: SaveAgentFormProps) {
Expand All @@ -36,6 +39,8 @@ export function SaveAgentForm({
errorRef.current?.scrollIntoView?.({ block: 'nearest', behavior: 'smooth' });
}, [error]);

const canSave = name.trim() !== '' && spec.model.name.trim() !== '' && description.trim() !== '';

return (
<div className="flex min-h-0 w-full flex-1 flex-col">
<div className="min-h-0 flex-1 overflow-y-auto px-5 py-3">
Expand All @@ -50,18 +55,18 @@ export function SaveAgentForm({
/>
</label>

{/* TODO: Uncomment the description field when the backend supports description */}
{/* <label className="mb-3 block">
<span className="mb-1.5 block text-sm font-medium">Description</span>
<textarea
value={spec.description ?? ''}
disabled={saving}
onChange={event => onChange({ ...spec, description: event.target.value })}
rows={4}
placeholder="Describe what this agent does."
className={auiInputClass('resize-y py-2 disabled:opacity-60')}
/>
</label> */}
<label className="mb-3 block">
<span className="mb-1.5 block text-sm font-medium">Description</span>
<textarea
value={description}
disabled={saving}
onChange={event => onDescriptionChange(event.target.value)}
rows={4}
maxLength={1024}
placeholder="Describe what this agent does."
className={auiInputClass('resize-y py-2 disabled:opacity-60')}
/>
</label>

{error ? (
<p
Expand All @@ -78,7 +83,7 @@ export function SaveAgentForm({
<Button.Secondary type="button" disabled={saving} onClick={onCancel}>
Cancel
</Button.Secondary>
<Button.Primary type="button" disabled={saving || !name.trim() || !spec.model.name.trim()} onClick={onSave}>
<Button.Primary type="button" disabled={saving || !canSave} onClick={onSave}>
{saving ? 'Saving…' : 'Save changes'}
</Button.Primary>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export function AgentDetailsHeader({ agentId, detail, onBack }: AgentDetailsHead
isCreateAgent: true,
agentId: detail.agentId,
agentName: detail.name,
...(detail.description === undefined ? {} : { description: detail.description }),
agentSpec: detail.agentSpec,
});
};
Expand Down Expand Up @@ -82,6 +83,7 @@ export function AgentDetailsHeader({ agentId, detail, onBack }: AgentDetailsHead
{detail != null ? (
<AgentOverflowMenu
agentName={detail.name}
{...(detail.description === undefined ? {} : { description: detail.description })}
agentSpec={detail.agentSpec}
canMutate={canMutate}
canUse={canUse}
Expand Down
Loading
Loading