Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.
| Agents have faces, and are never mistaken for people. Every agent wears a little bot face drawn from a look it owns, shape, colour, eyes, mouth, antenna, accessory and pattern, chosen in its builder and on its Profile tab with a live preview, Shuffle and a way back to the face its seed gives it; the face blinks on its own time, breathes, narrows its eyes while the agent works, shuts them asleep and bounces when it finishes, all of it still for anyone who asked for less motion. Wherever an agent shows, in chat, in a list, on a mention, on a review or a commit, its avatar carries an agent marker, and the people reading it are told so. In Chat, direct messages are two lists: People, and Agents, which also holds the agents you haven't talked to yet; a conversation with both a person and an agent in it is marked in the list, named in the conversation's header, spelled out by the composer and explained once the first time it opens. Agents keep their look in the agents service, which every service passes along. The chat and agents guides say so, and CONTRIBUTING makes the shared avatar the only way to draw an agent. | 1 | import type { AgentStatus, MemberProfile } from "@g1t/contracts"; |
| Chat and workspace agents: channels, DMs and named agents you talk to | 2 | |
| Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002) | 3 | import { AgentAvatar } from "../agent-avatar"; |
| One kind of access token; presence and status; usernames keep their case; the tour is a miniature of the real app; icons for password managers | 4 | import { WithPresence } from "../presence"; |
| The site is built from its shared components: one Button with its variants and sizes, one Badge, Card for every panel, Alert for notices, Kbd for keys, Table for the security tables, and Avatar, Command, Progress, ScrollArea, ToggleGroup, Collapsible and Accordion in the kit; raw buttons, hand-drawn cards and pills and per-file button class lists are gone, outline buttons stand the same height as filled ones, and the phone's Ask g1t loses its tile. Every page was compared pixel by pixel before and after. | 5 | |
| Chat and workspace agents: channels, DMs and named agents you talk to | 6 | import { cn } from "../../lib/cn"; |
| The site is built from its shared components: one Button with its variants and sizes, one Badge, Card for every panel, Alert for notices, Kbd for keys, Table for the security tables, and Avatar, Command, Progress, ScrollArea, ToggleGroup, Collapsible and Accordion in the kit; raw buttons, hand-drawn cards and pills and per-file button class lists are gone, outline buttons stand the same height as filled ones, and the phone's Ask g1t loses its tile. Every page was compared pixel by pixel before and after. | 7 | import { Avatar } from "../ui/avatar"; |
| Chat and workspace agents: channels, DMs and named agents you talk to | 8 | |
| 9 | /** | |
| Agents have faces, and are never mistaken for people. Every agent wears a little bot face drawn from a look it owns, shape, colour, eyes, mouth, antenna, accessory and pattern, chosen in its builder and on its Profile tab with a live preview, Shuffle and a way back to the face its seed gives it; the face blinks on its own time, breathes, narrows its eyes while the agent works, shuts them asleep and bounces when it finishes, all of it still for anyone who asked for less motion. Wherever an agent shows, in chat, in a list, on a mention, on a review or a commit, its avatar carries an agent marker, and the people reading it are told so. In Chat, direct messages are two lists: People, and Agents, which also holds the agents you haven't talked to yet; a conversation with both a person and an agent in it is marked in the list, named in the conversation's header, spelled out by the composer and explained once the first time it opens. Agents keep their look in the agents service, which every service passes along. The chat and agents guides say so, and CONTRIBUTING makes the shared avatar the only way to draw an agent. | 10 | * A member's face: a person's avatar, or an agent's picture or bot face |
| 11 | * with the agent marker (components/ui/avatar.tsx). With `presence`, a | |
| 12 | * person's carries their dot (active, away, notifications paused; | |
| 13 | * components/presence.tsx), cut out of `ring`, the colour behind it, which | |
| 14 | * also rings the agent marker. Agents keep their own status, shown apart. | |
| One kind of access token; presence and status; usernames keep their case; the tour is a miniature of the real app; icons for password managers | 15 | */ |
| Chat and workspace agents: channels, DMs and named agents you talk to | 16 | export function MemberAvatar({ |
| 17 | member, | |
| 18 | size = 20, | |
| One kind of access token; presence and status; usernames keep their case; the tour is a miniature of the real app; icons for password managers | 19 | presence = false, |
| 20 | ring, | |
| Chat and workspace agents: channels, DMs and named agents you talk to | 21 | }: { |
| Agents have faces, and are never mistaken for people. Every agent wears a little bot face drawn from a look it owns, shape, colour, eyes, mouth, antenna, accessory and pattern, chosen in its builder and on its Profile tab with a live preview, Shuffle and a way back to the face its seed gives it; the face blinks on its own time, breathes, narrows its eyes while the agent works, shuts them asleep and bounces when it finishes, all of it still for anyone who asked for less motion. Wherever an agent shows, in chat, in a list, on a mention, on a review or a commit, its avatar carries an agent marker, and the people reading it are told so. In Chat, direct messages are two lists: People, and Agents, which also holds the agents you haven't talked to yet; a conversation with both a person and an agent in it is marked in the list, named in the conversation's header, spelled out by the composer and explained once the first time it opens. Agents keep their look in the agents service, which every service passes along. The chat and agents guides say so, and CONTRIBUTING makes the shared avatar the only way to draw an agent. | 22 | member: { kind: "user" | "agent"; id?: string; name: string; avatar: string | null; avatar_seed?: string | null; look?: MemberProfile["look"] }; |
| Chat and workspace agents: channels, DMs and named agents you talk to | 23 | size?: number; |
| One kind of access token; presence and status; usernames keep their case; the tour is a miniature of the real app; icons for password managers | 24 | presence?: boolean; |
| 25 | ring?: string; | |
| Chat and workspace agents: channels, DMs and named agents you talk to | 26 | }) { |
| Agents have faces, and are never mistaken for people. Every agent wears a little bot face drawn from a look it owns, shape, colour, eyes, mouth, antenna, accessory and pattern, chosen in its builder and on its Profile tab with a live preview, Shuffle and a way back to the face its seed gives it; the face blinks on its own time, breathes, narrows its eyes while the agent works, shuts them asleep and bounces when it finishes, all of it still for anyone who asked for less motion. Wherever an agent shows, in chat, in a list, on a mention, on a review or a commit, its avatar carries an agent marker, and the people reading it are told so. In Chat, direct messages are two lists: People, and Agents, which also holds the agents you haven't talked to yet; a conversation with both a person and an agent in it is marked in the list, named in the conversation's header, spelled out by the composer and explained once the first time it opens. Agents keep their look in the agents service, which every service passes along. The chat and agents guides say so, and CONTRIBUTING makes the shared avatar the only way to draw an agent. | 27 | if (member.kind === "agent") return <AgentAvatar agent={member} size={size} ring={ring} />; |
| One kind of access token; presence and status; usernames keep their case; the tour is a miniature of the real app; icons for password managers | 28 | const avatar = <Avatar name={member.name} image={member.avatar} size={size} />; |
| 29 | if (!presence) return avatar; | |
| 30 | return ( | |
| 31 | <WithPresence person={{ id: member.id, username: member.name }} size={size} ring={ring}> | |
| 32 | {avatar} | |
| 33 | </WithPresence> | |
| 34 | ); | |
| Chat and workspace agents: channels, DMs and named agents you talk to | 35 | } |
| 36 | ||
| Agents have faces, and are never mistaken for people. Every agent wears a little bot face drawn from a look it owns, shape, colour, eyes, mouth, antenna, accessory and pattern, chosen in its builder and on its Profile tab with a live preview, Shuffle and a way back to the face its seed gives it; the face blinks on its own time, breathes, narrows its eyes while the agent works, shuts them asleep and bounces when it finishes, all of it still for anyone who asked for less motion. Wherever an agent shows, in chat, in a list, on a mention, on a review or a commit, its avatar carries an agent marker, and the people reading it are told so. In Chat, direct messages are two lists: People, and Agents, which also holds the agents you haven't talked to yet; a conversation with both a person and an agent in it is marked in the list, named in the conversation's header, spelled out by the composer and explained once the first time it opens. Agents keep their look in the agents service, which every service passes along. The chat and agents guides say so, and CONTRIBUTING makes the shared avatar the only way to draw an agent. | 37 | /** |
| 38 | * A person and an agent together, for a mixed direct message's row: the | |
| 39 | * person's face behind, the agent's in front with its marker, so the pair | |
| 40 | * says at a glance that an agent is in the conversation. | |
| 41 | */ | |
| 42 | export function PairAvatar({ | |
| 43 | person, | |
| 44 | agent, | |
| 45 | size = 20, | |
| 46 | ring = "var(--color-bg)", | |
| 47 | }: { | |
| 48 | person: { kind: "user" | "agent"; id?: string; name: string; avatar: string | null }; | |
| 49 | agent: { kind: "user" | "agent"; id?: string; name: string; avatar: string | null; avatar_seed?: string | null; look?: MemberProfile["look"] }; | |
| 50 | size?: number; | |
| 51 | ring?: string; | |
| 52 | }) { | |
| 53 | const each = Math.max(16, Math.round(size * 0.85)); | |
| 54 | const width = Math.round(each * 1.55); | |
| 55 | return ( | |
| 56 | <span className="relative inline-flex shrink-0" style={{ width, height: size + 2 }} aria-hidden="true"> | |
| 57 | <span className="absolute top-0 left-0 inline-flex rounded-full" style={{ boxShadow: `0 0 0 1.5px ${ring}` }}> | |
| 58 | <Avatar name={person.name} image={person.avatar} size={each} /> | |
| 59 | </span> | |
| 60 | <span className="absolute inline-flex rounded-md" style={{ left: width - each, top: size + 2 - each, boxShadow: `0 0 0 1.5px ${ring}` }}> | |
| 61 | <AgentAvatar agent={agent} size={each} ring={ring} /> | |
| 62 | </span> | |
| 63 | </span> | |
| 64 | ); | |
| 65 | } | |
| 66 | ||
| An archived agent can be found, brought back, or deleted for good. The Agents page ends with Archived, folded, where owners see every archived agent and members their own personal ones, each with when and by whom it was archived and what deleting it would take; Restore brings it back, under a new handle when a live agent has taken its old one, and Delete permanently asks for the handle to be typed and then removes its sessions, replies, memories, routines, versions, drafts, skills and its computer's home, leaving its messages in chat under its saved name, the workspace's spend totals and the audit log. In Chat an archived agent is deactivated the way people expect: never offered as someone to message or mention, never woken, a conversation with it kept in the list, dimmed and marked Archived, open to read with a note in place of the composer and a way to restore it for owners; after deletion the conversation still reads, its messages marked as a deleted agent's. The agents, chat and agent budgets guides say so. | 67 | /** |
| 68 | * AGENT, beside an agent's name wherever it speaks; DELETED AGENT, in the | |
| 69 | * quiet colours, beside the name an agent deleted for good spoke under | |
| 70 | * (docs.g1t.sh/guides/chat/, "Archived and deleted agents"). | |
| 71 | */ | |
| 72 | export function AgentPill({ className, deleted = false }: { className?: string; deleted?: boolean }) { | |
| Agents have faces, and are never mistaken for people. Every agent wears a little bot face drawn from a look it owns, shape, colour, eyes, mouth, antenna, accessory and pattern, chosen in its builder and on its Profile tab with a live preview, Shuffle and a way back to the face its seed gives it; the face blinks on its own time, breathes, narrows its eyes while the agent works, shuts them asleep and bounces when it finishes, all of it still for anyone who asked for less motion. Wherever an agent shows, in chat, in a list, on a mention, on a review or a commit, its avatar carries an agent marker, and the people reading it are told so. In Chat, direct messages are two lists: People, and Agents, which also holds the agents you haven't talked to yet; a conversation with both a person and an agent in it is marked in the list, named in the conversation's header, spelled out by the composer and explained once the first time it opens. Agents keep their look in the agents service, which every service passes along. The chat and agents guides say so, and CONTRIBUTING makes the shared avatar the only way to draw an agent. | 73 | // The avatar beside it carries the agent marker; this is the word, for where the name stands alone. |
| An archived agent can be found, brought back, or deleted for good. The Agents page ends with Archived, folded, where owners see every archived agent and members their own personal ones, each with when and by whom it was archived and what deleting it would take; Restore brings it back, under a new handle when a live agent has taken its old one, and Delete permanently asks for the handle to be typed and then removes its sessions, replies, memories, routines, versions, drafts, skills and its computer's home, leaving its messages in chat under its saved name, the workspace's spend totals and the audit log. In Chat an archived agent is deactivated the way people expect: never offered as someone to message or mention, never woken, a conversation with it kept in the list, dimmed and marked Archived, open to read with a note in place of the composer and a way to restore it for owners; after deletion the conversation still reads, its messages marked as a deleted agent's. The agents, chat and agent budgets guides say so. | 74 | return <AgentWord className={cn(deleted && "bg-raised text-muted", className)}>{deleted ? "Deleted agent" : "Agent"}</AgentWord>; |
| Agents have faces, and are never mistaken for people. Every agent wears a little bot face drawn from a look it owns, shape, colour, eyes, mouth, antenna, accessory and pattern, chosen in its builder and on its Profile tab with a live preview, Shuffle and a way back to the face its seed gives it; the face blinks on its own time, breathes, narrows its eyes while the agent works, shuts them asleep and bounces when it finishes, all of it still for anyone who asked for less motion. Wherever an agent shows, in chat, in a list, on a mention, on a review or a commit, its avatar carries an agent marker, and the people reading it are told so. In Chat, direct messages are two lists: People, and Agents, which also holds the agents you haven't talked to yet; a conversation with both a person and an agent in it is marked in the list, named in the conversation's header, spelled out by the composer and explained once the first time it opens. Agents keep their look in the agents service, which every service passes along. The chat and agents guides say so, and CONTRIBUTING makes the shared avatar the only way to draw an agent. | 75 | } |
| 76 | ||
| An archived agent can be found, brought back, or deleted for good. The Agents page ends with Archived, folded, where owners see every archived agent and members their own personal ones, each with when and by whom it was archived and what deleting it would take; Restore brings it back, under a new handle when a live agent has taken its old one, and Delete permanently asks for the handle to be typed and then removes its sessions, replies, memories, routines, versions, drafts, skills and its computer's home, leaving its messages in chat under its saved name, the workspace's spend totals and the audit log. In Chat an archived agent is deactivated the way people expect: never offered as someone to message or mention, never woken, a conversation with it kept in the list, dimmed and marked Archived, open to read with a note in place of the composer and a way to restore it for owners; after deletion the conversation still reads, its messages marked as a deleted agent's. The agents, chat and agent budgets guides say so. | 77 | function AgentWord({ className, children }: { className?: string; children: string }) { |
| Chat and workspace agents: channels, DMs and named agents you talk to | 78 | return ( |
| 79 | <span | |
| 80 | className={cn( | |
| 81 | "inline-flex h-4 shrink-0 items-center rounded-[4px] bg-accent/15 px-1 text-[0.5625rem] font-semibold tracking-[0.06em] text-accent uppercase", | |
| 82 | className, | |
| 83 | )} | |
| 84 | > | |
| An archived agent can be found, brought back, or deleted for good. The Agents page ends with Archived, folded, where owners see every archived agent and members their own personal ones, each with when and by whom it was archived and what deleting it would take; Restore brings it back, under a new handle when a live agent has taken its old one, and Delete permanently asks for the handle to be typed and then removes its sessions, replies, memories, routines, versions, drafts, skills and its computer's home, leaving its messages in chat under its saved name, the workspace's spend totals and the audit log. In Chat an archived agent is deactivated the way people expect: never offered as someone to message or mention, never woken, a conversation with it kept in the list, dimmed and marked Archived, open to read with a note in place of the composer and a way to restore it for owners; after deletion the conversation still reads, its messages marked as a deleted agent's. The agents, chat and agent budgets guides say so. | 85 | {children} |
| Chat and workspace agents: channels, DMs and named agents you talk to | 86 | </span> |
| 87 | ); | |
| 88 | } | |
| 89 | ||
| 90 | const STATUS: Record<AgentStatus, { label: string; dot: string }> = { | |
| 91 | idle: { label: "Idle", dot: "bg-faint/70" }, | |
| 92 | working: { label: "Working", dot: "bg-success shadow-[0_0_0_3px_color-mix(in_srgb,var(--color-success)_18%,transparent)]" }, | |
| 93 | waiting: { label: "Waiting on you", dot: "bg-warn" }, | |
| 94 | out_of_budget: { label: "Out of budget", dot: "bg-danger" }, | |
| 95 | paused: { label: "Paused", dot: "bg-muted" }, | |
| 96 | }; | |
| 97 | ||
| 98 | export function statusLabel(status: AgentStatus): string { | |
| 99 | return STATUS[status]?.label ?? status; | |
| 100 | } | |
| 101 | ||
| 102 | /** An agent's status as a small dot; the label is for screen readers unless shown. */ | |
| 103 | export function StatusDot({ status, className }: { status: AgentStatus; className?: string }) { | |
| 104 | const known = STATUS[status] ?? STATUS.idle; | |
| 105 | return ( | |
| 106 | <span className={cn("inline-block size-2 shrink-0 rounded-full", known.dot, className)}> | |
| 107 | <span className="sr-only">{known.label}</span> | |
| 108 | </span> | |
| 109 | ); | |
| 110 | } |
This file's history is long; its oldest lines are credited to the oldest commit read.