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.
| Last commits read trees two dozen commits at a time, and the file list waits at most 3 s for them | 1 | /** |
| 2 | * Guardrails: the form a workspace sets its defaults with and a project its | |
| The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404. | 3 | * overrides: the network a sandbox may reach, and how long its runs may take. |
| Last commits read trees two dozen commits at a time, and the file list waits at most 3 s for them | 4 | */ |
| The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404. | 5 | import { type ReactNode, useState } from "react"; |
| Last commits read trees two dozen commits at a time, and the file list waits at most 3 s for them | 6 | import { Form } from "react-router"; |
| 7 | ||
| The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404. | 8 | import { type GuardrailSettings, type Guardrails, type GuardrailsView, RUN_KIND_LABEL } from "@g1t/contracts"; |
| Last commits read trees two dozen commits at a time, and the file list waits at most 3 s for them | 9 | |
| The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404. | 10 | import { SANDBOX_RUN_KINDS, tri, workflowDomainLine } from "../lib/guardrails"; |
| Merge branch 'worktree-agent-a985c01f43538af01' | 11 | import { ErrorText, Input, SubmitButton, TimeAgo } from "./ui"; |
| 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. | 12 | import { Card } from "./ui/card"; |
| Last commits read trees two dozen commits at a time, and the file list waits at most 3 s for them | 13 | import { CheckboxOption } from "./ui/checkbox"; |
| 14 | import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./ui/select"; | |
| 15 | import { Textarea } from "./ui/textarea"; | |
| 16 | ||
| 17 | function Section({ title, about, children }: { title: string; about: ReactNode; children: ReactNode }) { | |
| 18 | return ( | |
| 19 | <section className="grid gap-x-10 gap-y-4 border-t border-line pt-8 first:border-t-0 first:pt-0 lg:grid-cols-[16rem_1fr]"> | |
| 20 | <div> | |
| 21 | <h2 className="font-medium">{title}</h2> | |
| 22 | <div className="mt-1 text-sm text-muted">{about}</div> | |
| 23 | </div> | |
| 24 | <div className="min-w-0 space-y-3">{children}</div> | |
| 25 | </section> | |
| 26 | ); | |
| 27 | } | |
| 28 | ||
| 29 | /** A choice of inheriting, or on or off, for one setting. */ | |
| 30 | function TriSelect({ | |
| 31 | name, | |
| 32 | value, | |
| 33 | inherited, | |
| 34 | parent, | |
| 35 | labels, | |
| 36 | title, | |
| 37 | children, | |
| 38 | }: { | |
| 39 | name: string; | |
| 40 | value: boolean | null | undefined; | |
| 41 | inherited: boolean; | |
| 42 | parent: string; | |
| 43 | labels: [on: string, off: string]; | |
| 44 | title: string; | |
| 45 | children: ReactNode; | |
| 46 | }) { | |
| 47 | 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. | 48 | <Card className="flex flex-col gap-3 p-4 sm:flex-row sm:items-start"> |
| Last commits read trees two dozen commits at a time, and the file list waits at most 3 s for them | 49 | <div className="min-w-0 grow"> |
| 50 | <p className="text-sm font-medium">{title}</p> | |
| 51 | <p className="mt-1 text-sm text-muted">{children}</p> | |
| 52 | </div> | |
| 53 | <Select name={name} defaultValue={tri(value)}> | |
| 54 | <SelectTrigger size="sm" aria-label={title} className="w-full shrink-0 sm:w-auto sm:min-w-44"> | |
| 55 | <SelectValue /> | |
| 56 | </SelectTrigger> | |
| 57 | <SelectContent align="end"> | |
| 58 | <SelectItem value="inherit"> | |
| 59 | As {parent} ({(inherited ? labels[0] : labels[1]).toLowerCase()}) | |
| 60 | </SelectItem> | |
| 61 | <SelectItem value="on">{labels[0]}</SelectItem> | |
| 62 | <SelectItem value="off">{labels[1]}</SelectItem> | |
| 63 | </SelectContent> | |
| 64 | </Select> | |
| 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. | 65 | </Card> |
| Last commits read trees two dozen commits at a time, and the file list waits at most 3 s for them | 66 | ); |
| 67 | } | |
| 68 | ||
| 69 | /** Hosts in a compact list. */ | |
| 70 | function Hosts({ hosts }: { hosts: string[] }) { | |
| 71 | return ( | |
| 72 | <span className="font-mono text-xs break-words text-faint">{hosts.length ? hosts.join(", ") : "none"}</span> | |
| 73 | ); | |
| 74 | } | |
| 75 | ||
| 76 | /** | |
| 77 | * One level's guardrails. `level` says which: the workspace's defaults, | |
| 78 | * over g1t's, or a project's overrides, over its workspace's. | |
| 79 | */ | |
| 80 | export function GuardrailsForm({ | |
| 81 | view, | |
| 82 | level, | |
| 83 | editable, | |
| 84 | saving, | |
| 85 | saved, | |
| 86 | error, | |
| 87 | }: { | |
| 88 | view: GuardrailsView; | |
| 89 | level: "workspace" | "project"; | |
| 90 | editable: boolean; | |
| Merge branch 'worktree-agent-a985c01f43538af01' | 91 | /** Working for a reason the Save button cannot see; it follows its own submission by itself. */ |
| 92 | saving?: boolean; | |
| Last commits read trees two dozen commits at a time, and the file list waits at most 3 s for them | 93 | saved: boolean; |
| 94 | error: string | null | undefined; | |
| 95 | }) { | |
| 96 | const own: GuardrailSettings = (level === "project" ? view.project : view.workspace) ?? {}; | |
| 97 | // What this level inherits, and what it is called. | |
| 98 | const base: Guardrails = level === "project" ? view.inherited : view.defaults; | |
| 99 | const parent = level === "project" ? "the workspace" : "g1t's default"; | |
| 100 | const [registryMode, setRegistryMode] = useState(own.registries ? "custom" : "inherit"); | |
| 101 | const shownRegistries = own.registries ?? base.registries; | |
| 102 | const inheritedDomains = level === "project" ? view.inherited.domains : []; | |
| 103 | const inheritedWorkflowDomains = level === "project" ? (view.inherited.workflowDomains ?? []) : []; | |
| 104 | return ( | |
| Merge branch 'worktree-agent-a985c01f43538af01' | 105 | // Keyed to the last change, so after a save the fields show what was kept, as it was tidied. |
| 106 | <Form key={own.updatedAt ?? "unset"} method="post" className="max-w-4xl space-y-8"> | |
| Last commits read trees two dozen commits at a time, and the file list waits at most 3 s for them | 107 | <fieldset disabled={!editable} className="min-w-0 space-y-8"> |
| 108 | <Section | |
| 109 | title="Network" | |
| 110 | about={ | |
| 111 | <> | |
| 112 | Which hosts a sandbox may reach. Requests anywhere else are refused at the sandbox's edge, and | |
| 113 | each refused host shows on the run as a step. | |
| 114 | </> | |
| 115 | } | |
| 116 | > | |
| 117 | <TriSelect | |
| 118 | name="restrictNetwork" | |
| 119 | value={own.restrictNetwork} | |
| 120 | inherited={base.restrictNetwork} | |
| 121 | parent={parent} | |
| 122 | labels={["Restricted", "Open"]} | |
| 123 | title="Only allowed hosts" | |
| 124 | > | |
| 125 | Restricted, a sandbox reaches g1t, the registries below and the domains you list, over HTTP and | |
| 126 | HTTPS only. Open, it reaches the whole internet. | |
| 127 | </TriSelect> | |
| 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. | 128 | <Card className="p-4"> |
| Last commits read trees two dozen commits at a time, and the file list waits at most 3 s for them | 129 | <p className="text-sm font-medium">Always allowed</p> |
| 130 | <p className="mt-1 text-sm text-muted"> | |
| The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404. | 131 | g1t's own hosts, for cloning, pushing and reporting: <Hosts hosts={view.g1tHosts} /> |
| Last commits read trees two dozen commits at a time, and the file list waits at most 3 s for them | 132 | </p> |
| 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 | </Card> |
| 134 | <Card className="p-4"> | |
| Last commits read trees two dozen commits at a time, and the file list waits at most 3 s for them | 135 | <div className="flex flex-col gap-3 sm:flex-row sm:items-start"> |
| 136 | <div className="min-w-0 grow"> | |
| 137 | <p className="text-sm font-medium">Package registries</p> | |
| 138 | <p className="mt-1 text-sm text-muted">Where installs fetch dependencies from.</p> | |
| 139 | </div> | |
| 140 | <Select name="registries" value={registryMode} onValueChange={setRegistryMode}> | |
| 141 | <SelectTrigger size="sm" aria-label="Package registries" className="w-full shrink-0 sm:w-auto sm:min-w-44"> | |
| 142 | <SelectValue /> | |
| 143 | </SelectTrigger> | |
| 144 | <SelectContent align="end"> | |
| 145 | <SelectItem value="inherit">As {parent}</SelectItem> | |
| 146 | <SelectItem value="custom">Choose</SelectItem> | |
| 147 | </SelectContent> | |
| 148 | </Select> | |
| 149 | </div> | |
| 150 | <div className="mt-4 grid gap-3 sm:grid-cols-2"> | |
| 151 | {view.registries.map((registry) => ( | |
| 152 | <CheckboxOption | |
| 153 | key={`${registry.id}-${registryMode}`} | |
| 154 | name={`registry:${registry.id}`} | |
| 155 | disabled={registryMode !== "custom"} | |
| 156 | defaultChecked={(registryMode === "custom" ? shownRegistries : base.registries).includes(registry.id)} | |
| 157 | label={registry.name} | |
| 158 | description={registry.hosts.join(", ")} | |
| 159 | /> | |
| 160 | ))} | |
| 161 | </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. | 162 | </Card> |
| 163 | <Card className="p-4"> | |
| Last commits read trees two dozen commits at a time, and the file list waits at most 3 s for them | 164 | <label htmlFor="guardrail-domains" className="text-sm font-medium"> |
| 165 | Allowed domains | |
| 166 | </label> | |
| 167 | <p className="mt-1 text-sm text-muted"> | |
| 168 | One per line: <span className="font-mono text-xs">api.stripe.com</span>, or{" "} | |
| 169 | <span className="font-mono text-xs">*.example.com</span> for its subdomains. | |
| 170 | {level === "project" && " These add to the workspace's."} | |
| 171 | </p> | |
| 172 | <Textarea | |
| 173 | id="guardrail-domains" | |
| 174 | name="domains" | |
| 175 | className="mt-3 font-mono text-xs" | |
| 176 | rows={4} | |
| 177 | defaultValue={(own.domains ?? []).join("\n")} | |
| 178 | placeholder="api.stripe.com" | |
| 179 | /> | |
| 180 | {inheritedDomains.length > 0 && ( | |
| 181 | <p className="mt-2 text-xs text-faint"> | |
| 182 | From the workspace: <Hosts hosts={inheritedDomains} /> | |
| 183 | </p> | |
| 184 | )} | |
| 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. | 185 | </Card> |
| 186 | <Card className="p-4"> | |
| Last commits read trees two dozen commits at a time, and the file list waits at most 3 s for them | 187 | <label htmlFor="guardrail-workflow-domains" className="text-sm font-medium"> |
| 188 | Workflow-only domains | |
| 189 | </label> | |
| 190 | <p className="mt-1 text-sm text-muted"> | |
| The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404. | 191 | Hosts that only workflow jobs may reach, such as the API a deploy uploads to. One |
| Last commits read trees two dozen commits at a time, and the file list waits at most 3 s for them | 192 | per line: the domain, then the workflows and the environments it is for, each comma-separated; |
| 193 | leave either out for any. Only jobs of runs that are not pull requests from forks get them. | |
| 194 | {level === "project" && " These add to the workspace's."} | |
| 195 | </p> | |
| 196 | <Textarea | |
| 197 | id="guardrail-workflow-domains" | |
| 198 | name="workflowDomains" | |
| 199 | className="mt-3 font-mono text-xs" | |
| 200 | rows={3} | |
| 201 | defaultValue={(own.workflowDomains ?? []).map(workflowDomainLine).join("\n")} | |
| 202 | placeholder="api.cloudflare.com | deploy.yml | production" | |
| 203 | /> | |
| 204 | {inheritedWorkflowDomains.length > 0 && ( | |
| 205 | <p className="mt-2 text-xs text-faint"> | |
| 206 | From the workspace:{" "} | |
| 207 | <span className="font-mono break-words">{inheritedWorkflowDomains.map(workflowDomainLine).join("; ")}</span> | |
| 208 | </p> | |
| 209 | )} | |
| 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. | 210 | </Card> |
| Last commits read trees two dozen commits at a time, and the file list waits at most 3 s for them | 211 | </Section> |
| 212 | ||
| The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404. | 213 | <Section title="Time limits" about="How long one run may take. A run that reaches it is stopped."> |
| 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. | 214 | <Card className="p-4"> |
| Last commits read trees two dozen commits at a time, and the file list waits at most 3 s for them | 215 | <p className="text-sm font-medium">Time per run, in minutes</p> |
| 216 | <p className="mt-1 text-sm text-muted">Empty: as {parent}, shown faded.</p> | |
| 217 | <div className="mt-4 grid grid-cols-2 gap-3 sm:grid-cols-3"> | |
| The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404. | 218 | {SANDBOX_RUN_KINDS.map((kind) => ( |
| Last commits read trees two dozen commits at a time, and the file list waits at most 3 s for them | 219 | <label key={kind} className="flex flex-col gap-1 text-xs text-muted"> |
| 220 | {RUN_KIND_LABEL[kind]} | |
| 221 | <Input | |
| 222 | name={`minutes:${kind}`} | |
| 223 | inputMode="numeric" | |
| 224 | defaultValue={own.minutes?.[kind] == null ? "" : String(own.minutes[kind])} | |
| 225 | placeholder={String(base.minutes[kind] ?? "")} | |
| 226 | /> | |
| 227 | </label> | |
| 228 | ))} | |
| 229 | </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. | 230 | </Card> |
| Last commits read trees two dozen commits at a time, and the file list waits at most 3 s for them | 231 | </Section> |
| 232 | </fieldset> | |
| 233 | ||
| 234 | {editable ? ( | |
| Phones reach every page: each mode's menu from the top bar or by tapping the tab you're on, Docs first in the avatar sheet, a project's pages as a swipeable strip; dialogs as bottom sheets above the keyboard, safe areas, menus kept on screen, 40px targets, touch-visible controls, save bars above the tab bar | 235 | <div className="sticky bottom-(--tabbar-h) in-data-[keyboard=open]:bottom-0 -mx-4 flex flex-wrap items-center gap-4 border-t border-line bg-bg/90 px-4 py-4 backdrop-blur"> |
| Merge branch 'worktree-agent-a985c01f43538af01' | 236 | <SubmitButton pending="Saving…" busy={saving}> |
| 237 | Save guardrails | |
| 238 | </SubmitButton> | |
| Last commits read trees two dozen commits at a time, and the file list waits at most 3 s for them | 239 | {saved && <span className="text-sm text-muted">Saved. Runs that start from now on get these.</span>} |
| 240 | <ErrorText>{error}</ErrorText> | |
| 241 | {own.updatedBy && own.updatedAt && !saved && !error && ( | |
| 242 | <span className="text-xs text-faint"> | |
| 243 | Last changed by <span className="font-mono">{own.updatedBy}</span> <TimeAgo at={own.updatedAt} /> | |
| 244 | </span> | |
| 245 | )} | |
| 246 | </div> | |
| 247 | ) : ( | |
| 248 | <p className="text-sm text-muted"> | |
| 249 | {level === "workspace" | |
| 250 | ? "Only the workspace's owners can change its defaults." | |
| 251 | : "Only members of the workspace can change a project's guardrails."} | |
| 252 | </p> | |
| 253 | )} | |
| 254 | </Form> | |
| 255 | ); | |
| 256 | } |
This file's history is long; its oldest lines are credited to the oldest commit read.