@@ -534,7 +552,7 @@ function PromptContextSections({ sections }: { sections: PromptSection[] }) {
{section.title}
-
+
{section.body.trim() || "No metadata."}
@@ -590,7 +608,7 @@ function TurnSetupFooter({
{showContext ? (
;
- agentName: string;
compact: boolean;
item: TranscriptItem;
profiles?: UserProfileLookup;
@@ -634,7 +653,9 @@ function TranscriptItemRow({
) : null}
;
@@ -734,6 +759,14 @@ function MessageItem({
profiles,
})
: item.title || "User";
+ const agentProfile = profiles?.[normalizePubkey(agentPubkey)] ?? null;
+ const assistantLabel = resolveUserLabel({
+ pubkey: agentPubkey,
+ fallbackName: agentName,
+ profiles,
+ preferResolvedSelfLabel: true,
+ });
+ const assistantAvatarUrl = agentProfile?.avatarUrl ?? agentAvatarUrl;
return (
@@ -764,14 +797,20 @@ function MessageItem({
)}
>
{isAssistant ? (
-
-
-
+
+
+
+ {assistantLabel}
-
{agentName}
{isActive ? (
@@ -824,7 +863,7 @@ function ThoughtItem({
{item.title}
{isActive ? (
@@ -868,7 +907,7 @@ function MetadataItem({
{item.title}
-
+
{item.sections.length} section{item.sections.length === 1 ? "" : "s"}
@@ -884,7 +923,7 @@ function MetadataItem({
{section.title}
-
+
{section.body.trim() || "No metadata."}
@@ -942,7 +981,7 @@ function TranscriptTimestamp({ timestamp }: { timestamp: string }) {
return (
-
+
{formatted}
diff --git a/desktop/src/features/agents/ui/ManagedAgentSessionPanel.tsx b/desktop/src/features/agents/ui/ManagedAgentSessionPanel.tsx
index 8f67ca870..b43207ce3 100644
--- a/desktop/src/features/agents/ui/ManagedAgentSessionPanel.tsx
+++ b/desktop/src/features/agents/ui/ManagedAgentSessionPanel.tsx
@@ -25,7 +25,9 @@ import { shorten } from "./agentSessionUtils";
import { useObserverEvents, useAgentTranscript } from "./useObserverEvents";
type ManagedAgentSessionPanelProps = {
- agent: Pick;
+ agent: Pick & {
+ avatarUrl?: string | null;
+ };
channelId?: string | null;
className?: string;
compact?: boolean;
@@ -101,7 +103,9 @@ export function ManagedAgentSessionPanel({
) : null}
& {
agentSource: "managed" | "member-bot" | "relay";
+ avatarUrl?: string | null;
canInterruptTurn: boolean;
channelIds?: string[];
channels?: string[];
@@ -53,6 +54,7 @@ export function buildChannelAgentSessionCandidates({
name: agent.name,
status: agent.status,
agentSource: "managed",
+ avatarUrl: agent.avatarUrl,
canInterruptTurn: true,
channelIds: existing?.channelIds,
channels: existing?.channels,
diff --git a/desktop/src/features/channels/ui/ChannelScreen.tsx b/desktop/src/features/channels/ui/ChannelScreen.tsx
index 749e2fa4e..834420555 100644
--- a/desktop/src/features/channels/ui/ChannelScreen.tsx
+++ b/desktop/src/features/channels/ui/ChannelScreen.tsx
@@ -181,25 +181,6 @@ export function ChannelScreen({
: [],
[activeChannel],
);
- const messageProfilePubkeys = React.useMemo(
- () => [
- ...new Set([
- ...messageAuthorPubkeys,
- ...messageMentionPubkeys,
- ...activeDmParticipantPubkeys,
- ...typingEntries.map((entry) => entry.pubkey),
- ]),
- ],
- [
- activeDmParticipantPubkeys,
- messageAuthorPubkeys,
- messageMentionPubkeys,
- typingEntries,
- ],
- );
- const messageProfilesQuery = useUsersBatchQuery(messageProfilePubkeys, {
- enabled: messageProfilePubkeys.length > 0,
- });
const channelMembersQuery = useChannelMembersQuery(activeChannel?.id ?? null);
const channelMembers = channelMembersQuery.data;
const managedAgentsQuery = useManagedAgentsQuery();
@@ -221,6 +202,27 @@ export function ChannelScreen({
}
return pubkeys;
}, [channelMembers, managedAgents, relayAgents]);
+ const messageProfilePubkeys = React.useMemo(
+ () => [
+ ...new Set([
+ ...messageAuthorPubkeys,
+ ...messageMentionPubkeys,
+ ...activeDmParticipantPubkeys,
+ ...agentPubkeys,
+ ...typingEntries.map((entry) => entry.pubkey),
+ ]),
+ ],
+ [
+ activeDmParticipantPubkeys,
+ agentPubkeys,
+ messageAuthorPubkeys,
+ messageMentionPubkeys,
+ typingEntries,
+ ],
+ );
+ const messageProfilesQuery = useUsersBatchQuery(messageProfilePubkeys, {
+ enabled: messageProfilePubkeys.length > 0,
+ });
const allAgentSessionCandidates = React.useMemo(
() =>
buildChannelAgentSessionCandidates({
diff --git a/desktop/src/features/channels/ui/useChannelActivityTyping.ts b/desktop/src/features/channels/ui/useChannelActivityTyping.ts
index 007f090d3..d3943dbb1 100644
--- a/desktop/src/features/channels/ui/useChannelActivityTyping.ts
+++ b/desktop/src/features/channels/ui/useChannelActivityTyping.ts
@@ -98,7 +98,7 @@ export function mergeAgentNamesIntoProfiles(
relayAgents: RelayAgent[],
): UserProfileLookup {
const merged = { ...profiles };
- for (const agent of [...relayAgents, ...managedAgents]) {
+ for (const agent of relayAgents) {
const key = normalizePubkey(agent.pubkey);
merged[key] = {
...merged[key],
@@ -108,5 +108,15 @@ export function mergeAgentNamesIntoProfiles(
isAgent: true,
};
}
+ for (const agent of managedAgents) {
+ const key = normalizePubkey(agent.pubkey);
+ merged[key] = {
+ ...merged[key],
+ displayName: merged[key]?.displayName || agent.name,
+ avatarUrl: merged[key]?.avatarUrl ?? agent.avatarUrl,
+ nip05Handle: merged[key]?.nip05Handle ?? null,
+ isAgent: true,
+ };
+ }
return merged;
}
diff --git a/desktop/src/shared/api/tauri.ts b/desktop/src/shared/api/tauri.ts
index 11e25da4d..0954193e8 100644
--- a/desktop/src/shared/api/tauri.ts
+++ b/desktop/src/shared/api/tauri.ts
@@ -45,10 +45,7 @@ import type {
OpenDmInput,
} from "@/shared/api/types";
-type RawIdentity = {
- pubkey: string;
- display_name: string;
-};
+type RawIdentity = { pubkey: string; display_name: string };
type RawProfile = {
pubkey: string;
@@ -213,6 +210,7 @@ export type RawManagedAgent = {
max_turn_duration_seconds: number | null;
parallelism: number;
system_prompt: string | null;
+ avatar_url?: string | null;
model: string | null;
mcp_toolsets: string | null;
env_vars?: Record;
@@ -868,6 +866,7 @@ export function fromRawManagedAgent(agent: RawManagedAgent): ManagedAgent {
maxTurnDurationSeconds: agent.max_turn_duration_seconds,
parallelism: agent.parallelism,
systemPrompt: agent.system_prompt,
+ avatarUrl: agent.avatar_url ?? null,
model: agent.model,
mcpToolsets: agent.mcp_toolsets,
envVars: agent.env_vars ?? {},
diff --git a/desktop/src/shared/api/types.ts b/desktop/src/shared/api/types.ts
index 653a1be99..99df2caa4 100644
--- a/desktop/src/shared/api/types.ts
+++ b/desktop/src/shared/api/types.ts
@@ -282,6 +282,7 @@ export type ManagedAgent = {
maxTurnDurationSeconds: number | null;
parallelism: number;
systemPrompt: string | null;
+ avatarUrl: string | null;
model: string | null;
mcpToolsets: string | null;
/** Per-agent env vars. Layered on top of persona envVars. */
diff --git a/desktop/src/testing/e2eBridge.ts b/desktop/src/testing/e2eBridge.ts
index f3481d234..8b8e59103 100644
--- a/desktop/src/testing/e2eBridge.ts
+++ b/desktop/src/testing/e2eBridge.ts
@@ -38,6 +38,7 @@ type MockCommandAvailability = {
type MockManagedAgentSeed = {
pubkey: string;
name: string;
+ avatarUrl?: string | null;
personaId?: string | null;
status?: RawManagedAgent["status"];
channelNames?: string[];
@@ -358,6 +359,7 @@ type RawManagedAgent = {
max_turn_duration_seconds: number | null;
parallelism: number;
system_prompt: string | null;
+ avatar_url: string | null;
model: string | null;
env_vars?: Record;
status: "running" | "stopped" | "deployed" | "not_deployed";
@@ -873,6 +875,7 @@ function cloneManagedAgent(agent: MockManagedAgent): RawManagedAgent {
max_turn_duration_seconds: agent.max_turn_duration_seconds ?? null,
parallelism: agent.parallelism,
system_prompt: agent.system_prompt,
+ avatar_url: agent.avatar_url ?? null,
model: agent.model,
env_vars: { ...(agent.env_vars ?? {}) },
status: agent.status,
@@ -962,6 +965,7 @@ function buildSeededManagedAgent(seed: MockManagedAgentSeed): MockManagedAgent {
max_turn_duration_seconds: null,
parallelism: 1,
system_prompt: null,
+ avatar_url: seed.avatarUrl ?? null,
model: null,
env_vars: {},
status,
@@ -4747,6 +4751,7 @@ async function handleCreateManagedAgent(
max_turn_duration_seconds: args.input.maxTurnDurationSeconds ?? null,
parallelism: args.input.parallelism ?? 1,
system_prompt: args.input.systemPrompt?.trim() || null,
+ avatar_url: avatarUrl,
model: args.input.model?.trim() || null,
env_vars: { ...(args.input.envVars ?? {}) },
status: args.input.spawnAfterCreate ? "running" : "stopped",
From 001236cb739e7e4d10370a17b2a9df66c83c2d1b Mon Sep 17 00:00:00 2001
From: Taylor Ho
Date: Sun, 14 Jun 2026 23:46:21 -0700
Subject: [PATCH 08/20] refactor(agents): standardize activity transcript
typography to text-xs
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- Replace arbitrary text-[7px]–text-[11px] sizes with Tailwind text-xs across the agent activity/session surface
- AgentSessionTranscriptList, AgentSessionThreadPanel, AgentSessionToolItem, RawEventRail, and BotActivityBar
- UserAvatar xs/sm size tokens now use text-xs so transcript and activity-bar avatars need no per-call font overrides
---
.../src/features/agents/ui/AgentSessionToolItem.tsx | 8 ++++----
.../features/agents/ui/AgentSessionTranscriptList.tsx | 2 +-
desktop/src/features/agents/ui/RawEventRail.tsx | 2 +-
.../features/channels/ui/AgentSessionThreadPanel.tsx | 9 +++------
desktop/src/features/channels/ui/BotActivityBar.tsx | 10 ++++++----
desktop/src/shared/ui/UserAvatar.tsx | 4 ++--
6 files changed, 17 insertions(+), 18 deletions(-)
diff --git a/desktop/src/features/agents/ui/AgentSessionToolItem.tsx b/desktop/src/features/agents/ui/AgentSessionToolItem.tsx
index 268c1835c..770e6b94a 100644
--- a/desktop/src/features/agents/ui/AgentSessionToolItem.tsx
+++ b/desktop/src/features/agents/ui/AgentSessionToolItem.tsx
@@ -81,7 +81,7 @@ export function ToolItem({
{isActive ? (
@@ -220,7 +220,7 @@ function ToolTimestamp({
return (
-
+
{time}
{duration ? ` · ${duration}` : null}
@@ -282,7 +282,7 @@ function BuzzToolInlineAction({
if (action.onClick) {
return (