Skip to content
858 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.

Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1/**
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2 * Security, as a project's page shows it: open alerts by severity, the
3 * secrets found in pushes and history, vulnerable dependencies with the
4 * security update g1t opened for each, and what happened to every alert.
5 * The page posts the intents in `routes/repo/security.tsx`'s action.
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API6 */
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily7import {
8 Bot,
9 CircleAlert,
10 CircleCheck,
11 CircleDot,
12 CircleSlash,
13 ExternalLink,
14 GitBranch,
15 GitMerge,
16 GitPullRequest,
17 GitPullRequestClosed,
18 History,
19 KeyRound,
20 Loader,
21 Package,
22 ShieldAlert,
23 ShieldCheck,
24} from "lucide-react";
25import { type ReactNode, useEffect, useRef, useState } from "react";
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API26import { Link, useFetcher } from "react-router";
27
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily28import {
29 type AlertActivity,
30 type AlertState,
31 DEPENDENCY_DISMISS_REASONS,
32 type DismissReason,
33 SECRET_DISMISS_REASONS,
34 SEVERITIES,
35 type PullStatus,
36 type SecretFinding,
37 type SecurityUpdate,
38 type Severity,
39 type SeverityCounts,
40 type Vulnerability,
41 dismissLabel,
42} from "@g1t/contracts";
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API43
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily44import {
45 type ActivityEntry,
46 type PackageGroup,
47 UPDATE_STATES,
48 alertActivity,
49 groupByPackage,
50 highestFix,
51 latestUpdate,
52 splitSecrets,
53 worstSeverity,
54} from "../lib/security-alerts";
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.55import { TimeAgo } from "./ui";
56import { Avatar } from "./ui/avatar";
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API57import { Badge, type BadgeTone } 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.58import { Button } from "./ui/button";
59import { Card } from "./ui/card";
Merge branch 'worktree-agent-ae1299e92e4462012'60import { Hint } from "./ui/hint";
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API61import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from "./ui/dialog";
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily62import { RadioGroup, RadioOption } from "./ui/radio-group";
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API63
64type Done = { ok: boolean; error?: string } | undefined;
65
66const SEVERITY: Record<Severity, { label: string; tone: BadgeTone }> = {
67 critical: { label: "Critical", tone: "danger" },
68 high: { label: "High", tone: "warn" },
69 medium: { label: "Medium", tone: "merged" },
70 low: { label: "Low", tone: "info" },
71 unknown: { label: "Unrated", tone: "neutral" },
72};
73
74
75export function SeverityBadge({ severity }: { severity: Severity }) {
76 return <Badge tone={SEVERITY[severity].tone}>{SEVERITY[severity].label}</Badge>;
77}
78
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily79/** Open alerts by severity, one tile each. */
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API80export function SeverityCountsGrid({ counts }: { counts: SeverityCounts }) {
81 return (
82 <div className="grid grid-cols-2 gap-3 sm:grid-cols-5">
83 {SEVERITIES.map((severity) => (
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.84 <Card key={severity} className="px-4 py-3">
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API85 <p className="text-xs text-muted">{SEVERITY[severity].label}</p>
86 <p className={`mt-1 text-2xl font-semibold tabular-nums ${counts[severity] > 0 && severity === "critical" ? "text-danger" : ""}`}>
87 {counts[severity]}
88 </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.89 </Card>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API90 ))}
91 </div>
92 );
93}
94
95/** A compact row of severity counts, for a list of projects. */
96export function SeverityCountsInline({ counts }: { counts: SeverityCounts }) {
97 const shown = SEVERITIES.filter((severity) => counts[severity] > 0);
98 if (shown.length === 0) {
99 return (
Merge branch 'worktree-agent-ad7c6d88d93adc817'100 <span className="inline-flex items-center gap-1 text-xs text-success">
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API101 <ShieldCheck size={13} />
102 Nothing open
103 </span>
104 );
105 }
106 return (
107 <span className="flex flex-wrap gap-1.5">
108 {shown.map((severity) => (
109 <Badge key={severity} tone={SEVERITY[severity].tone}>
110 {counts[severity]} {SEVERITY[severity].label.toLowerCase()}
111 </Badge>
112 ))}
113 </span>
114 );
115}
116
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily117const STATE_FILTERS: { state: AlertState; label: string; icon: ReactNode }[] = [
118 { state: "open", label: "Open", icon: <CircleDot size={14} /> },
119 { state: "dismissed", label: "Dismissed", icon: <CircleSlash size={14} /> },
120 { state: "fixed", label: "Fixed", icon: <CircleCheck size={14} /> },
121];
122
123/** Open, Dismissed and Fixed, with how many alerts each holds. */
124export function StateFilter({
125 counts,
126 value,
127 onChange,
128}: {
129 counts: Record<AlertState, number>;
130 value: AlertState;
131 onChange: (state: AlertState) => void;
132}) {
133 return (
134 <div role="group" aria-label="Filter alerts" className="flex flex-wrap items-center gap-x-4 gap-y-1 text-sm">
135 {STATE_FILTERS.map(({ state, label, icon }) => (
136 <button
137 key={state}
138 type="button"
139 aria-pressed={value === state}
140 onClick={() => onChange(state)}
141 className={`inline-flex items-center gap-1.5 transition-colors ${
142 value === state ? "font-medium text-fg" : "text-muted hover:text-fg"
143 }`}
144 >
145 {icon}
146 {counts[state]} {label}
147 </button>
148 ))}
149 </div>
150 );
151}
152
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API153const TEXTAREA =
154 "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";
155
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily156const SMALL_BUTTON =
157 "rounded-md border border-line px-2.5 py-1 text-xs font-medium text-muted transition-colors hover:border-line-strong hover:text-fg disabled:opacity-50";
158
159/** Dismiss an alert with one of `reasons` and an optional comment. */
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar160export function DismissDialog({
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily161 id,
162 title,
163 detail,
164 reasons,
165 note,
166 action,
167 defaultReason,
168 trigger = "Dismiss",
169}: {
170 id: string;
171 title: string;
172 /** What the alert is, in a line of code type. */
173 detail: string;
174 reasons: { reason: DismissReason; label: string; about: string }[];
175 note?: string;
176 action: string;
177 defaultReason?: DismissReason;
178 trigger?: string;
179}) {
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API180 const fetcher = useFetcher<Done>();
181 const [open, setOpen] = useState(false);
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily182 const [reason, setReason] = useState<string>(defaultReason ?? "");
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API183 useEffect(() => {
184 if (fetcher.state === "idle" && fetcher.data?.ok) setOpen(false);
185 }, [fetcher.state, fetcher.data]);
186 return (
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily187 <Dialog
188 open={open}
189 onOpenChange={(next) => {
190 setOpen(next);
191 if (next) setReason(defaultReason ?? "");
192 }}
193 >
194 <DialogTrigger className={SMALL_BUTTON}>{trigger}</DialogTrigger>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API195 <DialogContent>
196 <DialogHeader>
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily197 <DialogTitle>{title}</DialogTitle>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API198 <DialogDescription>
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily199 Say why it can stay. The alert keeps your name, the reason and your comment, and anyone with access can reopen it.
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API200 </DialogDescription>
201 </DialogHeader>
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily202 <fetcher.Form method="post" action={action} className="space-y-4">
203 <input type="hidden" name="intent" value="dismiss" />
204 <input type="hidden" name="id" value={id} />
205 <p className="font-mono text-xs break-all text-muted">{detail}</p>
206 <fieldset>
207 <legend className="text-xs font-medium text-muted">Reason</legend>
208 <RadioGroup name="reason" value={reason} onValueChange={setReason} required className="mt-2 gap-3">
209 {reasons.map((option) => (
210 <RadioOption key={option.reason} value={option.reason} label={option.label} description={option.about} />
211 ))}
212 </RadioGroup>
213 </fieldset>
214 {note && <p className="rounded-md border border-warn/30 bg-warn/5 px-3 py-2 text-xs text-warn">{note}</p>}
215 <label className="block">
216 <span className="mb-1.5 block text-xs font-medium text-muted">Comment (optional)</span>
217 <textarea name="comment" rows={3} maxLength={500} placeholder="What someone reading this later should know." className={TEXTAREA} />
218 </label>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API219 <div className="flex justify-end">
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.220 <Button
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API221 type="submit"
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.222 disabled={fetcher.state !== "idle" || !reason}>
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily223 {fetcher.state !== "idle" ? "Dismissing…" : "Dismiss alert"}
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.224 </Button>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API225 </div>
226 {fetcher.data?.error && <p className="text-sm text-danger">{fetcher.data.error}</p>}
227 </fetcher.Form>
228 </DialogContent>
229 </Dialog>
230 );
231}
232
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar233export function ReopenButton({ id, action }: { id: string; action: string }) {
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily234 const fetcher = useFetcher<Done>();
235 return (
236 <span className="flex flex-col items-end gap-1">
237 <button
238 type="button"
239 disabled={fetcher.state !== "idle"}
240 onClick={() => fetcher.submit({ intent: "reopen", id }, { method: "post", action })}
241 className={SMALL_BUTTON}
242 >
243 {fetcher.state !== "idle" ? "Reopening…" : "Reopen"}
244 </button>
245 {fetcher.data?.error && <span className="text-xs text-danger">{fetcher.data.error}</span>}
246 </span>
247 );
248}
249
250/** What happened to an alert, oldest first, folded away until asked for. */
251function ActivityLog({ entries, base, open }: { entries: ActivityEntry[]; base: string; open?: boolean }) {
252 if (entries.length === 0) return null;
253 return (
254 <details className="group mt-2" open={open}>
255 <summary className="inline-flex cursor-pointer list-none items-center gap-1.5 text-xs text-muted hover:text-fg [&::-webkit-details-marker]:hidden">
256 <History size={12} />
257 <span className="group-open:hidden">Show activity ({entries.length})</span>
258 <span className="hidden group-open:inline">Hide activity</span>
259 </summary>
260 <ol className="mt-2 space-y-2 border-l border-line pl-3">
261 {entries.map((entry) => (
262 <li key={entry.key} className="text-xs">
263 <p className="flex flex-wrap items-center gap-x-1.5 gap-y-1 text-muted">
264 {entry.actor ? (
265 <>
266 <Avatar name={entry.actor} size={16} />
267 <span className="font-medium text-fg">{entry.actor}</span>
268 </>
269 ) : (
270 <CircleDot size={12} className="text-faint" />
271 )}
272 <span>{entry.text}</span>
273 {entry.ref && (
274 <Link
275 to={`${base}/${entry.ref.kind === "pull" ? "pull" : "issues"}/${entry.ref.number}`}
276 className="font-medium text-fg-soft hover:text-fg hover:underline"
277 >
278 #{entry.ref.number}
279 </Link>
280 )}
281 {entry.reason && <Badge>{dismissLabel(entry.reason)}</Badge>}
282 <span className="text-faint">
283 <TimeAgo at={entry.at} />
284 </span>
285 </p>
286 {entry.comment && <p className="mt-1 text-fg-soft wrap-anywhere">“{entry.comment}”</p>}
287 </li>
288 ))}
289 </ol>
290 </details>
291 );
292}
293
294function useFocus<T extends HTMLElement>(focused: boolean) {
295 const ref = useRef<T>(null);
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API296 useEffect(() => {
297 if (focused) ref.current?.scrollIntoView({ block: "center" });
298 }, [focused]);
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily299 return ref;
300}
301
302const FOCUSED = "bg-accent/5 ring-1 ring-accent/40 ring-inset";
303
304function secretBadge(finding: SecretFinding): { label: string; tone: BadgeTone; about: string } {
305 if (finding.state === "dismissed") {
306 return {
307 label: "Dismissed",
308 tone: "neutral",
309 about: finding.status === "allowed" ? "Pushes carrying it go through." : "Dismissed.",
310 };
311 }
Merge branch 'worktree-agent-ad7c6d88d93adc817'312 if (finding.state === "fixed") return { label: "Revoked", tone: "success", about: "Revoked or rotated." };
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily313 if (finding.status === "blocked") {
314 return { label: "Push blocked", tone: finding.testValue ? "neutral" : "warn", about: "A push carrying it was refused, so it never landed." };
315 }
316 return {
317 label: "In history",
318 tone: finding.testValue ? "neutral" : "danger",
319 about: "In the repository's history. Rotate it with whoever issued it, then dismiss it as revoked.",
320 };
321}
322
323function SecretItem({
324 finding,
325 activity,
326 base,
327 action,
328 focused,
329 canDismiss,
330}: {
331 finding: SecretFinding;
332 activity: AlertActivity[];
333 base: string;
334 action: string;
335 focused: boolean;
336 canDismiss: boolean;
337}) {
338 const ref = useFocus<HTMLLIElement>(focused);
339 const badge = secretBadge(finding);
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API340 const landed = finding.source === "history" || finding.status === "open";
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily341 const where = `${finding.path}:${finding.line}`;
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API342 return (
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily343 <li ref={ref} id={finding.id} className={`flex flex-col gap-3 px-4 py-3 sm:flex-row sm:items-start ${focused ? FOCUSED : ""}`}>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API344 <KeyRound size={15} className="mt-0.5 hidden shrink-0 text-muted sm:block" />
345 <div className="min-w-0 grow">
346 <div className="flex flex-wrap items-center gap-2">
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar347 <Link to={`${base}/security/secret-scanning/${finding.id}`} className="text-sm font-medium first-letter:uppercase hover:underline">
348 {finding.label}
349 </Link>
Merge branch 'worktree-agent-ae1299e92e4462012'350 <Hint label={badge.about}>
351 <Badge tone={badge.tone}>{badge.label}</Badge>
352 </Hint>
353 {finding.testValue && (
354 <Hint label={finding.testValue}>
355 <Badge>Likely test value</Badge>
356 </Hint>
357 )}
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar358 {finding.validity === "active" && (
Merge branch 'worktree-agent-ae1299e92e4462012'359 <Hint label="Its issuer says it still works">
360 <Badge tone="danger">Active</Badge>
361 </Hint>
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar362 )}
Merge branch 'worktree-agent-ae1299e92e4462012'363 {finding.validity === "inactive" && (
364 <Hint label="Its issuer refused it: revoked or expired">
365 <Badge>Inactive</Badge>
366 </Hint>
367 )}
368 {finding.bypass && (
369 <Hint label={`Bypassed by ${finding.bypass.by}`}>
370 <Badge tone="warn">Bypassed</Badge>
371 </Hint>
372 )}
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily373 {finding.state === "dismissed" && finding.dismissedReason && <Badge>{dismissLabel(finding.dismissedReason)}</Badge>}
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API374 </div>
375 <p className="mt-1 truncate font-mono text-xs">
376 {landed ? (
377 <Link to={`${base}/blob/${finding.commit}/${finding.path}#L${finding.line}`} className="text-fg-soft hover:text-fg hover:underline">
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily378 {where}
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API379 </Link>
380 ) : (
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily381 <span className="text-fg-soft">{where}</span>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API382 )}
383 <span className="text-faint"> · {finding.preview}</span>
384 </p>
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily385 {finding.testValue && <p className="mt-1 text-xs text-muted">{finding.testValue}</p>}
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API386 <p className="mt-1.5 flex flex-wrap gap-x-3 gap-y-1 text-xs text-faint">
387 <span>
388 {finding.source === "push" ? "in a push" : "in history"}
389 {finding.foundBy && <> by {finding.foundBy}</>}, commit{" "}
390 {landed ? (
391 <Link to={`${base}/commit/${finding.commit}`} className="font-mono hover:text-fg">
392 {finding.commit.slice(0, 7)}
393 </Link>
394 ) : (
395 <span className="font-mono">{finding.commit.slice(0, 7)}</span>
396 )}
397 </span>
398 <span>
399 found <TimeAgo at={finding.foundAt} />
400 </span>
401 </p>
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily402 <ActivityLog entries={alertActivity(finding, activity)} base={base} open={focused && finding.state !== "open"} />
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API403 </div>
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily404 {canDismiss && (
405 <div className="flex shrink-0 items-center gap-1.5">
406 {finding.state === "open" ? (
407 <DismissDialog
408 id={finding.id}
409 title={`Dismiss ${finding.label}`}
410 detail={`${where} · ${finding.preview}`}
411 reasons={SECRET_DISMISS_REASONS}
412 note={
413 finding.status === "blocked" && !finding.testValue
414 ? "Dismissing it lets the same push through, unless you dismiss it as revoked."
415 : undefined
416 }
417 action={action}
418 />
419 ) : (
420 <ReopenButton id={finding.id} action={action} />
421 )}
422 </div>
423 )}
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API424 </li>
425 );
426}
427
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily428function Empty({ title, children }: { title: string; children: ReactNode }) {
429 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.430 <Card tone="plain" className="border-dashed px-6 py-10 text-center">
Merge branch 'worktree-agent-ad7c6d88d93adc817'431 <ShieldCheck size={22} className="mx-auto text-success" />
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily432 <p className="mt-2 font-medium">{title}</p>
433 <p className="mt-1 text-sm text-muted">{children}</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.434 </Card>
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily435 );
436}
437
438const LIST = "divide-y divide-line overflow-hidden rounded-xl border border-line bg-surface";
439
440/** The secret alerts in one state: on Open, real ones first, then likely test values. */
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API441export function SecretsList({
442 secrets,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily443 state,
444 activity,
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API445 base,
446 action,
447 focus,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily448 canDismiss,
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API449}: {
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily450 /** Already filtered to `state`. */
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API451 secrets: SecretFinding[];
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily452 state: AlertState;
453 activity: AlertActivity[];
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API454 base: string;
455 action: string;
456 focus: string | null;
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily457 /** Whether the viewer may dismiss and reopen secret alerts (Admin). */
458 canDismiss: boolean;
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API459}) {
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily460 const item = (finding: SecretFinding) => (
461 <SecretItem
462 key={finding.id}
463 finding={finding}
464 activity={activity}
465 base={base}
466 action={action}
467 focused={finding.id === focus}
468 canDismiss={canDismiss}
469 />
470 );
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API471 if (secrets.length === 0) {
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily472 return state === "open" ? (
473 <Empty title="No open secret alerts">
474 Pushes that add a key or a token are refused before they land, and the history is scanned once in the background.
475 </Empty>
476 ) : (
477 <Empty title={state === "dismissed" ? "No dismissed secret alerts" : "No revoked secrets"}>
478 {state === "dismissed"
479 ? "Alerts someone dismissed as a false positive, a test value or accepted are listed here."
480 : "Secrets dismissed as revoked are listed here."}
481 </Empty>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API482 );
483 }
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily484 if (state !== "open") return <ul className={LIST}>{secrets.map(item)}</ul>;
485 const { real, tests } = splitSecrets(secrets);
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API486 return (
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily487 <div className="space-y-5">
488 {real.length > 0 ? (
489 <ul className={LIST}>{real.map(item)}</ul>
490 ) : (
491 <Empty title="No secrets that look real">Only likely test values are open, and they never block a push.</Empty>
492 )}
493 {tests.length > 0 && (
494 <section>
495 <h4 className="text-sm font-medium">Likely test values</h4>
496 <p className="mt-0.5 mb-2 text-xs text-muted">
497 These look made for tests or documentation. They never block a push and are not counted as critical; dismiss them
498 to clear the list.
499 </p>
500 <ul className={LIST}>{tests.map(item)}</ul>
501 </section>
502 )}
503 </div>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API504 );
505}
506
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily507/** Where the legacy upgrade issue for a package stands, as the page loads it. */
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API508export type UpgradeFix = {
509 number: number;
510 state: "open" | "closed";
511 /** The newest pull request for the issue, if any. */
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily512 pull: { number: number; status: PullStatus; agent: string | null } | null;
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API513 resolvedBy: number | null;
514};
515
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily516/** A security update's pull request as the page loads it. */
517export type PullInfo = { number: number; status: PullStatus; title: string };
518
519function FixLink({ issue, fix, base }: { issue: number; fix: UpgradeFix | undefined; base: string }) {
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API520 const pull = fix?.pull;
521 return (
522 <span className="flex flex-wrap items-center gap-x-2 gap-y-1 text-xs">
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily523 <span className="text-muted">Upgrade issue</span>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API524 <Link to={`${base}/issues/${issue}`} className="inline-flex items-center gap-1 text-fg-soft hover:text-fg">
Merge branch 'worktree-agent-ad7c6d88d93adc817'525 {fix?.state === "closed" ? <CircleCheck size={12} className="text-merged" /> : <CircleDot size={12} className="text-success" />}#{issue}
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API526 </Link>
527 {pull && (
528 <Link to={`${base}/pull/${pull.number}`} className="inline-flex items-center gap-1 text-muted hover:text-fg">
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily529 {pull.agent ? <Bot size={12} /> : <GitPullRequest size={12} />}#{pull.number} {pull.status === "draft" ? "in progress" : pull.status}
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API530 </Link>
531 )}
532 </span>
533 );
534}
535
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily536const PULL_ICON: Record<PullStatus, ReactNode> = {
537 draft: <GitPullRequest size={13} className="text-muted" />,
Merge branch 'worktree-agent-ad7c6d88d93adc817'538 open: <GitPullRequest size={13} className="text-success" />,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily539 merged: <GitMerge size={13} className="text-merged" />,
540 closed: <GitPullRequestClosed size={13} className="text-danger" />,
541};
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API542
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily543/** Where g1t's security update for a package stands. */
544function UpdateStatus({ update, name, pulls, base }: { update: SecurityUpdate; name: string; pulls: Record<number, PullInfo>; base: string }) {
545 const meta = UPDATE_STATES[update.state];
546 const pull = update.pull != null ? pulls[update.pull] : undefined;
547 const about: Record<SecurityUpdate["state"], ReactNode> = {
548 requested: <>A sandbox is raising {name} to {update.target}.</>,
549 open: <>It raises {name} to {update.target} and lands through your branch's required checks.</>,
550 merged: <>{name} was raised to {update.target}.</>,
551 closed: <>The pull request was closed without merging.</>,
552 superseded: <>A newer update replaced it, or the package is no longer vulnerable, so g1t closed it.</>,
553 needs_code: (
554 <>
g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent555 Raising {name} to {update.target} needs code changes, so g1t opened an issue to make them
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily556 {update.issue != null && (
557 <>
558 :{" "}
559 <Link to={`${base}/issues/${update.issue}`} className="font-medium text-fg-soft hover:text-fg hover:underline">
560 #{update.issue}
561 </Link>
562 </>
563 )}
564 .
565 </>
566 ),
567 failed: <>{update.error ?? "g1t could not make the change."}</>,
568 };
569 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.570 <Card tone="plain" radius="lg" className="mt-2.5 bg-bg/40 px-3 py-2 text-xs sm:ml-7">
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily571 <div className="flex flex-wrap items-center gap-x-2.5 gap-y-1.5">
572 {update.state === "requested" ? (
573 <Loader size={13} className="animate-spin text-info motion-reduce:animate-none" />
574 ) : update.state === "failed" || update.state === "needs_code" ? (
575 <CircleAlert size={13} className={update.state === "failed" ? "text-danger" : "text-warn"} />
576 ) : (
577 PULL_ICON[pull?.status ?? (update.state === "merged" ? "merged" : update.state === "open" ? "open" : "closed")]
578 )}
579 <Badge tone={meta.tone}>{meta.label}</Badge>
580 {update.pull != null && (
581 <Link to={`${base}/pull/${update.pull}`} className="font-medium text-fg-soft hover:text-fg hover:underline">
582 #{update.pull}
583 {pull && <span className="font-normal text-muted"> {pull.status === "draft" ? "draft" : pull.status}</span>}
584 </Link>
585 )}
586 {update.branch && (
587 <span className="inline-flex min-w-0 items-center gap-1 font-mono text-muted">
588 <GitBranch size={12} className="shrink-0" />
589 <span className="truncate">{update.branch}</span>
590 </span>
591 )}
592 <span className="text-faint">
593 <TimeAgo at={update.updatedAt} />
594 </span>
595 </div>
596 <p className={`mt-1 ${update.state === "failed" ? "text-danger" : "text-muted"} wrap-anywhere`}>{about[update.state]}</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.597 </Card>
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily598 );
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API599}
600
601export function VulnerabilityList({
602 vulnerabilities,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily603 state,
604 activity,
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API605 fixes,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily606 pulls,
607 upkeep,
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API608 base,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily609 action,
610 focus,
611 canDismiss,
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API612}: {
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily613 /** Already filtered to `state`. */
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API614 vulnerabilities: Vulnerability[];
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily615 state: AlertState;
616 activity: AlertActivity[];
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API617 fixes: Record<number, UpgradeFix>;
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily618 pulls: Record<number, PullInfo>;
619 /** Whether security updates are on. */
620 upkeep: boolean;
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API621 base: string;
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily622 action: string;
623 focus: string | null;
624 /** Whether the viewer may dismiss and reopen dependency alerts (Write). */
625 canDismiss: boolean;
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API626}) {
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily627 const packages = groupByPackage(vulnerabilities);
628 if (packages.length === 0) {
629 return state === "open" ? (
630 <Empty title="No known vulnerabilities">
631 Every package the lockfiles resolve is checked against the OSV database on each push to the default branch, and daily.
632 </Empty>
633 ) : (
634 <Empty title={state === "dismissed" ? "No dismissed dependency alerts" : "Nothing fixed yet"}>
635 {state === "dismissed"
636 ? "Alerts someone dismissed, with their reason, are listed here."
637 : "Alerts whose package was upgraded, or is no longer vulnerable, are listed here."}
638 </Empty>
639 );
640 }
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API641 return (
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily642 <ul className={LIST}>
643 {packages.map((group) => (
644 <PackageItem
645 key={group.key}
646 group={group}
647 activity={activity}
648 fixes={fixes}
649 pulls={pulls}
650 upkeep={upkeep}
651 base={base}
652 action={action}
653 focus={focus}
654 canDismiss={canDismiss}
655 />
656 ))}
657 </ul>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API658 );
659}
660
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily661function PackageItem({
662 group,
663 activity,
664 fixes,
665 pulls,
666 upkeep,
667 base,
668 action,
669 focus,
670 canDismiss,
671}: {
672 group: PackageGroup;
673 activity: AlertActivity[];
674 fixes: Record<number, UpgradeFix>;
675 pulls: Record<number, PullInfo>;
676 upkeep: boolean;
677 base: string;
678 action: string;
679 focus: string | null;
680 canDismiss: boolean;
681}) {
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API682 const versions = [...new Set(group.vulns.map((vuln) => vuln.version))];
683 const manifests = [...new Set(group.vulns.map((vuln) => vuln.manifest))];
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily684 const target = highestFix(group.vulns);
685 const update = latestUpdate(group.vulns);
686 const issue = update ? null : (group.vulns.find((vuln) => vuln.issue != null)?.issue ?? null);
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API687 return (
688 <li className="px-4 py-3">
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily689 <div className="flex items-start gap-3">
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API690 <Package size={15} className="mt-0.5 hidden shrink-0 text-muted sm:block" />
691 <div className="min-w-0 grow">
692 <div className="flex flex-wrap items-center gap-2">
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily693 <span className="font-mono text-sm font-medium break-all">{group.name}</span>
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API694 <span className="font-mono text-xs text-muted">{versions.join(", ")}</span>
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily695 <Badge>{group.ecosystem}</Badge>
696 <SeverityBadge severity={worstSeverity(group.vulns)} />
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API697 </div>
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily698 <p className="mt-1 text-xs text-faint wrap-anywhere">
699 {target ? <>Fixed in {target}</> : "No patched version"} · locked in{" "}
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API700 <span className="font-mono">{manifests.join(", ")}</span>
701 </p>
702 </div>
703 </div>
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily704 {update && <UpdateStatus update={update} name={group.name} pulls={pulls} base={base} />}
705 {issue != null && (
706 <div className="mt-2 sm:ml-7">
707 <FixLink issue={issue} fix={fixes[issue]} base={base} />
708 </div>
709 )}
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.710 <Card asChild tone="plain" radius="lg" className="mt-2.5 space-y-px overflow-hidden sm:ml-7">
711 <ul>
712 {group.vulns.map((vuln) => (
713 <AdvisoryItem
714 key={vuln.id}
715 vuln={vuln}
716 showManifest={manifests.length > 1}
717 activity={activity}
718 upkeep={upkeep}
719 base={base}
720 action={action}
721 focused={vuln.id === focus}
722 canDismiss={canDismiss}
723 />
724 ))}
725 </ul>
726 </Card>
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily727 </li>
728 );
729}
730
731function AdvisoryItem({
732 vuln,
733 showManifest,
734 activity,
735 upkeep,
736 base,
737 action,
738 focused,
739 canDismiss,
740}: {
741 vuln: Vulnerability;
742 showManifest: boolean;
743 activity: AlertActivity[];
744 upkeep: boolean;
745 base: string;
746 action: string;
747 focused: boolean;
748 canDismiss: boolean;
749}) {
750 const ref = useFocus<HTMLLIElement>(focused);
751 const osv = `https://osv.dev/vulnerability/${vuln.osvId}`;
752 const dismiss = (reason?: DismissReason, trigger?: string) => (
753 <DismissDialog
754 id={vuln.id}
755 title={`Dismiss ${vuln.advisory}`}
756 detail={`${vuln.package} ${vuln.version} · ${vuln.manifest}`}
757 reasons={DEPENDENCY_DISMISS_REASONS}
758 action={action}
759 defaultReason={reason}
760 trigger={trigger}
761 />
762 );
763 return (
764 <li ref={ref} id={vuln.id} className={`bg-bg/30 px-3 py-2.5 not-first:border-t not-first:border-line ${focused ? FOCUSED : ""}`}>
765 <div className="flex flex-col gap-2 sm:flex-row sm:items-start">
766 <div className="min-w-0 grow text-xs">
767 <p className="flex flex-wrap items-center gap-x-2 gap-y-1">
768 <a href={osv} target="_blank" rel="noreferrer" className="inline-flex items-center gap-1 font-mono text-fg-soft hover:text-fg">
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API769 {vuln.advisory}
770 <ExternalLink size={10} />
771 </a>
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily772 <SeverityBadge severity={vuln.severity} />
773 {vuln.fixedVersion && <span className="text-faint">fixed in {vuln.fixedVersion}</span>}
774 {showManifest && <span className="font-mono text-faint">{vuln.manifest}</span>}
775 {vuln.state === "dismissed" && vuln.dismissedReason && <Badge>{dismissLabel(vuln.dismissedReason)}</Badge>}
776 </p>
777 <p className="mt-1 text-muted wrap-anywhere">{vuln.summary}</p>
778 </div>
779 {canDismiss && vuln.state !== "fixed" && (
780 <div className="flex shrink-0 items-center gap-1.5">
781 {vuln.state === "open" ? dismiss() : <ReopenButton id={vuln.id} action={action} />}
782 </div>
783 )}
784 </div>
785 {vuln.state === "open" && !vuln.fixedVersion && (
786 <div className="mt-2 rounded-md border border-warn/30 bg-warn/5 px-3 py-2 text-xs">
787 <p className="flex items-center gap-1.5 font-medium text-warn">
788 <ShieldAlert size={13} />
789 No patched version available
790 </p>
791 <p className="mt-1 text-muted">
792 No release fixes this yet. Dependencies are checked again daily
793 {upkeep
794 ? ", and g1t opens a pull request when a fix is published."
795 : "; turn on security updates and g1t opens a pull request when a fix is published."}
796 </p>
797 <div className="mt-2 flex flex-wrap items-center gap-2">
798 <a href={osv} target="_blank" rel="noreferrer" className="inline-flex items-center gap-1 text-fg-soft hover:text-fg hover:underline">
799 Read the advisory
800 <ExternalLink size={10} />
801 </a>
802 {canDismiss && dismiss("tolerable_risk", "Dismiss as tolerable risk")}
803 </div>
804 </div>
805 )}
806 <ActivityLog entries={alertActivity(vuln, activity)} base={base} open={focused && vuln.state !== "open"} />
Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API807 </li>
808 );
809}
810
811export function ScanSummary({
812 scan,
813}: {
814 scan: { history: string; commitsScanned: number; historyFinishedAt: string | null; dependenciesScannedAt: string | null; dependenciesError: string | null; lockfiles: string[] };
815}) {
816 const history =
817 scan.history === "done" ? (
818 <>
819 History scanned: {scan.commitsScanned.toLocaleString()} commits
820 {scan.historyFinishedAt && (
821 <>
822 , <TimeAgo at={scan.historyFinishedAt} />
823 </>
824 )}
825 </>
826 ) : scan.history === "stopped" ? (
827 "History scan paused: the workspace reached its spending limit"
828 ) : scan.history === "running" ? (
829 `Scanning history: ${scan.commitsScanned.toLocaleString()} commits so far`
830 ) : (
831 "History scan queued"
832 );
833 return (
834 <div className="flex flex-wrap gap-x-5 gap-y-1 text-xs text-muted">
835 <span className="inline-flex items-center gap-1.5">
836 <KeyRound size={12} />
837 {history}
838 </span>
839 <span className="inline-flex items-center gap-1.5">
840 <Package size={12} />
841 {scan.dependenciesScannedAt ? (
842 <>
843 Dependencies read <TimeAgo at={scan.dependenciesScannedAt} />
844 {scan.lockfiles.length > 0 ? <> from {scan.lockfiles.join(", ")}</> : " (no lockfiles found)"}
845 </>
846 ) : (
847 "Dependencies not read yet"
848 )}
849 </span>
850 {scan.dependenciesError && (
851 <span className="inline-flex items-center gap-1.5 text-warn">
852 <ShieldAlert size={12} />
853 {scan.dependenciesError}
854 </span>
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily855 )}
856 </div>
857 );
858}

This file's history is long; its oldest lines are credited to the oldest commit read.