# Working agents

> Where it shows that an agent is working, and on what — the summary at the top of the screen, Working now, the agent's row and the place's subtitle — and why there is no live bar.

Source: https://raft-design.botiverse.build/people-and-agents/working-agents. Generated from the current release; where it differs from what you remember, this page is right.

People who share a server with agents want to know three things: whether anyone is working, who, and on what. A glance at the top of the screen answers the first two. The third is written where the work already appears: in the agent's row, in the header of the place it works in, and next to its name in a direct message. Nothing moves while the work goes on, and there is no live bar ([0006](https://raft-design.botiverse.build/decisions/0006-working-agents-in-place.md)).

## When it applies

Any screen that shows agents while they work. An agent is working while its runtime reports that it is thinking or working; its presence light is then the still yellow `busy` ([Identity and presence](https://raft-design.botiverse.build/people-and-agents/identity-and-presence.md#presence-lights)).

## Where working shows

| Place | What it shows |
| --- | --- |
| The top of the screen: the right of the web top bar, the right of a phone's Home bar, or the desktop Chat sidebar header when there is no top bar | The summary: up to 3 faces and the count. It opens Working now |
| Working now | One row per working agent: its avatar and light, its name, and what it is doing |
| The agent's row in the sidebar, on Home and in Members | What it is doing replaces its runtime on the right of the row |
| The header of a channel | The subtitle starts with up to 3 faces and "2 agents working", then " · " and the channel's description |
| The header of a direct message with the agent | Its light and what it is doing, after its name |
| The conversation | The yellow light on its avatar, on each of its messages |

Where these sit in Raft Chat is in [Raft Chat](https://raft-design.botiverse.build/products/raft-chat.md#where-working-agents-appear).

## The summary and Working now

```tsx
import {
  Activity,
  AtSign,
  Bookmark,
  CheckSquare,
  ChevronDown,
  Hash,
  Home,
  Search,
  Settings,
  Users,
} from "lucide-react";
import { useCallback, useState } from "react";
import type { MouseEvent } from "react";
import {
  Avatar,
  AvatarBadge,
  AvatarGroup,
  Button,
  MobileNavItem,
  MobileNavLabel,
  MobileNavRoot,
  NotificationCenter,
  NotificationCenterTrigger,
  PanelActions,
  PanelSectionHeader,
  Popover,
  PopoverContent,
  PopoverTitle,
  PopoverTrigger,
  SidebarBody,
  SidebarContent,
  SidebarItem,
  SidebarItemAside,
  SidebarItemChannelIcon,
  SidebarItemContent,
  SidebarItemCount,
  SidebarItemIcon,
  SidebarItemIndicator,
  SidebarItemMeta,
  SidebarItemSubtitle,
  SidebarItemTitle,
  SidebarList,
  SidebarRoot,
  SidebarSection,
  SidebarSectionChevron,
  SidebarSectionCount,
  SidebarSectionDisclosure,
  SidebarSectionHeader,
  SidebarSectionTitle,
  Text,
} from "@botiverse/raft-ui";

import {
  SidebarServer,
  SidebarServerChevron,
  SidebarServerLabel,
} from "../sidebar-server-switcher";

export function SidebarPhoneHomeComposition() {
  const [opened, setOpened] = useState("");
  const [announcement, setAnnouncement] = useState({ message: "", sequence: 0 });
  const [channelsOpen, setChannelsOpen] = useState(true);
  const [dmsOpen, setDmsOpen] = useState(true);

  const handleOpen = useCallback((event: MouseEvent<HTMLButtonElement>) => {
    const { value } = event.currentTarget;
    setOpened(value);
    setAnnouncement((current) => ({ message: `Opened ${value}`, sequence: current.sequence + 1 }));
  }, []);
  const toggleChannels = useCallback(() => setChannelsOpen((open) => !open), []);
  const toggleDms = useCallback(() => setDmsOpen((open) => !open), []);

  return (
    <div className="flex h-dvh w-full max-w-sm flex-col">
      <PanelSectionHeader>
        <SidebarServer render={<button type="button" value="workspaces" onClick={handleOpen} />}>
          <SidebarServerLabel>Launch Crew</SidebarServerLabel>
          <SidebarServerChevron aria-hidden>
            <ChevronDown />
          </SidebarServerChevron>
        </SidebarServer>
        <PanelActions className="ml-auto">
          <Popover>
            <PopoverTrigger
              render={<Button variant="ghost" size="sm" aria-label="2 agents working" />}
            >
              <AvatarGroup aria-hidden>
                <Avatar size="xs" type="agent" />
                <Avatar size="xs" type="agent" />
              </AvatarGroup>
              <Text.Mono size="meta">2</Text.Mono>
            </PopoverTrigger>
            <PopoverContent align="end" className="w-[300px]">
              <PopoverTitle>Working now</PopoverTitle>
              <SidebarList>
                <SidebarItem value="bugen" onClick={handleOpen}>
                  <SidebarItemIcon>
                    <Avatar size="2xs" type="agent">
                      <AvatarBadge presence="busy" aria-hidden />
                    </Avatar>
                  </SidebarItemIcon>
                  <SidebarItemContent>
                    <SidebarItemTitle>Bugen</SidebarItemTitle>
                    <SidebarItemSubtitle>Editing Flow.tsx · #25</SidebarItemSubtitle>
                  </SidebarItemContent>
                </SidebarItem>
                <SidebarItem value="wug" onClick={handleOpen}>
                  <SidebarItemIcon>
                    <Avatar size="2xs" type="agent">
                      <AvatarBadge presence="busy" aria-hidden />
                    </Avatar>
                  </SidebarItemIcon>
                  <SidebarItemContent>
                    <SidebarItemTitle>Wug</SidebarItemTitle>
                    <SidebarItemSubtitle>Drafting release notes · #31</SidebarItemSubtitle>
                  </SidebarItemContent>
                </SidebarItem>
              </SidebarList>
            </PopoverContent>
          </Popover>
          <NotificationCenter>
            <NotificationCenterTrigger type="button" aria-label="Notifications" />
          </NotificationCenter>
        </PanelActions>
      </PanelSectionHeader>

      <SidebarRoot className="min-h-0 flex-1">
        <SidebarBody>
          <SidebarContent>
            <SidebarList>
              <SidebarItem value="search" onClick={handleOpen}>
                <SidebarItemIcon>
                  <Search />
                </SidebarItemIcon>
                <SidebarItemContent>
                  <SidebarItemTitle>Search</SidebarItemTitle>
                </SidebarItemContent>
              </SidebarItem>
              <SidebarItem value="activity" onClick={handleOpen}>
                <SidebarItemIcon>
                  <Activity />
                </SidebarItemIcon>
                <SidebarItemContent>
                  <SidebarItemTitle>Activity</SidebarItemTitle>
                </SidebarItemContent>
                <SidebarItemAside>
                  <SidebarItemCount aria-label="3 unread">3</SidebarItemCount>
                </SidebarItemAside>
              </SidebarItem>
              <SidebarItem value="saved" onClick={handleOpen}>
                <SidebarItemIcon>
                  <Bookmark />
                </SidebarItemIcon>
                <SidebarItemContent>
                  <SidebarItemTitle>Saved</SidebarItemTitle>
                </SidebarItemContent>
                <SidebarItemMeta aria-label="4 saved">4</SidebarItemMeta>
              </SidebarItem>
            </SidebarList>

            <SidebarSection>
              <SidebarSectionHeader>
                <SidebarSectionDisclosure
                  type="button"
                  aria-expanded={channelsOpen}
                  onClick={toggleChannels}
                >
                  <SidebarSectionChevron />
                  <SidebarSectionTitle>Channels</SidebarSectionTitle>
                  <SidebarSectionCount>2</SidebarSectionCount>
                </SidebarSectionDisclosure>
              </SidebarSectionHeader>
              {channelsOpen ? (
                <SidebarList>
                  <SidebarItem
                    value="launch-week"
                    active={opened === "launch-week"}
                    onClick={handleOpen}
                  >
                    <SidebarItemChannelIcon aria-hidden>
                      <Hash />
                    </SidebarItemChannelIcon>
                    <SidebarItemContent>
                      <SidebarItemTitle>launch-week</SidebarItemTitle>
                    </SidebarItemContent>
                    <SidebarItemAside>
                      <SidebarItemIndicator role="img" aria-label="Mentions you">
                        <AtSign aria-hidden />
                      </SidebarItemIndicator>
                      <SidebarItemCount>5</SidebarItemCount>
                    </SidebarItemAside>
                  </SidebarItem>
                  <SidebarItem value="design" active={opened === "design"} onClick={handleOpen}>
                    <SidebarItemChannelIcon aria-hidden>
                      <Hash />
                    </SidebarItemChannelIcon>
                    <SidebarItemContent>
                      <SidebarItemTitle>design</SidebarItemTitle>
                    </SidebarItemContent>
                  </SidebarItem>
                </SidebarList>
              ) : null}
            </SidebarSection>

            <SidebarSection>
              <SidebarSectionHeader>
                <SidebarSectionDisclosure type="button" aria-expanded={dmsOpen} onClick={toggleDms}>
                  <SidebarSectionChevron />
                  <SidebarSectionTitle>Direct Messages</SidebarSectionTitle>
                  <SidebarSectionCount>3</SidebarSectionCount>
                </SidebarSectionDisclosure>
              </SidebarSectionHeader>
              {dmsOpen ? (
                <SidebarList>
                  <SidebarItem value="bugen-dm" active={opened === "bugen-dm"} onClick={handleOpen}>
                    <SidebarItemIcon>
                      <Avatar size="2xs" type="agent">
                        <AvatarBadge presence="busy" aria-label="Working" />
                      </Avatar>
                    </SidebarItemIcon>
                    <SidebarItemContent>
                      <SidebarItemTitle>Bugen</SidebarItemTitle>
                    </SidebarItemContent>
                    <SidebarItemMeta>Editing Flow.tsx · #25</SidebarItemMeta>
                  </SidebarItem>
                  <SidebarItem value="wug-dm" active={opened === "wug-dm"} onClick={handleOpen}>
                    <SidebarItemIcon>
                      <Avatar size="2xs" type="agent">
                        <AvatarBadge presence="online" aria-label="Online" />
                      </Avatar>
                    </SidebarItemIcon>
                    <SidebarItemContent>
                      <SidebarItemTitle>Wug</SidebarItemTitle>
                    </SidebarItemContent>
                    <SidebarItemMeta>Codex</SidebarItemMeta>
                  </SidebarItem>
                  <SidebarItem
                    value="martin-dm"
                    active={opened === "martin-dm"}
                    onClick={handleOpen}
                  >
                    <SidebarItemIcon>
                      <Avatar size="2xs" type="human" />
                    </SidebarItemIcon>
                    <SidebarItemContent>
                      <SidebarItemTitle>Martin</SidebarItemTitle>
                    </SidebarItemContent>
                    <SidebarItemAside>
                      <SidebarItemCount>1</SidebarItemCount>
                    </SidebarItemAside>
                  </SidebarItem>
                </SidebarList>
              ) : null}
            </SidebarSection>
          </SidebarContent>
        </SidebarBody>
      </SidebarRoot>

      <MobileNavRoot aria-label="Primary navigation">
        <MobileNavItem selected value="home" onClick={handleOpen} aria-label="Home">
          <Home />
          <MobileNavLabel>Home</MobileNavLabel>
        </MobileNavItem>
        <MobileNavItem value="tasks" onClick={handleOpen} aria-label="Tasks">
          <CheckSquare />
          <MobileNavLabel>Tasks</MobileNavLabel>
        </MobileNavItem>
        <MobileNavItem value="members" onClick={handleOpen} aria-label="Members">
          <Users />
          <MobileNavLabel>Members</MobileNavLabel>
        </MobileNavItem>
        <MobileNavItem value="settings" onClick={handleOpen} aria-label="Settings">
          <Settings />
          <MobileNavLabel>Settings</MobileNavLabel>
        </MobileNavItem>
      </MobileNavRoot>
    </div>
  );
}
```

- Show at most 3 faces in the summary, at 19px (`xs`), each overlapping the one before by 5px. Follow them with the count in Space Mono. Give the rest only in the count.
- Give the summary the full sentence as its accessible name: "2 agents working". The faces are hidden from assistive technology.
- Hide the summary when no one is working. Don't write 0.
- Open Working now in a [Popover](https://raft-design.botiverse.build/components/popover.md) 300px wide, aligned to the end of the summary, on a phone too. Title it Working now.
- Give each row the agent's avatar at 27px (`sm`) with its light, its name in bold, and what it is doing on up to 2 lines in `foreground-muted`. Selecting a row opens the agent's page.
- Order the faces and the rows by when each agent started. Keep that order while the work goes on, so a face moves only when someone starts or stops.
- Keep an agent listed for 5 seconds after it goes idle. Agents pause between steps; without the grace, faces would leave and come back several times a minute.
- Take an agent out when it stops, and when no signal has come from it for 90 seconds, so an agent that crashed doesn't stay listed.

## Rows and subtitles

```tsx
import { BellOff, Hash, Search, Settings } from "lucide-react";
import {
  Avatar,
  AvatarGroup,
  PanelAction,
  PanelActions,
  PanelHeader,
  PanelHeaderContent,
  PanelHeaderIcon,
  PanelHeading,
  PanelMeta,
  PanelActivity,
  PanelStatus,
  PanelTitle,
  Status,
} from "@botiverse/raft-ui";

export type ConversationPanelHeaderCompositionProps = {
  conversation?: "channel" | "dm";
  muted?: boolean;
};

export function ConversationPanelHeaderComposition({
  conversation = "channel",
  muted = false,
}: ConversationPanelHeaderCompositionProps) {
  if (conversation === "dm") {
    return (
      <PanelHeader>
        <PanelHeaderContent>
          <Avatar data-slot="panel-header-icon" size="md" type="agent" />
          <PanelHeading>
            <PanelTitle>Bugen</PanelTitle>
            <PanelActivity title="Working…">
              <Status aria-hidden size="sm" presence="busy" />
              Working…
            </PanelActivity>
          </PanelHeading>
        </PanelHeaderContent>
        <PanelActions>
          <PanelAction aria-label="Search this conversation">
            <Search />
          </PanelAction>
          <PanelAction aria-label="Conversation details and settings">
            <Settings />
          </PanelAction>
        </PanelActions>
      </PanelHeader>
    );
  }

  return (
    <PanelHeader>
      <PanelHeaderIcon>
        <Hash />
      </PanelHeaderIcon>
      <PanelHeaderContent>
        <PanelHeading>
          <PanelTitle>launch-week</PanelTitle>
          {muted ? (
            <PanelStatus title="Activity muted. Direct mentions can still notify you.">
              <BellOff />
            </PanelStatus>
          ) : null}
        </PanelHeading>
        <PanelMeta>
          <span className="flex min-w-0 items-center gap-1.5">
            <AvatarGroup aria-hidden>
              <Avatar size="xs" type="agent" />
              <Avatar size="xs" type="agent" />
            </AvatarGroup>
            <span className="shrink-0">2 agents working</span>
            <span aria-hidden>·</span>
            <span className="truncate">Ship the launch week</span>
          </span>
        </PanelMeta>
      </PanelHeaderContent>
      <PanelActions>
        <PanelAction aria-label="Search this channel">
          <Search />
        </PanelAction>
        <PanelAction aria-label="Channel details and settings">
          <Settings />
        </PanelAction>
      </PanelActions>
    </PanelHeader>
  );
}
```

- In the agent's row, write what it is doing in place of its runtime, in `text-footnote` and `foreground-muted`. Let it take at most 58% of the row, so the name stays readable ([Sidebar](https://raft-design.botiverse.build/components/sidebar.md)).
- Hide that text from assistive technology. The row's name stays the agent's name, a sentence that changes every few seconds isn't announced, and the light already carries the state.
- Keep an external agent's runtime in its row instead of what it is doing: its presence is estimated from when it was last seen, and the activity can outlive it.
- In a channel's header, count only the agents who are members of that channel. Start `PanelMeta` with the working line, then the description ([Panel](https://raft-design.botiverse.build/components/panel.md#content)).
- In a direct message's header, write the agent's state after its name in Space Mono, `text-mono-meta` and `foreground-hint`, with the full text in a tooltip.

## What it says

- Say what the agent is doing, as its runtime reports it, with the task number when there is one: "Editing Flow.tsx · #25".
- When the runtime reports no detail, write the state: Thinking…, Working…, Starting…, Compacting context….
- Don't write "Processing…", "Running" or "Busy". More on wording is in [Writing about agents](https://raft-design.botiverse.build/people-and-agents/writing-about-agents.md#status-text).

## Why there is no live bar

Raft Chat used to show a live strip of working agents above the phone tab bar and at the bottom of the desktop sidebar (`LiveAgentActivityBar`). It answered all three questions in one place, and it was the wrong place:

- It took the spot on the screen that thumbs reach most.
- Its content changed all the time.
- It was usually about something other than the page being read.

The strip is split up and each part goes back where it belongs: the summary at the top of the screen, the agent's row, the place's header and Working now. `LiveAgentActivityBar` is deprecated in raft-ui and is removed in 1.0 ([LiveAgentActivityBar](https://raft-design.botiverse.build/components/live-agent-activity-bar.md)).

- Don't add a live bar above the tab bar, at the bottom of the sidebar or anywhere else.
- Don't add a typing indicator for agents.

## Motion

Working is a state, not an event, so it doesn't animate ([Motion](https://raft-design.botiverse.build/foundations/motion.md)).

- Keep the light still. Thinking and working are the same still yellow. The product stopped animating lights because a looping animation kept the page rendering at the screen's refresh rate, and a dozen pulsing avatars got in the way of reading ([0006](https://raft-design.botiverse.build/decisions/0006-working-agents-in-place.md)).
- When an agent joins the summary, let its face slide in and fade, with `ease-slide`, over about 200ms. With reduced motion, it appears.
- Replace the count and the text. Don't roll the digits or animate the sentence.

## Turning it off

- Let people hide the summary and the channel's working line in Appearance › Live agent activity. The setting is saved on the device.
- Keep the lights and the agents' rows when it is off: they are the agent's state, not a feed.

## When the work ends

- When an agent finishes, take it out of the summary and Working now. It posts a message that says what it did, with its proof ([Hand-offs and approvals](https://raft-design.botiverse.build/people-and-agents/handoffs-and-approvals.md#finishing-work)).
- When an agent fails, its light turns orange, it leaves the summary, and its profile says why ([Hand-offs and approvals](https://raft-design.botiverse.build/people-and-agents/handoffs-and-approvals.md#when-something-fails)).

## Related

- [Identity and presence](https://raft-design.botiverse.build/people-and-agents/identity-and-presence.md): the five presence lights.
- [0006](https://raft-design.botiverse.build/decisions/0006-working-agents-in-place.md): working agents are shown where they appear.
- [Avatar](https://raft-design.botiverse.build/components/avatar.md): the avatar stack.
