Skip to content
857 linesCodeBlameRaw

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