Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified api/vendor/agentfox-0.3.1-py3-none-any.whl
Binary file not shown.
964 changes: 404 additions & 560 deletions dashboard/app/(product)/app/agents/[slug]/page.tsx

Large diffs are not rendered by default.

551 changes: 142 additions & 409 deletions dashboard/app/(product)/app/agents/page.tsx

Large diffs are not rendered by default.

627 changes: 123 additions & 504 deletions dashboard/app/(product)/app/approvals/page.tsx

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { Metadata } from "next";
import { appPageMetadata } from "@/lib/site";
import { api, apiErrorProps } from "@/lib/product/api";
import { Breadcrumbs } from "@/components/product/Breadcrumbs";
import { Header } from "@/components/kit";
import { ApiDown } from "@/components/ui";

/**
Expand Down Expand Up @@ -44,8 +44,7 @@ export default async function FrameworkReview({

return (
<>
<Breadcrumbs crumbs={[{ label: "Frameworks", href: "/app/compliance?tab=frameworks" }]} />
<h1>{coverage.title}</h1>
<Header back={{ href: "/app/compliance?tab=frameworks", label: "Frameworks" }} title={coverage.title} />
<p className="sub">
{coverage.controls_mapped} of {coverage.controls_total} controls mapped,{" "}
{coverage.mappings_reviewed} of {coverage.mappings_total} mappings reviewed.
Expand Down
64 changes: 33 additions & 31 deletions dashboard/app/(product)/app/compliance/page.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import type { Metadata } from "next";
import { appPageMetadata } from "@/lib/site";
import Link from "next/link";
import { redirect } from "next/navigation";
import { api, apiErrorProps } from "@/lib/product/api";
import { ApiDown, ControlStatus, DraftCaveat, Gaps, InfoTip, InventoryStrip, Panel, Stat, StatLink, StatusBar, pct, ts } from "@/components/ui";
import { PageHeader } from "@/components/product/PageHeader";
import { REPORTS_TABS } from "@/components/product/AreaTabs";
import { Header, Tabs } from "@/components/kit";
import { PrintButton } from "@/components/product/PrintButton";

/**
Expand Down Expand Up @@ -34,9 +36,7 @@ const TABS: { key: string; label: string }[] = [
{ key: "frameworks", label: "Frameworks" },
{ key: "obligations", label: "Obligations" },
{ key: "risk", label: "Risk register" },
{ key: "evidence", label: "Evidence & reports" },
{ key: "retention", label: "Retention & legal hold" },
{ key: "board", label: "Board snapshot" },
];

export default async function Compliance({
Expand All @@ -45,6 +45,9 @@ export default async function Compliance({
searchParams: Promise<{ review_error?: string; review_notice?: string; tab?: string }>;
}) {
const { review_error, review_notice, tab: rawTab } = await searchParams;
// Evidence and the board summary moved to their own Reports tabs.
if (rawTab === "evidence") redirect("/app/reports?tab=evidence");
if (rawTab === "board") redirect("/app/reports");
// Controls is the default because /findings and /policies deep-link to
// /compliance#<control-key> — a control anchor that lands on the wrong tab
// never scrolls into view, so the tab that owns those anchors has to be first.
Expand All @@ -63,7 +66,8 @@ export default async function Compliance({
} catch (e: any) {
return (
<>
<h1>Compliance</h1>
<Header title="Reports" />
<Tabs items={REPORTS_TABS} active="compliance" />
<ApiDown {...apiErrorProps(e)} />
</>
);
Expand All @@ -75,15 +79,8 @@ export default async function Compliance({

return (
<>
<PageHeader
title="Compliance"
sub={
<>
One control set, seven frameworks. Status is{" "}
<strong>computed from telemetry</strong>, not attested on a form.
</>
}
/>
<Header title="Reports" />
<Tabs items={REPORTS_TABS} active="compliance" />

{review_error && <div className="error">{review_error}</div>}
{review_notice && <div className="note-panel">{review_notice}</div>}
Expand Down Expand Up @@ -118,24 +115,29 @@ export default async function Compliance({
unit={`effective of the ${assessed} control(s) with telemetry to assess — the ${counts.not_implemented || 0} not-yet-implemented are excluded from the ratio, not counted as failing`}
/>

<div className="tabbar">
{TABS.map((t) => (
<Link
key={t.key}
href={t.key === "controls" ? "/app/compliance" : `/app/compliance?tab=${t.key}`}
className={tab === t.key ? "active" : ""}
>
{t.label}
<span className="tab-count">
{t.key === "controls" && controls.controls.length}
{t.key === "frameworks" && frameworks.frameworks.length}
{t.key === "obligations" && obligations.obligations.length}
{t.key === "risk" && register.register.length}
{t.key === "evidence" && evidencePackages.packages.length}
{t.key === "retention" && retention.legal_holds.length}
</span>
</Link>
))}
{/* Sections of Compliance, under the Reports tab bar — chips rather than a
second underlined tab row, so the two levels never look alike. */}
<div className="chipbar">
{TABS.map((t) => {
const n: Record<string, number> = {
controls: controls.controls.length,
frameworks: frameworks.frameworks.length,
obligations: obligations.obligations.length,
risk: register.register.length,
evidence: evidencePackages.packages.length,
retention: retention.legal_holds.length,
};
return (
<Link
key={t.key}
href={t.key === "controls" ? "/app/compliance" : `/app/compliance?tab=${t.key}`}
className={tab === t.key ? "chip active" : "chip"}
>
{t.label}
{t.key in n && <span className="chip-n">{n[t.key]}</span>}
</Link>
);
})}
</div>

