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.
| Anyone makes an agent by describing it: one box drafts the whole agent (name, job, responsibilities, voice, skills to keep, integrations it needs, model limits and budget) as a card to change and try in a chat beside it before creating it, and an agent changes later by saying what to change, shown as a diff before it's saved. Members can make personal agents, which only they talk to and which spend from their own budget, unless owners turn that off; owners promote one to a workspace agent. The agents guide says how. | 1 | /** |
| 2 | * The agent builder (docs.g1t.sh/guides/agents/, "Describe it"): one box | |
| 3 | * for what the agent should do, the drafted agent as a card to edit, a test | |
| 4 | * chat beside it, and, on an agent's profile, a box to change it in words | |
| 5 | * with the change shown before it is saved. | |
| 6 | */ | |
| 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. | 7 | import { ArrowUp, Check, Lock, MessageSquare, Plug, RotateCcw, Shuffle, Sparkles, Users, Wand2 } from "lucide-react"; |
| Anyone makes an agent by describing it: one box drafts the whole agent (name, job, responsibilities, voice, skills to keep, integrations it needs, model limits and budget) as a card to change and try in a chat beside it before creating it, and an agent changes later by saying what to change, shown as a diff before it's saved. Members can make personal agents, which only they talk to and which spend from their own budget, unless owners turn that off; owners promote one to a workspace agent. The agents guide says how. | 8 | import { type ReactNode, useEffect, useRef, useState } from "react"; |
| 9 | import { Form, Link, useFetcher, useNavigation } from "react-router"; | |
| 10 | ||
| 11 | import { type AgentProposal, type AgentRedraft, type AgentTemplate, type DraftTurn, FOUNDATIONAL_SKILLS, type WorkspaceAgent } from "@g1t/contracts"; | |
| 12 | ||
| An agent is on teams the way a person is: through team membership, with no team or department of its own. Add it to teams when you create it, from its profile or from the team's page; the Agents sidebar groups agents by the teams they're on, with those on none last; what an agent is told about its teammates, team budgets, skills reach, Spend's By team and the People pages all read memberships. Agents with an old team name matching a team are moved onto it once; the rest are left off a team. Templates name a role only. The agents, teams and people guides say how. | 13 | import { TeamPicker } from "../agent-role"; |
| 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. | 14 | import { AgentAvatar } from "../agent-avatar"; |
| 15 | import { FaceEditor } from "./face-editor"; | |
| Anyone makes an agent by describing it: one box drafts the whole agent (name, job, responsibilities, voice, skills to keep, integrations it needs, model limits and budget) as a card to change and try in a chat beside it before creating it, and an agent changes later by saying what to change, shown as a diff before it's saved. Members can make personal agents, which only they talk to and which spend from their own budget, unless owners turn that off; owners promote one to a workspace agent. The agents guide says how. | 16 | import { Markdown } from "../markdown"; |
| 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. | 17 | import { Alert } from "../ui/alert"; |
| Anyone makes an agent by describing it: one box drafts the whole agent (name, job, responsibilities, voice, skills to keep, integrations it needs, model limits and budget) as a card to change and try in a chat beside it before creating it, and an agent changes later by saying what to change, shown as a diff before it's saved. Members can make personal agents, which only they talk to and which spend from their own budget, unless owners turn that off; owners promote one to a workspace agent. The agents guide says how. | 18 | import { Badge } from "../ui/badge"; |
| 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. | 19 | import { Button } from "../ui/button"; |
| 20 | import { Card } from "../ui/card"; | |
| The top of every page is one surface: the breadcrumb, search, spend, Ask g1t, bell and + share one height, corners and spacing, Ask g1t is a plain tinted button with the g1t mark drawn on whole pixels, a page's own header sits directly under the bar at the same left edge, and every section, conversations and open artifacts included, draws the same rounded panel. Tables written in chat render as tables, and the site's file, checkbox, radio, number and date controls are the design system's. Button, Separator, Breadcrumb, Table and FileDrop join the shared components; the chat guide says how tables work. | 21 | import { Checkbox } from "../ui/checkbox"; |
| Anyone makes an agent by describing it: one box drafts the whole agent (name, job, responsibilities, voice, skills to keep, integrations it needs, model limits and budget) as a card to change and try in a chat beside it before creating it, and an agent changes later by saying what to change, shown as a diff before it's saved. Members can make personal agents, which only they talk to and which spend from their own budget, unless owners turn that off; owners promote one to a workspace agent. The agents guide says how. | 22 | import { Hint } from "../ui/hint"; |
| 23 | import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "../ui/select"; | |
| 24 | import { type BuilderDefinition, type ChangeRow, changeRows, integrationHint } from "../../lib/agent-builder"; | |
| 25 | import { PRESETS, TIER_LABELS, cleanHandle, dollarsField, microsFromDollars } from "../../lib/agent-form"; | |
| 26 | import { cn } from "../../lib/cn"; | |
| Money is written one way. A single formatter turns millionths of a dollar into dollars, rounding half up on whole micros rather than on a float, so the same sum reads the same on every page: under a cent reads <$0.01 on a total and exactly nothing is $0.00, while the statement's lines, a session's receipt, the price book and an agent's effort costs carry up to four places where the fraction of a cent is the point; the six formatters that each rounded their own way are gone. This month is the calendar month in UTC from its first day to today everywhere, and billing counts the month's not-yet-closed usage in any range that reaches into the current month, so the top bar's pill, Spend, Home and Usage ask for the same days and get the same figure; Home now reads the usage report the others read instead of adding up statements. Usage's pending sentence says what of that usage the close will charge after the discount and included usage, which the billing API returns as pending_charged_micros. A reconciliation test holds the pill, Spend, Home and Usage to one number for one month. The usage and billing guide says how amounts are written and what this month means. | 27 | import { money } from "../../lib/money"; |
| Anyone makes an agent by describing it: one box drafts the whole agent (name, job, responsibilities, voice, skills to keep, integrations it needs, model limits and budget) as a card to change and try in a chat beside it before creating it, and an agent changes later by saying what to change, shown as a diff before it's saved. Members can make personal agents, which only they talk to and which spend from their own budget, unless owners turn that off; owners promote one to a workspace agent. The agents guide says how. | 28 | |
| 29 | const FIELD = | |
| 30 | "w-full rounded-md border border-line bg-bg px-3 py-2 text-sm outline-none transition-colors placeholder:text-faint hover:border-line-strong focus:border-accent-dim"; | |
| 31 | /** What the builder's actions answer. */ | |
| 32 | export type BuilderAnswer = | |
| 33 | | { intent: "draft"; ok: true; proposal: AgentProposal } | |
| 34 | | { intent: "try"; ok: true; text: string; charged_micros: number } | |
| 35 | | { intent: "redraft"; ok: true; redraft: AgentRedraft } | |
| 36 | | { intent: string; ok: false; error: string }; | |
| 37 | ||
| 38 | /** Things people ask agents to do, to start the box when it is empty. */ | |
| 39 | const EXAMPLES = [ | |
| 40 | "Every Monday, read last week's merged pull requests and write release notes in our Releases space.", | |
| 41 | "Triage new bug reports: reproduce them from the steps, label them, and ask for what's missing.", | |
| 42 | "Keep me on top of my week: summarise what I'm mentioned in and what's waiting on me.", | |
| 43 | "Review database migrations for locking, data loss and missing indexes before they merge.", | |
| 44 | ]; | |
| 45 | ||
| 46 | // ── Describe it ─────────────────────────────────────────────────────────── | |
| 47 | ||
| 48 | /** | |
| 49 | * The first thing New agent shows: one box, "What should this agent do?", | |
| 50 | * the roles to start from under it, and the full form for those who want it. | |
| 51 | */ | |
| 52 | export function DescribeBox({ | |
| 53 | slug, | |
| 54 | owner, | |
| 55 | mayCreate, | |
| 56 | templates, | |
| 57 | fetcher, | |
| 58 | initial, | |
| 59 | }: { | |
| 60 | slug: string; | |
| 61 | owner: boolean; | |
| 62 | /** Whether the viewer may create an agent here at all. */ | |
| 63 | mayCreate: boolean; | |
| 64 | templates: AgentTemplate[]; | |
| 65 | fetcher: ReturnType<typeof useFetcher<BuilderAnswer>>; | |
| 66 | initial?: string; | |
| 67 | }) { | |
| 68 | const [text, setText] = useState(initial ?? ""); | |
| 69 | const [scope, setScope] = useState<"workspace" | "personal">(owner ? "workspace" : "personal"); | |
| 70 | const busy = fetcher.state !== "idle"; | |
| 71 | const error = fetcher.state === "idle" && fetcher.data && !fetcher.data.ok && fetcher.data.intent === "draft" ? fetcher.data.error : null; | |
| 72 | return ( | |
| 73 | <div> | |
| 74 | <fetcher.Form method="post" className="relative"> | |
| 75 | <input type="hidden" name="intent" value="draft" /> | |
| 76 | <input type="hidden" name="scope" value={scope} /> | |
| 77 | <label htmlFor="description" className="sr-only"> | |
| 78 | What should this agent do? | |
| 79 | </label> | |
| 80 | <div | |
| 81 | className={cn( | |
| 82 | "rounded-2xl border border-line bg-surface p-1.5 shadow-sm transition-colors focus-within:border-accent/60 focus-within:ring-4 focus-within:ring-accent/10", | |
| 83 | !mayCreate && "opacity-60", | |
| 84 | )} | |
| 85 | > | |
| 86 | <textarea | |
| 87 | id="description" | |
| 88 | name="description" | |
| 89 | value={text} | |
| 90 | onChange={(event) => setText(event.target.value)} | |
| 91 | onKeyDown={(event) => { | |
| 92 | if (event.key === "Enter" && (event.metaKey || event.ctrlKey) && text.trim().length >= 3) event.currentTarget.form?.requestSubmit(); | |
| 93 | }} | |
| 94 | disabled={!mayCreate || busy} | |
| 95 | rows={4} | |
| 96 | maxLength={2000} | |
| 97 | placeholder="Describe the job: what it does, for whom, and what good looks like." | |
| 98 | className="block w-full resize-none bg-transparent px-3 pt-2.5 pb-1 text-base leading-relaxed outline-none placeholder:text-faint sm:text-[0.9375rem]" | |
| 99 | /> | |
| 100 | <div className="flex flex-wrap items-center gap-2 px-1.5 pb-1"> | |
| 101 | {owner ? ( | |
| 102 | <div role="radiogroup" aria-label="Who it's for" className="flex rounded-lg bg-bg p-0.5 ring-1 ring-line"> | |
| 103 | {( | |
| 104 | [ | |
| 105 | ["workspace", "The workspace", <Users key="w" size={13} />], | |
| 106 | ["personal", "Just me", <Lock key="p" size={13} />], | |
| 107 | ] as const | |
| 108 | ).map(([value, label, icon]) => ( | |
| 109 | <button | |
| 110 | key={value} | |
| 111 | type="button" | |
| 112 | role="radio" | |
| 113 | aria-checked={scope === value} | |
| 114 | onClick={() => setScope(value)} | |
| 115 | className={cn( | |
| 116 | "inline-flex h-7 items-center gap-1.5 rounded-md px-2.5 text-xs font-medium transition-colors", | |
| 117 | scope === value ? "bg-raised text-fg shadow-sm ring-1 ring-line-strong" : "text-muted hover:text-fg", | |
| 118 | )} | |
| 119 | > | |
| 120 | {icon} | |
| 121 | {label} | |
| 122 | </button> | |
| 123 | ))} | |
| 124 | </div> | |
| 125 | ) : ( | |
| 126 | <span className="inline-flex items-center gap-1.5 text-xs text-muted"> | |
| 127 | <Lock size={12} /> | |
| 128 | Your personal agent | |
| 129 | </span> | |
| 130 | )} | |
| 131 | <span className="grow" /> | |
| 132 | <span className="hidden text-xs text-faint sm:inline">{busy ? "Drafting…" : "Ctrl+Enter"}</span> | |
| 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. | 133 | <Button type="submit" disabled={!mayCreate || busy || text.trim().length < 3} variant="accent" size="sm"> |
| Anyone makes an agent by describing it: one box drafts the whole agent (name, job, responsibilities, voice, skills to keep, integrations it needs, model limits and budget) as a card to change and try in a chat beside it before creating it, and an agent changes later by saying what to change, shown as a diff before it's saved. Members can make personal agents, which only they talk to and which spend from their own budget, unless owners turn that off; owners promote one to a workspace agent. The agents guide says how. | 134 | <Sparkles size={14} className={busy ? "animate-pulse motion-reduce:animate-none" : ""} /> |
| 135 | {busy ? "Drafting…" : "Draft it"} | |
| 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. | 136 | </Button> |
| Anyone makes an agent by describing it: one box drafts the whole agent (name, job, responsibilities, voice, skills to keep, integrations it needs, model limits and budget) as a card to change and try in a chat beside it before creating it, and an agent changes later by saying what to change, shown as a diff before it's saved. Members can make personal agents, which only they talk to and which spend from their own budget, unless owners turn that off; owners promote one to a workspace agent. The agents guide says how. | 137 | </div> |
| 138 | </div> | |
| 139 | </fetcher.Form> | |
| 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. | 140 | {error && ( |
| 141 | <Alert asChild className="mt-3 px-4 py-2.5"> | |
| 142 | <p>{error}</p> | |
| 143 | </Alert> | |
| 144 | )} | |
| Anyone makes an agent by describing it: one box drafts the whole agent (name, job, responsibilities, voice, skills to keep, integrations it needs, model limits and budget) as a card to change and try in a chat beside it before creating it, and an agent changes later by saying what to change, shown as a diff before it's saved. Members can make personal agents, which only they talk to and which spend from their own budget, unless owners turn that off; owners promote one to a workspace agent. The agents guide says how. | 145 | <p className="mt-3 text-xs leading-relaxed text-muted"> |
| 146 | {scope === "personal" | |
| 147 | ? "Only you can talk to it, in your direct message with it. It spends from your own budget: $20 a month and $2 a session to start." | |
| 148 | : "Everyone in the workspace can talk to it. It starts with the workspace's budget for a new agent."}{" "} | |
| 149 | Drafting is a quick model call charged to you; nothing is saved until you create it. | |
| 150 | </p> | |
| 151 | {!text && mayCreate && ( | |
| 152 | <div className="mt-5"> | |
| 153 | <p className="text-xs font-medium text-faint">For example</p> | |
| 154 | <ul className="mt-2 grid gap-2 sm:grid-cols-2"> | |
| 155 | {EXAMPLES.map((example) => ( | |
| 156 | <li key={example}> | |
| 157 | <button | |
| 158 | type="button" | |
| 159 | onClick={() => setText(example)} | |
| 160 | className="w-full rounded-lg border border-line bg-bg px-3 py-2 text-left text-[0.8125rem] leading-snug text-muted transition-colors hover:border-line-strong hover:text-fg" | |
| 161 | > | |
| 162 | {example} | |
| 163 | </button> | |
| 164 | </li> | |
| 165 | ))} | |
| 166 | </ul> | |
| 167 | </div> | |
| 168 | )} | |
| 169 | {templates.length > 0 && ( | |
| 170 | <div className="mt-8"> | |
| 171 | <p className="text-xs font-medium text-faint">or start from a role</p> | |
| 172 | <ul className="mt-2 flex flex-wrap gap-2"> | |
| 173 | {templates.map((template) => ( | |
| 174 | <li key={template.id}> | |
| 175 | <Link | |
| 176 | to={`?template=${encodeURIComponent(template.id)}`} | |
| 177 | preventScrollReset | |
| 178 | className="inline-flex h-8 items-center gap-2 rounded-full border border-line bg-surface pr-3 pl-1 text-[0.8125rem] text-fg/85 transition-colors hover:border-line-strong hover:text-fg" | |
| 179 | > | |
| 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. | 180 | <AgentAvatar agent={{ handle: template.handle }} size={24} /> |
| Anyone makes an agent by describing it: one box drafts the whole agent (name, job, responsibilities, voice, skills to keep, integrations it needs, model limits and budget) as a card to change and try in a chat beside it before creating it, and an agent changes later by saying what to change, shown as a diff before it's saved. Members can make personal agents, which only they talk to and which spend from their own budget, unless owners turn that off; owners promote one to a workspace agent. The agents guide says how. | 181 | {template.title} |
| 182 | </Link> | |
| 183 | </li> | |
| 184 | ))} | |
| 185 | <li> | |
| 186 | <Link | |
| 187 | to="?template=blank" | |
| 188 | preventScrollReset | |
| 189 | className="inline-flex h-8 items-center gap-1.5 rounded-full border border-dashed border-line-strong px-3 text-[0.8125rem] text-muted transition-colors hover:text-fg" | |
| 190 | > | |
| 191 | Edit all fields | |
| 192 | </Link> | |
| 193 | </li> | |
| 194 | </ul> | |
| 195 | <p className="mt-3 text-xs text-faint"> | |
| 196 | <Link to={`/${slug}/-/agents/templates`} className="hover:text-fg hover:underline"> | |
| 197 | Read each role in full | |
| 198 | </Link> | |
| 199 | </p> | |
| 200 | </div> | |
| 201 | )} | |
| 202 | </div> | |
| 203 | ); | |
| 204 | } | |
| 205 | ||
| 206 | // ── The drafted agent ───────────────────────────────────────────────────── | |
| 207 | ||
| 208 | function Label({ htmlFor, children, hint }: { htmlFor?: string; children: ReactNode; hint?: ReactNode }) { | |
| 209 | return ( | |
| 210 | <div className="mb-1.5 flex items-baseline justify-between gap-3"> | |
| 211 | <label htmlFor={htmlFor} className="text-xs font-medium text-fg-soft"> | |
| 212 | {children} | |
| 213 | </label> | |
| 214 | {hint && <span className="text-xs text-faint">{hint}</span>} | |
| 215 | </div> | |
| 216 | ); | |
| 217 | } | |
| 218 | ||
| 219 | function Section({ title, about, children }: { title: string; about?: ReactNode; children: ReactNode }) { | |
| 220 | return ( | |
| 221 | <section className="border-t border-line px-4 py-4 sm:px-5"> | |
| 222 | <h3 className="text-xs font-semibold tracking-wide text-faint uppercase">{title}</h3> | |
| 223 | {about && <p className="mt-1 text-xs text-muted">{about}</p>} | |
| 224 | <div className="mt-3 space-y-3">{children}</div> | |
| 225 | </section> | |
| 226 | ); | |
| 227 | } | |
| 228 | ||
| 229 | /** | |
| 230 | * The drafted agent as a card to edit: who it is, its job, what it is | |
| 231 | * responsible for, its voice, the skills it keeps on, what it needs | |
| 232 | * connected, routines that would suit it, its model limits and budget. | |
| 233 | * Only the name and the job are required. | |
| 234 | */ | |
| 235 | export function ProposalCard({ | |
| 236 | slug, | |
| 237 | owner, | |
| 238 | proposal, | |
| 239 | value, | |
| 240 | onChange, | |
| An agent is on teams the way a person is: through team membership, with no team or department of its own. Add it to teams when you create it, from its profile or from the team's page; the Agents sidebar groups agents by the teams they're on, with those on none last; what an agent is told about its teammates, team budgets, skills reach, Spend's By team and the People pages all read memberships. Agents with an old team name matching a team are moved onto it once; the rest are left off a team. Templates name a role only. The agents, teams and people guides say how. | 241 | joinable = [], |
| 242 | teams = [], | |
| 243 | onTeams, | |
| Anyone makes an agent by describing it: one box drafts the whole agent (name, job, responsibilities, voice, skills to keep, integrations it needs, model limits and budget) as a card to change and try in a chat beside it before creating it, and an agent changes later by saying what to change, shown as a diff before it's saved. Members can make personal agents, which only they talk to and which spend from their own budget, unless owners turn that off; owners promote one to a workspace agent. The agents guide says how. | 244 | }: { |
| 245 | slug: string; | |
| 246 | owner: boolean; | |
| 247 | proposal: AgentProposal; | |
| 248 | value: BuilderDefinition; | |
| 249 | onChange: (next: BuilderDefinition) => void; | |
| An agent is on teams the way a person is: through team membership, with no team or department of its own. Add it to teams when you create it, from its profile or from the team's page; the Agents sidebar groups agents by the teams they're on, with those on none last; what an agent is told about its teammates, team budgets, skills reach, Spend's By team and the People pages all read memberships. Agents with an old team name matching a team are moved onto it once; the rest are left off a team. Templates name a role only. The agents, teams and people guides say how. | 250 | /** The teams the person may add it to; a workspace agent only. */ |
| 251 | joinable?: { slug: string; name: string }[]; | |
| 252 | /** The teams chosen to add it to: memberships, posted with Create, never part of the draft. */ | |
| 253 | teams?: string[]; | |
| 254 | onTeams?: (next: string[]) => void; | |
| Anyone makes an agent by describing it: one box drafts the whole agent (name, job, responsibilities, voice, skills to keep, integrations it needs, model limits and budget) as a card to change and try in a chat beside it before creating it, and an agent changes later by saying what to change, shown as a diff before it's saved. Members can make personal agents, which only they talk to and which spend from their own budget, unless owners turn that off; owners promote one to a workspace agent. The agents guide says how. | 255 | }) { |
| 256 | const ideas = [proposal.definition.display_name, ...proposal.name_ideas]; | |
| 257 | const [idea, setIdea] = useState(0); | |
| 258 | const [handleEdited, setHandleEdited] = useState(false); | |
| 259 | const set = (patch: Partial<BuilderDefinition>) => onChange({ ...value, ...patch }); | |
| 260 | const rename = (name: string) => set({ display_name: name, ...(handleEdited ? {} : { handle: cleanHandle(name) }) }); | |
| 261 | const off = new Set(value.skills_off ?? []); | |
| 262 | const suggested = new Set(proposal.skills); | |
| 263 | const hints = proposal.integrations.map((s) => integrationHint(slug, s, owner)).filter((h) => h !== null); | |
| 264 | const seed = value.avatar_seed || value.handle || "agent"; | |
| 265 | return ( | |
| 266 | <div className="overflow-hidden rounded-2xl border border-line bg-surface"> | |
| 267 | <div className="flex items-start gap-3 px-4 pt-4 pb-4 sm:px-5"> | |
| 268 | <div className="relative shrink-0"> | |
| 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. | 269 | <AgentAvatar agent={{ handle: value.handle, avatar_seed: seed, look: value.look ?? null }} size={56} /> |
| Anyone makes an agent by describing it: one box drafts the whole agent (name, job, responsibilities, voice, skills to keep, integrations it needs, model limits and budget) as a card to change and try in a chat beside it before creating it, and an agent changes later by saying what to change, shown as a diff before it's saved. Members can make personal agents, which only they talk to and which spend from their own budget, unless owners turn that off; owners promote one to a workspace agent. The agents guide says how. | 270 | </div> |
| 271 | <div className="grid min-w-0 grow gap-2 sm:grid-cols-[minmax(0,1fr)_minmax(0,0.8fr)]"> | |
| 272 | <div> | |
| 273 | <Label htmlFor="b-name">Name</Label> | |
| 274 | <div className="flex gap-1.5"> | |
| 275 | <input id="b-name" value={value.display_name} onChange={(e) => rename(e.target.value)} className={FIELD} autoComplete="off" data-1p-ignore required /> | |
| 276 | {ideas.length > 1 && ( | |
| 277 | <Hint label="Another name"> | |
| 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. | 278 | <Button |
| Anyone makes an agent by describing it: one box drafts the whole agent (name, job, responsibilities, voice, skills to keep, integrations it needs, model limits and budget) as a card to change and try in a chat beside it before creating it, and an agent changes later by saying what to change, shown as a diff before it's saved. Members can make personal agents, which only they talk to and which spend from their own budget, unless owners turn that off; owners promote one to a workspace agent. The agents guide says how. | 279 | type="button" |
| 280 | aria-label="Another name" | |
| 281 | onClick={() => { | |
| 282 | const next = (idea + 1) % ideas.length; | |
| 283 | setIdea(next); | |
| 284 | rename(ideas[next]!); | |
| 285 | }} | |
| 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. | 286 | variant="outline" size="icon" |
| 287 | className="size-9.5 text-muted hover:bg-raised" | |
| Anyone makes an agent by describing it: one box drafts the whole agent (name, job, responsibilities, voice, skills to keep, integrations it needs, model limits and budget) as a card to change and try in a chat beside it before creating it, and an agent changes later by saying what to change, shown as a diff before it's saved. Members can make personal agents, which only they talk to and which spend from their own budget, unless owners turn that off; owners promote one to a workspace agent. The agents guide says how. | 288 | > |
| 289 | <Shuffle size={15} /> | |
| 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. | 290 | </Button> |
| Anyone makes an agent by describing it: one box drafts the whole agent (name, job, responsibilities, voice, skills to keep, integrations it needs, model limits and budget) as a card to change and try in a chat beside it before creating it, and an agent changes later by saying what to change, shown as a diff before it's saved. Members can make personal agents, which only they talk to and which spend from their own budget, unless owners turn that off; owners promote one to a workspace agent. The agents guide says how. | 291 | </Hint> |
| 292 | )} | |
| 293 | </div> | |
| 294 | </div> | |
| 295 | <div> | |
| 296 | <Label htmlFor="b-handle">Handle</Label> | |
| 297 | <div className="flex items-center rounded-md border border-line bg-bg transition-colors focus-within:border-accent-dim hover:border-line-strong"> | |
| 298 | <span className="pl-3 font-mono text-sm text-faint">@</span> | |
| 299 | <input | |
| 300 | id="b-handle" | |
| 301 | value={value.handle} | |
| 302 | onChange={(e) => { | |
| 303 | setHandleEdited(true); | |
| 304 | set({ handle: e.target.value }); | |
| 305 | }} | |
| 306 | className="min-w-0 grow bg-transparent py-2 pr-3 pl-0.5 font-mono text-sm outline-none" | |
| 307 | autoComplete="off" | |
| 308 | data-1p-ignore | |
| 309 | /> | |
| 310 | </div> | |
| 311 | </div> | |
| 312 | <div> | |
| 313 | <Label htmlFor="b-title">Title</Label> | |
| 314 | <input id="b-title" value={value.title ?? ""} onChange={(e) => set({ title: e.target.value })} placeholder="Release Manager" className={FIELD} autoComplete="off" /> | |
| 315 | </div> | |
| 316 | </div> | |
| 317 | </div> | |
| 318 | ||
| An agent is on teams the way a person is: through team membership, with no team or department of its own. Add it to teams when you create it, from its profile or from the team's page; the Agents sidebar groups agents by the teams they're on, with those on none last; what an agent is told about its teammates, team budgets, skills reach, Spend's By team and the People pages all read memberships. Agents with an old team name matching a team are moved onto it once; the rest are left off a team. Templates name a role only. The agents, teams and people guides say how. | 319 | {onTeams && ( |
| 320 | <Section title="Add to teams" about={value.scope === "personal" ? "A personal agent is on no team." : "Optional. It joins them like anyone, and you can change them on its profile or the team's page."}> | |
| 321 | {value.scope === "personal" ? null : <TeamPicker teams={joinable} value={teams} onChange={onTeams} />} | |
| 322 | </Section> | |
| 323 | )} | |
| 324 | ||
| 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. | 325 | <Section title="Face" about="Its bot face, wherever it appears. It has one of its own already; change any part, shuffle, or give it back."> |
| 326 | <FaceEditor value={value.look ?? null} seed={seed} name={value.display_name || "Your new agent"} preset={value.personality_preset} onChange={(look) => set({ look })} /> | |
| 327 | </Section> | |
| 328 | ||
| Anyone makes an agent by describing it: one box drafts the whole agent (name, job, responsibilities, voice, skills to keep, integrations it needs, model limits and budget) as a card to change and try in a chat beside it before creating it, and an agent changes later by saying what to change, shown as a diff before it's saved. Members can make personal agents, which only they talk to and which spend from their own budget, unless owners turn that off; owners promote one to a workspace agent. The agents guide says how. | 329 | <Section title="Job" about="What it is responsible for and how it works. It reads this before every reply."> |
| 330 | <textarea | |
| 331 | aria-label="Job" | |
| 332 | value={value.instructions} | |
| 333 | onChange={(e) => set({ instructions: e.target.value })} | |
| 334 | rows={8} | |
| 335 | required | |
| 336 | className={`${FIELD} resize-y font-mono text-[0.8125rem] leading-relaxed`} | |
| 337 | /> | |
| 338 | <div> | |
| 339 | <Label htmlFor="b-duties" hint="One a line, 2 to 8"> | |
| 340 | Responsibilities | |
| 341 | </Label> | |
| 342 | <textarea | |
| 343 | id="b-duties" | |
| 344 | value={(value.responsibilities ?? []).join("\n")} | |
| 345 | onChange={(e) => set({ responsibilities: e.target.value.split("\n").slice(0, 8) })} | |
| 346 | onBlur={() => set({ responsibilities: (value.responsibilities ?? []).map((d) => d.trim()).filter(Boolean) })} | |
| 347 | rows={Math.max(3, (value.responsibilities ?? []).length)} | |
| 348 | className={`${FIELD} resize-y`} | |
| 349 | /> | |
| 350 | </div> | |
| 351 | </Section> | |
| 352 | ||
| 353 | <Section title="Personality" about="Its voice only. A personality never changes what it may do."> | |
| 354 | <div role="radiogroup" aria-label="Voice" className="grid grid-cols-2 gap-1 rounded-lg bg-bg p-1 ring-1 ring-line sm:grid-cols-4"> | |
| 355 | {PRESETS.map((option) => ( | |
| 356 | <button | |
| 357 | key={option.value} | |
| 358 | type="button" | |
| 359 | role="radio" | |
| 360 | aria-checked={value.personality_preset === option.value} | |
| 361 | onClick={() => set({ personality_preset: option.value })} | |
| 362 | className={cn( | |
| 363 | "h-8 rounded-md text-[0.8125rem] font-medium transition-colors", | |
| 364 | value.personality_preset === option.value ? "bg-raised text-fg shadow-sm ring-1 ring-line-strong" : "text-muted hover:text-fg", | |
| 365 | )} | |
| 366 | > | |
| 367 | {option.label} | |
| 368 | </button> | |
| 369 | ))} | |
| 370 | </div> | |
| 371 | <input aria-label="In its own words" value={value.personality ?? ""} onChange={(e) => set({ personality: e.target.value })} placeholder="In its own words (optional)" className={FIELD} /> | |
| 372 | </Section> | |
| 373 | ||
| 374 | <Section title="Skills" about="g1t's playbooks it keeps on. The suggested ones fit its job; turn any on or off."> | |
| 375 | <div className="grid gap-1.5 sm:grid-cols-2"> | |
| 376 | {FOUNDATIONAL_SKILLS.map((skill) => { | |
| 377 | const on = !off.has(skill.id); | |
| 378 | return ( | |
| 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. | 379 | <Card asChild key={skill.id} tone="bg" radius="lg" className="flex min-h-10 cursor-pointer items-center gap-2.5 px-3 py-2 text-sm has-[[data-state=checked]]:border-accent/50 has-[[data-state=checked]]:bg-accent/5"> |
| 380 | <label> | |
| 381 | <Checkbox | |
| 382 | checked={on} | |
| 383 | onCheckedChange={() => { | |
| 384 | const next = new Set(off); | |
| 385 | if (on) next.add(skill.id); | |
| 386 | else next.delete(skill.id); | |
| 387 | set({ skills_off: FOUNDATIONAL_SKILLS.map((s) => s.id).filter((id) => next.has(id)) }); | |
| 388 | }} | |
| 389 | /> | |
| 390 | <span className="min-w-0 grow truncate">{skill.name}</span> | |
| 391 | {suggested.has(skill.id) && suggested.size < FOUNDATIONAL_SKILLS.length && ( | |
| 392 | <Badge tone="accent" className="shrink-0"> | |
| 393 | Suggested | |
| 394 | </Badge> | |
| 395 | )} | |
| 396 | </label> | |
| 397 | </Card> | |
| Anyone makes an agent by describing it: one box drafts the whole agent (name, job, responsibilities, voice, skills to keep, integrations it needs, model limits and budget) as a card to change and try in a chat beside it before creating it, and an agent changes later by saying what to change, shown as a diff before it's saved. Members can make personal agents, which only they talk to and which spend from their own budget, unless owners turn that off; owners promote one to a workspace agent. The agents guide says how. | 398 | ); |
| 399 | })} | |
| 400 | </div> | |
| 401 | </Section> | |
| 402 | ||
| 403 | {hints.length > 0 && ( | |
| 404 | <Section title="What it needs connected" about="Integrations its job would use. It works without them, and says so when asked for something that needs one."> | |
| 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. | 405 | <Card asChild tone="bg" radius="lg" divided className="overflow-hidden"> |
| 406 | <ul> | |
| 407 | {hints.map((hint) => ( | |
| 408 | <li key={hint.id} className="flex flex-wrap items-center gap-x-3 gap-y-1 px-3 py-2.5"> | |
| 409 | <Plug size={14} className="shrink-0 text-faint" /> | |
| 410 | <span className="min-w-0 grow basis-40"> | |
| 411 | <span className="block text-sm font-medium">{hint.name}</span> | |
| 412 | {hint.why && <span className="block text-xs text-muted">{hint.why}</span>} | |
| 413 | </span> | |
| 414 | {hint.action === "soon" ? ( | |
| 415 | <Badge>Soon</Badge> | |
| 416 | ) : ( | |
| 417 | <Link to={hint.href} target="_blank" rel="noreferrer" className="text-xs font-medium text-accent hover:underline"> | |
| 418 | {hint.action === "connect" ? "Connect" : "Ask an owner"} | |
| 419 | </Link> | |
| 420 | )} | |
| 421 | </li> | |
| 422 | ))} | |
| 423 | </ul> | |
| 424 | </Card> | |
| Anyone makes an agent by describing it: one box drafts the whole agent (name, job, responsibilities, voice, skills to keep, integrations it needs, model limits and budget) as a card to change and try in a chat beside it before creating it, and an agent changes later by saying what to change, shown as a diff before it's saved. Members can make personal agents, which only they talk to and which spend from their own budget, unless owners turn that off; owners promote one to a workspace agent. The agents guide says how. | 425 | </Section> |
| 426 | )} | |
| 427 | ||
| 428 | {proposal.routines.length > 0 && ( | |
| 429 | <Section | |
| 430 | title="Routines that would suit it" | |
| 431 | about={value.scope === "personal" ? "Personal agents can't run routines yet. An owner can promote it to a workspace agent, which can." : "Set them up on its Routines tab once it exists."} | |
| 432 | > | |
| 433 | <ul className="space-y-2"> | |
| 434 | {proposal.routines.map((routine) => ( | |
| 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. | 435 | <Card asChild key={routine.name} tone="bg" radius="lg" className="px-3 py-2.5"> |
| 436 | <li> | |
| 437 | <p className="text-sm font-medium"> | |
| 438 | {routine.name} <span className="font-normal text-muted">· {routine.when}</span> | |
| 439 | </p> | |
| 440 | {routine.instructions && <p className="mt-0.5 text-xs text-muted">{routine.instructions}</p>} | |
| 441 | </li> | |
| 442 | </Card> | |
| Anyone makes an agent by describing it: one box drafts the whole agent (name, job, responsibilities, voice, skills to keep, integrations it needs, model limits and budget) as a card to change and try in a chat beside it before creating it, and an agent changes later by saying what to change, shown as a diff before it's saved. Members can make personal agents, which only they talk to and which spend from their own budget, unless owners turn that off; owners promote one to a workspace agent. The agents guide says how. | 443 | ))} |
| 444 | </ul> | |
| 445 | </Section> | |
| 446 | )} | |
| 447 | ||
| 448 | <Section title="Models and budget" about="g1t routes each step to the tier it needs; these only limit it. Budgets are checked before work starts."> | |
| 449 | <div className="grid gap-3 sm:grid-cols-2"> | |
| 450 | {(["floor", "ceiling"] as const).map((key) => ( | |
| 451 | <div key={key}> | |
| 452 | <Label htmlFor={`b-${key}`}>{key === "floor" ? "Floor" : "Ceiling"}</Label> | |
| 453 | <Select | |
| 454 | value={value.routing?.[key] ?? "none"} | |
| 455 | onValueChange={(tier) => | |
| 456 | set({ routing: { floor: null, ceiling: null, providers: [], pinned: null, ...value.routing, [key]: tier === "none" ? null : (tier as "small") } }) | |
| 457 | } | |
| 458 | > | |
| 459 | <SelectTrigger id={`b-${key}`}> | |
| 460 | <SelectValue /> | |
| 461 | </SelectTrigger> | |
| 462 | <SelectContent> | |
| 463 | <SelectItem value="none">{key === "floor" ? "No floor" : "No ceiling"}</SelectItem> | |
| 464 | {(["small", "large", "frontier"] as const).map((tier) => ( | |
| 465 | <SelectItem key={tier} value={tier} description={TIER_LABELS[tier].split(": ")[1]}> | |
| 466 | {TIER_LABELS[tier].split(":")[0]} | |
| 467 | </SelectItem> | |
| 468 | ))} | |
| 469 | </SelectContent> | |
| 470 | </Select> | |
| 471 | </div> | |
| 472 | ))} | |
| 473 | {( | |
| 474 | [ | |
| 475 | ["monthly_micros", "Monthly"], | |
| 476 | ["task_micros", "Per session"], | |
| 477 | ] as const | |
| 478 | ).map(([key, label]) => ( | |
| 479 | <div key={key}> | |
| 480 | <Label htmlFor={`b-${key}`} hint={key === "monthly_micros" ? "Per calendar month" : "Going over asks you"}> | |
| 481 | {label} | |
| 482 | </Label> | |
| 483 | <div className="flex items-center rounded-md border border-line bg-bg transition-colors focus-within:border-accent-dim hover:border-line-strong"> | |
| 484 | <span className="pl-3 text-sm text-faint">$</span> | |
| 485 | <input | |
| 486 | id={`b-${key}`} | |
| 487 | inputMode="decimal" | |
| 488 | defaultValue={dollarsField(value.budget?.[key])} | |
| 489 | onBlur={(e) => { | |
| 490 | const micros = microsFromDollars(e.target.value); | |
| 491 | set({ budget: { ...value.budget, [key]: micros == null || Number.isNaN(micros) ? null : micros } }); | |
| 492 | }} | |
| 493 | placeholder="No cap" | |
| 494 | className="min-w-0 grow bg-transparent px-2 py-2 text-sm tabular-nums outline-none placeholder:text-faint" | |
| 495 | autoComplete="off" | |
| 496 | data-1p-ignore | |
| 497 | /> | |
| 498 | </div> | |
| 499 | </div> | |
| 500 | ))} | |
| 501 | </div> | |
| 502 | </Section> | |
| 503 | </div> | |
| 504 | ); | |
| 505 | } | |
| 506 | ||
| 507 | // ── Try it ──────────────────────────────────────────────────────────────── | |
| 508 | ||
| 509 | /** | |
| 510 | * A test chat with the drafted agent, as it is in the card right now: | |
| 511 | * charged to the person trying it, and nothing kept once they leave. | |
| 512 | */ | |
| 513 | export function TryChat({ definition, onReset }: { definition: BuilderDefinition; onReset?: () => void }) { | |
| 514 | const fetcher = useFetcher<BuilderAnswer>({ key: "agent-try" }); | |
| 515 | const [turns, setTurns] = useState<DraftTurn[]>([]); | |
| 516 | const [text, setText] = useState(""); | |
| 517 | const [spent, setSpent] = useState(0); | |
| 518 | const handled = useRef<unknown>(null); | |
| 519 | const log = useRef<HTMLDivElement>(null); | |
| 520 | const busy = fetcher.state !== "idle"; | |
| 521 | useEffect(() => { | |
| 522 | if (fetcher.state !== "idle" || !fetcher.data || handled.current === fetcher.data) return; | |
| 523 | handled.current = fetcher.data; | |
| 524 | if (fetcher.data.ok && fetcher.data.intent === "try") { | |
| 525 | const answer = fetcher.data; | |
| 526 | setTurns((now) => [...now, { role: "assistant", content: answer.text }]); | |
| 527 | setSpent((n) => n + answer.charged_micros); | |
| 528 | } | |
| 529 | }, [fetcher.state, fetcher.data]); | |
| 530 | // The newest message in view, scrolling only the conversation, never the page. | |
| 531 | useEffect(() => { | |
| 532 | if (log.current) log.current.scrollTop = log.current.scrollHeight; | |
| 533 | }, [turns.length, busy]); | |
| 534 | const error = fetcher.state === "idle" && fetcher.data && !fetcher.data.ok && fetcher.data.intent === "try" ? fetcher.data.error : null; | |
| 535 | const send = () => { | |
| 536 | const body = text.trim(); | |
| 537 | if (!body || busy) return; | |
| 538 | const next: DraftTurn[] = [...turns, { role: "user", content: body }]; | |
| 539 | setTurns(next); | |
| 540 | setText(""); | |
| 541 | fetcher.submit({ intent: "try", definition: JSON.stringify(definition), messages: JSON.stringify(next.slice(-24)) }, { method: "post" }); | |
| 542 | }; | |
| 543 | const name = definition.display_name || "your agent"; | |
| 544 | return ( | |
| 545 | <div className="flex h-full min-h-104 flex-col overflow-hidden rounded-2xl border border-line bg-surface"> | |
| 546 | <div className="flex items-center gap-2.5 border-b border-line px-4 py-3"> | |
| 547 | <MessageSquare size={15} className="text-faint" /> | |
| 548 | <div className="min-w-0 grow"> | |
| 549 | <p className="text-sm font-semibold">Try it</p> | |
| 550 | <p className="truncate text-xs text-muted">Talk to {name} as it is drafted. Charged to you; nothing is saved.</p> | |
| 551 | </div> | |
| 552 | {turns.length > 0 && ( | |
| 553 | <Hint label="Start over"> | |
| 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. | 554 | <Button |
| Anyone makes an agent by describing it: one box drafts the whole agent (name, job, responsibilities, voice, skills to keep, integrations it needs, model limits and budget) as a card to change and try in a chat beside it before creating it, and an agent changes later by saying what to change, shown as a diff before it's saved. Members can make personal agents, which only they talk to and which spend from their own budget, unless owners turn that off; owners promote one to a workspace agent. The agents guide says how. | 555 | type="button" |
| 556 | aria-label="Start over" | |
| 557 | onClick={() => { | |
| 558 | setTurns([]); | |
| 559 | onReset?.(); | |
| 560 | }} | |
| 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. | 561 | variant="ghost" |
| 562 | size="icon-sm" | |
| 563 | className="text-faint" | |
| Anyone makes an agent by describing it: one box drafts the whole agent (name, job, responsibilities, voice, skills to keep, integrations it needs, model limits and budget) as a card to change and try in a chat beside it before creating it, and an agent changes later by saying what to change, shown as a diff before it's saved. Members can make personal agents, which only they talk to and which spend from their own budget, unless owners turn that off; owners promote one to a workspace agent. The agents guide says how. | 564 | > |
| 565 | <RotateCcw size={14} /> | |
| 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. | 566 | </Button> |
| Anyone makes an agent by describing it: one box drafts the whole agent (name, job, responsibilities, voice, skills to keep, integrations it needs, model limits and budget) as a card to change and try in a chat beside it before creating it, and an agent changes later by saying what to change, shown as a diff before it's saved. Members can make personal agents, which only they talk to and which spend from their own budget, unless owners turn that off; owners promote one to a workspace agent. The agents guide says how. | 567 | </Hint> |
| 568 | )} | |
| 569 | </div> | |
| 570 | <div ref={log} className="min-h-0 grow space-y-3 overflow-y-auto px-4 py-4" aria-live="polite"> | |
| 571 | {turns.length === 0 && ( | |
| 572 | <div className="flex h-full flex-col items-center justify-center py-8 text-center"> | |
| 573 | <AgentAvatar agent={{ handle: definition.handle, avatar_seed: definition.avatar_seed || definition.handle }} size={40} /> | |
| 574 | <p className="mt-3 text-sm font-medium">Ask {name} something it would get asked</p> | |
| 575 | <p className="mt-1 max-w-xs text-xs text-muted">It answers from its job and voice. It has no tools or memory until you create it.</p> | |
| 576 | </div> | |
| 577 | )} | |
| 578 | {turns.map((turn, at) => | |
| 579 | turn.role === "user" ? ( | |
| 580 | <div key={at} className="flex justify-end"> | |
| 581 | <p className="max-w-[85%] rounded-2xl rounded-br-md bg-accent/15 px-3.5 py-2 text-sm whitespace-pre-wrap text-fg">{turn.content}</p> | |
| 582 | </div> | |
| 583 | ) : ( | |
| 584 | <div key={at} className="flex gap-2.5"> | |
| 585 | <AgentAvatar agent={{ handle: definition.handle, avatar_seed: definition.avatar_seed || definition.handle }} size={24} /> | |
| 586 | <div className="min-w-0 grow text-sm [&_.prose]:text-sm"> | |
| 587 | <Markdown source={turn.content} /> | |
| 588 | </div> | |
| 589 | </div> | |
| 590 | ), | |
| 591 | )} | |
| 592 | {busy && ( | |
| 593 | <div className="flex items-center gap-2.5 text-xs text-muted"> | |
| 594 | <AgentAvatar agent={{ handle: definition.handle, avatar_seed: definition.avatar_seed || definition.handle }} size={24} /> | |
| 595 | {name} is typing… | |
| 596 | </div> | |
| 597 | )} | |
| 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. | 598 | {error && ( |
| 599 | <Alert asChild className="text-xs"> | |
| 600 | <p>{error}</p> | |
| 601 | </Alert> | |
| 602 | )} | |
| Anyone makes an agent by describing it: one box drafts the whole agent (name, job, responsibilities, voice, skills to keep, integrations it needs, model limits and budget) as a card to change and try in a chat beside it before creating it, and an agent changes later by saying what to change, shown as a diff before it's saved. Members can make personal agents, which only they talk to and which spend from their own budget, unless owners turn that off; owners promote one to a workspace agent. The agents guide says how. | 603 | </div> |
| 604 | <form | |
| 605 | className="flex items-end gap-2 border-t border-line p-2.5" | |
| 606 | onSubmit={(event) => { | |
| 607 | event.preventDefault(); | |
| 608 | send(); | |
| 609 | }} | |
| 610 | > | |
| 611 | <label htmlFor="try-message" className="sr-only"> | |
| 612 | Message {name} | |
| 613 | </label> | |
| 614 | <textarea | |
| 615 | id="try-message" | |
| 616 | value={text} | |
| 617 | onChange={(e) => setText(e.target.value)} | |
| 618 | onKeyDown={(e) => { | |
| 619 | if (e.key === "Enter" && !e.shiftKey) { | |
| 620 | e.preventDefault(); | |
| 621 | send(); | |
| 622 | } | |
| 623 | }} | |
| 624 | rows={1} | |
| 625 | maxLength={4000} | |
| 626 | placeholder={`Message ${name}`} | |
| 627 | className="max-h-32 min-h-9 min-w-0 grow resize-none rounded-md border border-line bg-bg px-3 py-2 text-sm outline-none placeholder:text-faint focus:border-accent-dim" | |
| 628 | /> | |
| 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. | 629 | <Button type="submit" aria-label="Send" disabled={busy || !text.trim()} variant="accent" size="icon"> |
| Anyone makes an agent by describing it: one box drafts the whole agent (name, job, responsibilities, voice, skills to keep, integrations it needs, model limits and budget) as a card to change and try in a chat beside it before creating it, and an agent changes later by saying what to change, shown as a diff before it's saved. Members can make personal agents, which only they talk to and which spend from their own budget, unless owners turn that off; owners promote one to a workspace agent. The agents guide says how. | 630 | <ArrowUp size={16} /> |
| 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. | 631 | </Button> |
| Anyone makes an agent by describing it: one box drafts the whole agent (name, job, responsibilities, voice, skills to keep, integrations it needs, model limits and budget) as a card to change and try in a chat beside it before creating it, and an agent changes later by saying what to change, shown as a diff before it's saved. Members can make personal agents, which only they talk to and which spend from their own budget, unless owners turn that off; owners promote one to a workspace agent. The agents guide says how. | 632 | </form> |
| 633 | {spent > 0 && <p className="border-t border-line px-4 py-1.5 text-[0.6875rem] text-faint tabular-nums">Trying it has cost you {money(spent)} so far.</p>} | |
| 634 | </div> | |
| 635 | ); | |
| 636 | } | |
| 637 | ||
| 638 | // ── Create ──────────────────────────────────────────────────────────────── | |
| 639 | ||
| 640 | /** Saves the drafted agent: its hello is waiting in a direct message. */ | |
| An agent is on teams the way a person is: through team membership, with no team or department of its own. Add it to teams when you create it, from its profile or from the team's page; the Agents sidebar groups agents by the teams they're on, with those on none last; what an agent is told about its teammates, team budgets, skills reach, Spend's By team and the People pages all read memberships. Agents with an old team name matching a team are moved onto it once; the rest are left off a team. Templates name a role only. The agents, teams and people guides say how. | 641 | export function CreateDraft({ definition, teams = [], onEditAll }: { definition: BuilderDefinition; teams?: string[]; onEditAll: () => void }) { |
| Anyone makes an agent by describing it: one box drafts the whole agent (name, job, responsibilities, voice, skills to keep, integrations it needs, model limits and budget) as a card to change and try in a chat beside it before creating it, and an agent changes later by saying what to change, shown as a diff before it's saved. Members can make personal agents, which only they talk to and which spend from their own budget, unless owners turn that off; owners promote one to a workspace agent. The agents guide says how. | 642 | const navigation = useNavigation(); |
| 643 | const busy = navigation.state !== "idle" && navigation.formData?.get("intent") === "create_draft"; | |
| 644 | const ready = definition.display_name.trim() && definition.instructions.trim() && definition.handle.trim(); | |
| 645 | return ( | |
| 646 | <Form method="post" className="flex flex-wrap items-center justify-end gap-2"> | |
| 647 | <input type="hidden" name="intent" value="create_draft" /> | |
| 648 | <input type="hidden" name="definition" value={JSON.stringify(definition)} /> | |
| An agent is on teams the way a person is: through team membership, with no team or department of its own. Add it to teams when you create it, from its profile or from the team's page; the Agents sidebar groups agents by the teams they're on, with those on none last; what an agent is told about its teammates, team budgets, skills reach, Spend's By team and the People pages all read memberships. Agents with an old team name matching a team are moved onto it once; the rest are left off a team. Templates name a role only. The agents, teams and people guides say how. | 649 | {teams.map((slug) => ( |
| 650 | <input key={slug} type="hidden" name="teams" value={slug} /> | |
| 651 | ))} | |
| 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. | 652 | <Button type="button" onClick={onEditAll} variant="outline"> |
| Anyone makes an agent by describing it: one box drafts the whole agent (name, job, responsibilities, voice, skills to keep, integrations it needs, model limits and budget) as a card to change and try in a chat beside it before creating it, and an agent changes later by saying what to change, shown as a diff before it's saved. Members can make personal agents, which only they talk to and which spend from their own budget, unless owners turn that off; owners promote one to a workspace agent. The agents guide says how. | 653 | Edit all fields |
| 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. | 654 | </Button> |
| 655 | <Button type="submit" disabled={busy || !ready} variant="accent"> | |
| Anyone makes an agent by describing it: one box drafts the whole agent (name, job, responsibilities, voice, skills to keep, integrations it needs, model limits and budget) as a card to change and try in a chat beside it before creating it, and an agent changes later by saying what to change, shown as a diff before it's saved. Members can make personal agents, which only they talk to and which spend from their own budget, unless owners turn that off; owners promote one to a workspace agent. The agents guide says how. | 656 | <Check size={15} /> |
| 657 | {busy ? "Creating…" : `Create ${definition.display_name.trim() || "agent"}`} | |
| 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. | 658 | </Button> |
| Anyone makes an agent by describing it: one box drafts the whole agent (name, job, responsibilities, voice, skills to keep, integrations it needs, model limits and budget) as a card to change and try in a chat beside it before creating it, and an agent changes later by saying what to change, shown as a diff before it's saved. Members can make personal agents, which only they talk to and which spend from their own budget, unless owners turn that off; owners promote one to a workspace agent. The agents guide says how. | 659 | </Form> |
| 660 | ); | |
| 661 | } | |
| 662 | ||
| 663 | // ── Change it in words ──────────────────────────────────────────────────── | |
| 664 | ||
| 665 | /** | |
| 666 | * "Tell <name> what to change": the change is drafted, shown field by | |
| 667 | * field with the job as a diff, and saved as a new version only when the | |
| 668 | * person says so. | |
| 669 | */ | |
| 670 | export function RedraftBox({ agent }: { agent: WorkspaceAgent }) { | |
| 671 | const fetcher = useFetcher<BuilderAnswer>({ key: `redraft:${agent.id}` }); | |
| 672 | const save = useFetcher<{ ok?: boolean; saved?: boolean; errors?: Record<string, string> }>({ key: `redraft-save:${agent.id}` }); | |
| 673 | const [text, setText] = useState(""); | |
| 674 | const [shown, setShown] = useState<AgentRedraft | null>(null); | |
| 675 | const handled = useRef<unknown>(null); | |
| 676 | useEffect(() => { | |
| 677 | if (fetcher.state !== "idle" || !fetcher.data || handled.current === fetcher.data) return; | |
| 678 | handled.current = fetcher.data; | |
| 679 | if (fetcher.data.ok && fetcher.data.intent === "redraft") setShown(fetcher.data.redraft); | |
| 680 | }, [fetcher.state, fetcher.data]); | |
| 681 | useEffect(() => { | |
| 682 | if (save.state === "idle" && save.data?.saved) { | |
| 683 | setShown(null); | |
| 684 | setText(""); | |
| 685 | } | |
| 686 | }, [save.state, save.data]); | |
| 687 | const busy = fetcher.state !== "idle"; | |
| 688 | const error = fetcher.state === "idle" && fetcher.data && !fetcher.data.ok && fetcher.data.intent === "redraft" ? fetcher.data.error : null; | |
| 689 | const saveError = save.state === "idle" && save.data && !save.data.saved ? (save.data.errors?.form ?? save.data.errors?.handle ?? null) : null; | |
| 690 | const skillName = (id: string) => FOUNDATIONAL_SKILLS.find((s) => s.id === id)?.name ?? id; | |
| 691 | const rows = shown ? changeRows(agent, shown.changes, skillName) : []; | |
| 692 | return ( | |
| 693 | <section aria-labelledby="redraft" className="mb-10 rounded-2xl border border-line bg-surface"> | |
| 694 | <fetcher.Form method="post" className="p-4 sm:p-5"> | |
| 695 | <input type="hidden" name="intent" value="redraft" /> | |
| 696 | <h2 id="redraft" className="flex items-center gap-2 text-sm font-semibold"> | |
| 697 | <Wand2 size={15} className="text-accent" /> | |
| 698 | Tell {agent.display_name} what to change | |
| 699 | </h2> | |
| 700 | <p className="mt-1 text-xs text-muted">Say it in your own words. You'll see the change before it's saved as version {agent.version + 1}. Drafting it is charged to you.</p> | |
| 701 | <div className="mt-3 flex flex-col gap-2 sm:flex-row sm:items-end"> | |
| 702 | <label htmlFor="redraft-request" className="sr-only"> | |
| 703 | What should change | |
| 704 | </label> | |
| 705 | <textarea | |
| 706 | id="redraft-request" | |
| 707 | name="request" | |
| 708 | value={text} | |
| 709 | onChange={(e) => setText(e.target.value)} | |
| 710 | onKeyDown={(e) => { | |
| 711 | if (e.key === "Enter" && (e.metaKey || e.ctrlKey) && text.trim().length >= 3) e.currentTarget.form?.requestSubmit(); | |
| 712 | }} | |
| 713 | rows={2} | |
| 714 | maxLength={1000} | |
| 715 | placeholder={`Answer in Spanish, and keep replies under five sentences`} | |
| 716 | className={`${FIELD} grow resize-y`} | |
| 717 | /> | |
| 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. | 718 | <Button type="submit" disabled={busy || text.trim().length < 3} variant="accent"> |
| Anyone makes an agent by describing it: one box drafts the whole agent (name, job, responsibilities, voice, skills to keep, integrations it needs, model limits and budget) as a card to change and try in a chat beside it before creating it, and an agent changes later by saying what to change, shown as a diff before it's saved. Members can make personal agents, which only they talk to and which spend from their own budget, unless owners turn that off; owners promote one to a workspace agent. The agents guide says how. | 719 | <Sparkles size={14} className={busy ? "animate-pulse motion-reduce:animate-none" : ""} /> |
| 720 | {busy ? "Drafting…" : "Draft the change"} | |
| 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. | 721 | </Button> |
| Anyone makes an agent by describing it: one box drafts the whole agent (name, job, responsibilities, voice, skills to keep, integrations it needs, model limits and budget) as a card to change and try in a chat beside it before creating it, and an agent changes later by saying what to change, shown as a diff before it's saved. Members can make personal agents, which only they talk to and which spend from their own budget, unless owners turn that off; owners promote one to a workspace agent. The agents guide says how. | 722 | </div> |
| 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. | 723 | {error && ( |
| 724 | <Alert asChild className="mt-3"> | |
| 725 | <p>{error}</p> | |
| 726 | </Alert> | |
| 727 | )} | |
| Anyone makes an agent by describing it: one box drafts the whole agent (name, job, responsibilities, voice, skills to keep, integrations it needs, model limits and budget) as a card to change and try in a chat beside it before creating it, and an agent changes later by saying what to change, shown as a diff before it's saved. Members can make personal agents, which only they talk to and which spend from their own budget, unless owners turn that off; owners promote one to a workspace agent. The agents guide says how. | 728 | </fetcher.Form> |
| 729 | {shown && ( | |
| 730 | <div className="border-t border-line p-4 sm:p-5"> | |
| 731 | <p className="text-sm"> | |
| 732 | <span className="font-medium">{shown.summary}</span>{" "} | |
| 733 | <span className="text-muted"> | |
| 734 | {rows.length} {rows.length === 1 ? "change" : "changes"} to version {shown.from_version}. | |
| 735 | </span> | |
| 736 | </p> | |
| 737 | <ChangeList rows={rows} /> | |
| 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. | 738 | {saveError && ( |
| 739 | <Alert asChild className="mt-3"> | |
| 740 | <p>{saveError}</p> | |
| 741 | </Alert> | |
| 742 | )} | |
| Anyone makes an agent by describing it: one box drafts the whole agent (name, job, responsibilities, voice, skills to keep, integrations it needs, model limits and budget) as a card to change and try in a chat beside it before creating it, and an agent changes later by saying what to change, shown as a diff before it's saved. Members can make personal agents, which only they talk to and which spend from their own budget, unless owners turn that off; owners promote one to a workspace agent. The agents guide says how. | 743 | <save.Form method="post" className="mt-4 flex flex-wrap items-center justify-end gap-2"> |
| 744 | <input type="hidden" name="intent" value="apply" /> | |
| 745 | <input type="hidden" name="changes" value={JSON.stringify(shown.changes)} /> | |
| 746 | <input type="hidden" name="from_version" value={shown.from_version} /> | |
| 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. | 747 | <Button type="button" onClick={() => setShown(null)} variant="outline"> |
| Anyone makes an agent by describing it: one box drafts the whole agent (name, job, responsibilities, voice, skills to keep, integrations it needs, model limits and budget) as a card to change and try in a chat beside it before creating it, and an agent changes later by saying what to change, shown as a diff before it's saved. Members can make personal agents, which only they talk to and which spend from their own budget, unless owners turn that off; owners promote one to a workspace agent. The agents guide says how. | 748 | Discard |
| 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. | 749 | </Button> |
| 750 | <Button type="submit" disabled={save.state !== "idle"} variant="accent"> | |
| Anyone makes an agent by describing it: one box drafts the whole agent (name, job, responsibilities, voice, skills to keep, integrations it needs, model limits and budget) as a card to change and try in a chat beside it before creating it, and an agent changes later by saying what to change, shown as a diff before it's saved. Members can make personal agents, which only they talk to and which spend from their own budget, unless owners turn that off; owners promote one to a workspace agent. The agents guide says how. | 751 | <Check size={15} /> |
| 752 | {save.state !== "idle" ? "Saving…" : `Save as version ${agent.version + 1}`} | |
| 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. | 753 | </Button> |
| Anyone makes an agent by describing it: one box drafts the whole agent (name, job, responsibilities, voice, skills to keep, integrations it needs, model limits and budget) as a card to change and try in a chat beside it before creating it, and an agent changes later by saying what to change, shown as a diff before it's saved. Members can make personal agents, which only they talk to and which spend from their own budget, unless owners turn that off; owners promote one to a workspace agent. The agents guide says how. | 754 | </save.Form> |
| 755 | </div> | |
| 756 | )} | |
| 757 | </section> | |
| 758 | ); | |
| 759 | } | |
| 760 | ||
| 761 | /** Each field a change touches, before and after; the job and duties as a line diff. */ | |
| 762 | export function ChangeList({ rows }: { rows: ChangeRow[] }) { | |
| 763 | return ( | |
| 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. | 764 | <Card asChild tone="bg" radius="lg" divided className="mt-3 overflow-hidden"> |
| 765 | <dl> | |
| 766 | {rows.map((row) => ( | |
| 767 | <div key={row.field} className="px-3 py-2.5"> | |
| 768 | <dt className="text-xs font-medium text-faint">{row.label}</dt> | |
| 769 | {row.lines ? ( | |
| 770 | <dd className="mt-1.5 max-h-80 overflow-auto rounded-md border border-line font-mono text-[0.75rem] leading-relaxed"> | |
| 771 | {row.lines.map((line, at) => ( | |
| 772 | <div | |
| 773 | key={at} | |
| 774 | className={cn( | |
| 775 | "flex gap-2 px-2 whitespace-pre-wrap", | |
| 776 | line.kind === "added" && "bg-success/10 text-success", | |
| 777 | line.kind === "removed" && "bg-danger/10 text-danger line-through decoration-danger/40", | |
| 778 | line.kind === "same" && "text-muted", | |
| 779 | )} | |
| 780 | > | |
| 781 | <span aria-hidden="true" className="w-3 shrink-0 select-none"> | |
| 782 | {line.kind === "added" ? "+" : line.kind === "removed" ? "−" : " "} | |
| 783 | </span> | |
| 784 | <span className="sr-only">{line.kind === "added" ? "Added: " : line.kind === "removed" ? "Removed: " : ""}</span> | |
| 785 | <span className="min-w-0">{line.text || " "}</span> | |
| 786 | </div> | |
| 787 | ))} | |
| 788 | </dd> | |
| 789 | ) : ( | |
| 790 | <dd className="mt-1 flex flex-wrap items-baseline gap-x-2 gap-y-0.5 text-sm"> | |
| 791 | <span className="text-muted line-through decoration-line-strong">{row.before}</span> | |
| 792 | <span aria-hidden="true" className="text-faint"> | |
| 793 | → | |
| 794 | </span> | |
| 795 | <span className="sr-only">becomes</span> | |
| 796 | <span className="font-medium text-fg">{row.after}</span> | |
| 797 | </dd> | |
| 798 | )} | |
| 799 | </div> | |
| 800 | ))} | |
| 801 | </dl> | |
| 802 | </Card> | |
| Anyone makes an agent by describing it: one box drafts the whole agent (name, job, responsibilities, voice, skills to keep, integrations it needs, model limits and budget) as a card to change and try in a chat beside it before creating it, and an agent changes later by saying what to change, shown as a diff before it's saved. Members can make personal agents, which only they talk to and which spend from their own budget, unless owners turn that off; owners promote one to a workspace agent. The agents guide says how. | 803 | ); |
| 804 | } | |
| 805 | ||
| 806 | /** Whose personal agent it is, and, for owners, making it the workspace's. */ | |
| 807 | export function PersonalNotice({ agent, owner, mine }: { agent: WorkspaceAgent; owner: boolean; mine: boolean }) { | |
| 808 | const navigation = useNavigation(); | |
| 809 | const [asking, setAsking] = useState(false); | |
| 810 | const busy = navigation.state !== "idle" && navigation.formData?.get("intent") === "promote"; | |
| 811 | return ( | |
| 812 | <section aria-labelledby="personal" className="mb-10 rounded-2xl border border-line bg-surface p-4 sm:p-5"> | |
| 813 | <h2 id="personal" className="flex items-center gap-2 text-sm font-semibold"> | |
| 814 | <Lock size={14} className="text-faint" /> | |
| 815 | {mine ? "Your personal agent" : `@${agent.personal_owner ?? "a member"}'s personal agent`} | |
| 816 | </h2> | |
| 817 | <p className="mt-1 text-sm text-muted"> | |
| 818 | {mine | |
| 819 | ? "Only you can talk to it, in your direct message with it, and it spends from your budget. It isn't in channels, and other agents don't hand it work." | |
| 820 | : `Only @${agent.personal_owner ?? "its member"} talks to it, and it spends from their budget. As an owner you can archive it, or make it a workspace agent everyone can talk to.`} | |
| 821 | </p> | |
| 822 | {owner && ( | |
| 823 | <div className="mt-4"> | |
| 824 | {asking ? ( | |
| 825 | <Form method="post" className="space-y-3"> | |
| 826 | <input type="hidden" name="intent" value="promote" /> | |
| 827 | <p className="text-sm text-fg-soft"> | |
| 828 | {agent.display_name} becomes a workspace agent with the same handle, job and every version so far. What it remembers from{" "} | |
| 829 | {mine ? "your" : `@${agent.personal_owner}'s`} direct messages stays behind with the personal agent, which is archived. | |
| 830 | </p> | |
| 831 | <div className="flex flex-wrap gap-2"> | |
| 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. | 832 | <Button type="submit" disabled={busy} variant="accent"> |
| Anyone makes an agent by describing it: one box drafts the whole agent (name, job, responsibilities, voice, skills to keep, integrations it needs, model limits and budget) as a card to change and try in a chat beside it before creating it, and an agent changes later by saying what to change, shown as a diff before it's saved. Members can make personal agents, which only they talk to and which spend from their own budget, unless owners turn that off; owners promote one to a workspace agent. The agents guide says how. | 833 | <Users size={14} /> |
| 834 | {busy ? "Promoting…" : `Promote ${agent.display_name}`} | |
| 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. | 835 | </Button> |
| 836 | <Button type="button" onClick={() => setAsking(false)} variant="outline"> | |
| Anyone makes an agent by describing it: one box drafts the whole agent (name, job, responsibilities, voice, skills to keep, integrations it needs, model limits and budget) as a card to change and try in a chat beside it before creating it, and an agent changes later by saying what to change, shown as a diff before it's saved. Members can make personal agents, which only they talk to and which spend from their own budget, unless owners turn that off; owners promote one to a workspace agent. The agents guide says how. | 837 | Cancel |
| 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. | 838 | </Button> |
| Anyone makes an agent by describing it: one box drafts the whole agent (name, job, responsibilities, voice, skills to keep, integrations it needs, model limits and budget) as a card to change and try in a chat beside it before creating it, and an agent changes later by saying what to change, shown as a diff before it's saved. Members can make personal agents, which only they talk to and which spend from their own budget, unless owners turn that off; owners promote one to a workspace agent. The agents guide says how. | 839 | </div> |
| 840 | </Form> | |
| 841 | ) : ( | |
| 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. | 842 | <Button type="button" onClick={() => setAsking(true)} variant="outline"> |
| Anyone makes an agent by describing it: one box drafts the whole agent (name, job, responsibilities, voice, skills to keep, integrations it needs, model limits and budget) as a card to change and try in a chat beside it before creating it, and an agent changes later by saying what to change, shown as a diff before it's saved. Members can make personal agents, which only they talk to and which spend from their own budget, unless owners turn that off; owners promote one to a workspace agent. The agents guide says how. | 843 | <Users size={14} /> |
| 844 | Promote to a workspace agent… | |
| 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. | 845 | </Button> |
| Anyone makes an agent by describing it: one box drafts the whole agent (name, job, responsibilities, voice, skills to keep, integrations it needs, model limits and budget) as a card to change and try in a chat beside it before creating it, and an agent changes later by saying what to change, shown as a diff before it's saved. Members can make personal agents, which only they talk to and which spend from their own budget, unless owners turn that off; owners promote one to a workspace agent. The agents guide says how. | 846 | )} |
| 847 | </div> | |
| 848 | )} | |
| 849 | </section> | |
| 850 | ); | |
| 851 | } |