{tab === "controls" && (
Expand Down
17 changes: 6 additions & 11 deletions dashboard/app/(product)/app/coverage/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import Link from "next/link";
import { appPageMetadata } from "@/lib/site";
import { api, apiErrorProps } from "@/lib/product/api";
import { ApiDown, InfoTip } from "@/components/ui";
import { PageHeader } from "@/components/product/PageHeader";
import { REPORTS_TABS } from "@/components/product/AreaTabs";
import { Header, Tabs } from "@/components/kit";

export const metadata: Metadata = appPageMetadata(
"Threat coverage",
Expand Down Expand Up @@ -54,7 +55,8 @@ export default async function Coverage({
} catch (e) {
return (
<>
<PageHeader title="Threat coverage" />
<Header title="Reports" />
<Tabs items={REPORTS_TABS} active="coverage" />
<ApiDown {...apiErrorProps(e)} />
</>
);
Expand All @@ -65,15 +67,8 @@ export default async function Coverage({

return (
<>
<PageHeader
title="Threat coverage"
sub={
<>
Every threat in the published lists, and what this deployment actually does
about it. Detections counted over the last {data.window_days} days.
</>
}
/>
<Header title="Reports" />
<Tabs items={REPORTS_TABS} active="coverage" />

<div className="cards">
<Stat n={`${pct}%`} label={`of ${data.scored} in-scope threats enforced`} tone={pct >= 80 ? "ok" : pct >= 50 ? "warn" : "bad"} />
Expand Down
15 changes: 9 additions & 6 deletions dashboard/app/(product)/app/entitlement/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,17 @@ import { appPageMetadata } from "@/lib/site";
import Link from "next/link";
import { api, apiErrorProps } from "@/lib/product/api";
import { ApiDown, Empty, InfoTip, Panel, Stat } from "@/components/ui";
import { PageHeader } from "@/components/product/PageHeader";
import { SETTINGS_TABS } from "@/components/product/AreaTabs";
import { Header, Tabs } from "@/components/kit";
import { Modal } from "@/components/product/Modal";

/**
* Behind the sign-in wall: `noindex`, plus a tab title that is not the fourth
* copy of "AgentFox Control Plane". See lib/site.ts appPageMetadata.
*/
export const metadata: Metadata = appPageMetadata(
"Access control",
"What each agent was granted, and what it is therefore allowed to call.",
"Data access",
"Who can see which data source through a shared agent identity.",
);

export const dynamic = "force-dynamic";
Expand Down Expand Up @@ -54,7 +55,8 @@ export default async function Entitlement() {
} catch (e: any) {
return (
<>
<h1>Access Control</h1>
<Header title="Settings" />
<Tabs items={SETTINGS_TABS} active="entitlement" />
<ApiDown {...apiErrorProps(e)} />
</>
);
Expand All @@ -64,7 +66,8 @@ export default async function Entitlement() {

return (
<>
<PageHeader title="Access control" sub="Whether a shared agent identity actually stops one person seeing another’s data." />
<Header title="Settings" />
<Tabs items={SETTINGS_TABS} active="entitlement" />

{report.requests === 0 ? (
<div className="hero empty">
Expand Down Expand Up @@ -207,7 +210,7 @@ export default async function Entitlement() {
<div className="section-head">
<h2>
Grants
<InfoTip text="A resource here is the same identifier space as a source's key on the Verified sources page — that page tells you whether the resource itself is trustworthy; this one tells you who's allowed to see it." />
<InfoTip text="A resource here is the same identifier space as a source's key on the Settings → Verified sources tab — that page tells you whether the resource itself is trustworthy; this one tells you who's allowed to see it." />
</h2>
<Modal trigger="+ Add a grant" triggerClassName="btn-primary" title="Add a grant — who can see which source">
<form action="/api/entitlement/grants" method="POST" className="body stack">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { Metadata } from "next";
import { appPageMetadata } from "@/lib/site";
import Link from "next/link";
import { ApiError, api, apiErrorProps } from "@/lib/product/api";
import { Breadcrumbs } from "@/components/product/Breadcrumbs";
import { Header } from "@/components/kit";
import { ApiDown, InfoTip, NotFound, Severity, ts } from "@/components/ui";

/**
Expand Down Expand Up @@ -53,8 +53,7 @@ export default async function EscalationConversation({

return (
<>
<Breadcrumbs crumbs={[{ label: "Escalation", href: "/app/approvals?tab=escalation" }]} />
<h1>{data.handoff?.summary || data.handoff?.reason || "Conversation"}</h1>
<Header back={{ href: "/app/approvals?tab=handoffs", label: "Hand-offs" }} title={data.handoff?.summary || data.handoff?.reason || "Conversation"} />
<p className="mono small muted" style={{ marginTop: -8 }}>{sessionId}</p>
<p className="sub">
Every turn of this conversation, replayed against the escalation policy — so
Expand Down
8 changes: 3 additions & 5 deletions dashboard/app/(product)/app/evals/[key]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { appPageMetadata } from "@/lib/site";
import Link from "next/link";
import { api, safeApi, apiErrorProps } from "@/lib/product/api";
import { ApiDown, Panel, ts } from "@/components/ui";
import { Breadcrumbs } from "@/components/product/Breadcrumbs";
import { Header } from "@/components/kit";

/**
* Behind the sign-in wall: `noindex`, plus a tab title that is not the fourth
Expand Down Expand Up @@ -41,9 +41,7 @@ export default async function SuiteDetail({

return (
<>
<Breadcrumbs crumbs={[{ label: "Evaluation", href: "/app/evals" }]} />
<h1>{suite.name || suite.key}</h1>
<p className="sub">{suite.description || "No description."}</p>
<Header back={{ href: "/app/test?tab=suites", label: "Test suites" }} title={suite.name || suite.key} hint={suite.description || undefined} />
{review_error && <div className="error">{review_error}</div>}

<h2>Cases ({suite.cases.length})</h2>
Expand All @@ -54,7 +52,7 @@ export default async function SuiteDetail({
A suite with no cases has nothing to run. Add one below, or — the faster
path once you have real traffic — promote an actual production trace
into a regression case from its ID (find one on{" "}
<Link href="/app/traces">Traces</Link>).
<Link href="/app/traces">Activity</Link>).
</p>
</div>
) : (
Expand Down
10 changes: 2 additions & 8 deletions dashboard/app/(product)/app/evals/[key]/runs/[runId]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { appPageMetadata } from "@/lib/site";
import Link from "next/link";
import { ApiError, api, safeApi, apiErrorProps } from "@/lib/product/api";
import { ApiDown, InfoTip, NotFound, Panel, ts } from "@/components/ui";
import { Breadcrumbs } from "@/components/product/Breadcrumbs";
import { Header } from "@/components/kit";
import { Modal } from "@/components/product/Modal";

/**
Expand Down Expand Up @@ -51,13 +51,7 @@ export default async function RunDetail({

return (
<>
<Breadcrumbs
crumbs={[
{ label: "Evaluation", href: "/app/evals" },
{ label: key, href: `/app/evals/${key}` },
]}
/>
<h1 className="mono">{run.id}</h1>
<Header back={{ href: `/app/evals/${key}`, label: key }} title="Test run" />
<p className="sub">
<Link href={`/app/evals/${key}`}>{key}</Link>{" "}
<span className="mono small muted">{run.runner}</span>{" "}
Expand Down
6 changes: 3 additions & 3 deletions dashboard/app/(product)/app/evals/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ import { AgentLink, ApiDown, Empty, InfoTip, InventoryStrip, Panel, ts } from "@
function score(v: unknown): string {
return typeof v === "number" && Number.isFinite(v) ? v.toFixed(2) : "—";
}
import { PageHeader } from "@/components/product/PageHeader";
import { Header } from "@/components/kit";
import { Explainer } from "@/components/product/Explainer";

/**
* Behind the sign-in wall: `noindex`, plus a tab title that is not the fourth
* copy of "AgentFox Control Plane". See lib/site.ts appPageMetadata.
*/
export const metadata: Metadata = appPageMetadata(
"Evaluation",
"Test",
"Pre-release test suites, red-team runs and the drift between them.",
);

Expand Down Expand Up @@ -62,7 +62,7 @@ export default async function Evals({

return (
<>
<PageHeader title="Evaluation" sub="Whether your agent gets the answer right — graded automatically, tracked over time." />
<Header back={{ href: "/app/test", label: "Test" }} title="Evaluation details" />

{/* Four tiles for four inventory counts — none of them is a number that
wants a person, they are just what exists. A strip says the same thing
Expand Down
Loading
Loading